cleanup: Bluetooth/A2DP code cleanup
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user