usb_gadget 0.5.0 copy "usb_gadget: ^0.5.0" to clipboard
usb_gadget: ^0.5.0 copied to clipboard

Expose standard or fully custom USB peripherals (gadgets) through a USB device controller (UDC) on Linux.

usb-gadget #

Warning

This library is under active development. Breaking changes may occur without warning.

Turn any Linux device with a USB controller into a USB peripheral — keyboard, mouse, storage, network adapter, audio device, or your own custom protocol — written entirely in Dart.

pub package License: Apache 2.0


What You Can Build #

Ready-Made Kernel Functions #

These work with zero protocol boilerplate — just configure and go.

Category Functions
Network CDC ECM, ECM Subset, EEM, NCM, RNDIS
Serial CDC ACM, Generic Serial
HID Keyboards, mice, gamepads, custom devices
Storage Mass Storage Device (USB flash drive emulation)
Audio/Video MIDI, UAC1, UAC2, UVC (webcam)
Printer USB Printer Class
Testing Loopback, SourceSink

Custom Functions with FunctionFS #

Need full control? FunctionFS lets you define your own USB descriptors, handle endpoints directly, implement proprietary protocols, and respond to USB lifecycle events (bind, unbind, enable, disable, suspend, resume).

The library includes HIDFunctionFs — a ready-made FunctionFS specialization for custom HID devices with complex report descriptors.


Quick Start #

1. Check hardware compatibility #

You need a device with a USB Device Controller (UDC). Desktop PCs typically don't have one — embedded boards like the Raspberry Pi do.

ls /sys/class/udc/
# Should list at least one device, e.g.: fe980000.usb

Tested devices: Raspberry Pi 4/5, Raspberry Pi Zero 2 W, OnePlus 7 Pro (Android)

2. Install the system dependency #

# Debian/Ubuntu
sudo apt-get install libaio-dev

# Arch Linux
sudo pacman -S libaio

# Fedora/RHEL
sudo dnf install libaio-devel

3. Add the package #

dart pub add usb_gadget

4. Mount ConfigFS (if needed) #

# Check if already mounted
mount | grep configfs

# Mount if not present
sudo mount -t configfs none /sys/kernel/config

Example: USB Keyboard #

import 'dart:io';
import 'dart:typed_data';

import 'package:usb_gadget/usb_gadget.dart';

final _descriptor = Uint8List.fromList([0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0, 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05, 0x07, 0x19, 0x00, 0x29, 0x65, 0x81, 0x00, 0xC0]);

class Keyboard extends HIDFunction {
  Keyboard() : super(
    name: 'keyboard',
    descriptor: _descriptor,
    protocol: .keyboard,
    subclass: .boot,
    reportLength: 8,
  );

  void sendKey(int keyCode, {int modifiers = 0}) =>
      file..writeFromSync(
        Uint8List(8)
          ..[0] = modifiers
          ..[2] = keyCode,
      )..writeFromSync(Uint8List(8));
}

Future<void> main() async {
  final keyboard = Keyboard();
  final gadget = Gadget(
    name: 'hid_keyboard',
    idVendor: 0x1234,
    idProduct: 0x5679,
    deviceClass: .composite,
    deviceSubClass: .none,
    deviceProtocol: .none,
    strings: {
          .enUS: const .new(
        manufacturer: 'ACME Corp',
        product: 'USB Keyboard',
        serialnumber: 'KB001',
      ),
    },
    config: .new(functions: [keyboard]),
  );

  try {
    await gadget.bind();
    await gadget.awaitState(.configured);
    // A short delay prevents the first few keypresses from being missed on some hosts.
    await Future<void>.delayed(const .new(milliseconds: 100));
    [0x0B, 0x08, 0x0F, 0x0F, 0x12, 0x2C, 0x1A, 0x12, 0x15, 0x0F, 0x07, 0x28]
    // Write "hello world\n"
    .forEach(keyboard.sendKey);
    stdout.writeln('Ctrl+C to exit.');
    await ProcessSignal.sigint.watch().first;
  } finally {
    await gadget.unbind();
  }
}

Run it #

# Development
sudo dart run bin/your_app.dart

# Compiled executable (recommended for deployment)
dart compile exe bin/your_app.dart -o my_gadget
sudo ./my_gadget

# With debug logging
dart compile exe -DUSB_GADGET_DEBUG=true bin/your_app.dart -o my_gadget
sudo ./my_gadget

More examples are available in the example/ directory.


Permissions #

Root or CAP_SYS_ADMIN is required to configure USB gadgets.

# Option A: run with sudo
sudo ./my_gadget

# Option B: grant capability to the binary (avoids full root)
sudo setcap cap_sys_admin+ep ./my_gadget
./my_gadget

Kernel Configuration #

Most modern embedded distributions already include the required options. To verify:

zcat /proc/config.gz | grep -E 'CONFIG_USB_(GADGET|CONFIGFS|FUNCTIONFS)'

Key options: CONFIG_USB_GADGET, CONFIG_USB_CONFIGFS, CONFIG_USB_FUNCTIONFS, plus any function-specific drivers you need (e.g. CONFIG_USB_CONFIGFS_F_HID for HID).


To incorporate the official Android build documentation into your guide, I have refined the steps to ensure the .gclient configuration is correct and added the necessary dependency installation step required for Linux hosts.

Android Support #

Important

Prerequisites: The host (build) machine must be an x64 Linux machine or a Mac. The target Android device must support the Android NDK. The resulting Dart VM can only be run from the Android command line and only has access to dart:core APIs — it does not have access to Android C or Java APIs.

Because there are no pre-built Dart SDKs for Android, you must cross-compile the SDK and libaio on your host machine before deploying to your device.

Expand Android setup instructions

1. Build the Dart SDK for Android #

First, follow the official Dart SDK build guide to set up depot_tools and fetch the Dart source tree.

Then, use a text editor to add the download_android_deps variable to your .gclient file (located in the directory where you ran fetch dart):

solutions = [
  {
    "name": "sdk",
    "url": "https://dart.googlesource.com/sdk.git",
    "deps_file": "DEPS",
    "managed": False,
    "custom_deps": {},
    "custom_vars": {
        "download_android_deps": True,
    },
  },
]

Then sync dependencies and build:

# Download Android NDK and SDK dependencies
gclient sync

# Build the full SDK for ARM64 Android
./tools/build.py --mode=release --arch=arm64 --os=android create_sdk

# Push the built SDK to your device
adb push out/android/ReleaseAndroidARM64/dart-sdk /data/local/tmp/dart-sdk

2. Cross-compile libaio #

libaio is required by this library but is not available via a package manager on Android. You must cross-compile it on your host using the Clang toolchain bundled with the Dart SDK's third-party dependencies.

git clone https://pagure.io/libaio.git && cd libaio

# Adjust the path below to match your Dart SDK checkout location
export TOOLCHAIN=$HOME/dart-sdk/sdk/third_party/android_tools/ndk/toolchains/llvm/prebuilt/linux-x86_64
export CC=$TOOLCHAIN/bin/aarch64-linux-android21-clang
export AR=$TOOLCHAIN/bin/llvm-ar
export RANLIB=$TOOLCHAIN/bin/llvm-ranlib

make clean && make CC="$CC" AR="$AR" RANLIB="$RANLIB"

# Deploy to device
adb push src/libaio.so.1.0.2 /data/local/tmp/
adb shell ln -s /data/local/tmp/libaio.so.1.0.2 /data/local/tmp/libaio.so

3. Compile and Execute On-Device #

# Upload your project
adb push /path/to/your/project /data/local/tmp/usb_gadget_project
adb shell
su

# Setup environment
export PATH=/data/local/tmp/dart-sdk/bin:$PATH
export PUB_CACHE=/data/local/tmp/.pub-cache/
cd /data/local/tmp/usb_gadget_project

# Fetch dependencies and compile
dart pub get
dart compile exe bin/your_app.dart -o usb_gadget_app
chmod +x usb_gadget_app

# Detach the UDC from the Android USB stack
ORIGINAL=$(getprop sys.usb.config)
setprop sys.usb.config none && sleep 1
mount -t configfs none /sys/kernel/config

# Run the app with the cross-compiled libaio
LD_LIBRARY_PATH=/data/local/tmp ./usb_gadget_app

# Cleanup: Restore USB config when finished
# setprop sys.usb.config "$ORIGINAL"

Resources #

0
likes
160
points
675
downloads

Publisher

unverified uploader

Weekly Downloads

Expose standard or fully custom USB peripherals (gadgets) through a USB device controller (UDC) on Linux.

Repository (GitHub)
View/report issues

Documentation

API reference

License

Apache-2.0 (license)

Dependencies

ffi, meta, using

More

Packages that depend on usb_gadget