From 584b2744a6e9cadcaea2fac383ae733a659f33d9 Mon Sep 17 00:00:00 2001 From: Dmitry Ilyin <6576495+widgetii@users.noreply.github.com> Date: Sun, 3 May 2026 17:33:21 +0300 Subject: [PATCH] Cross-diff against widgetii/sc_sc2315e (older RE port) Closes the sixth gap from #145's plan-vs-shipped audit. The plan called for triangulating the trace against widgetii/sc_sc2315e (the older reverse-engineered port from the SC2235 SDK template, predating widgetii/smart_sc2315e). Two small relaxations in trace_diff.py let that diff run cleanly: * extract_function_body() previously hard-required `void ` to open. The RE port's init returns int (`int sc2235_init(VI_PIPE)`), so the scope flag silently matched zero writes. Relaxed the regex to accept any whitespace-delimited rettype/qualifier sequence. * RE_ANY_WRITE expected the register-write call name to end at `write_register(`. The RE port uses `sensor_write_register_0(...)` (bus-numbered suffix). Allowed an optional `\w*` after `register` before the open paren. Result: pair-wise diff between trace, smart_sc2315e, and sc_sc2315e all show 100/100/100% (172 writes, 169 unique regs, identical values, identical order). Three independent artifacts converge on the same canonical SC2315E init - vendor binary, OpenIPC port from SC2231 template, RE port from SC2235 template. No drift, no missing regs. test_pipeline.sh extended with a synthetic ref using the older RE-port shape (int-returning function, _0-suffix call) so the relaxed regex never regresses. Self-diff (current shape) and cross-style (older shape) both asserted at 100% address match. Docs: new "Triangulating against multiple references" section walks through the four-way comparison procedure and the SC2315E result. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/sensor-driver-extraction.md | 40 ++++++++++++++++++++++++++++++++ tools/test_pipeline.sh | 27 +++++++++++++++++++++ tools/trace_diff.py | 18 ++++++++++---- 3 files changed, 80 insertions(+), 5 deletions(-) diff --git a/docs/sensor-driver-extraction.md b/docs/sensor-driver-extraction.md index fdb4bba..9b557f8 100644 --- a/docs/sensor-driver-extraction.md +++ b/docs/sensor-driver-extraction.md @@ -516,6 +516,46 @@ function carries the default. This is **expected**, not a bug; scope the diff to the `linear_init` function on both sides and the mismatches disappear. +### Triangulating against multiple references + +When a sensor has more than one published source — e.g. an OpenIPC port +**and** an older reverse-engineering effort — diffing against both lets +you triangulate registers that appear in only one as either: + +- **In trace + only in ref-A**: ref-B is incomplete (the RE missed + this register, or the port pruned it). +- **In trace + only in ref-B**: ref-A drifted from the binary + (vendor patched the closed driver, port didn't follow). +- **In both refs but not in trace**: probably dead code in both refs, + or behind a build flag the trace didn't exercise. +- **In trace and both refs**: high confidence, ship it. + +For SC2315E specifically, the four artifacts available — the trace +from Majestic, the trace from Sofia, `widgetii/smart_sc2315e` +(OpenIPC port from SC2231 SDK template), and `widgetii/sc_sc2315e` +(older RE port from SC2235 SDK template) — agree byte-for-byte on +init: 172 writes, 169 unique addresses, identical values, identical +order, every pair-wise comparison at 100/100/100%. + +```bash +# OpenIPC port +python3 tools/trace_diff.py generated.c \ + /tmp/smart_sc2315e/sc2315e_sensor_ctl.c \ + --gen-scope sc2315e_linear_init \ + --ref-scope sc2315e_linear_1080P30_init + +# Reverse-engineered port (note: int return, sensor_write_register_0) +python3 tools/trace_diff.py generated.c \ + /tmp/sc_sc2315e/sc2235_sensor_ctl.c \ + --gen-scope sc2315e_linear_init \ + --ref-scope sc2235_init +``` + +`trace_diff.py` accepts both `void (...)` and `int (...)` +function definitions, and its register-write regex matches both +`sensor_write_register(...)` and `sensor_write_register_0(...)` +(the bus-numbered suffix used by the older RE port). + Other expected sources of mismatch on a real capture: - **Registers in the reference source but not in the trace**: these are diff --git a/tools/test_pipeline.sh b/tools/test_pipeline.sh index 5d24655..81aff44 100755 --- a/tools/test_pipeline.sh +++ b/tools/test_pipeline.sh @@ -84,4 +84,31 @@ python3 tools/trace_diff.py "$tmp/driver.c" "$tmp/driver.c" \ grep -q '100.0%)' "$tmp/diff.out" \ || { echo "self-diff not 100%"; exit 1; } +# Diff against a reference that uses the older RE-port shape: +# `int (VI_PIPE)` returning int (not void), with +# `sensor_write_register_0(...)` (bus-numbered suffix). Validates that +# extract_function_body and RE_ANY_WRITE handle both styles. +echo "== trace_diff.py cross-style ref ==" +cat > "$tmp/ref_old_style.c" <<'REF' +#include +typedef int VI_PIPE; +static void sensor_write_register_0(unsigned int a, unsigned int v) +{ (void)a; (void)v; } + +int oldstyle_init(VI_PIPE pipe) { + (void)pipe; + sensor_write_register_0(0x100, 0x0); + sensor_write_register_0(0x3034, 0x81); + sensor_write_register_0(0x3039, 0xa6); + sensor_write_register_0(0x320e, 0x4); + sensor_write_register_0(0x100, 0x1); + return 0; +} +REF +python3 tools/trace_diff.py "$tmp/driver.c" "$tmp/ref_old_style.c" \ + --gen-scope testsensor_linear_init \ + --ref-scope oldstyle_init | tee "$tmp/cross.out" +grep -q 'address match: 4 / 4' "$tmp/cross.out" \ + || { echo "cross-style ref didn't match (relaxed regex broken?)"; exit 1; } + echo "OK: pipeline test passed" diff --git a/tools/trace_diff.py b/tools/trace_diff.py index 6a6fd68..88eec1d 100644 --- a/tools/trace_diff.py +++ b/tools/trace_diff.py @@ -26,9 +26,11 @@ r"((?:0[xX])?[0-9a-fA-F]+)\s*,\s*" r"((?:0[xX])?[0-9a-fA-F]+)\s*\)" ) -# Wider net for things like sc2315e_write_register(...). +# Wider net for things like sc2315e_write_register(...) or +# sensor_write_register_0(...) (the older RE port at widgetii/sc_sc2315e +# uses the bus-numbered suffix). RE_ANY_WRITE = re.compile( - r"\b\w*write_register\s*\(\s*" + r"\b\w*write_register\w*\s*\(\s*" r"(?:[^,]+,\s*)?" r"((?:0[xX])?[0-9a-fA-F]+)\s*,\s*" r"((?:0[xX])?[0-9a-fA-F]+)\s*\)" @@ -44,12 +46,18 @@ def parse_int(s): def extract_function_body(src, fn_name): - """Return text inside `void (...) { ... }`, brace-balanced. + """Return text inside ` (...) { ... }`, brace-balanced. - Strings/comments are not handled — fine for these driver files which + rettype is any whitespace-delimited token (int, void, HI_S32, HI_VOID, + static, etc.) - some references use C int returns instead of void. + Strings/comments are not handled - fine for these driver files which keep everything on one line per write. """ - pat = re.compile(r"\bvoid\s+" + re.escape(fn_name) + r"\s*\([^)]*\)\s*\{") + # Match a function definition: identifier(s) before the name, then (args) { + # The qualifier+rettype block can include `static`, `inline`, multiple words. + pat = re.compile( + r"(?:\b\w+\s+)+" + re.escape(fn_name) + r"\s*\([^)]*\)\s*\{" + ) m = pat.search(src) if not m: return None