I'm unable to successfully set the bDeviceClass, bDeviceProtocol, or bDeviceSubClass when configuring a device as an RNDIS gadget in /sys/kernel/config/usb_gadget/g1.

I'm configuring the gadget with the following values and can read them back successfully in Linux:

echo 0xef > bDeviceClass
echo 0x02 > bDeviceSubClass
echo 0x01 > bDeviceProtocol

But when I plug the device into my Windows machine and observe the USB device descriptor the I get the following:

bDeviceClass:    0x02
bDeviceSubClass: 0x00
bDeviceProtocol: 0x00

On a different device running the 4.9 kernel this same procedure works correctly and Windows reads the correct values from the device. My current device is running 4.14.53. I'm not sure why the previous method no longer works.

I'm not sure if it's related or not, but in order to pass along product, manufacturer and serialnumber information on my current device I had to pass in those values as arguments when modprobing g_ether, rather than populating the fields in strings/0x409. I have tried do the same thing with bDeviceClass, bDeviceSubclass, and bDeviceProtocol but haven't had any luck.

Does anyone have any idea as to what I'm doing wrong?

1 Answer 1


The solution was to modprobe the libcomposite module instead of g_ether directly. That allowed the USB device descriptors to bet set as expected.

This also requires the script to bind the gadget to UDC before exiting.

I've included an admittedly maximal example, but it's what I'm using and it's what works for me.


modprobe libcomposite

# Set up the USB Device
cd /sys/kernel/config/usb_gadget
mkdir g1
cd g1

# Make the necessary directories.
mkdir functions/rndis.usb0
mkdir configs/c.1
mkdir strings/0x409
mkdir configs/c.1/strings/0x409

echo 64 > bMaxPacketSize0
echo $BCDUSB > bcdUSB
echo $BCDDEVICE > bcdDevice
echo $VENDORID > idVendor
echo $PRODUCTID > idProduct
echo 0xef > bDeviceClass
echo 0x02 > bDeviceSubClass
echo 0x01 > bDeviceProtocol
echo $MANUFACTURER > strings/0x409/manufacturer
echo $PRODUCT > strings/0x409/product
echo $CONF > configs/c.1/strings/0x409/configuration
echo $SERNO > strings/0x409/serialnumber

echo 1 > os_desc/use
echo 0xcd > os_desc/b_vendor_code
echo MSFT100 > os_desc/qw_sign

echo RNDIS > functions/rndis.usb0/os_desc/interface.rndis/compatible_id
echo 5162001 > functions/rndis.usb0/os_desc/interface.rndis/sub_compatible_id

mkdir functions/rndis.usb0/os_desc/interface.rndis/Icons
echo 2 > functions/rndis.usb0/os_desc/interface.rndis/Icons/type
echo "%SystemRoot%\system32\ddores.dll,-134" > functions/rndis.usb0/os_desc/interface.rndis/Icons/data

mkdir functions/rndis.usb0/os_desc/interface.rndis/Label
echo 1 > functions/rndis.usb0/os_desc/interface.rndis/Label/type
echo $PRODUCT > functions/rndis.usb0/os_desc/interface.rndis/Label/data

ln -s configs/c.1 os_desc

ln -s functions/rndis.usb0 configs/c.1

echo 7600000.dwc3 > UDC
  • Could explain what you mean with "This also requires the script to bind the gadget to UDC before exiting." I'm trying to load the g_mass_storage gadget (after the libcomposite) and receive the log message "udc-core: couldn't find an available UDC - added [g_mass_storage] to list of pending drivers" in dmesg... I wonder if this is the problem you're pointing out here.
    – SeVe
    Sep 6, 2022 at 12:48
  • Could be. In the same script I use for RNDIS I also handle mass storage gadget access on-the-fly. If the UDC is already in use I have to clear it with echo "" > UDC, then symlink the mass storage function to the config folder, and then reenable the UDC. Sep 7, 2022 at 15:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .