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 27f0c078e6SAndrew Thompson #include "opt_ddb.h" 28f0c078e6SAndrew Thompson 29ed6d949aSAndrew Thompson #include <sys/stdint.h> 30ed6d949aSAndrew Thompson #include <sys/stddef.h> 31ed6d949aSAndrew Thompson #include <sys/param.h> 32ed6d949aSAndrew Thompson #include <sys/queue.h> 33ed6d949aSAndrew Thompson #include <sys/types.h> 34ed6d949aSAndrew Thompson #include <sys/systm.h> 35ed6d949aSAndrew Thompson #include <sys/kernel.h> 36ed6d949aSAndrew Thompson #include <sys/bus.h> 37ed6d949aSAndrew Thompson #include <sys/linker_set.h> 38ed6d949aSAndrew Thompson #include <sys/module.h> 39ed6d949aSAndrew Thompson #include <sys/lock.h> 40ed6d949aSAndrew Thompson #include <sys/mutex.h> 41ed6d949aSAndrew Thompson #include <sys/condvar.h> 42ed6d949aSAndrew Thompson #include <sys/sysctl.h> 43ed6d949aSAndrew Thompson #include <sys/sx.h> 44ed6d949aSAndrew Thompson #include <sys/unistd.h> 45ed6d949aSAndrew Thompson #include <sys/callout.h> 46ed6d949aSAndrew Thompson #include <sys/malloc.h> 47ed6d949aSAndrew Thompson #include <sys/priv.h> 48ed6d949aSAndrew Thompson 4902ac6454SAndrew Thompson #include <dev/usb/usb.h> 50ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 5102ac6454SAndrew Thompson 52a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_ctrl_debug 5302ac6454SAndrew Thompson 5402ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 5502ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 5602ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 5702ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 5802ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h> 5902ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 6002ac6454SAndrew Thompson #include <dev/usb/usb_hub.h> 6102ac6454SAndrew Thompson 6202ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 6302ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 6418ec6525SWeongyo Jeong #include <dev/usb/usb_pf.h> 6502ac6454SAndrew Thompson 6602ac6454SAndrew Thompson /* function prototypes */ 6702ac6454SAndrew Thompson 68a593f6b8SAndrew Thompson static device_probe_t usb_probe; 69a593f6b8SAndrew Thompson static device_attach_t usb_attach; 70a593f6b8SAndrew Thompson static device_detach_t usb_detach; 7102ac6454SAndrew Thompson 72a593f6b8SAndrew Thompson static void usb_attach_sub(device_t, struct usb_bus *); 7302ac6454SAndrew Thompson 7402ac6454SAndrew Thompson /* static variables */ 7502ac6454SAndrew Thompson 76ed6d949aSAndrew Thompson #ifdef USB_DEBUG 77a593f6b8SAndrew Thompson static int usb_ctrl_debug = 0; 7802ac6454SAndrew Thompson 799360ae40SAndrew Thompson SYSCTL_NODE(_hw_usb, OID_AUTO, ctrl, CTLFLAG_RW, 0, "USB controller"); 80a593f6b8SAndrew Thompson SYSCTL_INT(_hw_usb_ctrl, OID_AUTO, debug, CTLFLAG_RW, &usb_ctrl_debug, 0, 8102ac6454SAndrew Thompson "Debug level"); 8202ac6454SAndrew Thompson #endif 8302ac6454SAndrew Thompson 84a0c61406SAlfred Perlstein static int usb_no_boot_wait = 0; 85a0c61406SAlfred Perlstein TUNABLE_INT("hw.usb.no_boot_wait", &usb_no_boot_wait); 86a0c61406SAlfred Perlstein SYSCTL_INT(_hw_usb, OID_AUTO, no_boot_wait, CTLFLAG_RDTUN, &usb_no_boot_wait, 0, 87a0c61406SAlfred Perlstein "No device enumerate waiting at boot."); 88a0c61406SAlfred Perlstein 89a593f6b8SAndrew Thompson static devclass_t usb_devclass; 9002ac6454SAndrew Thompson 91a593f6b8SAndrew Thompson static device_method_t usb_methods[] = { 92a593f6b8SAndrew Thompson DEVMETHOD(device_probe, usb_probe), 93a593f6b8SAndrew Thompson DEVMETHOD(device_attach, usb_attach), 94a593f6b8SAndrew Thompson DEVMETHOD(device_detach, usb_detach), 9502ac6454SAndrew Thompson DEVMETHOD(device_suspend, bus_generic_suspend), 9602ac6454SAndrew Thompson DEVMETHOD(device_resume, bus_generic_resume), 9702ac6454SAndrew Thompson DEVMETHOD(device_shutdown, bus_generic_shutdown), 9802ac6454SAndrew Thompson {0, 0} 9902ac6454SAndrew Thompson }; 10002ac6454SAndrew Thompson 101a593f6b8SAndrew Thompson static driver_t usb_driver = { 10202ac6454SAndrew Thompson .name = "usbus", 103a593f6b8SAndrew Thompson .methods = usb_methods, 10402ac6454SAndrew Thompson .size = 0, 10502ac6454SAndrew Thompson }; 10602ac6454SAndrew Thompson 107864bc412SHans Petter Selasky /* Host Only Drivers */ 108a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, ohci, usb_driver, usb_devclass, 0, 0); 109a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, uhci, usb_driver, usb_devclass, 0, 0); 110a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, ehci, usb_driver, usb_devclass, 0, 0); 111963169b4SHans Petter Selasky DRIVER_MODULE(usbus, xhci, usb_driver, usb_devclass, 0, 0); 112864bc412SHans Petter Selasky 113864bc412SHans Petter Selasky /* Device Only Drivers */ 114a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, at91_udp, usb_driver, usb_devclass, 0, 0); 115864bc412SHans Petter Selasky DRIVER_MODULE(usbus, musbotg, usb_driver, usb_devclass, 0, 0); 116a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, uss820, usb_driver, usb_devclass, 0, 0); 11702ac6454SAndrew Thompson 11802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 119a593f6b8SAndrew Thompson * usb_probe 12002ac6454SAndrew Thompson * 12102ac6454SAndrew Thompson * This function is called from "{ehci,ohci,uhci}_pci_attach()". 12202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 12302ac6454SAndrew Thompson static int 124a593f6b8SAndrew Thompson usb_probe(device_t dev) 12502ac6454SAndrew Thompson { 12602ac6454SAndrew Thompson DPRINTF("\n"); 12702ac6454SAndrew Thompson return (0); 12802ac6454SAndrew Thompson } 12902ac6454SAndrew Thompson 1303df007ceSHans Petter Selasky static void 1313df007ceSHans Petter Selasky usb_root_mount_rel(struct usb_bus *bus) 1323df007ceSHans Petter Selasky { 1333df007ceSHans Petter Selasky if (bus->bus_roothold != NULL) { 1343df007ceSHans Petter Selasky DPRINTF("Releasing root mount hold %p\n", bus->bus_roothold); 1353df007ceSHans Petter Selasky root_mount_rel(bus->bus_roothold); 1363df007ceSHans Petter Selasky bus->bus_roothold = NULL; 1373df007ceSHans Petter Selasky } 1383df007ceSHans Petter Selasky } 1393df007ceSHans Petter Selasky 14002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 141a593f6b8SAndrew Thompson * usb_attach 14202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 14302ac6454SAndrew Thompson static int 144a593f6b8SAndrew Thompson usb_attach(device_t dev) 14502ac6454SAndrew Thompson { 146760bc48eSAndrew Thompson struct usb_bus *bus = device_get_ivars(dev); 14702ac6454SAndrew Thompson 14802ac6454SAndrew Thompson DPRINTF("\n"); 14902ac6454SAndrew Thompson 15002ac6454SAndrew Thompson if (bus == NULL) { 151767cb2e2SAndrew Thompson device_printf(dev, "USB device has no ivars\n"); 15202ac6454SAndrew Thompson return (ENXIO); 15302ac6454SAndrew Thompson } 15402ac6454SAndrew Thompson 155a0c61406SAlfred Perlstein if (usb_no_boot_wait == 0) { 15602ac6454SAndrew Thompson /* delay vfs_mountroot until the bus is explored */ 157853a10a5SAndrew Thompson bus->bus_roothold = root_mount_hold(device_get_nameunit(dev)); 158a0c61406SAlfred Perlstein } 15902ac6454SAndrew Thompson 160a593f6b8SAndrew Thompson usb_attach_sub(dev, bus); 16134b48722SAlfred Perlstein 16202ac6454SAndrew Thompson return (0); /* return success */ 16302ac6454SAndrew Thompson } 16402ac6454SAndrew Thompson 16502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 166a593f6b8SAndrew Thompson * usb_detach 16702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 16802ac6454SAndrew Thompson static int 169a593f6b8SAndrew Thompson usb_detach(device_t dev) 17002ac6454SAndrew Thompson { 171760bc48eSAndrew Thompson struct usb_bus *bus = device_get_softc(dev); 17202ac6454SAndrew Thompson 17302ac6454SAndrew Thompson DPRINTF("\n"); 17402ac6454SAndrew Thompson 17502ac6454SAndrew Thompson if (bus == NULL) { 17602ac6454SAndrew Thompson /* was never setup properly */ 17702ac6454SAndrew Thompson return (0); 17802ac6454SAndrew Thompson } 17902ac6454SAndrew Thompson /* Stop power watchdog */ 180a593f6b8SAndrew Thompson usb_callout_drain(&bus->power_wdog); 18102ac6454SAndrew Thompson 18202ac6454SAndrew Thompson /* Let the USB explore process detach all devices. */ 1833df007ceSHans Petter Selasky usb_root_mount_rel(bus); 18402ac6454SAndrew Thompson 18502ac6454SAndrew Thompson USB_BUS_LOCK(bus); 186a593f6b8SAndrew Thompson if (usb_proc_msignal(&bus->explore_proc, 18702ac6454SAndrew Thompson &bus->detach_msg[0], &bus->detach_msg[1])) { 18802ac6454SAndrew Thompson /* ignore */ 18902ac6454SAndrew Thompson } 19002ac6454SAndrew Thompson /* Wait for detach to complete */ 19102ac6454SAndrew Thompson 192a593f6b8SAndrew Thompson usb_proc_mwait(&bus->explore_proc, 19302ac6454SAndrew Thompson &bus->detach_msg[0], &bus->detach_msg[1]); 19402ac6454SAndrew Thompson 19502ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 19602ac6454SAndrew Thompson 19702ac6454SAndrew Thompson /* Get rid of USB callback processes */ 19802ac6454SAndrew Thompson 199a593f6b8SAndrew Thompson usb_proc_free(&bus->giant_callback_proc); 200a593f6b8SAndrew Thompson usb_proc_free(&bus->non_giant_callback_proc); 20102ac6454SAndrew Thompson 20202ac6454SAndrew Thompson /* Get rid of USB explore process */ 20302ac6454SAndrew Thompson 204a593f6b8SAndrew Thompson usb_proc_free(&bus->explore_proc); 20502ac6454SAndrew Thompson 206672c9965SAndrew Thompson /* Get rid of control transfer process */ 207672c9965SAndrew Thompson 208a593f6b8SAndrew Thompson usb_proc_free(&bus->control_xfer_proc); 209672c9965SAndrew Thompson 210*fe1c24e3SWeongyo Jeong usbpf_detach(bus); 211*fe1c24e3SWeongyo Jeong 21202ac6454SAndrew Thompson return (0); 21302ac6454SAndrew Thompson } 21402ac6454SAndrew Thompson 21502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 216a593f6b8SAndrew Thompson * usb_bus_explore 21702ac6454SAndrew Thompson * 21802ac6454SAndrew Thompson * This function is used to explore the device tree from the root. 21902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 22002ac6454SAndrew Thompson static void 221a593f6b8SAndrew Thompson usb_bus_explore(struct usb_proc_msg *pm) 22202ac6454SAndrew Thompson { 223760bc48eSAndrew Thompson struct usb_bus *bus; 224760bc48eSAndrew Thompson struct usb_device *udev; 22502ac6454SAndrew Thompson 226760bc48eSAndrew Thompson bus = ((struct usb_bus_msg *)pm)->bus; 22702ac6454SAndrew Thompson udev = bus->devices[USB_ROOT_HUB_ADDR]; 22802ac6454SAndrew Thompson 22902ac6454SAndrew Thompson if (udev && udev->hub) { 23002ac6454SAndrew Thompson 23102ac6454SAndrew Thompson if (bus->do_probe) { 23202ac6454SAndrew Thompson bus->do_probe = 0; 23302ac6454SAndrew Thompson bus->driver_added_refcount++; 23402ac6454SAndrew Thompson } 23502ac6454SAndrew Thompson if (bus->driver_added_refcount == 0) { 23602ac6454SAndrew Thompson /* avoid zero, hence that is memory default */ 23702ac6454SAndrew Thompson bus->driver_added_refcount = 1; 23802ac6454SAndrew Thompson } 23902ac6454SAndrew Thompson 240f0c078e6SAndrew Thompson #ifdef DDB 24134b48722SAlfred Perlstein /* 24234b48722SAlfred Perlstein * The following three lines of code are only here to 24334b48722SAlfred Perlstein * recover from DDB: 24434b48722SAlfred Perlstein */ 24534b48722SAlfred Perlstein usb_proc_rewakeup(&bus->control_xfer_proc); 24634b48722SAlfred Perlstein usb_proc_rewakeup(&bus->giant_callback_proc); 24734b48722SAlfred Perlstein usb_proc_rewakeup(&bus->non_giant_callback_proc); 248f0c078e6SAndrew Thompson #endif 24934b48722SAlfred Perlstein 25034b48722SAlfred Perlstein USB_BUS_UNLOCK(bus); 251a56fe095SJohn Baldwin 252e727a16cSAndrew Thompson #if USB_HAVE_POWERD 25302ac6454SAndrew Thompson /* 25402ac6454SAndrew Thompson * First update the USB power state! 25502ac6454SAndrew Thompson */ 256a593f6b8SAndrew Thompson usb_bus_powerd(bus); 257e727a16cSAndrew Thompson #endif 25834b48722SAlfred Perlstein /* Explore the Root USB HUB. */ 25902ac6454SAndrew Thompson (udev->hub->explore) (udev); 26002ac6454SAndrew Thompson USB_BUS_LOCK(bus); 26102ac6454SAndrew Thompson } 2623df007ceSHans Petter Selasky usb_root_mount_rel(bus); 26302ac6454SAndrew Thompson } 26402ac6454SAndrew Thompson 26502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 266a593f6b8SAndrew Thompson * usb_bus_detach 26702ac6454SAndrew Thompson * 26802ac6454SAndrew Thompson * This function is used to detach the device tree from the root. 26902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 27002ac6454SAndrew Thompson static void 271a593f6b8SAndrew Thompson usb_bus_detach(struct usb_proc_msg *pm) 27202ac6454SAndrew Thompson { 273760bc48eSAndrew Thompson struct usb_bus *bus; 274760bc48eSAndrew Thompson struct usb_device *udev; 27502ac6454SAndrew Thompson device_t dev; 27602ac6454SAndrew Thompson 277760bc48eSAndrew Thompson bus = ((struct usb_bus_msg *)pm)->bus; 27802ac6454SAndrew Thompson udev = bus->devices[USB_ROOT_HUB_ADDR]; 27902ac6454SAndrew Thompson dev = bus->bdev; 28002ac6454SAndrew Thompson /* clear the softc */ 28102ac6454SAndrew Thompson device_set_softc(dev, NULL); 28202ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 28302ac6454SAndrew Thompson 28402ac6454SAndrew Thompson /* detach children first */ 28534b48722SAlfred Perlstein mtx_lock(&Giant); 28602ac6454SAndrew Thompson bus_generic_detach(dev); 28734b48722SAlfred Perlstein mtx_unlock(&Giant); 28802ac6454SAndrew Thompson 28902ac6454SAndrew Thompson /* 290d88688c7SAndrew Thompson * Free USB device and all subdevices, if any. 29102ac6454SAndrew Thompson */ 292d88688c7SAndrew Thompson usb_free_device(udev, 0); 29302ac6454SAndrew Thompson 29402ac6454SAndrew Thompson USB_BUS_LOCK(bus); 29502ac6454SAndrew Thompson /* clear bdev variable last */ 29602ac6454SAndrew Thompson bus->bdev = NULL; 29702ac6454SAndrew Thompson } 29802ac6454SAndrew Thompson 29902ac6454SAndrew Thompson static void 300a593f6b8SAndrew Thompson usb_power_wdog(void *arg) 30102ac6454SAndrew Thompson { 302760bc48eSAndrew Thompson struct usb_bus *bus = arg; 30302ac6454SAndrew Thompson 30402ac6454SAndrew Thompson USB_BUS_LOCK_ASSERT(bus, MA_OWNED); 30502ac6454SAndrew Thompson 306a593f6b8SAndrew Thompson usb_callout_reset(&bus->power_wdog, 307a593f6b8SAndrew Thompson 4 * hz, usb_power_wdog, arg); 30802ac6454SAndrew Thompson 309f0c078e6SAndrew Thompson #ifdef DDB 31034b48722SAlfred Perlstein /* 31134b48722SAlfred Perlstein * The following line of code is only here to recover from 31234b48722SAlfred Perlstein * DDB: 31334b48722SAlfred Perlstein */ 31434b48722SAlfred Perlstein usb_proc_rewakeup(&bus->explore_proc); /* recover from DDB */ 315f0c078e6SAndrew Thompson #endif 31634b48722SAlfred Perlstein 317e727a16cSAndrew Thompson #if USB_HAVE_POWERD 31802ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 31902ac6454SAndrew Thompson 320a593f6b8SAndrew Thompson usb_bus_power_update(bus); 32102ac6454SAndrew Thompson 322684e3f22SAndrew Thompson USB_BUS_LOCK(bus); 323e727a16cSAndrew Thompson #endif 32402ac6454SAndrew Thompson } 32502ac6454SAndrew Thompson 32602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 327a593f6b8SAndrew Thompson * usb_bus_attach 32802ac6454SAndrew Thompson * 32902ac6454SAndrew Thompson * This function attaches USB in context of the explore thread. 33002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 33102ac6454SAndrew Thompson static void 332a593f6b8SAndrew Thompson usb_bus_attach(struct usb_proc_msg *pm) 33302ac6454SAndrew Thompson { 334760bc48eSAndrew Thompson struct usb_bus *bus; 335760bc48eSAndrew Thompson struct usb_device *child; 33602ac6454SAndrew Thompson device_t dev; 337e0a69b51SAndrew Thompson usb_error_t err; 3388d2dd5ddSAndrew Thompson enum usb_dev_speed speed; 33902ac6454SAndrew Thompson 340760bc48eSAndrew Thompson bus = ((struct usb_bus_msg *)pm)->bus; 34102ac6454SAndrew Thompson dev = bus->bdev; 34202ac6454SAndrew Thompson 34302ac6454SAndrew Thompson DPRINTF("\n"); 34402ac6454SAndrew Thompson 34502ac6454SAndrew Thompson switch (bus->usbrev) { 34602ac6454SAndrew Thompson case USB_REV_1_0: 34702ac6454SAndrew Thompson speed = USB_SPEED_FULL; 34802ac6454SAndrew Thompson device_printf(bus->bdev, "12Mbps Full Speed USB v1.0\n"); 34902ac6454SAndrew Thompson break; 35002ac6454SAndrew Thompson 35102ac6454SAndrew Thompson case USB_REV_1_1: 35202ac6454SAndrew Thompson speed = USB_SPEED_FULL; 35302ac6454SAndrew Thompson device_printf(bus->bdev, "12Mbps Full Speed USB v1.1\n"); 35402ac6454SAndrew Thompson break; 35502ac6454SAndrew Thompson 35602ac6454SAndrew Thompson case USB_REV_2_0: 35702ac6454SAndrew Thompson speed = USB_SPEED_HIGH; 35802ac6454SAndrew Thompson device_printf(bus->bdev, "480Mbps High Speed USB v2.0\n"); 35902ac6454SAndrew Thompson break; 36002ac6454SAndrew Thompson 36102ac6454SAndrew Thompson case USB_REV_2_5: 36202ac6454SAndrew Thompson speed = USB_SPEED_VARIABLE; 36302ac6454SAndrew Thompson device_printf(bus->bdev, "480Mbps Wireless USB v2.5\n"); 36402ac6454SAndrew Thompson break; 36502ac6454SAndrew Thompson 366963169b4SHans Petter Selasky case USB_REV_3_0: 367963169b4SHans Petter Selasky speed = USB_SPEED_SUPER; 368963169b4SHans Petter Selasky device_printf(bus->bdev, "4.8Gbps Super Speed USB v3.0\n"); 369963169b4SHans Petter Selasky break; 370963169b4SHans Petter Selasky 37102ac6454SAndrew Thompson default: 372767cb2e2SAndrew Thompson device_printf(bus->bdev, "Unsupported USB revision\n"); 3733df007ceSHans Petter Selasky usb_root_mount_rel(bus); 37402ac6454SAndrew Thompson return; 37502ac6454SAndrew Thompson } 37602ac6454SAndrew Thompson 37702ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 37802ac6454SAndrew Thompson 3794eae601eSAndrew Thompson /* default power_mask value */ 3804eae601eSAndrew Thompson bus->hw_power_state = 3814eae601eSAndrew Thompson USB_HW_POWER_CONTROL | 3824eae601eSAndrew Thompson USB_HW_POWER_BULK | 3834eae601eSAndrew Thompson USB_HW_POWER_INTERRUPT | 3844eae601eSAndrew Thompson USB_HW_POWER_ISOC | 3854eae601eSAndrew Thompson USB_HW_POWER_NON_ROOT_HUB; 3864eae601eSAndrew Thompson 3874eae601eSAndrew Thompson /* make sure power is set at least once */ 3884eae601eSAndrew Thompson 3894eae601eSAndrew Thompson if (bus->methods->set_hw_power != NULL) { 3904eae601eSAndrew Thompson (bus->methods->set_hw_power) (bus); 3914eae601eSAndrew Thompson } 3924eae601eSAndrew Thompson 39302ac6454SAndrew Thompson /* Allocate the Root USB device */ 39402ac6454SAndrew Thompson 395a593f6b8SAndrew Thompson child = usb_alloc_device(bus->bdev, bus, NULL, 0, 0, 1, 39602ac6454SAndrew Thompson speed, USB_MODE_HOST); 39702ac6454SAndrew Thompson if (child) { 398a593f6b8SAndrew Thompson err = usb_probe_and_attach(child, 39902ac6454SAndrew Thompson USB_IFACE_INDEX_ANY); 40002ac6454SAndrew Thompson if (!err) { 4011be5bf51SAndrew Thompson if ((bus->devices[USB_ROOT_HUB_ADDR] == NULL) || 4021be5bf51SAndrew Thompson (bus->devices[USB_ROOT_HUB_ADDR]->hub == NULL)) { 40302ac6454SAndrew Thompson err = USB_ERR_NO_ROOT_HUB; 40402ac6454SAndrew Thompson } 40502ac6454SAndrew Thompson } 40602ac6454SAndrew Thompson } else { 40702ac6454SAndrew Thompson err = USB_ERR_NOMEM; 40802ac6454SAndrew Thompson } 40902ac6454SAndrew Thompson 41002ac6454SAndrew Thompson USB_BUS_LOCK(bus); 41102ac6454SAndrew Thompson 41202ac6454SAndrew Thompson if (err) { 41302ac6454SAndrew Thompson device_printf(bus->bdev, "Root HUB problem, error=%s\n", 414a593f6b8SAndrew Thompson usbd_errstr(err)); 4153df007ceSHans Petter Selasky usb_root_mount_rel(bus); 41602ac6454SAndrew Thompson } 41702ac6454SAndrew Thompson 41802ac6454SAndrew Thompson /* set softc - we are ready */ 41902ac6454SAndrew Thompson device_set_softc(dev, bus); 42002ac6454SAndrew Thompson 421684e3f22SAndrew Thompson /* start watchdog */ 422a593f6b8SAndrew Thompson usb_power_wdog(bus); 42302ac6454SAndrew Thompson } 42402ac6454SAndrew Thompson 42502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 426a593f6b8SAndrew Thompson * usb_attach_sub 42702ac6454SAndrew Thompson * 42834b48722SAlfred Perlstein * This function creates a thread which runs the USB attach code. 42902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 43002ac6454SAndrew Thompson static void 431a593f6b8SAndrew Thompson usb_attach_sub(device_t dev, struct usb_bus *bus) 43202ac6454SAndrew Thompson { 43302ac6454SAndrew Thompson const char *pname = device_get_nameunit(dev); 43402ac6454SAndrew Thompson 43534b48722SAlfred Perlstein mtx_lock(&Giant); 43634b48722SAlfred Perlstein if (usb_devclass_ptr == NULL) 43734b48722SAlfred Perlstein usb_devclass_ptr = devclass_find("usbus"); 43834b48722SAlfred Perlstein mtx_unlock(&Giant); 43934b48722SAlfred Perlstein 440*fe1c24e3SWeongyo Jeong usbpf_attach(bus); 441*fe1c24e3SWeongyo Jeong 44202ac6454SAndrew Thompson /* Initialise USB process messages */ 443a593f6b8SAndrew Thompson bus->explore_msg[0].hdr.pm_callback = &usb_bus_explore; 44402ac6454SAndrew Thompson bus->explore_msg[0].bus = bus; 445a593f6b8SAndrew Thompson bus->explore_msg[1].hdr.pm_callback = &usb_bus_explore; 44602ac6454SAndrew Thompson bus->explore_msg[1].bus = bus; 44702ac6454SAndrew Thompson 448a593f6b8SAndrew Thompson bus->detach_msg[0].hdr.pm_callback = &usb_bus_detach; 44902ac6454SAndrew Thompson bus->detach_msg[0].bus = bus; 450a593f6b8SAndrew Thompson bus->detach_msg[1].hdr.pm_callback = &usb_bus_detach; 45102ac6454SAndrew Thompson bus->detach_msg[1].bus = bus; 45202ac6454SAndrew Thompson 453a593f6b8SAndrew Thompson bus->attach_msg[0].hdr.pm_callback = &usb_bus_attach; 45402ac6454SAndrew Thompson bus->attach_msg[0].bus = bus; 455a593f6b8SAndrew Thompson bus->attach_msg[1].hdr.pm_callback = &usb_bus_attach; 45602ac6454SAndrew Thompson bus->attach_msg[1].bus = bus; 45702ac6454SAndrew Thompson 45839307315SAndrew Thompson /* Create USB explore and callback processes */ 45902ac6454SAndrew Thompson 460a593f6b8SAndrew Thompson if (usb_proc_create(&bus->giant_callback_proc, 46102ac6454SAndrew Thompson &bus->bus_mtx, pname, USB_PRI_MED)) { 46202ac6454SAndrew Thompson printf("WARNING: Creation of USB Giant " 46302ac6454SAndrew Thompson "callback process failed.\n"); 464a593f6b8SAndrew Thompson } else if (usb_proc_create(&bus->non_giant_callback_proc, 46502ac6454SAndrew Thompson &bus->bus_mtx, pname, USB_PRI_HIGH)) { 46602ac6454SAndrew Thompson printf("WARNING: Creation of USB non-Giant " 46702ac6454SAndrew Thompson "callback process failed.\n"); 468a593f6b8SAndrew Thompson } else if (usb_proc_create(&bus->explore_proc, 46902ac6454SAndrew Thompson &bus->bus_mtx, pname, USB_PRI_MED)) { 47002ac6454SAndrew Thompson printf("WARNING: Creation of USB explore " 47102ac6454SAndrew Thompson "process failed.\n"); 472a593f6b8SAndrew Thompson } else if (usb_proc_create(&bus->control_xfer_proc, 473672c9965SAndrew Thompson &bus->bus_mtx, pname, USB_PRI_MED)) { 474672c9965SAndrew Thompson printf("WARNING: Creation of USB control transfer " 475672c9965SAndrew Thompson "process failed.\n"); 47602ac6454SAndrew Thompson } else { 47702ac6454SAndrew Thompson /* Get final attach going */ 47802ac6454SAndrew Thompson USB_BUS_LOCK(bus); 479a593f6b8SAndrew Thompson if (usb_proc_msignal(&bus->explore_proc, 48002ac6454SAndrew Thompson &bus->attach_msg[0], &bus->attach_msg[1])) { 48102ac6454SAndrew Thompson /* ignore */ 48202ac6454SAndrew Thompson } 48302ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 48434b48722SAlfred Perlstein 48534b48722SAlfred Perlstein /* Do initial explore */ 48634b48722SAlfred Perlstein usb_needs_explore(bus, 1); 48702ac6454SAndrew Thompson } 48802ac6454SAndrew Thompson } 48902ac6454SAndrew Thompson 490a593f6b8SAndrew Thompson SYSUNINIT(usb_bus_unload, SI_SUB_KLD, SI_ORDER_ANY, usb_bus_unload, NULL); 49102ac6454SAndrew Thompson 49202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 493a593f6b8SAndrew Thompson * usb_bus_mem_flush_all_cb 49402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 495bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 49602ac6454SAndrew Thompson static void 497a593f6b8SAndrew Thompson usb_bus_mem_flush_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, 498f9cb546cSAndrew Thompson struct usb_page *pg, usb_size_t size, usb_size_t align) 49902ac6454SAndrew Thompson { 500a593f6b8SAndrew Thompson usb_pc_cpu_flush(pc); 50102ac6454SAndrew Thompson } 502bdc081c6SAndrew Thompson #endif 50302ac6454SAndrew Thompson 50402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 505a593f6b8SAndrew Thompson * usb_bus_mem_flush_all - factored out code 50602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 507bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 50802ac6454SAndrew Thompson void 509a593f6b8SAndrew Thompson usb_bus_mem_flush_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb) 51002ac6454SAndrew Thompson { 51102ac6454SAndrew Thompson if (cb) { 512a593f6b8SAndrew Thompson cb(bus, &usb_bus_mem_flush_all_cb); 51302ac6454SAndrew Thompson } 51402ac6454SAndrew Thompson } 515bdc081c6SAndrew Thompson #endif 51602ac6454SAndrew Thompson 51702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 518a593f6b8SAndrew Thompson * usb_bus_mem_alloc_all_cb 51902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 520bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 52102ac6454SAndrew Thompson static void 522a593f6b8SAndrew Thompson usb_bus_mem_alloc_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, 523f9cb546cSAndrew Thompson struct usb_page *pg, usb_size_t size, usb_size_t align) 52402ac6454SAndrew Thompson { 52502ac6454SAndrew Thompson /* need to initialize the page cache */ 52602ac6454SAndrew Thompson pc->tag_parent = bus->dma_parent_tag; 52702ac6454SAndrew Thompson 528a593f6b8SAndrew Thompson if (usb_pc_alloc_mem(pc, pg, size, align)) { 52902ac6454SAndrew Thompson bus->alloc_failed = 1; 53002ac6454SAndrew Thompson } 53102ac6454SAndrew Thompson } 532bdc081c6SAndrew Thompson #endif 53302ac6454SAndrew Thompson 53402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 535a593f6b8SAndrew Thompson * usb_bus_mem_alloc_all - factored out code 53602ac6454SAndrew Thompson * 53702ac6454SAndrew Thompson * Returns: 53802ac6454SAndrew Thompson * 0: Success 53902ac6454SAndrew Thompson * Else: Failure 54002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 54102ac6454SAndrew Thompson uint8_t 542a593f6b8SAndrew Thompson usb_bus_mem_alloc_all(struct usb_bus *bus, bus_dma_tag_t dmat, 543e0a69b51SAndrew Thompson usb_bus_mem_cb_t *cb) 54402ac6454SAndrew Thompson { 54502ac6454SAndrew Thompson bus->alloc_failed = 0; 54602ac6454SAndrew Thompson 54702ac6454SAndrew Thompson mtx_init(&bus->bus_mtx, device_get_nameunit(bus->parent), 54802ac6454SAndrew Thompson NULL, MTX_DEF | MTX_RECURSE); 54902ac6454SAndrew Thompson 550a593f6b8SAndrew Thompson usb_callout_init_mtx(&bus->power_wdog, 551684e3f22SAndrew Thompson &bus->bus_mtx, 0); 55202ac6454SAndrew Thompson 55302ac6454SAndrew Thompson TAILQ_INIT(&bus->intr_q.head); 55402ac6454SAndrew Thompson 555bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 556a593f6b8SAndrew Thompson usb_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags, 557bdc081c6SAndrew Thompson dmat, &bus->bus_mtx, NULL, 32, USB_BUS_DMA_TAG_MAX); 558bdc081c6SAndrew Thompson #endif 55902ac6454SAndrew Thompson if ((bus->devices_max > USB_MAX_DEVICES) || 56002ac6454SAndrew Thompson (bus->devices_max < USB_MIN_DEVICES) || 56102ac6454SAndrew Thompson (bus->devices == NULL)) { 56202ac6454SAndrew Thompson DPRINTFN(0, "Devices field has not been " 563767cb2e2SAndrew Thompson "initialised properly\n"); 56402ac6454SAndrew Thompson bus->alloc_failed = 1; /* failure */ 56502ac6454SAndrew Thompson } 566bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 56702ac6454SAndrew Thompson if (cb) { 568a593f6b8SAndrew Thompson cb(bus, &usb_bus_mem_alloc_all_cb); 56902ac6454SAndrew Thompson } 570bdc081c6SAndrew Thompson #endif 57102ac6454SAndrew Thompson if (bus->alloc_failed) { 572a593f6b8SAndrew Thompson usb_bus_mem_free_all(bus, cb); 57302ac6454SAndrew Thompson } 57402ac6454SAndrew Thompson return (bus->alloc_failed); 57502ac6454SAndrew Thompson } 57602ac6454SAndrew Thompson 57702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 578a593f6b8SAndrew Thompson * usb_bus_mem_free_all_cb 57902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 580bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 58102ac6454SAndrew Thompson static void 582a593f6b8SAndrew Thompson usb_bus_mem_free_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, 583f9cb546cSAndrew Thompson struct usb_page *pg, usb_size_t size, usb_size_t align) 58402ac6454SAndrew Thompson { 585a593f6b8SAndrew Thompson usb_pc_free_mem(pc); 58602ac6454SAndrew Thompson } 587bdc081c6SAndrew Thompson #endif 58802ac6454SAndrew Thompson 58902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 590a593f6b8SAndrew Thompson * usb_bus_mem_free_all - factored out code 59102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 59202ac6454SAndrew Thompson void 593a593f6b8SAndrew Thompson usb_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb) 59402ac6454SAndrew Thompson { 595bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 59602ac6454SAndrew Thompson if (cb) { 597a593f6b8SAndrew Thompson cb(bus, &usb_bus_mem_free_all_cb); 59802ac6454SAndrew Thompson } 599a593f6b8SAndrew Thompson usb_dma_tag_unsetup(bus->dma_parent_tag); 600bdc081c6SAndrew Thompson #endif 60102ac6454SAndrew Thompson 60202ac6454SAndrew Thompson mtx_destroy(&bus->bus_mtx); 60302ac6454SAndrew Thompson } 60418ec6525SWeongyo Jeong 60518ec6525SWeongyo Jeong struct usb_bus * 60618ec6525SWeongyo Jeong usb_bus_find(const char *name) 60718ec6525SWeongyo Jeong { 60818ec6525SWeongyo Jeong struct usb_bus *ubus; 60918ec6525SWeongyo Jeong devclass_t dc; 61018ec6525SWeongyo Jeong device_t *devlist; 61118ec6525SWeongyo Jeong int devcount, error, i; 61218ec6525SWeongyo Jeong const char *nameunit; 61318ec6525SWeongyo Jeong 61418ec6525SWeongyo Jeong dc = devclass_find("usbus"); 61518ec6525SWeongyo Jeong if (dc == NULL) 61618ec6525SWeongyo Jeong return (NULL); 61718ec6525SWeongyo Jeong error = devclass_get_devices(dc, &devlist, &devcount); 61818ec6525SWeongyo Jeong if (error != 0) 61918ec6525SWeongyo Jeong return (NULL); 62018ec6525SWeongyo Jeong for (i = 0; i < devcount; i++) { 62118ec6525SWeongyo Jeong nameunit = device_get_nameunit(devlist[i]); 62218ec6525SWeongyo Jeong if (!strncmp(name, nameunit, strlen(nameunit))) { 62318ec6525SWeongyo Jeong ubus = device_get_ivars(devlist[i]); 62418ec6525SWeongyo Jeong free(devlist, M_TEMP); 62518ec6525SWeongyo Jeong return (ubus); 62618ec6525SWeongyo Jeong } 62718ec6525SWeongyo Jeong } 62818ec6525SWeongyo Jeong free(devlist, M_TEMP); 62918ec6525SWeongyo Jeong return (NULL); 63018ec6525SWeongyo Jeong } 631