10.6. 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 (DTBO overlays).
The e-ink display path itself is in E-ink driver internals (register-level, from the kernel); the
front-light (lm3630a) and its class devices are in The e-ink software stack (Onyx EPDC).
Capacitive touch (the separate finger-layer digitizer) is in
Capacitive touch (Parade pt_core).
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
E-ink driver internals (register-level, from the kernel), scripts in artifacts/re_static/). No code was
executed.
10.6.1. DT binding¶
Device |
|
Bus |
GPIOs / notes |
|---|---|---|---|
Wacom pen ( |
|
I²C |
irq gpio52, rst gpio54, vdd-ctrl gpio10, vcc-ctrl gpio49;
|
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".
10.6.2. 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_flagsgates whether tilt is reported;fw— firmware version byte; the MPU type byte becomes the input-device nameWacom-0x%X.
10.6.3. 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:
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 ( |
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).
10.6.4. 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 —
Recovery ramdisk (recovery environment)). Power management: wacom_suspend / wacom_resume with
an IRQ wake-lock (wacom_irq_wakelock) and a wacom_pm sysfs store.
10.6.5. 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 plainwrite(2)(the fortified__write_chk) directly on the/dev/i2c-1fd. Registers0x0004/0x0005are 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_msgwrite-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):
Function (offset) |
Sub-op |
SET payload (hex) |
Notes |
|---|---|---|---|
|
|
|
handshake/identity; GET (opcode |
|
|
|
get bootloader version; |
|
|
|
get MPU/chip type; |
|
|
|
erase; |
|
|
|
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 |
“ |
|
large (~0x138-byte) payload |
write+verify step; GET opcode |
|
— (top-level opcode |
|
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 (E-ink driver internals (register-level, from the kernel)’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 Capacitive touch (Parade pt_core).
10.6.6. Provenance¶
- Source:
artifacts/dtbo_a/overlay_0.dts(thecompatiblebinding, I²C address, GPIO assignments);artifacts/kernel(Wacom driver functions and the query/report decode — disassembled via the__func__-xref +capstonetoolkit, scriptartifacts/re_static/wacom.py);artifacts/vendor_a/bin/wacom_flash(standalone flashing tool, disassembled directly withcapstone/elfdis.pyplus manual.rela.pltresolution — the flashing opcode table above; full evidence inartifacts/re_static/item_mxo_wacom_recheck.txt).- Method:
device-tree reading + targeted static disassembly (no ELF symbols / no
kallsymsfor the kernel;wacom_flashis a stripped ELF resolved via its own.dynsym/.rela.pltand embedded debug strings). No code executed; nothing run on a device.- Cross-refs:
E-ink driver internals (register-level, from the kernel), The e-ink software stack (Onyx EPDC), Capacitive touch (Parade pt_core), Misc sensors (fingerprint, hall, keyboard cover), Recovery ramdisk (recovery environment), DTBO overlays.