I2C kernel driver testing using virtme

The last few days I worked on the MCP23017 kernel driver and wondered about a good method to test my changes in a comfortable way. Fortunately I built myself an i2c-tiny-usb adapter some time ago, which is supported by mainline Linux. Thus any system with USB host support could be used for testing the above chip. My minimal test-setup can be seen in the image below. Basically I supplied 5V, Ground, SCL & SDA from the adapter to MCP23017, connected the low-active reset pin to 5V and the address-selection pins to Ground.

I2C-Tiny-USB with MCP23017

The obvious platform would be my development notebook, but rebooting for every test is a annoying. Kernel developers working on scheduler e.t.c. often use qemu for testing, so I wondered if I could do the same by forwarding the USB device. Also I do not want to spend time for configuring a rootfs for the emulated system. Thankfully Andy Lutomirski wrote virtme, which takes care of this by using the normal rootfs. I created a Debian package, which can be found on the following git repository at collabora.

$ cd ~/src/collabora/linux
$ # edit .config for usage with qemu (enable VIRTIO & 9P related options)
$ make -j4
...
Kernel: arch/x86/boot/bzImage is ready  (#13)
  MODPOST 3326 modules
$ # ensure your user has permissions for USB device, so that qemu can grab it
$ virtme-run --kdir ~/src/collabora/linux --qemu-opts -usb -usbdevice host:0403:c631
kernel boot messages...
[    3.044289] virtme-init: udevd not found
[    3.126059] usb 1-1: New USB device found, idVendor=0403, idProduct=c631
[    3.127210] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[    3.127837] usb 1-1: Product: i2c-tiny-usb
[    3.128336] usb 1-1: Manufacturer: Till Harbaum
[    3.131808] i2c-tiny-usb 1-1:1.0: version 1.05 found at bus 001 address 002
[    3.138173] i2c i2c-0: connected i2c-tiny-usb device
virtme-init: console is ttyS0
root@(none):/# i2cdetect -l
i2c-0   i2c         i2c-tiny-usb at bus 001 device 003  I2C adapter
root@(none):/# i2cdetect -y 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --                       
root@(none):/# echo mcp23017 0x20 > /sys/bus/i2c/devices/i2c-0/new_device
[   71.762916] i2c i2c-0: new_device: Instantiated device mcp23017 at 0x20
root@(none):/# cat /sys/kernel/debug/gpio 
gpiochip0: GPIOs 496-511, parent: i2c/0-0020, mcp23017, can sleep:
root@(none):/# echo 496 > /sys/class/gpio/export
root@(none):/# echo 497 > /sys/class/gpio/export
root@(none):/# echo 498 > /sys/class/gpio/export
root@(none):/# echo out > /sys/class/gpio/gpio497/direction
root@(none):/# echo out > /sys/class/gpio/gpio498/direction
root@(none):/# echo 1 > /sys/class/gpio/gpio498/value
root@(none):/# cat /sys/kernel/debug/gpio
gpiochip0: GPIOs 496-511, parent: i2c/0-0020, mcp23017, can sleep:
 gpio-496 P0.0 (sysfs       ) in  lo   
 gpio-497 P0.1 (sysfs       ) out lo   
 gpio-498 P0.2 (sysfs       ) out hi 
root@(none):/# cat /sys/kernel/debug/regmap/0-0020/registers
00: fff9
02: 0000
04: 0000
06: 0000
08: 0000
0a: 0808
0c: 0000
0e: 0000
10: 0000
14: 0004
# emulating short power-loss by pulling reset to low for some seconds
root@(none):/# cat /sys/kernel/debug/regmap/0-0020/registers
00: fff9
02: 0000
04: 0000
06: 0000
08: 0000
0a: 0808
0c: 0000
0e: 0000
10: 0000
14: 0004
# that's not what is in the registers after power-loss, its from regmap
# cache, since regmap does not know about the power-loss.
root@(none):/# cat /sys/kernel/debug/gpio
[  170.011018] mcp230xx 0-0020: restoring reg 0x00 from 0xffff to 0xfff9 (power-loss?)
[  170.098648] mcp230xx 0-0020: restoring reg 0x05 from 0x0000 to 0x0808 (power-loss?)
gpiochip0: GPIOs 496-511, parent: i2c/0-0020, mcp23017, can sleep:
 gpio-496 P0.0 (sysfs       ) in  lo   
 gpio-497 P0.1 (sysfs       ) out lo   
 gpio-498 P0.2 (sysfs       ) out lo

So my patch moving to regmap based caching work as expected and surpass the original driver, since it can detect & fix context-loss scenarios in debugfs :) While those are quite unlikely to appear on production hardware, MCP23xxx is commonly used by hobbyists and their hardware is often less stable.

As a by-product of my work on mcp23xxx I also had to fix i2c-tiny-usb, though. Before kernel 4.9 the driver worked flawlessly, but since then it will complain, that the transfer buffers are not DMA capable. A patch for that has been sent and will hopefully be backported.

All in all I must say the virtme based testing was really nice and I hope I can use it again.

– Sebastian