/* * returns cc of command in question (pointed to by `address`) * caller should abort command if cc is TIMEOUT */ int xhci_wait_for_command_done(xhci_t *const xhci, const trb_t *const address, const int clear_event) { /* * The Address Device Command should take most time, as it has to * communicate with the USB device. Set address processing shouldn't * take longer than 50ms (at the slave). Let's take a timeout of * 100ms. */ unsigned long timeout_us = 100 * 1000; /* 100ms */ int cc = TIMEOUT; while (xhci_wait_for_event_type(xhci, TRB_EV_CMD_CMPL, &timeout_us)) { if ((xhci->er.cur->ptr_low == virt_to_phys(address)) && (xhci->er.cur->ptr_high == 0)) { cc = TRB_GET(CC, xhci->er.cur); break; } xhci_handle_command_completion_event(xhci); } if (!timeout_us) { xhci_debug("Warning: Timed out waiting for TRB_EV_CMD_CMPL.\n"); } else if (clear_event) { xhci_advance_event_ring(xhci); } xhci_update_event_dq(xhci); return cc; }
void xhci_handle_events(xhci_t *const xhci) { while (xhci_event_ready(&xhci->er)) xhci_handle_event(xhci); xhci_update_event_dq(xhci); }
static void xhci_handle_host_controller_event(xhci_t *const xhci) { const trb_t *const ev = xhci->er.cur; const int cc = TRB_GET(CC, ev); switch (cc) { case CC_EVENT_RING_FULL_ERROR: xhci_debug("Event ring full! (@%p)\n", xhci->er.cur); /* * If we get here, we have processed the whole queue: * xHC pushes this event, when it sees the ring full, * full of other events. * IMO it's save and necessary to update the dequeue * pointer here. */ xhci_advance_event_ring(xhci); xhci_update_event_dq(xhci); break; default: xhci_debug("Warning: Spurious host controller event: %d\n", cc); xhci_advance_event_ring(xhci); break; } }
void xhci_advance_event_ring(xhci_t *const xhci) { xhci->er.cur++; xhci->er.adv = 1; if (xhci->er.cur == xhci->er.last) { xhci_spew("Roll over in event ring\n"); xhci->er.cur = xhci->er.ring; xhci->er.ccs ^= 1; xhci_update_event_dq(xhci); } }
/* returns cc of command in question (pointed to by `address`) */ int xhci_wait_for_command_aborted(xhci_t *const xhci, const trb_t *const address) { /* * Specification says that something might be seriously wrong, if * we don't get a response after 5s. Still, let the caller decide, * what to do then. */ unsigned long timeout_us = 5 * 1000 * 1000; /* 5s */ int cc = TIMEOUT; /* * Expects two command completion events: * The first with CC == COMMAND_ABORTED should point to address, * the second with CC == COMMAND_RING_STOPPED should point to new dq. */ while (xhci_wait_for_event_type(xhci, TRB_EV_CMD_CMPL, &timeout_us)) { if ((xhci->er.cur->ptr_low == virt_to_phys(address)) && (xhci->er.cur->ptr_high == 0)) { cc = TRB_GET(CC, xhci->er.cur); xhci_advance_event_ring(xhci); break; } xhci_handle_command_completion_event(xhci); } if (!timeout_us) xhci_debug("Warning: Timed out waiting for COMMAND_ABORTED.\n"); while (xhci_wait_for_event_type(xhci, TRB_EV_CMD_CMPL, &timeout_us)) { if (TRB_GET(CC, xhci->er.cur) == CC_COMMAND_RING_STOPPED) { xhci->cr.cur = phys_to_virt(xhci->er.cur->ptr_low); xhci_advance_event_ring(xhci); break; } xhci_handle_command_completion_event(xhci); } if (!timeout_us) xhci_debug("Warning: Timed out " "waiting for COMMAND_RING_STOPPED.\n"); xhci_update_event_dq(xhci); return cc; }
/* returns cc of transfer for given slot/endpoint pair */ int xhci_wait_for_transfer(xhci_t *const xhci, const int slot_id, const int ep_id) { xhci_spew("Waiting for transfer on ID %d EP %d\n", slot_id, ep_id); /* 2s for all types of transfers */ /* TODO: test, wait longer? */ unsigned long timeout_us = 2 * 1000 * 1000; int cc = TIMEOUT; while (xhci_wait_for_event_type(xhci, TRB_EV_TRANSFER, &timeout_us)) { if (TRB_GET(ID, xhci->er.cur) == slot_id && TRB_GET(EP, xhci->er.cur) == ep_id) { cc = TRB_GET(CC, xhci->er.cur); xhci_advance_event_ring(xhci); break; } xhci_handle_transfer_event(xhci); } if (!timeout_us) xhci_debug("Warning: Timed out waiting for TRB_EV_TRANSFER.\n"); xhci_update_event_dq(xhci); return cc; }
static void xhci_reinit (hci_t *controller) { xhci_t *const xhci = XHCI_INST(controller); if (xhci_wait_ready(xhci)) return; /* Enable all available slots */ xhci->opreg->config = xhci->max_slots_en; /* Set DCBAA */ xhci->opreg->dcbaap_lo = virt_to_phys(xhci->dcbaa); xhci->opreg->dcbaap_hi = 0; /* Initialize command ring */ xhci_init_cycle_ring(&xhci->cr, COMMAND_RING_SIZE); xhci_debug("command ring @%p (0x%08x)\n", xhci->cr.ring, virt_to_phys(xhci->cr.ring)); xhci->opreg->crcr_lo = virt_to_phys(xhci->cr.ring) | CRCR_RCS; xhci->opreg->crcr_hi = 0; /* Make sure interrupts are disabled */ xhci->opreg->usbcmd &= ~USBCMD_INTE; /* Initialize event ring */ xhci_reset_event_ring(&xhci->er); xhci_debug("event ring @%p (0x%08x)\n", xhci->er.ring, virt_to_phys(xhci->er.ring)); xhci_debug("ERST Max: 0x%lx -> 0x%lx entries\n", xhci->capreg->ERST_Max, 1 << xhci->capreg->ERST_Max); memset((void*)xhci->ev_ring_table, 0x00, sizeof(erst_entry_t)); xhci->ev_ring_table[0].seg_base_lo = virt_to_phys(xhci->er.ring); xhci->ev_ring_table[0].seg_base_hi = 0; xhci->ev_ring_table[0].seg_size = EVENT_RING_SIZE; /* Initialize primary interrupter */ xhci->hcrreg->intrrs[0].erstsz = 1; xhci_update_event_dq(xhci); /* erstba has to be written at last */ xhci->hcrreg->intrrs[0].erstba_lo = virt_to_phys(xhci->ev_ring_table); xhci->hcrreg->intrrs[0].erstba_hi = 0; xhci_start(controller); #ifdef USB_DEBUG int i; for (i = 0; i < 32; ++i) { xhci_debug("NOOP run #%d\n", i); trb_t *const cmd = xhci_next_command_trb(xhci); TRB_SET(TT, cmd, TRB_CMD_NOOP); xhci_post_command(xhci); /* Wait for result in event ring */ xhci_wait_for_command_done(xhci, cmd, 1); xhci_debug("Command ring is %srunning\n", (xhci->opreg->crcr_lo & CRCR_CRR) ? "" : "not "); } #endif }