Add basic HID keyboard support
This commit is contained in:
+264
-104
@@ -68,6 +68,7 @@ typedef volatile struct __attribute__((packed)) {
|
||||
#define XHCI_PORT_REGS_PED(p) BITS_R((p)->portsc, 1, 1)
|
||||
#define XHCI_PORT_REGS_PR_R(p) BITS_R((p)->portsc, 4, 4)
|
||||
#define XHCI_PORT_REGS_PR_W(p, v) BITS_W((p)->portsc, 4, 4, v)
|
||||
#define XHCI_PORT_REGS_PLL(p) BITS_R((p)->portsc, 8, 5)
|
||||
#define XHCI_PORT_REGS_PP(p) BITS_R((p)->portsc, 9, 9)
|
||||
#define XHCI_PORT_REGS_PS(p) BITS_R((p)->portsc, 13, 10)
|
||||
|
||||
@@ -112,14 +113,19 @@ typedef struct __attribute__((packed)) {
|
||||
#define XHCI_TRB_CYCLE_W(t, v) BITS_W((t)->control, 0, 0, v)
|
||||
#define XHCI_TRB_TOGGLE_CYCLE_R(t) BITS_R((t)->control, 1, 1)
|
||||
#define XHCI_TRB_TOGGLE_CYCLE_W(t, v) BITS_W((t)->control, 1, 1, v)
|
||||
#define XHCI_TRB_IOC_R(t) BITS_R((t)->control, 5, 5)
|
||||
#define XHCI_TRB_IOC_W(t, v) BITS_W((t)->control, 5, 5, v)
|
||||
#define XHCI_TRB_TRB_TYPE_R(t) BITS_R((t)->control, 15, 10)
|
||||
#define XHCI_TRB_TRB_TYPE_W(t, v) BITS_W((t)->control, 15, 10, v)
|
||||
#define XHCI_TRB_SLOT_TYPE_R(t) BITS_R((t)->control, 20, 16)
|
||||
#define XHCI_TRB_SLOT_TYPE_W(t, v) BITS_W((t)->control, 20, 16, v)
|
||||
#define XHCI_TRB_ENDPOINT_ID_R(t) BITS_R((t)->control, 20, 16)
|
||||
#define XHCI_TRB_ENDPOINT_ID_W(t, v) BITS_W((t)->control, 20, 16, v)
|
||||
#define XHCI_TRB_COMPLETION_CODE(t) BITS_R((t)->status, 31, 24)
|
||||
#define XHCI_TRB_SLOT_ID_R(t) BITS_R((t)->control, 31, 24)
|
||||
#define XHCI_TRB_SLOT_ID_W(t, v) BITS_W((t)->control, 31, 24, v)
|
||||
|
||||
#define XHCI_TRB_TYPE_TRANSFER_NORMAL 1
|
||||
#define XHCI_TRB_TYPE_TRANSFER_SETUP_STAGE 2
|
||||
#define XHCI_TRB_TYPE_TRANSFER_DATA_STAGE 3
|
||||
#define XHCI_TRB_TYPE_TRANSFER_STATUS_STAGE 4
|
||||
@@ -127,6 +133,8 @@ typedef struct __attribute__((packed)) {
|
||||
#define XHCI_TRB_TYPE_COMMAND_ENABLE_SLOT 9
|
||||
#define XHCI_TRB_TYPE_COMMAND_DISABLE_SLOT 10
|
||||
#define XHCI_TRB_TYPE_COMMAND_ADDRESS_DEVICE 11
|
||||
#define XHCI_TRB_TYPE_COMMAND_CONFIGURE_ENDPOINT 12
|
||||
#define XHCI_TRB_TYPE_COMMAND_STOP_ENDPOINT 15
|
||||
#define XHCI_TRB_TYPE_COMMAND_NOOP 23
|
||||
#define XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION 32
|
||||
#define XHCI_TRB_TYPE_EVENT_COMMAND_COMPLETION 33
|
||||
@@ -136,13 +144,25 @@ static xchi_erst_entry_t erst[1] __attribute__((aligned(PAGE_SIZE)));
|
||||
|
||||
#define RING_LENGTH 32
|
||||
|
||||
static uint8_t cr_cycle = 1;
|
||||
static uint8_t cr_i = 0;
|
||||
static xhci_trb_t cr[RING_LENGTH + 1] __attribute__((aligned(PAGE_SIZE)));
|
||||
#define RING(name) \
|
||||
static uint8_t name##_r_cycle = 1; \
|
||||
static uint8_t name##_r_i = 0; \
|
||||
static xhci_trb_t name##_r[RING_LENGTH + 1] __attribute__((aligned(PAGE_SIZE)));
|
||||
|
||||
static uint8_t er_cycle = 1;
|
||||
static uint8_t er_i = 0;
|
||||
static xhci_trb_t er[RING_LENGTH] __attribute__((aligned(PAGE_SIZE)));
|
||||
#define RING_WRAP(name) \
|
||||
xhci_trb_t *name##_wrapper = &name##_r[RING_LENGTH]; \
|
||||
name##_wrapper->parameter = (uint64_t)VIRT_TO_PHYS(name##_r); \
|
||||
name##_wrapper->status = 0; \
|
||||
XHCI_TRB_TRB_TYPE_W(name##_wrapper, XHCI_TRB_TYPE_LINK); \
|
||||
XHCI_TRB_TOGGLE_CYCLE_W(name##_wrapper, 1);
|
||||
|
||||
#define RING_ADVANCE(name) \
|
||||
name##_r_i = (name##_r_i + 1) % RING_LENGTH; \
|
||||
XHCI_TRB_CYCLE_W(&name##_r[RING_LENGTH], name##_r_cycle); \
|
||||
name##_r_cycle ^= (name##_r_i == 0);
|
||||
|
||||
RING(cmd);
|
||||
RING(evt);
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint32_t drop_flags;
|
||||
@@ -173,10 +193,14 @@ typedef struct __attribute__((packed)) {
|
||||
uint32_t reserved[3];
|
||||
} xhci_ep_ctx_t;
|
||||
|
||||
#define USB_EP_CTX_EP_TYPE_R(e) BITS_R((e)->dw1, 5, 3)
|
||||
#define USB_EP_CTX_EP_TYPE_W(e, v) BITS_W((e)->dw1, 5, 3, v)
|
||||
#define USB_EP_CTX_MAX_PACKET_SIZE_R(e) BITS_R((e)->dw1, 31, 16)
|
||||
#define USB_EP_CTX_MAX_PACKET_SIZE_W(e, v) BITS_W((e)->dw1, 31, 16, v)
|
||||
#define XHCI_EP_CTX_INTERVAL_R(e) BITS_R((e)->dw0, 23, 16)
|
||||
#define XHCI_EP_CTX_INTERVAL_W(e, v) BITS_W((e)->dw0, 23, 16, v)
|
||||
#define XHCI_EP_CTX_EP_TYPE_R(e) BITS_R((e)->dw1, 5, 3)
|
||||
#define XHCI_EP_CTX_EP_TYPE_W(e, v) BITS_W((e)->dw1, 5, 3, v)
|
||||
#define XHCI_EP_CTX_MAX_PACKET_SIZE_R(e) BITS_R((e)->dw1, 31, 16)
|
||||
#define XHCI_EP_CTX_MAX_PACKET_SIZE_W(e, v) BITS_W((e)->dw1, 31, 16, v)
|
||||
|
||||
#define XHCI_EP_CTX_MAX_PACKET_SIZE 64
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
xhci_input_control_ctx_t control;
|
||||
@@ -193,10 +217,15 @@ typedef struct __attribute__((packed)) {
|
||||
|
||||
static xchi_device_ctx_t device_ctx __attribute__((aligned(64)));
|
||||
|
||||
static uint8_t tr_attached_slot = 0;
|
||||
static uint8_t tr_cycle = 1;
|
||||
static uint8_t tr_i = 0;
|
||||
static xhci_trb_t tr[RING_LENGTH + 1] __attribute__((aligned(PAGE_SIZE)));
|
||||
static uint8_t tsf_r_attached_slot = 0;
|
||||
RING(tsf);
|
||||
|
||||
static uint8_t ep_r_attached_endpoint = 0;
|
||||
RING(ep);
|
||||
|
||||
static uint16_t ep_transfer_size = 0;
|
||||
static uint8_t ep_buffer[RING_LENGTH][XHCI_EP_CTX_MAX_PACKET_SIZE] __attribute__((aligned(64)));
|
||||
static uint8_t ep_processed_events = RING_LENGTH;
|
||||
|
||||
static const xhci_trb_t *command_exec_sync(const xhci_trb_t *cmd) {
|
||||
LOG_LN_STEP("Queueing command TRB...");
|
||||
@@ -204,14 +233,10 @@ static const xhci_trb_t *command_exec_sync(const xhci_trb_t *cmd) {
|
||||
LOG_VAL_TRACE(cmd->status, "%x");
|
||||
LOG_VAL_TRACE(cmd->control, "%x");
|
||||
|
||||
xhci_trb_t *cre = &cr[cr_i];
|
||||
|
||||
xhci_trb_t *cre = &cmd_r[cmd_r_i];
|
||||
memory_copy(cmd, sizeof(xhci_trb_t), cre);
|
||||
XHCI_TRB_CYCLE_W(cre, cr_cycle);
|
||||
|
||||
cr_i = (cr_i + 1) % RING_LENGTH;
|
||||
XHCI_TRB_CYCLE_W((&cr[RING_LENGTH]), cr_cycle);
|
||||
cr_cycle ^= (cr_i == 0);
|
||||
XHCI_TRB_CYCLE_W(cre, cmd_r_cycle);
|
||||
RING_ADVANCE(cmd);
|
||||
|
||||
LOG_LN_STEP("Ringing command doorbell...");
|
||||
LOG_VAL_TRACE(op_regs->usbsts, "%x");
|
||||
@@ -220,16 +245,17 @@ static const xhci_trb_t *command_exec_sync(const xhci_trb_t *cmd) {
|
||||
|
||||
LOG_LN_STEP("Waiting for completion...");
|
||||
while (1) {
|
||||
xhci_trb_t *cmp = &er[er_i];
|
||||
while (XHCI_TRB_CYCLE_R(cmp) != er_cycle)
|
||||
xhci_trb_t *cmp = &evt_r[evt_r_i];
|
||||
while (XHCI_TRB_CYCLE_R(cmp) != evt_r_cycle)
|
||||
;
|
||||
|
||||
er_i = (er_i + 1) % RING_LENGTH;
|
||||
intr_regs->erdp = (uint64_t)(VIRT_TO_PHYS(&er[er_i]));
|
||||
er_cycle ^= (er_i == 0);
|
||||
RING_ADVANCE(evt);
|
||||
|
||||
if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_COMMAND_COMPLETION || (void *)cmp->parameter != VIRT_TO_PHYS(cre)) {
|
||||
LOG_LN_STEP("Received irrelevant event, skipping...");
|
||||
|
||||
LOG_VAL_TRACE(cmp->parameter, "%lx");
|
||||
LOG_VAL_TRACE(cmp->status, "%x");
|
||||
LOG_VAL_TRACE(cmp->control, "%x");
|
||||
} else {
|
||||
LOG_LN_STEP("Received command completion event...");
|
||||
|
||||
@@ -246,10 +272,88 @@ static const xhci_trb_t *command_exec_sync(const xhci_trb_t *cmd) {
|
||||
}
|
||||
}
|
||||
|
||||
static const xhci_trb_t *control_transfer_exec_sync(uint64_t setup, void *data, uint16_t length) {
|
||||
ASSERT(tsf_r_attached_slot, "control_transfer_exec_sync: Slot not attached.");
|
||||
|
||||
LOG_LN_STEP("Queueing transfer TRBs...");
|
||||
|
||||
xhci_trb_t *tre = &tsf_r[tsf_r_i];
|
||||
|
||||
tre->parameter = setup;
|
||||
tre->status = sizeof(setup);
|
||||
XHCI_TRB_TRB_TYPE_W(tre, XHCI_TRB_TYPE_TRANSFER_SETUP_STAGE);
|
||||
BITS_W(tre->control, 17, 16, 3);
|
||||
BITS_W(tre->control, 6, 6, 1);
|
||||
XHCI_TRB_CYCLE_W(tre, tsf_r_cycle);
|
||||
|
||||
LOG_VAL_TRACE(tre->parameter, "%lx");
|
||||
LOG_VAL_TRACE(tre->status, "%x");
|
||||
LOG_VAL_TRACE(tre->control, "%x");
|
||||
|
||||
RING_ADVANCE(tsf);
|
||||
|
||||
if (data != NUL) {
|
||||
tre = &tsf_r[tsf_r_i];
|
||||
|
||||
tre->parameter = (uint64_t)VIRT_TO_PHYS(data);
|
||||
tre->status = length;
|
||||
XHCI_TRB_TRB_TYPE_W(tre, XHCI_TRB_TYPE_TRANSFER_DATA_STAGE);
|
||||
BITS_W(tre->control, 16, 16, 1);
|
||||
XHCI_TRB_CYCLE_W(tre, tsf_r_cycle);
|
||||
|
||||
LOG_VAL_TRACE(tre->parameter, "%lx");
|
||||
LOG_VAL_TRACE(tre->status, "%x");
|
||||
LOG_VAL_TRACE(tre->control, "%x");
|
||||
|
||||
RING_ADVANCE(tsf);
|
||||
}
|
||||
|
||||
tre = &tsf_r[tsf_r_i];
|
||||
|
||||
XHCI_TRB_TRB_TYPE_W(tre, XHCI_TRB_TYPE_TRANSFER_STATUS_STAGE);
|
||||
BITS_W(tre->control, 16, 16, 0);
|
||||
XHCI_TRB_IOC_W(tre, 1);
|
||||
XHCI_TRB_CYCLE_W(tre, tsf_r_cycle);
|
||||
|
||||
RING_ADVANCE(tsf);
|
||||
|
||||
LOG_LN_STEP("Ringing slot doorbell...");
|
||||
db_regs[tsf_r_attached_slot] = 1;
|
||||
|
||||
LOG_LN_STEP("Waiting for completion...");
|
||||
while (1) {
|
||||
xhci_trb_t *cmp = &evt_r[evt_r_i];
|
||||
while (XHCI_TRB_CYCLE_R(cmp) != evt_r_cycle)
|
||||
;
|
||||
|
||||
RING_ADVANCE(evt);
|
||||
|
||||
if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION || (void *)cmp->parameter != VIRT_TO_PHYS(tre)) {
|
||||
LOG_LN_STEP("Received irrelevant event, skipping...");
|
||||
|
||||
LOG_VAL_TRACE(cmp->parameter, "%lx");
|
||||
LOG_VAL_TRACE(cmp->status, "%x");
|
||||
LOG_VAL_TRACE(cmp->control, "%x");
|
||||
} else {
|
||||
LOG_LN_STEP("Received transfer completion event...");
|
||||
|
||||
LOG_VAL_TRACE(cmp->parameter, "%lx");
|
||||
LOG_VAL_TRACE(cmp->status, "%x");
|
||||
LOG_VAL_TRACE(cmp->control, "%x");
|
||||
|
||||
ASSERT(XHCI_TRB_COMPLETION_CODE(cmp) == 1, "control_transfer_exec_sync: Transfer failed.");
|
||||
|
||||
LOG_LN_STEP("Done.");
|
||||
|
||||
return cmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void xhci_init() {
|
||||
LOG_LN_INFO("Searching for suitable controller...");
|
||||
pci_bdf_t bdf = pci_find_by_class(XHCI_PCI_CLASS, XHCI_PCI_SUBCLASS, XHCI_PCI_INTERFACE);
|
||||
ASSERT(bdf, "xhci_init: No suitable controllers found.");
|
||||
ASSERT(bdf != (pci_bdf_t)-1, "xhci_init: No suitable controllers found.");
|
||||
|
||||
LOG_LN_INFO("Mapping controller to virtual memory...");
|
||||
pci_map(bdf, cap_regs, 4);
|
||||
@@ -300,19 +404,15 @@ void xhci_init() {
|
||||
LOG_VAL_DATA(op_regs->usbsts, "%x");
|
||||
|
||||
LOG_LN_INFO("Setting up command ring...");
|
||||
xhci_trb_t *wrapper = &cr[RING_LENGTH];
|
||||
wrapper->parameter = (uint64_t)VIRT_TO_PHYS(cr);
|
||||
wrapper->status = 0;
|
||||
XHCI_TRB_TRB_TYPE_W(wrapper, XHCI_TRB_TYPE_LINK);
|
||||
XHCI_TRB_TOGGLE_CYCLE_W(wrapper, 1);
|
||||
XHCI_TRB_CYCLE_W(wrapper, cr_cycle);
|
||||
RING_WRAP(cmd);
|
||||
XHCI_TRB_CYCLE_W(&cmd_r[RING_LENGTH], cmd_r_cycle);
|
||||
|
||||
LOG_LN_INFO("Configuring controller...");
|
||||
XHCI_OP_REGS_MAX_SLOTS_EN_W(op_regs, MAX_SLOTS);
|
||||
op_regs->dcbaap = (uint64_t)VIRT_TO_PHYS(dcbaa);
|
||||
op_regs->crcr = (uint64_t)VIRT_TO_PHYS(cr) | cr_cycle;
|
||||
op_regs->crcr = (uint64_t)VIRT_TO_PHYS(cmd_r) | cmd_r_cycle;
|
||||
erst[0].size = RING_LENGTH;
|
||||
erst[0].base = (uint64_t)VIRT_TO_PHYS(er);
|
||||
erst[0].base = (uint64_t)VIRT_TO_PHYS(evt_r);
|
||||
intr_regs->erstsz = sizeof(erst) / sizeof(xchi_erst_entry_t);
|
||||
intr_regs->erstba = (uint64_t)VIRT_TO_PHYS(erst);
|
||||
intr_regs->erdp = erst[0].base;
|
||||
@@ -336,6 +436,7 @@ void xhci_init() {
|
||||
;
|
||||
LOG_VAL_DATA(op_regs->usbcmd, "%x");
|
||||
LOG_VAL_DATA(op_regs->usbsts, "%x");
|
||||
sleep(1000); // Wait for ports to settle
|
||||
|
||||
PRINT_LN(kernel_log, "Enumerating ports...");
|
||||
xhci_enumerate(kernel_log);
|
||||
@@ -343,9 +444,9 @@ void xhci_init() {
|
||||
}
|
||||
|
||||
void xhci_enumerate(stream_t *out) {
|
||||
for (uint8_t i = 0; i < XHCI_CAP_REGS_MAX_PORTS(cap_regs); i++) {
|
||||
PRINT_LN(out, "port=%d CCS=%d PED=%d PP=%d PS=%d", i, XHCI_PORT_REGS_CCS(&port_regs[i]), XHCI_PORT_REGS_PED(&port_regs[i]),
|
||||
XHCI_PORT_REGS_PP(&port_regs[i]), XHCI_PORT_REGS_PS(&port_regs[i]));
|
||||
for (uint16_t i = 0; i < XHCI_CAP_REGS_MAX_PORTS(cap_regs); i++) {
|
||||
PRINT_LN(out, "port=%d CCS=%d PED=%d PP=%d PS=%d portsc=%x", i, XHCI_PORT_REGS_CCS(&port_regs[i]), XHCI_PORT_REGS_PED(&port_regs[i]),
|
||||
XHCI_PORT_REGS_PP(&port_regs[i]), XHCI_PORT_REGS_PS(&port_regs[i]), port_regs[i].portsc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,27 +454,33 @@ uint8_t xhci_port_count() {
|
||||
return XHCI_CAP_REGS_MAX_PORTS(cap_regs);
|
||||
}
|
||||
|
||||
uint8_t xhci_port_connected(uint8_t port) {
|
||||
uint8_t xhci_port_connected(xhci_port_t port) {
|
||||
ASSERT(port < XHCI_CAP_REGS_MAX_PORTS(cap_regs), "xhci_port_connected: Port doesn't exist.");
|
||||
|
||||
return XHCI_PORT_REGS_CCS(&port_regs[port]);
|
||||
}
|
||||
|
||||
xhci_slot_t xhci_attach(uint8_t port) {
|
||||
xhci_slot_t xhci_attach(xhci_port_t port) {
|
||||
LOG_LN_STEP("Attaching port %d ...", port);
|
||||
|
||||
ASSERT(!tr_attached_slot, "xhci_attach: Can only attach one port at a time.")
|
||||
ASSERT(port < XHCI_CAP_REGS_MAX_PORTS(cap_regs), "xhci_attach: Port doesn't exist.");
|
||||
ASSERT(!tsf_r_attached_slot, "xhci_attach: Can only attach one port at a time.");
|
||||
ASSERT(XHCI_PORT_REGS_CCS(&port_regs[port]), "xhci_attach: Port is not connected.");
|
||||
|
||||
memory_set(0, sizeof(tr), tr);
|
||||
tr_i = 0;
|
||||
tr_cycle = 1;
|
||||
LOG_LN_STEP("Setting up transfer ring...");
|
||||
memory_set(0, sizeof(tsf_r), tsf_r);
|
||||
tsf_r_i = 0;
|
||||
tsf_r_cycle = 1;
|
||||
RING_WRAP(tsf);
|
||||
|
||||
LOG_LN_STEP("Resetting device...");
|
||||
port_regs[port].portsc = 1 << 4;
|
||||
port_regs[port].portsc = (port_regs[port].portsc & ~(uint32_t)0x00FE0000) | (1 << 4);
|
||||
while (XHCI_PORT_REGS_PR_R(&port_regs[port]))
|
||||
;
|
||||
while (!XHCI_PORT_REGS_PED(&port_regs[port]))
|
||||
LOG_VAL_TRACE(&port_regs[port].portsc, "%x");
|
||||
while (!XHCI_PORT_REGS_PED(&port_regs[port]) && XHCI_PORT_REGS_PLL(&port_regs[port]) != 0)
|
||||
;
|
||||
LOG_VAL_TRACE(XHCI_PORT_REGS_PED(&port_regs[port]), "%hhx");
|
||||
LOG_VAL_TRACE(&port_regs[port].portsc, "%x");
|
||||
|
||||
LOG_LN_STEP("Enabling a slot...");
|
||||
xhci_trb_t cmd_es;
|
||||
@@ -387,9 +494,9 @@ xhci_slot_t xhci_attach(uint8_t port) {
|
||||
XHCI_SLOT_CTX_CTX_ENTRIES_W(&input_ctx.slot, 1);
|
||||
XHCI_SLOT_CTX_ROOT_HUB_PORT_W(&input_ctx.slot, port + 1);
|
||||
XHCI_SLOT_CTX_SPEED_W(&input_ctx.slot, XHCI_PORT_REGS_PS((&port_regs[port])));
|
||||
USB_EP_CTX_EP_TYPE_W(&input_ctx.ep[0], 4);
|
||||
USB_EP_CTX_MAX_PACKET_SIZE_W(&input_ctx.ep[0], 64);
|
||||
input_ctx.ep[0].dequeue = (uint64_t)VIRT_TO_PHYS(tr) | tr_cycle;
|
||||
XHCI_EP_CTX_EP_TYPE_W(&input_ctx.ep[0], 4);
|
||||
XHCI_EP_CTX_MAX_PACKET_SIZE_W(&input_ctx.ep[0], 64);
|
||||
input_ctx.ep[0].dequeue = (uint64_t)VIRT_TO_PHYS(tsf_r) | tsf_r_cycle;
|
||||
|
||||
dcbaa[slot] = VIRT_TO_PHYS(&device_ctx);
|
||||
|
||||
@@ -400,7 +507,7 @@ xhci_slot_t xhci_attach(uint8_t port) {
|
||||
XHCI_TRB_SLOT_ID_W(&cmd_ad, slot);
|
||||
command_exec_sync(&cmd_ad);
|
||||
|
||||
tr_attached_slot = slot;
|
||||
tsf_r_attached_slot = slot;
|
||||
|
||||
LOG_LN_STEP("Done.");
|
||||
|
||||
@@ -408,85 +515,138 @@ xhci_slot_t xhci_attach(uint8_t port) {
|
||||
}
|
||||
|
||||
uint64_t xhci_control_transfer(xhci_slot_t slot, uint64_t setup, void *data, uint16_t length) {
|
||||
ASSERT(tr_attached_slot == slot, "xhci_control_transfer: Can only attach one port at a time.");
|
||||
ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist.");
|
||||
ASSERT(tsf_r_attached_slot == slot, "xhci_control_transfer: Slot is not attached.");
|
||||
ASSERT(!ep_r_attached_endpoint, "xhci_control_transfer: Can not control transfer while in polling mode.");
|
||||
|
||||
LOG_LN_STEP("Queueing transfer TRBs...");
|
||||
return length - (control_transfer_exec_sync(setup, data, length)->status & 0xFFFFFF);
|
||||
}
|
||||
|
||||
tr[tr_i].parameter = setup;
|
||||
tr[tr_i].status = sizeof(setup);
|
||||
XHCI_TRB_TRB_TYPE_W(&tr[tr_i], XHCI_TRB_TYPE_TRANSFER_SETUP_STAGE);
|
||||
BITS_W(tr[tr_i].control, 17, 16, 3);
|
||||
BITS_W(tr[tr_i].control, 6, 6, 1);
|
||||
XHCI_TRB_CYCLE_W(&tr[tr_i], tr_cycle);
|
||||
xhci_endpoint_t xhci_open_endpoint(xhci_slot_t slot, uint8_t address, uint8_t type, uint16_t max_packet_size, uint8_t interval) {
|
||||
LOG_LN_INFO("Opening endpoint slot %d address %hhx...", slot, address);
|
||||
|
||||
LOG_VAL_TRACE(tr[tr_i].parameter, "%lx");
|
||||
LOG_VAL_TRACE(tr[tr_i].status, "%x");
|
||||
LOG_VAL_TRACE(tr[tr_i].control, "%x");
|
||||
ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist.");
|
||||
ASSERT(tsf_r_attached_slot == slot, "xhci_open_endpoint: Slot is not attached.");
|
||||
ASSERT(!ep_r_attached_endpoint, "xhci_open_endpoint: Can only open one endpoint at a time.");
|
||||
ASSERT(max_packet_size <= XHCI_EP_CTX_MAX_PACKET_SIZE, "xhci_open_endpoint: Packet size too big.")
|
||||
|
||||
tr_i = (tr_i + 1) % RING_LENGTH;
|
||||
XHCI_TRB_CYCLE_W(&tr[RING_LENGTH], tr_cycle);
|
||||
tr_cycle ^= (tr_i == 0);
|
||||
uint8_t ep_num = address & 0x0F;
|
||||
uint8_t ep_dir = (address >> 7) & 1;
|
||||
uint8_t dci = ep_num * 2 + ep_dir;
|
||||
|
||||
tr[tr_i].parameter = (uint64_t)VIRT_TO_PHYS(data);
|
||||
tr[tr_i].status = length;
|
||||
XHCI_TRB_TRB_TYPE_W(&tr[tr_i], XHCI_TRB_TYPE_TRANSFER_DATA_STAGE);
|
||||
BITS_W(tr[tr_i].control, 16, 16, 1);
|
||||
XHCI_TRB_CYCLE_W(&tr[tr_i], tr_cycle);
|
||||
LOG_LN_INFO("Setting up endpoint ring...");
|
||||
RING_WRAP(ep);
|
||||
XHCI_TRB_CYCLE_W(&ep_r[RING_LENGTH], ep_r_cycle);
|
||||
|
||||
LOG_VAL_TRACE(tr[tr_i].parameter, "%lx");
|
||||
LOG_VAL_TRACE(tr[tr_i].status, "%x");
|
||||
LOG_VAL_TRACE(tr[tr_i].control, "%x");
|
||||
LOG_LN_INFO("Configuring endpoint...");
|
||||
memory_set(0, sizeof(xhci_input_ctx_t), &input_ctx);
|
||||
input_ctx.slot = device_ctx.slot;
|
||||
input_ctx.control.add_flags = (1 << 0) | (1 << dci);
|
||||
XHCI_SLOT_CTX_CTX_ENTRIES_W(&input_ctx.slot, dci);
|
||||
|
||||
tr_i = (tr_i + 1) % RING_LENGTH;
|
||||
XHCI_TRB_CYCLE_W(&tr[RING_LENGTH], tr_cycle);
|
||||
tr_cycle ^= (tr_i == 0);
|
||||
XHCI_EP_CTX_EP_TYPE_W(&input_ctx.ep[dci - 1], type);
|
||||
XHCI_EP_CTX_MAX_PACKET_SIZE_W(&input_ctx.ep[dci - 1], max_packet_size);
|
||||
XHCI_EP_CTX_INTERVAL_W(&input_ctx.ep[dci - 1], interval);
|
||||
input_ctx.ep[dci - 1].dequeue = (uint64_t)VIRT_TO_PHYS(ep_r) | ep_r_cycle;
|
||||
|
||||
xhci_trb_t *tre = &tr[tr_i];
|
||||
xhci_trb_t cmd_ce;
|
||||
memory_set(0, sizeof(xhci_trb_t), &cmd_ce);
|
||||
XHCI_TRB_TRB_TYPE_W(&cmd_ce, XHCI_TRB_TYPE_COMMAND_CONFIGURE_ENDPOINT);
|
||||
cmd_ce.parameter = (uint64_t)VIRT_TO_PHYS(&input_ctx);
|
||||
XHCI_TRB_SLOT_ID_W(&cmd_ce, slot);
|
||||
command_exec_sync(&cmd_ce);
|
||||
|
||||
XHCI_TRB_TRB_TYPE_W(&tr[tr_i], XHCI_TRB_TYPE_TRANSFER_STATUS_STAGE);
|
||||
BITS_W(tr[tr_i].control, 16, 16, 0);
|
||||
BITS_W(tr[tr_i].control, 5, 5, 1);
|
||||
XHCI_TRB_CYCLE_W(&tr[tr_i], tr_cycle);
|
||||
ep_r_attached_endpoint = dci;
|
||||
ep_transfer_size = max_packet_size;
|
||||
|
||||
tr_i = (tr_i + 1) % RING_LENGTH;
|
||||
XHCI_TRB_CYCLE_W(&tr[RING_LENGTH], tr_cycle);
|
||||
tr_cycle ^= (tr_i == 0);
|
||||
return dci;
|
||||
}
|
||||
|
||||
LOG_LN_STEP("Ringing slot doorbell...");
|
||||
db_regs[tr_attached_slot] = 1;
|
||||
uint16_t xhci_read_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint, uint16_t max, void *to) {
|
||||
LOG_LN_STEP("Reading from endpoint %hhx...", endpoint);
|
||||
|
||||
LOG_LN_STEP("Waiting for completion...");
|
||||
while (1) {
|
||||
xhci_trb_t *cmp = &er[er_i];
|
||||
while (XHCI_TRB_CYCLE_R(cmp) != er_cycle)
|
||||
;
|
||||
ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist.");
|
||||
ASSERT(tsf_r_attached_slot == slot, "xhci_read_endpoint: Slot is not attached.");
|
||||
ASSERT(ep_r_attached_endpoint == endpoint, "xhci_read_endpoint: Endpoint not open.");
|
||||
|
||||
er_i = (er_i + 1) % RING_LENGTH;
|
||||
intr_regs->erdp = (uint64_t)(VIRT_TO_PHYS(&er[er_i]));
|
||||
er_cycle ^= (er_i == 0);
|
||||
if (ep_processed_events == RING_LENGTH) {
|
||||
LOG_LN_STEP("Event batch finished, posting new transfer requests...");
|
||||
ep_processed_events = 0;
|
||||
|
||||
if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION || (void *)cmp->parameter != VIRT_TO_PHYS(tre)) {
|
||||
ASSERT(ep_r_i == 0, "xhci_read_endpoint: Endpoint ring cycle broken.");
|
||||
for (uint64_t i = 0; i < RING_LENGTH; i++) {
|
||||
xhci_trb_t *ere = &ep_r[i];
|
||||
|
||||
ere->parameter = (uint64_t)VIRT_TO_PHYS(&ep_buffer[i]);
|
||||
ere->status = ep_transfer_size;
|
||||
XHCI_TRB_TRB_TYPE_W(ere, XHCI_TRB_TYPE_TRANSFER_NORMAL);
|
||||
XHCI_TRB_IOC_W(ere, 1);
|
||||
XHCI_TRB_CYCLE_W(ere, ep_r_cycle);
|
||||
RING_ADVANCE(ep);
|
||||
}
|
||||
|
||||
db_regs[tsf_r_attached_slot] = ep_r_attached_endpoint;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t transferred = 0;
|
||||
while (XHCI_TRB_CYCLE_R(&evt_r[evt_r_i]) == evt_r_cycle && !transferred) {
|
||||
xhci_trb_t *cmp = &evt_r[evt_r_i];
|
||||
|
||||
if (XHCI_TRB_TRB_TYPE_R(cmp) != XHCI_TRB_TYPE_EVENT_TRANSFER_COMPLETION) {
|
||||
LOG_LN_STEP("Received irrelevant event, skipping...");
|
||||
} else {
|
||||
LOG_LN_STEP("Received transfer completion event...");
|
||||
|
||||
LOG_VAL_TRACE(cmp->parameter, "%lx");
|
||||
LOG_VAL_TRACE(cmp->status, "%x");
|
||||
LOG_VAL_TRACE(cmp->control, "%x");
|
||||
|
||||
ASSERT(XHCI_TRB_COMPLETION_CODE(cmp) == 1, "xhci_control_transfer: Transfer failed.");
|
||||
} else if (XHCI_TRB_COMPLETION_CODE(cmp) != 1) {
|
||||
LOG_LN_STEP("Received unsuccessful transfer event, skipping...");
|
||||
ep_processed_events++;
|
||||
|
||||
LOG_LN_STEP("Done.");
|
||||
LOG_VAL_TRACE(cmp->parameter, "%lx");
|
||||
LOG_VAL_TRACE(cmp->status, "%x");
|
||||
LOG_VAL_TRACE(cmp->control, "%x");
|
||||
} else {
|
||||
LOG_LN_STEP("Received transfer event, returning...");
|
||||
ep_processed_events++;
|
||||
|
||||
return length - (cmp->status & 0xFFFFFF);
|
||||
xhci_trb_t *cmd = PHYS_TO_VIRT(cmp->parameter);
|
||||
uint16_t size = max < cmd->status ? max : (uint16_t)cmd->status;
|
||||
memory_copy(PHYS_TO_VIRT(cmd->parameter), size, to);
|
||||
|
||||
transferred = size;
|
||||
}
|
||||
|
||||
RING_ADVANCE(evt);
|
||||
}
|
||||
|
||||
return transferred;
|
||||
}
|
||||
|
||||
void xhci_close_endpoint(xhci_slot_t slot, xhci_endpoint_t endpoint) {
|
||||
LOG_LN_INFO("Closing endpoind %hhx...", endpoint);
|
||||
|
||||
ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist.");
|
||||
ASSERT(tsf_r_attached_slot == slot, "xhci_close_endpoint: Slot is not attached.");
|
||||
ASSERT(ep_r_attached_endpoint == endpoint, "xhci_close_endpoint: Endpoint not open.");
|
||||
|
||||
ep_r_attached_endpoint = NUL;
|
||||
|
||||
xhci_trb_t cmd_se;
|
||||
memory_set(0, sizeof(xhci_trb_t), &cmd_se);
|
||||
XHCI_TRB_TRB_TYPE_W(&cmd_se, XHCI_TRB_TYPE_COMMAND_STOP_ENDPOINT);
|
||||
XHCI_TRB_SLOT_ID_W(&cmd_se, slot);
|
||||
XHCI_TRB_ENDPOINT_ID_W(&cmd_se, endpoint);
|
||||
command_exec_sync(&cmd_se);
|
||||
}
|
||||
|
||||
void xhci_detach(xhci_slot_t slot) {
|
||||
LOG_LN_STEP("Detaching slot %d...", slot);
|
||||
|
||||
ASSERT(tr_attached_slot == slot, "xhci_detach: Slot not attached.");
|
||||
ASSERT(slot < MAX_SLOTS, "xhci_control_transfer: Slot does not exist.");
|
||||
ASSERT(tsf_r_attached_slot == slot, "xhci_detach: Slot not attached.");
|
||||
ASSERT(!ep_r_attached_endpoint, "xhci_detach: Can not detach slot while in polling mode.");
|
||||
|
||||
xhci_trb_t cmd_ds;
|
||||
memory_set(0, sizeof(xhci_trb_t), &cmd_ds);
|
||||
@@ -495,7 +655,7 @@ void xhci_detach(xhci_slot_t slot) {
|
||||
command_exec_sync(&cmd_ds);
|
||||
|
||||
dcbaa[slot] = 0;
|
||||
tr_attached_slot = 0;
|
||||
tsf_r_attached_slot = 0;
|
||||
|
||||
LOG_LN_STEP("Done.");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user