Skip to content

sstar: gpio get/set/scan and the IR-cut hint on infinity6c - #222

Merged
openipc-ai merged 3 commits into
masterfrom
sstar-gpio-infinity6c
Sep 25, 2026
Merged

openipc-ai merged 3 commits into
masterfrom
sstar-gpio-infinity6c

Conversation

@widgetii

@widgetii widgetii commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

The three GPIO subcommands and the possible-IR-cut-GPIO report hint have been
HiSilicon-only since they exist: get_chip_gpio_adress() has no SigmaStar
entry, so on SigmaStar gpio get/gpio set answered "Platform is not
supported" and the board report silently dropped the hint. This adds
Infinity6C (SSC37X), from the vendor's own per-pad register layout.

Registers

HiSilicon banks eight pins behind a data word and a direction word. Infinity6C
gives every pad one byte: bit 0 the pin level, bit 1 the output value, bit 2
the output enable, which reads 1 while the pad drives Hi-Z (an input) and 0
while it drives. A pad's byte sits at 0x1F207C00 + 2 * riu_off, and the
table is the one the vendor driver carries (drivers/sstar/gpio/infinity6c/ mhal_gpio.c, GPL-2) — including its two holes, at pads 24 and 42, because
the RIU offset is not linear in the pad number. The four PAD_ETH_* pads the
vendor appends by hand sit outside the gpiochip range and stay out. 82 pads,
numbered exactly as the kernel's gpiochip numbers them, so sysfs and gpio get describe the same wire.

Commands

  • gpio get/set <pad> take the plain pad number — there is no 5_6 spelling
    to parse, nothing is banked — and print the same mux line the HiSilicon path
    prints.
  • gpio scan prints one Pad: baseline line per GPIO-muxed pad, decoded into
    in/out/oe_n, then runs the same 100 ms diff loop as HiSilicon.
  • gpio_possible_ircut() keeps both of its rules with pads in place of
    groups: a mapped streamer trims the guess to a board whose whole pad list
    holds at most two driving pads, and the fallback stays "every pad currently
    driving low".

SigmaStar families other than infinity6c keep today's behaviour; their tables
slot into the same shape when someone measures them.

Review round

Four Qodo findings, all taken — and three of the four had a HiSilicon twin,
because the SigmaStar paths were written to match the HiSilicon ones. Those
are fixed where the code now shares one place:

  • A malformed level is refused, not written. strtoul reads every
    non-number as 0, and 0 is a level gpio set really writes, so
    gpio set 12 foo used to switch the pad to an output and drive it low.
    parse_gpio_level() — end pointer, errno, whole string, 0 or 1 — refuses
    it before any register bit moves, on both vendors' set paths.
  • A /dev/mem mapping counts from its full interval, not only from the
    window its offset starts in: a daemon holding one broad RIU window from
    below holds the GPIO registers all the same. The math lives in
    gpio_windows_in_mapping() (unit-tested, including the broad-window
    shape) and both the group walk and the single-window question go through
    it.
  • PID 1 is visited. The /proc walk takes every digit-named pid — init
    running the streamer is a real shape on minimal firmware.
  • gpio scan stops at the first failing read instead of retrying every
    100 ms and logging the same register forever. Same fix on both vendors.

Two reginfo_test blocks pin the level parser and the window math: the
refusals are "", foo, 1x, 0x1, 2, -1 and the ERANGE overflow, the
windows are the exact fit, the start-below overlap, the adjacent page that
must not count, and the SigmaStar call shape (one window, ends not
page-aligned).

Verification

  • reginfo_test pins the measured addresses — pad 0 at 0x1F207C00, 12 at
    0x1F207C30, 23 at 0x1F207C5C, 30 at 0x1F207C7C, 41 at 0x1F207CA8,
    42 at 0x1F207CC4, 81 at 0x1F207D60 — so an address edit that silently
    moves the table fails the sweep (monotonic, 4-byte aligned, in range)
    instead of a camera.
  • Clean builds in all four vendor configurations (all, sstar only,
    none, and the musl cross-toolchain), no new warnings; reginfo_test and
    tools/test_pipeline.sh pass.
  • Live on an SSC37X board (Anjoy MTF45, SSC027A-S01A): gpio get 12 and an
    independent byte-mapped /dev/mem reader agree at 0x1F207C30; gpio set 12 0 moves that byte 0x5b -> 0x58; after the review round gpio set 12 foo refuses with the byte untouched (0x58 before and after) while valid
    sets still move it; the scan baseline lists the 42 GPIO-muxed pads the mux
    tables predict; the board report carries
    possible-IR-cut-GPIO: 0,1,2,3,4,5,11,12,30,32,80 before and after the
    walk fix (no process on this board maps /dev/mem, so the fallback rule is
    what it exercises) — checked with a build whose report omits the sensor
    sweep, which on this vendor firmware stalls against the live streamer for
    minutes and is not a regression of this patch.
  • The HiSilicon twins are not live-verified this round: the lab Hisilicon
    cameras did not answer (hung at PoE trickle; a bounce did not revive
    them), so those two lines rest on the shared code path, the unit tests on
    the math, and inspection. Happy to drive them when a board is back.
  • docs/gpio.md updated: the numbering paragraph and the IR-cut hint rules
    now describe the SigmaStar shape alongside the HiSilicon one.

Infinity6C gives every pad a one-byte register (bit 0 level, bit 1 output
value, bit 2 output enable, 1 = Hi-Z) at 0x1F207C00 + 2 * riu_off, per the
vendor mhal_gpio table, with the two holes in it kept: the RIU offset is not
linear in the pad number (pad 24, pad 42). The four vendor-only PAD_ETH_*
pads sit outside the gpiochip and stay out. gpio get/set take the plain pad
number and print the same mux line as the HiSilicon path; gpio scan prints
one Pad: line per GPIO pad with in/out/oe_n decoded; the
possible-IR-cut-GPIO heuristic keeps both of its rules with pads in place
of groups. Addresses measured on a live SSC37X board and pinned by
reginfo_test.
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add Infinity6C GPIO commands and IR-cut detection

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Add Infinity6C per-pad GPIO access for get, set, and scan commands.
• Extend IR-cut candidate detection to SigmaStar GPIO pads.
• Validate register mappings and document SigmaStar numbering and scan semantics.
Diagram

graph TD
  CLI["GPIO commands"] --> Handlers["Command handlers"] --> HAL["SigmaStar GPIO HAL"] --> RIU[("Infinity6C RIU")]
  Report["Board report"] --> Heuristic["IR-cut heuristic"] --> HAL
  Handlers --> Padmux["Pad-mux backend"]
  Heuristic --> Padmux
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Introduce a generic GPIO backend interface
  • ➕ Would isolate both HiSilicon and SigmaStar register models behind common operations.
  • ➕ Would simplify adding further SigmaStar families and future GPIO layouts.
  • ➖ Requires a broader refactor of established HiSilicon command and heuristic paths.
  • ➖ Increases regression risk and review scope for the first non-HiSilicon implementation.
2. Compute addresses with explicit hole adjustments
  • ➕ Would reduce the size of the address table.
  • ➕ Could express mostly linear register spacing compactly.
  • ➖ Would obscure correspondence with the vendor-maintained pad table.
  • ➖ Is easier to break when additional non-linear offsets or families are added.

Recommendation: Keep the PR's dedicated SigmaStar HAL and explicit vendor-derived address table. It cleanly contains the hardware-specific access model and preserves non-linear offsets; a common GPIO backend interface is better deferred until another family provides enough evidence for a stable abstraction.

Files changed (6) +379 / -1

Enhancement (3) +321 / -0
sstar_gpio.cImplement Infinity6C per-pad GPIO register access +86/-0

Implement Infinity6C per-pad GPIO register access

• Defines the 82-pad physical register map, including non-linear holes, and limits support to Infinity6C. Provides bounded 16-bit read and read-modify-write helpers that preserve the unmapped slot's upper byte.

src/hal/sstar_gpio.c

sstar_gpio.hDeclare SigmaStar GPIO register semantics and API +26/-0

Declare SigmaStar GPIO register semantics and API

• Defines input, output, and active-low output-enable bits. Exposes chip support, pad count, address lookup, and per-pad access functions.

src/hal/sstar_gpio.h

reginfo.cRoute GPIO commands and IR-cut detection through SigmaStar pads +209/-0

Route GPIO commands and IR-cut detection through SigmaStar pads

• Adds Infinity6C implementations for GPIO get, set, and continuous scan using linear pad numbers. Extends IR-cut candidate reporting with pad-mux filtering, streamer mapping detection, output-count limits, and low-output fallback behavior.

src/reginfo.c

Tests (1) +40 / -0
reginfo_test.cVerify the Infinity6C GPIO register table +40/-0

Verify the Infinity6C GPIO register table

• Checks support detection, the 82-pad count, measured landmark addresses, alignment, ordering, and range. Also verifies unsupported-chip and invalid-address behavior.

src/reginfo_test.c

Documentation (1) +13 / -0
gpio.mdDocument SigmaStar pad numbering and IR-cut behavior +13/-0

Document SigmaStar pad numbering and IR-cut behavior

• Explains SigmaStar's linear gpiochip numbering and per-pad scan output fields. Documents how the existing IR-cut rules translate from HiSilicon groups to SigmaStar pads.

docs/gpio.md

Other (1) +5 / -1
CMakeLists.txtInclude the SigmaStar GPIO HAL in applicable builds +5/-1

Include the SigmaStar GPIO HAL in applicable builds

• Adds the new GPIO source and header to the full executable source set. Vendor-filtered library builds include them only when SigmaStar support is selected.

CMakeLists.txt

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Invalid levels drive pads low ✓ Resolved 🐞 Bug ≡ Correctness
Description
sstar_gpio_getset() parses the requested level with strtoul() but never verifies that any digits
were consumed or that the entire argument was valid. A command such as gpio set 12 foo is
consequently accepted as level zero, enables output mode, and writes a low level to the physical
pad.
Code

src/reginfo.c[R3832-3834]

+        unsigned level = strtoul(argv[2], NULL, 10);
+        if (level > 1)
+            return EXIT_FAILURE;
Evidence
The new SigmaStar branch performs an unchecked conversion and then directly clears output-enable and
output-value bits. The command documentation limits the argument to a value used to drive the pad,
so malformed text must not become a hardware write.

src/reginfo.c[3831-3845]
docs/gpio.md[15-24]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
SigmaStar GPIO set accepts malformed level strings as zero and can unexpectedly drive a physical pad low.
## Fix Focus Areas
- src/reginfo.c[3831-3842]
## Recommended Fix
Parse the level with an end pointer, reset and inspect `errno`, and reject the argument unless exactly one digit was consumed, the entire string ended, and the value is either zero or one before changing any register bits.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Broad mappings weaken the filter hint ✓ Resolved 🐞 Bug ≡ Correctness
Description
sstar_gpio_streamer_mapped() compares only a mapping's starting file offset with the GPIO page
rather than testing whether the mapping's full physical interval overlaps it. A daemon mapping a
broader RIU window from below 0x1f207000 is classified as absent even when that mapping contains
every pad register, switching the report to the less selective low-output fallback.
Code

src/reginfo.c[R3795-3798]

+            unsigned long offset;
+            if (sscanf(line, "%*x-%*x %*s %lx", &offset) != 1)
+                continue;
+            if (offset >= base && offset < end) {
Evidence
The detector parses only the /dev/mem file offset and requires that starting value to lie inside
the GPIO range. The repository's own memory accessor demonstrates a valid mapping pattern that
starts at a 64-KiB boundary below the requested register and spans forward over it.

src/reginfo.c[3772-3798]
src/tools.c[72-82]
src/hal/sstar_gpio.c[28-42]
src/reginfo.c[3886-3898]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Streamer detection misses broad `/dev/mem` mappings whose physical range covers the GPIO registers but begins below their page.
## Fix Focus Areas
- src/reginfo.c[3791-3798]
## Recommended Fix
Parse the mapping's virtual start and end along with its file offset, derive the mapped physical interval from its length, and report the streamer when that interval overlaps the GPIO register interval.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Init streamers lose one filter pad ✓ Resolved 🐞 Bug ≡ Correctness
Description
sstar_gpio_streamer_mapped() rejects every /proc entry whose first character is below 2, which
unconditionally excludes PID 1. When the camera streamer runs as init and drives an opposite-level
filter pair, its mapping is ignored and the fallback reports only the member currently driven low.
Code

src/reginfo.c[R3781-3783]

+    while ((ent = readdir(proc))) {
+        if (ent->d_name[0] < '2' || ent->d_name[0] > '9')
+            continue;
Evidence
The directory filter explicitly starts accepted process names at character 2, and only accepted
entries have their maps examined. The resulting boolean selects between returning all lightly loaded
output pads and returning only outputs currently low.

src/reginfo.c[3780-3789]
src/reginfo.c[3886-3898]
docs/gpio.md[121-124]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The process scan excludes PID 1, so a streamer running as init cannot activate the mapped-streamer filter heuristic.
## Fix Focus Areas
- src/reginfo.c[3780-3787]
## Recommended Fix
Accept every directory name that consists entirely of decimal digits, including `1`, before opening its maps file.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Scan failures flood logs forever ✓ Resolved 🐞 Bug ☼ Reliability
Description
sstar_gpio_scan_cmd() handles a polling read failure by breaking only the inner pad loop while its
outer infinite loop remains active. Once register access persistently fails, the command retries
every 100 milliseconds and repeatedly emits the same error instead of returning failure.
Code

src/reginfo.c[R3944-3946]

+            if (!sstar_gpio_read(pad, &val)) {
+                fprintf(stderr, "Error at %#x\n", sstar_gpio_pad_addr(pad));
+                break;
Evidence
The read-error branch breaks only the for loop, after which execution sleeps and immediately
enters the enclosing while (1) again. The initial baseline path already treats the same read
failure as fatal, establishing the expected command behavior.

src/reginfo.c[3923-3930]
src/reginfo.c[3939-3962]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A register read failure during SigmaStar polling leaves the scan running indefinitely and repeatedly logging the error.
## Fix Focus Areas
- src/reginfo.c[3939-3962]
## Recommended Fix
Return `EXIT_FAILURE` when a polling read fails, or propagate an explicit failure flag out of both loops and terminate the command after logging once.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/reginfo.c Outdated
Comment thread src/reginfo.c Outdated
Comment thread src/reginfo.c Outdated
Comment thread src/reginfo.c Outdated
widgetii and others added 2 commits September 25, 2026 17:32
Three of the four review findings had a HiSilicon twin, because the SigmaStar
paths were written against the HiSilicon ones, and are fixed in the one place
both share:

- The level a `gpio set` writes is parsed strictly now. strtoul reads every
  string that is not a number as 0, and 0 is a level the command really
  writes, so `gpio set 12 foo` used to switch the pad to an output and drive
  it low. Both vendors' set paths refused after this fix only.

- A /dev/mem mapping counts from its full interval, not only from the page its
  offset names: a daemon holding one broad RIU window from below held the GPIO
  registers all the same, and the walk now knows. The interval math is
  gpio_windows_in_mapping(), pinned by tests; both the HiSilicon group walk and
  the SigmaStar single-window question go through it.

- The /proc walk visits every digit-named pid, including init. PID 1 running
  the streamer is a real shape.

- `gpio scan` on either vendor now exits at the first register read that
  fails, instead of retrying every 100 ms and logging the same error forever
  while watching nothing.

Verified on the SSC37X board: `gpio set 12 foo` refuses with the pad byte
at 0x1f207c30 untouched (0x58 before and after), valid sets still move it
0x58/0x5b, and the board report's possible-IR-cut-GPIO is the same eleven
pads as before the walk was fixed. The HiSilicon twins are not live-verified
this round -- the lab HiSilicon cameras did not answer.
mem_reg() keeps its last /dev/mem window mapped, and the SigmaStar
IR-cut hint reads every pad before asking the walk whether a daemon
holds the pads. The walk visited every digit-named pid, ipctool's own
included, so it always found the window it had just used, always
answered "streamer", and on any board with more than two driving pads
the hint was dropped. The fallback rule could never run. Skip getpid().

The window the hint asks about now comes from sstar_gpio_window(), and
reginfo_test checks that call -- 0x1F207000 + 0xD64 -- instead of a
stand-in with a different base and length.
@openipc-ai

Copy link
Copy Markdown
Contributor

Review fix: the /proc walk found ipctool's own /dev/mem window

mem_reg() keeps its last /dev/mem window mapped, and dev_mem_windows_marked() visited every digit-named pid, including ipctool's own. The SigmaStar IR-cut hint reads pads just before the walk. So whenever the last register access before the walk landed in the 0x1F200000 window, ipctool counted itself as the streamer, and on any board with more than two driving pads the hint was dropped. The walk now skips getpid(). This also fixes the same latent self-match on the HiSilicon path.

The failure depended on the board. The last register access is the pad-mux check of pad 81, plus its GPIO read only when pad 81 is GPIO-muxed. When pad 81 is not GPIO-muxed, the cached window is 0x1F000000 and the bug stays hidden.

reginfo_test also used a stand-in base and length for the SigmaStar window (0x1F207C00/0x1D64), which did not match the real call. The window now comes from sstar_gpio_window(), and the test checks that function's 0x1F207000 + 0xD64.

Live A/B on an SSC37X lab camera (OpenIPC 2.6.09.23, kernel 5.10.61, majestic running; majestic maps only 0x1F000000+64K, so no daemon holds the pads):

case PR head this commit
as found (pad 81 on SAR_MODE_0) 0,1,2,3,4,5,32 0,1,2,3,4,5,32
pad 81 temporarily muxed to GPIO (Hi-Z input, restored afterwards) no hint 0,1,2,3,4,5,32
a helper process holds /dev/mem @ 0x1F207000 no hint no hint (streamer rule, as designed)

Also on that board, with this commit: gpio get 32 returns the level stored in its register byte. gpio set 32 foo and gpio set 32 2 are refused and the byte stays 0x0A58. gpio set 32 0 is accepted. gpio get 99 is refused. The gpio scan baseline shows the same 58 pads as PR head.

Native builds with IPCHW_VENDORS=all|sstar|none and the arm32 musl cross build all pass, as do reginfo_test, cYAML_test and tools/test_pipeline.sh.

@openipc-ai
openipc-ai merged commit ac57899 into master Sep 25, 2026
5 checks passed
@openipc-ai
openipc-ai deleted the sstar-gpio-infinity6c branch September 25, 2026 18:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants