|
|
|
|
@@ -3,6 +3,7 @@
|
|
|
|
|
#include <device/usb/usb.h>
|
|
|
|
|
#include <device/usb/xhci.h>
|
|
|
|
|
#include <irq/irq.h>
|
|
|
|
|
#include <libk/align.h>
|
|
|
|
|
#include <libk/list.h>
|
|
|
|
|
#include <libk/std.h>
|
|
|
|
|
#include <libk/string.h>
|
|
|
|
|
@@ -189,6 +190,11 @@
|
|
|
|
|
#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
|
|
|
|
|
@@ -365,31 +371,33 @@ static void xhci_port_reset (struct xhci* xhci, uint8_t port) {
|
|
|
|
|
stall_ms (100);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void xhci_create_pdevice (struct xhci* xhci, uint8_t port) {
|
|
|
|
|
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, port_value, port, pdevices_link);
|
|
|
|
|
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->port_value = port;
|
|
|
|
|
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, uint8_t port) {
|
|
|
|
|
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, port_value, port, pdevices_link);
|
|
|
|
|
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);
|
|
|
|
|
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;
|
|
|
|
|
@@ -431,10 +439,12 @@ static void xhci_event_dispatch (struct xhci* xhci, struct xhci_trb* event, uint
|
|
|
|
|
|
|
|
|
|
DEBUG ("transfer completion: code=%u,slot=%u,endpoint_id=%u\n", cmpl_code, slot_id,
|
|
|
|
|
endpoint_id);
|
|
|
|
|
|
|
|
|
|
xhci->last_slot_id = slot_id;
|
|
|
|
|
xhci->last_cmpl_code = cmpl_code;
|
|
|
|
|
} break;
|
|
|
|
|
default:
|
|
|
|
|
DEBUG ("Unhandled event type %u at %u\n", type, xhci->event_ring_idx);
|
|
|
|
|
DEBUG ("Unhandled event type %u at %u\n", type, xhci->event_ring.idx);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -444,9 +454,9 @@ static void xhci_poll_events (struct xhci* xhci) {
|
|
|
|
|
|
|
|
|
|
bool serviced = false;
|
|
|
|
|
for (;;) {
|
|
|
|
|
struct xhci_trb* event = &xhci->event_ring[xhci->event_ring_idx];
|
|
|
|
|
struct xhci_trb* event = &xhci->event_ring.trbs[xhci->event_ring.idx];
|
|
|
|
|
|
|
|
|
|
if ((event->ctrl & (1 << XHCI_GTRB_CYCLE_BIT)) != xhci->event_cycle_bit) {
|
|
|
|
|
if ((event->ctrl & (1 << XHCI_GTRB_CYCLE_BIT)) != xhci->event_ring.cycle_bit) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -456,17 +466,17 @@ static void xhci_poll_events (struct xhci* xhci) {
|
|
|
|
|
|
|
|
|
|
xhci_event_dispatch (xhci, event, type);
|
|
|
|
|
|
|
|
|
|
xhci->event_ring_idx++;
|
|
|
|
|
xhci->event_ring.idx++;
|
|
|
|
|
|
|
|
|
|
if (xhci->event_ring_idx >= xhci->event_ring_size) {
|
|
|
|
|
xhci->event_ring_idx = 0;
|
|
|
|
|
xhci->event_cycle_bit ^= 1;
|
|
|
|
|
if (xhci->event_ring.idx >= xhci->event_ring.size) {
|
|
|
|
|
xhci->event_ring.idx = 0;
|
|
|
|
|
xhci->event_ring.cycle_bit ^= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (serviced) {
|
|
|
|
|
uintptr_t dequeue_ptr =
|
|
|
|
|
xhci->event_ring_phys + (xhci->event_ring_idx * sizeof (struct xhci_trb));
|
|
|
|
|
xhci->event_ring.phys + (xhci->event_ring.idx * sizeof (struct xhci_trb));
|
|
|
|
|
|
|
|
|
|
xhci_write32 (ir_base, XHCI_ERDP, (uint32_t)dequeue_ptr | (1 << 3));
|
|
|
|
|
xhci_write32 (ir_base, XHCI_ERDP + 4, (uint32_t)(dequeue_ptr >> 32));
|
|
|
|
|
@@ -495,25 +505,25 @@ static void xhci_irq (void* arg, void* regs, bool user, struct reschedule_ctx* r
|
|
|
|
|
spin_unlock (&xhci->device->lock, fd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void xhci_endpoint0_put_trb (struct xhci_pdevice* pdevice, struct xhci_trb trb) {
|
|
|
|
|
if (pdevice->endpoint0_ring_idx == pdevice->endpoint0_ring_size - 1) {
|
|
|
|
|
struct xhci_trb* link = &pdevice->endpoint0_ring[pdevice->endpoint0_ring_idx];
|
|
|
|
|
link->param = pdevice->endpoint0_ring_phys;
|
|
|
|
|
static void xhci_endpoint0_put_trb (struct xhci_pdevice* pdevice, struct xhci_trb put_trb) {
|
|
|
|
|
if (pdevice->endpoint0_ring.idx == pdevice->endpoint0_ring.size - 1) {
|
|
|
|
|
struct xhci_trb* link = &pdevice->endpoint0_ring.trbs[pdevice->endpoint0_ring.idx];
|
|
|
|
|
link->param = pdevice->endpoint0_ring.phys;
|
|
|
|
|
link->status = 0;
|
|
|
|
|
link->ctrl = (XHCI_TRB_LINK << XHCI_LNKTRB_CTRL_TRB_TYPE) |
|
|
|
|
|
(1 << XHCI_LNKTRB_CTRL_TC) |
|
|
|
|
|
pdevice->endpoint0_cycle_bit;
|
|
|
|
|
pdevice->endpoint0_ring.cycle_bit;
|
|
|
|
|
|
|
|
|
|
pdevice->endpoint0_ring_idx = 0;
|
|
|
|
|
pdevice->endpoint0_cycle_bit ^= 1;
|
|
|
|
|
pdevice->endpoint0_ring.idx = 0;
|
|
|
|
|
pdevice->endpoint0_ring.cycle_bit ^= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pdevice->endpoint0_ring[pdevice->endpoint0_ring_idx] = trb;
|
|
|
|
|
pdevice->endpoint0_ring[pdevice->endpoint0_ring_idx].ctrl =
|
|
|
|
|
(pdevice->endpoint0_ring[pdevice->endpoint0_ring_idx].ctrl & ~1u) |
|
|
|
|
|
pdevice->endpoint0_cycle_bit;
|
|
|
|
|
struct xhci_trb* trb = &pdevice->endpoint0_ring.trbs[pdevice->endpoint0_ring.idx];
|
|
|
|
|
trb->param = put_trb.param;
|
|
|
|
|
trb->status = put_trb.status;
|
|
|
|
|
trb->ctrl = (put_trb.ctrl & ~XHCI_LNKTRB_CTRL_TC) | pdevice->endpoint0_ring.cycle_bit;
|
|
|
|
|
|
|
|
|
|
pdevice->endpoint0_ring_idx++;
|
|
|
|
|
pdevice->endpoint0_ring.idx++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool xhci_endpoint0_ctrl_in (struct xhci* xhci, struct xhci_pdevice* pdevice,
|
|
|
|
|
@@ -530,23 +540,20 @@ static bool xhci_endpoint0_ctrl_in (struct xhci* xhci, struct xhci_pdevice* pdev
|
|
|
|
|
|
|
|
|
|
struct xhci_trb trb;
|
|
|
|
|
|
|
|
|
|
xhci->last_cmpl_code = 0;
|
|
|
|
|
atomic_store (&xhci->pending, true);
|
|
|
|
|
|
|
|
|
|
/* setup stage */
|
|
|
|
|
memset (&trb, 0, sizeof (trb));
|
|
|
|
|
trb.param = setup;
|
|
|
|
|
trb.status = 8;
|
|
|
|
|
trb.ctrl =
|
|
|
|
|
(XHCI_TRB_SETUP_STAGE << XHCI_SSTRB_CTRL_TRB_TYPE) | (1 << XHCI_SSTRB_CTRL_IDT) | (1 << 4);
|
|
|
|
|
trb.ctrl = (XHCI_TRB_SETUP_STAGE << XHCI_SSTRB_CTRL_TRB_TYPE) |
|
|
|
|
|
(1 << XHCI_SSTRB_CTRL_IDT) |
|
|
|
|
|
(XHCI_SSTRB_TRT_IN_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;
|
|
|
|
|
trb.ctrl =
|
|
|
|
|
(XHCI_TRB_DATA_STAGE << XHCI_DSTRB_CTRL_TRB_TYPE) | (1 << XHCI_DSTRB_CTRL_DIR) | (1 << 4);
|
|
|
|
|
trb.ctrl = (XHCI_TRB_DATA_STAGE << XHCI_DSTRB_CTRL_TRB_TYPE) | (1 << XHCI_DSTRB_CTRL_DIR);
|
|
|
|
|
xhci_endpoint0_put_trb (pdevice, trb);
|
|
|
|
|
|
|
|
|
|
/* status stage */
|
|
|
|
|
@@ -556,12 +563,14 @@ static bool xhci_endpoint0_ctrl_in (struct xhci* xhci, struct xhci_pdevice* pdev
|
|
|
|
|
trb.ctrl = (XHCI_TRB_STATUS_STAGE << XHCI_STSTRB_CTRL_TRB_TYPE) | (1 << XHCI_STSTRB_CTRL_IOC);
|
|
|
|
|
xhci_endpoint0_put_trb (pdevice, trb);
|
|
|
|
|
|
|
|
|
|
xhci_write32 (xhci->xhci_doorbell_base, pdevice->slot_id * 4, 1);
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
@@ -577,47 +586,40 @@ static bool xhci_endpoint0_ctrl_in (struct xhci* xhci, struct xhci_pdevice* pdev
|
|
|
|
|
|
|
|
|
|
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];
|
|
|
|
|
link->param = xhci->cmd_ring_phys;
|
|
|
|
|
link->status = 0;
|
|
|
|
|
link->ctrl = (XHCI_TRB_LINK << XHCI_LNKTRB_CTRL_TRB_TYPE) |
|
|
|
|
|
(1 << XHCI_LNKTRB_CTRL_TC) |
|
|
|
|
|
xhci->cmd_cycle_bit;
|
|
|
|
|
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;
|
|
|
|
|
link->status = 0;
|
|
|
|
|
link->ctrl = (XHCI_TRB_LINK << XHCI_LNKTRB_CTRL_TRB_TYPE) |
|
|
|
|
|
(1 << XHCI_LNKTRB_CTRL_TC) |
|
|
|
|
|
xhci->cmd_ring.cycle_bit;
|
|
|
|
|
|
|
|
|
|
xhci->cmd_ring_idx = 0;
|
|
|
|
|
xhci->cmd_cycle_bit ^= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct xhci_trb* trb = &xhci->cmd_ring[xhci->cmd_ring_idx];
|
|
|
|
|
trb->param = param;
|
|
|
|
|
trb->status = status;
|
|
|
|
|
trb->ctrl = (ctrl & ~XHCI_LNKTRB_CTRL_TC) | xhci->cmd_cycle_bit;
|
|
|
|
|
|
|
|
|
|
xhci->cmd_ring_idx++;
|
|
|
|
|
|
|
|
|
|
atomic_store (&xhci->pending, true);
|
|
|
|
|
|
|
|
|
|
xhci_write32 (xhci->xhci_doorbell_base, 0, 0);
|
|
|
|
|
|
|
|
|
|
int timeout = 100;
|
|
|
|
|
|
|
|
|
|
spin_unlock (&xhci->device->lock, *lockflags);
|
|
|
|
|
|
|
|
|
|
while (atomic_load (&xhci->pending) && --timeout > 0)
|
|
|
|
|
stall_ms (10);
|
|
|
|
|
|
|
|
|
|
spin_lock (&xhci->device->lock, lockflags);
|
|
|
|
|
|
|
|
|
|
if (timeout == 0)
|
|
|
|
|
DEBUG ("timed out\n");
|
|
|
|
|
} else {
|
|
|
|
|
while (atomic_load (&xhci->pending)) {
|
|
|
|
|
xhci_poll_events (xhci);
|
|
|
|
|
spin_lock_relax ();
|
|
|
|
|
}
|
|
|
|
|
xhci->cmd_ring.idx = 0;
|
|
|
|
|
xhci->cmd_ring.cycle_bit ^= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct xhci_trb* trb = &xhci->cmd_ring.trbs[xhci->cmd_ring.idx];
|
|
|
|
|
trb->param = param;
|
|
|
|
|
trb->status = status;
|
|
|
|
|
trb->ctrl = (ctrl & ~XHCI_LNKTRB_CTRL_TC) | xhci->cmd_ring.cycle_bit;
|
|
|
|
|
|
|
|
|
|
xhci->cmd_ring.idx++;
|
|
|
|
|
|
|
|
|
|
atomic_store (&xhci->pending, true);
|
|
|
|
|
|
|
|
|
|
int timeout = 100;
|
|
|
|
|
|
|
|
|
|
spin_unlock (&xhci->device->lock, *lockflags);
|
|
|
|
|
|
|
|
|
|
xhci_write32 (xhci->xhci_doorbell_base, 0, 0);
|
|
|
|
|
|
|
|
|
|
while (atomic_load (&xhci->pending) && --timeout > 0)
|
|
|
|
|
stall_ms (10);
|
|
|
|
|
|
|
|
|
|
spin_lock (&xhci->device->lock, lockflags);
|
|
|
|
|
|
|
|
|
|
if (timeout == 0)
|
|
|
|
|
DEBUG ("timed out\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void xhci_bios_handover (struct xhci* xhci) {
|
|
|
|
|
@@ -704,7 +706,7 @@ static void xhci_reset_ports (struct xhci* xhci) {
|
|
|
|
|
if ((portsc & (1 << XHCI_PORTSC_CCS))) {
|
|
|
|
|
DEBUG ("Device connected. resetting\n");
|
|
|
|
|
xhci_port_reset (xhci, port);
|
|
|
|
|
xhci_create_pdevice (xhci, port);
|
|
|
|
|
xhci_create_pdevice (xhci, xhci_port);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DEBUG ("PORT %u: USB %u.%u\n", port, major, minor);
|
|
|
|
|
@@ -724,10 +726,10 @@ static void xhci_pdevice_setup_addressing (struct xhci* xhci, struct xhci_pdevic
|
|
|
|
|
struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
|
|
|
|
|
|
|
|
|
|
struct xhci_port* xhci_port;
|
|
|
|
|
list_find (struct xhci_port, xhci->xhci_ports, xhci_port, port_value, pdevice->port_value,
|
|
|
|
|
ports_link);
|
|
|
|
|
list_find (struct xhci_port, xhci->xhci_ports, xhci_port, port_value,
|
|
|
|
|
pdevice->xhci_port->port_value, ports_link);
|
|
|
|
|
|
|
|
|
|
uint32_t portsc = xhci_portsc_read (xhci, pdevice->port_value);
|
|
|
|
|
uint32_t portsc = xhci_portsc_read (xhci, pdevice->xhci_port->port_value);
|
|
|
|
|
|
|
|
|
|
uint32_t speed = (portsc >> XHCI_PORTSC_PORTSPEED) & 0x0F;
|
|
|
|
|
|
|
|
|
|
@@ -746,13 +748,13 @@ static void xhci_pdevice_setup_addressing (struct xhci* xhci, struct xhci_pdevic
|
|
|
|
|
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);
|
|
|
|
|
pdevice->endpoint0_ring_size = PAGE_SIZE / sizeof (struct xhci_trb);
|
|
|
|
|
pdevice->endpoint0_ring_idx = 0;
|
|
|
|
|
pdevice->endpoint0_cycle_bit = 1;
|
|
|
|
|
pdevice->endpoint0_ring.phys = pmm_alloc (1);
|
|
|
|
|
pdevice->endpoint0_ring.trbs =
|
|
|
|
|
(struct xhci_trb*)(pdevice->endpoint0_ring.phys + (uintptr_t)hhdm->offset);
|
|
|
|
|
memset (pdevice->endpoint0_ring.trbs, 0, PAGE_SIZE);
|
|
|
|
|
pdevice->endpoint0_ring.size = PAGE_SIZE / sizeof (struct xhci_trb);
|
|
|
|
|
pdevice->endpoint0_ring.idx = 0;
|
|
|
|
|
pdevice->endpoint0_ring.cycle_bit = 1;
|
|
|
|
|
|
|
|
|
|
uintptr_t input_ctx_phys = pmm_alloc (1);
|
|
|
|
|
void* input_ctx_virt = (void*)(input_ctx_phys + (uintptr_t)hhdm->offset);
|
|
|
|
|
@@ -767,13 +769,14 @@ static void xhci_pdevice_setup_addressing (struct xhci* xhci, struct xhci_pdevic
|
|
|
|
|
ctx64->ctrl.dw[1] = (1 << 0) | (1 << 1);
|
|
|
|
|
|
|
|
|
|
ctx64->slot.dw[0] = (1 << XHCI_SLCTX_SLOT_STATE) | (speed << XHCI_SLCTX_SPEED);
|
|
|
|
|
ctx64->slot.dw[1] = ((pdevice->port_value + 1) << XHCI_SLCTX_ROOTHUBPRNUM);
|
|
|
|
|
ctx64->slot.dw[1] = ((pdevice->xhci_port->port_value + 1) << XHCI_SLCTX_ROOTHUBPRNUM);
|
|
|
|
|
|
|
|
|
|
ctx64->endpoints[0].dw[0] = 0;
|
|
|
|
|
ctx64->endpoints[0].dw[1] =
|
|
|
|
|
3 | (XHCI_EP_CTRL_BI << XHCI_EPCTX_EP_TYPE) | (max_packet_size << XHCI_EPCTX_MAX_PKT_SZ);
|
|
|
|
|
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);
|
|
|
|
|
ctx64->endpoints[0].dw[1] = (3 << XHCI_EPCTX_ERR_COUNT) |
|
|
|
|
|
(XHCI_EP_CTRL_BI << XHCI_EPCTX_EP_TYPE) |
|
|
|
|
|
(max_packet_size << XHCI_EPCTX_MAX_PKT_SZ);
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
@@ -782,12 +785,13 @@ static void xhci_pdevice_setup_addressing (struct xhci* xhci, struct xhci_pdevic
|
|
|
|
|
ctx32->ctrl.dw[1] = (1 << 0) | (1 << 1);
|
|
|
|
|
|
|
|
|
|
ctx32->slot.dw[0] = (1 << XHCI_SLCTX_SLOT_STATE) | (speed << XHCI_SLCTX_SPEED);
|
|
|
|
|
ctx32->slot.dw[1] = ((pdevice->port_value + 1) << XHCI_SLCTX_ROOTHUBPRNUM);
|
|
|
|
|
ctx32->slot.dw[1] = ((pdevice->xhci_port->port_value + 1) << XHCI_SLCTX_ROOTHUBPRNUM);
|
|
|
|
|
|
|
|
|
|
ctx32->endpoints[0].dw[1] =
|
|
|
|
|
3 | (XHCI_EP_CTRL_BI << XHCI_EPCTX_EP_TYPE) | (max_packet_size << XHCI_EPCTX_MAX_PKT_SZ);
|
|
|
|
|
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);
|
|
|
|
|
ctx32->endpoints[0].dw[1] = (3 << XHCI_EPCTX_ERR_COUNT) |
|
|
|
|
|
(XHCI_EP_CTRL_BI << XHCI_EPCTX_EP_TYPE) |
|
|
|
|
|
(max_packet_size << XHCI_EPCTX_MAX_PKT_SZ);
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xhci->last_cmpl_code = 0;
|
|
|
|
|
@@ -797,10 +801,11 @@ static void xhci_pdevice_setup_addressing (struct xhci* xhci, struct xhci_pdevic
|
|
|
|
|
stall_ms (500);
|
|
|
|
|
|
|
|
|
|
if (xhci->last_cmpl_code != 1) {
|
|
|
|
|
DEBUG ("Failed to address device. port = %u, slot = %u\n", pdevice->port_value,
|
|
|
|
|
DEBUG ("Failed to address device. port = %u, slot = %u\n", pdevice->xhci_port->port_value,
|
|
|
|
|
pdevice->slot_id);
|
|
|
|
|
} else {
|
|
|
|
|
DEBUG ("Device on port %u addressed on slot %u!\n", pdevice->port_value, pdevice->slot_id);
|
|
|
|
|
DEBUG ("Device on port %u addressed on slot %u!\n", pdevice->xhci_port->port_value,
|
|
|
|
|
pdevice->slot_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pmm_free (input_ctx_phys, 1);
|
|
|
|
|
@@ -829,6 +834,35 @@ static bool xhci_pdevice_setup_get_info (struct xhci* xhci, struct xhci_pdevice*
|
|
|
|
|
usb_desc->vendor_id, usb_desc->product_id, usb_desc->dev_class, usb_desc->dev_subclass,
|
|
|
|
|
usb_desc->max_packet_size, usb_desc->num_configs);
|
|
|
|
|
|
|
|
|
|
pdevice->device_desc = *usb_desc;
|
|
|
|
|
|
|
|
|
|
pmm_free (response_buf_phys, 1);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool xhci_pdevice_setup_get_config (struct xhci* xhci, struct xhci_pdevice* pdevice,
|
|
|
|
|
uint64_t* lockflags) {
|
|
|
|
|
struct limine_hhdm_response* hhdm = limine_hhdm_request.response;
|
|
|
|
|
|
|
|
|
|
uintptr_t response_buf_phys = pmm_alloc (1);
|
|
|
|
|
|
|
|
|
|
void* response_buf = (void*)(response_buf_phys + (uintptr_t)hhdm->offset);
|
|
|
|
|
memset (response_buf, 0, PAGE_SIZE);
|
|
|
|
|
|
|
|
|
|
bool ok =
|
|
|
|
|
xhci_endpoint0_ctrl_in (xhci, pdevice, 0x80, 6, (2 << 8), 0, response_buf_phys, 9, lockflags);
|
|
|
|
|
|
|
|
|
|
if (!ok) {
|
|
|
|
|
pmm_free (response_buf_phys, 1);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct usb_config_desc* usb_config = response_buf;
|
|
|
|
|
|
|
|
|
|
DEBUG ("USB device config: total_len=%u num_ifs=%u, cfgval=%u cfg=%u attrs=%02x maxpow=%u\n",
|
|
|
|
|
usb_config->total_length, usb_config->num_ifs, usb_config->config_value,
|
|
|
|
|
usb_config->config, usb_config->attrs, usb_config->max_power);
|
|
|
|
|
|
|
|
|
|
pmm_free (response_buf_phys, 1);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
@@ -846,11 +880,14 @@ static void xhci_poll_setup_devices (struct xhci* xhci, uint64_t* lockflags) {
|
|
|
|
|
xhci_pdevice_setup_addressing (xhci, pdevice, lockflags);
|
|
|
|
|
|
|
|
|
|
xhci_pdevice_setup_get_info (xhci, pdevice, lockflags);
|
|
|
|
|
|
|
|
|
|
xhci_pdevice_setup_get_config (xhci, pdevice, lockflags);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void xhci_poll_port_changes (struct xhci* xhci) {
|
|
|
|
|
struct list_node_link *port_change_link, *tmp_port_change_link;
|
|
|
|
|
struct xhci_port* xhci_port;
|
|
|
|
|
|
|
|
|
|
list_foreach (xhci->port_changes, port_change_link, tmp_port_change_link) {
|
|
|
|
|
struct xhci_port_status_change* change =
|
|
|
|
|
@@ -858,16 +895,18 @@ static void xhci_poll_port_changes (struct xhci* xhci) {
|
|
|
|
|
|
|
|
|
|
list_remove (xhci->port_changes, &change->port_changes_link);
|
|
|
|
|
|
|
|
|
|
list_find (struct xhci_port, xhci->xhci_ports, xhci_port, port_value, change->port, ports_link);
|
|
|
|
|
|
|
|
|
|
if ((change->portsc & (1 << XHCI_PORTSC_CSC))) {
|
|
|
|
|
if ((change->portsc & (1 << XHCI_PORTSC_CCS))) {
|
|
|
|
|
DEBUG ("Device attached to port %u!\n", change->port);
|
|
|
|
|
|
|
|
|
|
xhci_port_reset (xhci, change->port);
|
|
|
|
|
xhci_create_pdevice (xhci, change->port);
|
|
|
|
|
xhci_create_pdevice (xhci, xhci_port);
|
|
|
|
|
} else {
|
|
|
|
|
DEBUG ("Device detached from port %u!\n", change->port);
|
|
|
|
|
|
|
|
|
|
xhci_delete_pdevice (xhci, change->port);
|
|
|
|
|
xhci_delete_pdevice (xhci, xhci_port);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -972,29 +1011,29 @@ DEFINE_DEVICE_INIT (xhci_init) {
|
|
|
|
|
xhci_write32 (xhci->xhci_oper_base, XHCI_DCBAAP, (uint32_t)xhci->xhci_dcbaa_phys);
|
|
|
|
|
xhci_write32 (xhci->xhci_oper_base, XHCI_DCBAAP + 4, (uint32_t)(xhci->xhci_dcbaa_phys >> 32));
|
|
|
|
|
|
|
|
|
|
xhci->cmd_ring_phys = pmm_alloc (1);
|
|
|
|
|
xhci->cmd_ring = (struct xhci_trb*)(xhci->cmd_ring_phys + (uintptr_t)hhdm->offset);
|
|
|
|
|
memset (xhci->cmd_ring, 0, PAGE_SIZE);
|
|
|
|
|
xhci->cmd_ring_size = PAGE_SIZE / sizeof (struct xhci_trb);
|
|
|
|
|
xhci->cmd_ring_idx = 0;
|
|
|
|
|
xhci->cmd_cycle_bit = 1;
|
|
|
|
|
xhci->cmd_ring.phys = pmm_alloc (1);
|
|
|
|
|
xhci->cmd_ring.trbs = (struct xhci_trb*)(xhci->cmd_ring.phys + (uintptr_t)hhdm->offset);
|
|
|
|
|
memset (xhci->cmd_ring.trbs, 0, PAGE_SIZE);
|
|
|
|
|
xhci->cmd_ring.size = PAGE_SIZE / sizeof (struct xhci_trb);
|
|
|
|
|
xhci->cmd_ring.idx = 0;
|
|
|
|
|
xhci->cmd_ring.cycle_bit = 1;
|
|
|
|
|
|
|
|
|
|
uint64_t crcr = xhci->cmd_ring_phys | xhci->cmd_cycle_bit;
|
|
|
|
|
uint64_t crcr = xhci->cmd_ring.phys | xhci->cmd_ring.cycle_bit;
|
|
|
|
|
xhci_write32 (xhci->xhci_oper_base, XHCI_CRCR, (uint32_t)crcr);
|
|
|
|
|
xhci_write32 (xhci->xhci_oper_base, XHCI_CRCR + 4, (uint32_t)(crcr >> 32));
|
|
|
|
|
|
|
|
|
|
xhci->event_ring_phys = pmm_alloc (1);
|
|
|
|
|
xhci->event_ring = (struct xhci_trb*)(xhci->event_ring_phys + (uintptr_t)hhdm->offset);
|
|
|
|
|
memset (xhci->event_ring, 0, PAGE_SIZE);
|
|
|
|
|
xhci->event_ring_size = PAGE_SIZE / sizeof (struct xhci_trb);
|
|
|
|
|
xhci->event_ring_idx = 0;
|
|
|
|
|
xhci->event_cycle_bit = 1;
|
|
|
|
|
xhci->event_ring.phys = pmm_alloc (1);
|
|
|
|
|
xhci->event_ring.trbs = (struct xhci_trb*)(xhci->event_ring.phys + (uintptr_t)hhdm->offset);
|
|
|
|
|
memset (xhci->event_ring.trbs, 0, PAGE_SIZE);
|
|
|
|
|
xhci->event_ring.size = PAGE_SIZE / sizeof (struct xhci_trb);
|
|
|
|
|
xhci->event_ring.idx = 0;
|
|
|
|
|
xhci->event_ring.cycle_bit = 1;
|
|
|
|
|
|
|
|
|
|
xhci->erst_phys = pmm_alloc (1);
|
|
|
|
|
xhci->erst = (struct xhci_erst_entry*)(xhci->erst_phys + (uintptr_t)hhdm->offset);
|
|
|
|
|
memset (xhci->erst, 0, PAGE_SIZE);
|
|
|
|
|
xhci->erst[0].ptr = xhci->event_ring_phys;
|
|
|
|
|
xhci->erst[0].size = xhci->event_ring_size;
|
|
|
|
|
xhci->erst[0].ptr = xhci->event_ring.phys;
|
|
|
|
|
xhci->erst[0].size = xhci->event_ring.size;
|
|
|
|
|
xhci->erst[0]._rsvd = 0;
|
|
|
|
|
|
|
|
|
|
uintptr_t ir_base = xhci->xhci_runtime_base + 0x20;
|
|
|
|
|
@@ -1002,14 +1041,12 @@ DEFINE_DEVICE_INIT (xhci_init) {
|
|
|
|
|
xhci_write32 (ir_base, XHCI_ERSTBA, (uint32_t)xhci->erst_phys);
|
|
|
|
|
xhci_write32 (ir_base, XHCI_ERSTBA + 4, (uint32_t)(xhci->erst_phys >> 32));
|
|
|
|
|
|
|
|
|
|
xhci_write32 (ir_base, XHCI_ERDP, (uint32_t)xhci->event_ring_phys | (1 << 3));
|
|
|
|
|
xhci_write32 (ir_base, XHCI_ERDP + 4, (uint32_t)(xhci->event_ring_phys >> 32));
|
|
|
|
|
xhci_write32 (ir_base, XHCI_ERDP, (uint32_t)xhci->event_ring.phys | (1 << 3));
|
|
|
|
|
xhci_write32 (ir_base, XHCI_ERDP + 4, (uint32_t)(xhci->event_ring.phys >> 32));
|
|
|
|
|
|
|
|
|
|
if (xhci->irqs_support) {
|
|
|
|
|
/* enable interrupter */
|
|
|
|
|
irq_attach (&xhci_irq, xhci, xhci->irq);
|
|
|
|
|
xhci_write32 (ir_base, XHCI_IMAN, xhci_read32 (ir_base, XHCI_IMAN) | (1 << 1));
|
|
|
|
|
}
|
|
|
|
|
/* enable interrupter */
|
|
|
|
|
irq_attach (&xhci_irq, xhci, xhci->irq);
|
|
|
|
|
xhci_write32 (ir_base, XHCI_IMAN, xhci_read32 (ir_base, XHCI_IMAN) | (1 << 1));
|
|
|
|
|
|
|
|
|
|
usbcmd = xhci_read32 (xhci->xhci_oper_base, XHCI_USBCMD);
|
|
|
|
|
xhci_write32 (xhci->xhci_oper_base, XHCI_USBCMD, usbcmd | (1 << 0) | (1 << 2));
|
|
|
|
|
@@ -1047,8 +1084,8 @@ DEFINE_DEVICE_FINI (xhci_fini) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pmm_free (xhci->xhci_dcbaa_phys, 1);
|
|
|
|
|
pmm_free (xhci->cmd_ring_phys, 1);
|
|
|
|
|
pmm_free (xhci->event_ring_phys, 1);
|
|
|
|
|
pmm_free (xhci->cmd_ring.phys, 1);
|
|
|
|
|
pmm_free (xhci->event_ring.phys, 1);
|
|
|
|
|
pmm_free (xhci->erst_phys, 1);
|
|
|
|
|
|
|
|
|
|
struct list_node_link *port_link, *tmp_port_link;
|
|
|
|
|
|