=============================== Wacom EMR pen digitizer driver =============================== Register/protocol-level reverse engineering of the active-pen kernel driver — split out from the general input-peripherals sweep because the pen is tightly coupled to the display stack (it shares the panel surface and the same "recovered by static disassembly" method as the e-ink driver). Built into ``artifacts/kernel`` and bound by the DTB overlay (:doc:`/boot/dtbo`). The e-ink display path itself is in :doc:`tcon`; the front-light (``lm3630a``) and its class devices are in :doc:`stack`. Capacitive touch (the separate finger-layer digitizer) is in :doc:`/sensors/touchscreen`. Method: the driver ``compatible``/GPIO bindings are read from ``artifacts/dtbo_a/overlay_0.dts``; the protocol details are from static disassembly of the kernel (function starts located by ``__func__`` string cross-reference, then read with ``capstone`` — the same toolkit as :doc:`tcon`, scripts in ``artifacts/re_static/``). No code was executed. DT binding ========== .. list-table:: :header-rows: 1 :widths: 22 16 12 50 * - Device - ``compatible`` - Bus - GPIOs / notes * - **Wacom pen** (``wacom@09``) - ``onyx,wacom_i2c`` - I²C ``0x09`` - irq **gpio52**, rst **gpio54**, vdd-ctrl **gpio10**, vcc-ctrl **gpio49**; ``onyx,report-rate-high`` The active pen is an electromagnetic-resonance (EMR) digitizer built on a **Wacom W9021** MPU, reached over I²C at address ``0x09``. The driver names the chip family explicitly (``wacom_flash_w9021`` / ``wacom_write_block_w9021`` / ``wacom_set_query_w9021``) and prints the detected part as ``"Wacom-0x%X"``. Capability query ================ ``wacom_query_device`` (``@0x9ea628``) → ``wacom_get_hid_desc`` reads the device's HID/query descriptor and logs the pen's capabilities from fixed offsets in the descriptor struct: - ``x_max`` / ``y_max`` — active-area resolution (log ``"x_max:%d, y_max:%d, pressure:%d, fw:%d"``); - ``pressure`` — pressure range; - ``height`` — hover height (log ``"height:%d, tilt_x:%d, tilt_y:%d"``); - ``tilt_x`` / ``tilt_y`` — tilt range (bytes); ``wacom_tilt_flags`` gates whether tilt is reported; - ``fw`` — firmware version byte; the MPU type byte becomes the input-device name ``Wacom-0x%X``. Report (coordinate) packet =========================== ``wacom_i2c_data_read`` (``@0x9eb1d8``) performs an I²C read of the input report (length = descriptor ``input report length``) into the client buffer and decodes, from fixed offsets: .. list-table:: :header-rows: 1 :widths: 16 18 66 * - Field - Encoding - Meaning * - flags - byte - tool/tip/side-button state * - X - u16 LE - raw X coordinate * - Y - u16 LE - raw Y coordinate * - pressure - u16 LE - pen pressure, clamped to **12-bit** (``0xfff``) The raw X/Y are then transformed by (a) an orientation word — swap X↔Y and/or invert against ``x_max``/``y_max`` per rotation bits — and (b) a 2×3 **calibration matrix** (``(m00·x + m01·y + off)/denom``) applied before the values are reported to the input subsystem. The read has a **10-attempt retry** loop with a short delay between tries; after **11** consecutive failures it triggers a controller reset (``@0x9eaee8``). Firmware update and features ============================= The driver can reflash the pen MPU: ``wacom_get_bootloader_version`` / ``wacom_get_mpu_type`` / ``wacom_flash_cmd`` / ``wacom_flash_w9021`` / ``wacom_write_block_w9021`` / ``wacom_write_data_w9021`` / ``wacom_flash_end_w9021`` implement the W9021 flashing protocol (``wacom_fw_update`` / ``wacom-gpio_update`` drive it). Runtime features: ``wacom_set_report_rate`` (with the DT ``onyx,report-rate-high`` knob), ``wacom_i2c_set_feature`` / ``…_get_feature`` (HID feature reports), and **USI active-pen** support (the recovery menu exposes ``/sys/onyx_misc/onyx_active_pen/usi_enable`` — :doc:`/boot/recovery`). Power management: ``wacom_suspend`` / ``wacom_resume`` with an IRQ wake-lock (``wacom_irq_wakelock``) and a ``wacom_pm`` sysfs store. Flashing protocol opcodes (recovered from the userspace flash tool) ===================================================================== The kernel driver above only names the flashing *functions*; the actual **command opcodes** are not present there. They are recovered here from a standalone userspace tool, ``artifacts/vendor_a/bin/wacom_flash`` (stripped ARM64 ELF, entry ``0x2000``; its own debug strings call the flashable part "**WEZ01**", the internal MCU behind the W9021 EMR front-end, and its usage string names a sample image ``WEZ01_056.hex``). All protocol logic is local to the binary (only libc is imported), so it was disassembled directly — no shortcut via imported symbols was available. Two local helpers do all the I/O, identified by their own embedded debug strings (``"wacom_i2c_set_feature"`` / ``"wacom_i2c_get_feature"``, loaded right next to each call): - **``wacom_i2c_set_feature`` (file/vaddr ``0x27b4``)** builds an ``(len+8)``-byte buffer — ``[reg 0x0004 u16 LE][opcode|0x30][0x03] [reg 0x0005 u16 LE][len+2 as u16 LE][payload…]`` — and sends it with a plain ``write(2)`` (the fortified ``__write_chk``) directly on the ``/dev/i2c-1`` fd. Registers ``0x0004``/``0x0005`` are distinct from the runtime touch-report register documented above. - **``wacom_i2c_get_feature`` (file/vaddr ``0x28f4``)** builds a near-identical header but issues it via ``ioctl(fd, 0x0707 /* I2C_RDWR */, &i2c_rdwr_ioctl_data)`` — a standard two-``i2c_msg`` write-then-read I²C transaction (confirmed by resolving the call's PLT target through ``.rela.plt``). Each flashing sub-command is a distinct function, its name confirmed by its own adjacent debug-string load (not inferred): .. list-table:: :header-rows: 1 :widths: 22 12 30 36 * - Function (offset) - Sub-op - SET payload (hex) - Notes * - ``flash_query_wez01`` (``0x2a7c``) - ``0x07`` - ``07 07 07`` - handshake/identity; GET (opcode ``0x08``, len 6) must return ``resp[1]=7, resp[2]=7, resp[3]=6`` * - ``flash_blver_wez01`` (``0x2bdc``) - ``0x04`` - ``07 04 07`` - get bootloader version; ``resp[1]=4, resp[2]=7`` gate the check, ``resp[3]`` is the version byte (``"BL version: %x"``) * - ``flash_mputype_wez01`` (``0x2d38``) - ``0x05`` - ``07 05 07`` - get MPU/chip type; ``resp[1]=5, resp[2]=7`` gate, ``resp[3]`` is the MPU-type byte (``"MPU type: %x"``) * - ``flash_erase_all`` (``0x2e94``) - ``0x10`` - ``07 10 02 03 d8`` - erase; ``resp[1]=0x10, resp[2]=2`` gate, then ``resp[3]`` (an MPU-type byte in ``0x81``–``0x87``) is bit-tested against mask ``0x43`` — only MPU types ``0x81``/``0x82``/``0x87`` are allowed to proceed * - ``flash_write_block_wez01`` (``0x3060``) - ``0x07`` - ``[addr:2][≤264-byte data][1-byte checksum]`` (len ``0x10a``) - address bytes come from the Intel-HEX record's own address field; checksum is an 8-bit two's-complement (negated running sum), computed in a loop immediately before the call * - "``GetAlignmentInfo``" / ``flash_write_wez01`` (``0x35e4``) - ``0x07`` - large (~0x138-byte) payload - write+verify step; GET opcode ``0x0e``, len 269; its own, *different* ``resp[1]=0xf, resp[2]=1`` gate — numerically distinct from ``flash_erase_all``'s ``resp[1]=0x10, resp[2]=2`` — but the same downstream MPU-type/mask-``0x43`` check * - ``flash_end_wez01`` (``0x3b20``) - — (top-level opcode ``0x02``, not ``0x07``) - ``02 02`` - closes boot mode Every opcode above is read directly off ``wacom_flash``'s own machine code and cited by file/vaddr offset in ``artifacts/re_static/item_mxo_wacom_recheck.txt`` (§3), including the PLT resolution that confirms ``ioctl``/``__write_chk`` and the ``I2C_RDWR`` request code. ``libonyx_pen_touch_reader.so`` was also checked for flashing detail: its only flash/erase-adjacent symbols are ``EraseReader`` / ``SideEraseReader`` (the stylus eraser-tip / side-button input handlers, unrelated to MCU flashing) — no flashing logic exists in that library. .. note:: **Honest limit.** No W9021/WEZ01 firmware image (``.hex`` or otherwise) exists anywhere in this dump (checked broadly across ``_READONLY`` and ``artifacts``) — only the flashing tool. So while the wire protocol (registers, opcodes, response gating, checksum) has been recovered, what a flashed image's *contents* mean — the WEZ01 MCU's own instruction set / memory map — is not, for the same reason the MXO NVCM register semantics aren't (:doc:`tcon`'s MXO section): no datasheet or firmware sample is present in this dump. This is a hard "sample not present" limit: recovering it would require a firmware image that does not exist anywhere in this dump. A shared Onyx "TP info" debug function (``0x9e94a8``) covers both this chip and the capacitive-touch controller through one common struct — the "compile every candidate vendor driver" house convention this confirms is detailed in :doc:`/sensors/touchscreen`. Provenance ========== :Source: ``artifacts/dtbo_a/overlay_0.dts`` (the ``compatible`` binding, I²C address, GPIO assignments); ``artifacts/kernel`` (Wacom driver functions and the query/report decode — disassembled via the ``__func__``-xref + ``capstone`` toolkit, script ``artifacts/re_static/wacom.py``); ``artifacts/vendor_a/bin/wacom_flash`` (standalone flashing tool, disassembled directly with ``capstone``/``elfdis.py`` plus manual ``.rela.plt`` resolution — the flashing opcode table above; full evidence in ``artifacts/re_static/item_mxo_wacom_recheck.txt``). :Method: device-tree reading + targeted static disassembly (no ELF symbols / no ``kallsyms`` for the kernel; ``wacom_flash`` is a stripped ELF resolved via its own ``.dynsym``/``.rela.plt`` and embedded debug strings). No code executed; nothing run on a device. :Cross-refs: :doc:`tcon`, :doc:`stack`, :doc:`/sensors/touchscreen`, :doc:`/sensors/misc`, :doc:`/boot/recovery`, :doc:`/boot/dtbo`.