How to use USB/IP on an NSLU2
Note: a binary release of USB/IP 0.1.5 is available and discussed at http://sourceforge.net/forum/forum.php?thread_id=1786127&forum_id=418507
The Linksys NSLU2 (aka Slug) is a small, cheap, low-power unit with an XScale ARM processor, 10/100 ethernet, and two USB2.0 host ports. It runs Linux and there is an active development community at http://nslu2-linux.org/. Linux possibilities range from small systems running entirely from the internal flash (8 MBytes) to fully-fledged distributions running from external USB hard disks. In my case I run Debian / NSLU2 from a 1 GByte external flash drive, and the instructions below relate to this system.
USB/IP is a method of transfering USB data between two Linux systems across a network. Specifically it consists of two kernel modules, one for each end of the link, and control utilities. USB peripherals connected to one system can be exported to the other system where they appear as if they were locally connected. USB/IP is not yet included in the official Linux kernel, but is available as a patch. It has been developed by Takahiro Hirofuchi of the Nara Institute of Science and Technology, Japan and is now an open source project. The main webpage is at http://usbip.sourceforge.net/.
Running USB/IP on the slug is an appealing idea. At present a slug can be used to interface many USB peripherals (printers, storage devices, scanners, ...) to a remote PC but in each case specific server software is needed (CUPS, NFS, SANE, ...). Often this works well, but in some cases this server software may make excessive demands on the slug's limited processing power, be complex to install, or otherwise difficult. Other peripherals may not have servers to support remote operation at all. In contrast, USB/IP provides a way to make any peripheral accessible remotely with no additional software to be installed on the slug.
There are, however, some disadvantages to the USB/IP approach. In particular it is not able to make a printer or storage device available to multiple clients simultaneously in the way that CUPS and NFS do. Also, users with non-Linux clients will unable to use USB/IP unless it is ported to their operating system.
This page describes how to compile and use USB/IP between an x86 Linux PC and a slug. It assumes that you're running Debian on the PC and Debian / NSLU2 on the slug, though much of the procedure will be the same for other distributions. I also assume that you'll cross-compile the slug code on the PC.
At present I suggest that this is not something that NSLU2 "newbies" attempt to do. In time I expect that the procedure will become simpler and better-documented; when that happens, this should be something that everyone can use.
This is based on USB/IP version 0.1.4.
You'll need a cross-compilation environment. The easiest way to get this is to install the pre-compiled packages available from Emdebian (http://www.emdebian.org/tools/crosstools.html); at the time of writing you need to follow the link to "download raw debian packages", rather than using apt-get. These packages will provide you with cross-compiling versions of all of the gcc and binutils programs with the prefix arm-linux-gnu-, and suitable core libraries.
To compile the USB/IP kernel modules you need kernel source trees in which kernels have been built, one for the PC and one for the slug. You may already have these but if you installed binary kernel packages you need to make one now, as follows:
Get the kernel source. I'm using 2.6.17 on my PC and my slug; it will probably work with other reasonably recent versions, but I don't know for sure:
# apt-get linux-source-2.6.17
Unpack two copies:
$ tar xjf /usr/src/linux-source-2.6.17.tar.bz2 $ mv linux-source-2.6.17 linux-source-2.6.17-x86 $ tar xjf /usr/src/linux-source-2.6.17.tar.bz2 $ mv linux-source-2.6.16 linux-source-2.6.17-arm
Copy over your existing kernel configurations. This will work if you installed Debian kernel image packages:
$ cp /boot/config linux-source-2.6.17-x86/.config $ scp slug:/boot/config linux-source-2.6.17-arm/.config
Build in the x86 tree:
$ cd /path/to/linux-source-x86 $ make
Build in the arm tree:
$ cd /path/to/linux-source-arm $ make ARCH=arm CROSS_COMPILE=arm-linux-gnu-
(Apparently you can interrupt the build process after it has reported "SPLIT", as all of this files that are needed by the USB/IP modules have been built by then.)
The following packages are also needed, according to the USB/IP README file:
To install hwdata, just "apt-get hwdata" on both the PC and the slug.
In the case of the libraries, for the PC you need to install the libraries and their -dev packages. For the slug, you need to install only the libraries. Then, so that you can cross-compile against these libraries, you need to install the slug versions of the libraries and their -dev packages on the PC using dpkg-cross. (Basically, dpkg-cross takes a -arm package containing /usr/include and /usr/lib files and turns it into a package that you can install on your PC under /usr/arm-linux-gnu/include and /usr/arm-linux-gnu/lib, where the cross compiler looks.)
I find that the easiest way to install slug libraries and -dev packages for cross-compiling is like this:
slug# apt-get -d install libXYZ libXYZ-dev (download only) slug# scp /var/cache/apt/arhives/libXYZ*.deb pc:/tmp slug# apt-get install libXYZ pc# dpkg-cross -i /tmp/libXYZ*.deb
Remember to apt-get clean on the slug afterwards to get rid of the unwanted .debs.
Download and unpack
Download the USB/IP tarball from its sourceforge page (http://sourceforge.net/projects/usbip/, then click on "download").
Unpack two copies of it:
$ tar xzf usbip-0.1.4.tar.gz $ mv usbip-0.1.4 usbip-0.1.4-x86 $ tar xzf usbip-0.1.4.tar.gz $ mv usbip-0.1.4 usbip-0.1.4-arm
You might like to review the included README files at this point.
Apply the following fixes to the arm version:
In src/configure.ac, comment out the lines:
(These tests check if malloc(0) and realloc(0) behave as the GNU C library does. For cross-compilation they can't test this directly so they are pessimistic, and the compilation will fail. But we know that in our case the slug is using the GNU C library.)
In src/include/usbip_common.h, remove the "__attribute__((packed))" at the end of the declaration of struct usb_device.
(This causes the whole structure to be unaligned, which is bad; I think that the author intends just that the fields have no gaps between them, which seems to happen even without this attribute.)
Build kernel modules
Build the x86 kernel modules. There is one directory per kernel version; in my case I use 2.6.17:
$ cd /path/to/usbip-0.1.4-x86/driver/2.6.17/ $ make KSOURCE=/path/to/linux-source-2.6.17-x86
Build the arm kernel modules:
$ cd /path/to/usbip-0.1.4-arm/driver/2.6.17 $ make KSOURCE=/path/to/linux-source-2.6.17-arm ARCH=arm \ CROSS_COMPILE=arm-linux-gnu- $ scp *.ko slug:/somewhere
Build the utilities
First build the x86 utilities:
$ cd /path/to/usbip-0.1.4-x86/src $ aclocal; autoheader; automake-1.9 -a -c -f; autoconf $ ./configure $ make # make install
You probably need to do this so that the libraries can be found:
echo "/usr/local/lib" >> /etc/ld.so.conf ldconfig
Now for the ARM versions. I don't know if these are needed, but they were suggested by some autotools documentation that I read:
$ export CC=arm-linux-gnu-gcc $ export AR=arm-linux-gcc-ar $ export RANLIB=arm-linux-gnu-ranlib $ export AR=arm-linux-gnu-ar $ export AS=arm-linux-gnu-as $ export LD=arm-linux-gnu-ld $ export NM=arm-linux-gnu-nm
If you do set them, be sure to unsetthem (or just use a new shell) before you next compile x86 code.
$ cd usbip-0.1.4-arm/src/ $ aclocal; autoheader; automake-1.9 -a -c -f; autoconf $ ./configure --build=`./config.guess` --host=arm-linux-gnu $ make $ make install DESTDIR=/path/to/tmp-arm-usbip
Now copy them over to the slug:
$ cd /path/to/tmp-arm-usbip $ tar cf usbip.tar * $ scp usbip.tar slug:/tmp slug# cd / slug# tar xf /tmp/usbip.tar
slug# echo "/usr/local/lib" >> /etc/ld.so.conf slug# ldconfig
Try it out
On the slug:
Plug in a usb device.
Verify its presence using lsusb or /proc/usb
Start up USB/IP:
# insmod /path/to/usbip.ko # usbipd -D
Find the device that you want to share, and export it:
# bind_device --list # bind_device --usbip 1-1.4
On the PC:
Start up USB/IP:
# insmod /path/to/vhch-hci.ko
See what the slug is exporting, and attach to it:
# usbip --list slug # usbip --attach slug 1-1.4
See that it is attached:
# usbip --port # lsusb
Now attempt to use the device. If it's a keyboard you can probably just type on it straight away. A mass storage device will probably have created its /dev/sd* node and you can mount it.
Congratulations if you get this far. Please share your experiences.