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_sentSamples = 0; // per-channel samples sent since reset
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() {
g_clockBase = Timekeeping::GetMilliseconds();
g_sentSamples = 0;
g_lastSendMs = g_clockBase;
g_inUnderrun = false;
}
// Volume
@@ -515,10 +512,6 @@ namespace Drivers::USB::Bluetooth::A2dp {
// remoteCid=0/connRsp=1 media-setup failure seen on HW even with SCMS-T
// 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
// id, all SDP data elements big-endian.
static const uint8_t kSourceRecord[] = {
@@ -766,15 +759,6 @@ namespace Drivers::USB::Bluetooth::A2dp {
// silently (its device interrogation then never completes, and a
// stack that service-authorizes channels against that
// 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
// 4-byte record handle at 5. Both follow with max byte count,
// AttributeIDList, continuation state.
@@ -916,7 +900,6 @@ namespace Drivers::USB::Bluetooth::A2dp {
SdpServerSend(cid, 0x01, tid, params, n);
}
}
// SDP-HOST-TEST-END
// 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).
@@ -945,7 +928,6 @@ namespace Drivers::USB::Bluetooth::A2dp {
if (!cid || !L2cap::WaitConfigured(cid, timeoutMs)) {
KernelLogStream(WARNING, "BT-A2DP") << "SDP channel setup failed (connRsp="
<< base::hex << (uint64_t)L2cap::LastConnRspResult() << base::dec << ")";
Hci::DumpAclStats(); // did our CONN_REQ even go out / any reply arrive?
return false;
}
g_sdpCid = cid;
@@ -993,9 +975,9 @@ namespace Drivers::USB::Bluetooth::A2dp {
constexpr int kMaxAttempts = 4;
// Connection phase, retried. A sink commonly ignores the very first
// L2CAP CONN_REQ that lands right after Encryption Change (the build-39
// HW symptom: connRsp stays 0xFFFF, the headset never answers). Re-dial
// up to kMaxAttempts. We retry ONLY when the remote ignored us
// L2CAP CONN_REQ that lands right after Encryption Change (connRsp stays
// 0xFFFF, the headset never answers). Re-dial up to kMaxAttempts. We
// retry ONLY when the remote ignored us
// (connRsp==0xFFFF -> our dialed channel has RemoteCid==0, so freeing it
// owes the peer no Disconnect); if it answered but config stalled
// (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)
<< " remoteCfg=" << (uint64_t)(ch ? ch->RemoteConfigDone : 0)
<< " connRsp=" << base::hex << (uint64_t)L2cap::LastConnRspResult()
<< base::dec << " incomingReqs="
<< (uint64_t)L2cap::IncomingAvdtpReqCount() << ")";
<< base::dec << ")";
// Retry only if the remote IGNORED our CONN_REQ entirely. If it
// answered (connRsp != 0xFFFF) the stall is in the config exchange,
// not the dial -- don't loop; let the config-phase logs speak.
if (L2cap::LastConnRspResult() != 0xFFFF) {
Hci::DumpAclStats();
return false;
}
// Free our unanswered dialed channel so repeated retries don't leak
@@ -1062,7 +1042,6 @@ namespace Drivers::USB::Bluetooth::A2dp {
if (g_sigCid == 0) {
KernelLogStream(WARNING, "BT-A2DP") << "AVDTP signaling setup gave up after retries";
Hci::DumpAclStats();
return false;
}
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
// 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
// NOT tear it down and re-dial -- doing so (build 47) only churned CIDs
// and abandoned the very connection the sink was authorizing, while a
// single held dial (build 44) already proved holding alone is harmless.
// NOT tear it down and re-dial -- doing so only churns CIDs and
// abandons the very connection the sink is authorizing; a single held
// dial is harmless even when the sink takes seconds to authorize.
// Also accept an inbound transport channel (some sinks open it).
// NOTE: Bose service-gates this channel TWICE: SetConfiguration must
// include Content Protection (SCMS-T, see AvdtpSetConfiguration), AND
@@ -1145,9 +1124,7 @@ namespace Drivers::USB::Bluetooth::A2dp {
<< " localCfg=" << (uint64_t)(ch ? ch->LocalConfigDone : 0)
<< " remoteCfg=" << (uint64_t)(ch ? ch->RemoteConfigDone : 0)
<< " connRsp=" << base::hex << (uint64_t)L2cap::LastConnRspResult()
<< base::dec << " incomingReqs="
<< (uint64_t)L2cap::IncomingAvdtpReqCount() << ")";
Hci::DumpAclStats();
<< base::dec << ")";
return false;
}
@@ -1155,12 +1132,12 @@ namespace Drivers::USB::Bluetooth::A2dp {
<< base::hex << (uint64_t)g_mediaCid << base::dec << " state=Open";
// 5. AVRCP control channel (PSM 0x17), best effort, AFTER the stream
// exists -- the order every phone uses. An earlier build dialed it
// right after the signaling channel; the sink answered PENDING and
// never completed it, so the half-open dial just sat in its
// authorization queue ahead of the media channel. Dialing here
// keeps the bring-up clean and still gives the headset its absolute
// volume path; an inbound AVCTP connect is accepted at any time.
// exists -- the order every phone uses. Dialed before the media
// channel, the sink answers PENDING and never completes it, leaving a
// half-open dial parked in its authorization queue ahead of the media
// channel. Dialing here keeps the bring-up clean and still gives the
// headset its absolute volume path; an inbound AVCTP connect is
// accepted at any time.
{
uint16_t avrcp = L2cap::Connect(L2cap::PSM_AVCTP);
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 elapsed = now - g_clockBase;
if (fill < bytesPerFrame) {
// 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 (fill < bytesPerFrame) break; // ring dry, nothing to send
if (audioMs >= elapsed + LEAD_MS) break; // sink lead is full
if (!Hci::AclTxReady()) {
// 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_sentSamples += samplesPerFrame * nFrames;
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);