Test a Linux kernel USB Device Controller driver with testusb

At Bootlin, we recently developed from scratch a new Linux driver for the USB Device Controller found in the Renesas RZ/N1 processor. This driver is already accepted upstream, is currently visible in linux-next and should hopefully be part of the upcoming Linux 6.3 release.

As part of developing this driver, we of course had to… test it! To test a USB Device Controller driver, the obvious idea that comes to mind is to use the available USB gadget drivers in the Linux kernel, to expose a USB mass-storage device, a USB network device, etc. However, these existing USB gadget drivers are not necessarily the best option for this kind of testing: they perform some more or less complex transfers and it can be difficult to find the root cause of an error using these gadget drivers.

Fortunately, a tool exists precisely to perform testing of USB transfers: this tool is called testusb, and it can be found directly in the Linux kernel source code in tools/usb/testusb.c. The tool is quite old and not very well known, but it proved to be very useful for our testing, so in this blog post we are sharing some details on how to use it.

First of all, some documentation can be found at http://www.linux-usb.org/usbtest/.

Here are some details on how we used it. Using testusb requires the following:

  1. The system under test, which contains the USB Device Controller to be tested, which must expose some USB functionality using a USB gadget driver
  2. The test system, which runs the tests themselves, which has a USB Host Controller. On this system, a dedicated USB test kernel module is needed, together with the testusb user-space application.
Typical usage of testusb
Typical usage of testusb

On the tested system, the simple g_zero gadget module (CONFIG_USB_ZERO=m) can be used. Indeed, this gadget is compatible with testusb.

The testusb part that runs on the test system is composed of a kernel driver and a simple user-space program that asks the driver to run the tests. The driver can hang on some failures and, in that case, a test system reboot is needed. So we didn’t use our workstation as the test system.

Instead, since our board that contains the USB Device Controller to be tested also includes a USB Host controller, we used the board itself as a test system, and looped back the USB Host to the USB Device using the appropriate USB cable.

Usage of test-usb where the test and tested system are the same
Usage of test-usb where the test and tested system are the same

For the test system, one needs to enable the USB test driver, using CONFIG_USB_TEST=m. Of course USB Host support must also be enabled, with the relevant drivers depending on your specific hardware.

The user-space application located in tools/usb/ in the Linux kernel code needs to be cross-compiled. A convenient Makefile is provided to this aim in this tools/usb directory.

Once everything is installed, on the system under test load the g_zero module:

# modprobe g_zero

On the test system, load the usbtest module and run the tests:

# modprobe usbtest
# testusb -a -v512
[ 220.276460] usbtest 2-1:3.0: TEST 0: NOP
[ 220.292316] usbtest 2-1:3.0: TEST 1: write 1024 bytes 8 times
[ 220.324711] usbtest 2-1:3.0: TEST 2: read 1024 bytes 8 times
[ 220.364648] usbtest 2-1:3.0: TEST 3: write/512 0..1024 bytes 8 times
[ 220.394705] usbtest 2-1:3.0: TEST 4: read/512 0..1024 bytes 8 times
[ 220.434605] usbtest 2-1:3.0: TEST 5: write 8 sglists 32 entries of 1024 bytes
[ 220.504601] usbtest 2-1:3.0: TEST 6: read 8 sglists 32 entries of 1024 bytes
[ 220.554617] usbtest 2-1:3.0: TEST 7: write/512 8 sglists 32 entries 0..1024 bytes
[ 220.604618] usbtest 2-1:3.0: TEST 8: read/512 8 sglists 32 entries 0..1024 bytes
[ 220.631632] usbtest 2-1:3.0: TEST 9: ch9 (subset) control tests, 8 times
[ 220.840328] usbtest 2-1:3.0: TEST 10: queue 32 control calls, 8 times
[ 220.891842] usbtest 2-1:3.0: TEST 11: unlink 8 reads of 1024
[ 221.238312] usbtest 2-1:3.0: TEST 12: unlink 8 writes of 1024
[ 221.588363] usbtest 2-1:3.0: TEST 13: set/clear 8 halts
[ 221.724579] usbtest 2-1:3.0: TEST 14: 8 ep0out, 1..1024 vary 512
[ 222.328352] usbtest 2-1:3.0: TEST 17: write odd addr 1024 bytes 8 times core map
[ 222.364632] usbtest 2-1:3.0: TEST 18: read odd addr 1024 bytes 8 times core map
[ 222.394700] usbtest 2-1:3.0: TEST 19: write odd addr 1024 bytes 8 times premapped
[ 222.434615] usbtest 2-1:3.0: TEST 20: read odd addr 1024 bytes 8 times premapped
[ 222.474661] usbtest 2-1:3.0: TEST 21: 8 ep0out odd addr, 1..1024 vary 512
[ 223.086524] usbtest 2-1:3.0: TEST 24: unlink from 8 queues of 32 1024-byte writes
[ 223.182482] usbtest 2-1:3.0: TEST 27: bulk write 0Mbytes
[ 223.222741] usbtest 2-1:3.0: TEST 28: bulk read 0Mbytes
[ 223.250355] usbtest 2-1:3.0: TEST 29: Clear toggle between bulk writes 8 times

Obviously, all tests should end with a success… if not, you need to debug your driver.

The g_zero gadget also support isochronous transfers in its alternate configuration. The usbtest module can support isochronous tests and in order to do so with the g_zero gadget, we have to tell usbtestto use this second configuration:

# modprobe g_zero
# modprobe usbtest alt=1

Then simply run the test:

# testusb -a -v512

testusb can be used with an USB composite function gadget, but in our tests we didn’t use this possibility.

However, some of the kernel provided pre-composed USB Gadget drivers are interesting to test specific use-cases. For instance:

  • g_mass_storage (CONFIG_USB_MASS_STORAGE): this gadget driver has the characteristic of halting some endpoints, which is useful to test the halt feature.</br/>
    To use this gadget, on the target, create a file for the mass storage backend and load the module:

    dd if=/dev/zero of=/tmp/storage.part bs=1M count=8
    modprobe g_mass_storage file=/tmp/storage.part

    On the PC the new removable storage device appears. You can format it and transfer file to/from this device.

  • g_ether (CONFIG_USB_ETH): this gadget driver has the characteristic of using transfer sizes that are not a multiple of MaxPacketSize, which is useful to test transfers spanned on multiple packets with the last one less than MaxPacketSize.
    To use this gadget driver, we used a simple HTTP server on a PC serving test files of various sizes (700 bytes, 1000 bytes, 1100 bytes and 64 MB). wget calls on the tested system did the download and Wireshark on the PC helped to check the transferred packet size. Even if the USB request spans on multiple USB packets, the USB request should be completed with the Ethernet packet size exposed by Wireshark.
  • g_serial (CONFIG_USB_G_SERIAL): this gadget driver has the charateristic that in a basic configuration each byte sent is echoed, which means that very short packets are transferred.
    To use this gadget, on the target, do a software loopback: cat /dev/ttyGS0 > /dev/ttyGS0
    On the PC: picocom -b115200 /dev/ttyACM0
    Each character you type on the PC (picocom) is echoed and when you hit ‘enter’, the whole buffer is echoed.

We hope that this short blog post about about testusb and pre-composed gadgets will help you to reproduce, isolate, find the root cause of some issues and finally debug your USB Device Controller drivers.

One thought on “Test a Linux kernel USB Device Controller driver with testusb”

Leave a Reply