Add isoc support. From FreeBSD via Berndt Josef Wulf <wulf@ping.net.au>

This commit is contained in:
augustss 2000-09-08 00:55:26 +00:00
parent f0c15086d2
commit 96d1acf3d6
1 changed files with 183 additions and 9 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: ugen.c,v 1.40 2000/06/01 14:28:59 augustss Exp $ */
/* $NetBSD: ugen.c,v 1.41 2000/09/08 00:55:26 augustss Exp $ */
/* $FreeBSD: src/sys/dev/usb/ugen.c,v 1.26 1999/11/17 22:33:41 n_hibma Exp $ */
/*
@ -75,6 +75,14 @@ int ugendebug = 0;
#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 @@ struct ugen_endpoint {
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 struct cdevsw ugen_cdevsw = {
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 @@ ugenopen(dev_t dev, int flag, int mode, struct proc *p)
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 @@ ugenopen(dev_t dev, int flag, int mode, struct proc *p)
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 @@ ugenclose(dev_t dev, int flag, int mode, struct proc *p)
struct ugen_softc *sc;
struct ugen_endpoint *sce;
int dir;
int i;
USB_GET_SC(ugen, UGENUNIT(dev), sc);
@ -387,6 +451,19 @@ ugenclose(dev_t dev, int flag, int mode, struct proc *p)
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 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct uio *uio, int flag)
}
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 @@ ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
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)
{
@ -1123,6 +1289,14 @@ ugenpoll(dev_t dev, int events, struct proc *p)
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);
}
break;
case UE_BULK:
/*
* We have no easy way of determining if a read will