usb-gadget
Important
This library is experimental and not suitable for production use. Breaking changes may occur without warning.
Caution
Misconfigured USB gadgets can destabilize your system or prevent booting. Always test on non-essential devices with physical recovery access available.
A comprehensive Dart library for creating USB gadgets on Linux using ConfigFS and FunctionFS. Transform your Linux device into a USB peripheral—implement keyboards, mice, storage devices, network interfaces, or create entirely custom USB protocols in pure Dart.
Features
Kernel-Based Functions
Pre-configured USB functions implemented by kernel drivers, ready to use with minimal setup:
Network Interfaces
- CDC ECM - Ethernet over USB (widely supported)
- CDC ECM Subset - Simplified ECM for legacy hosts
- CDC EEM - Ethernet Emulation Model (lowest overhead)
- CDC NCM - Network Control Model (high performance)
- RNDIS - Remote NDIS (Windows compatibility)
Serial Ports
- CDC ACM - Abstract Control Model (standard virtual serial)
- Generic Serial - Non-CDC serial implementation
Human Interface Devices
- HID - Keyboards, mice, gamepads, and custom HID devices
Storage & Media
- Mass Storage Device (MSD) - USB flash drive emulation
- Printer - USB printer class device
Audio & Video
- MIDI - Musical Instrument Digital Interface
- UAC1 - USB Audio Class 1.0
- UAC2 - USB Audio Class 2.0
- UVC - USB Video Class (webcam emulation)
Testing & Development
- Loopback - Data loopback for testing
- SourceSink - Pattern generation and validation
FunctionFS (Custom USB Functions)
FunctionFS provides complete control over USB functionality in userspace, enabling you to:
- Define custom USB descriptors - Create devices with any interface, endpoint configuration, or capability
- Handle USB endpoints directly - Full control over data transfer, timing, and flow control
- Implement custom protocols - Build proprietary or specialized USB communication protocols
- Process control requests - Handle vendor-specific, class-specific, or standard USB requests
- React to USB events - Respond to bind, unbind, enable, disable, suspend, and resume events
Ideal for:
- Complex HID devices with custom report descriptors
- Proprietary USB protocols and interfaces
- USB device prototyping and experimentation
- Devices requiring precise timing or advanced USB features
- Applications needing complete protocol control
The library includes HIDFunctionFs as a specialized FunctionFS implementation with built-in HID protocol handling,
making custom HID devices straightforward to implement.
Requirements
Platform Support
- Operating System: Linux with USB Device Controller (UDC) support
- Architecture: ARM, ARM64, x86_64 (any architecture with Linux UDC support)
- Dart SDK: 3.10.0 or higher
Hardware Requirements
A Linux device with a USB Device Controller (UDC) is required. Standard desktop PCs typically do not include a UDC—they only have USB host controllers.
Compatible devices include:
- Raspberry Pi 4, Pi Zero (USB-C or micro-USB port)
- Raspberry Pi 5 (USB-C port)
- Orange Pi, Banana Pi (model-dependent)
- BeagleBone Black, BeagleBone AI
- Most embedded Linux boards with USB OTG capability
To check if your device has a UDC:
ls /sys/class/udc/
# Should list one or more UDC devices (e.g., fe980000.usb)
System Dependencies
Install the Linux Asynchronous I/O library:
Debian/Ubuntu:
sudo apt-get install libaio-dev
Arch Linux:
sudo pacman -S libaio
Fedora/RHEL:
sudo dnf install libaio-devel
Linux Kernel Configuration
The following kernel options must be enabled. Most modern embedded Linux distributions include these by default.
Required Kernel Configuration Options
Core USB Gadget Support:
CONFIG_USB_GADGET- USB Gadget frameworkCONFIG_USB_CONFIGFS- ConfigFS-based gadget configurationCONFIG_USB_FUNCTIONFS- FunctionFS support
Kernel Function Drivers:
CONFIG_USB_CONFIGFS_SERIAL- Serial functionsCONFIG_USB_CONFIGFS_ACM- CDC ACM serialCONFIG_USB_CONFIGFS_NCM- CDC NCM ethernetCONFIG_USB_CONFIGFS_ECM- CDC ECM ethernetCONFIG_USB_CONFIGFS_ECM_SUBSET- ECM Subset ethernetCONFIG_USB_CONFIGFS_RNDIS- RNDIS ethernetCONFIG_USB_CONFIGFS_EEM- CDC EEM ethernetCONFIG_USB_CONFIGFS_MASS_STORAGE- Mass storageCONFIG_USB_CONFIGFS_F_HID- HID functionsCONFIG_USB_CONFIGFS_F_PRINTER- Printer functionCONFIG_USB_CONFIGFS_F_MIDI- MIDI functionCONFIG_USB_CONFIGFS_F_UAC1- Audio Class 1.0CONFIG_USB_CONFIGFS_F_UAC2- Audio Class 2.0CONFIG_USB_CONFIGFS_F_UVC- Video Class
Check your kernel configuration:
zcat /proc/config.gz | grep -E 'CONFIG_USB_(GADGET|CONFIGFS|FUNCTIONFS)'
# or
grep -E 'CONFIG_USB_(GADGET|CONFIGFS|FUNCTIONFS)' /boot/config-$(uname -r)
Permissions
Root privileges or the CAP_SYS_ADMIN capability are required to configure USB gadgets.
Verify ConfigFS is mounted:
mount | grep configfs
# Should show: configfs on /sys/kernel/config type configfs (rw,relatime)
Mount ConfigFS if needed:
sudo mount -t configfs none /sys/kernel/config
Running without full root (using capabilities):
# Grant CAP_SYS_ADMIN to your Dart executable
sudo setcap cap_sys_admin+ep /path/to/your/dart/executable
Installation & Usage
Add the Package
dart pub add usb_gadget
Basic Example: USB Keyboard
This example creates a USB HID keyboard that types "hello world" when connected:
final _descriptor = Uint8List.fromList([ /* ... */]);
class Keyboard extends HIDFunction {
Keyboard()
: super(
name: 'keyboard',
descriptor: _descriptor,
protocol: HIDProtocol.keyboard,
subclass: HIDSubclass.boot,
reportLength: 8,
);
void sendKey(int keyCode, {int modifiers = 0}) {
// Press key
file.writeFromSync(
Uint8List(8)
..[0] = modifiers
..[2] = keyCode,
);
// Release key
file.writeFromSync(Uint8List(8));
}
}
Future<void> main() async {
final keyboard = Keyboard();
final gadget = Gadget(
name: 'hid_keyboard',
idVendor: 0x1234,
idProduct: 0x5679,
deviceClass: USBClass.composite,
deviceSubClass: USBSubClass.none,
deviceProtocol: USBProtocol.none,
strings: {
Language.enUS: const GadgetStrings(
manufacturer: 'ACME Corp',
product: 'USB Keyboard',
serialnumber: 'KB001',
),
},
config: Config(functions: [keyboard]),
);
try {
await gadget.bind();
await gadget.waitForState(.configured);
// An additional delay here prevents the first few keypresses from
// being missed on some hosts.
await Future.delayed(const .new(milliseconds: 100));
[0x0B, 0x08, 0x0F, 0x0F, 0x12, 0x2C, 0x1A, 0x12, 0x15, 0x0F, 0x07, 0x28]
// Write "hello world\n" keycodes
.forEach(keyboard.sendKey);
stdout.writeln('Keyboard ready. Press Ctrl+C to exit.');
await ProcessSignal.sigint.watch().first;
} finally {
await gadget.unbind();
}
}
Running Your Gadget
Standard execution (requires root):
sudo dart run bin/your_app.dart
Compiled executable:
dart compile exe bin/your_app.dart -o keyboard
sudo ./keyboard
With debug logging:
dart compile exe -DUSB_GADGET_DEBUG=true bin/your_app.dart -o keyboard
sudo ./keyboard
More Examples
The examples directory contains ready-to-run implementations:
Resources
- API Documentation - Complete API reference
- Linux USB Gadget API - Kernel documentation
- HID Usage Tables - HID keyboard/mouse codes
- usb-gadget (Rust) - Inspiration and reference implementation