feat: Intel HDA audio driver, audio streaming syscalls, userspace Music app, fixes and improvements, rudimentary Bluetooth support

This commit is contained in:
2026-03-10 17:14:33 +01:00
parent bcd959b80f
commit 9f54e4957d
58 changed files with 11275 additions and 137 deletions
+180 -18
View File
@@ -98,6 +98,13 @@ namespace Drivers::USB::Xhci {
static uint8_t* g_interruptDataBuf[MAX_SLOTS + 1] = {};
static uint64_t g_interruptDataBufPhys[MAX_SLOTS + 1] = {};
// Bulk IN transfer data buffers (per slot)
static uint8_t* g_bulkInDataBuf[MAX_SLOTS + 1] = {};
static uint64_t g_bulkInDataBufPhys[MAX_SLOTS + 1] = {};
// Transfer callbacks for non-HID class drivers (per slot)
static TransferCallback g_transferCallbacks[MAX_SLOTS + 1] = {};
// Scratchpad buffer array
static uint64_t* g_scratchpadBufs = nullptr;
@@ -174,6 +181,36 @@ namespace Drivers::USB::Xhci {
}
}
// Advance bulk IN ring enqueue pointer, activating Link TRB when reached
static void AdvanceBulkInRing(UsbDeviceInfo& dev) {
dev.BulkInRingEnqueue++;
if (dev.BulkInRingEnqueue >= XFER_RING_SIZE - 1) {
TRB& link = dev.BulkInRing[XFER_RING_SIZE - 1];
if (dev.BulkInRingCCS) {
link.Control |= TRB_CYCLE_BIT;
} else {
link.Control &= ~TRB_CYCLE_BIT;
}
dev.BulkInRingCCS = !dev.BulkInRingCCS;
dev.BulkInRingEnqueue = 0;
}
}
// Advance bulk OUT ring enqueue pointer, activating Link TRB when reached
static void AdvanceBulkOutRing(UsbDeviceInfo& dev) {
dev.BulkOutRingEnqueue++;
if (dev.BulkOutRingEnqueue >= XFER_RING_SIZE - 1) {
TRB& link = dev.BulkOutRing[XFER_RING_SIZE - 1];
if (dev.BulkOutRingCCS) {
link.Control |= TRB_CYCLE_BIT;
} else {
link.Control &= ~TRB_CYCLE_BIT;
}
dev.BulkOutRingCCS = !dev.BulkOutRingCCS;
dev.BulkOutRingEnqueue = 0;
}
}
// -------------------------------------------------------------------------
// Forward declarations
// -------------------------------------------------------------------------
@@ -279,37 +316,62 @@ namespace Drivers::USB::Xhci {
g_xferCompletionCode = completionCode;
g_xferCompleted = true;
} else if (slotId > 0 && slotId <= MAX_SLOTS && g_devices[slotId].Active) {
// Interrupt IN endpoint completion
UsbDeviceInfo& dev = g_devices[slotId];
if (completionCode == CC_SUCCESS || completionCode == CC_SHORT_PACKET) {
uint16_t len = dev.InterruptMaxPacket;
// Residual byte count: bits 23:0 of Status gives residual
// Compute actual transfer length from residual
uint32_t residual = evt.Status & 0x00FFFFFF;
if (residual < len) {
len = dev.InterruptMaxPacket - (uint16_t)residual;
}
// Dispatch to HID driver based on interface protocol
if (dev.InterfaceClass == UsbDevice::CLASS_HID) {
if (dev.InterfaceProtocol == UsbDevice::PROTOCOL_KEYBOARD) {
HidKeyboard::ProcessReport(g_interruptDataBuf[slotId], len);
} else if (dev.InterfaceProtocol == UsbDevice::PROTOCOL_MOUSE) {
HidMouse::ProcessReport(g_interruptDataBuf[slotId], len);
// Check if this is a bulk IN endpoint completion
uint8_t bulkInDci = dev.BulkInEpNum ? (dev.BulkInEpNum * 2 + 1) : 0;
uint8_t bulkOutDci = dev.BulkOutEpNum ? (dev.BulkOutEpNum * 2) : 0;
uint8_t intDci = dev.InterruptEpNum ? (dev.InterruptEpNum * 2 + 1) : 0;
if (epDci == bulkInDci && g_transferCallbacks[slotId]) {
// Bulk IN — dispatch via registered callback
uint16_t len = dev.BulkInMaxPacket;
if (residual < len) len = dev.BulkInMaxPacket - (uint16_t)residual;
g_transferCallbacks[slotId](slotId, epDci,
g_bulkInDataBuf[slotId], len, completionCode);
} else if (epDci == bulkOutDci && g_transferCallbacks[slotId]) {
// Bulk OUT completion — notify callback
g_transferCallbacks[slotId](slotId, epDci,
nullptr, 0, completionCode);
} else if (epDci == intDci) {
// Interrupt IN — HID or callback dispatch
uint16_t len = dev.InterruptMaxPacket;
if (residual < len) len = dev.InterruptMaxPacket - (uint16_t)residual;
if (dev.InterfaceClass == UsbDevice::CLASS_HID) {
if (dev.InterfaceProtocol == UsbDevice::PROTOCOL_KEYBOARD) {
HidKeyboard::ProcessReport(g_interruptDataBuf[slotId], len);
} else if (dev.InterfaceProtocol == UsbDevice::PROTOCOL_MOUSE) {
HidMouse::ProcessReport(g_interruptDataBuf[slotId], len);
}
// Re-queue for next HID report
QueueInterruptTransfer(slotId);
} else if (g_transferCallbacks[slotId]) {
// Callback is responsible for re-queuing
g_transferCallbacks[slotId](slotId, epDci,
g_interruptDataBuf[slotId], len, completionCode);
}
} else if (g_transferCallbacks[slotId]) {
// Unknown endpoint — try callback
g_transferCallbacks[slotId](slotId, epDci,
nullptr, 0, completionCode);
}
} else {
KernelLogStream(WARNING, "xHCI") << "Transfer error on slot "
<< base::dec << (uint64_t)slotId << " ep " << (uint64_t)epDci
<< " cc=" << (uint64_t)completionCode;
// Notify callback of errors too
if (g_transferCallbacks[slotId]) {
g_transferCallbacks[slotId](slotId, epDci,
nullptr, 0, completionCode);
}
}
// Always re-queue the next interrupt transfer so the
// device can continue delivering reports. Transient
// errors (stall, babble, etc.) must not permanently
// kill the transfer chain.
QueueInterruptTransfer(slotId);
}
break;
}
@@ -495,6 +557,31 @@ namespace Drivers::USB::Xhci {
}
KernelLogStream(WARNING, "xHCI") << "Control transfer timeout on slot " << base::dec << (uint64_t)slotId;
// Recover EP0 ring: Stop Endpoint, then Set TR Dequeue Pointer
// so that subsequent control transfers on this slot still work.
TRB stopTrb = {};
stopTrb.Control = (TRB_STOP_ENDPOINT << TRB_TYPE_SHIFT)
| ((uint32_t)slotId << 24)
| (1 << 16); // DCI=1 (EP0) in bits 20:16
SendCommand(stopTrb);
// Reset the enqueue pointer to the start and set the dequeue pointer
// to match so the ring is back in sync.
uint64_t newDeq = dev.EP0RingPhys
+ (uint64_t)dev.EP0RingEnqueue * sizeof(TRB);
if (dev.EP0RingCCS) {
newDeq |= 1; // DCS bit
}
TRB deqTrb = {};
deqTrb.Parameter0 = (uint32_t)(newDeq & 0xFFFFFFFF);
deqTrb.Parameter1 = (uint32_t)(newDeq >> 32);
deqTrb.Control = (TRB_SET_TR_DEQUEUE << TRB_TYPE_SHIFT)
| ((uint32_t)slotId << 24)
| (1 << 16); // DCI=1 (EP0)
SendCommand(deqTrb);
return 0xFF;
}
@@ -534,6 +621,81 @@ namespace Drivers::USB::Xhci {
WriteDoorbell(slotId, target);
}
// -------------------------------------------------------------------------
// QueueBulkInTransfer
// -------------------------------------------------------------------------
void QueueBulkInTransfer(uint8_t slotId, uint8_t* data, uint64_t dataPhys, uint32_t length) {
if (slotId == 0 || slotId > MAX_SLOTS || !g_devices[slotId].Active) return;
UsbDeviceInfo& dev = g_devices[slotId];
if (!dev.BulkInRing || dev.BulkInEpNum == 0) return;
// If caller provides nullptr, use the per-slot bulk IN DMA buffer
if (data == nullptr) {
if (g_bulkInDataBuf[slotId] == nullptr) {
g_bulkInDataBuf[slotId] = AllocateDmaBuffer(g_bulkInDataBufPhys[slotId]);
}
data = g_bulkInDataBuf[slotId];
dataPhys = g_bulkInDataBufPhys[slotId];
}
TRB& trb = dev.BulkInRing[dev.BulkInRingEnqueue];
trb.Parameter0 = (uint32_t)(dataPhys & 0xFFFFFFFF);
trb.Parameter1 = (uint32_t)(dataPhys >> 32);
trb.Status = length;
uint32_t control = (TRB_NORMAL << TRB_TYPE_SHIFT) | TRB_IOC | TRB_ISP;
if (dev.BulkInRingCCS) {
control |= TRB_CYCLE_BIT;
}
trb.Control = control;
AdvanceBulkInRing(dev);
// Ring doorbell: DCI for bulk IN = EpNum * 2 + 1
uint8_t target = dev.BulkInEpNum * 2 + 1;
WriteDoorbell(slotId, target);
}
// -------------------------------------------------------------------------
// QueueBulkOutTransfer
// -------------------------------------------------------------------------
void QueueBulkOutTransfer(uint8_t slotId, uint8_t* data, uint64_t dataPhys, uint32_t length) {
if (slotId == 0 || slotId > MAX_SLOTS || !g_devices[slotId].Active) return;
UsbDeviceInfo& dev = g_devices[slotId];
if (!dev.BulkOutRing || dev.BulkOutEpNum == 0) return;
TRB& trb = dev.BulkOutRing[dev.BulkOutRingEnqueue];
trb.Parameter0 = (uint32_t)(dataPhys & 0xFFFFFFFF);
trb.Parameter1 = (uint32_t)(dataPhys >> 32);
trb.Status = length;
uint32_t control = (TRB_NORMAL << TRB_TYPE_SHIFT) | TRB_IOC;
if (dev.BulkOutRingCCS) {
control |= TRB_CYCLE_BIT;
}
trb.Control = control;
AdvanceBulkOutRing(dev);
// Ring doorbell: DCI for bulk OUT = EpNum * 2
uint8_t target = dev.BulkOutEpNum * 2;
WriteDoorbell(slotId, target);
}
// -------------------------------------------------------------------------
// RegisterTransferCallback
// -------------------------------------------------------------------------
void RegisterTransferCallback(uint8_t slotId, TransferCallback cb) {
if (slotId > 0 && slotId <= MAX_SLOTS) {
g_transferCallbacks[slotId] = cb;
}
}
// -------------------------------------------------------------------------
// RingDoorbell
// -------------------------------------------------------------------------