102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /*- 3718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 4718cf2ccSPedro F. Giffuni * 502ac6454SAndrew Thompson * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 602ac6454SAndrew Thompson * 702ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 802ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 902ac6454SAndrew Thompson * are met: 1002ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1202ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1302ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1402ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1502ac6454SAndrew Thompson * 1602ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1702ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1802ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1902ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2002ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2102ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2202ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2302ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2402ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2502ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2602ac6454SAndrew Thompson * SUCH DAMAGE. 2702ac6454SAndrew Thompson */ 2802ac6454SAndrew Thompson 29d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE 30d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE 31d2b99310SHans Petter Selasky #else 32f0c078e6SAndrew Thompson #include "opt_ddb.h" 33f0c078e6SAndrew Thompson 34ed6d949aSAndrew Thompson #include <sys/stdint.h> 35ed6d949aSAndrew Thompson #include <sys/stddef.h> 36ed6d949aSAndrew Thompson #include <sys/param.h> 37ed6d949aSAndrew Thompson #include <sys/queue.h> 38ed6d949aSAndrew Thompson #include <sys/types.h> 39ed6d949aSAndrew Thompson #include <sys/systm.h> 40ed6d949aSAndrew Thompson #include <sys/kernel.h> 41ed6d949aSAndrew Thompson #include <sys/bus.h> 42ed6d949aSAndrew Thompson #include <sys/module.h> 43ed6d949aSAndrew Thompson #include <sys/lock.h> 44ed6d949aSAndrew Thompson #include <sys/mutex.h> 45ed6d949aSAndrew Thompson #include <sys/condvar.h> 46ed6d949aSAndrew Thompson #include <sys/sysctl.h> 47ed6d949aSAndrew Thompson #include <sys/sx.h> 48ed6d949aSAndrew Thompson #include <sys/unistd.h> 49ed6d949aSAndrew Thompson #include <sys/callout.h> 50ed6d949aSAndrew Thompson #include <sys/malloc.h> 51ed6d949aSAndrew Thompson #include <sys/priv.h> 52ed6d949aSAndrew Thompson 5302ac6454SAndrew Thompson #include <dev/usb/usb.h> 54ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 5502ac6454SAndrew Thompson 56a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_ctrl_debug 5702ac6454SAndrew Thompson 5802ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 5902ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 6002ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 6102ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 6202ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h> 6302ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 64b78e84d1SHans Petter Selasky #include <dev/usb/usb_dev.h> 6502ac6454SAndrew Thompson #include <dev/usb/usb_hub.h> 6602ac6454SAndrew Thompson 6702ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 6802ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 6918ec6525SWeongyo Jeong #include <dev/usb/usb_pf.h> 702e141748SHans Petter Selasky #include "usb_if.h" 71d2b99310SHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */ 7202ac6454SAndrew Thompson 7302ac6454SAndrew Thompson /* function prototypes */ 7402ac6454SAndrew Thompson 75a593f6b8SAndrew Thompson static device_probe_t usb_probe; 76a593f6b8SAndrew Thompson static device_attach_t usb_attach; 77a593f6b8SAndrew Thompson static device_detach_t usb_detach; 782e141748SHans Petter Selasky static device_suspend_t usb_suspend; 792e141748SHans Petter Selasky static device_resume_t usb_resume; 802e141748SHans Petter Selasky static device_shutdown_t usb_shutdown; 8102ac6454SAndrew Thompson 82a593f6b8SAndrew Thompson static void usb_attach_sub(device_t, struct usb_bus *); 8302ac6454SAndrew Thompson 8402ac6454SAndrew Thompson /* static variables */ 8502ac6454SAndrew Thompson 86ed6d949aSAndrew Thompson #ifdef USB_DEBUG 87a593f6b8SAndrew Thompson static int usb_ctrl_debug = 0; 8802ac6454SAndrew Thompson 89f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, ctrl, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 90f8d2b1f3SPawel Biernacki "USB controller"); 91ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb_ctrl, OID_AUTO, debug, CTLFLAG_RWTUN, &usb_ctrl_debug, 0, 9202ac6454SAndrew Thompson "Debug level"); 9302ac6454SAndrew Thompson #endif 9402ac6454SAndrew Thompson 95d2b99310SHans Petter Selasky #if USB_HAVE_ROOT_MOUNT_HOLD 96a0c61406SAlfred Perlstein static int usb_no_boot_wait = 0; 97af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, no_boot_wait, CTLFLAG_RDTUN, &usb_no_boot_wait, 0, 985ed294aeSHans Petter Selasky "No USB device enumerate waiting at boot."); 99d2b99310SHans Petter Selasky #endif 1005ed294aeSHans Petter Selasky 10102f728afSHans Petter Selasky static int usb_no_suspend_wait = 0; 102af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, no_suspend_wait, CTLFLAG_RWTUN, 10302f728afSHans Petter Selasky &usb_no_suspend_wait, 0, "No USB device waiting at system suspend."); 10402f728afSHans Petter Selasky 1055ed294aeSHans Petter Selasky static int usb_no_shutdown_wait = 0; 106af3b2549SHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, no_shutdown_wait, CTLFLAG_RWTUN, 10702f728afSHans Petter Selasky &usb_no_shutdown_wait, 0, "No USB device waiting at system shutdown."); 108a0c61406SAlfred Perlstein 109a593f6b8SAndrew Thompson static device_method_t usb_methods[] = { 110a593f6b8SAndrew Thompson DEVMETHOD(device_probe, usb_probe), 111a593f6b8SAndrew Thompson DEVMETHOD(device_attach, usb_attach), 112a593f6b8SAndrew Thompson DEVMETHOD(device_detach, usb_detach), 1132e141748SHans Petter Selasky DEVMETHOD(device_suspend, usb_suspend), 1142e141748SHans Petter Selasky DEVMETHOD(device_resume, usb_resume), 1152e141748SHans Petter Selasky DEVMETHOD(device_shutdown, usb_shutdown), 11661bfd867SSofian Brabez 11761bfd867SSofian Brabez DEVMETHOD_END 11802ac6454SAndrew Thompson }; 11902ac6454SAndrew Thompson 120a593f6b8SAndrew Thompson static driver_t usb_driver = { 12102ac6454SAndrew Thompson .name = "usbus", 122a593f6b8SAndrew Thompson .methods = usb_methods, 12302ac6454SAndrew Thompson .size = 0, 12402ac6454SAndrew Thompson }; 12502ac6454SAndrew Thompson 126864bc412SHans Petter Selasky /* Host Only Drivers */ 127bc9372d7SJohn Baldwin DRIVER_MODULE(usbus, ohci, usb_driver, 0, 0); 128bc9372d7SJohn Baldwin DRIVER_MODULE(usbus, uhci, usb_driver, 0, 0); 129bc9372d7SJohn Baldwin DRIVER_MODULE(usbus, ehci, usb_driver, 0, 0); 130bc9372d7SJohn Baldwin DRIVER_MODULE(usbus, xhci, usb_driver, 0, 0); 131864bc412SHans Petter Selasky 132864bc412SHans Petter Selasky /* Device Only Drivers */ 133bc9372d7SJohn Baldwin DRIVER_MODULE(usbus, musbotg, usb_driver, 0, 0); 134bc9372d7SJohn Baldwin DRIVER_MODULE(usbus, uss820dci, usb_driver, 0, 0); 135bc9372d7SJohn Baldwin DRIVER_MODULE(usbus, octusb, usb_driver, 0, 0); 13602ac6454SAndrew Thompson 137268ae63aSHans Petter Selasky /* Dual Mode Drivers */ 138bc9372d7SJohn Baldwin DRIVER_MODULE(usbus, dwcotg, usb_driver, 0, 0); 139bc9372d7SJohn Baldwin DRIVER_MODULE(usbus, saf1761otg, usb_driver, 0, 0); 140268ae63aSHans Petter Selasky 14102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 142a593f6b8SAndrew Thompson * usb_probe 14302ac6454SAndrew Thompson * 14402ac6454SAndrew Thompson * This function is called from "{ehci,ohci,uhci}_pci_attach()". 14502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 14602ac6454SAndrew Thompson static int 147a593f6b8SAndrew Thompson usb_probe(device_t dev) 14802ac6454SAndrew Thompson { 14902ac6454SAndrew Thompson DPRINTF("\n"); 15002ac6454SAndrew Thompson return (0); 15102ac6454SAndrew Thompson } 15202ac6454SAndrew Thompson 153d2b99310SHans Petter Selasky #if USB_HAVE_ROOT_MOUNT_HOLD 1543df007ceSHans Petter Selasky static void 1553df007ceSHans Petter Selasky usb_root_mount_rel(struct usb_bus *bus) 1563df007ceSHans Petter Selasky { 1573df007ceSHans Petter Selasky if (bus->bus_roothold != NULL) { 1583df007ceSHans Petter Selasky DPRINTF("Releasing root mount hold %p\n", bus->bus_roothold); 1593df007ceSHans Petter Selasky root_mount_rel(bus->bus_roothold); 1603df007ceSHans Petter Selasky bus->bus_roothold = NULL; 1613df007ceSHans Petter Selasky } 1623df007ceSHans Petter Selasky } 163d2b99310SHans Petter Selasky #endif 1643df007ceSHans Petter Selasky 16502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 166a593f6b8SAndrew Thompson * usb_attach 16702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 16802ac6454SAndrew Thompson static int 169a593f6b8SAndrew Thompson usb_attach(device_t dev) 17002ac6454SAndrew Thompson { 171760bc48eSAndrew Thompson struct usb_bus *bus = device_get_ivars(dev); 17202ac6454SAndrew Thompson 17302ac6454SAndrew Thompson DPRINTF("\n"); 17402ac6454SAndrew Thompson 17502ac6454SAndrew Thompson if (bus == NULL) { 176767cb2e2SAndrew Thompson device_printf(dev, "USB device has no ivars\n"); 17702ac6454SAndrew Thompson return (ENXIO); 17802ac6454SAndrew Thompson } 17902ac6454SAndrew Thompson 180d2b99310SHans Petter Selasky #if USB_HAVE_ROOT_MOUNT_HOLD 181a0c61406SAlfred Perlstein if (usb_no_boot_wait == 0) { 18202ac6454SAndrew Thompson /* delay vfs_mountroot until the bus is explored */ 183853a10a5SAndrew Thompson bus->bus_roothold = root_mount_hold(device_get_nameunit(dev)); 184a0c61406SAlfred Perlstein } 185d2b99310SHans Petter Selasky #endif 186a593f6b8SAndrew Thompson usb_attach_sub(dev, bus); 18734b48722SAlfred Perlstein 18802ac6454SAndrew Thompson return (0); /* return success */ 18902ac6454SAndrew Thompson } 19002ac6454SAndrew Thompson 19102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 192a593f6b8SAndrew Thompson * usb_detach 19302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 19402ac6454SAndrew Thompson static int 195a593f6b8SAndrew Thompson usb_detach(device_t dev) 19602ac6454SAndrew Thompson { 197760bc48eSAndrew Thompson struct usb_bus *bus = device_get_softc(dev); 19802ac6454SAndrew Thompson 19902ac6454SAndrew Thompson DPRINTF("\n"); 20002ac6454SAndrew Thompson 20102ac6454SAndrew Thompson if (bus == NULL) { 20202ac6454SAndrew Thompson /* was never setup properly */ 20302ac6454SAndrew Thompson return (0); 20402ac6454SAndrew Thompson } 20502ac6454SAndrew Thompson /* Stop power watchdog */ 206a593f6b8SAndrew Thompson usb_callout_drain(&bus->power_wdog); 20702ac6454SAndrew Thompson 208d2b99310SHans Petter Selasky #if USB_HAVE_ROOT_MOUNT_HOLD 20902ac6454SAndrew Thompson /* Let the USB explore process detach all devices. */ 2103df007ceSHans Petter Selasky usb_root_mount_rel(bus); 211d2b99310SHans Petter Selasky #endif 21202ac6454SAndrew Thompson 21302ac6454SAndrew Thompson USB_BUS_LOCK(bus); 21402ac6454SAndrew Thompson 2152e141748SHans Petter Selasky /* Queue detach job */ 2169b3a48eeSHans Petter Selasky usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), 2172e141748SHans Petter Selasky &bus->detach_msg[0], &bus->detach_msg[1]); 2182e141748SHans Petter Selasky 2192e141748SHans Petter Selasky /* Wait for detach to complete */ 2209b3a48eeSHans Petter Selasky usb_proc_mwait(USB_BUS_EXPLORE_PROC(bus), 22102ac6454SAndrew Thompson &bus->detach_msg[0], &bus->detach_msg[1]); 22202ac6454SAndrew Thompson 223b78e84d1SHans Petter Selasky #if USB_HAVE_UGEN 224b78e84d1SHans Petter Selasky /* Wait for cleanup to complete */ 225b78e84d1SHans Petter Selasky usb_proc_mwait(USB_BUS_EXPLORE_PROC(bus), 226b78e84d1SHans Petter Selasky &bus->cleanup_msg[0], &bus->cleanup_msg[1]); 227b78e84d1SHans Petter Selasky #endif 22802ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 22902ac6454SAndrew Thompson 2309b3a48eeSHans Petter Selasky #if USB_HAVE_PER_BUS_PROCESS 23102ac6454SAndrew Thompson /* Get rid of USB callback processes */ 23202ac6454SAndrew Thompson 2339b3a48eeSHans Petter Selasky usb_proc_free(USB_BUS_GIANT_PROC(bus)); 23443ea03d7SHans Petter Selasky usb_proc_free(USB_BUS_NON_GIANT_ISOC_PROC(bus)); 23543ea03d7SHans Petter Selasky usb_proc_free(USB_BUS_NON_GIANT_BULK_PROC(bus)); 23602ac6454SAndrew Thompson 23702ac6454SAndrew Thompson /* Get rid of USB explore process */ 23802ac6454SAndrew Thompson 2399b3a48eeSHans Petter Selasky usb_proc_free(USB_BUS_EXPLORE_PROC(bus)); 24002ac6454SAndrew Thompson 241672c9965SAndrew Thompson /* Get rid of control transfer process */ 242672c9965SAndrew Thompson 2439b3a48eeSHans Petter Selasky usb_proc_free(USB_BUS_CONTROL_XFER_PROC(bus)); 2449b3a48eeSHans Petter Selasky #endif 245672c9965SAndrew Thompson 2468be09334SHans Petter Selasky #if USB_HAVE_PF 247fe1c24e3SWeongyo Jeong usbpf_detach(bus); 2488be09334SHans Petter Selasky #endif 24902ac6454SAndrew Thompson return (0); 25002ac6454SAndrew Thompson } 25102ac6454SAndrew Thompson 25202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 2532e141748SHans Petter Selasky * usb_suspend 2542e141748SHans Petter Selasky *------------------------------------------------------------------------*/ 2552e141748SHans Petter Selasky static int 2562e141748SHans Petter Selasky usb_suspend(device_t dev) 2572e141748SHans Petter Selasky { 2582e141748SHans Petter Selasky struct usb_bus *bus = device_get_softc(dev); 2592e141748SHans Petter Selasky 2602e141748SHans Petter Selasky DPRINTF("\n"); 2612e141748SHans Petter Selasky 2622e141748SHans Petter Selasky if (bus == NULL) { 2632e141748SHans Petter Selasky /* was never setup properly */ 2642e141748SHans Petter Selasky return (0); 2652e141748SHans Petter Selasky } 2662e141748SHans Petter Selasky 2672e141748SHans Petter Selasky USB_BUS_LOCK(bus); 2689b3a48eeSHans Petter Selasky usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), 2692e141748SHans Petter Selasky &bus->suspend_msg[0], &bus->suspend_msg[1]); 27002f728afSHans Petter Selasky if (usb_no_suspend_wait == 0) { 27102f728afSHans Petter Selasky /* wait for suspend callback to be executed */ 2729b3a48eeSHans Petter Selasky usb_proc_mwait(USB_BUS_EXPLORE_PROC(bus), 27302f728afSHans Petter Selasky &bus->suspend_msg[0], &bus->suspend_msg[1]); 27402f728afSHans Petter Selasky } 2752e141748SHans Petter Selasky USB_BUS_UNLOCK(bus); 2762e141748SHans Petter Selasky 2772e141748SHans Petter Selasky return (0); 2782e141748SHans Petter Selasky } 2792e141748SHans Petter Selasky 2802e141748SHans Petter Selasky /*------------------------------------------------------------------------* 2812e141748SHans Petter Selasky * usb_resume 2822e141748SHans Petter Selasky *------------------------------------------------------------------------*/ 2832e141748SHans Petter Selasky static int 2842e141748SHans Petter Selasky usb_resume(device_t dev) 2852e141748SHans Petter Selasky { 2862e141748SHans Petter Selasky struct usb_bus *bus = device_get_softc(dev); 2872e141748SHans Petter Selasky 2882e141748SHans Petter Selasky DPRINTF("\n"); 2892e141748SHans Petter Selasky 2902e141748SHans Petter Selasky if (bus == NULL) { 2912e141748SHans Petter Selasky /* was never setup properly */ 2922e141748SHans Petter Selasky return (0); 2932e141748SHans Petter Selasky } 2942e141748SHans Petter Selasky 2952e141748SHans Petter Selasky USB_BUS_LOCK(bus); 2969b3a48eeSHans Petter Selasky usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), 2972e141748SHans Petter Selasky &bus->resume_msg[0], &bus->resume_msg[1]); 2982e141748SHans Petter Selasky USB_BUS_UNLOCK(bus); 2992e141748SHans Petter Selasky 3002e141748SHans Petter Selasky return (0); 3012e141748SHans Petter Selasky } 3022e141748SHans Petter Selasky 3032e141748SHans Petter Selasky /*------------------------------------------------------------------------* 304563ab081SHans Petter Selasky * usb_bus_reset_async_locked 305563ab081SHans Petter Selasky *------------------------------------------------------------------------*/ 306563ab081SHans Petter Selasky void 307563ab081SHans Petter Selasky usb_bus_reset_async_locked(struct usb_bus *bus) 308563ab081SHans Petter Selasky { 309563ab081SHans Petter Selasky USB_BUS_LOCK_ASSERT(bus, MA_OWNED); 310563ab081SHans Petter Selasky 311563ab081SHans Petter Selasky DPRINTF("\n"); 312563ab081SHans Petter Selasky 313563ab081SHans Petter Selasky if (bus->reset_msg[0].hdr.pm_qentry.tqe_prev != NULL || 314563ab081SHans Petter Selasky bus->reset_msg[1].hdr.pm_qentry.tqe_prev != NULL) { 315563ab081SHans Petter Selasky DPRINTF("Reset already pending\n"); 316563ab081SHans Petter Selasky return; 317563ab081SHans Petter Selasky } 318563ab081SHans Petter Selasky 319563ab081SHans Petter Selasky device_printf(bus->parent, "Resetting controller\n"); 320563ab081SHans Petter Selasky 321563ab081SHans Petter Selasky usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), 322563ab081SHans Petter Selasky &bus->reset_msg[0], &bus->reset_msg[1]); 323563ab081SHans Petter Selasky } 324563ab081SHans Petter Selasky 325563ab081SHans Petter Selasky /*------------------------------------------------------------------------* 3262e141748SHans Petter Selasky * usb_shutdown 3272e141748SHans Petter Selasky *------------------------------------------------------------------------*/ 3282e141748SHans Petter Selasky static int 3292e141748SHans Petter Selasky usb_shutdown(device_t dev) 3302e141748SHans Petter Selasky { 3312e141748SHans Petter Selasky struct usb_bus *bus = device_get_softc(dev); 3322e141748SHans Petter Selasky 3332e141748SHans Petter Selasky DPRINTF("\n"); 3342e141748SHans Petter Selasky 3352e141748SHans Petter Selasky if (bus == NULL) { 3362e141748SHans Petter Selasky /* was never setup properly */ 3372e141748SHans Petter Selasky return (0); 3382e141748SHans Petter Selasky } 3392e141748SHans Petter Selasky 340cabe79d9SMarius Strobl DPRINTF("%s: Controller shutdown\n", device_get_nameunit(bus->bdev)); 3415ed294aeSHans Petter Selasky 3422e141748SHans Petter Selasky USB_BUS_LOCK(bus); 3439b3a48eeSHans Petter Selasky usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), 3442e141748SHans Petter Selasky &bus->shutdown_msg[0], &bus->shutdown_msg[1]); 3455ed294aeSHans Petter Selasky if (usb_no_shutdown_wait == 0) { 3465ed294aeSHans Petter Selasky /* wait for shutdown callback to be executed */ 3479b3a48eeSHans Petter Selasky usb_proc_mwait(USB_BUS_EXPLORE_PROC(bus), 3485ed294aeSHans Petter Selasky &bus->shutdown_msg[0], &bus->shutdown_msg[1]); 3495ed294aeSHans Petter Selasky } 3502e141748SHans Petter Selasky USB_BUS_UNLOCK(bus); 3512e141748SHans Petter Selasky 352cabe79d9SMarius Strobl DPRINTF("%s: Controller shutdown complete\n", 353cabe79d9SMarius Strobl device_get_nameunit(bus->bdev)); 3545ed294aeSHans Petter Selasky 3552e141748SHans Petter Selasky return (0); 3562e141748SHans Petter Selasky } 3572e141748SHans Petter Selasky 3582e141748SHans Petter Selasky /*------------------------------------------------------------------------* 359a593f6b8SAndrew Thompson * usb_bus_explore 36002ac6454SAndrew Thompson * 36102ac6454SAndrew Thompson * This function is used to explore the device tree from the root. 36202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 36302ac6454SAndrew Thompson static void 364a593f6b8SAndrew Thompson usb_bus_explore(struct usb_proc_msg *pm) 36502ac6454SAndrew Thompson { 366760bc48eSAndrew Thompson struct usb_bus *bus; 367760bc48eSAndrew Thompson struct usb_device *udev; 36802ac6454SAndrew Thompson 369760bc48eSAndrew Thompson bus = ((struct usb_bus_msg *)pm)->bus; 37002ac6454SAndrew Thompson udev = bus->devices[USB_ROOT_HUB_ADDR]; 37102ac6454SAndrew Thompson 3722e141748SHans Petter Selasky if (bus->no_explore != 0) 3732e141748SHans Petter Selasky return; 3742e141748SHans Petter Selasky 375d64e9217SHans Petter Selasky if (udev != NULL) { 376d64e9217SHans Petter Selasky USB_BUS_UNLOCK(bus); 377d64e9217SHans Petter Selasky uhub_explore_handle_re_enumerate(udev); 378d64e9217SHans Petter Selasky USB_BUS_LOCK(bus); 379d64e9217SHans Petter Selasky } 380d64e9217SHans Petter Selasky 381f80ccb40SHans Petter Selasky if (udev != NULL && udev->hub != NULL) { 38202ac6454SAndrew Thompson if (bus->do_probe) { 38302ac6454SAndrew Thompson bus->do_probe = 0; 38402ac6454SAndrew Thompson bus->driver_added_refcount++; 38502ac6454SAndrew Thompson } 38602ac6454SAndrew Thompson if (bus->driver_added_refcount == 0) { 38702ac6454SAndrew Thompson /* avoid zero, hence that is memory default */ 38802ac6454SAndrew Thompson bus->driver_added_refcount = 1; 38902ac6454SAndrew Thompson } 39002ac6454SAndrew Thompson 391f0c078e6SAndrew Thompson #ifdef DDB 39234b48722SAlfred Perlstein /* 39334b48722SAlfred Perlstein * The following three lines of code are only here to 39434b48722SAlfred Perlstein * recover from DDB: 39534b48722SAlfred Perlstein */ 3969b3a48eeSHans Petter Selasky usb_proc_rewakeup(USB_BUS_CONTROL_XFER_PROC(bus)); 3979b3a48eeSHans Petter Selasky usb_proc_rewakeup(USB_BUS_GIANT_PROC(bus)); 39843ea03d7SHans Petter Selasky usb_proc_rewakeup(USB_BUS_NON_GIANT_ISOC_PROC(bus)); 39943ea03d7SHans Petter Selasky usb_proc_rewakeup(USB_BUS_NON_GIANT_BULK_PROC(bus)); 400f0c078e6SAndrew Thompson #endif 40134b48722SAlfred Perlstein 40234b48722SAlfred Perlstein USB_BUS_UNLOCK(bus); 403a56fe095SJohn Baldwin 404e727a16cSAndrew Thompson #if USB_HAVE_POWERD 40502ac6454SAndrew Thompson /* 40602ac6454SAndrew Thompson * First update the USB power state! 40702ac6454SAndrew Thompson */ 408a593f6b8SAndrew Thompson usb_bus_powerd(bus); 409e727a16cSAndrew Thompson #endif 41034b48722SAlfred Perlstein /* Explore the Root USB HUB. */ 41102ac6454SAndrew Thompson (udev->hub->explore) (udev); 41202ac6454SAndrew Thompson USB_BUS_LOCK(bus); 41302ac6454SAndrew Thompson } 414d2b99310SHans Petter Selasky #if USB_HAVE_ROOT_MOUNT_HOLD 4153df007ceSHans Petter Selasky usb_root_mount_rel(bus); 416d2b99310SHans Petter Selasky #endif 41755a3bd00SHans Petter Selasky 41855a3bd00SHans Petter Selasky /* Nice the enumeration a bit, to avoid looping too fast. */ 419*8758aabbSHans Petter Selasky usb_pause_mtx(&bus->bus_mtx, USB_MS_TO_TICKS(usb_enum_nice_time)); 42002ac6454SAndrew Thompson } 42102ac6454SAndrew Thompson 42202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 423a593f6b8SAndrew Thompson * usb_bus_detach 42402ac6454SAndrew Thompson * 42502ac6454SAndrew Thompson * This function is used to detach the device tree from the root. 42602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 42702ac6454SAndrew Thompson static void 428a593f6b8SAndrew Thompson usb_bus_detach(struct usb_proc_msg *pm) 42902ac6454SAndrew Thompson { 430760bc48eSAndrew Thompson struct usb_bus *bus; 431760bc48eSAndrew Thompson struct usb_device *udev; 43202ac6454SAndrew Thompson device_t dev; 43302ac6454SAndrew Thompson 434760bc48eSAndrew Thompson bus = ((struct usb_bus_msg *)pm)->bus; 43502ac6454SAndrew Thompson udev = bus->devices[USB_ROOT_HUB_ADDR]; 43602ac6454SAndrew Thompson dev = bus->bdev; 43702ac6454SAndrew Thompson /* clear the softc */ 43802ac6454SAndrew Thompson device_set_softc(dev, NULL); 43902ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 44002ac6454SAndrew Thompson 44102ac6454SAndrew Thompson /* detach children first */ 442c6df6f53SWarner Losh bus_topo_lock(); 44302ac6454SAndrew Thompson bus_generic_detach(dev); 444c6df6f53SWarner Losh bus_topo_unlock(); 44502ac6454SAndrew Thompson 44602ac6454SAndrew Thompson /* 447d88688c7SAndrew Thompson * Free USB device and all subdevices, if any. 44802ac6454SAndrew Thompson */ 449d88688c7SAndrew Thompson usb_free_device(udev, 0); 45002ac6454SAndrew Thompson 45102ac6454SAndrew Thompson USB_BUS_LOCK(bus); 45202ac6454SAndrew Thompson /* clear bdev variable last */ 45302ac6454SAndrew Thompson bus->bdev = NULL; 45402ac6454SAndrew Thompson } 45502ac6454SAndrew Thompson 4562e141748SHans Petter Selasky /*------------------------------------------------------------------------* 4572e141748SHans Petter Selasky * usb_bus_suspend 4582e141748SHans Petter Selasky * 459468f354bSHans Petter Selasky * This function is used to suspend the USB controller. 4602e141748SHans Petter Selasky *------------------------------------------------------------------------*/ 4612e141748SHans Petter Selasky static void 4622e141748SHans Petter Selasky usb_bus_suspend(struct usb_proc_msg *pm) 4632e141748SHans Petter Selasky { 4642e141748SHans Petter Selasky struct usb_bus *bus; 4652e141748SHans Petter Selasky struct usb_device *udev; 4662e141748SHans Petter Selasky usb_error_t err; 467a18a7a41SHans Petter Selasky uint8_t do_unlock; 4682e141748SHans Petter Selasky 469563ab081SHans Petter Selasky DPRINTF("\n"); 470563ab081SHans Petter Selasky 4712e141748SHans Petter Selasky bus = ((struct usb_bus_msg *)pm)->bus; 4722e141748SHans Petter Selasky udev = bus->devices[USB_ROOT_HUB_ADDR]; 4732e141748SHans Petter Selasky 4742e141748SHans Petter Selasky if (udev == NULL || bus->bdev == NULL) 4752e141748SHans Petter Selasky return; 4762e141748SHans Petter Selasky 4776bd3e535SHans Petter Selasky USB_BUS_UNLOCK(bus); 4786bd3e535SHans Petter Selasky 47902f728afSHans Petter Selasky /* 48002f728afSHans Petter Selasky * We use the shutdown event here because the suspend and 48102f728afSHans Petter Selasky * resume events are reserved for the USB port suspend and 48202f728afSHans Petter Selasky * resume. The USB system suspend is implemented like full 48302f728afSHans Petter Selasky * shutdown and all connected USB devices will be disconnected 48402f728afSHans Petter Selasky * subsequently. At resume all USB devices will be 48502f728afSHans Petter Selasky * re-connected again. 48602f728afSHans Petter Selasky */ 48702f728afSHans Petter Selasky 4882e141748SHans Petter Selasky bus_generic_shutdown(bus->bdev); 4892e141748SHans Petter Selasky 490a18a7a41SHans Petter Selasky do_unlock = usbd_enum_lock(udev); 4912e141748SHans Petter Selasky 4922e141748SHans Petter Selasky err = usbd_set_config_index(udev, USB_UNCONFIG_INDEX); 4932e141748SHans Petter Selasky if (err) 4942e141748SHans Petter Selasky device_printf(bus->bdev, "Could not unconfigure root HUB\n"); 4952e141748SHans Petter Selasky 4962e141748SHans Petter Selasky USB_BUS_LOCK(bus); 4972e141748SHans Petter Selasky bus->hw_power_state = 0; 4982e141748SHans Petter Selasky bus->no_explore = 1; 4992e141748SHans Petter Selasky USB_BUS_UNLOCK(bus); 5002e141748SHans Petter Selasky 5012e141748SHans Petter Selasky if (bus->methods->set_hw_power != NULL) 5022e141748SHans Petter Selasky (bus->methods->set_hw_power) (bus); 5032e141748SHans Petter Selasky 5042e141748SHans Petter Selasky if (bus->methods->set_hw_power_sleep != NULL) 5052e141748SHans Petter Selasky (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_SUSPEND); 5062e141748SHans Petter Selasky 507a18a7a41SHans Petter Selasky if (do_unlock) 5082e141748SHans Petter Selasky usbd_enum_unlock(udev); 5096bd3e535SHans Petter Selasky 5106bd3e535SHans Petter Selasky USB_BUS_LOCK(bus); 5112e141748SHans Petter Selasky } 5122e141748SHans Petter Selasky 5132e141748SHans Petter Selasky /*------------------------------------------------------------------------* 5142e141748SHans Petter Selasky * usb_bus_resume 5152e141748SHans Petter Selasky * 516468f354bSHans Petter Selasky * This function is used to resume the USB controller. 5172e141748SHans Petter Selasky *------------------------------------------------------------------------*/ 5182e141748SHans Petter Selasky static void 5192e141748SHans Petter Selasky usb_bus_resume(struct usb_proc_msg *pm) 5202e141748SHans Petter Selasky { 5212e141748SHans Petter Selasky struct usb_bus *bus; 5222e141748SHans Petter Selasky struct usb_device *udev; 5232e141748SHans Petter Selasky usb_error_t err; 524a18a7a41SHans Petter Selasky uint8_t do_unlock; 5252e141748SHans Petter Selasky 526563ab081SHans Petter Selasky DPRINTF("\n"); 527563ab081SHans Petter Selasky 5282e141748SHans Petter Selasky bus = ((struct usb_bus_msg *)pm)->bus; 5292e141748SHans Petter Selasky udev = bus->devices[USB_ROOT_HUB_ADDR]; 5302e141748SHans Petter Selasky 5312e141748SHans Petter Selasky if (udev == NULL || bus->bdev == NULL) 5322e141748SHans Petter Selasky return; 5332e141748SHans Petter Selasky 5346bd3e535SHans Petter Selasky USB_BUS_UNLOCK(bus); 5356bd3e535SHans Petter Selasky 536a18a7a41SHans Petter Selasky do_unlock = usbd_enum_lock(udev); 5372e141748SHans Petter Selasky #if 0 5382e141748SHans Petter Selasky DEVMETHOD(usb_take_controller, NULL); /* dummy */ 5392e141748SHans Petter Selasky #endif 5402e141748SHans Petter Selasky USB_TAKE_CONTROLLER(device_get_parent(bus->bdev)); 5412e141748SHans Petter Selasky 5422e141748SHans Petter Selasky USB_BUS_LOCK(bus); 5432e141748SHans Petter Selasky bus->hw_power_state = 5442e141748SHans Petter Selasky USB_HW_POWER_CONTROL | 5452e141748SHans Petter Selasky USB_HW_POWER_BULK | 5462e141748SHans Petter Selasky USB_HW_POWER_INTERRUPT | 5472e141748SHans Petter Selasky USB_HW_POWER_ISOC | 5482e141748SHans Petter Selasky USB_HW_POWER_NON_ROOT_HUB; 5492e141748SHans Petter Selasky bus->no_explore = 0; 5502e141748SHans Petter Selasky USB_BUS_UNLOCK(bus); 5512e141748SHans Petter Selasky 5522e141748SHans Petter Selasky if (bus->methods->set_hw_power_sleep != NULL) 5532e141748SHans Petter Selasky (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_RESUME); 5542e141748SHans Petter Selasky 5552e141748SHans Petter Selasky if (bus->methods->set_hw_power != NULL) 5562e141748SHans Petter Selasky (bus->methods->set_hw_power) (bus); 5572e141748SHans Petter Selasky 5586bbe7cdfSHans Petter Selasky /* restore USB configuration to index 0 */ 5592e141748SHans Petter Selasky err = usbd_set_config_index(udev, 0); 5602e141748SHans Petter Selasky if (err) 5612e141748SHans Petter Selasky device_printf(bus->bdev, "Could not configure root HUB\n"); 5622e141748SHans Petter Selasky 5636bbe7cdfSHans Petter Selasky /* probe and attach */ 5646bbe7cdfSHans Petter Selasky err = usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY); 5656bbe7cdfSHans Petter Selasky if (err) { 5666bbe7cdfSHans Petter Selasky device_printf(bus->bdev, "Could not probe and " 5676bbe7cdfSHans Petter Selasky "attach root HUB\n"); 5686bbe7cdfSHans Petter Selasky } 5696bbe7cdfSHans Petter Selasky 570a18a7a41SHans Petter Selasky if (do_unlock) 5712e141748SHans Petter Selasky usbd_enum_unlock(udev); 5726bd3e535SHans Petter Selasky 5736bd3e535SHans Petter Selasky USB_BUS_LOCK(bus); 5742e141748SHans Petter Selasky } 5752e141748SHans Petter Selasky 5762e141748SHans Petter Selasky /*------------------------------------------------------------------------* 577563ab081SHans Petter Selasky * usb_bus_reset 578563ab081SHans Petter Selasky * 579468f354bSHans Petter Selasky * This function is used to reset the USB controller. 580563ab081SHans Petter Selasky *------------------------------------------------------------------------*/ 581563ab081SHans Petter Selasky static void 582563ab081SHans Petter Selasky usb_bus_reset(struct usb_proc_msg *pm) 583563ab081SHans Petter Selasky { 584563ab081SHans Petter Selasky struct usb_bus *bus; 585563ab081SHans Petter Selasky 586563ab081SHans Petter Selasky DPRINTF("\n"); 587563ab081SHans Petter Selasky 588563ab081SHans Petter Selasky bus = ((struct usb_bus_msg *)pm)->bus; 589563ab081SHans Petter Selasky 590563ab081SHans Petter Selasky if (bus->bdev == NULL || bus->no_explore != 0) 591563ab081SHans Petter Selasky return; 592563ab081SHans Petter Selasky 593563ab081SHans Petter Selasky /* a suspend and resume will reset the USB controller */ 594563ab081SHans Petter Selasky usb_bus_suspend(pm); 595563ab081SHans Petter Selasky usb_bus_resume(pm); 596563ab081SHans Petter Selasky } 597563ab081SHans Petter Selasky 598563ab081SHans Petter Selasky /*------------------------------------------------------------------------* 5992e141748SHans Petter Selasky * usb_bus_shutdown 6002e141748SHans Petter Selasky * 601468f354bSHans Petter Selasky * This function is used to shutdown the USB controller. 6022e141748SHans Petter Selasky *------------------------------------------------------------------------*/ 6032e141748SHans Petter Selasky static void 6042e141748SHans Petter Selasky usb_bus_shutdown(struct usb_proc_msg *pm) 6052e141748SHans Petter Selasky { 6062e141748SHans Petter Selasky struct usb_bus *bus; 6072e141748SHans Petter Selasky struct usb_device *udev; 6082e141748SHans Petter Selasky usb_error_t err; 609a18a7a41SHans Petter Selasky uint8_t do_unlock; 6102e141748SHans Petter Selasky 6112e141748SHans Petter Selasky bus = ((struct usb_bus_msg *)pm)->bus; 6122e141748SHans Petter Selasky udev = bus->devices[USB_ROOT_HUB_ADDR]; 6132e141748SHans Petter Selasky 6142e141748SHans Petter Selasky if (udev == NULL || bus->bdev == NULL) 6152e141748SHans Petter Selasky return; 6162e141748SHans Petter Selasky 6176bd3e535SHans Petter Selasky USB_BUS_UNLOCK(bus); 6186bd3e535SHans Petter Selasky 6192e141748SHans Petter Selasky bus_generic_shutdown(bus->bdev); 6202e141748SHans Petter Selasky 621a18a7a41SHans Petter Selasky do_unlock = usbd_enum_lock(udev); 6222e141748SHans Petter Selasky 6232e141748SHans Petter Selasky err = usbd_set_config_index(udev, USB_UNCONFIG_INDEX); 6242e141748SHans Petter Selasky if (err) 6252e141748SHans Petter Selasky device_printf(bus->bdev, "Could not unconfigure root HUB\n"); 6262e141748SHans Petter Selasky 6272e141748SHans Petter Selasky USB_BUS_LOCK(bus); 6282e141748SHans Petter Selasky bus->hw_power_state = 0; 6292e141748SHans Petter Selasky bus->no_explore = 1; 6302e141748SHans Petter Selasky USB_BUS_UNLOCK(bus); 6312e141748SHans Petter Selasky 6322e141748SHans Petter Selasky if (bus->methods->set_hw_power != NULL) 6332e141748SHans Petter Selasky (bus->methods->set_hw_power) (bus); 6342e141748SHans Petter Selasky 6352e141748SHans Petter Selasky if (bus->methods->set_hw_power_sleep != NULL) 6362e141748SHans Petter Selasky (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_SHUTDOWN); 6372e141748SHans Petter Selasky 638a18a7a41SHans Petter Selasky if (do_unlock) 6392e141748SHans Petter Selasky usbd_enum_unlock(udev); 6406bd3e535SHans Petter Selasky 6416bd3e535SHans Petter Selasky USB_BUS_LOCK(bus); 6422e141748SHans Petter Selasky } 6432e141748SHans Petter Selasky 644b78e84d1SHans Petter Selasky /*------------------------------------------------------------------------* 645b78e84d1SHans Petter Selasky * usb_bus_cleanup 646b78e84d1SHans Petter Selasky * 647b78e84d1SHans Petter Selasky * This function is used to cleanup leftover USB character devices. 648b78e84d1SHans Petter Selasky *------------------------------------------------------------------------*/ 649b78e84d1SHans Petter Selasky #if USB_HAVE_UGEN 650b78e84d1SHans Petter Selasky static void 651b78e84d1SHans Petter Selasky usb_bus_cleanup(struct usb_proc_msg *pm) 652b78e84d1SHans Petter Selasky { 653b78e84d1SHans Petter Selasky struct usb_bus *bus; 654b78e84d1SHans Petter Selasky struct usb_fs_privdata *pd; 655b78e84d1SHans Petter Selasky 656b78e84d1SHans Petter Selasky bus = ((struct usb_bus_msg *)pm)->bus; 657b78e84d1SHans Petter Selasky 658b78e84d1SHans Petter Selasky while ((pd = LIST_FIRST(&bus->pd_cleanup_list)) != NULL) { 659b78e84d1SHans Petter Selasky LIST_REMOVE(pd, pd_next); 660b78e84d1SHans Petter Selasky USB_BUS_UNLOCK(bus); 661b78e84d1SHans Petter Selasky 662b78e84d1SHans Petter Selasky usb_destroy_dev_sync(pd); 663b78e84d1SHans Petter Selasky 664b78e84d1SHans Petter Selasky USB_BUS_LOCK(bus); 665b78e84d1SHans Petter Selasky } 666b78e84d1SHans Petter Selasky } 667b78e84d1SHans Petter Selasky #endif 668b78e84d1SHans Petter Selasky 66902ac6454SAndrew Thompson static void 670a593f6b8SAndrew Thompson usb_power_wdog(void *arg) 67102ac6454SAndrew Thompson { 672760bc48eSAndrew Thompson struct usb_bus *bus = arg; 67302ac6454SAndrew Thompson 67402ac6454SAndrew Thompson USB_BUS_LOCK_ASSERT(bus, MA_OWNED); 67502ac6454SAndrew Thompson 676a593f6b8SAndrew Thompson usb_callout_reset(&bus->power_wdog, 677a593f6b8SAndrew Thompson 4 * hz, usb_power_wdog, arg); 67802ac6454SAndrew Thompson 679f0c078e6SAndrew Thompson #ifdef DDB 68034b48722SAlfred Perlstein /* 68134b48722SAlfred Perlstein * The following line of code is only here to recover from 68234b48722SAlfred Perlstein * DDB: 68334b48722SAlfred Perlstein */ 6849b3a48eeSHans Petter Selasky usb_proc_rewakeup(USB_BUS_EXPLORE_PROC(bus)); /* recover from DDB */ 685f0c078e6SAndrew Thompson #endif 68634b48722SAlfred Perlstein 687e727a16cSAndrew Thompson #if USB_HAVE_POWERD 68802ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 68902ac6454SAndrew Thompson 690a593f6b8SAndrew Thompson usb_bus_power_update(bus); 69102ac6454SAndrew Thompson 692684e3f22SAndrew Thompson USB_BUS_LOCK(bus); 693e727a16cSAndrew Thompson #endif 69402ac6454SAndrew Thompson } 69502ac6454SAndrew Thompson 69602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 697a593f6b8SAndrew Thompson * usb_bus_attach 69802ac6454SAndrew Thompson * 69902ac6454SAndrew Thompson * This function attaches USB in context of the explore thread. 70002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 70102ac6454SAndrew Thompson static void 702a593f6b8SAndrew Thompson usb_bus_attach(struct usb_proc_msg *pm) 70302ac6454SAndrew Thompson { 704760bc48eSAndrew Thompson struct usb_bus *bus; 705760bc48eSAndrew Thompson struct usb_device *child; 70602ac6454SAndrew Thompson device_t dev; 707e0a69b51SAndrew Thompson usb_error_t err; 7088d2dd5ddSAndrew Thompson enum usb_dev_speed speed; 70902ac6454SAndrew Thompson 710760bc48eSAndrew Thompson bus = ((struct usb_bus_msg *)pm)->bus; 71102ac6454SAndrew Thompson dev = bus->bdev; 71202ac6454SAndrew Thompson 71302ac6454SAndrew Thompson DPRINTF("\n"); 71402ac6454SAndrew Thompson 71502ac6454SAndrew Thompson switch (bus->usbrev) { 71602ac6454SAndrew Thompson case USB_REV_1_0: 71702ac6454SAndrew Thompson speed = USB_SPEED_FULL; 71802ac6454SAndrew Thompson device_printf(bus->bdev, "12Mbps Full Speed USB v1.0\n"); 71902ac6454SAndrew Thompson break; 72002ac6454SAndrew Thompson 72102ac6454SAndrew Thompson case USB_REV_1_1: 72202ac6454SAndrew Thompson speed = USB_SPEED_FULL; 72302ac6454SAndrew Thompson device_printf(bus->bdev, "12Mbps Full Speed USB v1.1\n"); 72402ac6454SAndrew Thompson break; 72502ac6454SAndrew Thompson 72602ac6454SAndrew Thompson case USB_REV_2_0: 72702ac6454SAndrew Thompson speed = USB_SPEED_HIGH; 72802ac6454SAndrew Thompson device_printf(bus->bdev, "480Mbps High Speed USB v2.0\n"); 72902ac6454SAndrew Thompson break; 73002ac6454SAndrew Thompson 73102ac6454SAndrew Thompson case USB_REV_2_5: 73202ac6454SAndrew Thompson speed = USB_SPEED_VARIABLE; 73302ac6454SAndrew Thompson device_printf(bus->bdev, "480Mbps Wireless USB v2.5\n"); 73402ac6454SAndrew Thompson break; 73502ac6454SAndrew Thompson 736963169b4SHans Petter Selasky case USB_REV_3_0: 737963169b4SHans Petter Selasky speed = USB_SPEED_SUPER; 738ccac019aSHans Petter Selasky device_printf(bus->bdev, "5.0Gbps Super Speed USB v3.0\n"); 739963169b4SHans Petter Selasky break; 740963169b4SHans Petter Selasky 74102ac6454SAndrew Thompson default: 742767cb2e2SAndrew Thompson device_printf(bus->bdev, "Unsupported USB revision\n"); 743d2b99310SHans Petter Selasky #if USB_HAVE_ROOT_MOUNT_HOLD 7443df007ceSHans Petter Selasky usb_root_mount_rel(bus); 745d2b99310SHans Petter Selasky #endif 74602ac6454SAndrew Thompson return; 74702ac6454SAndrew Thompson } 74802ac6454SAndrew Thompson 7494eae601eSAndrew Thompson /* default power_mask value */ 7504eae601eSAndrew Thompson bus->hw_power_state = 7514eae601eSAndrew Thompson USB_HW_POWER_CONTROL | 7524eae601eSAndrew Thompson USB_HW_POWER_BULK | 7534eae601eSAndrew Thompson USB_HW_POWER_INTERRUPT | 7544eae601eSAndrew Thompson USB_HW_POWER_ISOC | 7554eae601eSAndrew Thompson USB_HW_POWER_NON_ROOT_HUB; 7564eae601eSAndrew Thompson 7572e141748SHans Petter Selasky USB_BUS_UNLOCK(bus); 7582e141748SHans Petter Selasky 7594eae601eSAndrew Thompson /* make sure power is set at least once */ 7604eae601eSAndrew Thompson 7614eae601eSAndrew Thompson if (bus->methods->set_hw_power != NULL) { 7624eae601eSAndrew Thompson (bus->methods->set_hw_power) (bus); 7634eae601eSAndrew Thompson } 7644eae601eSAndrew Thompson 7652e141748SHans Petter Selasky /* allocate the Root USB device */ 76602ac6454SAndrew Thompson 767a593f6b8SAndrew Thompson child = usb_alloc_device(bus->bdev, bus, NULL, 0, 0, 1, 76802ac6454SAndrew Thompson speed, USB_MODE_HOST); 76902ac6454SAndrew Thompson if (child) { 770a593f6b8SAndrew Thompson err = usb_probe_and_attach(child, 77102ac6454SAndrew Thompson USB_IFACE_INDEX_ANY); 77202ac6454SAndrew Thompson if (!err) { 7731be5bf51SAndrew Thompson if ((bus->devices[USB_ROOT_HUB_ADDR] == NULL) || 7741be5bf51SAndrew Thompson (bus->devices[USB_ROOT_HUB_ADDR]->hub == NULL)) { 77502ac6454SAndrew Thompson err = USB_ERR_NO_ROOT_HUB; 77602ac6454SAndrew Thompson } 77702ac6454SAndrew Thompson } 77802ac6454SAndrew Thompson } else { 77902ac6454SAndrew Thompson err = USB_ERR_NOMEM; 78002ac6454SAndrew Thompson } 78102ac6454SAndrew Thompson 78202ac6454SAndrew Thompson USB_BUS_LOCK(bus); 78302ac6454SAndrew Thompson 78402ac6454SAndrew Thompson if (err) { 78502ac6454SAndrew Thompson device_printf(bus->bdev, "Root HUB problem, error=%s\n", 786a593f6b8SAndrew Thompson usbd_errstr(err)); 787d2b99310SHans Petter Selasky #if USB_HAVE_ROOT_MOUNT_HOLD 7883df007ceSHans Petter Selasky usb_root_mount_rel(bus); 789d2b99310SHans Petter Selasky #endif 79002ac6454SAndrew Thompson } 79102ac6454SAndrew Thompson 79202ac6454SAndrew Thompson /* set softc - we are ready */ 79302ac6454SAndrew Thompson device_set_softc(dev, bus); 79402ac6454SAndrew Thompson 795684e3f22SAndrew Thompson /* start watchdog */ 796a593f6b8SAndrew Thompson usb_power_wdog(bus); 79702ac6454SAndrew Thompson } 79802ac6454SAndrew Thompson 79902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 800a593f6b8SAndrew Thompson * usb_attach_sub 80102ac6454SAndrew Thompson * 80234b48722SAlfred Perlstein * This function creates a thread which runs the USB attach code. 80302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 80402ac6454SAndrew Thompson static void 805a593f6b8SAndrew Thompson usb_attach_sub(device_t dev, struct usb_bus *bus) 80602ac6454SAndrew Thompson { 807c6df6f53SWarner Losh bus_topo_lock(); 80834b48722SAlfred Perlstein if (usb_devclass_ptr == NULL) 80934b48722SAlfred Perlstein usb_devclass_ptr = devclass_find("usbus"); 810c6df6f53SWarner Losh bus_topo_unlock(); 81134b48722SAlfred Perlstein 8128be09334SHans Petter Selasky #if USB_HAVE_PF 813fe1c24e3SWeongyo Jeong usbpf_attach(bus); 8148be09334SHans Petter Selasky #endif 81502ac6454SAndrew Thompson /* Initialise USB process messages */ 816a593f6b8SAndrew Thompson bus->explore_msg[0].hdr.pm_callback = &usb_bus_explore; 81702ac6454SAndrew Thompson bus->explore_msg[0].bus = bus; 818a593f6b8SAndrew Thompson bus->explore_msg[1].hdr.pm_callback = &usb_bus_explore; 81902ac6454SAndrew Thompson bus->explore_msg[1].bus = bus; 82002ac6454SAndrew Thompson 821a593f6b8SAndrew Thompson bus->detach_msg[0].hdr.pm_callback = &usb_bus_detach; 82202ac6454SAndrew Thompson bus->detach_msg[0].bus = bus; 823a593f6b8SAndrew Thompson bus->detach_msg[1].hdr.pm_callback = &usb_bus_detach; 82402ac6454SAndrew Thompson bus->detach_msg[1].bus = bus; 82502ac6454SAndrew Thompson 826a593f6b8SAndrew Thompson bus->attach_msg[0].hdr.pm_callback = &usb_bus_attach; 82702ac6454SAndrew Thompson bus->attach_msg[0].bus = bus; 828a593f6b8SAndrew Thompson bus->attach_msg[1].hdr.pm_callback = &usb_bus_attach; 82902ac6454SAndrew Thompson bus->attach_msg[1].bus = bus; 83002ac6454SAndrew Thompson 8312e141748SHans Petter Selasky bus->suspend_msg[0].hdr.pm_callback = &usb_bus_suspend; 8322e141748SHans Petter Selasky bus->suspend_msg[0].bus = bus; 8332e141748SHans Petter Selasky bus->suspend_msg[1].hdr.pm_callback = &usb_bus_suspend; 8342e141748SHans Petter Selasky bus->suspend_msg[1].bus = bus; 8352e141748SHans Petter Selasky 8362e141748SHans Petter Selasky bus->resume_msg[0].hdr.pm_callback = &usb_bus_resume; 8372e141748SHans Petter Selasky bus->resume_msg[0].bus = bus; 8382e141748SHans Petter Selasky bus->resume_msg[1].hdr.pm_callback = &usb_bus_resume; 8392e141748SHans Petter Selasky bus->resume_msg[1].bus = bus; 8402e141748SHans Petter Selasky 841563ab081SHans Petter Selasky bus->reset_msg[0].hdr.pm_callback = &usb_bus_reset; 842563ab081SHans Petter Selasky bus->reset_msg[0].bus = bus; 843563ab081SHans Petter Selasky bus->reset_msg[1].hdr.pm_callback = &usb_bus_reset; 844563ab081SHans Petter Selasky bus->reset_msg[1].bus = bus; 845563ab081SHans Petter Selasky 8462e141748SHans Petter Selasky bus->shutdown_msg[0].hdr.pm_callback = &usb_bus_shutdown; 8472e141748SHans Petter Selasky bus->shutdown_msg[0].bus = bus; 8482e141748SHans Petter Selasky bus->shutdown_msg[1].hdr.pm_callback = &usb_bus_shutdown; 8492e141748SHans Petter Selasky bus->shutdown_msg[1].bus = bus; 8502e141748SHans Petter Selasky 851b78e84d1SHans Petter Selasky #if USB_HAVE_UGEN 852b78e84d1SHans Petter Selasky LIST_INIT(&bus->pd_cleanup_list); 853b78e84d1SHans Petter Selasky bus->cleanup_msg[0].hdr.pm_callback = &usb_bus_cleanup; 854b78e84d1SHans Petter Selasky bus->cleanup_msg[0].bus = bus; 855b78e84d1SHans Petter Selasky bus->cleanup_msg[1].hdr.pm_callback = &usb_bus_cleanup; 856b78e84d1SHans Petter Selasky bus->cleanup_msg[1].bus = bus; 857b78e84d1SHans Petter Selasky #endif 858b78e84d1SHans Petter Selasky 8599b3a48eeSHans Petter Selasky #if USB_HAVE_PER_BUS_PROCESS 86039307315SAndrew Thompson /* Create USB explore and callback processes */ 86102ac6454SAndrew Thompson 8629b3a48eeSHans Petter Selasky if (usb_proc_create(USB_BUS_GIANT_PROC(bus), 8639b3a48eeSHans Petter Selasky &bus->bus_mtx, device_get_nameunit(dev), USB_PRI_MED)) { 86488334428SHans Petter Selasky device_printf(dev, "WARNING: Creation of USB Giant " 86502ac6454SAndrew Thompson "callback process failed.\n"); 86643ea03d7SHans Petter Selasky } else if (usb_proc_create(USB_BUS_NON_GIANT_ISOC_PROC(bus), 86743ea03d7SHans Petter Selasky &bus->bus_mtx, device_get_nameunit(dev), USB_PRI_HIGHEST)) { 86843ea03d7SHans Petter Selasky device_printf(dev, "WARNING: Creation of USB non-Giant ISOC " 86943ea03d7SHans Petter Selasky "callback process failed.\n"); 87043ea03d7SHans Petter Selasky } else if (usb_proc_create(USB_BUS_NON_GIANT_BULK_PROC(bus), 8719b3a48eeSHans Petter Selasky &bus->bus_mtx, device_get_nameunit(dev), USB_PRI_HIGH)) { 87243ea03d7SHans Petter Selasky device_printf(dev, "WARNING: Creation of USB non-Giant BULK " 87302ac6454SAndrew Thompson "callback process failed.\n"); 8749b3a48eeSHans Petter Selasky } else if (usb_proc_create(USB_BUS_EXPLORE_PROC(bus), 8759b3a48eeSHans Petter Selasky &bus->bus_mtx, device_get_nameunit(dev), USB_PRI_MED)) { 87688334428SHans Petter Selasky device_printf(dev, "WARNING: Creation of USB explore " 87702ac6454SAndrew Thompson "process failed.\n"); 8789b3a48eeSHans Petter Selasky } else if (usb_proc_create(USB_BUS_CONTROL_XFER_PROC(bus), 8799b3a48eeSHans Petter Selasky &bus->bus_mtx, device_get_nameunit(dev), USB_PRI_MED)) { 88088334428SHans Petter Selasky device_printf(dev, "WARNING: Creation of USB control transfer " 881672c9965SAndrew Thompson "process failed.\n"); 8829b3a48eeSHans Petter Selasky } else 8839b3a48eeSHans Petter Selasky #endif 8849b3a48eeSHans Petter Selasky { 88502ac6454SAndrew Thompson /* Get final attach going */ 88602ac6454SAndrew Thompson USB_BUS_LOCK(bus); 8879b3a48eeSHans Petter Selasky usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus), 8882e141748SHans Petter Selasky &bus->attach_msg[0], &bus->attach_msg[1]); 88902ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 89034b48722SAlfred Perlstein 89134b48722SAlfred Perlstein /* Do initial explore */ 89234b48722SAlfred Perlstein usb_needs_explore(bus, 1); 89302ac6454SAndrew Thompson } 89402ac6454SAndrew Thompson } 895a593f6b8SAndrew Thompson SYSUNINIT(usb_bus_unload, SI_SUB_KLD, SI_ORDER_ANY, usb_bus_unload, NULL); 89602ac6454SAndrew Thompson 89702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 898a593f6b8SAndrew Thompson * usb_bus_mem_flush_all_cb 89902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 900bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 90102ac6454SAndrew Thompson static void 902a593f6b8SAndrew Thompson usb_bus_mem_flush_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, 903f9cb546cSAndrew Thompson struct usb_page *pg, usb_size_t size, usb_size_t align) 90402ac6454SAndrew Thompson { 905a593f6b8SAndrew Thompson usb_pc_cpu_flush(pc); 90602ac6454SAndrew Thompson } 907bdc081c6SAndrew Thompson #endif 90802ac6454SAndrew Thompson 90902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 910a593f6b8SAndrew Thompson * usb_bus_mem_flush_all - factored out code 91102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 912bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 91302ac6454SAndrew Thompson void 914a593f6b8SAndrew Thompson usb_bus_mem_flush_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb) 91502ac6454SAndrew Thompson { 91602ac6454SAndrew Thompson if (cb) { 917a593f6b8SAndrew Thompson cb(bus, &usb_bus_mem_flush_all_cb); 91802ac6454SAndrew Thompson } 91902ac6454SAndrew Thompson } 920bdc081c6SAndrew Thompson #endif 92102ac6454SAndrew Thompson 92202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 923a593f6b8SAndrew Thompson * usb_bus_mem_alloc_all_cb 92402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 925bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 92602ac6454SAndrew Thompson static void 927a593f6b8SAndrew Thompson usb_bus_mem_alloc_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, 928f9cb546cSAndrew Thompson struct usb_page *pg, usb_size_t size, usb_size_t align) 92902ac6454SAndrew Thompson { 93002ac6454SAndrew Thompson /* need to initialize the page cache */ 93102ac6454SAndrew Thompson pc->tag_parent = bus->dma_parent_tag; 93202ac6454SAndrew Thompson 933a593f6b8SAndrew Thompson if (usb_pc_alloc_mem(pc, pg, size, align)) { 93402ac6454SAndrew Thompson bus->alloc_failed = 1; 93502ac6454SAndrew Thompson } 93602ac6454SAndrew Thompson } 937bdc081c6SAndrew Thompson #endif 93802ac6454SAndrew Thompson 93902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 940a593f6b8SAndrew Thompson * usb_bus_mem_alloc_all - factored out code 94102ac6454SAndrew Thompson * 94202ac6454SAndrew Thompson * Returns: 94302ac6454SAndrew Thompson * 0: Success 94402ac6454SAndrew Thompson * Else: Failure 94502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 94602ac6454SAndrew Thompson uint8_t 947a593f6b8SAndrew Thompson usb_bus_mem_alloc_all(struct usb_bus *bus, bus_dma_tag_t dmat, 948e0a69b51SAndrew Thompson usb_bus_mem_cb_t *cb) 94902ac6454SAndrew Thompson { 95002ac6454SAndrew Thompson bus->alloc_failed = 0; 95102ac6454SAndrew Thompson 95202ac6454SAndrew Thompson mtx_init(&bus->bus_mtx, device_get_nameunit(bus->parent), 9534a4da38fSHans Petter Selasky "usb_def_mtx", MTX_DEF | MTX_RECURSE); 95402ac6454SAndrew Thompson 9552fe7ad87SHans Petter Selasky mtx_init(&bus->bus_spin_lock, device_get_nameunit(bus->parent), 9564a4da38fSHans Petter Selasky "usb_spin_mtx", MTX_SPIN | MTX_RECURSE); 9572fe7ad87SHans Petter Selasky 958a593f6b8SAndrew Thompson usb_callout_init_mtx(&bus->power_wdog, 959684e3f22SAndrew Thompson &bus->bus_mtx, 0); 96002ac6454SAndrew Thompson 96102ac6454SAndrew Thompson TAILQ_INIT(&bus->intr_q.head); 96202ac6454SAndrew Thompson 963bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 964a593f6b8SAndrew Thompson usb_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags, 965b217d184SHans Petter Selasky dmat, &bus->bus_mtx, NULL, bus->dma_bits, USB_BUS_DMA_TAG_MAX); 966bdc081c6SAndrew Thompson #endif 96702ac6454SAndrew Thompson if ((bus->devices_max > USB_MAX_DEVICES) || 96802ac6454SAndrew Thompson (bus->devices_max < USB_MIN_DEVICES) || 96902ac6454SAndrew Thompson (bus->devices == NULL)) { 97002ac6454SAndrew Thompson DPRINTFN(0, "Devices field has not been " 971767cb2e2SAndrew Thompson "initialised properly\n"); 97202ac6454SAndrew Thompson bus->alloc_failed = 1; /* failure */ 97302ac6454SAndrew Thompson } 974bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 97502ac6454SAndrew Thompson if (cb) { 976a593f6b8SAndrew Thompson cb(bus, &usb_bus_mem_alloc_all_cb); 97702ac6454SAndrew Thompson } 978bdc081c6SAndrew Thompson #endif 97902ac6454SAndrew Thompson if (bus->alloc_failed) { 980a593f6b8SAndrew Thompson usb_bus_mem_free_all(bus, cb); 98102ac6454SAndrew Thompson } 98202ac6454SAndrew Thompson return (bus->alloc_failed); 98302ac6454SAndrew Thompson } 98402ac6454SAndrew Thompson 98502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 986a593f6b8SAndrew Thompson * usb_bus_mem_free_all_cb 98702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 988bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 98902ac6454SAndrew Thompson static void 990a593f6b8SAndrew Thompson usb_bus_mem_free_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, 991f9cb546cSAndrew Thompson struct usb_page *pg, usb_size_t size, usb_size_t align) 99202ac6454SAndrew Thompson { 993a593f6b8SAndrew Thompson usb_pc_free_mem(pc); 99402ac6454SAndrew Thompson } 995bdc081c6SAndrew Thompson #endif 99602ac6454SAndrew Thompson 99702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 998a593f6b8SAndrew Thompson * usb_bus_mem_free_all - factored out code 99902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 100002ac6454SAndrew Thompson void 1001a593f6b8SAndrew Thompson usb_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb) 100202ac6454SAndrew Thompson { 1003bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 100402ac6454SAndrew Thompson if (cb) { 1005a593f6b8SAndrew Thompson cb(bus, &usb_bus_mem_free_all_cb); 100602ac6454SAndrew Thompson } 1007a593f6b8SAndrew Thompson usb_dma_tag_unsetup(bus->dma_parent_tag); 1008bdc081c6SAndrew Thompson #endif 100902ac6454SAndrew Thompson 101002ac6454SAndrew Thompson mtx_destroy(&bus->bus_mtx); 10112fe7ad87SHans Petter Selasky mtx_destroy(&bus->bus_spin_lock); 101202ac6454SAndrew Thompson } 1013751aaf5aSHans Petter Selasky 1014751aaf5aSHans Petter Selasky /* convenience wrappers */ 1015751aaf5aSHans Petter Selasky void 1016751aaf5aSHans Petter Selasky usb_proc_explore_mwait(struct usb_device *udev, void *pm1, void *pm2) 1017751aaf5aSHans Petter Selasky { 1018751aaf5aSHans Petter Selasky usb_proc_mwait(USB_BUS_EXPLORE_PROC(udev->bus), pm1, pm2); 1019751aaf5aSHans Petter Selasky } 1020751aaf5aSHans Petter Selasky 1021751aaf5aSHans Petter Selasky void * 1022751aaf5aSHans Petter Selasky usb_proc_explore_msignal(struct usb_device *udev, void *pm1, void *pm2) 1023751aaf5aSHans Petter Selasky { 1024751aaf5aSHans Petter Selasky return (usb_proc_msignal(USB_BUS_EXPLORE_PROC(udev->bus), pm1, pm2)); 1025751aaf5aSHans Petter Selasky } 1026751aaf5aSHans Petter Selasky 1027751aaf5aSHans Petter Selasky void 1028751aaf5aSHans Petter Selasky usb_proc_explore_lock(struct usb_device *udev) 1029751aaf5aSHans Petter Selasky { 1030751aaf5aSHans Petter Selasky USB_BUS_LOCK(udev->bus); 1031751aaf5aSHans Petter Selasky } 1032751aaf5aSHans Petter Selasky 1033751aaf5aSHans Petter Selasky void 1034751aaf5aSHans Petter Selasky usb_proc_explore_unlock(struct usb_device *udev) 1035751aaf5aSHans Petter Selasky { 1036751aaf5aSHans Petter Selasky USB_BUS_UNLOCK(udev->bus); 1037751aaf5aSHans Petter Selasky } 1038