[Voyageurs] Send AddressDevice Command to move port to 'Addressed' State.

This commit is contained in:
Drew Galbraith 2024-02-22 18:16:08 -08:00
parent dd2687a59a
commit 8e78950ac7
10 changed files with 236 additions and 33 deletions

View File

@ -1,5 +1,6 @@
add_executable(voyageurs
keyboard/keyboard_driver.cpp
xhci/device_slot.cpp
xhci/trb.cpp
xhci/trb_ring.cpp
xhci/xhci_driver.cpp

View File

@ -0,0 +1,47 @@
#include "xhci/device_slot.h"
#include "xhci/trb.h"
void DeviceSlot::EnableAndInitializeDataStructures(uint8_t slot_index,
uint64_t* output_context) {
enabled_ = true;
slot_index_ = slot_index;
context_memory_ =
mmth::OwnedMemoryRegion::ContiguousPhysical(0x1000, &context_phys_);
device_context_ =
reinterpret_cast<XhciDeviceContext*>(context_memory_.vaddr());
*output_context = context_phys_;
input_context_ = reinterpret_cast<XhciInputContext*>(context_memory_.vaddr() +
kInputSlotContextOffset);
control_endpoint_transfer_trb_ = glcr::MakeUnique<TrbRingWriter>();
}
XhciTrb DeviceSlot::CreateAddressDeviceCommand(uint8_t root_port,
uint32_t route_string,
uint16_t max_packet_size) {
// Initialize Slot Context and Endpoint 0 Context.
input_context_->input.add_contexts = 0x3;
// Set context_entries to 1. XHCI 4.3.3
input_context_->slot_context.route_speed_entries = (0x1 << 27) | route_string;
input_context_->slot_context.latency_port_number = root_port << 16;
// Initialize Control Endpoint.
input_context_->endpoint_contexts[0].state = 0;
constexpr uint16_t kCerr = 0x3 << 1;
constexpr uint16_t kTypeControl = 0x4 << 3;
input_context_->endpoint_contexts[0].error_and_type =
kCerr | kTypeControl | (max_packet_size << 16);
input_context_->endpoint_contexts[0].tr_dequeue_ptr =
control_endpoint_transfer_trb_->PhysicalAddress() | 0x1;
return ::CreateAddressDeviceCommand(context_phys_ + kInputSlotContextOffset,
slot_index_);
}
uint8_t DeviceSlot::State() {
return device_context_->slot_context.address_and_state >> 27;
}

View File

@ -0,0 +1,37 @@
#pragma once
#include <glacier/memory/unique_ptr.h>
#include <mammoth/util/memory_region.h>
#include "xhci/trb_ring.h"
#include "xhci/xhci.h"
class DeviceSlot {
public:
DeviceSlot() = default;
DeviceSlot(const DeviceSlot&) = delete;
DeviceSlot(DeviceSlot&&) = delete;
void EnableAndInitializeDataStructures(uint8_t slot_index_,
uint64_t* output_context);
XhciTrb CreateAddressDeviceCommand(uint8_t root_port, uint32_t route_string,
uint16_t max_packet_size);
uint8_t State();
private:
bool enabled_ = false;
uint8_t slot_index_ = 0;
uint64_t context_phys_ = 0;
mmth::OwnedMemoryRegion context_memory_;
static constexpr uint64_t kInputSlotContextOffset = 0x400;
XhciDeviceContext* device_context_;
XhciInputContext* input_context_;
glcr::UniquePtr<TrbRingWriter> control_endpoint_transfer_trb_;
};

View File

@ -1,26 +1,27 @@
#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;
constexpr uint16_t kTrb_Cycle = 1;
constexpr uint16_t kTrb_BSR = (1 << 9);
namespace {
uint16_t TypeToInt(TrbType type) {
return static_cast<uint16_t>(type) << kTrb_TypeOffset;
}
} // namespace
TrbType GetType(const XhciTrb& trb) {
return TrbType(trb.type_and_cycle >> kTrb_TypeOffset);
}
XhciTrb CreateLinkTrb(uint64_t physical_address) {
return {
.parameter = physical_address,
.status = 0,
.type_and_cycle = kTrb_Link << kTrb_TypeOffset,
.type_and_cycle = TypeToInt(TrbType::Link),
.control = 0,
};
}
@ -30,18 +31,30 @@ XhciTrb CreateEnableSlotTrb() {
.parameter = 0,
.status = 0,
// FIXME: Accept Cycle Bit as a parameter.
.type_and_cycle = kTrb_EnableSlot << kTrb_TypeOffset | kTrb_Cycle,
.type_and_cycle = (uint16_t)(TypeToInt(TrbType::EnableSlot) | kTrb_Cycle),
// FIXME: Specify slot type if necessary. (XHCI Table 7-9)?
.control = 0,
};
}
XhciTrb CreateAddressDeviceCommand(uint64_t input_context, uint8_t slot_id) {
return {
.parameter = input_context,
.status = 0,
// Always cycle the device straight to addressed.
.type_and_cycle =
(uint16_t)(TypeToInt(TrbType::AddressDevice) | kTrb_Cycle),
.control = (uint16_t)(slot_id << 8),
};
}
XhciTrb CreateNoOpCommandTrb() {
return {
.parameter = 0,
.status = 0,
// FIXME: Accept Cycle Bit as a parameter.
.type_and_cycle = kTrb_NoOpCommand << kTrb_TypeOffset | kTrb_Cycle,
.type_and_cycle =
(uint16_t)(TypeToInt(TrbType::NoOpCommand) | kTrb_Cycle),
.control = 0,
};
}

View File

@ -2,7 +2,33 @@
#include "xhci/xhci.h"
enum class TrbType : uint8_t {
Reserved = 0,
// Transfers
Normal = 1,
SetupStage = 2,
DataStage = 3,
StatusStage = 4,
Isoch = 5,
Link = 6,
EventData = 7,
NoOp = 8,
// Commands
EnableSlot = 9,
AddressDevice = 11,
NoOpCommand = 23,
// Events
CommandCompletion = 33,
PortStatusChange = 34,
};
TrbType GetType(const XhciTrb& trb);
XhciTrb CreateLinkTrb(uint64_t physical_address);
XhciTrb CreateEnableSlotTrb();
XhciTrb CreateAddressDeviceCommand(uint64_t input_context, uint8_t slot_id);
XhciTrb CreateNoOpCommandTrb();

View File

@ -20,6 +20,15 @@ TrbRing::TrbRing() {
trb_list_[trb_list_.size() - 1] = CreateLinkTrb(phys_address_);
}
XhciTrb TrbRing::GetTrbFromPhysical(uint64_t address) {
uint64_t offset = address - phys_address_;
if (offset >= 0x1000) {
crash("Invalid offset in GetTrbFromPhysical", glcr::INVALID_ARGUMENT);
}
offset /= sizeof(XhciTrb);
return trb_list_[offset];
}
void TrbRingWriter::EnqueueTrb(const XhciTrb& trb) {
uint64_t ptr = enqueue_ptr_++;
if (enqueue_ptr_ == trb_list_.size()) {

View File

@ -10,6 +10,7 @@ class TrbRing {
TrbRing();
uint64_t PhysicalAddress() { return phys_address_; }
XhciTrb GetTrbFromPhysical(uint64_t address);
protected:
uint64_t phys_address_;

View File

@ -43,6 +43,13 @@ struct XhciCapabilities {
uint32_t capabilities2;
} __attribute__((packed));
struct XhciPort {
uint32_t status_and_control;
uint32_t power_management;
uint32_t link_info;
uint32_t lpm_control;
} __attribute__((packed));
struct XhciOperational {
uint32_t usb_command;
uint32_t usb_status;
@ -55,6 +62,7 @@ struct XhciOperational {
uint64_t reserved4;
uint64_t device_context_base;
uint32_t configure;
XhciPort ports[255];
} __attribute__((packed));
struct XhciInterrupter {
@ -79,13 +87,6 @@ struct XhciDoorbells {
uint32_t doorbell[256];
} __attribute__((packed));
struct XhciPort {
uint32_t status_and_control;
uint32_t power_management;
uint32_t link_info;
uint32_t lpm_control;
} __attribute__((packed));
struct XhciSlotContext {
uint32_t route_speed_entries;
uint32_t latency_port_number;
@ -109,6 +110,24 @@ struct XhciDeviceContext {
XhciEndpointContext endpoint_contexts[31];
} __attribute__((packed));
struct XhciInputControlContext {
uint32_t drop_contexts;
uint32_t add_contexts;
uint64_t reserved1;
uint64_t reserved2;
uint32_t reserved3;
uint8_t configuration_value;
uint8_t interface_number;
uint8_t alternate_setting;
uint8_t reserved4;
} __attribute__((packed));
struct XhciInputContext {
XhciInputControlContext input;
XhciSlotContext slot_context;
XhciEndpointContext endpoint_contexts[31];
} __attribute__((packed));
struct XhciTrb {
uint64_t parameter;
uint32_t status;

View File

@ -42,23 +42,18 @@ void XhciDriver::InterruptLoop() {
}
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. TRB Ptr: {x}, Status: {x}, Param: {x} "
"Slot ID: {x}",
trb.parameter, trb.status >> 24, trb.status & 0xFFFFFF,
trb.control >> 8);
switch (GetType(trb)) {
case TrbType::CommandCompletion:
HandleCommandCompletion(trb);
break;
case 34:
case TrbType::PortStatusChange:
dbgln("Port Status Change Event on Port {x}, enabling slot.",
((trb.parameter >> 24) & 0xFF) - 1);
command_ring_.EnqueueTrb(CreateEnableSlotTrb());
doorbells_->doorbell[0] = 0;
break;
default:
dbgln("Unknown TRB Type {x} received.", type);
dbgln("Unknown TRB Type {x} received.", (uint8_t)GetType(trb));
break;
}
}
@ -125,6 +120,9 @@ glcr::ErrorCode XhciDriver::ParseMmioStructures() {
capabilities_ = reinterpret_cast<XhciCapabilities*>(mmio_regions_.vaddr());
uint8_t max_device_slots = capabilities_->hcs_params_1 & 0xFF;
devices_ = glcr::Array<DeviceSlot>(max_device_slots);
uint64_t op_base =
mmio_regions_.vaddr() + (capabilities_->length_and_version & 0xFF);
operational_ = reinterpret_cast<XhciOperational*>(op_base);
@ -247,3 +245,47 @@ glcr::ErrorCode XhciDriver::NoOpCommand() {
doorbells_->doorbell[0] = 0;
return glcr::OK;
}
void XhciDriver::HandleCommandCompletion(
const XhciTrb& command_completion_trb) {
uint8_t status = command_completion_trb.status >> 24;
if (status != 1) {
dbgln("Command Completion Status: {x}", command_completion_trb.status);
check(glcr::INTERNAL);
}
XhciTrb orig_trb =
command_ring_.GetTrbFromPhysical(command_completion_trb.parameter);
uint8_t slot = command_completion_trb.control >> 8;
switch (GetType(orig_trb)) {
case TrbType::EnableSlot:
dbgln("Slot Enabled: {x}", slot);
InitializeSlot(slot);
break;
case TrbType::AddressDevice:
dbgln("Device Addressed: {x}", slot);
dbgln("State: {x}", devices_[slot - 1].State());
break;
case TrbType::NoOpCommand:
dbgln("No-op Command Completed");
break;
default:
dbgln("Unhandled Command Completion Type: {x}",
(uint8_t)(GetType(orig_trb)));
}
}
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]));
XhciPort* port =
reinterpret_cast<XhciPort*>(reinterpret_cast<uint64_t>(operational_) +
0x400 + (0x10 * (slot_index - 1)));
uint8_t port_speed = (port->status_and_control >> 10) & 0xF;
uint8_t max_packet_size = 8;
XhciTrb address_device = devices_[slot_index - 1].CreateAddressDeviceCommand(
0x5, 0, max_packet_size);
command_ring_.EnqueueTrb(address_device);
doorbells_->doorbell[0] = 0;
}

View File

@ -1,11 +1,13 @@
#pragma once
#include <glacier/container/array.h>
#include <glacier/memory/unique_ptr.h>
#include <glacier/status/error_or.h>
#include <mammoth/proc/thread.h>
#include <mammoth/util/memory_region.h>
#include <yellowstone/yellowstone.yunq.client.h>
#include "xhci/device_slot.h"
#include "xhci/trb_ring.h"
#include "xhci/xhci.h"
@ -45,6 +47,8 @@ class XhciDriver {
TrbRingReader event_ring_;
Thread interrupt_thread_;
glcr::Array<DeviceSlot> devices_;
XhciDriver(mmth::OwnedMemoryRegion&& pci_space);
glcr::ErrorCode ParseMmioStructures();
@ -60,4 +64,8 @@ class XhciDriver {
glcr::ErrorCode InitiateDevices();
glcr::ErrorCode NoOpCommand();
void HandleCommandCompletion(const XhciTrb& command_completion_trb);
void InitializeSlot(uint8_t slot_index);
};