Subject: Re: isochronous support for ugen in the 1.5 branch?
To: None <netbsd-users@netbsd.org>
From: Steven M. Bellovin <smb@research.att.com>
List: netbsd-users
Date: 08/17/2001 09:26:53
In message <20010816160955.92F1F7B55@berkshire.research.att.com>, Steve Bellovi
n writes:
>Has anyone installed isochronous support in the ugen devices in the 1.5
>branch? I see that it's in -current, but I run 1.5.2alpha. (My goal
>is to run the 'vid' USB camera package -- see
>http://members.home.net/housel/projects.htm for details.)
>
>The -current ugen0 doesn't compile, and I'd rather not do the back-port
>myself if I can help it.
I decided to apply Housel's mods to the 1.5.2alpha driver. If anyone
wants it, here are the diffs:
------------------
*** ugen.c.orig Thu Aug 16 14:13:03 2001
--- ugen.c Thu Aug 16 14:25:11 2001
***************
*** 75,80 ****
--- 75,88 ----
#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,98 ****
usbd_pipe_handle pipeh;
struct clist q;
struct selinfo rsel;
! void *ibuf;
u_int32_t timeout;
};
- #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;
--- 93,111 ----
usbd_pipe_handle pipeh;
struct clist q;
struct selinfo rsel;
! 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];
};
struct ugen_softc {
USBBASEDEVICE sc_dev; /* base device */
usbd_device_handle sc_udev;
***************
*** 138,143 ****
--- 151,159 ----
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);
***************
*** 274,279 ****
--- 290,298 ----
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,346 ****
if (err)
return (EIO);
break;
- case UE_CONTROL:
case UE_ISOCHRONOUS:
return (EINVAL);
}
}
--- 358,410 ----
if (err)
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));
+ 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,360 ****
--- 419,425 ----
struct ugen_softc *sc;
struct ugen_endpoint *sce;
int dir;
+ int i;
USB_GET_SC(ugen, UGENUNIT(dev), sc);
***************
*** 386,391 ****
--- 451,468 ----
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);
***************
*** 498,503 ****
--- 575,619 ----
}
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,707 ****
--- 818,873 ----
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)
{
***************
*** 1118,1123 ****
--- 1284,1297 ----
case UE_INTERRUPT:
if (events & (POLLIN | POLLRDNORM)) {
if (sce->q.c_cc > 0)
+ revents |= events & (POLLIN | POLLRDNORM);
+ else
+ 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);
--Steve Bellovin, http://www.research.att.com/~smb