Skip to content

Commit 5baf561

Browse files
authored
Merge pull request qualcomm-linux#99 from smuppand/bluetooth
[BT][Test] Implement BT_SCAN_PAIR test with pairing retries, whitelist, and expect automation
2 parents 6d511eb + 28f20dc commit 5baf561

File tree

6 files changed

+802
-17
lines changed

6 files changed

+802
-17
lines changed

Runner/suites/Connectivity/Bluetooth/run.sh renamed to Runner/suites/Connectivity/Bluetooth/BT_ON_FF/run.sh

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
44
# SPDX-License-Identifier: BSD-3-Clause-Clear
5-
6-
# Source init_env and functestlib.sh
5+
6+
# Robustly find and source init_env
77
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
88
INIT_ENV=""
99
SEARCH="$SCRIPT_DIR"
@@ -14,33 +14,36 @@ while [ "$SEARCH" != "/" ]; do
1414
fi
1515
SEARCH=$(dirname "$SEARCH")
1616
done
17-
17+
1818
if [ -z "$INIT_ENV" ]; then
1919
echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2
2020
exit 1
2121
fi
22-
23-
# shellcheck disable=SC1090
24-
. "$INIT_ENV"
25-
22+
23+
if [ -z "$__INIT_ENV_LOADED" ]; then
24+
# shellcheck disable=SC1090
25+
. "$INIT_ENV"
26+
fi
2627
# shellcheck disable=SC1090,SC1091
2728
. "$TOOLS/functestlib.sh"
28-
29-
TESTNAME="Bluetooth"
29+
30+
TESTNAME="BT_ON_FF"
3031
test_path=$(find_test_case_by_name "$TESTNAME") || {
3132
log_fail "$TESTNAME : Test directory not found."
3233
echo "$TESTNAME FAIL" > "./$TESTNAME.res"
3334
exit 1
3435
}
35-
36+
3637
cd "$test_path" || exit 1
3738
res_file="./$TESTNAME.res"
3839
rm -f "$res_file"
39-
40-
log_info "-----------------------------------------------------------------------------------------"
41-
log_info "-------------------Starting $TESTNAME Testcase----------------------------"
40+
41+
log_info "------------------------------------------------------------"
42+
log_info "Starting $TESTNAME Testcase"
4243
log_info "Checking dependency: bluetoothctl"
43-
check_dependencies bluetoothctl
44+
45+
# verify that all necessary dependencies
46+
check_dependencies bluetoothctl pgrep
4447

4548
log_info "Checking if bluetoothd is running..."
4649
MAX_RETRIES=3
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
2+
# Bluetooth BT_SCAN_PAIR Test
3+
4+
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.
5+
6+
## Features
7+
8+
- Scans for Bluetooth devices
9+
- Optionally pairs with a device by name or MAC address
10+
- Retries pairing on failure, including handling for busy/temporarily unavailable devices
11+
- Cleans up previous pairings for repeatable CI runs
12+
- Accepts device name/MAC as argument, environment variable, or in `bt_device_list.txt`
13+
- Generates summary and detailed logs (`scan.log`, `pair.log`, `found_devices.log`)
14+
15+
## Usage
16+
17+
```sh
18+
./run.sh [DEVICE_NAME_OR_MAC] [WHITELIST]
19+
```
20+
- `DEVICE_NAME_OR_MAC` – (optional) Device name or MAC address to pair.
21+
- Can also be set as `BT_NAME_ENV` or in `bt_device_list.txt`
22+
- `WHITELIST` – (optional) Comma-separated MACs/names allowed for pairing.
23+
- Can also be set as `BT_WHITELIST_ENV`
24+
25+
If no device name is given, only scanning is performed and the test passes if devices are found.
26+
27+
## Examples
28+
29+
```sh
30+
./run.sh [BT_NAME] [WHITELIST]
31+
```
32+
33+
- `BT_NAME` - Optional. Bluetooth name or MAC to search for.
34+
- `WHITELIST` - Optional. Comma-separated names/MACs allowed for pairing.
35+
36+
- Scan for any device (no pairing):
37+
38+
```
39+
./run.sh
40+
```
41+
42+
- Scan and pair with a device named "MySpeaker":
43+
44+
```
45+
./run.sh MySpeaker
46+
```
47+
48+
- Scan and pair only if device MAC is in whitelist:
49+
50+
```
51+
./run.sh MySpeaker 00:11:22:33:44:55,AnotherSpeaker
52+
```
53+
54+
- Use environment variables:
55+
56+
```
57+
export BT_NAME_ENV="MySpeaker"
58+
export BT_WHITELIST_ENV="00:11:22:33:44:55"
59+
./run.sh
60+
```
61+
62+
- Device list file (first line is used):
63+
64+
```
65+
echo "MySpeaker" > bt_device_list.txt
66+
./run.sh
67+
```
68+
69+
## Whitelist Usage
70+
71+
To ensure only known devices are considered during scan:
72+
73+
```sh
74+
./run.sh JBL_Speaker "JBL_Speaker,12:34:56:78:9A:BC"
75+
```
76+
77+
## Arguments & Variables
78+
79+
- Argument 1: Device name or MAC address (takes precedence)
80+
- `BT_NAME_ENV`: Device name or MAC from the environment
81+
- `bt_device_list.txt`: Fallback if argument or env is not set
82+
83+
## Example Summary Output
84+
85+
```
86+
[INFO] 2025-06-23 10:00:00 - Starting BT_SCAN_PAIR Testcase
87+
[INFO] 2025-06-23 10:00:02 - Unblocking and powering on Bluetooth
88+
[INFO] 2025-06-23 10:00:05 - Devices found during scan:
89+
Device 12:34:56:78:9A:BC SomeBTHeadset
90+
[INFO] 2025-06-23 10:00:06 - Expected device 'SomeBTHeadset' found in scan
91+
[PASS] 2025-06-23 10:00:08 - Pairing successful with 12:34:56:78:9A:BC
92+
```
93+
94+
## Result
95+
96+
- PASS: Device paired (or scan-only with no target)
97+
- FAIL: Device not found, not in whitelist, or pairing failed
98+
- All paired devices are removed after the test
99+
100+
## Files Generated
101+
102+
- `BT_SCAN_PAIR.res`: Test PASS/FAIL/SKIP result
103+
- `scan.log`: Output of Bluetooth device scan
104+
- `found_devices.log`: List of discovered device names/MACs
105+
- `pair.log`: Detailed pairing output and errors
106+
107+
## Troubleshooting
108+
109+
- Ensure `bluetoothctl`, `rfkill`, `expect`, and `hciconfig` are available.
110+
- For headless automation, the remote device must be in pairing/discoverable mode.
111+
- The script retries pairing if "busy" or "temporarily unavailable" errors are seen.
112+
- Check `pair.log` and `scan.log` for detailed debug info if a failure occurs.
113+
114+
## Helper Functions (in functestlib.sh)
115+
116+
- `bt_scan_devices` – Scans and logs found BT devices
117+
- `bt_pair_with_mac` – Attempts pairing via expect with retries
118+
- `bt_in_whitelist` – Checks if MAC/name is in whitelist
119+
- `bt_cleanup_paired_device` – Removes paired device by MAC
120+
121+
## Customization
122+
123+
- **Whitelist**: You can restrict scan to a whitelist of MAC addresses or names using environment variables or script customization.
124+
- **Retries/Timeouts**: Retry and timeout values can be set in the script for more robust pairing.
125+
126+
## Integration with LAVA
127+
128+
In your LAVA job:
129+
130+
```yaml
131+
deploy:
132+
to: tftp
133+
images:
134+
bt_device_list.txt:
135+
image: path/to/bt_device_list.txt
136+
compression: none
137+
```
138+
139+
Injects a per-DUT Bluetooth configuration.
140+
141+
## Dependencies
142+
143+
- `bluetoothctl`, `expect`, `rfkill`, `hciconfig`
144+
- BlueZ stack running on embedded Linux
145+
146+
## License
147+
148+
SPDX-License-Identifier: BSD-3-Clause-Clear
149+
Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.

Runner/suites/Connectivity/Bluetooth/BT_SCAN_PAIR/bt_device_list.txt

Whitespace-only changes.
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
#!/bin/sh
2+
3+
# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
4+
# SPDX-License-Identifier: BSD-3-Clause-Clear
5+
6+
# Robustly find and source init_env
7+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
8+
INIT_ENV=""
9+
SEARCH="$SCRIPT_DIR"
10+
11+
while [ "$SEARCH" != "/" ]; do
12+
if [ -f "$SEARCH/init_env" ]; then
13+
INIT_ENV="$SEARCH/init_env"
14+
break
15+
fi
16+
SEARCH=$(dirname "$SEARCH")
17+
done
18+
19+
if [ -z "$INIT_ENV" ]; then
20+
echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2
21+
exit 1
22+
fi
23+
24+
if [ -z "$__INIT_ENV_LOADED" ]; then
25+
# shellcheck disable=SC1090
26+
. "$INIT_ENV"
27+
fi
28+
29+
# shellcheck disable=SC1090,SC1091
30+
. "$TOOLS/functestlib.sh"
31+
32+
TESTNAME="BT_SCAN_PAIR"
33+
test_path=$(find_test_case_by_name "$TESTNAME") || {
34+
log_fail "$TESTNAME : Test directory not found."
35+
echo "$TESTNAME FAIL" > "./$TESTNAME.res"
36+
exit 1
37+
}
38+
cd "$test_path" || exit 1
39+
RES_FILE="./$TESTNAME.res"
40+
rm -f "$RES_FILE"
41+
42+
log_info "------------------------------------------------------------"
43+
log_info "Starting $TESTNAME Testcase"
44+
45+
BT_NAME=""
46+
BT_MAC=""
47+
WHITELIST=""
48+
PAIR_RETRIES="${PAIR_RETRIES:-3}"
49+
SCAN_ATTEMPTS="${SCAN_ATTEMPTS:-2}"
50+
51+
# Parse arguments
52+
if [ -n "$1" ]; then
53+
if echo "$1" | grep -Eq '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then
54+
BT_MAC="$1"
55+
else
56+
BT_NAME="$1"
57+
fi
58+
fi
59+
60+
if [ -n "$2" ]; then
61+
WHITELIST="$2"
62+
if [ -z "$BT_MAC" ] && echo "$2" | grep -Eq '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$'; then
63+
BT_MAC="$2"
64+
fi
65+
fi
66+
67+
# Fallback to file
68+
if [ -z "$BT_NAME" ] && [ -z "$BT_MAC" ] && [ -f "./bt_device_list.txt" ]; then
69+
BT_NAME=$(awk '!/^#/ && NF {print $2}' ./bt_device_list.txt | head -n1)
70+
BT_MAC=$(awk '!/^#/ && NF {print $1}' ./bt_device_list.txt | head -n1)
71+
fi
72+
73+
check_dependencies bluetoothctl rfkill expect hciconfig || {
74+
echo "$TESTNAME SKIP" > "$RES_FILE"
75+
exit 0
76+
}
77+
78+
cleanup_bt_test() {
79+
[ -n "$BT_MAC" ] && bt_cleanup_paired_device "$BT_MAC"
80+
killall -q bluetoothctl 2>/dev/null
81+
}
82+
trap cleanup_bt_test EXIT
83+
84+
rfkill unblock bluetooth
85+
retry_command_bt "hciconfig hci0 up" "Bring up hci0" || {
86+
log_fail "Failed to bring up hci0"
87+
echo "$TESTNAME FAIL" > "$RES_FILE"
88+
exit 1
89+
}
90+
91+
bt_remove_all_paired_devices
92+
93+
MATCH_FOUND=0
94+
95+
for scan_try in $(seq 1 "$SCAN_ATTEMPTS"); do
96+
log_info "Bluetooth scan attempt $scan_try..."
97+
bt_scan_devices
98+
99+
LATEST_FOUND_LOG=$(find . -maxdepth 1 -name 'found_devices_*.log' -type f -print | sort -r | head -n1)
100+
[ -z "$LATEST_FOUND_LOG" ] && continue
101+
102+
log_info "Devices found during scan:"
103+
cat "$LATEST_FOUND_LOG"
104+
105+
if [ -z "$BT_NAME" ] && [ -z "$BT_MAC" ]; then
106+
log_pass "No device specified. Scan-only mode."
107+
echo "$TESTNAME PASS" > "$RES_FILE"
108+
exit 0
109+
fi
110+
111+
log_info "Matching against: BT_NAME='$BT_NAME', BT_MAC='$BT_MAC', WHITELIST='$WHITELIST'"
112+
bt_remove_all_paired_devices
113+
bluetoothctl --timeout 3 devices | awk '{print $2}' | while read -r addr; do
114+
log_info "Forcing device removal: $addr"
115+
bluetoothctl remove "$addr" >/dev/null 2>&1
116+
done
117+
118+
# Clean and prepare whitelist
119+
WHITELIST_CLEAN=$(echo "$WHITELIST" | tr -d '\r' | tr ',\n' ' ' | xargs)
120+
MATCH_FOUND=0
121+
122+
while IFS= read -r line; do
123+
mac=$(echo "$line" | awk '{print $1}')
124+
name=$(echo "$line" | cut -d' ' -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
125+
126+
log_info "Parsed: MAC='$mac' NAME='$name'"
127+
log_info "Checking if MAC or NAME is in whitelist: '$WHITELIST_CLEAN'"
128+
129+
if [ -n "$BT_MAC" ] && [ "$mac" = "$BT_MAC" ]; then
130+
log_info "MAC matched: $mac"
131+
if [ -z "$WHITELIST_CLEAN" ] || echo "$WHITELIST_CLEAN" | grep -wq "$mac" || echo "$WHITELIST_CLEAN" | grep -wq "$name"; then
132+
log_info "MAC allowed by whitelist: $mac ($name)"
133+
MATCH_FOUND=1
134+
break
135+
else
136+
log_info "MAC matched but not in whitelist: $name"
137+
fi
138+
elif [ -n "$BT_NAME" ] && [ "$name" = "$BT_NAME" ]; then
139+
log_info "Name matched: $name"
140+
if [ -z "$WHITELIST_CLEAN" ] || echo "$WHITELIST_CLEAN" | grep -wq "$mac" || echo "$WHITELIST_CLEAN" | grep -wq "$name"; then
141+
log_info "Name allowed by whitelist: $name ($mac)"
142+
BT_MAC="$mac"
143+
MATCH_FOUND=1
144+
break
145+
else
146+
log_info "Name matched but not in whitelist: $name"
147+
fi
148+
fi
149+
done < "$LATEST_FOUND_LOG"
150+
151+
[ "$MATCH_FOUND" -eq 1 ] && break
152+
sleep 2
153+
done
154+
155+
if [ "$MATCH_FOUND" -ne 1 ]; then
156+
log_fail "Expected device not found or not in whitelist"
157+
echo "$TESTNAME FAIL" > "$RES_FILE"
158+
exit 1
159+
fi
160+
161+
log_info "Attempting to pair with $BT_NAME ($BT_MAC)"
162+
if bt_pair_with_mac "$BT_MAC" "$PAIR_RETRIES"; then
163+
log_info "Pairing successful. Attempting post-pair connection..."
164+
if bt_post_pair_connect "$BT_MAC"; then
165+
log_info "Post-pair connection successful, verifying with l2ping..."
166+
if bt_l2ping_check "$BT_MAC" "$RES_FILE"; then
167+
log_pass "Post-pair connection and l2ping verified"
168+
echo "$TESTNAME PASS" > "$RES_FILE"
169+
exit 0
170+
else
171+
log_warn "Post-pair successful but l2ping failed"
172+
echo "$TESTNAME FAIL" > "$RES_FILE"
173+
exit 1
174+
fi
175+
fi
176+
else
177+
log_fail "Pairing failed after $PAIR_RETRIES retries"
178+
echo "$TESTNAME FAIL" > "$RES_FILE"
179+
exit 1
180+
fi

0 commit comments

Comments
 (0)