Add isochronous xfer support for ehci(4). From NetBSD.
authormglocker <mglocker@openbsd.org>
Sat, 9 Aug 2008 22:59:20 +0000 (22:59 +0000)
committermglocker <mglocker@openbsd.org>
Sat, 9 Aug 2008 22:59:20 +0000 (22:59 +0000)
OK brad@

sys/dev/usb/ehci.c
sys/dev/usb/ehcireg.h
sys/dev/usb/ehcivar.h
sys/dev/usb/usb.h

index da64110..450a2d0 100644 (file)
@@ -1,8 +1,9 @@
-/*     $OpenBSD: ehci.c,v 1.84 2008/06/29 10:04:15 yuo Exp $ */
+/*     $OpenBSD: ehci.c,v 1.85 2008/08/09 22:59:20 mglocker Exp $ */
 /*     $NetBSD: ehci.c,v 1.66 2004/06/30 03:11:56 mycroft Exp $        */
 
 /*
- * Copyright (c) 2004 The NetBSD Foundation, Inc.
+ * Copyright (c) 2004,2005 The NetBSD Foundation, Inc.
+ * Copyright (c) 2008 Jeremy Morse <jeremy.morse@gmail.com>
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -117,7 +118,10 @@ struct ehci_pipe {
                        u_int length;
                } bulk;
                /* Iso pipe */
-               /* XXX */
+               struct {
+                       u_int next_frame;
+                       u_int cur_xfers;
+               } isoc;
        } u;
 };
 
@@ -131,6 +135,8 @@ void                ehci_softintr(void *);
 int            ehci_intr1(ehci_softc_t *);
 void           ehci_waitintr(ehci_softc_t *, usbd_xfer_handle);
 void           ehci_check_intr(ehci_softc_t *, struct ehci_xfer *);
+void           ehci_check_qh_intr(ehci_softc_t *, struct ehci_xfer *);
+void           ehci_check_itd_intr(ehci_softc_t *, struct ehci_xfer *);
 void           ehci_idone(struct ehci_xfer *);
 void           ehci_timeout(void *);
 void           ehci_timeout_task(void *);
@@ -196,6 +202,13 @@ usbd_status        ehci_alloc_sqtd_chain(struct ehci_pipe *,
 void           ehci_free_sqtd_chain(ehci_softc_t *, ehci_soft_qtd_t *,
                            ehci_soft_qtd_t *);
 
+ehci_soft_itd_t        *ehci_alloc_itd(ehci_softc_t *sc);
+void           ehci_free_itd(ehci_softc_t *sc, ehci_soft_itd_t *itd);
+void           ehci_rem_free_itd_chain(ehci_softc_t *sc,
+                   struct ehci_xfer *exfer);
+void           ehci_abort_isoc_xfer(usbd_xfer_handle xfer,
+                   usbd_status status);
+
 usbd_status    ehci_device_request(usbd_xfer_handle xfer);
 
 usbd_status    ehci_device_setintr(ehci_softc_t *, ehci_soft_qh_t *,
@@ -219,6 +232,10 @@ void               ehci_dump_sqtds(ehci_soft_qtd_t *);
 void           ehci_dump_sqtd(ehci_soft_qtd_t *);
 void           ehci_dump_qtd(ehci_qtd_t *);
 void           ehci_dump_sqh(ehci_soft_qh_t *);
+#if notyet
+void           ehci_dump_sitd(struct ehci_soft_itd *itd);
+void           ehci_dump_itd(struct ehci_soft_itd *);
+#endif
 #ifdef DIAGNOSTIC
 void           ehci_dump_exfer(struct ehci_xfer *);
 #endif
@@ -382,8 +399,19 @@ ehci_init(ehci_softc_t *sc)
                return (err);
        DPRINTF(("%s: flsize=%d\n", sc->sc_bus.bdev.dv_xname,sc->sc_flsize));
        sc->sc_flist = KERNADDR(&sc->sc_fldma, 0);
+
+       for (i = 0; i < sc->sc_flsize; i++) {
+               sc->sc_flist[i] = EHCI_NULL;
+       }
+
        EOWRITE4(sc, EHCI_PERIODICLISTBASE, DMAADDR(&sc->sc_fldma, 0));
 
+       sc->sc_softitds = malloc(sc->sc_flsize * sizeof(ehci_soft_itd_t *),
+           M_USB, M_WAITOK | M_ZERO);
+       if (sc->sc_softitds == NULL)
+               return (ENOMEM);
+       LIST_INIT(&sc->sc_freeitds);
+
        /* Set up the bus struct. */
        sc->sc_bus.methods = &ehci_bus_methods;
        sc->sc_bus.pipe_size = sizeof(struct ehci_pipe);
@@ -654,19 +682,34 @@ ehci_softintr(void *v)
 void
 ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
 {
-       ehci_soft_qtd_t *sqtd, *lsqtd;
-       u_int32_t status;
+       int attr;
 
        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
+               ehci_check_qh_intr(sc, ex);
+
+       return;
+}
+
+void
+ehci_check_qh_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
+{
+       ehci_soft_qtd_t *sqtd, *lsqtd;
+       __uint32_t status;
+
        if (ex->sqtdstart == NULL) {
-               printf("ehci_check_intr: sqtdstart=NULL\n");
+               printf("ehci_check_qh_intr: not valid sqtd\n");
                return;
        }
+
        lsqtd = ex->sqtdend;
 #ifdef DIAGNOSTIC
        if (lsqtd == NULL) {
-               printf("ehci_check_intr: lsqtd==0\n");
+               printf("ehci_check_qh_intr: lsqtd==0\n");
                return;
        }
 #endif
@@ -700,6 +743,63 @@ ehci_check_intr(ehci_softc_t *sc, struct ehci_xfer *ex)
        ehci_idone(ex);
 }
 
+void
+ehci_check_itd_intr(ehci_softc_t *sc, struct ehci_xfer *ex) {
+       ehci_soft_itd_t *itd;
+       int i;
+
+       if (ex->itdstart == NULL) {
+               printf("ehci_check_itd_intr: not valid itd\n");
+               return;
+       }
+
+       itd = ex->itdend;
+#ifdef DIAGNOSTIC
+       if (itd == NULL) {
+               printf("ehci_check_itd_intr: itdend == 0\n");
+               return;
+       }
+#endif
+
+       /*
+        * Step 1, check no active transfers in last itd, meaning we're finished
+        */
+       for (i = 0; i < 8; i++) {
+               if (letoh32(itd->itd.itd_ctl[i]) & EHCI_ITD_ACTIVE)
+                       break;
+       }
+
+       if (i == 8) {
+               goto done; /* All 8 descriptors inactive, it's done */
+       }
+
+       /*
+        * Step 2, check for errors in status bits, throughout chain...
+        */
+
+       DPRINTFN(12, ("ehci_check_itd_intr: active ex=%p\n", ex));
+
+       for (itd = ex->itdstart; itd != ex->itdend; itd = itd->xfer_next) {
+               for (i = 0; i < 8; i++) {
+                       if (letoh32(itd->itd.itd_ctl[i]) & (EHCI_ITD_BUF_ERR |
+                           EHCI_ITD_BABBLE | EHCI_ITD_ERROR))
+                               break;
+               }
+               if (i != 8) { /* Error in one of the itds */
+                       goto done;
+               }
+       } /* itd search loop */
+
+       DPRINTFN(12, ("ehci_check_itd_intr: ex %p itd %p still active\n", ex,
+           ex->itdstart));
+       return;
+
+done:
+       DPRINTFN(12, ("ehci_check_itd_intr: ex=%p done\n", ex));
+       timeout_del(&ex->xfer.timeout_handle);
+       ehci_idone(ex);
+}
+
 void
 ehci_idone(struct ehci_xfer *ex)
 {
@@ -729,7 +829,6 @@ ehci_idone(struct ehci_xfer *ex)
                splx(s);
        }
 #endif
-
        if (xfer->status == USBD_CANCELLED ||
            xfer->status == USBD_TIMEOUT) {
                DPRINTF(("ehci_idone: aborted xfer=%p\n", xfer));
@@ -743,6 +842,55 @@ ehci_idone(struct ehci_xfer *ex)
 #endif
 
        /* The transfer is done, compute actual length and status. */
+
+       if (UE_GET_XFERTYPE(xfer->pipe->endpoint->edesc->bmAttributes)
+           == UE_ISOCHRONOUS) {
+               /* Isoc transfer */
+               struct ehci_soft_itd *itd;
+               int i, nframes, len, uframes;
+
+               nframes = 0;
+               actlen = 0;
+
+               switch (xfer->pipe->endpoint->edesc->bInterval) {
+               case 0:
+                       panic("ehci: isoc xfer suddenly has 0 bInterval, "
+                           "invalid");
+               case 1: uframes = 1; break;
+               case 2: uframes = 2; break;
+               case 3: uframes = 4; break;
+               default: uframes = 8; break;
+               }
+
+               for (itd = ex->itdstart; itd != NULL; itd = itd->xfer_next) {
+                       for (i = 0; i < 8; i += uframes) {
+                               /* 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 = letoh32(itd->itd.itd_ctl[i]);
+                               len = EHCI_ITD_GET_LEN(status);
+                               xfer->frlengths[nframes++] = len;
+                               actlen += len;
+                       }
+
+                       if (nframes >= xfer->nframes)
+                               break;
+               }
+
+               xfer->actlen = actlen;
+               xfer->status = USBD_NORMAL_COMPLETION;
+
+               goto end;
+       }
+
+       /* Continue processing xfers using queue heads */
+
        lsqtd = ex->sqtdend;
        actlen = 0;
        for (sqtd = ex->sqtdstart; sqtd != lsqtd->nextqtd;
@@ -789,7 +937,10 @@ ehci_idone(struct ehci_xfer *ex)
                        xfer->status = USBD_IOERROR; /* more info XXX */
        } else
                xfer->status = USBD_NORMAL_COMPLETION;
-
+       end:
+       /* XXX transfer_complete memcpys out transfer data (for in endpoints)
+        * during this call, before methods->done is called: dma sync required
+        * beforehand? */
        usb_transfer_complete(xfer);
        DPRINTFN(/*12*/2, ("ehci_idone: ex=%p done\n", ex));
 }
@@ -1252,13 +1403,54 @@ ehci_dump_sqh(ehci_soft_qh_t *sqh)
        ehci_dump_qtd(&qh->qh_qtd);
 }
 
+#if notyet
+void
+ehci_dump_itd(struct ehci_soft_itd *itd)
+{
+       ehci_isoc_trans_t t;
+       ehci_isoc_bufr_ptr_t b, b2, b3;
+       int i;
+
+       printf("ITD: next phys=%X\n", itd->itd.itd_next);
+
+       for (i = 0; i < 8;i++) {
+               t = letoh32(itd->itd.itd_ctl[i]);
+               printf("ITDctl %d: stat=%X len=%X ioc=%X pg=%X offs=%X\n", i,
+                   EHCI_ITD_GET_STATUS(t), EHCI_ITD_GET_LEN(t),
+                   EHCI_ITD_GET_IOC(t), EHCI_ITD_GET_PG(t),
+                   EHCI_ITD_GET_OFFS(t));
+       }
+       printf("ITDbufr: ");
+       for (i = 0; i < 7; i++)
+               printf("%X,", EHCI_ITD_GET_BPTR(letoh32(itd->itd.itd_bufr[i])));
+
+       b = letoh32(itd->itd.itd_bufr[0]);
+       b2 = letoh32(itd->itd.itd_bufr[1]);
+       b3 = letoh32(itd->itd.itd_bufr[2]);
+       printf("\nep=%X daddr=%X dir=%d maxpkt=%X multi=%X\n",
+           EHCI_ITD_GET_EP(b), EHCI_ITD_GET_DADDR(b), EHCI_ITD_GET_DIR(b2),
+           EHCI_ITD_GET_MAXPKT(b2), EHCI_ITD_GET_MULTI(b3));
+}
+
+void
+ehci_dump_sitd(struct ehci_soft_itd *itd)
+{
+       printf("SITD %p next=%p prev=%p xfernext=%p physaddr=%X slot=%d\n",
+           itd, itd->u.frame_list.next, itd->u.frame_list.prev,
+           itd->xfer_next, itd->physaddr, itd->slot);
+}
+#endif
+
 #ifdef DIAGNOSTIC
 void
 ehci_dump_exfer(struct ehci_xfer *ex)
 {
-       printf("ehci_dump_exfer: ex=%p\n", ex);
+       printf("ehci_dump_exfer: ex=%p sqtdstart=%p end=%p itdstart=%p end=%p "
+           "isdone=%d\n", ex, ex->sqtdstart, ex->sqtdend, ex->itdstart,
+           ex->itdend, ex->isdone);
 }
 #endif
+
 #endif /* EHCI_DEBUG */
 
 usbd_status
@@ -1312,8 +1504,8 @@ ehci_open(usbd_pipe_handle pipe)
        default: panic("ehci_open: bad device speed %d", dev->speed);
        }
        if (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_ISOCHRONOUS) {
-               printf("%s: *** WARNING: opening low/full speed isochronous "
-                   "device, this does not work yet.\n",
+               printf("%s: *** Error: opening low/full speed isoc device on"
+                   "ehci, this does not work yet. Feel free to implement\n",
                    sc->sc_bus.bdev.dv_xname);
                DPRINTFN(1,("ehci_open: hshubaddr=%d hshubport=%d\n",
                    hshubaddr, hshubport));
@@ -1321,33 +1513,40 @@ ehci_open(usbd_pipe_handle pipe)
        }
 
        naks = 8;               /* XXX */
-       sqh = ehci_alloc_sqh(sc);
-       if (sqh == NULL)
-               goto bad0;
-       /* qh_link filled when the QH is added */
-       sqh->qh.qh_endp = htole32(
-           EHCI_QH_SET_ADDR(addr) |
-           EHCI_QH_SET_ENDPT(UE_GET_ADDR(ed->bEndpointAddress)) |
-           EHCI_QH_SET_EPS(speed) |
-           (xfertype == UE_CONTROL ? EHCI_QH_DTC : 0) |
-           EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) |
-           (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ?
-            EHCI_QH_CTL : 0) |
-           EHCI_QH_SET_NRL(naks));
-       sqh->qh.qh_endphub = htole32(
-           EHCI_QH_SET_MULT(1) |
-           EHCI_QH_SET_HUBA(hshubaddr) |
-           EHCI_QH_SET_PORT(hshubport) |
-           EHCI_QH_SET_CMASK(0x1c) | /* XXX */
-           EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x01 : 0));
-       sqh->qh.qh_curqtd = EHCI_NULL;
-       /* Fill the overlay qTD */
-       sqh->qh.qh_qtd.qtd_next = EHCI_NULL;
-       sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL;
-       sqh->qh.qh_qtd.qtd_status =
-           htole32(EHCI_QTD_SET_TOGGLE(pipe->endpoint->savedtoggle));
 
-       epipe->sqh = sqh;
+       /* Allocate sqh for everything, save isoc xfers */
+       if (xfertype != UE_ISOCHRONOUS) {
+               sqh = ehci_alloc_sqh(sc);
+               if (sqh == NULL)
+                       return (USBD_NOMEM);
+               /* qh_link filled when the QH is added */
+               sqh->qh.qh_endp = htole32(
+                   EHCI_QH_SET_ADDR(addr) |
+                   EHCI_QH_SET_ENDPT(UE_GET_ADDR(ed->bEndpointAddress)) |
+                   EHCI_QH_SET_EPS(speed) |
+                   (xfertype == UE_CONTROL ? EHCI_QH_DTC : 0) |
+                   EHCI_QH_SET_MPL(UGETW(ed->wMaxPacketSize)) |
+                   (speed != EHCI_QH_SPEED_HIGH && xfertype == UE_CONTROL ?
+                   EHCI_QH_CTL : 0) |
+                   EHCI_QH_SET_NRL(naks)
+                   );
+               sqh->qh.qh_endphub = htole32(
+                   EHCI_QH_SET_MULT(1) |
+                   EHCI_QH_SET_HUBA(hshubaddr) |
+                   EHCI_QH_SET_PORT(hshubport) |
+                   EHCI_QH_SET_CMASK(0x1c) | /* XXX */
+                   EHCI_QH_SET_SMASK(xfertype == UE_INTERRUPT ? 0x01 : 0)
+                   );
+               sqh->qh.qh_curqtd = EHCI_NULL;
+               /* Fill the overlay qTD */
+               sqh->qh.qh_qtd.qtd_next = EHCI_NULL;
+               sqh->qh.qh_qtd.qtd_altnext = EHCI_NULL;
+               sqh->qh.qh_qtd.qtd_status =
+                   htole32(EHCI_QTD_SET_TOGGLE(pipe->endpoint->savedtoggle));
+               epipe->sqh = sqh;
+       } else {
+               sqh = NULL;
+       } /*xfertype == UE_ISOC*/
 
        switch (xfertype) {
        case UE_CONTROL:
@@ -1358,7 +1557,7 @@ ehci_open(usbd_pipe_handle pipe)
                        printf("ehci_open: usb_allocmem()=%d\n", err);
 #endif
                if (err)
-                       goto bad1;
+                       goto bad;
                pipe->methods = &ehci_device_ctrl_methods;
                s = splusb();
                ehci_add_qh(sqh, sc->sc_async_head);
@@ -1378,16 +1577,29 @@ ehci_open(usbd_pipe_handle pipe)
                return (ehci_device_setintr(sc, sqh, ival));
        case UE_ISOCHRONOUS:
                pipe->methods = &ehci_device_isoc_methods;
-               return (USBD_INVAL);
+               if (ed->bInterval == 0 || ed->bInterval >= 16) {
+                       printf("ehci: opening pipe with invalid bInterval\n");
+                       err = USBD_INVAL;
+                       goto bad;
+               }
+               if (UGETW(ed->wMaxPacketSize) == 0) {
+                       printf("ehci: zero length endpoint open request\n");
+                       err = USBD_INVAL;
+                       goto bad;
+               }
+               epipe->u.isoc.next_frame = 0;
+               epipe->u.isoc.cur_xfers = 0;
+               break;
        default:
+               DPRINTF(("ehci: bad xfer type %d\n", xfertype));
                return (USBD_INVAL);
        }
        return (USBD_NORMAL_COMPLETION);
 
-bad1:
-       ehci_free_sqh(sc, sqh);
-bad0:
-       return (USBD_NOMEM);
+bad:
+       if (sqh != NULL)
+               ehci_free_sqh(sc, sqh);
+       return (err);
 }
 
 /*
@@ -1496,6 +1708,49 @@ ehci_sync_hc(ehci_softc_t *sc)
        DPRINTFN(2,("ehci_sync_hc: exit\n"));
 }
 
+/*Call at splusb*/
+void
+ehci_rem_free_itd_chain(ehci_softc_t *sc, struct ehci_xfer *exfer)
+{
+       struct ehci_soft_itd *itd, *prev;
+
+       prev = NULL;
+
+       if (exfer->itdstart == NULL || exfer->itdend == NULL)
+               panic("ehci isoc xfer being freed, but with no itd chain");
+
+       for (itd = exfer->itdstart; itd != NULL; itd = itd->xfer_next) {
+               prev = itd->u.frame_list.prev;
+               /* Unlink itd from hardware chain, or frame array */
+               if (prev == NULL) { /* We're at the table head */
+                       sc->sc_softitds[itd->slot] = itd->u.frame_list.next;
+                       sc->sc_flist[itd->slot] = itd->itd.itd_next;
+
+                       if (itd->u.frame_list.next != NULL)
+                               itd->u.frame_list.next->u.frame_list.prev =
+                                   NULL;
+               } else {
+                       /* XXX this part is untested... */
+                       prev->itd.itd_next = itd->itd.itd_next;
+                       prev->u.frame_list.next = itd->u.frame_list.next;
+                       if (itd->u.frame_list.next != NULL)
+                               itd->u.frame_list.next->u.frame_list.prev =
+                                   prev;
+               }
+       }
+
+       prev = NULL;
+       for (itd = exfer->itdstart; itd != NULL; itd = itd->xfer_next) {
+               if (prev != NULL)
+                       ehci_free_itd(sc, prev);
+               prev = itd;
+       }
+       if (prev)
+               ehci_free_itd(sc, prev);
+       exfer->itdstart = NULL;
+       exfer->itdend = NULL;
+}
+
 /***********/
 
 /*
@@ -2327,6 +2582,76 @@ ehci_free_sqtd_chain(ehci_softc_t *sc, ehci_soft_qtd_t *sqtd,
        }
 }
 
+ehci_soft_itd_t *
+ehci_alloc_itd(ehci_softc_t *sc)
+{
+       struct ehci_soft_itd *itd, *freeitd;
+       usbd_status err;
+       int i, s, offs, frindex, previndex;
+       usb_dma_t dma;
+
+       s = splusb();
+
+       /* Find an itd that wasn't freed this frame or last frame. This can
+        * discard itds 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;
+
+       freeitd = NULL;
+       LIST_FOREACH(itd, &sc->sc_freeitds, u.free_list) {
+               if (itd == NULL)
+                       break;
+               if (itd->slot != frindex && itd->slot != previndex) {
+                       freeitd = itd;
+                       break;
+               }
+       }
+
+       if (freeitd == NULL) {
+               DPRINTFN(2, ("ehci_alloc_itd allocating chunk\n"));
+               err = usb_allocmem(&sc->sc_bus, EHCI_ITD_SIZE * EHCI_ITD_CHUNK,
+                   EHCI_PAGE_SIZE, &dma);
+
+               if (err) {
+                       DPRINTF(("ehci_alloc_itd, alloc returned %d\n", err));
+                       return (NULL);
+               }
+
+               for (i = 0; i < EHCI_ITD_CHUNK; i++) {
+                       offs = i * EHCI_ITD_SIZE;
+                       itd = KERNADDR(&dma, offs);
+                       itd->physaddr = DMAADDR(&dma, offs);
+                       itd->dma = dma;
+                       itd->offs = offs;
+                       LIST_INSERT_HEAD(&sc->sc_freeitds, itd, u.free_list);
+               }
+               freeitd = LIST_FIRST(&sc->sc_freeitds);
+       }
+
+       itd = freeitd;
+       LIST_REMOVE(itd, u.free_list);
+       memset(&itd->itd, 0, sizeof(ehci_itd_t));
+       itd->u.frame_list.next = NULL;
+       itd->u.frame_list.prev = NULL;
+       itd->xfer_next = NULL;
+       itd->slot = 0;
+       splx(s);
+
+       return (itd);
+}
+
+void
+ehci_free_itd(ehci_softc_t *sc, ehci_soft_itd_t *itd)
+{
+       int s;
+
+       s = splusb();
+       LIST_INSERT_AFTER(LIST_FIRST(&sc->sc_freeitds), itd, u.free_list);
+       splx(s);
+}
+
 /****************/
 
 /*
@@ -2385,7 +2710,7 @@ ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
                return;
        }
 
-       if (xfer->device->bus->intr_context || !curproc)
+       if (xfer->device->bus->intr_context)
                panic("ehci_abort_xfer: not in process context");
 
        /*
@@ -2555,6 +2880,83 @@ ehci_abort_xfer(usbd_xfer_handle xfer, usbd_status status)
 #undef exfer
 }
 
+ void
+ehci_abort_isoc_xfer(usbd_xfer_handle xfer, usbd_status status)
+{
+       ehci_isoc_trans_t trans_status;
+       struct ehci_pipe *epipe;
+       struct ehci_xfer *exfer;
+       ehci_softc_t *sc;
+       struct ehci_soft_itd *itd;
+       int s, i, wake;
+
+       epipe = (struct ehci_pipe *) xfer->pipe;
+       exfer = EXFER(xfer);
+       sc = (ehci_softc_t *)epipe->pipe.device->bus;
+
+       DPRINTF(("ehci_abort_isoc_xfer: xfer %p pipe %p\n", xfer, epipe));
+
+       if (sc->sc_dying) {
+               s = splusb();
+               xfer->status = status;
+               timeout_del(&xfer->timeout_handle);
+               usb_transfer_complete(xfer);
+               splx(s);
+               return;
+       }
+
+       if (exfer->ehci_xfer_flags & EHCI_XFER_ABORTING) {
+               DPRINTFN(2, ("ehci_abort_isoc_xfer: already aborting\n"));
+
+#ifdef DIAGNOSTIC
+               if (status == USBD_TIMEOUT)
+               printf("ehci_abort_xfer: TIMEOUT while aborting\n");
+#endif
+
+               xfer->status = status;
+               DPRINTFN(2, ("ehci_abort_xfer: waiting for abort to finish\n"));
+               exfer->ehci_xfer_flags |= EHCI_XFER_ABORTING;
+               while (exfer->ehci_xfer_flags & EHCI_XFER_ABORTING)
+                       tsleep(&exfer->ehci_xfer_flags, PZERO, "ehciiaw", 0);
+               return;
+       }
+       exfer->ehci_xfer_flags |= EHCI_XFER_ABORTING;
+
+       xfer->status = status;
+       timeout_del(&xfer->timeout_handle);
+
+       s = splusb();
+       for (itd = exfer->itdstart; itd != NULL; itd = itd->xfer_next) {
+               for (i = 0; i < 8; i++) {
+                       trans_status = letoh32(itd->itd.itd_ctl[i]);
+                       trans_status &= ~EHCI_ITD_ACTIVE;
+                       itd->itd.itd_ctl[i] = htole32(trans_status);
+               }
+       }
+       splx(s);
+
+       s = splusb();
+#ifdef USB_USE_SOFTINTR
+       sc->sc_softwake = 1;
+#endif /* USB_USE_SOFTINTR */
+       usb_schedsoftintr(&sc->sc_bus);
+#ifdef USB_USE_SOFTINTR
+       tsleep(&sc->sc_softwake, PZERO, "ehciab", 0);
+#endif /* USB_USE_SOFTINTR */
+       splx(s);
+
+#ifdef DIAGNOSTIC
+       exfer->isdone = 1;
+#endif
+       wake = exfer->ehci_xfer_flags & EHCI_XFER_ABORTING;
+       exfer->ehci_xfer_flags &= ~(EHCI_XFER_ABORTING | EHCI_XFER_ABORTWAIT);
+       usb_transfer_complete(xfer);
+       if (wake)
+               wakeup(&exfer->ehci_xfer_flags);
+
+       return;
+}
+
 void
 ehci_timeout(void *addr)
 {
@@ -3133,6 +3535,11 @@ ehci_device_intr_abort(usbd_xfer_handle xfer)
                DPRINTFN(1, ("ehci_device_intr_abort: remove\n"));
                xfer->pipe->intrxfer = NULL;
        }
+       /*
+        * XXX - abort_xfer uses ehci_sync_hc, which syncs via the advance
+        *       async doorbell. That's dependant on the async list, wheras
+        *       intr xfers are periodic, should not use this?
+        */
        ehci_abort_xfer(xfer, USBD_CANCELLED);
 }
 
@@ -3210,8 +3617,310 @@ ehci_device_intr_done(usbd_xfer_handle xfer)
 
 /************************/
 
-usbd_status    ehci_device_isoc_transfer(usbd_xfer_handle xfer) { return USBD_IOERROR; }
-usbd_status    ehci_device_isoc_start(usbd_xfer_handle xfer) { return USBD_IOERROR; }
-void           ehci_device_isoc_abort(usbd_xfer_handle xfer) { }
-void           ehci_device_isoc_close(usbd_pipe_handle pipe) { }
-void           ehci_device_isoc_done(usbd_xfer_handle xfer) { }
+usbd_status
+ehci_device_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_isoc_start(xfer));
+}
+
+usbd_status
+ehci_device_isoc_start(usbd_xfer_handle xfer)
+{
+       struct ehci_pipe *epipe;
+       usbd_device_handle dev;
+       ehci_softc_t *sc;
+       struct ehci_xfer *exfer;
+       ehci_soft_itd_t *itd, *prev, *start, *stop;
+       usb_dma_t *dma_buf;
+       int i, j, k, frames, uframes;
+       int s, trans_count, offs, total_length;
+       int frindex;
+
+       start = NULL;
+       prev = NULL;
+       itd = NULL;
+       trans_count = 0;
+       total_length = 0;
+       exfer = (struct ehci_xfer *) xfer;
+       sc = (ehci_softc_t *)xfer->pipe->device->bus;
+       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->itdstart != NULL)
+               return (USBD_IN_PROGRESS);
+
+       DPRINTFN(2, ("ehci_device_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 ((1 << (epipe->pipe.endpoint->edesc->bInterval)) *
+           xfer->nframes >= (sc->sc_flsize - 4) * 8) {
+               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_isoc_start: request");
+
+       if (!exfer->isdone)
+               printf("ehci_device_isoc_start: not done, ex = %p\n", exfer);
+       exfer->isdone = 0;
+#endif
+
+       /*
+        * Step 1: Allocate and initialize itds, how many do we need?
+        * One per transfer if interval >= 8 microframes, fewer if we use
+        * multiple microframes per frame.
+        */
+
+       i = epipe->pipe.endpoint->edesc->bInterval;
+       if (i > 16 || i == 0) {
+               /* Spec page 271 says intervals > 16 are invalid */
+               DPRINTF(("ehci_device_isoc_start: bInvertal %d invalid\n", i));
+               return (USBD_INVAL);
+       } else if (i >= 4) {
+               frames = xfer->nframes;
+               uframes = 8;
+       } else {
+               frames = xfer->nframes + 0x7; /* 7 added for rounding up */
+               uframes = 0;
+               switch (i) {
+                       case 1: frames /= 8; uframes = 1; break;
+                       case 2: frames /= 4; uframes = 2; break;
+                       case 3: frames /= 2; uframes = 4; break;
+               }
+       }
+
+       if (frames == 0) {
+               DPRINTF(("ehci_device_isoc_start: frames == 0\n"));
+               return (USBD_INVAL);
+       }
+
+       dma_buf = &xfer->dmabuf;
+       offs = 0;
+
+       for (i = 0; i < frames; i++) {
+               int froffs = offs;
+               itd = ehci_alloc_itd(sc);
+
+               if (prev != NULL) {
+                       prev->itd.itd_next =
+                           htole32(itd->physaddr | EHCI_LINK_ITD);
+                       prev->xfer_next = itd;
+               } else {
+                       start = itd;
+               }
+
+               /*
+                * Step 1.5, initialize uframes
+                */
+               for (j = 0; j < 8; j += uframes) {
+                       /* Calculate which page in the list this starts in */
+                       int addr = DMAADDR(dma_buf, froffs);
+                       addr = EHCI_PAGE_OFFSET(addr);
+                       addr += (offs - froffs);
+                       addr = EHCI_PAGE(addr);
+                       addr /= EHCI_PAGE_SIZE;
+
+                       /* This gets the initial offset into the first page,
+                        * looks how far further along the current uframe
+                        * offset is. Works out how many pages that is.
+                        */
+
+                       itd->itd.itd_ctl[j] = htole32 ( EHCI_ITD_ACTIVE |
+                           EHCI_ITD_SET_LEN(xfer->frlengths[trans_count]) | 
+                           EHCI_ITD_SET_PG(addr) |
+                           EHCI_ITD_SET_OFFS(EHCI_PAGE_OFFSET(DMAADDR(dma_buf,
+                           offs))));
+
+                       total_length += xfer->frlengths[trans_count];
+                       offs += xfer->frlengths[trans_count];
+                       trans_count++;
+
+                       if (trans_count >= xfer->nframes) { /*Set IOC*/
+                               itd->itd.itd_ctl[j] |= htole32(EHCI_ITD_IOC);
+                       }
+               }       
+
+               /* Step 1.75, set buffer pointers. To simplify matters, all
+                * pointers are filled out for the next 7 hardware pages in
+                * the dma block, so no need to worry what pages to cover
+                * and what to not.
+                */
+
+               for (j=0; j < 7; j++) {
+                       /*
+                        * Don't try to lookup a page that's past the end
+                        * of buffer
+                        */
+                       int page_offs = EHCI_PAGE(froffs +
+                           (EHCI_PAGE_SIZE * j));
+
+                       if (page_offs >= dma_buf->block->size)
+                               break;
+
+                       int page = DMAADDR(dma_buf, page_offs);
+                       page = EHCI_PAGE(page);
+                       itd->itd.itd_bufr[j] =
+                           htole32(EHCI_ITD_SET_BPTR(page) | 
+                           EHCI_LINK_ITD);
+               }
+
+               /*
+                * Other special values
+                */
+
+               k = epipe->pipe.endpoint->edesc->bEndpointAddress;
+               itd->itd.itd_bufr[0] |=
+                   htole32(EHCI_ITD_SET_EP(UE_GET_ADDR(k)) |
+                   EHCI_ITD_SET_DADDR(epipe->pipe.device->address));
+
+               k = (UE_GET_DIR(epipe->pipe.endpoint->edesc->bEndpointAddress))
+                   ? 1 : 0;
+               j = UE_GET_SIZE(
+                   UGETW(epipe->pipe.endpoint->edesc->wMaxPacketSize));
+
+               itd->itd.itd_bufr[1] |= htole32(EHCI_ITD_SET_DIR(k) |
+                   EHCI_ITD_SET_MAXPKT(UE_GET_SIZE(j)));
+
+               /* FIXME: handle invalid trans */
+               itd->itd.itd_bufr[2] |= 
+                   htole32(EHCI_ITD_SET_MULTI(UE_GET_TRANS(j)+1));
+               prev = itd;
+       } /* End of frame */
+
+       stop = itd;
+       stop->xfer_next = NULL;
+       exfer->isoc_len = total_length;
+
+       /*
+        * Part 2: Transfer descriptors have now been set up, now they must
+        * be scheduled into the period 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 = (1 << epipe->pipe.endpoint->edesc->bInterval);
+       if (i / 8 == 0)
+               i = 1;
+       else
+               i /= 8;
+
+       itd = start;
+       for (j = 0; j < frames; j++) {
+               if (itd == NULL)
+                       panic("ehci: unexpectedly ran out of isoc itds, "
+                           "isoc_start");
+
+               itd->itd.itd_next = sc->sc_flist[frindex];
+               if (itd->itd.itd_next == 0)
+                       /* FIXME: frindex table gets initialized to NULL
+                        * or EHCI_NULL? */
+                       itd->itd.itd_next = htole32(EHCI_NULL);
+
+               sc->sc_flist[frindex] = htole32(EHCI_LINK_ITD | itd->physaddr);
+               itd->u.frame_list.next = sc->sc_softitds[frindex];
+               sc->sc_softitds[frindex] = itd;
+               if (itd->u.frame_list.next != NULL)
+                       itd->u.frame_list.next->u.frame_list.prev = itd;
+               itd->slot = frindex;
+               itd->u.frame_list.prev = NULL;
+
+               frindex += i;
+               if (frindex >= sc->sc_flsize)
+                       frindex -= sc->sc_flsize;
+
+               itd = itd->xfer_next;
+       }
+
+       epipe->u.isoc.cur_xfers++;
+       epipe->u.isoc.next_frame = frindex;
+
+       exfer->itdstart = start;
+       exfer->itdend = stop;
+       exfer->sqtdstart = NULL;
+       exfer->sqtdstart = NULL;
+
+       ehci_add_intr_list(sc, exfer);
+       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);
+}
+
+void
+ehci_device_isoc_abort(usbd_xfer_handle xfer)
+{
+       DPRINTFN(1, ("ehci_device_isoc_abort: xfer = %p\n", xfer));
+       ehci_abort_isoc_xfer(xfer, USBD_CANCELLED);
+}
+
+void
+ehci_device_isoc_close(usbd_pipe_handle pipe)
+{
+       DPRINTFN(1, ("ehci_device_isoc_close: nothing in the pipe to free?\n"));
+}
+
+void
+ehci_device_isoc_done(usbd_xfer_handle xfer)
+{
+       struct ehci_xfer *exfer;
+       ehci_softc_t *sc;
+       struct ehci_pipe *epipe;
+       int s;
+
+       exfer = EXFER(xfer);
+       sc = (ehci_softc_t *)xfer->pipe->device->bus;
+       epipe = (struct ehci_pipe *) xfer->pipe;
+
+       s = splusb();
+       epipe->u.isoc.cur_xfers--;
+       if (xfer->status != USBD_NOMEM && ehci_active_intr_list(exfer)) {
+               ehci_del_intr_list(exfer);
+               ehci_rem_free_itd_chain(sc, exfer);
+       }
+       splx(s);
+}
index 482a1a7..b72b8bf 100644 (file)
@@ -1,8 +1,8 @@
-/*     $OpenBSD: ehcireg.h,v 1.14 2008/06/26 05:42:18 ray Exp $ */
+/*     $OpenBSD: ehcireg.h,v 1.15 2008/08/09 22:59:20 mglocker Exp $ */
 /*     $NetBSD: ehcireg.h,v 1.17 2004/06/23 06:45:56 mycroft Exp $     */
 
 /*
- * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * Copyright (c) 2001, 2004 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -186,42 +186,44 @@ typedef u_int32_t ehci_link_t;
 #define EHCI_LINK_ADDR(x)      ((x) &~ 0x1f)
 
 typedef u_int32_t ehci_physaddr_t;
+typedef u_int32_t ehci_isoc_trans_t;
+typedef u_int32_t ehci_isoc_bufr_ptr_t;
 #define        EHCI_BUFPTR_MASK        0xfffff000
 
 /* Isochronous Transfer Descriptor */
 #define EHCI_ITD_NTRANS                8
 #define EHCI_ITD_NBUFFERS      7
 typedef struct {
-       ehci_link_t     itd_next;
-       u_int32_t       itd_trans[EHCI_ITD_NTRANS];
-#define EHCI_ITD_GET_OFFSET(x) (((x) >>  0) & 0xfff) /* offset from buf ptr */
-#define EHCI_ITD_SET_OFFSET(x) (x)
-#define EHCI_ITD_GET_PG(x)     (((x) >> 12) & 0x7) /* buffer page */
-#define EHCI_ITD_SET_PG(x)     ((x) << 12)
-#define EHCI_ITD_GET_IOC(x)    (((x) >> 15) &  0x1) /* interrupt on complete */
-#define EHCI_ITD_IOC           0x00008000
-#define EHCI_ITD_GET_XLEN(x)   (((x) >> 16) & 0xfff) /* transaction length */
-#define EHCI_ITD_SET_XLEN(x)   ((x) << 12)
-#define EHCI_ITD_GET_STATUS(x) (((x) >> 28) & 0xf) /* duh */
-#define EHCI_ITD_SET_STATUS(x) ((x) << 28)
-#define  EHCI_ITD_ACTIVE       0x8
-#define  EHCI_ITD_BUFERR       0x4
-#define  EHCI_ITD_BABBLE       0x2
-#define  EHCI_ITD_XACTERR      0x1
-       ehci_physaddr_t itd_buffer[EHCI_ITD_NBUFFERS];
-/* page (buffer) 0 */
-#define EHCI_ITD_GET_ADDR(x)   (((x) >>  0) & 0x7f) /* endpoint addr */
-#define EHCI_ITD_SET_ADDR(x)   (x)
-#define EHCI_ITD_GET_ENDPT(x)  (((x) >>  8) & 0xf) /* endpoint no */
-#define EHCI_ITD_SET_ENDPT(x)  ((x) <<  8)
-/* page (buffer) 2 */
-#define EHCI_ITD_GET_MPS(x)    (((x) >>  0) & 0x7ff) /* max packet size */
-#define EHCI_ITD_SET_MPS(x)    (x)
-#define EHCI_ITD_DIRECTION     0x00000800
-/* page (buffer) 3 */
-#define EHCI_ITD_GET_MULTI(x)  (((x) >>  0) & 0x3) /* trans per microframe */
-#define EHCI_ITD_SET_MULTI(x)  (x)
-       ehci_physaddr_t itd_buffer_hi[EHCI_ITD_NBUFFERS]; /* 64bit */
+       volatile ehci_link_t            itd_next;
+       volatile ehci_isoc_trans_t      itd_ctl[8];
+#define EHCI_ITD_GET_STATUS(x) (((x) >> 28) & 0xf)
+#define EHCI_ITD_SET_STATUS(x) (((x) & 0xf) << 28)
+#define EHCI_ITD_ACTIVE                0x80000000
+#define EHCI_ITD_BUF_ERR       0x40000000
+#define EHCI_ITD_BABBLE                0x20000000
+#define EHCI_ITD_ERROR         0x10000000
+#define EHCI_ITD_GET_LEN(x)    (((x) >> 16) & 0xfff)
+#define EHCI_ITD_SET_LEN(x)    (((x) & 0xfff) << 16)
+#define EHCI_ITD_IOC           0x8000
+#define EHCI_ITD_GET_IOC(x)    (((x) >> 15) & 1)
+#define EHCI_ITD_SET_IOC(x)    (((x) << 15) & EHCI_ITD_IOC)
+#define EHCI_ITD_GET_PG(x)     (((x) >> 12) & 0xf)
+#define EHCI_ITD_SET_PG(x)     (((x) & 0xf) << 12)
+#define EHCI_ITD_GET_OFFS(x)   (((x) >> 0) & 0xfff)
+#define EHCI_ITD_SET_OFFS(x)   (((x) & 0xfff) << 0)
+       volatile ehci_isoc_bufr_ptr_t   itd_bufr[7];
+#define EHCI_ITD_GET_BPTR(x)   ((x) & 0xfffff000)
+#define EHCI_ITD_SET_BPTR(x)   ((x) & 0xfffff000)
+#define EHCI_ITD_GET_EP(x)     (((x) >> 8) & 0xf)
+#define EHCI_ITD_SET_EP(x)     (((x) & 0xf) << 8)
+#define EHCI_ITD_GET_DADDR(x)  ((x) & 0x7f)
+#define EHCI_ITD_SET_DADDR(x)  ((x) & 0x7f)
+#define EHCI_ITD_GET_DIR(x)    (((x) >> 11) & 1)
+#define EHCI_ITD_SET_DIR(x)    (((x) & 1) << 11)
+#define EHCI_ITD_GET_MAXPKT(x) ((x) & 0x7ff)
+#define EHCI_ITD_SET_MAXPKT(x) ((x) & 0x7ff)
+#define EHCI_ITD_GET_MULTI(x)  ((x) & 0x3)
+#define EHCI_ITD_SET_MULTI(x)  ((x) & 0x3)
 } ehci_itd_t;
 #define EHCI_ITD_ALIGN         32
 
index eba0ac7..b69157a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ehcivar.h,v 1.15 2008/06/26 05:42:18 ray Exp $ */
+/*     $OpenBSD: ehcivar.h,v 1.16 2008/08/09 22:59:20 mglocker Exp $ */
 /*     $NetBSD: ehcivar.h,v 1.19 2005/04/29 15:04:29 augustss Exp $    */
 
 /*
@@ -52,12 +52,36 @@ typedef struct ehci_soft_qh {
 #define EHCI_SQH_SIZE ((sizeof (struct ehci_soft_qh) + EHCI_QH_ALIGN - 1) / EHCI_QH_ALIGN * EHCI_QH_ALIGN)
 #define EHCI_SQH_CHUNK (EHCI_PAGE_SIZE / EHCI_SQH_SIZE)
 
+typedef struct ehci_soft_itd {
+       ehci_itd_t itd;
+       union {
+               struct {
+                       /* soft_itds links in a periodic frame*/
+                       struct ehci_soft_itd *next;
+                       struct ehci_soft_itd *prev;
+               } frame_list;
+               /* circular list of free itds */
+               LIST_ENTRY(ehci_soft_itd) free_list;
+       } u;
+       struct ehci_soft_itd *xfer_next; /* Next soft_itd in xfer */
+       ehci_physaddr_t physaddr;
+       usb_dma_t dma;
+       int offs;
+       int slot;
+       struct timeval t; /* store free time */
+} ehci_soft_itd_t;
+#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)
+
 struct ehci_xfer {
        struct usbd_xfer xfer;
        struct usb_task abort_task;
        LIST_ENTRY(ehci_xfer) inext; /* list of active xfers */
        ehci_soft_qtd_t *sqtdstart;
        ehci_soft_qtd_t *sqtdend;
+       ehci_soft_itd_t *itdstart;
+       ehci_soft_itd_t *itdend;
+       u_int isoc_len;
        u_int32_t ehci_xfer_flags;
 #ifdef DIAGNOSTIC
        int isdone;
@@ -84,6 +108,8 @@ struct ehci_soft_islot {
 #define EHCI_HASH_SIZE 128
 #define EHCI_COMPANION_MAX 8
 
+#define EHCI_FREE_LIST_INTERVAL 100
+
 typedef struct ehci_softc {
        struct usbd_bus sc_bus;         /* base device */
        bus_space_tag_t iot;
@@ -107,10 +133,16 @@ typedef struct ehci_softc {
 
        struct ehci_soft_islot sc_islots[EHCI_INTRQHS];
 
+       /* jcmm - an array matching sc_flist, but with software pointers,
+        * not hardware address pointers
+        */
+       struct ehci_soft_itd **sc_softitds;
+
        LIST_HEAD(, ehci_xfer) sc_intrhead;
 
        ehci_soft_qh_t *sc_freeqhs;
        ehci_soft_qtd_t *sc_freeqtds;
+       LIST_HEAD(sc_freeitds, ehci_soft_itd) sc_freeitds;
 
        int sc_noport;
        u_int8_t sc_addr;               /* device address */
index 04a0417..563d9c4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: usb.h,v 1.32 2008/06/29 10:04:15 yuo Exp $ */
+/*     $OpenBSD: usb.h,v 1.33 2008/08/09 22:59:20 mglocker Exp $ */
 /*     $NetBSD: usb.h,v 1.69 2002/09/22 23:20:50 augustss Exp $        */
 /*     $FreeBSD: src/sys/dev/usb/usb.h,v 1.14 1999/11/17 22:33:46 n_hibma Exp $        */
 
@@ -240,6 +240,8 @@ typedef struct {
 #define  UE_ISO_SYNC   0x0c
 #define UE_GET_ISO_TYPE(a)     ((a) & UE_ISO_TYPE)
        uWord           wMaxPacketSize;
+#define UE_GET_TRANS(a)        (((a) >> 11) & 0x3)
+#define UE_GET_SIZE(a) ((a) & 0x7ff)
        uByte           bInterval;
 } __packed usb_endpoint_descriptor_t;
 #define USB_ENDPOINT_DESCRIPTOR_SIZE 7