Bug #994 ยป ugen-fixes.patch
| sys/dev/usbmisc/ugen/ugen.c | ||
|---|---|---|
|
make_dev(&ugen_ops, UGENMINOR(device_get_unit(sc->sc_dev), 0),
|
||
|
UID_ROOT, GID_OPERATOR, 0644, "%s", device_get_nameunit(sc->sc_dev));
|
||
|
usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev);
|
||
|
return 0;
|
||
|
}
|
||
| ... | ... | |
|
static void
|
||
|
ugen_destroy_devnodes(struct ugen_softc *sc)
|
||
|
{
|
||
|
int endptno;
|
||
|
int endptno, prev_sc_dying;
|
||
|
cdev_t dev;
|
||
|
prev_sc_dying = sc->sc_dying;
|
||
|
sc->sc_dying = 1;
|
||
|
/* destroy all devices for the other (existing) endpoints as well */
|
||
|
for (endptno = 1; endptno < USB_MAX_ENDPOINTS; endptno++) {
|
||
| ... | ... | |
|
}
|
||
|
}
|
||
|
}
|
||
|
sc->sc_dying = prev_sc_dying;
|
||
|
}
|
||
|
static int
|
||
| ... | ... | |
|
DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n",
|
||
|
ap->a_oflags, ap->a_devtype, unit, endpt));
|
||
|
if (sc == NULL || sc->sc_dying)
|
||
|
if (sc->sc_dying)
|
||
|
return (ENXIO);
|
||
|
if (sc->sc_is_open[endpt])
|
||
| ... | ... | |
|
if (!(ap->a_fflag & (dir == OUT ? FWRITE : FREAD)))
|
||
|
continue;
|
||
|
sce = &sc->sc_endpoints[endpt][dir];
|
||
|
if (sce == NULL || sce->pipeh == NULL)
|
||
|
if (sce->pipeh == NULL)
|
||
|
continue;
|
||
|
DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n",
|
||
|
endpt, dir, sce));
|
||
| ... | ... | |
|
if (endpt == USB_CONTROL_ENDPOINT)
|
||
|
return (ENODEV);
|
||
|
if (sce == NULL)
|
||
|
return (EINVAL);
|
||
|
#ifdef DIAGNOSTIC
|
||
|
if (sce->edesc == NULL) {
|
||
|
kprintf("ugenread: no edesc\n");
|
||
|
return (EIO);
|
||
| ... | ... | |
|
kprintf("ugenread: no pipe\n");
|
||
|
return (EIO);
|
||
|
}
|
||
|
#endif
|
||
|
buf = getugenbuf(ugen_bufsize, &ugen_bbsize);
|
||
| ... | ... | |
|
/* Block until activity occurred. */
|
||
|
crit_enter();
|
||
|
while (sce->q.c_cc == 0) {
|
||
|
if (flag & IO_NDELAY) {
|
||
|
if (flag & O_NONBLOCK) {
|
||
|
crit_exit();
|
||
|
error = EWOULDBLOCK;
|
||
|
goto done;
|
||
|
}
|
||
|
sce->state |= UGEN_ASLP;
|
||
|
DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
|
||
|
error = tsleep(sce, PCATCH, "ugenri", 0);
|
||
|
error = tsleep(sce, PCATCH, "ugenri",
|
||
|
(sce->timeout * hz + 999) / 1000);
|
||
|
sce->state &= ~UGEN_ASLP;
|
||
|
DPRINTFN(5, ("ugenread: woke, error=%d\n", error));
|
||
|
if (sc->sc_dying)
|
||
|
error = EIO;
|
||
|
if (error) {
|
||
|
sce->state &= ~UGEN_ASLP;
|
||
|
if (error == EAGAIN) {
|
||
|
error = 0; /* timeout, return 0 bytes */
|
||
|
break;
|
||
|
}
|
||
|
if (error)
|
||
|
break;
|
||
|
}
|
||
|
crit_exit();
|
||
| ... | ... | |
|
case UE_ISOCHRONOUS:
|
||
|
crit_enter();
|
||
|
while (sce->cur == sce->fill) {
|
||
|
if (flag & IO_NDELAY) {
|
||
|
if (flag & O_NONBLOCK) {
|
||
|
crit_exit();
|
||
|
error = EWOULDBLOCK;
|
||
|
goto done;
|
||
|
}
|
||
|
sce->state |= UGEN_ASLP;
|
||
|
DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
|
||
|
error = tsleep(sce, PCATCH, "ugenri", 0);
|
||
|
error = tsleep(sce, PCATCH, "ugenri",
|
||
|
(sce->timeout * hz + 999) / 1000);
|
||
|
sce->state &= ~UGEN_ASLP;
|
||
|
DPRINTFN(5, ("ugenread: woke, error=%d\n", error));
|
||
|
if (sc->sc_dying)
|
||
|
error = EIO;
|
||
|
if (error) {
|
||
|
sce->state &= ~UGEN_ASLP;
|
||
|
if (error == EAGAIN) {
|
||
|
error = 0; /* timeout, return 0 bytes */
|
||
|
break;
|
||
|
}
|
||
|
if (error)
|
||
|
break;
|
||
|
}
|
||
|
while (sce->cur != sce->fill && uio->uio_resid > 0 && !error) {
|
||
|
if(sce->fill > sce->cur)
|
||
|
if (sce->fill > sce->cur)
|
||
|
n = min(sce->fill - sce->cur, uio->uio_resid);
|
||
|
else
|
||
|
n = min(sce->limit - sce->cur, uio->uio_resid);
|
||
| ... | ... | |
|
int error;
|
||
|
sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev));
|
||
|
if (sc->sc_dying)
|
||
|
return (EIO);
|
||
|
sc->sc_refcnt++;
|
||
|
error = ugen_do_read(sc, endpt, ap->a_uio, ap->a_ioflag);
|
||
| ... | ... | |
|
if (endpt == USB_CONTROL_ENDPOINT)
|
||
|
return (ENODEV);
|
||
|
if (sce == NULL)
|
||
|
return (EINVAL);
|
||
|
#ifdef DIAGNOSTIC
|
||
|
if (sce->edesc == NULL) {
|
||
|
kprintf("ugenwrite: no edesc\n");
|
||
|
return (EIO);
|
||
| ... | ... | |
|
kprintf("ugenwrite: no pipe\n");
|
||
|
return (EIO);
|
||
|
}
|
||
|
#endif
|
||
|
buf = getugenbuf(ugen_bufsize, &ugen_bbsize);
|
||
| ... | ... | |
|
sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev));
|
||
|
if (sc->sc_dying)
|
||
|
return (EIO);
|
||
|
sc->sc_refcnt++;
|
||
|
error = ugen_do_write(sc, endpt, ap->a_uio, ap->a_ioflag);
|
||
|
if (--sc->sc_refcnt < 0)
|
||
| ... | ... | |
|
sce = &sc->sc_endpoints[i][dir];
|
||
|
if (sce && sce->pipeh)
|
||
|
usbd_abort_pipe(sce->pipeh);
|
||
|
selwakeup(&sce->rsel);
|
||
|
}
|
||
|
}
|
||
|
crit_enter();
|
||
| ... | ... | |
|
ugen_destroy_devnodes(sc);
|
||
|
dev_ops_remove(&ugen_ops,
|
||
|
UGENUNITMASK, UGENMINOR(device_get_unit(sc->sc_dev), 0));
|
||
|
usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
|
||
|
return (0);
|
||
|
}
|
||
| ... | ... | |
|
if (endpt == USB_CONTROL_ENDPOINT)
|
||
|
return (EINVAL);
|
||
|
sce = &sc->sc_endpoints[endpt][IN];
|
||
|
if (sce == NULL)
|
||
|
return (EINVAL);
|
||
|
if (sce->pipeh == NULL) {
|
||
|
kprintf("ugenioctl: USB_SET_SHORT_XFER, no pipe\n");
|
||
| ... | ... | |
|
return (0);
|
||
|
case USB_SET_TIMEOUT:
|
||
|
sce = &sc->sc_endpoints[endpt][IN];
|
||
|
if (sce == NULL)
|
||
|
return (EINVAL);
|
||
|
sce->timeout = *(int *)addr;
|
||
|
sce = &sc->sc_endpoints[endpt][OUT];
|
||
|
sce->timeout = *(int *)addr;
|
||
|
return (0);
|
||
|
default:
|
||
| ... | ... | |
|
int error;
|
||
|
sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev));
|
||
|
if (sc->sc_dying)
|
||
|
return (EIO);
|
||
|
sc->sc_refcnt++;
|
||
|
error = ugen_do_ioctl(sc, endpt, ap->a_cmd, ap->a_data, ap->a_fflag);
|
||
| ... | ... | |
|
{
|
||
|
cdev_t dev = ap->a_head.a_dev;
|
||
|
struct ugen_softc *sc;
|
||
|
struct ugen_endpoint *sce;
|
||
|
struct ugen_endpoint *sce_in, *sce_out;
|
||
|
usb_endpoint_descriptor_t *edesc;
|
||
|
int revents = 0;
|
||
|
sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev));
|
||
|
if (sc->sc_dying)
|
||
|
return (EIO);
|
||
|
/* XXX always IN */
|
||
|
sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN];
|
||
|
if (sce == NULL)
|
||
|
return (EINVAL);
|
||
|
if (!sce->edesc) {
|
||
|
kprintf("ugenpoll: no edesc\n");
|
||
|
return (EIO);
|
||
|
}
|
||
|
if (!sce->pipeh) {
|
||
|
kprintf("ugenpoll: no pipe\n");
|
||
|
return (EIO);
|
||
|
if (sc->sc_dying) {
|
||
|
return ((ap->a_events & (POLLIN | POLLOUT | POLLRDNORM |
|
||
|
POLLWRNORM)) | POLLHUP);
|
||
|
}
|
||
|
/* Do not allow to poll a control endpoint */
|
||
|
if (UGENENDPOINT(dev) == USB_CONTROL_ENDPOINT) {
|
||
|
return (ap->a_events & (POLLIN | POLLOUT | POLLRDNORM |
|
||
|
POLLWRNORM));
|
||
|
}
|
||
|
sce_in = &sc->sc_endpoints[UGENENDPOINT(dev)][IN];
|
||
|
sce_out = &sc->sc_endpoints[UGENENDPOINT(dev)][OUT];
|
||
|
edesc = (sce_in->edesc != NULL) ? sce_in->edesc : sce_out->edesc;
|
||
|
KASSERT(edesc != NULL, ("ugenpoll: NULL edesc"));
|
||
|
if (sce_in->edesc == NULL || sce_in->pipeh == NULL)
|
||
|
sce_in = NULL;
|
||
|
if (sce_out->edesc == NULL || sce_out->pipeh == NULL)
|
||
|
sce_out = NULL;
|
||
|
crit_enter();
|
||
|
switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
|
||
|
switch (edesc->bmAttributes & UE_XFERTYPE) {
|
||
|
case UE_INTERRUPT:
|
||
|
if (ap->a_events & (POLLIN | POLLRDNORM)) {
|
||
|
if (sce->q.c_cc > 0)
|
||
|
if (sce_in != NULL && (ap->a_events & (POLLIN | POLLRDNORM))) {
|
||
|
if (sce_in->q.c_cc > 0)
|
||
|
revents |= ap->a_events & (POLLIN | POLLRDNORM);
|
||
|
else
|
||
|
selrecord(curthread, &sce->rsel);
|
||
|
selrecord(curthread, &sce_in->rsel);
|
||
|
}
|
||
|
if (sce_out != NULL && (ap->a_events & (POLLOUT | POLLWRNORM))) {
|
||
|
if (sce_out->q.c_cc > 0)
|
||
|
revents |= ap->a_events & (POLLOUT | POLLWRNORM);
|
||
|
else
|
||
|
selrecord(curthread, &sce_out->rsel);
|
||
|
}
|
||
|
break;
|
||
|
case UE_ISOCHRONOUS:
|
||
|
if (ap->a_events & (POLLIN | POLLRDNORM)) {
|
||
|
if (sce->cur != sce->fill)
|
||
|
if (sce_in != NULL && (ap->a_events & (POLLIN | POLLRDNORM))) {
|
||
|
if (sce_in->cur != sce_in->fill)
|
||
|
revents |= ap->a_events & (POLLIN | POLLRDNORM);
|
||
|
else
|
||
|
selrecord(curthread, &sce->rsel);
|
||
|
selrecord(curthread, &sce_in->rsel);
|
||
|
}
|
||
|
if (sce_out != NULL && (ap->a_events & (POLLOUT | POLLWRNORM))) {
|
||
|
if (sce_out->cur != sce_out->fill)
|
||
|
revents |= ap->a_events & (POLLOUT | POLLWRNORM);
|
||
|
else
|
||
|
selrecord(curthread, &sce_out->rsel);
|
||
|
}
|
||
|
break;
|
||
|
case UE_BULK:
|
||