Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 23 Jun 2004 10:02:10 GMT
From:      Matthew Gream <matthew.gream@pobox.com>
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   kern/68232: ugen(4) isochronous handling correction and tx support
Message-ID:  <200406231002.i5NA2AUh019693@home.matthewgream.net>
Resent-Message-ID: <200406231010.i5NAATWa016500@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         68232
>Category:       kern
>Synopsis:       ugen(4) isochronous handling correction and tx support
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Wed Jun 23 10:10:28 GMT 2004
>Closed-Date:
>Last-Modified:
>Originator:     Matthew Gream <matthew.gream@pobox.com>
>Release:        FreeBSD 5.2.1-RELEASE i386
>Organization:
>Environment:
System: FreeBSD usbdev 5.2.1-RELEASE FreeBSD 5.2.1-RELEASE #9: Tue Jun 22 13:44:23 BST 2004 root@usbdev:/usr/src/sys/i386/compile/USBDEV i386

>Description:
	see netbsd pr: kern/25960
	http://www.netbsd.org/cgi-bin/query-pr-single.pl?number=25960
	Problems with ugen(4) isoc handling, and lack of write support.

>How-To-Repeat:
	see netbsd pr: kern/25960

>Fix:
	see netbsd pr: kern/25960
	ported to 5.2.1-RELEASE, below
	note: partially reverts some changes to ohci isoc handling
	that broke isoc rx.
	(to be honest: the ohci/ugen isoc handling seems to need 
	a good dose of attention)

Index: ohci.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/usb/ohci.c,v
retrieving revision 1.139
diff -u -u -r1.139 ohci.c
--- ohci.c	25 Nov 2003 02:23:29 -0000	1.139
+++ ohci.c	23 Jun 2004 07:34:23 -0000
@@ -8,7 +8,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: src/sys/dev/usb/ohci.c,v 1.139 2003/11/25 02:23:29 iedowse Exp $");
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -927,9 +927,9 @@
  bad4:
 	ohci_free_sed(sc, sc->sc_isoc_head);
  bad3:
-	ohci_free_sed(sc, sc->sc_ctrl_head);
- bad2:
 	ohci_free_sed(sc, sc->sc_bulk_head);
+ bad2:
+	ohci_free_sed(sc, sc->sc_ctrl_head);
  bad1:
 	usb_freemem(&sc->sc_bus, &sc->sc_hccadma);
 	return (err);
@@ -978,14 +978,6 @@
 ohci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer)
 {
 	struct ohci_softc *sc = (struct ohci_softc *)bus;
-	struct ohci_xfer *oxfer = (struct ohci_xfer *)xfer;
-	ohci_soft_itd_t *sitd;
-
-        if (oxfer->ohci_xfer_flags & OHCI_ISOC_DIRTY) {
-		for (sitd = xfer->hcpriv; sitd != NULL && sitd->xfer == xfer;
-		    sitd = sitd->nextitd)
-			ohci_free_sitd(sc, sitd);
-	}
 
 #ifdef DIAGNOSTIC
 	if (xfer->busy_free != XFER_BUSY) {
@@ -1478,22 +1470,48 @@
 		opipe = (struct ohci_pipe *)xfer->pipe;
 		if (opipe->aborting)
 			continue;
- 
-		cc = OHCI_ITD_GET_CC(le32toh(sitd->itd.itd_flags));
-		if (cc == OHCI_CC_NO_ERROR) {
-			/* XXX compute length for input */
-			if (sitd->flags & OHCI_CALL_DONE) {
-				opipe->u.iso.inuse -= xfer->nframes;
-				/* XXX update frlengths with actual length */
-				/* XXX xfer->actlen = actlen; */
-				xfer->status = USBD_NORMAL_COMPLETION;
-				s = splusb();
-				usb_transfer_complete(xfer);
-				splx(s);
+
+		if (sitd->flags & OHCI_CALL_DONE) {
+			ohci_soft_itd_t *next;
+			int i, j, actlen, iframes, dir;
+
+			opipe->u.iso.inuse -= xfer->nframes;
+			dir = UE_GET_DIR(xfer->pipe->endpoint->edesc->
+			    bEndpointAddress);
+			xfer->status = USBD_NORMAL_COMPLETION;
+			actlen = 0;
+			for (i = 0, sitd = xfer->hcpriv;; sitd = next) {
+				next = sitd->nextitd;
+				if (OHCI_ITD_GET_CC(le32toh(sitd->
+				    itd.itd_flags)) != OHCI_CC_NO_ERROR)
+					xfer->status = USBD_IOERROR;
+				/* For input, update frlengths with actual */
+				/* XXX anything necessary for output? */
+				if (dir == UE_DIR_IN &&
+				    xfer->status == USBD_NORMAL_COMPLETION) {
+					iframes = OHCI_ITD_GET_FC(le32toh(
+					    sitd->itd.itd_flags));
+					for (j = 0; j < iframes; i++, j++) {
+						len = le16toh(sitd->
+						    itd.itd_offset[j]);
+						len =
+						    (OHCI_ITD_PSW_GET_CC(len) ==
+						    OHCI_CC_NOT_ACCESSED) ? 0 :
+						    OHCI_ITD_PSW_LENGTH(len);
+						xfer->frlengths[i] = len;
+						actlen += len;
+					}
+				}
+				if (sitd->flags & OHCI_CALL_DONE)
+					break;
+				ohci_free_sitd(sc, sitd);
 			}
-		} else {
-			/* XXX Do more */
-			xfer->status = USBD_IOERROR;
+			ohci_free_sitd(sc, sitd);
+			if (dir == UE_DIR_IN &&
+			    xfer->status == USBD_NORMAL_COMPLETION)
+				xfer->actlen = actlen;
+			xfer->hcpriv = NULL;
+
 			s = splusb();
 			usb_transfer_complete(xfer);
 			splx(s);
@@ -1521,7 +1539,6 @@
 		panic("ohci_device_ctrl_done: not a request");
 	}
 #endif
-	xfer->hcpriv = NULL;
 }
 
 void
@@ -1536,8 +1553,6 @@
 	DPRINTFN(10,("ohci_device_intr_done: xfer=%p, actlen=%d\n",
 		     xfer, xfer->actlen));
 
-	xfer->hcpriv = NULL;
-
 	if (xfer->pipe->repeat) {
 		data = opipe->tail.td;
 		tail = ohci_alloc_std(sc); /* XXX should reuse TD */
@@ -1571,10 +1586,7 @@
 void
 ohci_device_bulk_done(usbd_xfer_handle xfer)
 {
-	DPRINTFN(10,("ohci_device_bulk_done: xfer=%p, actlen=%d\n",
-		     xfer, xfer->actlen));
-
-	xfer->hcpriv = NULL;
+	DPRINTFN(10,("ohci_device_bulk_done: xfer=%p\n", xfer));
 }
 
 void
@@ -1614,13 +1626,13 @@
 void
 ohci_root_intr_done(usbd_xfer_handle xfer)
 {
-	xfer->hcpriv = NULL;
+	DPRINTFN(10,("ohci_root_intr_done: xfer=%p\n", xfer));
 }
 
 void
 ohci_root_ctrl_done(usbd_xfer_handle xfer)
 {
-	xfer->hcpriv = NULL;
+	DPRINTFN(10,("ohci_root_ctrl_done: xfer=%p\n", xfer));
 }
 
 /*
@@ -2114,7 +2126,7 @@
 		}
 		sed->ed.ed_flags = htole32(
 			OHCI_ED_SET_FA(addr) |
-			OHCI_ED_SET_EN(ed->bEndpointAddress) |
+			OHCI_ED_SET_EN(UE_GET_ADDR(ed->bEndpointAddress)) |
 			(dev->speed == USB_SPEED_LOW ? OHCI_ED_SPEED : 0) |
 			fmt |
 			OHCI_ED_SET_MAXP(UGETW(ed->wMaxPacketSize)));
@@ -2612,7 +2624,7 @@
 		}
 		break;
 	case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
-		if (value != 0) {
+		if ((value & 0xff) != 0) {
 			err = USBD_IOERROR;
 			goto ret;
 		}
@@ -3247,9 +3259,8 @@
 	ohci_softc_t *sc = (ohci_softc_t *)dev->bus;
 	ohci_soft_ed_t *sed = opipe->sed;
 	struct iso *iso = &opipe->u.iso;
-	struct ohci_xfer *oxfer = (struct ohci_xfer *)xfer;
 	ohci_soft_itd_t *sitd, *nsitd;
-	ohci_physaddr_t buf, offs, noffs, bp0, tdphys;
+	ohci_physaddr_t buf, offs, noffs, bp0;
 	int i, ncur, nframes;
 	int s;
 
@@ -3267,24 +3278,6 @@
 			    iso->next));
 	}
 
-	if (xfer->hcpriv) {
-		for (sitd = xfer->hcpriv; sitd != NULL && sitd->xfer == xfer;
-		    sitd = sitd->nextitd)
-			ohci_free_sitd(sc, sitd); /* Free ITDs in prev xfer*/
-
-		if (sitd == NULL) {
-			sitd = ohci_alloc_sitd(sc);
-			if (sitd == NULL)
-				panic("cant alloc isoc");
-			opipe->tail.itd = sitd;
-			tdphys = sitd->physaddr;
-			sed->ed.ed_flags |= htole32(OHCI_ED_SKIP); /* Stop*/
-			sed->ed.ed_headp =
-			sed->ed.ed_tailp = htole32(tdphys);
-			sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP); /* Start.*/
-		}
-	}
-
 	sitd = opipe->tail.itd;
 	buf = DMAADDR(&xfer->dmabuf, 0);
 	bp0 = OHCI_PAGE(buf);
@@ -3353,8 +3346,6 @@
 
 	xfer->status = USBD_IN_PROGRESS;
 
-	oxfer->ohci_xfer_flags |= OHCI_ISOC_DIRTY;
-
 #ifdef USB_DEBUG
 	if (ohcidebug > 5) {
 		DPRINTF(("ohci_device_isoc_enter: frame=%d\n",
@@ -3386,8 +3377,6 @@
 {
 	struct ohci_pipe *opipe = (struct ohci_pipe *)xfer->pipe;
 	ohci_softc_t *sc = (ohci_softc_t *)opipe->pipe.device->bus;
-	ohci_soft_ed_t *sed;
-	int s;
 
 	DPRINTFN(5,("ohci_device_isoc_start: xfer=%p\n", xfer));
 
@@ -3399,13 +3388,6 @@
 		printf("ohci_device_isoc_start: not in progress %p\n", xfer);
 #endif
 
-	/* XXX anything to do? */
-
-	s = splusb();
-	sed = opipe->sed;  /*  Turn off ED skip-bit to start processing */
-	sed->ed.ed_flags &= htole32(~OHCI_ED_SKIP);    /* ED's ITD list.*/
-	splx(s);
-
 	return (USBD_IN_PROGRESS);
 }
 
@@ -3501,23 +3483,7 @@
 void
 ohci_device_isoc_done(usbd_xfer_handle xfer)
 {
-	/* This null routine corresponds to non-isoc "done()" routines
-	 * that free the stds associated with an xfer after a completed
-	 * xfer interrupt. However, in the case of isoc transfers, the
-	 * sitds associated with the transfer have already been processed
-	 * and reallocated for the next iteration by
-	 * "ohci_device_isoc_transfer()".
-	 *
-	 * Routine "usb_transfer_complete()" is called at the end of every
-	 * relevant usb interrupt. "usb_transfer_complete()" indirectly
-	 * calls 1) "ohci_device_isoc_transfer()" (which keeps pumping the
-	 * pipeline by setting up the next transfer iteration) and 2) then 
-	 * calls "ohci_device_isoc_done()". Isoc transfers have not been 
-	 * working for the ohci usb because this routine was trashing the
-	 * xfer set up for the next iteration (thus, only the first 
-	 * UGEN_NISOREQS xfers outstanding on an open would work). Perhaps
-	 * this could all be re-factored, but that's another pass...
-	 */
+	DPRINTFN(10,("ohci_device_isoc_done: xfer=%p\n", xfer));
 }
 
 usbd_status
Index: ohcireg.h
===================================================================
RCS file: /home/ncvs/src/sys/dev/usb/ohcireg.h,v
retrieving revision 1.20
diff -u -u -r1.20 ohcireg.h
--- ohcireg.h	15 Jul 2003 23:12:54 -0000	1.20
+++ ohcireg.h	23 Jun 2004 07:34:23 -0000
@@ -1,5 +1,5 @@
 /*	$NetBSD: ohcireg.h,v 1.17 2000/04/01 09:27:35 augustss Exp $	*/
-/*	$FreeBSD$	*/
+/*	$FreeBSD: src/sys/dev/usb/ohcireg.h,v 1.20 2003/07/15 23:12:54 jmg Exp $	*/
 
 
 /*
@@ -147,7 +147,6 @@
 #define OHCI_PAGE_SIZE 0x1000
 #define OHCI_PAGE(x) ((x) &~ 0xfff)
 #define OHCI_PAGE_OFFSET(x) ((x) & 0xfff)
-#define OHCI_PAGE_MASK(x) ((x) & 0xfff)
 
 typedef struct {
 	u_int32_t	ed_flags;
Index: ohcivar.h
===================================================================
RCS file: /home/ncvs/src/sys/dev/usb/ohcivar.h,v
retrieving revision 1.35
diff -u -u -r1.35 ohcivar.h
--- ohcivar.h	15 Jul 2003 23:19:49 -0000	1.35
+++ ohcivar.h	23 Jun 2004 07:34:23 -0000
@@ -1,5 +1,5 @@
 /*	$NetBSD: ohcivar.h,v 1.30 2001/12/31 12:20:35 augustss Exp $	*/
-/*	$FreeBSD$	*/
+/*	$FreeBSD: src/sys/dev/usb/ohcivar.h,v 1.35 2003/07/15 23:19:49 jmg Exp $	*/
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -151,9 +151,7 @@
 struct ohci_xfer {
 	struct usbd_xfer xfer;
 	struct usb_task	abort_task;
-	u_int32_t ohci_xfer_flags;
 };
-#define OHCI_ISOC_DIRTY  0x01
 
 #define OXFER(xfer) ((struct ohci_xfer *)(xfer))
 
Index: ugen.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/usb/ugen.c,v
retrieving revision 1.81
diff -u -u -r1.81 ugen.c
--- ugen.c	9 Nov 2003 09:17:22 -0000	1.81
+++ ugen.c	23 Jun 2004 07:34:23 -0000
@@ -7,7 +7,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: src/sys/dev/usb/ugen.c,v 1.81 2003/11/09 09:17:22 tanimura Exp $");
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -94,8 +94,8 @@
 #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 */
+#define UGEN_NISOREQS	4	/* number of outstanding xfer requests */
+#define UGEN_NISORFRMS	10	/* number of frames (milliseconds) per req */
 
 struct ugen_endpoint {
 	struct ugen_softc *sc;
@@ -115,6 +115,9 @@
 	u_char *limit;		/* end of circular buffer (isoc) */
 	u_char *cur;		/* current read location (isoc) */
 	u_int32_t timeout;
+	int isorate;
+	int isoflen;
+	int isofres;
 	struct isoreq {
 		struct ugen_endpoint *sce;
 		usbd_xfer_handle xfer;
@@ -134,11 +137,67 @@
 	struct ugen_endpoint sc_endpoints[USB_MAX_ENDPOINTS][2];
 #define OUT 0
 #define IN  1
+	int sc_isorate;
 
 	int sc_refcnt;
 	u_char sc_dying;
 };
 
+#define ISOC_CALC_FLEN(r) \
+	((r / USB_FRAMES_PER_SECOND) & (~1))
+#define ISOC_CALC_FRES(r) \
+	(((r - USB_FRAMES_PER_SECOND * ISOC_CALC_FLEN(r)) / \
+		(USB_FRAMES_PER_SECOND / UGEN_NISORFRMS)) >> 1)
+#define ISOC_MAKE_FLEN(l,r,o) \
+	(l + ((o < r) ? 2 : 0))
+
+#define IBUF_SPACE_ALLOC(sce,s,d) { \
+	sce->ibuf = malloc(s, d, M_WAITOK); \
+	sce->cur = sce->fill = sce->ibuf; \
+	sce->limit = sce->ibuf + (s); \
+}
+#define IBUF_SPACE_FREE(sce,d) \
+	if (sce->ibuf != NULL) { \
+		free(sce->ibuf, d); \
+		sce->ibuf = NULL; \
+	}
+#define IBUF_SPACE_PNTR(sce) \
+	(sce->ibuf)
+#define IBUF_SPACE_WR_PNTR(sce) \
+	(sce->fill)
+#define IBUF_SPACE_WR_SIZE(sce) \
+	(sce->cur > sce->fill ? \
+		(sce->cur - sce->fill) - 1 : \
+		(sce->limit - sce->fill) + (sce->cur - sce->ibuf) - 1)
+#define IBUF_SPACE_WR_ZERO(sce) \
+	(sce->fill + 1 == sce->cur) || \
+		(sce->fill + 1 == sce->limit && sce->cur == sce->ibuf)
+#define IBUF_SPACE_WR_CHNK(sce) \
+	(sce->cur > sce->fill ? \
+		(sce->cur - sce->fill) - 1 : \
+		(sce->limit - sce->fill) - (sce->cur == sce->ibuf ? 1 : 0))
+#define IBUF_SPACE_WR_INCR(sce, n) { \
+	sce->fill += (n); \
+	if (sce->fill >= sce->limit) \
+		sce->fill = sce->ibuf + (sce->fill - sce->limit); \
+	}
+#define IBUF_SPACE_RD_PNTR(sce) \
+	(sce->cur)
+#define IBUF_SPACE_RD_SIZE(sce) \
+	(sce->fill >= sce->cur ? \
+		sce->fill - sce->cur : \
+		(sce->limit - sce->cur) + (sce->fill - sce->ibuf))
+#define IBUF_SPACE_RD_ZERO(sce) \
+	(sce->fill == sce->cur)
+#define IBUF_SPACE_RD_CHNK(sce) \
+	(sce->fill > sce->cur ? \
+		sce->fill - sce->cur : sce->limit - sce->cur)
+#define IBUF_SPACE_RD_INCR(sce, n) { \
+	sce->cur += (n); \
+	if (sce->cur >= sce->limit) \
+		sce->cur = sce->ibuf + (sce->cur - sce->limit); \
+	}
+
 #if defined(__NetBSD__) || defined(__OpenBSD__)
 cdev_decl(ugen);
 #elif defined(__FreeBSD__)
@@ -168,12 +227,13 @@
 
 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,
+Static void ugenintr_isoc(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,
 			 caddr_t, int, usb_proc_ptr);
+Static int ugen_do_poll(struct ugen_softc *, int, int, usb_proc_ptr);
 #if defined(__FreeBSD__)
 Static void ugen_make_devnodes(struct ugen_softc *sc);
 Static void ugen_destroy_devnodes(struct ugen_softc *sc);
@@ -391,6 +451,7 @@
 	usbd_xfer_handle xfer;
 	void *buf;
 	int i, j;
+	int s;
 
 	USB_GET_SC_OPEN(ugen, unit, sc);
 
@@ -440,18 +501,22 @@
 			isize = UGETW(edesc->wMaxPacketSize);
 			if (isize == 0)	/* shouldn't happen */
 				return (EINVAL);
-			sce->ibuf = malloc(isize, M_USBDEV, M_WAITOK);
 			DPRINTFN(5, ("ugenopen: intr endpt=%d,isize=%d\n",
 				     endpt, isize));
-			if (clalloc(&sce->q, UGEN_IBSIZE, 0) == -1)
+			IBUF_SPACE_ALLOC(sce, isize, M_USBDEV);
+			if (!IBUF_SPACE_PNTR(sce))
+ 				return (ENOMEM);
+			if (clalloc(&sce->q, UGEN_IBSIZE, 0) == -1) {
+				IBUF_SPACE_FREE(sce, M_USBDEV);
 				return (ENOMEM);
+			}
 			err = usbd_open_pipe_intr(sce->iface,
 				edesc->bEndpointAddress,
 				USBD_SHORT_XFER_OK, &sce->pipeh, sce,
 				sce->ibuf, isize, ugenintr,
 				USBD_DEFAULT_INTERVAL);
 			if (err) {
-				free(sce->ibuf, M_USBDEV);
+				IBUF_SPACE_FREE(sce, M_USBDEV);
 				clfree(&sce->q);
 				return (EIO);
 			}
@@ -464,25 +529,25 @@
 				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));
+			IBUF_SPACE_ALLOC(sce, isize * UGEN_NISOFRAMES, 
+				M_USBDEV);
+			if (!IBUF_SPACE_PNTR(sce))
+				return (ENOMEM);
 			err = usbd_open_pipe(sce->iface,
 				  edesc->bEndpointAddress, 0, &sce->pipeh);
 			if (err) {
-				free(sce->ibuf, M_USBDEV);
+				IBUF_SPACE_FREE(sce, M_USBDEV);
 				return (EIO);
 			}
+			sce->isorate = sc->sc_isorate;
+			sce->isoflen = ISOC_CALC_FLEN(sce->isorate);
+			sce->isofres = ISOC_CALC_FRES(sce->isorate);
 			for(i = 0; i < UGEN_NISOREQS; ++i) {
-				sce->isoreqs[i].sce = sce;
 				xfer = usbd_alloc_xfer(sc->sc_udev);
 				if (xfer == 0)
 					goto bad;
@@ -494,20 +559,30 @@
 					goto bad;
 				}
 				sce->isoreqs[i].dmabuf = buf;
+				sce->isoreqs[i].sce = sce;
+				if (dir == OUT)
+					memset(buf, 0, isize * UGEN_NISORFRMS);
 				for(j = 0; j < UGEN_NISORFRMS; ++j)
-					sce->isoreqs[i].sizes[j] = isize;
+					sce->isoreqs[i].sizes[j] = 
+						(dir == IN) ? isize :
+						ISOC_MAKE_FLEN(sce->isoflen,
+							sce->isofres, j);	
 				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);
+					 ugenintr_isoc);
 			}
+			s = splusb();
+			for(i = 0; i < UGEN_NISOREQS; ++i)
+				(void)usbd_transfer(sce->isoreqs[i].xfer);
+			splx(s);
 			DPRINTFN(5, ("ugenopen: isoc open done\n"));
 			break;
 		bad:
 			while (--i >= 0) /* implicit buffer free */
 				usbd_free_xfer(sce->isoreqs[i].xfer);
+			IBUF_SPACE_FREE(sce, M_USBDEV);
 			return (ENOMEM);
 		case UE_CONTROL:
 			sce->timeout = USBD_DEFAULT_TIMEOUT;
@@ -570,11 +645,7 @@
 			break;
 		}
 
-		if (sce->ibuf != NULL) {
-			free(sce->ibuf, M_USBDEV);
-			sce->ibuf = NULL;
-			clfree(&sce->q);
-		}
+		IBUF_SPACE_FREE(sce, M_USBDEV);
 	}
 	sc->sc_is_open[endpt] = 0;
 
@@ -680,44 +751,34 @@
 		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", sce));
-			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;
-
-
+ 		s = splusb();
+		while (uio->uio_resid > 0 && !error) {
+			if (IBUF_SPACE_RD_ZERO(sce)) {
+				if (flag & IO_NDELAY) {
+					error = EWOULDBLOCK;
+					break;
+				}
+				sce->state |= UGEN_ASLP;
+				DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
+				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;
+			} else {
+				n = min(IBUF_SPACE_RD_CHNK(sce),uio->uio_resid);
+
+				DPRINTFN(5, ("ugenread: isoc read %d\n", n));
+
+				error = uiomove(IBUF_SPACE_RD_PNTR(sce), n,uio);
+				if (!error)
+					IBUF_SPACE_RD_INCR(sce, n);
+ 			}
+ 		}
+ 		splx(s);
+ 		break;
 	default:
 		return (ENXIO);
 	}
@@ -749,6 +810,7 @@
 	char buf[UGEN_BBSIZE];
 	usbd_xfer_handle xfer;
 	usbd_status err;
+	int s;
 
 	DPRINTFN(5, ("%s: ugenwrite: %d\n", USBDEVNAME(sc->sc_dev), endpt));
 
@@ -818,6 +880,35 @@
 		}
 		usbd_free_xfer(xfer);
 		break;
+	case UE_ISOCHRONOUS:
+		s = splusb();
+		while(uio->uio_resid > 0 && !error) {
+			if (IBUF_SPACE_WR_ZERO(sce)) {
+				if (flag & IO_NDELAY) {
+					error = EWOULDBLOCK;
+					break;
+				}
+				sce->state |= UGEN_ASLP;
+				DPRINTFN(5, ("ugenwrite: sleep on %p\n", sce));
+				error = tsleep(sce, PZERO|PCATCH, "ugenwi", 0);
+				DPRINTFN(5, ("ugenwrite: woke, error=%d\n",
+					 error));
+				if (sc->sc_dying)
+ 					error = EIO;
+				if (error)
+					sce->state &= ~UGEN_ASLP;
+			} else {
+				n = min(IBUF_SPACE_WR_CHNK(sce),uio->uio_resid);
+
+				DPRINTFN(5, ("ugenwrite: isoc write %d\n", n));
+
+				error = uiomove(IBUF_SPACE_WR_PNTR(sce), n,uio);
+				if (!error)
+					IBUF_SPACE_WR_INCR(sce, n);
+ 			}
+ 		}
+		splx(s);
+ 		break;
 	default:
 		return (ENXIO);
 	}
@@ -949,60 +1040,72 @@
 }
 
 Static void
-ugen_isoc_rintr(usbd_xfer_handle xfer, usbd_private_handle addr,
+ugenintr_isoc(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;
-	int i, isize;
+	u_int32_t count;
+	int i, dir, psize, n;
 
 	/* Return if we are aborting. */
 	if (status == USBD_CANCELLED)
 		return;
 
+	dir = UE_GET_DIR(sce->edesc->bEndpointAddress) == UE_DIR_IN ? IN : OUT;
+	psize = UGETW(sce->edesc->wMaxPacketSize);
+
 	usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL);
-	DPRINTFN(5,("ugen_isoc_rintr: xfer %d, count=%d\n",
-		    (int)(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));
-	}
-
-	isize = UGETW(sce->edesc->wMaxPacketSize);
-	for (i = 0; i < UGEN_NISORFRMS; i++) {
-		u_int32_t actlen = req->sizes[i];
-		char const *buf = (char const *)req->dmabuf + isize * i;
-
-		/* copy data to buffer */
-		while (actlen > 0) {
-			n = min(actlen, sce->limit - sce->fill);
-			memcpy(sce->fill, buf, n);
-
-			buf += n;
-			actlen -= n;
-			sce->fill += n;
-			if(sce->fill == sce->limit)
-				sce->fill = sce->ibuf;
-		}
+	DPRINTFN(5,("ugenintr_isoc: xfer %ld, dir %s, count=%d\n",
+	    (long)(req - sce->isoreqs), (dir == IN) ? "IN" : "OUT", count));
+
+	if (dir == OUT) {
+		u_char* buf = (u_char *)req->dmabuf;
+		for (i = 0; i < UGEN_NISORFRMS; i++) {
+			int len = ISOC_MAKE_FLEN(sce->isoflen, sce->isofres, i);
+			req->sizes[i] = len;
+
+			for (; len > 0 && !IBUF_SPACE_RD_ZERO(sce);
+					buf += n, len -= n) {
+				n = min(IBUF_SPACE_RD_CHNK(sce), len);
+				memcpy(buf, IBUF_SPACE_RD_PNTR(sce), n);
+				IBUF_SPACE_RD_INCR(sce, n);
+			}
 
-		/* setup size for next transfer */
-		req->sizes[i] = isize;
+			if (len > 0) {
+				memset(buf, 0, len);
+				buf += len;
+			}
+ 		}
+	} else {
+		u_char* buf = (u_char *)req->dmabuf;
+		for (i = 0; i < UGEN_NISORFRMS; i++) {
+			int len = req->sizes[i];
+
+			if ((n = IBUF_SPACE_WR_SIZE(sce)) < len) {
+				IBUF_SPACE_RD_INCR(sce, len - n);
+				DPRINTFN(5, ("ugenintr_isoc: throw %d bytes\n",
+					len - n));
+			}
+
+			for (; len > 0; buf += n, len -= n) {
+				n = min(IBUF_SPACE_WR_CHNK(sce), len);
+				memcpy(IBUF_SPACE_WR_PNTR(sce), buf, n);
+				IBUF_SPACE_WR_INCR(sce, n);
+			}
+
+			buf += (psize - req->sizes[i]);
+			req->sizes[i] = psize;
+		}
 	}
 
 	usbd_setup_isoc_xfer(xfer, sce->pipeh, req, req->sizes, UGEN_NISORFRMS,
-			     USBD_NO_COPY, ugen_isoc_rintr);
+			     USBD_NO_COPY, ugenintr_isoc);
 	(void)usbd_transfer(xfer);
 
 	if (sce->state & UGEN_ASLP) {
 		sce->state &= ~UGEN_ASLP;
-		DPRINTFN(5, ("ugen_isoc_rintr: waking %p\n", sce));
+		DPRINTFN(5, ("ugenintr_isoc: waking %p\n", sce));
 		wakeup(sce);
 	}
 	selwakeuppri(&sce->rsel, PZERO);
@@ -1170,6 +1273,28 @@
 			return (EINVAL);
 		sce->timeout = *(int *)addr;
 		return (0);
+	case USB_GET_ISOC_RATE:
+		if (endpt == USB_CONTROL_ENDPOINT)
+			*(int *)addr = sc->sc_isorate;
+		else {
+			sce = &sc->sc_endpoints[endpt][OUT];
+			if (sce == NULL || sce->edesc == NULL)
+				return (EINVAL);
+			*(int *)addr = sce->isorate;
+		}
+		return (0);
+	case USB_SET_ISOC_RATE:
+		if (endpt == USB_CONTROL_ENDPOINT)
+			sc->sc_isorate = *(int *)addr;
+		else {
+			sce = &sc->sc_endpoints[endpt][OUT];
+			if (sce == NULL || sce->edesc == NULL)
+				return (EINVAL);
+			sce->isorate = *(int *)addr;
+			sce->isoflen = ISOC_CALC_FLEN(sce->isorate);
+			sce->isofres = ISOC_CALC_FRES(sce->isorate);
+		}
+		return (0);
 	default:
 		break;
 	}
@@ -1405,65 +1530,80 @@
 	return (error);
 }
 
-int
-ugenpoll(dev_t dev, int events, usb_proc_ptr p)
+Static int
+ugen_do_poll(struct ugen_softc *sc, int endpt, int events, usb_proc_ptr p)
 {
-	struct ugen_softc *sc;
-	struct ugen_endpoint *sce;
+	struct ugen_endpoint *sce_in, *sce_out;
+	usb_endpoint_descriptor_t *edesc;
 	int revents = 0;
 	int s;
 
-	USB_GET_SC(ugen, UGENUNIT(dev), sc);
-
 	if (sc->sc_dying)
 		return (EIO);
 
-	/* XXX always IN */
-	sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN];
-	if (sce == NULL)
+	sce_in = &sc->sc_endpoints[endpt][IN];
+	sce_out = &sc->sc_endpoints[endpt][OUT];
+	if (sce_in == NULL && sce_out == NULL)
 		return (EINVAL);
 
-	if (!sce->edesc) {
-		printf("ugenpoll: no edesc\n");
+	if (sce_in && sce_in->edesc)
+		edesc = sce_in->edesc;
+	else if (sce_out && sce_out->edesc)
+		edesc = sce_out->edesc;
+	else /* no edesc! */
 		return (EIO);
-	}
-	if (!sce->pipeh) {
-		printf("ugenpoll: no pipe\n");
-		return (EIO);
-	}
 
 	s = splusb();
-	switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
+	switch (edesc->bmAttributes & UE_XFERTYPE) {
 	case UE_INTERRUPT:
-		if (events & (POLLIN | POLLRDNORM)) {
-			if (sce->q.c_cc > 0)
+		if (sce_in && events & (POLLIN | POLLRDNORM)) {
+			if (sce_in->q.c_cc > 0)
 				revents |= events & (POLLIN | POLLRDNORM);
 			else
-				selrecord(p, &sce->rsel);
+				selrecord(p, &sce_in->rsel);
 		}
 		break;
 	case UE_ISOCHRONOUS:
-		if (events & (POLLIN | POLLRDNORM)) {
-			if (sce->cur != sce->fill)
+		if (sce_in && events & (POLLIN | POLLRDNORM)) {
+			if (!IBUF_SPACE_RD_ZERO(sce_in))
 				revents |= events & (POLLIN | POLLRDNORM);
 			else
-				selrecord(p, &sce->rsel);
+				selrecord(p, &sce_in->rsel);
+		}
+		if (sce_out && events & (POLLOUT | POLLWRNORM)) {
+			if (!IBUF_SPACE_WR_ZERO(sce_out))
+				revents |= events & (POLLOUT | POLLWRNORM);
+			else
+				selrecord(p, &sce_out->rsel);
 		}
 		break;
 	case UE_BULK:
-		/*
-		 * We have no easy way of determining if a read will
-		 * yield any data or a write will happen.
-		 * Pretend they will.
-		 */
-		revents |= events &
-			   (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM);
+		if (sce_in && events & (POLLIN | POLLRDNORM))
+			revents |= events & (POLLIN | POLLRDNORM);
+		if (sce_out && events & (POLLOUT | POLLWRNORM))
+			revents |= events & (POLLOUT | POLLWRNORM);
 		break;
 	default:
 		break;
 	}
 	splx(s);
 	return (revents);
+}
+
+int
+ugenpoll(dev_t dev, int events, usb_proc_ptr p)
+{
+	int endpt = UGENENDPOINT(dev);
+	struct ugen_softc *sc;
+	int error;
+
+	USB_GET_SC(ugen, UGENUNIT(dev), sc);
+
+	sc->sc_refcnt++;
+	error = ugen_do_poll(sc, endpt, events, p);
+	if (--sc->sc_refcnt < 0)
+		usb_detach_wakeup(USBDEV(sc->sc_dev));
+	return (error);
 }
 
 #if defined(__FreeBSD__)
Index: usb.h
===================================================================
RCS file: /home/ncvs/src/sys/dev/usb/usb.h,v
retrieving revision 1.38
diff -u -u -r1.38 usb.h
--- usb.h	6 Nov 2002 21:37:21 -0000	1.38
+++ usb.h	23 Jun 2004 07:34:23 -0000
@@ -1,5 +1,5 @@
 /*	$NetBSD: usb.h,v 1.69 2002/09/22 23:20:50 augustss Exp $	*/
-/*	$FreeBSD$    */
+/*	$FreeBSD: src/sys/dev/usb/usb.h,v 1.38 2002/11/06 21:37:21 joe Exp $    */
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -685,6 +685,8 @@
 #define USB_GET_DEVICEINFO	_IOR ('U', 112, struct usb_device_info)
 #define USB_SET_SHORT_XFER	_IOW ('U', 113, int)
 #define USB_SET_TIMEOUT		_IOW ('U', 114, int)
+#define USB_GET_ISOC_RATE	_IOR ('U', 115, int)
+#define USB_SET_ISOC_RATE	_IOW ('U', 116, int)
 
 /* Modem device */
 #define USB_GET_CM_OVER_DATA	_IOR ('U', 130, int)
Index: usbdi.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/usb/usbdi.c,v
retrieving revision 1.84
diff -u -u -r1.84 usbdi.c
--- usbdi.c	9 Nov 2003 23:56:19 -0000	1.84
+++ usbdi.c	23 Jun 2004 07:34:25 -0000
@@ -1,7 +1,7 @@
 /*	$NetBSD: usbdi.c,v 1.103 2002/09/27 15:37:38 provos Exp $	*/
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: src/sys/dev/usb/usbdi.c,v 1.84 2003/11/09 23:56:19 joe Exp $");
 
 /*
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -758,6 +758,9 @@
 {
 	usbd_pipe_handle pipe = xfer->pipe;
 	usb_dma_t *dmap = &xfer->dmabuf;
+	int sync = xfer->flags & USBD_SYNCHRONOUS;
+	int errd = xfer->status == USBD_CANCELLED || 
+		    xfer->status == USBD_TIMEOUT;
 	int repeat = pipe->repeat;
 	int polling;
 
@@ -842,14 +845,12 @@
 	pipe->methods->done(xfer);
 #endif
 
-	if ((xfer->flags & USBD_SYNCHRONOUS) && !polling)
+	if (sync && !polling)
 		wakeup(xfer);
 
 	if (!repeat) {
 		/* XXX should we stop the queue on all errors? */
-		if ((xfer->status == USBD_CANCELLED ||
-		     xfer->status == USBD_TIMEOUT) &&
-		    pipe->iface != NULL)		/* not control pipe */
+		if (errd && pipe->iface != NULL) /* not control pipe */
 			pipe->running = 0;
 		else
 			usbd_start_next(pipe);
>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200406231002.i5NA2AUh019693>