Skip to content

Instantly share code, notes, and snippets.

@Gadgetoid
Last active February 20, 2023 09:35
Show Gist options
  • Star 47 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save Gadgetoid/c52ee2e04f1cd1c0854c3e77360011e2 to your computer and use it in GitHub Desktop.
Save Gadgetoid/c52ee2e04f1cd1c0854c3e77360011e2 to your computer and use it in GitHub Desktop.
Raspberry Pi Zero / Windows 10 automatic RNDIS driver install for composite gadgets

Preface

I owe my very rapid learning journey in the world of ConfigFs to several key sources which aren't necessarily relevant to this result, but I feel deserve a mention anyway.

The Result

One of the biggest and most repeated issues with the Pi Zero and OTG USB is that a composite gadget including RNDIS Ethernet for Ethernet-over-USB support in Windows will not automatically install drivers and, furthermore, is a heinous pain to install drivers for. My original idea was to develop and sign drivers to solve this problem- I still plan to do that, because the solution presented here is a hack - but it turned out signing inf files is really waaay more complex than it has any right to be.

So, using the resources above, plus some others (probably) which I've forgotten, and lots, and lots and lots of trial and error I finally accidentally hit upon a method to trick Windows 10 - repeatably I believe - into installing RNDIS drivers for a composite gadget.

The Technique

The technique is ridiculously simple;

  • Set up an RNDIS gadget using a VID/PID of a known good device that's compatible with composite RNDIS
  • Set bDeviceClass and bDeviceSubClass to 0x02 for a valid gadget
  • Set up the "os_desc" node with Windows magic (I don't think this is 100% necessary, but voodoo is voodoo!
  • Link only the RNDIS function to the config
  • Attach the USB gaget to the device
  • Wait some time for Windows to detect/install drivers- 5sec seems plenty as it happens, some experimentation required here
  • Detach the USB gadget
  • Link the rest of your functions- Mass Storage, Serial ACM, etc ( So far only tested with these )
  • Set bDeviceClass to 0x00
  • Re-attach the USB gadget
  • Now it should show up - in Windows 10 at least - as a composite gadget with functional RNDIS, Serial and Mass Storage. Yay!

Changelog

2017-11-05 - 00:26 - Tweaked to better support OSX. Still no RNDIS funcionality in 10.12, but Mass Storage and Serial seem to work

#!/bin/bash
# TODO: Figure out how to get ECM to work here
# without breaking Windows support
# Currently OSX supports Mass Storage + Serial but *not* RNDIS (at least not 10.12 anyway)
# Windows 10 and Linux seem to support everything
# Windows 8, 7 and below are untested
if [ ! -d /sys/kernel/config/usb_gadget ]; then
modprobe libcomposite
fi
if [ -d /sys/kernel/config/usb_gadget/g1 ]; then
exit 0
fi
ID_VENDOR="0x1d6b"
ID_PRODUCT="0x0104"
SERIAL="$(grep Serial /proc/cpuinfo | sed 's/Serial\s*: 0000\(\w*\)/\1/')"
MAC="$(echo ${SERIAL} | sed 's/\(\w\w\)/:\1/g' | cut -b 2-)"
MAC_HOST="12$(echo ${MAC} | cut -b 3-)"
MAC_DEV="02$(echo ${MAC} | cut -b 3-)"
cd /sys/kernel/config/usb_gadget/
mkdir g1
cd g1
echo "0x0200" > bcdUSB
echo "0x02" > bDeviceClass
echo "0x00" > bDeviceSubClass
echo "0x3066" > bcdDevice
echo $ID_VENDOR > idVendor
echo $ID_PRODUCT > idProduct
# Windows extensions to force config
echo "1" > os_desc/use
echo "0xcd" > os_desc/b_vendor_code
echo "MSFT100" > os_desc/qw_sign
mkdir strings/0x409
echo "9112473" > strings/0x409/serialnumber
echo "Pimoroni Ltd." > strings/0x409/manufacturer
echo "PiratePython" > strings/0x409/product
# Config #1 for OSX / Linux
mkdir configs/c.1
mkdir configs/c.1/strings/0x409
echo "CDC 2xACM+Mass Storage+RNDIS" > configs/c.1/strings/0x409/configuration
mkdir functions/acm.GS0
mkdir functions/acm.GS1
#mkdir functions/ecm.usb0 # OSX/Linux
mkdir functions/rndis.usb0 # Flippin' Windows
mkdir functions/mass_storage.piratepython
echo "/dev/mmcblk0p1" > functions/mass_storage.piratepython/lun.0/file
echo 0 > functions/mass_storage.piratepython/stall
echo 0 > functions/mass_storage.piratepython/lun.0/cdrom
echo 0 > functions/mass_storage.piratepython/lun.0/nofua
echo 1 > functions/mass_storage.piratepython/lun.0/removable
echo "PiratePython" > functions/mass_storage.piratepython/lun.0/inquiry_string
echo "RNDIS" > functions/rndis.usb0/os_desc/interface.rndis/compatible_id
echo "5162001" > functions/rndis.usb0/os_desc/interface.rndis/sub_compatible_id
echo $MAC_HOST > functions/rndis.usb0/host_addr
echo $MAC_DEV > functions/rndis.usb0/dev_addr
# Set up the rndis device only first
ln -s functions/rndis.usb0 configs/c.1
# Tell Windows to use config #2
ln -s configs/c.1 os_desc
# Show Windows the RNDIS device with
# bDeviceClass 0x02
# bDeviceSubClass 0x02
echo "20980000.usb" > UDC
# Give it time to install
sleep 5
# Yank it back
echo "" > UDC
# Sneak in all the extra goodies
ln -s functions/acm.GS0 configs/c.1
ln -s functions/acm.GS1 configs/c.1
ln -s functions/mass_storage.piratepython configs/c.1
# Reset bDeviceClass to 0x00
# This is essential to make it work in Windows 10
# Basically forces it to use device information
# in the descriptors versus assuming a particular class.
echo "0x00" > bDeviceClass
# Re-attach the gadget
echo "20980000.usb" > UDC
# BOOM!
ifconfig usb0 up 10.0.99.1
@Gadgetoid
Copy link
Author

Note: This technique doesn't work at all for OSX. Seems to be fine in both Linux (tested in Ubuntu 17.10 and Windows 10 though. I'm experimenting with OSX fixes, but it's a delicate hack. Might just be easier to use a ground-up alternate config depending on host. Not ideal though!

@andrewwakeling
Copy link

Thanks for putting together this gist with links and sample config!

It's apparently possible to detect the OS during the USB setup and modify the config appropriately. This is what HackPi does. (I haven't confirmed how well this work myself though).

@rohtua
Copy link

rohtua commented Feb 24, 2018

Thanks for this. I've driven myself mad trying to get this working on my pi0. Using your sample configuration above it works for me :) the only thing is when I plug the pi in I get a pop message saying the USB device didn't start properly and then I get a few beeps as though I'm plugging a device in and out then it loads properly. Is it supposed to do that or do I need to change something in the script???

@GavinDarkglider
Copy link

It took me a few days messing with the USB composite devices on my UP Board, which also has USB OTG like the pi. The solution actually had less to do with the os_desc stuff, and more to do with the order libcomposite loads the modules into the kernel. If you load libcomposite, it loads in the gadget modules when you create the devices in configfs. Unfortunatly, windows wants RNDIS to take up the 0 and 1 slot in the the composite device header. libcomposite sets these values based upon the order the modules are loaded. So, if OS_Desc is set properly, and you force the usb_f_rndis module to load right after libcomposite, but before any of the other usb_f_* modules, then it doesnt matter what vid/pid combination you use, Windows will find it, set it up, and start the device. ;) I am in the process of writing an easy setup script for all of the various gadgets. I currently have HID,RNDIS,Serial ACM, ECM Networking, and Mass Storage all working properly in multiple composite arangements, with no problems in windows 7. Havnt tested in 8 or 10, bu I think if it works in 7, then chances are it will work in 8 or 10.

@x821938
Copy link

x821938 commented Apr 25, 2018

Nice job! It works like a charm on the Raspberry Pi Zero :)

GavidDarkGlider: I currently have HID,RNDIS,Serial ACM, ECM Networking, and Mass Storage all working properly in multiple composite arangements, with no problems in windows 7

I am figthing to get RNDIS and HID working at the same time. Do you have an idea how to get that working?

@mame82
Copy link

mame82 commented Apr 28, 2018

Hi,

I'm running the P4wnP1 project and solved this issue over a year ago.

I'm using a composite device with RNDIS and additional gadget funtions (could be enabled optional).

The keys for proper PnP RNDIS on Win 10:

  • use inly ONE configuration
  • add Microsoft OS descriptors
  • in case of multilple compisite functions, RNDIS has to be the first one
  • if the VID+PID have been in use with misding or wrong IS descriptors, they're never enumerated again by Windows (cached in registry hive). So these reg keys have to be removed or the PID or VID have to be changed

Working code:
https://github.com/mame82/P4wnP1/blob/master/boot/init_usb.sh

Details on the registry caching of OS descriptors in this line:
https://github.com/mame82/P4wnP1/blob/master/boot/init_usb.sh#L150

Cheers

@nunojpg
Copy link

nunojpg commented Oct 22, 2018

Hi @mame82, I've tried without success your concept. Can you confirm if specific VID/PID are required or if any .INF installation is required at Windows 10? Thanks!

@aurelihein
Copy link

Since I see that you have modified your script 12 days ago, you should try this one : https://gist.github.com/geekman/5bdb5abdc9ec6ac91d5646de0c0c60c4 which for me is working under Windows 10

@jdevelop
Copy link

I tried this script and the one posted by @mame82 / @aurelihein - on Windows 10 the device appears under "Other devices" as RNDIS, but Windows can't find driver for it.

I wonder if anyone else faces this issue?

@Roy996
Copy link

Roy996 commented Oct 27, 2021

I tried this script and the one posted by @mame82 / @aurelihein - on Windows 10 the device appears under "Other devices" as RNDIS, but Windows can't find driver for it.

I wonder if anyone else faces this issue?

Did you solve it? I got the same problem now.

@AddaxSoft
Copy link

for people who dont want to bother you can get the driver from here: https://modclouddownloadprod.blob.core.windows.net/shared/mod-rndis-driver-windows.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment