Skip to content

Commit

Permalink
added a skeleton for device setup routine
Browse files Browse the repository at this point in the history
  • Loading branch information
FlareCoding committed Sep 18, 2024
1 parent 7bc8678 commit cc10515
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 1 deletion.
Binary file modified efi/OVMF_VARS.fd
Binary file not shown.
5 changes: 5 additions & 0 deletions kernel/src/drivers/usb/xhci/xhci_ctx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ XhciHcContext::XhciHcContext(uint64_t xhcBase) {
new XhciExtendedCapability(headCapPtr)
);

// Construct a manager class instance for the doorbell register array
doorbellManager = kstl::SharedPtr<XhciDoorbellManager>(
new XhciDoorbellManager(xhcBase + capRegs->dboff)
);

// Allocate a command ring
commandRing = kstl::SharedPtr<XhciCommandRing>(
new XhciCommandRing(XHCI_COMMAND_RING_TRB_COUNT)
Expand Down
3 changes: 3 additions & 0 deletions kernel/src/drivers/usb/xhci/xhci_ctx.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ class XhciHcContext {

// Primary event ring
kstl::SharedPtr<XhciEventRing> eventRing;

// Doorbell register manager
kstl::SharedPtr<XhciDoorbellManager> doorbellManager;
};

#endif
148 changes: 148 additions & 0 deletions kernel/src/drivers/usb/xhci/xhci_hcd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,73 @@

#include "xhci_ext_cap.h"

const char* trbCompletionCodeToString(uint8_t completionCode) {
switch (completionCode) {
case XHCI_TRB_COMPLETION_CODE_INVALID:
return "INVALID";
case XHCI_TRB_COMPLETION_CODE_SUCCESS:
return "SUCCESS";
case XHCI_TRB_COMPLETION_CODE_DATA_BUFFER_ERROR:
return "DATA_BUFFER_ERROR";
case XHCI_TRB_COMPLETION_CODE_BABBLE_DETECTED_ERROR:
return "BABBLE_DETECTED_ERROR";
case XHCI_TRB_COMPLETION_CODE_USB_TRANSACTION_ERROR:
return "USB_TRANSACTION_ERROR";
case XHCI_TRB_COMPLETION_CODE_TRB_ERROR:
return "TRB_ERROR";
case XHCI_TRB_COMPLETION_CODE_STALL_ERROR:
return "STALL_ERROR";
case XHCI_TRB_COMPLETION_CODE_RESOURCE_ERROR:
return "RESOURCE_ERROR";
case XHCI_TRB_COMPLETION_CODE_BANDWIDTH_ERROR:
return "BANDWIDTH_ERROR";
case XHCI_TRB_COMPLETION_CODE_NO_SLOTS_AVAILABLE:
return "NO_SLOTS_AVAILABLE";
case XHCI_TRB_COMPLETION_CODE_INVALID_STREAM_TYPE:
return "INVALID_STREAM_TYPE";
case XHCI_TRB_COMPLETION_CODE_SLOT_NOT_ENABLED:
return "SLOT_NOT_ENABLED";
case XHCI_TRB_COMPLETION_CODE_ENDPOINT_NOT_ENABLED:
return "ENDPOINT_NOT_ENABLED";
case XHCI_TRB_COMPLETION_CODE_SHORT_PACKET:
return "SHORT_PACKET";
case XHCI_TRB_COMPLETION_CODE_RING_UNDERRUN:
return "RING_UNDERRUN";
case XHCI_TRB_COMPLETION_CODE_RING_OVERRUN:
return "RING_OVERRUN";
case XHCI_TRB_COMPLETION_CODE_VF_EVENT_RING_FULL:
return "VF_EVENT_RING_FULL";
case XHCI_TRB_COMPLETION_CODE_PARAMETER_ERROR:
return "PARAMETER_ERROR";
case XHCI_TRB_COMPLETION_CODE_BANDWIDTH_OVERRUN:
return "BANDWIDTH_OVERRUN";
case XHCI_TRB_COMPLETION_CODE_CONTEXT_STATE_ERROR:
return "CONTEXT_STATE_ERROR";
case XHCI_TRB_COMPLETION_CODE_NO_PING_RESPONSE:
return "NO_PING_RESPONSE";
case XHCI_TRB_COMPLETION_CODE_EVENT_RING_FULL:
return "EVENT_RING_FULL";
case XHCI_TRB_COMPLETION_CODE_INCOMPATIBLE_DEVICE:
return "INCOMPATIBLE_DEVICE";
case XHCI_TRB_COMPLETION_CODE_MISSED_SERVICE:
return "MISSED_SERVICE";
case XHCI_TRB_COMPLETION_CODE_COMMAND_RING_STOPPED:
return "COMMAND_RING_STOPPED";
case XHCI_TRB_COMPLETION_CODE_COMMAND_ABORTED:
return "COMMAND_ABORTED";
case XHCI_TRB_COMPLETION_CODE_STOPPED:
return "STOPPED";
case XHCI_TRB_COMPLETION_CODE_STOPPED_LENGTH_INVALID:
return "STOPPED_LENGTH_INVALID";
case XHCI_TRB_COMPLETION_CODE_STOPPED_SHORT_PACKET:
return "STOPPED_SHORT_PACKET";
case XHCI_TRB_COMPLETION_CODE_MAX_EXIT_LATENCY_ERROR:
return "MAX_EXIT_LATENCY_ERROR";
default:
return "UNKNOWN_COMPLETION_CODE";
}
}

void XhciHcd::init(PciDeviceInfo* deviceInfo) {
uint64_t xhcBase = xhciMapMmio(deviceInfo->barAddress);

Expand Down Expand Up @@ -43,6 +110,27 @@ void XhciHcd::init(PciDeviceInfo* deviceInfo) {

// Reset the ports
resetAllPorts();

// After port resets, there will be extreneous port state change events
// for ports with connected devices, but without CSC bit set, so we have
// to manually iterate the ports with connected devices and set them up.
m_ctx->eventRing->flushUnprocessedEvents();

// Clear the interrupt pending flags
clearIrqFlags(0);

for (uint8_t port = 0; port < m_ctx->getMaxPorts(); ++port) {
auto portRegisterSet = m_ctx->getPortRegisterSet(port);
XhciPortscRegister portsc;
portRegisterSet.readPortscReg(portsc);

if (portsc.ccs) {
_setupDevice(port);

// For debugging purposes
break;
}
}
}

bool XhciHcd::resetController() {
Expand Down Expand Up @@ -202,6 +290,48 @@ void XhciHcd::clearIrqFlags(uint8_t interrupter) {
m_ctx->opRegs->usbsts |= ~XHCI_USBSTS_EINT;
}

XhciCommandCompletionTrb_t* XhciHcd::sendCommand(XhciTrb_t* trb) {
// Small delay period between ringing the
// doorbell and polling the event ring.
const uint32_t commandDelay = 40;

// Enqueue the TRB
m_ctx->commandRing->enqueue(trb);

// Ring the command doorbell
m_ctx->doorbellManager->ringCommandDoorbell();

// Let the host controller process the command
msleep(commandDelay);

// Poll the event ring for the command completion event
kstl::vector<XhciTrb_t*> events;
if (m_ctx->eventRing->hasUnprocessedEvents()) {
m_ctx->eventRing->dequeueEvents(events);
clearIrqFlags(0);
}

XhciCommandCompletionTrb_t* completionTrb = nullptr;
for (size_t i = 0; i < events.size(); ++i) {
if (events[i]->trbType == XHCI_TRB_TYPE_CMD_COMPLETION_EVENT) {
completionTrb = reinterpret_cast<XhciCommandCompletionTrb_t*>(events[i]);
break;
}
}

if (!completionTrb) {
kprintError("[*] Failed to find completion TRB for command %i\n", trb->trbType);
return nullptr;
}

if (completionTrb->completionCode != XHCI_TRB_COMPLETION_CODE_SUCCESS) {
kprintError("[*] Command TRB failed with error: %s\n", trbCompletionCodeToString(completionTrb->completionCode));
return nullptr;
}

return completionTrb;
}

void XhciHcd::_logUsbsts() {
uint32_t status = m_ctx->opRegs->usbsts;
kprint("===== USBSTS =====\n");
Expand Down Expand Up @@ -250,3 +380,21 @@ void XhciHcd::_configureOperationalRegs() {
// Write the CRCR register
m_ctx->opRegs->crcr = m_ctx->commandRing->getPhysicalBase();
}

void XhciHcd::_setupDevice(uint8_t port) {
auto portRegisterSet = m_ctx->getPortRegisterSet(port);
XhciPortscRegister portsc;
portRegisterSet.readPortscReg(portsc);

kprintInfo("Port State Change Event on port %i: ", port);
kprint("%s device ATTACHED with speed ", m_ctx->isPortUsb3(port) ? "USB3" : "USB2");

switch (portsc.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;
case XHCI_USB_SPEED_SUPER_SPEED: kprint("Super Speed (5 Gb/s - USB3.0)\n"); break;
case XHCI_USB_SPEED_SUPER_SPEED_PLUS: kprint("Super Speed Plus (10 Gb/s - USB 3.1)\n"); break;
default: kprint("Undefined\n"); break;
}
}
6 changes: 5 additions & 1 deletion kernel/src/drivers/usb/xhci/xhci_hcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ struct PciDeviceInfo;

class XhciHcd {
public:
XhciHcd() = default;;
XhciHcd() = default;
~XhciHcd() = default;

void init(PciDeviceInfo* deviceInfo);
Expand All @@ -23,11 +23,15 @@ class XhciHcd {

void clearIrqFlags(uint8_t interrupter);

XhciCommandCompletionTrb_t* sendCommand(XhciTrb_t* trb);

private:
void _logUsbsts();
void _identifyUsb3Ports();
void _configureOperationalRegs();

void _setupDevice(uint8_t port);

private:
kstl::SharedPtr<XhciHcContext> m_ctx;
kstl::SharedPtr<XhciDeviceContextManager> m_deviceContextManager;
Expand Down
16 changes: 16 additions & 0 deletions kernel/src/drivers/usb/xhci/xhci_regs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ const char* xhciExtendedCapabilityToString(XhciExtendedCapabilityCode capid) {
return "Vendor Specific";
}

XhciDoorbellManager::XhciDoorbellManager(uint64_t base) {
m_doorbellRegisters = reinterpret_cast<XhciDoorbellRegister*>(base);
}

void XhciDoorbellManager::ringDoorbell(uint8_t doorbell, uint8_t target) {
m_doorbellRegisters[doorbell].raw = target;
}

void XhciDoorbellManager::ringCommandDoorbell() {
ringDoorbell(0, XHCI_DOORBELL_TARGET_COMMAND_RING);
}

void XhciDoorbellManager::ringControlEndpointDoorbell(uint8_t doorbell) {
ringDoorbell(doorbell, XHCI_DOORBELL_TARGET_CONTROL_EP_RING);
}

XhciExtendedCapability::XhciExtendedCapability(volatile uint32_t* capPtr) : m_base(capPtr) {
m_entry.raw = *m_base;
_readNextExtCaps();
Expand Down
50 changes: 50 additions & 0 deletions kernel/src/drivers/usb/xhci/xhci_regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,56 @@ struct XhciRuntimeRegisters {
volatile XhciInterrupterRegisters ir[1024]; // Interrupter Register Sets (offset 0020h to 8000h)
};

/*
// xHci Spec Section 5.6 Figure 5-29: Doorbell Register (page 394)
The Doorbell Array is organized as an array of up to 256 Doorbell Registers.
One 32-bit Doorbell Register is defined in the array for each Device Slot.
System software utilizes the Doorbell Register to notify the xHC that it has
Device Slot related work for the xHC to perform.
The number of Doorbell Registers implemented by a particular instantiation of a
host controller is documented in the Number of Device Slots (MaxSlots) field of
the HCSPARAMS1 register (section 5.3.3).
These registers are pointed to by the Doorbell Offset Register (DBOFF) in the
xHC Capability register space. The Doorbell Array base address shall be Dword
aligned and is calculated by adding the value in the DBOFF register (section
5.3.7) to “Base” (the base address of the xHCI Capability register address
space).
All registers are 32 bits in length. Software should read and write these
registers using only Dword accesses
Note: Software shall not write the Doorbell of an endpoint until after it has issued a
Configure Endpoint Command for the endpoint and received a successful
Command Completion Event.
*/
struct XhciDoorbellRegister {
union {
struct {
uint8_t dbTarget;
uint8_t rsvd;
uint16_t dbStreamId;
};

// Must be accessed using 32-bit dwords
uint32_t raw;
};
} __attribute__((packed));

class XhciDoorbellManager {
public:
XhciDoorbellManager(uint64_t base);

// TargeValue = 2 + (ZeroBasedEndpoint * 2) + (isOutEp ? 0 : 1)
void ringDoorbell(uint8_t doorbell, uint8_t target);

void ringCommandDoorbell();
void ringControlEndpointDoorbell(uint8_t doorbell);

private:
XhciDoorbellRegister* m_doorbellRegisters;
};

/*
// xHci Spec Section 7.0 Table 7-1: Format of xHCI Extended Capability Pointer Register
Expand Down

0 comments on commit cc10515

Please sign in to comment.