The reason why this USB HID device works in Windows is because there may be specific drivers written for it, the reason why it works for GRUB is that the device operates in so-called boot mode. USB HID devices support two modes of operation, boot mode and HID mode. The former makes it possible to use the device in BIOS without having BIOS implement a full HID stack. The latter provides more flexibility such that extended features can be added (like extra buttons). Support for boot mode is indicated by the presence of the Boot Interface Subclass flag in the interface descriptor (see lsusb -v -d 05a4:8003
):
Interface Descriptor:
...
bInterfaceSubClass 1 Boot Interface Subclass
A quick grep through the Linux kernel sources (3.14-rc8) did not show a special driver for this USB HID device. Therefore, the kernel falls back to the generic HID driver (hid-generic
). It seems however, that there is an incompatibility with this device and the driver used by Linux.
To see if whether the device generates any (invisible) activity, install the evtest
program and run sudo evtest
. When prompted for a device, enter the number matching your keypad. If things work, then you should see events on pressing keys.
If you do not see any keys and you want to debug this further, install wireshark
to capture the USB interface (using usbmon). As you can see in the lsusb
output, the input device is attached to bus 1, hence you should specify usbmon1
as interface. The command to capture USB traffic is:
sudo dumpcap -i usbmon1 -w - > usb.pcapng
(This method allows you to write usb.pcapng
as a regular user, the alternative is making /dev/usbmon1
readable for the regular user or changing the ownership after the file is created.)
Analyzing a USB HID capture
As the unprivileged user, you can now start analyzing the captured USB traffic. You will need to know the basics of USB and HID to make some sense of it. The simplest HID devices usually have two endpoints, a bi-directional control endpoint (EP0, this always exists) and an interrupt endpoint for transferring data from the device to the USB host ("the computer").
I have made an example annotated capture and uploaded it to https://www.cloudshark.org/captures/a6c9580208b7. I suggest you to read the packet comments to get a better idea how it works. When you open this packet offline in Wireshark, I recommend to add some color (View -> Coloring Rules):
frame.comment
- easier see which frames got a command
usb and frame.len == 64
(or its negation, usb and not frame.len == 64
) - notice faster which packets got a payload.
It is also recommended to add some extra columns (right-click on a column, Column Preferences):
usb.urb_type
- see whether a packet is submitted (URB_SUBMIT
) to or a received from the device (URB_COMPLETE
).
usb.transfer_type
- see whether you are looking at control data (over EP0) or other data (such as interrupt data for USB HID devices).
usb.endpoint_number.direction
- see the direction of the data transfer (IN is from device to host ("PC"), OUT is from host ("PC") to device).
I have dragged these columns next to the Len(gth) column and resized such that the Info column is still visible on the right of those three USB columns.
With these thing set-up, you can now start the analysis. Pressing any keys should generate an interrupt with data conforming to the format from the HID descriptor. If not, then there are probably some vendor-specific commands needed (i.e. a special driver) or there is a bug in the Linux USB (HID) stack.
Best Answer
Ok, turns out the answer was staring me in the face.
Firstly, whether using our custom driver, or using the generic one that normally takes over the device, it's still all ultimately controlled by HID, and not USB.
Previously I tried to unbind it from HID, which is not the way to go. HID has sub-drivers, the one that takes over devices that have no specialized driver is called generic-usb. This is what I needed to unbind from, before binding to hid-g19. Also, I needed to use the HID address which looks like "0003:046d:c229.0036" and not the USB address which looks "1-1.1:1.1".
So before rebinding I would see this on dmesg:
Then I do:
And then I see on dmesg:
So like I said, staring me in the face, because the two key pieces of information are the first two things on the line when the device binds...