diff --git a/Runner/config/pkg_command_map.conf b/Runner/config/pkg_command_map.conf index 1da8577ce..b2aa8271c 100755 --- a/Runner/config/pkg_command_map.conf +++ b/Runner/config/pkg_command_map.conf @@ -103,16 +103,10 @@ ubuntu:benchmark_model=tensorflow-lite-qcom-apps debian:qnn-net-run=qnn-tools ubuntu:qnn-net-run=qnn-tools -# On general-purpose distributions, audio asset download is recovered only -# when AudioPlayback needs a remote clip and neither curl nor wget is already -# available. curl is sufficient because the downloader retains wget as an -# image-provided fallback. -debian:package-set:audio-download=curl -ubuntu:package-set:audio-download=curl -centos:package-set:audio-download=curl +# Audio asset downloads use only image-provided curl or wget clients. # --------------------------------------------------------------------------- -# Ubuntu Bluetooth userspace stack. +# Bluetooth userspace stack and firmware. # # Ubuntu Server installs this package set before running the current Bluetooth # validation tests. Ubuntu Desktop uses its image-provided stack and skips @@ -120,9 +114,13 @@ centos:package-set:audio-download=curl # # PipeWire, WirePlumber, and libspa-0.2-bluetooth provide the BlueZ media # endpoint/profile integration validated as part of the Ubuntu BT stack. +# CentOS uses BlueZ from BaseOS and Qualcomm/Atheros firmware from the +# atheros-firmware package. The package set is intentionally minimal because +# hciconfig is not shipped by CentOS Stream 10 and is optional to the tests. # --------------------------------------------------------------------------- ubuntu:package-set:bluetooth=bluez pipewire wireplumber libspa-0.2-bluetooth alsa-utils rfkill expect +centos:package-set:bluetooth=bluez atheros-firmware expect # --------------------------------------------------------------------------- # Weston compositor and example-client mappings. @@ -220,6 +218,24 @@ rpm:gst-launch-1.0=gstreamer1.0-tools rpm:gst-inspect-1.0=gstreamer1.0-tools rpm:efivar=efivar +# CentOS Stream 10 Bluetooth command ownership. Keep these OS-specific so +# Yocto RPM images retain their existing package policy. hciconfig and hcitool +# are deprecated BlueZ tools and are not shipped by CentOS Stream 10 +# BaseOS/AppStream/CRB, so the helpers use sysfs and bluetoothctl instead. +# l2ping is an optional fallback supplied by bluez-libs-devel from CRB. +centos:bluetoothctl=bluez +centos:bluetoothd=bluez +centos:btattach=bluez +centos:btmgmt=bluez +centos:btmon=bluez +centos:l2ping=bluez-libs-devel +centos:rfkill=util-linux +centos:expect=expect +centos:pgrep=procps-ng +centos:killall=psmisc +centos:lsmod=kmod +centos:modprobe=kmod + # --------------------------------------------------------------------------- # Common opkg provider mappings. # Applies to opkg-based images. @@ -280,11 +296,13 @@ ubuntu:package-set:fastrpc=fastrpc-support fastrpc-tests debian:package-set:fastcv-runtime=qcom-fastcv-binaries libfastcvopt-dev ubuntu:package-set:fastcv-runtime=qcom-fastcv-binaries libfastcvopt-dev -centos:package-set:fastcv-rpm-prerequisites=epel-release centos:package-set:fastcv-runtime=qcom-fastcv-binaries libfastcvopt-devel -rhel:package-set:fastcv-rpm-prerequisites=epel-release rhel:package-set:fastcv-runtime=qcom-fastcv-binaries libfastcvopt-devel +# Shared prerequisite for explicit Qualcomm RPM package preparation. +centos:package-set:qualcomm-rpm-prerequisites=epel-release +rhel:package-set:qualcomm-rpm-prerequisites=epel-release + # --------------------------------------------------------------------------- # Qualcomm MinkIPC runtime and test packages. # @@ -350,13 +368,15 @@ ubuntu:package-set:urm=userspace-resource-manager userspace-resource-manager-tes # for example: # ./run.sh --overlay # -# Currently available from qli-staging. Once promoted to qli, only the source -# selection in the caller needs to change. The package-set name remains graphics. +# Debian packages are currently available from qli-staging. Once promoted to +# qli, only the source selection in the caller needs to change. CentOS packages +# use the Qualcomm production RPM repositories. # # DKMS packages are skipped by lib_pkg_provider.sh when matching kernel headers # are unavailable. Remaining userspace packages continue to install. # -# No qcom-distro/rpm mapping is provided, so Yocto remains no-op. +# CentOS Stream 10 uses the verified Qualcomm RPM package names below. +# No qcom-distro mapping is provided, so Yocto remains no-op. # --------------------------------------------------------------------------- # Qualcomm proprietary graphics overlay package set. @@ -371,16 +391,19 @@ ubuntu:package-set:graphics-base=libegl-mesa0 libgl1-mesa-dri libgbm1 libegl1 li # Qualcomm proprietary graphics overlay package set for Ubuntu. ubuntu:package-set:graphics=kgsl-dkms adreno-common libgbm-msm adreno-gles1 adreno-gles2 adreno-egl1 adreno-vulkan-icd adreno-opencl-icd adreno-opencl-dev +# CentOS Stream 10 Qualcomm graphics overlay. gbm-msm-backend is listed +# explicitly because graphics validation checks the installed GBM backend. +centos:package-set:graphics=gbm-msm-backend adreno-egl1 adreno-gles2 adreno-opencl-icd adreno-vulkan-icd + # --------------------------------------------------------------------------- # Audio validation package recovery. # -# Debian Audio tests use the upstream base Audio stack by default. -# Qualcomm AudioReach packages are installed only when --overlay is explicitly -# requested. +# Debian and CentOS Audio tests use the base Audio stack by default. Qualcomm +# AudioReach packages are installed only when --overlay is explicitly requested. # -# No rpm, opkg, qcom-distro, or generic provider package-set mapping is -# provided. Yocto and other embedded images therefore continue using their -# image-provided Audio stack without package recovery. +# CentOS Stream 10 uses the verified Qualcomm RPM package names below. No opkg, +# qcom-distro, or generic provider package-set mapping is provided, so Yocto +# and other embedded images continue using their image-provided Audio stack. # --------------------------------------------------------------------------- # Base Debian Audio stack. @@ -397,6 +420,9 @@ ubuntu:package-set:audio-desktop=pipewire pipewire-pulse wireplumber pipewire-bi # This package set is complete by itself because overlay mode checks only # "audio"; it does not check "audio-base" first. debian:package-set:audio=alsa-utils pipewire-bin wireplumber pulseaudio-utils audioreach-pipewire-plugin audioreach-kernel-dkms audioreach-config + +# CentOS Stream 10 Qualcomm AudioReach overlay. +centos:package-set:audio=audioreach-dkms audioreach-pal audioreach-pipewire-plugin # Command recovery used by check_dependencies()/pkg_ensure_command(). apt:aplay=alsa-utils @@ -409,6 +435,9 @@ apt:pw-record=pipewire-bin apt:pactl=pulseaudio-utils apt:paplay=pulseaudio-utils apt:parecord=pulseaudio-utils +rpm:pw-play=pipewire-utils +rpm:pw-record=pipewire-utils +rpm:pw-cat=pipewire-utils # --------------------------------------------------------------------------- # Qualcomm Sensors runtime and test packages. @@ -463,4 +492,11 @@ centos:Sensors:ssc_drva_test=qcom-sensors-test-core qcom-sensors-test-apps debian:package-set:camera-nhx=camx-dkms camx-glymur libcamx-glymur1 camx-firmware-glymur camx-nhx ubuntu:package-set:camera-nhx=camx-dkms camx-glymur libcamx-glymur1 camx-firmware-glymur camx-nhx -centos:package-set:camera-nhx=camx-dkms camx-glymur libcamx-glymur1 camx-firmware-glymur camx-nhx +centos:package-set:camera-nhx=camx-dkms camx-firmware-kodiak camx-kodiak libcamx-kodiak1 + +# Qualcomm Iris video overlay. Debian and Ubuntu use the public DKMS package, +# whose post-install flow builds, registers, and loads iris_vpu. CentOS uses +# the equivalent RPM package and scriptlet. +debian:package-set:video-overlay=iris-vpu-dkms +ubuntu:package-set:video-overlay=iris-vpu-dkms +centos:package-set:video-overlay=iris-vpu diff --git a/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/run.sh b/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/run.sh index b96a83b9c..80c6e49c0 100755 --- a/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/run.sh +++ b/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/run.sh @@ -113,15 +113,19 @@ WARN_COUNT=0 log_info "------------------------------------------------------------" log_info "Starting $TESTNAME" -if ! bt_prepare_ubuntu_stack; then - test_result_finish "FAIL" "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" +if ! bt_prepare_bluetooth_stack; then + test_result_finish "FAIL" "$TESTNAME FAIL - Bluetooth stack preparation failed" fi -log_info "Checking dependencies: bluetoothctl hciconfig lsmod" -if ! check_dependencies bluetoothctl hciconfig lsmod; then +log_info "Checking dependencies: bluetoothctl lsmod" +if ! check_dependencies bluetoothctl lsmod; then test_result_finish "SKIP" "$TESTNAME SKIP - required Bluetooth tools are unavailable" fi +if ! command -v hciconfig >/dev/null 2>&1; then + log_info "Optional hciconfig command is unavailable, using sysfs and bluetoothctl diagnostics" +fi + # ---------- Bluetooth runtime check/ readiness ---------- log_info "Waiting up to ${BT_RUNTIME_READY_WAIT}s for a usable Bluetooth runtime" if bt_ensure_runtime_ready \ @@ -136,7 +140,9 @@ if bt_ensure_runtime_ready \ test_result_record "PASS" "Bluetooth runtime exposed an HCI adapter with a valid BD address" fi else - test_result_record "FAIL" "Bluetooth runtime remained unusable after bounded recovery attempts" + test_result_record \ + "FAIL" \ + "${BT_RUNTIME_FAILURE_REASON:-Bluetooth runtime remained unusable after bounded recovery attempts}" fi ADAPTER="$BT_RUNTIME_READY_ADAPTER" @@ -181,7 +187,7 @@ fi if fw_dir="$(btfwpresent 2>/dev/null)"; then test_result_record "PASS" "Firmware present in: $fw_dir" else - log_warn "No BT firmware matching msbtfw*/msnv* or cmbtfw*/cmnv* found under standard firmware paths." + log_warn "No supported Qualcomm Bluetooth firmware or NVM file found under standard firmware paths" WARN_COUNT=$((WARN_COUNT + 1)) fi @@ -248,7 +254,9 @@ if command -v btfwloaded >/dev/null 2>&1; then log_warn "No retained BT firmware-load signature found, but BT runtime state is healthy." WARN_COUNT=$((WARN_COUNT + 1)) else - test_result_record "FAIL" "Firmware load/setup is incomplete and Bluetooth runtime state is unhealthy" + test_result_record \ + "FAIL" \ + "Firmware load/setup is incomplete and Bluetooth runtime is unhealthy, verify the image firmware package and reboot after provisioning new firmware" fi ;; esac @@ -316,7 +324,9 @@ if [ -n "$ADAPTER" ]; then if btbdok "$ADAPTER"; then test_result_record "PASS" "BD address is valid for $ADAPTER" else - test_result_record "FAIL" "BD address is invalid or all zeros for $ADAPTER" + test_result_record \ + "FAIL" \ + "BD address is invalid or all zeros for $ADAPTER, verify Qualcomm firmware and platform BD-address provisioning" fi fi @@ -341,8 +351,7 @@ log_info "=== hciconfig -a (if available) ===" if command -v hciconfig >/dev/null 2>&1; then hciconfig -a || true else - log_warn "hciconfig command not available." - WARN_COUNT=$((WARN_COUNT + 1)) + log_info "Optional hciconfig command is unavailable" fi log_info "=== bluetoothctl list (controllers) ===" diff --git a/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/README.md b/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/README.md index 2c7ebedcc..f016812ea 100644 --- a/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/README.md +++ b/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/README.md @@ -25,6 +25,11 @@ Select an adapter and tune the retry delays: The command-line adapter overrides `BT_ADAPTER`. When neither is set, the shared Bluetooth helper selects a usable runtime controller. +On CentOS, direct execution verifies the minimal `bluez`, `atheros-firmware`, +and `expect` package set before runtime validation. Existing complete images +avoid package-manager and network work. Debian and Ubuntu retain their existing +preparation behavior, while Yocto continues to use image-provided components. + ## Validation contract The test uses the exact BlueZ `Powered: yes|no` property as its power-state @@ -46,6 +51,12 @@ Power ON completed with consecutive Powered=yes confirmations The test fails when the requested stable state is not observed within the bounded helper attempts. If a power-on attempt fails, the suite records diagnostics and can perform the configured controlled recovery before retrying. +An all-zero runtime BD address remains a failure. Recovery uses +`btmgmt public-addr` with an authoritative address read from the adapter +device-tree node or from the exact Qualcomm NVM firmware selected by the kernel. +It never hardcodes or generates an address. If firmware was newly provisioned +and the HCI controller remains at `00:00:00:00:00:00`, reboot the target and +rerun the test. ## LAVA diff --git a/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/run.sh b/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/run.sh index 2b975d4ea..704e40f0a 100755 --- a/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/run.sh +++ b/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/run.sh @@ -148,8 +148,8 @@ test_result_init "$TESTNAME" "$RES_FILE" || exit 1 log_info "------------------------------------------------------------" log_info "Starting $TESTNAME Testcase" log_info "Config: BT_POWER_CYCLE_DELAY=${BT_POWER_CYCLE_DELAY}s BT_POWER_ON_ATTEMPTS=$BT_POWER_ON_ATTEMPTS BT_POWER_ON_RETRY_DELAY=${BT_POWER_ON_RETRY_DELAY}s BT_RESTART_SERVICE_ON_RETRY=$BT_RESTART_SERVICE_ON_RETRY" -if ! bt_prepare_ubuntu_stack; then - test_result_finish "FAIL" "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" +if ! bt_prepare_bluetooth_stack; then + test_result_finish "FAIL" "$TESTNAME FAIL - Bluetooth stack preparation failed" fi log_info "Checking dependency: bluetoothctl" @@ -165,7 +165,9 @@ if ! bt_ensure_runtime_ready \ "$BT_RUNTIME_READY_WAIT" \ "$BT_RUNTIME_RECOVERY_WAIT" \ "$BT_RUNTIME_RECOVERY_ATTEMPTS"; then - test_result_finish "FAIL" "Bluetooth runtime remained unusable after bounded recovery attempts" + test_result_finish \ + "FAIL" \ + "${BT_RUNTIME_FAILURE_REASON:-Bluetooth runtime remained unusable after bounded recovery attempts}" fi ADAPTER="$BT_RUNTIME_READY_ADAPTER" diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN/BT_SCAN_README.md b/Runner/suites/Connectivity/Bluetooth/BT_SCAN/BT_SCAN_README.md index 6c2e0039f..70499c6ff 100644 --- a/Runner/suites/Connectivity/Bluetooth/BT_SCAN/BT_SCAN_README.md +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN/BT_SCAN_README.md @@ -59,6 +59,12 @@ Required runtime support: /sys/class/bluetooth/hci* ``` +On CentOS, direct execution verifies `bluez`, `atheros-firmware`, and `expect` +before validation. If an attached controller exposes an all-zero address, the +shared helper uses `btmgmt public-addr` with an authoritative address from +device tree or the exact Qualcomm NVM firmware selected by the kernel. It does +not hardcode or generate a Bluetooth address. + The test automatically skips if no HCI adapter is found. ## Basic usage diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN/run.sh b/Runner/suites/Connectivity/Bluetooth/BT_SCAN/run.sh index 267de2ca9..85f5a9d87 100755 --- a/Runner/suites/Connectivity/Bluetooth/BT_SCAN/run.sh +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN/run.sh @@ -134,8 +134,8 @@ test_result_init "$TESTNAME" "$RES_FILE" || exit 1 log_info "------------------------------------------------------------" log_info "Starting $TESTNAME Testcase" -if ! bt_prepare_ubuntu_stack; then - test_result_finish "FAIL" "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" +if ! bt_prepare_bluetooth_stack; then + test_result_finish "FAIL" "$TESTNAME FAIL - Bluetooth stack preparation failed" fi log_info "Checking dependencies: bluetoothctl pgrep" @@ -150,7 +150,9 @@ if ! bt_ensure_runtime_ready \ "$BT_RUNTIME_READY_WAIT" \ "$BT_RUNTIME_RECOVERY_WAIT" \ "$BT_RUNTIME_RECOVERY_ATTEMPTS"; then - test_result_finish "FAIL" "Bluetooth runtime remained unusable after bounded recovery attempts" + test_result_finish \ + "FAIL" \ + "${BT_RUNTIME_FAILURE_REASON:-Bluetooth runtime remained unusable after bounded recovery attempts}" fi ADAPTER="$BT_RUNTIME_READY_ADAPTER" diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAIR_README.md b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAIR_README.md index 2af63bb2d..b873529f3 100644 --- a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAIR_README.md +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAIR_README.md @@ -99,7 +99,15 @@ When a whitelist is specified (CLI or `BT_WHITELIST_ENV`), only devices whose MA ## Troubleshooting -- Ensure `bluetoothctl`, `rfkill`, `expect`, and `hciconfig` are installed and in PATH. +- Ensure `bluetoothctl`, `rfkill`, and `expect` are installed and in PATH. + CentOS direct execution verifies `bluez`, `atheros-firmware`, and `expect` + before starting the test. + `hciconfig` is optional because current CentOS BlueZ packages omit that + deprecated utility. The test uses sysfs and `bluetoothctl` when it is absent. +- When an attached controller reports an all-zero address, the shared helper + invokes `btmgmt public-addr` with an authoritative address read from device + tree or the exact Qualcomm NVM firmware selected by the kernel. No address is + hardcoded or generated. - Confirm the DUT’s Bluetooth adapter is present and powered on. - For headless devices, ensure target is in discoverable/pairing mode. - Inspect `scan.log` and `pair.log` for detailed errors. diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh index 6292ca6ab..abf8708b7 100755 --- a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh @@ -177,21 +177,31 @@ if [ -z "$BT_MAC" ] && [ -z "$BT_NAME" ] && [ ! -f "./bt_device_list.txt" ]; the test_result_finish "SKIP" "No Bluetooth target and no bt_device_list.txt were provided" fi -if ! bt_prepare_ubuntu_stack; then - test_result_finish "FAIL" "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" +if ! bt_prepare_bluetooth_stack; then + test_result_finish "FAIL" "$TESTNAME FAIL - Bluetooth stack preparation failed" fi -if ! check_dependencies bluetoothctl rfkill expect hciconfig; then +if ! check_dependencies \ + bluetoothctl \ + rfkill \ + expect \ + l2ping; then test_result_finish "SKIP" "$TESTNAME SKIP - required Bluetooth tools are unavailable" fi +if ! command -v hciconfig >/dev/null 2>&1; then + log_info "Optional hciconfig command is unavailable, using sysfs and bluetoothctl for adapter discovery" +fi + log_info "Ensuring Bluetooth runtime readiness before pairing" if ! bt_ensure_runtime_ready \ "${BT_ADAPTER:-}" \ "$BT_RUNTIME_READY_WAIT" \ "$BT_RUNTIME_RECOVERY_WAIT" \ "$BT_RUNTIME_RECOVERY_ATTEMPTS"; then - test_result_finish "FAIL" "Bluetooth runtime remained unusable after bounded recovery attempts" + test_result_finish \ + "FAIL" \ + "${BT_RUNTIME_FAILURE_REASON:-Bluetooth runtime remained unusable after bounded recovery attempts}" fi BT_ADAPTER="$BT_RUNTIME_READY_ADAPTER" diff --git a/Runner/suites/Connectivity/WiFi/WiFi_Firmware_Driver/README.md b/Runner/suites/Connectivity/WiFi/WiFi_Firmware_Driver/README.md index 64f115a31..efa19883b 100644 --- a/Runner/suites/Connectivity/WiFi/WiFi_Firmware_Driver/README.md +++ b/Runner/suites/Connectivity/WiFi/WiFi_Firmware_Driver/README.md @@ -10,7 +10,8 @@ This test is useful for verifying basic WiFi firmware and driver readiness befor - Required command-line utilities are available. - The SoC model is detected from device tree when available. -- WiFi firmware is found under `/lib/firmware`. +- WiFi firmware is found under `/lib/firmware`, including readable symlinks + and kernel-supported `.xz` or `.zst` compressed files. - The WiFi firmware family is detected. - Family-specific runtime preparation succeeds. - Family-specific modules are visible. @@ -97,7 +98,8 @@ The test passes when: The test skips when: - Required tools are missing. -- No supported WiFi firmware is found under `/lib/firmware`. +- No supported uncompressed, `.xz`, or `.zst` WiFi firmware file or readable + symlink is found under `/lib/firmware`. ## FAIL criteria diff --git a/Runner/suites/Connectivity/WiFi/WiFi_Firmware_Driver/run.sh b/Runner/suites/Connectivity/WiFi/WiFi_Firmware_Driver/run.sh index 7436a0d48..28997024b 100755 --- a/Runner/suites/Connectivity/WiFi/WiFi_Firmware_Driver/run.sh +++ b/Runner/suites/Connectivity/WiFi/WiFi_Firmware_Driver/run.sh @@ -67,7 +67,8 @@ suite_rc=0 log_info "=== WiFi Firmware Detection ===" if ! wifi_detect_firmware_info; then - log_skip "$TESTNAME SKIP - No ath12k/ath11k/ath10k WiFi firmware found under /lib/firmware" + log_skip \ + "$TESTNAME SKIP - No readable ath12k/ath11k/ath10k WiFi firmware asset found under /lib/firmware, including .xz and .zst files" echo "$TESTNAME SKIP" >"$RES_FILE" exit 0 fi diff --git a/Runner/suites/Multimedia/Audio/AudioPlayback/Read_me.md b/Runner/suites/Multimedia/Audio/AudioPlayback/Read_me.md index 0878576f0..3e87395eb 100644 --- a/Runner/suites/Multimedia/Audio/AudioPlayback/Read_me.md +++ b/Runner/suites/Multimedia/Audio/AudioPlayback/Read_me.md @@ -60,7 +60,8 @@ The test suite includes 10 diverse audio clip configurations covering various sa Yocto images must provide the following components. The suite does not change the Yocto package flow. -- PipeWire: `pw-play`, `wpctl` +- PipeWire: `pw-play` or `pw-cat --playback`, plus `wpctl`. CentOS provides + these playback clients in the `pipewire-utils` image package. - PulseAudio: `paplay`, `pactl` - ALSA: `aplay`, `amixer`, `alsaucm` when UCM is available - Common tools: `pgrep`, `timeout`, `grep`, `tar`, and either `curl` or `wget` @@ -70,10 +71,18 @@ the Yocto package flow. When run as root, the suite uses the shared package provider to install missing Ubuntu audio packages. It does not run a blanket distribution upgrade. -When network download is enabled and neither `curl` nor `wget` is present, the -same provider installs the OS-specific `audio-download` package set on Debian, -Ubuntu, or CentOS. Image-managed Yocto targets remain non-installing and skip -the download when neither tool is provided by the image. +Network download uses an image-provided `curl` or `wget` and never installs a +downloader package at runtime. Each displayed download attempt runs one client +invocation with a finite deadline. When neither tool is available, the suite +skips the download and reports the missing image prerequisite. + +If the target already has a valid global IPv4 address but the generic ICMP +probe is blocked, the suite preserves the active interface and lets the bounded +HTTPS download validate endpoint reachability. It does not cycle Wi-Fi, renew +DHCP, or probe unrelated network services in that state. CentOS targets using +the systemd-managed `NetworkManager.service` are recognized through `nmcli` +when network activation is actually required. The absence of +`systemd-networkd` or ConnMan is not reported when NetworkManager is active. - `auto` is the default profile. It selects `desktop` when `graphical.target` is active and otherwise selects `server`. @@ -86,6 +95,25 @@ the download when neither tool is provided by the image. The optional `snd-soc-wcd938x` codec module is loaded only when the running kernel provides it. Targets without that module are unchanged. +### CentOS Stream 10 overlay preparation + +An explicit `./run.sh --overlay` request ensures EPEL and the Qualcomm CentOS +10 aarch64 and noarch repositories, refreshes DNF metadata, and installs: + +```text +audioreach-dkms audioreach-pal audioreach-pipewire-plugin +``` + +If the DKMS package changes, the suite requests a reboot before validation. +Base mode continues to use the components provided by the CentOS image. + +Backend client checks are read-only after package preparation. PipeWire uses +`pw-play` when available and falls back to `pw-cat --playback`. A missing +playback client does not start an additional package-manager transaction. +Automatic backend selection falls back to an available ALSA path. An explicitly +requested backend skips with the missing image-package prerequisite before any +audio-clip download begins. + ```sh # Let the suite detect a server or desktop Ubuntu image ./run.sh --clip-name "playback_config1 playback_config7" @@ -101,13 +129,13 @@ AUDIO_PACKAGE_PROFILE=desktop AUDIO_PACKAGE_UPDATE=1 \ ## Backend and Route Selection -When no backend is requested, the suite uses automatic selection. A physical PipeWire audio sink uses `pw-play`, and a physical PulseAudio sink uses `paplay`. For the `speakers` route, a speaker endpoint takes precedence over headphones, then other physical outputs. Dummy, null, monitor, and loopback PipeWire sinks are not accepted as speaker routes. +When no backend is requested, the suite uses automatic selection. A physical PipeWire audio sink uses `pw-play` or `pw-cat --playback`, and a physical PulseAudio sink uses `paplay`. For the `speakers` route, a speaker endpoint takes precedence over headphones, then other physical outputs. Dummy, null, monitor, and loopback PipeWire sinks are not accepted as speaker routes. If automatic selection finds no physical managed speaker sink, the suite probes direct ALSA playback. It selects an ALSA card and PCM from the available device inventory, applies only mixer controls exposed by that card, and runs `aplay -D `. This supports the Shikra primary-MI2S, secondary-TDM, and codec-direct route capabilities without selecting a form factor or assuming card `0`. An explicit backend request is never replaced: -- `--backend pipewire` or `AUDIO_BACKEND=pipewire` runs `pw-play` only and skips if PipeWire has no physical speaker sink. +- `--backend pipewire` or `AUDIO_BACKEND=pipewire` runs `pw-play` or `pw-cat --playback` and skips if the `pipewire-utils` playback client or a physical PipeWire speaker sink is absent. - `--backend pulseaudio` or `AUDIO_BACKEND=pulseaudio` runs `paplay` only and skips if no matching sink is available. - `--backend alsa` or `AUDIO_BACKEND=alsa` runs `aplay` with the discovered ALSA route. diff --git a/Runner/suites/Multimedia/Audio/AudioPlayback/run.sh b/Runner/suites/Multimedia/Audio/AudioPlayback/run.sh index 02b656164..16f14a4ca 100755 --- a/Runner/suites/Multimedia/Audio/AudioPlayback/run.sh +++ b/Runner/suites/Multimedia/Audio/AudioPlayback/run.sh @@ -113,7 +113,7 @@ EXTRACT_AUDIO_ASSETS="${EXTRACT_AUDIO_ASSETS:-true}" ENABLE_NETWORK_DOWNLOAD="${ENABLE_NETWORK_DOWNLOAD:-false}" # Default: no network operations AUDIO_CLIPS_BASE_DIR="${AUDIO_CLIPS_BASE_DIR:-}" # Custom path for audio clips (CI use) -# Only the explicit --overlay option enables Debian AudioReach preparation. +# Only the explicit --overlay option enables AudioReach package preparation. # AUDIO_OVERLAY_REQUESTED was derived above without consuming the CLI. AUDIO_PLAYBACK_VOLUME="${AUDIO_PLAYBACK_VOLUME:-1.0}" export AUDIO_OVERLAY_REQUESTED AUDIO_PLAYBACK_VOLUME @@ -125,6 +125,7 @@ AUDIO_STARTED_PIDS="" AUDIO_CREATED_RUNTIME_DIR=0 AUDIO_SYSTEMD_MANAGED=0 AUDIO_ALSA_PLAYBACK_DEVICE="" +AUDIO_PIPEWIRE_PLAY_COMMAND="" export AUDIO_BOOTSTRAP_MODE AUDIO_RUNTIME_DIR AUDIO_STARTED_PIDS AUDIO_CREATED_RUNTIME_DIR MINIMAL_RAMDISK_MODE AUDIO_SYSTEMD_MANAGED AUDIO_ALSA_PLAYBACK_DEVICE # New clip-based testing options @@ -149,9 +150,9 @@ Usage: $0 [options] --backend {pipewire|pulseaudio|alsa} --sink {speakers|null} --overlay - On Debian, ensure the Qualcomm AudioReach package set and prepare the - PipeWire runtime before playback. Without this flag, use the native/base - audio stack. qcom-distro/Yocto remains unchanged. + On Debian or CentOS, ensure the Qualcomm AudioReach package set before + playback. Debian also prepares its PipeWire runtime. Without this flag, + use the native/base audio stack. qcom-distro/Yocto remains unchanged. --formats "wav" # Legacy matrix mode only --durations "short|short medium" # Legacy matrix mode only (not recommended for new tests) --clip-name "play_48KHz_16b_2ch" # Test specific clip(s) by name (space-separated) @@ -428,6 +429,15 @@ done AUDIO_BACKEND_REQUESTED="$AUDIO_BACKEND" export AUDIO_BACKEND_REQUESTED +# Prefer the dedicated PipeWire playback alias. Some distributions provide +# only pw-cat, whose explicit --playback mode is the same client capability. +if command -v pw-play >/dev/null 2>&1; then + AUDIO_PIPEWIRE_PLAY_COMMAND="pw-play" +elif command -v pw-cat >/dev/null 2>&1 && + pw-cat --help 2>&1 | grep -q -- '--playback'; then + AUDIO_PIPEWIRE_PLAY_COMMAND="pw-cat" +fi + # Prepare only the Debian user capabilities required by the selected mode. # Explicit base ALSA playback needs group membership but no systemd user # manager. Overlay and managed backends require the user session. @@ -637,6 +647,42 @@ fi log_info "Args: backend=${AUDIO_BACKEND:-auto} sink=$SINK_CHOICE overlay=$AUDIO_OVERLAY_REQUESTED volume=$AUDIO_PLAYBACK_VOLUME loops=$LOOPS timeout=$TIMEOUT formats='$FORMATS' durations='$DURATIONS' strict=$STRICT dmesg=$DMESG_SCAN extract=$EXTRACT_AUDIO_ASSETS network_download=$ENABLE_NETWORK_DOWNLOAD clips_path=${AUDIO_CLIPS_BASE_DIR:-default} bootstrap=$AUDIO_BOOTSTRAP_MODE runtime_dir=${AUDIO_RUNTIME_DIR:-auto}" +# Reject an unusable requested backend before network setup or clip download. +# In automatic mode, continue when any supported image-provided playback +# client is available because backend selection may fall back later. +case "${AUDIO_BACKEND_REQUESTED:-auto}" in + pipewire) + if [ -z "$AUDIO_PIPEWIRE_PLAY_COMMAND" ]; then + log_skip "$TESTNAME SKIP - PipeWire playback client is absent, provision the pipewire-utils image package providing pw-play or pw-cat" + echo "$RESULT_TESTNAME SKIP" >"$RES_FILE" + exit 0 + fi + ;; + pulseaudio) + if ! command -v paplay >/dev/null 2>&1; then + log_skip "$TESTNAME SKIP - PulseAudio playback client paplay is absent from the image" + echo "$RESULT_TESTNAME SKIP" >"$RES_FILE" + exit 0 + fi + ;; + alsa) + if ! command -v aplay >/dev/null 2>&1; then + log_skip "$TESTNAME SKIP - ALSA playback client aplay is absent from the image" + echo "$RESULT_TESTNAME SKIP" >"$RES_FILE" + exit 0 + fi + ;; + auto) + if [ -z "$AUDIO_PIPEWIRE_PLAY_COMMAND" ] && + ! command -v paplay >/dev/null 2>&1 && + ! command -v aplay >/dev/null 2>&1; then + log_skip "$TESTNAME SKIP - no image-provided playback client is available, provision pipewire-utils, pulseaudio-utils, or alsa-utils" + echo "$RESULT_TESTNAME SKIP" >"$RES_FILE" + exit 0 + fi + ;; +esac + # --- Rootfs minimum size check (mirror video policy) --- if [ "$TOP_LEVEL_RUN" -eq 1 ]; then ensure_rootfs_min_size 2 @@ -675,13 +721,20 @@ if [ "$TOP_LEVEL_RUN" -eq 1 ]; then NET_RC="$?" fi - if [ "$NET_RC" -ne 0 ]; then - video_step "" "Bring network online (Wi-Fi credentials if provided)" - ensure_network_online || true - sleep "${NET_STABILIZE_SLEEP}" - else - sleep "${NET_STABILIZE_SLEEP}" - fi + case "$NET_RC" in + 0) + log_info "Network is already reachable, skipping interface reconfiguration" + ;; + 2) + log_warn "A valid IP address is already assigned, skipping Wi-Fi and DHCP reconfiguration" + log_info "The audio asset download will validate direct HTTPS reachability" + ;; + *) + video_step "" "Bring network online (Wi-Fi credentials if provided)" + ensure_network_online || true + sleep "${NET_STABILIZE_SLEEP}" + ;; + esac # Download and extract audio clips tarball log_info "Downloading audio clips from: $AUDIO_TAR_URL" @@ -820,40 +873,49 @@ if [ "$backend_ok" -ne 1 ]; then fi # Dependencies per backend +# Package preparation has already completed above. Keep these late backend +# checks read-only so an automatically detected backend cannot trigger an +# unrelated package transaction when its playback client is absent. case "$AUDIO_BACKEND" in pipewire) - if ! check_dependencies pw-play; then + if [ -z "$AUDIO_PIPEWIRE_PLAY_COMMAND" ]; then if [ -z "$AUDIO_BACKEND_REQUESTED" ] && - audio_playback_probe_alsa_with_recovery && check_dependencies aplay; then + audio_playback_probe_alsa_with_recovery && + command -v aplay >/dev/null 2>&1; then log_warn "$TESTNAME: PipeWire playback utility missing - falling back to ALSA" AUDIO_BACKEND="alsa" AUDIO_SYSTEMD_MANAGED=0 export AUDIO_SYSTEMD_MANAGED else - log_skip "$TESTNAME SKIP - missing PipeWire playback utility" + log_skip "$TESTNAME SKIP - PipeWire playback client is absent, provision the pipewire-utils image package providing pw-play or pw-cat" echo "$RESULT_TESTNAME SKIP" >"$RES_FILE" exit 0 fi + elif [ "$AUDIO_PIPEWIRE_PLAY_COMMAND" = "pw-cat" ]; then + log_info "Using PipeWire playback client: pw-cat --playback" + else + log_info "Using PipeWire playback client: pw-play" fi ;; pulseaudio) - if ! check_dependencies paplay; then + if ! command -v paplay >/dev/null 2>&1; then if [ -z "$AUDIO_BACKEND_REQUESTED" ] && - audio_playback_probe_alsa_with_recovery && check_dependencies aplay; then + audio_playback_probe_alsa_with_recovery && + command -v aplay >/dev/null 2>&1; then log_warn "$TESTNAME: PulseAudio playback utility missing - falling back to ALSA" AUDIO_BACKEND="alsa" AUDIO_SYSTEMD_MANAGED=0 export AUDIO_SYSTEMD_MANAGED else - log_skip "$TESTNAME SKIP - missing PulseAudio playback utility" + log_skip "$TESTNAME SKIP - missing PulseAudio playback utility: paplay" echo "$RESULT_TESTNAME SKIP" >"$RES_FILE" exit 0 fi fi ;; alsa) - if ! check_dependencies aplay; then - log_skip "$TESTNAME SKIP - missing ALSA playback utility" + if ! command -v aplay >/dev/null 2>&1; then + log_skip "$TESTNAME SKIP - missing ALSA playback utility: aplay" echo "$RESULT_TESTNAME SKIP" >"$RES_FILE" exit 0 fi @@ -877,7 +939,8 @@ if [ "$AUDIO_BACKEND" = "pipewire" ]; then if ! audio_run_helper_as_test_user --require-session audio_pw_ctl_ok 2>/dev/null; then if [ -z "$AUDIO_BACKEND_REQUESTED" ] && - audio_playback_probe_alsa_with_recovery && check_dependencies aplay; then + audio_playback_probe_alsa_with_recovery && + command -v aplay >/dev/null 2>&1; then log_warn "$TESTNAME: falling back to ALSA direct playback path" AUDIO_BACKEND="alsa" AUDIO_SYSTEMD_MANAGED=0 @@ -901,7 +964,8 @@ elif [ "$AUDIO_BACKEND" = "pulseaudio" ]; then if ! audio_run_helper_as_test_user --require-session audio_pa_ctl_ok 2>/dev/null; then if [ -z "$AUDIO_BACKEND_REQUESTED" ] && - audio_playback_probe_alsa_with_recovery && check_dependencies aplay; then + audio_playback_probe_alsa_with_recovery && + command -v aplay >/dev/null 2>&1; then log_warn "$TESTNAME: falling back to ALSA direct playback path" AUDIO_BACKEND="alsa" AUDIO_SYSTEMD_MANAGED=0 @@ -1128,8 +1192,13 @@ if [ "$USE_CLIP_DISCOVERY" = "true" ]; then start_s="$(date +%s 2>/dev/null || echo 0)" if [ "$AUDIO_BACKEND" = "pipewire" ]; then - log_info "[$case_name] exec: pw-play -v \"$clip_path\"" - audio_run_with_timeout_as_test_user --require-session "$effective_timeout" pw-play -v "$clip_path" >>"$logf" 2>&1 + if [ "$AUDIO_PIPEWIRE_PLAY_COMMAND" = "pw-cat" ]; then + log_info "[$case_name] exec: pw-cat --playback -v \"$clip_path\"" + audio_run_with_timeout_as_test_user --require-session "$effective_timeout" pw-cat --playback -v "$clip_path" >>"$logf" 2>&1 + else + log_info "[$case_name] exec: pw-play -v \"$clip_path\"" + audio_run_with_timeout_as_test_user --require-session "$effective_timeout" pw-play -v "$clip_path" >>"$logf" 2>&1 + fi rc=$? elif [ "$AUDIO_BACKEND" = "pulseaudio" ]; then log_info "[$case_name] exec: paplay --device=\"$SINK_NAME\" \"$clip_path\"" @@ -1265,8 +1334,13 @@ else start_s="$(date +%s 2>/dev/null || echo 0)" if [ "$AUDIO_BACKEND" = "pipewire" ]; then - log_info "[$case_name] exec: pw-play -v \"$clip\"" - audio_run_with_timeout_as_test_user --require-session "$TIMEOUT" pw-play -v "$clip" >>"$logf" 2>&1 + if [ "$AUDIO_PIPEWIRE_PLAY_COMMAND" = "pw-cat" ]; then + log_info "[$case_name] exec: pw-cat --playback -v \"$clip\"" + audio_run_with_timeout_as_test_user --require-session "$TIMEOUT" pw-cat --playback -v "$clip" >>"$logf" 2>&1 + else + log_info "[$case_name] exec: pw-play -v \"$clip\"" + audio_run_with_timeout_as_test_user --require-session "$TIMEOUT" pw-play -v "$clip" >>"$logf" 2>&1 + fi rc=$? elif [ "$AUDIO_BACKEND" = "pulseaudio" ]; then log_info "[$case_name] exec: paplay --device=\"$SINK_NAME\" \"$clip\"" diff --git a/Runner/suites/Multimedia/Audio/AudioRecord/Read_me.md b/Runner/suites/Multimedia/Audio/AudioRecord/Read_me.md index 592985051..30f8d373f 100644 --- a/Runner/suites/Multimedia/Audio/AudioRecord/Read_me.md +++ b/Runner/suites/Multimedia/Audio/AudioRecord/Read_me.md @@ -79,9 +79,20 @@ Before backend discovery, the suite enumerates runtime remoteprocs and checks th If the matching remoteproc is `running`, the suite records its name and firmware evidence. If it is `offline`, the suite starts it only after confirming the corresponding firmware is provisioned. Generic audio DSP paths also require adjacent topology evidence. Shikra's modem-hosted path does not require an adjacent topology file because its public image packages `qdsp6sw` modem firmware and `lpaicp` audio firmware separately. The suite never stops a remoteproc. Platforms with no applicable audio remoteproc continue normally. Provisioning or start issues produce a clean skip with the reason. -## Overlay Build Support - -For overlay builds using audioreach kernel modules, the test automatically: +## Overlay Build Support + +On CentOS Stream 10, an explicit `./run.sh --overlay` request ensures EPEL and +the Qualcomm aarch64 and noarch RPM repositories, refreshes DNF metadata, and +installs `audioreach-dkms`, `audioreach-pal`, and +`audioreach-pipewire-plugin`. If the DKMS package changes, reboot before +rerunning validation. Yocto remains image-provided and non-installing. + +Backend client checks are read-only after package preparation. A missing +`pw-record`, `wpctl`, `parecord`, `pactl`, or `arecord` does not start an +additional package-manager transaction and instead produces a clean SKIP for +the unavailable recording path. + +For overlay builds using audioreach kernel modules, the test automatically: - Detects the overlay build configuration - Sets required DMA heap permissions - Restarts PipeWire service diff --git a/Runner/suites/Multimedia/Audio/AudioRecord/run.sh b/Runner/suites/Multimedia/Audio/AudioRecord/run.sh index 924dda598..91c1c0ad9 100755 --- a/Runner/suites/Multimedia/Audio/AudioRecord/run.sh +++ b/Runner/suites/Multimedia/Audio/AudioRecord/run.sh @@ -133,7 +133,7 @@ usage() { Usage: $0 [options] --backend {pipewire|pulseaudio|alsa} --source {mic|null} - --overlay Prepare the Debian Qualcomm AudioReach overlay + --overlay Prepare the Debian or CentOS Qualcomm AudioReach overlay Without this flag, use the native/base stack. --config-name "record_config1" # Test specific config(s) by name (space-separated) # Also supports record_config1, record_config2, ..., record_config10 @@ -655,25 +655,28 @@ if [ "$backend_ok" -ne 1 ]; then exit 0 fi -# Dependencies per backend (include ALSA) +# Package preparation has already completed above. Keep backend-client checks +# read-only so automatic backend discovery cannot trigger package installation. case "$AUDIO_BACKEND" in pipewire) - if ! check_dependencies wpctl pw-record; then - log_skip "$TESTNAME SKIP - missing PipeWire utils" + if ! command -v wpctl >/dev/null 2>&1 || + ! command -v pw-record >/dev/null 2>&1; then + log_skip "$TESTNAME SKIP - missing PipeWire recording utilities: wpctl and/or pw-record" echo "$RESULT_TESTNAME SKIP" > "$RES_FILE" exit 0 fi ;; pulseaudio) - if ! check_dependencies pactl parecord; then - log_skip "$TESTNAME SKIP - missing PulseAudio utils" + if ! command -v pactl >/dev/null 2>&1 || + ! command -v parecord >/dev/null 2>&1; then + log_skip "$TESTNAME SKIP - missing PulseAudio recording utilities: pactl and/or parecord" echo "$RESULT_TESTNAME SKIP" > "$RES_FILE" exit 0 fi ;; alsa) - if ! check_dependencies arecord; then - log_skip "$TESTNAME SKIP - missing arecord" + if ! command -v arecord >/dev/null 2>&1; then + log_skip "$TESTNAME SKIP - missing ALSA recording utility: arecord" echo "$RESULT_TESTNAME SKIP" > "$RES_FILE" exit 0 fi @@ -889,22 +892,24 @@ fi # If fallback changed backend, ensure deps are present (non-fatal → SKIP) case "$AUDIO_BACKEND" in pipewire) - if ! check_dependencies wpctl pw-record; then - log_skip "$TESTNAME SKIP - missing PipeWire utils" + if ! command -v wpctl >/dev/null 2>&1 || + ! command -v pw-record >/dev/null 2>&1; then + log_skip "$TESTNAME SKIP - missing PipeWire recording utilities: wpctl and/or pw-record" echo "$RESULT_TESTNAME SKIP" > "$RES_FILE" exit 0 fi ;; pulseaudio) - if ! check_dependencies pactl parecord; then - log_skip "$TESTNAME SKIP - missing PulseAudio utils" + if ! command -v pactl >/dev/null 2>&1 || + ! command -v parecord >/dev/null 2>&1; then + log_skip "$TESTNAME SKIP - missing PulseAudio recording utilities: pactl and/or parecord" echo "$RESULT_TESTNAME SKIP" > "$RES_FILE" exit 0 fi ;; alsa) - if ! check_dependencies arecord; then - log_skip "$TESTNAME SKIP - missing arecord" + if ! command -v arecord >/dev/null 2>&1; then + log_skip "$TESTNAME SKIP - missing ALSA recording utility: arecord" echo "$RESULT_TESTNAME SKIP" > "$RES_FILE" exit 0 fi diff --git a/Runner/suites/Multimedia/Audio/Audio_Card_Registration/README_Audio_Card_Registration.md b/Runner/suites/Multimedia/Audio/Audio_Card_Registration/README_Audio_Card_Registration.md index cc4d287ab..452e4ab3b 100644 --- a/Runner/suites/Multimedia/Audio/Audio_Card_Registration/README_Audio_Card_Registration.md +++ b/Runner/suites/Multimedia/Audio/Audio_Card_Registration/README_Audio_Card_Registration.md @@ -111,6 +111,11 @@ The test reports `FAIL` when: ## Command Line Usage +On CentOS Stream 10, `--overlay` prepares EPEL and the Qualcomm aarch64 and +noarch RPM repositories, refreshes DNF metadata, and installs +`audioreach-dkms`, `audioreach-pal`, and `audioreach-pipewire-plugin`. The +default mode continues to use image-provided audio components. + ```sh ./run.sh [options] ``` diff --git a/Runner/suites/Multimedia/Audio/Audio_Card_Registration/run.sh b/Runner/suites/Multimedia/Audio/Audio_Card_Registration/run.sh index 3e2531fc6..032dd3b44 100755 --- a/Runner/suites/Multimedia/Audio/Audio_Card_Registration/run.sh +++ b/Runner/suites/Multimedia/Audio/Audio_Card_Registration/run.sh @@ -6,7 +6,7 @@ # - validates ALSA sound card registration # - validates /dev/snd/controlC nodes # - optionally validates PCM/playback/capture entries -# - optionally prepares Debian AudioReach packages with --overlay +# - optionally prepares Debian or CentOS AudioReach packages with --overlay # - does not start/restart PipeWire, PulseAudio, ADSP, or remoteproc # - does not play or record audio @@ -49,7 +49,7 @@ fi TESTNAME="Audio_Card_Registration" -# Only the explicit --overlay option enables Debian AudioReach preparation. +# Only the explicit --overlay option enables AudioReach package preparation. # Ignore inherited values so native/base mode remains the default. AUDIO_OVERLAY_REQUESTED=0 AUDIO_EARLY_HELP_REQUESTED=0 @@ -70,9 +70,9 @@ Usage: $0 [options] Options: --overlay - On Debian, ensure the Qualcomm AudioReach package set before validating - ALSA card registration. Without this flag, use the native/base audio - stack. This test never starts or restarts PipeWire. + On Debian or CentOS, ensure the Qualcomm AudioReach package set before + validating ALSA card registration. Without this flag, use the + native/base audio stack. This test never starts or restarts PipeWire. --wait-secs N Wait time for ALSA sound card registration. @@ -547,4 +547,3 @@ fi log_info "------------------- Completed $TESTNAME Testcase --------------------------" exit 0 - diff --git a/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test_README.md b/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test_README.md index e9972ec70..6045868f5 100644 --- a/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test_README.md +++ b/Runner/suites/Multimedia/CDSP/fastrpc_test/fastrpc_test_README.md @@ -16,6 +16,9 @@ Supported capabilities: character endpoints. The fallback cannot discover PD support directly, so it uses the documented conservative protocol map. The fallback recognizes both `sdsp` and the public `slpi` remoteproc identity for the sensor DSP. Host, test, and DSP skeleton libraries remain runtime-discovered. + The functional invocation is the final capability proof. If an older or vendor-specific test + payload cannot represent a runtime-ready domain, that invocation is reported as FAIL without + relying on package-version or distribution checks. - Multiple iterations with a required finite timeout. - Precise control over binary location via `--bin-dir`. - Line-buffered output through `stdbuf` when available. @@ -26,7 +29,7 @@ Supported capabilities: - CI-ready logs with timestamps and per-iteration, per-domain/PD results - Parameterized control (`--arch`, `--repeat`, `--timeout`, `--bin-dir`, `--domain-mode`, `--domain`, `--domain-name`, `--pd-mode`, `--unsigned-pd`, `--verbose`) -- Auto-discovery of system libraries and DSP skeletons for both Yocto and Debian layouts +- Auto-discovery of system libraries and DSP skeletons for Yocto, Debian, Ubuntu, and RPM layouts - Runtime domain and PD discovery without SoC-name filtering ## Prerequisites @@ -40,16 +43,18 @@ Have these on the target (or specify paths with the flags below): is absent. - FastRPC system libraries and DSP skeletons auto-discovered from standard locations: - Yocto: `/usr/local/lib`, `/usr/local/lib/fastrpc_test`, `/usr/local/share/fastrpc_test` - - Debian: `/usr/lib/`, `/usr/lib//fastrpc_test`, `/usr/share/fastrpc_test` - - RPM-based images: `/usr/lib64`, `/usr/lib64/fastrpc_test`, `/usr/share/fastrpc_test` + - Debian/Ubuntu: `/usr/lib/`, `/usr/lib//fastrpc_test`, `/usr/share/fastrpc_test` + - CentOS and other RPM-based images: `/usr/lib64`, `/usr/lib64/fastrpc_test`, `/usr/share/fastrpc_test` - A selected DSP skeleton directory must contain the complete calculator, HAP example, and multithreading skeleton set. Discovered directories are prepended to the semicolon-separated `DSP_LIBRARY_PATH` used by the public `fastrpc_test` utility while preserving existing DSP search entries. The same merged value is exported through the domain-specific library paths. - Optional but recommended: - `stdbuf` for line-buffered output. Execution remains bounded without it. -- The suite does not install packages at runtime. Missing optional image assets are reported as - SKIP unless the operator explicitly selected a domain whose required runtime library is absent. +- The suite does not install packages at runtime on Yocto, Debian, Ubuntu, CentOS, or other + distributions. Provision `fastrpc_test`, its host libraries, and DSP skeletons in the image. +- Missing optional image assets are reported as SKIP unless the operator explicitly selected a + domain whose required runtime library is absent. ## Directory Structure @@ -141,7 +146,10 @@ An explicitly selected domain fails when it is unavailable, lacks its runtime li support the requested PD mode. Automatic selection from the runtime fallback excludes domains without complete remoteproc and endpoint evidence. When `fastrpc-healthcheck` reports an online, FastRPC-supported domain, a missing endpoint or required runtime library is treated as a broken -installation and fails the suite. +installation and fails the suite. The runner does not infer client capability from a distro name or +package version. When runtime fallback discovers a domain but the installed `fastrpc_test` payload +reports that it cannot represent that domain, the invocation is classified as FAIL so the image or +package gap remains visible until a matching payload is provisioned. Other domains still execute. ### LAVA integration example @@ -186,6 +194,10 @@ installation and fails the suite. domains continue. - **Unsupported automatic domain**: Logged with the healthcheck reason and excluded. Other runnable domains continue. +- **Test payload lacks a runtime-ready domain**: A retained invocation log proving that the + installed client cannot represent the selected numeric domain is classified as FAIL. This is + feature probing, not a package-version check, so it behaves the same on Yocto, Debian, Ubuntu, + and CentOS and automatically starts passing when a capable package is provisioned. - **Offline explicitly requested domain**: Fails with the healthcheck or remoteproc state. - **Missing runtime artifacts**: When the capability source is `fastrpc-healthcheck`, a domain missing its endpoint or required runtime library fails the suite — healthcheck has declared it @@ -194,6 +206,8 @@ installation and fails the suite. Missing shared test libraries or DSP skeletons skip the suite before execution. - **Binary resolved to /bin/fastrpc_test**: Blocked by default. Set `ALLOW_BIN_FASTRPC=1` or use `--bin-dir` to a non-`/bin` path. +- **Missing binary on any distribution**: Package installation is not attempted. The suite reports + SKIP with the expected image-provided binary path. - **Session create errors with -U 1**: If unsigned PD returns `0x80000416`, confirm your image includes unsigned shells/policies (or use `--pd-mode signed-only`). - **Domain not discovered**: Check `dmesg` for remoteproc firmware load errors. The test @@ -208,7 +222,8 @@ installation and fails the suite. - Domain and PD support are derived from `fastrpc-healthcheck` when available. The fallback is selected only when the tool is absent and uses runtime remoteproc and endpoint evidence, with - a conservative protocol mapping for PD support. + a conservative protocol mapping for PD support. Actual invocation output then provides the + distro-independent functional proof that the installed client supports the selected domain. - The existing `/usr/lib/dsp` compatibility links are prepared before healthcheck captures its capability report, so the report and the subsequent functional run observe the same layout. - This suite runs the public `fastrpc_test` character-device path and therefore requires either diff --git a/Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh b/Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh index 16596f8f7..d668ebcef 100755 --- a/Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh +++ b/Runner/suites/Multimedia/CDSP/fastrpc_test/run.sh @@ -322,7 +322,7 @@ RUN_DIR="$BIN_DIR" RUN_BIN="$RUN_DIR/fastrpc_test" if [ ! -x "$RUN_BIN" ]; then - test_result_finish "SKIP" "$TESTNAME SKIP - fastrpc_test not installed (expected at: $RUN_BIN)" + test_result_finish "SKIP" "$TESTNAME SKIP - image-provided fastrpc_test is unavailable, expected=$RUN_BIN" fi # -------------------- Logging root ----------------------------- @@ -605,6 +605,26 @@ for DOMAIN in $DOMAINS_TO_TEST; do echo "----- $iter_tag output end -----" fi + domain_unsupported_reason="" + if [ -r "$iter_log" ]; then + domain_unsupported_reason="$( + fastrpc_invocation_domain_unsupported_reason \ + "$iter_log" \ + "$DOMAIN" || true + )" + fi + + if [ -n "$domain_unsupported_reason" ]; then + test_result_record "FAIL" "$iter_tag: runtime domain=$dom_name id=$DOMAIN is unsupported by installed fastrpc_test, reason=$domain_unsupported_reason capability_source=$CAPABILITY_SOURCE selection=$SELECTION_SOURCE required=image-package-with-matching-domain-support" + combo_fail=$((combo_fail + 1)) + combo_subtests_total=$((combo_subtests_total + 1)) + combo_subtests_fail=$((combo_subtests_fail + 1)) + RUNTIME_FAILURE_SEEN=1 + + i=$((i + 1)) + continue + fi + if [ "$rc" -ne 0 ]; then log_fail "$iter_tag: fastrpc_test exited $rc" RUNTIME_FAILURE_SEEN=1 diff --git a/Runner/suites/Multimedia/Camera/Camera_NHX/README_Camera_NHX.md b/Runner/suites/Multimedia/Camera/Camera_NHX/README_Camera_NHX.md index 0481659c9..8cd42f819 100644 --- a/Runner/suites/Multimedia/Camera/Camera_NHX/README_Camera_NHX.md +++ b/Runner/suites/Multimedia/Camera/Camera_NHX/README_Camera_NHX.md @@ -111,13 +111,24 @@ Snapshot JSON files are currently expected only for targets where the files are ## Desktop CAMX overlay flow Yocto and meta-qcom LAVA images keep their image-provided camera stack and do -not use this flow. On Debian, Ubuntu, and CentOS, an explicit overlay request -installs the `camera-nhx` package set: +not use this flow. On Debian and Ubuntu, an explicit overlay request installs +the `camera-nhx` package set: ```text camx-dkms camx-glymur libcamx-glymur1 camx-firmware-glymur camx-nhx ``` +On CentOS Stream 10, the runner follows the Qualcomm overlay installation +guide and installs the Kodiak package set: + +```text +camx-dkms camx-firmware-kodiak camx-kodiak libcamx-kodiak1 +``` + +Before a missing CentOS package is installed, the shared package provider +ensures EPEL and both `qualcomm-linux-aarch64` and +`qualcomm-linux-noarch` repositories are enabled, then refreshes DNF metadata. + `--overlay` requires the CAMX FIT DTB and uses the compatibility name `camx`. The same requirement can be stated explicitly with `--fit-dtb camx`: diff --git a/Runner/suites/Multimedia/FastCV/FastCV_Module_Functional_Validation/README.md b/Runner/suites/Multimedia/FastCV/FastCV_Module_Functional_Validation/README.md index 1becbc736..c9d53ca96 100644 --- a/Runner/suites/Multimedia/FastCV/FastCV_Module_Functional_Validation/README.md +++ b/Runner/suites/Multimedia/FastCV/FastCV_Module_Functional_Validation/README.md @@ -59,7 +59,8 @@ https://softwarecenter.qualcomm.com/nexus/rpm/centos/10/os/noarch/ When neither repository ID is already enabled, the suite creates `/etc/yum.repos.d/qualcomm-linux.repo` with the `qualcomm-linux-aarch64` and `qualcomm-linux-noarch` definitions from the RPM -image setup guide. It then checks and installs: +image setup guide. The suite runs the documented metadata cleanup and cache +refresh before it checks and installs: ```text qcom-fastcv-binaries libfastcvopt-devel diff --git a/Runner/suites/Multimedia/FastCV/FastCV_Simple_Deinterleave_Validation/README.md b/Runner/suites/Multimedia/FastCV/FastCV_Simple_Deinterleave_Validation/README.md index 87e6e41fa..fc98bd869 100644 --- a/Runner/suites/Multimedia/FastCV/FastCV_Simple_Deinterleave_Validation/README.md +++ b/Runner/suites/Multimedia/FastCV/FastCV_Simple_Deinterleave_Validation/README.md @@ -54,7 +54,8 @@ https://softwarecenter.qualcomm.com/nexus/rpm/centos/10/os/noarch/ ``` When needed, their definitions are written to -`/etc/yum.repos.d/qualcomm-linux.repo`. The suite then checks and installs: +`/etc/yum.repos.d/qualcomm-linux.repo`. The suite runs the documented metadata +cleanup and cache refresh before it checks and installs: ```text qcom-fastcv-binaries libfastcvopt-devel diff --git a/Runner/suites/Multimedia/Graphics/KMSCube/README_KMSCube.md b/Runner/suites/Multimedia/Graphics/KMSCube/README_KMSCube.md index 2dd179756..6e0b26c05 100644 --- a/Runner/suites/Multimedia/Graphics/KMSCube/README_KMSCube.md +++ b/Runner/suites/Multimedia/Graphics/KMSCube/README_KMSCube.md @@ -1,6 +1,8 @@ Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. -SPDX-License-Identifier: BSD-3-Clause# KMSCube GraphicsTest Scripts for Qualcomm Linux based platform (Yocto) +SPDX-License-Identifier: BSD-3-Clause + +# KMSCube Graphics Test Scripts # Overview Graphics scripts automates the validation of Graphics OpenGL ES 2.0 capabilities on the Qualcomm RB3 Gen2 platform running a Yocto-based Linux system. It utilizes kmscube test app which is publicly available at https://gitlab.freedesktop.org/mesa/kmscube @@ -14,12 +16,20 @@ Graphics scripts automates the validation of Graphics OpenGL ES 2.0 capabilities ## Prerequisites -Ensure the following components are present in the target Yocto build: +Ensure the following components are present in the target image: - kmscube (Binary Available in /usr/bin) - this test app can be compiled from https://gitlab.freedesktop.org/mesa/kmscube +- modetest from libdrm-tests, optional on CentOS - Weston should be killed while running KMSCube Test - Write access to root filesystem (for environment setup) +Yocto uses image-provided components. On Debian, Ubuntu, RHEL, and Fedora, a +missing `modetest` command is recovered through the shared package provider +before display detection. CentOS currently has no verified package providing +`modetest`, so its absence is reported as WARN and connector detection continues +through sysfs. If recovery fails on a distribution where `modetest` is +required, the test reports FAIL. + ## Directory Structure ``` @@ -52,6 +62,19 @@ scp -r common Runner user@target_device_ip: ssh user@target_device_ip cd /Runner && ./run-test.sh KMSCube ``` + +Direct runner usage: + +```sh +./run.sh +./run.sh --overlay +./run.sh --help +``` + +`--overlay` requests the Qualcomm graphics overlay on supported desktop +distributions. `-h` and `--help` print usage and exit without changing package, +GPU, display-manager, or Weston state. Unknown arguments are reported as FAIL. + #### Sample output: ``` sh-5.2# cd /Runner/ && ./run-test.sh KMSCube @@ -79,3 +102,28 @@ sh-5.2# - It validates the graphics gles2 functionalities. - If any critical tool is missing, the script exits with an error message. +- A non-zero KMSCube exit status, insufficient rendered frames, or a + case-insensitive `ERROR`, `FAIL`, `FAILED`, or `FAILURE` marker in the retained + KMSCube output is classified as FAIL. Common zero-failure summaries such as + `Failed: 0` and `0 failed` are ignored. +- Output markers are retained in `KMSCube_failure_markers.log` and the complete + command output remains in `KMSCube_run.log`. + +## CentOS Stream 10 overlay preparation + +An explicit `./run.sh --overlay` request uses the shared package provider to +ensure EPEL and the Qualcomm CentOS 10 aarch64 and noarch repositories, refresh +DNF metadata, and install the documented Adreno overlay packages: + +```text +adreno-egl1 adreno-gles2 adreno-opencl-icd adreno-vulkan-icd +``` + +The `gbm-msm-backend` dependency is tracked explicitly because this suite +validates the Qualcomm GBM backend before running KMSCube. Yocto execution +continues to use image-provided components. + +When the requested GPU stack has been installed but the running kernel still +uses the opposite `msm.skip_gpu` policy, the suite reports SKIP with a reboot +requirement. For example, overlay mode with `msm skip_gpu=N` requires a reboot +before KGSL and `/dev/kgsl-3d0` can be validated. diff --git a/Runner/suites/Multimedia/Graphics/KMSCube/run.sh b/Runner/suites/Multimedia/Graphics/KMSCube/run.sh index 2f35ba047..3e5bf905c 100755 --- a/Runner/suites/Multimedia/Graphics/KMSCube/run.sh +++ b/Runner/suites/Multimedia/Graphics/KMSCube/run.sh @@ -3,8 +3,63 @@ # SPDX-License-Identifier: BSD-3-Clause # KMSCube Validator Script (Yocto-Compatible, POSIX sh) -# --- Robustly find and source init_env --------------------------------------- -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +usage() { + cat <&2 + printf '%s\n' "$TESTNAME FAIL" >"$RES_FILE" + exit 0 +fi + +FRAME_COUNT="${FRAME_COUNT:-999}" +EXPECTED_MIN=$((FRAME_COUNT - 1)) KMSCUBE_DRM_CONNECTOR="" KMSCUBE_DRM_DEV="" -OVERLAY_REQUESTED=0 OS_ID="unknown" DISTRO_GPU_HANDLING_SUPPORTED=0 @@ -70,12 +135,19 @@ GPU_BOOT_ARTIFACTS_CHANGED=0 GPU_MODULE="msm_kgsl" GPU_OVERLAY_DEVICE="/dev/kgsl-3d0" GPU_OVERLAY_GBM_PACKAGE="${GPU_OVERLAY_GBM_PACKAGE:-}" +GPU_KERNEL_PACKAGE="kgsl-dkms" +GPU_KERNEL_VERSION_BEFORE="" +GPU_KERNEL_VERSION_AFTER="" DISPLAY_MANAGER_SERVICE="${DISPLAY_MANAGER_SERVICE:-display-manager.service}" DISPLAY_MANAGER_STATE_FILE="/tmp/qcom-testkit-${TESTNAME}-display-manager.$$.state" weston_stopped_by_test=0 -rm -f "$RES_FILE" "$LOG_FILE" "$DISPLAY_MANAGER_STATE_FILE" +rm -f \ + "$RES_FILE" \ + "$LOG_FILE" \ + "$FAILURE_LOG" \ + "$DISPLAY_MANAGER_STATE_FILE" trap ' if [ "${weston_stopped_by_test:-0}" -eq 1 ] && @@ -91,15 +163,6 @@ trap 'exit 129' HUP trap 'exit 130' INT trap 'exit 143' TERM -# --- Resolve requested graphics runtime mode --------------------------------- -for kmscube_arg in "$@"; do - case "$kmscube_arg" in - --overlay) - OVERLAY_REQUESTED=1 - ;; - esac -done - # --- Detect OS once ---------------------------------------------------------- if command -v pkg_detect_os_id >/dev/null 2>&1; then OS_ID="$(pkg_detect_os_id 2>/dev/null || true)" @@ -119,6 +182,9 @@ if [ -z "$GPU_OVERLAY_GBM_PACKAGE" ]; then ubuntu) GPU_OVERLAY_GBM_PACKAGE="libgbm-msm" ;; + centos|rhel) + GPU_OVERLAY_GBM_PACKAGE="gbm-msm-backend" + ;; *) GPU_OVERLAY_GBM_PACKAGE="libgbm-msm1" ;; @@ -136,6 +202,8 @@ esac if [ "$DISTRO_GPU_HANDLING_SUPPORTED" -eq 1 ]; then for required_helper in \ pkg_package_set_contains \ + pkg_ensure_command \ + pkg_installed_package_version \ pkg_ensure_required_package_set_present \ pkg_ensure_optional_package_set_present \ pkg_restore_package_set \ @@ -160,6 +228,11 @@ if [ "$DISTRO_GPU_HANDLING_SUPPORTED" -eq 1 ]; then exit 0 fi + GPU_KERNEL_VERSION_BEFORE="$( + pkg_installed_package_version \ + "$GPU_KERNEL_PACKAGE" 2>/dev/null || true + )" + if ! pkg_ensure_optional_package_set_present \ graphics \ qli-staging \ @@ -170,6 +243,18 @@ if [ "$DISTRO_GPU_HANDLING_SUPPORTED" -eq 1 ]; then exit 0 fi + GPU_KERNEL_VERSION_AFTER="$( + pkg_installed_package_version \ + "$GPU_KERNEL_PACKAGE" 2>/dev/null || true + )" + + if [ "$GPU_KERNEL_VERSION_BEFORE" != "$GPU_KERNEL_VERSION_AFTER" ]; then + log_info "Qualcomm graphics kernel package changed, package=$GPU_KERNEL_PACKAGE version=${GPU_KERNEL_VERSION_BEFORE:-not-installed}->${GPU_KERNEL_VERSION_AFTER:-not-installed}" + log_skip "$TESTNAME SKIP - Qualcomm overlay packages are ready, reboot required to activate KGSL" + echo "$TESTNAME SKIP" >"$RES_FILE" + exit 0 + fi + if ! pkg_package_has_file_matching \ "$GPU_OVERLAY_GBM_PACKAGE" \ '/gbm/msm_gbm[.]so$'; then @@ -189,7 +274,7 @@ if [ "$DISTRO_GPU_HANDLING_SUPPORTED" -eq 1 ]; then 0) ;; 2) - log_skip "$TESTNAME SKIP - Qualcomm overlay packages are ready; reboot required to activate KGSL" + log_skip "$TESTNAME SKIP - Qualcomm overlay packages are ready, reboot required to activate KGSL" echo "$TESTNAME SKIP" >"$RES_FILE" exit 0 ;; @@ -308,6 +393,29 @@ else log_info "Graphics package-stack and GPU boot-mode handling skipped for os=$OS_ID" fi +# modetest provides connector diagnostics before the no-display decision. +# CentOS continues with sysfs display detection when the command is absent +# because no verified CentOS package currently provides it. +if ! command -v modetest >/dev/null 2>&1; then + if [ "$OS_ID" = "centos" ]; then + log_warn "modetest is unavailable on CentOS, continuing with sysfs display detection" + elif [ "$DISTRO_GPU_HANDLING_SUPPORTED" -eq 1 ]; then + log_info "modetest is missing, attempting package recovery" + + if ! pkg_ensure_command modetest; then + log_fail "$TESTNAME FAIL - failed to recover required command modetest, install the mapped libdrm-tests package" + printf '%s\n' "$TESTNAME FAIL" >"$RES_FILE" + exit 0 + fi + + hash -r 2>/dev/null || true + else + log_skip "$TESTNAME SKIP - required command modetest is absent from the image" + printf '%s\n' "$TESTNAME SKIP" >"$RES_FILE" + exit 0 + fi +fi + log_info "-------------------------------------------------------------------" log_info "------------------- Starting $TESTNAME Testcase -------------------" @@ -390,10 +498,28 @@ if [ ! -e "$1" ]; then fi # --- Dependencies ------------------------------------------------------------ -if ! CHECK_DEPS_NO_EXIT=1 check_dependencies kmscube modetest; then - log_skip "$TESTNAME SKIP - missing dependencies: kmscube and/or modetest" - echo "$TESTNAME SKIP" >"$RES_FILE" - exit 0 +if [ "$DISTRO_GPU_HANDLING_SUPPORTED" -eq 1 ]; then + CHECK_DEPS_RECOVER=1 +else + CHECK_DEPS_RECOVER=0 +fi + +if [ "$OS_ID" = "centos" ]; then + if ! CHECK_DEPS_RECOVER="$CHECK_DEPS_RECOVER" \ + CHECK_DEPS_NO_EXIT=1 \ + check_dependencies kmscube; then + log_skip "$TESTNAME SKIP - required command kmscube is absent from the CentOS image" + echo "$TESTNAME SKIP" >"$RES_FILE" + exit 0 + fi +else + if ! CHECK_DEPS_RECOVER="$CHECK_DEPS_RECOVER" \ + CHECK_DEPS_NO_EXIT=1 \ + check_dependencies kmscube modetest; then + log_skip "$TESTNAME SKIP - missing dependencies: kmscube and/or modetest" + echo "$TESTNAME SKIP" >"$RES_FILE" + exit 0 + fi fi KMSCUBE_BIN="$(command -v kmscube 2>/dev/null || true)" @@ -484,6 +610,35 @@ else unset EGL_PLATFORM fi +# Treat explicit error and failure words from kmscube as authoritative even +# when the process exits successfully. Ignore common zero-failure summaries. +awk ' + { + line = tolower($0) + has_error = line ~ /(^|[^[:alnum:]_])error([^[:alnum:]_]|$)/ + has_failure = line ~ /(^|[^[:alnum:]_])fail(ed|ure|ures)?([^[:alnum:]_]|$)/ + zero_failure = ( + line ~ /fail(ed|ure|ures)?[[:space:]]*[:=][[:space:]]*0([^0-9]|$)/ || + line ~ /(^|[^0-9])0[[:space:]]+(tests?[[:space:]]+)?fail(ed|ure|ures)?([^[:alnum:]_]|$)/ + ) + + if ((has_error || has_failure) && !zero_failure) { + print + } + } +' "$LOG_FILE" >"$FAILURE_LOG" + +FAILURE_MARKER_COUNT="$(awk 'END { print NR + 0 }' "$FAILURE_LOG")" + +if [ "$FAILURE_MARKER_COUNT" -gt 0 ]; then + log_error "kmscube reported ERROR or FAIL output, matches=$FAILURE_MARKER_COUNT artifact=$FAILURE_LOG" + + while IFS= read -r failure_line; do + [ -n "$failure_line" ] || continue + log_error "[kmscube-output] $failure_line" + done <"$FAILURE_LOG" +fi + if [ "$rc" -ne 0 ]; then log_fail "$TESTNAME : Execution failed (rc=$rc) - see $LOG_FILE" cat "$LOG_FILE" @@ -554,6 +709,12 @@ if ! display_restore_service_from_state "$DISPLAY_MANAGER_STATE_FILE"; then fi # --- Verdict ----------------------------------------------------------------- +if [ "$FAILURE_MARKER_COUNT" -gt 0 ]; then + log_fail "$TESTNAME : FAIL (kmscube reported ERROR or FAIL output, matches=${FAILURE_MARKER_COUNT})" + printf '%s\n' "$TESTNAME FAIL" >"$RES_FILE" + exit 1 +fi + if [ "$FRAMES_RENDERED" -lt "$EXPECTED_MIN" ]; then log_fail "$TESTNAME : FAIL (rendered ${FRAMES_RENDERED} < ${EXPECTED_MIN})" echo "$TESTNAME FAIL" >"$RES_FILE" 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 a5d3d5fb9..727ac3bbf 100644 --- a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/README_Video.md +++ b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/README_Video.md @@ -10,7 +10,7 @@ These scripts automate validation of video **encoding** and **decoding** on Qualcomm Linux platforms running a Yocto-based rootfs. They drive the public `iris_v4l2_test` app: . -The suite includes a **reboot-free video stack switcher** (upstream ↔ downstream), a **Kodiak (QCS6490/RB3gen2) firmware swap flow**, and robust **pre-flight checks** (rootfs size, network bootstrap, module sanity, device nodes). +The suite includes a **reboot-free video stack switcher** (upstream ↔ downstream), an optional **Kodiak (QCS6490/RB3gen2) firmware override flow**, and robust **pre-flight checks** (rootfs size, network bootstrap, module sanity, device nodes). --- @@ -59,6 +59,9 @@ The suite includes a **reboot-free video stack switcher** (upstream ↔ downstre - **Rootfs size guard** (auto‑resize) **before** fetching assets - **Network bootstrap** (Ethernet → Wi‑Fi via `nmcli`/`wpa_supplicant`) when needed for downloads - Timeout, repeat, dry-run, JUnit XML, dmesg triage +- Strict app-result validation: zero exit status and the configured success + marker are required, while explicit `ERROR`, `FAIL`, `FAILED!`, or failed + testcase markers always make the invocation fail - **Stack switcher**: upstream ↔ downstream without reboot - **Kodiak firmware live swap** with backup/restore helpers - **udev refresh + prune** of stale device nodes @@ -148,7 +151,7 @@ cd /Runner | `--app /path/to/iris_v4l2_test` | Override test app path | | `--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`) | +| `--downstream-fw PATH` | **Kodiak**: optional custom firmware override. Without it, the runner uses upstream image-provided firmware. | | `--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` | @@ -156,6 +159,16 @@ cd /Runner > **Default remains unchanged.** If you omit all `--ko-*` flags, the runner uses the system module tree and `modinfo`/`modprobe` resolution only. +On Debian and Ubuntu, selecting `--stack downstream`, `--stack overlay`, or the +overlay half of `--stack both` uses the existing APT sources to prepare the +`iris-vpu-dkms` package. On CentOS Stream 10, the same selection ensures EPEL +and both Qualcomm RPM repositories, refreshes DNF metadata, and prepares the +`iris-vpu` package. These packages run DKMS, `depmod`, and module activation in +their post-install flow. Immediately after package provisioning, the runner +validates the resulting module state without repeating `modprobe` operations. +Yocto and other image-managed systems continue to use image-provided packages +and the runner-managed module switch. + --- ## Pre‑Flight: Rootfs Size & Network @@ -193,7 +206,10 @@ export PASSWORD="WIFI_PASSWORD" # or create ./ssid_list.txt with: WIFI_SSID WIFI_PASSWORD ``` -When network remains unreachable and clips are missing, **decode cases are SKIPPED** (not failed). +When a valid IP route exists but a generic ICMP probe is inconclusive, the +runner preserves the interface and tries the configured media URL directly. +When no IP route exists or the media endpoint is unavailable and clips are +missing, **decode cases are SKIPPED** (not failed). --- @@ -209,16 +225,22 @@ When network remains unreachable and clips are missing, **decode cases are SKIPP The runner: 1. Prints **pre/post** module snapshots and any runtime/persistent modprobe blocks -2. Switches stacks without reboot (uses runtime blacklists under `/run/modprobe.d`) -3. **Refreshes** `/dev/video*` & `/dev/media*` with udev and **prunes** stale nodes -4. Applies small **waits/retries** around unload/load and de‑blacklist/blacklist paths +2. Uses package-managed module activation after provisioning on Debian, Ubuntu, and CentOS +3. Switches stacks without reboot on Yocto/image-managed systems (uses runtime blacklists under `/run/modprobe.d`) +4. **Refreshes** `/dev/video*` & `/dev/media*` with udev and **prunes** stale nodes +5. Applies small **waits/retries** around unload/load and de‑blacklist/blacklist paths --- ## Kodiak Firmware Flows -### Downstream (custom blob) -When `--stack downstream` and you pass `--downstream-fw /path/to/vpu20_1v.mbn`: +### Downstream + +Kodiak downstream runs use the upstream firmware already provisioned by the +image. A separate firmware file is not required. + +When an explicit custom firmware override is needed, pass +`--downstream-fw /path/to/vpu20_1v.mbn`: 1. The blob is copied to: `/lib/firmware/qcom/vpu/vpu20_p1_gen2.mbn` 2. Previous image is backed up to: `/opt/video-fw-backups/vpu20_p1_gen2.mbn..bak` 3. Runner tries **remoteproc restart**, then **module reload**, then **unbind/bind** (with short waits between steps) @@ -260,7 +282,12 @@ export VIDEO_FW_BACKUP_DIR=/opt ./run.sh --stack upstream ``` -### Kodiak: downstream with custom firmware (live swap) +### Kodiak: downstream with image-provided firmware +```sh +./run.sh --platform kodiak --stack downstream +``` + +### Kodiak: downstream with an optional custom firmware override ```sh ./run.sh --platform kodiak --stack downstream --downstream-fw /data/fw/vpu20_1v.mbn ``` @@ -350,4 +377,3 @@ export VIDEO_INTER_TEST_SLEEP=3 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. --- - diff --git a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/run.sh b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/run.sh index 72fa5bdca..de148f4f4 100755 --- a/Runner/suites/Multimedia/Video/Video_V4L2_Runner/run.sh +++ b/Runner/suites/Multimedia/Video/Video_V4L2_Runner/run.sh @@ -38,6 +38,11 @@ fi # shellcheck disable=SC1091 . "$TOOLS/lib_video.sh" +if [ -r "$TOOLS/lib_pkg_provider.sh" ]; then + # shellcheck disable=SC1090,SC1091 + . "$TOOLS/lib_pkg_provider.sh" +fi + TESTNAME="Video_V4L2_Runner" RES_FILE="./${TESTNAME}.res" @@ -105,7 +110,7 @@ Usage: $0 [--config path.json|/path/dir] [--dir DIR] [--pattern GLOB] [--junit FILE] [--dry-run] [--verbose] [--stack auto|upstream|downstream|base|overlay|up|down|both] [--platform lemans|monaco|kodiak] - [--downstream-fw PATH] [--force] + [--downstream-fw PATH] [--force] # optional Kodiak firmware override [--app /path/to/iris_v4l2_test] [--ssid SSID] [--password PASS] [--ko-dir DIR[:DIR2:...]] # opt-in: search these dirs for .ko on failure @@ -339,16 +344,6 @@ if [ -n "$VIDEO_APP" ] && [ -f "$VIDEO_APP" ] && [ ! -x "$VIDEO_APP" ]; then fi fi -# ---- Default firmware path for Kodiak downstream if CLI not given ---- -if [ -z "${VIDEO_FW_DS:-}" ]; then - default_fw="/data/vendor/iris_test_app/firmware/vpu20_1v.mbn" - if [ -f "$default_fw" ]; then - VIDEO_FW_DS="$default_fw" - export VIDEO_FW_DS - log_info "Using default downstream firmware path: $VIDEO_FW_DS" - fi -fi - # Decide final app path: if --app given, require it; otherwise search PATH, /usr/bin, /data/vendor/iris_test_app final_app="" @@ -478,7 +473,8 @@ else log_info "Sub-run: skipping initial network bring-up." fi -# --- Early guard: bail out BEFORE any download if Kodiak-downstream lacks --downstream-fw --- +# Validate an explicitly requested Kodiak firmware override before downloads. +# Kodiak normally uses the firmware already provisioned by the image. early_plat="$VIDEO_PLATFORM" if [ -z "$early_plat" ]; then early_plat="$(video_detect_platform)" @@ -486,10 +482,22 @@ fi early_stack="$(video_normalize_stack "$VIDEO_STACK")" -if [ "$early_plat" = "kodiak" ] && [ "$early_stack" = "downstream" ] && [ -z "${VIDEO_FW_DS:-}" ]; then - log_skip "On Kodiak, downstream/overlay requires --downstream-fw ; skipping run." - printf '%s\n' "$TESTNAME SKIP" >"$RES_FILE" - exit 0 +if [ "$early_plat" = "kodiak" ]; then + case "$early_stack" in + downstream|both) + if [ -n "${VIDEO_FW_DS:-}" ] && [ ! -f "$VIDEO_FW_DS" ]; then + log_fail "$TESTNAME FAIL - requested Kodiak firmware override does not exist: $VIDEO_FW_DS" + printf '%s\n' "$TESTNAME FAIL" >"$RES_FILE" + exit 1 + fi + + if [ -n "${VIDEO_FW_DS:-}" ]; then + log_info "Kodiak downstream will use the explicit firmware override: $VIDEO_FW_DS" + else + log_info "Kodiak downstream will use upstream image-provided firmware" + fi + ;; + esac fi # --- Optional early fetch of bundle (best-effort, ALWAYS in LOG_ROOT) — only once @@ -505,11 +513,20 @@ if [ "$TOP_LEVEL_RUN" -eq 1 ]; then export LOG_DIR if command -v check_network_status_rc >/dev/null 2>&1; then - if ! check_network_status_rc; then - log_info "Network unreachable; skipping early media bundle fetch." - else - extract_tar_from_url "$TAR_URL" || true - fi + check_network_status_rc + early_network_rc=$? + + case "$early_network_rc" in + 1) + log_info "No configured IP route is available, skipping early media bundle fetch" + ;; + 0|2) + if [ "$early_network_rc" -eq 2 ]; then + log_info "Generic reachability is inconclusive, trying the configured media endpoint" + fi + extract_tar_from_url "$TAR_URL" || true + ;; + esac else extract_tar_from_url "$TAR_URL" || true fi @@ -681,22 +698,9 @@ if [ "${VIDEO_STACK}" = "both" ]; then base_status="$(printf '%s\n' "$base_res_line" | awk '{print $2}')" overlay_status="$(printf '%s\n' "$overlay_res_line" | awk '{print $2}')" - overlay_reason="" - plat_for_reason="$VIDEO_PLATFORM" - if [ -z "$plat_for_reason" ]; then - plat_for_reason="$(video_detect_platform)" - fi - if [ "$overlay_status" = "SKIP" ] && [ "$plat_for_reason" = "kodiak" ] && [ -z "${VIDEO_FW_DS:-}" ]; then - overlay_reason="missing --downstream-fw" - fi - if [ "$rc_base" -eq 0 ] && [ "$rc_overlay" -eq 0 ] ; then if [ "$base_status" = "PASS" ] && [ "$overlay_status" = "SKIP" ]; then - if [ -n "$overlay_reason" ]; then - log_info "[both] upstream/base executed and PASS; downstream/overlay SKIP ($overlay_reason). Overall PASS." - else - log_info "[both] upstream/base executed and PASS; downstream/overlay SKIP. Overall PASS." - fi + log_info "[both] upstream/base executed and PASS; downstream/overlay SKIP. Overall PASS." elif [ "$base_status" = "SKIP" ] && [ "$overlay_status" = "PASS" ]; then log_info "[both] downstream/overlay executed and PASS; upstream/base SKIP. Overall PASS." else @@ -750,31 +754,81 @@ VIDEO_STACK="$(video_normalize_stack "$VIDEO_STACK")" pre_stack="$(video_stack_status "$plat")" log_info "Current video stack (pre): $pre_stack" -# Kodiak + upstream → install backup firmware to /lib/firmware before switching -if [ "$plat" = "kodiak" ]; then - case "$VIDEO_STACK" in - upstream|up|base) - video_step "" "Kodiak upstream firmware install" - video_kodiak_install_firmware || true +VIDEO_OS_ID="unknown" +if command -v pkg_detect_os_id >/dev/null 2>&1; then + VIDEO_OS_ID="$(pkg_detect_os_id 2>/dev/null || true)" +elif [ -r /etc/os-release ]; then + VIDEO_OS_ID="$( + sed -n 's/^ID=//p' /etc/os-release | + sed -n '1p' | + tr -d '"' | + tr '[:upper:]' '[:lower:]' + )" +fi + +VIDEO_PACKAGE_MANAGED_STACK=0 +VIDEO_PACKAGE_RECOVERY_APPLICABLE=0 +video_package_set_ready_before=0 + +if [ "$VIDEO_STACK" = "downstream" ]; then + case "$VIDEO_OS_ID" in + debian|ubuntu|centos) + VIDEO_PACKAGE_RECOVERY_APPLICABLE=1 ;; esac fi -# ---- Enforce --downstream-fw on Kodiak when requesting downstream/overlay (SKIP if unmet) ---- +if [ "$VIDEO_PACKAGE_RECOVERY_APPLICABLE" -eq 1 ]; then + for required_helper in \ + pkg_provider_init \ + pkg_verify_package_set_installed \ + pkg_ensure_optional_package_set_present; do + if ! command -v "$required_helper" >/dev/null 2>&1; then + log_fail "$TESTNAME FAIL - required video overlay package helper is unavailable: $required_helper" + printf '%s\n' "$TESTNAME FAIL" >"$RES_FILE" + exit 1 + fi + done + + pkg_provider_init + + if pkg_verify_package_set_installed video-overlay; then + video_package_set_ready_before=1 + fi + + if ! pkg_ensure_optional_package_set_present \ + video-overlay \ + none \ + auto \ + --overlay; then + log_fail "$TESTNAME FAIL - failed to ensure Qualcomm video overlay package set" + printf '%s\n' "$TESTNAME FAIL" >"$RES_FILE" + exit 1 + fi + + log_pass "Qualcomm video overlay package set is ready" + + if [ "$video_package_set_ready_before" -eq 0 ]; then + VIDEO_PACKAGE_MANAGED_STACK=1 + fi +fi + +# Kodiak + upstream → install backup firmware to /lib/firmware before switching if [ "$plat" = "kodiak" ]; then case "$VIDEO_STACK" in - downstream|overlay|down) - if [ -z "$VIDEO_FW_DS" ] || [ ! -f "$VIDEO_FW_DS" ]; then - log_skip "On Kodiak, downstream/overlay requires --downstream-fw ; skipping run." - printf '%s\n' "$TESTNAME SKIP" >"$RES_FILE" - exit 0 - fi + upstream|up|base) + video_step "" "Kodiak upstream firmware install" + video_kodiak_install_firmware || true ;; esac fi # --- Optional cleanup: robust capture + normalization of post-stack value --- -video_dump_stack_state "pre" +video_stack_probe_mode="full" +if [ "$VIDEO_PACKAGE_MANAGED_STACK" -eq 1 ]; then + video_stack_probe_mode="loaded-only" +fi +video_dump_stack_state "pre" "$video_stack_probe_mode" # --- Custom .ko staging (only if user provided --ko-dir) --- if [ -n "${KO_DIRS:-}" ]; then @@ -808,7 +862,12 @@ video_step "" "Apply desired stack = $VIDEO_STACK" stack_tmp="$LOG_DIR/.ensure_stack.$$.out" : > "$stack_tmp" -video_ensure_stack "$VIDEO_STACK" "$plat" >"$stack_tmp" 2>&1 || true +if [ "$VIDEO_PACKAGE_MANAGED_STACK" -eq 1 ]; then + log_info "Package-managed video stack, validating module state without runner modprobe operations" + video_stack_status "$plat" >"$stack_tmp" 2>&1 || true +else + video_ensure_stack "$VIDEO_STACK" "$plat" >"$stack_tmp" 2>&1 || true +fi if [ -s "$stack_tmp" ]; then total_lines="$(wc -l < "$stack_tmp" 2>/dev/null | tr -d ' ')" @@ -829,7 +888,7 @@ fi log_info "Video stack (post): $post_stack" -video_dump_stack_state "post" +video_dump_stack_state "post" "$video_stack_probe_mode" # --- Custom .ko load assist (only if user provided --ko-dir) --- if [ -n "${KO_DIRS:-}" ]; then @@ -1069,7 +1128,10 @@ while IFS= read -r cfg; do sleep "${NET_STABILIZE_SLEEP:-5}" if command -v check_network_status_rc >/dev/null 2>&1; then - if ! check_network_status_rc; then + check_network_status_rc + case_network_rc=$? + + if [ "$case_network_rc" -eq 1 ]; then ce=2 fi elif command -v check_network_status >/dev/null 2>&1; then diff --git a/Runner/utils/audio_common.sh b/Runner/utils/audio_common.sh index ae5c744ee..894c2d3b4 100755 --- a/Runner/utils/audio_common.sh +++ b/Runner/utils/audio_common.sh @@ -96,47 +96,24 @@ resolve_clip() { # audio_ensure_download_client # Ensures that curl or wget is available. Takes no arguments and emits no -# machine-readable stdout. It may install the mapped audio-download package set -# on Debian, Ubuntu, or CentOS and writes diagnostics through log_* helpers. -# Returns 0 when a downloader is available and 1 otherwise. Image-managed -# distributions are never modified. +# machine-readable stdout. Returns 0 when an image-provided downloader is +# available and 1 otherwise. Runtime package installation is intentionally not +# attempted by an audio test. audio_ensure_download_client() { if command -v curl >/dev/null 2>&1 || command -v wget >/dev/null 2>&1; then return 0 fi - if command -v pkg_ensure_host_distro_package_set_present >/dev/null 2>&1; then - log_info "Audio asset download requires curl or wget, attempting mapped package recovery" - pkg_ensure_host_distro_package_set_present audio-download - aedc_recovery_rc=$? - - case "$aedc_recovery_rc" in - 0) - ;; - 2) - log_warn "Audio downloader package recovery is not enabled for this image-managed OS" - ;; - *) - log_error "Failed to recover the audio-download package set" - ;; - esac - fi - - if command -v curl >/dev/null 2>&1 || - command -v wget >/dev/null 2>&1; then - return 0 - fi - - log_error "No downloader is available, install curl or wget" + log_error "No image-provided downloader is available, provision curl or wget" return 1 } # audio_download_with_any # Downloads the non-empty URL to the output path using an available curl or -# wget client. It may recover the OS-specific audio-download package set and -# writes downloader output to stdout and stderr. Returns the downloader status, -# or 1 when neither client is available. The caller owns output-file cleanup. +# wget client and writes downloader output to stdout and stderr. Returns the +# downloader status, or 1 when neither client is available. The caller owns +# output-file cleanup. audio_download_with_any() { url="$1" out="$2" @@ -185,10 +162,9 @@ audio_has_runnable_discovery_clips() { # audio_fetch_assets_from_url # Downloads and extracts the audio archive URL into AUDIO_CLIPS_BASE_DIR. The # URL must be non-empty. The function writes diagnostic logs and temporary -# archive files, installs the mapped audio-download package set on supported -# host distributions only when neither curl nor wget exists, and emits no +# archive files, uses only image-provided download clients, and emits no # machine-readable stdout. Returns 0 when runnable clips are ready and 1 when -# downloader recovery, download, extraction, or validation fails. +# download, extraction, or validation fails. audio_fetch_assets_from_url() { url="$1" clips_dir="${AUDIO_CLIPS_BASE_DIR:-AudioClips}" @@ -229,26 +205,45 @@ audio_fetch_assets_from_url() { rm -f "$fetch_log" >/dev/null 2>&1 || true download_ok=0 + fetch_client="" - if command -v curl >/dev/null 2>&1; then - log_info "exec: curl -fL --retry 3 --retry-delay 2 --connect-timeout 20 -o \"$archive_path\" \"$url\" (attempt ${fetch_attempt}/${fetch_attempts})" - if curl -fL --retry 3 --retry-delay 2 --connect-timeout 20 -o "$archive_path" "$url" >"$fetch_log" 2>&1; then + if command -v curl >/dev/null 2>&1 && + command -v wget >/dev/null 2>&1; then + if [ $((fetch_attempt % 2)) -eq 1 ]; then + fetch_client="curl" + else + fetch_client="wget" + fi + elif command -v curl >/dev/null 2>&1; then + fetch_client="curl" + elif command -v wget >/dev/null 2>&1; then + fetch_client="wget" + fi + + if [ "$fetch_client" = "curl" ]; then + log_info "exec: curl -fL --connect-timeout 20 --max-time 120 -o \"$archive_path\" \"$url\" (attempt ${fetch_attempt}/${fetch_attempts})" + if audio_exec_with_timeout 125 \ + curl -fL \ + --connect-timeout 20 \ + --max-time 120 \ + -o "$archive_path" \ + "$url" >"$fetch_log" 2>&1; then download_ok=1 else log_warn "curl download failed on attempt ${fetch_attempt}/${fetch_attempts}, showing last lines from $fetch_log" tail -n 20 "$fetch_log" 2>/dev/null || true fi - fi - - if [ "$download_ok" -ne 1 ]; then - if command -v wget >/dev/null 2>&1; then - log_info "exec: wget --tries=3 --timeout=20 -O \"$archive_path\" \"$url\" (attempt ${fetch_attempt}/${fetch_attempts})" - if wget --tries=3 --timeout=20 -O "$archive_path" "$url" >"$fetch_log" 2>&1; then - download_ok=1 - else - log_warn "wget download failed on attempt ${fetch_attempt}/${fetch_attempts}, showing last lines from $fetch_log" - tail -n 20 "$fetch_log" 2>/dev/null || true - fi + elif [ "$fetch_client" = "wget" ]; then + log_info "exec: wget --tries=1 --timeout=20 -O \"$archive_path\" \"$url\" (attempt ${fetch_attempt}/${fetch_attempts})" + if audio_exec_with_timeout 125 \ + wget --tries=1 \ + --timeout=20 \ + -O "$archive_path" \ + "$url" >"$fetch_log" 2>&1; then + download_ok=1 + else + log_warn "wget download failed on attempt ${fetch_attempt}/${fetch_attempts}, showing last lines from $fetch_log" + tail -n 20 "$fetch_log" 2>/dev/null || true fi fi @@ -5016,6 +5011,8 @@ audio_prepare_audioreach_udev_rule() { # Debian overlay - ensure audio-base and Debian AudioReach package sets # Ubuntu server - ensure ALSA utilities # Ubuntu desktop - ensure ALSA, PipeWire, PipeWire-Pulse, and WirePlumber +# CentOS base - use image-provided components +# CentOS overlay - ensure the documented Qualcomm AudioReach RPM set # other distros - no-op until their package mappings are verified # # Environment: @@ -5071,7 +5068,7 @@ audio_prepare_test_packages() { log_info "Native image detected, Audio package preparation is not required" return 0 ;; - debian|ubuntu) + debian|ubuntu|centos) ;; *) log_info "Audio package preparation is not enabled for os=$atp_os_id" @@ -5079,7 +5076,7 @@ audio_prepare_test_packages() { ;; esac - # Debian and Ubuntu package preparation must run from root orchestration. + # Host-distribution package preparation must run from root orchestration. if [ "$(id -u 2>/dev/null || echo 1)" -ne 0 ]; then log_fail "Audio package preparation must run as root, os=$atp_os_id" return 1 @@ -5148,6 +5145,12 @@ audio_prepare_test_packages() { return 0 fi + if [ "$atp_os_id" = "centos" ] && + [ "$atp_overlay_requested" -eq 0 ]; then + log_info "CentOS base Audio mode uses image-provided components" + return 0 + fi + ########################################################################### # Debian base mode ########################################################################### @@ -5169,7 +5172,7 @@ audio_prepare_test_packages() { fi ########################################################################### - # Debian AudioReach overlay mode + # Debian and CentOS AudioReach overlay mode ########################################################################### if ! command -v pkg_ensure_optional_package_set_present \ @@ -5184,24 +5187,37 @@ audio_prepare_test_packages() { return 1 fi + case "$atp_os_id" in + centos) + atp_plugin_package="audioreach-pipewire-plugin" + atp_dkms_package="audioreach-dkms" + atp_support_package="audioreach-pal" + ;; + *) + atp_plugin_package="audioreach-pipewire-plugin" + atp_dkms_package="audioreach-kernel-dkms" + atp_support_package="audioreach-config" + ;; + esac + # Capture AudioReach package versions before package preparation. These # values let us distinguish an already-ready target from an install or # upgrade performed during this invocation. atp_before_plugin="$( pkg_installed_package_version \ - audioreach-pipewire-plugin 2>/dev/null || + "$atp_plugin_package" 2>/dev/null || true )" atp_before_dkms="$( pkg_installed_package_version \ - audioreach-kernel-dkms 2>/dev/null || + "$atp_dkms_package" 2>/dev/null || true )" - atp_before_config="$( + atp_before_support="$( pkg_installed_package_version \ - audioreach-config 2>/dev/null || + "$atp_support_package" 2>/dev/null || true )" @@ -5214,31 +5230,31 @@ audio_prepare_test_packages() { auto \ --overlay \ "$@"; then - log_fail "Failed to ensure Debian AudioReach package set" + log_fail "Failed to ensure $atp_os_id AudioReach package set" return 1 fi atp_after_plugin="$( pkg_installed_package_version \ - audioreach-pipewire-plugin 2>/dev/null || + "$atp_plugin_package" 2>/dev/null || true )" atp_after_dkms="$( pkg_installed_package_version \ - audioreach-kernel-dkms 2>/dev/null || + "$atp_dkms_package" 2>/dev/null || true )" - atp_after_config="$( + atp_after_support="$( pkg_installed_package_version \ - audioreach-config 2>/dev/null || + "$atp_support_package" 2>/dev/null || true )" if [ "$atp_before_plugin" != "$atp_after_plugin" ] || [ "$atp_before_dkms" != "$atp_after_dkms" ] || - [ "$atp_before_config" != "$atp_after_config" ]; then + [ "$atp_before_support" != "$atp_after_support" ]; then AUDIO_OVERLAY_PACKAGES_CHANGED=1 export AUDIO_OVERLAY_PACKAGES_CHANGED @@ -5248,8 +5264,8 @@ audio_prepare_test_packages() { log_info "AudioReach PipeWire plugin version changed: ${atp_before_plugin:-not-installed} -> ${atp_after_plugin:-not-installed}" fi - if [ "$atp_before_config" != "$atp_after_config" ]; then - log_info "AudioReach configuration version changed: ${atp_before_config:-not-installed} -> ${atp_after_config:-not-installed}" + if [ "$atp_before_support" != "$atp_after_support" ]; then + log_info "AudioReach support package changed, package=$atp_support_package version=${atp_before_support:-not-installed}->${atp_after_support:-not-installed}" fi if [ "$atp_before_dkms" != "$atp_after_dkms" ]; then @@ -5285,7 +5301,7 @@ audio_prepare_test_packages() { return 1 fi - log_pass "Debian AudioReach package set is ready" + log_pass "$atp_os_id AudioReach package set is ready" return 0 } diff --git a/Runner/utils/functestlib.sh b/Runner/utils/functestlib.sh index fdcc60fd5..638568c69 100755 --- a/Runner/utils/functestlib.sh +++ b/Runner/utils/functestlib.sh @@ -1750,8 +1750,9 @@ extract_tar_from_url() { fi log_info "Downloading $url -> $tarfile" - if ! try_download "$url" "$tarfile"; then - rc=$? + try_download "$url" "$tarfile" + rc=$? + if [ "$rc" -ne 0 ]; then if [ $rc -eq 60 ]; then log_warn "TLS/handshake problem while downloading (cert/clock/firewall or minimal wget). Marking SKIP." : > "$skip_sentinel" 2>/dev/null || true @@ -9008,14 +9009,36 @@ net_log_iface_snapshot() { # ---- Bring the system online if possible (0 OK, 2 IP/no-internet, 1 no IP) ---- ensure_network_online() { check_network_status_rc; net_rc=$? - if [ "$net_rc" -eq 0 ]; then - ensure_reasonable_clock || log_warn "Proceeding in limited-network mode." - unset net_rc - return 0 - fi + case "$net_rc" in + 0) + ensure_reasonable_clock || log_warn "Proceeding in limited-network mode." + unset net_rc + return 0 + ;; + 2) + log_info "[NET] A valid IP address is already assigned, preserving the existing network configuration" + log_info "[NET] The generic reachability probe was inconclusive, endpoint-specific validation will continue" + ensure_reasonable_clock || log_warn "Proceeding in limited-network mode." + unset net_rc + return 2 + ;; + esac - if command -v systemctl >/dev/null 2>&1 && command -v check_systemd_services >/dev/null 2>&1; then - check_systemd_services NetworkManager systemd-networkd connman || true + net_nmcli_available=0 + if command -v nmcli >/dev/null 2>&1 && + nmcli general status >/dev/null 2>&1; then + net_nmcli_available=1 + log_info "[NET] NetworkManager is available through nmcli" + elif command -v systemd_service_is_active >/dev/null 2>&1; then + if systemd_service_is_active NetworkManager; then + log_info "[NET] NetworkManager service is active" + elif systemd_service_is_active systemd-networkd; then + log_info "[NET] systemd-networkd service is active" + elif systemd_service_is_active connman; then + log_info "[NET] ConnMan service is active" + elif command -v check_systemd_services >/dev/null 2>&1; then + check_systemd_services NetworkManager systemd-networkd connman || true + fi fi net_had_any_ip=0 @@ -9031,20 +9054,26 @@ ensure_network_online() { if command -v is_link_up >/dev/null 2>&1; then if ! is_link_up "$net_ifc"; then - log_info "[NET] ${net_ifc}: link=down → skipping DHCP" + log_info "[NET] ${net_ifc}: link=down, skipping DHCP" continue fi fi - log_info "[NET] ${net_ifc}: bringing up and requesting DHCP..." - if command -v bringup_interface >/dev/null 2>&1; then - bringup_interface "$net_ifc" 2 2 || true - fi + if [ "$net_nmcli_available" -eq 1 ]; then + log_info "[NET] ${net_ifc}: requesting an existing NetworkManager connection" + run_with_timeout 15 \ + nmcli device connect "$net_ifc" >/dev/null 2>&1 || true + else + log_info "[NET] ${net_ifc}: bringing up and requesting DHCP..." + if command -v bringup_interface >/dev/null 2>&1; then + bringup_interface "$net_ifc" 2 2 || true + fi - if command -v run_dhcp_client >/dev/null 2>&1; then - run_dhcp_client "$net_ifc" 10 >/dev/null 2>&1 || true - elif command -v try_dhcp_client_safe >/dev/null 2>&1; then - try_dhcp_client_safe "$net_ifc" 8 || true + if command -v run_dhcp_client >/dev/null 2>&1; then + run_dhcp_client "$net_ifc" 10 >/dev/null 2>&1 || true + elif command -v try_dhcp_client_safe >/dev/null 2>&1; then + try_dhcp_client_safe "$net_ifc" 8 || true + fi fi net_log_iface_snapshot "$net_ifc" @@ -9054,12 +9083,14 @@ ensure_network_online() { 0) log_pass "[NET] ${net_ifc}: internet reachable" ensure_reasonable_clock || log_warn "Proceeding in limited-network mode." - unset net_ifaces net_ifc net_rc net_had_any_ip + unset net_ifaces net_ifc net_rc net_had_any_ip net_nmcli_available return 0 ;; 2) log_warn "[NET] ${net_ifc}: IP assigned but internet not reachable" - net_had_any_ip=1 + log_warn "[NET] Preserving the existing interface state for endpoint-specific validation" + unset net_ifaces net_ifc net_rc net_had_any_ip net_nmcli_available + return 2 ;; 1) log_info "[NET] ${net_ifc}: still no IP after DHCP attempt" @@ -9074,10 +9105,13 @@ ensure_network_online() { fi if [ -n "$net_wifi" ]; then net_log_iface_snapshot "$net_wifi" - log_info "[NET] ${net_wifi}: bringing up Wi-Fi..." - - if command -v bringup_interface >/dev/null 2>&1; then - bringup_interface "$net_wifi" 2 2 || true + if [ "$net_nmcli_available" -eq 1 ]; then + log_info "[NET] ${net_wifi}: using the existing NetworkManager configuration" + else + log_info "[NET] ${net_wifi}: bringing up Wi-Fi..." + if command -v bringup_interface >/dev/null 2>&1; then + bringup_interface "$net_wifi" 2 2 || true + fi fi net_creds="" @@ -9111,7 +9145,7 @@ ensure_network_online() { # If nmcli brought us up, do NOT fall back to wpa_supplicant check_network_status_rc; net_rc=$? - if [ "$net_rc" -ne 0 ]; then + if [ "$net_rc" -eq 1 ]; then log_info "[NET] ${net_wifi}: falling back to wpa_supplicant + DHCP" if command -v wifi_connect_wpa_supplicant >/dev/null 2>&1; then wifi_connect_wpa_supplicant "$net_wifi" "$net_ssid" "$net_pass" || true @@ -9121,9 +9155,15 @@ ensure_network_online() { fi fi else - log_info "[NET] ${net_wifi}: no credentials provided → DHCP only" - if command -v run_dhcp_client >/dev/null 2>&1; then - run_dhcp_client "$net_wifi" 10 >/dev/null 2>&1 || true + if [ "$net_nmcli_available" -eq 1 ]; then + log_info "[NET] ${net_wifi}: no credentials provided, requesting a saved NetworkManager connection" + run_with_timeout 15 \ + nmcli device connect "$net_wifi" >/dev/null 2>&1 || true + else + log_info "[NET] ${net_wifi}: no credentials provided, DHCP only" + if command -v run_dhcp_client >/dev/null 2>&1; then + run_dhcp_client "$net_wifi" 10 >/dev/null 2>&1 || true + fi fi fi @@ -9133,12 +9173,14 @@ ensure_network_online() { 0) log_pass "[NET] ${net_wifi}: internet reachable" ensure_reasonable_clock || log_warn "Proceeding in limited-network mode." - unset net_wifi net_ifaces net_ifc net_rc net_had_any_ip net_creds net_ssid net_pass wifi_attempt wifi_max_attempts wifi_retry_delay + unset net_wifi net_ifaces net_ifc net_rc net_had_any_ip net_creds net_ssid net_pass wifi_attempt wifi_max_attempts wifi_retry_delay net_nmcli_available return 0 ;; 2) log_warn "[NET] ${net_wifi}: IP assigned but internet not reachable" - net_had_any_ip=1 + log_warn "[NET] Preserving the existing interface state for endpoint-specific validation" + unset net_wifi net_ifaces net_ifc net_rc net_had_any_ip net_creds net_ssid net_pass wifi_attempt wifi_max_attempts wifi_retry_delay net_nmcli_available + return 2 ;; 1) log_info "[NET] ${net_wifi}: still no IP after connect/DHCP attempt" @@ -9147,14 +9189,18 @@ ensure_network_online() { # If not last attempt, cooldown + cleanup before retry if [ "$wifi_attempt" -lt "$wifi_max_attempts" ]; then - if command -v wifi_cleanup >/dev/null 2>&1; then - wifi_cleanup "$net_wifi" || true - fi - if command -v bringup_interface >/dev/null 2>&1; then - bringup_interface "$net_wifi" 2 2 || true + if [ "$net_nmcli_available" -eq 1 ]; then + log_info "[NET] ${net_wifi}: preserving NetworkManager state before retry" + else + if command -v wifi_cleanup >/dev/null 2>&1; then + wifi_cleanup "$net_wifi" || true + fi + if command -v bringup_interface >/dev/null 2>&1; then + bringup_interface "$net_wifi" 2 2 || true + fi fi if [ "$wifi_retry_delay" -gt 0 ] 2>/dev/null; then - log_info "[NET] ${net_wifi}: retrying in ${wifi_retry_delay}s…" + log_info "[NET] ${net_wifi}: retrying in ${wifi_retry_delay}s" sleep "$wifi_retry_delay" fi fi @@ -9168,8 +9214,9 @@ ensure_network_online() { if command -v ensure_udhcpc_script >/dev/null 2>&1; then net_script_path="$(ensure_udhcpc_script 2>/dev/null || echo "")" fi - if [ -n "$net_script_path" ]; then - log_info "[NET] udhcpc default.script present → refreshing leases" + if [ -n "$net_script_path" ] && + [ "$net_nmcli_available" -ne 1 ]; then + log_info "[NET] udhcpc default.script present, refreshing leases" for net_ifc in $net_ifaces $net_wifi; do [ -n "$net_ifc" ] || continue if command -v run_dhcp_client >/dev/null 2>&1; then @@ -9181,7 +9228,7 @@ ensure_network_online() { 0) log_pass "[NET] connectivity restored after udhcpc fixup" ensure_reasonable_clock || log_warn "Proceeding in limited-network mode." - unset net_script_path net_ifaces net_wifi net_ifc net_rc net_had_any_ip + unset net_script_path net_ifaces net_wifi net_ifc net_rc net_had_any_ip net_nmcli_available return 0 ;; 2) @@ -9192,10 +9239,10 @@ ensure_network_online() { fi if [ "$net_had_any_ip" -eq 1 ] 2>/dev/null; then - unset net_script_path net_ifaces net_wifi net_ifc net_rc net_had_any_ip + unset net_script_path net_ifaces net_wifi net_ifc net_rc net_had_any_ip net_nmcli_available return 2 fi - unset net_script_path net_ifaces net_wifi net_ifc net_rc net_had_any_ip + unset net_script_path net_ifaces net_wifi net_ifc net_rc net_had_any_ip net_nmcli_available return 1 } diff --git a/Runner/utils/lib_bluetooth.sh b/Runner/utils/lib_bluetooth.sh index 4ecbb7624..1982c8fb3 100755 --- a/Runner/utils/lib_bluetooth.sh +++ b/Runner/utils/lib_bluetooth.sh @@ -409,6 +409,82 @@ CONFIG_EOF return 0 } +# Prepare distribution-specific Bluetooth packages and services. +# Ubuntu and Debian retain the existing preparation behavior. CentOS ensures +# the minimal BlueZ and Qualcomm/Atheros firmware package set is present. +# Yocto and other image-managed distributions return unchanged. +bt_prepare_bluetooth_stack() { + if command -v pkg_detect_os_id >/dev/null 2>&1; then + bt_pbs_os_id="$(pkg_detect_os_id 2>/dev/null || echo unknown)" + else + bt_pbs_os_id="$( + bt_os_release_value ID 2>/dev/null | + tr '[:upper:]' '[:lower:]' + )" + fi + + if [ "$bt_pbs_os_id" != "centos" ]; then + bt_prepare_ubuntu_stack + return $? + fi + + if [ "$(id -u 2>/dev/null || echo 1)" -ne 0 ]; then + log_fail "CentOS Bluetooth stack preparation must run as root" + return 1 + fi + + if ! command -v pkg_ensure_required_package_set_present \ + >/dev/null 2>&1; then + if [ -n "${TOOLS:-}" ] && + [ -r "$TOOLS/lib_pkg_provider.sh" ]; then + # shellcheck disable=SC1090,SC1091 + . "$TOOLS/lib_pkg_provider.sh" + fi + fi + + if ! command -v pkg_ensure_required_package_set_present \ + >/dev/null 2>&1; then + log_fail "Bluetooth package-set helper is unavailable" + return 1 + fi + + log_info "CentOS Bluetooth stack preparation: ensuring BlueZ, firmware, and expect packages" + + bt_pbs_firmware_present_before=0 + if command -v pkg_have_package >/dev/null 2>&1 && + pkg_have_package atheros-firmware; then + bt_pbs_firmware_present_before=1 + fi + + if ! pkg_ensure_required_package_set_present bluetooth; then + log_fail "Failed to prepare CentOS Bluetooth packages" + return 1 + fi + + if [ "$bt_pbs_firmware_present_before" -eq 0 ]; then + log_warn "atheros-firmware was newly provisioned, reboot the target if the active HCI controller still reports an all-zero BD address" + fi + + if command -v systemctl >/dev/null 2>&1; then + if ! systemctl start bluetooth.service >/dev/null 2>&1; then + log_fail "Failed to start bluetooth.service on CentOS" + systemctl status bluetooth.service --no-pager 2>/dev/null || true + return 1 + fi + + if ! systemctl is-active --quiet bluetooth.service; then + log_fail "bluetooth.service is not active on CentOS" + systemctl status bluetooth.service --no-pager 2>/dev/null || true + return 1 + fi + + log_pass "bluetooth.service is active" + fi + + log_pass "CentOS Bluetooth stack packages are ready" + return 0 +} + bt_has_controller() { # Use plain bluetoothctl list here; the expect wrapper is overkill and # sometimes swallows the "Controller ..." line for pipelines. @@ -540,9 +616,10 @@ bt_scan_devices_expect() { mac_id="${MAC_ID:-}" [ -z "${mac_id}" ] && [ -n "${1:-}" ] && mac_id="$1" # allow positional MAC - # Detect adapter; hciconfig prints "hci0:"; strip trailing colon. + # Prefer sysfs discovery because current BlueZ distributions may omit the + # deprecated hciconfig utility. if [ -z "${adapter}" ]; then - adapter="$(hciconfig 2>/dev/null | awk '/^hci[0-9]+/ {print $1}' | head -n1)" + adapter="$(listhcis 2>/dev/null | sed -n '1p')" fi adapter="${adapter%:}" @@ -552,7 +629,9 @@ bt_scan_devices_expect() { fi log_info "Using Bluetooth adapter: ${adapter}" - hciconfig "${adapter}" up 2>/dev/null || true + if command -v hciconfig >/dev/null 2>&1; then + hciconfig "${adapter}" up 2>/dev/null || true + fi sleep 1 # Expect flow (prompt-tolerant, in-memory capture) @@ -2139,23 +2218,375 @@ findhcisysfs() { return 1 } +# Normalize and validate a Bluetooth address read from a runtime interface. +# stdout: uppercase MAC when valid +bt_normalize_bdaddr() { + bt_nba_addr="$( + printf '%s' "${1:-}" | + tr '[:lower:]' '[:upper:]' | + tr -d '[:space:]' + )" + + case "$bt_nba_addr" in + 00:00:00:00:00:00|'') + return 1 + ;; + [0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]) + printf '%s\n' "$bt_nba_addr" + return 0 + ;; + esac + + return 1 +} + +# Resolve the device-tree node associated with an HCI adapter. +# stdout: resolved DT node path +bt_adapter_dt_node() { + bt_adn_adapter="${1:-}" + bt_adn_current="" + bt_adn_node="" + + [ -n "$bt_adn_adapter" ] || return 1 + [ -e "/sys/class/bluetooth/$bt_adn_adapter/device" ] || return 1 + + bt_adn_current="$( + readlink -f "/sys/class/bluetooth/$bt_adn_adapter/device" \ + 2>/dev/null || true + )" + + while [ -n "$bt_adn_current" ] && [ "$bt_adn_current" != "/" ]; do + if [ -L "$bt_adn_current/of_node" ] || + [ -d "$bt_adn_current/of_node" ]; then + bt_adn_node="$( + readlink -f "$bt_adn_current/of_node" 2>/dev/null || true + )" + + if [ -n "$bt_adn_node" ] && [ -d "$bt_adn_node" ]; then + printf '%s\n' "$bt_adn_node" + return 0 + fi + fi + + bt_adn_parent="$(dirname "$bt_adn_current")" + [ "$bt_adn_parent" != "$bt_adn_current" ] || break + bt_adn_current="$bt_adn_parent" + done + + return 1 +} + +# Read an authoritative Bluetooth address provisioned in device tree. +# stdout: uppercase MAC when a valid six-byte property is present +btgetdtbdaddr() { + bt_gdba_adapter="${1:-}" + bt_gdba_node="" + bt_gdba_property="" + bt_gdba_bytes="" + bt_gdba_addr="" + + bt_gdba_node="$(bt_adapter_dt_node "$bt_gdba_adapter" 2>/dev/null || true)" + [ -n "$bt_gdba_node" ] || return 1 + + for bt_gdba_name in local-bd-address qcom,local-bd-address; do + bt_gdba_property="$bt_gdba_node/$bt_gdba_name" + [ -r "$bt_gdba_property" ] || continue + + bt_gdba_bytes="$( + od -An -tx1 -N 6 -v "$bt_gdba_property" 2>/dev/null | + tr '\n' ' ' | + tr -s ' ' | + sed 's/^ //;s/ $//' + )" + + # Intentional splitting of the six hexadecimal bytes returned by od. + # shellcheck disable=SC2086 + set -- $bt_gdba_bytes + [ "$#" -eq 6 ] || continue + + if [ -e "$bt_gdba_node/reversed-bd-address" ]; then + bt_gdba_addr="$6:$5:$4:$3:$2:$1" + else + bt_gdba_addr="$1:$2:$3:$4:$5:$6" + fi + + if bt_normalize_bdaddr "$bt_gdba_addr"; then + return 0 + fi + done + + return 1 +} + +# Resolve the exact Qualcomm NVM firmware selected by the current-boot driver +# log. Avoid choosing an arbitrary NVM from a multi-board firmware package. +# stdout: readable firmware path +bt_find_loaded_nvm_firmware() { + bt_flnf_adapter="${1:-}" + bt_flnf_name="" + bt_flnf_log="" + bt_flnf_adapter_log="" + + if command -v get_kernel_log >/dev/null 2>&1; then + bt_flnf_log="$(get_kernel_log 2>/dev/null || true)" + fi + + [ -n "$bt_flnf_log" ] || return 1 + + if [ -n "$bt_flnf_adapter" ]; then + bt_flnf_adapter_log="$( + printf '%s\n' "$bt_flnf_log" | + grep -i -E "Bluetooth:[[:space:]]+${bt_flnf_adapter}:|${bt_flnf_adapter}:" \ + 2>/dev/null || true + )" + + if [ -n "$bt_flnf_adapter_log" ]; then + bt_flnf_log="$bt_flnf_adapter_log" + fi + fi + + bt_flnf_name="$( + printf '%s\n' "$bt_flnf_log" | + awk ' + { + lower = tolower($0) + marker = "using nvm file:" + start = index(lower, marker) + + if (start == 0) { + marker = "qca downloading " + start = index(lower, marker) + } + + if (start == 0) { + next + } + + value = substr($0, start + length(marker)) + sub(/^[[:space:]]+/, "", value) + split(value, fields, /[[:space:]]+/) + gsub(/[,"]+$/, "", fields[1]) + + name = tolower(fields[1]) + sub(/^.*\//, "", name) + + if (name ~ /^(msnv|cmnv|apnv|crnv|htnv|hmtnv|hpnv|wcnhpnv|nvm_)/) { + print fields[1] + } + } + ' | + tail -n 1 + )" + + [ -n "$bt_flnf_name" ] || return 1 + + case "$bt_flnf_name" in + /*) + for bt_flnf_path in \ + "$bt_flnf_name" \ + "$bt_flnf_name.xz" \ + "$bt_flnf_name.zst" + do + if [ -r "$bt_flnf_path" ]; then + printf '%s\n' "$bt_flnf_path" + return 0 + fi + done + ;; + *) + for bt_flnf_root in /lib/firmware /usr/lib/firmware; do + for bt_flnf_path in \ + "$bt_flnf_root/$bt_flnf_name" \ + "$bt_flnf_root/$bt_flnf_name.xz" \ + "$bt_flnf_root/$bt_flnf_name.zst" + do + if [ -r "$bt_flnf_path" ]; then + printf '%s\n' "$bt_flnf_path" + return 0 + fi + done + done + ;; + esac + + return 1 +} + +# Stream a raw or compressed firmware file to stdout. +bt_stream_firmware() { + bt_sf_path="${1:-}" + + [ -r "$bt_sf_path" ] || return 1 + + case "$bt_sf_path" in + *.xz) + if command -v xz >/dev/null 2>&1; then + xz -dc "$bt_sf_path" + elif command -v xzcat >/dev/null 2>&1; then + xzcat "$bt_sf_path" + else + return 1 + fi + ;; + *.zst) + if command -v zstd >/dev/null 2>&1; then + zstd -q -d -c "$bt_sf_path" + elif command -v zstdcat >/dev/null 2>&1; then + zstdcat "$bt_sf_path" + else + return 1 + fi + ;; + *) + command cat "$bt_sf_path" + ;; + esac +} + +# Read the BD address from tag 2 of the exact Qualcomm TLV NVM selected by the +# kernel. Qualcomm stores bdaddr_t bytes least-significant byte first. +# stdout: uppercase MAC when the firmware contains a valid address +btgetfirmwarebdaddr() { + bt_gfba_adapter="${1:-}" + bt_gfba_path="${2:-}" + bt_gfba_addr="" + + if [ -z "$bt_gfba_path" ]; then + bt_gfba_path="$( + bt_find_loaded_nvm_firmware "$bt_gfba_adapter" \ + 2>/dev/null || true + )" + fi + + [ -n "$bt_gfba_path" ] || return 1 + + bt_gfba_addr="$( + bt_stream_firmware "$bt_gfba_path" 2>/dev/null | + od -An -tu1 -v 2>/dev/null | + awk ' + { + for (i = 1; i <= NF; i++) { + bytes[++count] = $i + } + } + + END { + if (count < 4) { + exit 1 + } + + type = bytes[1] + tlv_length = bytes[2] + (bytes[3] * 256) + (bytes[4] * 65536) + offset = 5 + + if (type == 4) { + if (count < 8) { + exit 1 + } + + type = bytes[5] + tlv_length = bytes[6] + (bytes[7] * 256) + (bytes[8] * 65536) + offset = 9 + } + + if (type != 2) { + exit 1 + } + + limit = offset + tlv_length - 1 + if (limit > count) { + limit = count + } + + while ((offset + 11) <= limit) { + tag_id = bytes[offset] + (bytes[offset + 1] * 256) + tag_len = bytes[offset + 2] + (bytes[offset + 3] * 256) + data = offset + 12 + + if ((data + tag_len - 1) > limit) { + exit 1 + } + + if (tag_id == 2 && tag_len == 6) { + printf "%02X:%02X:%02X:%02X:%02X:%02X\n", bytes[data + 5], bytes[data + 4], bytes[data + 3], bytes[data + 2], bytes[data + 1], bytes[data] + exit 0 + } + + offset = data + tag_len + } + + exit 1 + } + ' + )" || bt_gfba_addr="" + + bt_normalize_bdaddr "$bt_gfba_addr" +} + # stdout: MAC if known (e.g. 00:00:00:00:5A:AD) # ret: 0=ok, 1=not found btgetbdaddr() { dev="${1:-}" - + addr="" + + if [ -z "$dev" ]; then + dev="$(listhcis 2>/dev/null | sed -n '1p')" + fi + + if [ -n "$dev" ] && [ -r "/sys/class/bluetooth/$dev/address" ]; then + addr="$(cat "/sys/class/bluetooth/$dev/address" 2>/dev/null || true)" + + if bt_normalize_bdaddr "$addr"; then + return 0 + fi + fi + if command -v hciconfig >/dev/null 2>&1; then if [ -n "$dev" ]; then addr="$(hciconfig -a "$dev" 2>/dev/null | awk '/BD Address:/ {print $3; exit}')" else addr="$(hciconfig -a 2>/dev/null | awk '/BD Address:/ {print $3; exit}')" fi - if [ -n "$addr" ]; then - # IMPORTANT: no log_info here, this is used in command substitution. - printf '%s\n' "$addr" + if bt_normalize_bdaddr "$addr"; then return 0 fi fi + + if [ -n "$dev" ] && command -v btmgmt >/dev/null 2>&1; then + bt_gba_index="${dev#hci}" + + case "$bt_gba_index" in + ''|*[!0-9]*) + ;; + *) + if command -v run_with_timeout >/dev/null 2>&1; then + bt_gba_info="$( + run_with_timeout 3 \ + btmgmt --index "$bt_gba_index" info \ + 2>/dev/null || true + )" + elif command -v timeout >/dev/null 2>&1; then + bt_gba_info="$( + timeout 3 btmgmt --index "$bt_gba_index" info \ + 2>/dev/null || true + )" + else + bt_gba_info="$( + btmgmt --index "$bt_gba_index" info 2>/dev/null || true + )" + fi + + addr="$( + printf '%s\n' "$bt_gba_info" | + awk 'tolower($1) == "addr" { print $2; exit }' + )" + + if bt_normalize_bdaddr "$addr"; then + return 0 + fi + ;; + esac + fi # No logging here either, caller will log if needed. return 1 @@ -2257,47 +2688,131 @@ bt_controller_visible() { btcontrollervisible "${1:-}" } +# Usage: btensurepublicaddr hci0 +# Apply a public address through btmgmt using the numeric HCI index. +btmgmtsetpublicaddr() { + bt_mspa_dev="${1:-}" + bt_mspa_addr="${2:-}" + bt_mspa_index="${bt_mspa_dev#hci}" + bt_mspa_output="" + + command -v btmgmt >/dev/null 2>&1 || return 1 + + case "$bt_mspa_index" in + ''|*[!0-9]*) + return 1 + ;; + esac + + if ! bt_mspa_addr="$(bt_normalize_bdaddr "$bt_mspa_addr")"; then + return 1 + fi + + if command -v run_with_timeout >/dev/null 2>&1; then + if ! bt_mspa_output="$( + run_with_timeout 10 \ + btmgmt --index "$bt_mspa_index" \ + public-addr "$bt_mspa_addr" 2>&1 + )"; then + log_warn "btmgmt failed to configure public address $bt_mspa_addr on $bt_mspa_dev" + [ -z "$bt_mspa_output" ] || printf '%s\n' "$bt_mspa_output" >&2 + return 1 + fi + elif command -v timeout >/dev/null 2>&1; then + if ! bt_mspa_output="$( + timeout 10 btmgmt --index "$bt_mspa_index" \ + public-addr "$bt_mspa_addr" 2>&1 + )"; then + log_warn "btmgmt failed to configure public address $bt_mspa_addr on $bt_mspa_dev" + [ -z "$bt_mspa_output" ] || printf '%s\n' "$bt_mspa_output" >&2 + return 1 + fi + elif ! bt_mspa_output="$( + btmgmt --index "$bt_mspa_index" public-addr "$bt_mspa_addr" 2>&1 + )"; then + log_warn "btmgmt failed to configure public address $bt_mspa_addr on $bt_mspa_dev" + [ -z "$bt_mspa_output" ] || printf '%s\n' "$bt_mspa_output" >&2 + return 1 + fi + + log_info "btmgmt configured public address $bt_mspa_addr on $bt_mspa_dev" + [ -z "$bt_mspa_output" ] || printf '%s\n' "$bt_mspa_output" >&2 + return 0 +} + # Usage: btensurepublicaddr hci0 # Logic: -# - If bluetoothctl already sees a Controller -> no-op (no expect) -# - Else read BD from hciconfig and run "menu mgmt / public-addr " via btctl_script +# - If bluetoothctl already sees a Controller, do nothing +# - Otherwise use a valid runtime, device-tree, or selected NVM address +# - Prefer the direct btmgmt public-addr command used by MSL hosts # Return: # 0 = controller visible (already or now) # 1 = failed to make visible # 2 = could not read BD address btensurepublicaddr() { dev="${1:-}" + mac="" + mac_source="" + firmware_path="" - # Already visible: nothing to do. - if btcontrollervisible "$dev"; then + # A visible controller with a valid runtime address needs no bootstrap. + # Do not accept an all-zero controller merely because BlueZ lists it. + if btbdok "$dev" && btcontrollervisible "$dev"; then log_info "controller already visible via bluetoothctl, skip public-addr" return 0 fi - if ! btbdok "$dev"; then - log_warn "Bluetooth adapter ${dev:-} has no valid BD address, public-addr cannot be applied" - return 2 + if btbdok "$dev"; then + mac="$(btgetbdaddr "$dev" 2>/dev/null | sed -n '1p')" + mac_source="runtime" + elif mac="$(btgetdtbdaddr "$dev" 2>/dev/null | sed -n '1p')" && + [ -n "$mac" ]; then + mac_source="device-tree" + else + firmware_path="$( + bt_find_loaded_nvm_firmware "$dev" 2>/dev/null || true + )" + + if [ -n "$firmware_path" ]; then + mac="$( + btgetfirmwarebdaddr "$dev" "$firmware_path" \ + 2>/dev/null | sed -n '1p' + )" + fi + + if [ -n "$mac" ]; then + mac_source="firmware-nvm:$firmware_path" + fi fi - - mac="$( - btgetbdaddr "$dev" 2>/dev/null \ - | head -n 1 \ - | awk '{print $NF}' - )" - + if [ -z "$mac" ]; then - log_warn "could not read bd address ${dev:+for $dev} public-addr cannot be applied" + log_warn "Bluetooth adapter ${dev:-} has no valid runtime, device-tree, or selected firmware NVM BD address, public-addr cannot be applied" return 2 fi - - log_info "applying bluetoothctl public-addr $mac" - - btctl_script "menu mgmt + + log_info "Applying public address $mac from $mac_source to $dev" + + bt_public_addr_applied=0 + if btmgmtsetpublicaddr "$dev" "$mac"; then + bt_public_addr_applied=1 + elif command -v bluetoothctl >/dev/null 2>&1; then + log_warn "Direct btmgmt public-addr failed, trying the bluetoothctl management fallback" + + if btctl_script "menu mgmt public-addr $mac back -quit" >/dev/null 2>&1 || true - - # Poll for controller visibility (BlueZ can be async) +quit" >/dev/null 2>&1; then + bt_public_addr_applied=1 + fi + fi + + if [ "$bt_public_addr_applied" -eq 0 ]; then + log_warn "Unable to apply public address $mac to $dev" + return 1 + fi + + # Poll for the runtime address and BlueZ controller visibility because both + # are updated asynchronously after the management command completes. i=0 max_wait="${BT_CONTROLLER_VISIBLE_WAIT:-15}" @@ -2307,19 +2822,17 @@ quit" >/dev/null 2>&1 || true ;; esac - max_attempts=$(((max_wait + 4) / 5)) - [ "$max_attempts" -gt 0 ] || max_attempts=1 - - while [ "$i" -lt "$max_attempts" ]; do - if btcontrollervisible "$dev"; then - log_info "controller visible after public-addr $mac" + while [ "$i" -lt "$max_wait" ]; do + if btbdok "$dev" && btcontrollervisible "$dev"; then + log_info "Controller $dev is visible after applying public address $mac" return 0 fi + sleep 1 i=$((i + 1)) done - - log_warn "controller still not visible after public-addr $mac (after ${max_wait}s)" + + log_warn "Controller $dev is not ready after applying public address $mac and waiting ${max_wait}s" return 1 } @@ -3177,37 +3690,57 @@ btpower() { return 1 } +# Return success for public Qualcomm/Atheros Bluetooth firmware and NVM names. +bt_qca_firmware_name_supported() { + bt_qfns_name="$(basename "${1:-}")" + + case "$bt_qfns_name" in + msbtfw*|msnv*|cmbtfw*|cmnv*|apbtfw*|apnv*|\ + crbtfw*|crnv*|htbtfw*|htnv*|hmtbtfw*|hmtnv*|\ + hpbtfw*|hpnv*|wcnhpbtfw*|wcnhpnv*) + return 0 + ;; + esac + + return 1 +} + +# Print the directory containing the first readable supported firmware file. btfwpresent() { - dir="" - pattern="" - file="" - - for d in /lib/firmware/qca /usr/lib/firmware/qca /lib/firmware /usr/lib/firmware; do - [ -d "$d" ] || continue - - for pattern in \ - "msbtfw*.mbn" \ - "msbtfw*.tlv" \ - "msnv*.bin" \ - "cmbtfw*.tlv" \ - "cmnv*.bin" \ - "hpbtfw*.tlv" \ - "wcnhpbtfw*.tlv" \ - "hmtbtfw*.tlv" \ - "hmtnv*.bin" \ - "hpnv*.bin" \ - "wcnhpnv*.bin" - do - for file in "$d"/$pattern; do - if [ -e "$file" ]; then - dir="$d" - printf '%s\n' "$dir" - return 0 - fi - done - done + bt_fwp_file="" + + for bt_fwp_root in \ + /lib/firmware/qca \ + /usr/lib/firmware/qca \ + /lib/firmware \ + /usr/lib/firmware + do + [ -d "$bt_fwp_root" ] || continue + + bt_fwp_file="$( + find "$bt_fwp_root" -maxdepth 4 \ + \( -type f -o -type l \) -print 2>/dev/null | + ( + while IFS= read -r bt_fwp_candidate || + [ -n "$bt_fwp_candidate" ]; do + [ -r "$bt_fwp_candidate" ] || continue + + if bt_qca_firmware_name_supported "$bt_fwp_candidate"; then + printf '%s\n' "$bt_fwp_candidate" + exit 0 + fi + done + + exit 1 + ) + )" || bt_fwp_file="" + + if [ -n "$bt_fwp_file" ]; then + dirname "$bt_fwp_file" + return 0 + fi done - + return 1 } @@ -3343,6 +3876,10 @@ bt_recover_runtime() { bt_rr_action=1 fi + if [ -n "$bt_rr_adapter" ]; then + btensurepublicaddr "$bt_rr_adapter" || true + fi + if [ -n "$bt_rr_adapter" ] && command -v hciconfig >/dev/null 2>&1; then hciconfig "$bt_rr_adapter" up >/dev/null 2>&1 || true bt_rr_action=1 @@ -3375,6 +3912,8 @@ bt_ensure_runtime_ready() { BT_RUNTIME_RECOVERED=0 # shellcheck disable=SC2034 BT_RUNTIME_READY_ADAPTER="" + # shellcheck disable=SC2034 + BT_RUNTIME_FAILURE_REASON="" for bt_err_value in \ "$bt_err_initial_wait" \ @@ -3389,6 +3928,17 @@ bt_ensure_runtime_ready() { esac done + bt_err_bootstrap_adapter="$bt_err_adapter" + if [ -z "$bt_err_bootstrap_adapter" ]; then + bt_err_bootstrap_adapter="$(listhcis 2>/dev/null | sed -n '1p')" + fi + + if [ -n "$bt_err_bootstrap_adapter" ] && + ! btbdok "$bt_err_bootstrap_adapter"; then + log_info "Bluetooth adapter $bt_err_bootstrap_adapter has no valid runtime BD address, attempting authoritative public-address bootstrap" + btensurepublicaddr "$bt_err_bootstrap_adapter" || true + fi + if bt_wait_ready "$bt_err_initial_wait" 2 "$bt_err_adapter"; then return 0 fi @@ -3420,7 +3970,35 @@ bt_ensure_runtime_ready() { bt_err_attempt=$((bt_err_attempt + 1)) done - log_error "Bluetooth runtime remained unusable after $bt_err_attempts controlled recovery attempts" + bt_err_final_adapter="$bt_err_adapter" + if [ -z "$bt_err_final_adapter" ]; then + bt_err_final_adapter="$(listhcis 2>/dev/null | sed -n '1p')" + fi + + bt_err_raw_addr="" + if [ -n "$bt_err_final_adapter" ] && + [ -r "/sys/class/bluetooth/$bt_err_final_adapter/address" ]; then + bt_err_raw_addr="$( + cat "/sys/class/bluetooth/$bt_err_final_adapter/address" \ + 2>/dev/null || true + )" + bt_err_raw_addr="$( + printf '%s' "$bt_err_raw_addr" | + tr '[:lower:]' '[:upper:]' | + tr -d '[:space:]' + )" + fi + + if [ "$bt_err_raw_addr" = "00:00:00:00:00:00" ]; then + BT_RUNTIME_FAILURE_REASON="Bluetooth adapter $bt_err_final_adapter reports an all-zero BD address after recovery, verify Qualcomm firmware and platform BD-address provisioning, then reboot after any firmware package update" + elif [ -n "$bt_err_final_adapter" ]; then + BT_RUNTIME_FAILURE_REASON="Bluetooth adapter $bt_err_final_adapter has no valid runtime BD address after recovery, verify Qualcomm firmware and platform BD-address provisioning" + else + BT_RUNTIME_FAILURE_REASON="No Bluetooth HCI adapter appeared after recovery, verify the kernel driver, firmware, and hardware description" + fi + + export BT_RUNTIME_FAILURE_REASON + log_error "$BT_RUNTIME_FAILURE_REASON" return 1 } @@ -3439,7 +4017,7 @@ btfwloaded() { fatal_re="${BTFW_FATAL_RE:-tx timeout|Reading QCA version information failed|failed to open firmware|firmware file.*not found|download.*firmware.*failed|failed to download.*firmware|timeout waiting for firmware|firmware.*load.*failed}" # Retry/transient hints. - transient_re="${BTFW_TRANSIENT_RE:-Retry BT power ON|retry bt power on|reset|re-init|reinit|failed.*\\(-110\\)}" + transient_re="${BTFW_TRANSIENT_RE:-Retry BT power ON|retry bt power on|reset|re-init|reinit|failed.*[(]-110[)]}" # ---- Collect recent relevant current-boot kernel log ---- if command -v get_kernel_log >/dev/null 2>&1; then @@ -3570,35 +4148,9 @@ bthcipresent() { btbdok() { dev="${1:-}" addr="" - - if [ -n "$dev" ] && [ -r "/sys/class/bluetooth/$dev/address" ]; then - addr="$(cat "/sys/class/bluetooth/$dev/address" 2>/dev/null || true)" - fi - - if [ -z "$addr" ] && command -v hciconfig >/dev/null 2>&1; then - if [ -n "$dev" ]; then - addr="$(hciconfig -a "$dev" 2>/dev/null | awk '/BD Address:/ {print $3; exit}')" - else - addr="$(hciconfig -a 2>/dev/null | awk '/BD Address:/ {print $3; exit}')" - fi - fi - - addr="$(printf '%s' "$addr" | tr '[:lower:]' '[:upper:]' | tr -d '[:space:]')" - - case "$addr" in - '') - return 1 - ;; - 00:00:00:00:00:00) - return 1 - ;; - [0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]:[0-9A-F][0-9A-F]) - return 0 - ;; - *) - return 1 - ;; - esac + + addr="$(btgetbdaddr "$dev" 2>/dev/null || true)" + [ -n "$addr" ] } # Robust polling after scan ON: @@ -3742,57 +4294,41 @@ bt_scan_poll_off() { return 1 } -# Return BD address for a given HCI adapter using hciconfig. +# Return BD address for a given HCI adapter using sysfs or hciconfig. # Prints empty output if the adapter is missing or address cannot be parsed. bt_hci_bdaddr() { adapter="$1" [ -n "$adapter" ] || return 1 - if ! command -v hciconfig >/dev/null 2>&1; then - return 1 - fi - - hciconfig "$adapter" 2>/dev/null | awk ' - /BD Address:/ { - for (i = 1; i <= NF; i++) { - if ($i == "Address:") { - print $(i + 1) - exit - } - } - } - ' + btgetbdaddr "$adapter" } # Check whether an HCI adapter is usable for BT_ON_OFF. -# Requires UP RUNNING state and a non-zero BD address. +# Requires a non-zero BD address. When hciconfig is available, retain its +# UP/RUNNING check. Otherwise use BlueZ controller visibility as the runtime +# state evidence. Returns 0 when usable and 1 otherwise, with no output or +# runtime state changes. bt_adapter_is_usable() { adapter="$1" out="" - addr="" [ -n "$adapter" ] || return 1 - if ! command -v hciconfig >/dev/null 2>&1; then + if ! btbdok "$adapter"; then return 1 fi - out="$(hciconfig "$adapter" 2>/dev/null || true)" - [ -n "$out" ] || return 1 - - addr="$(bt_hci_bdaddr "$adapter" 2>/dev/null || true)" - case "$addr" in - ""|"00:00:00:00:00:00") - return 1 - ;; - esac + if command -v hciconfig >/dev/null 2>&1; then + out="$(hciconfig "$adapter" 2>/dev/null || true)" + if printf '%s\n' "$out" | grep -q 'UP RUNNING'; then + return 0 + fi - if printf '%s\n' "$out" | grep -q 'UP RUNNING'; then - return 0 + return 1 fi - return 1 + btcontrollervisible "$adapter" } # Select the best available Bluetooth adapter. @@ -3845,7 +4381,7 @@ bt_log_hci_candidates() { usable="no" if ! command -v hciconfig >/dev/null 2>&1; then - log_warn "hciconfig not available; cannot log HCI adapter candidates." + log_info "Optional hciconfig is unavailable, using sysfs and BlueZ adapter evidence" return 0 fi diff --git a/Runner/utils/lib_connectivity.sh b/Runner/utils/lib_connectivity.sh index 58dc6fca8..b4ef135ac 100755 --- a/Runner/utils/lib_connectivity.sh +++ b/Runner/utils/lib_connectivity.sh @@ -378,8 +378,42 @@ wifi_dump_debug_info() { wifi_dump_runtime_info } +# wifi_find_firmware_asset ... +# Prints the first readable regular file or symlink matching the ordered +# patterns. Returns 0 on a match and 1 when the root or asset is unavailable. +# This helper does not modify the firmware tree. +wifi_find_firmware_asset() { + wifi_ffa_root="$1" + shift + + [ -d "$wifi_ffa_root" ] || return 1 + + for wifi_ffa_name in "$@"; do + wifi_ffa_path="$( + find "$wifi_ffa_root" \ + \( -type f -o -type l \) \ + -name "$wifi_ffa_name" \ + -print 2>/dev/null | + while IFS= read -r wifi_ffa_candidate; do + if [ -r "$wifi_ffa_candidate" ]; then + printf '%s\n' "$wifi_ffa_candidate" + break + fi + done + )" + + if [ -n "$wifi_ffa_path" ]; then + printf '%s\n' "$wifi_ffa_path" + return 0 + fi + done + + return 1 +} + # Detect WiFi firmware family and a representative firmware file under -# /lib/firmware for ath12k, ath11k, or ath10k based platforms. +# /lib/firmware for ath12k, ath11k, or ath10k based platforms. Firmware may be +# an uncompressed file or a readable symlink/file with an .xz or .zst suffix. wifi_detect_firmware_info() { WIFI_FW_FILE="" WIFI_FW_FAMILY="" @@ -387,18 +421,36 @@ wifi_detect_firmware_info() { WIFI_FW_SIZE="" if [ -d /lib/firmware/ath12k ]; then - WIFI_FW_FILE="$(find /lib/firmware/ath12k -type f -name amss.bin -print -quit 2>/dev/null)" + WIFI_FW_FILE="$( + wifi_find_firmware_asset \ + /lib/firmware/ath12k \ + amss.bin \ + amss.bin.xz \ + amss.bin.zst + )" if [ -n "$WIFI_FW_FILE" ]; then WIFI_FW_FAMILY="ath12k" fi fi if [ -z "$WIFI_FW_FILE" ] && [ -d /lib/firmware/ath11k ]; then - WIFI_FW_FILE="$(find /lib/firmware/ath11k -type f -name amss.bin -print -quit 2>/dev/null)" + WIFI_FW_FILE="$( + wifi_find_firmware_asset \ + /lib/firmware/ath11k \ + amss.bin \ + amss.bin.xz \ + amss.bin.zst + )" if [ -n "$WIFI_FW_FILE" ]; then WIFI_FW_FAMILY="ath11k" else - WIFI_FW_FILE="$(find /lib/firmware/ath11k -type f -name wpss.mbn -print -quit 2>/dev/null)" + WIFI_FW_FILE="$( + wifi_find_firmware_asset \ + /lib/firmware/ath11k \ + wpss.mbn \ + wpss.mbn.xz \ + wpss.mbn.zst + )" if [ -n "$WIFI_FW_FILE" ]; then WIFI_FW_FAMILY="ath11k" fi @@ -406,11 +458,23 @@ wifi_detect_firmware_info() { fi if [ -z "$WIFI_FW_FILE" ] && [ -d /lib/firmware/ath10k ]; then - WIFI_FW_FILE="$(find /lib/firmware/ath10k -type f -name wlanmdsp.mbn -print -quit 2>/dev/null)" + WIFI_FW_FILE="$( + wifi_find_firmware_asset \ + /lib/firmware/ath10k \ + wlanmdsp.mbn \ + wlanmdsp.mbn.xz \ + wlanmdsp.mbn.zst + )" if [ -n "$WIFI_FW_FILE" ]; then WIFI_FW_FAMILY="ath10k" else - WIFI_FW_FILE="$(find /lib/firmware/ath10k -type f -name 'firmware-*.bin' -print -quit 2>/dev/null)" + WIFI_FW_FILE="$( + wifi_find_firmware_asset \ + /lib/firmware/ath10k \ + 'firmware-*.bin' \ + 'firmware-*.bin.xz' \ + 'firmware-*.bin.zst' + )" if [ -n "$WIFI_FW_FILE" ]; then WIFI_FW_FAMILY="ath10k" fi @@ -422,7 +486,7 @@ wifi_detect_firmware_info() { fi WIFI_FW_BASENAME="${WIFI_FW_FILE##*/}" - WIFI_FW_SIZE="$(stat -c%s "$WIFI_FW_FILE" 2>/dev/null || echo unknown)" + WIFI_FW_SIZE="$(stat -Lc%s "$WIFI_FW_FILE" 2>/dev/null || echo unknown)" # These variables are intentionally returned to callers through shell scope. : "$WIFI_FW_BASENAME" "$WIFI_FW_SIZE" @@ -469,7 +533,7 @@ wifi_handle_firmware_family() { ;; ath11k) case "$basename" in - wpss.mbn) + wpss.mbn|wpss.mbn.xz|wpss.mbn.zst) log_info "ath11k WPSS firmware detected, validating wpss remoteproc." if validate_remoteproc_running "wpss"; then log_info "Remoteproc 'wpss' is active and validated." @@ -478,7 +542,7 @@ wifi_handle_firmware_family() { log_fail "Remoteproc 'wpss' validation failed." return 1 ;; - amss.bin) + amss.bin|amss.bin.xz|amss.bin.zst) log_info "ath11k amss.bin firmware detected, handling PCI/AHB/SNOC class platform." if wifi_load_first_available_module ath11k_pci ath11k_ahb ath11k_snoc ath11k; then return 0 diff --git a/Runner/utils/lib_display.sh b/Runner/utils/lib_display.sh index 613af75b9..8a1d6bf7f 100755 --- a/Runner/utils/lib_display.sh +++ b/Runner/utils/lib_display.sh @@ -5562,6 +5562,9 @@ display_prepare_desktop_graphics_stack() { dpdgs_rc=0 dpdgs_package_changed=0 dpdgs_boot_changed=0 + dpdgs_dkms_package="kgsl-dkms" + dpdgs_dkms_before="" + dpdgs_dkms_after="" if [ -z "$dpdgs_gbm_package" ]; then if command -v pkg_detect_os_id >/dev/null 2>&1 && @@ -5582,6 +5585,7 @@ display_prepare_desktop_graphics_stack() { for dpdgs_helper in \ pkg_package_set_contains \ pkg_ensure_optional_package_set_present \ + pkg_installed_package_version \ pkg_package_has_file_matching \ mrv_qcom_gpu_validate_boot_mode \ display_select_egl_vendor; do @@ -5596,6 +5600,11 @@ display_prepare_desktop_graphics_stack() { return 1 fi + dpdgs_dkms_before="$( + pkg_installed_package_version \ + "$dpdgs_dkms_package" 2>/dev/null || true + )" + if ! pkg_ensure_optional_package_set_present \ graphics \ qli-staging \ @@ -5605,6 +5614,17 @@ display_prepare_desktop_graphics_stack() { return 1 fi + dpdgs_dkms_after="$( + pkg_installed_package_version \ + "$dpdgs_dkms_package" 2>/dev/null || true + )" + + if [ "$dpdgs_dkms_before" != "$dpdgs_dkms_after" ]; then + log_info "Qualcomm graphics kernel package changed, package=$dpdgs_dkms_package version=${dpdgs_dkms_before:-not-installed}->${dpdgs_dkms_after:-not-installed}" + log_skip "$dpdgs_testname SKIP - Qualcomm overlay packages are ready, reboot required to activate KGSL" + return 2 + fi + if ! pkg_package_has_file_matching \ "$dpdgs_gbm_package" \ '/gbm/msm_gbm[.]so$'; then diff --git a/Runner/utils/lib_fastcv.sh b/Runner/utils/lib_fastcv.sh index 4b0375d25..fa699b74d 100755 --- a/Runner/utils/lib_fastcv.sh +++ b/Runner/utils/lib_fastcv.sh @@ -102,6 +102,7 @@ fastcv_prepare_runtime_packages() { for fcprp_helper in \ pkg_provider_init \ pkg_ensure_host_distro_package_set_present \ + pkg_prepare_qualcomm_rpm_overlay_repositories \ pkg_ensure_required_package_set_present \ pkg_verify_package_set_installed \ pkg_package_recovery_supported_os; do @@ -132,20 +133,7 @@ fastcv_prepare_runtime_packages() { case "$fcprp_os_id" in centos|rhel) - if ! command -v pkg_ensure_qualcomm_rpm_repository >/dev/null 2>&1; then - log_error "FastCV RPM repository preparation helper is unavailable" - fcprp_rc=1 - elif ! pkg_ensure_required_package_set_present \ - fastcv-rpm-prerequisites; then - log_error "FastCV EPEL repository prerequisite installation failed, os=$fcprp_os_id package=epel-release" - fcprp_rc=1 - elif ! command -v pkg_rpm_repository_enabled >/dev/null 2>&1; then - log_error "FastCV RPM repository verification helper is unavailable" - fcprp_rc=1 - elif ! pkg_rpm_repository_enabled epel; then - log_error "FastCV EPEL repository is not enabled after installing epel-release, os=$fcprp_os_id repository=epel" - fcprp_rc=1 - elif ! pkg_ensure_qualcomm_rpm_repository; then + if ! pkg_prepare_qualcomm_rpm_overlay_repositories; then fcprp_rc=1 fi ;; diff --git a/Runner/utils/lib_fastrpc.sh b/Runner/utils/lib_fastrpc.sh index 85f27a6cd..fe9529392 100755 --- a/Runner/utils/lib_fastrpc.sh +++ b/Runner/utils/lib_fastrpc.sh @@ -4,18 +4,23 @@ # FastRPC runtime layout helpers. # -# Supports both Yocto/current and Debian package layouts. +# Supports Yocto, Debian, Ubuntu, and RPM-based package layouts. # # Yocto/current: # libs : /usr/local/lib # test libs : /usr/local/lib/fastrpc_test # skeletons : dynamically discovered below /usr/local/share/fastrpc_test # -# Debian: +# Debian/Ubuntu: # libs : /usr/lib/ # test libs : /usr/lib//fastrpc_test # skeletons : dynamically discovered below /usr/share/fastrpc_test # +# RPM-based: +# libs : /usr/lib64 +# test libs : /usr/lib64/fastrpc_test +# skeletons : dynamically discovered below /usr/share/fastrpc_test +# # Optional environment overrides: # FASTRPC_LIB_SYS_DIR # FASTRPC_LIB_TEST_DIR @@ -312,7 +317,7 @@ fastrpc_discover_runtime_layout() { FASTRPC_LIB_TEST_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_TEST_DIRS_CHECKED" "/usr/local/lib64/fastrpc_test")" FASTRPC_SKEL_BASES_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_SKEL_BASES_CHECKED" "/usr/local/share/fastrpc_test")" - # Debian/multiarch layout. + # Debian/Ubuntu multiarch layout. if [ -n "$FASTRPC_MULTIARCH_TRIPLET" ]; then FASTRPC_LIB_SYS_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_SYS_DIRS_CHECKED" "/usr/lib/$FASTRPC_MULTIARCH_TRIPLET")" FASTRPC_LIB_TEST_DIRS_CHECKED="$(fastrpc_append_word_unique "$FASTRPC_LIB_TEST_DIRS_CHECKED" "/usr/lib/$FASTRPC_MULTIARCH_TRIPLET/fastrpc_test")" @@ -661,6 +666,46 @@ extract_test_summary_counts() { printf '%s:%s:%s:%s\n' "$total" "$passed" "$failed" "$skipped" } +# fastrpc_invocation_domain_unsupported_reason LOG_FILE DOMAIN_ID +# Detect output proving that the installed test payload cannot represent a selected FastRPC domain. +# Inputs: retained invocation log and numeric domain ID. Output: a stable reason token on stdout. +# Returns: 0 when client-side domain support is absent, 1 otherwise. Side effects: none. +fastrpc_invocation_domain_unsupported_reason() { + invocation_log="$1" + invocation_domain="$2" + + [ -r "$invocation_log" ] || return 1 + + case "$invocation_domain" in + ''|*[!0-9]*) + return 1 + ;; + esac + + if grep -Eiq \ + "unable to get domain struct[[:space:]]+$invocation_domain([^[:digit:]]|$)" \ + "$invocation_log"; then + printf '%s\n' "test-client-domain-table-missing" + return 0 + fi + + if grep -Eiq \ + "domain([[:space:]]+id)?[[:space:]:=]+$invocation_domain([^[:digit:]]|$).*(unsupported|not supported|invalid)" \ + "$invocation_log"; then + printf '%s\n' "test-client-domain-unsupported" + return 0 + fi + + if grep -Eiq \ + "(unsupported|not supported|invalid).*domain([[:space:]]+id)?[[:space:]:=]+$invocation_domain([^[:digit:]]|$)" \ + "$invocation_log"; then + printf '%s\n' "test-client-domain-unsupported" + return 0 + fi + + return 1 +} + # log_dsp_remoteproc_status # Log runtime remoteproc state for every FastRPC firmware alias in the domain table. # Inputs: runtime DT and remoteproc sysfs through shared helpers. Output: no stdout contract. diff --git a/Runner/utils/lib_module_reload.sh b/Runner/utils/lib_module_reload.sh index c136e77ab..46693eea3 100755 --- a/Runner/utils/lib_module_reload.sh +++ b/Runner/utils/lib_module_reload.sh @@ -1735,7 +1735,7 @@ mrv_qcom_gpu_unsafe_reload_detected() { # Return values: # 0 - requested boot mode is active # 1 - state is contradictory or unavailable -# 2 - the other valid boot mode is active, so reboot is required +# 2 - the requested boot policy is not active, so reboot is required mrv_qcom_gpu_validate_boot_mode() { mqgvbm_expected="$1" mqgvbm_kgsl_module="${2:-msm_kgsl}" @@ -1791,6 +1791,16 @@ mrv_qcom_gpu_validate_boot_mode() { ;; esac + # A newly installed GPU module can leave both drivers visible until the + # next boot. The msm skip_gpu setting is authoritative for that transition: + # it cannot be changed safely for the current boot, even when the remaining + # runtime evidence is contradictory and the detected mode is "unknown". + case "$mqgvbm_expected:$mqgvbm_skip_gpu" in + kgsl:N|kgsl:n|kgsl:0|msm:Y|msm:y|msm:1) + return 2 + ;; + esac + return 1 } diff --git a/Runner/utils/lib_pkg_provider.sh b/Runner/utils/lib_pkg_provider.sh index f3337b675..c8ba418c1 100755 --- a/Runner/utils/lib_pkg_provider.sh +++ b/Runner/utils/lib_pkg_provider.sh @@ -421,6 +421,8 @@ pkg_lookup_key_in_map() { } # Resolve command to package names using map. +# Stdout is reserved for the mapped package list because callers use command +# substitution. Diagnostics must be written to stderr. pkg_lookup_packages_for_command() { lookup_cmd="$1" lookup_provider="$(pkg_active_provider)" @@ -429,7 +431,7 @@ pkg_lookup_packages_for_command() { lookup_map_file="$(pkg_resolve_path "$PKG_PACKAGE_MAP")" if [ ! -r "$lookup_map_file" ]; then - pkg_log_warn "Package map file is not readable, $lookup_map_file" + pkg_log_warn "Package map file is not readable, $lookup_map_file" >&2 return 1 fi @@ -459,7 +461,7 @@ pkg_lookup_packages_for_command() { return 0 fi - pkg_log_warn "No package mapping found for command, os=$lookup_os_id provider=$lookup_provider cmd=$lookup_cmd" + pkg_log_warn "No package mapping found for command, os=$lookup_os_id provider=$lookup_provider cmd=$lookup_cmd" >&2 return 1 } @@ -619,6 +621,12 @@ pkg_ensure_optional_package_set() { rm -f "${PKG_APT_UPDATED_MARK:-/tmp/qcom_testkit_apt_updated}" 2>/dev/null || true fi ;; + rpm) + if ! pkg_prepare_qualcomm_rpm_overlay_repositories; then + pkg_log_fail "Qualcomm RPM overlay repository preparation failed, os=$optional_os_id set=$optional_set_name" + return 1 + fi + ;; esac pkg_log_info "Optional package-set recovery requested, os=$optional_os_id provider=$optional_provider set=$optional_set_name packages=$optional_packages" @@ -1656,7 +1664,7 @@ pkg_rpm_repository_enabled() { esac [ -n "$prre_tool" ] || return 1 - if ! prre_repo_list="$($prre_tool repolist enabled 2>/dev/null)"; then + if ! prre_repo_list="$($prre_tool repolist 2>/dev/null)"; then return 1 fi @@ -1671,8 +1679,8 @@ pkg_rpm_repository_enabled() { # Returns: 0 when both repositories are already enabled or configured, and # 1 when the RPM provider, privileges, architecture, OS version, or # configuration fails. -# Side effects: may create PKG_QCOM_RPM_REPO_FILE and invalidate the RPM -# metadata-update marker. Existing repository definitions are not overwritten. +# Side effects: may create PKG_QCOM_RPM_REPO_FILE and refresh RPM metadata. +# Existing repository definitions are not overwritten. pkg_ensure_qualcomm_rpm_repository() { qrr_os_id="$(pkg_detect_os_id)" qrr_os_version="$(pkg_os_release_value VERSION_ID || true)" @@ -1699,7 +1707,7 @@ pkg_ensure_qualcomm_rpm_repository() { return 1 fi - if ! qrr_repo_list="$($qrr_tool repolist enabled 2>/dev/null)"; then + if ! qrr_repo_list="$($qrr_tool repolist 2>/dev/null)"; then pkg_log_fail "Could not inspect enabled RPM repositories, tool=$qrr_tool" return 1 fi @@ -1787,7 +1795,7 @@ pkg_ensure_qualcomm_rpm_repository() { rm -f "$qrr_temp_file" rm -f "$PKG_RPM_UPDATED_MARK" 2>/dev/null || true - if ! qrr_repo_list="$($qrr_tool repolist enabled 2>/dev/null)"; then + if ! qrr_repo_list="$($qrr_tool repolist 2>/dev/null)"; then pkg_log_fail "Could not verify enabled Qualcomm RPM repositories, tool=$qrr_tool file=$PKG_QCOM_RPM_REPO_FILE" rm -f "$PKG_QCOM_RPM_REPO_FILE" return 1 @@ -1803,7 +1811,12 @@ pkg_ensure_qualcomm_rpm_repository() { return 1 fi - pkg_log_pass "Configured Qualcomm RPM repositories, file=$PKG_QCOM_RPM_REPO_FILE" + if ! pkg_rpm_update; then + pkg_log_fail "Qualcomm RPM repositories were configured but metadata refresh failed, file=$PKG_QCOM_RPM_REPO_FILE" + return 1 + fi + + pkg_log_pass "Configured Qualcomm RPM repositories and refreshed metadata, file=$PKG_QCOM_RPM_REPO_FILE" return 0 } @@ -1840,6 +1853,70 @@ pkg_rpm_update() { return "$rpm_update_rc" } +# pkg_prepare_qualcomm_rpm_overlay_repositories +# Prepare the documented CentOS 10 repositories used by Qualcomm overlays. +# Inputs: none. Output: no machine-readable stdout contract. +# Returns: 0 when EPEL and both Qualcomm repositories are enabled with current +# metadata, 1 when repository preparation fails, and 0 when not applicable. +# Side effects: on supported CentOS/RHEL aarch64 targets, may install +# epel-release, create PKG_QCOM_RPM_REPO_FILE, clean RPM metadata, and rebuild +# the package-manager cache. Call only from explicit package preparation paths. +pkg_prepare_qualcomm_rpm_overlay_repositories() { + pqr_os_id="$(pkg_detect_os_id)" + pqr_provider="$(pkg_active_provider)" + pqr_epel_was_installed=0 + + case "$pqr_os_id" in + centos|rhel) + ;; + *) + return 0 + ;; + esac + + if [ "$pqr_provider" != "rpm" ]; then + pkg_log_fail "Qualcomm RPM overlay repository preparation requires the rpm provider, os=$pqr_os_id provider=$pqr_provider" + return 1 + fi + + if pkg_have_package epel-release; then + pqr_epel_was_installed=1 + fi + + if ! pkg_ensure_required_package_set_present \ + qualcomm-rpm-prerequisites; then + pkg_log_fail "EPEL repository prerequisite installation failed, os=$pqr_os_id package=epel-release" + return 1 + fi + + if ! pkg_rpm_repository_enabled epel; then + pkg_log_fail "EPEL repository is not enabled after installing epel-release, os=$pqr_os_id repository=epel" + return 1 + fi + + if [ "$pqr_epel_was_installed" -eq 0 ]; then + rm -f "$PKG_RPM_UPDATED_MARK" 2>/dev/null || true + fi + + if ! pkg_ensure_qualcomm_rpm_repository; then + return 1 + fi + + if ! pkg_rpm_repository_enabled qualcomm-linux-aarch64 || + ! pkg_rpm_repository_enabled qualcomm-linux-noarch; then + pkg_log_fail "Qualcomm RPM repository verification failed, required=qualcomm-linux-aarch64,qualcomm-linux-noarch" + return 1 + fi + + if ! pkg_rpm_update; then + pkg_log_fail "Qualcomm RPM overlay metadata refresh failed" + return 1 + fi + + pkg_log_pass "Qualcomm RPM overlay repositories are ready, repositories=epel,qualcomm-linux-aarch64,qualcomm-linux-noarch" + return 0 +} + # Optionally upgrade rpm packages. pkg_rpm_upgrade() { rpm_tool="$(pkg_rpm_tool || true)" diff --git a/Runner/utils/lib_video.sh b/Runner/utils/lib_video.sh index c51b2a76b..7249641a1 100755 --- a/Runner/utils/lib_video.sh +++ b/Runner/utils/lib_video.sh @@ -276,16 +276,19 @@ video_list_runtime_blocks() { # ------------------------------------------------------------------------- video_dump_stack_state() { when="$1" # pre|post + probe_mode="${2:-full}" log_info "Modules ($when):" log_info "lsmod (iris/venus):" "$LSMOD" 2>/dev/null | awk 'NR==1 || $1 ~ /^(iris_vpu|qcom_iris|venus_core|venus_dec|venus_enc)$/ {print}' - video_modprobe_dryrun qcom_iris - video_modprobe_dryrun iris_vpu - video_modprobe_dryrun venus_core - video_modprobe_dryrun venus_dec - video_modprobe_dryrun venus_enc + if [ "$probe_mode" = "full" ]; then + video_modprobe_dryrun qcom_iris + video_modprobe_dryrun iris_vpu + video_modprobe_dryrun venus_core + video_modprobe_dryrun venus_dec + video_modprobe_dryrun venus_enc + fi log_info "runtime blocks:" video_list_runtime_blocks @@ -974,9 +977,13 @@ video_hot_switch_modules() { rc=1 fi - log_info "Kodiak: invoking firmware swap/reload helper (if VIDEO_FW_DS provided)" - if ! video_kodiak_swap_and_reload "${VIDEO_FW_DS}"; then - log_warn "Kodiak: swap/reload helper reported failure (continuing)" + if [ -n "${VIDEO_FW_DS:-}" ] && [ -f "$VIDEO_FW_DS" ]; then + log_info "Kodiak: applying the explicit downstream firmware override" + if ! video_kodiak_swap_and_reload "$VIDEO_FW_DS"; then + log_warn "Kodiak: firmware override swap/reload reported failure" + fi + else + log_info "Kodiak: using upstream image-provided firmware for the downstream stack" fi video_usleep "${MOD_SETTLE_SLEEP}" @@ -1456,22 +1463,42 @@ video_ensure_clips_present_or_fetch() { fi if command -v ensure_network_online >/dev/null 2>&1; then - if ! ensure_network_online; then - log_warn "Network offline/limited; cannot fetch media bundle" - rm -f "$tmp_list" 2>/dev/null || true - return 2 - fi + ensure_network_online + video_network_rc=$? + + case "$video_network_rc" in + 0) + ;; + 1) + log_warn "No configured IP route is available, cannot fetch media bundle" + rm -f "$tmp_list" 2>/dev/null || true + return 2 + ;; + 2) + log_info "Generic reachability is inconclusive, trying the configured media endpoint" + ;; + esac fi if [ -n "$tu" ]; then log_info "Attempting fetch via TAR_URL=$tu" - if extract_tar_from_url "$tu"; then - rm -f "$tmp_list" 2>/dev/null || true - return 0 - fi - log_warn "Fetch/extract failed for TAR_URL" + extract_tar_from_url "$tu" + video_fetch_rc=$? rm -f "$tmp_list" 2>/dev/null || true - return 1 + + case "$video_fetch_rc" in + 0) + return 0 + ;; + 2) + log_warn "Media endpoint is unavailable for this environment" + return 2 + ;; + *) + log_warn "Fetch/extract failed for TAR_URL" + return 1 + ;; + esac fi log_warn "No TAR_URL provided; cannot fetch media bundle." @@ -1553,12 +1580,24 @@ video_prepare_app() { return 1 } +video_iris_output_has_failure() { + # Return success when iris_v4l2_test emitted a conclusive failure marker. + output_file="$1" + + [ -r "$output_file" ] || return 1 + + grep -Eiq \ + '^[[:space:]]*(ERROR|FAIL)([[:space:]:!]|$)|^[[:space:]]*\[(ERROR|FAIL)\]|^[[:space:]]*FAILED!([[:space:]]|$)|Testcase(\[[^]]*\])?[[:space:]]*:[[:space:]]*Failed([[:space:]]|$)' \ + "$output_file" +} + video_run_once() { cfg="$1" logf="$2" tmo="$3" suc="$4" lvl="$5" + rc=0 video_prepare_app || true @@ -1578,29 +1617,43 @@ video_run_once() { : else rc=$? - if [ "$rc" -eq 124 ] 2>/dev/null; then - log_fail "[run] timeout after ${tmo}s" - else - log_fail "[run] $VIDEO_APP exited rc=$rc" - fi - printf 'END-RUN rc=%s\n' "$rc" >>"$logf" - grep -Eq "$suc" "$logf" - return $? fi else if "$VIDEO_APP" --config "$cfg" --loglevel "$lvl" >>"$logf" 2>&1; then : else rc=$? + fi + fi + + printf 'END-RUN rc=%s\n' "$rc" >>"$logf" + + if [ "$rc" -ne 0 ]; then + if [ "$rc" -eq 124 ] 2>/dev/null; then + log_fail "[run] timeout after ${tmo}s" + elif video_have_run_with_timeout; then + log_fail "[run] $VIDEO_APP exited rc=$rc" + else log_fail "[run] $VIDEO_APP exited rc=$rc (no timeout enforced)" - printf 'END-RUN rc=%s\n' "$rc" >>"$logf" - grep -Eq "$suc" "$logf" - return $? fi + printf 'VALIDATION=FAIL reason=nonzero-exit\n' >>"$logf" + return "$rc" + fi + + if video_iris_output_has_failure "$logf"; then + log_fail "[run] $VIDEO_APP reported an explicit ERROR or FAIL marker" + printf 'VALIDATION=FAIL reason=explicit-failure-marker\n' >>"$logf" + return 1 fi - printf 'END-RUN rc=0\n' >>"$logf" - grep -Eq "$suc" "$logf" + if ! grep -Eq "$suc" "$logf"; then + log_fail "[run] $VIDEO_APP did not report the required success marker" + printf 'VALIDATION=FAIL reason=success-marker-missing\n' >>"$logf" + return 1 + fi + + printf 'VALIDATION=PASS\n' >>"$logf" + return 0 } # -----------------------------------------------------------------------------