What is going on is actually quite simple: In order to provide true N-key rollover and all of those extra gaming and other keys, Logitech has adjusted the way that it reports keyboard input events over USB. The USB input report parsing library that is built into the FreeBSD kernel does not support this. If you had not filtered the kernel message output you would have seen messages like
hid_get_item: Number of items(991) truncated to 255
hid_get_item: Number of items(257) truncated to 255
hid_get_item:364: Number of items truncated to 255
The USB input report protocol allows USB HIDs to have two sets of up to 65531 keys each. The current USB standards define the codes for keys 4 (A a) to 231 (⌘ Right GUI) in the first set. A "gaming keyboard" has a lot more keys than that, most of which do not correspond with standardized key codes, and may in fact go in the second set in any case.
USB HIDs conceptually report keyboard activity as a huge bitmap, one bit for every key, describing the current up/down state of all keys on the keyboard. Whilst this is indeed the case for the 8 modifier keys, with codes 224 to 231, usually other keys are reported in the form of an inverted array. The keyboard input report from the device over the wire to the USB host comprises the indices of the bits in the bitmap that are set to 1.
This inverted array form saves space. My 126-key multimedia keyboard that I am typing this answer on right now would need 16 bytes per input report to report the entire keyboard in bitmap form. With the inverted array, however, it uses a mere 8 bytes, 6 of which are the array indices of non-modifier keys.
The problems with this inverted array form are that one does not get something for nothing:
- It limits rollover. With the full bitmap, one would have true N-key rollover (subject to any hardware limitations of the keyboard matrix itself). With the inverted array form, the number of array indices limits the amount of rollover. Conventionally, because the USB HID standards actually describe this "boot" report format in an appendix, USB HID keyboards provide 6 array indices in their reports, allowing up to 6 (non-modifier) keys to be down simultaneously.
- It limits key codes. The array indices are usually 8 bit integers. Make them wider so that they can accommodate more than 256 key codes, and either it takes more than 8 bytes to report 6 simultaneously pressed (non-modifier) keys, or one reduces the number of simultaneously pressed keys that can be reported to less than 6.
Unfortunately, a gaming keyboard needs to be able to report far more than 256 key codes. As you can see from the aforementioned log excerpts, the Logitech gaming keyboard has key codes up to 257 in one case and up to 991 in another. This does not mean that it has 257 and 991 keys. This means that the range of key code values is that wide.
The limitations of the FreeBSD kernel, more specifically of the cut-down version of libusb that is linked into the FreeBSD kernel, are somewhat subtle. They are not, as is often reported, a limit on the bitmap size. They are a limit on what is known in USB parlance as the report count of an input item. FreeBSD caps the report count at 255, printing the aforementioned messages if the actual report count is greater than 255.
This has consequences for both the straight bitmap form and the inverted array form of keyboard input.
- For the straight bitmap form, it means that bitmaps larger than 255 will be truncated at the 255th key code. i.e. only the first 64 bytes of the bitmap will be considered. This caps the range of key codes that will be recognized.
- For the inverted array form, it means that only the first 255 array indices will be used. The first 255 array indices can contain any key code, of course. This caps the effective amount of rollover.
This is simply more of a problem for the former than the latter. No-one sensible would use the inverted array form rather than the bitmap form in the case where this would matter. An inverted array with 255 array indices is a lot less space efficient than the equivalent bitmap form, as a bitmap of the same size could represent 8 times as many key codes with true N-key rollover.
So Logitech wants to report any key code in ranges of either 257 or 991 key codes, to make all of the extra keys work. (Why it is split this way relates to the different "pages" that the codes for different types of keys are grouped into, and the way that input items cannot span multiple "pages". This is USB arcana that is beyond the scope of this answer.) And it wants to give people more than 6-key rollover.
To do so, it switches to a USB input report descriptor format that uses bitmaps and large report counts. (USB allows HIDs to have multiple input report formats, which they can switch amongst.) The FreeBSD kernel does not like this, and problems ensue.
The local fix is to force the Logitech keyboard to keep using the "boot" input report descriptor format, which provides 6-key rollover and cannot send the codes for any of the extra keys on the keyboard, instead of its "native" report descriptor format. This can be done at every system bootstrap with the
usbconfig command, giving it the
add_quirk UQ_KBD_BOOTPROTO subcommand.
The bodge is to put this quirk into the already existing list of pre-defined quirks that is built in to the FreeBSD kernel, and magically applied by the kernel to matching USB devices.
The service fix is to fix the cut-down libusb in the kernel so that it does not cap report counts at 255. No-one has done this, although it has been briefly and superficially discussed on one of the FreeBSD mailing lists.