From 96d1acf3d676fea89ebba8753410a709ae633ba6 Mon Sep 17 00:00:00 2001 From: augustss Date: Fri, 8 Sep 2000 00:55:26 +0000 Subject: [PATCH] Add isoc support. From FreeBSD via Berndt Josef Wulf --- sys/dev/usb/ugen.c | 192 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 183 insertions(+), 9 deletions(-) diff --git a/sys/dev/usb/ugen.c b/sys/dev/usb/ugen.c index 7d5d28ec354a..69a42909c0c1 100644 --- a/sys/dev/usb/ugen.c +++ b/sys/dev/usb/ugen.c @@ -1,4 +1,4 @@ -/* $NetBSD: ugen.c,v 1.40 2000/06/01 14:28:59 augustss Exp $ */ +/* $NetBSD: ugen.c,v 1.41 2000/09/08 00:55:26 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/ugen.c,v 1.26 1999/11/17 22:33:41 n_hibma Exp $ */ /* @@ -75,6 +75,14 @@ int ugendebug = 0; #define DPRINTFN(n,x) #endif +#define UGEN_CHUNK 128 /* chunk size for read */ +#define UGEN_IBSIZE 1020 /* buffer size */ +#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 */ + struct ugen_endpoint { struct ugen_softc *sc; usb_endpoint_descriptor_t *edesc; @@ -85,14 +93,19 @@ struct ugen_endpoint { usbd_pipe_handle pipeh; struct clist q; struct selinfo rsel; - void *ibuf; + u_char *ibuf; /* start of buffer (circular for isoc) */ + u_char *fill; /* location for input (isoc) */ + u_char *limit; /* end of circular buffer (isoc) */ + u_char *cur; /* current read location (isoc) */ u_int32_t timeout; + struct isoreq { + struct ugen_endpoint *sce; + usbd_xfer_handle xfer; + void *dmabuf; + u_int16_t sizes[UGEN_NISORFRMS]; + } isoreqs[UGEN_NISOREQS]; }; -#define UGEN_CHUNK 128 /* chunk size for read */ -#define UGEN_IBSIZE 1020 /* buffer size */ -#define UGEN_BBSIZE 1024 - struct ugen_softc { USBBASEDEVICE sc_dev; /* base device */ usbd_device_handle sc_udev; @@ -138,7 +151,9 @@ Static struct cdevsw ugen_cdevsw = { Static void ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status); - +Static void ugen_isoc_rintr __P((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, @@ -274,6 +289,9 @@ ugenopen(dev_t dev, int flag, int mode, struct proc *p) struct ugen_endpoint *sce; int dir, isize; usbd_status err; + usbd_xfer_handle xfer; + void *buf; + int i, j; USB_GET_SC_OPEN(ugen, unit, sc); @@ -339,8 +357,53 @@ ugenopen(dev_t dev, int flag, int mode, struct proc *p) if (err) return (EIO); break; - case UE_CONTROL: 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)); + err = usbd_open_pipe(sce->iface, + edesc->bEndpointAddress, 0, &sce->pipeh); + if (err) { + free(sce->ibuf, M_USBDEV); + return (EIO); + } + for(i = 0; i < UGEN_NISOREQS; ++i) { + sce->isoreqs[i].sce = sce; + xfer = usbd_alloc_xfer(sc->sc_udev); + if (xfer == 0) + goto bad; + sce->isoreqs[i].xfer = xfer; + buf = usbd_alloc_buffer + (xfer, isize * UGEN_NISORFRMS); + if (buf == 0) { + i++; + goto bad; + } + sce->isoreqs[i].dmabuf = buf; + for(j = 0; j < UGEN_NISORFRMS; ++j) + sce->isoreqs[i].sizes[j] = isize; + 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); + } + DPRINTFN(5, ("ugenopen: isoc open done\n")); + break; + bad: + while (--i >= 0) /* implicit buffer free */ + usbd_free_xfer(sce->isoreqs[i].xfer); + return (ENOMEM); + case UE_CONTROL: return (EINVAL); } } @@ -355,6 +418,7 @@ ugenclose(dev_t dev, int flag, int mode, struct proc *p) struct ugen_softc *sc; struct ugen_endpoint *sce; int dir; + int i; USB_GET_SC(ugen, UGENUNIT(dev), sc); @@ -386,7 +450,20 @@ ugenclose(dev_t dev, int flag, int mode, struct proc *p) usbd_abort_pipe(sce->pipeh); usbd_close_pipe(sce->pipeh); sce->pipeh = NULL; - + + switch (sce->edesc->bmAttributes & UE_XFERTYPE) { + case UE_INTERRUPT: + ndflush(&sce->q, sce->q.c_cc); + clfree(&sce->q); + break; + case UE_ISOCHRONOUS: + for (i = 0; i < UGEN_NISOREQS; ++i) + usbd_free_xfer(sce->isoreqs[i].xfer); + + default: + break; + } + if (sce->ibuf != NULL) { free(sce->ibuf, M_USBDEV); sce->ibuf = NULL; @@ -498,6 +575,45 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct uio *uio, int flag) } 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", sc)); + 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; + + default: return (ENXIO); } @@ -702,6 +818,56 @@ ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) selwakeup(&sce->rsel); } +Static void +ugen_isoc_rintr(xfer, addr, status) + 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; + + /* Return if we are aborting. */ + if (status == USBD_CANCELLED) + return; + + usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL); + DPRINTFN(5,("ugen_isoc_rintr: xfer %d, count=%d\n", 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)); + } + + /* copy data to buffer */ + while(count > 0) { + n = min(count, sce->limit - sce->fill); + memcpy(sce->fill, req->dmabuf, n); + + count -= n; + sce->fill += n; + if(sce->fill == sce->limit) + sce->fill = sce->ibuf; + } + + usbd_setup_isoc_xfer(xfer, sce->pipeh, req, req->sizes, UGEN_NISORFRMS, + USBD_NO_COPY, ugen_isoc_rintr); + (void)usbd_transfer(xfer); + + if (sce->state & UGEN_ASLP) { + sce->state &= ~UGEN_ASLP; + DPRINTFN(5, ("ugen_isoc_rintr: waking %p\n", sce)); + wakeup(sce); + } + selwakeup(&sce->rsel); +} + Static usbd_status ugen_set_interface(struct ugen_softc *sc, int ifaceidx, int altno) { @@ -1123,6 +1289,14 @@ ugenpoll(dev_t dev, int events, struct proc *p) selrecord(p, &sce->rsel); } break; + case UE_ISOCHRONOUS: + if (events & (POLLIN | POLLRDNORM)) { + if (sce->cur != sce->fill) + revents |= events & (POLLIN | POLLRDNORM); + else + selrecord(p, &sce->rsel); + } + break; case UE_BULK: /* * We have no easy way of determining if a read will