first commit
This commit is contained in:
BIN
doc/base.png
Normal file
BIN
doc/base.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
BIN
doc/fn.png
Normal file
BIN
doc/fn.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 89 KiB |
91
doc/protocol.md
Normal file
91
doc/protocol.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# HHKB Professional vendor HID protocol
|
||||
|
||||
Reverse-engineered notes for the configuration channel used by PFU's Keymap
|
||||
Tool, as implemented in `internal/hhkb`. Verified against the HHKB Professional
|
||||
Classic (`04fe:0020`); the Hybrid (`0021`/`0022`) is the same.
|
||||
|
||||
## Transport
|
||||
|
||||
The keyboard exposes three USB-HID interfaces. Interface 2 is vendor-defined and
|
||||
carries the config protocol. Its report descriptor begins:
|
||||
|
||||
```
|
||||
06 00 ff Usage Page (Vendor-Defined 0xFF00)
|
||||
09 01 Usage 1
|
||||
a1 01 Collection (Application)
|
||||
09 02 ... 75 08 95 40 81 02 Input report, 64 bytes
|
||||
09 03 ... 75 08 95 40 91 02 Output report, 64 bytes
|
||||
c0
|
||||
```
|
||||
|
||||
We find it by scanning `/sys/class/hidraw/*`: the node whose `device/uevent`
|
||||
contains HID id `…04FE:00000020` and whose `device/report_descriptor` starts
|
||||
with `06 00 ff`.
|
||||
|
||||
Reports are 64 bytes with no report id. A `write(2)` to the hidraw node takes a
|
||||
leading `0x00` report-number byte (65 bytes total); the kernel strips it.
|
||||
|
||||
## Framing
|
||||
|
||||
Request (64 bytes):
|
||||
|
||||
```
|
||||
byte 0 0xAA
|
||||
byte 1 0xAA
|
||||
byte 2 command
|
||||
byte 3+ arguments
|
||||
```
|
||||
|
||||
Reply (64 bytes):
|
||||
|
||||
```
|
||||
byte 0 0x55 acknowledgement
|
||||
byte 1 0x55
|
||||
byte 2+ status / payload (data fields start at byte 6)
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
| id | name | request args | reply payload (from byte 6) |
|
||||
|-----|-----------------|-----------------------------|------------------------------------|
|
||||
| 1 | notify app | `00 01 <closed>` | — |
|
||||
| 2 | get info | — | type[20] rev[4] serial[16] fw… |
|
||||
| 3 | factory reset | — | header `55 55 03 00` on success |
|
||||
| 4 | confirm keymap | — | — |
|
||||
| 5 | get DIP | — | 6 bytes, one per switch |
|
||||
| 6 | get mode | — | 1 byte: 0 HHK, 1 Mac, 2 Lite, 3 Secret |
|
||||
| 7 | reset DIP | `00 01` | — |
|
||||
| 134 | write keymap | see below | — |
|
||||
| 135 | get keymap | `00 02 <mode> <fn>` | streamed, see below |
|
||||
|
||||
Firmware commands (208, 224–231) exist but are deliberately unimplemented here.
|
||||
|
||||
## Keymap layout
|
||||
|
||||
A layer is 128 bytes: `layer[keyNumber] = scancode`. Key numbers and the US
|
||||
(ANSI) geometry live in `keymap.go` (`ANSI`). `<fn>` selects base (0) or Fn (1).
|
||||
|
||||
### Read (command 135)
|
||||
|
||||
One request, then three input reports tiling the layer (data at byte 6 of each):
|
||||
|
||||
```
|
||||
report 1 -> layer[0:58]
|
||||
report 2 -> layer[58:116]
|
||||
report 3 -> layer[116:128]
|
||||
```
|
||||
|
||||
### Write (command 134)
|
||||
|
||||
Three requests. The pair after the command id marks offset/length; the first
|
||||
pass also carries mode and fn before the key bytes:
|
||||
|
||||
```
|
||||
AA AA 86 41 3B <mode> <fn> layer[0:57]
|
||||
AA AA 86 82 3B layer[57:116]
|
||||
AA AA 86 C3 0C layer[116:128]
|
||||
```
|
||||
|
||||
A full remap is: notify-app(open) → get mode → read layer → set the byte →
|
||||
write (3 passes) → confirm (4) → reset DIP (7) → notify-app(closed). Reversible
|
||||
via factory reset (3).
|
||||
Reference in New Issue
Block a user