summaryrefslogtreecommitdiff
path: root/tools/check_example_pids.py
diff options
context:
space:
mode:
authorHiFiPHile <[email protected]>2026-08-25 09:27:48 +0200
committerHiFiPHile <[email protected]>2026-08-25 09:27:48 +0200
commitdfac26a272fa7bbbca2050fbe9f1ca09008e548e (patch)
treeefbc53f8f2c1e5d9c7f38e5fef6d774a20053cec /tools/check_example_pids.py
parente590b45fcf51f9ddace73178074e4fe6d691e319 (diff)
parent5c0e31cdabaf37f14e1f5e988a020abfc1000495 (diff)
Merge master updates into the UAC1 host branch
Bring the audio work onto the current host core and build files before applying the remaining review fixes. Signed-off-by: HiFiPHile <[email protected]>
Diffstat (limited to 'tools/check_example_pids.py')
-rw-r--r--tools/check_example_pids.py55
1 files changed, 55 insertions, 0 deletions
diff --git a/tools/check_example_pids.py b/tools/check_example_pids.py
new file mode 100644
index 000000000..d8795af34
--- /dev/null
+++ b/tools/check_example_pids.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+"""Check that every example enumerates with a unique USB PID.
+
+Each example's usb_descriptors.c hardcodes its idProduct (0x40xx). Uniqueness is what
+guarantees back-to-back re-enumeration on the HIL rig and a fresh host driver match, and
+it is easy to break by hand: a new example copying a neighbour's PID, or an arithmetic
+PID (dynamic_configuration derives a second one from USB_PID). This collects every
+`#define USB_PID 0x....`, every literal `.idProduct = 0x....`, and every `USB_PID + <n>`
+derivation across examples/, and fails on any duplicate value.
+"""
+
+import re
+import sys
+from pathlib import Path
+
+EXAMPLES = Path(__file__).resolve().parents[1] / 'examples'
+
+RE_DEFINE = re.compile(r'#define\s+USB_PID\s+\(?(0x[0-9a-fA-F]+)\)?')
+RE_LITERAL = re.compile(r'\.idProduct\s*=\s*(0x[0-9a-fA-F]+)')
+RE_DERIVED = re.compile(r'\.idProduct\s*=\s*USB_PID\s*\+\s*(0x[0-9a-fA-F]+|\d+)')
+
+
+def main() -> int:
+ pids: dict[int, list[str]] = {}
+ for f in sorted(EXAMPLES.glob('*/*/src/usb_descriptors.c')):
+ text = f.read_text(errors='replace')
+ rel = f.relative_to(EXAMPLES.parent)
+ base = None
+ m = RE_DEFINE.search(text)
+ if m:
+ base = int(m.group(1), 16)
+ for m in RE_LITERAL.finditer(text):
+ pids.setdefault(int(m.group(1), 16), []).append(str(rel))
+ for m in RE_DERIVED.finditer(text):
+ if base is None:
+ print(f'{rel}: derived idProduct but no USB_PID define', file=sys.stderr)
+ return 1
+ pids.setdefault(base + int(m.group(1), 0), []).append(f'{rel} (USB_PID + {m.group(1)})')
+ # examples whose descriptor uses .idProduct = USB_PID pick up the define itself
+ if base is not None and re.search(r'\.idProduct\s*=\s*USB_PID\s*[,;]', text):
+ pids.setdefault(base, []).append(str(rel))
+
+ dups = {pid: users for pid, users in pids.items() if len(users) > 1}
+ for pid, users in sorted(dups.items()):
+ print(f'duplicate USB PID 0x{pid:04x}:', file=sys.stderr)
+ for u in users:
+ print(f' {u}', file=sys.stderr)
+ if dups:
+ return 1
+ print(f'{len(pids)} unique example USB PIDs')
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())