feat(emulator): add runtime X3 and X4 selection

Model the ESP32-C3 I2C fingerprint path for stock firmware detection. Add a blank X4 panel stub, live GPIO inputs, and pin the exact QEMU revision.
This commit is contained in:
2026-07-19 20:51:44 -04:00
parent f972fbe78b
commit ca07c02e02
7 changed files with 508 additions and 137 deletions
+31 -26
View File
@@ -19,28 +19,31 @@ running in-browser from static files. The endgame merges those two.
## Status
**Phase 1 (native peripheral models) is done for the X3.** The unmodified
canonical firmware (`crosspoint-reader/crosspoint-reader`, X3-selected) boots to
the home screen, mounts a FAT32 SD image, lists its contents, and responds to
button presses — all under native espressif-qemu with our patch. Reproduce:
**Phase 1 (native foundation) is done for X3 and X4 selection.** Unmodified
[canonical firmware](https://github.com/crosspoint-reader/crosspoint-reader)
auto-detects `xteink,variant=x3` through its real I²C fingerprint and boots the
X3 home screen with SD support. `variant=x4` omits those chips, so the same
firmware selects its SSD1677 driver and sends its command stream to a blank X4
panel stub. Reproduce:
```sh
make qemu # clone espressif/qemu @ pinned tag, apply patch, build
make qemu # clone espressif/qemu @ pinned commit, patch, build
make firmware sdimage # 16 MB flash.bin + FAT32 sd.img from a pio build
make run # boot the xteink-x3 machine (add `-display none` for headless)
make run # X3 (default)
make run VARIANT=x4 # X4 detection + blank panel stub
```
Screenshots (via QMP `screendump`) confirmed: boot → home → Browse Files.
QMP screenshots confirmed the X3 home screen and 528×792 orientation; captured
X4 SPI traffic confirmed the firmware selected its SSD1677 driver.
### Device selection (X3 vs X4)
X3 and X4 share one ESP32-C3 binary and pick a profile at runtime via an I²C
fingerprint (BQ27220 gauge + DS3231 RTC + QMI8658 IMU on SDA20/SCL0), with an
NVS override (`cphw/dev_ovr`: 1=X4, 2=X3). **The emulator picks X3 vs X4 at
launch** by starting the matching machine (`xteink-x3`, later `xteink-x4`); the
X3 machine will expose the X3 fingerprint so stock dual firmware auto-detects.
No recompiled/custom firmware — the current test build forces X3 only because
the fingerprint I²C devices aren't modelled yet.
launch** with one machine property: `-machine xteink,variant=x3|x4`. X3 attaches
minimal BQ27220/DS3231/QMI8658 targets; X4 leaves the I²C bus empty. This drives
both firmware detection and panel wiring without modifying the firmware.
## The interfaces (native now → browser later)
@@ -49,38 +52,40 @@ reuses unchanged — no bespoke abstraction layer:
| Peripheral | Native seam | Browser binding (phase 3) |
|------------|-------------|---------------------------|
| e-ink panel | `xteink-x3-eink` SSI device → QEMU graphical console (528×792, physical orientation) | `<canvas>` from the same display surface |
| e-ink panel | X3 UC8253 (528×792) or blank X4 SSD1677 stub (480×800) → QEMU graphical console | `<canvas>` from the same display surface |
| SD card | `ssi-sd` + `sd-card-spi` backed by QEMU block layer (`-drive if=sd`) | browser-supplied blockdev (File/OPFS) |
| Buttons + battery | `esp32c3.adc` QOM props `adci[1]`/`adci[2]`; power button = GPIO3 input | DOM buttons → `qom-set` equivalent |
| Buttons + battery | ADC QOM props `adci[1]`/`adci[2]`; GPIO QOM `input-level[3]` for Power | DOM buttons → `qom-set` equivalent |
| Firmware image | flash via `-drive if=mtd` | uploaded `.bin` written to emulated flash |
## Phase 1 device models (in `qemu/patches/0001-xteink-x3-machine.patch`)
## Phase 1 device models (in `qemu/patches/0001-xteink-machine.patch`)
- **`esp32c3.adc`** — SAR ADC: oneshot completes immediately; per-channel value
is settable live via QOM (`adci[*]`). Feeds battery and the two resistor-ladder
button groups. Replaced the original always-done register shim.
- **`esp32_gpio`** — real OUT/ENABLE/IN registers with 32 named input + output
lines. Outputs read high while a pin is input-configured (matches the board's
pull-ups; without this, display CS floated low during SD probing).
lines and live `input-level[*]` QOM controls. Outputs read high while a pin is
input-configured (matches the board's pull-ups).
- **`ssi.esp32c3.spi2`** — the general-purpose SPI2 controller (CPU-buffer
transactions on an SSI bus). DMA path deliberately unimplemented until needed.
- **`xteink-x3-eink`** — UC8253 panel: decodes DTM1/DTM2 planes (52 272 B each),
drives BUSY, renders to a QEMU console. Protocol from `chip/eink-x3.chip.c`.
- **`ssi-sd` fix** — track card idle state so CMD58 reports ready after ACMD41
(the 9.2 fork hardcoded idle, which SdFat rejects).
- **`xteink-x3` machine** — subclass of `esp32c3` wiring panel CS21/DC4/RST5/
BUSY6 and SD CS12 onto SPI2, and the SD card onto QEMU's block layer.
- **`esp32.i2c` + fingerprint targets** — C3 opcode support and minimal
BQ27220/DS3231/QMI8658 register responses for stock X3 detection.
- **`xteink` machine** — `variant=x3|x4` controls fingerprint presence and panel
type; shared panel/SD wiring stays in one machine.
- **X4 panel stub** — blank 480×800 console with X4 idle-low BUSY; accepts the
SSD1677 command stream without implementing it.
## Remaining phases
1. **X4 machine** — SSD1677 panel + X4 battery/ADC profile as a sibling machine.
The X3 command trace (`0x18/0x0c/0x44/0x45…`) is already captured.
2. **Compile the patched fork to WASM** — merge Espressif's SoC models with
1. **Compile the patched fork to WASM** — combine Espressif's SoC models with
qemu-wasm's WASM TCG/TCI backend; build `qemu-system-riscv32` with Emscripten.
Output: static `.wasm` + `.js` + packaged BIOS/ROM.
3. **Browser front-end (static)** — upload `.bin` → emulated flash; `<canvas>`
from the display surface; DOM buttons → ADC/GPIO injection; preloaded SD
image; launch-time X3/X4 selection. Fully airgapped.
2. **Browser front-end (static)** — uploaded flash, canvas, physical buttons,
SD block backend, and pre-boot X3/X4 picker. Fully airgapped.
3. **Full X4 panel** — implement SSD1677 RAM/window/update commands when visible
X4 rendering is needed; the selection, dimensions, wiring, and stub exist.
## Repo layout
@@ -89,7 +94,7 @@ reuses unchanged — no bespoke abstraction layer:
| `flake.nix` | espressif-qemu release pkg (`nix run .#run-upstream`) + full QEMU build devShell |
| `Makefile` | `qemu` (source build), `firmware`, `sdimage`, `run`, `chip` |
| `scripts/build-qemu.sh` | clone pinned espressif/qemu, apply patch, build riscv32-softmmu |
| `qemu/patches/` | the xteink X3 machine + device models (applied onto the pinned tag) |
| `qemu/patches/` | xteink machine + device models (applied onto the pinned QEMU commit) |
| `chip/eink-x3.chip.c` | legacy Wokwi model — the reverse-engineered X3 e-ink protocol reference |
| `web/` | (phase 3) static front-end: upload + canvas + buttons |