Module Name:    src
Committed By:   skrll
Date:           Sun Nov 30 13:46:00 UTC 2014

Modified Files:
        src/sys/dev/usb [nick-nhusb]: ehci.c ehcireg.h ehcivar.h

Log Message:
Add full speed isoc support to ehci(4). Based on the patch posted in

https://mail-index.netbsd.org/port-arm/2013/04/14/msg001842.html

>From Masao Uebayashi via Sebastien Bocahu


To generate a diff of this commit:
cvs rdiff -u -r1.234.2.2 -r1.234.2.3 src/sys/dev/usb/ehci.c
cvs rdiff -u -r1.34.14.1 -r1.34.14.2 src/sys/dev/usb/ehcireg.h
cvs rdiff -u -r1.42.14.1 -r1.42.14.2 src/sys/dev/usb/ehcivar.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/usb/ehci.c
diff -u src/sys/dev/usb/ehci.c:1.234.2.2 src/sys/dev/usb/ehci.c:1.234.2.3
--- src/sys/dev/usb/ehci.c:1.234.2.2	Sun Nov 30 13:14:11 2014
+++ src/sys/dev/usb/ehci.c	Sun Nov 30 13:46:00 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: ehci.c,v 1.234.2.2 2014/11/30 13:14:11 skrll Exp $ */
+/*	$NetBSD: ehci.c,v 1.234.2.3 2014/11/30 13:46:00 skrll Exp $ */
 
 /*
  * Copyright (c) 2004-2012 The NetBSD Foundation, Inc.
@@ -53,7 +53,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.234.2.2 2014/11/30 13:14:11 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ehci.c,v 1.234.2.3 2014/11/30 13:46:00 skrll Exp $");
 
 #include "ohci.h"
 #include "uhci.h"
@@ -131,6 +131,7 @@ struct ehci_pipe {
 	union {
 		ehci_soft_qtd_t *qtd;
 		/* ehci_soft_itd_t *itd; */
+		/* ehci_soft_sitd_t *sitd; */
 	} tail;
 	union {
 		/* Control pipe */
@@ -161,6 +162,7 @@ Static void		ehci_waitintr(ehci_softc_t 
 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_check_sitd_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 *);
@@ -211,6 +213,12 @@ Static void		ehci_device_isoc_abort(usbd
 Static void		ehci_device_isoc_close(usbd_pipe_handle);
 Static void		ehci_device_isoc_done(usbd_xfer_handle);
 
+Static usbd_status	ehci_device_fs_isoc_transfer(usbd_xfer_handle);
+Static usbd_status	ehci_device_fs_isoc_start(usbd_xfer_handle);
+Static void		ehci_device_fs_isoc_abort(usbd_xfer_handle);
+Static void		ehci_device_fs_isoc_close(usbd_pipe_handle);
+Static void		ehci_device_fs_isoc_done(usbd_xfer_handle);
+
 Static void		ehci_device_clear_toggle(usbd_pipe_handle pipe);
 Static void		ehci_noop(usbd_pipe_handle pipe);
 
@@ -228,9 +236,13 @@ Static void		ehci_free_sqtd_chain(ehci_s
 					    ehci_soft_qtd_t *);
 
 Static ehci_soft_itd_t	*ehci_alloc_itd(ehci_softc_t *sc);
+Static ehci_soft_sitd_t *ehci_alloc_sitd(ehci_softc_t *sc);
 Static void		ehci_free_itd(ehci_softc_t *sc, ehci_soft_itd_t *itd);
+Static void		ehci_free_sitd(ehci_softc_t *sc, ehci_soft_sitd_t *);
 Static void 		ehci_rem_free_itd_chain(ehci_softc_t *sc,
 						struct ehci_xfer *exfer);
+Static void		ehci_rem_free_sitd_chain(ehci_softc_t *sc,
+						 struct ehci_xfer *exfer);
 Static void 		ehci_abort_isoc_xfer(usbd_xfer_handle xfer,
 						usbd_status status);
 
@@ -344,6 +356,15 @@ Static const struct usbd_pipe_methods eh
 	.done =		ehci_device_isoc_done,
 };
 
+Static const struct usbd_pipe_methods ehci_device_fs_isoc_methods = {
+	ehci_device_fs_isoc_transfer,
+	ehci_device_fs_isoc_start,
+	ehci_device_fs_isoc_abort,
+	ehci_device_fs_isoc_close,
+	ehci_noop,
+	ehci_device_fs_isoc_done,
+};
+
 static const uint8_t revbits[EHCI_MAX_POLLRATE] = {
 0x00,0x40,0x20,0x60,0x10,0x50,0x30,0x70,0x08,0x48,0x28,0x68,0x18,0x58,0x38,0x78,
 0x04,0x44,0x24,0x64,0x14,0x54,0x34,0x74,0x0c,0x4c,0x2c,0x6c,0x1c,0x5c,0x3c,0x7c,
@@ -486,6 +507,7 @@ ehci_init(ehci_softc_t *sc)
 	if (sc->sc_softitds == NULL)
 		return ENOMEM;
 	LIST_INIT(&sc->sc_freeitds);
+	LIST_INIT(&sc->sc_freesitds);
 	TAILQ_INIT(&sc->sc_intrhead);
 
 	/* Set up the bus struct. */
@@ -798,6 +820,7 @@ ehci_softintr(void *v)
 Static void
 ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
 {
+	usbd_device_handle dev = ex->xfer.pipe->device;
 	int attr;
 
 	USBHIST_FUNC();	USBHIST_CALLED(ehcidebug);
@@ -806,9 +829,12 @@ ehci_check_intr(ehci_softc_t *sc, struct
 	KASSERT(sc->sc_bus.use_polling || mutex_owned(&sc->sc_lock));
 
 	attr = ex->xfer.pipe->endpoint->edesc->bmAttributes;
-	if (UE_GET_XFERTYPE(attr) == UE_ISOCHRONOUS)
-		ehci_check_itd_intr(sc, ex);
-	else
+	if (UE_GET_XFERTYPE(attr) == UE_ISOCHRONOUS) {
+		if (dev->speed == USB_SPEED_HIGH)
+			ehci_check_itd_intr(sc, ex);
+		else
+			ehci_check_sitd_intr(sc, ex);
+	} else
 		ehci_check_qh_intr(sc, ex);
 
 	return;
@@ -949,6 +975,48 @@ done:
 	ehci_idone(ex);
 }
 
+void
+ehci_check_sitd_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
+{
+	ehci_soft_sitd_t *sitd;
+
+	USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
+
+	KASSERT(mutex_owned(&sc->sc_lock));
+
+	if (&ex->xfer != SIMPLEQ_FIRST(&ex->xfer.pipe->queue))
+		return;
+
+	if (ex->sitdstart == NULL) {
+		printf("ehci_check_sitd_intr: not valid sitd\n");
+		return;
+	}
+
+	sitd = ex->sitdend;
+#ifdef DIAGNOSTIC
+	if (sitd == NULL) {
+		printf("ehci_check_sitd_intr: sitdend == 0\n");
+		return;
+	}
+#endif
+
+	/*
+	 * check no active transfers in last sitd, meaning we're finished
+	 */
+
+	usb_syncmem(&sitd->dma, sitd->offs + offsetof(ehci_sitd_t, sitd_buffer),
+		    sizeof(sitd->sitd.sitd_buffer), BUS_DMASYNC_POSTWRITE |
+		    BUS_DMASYNC_POSTREAD);
+
+	if (le32toh(sitd->sitd.sitd_trans) & EHCI_SITD_ACTIVE)
+		return;
+
+	USBHIST_LOGN(ehcidebug, 10, "ex=%p done", ex, 0, 0, 0);
+	callout_stop(&(ex->xfer.timeout_handle));
+	ehci_idone(ex);
+}
+
+
 Static void
 ehci_idone(struct ehci_xfer *ex)
 {
@@ -989,9 +1057,13 @@ ehci_idone(struct ehci_xfer *ex)
 
 	/* The transfer is done, compute actual length and status. */
 
-	if (UE_GET_XFERTYPE(xfer->pipe->endpoint->edesc->bmAttributes)
-				== UE_ISOCHRONOUS) {
-		/* Isoc transfer */
+	u_int xfertype, speed;
+
+	xfertype = UE_GET_XFERTYPE(xfer->pipe->endpoint->edesc->bmAttributes);
+	speed = xfer->pipe->device->speed;
+	if (xfertype == UE_ISOCHRONOUS && speed == USB_SPEED_HIGH) {
+		/* HS isoc transfer */
+
 		struct ehci_soft_itd *itd;
 		int i, nframes, len, uframes;
 
@@ -1034,6 +1106,53 @@ ehci_idone(struct ehci_xfer *ex)
 		goto end;
 	}
 
+	if (xfertype == UE_ISOCHRONOUS && speed == USB_SPEED_FULL) {
+		/* FS isoc transfer */
+		struct ehci_soft_sitd *sitd;
+		int nframes, len;
+
+		nframes = 0;
+		actlen = 0;
+
+		for (sitd = ex->sitdstart; sitd != NULL; sitd = sitd->xfer_next) {
+			usb_syncmem(&sitd->dma,sitd->offs + offsetof(ehci_sitd_t, sitd_buffer),
+			    sizeof(sitd->sitd.sitd_buffer), BUS_DMASYNC_POSTWRITE |
+			    BUS_DMASYNC_POSTREAD);
+
+			/* 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(sitd->sitd.sitd_trans);
+			len = EHCI_SITD_GET_LEN(status);
+			if (status & (EHCI_SITD_ERR|EHCI_SITD_BUFERR|
+			    EHCI_SITD_BABBLE|EHCI_SITD_XACTERR|EHCI_SITD_MISS)) {
+				/* No valid data on error */
+				len = xfer->frlengths[nframes];
+			}
+
+			/*
+			 * frlengths[i]: # of bytes to send
+			 * len: # of bytes host didn't send
+			 */
+			xfer->frlengths[nframes] -= len;
+			/* frlengths[i]: # of bytes host sent */
+			actlen += xfer->frlengths[nframes++];
+
+			if (nframes >= xfer->nframes)
+				break;
+	    	}
+
+		xfer->actlen = actlen;
+		xfer->status = USBD_NORMAL_COMPLETION;
+		goto end;
+	}
+
 	/* Continue processing xfers using queue heads */
 
 	lsqtd = ex->sqtdend;
@@ -1824,14 +1943,7 @@ ehci_open(usbd_pipe_handle pipe)
 	case USB_SPEED_HIGH: speed = EHCI_QH_SPEED_HIGH; break;
 	default: panic("ehci_open: bad device speed %d", dev->speed);
 	}
-	if (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_ISOCHRONOUS) {
-		aprint_error_dev(sc->sc_dev, "error opening low/full speed "
-		    "isoc endpoint.\n");
-		aprint_normal_dev(sc->sc_dev, "a low/full speed device is "
-		    "attached to a USB2 hub, and transaction translations are "
-		    "not yet supported.\n");
-		aprint_normal_dev(sc->sc_dev, "reattach the device to the "
-		    "root hub instead.\n");
+	if (speed == EHCI_QH_SPEED_LOW && xfertype == UE_ISOCHRONOUS) {
 		USBHIST_LOG(ehcidebug, "hshubaddr=%d hshubport=%d",
 			    hshubaddr, hshubport, 0, 0);
 		return USBD_INVAL;
@@ -1927,7 +2039,10 @@ ehci_open(usbd_pipe_handle pipe)
 			goto bad;
 		break;
 	case UE_ISOCHRONOUS:
-		pipe->methods = &ehci_device_isoc_methods;
+		if (speed == EHCI_QH_SPEED_HIGH)
+			pipe->methods = &ehci_device_isoc_methods;
+		else
+			pipe->methods = &ehci_device_fs_isoc_methods;
 		if (ed->bInterval == 0 || ed->bInterval > 16) {
 			printf("ehci: opening pipe with invalid bInterval\n");
 			err = USBD_INVAL;
@@ -2127,6 +2242,55 @@ ehci_rem_free_itd_chain(ehci_softc_t *sc
 	exfer->itdend = NULL;
 }
 
+Static void
+ehci_rem_free_sitd_chain(ehci_softc_t *sc, struct ehci_xfer *exfer)
+{
+	struct ehci_soft_sitd *sitd, *prev;
+
+	prev = NULL;
+
+	if (exfer->sitdstart == NULL || exfer->sitdend == NULL)
+		panic("ehci isoc xfer being freed, but with no sitd chain\n");
+
+	for (sitd = exfer->sitdstart; sitd != NULL; sitd = sitd->xfer_next) {
+		prev = sitd->u.frame_list.prev;
+		/* Unlink sitd from hardware chain, or frame array */
+		if (prev == NULL) { /* We're at the table head */
+			sc->sc_softsitds[sitd->slot] = sitd->u.frame_list.next;
+			sc->sc_flist[sitd->slot] = sitd->sitd.sitd_next;
+			usb_syncmem(&sc->sc_fldma,
+			    sizeof(ehci_link_t) * sitd->slot,
+			    sizeof(ehci_link_t),
+			    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+			if (sitd->u.frame_list.next != NULL)
+				sitd->u.frame_list.next->u.frame_list.prev = NULL;
+		} else {
+			/* XXX this part is untested... */
+			prev->sitd.sitd_next = sitd->sitd.sitd_next;
+			usb_syncmem(&sitd->dma,
+			    sitd->offs + offsetof(ehci_sitd_t, sitd_next),
+			    sizeof(sitd->sitd.sitd_next), BUS_DMASYNC_PREWRITE);
+
+			prev->u.frame_list.next = sitd->u.frame_list.next;
+			if (sitd->u.frame_list.next != NULL)
+				sitd->u.frame_list.next->u.frame_list.prev = prev;
+		}
+	}
+
+	prev = NULL;
+	for (sitd = exfer->sitdstart; sitd != NULL; sitd = sitd->xfer_next) {
+		if (prev != NULL)
+			ehci_free_sitd(sc, prev);
+		prev = sitd;
+	}
+	if (prev)
+		ehci_free_sitd(sc, prev);
+	exfer->sitdstart = NULL;
+	exfer->sitdend = NULL;
+}
+
+
 /***********/
 
 /*
@@ -3101,6 +3265,75 @@ ehci_alloc_itd(ehci_softc_t *sc)
 	return itd;
 }
 
+Static ehci_soft_sitd_t *
+ehci_alloc_sitd(ehci_softc_t *sc)
+{
+	struct ehci_soft_sitd *sitd, *freesitd;
+	usbd_status err;
+	int i, offs, frindex, previndex;
+	usb_dma_t dma;
+
+	USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
+
+	mutex_enter(&sc->sc_lock);
+
+	/* Find an sitd that wasn't freed this frame or last frame. This can
+	 * discard sitds 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;
+
+	freesitd = NULL;
+	LIST_FOREACH(sitd, &sc->sc_freesitds, u.free_list) {
+		if (sitd == NULL)
+			break;
+		if (sitd->slot != frindex && sitd->slot != previndex) {
+			freesitd = sitd;
+			break;
+		}
+	}
+
+	if (freesitd == NULL) {
+		USBHIST_LOG(ehcidebug, "allocating chunk", 0, 0, 0, 0);
+		err = usb_allocmem(&sc->sc_bus, EHCI_SITD_SIZE * EHCI_SITD_CHUNK,
+				EHCI_PAGE_SIZE, &dma);
+
+		if (err) {
+			USBHIST_LOG(ehcidebug,
+			    "alloc returned %d", err, 0, 0, 0);
+			mutex_exit(&sc->sc_lock);
+			return NULL;
+		}
+
+		for (i = 0; i < EHCI_SITD_CHUNK; i++) {
+			offs = i * EHCI_SITD_SIZE;
+			sitd = KERNADDR(&dma, offs);
+			sitd->physaddr = DMAADDR(&dma, offs);
+	 		sitd->dma = dma;
+			sitd->offs = offs;
+			LIST_INSERT_HEAD(&sc->sc_freesitds, sitd, u.free_list);
+		}
+		freesitd = LIST_FIRST(&sc->sc_freesitds);
+	}
+
+	sitd = freesitd;
+	LIST_REMOVE(sitd, u.free_list);
+	memset(&sitd->sitd, 0, sizeof(ehci_sitd_t));
+	usb_syncmem(&sitd->dma, sitd->offs + offsetof(ehci_sitd_t, sitd_next),
+		    sizeof(sitd->sitd.sitd_next), BUS_DMASYNC_PREWRITE |
+		    BUS_DMASYNC_PREREAD);
+
+	sitd->u.frame_list.next = NULL;
+	sitd->u.frame_list.prev = NULL;
+	sitd->xfer_next = NULL;
+	sitd->slot = 0;
+
+	mutex_exit(&sc->sc_lock);
+
+	return sitd;
+}
+
 Static void
 ehci_free_itd(ehci_softc_t *sc, ehci_soft_itd_t *itd)
 {
@@ -3110,6 +3343,15 @@ ehci_free_itd(ehci_softc_t *sc, ehci_sof
 	LIST_INSERT_HEAD(&sc->sc_freeitds, itd, u.free_list);
 }
 
+Static void
+ehci_free_sitd(ehci_softc_t *sc, ehci_soft_sitd_t *sitd)
+{
+
+	KASSERT(mutex_owned(&sc->sc_lock));
+
+	LIST_INSERT_HEAD(&sc->sc_freesitds, sitd, u.free_list);
+}
+
 /****************/
 
 /*
@@ -3294,6 +3536,7 @@ ehci_abort_isoc_xfer(usbd_xfer_handle xf
 	struct ehci_xfer *exfer;
 	ehci_softc_t *sc;
 	struct ehci_soft_itd *itd;
+	struct ehci_soft_sitd *sitd;
 	int i, wake;
 
 	USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
@@ -3351,6 +3594,21 @@ ehci_abort_isoc_xfer(usbd_xfer_handle xf
 		    sizeof(itd->itd.itd_ctl),
 		    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
 	}
+	for (sitd = exfer->sitdstart; sitd != NULL; sitd = sitd->xfer_next) {
+		usb_syncmem(&sitd->dma,
+		    sitd->offs + offsetof(ehci_sitd_t, sitd_buffer),
+		    sizeof(sitd->sitd.sitd_buffer),
+		    BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+
+		trans_status = le32toh(sitd->sitd.sitd_trans);
+		trans_status &= ~EHCI_SITD_ACTIVE;
+		sitd->sitd.sitd_trans = htole32(trans_status);
+
+		usb_syncmem(&sitd->dma,
+		    sitd->offs + offsetof(ehci_sitd_t, sitd_buffer),
+		    sizeof(sitd->sitd.sitd_buffer),
+		    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+	}
 
 	sc->sc_softwake = 1;
 	usb_schedsoftintr(&sc->sc_bus);
@@ -4099,6 +4357,325 @@ ehci_device_intr_done(usbd_xfer_handle x
 /************************/
 
 Static usbd_status
+ehci_device_fs_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_fs_isoc_start(xfer);
+}
+
+Static usbd_status
+ehci_device_fs_isoc_start(usbd_xfer_handle xfer)
+{
+	struct ehci_pipe *epipe;
+	usbd_device_handle dev;
+	ehci_softc_t *sc;
+	struct ehci_xfer *exfer;
+	ehci_soft_sitd_t *sitd, *prev, *start, *stop;
+	usb_dma_t *dma_buf;
+	int i, j, k, frames;
+	int offs, total_length;
+	int frindex;
+	u_int huba, dir;
+
+	USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
+
+	start = NULL;
+	prev = NULL;
+	sitd = NULL;
+	total_length = 0;
+	exfer = (struct ehci_xfer *) xfer;
+	sc = xfer->pipe->device->bus->hci_private;
+	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->sitdstart != NULL)
+		return USBD_IN_PROGRESS;
+
+	USBHIST_LOG(ehcidebug, "xfer %p len %d flags %d",
+	    xfer, xfer->length, xfer->flags, 0);
+
+	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 (epipe->pipe.endpoint->edesc->bInterval *
+			xfer->nframes >= sc->sc_flsize - 4) {
+		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_fs_isoc_start: request\n");
+
+	if (!exfer->isdone)
+		printf("ehci_device_fs_isoc_start: not done, ex = %p\n", exfer);
+	exfer->isdone = 0;
+#endif
+
+	/*
+	 * Step 1: Allocate and initialize sitds.
+	 */
+
+	i = epipe->pipe.endpoint->edesc->bInterval;
+	if (i > 16 || i == 0) {
+		/* Spec page 271 says intervals > 16 are invalid */
+		USBHIST_LOG(ehcidebug, "bInverval %d invalid\n", 0, 0, 0, 0);
+
+		return USBD_INVAL;
+	}
+
+	frames = xfer->nframes;
+
+	if (frames == 0) {
+		USBHIST_LOG(ehcidebug, "frames == 0", 0, 0, 0, 0);
+
+		return USBD_INVAL;
+	}
+
+	dma_buf = &xfer->dmabuf;
+	offs = 0;
+
+	for (i = 0; i < frames; i++) {
+		sitd = ehci_alloc_sitd(sc);
+
+		if (prev)
+			prev->xfer_next = sitd;
+		else
+			start = sitd;
+
+#ifdef DIAGNOSTIC
+		if (xfer->frlengths[i] > 0x3ff) {
+			printf("ehci: invalid frame length\n");
+			xfer->frlengths[i] = 0x3ff;
+		}
+#endif
+
+		sitd->sitd.sitd_trans = htole32(EHCI_SITD_ACTIVE |
+		    EHCI_SITD_SET_LEN(xfer->frlengths[i]));
+
+		/* Set page0 index and offset. */
+		sitd->sitd.sitd_buffer[0] = htole32(DMAADDR(dma_buf, offs));
+
+		total_length += xfer->frlengths[i];
+		offs += xfer->frlengths[i];
+
+		sitd->sitd.sitd_buffer[1] =
+		    htole32(EHCI_SITD_SET_BPTR(DMAADDR(dma_buf, offs - 1)));
+
+		huba = dev->myhsport->parent->address;
+
+/*		if (sc->sc_flags & EHCIF_FREESCALE) {
+			// Set hub address to 0 if embedded TT is used.
+			if (huba == sc->sc_addr)
+				huba = 0;
+		}
+*/
+
+		k = epipe->pipe.endpoint->edesc->bEndpointAddress;
+		dir = UE_GET_DIR(k) ? 1 : 0;
+		sitd->sitd.sitd_endp =
+		    htole32(EHCI_SITD_SET_ENDPT(UE_GET_ADDR(k)) |
+		    EHCI_SITD_SET_DADDR(dev->address) |
+		    EHCI_SITD_SET_PORT(dev->myhsport->portno) |
+		    EHCI_SITD_SET_HUBA(huba) |
+		    EHCI_SITD_SET_DIR(dir));
+
+		sitd->sitd.sitd_back = htole32(EHCI_LINK_TERMINATE);
+
+		/* XXX */
+		u_char sa, sb;
+		u_int temp, tlen;
+		sa = 0;
+
+		if (dir == 0) {	/* OUT */
+			temp = 0;
+			tlen = xfer->frlengths[i];
+			if (tlen <= 188) {
+				temp |= 1;	/* T-count = 1, TP = ALL */
+				tlen = 1;
+			} else {
+				tlen += 187;
+				tlen /= 188;
+				temp |= tlen;	/* T-count = [1..6] */
+				temp |= 8;	/* TP = Begin */
+			}
+			sitd->sitd.sitd_buffer[1] |= htole32(temp);
+
+			tlen += sa;
+
+			if (tlen >= 8) {
+				sb = 0;
+			} else {
+				sb = (1 << tlen);
+			}
+
+			sa = (1 << sa);
+			sa = (sb - sa) & 0x3F;
+			sb = 0;
+		} else {
+			sb = (-(4 << sa)) & 0xFE;
+			sa = (1 << sa) & 0x3F;
+			sa = 0x01;
+			sb = 0xfc;
+		}
+
+		sitd->sitd.sitd_sched = htole32(EHCI_SITD_SET_SMASK(sa) |
+		    EHCI_SITD_SET_CMASK(sb));
+
+		prev = sitd;
+	} /* End of frame */
+
+	sitd->sitd.sitd_trans |= htole32(EHCI_SITD_IOC);
+
+	stop = sitd;
+	stop->xfer_next = NULL;
+	exfer->isoc_len = total_length;
+
+	usb_syncmem(&exfer->xfer.dmabuf, 0, total_length,
+		BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+	/*
+	 * Part 2: Transfer descriptors have now been set up, now they must
+	 * be scheduled into the periodic frame list. Erk. Not wanting to
+	 * complicate matters, transfer is denied if the transfer spans
+	 * more than the period frame list.
+	 */
+
+	mutex_enter(&sc->sc_lock);
+
+	/* 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 = epipe->pipe.endpoint->edesc->bInterval;
+
+	sitd = start;
+	for (j = 0; j < frames; j++) {
+		if (sitd == NULL)
+			panic("ehci: unexpectedly ran out of isoc sitds\n");
+
+		sitd->sitd.sitd_next = sc->sc_flist[frindex];
+		if (sitd->sitd.sitd_next == 0)
+			/* FIXME: frindex table gets initialized to NULL
+			 * or EHCI_NULL? */
+			sitd->sitd.sitd_next = EHCI_NULL;
+
+		usb_syncmem(&sitd->dma,
+		    sitd->offs + offsetof(ehci_sitd_t, sitd_next),
+		    sizeof(ehci_sitd_t),
+		    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+		sc->sc_flist[frindex] =
+		    htole32(EHCI_LINK_SITD | sitd->physaddr);
+
+		usb_syncmem(&sc->sc_fldma,
+		    sizeof(ehci_link_t) * frindex,
+		    sizeof(ehci_link_t),
+		    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+		sitd->u.frame_list.next = sc->sc_softsitds[frindex];
+		sc->sc_softsitds[frindex] = sitd;
+		if (sitd->u.frame_list.next != NULL)
+			sitd->u.frame_list.next->u.frame_list.prev = sitd;
+		sitd->slot = frindex;
+		sitd->u.frame_list.prev = NULL;
+
+		frindex += i;
+		if (frindex >= sc->sc_flsize)
+			frindex -= sc->sc_flsize;
+
+		sitd = sitd->xfer_next;
+	}
+
+	epipe->u.isoc.cur_xfers++;
+	epipe->u.isoc.next_frame = frindex;
+
+	exfer->sitdstart = start;
+	exfer->sitdend = stop;
+	exfer->sqtdstart = NULL;
+	exfer->sqtdstart = NULL;
+
+	ehci_add_intr_list(sc, exfer);
+	xfer->status = USBD_IN_PROGRESS;
+	xfer->done = 0;
+
+	mutex_exit(&sc->sc_lock);
+
+	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_fs_isoc_abort(usbd_xfer_handle xfer)
+{
+	USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
+
+	USBHIST_LOG(ehcidebug, "xfer = %p", xfer, 0, 0, 0);
+	ehci_abort_isoc_xfer(xfer, USBD_CANCELLED);
+}
+
+Static void
+ehci_device_fs_isoc_close(usbd_pipe_handle pipe)
+{
+	USBHIST_FUNC(); USBHIST_CALLED(ehcidebug);
+
+	USBHIST_LOG(ehcidebug, "nothing in the pipe to free?", 0, 0, 0, 0);
+}
+
+Static void
+ehci_device_fs_isoc_done(usbd_xfer_handle xfer)
+{
+	struct ehci_xfer *exfer;
+	ehci_softc_t *sc;
+	struct ehci_pipe *epipe;
+
+	exfer = EXFER(xfer);
+	sc = xfer->pipe->device->bus->hci_private;
+	epipe = (struct ehci_pipe *) xfer->pipe;
+
+	KASSERT(mutex_owned(&sc->sc_lock));
+
+	epipe->u.isoc.cur_xfers--;
+	if (xfer->status != USBD_NOMEM && ehci_active_intr_list(exfer)) {
+		ehci_del_intr_list(sc, exfer);
+		ehci_rem_free_sitd_chain(sc, exfer);
+	}
+
+	usb_syncmem(&xfer->dmabuf, 0, xfer->length, BUS_DMASYNC_POSTWRITE |
+		    BUS_DMASYNC_POSTREAD);
+}
+Static usbd_status
 ehci_device_isoc_transfer(usbd_xfer_handle xfer)
 {
 	ehci_softc_t *sc = xfer->pipe->device->bus->hci_private;

Index: src/sys/dev/usb/ehcireg.h
diff -u src/sys/dev/usb/ehcireg.h:1.34.14.1 src/sys/dev/usb/ehcireg.h:1.34.14.2
--- src/sys/dev/usb/ehcireg.h:1.34.14.1	Sun Nov 30 12:18:58 2014
+++ src/sys/dev/usb/ehcireg.h	Sun Nov 30 13:46:00 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: ehcireg.h,v 1.34.14.1 2014/11/30 12:18:58 skrll Exp $	*/
+/*	$NetBSD: ehcireg.h,v 1.34.14.2 2014/11/30 13:46:00 skrll Exp $	*/
 
 /*
  * Copyright (c) 2001, 2004 The NetBSD Foundation, Inc.
@@ -247,7 +247,35 @@ typedef struct {
 /* Split Transaction Isochronous Transfer Descriptor */
 typedef struct {
 	volatile ehci_link_t	sitd_next;
-	/* XXX many more */
+	volatile uint32_t	sitd_endp;
+#define EHCI_SITD_SET_DIR(x)	(((x) & 0x01) << 31)
+#define EHCI_SITD_SET_PORT(x)	(((x) & 0x7f) << 24)
+#define EHCI_SITD_SET_HUBA(x)	(((x) & 0x7f) << 16)
+#define EHCI_SITD_SET_ENDPT(x)	(((x) & 0x0f) << 8)
+#define EHCI_SITD_SET_DADDR(x)	((x) & 0x7f)
+
+	volatile uint32_t	sitd_sched;
+#define EHCI_SITD_SET_SMASK(x)	((x) & 0xff)
+#define EHCI_SITD_SET_CMASK(x)	(((x) & 0xff) << 8)
+
+	volatile uint32_t	sitd_trans;
+#define EHCI_SITD_IOC		0x80000000
+#define EHCI_SITD_P		0x40000000
+#define EHCI_SITD_GET_LEN(x)	(((x) & 0x03ff0000) >> 16)
+#define EHCI_SITD_SET_LEN(x)	(((x) & 0x3ff) << 16)
+#define EHCI_SITD_ACTIVE	0x00000080
+#define EHCI_SITD_ERR		0x00000040
+#define EHCI_SITD_BUFERR	0x00000020
+#define EHCI_SITD_BABBLE	0x00000010
+#define EHCI_SITD_XACTERR	0x00000008
+#define EHCI_SITD_MISS		0x00000004
+#define EHCI_SITD_SPLITXSTATE	0x00000002
+
+	volatile uint32_t	sitd_buffer[2];
+#define EHCI_SITD_SET_BPTR(x)	((x) & 0xfffff000)
+#define EHCI_SITD_SET_OFFS(x)	((x) & 0xfff)
+
+	volatile uint32_t	sitd_back;
 } ehci_sitd_t;
 #define EHCI_SITD_ALIGN 32
 

Index: src/sys/dev/usb/ehcivar.h
diff -u src/sys/dev/usb/ehcivar.h:1.42.14.1 src/sys/dev/usb/ehcivar.h:1.42.14.2
--- src/sys/dev/usb/ehcivar.h:1.42.14.1	Sun Nov 30 12:18:58 2014
+++ src/sys/dev/usb/ehcivar.h	Sun Nov 30 13:46:00 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: ehcivar.h,v 1.42.14.1 2014/11/30 12:18:58 skrll Exp $ */
+/*	$NetBSD: ehcivar.h,v 1.42.14.2 2014/11/30 13:46:00 skrll Exp $ */
 
 /*
  * Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -61,7 +61,10 @@ typedef struct ehci_soft_qh {
 #define EHCI_SQH_CHUNK (EHCI_PAGE_SIZE / EHCI_SQH_SIZE)
 
 typedef struct ehci_soft_itd {
-	ehci_itd_t itd;
+	union {
+		ehci_itd_t itd;
+		ehci_sitd_t sitd;
+	};
 	union {
 		struct {
 			/* soft_itds links in a periodic frame*/
@@ -81,6 +84,12 @@ typedef struct ehci_soft_itd {
 #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)
 
+#define ehci_soft_sitd_t ehci_soft_itd_t
+#define ehci_soft_sitd ehci_soft_itd
+#define sc_softsitds sc_softitds
+#define EHCI_SITD_SIZE ((sizeof(struct ehci_soft_sitd) + EHCI_QH_ALIGN - 1) / EHCI_SITD_ALIGN * EHCI_SITD_ALIGN)
+#define EHCI_SITD_CHUNK (EHCI_PAGE_SIZE / EHCI_SITD_SIZE)
+
 struct ehci_xfer {
 	struct usbd_xfer xfer;
 	struct usb_task	abort_task;
@@ -89,6 +98,8 @@ struct ehci_xfer {
 	ehci_soft_qtd_t *sqtdend;
 	ehci_soft_itd_t *itdstart;
 	ehci_soft_itd_t *itdend;
+	ehci_soft_sitd_t *sitdstart;
+	ehci_soft_sitd_t *sitdend;
 	u_int isoc_len;
 	int isdone;	/* used only when DIAGNOSTIC is defined */
 };
@@ -155,6 +166,7 @@ typedef struct ehci_softc {
 	ehci_soft_qh_t *sc_freeqhs;
 	ehci_soft_qtd_t *sc_freeqtds;
 	LIST_HEAD(sc_freeitds, ehci_soft_itd) sc_freeitds;
+	LIST_HEAD(sc_freesitds, ehci_soft_sitd) sc_freesitds;
 
 	int sc_noport;
 	uint8_t sc_hasppc;		/* has Port Power Control */

Reply via email to