USB Polling Rate Adventure

I like dance games, that's no secret. But recently I had some problems with my dance pad: getting those "Fantastics" was just too damn hard.

At first I, of course, assumed that it was just me being bad at the game, but then I started investigating.

Investigation

Let's have a look at the USB device with lsusb:

$ lsusb Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 006: ID 17ef:6099 Lenovo Bus 001 Device 005: ID 046d:085c Logitech, Inc. Bus 001 Device 004: ID 17ef:608d Lenovo Bus 001 Device 003: ID 6667:c006 <----- THAT'S THE DANCE PAD Bus 001 Device 002: ID 8087:0a2b Intel Corp. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

The device with vendorID:productID pair 6667:c006 is the dance pad. Let's have a closer look:

$ lsusb -vd 6667:c006 Bus 001 Device 003: ID 6667:c006 Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 1.10 bDeviceClass 0 (Defined at Interface level) bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 8 idVendor 0x6667 idProduct 0xc006 bcdDevice 1.02 iManufacturer 1 www.laser-tek.pl iProduct 2 Mata taneczna L-TEK iSerial 0 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 34 bNumInterfaces 1 bConfigurationValue 1 iConfiguration 0 bmAttributes 0x80 (Bus Powered) MaxPower 100mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 3 Human Interface Device bInterfaceSubClass 0 No Subclass bInterfaceProtocol 0 None iInterface 0 HID Device Descriptor: bLength 9 bDescriptorType 33 bcdHID 1.01 bCountryCode 0 Not supported bNumDescriptors 1 bDescriptorType 34 Report wDescriptorLength 32 Report Descriptors: ** UNAVAILABLE ** Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 3 Transfer Type Interrupt Synch Type None Usage Type Data wMaxPacketSize 0x0008 1x 8 bytes bInterval 10 <----- REQUESTED POLLING RATE Device Status: 0x0000 (Bus Powered)

It turns out that the USB device built into the pad requests a polling interval of 10ms. Given that the standard ITG timing window for "Fantastic" is only 21ms, this makes it very hard to reliably get good scores. Not good.

The Linux kernel takes the polling interval that's requested by the device and rounds it down to the nearest power of two. That's the rate it will effectively use. And indeed using wireshark and the usbmon kernel module I verified that this is actually the case. Here is the capture file. You can see that the reports are roughly 8ms apart.

Attempted Fix #1

At that point I found a nice page on the Arch Linux wiki that explains everything I never wanted to know about USB polling rates in Linux. It also mentions a parameter for the usbhid kernel module that allows to force a faster polling rate.

Great, this will fix the issue!

# rmmod usbhid && modprobe usbhid jspoll=1

Dance pad replugged, wireshark running, and ... nope! Didn't work.

The dance mat doesn't register as a joystick, but as a gamepad and the jspoll parameter only affects joysticks. Interestingly, there is no equivalent parameter for gamepads. Yay for consistent interfaces!

Attempted Fix #2

Haha, what did I learn to write code for. Let's modify the kernel to force a polling rate of 1ms for all devices. (Yes, I was desperate.)

About two hours later I had my own modified kernel and it actually booted without issues. Still, no dice. The polling rate didn't change.

I read up a bit more on USB and found out that for USB 3 the polling of devices is apparently not done by the kernel, but by the host controller. That's why my patch didn't work.

To verify that theory I fired up my only machine without USB 3 hardware, an old Thinkpad. The patch worked! Unfortunately that machine is too slow and has overheating issues, so I can't run StepMania on it.

Communication with the Manufacturer

At that point I didn't want to invest even more time in the issue and decided to write to the manufacturer to ask for help. I sent them the lsusb output, the wireshark capture file and some more Linux debug information accompanied by a verbose description of what is wrong.

Unfortunately, the only "help" I got out of them was a comment that my problems must be caused by a software issue, probably Linux related.

We cannot help you.

To be fair I never tried the pad with Windows, so I don't know if that would have changed anything.

Replacing the Controller

Not wiser than before I decided to replace the original USB controller with an Arduino micro board. The micro is a good fit for a project like that because it has USB built in. It was about an afternoon of work to get everything wired up (not counting the shopping trip to get all necessary material) and writing the software running on the board to register as an HID gamepad.

Getting the Arduino into the casing was a bit of a challenge, because it was higher than the original controller, so I had to carve out more space with an old screwdriver.

First time I turned it on, it actually worked. Nice!

You can find the software running on the Arduino on my GitLab page.

Conclusion

The polling issues have been resolved. My scores are not significantly better yet (it has been one day with the new controller so far), but now I can at least be sure that all the mistakes are my own doing.