diff --git a/Runner/suites/Connectivity/Bluetooth/README.md b/Runner/suites/Connectivity/Bluetooth/BT_ON_FF/README.md similarity index 100% rename from Runner/suites/Connectivity/Bluetooth/README.md rename to Runner/suites/Connectivity/Bluetooth/BT_ON_FF/README.md diff --git a/Runner/suites/Connectivity/Bluetooth/run.sh b/Runner/suites/Connectivity/Bluetooth/BT_ON_FF/run.sh similarity index 90% rename from Runner/suites/Connectivity/Bluetooth/run.sh rename to Runner/suites/Connectivity/Bluetooth/BT_ON_FF/run.sh index 94699aa5..bb479e05 100755 --- a/Runner/suites/Connectivity/Bluetooth/run.sh +++ b/Runner/suites/Connectivity/Bluetooth/BT_ON_FF/run.sh @@ -2,8 +2,8 @@ # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. # SPDX-License-Identifier: BSD-3-Clause-Clear - -# Source init_env and functestlib.sh + +# Robustly find and source init_env SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" INIT_ENV="" SEARCH="$SCRIPT_DIR" @@ -14,31 +14,32 @@ while [ "$SEARCH" != "/" ]; do fi SEARCH=$(dirname "$SEARCH") done - + if [ -z "$INIT_ENV" ]; then echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 exit 1 fi - -# shellcheck disable=SC1090 -. "$INIT_ENV" - + +if [ -z "$__INIT_ENV_LOADED" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" +fi # shellcheck disable=SC1090,SC1091 . "$TOOLS/functestlib.sh" - -TESTNAME="Bluetooth" + +TESTNAME="BT_ON_FF" test_path=$(find_test_case_by_name "$TESTNAME") || { log_fail "$TESTNAME : Test directory not found." echo "$TESTNAME FAIL" > "./$TESTNAME.res" exit 1 } - + cd "$test_path" || exit 1 res_file="./$TESTNAME.res" rm -f "$res_file" - -log_info "-----------------------------------------------------------------------------------------" -log_info "-------------------Starting $TESTNAME Testcase----------------------------" + +log_info "------------------------------------------------------------" +log_info "Starting $TESTNAME Testcase" log_info "Checking dependency: bluetoothctl" check_dependencies bluetoothctl diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAID_README.md b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAID_README.md new file mode 100644 index 00000000..a5dd66ac --- /dev/null +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAID_README.md @@ -0,0 +1,149 @@ + +# Bluetooth BT_SCAN_PAIR Test + +This test automates Bluetooth scanning and pairing for embedded Linux devices using BlueZ and bluetoothctl. It is designed for use in the [qcom-linux-testkit](https://github.com/qualcomm-linux/qcom-linux-testkit) test suite. + +## Features + +- Scans for Bluetooth devices +- Optionally pairs with a device by name or MAC address +- Retries pairing on failure, including handling for busy/temporarily unavailable devices +- Cleans up previous pairings for repeatable CI runs +- Accepts device name/MAC as argument, environment variable, or in `bt_device_list.txt` +- Generates summary and detailed logs (`scan.log`, `pair.log`, `found_devices.log`) + +## Usage + +```sh +./run.sh [DEVICE_NAME_OR_MAC] [WHITELIST] +``` +- `DEVICE_NAME_OR_MAC` – (optional) Device name or MAC address to pair. + - Can also be set as `BT_NAME_ENV` or in `bt_device_list.txt` +- `WHITELIST` – (optional) Comma-separated MACs/names allowed for pairing. + - Can also be set as `BT_WHITELIST_ENV` + +If no device name is given, only scanning is performed and the test passes if devices are found. + +## Examples + +```sh +./run.sh [BT_NAME] [WHITELIST] +``` + +- `BT_NAME` - Optional. Bluetooth name or MAC to search for. +- `WHITELIST` - Optional. Comma-separated names/MACs allowed for pairing. + +- Scan for any device (no pairing): + + ``` + ./run.sh + ``` + +- Scan and pair with a device named "MySpeaker": + + ``` + ./run.sh MySpeaker + ``` + +- Scan and pair only if device MAC is in whitelist: + + ``` + ./run.sh MySpeaker 00:11:22:33:44:55,AnotherSpeaker + ``` + +- Use environment variables: + + ``` + export BT_NAME_ENV="MySpeaker" + export BT_WHITELIST_ENV="00:11:22:33:44:55" + ./run.sh + ``` + +- Device list file (first line is used): + + ``` + echo "MySpeaker" > bt_device_list.txt + ./run.sh + ``` + +## Whitelist Usage + +To ensure only known devices are considered during scan: + +```sh +./run.sh JBL_Speaker "JBL_Speaker,12:34:56:78:9A:BC" +``` + +## Arguments & Variables + +- Argument 1: Device name or MAC address (takes precedence) +- `BT_NAME_ENV`: Device name or MAC from the environment +- `bt_device_list.txt`: Fallback if argument or env is not set + +## Example Summary Output + +``` +[INFO] 2025-06-23 10:00:00 - Starting BT_SCAN_PAIR Testcase +[INFO] 2025-06-23 10:00:02 - Unblocking and powering on Bluetooth +[INFO] 2025-06-23 10:00:05 - Devices found during scan: +Device 12:34:56:78:9A:BC SomeBTHeadset +[INFO] 2025-06-23 10:00:06 - Expected device 'SomeBTHeadset' found in scan +[PASS] 2025-06-23 10:00:08 - Pairing successful with 12:34:56:78:9A:BC +``` + +## Result + +- PASS: Device paired (or scan-only with no target) +- FAIL: Device not found, not in whitelist, or pairing failed +- All paired devices are removed after the test + +## Files Generated + +- `BT_SCAN_PAIR.res`: Test PASS/FAIL/SKIP result +- `scan.log`: Output of Bluetooth device scan +- `found_devices.log`: List of discovered device names/MACs +- `pair.log`: Detailed pairing output and errors + +## Troubleshooting + +- Ensure `bluetoothctl`, `rfkill`, `expect`, and `hciconfig` are available. +- For headless automation, the remote device must be in pairing/discoverable mode. +- The script retries pairing if "busy" or "temporarily unavailable" errors are seen. +- Check `pair.log` and `scan.log` for detailed debug info if a failure occurs. + +## Helper Functions (in functestlib.sh) + +- `bt_scan_devices` – Scans and logs found BT devices +- `bt_pair_with_mac` – Attempts pairing via expect with retries +- `bt_in_whitelist` – Checks if MAC/name is in whitelist +- `bt_cleanup_paired_device` – Removes paired device by MAC + +## Customization + +- **Whitelist**: You can restrict scan to a whitelist of MAC addresses or names using environment variables or script customization. +- **Retries/Timeouts**: Retry and timeout values can be set in the script for more robust pairing. + +## Integration with LAVA + +In your LAVA job: + +```yaml +deploy: + to: tftp + images: + bt_device_list.txt: + image: path/to/bt_device_list.txt + compression: none +``` + +Injects a per-DUT Bluetooth configuration. + +## Dependencies + +- `bluetoothctl`, `expect`, `rfkill`, `hciconfig` +- BlueZ stack running on embedded Linux + +## License + +SPDX-License-Identifier: BSD-3-Clause-Clear +Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/bt_device_list.txt b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/bt_device_list.txt new file mode 100755 index 00000000..e69de29b diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/bt_scan_pair_job.yaml b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/bt_scan_pair_job.yaml new file mode 100644 index 00000000..e7b8b032 --- /dev/null +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/bt_scan_pair_job.yaml @@ -0,0 +1,35 @@ +job_name: BT Scan and Pair Test +device_type: qcom-dut +priority: medium +timeouts: + job: + minutes: 10 + action: + minutes: 5 +visibility: + group: everyone + +actions: +- deploy: + timeout: + minutes: 2 + to: tftp + images: + bt_device_list.txt: + image: bt_device_list.txt + compression: none + +- boot: + method: minimal + +- test: + timeout: + minutes: 5 + definitions: + - name: bt_scan_pair + from: inline + path: inline/bt_scan_pair.sh + inline: + - chmod +x ./run.sh + - ./run.sh + diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh new file mode 100755 index 00000000..e6b2abbb --- /dev/null +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh @@ -0,0 +1,116 @@ +#!/bin/sh + +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# SPDX-License-Identifier: BSD-3-Clause-Clear + +# Robustly find and source init_env +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +INIT_ENV="" +SEARCH="$SCRIPT_DIR" +while [ "$SEARCH" != "/" ]; do + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH=$(dirname "$SEARCH") +done + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +if [ -z "$__INIT_ENV_LOADED" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" +fi +# shellcheck disable=SC1090,SC1091 +. "$TOOLS/functestlib.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 1 +} +cd "$test_path" || exit 1 +RES_FILE="./$TESTNAME.res" +rm -f "$RES_FILE" + +log_info "------------------------------------------------------------" +log_info "Starting $TESTNAME Testcase" + +BT_NAME="" +BT_MAC="" +WHITELIST="" +PAIR_RETRIES="${PAIR_RETRIES:-3}" + +# Check bt_device_list.txt if present +if [ -f "./bt_device_list.txt" ]; then + BT_NAME=$(awk '!/^#/ && NF {print $2}' ./bt_device_list.txt | head -n1) + BT_MAC=$(awk '!/^#/ && NF {print $1}' ./bt_device_list.txt | head -n1) +fi + +# Command-line override +[ -n "$1" ] && BT_NAME="$1" +[ -n "$2" ] && WHITELIST="$2" + +check_dependencies bluetoothctl rfkill expect hciconfig || { + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 +} + +cleanup_bt_test() { + [ -n "$BT_MAC" ] && bt_cleanup_paired_device "$BT_MAC" + killall -q bluetoothctl 2>/dev/null +} +trap cleanup_bt_test EXIT + +rfkill unblock bluetooth +retry_command_bt "hciconfig hci0 up" "Bring up hci0" || { + log_fail "Failed to bring up hci0" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +} + +log_info "Scanning for Bluetooth devices..." +bt_scan_devices +log_info "Devices found during scan:" +cat found_devices.log + +if [ -z "$BT_NAME" ] && [ -z "$BT_MAC" ]; then + log_pass "No device specified. Scan-only mode." + echo "$TESTNAME PASS" > "$RES_FILE" + exit 0 +fi + +MATCH_FOUND=0 +while IFS= read -r line; do + mac=$(echo "$line" | awk '{print $3}') + name=$(echo "$line" | cut -d' ' -f4-) + if { [ "$mac" = "$BT_MAC" ] || [ "$name" = "$BT_NAME" ]; } && \ + { bt_in_whitelist "$mac" "$WHITELIST" || bt_in_whitelist "$name" "$WHITELIST"; }; then + BT_MAC="$mac" + MATCH_FOUND=1 + break + fi +done < found_devices.log + +if [ "$MATCH_FOUND" -ne 1 ]; then + log_fail "Expected device not found or not in whitelist" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi + +log_info "Attempting to pair with $BT_NAME ($BT_MAC)" +if bt_pair_with_mac "$BT_MAC" "$PAIR_RETRIES"; then + log_pass "Pairing successful" + echo "$TESTNAME PASS" > "$RES_FILE" + exit 0 +else + log_fail "Pairing failed after $PAIR_RETRIES retries" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi + +log_info "Completed $TESTNAME Testcase" diff --git a/Runner/utils/functestlib.sh b/Runner/utils/functestlib.sh index 68612bd7..f0988c74 100755 --- a/Runner/utils/functestlib.sh +++ b/Runner/utils/functestlib.sh @@ -391,3 +391,92 @@ weston_start() { fi } +# Remove paired BT device by MAC +bt_cleanup_paired_device() { + mac="$1" + if [ -n "$mac" ]; then + log_info "Cleaning up: Removing paired BT device $mac" + bluetoothctl remove "$mac" >/dev/null 2>&1 + fi +} + +# Retry a shell command N times with sleep +retry_command_bt() { + cmd="$1" + msg="$2" + max="${3:-3}" + count=1 + while [ "$count" -le "$max" ]; do + if eval "$cmd"; then + return 0 + fi + log_warn "Retry $count/$max failed: $msg" + count=$((count + 1)) + sleep 2 + done + return 1 +} + +# Scan for BT devices, returns scan.log file and optionally filtered devices +bt_scan_devices() { + bluetoothctl --timeout 12 scan on > scan.log 2>&1 & + sleep 14 + killall -q bluetoothctl 2>/dev/null + grep -i 'Device' scan.log | sort | uniq > found_devices.log +} + +# Find MAC by device name in scan.log (case-insensitive) +bt_find_mac_by_name() { + target="$1" + log="$2" + grep -i "$target" "$log" | awk '{print $3}' | head -n1 +} + +# Is device in whitelist? (names or MACs, comma-separated) +bt_in_whitelist() { + dev="$1" + whitelist="$2" + if [ -z "$whitelist" ]; then return 0; fi # empty whitelist = accept all + echo "$whitelist" | tr ',' '\n' | while read -r w; do + [ "$dev" = "$w" ] && return 0 + done + return 1 +} + +# Pair with BT device using expect, with busy/retry logic +bt_pair_with_mac() { + mac="$1" + retries="${2:-3}" + i=1 + while [ $i -le "$retries" ]; do + expect < pair.log 2>&1 +spawn bluetoothctl +expect "#" +send "agent on\r" +expect "#" +send "default-agent\r" +expect "#" +send "pair $mac\r" +expect { + "Pairing successful" { exit 0 } + "Failed to pair: org.bluez.Error.AlreadyExists" { exit 0 } + "Failed to pair: org.bluez.Error.InProgress" { exit 1 } + "Failed to pair: org.bluez.Error.AuthenticationFailed" { exit 1 } + "Failed to pair: org.bluez.Error.Busy" { exit 2 } + timeout { exit 1 } +} +EOF + rc=$? + if [ "$rc" -eq 0 ]; then + return 0 + elif [ "$rc" -eq 2 ]; then + log_warn "Pairing busy, will retry ($i/$retries)..." + sleep 3 + else + log_warn "Pairing failed attempt $i/$retries..." + sleep 2 + fi + i=$((i + 1)) + done + return 1 +}