Cross-platform: Sony IMX init pattern + gen2 LE write decoder + V2 ioctls#152
Merged
Cross-platform: Sony IMX init pattern + gen2 LE write decoder + V2 ioctls#152
Conversation
Cross-platform sweep (sibling Majestic captures on hi3516cv300+IMX291 and hi3516av200+IMX385) showed the segmenter always falling back to pre_sensor-only. Root cause: find_init_bounds() was hardcoded to look for SmartSens's `0x100=0 ... 0x100=1` cycle. Sony IMX uses the reverse polarity on a different register: `0x3000=1` (standby) followed by `0x3000=0` (stream-on). Refactored to a small table of (family, reg, init_val, stream_val) patterns. find_init_bounds() tries each in order, returns the first that finds both endpoints, and reports the matched pattern. Mode-switch detection takes the same pattern (so a Sony sensor that re-cycles 0x3000 mid-trace correctly registers as a mode_switch_N). Two families seeded: * smartsens - 0x100=0/0x100=1 (SC2315E, SC2335, SC*) * sony_imx - 0x3000=1/0x3000=0 (IMX291, IMX385, IMX307, ...) Validated end-to-end on real captures from three sibling Majestic cameras: * hi3516cv300 + IMX291: 65 init writes, sony_imx detected, scaffold compiles with full LVDS attr struct dump * hi3516av200 + IMX385: 54 init writes, sony_imx detected, scaffold compiles with full MIPI attr struct dump * hi3516ev200 + SC2315E: 173 init events, smartsens detected, diff vs widgetii/smart_sc2315e unchanged at 100/100/100% (regression) Pipeline test extended with a Sony fixture asserting both pattern detection and successful scaffold generation. Docs updated with the "Sensor-family init patterns" section listing the table and noting how to add new families. Also documented a separate platform limitation discovered in the sweep: hi3518ev200 + jxf22 produces an empty trace because libsns_jxf22.so writes the SoC I2C controller via mmap'd /dev/mem instead of /dev/i2c-X. ipctool's ptrace can't decode that path; flagged in docs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
and CLONE_FILES sibling fd state Cross-platform sweep flagged hi3518ev200 + jxf22 as producing an empty trace. Multiple decoder issues, all separately useful: * i2c_write_exit_cb was hardcoded for V3+'s 2+1-byte big-endian shape. hisi_gen2_sensor_write_register on V1/V2/V2A packs reg_addr little- endian and supports 1+1 / 2+1 / 2+2 byte forms (set via I2C_16BIT_REG / I2C_16BIT_DATA ioctls). Now infers widths from nbyte and picks endianness from chip_generation, matching both hisi_gen2_sensor_write_register and hisi_sensor_write_register in hal_hisi.c. * New hisi_gen2_ioctl_exit_cb decodes I2C_SLAVE_FORCE on V2/V2A so `sensor_i2c_change_addr` lines surface there too (V3+ already had this via hisi_i2c_read_exit_cb). * uClibc on some V1/V2 firmwares maps the libc write() function to __NR_writev (146) with a single iovec instead of __NR_write (4), silently dropping any sensor I/O from our previous decoder. New syscall_writev_exit reads the iovec from the tracee, validates iovcnt (cap 8), and forwards to the same write_exit callback as plain write() with iov[0].base/len. Multi-iovec stdio writes (puts/printf) still flow through but the i2c decoder's nbyte 2..4 sanity check filters them out. * PTRACE_O_TRACEFORK / TRACEVFORK added alongside TRACECLONE for defensive coverage of streamers that spawn workers via genuine fork() (most use threads via CLONE; this is just-in-case). * CLONE_FILES siblings share the kernel fd table; ipctool tracked fds per process_t, so an open in the parent left peer->fds[N] empty and any peer write on fdN dropped to no callback. New broadcast_fd_open / broadcast_fd_close mirror open/close events to every tracked process_t, restoring the invariant. Verified end-to-end: * SC2315E + Majestic regression: still 100/100/100% diff against widgetii/smart_sc2315e. * IMX291 (cv300, sony_imx pattern), IMX385 (av200, sony_imx pattern): scaffolds still compile. * /bin/echo on hi3518ev200 traces correctly (sanity check that the syscall path works on V2 kernel). The hi3518ev200 + jxf22 specific case empirically still produces an empty trace despite the published source using write() - the .so on that camera appears to take a path our dispatch doesn't see. Documented the diagnostic checklist (compare /proc/<pid>/fd with the trace's i2c-N banner; look for unfamiliar /dev/* devices) so the next researcher recognises the failure mode quickly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
c193e7a to
a4b7709
Compare
3 tasks
widgetii
added a commit
that referenced
this pull request
May 3, 2026
…race While investigating an empty trace from libsns_jxf22.so on hi3518ev200, two real bugs in the wait loop turned up that are worth fixing independently of jxf22's specific issue. * Signal forwarding. The loop ended every iteration with ptrace(PTRACE_SYSCALL, pid, 1, NULL). The fourth arg is the signal to inject when resuming the tracee, and NULL meant "drop the signal entirely". So if a child stopped on a real signal (anything other than SIGTRAP - SIGCHLD, SIGRT*, SIGUSR*, etc.), ipctool swallowed it instead of forwarding it. The HiSilicon SDK uses realtime signals heavily for video pipeline coordination; dropping them under trace can deadlock a streamer. Now: if the stop signal is SIGTRAP it stays at 0 (nothing to forward); if it's a genuine signal-delivery stop, the original signal gets re-injected when the tracee resumes. * PTRACE_EVENT_FORK / PTRACE_EVENT_VFORK weren't handled. #152 added the matching PTRACE_O_TRACEFORK/VFORK options but the wait loop only matched PTRACE_EVENT_CLONE. So a forked child fired PTRACE_EVENT_FORK in its parent (ignored), then on its first syscall stop the lookup against `pids` returned NULL and we hit the "BAD lookup" branch which `break`'d out of the wait loop - killing the whole trace. Now: the same CLONE handling block matches all three events (CLONE | FORK | VFORK). Plus the BAD-lookup case no longer breaks - it just continues, since under TRACEFORK there's a brief window where a child can hit a syscall stop before its parent's EVENT_FORK arrives and we register it. * Exit handling for unknown PIDs no longer breaks the loop either. If a child exits before we observed its creation event, we just skip the bookkeeping and keep tracing the rest. tools/sns_init_probe.c added: a tiny dlopen+dlsym wrapper that loads a libsns_*.so directly and calls its sensor init function. Lets a future researcher exercise sensor I/O paths in isolation from the streamer (handy for narrowing down "empty trace" issues to the .so vs the surrounding application). Build instructions in the file header. Verified: * SC2315E + Majestic regression: 100/100/100% diff against widgetii/smart_sc2315e unchanged. * hi3518ev200 + jxf22 still produces an empty trace despite the signal/fork fixes. Strace confirms the streamer DOES make 79 write() calls of 2 bytes to a /dev/i2c-0 fd (opened TWICE: first at fd 18 by the probe code, then a second open at fd 25 by libsns_jxf22.so itself - that second open is what we're missing). The bug is somewhere else in the trace path on this specific camera/build combo; tracked separately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
widgetii
added a commit
that referenced
this pull request
May 3, 2026
…race (#153) While investigating an empty trace from libsns_jxf22.so on hi3518ev200, two real bugs in the wait loop turned up that are worth fixing independently of jxf22's specific issue. * Signal forwarding. The loop ended every iteration with ptrace(PTRACE_SYSCALL, pid, 1, NULL). The fourth arg is the signal to inject when resuming the tracee, and NULL meant "drop the signal entirely". So if a child stopped on a real signal (anything other than SIGTRAP - SIGCHLD, SIGRT*, SIGUSR*, etc.), ipctool swallowed it instead of forwarding it. The HiSilicon SDK uses realtime signals heavily for video pipeline coordination; dropping them under trace can deadlock a streamer. Now: if the stop signal is SIGTRAP it stays at 0 (nothing to forward); if it's a genuine signal-delivery stop, the original signal gets re-injected when the tracee resumes. * PTRACE_EVENT_FORK / PTRACE_EVENT_VFORK weren't handled. #152 added the matching PTRACE_O_TRACEFORK/VFORK options but the wait loop only matched PTRACE_EVENT_CLONE. So a forked child fired PTRACE_EVENT_FORK in its parent (ignored), then on its first syscall stop the lookup against `pids` returned NULL and we hit the "BAD lookup" branch which `break`'d out of the wait loop - killing the whole trace. Now: the same CLONE handling block matches all three events (CLONE | FORK | VFORK). Plus the BAD-lookup case no longer breaks - it just continues, since under TRACEFORK there's a brief window where a child can hit a syscall stop before its parent's EVENT_FORK arrives and we register it. * Exit handling for unknown PIDs no longer breaks the loop either. If a child exits before we observed its creation event, we just skip the bookkeeping and keep tracing the rest. tools/sns_init_probe.c added: a tiny dlopen+dlsym wrapper that loads a libsns_*.so directly and calls its sensor init function. Lets a future researcher exercise sensor I/O paths in isolation from the streamer (handy for narrowing down "empty trace" issues to the .so vs the surrounding application). Build instructions in the file header. Verified: * SC2315E + Majestic regression: 100/100/100% diff against widgetii/smart_sc2315e unchanged. * hi3518ev200 + jxf22 still produces an empty trace despite the signal/fork fixes. Strace confirms the streamer DOES make 79 write() calls of 2 bytes to a /dev/i2c-0 fd (opened TWICE: first at fd 18 by the probe code, then a second open at fd 25 by libsns_jxf22.so itself - that second open is what we're missing). The bug is somewhere else in the trace path on this specific camera/build combo; tracked separately. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 3, 2026
ipctool trace produces empty output on Hi3518EV200 + libsns_jxf22.so despite write() being used
#154
Closed
widgetii
added a commit
that referenced
this pull request
May 4, 2026
#156) Documents what's actually needed to capture sensor-mode reconfiguration on a XiongMai Sofia build, after we walked through the false leads: * Sofia's runtime DVR-IP `BroadTrends.AutoGain` knob does NOT reconfigure the sensor. The path is `Public_LinearWDR_Switch` -> `LibXmCap_System_switchWdrMode` -> `XmCap_IspVi_switchWdrMode`, which calls `HI_MPI_ISP_GetPubAttr`, optionally does a VI unbind/rebind, and persists the new mode to `/mnt/mtd/Config/wdrmode.dat` via `libdvr.so`'s `SetWdrMode` (a thin `fopen + fwrite`). It does not call `HI_MPI_ISP_SetWDRMode` and does not invoke any sensor-driver code. Confirmed empirically on two boards: zero `0x100` (SmartSens) / `0x3000` (Sony) cycles after `AutoGain` toggle. * The actual sensor-side mode dispatch happens at Sofia startup: Sofia reads wdrmode.dat (4-byte LE int) and calls `cmos_set_image_mode(mode)` inside the per-sensor driver. For Sony IMX29x the case-set is 0=linear, 2=line-WDR (DOL), 3=half-frame WDR, 4=full-frame WDR. * Procedure: write the desired mode to wdrmode.dat with `printf '\x02\x00\x00\x00' > ...`, kill Sofia, restart under the bind-mount trace wrapper. Sofia at startup picks up the new mode and the trace captures the alternative init register sequence. * Worked example: Hi3516CV300 + IMX291 + Sofia. Mode 0 (linear) produced 70 writes; mode 2 (line-WDR) produced 140 writes with 17 WDR-only register values (`0x3007=0x00`, `0x3009=0x02`, `0x3018=0x6D`, `0x3046=0xE1`, etc. - matching Sony's IMX29x DOL configuration). `trace_segment.py`'s existing `find_mode_switches` heuristic catches the two-pass init as `init` + `mode_switch_1` with no changes; `trace_to_driver.py` emits the WDR body as `<sensor>_set_mode_1`. * Diagnostic: which sensors in your Sofia binary have WDR firmware vs being linear-only? Grep the binary for `_init ` debug markers and `LINE Init OK` banners. A sensor with both has multi-mode drivers; one with only `_init` is linear-only and `wdrmode.dat` changes are no-ops at the sensor level (the file still gets persisted, but no sensor-driver code path responds). Also generalises the existing "Segmenter heuristic" note from the hardcoded SmartSens `0x100` pair to "the stream-control register pair the matched `init_pattern` used", with explicit Sony IMX and SOI/JX examples - reflects the actual code in `trace_segment.py` post-#152. No tooling changes; CI `test-extraction-pipeline` passes unchanged. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cross-platform sweep of Majestic captures on three sibling cameras (hi3516cv300+IMX291, hi3516av200+IMX385, hi3518ev200+jxf22) revealed several distinct platform-handling issues. All five fixes are useful independently; the open question on jxf22 specifically is documented as a diagnostic checklist.
Fixes
1. Segmenter: detect sensor family by init pattern
Was hardcoded for SmartSens's
0x100=0 ... 0x100=1cycle. Sony IMX uses the reverse polarity on a different register:0x3000=1(standby) →0x3000=0(stream-on). Refactored to a small per-family table:2. Decoder: gen2 little-endian writes
hisi_gen2_sensor_write_register(V1/V2/V2A) packs reg_addr little-endian viawrite(fd, buf, reg_width + data_width);hisi_sensor_write_register(V3+) packs big-endian.i2c_write_exit_cbwas hardcoded for V3+'s 2+1-byte big-endian shape, so any V2 driver writing 1+1 bytes (e.g. JXF22-class 8-bit-reg sensors) or any V2 driver writing 2+1 LE produced wrong values or was silently dropped. Now handles 1+1 / 2+1 / 2+2 byte forms with chip-family-aware endianness.3. Decoder: V2 I2C_SLAVE_FORCE handler
New
hisi_gen2_ioctl_exit_cbdecodesI2C_SLAVE_FORCEon V2/V2A sosensor_i2c_change_addrlines surface (V3+ had this viahisi_i2c_read_exit_cb).4. Decoder: writev support
uClibc on some V1/V2 firmwares maps the libc
write()function to__NR_writev(146) with a single iovec instead of__NR_write(4). Without this handler, sensor I/O on those targets is invisible. Newsyscall_writev_exitreads the iovec from the tracee, caps iovcnt at 8, and forwards to the same fd write_exit callback as plain write() withiov[0].base/len. Multi-iovec stdio writes (puts/printf) flow through but the i2c decoder's nbyte 2..4 check filters them.5. CLONE_FILES sibling fd-state broadcast
Threads cloned with CLONE_FILES share the kernel fd table — opening in one thread makes the fd usable in all peers. ipctool tracked fds per
process_t, so an open in the parent leftpeer->fds[N]empty and any peer write on fdN silently dropped to no callback. Newbroadcast_fd_open/broadcast_fd_closemirror open/close events to every tracked process, restoring the invariant.6. PTRACE_O_TRACEFORK / TRACEVFORK
Added alongside
TRACECLONEfor defensive coverage of streamers that spawn workers via genuine fork(). Most use threads via CLONE; this is just-in-case.Cross-platform validation
sony_imx#if 0blocksony_imx#if 0blocksmartsenswidgetii/smart_sc2315eunchanged at 100/100/100% (regression)/bin/echoon hi3518ev200 traces correctly (syscall=4fires) — the trace path itself is sound on this kernel.hi3518ev200 + jxf22 status
Source code at OpenIPC/glutinium/hi35xx_sensor_jxf22 confirms the driver uses
write(g_fd, buf, idx)after the I2C_SLAVE_FORCE / I2C_16BIT_REG / I2C_16BIT_DATA setup. ipctool now decodes that path correctly. But on the specific test camera I have, the trace still shows zerosensor_write_registerlines despite the streamer reportinginit success. Best reading: the.soon this camera takes a path our dispatch doesn't see — I observe/dev/sys(char major 218 minor 8, HiSilicon-specific) being opened by Majestic at runtime, and our trace ends shortly after thei2c-Nbanner. Diagnostic checklist now indocs/sensor-driver-extraction.mdso the next researcher recognises the failure mode quickly.Test plan
tools/test_pipeline.shextended with a Sony fixture (assertsinit_pattern == \"sony_imx\"and successful scaffold generation)widgetii/smart_sc2315eunchanged at 100/100/100%test-extraction-pipelinepasses both old and new fixture assertionspre_sensoronly🤖 Generated with Claude Code