Skip to content

Architecture

Syax89 edited this page Jul 8, 2026 · 1 revision

Architecture

Why a custom driver

The mainline kernel has an upstream spi-hid driver (linux-surface/spi-hid, adapted from Surface Duo 2 Android sources) implementing the public HID over SPI Protocol Specification v1.0. This device does not speak that protocol.

Investigation found the touchscreen actually implements "HidSpiDeviceV0" — an internal, pre-release Microsoft protocol (SPI_HID_SUPPORTED_VERSION 0x0100, not 0x0300/v1.0) used on AMD/x86 Surface devices, confirmed independently by the linux-surface maintainer on the upstream mailing list. The wire framing looks superficially similar to v1.0 but enough details differ (see Wire Protocol) that the generic driver can't talk to it. This repo's driver/spi-hid-core.c keeps the generic v1.0 hid_ll_driver scaffolding (for hid_add_device() integration) but drives the actual wire protocol with a separate, custom state machine built specifically for this device — the "SEQ sequencer".

Two-layer design

┌──────────────────────────────────────┐
│  spi-hid.ko (driver/spi-hid-core.c)  │
│  HID-over-SPI protocol state machine │
│  · spi_hid_seq_thread(): states 0-5  │
│  · Standard HID mode (Report 0x40)   │
│  · Optional raw heatmap interception │
└──────────────┬───────────────────────┘
               │ Linux SPI framework (spi_sync/spi_async)
┌──────────────┴───────────────────────┐
│  spi-amd.ko (driver/spi-amd.c)       │
│  AMD FCH SPI controller driver (V2)  │
│  · TX/RX FIFO management (70B FIFO)  │
│  · Chunked reads for bulk transfers  │
│  · PIO remainder path                │
└──────────────┬───────────────────────┘
               │ MMIO
┌──────────────┴───────────────────────┐
│  AMD FCH SPI Controller @ 0xFEC10000 │
└──────────────────────────────────────┘
  • spi-amd.c is a normal Linux spi_master controller driver. It doesn't know anything about HID — it just executes SPI transfers (spi_transfer/spi_message) the same way any SPI controller driver would, using the AMD FCH's register-triggered TX/RX FIFO (see Wire Protocol for the register-level detail, including the 64-byte "page" quirk that matters for the report descriptor).
  • spi-hid-core.c is where the actual protocol logic lives. It registers as a normal SPI device driver (spi_driver) against spi-amd's controller, and on top of that runs its own IRQ-triggered state machine that speaks the device's specific byte protocol.

The IRQ-driven sequencer

The touchscreen signals the host via a GPIO interrupt line (ACPI pin 0x55 / 85) every time it has something to say — a reset notification, a response to a request, or (once streaming) a new input report. spi_hid_seq_thread() is the threaded IRQ handler that reacts to every such edge. It's a small explicit state machine (shid->seq_state, states 05):

State Name What happens here
0 WAIT_RESET Drain the device's RESET_RSP, send DESCREQ
1 WAIT_DESC Wait for DEVICE_DESC (28 bytes: VID/PID/register map), send a second descriptor request
2 WAIT_RPT Read the 936-byte HID report descriptor, then either enter standard mode or (if raw_mode=1) start the GET_FEATURE/SET_FEATURE exchange
5 WAIT_FEAT_RESP (raw_mode=1 only) waiting for GET_FEAT_RESP, then send SET_FEATURE
3 VENDOR_INIT A fallback path if the descriptor exchange doesn't go as expected
4 DONE Steady state — every further IRQ delivers a real input report, forwarded via hid_input_report()

A device-initiated reset (a RESET_RSP arriving while already in state 4) is handled by resetting back to state 1 and restarting the descriptor exchange — this is the same mechanism the multi-touch handshake watchdog piggybacks on when SET_FEATURE needs to be retried.

Two auxiliary async mechanisms exist alongside the IRQ path:

  • descreq_work — a delayed work item that polls for DEVICE_DESC even if no IRQ arrives, as a fallback.
  • create_device_work — a work item that calls hid_add_device() once a usable report descriptor is available, decoupling HID-core registration from the IRQ context.

See Report Descriptor and Standard Touch Mode for what happens once the device reaches steady state.

Clone this wiki locally