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 */