Date: Wed, 23 Jun 2004 10:02:10 GMT From: Matthew Gream <matthew.gream@pobox.com> To: FreeBSD-gnats-submit@FreeBSD.org Subject: kern/68232: ugen(4) isochronous handling correction and tx support Message-ID: <200406231002.i5NA2AUh019693@home.matthewgream.net> Resent-Message-ID: <200406231010.i5NAATWa016500@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 68232 >Category: kern >Synopsis: ugen(4) isochronous handling correction and tx support >Confidential: no >Severity: non-critical >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Wed Jun 23 10:10:28 GMT 2004 >Closed-Date: >Last-Modified: >Originator: Matthew Gream <matthew.gream@pobox.com> >Release: FreeBSD 5.2.1-RELEASE i386 >Organization: >Environment: System: FreeBSD usbdev 5.2.1-RELEASE FreeBSD 5.2.1-RELEASE #9: Tue Jun 22 13:44:23 BST 2004 root@usbdev:/usr/src/sys/i386/compile/USBDEV i386 >Description: see netbsd pr: kern/25960 http://www.netbsd.org/cgi-bin/query-pr-single.pl?number=25960 Problems with ugen(4) isoc handling, and lack of write support. >How-To-Repeat: see netbsd pr: kern/25960 >Fix: see netbsd pr: kern/25960 ported to 5.2.1-RELEASE, below note: partially reverts some changes to ohci isoc handling that broke isoc rx. (to be honest: the ohci/ugen isoc handling seems to need a good dose of attention) Index: ohci.c =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/ohci.c,v retrieving revision 1.139 diff -u -u -r1.139 ohci.c --- ohci.c 25 Nov 2003 02:23:29 -0000 1.139 +++ ohci.c 23 Jun 2004 07:34:23 -0000 @@ -8,7 +8,7 @@ */ #include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: src/sys/dev/usb/ohci.c,v 1.139 2003/11/25 02:23:29 iedowse Exp $"); /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -927,9 +927,9 @@ bad4: ohci_free_sed(sc, sc->sc_isoc_head); bad3: - ohci_free_sed(sc, sc->sc_ctrl_head); - bad2: ohci_free_sed(sc, sc->sc_bulk_head); + bad2: + ohci_free_sed(sc, sc->sc_ctrl_head); bad1: usb_freemem(&sc->sc_bus, &sc->sc_hccadma); return (err); @@ -978,14 +978,6 @@ ohci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer) { struct ohci_softc *sc = (struct ohci_softc *)bus; - struct ohci_xfer *oxfer = (struct ohci_xfer *)xfer; - ohci_soft_itd_t *sitd; - - if (oxfer->ohci_xfer_flags & OHCI_ISOC_DIRTY) { - for (sitd = xfer->hcpriv; sitd != NULL && sitd->xfer == xfer; - sitd = sitd->nextitd) - ohci_free_sitd(sc, sitd); - } #ifdef DIAGNOSTIC if (xfer->busy_free != XFER_BUSY) { @@ -1478,22 +1470,48 @@ opipe = (struct ohci_pipe *)xfer->pipe; if (opipe->aborting) continue; - - cc = OHCI_ITD_GET_CC(le32toh(sitd->itd.itd_flags)); - if (cc == OHCI_CC_NO_ERROR) { - /* XXX compute length for input */ - if (sitd->flags & OHCI_CALL_DONE) { - opipe->u.iso.inuse -= xfer->nframes; - /* XXX update frlengths with actual length */ - /* XXX xfer->actlen = actlen; */ - xfer->status = USBD_NORMAL_COMPLETION; - s = splusb(); - usb_transfer_complete(xfer); - splx(s); + + if (sitd->flags & OHCI_CALL_DONE) { + ohci_soft_itd_t *next; + int i, j, actlen, iframes, dir; + + opipe->u.iso.inuse -= xfer->nframes; + dir = UE_GET_DIR(xfer->pipe->endpoint->edesc-> + bEndpointAddress); + xfer->status = USBD_NORMAL_COMPLETION; + actlen = 0; + for (i = 0, sitd = xfer->hcpriv;; sitd = next) { + next = sitd->nextitd; + if (OHCI_ITD_GET_CC(le32toh(sitd-> + itd.itd_flags)) != OHCI_CC_NO_ERROR) + xfer->status = USBD_IOERROR; + /* For input, update frlengths with actual */ + /* XXX anything necessary for output? */ + if (dir == UE_DIR_IN && + xfer->status == USBD_NORMAL_COMPLETION) { + iframes = OHCI_ITD_GET_FC(le32toh( + sitd->itd.itd_flags)); + for (j = 0; j < iframes; i++, j++) { + len = le16toh(sitd-> + itd.itd_offset[j]); + len = + (OHCI_ITD_PSW_GET_CC(len) == + OHCI_CC_NOT_ACCESSED) ? 0 : + OHCI_ITD_PSW_LENGTH(len); + xfer->frlengths[i] = len; + actlen += len; + } + } + if (sitd->flags & OHCI_CALL_DONE) + break; + ohci_free_sitd(sc, sitd); } - } else { - /* XXX Do more */ - xfer->status = USBD_IOERROR; + ohci_free_sitd(sc, sitd); + if (dir == UE_DIR_IN && + xfer->status == USBD_NORMAL_COMPLETION) + xfer->actlen = actlen; + xfer->hcpriv = NULL; + s = splusb(); usb_transfer_complete(xfer); splx(s); @@ -1521,7 +1539,6 @@ panic("ohci_device_ctrl_done: not a request"); } #endif - xfer->hcpriv = NULL; } void @@ -1536,8 +1553,6 @@ DPRINTFN(10,("ohci_device_intr_done: xfer=%p, actlen=%d\n", xfer, xfer->actlen)); - xfer->hcpriv = NULL; - if (xfer->pipe->repeat) { data = opipe->tail.td; tail = ohci_alloc_std(sc); /* XXX should reuse TD */ @@ -1571,10 +1586,7 @@ void ohci_device_bulk_done(usbd_xfer_handle xfer) { - DPRINTFN(10,("ohci_device_bulk_done: xfer=%p, actlen=%d\n", - xfer, xfer->actlen)); - - xfer->hcpriv = NULL; + DPRINTFN(10,("ohci_device_bulk_done: xfer=%p\n", xfer)); } void @@ -1614,13 +1626,13 @@ void ohci_root_intr_done(usbd_xfer_handle xfer) { - xfer->hcpriv = NULL; + DPRINTFN(10,("ohci_root_intr_done: xfer=%p\n", xfer)); } void ohci_root_ctrl_done(usbd_xfer_handle xfer) { - xfer->hcpriv = NULL; + DPRINTFN(10,("ohci_root_ctrl_done: xfer=%p\n", xfer)); } /* @@ -2114,7 +2126,7 @@ } sed->ed.ed_flags = htole32( OHCI_ED_SET_FA(addr) | - OHCI_ED_SET_EN(ed->bEndpointAddress) | + OHCI_ED_SET_EN(UE_GET_ADDR(ed->bEndpointAddress)) | (dev->speed == USB_SPEED_LOW ? OHCI_ED_SPEED : 0) | fmt | OHCI_ED_SET_MAXP(UGETW(ed->wMaxPacketSize))); @@ -2612,7 +2624,7 @@ } break; case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): - if (value != 0) { + if ((value & 0xff) != 0) { err = USBD_IOERROR; goto ret; } @@ -3247,9 +3259,8 @@ ohci_softc_t *sc = (ohci_softc_t *)dev->bus; ohci_soft_ed_t *sed = opipe->sed; struct iso *iso = &opipe->u.iso; - struct ohci_xfer *oxfer = (struct ohci_xfer *)xfer; ohci_soft_itd_t *sitd, *nsitd; - ohci_physaddr_t buf, offs, noffs, bp0, tdphys; + ohci_physaddr_t buf, offs, noffs, bp0; int i, ncur, nframes; int s; @@ -3267,24 +3278,6 @@ iso->next)); } - if (xfer->hcpriv) { - for (sitd = xfer->hcpriv; sitd != NULL && sitd->xfer == xfer; - sitd = sitd->nextitd) - ohci_free_sitd(sc, sitd); /* Free ITDs in prev xfer*/ - - if (sitd == NULL) { - sitd = ohci_alloc_sitd(sc); - if (sitd == NULL) - panic("cant alloc isoc"); - opipe->tail.itd = sitd; - tdphys = sitd->physaddr; - sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* Stop*/ - sed->ed.ed_headp = - sed->ed.ed_tailp = htole32(tdphys); - sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* Start.*/ - } - } - sitd = opipe->tail.itd; buf = DMAADDR(&xfer->dmabuf, 0); bp0 = OHCI_PAGE(buf); @@ -3353,8 +3346,6 @@ xfer->status = USBD_IN_PROGRESS; - oxfer->ohci_xfer_flags |= OHCI_ISOC_DIRTY; - #ifdef USB_DEBUG if (ohcidebug > 5) { DPRINTF(("ohci_device_isoc_enter: frame=%d\n", @@ -3386,8 +3377,6 @@ { struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe; ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus; - ohci_soft_ed_t *sed; - int s; DPRINTFN(5,("ohci_device_isoc_start: xfer=%p\n", xfer)); @@ -3399,13 +3388,6 @@ printf("ohci_device_isoc_start: not in progress %p\n", xfer); #endif - /* XXX anything to do? */ - - s = splusb(); - sed = opipe->sed; /* Turn off ED skip-bit to start processing */ - sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* ED's ITD list.*/ - splx(s); - return (USBD_IN_PROGRESS); } @@ -3501,23 +3483,7 @@ void ohci_device_isoc_done(usbd_xfer_handle xfer) { - /* This null routine corresponds to non-isoc "done()" routines - * that free the stds associated with an xfer after a completed - * xfer interrupt. However, in the case of isoc transfers, the - * sitds associated with the transfer have already been processed - * and reallocated for the next iteration by - * "ohci_device_isoc_transfer()". - * - * Routine "usb_transfer_complete()" is called at the end of every - * relevant usb interrupt. "usb_transfer_complete()" indirectly - * calls 1) "ohci_device_isoc_transfer()" (which keeps pumping the - * pipeline by setting up the next transfer iteration) and 2) then - * calls "ohci_device_isoc_done()". Isoc transfers have not been - * working for the ohci usb because this routine was trashing the - * xfer set up for the next iteration (thus, only the first - * UGEN_NISOREQS xfers outstanding on an open would work). Perhaps - * this could all be re-factored, but that's another pass... - */ + DPRINTFN(10,("ohci_device_isoc_done: xfer=%p\n", xfer)); } usbd_status Index: ohcireg.h =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/ohcireg.h,v retrieving revision 1.20 diff -u -u -r1.20 ohcireg.h --- ohcireg.h 15 Jul 2003 23:12:54 -0000 1.20 +++ ohcireg.h 23 Jun 2004 07:34:23 -0000 @@ -1,5 +1,5 @@ /* $NetBSD: ohcireg.h,v 1.17 2000/04/01 09:27:35 augustss Exp $ */ -/* $FreeBSD$ */ +/* $FreeBSD: src/sys/dev/usb/ohcireg.h,v 1.20 2003/07/15 23:12:54 jmg Exp $ */ /* @@ -147,7 +147,6 @@ #define OHCI_PAGE_SIZE 0x1000 #define OHCI_PAGE(x) ((x) &~ 0xfff) #define OHCI_PAGE_OFFSET(x) ((x) & 0xfff) -#define OHCI_PAGE_MASK(x) ((x) & 0xfff) typedef struct { u_int32_t ed_flags; Index: ohcivar.h =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/ohcivar.h,v retrieving revision 1.35 diff -u -u -r1.35 ohcivar.h --- ohcivar.h 15 Jul 2003 23:19:49 -0000 1.35 +++ ohcivar.h 23 Jun 2004 07:34:23 -0000 @@ -1,5 +1,5 @@ /* $NetBSD: ohcivar.h,v 1.30 2001/12/31 12:20:35 augustss Exp $ */ -/* $FreeBSD$ */ +/* $FreeBSD: src/sys/dev/usb/ohcivar.h,v 1.35 2003/07/15 23:19:49 jmg Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -151,9 +151,7 @@ struct ohci_xfer { struct usbd_xfer xfer; struct usb_task abort_task; - u_int32_t ohci_xfer_flags; }; -#define OHCI_ISOC_DIRTY 0x01 #define OXFER(xfer) ((struct ohci_xfer *)(xfer)) Index: ugen.c =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/ugen.c,v retrieving revision 1.81 diff -u -u -r1.81 ugen.c --- ugen.c 9 Nov 2003 09:17:22 -0000 1.81 +++ ugen.c 23 Jun 2004 07:34:23 -0000 @@ -7,7 +7,7 @@ */ #include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: src/sys/dev/usb/ugen.c,v 1.81 2003/11/09 09:17:22 tanimura Exp $"); /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -94,8 +94,8 @@ #define UGEN_BBSIZE 1024 #define UGEN_NISOFRAMES 500 /* 0.5 seconds worth */ -#define UGEN_NISOREQS 6 /* number of outstanding xfer requests */ -#define UGEN_NISORFRMS 4 /* number of frames (miliseconds) per req */ +#define UGEN_NISOREQS 4 /* number of outstanding xfer requests */ +#define UGEN_NISORFRMS 10 /* number of frames (milliseconds) per req */ struct ugen_endpoint { struct ugen_softc *sc; @@ -115,6 +115,9 @@ u_char *limit; /* end of circular buffer (isoc) */ u_char *cur; /* current read location (isoc) */ u_int32_t timeout; + int isorate; + int isoflen; + int isofres; struct isoreq { struct ugen_endpoint *sce; usbd_xfer_handle xfer; @@ -134,11 +137,67 @@ struct ugen_endpoint sc_endpoints[USB_MAX_ENDPOINTS][2]; #define OUT 0 #define IN 1 + int sc_isorate; int sc_refcnt; u_char sc_dying; }; +#define ISOC_CALC_FLEN(r) \ + ((r / USB_FRAMES_PER_SECOND) & (~1)) +#define ISOC_CALC_FRES(r) \ + (((r - USB_FRAMES_PER_SECOND * ISOC_CALC_FLEN(r)) / \ + (USB_FRAMES_PER_SECOND / UGEN_NISORFRMS)) >> 1) +#define ISOC_MAKE_FLEN(l,r,o) \ + (l + ((o < r) ? 2 : 0)) + +#define IBUF_SPACE_ALLOC(sce,s,d) { \ + sce->ibuf = malloc(s, d, M_WAITOK); \ + sce->cur = sce->fill = sce->ibuf; \ + sce->limit = sce->ibuf + (s); \ +} +#define IBUF_SPACE_FREE(sce,d) \ + if (sce->ibuf != NULL) { \ + free(sce->ibuf, d); \ + sce->ibuf = NULL; \ + } +#define IBUF_SPACE_PNTR(sce) \ + (sce->ibuf) +#define IBUF_SPACE_WR_PNTR(sce) \ + (sce->fill) +#define IBUF_SPACE_WR_SIZE(sce) \ + (sce->cur > sce->fill ? \ + (sce->cur - sce->fill) - 1 : \ + (sce->limit - sce->fill) + (sce->cur - sce->ibuf) - 1) +#define IBUF_SPACE_WR_ZERO(sce) \ + (sce->fill + 1 == sce->cur) || \ + (sce->fill + 1 == sce->limit && sce->cur == sce->ibuf) +#define IBUF_SPACE_WR_CHNK(sce) \ + (sce->cur > sce->fill ? \ + (sce->cur - sce->fill) - 1 : \ + (sce->limit - sce->fill) - (sce->cur == sce->ibuf ? 1 : 0)) +#define IBUF_SPACE_WR_INCR(sce, n) { \ + sce->fill += (n); \ + if (sce->fill >= sce->limit) \ + sce->fill = sce->ibuf + (sce->fill - sce->limit); \ + } +#define IBUF_SPACE_RD_PNTR(sce) \ + (sce->cur) +#define IBUF_SPACE_RD_SIZE(sce) \ + (sce->fill >= sce->cur ? \ + sce->fill - sce->cur : \ + (sce->limit - sce->cur) + (sce->fill - sce->ibuf)) +#define IBUF_SPACE_RD_ZERO(sce) \ + (sce->fill == sce->cur) +#define IBUF_SPACE_RD_CHNK(sce) \ + (sce->fill > sce->cur ? \ + sce->fill - sce->cur : sce->limit - sce->cur) +#define IBUF_SPACE_RD_INCR(sce, n) { \ + sce->cur += (n); \ + if (sce->cur >= sce->limit) \ + sce->cur = sce->ibuf + (sce->cur - sce->limit); \ + } + #if defined(__NetBSD__) || defined(__OpenBSD__) cdev_decl(ugen); #elif defined(__FreeBSD__) @@ -168,12 +227,13 @@ Static void ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status); -Static void ugen_isoc_rintr(usbd_xfer_handle xfer, usbd_private_handle addr, +Static void ugenintr_isoc(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status); Static int ugen_do_read(struct ugen_softc *, int, struct uio *, int); Static int ugen_do_write(struct ugen_softc *, int, struct uio *, int); Static int ugen_do_ioctl(struct ugen_softc *, int, u_long, caddr_t, int, usb_proc_ptr); +Static int ugen_do_poll(struct ugen_softc *, int, int, usb_proc_ptr); #if defined(__FreeBSD__) Static void ugen_make_devnodes(struct ugen_softc *sc); Static void ugen_destroy_devnodes(struct ugen_softc *sc); @@ -391,6 +451,7 @@ usbd_xfer_handle xfer; void *buf; int i, j; + int s; USB_GET_SC_OPEN(ugen, unit, sc); @@ -440,18 +501,22 @@ isize = UGETW(edesc->wMaxPacketSize); if (isize == 0) /* shouldn't happen */ return (EINVAL); - sce->ibuf = malloc(isize, M_USBDEV, M_WAITOK); DPRINTFN(5, ("ugenopen: intr endpt=%d,isize=%d\n", endpt, isize)); - if (clalloc(&sce->q, UGEN_IBSIZE, 0) == -1) + IBUF_SPACE_ALLOC(sce, isize, M_USBDEV); + if (!IBUF_SPACE_PNTR(sce)) + return (ENOMEM); + if (clalloc(&sce->q, UGEN_IBSIZE, 0) == -1) { + IBUF_SPACE_FREE(sce, M_USBDEV); return (ENOMEM); + } err = usbd_open_pipe_intr(sce->iface, edesc->bEndpointAddress, USBD_SHORT_XFER_OK, &sce->pipeh, sce, sce->ibuf, isize, ugenintr, USBD_DEFAULT_INTERVAL); if (err) { - free(sce->ibuf, M_USBDEV); + IBUF_SPACE_FREE(sce, M_USBDEV); clfree(&sce->q); return (EIO); } @@ -464,25 +529,25 @@ return (EIO); break; case UE_ISOCHRONOUS: - if (dir == OUT) - return (EINVAL); isize = UGETW(edesc->wMaxPacketSize); if (isize == 0) /* shouldn't happen */ return (EINVAL); - sce->ibuf = malloc(isize * UGEN_NISOFRAMES, - M_USBDEV, M_WAITOK); - sce->cur = sce->fill = sce->ibuf; - sce->limit = sce->ibuf + isize * UGEN_NISOFRAMES; DPRINTFN(5, ("ugenopen: isoc endpt=%d, isize=%d\n", endpt, isize)); + IBUF_SPACE_ALLOC(sce, isize * UGEN_NISOFRAMES, + M_USBDEV); + if (!IBUF_SPACE_PNTR(sce)) + return (ENOMEM); err = usbd_open_pipe(sce->iface, edesc->bEndpointAddress, 0, &sce->pipeh); if (err) { - free(sce->ibuf, M_USBDEV); + IBUF_SPACE_FREE(sce, M_USBDEV); return (EIO); } + sce->isorate = sc->sc_isorate; + sce->isoflen = ISOC_CALC_FLEN(sce->isorate); + sce->isofres = ISOC_CALC_FRES(sce->isorate); for(i = 0; i < UGEN_NISOREQS; ++i) { - sce->isoreqs[i].sce = sce; xfer = usbd_alloc_xfer(sc->sc_udev); if (xfer == 0) goto bad; @@ -494,20 +559,30 @@ goto bad; } sce->isoreqs[i].dmabuf = buf; + sce->isoreqs[i].sce = sce; + if (dir == OUT) + memset(buf, 0, isize * UGEN_NISORFRMS); for(j = 0; j < UGEN_NISORFRMS; ++j) - sce->isoreqs[i].sizes[j] = isize; + sce->isoreqs[i].sizes[j] = + (dir == IN) ? isize : + ISOC_MAKE_FLEN(sce->isoflen, + sce->isofres, j); usbd_setup_isoc_xfer (xfer, sce->pipeh, &sce->isoreqs[i], sce->isoreqs[i].sizes, UGEN_NISORFRMS, USBD_NO_COPY, - ugen_isoc_rintr); - (void)usbd_transfer(xfer); + ugenintr_isoc); } + s = splusb(); + for(i = 0; i < UGEN_NISOREQS; ++i) + (void)usbd_transfer(sce->isoreqs[i].xfer); + splx(s); DPRINTFN(5, ("ugenopen: isoc open done\n")); break; bad: while (--i >= 0) /* implicit buffer free */ usbd_free_xfer(sce->isoreqs[i].xfer); + IBUF_SPACE_FREE(sce, M_USBDEV); return (ENOMEM); case UE_CONTROL: sce->timeout = USBD_DEFAULT_TIMEOUT; @@ -570,11 +645,7 @@ break; } - if (sce->ibuf != NULL) { - free(sce->ibuf, M_USBDEV); - sce->ibuf = NULL; - clfree(&sce->q); - } + IBUF_SPACE_FREE(sce, M_USBDEV); } sc->sc_is_open[endpt] = 0; @@ -680,44 +751,34 @@ usbd_free_xfer(xfer); break; case UE_ISOCHRONOUS: - s = splusb(); - while (sce->cur == sce->fill) { - if (flag & IO_NDELAY) { - splx(s); - return (EWOULDBLOCK); - } - sce->state |= UGEN_ASLP; - DPRINTFN(5, ("ugenread: sleep on %p\n", sce)); - error = tsleep(sce, PZERO | PCATCH, "ugenri", 0); - DPRINTFN(5, ("ugenread: woke, error=%d\n", error)); - if (sc->sc_dying) - error = EIO; - if (error) { - sce->state &= ~UGEN_ASLP; - break; - } - } - - while (sce->cur != sce->fill && uio->uio_resid > 0 && !error) { - if(sce->fill > sce->cur) - n = min(sce->fill - sce->cur, uio->uio_resid); - else - n = min(sce->limit - sce->cur, uio->uio_resid); - - DPRINTFN(5, ("ugenread: isoc got %d chars\n", n)); - - /* Copy the data to the user process. */ - error = uiomove(sce->cur, n, uio); - if (error) - break; - sce->cur += n; - if(sce->cur >= sce->limit) - sce->cur = sce->ibuf; - } - splx(s); - break; - - + s = splusb(); + while (uio->uio_resid > 0 && !error) { + if (IBUF_SPACE_RD_ZERO(sce)) { + if (flag & IO_NDELAY) { + error = EWOULDBLOCK; + break; + } + sce->state |= UGEN_ASLP; + DPRINTFN(5, ("ugenread: sleep on %p\n", sce)); + error = tsleep(sce, PZERO|PCATCH, "ugenri", 0); + DPRINTFN(5, ("ugenread: woke, error=%d\n", + error)); + if (sc->sc_dying) + error = EIO; + if (error) + sce->state &= ~UGEN_ASLP; + } else { + n = min(IBUF_SPACE_RD_CHNK(sce),uio->uio_resid); + + DPRINTFN(5, ("ugenread: isoc read %d\n", n)); + + error = uiomove(IBUF_SPACE_RD_PNTR(sce), n,uio); + if (!error) + IBUF_SPACE_RD_INCR(sce, n); + } + } + splx(s); + break; default: return (ENXIO); } @@ -749,6 +810,7 @@ char buf[UGEN_BBSIZE]; usbd_xfer_handle xfer; usbd_status err; + int s; DPRINTFN(5, ("%s: ugenwrite: %d\n", USBDEVNAME(sc->sc_dev), endpt)); @@ -818,6 +880,35 @@ } usbd_free_xfer(xfer); break; + case UE_ISOCHRONOUS: + s = splusb(); + while(uio->uio_resid > 0 && !error) { + if (IBUF_SPACE_WR_ZERO(sce)) { + if (flag & IO_NDELAY) { + error = EWOULDBLOCK; + break; + } + sce->state |= UGEN_ASLP; + DPRINTFN(5, ("ugenwrite: sleep on %p\n", sce)); + error = tsleep(sce, PZERO|PCATCH, "ugenwi", 0); + DPRINTFN(5, ("ugenwrite: woke, error=%d\n", + error)); + if (sc->sc_dying) + error = EIO; + if (error) + sce->state &= ~UGEN_ASLP; + } else { + n = min(IBUF_SPACE_WR_CHNK(sce),uio->uio_resid); + + DPRINTFN(5, ("ugenwrite: isoc write %d\n", n)); + + error = uiomove(IBUF_SPACE_WR_PNTR(sce), n,uio); + if (!error) + IBUF_SPACE_WR_INCR(sce, n); + } + } + splx(s); + break; default: return (ENXIO); } @@ -949,60 +1040,72 @@ } Static void -ugen_isoc_rintr(usbd_xfer_handle xfer, usbd_private_handle addr, +ugenintr_isoc(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) { struct isoreq *req = addr; struct ugen_endpoint *sce = req->sce; - u_int32_t count, n; - int i, isize; + u_int32_t count; + int i, dir, psize, n; /* Return if we are aborting. */ if (status == USBD_CANCELLED) return; + dir = UE_GET_DIR(sce->edesc->bEndpointAddress) == UE_DIR_IN ? IN : OUT; + psize = UGETW(sce->edesc->wMaxPacketSize); + usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL); - DPRINTFN(5,("ugen_isoc_rintr: xfer %d, count=%d\n", - (int)(req - sce->isoreqs), - count)); - - /* throw away oldest input if the buffer is full */ - if(sce->fill < sce->cur && sce->cur <= sce->fill + count) { - sce->cur += count; - if(sce->cur >= sce->limit) - sce->cur = sce->ibuf + (sce->limit - sce->cur); - DPRINTFN(5, ("ugen_isoc_rintr: throwing away %d bytes\n", - count)); - } - - isize = UGETW(sce->edesc->wMaxPacketSize); - for (i = 0; i < UGEN_NISORFRMS; i++) { - u_int32_t actlen = req->sizes[i]; - char const *buf = (char const *)req->dmabuf + isize * i; - - /* copy data to buffer */ - while (actlen > 0) { - n = min(actlen, sce->limit - sce->fill); - memcpy(sce->fill, buf, n); - - buf += n; - actlen -= n; - sce->fill += n; - if(sce->fill == sce->limit) - sce->fill = sce->ibuf; - } + DPRINTFN(5,("ugenintr_isoc: xfer %ld, dir %s, count=%d\n", + (long)(req - sce->isoreqs), (dir == IN) ? "IN" : "OUT", count)); + + if (dir == OUT) { + u_char* buf = (u_char *)req->dmabuf; + for (i = 0; i < UGEN_NISORFRMS; i++) { + int len = ISOC_MAKE_FLEN(sce->isoflen, sce->isofres, i); + req->sizes[i] = len; + + for (; len > 0 && !IBUF_SPACE_RD_ZERO(sce); + buf += n, len -= n) { + n = min(IBUF_SPACE_RD_CHNK(sce), len); + memcpy(buf, IBUF_SPACE_RD_PNTR(sce), n); + IBUF_SPACE_RD_INCR(sce, n); + } - /* setup size for next transfer */ - req->sizes[i] = isize; + if (len > 0) { + memset(buf, 0, len); + buf += len; + } + } + } else { + u_char* buf = (u_char *)req->dmabuf; + for (i = 0; i < UGEN_NISORFRMS; i++) { + int len = req->sizes[i]; + + if ((n = IBUF_SPACE_WR_SIZE(sce)) < len) { + IBUF_SPACE_RD_INCR(sce, len - n); + DPRINTFN(5, ("ugenintr_isoc: throw %d bytes\n", + len - n)); + } + + for (; len > 0; buf += n, len -= n) { + n = min(IBUF_SPACE_WR_CHNK(sce), len); + memcpy(IBUF_SPACE_WR_PNTR(sce), buf, n); + IBUF_SPACE_WR_INCR(sce, n); + } + + buf += (psize - req->sizes[i]); + req->sizes[i] = psize; + } } usbd_setup_isoc_xfer(xfer, sce->pipeh, req, req->sizes, UGEN_NISORFRMS, - USBD_NO_COPY, ugen_isoc_rintr); + USBD_NO_COPY, ugenintr_isoc); (void)usbd_transfer(xfer); if (sce->state & UGEN_ASLP) { sce->state &= ~UGEN_ASLP; - DPRINTFN(5, ("ugen_isoc_rintr: waking %p\n", sce)); + DPRINTFN(5, ("ugenintr_isoc: waking %p\n", sce)); wakeup(sce); } selwakeuppri(&sce->rsel, PZERO); @@ -1170,6 +1273,28 @@ return (EINVAL); sce->timeout = *(int *)addr; return (0); + case USB_GET_ISOC_RATE: + if (endpt == USB_CONTROL_ENDPOINT) + *(int *)addr = sc->sc_isorate; + else { + sce = &sc->sc_endpoints[endpt][OUT]; + if (sce == NULL || sce->edesc == NULL) + return (EINVAL); + *(int *)addr = sce->isorate; + } + return (0); + case USB_SET_ISOC_RATE: + if (endpt == USB_CONTROL_ENDPOINT) + sc->sc_isorate = *(int *)addr; + else { + sce = &sc->sc_endpoints[endpt][OUT]; + if (sce == NULL || sce->edesc == NULL) + return (EINVAL); + sce->isorate = *(int *)addr; + sce->isoflen = ISOC_CALC_FLEN(sce->isorate); + sce->isofres = ISOC_CALC_FRES(sce->isorate); + } + return (0); default: break; } @@ -1405,65 +1530,80 @@ return (error); } -int -ugenpoll(dev_t dev, int events, usb_proc_ptr p) +Static int +ugen_do_poll(struct ugen_softc *sc, int endpt, int events, usb_proc_ptr p) { - struct ugen_softc *sc; - struct ugen_endpoint *sce; + struct ugen_endpoint *sce_in, *sce_out; + usb_endpoint_descriptor_t *edesc; int revents = 0; int s; - USB_GET_SC(ugen, UGENUNIT(dev), sc); - if (sc->sc_dying) return (EIO); - /* XXX always IN */ - sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN]; - if (sce == NULL) + sce_in = &sc->sc_endpoints[endpt][IN]; + sce_out = &sc->sc_endpoints[endpt][OUT]; + if (sce_in == NULL && sce_out == NULL) return (EINVAL); - if (!sce->edesc) { - printf("ugenpoll: no edesc\n"); + if (sce_in && sce_in->edesc) + edesc = sce_in->edesc; + else if (sce_out && sce_out->edesc) + edesc = sce_out->edesc; + else /* no edesc! */ return (EIO); - } - if (!sce->pipeh) { - printf("ugenpoll: no pipe\n"); - return (EIO); - } s = splusb(); - switch (sce->edesc->bmAttributes & UE_XFERTYPE) { + switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: - if (events & (POLLIN | POLLRDNORM)) { - if (sce->q.c_cc > 0) + if (sce_in && events & (POLLIN | POLLRDNORM)) { + if (sce_in->q.c_cc > 0) revents |= events & (POLLIN | POLLRDNORM); else - selrecord(p, &sce->rsel); + selrecord(p, &sce_in->rsel); } break; case UE_ISOCHRONOUS: - if (events & (POLLIN | POLLRDNORM)) { - if (sce->cur != sce->fill) + if (sce_in && events & (POLLIN | POLLRDNORM)) { + if (!IBUF_SPACE_RD_ZERO(sce_in)) revents |= events & (POLLIN | POLLRDNORM); else - selrecord(p, &sce->rsel); + selrecord(p, &sce_in->rsel); + } + if (sce_out && events & (POLLOUT | POLLWRNORM)) { + if (!IBUF_SPACE_WR_ZERO(sce_out)) + revents |= events & (POLLOUT | POLLWRNORM); + else + selrecord(p, &sce_out->rsel); } break; case UE_BULK: - /* - * We have no easy way of determining if a read will - * yield any data or a write will happen. - * Pretend they will. - */ - revents |= events & - (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM); + if (sce_in && events & (POLLIN | POLLRDNORM)) + revents |= events & (POLLIN | POLLRDNORM); + if (sce_out && events & (POLLOUT | POLLWRNORM)) + revents |= events & (POLLOUT | POLLWRNORM); break; default: break; } splx(s); return (revents); +} + +int +ugenpoll(dev_t dev, int events, usb_proc_ptr p) +{ + int endpt = UGENENDPOINT(dev); + struct ugen_softc *sc; + int error; + + USB_GET_SC(ugen, UGENUNIT(dev), sc); + + sc->sc_refcnt++; + error = ugen_do_poll(sc, endpt, events, p); + if (--sc->sc_refcnt < 0) + usb_detach_wakeup(USBDEV(sc->sc_dev)); + return (error); } #if defined(__FreeBSD__) Index: usb.h =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/usb.h,v retrieving revision 1.38 diff -u -u -r1.38 usb.h --- usb.h 6 Nov 2002 21:37:21 -0000 1.38 +++ usb.h 23 Jun 2004 07:34:23 -0000 @@ -1,5 +1,5 @@ /* $NetBSD: usb.h,v 1.69 2002/09/22 23:20:50 augustss Exp $ */ -/* $FreeBSD$ */ +/* $FreeBSD: src/sys/dev/usb/usb.h,v 1.38 2002/11/06 21:37:21 joe Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -685,6 +685,8 @@ #define USB_GET_DEVICEINFO _IOR ('U', 112, struct usb_device_info) #define USB_SET_SHORT_XFER _IOW ('U', 113, int) #define USB_SET_TIMEOUT _IOW ('U', 114, int) +#define USB_GET_ISOC_RATE _IOR ('U', 115, int) +#define USB_SET_ISOC_RATE _IOW ('U', 116, int) /* Modem device */ #define USB_GET_CM_OVER_DATA _IOR ('U', 130, int) Index: usbdi.c =================================================================== RCS file: /home/ncvs/src/sys/dev/usb/usbdi.c,v retrieving revision 1.84 diff -u -u -r1.84 usbdi.c --- usbdi.c 9 Nov 2003 23:56:19 -0000 1.84 +++ usbdi.c 23 Jun 2004 07:34:25 -0000 @@ -1,7 +1,7 @@ /* $NetBSD: usbdi.c,v 1.103 2002/09/27 15:37:38 provos Exp $ */ #include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); +__FBSDID("$FreeBSD: src/sys/dev/usb/usbdi.c,v 1.84 2003/11/09 23:56:19 joe Exp $"); /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -758,6 +758,9 @@ { usbd_pipe_handle pipe = xfer->pipe; usb_dma_t *dmap = &xfer->dmabuf; + int sync = xfer->flags & USBD_SYNCHRONOUS; + int errd = xfer->status == USBD_CANCELLED || + xfer->status == USBD_TIMEOUT; int repeat = pipe->repeat; int polling; @@ -842,14 +845,12 @@ pipe->methods->done(xfer); #endif - if ((xfer->flags & USBD_SYNCHRONOUS) && !polling) + if (sync && !polling) wakeup(xfer); if (!repeat) { /* XXX should we stop the queue on all errors? */ - if ((xfer->status == USBD_CANCELLED || - xfer->status == USBD_TIMEOUT) && - pipe->iface != NULL) /* not control pipe */ + if (errd && pipe->iface != NULL) /* not control pipe */ pipe->running = 0; else usbd_start_next(pipe); >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200406231002.i5NA2AUh019693>