Automatically share usb devices over the network (Linux)
Transform any computer into a wireless USB hub using usbip
I have always wanted to have a console-like game PC experience. One problem that I had, was that game PC's are big and noisy. Hardly something you would put next to your TV in the living room. On top of that, if I wanted to game using a keyboard and mouse at my desk, I would have to lug my PC over there.
It would be perfect if I could have my PC under my desk and somehow still game on my living room TV. One solution is to use game streaming, but I found that this never really works reliably on linux. So, I went with the more 'primitive' solution: a really long HDMI cable.
But what about peripherals? Long HDMI cables are available for reasonable prices, but long USB cables (over 15 m) are ludicrously expensive, because they need repeaters.
That brings me to this tutorial. For a while now, I have used usbip with some automation on old laptop, to forward any USB devices over the network to my PC. I game with a Bluetooth controller and simply have a Bluetooth dongle plugged into this laptop. It's a wireless USB hub!
It works like a dream. Latency is much lower than streaming and it is very reliable. It also works for other peripherals, such as printers and flash drives (only small files).
I will now explain how you too can automatically forward any USB devices over the network.
Prerequisites
- A small Linux computer for in your living room
- Anything will do, an old laptop, a NUC or even a Raspberry Pi, as long as it has USB ports.
- This will be your server, the PC your client.
- A static IP address for the server
- This can be set up in linux (tutorial) or in your router.
Install Server & Client
You only need a single package and it's the same one on the client and the PC. On debian and ubunutu-like systems, you can simply
sudo apt install usbip
On other systems, the install will be similar.
Server Setup
Kernel Modules
You will need to load a kernel module at boot time for usbip to work. You should add the following line to /etc/modules-load.d/modules.conf
.
usbip_host
Then reload your initramfs:
sudo update-initramfs -u
Systemd Service
We will use systemd to start the usbip deamon. You should put the config file in the following location: /etc/systemd/system/usbip.service
.
[Unit]
Description=USBIP deamon
After=network-online.target multi-user.target
[Service]
Type=forking
ExecStart=/usr/sbin/usbipd -D
Restart=on-failure
[Install]
WantedBy=multi-user.target
Then you can enable it:
sudo systemctl enable --now usbip
Udev Script
Now, we want to run a little script whenever a new USB device is connected to the server. We will do this using udev. But before we do that, there are a few devices we should blacklist, so that they aren't forwarded to the PC. For a laptop, this would be the keyboard, track-pad and any other internal USB devices. For other computers, you should probably blacklist at least one keyboard to be able to interact with it. If you are planning on using USB-hubs, those should also be blacklisted, so that only the devices themselves are forwarded to the PC. You can get the bus-ids of your devices with the following command:
sudo usbip list --local
- busid 1-6 (064e:c355)
Suyin Copr. | unknown product (064e:c355)
- bus-id 1-7 (8087:0a2a)
Intel Copr. : Bluetooth wireless interface (8087:0a2a)
- bus 1-8 (04f3:2274)
Elan Microelectronics Copr. : unknown product (04f3:2274)
Armed with this knowledge, we can put the following script at this location: /usr/local/bin/udev-usb-to-usbip.sh
.
#!/bin/bash
blacklist=("1-6" "1-7" "1-8")
bus_id=$(basename $1)
if ! [[ ${blacklist[@]} =~ $bus_id ]]
then
usbip bind -b $bus_id
fi
exit 0
Then we need to configure udev to run our script whenever a new USB is connected. Create a new file here: /etc/udev/rules.d/99-usbip-new-usb.rules
with the following content.
SUBSYSTEMS=="usb", ENV{DEVTYPE}=="usb_device", ACTION=="add", RUN+="/bin/bash /usr/local/bin/udev-usb-to-usbip.sh '$env{DEVPATH}'"
And that's all on the server side!
Client Setup
Kernel Module
The client also needs a kernel module to be loaded at boot, but a different one than the server. Add the following line to /etc/modules-load.d/modules.conf
.
vhci-hcd
Systemd Service
The client also uses a systemd service to handle startup and shutdown of our automation.
Put the config file in here: /etc/systemd/system/usbip-client.service
.
[Unit]
Description=listen for new usb devices and attach them
After=multi-user.target bluetooth.target network.target
[Service]
Type=exec
ExecStart=/usr/local/bin/usbip-client.sh
Restart=on-failure
ExecStopPost=/usr/local/bin/detach-usbip.sh
[Install]
WantedBy=multi-user.target
Daemon Script
We want the PC to periodically poll for new USB devices on our server and import them when they become available. You can use the following script and put it here: /usr/local/bin/usbip-client.sh
. Replace <YOUR SERVER IP>
with the IP of your server.
#!/bin/bash
while [[ true ]]
do
# list available devices
# get device bus path
# get last part of path (bus id)
bus_ids=$(usbip list -r 192.168.178.76 | grep /sys/devices | awk '{n=split($NF,a,"/");print a[n]}')
for id in $bus_ids
do
usbip attach -r <YOUR SERVER IP> $id
done
echo $bus_ids 1>&2;
sleep 2
done
Shutdown Script
When the client shuts down, we want to release any USB devices so that they become available for us at the next boot. For this, I have written the following script, which you can put at: /usr/local/bin/detach-usbip.sh
.
#!/bin/bash
all_ports=$(usbip port | grep Port | awk '{ print substr($2, 1, length($2)-1) }')
for port in $all_ports
do
usbip detach -p $port
done
Conclusion
And there you have it! You can now use any computer as a wireless USB hub. As always, you can reach me via email if you have any questions: [email protected]