cleanup: Bluetooth/A2DP code cleanup

This commit is contained in:
2026-06-21 15:01:37 +02:00
parent 4c8e1c3591
commit c8d8ed6ebe
6 changed files with 29 additions and 178 deletions
+15 -66
View File
@@ -124,14 +124,11 @@ namespace Drivers::USB::Bluetooth::A2dp {
static uint64_t g_clockBase = 0; // ms timestamp of the media clock zero static uint64_t g_clockBase = 0; // ms timestamp of the media clock zero
static uint64_t g_sentSamples = 0; // per-channel samples sent since reset static uint64_t g_sentSamples = 0; // per-channel samples sent since reset
static uint64_t g_lastSendMs = 0; static uint64_t g_lastSendMs = 0;
static uint32_t g_underruns = 0; // ring ran dry while behind schedule
static bool g_inUnderrun = false;
static void ResetMediaClock() { static void ResetMediaClock() {
g_clockBase = Timekeeping::GetMilliseconds(); g_clockBase = Timekeeping::GetMilliseconds();
g_sentSamples = 0; g_sentSamples = 0;
g_lastSendMs = g_clockBase; g_lastSendMs = g_clockBase;
g_inUnderrun = false;
} }
// Volume // Volume
@@ -515,10 +512,6 @@ namespace Drivers::USB::Bluetooth::A2dp {
// remoteCid=0/connRsp=1 media-setup failure seen on HW even with SCMS-T // remoteCid=0/connRsp=1 media-setup failure seen on HW even with SCMS-T
// configured. So we serve one record: A2DP AudioSource. // configured. So we serve one record: A2DP AudioSource.
// SDP-HOST-TEST-BEGIN: the host harness (/tmp/sdptest) extracts the region
// between these markers verbatim and unit-tests it with libc, so keep it
// free of kernel-only dependencies (KernelLogStream and L2cap::SendData
// are stubbed there).
// AudioSource service record: attribute id (uint16 DE) + value, sorted by // AudioSource service record: attribute id (uint16 DE) + value, sorted by
// id, all SDP data elements big-endian. // id, all SDP data elements big-endian.
static const uint8_t kSourceRecord[] = { static const uint8_t kSourceRecord[] = {
@@ -766,15 +759,6 @@ namespace Drivers::USB::Bluetooth::A2dp {
// silently (its device interrogation then never completes, and a // silently (its device interrogation then never completes, and a
// stack that service-authorizes channels against that // stack that service-authorizes channels against that
// interrogation parks them at "authorization pending" forever). // interrogation parks them at "authorization pending" forever).
{ // Raw request dump: ONE decisive run beats guessing what the
// headset asked for. Queries are once-per-connection rare.
KernelLogStream cl(INFO, "BT-A2DP");
cl << "SDP req:" << base::hex;
for (uint16_t i = 0; i < len && i < 28; i++)
cl << " " << (uint64_t)d[i];
cl << base::dec;
}
// Field offsets differ: 0x06 has the search pattern at 5, 0x04 a // Field offsets differ: 0x06 has the search pattern at 5, 0x04 a
// 4-byte record handle at 5. Both follow with max byte count, // 4-byte record handle at 5. Both follow with max byte count,
// AttributeIDList, continuation state. // AttributeIDList, continuation state.
@@ -916,7 +900,6 @@ namespace Drivers::USB::Bluetooth::A2dp {
SdpServerSend(cid, 0x01, tid, params, n); SdpServerSend(cid, 0x01, tid, params, n);
} }
} }
// SDP-HOST-TEST-END
// Called by L2CAP when data arrives on ANY SDP channel -- ours (the client // Called by L2CAP when data arrives on ANY SDP channel -- ours (the client
// query response) or one the headset opened to us (a request to serve). // query response) or one the headset opened to us (a request to serve).
@@ -945,7 +928,6 @@ namespace Drivers::USB::Bluetooth::A2dp {
if (!cid || !L2cap::WaitConfigured(cid, timeoutMs)) { if (!cid || !L2cap::WaitConfigured(cid, timeoutMs)) {
KernelLogStream(WARNING, "BT-A2DP") << "SDP channel setup failed (connRsp=" KernelLogStream(WARNING, "BT-A2DP") << "SDP channel setup failed (connRsp="
<< base::hex << (uint64_t)L2cap::LastConnRspResult() << base::dec << ")"; << base::hex << (uint64_t)L2cap::LastConnRspResult() << base::dec << ")";
Hci::DumpAclStats(); // did our CONN_REQ even go out / any reply arrive?
return false; return false;
} }
g_sdpCid = cid; g_sdpCid = cid;
@@ -993,9 +975,9 @@ namespace Drivers::USB::Bluetooth::A2dp {
constexpr int kMaxAttempts = 4; constexpr int kMaxAttempts = 4;
// Connection phase, retried. A sink commonly ignores the very first // Connection phase, retried. A sink commonly ignores the very first
// L2CAP CONN_REQ that lands right after Encryption Change (the build-39 // L2CAP CONN_REQ that lands right after Encryption Change (connRsp stays
// HW symptom: connRsp stays 0xFFFF, the headset never answers). Re-dial // 0xFFFF, the headset never answers). Re-dial up to kMaxAttempts. We
// up to kMaxAttempts. We retry ONLY when the remote ignored us // retry ONLY when the remote ignored us
// (connRsp==0xFFFF -> our dialed channel has RemoteCid==0, so freeing it // (connRsp==0xFFFF -> our dialed channel has RemoteCid==0, so freeing it
// owes the peer no Disconnect); if it answered but config stalled // owes the peer no Disconnect); if it answered but config stalled
// (connRsp!=0xFFFF) we do NOT loop -- that's the config phase, surfaced // (connRsp!=0xFFFF) we do NOT loop -- that's the config phase, surfaced
@@ -1038,14 +1020,12 @@ namespace Drivers::USB::Bluetooth::A2dp {
<< " localCfg=" << (uint64_t)(ch ? ch->LocalConfigDone : 0) << " localCfg=" << (uint64_t)(ch ? ch->LocalConfigDone : 0)
<< " remoteCfg=" << (uint64_t)(ch ? ch->RemoteConfigDone : 0) << " remoteCfg=" << (uint64_t)(ch ? ch->RemoteConfigDone : 0)
<< " connRsp=" << base::hex << (uint64_t)L2cap::LastConnRspResult() << " connRsp=" << base::hex << (uint64_t)L2cap::LastConnRspResult()
<< base::dec << " incomingReqs=" << base::dec << ")";
<< (uint64_t)L2cap::IncomingAvdtpReqCount() << ")";
// Retry only if the remote IGNORED our CONN_REQ entirely. If it // Retry only if the remote IGNORED our CONN_REQ entirely. If it
// answered (connRsp != 0xFFFF) the stall is in the config exchange, // answered (connRsp != 0xFFFF) the stall is in the config exchange,
// not the dial -- don't loop; let the config-phase logs speak. // not the dial -- don't loop; let the config-phase logs speak.
if (L2cap::LastConnRspResult() != 0xFFFF) { if (L2cap::LastConnRspResult() != 0xFFFF) {
Hci::DumpAclStats();
return false; return false;
} }
// Free our unanswered dialed channel so repeated retries don't leak // Free our unanswered dialed channel so repeated retries don't leak
@@ -1062,7 +1042,6 @@ namespace Drivers::USB::Bluetooth::A2dp {
if (g_sigCid == 0) { if (g_sigCid == 0) {
KernelLogStream(WARNING, "BT-A2DP") << "AVDTP signaling setup gave up after retries"; KernelLogStream(WARNING, "BT-A2DP") << "AVDTP signaling setup gave up after retries";
Hci::DumpAclStats();
return false; return false;
} }
KernelLogStream(OK, "BT-A2DP") << "AVDTP signaling channel ready, cid=" KernelLogStream(OK, "BT-A2DP") << "AVDTP signaling channel ready, cid="
@@ -1107,9 +1086,9 @@ namespace Drivers::USB::Bluetooth::A2dp {
// sink's open-acceptor window), and HOLD that same channel while // sink's open-acceptor window), and HOLD that same channel while
// polling. A CONN_RSP result=1 (PENDING) is NON-FINAL: a conformant // polling. A CONN_RSP result=1 (PENDING) is NON-FINAL: a conformant
// sink follows it with SUCCESS/REFUSED on the SAME channel, so we must // sink follows it with SUCCESS/REFUSED on the SAME channel, so we must
// NOT tear it down and re-dial -- doing so (build 47) only churned CIDs // NOT tear it down and re-dial -- doing so only churns CIDs and
// and abandoned the very connection the sink was authorizing, while a // abandons the very connection the sink is authorizing; a single held
// single held dial (build 44) already proved holding alone is harmless. // dial is harmless even when the sink takes seconds to authorize.
// Also accept an inbound transport channel (some sinks open it). // Also accept an inbound transport channel (some sinks open it).
// NOTE: Bose service-gates this channel TWICE: SetConfiguration must // NOTE: Bose service-gates this channel TWICE: SetConfiguration must
// include Content Protection (SCMS-T, see AvdtpSetConfiguration), AND // include Content Protection (SCMS-T, see AvdtpSetConfiguration), AND
@@ -1145,9 +1124,7 @@ namespace Drivers::USB::Bluetooth::A2dp {
<< " localCfg=" << (uint64_t)(ch ? ch->LocalConfigDone : 0) << " localCfg=" << (uint64_t)(ch ? ch->LocalConfigDone : 0)
<< " remoteCfg=" << (uint64_t)(ch ? ch->RemoteConfigDone : 0) << " remoteCfg=" << (uint64_t)(ch ? ch->RemoteConfigDone : 0)
<< " connRsp=" << base::hex << (uint64_t)L2cap::LastConnRspResult() << " connRsp=" << base::hex << (uint64_t)L2cap::LastConnRspResult()
<< base::dec << " incomingReqs=" << base::dec << ")";
<< (uint64_t)L2cap::IncomingAvdtpReqCount() << ")";
Hci::DumpAclStats();
return false; return false;
} }
@@ -1155,12 +1132,12 @@ namespace Drivers::USB::Bluetooth::A2dp {
<< base::hex << (uint64_t)g_mediaCid << base::dec << " state=Open"; << base::hex << (uint64_t)g_mediaCid << base::dec << " state=Open";
// 5. AVRCP control channel (PSM 0x17), best effort, AFTER the stream // 5. AVRCP control channel (PSM 0x17), best effort, AFTER the stream
// exists -- the order every phone uses. An earlier build dialed it // exists -- the order every phone uses. Dialed before the media
// right after the signaling channel; the sink answered PENDING and // channel, the sink answers PENDING and never completes it, leaving a
// never completed it, so the half-open dial just sat in its // half-open dial parked in its authorization queue ahead of the media
// authorization queue ahead of the media channel. Dialing here // channel. Dialing here keeps the bring-up clean and still gives the
// keeps the bring-up clean and still gives the headset its absolute // headset its absolute volume path; an inbound AVCTP connect is
// volume path; an inbound AVCTP connect is accepted at any time. // accepted at any time.
{ {
uint16_t avrcp = L2cap::Connect(L2cap::PSM_AVCTP); uint16_t avrcp = L2cap::Connect(L2cap::PSM_AVCTP);
if (avrcp && L2cap::WaitConfigured(avrcp, 1500)) { if (avrcp && L2cap::WaitConfigured(avrcp, 1500)) {
@@ -1411,15 +1388,7 @@ namespace Drivers::USB::Bluetooth::A2dp {
uint64_t audioMs = g_sentSamples * 1000 / g_pcmRate; uint64_t audioMs = g_sentSamples * 1000 / g_pcmRate;
uint64_t elapsed = now - g_clockBase; uint64_t elapsed = now - g_clockBase;
if (fill < bytesPerFrame) { if (fill < bytesPerFrame) break; // ring dry, nothing to send
// Ring dry. If the sink's lead is exhausted too, this is an
// audible dropout caused by the app not feeding in time.
if (!g_inUnderrun && g_sentSamples != 0 && elapsed > audioMs) {
g_inUnderrun = true;
g_underruns++;
}
break;
}
if (audioMs >= elapsed + LEAD_MS) break; // sink lead is full if (audioMs >= elapsed + LEAD_MS) break; // sink lead is full
if (!Hci::AclTxReady()) { if (!Hci::AclTxReady()) {
// Normal credit pacing most of the time. A credit pool stuck // Normal credit pacing most of the time. A credit pool stuck
@@ -1501,26 +1470,6 @@ namespace Drivers::USB::Bluetooth::A2dp {
g_timestamp += samplesPerFrame * nFrames; g_timestamp += samplesPerFrame * nFrames;
g_sentSamples += samplesPerFrame * nFrames; g_sentSamples += samplesPerFrame * nFrames;
g_lastSendMs = now; g_lastSendMs = now;
g_inUnderrun = false;
// Bring-up diagnostic: heartbeat every 512 packets (~6.8 s of
// audio at 5 frames/packet) proving the pipeline is healthy.
if ((g_seqNum & 0x1FF) == 0) {
uint32_t left = g_ringHead.load(std::memory_order_acquire)
- g_ringTail.load(std::memory_order_relaxed);
uint32_t ringMs = (g_pcmRate && g_sbcEncoder.Channels)
? (uint32_t)((uint64_t)left * 1000
/ ((uint64_t)g_pcmRate * g_sbcEncoder.Channels * 2))
: 0;
KernelLogStream(INFO, "BT-A2DP") << "media: seq="
<< (uint64_t)g_seqNum
<< " bundle=" << (uint64_t)nFrames
<< " ring=" << (uint64_t)ringMs << "ms"
<< " lead=" << (uint64_t)(audioMs > elapsed ? audioMs - elapsed : 0) << "ms"
<< " inflight=" << (uint64_t)Hci::AclTxInFlight()
<< " pending=" << (uint64_t)Hci::AclPendingCount()
<< " underruns=" << (uint64_t)g_underruns;
}
} }
g_pumpActive.store(false, std::memory_order_release); g_pumpActive.store(false, std::memory_order_release);
@@ -292,9 +292,8 @@ namespace Drivers::USB::Bluetooth {
<< (uint64_t)g_bdAddr[1] << ":" << (uint64_t)g_bdAddr[0] << base::dec; << (uint64_t)g_bdAddr[1] << ":" << (uint64_t)g_bdAddr[0] << base::dec;
} }
// NOTE: an earlier build overrode the BD_ADDR via 0xFC31 to dodge a // NOTE: do NOT override the BD_ADDR via 0xFC31 here. The BD_ADDR is an
// (since disproven) stale-bond theory. Removed: the BD_ADDR is an input // input to the SSP authentication confirmation, and if the override only
// to the SSP authentication confirmation, and if the override only
// changes the advertised address but not the address the firmware uses // changes the advertised address but not the address the firmware uses
// in the crypto, the two sides compute different confirmations and // in the crypto, the two sides compute different confirmations and
// pairing fails (Simple Pairing Complete = 0x05). Use the real address. // pairing fails (Simple Pairing Complete = 0x05). Use the real address.
@@ -501,8 +500,6 @@ namespace Drivers::USB::Bluetooth {
int Connect(const uint8_t* bdAddr, uint32_t timeoutMs) { int Connect(const uint8_t* bdAddr, uint32_t timeoutMs) {
if (!g_initialized || !bdAddr) return -1; if (!g_initialized || !bdAddr) return -1;
Hci::ResetEventTrace(); // capture the pairing/SSP event sequence
if (!Hci::CreateConnection(bdAddr)) return -1; if (!Hci::CreateConnection(bdAddr)) return -1;
// Wait for Connection Complete event // Wait for Connection Complete event
@@ -571,7 +568,6 @@ namespace Drivers::USB::Bluetooth {
// the link later dropped, so the disk write never stalls // the link later dropped, so the disk write never stalls
// the nested pairing event handler. // the nested pairing event handler.
Hci::FlushLinkKeys(); Hci::FlushLinkKeys();
Hci::DumpEventTrace(); // show the pairing/SSP sequence
return 0; return 0;
} }
} }
@@ -582,7 +578,6 @@ namespace Drivers::USB::Bluetooth {
} }
} }
Hci::DumpEventTrace(); // show whatever events did arrive
return -1; // Timeout return -1; // Timeout
} }
+8 -76
View File
@@ -82,17 +82,11 @@ namespace Drivers::USB::Bluetooth::Hci {
// on another core. int32 so a stray double-NOCP clamps instead of wrapping. // on another core. int32 so a stray double-NOCP clamps instead of wrapping.
static std::atomic<int32_t> g_aclPendingCount{0}; static std::atomic<int32_t> g_aclPendingCount{0};
// Diagnostic ACL data-path counters: TX submitted, TX completed (bulk OUT // ACL TX flow-control counters: TX submitted and TX completed (bulk OUT
// completion), RX received (bulk IN). Used to tell whether L2CAP signaling // completion). tx - txDone is the number of TX DMA ring slots still owned
// actually flows over ACL after encryption is enabled. tx - txDone is also // by the xHCI (see AclTxReady), so the media writer throttles on it.
// the number of TX DMA ring slots still owned by the xHCI (see AclTxReady).
static std::atomic<uint32_t> g_aclTxCount{0}; static std::atomic<uint32_t> g_aclTxCount{0};
static std::atomic<uint32_t> g_aclTxDoneCount{0}; static std::atomic<uint32_t> g_aclTxDoneCount{0};
static volatile uint32_t g_aclRxCount = 0;
// Incremented when an incoming ACL packet is dropped because the RX ring was
// full -- a nonzero value means a burst overran the ring (and could have
// dropped an L2CAP Config Response). Surfaced by DumpAclStats().
static volatile uint32_t g_aclRxDropCount = 0;
// Inquiry results // Inquiry results
static InquiryDevice g_inquiryResults[MAX_INQUIRY_RESULTS] = {}; static InquiryDevice g_inquiryResults[MAX_INQUIRY_RESULTS] = {};
@@ -120,19 +114,6 @@ namespace Drivers::USB::Bluetooth::Hci {
static volatile uint32_t g_ssFragsSent = 0; static volatile uint32_t g_ssFragsSent = 0;
static volatile uint32_t g_lastControlCC = 0; static volatile uint32_t g_lastControlCC = 0;
// Lockless HCI-event trace for diagnosing the pairing/SSP sequence.
// ProcessEvent (which may run from the xHCI IRQ) only does an array write
// here -- no lock, no terminal I/O, so it cannot deadlock against g_termLock.
// DumpEventTrace() prints it from top-level (process) context.
static constexpr int EVT_TRACE_MAX = 48;
static volatile uint8_t g_evtTrace[EVT_TRACE_MAX] = {};
static volatile uint8_t g_evtTraceCount = 0;
// Status byte of the last Simple Pairing Complete (0x36) / Auth Complete
// (0x06) / Disconnection (0x05) -- captured to find WHY pairing fails.
static volatile uint8_t g_lastSppStatus = 0xEE;
static volatile uint8_t g_lastAuthStatus = 0xEE;
static volatile uint8_t g_lastDiscReason = 0xEE;
// Pending-command queue. Pairing replies (IO-cap / user-confirm / link-key) // Pending-command queue. Pairing replies (IO-cap / user-confirm / link-key)
// are triggered from event handlers running NESTED under PollEvents, where a // are triggered from event handlers running NESTED under PollEvents, where a
// command can only be fire-and-forget (the reentrancy guard makes a nested // command can only be fire-and-forget (the reentrancy guard makes a nested
@@ -327,16 +308,13 @@ namespace Drivers::USB::Bluetooth::Hci {
// re-arm on every completion below (the bulk IN must keep cycling to // re-arm on every completion below (the bulk IN must keep cycling to
// absorb the device's ~635 KB cc=4 glitch during firmware download). // absorb the device's ~635 KB cc=4 glitch during firmware download).
if (data && length >= sizeof(AclHeader) + 4) { if (data && length >= sizeof(AclHeader) + 4) {
g_aclRxCount++;
uint8_t next = (uint8_t)((g_aclRxHead + 1) % ACL_RX_SLOTS); uint8_t next = (uint8_t)((g_aclRxHead + 1) % ACL_RX_SLOTS);
if (next != g_aclRxTail) { // ring not full if (next != g_aclRxTail) { // ring not full (else drop: overran)
uint32_t copyLen = length; uint32_t copyLen = length;
if (copyLen > ACL_RX_SLOT_SIZE) copyLen = ACL_RX_SLOT_SIZE; if (copyLen > ACL_RX_SLOT_SIZE) copyLen = ACL_RX_SLOT_SIZE;
memcpy(g_aclRxRing[g_aclRxHead], data, copyLen); memcpy(g_aclRxRing[g_aclRxHead], data, copyLen);
g_aclRxLens[g_aclRxHead] = (uint16_t)copyLen; g_aclRxLens[g_aclRxHead] = (uint16_t)copyLen;
g_aclRxHead = next; g_aclRxHead = next;
} else {
g_aclRxDropCount++; // ring overran -> a packet was lost
} }
} }
@@ -409,10 +387,10 @@ namespace Drivers::USB::Bluetooth::Hci {
// glitches a USB transaction error (cc=4) near ~635 KB of the upload, and an // glitches a USB transaction error (cc=4) near ~635 KB of the upload, and an
// armed bulk IN ABSORBS it (a benign cc=4 on the bulk endpoint) so the // armed bulk IN ABSORBS it (a benign cc=4 on the bulk endpoint) so the
// interrupt-IN event pipe survives and the download completes. Deferring the // interrupt-IN event pipe survives and the download completes. Deferring the
// bulk-IN arm (build 40) moved that cc=4 onto the interrupt IN and wedged the // bulk-IN arm moves that cc=4 onto the interrupt IN and wedges the download at
// download at 635 KB -- reverted. The harmless firmware-phase bulk-IN runts // 635 KB, so the bulk IN must be armed up front. The harmless firmware-phase
// (4-7 byte completions) are filtered at enqueue in TransferCallback so they // bulk-IN runts (4-7 byte completions) are filtered at enqueue in
// never pollute the RX ring. // TransferCallback so they never pollute the RX ring.
void StartEventPipe() { void StartEventPipe() {
if (!g_initialized) return; if (!g_initialized) return;
@@ -648,16 +626,6 @@ namespace Drivers::USB::Bluetooth::Hci {
g_aclPendingCount.store(0, std::memory_order_relaxed); g_aclPendingCount.store(0, std::memory_order_relaxed);
} }
void DumpAclStats() {
KernelLogStream(INFO, "BT-HCI") << "ACL stats: tx="
<< (uint64_t)g_aclTxCount.load(std::memory_order_relaxed)
<< " txDone=" << (uint64_t)g_aclTxDoneCount.load(std::memory_order_relaxed)
<< " rx=" << (uint64_t)g_aclRxCount
<< " rxDrop=" << (uint64_t)g_aclRxDropCount
<< " pending=" << (uint64_t)AclPendingCount()
<< " bufNum=" << (uint64_t)g_aclMaxNum;
}
// ========================================================================= // =========================================================================
// ProcessEvent — handle HCI events // ProcessEvent — handle HCI events
// ========================================================================= // =========================================================================
@@ -669,17 +637,6 @@ namespace Drivers::USB::Bluetooth::Hci {
uint8_t evtParamLen = data[1]; uint8_t evtParamLen = data[1];
const uint8_t* params = data + 2; const uint8_t* params = data + 2;
// Record into the lockless trace (safe from the IRQ path: array write
// only, no lock / no terminal I/O). DumpEventTrace() prints it later
// from top-level context.
if (evtCode != EVT_NUM_COMPLETED_PACKETS && g_evtTraceCount < EVT_TRACE_MAX) {
g_evtTrace[g_evtTraceCount++] = evtCode;
}
// status byte = params[0] for these (Disconnection: status,handle,reason)
if (evtCode == EVT_SIMPLE_PAIRING_COMPLETE && evtParamLen >= 1) g_lastSppStatus = params[0];
if (evtCode == EVT_AUTH_COMPLETE && evtParamLen >= 1) g_lastAuthStatus = params[0];
if (evtCode == EVT_DISCONNECTION_COMPLETE && evtParamLen >= 4) g_lastDiscReason = params[3];
switch (evtCode) { switch (evtCode) {
case EVT_CONNECTION_COMPLETE: { case EVT_CONNECTION_COMPLETE: {
if (evtParamLen >= 11) { if (evtParamLen >= 11) {
@@ -1124,31 +1081,6 @@ namespace Drivers::USB::Bluetooth::Hci {
return true; return true;
} }
void ResetEventTrace() {
g_evtTraceCount = 0;
g_lastSppStatus = 0xEE;
g_lastAuthStatus = 0xEE;
g_lastDiscReason = 0xEE;
}
void DumpEventTrace() {
// Top-level only (acquires g_termLock). Shows the HCI-event sequence so
// a stalled pairing/SSP flow is visible, e.g. whether an IO Capability
// Request (0x31) / User Confirm (0x33) arrive after a link-key reply, or
// the link just goes 0x03 (connect) -> 0x17 (link-key req) -> 0x05
// (disconnect/auth-fail) with no pairing in between.
KernelLogStream s(INFO, "BT-HCI");
s << "event trace:";
uint8_t n = g_evtTraceCount;
if (n > EVT_TRACE_MAX) n = EVT_TRACE_MAX;
for (uint8_t i = 0; i < n; i++) {
s << " " << base::hex << (uint64_t)g_evtTrace[i] << base::dec;
}
s << " | spp=" << base::hex << (uint64_t)g_lastSppStatus
<< " auth=" << (uint64_t)g_lastAuthStatus
<< " disc=" << (uint64_t)g_lastDiscReason << base::dec;
}
void ProcessPendingCommands() { void ProcessPendingCommands() {
// Top-level only: drains queued pairing replies with real, confirmed // Top-level only: drains queued pairing replies with real, confirmed
// control transfers. Must NOT be called from inside PollEvents. // control transfers. Must NOT be called from inside PollEvents.
-8
View File
@@ -304,19 +304,11 @@ namespace Drivers::USB::Bluetooth::Hci {
// remote that holds a stale, un-clearable bond to our real address. // remote that holds a stale, un-clearable bond to our real address.
bool SetBdAddr(const uint8_t* addr); bool SetBdAddr(const uint8_t* addr);
// Lockless HCI-event trace (diagnostic). Reset before a connection attempt,
// dump afterwards from top-level to see the pairing/SSP event sequence.
void ResetEventTrace();
void DumpEventTrace();
// Send any queued pairing replies (IO-cap / user-confirm / link-key) with // Send any queued pairing replies (IO-cap / user-confirm / link-key) with
// real confirmed transfers. Call from top-level (e.g. the connect loop), // real confirmed transfers. Call from top-level (e.g. the connect loop),
// NOT from an event handler -- event handlers only enqueue. // NOT from an event handler -- event handlers only enqueue.
void ProcessPendingCommands(); void ProcessPendingCommands();
// Diagnostic: print ACL data-path counters (tx / txDone / rx / pending).
void DumpAclStats();
// ACL TX flow control: outstanding (un-acked) ACL packets, and the // ACL TX flow control: outstanding (un-acked) ACL packets, and the
// controller's ACL buffer count (Number-Of-Completed-Packets credits). The // controller's ACL buffer count (Number-Of-Completed-Packets credits). The
// media writer throttles on these so it never overruns the controller. // media writer throttles on these so it never overruns the controller.
@@ -31,19 +31,10 @@ namespace Drivers::USB::Bluetooth::L2cap {
// 3=security-block, 4=no-resources). 0xFFFF = none received yet. // 3=security-block, 4=no-resources). 0xFFFF = none received yet.
static volatile uint16_t g_lastConnRspResult = 0xFFFF; static volatile uint16_t g_lastConnRspResult = 0xFFFF;
// Diagnostic: how many incoming AVDTP connection requests the remote has
// made (tells us whether the headset wants to initiate the audio channel).
static volatile uint16_t g_incomingAvdtpReqs = 0;
// Channel table // Channel table
static ChannelInfo g_channels[MAX_CHANNELS] = {}; static ChannelInfo g_channels[MAX_CHANNELS] = {};
static uint16_t g_nextCid = CID_DYNAMIC_START; static uint16_t g_nextCid = CID_DYNAMIC_START;
// Signaling response tracking
static volatile bool g_sigResponseReady = false;
static uint8_t g_sigResponseBuf[64] = {};
static uint32_t g_sigResponseLen = 0;
// ========================================================================= // =========================================================================
// Helpers // Helpers
// ========================================================================= // =========================================================================
@@ -154,7 +145,6 @@ namespace Drivers::USB::Bluetooth::L2cap {
// Accept connections for AVDTP, SDP, and AVRCP control // Accept connections for AVDTP, SDP, and AVRCP control
if (psm == PSM_AVDTP || psm == PSM_SDP || psm == PSM_AVCTP) { if (psm == PSM_AVDTP || psm == PSM_SDP || psm == PSM_AVCTP) {
if (psm == PSM_AVDTP) g_incomingAvdtpReqs++;
auto* ch = AllocChannel(psm); auto* ch = AllocChannel(psm);
if (ch) { if (ch) {
ch->RemoteCid = srcCid; ch->RemoteCid = srcCid;
@@ -565,8 +555,4 @@ namespace Drivers::USB::Bluetooth::L2cap {
return g_lastConnRspResult; return g_lastConnRspResult;
} }
uint16_t IncomingAvdtpReqCount() {
return g_incomingAvdtpReqs;
}
} }
+4 -7
View File
@@ -125,13 +125,10 @@ namespace Drivers::USB::Bluetooth::L2cap {
// Get the ACL handle // Get the ACL handle
uint16_t GetAclHandle(); uint16_t GetAclHandle();
// Diagnostic: result code of the most recent outgoing L2CAP Connection // Result code of the most recent outgoing L2CAP Connection Response
// Response (0xFFFF if none yet). 0=success, 1=pending, 2=PSM-unsupported, // (0xFFFF if none yet). 0=success, 1=pending, 2=PSM-unsupported,
// 3=security-block, 4=no-resources. // 3=security-block, 4=no-resources. StartSource() retries only when the
// remote ignored the dial entirely (0xFFFF).
uint16_t LastConnRspResult(); uint16_t LastConnRspResult();
// Diagnostic: number of incoming AVDTP connection requests seen (whether the
// remote wants to initiate the audio channel itself).
uint16_t IncomingAvdtpReqCount();
} }