An unknown error ocurred. Please try again.

Automatically share usb devices over the network (Linux)

Transform any computer into a wireless USB hub using usbip

Article Image
1/1
published on

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]