PERFORCE change 181631 for review

Hans Petter Selasky hselasky at FreeBSD.org
Sat Jul 31 09:20:52 UTC 2010


http://p4web.freebsd.org/@@181631?ac=10

Change 181631 by hselasky at hselasky_laptop001 on 2010/07/31 09:20:10

	USB controller (XHCI):
		- import current work in progress

Affected files ...

.. //depot/projects/usb/src/sys/dev/usb/controller/xhci.c#2 edit
.. //depot/projects/usb/src/sys/dev/usb/controller/xhci.h#4 edit
.. //depot/projects/usb/src/sys/dev/usb/controller/xhcireg.h#4 edit

Differences ...

==== //depot/projects/usb/src/sys/dev/usb/controller/xhci.c#2 (text+ko) ====

@@ -31,3 +31,1734 @@
  * and the USB 3.0 spec at
  * http://www.usb.org/developers/docs/usb_30_spec_060910.zip
  */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/dev/usb/controller/xhci.c $");
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/linker_set.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#define	USB_DEBUG_VAR xhcidebug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/xhci.h>
+#include <dev/usb/controller/xhcireg.h>
+
+#define	XHCI_BUS2SC(bus) \
+   ((struct xhci_softc *)(((uint8_t *)(bus)) - \
+    ((uint8_t *)&(((struct xhci_softc *)0)->sc_bus))))
+
+#ifdef USB_DEBUG
+static int xhcidebug = 0;
+
+SYSCTL_NODE(_hw_usb, OID_AUTO, xhci, CTLFLAG_RW, 0, "USB XHCI");
+SYSCTL_INT(_hw_usb_xhci, OID_AUTO, debug, CTLFLAG_RW,
+    &xhcidebug, 0, "Debug level");
+
+TUNABLE_INT("hw.usb.xhci.debug", &xhcidebug);
+
+#endif
+
+#define	XHCI_INTR_ENDPT 1
+
+struct xhci_std_temp {
+	struct xhci_softc *sc;
+	struct usb_page_cache *pc;
+	struct xhci_td *td;
+	struct xhci_td *td_next;
+	uint32_t len;
+	uint32_t offset;
+	uint32_t max_packet_size;
+	uint32_t average;
+	uint16_t isoc_delta;
+	uint16_t isoc_frame;
+	uint8_t	shortpkt;
+	uint8_t multishort;
+	uint8_t last_frame;
+	uint8_t trb_type;
+	uint8_t direction;
+};
+
+void
+xhci_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb)
+{
+
+
+}
+
+usb_error_t
+xhci_reset(struct xhci_softc *sc)
+{
+
+
+}
+
+static usb_error_t
+xhci_hcreset(struct xhci_softc *sc)
+{
+
+
+}
+
+usb_error_t
+xhci_init(struct xhci_softc *sc)
+{
+
+
+}
+
+void
+xhci_detach(struct xhci_softc *sc)
+{
+
+
+}
+
+void
+xhci_suspend(struct xhci_softc *sc)
+{
+
+
+}
+
+void
+xhci_resume(struct xhci_softc *sc)
+{
+
+
+}
+
+void
+xhci_shutdown(struct xhci_softc *sc)
+{
+
+
+}
+
+static void
+xhci_transfer_intr_enqueue(struct usb_xfer *xfer)
+{
+	/* check for early completion */
+	if (xhci_check_transfer(xfer))
+		return;
+
+	/* put transfer on interrupt queue */
+	usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
+}
+
+static usb_error_t
+xhci_generic_done_sub(struct usb_xfer *xfer)
+{
+	struct xhci_td *td;
+	struct xhci_td *td_alt_next;
+	uint32_t len;
+	uint8_t status;
+
+	td = xfer->td_transfer_cache;
+	td_alt_next = td->alt_next;
+
+	if (xfer->aframes != xfer->nframes)
+		usbd_xfer_set_frame_len(xfer, xfer->aframes, 0);
+
+	while (1) {
+
+		usb_pc_cpu_invalidate(td->page_cache);
+
+		status = td->status;
+		len = td->remainder;
+
+		DPRINTFN(4, "xfer=%p[%u/%u] len=%u/%u status=%u\n",
+		    xfer, (unsigned int)xfer->aframes,
+		    (unsigned int)xfer->nframes,
+		    (unsigned int)len, (unsigned int)td->len,
+		    (unsigned int)status);
+
+		/*
+	         * Verify the status length and
+		 * add the length to "frlengths[]":
+	         */
+		if (len > td->len) {
+			/* should not happen */
+			DPRINTF("Invalid status length, "
+			    "0x%04x/0x%04x bytes\n", len, td->len);
+			status = XHCI_TRB_ERROR_LENGTH;
+		} else if (xfer->aframes != xfer->nframes) {
+			xfer->frlengths[xfer->aframes] += td->len - len;
+		}
+		/* Check for last transfer */
+		if (((void *)td) == xfer->td_transfer_last) {
+			td = NULL;
+			break;
+		}
+		/* Check for transfer error */
+		if ((status != XHCI_TRB_ERROR_SHORT_PKT) &&
+		    (status != XHCI_TRB_ERROR_SUCCESS)) {
+			/* the transfer is finished */
+			td = NULL;
+			break;
+		}
+		/* Check for short transfer */
+		if (len > 0) {
+			if (xfer->flags_int.short_frames_ok || 
+			    xfer->flags_int.isochronous_xfr) {
+				/* follow alt next */
+				td = td->alt_next;
+			} else {
+				/* the transfer is finished */
+				td = NULL;
+			}
+			break;
+		}
+		td = td->obj_next;
+
+		if (td->alt_next != td_alt_next) {
+			/* this USB frame is complete */
+			break;
+		}
+	}
+
+	/* update transfer cache */
+
+	xfer->td_transfer_cache = td;
+
+	return ((status == XHCI_TRB_ERROR_STALL) ? USB_ERR_STALLED : 
+	    ((status != XHCI_TRB_ERROR_SHORT_PKT) && 
+	    (status != XHCI_TRB_ERROR_SUCCESS)) ? USB_ERR_IOERROR :
+	    USB_ERR_NORMAL_COMPLETION);
+}
+
+static void
+xhci_generic_done(struct usb_xfer *xfer)
+{
+	usb_error_t err = 0;
+
+	DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n",
+	    xfer, xfer->endpoint);
+
+	/* reset scanner */
+
+	xfer->td_transfer_cache = xfer->td_transfer_first;
+
+	if (xfer->flags_int.control_xfr) {
+
+		if (xfer->flags_int.control_hdr)
+			err = xhci_generic_done_sub(xfer);
+
+		xfer->aframes = 1;
+
+		if (xfer->td_transfer_cache == NULL)
+			goto done;
+	}
+
+	while (xfer->aframes != xfer->nframes) {
+
+		err = xhci_generic_done_sub(xfer);
+		xfer->aframes++;
+
+		if (xfer->td_transfer_cache == NULL)
+			goto done;
+	}
+
+	if (xfer->flags_int.control_xfr &&
+	    !xfer->flags_int.control_act)
+		err = xhci_generic_done_sub(xfer);
+
+done:
+	xhci_device_done(xfer, err);
+}
+
+/*------------------------------------------------------------------------*
+ *	xhci_check_transfer
+ *------------------------------------------------------------------------*/
+static void
+xhci_check_transfer(struct xhci_softc *sc, struct xhci_trb *trb)
+{
+	struct usb_xfer *xfer;
+	struct xhci_td *td;
+	uint64_t td_event;
+	uint32_t temp;
+	uint32_t actlen;
+	uint8_t status;
+
+	/* decode TRB */
+	td_event = trb->qwTrb0;
+	temp = le32toh(trb->dwTrb2);
+
+	/* try to find the USB transfer that generated the event */
+	TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+
+		td = xfer->td_transfer_cache;
+
+		if (td_event == td->td_event) {
+
+			actlen = XHCI_TRB_2_ACTLEN_GET(temp);
+			status = XHCI_TRB_2_ERROR_GET(temp);
+
+			usb_pc_cpu_invalidate(td->page_cache);
+
+			/* "td->remainder" is verified later */
+			td->remainder -= actlen;
+			td->status = status;
+
+			usb_pc_cpu_flush(td->page_cache);
+
+			/*
+			 * 1) Last transfer descriptor makes the
+			 * transfer done
+			 */
+			if (((void *)td) == xfer->td_transfer_last) {
+				xhci_generic_done(xfer);
+				break;
+			}
+
+			/*
+			 * 2) Any kind of error makes the transfer
+			 * done
+			 */
+			if ((status != XHCI_TRB_ERROR_SHORT_PKT) &&
+			    (status != XHCI_TRB_ERROR_SUCCESS)) {
+				xhci_generic_done(xfer);
+				break;
+			}
+
+			/*
+			 * 3) If there is no alternate next transfer,
+			 * a short packet also makes the transfer done
+			 */
+			if (td->remainder > 0) {
+				if (xfer->flags_int.short_frames_ok) {
+					/* follow alt next */
+					if (td->alt_next != NULL) {
+						xfer->td_transfer_cache = td->alt_next;
+						break;
+					}
+				}
+				xhci_generic_done(xfer);
+				break;
+			}
+
+			/*
+			 * 4) Transfer complete - go to next TD
+			 */
+			xfer->td_transfer_cache = td->obj_next;
+			break;		/* there should only be one match */
+		}
+	}
+}
+
+static void
+xhci_pcd_enable(struct xhci_softc *sc)
+{
+
+}
+
+static void
+xhci_interrupt_poll(struct xhci_softc *sc)
+{
+	struct usb_xfer *xfer;
+
+repeat:
+	TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+		/*
+		 * check if transfer is transferred
+		 */
+		if (xhci_check_transfer(xfer)) {
+			/* queue has been modified */
+			goto repeat;
+		}
+	}
+}
+
+/*------------------------------------------------------------------------*
+ *	xhci_interrupt - XHCI interrupt handler
+ *------------------------------------------------------------------------*/
+void
+xhci_interrupt(struct xhci_softc *sc)
+{
+
+
+}
+
+/*------------------------------------------------------------------------*
+ *	xhci_timeout - XHCI timeout handler
+ *------------------------------------------------------------------------*/
+static void
+xhci_timeout(void *arg)
+{
+	struct usb_xfer *xfer = arg;
+
+	DPRINTF("xfer=%p\n", xfer);
+
+	USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+	/* transfer is transferred */
+	xhci_device_done(xfer, USB_ERR_TIMEOUT);
+}
+
+static void
+xhci_do_poll(struct usb_bus *bus)
+{
+	struct xhci_softc *sc = XHCI_BUS2SC(bus);
+
+	USB_BUS_LOCK(&sc->sc_bus);
+	xhci_interrupt_poll(sc);
+	USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+xhci_setup_generic_chain_sub(struct xhci_std_temp *temp)
+{
+	struct usb_page_search buf_res;
+	struct xhci_td *td;
+	struct xhci_td *td_next;
+	struct xhci_td *td_alt_next;
+	uint32_t buf_offset;
+	uint32_t average;
+	uint32_t len_old;
+	uint32_t dword;
+	uint8_t shortpkt_old;
+	uint8_t precompute;
+	uint8_t x;
+
+	td_alt_next = NULL;
+	buf_offset = 0;
+	shortpkt_old = temp->shortpkt;
+	len_old = temp->len;
+	precompute = 1;
+
+restart:
+
+	td = temp->td;
+	td_next = temp->td_next;
+
+	while (1) {
+
+		if (temp->len == 0) {
+
+			if (temp->shortpkt)
+				break;
+
+			/* send a Zero Length Packet, ZLP, last */
+
+			temp->shortpkt = 1;
+			average = 0;
+
+		} else {
+
+			average = temp->average;
+
+			if (temp->len < average) {
+				if (temp->len % temp->max_packet_size) {
+					temp->shortpkt = 1;
+				}
+				average = temp->len;
+			}
+		}
+
+		if (td_next == NULL)
+			panic("%s: out of XHCI transfer descriptors!", __FUNCTION__);
+
+		/* get next TD */
+
+		td = td_next;
+		td_next = td->obj_next;
+
+		/* check if we are pre-computing */
+
+		if (precompute) {
+
+			/* update remaining length */
+
+			temp->len -= average;
+
+			continue;
+		}
+		/* fill out current TD */
+
+		td->len = average;
+		td->remainder = average;
+		td->status = 0;
+
+		/* update remaining length */
+
+		temp->len -= average;
+
+		/* reset TRB index */
+
+		x = 0;
+
+		if (temp->trb_type == XHCI_TRB_TYPE_SETUP_STAGE) {
+			/* immediate data */
+
+			if (average > 8)
+				average = 8;
+
+			td->td_trb[0].qwTrb0 = 0;
+
+			usbd_copy_out(temp->pc, temp->offset + buf_offset, 
+			   (uint8_t *)(uintptr_t)&td->td_trb[0].qwTrb0, average);
+
+			dword = XHCI_TRB_2_BYTES_SET(8) |
+			    XHCI_TRB_2_TDSZ_SET(0) |
+			    XHCI_TRB_2_IRQ_SET(0);
+
+			td->td_trb[0].dwTrb2 = htole32(dword);
+
+			dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SETUP_STAGE) |
+			  XHCI_TRB_3_IDT_BIT;
+
+			/* check wLength */
+			if (td->td_trb[0].qwTrb0 & htole64(0xFFFF00000000ULL)) {
+				if (td->td_trb[0].qwTrb0 & htole64(1))
+					dword |= XHCI_TRB_3_TRT_IN;
+				else
+					dword |= XHCI_TRB_3_TRT_OUT;
+			}
+
+			td->td_trb[0].dwTrb3 = htole32(dword);
+
+			x++;
+
+		} else do {
+
+			uint32_t npkt;
+
+			/* fill out buffer pointers */
+
+			if (average == 0) {
+				npkt = 1;
+				memset(&buf_res, 0, sizeof(buf_res));
+			} else {
+				usbd_get_page(temp->pc, temp->offset + buf_offset, &buf_res);
+
+				/* get length to end of page */
+				if (buf_res.length > average)
+					buf_res.length = average;
+
+				/* check for maximum length */
+				if (buf_res.length > XHCI_TD_PAGE_SIZE)
+					buf_res.length = XHCI_TD_PAGE_SIZE;
+
+				/* setup npkt */
+				npkt = (average + temp->max_packet_size - 1) / temp->max_packet_size;
+				if (npkt > 31)
+					npkt = 31;
+			}
+
+			/* fill out TRB's */
+			td->td_trb[x].qwTrb0 =
+			    htole64((uint64_t)buf_res.physaddr);
+
+			dword =
+			  XHCI_TRB_2_BYTES_SET(buf_res.length) |
+			  XHCI_TRB_2_TDSZ_SET(npkt) | 
+			  XHCI_TRB_2_IRQ_SET(0);
+
+			td->td_trb[x].dwTrb2 = htole32(dword);
+
+			dword = XHCI_TRB_3_CHAIN_BIT |
+			  XHCI_TRB_3_TYPE_SET(temp->trb_type) | 
+			  XHCI_TRB_3_FRID_SET(temp->isoc_frame);
+
+			if (temp->direction == UE_DIR_IN)
+				dword |= XHCI_TRB_3_DIR_IN;
+
+			td->td_trb[x].dwTrb3 = htole32(dword);
+
+			average -= buf_res.length;
+			buf_offset += buf_res.length;
+			x++;
+
+		} while (average != 0);
+
+		/* store number of data TRB's */
+
+		td->ntrb = x;
+
+		/* compute event pointer */
+
+		if (1) {
+			uint64_t td_event;
+
+			td_event = le64toh(td->td_self);
+			td_event += x * sizeof(td->td_trb[0]);
+			td->td_event = htole64(td_event);
+		}
+
+		/* fill out link TRB */
+
+		if (td_next != NULL) {
+			/* link the current TD with the next one */
+			td->td_trb[x].qwTrb0 = td_next->td_self;
+		} else {
+			/* this field will get updated later */
+		}
+
+		dword = XHCI_TRB_2_IRQ_SET(0);
+
+		td->td_trb[x].dwTrb2 = htole32(dword);
+
+		dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) |
+		    XHCI_TRB_3_IOC_BIT | XHCI_TRB_3_CHAIN_BIT;
+
+		td->td_trb[x].dwTrb3 = htole32(dword);
+
+		td->alt_next = td_alt_next;
+
+		usb_pc_cpu_flush(td->page_cache);
+	}
+
+	if (precompute) {
+		precompute = 0;
+
+		/* setup alt next pointer, if any */
+		if (temp->last_frame) {
+			td_alt_next = NULL;
+		} else {
+			/* we use this field internally */
+			td_alt_next = td_next;
+		}
+
+		/* restore */
+		temp->shortpkt = shortpkt_old;
+		temp->len = len_old;
+		goto restart;
+	} else {
+		if (temp->multishort == 0) {
+			/* remove chain bit and clear TD SIZE - end of frame */
+			td->td_trb[td->ntrb - 1].dwTrb2 &= ~htole32(XHCI_TRB_2_TDSZ_SET(15));
+			td->td_trb[td->ntrb - 1].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT);
+			td->td_trb[td->ntrb].dwTrb2 &= ~htole32(XHCI_TRB_2_TDSZ_SET(15));
+			td->td_trb[td->ntrb].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT);
+			usb_pc_cpu_flush(td->page_cache);
+		}
+	}
+	temp->td = td;
+	temp->td_next = td_next;
+}
+
+static void
+xhci_setup_generic_chain(struct usb_xfer *xfer)
+{
+	struct xhci_std_temp temp;
+	struct xhci_td *td;
+	uint32_t x;
+	uint32_t y;
+
+	temp.average = xfer->max_hc_frame_size;
+	temp.max_packet_size = xfer->max_packet_size;
+	temp.sc = XHCI_BUS2SC(xfer->xroot->bus);
+	temp.pc = NULL;
+	temp.last_frame = 0;
+	temp.offset = 0;
+	temp.multishort = xfer->flags_int.isochronous_xfr ||
+	    xfer->flags_int.short_frames_ok;
+
+	/* toggle the DMA set we are using */
+	xfer->flags_int.curr_dma_set ^= 1;
+
+	/* get next DMA set */
+	td = xfer->td_start[xfer->flags_int.curr_dma_set];
+
+	temp.td = NULL;
+	temp.td_next = td;
+
+	xfer->td_transfer_first = td;
+	xfer->td_transfer_cache = td;
+
+	if (xfer->flags_int.isochronous_xfr) {
+
+		uint8_t shift;
+
+		x = XREAD4(temp.sc, runt, XHCI_MFINDEX);
+
+		switch (usbd_get_speed(xfer->xroot->udev)) {
+		case USB_SPEED_FULL:
+			shift = 3;
+			temp.isoc_delta = 8;	/* 1ms */
+			break;
+		default:
+			shift = usbd_xfer_get_fps_shift(xfer);
+			temp.isoc_delta = 1U << shift;
+			break;
+		}
+
+		x += temp.isoc_delta - 1;
+		x &= ~(temp.isoc_delta - 1);
+		x += xfer->endpoint->usb_uframe;
+
+		y = XHCI_MFINDEX_GET(x - xfer->endpoint->isoc_next);
+
+		if ((xfer->endpoint->is_synced == 0) ||
+		    (y < (xfer->nframes << shift))) {
+			/*
+			 * If there is data underflow or the pipe
+			 * queue is empty we schedule the transfer a
+			 * few frames ahead of the current frame
+			 * position. Else two isochronous transfers
+			 * might overlap.
+			 */
+			xfer->endpoint->isoc_next = XHCI_MFINDEX_GET(x + (3 * 8));
+			xfer->endpoint->is_synced = 1;
+			DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
+		}
+
+		x = 0;
+		temp.isoc_frame = xfer->endpoint->isoc_next;
+		temp.trb_type = XHCI_TRB_TYPE_ISOCH;
+
+		xfer->endpoint->isoc_next += xfer->nframes << shift;
+
+	} else if (xfer->flags_int.control_xfr) {
+
+		/* check if we should prepend a setup message */
+
+		if (xfer->flags_int.control_hdr) {
+
+			temp.len = xfer->frlengths[0];
+			temp.pc = xfer->frbuffers + 0;
+			temp.shortpkt = temp.len ? 1 : 0;
+			temp.trb_type = XHCI_TRB_TYPE_SETUP_STAGE;
+			temp.direction = 0;
+
+			/* check for last frame */
+			if (xfer->nframes == 1) {
+				/* no STATUS stage yet, SETUP is last */
+				if (xfer->flags_int.control_act)
+					temp.last_frame = 1;
+			}
+
+			xhci_setup_generic_chain_sub(&temp);
+		}
+		x = 1;
+		temp.isoc_delta = 0;
+		temp.isoc_frame = 0;
+		temp.trb_type = XHCI_TRB_TYPE_DATA_STAGE;
+	} else {
+		x = 0;
+		temp.isoc_delta = 0;
+		temp.isoc_frame = 0;
+		temp.trb_type = XHCI_TRB_TYPE_NORMAL;
+	}
+
+	if (x != xfer->nframes) {
+                /* setup page_cache pointer */
+                temp.pc = xfer->frbuffers + x;
+		/* set endpoint direction */
+		temp.direction = UE_GET_DIR(xfer->endpointno);
+	}
+
+	while (x != xfer->nframes) {
+
+		/* DATA0 / DATA1 message */
+
+		temp.len = xfer->frlengths[x];
+
+		x++;
+
+		if (x == xfer->nframes) {
+			if (xfer->flags_int.control_xfr) {
+				/* no STATUS stage yet, DATA is last */
+				if (xfer->flags_int.control_act)
+					temp.last_frame = 1;
+			} else {
+				temp.last_frame = 1;
+			}
+		}
+		if (temp.len == 0) {
+
+			/* make sure that we send an USB packet */
+
+			temp.shortpkt = 0;
+
+		} else if (xfer->flags_int.isochronous_xfr) {
+
+			/* isochronous transfers don't have short packet termination */
+
+			temp.shortpkt = 1;
+
+			/* isochronous transfers have a transfer limit */
+
+			if (temp.len > xfer->max_frame_size)
+				temp.len = xfer->max_frame_size;
+		} else {
+
+			/* regular data transfer */
+
+			temp.shortpkt = xfer->flags.force_short_xfer ? 0 : 1;
+		}
+
+		xhci_setup_generic_chain_sub(&temp);
+
+		if (xfer->flags_int.isochronous_xfr) {
+			temp.offset += xfer->frlengths[x - 1];
+			temp.isoc_frame += temp.isoc_delta;
+		} else {
+			/* get next Page Cache pointer */
+			temp.pc = xfer->frbuffers + x;
+		}
+	}
+
+	/* check if we should append a status stage */
+
+	if (xfer->flags_int.control_xfr &&
+	    !xfer->flags_int.control_act) {
+
+		/*
+		 * Send a DATA1 message and invert the current
+		 * endpoint direction.
+		 */
+		temp.direction = UE_GET_DIR(xfer->endpointno) ^ UE_DIR_IN;
+		temp.len = 0;
+		temp.pc = NULL;
+		temp.shortpkt = 0;
+		temp.last_frame = 1;
+		temp.trb_type = XHCI_TRB_TYPE_STATUS_STAGE;
+
+		xhci_setup_generic_chain_sub(&temp);
+	}
+
+	td = temp.td;
+
+	/* remove chain bit and clear TD SIZE - end of frame */
+	td->td_trb[td->ntrb - 1].dwTrb2 &= ~htole32(XHCI_TRB_2_TDSZ_SET(15));
+	td->td_trb[td->ntrb - 1].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT);
+	td->td_trb[td->ntrb].dwTrb2 &= ~htole32(XHCI_TRB_2_TDSZ_SET(15));
+	td->td_trb[td->ntrb].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT);
+
+	/* link the current TD with the next one */
+	td->td_trb[td->ntrb].qwTrb0 = XXX;
+
+	usb_pc_cpu_flush(td->page_cache);
+
+	/* must have at least one frame! */
+
+	xfer->td_transfer_last = td;
+
+	if (xfer->xroot->udev->flags.self_suspended == 0) {
+		EHCI_APPEND_QH(qh, *qh_last); XXX;
+	}
+}
+
+static void
+xhci_root_intr(struct xhci_softc *sc)
+{
+
+}
+
+/*------------------------------------------------------------------------*
+ *	xhci_device_done - XHCI done handler
+ *
+ * NOTE: This function can be called two times in a row on
+ * the same USB transfer. From close and from interrupt.
+ *------------------------------------------------------------------------*/
+static void
+xhci_device_done(struct usb_xfer *xfer, usb_error_t error)
+{
+	DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n",
+	    xfer, xfer->endpoint, error);
+
+	/* dequeue transfer and start next transfer */
+	usbd_transfer_done(xfer, error);
+}
+
+/*------------------------------------------------------------------------*
+ * XHCI data transfer support (generic type)
+ *------------------------------------------------------------------------*/
+static void
+xhci_device_generic_open(struct usb_xfer *xfer)
+{
+	if (xfer->flags_int.isochronous_xfr)
+		usb_hs_bandwidth_alloc(xfer);
+}
+
+static void
+xhci_device_generic_close(struct usb_xfer *xfer)
+{
+	xhci_device_done(xfer, USB_ERR_CANCELLED);
+
+	if (xfer->flags_int.isochronous_xfr)
+		usb_hs_bandwidth_free(xfer);
+}
+
+static void
+xhci_device_generic_enter(struct usb_xfer *xfer)
+{
+	/* setup TD's and QH */
+	xhci_setup_generic_chain(xfer);
+
+	/* put transfer on interrupt queue */
+	xhci_transfer_intr_enqueue(xfer);
+}
+
+static void
+xhci_device_generic_start(struct usb_xfer *xfer)
+{
+	/* start timeout, if any */
+	if (xfer->timeout != 0)
+		usbd_transfer_timeout_ms(xfer, &xhci_timeout, xfer->timeout);
+}
+
+struct usb_pipe_methods xhci_device_generic_methods =
+{
+	.open = xhci_device_generic_open,
+	.close = xhci_device_generic_close,
+	.enter = xhci_device_generic_enter,
+	.start = xhci_device_generic_start,
+};
+
+
+/*------------------------------------------------------------------------*
+ * xhci root HUB support
+ *------------------------------------------------------------------------*
+ * Simulate a hardware HUB by handling all the necessary requests.
+ *------------------------------------------------------------------------*/
+
+#define	HSETW(ptr, val) ptr[0] = (uint8_t)(val), ptr[1] = (uint8_t)((val) >> 8)
+
+static const
+struct usb_device_descriptor xhci_devd =
+{
+	.bLength = sizeof(xhci_devd),
+	.bDescriptorType = UDESC_DEVICE,	/* type */
+	HSETW(.bcdUSB, 0x0300),			/* USB version */
+	.bDeviceClass = UDCLASS_HUB,		/* class */
+	.bDeviceSubClass = UDSUBCLASS_HUB,	/* subclass */
+	.bDeviceProtocol = UDPROTO_SSHUB,	/* protocol */
+	.bMaxPacketSize = 9,			/* max packet size */
+	HSETW(.idVendor, 0x0000),		/* vendor */
+	HSETW(.idProduct, 0x0000),		/* product */
+	HSETW(.bcdDevice, 0x0100),		/* device version */
+	.iManufacturer = 1,
+	.iProduct = 2,
+	.iSerialNumber = 0,
+	.bNumConfigurations = 1,		/* # of configurations */
+};
+
+static const
+struct xhci_bos_desc xhci_bosd = {
+	.bosd = {
+		.bLength = sizeof(xhci_bosd.bosd),
+		.bDescriptorType = UDESC_BOS,
+		HSETW(.wTotalLength, sizeof(xhci_bosd)),
+		.bNumDeviceCaps = 3,
+	},
+	.usb2extd = {
+		.bLength = sizeof(xhci_bosd.usb2extd),
+		.bDescriptorType = 1,
+		.bDevCapabilityType = 2,
+		.bmAttributes = 2,
+	},
+	.usbdcd = {
+		.bLength = sizeof(xhci_bosd.usbdcd),
+		.bDescriptorType = UDESC_DEVICE_CAPABILITY,
+		.bDevCapabilityType = 3,
+		.bmAttributes = XXX,
+		HSETW(.wSpeedsSupported, 0x000C),
+		.bFunctionalitySupport = 8,
+		.bU1DevExitLat = 128,	/* dummy - not used */
+		.bU2DevExitLat = 128,	/* dummy - not used */
+	},
+	.cidd = {
+		.bLength = sizeof(xhci_bosd.cidd),
+		.bDescriptorType = 1,
+		.bDevCapabilityType = 4,
+		.bReserved = 0,
+		.bContainerID = XXX,
+	},
+};
+
+static const
+struct xhci_config_desc xhci_confd = {
+	.confd = {
+		.bLength = sizeof(xhci_confd.confd),
+		.bDescriptorType = UDESC_CONFIG,
+		.wTotalLength[0] = sizeof(xhci_confd),
+		.bNumInterface = 1,
+		.bConfigurationValue = 1,
+		.iConfiguration = 0,
+		.bmAttributes = UC_SELF_POWERED,
+		.bMaxPower = 0		/* max power */
+	},
+	.ifcd = {
+		.bLength = sizeof(xhci_confd.ifcd),

>>> TRUNCATED FOR MAIL (1000 lines) <<<


More information about the p4-projects mailing list