diff options
author | roger <roger@FreeBSD.org> | 2000-12-07 10:28:25 +0000 |
---|---|---|
committer | roger <roger@FreeBSD.org> | 2000-12-07 10:28:25 +0000 |
commit | c9bce1858b8e913bd5e2c8ac35b46906c1dd980e (patch) | |
tree | 8910b18140b53a4b6d29813c9c71c5c0f55692b8 /sys/dev/usb/ugen.c | |
parent | c2fe91663313cf4a97441496bc4a671588edf60e (diff) | |
download | FreeBSD-src-c9bce1858b8e913bd5e2c8ac35b46906c1dd980e.zip FreeBSD-src-c9bce1858b8e913bd5e2c8ac35b46906c1dd980e.tar.gz |
Add Isochronus transfer mode support required by
USB WebCams, using a patch from Peter Housel.
With this change ugen, and with Peter's 'vid' program
in ports/graphics/vid, we can capture single images from USB Cameras
using the OmniVision OV511 chipset (including some models of the
Creative WebCam 3)
NetBSD merged in Peter's patch to their ugen.c file
several months ago, so this brings us back in line.
Submitted by: Peter Housel <housel@acm.org>
http://members.home.com/housel/
Approved by: Nick Hibma
Diffstat (limited to 'sys/dev/usb/ugen.c')
-rw-r--r-- | sys/dev/usb/ugen.c | 193 |
1 files changed, 180 insertions, 13 deletions
diff --git a/sys/dev/usb/ugen.c b/sys/dev/usb/ugen.c index d324220..47a933b 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; - - if (sce->ibuf != NULL) { - free(sce->ibuf, M_USBDEV); - sce->ibuf = 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; } } 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 |