Remote USB device over IP in Linux

Hi There,
a quick hotwo on how to use Linux’s usbip package to connect a remote USB device to a linux host in a way that is transparent to apps expecting the USB connection to be local


 ┌─────────────────────────────┐                   ┌────────────────────────────────┐
 │                             │                   │                                │
 │                             │                   │                                │
 │      ┌──────────────────┐   │                   │ ┌───────────────────┐          │
 │      │                  │   │                   │ │                   │          │
 │      │                  │   │                   │ │                   │          │
 │      │  Server          ◄───┼───────────────────┼─►  Raspberry        │          │
 │      │                  │   │    Ethernet       │ │                   │          │
 │      │                  │   │                   │ │                   │          │
 │      └──────────────────┘   │                   │ └────────▲──────────┘          │
 │                             │                   │          │                     │
 │                             │                   │          │                     │
 │                             │                   │          │ USB                 │
 │        Attic                │                   │          │                     │
 └─────────────────────────────┘                   │          │                     │
                                                   │ ┌───┬───┐▼──┬───┬───┐          │
                                                   │ │   │   │   │   │   │          │
                                                   │ ├───┼───┼───┼───┼───┤          │
                                                   │ │   │   │   │   │   │          │
                                                   │ ├───┼───┼───┼───┼───┤          │
                                                   │ │   │   │   │   │   │          │
                                                   │ └───┴───┴───┴───┴───┘          │
                                                   │    Streamdeck                  │
                                                   │                                │
                                                   │                        Desk    │
                                                   └────────────────────────────────┘

WHAT ?

this post would like to explain how to use Linux’s usbip package to attach an USB peripheral physically connected to an USB port of a remote host and have it appear as a standard, local USB device

WHY ?

My use case, I am using an Elgato Streamdeck as a control surface to operate a headless proxmox server that has been moved in my attic, so that I can start VMs, stop VMs, check the server status without needing a remote session/laptop to do it. SInce the distance between the attic and my desk is over 10M (and I don’t have space in my conduits) I used a raspberry Pi I had Lying around to keep the streamdeck on my desk, but have it presented as a physical usb device to the server in the attic

HOW?

USB Server - Raspberry Pi2, connected to the streamdeck using a standard USB cable:

root@deckmaster01:~# lsusb
Bus 001 Device 004: ID 0fd9:0060 Elgato Systems GmbH Stream Deck

Install the usbip package

apt-get install usbip

In my case it’s already installed

root@deckmaster01:~# apt-cache policy usbip
usbip:
  Installed: 2.0+5.10.103-1+rpi1
  Candidate: 2.0+5.10.103-1+rpi1
  Version table:
 *** 2.0+5.10.103-1+rpi1 500
        500 http://raspbian.raspberrypi.org/raspbian bullseye/main armhf Packages
        100 /var/lib/dpkg/status

Load the usbip_host module and make sure it survives reboots:

root@deckmaster01:~# modprobe usbip_host

root@deckmaster01:~# echo usbip_host >> /etc/modules-load.d/modules.conf

Check that the peripheral you want to share is supported

root@deckmaster01:~# usbip list  -l
usbip: error: Protocol spec without prior Class and Subclass spec at line 23299
 - busid 1-1.1 (0424:ec00)
   Microchip Technology, Inc. (formerly SMSC) : SMSC9512/9514 Fast Ethernet Adapter (0424:ec00)

 - busid 1-1.2 (0fd9:0060)
   Elgato Systems GmbH : Stream Deck (0fd9:0060)

Create a generic service for the usbip host module(I use raspbian so systemd):

root@deckmaster01:~# cat /etc/systemd/system/[email protected]
[Unit]
Description=USB-IP Binding on bus id %I
After=network-online.target usbipd.service
Wants=network-online.target
Requires=usbipd.service
#DefaultInstance=1-1.5

[Service]
Type=simple
ExecStart=/usr/sbin/usbip bind -b %i
RemainAfterExit=yes
ExecStop=/usr/sbin/usbip unbind -b %i
Restart=on-failure

[Install]
WantedBy=multi-user.target

Enable the service using the port id of the peripheral you want to share (in my case 1-1.2):

root@deckmaster01:~# systemctl enable [email protected]

root@deckmaster01:~# systemctl status [email protected][email protected] - USB-IP Binding on bus id 1/1.2
     Loaded: loaded (/etc/systemd/system/[email protected]; enabled; vendor preset: enabled)
     Active: active (exited) since Fri 2022-05-20 06:23:56 BST; 3 days ago
    Process: 543 ExecStart=/usr/sbin/usbip bind -b 1-1.2 (code=exited, status=0/SUCCESS)
   Main PID: 543 (code=exited, status=0/SUCCESS)

May 20 06:23:56 deckmaster01 systemd[1]: Started USB-IP Binding on bus id 1/1.2.
May 20 06:23:57 deckmaster01 usbip[543]: usbip: info: bind device on busid 1-1.2: complete

Test that the device has been exported as expected:

root@deckmaster01:~# usbip list --remote=127.0.0.1
Exportable USB devices
======================
 - 127.0.0.1
      1-1.2: Elgato Systems GmbH : Stream Deck (0fd9:0060)
           : /sys/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.2
           : (Defined at Interface level) (00/00/00)

root@deckmaster01:~#

This should complete the configuration on the USB server host

USB Client - In my case a proxmox server running an instance of deckmaster, a linux go client for the streamdeck that allows configuring specific actions for each streamdeck button

Install the usbip package:

apt-get install usbip

Check that the remote USB host is reachable and has a device available to share:

root@pve:~# usbip list -r 172.30.2.208
usbip: error: Protocol spec without prior Class and Subclass spec at line 23299
Exportable USB devices
======================
 - 172.30.2.208
      1-1.2: Elgato Systems GmbH : Stream Deck (0fd9:0060)
           : /sys/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.2
           : (Defined at Interface level) (00/00/00)

List your local usb bus attached devices:

root@pve:~# lsusb
Bus 009 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 010 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 006 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 005 Device 005: ID 046b:ff10 American Megatrends, Inc. Virtual Keyboard and Mouse
Bus 005 Device 004: ID 046b:ffb0 American Megatrends, Inc. Virtual Ethernet
Bus 005 Device 006: ID 046b:ff20 American Megatrends, Inc. Virtual Cdrom Device
Bus 005 Device 003: ID 046b:ff01 American Megatrends, Inc. Virtual Hub
Bus 005 Device 002: ID 05e3:0610 Genesys Logic, Inc. Hub
Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 008 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 007 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Manually attach the remote device - testing phase:
Syntax is: usbip attach -r -b


root@pve:~# usbip attach -r 172.30.2.208 -b 1-1.2
root@pve:~# lsusb
Bus 009 Device 011: ID 0fd9:0060 Elgato Systems GmbH Stream Deck

Test that whatever local software is expected to see/use the USB device works as expected

Once you are satisfied and ready to make the config permanent:

Detach the USB device:

/usr/sbin/usbip detach  --port=0

create a local service to connect the device at every start of the client:
(note I have kept it dumb and hardcoded the remote server IP

root@pve:~# cat /etc/systemd/system/[email protected]
[Unit]
Description=USB/IP bind service
After=network.target
Requires=

[Service]
Type=simple
ExecStart=/usr/sbin/usbip attach -r 172.30.2.208 --busid %i
ExecStop=/usr/sbin/usbip detach --port=0
RemainAfterExit=yes
Restart=on-failure

[Install]
WantedBy=multi-user.target

Enable the service

systemctl enable [email protected]

Check that the service started properly:

root@pve:~# systemctl start [email protected]
root@pve:~# !jo
journalctl -u [email protected] -f
-- Journal begins at Thu 2022-04-28 09:32:45 CEST. --
May 23 14:15:13 pve systemd[1]: Started USB/IP bind service.

Make sure you still have the proper local usb device

root@pve:~# !lsusb
lsusb
Bus 009 Device 012: ID 0fd9:0060 Elgato Systems GmbH Stream Deck

And … you’re good to go :slight_smile: (in my case, starting the deckmaster service)

journalctl -u deckmaster -f
-- Journal begins at Thu 2022-04-28 09:32:45 CEST. --
May 23 14:06:08 pve systemd[1]: deckmaster.service: Succeeded.
May 23 14:16:39 pve systemd[1]: Started Deckmaster.
May 23 14:16:39 pve dbus-daemon[442301]: [session uid=0 pid=442299] AppArmor D-Bus mediation is enabled
May 23 14:16:39 pve appgoservice[442290]: Found device with serial AL31H1B07920 (firmware 1.0.191203
May 23 14:16:39 pve appgoservice[442290]: )
May 23 14:16:48 pve appgoservice[442290]: Mqtt update skip
May 23 14:16:48 pve appgoservice[442290]: 2022/05/23 14:16:48 MEROSS Init
May 23 14:16:48 pve appgoservice[442290]: 2022/05/23 14:16:48 Handling TOPIC:  /app/pve-streamdeck/publish
7 Likes