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 $ */
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue