Add isoc support. From FreeBSD via Berndt Josef Wulf <wulf@ping.net.au>
This commit is contained in:
parent
f0c15086d2
commit
96d1acf3d6
|
@ -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 $ */
|
/* $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)
|
#define DPRINTFN(n,x)
|
||||||
#endif
|
#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_endpoint {
|
||||||
struct ugen_softc *sc;
|
struct ugen_softc *sc;
|
||||||
usb_endpoint_descriptor_t *edesc;
|
usb_endpoint_descriptor_t *edesc;
|
||||||
|
@ -85,14 +93,19 @@ struct ugen_endpoint {
|
||||||
usbd_pipe_handle pipeh;
|
usbd_pipe_handle pipeh;
|
||||||
struct clist q;
|
struct clist q;
|
||||||
struct selinfo rsel;
|
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;
|
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 {
|
struct ugen_softc {
|
||||||
USBBASEDEVICE sc_dev; /* base device */
|
USBBASEDEVICE sc_dev; /* base device */
|
||||||
usbd_device_handle sc_udev;
|
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,
|
Static void ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr,
|
||||||
usbd_status status);
|
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_read(struct ugen_softc *, int, struct uio *, int);
|
||||||
Static int ugen_do_write(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,
|
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;
|
struct ugen_endpoint *sce;
|
||||||
int dir, isize;
|
int dir, isize;
|
||||||
usbd_status err;
|
usbd_status err;
|
||||||
|
usbd_xfer_handle xfer;
|
||||||
|
void *buf;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
USB_GET_SC_OPEN(ugen, unit, sc);
|
USB_GET_SC_OPEN(ugen, unit, sc);
|
||||||
|
|
||||||
|
@ -339,8 +357,53 @@ ugenopen(dev_t dev, int flag, int mode, struct proc *p)
|
||||||
if (err)
|
if (err)
|
||||||
return (EIO);
|
return (EIO);
|
||||||
break;
|
break;
|
||||||
case UE_CONTROL:
|
|
||||||
case UE_ISOCHRONOUS:
|
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);
|
return (EINVAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,6 +418,7 @@ ugenclose(dev_t dev, int flag, int mode, struct proc *p)
|
||||||
struct ugen_softc *sc;
|
struct ugen_softc *sc;
|
||||||
struct ugen_endpoint *sce;
|
struct ugen_endpoint *sce;
|
||||||
int dir;
|
int dir;
|
||||||
|
int i;
|
||||||
|
|
||||||
USB_GET_SC(ugen, UGENUNIT(dev), sc);
|
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);
|
usbd_close_pipe(sce->pipeh);
|
||||||
sce->pipeh = NULL;
|
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) {
|
if (sce->ibuf != NULL) {
|
||||||
free(sce->ibuf, M_USBDEV);
|
free(sce->ibuf, M_USBDEV);
|
||||||
sce->ibuf = NULL;
|
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);
|
usbd_free_xfer(xfer);
|
||||||
break;
|
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:
|
default:
|
||||||
return (ENXIO);
|
return (ENXIO);
|
||||||
}
|
}
|
||||||
|
@ -702,6 +818,56 @@ ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
|
||||||
selwakeup(&sce->rsel);
|
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
|
Static usbd_status
|
||||||
ugen_set_interface(struct ugen_softc *sc, int ifaceidx, int altno)
|
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);
|
selrecord(p, &sce->rsel);
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case UE_BULK:
|
||||||
/*
|
/*
|
||||||
* We have no easy way of determining if a read will
|
* We have no easy way of determining if a read will
|
||||||
|
|
Loading…
Reference in New Issue