Author(s): Paul Brown

The Linux kernel has many interesting but unknown services. USB/IP, in particular, is one that you'll probably wonder why you have never encountered. USB/IP lets you use USB devices connected to other machines on your network as if they were plugged directly into your computer.

This happened to me recently: I have an oldish Brother multifunction printer/scanner/fax machine. It is an okay thing to have and quite useful, but it is as dumb as brick and has no network capabilities at all. This means you must have a computer plugged into it directly if you need to use it. If you want to share it over a network, which of course you do, you have to make the computer a server and have it manage the network side of things.

Brother supplies some drivers for Linux, but they are closed source. This means that, if something doesn't work, you face a familiar conundrum: Firstly, Brother will not assign an engineer to sort out a problem for a system used by a minority of desktop users and a machine that is at least seven years old. Secondly, you can't solve things yourself because the drivers are closed source.

Unfortunately for me, something doesn't work.

Brother's scanner driver refuses to play ball with SANE's network printing protocol. That means, if I want to scan without lifting my laptop off the table, walking into the living room, unplugging it from the server, and attaching my computer directly to the lunk, I have to proceed as follows:

Open an SSH session from my laptop to the server. Scan from the command line with scanimage . Copy the file that scanimage creates over to my laptop using scp , and open it in an image visualizing program. Make sure that the scan is the correct in size, color, contrast, and alignment. If not, get up and adjust the piece of paper and the scanimage flags Start all over again.

This is less than ideal, especially when you have graphical utilities, like, say, XSane [1], a comprehensive graphical application for all your scanning needs.

Is there a way to make the process less cumbersome? Thankfully, there is. It's called USB/IP (read: USB over IP); it is a set of modules and tools that comes with all modern kernels from 3.17 onwards. USB/IP allows you to set up a USB server on one machine (e.g., on your printer/scanner server) and share it with the rest of your network as if it were connected directly to the client machine.

USB/IP in Practice

This may sound like a godsend, but USB/IP, precisely because not very many people use it, is a bit finicky. All distributions provide the modules, as they are a standard part of modern kernels. The suitability of the tools you need for setting up the server and sharing to the client, however, is a different matter. The combination that worked for me was to use an up-to-date Debian as a server, and machines loaded with Ubuntu and KDE neon (which is currently based on Ubuntu 16.04) as clients. If you try anything else, the steps you follow should be very similar, but your mileage may vary.

Although the USB/IP modules ( usbip-core , usb-host , and vhci-hcd ) are standard, you will have to install the tools that allow you to set up a server and start a client. In Debian, the package you need is called simply usbip , but Ubuntu's usbip package contains out-dated and flawed versions of the software. I have no idea why this exists – very confusing. If you have installed the usbip package before reading this, purge it now. What you are looking for is a package called linux-tools-generic . Install that.

Once you have everything installed on your server and clients, connect to the machine that will act as the USB/IP server and load two modules into the kernel:

su -c "modprobe usbip-core; modprobe usbip-host"

Then to start the server; still as root, do:

su -c "usbipd"

By starting the server like this, you will be able to see what it is doing. Your computer will display the machines that try to connect and show whether they are successful. This will help you troubleshoot during the first few runs. Once you are confident everything works as it should, you may want to run the server with the -D flag:

su -c "usbipd -D"

This will push usbipd to the background and have it run as a daemon.

You can now check what USB devices you can share. To do this, use the instruction shown in Listing 1, line 1. The list command is, as its name implies, for listing devices. The -l flag tells usbip to list the devices connected "locally." As you will see later, you use a similar instruction to find out which devices are available on the server from the client.

Listing 1 Shareable Devices $ su - c "usbip list -l" - busid 1-1 (0474:025f) Sanyo Electric Co., Ltd : unknown product (0474:025f) - busid 1-2 (04f9:01eb) Brother Industries, Ltd : MFC-7320 (04f9:01eb) - busid 1-4 (8087:0a2a) Intel Corp. : unknown product (8087:0a2a)

As Listing 1 shows, there seems to be three devices you can share, but really there are only two: The last one ( busid 1-4 ) is the machine's internal USB hub doohickey (pretty sure that's its technical name). The two devices you can share are busid 1-1 , a Sanyo webcam, and busid 1-2 , the multifunction printer/scanner/fax machine I mentioned earlier.

Notice that, in the case of the webcam, the server has no idea what the device is. But, then again, it doesn't have to: The task of figuring out what a device is and how to use it falls to the client.

The next step is binding a device to the server, so it can be shared. To bind a device, you have to pass the device's bus ID to the server:

su -c "usbip bind -b 1-1"

The -b stands for "bus ID," and, as per Listing 1, the instruction above shares the Sanyo video camera.

To share the printer/scanner, you should do this:

su -c "usbip bind -b 1-2"

To stop sharing a device, you should use the unbind command. The command:

su -c "usbip unbind -b 1-2"

disconnects the printer/scanner from the server, for example.

Client Side

To get the Ubuntu/neon client set up (see the "Ubuntu Paths" box), load the modules you need into the kernel:

sudo modprobe usbip-core sudo modprobe vhci-hcd

To query the server and see what devices it has on offer, use another variant of the list instruction:

sudo usbip list -r <server>

where <server> is the name or IP address of the USB/IP server. Listing 2 shows what querying the server looks like.

Listing 2 Querying the Server $ sudo usbip list -r 192.168.1.24 Exportable USB devices ====================== - 192.168.1.24 1-2: Brother Industries, Ltd : MFC-7320 (04f9:01eb) : /sys/devices/pci0000:00/0000:00:14.0/usb1/1-2 : (Defined at Interface level) (00/00/00) 1-1: Sanyo Electric Co., Ltd : unknown product (0474:025f) : /sys/devices/pci0000:00/0000:00:14.0/usb1/1-1 : Miscellaneous Device / ? / Interface Association (ef/02/01)

Ubuntu Paths Ubuntu saves the USB/IP tools in a weird place that is not in your $PATH . This makes calling usbip cumbersome, since you have to prepend the whole path ( /usr/lib/linux-tools-<version number>/ ) each time you want to use it. This gets boring quickly, so you may want to make a soft link to the tool somewhere in your path. As it is a command you should issue only with root privileges, you can do: sudo ln -s /usr/lib/flfl linux-tools-<version number>/usbip /usr/sbin/ For the rest of the article, I'll assume you have done that.

The listing gives you the two devices, the printer/scanner and the webcam, that the server is sharing.

To start using a device, you have to first attach it

:

sudo usbip attach -r <server> -b <bus ID>

As before, <server> is your server machine's name or IP ( -r stands for "remote") and <bus ID> is the bus ID of the device, as shown using the list command in Listing 2.

To attach the Sanyo webcam shown in Listing 2, for example, you should do this:

sudo usbip attach -r 192.168.1.24 -b 1-1

Note that some versions of usbip use -h (for "host") instead of -r .

If everything goes well, the device should now show up on your system as if it were plugged directly into one of your USB ports. Listing 3 shows what the list of USB devices looks like on the client. Figure 1 shows what it looks like to see yourself from your laptop's webcam and from a webcam connected to the USB server several feet away.

Listing 3 Client USB Devices $ lsusb <Bus 003 Device 004: ID 0474:025f Sanyo Electric Co., Ltd> Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 003: ID 058f:d102 Alcor Micro Corp. Bus 001 Device 002: ID 8087:0a2a Intel Corp. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Figure 1: A question of perspective: video feed from the laptop camera (bottom right) and from a webcam connected to the USB/IP server several feet away (center).

Attaching the printer/scanner had the interesting effect of providing the client with two printers: one network printer via CUPS and one virtual USB-connected printer (Figure 2), even though both are the same physical machine.

Figure 2: Two printers in one. The top printer is the virtual USB/IP printer, and the bottom one is detected via CUPS, although they are both the same physical machine.

Just to show how the client sees the printers differently, take a look at Figure 3. On the left is what you get if you tell the CUPS network printer to print a test page – it is the server that sends the test page to the printer, hence the Debian/CUPS logos. By contrast, on the right is the test page from the virtual USB/IP printer. As the machine "thinks" the printer is connected locally to one of its USB ports, it sends its own, internal test page.

Figure 3: Same printer, but different test pages: on the left, the test page from the networked CUPS printer; on the right, the test page from the USB/IP printer.

With a bit of fiddling (and by "a bit" I mean "hours of hair-wrenching frustration") you can get even the most stubborn scanner to work as it should (Figure 4).

Figure 4: Beat your scanner into submission and make it allow scanning from the network thanks to USB/IP.

Detaching

Devices that are attached to a client via USB/IP are blocked for the rest of the network, so at some point, you will want to detach them and let others use them.

To detach a device, first you need to know to which virtual port each device is attached. Listing 4 shows an example from a client with the printer/scanner and webcam attached. If you are not sure which device is which, look at the vendor/product line and compare the numbers in brackets with what you get from lsusb .

Listing 4 Connected Ports $ sudo usbip port Imported USB devices ==================== Port 00: <Port in Use> at High Speed(480Mbps) unknown vendor : unknown product (0474:025f) 8-1 -> usbip://192.168.1.24:3240/1-1 -> remote bus/dev 001/002 Port 01: <Port in Use> at Full Speed(12Mbps) unknown vendor : unknown product (04f9:01eb) 8-2 -> usbip://192.168.1.24:3240/1-2 -> remote bus/dev 001/004

Armed with the port number, you can detach a device with:

sudo usbip detach -p <port number>

So, to detach the printer/scanner ( in Listing 4), do:

sudo usbip detach -p 01