10.5. The /dev/ebc interface: ioctls, update modes, and the waveform format¶
This page is the single copy of the /dev/ebc control-surface contract
that The e-ink software stack (Onyx EPDC) (userspace down to the driver interface) and
E-ink driver internals (register-level, from the kernel) (register-level driver internals) both need: the ioctl command
set, the update-mode/scheme taxonomy, and
the .wbf waveform file format every mode indexes into. Everything below
is a fact about the interface, not about who calls it or how the FPGA
executes it — for that, see The e-ink software stack (Onyx EPDC) (userspace glue,
init.onyx.rc, the Onyx libraries) and E-ink driver internals (register-level, from the kernel) (the lfe5u/
onyx_tcon/onyx_epdc register-level internals, the LUT pipeline, the
SDE-atomic-commit splice).
10.5.1. The /dev/ebc ioctl surface¶
The handler is epdc_ioctl (kernel file offset @0x57a988; a ~15 KB
switch) with a legacy ebc_ioctl alongside. Dispatch is on the ioctl
sequence number via a jump table (not a flat compare chain), so the
numeric _IOC encodings are not literal constants in the switch; one
number is pinned from the userspace side — the Onyx-patched SurfaceFlinger
issues ioctl(/dev/ebc, SET_EBC_SEND_UPDATE = 0x700c, …), in the same
0x70xx rockchip-style “ebc” range the Onyx software EPDC reuses.
ioctl |
Purpose |
|---|---|
|
query framebuffer geometry / obtain a drawing buffer |
|
submit a filled frame (with byte offset) |
|
update a rectangle, tagged |
|
fence on / cancel outstanding updates |
|
queue scheme; queue depth + power-down delay |
|
waveform override, LUT enable, gamma table |
|
external-buffer → framebuffer sync |
|
screen-capture path |
|
driver serial / version |
The update-request struct carries {waveform_mode, update_mode,
update_marker, flags, temp} over a rectangle [l, t, w, h]. The other
EBC ioctls SurfaceFlinger issues sit in the same 0x70xx family (e.g.
0x701d) — plain 0x7000-based magic numbers, not a documented public
ABI.
10.5.2. Update modes and schemes¶
The named, special-cased paths (these correspond to the lfe5u_i2c DT
capability flags):
Mode / scheme |
Evidence |
|---|---|
A2 (fast) |
|
GU / REGAL |
|
CFA (colour) |
|
handwrite |
|
dither |
|
merge / snapshot |
|
10.5.3. The waveform file format (.wbf)¶
The .wbf blob is eink_waveform.wbf — an E-Ink Corp .wbf waveform
blob, 393,123 bytes, present as a file at /system/waveform/
eink_waveform.wbf and compiled into the kernel image through
CONFIG_EXTRA_FIRMWARE. It carries an embedded FPL / panel identifier
string, 470_VSF006_HM7501_EC103KH1C1_SC1452-KCC_TC (the 10.3″ colour
E-Ink FPL for this device), loaded from /vendor/waveform or
/system/waveform or the built-in firmware.
10.5.3.1. Header fields¶
Parsed on the way to panel init (onyx_waveform_parse_and_init_panel); the
header field-copy code (kernel file offset @0x593038) reads fields from
the raw header into its internal descriptor and logs them in two groups:
file_size,fpl_material_version, ``waveform_mode_version`` at header offset ``0x10``, ``WFM_REV`` at offset ``0x16``,waveform_version;frame_rate(Hz),waveform_lookup_table_fmt, a 24-bitwaveform_mode_table_address, and the three table dimensions ``mode_num`` × ``temp_num`` × ``gray_num`` — one LUT per (mode, temperature-band, gray-level).
onyx_waveform_mode_transform_init maps framework mode names to the
blob’s numeric indices; the .wbf itself carries only the numbers.
10.5.3.2. Decoded structure¶
Parsing the actual eink_waveform.wbf (393,123 bytes) against that header
map — it is the standard E-Ink/i.MX waveform binary — yields:
Field |
Value |
|---|---|
|
|
|
|
|
|
|
|
|
|
Temperature bands — the table at file offset 0x30 gives the band
boundaries in °C: 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 38, 43, 48,
71 — 14 ranges spanning 0–71 °C (finer near room temperature).
Userspace temp selects the band.
Mode / temperature / LUT tree — the blob is a three-level index. wmta
(0x6c) holds 7 mode entries, each 4 bytes = a 24-bit offset + a
1-byte checksum (the checksum is the byte sum (b0+b1+b2) & 0xff, not
an XOR — verified for every entry). The 7 mode pointers point to per-mode
temperature tables at 0x88, 0xf8, 0x168, 0x1d8, 0x248,
0x2b8, 0x328 (stride 0x70); each of those is 14 entries of
the same [u24 addr][u8 sum] form, pointing to the actual waveform LUT for
that (mode, temperature band). All 98 (7×14) cells resolve inside the file
(0x3a8–0x5fef9 < 0x5ffa3); 92 are distinct — six cells reuse a
neighbour’s pointer, i.e. two adjacent bands share one waveform.
Temperature compensation is visible directly in the LUT sizes. Taking the raw byte-span between consecutive LUT pointers (no decoding needed), within every mode the span shrinks monotonically as the panel warms — cold e-ink needs the longer, more-frame waveforms:
mode\band 0° 3° 6° 9° 12° 15° 18° 21° 24° 27° 30° 33° 38° 43°
m0 212 184 164 144 128 216 192 168 152 136 136 128 120 112
m1 722 638 562 484 407 347 302 258 228 188 186 180 169 128
m2 12781 10820 9669 8446 7532 6250 5441 4804 4350 4156 4263 4191 4207 3925
m3 13012 11021 9824 8579 7653 6360 5527 4874 4400 4213 4322 4257 4276 3994
m4 15445 12889 11289 9694 8639 7571 6389 5050 4610 4448 4260 3964 4097 3804
m5 14880 12390 10881 9321 8329 7311 6174 4901 4481 4319 4126 3866 3999 3720
m6 206 190 182 158 134 110 102 86 · (bands 8–13 degenerate)
Modes 2–5 are the multi-kilobyte grayscale / REGAL waveforms (~13 KB cold →
~4 KB hot); mode 0 and mode 6 are the small fast modes (~100–220 bytes). Mode
6 is defined only for the 8 cold bands (0–21 °C); its bands 8–13 all
collapse to a single trailing offset (0x5fef9, a degenerate LUT) — that
fast mode is simply not used above ~24 °C.
LUT payload encoding — the container above is exact; the inner payload is
the FPL-specific part and is only partially decoded. The small modes (0, 6)
carry no 0xfc byte and read as literal packed transition bytes with
0xff separators (e.g. mode 0/temp 0 = 55 ff 55 ff … aa ff 00 ff + a
short trailer; 0x55 = 01 01 01 01, four 2-bit transition codes per
byte). The large modes (2–5) are RLE-compressed with ``0xfc`` (the header
``sb`` byte) as the run escape: a toggle-style decode (0xfc flips
run-mode; a value byte is then followed by a repeat count) consumes every LUT
buffer exactly to its end pointer, confirming 0xfc is the control byte —
but the expanded lengths do not factor into a single fixed frame size
(per-mode GCD = 1), so no frames × framesize grid is asserted. The exact
expansion and the 2-bit-code → drive voltage mapping are completed at runtime
by onyx_get_waveform_one_frame_segment_16bit (@0x59e0b0) and
_onyx_epdc_produce_wf_sg (gray handling is 4-bit / 16-level).
The 7 modes are the numeric waveform_mode indices userspace passes in the
ebc update struct, mapped through onyx_waveform_mode_transform_init
(above). Full parse in artifacts/re_static/wbf_format.txt (and
artifacts/re_eink/wbf_parse.txt).
The TCON firmware and panel configs the FPGA needs are also built into the
kernel via CONFIG_EXTRA_FIRMWARE (they are not files in any partition):
lfcpnx/lfcpnx100_tcon_fw_{9f,9e,99,a2,a5,a7}.bin (six LF-CPNX FPGA TCON
firmware variants, selected by panel hardware revision) and
mxo/mxo1300_nvcm_{81,82,83,84,86,87}.ied + mxo/mxo4300_nvcm_{81,82}.ied
(panel NVCM init-data variants; see E-ink driver internals (register-level, from the kernel) for the exact
bundled set).
Because the waveform + TCON firmware live inside the boot image’s kernel, a
replacement bootloader/kernel would have to carry (or re-request) them; they
are the one part of the e-ink path that is effectively below userspace
even though the driver that consumes them is an ordinary built-in kernel
driver.
10.5.4. Provenance¶
- Source:
artifacts/kernel(built-inonyx_epdcdriver strings: theepdc_ioctlswitch, update-parm struct, waveform-header parse,a2_enhance/gu_regal/cfa_mode/ dither / handwrite/merge tokens),artifacts/boot_a/kernel_config.txt(CONFIG_ONYX_EPDC_*/CONFIG_EXTRA_FIRMWARE),system_a.img’swaveform/eink_waveform.wbf(parsed against the recovered header map — full walk inartifacts/re_eink/wbf_parse.txt).- Method:
strings + targeted static disassembly (base-free ADRP/ADD cross-referencer + capstone; function starts located via
__func__log-string cross-reference). No code executed; nothing run on a device.- Cross-refs:
The e-ink software stack (Onyx EPDC) (the userspace client of this interface — SurfaceFlinger/
libgui,init.onyx.rc, the Onyx libraries), E-ink driver internals (register-level, from the kernel) (the register-level FPGA/TCON and EPDC internals that implement this interface), Display pipeline — DSI transport to the color e-ink TCON (DSI transport, panel geometry), Firmware-blob inventory (the payloads) (the built-in TCON/waveform/NVCM firmware inventory).