fix: move RTL-SDR to userspace

This commit is contained in:
daniel committed 2026-08-29 10:02:32 +02:00
1 parent b1f1cfe32b
commit 5cd5c2e6be
35 files changed
+1922 -1592

No files matched your search

+37 -17
View File
@@ -10,7 +10,7 @@
#include "HidKeyboard.hpp"
#include "HidMouse.hpp"
#include "MassStorage.hpp"
#include "Radio/RtlSdr.hpp"
#include "UserUsb.hpp"
#include <Pci/Pci.hpp>
#include <Terminal/Terminal.hpp>
#include <CppLib/Stream.hpp>
@@ -167,8 +167,7 @@ namespace Drivers::USB::Xhci {
// nesting (a callback invoked from THIS core's PollEvents); a different
// core merely polling must not make a process-context transfer skip its
// wait -- that returned CC_SUCCESS before the device filled the buffer
// (observed as garbled RTL-SDR register reads while the BT firmware
// download was polling on another core).
// (observed as corrupted USB control reads while another core was polling).
static std::atomic<int> g_pollOwnerCpu{-1};
// Serialises non-nested (waiting) control transfers so only one EP0
@@ -198,7 +197,7 @@ namespace Drivers::USB::Xhci {
// re-arms the SAME buffer at the ring tail, so the endpoint is never without
// a place to DMA. This closes the gap that single-outstanding bulk IN leaves
// between completion and re-arm, during which the device FIFO overflows
// (the RTL-SDR ~88% sample-drop at 2.048 Msps). PoolCount==0 => the legacy
// under sustained high-rate input. PoolCount==0 selects the legacy
// single-buffer path above (used by Bluetooth ACL), unchanged.
static constexpr uint32_t BULK_IN_POOL_MAX = 16;
static uint8_t* g_bulkInPool[MAX_SLOTS + 1][BULK_IN_POOL_MAX] = {};
@@ -206,6 +205,7 @@ namespace Drivers::USB::Xhci {
static uint32_t g_bulkInPoolCount[MAX_SLOTS + 1] = {}; // outstanding URBs (0=off)
static uint32_t g_bulkInPoolHead[MAX_SLOTS + 1] = {}; // next buffer to complete
static uint32_t g_bulkInPoolXferLen[MAX_SLOTS + 1] = {}; // bytes per transfer
static kcp::Spinlock g_bulkInPoolLocks[MAX_SLOTS + 1];
// Transfer callbacks for non-HID class drivers (per slot)
static TransferCallback g_transferCallbacks[MAX_SLOTS + 1] = {};
@@ -590,15 +590,19 @@ namespace Drivers::USB::Xhci {
// buffer is safe -- it will not be DMA'd into again
// until the other PoolCount-1 transfers ahead of it
// complete (~PoolCount ms of slack).
uint32_t i = g_bulkInPoolHead[slotId];
uint32_t reqLen = g_bulkInPoolXferLen[slotId];
uint32_t len = (residual < reqLen) ? (reqLen - residual) : 0;
g_transferCallbacks[slotId](slotId, epDci,
g_bulkInPool[slotId][i], len, completionCode);
QueueBulkInTransfer(slotId, g_bulkInPool[slotId][i],
g_bulkInPoolPhys[slotId][i], reqLen);
g_bulkInPoolHead[slotId] =
(i + 1) % g_bulkInPoolCount[slotId];
g_bulkInPoolLocks[slotId].Acquire();
uint32_t poolCount = g_bulkInPoolCount[slotId];
if (poolCount > 0) {
uint32_t i = g_bulkInPoolHead[slotId];
uint32_t reqLen = g_bulkInPoolXferLen[slotId];
uint32_t len = (residual < reqLen) ? (reqLen - residual) : 0;
g_transferCallbacks[slotId](slotId, epDci,
g_bulkInPool[slotId][i], len, completionCode);
QueueBulkInTransfer(slotId, g_bulkInPool[slotId][i],
g_bulkInPoolPhys[slotId][i], reqLen);
g_bulkInPoolHead[slotId] = (i + 1) % poolCount;
}
g_bulkInPoolLocks[slotId].Release();
} else if (epDci == bulkInDci && g_transferCallbacks[slotId]) {
// Bulk IN — dispatch via registered callback.
// len = actually-transferred bytes (requested -
@@ -1149,13 +1153,18 @@ namespace Drivers::USB::Xhci {
// single-buffer start relies on.
void PrimeBulkInStream(uint8_t slotId) {
if (slotId == 0 || slotId > MAX_SLOTS || !g_devices[slotId].Active) return;
g_bulkInPoolLocks[slotId].Acquire();
uint32_t n = g_bulkInPoolCount[slotId];
if (n == 0) return;
if (n == 0) {
g_bulkInPoolLocks[slotId].Release();
return;
}
g_bulkInPoolHead[slotId] = 0;
uint32_t len = g_bulkInPoolXferLen[slotId];
for (uint32_t i = 0; i < n; i++)
QueueBulkInTransfer(slotId, g_bulkInPool[slotId][i],
g_bulkInPoolPhys[slotId][i], len);
g_bulkInPoolLocks[slotId].Release();
}
void StartBulkInStream(uint8_t slotId, uint32_t xferLen, uint32_t numBuffers) {
@@ -1195,15 +1204,27 @@ namespace Drivers::USB::Xhci {
for (uint32_t i = 0; i < numBuffers; i++)
QueueBulkInTransfer(slotId, g_bulkInPool[slotId][i],
g_bulkInPoolPhys[slotId][i], xferLen);
g_bulkInPoolLocks[slotId].Acquire();
g_bulkInPoolCount[slotId] = numBuffers;
g_bulkInPoolLocks[slotId].Release();
}
bool IsBulkInStreamActive(uint8_t slotId) {
if (slotId == 0 || slotId > MAX_SLOTS) return false;
g_bulkInPoolLocks[slotId].Acquire();
bool active = g_bulkInPoolCount[slotId] != 0;
g_bulkInPoolLocks[slotId].Release();
return active;
}
void StopBulkInStream(uint8_t slotId) {
if (slotId == 0 || slotId > MAX_SLOTS) return;
// Disarm the rotation; any late completion now takes the (no-op for SDR)
// legacy path and is not re-armed. Buffers are retained for reuse.
g_bulkInPoolLocks[slotId].Acquire();
bool wasArmed = g_bulkInPoolCount[slotId] != 0;
g_bulkInPoolCount[slotId] = 0;
g_bulkInPoolLocks[slotId].Release();
if (!wasArmed) return;
// Flush the up-to-PoolCount TRBs still pending on the ring: Stop
@@ -1437,9 +1458,7 @@ namespace Drivers::USB::Xhci {
}
static void UnregisterClassDriver(uint8_t slotId, const UsbDeviceInfo& dev) {
if (Radio::IsRtlSdr(dev.VendorId, dev.ProductId)) {
Radio::UnregisterDevice(slotId);
} else if (dev.InterfaceClass == UsbDevice::CLASS_MASS_STORAGE) {
if (dev.InterfaceClass == UsbDevice::CLASS_MASS_STORAGE) {
MassStorage::UnregisterDevice(slotId);
} else if (dev.InterfaceClass == UsbDevice::CLASS_HID &&
dev.InterfaceProtocol == UsbDevice::PROTOCOL_KEYBOARD) {
@@ -1559,6 +1578,7 @@ namespace Drivers::USB::Xhci {
// Device disconnected — deactivate its slot
for (uint8_t s = 1; s <= MAX_SLOTS; s++) {
if (g_devices[s].Active && g_devices[s].PortId == port + 1) {
UserUsb::DeviceDisconnected(s);
UnregisterClassDriver(s, g_devices[s]);
g_devices[s].Active = false;
g_transferCallbacks[s] = nullptr;