From 0cb2169c290a01c0ff23f462f34b7a9e92b37562 Mon Sep 17 00:00:00 2001 From: Freywar Ulvnaudgari Date: Fri, 14 Aug 2026 20:51:07 +0300 Subject: [PATCH] Add basic HID keyboard support --- meson.build | 3 +- src/kernel/hid-keyboard.c | 200 ++++++++++++ src/kernel/hid-keyboard.h | 7 + src/kernel/kernel.c | 4 +- src/kernel/nvme.c | 2 +- src/kernel/panic.c | 2 +- src/kernel/pci.c | 9 +- src/kernel/{keyboard.c => ps2-keyboard.c} | 8 +- src/kernel/{keyboard.h => ps2-keyboard.h} | 2 +- src/kernel/usb.c | 240 +++++++++++++- src/kernel/usb.h | 13 + src/kernel/xhci.c | 368 ++++++++++++++++------ src/kernel/xhci.h | 12 +- src/lib/util.h | 5 + 14 files changed, 747 insertions(+), 128 deletions(-) create mode 100644 src/kernel/hid-keyboard.c create mode 100644 src/kernel/hid-keyboard.h rename src/kernel/{keyboard.c => ps2-keyboard.c} (96%) rename src/kernel/{keyboard.h => ps2-keyboard.h} (68%) diff --git a/meson.build b/meson.build index 502b62d..5a19d2c 100644 --- a/meson.build +++ b/meson.build @@ -71,9 +71,9 @@ kernel_sources = files([ 'src/kernel/fat16.c', 'src/kernel/fs.c', 'src/kernel/gdt.c', + 'src/kernel/hid-keyboard.c', 'src/kernel/idt.c', 'src/kernel/kernel.c', - 'src/kernel/keyboard.c', 'src/kernel/log.c', 'src/kernel/memory.c', 'src/kernel/nvme.c', @@ -83,6 +83,7 @@ kernel_sources = files([ 'src/kernel/pic.c', 'src/kernel/pipe.c', 'src/kernel/process.c', + 'src/kernel/ps2-keyboard.c', 'src/kernel/syscall.c', 'src/kernel/timer.c', 'src/kernel/tss.c', diff --git a/src/kernel/hid-keyboard.c b/src/kernel/hid-keyboard.c new file mode 100644 index 0000000..db87654 --- /dev/null +++ b/src/kernel/hid-keyboard.c @@ -0,0 +1,200 @@ +#include "src/kernel/log.h" +#include "src/kernel/panic.h" +#include "src/kernel/process.h" +#include "src/kernel/stream.h" +#include "src/kernel/usb.h" +#include "src/kernel/xhci.h" +#include "src/lib/memory.h" + +#define USB_INTERFACE_HID_KEYBOARD_CLASS 0x3 +#define USB_INTERFACE_HID_KEYBOARD_SUBCLASS 0x1 +#define USB_INTERFACE_HID_KEYBOARD_PROTOCOL 0x1 + +#define USB_HID_BOOT_PACKET_SIZE 8 + +#define HID_MOD_LCTRL 0b00000001 +#define HID_MOD_LSHIFT 0b00000010 +#define HID_MOD_LALT 0b00000100 +#define HID_MOD_RCTRL 0b00010000 +#define HID_MOD_RSHIFT 0b00100000 +#define HID_MOD_RALT 0b01000000 + +#define HID_KEY_ENTER 0x28 +#define HID_KEY_BACKSPACE 0x2A +#define HID_KEY_TAB 0x2B +#define HID_KEY_HOME 0x4A +#define HID_KEY_END 0x4D +#define HID_KEY_DELETE 0x4C +#define HID_KEY_RIGHT 0x4F +#define HID_KEY_LEFT 0x50 +#define HID_KEY_DOWN 0x51 +#define HID_KEY_UP 0x52 + +// HID keycode to ASCII, index 0 = keycode 4 ('a') +static const char hid_normal[128] = { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', + 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', + '\n', 0, '\b', '\t', ' ', '-', '=', '[', ']', '\\', 0, ';', '\'', '`', ',', '.', '/', +}; + +static const char hid_shifted[128] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', + '\n', 0, '\b', '\t', ' ', '_', '+', '{', '}', '|', 0, ':', '"', '~', '<', '>', '?', +}; + +static xhci_port_t keyboard_port; +static xhci_slot_t keyboard_slot; +static xhci_endpoint_t keyboard_endpoint; + +typedef uint64_t hid_report; + +static hid_report prev; + +#define BUFFER_SIZE 256 + +static char buffer[BUFFER_SIZE]; +static uint16_t buffer_offset = 0; +static uint16_t buffer_length = 0; + +static void append(char c) { + buffer[(buffer_offset + buffer_length) % BUFFER_SIZE] = c; + if (buffer_length < BUFFER_SIZE) { + buffer_length++; + } +} + +static void append_sequence(const char *s) { + while (*s) { + append(*s); + s++; + } +} + +static void on_key_pressed(uint8_t code, uint8_t modifiers) { + code -= 4; + if (code >= 128) { + return; + } + + switch (code + 4) { + case HID_KEY_ENTER: + append('\n'); + return; + case HID_KEY_BACKSPACE: + append('\b'); + return; + case HID_KEY_TAB: + append('\t'); + return; + case HID_KEY_HOME: + append_sequence(STREAM_SEQ_HOME); + return; + case HID_KEY_END: + append_sequence(STREAM_SEQ_END); + return; + case HID_KEY_DELETE: + append_sequence(STREAM_SEQ_DELETE); + return; + case HID_KEY_RIGHT: + append_sequence(STREAM_SEQ_RIGHT); + return; + case HID_KEY_LEFT: + append_sequence(STREAM_SEQ_LEFT); + return; + case HID_KEY_DOWN: + append_sequence(STREAM_SEQ_DOWN); + return; + case HID_KEY_UP: + append_sequence(STREAM_SEQ_UP); + return; + } + + if (modifiers & (HID_MOD_LCTRL | HID_MOD_RCTRL)) { + if ((hid_normal[code] >= '0' && hid_normal[code] <= '9') || (hid_normal[code] >= 'a' && hid_normal[code] <= 'z')) { + append(hid_normal[code] - 'a' + 1); + } + } else if (modifiers & (HID_MOD_LALT | HID_MOD_RALT)) { + // TODO Alt combinations. + } else { + append(((modifiers & (HID_MOD_LSHIFT | HID_MOD_RSHIFT)) ? hid_shifted : hid_normal)[code]); + } +} + +static uint64_t stream_write(__attribute__((unused)) stream_t *self, __attribute__((unused)) const char *from, + __attribute__((unused)) uint64_t bytes) { + return 0; +} + +static uint64_t stream_read(__attribute__((unused)) stream_t *self, uint64_t max, char *to) { + while (!buffer_length) { + hid_report next; + while (xhci_read_endpoint(keyboard_slot, keyboard_endpoint, sizeof(next), &next)) { + uint8_t *prev_bytes = (uint8_t *)&prev; + uint8_t *next_bytes = (uint8_t *)&next; + if (next == prev) { // Assuming new report without changes means automatic repetition. + for (uint8_t i = 2; i < 8; i++) { + if (next_bytes[i]) { + on_key_pressed(next_bytes[i], next_bytes[0]); + } + } + } else { + for (uint8_t i = 2; i < 8; i++) { + uint8_t found = 0; + for (uint8_t j = 2; j < 8; j++) { + if (prev_bytes[j] == next_bytes[i]) { + found = 1; + break; + } + } + if (next_bytes[i] && !found) { + on_key_pressed(next_bytes[i], next_bytes[0]); + } + } + } + prev = next; + } + + __asm__ volatile("sti"); + process_next(); + __asm__ volatile("cli"); + } + + uint64_t size = buffer_length > max ? max : buffer_length; + if (buffer_offset + size <= BUFFER_SIZE) { + memory_copy(buffer + buffer_offset, size, to); + } else { + uint64_t chunk_0 = BUFFER_SIZE - buffer_offset; + memory_copy(buffer + buffer_offset, chunk_0, to); + memory_copy(buffer, size - chunk_0, to + chunk_0); + } + buffer_length -= size; + buffer_offset = (buffer_offset + size) % BUFFER_SIZE; + return size; +} + +static uint64_t stream_truncate(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t size) { + return size; +} + +static void stream_close(__attribute__((unused)) stream_t *self) { +} + +static stream_t stream = {stream_write, stream_read, stream_truncate, stream_close}; + +stream_t *hid_keyboard_init() { + LOG_LN_INFO("Searching for suitable device..."); + keyboard_port = + usb_find_by_interface(USB_INTERFACE_HID_KEYBOARD_CLASS, USB_INTERFACE_HID_KEYBOARD_SUBCLASS, USB_INTERFACE_HID_KEYBOARD_PROTOCOL); + ASSERT(keyboard_port != (xhci_port_t)-1, "hid_keyboard_init: No suitable device found."); + + keyboard_slot = usb_attach((uint8_t)keyboard_port); + ASSERT(keyboard_slot != (xhci_slot_t)-1, "hid_keyboard_init: Could not attach the device."); + + keyboard_endpoint = usb_open_endpoint(keyboard_slot, 7, USB_HID_BOOT_PACKET_SIZE); + ASSERT(keyboard_endpoint != (xhci_endpoint_t)-1, "hid_keyboard_init: Could not open endpoint."); + + LOG_LN_INFO("Done."); + + return &stream; +} diff --git a/src/kernel/hid-keyboard.h b/src/kernel/hid-keyboard.h new file mode 100644 index 0000000..d5c3868 --- /dev/null +++ b/src/kernel/hid-keyboard.h @@ -0,0 +1,7 @@ +#pragma once + +#include "src/kernel/stream.h" +#include + +stream_t *hid_keyboard_init(); // Assuming one and only one HID device. + diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 09535b2..913738b 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -1,7 +1,7 @@ #include "src/kernel/fs.h" #include "src/kernel/gdt.h" +#include "src/kernel/hid-keyboard.h" #include "src/kernel/idt.h" -#include "src/kernel/keyboard.h" #include "src/kernel/log.h" #include "src/kernel/nvme.h" #include "src/kernel/panic.h" @@ -87,7 +87,7 @@ void kernel_main() { kernel->kernel_stack = kernel->kernel_rsp = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE; kernel->user_stack = kernel->user_rsp = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE; kernel->cwd = memory_allocate(sizeof(path_t)); - kernel->fds[STDIN] = keyboard_init(); + kernel->fds[STDIN] = hid_keyboard_init(); kernel->fds[STDOUT] = kernel->fds[STDERR] = vga; kernel->free_fd = STDERR + 1; kernel->code = EXIT_CODE_OK; diff --git a/src/kernel/nvme.c b/src/kernel/nvme.c index f518d22..0e3d1a7 100644 --- a/src/kernel/nvme.c +++ b/src/kernel/nvme.c @@ -167,7 +167,7 @@ static void io_exec_sync(nvme_sqe_t *cmd) { void nvme_init() { LOG_LN_INFO("Searching for suitable device..."); pci_bdf_t bdf = pci_find_by_class(NVME_PCI_CLASS, NVME_PCI_SUBCLASS, NUL); - ASSERT(bdf, "nvme_init: No suitable devices found."); + ASSERT(bdf != (pci_bdf_t)-1, "nvme_init: No suitable devices found."); LOG_LN_INFO("Mapping device to virtual memory..."); pci_map(bdf, regs, 4); diff --git a/src/kernel/panic.c b/src/kernel/panic.c index f7adc8c..7ad4168 100644 --- a/src/kernel/panic.c +++ b/src/kernel/panic.c @@ -2,7 +2,7 @@ #include "src/kernel/vga.h" void kernel_panic(const char *msg, __attribute__((unused)) const char *file, __attribute__((unused)) int line) { - vga_set_string(0, VGA_HEIGHT - 1, msg, 0x28); + vga_set_string(VGA_HEIGHT - 1, 0, msg, 0x28); while (1) ; } diff --git a/src/kernel/pci.c b/src/kernel/pci.c index e78d302..ecf4611 100644 --- a/src/kernel/pci.c +++ b/src/kernel/pci.c @@ -1,6 +1,7 @@ #include "src/kernel/pci.h" #include "src/kernel/log.h" #include "src/kernel/memory.h" +#include "src/kernel/panic.h" #include "src/kernel/util.h" #include "src/lib/layout.h" #include "src/lib/util.h" @@ -70,7 +71,7 @@ void pci_enumerate(stream_t *out) { pci_bdf_t pci_find_by_class(uint8_t class, uint8_t subclass, uint8_t iface) { for (uint16_t b = 0; b < PCI_MAX_BUSES; b++) { for (uint8_t d = 0; d < PCI_MAX_DEVICES; d++) { - for (uint8_t f = 0; PCI_MAX_FUNCTIONS; f++) { + for (uint8_t f = 0; f < PCI_MAX_FUNCTIONS; f++) { uint32_t id = pci_read(PCI_BDF(b, d, f), PCI_OFFSET_DEVICE_VENDOR); if (PCI_DEVICE(id) == PCI_DEVICE_EMPTY) { continue; @@ -85,10 +86,12 @@ pci_bdf_t pci_find_by_class(uint8_t class, uint8_t subclass, uint8_t iface) { } } } - return NUL; + return (pci_bdf_t)-1; } void pci_map(pci_bdf_t bdf, volatile void *virt, uint8_t pages) { + ASSERT(bdf != (pci_bdf_t)-1, "pci_map: Invalid BDF."); + pci_write(bdf, PCI_OFFSET_STATUS_COMMAND, pci_read(bdf, PCI_OFFSET_STATUS_COMMAND) | PCI_COMMAND_MEMORY_SPACE | PCI_COMMAND_BUS_MASTER); uint64_t bar = ((uint64_t)pci_read(bdf, PCI_OFFSET_BAR_1) << 32) | (pci_read(bdf, PCI_OFFSET_BAR_0) & 0xFFFFFFF0); @@ -101,6 +104,8 @@ void pci_map(pci_bdf_t bdf, volatile void *virt, uint8_t pages) { } void pci_unmap(pci_bdf_t bdf, volatile void *virt, uint8_t pages) { + ASSERT(bdf != (pci_bdf_t)-1, "pci_unmap: Invalid BDF."); + LOG_LN_STEP("Unmapping BAR from memory, virt %lx...", virt); for (uint8_t i = 0; i < pages; i++) { memory_page_unmap((void *)KERNEL_VIRTUAL_PML4, (void *)((uint8_t *)virt + i * PAGE_SIZE)); diff --git a/src/kernel/keyboard.c b/src/kernel/ps2-keyboard.c similarity index 96% rename from src/kernel/keyboard.c rename to src/kernel/ps2-keyboard.c index 6d6f1ac..61f9d0b 100644 --- a/src/kernel/keyboard.c +++ b/src/kernel/ps2-keyboard.c @@ -1,4 +1,4 @@ -#include "src/kernel/keyboard.h" +#include "src/kernel/ps2-keyboard.h" #include "src/kernel/idt.h" #include "src/kernel/process.h" #include "src/kernel/stream.h" @@ -158,15 +158,15 @@ static void on_key(uint8_t scancode) { } } -__attribute__((interrupt)) static void isr_keyboard(__attribute__((unused)) struct interrupt_frame *frame) { +__attribute__((interrupt)) static void isr_ps2_keyboard(__attribute__((unused)) struct interrupt_frame *frame) { uint8_t scancode = inb(0x60); outb(0x20, 0x20); on_key(scancode); } -stream_t *keyboard_init() { +stream_t *ps2_keyboard_init() { outb(0x21, inb(0x21) & ~0x02); - idt_set_entry(33, isr_keyboard, 0x8E); + idt_set_entry(33, isr_ps2_keyboard, 0x8E); return &stream; } diff --git a/src/kernel/keyboard.h b/src/kernel/ps2-keyboard.h similarity index 68% rename from src/kernel/keyboard.h rename to src/kernel/ps2-keyboard.h index 3c47fd5..0fac2cc 100644 --- a/src/kernel/keyboard.h +++ b/src/kernel/ps2-keyboard.h @@ -3,4 +3,4 @@ #include "src/kernel/stream.h" #include -stream_t *keyboard_init(); +stream_t *ps2_keyboard_init(); diff --git a/src/kernel/usb.c b/src/kernel/usb.c index 3a7ad6b..b410444 100644 --- a/src/kernel/usb.c +++ b/src/kernel/usb.c @@ -1,16 +1,70 @@ #include "src/kernel/usb.h" #include "src/kernel/log.h" +#include "src/kernel/panic.h" #include "src/kernel/stream.h" #include "src/kernel/xhci.h" +#include "src/lib/layout.h" +#include "src/lib/memory.h" +#include "src/lib/util.h" + +typedef struct __attribute__((packed)) { + uint8_t bm_request_type; + uint8_t b_request; + uint16_t w_value; + uint16_t w_index; + uint16_t w_length; +} usb_setup_t; + +#define USB_SETUP_DIRECTION_R(s) BITS_R((s)->bm_request_type, 7, 7) +#define USB_SETUP_DIRECTION_W(s, v) BITS_W((s)->bm_request_type, 7, 7, v) +#define USB_SETUP_TYPE_R(s) BITS_R((s)->bm_request_type, 6, 5) +#define USB_SETUP_TYPE_W(s, v) BITS_W((s)->bm_request_type, 6, 5, v) +#define USB_SETUP_RECIPIENT_R(s) BITS_R((s)->bm_request_type, 4, 0) +#define USB_SETUP_RECIPIENT_W(s, v) BITS_W((s)->bm_request_type, 4, 0, v) + +#define USB_SETUP_DIRECTION_H2D 0 +#define USB_SETUP_DIRECTION_D2H 1 + +#define USB_SETUP_TYPE_STANDARD 0 +#define USB_SETUP_TYPE_CLASS 1 +#define USB_SETUP_TYPE_VENDOR 2 + +#define USB_SETUP_RECIPIENT_DEVICE 0 +#define USB_SETUP_RECIPIENT_INTERFACE 1 +#define USB_SETUP_RECIPIENT_ENDPOINT 2 +#define USB_SETUP_RECIPIENT_OTHER 3 + +#define USB_SETUP_GET_DESCRIPTOR 0x06 +#define USB_SETUP_SET_PROTOCOL 0x0B + +#define USB_SETUP_DESCRIPTOR_TYPE_R(s) BITS_R((s)->w_value, 15, 8) +#define USB_SETUP_DESCRIPTOR_TYPE_W(s, v) BITS_W((s)->w_value, 15, 8, v) + +#define USB_SETUP_DESCRIPTOR_INDEX_R(s) BITS_R((s)->w_value, 7, 0) +#define USB_SETUP_DESCRIPTOR_INDEX_W(s, v) BITS_W((s)->w_value, 7, 0, v) typedef struct __attribute__((packed)) { uint8_t length; - uint8_t descriptor_type; // 0x01 for device descriptor - uint16_t usb_version; // BCD, e.g. 0x0200 for USB 2.0 + uint8_t descriptor_type; +} usb_descriptor_base_t; + +#define USB_DESCRIPTOR_TYPE_DEVICE 0x01 +#define USB_DESCRIPTOR_TYPE_CONFIGURATION 0x02 +#define USB_DESCRIPTOR_TYPE_INTERFACE 0x04 +#define USB_DESCRIPTOR_TYPE_ENDPOINT 0x05 +#define USB_DESCRIPTOR_TYPE_HID 0x21 + +#define USB_INTERFACE_HID_KEYBOARD_CLASS 0x3 +#define USB_INTERFACE_HID_KEYBOARD_SUBCLASS 0x1 +#define USB_INTERFACE_HID_KEYBOARD_PROTOCOL 0x1 + +typedef struct __attribute__((packed)) { + usb_descriptor_base_t base; + uint16_t usb_version; uint8_t device_class; uint8_t device_subclass; uint8_t device_protocol; - uint8_t max_packet_size; // for ep0 + uint8_t max_packet_size; uint16_t vendor_id; uint16_t product_id; uint16_t device_version; @@ -20,7 +74,94 @@ typedef struct __attribute__((packed)) { uint8_t num_configurations; } usb_device_descriptor_t; -static usb_device_descriptor_t device_descriptor __attribute__((aligned(64))); +typedef struct __attribute__((packed)) { + usb_descriptor_base_t base; + uint16_t total_length; + uint8_t num_interfaces; + uint8_t configuration_value; + uint8_t configuration_str; + uint8_t attributes; + uint8_t max_power; +} usb_configuration_descriptor_t; + +typedef struct __attribute__((packed)) { + usb_descriptor_base_t base; + uint8_t interface_number; + uint8_t alternate_setting; + uint8_t num_endpoints; + uint8_t interface_class; + uint8_t interface_subclass; + uint8_t interface_protocol; + uint8_t interface_str; +} usb_interface_descriptor_t; + +typedef struct __attribute__((packed)) { + usb_descriptor_base_t base; + uint8_t endpoint_address; + uint8_t attributes; + uint16_t max_packet_size; + uint8_t interval; +} usb_endpoint_descriptor_t; + +typedef struct __attribute__((packed)) { + usb_descriptor_base_t base; + uint16_t hid_version; + uint8_t country_code; + uint8_t num_descriptors; + uint8_t report_descriptor_type; + uint16_t report_descriptor_length; +} usb_hid_descriptor_t; + +static uint8_t descriptor_buffer[PAGE_SIZE] __attribute__((aligned(64))); + +static void scan_device(xhci_slot_t slot, usb_device_descriptor_t *device, usb_configuration_descriptor_t *configuration, + usb_interface_descriptor_t *interface, usb_endpoint_descriptor_t *endpoint) { + memory_set(0, sizeof(usb_device_descriptor_t), device); + memory_set(0, sizeof(usb_configuration_descriptor_t), configuration); + memory_set(0, sizeof(usb_interface_descriptor_t), interface); + memory_set(0, sizeof(usb_endpoint_descriptor_t), endpoint); + + usb_setup_t setup; + memory_set(0, sizeof(setup), &setup); + USB_SETUP_DIRECTION_W(&setup, USB_SETUP_DIRECTION_D2H); + setup.b_request = USB_SETUP_GET_DESCRIPTOR; + USB_SETUP_DESCRIPTOR_TYPE_W(&setup, USB_DESCRIPTOR_TYPE_DEVICE); + setup.w_length = sizeof(usb_device_descriptor_t); + memory_set(0, sizeof(descriptor_buffer), descriptor_buffer); + xhci_control_transfer(slot, *(uint64_t *)&setup, &descriptor_buffer, setup.w_length); + + memory_copy(descriptor_buffer, sizeof(usb_device_descriptor_t), device); + + memory_set(0, sizeof(setup), &setup); + USB_SETUP_DIRECTION_W(&setup, USB_SETUP_DIRECTION_D2H); + setup.b_request = USB_SETUP_GET_DESCRIPTOR; + USB_SETUP_DESCRIPTOR_TYPE_W(&setup, USB_DESCRIPTOR_TYPE_CONFIGURATION); + setup.w_length = sizeof(usb_configuration_descriptor_t); + memory_set(0, sizeof(descriptor_buffer), descriptor_buffer); + xhci_control_transfer(slot, *(uint64_t *)&setup, &descriptor_buffer, setup.w_length); + + memory_set(0, sizeof(setup), &setup); + USB_SETUP_DIRECTION_W(&setup, USB_SETUP_DIRECTION_D2H); + setup.b_request = USB_SETUP_GET_DESCRIPTOR; + USB_SETUP_DESCRIPTOR_TYPE_W(&setup, USB_DESCRIPTOR_TYPE_CONFIGURATION); + setup.w_length = ((usb_configuration_descriptor_t *)&descriptor_buffer)->total_length; + memory_set(0, sizeof(descriptor_buffer), descriptor_buffer); + xhci_control_transfer(slot, *(uint64_t *)&setup, &descriptor_buffer, setup.w_length); + + usb_descriptor_base_t *descriptor = (usb_descriptor_base_t *)&descriptor_buffer; + while (descriptor->descriptor_type) { // Assuming `descriptor_buffer` longer than any potential descriptor set. + if (descriptor->descriptor_type == USB_DESCRIPTOR_TYPE_CONFIGURATION && !configuration->base.descriptor_type) { + memory_copy(descriptor, sizeof(usb_configuration_descriptor_t), configuration); + } + if (descriptor->descriptor_type == USB_DESCRIPTOR_TYPE_INTERFACE && !interface->base.descriptor_type) { + memory_copy(descriptor, sizeof(usb_interface_descriptor_t), interface); + } + if (descriptor->descriptor_type == USB_DESCRIPTOR_TYPE_ENDPOINT && !endpoint->base.descriptor_type) { + memory_copy(descriptor, sizeof(usb_endpoint_descriptor_t), endpoint); + } + descriptor = (usb_descriptor_base_t *)((uint8_t *)descriptor + descriptor->length); + } +} void usb_init() { PRINT_LN(kernel_log, "Enumerating devices..."); @@ -29,16 +170,95 @@ void usb_init() { } void usb_enumerate(stream_t *out) { - for (uint8_t i = 0; i < xhci_port_count(); i++) { - if (!xhci_port_connected(i)) { + for (xhci_port_t port = 0; port < xhci_port_count(); port++) { + if (!xhci_port_connected(port)) { continue; } - xhci_slot_t slot = xhci_attach(i); + xhci_slot_t slot = xhci_attach(port); - xhci_control_transfer(slot, 0x0012000001000680ULL, &device_descriptor, sizeof(usb_device_descriptor_t)); + if (slot == (xhci_slot_t)-1) { + continue; + } - PRINT_LN(out, "%hx:%hx class=%hx subclass=%hx protocol=%hx", device_descriptor.vendor_id, device_descriptor.product_id, - device_descriptor.device_class, device_descriptor.device_subclass, device_descriptor.device_protocol); + usb_device_descriptor_t device; + usb_configuration_descriptor_t configuration; + usb_interface_descriptor_t interface; + usb_endpoint_descriptor_t endpoint; + scan_device(slot, &device, &configuration, &interface, &endpoint); + + xhci_detach(slot); + + PRINT_LN( + out, + "%hx:%hx usb_version=%hx device_class=%hhx device_subclass=%hhx device_protocol=%hhx interface_class=%hhx interface_subclass=%hhx " + "interface_protocol=%hhx endpoint_address=%hhx max_packet_size=%hx interval=%hhx", + device.vendor_id, device.product_id, device.usb_version, device.device_class, device.device_subclass, device.device_protocol, + interface.interface_class, interface.interface_subclass, interface.interface_protocol, endpoint.endpoint_address, + endpoint.max_packet_size, endpoint.interval); } } + +xhci_port_t usb_find_by_interface(uint8_t class, uint8_t subclass, uint8_t protocol) { + for (xhci_port_t port = 0; port < xhci_port_count(); port++) { + if (!xhci_port_connected(port)) { + continue; + } + + xhci_slot_t slot = xhci_attach(port); + + if (slot == (xhci_slot_t)-1) { + continue; + } + + usb_device_descriptor_t device; + usb_configuration_descriptor_t configuration; + usb_interface_descriptor_t interface; + usb_endpoint_descriptor_t endpoint; + scan_device(slot, &device, &configuration, &interface, &endpoint); + + xhci_detach(slot); + + if ((class == NUL || interface.interface_class == class) && (subclass == NUL || interface.interface_subclass == subclass) && + (protocol == NUL || interface.interface_protocol == protocol)) { + return port; + } + } + return (xhci_port_t)-1; +} + +xhci_slot_t usb_attach(xhci_port_t port) { + return xhci_attach(port); +} + +xhci_endpoint_t usb_open_endpoint(xhci_slot_t slot, uint8_t type, uint16_t max_packet_size) { + usb_device_descriptor_t device; + usb_configuration_descriptor_t configuration; + usb_interface_descriptor_t interface; + usb_endpoint_descriptor_t endpoint; + scan_device(slot, &device, &configuration, &interface, &endpoint); + + usb_setup_t setup; + memory_set(0, sizeof(setup), &setup); + USB_SETUP_DIRECTION_W(&setup, USB_SETUP_DIRECTION_H2D); + USB_SETUP_TYPE_W(&setup, USB_SETUP_TYPE_CLASS); + USB_SETUP_RECIPIENT_W(&setup, USB_SETUP_RECIPIENT_INTERFACE); + setup.b_request = USB_SETUP_SET_PROTOCOL; + xhci_control_transfer(slot, *(uint64_t *)&setup, NUL, 0); + + ASSERT(endpoint.max_packet_size == max_packet_size, "usb_open_endpoint: Unexpected max packet size."); + + return xhci_open_endpoint(slot, endpoint.endpoint_address, type, endpoint.max_packet_size, endpoint.interval); +} + +uint16_t usb_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t max, void *to) { + return xhci_read_endpoint(slot, endpoint, max, to); +} + +void usb_close_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint) { + return xhci_close_endpoint(slot, endpoint); +} + +void usb_detach(xhci_slot_t slot) { + return xhci_detach(slot); +} diff --git a/src/kernel/usb.h b/src/kernel/usb.h index be99494..668e39a 100644 --- a/src/kernel/usb.h +++ b/src/kernel/usb.h @@ -1,8 +1,21 @@ #pragma once #include "src/kernel/stream.h" +#include "src/kernel/xhci.h" #include void usb_init(); void usb_enumerate(stream_t *out); + +xhci_port_t usb_find_by_interface(uint8_t class, uint8_t subclass, uint8_t protocol); + +xhci_slot_t usb_attach(xhci_port_t port); + +xhci_endpoint_t usb_open_endpoint(xhci_slot_t slot, uint8_t type, uint16_t max_packet_size); + +uint16_t usb_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t max, void *to); + +void usb_close_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint); + +void usb_detach(xhci_slot_t slot); diff --git a/src/kernel/xhci.c b/src/kernel/xhci.c index 1006b1a..f7d6c4a 100644 --- a/src/kernel/xhci.c +++ b/src/kernel/xhci.c @@ -68,6 +68,7 @@ typedef volatile struct __attribute__((packed)) { #define XHCI_PORT_REGS_PED(p) BITS_R((p)->portsc, 1, 1) #define XHCI_PORT_REGS_PR_R(p) BITS_R((p)->portsc, 4, 4) #define XHCI_PORT_REGS_PR_W(p, v) BITS_W((p)->portsc, 4, 4, v) +#define XHCI_PORT_REGS_PLL(p) BITS_R((p)->portsc, 8, 5) #define XHCI_PORT_REGS_PP(p) BITS_R((p)->portsc, 9, 9) #define XHCI_PORT_REGS_PS(p) BITS_R((p)->portsc, 13, 10) @@ -112,14 +113,19 @@ typedef struct __attribute__((packed)) { #define XHCI_TRB_CYCLE_W(t, v) BITS_W((t)->control, 0, 0, v) #define XHCI_TRB_TOGGLE_CYCLE_R(t) BITS_R((t)->control, 1, 1) #define XHCI_TRB_TOGGLE_CYCLE_W(t, v) BITS_W((t)->control, 1, 1, v) +#define XHCI_TRB_IOC_R(t) BITS_R((t)->control, 5, 5) +#define XHCI_TRB_IOC_W(t, v) BITS_W((t)->control, 5, 5, v) #define XHCI_TRB_TRB_TYPE_R(t) BITS_R((t)->control, 15, 10) #define XHCI_TRB_TRB_TYPE_W(t, v) BITS_W((t)->control, 15, 10, v) #define XHCI_TRB_SLOT_TYPE_R(t) BITS_R((t)->control, 20, 16) #define XHCI_TRB_SLOT_TYPE_W(t, v) BITS_W((t)->control, 20, 16, v) +#define XHCI_TRB_ENDPOINT_ID_R(t) BITS_R((t)->control, 20, 16) +#define XHCI_TRB_ENDPOINT_ID_W(t, v) BITS_W((t)->control, 20, 16, v) #define XHCI_TRB_COMPLETION_CODE(t) BITS_R((t)->status, 31, 24) #define XHCI_TRB_SLOT_ID_R(t) BITS_R((t)->control, 31, 24) #define XHCI_TRB_SLOT_ID_W(t, v) BITS_W((t)->control, 31, 24, v) +#define XHCI_TRB_TYPE_TRANSFER_NORMAL 1 #define XHCI_TRB_TYPE_TRANSFER_SETUP_STAGE 2 #define XHCI_TRB_TYPE_TRANSFER_DATA_STAGE 3 #define XHCI_TRB_TYPE_TRANSFER_STATUS_STAGE 4 @@ -127,6 +133,8 @@ typedef struct __attribute__((packed)) { #define XHCI_TRB_TYPE_COMMAND_ENABLE_SLOT 9 #define XHCI_TRB_TYPE_COMMAND_DISABLE_SLOT 10 #define XHCI_TRB_TYPE_COMMAND_ADDRESS_DEVICE 11 +#define XHCI_TRB_TYPE_COMMAND_CONFIGURE_ENDPOINT 12 +#define XHCI_TRB_TYPE_COMMAND_STOP_ENDPOINT 15 #define XHCI_TRB_TYPE_COMMAND_NOOP 23 #define XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION 32 #define XHCI_TRB_TYPE_EVENT_COMMAND_COMPLETION 33 @@ -136,13 +144,25 @@ static xchi_erst_entry_t erst[1] __attribute__((aligned(PAGE_SIZE))); #define RING_LENGTH 32 -static uint8_t cr_cycle = 1; -static uint8_t cr_i = 0; -static xhci_trb_t cr[RING_LENGTH + 1] __attribute__((aligned(PAGE_SIZE))); +#define RING(name) \ + static uint8_t name##_r_cycle = 1; \ + static uint8_t name##_r_i = 0; \ + static xhci_trb_t name##_r[RING_LENGTH + 1] __attribute__((aligned(PAGE_SIZE))); -static uint8_t er_cycle = 1; -static uint8_t er_i = 0; -static xhci_trb_t er[RING_LENGTH] __attribute__((aligned(PAGE_SIZE))); +#define RING_WRAP(name) \ + xhci_trb_t *name##_wrapper = &name##_r[RING_LENGTH]; \ + name##_wrapper->parameter = (uint64_t)VIRT_TO_PHYS(name##_r); \ + name##_wrapper->status = 0; \ + XHCI_TRB_TRB_TYPE_W(name##_wrapper, XHCI_TRB_TYPE_LINK); \ + XHCI_TRB_TOGGLE_CYCLE_W(name##_wrapper, 1); + +#define RING_ADVANCE(name) \ + name##_r_i = (name##_r_i + 1) % RING_LENGTH; \ + XHCI_TRB_CYCLE_W(&name##_r[RING_LENGTH], name##_r_cycle); \ + name##_r_cycle ^= (name##_r_i == 0); + +RING(cmd); +RING(evt); typedef struct __attribute__((packed)) { uint32_t drop_flags; @@ -173,10 +193,14 @@ typedef struct __attribute__((packed)) { uint32_t reserved[3]; } xhci_ep_ctx_t; -#define USB_EP_CTX_EP_TYPE_R(e) BITS_R((e)->dw1, 5, 3) -#define USB_EP_CTX_EP_TYPE_W(e, v) BITS_W((e)->dw1, 5, 3, v) -#define USB_EP_CTX_MAX_PACKET_SIZE_R(e) BITS_R((e)->dw1, 31, 16) -#define USB_EP_CTX_MAX_PACKET_SIZE_W(e, v) BITS_W((e)->dw1, 31, 16, v) +#define XHCI_EP_CTX_INTERVAL_R(e) BITS_R((e)->dw0, 23, 16) +#define XHCI_EP_CTX_INTERVAL_W(e, v) BITS_W((e)->dw0, 23, 16, v) +#define XHCI_EP_CTX_EP_TYPE_R(e) BITS_R((e)->dw1, 5, 3) +#define XHCI_EP_CTX_EP_TYPE_W(e, v) BITS_W((e)->dw1, 5, 3, v) +#define XHCI_EP_CTX_MAX_PACKET_SIZE_R(e) BITS_R((e)->dw1, 31, 16) +#define XHCI_EP_CTX_MAX_PACKET_SIZE_W(e, v) BITS_W((e)->dw1, 31, 16, v) + +#define XHCI_EP_CTX_MAX_PACKET_SIZE 64 typedef struct __attribute__((packed)) { xhci_input_control_ctx_t control; @@ -193,10 +217,15 @@ typedef struct __attribute__((packed)) { static xchi_device_ctx_t device_ctx __attribute__((aligned(64))); -static uint8_t tr_attached_slot = 0; -static uint8_t tr_cycle = 1; -static uint8_t tr_i = 0; -static xhci_trb_t tr[RING_LENGTH + 1] __attribute__((aligned(PAGE_SIZE))); +static uint8_t tsf_r_attached_slot = 0; +RING(tsf); + +static uint8_t ep_r_attached_endpoint = 0; +RING(ep); + +static uint16_t ep_transfer_size = 0; +static uint8_t ep_buffer[RING_LENGTH][XHCI_EP_CTX_MAX_PACKET_SIZE] __attribute__((aligned(64))); +static uint8_t ep_processed_events = RING_LENGTH; static const xhci_trb_t *command_exec_sync(const xhci_trb_t *cmd) { LOG_LN_STEP("Queueing command TRB..."); @@ -204,14 +233,10 @@ static const xhci_trb_t *command_exec_sync(const xhci_trb_t *cmd) { LOG_VAL_TRACE(cmd->status, "%x"); LOG_VAL_TRACE(cmd->control, "%x"); - xhci_trb_t *cre = &cr[cr_i]; - + xhci_trb_t *cre = &cmd_r[cmd_r_i]; memory_copy(cmd, sizeof(xhci_trb_t), cre); - XHCI_TRB_CYCLE_W(cre, cr_cycle); - - cr_i = (cr_i + 1) % RING_LENGTH; - XHCI_TRB_CYCLE_W((&cr[RING_LENGTH]), cr_cycle); - cr_cycle ^= (cr_i == 0); + XHCI_TRB_CYCLE_W(cre, cmd_r_cycle); + RING_ADVANCE(cmd); LOG_LN_STEP("Ringing command doorbell..."); LOG_VAL_TRACE(op_regs->usbsts, "%x"); @@ -220,16 +245,17 @@ static const xhci_trb_t *command_exec_sync(const xhci_trb_t *cmd) { LOG_LN_STEP("Waiting for completion..."); while (1) { - xhci_trb_t *cmp = &er[er_i]; - while (XHCI_TRB_CYCLE_R(cmp) != er_cycle) + xhci_trb_t *cmp = &evt_r[evt_r_i]; + while (XHCI_TRB_CYCLE_R(cmp) != evt_r_cycle) ; - - er_i = (er_i + 1) % RING_LENGTH; - intr_regs->erdp = (uint64_t)(VIRT_TO_PHYS(&er[er_i])); - er_cycle ^= (er_i == 0); + RING_ADVANCE(evt); if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_COMMAND_COMPLETION || (void *)cmp->parameter != VIRT_TO_PHYS(cre)) { LOG_LN_STEP("Received irrelevant event, skipping..."); + + LOG_VAL_TRACE(cmp->parameter, "%lx"); + LOG_VAL_TRACE(cmp->status, "%x"); + LOG_VAL_TRACE(cmp->control, "%x"); } else { LOG_LN_STEP("Received command completion event..."); @@ -246,10 +272,88 @@ static const xhci_trb_t *command_exec_sync(const xhci_trb_t *cmd) { } } +static const xhci_trb_t *control_transfer_exec_sync(uint64_t setup, void *data, uint16_t length) { + ASSERT(tsf_r_attached_slot, "control_transfer_exec_sync: Slot not attached."); + + LOG_LN_STEP("Queueing transfer TRBs..."); + + xhci_trb_t *tre = &tsf_r[tsf_r_i]; + + tre->parameter = setup; + tre->status = sizeof(setup); + XHCI_TRB_TRB_TYPE_W(tre, XHCI_TRB_TYPE_TRANSFER_SETUP_STAGE); + BITS_W(tre->control, 17, 16, 3); + BITS_W(tre->control, 6, 6, 1); + XHCI_TRB_CYCLE_W(tre, tsf_r_cycle); + + LOG_VAL_TRACE(tre->parameter, "%lx"); + LOG_VAL_TRACE(tre->status, "%x"); + LOG_VAL_TRACE(tre->control, "%x"); + + RING_ADVANCE(tsf); + + if (data != NUL) { + tre = &tsf_r[tsf_r_i]; + + tre->parameter = (uint64_t)VIRT_TO_PHYS(data); + tre->status = length; + XHCI_TRB_TRB_TYPE_W(tre, XHCI_TRB_TYPE_TRANSFER_DATA_STAGE); + BITS_W(tre->control, 16, 16, 1); + XHCI_TRB_CYCLE_W(tre, tsf_r_cycle); + + LOG_VAL_TRACE(tre->parameter, "%lx"); + LOG_VAL_TRACE(tre->status, "%x"); + LOG_VAL_TRACE(tre->control, "%x"); + + RING_ADVANCE(tsf); + } + + tre = &tsf_r[tsf_r_i]; + + XHCI_TRB_TRB_TYPE_W(tre, XHCI_TRB_TYPE_TRANSFER_STATUS_STAGE); + BITS_W(tre->control, 16, 16, 0); + XHCI_TRB_IOC_W(tre, 1); + XHCI_TRB_CYCLE_W(tre, tsf_r_cycle); + + RING_ADVANCE(tsf); + + LOG_LN_STEP("Ringing slot doorbell..."); + db_regs[tsf_r_attached_slot] = 1; + + LOG_LN_STEP("Waiting for completion..."); + while (1) { + xhci_trb_t *cmp = &evt_r[evt_r_i]; + while (XHCI_TRB_CYCLE_R(cmp) != evt_r_cycle) + ; + + RING_ADVANCE(evt); + + if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION || (void *)cmp->parameter != VIRT_TO_PHYS(tre)) { + LOG_LN_STEP("Received irrelevant event, skipping..."); + + LOG_VAL_TRACE(cmp->parameter, "%lx"); + LOG_VAL_TRACE(cmp->status, "%x"); + LOG_VAL_TRACE(cmp->control, "%x"); + } else { + LOG_LN_STEP("Received transfer completion event..."); + + LOG_VAL_TRACE(cmp->parameter, "%lx"); + LOG_VAL_TRACE(cmp->status, "%x"); + LOG_VAL_TRACE(cmp->control, "%x"); + + ASSERT(XHCI_TRB_COMPLETION_CODE(cmp) == 1, "control_transfer_exec_sync: Transfer failed."); + + LOG_LN_STEP("Done."); + + return cmp; + } + } +} + void xhci_init() { LOG_LN_INFO("Searching for suitable controller..."); pci_bdf_t bdf = pci_find_by_class(XHCI_PCI_CLASS, XHCI_PCI_SUBCLASS, XHCI_PCI_INTERFACE); - ASSERT(bdf, "xhci_init: No suitable controllers found."); + ASSERT(bdf != (pci_bdf_t)-1, "xhci_init: No suitable controllers found."); LOG_LN_INFO("Mapping controller to virtual memory..."); pci_map(bdf, cap_regs, 4); @@ -300,19 +404,15 @@ void xhci_init() { LOG_VAL_DATA(op_regs->usbsts, "%x"); LOG_LN_INFO("Setting up command ring..."); - xhci_trb_t *wrapper = &cr[RING_LENGTH]; - wrapper->parameter = (uint64_t)VIRT_TO_PHYS(cr); - wrapper->status = 0; - XHCI_TRB_TRB_TYPE_W(wrapper, XHCI_TRB_TYPE_LINK); - XHCI_TRB_TOGGLE_CYCLE_W(wrapper, 1); - XHCI_TRB_CYCLE_W(wrapper, cr_cycle); + RING_WRAP(cmd); + XHCI_TRB_CYCLE_W(&cmd_r[RING_LENGTH], cmd_r_cycle); LOG_LN_INFO("Configuring controller..."); XHCI_OP_REGS_MAX_SLOTS_EN_W(op_regs, MAX_SLOTS); op_regs->dcbaap = (uint64_t)VIRT_TO_PHYS(dcbaa); - op_regs->crcr = (uint64_t)VIRT_TO_PHYS(cr) | cr_cycle; + op_regs->crcr = (uint64_t)VIRT_TO_PHYS(cmd_r) | cmd_r_cycle; erst[0].size = RING_LENGTH; - erst[0].base = (uint64_t)VIRT_TO_PHYS(er); + erst[0].base = (uint64_t)VIRT_TO_PHYS(evt_r); intr_regs->erstsz = sizeof(erst) / sizeof(xchi_erst_entry_t); intr_regs->erstba = (uint64_t)VIRT_TO_PHYS(erst); intr_regs->erdp = erst[0].base; @@ -336,6 +436,7 @@ void xhci_init() { ; LOG_VAL_DATA(op_regs->usbcmd, "%x"); LOG_VAL_DATA(op_regs->usbsts, "%x"); + sleep(1000); // Wait for ports to settle PRINT_LN(kernel_log, "Enumerating ports..."); xhci_enumerate(kernel_log); @@ -343,9 +444,9 @@ void xhci_init() { } void xhci_enumerate(stream_t *out) { - for (uint8_t i = 0; i < XHCI_CAP_REGS_MAX_PORTS(cap_regs); i++) { - PRINT_LN(out, "port=%d CCS=%d PED=%d PP=%d PS=%d", i, XHCI_PORT_REGS_CCS(&port_regs[i]), XHCI_PORT_REGS_PED(&port_regs[i]), - XHCI_PORT_REGS_PP(&port_regs[i]), XHCI_PORT_REGS_PS(&port_regs[i])); + for (uint16_t i = 0; i < XHCI_CAP_REGS_MAX_PORTS(cap_regs); i++) { + PRINT_LN(out, "port=%d CCS=%d PED=%d PP=%d PS=%d portsc=%x", i, XHCI_PORT_REGS_CCS(&port_regs[i]), XHCI_PORT_REGS_PED(&port_regs[i]), + XHCI_PORT_REGS_PP(&port_regs[i]), XHCI_PORT_REGS_PS(&port_regs[i]), port_regs[i].portsc); } } @@ -353,27 +454,33 @@ uint8_t xhci_port_count() { return XHCI_CAP_REGS_MAX_PORTS(cap_regs); } -uint8_t xhci_port_connected(uint8_t port) { +uint8_t xhci_port_connected(xhci_port_t port) { + ASSERT(port < XHCI_CAP_REGS_MAX_PORTS(cap_regs), "xhci_port_connected: Port doesn't exist."); + return XHCI_PORT_REGS_CCS(&port_regs[port]); } -xhci_slot_t xhci_attach(uint8_t port) { +xhci_slot_t xhci_attach(xhci_port_t port) { LOG_LN_STEP("Attaching port %d ...", port); - ASSERT(!tr_attached_slot, "xhci_attach: Can only attach one port at a time.") + ASSERT(port < XHCI_CAP_REGS_MAX_PORTS(cap_regs), "xhci_attach: Port doesn't exist."); + ASSERT(!tsf_r_attached_slot, "xhci_attach: Can only attach one port at a time."); ASSERT(XHCI_PORT_REGS_CCS(&port_regs[port]), "xhci_attach: Port is not connected."); - memory_set(0, sizeof(tr), tr); - tr_i = 0; - tr_cycle = 1; + LOG_LN_STEP("Setting up transfer ring..."); + memory_set(0, sizeof(tsf_r), tsf_r); + tsf_r_i = 0; + tsf_r_cycle = 1; + RING_WRAP(tsf); LOG_LN_STEP("Resetting device..."); - port_regs[port].portsc = 1 << 4; + port_regs[port].portsc = (port_regs[port].portsc & ~(uint32_t)0x00FE0000) | (1 << 4); while (XHCI_PORT_REGS_PR_R(&port_regs[port])) ; - while (!XHCI_PORT_REGS_PED(&port_regs[port])) + LOG_VAL_TRACE(&port_regs[port].portsc, "%x"); + while (!XHCI_PORT_REGS_PED(&port_regs[port]) && XHCI_PORT_REGS_PLL(&port_regs[port]) != 0) ; - LOG_VAL_TRACE(XHCI_PORT_REGS_PED(&port_regs[port]), "%hhx"); + LOG_VAL_TRACE(&port_regs[port].portsc, "%x"); LOG_LN_STEP("Enabling a slot..."); xhci_trb_t cmd_es; @@ -387,9 +494,9 @@ xhci_slot_t xhci_attach(uint8_t port) { XHCI_SLOT_CTX_CTX_ENTRIES_W(&input_ctx.slot, 1); XHCI_SLOT_CTX_ROOT_HUB_PORT_W(&input_ctx.slot, port + 1); XHCI_SLOT_CTX_SPEED_W(&input_ctx.slot, XHCI_PORT_REGS_PS((&port_regs[port]))); - USB_EP_CTX_EP_TYPE_W(&input_ctx.ep[0], 4); - USB_EP_CTX_MAX_PACKET_SIZE_W(&input_ctx.ep[0], 64); - input_ctx.ep[0].dequeue = (uint64_t)VIRT_TO_PHYS(tr) | tr_cycle; + XHCI_EP_CTX_EP_TYPE_W(&input_ctx.ep[0], 4); + XHCI_EP_CTX_MAX_PACKET_SIZE_W(&input_ctx.ep[0], 64); + input_ctx.ep[0].dequeue = (uint64_t)VIRT_TO_PHYS(tsf_r) | tsf_r_cycle; dcbaa[slot] = VIRT_TO_PHYS(&device_ctx); @@ -400,7 +507,7 @@ xhci_slot_t xhci_attach(uint8_t port) { XHCI_TRB_SLOT_ID_W(&cmd_ad, slot); command_exec_sync(&cmd_ad); - tr_attached_slot = slot; + tsf_r_attached_slot = slot; LOG_LN_STEP("Done."); @@ -408,85 +515,138 @@ xhci_slot_t xhci_attach(uint8_t port) { } uint64_t xhci_control_transfer(xhci_slot_t slot, uint64_t setup, void *data, uint16_t length) { - ASSERT(tr_attached_slot == slot, "xhci_control_transfer: Can only attach one port at a time."); + ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist."); + ASSERT(tsf_r_attached_slot == slot, "xhci_control_transfer: Slot is not attached."); + ASSERT(!ep_r_attached_endpoint, "xhci_control_transfer: Can not control transfer while in polling mode."); - LOG_LN_STEP("Queueing transfer TRBs..."); + return length - (control_transfer_exec_sync(setup, data, length)->status & 0xFFFFFF); +} - tr[tr_i].parameter = setup; - tr[tr_i].status = sizeof(setup); - XHCI_TRB_TRB_TYPE_W(&tr[tr_i], XHCI_TRB_TYPE_TRANSFER_SETUP_STAGE); - BITS_W(tr[tr_i].control, 17, 16, 3); - BITS_W(tr[tr_i].control, 6, 6, 1); - XHCI_TRB_CYCLE_W(&tr[tr_i], tr_cycle); +xhci_endpoint_t xhci_open_endpoint(xhci_slot_t slot, uint8_t address, uint8_t type, uint16_t max_packet_size, uint8_t interval) { + LOG_LN_INFO("Opening endpoint slot %d address %hhx...", slot, address); - LOG_VAL_TRACE(tr[tr_i].parameter, "%lx"); - LOG_VAL_TRACE(tr[tr_i].status, "%x"); - LOG_VAL_TRACE(tr[tr_i].control, "%x"); + ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist."); + ASSERT(tsf_r_attached_slot == slot, "xhci_open_endpoint: Slot is not attached."); + ASSERT(!ep_r_attached_endpoint, "xhci_open_endpoint: Can only open one endpoint at a time."); + ASSERT(max_packet_size <= XHCI_EP_CTX_MAX_PACKET_SIZE, "xhci_open_endpoint: Packet size too big.") - tr_i = (tr_i + 1) % RING_LENGTH; - XHCI_TRB_CYCLE_W(&tr[RING_LENGTH], tr_cycle); - tr_cycle ^= (tr_i == 0); + uint8_t ep_num = address & 0x0F; + uint8_t ep_dir = (address >> 7) & 1; + uint8_t dci = ep_num * 2 + ep_dir; - tr[tr_i].parameter = (uint64_t)VIRT_TO_PHYS(data); - tr[tr_i].status = length; - XHCI_TRB_TRB_TYPE_W(&tr[tr_i], XHCI_TRB_TYPE_TRANSFER_DATA_STAGE); - BITS_W(tr[tr_i].control, 16, 16, 1); - XHCI_TRB_CYCLE_W(&tr[tr_i], tr_cycle); + LOG_LN_INFO("Setting up endpoint ring..."); + RING_WRAP(ep); + XHCI_TRB_CYCLE_W(&ep_r[RING_LENGTH], ep_r_cycle); - LOG_VAL_TRACE(tr[tr_i].parameter, "%lx"); - LOG_VAL_TRACE(tr[tr_i].status, "%x"); - LOG_VAL_TRACE(tr[tr_i].control, "%x"); + LOG_LN_INFO("Configuring endpoint..."); + memory_set(0, sizeof(xhci_input_ctx_t), &input_ctx); + input_ctx.slot = device_ctx.slot; + input_ctx.control.add_flags = (1 << 0) | (1 << dci); + XHCI_SLOT_CTX_CTX_ENTRIES_W(&input_ctx.slot, dci); - tr_i = (tr_i + 1) % RING_LENGTH; - XHCI_TRB_CYCLE_W(&tr[RING_LENGTH], tr_cycle); - tr_cycle ^= (tr_i == 0); + XHCI_EP_CTX_EP_TYPE_W(&input_ctx.ep[dci - 1], type); + XHCI_EP_CTX_MAX_PACKET_SIZE_W(&input_ctx.ep[dci - 1], max_packet_size); + XHCI_EP_CTX_INTERVAL_W(&input_ctx.ep[dci - 1], interval); + input_ctx.ep[dci - 1].dequeue = (uint64_t)VIRT_TO_PHYS(ep_r) | ep_r_cycle; - xhci_trb_t *tre = &tr[tr_i]; + xhci_trb_t cmd_ce; + memory_set(0, sizeof(xhci_trb_t), &cmd_ce); + XHCI_TRB_TRB_TYPE_W(&cmd_ce, XHCI_TRB_TYPE_COMMAND_CONFIGURE_ENDPOINT); + cmd_ce.parameter = (uint64_t)VIRT_TO_PHYS(&input_ctx); + XHCI_TRB_SLOT_ID_W(&cmd_ce, slot); + command_exec_sync(&cmd_ce); - XHCI_TRB_TRB_TYPE_W(&tr[tr_i], XHCI_TRB_TYPE_TRANSFER_STATUS_STAGE); - BITS_W(tr[tr_i].control, 16, 16, 0); - BITS_W(tr[tr_i].control, 5, 5, 1); - XHCI_TRB_CYCLE_W(&tr[tr_i], tr_cycle); + ep_r_attached_endpoint = dci; + ep_transfer_size = max_packet_size; - tr_i = (tr_i + 1) % RING_LENGTH; - XHCI_TRB_CYCLE_W(&tr[RING_LENGTH], tr_cycle); - tr_cycle ^= (tr_i == 0); + return dci; +} - LOG_LN_STEP("Ringing slot doorbell..."); - db_regs[tr_attached_slot] = 1; +uint16_t xhci_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t max, void *to) { + LOG_LN_STEP("Reading from endpoint %hhx...", endpoint); - LOG_LN_STEP("Waiting for completion..."); - while (1) { - xhci_trb_t *cmp = &er[er_i]; - while (XHCI_TRB_CYCLE_R(cmp) != er_cycle) - ; + ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist."); + ASSERT(tsf_r_attached_slot == slot, "xhci_read_endpoint: Slot is not attached."); + ASSERT(ep_r_attached_endpoint == endpoint, "xhci_read_endpoint: Endpoint not open."); - er_i = (er_i + 1) % RING_LENGTH; - intr_regs->erdp = (uint64_t)(VIRT_TO_PHYS(&er[er_i])); - er_cycle ^= (er_i == 0); + if (ep_processed_events == RING_LENGTH) { + LOG_LN_STEP("Event batch finished, posting new transfer requests..."); + ep_processed_events = 0; - if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION || (void *)cmp->parameter != VIRT_TO_PHYS(tre)) { + ASSERT(ep_r_i == 0, "xhci_read_endpoint: Endpoint ring cycle broken."); + for (uint64_t i = 0; i < RING_LENGTH; i++) { + xhci_trb_t *ere = &ep_r[i]; + + ere->parameter = (uint64_t)VIRT_TO_PHYS(&ep_buffer[i]); + ere->status = ep_transfer_size; + XHCI_TRB_TRB_TYPE_W(ere, XHCI_TRB_TYPE_TRANSFER_NORMAL); + XHCI_TRB_IOC_W(ere, 1); + XHCI_TRB_CYCLE_W(ere, ep_r_cycle); + RING_ADVANCE(ep); + } + + db_regs[tsf_r_attached_slot] = ep_r_attached_endpoint; + + return 0; + } + + uint16_t transferred = 0; + while (XHCI_TRB_CYCLE_R(&evt_r[evt_r_i]) == evt_r_cycle && !transferred) { + xhci_trb_t *cmp = &evt_r[evt_r_i]; + + if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION) { LOG_LN_STEP("Received irrelevant event, skipping..."); - } else { - LOG_LN_STEP("Received transfer completion event..."); - LOG_VAL_TRACE(cmp->parameter, "%lx"); LOG_VAL_TRACE(cmp->status, "%x"); LOG_VAL_TRACE(cmp->control, "%x"); - ASSERT(XHCI_TRB_COMPLETION_CODE(cmp) == 1, "xhci_control_transfer: Transfer failed."); + } else if (XHCI_TRB_COMPLETION_CODE(cmp) != 1) { + LOG_LN_STEP("Received unsuccessful transfer event, skipping..."); + ep_processed_events++; - LOG_LN_STEP("Done."); + LOG_VAL_TRACE(cmp->parameter, "%lx"); + LOG_VAL_TRACE(cmp->status, "%x"); + LOG_VAL_TRACE(cmp->control, "%x"); + } else { + LOG_LN_STEP("Received transfer event, returning..."); + ep_processed_events++; - return length - (cmp->status & 0xFFFFFF); + xhci_trb_t *cmd = PHYS_TO_VIRT(cmp->parameter); + uint16_t size = max < cmd->status ? max : (uint16_t)cmd->status; + memory_copy(PHYS_TO_VIRT(cmd->parameter), size, to); + + transferred = size; } + + RING_ADVANCE(evt); } + + return transferred; +} + +void xhci_close_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint) { + LOG_LN_INFO("Closing endpoind %hhx...", endpoint); + + ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist."); + ASSERT(tsf_r_attached_slot == slot, "xhci_close_endpoint: Slot is not attached."); + ASSERT(ep_r_attached_endpoint == endpoint, "xhci_close_endpoint: Endpoint not open."); + + ep_r_attached_endpoint = NUL; + + xhci_trb_t cmd_se; + memory_set(0, sizeof(xhci_trb_t), &cmd_se); + XHCI_TRB_TRB_TYPE_W(&cmd_se, XHCI_TRB_TYPE_COMMAND_STOP_ENDPOINT); + XHCI_TRB_SLOT_ID_W(&cmd_se, slot); + XHCI_TRB_ENDPOINT_ID_W(&cmd_se, endpoint); + command_exec_sync(&cmd_se); } void xhci_detach(xhci_slot_t slot) { LOG_LN_STEP("Detaching slot %d...", slot); - ASSERT(tr_attached_slot == slot, "xhci_detach: Slot not attached."); + ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist."); + ASSERT(tsf_r_attached_slot == slot, "xhci_detach: Slot not attached."); + ASSERT(!ep_r_attached_endpoint, "xhci_detach: Can not detach slot while in polling mode."); xhci_trb_t cmd_ds; memory_set(0, sizeof(xhci_trb_t), &cmd_ds); @@ -495,7 +655,7 @@ void xhci_detach(xhci_slot_t slot) { command_exec_sync(&cmd_ds); dcbaa[slot] = 0; - tr_attached_slot = 0; + tsf_r_attached_slot = 0; LOG_LN_STEP("Done."); } diff --git a/src/kernel/xhci.h b/src/kernel/xhci.h index 7a9f805..8b88735 100644 --- a/src/kernel/xhci.h +++ b/src/kernel/xhci.h @@ -3,7 +3,9 @@ #include "src/kernel/stream.h" #include +typedef uint8_t xhci_port_t; typedef uint8_t xhci_slot_t; +typedef uint8_t xhci_endpoint_t; void xhci_init(); // Assuming one and only one xHCI controller. @@ -11,10 +13,16 @@ void xhci_enumerate(stream_t *out); uint8_t xhci_port_count(); -uint8_t xhci_port_connected(uint8_t port); +uint8_t xhci_port_connected(xhci_port_t port); -xhci_slot_t xhci_attach(uint8_t port); +xhci_slot_t xhci_attach(xhci_port_t port); uint64_t xhci_control_transfer(xhci_slot_t slot, uint64_t setup, void *data, uint16_t length); +xhci_endpoint_t xhci_open_endpoint(xhci_slot_t slot, uint8_t address, uint8_t type, uint16_t max_packet_size, uint8_t interval); + +uint16_t xhci_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t max, void *to); + +void xhci_close_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint); + void xhci_detach(xhci_slot_t slot); diff --git a/src/lib/util.h b/src/lib/util.h index ffb957a..71515c6 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -17,3 +17,8 @@ typedef uint64_t exit_code_t; #define EXIT_CODE_OK 0 #define EXIT_CODE_NOT_FOUND ((uint64_t)-2) #define EXIT_CODE_GENERAL_FAILURE ((uint64_t)-1) + +static inline void sleep(uint64_t ms) { + for (volatile uint64_t i = 1000000 * ms; i; i--) // Very approximately + ; +}