Skip to content

Creating a USB Composite Multi-Function Gadget on Raspberry Pi using ConfigFS

Learn how to configure your Raspberry Pi as a multi-functional USB device! Using Linux ConfigFS, you can share a storage volume, emulate an ethernet connection, and provide a serial console simultaneously over a single USB cable.

Introduction

Legacy Linux USB gadget modules (like g_ether or g_mass_storage) only allow you to emulate one device type at a time. If you want your Raspberry Pi to act as both a network interface and a USB drive, you must use ConfigFS (specifically, the libcomposite driver).

ConfigFS allows you to build a USB Composite Device. When plugged into a computer, it shows up as multiple independent devices connected through a virtual USB hub.


Requirements & Setup

Make sure your Raspberry Pi model supports USB OTG (Pi Zero, Zero W, Zero 2 W, or Pi 4) and that you are using a USB data cable.

1. Enable USB Drivers

Edit the boot config file:

sudo nano /boot/firmware/config.txt
Add this overlay at the bottom:
dtoverlay=dwc2

Next, add the kernel composite modules to load at boot:

sudo nano /etc/modules
Add the following line (unlike the legacy driver, we use libcomposite here):
dwc2
libcomposite
Save and exit.

2. Create the ConfigFS Gadget Script

The configuration of a composite device is handled entirely by creating directory structures in /sys/kernel/config/usb_gadget/.

We will write a shell script to set up a composite gadget that provides USB Ethernet (RNDIS/CDC) and a Virtual USB Storage Drive.

  1. Create the configuration script:
    sudo nano /usr/bin/enable-composite-gadget.sh
    
  2. Paste the following script:
    #!/bin/bash
    # Set up a Multi-Function USB Composite Gadget
    
    # 1. Create a virtual disk image to share (if not already created)
    if [ ! -f /shared_storage.bin ]; then
        # Create a 256MB raw storage file
        dd if=/dev/zero of=/shared_storage.bin bs=1M count=256
        mkdosfs /shared_storage.bin -F 32 -I
    fi
    
    # 2. Enter ConfigFS gadget tree
    cd /sys/kernel/config/usb_gadget/
    mkdir -p multi_gadget
    cd multi_gadget
    
    # 3. Setup USB descriptors
    echo 0x1d6b > idVendor  # Linux Foundation
    echo 0x0104 > idProduct # Multifunction Composite Gadget
    echo 0x0100 > bcdDevice
    echo 0x0200 > bcdUSB
    
    mkdir -p strings/0x409
    echo "1234567890" > strings/0x409/serialnumber
    echo "Raspberry Pi" > strings/0x409/manufacturer
    echo "Pi Composite Gadget" > strings/0x409/product
    
    # 4. Define functions
    
    # Function A: USB Ethernet (CDC-ECM for macOS/Linux, RNDIS for Windows compatibility)
    mkdir -p functions/ecm.usb0
    
    # Function B: USB Mass Storage
    mkdir -p functions/mass_storage.usb0
    echo 1 > functions/mass_storage.usb0/stall
    echo /shared_storage.bin > functions/mass_storage.usb0/lun.0/file
    echo 1 > functions/mass_storage.usb0/lun.0/removable
    
    # 5. Bind functions to configurations
    mkdir -p configs/c.1/strings/0x409
    echo "Composite Configuration" > configs/c.1/strings/0x409/configuration
    echo 250 > configs/c.1/bmAttributes
    echo 500 > configs/c.1/MaxPower
    
    # Link both functions to this configuration
    ln -s functions/ecm.usb0 configs/c.1/
    ln -s functions/mass_storage.usb0 configs/c.1/
    
    # 6. Enable the composite gadget
    ls /sys/class/udc > UDC
    
  3. Make the script executable:
    sudo chmod +x /usr/bin/enable-composite-gadget.sh
    

3. Enable Service at Boot

Run the script automatically when the system starts by wrapping it in a systemd service.

  1. Create the service:
    sudo nano /etc/systemd/system/usb-composite.service
    
  2. Paste the following configuration:
    [Unit]
    Description=USB Composite Gadget Installer
    After=local-fs.target
    
    [Service]
    Type=oneshot
    ExecStart=/usr/bin/enable-composite-gadget.sh
    RemainAfterExit=yes
    
    [Install]
    WantedBy=multi-user.target
    
  3. Enable the service:
    sudo systemctl enable usb-composite.service
    
  4. Reboot the Raspberry Pi:
    sudo reboot
    

4. Connecting and Configuring the Host

Once rebooted, connect the Pi to your computer:

  • As a USB Storage Drive: Your host PC will instantly mount a new 256MB flash drive.
  • As an Ethernet Card: The host PC will detect a new virtual network adapter.
    • To access your Pi over the network, you must configure a static IP on the Pi's usb0 interface.

To assign a static IP to the virtual network adapter on the Pi, edit /etc/dhcpcd.conf (or use Netplan / NetworkManager depending on your OS version) and assign 192.168.7.1 to the usb0 interface. Then, you can access the Pi via SSH at username@192.168.7.1.


Troubleshooting

  • Host does not recognize the Ethernet adapter (Windows):
    • Windows occasionally struggles to recognize ConfigFS virtual ethernet adapters by default. You may need to manually open the Device Manager, find the unknown network device, and update its driver choosing the "Remote NDIS (RNDIS) Compatible Device" driver from the Windows system driver list.
  • File System Conflicts:
    • Warning: Do not mount /shared_storage.bin on both the host computer and the Raspberry Pi simultaneously in write-mode. FAT32 does not support concurrent write access, and doing so will corrupt the filesystem. If you need the Pi to read files written by the host, unmount the drive on the host PC first, then mount it on the Pi.