Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save TalalMash/c20e6aa237e1f123ddf9686a07a1bfed to your computer and use it in GitHub Desktop.

Select an option

Save TalalMash/c20e6aa237e1f123ddf9686a07a1bfed to your computer and use it in GitHub Desktop.
USB ethernet gadget on Raspberry Pi OS example compatible with Windows 10 1709+ (driverless), MacOS, iOS 13+, Android

USB ethernet gadget on Raspberry Pi OS compatible with:

  • Windows 10 1709+ (driverless)
  • MacOS
  • iOS 13+
  • Android (RNDIS is used for Android 6 and below) *1
  • ChromeOS *2
  • Linux 4.x+ *2

*1 Android will not request for an IP address if gateway is empty or internet is not found after the first connection, requiring a reboot. To mitigate, add captive portal redirect page or toggle airplane mode.

References:

https://jordemort.dev/blog/why-android-cant-use-cdc-ethernet/

https://news.ycombinator.com/item?id=44219405#:~:text=I%20wrote%20this,a%20CDC%20adapter)

TODO

  • *2 Prefer EEM/ECM for Linux by taking down RNDIS on link up hotplug event
#!/bin/sh
set -e
BOOT_CFG="/boot/firmware/config.txt"
CMDLINE="/boot/firmware/cmdline.txt"
grep -q '^dtoverlay=dwc2' "$BOOT_CFG" 2>/dev/null || echo 'dtoverlay=dwc2' >> "$BOOT_CFG"
if [ -f "$CMDLINE" ]; then
grep -q 'dwc2' "$CMDLINE" || sed -i '1 s/$/ modules-load=dwc2,libcomposite/' "$CMDLINE"
fi
cat >/usr/local/sbin/usb-gadget.sh <<'SH'
#!/bin/sh
set -e
CONFIGFS=/sys/kernel/config/usb_gadget
G=custom
mountpoint -q /sys/kernel/config || mount -t configfs none /sys/kernel/config
modprobe libcomposite
cd "$CONFIGFS"
if [ -d "$G" ]; then
echo "" > "$G/UDC" 2>/dev/null || true
sleep 1
rm -rf "$G"
fi
mkdir "$G"
cd "$G"
echo "0x0200" > bcdUSB
echo "0x02" > bDeviceClass
echo "0x00" > bDeviceSubClass
echo "0x3066" > bcdDevice
echo 0x1d6b > idVendor
echo 0x0104 > idProduct
mkdir -p strings/0x409
echo "9112473" > strings/0x409/serialnumber
echo "Example" > strings/0x409/manufacturer
echo "Example Pi Edition" > strings/0x409/product
mkdir -p configs/c.1/strings/0x409
echo "CDC ECM+RNDIS" > configs/c.1/strings/0x409/configuration
mkdir -p functions/ecm.usb0
mkdir -p functions/rndis.usb0
echo "RNDIS" > functions/rndis.usb0/os_desc/interface.rndis/compatible_id
echo "5162001" > functions/rndis.usb0/os_desc/interface.rndis/sub_compatible_id
echo "b8:27:eb:c3:ba:09" > functions/rndis.usb0/host_addr
echo "f0:fe:9f:95:db:0a" > functions/rndis.usb0/dev_addr
echo "b8:27:eb:e8:05:0f" > functions/ecm.usb0/host_addr
echo "e4:d5:8e:eb:20:36" > functions/ecm.usb0/dev_addr
ln -s functions/rndis.usb0 configs/c.1/ 2>/dev/null || true
ln -s functions/ecm.usb0 configs/c.1/ 2>/dev/null || true
mkdir -p os_desc
ln -s configs/c.1 os_desc/c.1 2>/dev/null || true
echo 0x00 > bDeviceClass
for i in $(seq 1 50); do
UDC=$(ls /sys/class/udc 2>/dev/null || true)
[ -n "$UDC" ] && break
sleep 0.1
done
echo "$UDC" > UDC
ip link add name br-usb type bridge 2>/dev/null || true
ip link set br-usb up 2>/dev/null || true
ip link set usb0 up 2>/dev/null || true
ip link set usb1 up 2>/dev/null || true
ip link set usb0 master br-usb 2>/dev/null || true
ip link set usb1 master br-usb 2>/dev/null || true
ip addr add 10.0.95.1/29 dev br-usb 2>/dev/null || true
SH
chmod +x /usr/local/sbin/usb-gadget.sh
cat >/etc/systemd/system/usb-gadget.service <<'SRV'
[Unit]
Description=USB Gadget ECM+RNDIS Bridge
After=systemd-modules-load.service local-fs.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/usb-gadget.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
SRV
systemctl daemon-reload
systemctl enable usb-gadget.service
systemctl start usb-gadget.service
#/etc/dnsmasq.d/usb-interfaces.conf
#Android won't setup routes without DHCP IPv4 on some phones, IPv6 only does not work.
interface=br-usb
dhcp-range=10.0.98.10,10.0.98.200,12h
bind-interfaces
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment