Skip to content

Commit 5a32d63

Browse files
committed
Add test script to validate USB UAC
The shell script verifies the enumeration of USB Audio Class devices. Signed-off-by: Aanchal Chaurasia <achauras@qti.qualcomm.com>
1 parent d76515d commit 5a32d63

3 files changed

Lines changed: 342 additions & 0 deletions

File tree

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
```
2+
Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
3+
SPDX-License-Identifier: BSD-3-Clause
4+
```
5+
6+
# USB Audio Class Validation
7+
8+
## Overview
9+
10+
This shell script executes on the DUT (Device-Under-Test) and validates USB Audio Class (UAC) devices.
11+
The test validation scope includes:
12+
- Successful enumeration of UAC devices and display following details for each device:
13+
- DEVICE (USB device address), VID:PID, and PRODUCT string.
14+
- Validation of ALSA integration:
15+
- Confirm /proc/asound/cards exists.
16+
- Identify ALSA cards corresponding to the UAC device.
17+
- At least one PCM playback or capture node exists for each such card.
18+
- Print a table of enumerated devices:
19+
20+
```
21+
DEVICE VID:PID DRIVER PRODUCT
22+
-------------------------------------------------------------------------------
23+
<dev> <vid:pid> <snd-usb-audio> <product>
24+
```
25+
The test PASS requires all detected UAC devices to have associated ALSA nodes.
26+
Running this test on a DUT without a connected USB Audio peripheral is expected to FAIL with the message: No 'USB Audio Device' found.
27+
28+
---
29+
30+
## Setup
31+
32+
- Connect USB Audio peripheral(s) to USB port(s) on DUT.
33+
- Only applicable for USB ports that support Host Mode functionality.
34+
- USB Audio peripherals examples: USB headset, microphone, sound card, etc.
35+
36+
---
37+
38+
## Usage
39+
### Instructions:
40+
1. **Copy the test suite to the target device** using `scp` or any preferred method.
41+
2. **Navigate to the test directory** on the target device.
42+
3. **Run the test script** using the test runner or directly.
43+
44+
---
45+
46+
### Quick Example
47+
```
48+
cd Runner
49+
./run-test.sh usb_uac
50+
```
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
#!/bin/sh
2+
3+
# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
4+
# SPDX-License-Identifier: BSD-3-Clause
5+
# Validate USB Audio Class (UAC) device detection
6+
# Requires at least one USB Audio peripheral (e.g., USB headset, microphone, sound card) connected to a USB Host port.
7+
8+
TESTNAME="usb_uac"
9+
10+
# Robustly find and source init_env
11+
SCRIPT_DIR="$(
12+
cd "$(dirname "$0")" || exit 1
13+
pwd
14+
)"
15+
16+
# Default result file (works even before functestlib is available)
17+
# shellcheck disable=SC2034
18+
RES_FILE="$SCRIPT_DIR/${TESTNAME}.res"
19+
20+
INIT_ENV=""
21+
SEARCH="$SCRIPT_DIR"
22+
while [ "$SEARCH" != "/" ]; do
23+
if [ -f "$SEARCH/init_env" ]; then
24+
INIT_ENV="$SEARCH/init_env"
25+
break
26+
fi
27+
SEARCH=$(dirname "$SEARCH")
28+
done
29+
30+
if [ -z "$INIT_ENV" ]; then
31+
echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2
32+
echo "$TESTNAME SKIP" >"$RES_FILE" 2>/dev/null || true
33+
exit 0
34+
fi
35+
36+
# Only source if not already loaded (idempotent)
37+
if [ -z "${__INIT_ENV_LOADED:-}" ]; then
38+
# shellcheck disable=SC1090
39+
. "$INIT_ENV"
40+
__INIT_ENV_LOADED=1
41+
fi
42+
# Always source functestlib.sh, using $TOOLS exported by init_env
43+
# shellcheck disable=SC1090,SC1091
44+
. "$TOOLS/functestlib.sh"
45+
46+
# Resolve test path and cd (single SKIP/exit path)
47+
SKIP_REASON=""
48+
test_path=$(find_test_case_by_name "$TESTNAME")
49+
if [ -z "$test_path" ] || [ ! -d "$test_path" ]; then
50+
SKIP_REASON="$TESTNAME SKIP - test path not found"
51+
elif ! cd "$test_path"; then
52+
SKIP_REASON="$TESTNAME SKIP - cannot cd into $test_path"
53+
else
54+
RES_FILE="$test_path/${TESTNAME}.res"
55+
fi
56+
57+
if [ -n "$SKIP_REASON" ]; then
58+
log_skip "$SKIP_REASON"
59+
echo "$TESTNAME SKIP" >"$RES_FILE" 2>/dev/null || true
60+
exit 0
61+
fi
62+
63+
log_info "-----------------------------------------------------------------------------------------"
64+
log_info "-------------------Starting $TESTNAME Testcase----------------------------"
65+
log_info "=== Test Initialization ==="
66+
67+
# Check if dependencies are installed, else skip test
68+
# Include all external utilities used by this script
69+
deps_list="grep sed sort wc tr readlink head awk"
70+
check_dependencies "$deps_list"
71+
72+
# Detect unique devices with bInterfaceClass = 01 (UAC) under /sys/bus/usb/devices
73+
log_info "=== USB Audio device Detection ==="
74+
audio_device_list="$(
75+
for f in /sys/bus/usb/devices/*/bInterfaceClass; do
76+
[ -r "$f" ] || continue
77+
if grep -qx '01' "$f"; then
78+
d=${f%/bInterfaceClass}
79+
d=${d%:*}
80+
printf '%s\n' "${d##*/}"
81+
fi
82+
done 2>/dev/null | sort -u
83+
)"
84+
85+
audio_device_count="$(printf "%s\n" "$audio_device_list" | sed '/^$/d' | wc -l | tr -d '[:space:]')"
86+
log_info "Number of USB audio devices found: $audio_device_count"
87+
88+
if [ "$audio_device_count" -gt 0 ] 2>/dev/null; then
89+
log_info "=== Enumerated USB Audio Devices ==="
90+
printf '\n%-9s %-9s %-18s %-s\n' "DEVICE" "VID:PID" "DRIVER" "PRODUCT"
91+
printf '%s\n' "--------------------------------------------------------"
92+
dev_info_db=""
93+
for dev in $(printf "%s\n" "$audio_device_list" | sed '/^$/d'); do
94+
sys="/sys/bus/usb/devices/$dev"
95+
vid=$([ -r "$sys/idVendor" ] && tr -d '[:space:]' < "$sys/idVendor" || echo -)
96+
pid=$([ -r "$sys/idProduct" ] && tr -d '[:space:]' < "$sys/idProduct" || echo -)
97+
if [ -r "$sys/product" ]; then
98+
product=$(tr -d '\000' < "$sys/product")
99+
else
100+
product="-"
101+
fi
102+
# Determine driver from the UAC interface driver symlink
103+
driver="-"
104+
105+
for intf in "$sys":*; do
106+
# Only consider UAC interfaces (bInterfaceClass == 01)
107+
if [ -r "$intf/bInterfaceClass" ] && grep -qx '01' "$intf/bInterfaceClass"; then
108+
# Resolve driver symlink and extract driver name
109+
if [ -L "$intf/driver" ]; then
110+
link="$(readlink "$intf/driver" 2>/dev/null)"
111+
driver="$(printf "%s\n" "$link" | grep -o 'snd-usb-audio' || echo -)"
112+
fi
113+
break
114+
fi
115+
done
116+
dev_info_db="${dev_info_db}\n${dev}|${vid}:${pid}|${driver}|${product}"
117+
printf '%-9s %-9s %-18s %-s\n' "$dev" "$vid:$pid" "$driver" "$product"
118+
done
119+
printf '\n'
120+
fi
121+
122+
if [ "$audio_device_count" -le 0 ] 2>/dev/null; then
123+
log_fail "$TESTNAME : Test Failed - No 'USB Audio Device' found"
124+
echo "$TESTNAME FAIL" > "$RES_FILE"
125+
exit 0
126+
fi
127+
128+
# Verify ALSA is available
129+
if [ ! -r /proc/asound/cards ]; then
130+
log_fail "$TESTNAME : Test Failed - ALSA not available (/proc/asound/cards missing)"
131+
echo "$TESTNAME FAIL" > "$RES_FILE"
132+
exit 0
133+
fi
134+
135+
if [ -r /proc/asound/cards ]; then
136+
log_info "ALSA cards (/proc/asound/cards):"
137+
while IFS= read -r line; do
138+
log_info " $line"
139+
done < /proc/asound/cards
140+
fi
141+
142+
# Build ALSA card -> USB device map via sysfs (use /proc/asound/cards only for logging)
143+
card_map=""
144+
for card_dir in /sys/class/sound/card*; do
145+
[ -d "$card_dir" ] || continue
146+
c="${card_dir##*/}"
147+
c="${c#card}"
148+
target="$(readlink -f "$card_dir/device" 2>/dev/null || true)"
149+
[ -n "$target" ] || continue
150+
151+
cur="$target"
152+
usb_parent=""
153+
# Walk up until a USB device (with idVendor/idProduct) or an interface dir (X-Y:Z.W) is found
154+
while [ "$cur" != "/" ] && [ -z "$usb_parent" ]; do
155+
if [ -r "$cur/idVendor" ] && [ -r "$cur/idProduct" ]; then
156+
base="${cur##*/}"
157+
usb_parent="$base"
158+
break
159+
fi
160+
base="${cur##*/}"
161+
case "$base" in
162+
*:*)
163+
usb_parent="${base%%:*}"
164+
break
165+
;;
166+
esac
167+
cur="$(dirname "$cur")"
168+
done
169+
170+
[ -n "$usb_parent" ] || continue
171+
172+
# Only map cards whose parent USB device is in detected UAC device list
173+
if printf "%s\n" "$audio_device_list" | sed '/^$/d' | grep -qx "$usb_parent"; then
174+
card_map="${card_map}${usb_parent}|${c}\n"
175+
log_info "Mapped ALSA card$c -> USB device $usb_parent"
176+
fi
177+
done
178+
179+
# For each detected UAC device, verify mapped ALSA card(s) and device nodes
180+
has_devnodes_count=0
181+
for dev in $(printf "%s\n" "$audio_device_list" | sed '/^$/d'); do
182+
# Look up device details for debug messages (literal field matching)
183+
vidpid="$(printf "%b" "$dev_info_db" | awk -F'|' -v dev="$dev" '$1==dev{print $2; exit}')"
184+
driver_info="$(printf "%b" "$dev_info_db" | awk -F'|' -v dev="$dev" '$1==dev{print $3; exit}')"
185+
product_info="$(printf "%b" "$dev_info_db" | awk -F'|' -v dev="$dev" '$1==dev{print $4; exit}')"
186+
187+
missing_nodes=0
188+
189+
# Cards mapped to this USB device (literal field matching)
190+
cards_for_dev="$(printf "%b" "$card_map" | awk -F'|' -v dev="$dev" '$1==dev{print $2}' | tr '\n' ' ' | sed 's/[[:space:]]*$//')"
191+
192+
if [ -z "$cards_for_dev" ]; then
193+
log_info "UAC device $dev ($vidpid '$product_info', driver: $driver_info): No ALSA card mapped"
194+
missing_nodes=1
195+
continue
196+
fi
197+
198+
for c in $cards_for_dev; do
199+
ctrl_dev="/dev/snd/controlC$c"
200+
201+
# Check if control device exists
202+
if [ ! -e "$ctrl_dev" ]; then
203+
log_info "UAC device $dev (card$c): Missing control device $ctrl_dev"
204+
missing_nodes=1
205+
else
206+
log_info "UAC device $dev ($vidpid '$product_info') -> card$c: $ctrl_dev exists"
207+
fi
208+
209+
# Validate PCMs using /proc/asound/pcm, then verify corresponding /dev nodes
210+
has_play_cap="$(awk -v c="$c" '
211+
BEGIN{p=0;cap=0}
212+
/^[0-9]+-[0-9]+:/ {
213+
s=$1
214+
s=sub(":", "", s)
215+
}
216+
/^[0-9]+-[0-9]+:/ {
217+
split($1,a,"-"); gsub(":","",a[2]); if ((a[1]+0)==c) {
218+
if ($0 ~ /playback[[:space:]]+[1-9]/) p=1
219+
if ($0 ~ /capture[[:space:]]+[1-9]/) cap=1
220+
}
221+
}
222+
END{ printf "%d %d\n", p, cap }
223+
' /proc/asound/pcm 2>/dev/null || printf "0 0\n")"
224+
225+
has_play="$(printf "%s" "$has_play_cap" | awk '{print $1}')"
226+
has_cap="$(printf "%s" "$has_play_cap" | awk '{print $2}')"
227+
228+
if [ "${has_play:-0}" -eq 1 ]; then
229+
play_node_exists=0
230+
for n in /dev/snd/pcmC"${c}"D*p; do
231+
[ -e "$n" ] || continue
232+
log_info " PCM device (playback): $n exists"
233+
play_node_exists=1
234+
done
235+
if [ "$play_node_exists" -eq 0 ]; then
236+
log_info " PCM device (playback): nodes missing for card$c"
237+
missing_nodes=1
238+
fi
239+
else
240+
log_info " No playback streams for card$c"
241+
fi
242+
243+
if [ "${has_cap:-0}" -eq 1 ]; then
244+
cap_node_exists=0
245+
for n in /dev/snd/pcmC"${c}"D*c; do
246+
[ -e "$n" ] || continue
247+
log_info " PCM device (capture): $n exists"
248+
cap_node_exists=1
249+
done
250+
if [ "$cap_node_exists" -eq 0 ]; then
251+
log_info " PCM device (capture): nodes missing for card$c"
252+
missing_nodes=1
253+
fi
254+
else
255+
log_info " No capture streams for card$c"
256+
fi
257+
258+
if [ "${has_play:-0}" -eq 0 ] && [ "${has_cap:-0}" -eq 0 ]; then
259+
log_info " No PCM playback/capture streams found for card$c"
260+
missing_nodes=1
261+
fi
262+
done
263+
if [ "$missing_nodes" -eq 0 ]; then
264+
has_devnodes_count=$((has_devnodes_count + 1))
265+
fi
266+
done
267+
268+
if [ "${has_devnodes_count:-0}" -eq "$audio_device_count" ] 2>/dev/null; then
269+
log_pass "$TESTNAME : Test Passed - All ($audio_device_count/$audio_device_count) USB Audio device(s) detected have associated ALSA device nodes present"
270+
echo "$TESTNAME PASS" > "$RES_FILE"
271+
exit 0
272+
else
273+
log_fail "$TESTNAME : Test Failed - $((audio_device_count - has_devnodes_count))/$audio_device_count USB Audio device(s) missing associated ALSA device nodes"
274+
echo "$TESTNAME FAIL" > "$RES_FILE"
275+
exit 0
276+
fi
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
metadata:
2+
name: usb_uac
3+
format: "Lava-Test Test Definition 1.0"
4+
description: "This shell script executes on the DUT (Device-Under-Test) and verifies enumeration of connected USB Audio Class (UAC) Devices."
5+
os:
6+
- linux
7+
scope:
8+
- functional
9+
10+
run:
11+
steps:
12+
- REPO_PATH=$PWD
13+
- cd Runner/suites/Kernel/Baseport/USB/usb_uac
14+
- ./run.sh || true
15+
- $REPO_PATH/Runner/utils/send-to-lava.sh usb_uac.res
16+

0 commit comments

Comments
 (0)