Show Source

I recently acquired a Kinesis Freestyle 2 for Mac and really enjoy how it feels, but the layout is not quite to my taste. Having found http://alvarop.com/2013/08/kinesis-freestyle-2-keyboard-mod-to-fix-media-keys and https://github.com/rbasoalto/kinesis-freestyle-fw-hack I decided to follow along and am documenting my effort for the world to see.

Theory of Operation

At its core, this keyboard is little more than an wrapper around the Alcor Semiconductor AU9410 chip, which is probably how these things should be. The reference manual for that chip is, unfortunately, about as clear as mud, but so it goes.

Being a USB keyboard, the chip leans heavily on the USB HID standard, which may be found at http://www.usb.org/developers/hidpage/Hut1_12v2.pdf . This page will make heavy reference to said document as well, naturally enough.

In any case, there’s an I2C EEPROM off the board that is all that we really care about. This appears to hold a bunch of scary USB descriptors for the host (confusingly, the manual talks about putting hub descriptors here), some configuration of the chip itself, and, most importantly, an array of 4-byte values that correspond to the 19x8 keyboard matrix, at 0x1A0 bytes into this memory. (Which makes sense, as it’s a 0x400 byte memory and our array requires 0x260 bytes and 0x1A0 + 0x260 = 0x400.)

The keyboard has essentially two mappings for its keys: those when the FN switch is active and those when it’s not. The Kinesis boards use the AU9410’s ability to have the FN button toggle the mode rather than requre the FN button be pressed down. Moreover, when FN is active, the chip pulls LED2 on. These behaviors are documented in the manual (sort of):

  • Bit 4 of byte 1 in the EEPROM is 1 for FN toggling and 0 otherwise.
  • Bit 5 of byte 1 is 1 for FN mode to sink LED2 and 0 otherwise.

As far as I have been able to devine, there are three fields: a flag and page byte, a “fn-active” value (two bytes, from the USB HID page chosen by the page byte), and a “fn-inactive” value (1 byte, always from the HID keyboard codepage?).

  • The first byte (at lowest memory address) is a kind of “table selector” for when the FN mode is active. I have observed the following values:

    0x00 Use HID Keyboard page (0x07)
    0x01 Never observed?
    0x02 Use HID Generic Desktop page (0x01) (Alcor manual only)
    0x03 Use HID Consumer page (0x0C)
    0x04 Unclear (Alcor manual only)

    Bit 0x40 indicates a role reversal of the two fields: that is, the fn-inactive value gets sent when fn is active, and the fn-active value is sent when fn is inactive.

    None of the bits in 0xB8 have been observed in the manual or a dump.

  • The next two bytes determine the behavior for the FN-active mode (unless the reversal bit is set above).

    If the first byte was 0x00, then the MSB of this pair is the USB usage value from the HID keyboard page. The LSB appears to be a set of flags, which can apparently be OR’d together (0x0C appears in some dumps):

    0x08 Act as if Left GUI (usage 0xE3) was held during stroke
    0x04 Act as if Left Alt (usage 0xE2) was held during stroke
    0x02 Never seen in dumps or manual Conjectured to be Left Shift (usage 0xE1) during stroke
    0x01 Act as of Left Control (usage 0xE0) was held during stroke

    The other four bits have never been observed in the manual or a dump. The Mac variant uses 0x08 extensively for its special keys.

    For other pages, these represent the entry in the USB HID page chosen in little-endian form. (That is, the LSB comes first in the firmware.)

  • The last byte (at the highest memory address) appears to be the HID usage value from the HID Keyboard page (0x07) that the chip sends when FN mode is inactive. 0x00 corresponds to “no action” and the chip could not generate anything above 0xFF, but those are reserved anyway.

Note

Because Kinesis uses the FN-toggle functionality, almost all of the keyboard’s “normal” keys are redundantly programmed, in that they look like (LSB) 00 00 VV VV (MSB) so that they function identically in FN-active and FN-inactive modes.

Integrated USB Hub

There’s a USB hub in the right half unit, too, based on a 4-port chip with only three of them actually used. Surely we can have fun with that! Since I2C is multi-master, we could maybe even stick an I2C to USB interface in there and use it to emulate the onboard EEPROM and act as if we can reprogram the keyboard live!

Firmware Layout

Based on my “for Mac” edition. It may be helpful to have http://kinesis-ergo.com/wp-content/uploads/2013/06/freestyle2-mac-layout-800x307.jpg open while looking at this table.

  8 (00) 7 (04) 6 (08) 5 (0C) 4 (10) 3 (14) 2 (18) 1 (1C)
19 (1A0) null null null null null null null null
18 (1C0) null null null null ?r alt? ?r alt? null null
17 (1E0) ?eject? ?r shift? tab back tab fwd web back web fwd left alt ?5 %?
16 (200) ?F1? ?Y? copy sel all undo l shift r shift ?4 $?
15 (220) end home left arr null up arr ?HUT 58? ?HUT 67? ?HUT 57?
14 (240) pg down pg up ?HUT 56? ?HUT 55? ?HUT 63? ?HUT 5B? ?HUT 5E? ?HUT 61?
13 (260) null off/slp rght arr ?HUT 54? ?HUT 62? ?HUT 5A? ?HUT 5D? ?r shift?
12 (280) eject delete down arr ?HUT 53? left spc ?HUT 59? ?HUT 5C? ?r ctrl?
11 (2A0) F10 F9 F12 return F11 \ | backspace ?r alt?
10 (2C0) null ?V? rght gui ?G? left gui ?r ctrl? cut 6
9 (2E0) 0 ) - _ / ? ?HUT 32? ‘ “ ; : [ { P
8 (300) 9 ( F8 null . > null L null O
7 (320) 8 * ?= +? ?HUT 87? , < null K ] } I
6 (340) ?7? null N M H J Y U
5 (360) 4 $ 5 % B V G F T R
4 (380) 3 # F2 r space C F4 D F3 E
3 (3A0) 2 @ F1 F7 X ?HUT 64? S caps lock W
2 (3C0) 1 ! ` ~ F6 Z escape A tab Q
1 (3E0) F5 l ctrl paste r alt null ?delete? ?6? ?6?

“null” in the above table means that the bytes are 00 00 00 00 in my firmware dump, suggesting that there is no key at that position in the matrix in any product variant.

Note that this table is full of insanity! There’s no clear “keyboard cut in half” feel to it, suggesting that the inter-half linking cable is carrying many, many more wires than it would have to. WTF.

Additionally crazy are that some keycodes are duplicated. They have been marked above as ?x?; it is unclear what these are doing in the design. Perhaps the Kinesis keymaps are based on other products or are interchangable among several? The use of ”?HUT xx?” indicates a key that’s really unusual; xx are the hex value from HUT keyboard page (0x07). Note that HUT 53 – HUT 63 are keypad scancodes; the tight packing of these scancodes into the rectangle bounded by 230 – 29C suggests either a version with a keypad or that the same firmware is used in the Kinesis standalone keypad product.

Note that the mac version of this product lacks a “r ctrl” key; it seems plausible that 2D4 is used on the PC version even if it is not on the mac? The positions for HUT 32 and HUT 87 are likely used on the internationalized variant.

Programming Header

The connector on the keyboard internal board is layed out as follows, when viewed with the keyboard opened but the board still in place. The silk screen with pin numbers is on the other side of the board, annoyingly enough.

GROUND (5) SDA (4) SCL (3)
Protect (2) KEY 3.3V (1)

Protect is active-high and pulled high by default. Pull low to enable writing to the chip.

Example Interaction with Bus Pirate

http://ww1.microchip.com/downloads/en/devicedoc/21081G.pdf documents the EEPROM’s I2C protocol pretty well.

  • [0xA0 0x00][0xa1 r:1024] reads entire firmware
  • [0xA6 0xA8 A B C D] writes the bytes A B C D to address 3A8; the 6 in A6 is the leading 3 of the address shifted over one.
  • [0xA6 0xA8][0xA7 r:4] reads four bytes from 3A8. The A7 is A6 with the bottom bit turned on to indicate that we are doing a sequential read. Don’t get the banks wrong between the two halves or the chip will bark at you and hang the buspirate’s I2C interface.
  • @ will read the AUX pin, which can be easily connected to write protect. a will set it low to enable writing to the chip; A will protect it again.

Switching Left Control and Caps Lock

  • [0xA6 0xE4][0xA7 r:4] reveals that left control is 0x00 0x00 0xE0 0xE0.
  • [0xA6 0xB8][0xA7 r:4] reveals that capslock is 0x00 0x00 0x39 0x39.
  • [0xA6 0xB8 0x00 0x00 0xE0 0xE0] sets capslock to actually scan as left control.
  • [0xA6 0xE4 0x00 0x00 0xE0 0x39] sets left control to actually scan as capslock when Fn is pressed and as control otherwise, since I happen to not like caps lock all that much.

Swapping Delete and Backslash

Similar to the above: [0xA4 0xB4 0x00 0x00 0x2A 0x2A][0xA4 0xB8 0x00 0x00 0x31 0x31] I’ve gone back and forth on this and am currently not so swapping these keys. Given the shape of the keyboard and how I type, it’s easier for me to hit the bigger delete key.

Making F keys non-Fn

While here, I kicked the keyboard over to the UNIX special keys for some of them.

F1 [0xA6 0xA4 0x00 0x00 0x3A 0x69] (F1 / F14)
F2 [0xA6 0x84 0x00 0x00 0x3B 0x6A] (F2 / F15)
F3 [0xA6 0x98 0x00 0x00 0x3C 0x74] (F3 / UNIX “Execute”; was 00 01 52 3C)
F4 [0xA6 0x90 0x00 0x00 0x3D 0x75] (F4 / UNIX “Help”; was 00 00 45 3D)
F5 [0xA6 0xE0 0x00 0x00 0x3E 0x7E] (F5 / UNIX “Find”; was 03 B6 00 3E)
F6 [0xA6 0xC8 0x00 0x00 0x3F 0x79] (F6 / UNIX “Again”; was 03 CD 00 3F)
F7 [0xA6 0xA8 0x00 0x00 0x40 0x76] (F7 / UNIX “Menu”; was 03 B5 00 40)
F8 [0xA6 0x04 0x40 0x00 0x7F 0x41] (swapped and UNIX Mute’d; was 03 E2 00 41)
F9 [0xA4 0xA4 0x40 0x00 0x81 0x42] (swapped and UNIX Vol Down’d; was 03 EA 00 42)
F10 [0xA4 0xA0 0x40 0x00 0x80 0x43] (swapped and UNIX Vol Up’d; was 03 E9 00 43)
F11 [0xA4 0xB0 0x40 0x01 0x51 0x44] (swapped; was 00 01 51 44)
F12 [0xA4 0xA8 0x40 0x0C 0x07 0x45] (swapped; was 00 0C 07 45)

UNIX-like Left Special Keys

Undo Undo (both fn) [0xA4 0x10 0x00 0x00 0x7A 0x7A]
Cut Cut (both fn) [0xA4 0xD8 0x00 0x00 0x7B 0x7B]
Copy Copy (both fn) [0xA4 0x08 0x00 0x00 0x7C 0x7C]
Paste Paste (both fn) [0xA6 0xE8 0x00 0x00 0x7D 0x7D]
Select All Select (both fn) [0xA4 0x0C 0x00 0x00 0x77 0x77]

Swapping Home/End/PageUp/PageDown

Pos Original | Now
220 00 00 4D 4D (end) | 00 00 4E 4E (pgdn)
224 00 00 4A 4A (home) | 00 00 4B 4B (pgup)
240 00 00 4E 4E (pgdn) | 00 00 4D 4D (end)
244 00 00 4B 4B (pgup) | 00 00 4A 4A (home)

Other Remappings

Power Pause / Print Screen (with fn) [0xA4 0x64 0x00 0x00 0x48 0x46]
Eject Insert (both fn) [0xA4 0x80 0x00 0x00 0x49 0x49]
Web Next Escape (both fn) [0xA2 0xF4 0x00 0x00 0x29 0x29]
Web Back Alt+Leftarrow / Stop (with fn) [0xA2 0xF0 0x00 0x04 0x50 0x78]