Phase 03b — TWRP Custom Recovery

Phase 03b — TWRP Custom Recovery

Status: ✅ Stable — TWRP v3.7. Display and touch working. A2 waveform fix (NOP patch) pending.


Version History

VersionKey ChangeOutcome
v1.0Initial build from twrpdtgen skeleton❌ Recovery crashes immediately (libresetprop.so missing)
v1.1-v1.2ADB/USB configfs fixes⚠️ Recovery still crashes
v1.3Kernel format fix (gzip, not raw), LED breadcrumbs⚠️ Recovery starts but crashes — libresetprop.so still missing
v1.4fb0 symlink in init.rc⚠️ Crash persists
v1.5fb0 symlink added to ramdisk⚠️ Crash persists — not the root cause
v1.6libresetprop.so added to ramdisk overlay✅ Recovery binary RUNS — first working TWRP. Display blank (EPDC)
v2.0-v2.5EPDC /dev/ebc ioctl integration in minui⚠️ Display partially working, waveform firmware missing
v2.6Waveform firmware (eink_waveform.wbf) added to ramdiskDISPLAY WORKS
v3.0-v3.2EPDC integration refinement, buffer stride fix✅ Display stable
v3.3Touch rotation fix (swap+Y-invert in TWRP gui.cpp)TOUCH WORKS
v3.7Current stable release, crypto disabled✅ All core functions working

E-ink Display Architecture

Two display control paths exist on this hardware:

  1. /dev/graphics/fb0 (MDSS DSI framebuffer) — TWRP’s default path. Works for pixel writes, but the completion callback onyx_epdc_wait_all_update_complete never fires → TWRP hangs forever waiting for the display refresh to complete.
  2. /dev/ebc (Onyx EPDC control device) — used by the stock recovery. Write pixels to fb0, then issue ioctl 0x700c (EBC_SEND_UPDATE) to tell the EPDC to refresh. This path works.

TWRP minui Patch (graphics_fbdev.cpp)

  • Opens /dev/ebc, mmap’s pixel-exact buffer (7488 stride × 1404 rows)
  • Copies pixels row-by-row from fb0 (stride 7552) to ebc buffer (stride 7488) — 64-byte padding difference
  • Sends ioctl 0x700c with: waveform=0xff, update_mode=0x1000, flags=0x20000
  • Source overlay: device/onyx/noteair1/src-overlay/bootable/recovery/minuitwrp/graphics_fbdev.cpp

Waveform Firmware (critical)

The EPDC requires /waveform/eink_waveform.wbf at boot. In stock Android this is provided via system-as-root. In TWRP the ramdisk IS root, so the file must be included explicitly. Added at device/onyx/noteair1/recovery/root/waveform/eink_waveform.wbf (276 KB). Without this file, display mode 0 times out and the display stays blank.


Display Root Cause: libresetprop.so

The most critical early crash (v1.0–v1.5) was libresetprop.so not being in the recovery ramdisk. The TWRP build system copies this library to out/.../system/lib64/ but not into recovery/root/. Fix: copy explicitly into device/onyx/noteair1/recovery/root/system/lib64/. Note: PRODUCT_PACKAGES += libresetprop in device.mk is ineffective — it targets the system image, not the recovery ramdisk.


Input Devices

#NameHandlerNotes
0qpnp_ponevent0Power button
1cyttsp5_mtevent1Capacitive touchscreen (Parade/Cypress TT)
2onyx_emp Wacom I2C Digitizerevent2, mouse0EMR pen digitizer
3hbtp_vmevent3, mouse1Virtual mouse cursor (suppression needed for UI)
4sf-keysevent4Software function keys
5onyx-hallevent5Hall sensor (magnetic cover)
6gpio-keysevent6Hardware GPIO keys

Touch fix in v3.3: cyttsp5 events needed swap+Y-invert rotation correction in TWRP’s gui.cpp. TWRP’s built-in rotation (TW_ROTATION=90) was not sufficient — the touch coordinate transform must be applied manually.


A2 Waveform Mode (planned)

TWRP requests A2 waveform (0x0c) for fast refresh, but the kernel’s onyx_waveform_mode_transform() at offset 0x427a8c converts it to GC16 (0x02), causing slow full-screen flashes. Fix: NOP the call at offset 0x41d410 in the kernel binary. Tool ready: tools/patch-kernel-waveform-transform.py. Not yet applied — pending testing session.


Boot Trigger

Recovery can be entered via: (1) adb reboot recovery from Android; (2) close magnetic cover + press power (hall sensor method, ABL v2.4+); (3) BCB write (not recommended — risk of loop). Note: adb reboot recovery does NOT write the BCB on this device — safe to use.

More posts