diff --git a/sys/voyageurs/voyageurs.cpp b/sys/voyageurs/voyageurs.cpp index 00e3d0f..660f2b5 100644 --- a/sys/voyageurs/voyageurs.cpp +++ b/sys/voyageurs/voyageurs.cpp @@ -15,7 +15,7 @@ uint64_t main(uint64_t init_port) { YellowstoneClient yellowstone(gInitEndpointCap); - ASSIGN_OR_RETURN(XhciDriver xhci, XhciDriver::InitiateDriver(yellowstone)); + ASSIGN_OR_RETURN(auto xhci, XhciDriver::InitiateDriver(yellowstone)); dbgln("Initializing PS/2 Driver."); KeyboardDriver driver; diff --git a/sys/voyageurs/xhci/trb.cpp b/sys/voyageurs/xhci/trb.cpp index f11c093..61323fe 100644 --- a/sys/voyageurs/xhci/trb.cpp +++ b/sys/voyageurs/xhci/trb.cpp @@ -1,10 +1,47 @@ #include "xhci/trb.h" +constexpr uint8_t kTrb_Normal = 1; +constexpr uint8_t kTrb_SetupStage = 2; +constexpr uint8_t kTrb_DataStage = 3; +constexpr uint8_t kTrb_StatusStage = 4; +constexpr uint8_t kTrb_Isoch = 5; +constexpr uint8_t kTrb_Link = 6; +constexpr uint8_t kTrb_EventData = 7; +constexpr uint8_t kTrb_NoOp = 8; +constexpr uint8_t kTrb_EnableSlot = 9; +constexpr uint8_t kTrb_DisableSlot = 10; +constexpr uint8_t kTrb_NoOpCommand = 23; + +constexpr uint8_t kTrb_TypeOffset = 10; + +constexpr uint8_t kTrb_Cycle = 1; + XhciTrb CreateLinkTrb(uint64_t physical_address) { return { .parameter = physical_address, .status = 0, - .type_and_cycle = 6 << 10, + .type_and_cycle = kTrb_Link << kTrb_TypeOffset, + .control = 0, + }; +} + +XhciTrb CreateEnableSlotTrb() { + return { + .parameter = 0, + .status = 0, + // FIXME: Accept Cycle Bit as a parameter. + .type_and_cycle = kTrb_EnableSlot << kTrb_TypeOffset | kTrb_Cycle, + // FIXME: Specify slot type if necessary. (XHCI Table 7-9)? + .control = 0, + }; +} + +XhciTrb CreateNoOpCommandTrb() { + return { + .parameter = 0, + .status = 0, + // FIXME: Accept Cycle Bit as a parameter. + .type_and_cycle = kTrb_NoOpCommand << kTrb_TypeOffset | kTrb_Cycle, .control = 0, }; } diff --git a/sys/voyageurs/xhci/trb.h b/sys/voyageurs/xhci/trb.h index 333f883..22abc22 100644 --- a/sys/voyageurs/xhci/trb.h +++ b/sys/voyageurs/xhci/trb.h @@ -3,3 +3,6 @@ #include "xhci/xhci.h" XhciTrb CreateLinkTrb(uint64_t physical_address); + +XhciTrb CreateEnableSlotTrb(); +XhciTrb CreateNoOpCommandTrb(); diff --git a/sys/voyageurs/xhci/trb_ring.cpp b/sys/voyageurs/xhci/trb_ring.cpp index 0e1eb66..c578412 100644 --- a/sys/voyageurs/xhci/trb_ring.cpp +++ b/sys/voyageurs/xhci/trb_ring.cpp @@ -7,6 +7,12 @@ TrbRing::TrbRing() { uint64_t number_trbs = 0x1000 / sizeof(XhciTrb); page_ = mmth::OwnedMemoryRegion::ContiguousPhysical(0x1000, &phys_address_); + + // Zero out data. + uint64_t* page_ptr = reinterpret_cast(page_.vaddr()); + for (uint64_t i = 0; i < 0x1000 / sizeof(uint64_t); i++) { + page_ptr[i] = 0; + } trb_list_ = glcr::ArrayView( reinterpret_cast(page_.vaddr()), number_trbs); @@ -22,3 +28,9 @@ void TrbRingWriter::EnqueueTrb(const XhciTrb& trb) { trb_list_[ptr] = trb; } + +bool TrbRingReader::HasNext() { + return (trb_list_[dequeue_ptr_].type_and_cycle & 0x1) == cycle_bit_; +} + +XhciTrb TrbRingReader::Read() { return trb_list_[dequeue_ptr_++]; } diff --git a/sys/voyageurs/xhci/trb_ring.h b/sys/voyageurs/xhci/trb_ring.h index 8682fdb..3d6c998 100644 --- a/sys/voyageurs/xhci/trb_ring.h +++ b/sys/voyageurs/xhci/trb_ring.h @@ -24,3 +24,15 @@ class TrbRingWriter : public TrbRing { private: uint64_t enqueue_ptr_ = 0; }; + +class TrbRingReader : public TrbRing { + public: + bool HasNext(); + XhciTrb Read(); + + uint64_t DequeuePtr() { return phys_address_ + dequeue_ptr_; } + + private: + uint64_t dequeue_ptr_ = 0; + uint8_t cycle_bit_ = 1; +}; diff --git a/sys/voyageurs/xhci/xhci.h b/sys/voyageurs/xhci/xhci.h index 8a18469..2593ff3 100644 --- a/sys/voyageurs/xhci/xhci.h +++ b/sys/voyageurs/xhci/xhci.h @@ -73,7 +73,10 @@ struct XhciRuntime { uint64_t reserved3; uint64_t reserved4; XhciInterrupter interrupters[1024]; +} __attribute__((packed)); +struct XhciDoorbells { + uint32_t doorbell[256]; } __attribute__((packed)); struct XhciPort { diff --git a/sys/voyageurs/xhci/xhci_driver.cpp b/sys/voyageurs/xhci/xhci_driver.cpp index f9a87b3..56babdf 100644 --- a/sys/voyageurs/xhci/xhci_driver.cpp +++ b/sys/voyageurs/xhci/xhci_driver.cpp @@ -1,29 +1,67 @@ #include "xhci/xhci_driver.h" -#include #include #include #include +#include "xhci/trb.h" #include "xhci/xhci.h" -glcr::ErrorOr XhciDriver::InitiateDriver( +void interrupt_thread(void* void_driver) { + XhciDriver* driver = static_cast(void_driver); + + driver->InterruptLoop(); + + crash("Driver returned from interrupt loop", glcr::INTERNAL); +} + +glcr::ErrorOr> XhciDriver::InitiateDriver( yellowstone::YellowstoneClient& yellowstone) { yellowstone::XhciInfo info; check(yellowstone.GetXhciInfo(info)); mmth::OwnedMemoryRegion pci_region = mmth::OwnedMemoryRegion::FromCapability(info.mutable_xhci_region()); - XhciDriver driver(glcr::Move(pci_region)); - driver.ParseMmioStructures(); - driver.DumpDebugInfo(); - driver.FreeExistingMemoryStructures(); - driver.ResetController(); + // Have to make this a heap object so that the reference passed to the + // interrupt loop remains valid. + glcr::UniquePtr driver(new XhciDriver(glcr::Move(pci_region))); + driver->ParseMmioStructures(); + driver->FreeExistingMemoryStructures(); + driver->ResetController(); + driver->StartInterruptThread(); dbgln("XHCI CONTROLLER RESET"); - driver.DumpDebugInfo(); - check(ZThreadSleep(100)); - driver.DumpDebugInfo(); - return driver; + driver->NoOpCommand(); + driver->InitiateDevices(); + return glcr::Move(driver); +} + +void XhciDriver::InterruptLoop() { + while (true) { + while ((runtime_->interrupters[0].management & 0x1) != 0x1) { + check(ZThreadSleep(50)); + } + while (event_ring_.HasNext()) { + XhciTrb trb = event_ring_.Read(); + uint16_t type = trb.type_and_cycle >> 10; + switch (type) { + case 33: + dbgln("Command Completion Event. {x}", trb.parameter); + break; + case 34: + dbgln("Port Status Change Event, enabling slot."); + command_ring_.EnqueueTrb(CreateEnableSlotTrb()); + doorbells_->doorbell[0] = 0; + break; + default: + dbgln("Unknown TRB Type {x} received.", type); + break; + } + } + + runtime_->interrupters[0].event_ring_dequeue_pointer = + event_ring_.DequeuePtr() | 0x8; + runtime_->interrupters[0].management |= 0x1; + } } void XhciDriver::DumpDebugInfo() { @@ -62,6 +100,7 @@ void XhciDriver::DumpDebugInfo() { if ((port->status_and_control & 0x3) == 0x1) { dbgln("Resetting: {x}", i); port->status_and_control |= 0x10; + doorbells_->doorbell[0] = 0; } } @@ -88,10 +127,22 @@ glcr::ErrorCode XhciDriver::ParseMmioStructures() { runtime_ = reinterpret_cast(mmio_regions_.vaddr() + capabilities_->runtime_offset); + doorbells_ = reinterpret_cast(mmio_regions_.vaddr() + + capabilities_->doorbell_offset); + return glcr::OK; } glcr::ErrorCode XhciDriver::ResetController() { + // Stop the Host Controller. + // FIXME: Do this before freeing existing structures. + operational_->usb_command &= ~0x1; + + while ((operational_->usb_status & 0x1) != 0x1) { + dbgln("Waiting XHCI Halt."); + RET_ERR(ZThreadSleep(50)); + } + // Host Controller Reset operational_->usb_command |= 0x2; @@ -115,8 +166,13 @@ glcr::ErrorCode XhciDriver::ResetController() { return glcr::OK; } +void XhciDriver::StartInterruptThread() { + interrupt_thread_ = Thread(interrupt_thread, this); +} + glcr::ErrorCode XhciDriver::InitiateCommandRing() { operational_->command_ring_control = command_ring_.PhysicalAddress(); + dbgln("CRC: {x}", operational_->command_ring_control); return glcr::OK; } @@ -155,11 +211,35 @@ glcr::ErrorCode XhciDriver::InitiateEventRingSegmentTable() { event_ring_.PhysicalAddress(); event_ring_segment_table_[0].ring_segment_size = ers_size & 0xFFFF; - runtime_->interrupters[0].event_ring_segment_table_base_address = erst_phys; - runtime_->interrupters[0].event_ring_dequeue_pointer = erst_phys; + runtime_->interrupters[0].event_ring_dequeue_pointer = + event_ring_.PhysicalAddress() | 0x8; runtime_->interrupters[0].event_ring_segment_table_size = 1; + runtime_->interrupters[0].event_ring_segment_table_base_address = erst_phys; + // Enable interrupts. runtime_->interrupters[0].management |= 0x2; + runtime_->interrupters[0].moderation = 4000; operational_->usb_command |= 0x4; return glcr::OK; } + +glcr::ErrorCode XhciDriver::InitiateDevices() { + uint64_t max_ports = (capabilities_->hcs_params_1 & 0xFF00'0000) >> 24; + for (uint64_t i = 0; i < max_ports; i++) { + XhciPort* port = reinterpret_cast( + reinterpret_cast(operational_) + 0x400 + (0x10 * i)); + port->status_and_control &= ~0x10000; + dbgln("Port {x}: {x}", i, port->status_and_control); + if ((port->status_and_control & 0x3) == 0x1) { + dbgln("Resetting: {x}", i); + port->status_and_control |= 0x10; + } + } + return glcr::OK; +} + +glcr::ErrorCode XhciDriver::NoOpCommand() { + command_ring_.EnqueueTrb(CreateNoOpCommandTrb()); + doorbells_->doorbell[0] = 0; + return glcr::OK; +} diff --git a/sys/voyageurs/xhci/xhci_driver.h b/sys/voyageurs/xhci/xhci_driver.h index 0d05b01..c4efede 100644 --- a/sys/voyageurs/xhci/xhci_driver.h +++ b/sys/voyageurs/xhci/xhci_driver.h @@ -1,6 +1,8 @@ #pragma once +#include #include +#include #include #include @@ -9,22 +11,27 @@ class XhciDriver { public: - static glcr::ErrorOr InitiateDriver( + static glcr::ErrorOr> InitiateDriver( yellowstone::YellowstoneClient& yellowstone); + XhciDriver(const XhciDriver&) = delete; + XhciDriver(XhciDriver&&) = default; + void DumpDebugInfo(); + void InterruptLoop(); + private: // MMIO Structures. mmth::OwnedMemoryRegion pci_region_; PciDeviceHeader* pci_device_header_; mmth::OwnedMemoryRegion mmio_regions_; - XhciCapabilities* capabilities_; - XhciOperational* operational_; + volatile XhciCapabilities* capabilities_; + volatile XhciOperational* operational_; // TODO: Extended Capabilities. - XhciRuntime* runtime_; - // TODO: Doorbell Array. + volatile XhciRuntime* runtime_; + volatile XhciDoorbells* doorbells_; // Host Memory Regions. TrbRingWriter command_ring_; @@ -35,7 +42,8 @@ class XhciDriver { mmth::OwnedMemoryRegion event_ring_segment_table_mem_; XhciEventRingSegmentTableEntry* event_ring_segment_table_; - TrbRing event_ring_; + TrbRingReader event_ring_; + Thread interrupt_thread_; XhciDriver(mmth::OwnedMemoryRegion&& pci_space); @@ -43,8 +51,13 @@ class XhciDriver { glcr::ErrorCode FreeExistingMemoryStructures() { return glcr::OK; } glcr::ErrorCode ResetController(); + void StartInterruptThread(); glcr::ErrorCode InitiateCommandRing(); glcr::ErrorCode InitiateDeviceContextBaseArray(); glcr::ErrorCode InitiateEventRingSegmentTable(); + + glcr::ErrorCode InitiateDevices(); + + glcr::ErrorCode NoOpCommand(); };