Hi,

The attached patch that adds support for high speed isochronous 
transfer, which is taken from NetBSD.

        Kevin
diff -ruN sys.orig/dev/usb/ehci.c sys/dev/usb/ehci.c
--- sys.orig/dev/usb/ehci.c	2008-08-15 17:16:07.000000000 +0800
+++ sys/dev/usb/ehci.c	2008-08-15 17:15:44.000000000 +0800
@@ -129,7 +129,10 @@
 			u_int length;
 		} bulk;
 		/* Iso pipe */
-		/* XXX */
+		struct {
+			u_int next_frame;
+			u_int cur_xfers;
+		} isoc;
 	} u;
 };
 
@@ -139,6 +142,8 @@
 static int		ehci_intr1(ehci_softc_t *);
 static void		ehci_waitintr(ehci_softc_t *, usbd_xfer_handle);
 static void		ehci_check_intr(ehci_softc_t *, struct ehci_xfer *);
+static void		ehci_check_qh_intr(ehci_softc_t *, struct ehci_xfer *);
+static void		ehci_check_itd_intr(ehci_softc_t *, struct ehci_xfer *);
 static void		ehci_idone(struct ehci_xfer *);
 static void		ehci_timeout(void *);
 static void		ehci_timeout_task(void *);
@@ -205,6 +210,12 @@
 static void		ehci_free_sqtd_chain(ehci_softc_t *, ehci_soft_qh_t *,
 			    ehci_soft_qtd_t *, ehci_soft_qtd_t *);
 
+static ehci_soft_itd_t	*ehci_alloc_itd(ehci_softc_t *);
+static void		ehci_free_itd(ehci_softc_t *, ehci_soft_itd_t *);
+static void 		ehci_rem_free_itd_chain(ehci_softc_t *, 
+			    struct ehci_xfer *);
+static void 		ehci_abort_isoc_xfer(usbd_xfer_handle, usbd_status);
+
 static usbd_status	ehci_device_request(usbd_xfer_handle xfer);
 
 static usbd_status	ehci_device_setintr(ehci_softc_t *, ehci_soft_qh_t *,
@@ -228,6 +239,10 @@
 static void		ehci_dump_sqtd(ehci_soft_qtd_t *);
 static void		ehci_dump_qtd(ehci_qtd_t *);
 static void		ehci_dump_sqh(ehci_soft_qh_t *);
+#if notyet
+static void		ehci_dump_sitd(struct ehci_soft_itd *);
+static void		ehci_dump_itd(struct ehci_soft_itd *);
+#endif
 #ifdef DIAGNOSTIC
 static void		ehci_dump_exfer(struct ehci_xfer *);
 #endif
@@ -414,8 +429,19 @@
 		return (err);
 	DPRINTF(("%s: flsize=%d\n", device_get_nameunit(sc->sc_bus.bdev),sc->sc_flsize));
 	sc->sc_flist = KERNADDR(&sc->sc_fldma, 0);
+
+	for (i = 0; i < sc->sc_flsize; i++) {
+		sc->sc_flist[i] = EHCI_NULL;
+	}
+
 	EOWRITE4(sc, EHCI_PERIODICLISTBASE, DMAADDR(&sc->sc_fldma, 0));
 
+	sc->sc_softitds = malloc(sc->sc_flsize * sizeof(ehci_soft_itd_t *),
+	    M_USB, M_NOWAIT | M_ZERO);
+	if (sc->sc_softitds == NULL)
+		return (ENOMEM);
+	LIST_INIT(&sc->sc_freeitds);
+
 	/* Set up the bus struct. */
 	sc->sc_bus.methods = &ehci_bus_methods;
 	sc->sc_bus.pipe_size = sizeof(struct ehci_pipe);
@@ -714,19 +740,31 @@
 void
 ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
 {
-	ehci_soft_qtd_t *sqtd, *lsqtd;
-	u_int32_t status;
+	int attr;
 
 	DPRINTFN(/*15*/2, ("ehci_check_intr: ex=%p\n", ex));
 
+	attr = ex->xfer.pipe->endpoint->edesc->bmAttributes;
+	if (UE_GET_XFERTYPE(attr) == UE_ISOCHRONOUS)
+		ehci_check_itd_intr(sc, ex);
+	else
+		ehci_check_qh_intr(sc, ex);
+}
+
+void
+ehci_check_qh_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
+{
+	ehci_soft_qtd_t *sqtd, *lsqtd;
+	u_int32_t status;
+
 	if (ex->sqtdstart == NULL) {
-		printf("ehci_check_intr: sqtdstart=NULL\n");
+		printf("ehci_check_qh_intr: not valid sqtd\n");
 		return;
 	}
 	lsqtd = ex->sqtdend;
 #ifdef DIAGNOSTIC
 	if (lsqtd == NULL) {
-		printf("ehci_check_intr: lsqtd==0\n");
+		printf("ehci_check_qh_intr: lsqtd==0\n");
 		return;
 	}
 #endif
@@ -761,6 +799,64 @@
 }
 
 void
+ehci_check_itd_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
+{
+	ehci_soft_itd_t *itd;
+	int i;
+
+	if (ex->itdstart == NULL) {
+		printf("ehci_check_itd_intr: not valid itd\n");
+		return;
+	}
+
+	itd = ex->itdend;
+#ifdef DIAGNOSTIC
+	if (itd == NULL) {
+		printf("ehci_check_itd_intr: itdend == 0\n");
+		return;
+	}
+#endif
+
+	/*
+	 * Step 1, check no active transfers in last itd, meaning we're finished
+	 */
+	for (i = 0; i < 8; i++) {
+		if (le32toh(itd->itd.itd_ctl[i]) & EHCI_ITD_ACTIVE)
+			break;
+	}
+
+	if (i == 8) {
+		goto done;	/* All 8 descriptors inactive, it's done */
+	}
+
+	/*
+	 * Step 2, check for errors in status bits, throughout chain...
+	 */
+
+	DPRINTFN(12, ("ehci_check_itd_intr: active ex=%p\n", ex));
+
+	for (itd = ex->itdstart; itd != ex->itdend; itd = itd->xfer_next) {
+		for (i = 0; i < 8; i++) {
+			if (le32toh(itd->itd.itd_ctl[i]) & (EHCI_ITD_BUF_ERR |
+			    EHCI_ITD_BABBLE | EHCI_ITD_ERROR))
+				break;
+		}
+		if (i != 8) { /* Error in one of the itds */
+			goto done;
+		}
+	} /* itd search loop */
+
+	DPRINTFN(12, ("ehci_check_itd_intr: ex %p itd %p still active\n", ex,
+	    ex->itdstart));
+	return;
+done:
+	DPRINTFN(12, ("ehci_check_itd_intr: ex=%p done\n", ex));
+	callout_stop(&ex->xfer.timeout_handle);
+	usb_rem_task(ex->xfer.pipe->device, &ex->abort_task);
+	ehci_idone(ex);
+}
+
+void
 ehci_idone(struct ehci_xfer *ex)
 {
 	usbd_xfer_handle xfer = &ex->xfer;
@@ -826,9 +922,56 @@
 	}
 
 	/* The transfer is done, compute actual length and status. */
+	if (UE_GET_XFERTYPE(xfer->pipe->endpoint->edesc->bmAttributes)
+	    == UE_ISOCHRONOUS) {
+		/* Isoc transfer */
+		struct ehci_soft_itd *itd;
+		int i, nframes, len, uframes;
+
+		nframes = 0;
+		actlen = 0;
+
+		switch (xfer->pipe->endpoint->edesc->bInterval) {
+		case 0:
+			panic("ehci: isoc xfer suddenly has 0 bInterval, "
+			    "invalid\n");
+		case 1: uframes = 1; break;
+		case 2: uframes = 2; break;
+		case 3: uframes = 4; break;
+		default: uframes = 8; break;
+		}
+
+		for (itd = ex->itdstart; itd != NULL; itd = itd->xfer_next) {
+			for (i = 0; i < 8; i += uframes) {
+				/* XXX - driver didn't fill in the frame full
+				 *   of uframes. This leads to scheduling
+				 *   inefficiencies, but working around
+				 *   this doubles complexity of tracking
+				 *   an xfer.
+				 */
+				if (nframes >= xfer->nframes)
+					break;
+
+				status = le32toh(itd->itd.itd_ctl[i]);
+				len = EHCI_ITD_GET_LEN(status);
+				xfer->frlengths[nframes++] = len;
+				actlen += len;
+			}
+			if (nframes >= xfer->nframes)
+				break;
+		}
+		xfer->actlen = actlen;
+		xfer->status = USBD_NORMAL_COMPLETION;
+
+		goto end;
+	}
+
+	/* Continue processing xfers using queue heads */
+
 	lsqtd = ex->sqtdend;
 	actlen = 0;
-	for (sqtd = ex->sqtdstart; sqtd != lsqtd->nextqtd; sqtd=sqtd->nextqtd) {
+	for (sqtd = ex->sqtdstart; sqtd != lsqtd->nextqtd; 
+	    sqtd =sqtd->nextqtd) {
 		nstatus = le32toh(sqtd->qtd.qtd_status);
 		if (nstatus & EHCI_QTD_ACTIVE)
 			break;
@@ -871,7 +1014,11 @@
 	} else {
 		xfer->status = USBD_NORMAL_COMPLETION;
 	}
-
+end:
+	/* XXX transfer_complete memcpys out transfer data (for in endpoints)
+	 * during this call, before methods->done is called: dma sync required
+	 * beforehand?
+	 */
 	usb_transfer_complete(xfer);
 	DPRINTFN(/*12*/2, ("ehci_idone: ex=%p done\n", ex));
 }
@@ -1319,11 +1466,51 @@
 	ehci_dump_qtd(&qh->qh_qtd);
 }
 
+#if notyet
+void
+ehci_dump_itd(struct ehci_soft_itd *itd)
+{
+	ehci_isoc_trans_t t;
+	ehci_isoc_bufr_ptr_t b, b2, b3;
+	int i;
+
+	printf("ITD: next phys=%X\n", itd->itd.itd_next);
+
+	for (i = 0; i < 8;i++) {
+		t = le32toh(itd->itd.itd_ctl[i]);
+		printf("ITDctl %d: stat=%X len=%X ioc=%X pg=%X offs=%X\n", i,
+		    EHCI_ITD_GET_STATUS(t), EHCI_ITD_GET_LEN(t),
+		    EHCI_ITD_GET_IOC(t), EHCI_ITD_GET_PG(t),
+		    EHCI_ITD_GET_OFFS(t));
+	}
+	printf("ITDbufr: ");
+	for (i = 0; i < 7; i++)
+		printf("%X,", EHCI_ITD_GET_BPTR(le32toh(itd->itd.itd_bufr[i])));
+
+	b = le32toh(itd->itd.itd_bufr[0]);
+	b2 = le32toh(itd->itd.itd_bufr[1]);
+	b3 = le32toh(itd->itd.itd_bufr[2]);
+	printf("\nep=%X daddr=%X dir=%d maxpkt=%X multi=%X\n",
+	    EHCI_ITD_GET_EP(b), EHCI_ITD_GET_DADDR(b), EHCI_ITD_GET_DIR(b2),
+	    EHCI_ITD_GET_MAXPKT(b2), EHCI_ITD_GET_MULTI(b3));
+}
+
+void
+ehci_dump_sitd(struct ehci_soft_itd *itd)
+{
+	printf("SITD %p next=%p prev=%p xfernext=%p physaddr=%X slot=%d\n",
+	    itd, itd->u.frame_list.next, itd->u.frame_list.prev,
+	    itd->xfer_next, itd->physaddr, itd->slot);
+}
+#endif
+
 #ifdef DIAGNOSTIC
-static void
+void
 ehci_dump_exfer(struct ehci_xfer *ex)
 {
-	printf("ehci_dump_exfer: ex=%p\n", ex);
+	printf("ehci_dump_exfer: ex=%p sqtdstart=%p end=%p itdstart=%p "
+	    "end=%p isdone=%d\n", ex, ex->sqtdstart, ex->sqtdend, ex->itdstart,
+	    ex->itdend, ex->isdone);
 }
 #endif
 #endif
@@ -1366,6 +1553,8 @@
 			pipe->methods = &ehci_root_intr_methods;
 			break;
 		default:
+			DPRINTF(("ehci_open: bad bEndpointAddress 0x%02x\n",
+			    ed->bEndpointAddress));
 			return (USBD_INVAL);
 		}
 		return (USBD_NORMAL_COMPLETION);
@@ -1379,42 +1568,46 @@
 	default: panic("ehci_open: bad device speed %d", dev->speed);
 	}
 	if (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_ISOCHRONOUS) {
-		printf("%s: *** WARNING: opening low/full speed device, this "
-		       "does not work yet.\n",
-		       device_get_nameunit(sc->sc_bus.bdev));
+		printf("%s: *** Error: opening low/full speed isoc device on"
+		    "ehci, this does not work yet. Feel free to implement\n",
+		    device_get_nameunit(sc->sc_bus.bdev));
 		DPRINTFN(1,("ehci_open: hshubaddr=%d hshubport=%d\n",
 			    hshubaddr, hshubport));
 		return USBD_INVAL;
 	}
 
 	naks = 8;		/* XXX */
-	sqh = ehci_alloc_sqh(sc);
-	if (sqh == NULL)
-		goto bad0;
-	/* qh_link filled when the QH is added */
-	sqh->qh.qh_endp = htole32(
-		EHCI_QH_SET_ADDR(addr) |
-		EHCI_QH_SET_ENDPT(UE_GET_ADDR(ed->bEndpointAddress)) |
-		EHCI_QH_SET_EPS(speed) |
-		(xfertype == UE_CONTROL ? EHCI_QH_DTC : 0) |
-		EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) |
-		(speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ?
-		 EHCI_QH_CTL : 0) |
-		EHCI_QH_SET_NRL(naks)
-		);
-	sqh->qh.qh_endphub = htole32(
-		EHCI_QH_SET_MULT(1) |
-		EHCI_QH_SET_HUBA(hshubaddr) |
-		EHCI_QH_SET_PORT(hshubport) |
-		EHCI_QH_SET_CMASK(0x1c) |
-		EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x01 : 0)
-		);
-	sqh->qh.qh_curqtd = EHCI_NULL;
-	/* The overlay qTD was already set up by ehci_alloc_sqh(). */
-	sqh->qh.qh_qtd.qtd_status =
-	    htole32(EHCI_QTD_SET_TOGGLE(pipe->endpoint->savedtoggle));
-
-	epipe->sqh = sqh;
+	/* Allocate sqh for everything, save isoc xfers */
+	if (xfertype != UE_ISOCHRONOUS) {
+		sqh = ehci_alloc_sqh(sc);
+		if (sqh == NULL)
+			goto bad0;
+		/* qh_link filled when the QH is added */
+		sqh->qh.qh_endp = htole32(
+		    EHCI_QH_SET_ADDR(addr) |
+		    EHCI_QH_SET_ENDPT(UE_GET_ADDR(ed->bEndpointAddress)) |
+		    EHCI_QH_SET_EPS(speed) |
+		    (xfertype == UE_CONTROL ? EHCI_QH_DTC : 0) |
+		    EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) |
+		    (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ?
+		    EHCI_QH_CTL : 0) |
+		    EHCI_QH_SET_NRL(naks)
+		    );
+		sqh->qh.qh_endphub = htole32(
+		    EHCI_QH_SET_MULT(1) |
+		    EHCI_QH_SET_HUBA(hshubaddr) |
+		    EHCI_QH_SET_PORT(hshubport) |
+		    EHCI_QH_SET_CMASK(0x1c) |
+		    EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x01 : 0)
+		    );
+		sqh->qh.qh_curqtd = EHCI_NULL;
+		/* The overlay qTD was already set up by ehci_alloc_sqh(). */
+		sqh->qh.qh_qtd.qtd_status =
+	    	    htole32(EHCI_QTD_SET_TOGGLE(pipe->endpoint->savedtoggle));
+		epipe->sqh = sqh;
+	} else {
+		sqh = NULL;
+	}
 
 	switch (xfertype) {
 	case UE_CONTROL:
@@ -1445,14 +1638,29 @@
 		return (ehci_device_setintr(sc, sqh, ival));
 	case UE_ISOCHRONOUS:
 		pipe->methods = &ehci_device_isoc_methods;
-		return (USBD_INVAL);
+		if (ed->bInterval == 0 || ed->bInterval > 16) {
+			printf("ehci: opening pipe with invalid bInterval\n");
+			err = USBD_INVAL;
+			goto bad1;
+		}
+		if (UGETW(ed->wMaxPacketSize) == 0) {
+			printf("ehci: zero length endpoint open request\n");
+			err = USBD_INVAL;
+			goto bad1;
+		}
+		epipe->u.isoc.next_frame = 0;
+		epipe->u.isoc.cur_xfers = 0;
+		break;
 	default:
+		DPRINTF(("ehci: bad xfer type %d\n", xfertype));
 		return (USBD_INVAL);
 	}
 	return (USBD_NORMAL_COMPLETION);
 
  bad1:
-	ehci_free_sqh(sc, sqh);
+	if (sqh != NULL)
+		ehci_free_sqh(sc, sqh);
+	return (err);
  bad0:
 	return (USBD_NOMEM);
 }
@@ -1569,6 +1777,49 @@
 	DPRINTFN(2,("ehci_sync_hc: exit\n"));
 }
 
+/*Call at splusb*/
+void
+ehci_rem_free_itd_chain(ehci_softc_t *sc, struct ehci_xfer *exfer)
+{
+	struct ehci_soft_itd *itd, *prev;
+
+	prev = NULL;
+
+	if (exfer->itdstart == NULL || exfer->itdend == NULL)
+		panic("ehci isoc xfer being freed, but with no itd chain\n");
+
+	for (itd = exfer->itdstart; itd != NULL; itd = itd->xfer_next) {
+		prev = itd->u.frame_list.prev;
+		/* Unlink itd from hardware chain, or frame array */
+		if (prev == NULL) { /* We're at the table head */
+			sc->sc_softitds[itd->slot] = itd->u.frame_list.next;
+			sc->sc_flist[itd->slot] = itd->itd.itd_next;
+
+			if (itd->u.frame_list.next != NULL)
+				itd->u.frame_list.next->u.frame_list.prev = 
+				    NULL;
+		} else {
+			/* XXX this part is untested... */
+			prev->itd.itd_next = itd->itd.itd_next;
+			prev->u.frame_list.next = itd->u.frame_list.next;
+			if (itd->u.frame_list.next != NULL)
+				itd->u.frame_list.next->u.frame_list.prev = 
+				    prev;
+		}
+	}
+
+	prev = NULL;
+	for (itd = exfer->itdstart; itd != NULL; itd = itd->xfer_next) {
+		if (prev != NULL)
+			ehci_free_itd(sc, prev);
+		prev = itd;
+	}
+	if (prev)
+		ehci_free_itd(sc, prev);
+	exfer->itdstart = NULL;
+	exfer->itdend = NULL;
+}
+
 /***********/
 
 /*
@@ -2463,6 +2714,76 @@
 	}
 }
 
+ehci_soft_itd_t *
+ehci_alloc_itd(ehci_softc_t *sc)
+{
+	struct ehci_soft_itd *itd, *freeitd;
+	usbd_status err;
+	int i, s, offs, frindex, previndex;
+	usb_dma_t dma;
+
+	s = splusb();
+
+	/* Find an itd that wasn't freed this frame or last frame. This can
+	 * discard itds that were freed before frindex wrapped around
+	 * XXX - can this lead to thrashing? Could fix by enabling wrap-around
+	 *       interrupt and fiddling with list when that happens */
+	frindex = (EOREAD4(sc, EHCI_FRINDEX) + 1) >> 3;
+	previndex = (frindex != 0) ? frindex - 1 : sc->sc_flsize;
+
+	freeitd = NULL;
+	LIST_FOREACH(itd, &sc->sc_freeitds, u.free_list) {
+		if (itd == NULL)
+			break;
+		if (itd->slot != frindex && itd->slot != previndex) {
+			freeitd = itd;
+			break;
+		}
+	}
+
+	if (freeitd == NULL) {
+		DPRINTFN(2, ("ehci_alloc_itd allocating chunk\n"));
+		err = usb_allocmem(&sc->sc_bus, EHCI_ITD_SIZE * EHCI_ITD_CHUNK,
+		    EHCI_PAGE_SIZE, &dma);
+
+		if (err) {
+			DPRINTF(("ehci_alloc_itd, alloc returned %d\n", err));
+			return NULL;
+		}
+
+		for (i = 0; i < EHCI_ITD_CHUNK; i++) {
+			offs = i * EHCI_ITD_SIZE;
+			itd = KERNADDR(&dma, offs);
+			itd->physaddr = DMAADDR(&dma, offs);
+	 		itd->dma = dma;
+			itd->offs = offs;
+			LIST_INSERT_HEAD(&sc->sc_freeitds, itd, u.free_list);
+		}
+		freeitd = LIST_FIRST(&sc->sc_freeitds);
+	}
+
+	itd = freeitd;
+	LIST_REMOVE(itd, u.free_list);
+	memset(&itd->itd, 0, sizeof(ehci_itd_t));
+	itd->u.frame_list.next = NULL;
+	itd->u.frame_list.prev = NULL;
+	itd->xfer_next = NULL;
+	itd->slot = 0;
+	splx(s);
+
+	return (itd);
+}
+
+void
+ehci_free_itd(ehci_softc_t *sc, ehci_soft_itd_t *itd)
+{
+	int s;
+
+	s = splusb();
+	LIST_INSERT_AFTER(LIST_FIRST(&sc->sc_freeitds), itd, u.free_list);
+	splx(s);
+}
+
 /****************/
 
 /*
@@ -2522,7 +2843,7 @@
 		return;
 	}
 
-	if (xfer->device->bus->intr_context || !curproc)
+	if (xfer->device->bus->intr_context)
 		panic("ehci_abort_xfer: not in process context");
 
 	/*
@@ -2692,6 +3013,86 @@
 }
 
 void
+ehci_abort_isoc_xfer(usbd_xfer_handle xfer, usbd_status status)
+{
+	ehci_isoc_trans_t trans_status;
+	struct ehci_pipe *epipe;
+	struct ehci_xfer *exfer;
+	ehci_softc_t *sc;
+	struct ehci_soft_itd *itd;
+	int s, i;
+
+	epipe = (struct ehci_pipe *) xfer->pipe;
+	exfer = EXFER(xfer);
+	sc = (ehci_softc_t *)epipe->pipe.device->bus;
+
+	DPRINTF(("ehci_abort_isoc_xfer: xfer %p pipe %p\n", xfer, epipe));
+
+	if (sc->sc_dying) {
+		s = splusb();
+		xfer->status = status;
+		callout_stop(&xfer->timeout_handle);
+		usb_rem_task(epipe->pipe.device, &exfer->abort_task);
+		usb_transfer_complete(xfer);
+		splx(s);
+		return;
+	}
+
+	if (exfer->ehci_xfer_flags & EHCI_XFER_ABORTING) {
+		DPRINTFN(2, ("ehci_abort_isoc_xfer: already aborting\n"));
+
+#ifdef DIAGNOSTIC
+		if (status == USBD_TIMEOUT)
+			printf("ehci_abort_xfer: TIMEOUT while aborting\n");
+#endif
+
+		xfer->status = status;
+		DPRINTFN(2, ("ehci_abort_xfer: waiting for abort to finish\n"));
+		exfer->ehci_xfer_flags |= EHCI_XFER_ABORTWAIT;
+		while (exfer->ehci_xfer_flags & EHCI_XFER_ABORTING)
+			tsleep(&exfer->ehci_xfer_flags, PZERO, "ehciaw", 0);
+		return;
+	}
+	exfer->ehci_xfer_flags |= EHCI_XFER_ABORTING;
+
+	xfer->status = status;
+	callout_stop(&xfer->timeout_handle);
+	usb_rem_task(epipe->pipe.device, &exfer->abort_task);
+
+	s = splusb();
+	for (itd = exfer->itdstart; itd != NULL; itd = itd->xfer_next) {
+
+		for (i = 0; i < 8; i++) {
+			trans_status = le32toh(itd->itd.itd_ctl[i]);
+			trans_status &= ~EHCI_ITD_ACTIVE;
+			itd->itd.itd_ctl[i] = htole32(trans_status);
+		}
+
+	}
+	splx(s);
+
+        s = splusb();
+#ifdef USB_USE_SOFTINTR
+        sc->sc_softwake = 1;
+#endif /* USB_USE_SOFTINTR */
+        usb_schedsoftintr(&sc->sc_bus);
+#ifdef USB_USE_SOFTINTR
+        tsleep(&sc->sc_softwake, PZERO, "ehciab", 0);
+#endif /* USB_USE_SOFTINTR */
+        splx(s);
+
+#ifdef DIAGNOSTIC
+	exfer->isdone = 1;
+#endif
+	exfer->ehci_xfer_flags &= ~EHCI_XFER_ABORTING;
+	if (exfer->ehci_xfer_flags & EHCI_XFER_ABORTWAIT) {
+		exfer->ehci_xfer_flags &= ~EHCI_XFER_ABORTWAIT;
+		wakeup(&exfer->ehci_xfer_flags);
+	}
+	usb_transfer_complete(xfer);
+}
+
+void
 ehci_timeout_task(void *addr)
 {
 	usbd_xfer_handle xfer = addr;
@@ -3270,6 +3671,11 @@
 		DPRINTFN(1, ("ehci_device_intr_abort: remove\n"));
 		xfer->pipe->intrxfer = NULL;
 	}
+	/* 
+	 * XXX - abort_xfer uses ehci_sync_hc, which syncs via the advance
+	 *       async doorbell. That's dependant on the async list, wheras
+	 *       intr xfers are periodic, should not use this?
+	 */
 	ehci_abort_xfer(xfer, USBD_CANCELLED);
 }
 
@@ -3362,8 +3768,306 @@
 
 /************************/
 
-static usbd_status	ehci_device_isoc_transfer(usbd_xfer_handle xfer) { return USBD_IOERROR; }
-static usbd_status	ehci_device_isoc_start(usbd_xfer_handle xfer) { return USBD_IOERROR; }
-static void		ehci_device_isoc_abort(usbd_xfer_handle xfer) { }
-static void		ehci_device_isoc_close(usbd_pipe_handle pipe) { }
-static void		ehci_device_isoc_done(usbd_xfer_handle xfer) { }
+static usbd_status	
+ehci_device_isoc_transfer(usbd_xfer_handle xfer) 
+{
+	usbd_status err;
+
+	err = usb_insert_transfer(xfer);
+	if (err && err != USBD_IN_PROGRESS)
+		return (err);
+
+	return (ehci_device_isoc_start(xfer));
+}
+
+static usbd_status	
+ehci_device_isoc_start(usbd_xfer_handle xfer)
+{
+	struct ehci_pipe *epipe;
+	usbd_device_handle dev;
+	ehci_softc_t *sc;
+	struct ehci_xfer *exfer;
+	ehci_soft_itd_t *itd, *prev, *start, *stop;
+	usb_dma_t *dma_buf;
+	int i, j, k, frames, uframes, ufrperframe;
+	int s, trans_count, offs, total_length;
+	int frindex;
+
+	start = NULL;
+	prev = NULL;
+	itd = NULL;
+	trans_count = 0;
+	total_length = 0;
+	exfer = (struct ehci_xfer *) xfer;
+	sc = (ehci_softc_t *)xfer->pipe->device->bus;
+	dev = xfer->pipe->device;
+	epipe = (struct ehci_pipe *)xfer->pipe;
+
+	/*
+	 * To allow continuous transfers, above we start all transfers
+	 * immediately. However, we're still going to get usbd_start_next call
+	 * this when another xfer completes. So, check if this is already
+	 * in progress or not
+	 */
+
+	if (exfer->itdstart != NULL)
+		return (USBD_IN_PROGRESS);
+
+	DPRINTFN(2, ("ehci_device_isoc_start: xfer %p len %d flags %d\n",
+	    xfer, xfer->length, xfer->flags));
+
+	if (sc->sc_dying)
+		return (USBD_IOERROR);
+
+	/*
+	 * To avoid complication, don't allow a request right now that'll span
+	 * the entire frame table. To within 4 frames, to allow some leeway
+	 * on either side of where the hc currently is.
+	 */
+	if ((1 << (epipe->pipe.endpoint->edesc->bInterval)) *
+	    xfer->nframes >= (sc->sc_flsize - 4) * 8) {
+		printf("ehci: isoc descriptor requested that spans the entire"
+		    " frametable, too many frames\n");
+		return (USBD_INVAL);
+	}
+
+#ifdef DIAGNOSTIC
+	if (xfer->rqflags & URQ_REQUEST)
+		panic("ehci_device_isoc_start: request\n");
+
+	if (!exfer->isdone)
+		printf("ehci_device_isoc_start: not done, ex = %p\n", exfer);
+	exfer->isdone = 0;
+#endif
+
+	/*
+	 * Step 1: Allocate and initialize itds, how many do we need?
+	 * One per transfer if interval >= 8 microframes, fewer if we use
+	 * multiple microframes per frame.
+	 */
+
+	i = epipe->pipe.endpoint->edesc->bInterval;
+	if (i > 16 || i == 0) {
+		/* Spec page 271 says intervals > 16 are invalid */
+		DPRINTF(("ehci_device_isoc_start: bInvertal %d invalid\n", i));
+		return (USBD_INVAL);
+	}
+
+	switch (i) {
+	case 1: ufrperframe = 8;
+	case 2: ufrperframe = 4;
+	case 3: ufrperframe = 2;
+	default: ufrperframe = 1;
+	}
+	frames = (xfer->nframes + (ufrperframe - 1)) / ufrperframe;
+	uframes = 8 / ufrperframe;
+
+	if (frames == 0) {
+		DPRINTF(("ehci_device_isoc_start: frames == 0\n"));
+		return (USBD_INVAL);
+	}
+
+	dma_buf = xfer->buffer;
+	offs = 0;
+
+	for (i = 0; i < frames; i++) {
+		int froffs = offs;
+		itd = ehci_alloc_itd(sc);
+
+		if (prev != NULL) {
+			prev->itd.itd_next =
+			    htole32(itd->physaddr | EHCI_LINK_ITD);
+			prev->xfer_next = itd;
+	    	} else {
+			start = itd;
+		}
+
+		/*
+		 * Step 1.5, initialize uframes
+		 */
+		for (j = 0; j < 8; j += uframes) {
+			/* Calculate which page in the list this starts in */
+			int addr = DMAADDR(dma_buf, froffs);
+			addr = EHCI_PAGE_OFFSET(addr);
+			addr += (offs - froffs);
+			addr = EHCI_PAGE(addr);
+			addr /= EHCI_PAGE_SIZE;
+
+			/* This gets the initial offset into the first page,
+			 * looks how far further along the current uframe
+			 * offset is. Works out how many pages that is.
+			 */
+
+			itd->itd.itd_ctl[j] = htole32 ( EHCI_ITD_ACTIVE |
+			    EHCI_ITD_SET_LEN(xfer->frlengths[trans_count]) | 
+			    EHCI_ITD_SET_PG(addr) |
+			    EHCI_ITD_SET_OFFS(EHCI_PAGE_OFFSET(DMAADDR(dma_buf,
+				offs))));
+
+			total_length += xfer->frlengths[trans_count];
+			offs += xfer->frlengths[trans_count];
+			trans_count++;
+
+			if (trans_count >= xfer->nframes) { /*Set IOC*/
+				itd->itd.itd_ctl[j] |= htole32(EHCI_ITD_IOC);
+			}
+		}	
+
+		/* Step 1.75, set buffer pointers. To simplify matters, all
+		 * pointers are filled out for the next 7 hardware pages in
+		 * the dma block, so no need to worry what pages to cover
+		 * and what to not.
+		 */
+
+		for (j=0; j < 7; j++) {
+			/*
+			 * Don't try to lookup a page that's past the end
+			 * of buffer
+			 */
+			int page_offs = EHCI_PAGE(froffs + 
+			    (EHCI_PAGE_SIZE * j));
+			if (page_offs >= dma_buf->block->size)
+				break;
+
+			int page = DMAADDR(dma_buf, page_offs);
+			page = EHCI_PAGE(page);
+			itd->itd.itd_bufr[j] =
+			    htole32(EHCI_ITD_SET_BPTR(page) | EHCI_LINK_ITD);
+		}
+
+		/*
+		 * Other special values
+		 */
+
+		k = epipe->pipe.endpoint->edesc->bEndpointAddress;
+		itd->itd.itd_bufr[0] |= htole32(
+		    EHCI_ITD_SET_EP(UE_GET_ADDR(k)) |
+		    EHCI_ITD_SET_DADDR(epipe->pipe.device->address));
+
+		k = (UE_GET_DIR(epipe->pipe.endpoint->edesc->bEndpointAddress))
+		    ? 1 : 0;
+		j = UE_GET_SIZE(
+		    UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize));
+		itd->itd.itd_bufr[1] |= htole32(EHCI_ITD_SET_DIR(k) |
+		    EHCI_ITD_SET_MAXPKT(UE_GET_SIZE(j)));
+
+		/* FIXME: handle invalid trans */
+		itd->itd.itd_bufr[2] |= 
+		    htole32(EHCI_ITD_SET_MULTI(UE_GET_TRANS(j)+1));
+		prev = itd;
+	} /* End of frame */
+
+	stop = itd;
+	stop->xfer_next = NULL;
+	exfer->isoc_len = total_length;
+
+	/*
+	 * Part 2: Transfer descriptors have now been set up, now they must
+	 * be scheduled into the period frame list. Erk. Not wanting to
+	 * complicate matters, transfer is denied if the transfer spans
+	 * more than the period frame list.
+	 */
+
+	s = splusb();
+
+	/* Start inserting frames */
+	if (epipe->u.isoc.cur_xfers > 0) {
+		frindex = epipe->u.isoc.next_frame;
+	} else {
+		frindex = EOREAD4(sc, EHCI_FRINDEX);
+		frindex = frindex >> 3; /* Erase microframe index */
+		frindex += 2;
+	}
+
+	if (frindex >= sc->sc_flsize)
+		frindex &= (sc->sc_flsize - 1);
+
+	/* Whats the frame interval? */
+	i = (1 << epipe->pipe.endpoint->edesc->bInterval);
+	if (i / 8 == 0)
+		i = 1;
+	else
+		i /= 8;
+
+	itd = start;
+	for (j = 0; j < frames; j++) {
+		if (itd == NULL)
+			panic("ehci: unexpectedly ran out of isoc itds,"
+			    "isoc_start\n");
+
+		itd->itd.itd_next = sc->sc_flist[frindex];
+		if (itd->itd.itd_next == 0)
+			/* FIXME: frindex table gets initialized to NULL
+			 * or EHCI_NULL? */
+			itd->itd.itd_next = htole32(EHCI_NULL);
+
+		sc->sc_flist[frindex] = htole32(EHCI_LINK_ITD | itd->physaddr);
+
+		itd->u.frame_list.next = sc->sc_softitds[frindex];
+		sc->sc_softitds[frindex] = itd;
+		if (itd->u.frame_list.next != NULL)
+			itd->u.frame_list.next->u.frame_list.prev = itd;
+		itd->slot = frindex;
+		itd->u.frame_list.prev = NULL;
+
+		frindex += i;
+		if (frindex >= sc->sc_flsize)
+			frindex -= sc->sc_flsize;
+
+		itd = itd->xfer_next;
+	}
+
+	epipe->u.isoc.cur_xfers++;
+	epipe->u.isoc.next_frame = frindex;
+
+	exfer->itdstart = start;
+	exfer->itdend = stop;
+	exfer->sqtdstart = NULL;
+	exfer->sqtdstart = NULL;
+
+	ehci_add_intr_list(sc, exfer);
+	xfer->status = USBD_IN_PROGRESS;
+	xfer->done = 0;
+	splx(s);
+
+	if (sc->sc_bus.use_polling) {
+		printf("Starting ehci isoc xfer with polling. Bad idea?\n");
+		ehci_waitintr(sc, xfer);
+	}
+
+	return (USBD_IN_PROGRESS);
+}
+
+static void		
+ehci_device_isoc_abort(usbd_xfer_handle xfer)
+{
+	DPRINTFN(1, ("ehci_device_isoc_abort: xfer = %p\n", xfer));
+	ehci_abort_isoc_xfer(xfer, USBD_CANCELLED);
+}
+
+static void		
+ehci_device_isoc_close(usbd_pipe_handle pipe)
+{
+	printf("ehci_device_isoc_close: nothing in the pipe to free?\n");
+}
+
+static void		
+ehci_device_isoc_done(usbd_xfer_handle xfer)
+{
+	struct ehci_xfer *exfer;
+	ehci_softc_t *sc;
+	struct ehci_pipe *epipe;
+	int s;
+
+	exfer = EXFER(xfer);
+	sc = (ehci_softc_t *)xfer->pipe->device->bus;
+	epipe = (struct ehci_pipe *) xfer->pipe;
+
+	s = splusb();
+	epipe->u.isoc.cur_xfers--;
+	if (xfer->status != USBD_NOMEM && ehci_active_intr_list(exfer)) {
+		ehci_del_intr_list(exfer);
+		ehci_rem_free_itd_chain(sc, exfer);
+	}
+	splx(s);
+}
diff -ruN sys.orig/dev/usb/ehcireg.h sys/dev/usb/ehcireg.h
--- sys.orig/dev/usb/ehcireg.h	2008-08-15 17:16:07.000000000 +0800
+++ sys/dev/usb/ehcireg.h	2008-08-15 17:15:44.000000000 +0800
@@ -196,10 +196,41 @@
 
 typedef u_int32_t ehci_physaddr_t;
 
+typedef u_int32_t ehci_isoc_trans_t;
+typedef u_int32_t ehci_isoc_bufr_ptr_t;
+
 /* Isochronous Transfer Descriptor */
 typedef struct {
-	ehci_link_t	itd_next;
-	/* XXX many more */
+	ehci_link_t		itd_next;
+	ehci_isoc_trans_t	itd_ctl[8];
+#define EHCI_ITD_GET_STATUS(x) (((x) >> 28) & 0xf)
+#define EHCI_ITD_SET_STATUS(x) (((x) & 0xf) << 28)
+#define EHCI_ITD_ACTIVE		0x80000000
+#define EHCI_ITD_BUF_ERR	0x40000000
+#define EHCI_ITD_BABBLE		0x20000000
+#define EHCI_ITD_ERROR		0x10000000
+#define EHCI_ITD_GET_LEN(x) (((x) >> 16) & 0xfff)
+#define EHCI_ITD_SET_LEN(x) (((x) & 0xfff) << 16)
+#define EHCI_ITD_IOC		0x8000
+#define EHCI_ITD_GET_IOC(x) (((x) >> 15) & 1)
+#define EHCI_ITD_SET_IOC(x) (((x) << 15) & EHCI_ITD_IOC)
+#define EHCI_ITD_GET_PG(x) (((x) >> 12) & 0xf)
+#define EHCI_ITD_SET_PG(x) (((x) & 0xf) << 12)
+#define EHCI_ITD_GET_OFFS(x) (((x) >> 0) & 0xfff)
+#define EHCI_ITD_SET_OFFS(x) (((x) & 0xfff) << 0)
+	ehci_isoc_bufr_ptr_t	itd_bufr[7];
+#define EHCI_ITD_GET_BPTR(x) ((x) & 0xfffff000)
+#define EHCI_ITD_SET_BPTR(x) ((x) & 0xfffff000)
+#define EHCI_ITD_GET_EP(x) (((x) >> 8) & 0xf)
+#define EHCI_ITD_SET_EP(x) (((x) & 0xf) << 8)
+#define EHCI_ITD_GET_DADDR(x) ((x) & 0x7f)
+#define EHCI_ITD_SET_DADDR(x) ((x) & 0x7f)
+#define EHCI_ITD_GET_DIR(x) (((x) >> 11) & 1)
+#define EHCI_ITD_SET_DIR(x) (((x) & 1) << 11)
+#define EHCI_ITD_GET_MAXPKT(x) ((x) & 0x7ff)
+#define EHCI_ITD_SET_MAXPKT(x) ((x) & 0x7ff)
+#define EHCI_ITD_GET_MULTI(x) ((x) & 0x3)
+#define EHCI_ITD_SET_MULTI(x) ((x) & 0x3)
 } ehci_itd_t;
 #define EHCI_ITD_ALIGN 32
 
diff -ruN sys.orig/dev/usb/ehcivar.h sys/dev/usb/ehcivar.h
--- sys.orig/dev/usb/ehcivar.h	2008-08-15 17:16:07.000000000 +0800
+++ sys/dev/usb/ehcivar.h	2008-08-15 17:15:44.000000000 +0800
@@ -60,12 +60,36 @@
 #define EHCI_SQH_SIZE ((sizeof (struct ehci_soft_qh) + EHCI_QH_ALIGN - 1) / EHCI_QH_ALIGN * EHCI_QH_ALIGN)
 #define EHCI_SQH_CHUNK (EHCI_PAGE_SIZE / EHCI_SQH_SIZE)
 
+typedef struct ehci_soft_itd {
+	ehci_itd_t itd;
+	union {
+		struct {
+			/* soft_itds links in a periodic frame*/
+			struct ehci_soft_itd *next;
+			struct ehci_soft_itd *prev;
+		} frame_list;
+		/* circular list of free itds */
+		LIST_ENTRY(ehci_soft_itd) free_list;
+	} u;
+	struct ehci_soft_itd *xfer_next; /* Next soft_itd in xfer */
+	ehci_physaddr_t physaddr;
+	usb_dma_t dma;
+	int offs;
+	int slot;
+	struct timeval t; /* store free time */
+} ehci_soft_itd_t;
+#define EHCI_ITD_SIZE ((sizeof(struct ehci_soft_itd) + EHCI_QH_ALIGN - 1) / EHCI_ITD_ALIGN * EHCI_ITD_ALIGN)
+#define EHCI_ITD_CHUNK (EHCI_PAGE_SIZE / EHCI_ITD_SIZE)
+
 struct ehci_xfer {
 	struct usbd_xfer xfer;
 	struct usb_task	abort_task;
 	LIST_ENTRY(ehci_xfer) inext; /* list of active xfers */
 	ehci_soft_qtd_t *sqtdstart;
 	ehci_soft_qtd_t *sqtdend;
+	ehci_soft_itd_t *itdstart;
+	ehci_soft_itd_t *itdend;
+	u_int isoc_len;
 	u_int32_t ehci_xfer_flags;
 #ifdef DIAGNOSTIC
 	int isdone;
@@ -94,6 +118,8 @@
 #define EHCI_HASH_SIZE 128
 #define EHCI_COMPANION_MAX 8
 
+#define	EHCI_FREE_LIST_INTERVAL	100
+
 #define EHCI_SCFLG_DONEINIT	0x0001	/* ehci_init() has been called. */
 #define EHCI_SCFLG_LOSTINTRBUG	0x0002	/* workaround for VIA / ATI chipsets */
 
@@ -133,10 +159,16 @@
 
 	struct ehci_soft_islot sc_islots[EHCI_INTRQHS];
 
+	/* jcmm - an array matching sc_flist, but with software pointers,
+	 * not hardware address pointers
+	 */
+	struct ehci_soft_itd **sc_softitds;
+
 	LIST_HEAD(, ehci_xfer) sc_intrhead;
 
 	ehci_soft_qh_t *sc_freeqhs;
 	ehci_soft_qtd_t *sc_freeqtds;
+	LIST_HEAD(sc_freeitds, ehci_soft_itd) sc_freeitds;
 
 	int sc_noport;
 	u_int8_t sc_addr;		/* device address */
diff -ruN sys.orig/dev/usb/usb.h sys/dev/usb/usb.h
--- sys.orig/dev/usb/usb.h	2008-08-15 17:16:07.000000000 +0800
+++ sys/dev/usb/usb.h	2008-08-15 17:15:44.000000000 +0800
@@ -271,6 +271,8 @@
 #define  UE_ISO_SYNC	0x0c
 #define UE_GET_ISO_TYPE(a)	((a) & UE_ISO_TYPE)
 	uWord		wMaxPacketSize;
+#define UE_GET_TRANS(a)		(((a) >> 11) & 0x3)
+#define UE_GET_SIZE(a)		((a) & 0x7ff)
 	uByte		bInterval;
 } UPACKED usb_endpoint_descriptor_t;
 #define USB_ENDPOINT_DESCRIPTOR_SIZE 7
_______________________________________________
freebsd-usb@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-usb
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to