From 880d095da69805a289787076e6919a33448c3f39 Mon Sep 17 00:00:00 2001 From: Srikanth Muppandam Date: Fri, 4 Sep 2026 22:50:53 +0530 Subject: [PATCH 1/2] utils: add capability-driven hardware helpers Add shared runtime inventory and validation primitives for I2C, PCIe, USB, device-tree health, firmware discovery, and QRTR topology. Keep capability detection dynamic across platforms and expose bounded diagnostics for consumer suites. Signed-off-by: Srikanth Muppandam --- Runner/utils/functestlib.sh | 1084 ++++++++++++++++++++++++++++++++++- 1 file changed, 1078 insertions(+), 6 deletions(-) diff --git a/Runner/utils/functestlib.sh b/Runner/utils/functestlib.sh index 907c89d5..a5ae8867 100755 --- a/Runner/utils/functestlib.sh +++ b/Runner/utils/functestlib.sh @@ -295,6 +295,8 @@ check_dependencies() { # Support both: # check_dependencies date awk sed # check_dependencies "$deps" where deps="date awk sed" + # Set CHECK_DEPS_RECOVER=0 for image-validation suites that must never + # install packages while running on the target. if [ "$#" -eq 1 ]; then # Split the single string into args # shellcheck disable=SC2086 @@ -316,7 +318,8 @@ check_dependencies() { continue fi - if command -v pkg_check_dependencies_recover_enabled >/dev/null 2>&1; then + if [ "${CHECK_DEPS_RECOVER:-1}" = "1" ] && + command -v pkg_check_dependencies_recover_enabled >/dev/null 2>&1; then if pkg_check_dependencies_recover_enabled; then if pkg_ensure_command "$cmd"; then if command -v "$cmd" >/dev/null 2>&1; then @@ -3613,6 +3616,8 @@ dt_build_runtime_indexes() { -name '#clock-cells' -o \ -name '#reset-cells' -o \ -name regulator-name -o \ + -name '#power-domain-cells' -o \ + -name '#cooling-cells' -o \ -name '#mbox-cells' -o \ -name '#interconnect-cells' \ \) 2>/dev/null | @@ -3670,6 +3675,7 @@ dt_hw_capability_parse_args() { return 3 ;; esac + shift done DTRHC_AREAS=$(printf '%s' "$DTRHC_AREAS" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]') [ -n "$DTRHC_AREAS" ] || return 3 @@ -3840,6 +3846,51 @@ platform_device_driver_name() { basename "$pddn_driver_path" } +# kernel_modules_for_modalias +# Prints kernel module candidates resolved from the running image's alias +# database. Returns 0 when candidates are found, 1 when no alias matches, 2 +# when alias resolution is unavailable, and 3 for an empty modalias. +kernel_modules_for_modalias() { + kmfm_modalias="$1" + [ -n "$kmfm_modalias" ] || return 3 + command -v modprobe >/dev/null 2>&1 || return 2 + + kmfm_modules=$(modprobe -R "$kmfm_modalias" 2>/dev/null | awk 'NF && !seen[$0]++') + [ -n "$kmfm_modules" ] || return 1 + printf '%s\n' "$kmfm_modules" +} + +# kernel_module_runtime_origin +# Prints builtin, loaded, or the module file reported by the running image. +# Returns 1 when the optional runtime metadata is unavailable. +kernel_module_runtime_origin() { + kmro_module="$1" + [ -n "$kmro_module" ] || return 3 + + if command -v modinfo >/dev/null 2>&1; then + kmro_filename=$(modinfo -F filename "$kmro_module" 2>/dev/null || true) + case "$kmro_filename" in + '(builtin)') + printf '%s\n' "builtin" + return 0 + ;; + '') + ;; + *) + printf '%s\n' "module:$kmro_filename" + return 0 + ;; + esac + fi + + if is_module_loaded "$kmro_module"; then + printf '%s\n' "loaded" + return 0 + fi + + return 1 +} + # interconnect_debugfs_dir # Prints the ICC debugfs directory when its graph and summary are readable. # Returns 0 when available, otherwise 1. @@ -4185,6 +4236,927 @@ dt_validate_usb_host_runtime() { test_result_record "SKIP" "USB host controller runtime evidence is not exposed" } +############################################################################### +# i2c_collect_runtime_inventory +# Correlates enabled Qualcomm I2C controllers with runtime adapters and clients. +# Returns 0 for healthy applicable hardware, 1 for inconsistent runtime state, +# 2 when I2C is not exposed, and 3 for invalid arguments. +############################################################################### +i2c_collect_runtime_inventory() { + icri_result_dir="$1" + I2C_RUNTIME_CONTROLLER_COUNT=0 + I2C_RUNTIME_ADAPTER_COUNT=0 + I2C_RUNTIME_CLIENT_COUNT=0 + I2C_RUNTIME_BOUND_CLIENT_COUNT=0 + I2C_RUNTIME_WAITING_CLIENT_COUNT=0 + I2C_RUNTIME_REGISTERED_DRIVER_COUNT=0 + I2C_RUNTIME_FAILURE_REASON="" + icri_controller_unbound=0 + icri_declared_client_unbound=0 + icri_seen_adapters="" + icri_unbound_clients="" + icri_waiting_clients="" + + [ -n "$icri_result_dir" ] || return 3 + mkdir -p "$icri_result_dir" || return 1 + : >"$icri_result_dir/i2c_controllers.tsv" + : >"$icri_result_dir/i2c_adapters.tsv" + : >"$icri_result_dir/i2c_clients.tsv" + : >"$icri_result_dir/i2c_registered_drivers.log" + + for icri_driver_path in /sys/bus/i2c/drivers/*; do + [ -d "$icri_driver_path" ] || continue + I2C_RUNTIME_REGISTERED_DRIVER_COUNT=$((I2C_RUNTIME_REGISTERED_DRIVER_COUNT + 1)) + basename "$icri_driver_path" >>"$icri_result_dir/i2c_registered_drivers.log" + done + log_info "I2C driver registry: registered=$I2C_RUNTIME_REGISTERED_DRIVER_COUNT artifact=$icri_result_dir/i2c_registered_drivers.log" + + log_info "I2C validation: correlating enabled Qualcomm controllers, adapters, clients, and bound drivers" + + if dt_runtime_root >/dev/null 2>&1; then + if dt_list_compatible_nodes \ + 'qcom,(geni-i2c|i2c-geni)' \ + regex >"$icri_result_dir/i2c_controller_nodes.log"; then + while IFS= read -r icri_node; do + [ -n "$icri_node" ] || continue + I2C_RUNTIME_CONTROLLER_COUNT=$((I2C_RUNTIME_CONTROLLER_COUNT + 1)) + icri_compatible=$(dt_property_text "$icri_node" compatible 2>/dev/null || true) + icri_device="" + icri_driver="" + + if icri_device=$(find_platform_device_for_dt_node "$icri_node" 2>/dev/null); then + icri_driver=$(platform_device_driver_name "$icri_device" 2>/dev/null || true) + fi + + printf '%s\tcompatible=%s\tdevice=%s\tdriver=%s\n' \ + "$icri_node" \ + "${icri_compatible:-unknown}" \ + "${icri_device##*/}" \ + "${icri_driver:-unbound}" >>"$icri_result_dir/i2c_controllers.tsv" + log_info "[I2C-CONTROLLER] node=$icri_node device=${icri_device##*/} driver=${icri_driver:-unbound}" + + if [ -z "$icri_device" ] || [ -z "$icri_driver" ]; then + icri_controller_unbound=$((icri_controller_unbound + 1)) + fi + done <"$icri_result_dir/i2c_controller_nodes.log" + fi + fi + + for icri_adapter in \ + /sys/class/i2c-adapter/i2c-* \ + /sys/bus/i2c/devices/i2c-*; do + [ -d "$icri_adapter" ] || continue + icri_adapter_id=${icri_adapter##*/} + case " $icri_seen_adapters " in + *" $icri_adapter_id "*) + continue + ;; + esac + icri_seen_adapters="$icri_seen_adapters $icri_adapter_id" + I2C_RUNTIME_ADAPTER_COUNT=$((I2C_RUNTIME_ADAPTER_COUNT + 1)) + icri_adapter_name=$(cat "$icri_adapter/name" 2>/dev/null || true) + icri_adapter_path=$(readlink -f "$icri_adapter/device" 2>/dev/null || true) + icri_adapter_driver=$(platform_device_driver_name "$icri_adapter/device" 2>/dev/null || true) + icri_devnode="absent" + [ -c "/dev/$icri_adapter_id" ] && icri_devnode="present" + + printf '%s\tname=%s\tdriver=%s\tdevnode=%s\tpath=%s\n' \ + "$icri_adapter_id" \ + "${icri_adapter_name:-unknown}" \ + "${icri_adapter_driver:-framework-or-unexposed}" \ + "$icri_devnode" \ + "${icri_adapter_path:-unknown}" >>"$icri_result_dir/i2c_adapters.tsv" + log_info "[I2C-ADAPTER] adapter=$icri_adapter_id name=${icri_adapter_name:-unknown} driver=${icri_adapter_driver:-framework-or-unexposed} devnode=$icri_devnode" + done + + for icri_client in /sys/bus/i2c/devices/[0-9]*-[0-9a-fA-F]*; do + [ -d "$icri_client" ] || continue + I2C_RUNTIME_CLIENT_COUNT=$((I2C_RUNTIME_CLIENT_COUNT + 1)) + icri_client_name=$(cat "$icri_client/name" 2>/dev/null || true) + icri_client_driver=$(platform_device_driver_name "$icri_client" 2>/dev/null || true) + icri_client_of_node=$(readlink -f "$icri_client/of_node" 2>/dev/null || true) + icri_client_modalias=$(cat "$icri_client/modalias" 2>/dev/null || true) + icri_client_waiting=$(cat "$icri_client/waiting_for_supplier" 2>/dev/null || true) + icri_client_modules="" + icri_client_module_state="" + icri_client_dt_status="" + icri_client_dt_children=0 + icri_client_resources="" + icri_client_channels=0 + if [ -n "$icri_client_modalias" ] && + icri_client_module_lines=$(kernel_modules_for_modalias "$icri_client_modalias" 2>/dev/null); then + icri_client_modules=$(printf '%s\n' "$icri_client_module_lines" | tr '\n' ',' | sed 's/,$//') + for icri_client_module in $icri_client_module_lines; do + icri_client_origin=$(kernel_module_runtime_origin "$icri_client_module" 2>/dev/null || true) + [ -n "$icri_client_origin" ] || icri_client_origin="alias-resolved" + if [ -n "$icri_client_module_state" ]; then + icri_client_module_state="$icri_client_module_state,$icri_client_module=$icri_client_origin" + else + icri_client_module_state="$icri_client_module=$icri_client_origin" + fi + done + fi + if [ -n "$icri_client_of_node" ]; then + icri_client_dt_status=$(dt_property_text "$icri_client_of_node" status 2>/dev/null || true) + [ -n "$icri_client_dt_status" ] || icri_client_dt_status="okay-default" + icri_client_dt_children=$( + find "$icri_client_of_node" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | + wc -l | + tr -d '[:space:]' + ) + for icri_client_property in \ + vdd-supply reset-gpios resets clocks power-domains; do + [ -e "$icri_client_of_node/$icri_client_property" ] || continue + if [ -n "$icri_client_resources" ]; then + icri_client_resources="$icri_client_resources,$icri_client_property" + else + icri_client_resources="$icri_client_property" + fi + done + fi + [ -n "$icri_client_resources" ] || icri_client_resources="none-exposed" + for icri_client_channel in "$icri_client"/channel-*; do + [ -L "$icri_client_channel" ] || continue + icri_client_channels=$((icri_client_channels + 1)) + done + + if [ -n "$icri_client_driver" ]; then + I2C_RUNTIME_BOUND_CLIENT_COUNT=$((I2C_RUNTIME_BOUND_CLIENT_COUNT + 1)) + elif [ -n "$icri_client_of_node" ]; then + icri_declared_client_unbound=$((icri_declared_client_unbound + 1)) + icri_unbound_clients="$icri_unbound_clients ${icri_client##*/}(${icri_client_name:-unknown})" + case "$icri_client_waiting" in + 1|Y|y|yes|true) + I2C_RUNTIME_WAITING_CLIENT_COUNT=$((I2C_RUNTIME_WAITING_CLIENT_COUNT + 1)) + icri_waiting_clients="$icri_waiting_clients ${icri_client##*/}(${icri_client_name:-unknown})" + ;; + esac + log_warn "[I2C-UNBOUND] client=${icri_client##*/} name=${icri_client_name:-unknown} dt_status=$icri_client_dt_status waiting_for_supplier=${icri_client_waiting:-unexposed} module_state=${icri_client_module_state:-unresolved} dt_children=$icri_client_dt_children runtime_channels=$icri_client_channels resources=$icri_client_resources" + fi + + printf '%s\tname=%s\tdriver=%s\tmodalias=%s\tmodule_candidates=%s\tmodule_state=%s\twaiting_for_supplier=%s\tdt_status=%s\tdt_children=%s\truntime_channels=%s\tresources=%s\tof_node=%s\n' \ + "${icri_client##*/}" \ + "${icri_client_name:-unknown}" \ + "${icri_client_driver:-unbound}" \ + "${icri_client_modalias:-unexposed}" \ + "${icri_client_modules:-unresolved}" \ + "${icri_client_module_state:-unresolved}" \ + "${icri_client_waiting:-unexposed}" \ + "${icri_client_dt_status:-unknown}" \ + "$icri_client_dt_children" \ + "$icri_client_channels" \ + "$icri_client_resources" \ + "${icri_client_of_node:-none}" >>"$icri_result_dir/i2c_clients.tsv" + log_info "[I2C-CLIENT] client=${icri_client##*/} name=${icri_client_name:-unknown} driver=${icri_client_driver:-unbound} modalias=${icri_client_modalias:-unexposed} module_candidates=${icri_client_modules:-unresolved} waiting_for_supplier=${icri_client_waiting:-unexposed} of_node=${icri_client_of_node:-none}" + done + + log_info "I2C runtime summary: controllers=$I2C_RUNTIME_CONTROLLER_COUNT adapters=$I2C_RUNTIME_ADAPTER_COUNT clients=$I2C_RUNTIME_CLIENT_COUNT bound_clients=$I2C_RUNTIME_BOUND_CLIENT_COUNT waiting_for_supplier=$I2C_RUNTIME_WAITING_CLIENT_COUNT artifact=$icri_result_dir/i2c_adapters.tsv" + + if [ "$I2C_RUNTIME_CONTROLLER_COUNT" -eq 0 ] && + [ "$I2C_RUNTIME_ADAPTER_COUNT" -eq 0 ]; then + return 2 + fi + + if [ "$I2C_RUNTIME_CONTROLLER_COUNT" -gt 0 ] && + [ "$I2C_RUNTIME_ADAPTER_COUNT" -eq 0 ]; then + I2C_RUNTIME_FAILURE_REASON="enabled Qualcomm I2C controllers expose no runtime adapters" + elif [ "$icri_controller_unbound" -gt 0 ]; then + I2C_RUNTIME_FAILURE_REASON="$icri_controller_unbound enabled Qualcomm I2C controller(s) have no bound platform driver" + elif [ "$I2C_RUNTIME_WAITING_CLIENT_COUNT" -gt 0 ]; then + I2C_RUNTIME_FAILURE_REASON="$I2C_RUNTIME_WAITING_CLIENT_COUNT DT-declared I2C client(s) are waiting for unresolved suppliers:${icri_waiting_clients}" + elif [ "$icri_declared_client_unbound" -gt 0 ]; then + I2C_RUNTIME_FAILURE_REASON="$icri_declared_client_unbound DT-declared I2C client(s) are unbound:${icri_unbound_clients}" + fi + + [ -z "$I2C_RUNTIME_FAILURE_REASON" ] || return 1 + return 0 +} + +############################################################################### +# i2c_run_legacy_test +# Runs the image-provided i2c-msm-test compatibility path and verifies both its +# status and transfer markers. Returns 0 for success, 1 for a failed transfer, +# 2 when the tool is absent, 3 for invalid arguments, and 4 without an adapter. +############################################################################### +i2c_run_legacy_test() { + irlt_result_dir="$1" + irlt_requested_adapter="$2" + irlt_timeout="$3" + I2C_LEGACY_SELECTED_ADAPTER="" + + [ -n "$irlt_result_dir" ] && [ -n "$irlt_requested_adapter" ] || return 3 + case "$irlt_timeout" in + ''|*[!0-9]*|0) + return 3 + ;; + esac + + command -v i2c-msm-test >/dev/null 2>&1 || return 2 + + case "$irlt_requested_adapter" in + auto) + for irlt_devnode in /dev/i2c-*; do + [ -c "$irlt_devnode" ] || continue + I2C_LEGACY_SELECTED_ADAPTER="$irlt_devnode" + break + done + ;; + /dev/i2c-*) + I2C_LEGACY_SELECTED_ADAPTER="$irlt_requested_adapter" + ;; + *) + I2C_LEGACY_SELECTED_ADAPTER="/dev/i2c-$irlt_requested_adapter" + ;; + esac + + [ -n "$I2C_LEGACY_SELECTED_ADAPTER" ] && + [ -c "$I2C_LEGACY_SELECTED_ADAPTER" ] || return 4 + + log_info "I2C functional target: adapter=$I2C_LEGACY_SELECTED_ADAPTER command=i2c-msm-test timeout=${irlt_timeout}s" + if ! run_with_timeout_log \ + "$irlt_timeout" \ + "$irlt_result_dir/i2c_msm_test.log" \ + i2c-msm-test -v -D "$I2C_LEGACY_SELECTED_ADAPTER" -l; then + log_file_with_label "I2C-LEGACY" "$irlt_result_dir/i2c_msm_test.log" + return 1 + fi + + if ! grep -q 'Reading' "$irlt_result_dir/i2c_msm_test.log" || + ! grep -q 'ret:1' "$irlt_result_dir/i2c_msm_test.log"; then + log_file_with_label "I2C-LEGACY" "$irlt_result_dir/i2c_msm_test.log" + return 1 + fi + + return 0 +} + +############################################################################### +# pcie_collect_runtime_health +# Captures PCI device link, MSI, driver, and runtime-power evidence without +# changing link or power state. Returns 0 when inventory is readable, 1 when +# malformed, 2 when PCI is absent, and 3 for invalid arguments. +############################################################################### +pcie_collect_runtime_health() { + pcrh_result_dir="$1" + PCIE_RUNTIME_DEVICE_COUNT=0 + PCIE_RUNTIME_LINK_COUNT=0 + PCIE_RUNTIME_MSI_DEVICE_COUNT=0 + PCIE_RUNTIME_POWER_COUNT=0 + PCIE_RUNTIME_INACTIVE_PORT_COUNT=0 + PCIE_RUNTIME_FAILURE_REASON="" + pcrh_invalid_link_count=0 + + [ -n "$pcrh_result_dir" ] || return 3 + mkdir -p "$pcrh_result_dir" || return 1 + : >"$pcrh_result_dir/pcie_runtime.tsv" + + log_info "PCIe runtime validation: collecting driver, link, MSI, and runtime-power evidence" + + for pcrh_device in /sys/bus/pci/devices/*; do + [ -d "$pcrh_device" ] || continue + PCIE_RUNTIME_DEVICE_COUNT=$((PCIE_RUNTIME_DEVICE_COUNT + 1)) + pcrh_vendor=$(cat "$pcrh_device/vendor" 2>/dev/null || true) + pcrh_device_id=$(cat "$pcrh_device/device" 2>/dev/null || true) + pcrh_class=$(cat "$pcrh_device/class" 2>/dev/null || true) + pcrh_driver=$(platform_device_driver_name "$pcrh_device" 2>/dev/null || true) + pcrh_current_speed=$(cat "$pcrh_device/current_link_speed" 2>/dev/null || true) + pcrh_max_speed=$(cat "$pcrh_device/max_link_speed" 2>/dev/null || true) + pcrh_current_width=$(cat "$pcrh_device/current_link_width" 2>/dev/null || true) + pcrh_max_width=$(cat "$pcrh_device/max_link_width" 2>/dev/null || true) + pcrh_runtime_status=$(cat "$pcrh_device/power/runtime_status" 2>/dev/null || true) + pcrh_msi_count=0 + pcrh_link_state="unexposed" + + if [ -d "$pcrh_device/msi_irqs" ]; then + pcrh_msi_count=$(find "$pcrh_device/msi_irqs" -mindepth 1 -maxdepth 1 2>/dev/null | wc -l | tr -d '[:space:]') + fi + case "$pcrh_msi_count" in + ''|*[!0-9]*) + pcrh_msi_count=0 + ;; + esac + if [ "$pcrh_msi_count" -gt 0 ]; then + PCIE_RUNTIME_MSI_DEVICE_COUNT=$((PCIE_RUNTIME_MSI_DEVICE_COUNT + 1)) + fi + + if [ -n "$pcrh_current_speed" ] || [ -n "$pcrh_current_width" ]; then + PCIE_RUNTIME_LINK_COUNT=$((PCIE_RUNTIME_LINK_COUNT + 1)) + pcrh_link_state="up" + case "$pcrh_current_width" in + 0|0x|0X) + case "$pcrh_class" in + 0x0604*|0604*) + pcrh_link_state="inactive-bridge-port" + PCIE_RUNTIME_INACTIVE_PORT_COUNT=$((PCIE_RUNTIME_INACTIVE_PORT_COUNT + 1)) + ;; + *) + pcrh_link_state="invalid-zero-width" + pcrh_invalid_link_count=$((pcrh_invalid_link_count + 1)) + ;; + esac + ;; + esac + fi + [ -n "$pcrh_runtime_status" ] && PCIE_RUNTIME_POWER_COUNT=$((PCIE_RUNTIME_POWER_COUNT + 1)) + + printf '%s\tvendor=%s\tdevice=%s\tclass=%s\tdriver=%s\tcurrent_speed=%s\tmax_speed=%s\tcurrent_width=%s\tmax_width=%s\tlink_state=%s\tmsi_irqs=%s\truntime_status=%s\n' \ + "${pcrh_device##*/}" \ + "${pcrh_vendor:-unknown}" \ + "${pcrh_device_id:-unknown}" \ + "${pcrh_class:-unknown}" \ + "${pcrh_driver:-unbound}" \ + "${pcrh_current_speed:-unexposed}" \ + "${pcrh_max_speed:-unexposed}" \ + "${pcrh_current_width:-unexposed}" \ + "${pcrh_max_width:-unexposed}" \ + "$pcrh_link_state" \ + "$pcrh_msi_count" \ + "${pcrh_runtime_status:-unexposed}" >>"$pcrh_result_dir/pcie_runtime.tsv" + log_info "[PCIE] bdf=${pcrh_device##*/} id=${pcrh_vendor:-unknown}:${pcrh_device_id:-unknown} class=${pcrh_class:-unknown} driver=${pcrh_driver:-unbound} link=${pcrh_current_speed:-unexposed}/x${pcrh_current_width:-unexposed} link_state=$pcrh_link_state max=${pcrh_max_speed:-unexposed}/x${pcrh_max_width:-unexposed} msi_irqs=$pcrh_msi_count runtime_status=${pcrh_runtime_status:-unexposed}" + done + + if [ "$PCIE_RUNTIME_DEVICE_COUNT" -eq 0 ]; then + return 2 + fi + + if [ "$pcrh_invalid_link_count" -gt 0 ]; then + # Exported result for suite orchestration after this helper returns. + # shellcheck disable=SC2034 + PCIE_RUNTIME_FAILURE_REASON="$pcrh_invalid_link_count non-bridge PCIe device(s) report zero negotiated link width" + return 1 + fi + + log_info "PCIe runtime summary: devices=$PCIE_RUNTIME_DEVICE_COUNT links=$PCIE_RUNTIME_LINK_COUNT inactive_bridge_ports=$PCIE_RUNTIME_INACTIVE_PORT_COUNT msi_devices=$PCIE_RUNTIME_MSI_DEVICE_COUNT power_nodes=$PCIE_RUNTIME_POWER_COUNT artifact=$pcrh_result_dir/pcie_runtime.tsv" + return 0 +} + +############################################################################### +# usb_collect_host_inventory +# Captures USB root hubs, connected devices, interfaces, speeds, drivers, and +# runtime-power state. Returns 0 for host runtime, 1 for malformed host state, +# 2 when host mode is inactive, and 3 for invalid arguments. +############################################################################### +usb_collect_host_inventory() { + uchi_result_dir="$1" + USB_RUNTIME_ROOT_HUB_COUNT=0 + USB_RUNTIME_DEVICE_COUNT=0 + USB_RUNTIME_INTERFACE_COUNT=0 + USB_RUNTIME_BOUND_INTERFACE_COUNT=0 + USB_RUNTIME_FAILURE_REASON="" + + [ -n "$uchi_result_dir" ] || return 3 + mkdir -p "$uchi_result_dir" || return 1 + : >"$uchi_result_dir/usb_host_runtime.tsv" + + log_info "USB host validation: collecting root-hub, device, interface, speed, driver, and runtime-power evidence" + + for uchi_hub in /sys/bus/usb/devices/usb*; do + [ -d "$uchi_hub" ] || continue + USB_RUNTIME_ROOT_HUB_COUNT=$((USB_RUNTIME_ROOT_HUB_COUNT + 1)) + uchi_speed=$(cat "$uchi_hub/speed" 2>/dev/null || true) + uchi_runtime=$(cat "$uchi_hub/power/runtime_status" 2>/dev/null || true) + uchi_path=$(readlink -f "$uchi_hub" 2>/dev/null || true) + printf 'root-hub\t%s\tspeed=%s\truntime_status=%s\tpath=%s\n' \ + "${uchi_hub##*/}" \ + "${uchi_speed:-unknown}" \ + "${uchi_runtime:-unexposed}" \ + "${uchi_path:-unknown}" >>"$uchi_result_dir/usb_host_runtime.tsv" + log_info "[USB-ROOT] hub=${uchi_hub##*/} speed=${uchi_speed:-unknown} runtime_status=${uchi_runtime:-unexposed} path=${uchi_path:-unknown}" + done + + if [ "$USB_RUNTIME_ROOT_HUB_COUNT" -eq 0 ]; then + for uchi_role in /sys/class/usb_role/*/role /sys/class/typec/*/data_role; do + [ -r "$uchi_role" ] || continue + uchi_role_value=$(tr -d '[:space:]' <"$uchi_role" 2>/dev/null) + [ -n "$uchi_role_value" ] || continue + log_info "USB host mode is inactive: role_node=$uchi_role role=$uchi_role_value" + return 2 + done + # Exported result for suite orchestration after this helper returns. + # shellcheck disable=SC2034 + USB_RUNTIME_FAILURE_REASON="no USB root hubs are exposed and no inactive device role could be confirmed" + return 1 + fi + + for uchi_device in /sys/bus/usb/devices/*-*; do + [ -d "$uchi_device" ] || continue + case "${uchi_device##*/}" in + *:*) + continue + ;; + esac + [ -r "$uchi_device/idVendor" ] || continue + + USB_RUNTIME_DEVICE_COUNT=$((USB_RUNTIME_DEVICE_COUNT + 1)) + uchi_vendor=$(cat "$uchi_device/idVendor" 2>/dev/null || true) + uchi_product_id=$(cat "$uchi_device/idProduct" 2>/dev/null || true) + uchi_product=$(tr -d '\000' <"$uchi_device/product" 2>/dev/null || true) + uchi_speed=$(cat "$uchi_device/speed" 2>/dev/null || true) + uchi_runtime=$(cat "$uchi_device/power/runtime_status" 2>/dev/null || true) + log_info "[USB-DEVICE] device=${uchi_device##*/} id=${uchi_vendor:-unknown}:${uchi_product_id:-unknown} product=${uchi_product:-unknown} speed_mbps=${uchi_speed:-unknown} runtime_status=${uchi_runtime:-unexposed}" + + for uchi_interface in "$uchi_device":*; do + [ -d "$uchi_interface" ] || continue + [ -r "$uchi_interface/bInterfaceClass" ] || continue + USB_RUNTIME_INTERFACE_COUNT=$((USB_RUNTIME_INTERFACE_COUNT + 1)) + uchi_class=$(cat "$uchi_interface/bInterfaceClass" 2>/dev/null || true) + uchi_driver=$(platform_device_driver_name "$uchi_interface" 2>/dev/null || true) + [ -n "$uchi_driver" ] && USB_RUNTIME_BOUND_INTERFACE_COUNT=$((USB_RUNTIME_BOUND_INTERFACE_COUNT + 1)) + printf 'interface\t%s\tclass=%s\tdriver=%s\tparent=%s\n' \ + "${uchi_interface##*/}" \ + "${uchi_class:-unknown}" \ + "${uchi_driver:-unbound}" \ + "${uchi_device##*/}" >>"$uchi_result_dir/usb_host_runtime.tsv" + log_info "[USB-INTERFACE] interface=${uchi_interface##*/} class=${uchi_class:-unknown} driver=${uchi_driver:-unbound}" + done + done + + log_info "USB host summary: root_hubs=$USB_RUNTIME_ROOT_HUB_COUNT devices=$USB_RUNTIME_DEVICE_COUNT interfaces=$USB_RUNTIME_INTERFACE_COUNT bound_interfaces=$USB_RUNTIME_BOUND_INTERFACE_COUNT artifact=$uchi_result_dir/usb_host_runtime.tsv" + return 0 +} + +############################################################################### +# usb_validate_hid_runtime +# Validates binding for connected USB HID class interfaces. Returns 0 when all +# discovered interfaces are bound, 1 when any is unbound, 2 when absent, and 3 +# for invalid arguments. +############################################################################### +usb_validate_hid_runtime() { + uvhr_result_dir="$1" + USB_HID_INTERFACE_COUNT=0 + USB_HID_BOUND_COUNT=0 + USB_HID_UNBOUND_COUNT=0 + + [ -n "$uvhr_result_dir" ] || return 3 + mkdir -p "$uvhr_result_dir" || return 1 + : >"$uvhr_result_dir/usb_hid_runtime.tsv" + + log_info "USB HID validation: checking class interfaces and kernel-driver binding" + + for uvhr_class_file in /sys/bus/usb/devices/*:*/bInterfaceClass; do + [ -r "$uvhr_class_file" ] || continue + [ "$(cat "$uvhr_class_file" 2>/dev/null)" = "03" ] || continue + + uvhr_interface=$(dirname "$uvhr_class_file") + uvhr_interface_name=${uvhr_interface##*/} + uvhr_device_name=${uvhr_interface_name%%:*} + uvhr_device="/sys/bus/usb/devices/$uvhr_device_name" + uvhr_driver=$(platform_device_driver_name "$uvhr_interface" 2>/dev/null || true) + uvhr_vendor=$(cat "$uvhr_device/idVendor" 2>/dev/null || true) + uvhr_product_id=$(cat "$uvhr_device/idProduct" 2>/dev/null || true) + uvhr_product=$(tr -d '\000' <"$uvhr_device/product" 2>/dev/null || true) + USB_HID_INTERFACE_COUNT=$((USB_HID_INTERFACE_COUNT + 1)) + + if [ -n "$uvhr_driver" ]; then + USB_HID_BOUND_COUNT=$((USB_HID_BOUND_COUNT + 1)) + else + USB_HID_UNBOUND_COUNT=$((USB_HID_UNBOUND_COUNT + 1)) + fi + + printf '%s\tdevice=%s\tid=%s:%s\tproduct=%s\tdriver=%s\n' \ + "$uvhr_interface_name" \ + "$uvhr_device_name" \ + "${uvhr_vendor:-unknown}" \ + "${uvhr_product_id:-unknown}" \ + "${uvhr_product:-unknown}" \ + "${uvhr_driver:-unbound}" >>"$uvhr_result_dir/usb_hid_runtime.tsv" + log_info "[USB-HID] interface=$uvhr_interface_name device=$uvhr_device_name id=${uvhr_vendor:-unknown}:${uvhr_product_id:-unknown} product=${uvhr_product:-unknown} driver=${uvhr_driver:-unbound}" + done + + [ "$USB_HID_INTERFACE_COUNT" -gt 0 ] || return 2 + [ "$USB_HID_UNBOUND_COUNT" -eq 0 ] || return 1 + return 0 +} + +############################################################################### +# usb_validate_mass_storage_runtime +# Validates USB mass-storage binding and block nodes, then optionally performs +# a non-destructive 512-byte read. Returns 0 for healthy devices, 1 for a +# runtime failure, 2 when no fixture is present, and 3 for invalid arguments. +############################################################################### +usb_validate_mass_storage_runtime() { + uvms_result_dir="$1" + uvms_read_verify="$2" + uvms_wait_seconds="$3" + USB_MSD_DEVICE_COUNT=0 + USB_MSD_FAILURE_COUNT=0 + + [ -n "$uvms_result_dir" ] || return 3 + case "$uvms_read_verify" in + 0|1) + ;; + *) + return 3 + ;; + esac + case "$uvms_wait_seconds" in + ''|*[!0-9]*) + return 3 + ;; + esac + + mkdir -p "$uvms_result_dir" || return 1 + : >"$uvms_result_dir/usb_msd_runtime.tsv" + uvms_device_file="$uvms_result_dir/usb_msd_devices.list" + : >"$uvms_device_file" + + log_info "USB mass-storage validation: checking class binding, block-device creation, and optional read access" + + for uvms_class_file in /sys/bus/usb/devices/*:*/bInterfaceClass; do + [ -r "$uvms_class_file" ] || continue + [ "$(cat "$uvms_class_file" 2>/dev/null)" = "08" ] || continue + uvms_interface_name=$(basename "$(dirname "$uvms_class_file")") + uvms_device_name=${uvms_interface_name%%:*} + if ! grep -Fqx "$uvms_device_name" "$uvms_device_file"; then + printf '%s\n' "$uvms_device_name" >>"$uvms_device_file" + fi + done + + while IFS= read -r uvms_device_name; do + [ -n "$uvms_device_name" ] || continue + USB_MSD_DEVICE_COUNT=$((USB_MSD_DEVICE_COUNT + 1)) + uvms_device="/sys/bus/usb/devices/$uvms_device_name" + uvms_vendor=$(cat "$uvms_device/idVendor" 2>/dev/null || true) + uvms_product_id=$(cat "$uvms_device/idProduct" 2>/dev/null || true) + uvms_product=$(tr -d '\000' <"$uvms_device/product" 2>/dev/null || true) + uvms_driver="" + uvms_block_list="" + + for uvms_interface in "$uvms_device":*; do + [ -d "$uvms_interface" ] || continue + [ "$(cat "$uvms_interface/bInterfaceClass" 2>/dev/null)" = "08" ] || continue + uvms_driver=$(platform_device_driver_name "$uvms_interface" 2>/dev/null || true) + + if [ -z "$uvms_driver" ]; then + break + fi + + uvms_waited=0 + while [ "$uvms_waited" -le "$uvms_wait_seconds" ]; do + uvms_block_list="$({ + for uvms_block_path in \ + "$uvms_interface"/host*/target*/*/block/* \ + "$uvms_interface"/host*/target*/*/*/block/* \ + "$uvms_interface"/host*/target*/block/*; do + [ -e "$uvms_block_path" ] || continue + basename "$uvms_block_path" + done + } | sort -u)" + [ -n "$uvms_block_list" ] && break + [ "$uvms_waited" -eq "$uvms_wait_seconds" ] && break + sleep 1 + uvms_waited=$((uvms_waited + 1)) + done + break + done + + printf '%s\tid=%s:%s\tproduct=%s\tdriver=%s\tblocks=%s\n' \ + "$uvms_device_name" \ + "${uvms_vendor:-unknown}" \ + "${uvms_product_id:-unknown}" \ + "${uvms_product:-unknown}" \ + "${uvms_driver:-unbound}" \ + "${uvms_block_list:-none}" >>"$uvms_result_dir/usb_msd_runtime.tsv" + log_info "[USB-MSD] device=$uvms_device_name id=${uvms_vendor:-unknown}:${uvms_product_id:-unknown} product=${uvms_product:-unknown} driver=${uvms_driver:-unbound} blocks=${uvms_block_list:-none}" + + if [ -z "$uvms_driver" ]; then + USB_MSD_FAILURE_COUNT=$((USB_MSD_FAILURE_COUNT + 1)) + log_warn "USB mass-storage interface is unbound: device=$uvms_device_name" + continue + fi + + if [ -z "$uvms_block_list" ]; then + USB_MSD_FAILURE_COUNT=$((USB_MSD_FAILURE_COUNT + 1)) + log_warn "USB mass-storage device has no block device after ${uvms_wait_seconds}s: device=$uvms_device_name driver=$uvms_driver" + continue + fi + + if [ "$uvms_read_verify" -eq 1 ]; then + for uvms_block in $uvms_block_list; do + if [ ! -b "/dev/$uvms_block" ]; then + USB_MSD_FAILURE_COUNT=$((USB_MSD_FAILURE_COUNT + 1)) + log_warn "USB mass-storage block node is missing: /dev/$uvms_block" + continue + fi + + log_info "USB mass-storage read validation: device=/dev/$uvms_block bytes=512" + if ! dd \ + if="/dev/$uvms_block" \ + of=/dev/null \ + bs=512 \ + count=1 >"$uvms_result_dir/read_${uvms_block}.log" 2>&1; then + USB_MSD_FAILURE_COUNT=$((USB_MSD_FAILURE_COUNT + 1)) + log_file_with_label "USB-MSD-READ" "$uvms_result_dir/read_${uvms_block}.log" + fi + done + fi + done <"$uvms_device_file" + + [ "$USB_MSD_DEVICE_COUNT" -gt 0 ] || return 2 + [ "$USB_MSD_FAILURE_COUNT" -eq 0 ] || return 1 + return 0 +} + +############################################################################### +# dt_validate_pmic_glink_ucsi +# Validates the Qualcomm PMIC GLINK parent, UCSI auxiliary driver, and Type-C +# runtime exposure only when an enabled PMIC GLINK DT node declares support. +############################################################################### +dt_validate_pmic_glink_ucsi() { + dtvpgu_result_dir="$1" + dtvpgu_nodes_file="$dtvpgu_result_dir/pmic_glink_nodes.log" + dtvpgu_parent_count=0 + dtvpgu_aux_count=0 + dtvpgu_typec_count=0 + dtvpgu_role_invalid_count=0 + + [ -n "$dtvpgu_result_dir" ] || return 3 + : >"$dtvpgu_nodes_file" + + log_info "PMIC GLINK/UCSI validation: checking enabled DT parents, auxiliary binding, Type-C ports, and role state" + + if ! dt_list_compatible_nodes \ + '(^|[[:space:]])qcom,([^[:space:]]+-)?pmic-glink([[:space:]]|$)' \ + regex >"$dtvpgu_nodes_file"; then + test_result_record "SKIP" "Qualcomm PMIC GLINK UCSI capability is not enabled in the runtime device tree" + return 0 + fi + + while IFS= read -r dtvpgu_node; do + [ -n "$dtvpgu_node" ] || continue + dtvpgu_parent_count=$((dtvpgu_parent_count + 1)) + dtvpgu_compatible="$(dt_property_text "$dtvpgu_node" compatible 2>/dev/null || true)" + log_info "[PMIC-GLINK-DT] node=$dtvpgu_node compatible=${dtvpgu_compatible:-unknown}" + + if dtvpgu_device="$(find_platform_device_for_dt_node "$dtvpgu_node" 2>/dev/null)" && + dtvpgu_driver="$(platform_device_driver_name "$dtvpgu_device" 2>/dev/null)"; then + test_result_record "PASS" "PMIC GLINK parent is bound: device=$(basename "$dtvpgu_device") driver=$dtvpgu_driver" + else + test_result_record "FAIL" "Enabled PMIC GLINK node has no bound runtime platform driver: ${dtvpgu_node##*/}" + fi + done <"$dtvpgu_nodes_file" + + for dtvpgu_aux in /sys/bus/auxiliary/devices/pmic_glink.ucsi.*; do + [ -d "$dtvpgu_aux" ] || continue + dtvpgu_aux_count=$((dtvpgu_aux_count + 1)) + if [ -L "$dtvpgu_aux/driver" ]; then + dtvpgu_aux_driver="$(basename "$(readlink -f "$dtvpgu_aux/driver" 2>/dev/null)" 2>/dev/null || true)" + log_info "[PMIC-GLINK-UCSI] auxiliary=${dtvpgu_aux##*/} driver=${dtvpgu_aux_driver:-unknown}" + case "$dtvpgu_aux_driver" in + *pmic_glink_ucsi) + test_result_record "PASS" "PMIC GLINK UCSI auxiliary device is bound: device=${dtvpgu_aux##*/} driver=$dtvpgu_aux_driver" + ;; + *) + test_result_record "FAIL" "PMIC GLINK UCSI auxiliary device has unexpected driver: device=${dtvpgu_aux##*/} driver=${dtvpgu_aux_driver:-unknown}" + ;; + esac + else + log_info "[PMIC-GLINK-UCSI] auxiliary=${dtvpgu_aux##*/} driver=unbound" + test_result_record "FAIL" "PMIC GLINK UCSI auxiliary device is present but unbound: ${dtvpgu_aux##*/}" + fi + done + + if [ "$dtvpgu_aux_count" -eq 0 ]; then + test_result_record "FAIL" "PMIC GLINK is declared but no UCSI auxiliary device is exposed" + elif [ "$dtvpgu_aux_count" -lt "$dtvpgu_parent_count" ]; then + test_result_record "FAIL" "Only $dtvpgu_aux_count UCSI auxiliary device(s) are exposed for $dtvpgu_parent_count PMIC GLINK parent(s)" + fi + + : >"$dtvpgu_result_dir/typec_ports.log" + for dtvpgu_port in /sys/class/typec/port*; do + [ -d "$dtvpgu_port" ] || continue + case "${dtvpgu_port##*/}" in + *-partner*) + continue + ;; + esac + dtvpgu_port_path="$(readlink -f "$dtvpgu_port" 2>/dev/null || true)" + case "$dtvpgu_port_path" in + *pmic_glink.ucsi.*) + ;; + *) + continue + ;; + esac + + dtvpgu_typec_count=$((dtvpgu_typec_count + 1)) + dtvpgu_power_role="$(cat "$dtvpgu_port/power_role" 2>/dev/null || true)" + dtvpgu_data_role="$(cat "$dtvpgu_port/data_role" 2>/dev/null || true)" + printf '%s\tpower_role=%s\tdata_role=%s\tpath=%s\n' \ + "${dtvpgu_port##*/}" \ + "${dtvpgu_power_role:-unknown}" \ + "${dtvpgu_data_role:-unknown}" \ + "${dtvpgu_port_path:-unknown}" >>"$dtvpgu_result_dir/typec_ports.log" + log_info "[PMIC-GLINK-UCSI] port=${dtvpgu_port##*/} power_role=${dtvpgu_power_role:-unknown} data_role=${dtvpgu_data_role:-unknown}" + + case "$dtvpgu_power_role" in + source|sink|*'[source]'*|*'[sink]'*) + ;; + *) + dtvpgu_role_invalid_count=$((dtvpgu_role_invalid_count + 1)) + continue + ;; + esac + case "$dtvpgu_data_role" in + host|device|*'[host]'*|*'[device]'*) + ;; + *) + dtvpgu_role_invalid_count=$((dtvpgu_role_invalid_count + 1)) + ;; + esac + done + + if [ "$dtvpgu_typec_count" -eq 0 ]; then + test_result_record "FAIL" "PMIC GLINK UCSI is declared but no UCSI-backed Type-C ports are exposed" + elif [ "$dtvpgu_role_invalid_count" -gt 0 ]; then + test_result_record "FAIL" "PMIC GLINK UCSI has $dtvpgu_role_invalid_count Type-C port(s) with unreadable or invalid role state" + else + test_result_record "PASS" "PMIC GLINK UCSI exposes $dtvpgu_typec_count Type-C port(s) with valid power and data roles" + fi +} + +############################################################################### +# dt_validate_thermal_runtime +# Validates readable and plausible thermal-zone data when thermal zones are +# declared. Cooling devices are required only when a cooling provider exists. +############################################################################### +dt_validate_thermal_runtime() { + dtvtr_root="$1" + dtvtr_result_dir="$2" + dtvtr_zone_file="$dtvtr_result_dir/thermal_zones.log" + dtvtr_cooling_file="$dtvtr_result_dir/cooling_devices.log" + dtvtr_zone_count=0 + dtvtr_readable_count=0 + dtvtr_unreadable_count=0 + dtvtr_invalid_count=0 + dtvtr_cooling_count=0 + dtvtr_cooling_invalid_count=0 + dtvtr_declared_count=0 + + [ -d "$dtvtr_root" ] && [ -n "$dtvtr_result_dir" ] || return 3 + + log_info "Thermal validation: checking enabled DT thermal zones, runtime temperatures, and cooling-device state" + + if [ ! -d "$dtvtr_root/thermal-zones" ]; then + test_result_record "SKIP" "Runtime device tree does not declare thermal zones" + return 0 + fi + + for dtvtr_declared in "$dtvtr_root/thermal-zones"/*; do + [ -d "$dtvtr_declared" ] || continue + dt_node_enabled "$dtvtr_declared" || continue + dtvtr_declared_count=$((dtvtr_declared_count + 1)) + done + + if [ "$dtvtr_declared_count" -eq 0 ]; then + test_result_record "SKIP" "Runtime device tree has no enabled thermal zones" + return 0 + fi + + : >"$dtvtr_zone_file" + for dtvtr_zone in /sys/class/thermal/thermal_zone*; do + [ -d "$dtvtr_zone" ] || continue + dtvtr_zone_count=$((dtvtr_zone_count + 1)) + dtvtr_type="$(cat "$dtvtr_zone/type" 2>/dev/null || true)" + dtvtr_temp="$(cat "$dtvtr_zone/temp" 2>/dev/null || true)" + printf '%s\ttype=%s\ttemp_mC=%s\n' \ + "${dtvtr_zone##*/}" \ + "${dtvtr_type:-unknown}" \ + "${dtvtr_temp:-unreadable}" >>"$dtvtr_zone_file" + log_info "[THERMAL] zone=${dtvtr_zone##*/} type=${dtvtr_type:-unknown} temp_mC=${dtvtr_temp:-unreadable}" + + if [ -z "$dtvtr_type" ]; then + dtvtr_invalid_count=$((dtvtr_invalid_count + 1)) + continue + fi + + if [ -z "$dtvtr_temp" ]; then + dtvtr_unreadable_count=$((dtvtr_unreadable_count + 1)) + continue + fi + + case "$dtvtr_temp" in + -*) + dtvtr_temp_digits=${dtvtr_temp#-} + ;; + *) + dtvtr_temp_digits=$dtvtr_temp + ;; + esac + + case "$dtvtr_temp_digits" in + ''|*[!0-9]*) + dtvtr_invalid_count=$((dtvtr_invalid_count + 1)) + ;; + *) + if [ "$dtvtr_temp" -lt -100000 ] || [ "$dtvtr_temp" -gt 250000 ]; then + dtvtr_invalid_count=$((dtvtr_invalid_count + 1)) + else + dtvtr_readable_count=$((dtvtr_readable_count + 1)) + fi + ;; + esac + done + + if [ "$dtvtr_zone_count" -eq 0 ]; then + test_result_record "FAIL" "Thermal zones are declared but no runtime thermal zones are exposed" + elif [ "$dtvtr_invalid_count" -gt 0 ]; then + test_result_record "FAIL" "Thermal runtime has $dtvtr_invalid_count malformed or implausible zone(s) out of $dtvtr_zone_count" + elif [ "$dtvtr_readable_count" -eq 0 ]; then + test_result_record "FAIL" "Thermal zones are exposed but none currently provide a readable temperature" + else + test_result_record "PASS" "Thermal runtime exposes $dtvtr_readable_count readable zone(s) with plausible temperatures" + if [ "$dtvtr_unreadable_count" -gt 0 ]; then + test_result_record "SKIP" "$dtvtr_unreadable_count optional or aggregate thermal zone(s) do not currently expose temperature data" + fi + fi + + : >"$dtvtr_cooling_file" + for dtvtr_cooling in /sys/class/thermal/cooling_device*; do + [ -d "$dtvtr_cooling" ] || continue + dtvtr_cooling_count=$((dtvtr_cooling_count + 1)) + dtvtr_cooling_type="$(cat "$dtvtr_cooling/type" 2>/dev/null || true)" + dtvtr_cooling_state="$(cat "$dtvtr_cooling/cur_state" 2>/dev/null || true)" + dtvtr_cooling_max="$(cat "$dtvtr_cooling/max_state" 2>/dev/null || true)" + printf '%s\ttype=%s\tstate=%s\tmax_state=%s\n' \ + "${dtvtr_cooling##*/}" \ + "${dtvtr_cooling_type:-unknown}" \ + "${dtvtr_cooling_state:-unknown}" \ + "${dtvtr_cooling_max:-unknown}" >>"$dtvtr_cooling_file" + log_info "[THERMAL] cooling=${dtvtr_cooling##*/} type=${dtvtr_cooling_type:-unknown} state=${dtvtr_cooling_state:-unknown} max_state=${dtvtr_cooling_max:-unknown}" + + dtvtr_cooling_valid=1 + case "$dtvtr_cooling_state" in + ''|*[!0-9]*) + dtvtr_cooling_valid=0 + ;; + esac + case "$dtvtr_cooling_max" in + ''|*[!0-9]*) + dtvtr_cooling_valid=0 + ;; + esac + + if [ "$dtvtr_cooling_valid" -eq 0 ] || + [ "$dtvtr_cooling_state" -gt "$dtvtr_cooling_max" ]; then + dtvtr_cooling_invalid_count=$((dtvtr_cooling_invalid_count + 1)) + fi + done + + if dt_list_enabled_property_nodes "$dtvtr_root" '#cooling-cells' >/dev/null 2>&1; then + if [ "$dtvtr_cooling_count" -gt 0 ]; then + if [ "$dtvtr_cooling_invalid_count" -eq 0 ]; then + test_result_record "PASS" "Thermal runtime exposes $dtvtr_cooling_count valid cooling device(s)" + else + test_result_record "FAIL" "Thermal runtime has $dtvtr_cooling_invalid_count malformed cooling device(s) out of $dtvtr_cooling_count" + fi + else + test_result_record "FAIL" "Cooling providers are declared but no runtime cooling devices are exposed" + fi + else + test_result_record "SKIP" "Runtime device tree does not declare a cooling-device provider" + fi +} + +############################################################################### +# dt_capture_power_runtime +# Retains optional regulator and generic power-domain debugfs summaries. Their +# absence is not a failure because production images may disable debugfs. +############################################################################### +dt_capture_power_runtime() { + dtcpr_result_dir="$1" + dtcpr_regulator="/sys/kernel/debug/regulator/regulator_summary" + dtcpr_genpd="/sys/kernel/debug/pm_genpd/pm_genpd_summary" + + [ -n "$dtcpr_result_dir" ] || return 3 + + log_info "Power evidence validation: checking optional regulator and generic power-domain debugfs summaries" + + if [ -r "$dtcpr_regulator" ]; then + if cp "$dtcpr_regulator" "$dtcpr_result_dir/regulator_summary.log"; then + dtcpr_regulator_lines=$(wc -l <"$dtcpr_result_dir/regulator_summary.log" | tr -d '[:space:]') + log_info "[POWER] regulator_summary source=$dtcpr_regulator lines=${dtcpr_regulator_lines:-0} artifact=$dtcpr_result_dir/regulator_summary.log" + test_result_record "PASS" "Regulator runtime summary was captured" + else + test_result_record "FAIL" "Regulator runtime summary is readable but could not be captured" + fi + else + test_result_record "SKIP" "Regulator debugfs summary is not exposed" + fi + + if [ -r "$dtcpr_genpd" ]; then + if cp "$dtcpr_genpd" "$dtcpr_result_dir/power_domain_summary.log"; then + dtcpr_genpd_lines=$(wc -l <"$dtcpr_result_dir/power_domain_summary.log" | tr -d '[:space:]') + log_info "[POWER] power_domain_summary source=$dtcpr_genpd lines=${dtcpr_genpd_lines:-0} artifact=$dtcpr_result_dir/power_domain_summary.log" + test_result_record "PASS" "Generic power-domain runtime summary was captured" + else + test_result_record "FAIL" "Generic power-domain summary is readable but could not be captured" + fi + else + test_result_record "SKIP" "Generic power-domain debugfs summary is not exposed" + fi +} + ############################################################################### # dt_validate_tee_runtime # Distinguishes declared OP-TEE from a generic TEE device, which is commonly @@ -4385,7 +5357,7 @@ dt_validate_runtime_hardware_capabilities() { if dt_hw_capability_area_enabled "health"; then scan_dmesg_errors \ "$dtrhc_result_dir" \ - 'of|device.tree|devicetree|qcom.*(smmu|pcie|ufs|sdhci|dwc3|usb|ethqos|dpu|mdss)' \ + 'of|device.tree|devicetree|qcom.*(smmu|pcie|ufs|sdhci|dwc3|usb|ethqos|dpu|mdss)|pmic.gl|ucsi|thermal|tsens|rpmh|regulator|power.domain' \ '-517|EPROBE_DEFER|deferred probe|dummy regulator|supply [^ ]+ not found' || true log_info "Captured DT and controller kernel-health snapshot" fi @@ -4542,11 +5514,13 @@ dt_validate_runtime_hardware_capabilities() { dt_validate_property_provider "$dtrhc_root" "Clock" "#clock-cells" "$dtrhc_result_dir" dt_validate_property_provider "$dtrhc_root" "Reset" "#reset-cells" "$dtrhc_result_dir" dt_validate_property_provider "$dtrhc_root" "Regulator" "regulator-name" "$dtrhc_result_dir" + dt_validate_property_provider "$dtrhc_root" "Power domain" "#power-domain-cells" "$dtrhc_result_dir" dt_validate_property_provider "$dtrhc_root" "Mailbox" "#mbox-cells" "$dtrhc_result_dir" dt_validate_property_provider "$dtrhc_root" "Interconnect" "#interconnect-cells" "$dtrhc_result_dir" dt_validate_node_inventory "RPMh" 'qcom,.*rpmh' "$dtrhc_result_dir" dt_validate_platform_capability "LLCC" 'qcom,.*llcc' "$dtrhc_result_dir" dt_validate_platform_capability "SMMU" 'qcom,.*smmu-500' "$dtrhc_result_dir" + dt_capture_power_runtime "$dtrhc_result_dir" dt_summary_end_area fi @@ -4554,6 +5528,7 @@ dt_validate_runtime_hardware_capabilities() { dt_summary_begin_area "$dtrhc_result_dir" "USB" dt_validate_platform_capability "USB" 'qcom,.*(dwc3|usb)' "$dtrhc_result_dir" dt_validate_usb_host_runtime + dt_validate_pmic_glink_ucsi "$dtrhc_result_dir" dt_validate_runtime_path "USB gadget controller" '/sys/class/udc/*' dt_summary_end_area fi @@ -4614,6 +5589,7 @@ dt_validate_runtime_hardware_capabilities() { if dt_hw_capability_area_enabled "health"; then dt_summary_begin_area "$dtrhc_result_dir" "Kernel health" + dt_validate_thermal_runtime "$dtrhc_root" "$dtrhc_result_dir" if [ -s "$dtrhc_result_dir/dmesg_errors.log" ]; then test_result_record "FAIL" "Relevant DT or hardware-controller errors were found in the captured kernel log" else @@ -4655,15 +5631,29 @@ list_remoteproc_instances() { ############################################################################### # find_image_firmware -# Prints the first matching image-provided firmware path under the standard -# firmware roots, accepting uncompressed, .xz, and .zst files. Returns 0 on a -# match, 1 when no asset is exposed, and 3 when no name is supplied. +# Prints the first matching image-provided firmware path under the standard or +# running-kernel firmware roots, accepting uncompressed, .xz, and .zst files. +# Returns 0 on a match, 1 when no asset is exposed, and 3 when no name is +# supplied. ############################################################################### find_image_firmware() { firmware_name="$1" + firmware_release=$(uname -r 2>/dev/null || true) [ -n "$firmware_name" ] || return 3 - for firmware_root in /lib/firmware /usr/lib/firmware; do + for firmware_root in \ + "/lib/firmware/$firmware_release" \ + "/usr/lib/firmware/$firmware_release" \ + /lib/firmware \ + /usr/lib/firmware; do + [ -n "$firmware_release" ] || { + case "$firmware_root" in + /lib/firmware/|/usr/lib/firmware/) + continue + ;; + esac + } + [ -d "$firmware_root" ] || continue for firmware_path in \ "$firmware_root/$firmware_name" \ "$firmware_root/$firmware_name.xz" \ @@ -5907,6 +6897,88 @@ minkipc_prepare_test_packages() { return 0 } +# qrtr_runtime_present +# Reports whether the running kernel exposes QRTR transport evidence. Installed +# tools or modules that are not loaded are not treated as hardware evidence. +qrtr_runtime_present() { + if [ -d /sys/bus/qrtr ] || [ -r /proc/net/qrtr ] || [ -d /sys/module/qrtr ]; then + return 0 + fi + + if is_module_loaded qrtr; then + return 0 + fi + + return 1 +} + +# qrtr_capture_topology [timeout-seconds] +# Runs one bounded, read-only qrtr-lookup inventory and validates its tabular +# header. Returns 0 for a valid snapshot, 1 for a broken query, 2 when QRTR or +# qrtr-lookup is unavailable, and 3 for invalid arguments. +qrtr_capture_topology() { + qct_output_file="$1" + qct_timeout="${2:-${QRTR_LOOKUP_TIMEOUT:-10}}" + qct_lookup_bin="${QRTR_LOOKUP_BIN:-qrtr-lookup}" + + [ -n "$qct_output_file" ] || return 3 + case "$qct_timeout" in + ''|*[!0-9]*|0) + return 3 + ;; + esac + + qrtr_runtime_present || return 2 + command -v "$qct_lookup_bin" >/dev/null 2>&1 || return 2 + + qct_output_dir=$(dirname "$qct_output_file") + mkdir -p "$qct_output_dir" || return 1 + rm -f "$qct_output_file" + + if ! run_with_timeout_log \ + "$qct_timeout" \ + "$qct_output_file" \ + "$qct_lookup_bin"; then + return 1 + fi + + if ! awk ' + NR == 1 && $1 == "Service" && $2 == "Version" && + $3 == "Instance" && $4 == "Node" && $5 == "Port" { + valid=1 + } + END { exit !valid } + ' "$qct_output_file"; then + return 1 + fi + + return 0 +} + +# qrtr_topology_has_service [version] [instance] +# Matches a qrtr-lookup row. Empty version or instance arguments act as +# wildcards, allowing each consumer to enforce only its documented contract. +qrtr_topology_has_service() { + qths_file="$1" + qths_service="$2" + qths_version="${3:-}" + qths_instance="${4:-}" + + [ -r "$qths_file" ] && [ -n "$qths_service" ] || return 3 + + awk \ + -v service="$qths_service" \ + -v version="$qths_version" \ + -v instance="$qths_instance" ' + NR > 1 && $1 == service && + (version == "" || $2 == version) && + (instance == "" || $3 == instance) { + found=1 + } + END { exit !found } + ' "$qths_file" +} + ############################################################################### # Qualcomm RMTFS runtime helpers ############################################################################### From 4a137a8f40156bc80ae7a8b14c507c053db1bb0f Mon Sep 17 00:00:00 2001 From: Srikanth Muppandam Date: Fri, 4 Sep 2026 22:51:27 +0530 Subject: [PATCH 2/2] usb: add capability-driven runtime validation Add shared-inventory consumers for USB host, HID, and mass-storage runtime checks, including bounded optional read verification. Extend device-tree capability coverage for PMIC GLINK UCSI, thermal, regulator, and power-domain evidence while preserving clean skips for absent optional hardware. Signed-off-by: Srikanth Muppandam --- .../DeviceTree_HW_Capability_Validation.yaml | 2 +- .../README.md | 51 +++- .../Kernel/Baseport/USB/usb_msd/README.md | 34 ++- .../suites/Kernel/Baseport/USB/usb_msd/run.sh | 269 +++++++++--------- .../Kernel/Baseport/USB/usb_msd/usb_msd.yaml | 7 +- .../suites/Kernel/Baseport/USBHost/README.md | 26 +- .../Kernel/Baseport/USBHost/USBHost.yaml | 6 +- Runner/suites/Kernel/Baseport/USBHost/run.sh | 141 ++++++--- .../suites/Kernel/Baseport/usb_hid/README.md | 14 +- Runner/suites/Kernel/Baseport/usb_hid/run.sh | 98 +++---- .../Kernel/Baseport/usb_hid/usb_hid.yaml | 3 +- 11 files changed, 392 insertions(+), 259 deletions(-) diff --git a/Runner/suites/Kernel/Baseport/DeviceTree_HW_Capability_Validation/DeviceTree_HW_Capability_Validation.yaml b/Runner/suites/Kernel/Baseport/DeviceTree_HW_Capability_Validation/DeviceTree_HW_Capability_Validation.yaml index 8eb025a9..6dc9d998 100755 --- a/Runner/suites/Kernel/Baseport/DeviceTree_HW_Capability_Validation/DeviceTree_HW_Capability_Validation.yaml +++ b/Runner/suites/Kernel/Baseport/DeviceTree_HW_Capability_Validation/DeviceTree_HW_Capability_Validation.yaml @@ -1,7 +1,7 @@ metadata: name: DeviceTree_HW_Capability_Validation format: "Lava-Test Test Definition 1.0" - description: "Validate runtime device-tree identity, enabled hardware controllers, driver binding, and kernel health" + description: "Validate runtime device-tree identity, controller binding, PMIC GLINK/UCSI, thermal, power, and kernel health" os: - linux scope: diff --git a/Runner/suites/Kernel/Baseport/DeviceTree_HW_Capability_Validation/README.md b/Runner/suites/Kernel/Baseport/DeviceTree_HW_Capability_Validation/README.md index 27934a08..e1cf9e5f 100644 --- a/Runner/suites/Kernel/Baseport/DeviceTree_HW_Capability_Validation/README.md +++ b/Runner/suites/Kernel/Baseport/DeviceTree_HW_Capability_Validation/README.md @@ -19,15 +19,15 @@ attached peripherals. | `boot` | Optional `/chosen` `stdout-path` and `bootargs`, including stdout target resolution through `/aliases` | | `cpu-memory` | Enabled DT CPU nodes cover kernel-exposed CPUs; memory nodes expose `reg`; reserved-memory children expose `reg` or `size`; declared NUMA node identifiers are inventoried | | `interrupts` | Readable `/proc/interrupts`, interrupt-controller providers, and GIC, PDC, and GPIO structural or runtime evidence as appropriate | -| `fabric` | Clock, reset, regulator, mailbox, interconnect, RPMh, LLCC, and SMMU provider structure with platform-driver evidence where applicable | +| `fabric` | Clock, reset, regulator, power-domain, mailbox, interconnect, RPMh, LLCC, and SMMU provider structure with platform-driver evidence where applicable; optional regulator and generic power-domain debugfs summaries are retained when exposed | | `storage` | Enabled UFS, SDHCI, and SPI controller bindings; SPI-NOR DT inventory; optional NVMe and MTD runtime evidence | -| `usb` | Enabled USB controller binding plus optional host and gadget-controller runtime evidence, including role-state reporting when host mode is inactive | +| `usb` | Enabled USB controller binding plus optional host and gadget-controller runtime evidence, including role-state reporting when host mode is inactive; enabled Qualcomm PMIC GLINK declarations require a bound parent, UCSI auxiliary device, and Type-C port exposure | | `pcie` | Enabled PCIe controller binding plus optional endpoint enumeration evidence | | `network` | Enabled Ethernet controller binding, non-virtual network-runtime evidence, and separate Wi-Fi/Bluetooth DT inventory without requiring an external module or link | | `multimedia` | Display and GPU binding, audio and camera DT inventory, and optional DRM and ALSA runtime evidence | | `remoteproc` | Enabled Qualcomm remoteproc inventory, memory-region and firmware-name evidence, firmware provisioning status, and runtime instance correlation | | `security` | OP-TEE, SCM, TPM, and KVM EL2 DT inventory with optional TEE, TPM, and KVM runtime evidence, distinguishing a likely Qualcomm TEE flow from declared OP-TEE | -| `health` | Captured relevant DT and controller probe failures before and after validation | +| `health` | Captured relevant DT and controller probe failures plus capability-gated thermal-zone readings and cooling-device exposure. Optional or aggregate zones without a current temperature are reported separately when other readable zones establish runtime health | Existing focused suites remain responsible for active functional testing. For example, PCIe endpoint enumeration, Ethernet traffic, audio capture, @@ -38,12 +38,15 @@ interconnect voting, and EDAC counter behavior are not duplicated here. - **PASS**: Required DT structure is present and a discovered controller is bound. - **SKIP**: The runtime tree does not expose an optional capability, remoteproc firmware is not provisioned, an external fixture or USB role is inactive, a - framework provider has no direct runtime binding, or a required base utility - is absent. + framework provider has no direct runtime binding, optional debugfs evidence + is unavailable, an optional or aggregate thermal zone has no current + temperature, or a required base utility is absent. - **FAIL**: Required DT identity, CPU, or memory data is invalid; a hardware controller that requires an MMIO register range lacks `reg`; or the captured - kernel log reports relevant failures. Framework children and firmware nodes - without direct platform-device representation are reported as **SKIP**. + kernel log reports relevant failures. Enabled PMIC GLINK/UCSI or thermal + capabilities that do not produce their required runtime objects also fail. + Framework children and firmware nodes without direct platform-device + representation are reported as **SKIP**. An attached USB device, PCIe endpoint, Ethernet cable, display, camera, or wireless module is not required. Those are fixture-level validations. @@ -71,6 +74,13 @@ comma-separated set. The accepted area names correspond to the table above: ./run.sh --list-areas ``` +Run only the newly extended validation areas: + +```sh +./run.sh --area usb +./run.sh --area fabric,health +``` + The suite builds compatible, provider-property, and platform-device indexes once at startup, then reuses them for each selected area. This avoids repeated full device-tree and platform-bus scans. It deliberately does not run checks in @@ -88,7 +98,32 @@ results/DeviceTree_HW_Capability_Validation/ This directory also contains `dt_compatible_index.log`, `dt_property_index.log`, `platform_device_index.log`, and -`dt_area_summary.tsv`. +`dt_area_summary.tsv`. Capability-dependent runs additionally retain +`pmic_glink_nodes.log`, `typec_ports.log`, `thermal_zones.log`, +`cooling_devices.log`, `regulator_summary.log`, and +`power_domain_summary.log` when the corresponding runtime interfaces exist. + +Check the final result, area summary, and capability evidence: + +```sh +cat DeviceTree_HW_Capability_Validation.res +cat results/DeviceTree_HW_Capability_Validation/dt_area_summary.tsv +cat results/DeviceTree_HW_Capability_Validation/pmic_glink_nodes.log +cat results/DeviceTree_HW_Capability_Validation/typec_ports.log +cat results/DeviceTree_HW_Capability_Validation/thermal_zones.log +cat results/DeviceTree_HW_Capability_Validation/cooling_devices.log +cat results/DeviceTree_HW_Capability_Validation/dmesg_errors.log +``` + +The optional regulator and power-domain summaries can be checked when debugfs +exposes them: + +```sh +test -r results/DeviceTree_HW_Capability_Validation/regulator_summary.log && \ + cat results/DeviceTree_HW_Capability_Validation/regulator_summary.log +test -r results/DeviceTree_HW_Capability_Validation/power_domain_summary.log && \ + cat results/DeviceTree_HW_Capability_Validation/power_domain_summary.log +``` ## LAVA diff --git a/Runner/suites/Kernel/Baseport/USB/usb_msd/README.md b/Runner/suites/Kernel/Baseport/USB/usb_msd/README.md index fc614cd3..dafa6638 100644 --- a/Runner/suites/Kernel/Baseport/USB/usb_msd/README.md +++ b/Runner/suites/Kernel/Baseport/USB/usb_msd/README.md @@ -7,21 +7,18 @@ SPDX-License-Identifier: BSD-3-Clause ## Overview -This shell script executes on the DUT (Device-Under-Test) and verifies USB Mass Storage Devices (MSD). +This shell script executes on the DUT and validates USB mass-storage devices. The test validation scope includes: - Successful enumeration of MSD devices - For each device: - - Determine and report bound transport driver (`uas` or `usb-storage`) from the MSD interface for debug visibility + - Determine and report the bound transport driver - Discover associated block devices via sysfs - - If block device is missing, print device information to facilitate debug -- Print a table of enumerated devices: + - Wait a bounded time for asynchronous block-device creation + - Read 512 bytes from each block device by default without modifying media -``` -DEVICE VID:PID DRIVER PRODUCT -------------------------------------------------------------------------------- - -``` -The test PASS requires all detected MSD devices to have associated block device nodes. +The test passes only when every detected MSD interface is driver-bound, exposes +its block device, and completes the enabled read validation. A missing external +MSD fixture is reported as `SKIP`. --- ## Setup @@ -45,3 +42,20 @@ The test PASS requires all detected MSD devices to have associated block device cd Runner ./run-test.sh usb_msd ``` + +Run directly with defaults: + +```sh +cd Runner/suites/Kernel/Baseport/USB/usb_msd +./run.sh +``` + +Disable the read-only media check or adjust enumeration wait time: + +```sh +./run.sh --read-verify 0 --wait-seconds 20 +``` + +Equivalent environment variables are `USB_MSD_READ_VERIFY` and +`USB_MSD_WAIT_SECONDS`. Runtime evidence is retained under +`results/usb_msd/`. diff --git a/Runner/suites/Kernel/Baseport/USB/usb_msd/run.sh b/Runner/suites/Kernel/Baseport/USB/usb_msd/run.sh index 19b6e2b7..b206b851 100755 --- a/Runner/suites/Kernel/Baseport/USB/usb_msd/run.sh +++ b/Runner/suites/Kernel/Baseport/USB/usb_msd/run.sh @@ -2,23 +2,15 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause -# Validate USB Mass Storage device detection -# Requires at least one USB Mass Storage peripheral (USB flash drive, external HDD/SSD, etc.) connected to a USB Host port. -TESTNAME="usb_msd" - -# Robustly find and source init_env +# ---------- Repo env + helpers ---------- SCRIPT_DIR="$( cd "$(dirname "$0")" || exit 1 pwd )" - -# Default result file (works even before functestlib is available) -# shellcheck disable=SC2034 -RES_FILE="$SCRIPT_DIR/${TESTNAME}.res" - INIT_ENV="" SEARCH="$SCRIPT_DIR" + while [ "$SEARCH" != "/" ]; do if [ -f "$SEARCH/init_env" ]; then INIT_ENV="$SEARCH/init_env" @@ -29,147 +21,150 @@ done if [ -z "$INIT_ENV" ]; then echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 - echo "$TESTNAME SKIP" >"$RES_FILE" 2>/dev/null || true - exit 0 + exit 1 fi -# Only source if not already loaded (idempotent) +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. if [ -z "${__INIT_ENV_LOADED:-}" ]; then # shellcheck disable=SC1090 . "$INIT_ENV" __INIT_ENV_LOADED=1 fi -# Always source functestlib.sh, using $TOOLS exported by init_env -# shellcheck disable=SC1090,SC1091 + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" -# Resolve test path and cd (single SKIP/exit path) -SKIP_REASON="" -test_path=$(find_test_case_by_name "$TESTNAME") -if [ -z "$test_path" ] || [ ! -d "$test_path" ]; then - SKIP_REASON="$TESTNAME SKIP - test path not found" -elif ! cd "$test_path"; then - SKIP_REASON="$TESTNAME SKIP - cannot cd into $test_path" -else - RES_FILE="$test_path/${TESTNAME}.res" +TESTNAME="usb_msd" +RES_FILE="$SCRIPT_DIR/$TESTNAME.res" +RESULT_DIR="$SCRIPT_DIR/results/$TESTNAME" + +USB_MSD_READ_VERIFY="${USB_MSD_READ_VERIFY:-1}" +USB_MSD_WAIT_SECONDS="${USB_MSD_WAIT_SECONDS:-10}" + +usage() { + cat <<'EOF' +Usage: ./run.sh [options] + +Options: + --read-verify 0|1 Read the first 512 bytes from each block device, default: 1. + --wait-seconds N Wait for USB storage block-device creation, default: 10. + -h, --help Show this help. + +Environment: + USB_MSD_READ_VERIFY=0|1 + USB_MSD_WAIT_SECONDS=N +EOF +} + +parse_args() { + while [ "$#" -gt 0 ]; do + case "$1" in + --read-verify) + [ "$#" -ge 2 ] || return 1 + USB_MSD_READ_VERIFY="$2" + shift 2 + ;; + --wait-seconds) + [ "$#" -ge 2 ] || return 1 + USB_MSD_WAIT_SECONDS="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + log_error "Unknown argument: $1" + return 1 + ;; + esac + done +} + +parse_args "$@" || { + usage >&2 + exit 2 +} + +case "$USB_MSD_READ_VERIFY" in + 0|1) + ;; + *) + log_warn "Invalid USB_MSD_READ_VERIFY='$USB_MSD_READ_VERIFY', using 1" + USB_MSD_READ_VERIFY=1 + ;; +esac + +case "$USB_MSD_WAIT_SECONDS" in + ''|*[!0-9]*) + log_warn "Invalid USB_MSD_WAIT_SECONDS='$USB_MSD_WAIT_SECONDS', using 10" + USB_MSD_WAIT_SECONDS=10 + ;; +esac + +test_result_init "$TESTNAME" "$RES_FILE" || exit 1 + +if ! mkdir -p "$RESULT_DIR"; then + test_result_finish "FAIL" "$TESTNAME FAIL: cannot create result directory $RESULT_DIR" fi -if [ -n "$SKIP_REASON" ]; then - log_skip "$SKIP_REASON" - echo "$TESTNAME SKIP" >"$RES_FILE" 2>/dev/null || true - exit 0 +log_info "--------------------------------------------------------------------------" +log_info "Starting $TESTNAME Testcase" +log_info "Configuration: read_verify=$USB_MSD_READ_VERIFY wait_seconds=$USB_MSD_WAIT_SECONDS" + +if ! CHECK_DEPS_RECOVER=0 CHECK_DEPS_NO_EXIT=1 check_dependencies \ + basename \ + cat \ + dirname \ + grep \ + mkdir \ + readlink \ + sleep \ + tr; then + test_result_finish "SKIP" "$TESTNAME SKIP: required base utilities are unavailable" fi -log_info "-----------------------------------------------------------------------------------------" -log_info "-------------------Starting $TESTNAME Testcase----------------------------" -log_info "=== Test Initialization ===" - -# Check if dependencies are installed, else skip test -deps_list="grep sed sort wc tr readlink" -if ! check_dependencies "$deps_list"; then - log_skip "$TESTNAME SKIP - missing dependencies: $deps_list" - echo "$TESTNAME SKIP" >"$RES_FILE" - exit 0 +USB_MSD_EFFECTIVE_READ_VERIFY="$USB_MSD_READ_VERIFY" +usb_msd_read_tool_missing=0 +if [ "$USB_MSD_READ_VERIFY" -eq 1 ] && ! command -v dd >/dev/null 2>&1; then + USB_MSD_EFFECTIVE_READ_VERIFY=0 + usb_msd_read_tool_missing=1 fi -# Detect unique devices with bInterfaceClass = 08 (MSD) under /sys/bus/usb/devices -log_info "=== USB Mass Storage device Detection ===" -msd_device_list="$( - for f in /sys/bus/usb/devices/*/bInterfaceClass; do - [ -r "$f" ] || continue - if grep -qx '08' "$f"; then - d=${f%/bInterfaceClass} - d=${d%:*} - printf '%s\n' "${d##*/}" - fi - done 2>/dev/null | sort -u -)" - -msd_device_count="$(printf "%s\n" "$msd_device_list" | sed '/^$/d' | wc -l | tr -d '[:space:]')" -log_info "Number of MSD devices found: $msd_device_count" - -if [ "$msd_device_count" -gt 0 ] 2>/dev/null; then - - log_info "=== Enumerated Mass Storage Devices ===" - printf '\n%-9s %-9s %-18s %-s\n' "DEVICE" "VID:PID" "DRIVER" "PRODUCT" - printf '%s\n' "-------------------------------------------------------------------------------" - - has_devnodes_count=0 - missing_devices_info="" - - for dev in $(printf "%s\n" "$msd_device_list" | sed '/^$/d'); do - sys="/sys/bus/usb/devices/$dev" - vid=$([ -r "$sys/idVendor" ] && tr -d '[:space:]' < "$sys/idVendor" || echo -) - pid=$([ -r "$sys/idProduct" ] && tr -d '[:space:]' < "$sys/idProduct" || echo -) - if [ -r "$sys/product" ]; then - product=$(tr -d '\000' < "$sys/product") - else - product="-" - fi - - # Determine transport driver (uas vs usb-storage) from the MSD interface - driver="-" - found_block=0 - - for intf in "$sys":*; do - # Only consider MSD interfaces (bInterfaceClass == 08) - if [ -r "$intf/bInterfaceClass" ] && grep -qx '08' "$intf/bInterfaceClass"; then - # Resolve driver symlink and extract driver name (uas or usb-storage) - if [ -L "$intf/driver" ]; then - link="$(readlink "$intf/driver" 2>/dev/null)" - driver="$(printf "%s\n" "$link" | grep -Eo '(uas|usb-storage)' || echo -)" - fi - - # Discover associated block device(s) via sysfs - blk_list="" - for b in \ - "$intf"/host*/target*/*/block/* \ - "$intf"/host*/target*/*/*/block/* \ - "$intf"/host*/target*/block/*; do - [ -e "$b" ] || continue - bn=${b##*/} - blk_list="$blk_list $bn" - done - - # Verify at least one block dev node exists for the USB device - for bn in $blk_list; do - [ -n "$bn" ] || continue - if [ -e "/dev/$bn" ]; then - found_block=1 - break - fi - done - if [ "$found_block" -eq 1 ] 2>/dev/null; then - has_devnodes_count=$((has_devnodes_count + 1)) - else - missing_devices_info="${missing_devices_info}\nDEVICE: $dev VID:PID: $vid:$pid DRIVER: $driver PRODUCT: \"$product\"" - fi - break - fi - done - - printf '%-9s %-9s %-18s %-s\n' "$dev" "$vid:$pid" "$driver" "$product" - done - - printf '\n' +usb_validate_mass_storage_runtime \ + "$RESULT_DIR" \ + "$USB_MSD_EFFECTIVE_READ_VERIFY" \ + "$USB_MSD_WAIT_SECONDS" +usb_msd_status=$? + +case "$usb_msd_status" in + 0) + test_result_record \ + "PASS" \ + "All $USB_MSD_DEVICE_COUNT USB mass-storage device(s) passed driver and block-device validation" + ;; + 1) + test_result_record \ + "FAIL" \ + "$USB_MSD_FAILURE_COUNT validation failure(s) were found across $USB_MSD_DEVICE_COUNT USB mass-storage device(s)" + ;; + 2) + test_result_finish "SKIP" "$TESTNAME SKIP: no USB mass-storage peripheral is connected" + ;; + *) + test_result_finish "FAIL" "$TESTNAME FAIL: USB mass-storage runtime validation could not complete" + ;; +esac + +if [ "$usb_msd_read_tool_missing" -eq 1 ]; then + test_result_record "SKIP" "USB mass-storage read verification was requested but dd is unavailable" +elif [ "$USB_MSD_READ_VERIFY" -eq 0 ]; then + test_result_record "SKIP" "USB mass-storage read verification is disabled" +elif [ "$usb_msd_status" -eq 0 ]; then + test_result_record "PASS" "USB mass-storage read verification completed for every discovered block device" fi -if [ "$msd_device_count" -gt 0 ]; then - if [ "${has_devnodes_count:-0}" -eq "$msd_device_count" ] 2>/dev/null; then - log_pass "$TESTNAME : Test Passed - All ($msd_device_count/$msd_device_count) MSD device(s) have associated block device(s)" - echo "$TESTNAME PASS" > "$RES_FILE" - exit 0 - else - if [ -n "${missing_devices_info:-}" ]; then - log_info "MSD device(s) missing associated block device:" - printf "%s\n" "$missing_devices_info" | sed '/^$/d' - fi - log_fail "$TESTNAME : Test Failed - $((msd_device_count - has_devnodes_count))/$msd_device_count MSD device(s) missing associated block device(s)" - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 0 - fi -else - log_fail "$TESTNAME : Test Failed - No USB 'Mass Storage Device' found" - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 0 -fi +test_result_finish diff --git a/Runner/suites/Kernel/Baseport/USB/usb_msd/usb_msd.yaml b/Runner/suites/Kernel/Baseport/USB/usb_msd/usb_msd.yaml index 0e27f283..a5642f4a 100644 --- a/Runner/suites/Kernel/Baseport/USB/usb_msd/usb_msd.yaml +++ b/Runner/suites/Kernel/Baseport/USB/usb_msd/usb_msd.yaml @@ -1,16 +1,19 @@ metadata: name: usb_msd format: "Lava-Test Test Definition 1.0" - description: "This shell script executes on the DUT (Device-Under-Test) and verifies enumeration of connected USB Mass Storage Devices (MSD)." + description: "Validate USB mass-storage binding, block-device creation, and read access." os: - linux scope: - functional +params: + USB_MSD_READ_VERIFY: "1" + USB_MSD_WAIT_SECONDS: "10" + run: steps: - REPO_PATH=$PWD - cd Runner/suites/Kernel/Baseport/USB/usb_msd - ./run.sh || true - $REPO_PATH/Runner/utils/send-to-lava.sh usb_msd.res - diff --git a/Runner/suites/Kernel/Baseport/USBHost/README.md b/Runner/suites/Kernel/Baseport/USBHost/README.md index 44ebf170..c6853bd8 100644 --- a/Runner/suites/Kernel/Baseport/USBHost/README.md +++ b/Runner/suites/Kernel/Baseport/USBHost/README.md @@ -6,7 +6,15 @@ SPDX-License-Identifier: BSD-3-Clause``` ## Overview -This shell script executes on the DUT (Device-Under-Test) and verifies enumeration of connected USB devices. +This shell script executes on the DUT and validates USB host runtime state. It +reports root hubs, connected devices, negotiated speeds, interface classes, +bound drivers, runtime-power state, `lsusb` inventory when available, and a +focused USB kernel-health snapshot. + +A healthy host controller with no external peripheral is a valid controller +result and reports the fixture-dependent device check as `SKIP`. An inactive +device role is also reported as `SKIP`, rather than as a host-controller +failure. --- @@ -16,6 +24,22 @@ This shell script executes on the DUT (Device-Under-Test) and verifies enumerati - Only applicable for USB ports that support Host Mode functionality. - USB peripherals examples: Mass Storage devices (pendrives, SSD, hard drives, etc.), HID devices (Mouse, Keyboard, USB headset, USB camera, etc.) +## Run + +```sh +cd Runner/suites/Kernel/Baseport/USBHost +./run.sh +``` + +Artifacts are retained under `results/USBHost/`. + +Kernel-log findings are advisory by default. Enable strict gating after the +platform's expected boot-time messages are understood: + +```sh +USB_DMESG_STRICT=1 ./run.sh +``` + --- ## License diff --git a/Runner/suites/Kernel/Baseport/USBHost/USBHost.yaml b/Runner/suites/Kernel/Baseport/USBHost/USBHost.yaml index 6e8bf969..35e0510f 100644 --- a/Runner/suites/Kernel/Baseport/USBHost/USBHost.yaml +++ b/Runner/suites/Kernel/Baseport/USBHost/USBHost.yaml @@ -1,16 +1,18 @@ metadata: name: usb-host format: "Lava-Test Test Definition 1.0" - description: "This shell script executes on the DUT (Device-Under-Test) and verifies enumeration of connected USB devices." + description: "Validate USB host controllers, connected devices, interface drivers, speeds, and runtime power." os: - linux scope: - functional +params: + USB_DMESG_STRICT: "0" + run: steps: - REPO_PATH=$PWD - cd Runner/suites/Kernel/Baseport/USBHost - ./run.sh || true - $REPO_PATH/Runner/utils/send-to-lava.sh USBHost.res || true - diff --git a/Runner/suites/Kernel/Baseport/USBHost/run.sh b/Runner/suites/Kernel/Baseport/USBHost/run.sh index 1a110dce..a5620afb 100755 --- a/Runner/suites/Kernel/Baseport/USBHost/run.sh +++ b/Runner/suites/Kernel/Baseport/USBHost/run.sh @@ -2,12 +2,15 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause -#Setup requires at least one USB peripheral connected to USB port that supports Host mode function -# Robustly find and source init_env -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# ---------- Repo env + helpers ---------- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + pwd +)" INIT_ENV="" SEARCH="$SCRIPT_DIR" + while [ "$SEARCH" != "/" ]; do if [ -f "$SEARCH/init_env" ]; then INIT_ENV="$SEARCH/init_env" @@ -21,50 +24,118 @@ if [ -z "$INIT_ENV" ]; then exit 1 fi -# Only source if not already loaded (idempotent) -if [ -z "$__INIT_ENV_LOADED" ]; then +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. +if [ -z "${__INIT_ENV_LOADED:-}" ]; then # shellcheck disable=SC1090 . "$INIT_ENV" + __INIT_ENV_LOADED=1 fi -# Always source functestlib.sh, using $TOOLS exported by init_env -# shellcheck disable=SC1090,SC1091 + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" TESTNAME="USBHost" -test_path=$(find_test_case_by_name "$TESTNAME") -cd "$test_path" || exit 1 -# shellcheck disable=SC2034 -res_file="./$TESTNAME.res" +RES_FILE="$SCRIPT_DIR/$TESTNAME.res" +RESULT_DIR="$SCRIPT_DIR/results/$TESTNAME" +USB_DMESG_STRICT="${USB_DMESG_STRICT:-0}" -log_info "-----------------------------------------------------------------------------------------" -log_info "-------------------Starting $TESTNAME Testcase----------------------------" -log_info "=== Test Initialization ===" +test_result_init "$TESTNAME" "$RES_FILE" || exit 1 -# Check if lsusb is installed -check_dependencies lsusb +if ! mkdir -p "$RESULT_DIR"; then + test_result_finish "FAIL" "$TESTNAME FAIL: cannot create result directory $RESULT_DIR" +fi -# Run lsusb and capture output -usb_output=$(lsusb) -device_count=$(echo "$usb_output" | wc -l) +log_info "--------------------------------------------------------------------------" +log_info "Starting $TESTNAME Testcase" -# Filter out USB hubs -non_hub_count=$(printf '%s\n' "$usb_output" | grep -ivc "hub") +case "$USB_DMESG_STRICT" in + 0|1) + ;; + *) + log_warn "Invalid USB_DMESG_STRICT='$USB_DMESG_STRICT', using 0" + USB_DMESG_STRICT=0 + ;; +esac -echo "Enumerated USB devices..." -echo "$usb_output" +log_info "Configuration: USB_DMESG_STRICT=$USB_DMESG_STRICT" -# Check if any USB devices were found -if [ -z "$usb_output" ] || [ "$device_count" -eq 0 ]; then - log_fail "$TESTNAME : Test Failed - No USB devices found." - echo "$TESTNAME FAIL" > "$res_file" - exit 1 +if ! CHECK_DEPS_RECOVER=0 CHECK_DEPS_NO_EXIT=1 check_dependencies \ + basename \ + cat \ + find \ + grep \ + mkdir \ + readlink \ + rm \ + tr \ + wc; then + test_result_finish "SKIP" "$TESTNAME SKIP: required base utilities are unavailable" +fi -elif [ "$non_hub_count" -eq 0 ]; then - log_fail "$TESTNAME : Test Failed - Only USB hubs detected, no functional USB devices." - echo "$TESTNAME FAIL" > "$res_file" - exit 1 +usb_collect_host_inventory "$RESULT_DIR" +usb_inventory_status=$? + +case "$usb_inventory_status" in + 0) + test_result_record \ + "PASS" \ + "USB host runtime is healthy: root_hubs=$USB_RUNTIME_ROOT_HUB_COUNT devices=$USB_RUNTIME_DEVICE_COUNT interfaces=$USB_RUNTIME_INTERFACE_COUNT" + ;; + 1) + test_result_record \ + "FAIL" \ + "USB host runtime validation failed: ${USB_RUNTIME_FAILURE_REASON:-inconsistent host-controller state}" + ;; + 2) + test_result_finish "SKIP" "$TESTNAME SKIP: USB host mode is not active on the current connector" + ;; + *) + test_result_finish "FAIL" "$TESTNAME FAIL: USB host runtime inventory could not complete" + ;; +esac + +if [ "$USB_RUNTIME_DEVICE_COUNT" -gt 0 ]; then + test_result_record "PASS" "USB host has $USB_RUNTIME_DEVICE_COUNT connected non-root device(s)" + if [ "$USB_RUNTIME_BOUND_INTERFACE_COUNT" -gt 0 ]; then + test_result_record "PASS" "USB host has $USB_RUNTIME_BOUND_INTERFACE_COUNT bound interface driver(s)" + else + test_result_record "SKIP" "Connected USB devices expose no kernel-bound interfaces, userspace-managed functions may still be valid" + fi else - log_pass "$TESTNAME : Test Passed - $non_hub_count non-hub USB device(s) found." - echo "$TESTNAME PASS" > "$res_file" - exit 0 + test_result_record "SKIP" "USB host controller is healthy but no external peripheral is connected" fi + +if command -v lsusb >/dev/null 2>&1; then + log_info "USB userspace validation: collecting bounded lsusb inventory" + if lsusb >"$RESULT_DIR/lsusb.log" 2>&1; then + log_file_with_label "LSUSB" "$RESULT_DIR/lsusb.log" + test_result_record "PASS" "lsusb completed successfully" + else + log_file_with_label "LSUSB" "$RESULT_DIR/lsusb.log" + test_result_record "FAIL" "lsusb is installed but failed to enumerate the USB bus" + fi +else + test_result_record "SKIP" "Optional lsusb utility is not provided by the image" +fi + +log_info "USB kernel-health validation: capturing DWC3, xHCI, PHY, and UCSI errors" +scan_dmesg_errors \ + "$RESULT_DIR" \ + 'dwc3.*|xhci.*|usb.*|ucsi.*|pmic.gl.*' \ + 'deferred probe|EPROBE_DEFER|using dummy regulator|supply [^ ]+ not found' +usb_dmesg_status=$? + +if [ ! -s "$RESULT_DIR/dmesg_snapshot.log" ]; then + test_result_record "SKIP" "Kernel log access is unavailable for USB health validation" +elif [ "$usb_dmesg_status" -eq 0 ] && [ "$USB_DMESG_STRICT" -eq 1 ]; then + test_result_record "FAIL" "USB controller, PHY, or UCSI errors were found in $RESULT_DIR/dmesg_errors.log" +elif [ "$usb_dmesg_status" -eq 0 ]; then + test_result_record "SKIP" "USB kernel errors were retained as advisory evidence, set USB_DMESG_STRICT=1 to gate them" +else + test_result_record "PASS" "No non-benign USB controller, PHY, or UCSI errors were found in the captured kernel log" +fi + +test_result_finish diff --git a/Runner/suites/Kernel/Baseport/usb_hid/README.md b/Runner/suites/Kernel/Baseport/usb_hid/README.md index 218a902d..058cabf8 100644 --- a/Runner/suites/Kernel/Baseport/usb_hid/README.md +++ b/Runner/suites/Kernel/Baseport/usb_hid/README.md @@ -4,7 +4,10 @@ SPDX-License-Identifier: BSD-3-Clause ## Overview -This shell script executes on the DUT (Device-Under-Test) and verifies enumeration of connected USB Human Interface Devices (HID). +This shell script executes on the DUT and validates connected USB Human +Interface Device interfaces. It reports the device identity and verifies that +every HID class interface has a bound kernel driver. A missing external HID +fixture is reported as `SKIP`. --- @@ -29,3 +32,12 @@ This shell script executes on the DUT (Device-Under-Test) and verifies enumerati cd Runner ./run-test.sh usb_hid ``` + +Run directly: + +```sh +cd Runner/suites/Kernel/Baseport/usb_hid +./run.sh +``` + +Runtime evidence is retained under `results/usb_hid/`. diff --git a/Runner/suites/Kernel/Baseport/usb_hid/run.sh b/Runner/suites/Kernel/Baseport/usb_hid/run.sh index 94c50db7..90be216b 100755 --- a/Runner/suites/Kernel/Baseport/usb_hid/run.sh +++ b/Runner/suites/Kernel/Baseport/usb_hid/run.sh @@ -2,23 +2,15 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause -# Validate USB HID device detection -# Requires at least one USB HID peripheral (keyboard/mouse, etc.) connected to a USB Host port. -TESTNAME="usb_hid" - -# Robustly find and source init_env +# ---------- Repo env + helpers ---------- SCRIPT_DIR="$( cd "$(dirname "$0")" || exit 1 pwd )" - -# Default result file (works even before functestlib is available) -# shellcheck disable=SC2034 -RES_FILE="$SCRIPT_DIR/${TESTNAME}.res" - INIT_ENV="" SEARCH="$SCRIPT_DIR" + while [ "$SEARCH" != "/" ]; do if [ -f "$SEARCH/init_env" ]; then INIT_ENV="$SEARCH/init_env" @@ -29,70 +21,56 @@ done if [ -z "$INIT_ENV" ]; then echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 - echo "$TESTNAME SKIP" >"$RES_FILE" 2>/dev/null || true - exit 0 + exit 1 fi -# Only source if not already loaded (idempotent) +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. if [ -z "${__INIT_ENV_LOADED:-}" ]; then # shellcheck disable=SC1090 . "$INIT_ENV" __INIT_ENV_LOADED=1 fi -# Always source functestlib.sh, using $TOOLS exported by init_env -# shellcheck disable=SC1090,SC1091 + +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" -# Resolve test path and cd (single SKIP/exit path) -SKIP_REASON="" -test_path=$(find_test_case_by_name "$TESTNAME") -if [ -z "$test_path" ] || [ ! -d "$test_path" ]; then - SKIP_REASON="$TESTNAME SKIP - test path not found" -elif ! cd "$test_path"; then - SKIP_REASON="$TESTNAME SKIP - cannot cd into $test_path" -else - RES_FILE="$test_path/${TESTNAME}.res" -fi +TESTNAME="usb_hid" +RES_FILE="$SCRIPT_DIR/$TESTNAME.res" +RESULT_DIR="$SCRIPT_DIR/results/$TESTNAME" + +test_result_init "$TESTNAME" "$RES_FILE" || exit 1 -if [ -n "$SKIP_REASON" ]; then - log_skip "$SKIP_REASON" - echo "$TESTNAME SKIP" >"$RES_FILE" 2>/dev/null || true - exit 0 +if ! mkdir -p "$RESULT_DIR"; then + test_result_finish "FAIL" "$TESTNAME FAIL: cannot create result directory $RESULT_DIR" fi -log_info "-----------------------------------------------------------------------------------------" -log_info "-------------------Starting $TESTNAME Testcase----------------------------" -log_info "=== Test Initialization ===" +log_info "--------------------------------------------------------------------------" +log_info "Starting $TESTNAME Testcase" -# Check if grep is installed, else skip test -deps_list="grep sed sort wc" -if ! check_dependencies "$deps_list"; then - log_skip "$TESTNAME SKIP - missing dependencies: $deps_list" - echo "$TESTNAME SKIP" >"$RES_FILE" - exit 0 +if ! CHECK_DEPS_RECOVER=0 CHECK_DEPS_NO_EXIT=1 check_dependencies \ + basename cat dirname mkdir readlink tr; then + test_result_finish "SKIP" "$TESTNAME SKIP: required base utilities are unavailable" fi -# Count uniques devices with bInterfaceClass = 03 (HID) under /sys/bus/usb/devices -hid_device_count=0 -log_info "=== USB HID device Detection ===" -hid_device_count=$( - for f in /sys/bus/usb/devices/*/bInterfaceClass; do - [ -r "$f" ] || continue - if grep -qx '03' "$f"; then - d=${f%/bInterfaceClass} - echo "${d##*/}" - fi - done 2>/dev/null | sed 's/:.*$//' | sort -u | wc -l | tr -d '[:space:]' - ) +usb_validate_hid_runtime "$RESULT_DIR" +usb_hid_status=$? -log_info "Number of HID devices found: $hid_device_count" +case "$usb_hid_status" in + 0) + test_result_record "PASS" "All $USB_HID_INTERFACE_COUNT USB HID interface(s) are driver-bound" + ;; + 1) + test_result_record "FAIL" "$USB_HID_UNBOUND_COUNT of $USB_HID_INTERFACE_COUNT USB HID interface(s) have no bound kernel driver" + ;; + 2) + test_result_finish "SKIP" "$TESTNAME SKIP: no USB HID peripheral is connected" + ;; + *) + test_result_finish "FAIL" "$TESTNAME FAIL: USB HID runtime validation could not complete" + ;; +esac -if [ "$hid_device_count" -gt 0 ]; then - log_pass "$TESTNAME : Test Passed - USB HID device(s) detected" - echo "$TESTNAME PASS" > "$RES_FILE" - exit 0 -else - log_fail "$TESTNAME : Test Failed - No USB 'Human Interface Device' found" - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 0 -fi +test_result_finish diff --git a/Runner/suites/Kernel/Baseport/usb_hid/usb_hid.yaml b/Runner/suites/Kernel/Baseport/usb_hid/usb_hid.yaml index 0d0c9c15..9dc8e786 100644 --- a/Runner/suites/Kernel/Baseport/usb_hid/usb_hid.yaml +++ b/Runner/suites/Kernel/Baseport/usb_hid/usb_hid.yaml @@ -1,7 +1,7 @@ metadata: name: usb_hid format: "Lava-Test Test Definition 1.0" - description: "This shell script executes on the DUT (Device-Under-Test) and verifies enumeration of connected USB Human Interface Devices (HID)." + description: "Validate connected USB HID interfaces and kernel-driver binding." os: - linux scope: @@ -13,4 +13,3 @@ run: - cd Runner/suites/Kernel/Baseport/usb_hid - ./run.sh || true - $REPO_PATH/Runner/utils/send-to-lava.sh usb_hid.res -