1. 21
  1.  

  2. 7

    For some reason the developers of USB hardware seem to build their firmware’s behavior off of USB traffic logs of similar device instead of just reading the freely available specs, and this is just another instance.

    USB mass storage drivers still need to emulate the precise (and accidental) behavior of Windows 95 OSR 2.x’s driver because some USB sticks hang if truly independent requests come in a different order than they expect. That is, they don’t parse out a request but have a state machine[0] that waits for request type A, then for request type B, … and goes belly up if B appears first.

    [0] probably more like receive_packet_A(); receive_packet_B();

    1. 2

      I had a Noppoo Choc mini with nkro, but the implementation was buggy and I’d get double letters in macos (unusable) and occasional double letters in Linux. I used a blue cube adapter to force it into the boot protocol.

      Also, isn’t it also a limitation on how you wire your keyboard?

      1. 2

        I had a Noppoo Choc mini with nkro, but the implementation was buggy and I’d get double letters in macos (unusable) and occasional double letters in Linux. I used a blue cube adapter to force it into the boot protocol.

        Unfortunately, buggy firmware in USB devices is ridiculously common.

        HID stacks in OSes/windowing systems also don’t necessarily treat edge cases or rarely used report descriptor patterns equally, so you can end up with macOS, Linux/X11, and Windows doing slightly different things.

        It’s likely your issue could have been worked around software side too, I assume it worked “correctly” in Windows? I’m not aware of a generic HID driver for macOS which lets you arbitrarily rewrite report descriptors and reports into a format that WindowServer/Core Graphics deals with as intended. I’m guessing there might be some kind of built-in system for this in Linux or Xorg though.

        Also, isn’t it also a limitation on how you wire your keyboard?

        Yes, definitely, though that’s not as simple as supporting a hard limit of N simultaneous key presses, but rather that certain combinations of key presses become ambiguous, depending on which keys are wired to the same matrix rows and columns.

        1. 2

          I hear some old USB NKRO keyboards used ridiculous hacks like enumerating as multiple keyboards behind a hub, with the first keyboard reporting the first six scancodes, the second reporting the second, etc., or something. Of course, this is a completely ridiculous and unnecessary hack which implies that the people designing the keyboard don’t understand HID (or that the HID stacks of major OSes were too buggy at the time to work properly, perhaps?)

          As for keyboard wiring, that’s a separate matter. My post discusses the limitations of the USB protocol. What the keyboard microcontroller does to ascertain which keys are pressed is entirely up to it. In practice, to save cost keyboards use a key matrix, which creates key rollover limitations. More expensive NKRO keyboards tend to still use key matrices, as I understand it, but add some diodes to the matrix which facilitates NKRO if and only if the assumption that only one key will change between key scans is not violated (a fair assumption if the scan rate is high enough, due to the infeasibility of pressing two keys at exactly the same time.)

          FWIW, I also seem to recall that it’s common for modern “NKRO” keyboards to actually only be 10-key rollover, on the premise that humans only have 10 fingers (feels like dubious marketing to me.) I’m unsure as to whether this is to do with the key matrix, or whether they just decided to use a 10-element array as their reporting format rather than a bitfield.

          However, nothing stops you from making a keyboard which, for example, wires every key individually up to a microcontroller with hundreds of pins (and thus has the truest possible NKRO). It would simply be prohibitively expensive to do so, less because of the MCU, more because of the PCB layers it would require; I worked this out some time ago and suspect it would take about an 8-layer PCB.

          The Model F keyboard is known for supporting NKRO as an inherent benefit of its capacitative sensing, unlike its successor the Model M. Someone made an open hardware controller for existing Model F keyboards, enabling them to be retrofitted with USB, with full NKRO support.

          1. 1

            Can you explain why a hundred traces would require multiple PCB layers? In my mind, the MCU goes in the middle, with traces spidering out to each of the keys, and a ground belt surrounding the board. A second layer would be used to get the data and power into the MCU.

            1. 1

              Maaaaaybe this would be feasible with a large QFP/QFN package? The chip I was looking at was only available as BGA with the necessary pin count; the escape routing seemed infeasible with a low number of layers, and the manufacturer recommended 6-8, IIRC.

              1. 1

                Oh yeah, pin arrays are dark magic as far as I’m concerned.

        2. 1

          Yes, this means you could theoretically make an analog keyboard which reports the level of key depression

          How about practically? :D Of course they made it to emulate a gamepad instead of having analog reports for all the keys..

          1. 1

            I’ve used plenty of keyboards which implement NKRO by presenting as multiple USB keyboards to the OS, and reporting up to 6 key states per virtual “keyboard”

            Usually it’s not actually NKRO and more like 18 or 24KRO but you know, I’ve only got 10 fingers so works for me

            1. 1

              Great article, clarified a few things for me. Fun fact, the FreeBSD USB stack is limited to 6 key presses, even on keyboards that are NKRO. I think it’s defined here. There’s apparently a new HID stack in the works that might fix that since it sets the limit to 256.

              1. 2

                Looks like they just support the boot protocol and then emulate an AT keyboard. Kind of shocking that they don’t have a HID stack yet but I’m glad one is in the works.

                1. 1

                  just support the boot protocol

                  No, the boot protocol descriptor is only used for keyboards with a particular quirk.

                  emulate an AT keyboard

                  Nah, uhid directly manages evdev and legacy keyboard interfaces. The latter uses AT scancodes, which is why the ukbd_trtab table is there. That’s not really emulation at the driver level.

                  Kind of shocking that they don’t have a HID stack yet

                  Well, tightly coupled “this whole USB device is a USB keyboard/mouse/touchscreen” drivers worked well enough for most use cases :) The reason a proper “HID bus” abstraction was created was the rise of HID-over-I2C.

                  1. 1

                    Ahh cool. I was trying to follow the code on my phone which is a bit challenging.

                    As we get more devices that are interesting that look less and less like a PC this stuff gets complicated.