示例#1
0
/*
 * 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;
}
示例#2
0
void
xhci_handle_events(xhci_t *const xhci)
{
	while (xhci_event_ready(&xhci->er))
		xhci_handle_event(xhci);
	xhci_update_event_dq(xhci);
}
示例#3
0
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;
	}
}
示例#4
0
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);
	}
}
示例#5
0
/* 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;
}
示例#6
0
/* 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;
}
示例#7
0
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
}