diff --git a/kernel/device/usb/mass_storage.c b/kernel/device/usb/mass_storage.c new file mode 100644 index 0000000..350066b --- /dev/null +++ b/kernel/device/usb/mass_storage.c @@ -0,0 +1,6 @@ +#include +#include +#include +#include + +bool usb_mass_storage_init (void) { return true; } diff --git a/kernel/device/usb/mass_storage.h b/kernel/device/usb/mass_storage.h new file mode 100644 index 0000000..f766121 --- /dev/null +++ b/kernel/device/usb/mass_storage.h @@ -0,0 +1,8 @@ +#ifndef _KERNEL_DEVICE_USB_MASS_STORAGE_H +#define _KERNEL_DEVICE_USB_MASS_STORAGE_H + +#include + +bool usb_mass_storage_init (void); + +#endif // _KERNEL_DEVICE_USB_MASS_STORAGE_H diff --git a/kernel/device/usb/src.mk b/kernel/device/usb/src.mk index 2da6813..c6ae293 100644 --- a/kernel/device/usb/src.mk +++ b/kernel/device/usb/src.mk @@ -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 diff --git a/kernel/device/usb/usb.c b/kernel/device/usb/usb.c new file mode 100644 index 0000000..c9f9b68 --- /dev/null +++ b/kernel/device/usb/usb.c @@ -0,0 +1,6 @@ +#include +#include + +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}, +}; diff --git a/kernel/device/usb/usb.h b/kernel/device/usb/usb.h index 523d506..dd5c97d 100644 --- a/kernel/device/usb/usb.h +++ b/kernel/device/usb/usb.h @@ -4,6 +4,8 @@ #include #include +#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 diff --git a/kernel/device/usb/xhci.c b/kernel/device/usb/xhci.c index 50523b4..bd8e446 100644 --- a/kernel/device/usb/xhci.c +++ b/kernel/device/usb/xhci.c @@ -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); } }