USBIP

The max cable length for USB 2.0 standard is 5 meters. The USB 3.0 standard does not specify a max length, but practically consider it is 3 meters. Lengths can be exceeded by passive hubs between cable lengths tor reconditioning the signal and for still longer lengths, active powered hubs. USBIP consists of a USB server that translates USB signals to network signals and communicates with a client up to 100 meters away which translates network signals back to USB signals. Gigabit network adapters and cables can support 4 USB 2.0 devices at full capacity.

(See sub pages of this page for proprietary hardware and software solutions)

What about an external USB over IP gigabit network hub for multiple Displaylink based USB zero clients? These also are expensive and I have none to test. The mechanism in Manjaro for auto seat creation requires that the hierarchy of USB devices is maintained. The displaylink device must be seen virtually as a child of the usb zero client internal hub, otherwise no new seat is created automatically and it will be treated as a desktop display extention or mirror of the seat0 primary display.

USBIP already exists in both Raspberry Pi and Manjaro software repositories so only an app for non technical people is needed to configure USBIP at both the server and client ends.

Use Add/Remove Software GUI utility and search for USBIP and install on Raspberry Pi (I am using 3B plus model).

Initial experimentation shows a limit of 2 x 8 devices on the client end as implemented. 8 devices for USB 3.0 and 8 devices for USB 2.0. If you plug a USB 2.0 device into a USB 3.0 port on the server end, the client will see it as USB 2.0. The client end can connect to usb servers from different IP, but on the client end that distinction disappears and all will be lumped together with the total limit of 8 USB 2.0 devices.

A typical USB zero client requires a minimum of 3 devices – usb displaylink adapter, usb audio adapter, and a usb wireless keyboard/mouse receiver. Thus a maximum of 2 USB 2.0 zero clients could be served over a network. The next limitation is that all hub hierarchy is lost at the client end. You don’t know which of two usb displaylink adapters go with which usb audio devices, go with which keyboards/mice.

[asm32@manjaro-t1600 ~]$ lsusb -t
 /:  Bus 04.Port 1: Dev 1, Class=root_hub, Driver=vhci_hcd/8p, 5000M
 /:  Bus 03.Port 1: Dev 1, Class=root_hub, Driver=vhci_hcd/8p, 480M
     |_ Port 1: Dev 2, If 2, Class=Human Interface Device, Driver=usbhid, 12M     |_ Port 1: Dev 2, If 0, Class=Human Interface Device, Driver=usbhid, 12M
     |_ Port 1: Dev 2, If 1, Class=Human Interface Device, Driver=usbhid, 12M     |_ Port 2: Dev 3, If 3, Class=Human Interface Device, Driver=usbhid, 12M
     |_ Port 2: Dev 3, If 1, Class=Audio, Driver=snd-usb-audio, 12M     |_ Port 2: Dev 3, If 2, Class=Audio, Driver=snd-usb-audio, 12M
     |_ Port 2: Dev 3, If 0, Class=Audio, Driver=snd-usb-audio, 12M     |_ Port 3: Dev 4, If 0, Class=Vendor Specific Class, Driver=udl, 480M
     |_ Port 4: Dev 5, If 2, Class=Human Interface Device, Driver=usbhid, 12M     |_ Port 4: Dev 5, If 0, Class=Human Interface Device, Driver=usbhid, 12M
     |_ Port 4: Dev 5, If 1, Class=Human Interface Device, Driver=usbhid, 12M     |_ Port 5: Dev 6, If 2, Class=Audio, Driver=snd-usb-audio, 12M
     |_ Port 5: Dev 6, If 0, Class=Audio, Driver=snd-usb-audio, 12M     |_ Port 5: Dev 6, If 3, Class=Human Interface Device, Driver=usbhid, 12M
     |_ Port 5: Dev 6, If 1, Class=Audio, Driver=snd-usb-audio, 12M     |_ Port 6: Dev 7, If 0, Class=Vendor Specific Class, Driver=udl, 480M
 /:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/2p, 480M
     |_ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/8p, 480M /:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/2p, 480M     |_ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/6p, 480M

Perhaps studying the proprietary EST usb server will throw some light on how they deal with the hierarchy of USB hubs within a zeroclient when you have more than one zeroclient.

What follows was written before I discovered these limitations. I like binding a usb DVD player to a raspberry pi headless server and then viewing the film on a separate raspberry pi anywhere on my LAN without taking up desk space. I can also use it as a usb storage device to back up files anywhere on my LAN, but it is not as versatile as a NAS for multiple users.

Rather than setting up one Raspberry Pi as a media server using USBIP, an alternative approach would be to install minidlna which is a dlna server. Then on a different computer, perhaps a raspberry pi also, you can run VLC and choose Universal Plug n Play as the media source. This is a dlna client. With minidlna, you have to configure its /etc/minidlna.conf server for your usb DVD drive mount point and separately to mount this drive. You can putty in to mount the usb drive, or you can configure it to mount automatically by creating a rule file under /etc/dev/rules.d Warning, don’t try to set up auto booting in /etc/fstab because this will hang the boot process and you will have to move the microSD card to a different computer with card reader to remove your /etc/fstab entry for the usb dvd drive. With USBIP, the client computer mounts the server’s remote usb DVD drive automatically when it is attached to the exported remote usb device.

The Raspberry Pi will be the USBIP server (package contains both server and client). Googling USBIP returns a lot of out of date documentation. The following currently works in December 2019. In a terminal on the Pi:

pi@raspberrypi:~ $ sudo modprobe usbip_host

pi@raspberrypi:~ $ lsmod |grep usbip_host
usbip_host 28672 0
usbip_core 16384 1 usbip_host
pi@raspberrypi:~ $

pi@raspberrypi:~ $ sudo usbipd -D

pi@raspberrypi:~ $ usbip list -l

busid 1-1.4 (046d:c52b)
Logitech, Inc. : Unifying Receiver (046d:c52b)

 

Now plug in my Plugable 3 port USB 3.0 hub and look for usb zero client plugged into this hub which also has its own Logitech, Inc. Unifying Receiver:

pi@raspberrypi:~ $ usbip list -l

busid 1-1.2.1 (0b95:1790)
ASIX Electronics Corp. : AX88179 Gigabit Ethernet (0b95:1790) DisplayLink : unknown product (17e9:410e)busid 1-1.2.3.2 (0d8c:013a)
C-Media Electronics, Inc. : unknown product (0d8c:013a)busid 1-1.2.3.4 (046d:c52b)
Logitech, Inc. : Unifying Receiver (046d:c52b)busid 1-1.4 (046d:c52b)
Logitech, Inc. : Unifying Receiver (046d:c52b)

pi@raspberrypi:~ $

pi@raspberrypi:~ $ sudo usbip bind -b 1-1.2.3.2 <--- c-media
usbip: info: bind device on busid 1-1.2.3.2: complete
pi@raspberrypi:~ $ sudo usbip bind -b 1-1.2.3.4 <--- HP T150 with unifying receiver
usbip: info: bind device on busid 1-1.2.3.4: complete
pi@raspberrypi:~ $ sudo usbip bind -b 1-1.2.3.1 <--- displaylink
usbip: info: bind device on busid 1-1.2.3.1: complete

This is encouraging. It would be desirable for usbipd to be a service started at boot and for it to bind all usb devices so that they become available to the client on the Manjaro desktop. It would be desirable not to have to configure each device you want to be found. Enlisting google again with this additional criterion, I found this link:

https://derushadigital.com/other%20projects/2019/02/19/RPi-USBIP-ZWave.html

He shows how to export one specific usb device by code that searches for its idVendor:idProduct value. What if you want to export more than one device, or you don’t know in advance what devices will be plugged in? This suggests an alternative approach – instead of specifying for what you want to export, instead search for what you don’t want and avoid exporting that while exporting everything else. You will discover one thing that you must exclude. The Rasberry Pi 3x gigabit LAN port is the child of a usb bus. On the PI 3B, its idVendor:idProduct is 0424:ec00, but on the Pi 3Bplus, it is 0424:7800. If you were to export this usb LAN device you have broken the LAN pipe that you are exporting other usb devices over. The Pi 4B gigabit LAN is not USB based so not an issue.

If you are using docking stations as zero clients, then you have no use for the docking stations LAN port, so that is also good to avoid exporting. Implementing these changes to his code results in the following content which you need to create with an editor and root privileges and save as /lib/systemd/system/usbipd.service

 [Unit]
 Description=usbip host daemon
 After=network.target
 [Service]
 Type=forking
 ExecStart=/usr/sbin/usbipd -D
 # ExecStartPost=/bin/sh -c "/usr/sbin/usbip bind --$(/usr/sbin/usbip list -p -l | grep -v '0424:7800#'| grep -v '0b95:772a#' | cut '-d#' -f1)"
 # ExecStop=/bin/sh -c "/usr/sbin/usbip unbind --$(/usr/sbin/usbip list -p -l | grep -v '0424:7800#' | grep -v '0b95:772a#' | cut '-d#' -f1`); killall usbipd"
 [Install]
 WantedBy=multi-user.target
 pi@raspberrypi-3B:~ $

One very interesting aspect of launching a shell with the -c “….” option is that it can read multiple lines of text from standard input until all sti has been consumed. The code within (…) writes a line of text for every usb device found except for devices excluded by grep’s -v option. ‘0424:7800’ is the raspberry pi’s usb gigabit LAN device that must be excluded. ‘0b95:772a’ is the ASIX Electronics Corp. : AX88772A Fast Ethernet usb device on a Plugable UD-160-A docking station which is not relevant to its use as a zero client.

Following Derusha’s model results in his (…) writing multiple lines to sto, but the first part of his “ExecStartPost=” only consumes the first line of its sti, so only the first local usb device will be found. It will be easier to get a user pace solution working first, so I have commented out two lines.

!/bin/bash
 sudo modprobe usbip_host
 lsmod |grep usbip_host
 sudo usbipd -D
 usbip list -l
 read -p "Press any key to bind all"
 sudo usbip list -p -l | grep -v '0424:7800#'| grep -v '0b95:772a#' | cut '-d=' -f2 | cut '-d#' -f1 | xargs -L 1 -r sudo bash -c 'usbip bind -b"$0" '
 read -p "Press any key to exit"

We will also need an unbind version:

!/bin/bash
 sudo modprobe usbip_host
 lsmod |grep usbip_host
 sudo usbipd -D
 usbip list -l
 read -p "Press any key to unbind all"
 sudo usbip list -p -l | grep -v '0424:7800#'| grep -v '0b95:772a#' | cut '-d=' -f2 | cut '-d#' -f1 | xargs -L 1 -r sudo bash -c 'usbip unbind -b"$0" '
 read -p "Press any key to exit"

I have chosen to export all devices connected to the Raspberry Pi 3B+’s 4 usb ports, and to communicate with it with either Putty for CLI or with x2go for a full desktop GUI experience, rather than input devices using up one or two of its 4 usb ports. Both use port 22 for remote access. If you want to use the Pi for both a usb server of network attached usb zero clients and a desktop in its own right, then you would want to choose a different model of input device than will be used with any of the zero client workstations. For Logitech wireless keyboards and mice, there is one model of nano receiver (046d:c525) and 5 models of unifying receiver (046d:c52b, c52f, c531, c532, and c534). The one you choose to stay local to the Pi, should be added to my grep -v filters in the code above so that it is not found and exported.

Having created and saved /lib/systemd/system/usbipd.service, do the following steps as detailed by https://derushadigital.com/other%20projects/2019/02/19/RPi-USBIP-ZWave.html

# reload systemd, enable, then start the service 
sudo systemctl --system daemon-reload 
sudo systemctl enable usbipd.service 
sudo systemctl start usbipd.service

Note that if no usb devices are plugged into the Pi, none will be found when usbipd.service is started and the result is that this service will terminate since it has nothing to do.

Now for the client side on the Manjaro desktop that we want to recognize all potential remote usb over IP zero clients. Ideally when they are found, a new named seat will be created for each with a new login window.

This turns out to be more challenging. Most of the documentation you find on Google for USBIP is obsolete, or only applies to certain Linux distributions. My server side instructions may not work on other distributions than Raspbian for the Raspberry Pi I am using. USBIP started out as a user space project and then later parts were incorporated into the kernel. Add typos in various documentation and you have a lot to try and I am still working on it.

Raspbian does not support USB multi-seat, so that is not the end goal, but since it does have the USBIP package in its repository, it is handy for testing client code. The client needs to be able to attach and detach remote usb devices. Some usb devices have internal ports, so detaching is based on ports not usb bus IDs. 192.168.10.113 is the IP of my Raspberry Pi 3B+, you will need to edit this IP for your server. It turns out that the -r option accepts hostnames instead of IP addresses. I recommend that you name all your computers uniquely because their IP addresses may change. I have not updated what follows yet.

!/bin/bash
 sudo modprobe vhci_hcd
 lsmod |grep vhci_hcd
 sudo usbipd -D
 usbip list -r 192.168.10.113 |cut '-d:' -f1 |grep 1-1 |tr -d ' '
 read -p "Press any key to attach all"
 usbip list -r 192.168.10.113 |cut '-d:' -f1 |grep 1-1 |tr -d ' ' | xargs -L 1 -r sudo bash -c 'sudo usbip attach -r 192.168.10.113 -b"$0" '
 read -p "Press any key to exit"

The command, usbip port, will list the imported remote USB devices and their ports. Not all USB ports are necessarily occupied, so the following code removes error messages from trying to read empty USB ports.

!/bin/bash
 usbip port 2>/dev/null

Before can unbind USB on the server, the client has to detach any imported ports. However, bind and attach state does not persist across reboots, so there is no harm in just shutting down when done.

sudo modprobe vhci-hcd
  lsmod |grep vhci-hcd
  sudo usbipd -D
  usbip port 2>/dev/null | grep '' | sed -E 's/^Port ([0-9][0-9])./\1/'  read -p "Press any key to detach all ports"  usbip port 2>/dev/null | grep '' | sed -E 's/^Port ([0-9][0-9])./\1/'| xargs -L 1 -r sudo bash -c 'sudo usbip detach -p"$0" '
  read -p "Press any key to exit"

WordPress is removing some of my program characters. I need to figure out how to make it display correctly as well as produce the correct result when you copy and paste this code. Before I fix it, you are welcome to request the proper code as an email attachment using my contact form.

Raspberry Pi 3B + model binding all its USB ports for export; Raspberry Pi 4B+ model attached (imported) usb devices: Kingston flashdrive, USB DVD player with media, as well as a Plugable UD-160-A Docking Station/zeroclient with its own Logitech unifying receiver.

With only the Mediatek, Inc. USB drive connected, the Raspberry Pi 3B+ idles at 2.9 watts and uses 4.6-4.9 watts while serving a DVD movie to some other computer that has imported this USB drive. VLC will pop up when drive is imported with attach command.

Putty also can use hostnames instead of IP addresses. Putty requires openssh and uses the default port 22. It is handy for operating headless remote computers.