From ebd6dce835d07016042d9166c0d676941b66f0c2 Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Sat, 6 Sep 2025 19:58:13 +0100 Subject: [PATCH 01/21] First cut xHCI driver --- servers/Makefile | 10 +- servers/common/device_types.h | 27 +++- servers/pcidrv/enumerate.c | 51 ++++++ servers/pcidrv/main.c | 80 ++++++++- servers/xhcidrv/Makefile | 15 ++ servers/xhcidrv/include/pci.h | 38 +++++ servers/xhcidrv/include/xhci.h | 145 +++++++++++++++++ servers/xhcidrv/main.c | 262 ++++++++++++++++++++++++++++++ servers/xhcidrv/pci.c | 81 ++++++++++ servers/xhcidrv/xhci.c | 285 +++++++++++++++++++++++++++++++++ 10 files changed, 989 insertions(+), 5 deletions(-) create mode 100644 servers/xhcidrv/Makefile create mode 100644 servers/xhcidrv/include/pci.h create mode 100644 servers/xhcidrv/include/xhci.h create mode 100644 servers/xhcidrv/main.c create mode 100644 servers/xhcidrv/pci.c create mode 100644 servers/xhcidrv/xhci.c diff --git a/servers/Makefile b/servers/Makefile index fd59d946..ebabc22d 100644 --- a/servers/Makefile +++ b/servers/Makefile @@ -7,13 +7,14 @@ 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 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 $(RM) system.ramfs @@ -33,6 +34,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,8 +44,8 @@ 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 + $(MKRAMFS_BIN) system.ramfs test_server/test_server.elf devman/devman.elf pcidrv/pcidrv.elf ahcidrv/ahcidrv.elf xhcidrv/xhcidrv.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 $(MKRAMFS_BIN) system.ramfs test_server/test_server.elf kterminal/kterminal.elf devman/devman.elf ../system/sysconf.json 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/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..9796e888 100644 --- a/servers/pcidrv/main.c +++ b/servers/pcidrv/main.c @@ -112,6 +112,84 @@ 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]}, + }; + +#ifdef DEBUG_BUS_DRIVER_INIT + printf(" --> spawn: %s %s\n", argv[0], argv[1]); +#endif + + const int64_t pid = + spawn_process_via_system(0x100000, 14, 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 +291,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/xhcidrv/Makefile b/servers/xhcidrv/Makefile new file mode 100644 index 00000000..523cb74b --- /dev/null +++ b/servers/xhcidrv/Makefile @@ -0,0 +1,15 @@ +SERVER_NAME=xhcidrv +BINARY=xhcidrv +BINARY_OBJS=main.o xhci.o pci.o + +# 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 +# +CDEFS= + +include ../server_common.mk \ No newline at end of file diff --git a/servers/xhcidrv/include/pci.h b/servers/xhcidrv/include/pci.h new file mode 100644 index 00000000..3481ba75 --- /dev/null +++ b/servers/xhcidrv/include/pci.h @@ -0,0 +1,38 @@ +/* + * xhcidrv - PCI interface for xHCI driver + * anos - An Operating System + * + * Copyright (c) 2025 Ross Bamford + */ + +#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) + +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); + +#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..18cde0b5 --- /dev/null +++ b/servers/xhcidrv/include/xhci.h @@ -0,0 +1,145 @@ +/* + * 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 + +// 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 + +// xHCI Controller structure +typedef struct { + uint64_t base_addr; // Base address of xHCI registers + uint64_t pci_config_base; // PCI config space base + 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 + + 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(XHCIController *controller); +bool xhci_controller_start(XHCIController *controller); +bool xhci_controller_stop(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/main.c b/servers/xhcidrv/main.c new file mode 100644 index 00000000..152bb073 --- /dev/null +++ b/servers/xhcidrv/main.c @@ -0,0 +1,262 @@ +/* + * xHCI Driver Server + * anos - An Operating System + * + * Copyright (c) 2025 Ross Bamford + */ + +#include +#include +#include +#include + +#include + +#include "../common/device_types.h" +#include "include/xhci.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 XHCIController controller = {0}; +static XHCIPort ports[16] = {{0}}; // Support up to 16 ports +static uint64_t devman_channel = 0; +static uint64_t xhci_channel = 0; +static uint64_t pci_parent_id = 0; + +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 + + if (!xhci_controller_init(&controller, xhci_base, pci_config_base)) { + printf("Failed to initialize xHCI controller\n"); + return -1; + } + +#ifdef DEBUG_XHCI_INIT + printf("xHCI controller initialized successfully\n"); + printf(" HCI Version: 0x%04x\n", controller.hci_version); + printf(" Max Ports: %u\n", controller.max_ports); + printf(" Max Slots: %u\n", controller.max_slots); +#endif + + // Start the controller + if (!xhci_controller_start(&controller)) { + printf("Failed to start xHCI controller\n"); + return -1; + } + + // Initialize and scan all ports + controller.active_ports = 0; + for (uint8_t i = 0; i < controller.max_ports && i < 16; i++) { + if (xhci_port_init(&ports[i], &controller, i)) { + controller.active_ports |= (1U << i); +#ifdef DEBUG_XHCI_INIT + printf("Port %u initialized - device connected\n", i); +#endif + } + } + +#ifdef DEBUG_XHCI_INIT + printf("Active ports: 0x%08x\n", controller.active_ports); +#endif + + return 0; +} + +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; + 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"); + + 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 each connected USB device + for (uint8_t i = 0; i < controller.max_ports && i < 16; i++) { + if ((controller.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); + + 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); + } + } + } + + return true; +} + +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); +} + +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], NULL, 16); + const uint64_t pci_config_base = strtoull(argv[2], NULL, 16); + pci_parent_id = strtoull(argv[3], NULL, 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"); + + // Main message loop + 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); + } + } +} \ No newline at end of file diff --git a/servers/xhcidrv/pci.c b/servers/xhcidrv/pci.c new file mode 100644 index 00000000..2e84956e --- /dev/null +++ b/servers/xhcidrv/pci.c @@ -0,0 +1,81 @@ +/* + * 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 + +uint32_t xhci_pci_read32(const uint64_t config_base, const uint8_t offset) { + 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) { + 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) { + 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; +} \ No newline at end of file diff --git a/servers/xhcidrv/xhci.c b/servers/xhcidrv/xhci.c new file mode 100644 index 00000000..388c4113 --- /dev/null +++ b/servers/xhcidrv/xhci.c @@ -0,0 +1,285 @@ +/* + * 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" + +#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 PCI_CONFIG_BASE_ADDRESS 0xC000000000ULL +#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) { + 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 + const uint64_t mapped_pci_config = PCI_CONFIG_BASE_ADDRESS; + + if (!xhci_pci_enable_device(mapped_pci_config)) { + printf("Failed to enable xHCI PCI device\n"); + return false; + } + + // 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) + const size_t map_size = 64 * 1024; + + init_debugf("Mapping %zu 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_vdebugf("Doorbell registers at offset 0x%x\n", dboff); + init_vdebugf("Runtime registers at offset 0x%x\n", rtsoff); + + // 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(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; + } + anos_task_sleep_current_secs(1); + } + + printf("xHCI controller reset timeout\n"); + return false; +} + +bool xhci_controller_start(XHCIController *controller) { + init_debugf("Starting xHCI controller...\n"); + + // Set the run bit + uint32_t usbcmd = + xhci_read32(controller, controller->op_regs, XHCI_OP_USBCMD); + usbcmd |= XHCI_CMD_RUN | XHCI_CMD_INTE; + xhci_write32(controller, controller->op_regs, XHCI_OP_USBCMD, usbcmd); + + // 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(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; + const 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; + + if (port->connected) { + port->speed = (portsc & XHCI_PORTSC_SPEED_MASK) >> 10; + init_vdebugf("Port %u: Device connected (speed: %u)\n", port->port_num, + port->speed); + + // For now, create generic device info + port->vendor_id = 0x0000; // Unknown + port->product_id = 0x0000; // Unknown + port->device_class = 0x09; // Hub class as default + snprintf(port->manufacturer, sizeof(port->manufacturer), "Unknown"); + snprintf(port->product, sizeof(port->product), "USB Device on Port %u", + port->port_num); + snprintf(port->serial_number, sizeof(port->serial_number), "PORT%u", + port->port_num); + + 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 From 894714c7eacdd53d0d0a599776c6fb874e46d462 Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Sat, 6 Sep 2025 20:06:24 +0100 Subject: [PATCH 02/21] Debug info --- servers/Makefile | 6 +++--- servers/xhcidrv/xhci.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/servers/Makefile b/servers/Makefile index ebabc22d..05663cb7 100644 --- a/servers/Makefile +++ b/servers/Makefile @@ -44,9 +44,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 xhcidrv/xhcidrv.elf - $(MKRAMFS_BIN) system.ramfs test_server/test_server.elf devman/devman.elf pcidrv/pcidrv.elf ahcidrv/ahcidrv.elf xhcidrv/xhcidrv.elf fat32drv/fat32drv.elf kterminal/kterminal.elf xhcidrv/xhcidrv.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/xhcidrv/xhci.c b/servers/xhcidrv/xhci.c index 388c4113..eee3330b 100644 --- a/servers/xhcidrv/xhci.c +++ b/servers/xhcidrv/xhci.c @@ -81,7 +81,7 @@ bool xhci_controller_init(XHCIController *controller, const uint64_t base_addr, // For now, map a standard amount (64KB should be sufficient for most xHCI controllers) const size_t map_size = 64 * 1024; - init_debugf("Mapping %zu bytes of xHCI register space...\n", map_size); + 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, From d85b38371fee4a4370da8d204bd6d7f7627513c9 Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Sat, 6 Sep 2025 20:18:41 +0100 Subject: [PATCH 03/21] xHCI port enumeration --- servers/xhcidrv/xhci.c | 70 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/servers/xhcidrv/xhci.c b/servers/xhcidrv/xhci.c index eee3330b..76e98348 100644 --- a/servers/xhcidrv/xhci.c +++ b/servers/xhcidrv/xhci.c @@ -260,20 +260,66 @@ bool xhci_port_scan(XHCIPort *port) { port->connected = (portsc & XHCI_PORTSC_CCS) != 0; port->enabled = (portsc & XHCI_PORTSC_PED) != 0; + init_vdebugf("Port %u: PORTSC=0x%08x\n", port->port_num, portsc); + if (port->connected) { port->speed = (portsc & XHCI_PORTSC_SPEED_MASK) >> 10; - init_vdebugf("Port %u: Device connected (speed: %u)\n", port->port_num, - port->speed); - - // For now, create generic device info - port->vendor_id = 0x0000; // Unknown - port->product_id = 0x0000; // Unknown - port->device_class = 0x09; // Hub class as default - snprintf(port->manufacturer, sizeof(port->manufacturer), "Unknown"); - snprintf(port->product, sizeof(port->product), "USB Device on Port %u", - port->port_num); - snprintf(port->serial_number, sizeof(port->serial_number), "PORT%u", - port->port_num); + + 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"); + + // Create device info based on detected characteristics + port->vendor_id = + 0x0000; // Would need USB enumeration to get real VID/PID + port->product_id = 0x0000; + + // Guess device class based on speed (rough heuristic) + switch (port->speed) { + case XHCI_SPEED_LOW: + port->device_class = 0x03; // HID (keyboards, mice often low speed) + break; + case XHCI_SPEED_FULL: + case XHCI_SPEED_HIGH: + port->device_class = 0x08; // Mass Storage (common for USB drives) + break; + case XHCI_SPEED_SUPER: + port->device_class = 0x08; // Mass Storage (USB 3.0 drives) + break; + default: + port->device_class = 0x00; // Unknown + break; + } + + 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; From c4abb22999587e0e5dd8419a39cb52a48653b135 Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Sat, 6 Sep 2025 22:37:08 +0100 Subject: [PATCH 04/21] Fix tests / tidy up a bit --- servers/pcidrv/tests/pci.c | 8 ++++++++ servers/xhcidrv/include/xhci.h | 6 +++--- servers/xhcidrv/pci.c | 6 +++--- servers/xhcidrv/xhci.c | 22 +++++++++++++++++----- 4 files changed, 31 insertions(+), 11 deletions(-) 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/include/xhci.h b/servers/xhcidrv/include/xhci.h index 18cde0b5..3dc57719 100644 --- a/servers/xhcidrv/include/xhci.h +++ b/servers/xhcidrv/include/xhci.h @@ -127,9 +127,9 @@ typedef struct { // Function prototypes bool xhci_controller_init(XHCIController *controller, uint64_t base_addr, uint64_t pci_config_base); -bool xhci_controller_reset(XHCIController *controller); -bool xhci_controller_start(XHCIController *controller); -bool xhci_controller_stop(XHCIController *controller); +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); diff --git a/servers/xhcidrv/pci.c b/servers/xhcidrv/pci.c index 2e84956e..bfcf1a0a 100644 --- a/servers/xhcidrv/pci.c +++ b/servers/xhcidrv/pci.c @@ -18,7 +18,7 @@ #endif uint32_t xhci_pci_read32(const uint64_t config_base, const uint8_t offset) { - volatile uint32_t *reg = (volatile uint32_t *)(config_base + 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); @@ -26,7 +26,7 @@ uint32_t xhci_pci_read32(const uint64_t config_base, const uint8_t offset) { } uint16_t xhci_pci_read16(const uint64_t config_base, const uint8_t offset) { - volatile uint16_t *reg = (volatile uint16_t *)(config_base + 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); @@ -34,7 +34,7 @@ uint16_t xhci_pci_read16(const uint64_t config_base, const uint8_t offset) { } uint8_t xhci_pci_read8(const uint64_t config_base, const uint8_t offset) { - volatile uint8_t *reg = (volatile uint8_t *)(config_base + 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); diff --git a/servers/xhcidrv/xhci.c b/servers/xhcidrv/xhci.c index 76e98348..54e48247 100644 --- a/servers/xhcidrv/xhci.c +++ b/servers/xhcidrv/xhci.c @@ -15,6 +15,18 @@ #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 @@ -67,7 +79,7 @@ bool xhci_controller_init(XHCIController *controller, const uint64_t base_addr, } // Update PCI access to use mapped virtual address - const uint64_t mapped_pci_config = PCI_CONFIG_BASE_ADDRESS; + constexpr uint64_t mapped_pci_config = PCI_CONFIG_BASE_ADDRESS; if (!xhci_pci_enable_device(mapped_pci_config)) { printf("Failed to enable xHCI PCI device\n"); @@ -79,7 +91,7 @@ bool xhci_controller_init(XHCIController *controller, const uint64_t base_addr, xhci_pci_read32(mapped_pci_config, PCI_BAR0)); // For now, map a standard amount (64KB should be sufficient for most xHCI controllers) - const size_t map_size = 64 * 1024; + constexpr size_t map_size = 64 * 1024; init_debugf("Mapping %lu bytes of xHCI register space...\n", map_size); @@ -164,7 +176,7 @@ bool xhci_controller_init(XHCIController *controller, const uint64_t base_addr, return true; } -bool xhci_controller_reset(XHCIController *controller) { +bool xhci_controller_reset(const XHCIController *controller) { init_debugf("Resetting xHCI controller...\n"); // Set the reset bit @@ -186,7 +198,7 @@ bool xhci_controller_reset(XHCIController *controller) { return false; } -bool xhci_controller_start(XHCIController *controller) { +bool xhci_controller_start(const XHCIController *controller) { init_debugf("Starting xHCI controller...\n"); // Set the run bit @@ -210,7 +222,7 @@ bool xhci_controller_start(XHCIController *controller) { return false; } -bool xhci_controller_stop(XHCIController *controller) { +bool xhci_controller_stop(const XHCIController *controller) { init_debugf("Stopping xHCI controller...\n"); // Clear the run bit From eeb1fe393c15a04b9980f4d6469c40f983df098d Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Sun, 7 Sep 2025 10:10:51 +0100 Subject: [PATCH 05/21] Add USB core lib --- servers/common/usb/Makefile | 15 ++ servers/common/usb/usb_core.c | 481 ++++++++++++++++++++++++++++++++++ servers/common/usb/usb_core.h | 207 +++++++++++++++ servers/common/usb/usb_spec.h | 191 ++++++++++++++ 4 files changed, 894 insertions(+) create mode 100644 servers/common/usb/Makefile create mode 100644 servers/common/usb/usb_core.c create mode 100644 servers/common/usb/usb_core.h create mode 100644 servers/common/usb/usb_spec.h diff --git a/servers/common/usb/Makefile b/servers/common/usb/Makefile new file mode 100644 index 00000000..e5f226ac --- /dev/null +++ b/servers/common/usb/Makefile @@ -0,0 +1,15 @@ +USB_COMMON_NAME=usbcore +BINARY=$(USB_COMMON_NAME) +BINARY_OBJS=usb_core.o + +# USB core library build +CDEFS= + +include ../../server_common.mk + +# Override default target to create a library instead of executable +$(BINARY_NAME): $(BINARY_OBJS) + $(XAR) rcs lib$(USB_COMMON_NAME).a $(BINARY_OBJS) + +clean: + rm -f *.o *.a *.elf \ 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..6cb2ccc0 --- /dev/null +++ b/servers/common/usb/usb_core.c @@ -0,0 +1,481 @@ +/* + * 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" + +// ============================================================================= +// 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) { + printf("USB: Maximum number of host controllers reached\n"); + return -1; + } + + registered_hcds[num_registered_hcds++] = hcd; + printf("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; + printf("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) { + printf("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 + + printf("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; + } + + printf("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) { + printf("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) { + printf("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) { + printf("USB: Failed to get full device descriptor\n"); + return -1; + } + + printf("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) { + usb_get_string_descriptor(device, device->device_desc.iManufacturer, + device->manufacturer, + sizeof(device->manufacturer)); + } + + if (device->device_desc.iProduct) { + usb_get_string_descriptor(device, device->device_desc.iProduct, + device->product, sizeof(device->product)); + } + + if (device->device_desc.iSerialNumber) { + usb_get_string_descriptor(device, device->device_desc.iSerialNumber, + device->serial_number, + sizeof(device->serial_number)); + } + + return 0; +} + +int usb_configure_device(UsbDevice *device, 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) { + printf("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) { + printf("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) { + printf("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) { + printf("USB: Failed to set configuration %u\n", config_value); + return -1; + } + + device->state = USB_DEVICE_STATE_CONFIGURED; + printf("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) { + printf("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]; + 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) + uint8_t length = string_desc[0]; + if (length < 2) + return -1; + + size_t str_len = (length - 2) / 2; // UTF-16 string length + 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; + printf("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; + printf("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 From 6235e070147a754903831d174e491f2c98bd8874 Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Sun, 7 Sep 2025 10:11:24 +0100 Subject: [PATCH 06/21] USB core for enum (stubbed currently) --- servers/Makefile | 1 + servers/pcidrv/main.c | 5 +- servers/xhcidrv/Makefile | 17 +- servers/xhcidrv/include/xhci_hcd.h | 154 +++++++++ servers/xhcidrv/include/xhci_rings.h | 274 ++++++++++++++++ servers/xhcidrv/main.c | 62 +++- servers/xhcidrv/xhci_hcd.c | 467 +++++++++++++++++++++++++++ servers/xhcidrv/xhci_rings.c | 284 ++++++++++++++++ 8 files changed, 1246 insertions(+), 18 deletions(-) create mode 100644 servers/xhcidrv/include/xhci_hcd.h create mode 100644 servers/xhcidrv/include/xhci_rings.h create mode 100644 servers/xhcidrv/xhci_hcd.c create mode 100644 servers/xhcidrv/xhci_rings.c diff --git a/servers/Makefile b/servers/Makefile index 05663cb7..83ab5f82 100644 --- a/servers/Makefile +++ b/servers/Makefile @@ -17,6 +17,7 @@ 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) diff --git a/servers/pcidrv/main.c b/servers/pcidrv/main.c index 9796e888..1fe32e78 100644 --- a/servers/pcidrv/main.c +++ b/servers/pcidrv/main.c @@ -173,6 +173,9 @@ void spawn_xhci_driver(const uint64_t xhci_base, const uint64_t pci_config_base, {.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 @@ -180,7 +183,7 @@ void spawn_xhci_driver(const uint64_t xhci_base, const uint64_t pci_config_base, #endif const int64_t pid = - spawn_process_via_system(0x100000, 14, xhci_caps, 4, argv); + 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); diff --git a/servers/xhcidrv/Makefile b/servers/xhcidrv/Makefile index 523cb74b..9141581e 100644 --- a/servers/xhcidrv/Makefile +++ b/servers/xhcidrv/Makefile @@ -1,6 +1,10 @@ SERVER_NAME=xhcidrv BINARY=xhcidrv -BINARY_OBJS=main.o xhci.o pci.o +BINARY_OBJS=main.o xhci.o pci.o xhci_rings.o xhci_hcd.o + +# 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 # @@ -12,4 +16,13 @@ BINARY_OBJS=main.o xhci.o pci.o # CDEFS= -include ../server_common.mk \ No newline at end of file +include ../server_common.mk + +# Build USB core library first +$(USB_CORE_DIR)/libusbcore.a: + $(MAKE) -C $(USB_CORE_DIR) + +# Override linking to include USB core library +$(BINARY_NAME): $(BINARY_OBJS) $(USB_CORE_DIR)/libusbcore.a + $(XCC) -o $@ $(BINARY_OBJS) $(LDFLAGS) + chmod a-x $@ \ 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..96d73b5b --- /dev/null +++ b/servers/xhcidrv/include/xhci_hcd.h @@ -0,0 +1,154 @@ +/* + * 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 "../../common/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 + XhciDcbaa *dcbaa; // Device Context Base Address Array + 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); + +// 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_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..a1181340 --- /dev/null +++ b/servers/xhcidrv/include/xhci_rings.h @@ -0,0 +1,274 @@ +/* + * 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_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 { + XhciTrb *trbs; // TRB array (page-aligned) + 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); +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 index 152bb073..33cd0bff 100644 --- a/servers/xhcidrv/main.c +++ b/servers/xhcidrv/main.c @@ -13,7 +13,9 @@ #include #include "../common/device_types.h" +#include "../common/usb/usb_core.h" #include "include/xhci.h" +#include "include/xhci_hcd.h" #ifndef VERSTR #warning Version String not defined (-DVERSTR); Using default @@ -36,7 +38,7 @@ #define ops_vdebugf(...) #endif -static XHCIController controller = {0}; +static XhciHostController xhci_hcd = {0}; static XHCIPort ports[16] = {{0}}; // Support up to 16 ports static uint64_t devman_channel = 0; static uint64_t xhci_channel = 0; @@ -50,29 +52,59 @@ static int xhci_initialize_driver(const uint64_t xhci_base, printf(" PCI Config Base: 0x%016lx\n", pci_config_base); #endif - if (!xhci_controller_init(&controller, xhci_base, pci_config_base)) { - printf("Failed to initialize xHCI controller\n"); + // 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", controller.hci_version); - printf(" Max Ports: %u\n", controller.max_ports); - printf(" Max Slots: %u\n", controller.max_slots); + 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(&controller)) { + if (!xhci_controller_start(&xhci_hcd.base)) { printf("Failed to start xHCI controller\n"); return -1; } - // Initialize and scan all ports - controller.active_ports = 0; - for (uint8_t i = 0; i < controller.max_ports && i < 16; i++) { - if (xhci_port_init(&ports[i], &controller, i)) { - controller.active_ports |= (1U << i); + // Initialize and scan all ports, create USB devices through USB core + xhci_hcd.base.active_ports = 0; + for (uint8_t i = 0; i < xhci_hcd.base.max_ports && i < 16; i++) { + if (xhci_port_init(&ports[i], &xhci_hcd.base, i)) { + xhci_hcd.base.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 device in USB core (this will call back to xHCI) + if (usb_enumerate_device(usb_device) == 0) { + printf("USB device enumerated on port %u: VID:0x%04x " + "PID:0x%04x\n", + i, 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); + } + } + #ifdef DEBUG_XHCI_INIT printf("Port %u initialized - device connected\n", i); #endif @@ -80,7 +112,7 @@ static int xhci_initialize_driver(const uint64_t xhci_base, } #ifdef DEBUG_XHCI_INIT - printf("Active ports: 0x%08x\n", controller.active_ports); + printf("Active ports: 0x%08x\n", xhci_hcd.base.active_ports); #endif return 0; @@ -141,8 +173,8 @@ static bool register_with_devman(void) { } // Register each connected USB device - for (uint8_t i = 0; i < controller.max_ports && i < 16; i++) { - if ((controller.active_ports & (1U << i)) && ports[i].initialized) { + for (uint8_t i = 0; i < xhci_hcd.base.max_ports && i < 16; 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; diff --git a/servers/xhcidrv/xhci_hcd.c b/servers/xhcidrv/xhci_hcd.c new file mode 100644 index 00000000..1b39ef89 --- /dev/null +++ b/servers/xhcidrv/xhci_hcd.c @@ -0,0 +1,467 @@ +/* + * 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 "../../common/usb/usb_core.h" +#include "../../common/usb/usb_spec.h" +#include "include/xhci_hcd.h" + +#ifdef DEBUG_XHCI_HCD +#define hcd_debugf(...) printf(__VA_ARGS__) +#else +#define hcd_debugf(...) +#endif + +// Forward declarations +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); + +// ============================================================================= +// 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 +// ============================================================================= + +int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, + uint64_t pci_config_base) { + if (!xhci_hcd) { + return -1; + } + + memset(xhci_hcd, 0, sizeof(XhciHostController)); + + // Initialize base xHCI controller + if (!xhci_controller_init(&xhci_hcd->base, base_addr, pci_config_base)) { + printf("xHCI HCD: Failed to initialize base controller\n"); + return -1; + } + + // Initialize command ring + if (xhci_ring_init(&xhci_hcd->command_ring, XHCI_RING_SIZE) != 0) { + printf("xHCI HCD: Failed to initialize command ring\n"); + return -1; + } + + // Initialize event ring + if (xhci_ring_init(&xhci_hcd->event_ring, XHCI_RING_SIZE) != 0) { + printf("xHCI HCD: Failed to initialize event ring\n"); + xhci_ring_free(&xhci_hcd->command_ring); + return -1; + } + + // Allocate and initialize DCBAA + SyscallResultA dcbaa_alloc; + dcbaa_alloc = anos_alloc_physical_pages(4096); + if (dcbaa_alloc.result != SYSCALL_OK) { + printf("xHCI HCD: Failed to allocate DCBAA - syscall result: " + "0x%016lx\n", + (uint64_t)dcbaa_alloc.result); + printf("xHCI HCD: Requested 1 page (4096 bytes)\n"); + return -1; + } + + xhci_hcd->dcbaa_physical = dcbaa_alloc.value; + + SyscallResult dcbaa_map; + dcbaa_map = anos_map_physical( + xhci_hcd->dcbaa_physical, + (void *)(0x600000000 + xhci_hcd->dcbaa_physical), 4096, + ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | + ANOS_MAP_PHYSICAL_FLAG_NOCACHE); + + if (dcbaa_map.result != SYSCALL_OK) { + printf("xHCI HCD: Failed to map DCBAA\n"); + return -1; + } + + xhci_hcd->dcbaa = (XhciDcbaa *)(0x600000000 + xhci_hcd->dcbaa_physical); + memset(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)); + + // Set command ring control register + 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)); + + // Create USB host controller instance + xhci_hcd->usb_hcd = malloc(sizeof(UsbHostController)); + if (!xhci_hcd->usb_hcd) { + printf("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 = xhci_hcd; + + 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 = NULL; + } + + // 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: + printf("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 + 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) { + printf("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) { + printf("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) { + printf("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) { + printf("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 + uint8_t slot_id = xhci_alloc_device_slot(xhci_hcd); + if (slot_id == 0) { + printf("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); + + int result = xhci_send_command(xhci_hcd, (XhciTrb *)&enable_cmd); + if (result != 0) { + printf("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) { + printf("xHCI HCD: Enable Slot command timed out\n"); + xhci_free_device_slot(xhci_hcd, slot_id); + return -1; + } + + hcd_debugf("xHCI HCD: Device enabled in slot %u\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 NULL; + } + 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) { + hcd_debugf("xHCI HCD: Send command (stub)\n"); + return 0; // TODO: Implement command sending +} + +int xhci_wait_for_command_completion(XhciHostController *xhci_hcd, + uint32_t timeout_ms) { + hcd_debugf("xHCI HCD: Wait for command completion (stub)\n"); + return 0; // TODO: Implement command completion waiting +} + +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_device_context(XhciHostController *xhci_hcd, uint8_t slot_id, + UsbDevice *device) { + hcd_debugf("xHCI HCD: Setup device context for slot %u (stub)\n", slot_id); + return 0; // TODO: Implement device context setup +} + +int xhci_submit_control_transfer(XhciHostController *xhci_hcd, + UsbTransfer *transfer) { + hcd_debugf("xHCI HCD: Submit control transfer (stub)\n"); + // For now, mark transfer as completed immediately + transfer->status = USB_TRANSFER_STATUS_COMPLETED; + transfer->actual_length = transfer->buffer_length; + 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; +} \ 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..71f87112 --- /dev/null +++ b/servers/xhcidrv/xhci_rings.c @@ -0,0 +1,284 @@ +/* + * 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, 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 = (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(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 = NULL; + ring->trbs_physical = 0; + memset(ring, 0, sizeof(XhciRing)); +} + +XhciTrb *xhci_ring_enqueue_trb(XhciRing *ring) { + if (!ring || !ring->trbs) { + return NULL; + } + + // Check if we have space (leave one slot for link TRB if needed) + 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 NULL; + } + + XhciTrb *trb = &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; +} + +XhciTrb *xhci_ring_dequeue_trb(XhciRing *ring) { + if (!ring || !ring->trbs) { + return NULL; + } + + // Check if ring is empty + if (ring->enqueue_index == ring->dequeue_index) { + return NULL; + } + + XhciTrb *trb = &ring->trbs[ring->dequeue_index]; + + // Check cycle bit + bool trb_cycle = (trb->control & TRB_CONTROL_CYCLE_BIT) != 0; + if (trb_cycle != ring->consumer_cycle_state) { + return NULL; // 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(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(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 From c1a632c6c509654bb1d767ad6446fda2d15a3eb0 Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Sun, 7 Sep 2025 16:27:00 +0100 Subject: [PATCH 07/21] Basic hardware comms, polled only, squash --- servers/xhcidrv/include/xhci_hcd.h | 7 +- servers/xhcidrv/include/xhci_rings.h | 4 +- servers/xhcidrv/main.c | 36 +- servers/xhcidrv/xhci.c | 67 ++- servers/xhcidrv/xhci_hcd.c | 862 ++++++++++++++++++++++++++- servers/xhcidrv/xhci_rings.c | 10 +- 6 files changed, 952 insertions(+), 34 deletions(-) diff --git a/servers/xhcidrv/include/xhci_hcd.h b/servers/xhcidrv/include/xhci_hcd.h index 96d73b5b..a8100f30 100644 --- a/servers/xhcidrv/include/xhci_hcd.h +++ b/servers/xhcidrv/include/xhci_hcd.h @@ -77,7 +77,8 @@ typedef struct { XhciRing event_ring; // Primary event ring // Device context management - XhciDcbaa *dcbaa; // Device Context Base Address Array + volatile XhciDcbaa + *dcbaa; // Device Context Base Address Array (hardware-writable) uint64_t dcbaa_physical; // Physical address of DCBAA XhciInputContext @@ -130,10 +131,14 @@ int xhci_hcd_disable_port(UsbHostController *hcd, uint8_t port); 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); diff --git a/servers/xhcidrv/include/xhci_rings.h b/servers/xhcidrv/include/xhci_rings.h index a1181340..39aa3af7 100644 --- a/servers/xhcidrv/include/xhci_rings.h +++ b/servers/xhcidrv/include/xhci_rings.h @@ -111,7 +111,7 @@ typedef struct { #define XHCI_RING_SIZE 256 // Number of TRBs per ring typedef struct { - XhciTrb *trbs; // TRB array (page-aligned) + 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 @@ -262,7 +262,7 @@ typedef struct { int xhci_ring_init(XhciRing *ring, uint32_t size); void xhci_ring_free(XhciRing *ring); XhciTrb *xhci_ring_enqueue_trb(XhciRing *ring); -XhciTrb *xhci_ring_dequeue_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); diff --git a/servers/xhcidrv/main.c b/servers/xhcidrv/main.c index 33cd0bff..9cad6a6b 100644 --- a/servers/xhcidrv/main.c +++ b/servers/xhcidrv/main.c @@ -84,6 +84,18 @@ static int xhci_initialize_driver(const uint64_t xhci_base, } // Initialize and scan all ports, create USB devices through USB core + printf("xHCI: Scanning %u ports (max_ports=%u)\n", xhci_hcd.base.max_ports, + xhci_hcd.base.max_ports); + + // Debug: manually read first few port registers + printf("xHCI: Manual port register reads:\n"); + for (uint8_t j = 0; j < 8 && j < xhci_hcd.base.max_ports; j++) { + uint32_t portsc = + xhci_read32(&xhci_hcd.base, xhci_hcd.base.port_regs, j * 0x10); + printf(" Port %u (offset 0x%02x): PORTSC=0x%08x, CCS=%s\n", j, + j * 0x10, portsc, (portsc & 0x1) ? "Connected" : "Disconnected"); + } + xhci_hcd.base.active_ports = 0; for (uint8_t i = 0; i < xhci_hcd.base.max_ports && i < 16; i++) { if (xhci_port_init(&ports[i], &xhci_hcd.base, i)) { @@ -93,14 +105,24 @@ static int xhci_initialize_driver(const uint64_t xhci_base, UsbDevice *usb_device = usb_alloc_device(xhci_hcd.usb_hcd, i, ports[i].speed); if (usb_device) { - // Enable device in USB core (this will call back to xHCI) - if (usb_enumerate_device(usb_device) == 0) { - printf("USB device enumerated on port %u: VID:0x%04x " - "PID:0x%04x\n", - i, usb_device->device_desc.idVendor, - usb_device->device_desc.idProduct); + // First 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) { + + // Now enumerate device (this will do control transfers) + if (usb_enumerate_device(usb_device) == 0) { + printf("USB device enumerated on port %u: VID:0x%04x " + "PID:0x%04x\n", + i, 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 enumerate USB device on port %u\n", i); + printf("Failed to enable USB device on port %u\n", i); usb_free_device(usb_device); } } diff --git a/servers/xhcidrv/xhci.c b/servers/xhcidrv/xhci.c index 54e48247..6ad0dd1d 100644 --- a/servers/xhcidrv/xhci.c +++ b/servers/xhcidrv/xhci.c @@ -151,8 +151,10 @@ bool xhci_controller_init(XHCIController *controller, const uint64_t base_addr, (volatile void *)((uint8_t *)controller->cap_regs + (rtsoff & ~0x1F)); - init_vdebugf("Doorbell registers at offset 0x%x\n", dboff); - init_vdebugf("Runtime registers at offset 0x%x\n", rtsoff); + printf("xHCI: Doorbell registers at offset 0x%x (virtual addr %p)\n", dboff, + controller->doorbell_regs); + printf("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 = @@ -204,7 +206,7 @@ bool xhci_controller_start(const XHCIController *controller) { // Set the run bit uint32_t usbcmd = xhci_read32(controller, controller->op_regs, XHCI_OP_USBCMD); - usbcmd |= XHCI_CMD_RUN | XHCI_CMD_INTE; + usbcmd |= XHCI_CMD_RUN; // Remove XHCI_CMD_INTE for polling mode xhci_write32(controller, controller->op_regs, XHCI_OP_USBCMD, usbcmd); // Wait for controller to start (HCH bit cleared) @@ -265,16 +267,67 @@ bool xhci_port_init(XHCIPort *port, XHCIController *controller, bool xhci_port_scan(XHCIPort *port) { const uint32_t port_offset = port->port_num * 0x10; - const uint32_t portsc = - xhci_read32(port->controller, port->controller->port_regs, - port_offset + XHCI_PORT_SC); + const void *reg_addr = (const uint8_t *)port->controller->port_regs + + port_offset + XHCI_PORT_SC; + + printf("xHCI: Port %u: offset=0x%x, reg_addr=%p (base=%p)\n", + port->port_num, port_offset, reg_addr, port->controller->port_regs); + + 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_vdebugf("Port %u: PORTSC=0x%08x\n", port->port_num, portsc); + printf("xHCI: Scanning port %u: PORTSC=0x%08x, connected=%s\n", + port->port_num, portsc, (portsc & XHCI_PORTSC_CCS) ? "YES" : "NO"); if (port->connected) { + printf("xHCI: Port %u device detected, initiating reset\n", + port->port_num); + + // Initiate port reset by setting PR bit (bit 4) + 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) + printf("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) { + uint8_t pls = (portsc & XHCI_PORTSC_PLS_MASK) >> 5; + printf("xHCI: Port %u reset completed, PORTSC=0x%08x, PLS=%u\n", + port->port_num, portsc, pls); + break; + } + + // Sleep 1ms + // anos_task_sleep_current(1000000); // Uncomment if needed + for (volatile int i = 0; i < 100000; i++) + ; // Simple delay + } + + 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; diff --git a/servers/xhcidrv/xhci_hcd.c b/servers/xhcidrv/xhci_hcd.c index 1b39ef89..4001ef1c 100644 --- a/servers/xhcidrv/xhci_hcd.c +++ b/servers/xhcidrv/xhci_hcd.c @@ -31,6 +31,11 @@ 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); // ============================================================================= // Host Controller Operations Table @@ -106,8 +111,9 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, return -1; } - xhci_hcd->dcbaa = (XhciDcbaa *)(0x600000000 + xhci_hcd->dcbaa_physical); - memset(xhci_hcd->dcbaa, 0, sizeof(XhciDcbaa)); + 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, @@ -115,6 +121,105 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, 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; + } + printf("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); + + // Verify CONFIG register + uint32_t config_readback = xhci_read32( + &xhci_hcd->base, xhci_hcd->base.op_regs, XHCI_OP_CONFIG); + printf("xHCI HCD: CONFIG register readback: 0x%08x (should be %u)\n", + config_readback, max_device_slots); + + // DEBUG: Read all capability parameters to understand controller features + uint32_t hccparams1 = xhci_read32(&xhci_hcd->base, xhci_hcd->base.cap_regs, + XHCI_CAP_HCCPARAMS1); + 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); + + // Check extended capabilities pointer + uint16_t xecp = (hccparams1 >> 16) & 0xFFFF; + printf(" Extended capabilities pointer: 0x%04x\n", xecp); + if (xecp != 0) { + printf(" WARNING: Controller has extended capabilities that may need " + "setup\n"); + + // Read first extended capability + uint32_t ext_cap = + xhci_read32(&xhci_hcd->base, xhci_hcd->base.cap_regs, xecp); + uint8_t cap_id = ext_cap & 0xFF; + uint8_t next_ptr = (ext_cap >> 8) & 0xFF; + uint16_t cap_specific = (ext_cap >> 16) & 0xFFFF; + + printf(" First extended capability: ID=0x%02x, Next=0x%02x, " + "Specific=0x%04x\n", + cap_id, next_ptr, cap_specific); + + if (cap_id == 1) { // USB Legacy Support Capability + printf(" 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); + printf(" USBLEGCTLSTS: 0x%08x\n", usblegctlsts); + + // Check if BIOS/SMM owns the controller + if (usblegctlsts & (1 << 16)) { // HC BIOS Owned Semaphore + printf(" 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 + printf(" 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"); + } + } + } + } + } + // Set command ring control register 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, @@ -122,6 +227,12 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, xhci_write32(&xhci_hcd->base, xhci_hcd->base.op_regs, XHCI_OP_CRCR + 4, (uint32_t)(crcr >> 32)); + // Set up Event Ring Segment Table (ERST) + if (xhci_setup_event_ring(xhci_hcd) != 0) { + printf("xHCI HCD: Failed to setup event ring\n"); + return -1; + } + // Create USB host controller instance xhci_hcd->usb_hcd = malloc(sizeof(UsbHostController)); if (!xhci_hcd->usb_hcd) { @@ -283,6 +394,10 @@ int xhci_hcd_enable_device(UsbHostController *hcd, UsbDevice *device) { enable_cmd.control = TRB_CONTROL_CYCLE_BIT | (TRB_TYPE_ENABLE_SLOT << TRB_CONTROL_TYPE_SHIFT); + printf("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) { printf("xHCI HCD: Enable Slot command failed\n"); @@ -409,14 +524,232 @@ int xhci_hcd_configure_endpoint(UsbHostController *hcd, UsbDevice *device, } int xhci_send_command(XhciHostController *xhci_hcd, XhciTrb *command_trb) { - hcd_debugf("xHCI HCD: Send command (stub)\n"); - return 0; // TODO: Implement command sending + 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 + 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); + printf("xHCI HCD: Controller status before command - USBSTS=0x%08x " + "USBCMD=0x%08x\n", + usbsts, usbcmd); + printf(" 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"); + + // Additional status checks + printf(" Full USBSTS: 0x%08x\n", usbsts); + printf(" HSE (Host System Error): %s\n", + (usbsts & (1 << 2)) ? "YES" : "NO"); + printf(" EINT (Event Interrupt): %s\n", + (usbsts & (1 << 3)) ? "YES" : "NO"); + printf(" PCD (Port Change Detect): %s\n", + (usbsts & (1 << 4)) ? "YES" : "NO"); + printf(" SSS (Save State Status): %s\n", + (usbsts & (1 << 8)) ? "YES" : "NO"); + printf(" RSS (Restore State Status): %s\n", + (usbsts & (1 << 9)) ? "YES" : "NO"); + printf(" SRE (Save/Restore Error): %s\n", + (usbsts & (1 << 10)) ? "YES" : "NO"); + printf(" CAS (Command Abort Status): %s\n", + (usbsts & (1 << 11)) ? "YES" : "NO"); + + // Check DNCTRL register + uint32_t dnctrl = xhci_read32(&xhci_hcd->base, xhci_hcd->base.op_regs, + XHCI_OP_DNCTRL); + printf(" DNCTRL (Device Notification Control): 0x%08x\n", dnctrl); + + // Enqueue the command TRB to the command ring + XhciTrb *ring_trb = xhci_ring_enqueue_trb(&xhci_hcd->command_ring); + if (!ring_trb) { + printf("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 + printf("xHCI HCD: TRB written to ring[%u] - verifying...\n", + xhci_hcd->command_ring.enqueue_index); + printf(" Written TRB: param=0x%016lx status=0x%08x control=0x%08x\n", + command_trb->parameter, command_trb->status, command_trb->control); + printf(" Ring TRB: param=0x%016lx status=0x%08x control=0x%08x\n", + ring_trb->parameter, ring_trb->status, ring_trb->control); + printf(" 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 + uint32_t old_enqueue = xhci_hcd->command_ring.enqueue_index; + xhci_ring_inc_enqueue(&xhci_hcd->command_ring); + printf("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); + + // Ring the host controller doorbell (slot 0 = command ring) + printf("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"); + + // 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); + + // 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"); + return 0; } int xhci_wait_for_command_completion(XhciHostController *xhci_hcd, - uint32_t timeout_ms) { - hcd_debugf("xHCI HCD: Wait for command completion (stub)\n"); - return 0; // TODO: Implement command completion waiting + const uint32_t timeout_ms) { + if (!xhci_hcd) { + return -1; + } + + hcd_debugf("xHCI HCD: Waiting for command completion (timeout %u ms)\n", + timeout_ms); + + // For now, implement a simple polling mechanism + // In a real implementation, this would process the event ring + printf("xHCI HCD: Starting command completion wait, event ring @ %p (phys " + "0x%016lx)\n", + (void *)xhci_hcd->event_ring.trbs, + xhci_hcd->event_ring.trbs_physical); + printf("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); + + // CRITICAL DEBUG: Since EINT is set, events must be in the ring - scan entire ring + 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); + } + } + + 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); + printf("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) { + printf("xHCI HCD: Command completed successfully\n"); + xhci_ring_inc_dequeue(&xhci_hcd->event_ring); + return 0; + } + xhci_ring_inc_dequeue(&xhci_hcd->event_ring); + } 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); + } + } + + // Sleep for 1ms + anos_task_sleep_current(1000000); + } + + printf("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); + + // Wait for transfer completion event + for (uint32_t i = 0; i < timeout_ms; i++) { + // Check if there are any events in the event ring + volatile const XhciTrb *event_trb = + xhci_ring_dequeue_trb(&xhci_hcd->event_ring); + if (event_trb) { + const uint32_t trb_type = TRB_GET_TYPE(event_trb); + hcd_debugf("xHCI HCD: Received event TRB type %u\n", trb_type); + + if (trb_type == TRB_TYPE_TRANSFER) { + hcd_debugf("xHCI HCD: Transfer completed successfully\n"); + xhci_ring_inc_dequeue(&xhci_hcd->event_ring); + return 0; + } else if (trb_type == TRB_TYPE_COMMAND_COMPLETION) { + // Skip command completion events + hcd_debugf("xHCI HCD: Skipping command completion event\n"); + xhci_ring_inc_dequeue(&xhci_hcd->event_ring); + } else { + // Unknown event, skip it + xhci_ring_inc_dequeue(&xhci_hcd->event_ring); + } + } + + // Sleep for 1ms + anos_task_sleep_current(1000000); + } + + printf("xHCI HCD: Transfer completion timeout after %u ms\n", timeout_ms); + return -1; } int xhci_alloc_device_slot(XhciHostController *xhci_hcd) { @@ -437,18 +770,240 @@ 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) { + 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]) { + printf("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) { + printf("xHCI HCD: Failed to initialize endpoint ring\n"); + free(ring); + xhci_hcd->endpoint_rings[slot_id][endpoint_id] = NULL; + 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: Setup device context for slot %u (stub)\n", slot_id); - return 0; // TODO: Implement device context setup + 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) + SyscallResultA input_ctx_alloc = anos_alloc_physical_pages(4096); + if (input_ctx_alloc.result != SYSCALL_OK) { + printf("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), 4096, + ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | + ANOS_MAP_PHYSICAL_FLAG_NOCACHE); + + if (input_ctx_map.result != SYSCALL_OK) { + printf("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, 4096); + + // 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; // Default, should be updated after getting device descriptor + switch (device->speed) { + case 1: + max_packet_size = 8; + break; // Low speed + case 2: + max_packet_size = 64; + break; // Full speed + case 3: + max_packet_size = 64; + break; // High speed + case 4: + max_packet_size = 512; + break; // Super speed + } + ep0_context[1] = (max_packet_size << 16) | + (3 << 1); // Max packet size and Error Count = 3 + + // Set up endpoint 0 transfer ring + if (xhci_setup_endpoint_ring(xhci_hcd, slot_id, 0) != 0) { + printf("xHCI HCD: Failed to setup endpoint 0 ring\n"); + return -1; + } + + // Set Transfer Ring Base Address (TRBA) in endpoint context - DW2/DW3 + 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) + SyscallResultA device_ctx_alloc = anos_alloc_physical_pages(4096); + if (device_ctx_alloc.result != SYSCALL_OK) { + printf("xHCI HCD: Failed to allocate device context\n"); + return -1; + } + + // Map device context + SyscallResult device_ctx_map = anos_map_physical( + device_ctx_alloc.value, + (void *)(0xB00000000 + device_ctx_alloc.value), 4096, + ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | + ANOS_MAP_PHYSICAL_FLAG_NOCACHE); + + if (device_ctx_map.result != SYSCALL_OK) { + printf("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) { - hcd_debugf("xHCI HCD: Submit control transfer (stub)\n"); - // For now, mark transfer as completed immediately + 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) { + printf("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) { + printf("xHCI HCD: No endpoint ring for slot %u endpoint 0\n", slot_id); + return -1; + } + + if (!xhci_ring_has_space(ep0_ring, trbs_needed)) { + printf("xHCI HCD: Not enough space in endpoint transfer ring\n"); + return -1; + } + + // Build and enqueue TRBs for the control transfer + int result = xhci_build_control_transfer_trbs(xhci_hcd, transfer, slot_id); + if (result != 0) { + printf("xHCI HCD: Failed to build control transfer TRBs\n"); + return -1; + } + + // Ring doorbell to notify hardware + 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"); + int completion_result = xhci_wait_for_transfer_completion(xhci_hcd, 5000); + if (completion_result != 0) { + printf("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) { + uint64_t data_phys = (uint64_t)(uintptr_t)transfer->hcd_private; + if (data_phys) { + volatile void *data_virt = + (volatile void *)(0x800000000 + data_phys); + memcpy(transfer->buffer, (const void *)data_virt, setup->wLength); + hcd_debugf("xHCI HCD: Copied %u bytes back from device\n", + setup->wLength); + + // Debug: dump first few bytes of received data + printf("xHCI HCD: Raw data received: "); + uint8_t *data_bytes = (uint8_t *)transfer->buffer; + for (uint32_t i = 0; i < setup->wLength && i < 16; i++) { + printf("%02x ", data_bytes[i]); + } + printf("\n"); + } + } + + // Mark as completed after successful transfer transfer->status = USB_TRANSFER_STATUS_COMPLETED; - transfer->actual_length = transfer->buffer_length; + transfer->actual_length = setup->wLength; + + hcd_debugf("xHCI HCD: Control transfer submitted\n"); return 0; } @@ -464,4 +1019,287 @@ int xhci_submit_interrupt_transfer(XhciHostController *xhci_hcd, 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, uint8_t slot_id) { + 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(4096); // 1 page + if (setup_alloc.result != SYSCALL_OK) { + printf("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), 4096, + ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | + ANOS_MAP_PHYSICAL_FLAG_NOCACHE); + + if (setup_map.result != SYSCALL_OK) { + printf("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 + printf("xHCI HCD: Setup packet: %02x %02x %04x %04x %04x\n", + setup->bmRequestType, setup->bRequest, setup->wValue, setup->wIndex, + setup->wLength); + + // 1. Setup Stage TRB + XhciTrb *setup_trb = xhci_ring_enqueue_trb(ring); + if (!setup_trb) { + printf("xHCI HCD: Failed to enqueue setup TRB\n"); + return -1; + } + + setup_trb->parameter = + setup_alloc.value; // Physical address of setup packet + setup_trb->status = 8; // Setup packet is always 8 bytes + 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 + 4095) / 4096; + const size_t data_size = data_pages * 4096; + const SyscallResultA data_alloc = anos_alloc_physical_pages(data_size); + + if (data_alloc.result != SYSCALL_OK) { + printf("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) { + printf("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) { + printf("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) { + printf("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); + printf("xHCI HCD: Doorbell written to hardware\n"); + } else { + printf("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) + SyscallResultA erst_alloc = anos_alloc_physical_pages(4096); + if (erst_alloc.result != SYSCALL_OK) { + printf("xHCI HCD: Failed to allocate ERST\n"); + return -1; + } + + // Map ERST + SyscallResult erst_map = anos_map_physical( + erst_alloc.value, (void *)(0xA00000000 + erst_alloc.value), 4096, + ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | + ANOS_MAP_PHYSICAL_FLAG_NOCACHE); + + if (erst_map.result != SYSCALL_OK) { + printf("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) { + printf("xHCI HCD: ERROR - No runtime registers mapped\n"); + return -1; + } + + // Interrupter 0 registers (offset 0x20 from runtime base) + const 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); + + // CRITICAL: Disable interrupts and use polling mode + printf("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); + + // 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); + + printf("xHCI HCD: ERST registers verification:\n"); + printf(" ERSTSZ: 0x%08x (should be 1)\n", erstsz_read); + printf(" ERSTBA: 0x%08x%08x (should be 0x%016lx)\n", erstba_high, + erstba_low, erst_alloc.value); + printf(" 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); + + return 0; } \ No newline at end of file diff --git a/servers/xhcidrv/xhci_rings.c b/servers/xhcidrv/xhci_rings.c index 71f87112..17dcbc09 100644 --- a/servers/xhcidrv/xhci_rings.c +++ b/servers/xhcidrv/xhci_rings.c @@ -62,7 +62,7 @@ int xhci_ring_init(XhciRing *ring, uint32_t size) { return -1; } - ring->trbs = (XhciTrb *)(0x500000000 + ring->trbs_physical); + ring->trbs = (volatile XhciTrb *)(0x500000000 + ring->trbs_physical); // Initialize ring state ring->enqueue_index = 0; @@ -72,7 +72,7 @@ int xhci_ring_init(XhciRing *ring, uint32_t size) { ring->consumer_cycle_state = 1; // Clear all TRBs - memset(ring->trbs, 0, size * sizeof(XhciTrb)); + memset((void *)ring->trbs, 0, size * sizeof(XhciTrb)); ring_debugf( "xHCI: Initialized ring with %u TRBs at phys=0x%016lx virt=%p\n", @@ -104,7 +104,7 @@ XhciTrb *xhci_ring_enqueue_trb(XhciRing *ring) { return NULL; } - XhciTrb *trb = &ring->trbs[ring->enqueue_index]; + XhciTrb *trb = (XhciTrb *)&ring->trbs[ring->enqueue_index]; // Set cycle bit for producer trb->control = (trb->control & ~TRB_CONTROL_CYCLE_BIT) | @@ -116,7 +116,7 @@ XhciTrb *xhci_ring_enqueue_trb(XhciRing *ring) { return trb; } -XhciTrb *xhci_ring_dequeue_trb(XhciRing *ring) { +volatile XhciTrb *xhci_ring_dequeue_trb(XhciRing *ring) { if (!ring || !ring->trbs) { return NULL; } @@ -126,7 +126,7 @@ XhciTrb *xhci_ring_dequeue_trb(XhciRing *ring) { return NULL; } - XhciTrb *trb = &ring->trbs[ring->dequeue_index]; + volatile XhciTrb *trb = &ring->trbs[ring->dequeue_index]; // Check cycle bit bool trb_cycle = (trb->control & TRB_CONTROL_CYCLE_BIT) != 0; From 300201540a66304a65666dd6ab610ec40b2cda6e Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Sun, 7 Sep 2025 18:51:06 +0100 Subject: [PATCH 08/21] Getting close to enumerate, hitting stall on GET_DESCRIPTOR --- servers/xhcidrv/main.c | 3 + servers/xhcidrv/xhci.c | 35 +++-- servers/xhcidrv/xhci_hcd.c | 297 ++++++++++++++++++++++++++----------- 3 files changed, 239 insertions(+), 96 deletions(-) diff --git a/servers/xhcidrv/main.c b/servers/xhcidrv/main.c index 9cad6a6b..265bb6f6 100644 --- a/servers/xhcidrv/main.c +++ b/servers/xhcidrv/main.c @@ -83,6 +83,7 @@ static int xhci_initialize_driver(const uint64_t xhci_base, return -1; } +#ifdef DEBUG_XHCI_INIT // Initialize and scan all ports, create USB devices through USB core printf("xHCI: Scanning %u ports (max_ports=%u)\n", xhci_hcd.base.max_ports, xhci_hcd.base.max_ports); @@ -95,6 +96,7 @@ static int xhci_initialize_driver(const uint64_t xhci_base, printf(" Port %u (offset 0x%02x): PORTSC=0x%08x, CCS=%s\n", j, j * 0x10, portsc, (portsc & 0x1) ? "Connected" : "Disconnected"); } +#endif xhci_hcd.base.active_ports = 0; for (uint8_t i = 0; i < xhci_hcd.base.max_ports && i < 16; i++) { @@ -119,6 +121,7 @@ static int xhci_initialize_driver(const uint64_t xhci_base, } else { printf("Failed to enumerate USB device on port %u\n", i); + usb_free_device(usb_device); } } else { diff --git a/servers/xhcidrv/xhci.c b/servers/xhcidrv/xhci.c index 6ad0dd1d..23243d7e 100644 --- a/servers/xhcidrv/xhci.c +++ b/servers/xhcidrv/xhci.c @@ -151,10 +151,10 @@ bool xhci_controller_init(XHCIController *controller, const uint64_t base_addr, (volatile void *)((uint8_t *)controller->cap_regs + (rtsoff & ~0x1F)); - printf("xHCI: Doorbell registers at offset 0x%x (virtual addr %p)\n", dboff, - controller->doorbell_regs); - printf("xHCI: Runtime registers at offset 0x%x (virtual addr %p)\n", rtsoff, - controller->runtime_regs); + 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 = @@ -267,11 +267,15 @@ bool xhci_port_init(XHCIPort *port, XHCIController *controller, bool xhci_port_scan(XHCIPort *port) { const uint32_t port_offset = port->port_num * 0x10; + +#ifdef DEBUG_XHCI_INIT const void *reg_addr = (const uint8_t *)port->controller->port_regs + port_offset + XHCI_PORT_SC; - printf("xHCI: Port %u: offset=0x%x, reg_addr=%p (base=%p)\n", - port->port_num, port_offset, reg_addr, port->controller->port_regs); + 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 uint32_t portsc = xhci_read32(port->controller, port->controller->port_regs, port_offset + XHCI_PORT_SC); @@ -279,12 +283,13 @@ bool xhci_port_scan(XHCIPort *port) { port->connected = (portsc & XHCI_PORTSC_CCS) != 0; port->enabled = (portsc & XHCI_PORTSC_PED) != 0; - printf("xHCI: Scanning port %u: PORTSC=0x%08x, connected=%s\n", - port->port_num, portsc, (portsc & XHCI_PORTSC_CCS) ? "YES" : "NO"); + 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) { - printf("xHCI: Port %u device detected, initiating reset\n", - port->port_num); + init_debugf("xHCI: Port %u device detected, initiating reset\n", + port->port_num); // Initiate port reset by setting PR bit (bit 4) uint32_t reset_portsc = portsc | XHCI_PORTSC_PR; @@ -292,16 +297,20 @@ bool xhci_port_scan(XHCIPort *port) { port_offset + XHCI_PORT_SC, reset_portsc); // Wait for reset to complete (PR bit clears automatically) - printf("xHCI: Waiting for port %u reset to complete\n", port->port_num); + 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; - printf("xHCI: Port %u reset completed, PORTSC=0x%08x, PLS=%u\n", - port->port_num, portsc, pls); + init_debugf("xHCI: Port %u reset completed, PORTSC=0x%08x, " + "PLS=%u\n", + port->port_num, portsc, pls); +#endif break; } diff --git a/servers/xhcidrv/xhci_hcd.c b/servers/xhcidrv/xhci_hcd.c index 4001ef1c..9d6b9d06 100644 --- a/servers/xhcidrv/xhci_hcd.c +++ b/servers/xhcidrv/xhci_hcd.c @@ -20,8 +20,14 @@ #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 // Forward declarations @@ -138,9 +144,9 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, printf("xHCI HCD: CONFIG register readback: 0x%08x (should be %u)\n", config_readback, max_device_slots); - // DEBUG: Read all capability parameters to understand controller features 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, @@ -158,39 +164,46 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, 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 uint16_t xecp = (hccparams1 >> 16) & 0xFFFF; - printf(" Extended capabilities pointer: 0x%04x\n", xecp); + hcd_vdebugf(" Extended capabilities pointer: 0x%04x\n", xecp); if (xecp != 0) { - printf(" WARNING: Controller has extended capabilities that may need " - "setup\n"); + hcd_vdebugf( + " WARNING: Controller has extended capabilities that may need " + "setup\n"); // Read first extended capability 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; - printf(" First extended capability: ID=0x%02x, Next=0x%02x, " - "Specific=0x%04x\n", - cap_id, next_ptr, cap_specific); + 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 - printf(" Found USB Legacy Support Capability - may need to " - "disable OS handoff\n"); + 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); - printf(" USBLEGCTLSTS: 0x%08x\n", usblegctlsts); + hcd_vdebugf(" USBLEGCTLSTS: 0x%08x\n", usblegctlsts); // Check if BIOS/SMM owns the controller if (usblegctlsts & (1 << 16)) { // HC BIOS Owned Semaphore - printf(" WARNING: BIOS still owns the controller - " - "attempting OS handoff\n"); + 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, @@ -203,7 +216,7 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, xhci_hcd->base.cap_regs, xecp + 4); if (!(usblegctlsts & (1 << 16))) { // BIOS released control - printf(" OS handoff successful\n"); + hcd_debugf(" OS handoff successful\n"); break; } // Small delay @@ -412,7 +425,44 @@ int xhci_hcd_enable_device(UsbHostController *hcd, UsbDevice *device) { return -1; } - hcd_debugf("xHCI HCD: Device enabled in slot %u\n", slot_id); + printf("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) { + printf("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); + + printf("xHCI HCD: Sending Address Device command for slot %u\n", slot_id); + result = xhci_send_command(xhci_hcd, (XhciTrb *)&address_cmd); + if (result != 0) { + printf("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) { + printf("xHCI HCD: Address Device command timed out\n"); + xhci_free_device_slot(xhci_hcd, slot_id); + return -1; + } + + printf("xHCI HCD: Device fully enabled in slot %u with endpoint 0 " + "configured\n", + slot_id); return 0; } @@ -532,39 +582,40 @@ int xhci_send_command(XhciHostController *xhci_hcd, XhciTrb *command_trb) { TRB_GET_TYPE(command_trb)); // Verify controller is running before sending command +#ifdef DEBUG_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); - printf("xHCI HCD: Controller status before command - USBSTS=0x%08x " - "USBCMD=0x%08x\n", - usbsts, usbcmd); - printf(" 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"); - - // Additional status checks - printf(" Full USBSTS: 0x%08x\n", usbsts); - printf(" HSE (Host System Error): %s\n", - (usbsts & (1 << 2)) ? "YES" : "NO"); - printf(" EINT (Event Interrupt): %s\n", - (usbsts & (1 << 3)) ? "YES" : "NO"); - printf(" PCD (Port Change Detect): %s\n", - (usbsts & (1 << 4)) ? "YES" : "NO"); - printf(" SSS (Save State Status): %s\n", - (usbsts & (1 << 8)) ? "YES" : "NO"); - printf(" RSS (Restore State Status): %s\n", - (usbsts & (1 << 9)) ? "YES" : "NO"); - printf(" SRE (Save/Restore Error): %s\n", - (usbsts & (1 << 10)) ? "YES" : "NO"); - printf(" CAS (Command Abort Status): %s\n", - (usbsts & (1 << 11)) ? "YES" : "NO"); + 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"); // Check DNCTRL register uint32_t dnctrl = xhci_read32(&xhci_hcd->base, xhci_hcd->base.op_regs, XHCI_OP_DNCTRL); - printf(" DNCTRL (Device Notification Control): 0x%08x\n", 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); @@ -581,36 +632,43 @@ int xhci_send_command(XhciHostController *xhci_hcd, XhciTrb *command_trb) { __asm__ volatile("mfence" ::: "memory"); // DEBUG: Verify TRB was actually written to memory - printf("xHCI HCD: TRB written to ring[%u] - verifying...\n", - xhci_hcd->command_ring.enqueue_index); - printf(" Written TRB: param=0x%016lx status=0x%08x control=0x%08x\n", - command_trb->parameter, command_trb->status, command_trb->control); - printf(" Ring TRB: param=0x%016lx status=0x%08x control=0x%08x\n", - ring_trb->parameter, ring_trb->status, ring_trb->control); - printf(" 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)); + 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 - uint32_t old_enqueue = xhci_hcd->command_ring.enqueue_index; xhci_ring_inc_enqueue(&xhci_hcd->command_ring); - printf("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); +#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) - printf("xHCI HCD: Ringing doorbell - doorbell_regs=%p, writing 0x00000000 " - "to offset 0\n", - xhci_hcd->base.doorbell_regs); + 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 = @@ -624,6 +682,7 @@ int xhci_send_command(XhciHostController *xhci_hcd, XhciTrb *command_trb) { 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 = @@ -632,6 +691,7 @@ int xhci_send_command(XhciHostController *xhci_hcd, XhciTrb *command_trb) { doorbell_readback); hcd_debugf("xHCI HCD: Command TRB enqueued and doorbell rung\n"); +#endif return 0; } @@ -646,16 +706,17 @@ int xhci_wait_for_command_completion(XhciHostController *xhci_hcd, // For now, implement a simple polling mechanism // In a real implementation, this would process the event ring - printf("xHCI HCD: Starting command completion wait, event ring @ %p (phys " - "0x%016lx)\n", - (void *)xhci_hcd->event_ring.trbs, - xhci_hcd->event_ring.trbs_physical); - printf("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); - - // CRITICAL DEBUG: Since EINT is set, events must be in the ring - scan entire ring + hcd_vdebugf( + "xHCI HCD: Starting command completion wait, 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 VERY_NOISY_XHCI_HCD 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; @@ -671,6 +732,7 @@ int xhci_wait_for_command_completion(XhciHostController *xhci_hcd, trb_type, cycle); } } +#endif for (uint32_t i = 0; i < timeout_ms; i++) { // Check for events manually since hardware controls enqueue for event ring @@ -721,30 +783,91 @@ int xhci_wait_for_transfer_completion(XhciHostController *xhci_hcd, hcd_debugf("xHCI HCD: Waiting for transfer completion (timeout %u ms)\n", timeout_ms); - // Wait for transfer completion event - for (uint32_t i = 0; i < timeout_ms; i++) { - // Check if there are any events in the event ring - volatile const XhciTrb *event_trb = - xhci_ring_dequeue_trb(&xhci_hcd->event_ring); - if (event_trb) { + // Scan entire event ring for transfer events (very noisy debug only) + hcd_vdebugf("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); +#ifdef DEBUG_XHCI_HCD + bool cycle = (control & TRB_CONTROL_CYCLE_BIT) != 0; + hcd_vdebugf(" 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 + + if (trb_type == TRB_TYPE_TRANSFER) { + hcd_debugf(" *** TRANSFER EVENT FOUND at index %u! ***\n", + scan_idx); + } + } + } + + // 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_debugf("xHCI HCD: Received event TRB type %u\n", trb_type); + 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 completed successfully\n"); - xhci_ring_inc_dequeue(&xhci_hcd->event_ring); - return 0; + printf("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 + uint32_t completion_code = (event_trb->status >> 24) & 0xFF; + printf("xHCI HCD: Transfer completion code: %u (%s)\n", + completion_code, + xhci_completion_code_string(completion_code)); + + if (completion_code == XHCI_COMP_SUCCESS) { + printf("xHCI HCD: Transfer completed successfully!\n"); + xhci_ring_inc_dequeue(&xhci_hcd->event_ring); + return 0; + } else { + printf("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) { - // Skip command completion events - hcd_debugf("xHCI HCD: Skipping command completion event\n"); + hcd_vdebugf("xHCI HCD: Skipping command completion event " + "during transfer wait\n"); xhci_ring_inc_dequeue(&xhci_hcd->event_ring); } else { - // Unknown event, skip it + hcd_vdebugf("xHCI HCD: Skipping unknown event type %u\n", + trb_type); xhci_ring_inc_dequeue(&xhci_hcd->event_ring); } } - // Sleep for 1ms + // Sleep for 1ms before checking for new events anos_task_sleep_current(1000000); } @@ -967,6 +1090,9 @@ int xhci_submit_control_transfer(XhciHostController *xhci_hcd, } // Ring doorbell to notify hardware + printf("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 @@ -1075,6 +1201,11 @@ int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, setup_trb->parameter = setup_alloc.value; // Physical address of setup packet setup_trb->status = 8; // Setup packet is always 8 bytes + + // Preserve cycle bit set by xhci_ring_enqueue_trb, add TRB type + //setup_trb->control = (setup_trb->control & TRB_CONTROL_CYCLE_BIT) | + // (TRB_TYPE_SETUP_STAGE << TRB_CONTROL_TYPE_SHIFT); + setup_trb->control = TRB_CONTROL_CYCLE_BIT | (TRB_TYPE_SETUP_STAGE << TRB_CONTROL_TYPE_SHIFT) | TRB_CONTROL_IMMEDIATE_DATA; From 0d4ae2ebaaf01e5be600da8920542d18b084f684 Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Sun, 7 Sep 2025 21:28:05 +0100 Subject: [PATCH 09/21] Enumerating, but only immediate mode works --- servers/xhcidrv/xhci_hcd.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/servers/xhcidrv/xhci_hcd.c b/servers/xhcidrv/xhci_hcd.c index 9d6b9d06..202749ed 100644 --- a/servers/xhcidrv/xhci_hcd.c +++ b/servers/xhcidrv/xhci_hcd.c @@ -783,25 +783,23 @@ int xhci_wait_for_transfer_completion(XhciHostController *xhci_hcd, hcd_debugf("xHCI HCD: Waiting for transfer completion (timeout %u ms)\n", timeout_ms); - // Scan entire event ring for transfer events (very noisy debug only) - hcd_vdebugf("xHCI HCD: Scanning entire event ring for transfer events:\n"); + // Scan entire event ring for transfer events + 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); -#ifdef DEBUG_XHCI_HCD bool cycle = (control & TRB_CONTROL_CYCLE_BIT) != 0; - hcd_vdebugf(" 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 + 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) { - hcd_debugf(" *** TRANSFER EVENT FOUND at index %u! ***\n", - scan_idx); + printf(" *** TRANSFER EVENT FOUND at index %u! ***\n", + scan_idx); } } } @@ -1170,6 +1168,8 @@ int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, return -1; } + printf("!!!!!!!!!!!!!!! 0x%016lx\n", setup_alloc.value); + // Map setup packet memory const SyscallResult setup_map = anos_map_physical( setup_alloc.value, (void *)(0x700000000 + setup_alloc.value), 4096, @@ -1191,6 +1191,10 @@ int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, setup->bmRequestType, setup->bRequest, setup->wValue, setup->wIndex, setup->wLength); + printf("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) { @@ -1198,13 +1202,12 @@ int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, return -1; } - setup_trb->parameter = - setup_alloc.value; // Physical address of setup packet - setup_trb->status = 8; // Setup packet is always 8 bytes + // 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 - // Preserve cycle bit set by xhci_ring_enqueue_trb, add TRB type - //setup_trb->control = (setup_trb->control & TRB_CONTROL_CYCLE_BIT) | - // (TRB_TYPE_SETUP_STAGE << TRB_CONTROL_TYPE_SHIFT); + 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) | From f68bdf95ee63b73483a59b9bc3b174ed5cc216e2 Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Sun, 7 Sep 2025 22:28:37 +0100 Subject: [PATCH 10/21] Add USB devices for testing --- Makefile | 1 - 1 file changed, 1 deletion(-) 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 \ From cc813a4cfaee2a94eee2cc5118aad7de91d2a203 Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Sun, 7 Sep 2025 22:29:04 +0100 Subject: [PATCH 11/21] Optionally pipe down debug output --- servers/common/usb/usb_core.c | 82 +++++++---- servers/xhcidrv/Makefile | 3 + servers/xhcidrv/main.c | 7 +- servers/xhcidrv/xhci_hcd.c | 268 ++++++++++++++++++---------------- 4 files changed, 200 insertions(+), 160 deletions(-) diff --git a/servers/common/usb/usb_core.c b/servers/common/usb/usb_core.c index 6cb2ccc0..23d8e244 100644 --- a/servers/common/usb/usb_core.c +++ b/servers/common/usb/usb_core.c @@ -15,6 +15,12 @@ #include "usb_core.h" #include "usb_spec.h" +#ifdef DEBUG_USB_CORE +#define debugf printf +#else +#define debugf(...) +#endif + // ============================================================================= // Global USB Core State // ============================================================================= @@ -34,12 +40,12 @@ int usb_register_host_controller(UsbHostController *hcd) { } if (num_registered_hcds >= 4) { - printf("USB: Maximum number of host controllers reached\n"); + debugf("USB: Maximum number of host controllers reached\n"); return -1; } registered_hcds[num_registered_hcds++] = hcd; - printf("USB: Registered host controller '%s' with %u ports\n", hcd->name, + debugf("USB: Registered host controller '%s' with %u ports\n", hcd->name, hcd->num_ports); return 0; @@ -53,7 +59,7 @@ int usb_unregister_host_controller(UsbHostController *hcd) { registered_hcds[j] = registered_hcds[j + 1]; } registered_hcds[--num_registered_hcds] = NULL; - printf("USB: Unregistered host controller '%s'\n", hcd->name); + debugf("USB: Unregistered host controller '%s'\n", hcd->name); return 0; } } @@ -71,7 +77,7 @@ UsbDevice *usb_alloc_device(UsbHostController *hcd, uint8_t port_number, UsbDevice *device = calloc(1, sizeof(UsbDevice)); if (!device) { - printf("USB: Failed to allocate device structure\n"); + debugf("USB: Failed to allocate device structure\n"); return NULL; } @@ -81,7 +87,7 @@ UsbDevice *usb_alloc_device(UsbHostController *hcd, uint8_t port_number, device->state = USB_DEVICE_STATE_DEFAULT; device->address = 0; // Will be assigned during enumeration - printf("USB: Allocated device on port %u (speed: %s)\n", port_number, + debugf("USB: Allocated device on port %u (speed: %s)\n", port_number, usb_get_speed_string(speed)); return device; @@ -113,14 +119,14 @@ int usb_enumerate_device(UsbDevice *device) { return -1; } - printf("USB: Enumerating device on port %u\n", device->port_number); + 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) { - printf("USB: Failed to get partial device descriptor\n"); + debugf("USB: Failed to get partial device descriptor\n"); return -1; } @@ -135,7 +141,7 @@ int usb_enumerate_device(UsbDevice *device) { result = usb_set_address(device, new_address); if (result < 0) { - printf("USB: Failed to set device address %u\n", new_address); + debugf("USB: Failed to set device address %u\n", new_address); return -1; } @@ -148,36 +154,46 @@ int usb_enumerate_device(UsbDevice *device) { &device->device_desc, sizeof(UsbDeviceDescriptor)); if (result < 0) { - printf("USB: Failed to get full device descriptor\n"); + debugf("USB: Failed to get full device descriptor\n"); return -1; } - printf("USB: Device enumerated - VID:0x%04x PID:0x%04x Class:0x%02x\n", + 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) { - usb_get_string_descriptor(device, device->device_desc.iManufacturer, - device->manufacturer, - sizeof(device->manufacturer)); + 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) { - usb_get_string_descriptor(device, device->device_desc.iProduct, - device->product, sizeof(device->product)); + 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) { - usb_get_string_descriptor(device, device->device_desc.iSerialNumber, - device->serial_number, - sizeof(device->serial_number)); + 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, uint8_t config_value) { +int usb_configure_device(UsbDevice *device, const uint8_t config_value) { if (!device || device->state != USB_DEVICE_STATE_ADDRESS) { return -1; } @@ -188,14 +204,14 @@ int usb_configure_device(UsbDevice *device, uint8_t config_value) { config_value - 1, &config_desc, sizeof(UsbConfigurationDescriptor)); if (result < 0) { - printf("USB: Failed to get configuration descriptor\n"); + 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) { - printf("USB: Failed to allocate configuration descriptor buffer\n"); + debugf("USB: Failed to allocate configuration descriptor buffer\n"); return -1; } @@ -203,7 +219,7 @@ int usb_configure_device(UsbDevice *device, uint8_t config_value) { config_value - 1, device->raw_config_desc, config_desc.wTotalLength); if (result < 0) { - printf("USB: Failed to get full configuration descriptor\n"); + debugf("USB: Failed to get full configuration descriptor\n"); free(device->raw_config_desc); device->raw_config_desc = NULL; return -1; @@ -219,12 +235,12 @@ int usb_configure_device(UsbDevice *device, uint8_t config_value) { USB_REQ_TYPE_DEVICE, USB_REQ_SET_CONFIGURATION, config_value, 0, NULL, 0, 5000); if (result < 0) { - printf("USB: Failed to set configuration %u\n", config_value); + debugf("USB: Failed to set configuration %u\n", config_value); return -1; } device->state = USB_DEVICE_STATE_CONFIGURED; - printf("USB: Device configured with configuration %u\n", config_value); + debugf("USB: Device configured with configuration %u\n", config_value); return 0; } @@ -240,7 +256,7 @@ UsbTransfer *usb_alloc_transfer(UsbDevice *device, uint8_t endpoint, UsbTransfer *transfer = calloc(1, sizeof(UsbTransfer)); if (!transfer) { - printf("USB: Failed to allocate transfer structure\n"); + debugf("USB: Failed to allocate transfer structure\n"); return NULL; } @@ -364,18 +380,20 @@ int usb_get_string_descriptor(UsbDevice *device, uint8_t string_index, // Get string descriptor in UTF-16 uint8_t string_desc[256]; - int result = usb_get_descriptor(device, USB_DESC_TYPE_STRING, string_index, - string_desc, sizeof(string_desc)); + 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) - uint8_t length = string_desc[0]; + const uint8_t length = string_desc[0]; if (length < 2) return -1; - size_t str_len = (length - 2) / 2; // UTF-16 string length - size_t copy_len = (str_len < buffer_size - 1) ? str_len : buffer_size - 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 @@ -464,7 +482,7 @@ int usb_core_init(void) { num_registered_hcds = 0; usb_core_initialized = true; - printf("USB: Core layer initialized\n"); + debugf("USB: Core layer initialized\n"); return 0; } @@ -477,5 +495,5 @@ void usb_core_shutdown(void) { // TODO: Clean up registered controllers and devices usb_core_initialized = false; - printf("USB: Core layer shutdown\n"); + debugf("USB: Core layer shutdown\n"); } \ No newline at end of file diff --git a/servers/xhcidrv/Makefile b/servers/xhcidrv/Makefile index 9141581e..2d4349d6 100644 --- a/servers/xhcidrv/Makefile +++ b/servers/xhcidrv/Makefile @@ -13,6 +13,9 @@ LDFLAGS=-L$(USB_CORE_DIR) -lusbcore # 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= diff --git a/servers/xhcidrv/main.c b/servers/xhcidrv/main.c index 265bb6f6..412ab913 100644 --- a/servers/xhcidrv/main.c +++ b/servers/xhcidrv/main.c @@ -114,9 +114,10 @@ static int xhci_initialize_driver(const uint64_t xhci_base, // Now enumerate device (this will do control transfers) if (usb_enumerate_device(usb_device) == 0) { - printf("USB device enumerated on port %u: VID:0x%04x " - "PID:0x%04x\n", - i, usb_device->device_desc.idVendor, + 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", diff --git a/servers/xhcidrv/xhci_hcd.c b/servers/xhcidrv/xhci_hcd.c index 202749ed..ed0b8914 100644 --- a/servers/xhcidrv/xhci_hcd.c +++ b/servers/xhcidrv/xhci_hcd.c @@ -75,45 +75,43 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, // Initialize base xHCI controller if (!xhci_controller_init(&xhci_hcd->base, base_addr, pci_config_base)) { - printf("xHCI HCD: Failed to initialize base controller\n"); + hcd_debugf("xHCI HCD: Failed to initialize base controller\n"); return -1; } // Initialize command ring if (xhci_ring_init(&xhci_hcd->command_ring, XHCI_RING_SIZE) != 0) { - printf("xHCI HCD: Failed to initialize command ring\n"); + hcd_debugf("xHCI HCD: Failed to initialize command ring\n"); return -1; } // Initialize event ring if (xhci_ring_init(&xhci_hcd->event_ring, XHCI_RING_SIZE) != 0) { - printf("xHCI HCD: Failed to initialize event ring\n"); + hcd_debugf("xHCI HCD: Failed to initialize event ring\n"); xhci_ring_free(&xhci_hcd->command_ring); return -1; } // Allocate and initialize DCBAA - SyscallResultA dcbaa_alloc; - dcbaa_alloc = anos_alloc_physical_pages(4096); + const SyscallResultA dcbaa_alloc = anos_alloc_physical_pages(4096); if (dcbaa_alloc.result != SYSCALL_OK) { - printf("xHCI HCD: Failed to allocate DCBAA - syscall result: " - "0x%016lx\n", - (uint64_t)dcbaa_alloc.result); - printf("xHCI HCD: Requested 1 page (4096 bytes)\n"); + 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 (4096 bytes)\n"); return -1; } xhci_hcd->dcbaa_physical = dcbaa_alloc.value; - SyscallResult dcbaa_map; - dcbaa_map = anos_map_physical( + const SyscallResult dcbaa_map = anos_map_physical( xhci_hcd->dcbaa_physical, (void *)(0x600000000 + xhci_hcd->dcbaa_physical), 4096, ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | ANOS_MAP_PHYSICAL_FLAG_NOCACHE); if (dcbaa_map.result != SYSCALL_OK) { - printf("xHCI HCD: Failed to map DCBAA\n"); + hcd_debugf("xHCI HCD: Failed to map DCBAA\n"); return -1; } @@ -133,19 +131,20 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, if (max_device_slots > XHCI_MAX_DEVICES) { max_device_slots = XHCI_MAX_DEVICES; } - printf("xHCI HCD: Setting CONFIG register - MaxDeviceSlots=%u\n", - max_device_slots); + 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); - // Verify CONFIG register - uint32_t config_readback = xhci_read32( +#ifdef DEBUG_XHCI_HCD + const uint32_t config_readback = xhci_read32( &xhci_hcd->base, xhci_hcd->base.op_regs, XHCI_OP_CONFIG); - printf("xHCI HCD: CONFIG register readback: 0x%08x (should be %u)\n", - config_readback, max_device_slots); + hcd_vdebugf("xHCI HCD: CONFIG register readback: 0x%08x (should be %u)\n", + config_readback, max_device_slots); +#endif - uint32_t hccparams1 = xhci_read32(&xhci_hcd->base, xhci_hcd->base.cap_regs, - XHCI_CAP_HCCPARAMS1); + 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); @@ -167,7 +166,7 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, #endif // Check extended capabilities pointer - uint16_t xecp = (hccparams1 >> 16) & 0xFFFF; + const uint16_t xecp = (hccparams1 >> 16) & 0xFFFF; hcd_vdebugf(" Extended capabilities pointer: 0x%04x\n", xecp); if (xecp != 0) { hcd_vdebugf( @@ -175,7 +174,7 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, "setup\n"); // Read first extended capability - uint32_t ext_cap = + const uint32_t ext_cap = xhci_read32(&xhci_hcd->base, xhci_hcd->base.cap_regs, xecp); uint8_t cap_id = ext_cap & 0xFF; @@ -242,14 +241,14 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, // Set up Event Ring Segment Table (ERST) if (xhci_setup_event_ring(xhci_hcd) != 0) { - printf("xHCI HCD: Failed to setup event ring\n"); + hcd_debugf("xHCI HCD: Failed to setup event ring\n"); return -1; } // Create USB host controller instance xhci_hcd->usb_hcd = malloc(sizeof(UsbHostController)); if (!xhci_hcd->usb_hcd) { - printf("xHCI HCD: Failed to allocate USB host controller\n"); + hcd_debugf("xHCI HCD: Failed to allocate USB host controller\n"); return -1; } @@ -315,7 +314,7 @@ int xhci_hcd_submit_transfer(UsbHostController *hcd, UsbTransfer *transfer) { case USB_TRANSFER_INTERRUPT: return xhci_submit_interrupt_transfer(xhci_hcd, transfer); default: - printf("xHCI HCD: Unsupported transfer type %d\n", transfer->type); + hcd_debugf("xHCI HCD: Unsupported transfer type %d\n", transfer->type); return -1; } } @@ -349,7 +348,7 @@ int xhci_hcd_set_address(UsbHostController *hcd, UsbDevice *device, if (slot_id == 0) { slot_id = xhci_alloc_device_slot(xhci_hcd); if (slot_id == 0) { - printf("xHCI HCD: Failed to allocate device slot\n"); + hcd_debugf("xHCI HCD: Failed to allocate device slot\n"); return -1; } device->hcd_private = (void *)(uintptr_t)slot_id; @@ -357,7 +356,7 @@ int xhci_hcd_set_address(UsbHostController *hcd, UsbDevice *device, // Setup device context if (xhci_setup_device_context(xhci_hcd, slot_id, device) != 0) { - printf("xHCI HCD: Failed to setup device context\n"); + hcd_debugf("xHCI HCD: Failed to setup device context\n"); return -1; } @@ -370,14 +369,14 @@ int xhci_hcd_set_address(UsbHostController *hcd, UsbDevice *device, int result = xhci_send_command(xhci_hcd, (XhciTrb *)&address_cmd); if (result != 0) { - printf("xHCI HCD: Address Device command failed\n"); + 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) { - printf("xHCI HCD: Address Device command timed out\n"); + hcd_debugf("xHCI HCD: Address Device command timed out\n"); return -1; } @@ -394,9 +393,9 @@ int xhci_hcd_enable_device(UsbHostController *hcd, UsbDevice *device) { hcd_debugf("xHCI HCD: Enabling device on port %u\n", device->port_number); // Allocate device slot - uint8_t slot_id = xhci_alloc_device_slot(xhci_hcd); + const uint8_t slot_id = xhci_alloc_device_slot(xhci_hcd); if (slot_id == 0) { - printf("xHCI HCD: Failed to allocate device slot\n"); + hcd_debugf("xHCI HCD: Failed to allocate device slot\n"); return -1; } @@ -407,33 +406,33 @@ int xhci_hcd_enable_device(UsbHostController *hcd, UsbDevice *device) { enable_cmd.control = TRB_CONTROL_CYCLE_BIT | (TRB_TYPE_ENABLE_SLOT << TRB_CONTROL_TYPE_SHIFT); - printf("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); + 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) { - printf("xHCI HCD: Enable Slot command failed\n"); + 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) { - printf("xHCI HCD: Enable Slot command timed out\n"); + hcd_debugf("xHCI HCD: Enable Slot command timed out\n"); xhci_free_device_slot(xhci_hcd, slot_id); return -1; } - printf("xHCI HCD: Enable Slot completed for slot %u, setting up device " - "context\n", - slot_id); + 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) { - printf("xHCI HCD: Failed to setup device context for slot %u\n", - slot_id); + 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; } @@ -445,24 +444,25 @@ int xhci_hcd_enable_device(UsbHostController *hcd, UsbDevice *device) { (TRB_TYPE_ADDRESS_DEVICE << TRB_CONTROL_TYPE_SHIFT) | (slot_id << TRB_CONTROL_SLOT_ID_SHIFT); - printf("xHCI HCD: Sending Address Device command for slot %u\n", slot_id); + 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) { - printf("xHCI HCD: Address Device command failed\n"); + 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) { - printf("xHCI HCD: Address Device command timed out\n"); + hcd_debugf("xHCI HCD: Address Device command timed out\n"); xhci_free_device_slot(xhci_hcd, slot_id); return -1; } - printf("xHCI HCD: Device fully enabled in slot %u with endpoint 0 " - "configured\n", - slot_id); + hcd_debugf("xHCI HCD: Device fully enabled in slot %u with endpoint 0 " + "configured\n", + slot_id); return 0; } @@ -620,7 +620,7 @@ int xhci_send_command(XhciHostController *xhci_hcd, XhciTrb *command_trb) { // Enqueue the command TRB to the command ring XhciTrb *ring_trb = xhci_ring_enqueue_trb(&xhci_hcd->command_ring); if (!ring_trb) { - printf("xHCI HCD: Failed to enqueue command TRB\n"); + hcd_debugf("xHCI HCD: Failed to enqueue command TRB\n"); return -1; } @@ -716,7 +716,7 @@ int xhci_wait_for_command_completion(XhciHostController *xhci_hcd, xhci_hcd->event_ring.dequeue_index, xhci_hcd->event_ring.consumer_cycle_state); -#ifdef VERY_NOISY_XHCI_HCD +#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; @@ -745,17 +745,19 @@ int xhci_wait_for_command_completion(XhciHostController *xhci_hcd, if (has_event) { const uint32_t trb_type = TRB_GET_TYPE(event_trb); - printf("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); + 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) { - printf("xHCI HCD: Command completed successfully\n"); + 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) { @@ -764,13 +766,15 @@ int xhci_wait_for_command_completion(XhciHostController *xhci_hcd, i, xhci_hcd->event_ring.dequeue_index, xhci_hcd->event_ring.consumer_cycle_state); } +#endif } // Sleep for 1ms anos_task_sleep_current(1000000); } - printf("xHCI HCD: Command completion timeout after %u ms\n", timeout_ms); + hcd_debugf("xHCI HCD: Command completion timeout after %u ms\n", + timeout_ms); return -1; } @@ -783,7 +787,7 @@ int xhci_wait_for_transfer_completion(XhciHostController *xhci_hcd, hcd_debugf("xHCI HCD: Waiting for transfer completion (timeout %u ms)\n", timeout_ms); - // Scan entire event ring for transfer events +#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++) { @@ -803,6 +807,7 @@ int xhci_wait_for_transfer_completion(XhciHostController *xhci_hcd, } } } +#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 " @@ -832,25 +837,26 @@ int xhci_wait_for_transfer_completion(XhciHostController *xhci_hcd, trb_cycle); if (trb_type == TRB_TYPE_TRANSFER) { - printf("xHCI HCD: Transfer event found - param=0x%016lx " - "status=0x%08x control=0x%08x\n", - event_trb->parameter, event_trb->status, - event_trb->control); + 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 - uint32_t completion_code = (event_trb->status >> 24) & 0xFF; - printf("xHCI HCD: Transfer completion code: %u (%s)\n", - completion_code, - xhci_completion_code_string(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) { - printf("xHCI HCD: Transfer completed successfully!\n"); + hcd_debugf("xHCI HCD: Transfer completed successfully!\n"); xhci_ring_inc_dequeue(&xhci_hcd->event_ring); return 0; } else { - printf("xHCI HCD: Transfer failed with completion code " - "%u\n", - completion_code); + hcd_debugf("xHCI HCD: Transfer failed with completion code " + "%u\n", + completion_code); xhci_ring_inc_dequeue(&xhci_hcd->event_ring); return -1; } @@ -869,7 +875,8 @@ int xhci_wait_for_transfer_completion(XhciHostController *xhci_hcd, anos_task_sleep_current(1000000); } - printf("xHCI HCD: Transfer completion timeout after %u ms\n", timeout_ms); + hcd_debugf("xHCI HCD: Transfer completion timeout after %u ms\n", + timeout_ms); return -1; } @@ -901,13 +908,13 @@ int xhci_setup_endpoint_ring(XhciHostController *xhci_hcd, uint8_t slot_id, // 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]) { - printf("xHCI HCD: Failed to allocate endpoint ring\n"); + 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) { - printf("xHCI HCD: Failed to initialize endpoint ring\n"); + hcd_debugf("xHCI HCD: Failed to initialize endpoint ring\n"); free(ring); xhci_hcd->endpoint_rings[slot_id][endpoint_id] = NULL; return -1; @@ -927,9 +934,9 @@ int xhci_setup_device_context(XhciHostController *xhci_hcd, uint8_t slot_id, } // Allocate input context (1 page should be enough for slot + ep0 context) - SyscallResultA input_ctx_alloc = anos_alloc_physical_pages(4096); + const SyscallResultA input_ctx_alloc = anos_alloc_physical_pages(4096); if (input_ctx_alloc.result != SYSCALL_OK) { - printf("xHCI HCD: Failed to allocate input context\n"); + hcd_debugf("xHCI HCD: Failed to allocate input context\n"); return -1; } @@ -941,7 +948,7 @@ int xhci_setup_device_context(XhciHostController *xhci_hcd, uint8_t slot_id, ANOS_MAP_PHYSICAL_FLAG_NOCACHE); if (input_ctx_map.result != SYSCALL_OK) { - printf("xHCI HCD: Failed to map input context\n"); + hcd_debugf("xHCI HCD: Failed to map input context\n"); return -1; } @@ -995,7 +1002,7 @@ int xhci_setup_device_context(XhciHostController *xhci_hcd, uint8_t slot_id, // Set up endpoint 0 transfer ring if (xhci_setup_endpoint_ring(xhci_hcd, slot_id, 0) != 0) { - printf("xHCI HCD: Failed to setup endpoint 0 ring\n"); + hcd_debugf("xHCI HCD: Failed to setup endpoint 0 ring\n"); return -1; } @@ -1006,9 +1013,9 @@ int xhci_setup_device_context(XhciHostController *xhci_hcd, uint8_t slot_id, ep0_context[3] = (uint32_t)(ep0_ring->trbs_physical >> 32); // Allocate actual device context (separate from input context) - SyscallResultA device_ctx_alloc = anos_alloc_physical_pages(4096); + const SyscallResultA device_ctx_alloc = anos_alloc_physical_pages(4096); if (device_ctx_alloc.result != SYSCALL_OK) { - printf("xHCI HCD: Failed to allocate device context\n"); + hcd_debugf("xHCI HCD: Failed to allocate device context\n"); return -1; } @@ -1020,7 +1027,7 @@ int xhci_setup_device_context(XhciHostController *xhci_hcd, uint8_t slot_id, ANOS_MAP_PHYSICAL_FLAG_NOCACHE); if (device_ctx_map.result != SYSCALL_OK) { - printf("xHCI HCD: Failed to map device context\n"); + hcd_debugf("xHCI HCD: Failed to map device context\n"); return -1; } @@ -1058,7 +1065,7 @@ int xhci_submit_control_transfer(XhciHostController *xhci_hcd, // Get device slot ID const uint8_t slot_id = (uint8_t)(uintptr_t)transfer->device->hcd_private; if (slot_id == 0) { - printf("xHCI HCD: No device slot allocated for control transfer\n"); + hcd_debugf("xHCI HCD: No device slot allocated for control transfer\n"); return -1; } @@ -1071,33 +1078,37 @@ int xhci_submit_control_transfer(XhciHostController *xhci_hcd, // Get endpoint 0's transfer ring XhciRing *ep0_ring = xhci_hcd->endpoint_rings[slot_id][0]; if (!ep0_ring) { - printf("xHCI HCD: No endpoint ring for slot %u endpoint 0\n", slot_id); + 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)) { - printf("xHCI HCD: Not enough space in endpoint transfer ring\n"); + hcd_debugf("xHCI HCD: Not enough space in endpoint transfer ring\n"); return -1; } // Build and enqueue TRBs for the control transfer - int result = xhci_build_control_transfer_trbs(xhci_hcd, transfer, slot_id); + const int result = + xhci_build_control_transfer_trbs(xhci_hcd, transfer, slot_id); if (result != 0) { - printf("xHCI HCD: Failed to build control transfer TRBs\n"); + hcd_debugf("xHCI HCD: Failed to build control transfer TRBs\n"); return -1; } // Ring doorbell to notify hardware - printf("xHCI HCD: Control transfer TRBs built, ringing doorbell for slot " - "%u EP0\n", - slot_id); + 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"); - int completion_result = xhci_wait_for_transfer_completion(xhci_hcd, 5000); + const int completion_result = + xhci_wait_for_transfer_completion(xhci_hcd, 5000); if (completion_result != 0) { - printf("xHCI HCD: Transfer completion timeout\n"); + hcd_debugf("xHCI HCD: Transfer completion timeout\n"); transfer->status = USB_TRANSFER_STATUS_ERROR; return -1; } @@ -1105,21 +1116,24 @@ int xhci_submit_control_transfer(XhciHostController *xhci_hcd, // Copy data back for IN transfers after completion if (setup->wLength > 0 && (setup->bmRequestType & 0x80) && transfer->buffer) { - uint64_t data_phys = (uint64_t)(uintptr_t)transfer->hcd_private; + const uint64_t data_phys = (uint64_t)(uintptr_t)transfer->hcd_private; if (data_phys) { - volatile void *data_virt = + 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 - printf("xHCI HCD: Raw data received: "); - uint8_t *data_bytes = (uint8_t *)transfer->buffer; + 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++) { - printf("%02x ", data_bytes[i]); + hcd_debugf("%02x ", data_bytes[i]); } - printf("\n"); + hcd_debugf("\n"); +#endif } } @@ -1150,7 +1164,8 @@ int xhci_submit_interrupt_transfer(XhciHostController *xhci_hcd, // ============================================================================= int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, - UsbTransfer *transfer, uint8_t slot_id) { + UsbTransfer *transfer, + const uint8_t slot_id) { if (!xhci_hcd || !transfer || !transfer->setup_packet) { return -1; } @@ -1164,12 +1179,10 @@ int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, const SyscallResultA setup_alloc = anos_alloc_physical_pages(4096); // 1 page if (setup_alloc.result != SYSCALL_OK) { - printf("xHCI HCD: Failed to allocate setup packet memory\n"); + hcd_debugf("xHCI HCD: Failed to allocate setup packet memory\n"); return -1; } - printf("!!!!!!!!!!!!!!! 0x%016lx\n", setup_alloc.value); - // Map setup packet memory const SyscallResult setup_map = anos_map_physical( setup_alloc.value, (void *)(0x700000000 + setup_alloc.value), 4096, @@ -1177,7 +1190,7 @@ int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, ANOS_MAP_PHYSICAL_FLAG_NOCACHE); if (setup_map.result != SYSCALL_OK) { - printf("xHCI HCD: Failed to map setup packet memory\n"); + hcd_debugf("xHCI HCD: Failed to map setup packet memory\n"); return -1; } @@ -1187,18 +1200,19 @@ int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, *setup_phys = *setup; // Debug: show what we're sending - printf("xHCI HCD: Setup packet: %02x %02x %04x %04x %04x\n", - setup->bmRequestType, setup->bRequest, setup->wValue, setup->wIndex, - setup->wLength); + hcd_debugf("xHCI HCD: Setup packet: %02x %02x %04x %04x %04x\n", + setup->bmRequestType, setup->bRequest, setup->wValue, + setup->wIndex, setup->wLength); - printf("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); + 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) { - printf("xHCI HCD: Failed to enqueue setup TRB\n"); + hcd_debugf("xHCI HCD: Failed to enqueue setup TRB\n"); return -1; } @@ -1237,7 +1251,7 @@ int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, const SyscallResultA data_alloc = anos_alloc_physical_pages(data_size); if (data_alloc.result != SYSCALL_OK) { - printf("xHCI HCD: Failed to allocate data buffer\n"); + hcd_debugf("xHCI HCD: Failed to allocate data buffer\n"); return -1; } @@ -1248,7 +1262,7 @@ int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, ANOS_MAP_PHYSICAL_FLAG_NOCACHE); if (data_map.result != SYSCALL_OK) { - printf("xHCI HCD: Failed to map data buffer\n"); + hcd_debugf("xHCI HCD: Failed to map data buffer\n"); return -1; } @@ -1260,7 +1274,7 @@ int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, XhciTrb *data_trb = xhci_ring_enqueue_trb(ring); if (!data_trb) { - printf("xHCI HCD: Failed to enqueue data TRB\n"); + hcd_debugf("xHCI HCD: Failed to enqueue data TRB\n"); return -1; } @@ -1283,7 +1297,7 @@ int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, // 3. Status Stage TRB XhciTrb *status_trb = xhci_ring_enqueue_trb(ring); if (!status_trb) { - printf("xHCI HCD: Failed to enqueue status TRB\n"); + hcd_debugf("xHCI HCD: Failed to enqueue status TRB\n"); return -1; } @@ -1327,9 +1341,9 @@ void xhci_ring_doorbell(XhciHostController *xhci_hcd, uint8_t slot_id, if (xhci_hcd->base.doorbell_regs) { xhci_write32(&xhci_hcd->base, xhci_hcd->base.doorbell_regs, doorbell_offset, doorbell_value); - printf("xHCI HCD: Doorbell written to hardware\n"); + hcd_debugf("xHCI HCD: Doorbell written to hardware\n"); } else { - printf("xHCI HCD: ERROR - No doorbell registers mapped\n"); + hcd_debugf("xHCI HCD: ERROR - No doorbell registers mapped\n"); } } @@ -1341,20 +1355,20 @@ int xhci_setup_event_ring(XhciHostController *xhci_hcd) { hcd_debugf("xHCI HCD: Setting up event ring segment table\n"); // Allocate Event Ring Segment Table (ERST) - SyscallResultA erst_alloc = anos_alloc_physical_pages(4096); + const SyscallResultA erst_alloc = anos_alloc_physical_pages(4096); if (erst_alloc.result != SYSCALL_OK) { - printf("xHCI HCD: Failed to allocate ERST\n"); + hcd_debugf("xHCI HCD: Failed to allocate ERST\n"); return -1; } // Map ERST - SyscallResult erst_map = anos_map_physical( + const SyscallResult erst_map = anos_map_physical( erst_alloc.value, (void *)(0xA00000000 + erst_alloc.value), 4096, ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | ANOS_MAP_PHYSICAL_FLAG_NOCACHE); if (erst_map.result != SYSCALL_OK) { - printf("xHCI HCD: Failed to map ERST\n"); + hcd_debugf("xHCI HCD: Failed to map ERST\n"); return -1; } @@ -1367,19 +1381,19 @@ int xhci_setup_event_ring(XhciHostController *xhci_hcd) { // Write ERST registers to runtime register space if (!xhci_hcd->base.runtime_regs) { - printf("xHCI HCD: ERROR - No runtime registers mapped\n"); + hcd_debugf("xHCI HCD: ERROR - No runtime registers mapped\n"); return -1; } // Interrupter 0 registers (offset 0x20 from runtime base) - const uint32_t interrupter_base = 0x20; + 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); // CRITICAL: Disable interrupts and use polling mode - printf("xHCI HCD: Disabling interrupts - using polling mode\n"); + 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) @@ -1402,6 +1416,8 @@ int xhci_setup_event_ring(XhciHostController *xhci_hcd) { "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, @@ -1419,12 +1435,12 @@ int xhci_setup_event_ring(XhciHostController *xhci_hcd) { xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, interrupter_base + 0x1C); - printf("xHCI HCD: ERST registers verification:\n"); - printf(" ERSTSZ: 0x%08x (should be 1)\n", erstsz_read); - printf(" ERSTBA: 0x%08x%08x (should be 0x%016lx)\n", erstba_high, - erstba_low, erst_alloc.value); - printf(" ERDP: 0x%08x%08x (should be 0x%016lx)\n", erdp_high, erdp_low, - xhci_hcd->event_ring.trbs_physical); + 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 = @@ -1434,6 +1450,8 @@ int xhci_setup_event_ring(XhciHostController *xhci_hcd) { 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; } \ No newline at end of file From 354babfeadd39966be4bbb3e0fa3634fd4d85def Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Mon, 8 Sep 2025 19:42:17 +0100 Subject: [PATCH 12/21] Fix up incorrect target overrides in makefiles --- servers/common/usb/Makefile | 63 ++++++++++++++++++++++++++++++++----- servers/xhcidrv/Makefile | 7 +---- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/servers/common/usb/Makefile b/servers/common/usb/Makefile index e5f226ac..165a1899 100644 --- a/servers/common/usb/Makefile +++ b/servers/common/usb/Makefile @@ -1,15 +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 -BINARY=$(USB_COMMON_NAME) -BINARY_OBJS=usb_core.o +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= -include ../../server_common.mk +.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 -# Override default target to create a library instead of executable -$(BINARY_NAME): $(BINARY_OBJS) - $(XAR) rcs lib$(USB_COMMON_NAME).a $(BINARY_OBJS) +$(LIBRARY_NAME): $(LIBRARY_OBJS) + $(XAR) rcs lib$(USB_COMMON_NAME).a $(LIBRARY_OBJS) clean: - rm -f *.o *.a *.elf \ No newline at end of file + rm -f *.o *.a \ No newline at end of file diff --git a/servers/xhcidrv/Makefile b/servers/xhcidrv/Makefile index 2d4349d6..4431b8a0 100644 --- a/servers/xhcidrv/Makefile +++ b/servers/xhcidrv/Makefile @@ -1,6 +1,6 @@ SERVER_NAME=xhcidrv BINARY=xhcidrv -BINARY_OBJS=main.o xhci.o pci.o xhci_rings.o xhci_hcd.o +BINARY_OBJS=main.o xhci.o pci.o xhci_rings.o xhci_hcd.o $(USB_CORE_DIR)/libusbcore.a # Link with USB core library USB_CORE_DIR=../common/usb @@ -21,11 +21,6 @@ CDEFS= include ../server_common.mk -# Build USB core library first $(USB_CORE_DIR)/libusbcore.a: $(MAKE) -C $(USB_CORE_DIR) -# Override linking to include USB core library -$(BINARY_NAME): $(BINARY_OBJS) $(USB_CORE_DIR)/libusbcore.a - $(XCC) -o $@ $(BINARY_OBJS) $(LDFLAGS) - chmod a-x $@ \ No newline at end of file From 369754a0555a22dacf72c1903aa96917f9bea7a1 Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Mon, 8 Sep 2025 20:08:00 +0100 Subject: [PATCH 13/21] Fixes for conditional debug builds --- servers/xhcidrv/xhci.c | 2 ++ servers/xhcidrv/xhci_hcd.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/servers/xhcidrv/xhci.c b/servers/xhcidrv/xhci.c index 23243d7e..02595693 100644 --- a/servers/xhcidrv/xhci.c +++ b/servers/xhcidrv/xhci.c @@ -269,12 +269,14 @@ 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, diff --git a/servers/xhcidrv/xhci_hcd.c b/servers/xhcidrv/xhci_hcd.c index ed0b8914..07a80b40 100644 --- a/servers/xhcidrv/xhci_hcd.c +++ b/servers/xhcidrv/xhci_hcd.c @@ -137,10 +137,12 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, 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( @@ -583,6 +585,7 @@ int xhci_send_command(XhciHostController *xhci_hcd, XhciTrb *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, @@ -610,7 +613,7 @@ int xhci_send_command(XhciHostController *xhci_hcd, XhciTrb *command_trb) { (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); From 3aec53bb122c8ecfdd37d1c095d788a4cf03724d Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Mon, 8 Sep 2025 20:15:37 +0100 Subject: [PATCH 14/21] Support max 32 instead of 16 xHCI ports --- servers/xhcidrv/main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/servers/xhcidrv/main.c b/servers/xhcidrv/main.c index 412ab913..2cacb546 100644 --- a/servers/xhcidrv/main.c +++ b/servers/xhcidrv/main.c @@ -38,8 +38,10 @@ #define ops_vdebugf(...) #endif +static constexpr int MAX_PORTS = 32; + static XhciHostController xhci_hcd = {0}; -static XHCIPort ports[16] = {{0}}; // Support up to 16 ports +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; @@ -99,7 +101,7 @@ static int xhci_initialize_driver(const uint64_t xhci_base, #endif xhci_hcd.base.active_ports = 0; - for (uint8_t i = 0; i < xhci_hcd.base.max_ports && i < 16; i++) { + 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)) { xhci_hcd.base.active_ports |= (1U << i); From 4a8fd4614328c081b86ed60cbffa346d9a4aadc6 Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Tue, 9 Sep 2025 08:47:33 +0100 Subject: [PATCH 15/21] Tidying up a bit --- servers/xhcidrv/Makefile | 2 +- servers/xhcidrv/include/pci.h | 2 + servers/xhcidrv/include/xhci_hcd.h | 2 +- servers/xhcidrv/main.c | 237 +++++++++++++---------------- servers/xhcidrv/message_loop.c | 59 +++++++ servers/xhcidrv/message_loop.h | 15 ++ servers/xhcidrv/pci.c | 2 + servers/xhcidrv/xhci.c | 42 +++-- servers/xhcidrv/xhci_hcd.c | 237 +++++++++++++++++------------ servers/xhcidrv/xhci_rings.c | 21 +-- 10 files changed, 355 insertions(+), 264 deletions(-) create mode 100644 servers/xhcidrv/message_loop.c create mode 100644 servers/xhcidrv/message_loop.h diff --git a/servers/xhcidrv/Makefile b/servers/xhcidrv/Makefile index 4431b8a0..26f750d1 100644 --- a/servers/xhcidrv/Makefile +++ b/servers/xhcidrv/Makefile @@ -1,6 +1,6 @@ SERVER_NAME=xhcidrv BINARY=xhcidrv -BINARY_OBJS=main.o xhci.o pci.o xhci_rings.o xhci_hcd.o $(USB_CORE_DIR)/libusbcore.a +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 diff --git a/servers/xhcidrv/include/pci.h b/servers/xhcidrv/include/pci.h index 3481ba75..d7450f78 100644 --- a/servers/xhcidrv/include/pci.h +++ b/servers/xhcidrv/include/pci.h @@ -3,6 +3,8 @@ * 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 diff --git a/servers/xhcidrv/include/xhci_hcd.h b/servers/xhcidrv/include/xhci_hcd.h index a8100f30..4461c657 100644 --- a/servers/xhcidrv/include/xhci_hcd.h +++ b/servers/xhcidrv/include/xhci_hcd.h @@ -13,7 +13,7 @@ #include #include -#include "../../common/usb/usb_core.h" +#include "usb/usb_core.h" #include "xhci.h" #include "xhci_rings.h" diff --git a/servers/xhcidrv/main.c b/servers/xhcidrv/main.c index 2cacb546..a8e87695 100644 --- a/servers/xhcidrv/main.c +++ b/servers/xhcidrv/main.c @@ -8,14 +8,17 @@ #include #include #include +#include #include #include #include "../common/device_types.h" #include "../common/usb/usb_core.h" -#include "include/xhci.h" -#include "include/xhci_hcd.h" + +#include "message_loop.h" +#include "xhci.h" +#include "xhci_hcd.h" #ifndef VERSTR #warning Version String not defined (-DVERSTR); Using default @@ -46,6 +49,50 @@ 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 @@ -85,59 +132,7 @@ static int xhci_initialize_driver(const uint64_t xhci_base, return -1; } -#ifdef DEBUG_XHCI_INIT - // Initialize and scan all ports, create USB devices through USB core - printf("xHCI: Scanning %u ports (max_ports=%u)\n", xhci_hcd.base.max_ports, - xhci_hcd.base.max_ports); - - // Debug: manually read first few port registers - printf("xHCI: Manual port register reads:\n"); - for (uint8_t j = 0; j < 8 && j < xhci_hcd.base.max_ports; j++) { - uint32_t portsc = - xhci_read32(&xhci_hcd.base, xhci_hcd.base.port_regs, j * 0x10); - printf(" Port %u (offset 0x%02x): PORTSC=0x%08x, CCS=%s\n", j, - j * 0x10, portsc, (portsc & 0x1) ? "Connected" : "Disconnected"); - } -#endif - - xhci_hcd.base.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)) { - xhci_hcd.base.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) { - // First 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) { - - // Now 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 - } - } + xhci_hcd.base.active_ports = xhci_init_activate_ports(); #ifdef DEBUG_XHCI_INIT printf("Active ports: 0x%08x\n", xhci_hcd.base.active_ports); @@ -146,28 +141,8 @@ static int xhci_initialize_driver(const uint64_t xhci_base, return 0; } -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; +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; @@ -184,24 +159,14 @@ static bool register_with_devman(void) { "xHCI Controller"); snprintf(controller_info->driver_name, sizeof(controller_info->driver_name), "xhcidrv"); +} - 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; - } +static void register_usb_devices_with_devman(const uint64_t xhci_controller_id, + char *reg_buffer) { + DeviceRegistrationMessage *reg_msg = + (DeviceRegistrationMessage *)reg_buffer; - // Register each connected USB device - for (uint8_t i = 0; i < xhci_hcd.base.max_ports && i < 16; i++) { + 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; @@ -236,8 +201,9 @@ static bool register_with_devman(void) { strncpy(usb_info->serial_number, ports[i].serial_number, sizeof(usb_info->serial_number) - 1); - msg_size = + constexpr size_t msg_size = sizeof(DeviceRegistrationMessage) + sizeof(UsbDeviceInfo); + const SyscallResult reg_result = anos_send_message(devman_channel, 0, msg_size, reg_buffer); @@ -251,18 +217,54 @@ static bool register_with_devman(void) { } } } - - return true; } -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); +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); - // For now, just acknowledge any messages - // TODO: Implement proper USB device communication - anos_reply_message(msg_cookie, 0); + 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) { @@ -280,9 +282,9 @@ int main(const int argc, char **argv) { printf(" @ xHCI:0x%s PCI:0x%s Parent:%s\n", argv[1], argv[2], argv[3]); - const uint64_t xhci_base = strtoull(argv[1], NULL, 16); - const uint64_t pci_config_base = strtoull(argv[2], NULL, 16); - pci_parent_id = strtoull(argv[3], NULL, 10); + 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"); @@ -297,26 +299,5 @@ int main(const int argc, char **argv) { ops_debugf("xHCI driver ready, entering message loop...\n"); - // Main message loop - 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); - } - } + 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 index bfcf1a0a..15543772 100644 --- a/servers/xhcidrv/pci.c +++ b/servers/xhcidrv/pci.c @@ -17,6 +17,8 @@ #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; diff --git a/servers/xhcidrv/xhci.c b/servers/xhcidrv/xhci.c index 02595693..450fd755 100644 --- a/servers/xhcidrv/xhci.c +++ b/servers/xhcidrv/xhci.c @@ -56,6 +56,9 @@ void xhci_write32(const XHCIController *controller, volatile void *base, 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)); @@ -189,10 +192,13 @@ bool xhci_controller_reset(const XHCIController *controller) { 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); } @@ -213,10 +219,12 @@ bool xhci_controller_start(const XHCIController *controller) { 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); } @@ -237,10 +245,12 @@ bool xhci_controller_stop(const XHCIController *controller) { 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); } @@ -294,7 +304,7 @@ bool xhci_port_scan(XHCIPort *port) { port->port_num); // Initiate port reset by setting PR bit (bit 4) - uint32_t reset_portsc = portsc | XHCI_PORTSC_PR; + const uint32_t reset_portsc = portsc | XHCI_PORTSC_PR; xhci_write32(port->controller, port->controller->port_regs, port_offset + XHCI_PORT_SC, reset_portsc); @@ -316,10 +326,7 @@ bool xhci_port_scan(XHCIPort *port) { break; } - // Sleep 1ms - // anos_task_sleep_current(1000000); // Uncomment if needed - for (volatile int i = 0; i < 100000; i++) - ; // Simple delay + anos_task_sleep_current(1000000); } if (reset_timeout <= 0) { @@ -368,27 +375,13 @@ bool xhci_port_scan(XHCIPort *port) { port->port_num, speed_str, port_power ? "On" : "Off", link_state, port->enabled ? "Yes" : "No"); - // Create device info based on detected characteristics - port->vendor_id = - 0x0000; // Would need USB enumeration to get real VID/PID + // 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; - // Guess device class based on speed (rough heuristic) - switch (port->speed) { - case XHCI_SPEED_LOW: - port->device_class = 0x03; // HID (keyboards, mice often low speed) - break; - case XHCI_SPEED_FULL: - case XHCI_SPEED_HIGH: - port->device_class = 0x08; // Mass Storage (common for USB drives) - break; - case XHCI_SPEED_SUPER: - port->device_class = 0x08; // Mass Storage (USB 3.0 drives) - break; - default: - port->device_class = 0x00; // Unknown - break; - } + port->device_class = 0; snprintf(port->manufacturer, sizeof(port->manufacturer), "Unknown Manufacturer"); @@ -398,6 +391,7 @@ bool xhci_port_scan(XHCIPort *port) { "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); diff --git a/servers/xhcidrv/xhci_hcd.c b/servers/xhcidrv/xhci_hcd.c index 07a80b40..47b7388a 100644 --- a/servers/xhcidrv/xhci_hcd.c +++ b/servers/xhcidrv/xhci_hcd.c @@ -14,9 +14,9 @@ #include -#include "../../common/usb/usb_core.h" -#include "../../common/usb/usb_spec.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__) @@ -30,17 +30,23 @@ #define hcd_vdebugf(...) #endif -// Forward declarations +#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); // ============================================================================= @@ -65,40 +71,132 @@ static UsbHostControllerOps xhci_hcd_ops = { // Host Controller Setup // ============================================================================= -int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, - uint64_t pci_config_base) { +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; +} + +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)); - // Initialize base xHCI controller if (!xhci_controller_init(&xhci_hcd->base, base_addr, pci_config_base)) { hcd_debugf("xHCI HCD: Failed to initialize base controller\n"); return -1; } - // Initialize command ring if (xhci_ring_init(&xhci_hcd->command_ring, XHCI_RING_SIZE) != 0) { hcd_debugf("xHCI HCD: Failed to initialize command ring\n"); return -1; } - // Initialize event ring 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; } - // Allocate and initialize DCBAA - const SyscallResultA dcbaa_alloc = anos_alloc_physical_pages(4096); + 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 (4096 bytes)\n"); + hcd_debugf("xHCI HCD: Requested 1 page (%d bytes)\n", VM_PAGE_SIZE); return -1; } @@ -106,7 +204,7 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, const SyscallResult dcbaa_map = anos_map_physical( xhci_hcd->dcbaa_physical, - (void *)(0x600000000 + xhci_hcd->dcbaa_physical), 4096, + (void *)(0x600000000 + xhci_hcd->dcbaa_physical), VM_PAGE_SIZE, ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | ANOS_MAP_PHYSICAL_FLAG_NOCACHE); @@ -131,8 +229,10 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, 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); @@ -147,6 +247,7 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, 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); @@ -168,78 +269,20 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, #endif // Check extended capabilities pointer - const uint16_t xecp = (hccparams1 >> 16) & 0xFFFF; + 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"); - // 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"); - } - } - } - } + xhci_handle_extended_capability_setup(xhci_hcd, xecp); } // Set command ring control register - 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)); + xhci_set_command_ring_register(xhci_hcd); // Set up Event Ring Segment Table (ERST) if (xhci_setup_event_ring(xhci_hcd) != 0) { @@ -248,22 +291,11 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, uint64_t base_addr, } // Create USB host controller instance - xhci_hcd->usb_hcd = malloc(sizeof(UsbHostController)); - if (!xhci_hcd->usb_hcd) { - hcd_debugf("xHCI HCD: Failed to allocate USB host controller\n"); + if (xhci_init_usb_hcd(xhci_hcd) != 0) { + hcd_debugf("xHCI HCD: Failed to initialize 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 = xhci_hcd; - 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); @@ -282,7 +314,7 @@ void xhci_hcd_shutdown(XhciHostController *xhci_hcd) { // Clean up USB host controller if (xhci_hcd->usb_hcd) { free(xhci_hcd->usb_hcd); - xhci_hcd->usb_hcd = NULL; + xhci_hcd->usb_hcd = nullptr; } // TODO: Free device contexts and other allocated memory @@ -937,7 +969,8 @@ int xhci_setup_device_context(XhciHostController *xhci_hcd, uint8_t slot_id, } // Allocate input context (1 page should be enough for slot + ep0 context) - const SyscallResultA input_ctx_alloc = anos_alloc_physical_pages(4096); + 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; @@ -946,7 +979,7 @@ int xhci_setup_device_context(XhciHostController *xhci_hcd, uint8_t slot_id, // Map input context SyscallResult input_ctx_map = anos_map_physical( input_ctx_alloc.value, - (void *)(0x900000000 + input_ctx_alloc.value), 4096, + (void *)(0x900000000 + input_ctx_alloc.value), VM_PAGE_SIZE, ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | ANOS_MAP_PHYSICAL_FLAG_NOCACHE); @@ -957,7 +990,7 @@ int xhci_setup_device_context(XhciHostController *xhci_hcd, uint8_t slot_id, volatile uint8_t *input_context = (volatile uint8_t *)(0x900000000 + input_ctx_alloc.value); - memset((void *)input_context, 0, 4096); + 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; @@ -1016,7 +1049,8 @@ int xhci_setup_device_context(XhciHostController *xhci_hcd, uint8_t slot_id, 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(4096); + 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; @@ -1025,7 +1059,7 @@ int xhci_setup_device_context(XhciHostController *xhci_hcd, uint8_t slot_id, // Map device context SyscallResult device_ctx_map = anos_map_physical( device_ctx_alloc.value, - (void *)(0xB00000000 + device_ctx_alloc.value), 4096, + (void *)(0xB00000000 + device_ctx_alloc.value), VM_PAGE_SIZE, ANOS_MAP_PHYSICAL_FLAG_READ | ANOS_MAP_PHYSICAL_FLAG_WRITE | ANOS_MAP_PHYSICAL_FLAG_NOCACHE); @@ -1180,7 +1214,7 @@ int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, // Allocate physical memory for setup packet const SyscallResultA setup_alloc = - anos_alloc_physical_pages(4096); // 1 page + 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; @@ -1188,7 +1222,8 @@ int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, // Map setup packet memory const SyscallResult setup_map = anos_map_physical( - setup_alloc.value, (void *)(0x700000000 + setup_alloc.value), 4096, + 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); @@ -1249,8 +1284,9 @@ int xhci_build_control_transfer_trbs(XhciHostController *xhci_hcd, // 2. Data Stage TRB (if needed) if (setup->wLength > 0 && transfer->buffer) { // Allocate and map data buffer - const size_t data_pages = (setup->wLength + 4095) / 4096; - const size_t data_size = data_pages * 4096; + 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) { @@ -1358,7 +1394,7 @@ int xhci_setup_event_ring(XhciHostController *xhci_hcd) { 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(4096); + 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; @@ -1366,7 +1402,8 @@ int xhci_setup_event_ring(XhciHostController *xhci_hcd) { // Map ERST const SyscallResult erst_map = anos_map_physical( - erst_alloc.value, (void *)(0xA00000000 + erst_alloc.value), 4096, + 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); diff --git a/servers/xhcidrv/xhci_rings.c b/servers/xhcidrv/xhci_rings.c index 17dcbc09..89e82bf8 100644 --- a/servers/xhcidrv/xhci_rings.c +++ b/servers/xhcidrv/xhci_rings.c @@ -87,21 +87,21 @@ void xhci_ring_free(XhciRing *ring) { } // TODO: Unmap virtual memory and free physical pages - ring->trbs = NULL; + ring->trbs = nullptr; ring->trbs_physical = 0; memset(ring, 0, sizeof(XhciRing)); } XhciTrb *xhci_ring_enqueue_trb(XhciRing *ring) { if (!ring || !ring->trbs) { - return NULL; + return nullptr; } // Check if we have space (leave one slot for link TRB if needed) 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 NULL; + return nullptr; } XhciTrb *trb = (XhciTrb *)&ring->trbs[ring->enqueue_index]; @@ -118,20 +118,20 @@ XhciTrb *xhci_ring_enqueue_trb(XhciRing *ring) { volatile XhciTrb *xhci_ring_dequeue_trb(XhciRing *ring) { if (!ring || !ring->trbs) { - return NULL; + return nullptr; } // Check if ring is empty if (ring->enqueue_index == ring->dequeue_index) { - return NULL; + return nullptr; } volatile XhciTrb *trb = &ring->trbs[ring->dequeue_index]; // Check cycle bit - bool trb_cycle = (trb->control & TRB_CONTROL_CYCLE_BIT) != 0; + const bool trb_cycle = (trb->control & TRB_CONTROL_CYCLE_BIT) != 0; if (trb_cycle != ring->consumer_cycle_state) { - return NULL; // TRB not ready + return nullptr; // TRB not ready } ring_debugf("xHCI: Dequeuing TRB at index %u, cycle=%u\n", @@ -156,8 +156,9 @@ bool xhci_ring_has_space(XhciRing *ring, uint32_t num_trbs) { } void xhci_ring_inc_enqueue(XhciRing *ring) { - if (!ring) + if (!ring) { return; + } ring->enqueue_index = (ring->enqueue_index + 1) % ring->size; ring->enqueued_count++; @@ -189,7 +190,7 @@ void xhci_ring_inc_dequeue(XhciRing *ring) { // Utility Functions // ============================================================================= -const char *xhci_trb_type_string(uint32_t trb_type) { +const char *xhci_trb_type_string(const uint32_t trb_type) { switch (trb_type) { case TRB_TYPE_NORMAL: return "Normal"; @@ -236,7 +237,7 @@ const char *xhci_trb_type_string(uint32_t trb_type) { } } -const char *xhci_completion_code_string(uint32_t completion_code) { +const char *xhci_completion_code_string(const uint32_t completion_code) { switch (completion_code) { case XHCI_COMP_SUCCESS: return "Success"; From 00859c6f6e4e47d8e5b031890ed3c7139ac1f46e Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Tue, 9 Sep 2025 20:11:52 +0100 Subject: [PATCH 16/21] Initial work to enable MSI-X on xHCI --- servers/xhcidrv/include/pci.h | 32 +++ servers/xhcidrv/include/xhci.h | 18 +- servers/xhcidrv/include/xhci_rings.h | 1 + servers/xhcidrv/pci.c | 155 +++++++++++ servers/xhcidrv/xhci.c | 39 ++- servers/xhcidrv/xhci_hcd.c | 377 ++++++++++++++++++++++++++- 6 files changed, 609 insertions(+), 13 deletions(-) diff --git a/servers/xhcidrv/include/pci.h b/servers/xhcidrv/include/pci.h index d7450f78..f6aaec6a 100644 --- a/servers/xhcidrv/include/pci.h +++ b/servers/xhcidrv/include/pci.h @@ -29,6 +29,31 @@ #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); @@ -37,4 +62,11 @@ 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 index 3dc57719..1b2bf7a6 100644 --- a/servers/xhcidrv/include/xhci.h +++ b/servers/xhcidrv/include/xhci.h @@ -83,10 +83,21 @@ #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 + 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 @@ -100,6 +111,11 @@ typedef struct { 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; diff --git a/servers/xhcidrv/include/xhci_rings.h b/servers/xhcidrv/include/xhci_rings.h index 39aa3af7..9b6f2c7e 100644 --- a/servers/xhcidrv/include/xhci_rings.h +++ b/servers/xhcidrv/include/xhci_rings.h @@ -90,6 +90,7 @@ typedef struct { ((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) | \ diff --git a/servers/xhcidrv/pci.c b/servers/xhcidrv/pci.c index 15543772..f1d9765e 100644 --- a/servers/xhcidrv/pci.c +++ b/servers/xhcidrv/pci.c @@ -80,4 +80,159 @@ bool xhci_pci_enable_device(const uint64_t config_base) { 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 index 450fd755..a7db1c59 100644 --- a/servers/xhcidrv/xhci.c +++ b/servers/xhcidrv/xhci.c @@ -84,11 +84,27 @@ bool xhci_controller_init(XHCIController *controller, const uint64_t base_addr, // 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)); @@ -209,12 +225,31 @@ bool xhci_controller_reset(const XHCIController *controller) { bool xhci_controller_start(const XHCIController *controller) { init_debugf("Starting xHCI controller...\n"); - // Set the run bit + // 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; // Remove XHCI_CMD_INTE for polling mode + 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 = diff --git a/servers/xhcidrv/xhci_hcd.c b/servers/xhcidrv/xhci_hcd.c index 47b7388a..d00bf678 100644 --- a/servers/xhcidrv/xhci_hcd.c +++ b/servers/xhcidrv/xhci_hcd.c @@ -14,6 +14,7 @@ #include +#include "include/pci.h" #include "include/xhci_hcd.h" #include "usb/usb_core.h" #include "usb/usb_spec.h" @@ -49,6 +50,10 @@ void xhci_ring_doorbell(XhciHostController *xhci_hcd, uint8_t slot_id, 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 // ============================================================================= @@ -290,6 +295,58 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, const uint64_t base_addr, return -1; } + // Configure MSI interrupt if available + if (xhci_hcd->base.msi_cap_offset != 0) { + // Generate fake bus/device/function for interrupt allocation + // TODO: Get real PCI bus/device/function from system + constexpr uint32_t bus_device_func = 0x010000; // Mock PCI address + + uint64_t msi_address; + uint32_t msi_data; + + const SyscallResultU8 alloc_result = anos_allocate_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; + + // Check what type of capability 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"); @@ -739,13 +796,33 @@ int xhci_wait_for_command_completion(XhciHostController *xhci_hcd, hcd_debugf("xHCI HCD: Waiting for command completion (timeout %u ms)\n", timeout_ms); - // For now, implement a simple polling mechanism - // In a real implementation, this would process the event ring - hcd_vdebugf( - "xHCI HCD: Starting command completion wait, event ring @ %p (phys " - "0x%016lx)\n", - (void *)xhci_hcd->event_ring.trbs, - xhci_hcd->event_ring.trbs_physical); + // 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, @@ -822,6 +899,30 @@ int xhci_wait_for_transfer_completion(XhciHostController *xhci_hcd, 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; @@ -1432,10 +1533,24 @@ int xhci_setup_event_ring(XhciHostController *xhci_hcd) { xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, interrupter_base + 0x08, 1); - // CRITICAL: Disable interrupts and use polling mode - hcd_debugf("xHCI HCD: Disabling interrupts - using polling mode\n"); + // IMOD (Interrupt Moderation) - disable moderation for debugging xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, - interrupter_base + 0x00, 0); // IMAN = 0 (disable interrupts) + 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, @@ -1494,4 +1609,246 @@ int xhci_setup_event_ring(XhciHostController *xhci_hcd) { #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"); + + // CRITICAL: Write USBSTS first as required by xHCI spec for interrupt acknowledgment + 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); // Clear EINT bit + hcd_debugf("xHCI HCD: Cleared USBSTS.EINT (was 0x%08x)\n", usbsts); + } + + // 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); + + // Update ERDP (Event Ring Dequeue Pointer) and acknowledge interrupt + const uint64_t new_erdp = + xhci_hcd->event_ring.trbs_physical + + (xhci_hcd->event_ring.dequeue_index * sizeof(XhciTrb)); + + constexpr uint32_t interrupter_base = 0x20; + + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x18, + (uint32_t)(new_erdp & 0xFFFFFFFF)); + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x1C, (uint32_t)(new_erdp >> 32)); + + // Clear interrupt pending bit in IMAN + const uint32_t iman = + xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base); + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base, iman | (1 << 0)); + + // Flush posted writes with dummy read + (void)xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + 0x20); + + hcd_debugf("xHCI HCD: Updated ERDP to 0x%016lx, cleared interrupt " + "(IMAN was 0x%08x)\n", + new_erdp, iman); + + 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) { + const uint64_t new_erdp = + (xhci_hcd->event_ring.trbs_physical + + (xhci_hcd->event_ring.dequeue_index * sizeof(XhciTrb))) | + (1ULL << 3); // Set EHB (Event Handler Busy) bit + + constexpr uint32_t interrupter_base = 0x20; + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x18, + (uint32_t)(new_erdp & 0xFFFFFFFF)); + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x1C, (uint32_t)(new_erdp >> 32)); + hcd_debugf("xHCI HCD: Updated ERDP to 0x%016lx\n", new_erdp); + } + + // Clear interrupt pending bit while preserving interrupt enable +#ifdef DEBUG_XHCI_HCD + const uint32_t iman = +#endif + xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20); + + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20, + (1 << 1) | (1 << 0)); // IE=1, IP=1 (clear) + + // Flush posted writes with dummy read + (void)xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20); + + hcd_debugf("xHCI HCD: No command completion event found, but cleared " + "interrupt (IMAN was 0x%08x)\n", + iman); + + 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"); + + // CRITICAL: Write USBSTS first as required by xHCI spec for interrupt acknowledgment + 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); // Clear EINT bit + hcd_debugf("xHCI HCD: Cleared USBSTS.EINT (was 0x%08x)\n", usbsts); + } + + // 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); + + // Update ERDP and acknowledge interrupt + const uint64_t new_erdp = + (xhci_hcd->event_ring.trbs_physical + + (xhci_hcd->event_ring.dequeue_index * sizeof(XhciTrb))) | + (1ULL << 3); // Set EHB (Event Handler Busy) bit + constexpr uint32_t interrupter_base = 0x20; + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x18, + (uint32_t)(new_erdp & 0xFFFFFFFF)); + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x1C, (uint32_t)(new_erdp >> 32)); + + // Clear interrupt pending bit while preserving interrupt enable +#ifdef DEBUG_XHCI_HCD + const uint32_t iman = +#endif + xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + 0x20); + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20, + (1 << 1) | (1 << 0)); // IE=1, IP=1 (clear) + + // Flush posted writes with dummy read + (void)xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + 0x20); + + hcd_debugf("xHCI HCD: Acknowledged interrupt (ERDP=0x%016lx, " + "IMAN=0x%08x)\n", + new_erdp, iman); + + 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) { + const uint64_t new_erdp = + (xhci_hcd->event_ring.trbs_physical + + (xhci_hcd->event_ring.dequeue_index * sizeof(XhciTrb))) | + (1ULL << 3); // Set EHB (Event Handler Busy) bit + + constexpr uint32_t interrupter_base = 0x20; + + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x18, + (uint32_t)(new_erdp & 0xFFFFFFFF)); + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, + interrupter_base + 0x1C, (uint32_t)(new_erdp >> 32)); + + hcd_debugf("xHCI HCD: Updated ERDP to 0x%016lx\n", new_erdp); + } + + // Clear interrupt pending bit while preserving interrupt enable +#ifdef DEBUG_XHCI_HCD + const uint32_t iman = +#endif + xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20); + + xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20, + (1 << 1) | (1 << 0)); // IE=1, IP=1 (clear) + + // Flush posted writes with dummy read + (void)xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20); + + hcd_debugf("xHCI HCD: No transfer completion event found, but cleared " + "interrupt (IMAN was 0x%08x)\n", + iman); + + return -1; } \ No newline at end of file From 1394c161671f93c3b4461db2389677998198a9ae Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Wed, 10 Sep 2025 06:54:11 +0100 Subject: [PATCH 17/21] More tidy up, stop faking PCI data in xHCI --- servers/xhcidrv/include/xhci.h | 3 +++ servers/xhcidrv/xhci.c | 1 - servers/xhcidrv/xhci_hcd.c | 45 ++++++++++++++++++++++------------ 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/servers/xhcidrv/include/xhci.h b/servers/xhcidrv/include/xhci.h index 1b2bf7a6..f7611d10 100644 --- a/servers/xhcidrv/include/xhci.h +++ b/servers/xhcidrv/include/xhci.h @@ -11,6 +11,9 @@ #include #include +// PCI Configuration +#define PCI_CONFIG_BASE_ADDRESS 0xC000000000ULL + // xHCI Register Offsets #define XHCI_CAP_CAPLENGTH 0x00 #define XHCI_CAP_HCIVERSION 0x02 diff --git a/servers/xhcidrv/xhci.c b/servers/xhcidrv/xhci.c index a7db1c59..f9afd20e 100644 --- a/servers/xhcidrv/xhci.c +++ b/servers/xhcidrv/xhci.c @@ -39,7 +39,6 @@ #define init_vdebugf(...) #endif -#define PCI_CONFIG_BASE_ADDRESS 0xC000000000ULL #define XHCI_VIRTUAL_BASE 0x400000000 uint32_t xhci_read32(const XHCIController *controller, volatile void *base, diff --git a/servers/xhcidrv/xhci_hcd.c b/servers/xhcidrv/xhci_hcd.c index d00bf678..1330bdf8 100644 --- a/servers/xhcidrv/xhci_hcd.c +++ b/servers/xhcidrv/xhci_hcd.c @@ -15,6 +15,7 @@ #include #include "include/pci.h" +#include "include/xhci.h" #include "include/xhci_hcd.h" #include "usb/usb_core.h" #include "usb/usb_spec.h" @@ -295,11 +296,14 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, const uint64_t base_addr, return -1; } - // Configure MSI interrupt if available + // Configure MSI interrupt (if available) if (xhci_hcd->base.msi_cap_offset != 0) { - // Generate fake bus/device/function for interrupt allocation - // TODO: Get real PCI bus/device/function from system - constexpr uint32_t bus_device_func = 0x010000; // Mock PCI address + // 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; @@ -310,7 +314,7 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, const uint64_t base_addr, if (alloc_result.result == SYSCALL_OK && alloc_result.value != 0) { const uint8_t vector = alloc_result.value; - // Check what type of capability we have + // 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); @@ -435,6 +439,7 @@ int xhci_hcd_set_address(UsbHostController *hcd, UsbDevice *device, 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); @@ -649,7 +654,7 @@ int xhci_hcd_disable_port(UsbHostController *hcd, uint8_t port) { XhciHostController *xhci_hcd_from_usb_hcd(UsbHostController *hcd) { if (!hcd || !hcd->private_data) { - return NULL; + return nullptr; } return (XhciHostController *)hcd->private_data; } @@ -1049,10 +1054,11 @@ int xhci_setup_endpoint_ring(XhciHostController *xhci_hcd, uint8_t slot_id, } 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] = NULL; + xhci_hcd->endpoint_rings[slot_id][endpoint_id] = nullptr; return -1; } @@ -1118,24 +1124,27 @@ int xhci_setup_device_context(XhciHostController *xhci_hcd, uint8_t slot_id, ep0_context[0] = (4 << 3); // EP Type = Control (4), State = Disabled (0) // EP0 Context DW1: Max Packet Size - uint16_t max_packet_size = - 64; // Default, should be updated after getting device descriptor + uint16_t max_packet_size = 64; + switch (device->speed) { case 1: max_packet_size = 8; break; // Low speed case 2: - max_packet_size = 64; - break; // Full speed case 3: max_packet_size = 64; - break; // High speed + 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; } - ep0_context[1] = (max_packet_size << 16) | - (3 << 1); // Max packet size and Error Count = 3 + + // 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) { @@ -1144,7 +1153,8 @@ int xhci_setup_device_context(XhciHostController *xhci_hcd, uint8_t slot_id, } // Set Transfer Ring Base Address (TRBA) in endpoint context - DW2/DW3 - XhciRing *ep0_ring = xhci_hcd->endpoint_rings[slot_id][0]; + 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); @@ -1158,7 +1168,7 @@ int xhci_setup_device_context(XhciHostController *xhci_hcd, uint8_t slot_id, } // Map device context - SyscallResult device_ctx_map = anos_map_physical( + 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 | @@ -1304,6 +1314,9 @@ int xhci_submit_interrupt_transfer(XhciHostController *xhci_hcd, 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; } From 7beeabf8a58696149096ba60fed51381661d2099 Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Wed, 10 Sep 2025 07:15:31 +0100 Subject: [PATCH 18/21] Clean up repetition in xHCI interrupt handling --- servers/xhcidrv/xhci_hcd.c | 184 ++++++++++++------------------------- 1 file changed, 57 insertions(+), 127 deletions(-) diff --git a/servers/xhcidrv/xhci_hcd.c b/servers/xhcidrv/xhci_hcd.c index 1330bdf8..b841c6b6 100644 --- a/servers/xhcidrv/xhci_hcd.c +++ b/servers/xhcidrv/xhci_hcd.c @@ -172,6 +172,51 @@ static int xhci_init_usb_hcd(XhciHostController *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) { @@ -1636,14 +1681,7 @@ int xhci_process_command_completion_event(XhciHostController *xhci_hcd) { hcd_debugf( "xHCI HCD: Processing command completion event from interrupt\n"); - // CRITICAL: Write USBSTS first as required by xHCI spec for interrupt acknowledgment - 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); // Clear EINT bit - hcd_debugf("xHCI HCD: Cleared USBSTS.EINT (was 0x%08x)\n", usbsts); - } + 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; @@ -1673,34 +1711,8 @@ int xhci_process_command_completion_event(XhciHostController *xhci_hcd) { xhci_completion_code_string(completion_code)); xhci_ring_inc_dequeue(&xhci_hcd->event_ring); - - // Update ERDP (Event Ring Dequeue Pointer) and acknowledge interrupt - const uint64_t new_erdp = - xhci_hcd->event_ring.trbs_physical + - (xhci_hcd->event_ring.dequeue_index * sizeof(XhciTrb)); - - constexpr uint32_t interrupter_base = 0x20; - - xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, - interrupter_base + 0x18, - (uint32_t)(new_erdp & 0xFFFFFFFF)); - xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, - interrupter_base + 0x1C, (uint32_t)(new_erdp >> 32)); - - // Clear interrupt pending bit in IMAN - const uint32_t iman = - xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, - interrupter_base); - xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, - interrupter_base, iman | (1 << 0)); - - // Flush posted writes with dummy read - (void)xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, - 0x20); - - hcd_debugf("xHCI HCD: Updated ERDP to 0x%016lx, cleared interrupt " - "(IMAN was 0x%08x)\n", - new_erdp, iman); + xhci_update_erdp(xhci_hcd, false); + xhci_clear_iman_interrupt(xhci_hcd); return (completion_code == XHCI_COMP_SUCCESS) ? 0 : -1; } @@ -1712,35 +1724,12 @@ int xhci_process_command_completion_event(XhciHostController *xhci_hcd) { // 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) { - const uint64_t new_erdp = - (xhci_hcd->event_ring.trbs_physical + - (xhci_hcd->event_ring.dequeue_index * sizeof(XhciTrb))) | - (1ULL << 3); // Set EHB (Event Handler Busy) bit - - constexpr uint32_t interrupter_base = 0x20; - xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, - interrupter_base + 0x18, - (uint32_t)(new_erdp & 0xFFFFFFFF)); - xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, - interrupter_base + 0x1C, (uint32_t)(new_erdp >> 32)); - hcd_debugf("xHCI HCD: Updated ERDP to 0x%016lx\n", new_erdp); + xhci_update_erdp(xhci_hcd, true); } - // Clear interrupt pending bit while preserving interrupt enable -#ifdef DEBUG_XHCI_HCD - const uint32_t iman = -#endif - xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20); - - xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20, - (1 << 1) | (1 << 0)); // IE=1, IP=1 (clear) - - // Flush posted writes with dummy read - (void)xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20); - + xhci_clear_iman_interrupt(xhci_hcd); hcd_debugf("xHCI HCD: No command completion event found, but cleared " - "interrupt (IMAN was 0x%08x)\n", - iman); + "interrupt\n"); return -1; } @@ -1753,14 +1742,7 @@ int xhci_process_transfer_completion_event(XhciHostController *xhci_hcd) { hcd_debugf( "xHCI HCD: Processing transfer completion event from interrupt\n"); - // CRITICAL: Write USBSTS first as required by xHCI spec for interrupt acknowledgment - 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); // Clear EINT bit - hcd_debugf("xHCI HCD: Cleared USBSTS.EINT (was 0x%08x)\n", usbsts); - } + 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; @@ -1792,35 +1774,8 @@ int xhci_process_transfer_completion_event(XhciHostController *xhci_hcd) { xhci_completion_code_string(completion_code)); xhci_ring_inc_dequeue(&xhci_hcd->event_ring); - - // Update ERDP and acknowledge interrupt - const uint64_t new_erdp = - (xhci_hcd->event_ring.trbs_physical + - (xhci_hcd->event_ring.dequeue_index * sizeof(XhciTrb))) | - (1ULL << 3); // Set EHB (Event Handler Busy) bit - constexpr uint32_t interrupter_base = 0x20; - xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, - interrupter_base + 0x18, - (uint32_t)(new_erdp & 0xFFFFFFFF)); - xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, - interrupter_base + 0x1C, (uint32_t)(new_erdp >> 32)); - - // Clear interrupt pending bit while preserving interrupt enable -#ifdef DEBUG_XHCI_HCD - const uint32_t iman = -#endif - xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, - 0x20); - xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20, - (1 << 1) | (1 << 0)); // IE=1, IP=1 (clear) - - // Flush posted writes with dummy read - (void)xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, - 0x20); - - hcd_debugf("xHCI HCD: Acknowledged interrupt (ERDP=0x%016lx, " - "IMAN=0x%08x)\n", - new_erdp, iman); + xhci_update_erdp(xhci_hcd, true); + xhci_clear_iman_interrupt(xhci_hcd); return (completion_code == XHCI_COMP_SUCCESS) ? 0 : -1; } @@ -1831,37 +1786,12 @@ int xhci_process_transfer_completion_event(XhciHostController *xhci_hcd) { // Update ERDP if dequeue pointer changed if (xhci_hcd->event_ring.dequeue_index != initial_dequeue) { - const uint64_t new_erdp = - (xhci_hcd->event_ring.trbs_physical + - (xhci_hcd->event_ring.dequeue_index * sizeof(XhciTrb))) | - (1ULL << 3); // Set EHB (Event Handler Busy) bit - - constexpr uint32_t interrupter_base = 0x20; - - xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, - interrupter_base + 0x18, - (uint32_t)(new_erdp & 0xFFFFFFFF)); - xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, - interrupter_base + 0x1C, (uint32_t)(new_erdp >> 32)); - - hcd_debugf("xHCI HCD: Updated ERDP to 0x%016lx\n", new_erdp); + xhci_update_erdp(xhci_hcd, true); } - // Clear interrupt pending bit while preserving interrupt enable -#ifdef DEBUG_XHCI_HCD - const uint32_t iman = -#endif - xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20); - - xhci_write32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20, - (1 << 1) | (1 << 0)); // IE=1, IP=1 (clear) - - // Flush posted writes with dummy read - (void)xhci_read32(&xhci_hcd->base, xhci_hcd->base.runtime_regs, 0x20); - + xhci_clear_iman_interrupt(xhci_hcd); hcd_debugf("xHCI HCD: No transfer completion event found, but cleared " - "interrupt (IMAN was 0x%08x)\n", - iman); + "interrupt\n"); return -1; } \ No newline at end of file From 560604f2766d8c8f6043a06ebf691f6d2a54a726 Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Thu, 18 Sep 2025 20:38:39 +0100 Subject: [PATCH 19/21] Minor code tidy-up in xHCI rings --- servers/xhcidrv/xhci_rings.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/servers/xhcidrv/xhci_rings.c b/servers/xhcidrv/xhci_rings.c index 89e82bf8..11e2431d 100644 --- a/servers/xhcidrv/xhci_rings.c +++ b/servers/xhcidrv/xhci_rings.c @@ -24,7 +24,7 @@ // Ring Management // ============================================================================= -int xhci_ring_init(XhciRing *ring, uint32_t size) { +int xhci_ring_init(XhciRing *ring, const uint32_t size) { if (!ring || size == 0 || size > 4096) { return -1; } @@ -37,12 +37,14 @@ int xhci_ring_init(XhciRing *ring, uint32_t size) { 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; } @@ -98,7 +100,8 @@ XhciTrb *xhci_ring_enqueue_trb(XhciRing *ring) { } // Check if we have space (leave one slot for link TRB if needed) - uint32_t next_enqueue = (ring->enqueue_index + 1) % ring->size; + 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; @@ -141,8 +144,9 @@ volatile XhciTrb *xhci_ring_dequeue_trb(XhciRing *ring) { } bool xhci_ring_has_space(XhciRing *ring, uint32_t num_trbs) { - if (!ring) + if (!ring) { return false; + } uint32_t available_slots; if (ring->enqueue_index >= ring->dequeue_index) { @@ -172,8 +176,9 @@ void xhci_ring_inc_enqueue(XhciRing *ring) { } void xhci_ring_inc_dequeue(XhciRing *ring) { - if (!ring) + if (!ring) { return; + } ring->dequeue_index = (ring->dequeue_index + 1) % ring->size; ring->dequeued_count++; From e67ca13ca4202953020d39597d31651159a78e48 Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Sat, 20 Sep 2025 14:31:12 +0100 Subject: [PATCH 20/21] Fix ups after rebase onto kterminal --- servers/xhcidrv/xhci_hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers/xhcidrv/xhci_hcd.c b/servers/xhcidrv/xhci_hcd.c index b841c6b6..f3ed0955 100644 --- a/servers/xhcidrv/xhci_hcd.c +++ b/servers/xhcidrv/xhci_hcd.c @@ -353,7 +353,7 @@ int xhci_hcd_init(XhciHostController *xhci_hcd, const uint64_t base_addr, uint64_t msi_address; uint32_t msi_data; - const SyscallResultU8 alloc_result = anos_allocate_interrupt_vector( + 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) { From 53f5db2e13a8266e29714c43f24a910d3b67e808 Mon Sep 17 00:00:00 2001 From: Ross Bamford Date: Mon, 13 Oct 2025 22:36:04 +0100 Subject: [PATCH 21/21] Fix typo in dependencies --- servers/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servers/Makefile b/servers/Makefile index 83ab5f82..e25994b2 100644 --- a/servers/Makefile +++ b/servers/Makefile @@ -7,7 +7,7 @@ MKRAMFS_BIN?=$(MKRAMFS_DIR)/$(MKRAMFS) .PHONY: all clean -all: test_server/test_server.elf devman/devman.elf pcidrv/pcidrv.elf ahcidrv/ahcidrv.elf xhcidrv/xhcidrv.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