diff --git a/sys/dev/usb/ugen.c b/sys/dev/usb/ugen.c index d324220bfe0d..47a933b774b6 100644 --- a/sys/dev/usb/ugen.c +++ b/sys/dev/usb/ugen.c @@ -1,5 +1,5 @@ /* $NetBSD: ugen.c,v 1.27 1999/10/28 12:08:38 augustss Exp $ */ -/* $FreeBSD$ */ +/* $FreeBSD$ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -73,6 +73,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; @@ -83,14 +91,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; @@ -136,7 +149,8 @@ Static struct cdevsw ugen_cdevsw = { 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, + 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, @@ -300,6 +314,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); @@ -365,8 +382,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); } } @@ -381,6 +443,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); @@ -408,17 +471,26 @@ ugenclose(dev_t dev, int flag, int mode, struct proc *p) continue; DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n", endpt, dir, sce)); - + 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; - ndflush(&sce->q, sce->q.c_cc); - clfree(&sce->q); - } } sc->sc_is_open[endpt] = 0; @@ -523,6 +595,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); } @@ -755,6 +866,54 @@ ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) selwakeup(&sce->rsel); } +Static void +ugen_isoc_rintr(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) { @@ -1185,6 +1344,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