mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-19 10:53:58 +00:00
Fix a number of cases where ugen would panic, especially when the
device went away while open or if you tried to change the config number while devices were open. Based on the patch from the PR with a number of changes as discussed with the submitter. PR: usb/97271 Submitted by: Anish Mistry
This commit is contained in:
parent
b6cd2d9e08
commit
c62502d1f6
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=159267
@ -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 @@ USB_ATTACH(ugen)
|
||||
ugen_make_devnodes(sc);
|
||||
#endif
|
||||
|
||||
usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
|
||||
USBDEV(sc->sc_dev));
|
||||
|
||||
USB_ATTACH_SUCCESS_RETURN;
|
||||
}
|
||||
|
||||
@ -322,9 +325,11 @@ ugen_make_devnodes(struct ugen_softc *sc)
|
||||
Static void
|
||||
ugen_destroy_devnodes(struct ugen_softc *sc)
|
||||
{
|
||||
int endptno;
|
||||
int endptno, prev_sc_dying;
|
||||
struct cdev *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++) {
|
||||
if (sc->sc_endpoints[endptno][IN].sc != NULL ||
|
||||
@ -341,9 +346,17 @@ ugen_destroy_devnodes(struct ugen_softc *sc)
|
||||
dev = sc->sc_endpoints[endptno][IN].dev;
|
||||
else
|
||||
dev = sc->sc_endpoints[endptno][OUT].dev;
|
||||
destroy_dev(dev);
|
||||
|
||||
KASSERT(dev != NULL,
|
||||
("ugen_destroy_devnodes: NULL dev"));
|
||||
if(dev != NULL)
|
||||
destroy_dev(dev);
|
||||
|
||||
sc->sc_endpoints[endptno][IN].sc = NULL;
|
||||
sc->sc_endpoints[endptno][OUT].sc = NULL;
|
||||
}
|
||||
}
|
||||
sc->sc_dying = prev_sc_dying;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -378,9 +391,10 @@ ugen_set_config(struct ugen_softc *sc, int configno)
|
||||
return (err);
|
||||
/* store an array of endpoint descriptors to clear if the configuration
|
||||
* change succeeds - these aren't available afterwards */
|
||||
nendpt_cache = malloc(sizeof(u_int8_t) * niface, M_TEMP, M_WAITOK);
|
||||
nendpt_cache = malloc(sizeof(u_int8_t) * niface, M_TEMP, M_WAITOK |
|
||||
M_ZERO);
|
||||
sce_cache_arr = malloc(sizeof(struct ugen_endpoint **) * niface, M_TEMP,
|
||||
M_WAITOK);
|
||||
M_WAITOK | M_ZERO);
|
||||
niface_cache = niface;
|
||||
|
||||
for (ifaceno = 0; ifaceno < niface; ifaceno++) {
|
||||
@ -727,13 +741,12 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct uio *uio, int flag)
|
||||
sce->state |= UGEN_ASLP;
|
||||
DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
|
||||
error = tsleep(sce, PZERO | PCATCH, "ugenri", 0);
|
||||
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)
|
||||
break;
|
||||
}
|
||||
}
|
||||
splx(s);
|
||||
|
||||
@ -793,13 +806,12 @@ ugen_do_read(struct ugen_softc *sc, int endpt, struct uio *uio, int flag)
|
||||
sce->state |= UGEN_ASLP;
|
||||
DPRINTFN(5, ("ugenread: sleep on %p\n", sce));
|
||||
error = tsleep(sce, PZERO | PCATCH, "ugenri", 0);
|
||||
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)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (sce->cur != sce->fill && uio->uio_resid > 0 && !error) {
|
||||
@ -837,6 +849,9 @@ ugenread(struct cdev *dev, struct uio *uio, int flag)
|
||||
|
||||
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);
|
||||
@ -938,6 +953,9 @@ ugenwrite(struct cdev *dev, struct uio *uio, int flag)
|
||||
|
||||
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);
|
||||
@ -976,6 +994,20 @@ ugenpurge(struct cdev *dev)
|
||||
sce = &sc->sc_endpoints[endpt][IN];
|
||||
if (sce->pipeh)
|
||||
usbd_abort_pipe(sce->pipeh);
|
||||
if (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->pipeh)
|
||||
usbd_abort_pipe(sce->pipeh);
|
||||
if (sce->state & UGEN_ASLP) {
|
||||
DPRINTFN(5, ("ugenpurge: waking %p\n", sce));
|
||||
wakeup(sce);
|
||||
}
|
||||
selwakeuppri(&sce->rsel, PZERO);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1001,6 +1033,7 @@ USB_DETACH(ugen)
|
||||
sce = &sc->sc_endpoints[i][dir];
|
||||
if (sce->pipeh)
|
||||
usbd_abort_pipe(sce->pipeh);
|
||||
selwakeuppri(&sce->rsel, PZERO);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1040,6 +1073,9 @@ USB_DETACH(ugen)
|
||||
destroy_dev(sc->dev);
|
||||
#endif
|
||||
|
||||
usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
|
||||
USBDEV(sc->sc_dev));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -1548,6 +1584,9 @@ ugenioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p)
|
||||
|
||||
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);
|
||||
@ -1558,43 +1597,57 @@ int
|
||||
ugenpoll(struct cdev *dev, int events, usb_proc_ptr p)
|
||||
{
|
||||
struct ugen_softc *sc;
|
||||
struct ugen_endpoint *sce;
|
||||
struct ugen_endpoint *sce_in, *sce_out;
|
||||
usb_endpoint_descriptor_t *edesc;
|
||||
int revents = 0;
|
||||
int s;
|
||||
|
||||
USB_GET_SC(ugen, UGENUNIT(dev), sc);
|
||||
|
||||
if (sc->sc_dying)
|
||||
return (EIO);
|
||||
return ((events & (POLLIN | POLLOUT | POLLRDNORM |
|
||||
POLLWRNORM)) | POLLHUP);
|
||||
/* Do not allow to poll a control endpoint */
|
||||
if (UGENENDPOINT(dev) == USB_CONTROL_ENDPOINT)
|
||||
return (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;
|
||||
|
||||
/* XXX always IN */
|
||||
sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN];
|
||||
#ifdef DIAGNOSTIC
|
||||
if (!sce->edesc) {
|
||||
printf("ugenpoll: no edesc\n");
|
||||
return (EIO);
|
||||
}
|
||||
if (!sce->pipeh) {
|
||||
printf("ugenpoll: no pipe\n");
|
||||
return (EIO);
|
||||
}
|
||||
#endif
|
||||
s = splusb();
|
||||
switch (sce->edesc->bmAttributes & UE_XFERTYPE) {
|
||||
switch (edesc->bmAttributes & UE_XFERTYPE) {
|
||||
case UE_INTERRUPT:
|
||||
if (events & (POLLIN | POLLRDNORM)) {
|
||||
if (sce->q.c_cc > 0)
|
||||
if (sce_in != NULL && (events & (POLLIN | POLLRDNORM))) {
|
||||
if (sce_in->q.c_cc > 0)
|
||||
revents |= events & (POLLIN | POLLRDNORM);
|
||||
else
|
||||
selrecord(p, &sce->rsel);
|
||||
selrecord(p, &sce_in->rsel);
|
||||
}
|
||||
if (sce_out != NULL && (events & (POLLOUT | POLLWRNORM))) {
|
||||
if (sce_out->q.c_cc > 0)
|
||||
revents |= events & (POLLOUT | POLLWRNORM);
|
||||
else
|
||||
selrecord(p, &sce_out->rsel);
|
||||
}
|
||||
break;
|
||||
case UE_ISOCHRONOUS:
|
||||
if (events & (POLLIN | POLLRDNORM)) {
|
||||
if (sce->cur != sce->fill)
|
||||
if (sce_in != NULL && (events & (POLLIN | POLLRDNORM))) {
|
||||
if (sce_in->cur != sce_in->fill)
|
||||
revents |= events & (POLLIN | POLLRDNORM);
|
||||
else
|
||||
selrecord(p, &sce->rsel);
|
||||
selrecord(p, &sce_in->rsel);
|
||||
}
|
||||
if (sce_out != NULL && (events & (POLLOUT | POLLWRNORM))) {
|
||||
if (sce_out->cur != sce_out->fill)
|
||||
revents |= events & (POLLOUT | POLLWRNORM);
|
||||
else
|
||||
selrecord(p, &sce_out->rsel);
|
||||
}
|
||||
break;
|
||||
case UE_BULK:
|
||||
|
Loading…
Reference in New Issue
Block a user