示例#1
0
/* handle standard types:
 * - command completion event
 * - port status change event
 * - transfer event
 * - host controller event
 */
static void
xhci_handle_event(xhci_t *const xhci)
{
	const trb_t *const ev = xhci->er.cur;

	const int trb_type = TRB_GET(TT, ev);
	switch (trb_type) {
		/* Either pass along the event or advance event ring */
	case TRB_EV_TRANSFER:
		xhci_handle_transfer_event(xhci);
		break;
	case TRB_EV_CMD_CMPL:
		xhci_handle_command_completion_event(xhci);
		break;
	case TRB_EV_PORTSC:
		xhci_debug("Port Status Change Event for %d: %d\n",
			   TRB_GET(PORT, ev), TRB_GET(CC, ev));
		/* We ignore the event as we look for the PORTSC
		   registers instead, at a time when it suits _us_. */
		xhci_advance_event_ring(xhci);
		break;
	case TRB_EV_HOST:
		xhci_handle_host_controller_event(xhci);
		break;
	default:
		xhci_debug("Warning: Spurious event: %d, Completion Code: %d\n",
			   trb_type, TRB_GET(CC, ev));
		xhci_advance_event_ring(xhci);
		break;
	}
}
示例#2
0
static trb_t *
xhci_next_trb(trb_t *cur, int *const pcs)
{
	++cur;
	while (TRB_GET(TT, cur) == TRB_LINK) {
		if (pcs && TRB_GET(TC, cur))
			*pcs ^= 1;
		cur = phys_to_virt(cur->ptr_low);
	}
	return cur;
}
示例#3
0
static void
xhci_handle_command_completion_event(xhci_t *const xhci)
{
	const trb_t *const ev = xhci->er.cur;

	xhci_debug("Warning: Spurious command completion event:\n"
		   "  Pointer: 0x%08x%08x\n"
		   "       CC: %d\n"
		   "  Slot ID: %d\n"
		   "    Cycle: %d\n",
		   ev->ptr_high, ev->ptr_low,
		   TRB_GET(CC, ev), TRB_GET(ID, ev), ev->control & TRB_CYCLE);
	xhci_advance_event_ring(xhci);
}
示例#4
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;
}
示例#5
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;
	}
}
示例#6
0
static const trb_t *
xhci_next_trb(const trb_t *const cur)
{
	if (TRB_GET(TT, cur) == TRB_LINK)
		return (!cur->ptr_low) ? NULL : phys_to_virt(cur->ptr_low);
	else
		return cur + 1;
}
示例#7
0
static void
xhci_enqueue_trb(transfer_ring_t *const tr)
{
	const int chain = TRB_GET(CH, tr->cur);
	TRB_SET(C, tr->cur, tr->pcs);
	++tr->cur;

	while (TRB_GET(TT, tr->cur) == TRB_LINK) {
		xhci_spew("Handling LINK pointer\n");
		const int tc = TRB_GET(TC, tr->cur);
		TRB_SET(CH, tr->cur, chain);
		TRB_SET(C, tr->cur, tr->pcs);
		tr->cur = phys_to_virt(tr->cur->ptr_low);
		if (tc)
			tr->pcs ^= 1;
	}
}
示例#8
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;
}
示例#9
0
/* read one intr-packet from queue, if available. extend the queue for new input.
   return NULL if nothing new available.
   Recommended use: while (data=poll_intr_queue(q)) process(data);
 */
static u8 *
xhci_poll_intr_queue(void *const q)
{
	if (!q)
		return NULL;

	intrq_t *const intrq = (intrq_t *)q;
	endpoint_t *const ep = intrq->ep;
	xhci_t *const xhci = XHCI_INST(ep->dev->controller);

	/* TODO: Reset interrupt queue if it gets halted? */

	xhci_handle_events(xhci);

	u8 *reqdata = NULL;
	while (!reqdata && intrq->ready) {
		const int ep_id = xhci_ep_id(ep);
		transfer_ring_t *const tr =
			xhci->dev[ep->dev->address].transfer_rings[ep_id];

		/* Fetch the request's buffer */
		reqdata = phys_to_virt(intrq->next->ptr_low);

		/* Enqueue the last (spare) TRB and ring doorbell */
		xhci_enqueue_trb(tr);
		xhci->dbreg[ep->dev->address] = ep_id;

		/* Reuse the current buffer for the next spare TRB */
		xhci_clear_trb(tr->cur, tr->pcs);
		tr->cur->ptr_low = virt_to_phys(reqdata);
		tr->cur->ptr_high = 0;
		TRB_SET(TL,	tr->cur, intrq->size);
		TRB_SET(TT,	tr->cur, TRB_NORMAL);
		TRB_SET(ISP,	tr->cur, 1);
		TRB_SET(IOC,	tr->cur, 1);

		/* Check if anything was transferred */
		const size_t read = TRB_GET(TL, intrq->next);
		if (!read)
			reqdata = NULL;
		else if (read < intrq->size)
			/* At least zero it, poll interface is rather limited */
			memset(reqdata + read, 0x00, intrq->size - read);

		/* Advance the interrupt queue */
		if (intrq->ready == intrq->next)
			/* This was last TRB being ready */
			intrq->ready = NULL;
		intrq->next = xhci_next_trb(intrq->next, NULL);
	}

	return reqdata;
}
示例#10
0
static void
xhci_handle_transfer_event(xhci_t *const xhci)
{
	const trb_t *const ev = xhci->er.cur;

	const int cc = TRB_GET(CC, ev);
	const int id = TRB_GET(ID, ev);
	const int ep = TRB_GET(EP, ev);

	devinfo_t *di;
	intrq_t *intrq;

	if (id && id <= xhci->max_slots_en &&
			(di = DEVINFO_FROM_XHCI(xhci, id)) &&
			(intrq = di->interrupt_queues[ep])) {
		/* It's a running interrupt endpoint */
		intrq->ready = phys_to_virt(ev->ptr_low);
		if (cc == CC_SUCCESS || cc == CC_SHORT_PACKET) {
			TRB_SET(TL, intrq->ready,
				intrq->size - TRB_GET(EVTL, ev));
		} else {
			xhci_debug("Interrupt Transfer failed: %d\n",
				   cc);
			TRB_SET(TL, intrq->ready, 0);
		}
	} else if (cc == CC_STOPPED || cc == CC_STOPPED_LENGTH_INVALID) {
		/* Ignore 'Forced Stop Events' */
	} else {
		xhci_debug("Warning: "
			   "Spurious transfer event for ID %d, EP %d:\n"
			   "  Pointer: 0x%08x%08x\n"
			   "       TL: 0x%06x\n"
			   "       CC: %d\n",
			   id, ep,
			   ev->ptr_high, ev->ptr_low,
			   TRB_GET(EVTL, ev), cc);
	}
	xhci_advance_event_ring(xhci);
}
示例#11
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;
}
示例#12
0
static unsigned long
xhci_wait_for_event_type(xhci_t *const xhci,
		    const int trb_type,
		    unsigned long *const timeout_us)
{
	while (xhci_wait_for_event(&xhci->er, timeout_us)) {
		if (TRB_GET(TT, xhci->er.cur) == trb_type)
			break;

		xhci_handle_event(xhci);
	}
	return *timeout_us;
}
示例#13
0
void
xhci_post_command(xhci_t *const xhci)
{
	xhci_debug("Command %d (@%p)\n",
		   TRB_GET(TT, xhci->cr.cur), xhci->cr.cur);

	TRB_SET(C, xhci->cr.cur, xhci->cr.pcs);
	++xhci->cr.cur;

	/* pass command trb to hardware */
	wmb();
	/* Ring the doorbell */
	xhci->dbreg[0] = 0;

	while (TRB_GET(TT, xhci->cr.cur) == TRB_LINK) {
		xhci_debug("Handling LINK pointer (@%p)\n", xhci->cr.cur);
		const int tc = TRB_GET(TC, xhci->cr.cur);
		TRB_SET(C, xhci->cr.cur, xhci->cr.pcs);
		xhci->cr.cur = phys_to_virt(xhci->cr.cur->ptr_low);
		if (tc)
			xhci->cr.pcs ^= 1;
	}
}
示例#14
0
int
xhci_cmd_enable_slot(xhci_t *const xhci, int *const slot_id)
{
	trb_t *const cmd = xhci_next_command_trb(xhci);
	TRB_SET(TT, cmd, TRB_CMD_ENABLE_SLOT);
	xhci_post_command(xhci);

	int cc = xhci_wait_for_command(xhci, cmd, 0);
	if (cc >= 0) {
		if (cc == CC_SUCCESS) {
			*slot_id = TRB_GET(ID, xhci->er.cur);
			if (*slot_id > xhci->max_slots_en)
				cc = CONTROLLER_ERROR;
		}
		xhci_advance_event_ring(xhci);
		xhci_handle_events(xhci);
	}
	return cc;
}
示例#15
0
/* create and hook-up an intr queue into device schedule */
static void *
xhci_create_intr_queue(endpoint_t *const ep,
		       const int reqsize, const int reqcount,
		       const int reqtiming)
{
	/* reqtiming: We ignore it and use the interval from the
		      endpoint descriptor configured earlier. */

	xhci_t *const xhci = XHCI_INST(ep->dev->controller);
	const int slot_id = ep->dev->address;
	const int ep_id = xhci_ep_id(ep);
	transfer_ring_t *const tr = xhci->dev[slot_id].transfer_rings[ep_id];

	if (reqcount > (TRANSFER_RING_SIZE - 2)) {
		xhci_debug("reqcount is too high, at most %d supported\n",
			   TRANSFER_RING_SIZE - 2);
		return NULL;
	}
	if (reqsize > 0x10000) {
		xhci_debug("reqsize is too large, at most 64KiB supported\n");
		return NULL;
	}
	if (xhci->dev[slot_id].interrupt_queues[ep_id]) {
		xhci_debug("Only one interrupt queue per endpoint supported\n");
		return NULL;
	}

	/* Allocate intrq structure and reqdata chunks */

	intrq_t *const intrq = malloc(sizeof(*intrq));
	if (!intrq) {
		xhci_debug("Out of memory\n");
		return NULL;
	}

	int i;
	int pcs = tr->pcs;
	trb_t *cur = tr->cur;
	for (i = 0; i < reqcount; ++i) {
		if (TRB_GET(C, cur) == pcs) {
			xhci_debug("Not enough empty TRBs\n");
			goto _free_return;
		}
		void *const reqdata = xhci_align(1, reqsize);
		if (!reqdata) {
			xhci_debug("Out of memory\n");
			goto _free_return;
		}
		xhci_clear_trb(cur, pcs);
		cur->ptr_low = virt_to_phys(reqdata);
		cur->ptr_high = 0;
		TRB_SET(TL,	cur, reqsize);
		TRB_SET(TT,	cur, TRB_NORMAL);
		TRB_SET(ISP,	cur, 1);
		TRB_SET(IOC,	cur, 1);

		cur = xhci_next_trb(cur, &pcs);
	}

	intrq->size	= reqsize;
	intrq->count	= reqcount;
	intrq->next	= tr->cur;
	intrq->ready	= NULL;
	intrq->ep	= ep;
	xhci->dev[slot_id].interrupt_queues[ep_id] = intrq;

	/* Now enqueue all the prepared TRBs but the last
	   and ring the doorbell. */
	for (i = 0; i < (reqcount - 1); ++i)
		xhci_enqueue_trb(tr);
	xhci->dbreg[slot_id] = ep_id;

	return intrq;

_free_return:
	cur = tr->cur;
	for (--i; i >= 0; --i) {
		free(phys_to_virt(cur->ptr_low));
		cur = xhci_next_trb(cur, NULL);
	}
	free(intrq);
	return NULL;
}