Skip to content

load_*: stop a failed sensor detection latching the camera off for good - #2441

Merged
openipc-ai merged 2 commits into
masterfrom
sensor-detect-no-latch
Sep 19, 2026
Merged

openipc-ai merged 2 commits into
masterfrom
sensor-detect-no-latch

Conversation

@openipc-ai

@openipc-ai openipc-ai commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Problem

A camera whose sensor is not detected on the first boot never detects it
again
, no matter what is fixed afterwards.

The reporter of #2428 hit this on a GK7202V500. Their log catches the exact
moment:

goke: Get data from ipcinfo and set SENSOR as unknown
goke: Writing unknown to U-Boot ENV
goke: SENSOR is not detected, aborting...

The detect path stores whatever it found, and unknown is a value like any
other. Every subsequent boot takes the branch above it:

if fw_printenv -n sensor >/dev/null; then
        export SENSOR=$(fw_printenv -n sensor)
        logger ... "Get data from environment and set SENSOR as ${SENSOR}"
else
        insert_detect
        SENSOR_DETECT=$(ipcinfo --short-sensor)
        export SENSOR=${SENSOR_DETECT:=unknown}
        ...
        fw_setenv sensor $SENSOR && logger ...
fi

sensor exists, so it is read back as unknown and the script aborts —
detection never runs again. Reseating a ribbon cable, fixing a bus,
shipping the missing driver: none of it helps, because nothing looks a second
time. And the second boot's log is indistinguishable from a camera that
genuinely has no sensor, so there is nothing to tell the owner that a stale
value is the reason.

The recovery exists (fw_setenv sensor <name>, or deleting the variable) but is
undiscoverable from the symptom.

What changes

Two things, and the second is the one that matters on a real camera.

Do not persist a failed detection. The detect path is only reached when the
variable was unset, so declining to write leaves it unset and the next boot
probes again.

Clear a stale unknown before anything reads it. This is the half that
recovers a camera already latched by older firmware, and it has to happen early:
/etc/init.d/rcS line 2 is

export SENSOR=$(fw_printenv -n sensor)

so by the time S70vendor runs load_<vendor> -i, a latched camera already has
SENSOR=unknown in its environment. It takes the [ -n "$SENSOR" ] branch, logs
the sentinel as a manually-set sensor, and never reaches anything below. A guard
placed in the else is unreachable on exactly the cameras it is for — which is
what the first version of this PR did, and what the Qodo review on #2442 caught.
The clear now runs ahead of that test and blanks the shell variable as well as
the stored one.

load_ingenic has always done this correctly — on a miss it runs
unset SENSOR; fw_setenv sensor. Those six scripts are untouched; the other
thirteen get the same behaviour.

Not in scope, deliberately: the four load_sigmastar scripts. They cannot
latch, because on a failed probe ipcinfo -s prints nothing to stdout
(src/main.c writes No sensor detected to stderr and returns EXIT_FAILURE),
so SENSOR is empty and their [ -z "$SENSOR" ] gate re-detects. The literal
string unknown only exists where a loader supplies it itself via
${SENSOR_DETECT:=unknown}, which sigmastar has no equivalent of.

Hardware tested on

No physical camera. The half that is real hardware is the failure: the
GK7202V500 log in #2428 shows Writing unknown to U-Boot ENV, and that camera
never probing again afterwards.

For the fix itself this is shell logic with no device interaction — it decides
whether to call fw_setenv — so it is verified by executing the shipped block
with the surrounding calls stubbed.

Evidence

The block is lifted verbatim from the shipped load_goke (master vs this
branch), run under busybox ash with fw_printenv/fw_setenv backed by a file
and ipcinfo stubbed. SENSOR is exported from that file first, the way rcS
does
— the first version of this harness set it by hand instead, which is
precisely why it certified a fix that could not work. detection_runs counts
ipcinfo invocations.

A. latched camera (sensor=unknown stored), sensor now detectable
   before: boot1 SENSOR=unknown detection_runs=0 env=[sensor=unknown]
           boot2 SENSOR=unknown detection_runs=0 env=[sensor=unknown]
   after:  boot1 SENSOR=sc2336  detection_runs=1 env=[sensor=sc2336]
           boot2 SENSOR=sc2336  detection_runs=1 env=[sensor=sc2336]

B. fresh camera, undetectable on boot1, detectable on boot2
   before: boot1 SENSOR=unknown detection_runs=1 env=[sensor=unknown]
           boot2 SENSOR=unknown detection_runs=1 env=[sensor=unknown]
   after:  boot1 SENSOR=unknown detection_runs=1 env=[]
           boot2 SENSOR=sc2336  detection_runs=2 env=[sensor=sc2336]

C. working camera, sensor already stored  (the no-regression case)
   before: boot1 SENSOR=sc2336 detection_runs=0 env=[sensor=sc2336]
   after:  boot1 SENSOR=sc2336 detection_runs=0 env=[sensor=sc2336]

A is the case the first version got wrong: detection_runs=0 on every boot,
before — the old code does not record a wrong answer, it stops asking the
question. C is byte-identical either side.

Shell gates:

$ STRICT=1 bash .github/scripts/test_shell_parse.sh
checked 147 shell script(s) / all parsed clean under busybox ash
$ STRICT=1 bash .github/scripts/test_strip_shell_comments.sh
ok   147 shipped scripts parse identically after stripping

Scope

Thirteen load_<vendor> scripts across hisilicon, goke, grainmedia and novatek.
That is a wide reach for one change, so what it can do is worth being precise
about: it removes a write. A camera that detects its sensor is byte-identical in
behaviour; a camera that does not is today latched off permanently and
afterwards retries. There is no path by which this makes a working camera stop
working.

  • No kernel patches under general/package/all-patches/linux/
  • No files specific to a single retail camera model
  • No probing or bring-up tooling
  • Nothing under general/overlay/ or in a shared load_<vendor> script hardcodes a value specific to my board — this removes a value being written, and adds none
  • Package sources come from an OpenIPC repository, and any version bump keeps at least the specificity of the pin it replaces
  • No LD_PRELOAD, and no binaries that cannot be rebuilt from source
  • New code is selected by a defconfig, so CI actually builds it

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

Copy link
Copy Markdown

PR Summary by Qodo

Retry sensor detection after an unsuccessful boot

🐞 Bug fix 🕐 20-40 Minutes

Grey Divider

AI Description

• Leave failed sensor detections unset so subsequent boots probe again.
• Preserve successful sensor caching across 13 Goke, GrainMedia, HiSilicon, and Novatek loaders.
Diagram

graph TD
  A["Camera boot"] --> B{"Sensor cached?"}
  B -->|"Yes"| C["Load cached"]
  B -->|"No"| D["Run detection"] --> E{"Sensor found?"}
  E -->|"Yes"| F["Persist sensor"]
  E -->|"No"| G["Leave unset"]
  G -.->|"Next boot"| A
Loading
High-Level Assessment

The guarded write is the best scoped fix: the environment variable is already absent on this path, so skipping persistence avoids unnecessary flash writes and naturally enables retries. Explicitly clearing the variable, as Ingenic does, or extracting shared detection logic would add writes or cross-package refactoring without improving this behavior.

Files changed (13) +117 / -13

Bug fix (13) +117 / -13
load_gokeAvoid caching failed GK7205V200 sensor detection +9/-1

Avoid caching failed GK7205V200 sensor detection

• Guards the U-Boot environment write so only successfully detected sensors are persisted. An unknown result remains unset and can be retried on a later boot.

general/package/goke-osdrv-gk7205v200/files/script/load_goke

load_gokeAvoid caching failed GK7205V500 sensor detection +9/-1

Avoid caching failed GK7205V500 sensor detection

• Prevents the Goke loader from writing 'unknown' to the sensor environment variable. Successful detections continue to be cached normally.

general/package/goke-osdrv-gk7205v500/files/script/load_goke

load_grainmediaRetry failed GrainMedia sensor detection on later boots +9/-1

Retry failed GrainMedia sensor detection on later boots

• Persists the detected sensor only when 'ipcinfo' returns a valid name. Failed detection no longer creates a stale U-Boot value that bypasses future probes.

general/package/grainmedia-osdrv-gm8136/files/script/load_grainmedia

load_hisiliconAvoid caching unknown HI3516AV100 sensors +9/-1

Avoid caching unknown HI3516AV100 sensors

• Adds a validity guard around sensor persistence. Unknown detections remain absent from U-Boot environment so later boots can retry.

general/package/hisilicon-osdrv-hi3516av100/files/script/load_hisilicon

load_hisiliconAvoid caching unknown HI3516CV100 sensors +9/-1

Avoid caching unknown HI3516CV100 sensors

• Stops failed sensor detection from being persisted as 'unknown'. Valid sensor names retain the existing caching behavior.

general/package/hisilicon-osdrv-hi3516cv100/files/script/load_hisilicon

load_hisiliconAvoid caching unknown HI3516CV200 sensors +9/-1

Avoid caching unknown HI3516CV200 sensors

• Writes the sensor environment variable only after successful detection. This allows recovery from temporary sensor or driver failures on subsequent boots.

general/package/hisilicon-osdrv-hi3516cv200/files/script/load_hisilicon

load_hisiliconAvoid caching unknown HI3516CV300 sensors +9/-1

Avoid caching unknown HI3516CV300 sensors

• Prevents 'unknown' from entering persistent U-Boot state while preserving valid sensor caching. Future boots can rerun detection after a miss.

general/package/hisilicon-osdrv-hi3516cv300/files/script/load_hisilicon

load_hisiliconAvoid caching unknown HI3516CV500 sensors +9/-1

Avoid caching unknown HI3516CV500 sensors

• Guards sensor persistence against the 'unknown' fallback. A failed first boot therefore does not permanently suppress detection.

general/package/hisilicon-osdrv-hi3516cv500/files/script/load_hisilicon

load_hisiliconAvoid caching unknown HI3516CV6xx sensors +9/-1

Avoid caching unknown HI3516CV6xx sensors

• Leaves the sensor environment variable unset when detection fails. Successful results are still written and reused by later boots.

general/package/hisilicon-osdrv-hi3516cv6xx/files/script/load_hisilicon

load_hisiliconAvoid caching unknown HI3516EV200 sensors +9/-1

Avoid caching unknown HI3516EV200 sensors

• Adds a successful-detection check before writing U-Boot state. Temporary detection failures can now recover without manually clearing the environment.

general/package/hisilicon-osdrv-hi3516ev200/files/script/load_hisilicon

load_hisiliconAvoid caching unknown HI3519DV500 sensors +9/-1

Avoid caching unknown HI3519DV500 sensors

• Skips persistence when sensor detection resolves to 'unknown'. The loader will probe again on the next boot because no cached variable exists.

general/package/hisilicon-osdrv-hi3519dv500/files/script/load_hisilicon

load_hisiliconAvoid caching unknown HI3519V101 sensors +9/-1

Avoid caching unknown HI3519V101 sensors

• Prevents unsuccessful sensor detection from becoming permanent U-Boot state. Valid detections remain cached as before.

general/package/hisilicon-osdrv-hi3519v101/files/script/load_hisilicon

load_novatekRetry failed Novatek sensor detection on later boots +9/-1

Retry failed Novatek sensor detection on later boots

• Restricts U-Boot sensor writes to successful detection results. An unknown result stays transient, allowing the Novatek loader to probe again.

general/package/novatek-osdrv-nt9856x/files/script/load_novatek

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

qodo-free-for-open-source-projects Bot commented Sep 18, 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. Camera recovery remains unverified ✓ Resolved 📘 Rule violation ☼ Reliability
Description
load_goke now skips fw_setenv when SENSOR is unknown, changing boot-time sensor recovery
without a real-camera run. The PR explicitly states that no physical camera was used, while this
shipped branch determines whether later boots probe for the sensor again.
Code

general/package/goke-osdrv-gk7205v500/files/script/load_goke[R288-290]

+			if [ "$SENSOR" != "unknown" ]; then
+				fw_setenv sensor $SENSOR && logger -s -p daemon.info -t goke "Writing ${SENSOR} to U-Boot ENV"
+			fi
Evidence
PR Compliance ID 1 requires real-camera evidence when a change can alter firmware behavior and
explicitly treats a statement that hardware testing did not occur as a failure. The cited branch
changes persistent environment handling that controls whether sensor detection runs on subsequent
camera boots, while the PR description says No physical camera.

Rule 1: Hardware evidence is present and honest
general/package/goke-osdrv-gk7205v500/files/script/load_goke[288-290]

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 sensor retry behavior changes on shipped camera images, but the PR provides only a stubbed shell test for the fix and explicitly states that no physical camera was used.
## Fix Focus Areas
- general/package/goke-osdrv-gk7205v500/files/script/load_goke[288-290]
## Recommended Fix
Run the changed image on an affected camera and add real before-and-after boot output showing an initial failed detection followed by successful detection after the underlying sensor issue is corrected.

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


2. Upgraded cameras remain latched off ✓ Resolved 🐞 Bug ≡ Correctness
Description
load_goke and the other modified loaders only apply the new persistence guard inside the branch
reached when sensor is absent, while an existing sensor=unknown still satisfies fw_printenv
and bypasses detection. Cameras latched by the old firmware therefore continue loading the stale
sentinel after upgrade, reaching the existing abort paths without invoking ipcinfo.
Code

general/package/goke-osdrv-gk7205v200/files/script/load_goke[R476-477]

+			if [ "$SENSOR" != "unknown" ]; then
+				fw_setenv sensor $SENSOR && logger -s -p daemon.info -t goke "Writing ${SENSOR} to U-Boot ENV"
Evidence
Every affected loader checks only whether the environment variable exists before selecting the
cached-value branch; detection appears exclusively in the corresponding else branch. The existing
Goke abort then rejects the cached unknown, and neither startup nor upgrade processing clears the
stale variable, proving that installing this change does not recover already affected cameras.

general/package/goke-osdrv-gk7205v200/files/script/load_goke[461-466]
general/package/goke-osdrv-gk7205v200/files/script/load_goke[476-477]
general/package/goke-osdrv-gk7205v200/files/script/load_goke[487-488]
general/package/goke-osdrv-gk7205v500/files/script/load_goke[273-279]
general/package/grainmedia-osdrv-gm8136/files/script/load_grainmedia[27-33]
general/package/hisilicon-osdrv-hi3516av100/files/script/load_hisilicon[459-466]
general/package/hisilicon-osdrv-hi3516cv300/files/script/load_hisilicon[587-594]
general/package/novatek-osdrv-nt9856x/files/script/load_novatek[165-172]
general/overlay/etc/init.d/rcS[2-3]
general/overlay/usr/sbin/sysupgrade[1227-1245]

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

## Issue description
Devices upgraded from old firmware may already have `sensor=unknown` in U-Boot. Every modified loader still accepts that cached value before reaching the new write guard, so these devices never rerun sensor detection.
## Fix Focus Areas
- general/package/goke-osdrv-gk7205v200/files/script/load_goke[461-477]
- general/package/goke-osdrv-gk7205v500/files/script/load_goke[273-289]
- general/package/grainmedia-osdrv-gm8136/files/script/load_grainmedia[27-42]
- general/package/hisilicon-osdrv-hi3516av100/files/script/load_hisilicon[459-476]
- general/package/hisilicon-osdrv-hi3516cv100/files/script/load_hisilicon[442-459]
- general/package/hisilicon-osdrv-hi3516cv200/files/script/load_hisilicon[496-513]
- general/package/hisilicon-osdrv-hi3516cv300/files/script/load_hisilicon[587-604]
- general/package/hisilicon-osdrv-hi3516cv500/files/script/load_hisilicon[400-417]
- general/package/hisilicon-osdrv-hi3516cv6xx/files/script/load_hisilicon[355-372]
- general/package/hisilicon-osdrv-hi3516ev200/files/script/load_hisilicon[400-417]
- general/package/hisilicon-osdrv-hi3519dv500/files/script/load_hisilicon[355-372]
- general/package/hisilicon-osdrv-hi3519v101/files/script/load_hisilicon[901-918]
- general/package/novatek-osdrv-nt9856x/files/script/load_novatek[165-181]
## Recommended Fix
Read the cached sensor value before choosing the branch and treat an absent, empty, or `unknown` value as a cache miss. Clear a cached `unknown` value and run the normal detection path, preserving reuse of valid cached sensors and persisting only successful detections.

ⓘ 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 describe a rule in plain language on the Rules page and Qodo drafts it for you

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread general/package/goke-osdrv-gk7205v500/files/script/load_goke
Comment thread general/package/goke-osdrv-gk7205v200/files/script/load_goke
A camera whose sensor is not detected on first boot never detects it again,
however the underlying problem is fixed. Reported from a GK7202V500 in #2428,
whose log shows the moment it happens:

    goke: Get data from ipcinfo and set SENSOR as unknown
    goke: Writing unknown to U-Boot ENV
    goke: SENSOR is not detected, aborting...

The detect path stores whatever it found, and `unknown` is a value like any
other. Every later boot takes the branch above it --

    if fw_printenv -n sensor >/dev/null; then
            export SENSOR=$(fw_printenv -n sensor)

-- reads `unknown` back, and aborts without running detection at all. Reseating
the ribbon cable, fixing the bus, adding the missing driver: none of it helps,
because nothing looks again. Nothing in the log says why, either; the second
boot's output is identical to a camera that genuinely has no sensor.

Guarding the write is the whole fix. This path is only reached when the
variable was unset, so not writing leaves it unset and the next boot probes
again.

load_ingenic has always done this correctly -- on a miss it runs `unset SENSOR;
fw_setenv sensor` and clears the variable rather than storing the miss -- so
the six ingenic scripts are already right and are left alone. The other
thirteen get the same behaviour by not writing at all.
The previous attempt at this put the recovery inside the branch reached when
SENSOR is empty, which on a real camera is the one branch that never runs.
/etc/init.d/rcS line 2 is

    export SENSOR=$(fw_printenv -n sensor)

so by the time S70vendor calls load_<vendor> -i, an already-latched camera has
SENSOR=unknown in its environment. The loader takes

    if [ -n "$SENSOR" ]; then
            logger ... "SENSOR: ${SENSOR}"

logs the sentinel as a manually-set sensor, and the recovery below it is
unreachable. The fix was inert on exactly the cameras it was written for.

Clearing before that test, and blanking the shell variable as well as the
stored one, puts the camera back on the detection path.

Verified with the shipped block run under busybox ash, with fw_printenv /
fw_setenv backed by a file and -- this time -- SENSOR exported from that file
first, the way rcS does. The earlier harness set SENSOR= by hand, which is
precisely why it reported a fix that could not work:

    A. latched camera, sensor now detectable
       before: boot1 SENSOR=unknown detection_runs=0 env=[sensor=unknown]
       after:  boot1 SENSOR=sc2336  detection_runs=1 env=[sensor=sc2336]

    B. fresh camera, undetectable then detectable
       before: boot2 SENSOR=unknown detection_runs=1 env=[sensor=unknown]
       after:  boot2 SENSOR=sc2336  detection_runs=2 env=[sensor=sc2336]

    C. working camera, sensor already stored
       before: boot1 SENSOR=sc2336 detection_runs=0 env=[sensor=sc2336]
       after:  boot1 SENSOR=sc2336 detection_runs=0 env=[sensor=sc2336]

C is the no-regression case and is byte-identical either side.

Found by the Qodo review on #2442, which reviewed this branch's code against
that PR.
@openipc-ai
openipc-ai force-pushed the sensor-detect-no-latch branch from 0dc3351 to fdd6d00 Compare September 19, 2026 04:34
@openipc-ai
openipc-ai merged commit 9fece81 into master Sep 19, 2026
65 checks passed
@openipc-ai
openipc-ai deleted the sensor-detect-no-latch branch September 19, 2026 05:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant