USB driver polling user app, handle only one XHCI controller for now, Implement USB device addressing
All checks were successful
Build ISO image / build-and-deploy (push) Successful in 3m47s
Build documentation / build-and-deploy (push) Successful in 2m38s

This commit is contained in:
2026-03-29 18:59:20 +02:00
parent 69f063198a
commit 482afb47d7
22 changed files with 383 additions and 56 deletions

View File

@@ -4,6 +4,7 @@
#include <device/partitions.h>
#include <device/ramdrv.h>
#include <device/terminal.h>
#include <device_info.h>
#include <devices.h>
#include <id/id_alloc.h>
#include <libk/align.h>
@@ -50,7 +51,7 @@ struct device* device_find (const char* key) {
return hash_entry (found_link, struct device, device_table_link);
}
struct device* device_create (const char* key, device_op_func_t* ops, size_t ops_len,
struct device* device_create (int type, const char* key, device_op_func_t* ops, size_t ops_len,
device_init_func_t init, device_fini_func_t fini, void* arg,
struct proc* proc, struct reschedule_ctx* rctx) {
uint64_t fdt;
@@ -68,6 +69,7 @@ struct device* device_create (const char* key, device_op_func_t* ops, size_t ops
device->init = init;
device->fini = fini;
memcpy (device->key, key, strlen_null (key));
device->type = type;
if (ops != NULL)
memcpy (device->ops, ops, ops_len * sizeof (device_op_func_t));
@@ -91,6 +93,40 @@ struct device* device_create (const char* key, device_op_func_t* ops, size_t ops
return device;
}
size_t device_populate_device_infos (struct device_info* infos, size_t count) {
uint64_t fdt, fd;
if (count >= DEVICES_MAX)
count = DEVICES_MAX;
size_t j = 0;
spin_lock (&device_table.lock, &fdt);
for (size_t i = 0; i < lengthof (device_table.device_buckets); i++) {
struct hash_node_link* hash_link = device_table.device_buckets[i];
while (hash_link != NULL && j < count) {
struct device* device = hash_entry (hash_link, struct device, device_table_link);
struct device_info* info = &infos[j];
spin_lock (&device->lock, &fd);
memcpy (info->key, device->key, sizeof (info->key));
info->type = device->type;
spin_unlock (&device->lock, fd);
hash_link = hash_link->next;
j++;
}
}
spin_unlock (&device_table.lock, fdt);
return j;
}
static void debugconsole_device_init (void) {
struct reschedule_ctx rctx;
memset (&rctx, 0, sizeof (rctx));
@@ -98,8 +134,8 @@ static void debugconsole_device_init (void) {
device_op_func_t ops[] = {
[DEBUGCONSOLE_PUTSTR] = &debugconsole_putstr,
};
device_create ("debugconsole", ops, lengthof (ops), &debugconsole_init, &debugconsole_fini, NULL,
thiscpu->kproc, &rctx);
device_create (DEVICE_TYPE_DEBUGCONSOLE, "debugconsole", ops, lengthof (ops), &debugconsole_init,
&debugconsole_fini, NULL, thiscpu->kproc, &rctx);
}
static void terminal_device_init (void) {
@@ -110,8 +146,8 @@ static void terminal_device_init (void) {
[TERMINAL_PUTSTR] = &terminal_putstr,
[TERMINAL_DIMENSIONS] = &terminal_dimensions,
};
device_create ("terminal", ops, lengthof (ops), &terminal_init, &terminal_fini, NULL,
thiscpu->kproc, &rctx);
device_create (DEVICE_TYPE_TERMINAL, "terminal", ops, lengthof (ops), &terminal_init,
&terminal_fini, NULL, thiscpu->kproc, &rctx);
}
static void sys_device_init (void) {
@@ -155,8 +191,8 @@ static void sys_device_init (void) {
.sector_size = 512,
.buffer = unpack_buffer,
};
device_create ("sys0", ops, lengthof (ops), &ramdrv_init, &ramdrv_fini, &init, thiscpu->kproc,
&rctx);
device_create (DEVICE_TYPE_DRIVE, "sys0", ops, lengthof (ops), &ramdrv_init, &ramdrv_fini, &init,
thiscpu->kproc, &rctx);
LZ4F_freeDecompressionContext (dctx);
}
@@ -177,8 +213,8 @@ static void temp_device_init (void) {
.total_size = 1024 * 1024 * 20,
.sector_size = 512,
};
device_create ("temp0", ops, lengthof (ops), &ramdrv_init, &ramdrv_fini, &init, thiscpu->kproc,
&rctx);
device_create (DEVICE_TYPE_DRIVE, "temp0", ops, lengthof (ops), &ramdrv_init, &ramdrv_fini, &init,
thiscpu->kproc, &rctx);
}
#if defined(__x86_64__)
@@ -189,7 +225,8 @@ static void ps2kb_device_init (void) {
device_op_func_t ops[] = {
[KB_READ_KEY] = &ps2kb_read_key,
};
device_create ("kb", ops, lengthof (ops), &ps2kb_init, &ps2kb_fini, NULL, thiscpu->kproc, &rctx);
device_create (DEVICE_TYPE_KEYBOARD, "kb", ops, lengthof (ops), &ps2kb_init, &ps2kb_fini, NULL,
thiscpu->kproc, &rctx);
}
#endif

View File

@@ -1,6 +1,7 @@
#ifndef _KERNEL_DEVICE_DEVICE_H
#define _KERNEL_DEVICE_DEVICE_H
#include <device_info.h>
#include <libk/hash.h>
#include <libk/list.h>
#include <libk/std.h>
@@ -35,14 +36,17 @@ struct device {
device_init_func_t init;
device_fini_func_t fini;
void* udata;
int type;
};
struct device* device_create (const char* key, device_op_func_t* ops, size_t ops_len,
struct device* device_create (int type, const char* key, device_op_func_t* ops, size_t ops_len,
device_init_func_t init, device_fini_func_t fini, void* arg,
struct proc* proc, struct reschedule_ctx* rctx);
struct device* device_find (const char* key);
size_t device_populate_device_infos (struct device_info* infos, size_t count);
void devices_init (void);
#endif // _KERNEL_DEVICE_DEVICE_H

View File

@@ -59,7 +59,8 @@ static int device_probe_partitions_dos (struct proc* proc, struct reschedule_ctx
memset (key, 0, sizeof (key));
snprintf (key, sizeof (key), "%sp%zu", device->key, i);
device_create (key, ops, lengthof (ops), &partdrv_init, &partdrv_fini, &init, proc, rctx);
device_create (DEVICE_TYPE_DRIVE, key, ops, lengthof (ops), &partdrv_init, &partdrv_fini, &init,
proc, rctx);
}
spin_unlock (&device->lock, fd);

View File

@@ -58,8 +58,8 @@ static void ide_make_device (struct proc* proc, struct reschedule_ctx* rctx,
.irq = probe.irq,
.irqs_support = probe.irqs_support,
};
struct device* ide = device_create (device_key, ops, lengthof (ops), &idedrv_init, &idedrv_fini,
&init, proc, rctx);
struct device* ide = device_create (DEVICE_TYPE_DRIVE, device_key, ops, lengthof (ops),
&idedrv_init, &idedrv_fini, &init, proc, rctx);
device_probe_partitions (proc, rctx, ide);
}

View File

@@ -6,6 +6,7 @@
#include <device/pci_info.h>
#include <device/pci_xhci.h>
#include <device/xhci.h>
#include <devices.h>
#include <libk/align.h>
#include <libk/lengthof.h>
#include <libk/printf.h>
@@ -16,16 +17,11 @@
#include <sys/debug.h>
#include <sys/mm.h>
static int xhci_counter = 0;
static bool xhci_init_done = false;
bool pci_xhci_init (struct proc* proc, struct reschedule_ctx* rctx, struct pci_info pci_info) {
struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
if (xhci_counter == 3) {
DEBUG ("Cannot initialize more XHCI controllers\n");
return false;
}
uint8_t progif = pci_read8 (pci_info.bus, pci_info.slot, pci_info.func, PCI_PROG_IF);
/* not an XHCI controller */
@@ -33,6 +29,11 @@ bool pci_xhci_init (struct proc* proc, struct reschedule_ctx* rctx, struct pci_i
return true;
}
if (xhci_init_done) {
DEBUG ("Cannot initialize more XHCI controllers\n");
return false;
}
uint16_t pci_cmd = pci_read16 (pci_info.bus, pci_info.slot, pci_info.func, PCI_COMMAND);
uint16_t new_cmd = (pci_cmd | (1 << 1) | (1 << 2)) & ~(1 << 10);
@@ -48,7 +49,7 @@ bool pci_xhci_init (struct proc* proc, struct reschedule_ctx* rctx, struct pci_i
map_pages = pci_get_bar_size (pci_info.bus, pci_info.slot, pci_info.func, PCI_BAR0);
map_pages = div_align_up (map_pages, PAGE_SIZE);
if ((bar0 & PCI_BAR_MEM64)) {
if ((bar0 & 0x6) == 0x4) {
uint32_t bar1 = pci_read32 (pci_info.bus, pci_info.slot, pci_info.func, PCI_BAR1);
xhci_phys = ((uint64_t)bar1 << 32) | (bar0 & ~0xF);
DEBUG ("XHCI phys base addr=%p (64 bit)\n", xhci_phys);
@@ -73,15 +74,13 @@ bool pci_xhci_init (struct proc* proc, struct reschedule_ctx* rctx, struct pci_i
bool irqs_support = false;
uint32_t irq = INTR_XHCI0 + xhci_counter;
if (!pci_msi_init (pci_info.bus, pci_info.slot, pci_info.func, irq, thiscpu->lapic_id)) {
if (!pci_msi_init (pci_info.bus, pci_info.slot, pci_info.func, INTR_XHCI, thiscpu->lapic_id)) {
uint8_t intr_line = pci_read8 (pci_info.bus, pci_info.slot, pci_info.func, PCI_INTERRUPT_LINE);
uint8_t intr_pin = pci_read8 (pci_info.bus, pci_info.slot, pci_info.func, PCI_INTERRUPT_PIN);
if (intr_pin != 0) {
irqs_support = true;
ioapic_route_irq (irq, intr_line, 0, thiscpu->lapic_id);
ioapic_route_irq (INTR_XHCI, intr_line, 0, thiscpu->lapic_id);
}
} else {
irqs_support = true;
@@ -89,19 +88,20 @@ bool pci_xhci_init (struct proc* proc, struct reschedule_ctx* rctx, struct pci_i
DEBUG ("IRQ support=%d\n", irqs_support);
char key[32];
snprintf (key, sizeof (key), "xhci%d", xhci_counter++);
struct xhci_init init = {
.xhci_mmio_base = xhci_base,
.irqs_support = irqs_support,
.irq = irq,
.irq = INTR_XHCI,
};
device_op_func_t ops[] = {0};
device_op_func_t ops[] = {
[XUSBCTRL_POLL_DRIVER] = &xhci_poll_driver,
};
struct device* xhci =
device_create (key, ops, lengthof (ops), &xhci_init, &xhci_fini, &init, proc, rctx);
device_create (DEVICE_TYPE_USB_CTRL, "xhci", ops, lengthof (ops), &xhci_init, &xhci_fini, &init,
proc, rctx);
xhci_init_done = true;
return true;
}

View File

@@ -11,6 +11,7 @@
#include <proc/proc.h>
#include <proc/reschedule.h>
#include <proc/suspension_q.h>
#include <status.h>
#include <sys/debug.h>
#include <sys/spin_lock.h>
#include <sys/stall.h>
@@ -180,6 +181,37 @@ 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;
list_find (struct xhci_plugged_device, xhci->xhci_plugged_devices, pdevice, port_value, port,
plugged_devices_link);
if (pdevice != NULL)
return;
pdevice = malloc (sizeof (*pdevice));
memset (pdevice, 0, sizeof (*pdevice));
pdevice->port_value = port;
pdevice->slot_id = -1;
list_append (xhci->xhci_plugged_devices, &pdevice->plugged_devices_link);
}
static void xhci_delete_plugged_device (struct xhci* xhci, uint8_t port) {
struct xhci_plugged_device* pdevice;
list_find (struct xhci_plugged_device, xhci->xhci_plugged_devices, pdevice, port_value, port,
plugged_devices_link);
list_remove (xhci->xhci_plugged_devices, &pdevice->plugged_devices_link);
if (pdevice->endpoint0_ring_phys != 0)
pmm_free (pdevice->endpoint0_ring_phys, 1);
free (pdevice);
}
static void xhci_event_dispatch (struct xhci* xhci, struct xhci_trb* event, uint8_t type) {
switch (type) {
case XHCI_TRB_CMD_CMPL_EVENT: {
@@ -187,6 +219,8 @@ static void xhci_event_dispatch (struct xhci* xhci, struct xhci_trb* event, uint
uint8_t slot_id = (event->ctrl >> 24) & 0xFF;
DEBUG ("cmd completion: code=%u,slot=%u\n", cmpl_code, slot_id);
xhci->last_slot_id = slot_id;
} break;
case XHCI_TRB_PORT_STS_CHNG: {
uint8_t port = ((event->param >> 24) & 0xFF) - 1;
@@ -200,10 +234,15 @@ static void xhci_event_dispatch (struct xhci* xhci, struct xhci_trb* event, uint
xhci_portsc_write (xhci, port, portsc);
if ((portsc & (1 << 17))) {
if ((portsc & (1 << 0)))
if ((portsc & (1 << 0))) {
DEBUG ("Device attached to port %u!\n", port);
else
xhci_create_plugged_device (xhci, port);
} else {
DEBUG ("Device detached from port %u!\n", port);
xhci_delete_plugged_device (xhci, port);
}
}
if ((portsc & (1 << 21)))
@@ -271,11 +310,8 @@ static void xhci_irq (void* arg, void* regs, bool user, struct reschedule_ctx* r
spin_unlock (&xhci->device->lock, fd);
}
void xhci_send_cmd (struct xhci* xhci, uint64_t param, uint32_t status, uint32_t ctrl) {
uint64_t fd;
spin_lock (&xhci->device->lock, &fd);
static void xhci_send_cmd (struct xhci* xhci, uint64_t param, uint32_t status, uint32_t ctrl,
uint64_t* lockflags) {
if (xhci->irqs_support) {
if (xhci->cmd_ring_idx == (xhci->cmd_ring_size - 1)) {
struct xhci_trb* link = &xhci->cmd_ring[xhci->cmd_ring_idx];
@@ -298,14 +334,14 @@ void xhci_send_cmd (struct xhci* xhci, uint64_t param, uint32_t status, uint32_t
xhci_write32 (xhci->xhci_doorbell_base, 0, 0);
spin_unlock (&xhci->device->lock, fd);
int timeout = 100;
while (atomic_load (&xhci->pending) && --timeout > 0)
stall_ms (1);
spin_unlock (&xhci->device->lock, *lockflags);
spin_lock (&xhci->device->lock, &fd);
while (atomic_load (&xhci->pending) && --timeout > 0)
stall_ms (10);
spin_lock (&xhci->device->lock, lockflags);
if (timeout == 0)
DEBUG ("timed out\n");
@@ -315,8 +351,6 @@ void xhci_send_cmd (struct xhci* xhci, uint64_t param, uint32_t status, uint32_t
spin_lock_relax ();
}
}
spin_unlock (&xhci->device->lock, fd);
}
static void xhci_bios_handover (struct xhci* xhci) {
@@ -406,8 +440,6 @@ static void xhci_reset_ports (struct xhci* xhci) {
DEBUG ("PORT %u: USB %u.%u\n", port, major, minor);
}
break;
}
uint8_t next = (cap >> 8) & 0xFF;
@@ -451,6 +483,9 @@ DEFINE_DEVICE_INIT (xhci_init) {
uint32_t hcsparams2 = xhci_read32 (xhci->xhci_mmio_base, XHCI_HCSPARAMS2);
xhci->max_scratchpad = (((hcsparams2 >> 21) & 0x1F) << 5) | ((hcsparams2 >> 27) & 0x1F);
uint32_t hccparams1 = xhci_read32 (xhci->xhci_mmio_base, XHCI_HCCPARAMS1);
xhci->xhci_ctx_size = (hccparams1 & (1 << 2)) ? 64 : 32;
DEBUG ("starting init sequence\n");
/* stop running / clear Run/Stop bit */
@@ -544,7 +579,7 @@ DEFINE_DEVICE_INIT (xhci_init) {
usbcmd = xhci_read32 (xhci->xhci_oper_base, XHCI_USBCMD);
xhci_write32 (xhci->xhci_oper_base, XHCI_USBCMD, usbcmd | (1 << 0) | (1 << 2));
xhci_send_cmd (xhci, 0, 0, XHCI_TRB_SLOT_ENAB_CMD << 10);
stall_ms (500);
xhci_reset_ports (xhci);
@@ -569,6 +604,13 @@ DEFINE_DEVICE_FINI (xhci_fini) {
pmm_free (scratchpads_phys, 1);
}
for (size_t i = 0; i < PAGE_SIZE / sizeof (xhci->xhci_dcbaa[0]); i++) {
uintptr_t out_ctx = xhci->xhci_dcbaa[i];
if (out_ctx != 0)
pmm_free (out_ctx, 1);
}
pmm_free (xhci->xhci_dcbaa_phys, 1);
pmm_free (xhci->cmd_ring_phys, 1);
pmm_free (xhci->event_ring_phys, 1);
@@ -586,3 +628,86 @@ 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);
xhci_send_cmd (xhci, 0, 0, XHCI_TRB_SLOT_ENAB_CMD << 10, lockflags);
uint8_t slot_id = xhci->last_slot_id;
pdevice->slot_id = 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);
ctx64->slot.dw[1] = ((pdevice->port_value + 1) << 16);
ctx64->endpoints[0].dw[1] = (3 << 1) | (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);
ctx32->slot.dw[1] = ((pdevice->port_value + 1) << 16);
ctx32->endpoints[0].dw[1] = (3 << 1) | (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);
}
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);
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;
}

View File

@@ -23,6 +23,26 @@ struct xhci_trb {
uint32_t ctrl;
} PACKED;
struct xhci_ctx32 {
uint32_t dw[8];
} PACKED;
struct xhci_ctx64 {
uint32_t dw[16];
} PACKED;
struct xhci_input_ctx32 {
struct xhci_ctx32 ctrl;
struct xhci_ctx32 slot;
struct xhci_ctx32 endpoints[31];
};
struct xhci_input_ctx64 {
struct xhci_ctx64 ctrl;
struct xhci_ctx64 slot;
struct xhci_ctx64 endpoints[31];
};
struct xhci_init {
uintptr_t xhci_mmio_base;
bool irqs_support;
@@ -38,6 +58,15 @@ struct xhci_port {
int type;
};
struct xhci_plugged_device {
struct list_node_link plugged_devices_link;
uint8_t port_value;
int slot_id;
struct xhci_trb* endpoint0_ring;
uintptr_t endpoint0_ring_phys;
};
struct xhci {
struct device* device;
@@ -73,13 +102,21 @@ struct xhci {
struct xhci_erst_entry* erst;
uintptr_t erst_phys;
atomic_bool pending;
size_t xhci_ctx_size;
struct list_node_link* xhci_ports;
struct list_node_link* xhci_plugged_devices;
atomic_bool pending;
uint8_t last_slot_id;
spin_lock_t setup_lock;
};
DEFINE_DEVICE_INIT (xhci_init);
DEFINE_DEVICE_FINI (xhci_fini);
DEFINE_DEVICE_OP (xhci_poll_driver);
#endif // _KERNEL_DEVICE_XHCI_H