diff --git a/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/BT_FW_KMD_Service.yaml b/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/BT_FW_KMD_Service.yaml index f7e3b89b..7ff6d296 100644 --- a/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/BT_FW_KMD_Service.yaml +++ b/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/BT_FW_KMD_Service.yaml @@ -7,10 +7,15 @@ metadata: scope: - functional +params: + BT_ADAPTER: "" + BT_RUNTIME_READY_WAIT: "20" + BT_RUNTIME_RECOVERY_WAIT: "40" + BT_RUNTIME_RECOVERY_ATTEMPTS: "2" + run: steps: - REPO_PATH=$PWD - cd Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/ - ./run.sh || true - $REPO_PATH/Runner/utils/send-to-lava.sh BT_FW_KMD_Service.res - 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 334968a1..b96a83b9 100755 --- a/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/run.sh +++ b/Runner/suites/Connectivity/Bluetooth/BT_FW_KMD_Service/run.sh @@ -4,7 +4,7 @@ # BT_FW_KMD_Service - Bluetooth FW + KMD + service + controller infra validation # Non-expect version, using lib_bluetooth.sh helpers. -# ---------- init_env + tools ---------- +# ---------- Repo env + helpers ---------- SCRIPT_DIR="$( cd "$(dirname "$0")" || exit 1 pwd @@ -25,12 +25,16 @@ if [ -z "$INIT_ENV" ]; then exit 1 fi +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. if [ -z "${__INIT_ENV_LOADED:-}" ]; then # shellcheck disable=SC1090 . "$INIT_ENV" __INIT_ENV_LOADED=1 fi +# shellcheck disable=SC1090 +. "$INIT_ENV" # shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" # shellcheck disable=SC1091 @@ -38,63 +42,111 @@ fi # ---------- CLI / env parameters ---------- BT_ADAPTER="${BT_ADAPTER-}" +BT_RUNTIME_READY_WAIT="${BT_RUNTIME_READY_WAIT:-20}" +BT_RUNTIME_RECOVERY_WAIT="${BT_RUNTIME_RECOVERY_WAIT:-40}" +BT_RUNTIME_RECOVERY_ATTEMPTS="${BT_RUNTIME_RECOVERY_ATTEMPTS:-2}" + +usage() { + cat <&2 + return 2 + fi + + BT_ADAPTER="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + log_error "Unknown argument: $1" + usage >&2 + return 2 + ;; + esac + done +} + +parse_args "$@" || exit $? + +for wait_value in \ + "$BT_RUNTIME_READY_WAIT" \ + "$BT_RUNTIME_RECOVERY_WAIT" \ + "$BT_RUNTIME_RECOVERY_ATTEMPTS" +do + if ! is_unsigned_number "$wait_value" || [ "$wait_value" -eq 0 ]; then + log_error "Bluetooth runtime wait values must be positive integers" + exit 2 + fi done -TESTNAME="BT_FW_KMD_Service" -testpath="$(find_test_case_by_name "$TESTNAME")" || { - log_fail "$TESTNAME : Test directory not found." - echo "$TESTNAME FAIL" > "./$TESTNAME.res" +testpath="$(find_test_case_by_name "BT_FW_KMD_Service")" || { + log_fail "BT_FW_KMD_Service FAIL - test directory not found" + printf '%s\n' "BT_FW_KMD_Service FAIL" >"./BT_FW_KMD_Service.res" exit 1 } cd "$testpath" || exit 1 +TESTNAME="BT_FW_KMD_Service" RES_FILE="./${TESTNAME}.res" -rm -f "$RES_FILE" +test_result_init "$TESTNAME" "$RES_FILE" || exit 1 -FAIL_COUNT=0 WARN_COUNT=0 -inc_fail() { FAIL_COUNT=$((FAIL_COUNT + 1)); } -inc_warn() { WARN_COUNT=$((WARN_COUNT + 1)); } - log_info "------------------------------------------------------------" log_info "Starting $TESTNAME" if ! bt_prepare_ubuntu_stack; then - log_fail "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 0 + test_result_finish "FAIL" "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" fi log_info "Checking dependencies: bluetoothctl hciconfig lsmod" if ! check_dependencies bluetoothctl hciconfig lsmod; then - echo "$TESTNAME SKIP" > "$RES_FILE" - exit 0 + test_result_finish "SKIP" "$TESTNAME SKIP - required Bluetooth tools are unavailable" fi # ---------- Bluetooth runtime check/ readiness ---------- -log_info "Waiting for Bluetooth runtime readiness..." -bt_wait_ready 60 2 || true +log_info "Waiting up to ${BT_RUNTIME_READY_WAIT}s for a usable Bluetooth runtime" +if bt_ensure_runtime_ready \ + "$BT_ADAPTER" \ + "$BT_RUNTIME_READY_WAIT" \ + "$BT_RUNTIME_RECOVERY_WAIT" \ + "$BT_RUNTIME_RECOVERY_ATTEMPTS"; then + if [ "$BT_RUNTIME_RECOVERED" -eq 1 ]; then + WARN_COUNT=$((WARN_COUNT + 1)) + test_result_record "PASS" "Bluetooth runtime recovered with a valid HCI adapter" + else + 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" +fi + +ADAPTER="$BT_RUNTIME_READY_ADAPTER" # ---------- Bluetooth service / daemon ---------- log_info "Checking if bluetoothd (or bluetooth.service) is running..." if btsvcactive; then - log_pass "Bluetooth service/daemon active." + test_result_record "PASS" "Bluetooth service/daemon active" else - log_fail "Bluetooth service/daemon NOT active." - inc_fail + test_result_record "FAIL" "Bluetooth service/daemon is not active" fi # ---------- DT node / compatible ---------- @@ -109,10 +161,11 @@ if dt_confirm_node_or_compatible_all \ "qcom,bluetooth" \ "pcie-m2-e-connector" then - log_pass "DT node/compatible for BT or an M.2 E-key connector is present." + test_result_record "PASS" "DT node/compatible for BT or an M.2 E-key connector is present" else - dt_runtime_adapter="" - if command -v bt_select_usable_adapter >/dev/null 2>&1; then + dt_runtime_adapter="$ADAPTER" + if [ -z "$dt_runtime_adapter" ] && \ + command -v bt_select_usable_adapter >/dev/null 2>&1; then dt_runtime_adapter="$(bt_select_usable_adapter 2>/dev/null || true)" fi @@ -121,15 +174,15 @@ else else log_warn "No static BT DT node or M.2 E-key connector found, deferring the verdict to the authoritative KMD, HCI, service, firmware, and controller checks below (GH#533)." fi - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) fi # ---------- Firmware presence ---------- if fw_dir="$(btfwpresent 2>/dev/null)"; then - log_pass "Firmware present in: $fw_dir" + 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." - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) fi # ----------------------------- @@ -161,15 +214,16 @@ if command -v btfwloaded >/dev/null 2>&1; then rc=$? case "$rc" in 0) - log_pass "Firmware load/setup appears completed (kernel log)." + test_result_record "PASS" "Firmware load/setup appears completed in the kernel log" ;; 2) log_warn "Firmware load/setup completed after retry, transient errors seen earlier (kernel log)." - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) + test_result_record "PASS" "Firmware load/setup completed after a transient retry" ;; *) runtime_bt_ok=1 - fallback_adapter="$BT_ADAPTER" + fallback_adapter="$ADAPTER" if [ -z "$fallback_adapter" ] && findhcisysfs >/dev/null 2>&1; then fallback_adapter="$(findhcisysfs 2>/dev/null || true)" @@ -192,53 +246,52 @@ if command -v btfwloaded >/dev/null 2>&1; then if [ "$runtime_bt_ok" -eq 1 ]; then log_warn "No retained BT firmware-load signature found, but BT runtime state is healthy." - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) else - log_fail "Firmware load/setup does NOT look clean and BT runtime state is also unhealthy." - inc_fail + test_result_record "FAIL" "Firmware load/setup is incomplete and Bluetooth runtime state is unhealthy" fi ;; esac else # No SKIP: continue test, just warn. log_warn "btfwloaded() helper not available, firmware-load kernel-log validation not performed." - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) fi # ---------- Kernel modules / KMD ---------- if btkmdpresent; then - log_pass "Kernel BT driver stack present (bluetooth/hci_uart/btqca or built-in)." + test_result_record "PASS" "Kernel BT driver stack is present" else - log_fail "Kernel BT driver stack not detected (no bluetooth/hci_uart/btqca in sysfs/dmesg)." - inc_fail + test_result_record "FAIL" "Kernel BT driver stack was not detected" fi # ---------- HCI presence ---------- if bthcipresent; then - log_pass "HCI present in /sys/class/bluetooth." + test_result_record "PASS" "HCI is present in /sys/class/bluetooth" else - log_fail "No /sys/class/bluetooth/hci* found (HCI not up)." - inc_fail + test_result_record "FAIL" "No HCI adapter was found in /sys/class/bluetooth" fi # --- Bluetooth service / daemon check via btsvcactive() --- if btsvcactive; then - log_pass "Bluetooth service active (systemd bluetooth.service or bluetoothd)." + test_result_record "PASS" "Bluetooth service remains active" else log_warn "Bluetooth service is not active (bluetooth.service inactive and bluetoothd not running)." - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) fi # ----------------------------- # Detect adapter (CLI/ENV > auto-detect) # ----------------------------- -if [ -n "$BT_ADAPTER" ]; then - ADAPTER="$BT_ADAPTER" - log_info "Using adapter from BT_ADAPTER/CLI: $ADAPTER" -elif findhcisysfs >/dev/null 2>&1; then - ADAPTER="$(findhcisysfs 2>/dev/null || true)" -else - ADAPTER="" +if [ -z "$ADAPTER" ]; then + if [ -n "$BT_ADAPTER" ]; then + ADAPTER="$BT_ADAPTER" + log_info "Using adapter from BT_ADAPTER/CLI: $ADAPTER" + elif findhcisysfs >/dev/null 2>&1; then + ADAPTER="$(findhcisysfs 2>/dev/null || true)" + else + ADAPTER="" + fi fi if [ -n "$ADAPTER" ]; then @@ -251,21 +304,19 @@ fi if [ -z "$ADAPTER" ]; then log_warn "No HCI adapter found." - - if [ "$FAIL_COUNT" -gt 0 ]; then - echo "$TESTNAME FAIL" > "$RES_FILE" + + if [ "$TEST_RESULT_FAIL_COUNT" -gt 0 ]; then + test_result_finish else - echo "$TESTNAME SKIP" > "$RES_FILE" + test_result_finish "SKIP" "$TESTNAME SKIP - no HCI adapter was found" fi - exit 0 fi # ---------- BD address sanity check ---------- if [ -n "$ADAPTER" ]; then if btbdok "$ADAPTER"; then - log_pass "BD address sane for $ADAPTER (not all zeros)." + test_result_record "PASS" "BD address is valid for $ADAPTER" else - log_fail "BD address invalid or all zeros for $ADAPTER." - inc_fail + test_result_record "FAIL" "BD address is invalid or all zeros for $ADAPTER" fi fi @@ -278,11 +329,11 @@ if [ -n "$ADAPTER" ]; then # For this infra test we treat this as WARN, not FAIL: # stack is otherwise OK (firmware, KMD, HCI, BD). log_warn "No controller in 'bluetoothctl list' (controller not fully instantiated)." - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) fi else log_warn "Controller visibility not checked (no adapter determined)." - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) fi # ---------- Optional: dump some useful diagnostics ---------- @@ -291,12 +342,15 @@ if command -v hciconfig >/dev/null 2>&1; then hciconfig -a || true else log_warn "hciconfig command not available." - inc_warn + WARN_COUNT=$((WARN_COUNT + 1)) fi log_info "=== bluetoothctl list (controllers) ===" -out="$(bluetoothctl list 2>/dev/null | sanitize_bt_output || true)" +out="$( + run_with_timeout 3 bluetoothctl list 2>/dev/null \ + | sanitize_bt_output || true +)" if printf '%s\n' "$out" | grep -qi '^[[:space:]]*Controller[[:space:]]'; then # Non-interactive worked print what we got printf '%s\n' "$out" @@ -312,12 +366,5 @@ log_info "=== lsmod (subset: BT stack) ===" lsmod 2>/dev/null | grep -E '^(bluetooth|hci_uart|btqca|btbcm|rfkill|cfg80211)\b' || true # ---------- Final result ---------- -log_info "Completed with WARN=${WARN_COUNT}, FAIL=${FAIL_COUNT}" - -if [ "$FAIL_COUNT" -gt 0 ]; then - echo "$TESTNAME FAIL" > "$RES_FILE" -else - echo "$TESTNAME PASS" > "$RES_FILE" -fi - -exit 0 +log_info "Completed with WARN=${WARN_COUNT}, FAIL=${TEST_RESULT_FAIL_COUNT}" +test_result_finish diff --git a/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/BT_ON_OFF.yaml b/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/BT_ON_OFF.yaml index 48683d5e..146e23a0 100644 --- a/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/BT_ON_OFF.yaml +++ b/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/BT_ON_OFF.yaml @@ -13,10 +13,13 @@ params: BT_POWER_ON_ATTEMPTS: "2" BT_POWER_ON_RETRY_DELAY: "10" BT_RESTART_SERVICE_ON_RETRY: "1" + BT_RUNTIME_READY_WAIT: "20" + BT_RUNTIME_RECOVERY_WAIT: "40" + BT_RUNTIME_RECOVERY_ATTEMPTS: "2" run: steps: - REPO_PATH=$PWD - cd Runner/suites/Connectivity/Bluetooth/BT_ON_OFF - - ./run.sh --adapter "${BT_ADAPTER}" --power-cycle-delay "${BT_POWER_CYCLE_DELAY}" --power-on-attempts "${BT_POWER_ON_ATTEMPTS}" --power-on-retry-delay "${BT_POWER_ON_RETRY_DELAY}" --restart-service-on-retry "${BT_RESTART_SERVICE_ON_RETRY}" || true + - ./run.sh --power-cycle-delay "${BT_POWER_CYCLE_DELAY}" --power-on-attempts "${BT_POWER_ON_ATTEMPTS}" --power-on-retry-delay "${BT_POWER_ON_RETRY_DELAY}" --restart-service-on-retry "${BT_RESTART_SERVICE_ON_RETRY}" || true - $REPO_PATH/Runner/utils/send-to-lava.sh BT_ON_OFF.res diff --git a/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/run.sh b/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/run.sh index c2df8b9b..d1b80605 100755 --- a/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/run.sh +++ b/Runner/suites/Connectivity/Bluetooth/BT_ON_OFF/run.sh @@ -4,10 +4,10 @@ # SPDX-License-Identifier: BSD-3-Clause # BT_ON_OFF - Basic Bluetooth power toggle validation (non-expect version) -# Robustly find and source init_env +# ---------- Repo env + helpers ---------- SCRIPT_DIR="$( - cd "$(dirname "$0")" || exit 1 - pwd + cd "$(dirname "$0")" || exit 1 + pwd )" INIT_ENV="" SEARCH="$SCRIPT_DIR" @@ -26,12 +26,15 @@ if [ -z "$INIT_ENV" ]; then fi # Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. if [ -z "${__INIT_ENV_LOADED:-}" ]; then # shellcheck disable=SC1090 . "$INIT_ENV" __INIT_ENV_LOADED=1 fi +# shellcheck disable=SC1090 +. "$INIT_ENV" # shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" # shellcheck disable=SC1091 @@ -47,90 +50,126 @@ BT_POWER_CYCLE_DELAY="${BT_POWER_CYCLE_DELAY:-10}" BT_POWER_ON_ATTEMPTS="${BT_POWER_ON_ATTEMPTS:-2}" BT_POWER_ON_RETRY_DELAY="${BT_POWER_ON_RETRY_DELAY:-10}" BT_RESTART_SERVICE_ON_RETRY="${BT_RESTART_SERVICE_ON_RETRY:-1}" +BT_RUNTIME_READY_WAIT="${BT_RUNTIME_READY_WAIT:-20}" +BT_RUNTIME_RECOVERY_WAIT="${BT_RUNTIME_RECOVERY_WAIT:-40}" +BT_RUNTIME_RECOVERY_ATTEMPTS="${BT_RUNTIME_RECOVERY_ATTEMPTS:-2}" + +usage() { + cat <&2 + return 2 + fi + + case "$1" in + --adapter) + BT_ADAPTER="$2" + ;; + --power-cycle-delay) + BT_POWER_CYCLE_DELAY="$2" + ;; + --power-on-attempts) + BT_POWER_ON_ATTEMPTS="$2" + ;; + --power-on-retry-delay) + BT_POWER_ON_RETRY_DELAY="$2" + ;; + --restart-service-on-retry) + BT_RESTART_SERVICE_ON_RETRY="$2" + ;; + esac + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + log_error "Unknown argument: $1" + usage >&2 + return 2 + ;; + esac + done +} -case "$BT_POWER_ON_RETRY_DELAY" in - ''|*[!0-9]*) - BT_POWER_ON_RETRY_DELAY=10 - ;; -esac +parse_args "$@" || exit $? + +for positive_value in \ + "$BT_POWER_CYCLE_DELAY" \ + "$BT_POWER_ON_ATTEMPTS" \ + "$BT_POWER_ON_RETRY_DELAY" \ + "$BT_RUNTIME_READY_WAIT" \ + "$BT_RUNTIME_RECOVERY_WAIT" \ + "$BT_RUNTIME_RECOVERY_ATTEMPTS" +do + if ! is_unsigned_number "$positive_value" || [ "$positive_value" -eq 0 ]; then + log_error "Bluetooth retry counts and wait values must be positive integers" + exit 2 + fi +done case "$BT_RESTART_SERVICE_ON_RETRY" in 0|1) ;; *) - BT_RESTART_SERVICE_ON_RETRY=1 + log_error "--restart-service-on-retry must be 0 or 1" + exit 2 ;; esac -if [ "$BT_POWER_ON_ATTEMPTS" -lt 1 ] 2>/dev/null; then - BT_POWER_ON_ATTEMPTS=1 -fi - -TESTNAME="BT_ON_OFF" -testpath="$(find_test_case_by_name "$TESTNAME")" || { - log_fail "$TESTNAME : Test directory not found." - echo "$TESTNAME FAIL" > "./$TESTNAME.res" +testpath="$(find_test_case_by_name "BT_ON_OFF")" || { + log_fail "BT_ON_OFF FAIL - test directory not found" + printf '%s\n' "BT_ON_OFF FAIL" >./BT_ON_OFF.res exit 1 } cd "$testpath" || exit 1 -res_file="./$TESTNAME.res" -rm -f "$res_file" +TESTNAME="BT_ON_OFF" +RES_FILE="./$TESTNAME.res" +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 - log_fail "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" - echo "$TESTNAME FAIL" > "$res_file" - exit 0 + test_result_finish "FAIL" "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" fi log_info "Checking dependency: bluetoothctl" # Verify that all necessary dependencies are available. -check_dependencies bluetoothctl pgrep +if ! check_dependencies bluetoothctl pgrep; then + test_result_finish "SKIP" "$TESTNAME SKIP - required Bluetooth tools are unavailable" +fi + +log_info "Ensuring Bluetooth runtime readiness before power-cycle validation" +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" +fi + +ADAPTER="$BT_RUNTIME_READY_ADAPTER" +test_result_record "PASS" "Bluetooth runtime is ready with usable adapter $ADAPTER" log_info "Checking if bluetoothd is running..." MAX_RETRIES=3 @@ -149,16 +188,15 @@ while [ "$retry" -lt "$MAX_RETRIES" ]; do done if [ "$retry" -eq "$MAX_RETRIES" ]; then - log_fail "Bluetooth daemon not detected after ${MAX_RETRIES} attempts." - echo "$TESTNAME FAIL" > "$res_file" - exit 0 + test_result_finish "FAIL" "Bluetooth daemon was not detected after ${MAX_RETRIES} attempts" fi +test_result_record "PASS" "Bluetooth daemon is running" + # ----------------------------- # Detect adapter with precedence: CLI/ENV > auto-detect # ----------------------------- if [ -n "$BT_ADAPTER" ]; then - ADAPTER="$BT_ADAPTER" log_info "Using adapter from BT_ADAPTER/CLI: $ADAPTER" if command -v bt_adapter_is_usable >/dev/null 2>&1; then @@ -169,14 +207,6 @@ if [ -n "$BT_ADAPTER" ]; then fi else bt_log_hci_candidates || true - - if command -v bt_select_usable_adapter >/dev/null 2>&1; then - ADAPTER="$(bt_select_usable_adapter 2>/dev/null || true)" - elif findhcisysfs >/dev/null 2>&1; then - ADAPTER="$(findhcisysfs 2>/dev/null || true)" - else - ADAPTER="" - fi fi if [ -n "$ADAPTER" ]; then @@ -187,17 +217,21 @@ if [ -n "$ADAPTER" ]; then fi fi +if [ -z "$ADAPTER" ]; then + test_result_finish "FAIL" "No usable Bluetooth HCI adapter was found after runtime recovery" +fi + # Warn/diag if non-interactive "bluetoothctl list" is empty. This is non-fatal. btwarniflistempty "$ADAPTER" || true # Ensure controller is visible to bluetoothctl, trying public-addr if needed. if ! bt_ensure_controller_visible "$ADAPTER"; then btloghcidiag "$ADAPTER" failure "$testpath" || true - log_warn "SKIP — no controller visible to bluetoothctl (HCI RAW/DOWN or attach incomplete)." - echo "$TESTNAME SKIP" > "$res_file" - exit 0 + test_result_finish "FAIL" "No controller is visible to bluetoothctl after runtime recovery" fi +test_result_record "PASS" "Bluetooth controller is visible to bluetoothctl" + # Read initial power state. initial_power="$(btgetpower "$ADAPTER" 2>/dev/null || true)" [ -z "$initial_power" ] && initial_power="unknown" @@ -206,22 +240,18 @@ log_info "Initial Powered = $initial_power" # ---- Power OFF test ---- log_info "Powering OFF..." if ! btpower "$ADAPTER" off; then - log_fail "btpower($ADAPTER, off) failed (command-level error)." btloghcidiag "$ADAPTER" failure "$testpath" || true - echo "$TESTNAME FAIL" > "$res_file" - exit 0 + test_result_finish "FAIL" "btpower($ADAPTER, off) failed at command level" fi after_off="$(btgetpower "$ADAPTER" 2>/dev/null || true)" [ -z "$after_off" ] && after_off="unknown" if [ "$after_off" = "no" ]; then - log_pass "Post-OFF verification: Powered=no (as expected)." + test_result_record "PASS" "Post-OFF verification reported Powered=no" else - log_fail "Post-OFF verification failed (Powered=$after_off)." btloghcidiag "$ADAPTER" failure "$testpath" || true - echo "$TESTNAME FAIL" > "$res_file" - exit 0 + test_result_finish "FAIL" "Post-OFF verification failed with Powered=$after_off" fi # ---- Power ON test ---- @@ -256,20 +286,12 @@ while [ "$on_attempt" -le "$BT_POWER_ON_ATTEMPTS" ]; do log_warn "Preparing controlled Power ON retry after ${BT_POWER_ON_RETRY_DELAY}s" sleep "$BT_POWER_ON_RETRY_DELAY" - if command -v rfkill >/dev/null 2>&1; then + if [ "$BT_RESTART_SERVICE_ON_RETRY" -eq 1 ] 2>/dev/null; then + bt_recover_runtime "$ADAPTER" || \ + log_warn "Controlled Bluetooth recovery did not complete before retry" + elif command -v rfkill >/dev/null 2>&1; then log_info "Running rfkill unblock bluetooth before retry" rfkill unblock bluetooth >/dev/null 2>&1 || true - elif command -v rfkillunblocksysfs >/dev/null 2>&1; then - log_info "Running rfkillunblocksysfs before retry" - rfkillunblocksysfs >/dev/null 2>&1 || true - else - log_warn "No rfkill unblock helper available before retry" - fi - - if [ "$BT_RESTART_SERVICE_ON_RETRY" -eq 1 ] 2>/dev/null && command -v systemctl >/dev/null 2>&1; then - log_info "Restarting bluetooth.service before retry" - systemctl restart bluetooth >/dev/null 2>&1 || log_warn "systemctl restart bluetooth failed" - sleep 3 fi if command -v bt_ensure_controller_visible >/dev/null 2>&1; then @@ -287,15 +309,12 @@ if [ "$on_success" -eq 1 ]; then btwarniflistempty "$ADAPTER" || true - log_pass "Post-ON verification: Powered=yes (as expected)." - echo "$TESTNAME PASS" > "$res_file" - exit 0 + test_result_record "PASS" "Post-ON verification reported Powered=yes" + test_result_finish fi after_on="$(btgetpower "$ADAPTER" 2>/dev/null || true)" [ -z "$after_on" ] && after_on="unknown" -log_fail "Post-ON verification failed after $BT_POWER_ON_ATTEMPTS attempt(s) (Powered=$after_on)." btloghcidiag "$ADAPTER" failure "$testpath" || true -echo "$TESTNAME FAIL" > "$res_file" -exit 0 +test_result_finish "FAIL" "Post-ON verification failed after $BT_POWER_ON_ATTEMPTS attempts with Powered=$after_on" diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN/BT_SCAN.yaml b/Runner/suites/Connectivity/Bluetooth/BT_SCAN/BT_SCAN.yaml index 36e15a03..1f8b7ad3 100644 --- a/Runner/suites/Connectivity/Bluetooth/BT_SCAN/BT_SCAN.yaml +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN/BT_SCAN.yaml @@ -7,10 +7,19 @@ metadata: scope: - functional +params: + BT_ADAPTER: "" + BT_TARGET_MAC: "" + BT_SCAN_SECONDS: "15" + BT_SCAN_RETRIES: "3" + BT_SCAN_RETRY_DELAY: "2" + BT_RUNTIME_READY_WAIT: "20" + BT_RUNTIME_RECOVERY_WAIT: "40" + BT_RUNTIME_RECOVERY_ATTEMPTS: "2" + run: steps: - REPO_PATH=$PWD - cd Runner/suites/Connectivity/Bluetooth/BT_SCAN/ - - ./run.sh || true + - ./run.sh --target-mac "${BT_TARGET_MAC}" --scan-seconds "${BT_SCAN_SECONDS}" --scan-retries "${BT_SCAN_RETRIES}" --scan-retry-delay "${BT_SCAN_RETRY_DELAY}" || true - $REPO_PATH/Runner/utils/send-to-lava.sh BT_SCAN.res - diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN/run.sh b/Runner/suites/Connectivity/Bluetooth/BT_SCAN/run.sh index d21b5b86..267de2ca 100755 --- a/Runner/suites/Connectivity/Bluetooth/BT_SCAN/run.sh +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN/run.sh @@ -24,12 +24,16 @@ if [ -z "$INIT_ENV" ]; then exit 1 fi +# Only source once (idempotent) +# NOTE: We intentionally **do not export** any new vars. They stay local to this shell. if [ -z "${__INIT_ENV_LOADED:-}" ]; then # shellcheck disable=SC1090 . "$INIT_ENV" __INIT_ENV_LOADED=1 fi +# shellcheck disable=SC1090 +. "$INIT_ENV" # shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" # shellcheck disable=SC1091 @@ -42,62 +46,116 @@ BT_TARGET_MAC="${BT_TARGET_MAC-}" BT_SCAN_SECONDS="${BT_SCAN_SECONDS:-15}" BT_SCAN_RETRIES="${BT_SCAN_RETRIES:-3}" BT_SCAN_RETRY_DELAY="${BT_SCAN_RETRY_DELAY:-2}" +BT_RUNTIME_READY_WAIT="${BT_RUNTIME_READY_WAIT:-20}" +BT_RUNTIME_RECOVERY_WAIT="${BT_RUNTIME_RECOVERY_WAIT:-40}" +BT_RUNTIME_RECOVERY_ATTEMPTS="${BT_RUNTIME_RECOVERY_ATTEMPTS:-2}" + +usage() { + cat <&2 + return 2 + fi + + case "$1" in + --adapter) + BT_ADAPTER="$2" + ;; + --target-mac) + BT_SCAN_TARGET_MAC="$2" + ;; + --scan-seconds) + BT_SCAN_SECONDS="$2" + ;; + --scan-retries) + BT_SCAN_RETRIES="$2" + ;; + --scan-retry-delay) + BT_SCAN_RETRY_DELAY="$2" + ;; + esac + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + log_error "Unknown argument: $1" + usage >&2 + return 2 + ;; + esac + done +} -while [ "$#" -gt 0 ]; do - case "$1" in - --adapter) - BT_ADAPTER="$2" - shift 2 - ;; - --target-mac) - BT_TARGET_MAC="$2" - shift 2 - ;; - --scan-seconds) - BT_SCAN_SECONDS="$2" - shift 2 - ;; - --scan-retries) - BT_SCAN_RETRIES="$2" - shift 2 - ;; - --scan-retry-delay) - BT_SCAN_RETRY_DELAY="$2" - shift 2 - ;; - *) - log_warn "Unknown argument ignored: $1" - shift 1 - ;; - esac +parse_args "$@" || exit $? + +for positive_value in \ + "$BT_SCAN_SECONDS" \ + "$BT_SCAN_RETRIES" \ + "$BT_SCAN_RETRY_DELAY" \ + "$BT_RUNTIME_READY_WAIT" \ + "$BT_RUNTIME_RECOVERY_WAIT" \ + "$BT_RUNTIME_RECOVERY_ATTEMPTS" +do + if ! is_unsigned_number "$positive_value" || [ "$positive_value" -eq 0 ]; then + log_error "Bluetooth scan retry counts and wait values must be positive integers" + exit 2 + fi done -TESTNAME="BT_SCAN" -testpath="$(find_test_case_by_name "$TESTNAME")" || { - log_fail "$TESTNAME : Test directory not found." - echo "$TESTNAME FAIL" > "./$TESTNAME.res" - exit 0 +testpath="$(find_test_case_by_name "BT_SCAN")" || { + log_fail "BT_SCAN FAIL - test directory not found" + printf '%s\n' "BT_SCAN FAIL" >./BT_SCAN.res + exit 1 } cd "$testpath" || exit 1 -res_file="./$TESTNAME.res" -rm -f "$res_file" +TESTNAME="BT_SCAN" +RES_FILE="./$TESTNAME.res" +test_result_init "$TESTNAME" "$RES_FILE" || exit 1 log_info "------------------------------------------------------------" log_info "Starting $TESTNAME Testcase" if ! bt_prepare_ubuntu_stack; then - log_fail "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" - echo "$TESTNAME FAIL" > "$res_file" - exit 0 + test_result_finish "FAIL" "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" fi log_info "Checking dependencies: bluetoothctl pgrep" if ! check_dependencies bluetoothctl pgrep; then - echo "$TESTNAME SKIP" > "$res_file" - exit 0 + test_result_finish "SKIP" "$TESTNAME SKIP - required Bluetooth tools are unavailable" fi +log_info "Ensuring Bluetooth runtime readiness before scanning" +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" +fi + +ADAPTER="$BT_RUNTIME_READY_ADAPTER" +test_result_record "PASS" "Bluetooth runtime is ready with usable adapter $ADAPTER" + # ----------------------------- # 1. Ensure bluetoothd is running # ----------------------------- @@ -117,21 +175,18 @@ while [ "$retry" -lt "$MAX_RETRIES" ]; do done if [ "$retry" -eq "$MAX_RETRIES" ]; then - log_fail "bluetoothd not detected after $MAX_RETRIES attempts." - echo "$TESTNAME FAIL" > "$res_file" - exit 0 + test_result_finish "FAIL" "bluetoothd was not detected after $MAX_RETRIES attempts" fi +test_result_record "PASS" "Bluetooth daemon is running" + # ----------------------------- # 2. Detect adapter (CLI/ENV > auto-detect) # ----------------------------- if [ -n "$BT_ADAPTER" ]; then - ADAPTER="$BT_ADAPTER" log_info "Using adapter from BT_ADAPTER/CLI: $ADAPTER" -elif findhcisysfs >/dev/null 2>&1; then - ADAPTER="$(findhcisysfs 2>/dev/null || true)" else - ADAPTER="" + log_info "Using adapter selected during runtime readiness: $ADAPTER" fi if [ -n "$ADAPTER" ]; then @@ -145,34 +200,30 @@ fi if [ -n "$ADAPTER" ]; then log_info "Using adapter: $ADAPTER" else - log_warn "No HCI adapter found; skipping test." - echo "$TESTNAME SKIP" > "$res_file" - exit 0 + test_result_finish "FAIL" "No usable HCI adapter was found after runtime recovery" fi # ----------------------------- # 3. Ensure controller is visible # ----------------------------- if ! bt_ensure_controller_visible "$ADAPTER"; then - log_warn "SKIP — controller not visible to bluetoothctl." - echo "$TESTNAME SKIP" > "$res_file" - exit 0 + test_result_finish "FAIL" "Controller is not visible to bluetoothctl after runtime recovery" fi +test_result_record "PASS" "Bluetooth controller is visible to bluetoothctl" + # ----------------------------- # 4. Ensure power is ON # ----------------------------- pw="$(btgetpower "$ADAPTER" 2>/dev/null || true)" if [ "$pw" = "yes" ]; then - log_pass "Power ON verified before scan." + test_result_record "PASS" "Power ON was verified before scanning" else log_info "Controller Power=$pw — enabling now..." if ! btpower "$ADAPTER" on; then - log_fail "Failed to power ON controller." - echo "$TESTNAME FAIL" > "$res_file" - exit 0 + test_result_finish "FAIL" "Failed to power ON the Bluetooth controller" fi - log_pass "Power ON successful." + test_result_record "PASS" "Bluetooth controller powered on successfully" fi # ----------------------------- @@ -210,9 +261,9 @@ if bt_scan_devices "$TARGET_MAC"; then log_info "Discovering state after scan attempts: $dstate_on" if [ -n "$TARGET_MAC" ]; then - log_pass "Target MAC $TARGET_MAC detected." + test_result_record "PASS" "Target MAC $TARGET_MAC was detected" else - log_pass "At least one Bluetooth device discovered." + test_result_record "PASS" "At least one Bluetooth device was discovered" fi else dstate_on="$(bt_get_discovering 2>/dev/null || true)" @@ -225,8 +276,7 @@ else log_fail "No Bluetooth devices discovered after scan attempts." fi - echo "$TESTNAME FAIL" > "$res_file" - exit 0 + test_result_finish "FAIL" "Bluetooth scan did not discover the required device" fi # ----------------------------- @@ -241,12 +291,10 @@ fi # Use lib helper to avoid repetitive log spam and handle 'unknown' cleanly. if bt_scan_poll_off 10 1; then # On minimal/ramdisk images bt_scan_poll_off may treat persistent 'unknown' as non-fatal. - log_pass "Scan OFF cleanup completed." + test_result_record "PASS" "Scan OFF cleanup completed" else # If you keep bt_scan_poll_off strict, this may still warn; not a test failure. log_warn "Scan OFF cleanup did not confirm Discovering=no (non-fatal)." fi -echo "$TESTNAME PASS" > "$res_file" -exit 0 - +test_result_finish diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAIR.yaml b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAIR.yaml index 53496de8..a88ca864 100644 --- a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAIR.yaml +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAIR.yaml @@ -8,16 +8,15 @@ metadata: - functional params: - # Max pairing attempts per device - - PAIR_RETRIES: 3 - # Max scan retries per device before pairing - - SCAN_ATTEMPTS: 2 - # Overrides direct MAC (same format as CLI argument) - - BT_MAC_ENV: "" - # Overrides device name - - BT_NAME_ENV: "" - # Comma-separated MACs or names allowed for pairing - - BT_WHITELIST_ENV: "" + PAIR_RETRIES: "3" + SCAN_ATTEMPTS: "2" + BT_MAC_ENV: "" + BT_NAME_ENV: "" + BT_WHITELIST_ENV: "" + BT_ADAPTER: "" + BT_RUNTIME_READY_WAIT: "20" + BT_RUNTIME_RECOVERY_WAIT: "40" + BT_RUNTIME_RECOVERY_ATTEMPTS: "2" run: steps: diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh index fc42d031..6292ca6a 100755 --- a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh @@ -1,8 +1,11 @@ #!/bin/sh # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause -# 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" @@ -19,73 +22,149 @@ if [ -z "$INIT_ENV" ]; then exit 1 fi -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 -# shellcheck disable=SC1090,SC1091 +# shellcheck disable=SC1090 +. "$INIT_ENV" +# shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" -# shellcheck disable=SC1090,SC1091 +# shellcheck disable=SC1091 . "$TOOLS/lib_bluetooth.sh" -TESTNAME="BT_SCAN_PAIR" -test_path=$(find_test_case_by_name "$TESTNAME") || { - log_fail "$TESTNAME : Test directory not found." - echo "$TESTNAME FAIL" > "./$TESTNAME.res" - exit 0 +test_path=$(find_test_case_by_name "BT_SCAN_PAIR") || { + log_fail "BT_SCAN_PAIR FAIL - test directory not found" + printf '%s\n' "BT_SCAN_PAIR FAIL" >./BT_SCAN_PAIR.res + exit 1 } if ! cd "$test_path"; then - log_fail "$TESTNAME : Failed to cd into test directory: $test_path" - echo "$TESTNAME FAIL" > "./$TESTNAME.res" - exit 0 + log_fail "BT_SCAN_PAIR FAIL - failed to enter test directory $test_path" + printf '%s\n' "BT_SCAN_PAIR FAIL" >./BT_SCAN_PAIR.res + exit 1 fi +TESTNAME="BT_SCAN_PAIR" RES_FILE="./$TESTNAME.res" -rm -f "$RES_FILE" +test_result_init "$TESTNAME" "$RES_FILE" || exit 1 log_info "------------------------------------------------------------" log_info "Starting $TESTNAME Testcase" -# Capture BT_MAC from environment (LAVA secret) *before* we touch BT_MAC locally -BT_ENV_MAC="" -if [ -n "${BT_MAC:-}" ]; then - BT_ENV_MAC="$BT_MAC" -fi - # Defaults PAIR_RETRIES="${PAIR_RETRIES:-3}" +BT_RUNTIME_READY_WAIT="${BT_RUNTIME_READY_WAIT:-20}" +BT_RUNTIME_RECOVERY_WAIT="${BT_RUNTIME_RECOVERY_WAIT:-40}" +BT_RUNTIME_RECOVERY_ATTEMPTS="${BT_RUNTIME_RECOVERY_ATTEMPTS:-2}" +BT_ADAPTER="${BT_ADAPTER:-}" -BT_NAME="" +BT_ENV_MAC="${BT_MAC_ENV:-${BT_MAC:-}}" +BT_NAME="${BT_NAME_ENV:-}" BT_MAC="" -WHITELIST="" +WHITELIST="${BT_WHITELIST_ENV:-}" -# ------------------------- -# CLI parsing -# ------------------------- -if [ -n "$1" ]; then - if echo "$1" | grep -Eq '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then - # CLI MAC has highest priority - BT_MAC="$1" +usage() { + cat <&2 + return 2 + fi + + case "$1" in + --adapter) + BT_ADAPTER="$2" + ;; + --target) + set_pair_target "$2" + ;; + --whitelist) + WHITELIST="$2" + ;; + esac + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + --*) + log_error "Unknown argument: $1" + usage >&2 + return 2 + ;; + *) + positional_count=$((positional_count + 1)) + case "$positional_count" in + 1) + set_pair_target "$1" + ;; + 2) + WHITELIST="$1" + ;; + *) + log_error "Too many positional arguments" + usage >&2 + return 2 + ;; + esac + shift + ;; + esac + done +} + +parse_args "$@" || exit $? + +for positive_value in \ + "$PAIR_RETRIES" \ + "$BT_RUNTIME_READY_WAIT" \ + "$BT_RUNTIME_RECOVERY_WAIT" \ + "$BT_RUNTIME_RECOVERY_ATTEMPTS" +do + if ! is_unsigned_number "$positive_value" || [ "$positive_value" -eq 0 ]; then + log_error "Bluetooth pairing retry counts and wait values must be positive integers" + exit 2 + fi +done # If BT_MAC not set by CLI, fall back to BT_ENV_MAC (LAVA export) -if [ -z "$BT_MAC" ] && [ -n "$BT_ENV_MAC" ] && \ - echo "$BT_ENV_MAC" | grep -Eq '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then +if [ -z "$BT_MAC" ] && [ -z "$BT_NAME" ] && [ -n "$BT_ENV_MAC" ] && \ + printf '%s\n' "$BT_ENV_MAC" | grep -Eq '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then BT_MAC="$BT_ENV_MAC" fi # Optionally: if BT_MAC still empty and whitelist itself is a MAC, treat it as BT_MAC -if [ -z "$BT_MAC" ] && [ -n "$WHITELIST" ] && \ - echo "$WHITELIST" | grep -Eq '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then +if [ -z "$BT_MAC" ] && [ -z "$BT_NAME" ] && [ -n "$WHITELIST" ] && \ + printf '%s\n' "$WHITELIST" | grep -Eq '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then BT_MAC="$WHITELIST" fi @@ -95,22 +174,28 @@ fi # Skip if no MAC/name and no list file if [ -z "$BT_MAC" ] && [ -z "$BT_NAME" ] && [ ! -f "./bt_device_list.txt" ]; then - log_warn "No BT_MAC/BT_NAME and no bt_device_list.txt found. Skipping test." - echo "$TESTNAME SKIP" > "$RES_FILE" - exit 0 + test_result_finish "SKIP" "No Bluetooth target and no bt_device_list.txt were provided" fi if ! bt_prepare_ubuntu_stack; then - log_fail "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 0 + test_result_finish "FAIL" "$TESTNAME FAIL - Ubuntu Bluetooth stack preparation failed" fi -check_dependencies bluetoothctl rfkill expect hciconfig || { - log_warn "Missing required tools; skipping $TESTNAME." - echo "$TESTNAME SKIP" > "$RES_FILE" - exit 0 -} +if ! check_dependencies bluetoothctl rfkill expect hciconfig; then + test_result_finish "SKIP" "$TESTNAME SKIP - required Bluetooth tools are unavailable" +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" +fi + +BT_ADAPTER="$BT_RUNTIME_READY_ADAPTER" +test_result_record "PASS" "Bluetooth runtime is ready with usable adapter $BT_ADAPTER" # shellcheck disable=SC2317 # cleanup is invoked via trap cleanup_bt_test() { @@ -124,35 +209,34 @@ trap cleanup_bt_test EXIT rfkill unblock bluetooth 2>/dev/null || true # Detect adapter (no hardcoded hci0) -BT_ADAPTER="${BT_ADAPTER:-}" if [ -z "$BT_ADAPTER" ]; then - BT_ADAPTER="$(listhcis | head -n1)" + if command -v bt_select_usable_adapter >/dev/null 2>&1; then + BT_ADAPTER="$(bt_select_usable_adapter 2>/dev/null || true)" + else + BT_ADAPTER="$(listhcis | head -n1)" + fi fi if [ -z "$BT_ADAPTER" ]; then - log_fail "No Bluetooth HCI adapter found under /sys/class/bluetooth; cannot run $TESTNAME." - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 0 + test_result_finish "FAIL" "No usable Bluetooth HCI adapter was found after runtime recovery" fi log_info "Detected Bluetooth adapter: $BT_ADAPTER" # Ensure controller is visible to bluetoothctl (public-addr bootstrap) if bt_ensure_controller_visible "$BT_ADAPTER"; then - log_info "Bluetooth controller is visible to bluetoothctl after bt_ensure_controller_visible." + test_result_record "PASS" "Bluetooth controller is visible to bluetoothctl" else - log_fail "Bluetooth controller is not visible to bluetoothctl after bt_ensure_controller_visible." - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 0 + test_result_finish "FAIL" "Bluetooth controller is not visible to bluetoothctl after runtime recovery" fi -# Power on adapter via existing helper; do this regardless of HCICONFIG state. +# Power on adapter via the existing helper regardless of hciconfig state. if ! btpower "$BT_ADAPTER" on; then - log_fail "Failed to power on adapter $BT_ADAPTER via btpower (RF-kill/firmware issue?)." - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 0 + test_result_finish "FAIL" "Failed to power on adapter $BT_ADAPTER" fi +test_result_record "PASS" "Bluetooth adapter $BT_ADAPTER is powered on" + # Optional debug: show BD address and confirm firmware/driver presence bdaddr="$(btgetbdaddr "$BT_ADAPTER" 2>/dev/null || true)" [ -n "$bdaddr" ] && log_info "Adapter $BT_ADAPTER BD_ADDR=$bdaddr" @@ -162,7 +246,7 @@ if ! btkmdpresent; then fi if ! btfwpresent >/dev/null 2>&1; then - log_warn "No obvious Bluetooth firmware files found (btfwpresent); continuing anyway." + log_warn "No obvious Bluetooth firmware files found with btfwpresent, continuing anyway." fi # Remove any previously paired devices to start clean @@ -171,10 +255,9 @@ bt_remove_all_paired_devices # Helper: l2ping link verification verify_link() { mac="$1" - if bt_l2ping_check "$mac" "$RES_FILE"; then - log_pass "l2ping link check succeeded for $mac" - echo "$TESTNAME PASS" > "$RES_FILE" - exit 0 + if bt_l2ping_check "$mac" "./l2ping.log"; then + test_result_record "PASS" "l2ping link check succeeded for $mac" + test_result_finish else log_warn "l2ping link check failed for $mac" fi @@ -191,12 +274,12 @@ if [ -n "$BT_MAC" ]; then bt_cleanup_paired_device "$BT_MAC" if bt_pair_with_mac "$BT_MAC"; then - log_info "Pair succeeded; attempting post-pair connect to $BT_MAC" + log_info "Pair succeeded, attempting post-pair connect to $BT_MAC" if bt_post_pair_connect "$BT_MAC"; then log_pass "Post-pair connect succeeded for $BT_MAC" verify_link "$BT_MAC" else - log_warn "Post-pair connect failed; trying l2ping fallback for $BT_MAC" + log_warn "Post-pair connect failed, trying l2ping fallback for $BT_MAC" verify_link "$BT_MAC" bt_cleanup_paired_device "$BT_MAC" fi @@ -206,9 +289,7 @@ if [ -n "$BT_MAC" ]; then done log_warn "Exhausted direct pairing attempts for $BT_MAC" - log_fail "Direct pairing failed for ${BT_MAC:-$BT_NAME}" - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 0 + test_result_finish "FAIL" "Direct pairing failed for ${BT_MAC:-$BT_NAME}" fi # ------------------------- @@ -217,14 +298,16 @@ fi if [ -z "$BT_MAC" ] && [ -z "$BT_NAME" ] && [ -f "./bt_device_list.txt" ]; then # Skip if list is empty or only comments if ! grep -v -e '^[[:space:]]*#' -e '^[[:space:]]*$' bt_device_list.txt | grep -q .; then - log_warn "bt_device_list.txt is empty or only comments. Skipping test." - echo "$TESTNAME SKIP" > "$RES_FILE" - exit 0 + test_result_finish "SKIP" "bt_device_list.txt is empty or contains only comments" fi log_info "Using fallback device list in bt_device_list.txt" while IFS= read -r line || [ -n "$line" ]; do - case "$line" in ''|\#*) continue ;; esac + case "$line" in + ''|\#*) + continue + ;; + esac # split into MAC and NAME (simple space-separated) IFS=' ' read -r MAC NAME < "$RES_FILE" - exit 0 + test_result_finish "FAIL" "All fallback devices from bt_device_list.txt failed" fi # Should never reach here -log_fail "No execution path matched; exiting with FAIL." -echo "$TESTNAME FAIL" > "$RES_FILE" -exit 0 +test_result_finish "FAIL" "No Bluetooth pairing execution path matched" diff --git a/Runner/utils/lib_bluetooth.sh b/Runner/utils/lib_bluetooth.sh index 117e0b3c..48f3acd4 100755 --- a/Runner/utils/lib_bluetooth.sh +++ b/Runner/utils/lib_bluetooth.sh @@ -638,7 +638,7 @@ bt_scan_devices_interactive_fallback() { log_warn "bt_scan_devices: trying interactive bluetoothctl fallback on $adapter for ${scan_window}s" - if command -v timeout >/dev/null 2>&1; then + if command -v run_with_timeout >/dev/null 2>&1; then fallback_out="$( { printf 'select %s\n' "$adapter" @@ -652,7 +652,7 @@ bt_scan_devices_interactive_fallback() { printf 'devices\n' sleep 1 printf 'quit\n' - } | timeout "$total_timeout" bluetoothctl 2>&1 | sanitize_bt_output || true + } | run_with_timeout "$total_timeout" bluetoothctl 2>&1 | sanitize_bt_output || true )" else fallback_out="$( @@ -921,10 +921,10 @@ bt_scan_devices() { log_warn "bt_scan_devices: btpower($adapter on) did not report success; continuing" fi else - bluetoothctl power on >/dev/null 2>&1 || true + run_with_timeout 3 bluetoothctl power on >/dev/null 2>&1 || true fi - bluetoothctl select "$adapter" >/dev/null 2>&1 || true + run_with_timeout 3 bluetoothctl select "$adapter" >/dev/null 2>&1 || true attempt=1 found_lines="" @@ -934,9 +934,10 @@ bt_scan_devices() { live_out="$( { - bluetoothctl select "$adapter" 2>/dev/null || true - bluetoothctl power on 2>/dev/null || true - bluetoothctl --timeout "$scan_window" scan on 2>&1 || true + run_with_timeout 3 bluetoothctl select "$adapter" 2>/dev/null || true + run_with_timeout 3 bluetoothctl power on 2>/dev/null || true + run_with_timeout "$((scan_window + 5))" \ + bluetoothctl --timeout "$scan_window" scan on 2>&1 || true } | sanitize_bt_output )" @@ -945,7 +946,7 @@ bt_scan_devices() { log_warn "bt_scan_devices: bt_set_scan(off) reported failure after attempt $attempt" fi else - bluetoothctl scan off >/dev/null 2>&1 || true + run_with_timeout 5 bluetoothctl scan off >/dev/null 2>&1 || true fi if command -v bt_list_devices_raw >/dev/null 2>&1; then @@ -1728,7 +1729,7 @@ btctl_script() { BTINTERACTIVEMODELOGGED=1 fi - if command -v timeout >/dev/null 2>&1; then + if command -v run_with_timeout >/dev/null 2>&1; then { for line in "$@"; do [ -n "$line" ] || continue @@ -1736,7 +1737,7 @@ btctl_script() { sleep 0.2 done sleep 1 - } | timeout 6 bluetoothctl 2>/dev/null + } | run_with_timeout 6 bluetoothctl 2>/dev/null return $? fi @@ -1798,6 +1799,7 @@ bt_set_scan() { off) timeout="${BT_SCAN_OFF_TIMEOUT:-5}" + discovering_state="" if command -v log_info >/dev/null 2>&1; then log_info "bt_set_scan(off): running 'bluetoothctl --timeout $timeout scan off'" @@ -1812,22 +1814,31 @@ bt_set_scan() { done fi - # Treat "already stopped" as success too + # Treat an explicitly confirmed stop as success. if printf '%s\n' "$out" | grep -q "Discovery stopped"; then sleep 1 return 0 fi if printf '%s\n' "$out" | grep -qi "Failed to stop discovery"; then - if command -v log_info >/dev/null 2>&1; then - log_info "bt_set_scan(off): BlueZ reported stop failure. Discovering may already be stopped, treating as non-fatal" + discovering_state="$(bt_is_discovering 2>/dev/null || true)" + [ -n "$discovering_state" ] || discovering_state="unknown" + + if [ "$discovering_state" = "no" ]; then + if command -v log_info >/dev/null 2>&1; then + log_info "bt_set_scan(off): discovery is already stopped, treating scan off as success" + fi + sleep 1 + return 0 fi - sleep 1 - return 0 fi # Fallback: interactive bluetoothctl (needed on minimal/ramdisk) if command -v log_warn >/dev/null 2>&1; then - log_warn "bt_set_scan(off): non-interactive scan off did not confirm stop; falling back to interactive bluetoothctl." + if [ -n "$discovering_state" ]; then + log_warn "bt_set_scan(off): BlueZ failed to stop discovery and state is $discovering_state, falling back to interactive bluetoothctl" + else + log_warn "bt_set_scan(off): non-interactive scan off did not confirm stop, falling back to interactive bluetoothctl" + fi fi out2="$(btctl_script "scan off" "quit" 2>/dev/null | sanitize_bt_output || true)" @@ -1836,11 +1847,20 @@ bt_set_scan() { return 0 fi if printf '%s\n' "$out2" | grep -qi "Failed to stop discovery"; then - if command -v log_info >/dev/null 2>&1; then - log_info "bt_set_scan(off): discovery already stopped, treating as success" + discovering_state="$(bt_is_discovering 2>/dev/null || true)" + [ -n "$discovering_state" ] || discovering_state="unknown" + + if [ "$discovering_state" = "no" ]; then + if command -v log_info >/dev/null 2>&1; then + log_info "bt_set_scan(off): discovery is already stopped, treating scan off as success" + fi + sleep 1 + return 0 + fi + + if command -v log_warn >/dev/null 2>&1; then + log_warn "bt_set_scan(off): interactive scan off failed and discovery state is $discovering_state" fi - sleep 1 - return 0 fi sleep 1 @@ -2074,7 +2094,7 @@ btcontrollerpresent() { # ret: 0=controller visible, 1=not visible # This is just a clearer alias/wrapper if you prefer the name. bt_controller_visible() { - btcontrollerpresent + btcontrollervisible "${1:-}" } # Usage: btensurepublicaddr hci0 @@ -2089,10 +2109,15 @@ btensurepublicaddr() { dev="${1:-}" # Already visible: nothing to do. - if btcontrollerpresent || bt_controller_visible "$dev"; then + if 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 + fi mac="$( btgetbdaddr "$dev" 2>/dev/null \ @@ -2114,10 +2139,20 @@ quit" >/dev/null 2>&1 || true # Poll for controller visibility (BlueZ can be async) i=0 - max_wait=15 # was 5; 15 is still small but avoids flakiness - while [ "$i" -lt "$max_wait" ]; do - if btcontrollerpresent || bt_controller_visible "$dev"; then - log_info "controller visible after public-addr $mac (waited ${i}s)" + max_wait="${BT_CONTROLLER_VISIBLE_WAIT:-15}" + + case "$max_wait" in + ''|*[!0-9]*) + max_wait=15 + ;; + 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" return 0 fi sleep 1 @@ -2157,7 +2192,9 @@ btcontrollervisible() { esac btctlrun() { - if command -v timeout >/dev/null 2>&1; then + if command -v run_with_timeout >/dev/null 2>&1; then + run_with_timeout 2 bluetoothctl "$@" 2>/dev/null || true + elif command -v timeout >/dev/null 2>&1; then timeout 2 bluetoothctl "$@" 2>/dev/null || true else bluetoothctl "$@" 2>/dev/null || true @@ -2197,7 +2234,7 @@ bt_ensure_controller_visible() { adapter="${1:-}" # Fast path: already visible - if btcontrollerpresent || bt_controller_visible "$adapter"; then + if btcontrollervisible "$adapter"; then return 0 fi @@ -2217,7 +2254,7 @@ bt_ensure_controller_visible() { fi # Final controller visibility check - if btcontrollerpresent || bt_controller_visible "$adapter"; then + if btcontrollervisible "$adapter"; then return 0 fi @@ -2495,7 +2532,11 @@ bt_pair_once() { # Get current Discovering state from bluetoothctl show. # Prints one of: yes | no | unknown bt_get_discovering() { - out="$(bluetoothctl show 2>/dev/null | sanitize_bt_output | tr -d '\r')" + out="$( + run_with_timeout 3 bluetoothctl show 2>/dev/null \ + | sanitize_bt_output \ + | tr -d '\r' + )" case "$out" in *"Discovering: yes"*) @@ -2535,7 +2576,10 @@ bt_wait_discovering() { # Raw devices output from bluetoothctl bt_list_devices_raw() { - out="$(bluetoothctl devices 2>/dev/null | sanitize_bt_output || true)" + out="$( + run_with_timeout 3 bluetoothctl devices 2>/dev/null \ + | sanitize_bt_output || true + )" if [ -z "$out" ]; then # If controller list is already known-flaky, mark fallback so btctl_script logs once. @@ -2731,19 +2775,13 @@ btgetpower() { if [ -n "$mac" ]; then out="$( - { - printf 'show %s\n' "$mac" - sleep 1 - printf 'quit\n' - } | bluetoothctl 2>/dev/null | sanitize_bt_output || true + btctl_script "show $mac" "quit" 2>/dev/null \ + | sanitize_bt_output || true )" else out="$( - { - printf 'show\n' - sleep 1 - printf 'quit\n' - } | bluetoothctl 2>/dev/null | sanitize_bt_output || true + btctl_script "show" "quit" 2>/dev/null \ + | sanitize_bt_output || true )" fi @@ -2761,11 +2799,8 @@ btgetpower() { # Fallback: try default controller if adapter-specific attempt didn’t yield Powered: if [ -z "$state" ]; then out="$( - { - printf 'show\n' - sleep 1 - printf 'quit\n' - } | bluetoothctl 2>/dev/null | sanitize_bt_output || true + btctl_script "show" "quit" 2>/dev/null \ + | sanitize_bt_output || true )" state="$(printf '%s\n' "$out" \ | awk -F':[[:space:]]*' ' @@ -2847,11 +2882,8 @@ btpower() { # If Powered line is not available yet, try to parse PowerState as an informational fallback # (Some stacks lag on Powered; PowerState can show transitions like off-enabling/on-disabling.) out="$( - { - printf 'show\n' - sleep 1 - printf 'quit\n' - } | bluetoothctl 2>/dev/null | sanitize_bt_output || true + btctl_script "show" "quit" 2>/dev/null \ + | sanitize_bt_output || true )" pstate="$(printf '%s\n' "$out" \ @@ -2914,57 +2946,215 @@ btfwpresent() { } bt_wait_ready() { - max_wait="${1:-60}" - sleep_step="${2:-2}" - waited=0 - started_service=0 - - if [ -z "$max_wait" ]; then - max_wait=60 + bt_wr_max_wait="${1:-60}" + bt_wr_sleep_step="${2:-2}" + bt_wr_requested_adapter="${3:-${BT_ADAPTER:-}}" + bt_wr_waited=0 + bt_wr_started_service=0 + bt_wr_adapter="" + + BT_RUNTIME_READY_ADAPTER="" + + if [ -z "$bt_wr_max_wait" ]; then + bt_wr_max_wait=60 fi - if [ -z "$sleep_step" ]; then - sleep_step=2 + if [ -z "$bt_wr_sleep_step" ]; then + bt_wr_sleep_step=2 fi - - case "$max_wait" in + + case "$bt_wr_max_wait" in ''|*[!0-9]*) - max_wait=60 + bt_wr_max_wait=60 ;; esac - case "$sleep_step" in + case "$bt_wr_sleep_step" in ''|*[!0-9]*) - sleep_step=2 + bt_wr_sleep_step=2 ;; esac - - if [ "$max_wait" -le 0 ] 2>/dev/null; then - max_wait=60 + + if [ "$bt_wr_max_wait" -le 0 ] 2>/dev/null; then + bt_wr_max_wait=60 fi - if [ "$sleep_step" -le 0 ] 2>/dev/null; then - sleep_step=2 + if [ "$bt_wr_sleep_step" -le 0 ] 2>/dev/null; then + bt_wr_sleep_step=2 fi - - while [ "$waited" -lt "$max_wait" ]; do - if btsvcactive && bthcipresent; then - log_info "Bluetooth runtime became ready after ${waited}s." + + while [ "$bt_wr_waited" -le "$bt_wr_max_wait" ]; do + if [ -n "$bt_wr_requested_adapter" ]; then + bt_wr_adapter="$bt_wr_requested_adapter" + else + bt_wr_fallback_adapter="$(listhcis 2>/dev/null | sed -n '1p')" + bt_wr_adapter="$( + listhcis 2>/dev/null | + while IFS= read -r bt_wr_candidate; do + if btbdok "$bt_wr_candidate"; then + printf '%s\n' "$bt_wr_candidate" + break + fi + done + )" + + if [ -z "$bt_wr_adapter" ]; then + bt_wr_adapter="$bt_wr_fallback_adapter" + fi + fi + + if btsvcactive && + [ -n "$bt_wr_adapter" ] && + btbdok "$bt_wr_adapter"; then + BT_RUNTIME_READY_ADAPTER="$bt_wr_adapter" + log_info "Bluetooth runtime became ready after ${bt_wr_waited}s with usable adapter $bt_wr_adapter." return 0 fi - - if [ "$started_service" -eq 0 ]; then + + if [ "$bt_wr_started_service" -eq 0 ]; then if command -v systemctl >/dev/null 2>&1; then if ! btsvcactive; then log_info "Bluetooth service not active yet, attempting start." systemctl start bluetooth.service >/dev/null 2>&1 || true fi fi - started_service=1 + bt_wr_started_service=1 fi - - sleep "$sleep_step" - waited=$((waited + sleep_step)) + + if [ "$bt_wr_waited" -eq "$bt_wr_max_wait" ]; then + break + fi + + bt_wr_delay="$bt_wr_sleep_step" + bt_wr_remaining=$((bt_wr_max_wait - bt_wr_waited)) + if [ "$bt_wr_delay" -gt "$bt_wr_remaining" ]; then + bt_wr_delay="$bt_wr_remaining" + fi + + sleep "$bt_wr_delay" + bt_wr_waited=$((bt_wr_waited + bt_wr_delay)) done - - log_warn "Bluetooth runtime did not become ready within ${max_wait}s." + + if [ -n "$bt_wr_adapter" ]; then + log_warn "Bluetooth runtime did not become ready within ${bt_wr_max_wait}s, adapter $bt_wr_adapter has no valid BD address" + else + log_warn "Bluetooth runtime did not become ready within ${bt_wr_max_wait}s, no HCI adapter appeared" + fi + + return 1 +} + +# Perform one controlled recovery attempt for an incompletely initialized +# Bluetooth runtime. The controller is left unblocked and bluetooth.service is +# left running for subsequent tests. +# +# Usage: +# bt_recover_runtime [ADAPTER] +bt_recover_runtime() { + bt_rr_adapter="${1:-}" + bt_rr_action=0 + + log_warn "Attempting one controlled Bluetooth runtime recovery" + + if command -v rfkill >/dev/null 2>&1; then + log_info "Cycling the Bluetooth software rfkill state" + rfkill block bluetooth >/dev/null 2>&1 || true + sleep 2 + rfkill unblock bluetooth >/dev/null 2>&1 || true + sleep 2 + bt_rr_action=1 + elif command -v rfkillunblocksysfs >/dev/null 2>&1; then + log_info "Unblocking Bluetooth through sysfs" + rfkillunblocksysfs >/dev/null 2>&1 || true + bt_rr_action=1 + fi + + if command -v systemctl >/dev/null 2>&1; then + log_info "Restarting bluetooth.service after the recovery attempt" + if command -v run_with_timeout >/dev/null 2>&1; then + run_with_timeout 15 \ + systemctl restart bluetooth.service >/dev/null 2>&1 || true + else + systemctl restart bluetooth.service >/dev/null 2>&1 || true + fi + bt_rr_action=1 + 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 + fi + + if [ "$bt_rr_action" -eq 0 ]; then + log_warn "No supported Bluetooth recovery mechanism is available" + return 1 + fi + + return 0 +} + +# Ensure Bluetooth reaches a usable state, including bounded recovery retries. +# A usable runtime has an active service and an HCI adapter with a valid, +# non-zero BD address. BT_RUNTIME_RECOVERED and BT_RUNTIME_READY_ADAPTER are +# updated for callers that want to report whether recovery was required. +# +# Usage: +# bt_ensure_runtime_ready [ADAPTER] [INITIAL_WAIT] [RECOVERY_WAIT] [ATTEMPTS] +bt_ensure_runtime_ready() { + bt_err_adapter="${1:-${BT_ADAPTER:-}}" + bt_err_initial_wait="${2:-${BT_RUNTIME_READY_WAIT:-20}}" + bt_err_recovery_wait="${3:-${BT_RUNTIME_RECOVERY_WAIT:-40}}" + bt_err_attempts="${4:-${BT_RUNTIME_RECOVERY_ATTEMPTS:-2}}" + bt_err_attempt=1 + + # Exported result state for suite callers. + # shellcheck disable=SC2034 + BT_RUNTIME_RECOVERED=0 + # shellcheck disable=SC2034 + BT_RUNTIME_READY_ADAPTER="" + + for bt_err_value in \ + "$bt_err_initial_wait" \ + "$bt_err_recovery_wait" \ + "$bt_err_attempts" + do + case "$bt_err_value" in + ''|*[!0-9]*|0) + log_error "Bluetooth readiness waits and attempts must be positive integers" + return 2 + ;; + esac + done + + if bt_wait_ready "$bt_err_initial_wait" 2 "$bt_err_adapter"; then + return 0 + fi + + while [ "$bt_err_attempt" -le "$bt_err_attempts" ]; do + log_warn "Bluetooth recovery attempt $bt_err_attempt/$bt_err_attempts" + + bt_err_recovery_adapter="$bt_err_adapter" + if [ -z "$bt_err_recovery_adapter" ]; then + bt_err_recovery_adapter="$(listhcis 2>/dev/null | sed -n '1p')" + fi + + if bt_recover_runtime "$bt_err_recovery_adapter"; then + log_info "Waiting up to ${bt_err_recovery_wait}s after Bluetooth recovery" + + if bt_wait_ready \ + "$bt_err_recovery_wait" \ + 2 \ + "$bt_err_adapter"; then + # shellcheck disable=SC2034 + BT_RUNTIME_RECOVERED=1 + log_warn "Bluetooth runtime recovered on attempt $bt_err_attempt" + return 0 + fi + else + log_warn "Bluetooth recovery attempt $bt_err_attempt could not perform a recovery action" + fi + + bt_err_attempt=$((bt_err_attempt + 1)) + done + + log_error "Bluetooth runtime remained unusable after $bt_err_attempts controlled recovery attempts" return 1 }