Skip to content

Instantly share code, notes, and snippets.

@dafta
Last active June 26, 2024 18:49
Show Gist options
  • Save dafta/0aadeba3aa8bcbbc8b92a233977571ed to your computer and use it in GitHub Desktop.
Save dafta/0aadeba3aa8bcbbc8b92a233977571ed to your computer and use it in GitHub Desktop.
Steam Deck USB Ethernet
#!/bin/sh
if [ "$UID" -ne 0 ]; then
echo "This script needs to be executed as root"
exit 1
fi
vendor_id="0x3000" # Valve
product_id="0x28DE"
serial_number="$(dmidecode -s system-serial-number)" # The Steam Deck's serial number
manufacturer="Valve" # Manufacturer
product="Steam Deck" # Product
device="0x1004" # Device version
usb_version="0x0200" # USB 2.0
device_class="2" # Communications
cfg1="CDC" # Config 1 description
cfg2="RNDIS" # Config 2 description
power=250 # Max power
dev_mac1="42:61:64:55:53:42"
host_mac1="48:6f:73:74:50:43"
dev_mac2="42:61:64:55:53:44"
host_mac2="48:6f:73:74:50:45"
ms_vendor_code="0xcd" # Microsoft
ms_qw_sign="MSFT100" # Microsoft
ms_compat_id="RNDIS" # Matches Windows RNDIS drivers
ms_subcompat_id="5162001" # Matches Windows RNDIS 6.0 driver
cdc_mode="ecm" # Which CDC gadget to use
start_rndis=true # Whether to start the Microsoft RNDIS gadget
while getopts "ncerR" option ${@:2}; do
case "${option}" in
"n")
cdc_mode=ncm
;;
"c")
cdc_mode=ecm
;;
"e")
cdc_mode=eem
;;
"r")
start_rndis=true
;;
"R")
start_rndis=false
;;
esac
done
case "$1" in
start)
# Create the networkd config file for the USB interface
cat << EOF > /etc/systemd/network/usb0.network
[Match]
Name=usb0
[Network]
Address=192.168.100.1/24
DHCPServer=true
IPMasquerade=ipv4
[DHCPServer]
PoolOffset=100
PoolSize=20
EmitDNS=yes
DNS=8.8.8.8
EOF
cat << EOF > /etc/systemd/network/usb1.network
[Match]
Name=usb1
[Network]
Address=192.168.101.1/24
DHCPServer=true
IPMasquerade=ipv4
[DHCPServer]
PoolOffset=100
PoolSize=20
EmitDNS=yes
DNS=8.8.8.8
EOF
# Start networkd
systemctl start systemd-networkd
# Enable DRD driver
echo -n "0000:04:00.3" > /sys/bus/pci/drivers/xhci_hcd/unbind
echo -n "0000:04:00.3" > /sys/bus/pci/drivers/dwc3-pci/bind
# Load the drivers
modprobe libcomposite
# Create the gadget
mkdir /sys/kernel/config/usb_gadget/g.1
cd /sys/kernel/config/usb_gadget/g.1
# Specify the vendor and product ID
echo "${vendor_id}" > idVendor
echo "${product_id}" > idProduct
# Create the gadget configuration
mkdir configs/c.1
# Create the strings directories
mkdir strings/0x409
mkdir configs/c.1/strings/0x409
# Specify the serial number, manufacturer, and product strings
echo "${serial_number}" > strings/0x409/serialnumber
echo "${manufacturer}" > strings/0x409/manufacturer
echo "${product}" > strings/0x409/product
# Specify the device version, USB specification, and device class
echo "${device}" > bcdDevice
echo "${usb_version}" > bcdUSB
echo "${device_class}" > bDeviceClass
# Set the configuration description and power
echo "${cfg1}" > configs/c.1/strings/0x409/configuration
echo "${power}" > configs/c.1/MaxPower
# Create the gadget function
mkdir functions/${cdc_mode}.0
# Set the MAC addresses of the gadget
echo "${host_mac1}" > functions/${cdc_mode}.0/host_addr
echo "${dev_mac1}" > functions/${cdc_mode}.0/dev_addr
# Start RNDIS if enabled
if [ "${start_rndis}" = true ]; then
# Create the gadget configuration
mkdir configs/c.2
# Create the strings directories
mkdir configs/c.2/strings/0x409
# Specify the configuration description and power
echo "${cfg2}" > configs/c.2/strings/0x409/configuration
echo "${power}" > configs/c.2/MaxPower
# Set some Microsoft specific configuration
echo "1" > os_desc/use
echo "${ms_vendor_code}" > os_desc/b_vendor_code
echo "${ms_qw_sign}" > os_desc/qw_sign
# Create the gadget function
mkdir functions/rndis.0
# Set the MAC addresses of the gadget
echo "${host_mac2}" > functions/rndis.0/host_addr
echo "${dev_mac2}" > functions/rndis.0/dev_addr
# Set the RNDIS driver version
echo "${ms_compat_id}" > functions/rndis.0/os_desc/interface.rndis/compatible_id
echo "${ms_subcompat_id}" > functions/rndis.0/os_desc/interface.rndis/sub_compatible_id
fi
# Associate the CDC function with its configuration
ln -s functions/${cdc_mode}.0 configs/c.1/
# Associate the RNDIS function with its configuration
if [ "${start_rndis}" = true ]; then
ln -s functions/rndis.0 configs/c.2
ln -s configs/c.2 os_desc
fi
# Enable the gadget
ls /sys/class/udc > UDC
;;
stop)
# Disable the gadget
cd /sys/kernel/config/usb_gadget/g.1
echo "" > UDC
# Remove functions from the configuration
rm configs/c.1/ncm.0 2> /dev/null
rm configs/c.1/ecm.0 2> /dev/null
rm configs/c.1/eem.0 2> /dev/null
rm configs/c.2/rndis.0 2> /dev/null
# Remove the strings directories in configurations
rmdir configs/c.1/strings/0x409
rmdir configs/c.2/strings/0x409 2> /dev/null
# Remove the configurations
rmdir configs/c.1
rm os_desc/c.2 2> /dev/null
rmdir configs/c.2 2> /dev/null
# Remove the functions
rmdir functions/ncm.0 2> /dev/null
rmdir functions/ecm.0 2> /dev/null
rmdir functions/eem.0 2> /dev/null
rmdir functions/rndis.0 2> /dev/null
# Remove the strings directories in the gadget
rmdir strings/0x409
# Delete the gadget
cd ..
rmdir g.1
# Unload the drivers
cd ../../
modprobe -r usb_f_ncm
modprobe -r usb_f_ecm
modprobe -r usb_f_eem
modprobe -r usb_f_rndis
modprobe -r libcomposite
# Disable DRD driver
echo -n "0000:04:00.3" > /sys/bus/pci/drivers/dwc3-pci/unbind
echo -n "0000:04:00.3" > /sys/bus/pci/drivers/xhci_hcd/bind
# Stop networkd
systemctl stop systemd-networkd 2> /dev/null
# Remove the networkd config files for USB interfaces
rm /etc/systemd/network/usb0.network
rm /etc/systemd/network/usb1.network
;;
*)
echo "Usage:"
echo -e "\t./usb-ether.sh start\tStarts the USB ethernet"
echo -e "\t\t-n\tUse the CDC-NCM USB Ethernet driver (for OSX and iOS)"
echo -e "\t\t-c\tUse the CDC-ECM USB Ethernet driver (default)"
echo -e "\t\t-e\tUse the CDC-EEM USB Ethernet driver"
echo -e "\t\t-r\tEnable the RNDIS USB Ethernet driver for Windows (default)"
echo -e "\t\t-R\tDisable the RNDIS USB Ethernet driver for Windows"
echo -e "\t./usb-ether.sh stop - Stops the USB ethernet"
;;
esac
@YemingMeng
Copy link

YemingMeng commented Mar 1, 2023

Nothing happens after executing the script, I found the directory /sys/class/udc is empty.
Are there any pre-requirements before running the script, or is a reboot required?

And I got the following error message after running ./usb-ether.sh stop,

./usb-ether.sh: line 173: echo: write error: No such device

@dafta
Copy link
Author

dafta commented Mar 2, 2023

@YemingMeng Did you enable USB Dual-Role Device in BIOS?

@YemingMeng
Copy link

@YemingMeng Did you enable USB Dual-Role Device in BIOS?

Well, the script works as expected after enabling USB Dual-Role Device, thanks a lot.

@L1Z3
Copy link

L1Z3 commented Jul 18, 2023

This script was working for me, but after updating to SteamOS 3.5 (Main Channel) and then downgrading back to Stable (which upgraded the BIOS), it no longer works for me. I have ensured DRD is still enabled in the BIOS.

@parkerlreed
Copy link

BIOS 116 is busted for DRD. Just have to wait for an update or manaully downgrade to 115

@L1Z3
Copy link

L1Z3 commented Jul 18, 2023

Alright. I needed to upgrade the BIOS to fix another issue on my Deck, so I guess I'll just have to wait for an update.

@L1Z3
Copy link

L1Z3 commented Oct 22, 2023

I updated to BIOS 118 and it still doesn't seem to work. Any ideas?

DRD is enabled in the BIOS, but when running sudo ./usb-ether.sh stop I get:
./usb-ether.sh: line 173: echo: write error: No such device

EDIT: I tried downgrading to BIOS 115, but SteamOS auto-updated to BIOS 116. I then manually flashed BIOS 118, and now DRD works.

@MYSK-D25N
Copy link

EDIT: I tried downgrading to BIOS 115, but SteamOS auto-updated to BIOS 116. I then manually flashed BIOS 118, and now DRD works.

Sometime ago the script stopped working for me too. After downgrading BIOS from 120 to 118 today, everything works as intended.

@dafta
Copy link
Author

dafta commented Feb 14, 2024

BIOS 221 should also work.

@ArcaneNibble
Copy link

I'm not using this exact script, but in order to make gadget mode work on BIOS F7A0120 I needed the following commands:

echo -n "0000:04:00.3" > /sys/bus/pci/drivers/xhci_hcd/unbind
echo -n "0000:04:00.3" > /sys/bus/pci/drivers/dwc3-pci/bind

@dafta
Copy link
Author

dafta commented Mar 6, 2024

I'm not using this exact script, but in order to make gadget mode work on BIOS F7A0120 I needed the following commands:

echo -n "0000:04:00.3" > /sys/bus/pci/drivers/xhci_hcd/unbind
echo -n "0000:04:00.3" > /sys/bus/pci/drivers/dwc3-pci/bind

This is with DRD enabled in the BIOS?

@ArcaneNibble
Copy link

I'm not using this exact script, but in order to make gadget mode work on BIOS F7A0120 I needed the following commands:

echo -n "0000:04:00.3" > /sys/bus/pci/drivers/xhci_hcd/unbind
echo -n "0000:04:00.3" > /sys/bus/pci/drivers/dwc3-pci/bind

This is with DRD enabled in the BIOS?

Yes, the BIOS setting still has to be DRD

@dafta
Copy link
Author

dafta commented Mar 7, 2024

I'm not using this exact script, but in order to make gadget mode work on BIOS F7A0120 I needed the following commands:

echo -n "0000:04:00.3" > /sys/bus/pci/drivers/xhci_hcd/unbind
echo -n "0000:04:00.3" > /sys/bus/pci/drivers/dwc3-pci/bind

This is with DRD enabled in the BIOS?

Yes, the BIOS setting still has to be DRD

Thanks a ton for figuring this out, this will be a huge help.

@parkerlreed
Copy link

I'm not using this exact script, but in order to make gadget mode work on BIOS F7A0120 I needed the following commands:

echo -n "0000:04:00.3" > /sys/bus/pci/drivers/xhci_hcd/unbind
echo -n "0000:04:00.3" > /sys/bus/pci/drivers/dwc3-pci/bind

THANK YOU, This is tremendously helpful.

@MYSK-D25N
Copy link

The abofe gist is for sharing the internet Steam Deck has with some other device that'll be getting its internet access via the Steam Deck.

If you want the opposite, i.e. your Steam Deck using internet from another device, use this gist:

https://gist.github.com/cheater/00279e5b3dea743e14cf7bcd57f6c7fa

Basically it does two things:

  1. disables its own dhcp server
  2. instead of a fixed address, gets its ethernet address from the dhcp server run by the other computer.

Is there any way to get this working while docked? The usb-hub which I use has a USB-C port for data transfers, but upon trying it doesn't seem to work. Maybe I could point it to the port by changing something in the script, I don't really know. Would be cool if possible though.

@dafta
Copy link
Author

dafta commented Apr 25, 2024

@MYSK-D25N Most likely no. To use a dock, the USB port needs to be in host mode, while this script requires the port to be in client mode. There's no way around this, by using this script the USB port goes into client mode and requires the other end to be a PC or device in host mode.

The only way this would work is if your dock exposes a USB device controller (UDC) to the deck. You can check if you have more than one entry in /sys/class/udc, and if you do, you would echo the name of that other UDC to the file in line 167 of the script. But it's a question if the USB C port has Dual-Role Device support, and if the dock has firmware that enables that.

Good luck, and post a reply if you succeed.

@MYSK-D25N
Copy link

@dafta Thanks for the swift reply. It does seem unlikely that my dock is capable of that, there was one entry in UDC folder while nothing is plugged, and then no entries while docked.

Also looked around in dmesg output for clues, the dock shows up there as a "xHCI Host Controller" with the manufacturer as "Linux 6.1.52-valve16-1-neptune-61 xhci-hcd", despite DRD being ON. Didn't find if this can be changed somehow, probably not.

So yeah, doesn't really seem to work, but thanks for clarifying nonetheless.

@EpicestGamer
Copy link

Is there any chance of this being made into a Decky plugin like mentioned here?

@dafta
Copy link
Author

dafta commented Apr 29, 2024

I'm already working on it, but progress is slow when DRD breaks every other update by Valve.

@EpicestGamer
Copy link

Completely fair, glad to hear it. Thank you for all your hard work!

@indianaliam1
Copy link

so i'm no linux genius, is this an install or a no-install?

@dafta
Copy link
Author

dafta commented May 8, 2024

@indianaliam1 No install, just download and run the script from a terminal.

@kolanski
Copy link

seems didnt work with last update

@heathbarpunch
Copy link

heathbarpunch commented Jun 15, 2024

@kolanski

This from above fixed it for me on the current version:

echo -n "0000:04:00.3" > /sys/bus/pci/drivers/xhci_hcd/unbind
echo -n "0000:04:00.3" > /sys/bus/pci/drivers/dwc3-pci/bind

I had thought I had done this back in May but if I just do sudo then these commands it didn't work. I needed to use "sudo -i" to go into the root user account outright, issue these commands, and then issue the command "exit" to leave and go back to my normal user account.

if you issue the command "sudo lspci -v" from your usual user account you'll be able to see what driver is being used for your different devices, going down to 04.00.3 you'll be able to see if it says xhci or dwc3 as the active driver. For DRD to be the one you are using it needs to show dwc3 as the active driver instead of the xhci.

Ensuring that DRD is selected in the UEFI doesn't seem to be enough to get it to actually use DRD instead of XHCI in the current version.

Edit: Yep, I just disabled, booted, checked, re-enabled, booted, checked and all enabling DRD seems to do in latest builds is allow dwc3 to be an option, but not actually make it the active option, and the script can't make it the active driver either.

@dafta
Copy link
Author

dafta commented Jun 15, 2024

but if I just do sudo then these commands it didn't work. I needed to use "sudo -i" to go into the root user account outright, issue these commands, and then issue the command "exit" to leave and go back to my normal user account.

This is because of how bash works. When you do the above commands with sudo, it's the echo command that's run as root, but the redirect to file is done as the normal bash user and therefore doesn't have permission to write to that file. You can avoid this by replacing the file redirect with the tee command, like this:

echo -n "0000:04:00.3" | sudo tee /sys/bus/pci/drivers/xhci_hcd/unbind
echo -n "0000:04:00.3" | sudo tee /sys/bus/pci/drivers/dwc3-pci/bind

Notice that only tee has sudo in front of it, and not echo.

and the script can't make it the active driver either.

Not sure what you mean by this. In it's current state, yeah it doesn't, but if you add the two commands to the start and end respectively (the original version, you don't need the tee command here because the whole script is run as root), then the script would handle making the correct driver active.

@kolanski
Copy link

@dafta wow, your approach works, all i need its dhcp server in steamdeck, thank you!

@kolanski
Copy link

image
It depends on the cable, speed is better than USB 2.0

@dafta
Copy link
Author

dafta commented Jun 18, 2024

@dafta wow, your approach works, all i need its dhcp server in steamdeck, thank you!

Does the DHCP server not work for you? It should be included in systemd-networkd, and the configuration that's included in this script is supposed to enable the DHCP server on that network, lines 60 and 76.

@kolanski
Copy link

@dafta wow, your approach works, all i need its dhcp server in steamdeck, thank you!

Does the DHCP server not work for you? It should be included in systemd-networkd, and the configuration that's included in this script is supposed to enable the DHCP server on that network, lines 60 and 76.

I have no idea what happens with him, so I just use static address, also I executed this in terminal on steamdeck sudo ip addr add 192.168.100.2/24 dev usb0 for static ip
sudo ip link set usb0 up

@dafta
Copy link
Author

dafta commented Jun 18, 2024

Okay, that's interesting. I'll try and see if it works for me.

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