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 27ed6d949aSAndrew Thompson #include <sys/stdint.h> 28ed6d949aSAndrew Thompson #include <sys/stddef.h> 29ed6d949aSAndrew Thompson #include <sys/param.h> 30ed6d949aSAndrew Thompson #include <sys/queue.h> 31ed6d949aSAndrew Thompson #include <sys/types.h> 32ed6d949aSAndrew Thompson #include <sys/systm.h> 33ed6d949aSAndrew Thompson #include <sys/kernel.h> 34ed6d949aSAndrew Thompson #include <sys/bus.h> 35ed6d949aSAndrew Thompson #include <sys/linker_set.h> 36ed6d949aSAndrew Thompson #include <sys/module.h> 37ed6d949aSAndrew Thompson #include <sys/lock.h> 38ed6d949aSAndrew Thompson #include <sys/mutex.h> 39ed6d949aSAndrew Thompson #include <sys/condvar.h> 40ed6d949aSAndrew Thompson #include <sys/sysctl.h> 41ed6d949aSAndrew Thompson #include <sys/sx.h> 42ed6d949aSAndrew Thompson #include <sys/unistd.h> 43ed6d949aSAndrew Thompson #include <sys/callout.h> 44ed6d949aSAndrew Thompson #include <sys/malloc.h> 45ed6d949aSAndrew Thompson #include <sys/priv.h> 46ed6d949aSAndrew Thompson 4702ac6454SAndrew Thompson #include <dev/usb/usb.h> 48ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 4902ac6454SAndrew Thompson 50a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_ctrl_debug 5102ac6454SAndrew Thompson 5202ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 5302ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 5402ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 5502ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 5602ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h> 5702ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 5802ac6454SAndrew Thompson #include <dev/usb/usb_hub.h> 5902ac6454SAndrew Thompson 6002ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 6102ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 6202ac6454SAndrew Thompson 6302ac6454SAndrew Thompson /* function prototypes */ 6402ac6454SAndrew Thompson 65a593f6b8SAndrew Thompson static device_probe_t usb_probe; 66a593f6b8SAndrew Thompson static device_attach_t usb_attach; 67a593f6b8SAndrew Thompson static device_detach_t usb_detach; 6802ac6454SAndrew Thompson 69a593f6b8SAndrew Thompson static void usb_attach_sub(device_t, struct usb_bus *); 70a593f6b8SAndrew Thompson static void usb_post_init(void *); 7102ac6454SAndrew Thompson 7202ac6454SAndrew Thompson /* static variables */ 7302ac6454SAndrew Thompson 74ed6d949aSAndrew Thompson #ifdef USB_DEBUG 75a593f6b8SAndrew Thompson static int usb_ctrl_debug = 0; 7602ac6454SAndrew Thompson 779360ae40SAndrew Thompson SYSCTL_NODE(_hw_usb, OID_AUTO, ctrl, CTLFLAG_RW, 0, "USB controller"); 78a593f6b8SAndrew Thompson SYSCTL_INT(_hw_usb_ctrl, OID_AUTO, debug, CTLFLAG_RW, &usb_ctrl_debug, 0, 7902ac6454SAndrew Thompson "Debug level"); 8002ac6454SAndrew Thompson #endif 8102ac6454SAndrew Thompson 82a0c61406SAlfred Perlstein static int usb_no_boot_wait = 0; 83a0c61406SAlfred Perlstein TUNABLE_INT("hw.usb.no_boot_wait", &usb_no_boot_wait); 84a0c61406SAlfred Perlstein SYSCTL_INT(_hw_usb, OID_AUTO, no_boot_wait, CTLFLAG_RDTUN, &usb_no_boot_wait, 0, 85a0c61406SAlfred Perlstein "No device enumerate waiting at boot."); 86a0c61406SAlfred Perlstein 87a593f6b8SAndrew Thompson static uint8_t usb_post_init_called = 0; 8802ac6454SAndrew Thompson 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 107a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, ohci, usb_driver, usb_devclass, 0, 0); 108a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, uhci, usb_driver, usb_devclass, 0, 0); 109a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, ehci, usb_driver, usb_devclass, 0, 0); 110a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, at91_udp, usb_driver, usb_devclass, 0, 0); 111a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, uss820, usb_driver, usb_devclass, 0, 0); 11202ac6454SAndrew Thompson 11302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 114a593f6b8SAndrew Thompson * usb_probe 11502ac6454SAndrew Thompson * 11602ac6454SAndrew Thompson * This function is called from "{ehci,ohci,uhci}_pci_attach()". 11702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 11802ac6454SAndrew Thompson static int 119a593f6b8SAndrew Thompson usb_probe(device_t dev) 12002ac6454SAndrew Thompson { 12102ac6454SAndrew Thompson DPRINTF("\n"); 12202ac6454SAndrew Thompson return (0); 12302ac6454SAndrew Thompson } 12402ac6454SAndrew Thompson 12502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 126a593f6b8SAndrew Thompson * usb_attach 12702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 12802ac6454SAndrew Thompson static int 129a593f6b8SAndrew Thompson usb_attach(device_t dev) 13002ac6454SAndrew Thompson { 131760bc48eSAndrew Thompson struct usb_bus *bus = device_get_ivars(dev); 13202ac6454SAndrew Thompson 13302ac6454SAndrew Thompson DPRINTF("\n"); 13402ac6454SAndrew Thompson 13502ac6454SAndrew Thompson if (bus == NULL) { 13602ac6454SAndrew Thompson DPRINTFN(0, "USB device has no ivars\n"); 13702ac6454SAndrew Thompson return (ENXIO); 13802ac6454SAndrew Thompson } 13902ac6454SAndrew Thompson 140a0c61406SAlfred Perlstein if (usb_no_boot_wait == 0) { 14102ac6454SAndrew Thompson /* delay vfs_mountroot until the bus is explored */ 142853a10a5SAndrew Thompson bus->bus_roothold = root_mount_hold(device_get_nameunit(dev)); 143a0c61406SAlfred Perlstein } 14402ac6454SAndrew Thompson 145a593f6b8SAndrew Thompson if (usb_post_init_called) { 14602ac6454SAndrew Thompson mtx_lock(&Giant); 147a593f6b8SAndrew Thompson usb_attach_sub(dev, bus); 14802ac6454SAndrew Thompson mtx_unlock(&Giant); 149a593f6b8SAndrew Thompson usb_needs_explore(bus, 1); 15002ac6454SAndrew Thompson } 15102ac6454SAndrew Thompson return (0); /* return success */ 15202ac6454SAndrew Thompson } 15302ac6454SAndrew Thompson 15402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 155a593f6b8SAndrew Thompson * usb_detach 15602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 15702ac6454SAndrew Thompson static int 158a593f6b8SAndrew Thompson usb_detach(device_t dev) 15902ac6454SAndrew Thompson { 160760bc48eSAndrew Thompson struct usb_bus *bus = device_get_softc(dev); 16102ac6454SAndrew Thompson 16202ac6454SAndrew Thompson DPRINTF("\n"); 16302ac6454SAndrew Thompson 16402ac6454SAndrew Thompson if (bus == NULL) { 16502ac6454SAndrew Thompson /* was never setup properly */ 16602ac6454SAndrew Thompson return (0); 16702ac6454SAndrew Thompson } 16802ac6454SAndrew Thompson /* Stop power watchdog */ 169a593f6b8SAndrew Thompson usb_callout_drain(&bus->power_wdog); 17002ac6454SAndrew Thompson 17102ac6454SAndrew Thompson /* Let the USB explore process detach all devices. */ 17202ac6454SAndrew Thompson if (bus->bus_roothold != NULL) { 17302ac6454SAndrew Thompson root_mount_rel(bus->bus_roothold); 17402ac6454SAndrew Thompson bus->bus_roothold = NULL; 17502ac6454SAndrew Thompson } 17602ac6454SAndrew Thompson 17702ac6454SAndrew Thompson USB_BUS_LOCK(bus); 178a593f6b8SAndrew Thompson if (usb_proc_msignal(&bus->explore_proc, 17902ac6454SAndrew Thompson &bus->detach_msg[0], &bus->detach_msg[1])) { 18002ac6454SAndrew Thompson /* ignore */ 18102ac6454SAndrew Thompson } 18202ac6454SAndrew Thompson /* Wait for detach to complete */ 18302ac6454SAndrew Thompson 184a593f6b8SAndrew Thompson usb_proc_mwait(&bus->explore_proc, 18502ac6454SAndrew Thompson &bus->detach_msg[0], &bus->detach_msg[1]); 18602ac6454SAndrew Thompson 18702ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 18802ac6454SAndrew Thompson 18902ac6454SAndrew Thompson /* Get rid of USB callback processes */ 19002ac6454SAndrew Thompson 191a593f6b8SAndrew Thompson usb_proc_free(&bus->giant_callback_proc); 192a593f6b8SAndrew Thompson usb_proc_free(&bus->non_giant_callback_proc); 19302ac6454SAndrew Thompson 19402ac6454SAndrew Thompson /* Get rid of USB explore process */ 19502ac6454SAndrew Thompson 196a593f6b8SAndrew Thompson usb_proc_free(&bus->explore_proc); 19702ac6454SAndrew Thompson 198672c9965SAndrew Thompson /* Get rid of control transfer process */ 199672c9965SAndrew Thompson 200a593f6b8SAndrew Thompson usb_proc_free(&bus->control_xfer_proc); 201672c9965SAndrew Thompson 20202ac6454SAndrew Thompson return (0); 20302ac6454SAndrew Thompson } 20402ac6454SAndrew Thompson 20502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 206a593f6b8SAndrew Thompson * usb_bus_explore 20702ac6454SAndrew Thompson * 20802ac6454SAndrew Thompson * This function is used to explore the device tree from the root. 20902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 21002ac6454SAndrew Thompson static void 211a593f6b8SAndrew Thompson usb_bus_explore(struct usb_proc_msg *pm) 21202ac6454SAndrew Thompson { 213760bc48eSAndrew Thompson struct usb_bus *bus; 214760bc48eSAndrew Thompson struct usb_device *udev; 21502ac6454SAndrew Thompson 216760bc48eSAndrew Thompson bus = ((struct usb_bus_msg *)pm)->bus; 21702ac6454SAndrew Thompson udev = bus->devices[USB_ROOT_HUB_ADDR]; 21802ac6454SAndrew Thompson 21902ac6454SAndrew Thompson if (udev && udev->hub) { 22002ac6454SAndrew Thompson 22102ac6454SAndrew Thompson if (bus->do_probe) { 22202ac6454SAndrew Thompson bus->do_probe = 0; 22302ac6454SAndrew Thompson bus->driver_added_refcount++; 22402ac6454SAndrew Thompson } 22502ac6454SAndrew Thompson if (bus->driver_added_refcount == 0) { 22602ac6454SAndrew Thompson /* avoid zero, hence that is memory default */ 22702ac6454SAndrew Thompson bus->driver_added_refcount = 1; 22802ac6454SAndrew Thompson } 22902ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 23002ac6454SAndrew Thompson 23102ac6454SAndrew Thompson mtx_lock(&Giant); 23202ac6454SAndrew Thompson 23302ac6454SAndrew Thompson /* 23402ac6454SAndrew Thompson * First update the USB power state! 23502ac6454SAndrew Thompson */ 236a593f6b8SAndrew Thompson usb_bus_powerd(bus); 23702ac6454SAndrew Thompson /* 23802ac6454SAndrew Thompson * Explore the Root USB HUB. This call can sleep, 23902ac6454SAndrew Thompson * exiting Giant, which is actually Giant. 24002ac6454SAndrew Thompson */ 24102ac6454SAndrew Thompson (udev->hub->explore) (udev); 24202ac6454SAndrew Thompson 24302ac6454SAndrew Thompson mtx_unlock(&Giant); 24402ac6454SAndrew Thompson 24502ac6454SAndrew Thompson USB_BUS_LOCK(bus); 24602ac6454SAndrew Thompson } 24702ac6454SAndrew Thompson if (bus->bus_roothold != NULL) { 24802ac6454SAndrew Thompson root_mount_rel(bus->bus_roothold); 24902ac6454SAndrew Thompson bus->bus_roothold = NULL; 25002ac6454SAndrew Thompson } 25102ac6454SAndrew Thompson } 25202ac6454SAndrew Thompson 25302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 254a593f6b8SAndrew Thompson * usb_bus_detach 25502ac6454SAndrew Thompson * 25602ac6454SAndrew Thompson * This function is used to detach the device tree from the root. 25702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 25802ac6454SAndrew Thompson static void 259a593f6b8SAndrew Thompson usb_bus_detach(struct usb_proc_msg *pm) 26002ac6454SAndrew Thompson { 261760bc48eSAndrew Thompson struct usb_bus *bus; 262760bc48eSAndrew Thompson struct usb_device *udev; 26302ac6454SAndrew Thompson device_t dev; 26402ac6454SAndrew Thompson 265760bc48eSAndrew Thompson bus = ((struct usb_bus_msg *)pm)->bus; 26602ac6454SAndrew Thompson udev = bus->devices[USB_ROOT_HUB_ADDR]; 26702ac6454SAndrew Thompson dev = bus->bdev; 26802ac6454SAndrew Thompson /* clear the softc */ 26902ac6454SAndrew Thompson device_set_softc(dev, NULL); 27002ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 27102ac6454SAndrew Thompson 27202ac6454SAndrew Thompson mtx_lock(&Giant); 27302ac6454SAndrew Thompson 27402ac6454SAndrew Thompson /* detach children first */ 27502ac6454SAndrew Thompson bus_generic_detach(dev); 27602ac6454SAndrew Thompson 27702ac6454SAndrew Thompson /* 27802ac6454SAndrew Thompson * Free USB Root device, but not any sub-devices, hence they 27902ac6454SAndrew Thompson * are freed by the caller of this function: 28002ac6454SAndrew Thompson */ 281a593f6b8SAndrew Thompson usb_free_device(udev, 282bdd41206SAndrew Thompson USB_UNCFG_FLAG_FREE_EP0); 28302ac6454SAndrew Thompson 28402ac6454SAndrew Thompson mtx_unlock(&Giant); 28502ac6454SAndrew Thompson USB_BUS_LOCK(bus); 28602ac6454SAndrew Thompson /* clear bdev variable last */ 28702ac6454SAndrew Thompson bus->bdev = NULL; 28802ac6454SAndrew Thompson } 28902ac6454SAndrew Thompson 29002ac6454SAndrew Thompson static void 291a593f6b8SAndrew Thompson usb_power_wdog(void *arg) 29202ac6454SAndrew Thompson { 293760bc48eSAndrew Thompson struct usb_bus *bus = arg; 29402ac6454SAndrew Thompson 29502ac6454SAndrew Thompson USB_BUS_LOCK_ASSERT(bus, MA_OWNED); 29602ac6454SAndrew Thompson 297a593f6b8SAndrew Thompson usb_callout_reset(&bus->power_wdog, 298a593f6b8SAndrew Thompson 4 * hz, usb_power_wdog, arg); 29902ac6454SAndrew Thompson 30002ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 30102ac6454SAndrew Thompson 302a593f6b8SAndrew Thompson usb_bus_power_update(bus); 30302ac6454SAndrew Thompson 304684e3f22SAndrew Thompson USB_BUS_LOCK(bus); 30502ac6454SAndrew Thompson } 30602ac6454SAndrew Thompson 30702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 308a593f6b8SAndrew Thompson * usb_bus_attach 30902ac6454SAndrew Thompson * 31002ac6454SAndrew Thompson * This function attaches USB in context of the explore thread. 31102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 31202ac6454SAndrew Thompson static void 313a593f6b8SAndrew Thompson usb_bus_attach(struct usb_proc_msg *pm) 31402ac6454SAndrew Thompson { 315760bc48eSAndrew Thompson struct usb_bus *bus; 316760bc48eSAndrew Thompson struct usb_device *child; 31702ac6454SAndrew Thompson device_t dev; 318e0a69b51SAndrew Thompson usb_error_t err; 3198d2dd5ddSAndrew Thompson enum usb_dev_speed speed; 32002ac6454SAndrew Thompson 321760bc48eSAndrew Thompson bus = ((struct usb_bus_msg *)pm)->bus; 32202ac6454SAndrew Thompson dev = bus->bdev; 32302ac6454SAndrew Thompson 32402ac6454SAndrew Thompson DPRINTF("\n"); 32502ac6454SAndrew Thompson 32602ac6454SAndrew Thompson switch (bus->usbrev) { 32702ac6454SAndrew Thompson case USB_REV_1_0: 32802ac6454SAndrew Thompson speed = USB_SPEED_FULL; 32902ac6454SAndrew Thompson device_printf(bus->bdev, "12Mbps Full Speed USB v1.0\n"); 33002ac6454SAndrew Thompson break; 33102ac6454SAndrew Thompson 33202ac6454SAndrew Thompson case USB_REV_1_1: 33302ac6454SAndrew Thompson speed = USB_SPEED_FULL; 33402ac6454SAndrew Thompson device_printf(bus->bdev, "12Mbps Full Speed USB v1.1\n"); 33502ac6454SAndrew Thompson break; 33602ac6454SAndrew Thompson 33702ac6454SAndrew Thompson case USB_REV_2_0: 33802ac6454SAndrew Thompson speed = USB_SPEED_HIGH; 33902ac6454SAndrew Thompson device_printf(bus->bdev, "480Mbps High Speed USB v2.0\n"); 34002ac6454SAndrew Thompson break; 34102ac6454SAndrew Thompson 34202ac6454SAndrew Thompson case USB_REV_2_5: 34302ac6454SAndrew Thompson speed = USB_SPEED_VARIABLE; 34402ac6454SAndrew Thompson device_printf(bus->bdev, "480Mbps Wireless USB v2.5\n"); 34502ac6454SAndrew Thompson break; 34602ac6454SAndrew Thompson 34702ac6454SAndrew Thompson default: 34802ac6454SAndrew Thompson device_printf(bus->bdev, "Unsupported USB revision!\n"); 34902ac6454SAndrew Thompson return; 35002ac6454SAndrew Thompson } 35102ac6454SAndrew Thompson 35202ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 35302ac6454SAndrew Thompson mtx_lock(&Giant); /* XXX not required by USB */ 35402ac6454SAndrew Thompson 3554eae601eSAndrew Thompson /* default power_mask value */ 3564eae601eSAndrew Thompson bus->hw_power_state = 3574eae601eSAndrew Thompson USB_HW_POWER_CONTROL | 3584eae601eSAndrew Thompson USB_HW_POWER_BULK | 3594eae601eSAndrew Thompson USB_HW_POWER_INTERRUPT | 3604eae601eSAndrew Thompson USB_HW_POWER_ISOC | 3614eae601eSAndrew Thompson USB_HW_POWER_NON_ROOT_HUB; 3624eae601eSAndrew Thompson 3634eae601eSAndrew Thompson /* make sure power is set at least once */ 3644eae601eSAndrew Thompson 3654eae601eSAndrew Thompson if (bus->methods->set_hw_power != NULL) { 3664eae601eSAndrew Thompson (bus->methods->set_hw_power) (bus); 3674eae601eSAndrew Thompson } 3684eae601eSAndrew Thompson 36902ac6454SAndrew Thompson /* Allocate the Root USB device */ 37002ac6454SAndrew Thompson 371a593f6b8SAndrew Thompson child = usb_alloc_device(bus->bdev, bus, NULL, 0, 0, 1, 37202ac6454SAndrew Thompson speed, USB_MODE_HOST); 37302ac6454SAndrew Thompson if (child) { 374a593f6b8SAndrew Thompson err = usb_probe_and_attach(child, 37502ac6454SAndrew Thompson USB_IFACE_INDEX_ANY); 37602ac6454SAndrew Thompson if (!err) { 3771be5bf51SAndrew Thompson if ((bus->devices[USB_ROOT_HUB_ADDR] == NULL) || 3781be5bf51SAndrew Thompson (bus->devices[USB_ROOT_HUB_ADDR]->hub == NULL)) { 37902ac6454SAndrew Thompson err = USB_ERR_NO_ROOT_HUB; 38002ac6454SAndrew Thompson } 38102ac6454SAndrew Thompson } 38202ac6454SAndrew Thompson } else { 38302ac6454SAndrew Thompson err = USB_ERR_NOMEM; 38402ac6454SAndrew Thompson } 38502ac6454SAndrew Thompson 38602ac6454SAndrew Thompson mtx_unlock(&Giant); 38702ac6454SAndrew Thompson USB_BUS_LOCK(bus); 38802ac6454SAndrew Thompson 38902ac6454SAndrew Thompson if (err) { 39002ac6454SAndrew Thompson device_printf(bus->bdev, "Root HUB problem, error=%s\n", 391a593f6b8SAndrew Thompson usbd_errstr(err)); 39202ac6454SAndrew Thompson } 39302ac6454SAndrew Thompson 39402ac6454SAndrew Thompson /* set softc - we are ready */ 39502ac6454SAndrew Thompson device_set_softc(dev, bus); 39602ac6454SAndrew Thompson 397684e3f22SAndrew Thompson /* start watchdog */ 398a593f6b8SAndrew Thompson usb_power_wdog(bus); 39902ac6454SAndrew Thompson } 40002ac6454SAndrew Thompson 40102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 402a593f6b8SAndrew Thompson * usb_attach_sub 40302ac6454SAndrew Thompson * 40402ac6454SAndrew Thompson * This function creates a thread which runs the USB attach code. It 40502ac6454SAndrew Thompson * is factored out, hence it can be called at two different places in 40602ac6454SAndrew Thompson * time. During bootup this function is called from 407a593f6b8SAndrew Thompson * "usb_post_init". During hot-plug it is called directly from the 408a593f6b8SAndrew Thompson * "usb_attach()" method. 40902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 41002ac6454SAndrew Thompson static void 411a593f6b8SAndrew Thompson usb_attach_sub(device_t dev, struct usb_bus *bus) 41202ac6454SAndrew Thompson { 41302ac6454SAndrew Thompson const char *pname = device_get_nameunit(dev); 41402ac6454SAndrew Thompson 41502ac6454SAndrew Thompson /* Initialise USB process messages */ 416a593f6b8SAndrew Thompson bus->explore_msg[0].hdr.pm_callback = &usb_bus_explore; 41702ac6454SAndrew Thompson bus->explore_msg[0].bus = bus; 418a593f6b8SAndrew Thompson bus->explore_msg[1].hdr.pm_callback = &usb_bus_explore; 41902ac6454SAndrew Thompson bus->explore_msg[1].bus = bus; 42002ac6454SAndrew Thompson 421a593f6b8SAndrew Thompson bus->detach_msg[0].hdr.pm_callback = &usb_bus_detach; 42202ac6454SAndrew Thompson bus->detach_msg[0].bus = bus; 423a593f6b8SAndrew Thompson bus->detach_msg[1].hdr.pm_callback = &usb_bus_detach; 42402ac6454SAndrew Thompson bus->detach_msg[1].bus = bus; 42502ac6454SAndrew Thompson 426a593f6b8SAndrew Thompson bus->attach_msg[0].hdr.pm_callback = &usb_bus_attach; 42702ac6454SAndrew Thompson bus->attach_msg[0].bus = bus; 428a593f6b8SAndrew Thompson bus->attach_msg[1].hdr.pm_callback = &usb_bus_attach; 42902ac6454SAndrew Thompson bus->attach_msg[1].bus = bus; 43002ac6454SAndrew Thompson 43139307315SAndrew Thompson /* Create USB explore and callback processes */ 43202ac6454SAndrew Thompson 433a593f6b8SAndrew Thompson if (usb_proc_create(&bus->giant_callback_proc, 43402ac6454SAndrew Thompson &bus->bus_mtx, pname, USB_PRI_MED)) { 43502ac6454SAndrew Thompson printf("WARNING: Creation of USB Giant " 43602ac6454SAndrew Thompson "callback process failed.\n"); 437a593f6b8SAndrew Thompson } else if (usb_proc_create(&bus->non_giant_callback_proc, 43802ac6454SAndrew Thompson &bus->bus_mtx, pname, USB_PRI_HIGH)) { 43902ac6454SAndrew Thompson printf("WARNING: Creation of USB non-Giant " 44002ac6454SAndrew Thompson "callback process failed.\n"); 441a593f6b8SAndrew Thompson } else if (usb_proc_create(&bus->explore_proc, 44202ac6454SAndrew Thompson &bus->bus_mtx, pname, USB_PRI_MED)) { 44302ac6454SAndrew Thompson printf("WARNING: Creation of USB explore " 44402ac6454SAndrew Thompson "process failed.\n"); 445a593f6b8SAndrew Thompson } else if (usb_proc_create(&bus->control_xfer_proc, 446672c9965SAndrew Thompson &bus->bus_mtx, pname, USB_PRI_MED)) { 447672c9965SAndrew Thompson printf("WARNING: Creation of USB control transfer " 448672c9965SAndrew Thompson "process failed.\n"); 44902ac6454SAndrew Thompson } else { 45002ac6454SAndrew Thompson /* Get final attach going */ 45102ac6454SAndrew Thompson USB_BUS_LOCK(bus); 452a593f6b8SAndrew Thompson if (usb_proc_msignal(&bus->explore_proc, 45302ac6454SAndrew Thompson &bus->attach_msg[0], &bus->attach_msg[1])) { 45402ac6454SAndrew Thompson /* ignore */ 45502ac6454SAndrew Thompson } 45602ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 45702ac6454SAndrew Thompson } 45802ac6454SAndrew Thompson } 45902ac6454SAndrew Thompson 46002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 461a593f6b8SAndrew Thompson * usb_post_init 46202ac6454SAndrew Thompson * 46302ac6454SAndrew Thompson * This function is called to attach all USB busses that were found 46402ac6454SAndrew Thompson * during bootup. 46502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 46602ac6454SAndrew Thompson static void 467a593f6b8SAndrew Thompson usb_post_init(void *arg) 46802ac6454SAndrew Thompson { 469760bc48eSAndrew Thompson struct usb_bus *bus; 47002ac6454SAndrew Thompson devclass_t dc; 47102ac6454SAndrew Thompson device_t dev; 47202ac6454SAndrew Thompson int max; 47302ac6454SAndrew Thompson int n; 47402ac6454SAndrew Thompson 47502ac6454SAndrew Thompson mtx_lock(&Giant); 47602ac6454SAndrew Thompson 477a593f6b8SAndrew Thompson usb_devclass_ptr = devclass_find("usbus"); 47802ac6454SAndrew Thompson 479a593f6b8SAndrew Thompson dc = usb_devclass_ptr; 48002ac6454SAndrew Thompson if (dc) { 48102ac6454SAndrew Thompson max = devclass_get_maxunit(dc) + 1; 48202ac6454SAndrew Thompson for (n = 0; n != max; n++) { 48302ac6454SAndrew Thompson dev = devclass_get_device(dc, n); 48402ac6454SAndrew Thompson if (dev && device_is_attached(dev)) { 48502ac6454SAndrew Thompson bus = device_get_ivars(dev); 48602ac6454SAndrew Thompson if (bus) { 48702ac6454SAndrew Thompson mtx_lock(&Giant); 488a593f6b8SAndrew Thompson usb_attach_sub(dev, bus); 48902ac6454SAndrew Thompson mtx_unlock(&Giant); 49002ac6454SAndrew Thompson } 49102ac6454SAndrew Thompson } 49202ac6454SAndrew Thompson } 49302ac6454SAndrew Thompson } else { 49402ac6454SAndrew Thompson DPRINTFN(0, "no devclass\n"); 49502ac6454SAndrew Thompson } 496a593f6b8SAndrew Thompson usb_post_init_called = 1; 49702ac6454SAndrew Thompson 49802ac6454SAndrew Thompson /* explore all USB busses in parallell */ 49902ac6454SAndrew Thompson 500a593f6b8SAndrew Thompson usb_needs_explore_all(); 50102ac6454SAndrew Thompson 50202ac6454SAndrew Thompson mtx_unlock(&Giant); 50302ac6454SAndrew Thompson } 50402ac6454SAndrew Thompson 505a593f6b8SAndrew Thompson SYSINIT(usb_post_init, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb_post_init, NULL); 506a593f6b8SAndrew Thompson SYSUNINIT(usb_bus_unload, SI_SUB_KLD, SI_ORDER_ANY, usb_bus_unload, NULL); 50702ac6454SAndrew Thompson 50802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 509a593f6b8SAndrew Thompson * usb_bus_mem_flush_all_cb 51002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 511bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 51202ac6454SAndrew Thompson static void 513a593f6b8SAndrew Thompson usb_bus_mem_flush_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, 514f9cb546cSAndrew Thompson struct usb_page *pg, usb_size_t size, usb_size_t align) 51502ac6454SAndrew Thompson { 516a593f6b8SAndrew Thompson usb_pc_cpu_flush(pc); 51702ac6454SAndrew Thompson } 518bdc081c6SAndrew Thompson #endif 51902ac6454SAndrew Thompson 52002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 521a593f6b8SAndrew Thompson * usb_bus_mem_flush_all - factored out code 52202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 523bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 52402ac6454SAndrew Thompson void 525a593f6b8SAndrew Thompson usb_bus_mem_flush_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb) 52602ac6454SAndrew Thompson { 52702ac6454SAndrew Thompson if (cb) { 528a593f6b8SAndrew Thompson cb(bus, &usb_bus_mem_flush_all_cb); 52902ac6454SAndrew Thompson } 53002ac6454SAndrew Thompson } 531bdc081c6SAndrew Thompson #endif 53202ac6454SAndrew Thompson 53302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 534a593f6b8SAndrew Thompson * usb_bus_mem_alloc_all_cb 53502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 536bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 53702ac6454SAndrew Thompson static void 538a593f6b8SAndrew Thompson usb_bus_mem_alloc_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, 539f9cb546cSAndrew Thompson struct usb_page *pg, usb_size_t size, usb_size_t align) 54002ac6454SAndrew Thompson { 54102ac6454SAndrew Thompson /* need to initialize the page cache */ 54202ac6454SAndrew Thompson pc->tag_parent = bus->dma_parent_tag; 54302ac6454SAndrew Thompson 544a593f6b8SAndrew Thompson if (usb_pc_alloc_mem(pc, pg, size, align)) { 54502ac6454SAndrew Thompson bus->alloc_failed = 1; 54602ac6454SAndrew Thompson } 54702ac6454SAndrew Thompson } 548bdc081c6SAndrew Thompson #endif 54902ac6454SAndrew Thompson 55002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 551a593f6b8SAndrew Thompson * usb_bus_mem_alloc_all - factored out code 55202ac6454SAndrew Thompson * 55302ac6454SAndrew Thompson * Returns: 55402ac6454SAndrew Thompson * 0: Success 55502ac6454SAndrew Thompson * Else: Failure 55602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 55702ac6454SAndrew Thompson uint8_t 558a593f6b8SAndrew Thompson usb_bus_mem_alloc_all(struct usb_bus *bus, bus_dma_tag_t dmat, 559e0a69b51SAndrew Thompson usb_bus_mem_cb_t *cb) 56002ac6454SAndrew Thompson { 56102ac6454SAndrew Thompson bus->alloc_failed = 0; 56202ac6454SAndrew Thompson 56302ac6454SAndrew Thompson mtx_init(&bus->bus_mtx, device_get_nameunit(bus->parent), 56402ac6454SAndrew Thompson NULL, MTX_DEF | MTX_RECURSE); 56502ac6454SAndrew Thompson 566a593f6b8SAndrew Thompson usb_callout_init_mtx(&bus->power_wdog, 567684e3f22SAndrew Thompson &bus->bus_mtx, 0); 56802ac6454SAndrew Thompson 56902ac6454SAndrew Thompson TAILQ_INIT(&bus->intr_q.head); 57002ac6454SAndrew Thompson 571bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 572a593f6b8SAndrew Thompson usb_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags, 573bdc081c6SAndrew Thompson dmat, &bus->bus_mtx, NULL, 32, USB_BUS_DMA_TAG_MAX); 574bdc081c6SAndrew Thompson #endif 57502ac6454SAndrew Thompson if ((bus->devices_max > USB_MAX_DEVICES) || 57602ac6454SAndrew Thompson (bus->devices_max < USB_MIN_DEVICES) || 57702ac6454SAndrew Thompson (bus->devices == NULL)) { 57802ac6454SAndrew Thompson DPRINTFN(0, "Devices field has not been " 57902ac6454SAndrew Thompson "initialised properly!\n"); 58002ac6454SAndrew Thompson bus->alloc_failed = 1; /* failure */ 58102ac6454SAndrew Thompson } 582bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 58302ac6454SAndrew Thompson if (cb) { 584a593f6b8SAndrew Thompson cb(bus, &usb_bus_mem_alloc_all_cb); 58502ac6454SAndrew Thompson } 586bdc081c6SAndrew Thompson #endif 58702ac6454SAndrew Thompson if (bus->alloc_failed) { 588a593f6b8SAndrew Thompson usb_bus_mem_free_all(bus, cb); 58902ac6454SAndrew Thompson } 59002ac6454SAndrew Thompson return (bus->alloc_failed); 59102ac6454SAndrew Thompson } 59202ac6454SAndrew Thompson 59302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 594a593f6b8SAndrew Thompson * usb_bus_mem_free_all_cb 59502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 596bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 59702ac6454SAndrew Thompson static void 598a593f6b8SAndrew Thompson usb_bus_mem_free_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, 599f9cb546cSAndrew Thompson struct usb_page *pg, usb_size_t size, usb_size_t align) 60002ac6454SAndrew Thompson { 601a593f6b8SAndrew Thompson usb_pc_free_mem(pc); 60202ac6454SAndrew Thompson } 603bdc081c6SAndrew Thompson #endif 60402ac6454SAndrew Thompson 60502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 606a593f6b8SAndrew Thompson * usb_bus_mem_free_all - factored out code 60702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 60802ac6454SAndrew Thompson void 609a593f6b8SAndrew Thompson usb_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb) 61002ac6454SAndrew Thompson { 611bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA 61202ac6454SAndrew Thompson if (cb) { 613a593f6b8SAndrew Thompson cb(bus, &usb_bus_mem_free_all_cb); 61402ac6454SAndrew Thompson } 615a593f6b8SAndrew Thompson usb_dma_tag_unsetup(bus->dma_parent_tag); 616bdc081c6SAndrew Thompson #endif 61702ac6454SAndrew Thompson 61802ac6454SAndrew Thompson mtx_destroy(&bus->bus_mtx); 61902ac6454SAndrew Thompson } 620