diff --git a/README.md b/README.md index d49a698..860ff08 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,90 @@ # ku1255cfw Custom open firmware for the Lenovo KU-1255 compact USB keyboard +## Overview + +The Lenovo ThinkPad Compact USB Keyboard with TrackPoint (KU-1255) uses a +Sonix SN8F2288FG 8-bit MCU with 12K words (24KB) flash and 512 bytes RAM. +The TrackPoint is connected via bit-banged I2C (P2.4 SCL, P2.5 SDA, address +0x2A, Synaptics proprietary protocol). + +This firmware is a from-scratch rewrite based on the original by +[ranma](https://github.com/ranma/ku1255cfw), adding standalone middle-button +scroll and other missing features so the keyboard works fully without an +external converter. + +## Features added over base firmware + +### Middle-button scroll +3-state state machine (IDLE / UNDECIDED / SCROLLING) with 150ms timeout: +- **Short press** (<150ms): sends a normal middle click (deferred on release) +- **Hold + TrackPoint movement**: converts XY deltas to scroll wheel events +- **FN + middle button**: passes middle button through directly (no scroll logic) + +### CapsLock LED feedback +Host LED output reports (SET_REPORT) are parsed and CapsLock state (bit 1) drives +the power LED on P5.3/PWM0 (active-low). The flasher magic byte sequence detection +is preserved. + +### FN+F7 through FN+F12 +| Key | Normal | With FN held | +|-----|--------|-------------| +| F7 | F7 | LGUI+P (display settings) | +| F8 | F8 | F8 (passthrough) | +| F9 | F9 | LGUI+I (settings) | +| F10 | F10 | LGUI (search) | +| F11 | F11 | LCTRL+LALT+TAB (task switch) | +| F12 | F12 | F12 (passthrough) | + +### USB compliance fixes +- **HID GET_REPORT**: returns proper per-interface empty reports (8B keyboard / 5B mouse) + instead of stale buffer contents. Some OSes query this on resume from suspend. +- **SET/CLEAR FEATURE**: tracks DEVICE_REMOTE_WAKEUP state instead of just ACKing. + +## Flash space + +| Firmware | Words used | Free | Utilization | +|----------|-----------|------|-------------| +| OEM | 10,226 / 10,239 | 13 | 99.9% | +| This | 10,238 / 10,239 | 1 | 100.0% | + +Key debouncing is not implemented (and not needed) — the 8ms scan cycle naturally +debounces scissor switches (<1ms bounce time). The OEM firmware also does not debounce. + +## Flashing + +Requires [vpelletier/dissn8](https://github.com/vpelletier/dissn8) tools. + +1. Build: `asn8 main.s -o ku1255cfw.bin` +2. Enter bootloader: hold **Return** while plugging in the keyboard +3. Flash: `flashsn8 ku1255cfw.bin` + +## Simulator testing + +`test_scroll.py` runs 23 automated tests against the +[dissn8 simulator](https://github.com/vpelletier/dissn8) via the `ku1255_sim.py` +harness. Tests cover all scroll state transitions, deferred click timing, FN modifier +interaction, and edge cases (rapid clicks, timeout behavior). + +**Note:** The simulator requires a fix for PnUR register read handlers (see below). + +## Simulator fixes (for vpelletier/dissn8) + +Running this firmware in the dissn8 simulator exposed three bugs: + +1. **PnUR read handlers crash** — `_volatile_dict` maps P0UR-P5UR read handlers to + `None`. B0BSET/B0BCLR (read-modify-write) on these registers causes + `TypeError: 'NoneType' object is not callable`. Fixed by adding `readPullUp()` + returning the latch value. Branch: `pnur_readpullup` (PR pending upstream). + +2. **Hardcoded HID descriptor sizes** — `ku1255_sim.py` assumed 0x51/0xD3 byte + HID report descriptors (OEM sizes). This firmware uses 91/61 bytes. Fixed by + parsing sizes dynamically from the config descriptor. + +3. **Wrong HID descriptor recipient** — HID report descriptor requests must use + interface recipient (0x81), not device (0x80), per USB spec. The OEM firmware + happened to accept both. + ## Dev setup - [OpenViszla](https://github.com/openvizsla/ov_ftdi) USB protocol analyzer - 5V-tolerant PL2303 UART interface (e.g. https://www.adafruit.com/product/954)