feat: reliable Bluetooth pairing, reconnection, and A2DP setup

Four fixes, each a root cause verified on hardware (AX211 + Bose QC Ultra):

1. Link Key Request Reply TRUNCATED: the pending-command queue's params
   buffer was 16 bytes; the reply is 22 (addr 6 + key 16).  The controller
   got 10 key bytes -> every stored-key reconnection failed authentication
   (status 5) since 2026-06-03 (0f16785).  Fresh pairings never touch this
   path, which kept the bug perfectly disguised as a headset quirk.

2. Secure Connections host support (0x0C7A) now enabled: bonds are minted
   as P-256 (Type=7), interoperable with BlueZ's, and SC-bonded peers can
   actually authenticate us.

3. Never write the BD_ADDR override (0xFC31) with the factory address:
   it desyncs the firmware's crypto address from the on-air one and ALL
   SSP pairing fails with status 5.  (The spoofing feature itself was
   already known-cosmetic: the baseband answers pages on the factory
   address regardless.)  import-bluez-bond.sh now removes the override.

4. A2DP channel setup: wait for Encryption Change before dialing L2CAP
   (post-SSP sinks ignore unencrypted CONN_REQ), and LISTEN 2.5s first --
   on reconnection the sink dials AVDTP itself and ignores our dials while
   doing so.  Ends the historical connRsp=FFFF retry-then-give-up failures.

Plus: queued security replies now log delivery + controller status.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
2026-07-07 19:46:02 +02:00
co-authored by Claude Fable 5
parent b39a5f8739
commit c4480a3bf2
6 changed files with 98 additions and 14 deletions
@@ -334,6 +334,18 @@ namespace Drivers::USB::Bluetooth {
// Enable Simple Secure Pairing
Hci::WriteSSPMode(1);
// Enable Secure Connections host support (P-256 / AES-CCM). Link
// keys are procedure-bound: a bond minted over Secure Connections
// (BlueZ always negotiates SC -> key Type=7) CANNOT authenticate a
// legacy link -- the controller must fail with status 5. Without
// this, a key shared with a Linux install (dual boot, see
// scripts/import-bluez-bond.sh) is cryptographically fine yet
// unusable, and our own pairings mint legacy P-192 keys that Linux
// then silently replaces. Must follow Write SSP Mode.
uint8_t scOn = 0x01;
Hci::SendCommand(Hci::OP_WRITE_SC_HOST_SUPPORT, &scOn, 1);
Hci::WaitCommandComplete(Hci::OP_WRITE_SC_HOST_SUPPORT);
// Allow role switch + sniff on new connections. The controller default
// link policy is 0x0000 (deny both). A multipoint headset (Bose QC
// Ultra) that also holds a link to a phone requests a role switch to