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 divine, 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 bytesA B C D
to address3A8
; the6
inA6
is the leading3
of the address shifted over one.[0xA6 0xA8][0xA7 r:4]
reads four bytes from3A8
. TheA7
isA6
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 is0x00 0x00 0xE0 0xE0
.[0xA6 0xB8][0xA7 r:4]
reveals that capslock is0x00 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 |
|
F2 |
|
F3 |
|
F4 |
|
F5 |
|
F6 |
|
F7 |
|
F8 |
|
F9 |
|
F10 |
|
F11 |
|
F12 |
|
UNIX-like Left Special Keys¶
Undo |
Undo (both fn) |
Cut |
Cut (both fn) |
Copy |
Copy (both fn) |
Paste |
Paste (both fn) |
Select All |
Select (both fn) |
Swapping Home/End/PageUp/PageDown¶
Pos |
Original | Now |
|
220 |
|
|
224 |
|
|
240 |
|
|
244 |
|
Other Remappings¶
Power |
Pause / Print Screen (with fn) |
Eject |
Insert (both fn) |
Web Next |
Escape (both fn) |
Web Back |
Alt+Leftarrow / Stop (with fn)
|