--- /sys/dev/usb/ugen.c.orig Thu Dec 15 16:57:32 2005 +++ /sys/dev/usb/ugen.c Tue May 9 12:16:28 2006 @@ -1,4 +1,4 @@ -/* $NetBSD: ugen.c,v 1.59 2002/07/11 21:14:28 augustss Exp $ */ +/* $NetBSD: ugen.c,v 1.79 2006/03/01 12:38:13 yamt Exp $ */ /* Also already merged from NetBSD: * $NetBSD: ugen.c,v 1.61 2002/09/23 05:51:20 simonb Exp $ @@ -284,6 +284,9 @@ ugen_make_devnodes(sc); #endif + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + USB_ATTACH_SUCCESS_RETURN; } @@ -383,6 +386,7 @@ M_WAITOK); niface_cache = niface; + memset(sc->sc_endpoints, 0, sizeof sc->sc_endpoints); for (ifaceno = 0; ifaceno < niface; ifaceno++) { DPRINTFN(1,("ugen_set_config: ifaceno %d\n", ifaceno)); err = usbd_device2interface_handle(dev, ifaceno, &iface); @@ -511,7 +515,7 @@ for (dir = OUT; dir <= IN; dir++) { if (flag & (dir == OUT ? FWRITE : FREAD)) { sce = &sc->sc_endpoints[endpt][dir]; - if (sce->edesc == 0) + if (sce == 0 ||sce->edesc == 0) return (ENXIO); } } @@ -650,7 +654,7 @@ if (!(flag & (dir == OUT ? FWRITE : FREAD))) continue; sce = &sc->sc_endpoints[endpt][dir]; - if (sce->pipeh == NULL) + if (sce == NULL || sce->pipeh == NULL) continue; DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n", endpt, dir, sce)); @@ -835,6 +839,9 @@ USB_GET_SC(ugen, UGENUNIT(dev), sc); + if(sc->sc_dying) + return (EIO); + UGEN_DEV_REF(dev, sc); error = ugen_do_read(sc, endpt, uio, flag); UGEN_DEV_RELE(dev, sc); @@ -933,6 +940,9 @@ USB_GET_SC(ugen, UGENUNIT(dev), sc); + if (sc->sc_dying) + return (EIO); + UGEN_DEV_REF(dev, sc); error = ugen_do_write(sc, endpt, uio, flag); UGEN_DEV_RELE(dev, sc); @@ -969,8 +979,31 @@ return; USB_GET_SC(ugen, UGENUNIT(dev), sc); sce = &sc->sc_endpoints[endpt][IN]; - if (sce->pipeh) - usbd_abort_pipe(sce->pipeh); + if (sce) + { + if(sce->pipeh) + usbd_abort_pipe(sce->pipeh); + + if (sce->state & UGEN_ASLP) { + sce->state &= ~UGEN_ASLP; + DPRINTFN(5, ("ugenpurge: waking %p\n", sce)); + wakeup(sce); + } + selwakeuppri(&sce->rsel, PZERO); + } + sce = &sc->sc_endpoints[endpt][OUT]; + if (sce) + { + if(sce->pipeh) + usbd_abort_pipe(sce->pipeh); + + if (sce->state & UGEN_ASLP) { + sce->state &= ~UGEN_ASLP; + DPRINTFN(5, ("ugenpurge: waking %p\n", sce)); + wakeup(sce); + } + selwakeuppri(&sce->rsel, PZERO); + } } #endif @@ -994,11 +1027,13 @@ for (i = 0; i < USB_MAX_ENDPOINTS; i++) { for (dir = OUT; dir <= IN; dir++) { sce = &sc->sc_endpoints[i][dir]; - if (sce->pipeh) + if (sce && sce->pipeh) usbd_abort_pipe(sce->pipeh); + selwakeuppri(&sce->rsel, PZERO); } } + #if defined(__NetBSD__) || defined(__OpenBSD__) s = splusb(); if (sc->sc_refcnt > 0) { @@ -1035,6 +1070,9 @@ destroy_dev(sc->dev); #endif + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, + USBDEV(sc->sc_dev)); + return (0); } @@ -1292,7 +1330,7 @@ /* This flag only affects read */ sce = &sc->sc_endpoints[endpt][IN]; - if (sce->pipeh == NULL) { + if (sce == NULL || sce->pipeh == NULL) { printf("ugenioctl: USB_SET_SHORT_XFER, no pipe\n"); return (EIO); } @@ -1304,6 +1342,12 @@ return (0); case USB_SET_TIMEOUT: sce = &sc->sc_endpoints[endpt][IN]; + if (sce == NULL + /* XXX this shouldn't happen, but the distinction between + input and output pipes isn't clear enough. + || sce->pipeh == NULL */ + ) + return (EINVAL); sce->timeout = *(int *)addr; return (0); default: @@ -1331,9 +1375,6 @@ err = ugen_set_config(sc, *(int *)addr); switch (err) { case USBD_NORMAL_COMPLETION: -#if defined(__FreeBSD__) - ugen_make_devnodes(sc); -#endif break; case USBD_IN_USE: return (EBUSY); @@ -1541,6 +1582,9 @@ USB_GET_SC(ugen, UGENUNIT(dev), sc); + if (sc->sc_dying) + return (EIO); + UGEN_DEV_REF(dev, sc); error = ugen_do_ioctl(sc, endpt, cmd, addr, flag, p); UGEN_DEV_RELE(dev, sc); @@ -1555,14 +1599,32 @@ int revents = 0; int s; + /* Do not allow to poll a control endpoint */ + if ( UGENENDPOINT(dev) == USB_CONTROL_ENDPOINT ) + return (EIO); + USB_GET_SC(ugen, UGENUNIT(dev), sc); if (sc->sc_dying) return (EIO); - /* XXX always IN */ - sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN]; -#ifdef DIAGNOSTIC + if((events & POLLIN) && (events & POLLOUT)) { + printf("ugenpoll: POLLIN and POLLOUT? We're not handling it, so bail.\n"); + return (EIO); + } + + if(events & (POLLIN | POLLRDNORM)) + sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN]; + else if(events & (POLLOUT | POLLWRNORM)) + sce = &sc->sc_endpoints[UGENENDPOINT(dev)][OUT]; + else { + printf("ugenpoll: unhandled input event\n"); + return (EIO); + } + + if (sce == NULL) + return (EIO); + if (!sce->edesc) { printf("ugenpoll: no edesc\n"); return (EIO); @@ -1571,23 +1633,26 @@ printf("ugenpoll: no pipe\n"); return (EIO); } -#endif s = splusb(); switch (sce->edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: - if (events & (POLLIN | POLLRDNORM)) { - if (sce->q.c_cc > 0) + if (sce->q.c_cc > 0) { + if (events & (POLLIN | POLLRDNORM)) revents |= events & (POLLIN | POLLRDNORM); - else - selrecord(p, &sce->rsel); + else if (events & (POLLOUT | POLLWRNORM)) + revents |= events & (POLLOUT | POLLWRNORM); + } else { + selrecord(p, &sce->rsel); } break; case UE_ISOCHRONOUS: - if (events & (POLLIN | POLLRDNORM)) { - if (sce->cur != sce->fill) + if (sce->cur != sce->fill) { + if (events & (POLLIN | POLLRDNORM)) revents |= events & (POLLIN | POLLRDNORM); - else - selrecord(p, &sce->rsel); + else if (events & (POLLOUT | POLLWRNORM)) + revents |= events & (POLLOUT | POLLWRNORM); + } else { + selrecord(p, &sce->rsel); } break; case UE_BULK: