feat: A2DP bluetooth audio working
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include "Xhci.hpp"
|
||||
#include <atomic>
|
||||
#include "UsbDevice.hpp"
|
||||
#include "HidKeyboard.hpp"
|
||||
#include "HidMouse.hpp"
|
||||
@@ -112,7 +113,17 @@ namespace Drivers::USB::Xhci {
|
||||
// Bluetooth event handler) see this and must NOT wait for completion --
|
||||
// PollEvents is non-reentrant, so the wait would never observe the
|
||||
// completion. Used by InPollContext() / ControlTransfer().
|
||||
static volatile bool g_pollActive = false;
|
||||
//
|
||||
// MUST be a real atomic claimed with compare-exchange, not a plain
|
||||
// volatile bool: PollEvents runs from the MSI handler (BSP) AND from
|
||||
// syscall-context pumps (A2DP WriteAudio/StartSource on whatever core the
|
||||
// app runs on) AND from idle cores (ProcessDeferredWork). A check-then-set
|
||||
// on a volatile is a cross-core TOCTOU: two cores both pass the check,
|
||||
// both drain the shared event ring, g_evtRingDequeue/g_evtRingCCS desync,
|
||||
// and completions get double-delivered (duplicate ACL packets) or lost
|
||||
// (missed AVDTP responses) -- both observed on HW once audio streaming
|
||||
// started pumping from syscall context.
|
||||
static std::atomic<bool> g_pollActive{false};
|
||||
|
||||
// Interrupt transfer data buffers (per slot)
|
||||
static uint8_t* g_interruptDataBuf[MAX_SLOTS + 1] = {};
|
||||
@@ -377,7 +388,7 @@ namespace Drivers::USB::Xhci {
|
||||
// sent from a Bluetooth event handler). Such callers must fire-and-forget,
|
||||
// not wait, since a nested PollEvents is a no-op.
|
||||
bool InPollContext() {
|
||||
return g_pollActive;
|
||||
return g_pollActive.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -396,8 +407,11 @@ namespace Drivers::USB::Xhci {
|
||||
// the active poll loop drains these events itself. g_pollActive is also
|
||||
// read by InPollContext() so command submitters (ControlTransfer) can
|
||||
// tell they are nested and must fire-and-forget instead of waiting.
|
||||
if (g_pollActive) return;
|
||||
g_pollActive = true;
|
||||
bool expected = false;
|
||||
if (!g_pollActive.compare_exchange_strong(expected, true,
|
||||
std::memory_order_acquire)) {
|
||||
return; // another context is draining; it will reap our events
|
||||
}
|
||||
|
||||
// Bound the work per call so a flooding/wedged device can never spin
|
||||
// here forever; the outer wall-clock timeouts then fire instead of
|
||||
@@ -566,7 +580,7 @@ namespace Drivers::USB::Xhci {
|
||||
}
|
||||
}
|
||||
|
||||
g_pollActive = false;
|
||||
g_pollActive.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -726,7 +740,7 @@ namespace Drivers::USB::Xhci {
|
||||
// would never be observed and the timeout+recovery path would corrupt
|
||||
// the EP0 ring. The transfer is submitted (doorbell rung); let the
|
||||
// active PollEvents reap its completion. Fire-and-forget.
|
||||
if (g_pollActive) {
|
||||
if (g_pollActive.load(std::memory_order_relaxed)) {
|
||||
return CC_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user