Skip to content

Instantly share code, notes, and snippets.

@dafta
Last active September 13, 2024 22:23
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
@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.

@FrostEgiant
Copy link

So for the Arch Linux noobs who have stumbled over this script, could I talk someone here into writing up a step-by-step for this? I've done my best to google my way through things-- I'm into hour six of trying-- but I haven't had any luck with it. The script ran, but I honestly don't know what to do next. The deck shows a wired connection (Io) as well as wireless, but either something went wrong halfway through, or something's messed up with the Io connection settings. Ideally I'd like to get it set up so that I can click a shortcut(s?) on the desktop as a toggle, but just really ANY kind of help or guide would be much appreciated. Steam Link over USB is a tantalizing prospect, so I have a hunch I won't be the only one to end up in the weeds on this. Help, pretty please?

@kolanski
Copy link

So for the Arch Linux noobs who have stumbled over this script, could I talk someone here into writing up a step-by-step for this? I've done my best to google my way through things-- I'm into hour six of trying-- but I haven't had any luck with it. The script ran, but I honestly don't know what to do next. The deck shows a wired connection (Io) as well as wireless, but either something went wrong halfway through, or something's messed up with the Io connection settings. Ideally, I'd like to get it set up so that I can click a shortcut(s?) on the desktop as a toggle, but just really ANY kind of help or guide would be much appreciated. Steam Link over USB is a tantalizing prospect, so I have a hunch I won't be the only one to end up in the weeds on this. Help, pretty please?

there are 2 steps you need to additionally:

  1. before running script use
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
  1. after running the script with the correct parameter (it depends on the client OS you want to use) you need to configure either DHCP or static address for host and client.
sudo ip addr add 192.168.100.2/24 dev usb0
sudo ip link set usb0 

next just test via ping or iperf3 -c 192.168.100.1 if you configure 100.x subnet.

@dafta
Copy link
Author

dafta commented Jul 12, 2024

  1. before running script use
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

I added this directly into the script, so this isn't necessary if you have the latest version of the script.

You should also check if you enabled USB DRD in the BIOS, and that you have a recent BIOS version that has working DRD.

@dafta
Copy link
Author

dafta commented Jul 12, 2024

Also, @kolanski I tried the script again to see if it works still, and DHCP was working for me. I'm not sure why it doesn't work for you.

@kolanski
Copy link

Also, @kolanski I tried the script again to see if it works still, and DHCP was working for me. I'm not sure why it doesn't work for you.

I just have old revision of script)

@rocket0634
Copy link

rocket0634 commented Aug 1, 2024

Is there meant to be anything on screen when I run usb-ether.sh start?
I run the script and then it just puts me back into bash.
Running sudo lspci -v seems to show it's still using the xhci driver, and running usb-tether.sh stop shows that no such device exists.
Running usb-ether.sh twice in a row results in things showing up, and Windows making the usb sound, but it gives device is busy errors and I don't see anything available on Windows.

I'm on bios F7A0120, which reading through here appears to be a supported bios.

Edit: I was looking in the wrong place in lspci, it shows that the driver is replaced properly

@dafta
Copy link
Author

dafta commented Aug 4, 2024

There's no output when the script runs correctly.

Edit: I was looking in the wrong place for lspci, it shows that the driver is replaced properly

What do you mean by this?

@rocket0634
Copy link

I was looking at the title, which said "[xhci]" when I should've been looking at active driver.

In any case, if there's no output when I start the script, how do I know if the script is working or not? I know that when I try to stop it, it says no device exists, which suggests starting it doesn't make it fully work.

@dafta
Copy link
Author

dafta commented Aug 5, 2024

I know that when I try to stop it, it says no device exists, which suggests starting it doesn't make it fully work.

It's most likely lines 217 and 218 that say this, in which case it's safe to ignore the message. This is an optional step to revert lines 90 and 91, which also might not be necessary in certain conditions.

In any case, if there's no output in the start script, it should work. You should only have to run start once. You can try restarting the steam deck to remove any configuration that might be leftover from starting it twice or incorrectly stopping it, just in case, and then starting the script. Also, check if DRD is enabled in the BIOS. BIOS 120 did work, but it was a bit finnicky and it might require you to disable DRD, restart, and reenable DRD. Instead of that, you could also upgrade your BIOS to a newer version, as 121 and onwards seem to have fixed most of the quirkiness.

@rocket0634
Copy link

rocket0634 commented Aug 11, 2024

Sorry for taking so long to respond back, but it does not seem like I'm able to get it working on 120 even after disabling, restarting, and reenabling DRD. I don't hear anything in windows when I plug the usb cable either. It just doesn't seem to want to do anything until I run start twice, which causes Windows to detect the usb but doesn't seem to work by other means. And it appears to be line 177 that is saying "no such device", not 217 or 218

I switched to the beta branch which is on 131 and it seems to also be giving me issues. It tells me "No such Device" and "Device or resource busy" on lines 90 and 91.

Edit: I switched back to stable and I'm still on 131. Guess the deck doesn't allow for downgrades using that method.
Either way the first part still applies. I'm getting the same exact response.

@dafta
Copy link
Author

dafta commented Aug 13, 2024

I switched to the beta branch which is on 131 and it seems to also be giving me issues. It tells me "No such Device" and "Device or resource busy" on lines 90 and 91.

This should be okay as those lines fail because the USB controller is already bound to the correct driver, hence the "No such device" when trying to unbind from the one that's not loaded and the "Device busy" when trying to bind to the one that already is.

And it appears to be line 177 that is saying "no such device", not 217 or 218

This, however, shouldn't happen. Several things to try here. First would be to check in the BIOS if DRD is enabled, because the BIOS options get reset to the new BIOS version's default settings, which are currently set to DRD off. And again, if it's on already, turn it off, reboot, and turn it on again, in case the option wasn't appplied correctly on update.

Next, you could remove entirely the lines 90-91 and 217-218, as they don't seem to be needed in your case and the script might exit prematurely when those commands fail.

If none of those work, could you show me the output of lsmod | grep dwc?

@rocket0634
Copy link

dwc3_pci 24576 0

The output when the script is started:

dwc3 200704 0
roles 16384 1 dwc3
ulpi 20480 1 dwc3
udc_core 81920 5 usb_f_rndis, u_ether, usb_f_ecm,libcomposite,dwc3
dwc3_pci 24576 0

The output when the script is stopped is the same, except for udc_core
udc core 81920 1 dwc3

@romleouf
Copy link

romleouf commented Aug 22, 2024

hello i new on linux, i have a steamdeck i have a error for to use this scrypt , i want connect at my mbp for play with steam link my bios is F7A0131 sudo /home/deck/Desktop/usb-ether.sh start -n
0000:04:00.3tee: /sys/bus/pci/drivers/xhci_hcd/unbind: Aucun périphérique de ce type
tee: /sys/bus/pci/drivers/dwc3-pci/bind: Aucun fichier ou dossier de ce type
0000:04:00.3mkdir: impossible de créer le répertoire « /sys/kernel/config/usb_gadget/g.1 »: Le fichier existe
mkdir: impossible de créer le répertoire « configs/c.1 »: Le fichier existe
mkdir: impossible de créer le répertoire « strings/0x409 »: Le fichier existe
mkdir: impossible de créer le répertoire « configs/c.1/strings/0x409 »: Le fichier existe
mkdir: impossible de créer le répertoire « functions/ncm.0 »: Le fichier existe
/home/deck/Desktop/usb-ether.sh: ligne 129 : echo: erreur d'écriture : Périphérique ou ressource occupé
/home/deck/Desktop/usb-ether.sh: ligne 130 : echo: erreur d'écriture : Périphérique ou ressource occupé
mkdir: impossible de créer le répertoire « configs/c.2 »: Le fichier existe
mkdir: impossible de créer le répertoire « configs/c.2/strings/0x409 »: Le fichier existe
mkdir: impossible de créer le répertoire « functions/rndis.0 »: Le fichier existe
/home/deck/Desktop/usb-ether.sh: ligne 153 : echo: erreur d'écriture : Périphérique ou ressource occupé
/home/deck/Desktop/usb-ether.sh: ligne 154 : echo: erreur d'écriture : Périphérique ou ressource occupé
ln: impossible de créer le lien symbolique 'configs/c.1/ncm.0': Le fichier existe
ln: impossible de créer le lien symbolique 'configs/c.2/rndis.0': Le fichier existe
ln: impossible de créer le lien symbolique 'os_desc/c.2': Le fichier existe

@rocket0634
Copy link

rocket0634 commented Sep 12, 2024

I'm not sure what I did, but after a combination of messing around with this script and another script I found on the reddit thread, I did finally managed to get this to work. It felt like I was cheating the system because of how many attempts failed but I finally got it to work once.

I opened the deck and applied an update that came out today. And now the script doesn't work again. Using my previous method of running the script twice is no longer working, either. For that matter, before the update the gadget would still persist even after restarting the system, but after the update, restarting the system now results in the gadget being deleted.

I can't help but to feel that Valve put a hard lockdown on sys in the recent update, or something. I thought I'd at least let you know, just in case the latest update did actually end up killing the script.

I believe that this issue has always been on the deck's side. There's no indication that the script was functioning, and when trying to stop it, it'd say the device (the gadget) doesn't exist, suggesting it was never created in the first place. It would be nice to get more hands on troubleshooting with this, but with the latest update, I'm unsure if there's anything that can be done about it now. Unless I'm messing something up, somehow. I did check the folders that were supposed to be created when the script is run, so the only thing I can guess is that it's actually running the gadget that isn't working, and the script has no way of showcasing it working or not.

DRD is on, and I've disabled/enabled it many times. Doing anything with the bios and messing with the USB cable make no difference. At this point even lsmod is only showing one line, instead of the many I showed before. My BIOS version is still 131. It hasn't changed with the update.

@kolanski
Copy link

I'm not sure what I did, but after a combination of messing around with this script and another script I found on the reddit thread, I did finally managed to get this to work. It felt like I was cheating the system because of how many attempts failed but I finally got it to work once.

I opened the deck and applied an update that came out today. And now the script doesn't work again. Using my previous method of running the script twice is no longer working, either. For that matter, before the update the gadget would still persist even after restarting the system, but after the update, restarting the system now results in the gadget being deleted.

I can't help but to feel that Valve put a hard lockdown on sys in the recent update, or something. I thought I'd at least let you know, just in case the latest update did actually end up killing the script.

I believe that this issue has always been on the deck's side. There's no indication that the script was functioning, and when trying to stop it, it'd say the device (the gadget) doesn't exist, suggesting it was never created in the first place. It would be nice to get more hands on troubleshooting with this, but with the latest update, I'm unsure if there's anything that can be done about it now. Unless I'm messing something up, somehow. I did check the folders that were supposed to be created when the script is run, so the only thing I can guess is that it's actually running the gadget that isn't working, and the script has no way of showcasing it working or not.

DRD is on, and I've disabled/enabled it many times. Doing anything with the bios and messing with the USB cable make no difference. At this point even lsmod is only showing one line, instead of the many I showed before. My BIOS version is still 131. It hasn't changed with the update.

I think we need to freeze steam os version it worked.

@dafta
Copy link
Author

dafta commented Sep 13, 2024

I think we need to freeze steam os version it worked.

What do you mean? How would we freeze the steam os version?

I can't help but to feel that Valve put a hard lockdown on sys in the recent update, or something. I thought I'd at least let you know, just in case the latest update did actually end up killing the script.

It's very likely that an update did end up killing the script, but it's not Valve putting a hard lockdown on anything. This happens every now and again because they aren't currently focused on USB DRD and the drivers sometimes stop working until they fix it. It's just a matter of time until it starts working again. It might even work on the beta right now, or stable if you're on beta. It's also possible that the script needs updating.

There's no indication that the script was functioning

I never added any, this script was mostly a proof of concept.

with the latest update, I'm unsure if there's anything that can be done about it now.

Like I said, it's only a matter of time until it works again, or the script might need updating. I will, however, not be updating this script, it's provided as-is and is and always was just a proof of concept. I'm working on instead adding this feature to my decky plugin, DeckMTP, with the next version, and slowly adding more USB gadgets and features with time.

@rocket0634
Copy link

Ah, I thought the plugin was cancelled since I first saw it mentioned years ago. Is it something you're actively working on, or are you waiting on something else before you can work on it?

The main thing I'd like is to remove the latency in Steam Link between the deck and my PC, since my router is on the other side of the house from where my computer is. And throughout what I've searched, it seemed like this script was the only thing I could find that could do it. Would the plugin be able to do this? And would it be anything I could use within the next year or two?

@dafta
Copy link
Author

dafta commented Sep 13, 2024

No, the plugin was never canceled, some things had to be rewritten first to allow this to happen. I'm still working on it, however this is unpaid work that I'm working on in my free time whenever I'm able to and I'm not burnt out from actual work.

Would the plugin be able to do this?

Both the plugin, and this script if you manage to get it to run, should be able to do this. I've seen people use this script for this exact reason.

And would it be anything I could use within the next year or two?

It's the very next thing on my list of priorities. A couple of months will do it most likely.

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