diff --git a/Makefile b/Makefile index 6ed62ea5..9cacf276 100644 --- a/Makefile +++ b/Makefile @@ -153,7 +153,6 @@ QEMU_BASEOPTS= \ -device qemu-xhci,bus=pcie.1,id=xhci \ -device usb-mouse,bus=xhci.0 \ -device usb-kbd,bus=xhci.0 \ - -chardev null,id=usbterm \ -device usb-storage,bus=xhci.0,drive=stick \ -device ahci,id=ahci \ -device ide-hd,drive=drive0,bus=ahci.0 \ diff --git a/servers/Makefile b/servers/Makefile index fd59d946..e25994b2 100644 --- a/servers/Makefile +++ b/servers/Makefile @@ -7,15 +7,17 @@ MKRAMFS_BIN?=$(MKRAMFS_DIR)/$(MKRAMFS) .PHONY: all clean -all: test_server/test_server.elf devman/devman.elf pcidrv/pcidrv.elf ahcidrv/ahcidrv.elf fat32drv/fat32drv.elf kterminal +all: test_server/test_server.elf devman/devman.elf pcidrv/pcidrv.elf ahcidrv/ahcidrv.elf xhcidrv/xhcidrv.elf fat32drv/fat32drv.elf kterminal/kterminal.elf clean: $(MAKE) -C test_server clean $(MAKE) -C devman clean $(MAKE) -C pcidrv clean $(MAKE) -C ahcidrv clean + $(MAKE) -C xhcidrv clean $(MAKE) -C fat32drv clean $(MAKE) -C kterminal clean + $(MAKE) -C common/usb clean $(RM) system.ramfs $(MKRAMFS_BIN): $(MKRAMFS_DIR) @@ -33,6 +35,9 @@ pcidrv/pcidrv.elf: server_common.mk pcidrv/Makefile Makefile pcidrv/main.c ahcidrv/ahcidrv.elf: server_common.mk ahcidrv/Makefile Makefile ahcidrv/main.c $(MAKE) -C ahcidrv ahcidrv.elf +xhcidrv/xhcidrv.elf: server_common.mk xhcidrv/Makefile Makefile xhcidrv/main.c + $(MAKE) -C xhcidrv xhcidrv.elf + fat32drv/fat32drv.elf: server_common.mk fat32drv/Makefile Makefile fat32drv/main.c $(MAKE) -C fat32drv fat32drv.elf @@ -40,9 +45,9 @@ kterminal/kterminal.elf: server_common.mk kterminal/Makefile Makefile $(MAKE) -C kterminal kterminal.elf ifeq ($(ARCH),x86_64) -system_ramfs: $(MKRAMFS_BIN) test_server/test_server.elf devman/devman.elf pcidrv/pcidrv.elf ahcidrv/ahcidrv.elf fat32drv/fat32drv.elf kterminal/kterminal.elf - $(MKRAMFS_BIN) system.ramfs test_server/test_server.elf devman/devman.elf pcidrv/pcidrv.elf ahcidrv/ahcidrv.elf fat32drv/fat32drv.elf kterminal/kterminal.elf ../system/sysconf.json +system_ramfs: $(MKRAMFS_BIN) test_server/test_server.elf devman/devman.elf pcidrv/pcidrv.elf ahcidrv/ahcidrv.elf fat32drv/fat32drv.elf kterminal/kterminal.elf xhcidrv/xhcidrv.elf ../system/sysconf.json + $(MKRAMFS_BIN) system.ramfs test_server/test_server.elf devman/devman.elf pcidrv/pcidrv.elf ahcidrv/ahcidrv.elf fat32drv/fat32drv.elf kterminal/kterminal.elf xhcidrv/xhcidrv.elf ../system/sysconf.json else -system_ramfs: $(MKRAMFS_BIN) test_server/test_server.elf kterminal/kterminal.elf devman/devman.elf +system_ramfs: $(MKRAMFS_BIN) test_server/test_server.elf kterminal/kterminal.elf devman/devman.elf ../system/sysconf.json $(MKRAMFS_BIN) system.ramfs test_server/test_server.elf kterminal/kterminal.elf devman/devman.elf ../system/sysconf.json endif \ No newline at end of file diff --git a/servers/common/device_types.h b/servers/common/device_types.h index 92b6a096..62445ffa 100644 --- a/servers/common/device_types.h +++ b/servers/common/device_types.h @@ -24,7 +24,8 @@ typedef enum { DEVICE_TYPE_PCI = 1, DEVICE_TYPE_STORAGE = 2, DEVICE_TYPE_NETWORK = 3, - DEVICE_TYPE_DISPLAY = 4 + DEVICE_TYPE_DISPLAY = 4, + DEVICE_TYPE_USB = 5 } DeviceType; // Hardware types for storage devices @@ -36,6 +37,15 @@ typedef enum { STORAGE_HW_USB = 4 } StorageHardwareType; +// Hardware types for USB devices +typedef enum { + USB_HW_UNKNOWN = 0, + USB_HW_XHCI = 1, + USB_HW_EHCI = 2, + USB_HW_UHCI = 3, + USB_HW_OHCI = 4 +} UsbHardwareType; + // Query types typedef enum { QUERY_BY_TYPE = 1, QUERY_BY_ID = 2, QUERY_ALL = 3, QUERY_CHILDREN = 4 } DeviceQueryType; @@ -81,6 +91,21 @@ typedef struct { uint64_t config_base; // PCI config space base address } PciDeviceInfo; +// USB-specific device information +typedef struct { + DeviceInfo base; + uint16_t vendor_id; + uint16_t product_id; + uint8_t device_class; + uint8_t device_subclass; + uint8_t device_protocol; + uint8_t port_number; + uint8_t device_speed; + char manufacturer[64]; + char product[64]; + char serial_number[32]; +} UsbDeviceInfo; + // Device registration message typedef struct { DeviceMessageType msg_type; diff --git a/servers/common/usb/Makefile b/servers/common/usb/Makefile new file mode 100644 index 00000000..165a1899 --- /dev/null +++ b/servers/common/usb/Makefile @@ -0,0 +1,64 @@ +ARCH?=x86_64 +TARGET_TRIPLE?=$(ARCH)-elf-anos + +ASM?=nasm +XLD?=$(TARGET_TRIPLE)-ld +XOBJCOPY?=$(TARGET_TRIPLE)-objcopy +XOBJDUMP?=$(TARGET_TRIPLE)-objdump +STRIP?=$(TARGET_TRIPLE)-strip +XCC?=$(TARGET_TRIPLE)-gcc +XAR?=$(TARGET_TRIPLE)-ar +ASFLAGS=-f elf64 -F dwarf -g +CFLAGS=-Wall -Werror -Wpedantic -std=c23 \ + -fno-asynchronous-unwind-tables \ + -g \ + -O$(OPTIMIZE) + +SERVERS_ROOT_DIR=$(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +ROOT_DIR=$(abspath $(SERVERS_ROOT_DIR)/..) +THIS_DIR=$(SERVERS_ROOT_DIR)/$(SERVER_NAME) + +USB_COMMON_NAME=usbcore +LIBRARY=$(USB_COMMON_NAME) +LIBRARY_OBJS=usb_core.o + +LIBRARY_NAME=$(BINARY).a +LIBRARY_UC=$(shell echo '$(BINARY)' | tr '[:lower:]' '[:upper:]') +ALL_TARGETS=$(LIBRARY_NAME) +CLEAN_ARTIFACTS=$(THIS_DIR)/*.dis $(THIS_DIR)/*.elf $(THIS_DIR)/*.o $(THIS_DIR)/$(LIBRARY_NAME) +SHORT_HASH?=`git rev-parse --short HEAD` + +CDEFS+=-D$(LIBRARY_UC)_BUILD + +ifeq ($(CONSERVATIVE_BUILD),true) +CFLAGS+=-DCONSERVATIVE_BUILD +ifeq ($(CONSERVATIVE_UBSAN),true) +CFLAGS+=-fsanitize=undefined +LIBRARY_OBJS+=../common/ubsan.o +endif +endif + +# USB core library build +CDEFS= + +.PHONY: all build clean test + +all: build + +build: $(ALL_TARGETS) + +%.o: %.asm + $(ASM) \ + -DVERSTR=$(SHORT_HASH) \ + $(ASFLAGS) \ + -o $@ $< + +%.o: %.c + $(XCC) -DVERSTR=$(SHORT_HASH) $(CDEFS) $(CFLAGS) -c -o $@ $< \ + -I$(THIS_DIR)/include + +$(LIBRARY_NAME): $(LIBRARY_OBJS) + $(XAR) rcs lib$(USB_COMMON_NAME).a $(LIBRARY_OBJS) + +clean: + rm -f *.o *.a \ No newline at end of file diff --git a/servers/common/usb/usb_core.c b/servers/common/usb/usb_core.c new file mode 100644 index 00000000..23d8e244 --- /dev/null +++ b/servers/common/usb/usb_core.c @@ -0,0 +1,499 @@ +/* + * USB Core Layer Implementation + * anos - An Operating System + * + * Hardware-agnostic USB protocol implementation + * + * Copyright (c) 2025 Ross Bamford + */ + +#include +#include +#include +#include + +#include "usb_core.h" +#include "usb_spec.h" + +#ifdef DEBUG_USB_CORE +#define debugf printf +#else +#define debugf(...) +#endif + +// ============================================================================= +// Global USB Core State +// ============================================================================= + +static UsbHostController *registered_hcds[4] = { + NULL}; // Support up to 4 controllers +static uint8_t num_registered_hcds = 0; +static bool usb_core_initialized = false; + +// ============================================================================= +// Host Controller Management +// ============================================================================= + +int usb_register_host_controller(UsbHostController *hcd) { + if (!hcd || !hcd->ops) { + return -1; + } + + if (num_registered_hcds >= 4) { + debugf("USB: Maximum number of host controllers reached\n"); + return -1; + } + + registered_hcds[num_registered_hcds++] = hcd; + debugf("USB: Registered host controller '%s' with %u ports\n", hcd->name, + hcd->num_ports); + + return 0; +} + +int usb_unregister_host_controller(UsbHostController *hcd) { + for (uint8_t i = 0; i < num_registered_hcds; i++) { + if (registered_hcds[i] == hcd) { + // Shift remaining controllers down + for (uint8_t j = i; j < num_registered_hcds - 1; j++) { + registered_hcds[j] = registered_hcds[j + 1]; + } + registered_hcds[--num_registered_hcds] = NULL; + debugf("USB: Unregistered host controller '%s'\n", hcd->name); + return 0; + } + } + return -1; +} + +// ============================================================================= +// Device Management +// ============================================================================= + +UsbDevice *usb_alloc_device(UsbHostController *hcd, uint8_t port_number, + uint8_t speed) { + if (!hcd) + return NULL; + + UsbDevice *device = calloc(1, sizeof(UsbDevice)); + if (!device) { + debugf("USB: Failed to allocate device structure\n"); + return NULL; + } + + device->hcd = hcd; + device->port_number = port_number; + device->speed = speed; + device->state = USB_DEVICE_STATE_DEFAULT; + device->address = 0; // Will be assigned during enumeration + + debugf("USB: Allocated device on port %u (speed: %s)\n", port_number, + usb_get_speed_string(speed)); + + return device; +} + +void usb_free_device(UsbDevice *device) { + if (!device) + return; + + // Free configuration descriptor if allocated + if (device->raw_config_desc) { + free(device->raw_config_desc); + } + + // Remove from host controller's device list + if (device->hcd && device->address > 0) { + device->hcd->devices[device->address] = NULL; + } + + free(device); +} + +// ============================================================================= +// Device Enumeration +// ============================================================================= + +int usb_enumerate_device(UsbDevice *device) { + if (!device || !device->hcd || !device->hcd->ops) { + return -1; + } + + debugf("USB: Enumerating device on port %u\n", device->port_number); + + // Step 1: Get device descriptor (first 8 bytes for max packet size) + UsbDeviceDescriptor partial_desc; + int result = usb_get_descriptor(device, USB_DESC_TYPE_DEVICE, 0, + &partial_desc, 8); + if (result < 0) { + debugf("USB: Failed to get partial device descriptor\n"); + return -1; + } + + // Step 2: Assign device address + uint8_t new_address = 1; // TODO: Find free address + for (uint8_t addr = 1; addr < 128; addr++) { + if (device->hcd->devices[addr] == NULL) { + new_address = addr; + break; + } + } + + result = usb_set_address(device, new_address); + if (result < 0) { + debugf("USB: Failed to set device address %u\n", new_address); + return -1; + } + + device->address = new_address; + device->state = USB_DEVICE_STATE_ADDRESS; + device->hcd->devices[new_address] = device; + + // Step 3: Get full device descriptor + result = usb_get_descriptor(device, USB_DESC_TYPE_DEVICE, 0, + &device->device_desc, + sizeof(UsbDeviceDescriptor)); + if (result < 0) { + debugf("USB: Failed to get full device descriptor\n"); + return -1; + } + + debugf("USB: Device enumerated - VID:0x%04x PID:0x%04x Class:0x%02x\n", + device->device_desc.idVendor, device->device_desc.idProduct, + device->device_desc.bDeviceClass); + + // Step 4: Get string descriptors + if (device->device_desc.iManufacturer) { + int result = usb_get_string_descriptor( + device, device->device_desc.iManufacturer, device->manufacturer, + sizeof(device->manufacturer)); + if (result > 0) { + debugf("USB: Manufacturer: %s\n", device->manufacturer); + } + } + + if (device->device_desc.iProduct) { + int result = usb_get_string_descriptor( + device, device->device_desc.iProduct, device->product, + sizeof(device->product)); + if (result > 0) { + debugf("USB: Product: %s\n", device->product); + } + } + + if (device->device_desc.iSerialNumber) { + int result = usb_get_string_descriptor( + device, device->device_desc.iSerialNumber, + device->serial_number, sizeof(device->serial_number)); + if (result > 0) { + debugf("USB: Serial Number: %s\n", device->serial_number); + } + } + + return 0; +} + +int usb_configure_device(UsbDevice *device, const uint8_t config_value) { + if (!device || device->state != USB_DEVICE_STATE_ADDRESS) { + return -1; + } + + // Get configuration descriptor + UsbConfigurationDescriptor config_desc; + int result = usb_get_descriptor(device, USB_DESC_TYPE_CONFIGURATION, + config_value - 1, &config_desc, + sizeof(UsbConfigurationDescriptor)); + if (result < 0) { + debugf("USB: Failed to get configuration descriptor\n"); + return -1; + } + + // Get full configuration descriptor with all interfaces and endpoints + device->raw_config_desc = malloc(config_desc.wTotalLength); + if (!device->raw_config_desc) { + debugf("USB: Failed to allocate configuration descriptor buffer\n"); + return -1; + } + + result = usb_get_descriptor(device, USB_DESC_TYPE_CONFIGURATION, + config_value - 1, device->raw_config_desc, + config_desc.wTotalLength); + if (result < 0) { + debugf("USB: Failed to get full configuration descriptor\n"); + free(device->raw_config_desc); + device->raw_config_desc = NULL; + return -1; + } + + device->raw_config_desc_length = config_desc.wTotalLength; + device->config_desc = (UsbConfigurationDescriptor *)device->raw_config_desc; + + // Set configuration + result = usb_control_transfer( + device, + USB_REQ_TYPE_DIR_HOST_TO_DEV | USB_REQ_TYPE_STANDARD | + USB_REQ_TYPE_DEVICE, + USB_REQ_SET_CONFIGURATION, config_value, 0, NULL, 0, 5000); + if (result < 0) { + debugf("USB: Failed to set configuration %u\n", config_value); + return -1; + } + + device->state = USB_DEVICE_STATE_CONFIGURED; + debugf("USB: Device configured with configuration %u\n", config_value); + + return 0; +} + +// ============================================================================= +// Transfer Management +// ============================================================================= + +UsbTransfer *usb_alloc_transfer(UsbDevice *device, uint8_t endpoint, + UsbTransferType type) { + if (!device) + return NULL; + + UsbTransfer *transfer = calloc(1, sizeof(UsbTransfer)); + if (!transfer) { + debugf("USB: Failed to allocate transfer structure\n"); + return NULL; + } + + transfer->device = device; + transfer->endpoint = endpoint; + transfer->type = type; + transfer->status = USB_TRANSFER_STATUS_PENDING; + transfer->timeout_ms = 5000; // Default 5 second timeout + + return transfer; +} + +void usb_free_transfer(UsbTransfer *transfer) { + if (transfer) { + free(transfer); + } +} + +int usb_submit_transfer(UsbTransfer *transfer) { + if (!transfer || !transfer->device || !transfer->device->hcd) { + return -1; + } + + UsbHostController *hcd = transfer->device->hcd; + if (!hcd->ops || !hcd->ops->submit_transfer) { + return -1; + } + + return hcd->ops->submit_transfer(hcd, transfer); +} + +int usb_cancel_transfer(UsbTransfer *transfer) { + if (!transfer || !transfer->device || !transfer->device->hcd) { + return -1; + } + + UsbHostController *hcd = transfer->device->hcd; + if (!hcd->ops || !hcd->ops->cancel_transfer) { + return -1; + } + + return hcd->ops->cancel_transfer(hcd, transfer); +} + +// ============================================================================= +// Control Transfer Helpers +// ============================================================================= + +int usb_control_transfer(UsbDevice *device, uint8_t request_type, + uint8_t request, uint16_t value, uint16_t index, + void *data, uint16_t length, uint32_t timeout_ms) { + if (!device) + return -1; + + UsbDeviceRequest setup = {.bmRequestType = request_type, + .bRequest = request, + .wValue = value, + .wIndex = index, + .wLength = length}; + + UsbTransfer *transfer = usb_alloc_transfer(device, 0, USB_TRANSFER_CONTROL); + if (!transfer) + return -1; + + transfer->setup_packet = &setup; + transfer->buffer = data; + transfer->buffer_length = length; + transfer->timeout_ms = timeout_ms; + + int result = usb_submit_transfer(transfer); + if (result < 0) { + usb_free_transfer(transfer); + return result; + } + + // TODO: Wait for completion synchronously + // For now, assume immediate completion for control transfers + + int ret_val = (transfer->status == USB_TRANSFER_STATUS_COMPLETED) + ? (int)transfer->actual_length + : -1; + + usb_free_transfer(transfer); + return ret_val; +} + +int usb_get_descriptor(UsbDevice *device, uint8_t desc_type, uint8_t desc_index, + void *buffer, size_t buffer_length) { + return usb_control_transfer( + device, + USB_REQ_TYPE_DIR_DEV_TO_HOST | USB_REQ_TYPE_STANDARD | + USB_REQ_TYPE_DEVICE, + USB_REQ_GET_DESCRIPTOR, (desc_type << 8) | desc_index, 0, buffer, + buffer_length, 5000); +} + +int usb_set_address(UsbDevice *device, uint8_t address) { + return usb_control_transfer(device, + USB_REQ_TYPE_DIR_HOST_TO_DEV | + USB_REQ_TYPE_STANDARD | + USB_REQ_TYPE_DEVICE, + USB_REQ_SET_ADDRESS, address, 0, NULL, 0, 5000); +} + +int usb_set_configuration(UsbDevice *device, uint8_t config_value) { + return usb_control_transfer( + device, + USB_REQ_TYPE_DIR_HOST_TO_DEV | USB_REQ_TYPE_STANDARD | + USB_REQ_TYPE_DEVICE, + USB_REQ_SET_CONFIGURATION, config_value, 0, NULL, 0, 5000); +} + +// ============================================================================= +// String Descriptor Helpers +// ============================================================================= + +int usb_get_string_descriptor(UsbDevice *device, uint8_t string_index, + char *buffer, size_t buffer_size) { + if (!device || !buffer || buffer_size == 0) + return -1; + + // Get string descriptor in UTF-16 + uint8_t string_desc[256]; + const int result = + usb_get_descriptor(device, USB_DESC_TYPE_STRING, string_index, + string_desc, sizeof(string_desc)); + if (result < 0) + return -1; + + // Convert UTF-16 to ASCII (simplified) + const uint8_t length = string_desc[0]; + if (length < 2) + return -1; + + const size_t str_len = (length - 2) / 2; // UTF-16 string length + const size_t copy_len = + (str_len < buffer_size - 1) ? str_len : buffer_size - 1; + + for (size_t i = 0; i < copy_len; i++) { + buffer[i] = string_desc[2 + i * 2]; // Take low byte of UTF-16 char + } + buffer[copy_len] = '\0'; + + return (int)copy_len; +} + +// ============================================================================= +// Utility Functions +// ============================================================================= + +const char *usb_get_speed_string(uint8_t speed) { + switch (speed) { + case USB_SPEED_LOW: + return "Low Speed (1.5 Mbps)"; + case USB_SPEED_FULL: + return "Full Speed (12 Mbps)"; + case USB_SPEED_HIGH: + return "High Speed (480 Mbps)"; + case USB_SPEED_SUPER: + return "SuperSpeed (5 Gbps)"; + case USB_SPEED_SUPER_PLUS: + return "SuperSpeed+ (10 Gbps)"; + default: + return "Unknown Speed"; + } +} + +const char *usb_get_class_string(uint8_t device_class) { + switch (device_class) { + case USB_CLASS_UNDEFINED: + return "Undefined"; + case USB_CLASS_AUDIO: + return "Audio"; + case USB_CLASS_CDC: + return "CDC"; + case USB_CLASS_HID: + return "HID"; + case USB_CLASS_PHYSICAL: + return "Physical"; + case USB_CLASS_IMAGE: + return "Image"; + case USB_CLASS_PRINTER: + return "Printer"; + case USB_CLASS_MASS_STORAGE: + return "Mass Storage"; + case USB_CLASS_HUB: + return "Hub"; + case USB_CLASS_CDC_DATA: + return "CDC Data"; + case USB_CLASS_SMART_CARD: + return "Smart Card"; + case USB_CLASS_CONTENT_SECURITY: + return "Content Security"; + case USB_CLASS_VIDEO: + return "Video"; + case USB_CLASS_PERSONAL_HEALTHCARE: + return "Personal Healthcare"; + case USB_CLASS_DIAGNOSTIC_DEVICE: + return "Diagnostic Device"; + case USB_CLASS_WIRELESS: + return "Wireless"; + case USB_CLASS_MISCELLANEOUS: + return "Miscellaneous"; + case USB_CLASS_APP_SPECIFIC: + return "Application Specific"; + case USB_CLASS_VENDOR_SPECIFIC: + return "Vendor Specific"; + default: + return "Unknown Class"; + } +} + +// ============================================================================= +// Initialization +// ============================================================================= + +int usb_core_init(void) { + if (usb_core_initialized) { + return 0; + } + + memset(registered_hcds, 0, sizeof(registered_hcds)); + num_registered_hcds = 0; + + usb_core_initialized = true; + debugf("USB: Core layer initialized\n"); + + return 0; +} + +void usb_core_shutdown(void) { + if (!usb_core_initialized) { + return; + } + + // TODO: Clean up registered controllers and devices + + usb_core_initialized = false; + debugf("USB: Core layer shutdown\n"); +} \ No newline at end of file diff --git a/servers/common/usb/usb_core.h b/servers/common/usb/usb_core.h new file mode 100644 index 00000000..f14e5cfb --- /dev/null +++ b/servers/common/usb/usb_core.h @@ -0,0 +1,207 @@ +/* + * USB Core Layer Interface + * anos - An Operating System + * + * Hardware-agnostic USB protocol implementation + * Provides abstraction between USB devices and host controllers + * + * Copyright (c) 2025 Ross Bamford + */ + +#ifndef __ANOS_USB_CORE_H +#define __ANOS_USB_CORE_H + +#include "usb_spec.h" +#include +#include + +// Forward declarations +typedef struct UsbDevice UsbDevice; +typedef struct UsbHostController UsbHostController; +typedef struct UsbTransfer UsbTransfer; + +// ============================================================================= +// USB Transfer Types and Structures +// ============================================================================= + +typedef enum { + USB_TRANSFER_CONTROL, + USB_TRANSFER_BULK, + USB_TRANSFER_INTERRUPT, + USB_TRANSFER_ISOCHRONOUS +} UsbTransferType; + +typedef enum { + USB_TRANSFER_STATUS_PENDING, + USB_TRANSFER_STATUS_COMPLETED, + USB_TRANSFER_STATUS_ERROR, + USB_TRANSFER_STATUS_CANCELLED, + USB_TRANSFER_STATUS_TIMEOUT +} UsbTransferStatus; + +// USB Transfer completion callback +typedef void (*UsbTransferCallback)(UsbTransfer *transfer, void *user_data); + +// USB Transfer structure +struct UsbTransfer { + UsbDevice *device; // Target device + uint8_t endpoint; // Endpoint address + UsbTransferType type; // Transfer type + + // For control transfers + UsbDeviceRequest *setup_packet; // Setup packet (control only) + + // Data buffer + void *buffer; // Data buffer + size_t buffer_length; // Buffer size + size_t actual_length; // Actual transferred bytes + + // Completion handling + UsbTransferCallback callback; // Completion callback + void *user_data; // User data for callback + UsbTransferStatus status; // Transfer status + + // Timeout and retry + uint32_t timeout_ms; // Timeout in milliseconds + + // Internal use by host controller + void *hcd_private; // Host controller private data +}; + +// ============================================================================= +// USB Device Structure +// ============================================================================= + +typedef enum { + USB_DEVICE_STATE_DEFAULT, // Default state after reset + USB_DEVICE_STATE_ADDRESS, // Address assigned + USB_DEVICE_STATE_CONFIGURED, // Configuration set + USB_DEVICE_STATE_SUSPENDED, // Suspended + USB_DEVICE_STATE_ERROR // Error state +} UsbDeviceState; + +struct UsbDevice { + // Device identification + uint8_t address; // USB device address (1-127) + uint8_t port_number; // Port on hub/controller + uint8_t speed; // USB_SPEED_* constant + UsbDeviceState state; // Current device state + + // Device descriptors + UsbDeviceDescriptor device_desc; // Device descriptor + UsbConfigurationDescriptor *config_desc; // Active configuration + uint8_t *raw_config_desc; // Raw configuration descriptor data + size_t raw_config_desc_length; // Length of raw config data + + // String descriptors (cached) + char manufacturer[64]; // Manufacturer string + char product[64]; // Product string + char serial_number[32]; // Serial number string + + // Host controller association + UsbHostController *hcd; // Host controller driver + void *hcd_private; // Host controller private data + + // Parent/child relationships + UsbDevice *parent; // Parent device (hub) + UsbDevice *children[16]; // Child devices (for hubs) + uint8_t num_children; // Number of child devices + + // Device driver association + void *driver; // Device driver instance + void *driver_private; // Driver private data +}; + +// ============================================================================= +// Host Controller Driver Interface +// ============================================================================= + +// Host Controller Driver operations +typedef struct { + // Transfer operations + int (*submit_transfer)(UsbHostController *hcd, UsbTransfer *transfer); + int (*cancel_transfer)(UsbHostController *hcd, UsbTransfer *transfer); + + // Device management + int (*reset_device)(UsbHostController *hcd, UsbDevice *device); + int (*set_address)(UsbHostController *hcd, UsbDevice *device, + uint8_t address); + int (*enable_device)(UsbHostController *hcd, UsbDevice *device); + int (*disable_device)(UsbHostController *hcd, UsbDevice *device); + + // Endpoint management + int (*configure_endpoint)(UsbHostController *hcd, UsbDevice *device, + UsbEndpointDescriptor *ep_desc); + + // Port management + int (*get_port_status)(UsbHostController *hcd, uint8_t port, + uint32_t *status); + int (*reset_port)(UsbHostController *hcd, uint8_t port); + int (*enable_port)(UsbHostController *hcd, uint8_t port); + int (*disable_port)(UsbHostController *hcd, uint8_t port); +} UsbHostControllerOps; + +// Host Controller Driver structure +struct UsbHostController { + const char *name; // Controller name + UsbHostControllerOps *ops; // Controller operations + + // Controller capabilities + uint8_t max_devices; // Maximum devices supported + uint8_t num_ports; // Number of root hub ports + uint32_t supported_speeds; // Bitmask of supported speeds + + // Device management + UsbDevice *devices[128]; // Device array (address 1-127) + UsbDevice *root_hub; // Virtual root hub device + + // Private data for controller implementation + void *private_data; // Controller-specific private data +}; + +// ============================================================================= +// USB Core API Functions +// ============================================================================= + +// Host Controller Registration +int usb_register_host_controller(UsbHostController *hcd); +int usb_unregister_host_controller(UsbHostController *hcd); + +// Device Enumeration and Management +UsbDevice *usb_alloc_device(UsbHostController *hcd, uint8_t port_number, + uint8_t speed); +void usb_free_device(UsbDevice *device); +int usb_enumerate_device(UsbDevice *device); +int usb_configure_device(UsbDevice *device, uint8_t config_value); + +// Transfer Management +UsbTransfer *usb_alloc_transfer(UsbDevice *device, uint8_t endpoint, + UsbTransferType type); +void usb_free_transfer(UsbTransfer *transfer); +int usb_submit_transfer(UsbTransfer *transfer); +int usb_cancel_transfer(UsbTransfer *transfer); + +// Control Transfer Helpers +int usb_control_transfer(UsbDevice *device, uint8_t request_type, + uint8_t request, uint16_t value, uint16_t index, + void *data, uint16_t length, uint32_t timeout_ms); + +int usb_get_descriptor(UsbDevice *device, uint8_t desc_type, uint8_t desc_index, + void *buffer, size_t buffer_length); + +int usb_set_address(UsbDevice *device, uint8_t address); +int usb_set_configuration(UsbDevice *device, uint8_t config_value); + +// String Descriptor Helpers +int usb_get_string_descriptor(UsbDevice *device, uint8_t string_index, + char *buffer, size_t buffer_size); + +// Device Information +const char *usb_get_speed_string(uint8_t speed); +const char *usb_get_class_string(uint8_t device_class); + +// Initialization +int usb_core_init(void); +void usb_core_shutdown(void); + +#endif \ No newline at end of file diff --git a/servers/common/usb/usb_spec.h b/servers/common/usb/usb_spec.h new file mode 100644 index 00000000..fe627280 --- /dev/null +++ b/servers/common/usb/usb_spec.h @@ -0,0 +1,191 @@ +/* + * USB Specification Definitions + * anos - An Operating System + * + * Standard USB 3.0/2.0 protocol definitions + * Hardware-agnostic USB specification constants + * + * Copyright (c) 2025 Ross Bamford + */ + +#ifndef __ANOS_USB_SPEC_H +#define __ANOS_USB_SPEC_H + +#include + +// ============================================================================= +// USB Standard Request Types +// ============================================================================= + +// bmRequestType field breakdown +#define USB_REQ_TYPE_DIR_MASK 0x80 +#define USB_REQ_TYPE_DIR_HOST_TO_DEV 0x00 +#define USB_REQ_TYPE_DIR_DEV_TO_HOST 0x80 + +#define USB_REQ_TYPE_TYPE_MASK 0x60 +#define USB_REQ_TYPE_STANDARD 0x00 +#define USB_REQ_TYPE_CLASS 0x20 +#define USB_REQ_TYPE_VENDOR 0x40 + +#define USB_REQ_TYPE_RECIP_MASK 0x1F +#define USB_REQ_TYPE_DEVICE 0x00 +#define USB_REQ_TYPE_INTERFACE 0x01 +#define USB_REQ_TYPE_ENDPOINT 0x02 +#define USB_REQ_TYPE_OTHER 0x03 + +// Standard Request Codes (bRequest) +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +// ============================================================================= +// USB Descriptor Types +// ============================================================================= + +#define USB_DESC_TYPE_DEVICE 0x01 +#define USB_DESC_TYPE_CONFIGURATION 0x02 +#define USB_DESC_TYPE_STRING 0x03 +#define USB_DESC_TYPE_INTERFACE 0x04 +#define USB_DESC_TYPE_ENDPOINT 0x05 +#define USB_DESC_TYPE_DEVICE_QUALIFIER 0x06 +#define USB_DESC_TYPE_OTHER_SPEED 0x07 +#define USB_DESC_TYPE_INTERFACE_POWER 0x08 +#define USB_DESC_TYPE_BOS 0x0F + +// ============================================================================= +// USB Descriptor Structures +// ============================================================================= + +// USB Device Request (Setup Packet) +typedef struct { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} __attribute__((packed)) UsbDeviceRequest; + +// USB Device Descriptor +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} __attribute__((packed)) UsbDeviceDescriptor; + +// USB Configuration Descriptor +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +} __attribute__((packed)) UsbConfigurationDescriptor; + +// USB Interface Descriptor +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; +} __attribute__((packed)) UsbInterfaceDescriptor; + +// USB Endpoint Descriptor +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; +} __attribute__((packed)) UsbEndpointDescriptor; + +// ============================================================================= +// USB Device Classes +// ============================================================================= + +#define USB_CLASS_UNDEFINED 0x00 +#define USB_CLASS_AUDIO 0x01 +#define USB_CLASS_CDC 0x02 +#define USB_CLASS_HID 0x03 +#define USB_CLASS_PHYSICAL 0x05 +#define USB_CLASS_IMAGE 0x06 +#define USB_CLASS_PRINTER 0x07 +#define USB_CLASS_MASS_STORAGE 0x08 +#define USB_CLASS_HUB 0x09 +#define USB_CLASS_CDC_DATA 0x0A +#define USB_CLASS_SMART_CARD 0x0B +#define USB_CLASS_CONTENT_SECURITY 0x0D +#define USB_CLASS_VIDEO 0x0E +#define USB_CLASS_PERSONAL_HEALTHCARE 0x0F +#define USB_CLASS_DIAGNOSTIC_DEVICE 0xDC +#define USB_CLASS_WIRELESS 0xE0 +#define USB_CLASS_MISCELLANEOUS 0xEF +#define USB_CLASS_APP_SPECIFIC 0xFE +#define USB_CLASS_VENDOR_SPECIFIC 0xFF + +// ============================================================================= +// USB Device Speeds +// ============================================================================= + +#define USB_SPEED_UNKNOWN 0 +#define USB_SPEED_LOW 1 // 1.5 Mbps +#define USB_SPEED_FULL 2 // 12 Mbps +#define USB_SPEED_HIGH 3 // 480 Mbps +#define USB_SPEED_SUPER 4 // 5 Gbps +#define USB_SPEED_SUPER_PLUS 5 // 10 Gbps + +// ============================================================================= +// USB Endpoint Types and Directions +// ============================================================================= + +#define USB_ENDPOINT_DIR_MASK 0x80 +#define USB_ENDPOINT_DIR_OUT 0x00 +#define USB_ENDPOINT_DIR_IN 0x80 + +#define USB_ENDPOINT_TYPE_MASK 0x03 +#define USB_ENDPOINT_TYPE_CONTROL 0x00 +#define USB_ENDPOINT_TYPE_ISOC 0x01 +#define USB_ENDPOINT_TYPE_BULK 0x02 +#define USB_ENDPOINT_TYPE_INT 0x03 + +// ============================================================================= +// USB Configuration Attributes +// ============================================================================= + +#define USB_CONFIG_ATT_SELF_POWERED 0x40 +#define USB_CONFIG_ATT_REMOTE_WAKEUP 0x20 + +// ============================================================================= +// USB String Descriptor Language IDs +// ============================================================================= + +#define USB_LANGID_ENGLISH_US 0x0409 + +#endif \ No newline at end of file diff --git a/servers/pcidrv/enumerate.c b/servers/pcidrv/enumerate.c index b2b05644..f4b8f3d2 100644 --- a/servers/pcidrv/enumerate.c +++ b/servers/pcidrv/enumerate.c @@ -15,11 +15,14 @@ #include void spawn_ahci_driver(uint64_t ahci_base, uint64_t pci_config_base, uint64_t pci_device_id); +void spawn_xhci_driver(uint64_t xhci_base, uint64_t pci_config_base, + uint64_t pci_device_id); static uint64_t register_pci_device(const PCIBusDriver *bus_driver, const uint8_t bus, const uint8_t device, const uint8_t function, const uint16_t vendor_id, const uint16_t device_id, const uint8_t class_code, const uint8_t subclass, const uint8_t prog_if, const uint64_t pci_bus_device_id) { + // Find DEVMAN channel for this registration const SyscallResult devman_result = anos_find_named_channel("DEVMAN"); if (devman_result.result != SYSCALL_OK) { @@ -146,6 +149,54 @@ void pci_enumerate_function(const PCIBusDriver *bus_driver, const uint8_t bus, c } } + // Check for xHCI controller (USB 3.0) + if (class_code == 0x0C && subclass == 0x03 && prog_if == 0x30) { + + // For xHCI, use BAR0 to get the base address + const uint32_t bar0_low = + pci_config_read32(bus_driver, bus, device, function, 0x10); +#ifdef DEBUG_BUS_DRIVER_ENUM + const uint32_t bar0_high = + pci_config_read32(bus_driver, bus, device, function, 0x14); + +#ifdef VERY_NOISY_BUS_DRIVER + printf("\nDEBUG: BAR0 raw values: low=0x%08x high=0x%08x\n", bar0_low, + bar0_high); +#endif +#endif + + if (bar0_low != 0 && bar0_low != 0xFFFFFFFF) { + uint64_t xhci_base = (bar0_low & 0xFFFFFFF0); + +#ifdef DEBUG_BUS_DRIVER_ENUM + // Check if it's a 64-bit BAR + if ((bar0_low & 0x6) == 0x4) { + xhci_base |= ((uint64_t)bar0_high << 32); +#ifdef VERY_NOISY_BUS_DRIVER + printf("DEBUG: 64-bit BAR detected\n"); +#endif + } else { +#ifdef VERY_NOISY_BUS_DRIVER + printf("DEBUG: 32-bit BAR detected\n"); +#endif + } + + printf(" [xHCI Controller - Base: 0x%016lx]", xhci_base); +#else + printf("Found: xHCI Controller at 0x%016lx; Starting " + "driver...\n", + xhci_base); +#endif + + // Calculate PCI configuration space base for this device + const uint64_t pci_config_base = + bus_driver->ecam_base + ((uint64_t)bus << 20) + + ((uint64_t)device << 15) + ((uint64_t)function << 12); + + spawn_xhci_driver(xhci_base, pci_config_base, pci_device_id); + } + } + // Check if it's a bridge if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { #ifdef DEBUG_BUS_DRIVER_ENUM diff --git a/servers/pcidrv/main.c b/servers/pcidrv/main.c index c38566d7..1fe32e78 100644 --- a/servers/pcidrv/main.c +++ b/servers/pcidrv/main.c @@ -112,6 +112,87 @@ static int64_t spawn_process_via_system(const uint64_t stack_size, const uint16_ return (int64_t)spawn_result.value; } +void spawn_xhci_driver(const uint64_t xhci_base, const uint64_t pci_config_base, const uint64_t pci_device_id) { +#ifdef DEBUG_BUS_DRIVER_INIT + printf("\nSpawning xHCI driver for controller at 0x%016lx (PCI config at " + "0x%016lx)...\n", + xhci_base, pci_config_base); +#endif + + char xhci_base_str[32]; + char pci_config_str[32]; + snprintf(xhci_base_str, sizeof(xhci_base_str), "%lx", xhci_base); + snprintf(pci_config_str, sizeof(pci_config_str), "%lx", pci_config_base); + + char pci_device_id_str[32]; + snprintf(pci_device_id_str, sizeof(pci_device_id_str), "%lu", + pci_device_id); + + const char *argv[] = {"boot:/xhcidrv.elf", xhci_base_str, pci_config_str, + pci_device_id_str}; + + const InitCapability xhci_caps[] = { + {.capability_id = SYSCALL_ID_DEBUG_PRINT, + .capability_cookie = + __syscall_capabilities[SYSCALL_ID_DEBUG_PRINT]}, + {.capability_id = SYSCALL_ID_DEBUG_CHAR, + .capability_cookie = + __syscall_capabilities[SYSCALL_ID_DEBUG_CHAR]}, + {.capability_id = SYSCALL_ID_SLEEP, + .capability_cookie = __syscall_capabilities[SYSCALL_ID_SLEEP]}, + {.capability_id = SYSCALL_ID_MAP_PHYSICAL, + .capability_cookie = + __syscall_capabilities[SYSCALL_ID_MAP_PHYSICAL]}, + {.capability_id = SYSCALL_ID_MAP_VIRTUAL, + .capability_cookie = + __syscall_capabilities[SYSCALL_ID_MAP_VIRTUAL]}, + {.capability_id = SYSCALL_ID_ALLOC_PHYSICAL_PAGES, + .capability_cookie = + __syscall_capabilities[SYSCALL_ID_ALLOC_PHYSICAL_PAGES]}, + {.capability_id = SYSCALL_ID_KILL_CURRENT_TASK, + .capability_cookie = + __syscall_capabilities[SYSCALL_ID_KILL_CURRENT_TASK]}, + {.capability_id = SYSCALL_ID_ALLOC_INTERRUPT_VECTOR, + .capability_cookie = + __syscall_capabilities[SYSCALL_ID_ALLOC_INTERRUPT_VECTOR]}, + {.capability_id = SYSCALL_ID_WAIT_INTERRUPT, + .capability_cookie = + __syscall_capabilities[SYSCALL_ID_WAIT_INTERRUPT]}, + {.capability_id = SYSCALL_ID_FIND_NAMED_CHANNEL, + .capability_cookie = + __syscall_capabilities[SYSCALL_ID_FIND_NAMED_CHANNEL]}, + {.capability_id = SYSCALL_ID_SEND_MESSAGE, + .capability_cookie = + __syscall_capabilities[SYSCALL_ID_SEND_MESSAGE]}, + {.capability_id = SYSCALL_ID_RECV_MESSAGE, + .capability_cookie = + __syscall_capabilities[SYSCALL_ID_RECV_MESSAGE]}, + {.capability_id = SYSCALL_ID_REPLY_MESSAGE, + .capability_cookie = + __syscall_capabilities[SYSCALL_ID_REPLY_MESSAGE]}, + {.capability_id = SYSCALL_ID_CREATE_CHANNEL, + .capability_cookie = + __syscall_capabilities[SYSCALL_ID_CREATE_CHANNEL]}, + {.capability_id = SYSCALL_ID_CREATE_REGION, + .capability_cookie = + __syscall_capabilities[SYSCALL_ID_CREATE_REGION]}, + }; + +#ifdef DEBUG_BUS_DRIVER_INIT + printf(" --> spawn: %s %s\n", argv[0], argv[1]); +#endif + + const int64_t pid = + spawn_process_via_system(0x100000, 15, xhci_caps, 4, argv); + if (pid > 0) { +#ifdef DEBUG_BUS_DRIVER_INIT + printf(" --> xHCI driver spawned with PID %ld\n", pid); +#endif + } else { + printf("ERROR: Failed to spawn xHCI driver (error code: %ld)\n", pid); + } +} + void spawn_ahci_driver(const uint64_t ahci_base, const uint64_t pci_config_base, const uint64_t pci_device_id) { #ifdef DEBUG_BUS_DRIVER_INIT printf("\nSpawning AHCI driver for controller at 0x%016lx (PCI config at " @@ -213,7 +294,7 @@ static int pci_initialize_driver(const uint64_t ecam_base, const uint16_t segmen return 0; } -static void pci_enumerate_all_buses(uint64_t pci_bus_device_id) { +static void pci_enumerate_all_buses(const uint64_t pci_bus_device_id) { // TODO maybe don't run through all the buses? // should only have one root bus, can recursively scan from there... for (uint16_t bus = bus_driver.bus_start; bus <= bus_driver.bus_end; bus++) { diff --git a/servers/pcidrv/tests/pci.c b/servers/pcidrv/tests/pci.c index b87ecd07..73e5a7f3 100644 --- a/servers/pcidrv/tests/pci.c +++ b/servers/pcidrv/tests/pci.c @@ -41,6 +41,14 @@ void spawn_ahci_driver(uint64_t ahci_base, uint64_t pci_config_base, uint64_t pc // Mock - do nothing for tests } +void spawn_xhci_driver(uint64_t xhci_base, uint64_t pci_config_base, + uint64_t pci_device_id) { + (void)xhci_base; + (void)pci_config_base; + (void)pci_device_id; + // Mock - do nothing for tests +} + // Mock ECAM memory space (16MB to cover bus 0-15) static uint8_t mock_ecam_space[16 * 1024 * 1024]; static PCIBusDriver test_bus_driver; diff --git a/servers/xhcidrv/Makefile b/servers/xhcidrv/Makefile new file mode 100644 index 00000000..26f750d1 --- /dev/null +++ b/servers/xhcidrv/Makefile @@ -0,0 +1,26 @@ +SERVER_NAME=xhcidrv +BINARY=xhcidrv +BINARY_OBJS=main.o xhci.o pci.o xhci_rings.o xhci_hcd.o message_loop.o $(USB_CORE_DIR)/libusbcore.a + +# Link with USB core library +USB_CORE_DIR=../common/usb +LDFLAGS=-L$(USB_CORE_DIR) -lusbcore + +# The following C defines are recognised by the xHCI driver and enable various things +# +# DEBUG_XHCI_INIT Dump debug info about xHCI controller init +# VERY_NOISY_XHCI_INIT Dump **lots** of extra debug info about xHCI init +# DEBUG_XHCI_PCI Dump debug info from the driver's PCI interface +# DEBUG_XHCI_OPS Dump debug info about the drivers device operations +# VERY_NOISY_XHCI_OPS Dump **lots** of extra debug info about xHCI device ops +# DEBUG_XHCI_HCD Dump debug info from HCD interface code +# VERY_NOISY_XHCI_HCD Dump **lots** of extra debug info from HCD code +# DEBUG_XHCI_HCD_RINGS Dump debug info about HCD rings +# +CDEFS= + +include ../server_common.mk + +$(USB_CORE_DIR)/libusbcore.a: + $(MAKE) -C $(USB_CORE_DIR) + diff --git a/servers/xhcidrv/include/pci.h b/servers/xhcidrv/include/pci.h new file mode 100644 index 00000000..f6aaec6a --- /dev/null +++ b/servers/xhcidrv/include/pci.h @@ -0,0 +1,72 @@ +/* + * xhcidrv - PCI interface for xHCI driver + * anos - An Operating System + * + * Copyright (c) 2025 Ross Bamford + * + * TODO this duplication is silly, should really be in /servers/common + */ + +#ifndef __ANOS_XHCIDRV_PCI_H +#define __ANOS_XHCIDRV_PCI_H + +#include + +// PCI Configuration Space offsets +#define PCI_VENDOR_ID 0x00 +#define PCI_DEVICE_ID 0x02 +#define PCI_COMMAND 0x04 +#define PCI_STATUS 0x06 +#define PCI_CLASS_CODE 0x08 +#define PCI_HEADER_TYPE 0x0E +#define PCI_BAR0 0x10 +#define PCI_BAR1 0x14 +#define PCI_SUBSYSTEM_ID 0x2E + +// PCI Command register bits +#define PCI_CMD_IO_ENABLE (1 << 0) +#define PCI_CMD_MEM_ENABLE (1 << 1) +#define PCI_CMD_BUS_MASTER (1 << 2) +#define PCI_CMD_INTERRUPT_DISABLE (1 << 10) + +// PCI Capability List +#define PCI_CAPABILITY_LIST 0x34 +#define PCI_CAP_ID_MSI 0x05 +#define PCI_CAP_ID_MSIX 0x11 + +// MSI Capability structure offsets (from capability base) +#define MSI_CAP_CONTROL 0x02 +#define MSI_CAP_ADDRESS_LO 0x04 +#define MSI_CAP_ADDRESS_HI 0x08 +#define MSI_CAP_DATA_32 0x08 // For 32-bit addressing +#define MSI_CAP_DATA_64 0x0C // For 64-bit addressing + +// MSI Control register bits +#define MSI_CTRL_ENABLE (1 << 0) +#define MSI_CTRL_64BIT_CAPABLE (1 << 7) + +// MSI-X Capability structure offsets (from capability base) +#define MSIX_CAP_CONTROL 0x02 +#define MSIX_CAP_TABLE_OFFSET 0x04 +#define MSIX_CAP_PBA_OFFSET 0x08 + +// MSI-X Control register bits +#define MSIX_CTRL_ENABLE (1 << 15) +#define MSIX_CTRL_FUNCTION_MASK (1 << 14) + +uint32_t xhci_pci_read32(uint64_t config_base, uint8_t offset); +uint16_t xhci_pci_read16(uint64_t config_base, uint8_t offset); +uint8_t xhci_pci_read8(uint64_t config_base, uint8_t offset); +void xhci_pci_write32(uint64_t config_base, uint8_t offset, uint32_t value); +void xhci_pci_write16(uint64_t config_base, uint8_t offset, uint16_t value); + +bool xhci_pci_enable_device(uint64_t config_base); + +uint8_t xhci_pci_find_msi_capability(uint64_t config_base); +bool xhci_pci_configure_msi(uint64_t config_base, uint8_t msi_offset, + uint64_t msi_address, uint32_t msi_data); +bool xhci_pci_configure_msix(uint64_t config_base, uint8_t msix_offset, + uint64_t msi_address, uint32_t msi_data, + uint64_t xhci_base); + +#endif \ No newline at end of file diff --git a/servers/xhcidrv/include/xhci.h b/servers/xhcidrv/include/xhci.h new file mode 100644 index 00000000..f7611d10 --- /dev/null +++ b/servers/xhcidrv/include/xhci.h @@ -0,0 +1,164 @@ +/* + * xHCI (USB 3.0) Controller Driver + * anos - An Operating System + * + * Copyright (c) 2025 Ross Bamford + */ + +#ifndef __ANOS_XHCI_H +#define __ANOS_XHCI_H + +#include +#include + +// PCI Configuration +#define PCI_CONFIG_BASE_ADDRESS 0xC000000000ULL + +// xHCI Register Offsets +#define XHCI_CAP_CAPLENGTH 0x00 +#define XHCI_CAP_HCIVERSION 0x02 +#define XHCI_CAP_HCSPARAMS1 0x04 +#define XHCI_CAP_HCSPARAMS2 0x08 +#define XHCI_CAP_HCSPARAMS3 0x0C +#define XHCI_CAP_HCCPARAMS1 0x10 +#define XHCI_CAP_DBOFF 0x14 +#define XHCI_CAP_RTSOFF 0x18 +#define XHCI_CAP_HCCPARAMS2 0x1C + +// Operational Registers (offset by CAPLENGTH) +#define XHCI_OP_USBCMD 0x00 +#define XHCI_OP_USBSTS 0x04 +#define XHCI_OP_PAGESIZE 0x08 +#define XHCI_OP_DNCTRL 0x14 +#define XHCI_OP_CRCR 0x18 +#define XHCI_OP_DCBAAP 0x30 +#define XHCI_OP_CONFIG 0x38 + +// USB Command Register bits +#define XHCI_CMD_RUN (1 << 0) +#define XHCI_CMD_RESET (1 << 1) +#define XHCI_CMD_INTE (1 << 2) +#define XHCI_CMD_HSEE (1 << 3) + +// USB Status Register bits +#define XHCI_STS_HCH (1 << 0) +#define XHCI_STS_HSE (1 << 2) +#define XHCI_STS_EINT (1 << 3) +#define XHCI_STS_PCD (1 << 4) +#define XHCI_STS_SSS (1 << 8) +#define XHCI_STS_RSS (1 << 9) +#define XHCI_STS_SRE (1 << 10) +#define XHCI_STS_CNR (1 << 11) +#define XHCI_STS_HCE (1 << 12) + +// Port Register Offsets (from OP base + 0x400) +#define XHCI_PORT_SC 0x00 +#define XHCI_PORT_PMSC 0x04 +#define XHCI_PORT_LI 0x08 +#define XHCI_PORT_HLPMC 0x0C + +// Port Status and Control bits +#define XHCI_PORTSC_CCS (1 << 0) +#define XHCI_PORTSC_PED (1 << 1) +#define XHCI_PORTSC_OCA (1 << 3) +#define XHCI_PORTSC_PR (1 << 4) +#define XHCI_PORTSC_PLS_MASK (0xF << 5) +#define XHCI_PORTSC_PP (1 << 9) +#define XHCI_PORTSC_SPEED_MASK (0xF << 10) +#define XHCI_PORTSC_LWS (1 << 16) +#define XHCI_PORTSC_CSC (1 << 17) +#define XHCI_PORTSC_PEC (1 << 18) +#define XHCI_PORTSC_WRC (1 << 19) +#define XHCI_PORTSC_OCC (1 << 20) +#define XHCI_PORTSC_PRC (1 << 21) +#define XHCI_PORTSC_PLC (1 << 22) +#define XHCI_PORTSC_CEC (1 << 23) +#define XHCI_PORTSC_CAS (1 << 24) +#define XHCI_PORTSC_WCE (1 << 25) +#define XHCI_PORTSC_WDE (1 << 26) +#define XHCI_PORTSC_WOE (1 << 27) +#define XHCI_PORTSC_DR (1 << 30) +#define XHCI_PORTSC_WPR (1U << 31) + +// Port speeds +#define XHCI_SPEED_FULL 1 +#define XHCI_SPEED_LOW 2 +#define XHCI_SPEED_HIGH 3 +#define XHCI_SPEED_SUPER 4 + +// Runtime Interrupter Register Offsets (from interrupter base) +#define XHCI_IMAN 0x00 // Interrupt Management +#define XHCI_IMOD 0x04 // Interrupt Moderation +#define XHCI_ERSTSZ 0x08 // Event Ring Segment Table Size +#define XHCI_RSVD 0x0C // Reserved +#define XHCI_ERSTBA_LO 0x10 // Event Ring Segment Table Base Address (Low) +#define XHCI_ERSTBA_HI 0x14 // Event Ring Segment Table Base Address (High) +#define XHCI_ERDP_LO 0x18 // Event Ring Dequeue Pointer (Low) +#define XHCI_ERDP_HI 0x1C // Event Ring Dequeue Pointer (High) + +// xHCI Controller structure +typedef struct { + uint64_t base_addr; // Base address of xHCI registers + uint64_t pci_config_base; // PCI config space base (physical) + uint64_t pci_config_virt; // PCI config space base (virtual mapped) + volatile void *cap_regs; // Capability registers + volatile void *op_regs; // Operational registers + volatile void *port_regs; // Port registers base + volatile void *doorbell_regs; // Doorbell registers + volatile void *runtime_regs; // Runtime registers + + uint8_t cap_length; // Capability register length + uint16_t hci_version; // Host Controller Interface version + uint8_t max_slots; // Maximum device slots + uint8_t max_interrupters; // Maximum interrupters + uint8_t max_ports; // Maximum root hub ports + uint32_t page_size; // Controller page size + + // MSI interrupt configuration + uint8_t msi_cap_offset; // PCI MSI capability offset + uint8_t msi_vector; // Allocated MSI interrupt vector + bool msi_enabled; // MSI interrupts enabled + + bool initialized; // Controller initialization state + uint32_t active_ports; // Bitmask of active ports +} XHCIController; + +// xHCI Port structure +typedef struct { + XHCIController *controller; + uint8_t port_num; + uint8_t speed; + bool connected; + bool enabled; + bool initialized; + + // Device information (if connected) + uint16_t vendor_id; + uint16_t product_id; + uint8_t device_class; + uint8_t device_subclass; + uint8_t device_protocol; + char manufacturer[64]; + char product[64]; + char serial_number[32]; +} XHCIPort; + +// Function prototypes +bool xhci_controller_init(XHCIController *controller, uint64_t base_addr, + uint64_t pci_config_base); +bool xhci_controller_reset(const XHCIController *controller); +bool xhci_controller_start(const XHCIController *controller); +bool xhci_controller_stop(const XHCIController *controller); + +bool xhci_port_init(XHCIPort *port, XHCIController *controller, + uint8_t port_num); +bool xhci_port_reset(XHCIPort *port); +bool xhci_port_enable(XHCIPort *port); +bool xhci_port_scan(XHCIPort *port); + +uint32_t xhci_read32(const XHCIController *controller, volatile void *base, + uint32_t offset); +void xhci_write32(const XHCIController *controller, volatile void *base, + uint32_t offset, uint32_t value); + +#endif \ No newline at end of file diff --git a/servers/xhcidrv/include/xhci_hcd.h b/servers/xhcidrv/include/xhci_hcd.h new file mode 100644 index 00000000..4461c657 --- /dev/null +++ b/servers/xhcidrv/include/xhci_hcd.h @@ -0,0 +1,159 @@ +/* + * xHCI Host Controller Driver Interface + * anos - An Operating System + * + * xHCI implementation of USB Core Host Controller Interface + * + * Copyright (c) 2025 Ross Bamford + */ + +#ifndef __ANOS_XHCI_HCD_H +#define __ANOS_XHCI_HCD_H + +#include +#include + +#include "usb/usb_core.h" +#include "xhci.h" +#include "xhci_rings.h" + +// ============================================================================= +// xHCI Device Context Structures +// ============================================================================= + +// Device Context Base Address Array (DCBAA) +#define XHCI_MAX_DEVICES 128 + +typedef struct { + uint64_t device_context_ptrs[XHCI_MAX_DEVICES]; +} __attribute__((packed)) XhciDcbaa; + +// Slot Context +typedef struct { + uint32_t info1; // Route string, speed, MTT, hub, context entries + uint32_t info2; // Max exit latency, root hub port number, num ports + uint32_t tt_info; // TT hub slot ID, TT port number, TT think time + uint32_t state; // Slot state, device address + uint32_t reserved[4]; +} __attribute__((packed)) XhciSlotContext; + +// Endpoint Context +typedef struct { + uint32_t info1; // Endpoint state, mult, max primary streams, LSA, interval + uint32_t + info2; // Error count, endpoint type, HID, max burst size, max packet size + uint64_t dequeue_ptr; // TR dequeue pointer and DCS bit + uint32_t tx_info; // Average TRB length, max ESIT payload + uint32_t reserved[3]; +} __attribute__((packed)) XhciEndpointContext; + +// Device Context (1 slot context + 31 endpoint contexts) +typedef struct { + XhciSlotContext slot; + XhciEndpointContext endpoints[31]; +} __attribute__((packed)) XhciDeviceContext; + +// Input Context (control context + device context) +typedef struct { + uint32_t drop_flags; // Drop context flags + uint32_t add_flags; // Add context flags + uint32_t reserved[6]; // Reserved + XhciDeviceContext device_ctx; +} __attribute__((packed)) XhciInputContext; + +// ============================================================================= +// xHCI Extended Controller Structure +// ============================================================================= + +typedef struct { + // Base xHCI controller + XHCIController base; + + // USB Core integration + UsbHostController *usb_hcd; // USB host controller instance + + // Command and event handling + XhciRing command_ring; // Command ring + XhciRing event_ring; // Primary event ring + + // Device context management + volatile XhciDcbaa + *dcbaa; // Device Context Base Address Array (hardware-writable) + uint64_t dcbaa_physical; // Physical address of DCBAA + + XhciInputContext + *input_contexts[XHCI_MAX_DEVICES]; // Input contexts per slot + uint64_t input_context_physical[XHCI_MAX_DEVICES]; // Physical addresses + + XhciDeviceContext + *device_contexts[XHCI_MAX_DEVICES]; // Device contexts per slot + uint64_t device_context_physical[XHCI_MAX_DEVICES]; // Physical addresses + + // Transfer rings per endpoint per device + XhciRing *endpoint_rings[XHCI_MAX_DEVICES][32]; // [slot][endpoint] + + // Slot management + bool slots_enabled[XHCI_MAX_DEVICES]; // Track enabled slots + uint8_t next_slot_id; // Next available slot ID + + // Command completion tracking + uint32_t command_sequence; // Command sequence counter + bool command_completion_pending; // Command completion pending flag + +} XhciHostController; + +// ============================================================================= +// Function Prototypes +// ============================================================================= + +// Host Controller Setup +int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, + uint64_t pci_config_base); +void xhci_hcd_shutdown(XhciHostController *xhci_hcd); + +// USB Host Controller Operations (implements UsbHostControllerOps) +int xhci_hcd_submit_transfer(UsbHostController *hcd, UsbTransfer *transfer); +int xhci_hcd_cancel_transfer(UsbHostController *hcd, UsbTransfer *transfer); +int xhci_hcd_reset_device(UsbHostController *hcd, UsbDevice *device); +int xhci_hcd_set_address(UsbHostController *hcd, UsbDevice *device, + uint8_t address); +int xhci_hcd_enable_device(UsbHostController *hcd, UsbDevice *device); +int xhci_hcd_disable_device(UsbHostController *hcd, UsbDevice *device); +int xhci_hcd_configure_endpoint(UsbHostController *hcd, UsbDevice *device, + UsbEndpointDescriptor *ep_desc); +int xhci_hcd_get_port_status(UsbHostController *hcd, uint8_t port, + uint32_t *status); +int xhci_hcd_reset_port(UsbHostController *hcd, uint8_t port); +int xhci_hcd_enable_port(UsbHostController *hcd, uint8_t port); +int xhci_hcd_disable_port(UsbHostController *hcd, uint8_t port); + +// Command Processing +int xhci_send_command(XhciHostController *xhci_hcd, XhciTrb *command_trb); +int xhci_wait_for_command_completion(XhciHostController *xhci_hcd, + uint32_t timeout_ms); +int xhci_wait_for_transfer_completion(XhciHostController *xhci_hcd, + uint32_t timeout_ms); + +// Context Management +int xhci_alloc_device_slot(XhciHostController *xhci_hcd); +void xhci_free_device_slot(XhciHostController *xhci_hcd, uint8_t slot_id); +int xhci_setup_endpoint_ring(XhciHostController *xhci_hcd, uint8_t slot_id, + uint8_t endpoint_id); +int xhci_setup_device_context(XhciHostController *xhci_hcd, uint8_t slot_id, + UsbDevice *device); + +// Event Processing +void xhci_process_events(XhciHostController *xhci_hcd); +void xhci_handle_transfer_event(XhciHostController *xhci_hcd, + XhciTransferEventTrb *event); +void xhci_handle_command_completion(XhciHostController *xhci_hcd, + XhciCommandCompletionEventTrb *event); +void xhci_handle_port_status_change(XhciHostController *xhci_hcd, + XhciPortStatusChangeEventTrb *event); + +// Utility functions +XhciHostController *xhci_hcd_from_usb_hcd(UsbHostController *hcd); +const char *xhci_slot_state_string(uint32_t state); +const char *xhci_endpoint_state_string(uint32_t state); + +#endif \ No newline at end of file diff --git a/servers/xhcidrv/include/xhci_rings.h b/servers/xhcidrv/include/xhci_rings.h new file mode 100644 index 00000000..9b6f2c7e --- /dev/null +++ b/servers/xhcidrv/include/xhci_rings.h @@ -0,0 +1,275 @@ +/* + * xHCI Ring Management and TRB Definitions + * anos - An Operating System + * + * xHCI-specific Transfer Request Block (TRB) structures and ring management + * + * Copyright (c) 2025 Ross Bamford + */ + +#ifndef __ANOS_XHCI_RINGS_H +#define __ANOS_XHCI_RINGS_H + +#include +#include + +// ============================================================================= +// TRB (Transfer Request Block) Types +// ============================================================================= + +// TRB Types +#define TRB_TYPE_NORMAL 1 +#define TRB_TYPE_SETUP_STAGE 2 +#define TRB_TYPE_DATA_STAGE 3 +#define TRB_TYPE_STATUS_STAGE 4 +#define TRB_TYPE_ISOCH 5 +#define TRB_TYPE_LINK 6 +#define TRB_TYPE_EVENT_DATA 7 +#define TRB_TYPE_NOOP 8 + +// Command TRB Types +#define TRB_TYPE_ENABLE_SLOT 9 +#define TRB_TYPE_DISABLE_SLOT 10 +#define TRB_TYPE_ADDRESS_DEVICE 11 +#define TRB_TYPE_CONFIGURE_ENDPOINT 12 +#define TRB_TYPE_EVALUATE_CONTEXT 13 +#define TRB_TYPE_RESET_ENDPOINT 14 +#define TRB_TYPE_STOP_ENDPOINT 15 +#define TRB_TYPE_SET_TR_DEQUEUE 16 +#define TRB_TYPE_RESET_DEVICE 17 +#define TRB_TYPE_FORCE_EVENT 18 +#define TRB_TYPE_NEGOTIATE_BW 19 +#define TRB_TYPE_SET_LATENCY_TOLERANCE 20 +#define TRB_TYPE_GET_PORT_BW 21 +#define TRB_TYPE_FORCE_HEADER 22 +#define TRB_TYPE_NOOP_CMD 23 + +// Event TRB Types +#define TRB_TYPE_TRANSFER 32 +#define TRB_TYPE_COMMAND_COMPLETION 33 +#define TRB_TYPE_PORT_STATUS_CHANGE 34 +#define TRB_TYPE_BANDWIDTH_REQUEST 35 +#define TRB_TYPE_DOORBELL 36 +#define TRB_TYPE_HOST_CONTROLLER 37 +#define TRB_TYPE_DEVICE_NOTIFICATION 38 +#define TRB_TYPE_MFINDEX_WRAP 39 + +// ============================================================================= +// TRB Structure Definitions +// ============================================================================= + +// Generic TRB structure +typedef struct { + uint64_t parameter; + uint32_t status; + uint32_t control; +} __attribute__((packed)) XhciTrb; + +// TRB Control field bit definitions +#define TRB_CONTROL_CYCLE_BIT (1U << 0) +#define TRB_CONTROL_EVALUATE_NEXT (1U << 1) +#define TRB_CONTROL_INTERRUPT_ON_SHORT (1U << 2) +#define TRB_CONTROL_NO_SNOOP (1U << 3) +#define TRB_CONTROL_CHAIN_BIT (1U << 4) +#define TRB_CONTROL_INTERRUPT_ON_COMPLETE (1U << 5) +#define TRB_CONTROL_IMMEDIATE_DATA (1U << 6) +#define TRB_CONTROL_BLOCK_EVENT_INTERRUPT (1U << 9) + +#define TRB_CONTROL_TYPE_SHIFT 10 +#define TRB_CONTROL_TYPE_MASK (0x3F << TRB_CONTROL_TYPE_SHIFT) + +#define TRB_CONTROL_ENDPOINT_ID_SHIFT 16 +#define TRB_CONTROL_ENDPOINT_ID_MASK (0x1F << TRB_CONTROL_ENDPOINT_ID_SHIFT) + +#define TRB_CONTROL_SLOT_ID_SHIFT 24 +#define TRB_CONTROL_SLOT_ID_MASK (0xFF << TRB_CONTROL_SLOT_ID_SHIFT) + +// Helper macros for TRB fields +#define TRB_SET_TYPE(trb, type) \ + ((trb)->control = ((trb)->control & ~TRB_CONTROL_TYPE_MASK) | \ + ((type) << TRB_CONTROL_TYPE_SHIFT)) +#define TRB_GET_TYPE(trb) \ + (((trb)->control & TRB_CONTROL_TYPE_MASK) >> TRB_CONTROL_TYPE_SHIFT) +#define TRB_GET_COMPLETION_CODE(trb) (((trb)->status >> 24) & 0xFF) + +#define TRB_SET_SLOT_ID(trb, slot) \ + ((trb)->control = ((trb)->control & ~TRB_CONTROL_SLOT_ID_MASK) | \ + ((slot) << TRB_CONTROL_SLOT_ID_SHIFT)) +#define TRB_GET_SLOT_ID(trb) \ + (((trb)->control & TRB_CONTROL_SLOT_ID_MASK) >> TRB_CONTROL_SLOT_ID_SHIFT) + +#define TRB_SET_ENDPOINT_ID(trb, ep) \ + ((trb)->control = ((trb)->control & ~TRB_CONTROL_ENDPOINT_ID_MASK) | \ + ((ep) << TRB_CONTROL_ENDPOINT_ID_SHIFT)) +#define TRB_GET_ENDPOINT_ID(trb) \ + (((trb)->control & TRB_CONTROL_ENDPOINT_ID_MASK) >> \ + TRB_CONTROL_ENDPOINT_ID_SHIFT) + +// ============================================================================= +// Ring Structure Definitions +// ============================================================================= + +#define XHCI_RING_SIZE 256 // Number of TRBs per ring + +typedef struct { + volatile XhciTrb *trbs; // TRB array (page-aligned, hardware-writable) + uint64_t trbs_physical; // Physical address of TRB array + + uint32_t enqueue_index; // Next TRB to enqueue + uint32_t dequeue_index; // Next TRB to dequeue + uint32_t cycle_state; // Current cycle bit value + + uint32_t size; // Number of TRBs in ring + bool producer_cycle_state; // Producer cycle state + bool consumer_cycle_state; // Consumer cycle state + + // Statistics + uint32_t enqueued_count; // Total TRBs enqueued + uint32_t dequeued_count; // Total TRBs dequeued +} XhciRing; + +// ============================================================================= +// Command TRB Structures +// ============================================================================= + +// Enable Slot Command TRB +typedef struct { + uint64_t reserved1; + uint32_t reserved2; + uint32_t control; // Slot type in bits 16-20, cycle bit, TRB type +} __attribute__((packed)) XhciEnableSlotTrb; + +// Address Device Command TRB +typedef struct { + uint64_t input_context_ptr; // Input context physical address + uint32_t reserved; + uint32_t control; // BSR bit 9, slot ID bits 24-31, cycle bit, TRB type +} __attribute__((packed)) XhciAddressDeviceTrb; + +// Configure Endpoint Command TRB +typedef struct { + uint64_t input_context_ptr; // Input context physical address + uint32_t reserved; + uint32_t control; // DC bit 9, slot ID bits 24-31, cycle bit, TRB type +} __attribute__((packed)) XhciConfigureEndpointTrb; + +// ============================================================================= +// Transfer TRB Structures +// ============================================================================= + +// Setup Stage TRB (for control transfers) +typedef struct { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; + uint32_t trb_transfer_length; // Always 8 for setup stage + uint32_t control; // TRT bits 16-17, cycle bit, TRB type +} __attribute__((packed)) XhciSetupStageTrb; + +// Data Stage TRB (for control transfers) +typedef struct { + uint64_t data_buffer_ptr; // Data buffer physical address + uint32_t trb_transfer_length; // Transfer length and TD size + uint32_t control; // DIR bit 16, cycle bit, TRB type +} __attribute__((packed)) XhciDataStageTrb; + +// Status Stage TRB (for control transfers) +typedef struct { + uint64_t reserved; + uint32_t reserved2; + uint32_t control; // DIR bit 16, cycle bit, TRB type +} __attribute__((packed)) XhciStatusStageTrb; + +// Normal TRB (for bulk/interrupt transfers) +typedef struct { + uint64_t data_buffer_ptr; // Data buffer physical address + uint32_t trb_transfer_length; // Transfer length and TD size + uint32_t control; // ISP bit 2, cycle bit, TRB type +} __attribute__((packed)) XhciNormalTrb; + +// ============================================================================= +// Event TRB Structures +// ============================================================================= + +// Transfer Event TRB +typedef struct { + uint64_t trb_pointer; // TRB pointer or buffer pointer + uint32_t transfer_length; // Completion code and transfer length + uint32_t control; // Endpoint ID, slot ID, cycle bit, TRB type +} __attribute__((packed)) XhciTransferEventTrb; + +// Command Completion Event TRB +typedef struct { + uint64_t command_trb_pointer; // Command TRB pointer + uint32_t completion_parameter; // Completion code and parameter + uint32_t control; // VF ID, slot ID, cycle bit, TRB type +} __attribute__((packed)) XhciCommandCompletionEventTrb; + +// Port Status Change Event TRB +typedef struct { + uint32_t reserved1; + uint32_t port_id; // Port ID in bits 24-31 + uint32_t reserved2; + uint32_t control; // Completion code, cycle bit, TRB type +} __attribute__((packed)) XhciPortStatusChangeEventTrb; + +// ============================================================================= +// Completion Codes +// ============================================================================= + +#define XHCI_COMP_SUCCESS 1 +#define XHCI_COMP_DATA_BUFFER_ERROR 2 +#define XHCI_COMP_BABBLE_DETECTED 3 +#define XHCI_COMP_USB_TRANSACTION_ERROR 4 +#define XHCI_COMP_TRB_ERROR 5 +#define XHCI_COMP_STALL_ERROR 6 +#define XHCI_COMP_RESOURCE_ERROR 7 +#define XHCI_COMP_BANDWIDTH_ERROR 8 +#define XHCI_COMP_NO_SLOTS_AVAILABLE 9 +#define XHCI_COMP_INVALID_STREAM_TYPE 10 +#define XHCI_COMP_SLOT_NOT_ENABLED 11 +#define XHCI_COMP_ENDPOINT_NOT_ENABLED 12 +#define XHCI_COMP_SHORT_PACKET 13 +#define XHCI_COMP_RING_UNDERRUN 14 +#define XHCI_COMP_RING_OVERRUN 15 +#define XHCI_COMP_VF_EVENT_RING_FULL 16 +#define XHCI_COMP_PARAMETER_ERROR 17 +#define XHCI_COMP_BANDWIDTH_OVERRUN 18 +#define XHCI_COMP_CONTEXT_STATE_ERROR 19 +#define XHCI_COMP_NO_PING_RESPONSE 20 +#define XHCI_COMP_EVENT_RING_FULL 21 +#define XHCI_COMP_INCOMPATIBLE_DEVICE 22 +#define XHCI_COMP_MISSED_SERVICE 23 +#define XHCI_COMP_COMMAND_RING_STOPPED 24 +#define XHCI_COMP_COMMAND_ABORTED 25 +#define XHCI_COMP_STOPPED 26 +#define XHCI_COMP_STOPPED_LENGTH_INVALID 27 +#define XHCI_COMP_STOPPED_SHORT_PACKET 28 +#define XHCI_COMP_MAX_EXIT_LATENCY 29 +#define XHCI_COMP_ISOCH_BUFFER_OVERRUN 31 +#define XHCI_COMP_EVENT_LOST 32 +#define XHCI_COMP_UNDEFINED 33 +#define XHCI_COMP_INVALID_STREAM_ID 34 +#define XHCI_COMP_SECONDARY_BW_ERROR 35 +#define XHCI_COMP_SPLIT_TRANSACTION 36 + +// ============================================================================= +// Function Prototypes +// ============================================================================= + +// Ring Management +int xhci_ring_init(XhciRing *ring, uint32_t size); +void xhci_ring_free(XhciRing *ring); +XhciTrb *xhci_ring_enqueue_trb(XhciRing *ring); +volatile XhciTrb *xhci_ring_dequeue_trb(XhciRing *ring); +bool xhci_ring_has_space(XhciRing *ring, uint32_t num_trbs); +void xhci_ring_inc_enqueue(XhciRing *ring); +void xhci_ring_inc_dequeue(XhciRing *ring); + +// TRB Utilities +const char *xhci_trb_type_string(uint32_t trb_type); +const char *xhci_completion_code_string(uint32_t completion_code); + +#endif \ No newline at end of file diff --git a/servers/xhcidrv/main.c b/servers/xhcidrv/main.c new file mode 100644 index 00000000..a8e87695 --- /dev/null +++ b/servers/xhcidrv/main.c @@ -0,0 +1,303 @@ +/* + * xHCI Driver Server + * anos - An Operating System + * + * Copyright (c) 2025 Ross Bamford + */ + +#include +#include +#include +#include +#include + +#include + +#include "../common/device_types.h" +#include "../common/usb/usb_core.h" + +#include "message_loop.h" +#include "xhci.h" +#include "xhci_hcd.h" + +#ifndef VERSTR +#warning Version String not defined (-DVERSTR); Using default +#define VERSTR #unknown +#endif + +#define XSTRVER(verstr) #verstr +#define STRVER(xstrver) XSTRVER(xstrver) +#define VERSION STRVER(VERSTR) + +#ifdef DEBUG_XHCI_OPS +#define ops_debugf(...) printf(__VA_ARGS__) +#ifdef VERY_NOISY_XHCI_OPS +#define ops_vdebugf(...) printf(__VA_ARGS__) +#else +#define ops_vdebugf(...) +#endif +#else +#define ops_debugf(...) +#define ops_vdebugf(...) +#endif + +static constexpr int MAX_PORTS = 32; + +static XhciHostController xhci_hcd = {0}; +static XHCIPort ports[MAX_PORTS] = {{nullptr}}; // Support up to 32 ports +static uint64_t devman_channel = 0; +static uint64_t xhci_channel = 0; +static uint64_t pci_parent_id = 0; + +static uint32_t xhci_init_activate_ports() { + uint32_t active_ports = 0; + + for (uint8_t i = 0; i < xhci_hcd.base.max_ports && i < MAX_PORTS; i++) { + if (xhci_port_init(&ports[i], &xhci_hcd.base, i)) { + active_ports |= (1U << i); + + // Create USB device through USB core layer + UsbDevice *usb_device = + usb_alloc_device(xhci_hcd.usb_hcd, i, ports[i].speed); + if (usb_device) { + // enable the device (allocates xHCI slot) + if (xhci_hcd.usb_hcd->ops->enable_device && + xhci_hcd.usb_hcd->ops->enable_device(xhci_hcd.usb_hcd, + usb_device) == 0) { + + // enumerate device (this will do control transfers) + if (usb_enumerate_device(usb_device) == 0) { + printf(" USB device on port %u: %s [VID:0x%04x " + "PID:0x%04x]\n", + i, usb_device->product, + usb_device->device_desc.idVendor, + usb_device->device_desc.idProduct); + } else { + printf("Failed to enumerate USB device on port %u\n", + i); + + usb_free_device(usb_device); + } + } else { + printf("Failed to enable USB device on port %u\n", i); + usb_free_device(usb_device); + } + } + +#ifdef DEBUG_XHCI_INIT + printf("Port %u initialized - device connected\n", i); +#endif + } + } + + return active_ports; +} + +static int xhci_initialize_driver(const uint64_t xhci_base, + const uint64_t pci_config_base) { +#ifdef DEBUG_XHCI_INIT + printf("Initializing xHCI driver:\n"); + printf(" xHCI Base: 0x%016lx\n", xhci_base); + printf(" PCI Config Base: 0x%016lx\n", pci_config_base); +#endif + + // Initialize USB core + if (usb_core_init() != 0) { + printf("Failed to initialize USB core\n"); + return -1; + } + + // Initialize xHCI host controller driver + if (xhci_hcd_init(&xhci_hcd, xhci_base, pci_config_base) != 0) { + printf("Failed to initialize xHCI host controller driver\n"); + return -1; + } + + // Register with USB core + if (usb_register_host_controller(xhci_hcd.usb_hcd) != 0) { + printf("Failed to register xHCI with USB core\n"); + return -1; + } + +#ifdef DEBUG_XHCI_INIT + printf("xHCI controller initialized successfully\n"); + printf(" HCI Version: 0x%04x\n", xhci_hcd.base.hci_version); + printf(" Max Ports: %u\n", xhci_hcd.base.max_ports); + printf(" Max Slots: %u\n", xhci_hcd.base.max_slots); +#endif + + // Start the controller + if (!xhci_controller_start(&xhci_hcd.base)) { + printf("Failed to start xHCI controller\n"); + return -1; + } + + xhci_hcd.base.active_ports = xhci_init_activate_ports(); + +#ifdef DEBUG_XHCI_INIT + printf("Active ports: 0x%08x\n", xhci_hcd.base.active_ports); +#endif + + return 0; +} + +static void +populate_device_registration_message(DeviceRegistrationMessage *reg_msg) { + reg_msg->msg_type = DEVICE_MSG_REGISTER; + reg_msg->device_type = DEVICE_TYPE_USB; + reg_msg->device_count = 1; + + DeviceInfo *controller_info = (DeviceInfo *)reg_msg->data; + controller_info->device_id = 0; // Will be assigned by DEVMAN + controller_info->parent_id = pci_parent_id; + controller_info->device_type = DEVICE_TYPE_USB; + controller_info->hardware_type = USB_HW_XHCI; + controller_info->capabilities = DEVICE_CAP_HOTPLUG; + controller_info->driver_channel = xhci_channel; + + snprintf(controller_info->name, sizeof(controller_info->name), + "xHCI Controller"); + snprintf(controller_info->driver_name, sizeof(controller_info->driver_name), + "xhcidrv"); +} + +static void register_usb_devices_with_devman(const uint64_t xhci_controller_id, + char *reg_buffer) { + DeviceRegistrationMessage *reg_msg = + (DeviceRegistrationMessage *)reg_buffer; + + for (uint8_t i = 0; i < xhci_hcd.base.max_ports && i < MAX_PORTS; i++) { + if ((xhci_hcd.base.active_ports & (1U << i)) && ports[i].initialized) { + reg_msg = (DeviceRegistrationMessage *)reg_buffer; + reg_msg->msg_type = DEVICE_MSG_REGISTER; + reg_msg->device_type = DEVICE_TYPE_USB; + reg_msg->device_count = 1; + + UsbDeviceInfo *usb_info = (UsbDeviceInfo *)reg_msg->data; + usb_info->base.device_id = 0; // Will be assigned by DEVMAN + usb_info->base.parent_id = xhci_controller_id; + usb_info->base.device_type = DEVICE_TYPE_USB; + usb_info->base.hardware_type = USB_HW_XHCI; + usb_info->base.capabilities = 0; // Will be set based on device type + usb_info->base.driver_channel = xhci_channel; + + snprintf(usb_info->base.name, sizeof(usb_info->base.name), + "USB Device Port %u", i); + snprintf(usb_info->base.driver_name, + sizeof(usb_info->base.driver_name), "xhcidrv"); + + usb_info->vendor_id = ports[i].vendor_id; + usb_info->product_id = ports[i].product_id; + usb_info->device_class = ports[i].device_class; + usb_info->device_subclass = ports[i].device_subclass; + usb_info->device_protocol = ports[i].device_protocol; + usb_info->port_number = i; + usb_info->device_speed = ports[i].speed; + + strncpy(usb_info->manufacturer, ports[i].manufacturer, + sizeof(usb_info->manufacturer) - 1); + strncpy(usb_info->product, ports[i].product, + sizeof(usb_info->product) - 1); + strncpy(usb_info->serial_number, ports[i].serial_number, + sizeof(usb_info->serial_number) - 1); + + constexpr size_t msg_size = + sizeof(DeviceRegistrationMessage) + sizeof(UsbDeviceInfo); + + const SyscallResult reg_result = + anos_send_message(devman_channel, 0, msg_size, reg_buffer); + + if (reg_result.result == SYSCALL_OK && reg_result.value > 0) { + printf("Registered USB device on port %u with DEVMAN (ID: " + "%lu)\n", + i, reg_result.value); + } else { + printf("Failed to register USB device on port %u with DEVMAN\n", + i); + } + } + } +} + +static bool register_with_devman(void) { + const SyscallResult result = anos_find_named_channel("DEVMAN"); + if (result.result != SYSCALL_OK) { + printf("Failed to find DEVMAN channel\n"); + return false; + } + + devman_channel = result.value; + + // Create IPC channel for this driver + const SyscallResult channel_result = anos_create_channel(); + if (channel_result.result != SYSCALL_OK) { + printf("Failed to create xHCI driver IPC channel\n"); + return false; + } + + xhci_channel = channel_result.value; + static char __attribute__((aligned(4096))) reg_buffer[4096]; + + // First, register the xHCI controller itself + DeviceRegistrationMessage *reg_msg = + (DeviceRegistrationMessage *)reg_buffer; + + populate_device_registration_message(reg_msg); + + constexpr size_t msg_size = + sizeof(DeviceRegistrationMessage) + sizeof(DeviceInfo); + const SyscallResult controller_reg_result = + anos_send_message(devman_channel, 0, msg_size, reg_buffer); + + uint64_t xhci_controller_id = 0; + + if (controller_reg_result.result == SYSCALL_OK && + controller_reg_result.value > 0) { + xhci_controller_id = controller_reg_result.value; + + printf("Registered xHCI controller with DEVMAN (ID: %lu)\n", + xhci_controller_id); + } else { + printf("Failed to register xHCI controller with DEVMAN\n"); + return false; + } + + register_usb_devices_with_devman(xhci_controller_id, reg_buffer); + + return true; +} + +int main(const int argc, char **argv) { + printf("\nxHCI Driver #%s [libanos #%s]", VERSION, libanos_version()); + + if (argc < 4) { + printf("\n\nUsage: %s \n", + argv[0]); + printf("Arguments provided: %d\n", argc); + for (int i = 0; i < argc; i++) { + printf(" argv[%d]: %s\n", i, argv[i]); + } + return 1; + } + + printf(" @ xHCI:0x%s PCI:0x%s Parent:%s\n", argv[1], argv[2], argv[3]); + + const uint64_t xhci_base = strtoull(argv[1], nullptr, 16); + const uint64_t pci_config_base = strtoull(argv[2], nullptr, 16); + pci_parent_id = strtoull(argv[3], nullptr, 10); + + if (xhci_initialize_driver(xhci_base, pci_config_base) != 0) { + printf("Failed to initialize xHCI driver\n"); + return 1; + } + + ops_debugf("xHCI initialization @ 0x%s complete.\n", argv[1]); + + if (!register_with_devman()) { + printf("Warning: Failed to register xHCI with DEVMAN\n"); + } + + ops_debugf("xHCI driver ready, entering message loop...\n"); + + xhci_message_loop(xhci_channel); +} \ No newline at end of file diff --git a/servers/xhcidrv/message_loop.c b/servers/xhcidrv/message_loop.c new file mode 100644 index 00000000..fc010072 --- /dev/null +++ b/servers/xhcidrv/message_loop.c @@ -0,0 +1,59 @@ +/* + * Message loop for xHCI Driver + * anos - An Operating System + * + * Copyright (c) 2025 Ross Bamford + */ + +#include +#include +#include + +#include + +#ifdef DEBUG_XHCI_OPS +#define ops_debugf(...) printf(__VA_ARGS__) +#ifdef VERY_NOISY_XHCI_OPS +#define ops_vdebugf(...) printf(__VA_ARGS__) +#else +#define ops_vdebugf(...) +#endif +#else +#define ops_debugf(...) +#define ops_vdebugf(...) +#endif + +static void handle_usb_message(const uint64_t msg_cookie, const uint64_t tag, + void *buffer, const size_t buffer_size) { + ops_vdebugf("xHCI: Received message with tag %lu, size %lu\n", tag, + buffer_size); + + // For now, just acknowledge any messages + // TODO: Implement proper USB device communication + anos_reply_message(msg_cookie, 0); +} + +noreturn void xhci_message_loop(const uint64_t xhci_channel) { + while (1) { + void *ipc_buffer = (void *)0x300000000; + size_t buffer_size = 4096; + uint64_t tag = 0; + + const SyscallResult recv_result = + anos_recv_message(xhci_channel, &tag, &buffer_size, ipc_buffer); + + const uint64_t msg_cookie = recv_result.value; + + if (recv_result.result == SYSCALL_OK && msg_cookie) { + ops_vdebugf("xHCI: Received message with tag %lu, size %lu\n", tag, + buffer_size); + handle_usb_message(msg_cookie, tag, ipc_buffer, buffer_size); + } else { + ops_debugf("xHCI: Error receiving message [0x%016lx]\n", + (uint64_t)recv_result.result); + + // Sleep briefly to avoid pegging the CPU if we're in an error loop + anos_task_sleep_current_secs(1); + } + } +} diff --git a/servers/xhcidrv/message_loop.h b/servers/xhcidrv/message_loop.h new file mode 100644 index 00000000..f3411675 --- /dev/null +++ b/servers/xhcidrv/message_loop.h @@ -0,0 +1,15 @@ +/* +* Message loop for xHCI Driver + * anos - An Operating System + * + * Copyright (c) 2025 Ross Bamford + */ + +#include + +#ifndef ANOS_XHCI_MESSAGE_LOOP_H +#define ANOS_XHCI_MESSAGE_LOOP_H + +noreturn void xhci_message_loop(uint64_t xhci_channel); + +#endif //ANOS_XHCI_MESSAGE_LOOP_H diff --git a/servers/xhcidrv/pci.c b/servers/xhcidrv/pci.c new file mode 100644 index 00000000..f1d9765e --- /dev/null +++ b/servers/xhcidrv/pci.c @@ -0,0 +1,238 @@ +/* + * xhcidrv - PCI interface implementation + * anos - An Operating System + * + * Copyright (c) 2025 Ross Bamford + */ + +#include +#include +#include + +#include "include/pci.h" + +#ifdef DEBUG_XHCI_PCI +#define pci_debugf(...) printf(__VA_ARGS__) +#else +#define pci_debugf(...) +#endif + +// TODO this duplication is silly, should really be in /servers/common + +uint32_t xhci_pci_read32(const uint64_t config_base, const uint8_t offset) { + const volatile uint32_t *reg = (volatile uint32_t *)(config_base + offset); + const uint32_t value = *reg; + pci_debugf("PCI read32 [0x%lx + 0x%02x] = 0x%08x\n", config_base, offset, + value); + return value; +} + +uint16_t xhci_pci_read16(const uint64_t config_base, const uint8_t offset) { + const volatile uint16_t *reg = (volatile uint16_t *)(config_base + offset); + const uint16_t value = *reg; + pci_debugf("PCI read16 [0x%lx + 0x%02x] = 0x%04x\n", config_base, offset, + value); + return value; +} + +uint8_t xhci_pci_read8(const uint64_t config_base, const uint8_t offset) { + const volatile uint8_t *reg = (volatile uint8_t *)(config_base + offset); + const uint8_t value = *reg; + pci_debugf("PCI read8 [0x%lx + 0x%02x] = 0x%02x\n", config_base, offset, + value); + return value; +} + +void xhci_pci_write32(const uint64_t config_base, const uint8_t offset, + const uint32_t value) { + volatile uint32_t *reg = (volatile uint32_t *)(config_base + offset); + pci_debugf("PCI write32 [0x%lx + 0x%02x] = 0x%08x\n", config_base, offset, + value); + *reg = value; +} + +void xhci_pci_write16(const uint64_t config_base, const uint8_t offset, + const uint16_t value) { + volatile uint16_t *reg = (volatile uint16_t *)(config_base + offset); + pci_debugf("PCI write16 [0x%lx + 0x%02x] = 0x%04x\n", config_base, offset, + value); + *reg = value; +} + +bool xhci_pci_enable_device(const uint64_t config_base) { + pci_debugf("Enabling xHCI PCI device at 0x%lx\n", config_base); + + uint16_t command = xhci_pci_read16(config_base, PCI_COMMAND); + pci_debugf("Current PCI command register: 0x%04x\n", command); + + command |= PCI_CMD_MEM_ENABLE | PCI_CMD_BUS_MASTER; + command &= ~PCI_CMD_INTERRUPT_DISABLE; + + xhci_pci_write16(config_base, PCI_COMMAND, command); + + const uint16_t new_command = xhci_pci_read16(config_base, PCI_COMMAND); + if ((new_command & (PCI_CMD_MEM_ENABLE | PCI_CMD_BUS_MASTER)) != + (PCI_CMD_MEM_ENABLE | PCI_CMD_BUS_MASTER)) { + printf("ERROR: Failed to enable xHCI PCI device\n"); + return false; + } + + pci_debugf("xHCI PCI device enabled successfully (command: 0x%04x)\n", + new_command); + return true; +} + +uint8_t xhci_pci_find_msi_capability(const uint64_t config_base) { + // Check if capabilities are supported + const uint16_t status = xhci_pci_read16(config_base, 0x06); + if (!(status & (1U << 4))) { + pci_debugf("PCI device does not support capabilities\n"); + return 0; + } + + uint8_t cap_ptr = xhci_pci_read8(config_base, PCI_CAPABILITY_LIST) & 0xFC; + + while (cap_ptr != 0) { + const uint8_t cap_id = xhci_pci_read8(config_base, cap_ptr); + if (cap_id == PCI_CAP_ID_MSI) { + pci_debugf("Found MSI capability at offset 0x%02x\n", cap_ptr); + return cap_ptr; + } + if (cap_id == PCI_CAP_ID_MSIX) { + pci_debugf("Found MSI-X capability at offset 0x%02x\n", cap_ptr); + return cap_ptr; + } + cap_ptr = xhci_pci_read8(config_base, cap_ptr + 1) & 0xFC; + } + + pci_debugf("MSI or MSI-X capability not found\n"); + return 0; +} + +bool xhci_pci_configure_msi(const uint64_t config_base, + const uint8_t msi_offset, + const uint64_t msi_address, + const uint32_t msi_data) { + if (msi_offset == 0) { + pci_debugf("No MSI offset detected; MSI not configured.\n"); + return false; + } + + // Check capability type to determine MSI vs MSI-X + const uint8_t cap_id = xhci_pci_read8(config_base, msi_offset); + pci_debugf("Capability ID at offset 0x%02x: 0x%02x\n", msi_offset, cap_id); + pci_debugf("Configuring %s at offset 0x%02x: addr=0x%016lx, data=0x%08x\n", + (cap_id == PCI_CAP_ID_MSI) ? "MSI" : "MSI-X", msi_offset, + msi_address, msi_data); + + if (cap_id == PCI_CAP_ID_MSIX) { + pci_debugf("Detected MSI-X (0x%02x), configuring MSI-X interrupts\n", + PCI_CAP_ID_MSIX); + // We need the xHCI base address to configure MSI-X table, but we don't have it here + // For now, just enable MSI-X without table configuration + pci_debugf("MSI-X table configuration not implemented in generic " + "function\n"); + return false; + } + + pci_debugf("Continuing with MSI configuration for capability ID 0x%02x\n", + cap_id); + + // Read MSI control register + uint16_t msi_control = + xhci_pci_read16(config_base, msi_offset + MSI_CAP_CONTROL); + const bool is_64bit = (msi_control & MSI_CTRL_64BIT_CAPABLE) != 0; + + pci_debugf("MSI capability: %s addressing\n", + is_64bit ? "64-bit" : "32-bit"); + + // Disable MSI first + msi_control &= ~MSI_CTRL_ENABLE; + xhci_pci_write16(config_base, msi_offset + MSI_CAP_CONTROL, msi_control); + + // Write MSI address + xhci_pci_write32(config_base, msi_offset + MSI_CAP_ADDRESS_LO, + (uint32_t)(msi_address & 0xFFFFFFFF)); + + if (is_64bit) { + xhci_pci_write32(config_base, msi_offset + MSI_CAP_ADDRESS_HI, + (uint32_t)(msi_address >> 32)); + xhci_pci_write32(config_base, msi_offset + MSI_CAP_DATA_64, msi_data); + } else { + xhci_pci_write32(config_base, msi_offset + MSI_CAP_DATA_32, msi_data); + } + + // Enable MSI + msi_control |= MSI_CTRL_ENABLE; + xhci_pci_write16(config_base, msi_offset + MSI_CAP_CONTROL, msi_control); + + pci_debugf("MSI enabled successfully\n"); + return true; +} + +bool xhci_pci_configure_msix(const uint64_t config_base, + const uint8_t msix_offset, + const uint64_t msi_address, + const uint32_t msi_data, + const uint64_t xhci_base) { + pci_debugf("Configuring MSI-X at offset 0x%02x\n", msix_offset); + + // Read MSI-X control register + const uint16_t msix_control = + xhci_pci_read16(config_base, msix_offset + MSIX_CAP_CONTROL); + +#ifdef DEBUG_XHCI_PCI + const uint16_t table_size = + (msix_control & 0x07FF) + 1; // Bits 10:0 = table size - 1 + pci_debugf("MSI-X table size: %d entries\n", table_size); +#endif + + // Read table offset and BIR (BAR Indicator Register) + const uint32_t table_offset_bir = + xhci_pci_read32(config_base, msix_offset + MSIX_CAP_TABLE_OFFSET); + const uint8_t table_bir = table_offset_bir & 0x07; // Bits 2:0 = BAR number + const uint32_t table_offset = + table_offset_bir & ~0x07; // Bits 31:3 = offset + + pci_debugf("MSI-X table: BAR%d + 0x%08x\n", table_bir, table_offset); + + // Determine MSI-X table address based on the BIR + uint64_t msix_table_addr; + if (table_bir == 0) { + // Table is in BAR0 (xHCI register space) + msix_table_addr = xhci_base + table_offset; + } else { + // Table is in a different BAR - would need to map that BAR + pci_debugf("MSI-X table in BAR%d not currently supported\n", table_bir); + return false; + } + + // Configure MSI-X table entry 0 + // MSI-X table entry structure (16 bytes per entry): + // Offset 0x00: Message Address Low (32-bit) + // Offset 0x04: Message Address High (32-bit) + // Offset 0x08: Message Data (32-bit) + // Offset 0x0C: Vector Control (32-bit) - bit 0 = mask + pci_debugf("MSI-X table at virtual address 0x%016lx\n", msix_table_addr); + + // Configure table entry 0 with the allocated interrupt vector + volatile uint32_t *table_entry = (volatile uint32_t *)msix_table_addr; + + pci_debugf("Writing MSI-X table entry 0: addr=0x%016lx, data=0x%08x\n", + msi_address, msi_data); + + // Write message address (split into low/high 32-bit parts) + table_entry[0] = (uint32_t)(msi_address & 0xFFFFFFFF); // Address Low + table_entry[1] = (uint32_t)(msi_address >> 32); // Address High + table_entry[2] = msi_data; // Message Data + table_entry[3] = 0x00000000; // Vector Control (unmask interrupt) + + pci_debugf("MSI-X table entry 0 configured\n"); + + // Enable MSI-X - this will enable interrupt generation + const uint16_t new_control = msix_control | MSIX_CTRL_ENABLE; + xhci_pci_write16(config_base, msix_offset + MSIX_CAP_CONTROL, new_control); + + pci_debugf("MSI-X enabled with table configuration complete\n"); + return true; +} \ No newline at end of file diff --git a/servers/xhcidrv/xhci.c b/servers/xhcidrv/xhci.c new file mode 100644 index 00000000..f9afd20e --- /dev/null +++ b/servers/xhcidrv/xhci.c @@ -0,0 +1,435 @@ +/* + * xHCI Controller Implementation + * anos - An Operating System + * + * Copyright (c) 2025 Ross Bamford + */ + +#include +#include +#include +#include + +#include + +#include "include/pci.h" +#include "include/xhci.h" + +#if (__STDC_VERSION__ < 202000) +// TODO Apple clang doesn't support nullptr or constexpr yet - Sept 2025 +#ifndef nullptr +#ifdef NULL +#define nullptr NULL +#else +#define nullptr (((void *)0)) +#endif +#endif +#define constexpr const +#endif + +#ifdef DEBUG_XHCI_INIT +#define init_debugf(...) printf(__VA_ARGS__) +#ifdef VERY_NOISY_XHCI_INIT +#define init_vdebugf(...) printf(__VA_ARGS__) +#else +#define init_vdebugf(...) +#endif +#else +#define init_debugf(...) +#define init_vdebugf(...) +#endif + +#define XHCI_VIRTUAL_BASE 0x400000000 + +uint32_t xhci_read32(const XHCIController *controller, volatile void *base, + const uint32_t offset) { + volatile uint32_t *reg = (volatile uint32_t *)((uint8_t *)base + offset); + return *reg; +} + +void xhci_write32(const XHCIController *controller, volatile void *base, + const uint32_t offset, const uint32_t value) { + volatile uint32_t *reg = (volatile uint32_t *)((uint8_t *)base + offset); + *reg = value; +} + +bool xhci_controller_init(XHCIController *controller, const uint64_t base_addr, + const uint64_t pci_config_base) { + + // TODO tidy this up... + + init_debugf("Initializing xHCI controller at 0x%016lx\n", base_addr); + + memset(controller, 0, sizeof(XHCIController)); + controller->base_addr = base_addr; + controller->pci_config_base = pci_config_base; + + // Map PCI configuration space first + init_debugf("Mapping PCI config space: phys=0x%016lx -> virt=0x%016llx " + "(size=0x1000)\n", + pci_config_base, PCI_CONFIG_BASE_ADDRESS); + + const SyscallResult pci_result = anos_map_physical( + pci_config_base, (void *)PCI_CONFIG_BASE_ADDRESS, 0x1000, + ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | + ANOS_MAP_PHYSICAL_FLAG_NOCACHE); + + if (pci_result.result != SYSCALL_OK) { + printf("Failed to map PCI config space! Error code: %ld\n", + pci_result.result); + return false; + } + + // Update PCI access to use mapped virtual address + constexpr uint64_t mapped_pci_config = PCI_CONFIG_BASE_ADDRESS; + + // Store the virtual mapped address + controller->pci_config_virt = mapped_pci_config; + + if (!xhci_pci_enable_device(mapped_pci_config)) { + printf("Failed to enable xHCI PCI device\n"); + return false; + } + + // Find MSI capability for interrupt support + controller->msi_cap_offset = + xhci_pci_find_msi_capability(mapped_pci_config); + controller->msi_enabled = false; + controller->msi_vector = 0; + + if (controller->msi_cap_offset) { + init_debugf("MSI capability found at offset 0x%02x\n", + controller->msi_cap_offset); + } else { + init_debugf("No MSI capability found - will use polling mode\n"); + } + + // Get BAR0 to determine register space size (for future use) + init_vdebugf("BAR0: 0x%08x\n", + xhci_pci_read32(mapped_pci_config, PCI_BAR0)); + + // For now, map a standard amount (64KB should be sufficient for most xHCI controllers) + constexpr size_t map_size = 64 * 1024; + + init_debugf("Mapping %lu bytes of xHCI register space...\n", map_size); + + const SyscallResult map_result = anos_map_physical( + base_addr, (void *)XHCI_VIRTUAL_BASE, map_size, + ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | + ANOS_MAP_PHYSICAL_FLAG_NOCACHE); + + if (map_result.result != SYSCALL_OK) { + printf("Failed to map xHCI register space! Error: %ld\n", + map_result.result); + return false; + } + + controller->cap_regs = (volatile void *)XHCI_VIRTUAL_BASE; + init_debugf("xHCI registers mapped at virtual address 0x%lx\n", + (uintptr_t)controller->cap_regs); + + // Read capability registers + const uint32_t cap_length_version = + xhci_read32(controller, controller->cap_regs, XHCI_CAP_CAPLENGTH); + controller->cap_length = cap_length_version & 0xFF; + controller->hci_version = (cap_length_version >> 16) & 0xFFFF; + + init_debugf("Capability register length: %u bytes\n", + controller->cap_length); + init_debugf("HCI version: 0x%04x\n", controller->hci_version); + + // Set up operational register base + controller->op_regs = (volatile void *)((uint8_t *)controller->cap_regs + + controller->cap_length); + + // Read structural parameters + const uint32_t hcsparams1 = + xhci_read32(controller, controller->cap_regs, XHCI_CAP_HCSPARAMS1); + controller->max_slots = hcsparams1 & 0xFF; + controller->max_interrupters = (hcsparams1 >> 8) & 0x7FF; + controller->max_ports = (hcsparams1 >> 24) & 0xFF; + + init_debugf("Maximum device slots: %u\n", controller->max_slots); + init_debugf("Maximum interrupters: %u\n", controller->max_interrupters); + init_debugf("Maximum ports: %u\n", controller->max_ports); + + // Set up port register base + controller->port_regs = + (volatile void *)((uint8_t *)controller->op_regs + 0x400); + + // Read doorbell and runtime register offsets + const uint32_t dboff = + xhci_read32(controller, controller->cap_regs, XHCI_CAP_DBOFF); + const uint32_t rtsoff = + xhci_read32(controller, controller->cap_regs, XHCI_CAP_RTSOFF); + + controller->doorbell_regs = + (volatile void *)((uint8_t *)controller->cap_regs + (dboff & ~0x3)); + controller->runtime_regs = + (volatile void *)((uint8_t *)controller->cap_regs + + (rtsoff & ~0x1F)); + + init_debugf("xHCI: Doorbell registers at offset 0x%x (virtual addr %p)\n", + dboff, controller->doorbell_regs); + init_debugf("xHCI: Runtime registers at offset 0x%x (virtual addr %p)\n", + rtsoff, controller->runtime_regs); + + // Check if controller is already running + const uint32_t usbsts = + xhci_read32(controller, controller->op_regs, XHCI_OP_USBSTS); + if (!(usbsts & XHCI_STS_HCH)) { + init_debugf("Controller is running, stopping it first...\n"); + if (!xhci_controller_stop(controller)) { + printf("Failed to stop running xHCI controller\n"); + return false; + } + } + + // Reset the controller + if (!xhci_controller_reset(controller)) { + printf("Failed to reset xHCI controller\n"); + return false; + } + + controller->initialized = true; + init_debugf("xHCI controller initialization complete\n"); + return true; +} + +bool xhci_controller_reset(const XHCIController *controller) { + init_debugf("Resetting xHCI controller...\n"); + + // Set the reset bit + xhci_write32(controller, controller->op_regs, XHCI_OP_USBCMD, + XHCI_CMD_RESET); + + // Wait for reset to complete (CNR bit cleared) + for (int timeout = 0; timeout < 100; timeout++) { + const uint32_t usbsts = + xhci_read32(controller, controller->op_regs, XHCI_OP_USBSTS); + + if (!(usbsts & XHCI_STS_CNR)) { + init_debugf("xHCI controller reset complete\n"); + return true; + } + + // TODO these feel a bit ... long. + anos_task_sleep_current_secs(1); + } + + printf("xHCI controller reset timeout\n"); + return false; +} + +bool xhci_controller_start(const XHCIController *controller) { + init_debugf("Starting xHCI controller...\n"); + + // Set the run bit and enable interrupts if MSI is configured + uint32_t usbcmd = + xhci_read32(controller, controller->op_regs, XHCI_OP_USBCMD); + usbcmd |= XHCI_CMD_RUN; + + if (controller->msi_enabled) { + usbcmd |= XHCI_CMD_INTE; // Enable interrupts when MSI is available + init_debugf("xHCI: Enabling global interrupts (MSI vector 0x%02x)\n", + controller->msi_vector); + } else { + init_debugf("xHCI: Using polling mode (no MSI)\n"); + } + + xhci_write32(controller, controller->op_regs, XHCI_OP_USBCMD, usbcmd); + + // Enable interrupter 0 if MSI is configured + if (controller->msi_enabled) { + // Enable interrupter 0 in runtime registers + const uint32_t iman_offset = + 0x20; // IMAN register offset for interrupter 0 + xhci_write32(controller, controller->runtime_regs, iman_offset, + 0x02); // Set IE (Interrupt Enable) bit + init_debugf("xHCI: Enabled interrupter 0\n"); + } + + // Wait for controller to start (HCH bit cleared) + for (int timeout = 0; timeout < 100; timeout++) { + const uint32_t usbsts = + xhci_read32(controller, controller->op_regs, XHCI_OP_USBSTS); + + if (!(usbsts & XHCI_STS_HCH)) { + init_debugf("xHCI controller started successfully\n"); + return true; + } + + anos_task_sleep_current_secs(1); + } + + printf("xHCI controller start timeout\n"); + return false; +} + +bool xhci_controller_stop(const XHCIController *controller) { + init_debugf("Stopping xHCI controller...\n"); + + // Clear the run bit + uint32_t usbcmd = + xhci_read32(controller, controller->op_regs, XHCI_OP_USBCMD); + usbcmd &= ~XHCI_CMD_RUN; + xhci_write32(controller, controller->op_regs, XHCI_OP_USBCMD, usbcmd); + + // Wait for controller to stop (HCH bit set) + for (int timeout = 0; timeout < 100; timeout++) { + const uint32_t usbsts = + xhci_read32(controller, controller->op_regs, XHCI_OP_USBSTS); + + if (usbsts & XHCI_STS_HCH) { + init_debugf("xHCI controller stopped\n"); + return true; + } + + anos_task_sleep_current_secs(1); + } + + printf("xHCI controller stop timeout\n"); + return false; +} + +bool xhci_port_init(XHCIPort *port, XHCIController *controller, + const uint8_t port_num) { + init_vdebugf("Initializing xHCI port %u\n", port_num); + + if (port_num >= controller->max_ports) { + printf("Invalid port number %u (max: %u)\n", port_num, + controller->max_ports); + return false; + } + + memset(port, 0, sizeof(XHCIPort)); + port->controller = controller; + port->port_num = port_num; + + return xhci_port_scan(port); +} + +bool xhci_port_scan(XHCIPort *port) { + const uint32_t port_offset = port->port_num * 0x10; + +#ifdef DEBUG_XHCI_INIT +#ifdef VERY_NOISY_XHCI_INIT + const void *reg_addr = (const uint8_t *)port->controller->port_regs + + port_offset + XHCI_PORT_SC; + + init_vdebugf("xHCI: Port %u: offset=0x%x, reg_addr=%p (base=%p)\n", + port->port_num, port_offset, reg_addr, + port->controller->port_regs); +#endif +#endif + + uint32_t portsc = xhci_read32(port->controller, port->controller->port_regs, + port_offset + XHCI_PORT_SC); + + port->connected = (portsc & XHCI_PORTSC_CCS) != 0; + port->enabled = (portsc & XHCI_PORTSC_PED) != 0; + + init_debugf("xHCI: Scanning port %u: PORTSC=0x%08x, connected=%s\n", + port->port_num, portsc, + (portsc & XHCI_PORTSC_CCS) ? "YES" : "NO"); + + if (port->connected) { + init_debugf("xHCI: Port %u device detected, initiating reset\n", + port->port_num); + + // Initiate port reset by setting PR bit (bit 4) + const uint32_t reset_portsc = portsc | XHCI_PORTSC_PR; + xhci_write32(port->controller, port->controller->port_regs, + port_offset + XHCI_PORT_SC, reset_portsc); + + // Wait for reset to complete (PR bit clears automatically) + init_debugf("xHCI: Waiting for port %u reset to complete\n", + port->port_num); + int reset_timeout = 1000; // 1 second timeout + while (reset_timeout-- > 0) { + portsc = xhci_read32(port->controller, port->controller->port_regs, + port_offset + XHCI_PORT_SC); + + if ((portsc & XHCI_PORTSC_PR) == 0) { +#ifdef DEBUG_XHCI_INIT + uint8_t pls = (portsc & XHCI_PORTSC_PLS_MASK) >> 5; + init_debugf("xHCI: Port %u reset completed, PORTSC=0x%08x, " + "PLS=%u\n", + port->port_num, portsc, pls); +#endif + break; + } + + anos_task_sleep_current(1000000); + } + + if (reset_timeout <= 0) { + printf("xHCI: Port %u reset timeout\n", port->port_num); + return false; + } + + // Re-read port status after reset + portsc = xhci_read32(port->controller, port->controller->port_regs, + port_offset + XHCI_PORT_SC); + port->connected = (portsc & XHCI_PORTSC_CCS) != 0; + port->enabled = (portsc & XHCI_PORTSC_PED) != 0; + + if (!port->connected) { + printf("xHCI: Port %u device disconnected after reset\n", + port->port_num); + return false; + } + + port->speed = (portsc & XHCI_PORTSC_SPEED_MASK) >> 10; + + const char *speed_str; + switch (port->speed) { + case XHCI_SPEED_FULL: + speed_str = "Full Speed (12 Mbps)"; + break; + case XHCI_SPEED_LOW: + speed_str = "Low Speed (1.5 Mbps)"; + break; + case XHCI_SPEED_HIGH: + speed_str = "High Speed (480 Mbps)"; + break; + case XHCI_SPEED_SUPER: + speed_str = "SuperSpeed (5 Gbps)"; + break; + default: + speed_str = "Unknown Speed"; + break; + } + + const bool port_power = (portsc & XHCI_PORTSC_PP) != 0; + const uint8_t link_state = (portsc & XHCI_PORTSC_PLS_MASK) >> 5; + + printf("xHCI Port %u: Device connected - %s, Power=%s, LinkState=%u, " + "Enabled=%s\n", + port->port_num, speed_str, port_power ? "On" : "Off", link_state, + port->enabled ? "Yes" : "No"); + + // Setup default port info, will populate more later when we enumerate. + // defaulting here just so it's sane if enumeration fails... + // + port->vendor_id = 0x0000; // Populated later... + port->product_id = 0x0000; + + port->device_class = 0; + + snprintf(port->manufacturer, sizeof(port->manufacturer), + "Unknown Manufacturer"); + snprintf(port->product, sizeof(port->product), "%s Device (Port %u)", + speed_str, port->port_num); + snprintf(port->serial_number, sizeof(port->serial_number), + "XHCI-P%u-S%u", port->port_num, port->speed); + + port->initialized = true; + + return true; + } else { + init_vdebugf("Port %u: No device connected\n", port->port_num); + port->initialized = false; + return false; + } +} \ No newline at end of file diff --git a/servers/xhcidrv/xhci_hcd.c b/servers/xhcidrv/xhci_hcd.c new file mode 100644 index 00000000..f3ed0955 --- /dev/null +++ b/servers/xhcidrv/xhci_hcd.c @@ -0,0 +1,1797 @@ +/* + * xHCI Host Controller Driver Implementation + * anos - An Operating System + * + * xHCI implementation of USB Core Host Controller Interface + * + * Copyright (c) 2025 Ross Bamford + */ + +#include +#include +#include +#include + +#include + +#include "include/pci.h" +#include "include/xhci.h" +#include "include/xhci_hcd.h" +#include "usb/usb_core.h" +#include "usb/usb_spec.h" + +#ifdef DEBUG_XHCI_HCD +#define hcd_debugf(...) printf(__VA_ARGS__) +#ifdef VERY_NOISY_XHCI_HCD +#define hcd_vdebugf(...) printf(__VA_ARGS__) +#else +#define hcd_vdebugf(...) +#endif +#else +#define hcd_debugf(...) +#define hcd_vdebugf(...) +#endif + +#define VM_PAGE_SIZE ((4096)) + +int xhci_submit_control_transfer(XhciHostController *xhci_hcd, + UsbTransfer *transfer); + +int xhci_submit_bulk_transfer(XhciHostController *xhci_hcd, + UsbTransfer *transfer); + +int xhci_submit_interrupt_transfer(XhciHostController *xhci_hcd, + UsbTransfer *transfer); + +int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, + UsbTransfer *transfer, uint8_t slot_id); + +void xhci_ring_doorbell(XhciHostController *xhci_hcd, uint8_t slot_id, + uint8_t target); + +int xhci_setup_event_ring(XhciHostController *xhci_hcd); + +// Interrupt processing functions +int xhci_process_command_completion_event(XhciHostController *xhci_hcd); +int xhci_process_transfer_completion_event(XhciHostController *xhci_hcd); + +// ============================================================================= +// Host Controller Operations Table +// ============================================================================= + +static UsbHostControllerOps xhci_hcd_ops = { + .submit_transfer = xhci_hcd_submit_transfer, + .cancel_transfer = xhci_hcd_cancel_transfer, + .reset_device = xhci_hcd_reset_device, + .set_address = xhci_hcd_set_address, + .enable_device = xhci_hcd_enable_device, + .disable_device = xhci_hcd_disable_device, + .configure_endpoint = xhci_hcd_configure_endpoint, + .get_port_status = xhci_hcd_get_port_status, + .reset_port = xhci_hcd_reset_port, + .enable_port = xhci_hcd_enable_port, + .disable_port = xhci_hcd_disable_port, +}; + +// ============================================================================= +// Host Controller Setup +// ============================================================================= + +static uint16_t get_xecp(const uint32_t hccparams1) { + return (hccparams1 >> 16) & 0xFFFF; +} + +static void +xhci_handle_extended_capability_setup(const XhciHostController *xhci_hcd, + const uint16_t xecp) { + + // Read first extended capability + const uint32_t ext_cap = + xhci_read32(&xhci_hcd->base, xhci_hcd->base.cap_regs, xecp); + uint8_t cap_id = ext_cap & 0xFF; + +#ifdef DEBUG_XHCI_HCD +#ifdef VERY_NOISY_XHCI_HCD + uint8_t next_ptr = (ext_cap >> 8) & 0xFF; + uint16_t cap_specific = (ext_cap >> 16) & 0xFFFF; + + hcd_vdebugf(" First extended capability: ID=0x%02x, Next=0x%02x, " + "Specific=0x%04x\n", + cap_id, next_ptr, cap_specific); +#endif +#endif + + if (cap_id == 1) { // USB Legacy Support Capability + hcd_debugf(" Found USB Legacy Support Capability - may need to " + "disable OS handoff\n"); + + // Read USB Legacy Support Control/Status + if (xecp + 4 < 0x1000) { // Safety check + uint32_t usblegctlsts = xhci_read32( + &xhci_hcd->base, xhci_hcd->base.cap_regs, xecp + 4); + hcd_vdebugf(" USBLEGCTLSTS: 0x%08x\n", usblegctlsts); + + // Check if BIOS/SMM owns the controller + if (usblegctlsts & (1 << 16)) { // HC BIOS Owned Semaphore + hcd_debugf(" WARNING: BIOS still owns the controller - " + "attempting OS handoff\n"); + + // Set OS Owned Semaphore + xhci_write32(&xhci_hcd->base, xhci_hcd->base.cap_regs, xecp + 4, + usblegctlsts | (1 << 24)); + + // Wait for handoff (timeout after 1 second) + for (int timeout = 1000; timeout > 0; timeout--) { + usblegctlsts = xhci_read32( + &xhci_hcd->base, xhci_hcd->base.cap_regs, xecp + 4); + if (!(usblegctlsts & (1 << 16))) { // BIOS released control + hcd_debugf(" OS handoff successful\n"); + break; + } + // Small delay + for (volatile int i = 0; i < 10000; i++) + ; + } + + if (usblegctlsts & (1 << 16)) { + printf(" WARNING: OS handoff failed - BIOS still owns " + "controller\n"); + } + } + } + } +} + +static void xhci_set_command_ring_register(const XhciHostController *xhci_hcd) { + const uint64_t crcr = xhci_hcd->command_ring.trbs_physical | 0x1; // RCS bit + xhci_write32(&xhci_hcd->base, xhci_hcd->base.op_regs, XHCI_OP_CRCR, + (uint32_t)(crcr & 0xFFFFFFFF)); + xhci_write32(&xhci_hcd->base, xhci_hcd->base.op_regs, XHCI_OP_CRCR + 4, + (uint32_t)(crcr >> 32)); +} + +static int xhci_init_usb_hcd(XhciHostController *xhci_hcd) { + xhci_hcd->usb_hcd = malloc(sizeof(UsbHostController)); + if (!xhci_hcd->usb_hcd) { + hcd_debugf("xHCI HCD: Failed to allocate USB host controller\n"); + return -1; + } + + memset(xhci_hcd->usb_hcd, 0, sizeof(UsbHostController)); + + xhci_hcd->usb_hcd->name = "xHCI Host Controller"; + xhci_hcd->usb_hcd->ops = &xhci_hcd_ops; + xhci_hcd->usb_hcd->max_devices = 127; + xhci_hcd->usb_hcd->num_ports = xhci_hcd->base.max_ports; + xhci_hcd->usb_hcd->supported_speeds = + (1 << USB_SPEED_LOW) | (1 << USB_SPEED_FULL) | + (1 << USB_SPEED_HIGH) | (1 << USB_SPEED_SUPER); + + xhci_hcd->usb_hcd->private_data = (void *)xhci_hcd; + + return 0; +} + +static void xhci_clear_usbsts_interrupt(XhciHostController *xhci_hcd) { + const uint32_t usbsts = xhci_read32(&xhci_hcd->base, xhci_hcd->base.op_regs, + XHCI_OP_USBSTS); + if (usbsts & XHCI_STS_EINT) { + xhci_write32(&xhci_hcd->base, xhci_hcd->base.op_regs, XHCI_OP_USBSTS, + XHCI_STS_EINT); + hcd_debugf("xHCI HCD: Cleared USBSTS.EINT (was 0x%08x)\n", usbsts); + } +} + +static void xhci_update_erdp(XhciHostController *xhci_hcd, bool set_ehb) { + const uint64_t new_erdp = + xhci_hcd->event_ring.trbs_physical + + (xhci_hcd->event_ring.dequeue_index * sizeof(XhciTrb)); + + const uint64_t erdp_value = set_ehb ? (new_erdp | (1ULL << 3)) : new_erdp; + + constexpr uint32_t interrupter_base = 0x20; + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x18, (uint32_t)(erdp_value & 0xFFFFFFFF)); + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x1C, (uint32_t)(erdp_value >> 32)); + + hcd_debugf("xHCI HCD: Updated ERDP to 0x%016lx%s\n", erdp_value, + set_ehb ? " (with EHB)" : ""); +} + +static void xhci_clear_iman_interrupt(XhciHostController *xhci_hcd) { + constexpr uint32_t interrupter_base = 0x20; + +#ifdef DEBUG_XHCI_HCD + const uint32_t iman = xhci_read32( + &xhci_hcd->base, xhci_hcd->base.runtime_regs, interrupter_base); +#endif + + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, interrupter_base, + (1 << 1) | (1 << 0)); + + (void)xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base); + + hcd_debugf("xHCI HCD: Cleared interrupt pending bit (IMAN was 0x%08x)\n", + iman); +} + +int xhci_hcd_init(XhciHostController *xhci_hcd, const uint64_t base_addr, + const uint64_t pci_config_base) { + + if (!xhci_hcd) { + return -1; + } + + memset(xhci_hcd, 0, sizeof(XhciHostController)); + + if (!xhci_controller_init(&xhci_hcd->base, base_addr, pci_config_base)) { + hcd_debugf("xHCI HCD: Failed to initialize base controller\n"); + return -1; + } + + if (xhci_ring_init(&xhci_hcd->command_ring, XHCI_RING_SIZE) != 0) { + hcd_debugf("xHCI HCD: Failed to initialize command ring\n"); + return -1; + } + + if (xhci_ring_init(&xhci_hcd->event_ring, XHCI_RING_SIZE) != 0) { + hcd_debugf("xHCI HCD: Failed to initialize event ring\n"); + xhci_ring_free(&xhci_hcd->command_ring); + return -1; + } + + const SyscallResultA dcbaa_alloc = anos_alloc_physical_pages(VM_PAGE_SIZE); + if (dcbaa_alloc.result != SYSCALL_OK) { + hcd_debugf("xHCI HCD: Failed to allocate DCBAA - syscall result: " + "0x%016lx\n", + (uint64_t)dcbaa_alloc.result); + hcd_debugf("xHCI HCD: Requested 1 page (%d bytes)\n", VM_PAGE_SIZE); + return -1; + } + + xhci_hcd->dcbaa_physical = dcbaa_alloc.value; + + const SyscallResult dcbaa_map = anos_map_physical( + xhci_hcd->dcbaa_physical, + (void *)(0x600000000 + xhci_hcd->dcbaa_physical), VM_PAGE_SIZE, + ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | + ANOS_MAP_PHYSICAL_FLAG_NOCACHE); + + if (dcbaa_map.result != SYSCALL_OK) { + hcd_debugf("xHCI HCD: Failed to map DCBAA\n"); + return -1; + } + + xhci_hcd->dcbaa = + (volatile XhciDcbaa *)(0x600000000 + xhci_hcd->dcbaa_physical); + memset((void *)xhci_hcd->dcbaa, 0, sizeof(XhciDcbaa)); + + // Set DCBAAP register + xhci_write32(&xhci_hcd->base, xhci_hcd->base.op_regs, XHCI_OP_DCBAAP, + (uint32_t)(xhci_hcd->dcbaa_physical & 0xFFFFFFFF)); + xhci_write32(&xhci_hcd->base, xhci_hcd->base.op_regs, XHCI_OP_DCBAAP + 4, + (uint32_t)(xhci_hcd->dcbaa_physical >> 32)); + + // CRITICAL: Set CONFIG register with maximum device slots + // This MUST be set before any Enable Slot commands will work + uint32_t max_device_slots = xhci_hcd->base.max_slots; + if (max_device_slots > XHCI_MAX_DEVICES) { + max_device_slots = XHCI_MAX_DEVICES; + } + + hcd_vdebugf("xHCI HCD: Setting CONFIG register - MaxDeviceSlots=%u\n", + max_device_slots); + + xhci_write32(&xhci_hcd->base, xhci_hcd->base.op_regs, XHCI_OP_CONFIG, + max_device_slots); + +#ifdef DEBUG_XHCI_HCD +#ifdef VERY_NOISY_XHCI_HCD + const uint32_t config_readback = xhci_read32( + &xhci_hcd->base, xhci_hcd->base.op_regs, XHCI_OP_CONFIG); + hcd_vdebugf("xHCI HCD: CONFIG register readback: 0x%08x (should be %u)\n", + config_readback, max_device_slots); +#endif +#endif + + const uint32_t hccparams1 = xhci_read32( + &xhci_hcd->base, xhci_hcd->base.cap_regs, XHCI_CAP_HCCPARAMS1); + +#ifdef DEBUG_XHCI_HCD + uint32_t hccparams2 = xhci_read32(&xhci_hcd->base, xhci_hcd->base.cap_regs, + XHCI_CAP_HCCPARAMS2); + uint32_t hcsparams2 = xhci_read32(&xhci_hcd->base, xhci_hcd->base.cap_regs, + XHCI_CAP_HCSPARAMS2); + + printf("xHCI HCD: Capability Parameters Debug:\n"); + printf(" HCCPARAMS1: 0x%08x\n", hccparams1); + printf(" 64-bit addressing: %s\n", (hccparams1 & 0x1) ? "YES" : "NO"); + printf(" Bandwidth negotiation: %s\n", + (hccparams1 & 0x2) ? "YES" : "NO"); + printf(" Context size: %s\n", + (hccparams1 & 0x4) ? "64-byte" : "32-byte"); + printf(" Port power control: %s\n", (hccparams1 & 0x8) ? "YES" : "NO"); + printf(" Port indicators: %s\n", (hccparams1 & 0x10) ? "YES" : "NO"); + printf(" Light HC reset: %s\n", (hccparams1 & 0x20) ? "YES" : "NO"); + printf(" HCCPARAMS2: 0x%08x\n", hccparams2); + printf(" HCSPARAMS2: 0x%08x\n", hcsparams2); +#endif + + // Check extended capabilities pointer + const uint16_t xecp = get_xecp(hccparams1); + + hcd_vdebugf(" Extended capabilities pointer: 0x%04x\n", xecp); + + if (xecp != 0) { + hcd_vdebugf( + " WARNING: Controller has extended capabilities that may need " + "setup\n"); + + xhci_handle_extended_capability_setup(xhci_hcd, xecp); + } + + // Set command ring control register + xhci_set_command_ring_register(xhci_hcd); + + // Set up Event Ring Segment Table (ERST) + if (xhci_setup_event_ring(xhci_hcd) != 0) { + hcd_debugf("xHCI HCD: Failed to setup event ring\n"); + return -1; + } + + // Configure MSI interrupt (if available) + if (xhci_hcd->base.msi_cap_offset != 0) { + // PCIe ECAM format: Base + (Bus << 20) + (Device << 15) + (Function << 12) + const uint64_t pci_offset = pci_config_base - PCI_CONFIG_BASE_ADDRESS; + const uint8_t bus = (pci_offset >> 20) & 0xFF; + const uint8_t device = (pci_offset >> 15) & 0x1F; + const uint8_t function = (pci_offset >> 12) & 0x7; + const uint32_t bus_device_func = (bus << 16) | (device << 8) | function; + + uint64_t msi_address; + uint32_t msi_data; + + const SyscallResultU8 alloc_result = anos_alloc_interrupt_vector( + bus_device_func, &msi_address, &msi_data); + + if (alloc_result.result == SYSCALL_OK && alloc_result.value != 0) { + const uint8_t vector = alloc_result.value; + + // what type of capability do we have? + const uint8_t cap_id = + xhci_pci_read8(xhci_hcd->base.pci_config_virt, + xhci_hcd->base.msi_cap_offset); + + bool msi_configured = false; + if (cap_id == PCI_CAP_ID_MSIX) { + hcd_debugf("xHCI HCD: Configuring MSI-X interrupts\n"); + msi_configured = xhci_pci_configure_msix( + xhci_hcd->base.pci_config_virt, + xhci_hcd->base.msi_cap_offset, msi_address, msi_data, + (uint64_t)xhci_hcd->base.cap_regs); + } else if (cap_id == PCI_CAP_ID_MSI) { + hcd_debugf("xHCI HCD: Configuring MSI interrupts\n"); + msi_configured = xhci_pci_configure_msi( + xhci_hcd->base.pci_config_virt, + xhci_hcd->base.msi_cap_offset, msi_address, msi_data); + } + + if (msi_configured) { + xhci_hcd->base.msi_vector = vector; + xhci_hcd->base.msi_enabled = true; + hcd_debugf("xHCI HCD: MSI/MSI-X vector 0x%02x configured " + "successfully\n", + vector); + } else { + hcd_debugf("xHCI HCD: Failed to configure MSI/MSI-X hardware, " + "using polling\n"); + } + } else { + hcd_debugf("xHCI HCD: Failed to allocate interrupt vector, using " + "polling\n"); + } + } else { + hcd_debugf("xHCI HCD: No MSI capability found, using polling mode\n"); + } + + // Create USB host controller instance + if (xhci_init_usb_hcd(xhci_hcd) != 0) { + hcd_debugf("xHCI HCD: Failed to initialize USB host controller\n"); + return -1; + } + + hcd_debugf( + "xHCI HCD: Initialized with %u ports, command ring at 0x%016lx\n", + xhci_hcd->usb_hcd->num_ports, xhci_hcd->command_ring.trbs_physical); + + return 0; +} + +void xhci_hcd_shutdown(XhciHostController *xhci_hcd) { + if (!xhci_hcd) + return; + + // Clean up rings + xhci_ring_free(&xhci_hcd->command_ring); + xhci_ring_free(&xhci_hcd->event_ring); + + // Clean up USB host controller + if (xhci_hcd->usb_hcd) { + free(xhci_hcd->usb_hcd); + xhci_hcd->usb_hcd = nullptr; + } + + // TODO: Free device contexts and other allocated memory + + memset(xhci_hcd, 0, sizeof(XhciHostController)); +} + +// ============================================================================= +// USB Host Controller Operations Implementation +// ============================================================================= + +int xhci_hcd_submit_transfer(UsbHostController *hcd, UsbTransfer *transfer) { + XhciHostController *xhci_hcd = xhci_hcd_from_usb_hcd(hcd); + if (!xhci_hcd || !transfer) { + return -1; + } + + hcd_debugf( + "xHCI HCD: Submitting %s transfer to device %u endpoint 0x%02x\n", + (transfer->type == USB_TRANSFER_CONTROL) ? "control" + : (transfer->type == USB_TRANSFER_BULK) ? "bulk" + : (transfer->type == USB_TRANSFER_INTERRUPT) ? "interrupt" + : "isochronous", + transfer->device->address, transfer->endpoint); + + switch (transfer->type) { + case USB_TRANSFER_CONTROL: + return xhci_submit_control_transfer(xhci_hcd, transfer); + case USB_TRANSFER_BULK: + return xhci_submit_bulk_transfer(xhci_hcd, transfer); + case USB_TRANSFER_INTERRUPT: + return xhci_submit_interrupt_transfer(xhci_hcd, transfer); + default: + hcd_debugf("xHCI HCD: Unsupported transfer type %d\n", transfer->type); + return -1; + } +} + +int xhci_hcd_cancel_transfer(UsbHostController *hcd, UsbTransfer *transfer) { + XhciHostController *xhci_hcd = xhci_hcd_from_usb_hcd(hcd); + if (!xhci_hcd || !transfer) { + return -1; + } + + hcd_debugf("xHCI HCD: Cancelling transfer\n"); + + // TODO: Implement transfer cancellation + // Would need to send Stop Endpoint command and clean up transfer rings + + return 0; +} + +int xhci_hcd_set_address(UsbHostController *hcd, UsbDevice *device, + uint8_t address) { + XhciHostController *xhci_hcd = xhci_hcd_from_usb_hcd(hcd); + if (!xhci_hcd || !device) { + return -1; + } + + hcd_debugf("xHCI HCD: Setting address %u for device on port %u\n", address, + device->port_number); + + // Allocate device slot if not already done + // TODO still passing in an address, and ignoring it! + uint8_t slot_id = (uint8_t)(uintptr_t)device->hcd_private; + if (slot_id == 0) { + slot_id = xhci_alloc_device_slot(xhci_hcd); + if (slot_id == 0) { + hcd_debugf("xHCI HCD: Failed to allocate device slot\n"); + return -1; + } + device->hcd_private = (void *)(uintptr_t)slot_id; + } + + // Setup device context + if (xhci_setup_device_context(xhci_hcd, slot_id, device) != 0) { + hcd_debugf("xHCI HCD: Failed to setup device context\n"); + return -1; + } + + // Send Address Device command + XhciAddressDeviceTrb address_cmd = {0}; + address_cmd.input_context_ptr = xhci_hcd->input_context_physical[slot_id]; + address_cmd.control = TRB_CONTROL_CYCLE_BIT | + (TRB_TYPE_ADDRESS_DEVICE << TRB_CONTROL_TYPE_SHIFT) | + (slot_id << TRB_CONTROL_SLOT_ID_SHIFT); + + int result = xhci_send_command(xhci_hcd, (XhciTrb *)&address_cmd); + if (result != 0) { + hcd_debugf("xHCI HCD: Address Device command failed\n"); + return -1; + } + + // Wait for command completion + result = xhci_wait_for_command_completion(xhci_hcd, 5000); + if (result != 0) { + hcd_debugf("xHCI HCD: Address Device command timed out\n"); + return -1; + } + + hcd_debugf("xHCI HCD: Successfully set address %u\n", address); + return 0; +} + +int xhci_hcd_enable_device(UsbHostController *hcd, UsbDevice *device) { + XhciHostController *xhci_hcd = xhci_hcd_from_usb_hcd(hcd); + if (!xhci_hcd || !device) { + return -1; + } + + hcd_debugf("xHCI HCD: Enabling device on port %u\n", device->port_number); + + // Allocate device slot + const uint8_t slot_id = xhci_alloc_device_slot(xhci_hcd); + if (slot_id == 0) { + hcd_debugf("xHCI HCD: Failed to allocate device slot\n"); + return -1; + } + + device->hcd_private = (void *)(uintptr_t)slot_id; + + // Send Enable Slot command + XhciEnableSlotTrb enable_cmd = {0}; + enable_cmd.control = TRB_CONTROL_CYCLE_BIT | + (TRB_TYPE_ENABLE_SLOT << TRB_CONTROL_TYPE_SHIFT); + + hcd_debugf("xHCI HCD: Sending Enable Slot TRB: reserved1=0x%016lx " + "reserved2=0x%08x control=0x%08x\n", + enable_cmd.reserved1, enable_cmd.reserved2, enable_cmd.control); + + int result = xhci_send_command(xhci_hcd, (XhciTrb *)&enable_cmd); + if (result != 0) { + hcd_debugf("xHCI HCD: Enable Slot command failed\n"); + xhci_free_device_slot(xhci_hcd, slot_id); + return -1; + } + + result = xhci_wait_for_command_completion(xhci_hcd, 5000); + if (result != 0) { + hcd_debugf("xHCI HCD: Enable Slot command timed out\n"); + xhci_free_device_slot(xhci_hcd, slot_id); + return -1; + } + + hcd_debugf("xHCI HCD: Enable Slot completed for slot %u, setting up device " + "context\n", + slot_id); + + // Set up device context and endpoint 0 after successful Enable Slot + result = xhci_setup_device_context(xhci_hcd, slot_id, device); + if (result != 0) { + hcd_debugf("xHCI HCD: Failed to setup device context for slot %u\n", + slot_id); + xhci_free_device_slot(xhci_hcd, slot_id); + return -1; + } + + // Send Address Device command to configure endpoint 0 + XhciAddressDeviceTrb address_cmd = {0}; + address_cmd.input_context_ptr = xhci_hcd->input_context_physical[slot_id]; + address_cmd.control = TRB_CONTROL_CYCLE_BIT | + (TRB_TYPE_ADDRESS_DEVICE << TRB_CONTROL_TYPE_SHIFT) | + (slot_id << TRB_CONTROL_SLOT_ID_SHIFT); + + hcd_debugf("xHCI HCD: Sending Address Device command for slot %u\n", + slot_id); + result = xhci_send_command(xhci_hcd, (XhciTrb *)&address_cmd); + if (result != 0) { + hcd_debugf("xHCI HCD: Address Device command failed\n"); + xhci_free_device_slot(xhci_hcd, slot_id); + return -1; + } + + result = xhci_wait_for_command_completion(xhci_hcd, 5000); + if (result != 0) { + hcd_debugf("xHCI HCD: Address Device command timed out\n"); + xhci_free_device_slot(xhci_hcd, slot_id); + return -1; + } + + hcd_debugf("xHCI HCD: Device fully enabled in slot %u with endpoint 0 " + "configured\n", + slot_id); + return 0; +} + +int xhci_hcd_disable_device(UsbHostController *hcd, UsbDevice *device) { + XhciHostController *xhci_hcd = xhci_hcd_from_usb_hcd(hcd); + if (!xhci_hcd || !device) { + return -1; + } + + uint8_t slot_id = (uint8_t)(uintptr_t)device->hcd_private; + if (slot_id == 0) { + return 0; // Already disabled + } + + hcd_debugf("xHCI HCD: Disabling device in slot %u\n", slot_id); + + // TODO: Send Disable Slot command + xhci_free_device_slot(xhci_hcd, slot_id); + device->hcd_private = NULL; + + return 0; +} + +int xhci_hcd_reset_device(UsbHostController *hcd, UsbDevice *device) { + XhciHostController *xhci_hcd = xhci_hcd_from_usb_hcd(hcd); + if (!xhci_hcd || !device) { + return -1; + } + + uint8_t slot_id = (uint8_t)(uintptr_t)device->hcd_private; + hcd_debugf("xHCI HCD: Resetting device in slot %u (stub)\n", slot_id); + (void)slot_id; // Suppress unused variable warning for non-debug builds + + // TODO: Send Reset Device command + return 0; +} + +// ============================================================================= +// Port Management Operations +// ============================================================================= + +int xhci_hcd_get_port_status(UsbHostController *hcd, uint8_t port, + uint32_t *status) { + XhciHostController *xhci_hcd = xhci_hcd_from_usb_hcd(hcd); + if (!xhci_hcd || !status || port >= xhci_hcd->base.max_ports) { + return -1; + } + + const uint32_t port_offset = port * 0x10; + *status = + xhci_read32(&xhci_hcd->base, xhci_hcd->base.port_regs, port_offset); + + hcd_debugf("xHCI HCD: Port %u status: 0x%08x\n", port, *status); + return 0; +} + +int xhci_hcd_reset_port(UsbHostController *hcd, uint8_t port) { + XhciHostController *xhci_hcd = xhci_hcd_from_usb_hcd(hcd); + if (!xhci_hcd || port >= xhci_hcd->base.max_ports) { + return -1; + } + + hcd_debugf("xHCI HCD: Resetting port %u\n", port); + + const uint32_t port_offset = port * 0x10; + uint32_t portsc = + xhci_read32(&xhci_hcd->base, xhci_hcd->base.port_regs, port_offset); + + // Set port reset bit + portsc |= XHCI_PORTSC_PR; + xhci_write32(&xhci_hcd->base, xhci_hcd->base.port_regs, port_offset, + portsc); + + // TODO: Wait for reset completion + + return 0; +} + +int xhci_hcd_enable_port(UsbHostController *hcd, uint8_t port) { + hcd_debugf("xHCI HCD: Port enable not needed for xHCI (auto-enabled)\n"); + return 0; // xHCI ports are automatically enabled when device is connected +} + +int xhci_hcd_disable_port(UsbHostController *hcd, uint8_t port) { + hcd_debugf("xHCI HCD: Disabling port %u\n", port); + // TODO: Implement port disable if needed + return 0; +} + +// ============================================================================= +// Utility Functions +// ============================================================================= + +XhciHostController *xhci_hcd_from_usb_hcd(UsbHostController *hcd) { + if (!hcd || !hcd->private_data) { + return nullptr; + } + return (XhciHostController *)hcd->private_data; +} + +// ============================================================================= +// Stub Functions (To Be Implemented) +// ============================================================================= + +int xhci_hcd_configure_endpoint(UsbHostController *hcd, UsbDevice *device, + UsbEndpointDescriptor *ep_desc) { + hcd_debugf("xHCI HCD: Configure endpoint (stub)\n"); + return 0; // TODO: Implement endpoint configuration +} + +int xhci_send_command(XhciHostController *xhci_hcd, XhciTrb *command_trb) { + if (!xhci_hcd || !command_trb) { + return -1; + } + + hcd_debugf("xHCI HCD: Sending command TRB type %u\n", + TRB_GET_TYPE(command_trb)); + + // Verify controller is running before sending command +#ifdef DEBUG_XHCI_HCD +#ifdef VERY_NOISY_XHCI_HCD + uint32_t usbsts = xhci_read32(&xhci_hcd->base, xhci_hcd->base.op_regs, + XHCI_OP_USBSTS); + uint32_t usbcmd = xhci_read32(&xhci_hcd->base, xhci_hcd->base.op_regs, + XHCI_OP_USBCMD); + hcd_vdebugf("xHCI HCD: Controller status before command - USBSTS=0x%08x " + "USBCMD=0x%08x\n", + usbsts, usbcmd); + hcd_vdebugf(" HCH (Halted): %s, CNR (Not Ready): %s, Run bit: %s\n", + (usbsts & XHCI_STS_HCH) ? "YES" : "NO", + (usbsts & XHCI_STS_CNR) ? "YES" : "NO", + (usbcmd & XHCI_CMD_RUN) ? "YES" : "NO"); + + hcd_vdebugf(" Full USBSTS: 0x%08x\n", usbsts); + hcd_vdebugf(" HSE (Host System Error): %s\n", + (usbsts & (1 << 2)) ? "YES" : "NO"); + hcd_vdebugf(" EINT (Event Interrupt): %s\n", + (usbsts & (1 << 3)) ? "YES" : "NO"); + hcd_vdebugf(" PCD (Port Change Detect): %s\n", + (usbsts & (1 << 4)) ? "YES" : "NO"); + hcd_vdebugf(" SSS (Save State Status): %s\n", + (usbsts & (1 << 8)) ? "YES" : "NO"); + hcd_vdebugf(" RSS (Restore State Status): %s\n", + (usbsts & (1 << 9)) ? "YES" : "NO"); + hcd_vdebugf(" SRE (Save/Restore Error): %s\n", + (usbsts & (1 << 10)) ? "YES" : "NO"); + hcd_vdebugf(" CAS (Command Abort Status): %s\n", + (usbsts & (1 << 11)) ? "YES" : "NO"); +#endif + // Check DNCTRL register + uint32_t dnctrl = xhci_read32(&xhci_hcd->base, xhci_hcd->base.op_regs, + XHCI_OP_DNCTRL); + hcd_debugf(" DNCTRL (Device Notification Control): 0x%08x\n", dnctrl); +#endif + + // Enqueue the command TRB to the command ring + XhciTrb *ring_trb = xhci_ring_enqueue_trb(&xhci_hcd->command_ring); + if (!ring_trb) { + hcd_debugf("xHCI HCD: Failed to enqueue command TRB\n"); + return -1; + } + + // Copy the command TRB to the ring + *ring_trb = *command_trb; + + // CRITICAL: Ensure TRB write reaches memory before doorbell ring + // Memory barrier to ensure TRB is fully written to memory before doorbell + __asm__ volatile("mfence" ::: "memory"); + + // DEBUG: Verify TRB was actually written to memory + hcd_vdebugf("xHCI HCD: TRB written to ring[%u] - verifying...\n", + xhci_hcd->command_ring.enqueue_index); + hcd_vdebugf(" Written TRB: param=0x%016lx status=0x%08x control=0x%08x\n", + command_trb->parameter, command_trb->status, + command_trb->control); + hcd_vdebugf(" Ring TRB: param=0x%016lx status=0x%08x control=0x%08x\n", + ring_trb->parameter, ring_trb->status, ring_trb->control); + hcd_debugf(" Ring TRB address: %lx (physical: 0x%016lx + %u * %lu = " + "0x%016lx)\n", + (uintptr_t)ring_trb, xhci_hcd->command_ring.trbs_physical, + xhci_hcd->command_ring.enqueue_index, sizeof(XhciTrb), + xhci_hcd->command_ring.trbs_physical + + xhci_hcd->command_ring.enqueue_index * sizeof(XhciTrb)); + + // Advance the enqueue pointer + xhci_ring_inc_enqueue(&xhci_hcd->command_ring); +#ifdef DEBUG_XHCI_HCD + uint32_t old_enqueue = xhci_hcd->command_ring.enqueue_index; + hcd_debugf("xHCI HCD: Command ring - enqueue advanced from %u to %u, " + "cycle=%u\n", + old_enqueue, xhci_hcd->command_ring.enqueue_index, + xhci_hcd->command_ring.producer_cycle_state); +#endif + + // Ring the host controller doorbell (slot 0 = command ring) + hcd_debugf( + "xHCI HCD: Ringing doorbell - doorbell_regs=%p, writing 0x00000000 " + "to offset 0\n", + xhci_hcd->base.doorbell_regs); + + xhci_write32(&xhci_hcd->base, xhci_hcd->base.doorbell_regs, 0, 0); + + // Ensure doorbell write completes + __asm__ volatile("mfence" ::: "memory"); + +#ifdef DEBUG_XHCI_HCD +#ifdef VERY_NOISY_XHCI_HCD + // DEBUG: Try manual interrupt trigger to test event mechanism + printf("xHCI HCD: Testing interrupt mechanism...\n"); + uint32_t iman = + xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20); + printf(" IMAN (Interrupt Management): 0x%08x\n", iman); + + // Set Interrupt Pending bit manually to test if events work + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20, + iman | (1 << 0)); + + uint32_t iman_after = + xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20); + printf(" IMAN after setting IP bit: 0x%08x\n", iman_after); +#endif + + // Verify doorbell write (though doorbell registers are typically write-only) + uint32_t doorbell_readback = + xhci_read32(&xhci_hcd->base, xhci_hcd->base.doorbell_regs, 0); + printf("xHCI HCD: Doorbell readback: 0x%08x (may be 0 if write-only)\n", + doorbell_readback); + + hcd_debugf("xHCI HCD: Command TRB enqueued and doorbell rung\n"); +#endif + return 0; +} + +int xhci_wait_for_command_completion(XhciHostController *xhci_hcd, + const uint32_t timeout_ms) { + if (!xhci_hcd) { + return -1; + } + + hcd_debugf("xHCI HCD: Waiting for command completion (timeout %u ms)\n", + timeout_ms); + + // Use interrupt-driven processing if MSI is enabled + if (xhci_hcd->base.msi_enabled) { + hcd_debugf("xHCI HCD: Waiting for MSI interrupt on vector 0x%02x\n", + xhci_hcd->base.msi_vector); + + uint32_t event_data; + const SyscallResult result = + anos_wait_interrupt(xhci_hcd->base.msi_vector, &event_data); + + if (result.result == SYSCALL_OK) { + hcd_debugf("xHCI HCD: Received MSI interrupt (data=0x%08x)\n", + event_data); + + // Process the event ring to find command completion + return xhci_process_command_completion_event(xhci_hcd); + } else { + hcd_debugf("xHCI HCD: Failed to wait for interrupt (error=%ld)\n", + result.result); + return -1; + } + } + + // Fall back to polling mode if MSI is not enabled + hcd_vdebugf("xHCI HCD: Using polling mode, event ring @ %p (phys " + "0x%016lx)\n", + (void *)xhci_hcd->event_ring.trbs, + xhci_hcd->event_ring.trbs_physical); + hcd_vdebugf("xHCI HCD: Event ring enqueue=%u dequeue=%u cycle=%u\n", + xhci_hcd->event_ring.enqueue_index, + xhci_hcd->event_ring.dequeue_index, + xhci_hcd->event_ring.consumer_cycle_state); + +#ifdef DEBUG_XHCI_HCD_RINGS + printf("xHCI HCD: EINT flag indicates events present - scanning entire " + "event ring:\n"); + for (uint32_t scan_idx = 0; scan_idx < xhci_hcd->event_ring.size; + scan_idx++) { + volatile const XhciTrb *scan_trb = &xhci_hcd->event_ring.trbs[scan_idx]; + uint32_t control = scan_trb->control; + if (control != 0) { // Non-zero TRB found + uint32_t trb_type = TRB_GET_TYPE(scan_trb); + bool cycle = (control & TRB_CONTROL_CYCLE_BIT) != 0; + printf(" Ring[%u]: param=0x%016lx status=0x%08x control=0x%08x " + "(type=%u cycle=%u)\n", + scan_idx, scan_trb->parameter, scan_trb->status, control, + trb_type, cycle); + } + } +#endif + + for (uint32_t i = 0; i < timeout_ms; i++) { + // Check for events manually since hardware controls enqueue for event ring + volatile const XhciTrb *event_trb = + &xhci_hcd->event_ring.trbs[xhci_hcd->event_ring.dequeue_index]; + bool trb_cycle = (event_trb->control & TRB_CONTROL_CYCLE_BIT) != 0; + bool has_event = + (event_trb->control != 0) && + (trb_cycle == xhci_hcd->event_ring.consumer_cycle_state); + + if (has_event) { + const uint32_t trb_type = TRB_GET_TYPE(event_trb); + hcd_debugf( + "xHCI HCD: Found event TRB type %u at index %u (cycle=%u " + "expected=%u)\n", + trb_type, xhci_hcd->event_ring.dequeue_index, trb_cycle, + xhci_hcd->event_ring.consumer_cycle_state); + + if (trb_type == TRB_TYPE_COMMAND_COMPLETION) { + hcd_debugf("xHCI HCD: Command completed successfully\n"); + xhci_ring_inc_dequeue(&xhci_hcd->event_ring); + return 0; + } + xhci_ring_inc_dequeue(&xhci_hcd->event_ring); +#ifdef DEBUG_XHCI_HCD + } else { + // Show first few attempts to see if we're polling correctly + if (i < 10) { + printf("xHCI HCD: No event at attempt %u (dequeue=%u " + "cycle=%u)\n", + i, xhci_hcd->event_ring.dequeue_index, + xhci_hcd->event_ring.consumer_cycle_state); + } +#endif + } + + // Sleep for 1ms + anos_task_sleep_current(1000000); + } + + hcd_debugf("xHCI HCD: Command completion timeout after %u ms\n", + timeout_ms); + return -1; +} + +int xhci_wait_for_transfer_completion(XhciHostController *xhci_hcd, + const uint32_t timeout_ms) { + if (!xhci_hcd) { + return -1; + } + + hcd_debugf("xHCI HCD: Waiting for transfer completion (timeout %u ms)\n", + timeout_ms); + + // Use interrupt-driven processing if MSI is enabled + if (xhci_hcd->base.msi_enabled) { + hcd_debugf("xHCI HCD: Waiting for MSI interrupt on vector 0x%02x\n", + xhci_hcd->base.msi_vector); + + uint32_t event_data; + const SyscallResult result = + anos_wait_interrupt(xhci_hcd->base.msi_vector, &event_data); + + if (result.result == SYSCALL_OK) { + hcd_debugf("xHCI HCD: Received MSI interrupt (data=0x%08x)\n", + event_data); + + // Process the event ring to find transfer completion + return xhci_process_transfer_completion_event(xhci_hcd); + } else { + hcd_debugf("xHCI HCD: Failed to wait for interrupt (error=%ld)\n", + result.result); + return -1; + } + } + + // Fall back to polling mode if MSI is not enabled + +#ifdef DEBUG_XHCI_HCD_RINGS + printf("xHCI HCD: Scanning entire event ring for transfer events:\n"); + for (uint32_t scan_idx = 0; scan_idx < xhci_hcd->event_ring.size; + scan_idx++) { + volatile const XhciTrb *scan_trb = &xhci_hcd->event_ring.trbs[scan_idx]; + uint32_t control = scan_trb->control; + if (control != 0) { // Non-zero TRB found + uint32_t trb_type = TRB_GET_TYPE(scan_trb); + bool cycle = (control & TRB_CONTROL_CYCLE_BIT) != 0; + printf(" Ring[%u]: param=0x%016lx status=0x%08x " + "control=0x%08x (type=%u cycle=%u)\n", + scan_idx, scan_trb->parameter, scan_trb->status, control, + trb_type, cycle); + + if (trb_type == TRB_TYPE_TRANSFER) { + printf(" *** TRANSFER EVENT FOUND at index %u! ***\n", + scan_idx); + } + } + } +#endif + + // Process all pending events in ring order until we find a transfer event + hcd_debugf("xHCI HCD: Processing events sequentially from dequeue index %u " + "(cycle=%u)\n", + xhci_hcd->event_ring.dequeue_index, + xhci_hcd->event_ring.consumer_cycle_state); + + for (uint32_t attempts = 0; attempts < timeout_ms; attempts++) { + // Process all available events at current dequeue position + while (true) { + volatile const XhciTrb *event_trb = + &xhci_hcd->event_ring + .trbs[xhci_hcd->event_ring.dequeue_index]; + bool trb_cycle = (event_trb->control & TRB_CONTROL_CYCLE_BIT) != 0; + bool has_event = + (event_trb->control != 0) && + (trb_cycle == xhci_hcd->event_ring.consumer_cycle_state); + + if (!has_event) { + break; // No more events at current position + } + + const uint32_t trb_type = TRB_GET_TYPE(event_trb); + hcd_vdebugf("xHCI HCD: Processing event TRB type %u at index %u " + "(cycle=%u)\n", + trb_type, xhci_hcd->event_ring.dequeue_index, + trb_cycle); + + if (trb_type == TRB_TYPE_TRANSFER) { + hcd_debugf("xHCI HCD: Transfer event found - param=0x%016lx " + "status=0x%08x control=0x%08x\n", + event_trb->parameter, event_trb->status, + event_trb->control); + + // Check completion code + const uint32_t completion_code = + (event_trb->status >> 24) & 0xFF; + hcd_debugf("xHCI HCD: Transfer completion code: %u (%s)\n", + completion_code, + xhci_completion_code_string(completion_code)); + + if (completion_code == XHCI_COMP_SUCCESS) { + hcd_debugf("xHCI HCD: Transfer completed successfully!\n"); + xhci_ring_inc_dequeue(&xhci_hcd->event_ring); + return 0; + } else { + hcd_debugf("xHCI HCD: Transfer failed with completion code " + "%u\n", + completion_code); + xhci_ring_inc_dequeue(&xhci_hcd->event_ring); + return -1; + } + } else if (trb_type == TRB_TYPE_COMMAND_COMPLETION) { + hcd_vdebugf("xHCI HCD: Skipping command completion event " + "during transfer wait\n"); + xhci_ring_inc_dequeue(&xhci_hcd->event_ring); + } else { + hcd_vdebugf("xHCI HCD: Skipping unknown event type %u\n", + trb_type); + xhci_ring_inc_dequeue(&xhci_hcd->event_ring); + } + } + + // Sleep for 1ms before checking for new events + anos_task_sleep_current(1000000); + } + + hcd_debugf("xHCI HCD: Transfer completion timeout after %u ms\n", + timeout_ms); + return -1; +} + +int xhci_alloc_device_slot(XhciHostController *xhci_hcd) { + for (uint8_t i = 1; i < XHCI_MAX_DEVICES; i++) { + if (!xhci_hcd->slots_enabled[i]) { + xhci_hcd->slots_enabled[i] = true; + hcd_debugf("xHCI HCD: Allocated slot %u\n", i); + return i; + } + } + return 0; // No slots available +} + +void xhci_free_device_slot(XhciHostController *xhci_hcd, uint8_t slot_id) { + if (slot_id > 0 && slot_id < XHCI_MAX_DEVICES) { + xhci_hcd->slots_enabled[slot_id] = false; + hcd_debugf("xHCI HCD: Freed slot %u\n", slot_id); + } +} + +int xhci_setup_endpoint_ring(XhciHostController *xhci_hcd, uint8_t slot_id, + uint8_t endpoint_id) { + if (!xhci_hcd || slot_id == 0 || slot_id >= XHCI_MAX_DEVICES || + endpoint_id >= 32) { + return -1; + } + + // Allocate and initialize the endpoint transfer ring + xhci_hcd->endpoint_rings[slot_id][endpoint_id] = malloc(sizeof(XhciRing)); + if (!xhci_hcd->endpoint_rings[slot_id][endpoint_id]) { + hcd_debugf("xHCI HCD: Failed to allocate endpoint ring\n"); + return -1; + } + + XhciRing *ring = xhci_hcd->endpoint_rings[slot_id][endpoint_id]; + + if (xhci_ring_init(ring, XHCI_RING_SIZE) != 0) { + hcd_debugf("xHCI HCD: Failed to initialize endpoint ring\n"); + free(ring); + xhci_hcd->endpoint_rings[slot_id][endpoint_id] = nullptr; + return -1; + } + + hcd_debugf("xHCI HCD: Initialized endpoint ring for slot %u endpoint %u\n", + slot_id, endpoint_id); + return 0; +} + +int xhci_setup_device_context(XhciHostController *xhci_hcd, uint8_t slot_id, + UsbDevice *device) { + hcd_debugf("xHCI HCD: Setting up device context for slot %u\n", slot_id); + + if (!xhci_hcd || slot_id == 0 || slot_id >= XHCI_MAX_DEVICES || !device) { + return -1; + } + + // Allocate input context (1 page should be enough for slot + ep0 context) + const SyscallResultA input_ctx_alloc = + anos_alloc_physical_pages(VM_PAGE_SIZE); + if (input_ctx_alloc.result != SYSCALL_OK) { + hcd_debugf("xHCI HCD: Failed to allocate input context\n"); + return -1; + } + + // Map input context + SyscallResult input_ctx_map = anos_map_physical( + input_ctx_alloc.value, + (void *)(0x900000000 + input_ctx_alloc.value), VM_PAGE_SIZE, + ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | + ANOS_MAP_PHYSICAL_FLAG_NOCACHE); + + if (input_ctx_map.result != SYSCALL_OK) { + hcd_debugf("xHCI HCD: Failed to map input context\n"); + return -1; + } + + volatile uint8_t *input_context = + (volatile uint8_t *)(0x900000000 + input_ctx_alloc.value); + memset((void *)input_context, 0, VM_PAGE_SIZE); + + // Store the input context physical address + xhci_hcd->input_context_physical[slot_id] = input_ctx_alloc.value; + + // Set up Input Control Context (first 32 bytes) + volatile uint32_t *input_ctrl = (volatile uint32_t *)input_context; + input_ctrl[1] = + 0x3; // A0 and A1 bits - slot context and EP0 context are affected + + // Set up Slot Context (starts at offset 32) + volatile uint32_t *slot_context = (volatile uint32_t *)(input_context + 32); + + // Slot Context DW0: Route String and Speed + slot_context[0] = (device->speed << 20); // Speed in bits 23:20 + + // Slot Context DW1: Root Hub Port Number and Context Entries + slot_context[1] = ((device->port_number + 1) << 16) | + 1; // Port num (1-based) and 1 context entry (EP0) + + // Set up Endpoint 0 Context (starts at offset 64) + volatile uint32_t *ep0_context = (volatile uint32_t *)(input_context + 64); + + // EP0 Context DW0: Endpoint State and Type + ep0_context[0] = (4 << 3); // EP Type = Control (4), State = Disabled (0) + + // EP0 Context DW1: Max Packet Size + uint16_t max_packet_size = 64; + + switch (device->speed) { + case 1: + max_packet_size = 8; + break; // Low speed + case 2: + case 3: + max_packet_size = 64; + break; // Full speed or high speed + case 4: + max_packet_size = 512; + break; // Super speed + default: + // Unknown speed, leave size alone + // TODO should probably.... error out here, I guess? + max_packet_size = 64; + } + + // Max packet size and Error Count = 3 + ep0_context[1] = (max_packet_size << 16) | (3 << 1); + + // Set up endpoint 0 transfer ring + if (xhci_setup_endpoint_ring(xhci_hcd, slot_id, 0) != 0) { + hcd_debugf("xHCI HCD: Failed to setup endpoint 0 ring\n"); + return -1; + } + + // Set Transfer Ring Base Address (TRBA) in endpoint context - DW2/DW3 + const XhciRing *ep0_ring = xhci_hcd->endpoint_rings[slot_id][0]; + + ep0_context[2] = + (uint32_t)(ep0_ring->trbs_physical & 0xFFFFFFFF) | 1; // DCS = 1 + ep0_context[3] = (uint32_t)(ep0_ring->trbs_physical >> 32); + + // Allocate actual device context (separate from input context) + const SyscallResultA device_ctx_alloc = + anos_alloc_physical_pages(VM_PAGE_SIZE); + if (device_ctx_alloc.result != SYSCALL_OK) { + hcd_debugf("xHCI HCD: Failed to allocate device context\n"); + return -1; + } + + // Map device context + const SyscallResult device_ctx_map = anos_map_physical( + device_ctx_alloc.value, + (void *)(0xB00000000 + device_ctx_alloc.value), VM_PAGE_SIZE, + ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | + ANOS_MAP_PHYSICAL_FLAG_NOCACHE); + + if (device_ctx_map.result != SYSCALL_OK) { + hcd_debugf("xHCI HCD: Failed to map device context\n"); + return -1; + } + + // Store device context physical address + xhci_hcd->device_context_physical[slot_id] = device_ctx_alloc.value; + + // Update DCBAA to point to this device context + xhci_hcd->dcbaa->device_context_ptrs[slot_id] = device_ctx_alloc.value; + + hcd_debugf("xHCI HCD: Device context set up - slot %u, port %u, speed %u, " + "max_pkt %u\n", + slot_id, device->port_number, device->speed, max_packet_size); + hcd_debugf("xHCI HCD: EP0 ring @ phys 0x%016lx\n", ep0_ring->trbs_physical); + hcd_debugf( + "xHCI HCD: Device context @ phys 0x%016lx, DCBAA[%u] = 0x%016lx\n", + device_ctx_alloc.value, slot_id, + xhci_hcd->dcbaa->device_context_ptrs[slot_id]); + + return 0; +} + +int xhci_submit_control_transfer(XhciHostController *xhci_hcd, + UsbTransfer *transfer) { + if (!xhci_hcd || !transfer || !transfer->setup_packet) { + return -1; + } + + const UsbDeviceRequest *setup = transfer->setup_packet; + hcd_debugf( + "xHCI HCD: Control transfer - bmRequestType=0x%02x bRequest=0x%02x " + "wValue=0x%04x wIndex=0x%04x wLength=%u\n", + setup->bmRequestType, setup->bRequest, setup->wValue, setup->wIndex, + setup->wLength); + + // Get device slot ID + const uint8_t slot_id = (uint8_t)(uintptr_t)transfer->device->hcd_private; + if (slot_id == 0) { + hcd_debugf("xHCI HCD: No device slot allocated for control transfer\n"); + return -1; + } + + // Check if we have space in transfer ring for the transfer TRBs + uint32_t trbs_needed = 2; // Setup + Status + if (setup->wLength > 0) { + trbs_needed++; // + Data stage + } + + // Get endpoint 0's transfer ring + XhciRing *ep0_ring = xhci_hcd->endpoint_rings[slot_id][0]; + if (!ep0_ring) { + hcd_debugf("xHCI HCD: No endpoint ring for slot %u endpoint 0\n", + slot_id); + return -1; + } + + if (!xhci_ring_has_space(ep0_ring, trbs_needed)) { + hcd_debugf("xHCI HCD: Not enough space in endpoint transfer ring\n"); + return -1; + } + + // Build and enqueue TRBs for the control transfer + const int result = + xhci_build_control_transfer_trbs(xhci_hcd, transfer, slot_id); + if (result != 0) { + hcd_debugf("xHCI HCD: Failed to build control transfer TRBs\n"); + return -1; + } + + // Ring doorbell to notify hardware + hcd_debugf( + "xHCI HCD: Control transfer TRBs built, ringing doorbell for slot " + "%u EP0\n", + slot_id); + xhci_ring_doorbell(xhci_hcd, slot_id, 1); // DCI 1 = control endpoint + + // Wait for transfer completion + hcd_debugf("xHCI HCD: Waiting for transfer completion\n"); + const int completion_result = + xhci_wait_for_transfer_completion(xhci_hcd, 5000); + if (completion_result != 0) { + hcd_debugf("xHCI HCD: Transfer completion timeout\n"); + transfer->status = USB_TRANSFER_STATUS_ERROR; + return -1; + } + + // Copy data back for IN transfers after completion + if (setup->wLength > 0 && (setup->bmRequestType & 0x80) && + transfer->buffer) { + const uint64_t data_phys = (uint64_t)(uintptr_t)transfer->hcd_private; + if (data_phys) { + const volatile void *data_virt = + (volatile void *)(0x800000000 + data_phys); + memcpy(transfer->buffer, (const void *)data_virt, setup->wLength); + +#ifdef DEBUG_XHCI_HCD + hcd_debugf("xHCI HCD: Copied %u bytes back from device\n", + setup->wLength); + + // Debug: dump first few bytes of received data + hcd_debugf("xHCI HCD: Raw data received: "); + const uint8_t *data_bytes = (uint8_t *)transfer->buffer; + for (uint32_t i = 0; i < setup->wLength && i < 16; i++) { + hcd_debugf("%02x ", data_bytes[i]); + } + hcd_debugf("\n"); +#endif + } + } + + // Mark as completed after successful transfer + transfer->status = USB_TRANSFER_STATUS_COMPLETED; + transfer->actual_length = setup->wLength; + + hcd_debugf("xHCI HCD: Control transfer submitted\n"); + return 0; +} + +int xhci_submit_bulk_transfer(XhciHostController *xhci_hcd, + UsbTransfer *transfer) { + hcd_debugf("xHCI HCD: Submit bulk transfer (stub)\n"); + transfer->status = USB_TRANSFER_STATUS_COMPLETED; + return 0; +} + +int xhci_submit_interrupt_transfer(XhciHostController *xhci_hcd, + UsbTransfer *transfer) { + hcd_debugf("xHCI HCD: Submit interrupt transfer (stub)\n"); + transfer->status = USB_TRANSFER_STATUS_COMPLETED; + return 0; +} + +// ============================================================================= +// Control Transfer Implementation +// ============================================================================= + +int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, + UsbTransfer *transfer, + const uint8_t slot_id) { + + // TODO Holy shit this needs refactoring... + + if (!xhci_hcd || !transfer || !transfer->setup_packet) { + return -1; + } + + const UsbDeviceRequest *setup = transfer->setup_packet; + XhciRing *ring = + xhci_hcd->endpoint_rings[slot_id] + [0]; // Use endpoint 0's transfer ring + + // Allocate physical memory for setup packet + const SyscallResultA setup_alloc = + anos_alloc_physical_pages(VM_PAGE_SIZE); // 1 page + if (setup_alloc.result != SYSCALL_OK) { + hcd_debugf("xHCI HCD: Failed to allocate setup packet memory\n"); + return -1; + } + + // Map setup packet memory + const SyscallResult setup_map = anos_map_physical( + setup_alloc.value, (void *)(0x700000000 + setup_alloc.value), + VM_PAGE_SIZE, + ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | + ANOS_MAP_PHYSICAL_FLAG_NOCACHE); + + if (setup_map.result != SYSCALL_OK) { + hcd_debugf("xHCI HCD: Failed to map setup packet memory\n"); + return -1; + } + + // Copy setup packet to physical memory + volatile UsbDeviceRequest *setup_phys = + (volatile UsbDeviceRequest *)(0x700000000 + setup_alloc.value); + *setup_phys = *setup; + + // Debug: show what we're sending + hcd_debugf("xHCI HCD: Setup packet: %02x %02x %04x %04x %04x\n", + setup->bmRequestType, setup->bRequest, setup->wValue, + setup->wIndex, setup->wLength); + + hcd_vdebugf( + "xHCI HCD: Setup phys : %02x %02x %04x %04x %04x at 0x%016lx\n", + setup_phys->bmRequestType, setup_phys->bRequest, setup_phys->wValue, + setup_phys->wIndex, setup_phys->wLength, setup_alloc.value); + + // 1. Setup Stage TRB + XhciTrb *setup_trb = xhci_ring_enqueue_trb(ring); + if (!setup_trb) { + hcd_debugf("xHCI HCD: Failed to enqueue setup TRB\n"); + return -1; + } + + // Try immediate data mode - embed setup packet in TRB parameter field + uint64_t setup_data = 0; + memcpy(&setup_data, setup, 8); // Copy 8 bytes of setup packet + + setup_trb->parameter = setup_data; // Embed setup packet directly + setup_trb->status = 8; // Transfer Length = 8 bytes (immediate mode) + + setup_trb->control = TRB_CONTROL_CYCLE_BIT | + (TRB_TYPE_SETUP_STAGE << TRB_CONTROL_TYPE_SHIFT) | + TRB_CONTROL_IMMEDIATE_DATA; + + // Set transfer type in setup TRB based on data direction + if (setup->wLength > 0) { + if (setup->bmRequestType & 0x80) { + // Device to host - IN data stage + setup_trb->control |= (3 << 16); // TRT = IN data stage + } else { + // Host to device - OUT data stage + setup_trb->control |= (2 << 16); // TRT = OUT data stage + } + } else { + // No data stage + setup_trb->control |= (0 << 16); // TRT = No data stage + } + + xhci_ring_inc_enqueue(ring); + + // 2. Data Stage TRB (if needed) + if (setup->wLength > 0 && transfer->buffer) { + // Allocate and map data buffer + const size_t data_pages = + (setup->wLength + (VM_PAGE_SIZE - 1)) / VM_PAGE_SIZE; + const size_t data_size = data_pages * VM_PAGE_SIZE; + const SyscallResultA data_alloc = anos_alloc_physical_pages(data_size); + + if (data_alloc.result != SYSCALL_OK) { + hcd_debugf("xHCI HCD: Failed to allocate data buffer\n"); + return -1; + } + + const SyscallResult data_map = anos_map_physical( + data_alloc.value, (void *)(0x800000000 + data_alloc.value), + data_size, + ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | + ANOS_MAP_PHYSICAL_FLAG_NOCACHE); + + if (data_map.result != SYSCALL_OK) { + hcd_debugf("xHCI HCD: Failed to map data buffer\n"); + return -1; + } + + // Copy data if OUT transfer + if (!(setup->bmRequestType & 0x80) && transfer->buffer) { + memcpy((void *)(0x800000000 + data_alloc.value), transfer->buffer, + setup->wLength); + } + + XhciTrb *data_trb = xhci_ring_enqueue_trb(ring); + if (!data_trb) { + hcd_debugf("xHCI HCD: Failed to enqueue data TRB\n"); + return -1; + } + + data_trb->parameter = data_alloc.value; + data_trb->status = setup->wLength; + data_trb->control = TRB_CONTROL_CYCLE_BIT | + (TRB_TYPE_DATA_STAGE << TRB_CONTROL_TYPE_SHIFT); + + // Set direction bit + if (setup->bmRequestType & 0x80) { + data_trb->control |= (1 << 16); // DIR = IN + } + + xhci_ring_inc_enqueue(ring); + + // Store data buffer info for later copying back + transfer->hcd_private = (void *)(uintptr_t)data_alloc.value; + } + + // 3. Status Stage TRB + XhciTrb *status_trb = xhci_ring_enqueue_trb(ring); + if (!status_trb) { + hcd_debugf("xHCI HCD: Failed to enqueue status TRB\n"); + return -1; + } + + status_trb->parameter = 0; + status_trb->status = 0; + status_trb->control = TRB_CONTROL_CYCLE_BIT | + (TRB_TYPE_STATUS_STAGE << TRB_CONTROL_TYPE_SHIFT) | + TRB_CONTROL_INTERRUPT_ON_COMPLETE; + + // Status stage direction is opposite of data stage + if (setup->wLength > 0) { + if (setup->bmRequestType & 0x80) { + // Data was IN, status is OUT (no DIR bit) + } else { + // Data was OUT, status is IN + status_trb->control |= (1 << 16); // DIR = IN + } + } + + xhci_ring_inc_enqueue(ring); + + hcd_debugf("xHCI HCD: Built control transfer TRBs\n"); + return 0; +} + +void xhci_ring_doorbell(XhciHostController *xhci_hcd, uint8_t slot_id, + uint8_t target) { + if (!xhci_hcd || slot_id == 0) { + return; + } + + // Calculate doorbell register offset + const uint32_t doorbell_offset = slot_id * 4; + const uint32_t doorbell_value = target; // DCI (Doorbell Control Index) + + hcd_debugf("xHCI HCD: Ringing doorbell for slot %u, target %u " + "(offset=0x%x, value=0x%x)\n", + slot_id, target, doorbell_offset, doorbell_value); + + // Write to the actual doorbell register + if (xhci_hcd->base.doorbell_regs) { + xhci_write32(&xhci_hcd->base, xhci_hcd->base.doorbell_regs, + doorbell_offset, doorbell_value); + hcd_debugf("xHCI HCD: Doorbell written to hardware\n"); + } else { + hcd_debugf("xHCI HCD: ERROR - No doorbell registers mapped\n"); + } +} + +int xhci_setup_event_ring(XhciHostController *xhci_hcd) { + if (!xhci_hcd) { + return -1; + } + + hcd_debugf("xHCI HCD: Setting up event ring segment table\n"); + + // Allocate Event Ring Segment Table (ERST) + const SyscallResultA erst_alloc = anos_alloc_physical_pages(VM_PAGE_SIZE); + if (erst_alloc.result != SYSCALL_OK) { + hcd_debugf("xHCI HCD: Failed to allocate ERST\n"); + return -1; + } + + // Map ERST + const SyscallResult erst_map = anos_map_physical( + erst_alloc.value, (void *)(0xA00000000 + erst_alloc.value), + VM_PAGE_SIZE, + ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | + ANOS_MAP_PHYSICAL_FLAG_NOCACHE); + + if (erst_map.result != SYSCALL_OK) { + hcd_debugf("xHCI HCD: Failed to map ERST\n"); + return -1; + } + + // Set up ERST entry (16 bytes per entry) + volatile uint64_t *erst_entry = + (volatile uint64_t *)(0xA00000000 + erst_alloc.value); + erst_entry[0] = + xhci_hcd->event_ring.trbs_physical; // Ring Segment Base Address + erst_entry[1] = XHCI_RING_SIZE; // Ring Segment Size + + // Write ERST registers to runtime register space + if (!xhci_hcd->base.runtime_regs) { + hcd_debugf("xHCI HCD: ERROR - No runtime registers mapped\n"); + return -1; + } + + // Interrupter 0 registers (offset 0x20 from runtime base) + constexpr uint32_t interrupter_base = 0x20; + + // ERSTSZ (Event Ring Segment Table Size) - 1 segment + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x08, 1); + + // IMOD (Interrupt Moderation) - disable moderation for debugging + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x04, 0); // No moderation + hcd_debugf("xHCI HCD: Interrupt moderation disabled\n"); + + // Configure interrupt mode based on MSI availability + if (xhci_hcd->base.msi_enabled) { + hcd_debugf("xHCI HCD: Enabling interrupts for MSI vector 0x%02x\n", + xhci_hcd->base.msi_vector); + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x00, + 0x02); // IMAN = IE (Interrupt Enable) + } else { + hcd_debugf("xHCI HCD: Disabling interrupts - using polling mode\n"); + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x00, + 0); // IMAN = 0 (disable interrupts) + } + + // ERSTBA (Event Ring Segment Table Base Address) + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x10, + (uint32_t)(erst_alloc.value & 0xFFFFFFFF)); + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x14, (uint32_t)(erst_alloc.value >> 32)); + + // ERDP (Event Ring Dequeue Pointer) + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x18, + (uint32_t)(xhci_hcd->event_ring.trbs_physical & 0xFFFFFFFF)); + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x1C, + (uint32_t)(xhci_hcd->event_ring.trbs_physical >> 32)); + + hcd_debugf("xHCI HCD: Event ring configured - ERST at 0x%016lx, ring at " + "0x%016lx\n", + erst_alloc.value, xhci_hcd->event_ring.trbs_physical); + +#ifdef DEBUG_XHCI_HCD +#ifdef VERY_NOISY_XHCI_HCD + // Verify ERST registers were written correctly + uint32_t erstsz_read = + xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x08); + uint32_t erstba_low = + xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x10); + uint32_t erstba_high = + xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x14); + uint32_t erdp_low = + xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x18); + uint32_t erdp_high = + xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x1C); + + hcd_debugf("xHCI HCD: ERST registers verification:\n"); + hcd_debugf(" ERSTSZ: 0x%08x (should be 1)\n", erstsz_read); + hcd_debugf(" ERSTBA: 0x%08x%08x (should be 0x%016lx)\n", erstba_high, + erstba_low, erst_alloc.value); + hcd_debugf(" ERDP: 0x%08x%08x (should be 0x%016lx)\n", erdp_high, erdp_low, + xhci_hcd->event_ring.trbs_physical); + + // Verify CRCR (Command Ring Control Register) + uint32_t crcr_low = + xhci_read32(&xhci_hcd->base, xhci_hcd->base.op_regs, XHCI_OP_CRCR); + uint32_t crcr_high = xhci_read32(&xhci_hcd->base, xhci_hcd->base.op_regs, + XHCI_OP_CRCR + 4); + uint64_t expected_crcr = xhci_hcd->command_ring.trbs_physical | 0x1; + printf(" CRCR: 0x%08x%08x (should be 0x%016lx)\n", crcr_high, crcr_low, + expected_crcr); +#endif +#endif + + return 0; +} + +// ============================================================================= +// Interrupt-Driven Event Processing +// ============================================================================= + +int xhci_process_command_completion_event(XhciHostController *xhci_hcd) { + if (!xhci_hcd) { + return -1; + } + + hcd_debugf( + "xHCI HCD: Processing command completion event from interrupt\n"); + + xhci_clear_usbsts_interrupt(xhci_hcd); + + // Store initial dequeue index to update ERDP later + const uint32_t initial_dequeue = xhci_hcd->event_ring.dequeue_index; + + // Process all available events in the event ring + while (true) { + volatile const XhciTrb *event_trb = + &xhci_hcd->event_ring.trbs[xhci_hcd->event_ring.dequeue_index]; + bool trb_cycle = (event_trb->control & TRB_CONTROL_CYCLE_BIT) != 0; + bool has_event = + (event_trb->control != 0) && + (trb_cycle == xhci_hcd->event_ring.consumer_cycle_state); + + if (!has_event) { + break; // No more events available + } + + const uint32_t trb_type = TRB_GET_TYPE(event_trb); + + hcd_debugf("xHCI HCD: Processing event TRB type %u (%s)\n", trb_type, + xhci_trb_type_string(trb_type)); + + if (trb_type == TRB_TYPE_COMMAND_COMPLETION) { + const uint32_t completion_code = TRB_GET_COMPLETION_CODE(event_trb); + hcd_debugf("xHCI HCD: Command completion event (code %u: %s)\n", + completion_code, + xhci_completion_code_string(completion_code)); + + xhci_ring_inc_dequeue(&xhci_hcd->event_ring); + xhci_update_erdp(xhci_hcd, false); + xhci_clear_iman_interrupt(xhci_hcd); + + return (completion_code == XHCI_COMP_SUCCESS) ? 0 : -1; + } + + // Skip non-command-completion events + xhci_ring_inc_dequeue(&xhci_hcd->event_ring); + } + + // Even if no command completion found, we should acknowledge the interrupt + // Update ERDP if dequeue pointer changed + if (xhci_hcd->event_ring.dequeue_index != initial_dequeue) { + xhci_update_erdp(xhci_hcd, true); + } + + xhci_clear_iman_interrupt(xhci_hcd); + hcd_debugf("xHCI HCD: No command completion event found, but cleared " + "interrupt\n"); + + return -1; +} + +int xhci_process_transfer_completion_event(XhciHostController *xhci_hcd) { + if (!xhci_hcd) { + return -1; + } + + hcd_debugf( + "xHCI HCD: Processing transfer completion event from interrupt\n"); + + xhci_clear_usbsts_interrupt(xhci_hcd); + + // Store initial dequeue index to update ERDP later + const uint32_t initial_dequeue = xhci_hcd->event_ring.dequeue_index; + + // Process all available events in the event ring + while (true) { + volatile const XhciTrb *event_trb = + &xhci_hcd->event_ring.trbs[xhci_hcd->event_ring.dequeue_index]; + + const bool trb_cycle = + (event_trb->control & TRB_CONTROL_CYCLE_BIT) != 0; + const bool has_event = + (event_trb->control != 0) && + (trb_cycle == xhci_hcd->event_ring.consumer_cycle_state); + + if (!has_event) { + break; // No more events available + } + + const uint32_t trb_type = TRB_GET_TYPE(event_trb); + + hcd_debugf("xHCI HCD: Processing event TRB type %u (%s)\n", trb_type, + xhci_trb_type_string(trb_type)); + + if (trb_type == TRB_TYPE_TRANSFER) { + const uint32_t completion_code = TRB_GET_COMPLETION_CODE(event_trb); + hcd_debugf("xHCI HCD: Transfer completion event (code %u: %s)\n", + completion_code, + xhci_completion_code_string(completion_code)); + + xhci_ring_inc_dequeue(&xhci_hcd->event_ring); + xhci_update_erdp(xhci_hcd, true); + xhci_clear_iman_interrupt(xhci_hcd); + + return (completion_code == XHCI_COMP_SUCCESS) ? 0 : -1; + } + + // Skip non-transfer-completion events + xhci_ring_inc_dequeue(&xhci_hcd->event_ring); + } + + // Update ERDP if dequeue pointer changed + if (xhci_hcd->event_ring.dequeue_index != initial_dequeue) { + xhci_update_erdp(xhci_hcd, true); + } + + xhci_clear_iman_interrupt(xhci_hcd); + hcd_debugf("xHCI HCD: No transfer completion event found, but cleared " + "interrupt\n"); + + return -1; +} \ No newline at end of file diff --git a/servers/xhcidrv/xhci_rings.c b/servers/xhcidrv/xhci_rings.c new file mode 100644 index 00000000..11e2431d --- /dev/null +++ b/servers/xhcidrv/xhci_rings.c @@ -0,0 +1,290 @@ +/* + * xHCI Ring Management Implementation + * anos - An Operating System + * + * Copyright (c) 2025 Ross Bamford + */ + +#include +#include +#include +#include + +#include + +#include "include/xhci_rings.h" + +#ifdef DEBUG_XHCI_RINGS +#define ring_debugf(...) printf(__VA_ARGS__) +#else +#define ring_debugf(...) +#endif + +// ============================================================================= +// Ring Management +// ============================================================================= + +int xhci_ring_init(XhciRing *ring, const uint32_t size) { + if (!ring || size == 0 || size > 4096) { + return -1; + } + + memset(ring, 0, sizeof(XhciRing)); + ring->size = size; + + // Allocate physical memory for TRBs (must be page-aligned) + const size_t trb_array_size = size * sizeof(XhciTrb); + const size_t aligned_size = (trb_array_size + 0xFFF) & ~0xFFF; // Page align + + const SyscallResultA alloc_result = anos_alloc_physical_pages(aligned_size); + + if (alloc_result.result != SYSCALL_OK) { + printf("xHCI: Failed to allocate physical memory for ring - syscall " + "result: 0x%016lx\n", + (uint64_t)alloc_result.result); + printf("xHCI: Requested %lu pages (%lu bytes)\n", aligned_size / 4096, + aligned_size); + + return -1; + } + + ring->trbs_physical = alloc_result.value; + + // Map to virtual address space + const SyscallResult map_result = anos_map_physical( + ring->trbs_physical, + (void *)(0x500000000 + + ring->trbs_physical), // Simple virtual mapping + aligned_size, + ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | + ANOS_MAP_PHYSICAL_FLAG_NOCACHE); + + if (map_result.result != SYSCALL_OK) { + printf("xHCI: Failed to map ring memory\n"); + return -1; + } + + ring->trbs = (volatile XhciTrb *)(0x500000000 + ring->trbs_physical); + + // Initialize ring state + ring->enqueue_index = 0; + ring->dequeue_index = 0; + ring->cycle_state = 1; + ring->producer_cycle_state = 1; + ring->consumer_cycle_state = 1; + + // Clear all TRBs + memset((void *)ring->trbs, 0, size * sizeof(XhciTrb)); + + ring_debugf( + "xHCI: Initialized ring with %u TRBs at phys=0x%016lx virt=%p\n", + size, ring->trbs_physical, ring->trbs); + + return 0; +} + +void xhci_ring_free(XhciRing *ring) { + if (!ring || !ring->trbs) { + return; + } + + // TODO: Unmap virtual memory and free physical pages + ring->trbs = nullptr; + ring->trbs_physical = 0; + memset(ring, 0, sizeof(XhciRing)); +} + +XhciTrb *xhci_ring_enqueue_trb(XhciRing *ring) { + if (!ring || !ring->trbs) { + return nullptr; + } + + // Check if we have space (leave one slot for link TRB if needed) + const uint32_t next_enqueue = (ring->enqueue_index + 1) % ring->size; + + if (next_enqueue == ring->dequeue_index) { + ring_debugf("xHCI: Ring full, cannot enqueue TRB\n"); + return nullptr; + } + + XhciTrb *trb = (XhciTrb *)&ring->trbs[ring->enqueue_index]; + + // Set cycle bit for producer + trb->control = (trb->control & ~TRB_CONTROL_CYCLE_BIT) | + (ring->producer_cycle_state ? TRB_CONTROL_CYCLE_BIT : 0); + + ring_debugf("xHCI: Enqueuing TRB at index %u, cycle=%u\n", + ring->enqueue_index, ring->producer_cycle_state); + + return trb; +} + +volatile XhciTrb *xhci_ring_dequeue_trb(XhciRing *ring) { + if (!ring || !ring->trbs) { + return nullptr; + } + + // Check if ring is empty + if (ring->enqueue_index == ring->dequeue_index) { + return nullptr; + } + + volatile XhciTrb *trb = &ring->trbs[ring->dequeue_index]; + + // Check cycle bit + const bool trb_cycle = (trb->control & TRB_CONTROL_CYCLE_BIT) != 0; + if (trb_cycle != ring->consumer_cycle_state) { + return nullptr; // TRB not ready + } + + ring_debugf("xHCI: Dequeuing TRB at index %u, cycle=%u\n", + ring->dequeue_index, ring->consumer_cycle_state); + + return trb; +} + +bool xhci_ring_has_space(XhciRing *ring, uint32_t num_trbs) { + if (!ring) { + return false; + } + + uint32_t available_slots; + if (ring->enqueue_index >= ring->dequeue_index) { + available_slots = + ring->size - (ring->enqueue_index - ring->dequeue_index) - 1; + } else { + available_slots = ring->dequeue_index - ring->enqueue_index - 1; + } + + return available_slots >= num_trbs; +} + +void xhci_ring_inc_enqueue(XhciRing *ring) { + if (!ring) { + return; + } + + ring->enqueue_index = (ring->enqueue_index + 1) % ring->size; + ring->enqueued_count++; + + // Handle cycle bit wrap-around + if (ring->enqueue_index == 0) { + ring->producer_cycle_state = !ring->producer_cycle_state; + ring_debugf("xHCI: Producer cycle state flipped to %u\n", + ring->producer_cycle_state); + } +} + +void xhci_ring_inc_dequeue(XhciRing *ring) { + if (!ring) { + return; + } + + ring->dequeue_index = (ring->dequeue_index + 1) % ring->size; + ring->dequeued_count++; + + // Handle cycle bit wrap-around + if (ring->dequeue_index == 0) { + ring->consumer_cycle_state = !ring->consumer_cycle_state; + ring_debugf("xHCI: Consumer cycle state flipped to %u\n", + ring->consumer_cycle_state); + } +} + +// ============================================================================= +// Utility Functions +// ============================================================================= + +const char *xhci_trb_type_string(const uint32_t trb_type) { + switch (trb_type) { + case TRB_TYPE_NORMAL: + return "Normal"; + case TRB_TYPE_SETUP_STAGE: + return "Setup Stage"; + case TRB_TYPE_DATA_STAGE: + return "Data Stage"; + case TRB_TYPE_STATUS_STAGE: + return "Status Stage"; + case TRB_TYPE_ISOCH: + return "Isochronous"; + case TRB_TYPE_LINK: + return "Link"; + case TRB_TYPE_EVENT_DATA: + return "Event Data"; + case TRB_TYPE_NOOP: + return "No-Op"; + case TRB_TYPE_ENABLE_SLOT: + return "Enable Slot"; + case TRB_TYPE_DISABLE_SLOT: + return "Disable Slot"; + case TRB_TYPE_ADDRESS_DEVICE: + return "Address Device"; + case TRB_TYPE_CONFIGURE_ENDPOINT: + return "Configure Endpoint"; + case TRB_TYPE_EVALUATE_CONTEXT: + return "Evaluate Context"; + case TRB_TYPE_RESET_ENDPOINT: + return "Reset Endpoint"; + case TRB_TYPE_STOP_ENDPOINT: + return "Stop Endpoint"; + case TRB_TYPE_SET_TR_DEQUEUE: + return "Set TR Dequeue"; + case TRB_TYPE_RESET_DEVICE: + return "Reset Device"; + case TRB_TYPE_TRANSFER: + return "Transfer Event"; + case TRB_TYPE_COMMAND_COMPLETION: + return "Command Completion"; + case TRB_TYPE_PORT_STATUS_CHANGE: + return "Port Status Change"; + default: + return "Unknown"; + } +} + +const char *xhci_completion_code_string(const uint32_t completion_code) { + switch (completion_code) { + case XHCI_COMP_SUCCESS: + return "Success"; + case XHCI_COMP_DATA_BUFFER_ERROR: + return "Data Buffer Error"; + case XHCI_COMP_BABBLE_DETECTED: + return "Babble Detected"; + case XHCI_COMP_USB_TRANSACTION_ERROR: + return "USB Transaction Error"; + case XHCI_COMP_TRB_ERROR: + return "TRB Error"; + case XHCI_COMP_STALL_ERROR: + return "Stall Error"; + case XHCI_COMP_RESOURCE_ERROR: + return "Resource Error"; + case XHCI_COMP_BANDWIDTH_ERROR: + return "Bandwidth Error"; + case XHCI_COMP_NO_SLOTS_AVAILABLE: + return "No Slots Available"; + case XHCI_COMP_SLOT_NOT_ENABLED: + return "Slot Not Enabled"; + case XHCI_COMP_ENDPOINT_NOT_ENABLED: + return "Endpoint Not Enabled"; + case XHCI_COMP_SHORT_PACKET: + return "Short Packet"; + case XHCI_COMP_RING_UNDERRUN: + return "Ring Underrun"; + case XHCI_COMP_RING_OVERRUN: + return "Ring Overrun"; + case XHCI_COMP_PARAMETER_ERROR: + return "Parameter Error"; + case XHCI_COMP_CONTEXT_STATE_ERROR: + return "Context State Error"; + case XHCI_COMP_COMMAND_RING_STOPPED: + return "Command Ring Stopped"; + case XHCI_COMP_COMMAND_ABORTED: + return "Command Aborted"; + case XHCI_COMP_STOPPED: + return "Stopped"; + case XHCI_COMP_EVENT_RING_FULL: + return "Event Ring Full"; + default: + return "Unknown"; + } +} \ No newline at end of file