diff --git a/sys/dev/usb/uhub.c b/sys/dev/usb/uhub.c index 95421ca0eeb1..a4816145a761 100644 --- a/sys/dev/usb/uhub.c +++ b/sys/dev/usb/uhub.c @@ -1,4 +1,4 @@ -/* $NetBSD: uhub.c,v 1.48 2000/12/29 01:24:56 augustss Exp $ */ +/* $NetBSD: uhub.c,v 1.49 2001/01/21 19:00:06 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/uhub.c,v 1.18 1999/11/17 22:33:43 n_hibma Exp $ */ /* @@ -568,7 +568,7 @@ uhub_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) if (status != USBD_NORMAL_COMPLETION) usbd_clear_endpoint_stall_async(sc->sc_ipipe); - usb_needs_explore(sc->sc_hub->bus); + usb_needs_explore(sc->sc_hub); } #if defined(__FreeBSD__) diff --git a/sys/dev/usb/usb.c b/sys/dev/usb/usb.c index 9f0f345d4d61..57850976a179 100644 --- a/sys/dev/usb/usb.c +++ b/sys/dev/usb/usb.c @@ -1,4 +1,4 @@ -/* $NetBSD: usb.c,v 1.50 2001/01/21 16:55:11 augustss Exp $ */ +/* $NetBSD: usb.c,v 1.51 2001/01/21 19:00:06 augustss Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -93,14 +93,17 @@ struct usb_softc { usbd_bus_handle sc_bus; /* USB controller */ struct usbd_port sc_port; /* dummy port for root hub */ + SIMPLEQ_HEAD(, usb_task) sc_tasks; struct proc *sc_event_thread; + struct usb_task sc_exp_task; + char sc_dying; }; cdev_decl(usb); -Static usbd_status usb_discover(struct usb_softc *); +Static void usb_discover(void *); Static void usb_create_event_thread(void *); Static void usb_event_thread(void *); @@ -143,6 +146,10 @@ USB_ATTACH(usb) sc->sc_bus = aux; sc->sc_bus->usbctl = sc; sc->sc_port.power = USB_MAX_POWER; + SIMPLEQ_INIT(&sc->sc_tasks); + + sc->sc_exp_task.fun = usb_discover; + sc->sc_exp_task.arg = sc; usbrev = sc->sc_bus->usbrev; printf(": USB revision %s", usbrev_str[usbrev]); @@ -221,34 +228,52 @@ usb_create_event_thread(void *arg) } } +void +usb_add_task(usbd_device_handle dev, struct usb_task *task) +{ + struct usb_softc *sc = dev->bus->usbctl; + int s; + + s = splusb(); + if (!task->onqueue) { + DPRINTFN(2,("usb_add_task: sc=%p task=%p\n", sc, task)); + SIMPLEQ_INSERT_TAIL(&sc->sc_tasks, task, next); + task->onqueue = 1; + } else + DPRINTFN(3,("usb_add_task: sc=%p task=%p on q\n", sc, task)); + wakeup(&sc->sc_tasks); + splx(s); +} + void usb_event_thread(void *arg) { struct usb_softc *sc = arg; - int first = 1; + struct usb_task *task; + int s; DPRINTF(("usb_event_thread: start\n")); /* Make sure first discover does something. */ sc->sc_bus->needs_explore = 1; + usb_discover(sc); + config_pending_decr(); while (!sc->sc_dying) { -#ifdef USB_DEBUG - if (usb_noexplore < 2) -#endif - usb_discover(sc); - if (first) { - config_pending_decr(); - first = 0; + s = splusb(); + task = SIMPLEQ_FIRST(&sc->sc_tasks); + if (task == NULL) { + tsleep(&sc->sc_tasks, PWAIT, "usbevt", 0); + task = SIMPLEQ_FIRST(&sc->sc_tasks); } -#ifdef USB_DEBUG - (void)tsleep(&sc->sc_bus->needs_explore, PWAIT, "usbevt", - usb_noexplore ? 0 : hz * 60); -#else - (void)tsleep(&sc->sc_bus->needs_explore, PWAIT, "usbevt", - hz * 60); -#endif - DPRINTFN(2,("usb_event_thread: woke up\n")); + DPRINTFN(2,("usb_event_thread: woke up task=%p\n", task)); + if (task != NULL && !sc->sc_dying) { + SIMPLEQ_REMOVE_HEAD(&sc->sc_tasks, task, next); + task->onqueue = 0; + splx(s); + task->fun(task->arg); + } else + splx(s); } sc->sc_event_thread = NULL; @@ -482,9 +507,16 @@ usbpoll(dev_t dev, int events, struct proc *p) } /* Explore device tree from the root. */ -usbd_status -usb_discover(struct usb_softc *sc) +Static void +usb_discover(void *v) { + struct usb_softc *sc = v; + + DPRINTFN(2,("usb_discover\n")); +#ifdef USB_DEBUG + if (usb_noexplore > 1) + return; +#endif /* * We need mutual exclusion while traversing the device tree, * but this is guaranteed since this function is only called @@ -494,15 +526,14 @@ usb_discover(struct usb_softc *sc) sc->sc_bus->needs_explore = 0; sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub); } - - return (USBD_NORMAL_COMPLETION); } void -usb_needs_explore(usbd_bus_handle bus) +usb_needs_explore(usbd_device_handle dev) { - bus->needs_explore = 1; - wakeup(&bus->needs_explore); + DPRINTFN(2,("usb_needs_explore\n")); + dev->bus->needs_explore = 1; + usb_add_task(dev, &dev->bus->usbctl->sc_exp_task); } /* Called at splusb() */ @@ -628,7 +659,7 @@ usb_detach(device_ptr_t self, int flags) /* Kill off event thread. */ if (sc->sc_event_thread) { - wakeup(&sc->sc_bus->needs_explore); + wakeup(&sc->sc_tasks); if (tsleep(sc, PWAIT, "usbdet", hz * 60)) printf("%s: event thread didn't die\n", USBDEVNAME(sc->sc_dev)); diff --git a/sys/dev/usb/usbdi.h b/sys/dev/usb/usbdi.h index 00212b79f42e..36c3326bb76d 100644 --- a/sys/dev/usb/usbdi.h +++ b/sys/dev/usb/usbdi.h @@ -1,4 +1,4 @@ -/* $NetBSD: usbdi.h,v 1.47 2001/01/21 02:39:53 augustss Exp $ */ +/* $NetBSD: usbdi.h,v 1.48 2001/01/21 19:00:06 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usbdi.h,v 1.18 1999/11/17 22:33:49 n_hibma Exp $ */ /* @@ -178,6 +178,21 @@ usbd_status usbd_reload_device_desc(usbd_device_handle); int usbd_ratecheck(struct timeval *last); +/* + * The usb_task structs form a queue of things to run in the USB event + * thread. Normally this is just device discovery when a connect/disconnect + * has been detected. But it may also be used by drivers that need to + * perform (short) tasks that must have a process context. + */ +struct usb_task { + SIMPLEQ_ENTRY(usb_task) next; + void (*fun)(void *); + void *arg; + char onqueue; +}; + +void usb_add_task(usbd_device_handle dev, struct usb_task *task); + /* NetBSD attachment information */ /* Attach data */ diff --git a/sys/dev/usb/usbdivar.h b/sys/dev/usb/usbdivar.h index 92767018561f..099d3624b2e6 100644 --- a/sys/dev/usb/usbdivar.h +++ b/sys/dev/usb/usbdivar.h @@ -1,4 +1,4 @@ -/* $NetBSD: usbdivar.h,v 1.62 2001/01/21 02:39:53 augustss Exp $ */ +/* $NetBSD: usbdivar.h,v 1.63 2001/01/21 19:00:06 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/usbdivar.h,v 1.11 1999/11/17 22:33:51 n_hibma Exp $ */ /* @@ -248,7 +248,7 @@ void usb_transfer_complete(usbd_xfer_handle xfer); void usb_disconnect_port(struct usbd_port *up, device_ptr_t); /* Routines from usb.c */ -void usb_needs_explore(usbd_bus_handle); +void usb_needs_explore(usbd_device_handle); void usb_schedsoftintr(struct usbd_bus *); /*