From dcfc1a6e42c197b06f1335990415d2cd9b36e72a Mon Sep 17 00:00:00 2001 From: kamkow1 Date: Tue, 31 Mar 2026 15:32:14 +0200 Subject: [PATCH] XHCI get USB device descriptor --- kernel/device/usb.h | 63 ++++++++ kernel/device/xhci.c | 340 +++++++++++++++++++++++++++++-------------- kernel/device/xhci.h | 9 +- 3 files changed, 297 insertions(+), 115 deletions(-) create mode 100644 kernel/device/usb.h diff --git a/kernel/device/usb.h b/kernel/device/usb.h new file mode 100644 index 0000000..8bc653d --- /dev/null +++ b/kernel/device/usb.h @@ -0,0 +1,63 @@ +#ifndef _KERNEL_DEVICE_USB_H +#define _KERNEL_DEVICE_USB_H + +#include +#include + +struct usb_desc_hdr { + uint8_t length; + uint8_t desc_type; +} PACKED; + +struct usb_device_desc { + struct usb_desc_hdr hdr; + uint16_t bcd_usb; + uint8_t dev_class; + uint8_t dev_subclass; + uint8_t dev_proto; + uint8_t max_packet_size; + uint16_t vendor_id; + uint16_t product_id; + uint16_t bcd_device; + uint8_t manufacturer; + uint8_t product; + uint8_t serial_num; + uint8_t num_configs; +} PACKED; + +struct usb_string_lang_desc { + struct usb_desc_hdr hdr; + uint16_t langs[126]; +} PACKED; + +struct usb_config_desc { + struct usb_desc_hdr hdr; + uint16_t total_length; + uint8_t num_ifs; + uint8_t config_value; + uint8_t config; + uint8_t attrs; + uint8_t max_power; + uint8_t data[245]; +} PACKED; + +struct usb_if_desc { + struct usb_desc_hdr hdr; + uint8_t if_num; + uint8_t alt_setting; + uint8_t num_endpoints; + uint8_t if_class; + uint8_t if_subclass; + uint8_t if_proto; + uint8_t if1; +} PACKED; + +struct usb_endpoint_desc { + struct usb_desc_hdr hdr; + uint8_t endpoint_addr; + uint8_t attrs; + uint16_t max_packet_size; + uint8_t interval; +} PACKED; + +#endif // _KERNEL_DEVICE_USB_H diff --git a/kernel/device/xhci.c b/kernel/device/xhci.c index 25247da..aafa50b 100644 --- a/kernel/device/xhci.c +++ b/kernel/device/xhci.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -181,11 +182,10 @@ static void xhci_port_reset (struct xhci* xhci, uint8_t port) { stall_ms (100); } -static void xhci_create_plugged_device (struct xhci* xhci, uint8_t port) { - struct xhci_plugged_device* pdevice; +static void xhci_create_pdevice (struct xhci* xhci, uint8_t port) { + struct xhci_pdevice* pdevice; - list_find (struct xhci_plugged_device, xhci->xhci_plugged_devices, pdevice, port_value, port, - plugged_devices_link); + list_find (struct xhci_pdevice, xhci->xhci_pdevices, pdevice, port_value, port, pdevices_link); if (pdevice != NULL) return; @@ -195,16 +195,15 @@ static void xhci_create_plugged_device (struct xhci* xhci, uint8_t port) { pdevice->port_value = port; pdevice->slot_id = -1; - list_append (xhci->xhci_plugged_devices, &pdevice->plugged_devices_link); + list_append (xhci->xhci_pdevices, &pdevice->pdevices_link); } -static void xhci_delete_plugged_device (struct xhci* xhci, uint8_t port) { - struct xhci_plugged_device* pdevice; +static void xhci_delete_pdevice (struct xhci* xhci, uint8_t port) { + struct xhci_pdevice* pdevice; - list_find (struct xhci_plugged_device, xhci->xhci_plugged_devices, pdevice, port_value, port, - plugged_devices_link); + list_find (struct xhci_pdevice, xhci->xhci_pdevices, pdevice, port_value, port, pdevices_link); - list_remove (xhci->xhci_plugged_devices, &pdevice->plugged_devices_link); + list_remove (xhci->xhci_pdevices, &pdevice->pdevices_link); if (pdevice->endpoint0_ring_phys != 0) pmm_free (pdevice->endpoint0_ring_phys, 1); @@ -238,17 +237,26 @@ static void xhci_event_dispatch (struct xhci* xhci, struct xhci_trb* event, uint if ((portsc & (1 << 0))) { DEBUG ("Device attached to port %u!\n", port); - xhci_create_plugged_device (xhci, port); + xhci_create_pdevice (xhci, port); } else { DEBUG ("Device detached from port %u!\n", port); - xhci_delete_plugged_device (xhci, port); + xhci_delete_pdevice (xhci, port); } } if ((portsc & (1 << 21))) DEBUG ("Port %u reset done\n", port); } break; + case XHCI_TRB_TRANSFER_EVENT: { + uint8_t cmpl_code = (event->status >> 24) & 0xFF; + uint8_t slot_id = (event->ctrl >> 24) & 0xFF; + uint8_t endpoint_id = (event->ctrl >> 16) & 0x1F; + + DEBUG ("transfer completion: code=%u,slot=%u,endpoint_id=%u\n", cmpl_code, slot_id, + endpoint_id); + xhci->last_cmpl_code = cmpl_code; + } break; default: DEBUG ("Unhandled event type %u at %u\n", type, xhci->event_ring_idx); break; @@ -311,6 +319,77 @@ static void xhci_irq (void* arg, void* regs, bool user, struct reschedule_ctx* r spin_unlock (&xhci->device->lock, fd); } +static void xhci_endpoint0_put_trb (struct xhci_pdevice* pdevice, struct xhci_trb trb) { + if (pdevice->endpoint0_ring_idx == pdevice->endpoint0_ring_size - 1) { + struct xhci_trb* link = &pdevice->endpoint0_ring[pdevice->endpoint0_ring_idx]; + link->param = pdevice->endpoint0_ring_phys; + link->status = 0; + link->ctrl = (XHCI_TRB_LINK << 10) | (1 << 1) | pdevice->endpoint0_cycle_bit; + + pdevice->endpoint0_ring_idx = 0; + pdevice->endpoint0_cycle_bit ^= 1; + } + + pdevice->endpoint0_ring[pdevice->endpoint0_ring_idx] = trb; + pdevice->endpoint0_ring[pdevice->endpoint0_ring_idx].ctrl = + (pdevice->endpoint0_ring[pdevice->endpoint0_ring_idx].ctrl & ~1u) | + pdevice->endpoint0_cycle_bit; + + pdevice->endpoint0_ring_idx++; +} + +static bool xhci_endpoint0_ctrl_in (struct xhci* xhci, struct xhci_pdevice* pdevice, + uint8_t request_type, uint8_t request, uint16_t value, + uint16_t index, uintptr_t data_phys, uint16_t length, + uint64_t* lockflags) { + uint64_t setup = ((uint64_t)length << 48) | ((uint64_t)index << 32) | ((uint64_t)value << 16) | + ((uint64_t)request << 8) | ((uint64_t)request_type); + + struct xhci_trb trb; + + xhci->last_cmpl_code = 0; + atomic_store (&xhci->pending, true); + + /* setup stage */ + memset (&trb, 0, sizeof (trb)); + trb.param = setup; + trb.status = 8; + trb.ctrl = (XHCI_TRB_SETUP_STAGE << 10) | (1 << 6) | (1 << 4); + xhci_endpoint0_put_trb (pdevice, trb); + + /* data stage */ + memset (&trb, 0, sizeof (trb)); + trb.param = data_phys; + trb.status = length; + trb.ctrl = (XHCI_TRB_DATA_STAGE << 10) | (1 << 16) | (1 << 4); + xhci_endpoint0_put_trb (pdevice, trb); + + /* status stage */ + memset (&trb, 0, sizeof (trb)); + trb.param = 0; + trb.status = 0; + trb.ctrl = (XHCI_TRB_STATUS_STAGE << 10) | (1 << 5); + xhci_endpoint0_put_trb (pdevice, trb); + + xhci_write32 (xhci->xhci_doorbell_base, pdevice->slot_id * 4, 1); + + int timeout = 100; + + spin_unlock (&xhci->device->lock, *lockflags); + + while (atomic_load (&xhci->pending) && --timeout > 0) + stall_ms (10); + + spin_lock (&xhci->device->lock, lockflags); + + if (timeout == 0) { + DEBUG ("timed out\n"); + return false; + } + + return xhci->last_cmpl_code == 1; +} + static void xhci_send_cmd (struct xhci* xhci, uint64_t param, uint32_t status, uint32_t ctrl, uint64_t* lockflags) { if (xhci->irqs_support) { @@ -437,7 +516,7 @@ static void xhci_reset_ports (struct xhci* xhci) { if ((portsc & (1 << 0))) { DEBUG ("Device connected. resetting\n"); xhci_port_reset (xhci, port); - xhci_create_plugged_device (xhci, port); + xhci_create_pdevice (xhci, port); } DEBUG ("PORT %u: USB %u.%u\n", port, major, minor); @@ -452,6 +531,142 @@ static void xhci_reset_ports (struct xhci* xhci) { } } +static void xhci_pdevice_setup_addressing (struct xhci* xhci, struct xhci_pdevice* pdevice, + uint64_t* lockflags) { + struct limine_hhdm_response* hhdm = limine_hhdm_request.response; + + struct xhci_port* xhci_port; + list_find (struct xhci_port, xhci->xhci_ports, xhci_port, port_value, pdevice->port_value, + ports_link); + + uint32_t portsc = xhci_portsc_read (xhci, pdevice->port_value); + + uint32_t speed = (portsc >> 10) & 0x0F; + + xhci->last_cmpl_code = 0; + xhci_send_cmd (xhci, 0, 0, XHCI_TRB_SLOT_ENAB_CMD << 10, lockflags); + + if (xhci->last_cmpl_code != 1) { + DEBUG ("Enable slot failed\n"); + return; + } + + pdevice->slot_id = xhci->last_slot_id; + + uintptr_t out_ctx_phys = pmm_alloc (1); + void* out_ctx_virt = (void*)(out_ctx_phys + (uintptr_t)hhdm->offset); + memset (out_ctx_virt, 0, PAGE_SIZE); + xhci->xhci_dcbaa[pdevice->slot_id] = out_ctx_phys; + + pdevice->endpoint0_ring_phys = pmm_alloc (1); + pdevice->endpoint0_ring = + (struct xhci_trb*)(pdevice->endpoint0_ring_phys + (uintptr_t)hhdm->offset); + memset (pdevice->endpoint0_ring, 0, PAGE_SIZE); + pdevice->endpoint0_ring_size = PAGE_SIZE / sizeof (struct xhci_trb); + pdevice->endpoint0_ring_idx = 0; + pdevice->endpoint0_cycle_bit = 1; + + uintptr_t input_ctx_phys = pmm_alloc (1); + void* input_ctx_virt = (void*)(input_ctx_phys + (uintptr_t)hhdm->offset); + memset (input_ctx_virt, 0, PAGE_SIZE); + + if (xhci->xhci_ctx_size == 64) { + struct xhci_input_ctx64* ctx64 = input_ctx_virt; + + size_t max_packet_size = (xhci_port->type == XHCI_PORT_USB3) ? 512 : 64; + + /* Add slot and endpoint 0 */ + ctx64->ctrl.dw[1] = (1 << 0) | (1 << 1); + + ctx64->slot.dw[0] = (1 << 27) | (speed << 20); + ctx64->slot.dw[1] = ((pdevice->port_value + 1) << 16); + + ctx64->endpoints[0].dw[0] = 0; + ctx64->endpoints[0].dw[1] = 3 | (4 << 3) | (max_packet_size << 16); + ctx64->endpoints[0].dw[2] = (uint32_t)pdevice->endpoint0_ring_phys | (1 << 0); + ctx64->endpoints[0].dw[3] = (uint32_t)(pdevice->endpoint0_ring_phys >> 32); + } else { + struct xhci_input_ctx32* ctx32 = input_ctx_virt; + + size_t max_packet_size = (xhci_port->type == XHCI_PORT_USB3) ? 512 : 64; + + ctx32->ctrl.dw[1] = (1 << 0) | (1 << 1); + + ctx32->slot.dw[0] = (1 << 27) | (speed << 20); + ctx32->slot.dw[1] = ((pdevice->port_value + 1) << 16); + + ctx32->endpoints[0].dw[1] = 3 | (4 << 3) | (max_packet_size << 16); + ctx32->endpoints[0].dw[2] = (uint32_t)pdevice->endpoint0_ring_phys | (1 << 0); + ctx32->endpoints[0].dw[3] = (uint32_t)(pdevice->endpoint0_ring_phys >> 32); + } + + xhci->last_cmpl_code = 0; + uint32_t ctrl = (pdevice->slot_id << 24) | (XHCI_TRB_ADDR_DEV_CMD << 10); + xhci_send_cmd (xhci, input_ctx_phys, 0, ctrl, lockflags); + + stall_ms (500); + + if (xhci->last_cmpl_code != 1) { + DEBUG ("Failed to address device. port = %u, slot = %u\n", pdevice->port_value, + pdevice->slot_id); + } else { + DEBUG ("Device on port %u addressed on slot %u!\n", pdevice->port_value, pdevice->slot_id); + } + + pmm_free (input_ctx_phys, 1); +} + +static bool xhci_pdevice_setup_get_info (struct xhci* xhci, struct xhci_pdevice* pdevice, + uint64_t* lockflags) { + struct limine_hhdm_response* hhdm = limine_hhdm_request.response; + + uintptr_t response_buf_phys = pmm_alloc (1); + + void* response_buf = (void*)(response_buf_phys + (uintptr_t)hhdm->offset); + memset (response_buf, 0, PAGE_SIZE); + + bool ok = xhci_endpoint0_ctrl_in (xhci, pdevice, 0x80, 6, (1 << 8), 0, response_buf_phys, + sizeof (struct usb_device_desc), lockflags); + + if (!ok) { + pmm_free (response_buf_phys, 1); + return false; + } + + struct usb_device_desc* usb_desc = response_buf; + + DEBUG ("USB device info: vndr=%04x prdct=%04x class=%02x subclass=%02x mps=%u cfgs=%u\n", + usb_desc->vendor_id, usb_desc->product_id, usb_desc->dev_class, usb_desc->dev_subclass, + usb_desc->max_packet_size, usb_desc->num_configs); + + pmm_free (response_buf_phys, 1); + return true; +} + +static void xhci_poll_setup_devices (struct xhci* xhci, uint64_t* lockflags) { + struct list_node_link *pdevice_link, *tmp_pdevice_link; + + list_foreach (xhci->xhci_pdevices, pdevice_link, tmp_pdevice_link) { + struct xhci_pdevice* pdevice = list_entry (pdevice_link, struct xhci_pdevice, pdevices_link); + + /* slot assigned */ + if (pdevice->slot_id != -1) + continue; + + xhci_pdevice_setup_addressing (xhci, pdevice, lockflags); + + xhci_pdevice_setup_get_info (xhci, pdevice, lockflags); + } +} + +DEFINE_DEVICE_OP (xhci_poll_driver) { + struct xhci* xhci = device->udata; + + xhci_poll_setup_devices (xhci, lockflags); + + return ST_OK; +} + DEFINE_DEVICE_INIT (xhci_init) { struct limine_hhdm_response* hhdm = limine_hhdm_request.response; @@ -630,102 +845,3 @@ DEFINE_DEVICE_FINI (xhci_fini) { free (xhci); } - -static void xhci_poll_setup_devices (struct xhci* xhci, uint64_t* lockflags) { - struct list_node_link *pdevice_link, *tmp_pdevice_link; - struct limine_hhdm_response* hhdm = limine_hhdm_request.response; - - list_foreach (xhci->xhci_plugged_devices, pdevice_link, tmp_pdevice_link) { - struct xhci_plugged_device* pdevice = - list_entry (pdevice_link, struct xhci_plugged_device, plugged_devices_link); - - /* slot assigned */ - if (pdevice->slot_id != -1) - continue; - - struct xhci_port* xhci_port; - list_find (struct xhci_port, xhci->xhci_ports, xhci_port, port_value, pdevice->port_value, - ports_link); - - uint32_t portsc = xhci_portsc_read (xhci, pdevice->port_value); - - uint32_t speed = (portsc >> 10) & 0x0F; - - xhci->last_cmpl_code = 0; - xhci_send_cmd (xhci, 0, 0, XHCI_TRB_SLOT_ENAB_CMD << 10, lockflags); - - if (xhci->last_cmpl_code != 1) { - DEBUG ("Enable slot failed\n"); - continue; - } - - pdevice->slot_id = xhci->last_slot_id; - - uintptr_t out_ctx_phys = pmm_alloc (1); - void* out_ctx_virt = (void*)(out_ctx_phys + (uintptr_t)hhdm->offset); - memset (out_ctx_virt, 0, PAGE_SIZE); - xhci->xhci_dcbaa[pdevice->slot_id] = out_ctx_phys; - - pdevice->endpoint0_ring_phys = pmm_alloc (1); - pdevice->endpoint0_ring = - (struct xhci_trb*)(pdevice->endpoint0_ring_phys + (uintptr_t)hhdm->offset); - memset (pdevice->endpoint0_ring, 0, PAGE_SIZE); - - uintptr_t input_ctx_phys = pmm_alloc (1); - void* input_ctx_virt = (void*)(input_ctx_phys + (uintptr_t)hhdm->offset); - memset (input_ctx_virt, 0, PAGE_SIZE); - - if (xhci->xhci_ctx_size == 64) { - struct xhci_input_ctx64* ctx64 = input_ctx_virt; - - size_t max_packet_size = (xhci_port->type == XHCI_PORT_USB3) ? 512 : 64; - - /* Add slot and endpoint 0 */ - ctx64->ctrl.dw[1] = (1 << 0) | (1 << 1); - - ctx64->slot.dw[0] = (1 << 27) | (speed << 20); - ctx64->slot.dw[1] = ((pdevice->port_value + 1) << 16); - - ctx64->endpoints[0].dw[0] = 0; - ctx64->endpoints[0].dw[1] = 3 | (4 << 3) | (max_packet_size << 16); - ctx64->endpoints[0].dw[2] = (uint32_t)pdevice->endpoint0_ring_phys | (1 << 0); - ctx64->endpoints[0].dw[3] = (uint32_t)(pdevice->endpoint0_ring_phys >> 32); - } else { - struct xhci_input_ctx32* ctx32 = input_ctx_virt; - - size_t max_packet_size = (xhci_port->type == XHCI_PORT_USB3) ? 512 : 64; - - ctx32->ctrl.dw[1] = (1 << 0) | (1 << 1); - - ctx32->slot.dw[0] = (1 << 27) | (speed << 20); - ctx32->slot.dw[1] = ((pdevice->port_value + 1) << 16); - - ctx32->endpoints[0].dw[1] = 3 | (4 << 3) | (max_packet_size << 16); - ctx32->endpoints[0].dw[2] = (uint32_t)pdevice->endpoint0_ring_phys | (1 << 0); - ctx32->endpoints[0].dw[3] = (uint32_t)(pdevice->endpoint0_ring_phys >> 32); - } - - xhci->last_cmpl_code = 0; - uint32_t ctrl = (pdevice->slot_id << 24) | (XHCI_TRB_ADDR_DEV_CMD << 10); - xhci_send_cmd (xhci, input_ctx_phys, 0, ctrl, lockflags); - - stall_ms (500); - - if (xhci->last_cmpl_code != 1) { - DEBUG ("Failed to address device. port = %u, slot = %u\n", pdevice->port_value, - pdevice->slot_id); - } else { - DEBUG ("Device on port %u addressed on slot %u!\n", pdevice->port_value, pdevice->slot_id); - } - - pmm_free (input_ctx_phys, 1); - } -} - -DEFINE_DEVICE_OP (xhci_poll_driver) { - struct xhci* xhci = device->udata; - - xhci_poll_setup_devices (xhci, lockflags); - - return ST_OK; -} diff --git a/kernel/device/xhci.h b/kernel/device/xhci.h index cccc2d2..a480862 100644 --- a/kernel/device/xhci.h +++ b/kernel/device/xhci.h @@ -58,13 +58,16 @@ struct xhci_port { int type; }; -struct xhci_plugged_device { - struct list_node_link plugged_devices_link; +struct xhci_pdevice { + struct list_node_link pdevices_link; uint8_t port_value; int slot_id; struct xhci_trb* endpoint0_ring; uintptr_t endpoint0_ring_phys; + uint32_t endpoint0_ring_idx; + uint32_t endpoint0_ring_size; + uint8_t endpoint0_cycle_bit; }; struct xhci { @@ -105,7 +108,7 @@ struct xhci { size_t xhci_ctx_size; struct list_node_link* xhci_ports; - struct list_node_link* xhci_plugged_devices; + struct list_node_link* xhci_pdevices; atomic_bool pending; uint8_t last_slot_id;