diff --git a/efi/OVMF_VARS.fd b/efi/OVMF_VARS.fd index 0fad986..8a6ed7b 100644 Binary files a/efi/OVMF_VARS.fd and b/efi/OVMF_VARS.fd differ diff --git a/kernel/src/drivers/usb/xhci/xhci_device.cpp b/kernel/src/drivers/usb/xhci/xhci_device.cpp new file mode 100644 index 0000000..46015f1 --- /dev/null +++ b/kernel/src/drivers/usb/xhci/xhci_device.cpp @@ -0,0 +1,81 @@ +#include "xhci_device.h" + +uint16_t getInitialPacketSizeFromPortSpeed(uint8_t speed) { + // Calculate initial max packet size for the set device command + uint16_t initialMaxPacketSize = 0; + switch (speed) { + case XHCI_USB_SPEED_LOW_SPEED: initialMaxPacketSize = 8; break; + + case XHCI_USB_SPEED_FULL_SPEED: + case XHCI_USB_SPEED_HIGH_SPEED: initialMaxPacketSize = 64; break; + + case XHCI_USB_SPEED_SUPER_SPEED: + case XHCI_USB_SPEED_SUPER_SPEED_PLUS: + default: initialMaxPacketSize = 512; break; + } + + return initialMaxPacketSize; +} + +XhciDevice::XhciDevice(XhciHcContext* xhc) { + _allocInputContext(xhc); +} + +XhciDevice::XhciDevice(XhciHcContext* xhc, uint8_t port) : port(port) { + _allocInputContext(xhc); +} + +void XhciDevice::setupTransferRing() { + controlEpTransferRing = XhciTransferRing::allocate(slotId); +} + +void XhciDevice::setupAddressDeviceCtx(uint8_t portSpeed) { + auto& inputControlCtx = inputContext32.virtualBase->controlContext; + auto& inputDeviceCtx = inputContext32.virtualBase->deviceContext; + + // Setup the input control context + inputControlCtx.addFlags = (1 << 0) | (1 << 1); + inputControlCtx.dropFlags = 0; + + // Setup the slot context + auto& slotContext = inputDeviceCtx.slotContext; + slotContext.contextEntries = 1; + slotContext.speed = portSpeed; + slotContext.rootHubPortNum = port; + slotContext.routeString = 0; + slotContext.interrupterTarget = 0; + + // Setup the control endpoint context + auto& ctrlEpContext = inputDeviceCtx.controlEndpointContext; + ctrlEpContext.endpointState = XHCI_ENDPOINT_STATE_DISABLED; + ctrlEpContext.endpointType = XHCI_ENDPOINT_TYPE_CONTROL; + ctrlEpContext.interval = 0; + ctrlEpContext.errorCount = 3; + ctrlEpContext.maxPacketSize = getInitialPacketSizeFromPortSpeed(portSpeed); + ctrlEpContext.maxEsitPayloadLo = 0; + ctrlEpContext.maxEsitPayloadHi = 0; + ctrlEpContext.averageTrbLength = 8; + ctrlEpContext.transferRingDequeuePtr = controlEpTransferRing->getPhysicalBase(); + ctrlEpContext.dcs = 1; +} + +uint64_t XhciDevice::getInputContextPhysicalBase() { + // return inputContext64.physicalBase ? inputContext64.physicalBase : inputContext32.physicalBase; + return inputContext32.physicalBase; +} + +void XhciDevice::_allocInputContext(XhciHcContext* xhc) { + if (xhc->has64ByteContextSize()) { + inputContext64 = xhciAllocDma<XhciInputContext64>( + sizeof(XhciInputContext64), + XHCI_INPUT_CONTROL_CONTEXT_ALIGNMENT, + XHCI_INPUT_CONTROL_CONTEXT_BOUNDARY + ); + } else { + inputContext32 = xhciAllocDma<XhciInputContext32>( + sizeof(XhciInputContext32), + XHCI_INPUT_CONTROL_CONTEXT_ALIGNMENT, + XHCI_INPUT_CONTROL_CONTEXT_BOUNDARY + ); + } +} diff --git a/kernel/src/drivers/usb/xhci/xhci_device.h b/kernel/src/drivers/usb/xhci/xhci_device.h new file mode 100644 index 0000000..237ece7 --- /dev/null +++ b/kernel/src/drivers/usb/xhci/xhci_device.h @@ -0,0 +1,37 @@ +#ifndef XHCI_DEVICE_H +#define XHCI_DEVICE_H + +#include "xhci_device_ctx.h" +#include "xhci_ctx.h" + +class XhciDevice { +public: + XhciDevice(XhciHcContext* xhc); + XhciDevice(XhciHcContext* xhc, uint8_t port); + ~XhciDevice() = default; + + void setupTransferRing(); + void setupAddressDeviceCtx(uint8_t portSpeed); + + uint64_t getInputContextPhysicalBase(); + +public: + uint8_t port; + uint8_t slotId; + + // Pointer to the entry in DCBAA + XhciDma<XhciDeviceContext32> deviceContext32; + XhciDma<XhciDeviceContext64> deviceContext64; + + // Input context buffer for configuring the device + XhciDma<XhciInputContext32> inputContext32; + XhciDma<XhciInputContext64> inputContext64; + + // Control endpoint's transfer ring + kstl::SharedPtr<XhciTransferRing> controlEpTransferRing; + +private: + void _allocInputContext(XhciHcContext* xhc); +}; + +#endif diff --git a/kernel/src/drivers/usb/xhci/xhci_device_ctx.cpp b/kernel/src/drivers/usb/xhci/xhci_device_ctx.cpp index 8276d8c..d76d26d 100644 --- a/kernel/src/drivers/usb/xhci/xhci_device_ctx.cpp +++ b/kernel/src/drivers/usb/xhci/xhci_device_ctx.cpp @@ -1,9 +1,9 @@ #include "xhci_device_ctx.h" #include <kprint.h> -void XhciDeviceContextManager::allocateDcbaa(XhciHcContext* ctx) { - size_t contextEntrySize = ctx->has64ByteContextSize() ? 64 : 32; - size_t dcbaaSize = contextEntrySize * (ctx->getMaxDeviceSlots() + 1); +void XhciDeviceContextManager::allocateDcbaa(XhciHcContext* xhc) { + size_t contextEntrySize = xhc->has64ByteContextSize() ? 64 : 32; + size_t dcbaaSize = contextEntrySize * (xhc->getMaxDeviceSlots() + 1); m_dcbaa = xhciAllocDma<uint64_t>(dcbaaSize, XHCI_DEVICE_CONTEXT_ALIGNMENT, XHCI_DEVICE_CONTEXT_BOUNDARY); @@ -18,7 +18,7 @@ void XhciDeviceContextManager::allocateDcbaa(XhciHcContext* ctx) { */ // Initialize scratchpad buffer array if needed - uint8_t scratchpadBuffers = ctx->getMaxScratchpadBuffers(); + uint8_t scratchpadBuffers = xhc->getMaxScratchpadBuffers(); if (scratchpadBuffers > 0) { // Array of uint64_t pointers XhciDma<uint64_t> scratchpadArray = xhciAllocDma<uint64_t>(scratchpadBuffers * sizeof(uint64_t)); @@ -34,10 +34,26 @@ void XhciDeviceContextManager::allocateDcbaa(XhciHcContext* ctx) { } // Update the DCBAAP entry in operational registers - ctx->opRegs->dcbaap = m_dcbaa.physicalBase; + xhc->opRegs->dcbaap = m_dcbaa.physicalBase; } -void* XhciDeviceContextManager::allocateDeviceContext(uint8_t slot) const { - (void)slot; - return nullptr; +void XhciDeviceContextManager::allocateDeviceContext(XhciHcContext* xhc, uint8_t slot) const { + // Allocate a memory block for the device context + if (xhc->has64ByteContextSize()) { + auto ctx = xhciAllocDma<XhciDeviceContext64>( + sizeof(XhciDeviceContext64), + XHCI_DEVICE_CONTEXT_ALIGNMENT, + XHCI_DEVICE_CONTEXT_BOUNDARY + ); + + m_dcbaa.virtualBase[slot] = ctx.physicalBase; + } else { + auto ctx = xhciAllocDma<XhciDeviceContext32>( + sizeof(XhciDeviceContext32), + XHCI_DEVICE_CONTEXT_ALIGNMENT, + XHCI_DEVICE_CONTEXT_BOUNDARY + ); + + m_dcbaa.virtualBase[slot] = ctx.physicalBase; + } } diff --git a/kernel/src/drivers/usb/xhci/xhci_device_ctx.h b/kernel/src/drivers/usb/xhci/xhci_device_ctx.h index eb81f75..f5ec8e3 100644 --- a/kernel/src/drivers/usb/xhci/xhci_device_ctx.h +++ b/kernel/src/drivers/usb/xhci/xhci_device_ctx.h @@ -485,14 +485,94 @@ static_assert(sizeof(XhciDeviceContext32) == 1024, "Size mismatch for 32-byte De using XhciDeviceContext64 = XhciDeviceContext<64>; static_assert(sizeof(XhciDeviceContext64) == 2048, "Size mismatch for 64-byte Device Context."); +/* +// xHci Sped Section 6.2.5.1 Figure 6-6: Input Control Context (page 461) + +The Input Control Context data structure defines which Device Context data +structures are affected by a command and the operations to be performed on +those contexts +*/ +template <size_t Size> +struct XhciInputControlContext { + /* + Drop Context flags (D2 - D31). These single bit fields identify which Device Context data + structures should be disabled by command. If set to ‘1’, the respective Endpoint Context shall + be disabled. If cleared to ‘0’, the Endpoint Context is ignored. + */ + uint32_t dropFlags; + + /* + Add Context flags (A0 - A31). These single bit fields identify which Device Context data + structures shall be evaluated and/or enabled by a command. If set to ‘1’, the respective Context + shall be evaluated. If cleared to ‘0’, the Context is ignored. + */ + uint32_t addFlags; + + uint32_t rsvd0[5]; + + /* + Configuration Value. If CIC = ‘1’, CIE = ‘1’, and this Input Context is associated with a Configure + Endpoint Command, then this field contains the value of the Standard Configuration Descriptor + bConfigurationValue field associated with the command, otherwise the this field shall be + cleared to ‘0’. + */ + uint8_t configValue; + + /* + Interface Number. If CIC = ‘1’, CIE = ‘1’, this Input Context is associated with a Configure + Endpoint Command, and the command was issued due to a SET_INTERFACE request, then this + field contains the value of the Standard Interface Descriptor bInterfaceNumber field associated + with the command, otherwise the this field shall be cleared to ‘0’. + */ + uint8_t interfaceNumber; + + /* + Alternate Setting. If CIC = ‘1’, CIE = ‘1’, this Input Context is associated with a Configure + Endpoint Command, and the command was issued due to a SET_INTERFACE request, then this + field contains the value of the Standard Interface Descriptor bAlternateSetting field associated + with the command, otherwise the this field shall be cleared to ‘0’ + */ + uint8_t alternateSetting; + + // Reserved and zero'd + uint8_t rsvd1; + + // Reserved fields depending on context size + uint32_t rsvd2[Size == 64 ? 8 : 0]; // 8 reserved for 64-byte context, none for 32-byte +} __attribute__((packed)); + +using XhciInputControlContext32 = XhciInputControlContext<32>; +static_assert(sizeof(XhciInputControlContext32) == 32, "Size mismatch for 32-byte Input Control Context."); + +using XhciInputControlContext64 = XhciInputControlContext<64>; +static_assert(sizeof(XhciInputControlContext64) == 64, "Size mismatch for 64-byte Input Control Context."); + +template <size_t Size> +struct XhciInputContext { + XhciInputControlContext<Size> controlContext; + XhciDeviceContext<Size> deviceContext; +}; + +using XhciInputContext32 = XhciInputContext<32>; +static_assert(sizeof(XhciInputContext32) == 1056, "Size mismatch for 32-byte Input Context."); + +using XhciInputContext64 = XhciInputContext<64>; +static_assert(sizeof(XhciInputContext64) == 2112, "Size mismatch for 64-byte Input Context."); + class XhciDeviceContextManager { public: XhciDeviceContextManager() = default; ~XhciDeviceContextManager() = default; - void allocateDcbaa(XhciHcContext* ctx); + void allocateDcbaa(XhciHcContext* xhc); + + // Returns a physical address + void allocateDeviceContext(XhciHcContext* xhc, uint8_t slot) const; - void* allocateDeviceContext(uint8_t slot) const; + template <size_t Size> + inline XhciDeviceContext<Size>* getDeviceContext(uint8_t slot) { + return reinterpret_cast<XhciDeviceContext<Size>*>(m_dcbaa.virtualBase[slot]); + } private: XhciDma<uint64_t> m_dcbaa; diff --git a/kernel/src/drivers/usb/xhci/xhci_hcd.cpp b/kernel/src/drivers/usb/xhci/xhci_hcd.cpp index 68bc686..b43ec72 100644 --- a/kernel/src/drivers/usb/xhci/xhci_hcd.cpp +++ b/kernel/src/drivers/usb/xhci/xhci_hcd.cpp @@ -72,6 +72,102 @@ const char* trbCompletionCodeToString(uint8_t completionCode) { } } +void dumpDeviceContext32(const XhciDeviceContext32* context) { + // Dump Slot Context + kprint("\nSlot Context:\n"); + kprint(" Route String: 0x%x\n", context->slotContext.routeString); + kprint(" Speed: 0x%x\n", context->slotContext.speed); + kprint(" Reserved: 0x%x\n", context->slotContext.rz); + kprint(" MTT: 0x%x\n", context->slotContext.mtt); + kprint(" Hub: 0x%x\n", context->slotContext.hub); + kprint(" Context Entries: 0x%x\n", context->slotContext.contextEntries); + kprint(" Max Exit Latency: 0x%x\n", context->slotContext.maxExitLatency); + kprint(" Root Hub Port Number: 0x%x\n", context->slotContext.rootHubPortNum); + kprint(" Port Count: 0x%x\n", context->slotContext.portCount); + kprint(" Parent Hub Slot ID: 0x%x\n", context->slotContext.parentHubSlotId); + kprint(" Parent Port Number: 0x%x\n", context->slotContext.parentPortNumber); + kprint(" TT Think Time: 0x%x\n", context->slotContext.ttThinkTime); + kprint(" Reserved0: 0x%x\n", context->slotContext.rsvd0); + kprint(" Interrupter Target: 0x%x\n", context->slotContext.interrupterTarget); + kprint(" Device Address: 0x%x\n", context->slotContext.deviceAddress); + kprint(" Reserved1: 0x%x\n", context->slotContext.rsvd1); + kprint(" Slot State: 0x%x\n", context->slotContext.slotState); + + // Reserved fields + kprint(" Reserved Fields (Slot Context): [ "); + for (int i = 0; i < 4; ++i) { + kprint("0x%x ", context->slotContext.rsvdZ[i]); + } + kprint("]\n"); + + // Dump Control Endpoint Context + kprint("\nControl Endpoint Context:\n"); + kprint(" Endpoint State: 0x%x\n", context->controlEndpointContext.endpointState); + kprint(" Mult: 0x%x\n", context->controlEndpointContext.mult); + kprint(" Max Primary Streams: 0x%x\n", context->controlEndpointContext.maxPrimaryStreams); + kprint(" Linear Stream Array: 0x%x\n", context->controlEndpointContext.linearStreamArray); + kprint(" Interval: 0x%x\n", context->controlEndpointContext.interval); + kprint(" Max ESIT Payload Hi: 0x%x\n", context->controlEndpointContext.maxEsitPayloadHi); + kprint(" Error Count: 0x%x\n", context->controlEndpointContext.errorCount); + kprint(" Endpoint Type: 0x%x\n", context->controlEndpointContext.endpointType); + kprint(" Host Initiate Disable: 0x%x\n", context->controlEndpointContext.hostInitiateDisable); + kprint(" Max Burst Size: 0x%x\n", context->controlEndpointContext.maxBurstSize); + kprint(" Max Packet Size: 0x%x\n", context->controlEndpointContext.maxPacketSize); + kprint(" Dequeue Cycle State: 0x%llx\n", context->controlEndpointContext.dcs); + kprint(" TR Dequeue Ptr: 0x%llx\n", context->controlEndpointContext.trDequeuePtrAddressBits); + kprint(" Average TRB Length: 0x%x\n", context->controlEndpointContext.averageTrbLength); + kprint(" Max ESIT Payload Lo: 0x%x\n", context->controlEndpointContext.maxEsitPayloadLo); + + // Reserved fields (Control Endpoint Context) + kprint(" Reserved Fields (Control Endpoint Context): [ "); + for (int i = 0; i < 0; ++i) { + kprint("0x%x ", context->controlEndpointContext.rsvd[i]); + } + kprint("]\n"); + + // Dump each Endpoint Context (ep[0] to ep[29]) + for (int epIndex = 0; epIndex < 30; ++epIndex) { + if (context->ep[epIndex].endpointState == XHCI_ENDPOINT_STATE_DISABLED) { + continue; + } + + kprint("\nEndpoint Context %i:\n", epIndex); + kprint(" Endpoint State: 0x%x\n", context->ep[epIndex].endpointState); + kprint(" Mult: 0x%x\n", context->ep[epIndex].mult); + kprint(" Max Primary Streams: 0x%x\n", context->ep[epIndex].maxPrimaryStreams); + kprint(" Linear Stream Array: 0x%x\n", context->ep[epIndex].linearStreamArray); + kprint(" Interval: 0x%x\n", context->ep[epIndex].interval); + kprint(" Max ESIT Payload Hi: 0x%x\n", context->ep[epIndex].maxEsitPayloadHi); + kprint(" Error Count: 0x%x\n", context->ep[epIndex].errorCount); + kprint(" Endpoint Type: 0x%x\n", context->ep[epIndex].endpointType); + kprint(" Host Initiate Disable: 0x%x\n", context->ep[epIndex].hostInitiateDisable); + kprint(" Max Burst Size: 0x%x\n", context->ep[epIndex].maxBurstSize); + kprint(" Max Packet Size: 0x%x\n", context->ep[epIndex].maxPacketSize); + kprint(" Dequeue Cycle State: 0x%llx\n", context->ep[epIndex].dcs); + kprint(" TR Dequeue Ptr: 0x%llx\n", context->ep[epIndex].trDequeuePtrAddressBits); + kprint(" Average TRB Length: 0x%x\n", context->ep[epIndex].averageTrbLength); + kprint(" Max ESIT Payload Lo: 0x%x\n", context->ep[epIndex].maxEsitPayloadLo); + } +} + +void dumpXhciInputContext32(const XhciInputContext32* context) { + // Dump Input Control Context + kprint("Input Control Context:\n"); + kprint(" Drop Flags: 0x%x\n", context->controlContext.dropFlags); + kprint(" Add Flags: 0x%x\n", context->controlContext.addFlags); + kprint(" Reserved0: [ "); + for (int i = 0; i < 5; ++i) { + kprint("0x%x ", context->controlContext.rsvd0[i]); + } + kprint("]\n"); + kprint(" Config Value: 0x%x\n", context->controlContext.configValue); + kprint(" Interface Number: 0x%x\n", context->controlContext.interfaceNumber); + kprint(" Alternate Setting: 0x%x\n", context->controlContext.alternateSetting); + kprint(" Reserved1: 0x%x\n", context->controlContext.rsvd1); + + dumpDeviceContext32(&context->deviceContext); +} + void XhciHcd::init(PciDeviceInfo* deviceInfo) { uint64_t xhcBase = xhciMapMmio(deviceInfo->barAddress); @@ -125,7 +221,9 @@ void XhciHcd::init(PciDeviceInfo* deviceInfo) { portRegisterSet.readPortscReg(portsc); if (portsc.ccs) { - _setupDevice(port); + // Port number has to be 1-indexed + // in the device setup routine. + _setupDevice(port + 1); // For debugging purposes break; @@ -389,7 +487,8 @@ void XhciHcd::_setupDevice(uint8_t port) { kprintInfo("Port State Change Event on port %i: ", port); kprint("%s device ATTACHED with speed ", m_ctx->isPortUsb3(port) ? "USB3" : "USB2"); - switch (portsc.portSpeed) { + uint8_t portSpeed = portsc.portSpeed; + switch (portSpeed) { case XHCI_USB_SPEED_FULL_SPEED: kprint("Full Speed (12 MB/s - USB2.0)\n"); break; case XHCI_USB_SPEED_LOW_SPEED: kprint("Low Speed (1.5 Mb/s - USB 2.0)\n"); break; case XHCI_USB_SPEED_HIGH_SPEED: kprint("High Speed (480 Mb/s - USB 2.0)\n"); break; @@ -397,4 +496,55 @@ void XhciHcd::_setupDevice(uint8_t port) { case XHCI_USB_SPEED_SUPER_SPEED_PLUS: kprint("Super Speed Plus (10 Gb/s - USB 3.1)\n"); break; default: kprint("Undefined\n"); break; } + + auto device = new XhciDevice(m_ctx.get(), port); + device->slotId = _enableSlot(); + if (!device->slotId) { + kprint("[XHCI] Failed to allocate a slot for device on port %i\n", port); + return; + } + + device->setupTransferRing(); + device->setupAddressDeviceCtx(portSpeed); + + // Allocate the output device context entry in the DCBAA slot + m_deviceContextManager->allocateDeviceContext(m_ctx.get(), device->slotId); + + // Send the Address Device command + if (!_addressDevice(device)) { + return; + } +} + +uint8_t XhciHcd::_enableSlot() { + XhciTrb_t enableSlotTrb = XHCI_CONSTRUCT_CMD_TRB(XHCI_TRB_TYPE_ENABLE_SLOT_CMD); + auto completionTrb = sendCommand(&enableSlotTrb); + + if (!completionTrb) { + return 0; + } + + return completionTrb->slotId; +} + +bool XhciHcd::_addressDevice(XhciDevice* device) { + // Construct the Address Device TRB + XhciAddressDeviceCommandTrb_t addressDeviceTrb; + zeromem(&addressDeviceTrb, sizeof(XhciAddressDeviceCommandTrb_t)); + addressDeviceTrb.trbType = XHCI_TRB_TYPE_ADDRESS_DEVICE_CMD; + addressDeviceTrb.inputContextPhysicalBase = device->getInputContextPhysicalBase(); + addressDeviceTrb.bsr = 0; + addressDeviceTrb.slotId = device->slotId; + + // Send the AddressDevice command + auto completionTrb = sendCommand((XhciTrb_t*)&addressDeviceTrb); + if (!completionTrb || completionTrb->completionCode != XHCI_TRB_COMPLETION_CODE_SUCCESS) { + kprintError("[XHCI] Failed to complete the first Device Address command\n"); + return false; + } + + kprintInfo("[*] Successfully issued the first Device Address command!\n"); + msleep(200); + + return true; } diff --git a/kernel/src/drivers/usb/xhci/xhci_hcd.h b/kernel/src/drivers/usb/xhci/xhci_hcd.h index 58cb0b3..f4be360 100644 --- a/kernel/src/drivers/usb/xhci/xhci_hcd.h +++ b/kernel/src/drivers/usb/xhci/xhci_hcd.h @@ -1,7 +1,7 @@ #ifndef XHCI_HCD_H #define XHCI_HCD_H -#include "xhci_device_ctx.h" +#include "xhci_device.h" // Forward declaration struct PciDeviceInfo; @@ -32,6 +32,9 @@ class XhciHcd { void _setupDevice(uint8_t port); + uint8_t _enableSlot(); + bool _addressDevice(XhciDevice* device); + private: kstl::SharedPtr<XhciHcContext> m_ctx; kstl::SharedPtr<XhciDeviceContextManager> m_deviceContextManager; diff --git a/kernel/src/drivers/usb/xhci/xhci_rings.cpp b/kernel/src/drivers/usb/xhci/xhci_rings.cpp index d0ce0cd..8aea972 100644 --- a/kernel/src/drivers/usb/xhci/xhci_rings.cpp +++ b/kernel/src/drivers/usb/xhci/xhci_rings.cpp @@ -129,3 +129,45 @@ XhciTrb_t* XhciEventRing::_dequeueTrb() { return ret; } + +kstl::SharedPtr<XhciTransferRing> XhciTransferRing::allocate(uint8_t slotId) { + return kstl::SharedPtr<XhciTransferRing>( + new XhciTransferRing(XHCI_TRANSFER_RING_TRB_COUNT, slotId) + ); +} + +XhciTransferRing::XhciTransferRing(size_t maxTrbs, uint8_t doorbellId) { + m_maxTrbCount = maxTrbs; + m_rcsBit = 1; + m_dequeuePtr = 0; + m_enqueuePtr = 0; + m_doorbellId = doorbellId; + + const uint64_t ringSize = maxTrbs * sizeof(XhciTrb_t); + + // Create the transfer ring memory block + m_trbs = xhciAllocDma<XhciTrb_t>( + ringSize, + XHCI_TRANSFER_RING_SEGMENTS_ALIGNMENT, + XHCI_TRANSFER_RING_SEGMENTS_BOUNDARY + ); + + // Set the last TRB as a link TRB to point back to the first TRB + m_trbs.virtualBase[m_maxTrbCount - 1].parameter = m_trbs.physicalBase; + m_trbs.virtualBase[m_maxTrbCount - 1].control = (XHCI_TRB_TYPE_LINK << XHCI_TRB_TYPE_SHIFT) | m_rcsBit; +} + +void XhciTransferRing::enqueue(XhciTrb_t* trb) { + // Adjust the TRB's cycle bit to the current DCS + trb->cycleBit = m_rcsBit; + + // Insert the TRB into the ring + m_trbs.virtualBase[m_enqueuePtr] = *trb; + + // Advance and possibly wrap the enqueue pointer if needed. + // maxTrbCount - 1 accounts for the LINK_TRB. + if (++m_enqueuePtr == m_maxTrbCount - 1) { + m_enqueuePtr = 0; + m_rcsBit = !m_rcsBit; + } +} diff --git a/kernel/src/drivers/usb/xhci/xhci_rings.h b/kernel/src/drivers/usb/xhci/xhci_rings.h index 39be6f3..fcd7b7f 100644 --- a/kernel/src/drivers/usb/xhci/xhci_rings.h +++ b/kernel/src/drivers/usb/xhci/xhci_rings.h @@ -71,4 +71,27 @@ class XhciEventRing { XhciTrb_t* _dequeueTrb(); }; +class XhciTransferRing { +public: + static kstl::SharedPtr<XhciTransferRing> allocate(uint8_t slotId); + + XhciTransferRing(size_t maxTrbs, uint8_t doorbellId); + ~XhciTransferRing() = default; + + inline XhciTrb_t* getVirtualBase() const { return m_trbs.virtualBase; } + inline uint64_t getPhysicalBase() const { return m_trbs.physicalBase; } + inline uint8_t getCycleBit() const { return m_rcsBit; } + inline uint8_t getDoorbellId() const { return m_doorbellId; } + + void enqueue(XhciTrb_t* trb); + +private: + size_t m_maxTrbCount; // Number of valid TRBs in the ring including the LINK_TRB + size_t m_dequeuePtr; // Transfer ring consumer dequeue pointer + size_t m_enqueuePtr; // Transfer ring producer enqueue pointer + XhciDma<XhciTrb_t> m_trbs; // Base address of the ring buffer + uint8_t m_rcsBit; // Dequeue cycle state + uint8_t m_doorbellId; // ID of the doorbell associated with the ring +}; + #endif