Subject: ugen: isochronous transfer mode
To: None <tech-kern@netbsd.org, port-i386@netbsd.org>
From: Berndt Josef Wulf <wulf@ping.net.au>
List: tech-kern
Date: 09/08/2000 08:37:23
G'day,
I've been succesful in adding code that I found on a FreeBSD site to
the ugen device driver that extents its capability to include
isochronous tranfer mode. It was tested using a Creative Webcam3
digital camera and sofar no problems have been detected.
It would be nice if someone more knowledgeable than myself could have
a look over the source code and perhaps initiate its
implementation into NetBSD's kernel.
Find below a diff against /sys/dev/usb/ugen.c. The diff is cut against
the sources from NetBSD-1.5Alpha2, but should be fine with
NetBSD-current.
Whilst on the subject of usb device driver..., I've attempted to write
a driver for the QuikCam Express USB digital camera, but sofar had no
luck in deciphering the propriaty protocol used by Logitech. Is there
anyone out there who has access to a USB Protocol Analyzer such as a
CATC Chief (www.catc.com) or similar. This could be used to obtain a
dump from the device initialization phase allowing reverse engineering of
the protocol used by Logitech. Alternatively, are there any software
tools that will do this job?
I enterain your comments.
cheerio Berndt
--
Name : Berndt Josef Wulf | +++ With BSD on Packet Radio +++
E-Mail : wulf@ping.net.au | tfkiss, tnt, dpbox, wampes
ICQ : 18196098 | VK5ABN, Nairne, South Australia
URL : http://www.ping.net.au/~wulf | MBOX : vk5abn@vk5abn.#lmr.#sa.au.oc
Sysinfo : DEC AXPpci33+, NetBSD-1.4.2 | BBS : vk5abn.#lmr.#sa.aus.oc
------- snip --------
--- ugen.c.orig Fri Jun 2 19:57:35 2000
+++ ugen.c Sat Sep 2 20:40:42 2000
@@ -75,6 +75,14 @@
#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 @@
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 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 @@
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 @@
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 @@
struct ugen_softc *sc;
struct ugen_endpoint *sce;
int dir;
+ int i;
USB_GET_SC(ugen, UGENUNIT(dev), sc);
@@ -386,7 +450,20 @@
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 @@
}
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 @@
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,6 +1284,14 @@
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);