kern/68232: ugen(4) isochronous handling correction and tx support
Matthew Gream
matthew.gream at pobox.com
Wed Jun 23 10:10:29 GMT 2004
>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 at 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 at 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:
More information about the freebsd-bugs
mailing list