XHCI set device configuration
All checks were successful
Build documentation / build-and-deploy (push) Successful in 4m6s
Build ISO image / build-and-deploy (push) Successful in 4m55s

This commit is contained in:
2026-04-02 16:24:28 +02:00
parent 024512c5e4
commit 88df4cbdd5
6 changed files with 270 additions and 120 deletions

View File

@@ -0,0 +1,6 @@
#include <device/usb/mass_storage.h>
#include <device/usb/usb.h>
#include <libk/std.h>
#include <sys/debug.h>
bool usb_mass_storage_init (void) { return true; }

View File

@@ -0,0 +1,8 @@
#ifndef _KERNEL_DEVICE_USB_MASS_STORAGE_H
#define _KERNEL_DEVICE_USB_MASS_STORAGE_H
#include <libk/std.h>
bool usb_mass_storage_init (void);
#endif // _KERNEL_DEVICE_USB_MASS_STORAGE_H

View File

@@ -1,3 +1,7 @@
c += device/usb/xhci.c
c += device/usb/xhci.c \
device/usb/usb.c \
device/usb/mass_storage.c
o += device/usb/xhci.o
o += device/usb/xhci.o \
device/usb/usb.o \
device/usb/mass_storage.o

6
kernel/device/usb/usb.c Normal file
View File

@@ -0,0 +1,6 @@
#include <device/usb/mass_storage.h>
#include <device/usb/usb.h>
struct usb_driver_info usb_driver_infos[USB_DRIVER_MAX_MATCHES] = {
{.if_class = 0x08, .if_subclass = 0x06, .if_proto = 0x50, .init = &usb_mass_storage_init},
};

View File

@@ -4,6 +4,8 @@
#include <aux/compiler.h>
#include <libk/std.h>
#define USB_DRIVER_MAX_MATCHES 1
/* descriptor types */
#define USB_DESC_DEVICE 1
@@ -75,4 +77,13 @@ struct usb_endpoint_desc {
uint8_t interval;
} PACKED;
struct usb_driver_info {
uint8_t if_class;
uint8_t if_subclass;
uint8_t if_proto;
bool (*init) (void);
};
extern struct usb_driver_info usb_driver_infos[USB_DRIVER_MAX_MATCHES];
#endif // _KERNEL_DEVICE_USB_H

View File

@@ -330,6 +330,83 @@ static void xhci_portsc_write (struct xhci* xhci, uint8_t port, uint32_t value)
xhci_write32 (xhci->xhci_oper_base, 1024 + (16 * port), value);
}
static void xhci_create_pdevice (struct xhci* xhci, struct xhci_port* xhci_port) {
struct xhci_pdevice* pdevice;
list_find (struct xhci_pdevice, xhci->xhci_pdevices, pdevice, xhci_port->port_value,
xhci_port->port_value, pdevices_link);
if (pdevice != NULL)
return;
pdevice = malloc (sizeof (*pdevice));
memset (pdevice, 0, sizeof (*pdevice));
pdevice->xhci_port = xhci_port;
pdevice->slot_id = -1;
list_append (xhci->xhci_pdevices, &pdevice->pdevices_link);
}
static void xhci_delete_pdevice (struct xhci* xhci, struct xhci_port* xhci_port) {
struct xhci_pdevice* pdevice;
list_find (struct xhci_pdevice, xhci->xhci_pdevices, pdevice, xhci_port->port_value,
xhci_port->port_value, pdevices_link);
list_remove (xhci->xhci_pdevices, &pdevice->pdevices_link);
if (pdevice->endpoint0_ring.phys != 0)
pmm_free (pdevice->endpoint0_ring.phys, 1);
pmm_free (xhci->xhci_dcbaa[pdevice->slot_id], 1);
xhci->xhci_dcbaa[pdevice->slot_id] = 0;
free (pdevice);
}
static void xhci_bios_handover (struct xhci* xhci) {
uint32_t hccparams1 = xhci_read32 (xhci->xhci_mmio_base, XHCI_HCCPARAMS1);
uint32_t ext_offset = (hccparams1 >> XHCI_HCCPARAMS1_XECP) << 2;
if (ext_offset == 0)
return;
while (ext_offset) {
uintptr_t cap_ptr = xhci->xhci_mmio_base + ext_offset;
uint32_t cap = xhci_read32 (cap_ptr, 0);
uint8_t cap_id = cap & 0xFF;
if (cap_id == XHCI_EXTCAP_USB_LEGACY_SUPPORT) {
/* Make or break on real hardware. We need to take over ownership from the BIOS. */
if (cap & (1 << XHCI_USBLEGSUP_BIOS_SEMA)) {
DEBUG ("BIOS owns XHCI, requesting handover!\n");
xhci_write8 (cap_ptr, 3, 1);
/* Wait for BIOS Semaphore owned bit to be cleared */
int timeout = 1000;
while (--timeout > 0) {
uint32_t val = xhci_read32 (cap_ptr, 0);
if (!(val & (1 << XHCI_USBLEGSUP_BIOS_SEMA)) && (val & (1 << XHCI_USBLEGSUP_OS_SEMA)))
break;
stall_ms (100);
}
DEBUG ("XHCI Handover OK\n");
}
break;
}
uint8_t next = (cap >> XHCI_XECP_NEXT_PTR) & 0xFF;
if (!next)
break;
ext_offset += (next << 2);
}
}
static void xhci_port_reset (struct xhci* xhci, uint8_t port) {
struct xhci_port* xhci_port;
@@ -372,38 +449,60 @@ static void xhci_port_reset (struct xhci* xhci, uint8_t port) {
stall_ms (100);
}
static void xhci_create_pdevice (struct xhci* xhci, struct xhci_port* xhci_port) {
struct xhci_pdevice* pdevice;
static void xhci_reset_ports (struct xhci* xhci) {
uint32_t hccparams1 = xhci_read32 (xhci->xhci_mmio_base, XHCI_HCCPARAMS1);
uint32_t ext_offset = (hccparams1 >> XHCI_HCCPARAMS1_XECP) << 2;
list_find (struct xhci_pdevice, xhci->xhci_pdevices, pdevice, xhci_port->port_value,
xhci_port->port_value, pdevices_link);
if (pdevice != NULL)
if (ext_offset == 0)
return;
pdevice = malloc (sizeof (*pdevice));
memset (pdevice, 0, sizeof (*pdevice));
pdevice->xhci_port = xhci_port;
pdevice->slot_id = -1;
while (ext_offset) {
uintptr_t cap_ptr = xhci->xhci_mmio_base + ext_offset;
uint32_t cap = xhci_read32 (cap_ptr, 0);
uint8_t cap_id = cap & 0xFF;
list_append (xhci->xhci_pdevices, &pdevice->pdevices_link);
}
if (cap_id == XHCI_EXTCAP_SUPPORTED_PROTOCOL) {
uint8_t minor = (cap >> XHCI_SUPPROTO_DW0_MINOR_REV) & 0xFF;
uint8_t major = (cap >> XHCI_SUPPROTO_DW0_MAJOR_REV) & 0xFF;
static void xhci_delete_pdevice (struct xhci* xhci, struct xhci_port* xhci_port) {
struct xhci_pdevice* pdevice;
uint32_t dword2 = xhci_read32 (cap_ptr, 8);
uint8_t port_off = (dword2 >> XHCI_SUPPROTO_DW2_PORT_OFF) & 0xFF;
uint8_t port_count = (dword2 >> XHCI_SUPPROTO_DW2_PORT_COUNT) & 0xFF;
list_find (struct xhci_pdevice, xhci->xhci_pdevices, pdevice, xhci_port->port_value,
xhci_port->port_value, pdevices_link);
uint8_t first_port = port_off - 1;
uint8_t last_port = first_port + port_count - 1;
list_remove (xhci->xhci_pdevices, &pdevice->pdevices_link);
for (uint8_t port = first_port; port <= last_port; port++) {
struct xhci_port* xhci_port = malloc (sizeof (*xhci_port));
memset (xhci_port, 0, sizeof (*xhci_port));
if (pdevice->endpoint0_ring.phys != 0)
pmm_free (pdevice->endpoint0_ring.phys, 1);
if (major == 3)
xhci_port->type = XHCI_PORT_USB3;
else
xhci_port->type = XHCI_PORT_USB2;
pmm_free (xhci->xhci_dcbaa[pdevice->slot_id], 1);
xhci->xhci_dcbaa[pdevice->slot_id] = 0;
xhci_port->port_value = port;
free (pdevice);
list_append (xhci->xhci_ports, &xhci_port->ports_link);
uint32_t portsc = xhci_portsc_read (xhci, port);
if ((portsc & (1 << XHCI_PORTSC_CCS))) {
DEBUG ("Device connected. resetting\n");
xhci_port_reset (xhci, port);
xhci_create_pdevice (xhci, xhci_port);
}
DEBUG ("PORT %u: USB %u.%u\n", port, major, minor);
}
}
uint8_t next = (cap >> XHCI_XECP_NEXT_PTR) & 0xFF;
if (!next)
break;
ext_offset += (next << 2);
}
}
static void xhci_event_dispatch (struct xhci* xhci, struct xhci_trb* event, uint8_t type) {
@@ -563,20 +662,96 @@ static bool xhci_endpoint0_ctrl_in (struct xhci* xhci, struct xhci_pdevice* pdev
memset (&trb, 0, sizeof (trb));
trb.param = data_phys;
trb.status = length;
trb.ctrl = (XHCI_TRB_DATA_STAGE << XHCI_DSTRB_CTRL_TRB_TYPE) | (1 << XHCI_DSTRB_CTRL_DIR);
write_memory_barrier ();
trb.ctrl = (XHCI_TRB_DATA_STAGE << XHCI_DSTRB_CTRL_TRB_TYPE) | (1 << XHCI_DSTRB_CTRL_DIR);
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 << XHCI_STSTRB_CTRL_TRB_TYPE) | (1 << XHCI_STSTRB_CTRL_IOC);
write_memory_barrier ();
trb.ctrl = (XHCI_TRB_STATUS_STAGE << XHCI_STSTRB_CTRL_TRB_TYPE) | (1 << XHCI_STSTRB_CTRL_IOC);
xhci_endpoint0_put_trb (pdevice, trb);
atomic_store (&xhci->pending, true);
int timeout = 100;
spin_unlock (&xhci->device->lock, *lockflags);
xhci_write32 (xhci->xhci_doorbell_base, pdevice->slot_id * 4, 1);
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 bool xhci_endpoint0_ctrl_out (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) {
/* clang-format off */
uint64_t setup = ((uint64_t)length << XHCI_SSTRB_PARAM_WLENGTH)
| ((uint64_t)index << XHCI_SSTRB_PARAM_WINDEX)
| ((uint64_t)value << XHCI_SSTRB_PARAM_WVALUE)
| ((uint64_t)request << XHCI_SSTRB_PARAM_BREQUEST)
| ((uint64_t)request_type << XHCI_SSTRB_PARAM_BMREQUEST_TYPE);
/* clang-format on */
struct xhci_trb trb;
/* setup stage */
memset (&trb, 0, sizeof (trb));
trb.param = setup;
trb.status = 8;
write_memory_barrier ();
if (length == 0) {
trb.ctrl = (XHCI_TRB_SETUP_STAGE << XHCI_SSTRB_CTRL_TRB_TYPE) |
(1 << XHCI_SSTRB_CTRL_IDT) |
(XHCI_SSTRB_TRT_NO_DATA_STAGE << XHCI_SSTRB_CTRL_TRT);
xhci_endpoint0_put_trb (pdevice, trb);
} else {
trb.ctrl = (XHCI_TRB_SETUP_STAGE << XHCI_SSTRB_CTRL_TRB_TYPE) |
(1 << XHCI_SSTRB_CTRL_IDT) |
(XHCI_SSTRB_TRT_OUT_DATA_STAGE << XHCI_SSTRB_CTRL_TRT);
xhci_endpoint0_put_trb (pdevice, trb);
/* data stage */
memset (&trb, 0, sizeof (trb));
trb.param = data_phys;
trb.status = length;
write_memory_barrier ();
trb.ctrl = (XHCI_TRB_DATA_STAGE << XHCI_DSTRB_CTRL_TRB_TYPE);
xhci_endpoint0_put_trb (pdevice, trb);
}
/* status stage */
memset (&trb, 0, sizeof (trb));
trb.param = 0;
trb.status = 0;
write_memory_barrier ();
trb.ctrl = (XHCI_TRB_STATUS_STAGE << XHCI_STSTRB_CTRL_TRB_TYPE) |
(1 << XHCI_STSTRB_CTRL_IOC) |
(1 << XHCI_STSTRB_CTRL_DIR);
xhci_endpoint0_put_trb (pdevice, trb);
atomic_store (&xhci->pending, true);
@@ -644,103 +819,18 @@ static void xhci_send_cmd (struct xhci* xhci, uint64_t param, uint32_t status, u
DEBUG ("timed out\n");
}
static void xhci_bios_handover (struct xhci* xhci) {
uint32_t hccparams1 = xhci_read32 (xhci->xhci_mmio_base, XHCI_HCCPARAMS1);
uint32_t ext_offset = (hccparams1 >> XHCI_HCCPARAMS1_XECP) << 2;
static void xhci_pdevice_setup_init_endpoints (struct xhci* xhci, struct xhci_pdevice* pdevice) {}
if (ext_offset == 0)
static void xhci_pdevice_setup_set_config (struct xhci* xhci, struct xhci_pdevice* pdevice,
uint8_t config_val, uint64_t* lockflags) {
bool ok = xhci_endpoint0_ctrl_out (xhci, pdevice, 0x00, 0x09, config_val, 0, 0, 0, lockflags);
if (!ok) {
DEBUG ("Failed to set the config!\n");
return;
while (ext_offset) {
uintptr_t cap_ptr = xhci->xhci_mmio_base + ext_offset;
uint32_t cap = xhci_read32 (cap_ptr, 0);
uint8_t cap_id = cap & 0xFF;
if (cap_id == XHCI_EXTCAP_USB_LEGACY_SUPPORT) {
/* Make or break on real hardware. We need to take over ownership from the BIOS. */
if (cap & (1 << XHCI_USBLEGSUP_BIOS_SEMA)) {
DEBUG ("BIOS owns XHCI, requesting handover!\n");
xhci_write8 (cap_ptr, 3, 1);
/* Wait for BIOS Semaphore owned bit to be cleared */
int timeout = 1000;
while (--timeout > 0) {
uint32_t val = xhci_read32 (cap_ptr, 0);
if (!(val & (1 << XHCI_USBLEGSUP_BIOS_SEMA)) && (val & (1 << XHCI_USBLEGSUP_OS_SEMA)))
break;
stall_ms (100);
}
DEBUG ("XHCI Handover OK\n");
}
break;
}
uint8_t next = (cap >> XHCI_XECP_NEXT_PTR) & 0xFF;
if (!next)
break;
ext_offset += (next << 2);
}
}
static void xhci_reset_ports (struct xhci* xhci) {
uint32_t hccparams1 = xhci_read32 (xhci->xhci_mmio_base, XHCI_HCCPARAMS1);
uint32_t ext_offset = (hccparams1 >> XHCI_HCCPARAMS1_XECP) << 2;
if (ext_offset == 0)
return;
while (ext_offset) {
uintptr_t cap_ptr = xhci->xhci_mmio_base + ext_offset;
uint32_t cap = xhci_read32 (cap_ptr, 0);
uint8_t cap_id = cap & 0xFF;
if (cap_id == XHCI_EXTCAP_SUPPORTED_PROTOCOL) {
uint8_t minor = (cap >> XHCI_SUPPROTO_DW0_MINOR_REV) & 0xFF;
uint8_t major = (cap >> XHCI_SUPPROTO_DW0_MAJOR_REV) & 0xFF;
uint32_t dword2 = xhci_read32 (cap_ptr, 8);
uint8_t port_off = (dword2 >> XHCI_SUPPROTO_DW2_PORT_OFF) & 0xFF;
uint8_t port_count = (dword2 >> XHCI_SUPPROTO_DW2_PORT_COUNT) & 0xFF;
uint8_t first_port = port_off - 1;
uint8_t last_port = first_port + port_count - 1;
for (uint8_t port = first_port; port <= last_port; port++) {
struct xhci_port* xhci_port = malloc (sizeof (*xhci_port));
memset (xhci_port, 0, sizeof (*xhci_port));
if (major == 3)
xhci_port->type = XHCI_PORT_USB3;
else
xhci_port->type = XHCI_PORT_USB2;
xhci_port->port_value = port;
list_append (xhci->xhci_ports, &xhci_port->ports_link);
uint32_t portsc = xhci_portsc_read (xhci, port);
if ((portsc & (1 << XHCI_PORTSC_CCS))) {
DEBUG ("Device connected. resetting\n");
xhci_port_reset (xhci, port);
xhci_create_pdevice (xhci, xhci_port);
}
DEBUG ("PORT %u: USB %u.%u\n", port, major, minor);
}
}
uint8_t next = (cap >> XHCI_XECP_NEXT_PTR) & 0xFF;
if (!next)
break;
ext_offset += (next << 2);
}
DEBUG ("Config set!\n");
}
static void xhci_pdevice_setup_addressing (struct xhci* xhci, struct xhci_pdevice* pdevice,
@@ -938,7 +1028,7 @@ static bool xhci_pdevice_setup_get_config_full (struct xhci* xhci, struct xhci_p
DEBUG (
"Found USB device interface: ifnum=%u altsetting=%u eps=%u ifclass=%u ifsubclass=%u ifproto=%u if=%u\n",
dif->desc.if_num, dif->desc.alt_setting, dif->desc.num_endpoints, dif->desc.if_class,
dif->desc.if_subclass, dif->desc.if1);
dif->desc.if_subclass, dif->desc.if_proto, dif->desc.if1);
} break;
case USB_DESC_ENDPOINT: {
struct xhci_pdevice_dendpoint* dendpoint = malloc (sizeof (*dendpoint));
@@ -962,6 +1052,29 @@ static bool xhci_pdevice_setup_get_config_full (struct xhci* xhci, struct xhci_p
return true;
}
static void xhci_poll_setup_init_ifs (struct xhci* xhci, struct xhci_pdevice* pdevice,
uint64_t* lockflags) {
struct list_node_link *ifs_link, *tmp_ifs_link;
list_foreach (pdevice->difs, ifs_link, tmp_ifs_link) {
struct xhci_pdevice_dif* dif = list_entry (ifs_link, struct xhci_pdevice_dif, ifs_link);
for (size_t i = 0; i < USB_DRIVER_MAX_MATCHES; i++) {
struct usb_driver_info* info = &usb_driver_infos[i];
DEBUG ("%u %u / %u %u / %u %u\n", dif->desc.if_class, info->if_class, dif->desc.if_subclass,
info->if_subclass, dif->desc.if_proto, info->if_proto);
if (dif->desc.if_class == info->if_class &&
dif->desc.if_subclass == info->if_subclass &&
dif->desc.if_proto == info->if_proto) {
if (!info->init ())
DEBUG ("USB driver failed to initialize. Skipping device!\n");
}
}
}
}
static void xhci_poll_setup_devices (struct xhci* xhci, uint64_t* lockflags) {
struct list_node_link *pdevice_link, *tmp_pdevice_link;
@@ -976,6 +1089,8 @@ static void xhci_poll_setup_devices (struct xhci* xhci, uint64_t* lockflags) {
xhci_pdevice_setup_get_info (xhci, pdevice, lockflags);
xhci_pdevice_setup_get_config (xhci, pdevice, lockflags);
xhci_pdevice_setup_get_config_full (xhci, pdevice, lockflags);
xhci_pdevice_setup_set_config (xhci, pdevice, 0, lockflags);
xhci_poll_setup_init_ifs (xhci, pdevice, lockflags);
}
}