|
|
|
|
@@ -14,6 +14,7 @@
|
|
|
|
|
#include <proc/reschedule.h>
|
|
|
|
|
#include <proc/suspension_q.h>
|
|
|
|
|
#include <status.h>
|
|
|
|
|
#include <sync/biglock.h>
|
|
|
|
|
#include <sys/debug.h>
|
|
|
|
|
#include <sys/memorybarrier.h>
|
|
|
|
|
#include <sys/spin_lock.h>
|
|
|
|
|
@@ -324,12 +325,8 @@ static void xhci_poll_events(struct xhci* xhci) {
|
|
|
|
|
static void xhci_irq(void* arg, void* regs, bool user, struct reschedule_ctx* rctx) {
|
|
|
|
|
(void)user, (void)regs, (void)rctx;
|
|
|
|
|
|
|
|
|
|
uint64_t fd;
|
|
|
|
|
|
|
|
|
|
struct xhci* xhci = arg;
|
|
|
|
|
|
|
|
|
|
spin_lock(&xhci->device->lock, &fd);
|
|
|
|
|
|
|
|
|
|
uintptr_t ir_base = xhci->xhci_runtime_base + 0x20;
|
|
|
|
|
|
|
|
|
|
/* ack */
|
|
|
|
|
@@ -337,8 +334,6 @@ static void xhci_irq(void* arg, void* regs, bool user, struct reschedule_ctx* rc
|
|
|
|
|
xhci_write32(xhci->xhci_oper_base, XHCI_USBSTS, (1 << 3));
|
|
|
|
|
|
|
|
|
|
xhci_poll_events(xhci);
|
|
|
|
|
|
|
|
|
|
spin_unlock(&xhci->device->lock, fd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void xhci_ring_put_trb(struct xhci_ring* ring, struct xhci_trb put_trb) {
|
|
|
|
|
@@ -369,8 +364,7 @@ static void xhci_ring_put_trb(struct xhci_ring* ring, struct xhci_trb put_trb) {
|
|
|
|
|
|
|
|
|
|
static bool xhci_endpoint0_ctrl_in(struct xhci* xhci, struct xhci_usb_device* usb_device,
|
|
|
|
|
uint8_t request_type, uint8_t request, uint16_t value,
|
|
|
|
|
uint16_t index, uintptr_t data_phys, uint16_t length,
|
|
|
|
|
uint64_t* lockflags) {
|
|
|
|
|
uint16_t index, uintptr_t data_phys, uint16_t length) {
|
|
|
|
|
/* clang-format off */
|
|
|
|
|
uint64_t setup = ((uint64_t)length << XHCI_SSTRB_PARAM_WLENGTH)
|
|
|
|
|
| ((uint64_t)index << XHCI_SSTRB_PARAM_WINDEX)
|
|
|
|
|
@@ -417,14 +411,15 @@ static bool xhci_endpoint0_ctrl_in(struct xhci* xhci, struct xhci_usb_device* us
|
|
|
|
|
|
|
|
|
|
int timeout = 100;
|
|
|
|
|
|
|
|
|
|
spin_unlock(&xhci->device->lock, *lockflags);
|
|
|
|
|
|
|
|
|
|
xhci_write32(xhci->xhci_doorbell_base, usb_device->slot_id * 4, 1);
|
|
|
|
|
|
|
|
|
|
while (atomic_load(&usb_device->endpoint0_ring.pending) && --timeout > 0)
|
|
|
|
|
while (atomic_load(&usb_device->endpoint0_ring.pending) && --timeout > 0) {
|
|
|
|
|
biglock_unlock();
|
|
|
|
|
|
|
|
|
|
stall_ms(1);
|
|
|
|
|
|
|
|
|
|
spin_lock(&xhci->device->lock, lockflags);
|
|
|
|
|
biglock_lock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (timeout == 0) {
|
|
|
|
|
DEBUG("timed out\n");
|
|
|
|
|
@@ -436,8 +431,7 @@ static bool xhci_endpoint0_ctrl_in(struct xhci* xhci, struct xhci_usb_device* us
|
|
|
|
|
|
|
|
|
|
static bool xhci_endpoint0_ctrl_out(struct xhci* xhci, struct xhci_usb_device* usb_device,
|
|
|
|
|
uint8_t request_type, uint8_t request, uint16_t value,
|
|
|
|
|
uint16_t index, uintptr_t data_phys, uint16_t length,
|
|
|
|
|
uint64_t* lockflags) {
|
|
|
|
|
uint16_t index, uintptr_t data_phys, uint16_t length) {
|
|
|
|
|
/* clang-format off */
|
|
|
|
|
uint64_t setup = ((uint64_t)length << XHCI_SSTRB_PARAM_WLENGTH)
|
|
|
|
|
| ((uint64_t)index << XHCI_SSTRB_PARAM_WINDEX)
|
|
|
|
|
@@ -493,14 +487,15 @@ static bool xhci_endpoint0_ctrl_out(struct xhci* xhci, struct xhci_usb_device* u
|
|
|
|
|
|
|
|
|
|
int timeout = 100;
|
|
|
|
|
|
|
|
|
|
spin_unlock(&xhci->device->lock, *lockflags);
|
|
|
|
|
|
|
|
|
|
xhci_write32(xhci->xhci_doorbell_base, usb_device->slot_id * 4, 1);
|
|
|
|
|
|
|
|
|
|
while (atomic_load(&usb_device->endpoint0_ring.pending) && --timeout > 0)
|
|
|
|
|
while (atomic_load(&usb_device->endpoint0_ring.pending) && --timeout > 0) {
|
|
|
|
|
biglock_unlock();
|
|
|
|
|
|
|
|
|
|
stall_ms(1);
|
|
|
|
|
|
|
|
|
|
spin_lock(&xhci->device->lock, lockflags);
|
|
|
|
|
biglock_lock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (timeout == 0) {
|
|
|
|
|
DEBUG("timed out\n");
|
|
|
|
|
@@ -510,8 +505,7 @@ static bool xhci_endpoint0_ctrl_out(struct xhci* xhci, struct xhci_usb_device* u
|
|
|
|
|
return usb_device->endpoint0_ring.last_cmpl_code == 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void xhci_send_cmd(struct xhci* xhci, uint64_t param, uint32_t status, uint32_t ctrl,
|
|
|
|
|
uint64_t* lockflags) {
|
|
|
|
|
static void xhci_send_cmd(struct xhci* xhci, uint64_t param, uint32_t status, uint32_t ctrl) {
|
|
|
|
|
if (xhci->cmd_ring.idx == (xhci->cmd_ring.size - 1)) {
|
|
|
|
|
struct xhci_trb* link = &xhci->cmd_ring.trbs[xhci->cmd_ring.idx];
|
|
|
|
|
link->param = xhci->cmd_ring.phys;
|
|
|
|
|
@@ -541,14 +535,15 @@ static void xhci_send_cmd(struct xhci* xhci, uint64_t param, uint32_t status, ui
|
|
|
|
|
|
|
|
|
|
int timeout = 100;
|
|
|
|
|
|
|
|
|
|
spin_unlock(&xhci->device->lock, *lockflags);
|
|
|
|
|
|
|
|
|
|
xhci_write32(xhci->xhci_doorbell_base, 0, 0);
|
|
|
|
|
|
|
|
|
|
while (atomic_load(&xhci->event_ring.pending) && --timeout > 0)
|
|
|
|
|
while (atomic_load(&xhci->event_ring.pending) && --timeout > 0) {
|
|
|
|
|
biglock_unlock();
|
|
|
|
|
|
|
|
|
|
stall_ms(1);
|
|
|
|
|
|
|
|
|
|
spin_lock(&xhci->device->lock, lockflags);
|
|
|
|
|
biglock_lock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (timeout == 0)
|
|
|
|
|
DEBUG("timed out\n");
|
|
|
|
|
@@ -561,8 +556,7 @@ static uint8_t xhci_get_device_ctx_idx(struct usb_endpoint_desc* endpoint) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool xhci_usb_device_setup_init_endpoints(struct xhci* xhci,
|
|
|
|
|
struct xhci_usb_device* usb_device,
|
|
|
|
|
uint64_t* lockflags) {
|
|
|
|
|
struct xhci_usb_device* usb_device) {
|
|
|
|
|
struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
|
|
|
|
|
|
|
|
|
|
uintptr_t input_ctx_phys = pmm_alloc(1);
|
|
|
|
|
@@ -682,7 +676,7 @@ static bool xhci_usb_device_setup_init_endpoints(struct xhci* xhci,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t ctrl = (usb_device->slot_id << 24) | (XHCI_TRB_CFG_ENDP_CMD << XHCI_GTRB_TRB_TYPE);
|
|
|
|
|
xhci_send_cmd(xhci, input_ctx_phys, 0, ctrl, lockflags);
|
|
|
|
|
xhci_send_cmd(xhci, input_ctx_phys, 0, ctrl);
|
|
|
|
|
|
|
|
|
|
pmm_free(input_ctx_phys, 1);
|
|
|
|
|
|
|
|
|
|
@@ -696,8 +690,8 @@ static bool xhci_usb_device_setup_init_endpoints(struct xhci* xhci,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool xhci_usb_device_setup_set_config(struct xhci* xhci, struct xhci_usb_device* usb_device,
|
|
|
|
|
uint8_t config_val, uint64_t* lockflags) {
|
|
|
|
|
bool ok = xhci_endpoint0_ctrl_out(xhci, usb_device, 0x00, 0x09, config_val, 0, 0, 0, lockflags);
|
|
|
|
|
uint8_t config_val) {
|
|
|
|
|
bool ok = xhci_endpoint0_ctrl_out(xhci, usb_device, 0x00, 0x09, config_val, 0, 0, 0);
|
|
|
|
|
|
|
|
|
|
if (!ok) {
|
|
|
|
|
DEBUG("Failed to set the config!\n");
|
|
|
|
|
@@ -709,8 +703,8 @@ static bool xhci_usb_device_setup_set_config(struct xhci* xhci, struct xhci_usb_
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool xhci_usb_device_setup_addressing(struct xhci* xhci, struct xhci_usb_device* usb_device,
|
|
|
|
|
uint64_t* lockflags) {
|
|
|
|
|
static bool xhci_usb_device_setup_addressing(struct xhci* xhci,
|
|
|
|
|
struct xhci_usb_device* usb_device) {
|
|
|
|
|
struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
|
|
|
|
|
|
|
|
|
|
struct xhci_port* xhci_port;
|
|
|
|
|
@@ -722,7 +716,7 @@ static bool xhci_usb_device_setup_addressing(struct xhci* xhci, struct xhci_usb_
|
|
|
|
|
uint32_t speed = (portsc >> XHCI_PORTSC_PORTSPEED) & 0x0F;
|
|
|
|
|
|
|
|
|
|
xhci->event_ring.last_cmpl_code = 0;
|
|
|
|
|
xhci_send_cmd(xhci, 0, 0, XHCI_TRB_SLOT_ENAB_CMD << XHCI_GTRB_TRB_TYPE, lockflags);
|
|
|
|
|
xhci_send_cmd(xhci, 0, 0, XHCI_TRB_SLOT_ENAB_CMD << XHCI_GTRB_TRB_TYPE);
|
|
|
|
|
|
|
|
|
|
if (xhci->event_ring.last_cmpl_code != 1) {
|
|
|
|
|
DEBUG("Enable slot failed\n");
|
|
|
|
|
@@ -784,7 +778,7 @@ static bool xhci_usb_device_setup_addressing(struct xhci* xhci, struct xhci_usb_
|
|
|
|
|
|
|
|
|
|
xhci->event_ring.last_cmpl_code = 0;
|
|
|
|
|
uint32_t ctrl = (usb_device->slot_id << 24) | (XHCI_TRB_ADDR_DEV_CMD << XHCI_GTRB_TRB_TYPE);
|
|
|
|
|
xhci_send_cmd(xhci, input_ctx_phys, 0, ctrl, lockflags);
|
|
|
|
|
xhci_send_cmd(xhci, input_ctx_phys, 0, ctrl);
|
|
|
|
|
|
|
|
|
|
pmm_free(input_ctx_phys, 1);
|
|
|
|
|
|
|
|
|
|
@@ -799,8 +793,7 @@ static bool xhci_usb_device_setup_addressing(struct xhci* xhci, struct xhci_usb_
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool xhci_usb_device_setup_get_info(struct xhci* xhci, struct xhci_usb_device* usb_device,
|
|
|
|
|
uint64_t* lockflags) {
|
|
|
|
|
static bool xhci_usb_device_setup_get_info(struct xhci* xhci, struct xhci_usb_device* usb_device) {
|
|
|
|
|
struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
|
|
|
|
|
|
|
|
|
|
uintptr_t response_buf_phys = pmm_alloc(1);
|
|
|
|
|
@@ -808,8 +801,7 @@ static bool xhci_usb_device_setup_get_info(struct xhci* xhci, struct xhci_usb_de
|
|
|
|
|
void* response_buf = (void*)(response_buf_phys + (uintptr_t)hhdm->offset);
|
|
|
|
|
memset(response_buf, 0, PAGE_SIZE);
|
|
|
|
|
|
|
|
|
|
bool ok = xhci_endpoint0_ctrl_in(xhci, usb_device, 0x80, 6, (1 << 8), 0, response_buf_phys, 18,
|
|
|
|
|
lockflags);
|
|
|
|
|
bool ok = xhci_endpoint0_ctrl_in(xhci, usb_device, 0x80, 6, (1 << 8), 0, response_buf_phys, 18);
|
|
|
|
|
|
|
|
|
|
if (!ok) {
|
|
|
|
|
pmm_free(response_buf_phys, 1);
|
|
|
|
|
@@ -828,8 +820,8 @@ static bool xhci_usb_device_setup_get_info(struct xhci* xhci, struct xhci_usb_de
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool xhci_usb_device_setup_get_config(struct xhci* xhci, struct xhci_usb_device* usb_device,
|
|
|
|
|
uint64_t* lockflags) {
|
|
|
|
|
static bool xhci_usb_device_setup_get_config(struct xhci* xhci,
|
|
|
|
|
struct xhci_usb_device* usb_device) {
|
|
|
|
|
struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
|
|
|
|
|
|
|
|
|
|
uintptr_t response_buf_phys = pmm_alloc(1);
|
|
|
|
|
@@ -837,8 +829,7 @@ static bool xhci_usb_device_setup_get_config(struct xhci* xhci, struct xhci_usb_
|
|
|
|
|
void* response_buf = (void*)(response_buf_phys + (uintptr_t)hhdm->offset);
|
|
|
|
|
memset(response_buf, 0, PAGE_SIZE);
|
|
|
|
|
|
|
|
|
|
bool ok = xhci_endpoint0_ctrl_in(xhci, usb_device, 0x80, 6, (2 << 8), 0, response_buf_phys, 9,
|
|
|
|
|
lockflags);
|
|
|
|
|
bool ok = xhci_endpoint0_ctrl_in(xhci, usb_device, 0x80, 6, (2 << 8), 0, response_buf_phys, 9);
|
|
|
|
|
|
|
|
|
|
if (!ok) {
|
|
|
|
|
pmm_free(response_buf_phys, 1);
|
|
|
|
|
@@ -858,8 +849,7 @@ static bool xhci_usb_device_setup_get_config(struct xhci* xhci, struct xhci_usb_
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool xhci_usb_device_setup_get_config_full(struct xhci* xhci,
|
|
|
|
|
struct xhci_usb_device* usb_device,
|
|
|
|
|
uint64_t* lockflags) {
|
|
|
|
|
struct xhci_usb_device* usb_device) {
|
|
|
|
|
struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
|
|
|
|
|
|
|
|
|
|
uintptr_t response_buf_phys = pmm_alloc(1);
|
|
|
|
|
@@ -868,7 +858,7 @@ static bool xhci_usb_device_setup_get_config_full(struct xhci* xhci,
|
|
|
|
|
memset(response_buf, 0, PAGE_SIZE);
|
|
|
|
|
|
|
|
|
|
bool ok = xhci_endpoint0_ctrl_in(xhci, usb_device, 0x80, 6, (2 << 8), 0, response_buf_phys,
|
|
|
|
|
usb_device->config_desc.total_length, lockflags);
|
|
|
|
|
usb_device->config_desc.total_length);
|
|
|
|
|
|
|
|
|
|
if (!ok) {
|
|
|
|
|
pmm_free(response_buf_phys, 1);
|
|
|
|
|
@@ -927,8 +917,7 @@ static bool xhci_usb_device_setup_get_config_full(struct xhci* xhci,
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void xhci_poll_setup_init_ifs(struct xhci* xhci, struct xhci_usb_device* usb_device,
|
|
|
|
|
uint64_t* lockflags) {
|
|
|
|
|
static void xhci_poll_setup_init_ifs(struct xhci* xhci, struct xhci_usb_device* usb_device) {
|
|
|
|
|
struct reschedule_ctx rctx;
|
|
|
|
|
memset(&rctx, 0, sizeof(rctx));
|
|
|
|
|
|
|
|
|
|
@@ -941,11 +930,7 @@ static void xhci_poll_setup_init_ifs(struct xhci* xhci, struct xhci_usb_device*
|
|
|
|
|
if (if_->desc.if_class == info->if_class &&
|
|
|
|
|
if_->desc.if_subclass == info->if_subclass &&
|
|
|
|
|
if_->desc.if_proto == info->if_proto) {
|
|
|
|
|
spin_unlock(&xhci->device->lock, *lockflags);
|
|
|
|
|
|
|
|
|
|
struct device* device = info->init(xhci, usb_device, thiscpu->kproc, &rctx, lockflags);
|
|
|
|
|
|
|
|
|
|
spin_lock(&xhci->device->lock, lockflags);
|
|
|
|
|
struct device* device = info->init(xhci, usb_device, thiscpu->kproc, &rctx);
|
|
|
|
|
|
|
|
|
|
if (device == NULL)
|
|
|
|
|
DEBUG("USB driver failed to initialize. Skipping device!\n");
|
|
|
|
|
@@ -956,7 +941,7 @@ static void xhci_poll_setup_init_ifs(struct xhci* xhci, struct xhci_usb_device*
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void xhci_poll_setup_devices(struct xhci* xhci, uint64_t* lockflags) {
|
|
|
|
|
static void xhci_poll_setup_devices(struct xhci* xhci) {
|
|
|
|
|
struct list_node_link *usb_device_link, *tmp_usb_device_link;
|
|
|
|
|
bool ok;
|
|
|
|
|
|
|
|
|
|
@@ -968,26 +953,25 @@ static void xhci_poll_setup_devices(struct xhci* xhci, uint64_t* lockflags) {
|
|
|
|
|
if (usb_device->slot_id != -1)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
ok = xhci_usb_device_setup_addressing(xhci, usb_device, lockflags);
|
|
|
|
|
ok = xhci_usb_device_setup_addressing(xhci, usb_device);
|
|
|
|
|
|
|
|
|
|
if (ok)
|
|
|
|
|
ok = xhci_usb_device_setup_get_info(xhci, usb_device, lockflags);
|
|
|
|
|
ok = xhci_usb_device_setup_get_info(xhci, usb_device);
|
|
|
|
|
|
|
|
|
|
if (ok)
|
|
|
|
|
ok = xhci_usb_device_setup_get_config(xhci, usb_device, lockflags);
|
|
|
|
|
ok = xhci_usb_device_setup_get_config(xhci, usb_device);
|
|
|
|
|
|
|
|
|
|
if (ok)
|
|
|
|
|
ok = xhci_usb_device_setup_get_config_full(xhci, usb_device, lockflags);
|
|
|
|
|
ok = xhci_usb_device_setup_get_config_full(xhci, usb_device);
|
|
|
|
|
|
|
|
|
|
if (ok)
|
|
|
|
|
ok = xhci_usb_device_setup_set_config(xhci, usb_device, usb_device->config_desc.config_value,
|
|
|
|
|
lockflags);
|
|
|
|
|
ok = xhci_usb_device_setup_set_config(xhci, usb_device, usb_device->config_desc.config_value);
|
|
|
|
|
|
|
|
|
|
if (ok)
|
|
|
|
|
ok = xhci_usb_device_setup_init_endpoints(xhci, usb_device, lockflags);
|
|
|
|
|
ok = xhci_usb_device_setup_init_endpoints(xhci, usb_device);
|
|
|
|
|
|
|
|
|
|
if (ok)
|
|
|
|
|
xhci_poll_setup_init_ifs(xhci, usb_device, lockflags);
|
|
|
|
|
xhci_poll_setup_init_ifs(xhci, usb_device);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1022,7 +1006,7 @@ static void xhci_poll_port_changes(struct xhci* xhci, struct proc* proc,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
uintptr_t buffer_phys, size_t buffer_size) {
|
|
|
|
|
struct xhci_usb_device_endpoint* endpoint = NULL;
|
|
|
|
|
|
|
|
|
|
for (size_t ep = 0; ep < usb_device->endpoints_count; ep++) {
|
|
|
|
|
@@ -1076,14 +1060,15 @@ int xhci_bulk_transfer(struct xhci* xhci, struct xhci_usb_device* usb_device, ui
|
|
|
|
|
|
|
|
|
|
int timeout = 100;
|
|
|
|
|
|
|
|
|
|
spin_unlock(&xhci->device->lock, *lockflags);
|
|
|
|
|
|
|
|
|
|
xhci_write32(xhci->xhci_doorbell_base, usb_device->slot_id * 4, dci);
|
|
|
|
|
|
|
|
|
|
while (atomic_load(&endpoint->transfer_ring.pending) && --timeout > 0)
|
|
|
|
|
while (atomic_load(&endpoint->transfer_ring.pending) && --timeout > 0) {
|
|
|
|
|
biglock_unlock();
|
|
|
|
|
|
|
|
|
|
stall_ms(1);
|
|
|
|
|
|
|
|
|
|
spin_lock(&xhci->device->lock, lockflags);
|
|
|
|
|
biglock_lock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (timeout == 0) {
|
|
|
|
|
DEBUG("bulk transfer timed out\n");
|
|
|
|
|
@@ -1097,7 +1082,7 @@ DEFINE_DEVICE_OP(xhci_poll_driver) {
|
|
|
|
|
struct xhci* xhci = device->udata;
|
|
|
|
|
|
|
|
|
|
xhci_poll_port_changes(xhci, proc, rctx);
|
|
|
|
|
xhci_poll_setup_devices(xhci, lockflags);
|
|
|
|
|
xhci_poll_setup_devices(xhci);
|
|
|
|
|
|
|
|
|
|
return ST_OK;
|
|
|
|
|
}
|
|
|
|
|
|