diff --git a/sys/voyageurs/xhci/control_command.h b/sys/voyageurs/xhci/control_command.h new file mode 100644 index 0000000..d615e42 --- /dev/null +++ b/sys/voyageurs/xhci/control_command.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include + +#include "xhci/descriptors.h" +#include "xhci/xhci.h" + +template +class ReadControlCommand { + public: + ReadControlCommand() { + output_mem_ = + mmth::OwnedMemoryRegion::ContiguousPhysical(0x1000, &output_phys_); + } + + XhciTrb SetupTrb() { + uint64_t request_type = RequestConstants::RequestType(); + uint64_t request = RequestConstants::Request() << 8; + uint64_t value = RequestConstants::Value() << 16; + // TODO: May need to be non-0 for string descriptors. + uint64_t index = (uint64_t)0 << 32; + uint64_t length = sizeof(Output) << 48; + return { + .parameter = request_type | request | value | index | length, + .status = 8, + .type_and_cycle = 1 | (1 << 5) | (1 << 6) | (2 << 10), + // IN Data Stage + .control = 3, + }; + } + + XhciTrb DataTrb() { + return { + .parameter = output_phys_, + .status = sizeof(Output), + .type_and_cycle = 1 | (1 << 5) | (3 << 10), + .control = 1, + }; + } + + XhciTrb StatusTrb() { + return { + .parameter = 0, + .status = 0, + .type_and_cycle = 1 | (1 << 5) | (4 << 10), + .control = 0, + }; + } + + Output* AwaitResult() { + if (!is_complete_) { + semaphore_->Wait(); + } + is_complete_ = true; + return reinterpret_cast(output_mem_.vaddr()); + } + + glcr::SharedPtr CompletionSemaphore() { return semaphore_; } + + private: + uint64_t output_phys_; + mmth::OwnedMemoryRegion output_mem_; + + bool is_complete_ = false; + glcr::SharedPtr semaphore_ = + glcr::MakeShared(); +}; diff --git a/sys/voyageurs/xhci/descriptors.h b/sys/voyageurs/xhci/descriptors.h new file mode 100644 index 0000000..f54eb7e --- /dev/null +++ b/sys/voyageurs/xhci/descriptors.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +template +class RequestConstants { + public: + static uint8_t RequestType(); + static uint8_t Request(); + static uint16_t Value(); +}; + +struct DeviceDescriptor { + uint8_t length; + uint8_t type; + uint16_t usb_spec; + uint8_t device_class; + uint8_t device_subclass; + uint8_t device_protocol; + uint8_t max_packet_size_exp; + uint16_t vendor_id; + uint16_t product_id; + uint16_t device_release; + uint8_t manufacturer_string_index; + uint8_t product_string_index; + uint8_t serial_string_index; + uint8_t num_configurations; +} __attribute__((packed)); + +template <> +class RequestConstants { + public: + static uint8_t RequestType() { return 0x80; } + static uint8_t Request() { return 0x6; }; + static uint16_t Value() { return 0x100; }; +}; diff --git a/sys/voyageurs/xhci/device_slot.cpp b/sys/voyageurs/xhci/device_slot.cpp index 35b8b87..e69d537 100644 --- a/sys/voyageurs/xhci/device_slot.cpp +++ b/sys/voyageurs/xhci/device_slot.cpp @@ -1,11 +1,15 @@ #include "xhci/device_slot.h" +#include +#include + #include "xhci/trb.h" -void DeviceSlot::EnableAndInitializeDataStructures(uint8_t slot_index, - uint64_t* output_context) { +void DeviceSlot::EnableAndInitializeDataStructures( + uint8_t slot_index, uint64_t* output_context, volatile uint32_t* doorbell) { enabled_ = true; slot_index_ = slot_index; + doorbell_ = doorbell; context_memory_ = mmth::OwnedMemoryRegion::ContiguousPhysical(0x1000, &context_phys_); @@ -45,3 +49,17 @@ XhciTrb DeviceSlot::CreateAddressDeviceCommand(uint8_t root_port, uint8_t DeviceSlot::State() { return device_context_->slot_context.address_and_state >> 27; } + +void DeviceSlot::TransferComplete(uint8_t endpoint_index, uint64_t trb_phys) { + if (endpoint_index != 1) { + crash("Transfer complete on non control endpoint", glcr::UNIMPLEMENTED); + } + + if (!control_completion_sempahores_.Contains(trb_phys)) { + // Skip this if we don't have a semaphore for it (Setup and Transfer Trbs). + return; + } + + control_completion_sempahores_.at(trb_phys)->Signal(); + check(control_completion_sempahores_.Delete(trb_phys)); +} diff --git a/sys/voyageurs/xhci/device_slot.h b/sys/voyageurs/xhci/device_slot.h index d014021..749b243 100644 --- a/sys/voyageurs/xhci/device_slot.h +++ b/sys/voyageurs/xhci/device_slot.h @@ -1,8 +1,13 @@ #pragma once +#include +#include #include +#include +#include #include +#include "xhci/control_command.h" #include "xhci/trb_ring.h" #include "xhci/xhci.h" @@ -13,17 +18,25 @@ class DeviceSlot { DeviceSlot(DeviceSlot&&) = delete; void EnableAndInitializeDataStructures(uint8_t slot_index_, - uint64_t* output_context); + uint64_t* output_context, + volatile uint32_t* doorbell); XhciTrb CreateAddressDeviceCommand(uint8_t root_port, uint32_t route_string, uint16_t max_packet_size); + // Caller must keep the command in scope until it completes. + template + void ExecuteReadControlCommand(ReadControlCommand& command); + + void TransferComplete(uint8_t endpoint_index, uint64_t trb_phys); + uint8_t State(); private: bool enabled_ = false; uint8_t slot_index_ = 0; + volatile uint32_t* doorbell_ = nullptr; uint64_t context_phys_ = 0; mmth::OwnedMemoryRegion context_memory_; @@ -34,4 +47,19 @@ class DeviceSlot { XhciInputContext* input_context_; glcr::UniquePtr control_endpoint_transfer_trb_; + glcr::HashMap> + control_completion_sempahores_; }; + +template +void DeviceSlot::ExecuteReadControlCommand(ReadControlCommand& command) { + control_endpoint_transfer_trb_->EnqueueTrb(command.SetupTrb()); + control_endpoint_transfer_trb_->EnqueueTrb(command.DataTrb()); + uint64_t last_phys = + control_endpoint_transfer_trb_->EnqueueTrb(command.StatusTrb()); + // Ring the control endpoint doorbell. + *doorbell_ = 1; + + check(control_completion_sempahores_.Insert(last_phys, + command.CompletionSemaphore())); +} diff --git a/sys/voyageurs/xhci/trb.h b/sys/voyageurs/xhci/trb.h index c02286c..a02982a 100644 --- a/sys/voyageurs/xhci/trb.h +++ b/sys/voyageurs/xhci/trb.h @@ -21,6 +21,7 @@ enum class TrbType : uint8_t { NoOpCommand = 23, // Events + Transfer = 32, CommandCompletion = 33, PortStatusChange = 34, }; diff --git a/sys/voyageurs/xhci/trb_ring.cpp b/sys/voyageurs/xhci/trb_ring.cpp index 8965b90..4189727 100644 --- a/sys/voyageurs/xhci/trb_ring.cpp +++ b/sys/voyageurs/xhci/trb_ring.cpp @@ -29,13 +29,14 @@ XhciTrb TrbRing::GetTrbFromPhysical(uint64_t address) { return trb_list_[offset]; } -void TrbRingWriter::EnqueueTrb(const XhciTrb& trb) { +uint64_t TrbRingWriter::EnqueueTrb(const XhciTrb& trb) { uint64_t ptr = enqueue_ptr_++; if (enqueue_ptr_ == trb_list_.size()) { crash("Not implemented: queue wrapping", glcr::UNIMPLEMENTED); } trb_list_[ptr] = trb; + return phys_address_ + (ptr * sizeof(uint64_t)); } bool TrbRingReader::HasNext() { diff --git a/sys/voyageurs/xhci/trb_ring.h b/sys/voyageurs/xhci/trb_ring.h index 5c88ce1..c2d0668 100644 --- a/sys/voyageurs/xhci/trb_ring.h +++ b/sys/voyageurs/xhci/trb_ring.h @@ -20,7 +20,7 @@ class TrbRing { class TrbRingWriter : public TrbRing { public: - void EnqueueTrb(const XhciTrb& trb); + uint64_t EnqueueTrb(const XhciTrb& trb); private: uint64_t enqueue_ptr_ = 0; diff --git a/sys/voyageurs/xhci/xhci_driver.cpp b/sys/voyageurs/xhci/xhci_driver.cpp index ff0a11d..8f21eaf 100644 --- a/sys/voyageurs/xhci/xhci_driver.cpp +++ b/sys/voyageurs/xhci/xhci_driver.cpp @@ -1,9 +1,11 @@ #include "xhci/xhci_driver.h" +#include #include #include #include +#include "xhci/descriptors.h" #include "xhci/trb.h" #include "xhci/xhci.h" @@ -15,6 +17,22 @@ void interrupt_thread(void* void_driver) { crash("Driver returned from interrupt loop", glcr::INTERNAL); } +void configure_device(void* void_device_slot) { + DeviceSlot* device_slot = static_cast(void_device_slot); + + dbgln("Configuring device"); + + ReadControlCommand command; + + device_slot->ExecuteReadControlCommand(command); + + DeviceDescriptor* descriptor = command.AwaitResult(); + + dbgln("Descriptor Type {x}, ({x})", descriptor->type, descriptor->usb_spec); + dbgln("Device Class/Sub/Protocol: {x}/{x}/{x}", descriptor->device_class, + descriptor->device_subclass, descriptor->device_protocol); +} + glcr::ErrorOr> XhciDriver::InitiateDriver( yellowstone::YellowstoneClient& yellowstone) { yellowstone::XhciInfo info; @@ -43,6 +61,9 @@ void XhciDriver::InterruptLoop() { while (event_ring_.HasNext()) { XhciTrb trb = event_ring_.Read(); switch (GetType(trb)) { + case TrbType::Transfer: + HandleTransferCompletion(trb); + break; case TrbType::CommandCompletion: HandleCommandCompletion(trb); break; @@ -52,6 +73,7 @@ void XhciDriver::InterruptLoop() { command_ring_.EnqueueTrb(CreateEnableSlotTrb()); doorbells_->doorbell[0] = 0; break; + default: dbgln("Unknown TRB Type {x} received.", (uint8_t)GetType(trb)); break; @@ -132,6 +154,7 @@ glcr::ErrorCode XhciDriver::ParseMmioStructures() { doorbells_ = reinterpret_cast(mmio_regions_.vaddr() + capabilities_->doorbell_offset); + dbgln("Doorbells: {x}", (uint64_t)doorbells_); return glcr::OK; } @@ -265,6 +288,7 @@ void XhciDriver::HandleCommandCompletion( case TrbType::AddressDevice: dbgln("Device Addressed: {x}", slot); dbgln("State: {x}", devices_[slot - 1].State()); + Thread(configure_device, &devices_[slot - 1]); break; case TrbType::NoOpCommand: dbgln("No-op Command Completed"); @@ -275,10 +299,18 @@ void XhciDriver::HandleCommandCompletion( } } +void XhciDriver::HandleTransferCompletion(const XhciTrb& transfer_event_trb) { + uint8_t slot_id = transfer_event_trb.control >> 8; + uint8_t endpoint_id = transfer_event_trb.control & 0x1F; + uint64_t trb_phys = transfer_event_trb.parameter; + devices_[slot_id - 1].TransferComplete(endpoint_id, trb_phys); +} + void XhciDriver::InitializeSlot(uint8_t slot_index) { // TODO: Consider making this array one longer and ignore the first value. devices_[slot_index - 1].EnableAndInitializeDataStructures( - slot_index, &(device_context_base_array_[slot_index])); + slot_index, &(device_context_base_array_[slot_index]), + &doorbells_->doorbell[slot_index]); XhciPort* port = reinterpret_cast(reinterpret_cast(operational_) + 0x400 + (0x10 * (slot_index - 1))); diff --git a/sys/voyageurs/xhci/xhci_driver.h b/sys/voyageurs/xhci/xhci_driver.h index 8033f2b..3beb293 100644 --- a/sys/voyageurs/xhci/xhci_driver.h +++ b/sys/voyageurs/xhci/xhci_driver.h @@ -66,6 +66,7 @@ class XhciDriver { glcr::ErrorCode NoOpCommand(); void HandleCommandCompletion(const XhciTrb& command_completion_trb); + void HandleTransferCompletion(const XhciTrb& transfer_event_trb); void InitializeSlot(uint8_t slot_index); };