Wednesday, September 3, 2014

Reverse Engineering the Azio L70 Keyboard Protocol

To recap, I bought an Azio L70 Gaming Keyboard and discovered it did not work with Linux. I set out write a kernel driver for it; starting by capturing the usb packets with usbmon and WireShark.

When I finally figured out where the actual key-codes were in the packets it was not very hard to figure out the pattern. I had both a standard usb keyboard (that functioned in Linux) and my new L70 both attached to my computer. I could strike keys on either and watch the different patterns.

The Left Over Data portion of the usb packets were 16 hex digits long (8 bytes). The regluar keyboard sent packets like so:

a -> 00 00 00 00 00 00 00 01
b -> 00 00 00 00 00 00 00 02
c -> 00 00 00 00 00 00 00 03
d -> 00 00 00 00 00 00 00 04

The Azio was sending packets like this:

a -> 04 00 01 00 00 00 00 00
b -> 04 00 02 00 00 00 00 00
c -> 04 00 04 00 00 00 00 00
d -> 04 00 08 00 00 00 00 00

Once the pattern became obvious, so did the reason why the keyboard requires a device-specific driver. In fact, there was a hint right on the packaging. The L70 is billed as a gaming keyboard with “n-key rollover”. Not only that, but this rollover functioned over USB.

When I bought the keyboard and started on this little endeavor I did not even know what rollover was, let alone n-key rollover. For those not in the know, I will attempt to explain.

First a little history lesson…

At one time, PCs used a port called PS/2 for keyboard and mouse input. The PS/2 port used a BIOS-based interrupt mechanism to report key-presses to the system. When at key was pressed the keyboard interrupted the computer with the key code. If you smashed down 10 keys, the keyboard, insomuch as it had internal buffer to maintain the information, would dutifully report each key as unique sequential interrupt. Therefore, all keyboards had n-key rollover, or the ability to press N keys at once and have the OS receive them correctly.

Then came USB keyboards and mice. USB is still a serial protocol but unlike PS/2 it is a polling-based protocol. Rather than being interrupted by the device, the OS is responsible for constantly checking the bus to see if there is any information on it. This causes problems for rollover, aka multiple key-presses, on USB keyboards. What happens is that the key presses can pile up, and in fact, essentially change. At least that is my understanding. For example, take the keyboard input above. Key A sends a key code of 1. B and C send 2 and 3 respectively. Therefore, pressing A and B simultaneously might cause the system to see a 3 (or C) rather than the separate key presses.

The only way to work around this limitation is to write a different protocol. One where every key-press is unique and no combination of keys will produce another key’s code. This is exactly what Azio did. The 1,2,4,8,… pattern seen above is obvious to anyone familiar with bit masking.

Other companies take another approach. They include a PS/2 converter and advertise their product as having, most typically, 6 key rollover on USB and n-key rollover on PS/2 (with the adapter). This is the route that both DAS and Code took. Consequently they work with Linux and the standard USB keyboard driver.

One thing that still bugs me is that I never determined how it was that the keyboard functioned as much as it did. Why, when it was giving so different of input, did the vast majority of keys function? Remember, it was only the Ctrl, Alt and Meta (Windows) keys that did not operate.

Once I had the protocol reverse engineered it was then a matter of writing a Linux USB device driver. I will cover that in a later post.

No comments:

Post a Comment