Read USB mass storage sector count and sector size!

This commit is contained in:
2026-04-06 01:00:54 +02:00
parent 90217168be
commit 1cb1bad3dd
17 changed files with 800 additions and 347 deletions

View File

@@ -19,293 +19,12 @@
#include <sys/spin_lock.h>
#include <sys/stall.h>
/* REF:
* https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf
*/
/* clang-format off */
/* capability registers */
#define XHCI_CAPLENGTH 0x00
#define XHCI_RSVD 0x01
#define XHCI_HCIVERSION 0x02
#define XHCI_HCSPARAMS1 0x04
#define XHCI_HCSPARAMS2 0x08
#define XHCI_HCSPARAMS3 0x0C
#define XHCI_HCCPARAMS1 0x10
#define XHCI_DBOFF 0x14
#define XHCI_RTSOFF 0x18
#define XHCI_HCCPARAMS2 0x1C
/* operational registers */
#define XHCI_USBCMD 0x00
#define XHCI_USBSTS 0x04
#define XHCI_PAGESIZE 0x08
#define XHCI_DNCTRL 0x14
#define XHCI_CRCR 0x18
#define XHCI_DCBAAP 0x30
#define XHCI_CONFIG 0x38
/* port registers */
#define XHCI_PORTSC 0x00
#define XHCI_PORTPMSC 0x04
#define XHCI_PORTLI 0x08
/* runtime registers */
#define XHCI_MFINDEX 0x00
/* + IRQ sets (0x20) */
#define XHCI_IMAN 0x00
#define XHCI_IMOD 0x04
#define XHCI_ERSTSZ 0x08
#define XHCI_ERSTBA 0x10
#define XHCI_ERDP 0x18
/* HCCPARAMS1 */
#define XHCI_HCCPARAMS1_AC64 0
#define XHCI_HCCPARAMS1_BNC 1
#define XHCI_HCCPARAMS1_CSZ 2
#define XHCI_HCCPARAMS1_PPC 3
#define XHCI_HCCPARAMS1_PIND 4
#define XHCI_HCCPARAMS1_LHRC 5
#define XHCI_HCCPARAMS1_LTC 6
#define XHCI_HCCPARAMS1_NSS 7
#define XHCI_HCCPARAMS1_PAE 8
#define XHCI_HCCPARAMS1_SPC 9
#define XHCI_HCCPARAMS1_SEC 10
#define XHCI_HCCPARAMS1_CFC 11
#define XHCI_HCCPARAMS1_MAX_PSA_SIZE 12
#define XHCI_HCCPARAMS1_XECP 16
/* XHCI extended capabilities */
#define XHCI_XECP_CAP_ID 0
#define XHCI_XECP_NEXT_PTR 8
/* USB legacy support capability */
#define XHCI_USBLEGSUP_CAP_ID 0
#define XHCI_USBLEGSUP_NEXT_PTR 8
#define XHCI_USBLEGSUP_BIOS_SEMA 16
#define XHCI_USBLEGSUP_OS_SEMA 24
/* Supported protocol capability */
#define XHCI_SUPPROTO_DW0_CAP_ID 0
#define XHCI_SUPPROTO_DW0_NEXT_PTR 8
#define XHCI_SUPPROTO_DW0_MINOR_REV 16
#define XHCI_SUPPROTO_DW0_MAJOR_REV 24
#define XHCI_SUPPROTO_DW1_NAME_STRING 0
#define XHCI_SUPPROTO_DW2_PORT_OFF 0
#define XHCI_SUPPROTO_DW2_PORT_COUNT 8
#define XHCI_SUPPROTO_DW2_PROT_DEF 16
#define XHCI_SUPPROTO_DW2_PSIC 28
#define XHCI_SUPPROTO_DW3_SLOT_TYPE 0
/* HCSPARAMS1 */
#define XHCI_HCSPARAMS1_MAX_DEV_SLOTS 0
#define XHCI_HCSPARAMS1_MAX_INTRS 8
#define XHCI_HCSPARAMS1_MAX_PORTS 24
/* HCSPARAMS2 */
#define XHCI_HCSPARAMS2_IST 0
#define XHCI_HCSPARAMS2_ERST_MAX 4
#define XHCI_HCSPARAMS2_MAX_SCRTCH_HI 21
#define XHCI_HCSPARAMS2_SPR 26
#define XHCI_HCSPARAMS2_MAX_SCRTCH_LO 27
/* event types */
#define XHCI_TRB_NORMAL 1
#define XHCI_TRB_SETUP_STAGE 2
#define XHCI_TRB_DATA_STAGE 3
#define XHCI_TRB_STATUS_STAGE 4
#define XHCI_TRB_ISOCH 5
#define XHCI_TRB_LINK 6
#define XHCI_TRB_EVENT_DATA 7
#define XHCI_TRB_NOOP 8
#define XHCI_TRB_SLOT_ENAB_CMD 9
#define XHCI_TRB_SLOT_DISB_CMD 10
#define XHCI_TRB_ADDR_DEV_CMD 11
#define XHCI_TRB_CFG_ENDP_CMD 12
#define XHCI_TRB_EVAL_CTX_CMD 13
#define XHCI_TRB_RESET_ENDP_CMD 14
#define XHCI_TRB_STOP_ENDP_CMD 15
#define XHCI_TRB_SET_DRDQP_CMD 16
#define XHCI_TRB_RESET_DEV_CMD 17
#define XHCI_TRB_FORCE_EVT_CMD 18
#define XHCI_TRB_NEGO_BANDW_CMD 19
#define XHCI_TRB_SET_LTV_CMD 20
#define XHCI_TRB_PORT_BANDW_CMD 21
#define XHCI_TRB_FORCE_HEADER 22
#define XHCI_TRB_NOOP_CMD 23
#define XHCI_TRB_GET_EXTPRP_CMD 24
#define XHCI_TRB_SET_EXTPRP_CMD 25
#define XHCI_TRB_TRANSFER_EVENT 32
#define XHCI_TRB_CMD_CMPL_EVENT 33
#define XHCI_TRB_PORT_STS_CHNG 34
#define XHCI_TRB_BANDW_RQ_EVENT 35
#define XHCI_TRB_DOORBELL_EVENT 36
#define XHCI_TRB_HOST_CTRL_EVNT 37
#define XHCI_TRB_DEV_NOTIF_EVNT 38
#define XHCI_TRB_MFINDEX_WRAP 39
/* generic TRB bits */
#define XHCI_GTRB_TRB_TYPE 10
#define XHCI_GTRB_CYCLE_BIT 0
/* command completion event TRB */
#define XHCI_CCETRB_CTRL_SLOT_ID 24
#define XHCI_CCETRB_CTRL_VFID 16
#define XHCI_CCETRB_CTRL_TRB_TYPE 10
#define XHCI_CCETRB_CTRL_CYCLEBIT 0
#define XHCI_CCETRB_STS_CMPL_CODE 24
/* transfer event TRB */
#define XHCI_TETRB_CTRL_SLOT_ID 24
#define XHCI_TETRB_CTRL_ENDPOINT 16
#define XHCI_TETRB_CTRL_TRB_TYPE 10
#define XHCI_TETRB_CTRL_EVT_DATA 2
#define XHCI_TETRB_CTRL_CYCLEBIT 0
#define XHCI_TETRB_STS_CMPL_CODE 24
/* port status change event TRB */
#define XHCI_PSCETRB_CTRL_TRB_TYPE 10
#define XHCI_PSCETRB_CTRL_CYCLEBIT 0
#define XHCI_PSCETRB_STS_CMPL_CODE 24
#define XHCI_PSCETRB_PARAM_PORT_ID 24
/* link TRB */
#define XHCI_LNKTRB_CTRL_TRB_TYPE 10
#define XHCI_LNKTRB_CTRL_IOC 5
#define XHCI_LNKTRB_CTRL_CH 4
#define XHCI_LNKTRB_CTRL_TC 1
#define XHCI_LNKTRB_CTRL_CYCLEBIT 0
#define XHCI_LNKTRB_STS_INTR_TRGT 22
/* setup stage TRB */
#define XHCI_SSTRB_CTRL_TRT 16
#define XHCI_SSTRB_CTRL_TRB_TYPE 10
#define XHCI_SSTRB_CTRL_IDT 6
#define XHCI_SSTRB_CTRL_IOC 5
#define XHCI_SSTRB_CTRL_CYCLEBIT 0
#define XHCI_SSTRB_STS_INTR_TRGT 22
#define XHCI_SSTRB_PARAM_WLENGTH 48
#define XHCI_SSTRB_PARAM_WINDEX 32
#define XHCI_SSTRB_PARAM_WVALUE 16
#define XHCI_SSTRB_PARAM_BREQUEST 8
#define XHCI_SSTRB_PARAM_BMREQUEST_TYPE 0
/* setup stage TRB transfer types */
#define XHCI_SSTRB_TRT_NO_DATA_STAGE 0
#define XHCI_SSTRB_TRT_OUT_DATA_STAGE 2
#define XHCI_SSTRB_TRT_IN_DATA_STAGE 3
/* data stage TRB */
#define XHCI_DSTRB_CTRL_DIR 16
#define XHCI_DSTRB_CTRL_TRB_TYPE 10
#define XHCI_DSTRB_CTRL_IDT 6
#define XHCI_DSTRB_CTRL_IOC 5
#define XHCI_DSTRB_CTRL_CH 4
#define XHCI_DSTRB_CTRL_NS 3
#define XHCI_DSTRB_CTRL_ISP 2
#define XHCI_DSTRB_CTRL_ENT 1
#define XHCI_DSTRB_CTRL_CYCLEBIT 0
#define XHCI_DSTRB_STS_INTR_TRGT 22
#define XHCI_DSTRB_STS_TD_SIZE 17
/* status stage TRB */
#define XHCI_STSTRB_CTRL_DIR 16
#define XHCI_STSTRB_CTRL_TRB_TYPE 10
#define XHCI_STSTRB_CTRL_IOC 5
#define XHCI_STSTRB_CTRL_CH 4
#define XHCI_STSTRB_CTRL_ENT 1
#define XHCI_STSTRB_CTRL_CYCLEBIT 0
#define XHCI_STSTRB_STS_INTR_TRGT 22
/* extended capabilities */
#define XHCI_EXTCAP_USB_LEGACY_SUPPORT 1
#define XHCI_EXTCAP_SUPPORTED_PROTOCOL 2
#define XHCI_EXTCAP_EXT_POWER_MNGMNT 3
#define XHCI_EXTCAP_IO_VIRT 4
#define XHCI_EXTCAP_MSG_INTR 5
#define XHCI_EXTCAP_LOCAL_MEMORY 6
#define XHCI_EXTCAP_USB_DEBUG 7
#define XHCI_EXTCAP_EXT_MSG_INTR 8
#define XHCI_EXTCAP_VENDOR_DEFINED 9
/* PORTSC bits */
#define XHCI_PORTSC_CCS 0
#define XHCI_PORTSC_PED 1
#define XHCI_PORTSC_OCA 3
#define XHCI_PORTSC_PR 4
#define XHCI_PORTSC_PLS 5
#define XHCI_PORTSC_PP 9
#define XHCI_PORTSC_PORTSPEED 10
#define XHCI_PORTSC_PIC 14
#define XHCI_PORTSC_LWS 16
#define XHCI_PORTSC_CSC 17
#define XHCI_PORTSC_PEC 18
#define XHCI_PORTSC_WRC 19
#define XHCI_PORTSC_OCC 20
#define XHCI_PORTSC_PRC 21
#define XHCI_PORTSC_PLC 22
#define XHCI_PORTSC_CEC 23
#define XHCI_PORTSC_CAS 24
#define XHCI_PORTSC_WCE 25
#define XCHI_PORTSC_WDE 26
#define XHCI_PORTSC_WOE 27
#define XHCI_PORTSC_DR 30
#define XHCI_PORTSC_WPR 31
/* endpoint context */
#define XHCI_EPCTX_EP_STATE 0
#define XHCI_EPCTX_MULT 8
#define XHCI_EPCTX_MAXPSTRAMS 10
#define XHCI_EPCTX_LSA 15
#define XHCI_EPCTX_INTERVAL 16
#define XHCI_EPCTX_MAX_ESIT_HI 24
#define XHCI_EPCTX_ERR_COUNT 1
#define XHCI_EPCTX_EP_TYPE 3
#define XHCI_EPCTX_HID 7
#define XHCI_EPCTX_MAX_BURST_SZ 8
#define XHCI_EPCTX_MAX_PKT_SZ 16
#define XHCI_EPCTX_DCS 0
#define XHCI_EPCTX_TR_DQ_PTR 4
#define XHCI_EPCTX_AVG_TRB_LEN 0
#define XHCI_EPCTX_MAX_ESIT_LOW 16
/* endpoint types */
#define XHCI_EP_INVALID 0
#define XHCI_EP_ISOCH_OUT 1
#define XHCI_EP_BULK_OUT 2
#define XHCI_EP_INTR_OUT 3
#define XHCI_EP_CTRL_BI 4
#define XHCI_EP_ISOCH_IN 5
#define XHCI_EP_BULK_IN 6
#define XHCI_EP_INTR_IN 7
/* slot context */
#define XHIC_SLCTX_ROUTE_STR 0
#define XHCI_SLCTX_SPEED 20
#define XHCI_SLCTX_MTT 25
#define XHCI_SLCTX_HUB 26
#define XHCI_SLCTX_CTX_ENTRIES 27
#define XHCI_SLCTX_MAXEXITLTNCY 0
#define XHCI_SLCTX_ROOTHUBPRNUM 16
#define XHCI_SLCTX_PORT_COUNT 24
#define XHCI_SLCTX_PRNT_HUB_SLT 0
#define XHCI_SLCTX_PARENT_PORT 8
#define XHCI_SLCTX_TT_THINKTIME 16
#define XHCI_SLCTX_INTR_TARGET 22
#define XHCI_SLCTX_USB_DEV_ADDR 0
#define XHCI_SLCTX_SLOT_STATE 27
/* clang-format on */
static uint8_t xhci_get_device_ctx_idx (struct usb_endpoint_desc* endpoint);
static void xhci_write8 (uintptr_t base, uint32_t reg, uint8_t value) {
*(volatile uint8_t*)(base + reg) = value;
}
static void xhci_write16 (uintptr_t base, uint32_t reg, uint16_t value) {
*(volatile uint16_t*)(base + reg) = value;
}
static void xhci_write32 (uintptr_t base, uint32_t reg, uint32_t value) {
*(volatile uint32_t*)(base + reg) = value;
}
@@ -314,10 +33,6 @@ static uint8_t xhci_read8 (uintptr_t base, uint32_t reg) {
return *(volatile uint8_t*)(base + reg);
}
static uint16_t xhci_read16 (uintptr_t base, uint32_t reg) {
return *(volatile uint16_t*)(base + reg);
}
static uint32_t xhci_read32 (uintptr_t base, uint32_t reg) {
return *(volatile uint32_t*)(base + reg);
}
@@ -347,12 +62,15 @@ static void xhci_create_usb_device (struct xhci* xhci, struct xhci_port* xhci_po
list_append (xhci->xhci_usb_devices, &usb_device->usb_devices_link);
}
static void xhci_delete_usb_device (struct xhci* xhci, struct xhci_port* xhci_port) {
struct xhci_usb_device* usb_device;
static void xhci_delete_usb_device (struct xhci* xhci, struct xhci_port* xhci_port,
struct proc* proc, struct reschedule_ctx* rctx) {
struct xhci_usb_device* usb_device = NULL;
list_find (struct xhci_usb_device, xhci->xhci_usb_devices, usb_device, xhci_port->port_value,
xhci_port->port_value, usb_devices_link);
device_delete (usb_device->device->key, proc, rctx);
list_remove (xhci->xhci_usb_devices, &usb_device->usb_devices_link);
if (usb_device->endpoint0_ring.phys != 0)
@@ -1186,20 +904,24 @@ static bool xhci_usb_device_setup_get_config_full (struct xhci* xhci,
static void xhci_poll_setup_init_ifs (struct xhci* xhci, struct xhci_usb_device* usb_device,
uint64_t* lockflags) {
struct reschedule_ctx rctx;
memset (&rctx, 0, sizeof (rctx));
for (size_t if0 = 0; if0 < usb_device->ifs_count; if0++) {
struct xhci_usb_device_if* if_ = &usb_device->ifs[if0];
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", if_->desc.if_class, info->if_class, if_->desc.if_subclass,
info->if_subclass, if_->desc.if_proto, info->if_proto);
if (if_->desc.if_class == info->if_class &&
if_->desc.if_subclass == info->if_subclass &&
if_->desc.if_proto == info->if_proto) {
if (!info->init ())
struct device* device = info->init (xhci, usb_device, thiscpu->kproc, &rctx, lockflags);
if (device == NULL)
DEBUG ("USB driver failed to initialize. Skipping device!\n");
usb_device->device = device;
}
}
}
@@ -1227,7 +949,8 @@ static void xhci_poll_setup_devices (struct xhci* xhci, uint64_t* lockflags) {
}
}
static void xhci_poll_port_changes (struct xhci* xhci) {
static void xhci_poll_port_changes (struct xhci* xhci, struct proc* proc,
struct reschedule_ctx* rctx) {
struct list_node_link *port_change_link, *tmp_port_change_link;
struct xhci_port* xhci_port;
@@ -1248,7 +971,7 @@ static void xhci_poll_port_changes (struct xhci* xhci) {
} else {
DEBUG ("Device detached from port %u!\n", change->port);
xhci_delete_usb_device (xhci, xhci_port);
xhci_delete_usb_device (xhci, xhci_port, proc, rctx);
}
}
@@ -1256,10 +979,83 @@ static void xhci_poll_port_changes (struct xhci* xhci) {
}
}
int xhci_bulk_transfer (struct xhci* xhci, struct xhci_usb_device* usb_device,
uint8_t endpoint_addr, uintptr_t buffer_phys, size_t buffer_size,
uint64_t* lockflags) {
struct xhci_usb_device_endpoint* endpoint = NULL;
for (size_t ep = 0; ep < XHCI_USB_DEVICE_ENDPOINTS_MAX; ep++) {
endpoint = &usb_device->endpoints[ep];
if (endpoint->desc.endpoint_addr == endpoint_addr)
break;
}
if (endpoint == NULL)
return -ST_NOT_FOUND;
/* not bulk */
if ((endpoint->desc.attrs & 0x03) != 0x02)
return -ST_NOT_FOUND;
if (usb_device->slot_id < 0)
return -ST_NOT_FOUND;
xhci->last_cmpl_code = 0;
atomic_store (&xhci->pending, true);
uint32_t rem = buffer_size;
uint32_t current = buffer_phys;
while (rem > 0) {
uint32_t chunk = rem > 0x1FFFFU ? 0x1FFFFU : rem;
struct xhci_trb trb;
memset (&trb, 0, sizeof (trb));
trb.param = current;
trb.status = chunk;
write_memory_barrier ();
trb.ctrl = (XHCI_TRB_NORMAL << XHCI_GTRB_TRB_TYPE);
if (rem != chunk) {
trb.ctrl |= (1 << XHCI_DSTRB_CTRL_CH);
} else {
trb.ctrl |= (1 << XHCI_DSTRB_CTRL_IOC) | (1 << XHCI_DSTRB_CTRL_ISP);
}
xhci_ring_put_trb (&endpoint->transfer_ring, trb);
current += chunk;
rem -= chunk;
}
uint32_t dci = xhci_get_device_ctx_idx (&endpoint->desc);
int timeout = 100;
spin_unlock (&xhci->device->lock, *lockflags);
xhci_write32 (xhci->xhci_doorbell_base, usb_device->slot_id * 4, dci);
while (atomic_load (&xhci->pending) && --timeout > 0)
stall_ms (10);
spin_lock (&xhci->device->lock, lockflags);
if (timeout == 0) {
DEBUG ("bulk transfer timed out\n");
return -ST_TRY_AGAIN;
}
return xhci->last_cmpl_code == 1 ? ST_OK : -ST_USB_CTRL_ERROR;
}
DEFINE_DEVICE_OP (xhci_poll_driver) {
struct xhci* xhci = device->udata;
xhci_poll_port_changes (xhci);
xhci_poll_port_changes (xhci, proc, rctx);
xhci_poll_setup_devices (xhci, lockflags);
return ST_OK;
@@ -1436,7 +1232,7 @@ DEFINE_DEVICE_FINI (xhci_fini) {
list_remove (xhci->xhci_ports, &port->ports_link);
xhci_delete_usb_device (xhci, port);
xhci_delete_usb_device (xhci, port, proc, rctx);
free (port);
}