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 deleted file mode 100644 index a5dd66ac..00000000 --- a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAID_README.md +++ /dev/null @@ -1,149 +0,0 @@ - -# 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_SCAN_PAIR_README.md b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAIR_README.md new file mode 100644 index 00000000..b9cbffa5 --- /dev/null +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/BT_SCAN_PAIR_README.md @@ -0,0 +1,125 @@ +# Bluetooth BT_SCAN_PAIR Test + +This test automates Bluetooth scanning, pairing, and post-pair verification (via l2ping) on 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 (up to `$SCAN_ATTEMPTS` retries, default 2) +- Pairs with a device by MAC or name (up to `$PAIR_RETRIES` attempts, default 3) +- Verifies connectivity via `bt_post_pair_connect` and mandatory `l2ping` link check +- Cleans up prior pairings for repeatable CI runs +- Accepts input via: + - Command-line argument 1: device MAC or name + - Command-line argument 2: whitelist filter (MACs/names) + - Environment variables: + - `BT_MAC_ENV` or `BT_NAME_ENV` + - `BT_WHITELIST_ENV` + - Fallback: `bt_device_list.txt` +- Generates summary and detailed logs (`scan.log`, `found_devices.log`, `pair.log`) + +## Configuration & Environment Variables + +| Variable | Default | Description | +|---------------------|---------|---------------------------------------------------------------| +| `PAIR_RETRIES` | `3` | Max pairing attempts per device | +| `SCAN_ATTEMPTS` | `2` | Max scan retries per device before pairing | +| `BT_MAC_ENV` | ― | Overrides direct MAC (same format as CLI argument) | +| `BT_NAME_ENV` | ― | Overrides device name | +| `BT_WHITELIST_ENV` | ― | Comma-separated MACs or names allowed for pairing | + +## Usage + +```sh +# Override defaults (optional): +export PAIR_RETRIES=5 +export SCAN_ATTEMPTS=3 +export BT_NAME_ENV="MySpeaker" +export BT_WHITELIST_ENV="00:11:22:33:44:55,Speaker2" + +# Run test (direct pairing by MAC or name): +./run.sh [DEVICE_NAME_OR_MAC] [WHITELIST] +``` + +### Examples + +```sh +# 1. Scan-only (no target): +./run.sh + +# 2. Pair by name: +./run.sh MySpeaker + +# 3. Pair by MAC with whitelist: +./run.sh 00:11:22:33:44:55 12:34:56:78:9A:BC,OtherName + +# 4. Use command-line and environment variables: +export BT_NAME_ENV="MySpeaker" +export BT_WHITELIST_ENV="00:11:22:33:44:55,Speaker2" +./run.sh + +# 5. Override MAC via environment only: +export BT_MAC_ENV="00:11:22:33:44:55" +./run.sh +``` + +## Whitelist Behavior + +When a whitelist is specified (CLI or `BT_WHITELIST_ENV`), only devices whose MAC or name matches entries in the comma-separated list will be paired. + +```sh +# Only devices matching 'JBL_Speaker' or '12:34:56:78:9A:BC' +./run.sh "" "JBL_Speaker,12:34:56:78:9A:BC" +``` + +## Script Flow + +1. **Initialization**: locate and source `init_env` and `functestlib.sh`. +2. **Setup**: + - Unblock and power on `hci0`. + - Remove all existing pairings. +3. **Candidates**: build a list of one or more `(MAC, NAME)` from: + - CLI arg + - Env vars + - `bt_device_list.txt` +4. **Per-Device Loop**: for each candidate: + a. Apply whitelist filter (if any). + b. Up to `$SCAN_ATTEMPTS` scans to detect device. + c. Up to `$PAIR_RETRIES` pairing attempts via `expect`. + d. If paired, call `bt_post_pair_connect` then `bt_l2ping_check`. + e. On success: log PASS, write `BT_SCAN_PAIR PASS`, exit. + f. On failure: cleanup pairing, move to next candidate. +5. **Result**: if all candidates fail, log FAIL and write `BT_SCAN_PAIR FAIL`. + +## Generated Files + +- `BT_SCAN_PAIR.res`: PASS / FAIL / SKIP result code +- `scan.log`: raw scan output +- `found_devices.log`: parsed found devices +- `pair.log`: detailed pairing output and errors + +## Troubleshooting + +- Ensure `bluetoothctl`, `rfkill`, `expect`, and `hciconfig` are installed and in PATH. +- Confirm the DUT’s Bluetooth adapter is present and powered on. +- For headless devices, ensure target is in discoverable/pairing mode. +- Inspect `scan.log` and `pair.log` for detailed errors. +- Increase `PAIR_RETRIES` or `SCAN_ATTEMPTS` for flaky environments. + +## Helper Functions (in `functestlib.sh`) + +- `bt_scan_devices` – performs scan and writes `found_devices_.log` +- `bt_in_whitelist MAC NAME` – returns 0 if `MAC` or `NAME` matches whitelist +- `bt_pair_with_mac MAC` – interactive pairing via `expect` with retries +- `bt_post_pair_connect MAC` – attempts `bluetoothctl connect` with retries +- `bt_l2ping_check MAC RES_FILE` – verifies link via `l2ping` +- `bt_cleanup_paired_device MAC` – removes existing pairing + +## Integration & CI + +- Place `bt_device_list.txt` alongside `run.sh` to drive test data per DUT. +- Use LAVA YAML to deploy `bt_device_list.txt` and invoke `run.sh`. + +## License + +SPDX-License-Identifier: BSD-3-Clause-Clear +© Qualcomm Technologies, Inc. and/or its subsidiaries. diff --git a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh index 51fe787f..d4a585ed 100755 --- a/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh +++ b/Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/run.sh @@ -42,13 +42,15 @@ rm -f "$RES_FILE" log_info "------------------------------------------------------------" log_info "Starting $TESTNAME Testcase" +# Defaults +PAIR_RETRIES="${PAIR_RETRIES:-3}" +SCAN_ATTEMPTS="${SCAN_ATTEMPTS:-2}" + BT_NAME="" BT_MAC="" WHITELIST="" -PAIR_RETRIES="${PAIR_RETRIES:-3}" -SCAN_ATTEMPTS="${SCAN_ATTEMPTS:-2}" -# Parse arguments +# Parse CLI args if [ -n "$1" ]; then if echo "$1" | grep -Eq '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then BT_MAC="$1" @@ -56,7 +58,6 @@ if [ -n "$1" ]; then BT_NAME="$1" fi fi - if [ -n "$2" ]; then WHITELIST="$2" if [ -z "$BT_MAC" ] && echo "$2" | grep -Eq '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then @@ -64,10 +65,11 @@ if [ -n "$2" ]; then fi fi -# Fallback to file -if [ -z "$BT_NAME" ] && [ -z "$BT_MAC" ] && [ -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) +# Skip if no CLI input and no list file +if [ -z "$BT_MAC" ] && [ -z "$BT_NAME" ] && [ ! -f "./bt_device_list.txt" ]; then + log_warn "No MAC/name or bt_device_list.txt found. Skipping test." + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 fi check_dependencies bluetoothctl rfkill expect hciconfig || { @@ -90,91 +92,104 @@ retry_command_bt "hciconfig hci0 up" "Bring up hci0" || { bt_remove_all_paired_devices -MATCH_FOUND=0 - -for scan_try in $(seq 1 "$SCAN_ATTEMPTS"); do - log_info "Bluetooth scan attempt $scan_try..." - bt_scan_devices - - LATEST_FOUND_LOG=$(find . -maxdepth 1 -name 'found_devices_*.log' -type f -print | sort -r | head -n1) - [ -z "$LATEST_FOUND_LOG" ] && continue - - log_info "Devices found during scan:" - cat "$LATEST_FOUND_LOG" - - if [ -z "$BT_NAME" ] && [ -z "$BT_MAC" ]; then - log_pass "No device specified. Scan-only mode." +# 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 + else + log_warn "l2ping link check failed for $mac" fi +} - log_info "Matching against: BT_NAME='$BT_NAME', BT_MAC='$BT_MAC', WHITELIST='$WHITELIST'" - bt_remove_all_paired_devices - bluetoothctl --timeout 3 devices | awk '{print $2}' | while read -r addr; do - log_info "Forcing device removal: $addr" - bluetoothctl remove "$addr" >/dev/null 2>&1 - done - - # Clean and prepare whitelist -WHITELIST_CLEAN=$(echo "$WHITELIST" | tr -d '\r' | tr ',\n' ' ' | xargs) -MATCH_FOUND=0 - -while IFS= read -r line; do - mac=$(echo "$line" | awk '{print $1}') - name=$(echo "$line" | cut -d' ' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') - - log_info "Parsed: MAC='$mac' NAME='$name'" - log_info "Checking if MAC or NAME is in whitelist: '$WHITELIST_CLEAN'" - - if [ -n "$BT_MAC" ] && [ "$mac" = "$BT_MAC" ]; then - log_info "MAC matched: $mac" - if [ -z "$WHITELIST_CLEAN" ] || echo "$WHITELIST_CLEAN" | grep -wq "$mac" || echo "$WHITELIST_CLEAN" | grep -wq "$name"; then - log_info "MAC allowed by whitelist: $mac ($name)" - MATCH_FOUND=1 - break - else - log_info "MAC matched but not in whitelist: $name" - fi - elif [ -n "$BT_NAME" ] && [ "$name" = "$BT_NAME" ]; then - log_info "Name matched: $name" - if [ -z "$WHITELIST_CLEAN" ] || echo "$WHITELIST_CLEAN" | grep -wq "$mac" || echo "$WHITELIST_CLEAN" | grep -wq "$name"; then - log_info "Name allowed by whitelist: $name ($mac)" - BT_MAC="$mac" - MATCH_FOUND=1 - break +# Direct pairing if CLI MAC provided +if [ -n "$BT_MAC" ]; then + log_info "Direct pairing requested for $BT_MAC" + sleep 2 + for attempt in $(seq 1 "$PAIR_RETRIES"); do + log_info "Pair attempt $attempt/$PAIR_RETRIES for $BT_MAC" + if bt_pair_with_mac "$BT_MAC"; then + log_info "Pair succeeded; connecting 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 "Connect failed; trying l2ping fallback for $BT_MAC" + verify_link "$BT_MAC" + bt_cleanup_paired_device "$BT_MAC" + fi else - log_info "Name matched but not in whitelist: $name" + log_warn "Pair failed for $BT_MAC (attempt $attempt)" + bt_cleanup_paired_device "$BT_MAC" fi + done + log_warn "Exhausted direct pairing attempts for $BT_MAC" + # If CLI arg was provided, do not fallback on empty .txt + if [ -n "$BT_NAME" ] || [ -n "$BT_MAC" ]; then + log_fail "Direct pairing failed for ${BT_MAC:-$BT_NAME}" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 fi -done < "$LATEST_FOUND_LOG" - - [ "$MATCH_FOUND" -eq 1 ] && break - sleep 2 -done - -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_info "Pairing successful. Attempting post-pair connection..." - if bt_post_pair_connect "$BT_MAC"; then - log_info "Post-pair connection successful, verifying with l2ping..." - if bt_l2ping_check "$BT_MAC" "$RES_FILE"; then - log_pass "Post-pair connection and l2ping verified" - echo "$TESTNAME PASS" > "$RES_FILE" - exit 0 - else - log_warn "Post-pair successful but l2ping failed" - echo "$TESTNAME FAIL" > "$RES_FILE" - exit 1 - fi +# Fallback: only if no CLI input and list exists +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 fi -else - log_fail "Pairing failed after $PAIR_RETRIES retries" + + log_info "Using fallback list in bt_device_list.txt" + while IFS= read -r line || [ -n "$line" ]; do + case "$line" in ''|\#*) continue ;; esac + # split into MAC and NAME + IFS=' ' read -r MAC NAME < "$RES_FILE" exit 1 fi + +# Should never reach here +log_fail "No execution path matched; exiting" +echo "$TESTNAME FAIL" > "$RES_FILE" +exit 1 diff --git a/Runner/utils/functestlib.sh b/Runner/utils/functestlib.sh index 79a52983..5f185ab3 100755 --- a/Runner/utils/functestlib.sh +++ b/Runner/utils/functestlib.sh @@ -560,12 +560,35 @@ EOF # Remove paired BT device by MAC bt_cleanup_paired_device() { mac="$1" - expect < /dev/null 2>&1 -log_user 0 + log_info "Removing paired device: $mac" + + # Non-interactive remove to avoid “AlreadyExists” + bluetoothctl remove "$mac" >/dev/null 2>&1 || true + + # Full Expect cleanup (captures transcript in a logfile) + cleanup_log="bt_cleanup_${mac}_$(date +%Y%m%d_%H%M%S).log" + if expect <"$cleanup_log" 2>&1 +log_user 1 spawn bluetoothctl -expect "#" { send "remove $mac\r" } -expect "#" { send "quit\r" } +set timeout 10 + +# Match the prompt once, then send all commands in sequence +expect -re "#|\\[.*\\]#" { + send "power on\r" + send "agent off\r" + send "agent NoInputNoOutput\r" + send "default-agent\r" + send "remove $mac\r" + send "quit\r" +} + +expect eof EOF + then + log_info "Device $mac removed successfully (see $cleanup_log)" + else + log_warn "Failed to remove device $mac (see $cleanup_log)" + fi } # Retry a shell command N times with sleep @@ -649,142 +672,167 @@ bt_scan_devices() { timestamp=$(date '+%Y%m%d_%H%M%S') scan_log="scan_${timestamp}.log" found_log="found_devices_${timestamp}.log" + : > "$scan_log" : > "$found_log" - + log_info "Detecting Bluetooth adapter..." hcidev=$(hciconfig | awk '/^hci/ { print $1 }' | head -n1) if [ -z "$hcidev" ]; then log_error "No Bluetooth adapter found" return 1 fi - + log_info "Using Bluetooth adapter: $hcidev" hciconfig "$hcidev" up - - log_info "Running Bluetooth scan via hcitool..." - if hcitool -i "$hcidev" scan > hcitool_scan.tmp 2>/dev/null; then - tail -n +2 hcitool_scan.tmp | awk '{ printf "%s %s\n", $1, $2}' > "$found_log" - rm -f hcitool_scan.tmp - [ -s "$found_log" ] && return 0 + sleep 1 + + log_info "Running Bluetooth scan via hcitool (synchronous)..." + script -q -c "hcitool -i $hcidev scan" "$scan_log" + grep -E '^(\s)*([0-9A-F]{2}:){5}[0-9A-F]{2}' "$scan_log" | awk '{print $1, $2}' > "$found_log" + + if [ -s "$found_log" ]; then + log_info "hcitool scan found devices, skipping bluetoothctl fallback." + return 0 fi - + log_warn "hcitool scan returned nothing. Falling back to bluetoothctl scan..." - expect < "$scan_log" + + expect <> "$scan_log" log_user 0 spawn bluetoothctl -expect "#" { send "power on\r" } -expect "#" { send "agent NoInputNoOutput\r" } -expect "#" { send "default-agent\r" } -expect "#" { send "scan on\r" } +expect -re "#|\\\[.*\\\]#" { send "power on\r" } +expect -re "#|\\\[.*\\\]#" { send "agent NoInputNoOutput\r" } +expect -re "#|\\\[.*\\\]#" { send "default-agent\r" } +expect -re "#|\\\[.*\\\]#" { send "scan on\r" } sleep 10 send "scan off\r" -expect "#" { send "quit\r" } +expect -re "#|\\\[.*\\\]#" { send "quit\r" } EOF - - grep -E "^\s*\[NEW\] Device" "$scan_log" | awk '{ print $4, substr($0, index($0, $5)) }' > "$found_log" + + grep -E "^\s*\[NEW\] Device" "$scan_log" | awk '{print $4, substr($0, index($0, $5))}' > "$found_log" + if [ ! -s "$found_log" ]; then log_warn "Scan log is empty. Possible issue with bluetoothctl or adapter." return 1 fi - + return 0 } # Pair with Bluetooth device using MAC (with retries and timestamped logs) bt_pair_with_mac() { bt_mac="$1" + # Replace colons, strip any whitespace so no trailing spaces in filenames + safe_mac=$(echo "$bt_mac" | tr ':' '_' | tr -d '[:space:]') max_retries=3 retry=1 - + while [ "$retry" -le "$max_retries" ]; do - log_info "Attempt $retry: Pairing with $bt_mac using bluetoothctl" - - timestamp=$(date '+%Y%m%d_%H%M%S') - sanitized_mac=$(echo "$bt_mac" | sed 's/:/_/g') - logfile="pairing_attempt_${sanitized_mac}_${timestamp}.log" - - expect <]} { send "power on\r" } -} -expect { - -re {.*[#>]} { send "remove $bt_mac\r" } -} -expect { - -re {.*[#>]} { send "scan on\r" } -} -sleep 5 -expect { - -re {.*[#>]} { send "scan off\r" } -} -expect { - -re {.*[#>]} { send "agent KeyboardOnly\r" } -} -expect { - -re {.*[#>]} { send "default-agent\r" } -} -expect { - -re {.*[#>]} { send "pair $bt_mac\r" } -} -expect { - -re {.*Confirm passkey.*yes/no.*} { - send "yes\r" + -re {Confirm passkey.*yes/no} { + send \"yes\r\" exp_continue } - -re {.*Paired: yes.*} { - send "quit\r" - exit 0 + -re {Authorize service.*yes/no} { + send \"yes\r\" + exp_continue } - -re {.*Pairing successful.*} { - send "quit\r" + timeout { + send \"quit\r\" exit 0 } - -re {.*Failed to pair.*} { - send "quit\r" - exit 1 - } - -re {.*Device $bt_mac not available.*} { - send "quit\r" - exit 2 - } - timeout { - send "quit\r" - exit 3 + eof { + exit 0 } } -EOF - - result=$? - case "$result" in - 0) - log_pass "Pairing successful with $bt_mac" - return 0 - ;; - 1|3) - log_warn "Pairing attempt $retry failed for $bt_mac. Retrying after unpairing again..." - bt_cleanup_paired_device "$bt_mac" - ;; - 2) - log_warn "Device $bt_mac not available. Check proximity and power." - ;; - *) - log_warn "Unexpected pairing error code: $result" - ;; - esac - +" > "$log_file" 2>&1 + + # Now analyze the log + if grep -q "Pairing successful" "$log_file"; then + log_pass "Pairing successful with $bt_mac" + return 0 + elif grep -q "Failed to pair: org.bluez.Error" "$log_file"; then + log_warn "Pairing failed with $bt_mac (BlueZ error)" + elif grep -q "AuthenticationCanceled" "$log_file"; then + log_warn "Pairing canceled with $bt_mac" + else + log_warn "Pairing failed with unknown reason (check $log_file)" + fi + + bt_cleanup_paired_device "$bt_mac" retry=$((retry + 1)) sleep 2 done - + log_fail "Pairing failed after $max_retries attempts for $bt_mac" return 1 } +# Utility to reliably scan and pair Bluetooth devices through a unified workflow of repeated attempts. +retry_scan_and_pair() { + retry=1 + max_retries=2 + + while [ "$retry" -le "$max_retries" ]; do + log_info "Bluetooth scan attempt $retry..." + bt_scan_devices + + if [ -n "$BT_MAC" ]; then + log_info "Matching against: BT_NAME='$BT_NAME', BT_MAC='$BT_MAC', WHITELIST='$WHITELIST'" + if ! bt_in_whitelist "$BT_MAC" "$BT_NAME"; then + log_warn "Expected device not found or not in whitelist" + retry=$((retry + 1)) + continue + fi + bt_cleanup_paired_device "$BT_MAC" + if bt_pair_with_mac "$BT_MAC"; then + return 0 + fi + + elif [ -n "$BT_NAME" ]; then + matched_mac=$(awk -v name="$BT_NAME" 'tolower($0) ~ tolower(name) { print $1; exit }' "$SCAN_RESULT") + if [ -n "$matched_mac" ]; then + log_info "Found matching device by name ($BT_NAME): $matched_mac" + bt_cleanup_paired_device "$matched_mac" + if bt_pair_with_mac "$matched_mac"; then + BT_MAC="$matched_mac" + return 0 + fi + else + log_warn "Device with name $BT_NAME not found in scan results" + fi + + else + log_warn "No MAC or device name provided, and whitelist is empty" + fi + + retry=$((retry + 1)) + done + + log_fail "Retry scan and pair failed after $max_retries attempts" + return 1 +} + # Post-pairing connection test with bluetoothctl and l2ping fallback bt_post_pair_connect() { target_mac="$1" @@ -793,25 +841,24 @@ bt_post_pair_connect() { base_logfile="bt_connect_${sanitized_mac}_${timestamp}" max_attempts=3 attempt=1 - + if bluetoothctl info "$target_mac" | grep -q "Connected: yes"; then log_info "Device $target_mac is already connected, skipping explicit connect" log_pass "Post-pair connection successful" return 0 fi - + while [ "$attempt" -le "$max_attempts" ]; do log_info "Attempting to connect post-pair (try $attempt): $target_mac" logfile="${base_logfile}_attempt${attempt}.log" - + expect <"$logfile" 2>&1 -log_user 0 +log_user 1 set timeout 10 spawn bluetoothctl -expect { - -re ".*#.*" {} -} -send "connect $target_mac\r" +expect -re "#|\\[.*\\]#" { send "trust $target_mac\r" } +expect -re "#|\\[.*\\]#" { send "connect $target_mac\r" } + expect { -re "Connection successful" { exit 0 } -re "Failed to connect|Device not available" { exit 1 } @@ -827,24 +874,25 @@ EOF attempt=$((attempt + 1)) sleep 2 done - - log_info "Falling back to l2ping to trigger Bluetooth link with $target_mac" + + # Fallback to l2ping + log_info "Falling back to l2ping for $target_mac" + l2ping_log="${base_logfile}_l2ping_${timestamp}.log" if command -v l2ping >/dev/null 2>&1; then - if l2ping -c 3 -t 5 "$target_mac" >>"${base_logfile}_l2ping.log" 2>&1; then - if bluetoothctl info "$target_mac" | grep -q "Connected: yes"; then - log_pass "Fallback l2ping worked - device is now connected" - return 0 - fi + # Capture all output—even if ping succeeds, we log it + if l2ping -c 3 -t 5 "$target_mac" 2>&1 | tee "$l2ping_log" | grep -q "bytes from"; then + log_pass "Fallback l2ping succeeded for $target_mac (see $l2ping_log)" + return 0 else - log_warn "l2ping failed or no response" + log_warn "l2ping failed or no response for $target_mac (see $l2ping_log)" fi else - log_warn "l2ping not available - skipping fallback" - fi - + log_warn "l2ping not available, skipping fallback" + fi log_fail "Post-pair connection failed for $target_mac" return 1 } + # Find MAC address from device name in scan log bt_find_mac_by_name() { target="$1" @@ -852,138 +900,6 @@ bt_find_mac_by_name() { grep -i "$target" "$log" | awk '{print $3}' | head -n1 } -# Scan and pair (dynamic adapter, timestamped logs) -bt_scan_and_pair() { - target_name="$1" - target_mac="$2" - scan_attempts=3 - found=0 - timestamp=$(date '+%Y%m%d_%H%M%S') - scan_log="scan_${timestamp}.log" - found_log="found_devices_${timestamp}.log" - : > "$scan_log" - : > "$found_log" - - log_info "Detecting Bluetooth adapter..." - hcidev=$(hciconfig | awk '/^hci/ { print $1 }' | head -n1) - if [ -z "$hcidev" ]; then - log_error "No Bluetooth adapter found" - return 1 - fi - log_info "Using Bluetooth adapter: $hcidev" - hciconfig "$hcidev" up - - log_info "Checking for connected devices..." - for mac in $(bluetoothctl paired-devices | awk '{print $2}'); do - if bluetoothctl info "$mac" | grep -q "Connected: yes"; then - log_info "Disconnecting $mac..." - bluetoothctl disconnect "$mac" >/dev/null 2>&1 - fi - done - - i=1 - while [ "$i" -le "$scan_attempts" ]; do - log_info "Scan attempt $i of $scan_attempts..." - expect < "$scan_log" -log_user 0 -spawn bluetoothctl -expect "#" { send "power on\r" } -expect "#" { send "agent NoInputNoOutput\r" } -expect "#" { send "default-agent\r" } -expect "#" { send "scan on\r" } -sleep 20 -send "scan off\r" -expect "#" { send "quit\r" } -EOF - - grep -iE 'Device ([0-9A-F]{2}:){5}[0-9A-F]{2}' "$scan_log" | awk '{print $2, $3}' > "$found_log" - - if grep -q "$target_mac" "$found_log"; then - found=1 - break - fi - i=$((i + 1)) - done - - if [ "$found" -ne 1 ]; then - log_warn "Device $target_mac ($target_name) not found after $scan_attempts attempts" - return 1 - fi - - log_info "Device $target_mac found. Proceeding to pair..." - - if bluetoothctl paired-devices | grep -q "$target_mac"; then - log_info "Device $target_mac is already paired. Unpairing..." - bluetoothctl remove "$target_mac" >/dev/null 2>&1 - sleep 1 - fi - - i=1 - while [ "$i" -le 3 ]; do - log_info "Pairing attempt $i..." - if expect <