diff --git a/Runner/plans/video_pre-merge.yaml b/Runner/plans/video_pre-merge.yaml index cd97182f..827b9d02 100755 --- a/Runner/plans/video_pre-merge.yaml +++ b/Runner/plans/video_pre-merge.yaml @@ -1,55 +1,55 @@ metadata: format: Lava-Test Test Definition 1.0 - name: Video_V4L2_All - description: "Single YAML: pass TARGET, STACK, iris path and downstream FW; run.sh handles the rest" + name: Video_V4L2_With_Secrets + description: "Run Video V4L2 runner; use LAVA secrets for Wi-Fi; auto-args for kodiak vs lemans/monaco; run base & overlay" maintainer: - smuppand@qti.qualcomm.com os: - openembedded scope: - functional - + run: steps: - cd Runner - export REPO_ROOT="$PWD" - - # --- Job-level inputs (set per device/job) --- - - export TARGET="${TARGET:-}" # e.g. Kodiak|LeMans|Monaco|Talos - - export STACK="${STACK:-both}" # base|overlay|auto|both or synonyms: upstream|downstream - - export IRIS_BIN="${IRIS_BIN:-}" # optional: /path/to/iris_v4l2_test - - export DOWNSTREAM_FW="${DOWNSTREAM_FW:-}" # optional: firmware blob for overlay - - # Normalize STACK -> MODE (one-liner synonyms) - - export MODE_NORM="$(printf '%s' "$STACK" | tr '[:upper:]' '[:lower:]' | sed -e 's/^upstream$/base/' -e 's/^downstream$/overlay/')" - - | - case "$MODE_NORM" in base|overlay|auto|both) : ;; *) MODE_NORM="both" ;; esac - echo "TARGET=$TARGET STACK=$STACK MODE_NORM=$MODE_NORM" - - # Build args for run.sh (match your CLI exactly) + - export TARGET="${TARGET:-}" # expected values: kodiak | lemans | monaco (case-insensitive) + + # Read Wi-Fi credentials from LAVA-provided secrets (exported as env vars in the job) + - export SSID="${LAVA_WIFI_SSID:-}" + - export PASSWORD="${LAVA_WIFI_PASSWORD:-}" + + # Build args for run.sh based on TARGET; always pass secrets; add downstream FW only for kodiak - | + RPATH="$REPO_ROOT/suites/Multimedia/Video/Video_V4L2_Runner/run.sh" + RESFILE="$REPO_ROOT/suites/Multimedia/Video/Video_V4L2_Runner/Video_V4L2_Runner.res" ARGS="" - [ -n "$TARGET" ] && ARGS="$ARGS --target \"$TARGET\"" - - if [ -n "$IRIS_BIN" ]; then - ARGS="$ARGS --iris-bin \"$IRIS_BIN\"" - else - IB="$(command -v iris_v4l2_test 2>/dev/null || true)" - [ -n "$IB" ] && ARGS="$ARGS --iris-bin \"$IB\"" - fi - - [ -n "$DOWNSTREAM_FW" ] && ARGS="$ARGS --downstream-fw \"$DOWNSTREAM_FW\"" - - echo "ARGS=$ARGS" - - # Single call: run.sh handles base/overlay/auto and 'both' via its reexec shim - - | - cd "$REPO_ROOT/suites/Multimedia/Video/Video_V4L2_Runner" + TL="$(printf '%s' "${TARGET:-}" | tr '[:upper:]' '[:lower:]')" + case "$TL" in + kodiak) + ARGS="$ARGS --platform kodiak --app /data/vendor/iris_test_app/iris_v4l2_test --ssid \"$SSID\" --password \"$PASSWORD\" --downstream-fw /data/vendor/iris_test_app/vpu20_p1_gen2.mbn" + ;; + lemans) + ARGS="$ARGS --platform lemans --ssid \"$SSID\" --password \"$PASSWORD\"" --app /data/vendor/iris_test_app/iris_v4l2_test + ;; + monaco) + ARGS="$ARGS --platform monaco --ssid \"$SSID\" --password \"$PASSWORD\"" --app /data/vendor/iris_test_app/iris_v4l2_test + ;; + *) + # Unknown or not provided: still pass secrets; platform autodetect in run.sh + ARGS="$ARGS --ssid \"$SSID\" --password \"$PASSWORD\"" + ;; + esac + echo "TARGET=${TARGET:-unset} ARGS=$ARGS" + + # Run base (upstream) then overlay (downstream) + # shellcheck disable=SC2086 + sh -lc "$RPATH --stack base $ARGS" || true + "$REPO_ROOT/utils/send-to-lava.sh" "$RESFILE" || true + # shellcheck disable=SC2086 - sh -lc "./run.sh $ARGS --mode \"$MODE_NORM\"" || true - - # Report result (AUSanity style) - - "$REPO_ROOT/utils/send-to-lava.sh" "$REPO_ROOT/suites/Multimedia/Video/Video_V4L2_Runner/Video_V4L2_Runner.res" || true - - # Optional roll-up (ignored if absent) - - "$REPO_ROOT/utils/result_parse.sh" || true + sh -lc "$RPATH --stack overlay $ARGS" || true + "$REPO_ROOT/utils/send-to-lava.sh" "$RESFILE" || true + + # Optional roll-up (ignored if absent) + "$REPO_ROOT/utils/result_parse.sh" || true diff --git a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/README_Video.md b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/README_Video.md index 2fe82573..aa53fd16 100644 --- a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/README_Video.md +++ b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/README_Video.md @@ -37,6 +37,16 @@ The suite includes a **reboot-free video stack switcher** (upstream ↔ downstre - **CLI parity** `--stack both` is supported to run the suite twice in one invocation (BASE/upstream pass then OVERLAY/downstream pass). +- **NEW (opt‑in) custom module sources** + You can now point the runner at alternative module locations without disturbing the default flow. If you **do nothing**, behavior is unchanged. + - `--ko-dir DIR[:DIR2:...]` — search these dir(s) for `.ko*` files when resolving modules. + - `--ko-tree ROOT` — use `modprobe -d ROOT` (expects `ROOT/lib/modules/$(uname -r)`). + - `--ko-tar FILE.tar[.gz|.xz|.zst]` — unpack once under `/run/iris_mods/$KVER`; auto-derives a `--ko-tree` or `--ko-dir`. + - `--ko-prefer-custom` — prefer custom sources before the system tree. + - The loader now logs **path resolution** and **load method** lines, e.g.: + - `resolve-path: qcom_iris via KO_DIRS => /data/kos/qcom_iris.ko` + - `load-path: modprobe(system): qcom_iris` / `load-path: insmod: /tmp/qcom_iris.ko` + --- ## Features @@ -53,6 +63,7 @@ The suite includes a **reboot-free video stack switcher** (upstream ↔ downstre - **Kodiak firmware live swap** with backup/restore helpers - **udev refresh + prune** of stale device nodes - **Waits/retries/sleeps** integrated across networking, downloads, module ops, and app launches (see next section) +- **(Opt‑in)** custom module sources with **non-exported** CLI flags (`--ko-*`); defaults remain untouched --- @@ -68,8 +79,8 @@ These are **environment variables** (not user‑visible CLI flags) so your LAVA | `VIDEO_APP_LAUNCH_SLEEP` | `1` | Sleep (seconds) right before launching `iris_v4l2_test` for each case. | | `VIDEO_INTER_TEST_SLEEP` | `1` | Sleep (seconds) between cases to allow device/udev to settle. | -> Notes -> - If download **stalls** or the system clock is invalid for TLS, the runner re-checks network health and treats it as **offline** → decode cases **SKIP** (not FAIL). +> Notes +> - If download **stalls** or the system clock is invalid for TLS, the runner re-checks network health and treats it as **offline** → decode cases **SKIP** (not FAIL). > - Module management includes small internal waits (e.g., `modprobe -r` retry after 200ms, 1s delays around remoteproc/module reloads). These are built‑in, no extra env required. --- @@ -138,6 +149,12 @@ cd /Runner | `--stack auto|upstream|downstream|base|overlay|up|down|both` | Select target stack (use `both` for BASE→OVERLAY two-pass) | | `--platform lemans|monaco|kodiak` | Force platform (else auto-detect) | | `--downstream-fw PATH` | **Kodiak**: path to DS firmware (e.g. `vpu20_1v.mbn`) | +| `--ko-dir DIR[:DIR2:...]` | *(Opt‑in)* Additional directories to search for `.ko*` files during resolution | +| `--ko-tree ROOT` | *(Opt‑in)* Use `modprobe -d ROOT` (expects `ROOT/lib/modules/$(uname -r)`) | +| `--ko-tar FILE.tar[.gz|.xz|.zst]` | *(Opt‑in)* Unpack once into `/run/iris_mods/$KVER`; auto-derives `--ko-tree` or `--ko-dir` | +| `--ko-prefer-custom` | *(Opt‑in)* Prefer custom module sources (KO_DIRS/KO_TREE) before system | + +> **Default remains unchanged.** If you omit all `--ko-*` flags, the runner uses the system module tree and `modinfo`/`modprobe` resolution only. --- @@ -288,6 +305,34 @@ export VIDEO_INTER_TEST_SLEEP=3 ./run.sh --stack upstream ``` +### (Opt‑in) Use custom module sources +**Default behavior is unchanged.** Only use these when you want to test modules from a non-system location. + +#### Use a prepared tree (modprobe -d) +```sh +./run.sh --ko-tree /opt/custom-kmods --stack upstream +``` + +#### Search one or more directories of loose .ko files +```sh +./run.sh --ko-dir /data/kos:/mnt/usb/venus_kos --stack downstream +``` + +#### Prefer custom before system +```sh +./run.sh --ko-dir /sdcard/kos --ko-prefer-custom --stack upstream +``` + +#### Unpack a tarball of modules and auto-wire paths +```sh +./run.sh --ko-tar /sdcard/iris_kmods_${KVER}.tar.xz --stack upstream +# The runner unpacks into /run/iris_mods/$KVER and derives --ko-tree or --ko-dir. +``` + +> While resolving and loading modules, the runner logs lines like: +> - `resolve-path: venus_core via KO_TREE => /run/iris_mods/6.9.0/lib/modules/6.9.0/venus_core.ko` +> - `load-path: insmod: /data/kos/qcom_iris.ko` or `load-path: modprobe(system): qcom_iris` + --- ## Troubleshooting @@ -304,4 +349,5 @@ export VIDEO_INTER_TEST_SLEEP=3 - **Download fails** Ensure time is sane (TLS), network is reachable, and provide Wi‑Fi creds via env or `ssid_list.txt`. The downloader uses BusyBox‑compatible flags with retries and a final TLS‑lenient attempt if needed. When the network remains unreachable, the runner **SKIPs** decode cases. ---- \ No newline at end of file +--- + diff --git a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/run.sh b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/run.sh index f9959daa..043f7d1f 100755 --- a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/run.sh +++ b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/run.sh @@ -25,6 +25,7 @@ if [ -z "$INIT_ENV" ]; then fi # Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. if [ -z "${__INIT_ENV_LOADED:-}" ]; then # shellcheck disable=SC1090 . "$INIT_ENV" @@ -62,6 +63,16 @@ if [ -z "${REPEAT_POLICY:-}" ]; then REPEAT_POLICY="all"; fi JUNIT_OUT="" VERBOSE="0" +# --- Stabilizers (opt-in) --- +RETRY_ON_FAIL="0" # extra attempts after a FAIL +POST_TEST_SLEEP="0" # settle time after each case + +# --- Custom module source (opt-in; default is untouched) --- +KO_DIRS="" # colon-separated list of dirs that contain .ko files +KO_TREE="" # alt root that has lib/modules/$KVER +KO_TARBALL="" # optional tarball that we unpack once +KO_PREFER_CUSTOM="0" # 1 = try custom first; default 0 = system first + if [ -z "${VIDEO_STACK:-}" ]; then VIDEO_STACK="auto"; fi if [ -z "${VIDEO_PLATFORM:-}" ]; then VIDEO_PLATFORM=""; fi if [ -z "${VIDEO_FW_DS:-}" ]; then VIDEO_FW_DS=""; fi @@ -94,8 +105,15 @@ Usage: $0 [--config path.json|/path/dir] [--dir DIR] [--pattern GLOB] [--downstream-fw PATH] [--force] [--app /path/to/iris_v4l2_test] [--ssid SSID] [--password PASS] + [--ko-dir DIR[:DIR2:...]] # opt-in: search these dirs for .ko on failure + [--ko-tree ROOT] # opt-in: modprobe -d ROOT (expects lib/modules/\$(uname -r)) + [--ko-tar FILE.tar[.gz|.xz]] # opt-in: unpack once under /run/iris_mods/\$KVER, set --ko-tree/--ko-dir accordingly + [--ko-prefer-custom] # opt-in: try custom sources before system [--app-launch-sleep S] [--inter-test-sleep S] [--log-flavor NAME] # internal: e.g. upstream or downstream (used by --stack both) + # --- Stabilizers --- + [--retry-on-fail N] # retry up to N times if a case ends FAIL + [--post-test-sleep S] # sleep S seconds after each case EOF } @@ -190,6 +208,22 @@ while [ $# -gt 0 ]; do shift PASSWORD="$1" ;; + --ko-dir) + shift + KO_DIRS="$1" + ;; + --ko-tree) + shift + KO_TREE="$1" + ;; + --ko-tar) + shift + KO_TARBALL="$1" + ;; + --ko-prefer-custom) + KO_PREFER_CUSTOM="1" + ;; + --app-launch-sleep) shift APP_LAUNCH_SLEEP="$1" @@ -202,6 +236,15 @@ while [ $# -gt 0 ]; do shift LOG_FLAVOR="$1" ;; + # --- Stabilizers --- + --retry-on-fail) + shift + RETRY_ON_FAIL="$1" + ;; + --post-test-sleep) + shift + POST_TEST_SLEEP="$1" + ;; --help|-h) usage exit 0 @@ -234,6 +277,44 @@ export INTER_TEST_SLEEP # --- EARLY dependency check (bail out fast) --- # Ensure the app is executable if a path was provided but lacks +x + +# --- Optional: unpack a custom module tarball **once** (no env exports) --- +KVER="$(uname -r 2>/dev/null || printf '%s' unknown)" +if [ -n "$KO_TARBALL" ] && [ -f "$KO_TARBALL" ]; then + DEST="/run/iris_mods/$KVER" + if [ ! -d "$DEST" ]; then + mkdir -p "$DEST" 2>/dev/null || true + case "$KO_TARBALL" in + *.tar|*.tar.gz|*.tgz|*.tar.xz|*.txz|*.tar.zst) + if command -v tar >/dev/null 2>&1; then + # best-effort; keep extraction bounded to DEST + tar -xf "$KO_TARBALL" -C "$DEST" 2>/dev/null || true + fi + ;; + *) + # not a tar? treat as a directory if user passed one by mistake + : + ;; + esac + fi + # decide whether the tar contained a full tree or loose .ko’s + if [ -d "$DEST/lib/modules/$KVER" ]; then + KO_TREE="$DEST" + else + # find first dir that has at least one .ko (bounded depth) + first_ko_dir="$(find "$DEST" -type f -name '*.ko*' -maxdepth 3 2>/dev/null | head -n1 | xargs -r dirname)" + if [ -n "$first_ko_dir" ]; then + if [ -n "$KO_DIRS" ]; then + KO_DIRS="$first_ko_dir:$KO_DIRS" + else + KO_DIRS="$first_ko_dir" + fi + fi + fi + # quiet summary (only if user opted-in) + log_info "Custom module source prepared (tree='${KO_TREE:-none}', dirs='${KO_DIRS:-none}', prefer_custom=$KO_PREFER_CUSTOM)" +fi + if [ -n "$VIDEO_APP" ] && [ -f "$VIDEO_APP" ] && [ ! -x "$VIDEO_APP" ]; then chmod +x "$VIDEO_APP" 2>/dev/null || true if [ ! -x "$VIDEO_APP" ]; then @@ -497,6 +578,14 @@ if [ "${VIDEO_STACK}" = "both" ]; then args="$args --inter-test-sleep $(printf %s "$INTER_TEST_SLEEP")" fi + # --- Stabilizers passthrough --- + if [ -n "${RETRY_ON_FAIL:-}" ]; then + args="$args --retry-on-fail $(printf %s "$RETRY_ON_FAIL")" + fi + if [ -n "${POST_TEST_SLEEP:-}" ]; then + args="$args --post-test-sleep $(printf %s "$POST_TEST_SLEEP")" + fi + printf "%s" "$args" } @@ -564,6 +653,15 @@ log_info "APP=$VIDEO_APP" if [ -n "$VIDEO_FW_DS" ]; then log_info "Downstream FW override: $VIDEO_FW_DS" fi +if [ -n "$KO_TREE$KO_DIRS$KO_TARBALL" ]; then + # print only when user actually provided custom sources + if [ -n "$KO_TREE" ]; then + log_info "Custom module tree (modprobe -d): $KO_TREE" + fi + if [ -n "$KO_DIRS" ]; then + log_info "Custom ko dir(s): $KO_DIRS (prefer_custom=$KO_PREFER_CUSTOM)" + fi +fi if [ -n "$VIDEO_FW_BACKUP_DIR" ]; then log_info "FW backup override: $VIDEO_FW_BACKUP_DIR" fi @@ -1019,6 +1117,38 @@ while IFS= read -r cfg; do fi fi + # (2) Retry on final failure (extra attempts outside REPEAT loop, before recording results) + if [ "$final" = "FAIL" ] && [ "$RETRY_ON_FAIL" -gt 0 ] 2>/dev/null; then + r=1 + log_info "[$id] RETRY_ON_FAIL: up to $RETRY_ON_FAIL additional attempt(s)" + while [ "$r" -le "$RETRY_ON_FAIL" ]; do + # optional delay between retries, reuse REPEAT_DELAY for consistency + if [ "$REPEAT_DELAY" -gt 0 ] 2>/dev/null; then + sleep "$REPEAT_DELAY" + fi + + log_info "[$id] retry attempt $r/$RETRY_ON_FAIL" + if video_run_once "$cfg" "$logf" "$TIMEOUT" "$SUCCESS_RE" "$LOGLEVEL"; then + pass_runs=$((pass_runs + 1)) + final="PASS" + log_pass "[$id] RETRY succeeded — marking PASS" + break + else + # capture latest rc marker for visibility + rc_val="$(awk -F'=' '/^END-RUN rc=/{print $2}' "$logf" 2>/dev/null | tail -n1 | tr -d ' ')" + if [ -n "$rc_val" ]; then + case "$rc_val" in + 139) log_warn "[$id] Retry exited rc=139 (SIGSEGV)." ;; + 134) log_warn "[$id] Retry exited rc=134 (SIGABRT)." ;; + 137) log_warn "[$id] Retry exited rc=137 (SIGKILL/OOM?)." ;; + *) : ;; + esac + fi + fi + r=$((r + 1)) + done + fi + { printf 'RESULT id=%s mode=%s pretty="%s" final=%s pass_runs=%s fail_runs=%s elapsed=%s\n' \ "$id" "$mode" "$pretty" "$final" "$pass_runs" "$fail_runs" "$elapsed" @@ -1052,6 +1182,15 @@ while IFS= read -r cfg; do fi fi + # (6) Post-test settle sleep + case "$POST_TEST_SLEEP" in + ''|*[!0-9]* ) + : + ;; + 0) : ;; + *) log_info "Post-test sleep ${POST_TEST_SLEEP}s"; sleep "$POST_TEST_SLEEP" ;; + esac + if [ "$MAX" -gt 0 ] && [ "$total" -ge "$MAX" ]; then log_info "Reached MAX=$MAX tests; stopping" break diff --git a/Runner/utils/lib_video.sh b/Runner/utils/lib_video.sh index b75791a5..f27d8f00 100755 --- a/Runner/utils/lib_video.sh +++ b/Runner/utils/lib_video.sh @@ -26,6 +26,12 @@ VENUS_ENC_MOD="venus_enc" # Session-only blocks live under /run/modprobe.d RUNTIME_BLOCK_DIR="/run/modprobe.d" +# Optional custom module sources (set by run.sh only when user opts in) +# NOTE: Leaving these unset keeps the exact existing behavior. +KO_DIRS="${KO_DIRS:-}" # colon-separated dirs containing .ko files +KO_TREE="${KO_TREE:-}" # alt root containing lib/modules/$(uname -r) +KO_PREFER_CUSTOM="${KO_PREFER_CUSTOM:-0}" # 1 = prefer KO_DIRS before system tree + # Firmware path for Kodiak downstream blob FW_PATH_KODIAK="/lib/firmware/qcom/vpu/vpu20_p1_gen2.mbn" : "${FW_BACKUP_DIR:=/opt}" @@ -132,12 +138,21 @@ video_insmod_with_deps() { if ! "$MODPROBE" -q "$d" 2>/dev/null; then dpath="$(video_find_module_file "$d")" || dpath="" if [ -n "$dpath" ] && [ -f "$dpath" ]; then - insmod "$dpath" 2>/dev/null || true + if insmod "$dpath" 2>/dev/null; then + video_log_load_success "$d" dep-insmod "$dpath" + else + log_warn "dep insmod failed for $dpath" + fi + else + log_warn "dep resolve failed for $d" fi + else + video_log_load_success "$d" dep-modprobe fi done if insmod "$path" 2>/dev/null; then + video_log_load_success "$m" insmod "$path" return 0 fi @@ -171,6 +186,9 @@ video_list_runtime_blocks() { fi } +# ------------------------------------------------------------------------- +# Path resolution & load logging helpers +# ------------------------------------------------------------------------- video_dump_stack_state() { when="$1" # pre|post @@ -188,44 +206,149 @@ video_dump_stack_state() { video_list_runtime_blocks } +video_log_resolve() { + # usage: video_log_resolve + # how: modinfo | system-tree | updates-tree | altroot-tree | ko-dir + m="$1"; how="$2"; p="$3" + case "$how" in + modinfo) + log_info "resolve-path: $m via modinfo -n => $p" + ;; + system-tree) + log_info "resolve-path: $m via /lib/modules => $p" + ;; + updates-tree) + log_info "resolve-path: $m via /lib/modules/*/updates => $p" + ;; + altroot-tree) + log_info "resolve-path: $m via KO_TREE => $p" + ;; + ko-dir) + log_info "resolve-path: $m via KO_DIRS => $p" + ;; + esac +} + +video_log_load_success() { + # usage: video_log_load_success [extra] + # how: modprobe-system | modprobe-altroot | insmod | dep-modprobe | dep-insmod + m="$1"; how="$2"; extra="$3" + case "$how" in + modprobe-system) + log_info "load-path: modprobe(system): $m" + ;; + modprobe-altroot) + log_info "load-path: modprobe(altroot=$KO_TREE): $m" + ;; + insmod) + # extra = path + log_info "load-path: insmod: $extra" + ;; + dep-modprobe) + log_info "load-path(dep): modprobe(system): $m" + ;; + dep-insmod) + # extra = path + log_info "load-path(dep): insmod: $extra" + ;; + esac +} + video_find_module_file() { # Resolve a module file path for a logical mod name (handles _ vs -). # Prefers: modinfo -n, then .../updates/, then general search. m="$1" kr="$(uname -r 2>/dev/null)" - + if [ -z "$kr" ]; then kr="$(find /lib/modules -mindepth 1 -maxdepth 1 -type d -printf '%f\n' 2>/dev/null | head -n1)" fi - + if video_exist_cmd modinfo; then p="$(modinfo -n "$m" 2>/dev/null)" if [ -n "$p" ] && [ -f "$p" ]; then + video_log_resolve "$m" modinfo "$p" printf '%s\n' "$p" return 0 fi fi - + m_us="$m" m_hy="$(printf '%s' "$m" | tr '_' '-')" m_alt="$(printf '%s' "$m" | tr '-' '_')" - + + # Helper to scan KO_DIRS (bounded search) + scan_ko_dirs() { + modbase="$1" # without .ko* + if [ -z "$KO_DIRS" ]; then + return 1 + fi + OLD_IFS="$IFS" + IFS=':' + for d in $KO_DIRS; do + IFS="$OLD_IFS" + if [ -z "$d" ] || [ ! -d "$d" ]; then + continue + fi + p="$(find "$d" -maxdepth 3 -type f -name "${modbase}.ko*" -print -quit 2>/dev/null)" + if [ -n "$p" ]; then + video_log_resolve "$m" ko-dir "$p" + printf '%s\n' "$p" + return 0 + fi + done + IFS="$OLD_IFS" + return 1 + } + + # Optional order: prefer KO_DIRS first if requested + if [ "$KO_PREFER_CUSTOM" -eq 1 ] 2>/dev/null; then + for pat in "$m_us" "$m_hy" "$m_alt"; do + if scan_ko_dirs "$pat"; then + return 0 + fi + done + fi + + # Optional altroot tree (KO_TREE) first, if provided + if [ -n "$KO_TREE" ] && [ -d "$KO_TREE" ]; then + for pat in "$m_us" "$m_hy" "$m_alt"; do + p="$(find "$KO_TREE/lib/modules/$kr" -type f -name "${pat}.ko*" -print -quit 2>/dev/null)" + if [ -n "$p" ]; then + video_log_resolve "$m" altroot-tree "$p" + printf '%s\n' "$p" + return 0 + fi + done + fi + for pat in "$m_us" "$m_hy" "$m_alt"; do p="$(find "/lib/modules/$kr/updates" -type f -name "${pat}.ko*" 2>/dev/null | head -n 1)" if [ -n "$p" ]; then + video_log_resolve "$m" updates-tree "$p" printf '%s\n' "$p" return 0 fi done - + for pat in "$m_us" "$m_hy" "$m_alt"; do p="$(find "/lib/modules/$kr" -type f -name "${pat}.ko*" 2>/dev/null | head -n 1)" if [ -n "$p" ]; then + video_log_resolve "$m" system-tree "$p" printf '%s\n' "$p" return 0 fi done - + + # If not preferred-first, still try KO_DIRS at the end + if [ "$KO_PREFER_CUSTOM" -ne 1 ] 2>/dev/null; then + for pat in "$m_us" "$m_hy" "$m_alt"; do + if scan_ko_dirs "$pat"; then + return 0 + fi + done + fi + return 1 } @@ -359,16 +482,24 @@ video_retry_modprobe() { while [ "$i" -lt "$n" ]; do i=$((i+1)) - log_info "modprobe attempt $i/$n: $m" if video_has_module_loaded "$m"; then log_info "module became present before attempt $i: $m" return 0 fi - if "$MODPROBE" "$m" 2>/dev/null; then - log_info "modprobe succeeded on attempt $i: $m" - return 0 + if [ -n "$KO_TREE" ] && [ -d "$KO_TREE" ]; then + log_info "modprobe attempt $i/$n (altroot=$KO_TREE): $m" + if "$MODPROBE" -d "$KO_TREE" "$m" 2>/dev/null; then + video_log_load_success "$m" modprobe-altroot + return 0 + fi + else + log_info "modprobe attempt $i/$n (system): $m" + if "$MODPROBE" "$m" 2>/dev/null; then + video_log_load_success "$m" modprobe-system + return 0 + fi fi video_usleep "${MOD_RETRY_SLEEP}" @@ -602,8 +733,8 @@ video_stack_status() { case "$plat" in lemans|monaco) # Upstream accepted if: - # - pure upstream build: qcom_iris present and iris_vpu absent - # - base+overlay build: qcom_iris and iris_vpu both present + # - pure upstream build: qcom_iris present and iris_vpu absent + # - base+overlay build: qcom_iris and iris_vpu both present if video_has_module_loaded qcom_iris && ! video_has_module_loaded iris_vpu; then printf '%s\n' "upstream" return 0 @@ -623,8 +754,8 @@ video_stack_status() { kodiak) # Upstream accepted if: - # - Venus trio present (canonical upstream on Kodiak), OR - # - pure upstream build: qcom_iris present and iris_vpu absent + # - Venus trio present (canonical upstream on Kodiak), OR + # - pure upstream build: qcom_iris present and iris_vpu absent if video_has_module_loaded venus_core && video_has_module_loaded venus_dec && video_has_module_loaded venus_enc; then printf '%s\n' "upstream" return 0 @@ -787,7 +918,7 @@ video_hot_switch_modules() { # Entry point: ensure desired stack # ----------------------------------------------------------------------------- video_ensure_stack() { - want_raw="$1" # upstream|downstream|auto|base|overlay|up|down + want_raw="$1" # upstream|downstream|auto|base|overlay|up|down plat="$2" if [ -z "$plat" ]; then @@ -814,9 +945,9 @@ video_ensure_stack() { # ---------------------------------------------------------------------- # Early no-op: if current state already equals desired, do NOT hot switch. # This covers: - # - Build #1 (pure upstream: qcom_iris only) on lemans/monaco/kodiak - # - Build #2 (base+overlay: qcom_iris + iris_vpu) when upstream is requested - # - Downstream already active (e.g., iris_vpu only on lemans/monaco, or kodiak downstream) + # - Build #1 (pure upstream: qcom_iris only) on lemans/monaco/kodiak + # - Build #2 (base+overlay: qcom_iris + iris_vpu) when upstream is requested + # - Downstream already active (e.g., iris_vpu only on lemans/monaco, or kodiak downstream) # Still allow Kodiak downstream FW swap without touching modules. # ---------------------------------------------------------------------- cur_state="$(video_stack_status "$plat")"