Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/nick-nhusb]: src/sys/dev/usb Add full speed isoc support to ehci(4). Bas...
details: https://anonhg.NetBSD.org/src/rev/a447fa3e083e
branches: nick-nhusb
changeset: 334053:a447fa3e083e
user: skrll <skrll%NetBSD.org@localhost>
date: Sun Nov 30 13:46:00 2014 +0000
description:
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
diffstat:
sys/dev/usb/ehci.c | 611 ++++++++++++++++++++++++++++++++++++++++++++++++-
sys/dev/usb/ehcireg.h | 32 ++-
sys/dev/usb/ehcivar.h | 16 +-
3 files changed, 638 insertions(+), 21 deletions(-)
diffs (truncated from 858 to 300 lines):
diff -r 6472d7a7c6c9 -r a447fa3e083e sys/dev/usb/ehci.c
--- a/sys/dev/usb/ehci.c Sun Nov 30 13:14:11 2014 +0000
+++ b/sys/dev/usb/ehci.c Sun Nov 30 13:46:00 2014 +0000
@@ -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 @@
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_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_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 @@
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 @@
.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 @@
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 @@
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 @@
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 @@
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 @@
/* 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 @@
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 @@
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 @@
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 @@
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)
Home |
Main Index |
Thread Index |
Old Index