102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /*- 302ac6454SAndrew Thompson * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 402ac6454SAndrew Thompson * 502ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 602ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 702ac6454SAndrew Thompson * are met: 802ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 902ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1002ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1202ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1302ac6454SAndrew Thompson * 1402ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1502ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1602ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1702ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1802ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1902ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2002ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2102ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2202ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2302ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2402ac6454SAndrew Thompson * SUCH DAMAGE. 2502ac6454SAndrew Thompson */ 2602ac6454SAndrew Thompson 27d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE 28d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE 29d2b99310SHans Petter Selasky #else 30f0c078e6SAndrew Thompson #include "opt_ddb.h" 31f0c078e6SAndrew Thompson 32ed6d949aSAndrew Thompson #include <sys/stdint.h> 33ed6d949aSAndrew Thompson #include <sys/stddef.h> 34ed6d949aSAndrew Thompson #include <sys/param.h> 35ed6d949aSAndrew Thompson #include <sys/queue.h> 36ed6d949aSAndrew Thompson #include <sys/types.h> 37ed6d949aSAndrew Thompson #include <sys/systm.h> 38ed6d949aSAndrew Thompson #include <sys/kernel.h> 39ed6d949aSAndrew Thompson #include <sys/bus.h> 40ed6d949aSAndrew Thompson #include <sys/module.h> 41ed6d949aSAndrew Thompson #include <sys/lock.h> 42ed6d949aSAndrew Thompson #include <sys/mutex.h> 43ed6d949aSAndrew Thompson #include <sys/condvar.h> 44ed6d949aSAndrew Thompson #include <sys/sysctl.h> 45ed6d949aSAndrew Thompson #include <sys/sx.h> 46ed6d949aSAndrew Thompson #include <sys/unistd.h> 47ed6d949aSAndrew Thompson #include <sys/callout.h> 48ed6d949aSAndrew Thompson #include <sys/malloc.h> 49ed6d949aSAndrew Thompson #include <sys/priv.h> 50ed6d949aSAndrew Thompson 5102ac6454SAndrew Thompson #include <dev/usb/usb.h> 52ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 5302ac6454SAndrew Thompson 54a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_ctrl_debug 5502ac6454SAndrew Thompson 5602ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 5702ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 5802ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 5902ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 6002ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h> 6102ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 6202ac6454SAndrew Thompson #include <dev/usb/usb_hub.h> 6302ac6454SAndrew Thompson 6402ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 6502ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 6618ec6525SWeongyo Jeong #include <dev/usb/usb_pf.h> 672e141748SHans Petter Selasky #include "usb_if.h" 68d2b99310SHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */ 6902ac6454SAndrew Thompson 7002ac6454SAndrew Thompson /* function prototypes */ 7102ac6454SAndrew Thompson 72a593f6b8SAndrew Thompson static device_probe_t usb_probe; 73a593f6b8SAndrew Thompson static device_attach_t usb_attach; 74a593f6b8SAndrew Thompson static device_detach_t usb_detach; 752e141748SHans Petter Selasky static device_suspend_t usb_suspend; 762e141748SHans Petter Selasky static device_resume_t usb_resume; 772e141748SHans Petter Selasky static device_shutdown_t usb_shutdown; 7802ac6454SAndrew Thompson 79a593f6b8SAndrew Thompson static void usb_attach_sub(device_t, struct usb_bus *); 8002ac6454SAndrew Thompson 8102ac6454SAndrew Thompson /* static variables */ 8202ac6454SAndrew Thompson 83ed6d949aSAndrew Thompson #ifdef USB_DEBUG 84a593f6b8SAndrew Thompson static int usb_ctrl_debug = 0; 8502ac6454SAndrew Thompson 866472ac3dSEd Schouten static SYSCTL_NODE(_hw_usb, OID_AUTO, ctrl, CTLFLAG_RW, 0, "USB controller"); 87a593f6b8SAndrew Thompson SYSCTL_INT(_hw_usb_ctrl, OID_AUTO, debug, CTLFLAG_RW, &usb_ctrl_debug, 0, 8802ac6454SAndrew Thompson "Debug level"); 8902ac6454SAndrew Thompson #endif 9002ac6454SAndrew Thompson 91d2b99310SHans Petter Selasky #if USB_HAVE_ROOT_MOUNT_HOLD 92a0c61406SAlfred Perlstein static int usb_no_boot_wait = 0; 93*af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, no_boot_wait, CTLFLAG_RDTUN, &usb_no_boot_wait, 0, 945ed294aeSHans Petter Selasky "No USB device enumerate waiting at boot."); 95d2b99310SHans Petter Selasky #endif 965ed294aeSHans Petter Selasky 9702f728afSHans Petter Selasky static int usb_no_suspend_wait = 0; 98*af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, no_suspend_wait, CTLFLAG_RWTUN, 9902f728afSHans Petter Selasky &usb_no_suspend_wait, 0, "No USB device waiting at system suspend."); 10002f728afSHans Petter Selasky 1015ed294aeSHans Petter Selasky static int usb_no_shutdown_wait = 0; 102*af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, no_shutdown_wait, CTLFLAG_RWTUN, 10302f728afSHans Petter Selasky &usb_no_shutdown_wait, 0, "No USB device waiting at system shutdown."); 104a0c61406SAlfred Perlstein 105a593f6b8SAndrew Thompson static devclass_t usb_devclass; 10602ac6454SAndrew Thompson 107a593f6b8SAndrew Thompson static device_method_t usb_methods[] = { 108a593f6b8SAndrew Thompson DEVMETHOD(device_probe, usb_probe), 109a593f6b8SAndrew Thompson DEVMETHOD(device_attach, usb_attach), 110a593f6b8SAndrew Thompson DEVMETHOD(device_detach, usb_detach), 1112e141748SHans Petter Selasky DEVMETHOD(device_suspend, usb_suspend), 1122e141748SHans Petter Selasky DEVMETHOD(device_resume, usb_resume), 1132e141748SHans Petter Selasky DEVMETHOD(device_shutdown, usb_shutdown), 11461bfd867SSofian Brabez 11561bfd867SSofian Brabez DEVMETHOD_END 11602ac6454SAndrew Thompson }; 11702ac6454SAndrew Thompson 118a593f6b8SAndrew Thompson static driver_t usb_driver = { 11902ac6454SAndrew Thompson .name = "usbus", 120a593f6b8SAndrew Thompson .methods = usb_methods, 12102ac6454SAndrew Thompson .size = 0, 12202ac6454SAndrew Thompson }; 12302ac6454SAndrew Thompson 124864bc412SHans Petter Selasky /* Host Only Drivers */ 125a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, ohci, usb_driver, usb_devclass, 0, 0); 126a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, uhci, usb_driver, usb_devclass, 0, 0); 127a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, ehci, usb_driver, usb_devclass, 0, 0); 128963169b4SHans Petter Selasky DRIVER_MODULE(usbus, xhci, usb_driver, usb_devclass, 0, 0); 129864bc412SHans Petter Selasky 130864bc412SHans Petter Selasky /* Device Only Drivers */ 131a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, at91_udp, usb_driver, usb_devclass, 0, 0); 132864bc412SHans Petter Selasky DRIVER_MODULE(usbus, musbotg, usb_driver, usb_devclass, 0, 0); 133a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, uss820, usb_driver, usb_devclass, 0, 0); 134d42cc946SOleksandr Tymoshenko DRIVER_MODULE(usbus, octusb, usb_driver, usb_devclass, 0, 0); 13502ac6454SAndrew Thompson 136268ae63aSHans Petter Selasky /* Dual Mode Drivers */ 137268ae63aSHans Petter Selasky DRIVER_MODULE(usbus, dwcotg, usb_driver, usb_devclass, 0, 0); 1380b4dc07dSHans Petter Selasky DRIVER_MODULE(usbus, saf1761otg, usb_driver, usb_devclass, 0, 0); 139268ae63aSHans Petter Selasky 14002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 141a593f6b8SAndrew Thompson * usb_probe 14202ac6454SAndrew Thompson * 14302ac6454SAndrew Thompson * This function is called from "{ehci,ohci,uhci}_pci_attach()". 14402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 14502ac6454SAndrew Thompson static int 146a593f6b8SAndrew Thompson usb_probe(device_t dev) 14702ac6454SAndrew Thompson { 14802ac6454SAndrew Thompson DPRINTF("\n"); 14902ac6454SAndrew Thompson return (0); 15002ac6454SAndrew Thompson } 15102ac6454SAndrew Thompson 152d2b99310SHans Petter Selasky #if USB_HAVE_ROOT_MOUNT_HOLD 1533df007ceSHans Petter Selasky static void 1543df007ceSHans Petter Selasky usb_root_mount_rel(struct usb_bus *bus) 1553df007ceSHans Petter Selasky { 1563df007ceSHans Petter Selasky if (bus->bus_roothold != NULL) { 1573df007ceSHans Petter Selasky DPRINTF("Releasing root mount hold %p\n", bus->bus_roothold); 1583df007ceSHans Petter Selasky root_mount_rel(bus->bus_roothold); 1593df007ceSHans Petter Selasky bus->bus_roothold = NULL; 1603df007ceSHans Petter Selasky } 1613df007ceSHans Petter Selasky } 162d2b99310SHans Petter Selasky #endif 1633df007ceSHans Petter Selasky 16402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 165a593f6b8SAndrew Thompson * usb_attach 16602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 16702ac6454SAndrew Thompson static int 168a593f6b8SAndrew Thompson usb_attach(device_t dev) 16902ac6454SAndrew Thompson { 170760bc48eSAndrew Thompson struct usb_bus *bus = device_get_ivars(dev); 17102ac6454SAndrew Thompson 17202ac6454SAndrew Thompson DPRINTF("\n"); 17302ac6454SAndrew Thompson 17402ac6454SAndrew Thompson if (bus == NULL) { 175767cb2e2SAndrew Thompson device_printf(dev, "USB device has no ivars\n"); 17602ac6454SAndrew Thompson return (ENXIO); 17702ac6454SAndrew Thompson } 17802ac6454SAndrew Thompson 179d2b99310SHans Petter Selasky #if USB_HAVE_ROOT_MOUNT_HOLD 180a0c61406SAlfred Perlstein if (usb_no_boot_wait == 0) { 18102ac6454SAndrew Thompson /* delay vfs_mountroot until the bus is explored */ 182853a10a5SAndrew Thompson bus->bus_roothold = root_mount_hold(device_get_nameunit(dev)); 183a0c61406SAlfred Perlstein } 184d2b99310SHans Petter Selasky #endif 185a593f6b8SAndrew Thompson usb_attach_sub(dev, bus); 18634b48722SAlfred Perlstein 18702ac6454SAndrew Thompson return (0); /* return success */ 18802ac6454SAndrew Thompson } 18902ac6454SAndrew Thompson 19002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 191a593f6b8SAndrew Thompson * usb_detach 19202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 19302ac6454SAndrew Thompson static int 194a593f6b8SAndrew Thompson usb_detach(device_t dev) 19502ac6454SAndrew Thompson { 196760bc48eSAndrew Thompson struct usb_bus *bus = device_get_softc(dev); 19702ac6454SAndrew Thompson 19802ac6454SAndrew Thompson DPRINTF("\n"); 19902ac6454SAndrew Thompson 20002ac6454SAndrew Thompson if (bus == NULL) { 20102ac6454SAndrew Thompson /* was never setup properly */ 20202ac6454SAndrew Thompson return (0); 20302ac6454SAndrew Thompson } 20402ac6454SAndrew Thompson /* Stop power watchdog */ 205a593f6b8SAndrew Thompson usb_callout_drain(&bus->power_wdog); 20602ac6454SAndrew Thompson 207d2b99310SHans Petter Selasky #if USB_HAVE_ROOT_MOUNT_HOLD 20802ac6454SAndrew Thompson /* Let the USB explore process detach all devices. */ 2093df007ceSHans Petter Selasky usb_root_mount_rel(bus); 210d2b99310SHans Petter Selasky #endif 21102ac6454SAndrew Thompson 21202ac6454SAndrew Thompson USB_BUS_LOCK(bus); 21302ac6454SAndrew Thompson 2142e141748SHans Petter Selasky /* Queue detach job */ 2159b3a48eeSHans Petter Selasky usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), 2162e141748SHans Petter Selasky &bus->detach_msg[0], &bus->detach_msg[1]); 2172e141748SHans Petter Selasky 2182e141748SHans Petter Selasky /* Wait for detach to complete */ 2199b3a48eeSHans Petter Selasky usb_proc_mwait(USB_BUS_EXPLORE_PROC(bus), 22002ac6454SAndrew Thompson &bus->detach_msg[0], &bus->detach_msg[1]); 22102ac6454SAndrew Thompson 22202ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 22302ac6454SAndrew Thompson 2249b3a48eeSHans Petter Selasky #if USB_HAVE_PER_BUS_PROCESS 22502ac6454SAndrew Thompson /* Get rid of USB callback processes */ 22602ac6454SAndrew Thompson 2279b3a48eeSHans Petter Selasky usb_proc_free(USB_BUS_GIANT_PROC(bus)); 2289b3a48eeSHans Petter Selasky usb_proc_free(USB_BUS_NON_GIANT_PROC(bus)); 22902ac6454SAndrew Thompson 23002ac6454SAndrew Thompson /* Get rid of USB explore process */ 23102ac6454SAndrew Thompson 2329b3a48eeSHans Petter Selasky usb_proc_free(USB_BUS_EXPLORE_PROC(bus)); 23302ac6454SAndrew Thompson 234672c9965SAndrew Thompson /* Get rid of control transfer process */ 235672c9965SAndrew Thompson 2369b3a48eeSHans Petter Selasky usb_proc_free(USB_BUS_CONTROL_XFER_PROC(bus)); 2379b3a48eeSHans Petter Selasky #endif 238672c9965SAndrew Thompson 2398be09334SHans Petter Selasky #if USB_HAVE_PF 240fe1c24e3SWeongyo Jeong usbpf_detach(bus); 2418be09334SHans Petter Selasky #endif 24202ac6454SAndrew Thompson return (0); 24302ac6454SAndrew Thompson } 24402ac6454SAndrew Thompson 24502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2462e141748SHans Petter Selasky * usb_suspend 2472e141748SHans Petter Selasky *------------------------------------------------------------------------*/ 2482e141748SHans Petter Selasky static int 2492e141748SHans Petter Selasky usb_suspend(device_t dev) 2502e141748SHans Petter Selasky { 2512e141748SHans Petter Selasky struct usb_bus *bus = device_get_softc(dev); 2522e141748SHans Petter Selasky 2532e141748SHans Petter Selasky DPRINTF("\n"); 2542e141748SHans Petter Selasky 2552e141748SHans Petter Selasky if (bus == NULL) { 2562e141748SHans Petter Selasky /* was never setup properly */ 2572e141748SHans Petter Selasky return (0); 2582e141748SHans Petter Selasky } 2592e141748SHans Petter Selasky 2602e141748SHans Petter Selasky USB_BUS_LOCK(bus); 2619b3a48eeSHans Petter Selasky usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), 2622e141748SHans Petter Selasky &bus->suspend_msg[0], &bus->suspend_msg[1]); 26302f728afSHans Petter Selasky if (usb_no_suspend_wait == 0) { 26402f728afSHans Petter Selasky /* wait for suspend callback to be executed */ 2659b3a48eeSHans Petter Selasky usb_proc_mwait(USB_BUS_EXPLORE_PROC(bus), 26602f728afSHans Petter Selasky &bus->suspend_msg[0], &bus->suspend_msg[1]); 26702f728afSHans Petter Selasky } 2682e141748SHans Petter Selasky USB_BUS_UNLOCK(bus); 2692e141748SHans Petter Selasky 2702e141748SHans Petter Selasky return (0); 2712e141748SHans Petter Selasky } 2722e141748SHans Petter Selasky 2732e141748SHans Petter Selasky /*------------------------------------------------------------------------* 2742e141748SHans Petter Selasky * usb_resume 2752e141748SHans Petter Selasky *------------------------------------------------------------------------*/ 2762e141748SHans Petter Selasky static int 2772e141748SHans Petter Selasky usb_resume(device_t dev) 2782e141748SHans Petter Selasky { 2792e141748SHans Petter Selasky struct usb_bus *bus = device_get_softc(dev); 2802e141748SHans Petter Selasky 2812e141748SHans Petter Selasky DPRINTF("\n"); 2822e141748SHans Petter Selasky 2832e141748SHans Petter Selasky if (bus == NULL) { 2842e141748SHans Petter Selasky /* was never setup properly */ 2852e141748SHans Petter Selasky return (0); 2862e141748SHans Petter Selasky } 2872e141748SHans Petter Selasky 2882e141748SHans Petter Selasky USB_BUS_LOCK(bus); 2899b3a48eeSHans Petter Selasky usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), 2902e141748SHans Petter Selasky &bus->resume_msg[0], &bus->resume_msg[1]); 2912e141748SHans Petter Selasky USB_BUS_UNLOCK(bus); 2922e141748SHans Petter Selasky 2932e141748SHans Petter Selasky return (0); 2942e141748SHans Petter Selasky } 2952e141748SHans Petter Selasky 2962e141748SHans Petter Selasky /*------------------------------------------------------------------------* 297563ab081SHans Petter Selasky * usb_bus_reset_async_locked 298563ab081SHans Petter Selasky *------------------------------------------------------------------------*/ 299563ab081SHans Petter Selasky void 300563ab081SHans Petter Selasky usb_bus_reset_async_locked(struct usb_bus *bus) 301563ab081SHans Petter Selasky { 302563ab081SHans Petter Selasky USB_BUS_LOCK_ASSERT(bus, MA_OWNED); 303563ab081SHans Petter Selasky 304563ab081SHans Petter Selasky DPRINTF("\n"); 305563ab081SHans Petter Selasky 306563ab081SHans Petter Selasky if (bus->reset_msg[0].hdr.pm_qentry.tqe_prev != NULL || 307563ab081SHans Petter Selasky bus->reset_msg[1].hdr.pm_qentry.tqe_prev != NULL) { 308563ab081SHans Petter Selasky DPRINTF("Reset already pending\n"); 309563ab081SHans Petter Selasky return; 310563ab081SHans Petter Selasky } 311563ab081SHans Petter Selasky 312563ab081SHans Petter Selasky device_printf(bus->parent, "Resetting controller\n"); 313563ab081SHans Petter Selasky 314563ab081SHans Petter Selasky usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), 315563ab081SHans Petter Selasky &bus->reset_msg[0], &bus->reset_msg[1]); 316563ab081SHans Petter Selasky } 317563ab081SHans Petter Selasky 318563ab081SHans Petter Selasky /*------------------------------------------------------------------------* 3192e141748SHans Petter Selasky * usb_shutdown 3202e141748SHans Petter Selasky *------------------------------------------------------------------------*/ 3212e141748SHans Petter Selasky static int 3222e141748SHans Petter Selasky usb_shutdown(device_t dev) 3232e141748SHans Petter Selasky { 3242e141748SHans Petter Selasky struct usb_bus *bus = device_get_softc(dev); 3252e141748SHans Petter Selasky 3262e141748SHans Petter Selasky DPRINTF("\n"); 3272e141748SHans Petter Selasky 3282e141748SHans Petter Selasky if (bus == NULL) { 3292e141748SHans Petter Selasky /* was never setup properly */ 3302e141748SHans Petter Selasky return (0); 3312e141748SHans Petter Selasky } 3322e141748SHans Petter Selasky 333cabe79d9SMarius Strobl DPRINTF("%s: Controller shutdown\n", device_get_nameunit(bus->bdev)); 3345ed294aeSHans Petter Selasky 3352e141748SHans Petter Selasky USB_BUS_LOCK(bus); 3369b3a48eeSHans Petter Selasky usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), 3372e141748SHans Petter Selasky &bus->shutdown_msg[0], &bus->shutdown_msg[1]); 3385ed294aeSHans Petter Selasky if (usb_no_shutdown_wait == 0) { 3395ed294aeSHans Petter Selasky /* wait for shutdown callback to be executed */ 3409b3a48eeSHans Petter Selasky usb_proc_mwait(USB_BUS_EXPLORE_PROC(bus), 3415ed294aeSHans Petter Selasky &bus->shutdown_msg[0], &bus->shutdown_msg[1]); 3425ed294aeSHans Petter Selasky } 3432e141748SHans Petter Selasky USB_BUS_UNLOCK(bus); 3442e141748SHans Petter Selasky 345cabe79d9SMarius Strobl DPRINTF("%s: Controller shutdown complete\n", 346cabe79d9SMarius Strobl device_get_nameunit(bus->bdev)); 3475ed294aeSHans Petter Selasky 3482e141748SHans Petter Selasky return (0); 3492e141748SHans Petter Selasky } 3502e141748SHans Petter Selasky 3512e141748SHans Petter Selasky /*------------------------------------------------------------------------* 352a593f6b8SAndrew Thompson * usb_bus_explore 35302ac6454SAndrew Thompson * 35402ac6454SAndrew Thompson * This function is used to explore the device tree from the root. 35502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 35602ac6454SAndrew Thompson static void 357a593f6b8SAndrew Thompson usb_bus_explore(struct usb_proc_msg *pm) 35802ac6454SAndrew Thompson { 359760bc48eSAndrew Thompson struct usb_bus *bus; 360760bc48eSAndrew Thompson struct usb_device *udev; 36102ac6454SAndrew Thompson 362760bc48eSAndrew Thompson bus = ((struct usb_bus_msg *)pm)->bus; 36302ac6454SAndrew Thompson udev = bus->devices[USB_ROOT_HUB_ADDR]; 36402ac6454SAndrew Thompson 3652e141748SHans Petter Selasky if (bus->no_explore != 0) 3662e141748SHans Petter Selasky return; 3672e141748SHans Petter Selasky 368d64e9217SHans Petter Selasky if (udev != NULL) { 369d64e9217SHans Petter Selasky USB_BUS_UNLOCK(bus); 370d64e9217SHans Petter Selasky uhub_explore_handle_re_enumerate(udev); 371d64e9217SHans Petter Selasky USB_BUS_LOCK(bus); 372d64e9217SHans Petter Selasky } 373d64e9217SHans Petter Selasky 374d64e9217SHans Petter Selasky if (udev != NULL && udev->hub != NULL) { 37502ac6454SAndrew Thompson 37602ac6454SAndrew Thompson if (bus->do_probe) { 37702ac6454SAndrew Thompson bus->do_probe = 0; 37802ac6454SAndrew Thompson bus->driver_added_refcount++; 37902ac6454SAndrew Thompson } 38002ac6454SAndrew Thompson if (bus->driver_added_refcount == 0) { 38102ac6454SAndrew Thompson /* avoid zero, hence that is memory default */ 38202ac6454SAndrew Thompson bus->driver_added_refcount = 1; 38302ac6454SAndrew Thompson } 38402ac6454SAndrew Thompson 385f0c078e6SAndrew Thompson #ifdef DDB 38634b48722SAlfred Perlstein /* 38734b48722SAlfred Perlstein * The following three lines of code are only here to 38834b48722SAlfred Perlstein * recover from DDB: 38934b48722SAlfred Perlstein */ 3909b3a48eeSHans Petter Selasky usb_proc_rewakeup(USB_BUS_CONTROL_XFER_PROC(bus)); 3919b3a48eeSHans Petter Selasky usb_proc_rewakeup(USB_BUS_GIANT_PROC(bus)); 3929b3a48eeSHans Petter Selasky usb_proc_rewakeup(USB_BUS_NON_GIANT_PROC(bus)); 393f0c078e6SAndrew Thompson #endif 39434b48722SAlfred Perlstein 39534b48722SAlfred Perlstein USB_BUS_UNLOCK(bus); 396a56fe095SJohn Baldwin 397e727a16cSAndrew Thompson #if USB_HAVE_POWERD 39802ac6454SAndrew Thompson /* 39902ac6454SAndrew Thompson * First update the USB power state! 40002ac6454SAndrew Thompson */ 401a593f6b8SAndrew Thompson usb_bus_powerd(bus); 402e727a16cSAndrew Thompson #endif 40334b48722SAlfred Perlstein /* Explore the Root USB HUB. */ 40402ac6454SAndrew Thompson (udev->hub->explore) (udev); 40502ac6454SAndrew Thompson USB_BUS_LOCK(bus); 40602ac6454SAndrew Thompson } 407d2b99310SHans Petter Selasky #if USB_HAVE_ROOT_MOUNT_HOLD 4083df007ceSHans Petter Selasky usb_root_mount_rel(bus); 409d2b99310SHans Petter Selasky #endif 41002ac6454SAndrew Thompson } 41102ac6454SAndrew Thompson 41202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 413a593f6b8SAndrew Thompson * usb_bus_detach 41402ac6454SAndrew Thompson * 41502ac6454SAndrew Thompson * This function is used to detach the device tree from the root. 41602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 41702ac6454SAndrew Thompson static void 418a593f6b8SAndrew Thompson usb_bus_detach(struct usb_proc_msg *pm) 41902ac6454SAndrew Thompson { 420760bc48eSAndrew Thompson struct usb_bus *bus; 421760bc48eSAndrew Thompson struct usb_device *udev; 42202ac6454SAndrew Thompson device_t dev; 42302ac6454SAndrew Thompson 424760bc48eSAndrew Thompson bus = ((struct usb_bus_msg *)pm)->bus; 42502ac6454SAndrew Thompson udev = bus->devices[USB_ROOT_HUB_ADDR]; 42602ac6454SAndrew Thompson dev = bus->bdev; 42702ac6454SAndrew Thompson /* clear the softc */ 42802ac6454SAndrew Thompson device_set_softc(dev, NULL); 42902ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 43002ac6454SAndrew Thompson 43102ac6454SAndrew Thompson /* detach children first */ 43234b48722SAlfred Perlstein mtx_lock(&Giant); 43302ac6454SAndrew Thompson bus_generic_detach(dev); 43434b48722SAlfred Perlstein mtx_unlock(&Giant); 43502ac6454SAndrew Thompson 43602ac6454SAndrew Thompson /* 437d88688c7SAndrew Thompson * Free USB device and all subdevices, if any. 43802ac6454SAndrew Thompson */ 439d88688c7SAndrew Thompson usb_free_device(udev, 0); 44002ac6454SAndrew Thompson 44102ac6454SAndrew Thompson USB_BUS_LOCK(bus); 44202ac6454SAndrew Thompson /* clear bdev variable last */ 44302ac6454SAndrew Thompson bus->bdev = NULL; 44402ac6454SAndrew Thompson } 44502ac6454SAndrew Thompson 4462e141748SHans Petter Selasky /*------------------------------------------------------------------------* 4472e141748SHans Petter Selasky * usb_bus_suspend 4482e141748SHans Petter Selasky * 449468f354bSHans Petter Selasky * This function is used to suspend the USB controller. 4502e141748SHans Petter Selasky *------------------------------------------------------------------------*/ 4512e141748SHans Petter Selasky static void 4522e141748SHans Petter Selasky usb_bus_suspend(struct usb_proc_msg *pm) 4532e141748SHans Petter Selasky { 4542e141748SHans Petter Selasky struct usb_bus *bus; 4552e141748SHans Petter Selasky struct usb_device *udev; 4562e141748SHans Petter Selasky usb_error_t err; 457a18a7a41SHans Petter Selasky uint8_t do_unlock; 4582e141748SHans Petter Selasky 459563ab081SHans Petter Selasky DPRINTF("\n"); 460563ab081SHans Petter Selasky 4612e141748SHans Petter Selasky bus = ((struct usb_bus_msg *)pm)->bus; 4622e141748SHans Petter Selasky udev = bus->devices[USB_ROOT_HUB_ADDR]; 4632e141748SHans Petter Selasky 4642e141748SHans Petter Selasky if (udev == NULL || bus->bdev == NULL) 4652e141748SHans Petter Selasky return; 4662e141748SHans Petter Selasky 4676bd3e535SHans Petter Selasky USB_BUS_UNLOCK(bus); 4686bd3e535SHans Petter Selasky 46902f728afSHans Petter Selasky /* 47002f728afSHans Petter Selasky * We use the shutdown event here because the suspend and 47102f728afSHans Petter Selasky * resume events are reserved for the USB port suspend and 47202f728afSHans Petter Selasky * resume. The USB system suspend is implemented like full 47302f728afSHans Petter Selasky * shutdown and all connected USB devices will be disconnected 47402f728afSHans Petter Selasky * subsequently. At resume all USB devices will be 47502f728afSHans Petter Selasky * re-connected again. 47602f728afSHans Petter Selasky */ 47702f728afSHans Petter Selasky 4782e141748SHans Petter Selasky bus_generic_shutdown(bus->bdev); 4792e141748SHans Petter Selasky 480a18a7a41SHans Petter Selasky do_unlock = usbd_enum_lock(udev); 4812e141748SHans Petter Selasky 4822e141748SHans Petter Selasky err = usbd_set_config_index(udev, USB_UNCONFIG_INDEX); 4832e141748SHans Petter Selasky if (err) 4842e141748SHans Petter Selasky device_printf(bus->bdev, "Could not unconfigure root HUB\n"); 4852e141748SHans Petter Selasky 4862e141748SHans Petter Selasky USB_BUS_LOCK(bus); 4872e141748SHans Petter Selasky bus->hw_power_state = 0; 4882e141748SHans Petter Selasky bus->no_explore = 1; 4892e141748SHans Petter Selasky USB_BUS_UNLOCK(bus); 4902e141748SHans Petter Selasky 4912e141748SHans Petter Selasky if (bus->methods->set_hw_power != NULL) 4922e141748SHans Petter Selasky (bus->methods->set_hw_power) (bus); 4932e141748SHans Petter Selasky 4942e141748SHans Petter Selasky if (bus->methods->set_hw_power_sleep != NULL) 4952e141748SHans Petter Selasky (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_SUSPEND); 4962e141748SHans Petter Selasky 497a18a7a41SHans Petter Selasky if (do_unlock) 4982e141748SHans Petter Selasky usbd_enum_unlock(udev); 4996bd3e535SHans Petter Selasky 5006bd3e535SHans Petter Selasky USB_BUS_LOCK(bus); 5012e141748SHans Petter Selasky } 5022e141748SHans Petter Selasky 5032e141748SHans Petter Selasky /*------------------------------------------------------------------------* 5042e141748SHans Petter Selasky * usb_bus_resume 5052e141748SHans Petter Selasky * 506468f354bSHans Petter Selasky * This function is used to resume the USB controller. 5072e141748SHans Petter Selasky *------------------------------------------------------------------------*/ 5082e141748SHans Petter Selasky static void 5092e141748SHans Petter Selasky usb_bus_resume(struct usb_proc_msg *pm) 5102e141748SHans Petter Selasky { 5112e141748SHans Petter Selasky struct usb_bus *bus; 5122e141748SHans Petter Selasky struct usb_device *udev; 5132e141748SHans Petter Selasky usb_error_t err; 514a18a7a41SHans Petter Selasky uint8_t do_unlock; 5152e141748SHans Petter Selasky 516563ab081SHans Petter Selasky DPRINTF("\n"); 517563ab081SHans Petter Selasky 5182e141748SHans Petter Selasky bus = ((struct usb_bus_msg *)pm)->bus; 5192e141748SHans Petter Selasky udev = bus->devices[USB_ROOT_HUB_ADDR]; 5202e141748SHans Petter Selasky 5212e141748SHans Petter Selasky if (udev == NULL || bus->bdev == NULL) 5222e141748SHans Petter Selasky return; 5232e141748SHans Petter Selasky 5246bd3e535SHans Petter Selasky USB_BUS_UNLOCK(bus); 5256bd3e535SHans Petter Selasky 526a18a7a41SHans Petter Selasky do_unlock = usbd_enum_lock(udev); 5272e141748SHans Petter Selasky #if 0 5282e141748SHans Petter Selasky DEVMETHOD(usb_take_controller, NULL); /* dummy */ 5292e141748SHans Petter Selasky #endif 5302e141748SHans Petter Selasky USB_TAKE_CONTROLLER(device_get_parent(bus->bdev)); 5312e141748SHans Petter Selasky 5322e141748SHans Petter Selasky USB_BUS_LOCK(bus); 5332e141748SHans Petter Selasky bus->hw_power_state = 5342e141748SHans Petter Selasky USB_HW_POWER_CONTROL | 5352e141748SHans Petter Selasky USB_HW_POWER_BULK | 5362e141748SHans Petter Selasky USB_HW_POWER_INTERRUPT | 5372e141748SHans Petter Selasky USB_HW_POWER_ISOC | 5382e141748SHans Petter Selasky USB_HW_POWER_NON_ROOT_HUB; 5392e141748SHans Petter Selasky bus->no_explore = 0; 5402e141748SHans Petter Selasky USB_BUS_UNLOCK(bus); 5412e141748SHans Petter Selasky 5422e141748SHans Petter Selasky if (bus->methods->set_hw_power_sleep != NULL) 5432e141748SHans Petter Selasky (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_RESUME); 5442e141748SHans Petter Selasky 5452e141748SHans Petter Selasky if (bus->methods->set_hw_power != NULL) 5462e141748SHans Petter Selasky (bus->methods->set_hw_power) (bus); 5472e141748SHans Petter Selasky 5486bbe7cdfSHans Petter Selasky /* restore USB configuration to index 0 */ 5492e141748SHans Petter Selasky err = usbd_set_config_index(udev, 0); 5502e141748SHans Petter Selasky if (err) 5512e141748SHans Petter Selasky device_printf(bus->bdev, "Could not configure root HUB\n"); 5522e141748SHans Petter Selasky 5536bbe7cdfSHans Petter Selasky /* probe and attach */ 5546bbe7cdfSHans Petter Selasky err = usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY); 5556bbe7cdfSHans Petter Selasky if (err) { 5566bbe7cdfSHans Petter Selasky device_printf(bus->bdev, "Could not probe and " 5576bbe7cdfSHans Petter Selasky "attach root HUB\n"); 5586bbe7cdfSHans Petter Selasky } 5596bbe7cdfSHans Petter Selasky 560a18a7a41SHans Petter Selasky if (do_unlock) 5612e141748SHans Petter Selasky usbd_enum_unlock(udev); 5626bd3e535SHans Petter Selasky 5636bd3e535SHans Petter Selasky USB_BUS_LOCK(bus); 5642e141748SHans Petter Selasky } 5652e141748SHans Petter Selasky 5662e141748SHans Petter Selasky /*------------------------------------------------------------------------* 567563ab081SHans Petter Selasky * usb_bus_reset 568563ab081SHans Petter Selasky * 569468f354bSHans Petter Selasky * This function is used to reset the USB controller. 570563ab081SHans Petter Selasky *------------------------------------------------------------------------*/ 571563ab081SHans Petter Selasky static void 572563ab081SHans Petter Selasky usb_bus_reset(struct usb_proc_msg *pm) 573563ab081SHans Petter Selasky { 574563ab081SHans Petter Selasky struct usb_bus *bus; 575563ab081SHans Petter Selasky 576563ab081SHans Petter Selasky DPRINTF("\n"); 577563ab081SHans Petter Selasky 578563ab081SHans Petter Selasky bus = ((struct usb_bus_msg *)pm)->bus; 579563ab081SHans Petter Selasky 580563ab081SHans Petter Selasky if (bus->bdev == NULL || bus->no_explore != 0) 581563ab081SHans Petter Selasky return; 582563ab081SHans Petter Selasky 583563ab081SHans Petter Selasky /* a suspend and resume will reset the USB controller */ 584563ab081SHans Petter Selasky usb_bus_suspend(pm); 585563ab081SHans Petter Selasky usb_bus_resume(pm); 586563ab081SHans Petter Selasky } 587563ab081SHans Petter Selasky 588563ab081SHans Petter Selasky /*------------------------------------------------------------------------* 5892e141748SHans Petter Selasky * usb_bus_shutdown 5902e141748SHans Petter Selasky * 591468f354bSHans Petter Selasky * This function is used to shutdown the USB controller. 5922e141748SHans Petter Selasky *------------------------------------------------------------------------*/ 5932e141748SHans Petter Selasky static void 5942e141748SHans Petter Selasky usb_bus_shutdown(struct usb_proc_msg *pm) 5952e141748SHans Petter Selasky { 5962e141748SHans Petter Selasky struct usb_bus *bus; 5972e141748SHans Petter Selasky struct usb_device *udev; 5982e141748SHans Petter Selasky usb_error_t err; 599a18a7a41SHans Petter Selasky uint8_t do_unlock; 6002e141748SHans Petter Selasky 6012e141748SHans Petter Selasky bus = ((struct usb_bus_msg *)pm)->bus; 6022e141748SHans Petter Selasky udev = bus->devices[USB_ROOT_HUB_ADDR]; 6032e141748SHans Petter Selasky 6042e141748SHans Petter Selasky if (udev == NULL || bus->bdev == NULL) 6052e141748SHans Petter Selasky return; 6062e141748SHans Petter Selasky 6076bd3e535SHans Petter Selasky USB_BUS_UNLOCK(bus); 6086bd3e535SHans Petter Selasky 6092e141748SHans Petter Selasky bus_generic_shutdown(bus->bdev); 6102e141748SHans Petter Selasky 611a18a7a41SHans Petter Selasky do_unlock = usbd_enum_lock(udev); 6122e141748SHans Petter Selasky 6132e141748SHans Petter Selasky err = usbd_set_config_index(udev, USB_UNCONFIG_INDEX); 6142e141748SHans Petter Selasky if (err) 6152e141748SHans Petter Selasky device_printf(bus->bdev, "Could not unconfigure root HUB\n"); 6162e141748SHans Petter Selasky 6172e141748SHans Petter Selasky USB_BUS_LOCK(bus); 6182e141748SHans Petter Selasky bus->hw_power_state = 0; 6192e141748SHans Petter Selasky bus->no_explore = 1; 6202e141748SHans Petter Selasky USB_BUS_UNLOCK(bus); 6212e141748SHans Petter Selasky 6222e141748SHans Petter Selasky if (bus->methods->set_hw_power != NULL) 6232e141748SHans Petter Selasky (bus->methods->set_hw_power) (bus); 6242e141748SHans Petter Selasky 6252e141748SHans Petter Selasky if (bus->methods->set_hw_power_sleep != NULL) 6262e141748SHans Petter Selasky (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_SHUTDOWN); 6272e141748SHans Petter Selasky 628a18a7a41SHans Petter Selasky if (do_unlock) 6292e141748SHans Petter Selasky usbd_enum_unlock(udev); 6306bd3e535SHans Petter Selasky 6316bd3e535SHans Petter Selasky USB_BUS_LOCK(bus); 6322e141748SHans Petter Selasky } 6332e141748SHans Petter Selasky 63402ac6454SAndrew Thompson static void 635a593f6b8SAndrew Thompson usb_power_wdog(void *arg) 63602ac6454SAndrew Thompson { 637760bc48eSAndrew Thompson struct usb_bus *bus = arg; 63802ac6454SAndrew Thompson 63902ac6454SAndrew Thompson USB_BUS_LOCK_ASSERT(bus, MA_OWNED); 64002ac6454SAndrew Thompson 641a593f6b8SAndrew Thompson usb_callout_reset(&bus->power_wdog, 642a593f6b8SAndrew Thompson 4 * hz, usb_power_wdog, arg); 64302ac6454SAndrew Thompson 644f0c078e6SAndrew Thompson #ifdef DDB 64534b48722SAlfred Perlstein /* 64634b48722SAlfred Perlstein * The following line of code is only here to recover from 64734b48722SAlfred Perlstein * DDB: 64834b48722SAlfred Perlstein */ 6499b3a48eeSHans Petter Selasky usb_proc_rewakeup(USB_BUS_EXPLORE_PROC(bus)); /* recover from DDB */ 650f0c078e6SAndrew Thompson #endif 65134b48722SAlfred Perlstein 652e727a16cSAndrew Thompson #if USB_HAVE_POWERD 65302ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 65402ac6454SAndrew Thompson 655a593f6b8SAndrew Thompson usb_bus_power_update(bus); 65602ac6454SAndrew Thompson 657684e3f22SAndrew Thompson USB_BUS_LOCK(bus); 658e727a16cSAndrew Thompson #endif 65902ac6454SAndrew Thompson } 66002ac6454SAndrew Thompson 66102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 662a593f6b8SAndrew Thompson * usb_bus_attach 66302ac6454SAndrew Thompson * 66402ac6454SAndrew Thompson * This function attaches USB in context of the explore thread. 66502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 66602ac6454SAndrew Thompson static void 667a593f6b8SAndrew Thompson usb_bus_attach(struct usb_proc_msg *pm) 66802ac6454SAndrew Thompson { 669760bc48eSAndrew Thompson struct usb_bus *bus; 670760bc48eSAndrew Thompson struct usb_device *child; 67102ac6454SAndrew Thompson device_t dev; 672e0a69b51SAndrew Thompson usb_error_t err; 6738d2dd5ddSAndrew Thompson enum usb_dev_speed speed; 67402ac6454SAndrew Thompson 675760bc48eSAndrew Thompson bus = ((struct usb_bus_msg *)pm)->bus; 67602ac6454SAndrew Thompson dev = bus->bdev; 67702ac6454SAndrew Thompson 67802ac6454SAndrew Thompson DPRINTF("\n"); 67902ac6454SAndrew Thompson 68002ac6454SAndrew Thompson switch (bus->usbrev) { 68102ac6454SAndrew Thompson case USB_REV_1_0: 68202ac6454SAndrew Thompson speed = USB_SPEED_FULL; 68302ac6454SAndrew Thompson device_printf(bus->bdev, "12Mbps Full Speed USB v1.0\n"); 68402ac6454SAndrew Thompson break; 68502ac6454SAndrew Thompson 68602ac6454SAndrew Thompson case USB_REV_1_1: 68702ac6454SAndrew Thompson speed = USB_SPEED_FULL; 68802ac6454SAndrew Thompson device_printf(bus->bdev, "12Mbps Full Speed USB v1.1\n"); 68902ac6454SAndrew Thompson break; 69002ac6454SAndrew Thompson 69102ac6454SAndrew Thompson case USB_REV_2_0: 69202ac6454SAndrew Thompson speed = USB_SPEED_HIGH; 69302ac6454SAndrew Thompson device_printf(bus->bdev, "480Mbps High Speed USB v2.0\n"); 69402ac6454SAndrew Thompson break; 69502ac6454SAndrew Thompson 69602ac6454SAndrew Thompson case USB_REV_2_5: 69702ac6454SAndrew Thompson speed = USB_SPEED_VARIABLE; 69802ac6454SAndrew Thompson device_printf(bus->bdev, "480Mbps Wireless USB v2.5\n"); 69902ac6454SAndrew Thompson break; 70002ac6454SAndrew Thompson 701963169b4SHans Petter Selasky case USB_REV_3_0: 702963169b4SHans Petter Selasky speed = USB_SPEED_SUPER; 703ccac019aSHans Petter Selasky device_printf(bus->bdev, "5.0Gbps Super Speed USB v3.0\n"); 704963169b4SHans Petter Selasky break; 705963169b4SHans Petter Selasky 70602ac6454SAndrew Thompson default: 707767cb2e2SAndrew Thompson device_printf(bus->bdev, "Unsupported USB revision\n"); 708d2b99310SHans Petter Selasky #if USB_HAVE_ROOT_MOUNT_HOLD 7093df007ceSHans Petter Selasky usb_root_mount_rel(bus); 710d2b99310SHans Petter Selasky #endif 71102ac6454SAndrew Thompson return; 71202ac6454SAndrew Thompson } 71302ac6454SAndrew Thompson 7144eae601eSAndrew Thompson /* default power_mask value */ 7154eae601eSAndrew Thompson bus->hw_power_state = 7164eae601eSAndrew Thompson USB_HW_POWER_CONTROL | 7174eae601eSAndrew Thompson USB_HW_POWER_BULK | 7184eae601eSAndrew Thompson USB_HW_POWER_INTERRUPT | 7194eae601eSAndrew Thompson USB_HW_POWER_ISOC | 7204eae601eSAndrew Thompson USB_HW_POWER_NON_ROOT_HUB; 7214eae601eSAndrew Thompson 7222e141748SHans Petter Selasky USB_BUS_UNLOCK(bus); 7232e141748SHans Petter Selasky 7244eae601eSAndrew Thompson /* make sure power is set at least once */ 7254eae601eSAndrew Thompson 7264eae601eSAndrew Thompson if (bus->methods->set_hw_power != NULL) { 7274eae601eSAndrew Thompson (bus->methods->set_hw_power) (bus); 7284eae601eSAndrew Thompson } 7294eae601eSAndrew Thompson 7302e141748SHans Petter Selasky /* allocate the Root USB device */ 73102ac6454SAndrew Thompson 732a593f6b8SAndrew Thompson child = usb_alloc_device(bus->bdev, bus, NULL, 0, 0, 1, 73302ac6454SAndrew Thompson speed, USB_MODE_HOST); 73402ac6454SAndrew Thompson if (child) { 735a593f6b8SAndrew Thompson err = usb_probe_and_attach(child, 73602ac6454SAndrew Thompson USB_IFACE_INDEX_ANY); 73702ac6454SAndrew Thompson if (!err) { 7381be5bf51SAndrew Thompson if ((bus->devices[USB_ROOT_HUB_ADDR] == NULL) || 7391be5bf51SAndrew Thompson (bus->devices[USB_ROOT_HUB_ADDR]->hub == NULL)) { 74002ac6454SAndrew Thompson err = USB_ERR_NO_ROOT_HUB; 74102ac6454SAndrew Thompson } 74202ac6454SAndrew Thompson } 74302ac6454SAndrew Thompson } else { 74402ac6454SAndrew Thompson err = USB_ERR_NOMEM; 74502ac6454SAndrew Thompson } 74602ac6454SAndrew Thompson 74702ac6454SAndrew Thompson USB_BUS_LOCK(bus); 74802ac6454SAndrew Thompson 74902ac6454SAndrew Thompson if (err) { 75002ac6454SAndrew Thompson device_printf(bus->bdev, "Root HUB problem, error=%s\n", 751a593f6b8SAndrew Thompson usbd_errstr(err)); 752d2b99310SHans Petter Selasky #if USB_HAVE_ROOT_MOUNT_HOLD 7533df007ceSHans Petter Selasky usb_root_mount_rel(bus); 754d2b99310SHans Petter Selasky #endif 75502ac6454SAndrew Thompson } 75602ac6454SAndrew Thompson 75702ac6454SAndrew Thompson /* set softc - we are ready */ 75802ac6454SAndrew Thompson device_set_softc(dev, bus); 75902ac6454SAndrew Thompson 760684e3f22SAndrew Thompson /* start watchdog */ 761a593f6b8SAndrew Thompson usb_power_wdog(bus); 76202ac6454SAndrew Thompson } 76302ac6454SAndrew Thompson 76402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 765a593f6b8SAndrew Thompson * usb_attach_sub 76602ac6454SAndrew Thompson * 76734b48722SAlfred Perlstein * This function creates a thread which runs the USB attach code. 76802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 76902ac6454SAndrew Thompson static void 770a593f6b8SAndrew Thompson usb_attach_sub(device_t dev, struct usb_bus *bus) 77102ac6454SAndrew Thompson { 77234b48722SAlfred Perlstein mtx_lock(&Giant); 77334b48722SAlfred Perlstein if (usb_devclass_ptr == NULL) 77434b48722SAlfred Perlstein usb_devclass_ptr = devclass_find("usbus"); 77534b48722SAlfred Perlstein mtx_unlock(&Giant); 77634b48722SAlfred Perlstein 7778be09334SHans Petter Selasky #if USB_HAVE_PF 778fe1c24e3SWeongyo Jeong usbpf_attach(bus); 7798be09334SHans Petter Selasky #endif 78002ac6454SAndrew Thompson /* Initialise USB process messages */ 781a593f6b8SAndrew Thompson bus->explore_msg[0].hdr.pm_callback = &usb_bus_explore; 78202ac6454SAndrew Thompson bus->explore_msg[0].bus = bus; 783a593f6b8SAndrew Thompson bus->explore_msg[1].hdr.pm_callback = &usb_bus_explore; 78402ac6454SAndrew Thompson bus->explore_msg[1].bus = bus; 78502ac6454SAndrew Thompson 786a593f6b8SAndrew Thompson bus->detach_msg[0].hdr.pm_callback = &usb_bus_detach; 78702ac6454SAndrew Thompson bus->detach_msg[0].bus = bus; 788a593f6b8SAndrew Thompson bus->detach_msg[1].hdr.pm_callback = &usb_bus_detach; 78902ac6454SAndrew Thompson bus->detach_msg[1].bus = bus; 79002ac6454SAndrew Thompson 791a593f6b8SAndrew Thompson bus->attach_msg[0].hdr.pm_callback = &usb_bus_attach; 79202ac6454SAndrew Thompson bus->attach_msg[0].bus = bus; 793a593f6b8SAndrew Thompson bus->attach_msg[1].hdr.pm_callback = &usb_bus_attach; 79402ac6454SAndrew Thompson bus->attach_msg[1].bus = bus; 79502ac6454SAndrew Thompson 7962e141748SHans Petter Selasky bus->suspend_msg[0].hdr.pm_callback = &usb_bus_suspend; 7972e141748SHans Petter Selasky bus->suspend_msg[0].bus = bus; 7982e141748SHans Petter Selasky bus->suspend_msg[1].hdr.pm_callback = &usb_bus_suspend; 7992e141748SHans Petter Selasky bus->suspend_msg[1].bus = bus; 8002e141748SHans Petter Selasky 8012e141748SHans Petter Selasky bus->resume_msg[0].hdr.pm_callback = &usb_bus_resume; 8022e141748SHans Petter Selasky bus->resume_msg[0].bus = bus; 8032e141748SHans Petter Selasky bus->resume_msg[1].hdr.pm_callback = &usb_bus_resume; 8042e141748SHans Petter Selasky bus->resume_msg[1].bus = bus; 8052e141748SHans Petter Selasky 806563ab081SHans Petter Selasky bus->reset_msg[0].hdr.pm_callback = &usb_bus_reset; 807563ab081SHans Petter Selasky bus->reset_msg[0].bus = bus; 808563ab081SHans Petter Selasky bus->reset_msg[1].hdr.pm_callback = &usb_bus_reset; 809563ab081SHans Petter Selasky bus->reset_msg[1].bus = bus; 810563ab081SHans Petter Selasky 8112e141748SHans Petter Selasky bus->shutdown_msg[0].hdr.pm_callback = &usb_bus_shutdown; 8122e141748SHans Petter Selasky bus->shutdown_msg[0].bus = bus; 8132e141748SHans Petter Selasky bus->shutdown_msg[1].hdr.pm_callback = &usb_bus_shutdown; 8142e141748SHans Petter Selasky bus->shutdown_msg[1].bus = bus; 8152e141748SHans Petter Selasky 8169b3a48eeSHans Petter Selasky #if USB_HAVE_PER_BUS_PROCESS 81739307315SAndrew Thompson /* Create USB explore and callback processes */ 81802ac6454SAndrew Thompson 8199b3a48eeSHans Petter Selasky if (usb_proc_create(USB_BUS_GIANT_PROC(bus), 8209b3a48eeSHans Petter Selasky &bus->bus_mtx, device_get_nameunit(dev), USB_PRI_MED)) { 82188334428SHans Petter Selasky device_printf(dev, "WARNING: Creation of USB Giant " 82202ac6454SAndrew Thompson "callback process failed.\n"); 8239b3a48eeSHans Petter Selasky } else if (usb_proc_create(USB_BUS_NON_GIANT_PROC(bus), 8249b3a48eeSHans Petter Selasky &bus->bus_mtx, device_get_nameunit(dev), USB_PRI_HIGH)) { 82588334428SHans Petter Selasky device_printf(dev, "WARNING: Creation of USB non-Giant " 82602ac6454SAndrew Thompson "callback process failed.\n"); 8279b3a48eeSHans Petter Selasky } else if (usb_proc_create(USB_BUS_EXPLORE_PROC(bus), 8289b3a48eeSHans Petter Selasky &bus->bus_mtx, device_get_nameunit(dev), USB_PRI_MED)) { 82988334428SHans Petter Selasky device_printf(dev, "WARNING: Creation of USB explore " 83002ac6454SAndrew Thompson "process failed.\n"); 8319b3a48eeSHans Petter Selasky } else if (usb_proc_create(USB_BUS_CONTROL_XFER_PROC(bus), 8329b3a48eeSHans Petter Selasky &bus->bus_mtx, device_get_nameunit(dev), USB_PRI_MED)) { 83388334428SHans Petter Selasky device_printf(dev, "WARNING: Creation of USB control transfer " 834672c9965SAndrew Thompson "process failed.\n"); 8359b3a48eeSHans Petter Selasky } else 8369b3a48eeSHans Petter Selasky #endif 8379b3a48eeSHans Petter Selasky { 83802ac6454SAndrew Thompson /* Get final attach going */ 83902ac6454SAndrew Thompson USB_BUS_LOCK(bus); 8409b3a48eeSHans Petter Selasky usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), 8412e141748SHans Petter Selasky &bus->attach_msg[0], &bus->attach_msg[1]); 84202ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 84334b48722SAlfred Perlstein 84434b48722SAlfred Perlstein /* Do initial explore */ 84534b48722SAlfred Perlstein usb_needs_explore(bus, 1); 84602ac6454SAndrew Thompson } 84702ac6454SAndrew Thompson } 848a593f6b8SAndrew Thompson SYSUNINIT(usb_bus_unload, SI_SUB_KLD, SI_ORDER_ANY, usb_bus_unload, NULL); 84902ac6454SAndrew Thompson 85002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 851a593f6b8SAndrew Thompson * usb_bus_mem_flush_all_cb 85202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 853bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 85402ac6454SAndrew Thompson static void 855a593f6b8SAndrew Thompson usb_bus_mem_flush_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, 856f9cb546cSAndrew Thompson struct usb_page *pg, usb_size_t size, usb_size_t align) 85702ac6454SAndrew Thompson { 858a593f6b8SAndrew Thompson usb_pc_cpu_flush(pc); 85902ac6454SAndrew Thompson } 860bdc081c6SAndrew Thompson #endif 86102ac6454SAndrew Thompson 86202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 863a593f6b8SAndrew Thompson * usb_bus_mem_flush_all - factored out code 86402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 865bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 86602ac6454SAndrew Thompson void 867a593f6b8SAndrew Thompson usb_bus_mem_flush_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb) 86802ac6454SAndrew Thompson { 86902ac6454SAndrew Thompson if (cb) { 870a593f6b8SAndrew Thompson cb(bus, &usb_bus_mem_flush_all_cb); 87102ac6454SAndrew Thompson } 87202ac6454SAndrew Thompson } 873bdc081c6SAndrew Thompson #endif 87402ac6454SAndrew Thompson 87502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 876a593f6b8SAndrew Thompson * usb_bus_mem_alloc_all_cb 87702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 878bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 87902ac6454SAndrew Thompson static void 880a593f6b8SAndrew Thompson usb_bus_mem_alloc_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, 881f9cb546cSAndrew Thompson struct usb_page *pg, usb_size_t size, usb_size_t align) 88202ac6454SAndrew Thompson { 88302ac6454SAndrew Thompson /* need to initialize the page cache */ 88402ac6454SAndrew Thompson pc->tag_parent = bus->dma_parent_tag; 88502ac6454SAndrew Thompson 886a593f6b8SAndrew Thompson if (usb_pc_alloc_mem(pc, pg, size, align)) { 88702ac6454SAndrew Thompson bus->alloc_failed = 1; 88802ac6454SAndrew Thompson } 88902ac6454SAndrew Thompson } 890bdc081c6SAndrew Thompson #endif 89102ac6454SAndrew Thompson 89202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 893a593f6b8SAndrew Thompson * usb_bus_mem_alloc_all - factored out code 89402ac6454SAndrew Thompson * 89502ac6454SAndrew Thompson * Returns: 89602ac6454SAndrew Thompson * 0: Success 89702ac6454SAndrew Thompson * Else: Failure 89802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 89902ac6454SAndrew Thompson uint8_t 900a593f6b8SAndrew Thompson usb_bus_mem_alloc_all(struct usb_bus *bus, bus_dma_tag_t dmat, 901e0a69b51SAndrew Thompson usb_bus_mem_cb_t *cb) 90202ac6454SAndrew Thompson { 90302ac6454SAndrew Thompson bus->alloc_failed = 0; 90402ac6454SAndrew Thompson 90502ac6454SAndrew Thompson mtx_init(&bus->bus_mtx, device_get_nameunit(bus->parent), 9064a4da38fSHans Petter Selasky "usb_def_mtx", MTX_DEF | MTX_RECURSE); 90702ac6454SAndrew Thompson 9082fe7ad87SHans Petter Selasky mtx_init(&bus->bus_spin_lock, device_get_nameunit(bus->parent), 9094a4da38fSHans Petter Selasky "usb_spin_mtx", MTX_SPIN | MTX_RECURSE); 9102fe7ad87SHans Petter Selasky 911a593f6b8SAndrew Thompson usb_callout_init_mtx(&bus->power_wdog, 912684e3f22SAndrew Thompson &bus->bus_mtx, 0); 91302ac6454SAndrew Thompson 91402ac6454SAndrew Thompson TAILQ_INIT(&bus->intr_q.head); 91502ac6454SAndrew Thompson 916bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 917a593f6b8SAndrew Thompson usb_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags, 918bdc081c6SAndrew Thompson dmat, &bus->bus_mtx, NULL, 32, USB_BUS_DMA_TAG_MAX); 919bdc081c6SAndrew Thompson #endif 92002ac6454SAndrew Thompson if ((bus->devices_max > USB_MAX_DEVICES) || 92102ac6454SAndrew Thompson (bus->devices_max < USB_MIN_DEVICES) || 92202ac6454SAndrew Thompson (bus->devices == NULL)) { 92302ac6454SAndrew Thompson DPRINTFN(0, "Devices field has not been " 924767cb2e2SAndrew Thompson "initialised properly\n"); 92502ac6454SAndrew Thompson bus->alloc_failed = 1; /* failure */ 92602ac6454SAndrew Thompson } 927bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 92802ac6454SAndrew Thompson if (cb) { 929a593f6b8SAndrew Thompson cb(bus, &usb_bus_mem_alloc_all_cb); 93002ac6454SAndrew Thompson } 931bdc081c6SAndrew Thompson #endif 93202ac6454SAndrew Thompson if (bus->alloc_failed) { 933a593f6b8SAndrew Thompson usb_bus_mem_free_all(bus, cb); 93402ac6454SAndrew Thompson } 93502ac6454SAndrew Thompson return (bus->alloc_failed); 93602ac6454SAndrew Thompson } 93702ac6454SAndrew Thompson 93802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 939a593f6b8SAndrew Thompson * usb_bus_mem_free_all_cb 94002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 941bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 94202ac6454SAndrew Thompson static void 943a593f6b8SAndrew Thompson usb_bus_mem_free_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, 944f9cb546cSAndrew Thompson struct usb_page *pg, usb_size_t size, usb_size_t align) 94502ac6454SAndrew Thompson { 946a593f6b8SAndrew Thompson usb_pc_free_mem(pc); 94702ac6454SAndrew Thompson } 948bdc081c6SAndrew Thompson #endif 94902ac6454SAndrew Thompson 95002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 951a593f6b8SAndrew Thompson * usb_bus_mem_free_all - factored out code 95202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 95302ac6454SAndrew Thompson void 954a593f6b8SAndrew Thompson usb_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb) 95502ac6454SAndrew Thompson { 956bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 95702ac6454SAndrew Thompson if (cb) { 958a593f6b8SAndrew Thompson cb(bus, &usb_bus_mem_free_all_cb); 95902ac6454SAndrew Thompson } 960a593f6b8SAndrew Thompson usb_dma_tag_unsetup(bus->dma_parent_tag); 961bdc081c6SAndrew Thompson #endif 96202ac6454SAndrew Thompson 96302ac6454SAndrew Thompson mtx_destroy(&bus->bus_mtx); 9642fe7ad87SHans Petter Selasky mtx_destroy(&bus->bus_spin_lock); 96502ac6454SAndrew Thompson } 966751aaf5aSHans Petter Selasky 967751aaf5aSHans Petter Selasky /* convenience wrappers */ 968751aaf5aSHans Petter Selasky void 969751aaf5aSHans Petter Selasky usb_proc_explore_mwait(struct usb_device *udev, void *pm1, void *pm2) 970751aaf5aSHans Petter Selasky { 971751aaf5aSHans Petter Selasky usb_proc_mwait(USB_BUS_EXPLORE_PROC(udev->bus), pm1, pm2); 972751aaf5aSHans Petter Selasky } 973751aaf5aSHans Petter Selasky 974751aaf5aSHans Petter Selasky void * 975751aaf5aSHans Petter Selasky usb_proc_explore_msignal(struct usb_device *udev, void *pm1, void *pm2) 976751aaf5aSHans Petter Selasky { 977751aaf5aSHans Petter Selasky return (usb_proc_msignal(USB_BUS_EXPLORE_PROC(udev->bus), pm1, pm2)); 978751aaf5aSHans Petter Selasky } 979751aaf5aSHans Petter Selasky 980751aaf5aSHans Petter Selasky void 981751aaf5aSHans Petter Selasky usb_proc_explore_lock(struct usb_device *udev) 982751aaf5aSHans Petter Selasky { 983751aaf5aSHans Petter Selasky USB_BUS_LOCK(udev->bus); 984751aaf5aSHans Petter Selasky } 985751aaf5aSHans Petter Selasky 986751aaf5aSHans Petter Selasky void 987751aaf5aSHans Petter Selasky usb_proc_explore_unlock(struct usb_device *udev) 988751aaf5aSHans Petter Selasky { 989751aaf5aSHans Petter Selasky USB_BUS_UNLOCK(udev->bus); 990751aaf5aSHans Petter Selasky } 991