| Commit | Line | Data |
|---|---|---|
| e937bfce MD |
1 | /* |
| 2 | * $NetBSD: usb.c,v 1.68 2002/02/20 20:30:12 christos Exp $ | |
| 3 | * $FreeBSD: src/sys/dev/usb/usb.c,v 1.106 2005/03/27 15:31:23 iedowse Exp $ | |
| 6e46c965 | 4 | * $DragonFly: src/sys/bus/usb/usb.c,v 1.50 2008/09/26 08:21:22 hasso Exp $ |
| e937bfce | 5 | */ |
| 1550dfd9 MD |
6 | |
| 7 | /* Also already merged from NetBSD: | |
| 8 | * $NetBSD: usb.c,v 1.70 2002/05/09 21:54:32 augustss Exp $ | |
| 9 | * $NetBSD: usb.c,v 1.71 2002/06/01 23:51:04 lukem Exp $ | |
| 10 | * $NetBSD: usb.c,v 1.73 2002/09/23 05:51:19 simonb Exp $ | |
| 281cf3c2 | 11 | * $NetBSD: usb.c,v 1.80 2003/11/07 17:03:25 wiz Exp $ |
| 1550dfd9 | 12 | */ |
| 984263bc MD |
13 | |
| 14 | /* | |
| 15 | * Copyright (c) 1998 The NetBSD Foundation, Inc. | |
| 16 | * All rights reserved. | |
| 17 | * | |
| 18 | * This code is derived from software contributed to The NetBSD Foundation | |
| 19 | * by Lennart Augustsson (lennart@augustsson.net) at | |
| 20 | * Carlstedt Research & Technology. | |
| 21 | * | |
| 22 | * Redistribution and use in source and binary forms, with or without | |
| 23 | * modification, are permitted provided that the following conditions | |
| 24 | * are met: | |
| 25 | * 1. Redistributions of source code must retain the above copyright | |
| 26 | * notice, this list of conditions and the following disclaimer. | |
| 27 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 28 | * notice, this list of conditions and the following disclaimer in the | |
| 29 | * documentation and/or other materials provided with the distribution. | |
| 30 | * 3. All advertising materials mentioning features or use of this software | |
| 31 | * must display the following acknowledgement: | |
| 32 | * This product includes software developed by the NetBSD | |
| 33 | * Foundation, Inc. and its contributors. | |
| 34 | * 4. Neither the name of The NetBSD Foundation nor the names of its | |
| 35 | * contributors may be used to endorse or promote products derived | |
| 36 | * from this software without specific prior written permission. | |
| 37 | * | |
| 38 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS | |
| 39 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |
| 40 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| 41 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | |
| 42 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| 43 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| 44 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| 45 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| 46 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| 47 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
| 48 | * POSSIBILITY OF SUCH DAMAGE. | |
| 49 | */ | |
| 50 | ||
| 51 | /* | |
| 52 | * USB specifications and other documentation can be found at | |
| 281cf3c2 SZ |
53 | * http://www.usb.org/developers/docs/ and |
| 54 | * http://www.usb.org/developers/devclass_docs/ | |
| 984263bc MD |
55 | */ |
| 56 | ||
| 57 | #include <sys/param.h> | |
| 58 | #include <sys/systm.h> | |
| 59 | #include <sys/kernel.h> | |
| 1550dfd9 | 60 | #include <sys/lock.h> |
| 984263bc | 61 | #include <sys/malloc.h> |
| 1550dfd9 | 62 | #include <sys/unistd.h> |
| 984263bc MD |
63 | #include <sys/module.h> |
| 64 | #include <sys/bus.h> | |
| 281cf3c2 | 65 | #include <sys/fcntl.h> |
| 984263bc MD |
66 | #include <sys/filio.h> |
| 67 | #include <sys/uio.h> | |
| 1550dfd9 MD |
68 | #include <sys/kthread.h> |
| 69 | #include <sys/proc.h> | |
| 984263bc | 70 | #include <sys/conf.h> |
| fef8985e | 71 | #include <sys/device.h> |
| 984263bc MD |
72 | #include <sys/poll.h> |
| 73 | #include <sys/select.h> | |
| 74 | #include <sys/vnode.h> | |
| 75 | #include <sys/signalvar.h> | |
| 76 | #include <sys/sysctl.h> | |
| 4e01b467 | 77 | #include <sys/thread2.h> |
| 984263bc | 78 | |
| 281cf3c2 SZ |
79 | #include <bus/usb/usb.h> |
| 80 | #include <bus/usb/usbdi.h> | |
| 81 | #include <bus/usb/usbdi_util.h> | |
| 984263bc MD |
82 | |
| 83 | #define USBUNIT(d) (minor(d)) /* usb_discover device nodes, kthread */ | |
| 84 | #define USB_DEV_MINOR 255 /* event queue device */ | |
| 85 | ||
| 984263bc MD |
86 | MALLOC_DEFINE(M_USB, "USB", "USB"); |
| 87 | MALLOC_DEFINE(M_USBDEV, "USBdev", "USB device"); | |
| 88 | MALLOC_DEFINE(M_USBHC, "USBHC", "USB host controller"); | |
| 89 | ||
| 90 | #include "usb_if.h" | |
| 984263bc | 91 | |
| 281cf3c2 SZ |
92 | #include <bus/usb/usbdivar.h> |
| 93 | #include <bus/usb/usb_quirks.h> | |
| 984263bc MD |
94 | |
| 95 | /* Define this unconditionally in case a kernel module is loaded that | |
| 96 | * has been compiled with debugging options. | |
| 97 | */ | |
| 98 | SYSCTL_NODE(_hw, OID_AUTO, usb, CTLFLAG_RW, 0, "USB debugging"); | |
| 99 | ||
| eabfb7a3 MN |
100 | /* |
| 101 | * XXX: This is a hack! If your USB keyboard doesn't work | |
| 102 | * early at boot, try setting this tunable to 0 from | |
| 103 | * bootleader: | |
| 104 | * | |
| 105 | * set hw.usb.hack_defer_exploration=0 | |
| 106 | */ | |
| 107 | static int hack_defer_exploration = 1; | |
| 108 | ||
| 984263bc | 109 | #ifdef USB_DEBUG |
| fc1ef497 HT |
110 | #define DPRINTF(x) if (usbdebug) kprintf x |
| 111 | #define DPRINTFN(n,x) if (usbdebug>(n)) kprintf x | |
| 984263bc MD |
112 | int usbdebug = 0; |
| 113 | SYSCTL_INT(_hw_usb, OID_AUTO, debug, CTLFLAG_RW, | |
| 114 | &usbdebug, 0, "usb debug level"); | |
| eabfb7a3 | 115 | |
| 1550dfd9 | 116 | /* |
| 984263bc MD |
117 | * 0 - do usual exploration |
| 118 | * 1 - do not use timeout exploration | |
| 119 | * >1 - do no exploration | |
| 120 | */ | |
| 121 | int usb_noexplore = 0; | |
| 122 | #else | |
| 123 | #define DPRINTF(x) | |
| 124 | #define DPRINTFN(n,x) | |
| 125 | #endif | |
| 126 | ||
| 127 | struct usb_softc { | |
| 281cf3c2 | 128 | cdev_t sc_usbdev; |
| 5d0fb0e6 | 129 | TAILQ_ENTRY(usb_softc) sc_coldexplist; /* cold needs-explore list */ |
| 984263bc MD |
130 | usbd_bus_handle sc_bus; /* USB controller */ |
| 131 | struct usbd_port sc_port; /* dummy port for root hub */ | |
| 132 | ||
| 1550dfd9 | 133 | struct thread *sc_event_thread; |
| 984263bc MD |
134 | |
| 135 | char sc_dying; | |
| 136 | }; | |
| 137 | ||
| 281cf3c2 SZ |
138 | struct usb_taskq { |
| 139 | TAILQ_HEAD(, usb_task) tasks; | |
| 375a9af6 | 140 | struct thread *task_thread_proc; |
| 281cf3c2 SZ |
141 | const char *name; |
| 142 | int taskcreated; /* task thread exists. */ | |
| 143 | }; | |
| 144 | static struct usb_taskq usb_taskq[USB_NUM_TASKQS]; | |
| 1550dfd9 | 145 | |
| 1550dfd9 | 146 | d_open_t usbopen; |
| 984263bc MD |
147 | d_close_t usbclose; |
| 148 | d_read_t usbread; | |
| 149 | d_ioctl_t usbioctl; | |
| fef8985e MD |
150 | d_poll_t usbpoll; |
| 151 | ||
| 152 | struct dev_ops usb_ops = { | |
| 153 | { "usb", USB_CDEV_MAJOR, 0 }, | |
| 154 | .d_open = usbopen, | |
| 155 | .d_close = usbclose, | |
| 156 | .d_read = usbread, | |
| 157 | .d_ioctl = usbioctl, | |
| 158 | .d_poll = usbpoll, | |
| 984263bc | 159 | }; |
| 984263bc | 160 | |
| b9bd1250 | 161 | static void usb_discover(device_t); |
| 6ed427ca | 162 | static bus_child_detached_t usb_child_detached; |
| b9bd1250 | 163 | static void usb_create_event_thread(device_t); |
| 6ed427ca HT |
164 | static void usb_event_thread(void *); |
| 165 | static void usb_task_thread(void *); | |
| 281cf3c2 | 166 | |
| 6ed427ca | 167 | static cdev_t usb_dev; /* The /dev/usb device. */ |
| 4b83f092 | 168 | static int usb_ndevs; /* Number of /dev/usbN devices. */ |
| 5d0fb0e6 MN |
169 | /* Busses to explore at the end of boot-time device configuration */ |
| 170 | static TAILQ_HEAD(, usb_softc) usb_coldexplist = | |
| 171 | TAILQ_HEAD_INITIALIZER(usb_coldexplist); | |
| 172 | ||
| 1550dfd9 | 173 | #define USB_MAX_EVENTS 100 |
| 984263bc MD |
174 | struct usb_event_q { |
| 175 | struct usb_event ue; | |
| 1550dfd9 | 176 | TAILQ_ENTRY(usb_event_q) next; |
| 984263bc | 177 | }; |
| 6ed427ca | 178 | static TAILQ_HEAD(, usb_event_q) usb_events = |
| 1550dfd9 | 179 | TAILQ_HEAD_INITIALIZER(usb_events); |
| 6ed427ca HT |
180 | static int usb_nevents = 0; |
| 181 | static struct selinfo usb_selevent; | |
| 182 | static struct proc *usb_async_proc; /* process that wants USB SIGIO */ | |
| 183 | static int usb_dev_open = 0; | |
| 184 | static void usb_add_event(int, struct usb_event *); | |
| 984263bc | 185 | |
| 6ed427ca | 186 | static int usb_get_next_event(struct usb_event *); |
| 984263bc | 187 | |
| 6ed427ca | 188 | static const char *usbrev_str[] = USBREV_STR; |
| 984263bc | 189 | |
| 61b69189 HT |
190 | static device_probe_t usb_match; |
| 191 | static device_attach_t usb_attach; | |
| 192 | static device_detach_t usb_detach; | |
| 193 | ||
| 194 | static devclass_t usb_devclass; | |
| 195 | ||
| 196 | static kobj_method_t usb_methods[] = { | |
| 197 | DEVMETHOD(device_probe, usb_match), | |
| 198 | DEVMETHOD(device_attach, usb_attach), | |
| 199 | DEVMETHOD(device_detach, usb_detach), | |
| 200 | DEVMETHOD(bus_child_detached, usb_child_detached), | |
| 201 | DEVMETHOD(device_suspend, bus_generic_suspend), | |
| 202 | DEVMETHOD(device_resume, bus_generic_resume), | |
| 203 | DEVMETHOD(device_shutdown, bus_generic_shutdown), | |
| 204 | {0,0} | |
| 205 | }; | |
| 206 | ||
| 207 | static driver_t usb_driver = { | |
| 208 | "usb", | |
| 209 | usb_methods, | |
| 210 | sizeof(struct usb_softc) | |
| 211 | }; | |
| 984263bc | 212 | |
| 61b69189 | 213 | MODULE_DEPEND(usb, usb, 1, 1, 1); |
| 1550dfd9 | 214 | MODULE_VERSION(usb, 1); |
| 1550dfd9 | 215 | |
| e785a5d9 HT |
216 | static int |
| 217 | usb_match(device_t self) | |
| 984263bc | 218 | { |
| 4b83f092 | 219 | DPRINTF(("usb_match\n")); |
| 984263bc MD |
220 | return (UMATCH_GENERIC); |
| 221 | } | |
| 222 | ||
| e785a5d9 HT |
223 | static int |
| 224 | usb_attach(device_t self) | |
| 984263bc | 225 | { |
| 984263bc MD |
226 | struct usb_softc *sc = device_get_softc(self); |
| 227 | void *aux = device_get_ivars(self); | |
| 281cf3c2 | 228 | cdev_t tmp_dev; |
| 984263bc MD |
229 | usbd_device_handle dev; |
| 230 | usbd_status err; | |
| 231 | int usbrev; | |
| 1550dfd9 MD |
232 | int speed; |
| 233 | struct usb_event ue; | |
| 984263bc | 234 | |
| eabfb7a3 MN |
235 | TUNABLE_INT_FETCH("hw.usb.hack_defer_exploration", |
| 236 | &hack_defer_exploration); | |
| 237 | ||
| 4b83f092 | 238 | DPRINTF(("usb_attach\n")); |
| 984263bc MD |
239 | |
| 240 | usbd_init(); | |
| 241 | sc->sc_bus = aux; | |
| 242 | sc->sc_bus->usbctl = sc; | |
| 243 | sc->sc_port.power = USB_MAX_POWER; | |
| 244 | ||
| 984263bc | 245 | usbrev = sc->sc_bus->usbrev; |
| b9bd1250 | 246 | device_printf(self, "USB revision %s", usbrev_str[usbrev]); |
| 1550dfd9 MD |
247 | switch (usbrev) { |
| 248 | case USBREV_1_0: | |
| 249 | case USBREV_1_1: | |
| 250 | speed = USB_SPEED_FULL; | |
| 251 | break; | |
| 252 | case USBREV_2_0: | |
| 253 | speed = USB_SPEED_HIGH; | |
| 254 | break; | |
| 255 | default: | |
| 85f8e2ea | 256 | kprintf(", not supported\n"); |
| 1550dfd9 | 257 | sc->sc_dying = 1; |
| e785a5d9 | 258 | return ENXIO; |
| 984263bc | 259 | } |
| 85f8e2ea | 260 | kprintf("\n"); |
| 984263bc | 261 | |
| 1550dfd9 | 262 | /* Make sure not to use tsleep() if we are cold booting. */ |
| eabfb7a3 | 263 | if (hack_defer_exploration && cold) |
| 1550dfd9 MD |
264 | sc->sc_bus->use_polling++; |
| 265 | ||
| b9bd1250 | 266 | ue.u.ue_ctrlr.ue_bus = device_get_unit(self); |
| 1550dfd9 MD |
267 | usb_add_event(USB_EVENT_CTRLR_ATTACH, &ue); |
| 268 | ||
| 269 | #ifdef USB_USE_SOFTINTR | |
| 936f1896 | 270 | callout_init(&sc->sc_bus->softi); |
| 1550dfd9 | 271 | #endif |
| 1550dfd9 | 272 | |
| 76ad0d7d | 273 | err = usbd_new_device(self, sc->sc_bus, 0, speed, 0, &sc->sc_port); |
| 984263bc MD |
274 | if (!err) { |
| 275 | dev = sc->sc_port.device; | |
| 276 | if (dev->hub == NULL) { | |
| 277 | sc->sc_dying = 1; | |
| b9bd1250 | 278 | device_printf(self, |
| 720b8fbe | 279 | "root device is not a hub\n"); |
| e785a5d9 | 280 | return ENXIO; |
| 984263bc MD |
281 | } |
| 282 | sc->sc_bus->root_hub = dev; | |
| 283 | #if 1 | |
| 1550dfd9 | 284 | /* |
| 984263bc MD |
285 | * Turning this code off will delay attachment of USB devices |
| 286 | * until the USB event thread is running, which means that | |
| 287 | * the keyboard will not work until after cold boot. | |
| 288 | */ | |
| 281cf3c2 | 289 | if (cold) { |
| eabfb7a3 MN |
290 | if (hack_defer_exploration) { |
| 291 | /* Explore high-speed busses before others. */ | |
| 292 | if (speed == USB_SPEED_HIGH) | |
| 293 | dev->hub->explore(sc->sc_bus->root_hub); | |
| 294 | else | |
| 295 | TAILQ_INSERT_TAIL(&usb_coldexplist, sc, | |
| 296 | sc_coldexplist); | |
| 297 | } else { | |
| 298 | /* | |
| 299 | * XXX Exploring high speed devices here will | |
| 300 | * hang the system. | |
| 301 | */ | |
| 302 | if (speed != USB_SPEED_HIGH) | |
| 303 | dev->hub->explore(sc->sc_bus->root_hub); | |
| 304 | } | |
| 281cf3c2 | 305 | } |
| 281cf3c2 | 306 | #endif |
| 984263bc | 307 | } else { |
| b9bd1250 | 308 | device_printf(self, |
| 720b8fbe | 309 | "root hub problem, error=%d\n", err); |
| 984263bc MD |
310 | sc->sc_dying = 1; |
| 311 | } | |
| eabfb7a3 | 312 | if (hack_defer_exploration && cold) |
| 1550dfd9 | 313 | sc->sc_bus->use_polling--; |
| 984263bc | 314 | |
| b9bd1250 | 315 | usb_create_event_thread(self); |
| 3e82b46c MD |
316 | |
| 317 | /* | |
| 318 | * The per controller devices (used for usb_discover) | |
| 319 | * XXX This is redundant now, but old usbd's will want it | |
| 320 | */ | |
| 281cf3c2 SZ |
321 | tmp_dev = make_dev(&usb_ops, device_get_unit(self), |
| 322 | UID_ROOT, GID_OPERATOR, 0660, | |
| 323 | "usb%d", device_get_unit(self)); | |
| 324 | sc->sc_usbdev = reference_dev(tmp_dev); | |
| 325 | if (usb_ndevs++ == 0) { | |
| 984263bc | 326 | /* The device spitting out events */ |
| 281cf3c2 SZ |
327 | tmp_dev = make_dev(&usb_ops, USB_DEV_MINOR, |
| 328 | UID_ROOT, GID_OPERATOR, 0660, "usb"); | |
| 329 | usb_dev = reference_dev(tmp_dev); | |
| 984263bc | 330 | } |
| 984263bc | 331 | |
| e785a5d9 | 332 | return 0; |
| 984263bc MD |
333 | } |
| 334 | ||
| 6ed427ca | 335 | static const char *taskq_names[] = USB_TASKQ_NAMES; |
| 281cf3c2 | 336 | |
| 984263bc | 337 | void |
| b9bd1250 | 338 | usb_create_event_thread(device_t self) |
| 984263bc | 339 | { |
| b9bd1250 | 340 | struct usb_softc *sc = device_get_softc(self); |
| 281cf3c2 | 341 | int i; |
| 984263bc | 342 | |
| b9bd1250 MN |
343 | if (kthread_create(usb_event_thread, self, &sc->sc_event_thread, |
| 344 | "%s", device_get_nameunit(self))) { | |
| 345 | device_printf(self, | |
| 720b8fbe | 346 | "unable to create event thread for\n"); |
| 623c4956 MD |
347 | panic("usb_create_event_thread"); |
| 348 | } | |
| 349 | ||
| 281cf3c2 SZ |
350 | for (i = 0; i < USB_NUM_TASKQS; i++) { |
| 351 | struct usb_taskq *taskq = &usb_taskq[i]; | |
| 352 | ||
| 353 | if (taskq->taskcreated == 0) { | |
| 354 | taskq->taskcreated = 1; | |
| 355 | taskq->name = taskq_names[i]; | |
| 356 | TAILQ_INIT(&taskq->tasks); | |
| 67743126 | 357 | if (kthread_create(usb_task_thread, taskq, |
| 281cf3c2 | 358 | &taskq->task_thread_proc, taskq->name)) { |
| 85f8e2ea | 359 | kprintf("unable to create task thread\n"); |
| 281cf3c2 SZ |
360 | panic("usb_create_event_thread task"); |
| 361 | } | |
| 1550dfd9 MD |
362 | } |
| 363 | } | |
| 364 | } | |
| 365 | ||
| 366 | /* | |
| 367 | * Add a task to be performed by the task thread. This function can be | |
| 368 | * called from any context and the task will be executed in a process | |
| 369 | * context ASAP. | |
| 370 | */ | |
| 371 | void | |
| 281cf3c2 | 372 | usb_add_task(usbd_device_handle dev, struct usb_task *task, int queue) |
| 1550dfd9 | 373 | { |
| 281cf3c2 SZ |
374 | struct usb_taskq *taskq; |
| 375 | ||
| 4e01b467 | 376 | crit_enter(); |
| 281cf3c2 SZ |
377 | |
| 378 | taskq = &usb_taskq[queue]; | |
| 379 | if (task->queue == -1) { | |
| 1550dfd9 | 380 | DPRINTFN(2,("usb_add_task: task=%p\n", task)); |
| 281cf3c2 SZ |
381 | TAILQ_INSERT_TAIL(&taskq->tasks, task, next); |
| 382 | task->queue = queue; | |
| 1550dfd9 MD |
383 | } else { |
| 384 | DPRINTFN(3,("usb_add_task: task=%p on q\n", task)); | |
| 385 | } | |
| 1ea0c479 | 386 | wakeup(&taskq->tasks); |
| 281cf3c2 SZ |
387 | |
| 388 | crit_exit(); | |
| 389 | } | |
| 390 | ||
| 391 | void | |
| 392 | usb_do_task(usbd_device_handle dev, struct usb_task *task, int queue, | |
| 393 | int time_out) | |
| 394 | { | |
| 395 | struct usb_taskq *taskq; | |
| 396 | ||
| 397 | crit_enter(); | |
| 398 | ||
| 399 | taskq = &usb_taskq[queue]; | |
| 400 | if (task->queue == -1) { | |
| 401 | DPRINTFN(2,("usb_add_task: task=%p\n", task)); | |
| 402 | TAILQ_INSERT_TAIL(&taskq->tasks, task, next); | |
| 403 | task->queue = queue; | |
| 404 | } else { | |
| 405 | DPRINTFN(3,("usb_add_task: task=%p on q\n", task)); | |
| 406 | } | |
| 1ea0c479 | 407 | wakeup(&taskq->tasks); |
| 281cf3c2 | 408 | |
| 1ea0c479 SZ |
409 | /* Wait until task is finished */ |
| 410 | tsleep((&taskq->tasks + 1), 0, "usbdotsk", time_out); | |
| 281cf3c2 | 411 | |
| 4e01b467 | 412 | crit_exit(); |
| 1550dfd9 MD |
413 | } |
| 414 | ||
| 415 | void | |
| 416 | usb_rem_task(usbd_device_handle dev, struct usb_task *task) | |
| 417 | { | |
| 4e01b467 | 418 | crit_enter(); |
| 281cf3c2 SZ |
419 | if (task->queue != -1) { |
| 420 | TAILQ_REMOVE(&usb_taskq[task->queue].tasks, task, next); | |
| 421 | task->queue = -1; | |
| 1550dfd9 | 422 | } |
| 4e01b467 | 423 | crit_exit(); |
| 984263bc MD |
424 | } |
| 425 | ||
| 426 | void | |
| 427 | usb_event_thread(void *arg) | |
| 428 | { | |
| b9bd1250 MN |
429 | device_t self = arg; |
| 430 | struct usb_softc *sc = device_get_softc(self); | |
| 984263bc MD |
431 | |
| 432 | DPRINTF(("usb_event_thread: start\n")); | |
| 433 | ||
| 1550dfd9 MD |
434 | /* |
| 435 | * In case this controller is a companion controller to an | |
| 436 | * EHCI controller we need to wait until the EHCI controller | |
| 437 | * has grabbed the port. | |
| 438 | * XXX It would be nicer to do this with a tsleep(), but I don't | |
| 439 | * know how to synchronize the creation of the threads so it | |
| 440 | * will work. | |
| 441 | */ | |
| 442 | usb_delay_ms(sc->sc_bus, 500); | |
| 443 | ||
| 281cf3c2 SZ |
444 | crit_enter(); |
| 445 | ||
| 1550dfd9 MD |
446 | /* Make sure first discover does something. */ |
| 447 | sc->sc_bus->needs_explore = 1; | |
| b9bd1250 | 448 | usb_discover(self); |
| 1550dfd9 | 449 | |
| 984263bc MD |
450 | while (!sc->sc_dying) { |
| 451 | #ifdef USB_DEBUG | |
| 452 | if (usb_noexplore < 2) | |
| 453 | #endif | |
| b9bd1250 | 454 | usb_discover(self); |
| 984263bc | 455 | #ifdef USB_DEBUG |
| 0e224b5d SW |
456 | tsleep(&sc->sc_bus->needs_explore, 0, "usbevt", |
| 457 | usb_noexplore ? 0 : hz * 60); | |
| 1550dfd9 | 458 | #else |
| 0e224b5d | 459 | tsleep(&sc->sc_bus->needs_explore, 0, "usbevt", hz * 60); |
| 984263bc | 460 | #endif |
| 984263bc MD |
461 | DPRINTFN(2,("usb_event_thread: woke up\n")); |
| 462 | } | |
| 1550dfd9 | 463 | sc->sc_event_thread = NULL; |
| 984263bc | 464 | |
| 281cf3c2 SZ |
465 | crit_exit(); |
| 466 | ||
| 984263bc MD |
467 | /* In case parent is waiting for us to exit. */ |
| 468 | wakeup(sc); | |
| 469 | ||
| 470 | DPRINTF(("usb_event_thread: exit\n")); | |
| 0cfcada1 | 471 | kthread_exit(); |
| 984263bc MD |
472 | } |
| 473 | ||
| 1550dfd9 MD |
474 | void |
| 475 | usb_task_thread(void *arg) | |
| 476 | { | |
| 477 | struct usb_task *task; | |
| 281cf3c2 | 478 | struct usb_taskq *taskq; |
| 1550dfd9 | 479 | |
| 4e01b467 | 480 | crit_enter(); |
| 281cf3c2 SZ |
481 | |
| 482 | taskq = arg; | |
| 483 | DPRINTF(("usb_task_thread: start taskq %s\n", taskq->name)); | |
| 484 | ||
| 623c4956 | 485 | while (usb_ndevs > 0) { |
| 1ea0c479 SZ |
486 | task = TAILQ_FIRST(&taskq->tasks); |
| 487 | if (task == NULL) { | |
| 281cf3c2 | 488 | tsleep(&taskq->tasks, 0, "usbtsk", 0); |
| 1ea0c479 | 489 | task = TAILQ_FIRST(&taskq->tasks); |
| 1550dfd9 MD |
490 | } |
| 491 | DPRINTFN(2,("usb_task_thread: woke up task=%p\n", task)); | |
| 1ea0c479 SZ |
492 | if (task != NULL) { |
| 493 | TAILQ_REMOVE(&taskq->tasks, task, next); | |
| 494 | task->queue = -1; | |
| 495 | crit_exit(); | |
| 496 | task->fun(task->arg); | |
| 497 | crit_enter(); | |
| 498 | wakeup((&taskq->tasks + 1)); | |
| 499 | } | |
| 1550dfd9 | 500 | } |
| 281cf3c2 SZ |
501 | |
| 502 | crit_exit(); | |
| 503 | ||
| 504 | taskq->taskcreated = 0; | |
| 505 | wakeup(&taskq->taskcreated); | |
| 506 | ||
| 507 | DPRINTF(("usb_event_thread: exit\n")); | |
| 1550dfd9 MD |
508 | } |
| 509 | ||
| 984263bc | 510 | int |
| fef8985e | 511 | usbopen(struct dev_open_args *ap) |
| 984263bc | 512 | { |
| b13267a5 | 513 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
514 | int unit = USBUNIT(dev); |
| 515 | struct usb_softc *sc; | |
| 516 | ||
| 517 | if (unit == USB_DEV_MINOR) { | |
| 518 | if (usb_dev_open) | |
| 519 | return (EBUSY); | |
| 520 | usb_dev_open = 1; | |
| 41c20dac | 521 | usb_async_proc = NULL; |
| 984263bc | 522 | return (0); |
| 1550dfd9 | 523 | } |
| 984263bc | 524 | |
| 1e089e7e HT |
525 | sc = devclass_get_softc(usb_devclass, unit); |
| 526 | if (sc == NULL) | |
| 527 | return (ENXIO); | |
| 984263bc | 528 | |
| 1550dfd9 MD |
529 | if (sc->sc_dying) |
| 530 | return (EIO); | |
| 531 | ||
| 532 | return (0); | |
| 984263bc MD |
533 | } |
| 534 | ||
| 535 | int | |
| fef8985e | 536 | usbread(struct dev_read_args *ap) |
| 984263bc | 537 | { |
| b13267a5 | 538 | cdev_t dev = ap->a_head.a_dev; |
| fef8985e | 539 | struct uio *uio = ap->a_uio; |
| 984263bc MD |
540 | struct usb_event ue; |
| 541 | int unit = USBUNIT(dev); | |
| 4e01b467 | 542 | int error, n; |
| 984263bc MD |
543 | |
| 544 | if (unit != USB_DEV_MINOR) | |
| 545 | return (ENODEV); | |
| 546 | ||
| 547 | if (uio->uio_resid != sizeof(struct usb_event)) | |
| 548 | return (EINVAL); | |
| 549 | ||
| 550 | error = 0; | |
| 4e01b467 | 551 | crit_enter(); |
| 984263bc MD |
552 | for (;;) { |
| 553 | n = usb_get_next_event(&ue); | |
| 554 | if (n != 0) | |
| 555 | break; | |
| fef8985e | 556 | if (ap->a_ioflag & IO_NDELAY) { |
| 984263bc MD |
557 | error = EWOULDBLOCK; |
| 558 | break; | |
| 559 | } | |
| 377d4740 | 560 | error = tsleep(&usb_events, PCATCH, "usbrea", 0); |
| 984263bc MD |
561 | if (error) |
| 562 | break; | |
| 563 | } | |
| 4e01b467 | 564 | crit_exit(); |
| 984263bc MD |
565 | if (!error) |
| 566 | error = uiomove((void *)&ue, uio->uio_resid, uio); | |
| 567 | ||
| 568 | return (error); | |
| 569 | } | |
| 570 | ||
| 571 | int | |
| fef8985e | 572 | usbclose(struct dev_close_args *ap) |
| 984263bc | 573 | { |
| b13267a5 | 574 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
575 | int unit = USBUNIT(dev); |
| 576 | ||
| 577 | if (unit == USB_DEV_MINOR) { | |
| 41c20dac | 578 | usb_async_proc = NULL; |
| 984263bc MD |
579 | usb_dev_open = 0; |
| 580 | } | |
| 581 | ||
| 582 | return (0); | |
| 583 | } | |
| 584 | ||
| 585 | int | |
| fef8985e | 586 | usbioctl(struct dev_ioctl_args *ap) |
| 984263bc | 587 | { |
| b13267a5 | 588 | cdev_t devt = ap->a_head.a_dev; |
| 984263bc MD |
589 | struct usb_softc *sc; |
| 590 | int unit = USBUNIT(devt); | |
| 591 | ||
| 592 | if (unit == USB_DEV_MINOR) { | |
| fef8985e | 593 | switch (ap->a_cmd) { |
| 984263bc | 594 | case FIOASYNC: |
| fef8985e | 595 | if (*(int *)ap->a_data) |
| fef8985e | 596 | usb_async_proc = curproc; |
| 984263bc | 597 | else |
| 41c20dac | 598 | usb_async_proc = NULL; |
| 984263bc MD |
599 | return (0); |
| 600 | ||
| 601 | default: | |
| 602 | return (EINVAL); | |
| 603 | } | |
| 604 | } | |
| 605 | ||
| 1e089e7e | 606 | sc = devclass_get_softc(usb_devclass, unit); |
| 984263bc MD |
607 | |
| 608 | if (sc->sc_dying) | |
| 609 | return (EIO); | |
| 610 | ||
| fef8985e | 611 | switch (ap->a_cmd) { |
| 1550dfd9 | 612 | /* This part should be deleted */ |
| 984263bc | 613 | case USB_DISCOVER: |
| 984263bc | 614 | break; |
| 984263bc MD |
615 | case USB_REQUEST: |
| 616 | { | |
| fef8985e | 617 | struct usb_ctl_request *ur = (void *)ap->a_data; |
| e54488bb | 618 | size_t len = UGETW(ur->ucr_request.wLength); |
| 984263bc MD |
619 | struct iovec iov; |
| 620 | struct uio uio; | |
| 621 | void *ptr = 0; | |
| 622 | int addr = ur->ucr_addr; | |
| 623 | usbd_status err; | |
| 624 | int error = 0; | |
| 625 | ||
| e54488bb MD |
626 | DPRINTF(("usbioctl: USB_REQUEST addr=%d len=%ld\n", addr, len)); |
| 627 | if (len > 32768) | |
| 984263bc | 628 | return (EINVAL); |
| 1550dfd9 | 629 | if (addr < 0 || addr >= USB_MAX_DEVICES || |
| 984263bc MD |
630 | sc->sc_bus->devices[addr] == 0) |
| 631 | return (EINVAL); | |
| 632 | if (len != 0) { | |
| 633 | iov.iov_base = (caddr_t)ur->ucr_data; | |
| 634 | iov.iov_len = len; | |
| 635 | uio.uio_iov = &iov; | |
| 636 | uio.uio_iovcnt = 1; | |
| 637 | uio.uio_resid = len; | |
| 638 | uio.uio_offset = 0; | |
| 639 | uio.uio_segflg = UIO_USERSPACE; | |
| 640 | uio.uio_rw = | |
| 1550dfd9 | 641 | ur->ucr_request.bmRequestType & UT_READ ? |
| 984263bc | 642 | UIO_READ : UIO_WRITE; |
| fef8985e | 643 | uio.uio_td = curthread; |
| efda3bd0 | 644 | ptr = kmalloc(len, M_TEMP, M_WAITOK); |
| 984263bc MD |
645 | if (uio.uio_rw == UIO_WRITE) { |
| 646 | error = uiomove(ptr, len, &uio); | |
| 647 | if (error) | |
| 648 | goto ret; | |
| 649 | } | |
| 650 | } | |
| 651 | err = usbd_do_request_flags(sc->sc_bus->devices[addr], | |
| 1550dfd9 MD |
652 | &ur->ucr_request, ptr, ur->ucr_flags, &ur->ucr_actlen, |
| 653 | USBD_DEFAULT_TIMEOUT); | |
| 984263bc MD |
654 | if (err) { |
| 655 | error = EIO; | |
| 656 | goto ret; | |
| 657 | } | |
| 658 | if (len != 0) { | |
| 659 | if (uio.uio_rw == UIO_READ) { | |
| 660 | error = uiomove(ptr, len, &uio); | |
| 661 | if (error) | |
| 662 | goto ret; | |
| 663 | } | |
| 664 | } | |
| 665 | ret: | |
| 666 | if (ptr) | |
| efda3bd0 | 667 | kfree(ptr, M_TEMP); |
| 984263bc MD |
668 | return (error); |
| 669 | } | |
| 670 | ||
| 671 | case USB_DEVICEINFO: | |
| 672 | { | |
| fef8985e | 673 | struct usb_device_info *di = (void *)ap->a_data; |
| 984263bc MD |
674 | int addr = di->udi_addr; |
| 675 | usbd_device_handle dev; | |
| 676 | ||
| 677 | if (addr < 1 || addr >= USB_MAX_DEVICES) | |
| 678 | return (EINVAL); | |
| 679 | dev = sc->sc_bus->devices[addr]; | |
| 1550dfd9 | 680 | if (dev == NULL) |
| 984263bc | 681 | return (ENXIO); |
| 1550dfd9 | 682 | usbd_fill_deviceinfo(dev, di, 1); |
| 984263bc MD |
683 | break; |
| 684 | } | |
| 685 | ||
| 686 | case USB_DEVICESTATS: | |
| fef8985e | 687 | *(struct usb_device_stats *)ap->a_data = sc->sc_bus->stats; |
| 984263bc MD |
688 | break; |
| 689 | ||
| 690 | default: | |
| 691 | return (EINVAL); | |
| 692 | } | |
| 693 | return (0); | |
| 694 | } | |
| 695 | ||
| 696 | int | |
| fef8985e | 697 | usbpoll(struct dev_poll_args *ap) |
| 984263bc | 698 | { |
| b13267a5 | 699 | cdev_t dev = ap->a_head.a_dev; |
| 4e01b467 | 700 | int revents, mask; |
| 984263bc MD |
701 | int unit = USBUNIT(dev); |
| 702 | ||
| 703 | if (unit == USB_DEV_MINOR) { | |
| 704 | revents = 0; | |
| 705 | mask = POLLIN | POLLRDNORM; | |
| 706 | ||
| 4e01b467 | 707 | crit_enter(); |
| fef8985e MD |
708 | if (ap->a_events & mask && usb_nevents > 0) |
| 709 | revents |= ap->a_events & mask; | |
| 710 | if (revents == 0 && ap->a_events & mask) | |
| 711 | selrecord(curthread, &usb_selevent); | |
| 4e01b467 | 712 | crit_exit(); |
| fef8985e MD |
713 | ap->a_events = revents; |
| 714 | return (0); | |
| 984263bc | 715 | } else { |
| fef8985e | 716 | ap->a_events = 0; |
| 1550dfd9 | 717 | return (0); /* select/poll never wakes up - back compat */ |
| 984263bc MD |
718 | } |
| 719 | } | |
| 720 | ||
| 721 | /* Explore device tree from the root. */ | |
| 6ed427ca | 722 | static void |
| b9bd1250 | 723 | usb_discover(device_t self) |
| 984263bc | 724 | { |
| b9bd1250 | 725 | struct usb_softc *sc = device_get_softc(self); |
| 1550dfd9 | 726 | |
| 1550dfd9 MD |
727 | DPRINTFN(2,("usb_discover\n")); |
| 728 | #ifdef USB_DEBUG | |
| 729 | if (usb_noexplore > 1) | |
| 730 | return; | |
| 731 | #endif | |
| 732 | ||
| 733 | /* | |
| 984263bc MD |
734 | * We need mutual exclusion while traversing the device tree, |
| 735 | * but this is guaranteed since this function is only called | |
| 736 | * from the event thread for the controller. | |
| 737 | */ | |
| 4e01b467 | 738 | crit_enter(); |
| 984263bc MD |
739 | while (sc->sc_bus->needs_explore && !sc->sc_dying) { |
| 740 | sc->sc_bus->needs_explore = 0; | |
| 281cf3c2 | 741 | |
| 4e01b467 | 742 | crit_exit(); |
| 984263bc | 743 | sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub); |
| 4e01b467 | 744 | crit_enter(); |
| 281cf3c2 | 745 | |
| 984263bc | 746 | } |
| 4e01b467 | 747 | crit_exit(); |
| 984263bc MD |
748 | } |
| 749 | ||
| 750 | void | |
| 1550dfd9 | 751 | usb_needs_explore(usbd_device_handle dev) |
| 984263bc | 752 | { |
| 1550dfd9 MD |
753 | DPRINTFN(2,("usb_needs_explore\n")); |
| 754 | dev->bus->needs_explore = 1; | |
| 755 | wakeup(&dev->bus->needs_explore); | |
| 984263bc MD |
756 | } |
| 757 | ||
| 4e01b467 | 758 | /* Called from a critical section */ |
| 984263bc MD |
759 | int |
| 760 | usb_get_next_event(struct usb_event *ue) | |
| 761 | { | |
| 762 | struct usb_event_q *ueq; | |
| 763 | ||
| 764 | if (usb_nevents <= 0) | |
| 765 | return (0); | |
| 1550dfd9 MD |
766 | ueq = TAILQ_FIRST(&usb_events); |
| 767 | #ifdef DIAGNOSTIC | |
| 768 | if (ueq == NULL) { | |
| 85f8e2ea | 769 | kprintf("usb: usb_nevents got out of sync! %d\n", usb_nevents); |
| 1550dfd9 MD |
770 | usb_nevents = 0; |
| 771 | return (0); | |
| 772 | } | |
| 773 | #endif | |
| 7913af9e MN |
774 | if (ue) |
| 775 | *ue = ueq->ue; | |
| 1550dfd9 | 776 | TAILQ_REMOVE(&usb_events, ueq, next); |
| efda3bd0 | 777 | kfree(ueq, M_USBDEV); |
| 984263bc MD |
778 | usb_nevents--; |
| 779 | return (1); | |
| 780 | } | |
| 781 | ||
| 782 | void | |
| 1550dfd9 MD |
783 | usbd_add_dev_event(int type, usbd_device_handle udev) |
| 784 | { | |
| 785 | struct usb_event ue; | |
| 786 | ||
| 787 | usbd_fill_deviceinfo(udev, &ue.u.ue_device, USB_EVENT_IS_ATTACH(type)); | |
| 788 | usb_add_event(type, &ue); | |
| 789 | } | |
| 790 | ||
| 791 | void | |
| 6ed427ca | 792 | usbd_add_drv_event(int type, usbd_device_handle udev, device_t dev) |
| 1550dfd9 MD |
793 | { |
| 794 | struct usb_event ue; | |
| 795 | ||
| 796 | ue.u.ue_driver.ue_cookie = udev->cookie; | |
| 6ed427ca | 797 | strncpy(ue.u.ue_driver.ue_devname, device_get_nameunit(dev), |
| 1550dfd9 MD |
798 | sizeof ue.u.ue_driver.ue_devname); |
| 799 | usb_add_event(type, &ue); | |
| 800 | } | |
| 801 | ||
| 802 | void | |
| 803 | usb_add_event(int type, struct usb_event *uep) | |
| 984263bc MD |
804 | { |
| 805 | struct usb_event_q *ueq; | |
| 984263bc | 806 | struct timeval thetime; |
| 984263bc | 807 | |
| efda3bd0 | 808 | ueq = kmalloc(sizeof *ueq, M_USBDEV, M_INTWAIT); |
| 1550dfd9 MD |
809 | ueq->ue = *uep; |
| 810 | ueq->ue.ue_type = type; | |
| 811 | microtime(&thetime); | |
| 812 | TIMEVAL_TO_TIMESPEC(&thetime, &ueq->ue.ue_time); | |
| 813 | ||
| 4e01b467 | 814 | crit_enter(); |
| 1550dfd9 MD |
815 | if (USB_EVENT_IS_DETACH(type)) { |
| 816 | struct usb_event_q *ueqi, *ueqi_next; | |
| 817 | ||
| 818 | for (ueqi = TAILQ_FIRST(&usb_events); ueqi; ueqi = ueqi_next) { | |
| 819 | ueqi_next = TAILQ_NEXT(ueqi, next); | |
| 820 | if (ueqi->ue.u.ue_driver.ue_cookie.cookie == | |
| 821 | uep->u.ue_device.udi_cookie.cookie) { | |
| 822 | TAILQ_REMOVE(&usb_events, ueqi, next); | |
| efda3bd0 | 823 | kfree(ueqi, M_USBDEV); |
| 1550dfd9 MD |
824 | usb_nevents--; |
| 825 | ueqi_next = TAILQ_FIRST(&usb_events); | |
| 826 | } | |
| 827 | } | |
| 828 | } | |
| 829 | if (usb_nevents >= USB_MAX_EVENTS) { | |
| 984263bc MD |
830 | /* Too many queued events, drop an old one. */ |
| 831 | DPRINTF(("usb: event dropped\n")); | |
| 7913af9e | 832 | usb_get_next_event(NULL); |
| 984263bc | 833 | } |
| 1550dfd9 MD |
834 | TAILQ_INSERT_TAIL(&usb_events, ueq, next); |
| 835 | usb_nevents++; | |
| 984263bc | 836 | wakeup(&usb_events); |
| 375a9af6 | 837 | selwakeup(&usb_selevent); |
| 1550dfd9 | 838 | if (usb_async_proc != NULL) { |
| 84204577 | 839 | ksignal(usb_async_proc, SIGIO); |
| 1550dfd9 | 840 | } |
| 4e01b467 | 841 | crit_exit(); |
| 984263bc MD |
842 | } |
| 843 | ||
| 1550dfd9 MD |
844 | void |
| 845 | usb_schedsoftintr(usbd_bus_handle bus) | |
| 846 | { | |
| 847 | DPRINTFN(10,("usb_schedsoftintr: polling=%d\n", bus->use_polling)); | |
| 848 | #ifdef USB_USE_SOFTINTR | |
| 849 | if (bus->use_polling) { | |
| 850 | bus->methods->soft_intr(bus); | |
| 851 | } else { | |
| 1550dfd9 MD |
852 | if (!callout_pending(&bus->softi)) |
| 853 | callout_reset(&bus->softi, 0, bus->methods->soft_intr, | |
| 854 | bus); | |
| 1550dfd9 MD |
855 | } |
| 856 | #else | |
| 281cf3c2 | 857 | bus->methods->soft_intr(bus); |
| 1550dfd9 MD |
858 | #endif /* USB_USE_SOFTINTR */ |
| 859 | } | |
| 860 | ||
| e785a5d9 HT |
861 | static int |
| 862 | usb_detach(device_t self) | |
| 984263bc | 863 | { |
| e785a5d9 | 864 | struct usb_softc *sc = device_get_softc(self); |
| 1550dfd9 | 865 | struct usb_event ue; |
| 984263bc MD |
866 | |
| 867 | DPRINTF(("usb_detach: start\n")); | |
| 868 | ||
| 869 | sc->sc_dying = 1; | |
| 870 | ||
| 871 | /* Make all devices disconnect. */ | |
| 1550dfd9 | 872 | if (sc->sc_port.device != NULL) |
| 984263bc MD |
873 | usb_disconnect_port(&sc->sc_port, self); |
| 874 | ||
| 875 | /* Kill off event thread. */ | |
| 1550dfd9 | 876 | if (sc->sc_event_thread != NULL) { |
| 984263bc | 877 | wakeup(&sc->sc_bus->needs_explore); |
| 377d4740 | 878 | if (tsleep(sc, 0, "usbdet", hz * 60)) |
| b9bd1250 | 879 | device_printf(self, |
| 720b8fbe | 880 | "event thread didn't die\n"); |
| 984263bc MD |
881 | DPRINTF(("usb_detach: event thread dead\n")); |
| 882 | } | |
| 883 | ||
| 6e46c965 | 884 | release_dev(sc->sc_usbdev); |
| 6e46c965 | 885 | |
| 281cf3c2 SZ |
886 | if (--usb_ndevs == 0) { |
| 887 | int i; | |
| 888 | ||
| 6e46c965 | 889 | release_dev(usb_dev); |
| cd29885a | 890 | dev_ops_remove_minor(&usb_ops, USB_DEV_MINOR); |
| 281cf3c2 SZ |
891 | usb_dev = NULL; |
| 892 | ||
| 893 | for (i = 0; i < USB_NUM_TASKQS; i++) { | |
| 894 | struct usb_taskq *taskq = &usb_taskq[i]; | |
| 895 | wakeup(&taskq->tasks); | |
| 896 | if (tsleep(&taskq->taskcreated, 0, "usbtdt", | |
| 897 | hz * 60)) { | |
| 85f8e2ea | 898 | kprintf("usb task thread %s didn't die\n", |
| 281cf3c2 SZ |
899 | taskq->name); |
| 900 | } | |
| 901 | } | |
| 902 | } | |
| 281cf3c2 | 903 | |
| 984263bc | 904 | usbd_finish(); |
| 1550dfd9 MD |
905 | |
| 906 | #ifdef USB_USE_SOFTINTR | |
| 1550dfd9 MD |
907 | callout_stop(&sc->sc_bus->softi); |
| 908 | #endif | |
| 1550dfd9 | 909 | |
| b9bd1250 | 910 | ue.u.ue_ctrlr.ue_bus = device_get_unit(self); |
| 1550dfd9 MD |
911 | usb_add_event(USB_EVENT_CTRLR_DETACH, &ue); |
| 912 | ||
| 984263bc MD |
913 | return (0); |
| 914 | } | |
| 281cf3c2 | 915 | |
| 6ed427ca | 916 | static void |
| 281cf3c2 | 917 | usb_child_detached(device_t self, device_t child) |
| 984263bc | 918 | { |
| 281cf3c2 | 919 | struct usb_softc *sc = device_get_softc(self); |
| 984263bc | 920 | |
| 281cf3c2 SZ |
921 | /* XXX, should check it is the right device. */ |
| 922 | sc->sc_port.device = NULL; | |
| 984263bc | 923 | } |
| 984263bc | 924 | |
| 5d0fb0e6 MN |
925 | /* Explore USB busses at the end of device configuration */ |
| 926 | static void | |
| 927 | usb_cold_explore(void *arg) | |
| 928 | { | |
| 929 | struct usb_softc *sc; | |
| 930 | ||
| eabfb7a3 MN |
931 | TUNABLE_INT_FETCH("hw.usb.hack_defer_exploration", |
| 932 | &hack_defer_exploration); | |
| 933 | ||
| 934 | if (!hack_defer_exploration) | |
| 935 | return; | |
| 936 | ||
| 5d0fb0e6 MN |
937 | KASSERT(cold || TAILQ_EMPTY(&usb_coldexplist), |
| 938 | ("usb_cold_explore: busses to explore when !cold")); | |
| 939 | while (!TAILQ_EMPTY(&usb_coldexplist)) { | |
| 940 | sc = TAILQ_FIRST(&usb_coldexplist); | |
| 941 | TAILQ_REMOVE(&usb_coldexplist, sc, sc_coldexplist); | |
| 942 | ||
| 943 | sc->sc_bus->use_polling++; | |
| 944 | sc->sc_port.device->hub->explore(sc->sc_bus->root_hub); | |
| 945 | sc->sc_bus->use_polling--; | |
| 946 | } | |
| 947 | } | |
| 948 | ||
| 63bf4253 MD |
949 | struct usbd_bus * |
| 950 | usb_getbushandle(struct usb_softc *sc) | |
| 951 | { | |
| 952 | return (sc->sc_bus); | |
| 953 | } | |
| 954 | ||
| 955 | ||
| 5d0fb0e6 MN |
956 | SYSINIT(usb_cold_explore, SI_SUB_CONFIGURE, SI_ORDER_MIDDLE, |
| 957 | usb_cold_explore, NULL); | |
| 958 | ||
| 984263bc MD |
959 | DRIVER_MODULE(usb, ohci, usb_driver, usb_devclass, 0, 0); |
| 960 | DRIVER_MODULE(usb, uhci, usb_driver, usb_devclass, 0, 0); | |
| 1550dfd9 | 961 | DRIVER_MODULE(usb, ehci, usb_driver, usb_devclass, 0, 0); |