Using RPi Zero as a Keyboard Part 1: Setup and device definition26 Jun 2017
The Raspberry Pi Zero is a cool little piece of hardware with many possibilities. One of them is that it can work as a USB host OR as a USB gadget, meaning that it is possible to implement different types of devices such as ethernet, HID (keyboard, mouse, gamepad, etc.), audio, mass storage, etc. In this 3-part series of post we'll see how to configure and use a simple and generic keyboard gadget to send keys to the connected host.
In this part I'll go over the process of defining the gadget, breaking down what each different configuration files is used for and giving example values.
Edited October 12, 2017: Corrected the
bmAttributes value used and added explanation for the values.
Edited July 8, 2018: Tested on Raspbian Stretch (lite) 2018-06-27.
Edited August 1, 2018: Added link to gist with gadget creation and tester scripts at the end of the post.
Edited August 13, 2019: Updated link to USB HID document.
Post series index
First, let's talk about ConfigFS. For this part, I'm going to reference a brilliant presentation by Matt Porter , which greatly summarizes the key points of ConfigFS.
In essence, ConfigFS is a virtual filesystem which exposes a userspace API for the creation of USB devices. This was introduced in Linux 3.11, and recent versions of the Raspbian operating system already include the module.
Furthermore, this allows creating several devices at the same time, meaning that the Pi can act as a composite USB device with different functionalities.
Usage in Raspberry Pi Zero
First, let's assume that we have a microSD card with the following two partitions created when writing the official Raspbian image to the card:
BOOT: contains the configuration applied during boot time
DATA: contains the filesystem of the OS
Before using ConfigFS, it is necessary to load the module. Although it should be possible to use
modprobe for this, it's just easier to add the following line at the end of
And the following two lines at the end of
Once done, ConfigFS will be loaded in
/sys/kernel/config/usb_gadget when the Pi is started.
Defining a USB device
The way to define a new device in ConfigFS is to create a directory inside the virtual filesystem. Note that these commands are only for illustration purposes, as they are expected to be executed during runtime. For this example, the device will be called
mkdir /sys/kernel/config/usb_gadget/mykeyboard cd /sys/kernel/config/usb_gadget/mykeyboard
Listing the contents of this new directory shows that several files and directories have been created automatically. The meaning of each individual file can be obtained from the official USB specification . Regarding the files:
bcdDevice: device release number, assigned by manufacturer (format:
bcdUSB: USB specification number that the device implements (format:
bDeviceClass: USB class code, assigned by USB organization (format:
bDeviceProtocol: USB protocol code, assigned by USB organization (format:
bDeviceSubClass: USB subclass code, assigned by USB organization (format:
bMaxPacketSize0: maximum packet size for the device, only possible values are 8, 16, 32, and 64 (format:
idProduct: product ID, assigned by manufacturer (format:
idVendor: vendor ID, assigned by USB organization (format:
UDC: USB Device Controller, used to attach the gadget to the UDC driver in the machine
format comment at the end of each file. This means that the file should contain the given number of hex characters. For example, if
0x0000 is shown, then the file must contain a 4-digit hexadecimal number, such as
Now, let's start writing content to these files. Vendor and product IDs can be obtained from the Linux USB project :
echo 0x0100 > bcdDevice # Version 1.0.0 echo 0x0200 > bcdUSB # USB 2.0 echo 0x00 > bDeviceClass echo 0x00 > bDeviceProtocol echo 0x00 > bDeviceSubClass echo 0x08 > bMaxPacketSize0 echo 0x0104 > idProduct # Multifunction Composite Gadget echo 0x1d6b > idVendor # Linux Foundation
In addition, note that the following directories are also present:
configs/: contains specific configurations for the device
functions/: defines the capabilities of the virtual device
os_desc/: OS Descriptors (ignore it for now)
strings/: localized description of the device (e.g. in English)
Let's begin with the localization. For English, the hexadecimal code would be
0x409, which must be created as a directory inside
strings/. Once present, it is populated with three files:
manufacturer: name of the manufacturer of the device
product: product name/description
serialnumber: complete serial number of the product
mkdir strings/0x409 echo "My manufacturer" > strings/0x409/manufacturer echo "My virtual keyboard" > strings/0x409/product echo "0123456789" > strings/0x409/serialnumber
Now let's define the functions of the device. ConfigFS supports many different types of devices, but in this case we are interested in a USB keyboard. In order to have the device behave like a HID, a directory named
hid.usb0 has to be created inside
functions/. It contains the following files:
protocol: the protocol for the device
report_desc: binary descriptor for the reports sent by the keyboard
report_length: length of the reports sent by the keyboard
subclass: type of HID
For now we are going to ignore the
report_desc, as I will cover that in the next post, but we can write the values for the other files, as these are common for all keyboards:
mkdir functions/hid.usb0 echo 1 > functions/hid.usb0/protocol echo 8 > functions/hid.usb0/report_length # 8-byte reports echo 1 > functions/hid.usb0/subclass # echo "..." > functions/hid.usb0/report_desc # Check second post
Lastly, for the configuration, creating a new directory inside
configs/ will include the following:
MaxPower: maximum power for the device (in mA)
bmAttributes: configuration characteristics bitmask (in hex format:
0x00), the following bits can be set depending on the power characteristics:
- bit 7: bus powered (e.g.
- bit 6: self powered (e.g.
- bit 5: remote wakeup (e.g.
- bit 4 to bit 0: reserved
- bit 7: bus powered (e.g.
strings/: directory for localization of the configuration description (we will use
0x409for English, as before)
Note that it is possible to set several bits in the
bmAttributes bitmask at the same time. For instance, to indicate that the device is bus powered and has remote wakeup capabilities, the bitmask should be
10100000, which in hex translates to
mkdir configs/c.1 mkdir configs/c.1/strings/0x409 echo 0x80 > configs/c.1/bmAttributes # Only bus powered echo 100 > configs/c.1/MaxPower # 100 mA echo "Example configuration" > configs/c.1/strings/0x409/configuration
Once that is set up, the HID function defined previously must be linked to the configuration like so:
ln -s functions/hid.usb0 configs/c.1
Activating the device
Even though we are still missing the report descriptor, the device that was just configured can be activated by attaching the gadget to a UDC driver:
ls /sys/class/udc > UDC
After this (and after adding the report descriptor), the host to which the Pi is connected should recognize it as a USB keyboard.
The following script defines a USB keyboard as a gadget in ConfigFS. It should be executed by the Pi during runtime (e.g. on startup):
#!/bin/bash # Create gadget mkdir /sys/kernel/config/usb_gadget/mykeyboard cd /sys/kernel/config/usb_gadget/mykeyboard # Add basic information echo 0x0100 > bcdDevice # Version 1.0.0 echo 0x0200 > bcdUSB # USB 2.0 echo 0x00 > bDeviceClass echo 0x00 > bDeviceProtocol echo 0x00 > bDeviceSubClass echo 0x08 > bMaxPacketSize0 echo 0x0104 > idProduct # Multifunction Composite Gadget echo 0x1d6b > idVendor # Linux Foundation # Create English locale mkdir strings/0x409 echo "My manufacturer" > strings/0x409/manufacturer echo "My virtual keyboard" > strings/0x409/product echo "0123456789" > strings/0x409/serialnumber # Create HID function mkdir functions/hid.usb0 echo 1 > functions/hid.usb0/protocol echo 8 > functions/hid.usb0/report_length # 8-byte reports echo 1 > functions/hid.usb0/subclass # echo "..." > functions/hid.usb0/report_desc # Check second post # Create configuration mkdir configs/c.1 mkdir configs/c.1/strings/0x409 echo 0x80 > configs/c.1/bmAttributes echo 200 > configs/c.1/MaxPower # 200 mA echo "Example configuration" > configs/c.1/strings/0x409/configuration # Link HID function to configuration ln -s functions/hid.usb0 configs/c.1 # Enable gadget ls /sys/class/udc > UDC
Bonus: The previous snippet and the test scripts used in the next posts are available at https://gist.github.com/rmed/0d11b7225b3b772bb0dd89108ee93df0
-  http://events.linuxfoundation.org/sites/events/files/slides/USB%20Gadget%20Configfs%20API_0.pdf
-  https://www.usb.org/sites/default/files/documents/hid1_11.pdf
-  http://www.linux-usb.org/usb.ids
Post series index
My name is Rafael Medina, and I like code.