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