We're hiring!

Using dummy-hcd to play with USB gadgets

Andrzej Pietrasiewicz avatar

Andrzej Pietrasiewicz
June 24, 2019

Share this post:

In this article, I promised to tell you how to use dummy_hcd, which consists of a software-emulated host controller and a UDC chip. In other words, this means you can play with USB gadgets even if you don't have the appropriate hardware, because your PC can act as both a USB host and a USB device.

Of course we also need some gadget to run, so why don't we use cmtp-responder? You might also want to read about USB gadgets here and here. The description will be Debian-based, so for other systems you need to adjust it accordingly.


We will need a bunch of packages:

gcc # we need a compiler to compile anything
g++ # to satisfy cmake the easy way, but otherwise not used)
libconfig-dev # libusbgx and gt need libconfig
cmake # gt and cmtp-responder use cmake to generate Makefiles
git # we will be cloning from git.kernel.org and github.com
autoconf # libusbgx needs it
libtool # libusbgx needs it
asciidoc-base (for a2x) # gt builds its manpage with it
libncurses-dev # compiling the kernel
libglib2.0-dev # gt needs it
libsystemd-dev # gt needs it
usbutils # for lsusb

Run this:

apt-get install gcc g++ libconfig-dev cmake git autoconf libtool asciidoc-base libncurses-dev flex bison build-essential fakeroot libelf-dev libssl-dev bc libglib2.0-dev libsystemd-dev usbutils
apt-get clean

Once complete, you can then install libusbgx and gt.


git clone https://github.com/libusbgx/libusbgx.git
cd libusbgx
autoreconf -i
./configure  --prefix=/usr
make install # as root


git clone https://github.com/kopasiak/gt.git
cd gt/source
make install # as root

Unfortunately, the default kernel has neither ConfigFS nor dummy_hcd support turned on. Let's fix it!

The kernel

Ensure the following options are set in the kernel config:

CONFIG_CONFIGFS_FS=y               # ConfigFS support
CONFIG_USB=y                       # USB support
CONFIG_USB_GADGET=y                # USB gadget framework
CONFIG_USB_DUMMY_HCD=y             # dummy_hcd, our emulated USB host and device
CONFIG_USB_CONFIGFS=y              # composing USB gadgets with ConfigFS
CONFIG_USB_CONFIGFS_F_FS=y         # make FunctionFS a component for creating USB gadgets with ConfigFS

Compile and install the kernel your favorite way.


Thanks to the fact that we enabled the relevant bits of the kernel, we should have dummy_hcd up and running now. To confirm, do this:

ls -l /sys/class/udc

dummy_udc.0 should be there. If it is not, verify that all the previous steps have succeeded.

gt udc

This should also show dummy_udc.0.

Once dummy_udc.0 is there, your PC is ready to emulate USB gadget hardware!


We have chosen to enable ConfigFS support. As recent Debian releases come by default with systemd, your ConfigFS should be automatically mounted by /lib/systemd/system/sys-kernel-coonfig.mount unit under /sys/kernel/config.

Thanks to the enabled USB gadget, and the presence of our virtual UDC, /sys/kernel/config should now contain usb_gadget directory. From this moment on gadgets can be composed, for example with the gt.


We will create an MTP device with cmtp-responder. When it runs, if you chose to install graphical desktop environment, you will be able to click on its icon and use it as if it were, for example, the storage space of a connected smartphone.

The instructions for cmtp-responder are here, but I provide a quick summary for you below:

git clone https://github.com/cmtp-responder/cmtp-responder.git
cd cmtp-responder
make install # as root

All the below as root:

mkdir /etc/gt/templates
cp systemd/mtp-ffs.scheme /etc/gt/templates
cp systemd/*.socket /etc/systemd/system
cp systemd/*.service /etc/systemd/system
cp systemd/*.mount /etc/systemd/system
systemctl enable usb-gadget.service
systemctl enable run-ffs_mtp.mount
systemctl enable ffs.socket

Create /etc/systemd/system/usb-gadget.target if you don't have it in /lib/systemd/system:

Description=Harware activated USB gadget

And create /etc/udev/rules.d/99-systemd.rules with the below contents if your /lib/udev/rules.d/99-systemd.rules does not contain the following line:

SUBSYSTEM=="udc", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}+="usb-gadget.target"

After reboot your cmtp-responder should activate automatically. You can verify its existence with lsusb (run as root):

Bus 001 Device 002: ID 1d6b:0100 Linux Foundation PTP Gadget
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x1d6b Linux Foundation
  idProduct          0x0100 PTP Gadget
  bcdDevice            5.01
  iManufacturer           1 Collabora
  iProduct                2 MTP Gadget
  iSerial                 3 000000001
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           39
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower                2mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           3
      bInterfaceClass         6 Imaging
      bInterfaceSubClass      1 Still Image Capture
      bInterfaceProtocol      1 Picture Transfer Protocol (PIMA 15470)
      iInterface              4 Collabora MTP
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x85  EP 5 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               6
Device Qualifier (for other device speed):
  bLength                10
  bDescriptorType         6
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  bNumConfigurations      1
Device Status:     0x0000
  (Bus Powered)

This screenshot illustrates copying a larger file to our MTP gadget.

Screenshot illustrating copying a larger file to MTP gadget.

The backing storage is /media/card, and can be configured at compile time in include/mtp_config_h (MTP_EXTERNAL_PATH_CHAR).

Happy playing with cmtp-responder!

In the next installment, I will tell you how to run it on real ARM hardware, so stay tuned.

Comments (11)

  1. Metehan:
    Aug 17, 2019 at 03:06 AM

    Thank you for great share!
    What is the Debian version and Kernel version for this?

    Reply to this comment

    Reply to this comment

    1. Andrzej Pietrasiewicz:
      Aug 26, 2019 at 09:23 PM

      I used kernel v5.1 and the then up-to-date debian stable netinst, which must have been "stretch" at the moment of writing.

      Reply to this comment

      Reply to this comment

  2. Rektide:
    Nov 03, 2019 at 10:44 PM

    Nice article! Thanks. Gadgets are great!

    If you are maybe taking suggestions, perhaps do usb-ip next? :)

    Reply to this comment

    Reply to this comment

  3. maya:
    Mar 23, 2020 at 04:29 PM

    I have all listen kernel configurations enabled. However, run-ffs_mtp.mount gives me error about unknown filesystem 'functionfs'
    What can I do about it?

    Thanks for this project! I hope I get it to work

    Reply to this comment

    Reply to this comment

    1. Andrzej Pietrasiewicz:
      Mar 24, 2020 at 11:48 AM

      Ensure you have CONFIG_USB_CONFIGFS_F_FS=y, that you accidentally haven't changed any relevant options to "m" (i.e. ensure all the needed bits are compiled-in) and that you are actually running the kernel you compiled (and not e.g. your distribution kernel).

      Reply to this comment

      Reply to this comment

      1. maya:
        Mar 24, 2020 at 12:50 PM

        There was actually two problems I had. First, gt was failing silently so I didn't first know there was a problem with it until I got suspicious that it doesn't give me any errors no matter what I do with it. Then I found an issue on github about it and it was because of a misplaced gt.conf file.

        Second problem seems to be that the mounting happens too early? I can see from dmesg that it fails because functionfs filesystem is not recognized. But after boot, I can manually systemctl start it and the socket unit and then everything works. I used the exact same files as in your last october speech I found on youtube. If you know right away what it could be, let me know :) But I'm happy already that it works! Thanks alot!

        Reply to this comment

        Reply to this comment

  4. Tim Rehren:
    May 21, 2020 at 09:25 PM

    Im Stuck at here :

    root@raspberrypi:/gt/source# make
    [ 19%] Built target base
    [ 34%] Built target config
    [ 50%] Built target function
    [ 65%] Built target gadget
    [ 73%] Built target settings
    [ 88%] Built target udc
    [ 96%] Built target gt
    [100%] Generating gt.1.gz
    a2x: ERROR: "xmllint" --nonet --noout --valid "/gt/source/manpages/gt.1.xml" returned non-zero exit s tatus 4
    make[2]: *** [manpages/CMakeFiles/manpage.dir/build.make:61: manpages/gt.1.gz] Error 1
    make[1]: *** [CMakeFiles/Makefile2:463: manpages/CMakeFiles/manpage.dir/all] Error 2
    make: *** [Makefile:130: all] Error 2

    Reply to this comment

    Reply to this comment

    1. Andrzej Pietrasiewicz:
      May 22, 2020 at 11:55 AM

      On Debian and Debian-based systems you need libxml2-utils and asciidoc-base installed to have xmllint and a2x.

      Reply to this comment

      Reply to this comment

  5. Brooke Basile:
    Jul 24, 2020 at 09:12 PM

    Thank you for the guide.
    I'm having an issue with gt-- I've installed libusbgx and it seems to be working fine based on the directions from the github, but running make on gt fails with:

    -- Found PkgConfig: /usr/bin/pkg-config (found version "0.29.2")
    -- Checking for modules 'libusbgx>=0.2.0;libconfig'
    -- No package 'libusbgx' found
    CMake Error at /usr/share/cmake/Modules/FindPkgConfig.cmake:497 (message):
    A required package was not found
    Call Stack (most recent call first):
    /usr/share/cmake/Modules/FindPkgConfig.cmake:681 (_pkg_check_modules_internal)
    CMakeLists.txt:36 (pkg_check_modules)

    How can I fix this?

    Reply to this comment

    Reply to this comment

    1. Andrzej Pietrasiewicz:
      Aug 04, 2020 at 09:57 AM

      By default libusbgx installs itself in /usr/local/{include|lib}. You might want to change it, depending on your preferences and whether you compile natively or cross-compile. Make sure it is installed in a path that is searched by cmake/pkg-config and/or pass appropriate variable to cmake to point it to the right location.

      Reply to this comment

      Reply to this comment

      1. Brooke Basile:
        Aug 05, 2020 at 07:58 PM

        Ah, I gotcha. I did try playing with the location a bit but couldn't get it to work-- I guess I will just have to keep fiddling with it until I can get it going.

        Thank you!

        Reply to this comment

        Reply to this comment

Add a Comment

Allowed tags: <b><i><br>Add a new comment:

Search the newsroom

Latest Blog Posts

Desktop OpenGL 3.1 on Mali GPUs with Panfrost


The open source Panfrost driver for Arm Mali Midgard and Bifrost GPUs now provides non-conformant OpenGL ES 3.0 on Bifrost and desktop OpenGL…

Empathy first: Driving growth through people leadership


This year, the global pandemic has put a strain on us all. Motivation can become hard to maintain, worries can cloud our minds. Now more…

Developing Wayland Color Management and High Dynamic Range


Wayland is still lacking proper consideration for color management & support for high dynamic range (HDR) imagery. However, a group of developers…

A summer sprint: bringing near-native performance to Zink


This week marks two years since the OpenGL implementation on Vulkan was initially announced. Since then, and especially over the past few…

From Panfrost to production, a tale of Open Source graphics


Since our previous update on Panfrost, the open source stack for Arm's Mali Midgard and Bifrost GPUs, we've focused on taking our driver…

Engaging in an "Open First" remote internship at Collabora


The concept of a remote internship may raise some doubts, or even red flags, for many students, as would remote jobs for professionals.…

Open Since 2005 logo

We use cookies on this website to ensure that you get the best experience. By continuing to use this website you are consenting to the use of these cookies. To find out more please follow this link.

Collabora Ltd © 2005-2021. All rights reserved. Privacy Notice. Sitemap.