Files
hkkb-layout-web/doc/protocol.md
2026-05-27 18:30:14 +09:00

92 lines
3.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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, 224231) 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).