Port-arm archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: panic when using mpd on evbarm/kirkwood



Hi,

> please type 'tr' here and report. This will tell us what is using
> the pool in interrupt context.

This is the whole trace:

  panic: pool 'pvepl' is IPL_NONE, but called from interrupt context
  
  Stopped in pid 0.3 (system) at  netbsd:cpu_Debugger+0x4:        bx      r14
  db> tr
  netbsd:vpanic+0x10
          scp=0xc021e170 rlv=0xc021e3fc (netbsd:printf_nolog)
          rsp=0xc9fc3cfc rfp=0xc9fc3d10
          r7=0x00000002
  netbsd:panic+0x14
          scp=0xc021e3e8 rlv=0xc0219b28 (netbsd:pool_get+0x214)
          rsp=0xc9fc3d24 rfp=0xc9fc3d50
  netbsd:pool_get+0x10
          scp=0xc0219924 rlv=0xc01a8f08 (netbsd:pmap_enter+0x6e8)
          rsp=0xc9fc3d54 rfp=0xc9fc3da4
          r10=0xc04b0640 r9=0x01251552
          r8=0x00000000 r7=0x0000000f r6=0xcacb7000 r5=0xc104510c
          r4=0xc04b0640
  netbsd:pmap_enter+0x10
          scp=0xc01a8830 rlv=0xc0024d24 (netbsd:_bus_dmamem_map+0xe8)
          rsp=0xc9fc3da8 rfp=0xc9fc3de4
          r10=0xc04b0640 r9=0x00000004
          r8=0xc04bae20 r7=0x00001000 r6=0xc19fb798 r5=0xcacb7000
          r4=0x01251000
  netbsd:_bus_dmamem_map+0x10
          scp=0xc0024c4c rlv=0xc02ac828 (netbsd:usb_block_allocmem+0x188)
          rsp=0xc9fc3de8 rfp=0xc9fc3e24
          r10=0x00000001 r9=0xc9fc3e9c
          r8=0x00000000 r7=0xc19fb78c r6=0x00001000 r5=0x00001000
          r4=0xc04a02f4
  netbsd:usb_block_allocmem+0x10
          scp=0xc02ac6b0 rlv=0xc02ac914 (netbsd:usb_allocmem+0x3c)
          rsp=0xc9fc3e28 rfp=0xc9fc3e68
          r10=0x00000009 r9=0xcacb6be0
          r8=0x00000630 r7=0xc9fc3e9c r6=0xc04a02f4 r5=0xc19f0b8c
          r4=0x00000000
  netbsd:usb_allocmem+0x10
          scp=0xc02ac8e8 rlv=0xc005b8d4 (netbsd:ehci_device_fs_isoc_start+0x178)
          rsp=0xc9fc3e6c rfp=0xc9fc3ecc
          r10=0x00000009 r9=0xcacb6be0
          r8=0x00000630 r7=0xc1b3df14 r6=0xc1aa6008 r5=0xc19f0b8c
          r4=0x00000000
  netbsd:ehci_device_fs_isoc_start+0x10
          scp=0xc005b76c rlv=0xc02af638 (netbsd:usbd_transfer+0xd4)
          rsp=0xc9fc3ed0 rfp=0xc9fc3ef8
          r10=0xc1b3df50 r9=0xc1aa6008
          r8=0x00000001 r7=0x00000000 r6=0x00000000 r5=0xc21a3a9c
          r4=0xc1b3df14
  netbsd:usbd_transfer+0x10
          scp=0xc02af574 rlv=0xc026b254 (netbsd:uaudio_chan_pintr+0xb4)
          rsp=0xc9fc3efc rfp=0xc9fc3f1c
          r10=0x00000001 r9=0xc1aa6008
          r8=0x00000000 r7=0x00000000 r6=0x00000000 r5=0xc1b3c898
          r4=0xc1b3c820
  netbsd:uaudio_chan_pintr+0x10
          scp=0xc026b1b0 rlv=0xc02b00cc (netbsd:usb_transfer_complete+0x29c)
          rsp=0xc9fc3f20 rfp=0xc9fc3f44
          r5=0xc21a3a9c r4=0xc1b3d8f4
  netbsd:usb_transfer_complete+0x10
          scp=0xc02afe40 rlv=0xc0058f54 (netbsd:ehci_softintr+0x1f4)
          rsp=0xc9fc3f48 rfp=0xc9fc3f74
          r10=0xc1b3d814 r8=0xc1b3d8f4
          r7=0x00000000 r6=0xc03b3148 r5=0xc9fc0154 r4=0xcacb6640
  netbsd:ehci_softintr+0x10
          scp=0xc0058d70 rlv=0xc0128370 (netbsd:softint_thread+0xb0)
          rsp=0xc9fc3f78 rfp=0xc9fc3fb0
          r10=0xc03465d0 r9=0x00000226
          r8=0xc19f9a80 r7=0x00000000 r6=0xc03b3148 r5=0xc9fc0154
          r4=0xc9fc02b4
  netbsd:softint_thread+0x10
          scp=0xc01282d0 rlv=0xc003fc54 (netbsd:lwp_trampoline+0x14)
          rsp=0xc9fc3fb4 rfp=0x00000000
          r10=0x00000000 r9=0x00000000
          r8=0x00000000 r7=0xbfffffff r6=0xffffffff r5=0xc9fc0154
          r4=0xc01282c0
  db>  


It seems to me that it is related to the patch: ehci_device_fs_isoc_start() is
indeed an addition brung by the isochronous TT patch.

I've attached the patch to this email. It is basically the patch that Masao
kindly provided me and that was initially against a 3+ years old codebase.

I *guess* the patch does not handle memory allocation properly with the modern
codebase...


Thanks !

-- 
Sebastien
Index: ehci.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/ehci.c,v
retrieving revision 1.183
diff -u -r1.183 ehci.c
--- ehci.c      23 Dec 2011 00:51:43 -0000      1.183
+++ ehci.c      24 Mar 2013 18:04:25 -0000
@@ -98,6 +98,7 @@
        union {
                ehci_soft_qtd_t *qtd;
                /* ehci_soft_itd_t *itd; */
+               /* ehci_soft_sitd_t *sitd; */
        } tail;
        union {
                /* Control pipe */
@@ -129,6 +130,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 *);
@@ -176,6 +178,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);
 
@@ -194,9 +202,12 @@
                                            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);
 
@@ -309,6 +320,15 @@
        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,
@@ -436,6 +456,7 @@
        if (sc->sc_softitds == NULL)
                return ENOMEM;
        LIST_INIT(&sc->sc_freeitds);
+       LIST_INIT(&sc->sc_freesitds);
        TAILQ_INIT(&sc->sc_intrhead);
        mutex_init(&sc->sc_intrhead_lock, MUTEX_DEFAULT, IPL_USB);
 
@@ -724,13 +745,18 @@
 ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
 {
        int attr;
+       usbd_device_handle dev;
 
        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
+       dev  = ex->xfer.pipe->device;
+       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;
@@ -844,6 +870,43 @@
        ehci_idone(ex);
 }
 
+void
+ehci_check_sitd_intr(ehci_softc_t *sc, struct ehci_xfer *ex) {
+       ehci_soft_sitd_t *sitd;
+
+       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;
+
+       DPRINTFN(12, ("ehci_check_sitd_intr: ex=%p done\n", ex));
+       callout_stop(&(ex->xfer.timeout_handle));
+       ehci_idone(ex);
+}
+
+
 Static void
 ehci_idone(struct ehci_xfer *ex)
 {
@@ -885,9 +948,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;
 
@@ -930,6 +997,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;
@@ -1594,14 +1708,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) {
                DPRINTFN(1,("ehci_open: hshubaddr=%d hshubport=%d\n",
                            hshubaddr, hshubport));
                return USBD_INVAL;
@@ -1697,7 +1804,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;
@@ -1893,6 +2003,56 @@
        exfer->itdend = NULL;
 }
 
+/*Call at splusb*/
+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;
+}
+
+
 /***********/
 
 /*
@@ -2853,6 +3013,70 @@
        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, s, offs, frindex, previndex;
+       usb_dma_t dma;
+
+       s = splusb();
+
+       /* 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) {
+               DPRINTFN(2, ("ehci_alloc_sitd allocating chunk\n"));
+               err = usb_allocmem(&sc->sc_bus, EHCI_SITD_SIZE * 
EHCI_SITD_CHUNK,
+                               EHCI_PAGE_SIZE, &dma);
+
+               if (err) {
+                       DPRINTF(("ehci_alloc_sitd, alloc returned %d\n", err));
+                       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;
+       splx(s);
+
+       return sitd;
+}
+
 Static void
 ehci_free_itd(ehci_softc_t *sc, ehci_soft_itd_t *itd)
 {
@@ -2863,6 +3087,16 @@
        splx(s);
 }
 
+Static void
+ehci_free_sitd(ehci_softc_t *sc, ehci_soft_sitd_t *sitd)
+{
+       int s;
+
+       s = splusb();
+       LIST_INSERT_HEAD(&sc->sc_freesitds, sitd, u.free_list);
+       splx(s);
+}
+
 /****************/
 
 /*
@@ -3053,6 +3287,7 @@
        struct ehci_xfer *exfer;
        ehci_softc_t *sc;
        struct ehci_soft_itd *itd;
+       struct ehci_soft_sitd *sitd;
        int s, i, wake;
 
        epipe = (struct ehci_pipe *) xfer->pipe;
@@ -3108,6 +3343,21 @@
                    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);
+       }
        splx(s);
 
         s = splusb();
@@ -4156,3 +4406,320 @@
                     BUS_DMASYNC_POSTREAD);
 
 }
+
+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 s, offs, total_length;
+       int frindex;
+       u_int huba, dir;
+
+       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;
+
+       DPRINTFN(2, ("ehci_device_fs_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 (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 */
+               DPRINTF(("ehci_device_fs_isoc_start: bInvertal %d invalid\n", 
i));
+               return USBD_INVAL;
+       }
+
+       frames = xfer->nframes;
+
+       if (frames == 0) {
+               DPRINTF(("ehci_device_fs_isoc_start: frames == 0\n"));
+               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.
+        */
+
+       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 = 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;
+
+       mutex_enter(&sc->sc_intrhead_lock);
+       ehci_add_intr_list(sc, exfer);
+       mutex_exit(&sc->sc_intrhead_lock);
+       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_fs_isoc_abort(usbd_xfer_handle xfer)
+{
+       DPRINTFN(1, ("ehci_device_fs_isoc_abort: xfer = %p\n", xfer));
+       ehci_abort_isoc_xfer(xfer, USBD_CANCELLED);
+}
+
+Static void
+ehci_device_fs_isoc_close(usbd_pipe_handle pipe)
+{
+       DPRINTFN(1, ("ehci_device_fs_isoc_close: nothing in the pipe to 
free?\n"));
+}
+
+Static void
+ehci_device_fs_isoc_done(usbd_xfer_handle xfer)
+{
+       struct ehci_xfer *exfer;
+       ehci_softc_t *sc;
+       struct ehci_pipe *epipe;
+       int s;
+
+       exfer = EXFER(xfer);
+       sc = xfer->pipe->device->bus->hci_private;
+       epipe = (struct ehci_pipe *) xfer->pipe;
+
+       s = splusb();
+       epipe->u.isoc.cur_xfers--;
+       mutex_enter(&sc->sc_intrhead_lock);
+       if (xfer->status != USBD_NOMEM && ehci_active_intr_list(exfer)) {
+               ehci_del_intr_list(sc, exfer);
+               ehci_rem_free_sitd_chain(sc, exfer);
+       }
+       mutex_exit(&sc->sc_intrhead_lock);
+       splx(s);
+
+       usb_syncmem(&xfer->dmabuf, 0, xfer->length, BUS_DMASYNC_POSTWRITE |
+                    BUS_DMASYNC_POSTREAD);
+
+}
+
Index: ehcireg.h
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/ehcireg.h,v
retrieving revision 1.33
diff -u -r1.33 ehcireg.h
--- ehcireg.h   17 Oct 2011 16:44:02 -0000      1.33
+++ ehcireg.h   24 Mar 2013 18:04:25 -0000
@@ -245,12 +245,41 @@
 #define EHCI_ITD_ALIGN 32
 
 /* Split Transaction Isochronous Transfer Descriptor */
-typedef struct {
-       volatile ehci_link_t    sitd_next;
+ typedef struct {
        /* XXX many more */
-} ehci_sitd_t;
-#define EHCI_SITD_ALIGN 32
+       volatile u_int32_t sitd_next;
+       volatile u_int32_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 u_int32_t sitd_sched;
+#define EHCI_SITD_SET_SMASK(x) ((x) & 0xff)
+#define EHCI_SITD_SET_CMASK(x) (((x) & 0xff) << 8)
+
+       volatile u_int32_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 u_int32_t sitd_buffer[2];
+#define EHCI_SITD_SET_BPTR(x)  ((x) & 0xfffff000)
+#define EHCI_SITD_SET_OFFS(x)  ((x) & 0xfff)
 
+       volatile u_int32_t sitd_back;
+ } ehci_sitd_t;
+ #define EHCI_SITD_ALIGN 32
+ 
 /* Queue Element Transfer Descriptor */
 #define EHCI_QTD_NBUFFERS 5
 typedef struct {
Index: ehcivar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/ehcivar.h,v
retrieving revision 1.38
diff -u -r1.38 ehcivar.h
--- ehcivar.h   18 Jan 2011 08:29:24 -0000      1.38
+++ ehcivar.h   24 Mar 2013 18:04:25 -0000
@@ -55,7 +55,10 @@
 #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*/
@@ -75,6 +78,12 @@
 #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;
@@ -83,6 +92,8 @@
        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 */
 };
@@ -145,6 +156,7 @@
        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;
        u_int8_t sc_hasppc;             /* has Port Power Control */


Home | Main Index | Thread Index | Old Index