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