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 2702ac6454SAndrew Thompson #include <dev/usb/usb_mfunc.h> 2802ac6454SAndrew Thompson #include <dev/usb/usb_defs.h> 2902ac6454SAndrew Thompson #include <dev/usb/usb_error.h> 3002ac6454SAndrew Thompson #include <dev/usb/usb.h> 3102ac6454SAndrew Thompson 3202ac6454SAndrew Thompson #define USB_DEBUG_VAR usb2_ctrl_debug 3302ac6454SAndrew Thompson 3402ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 3502ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 3602ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 3702ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 3802ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h> 3902ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 4002ac6454SAndrew Thompson #include <dev/usb/usb_hub.h> 4102ac6454SAndrew Thompson 4202ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 4302ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 4402ac6454SAndrew Thompson 4502ac6454SAndrew Thompson /* function prototypes */ 4602ac6454SAndrew Thompson 4702ac6454SAndrew Thompson static device_probe_t usb2_probe; 4802ac6454SAndrew Thompson static device_attach_t usb2_attach; 4902ac6454SAndrew Thompson static device_detach_t usb2_detach; 5002ac6454SAndrew Thompson 5102ac6454SAndrew Thompson static void usb2_attach_sub(device_t, struct usb2_bus *); 5202ac6454SAndrew Thompson static void usb2_post_init(void *); 5302ac6454SAndrew Thompson static void usb2_bus_mem_flush_all_cb(struct usb2_bus *, 5402ac6454SAndrew Thompson struct usb2_page_cache *, struct usb2_page *, uint32_t, 5502ac6454SAndrew Thompson uint32_t); 5602ac6454SAndrew Thompson static void usb2_bus_mem_alloc_all_cb(struct usb2_bus *, 5702ac6454SAndrew Thompson struct usb2_page_cache *, struct usb2_page *, uint32_t, 5802ac6454SAndrew Thompson uint32_t); 5902ac6454SAndrew Thompson static void usb2_bus_mem_free_all_cb(struct usb2_bus *, 6002ac6454SAndrew Thompson struct usb2_page_cache *, struct usb2_page *, uint32_t, 6102ac6454SAndrew Thompson uint32_t); 6202ac6454SAndrew Thompson static void usb2_bus_roothub(struct usb2_proc_msg *pm); 6302ac6454SAndrew Thompson 6402ac6454SAndrew Thompson /* static variables */ 6502ac6454SAndrew Thompson 6602ac6454SAndrew Thompson #if USB_DEBUG 6702ac6454SAndrew Thompson static int usb2_ctrl_debug = 0; 6802ac6454SAndrew Thompson 6902ac6454SAndrew Thompson SYSCTL_NODE(_hw_usb2, OID_AUTO, ctrl, CTLFLAG_RW, 0, "USB controller"); 7002ac6454SAndrew Thompson SYSCTL_INT(_hw_usb2_ctrl, OID_AUTO, debug, CTLFLAG_RW, &usb2_ctrl_debug, 0, 7102ac6454SAndrew Thompson "Debug level"); 7202ac6454SAndrew Thompson #endif 7302ac6454SAndrew Thompson 7402ac6454SAndrew Thompson static uint8_t usb2_post_init_called = 0; 7502ac6454SAndrew Thompson 7602ac6454SAndrew Thompson static devclass_t usb2_devclass; 7702ac6454SAndrew Thompson 7802ac6454SAndrew Thompson static device_method_t usb2_methods[] = { 7902ac6454SAndrew Thompson DEVMETHOD(device_probe, usb2_probe), 8002ac6454SAndrew Thompson DEVMETHOD(device_attach, usb2_attach), 8102ac6454SAndrew Thompson DEVMETHOD(device_detach, usb2_detach), 8202ac6454SAndrew Thompson DEVMETHOD(device_suspend, bus_generic_suspend), 8302ac6454SAndrew Thompson DEVMETHOD(device_resume, bus_generic_resume), 8402ac6454SAndrew Thompson DEVMETHOD(device_shutdown, bus_generic_shutdown), 8502ac6454SAndrew Thompson {0, 0} 8602ac6454SAndrew Thompson }; 8702ac6454SAndrew Thompson 8802ac6454SAndrew Thompson static driver_t usb2_driver = { 8902ac6454SAndrew Thompson .name = "usbus", 9002ac6454SAndrew Thompson .methods = usb2_methods, 9102ac6454SAndrew Thompson .size = 0, 9202ac6454SAndrew Thompson }; 9302ac6454SAndrew Thompson 9402ac6454SAndrew Thompson DRIVER_MODULE(usbus, ohci, usb2_driver, usb2_devclass, 0, 0); 9502ac6454SAndrew Thompson DRIVER_MODULE(usbus, uhci, usb2_driver, usb2_devclass, 0, 0); 9602ac6454SAndrew Thompson DRIVER_MODULE(usbus, ehci, usb2_driver, usb2_devclass, 0, 0); 9702ac6454SAndrew Thompson DRIVER_MODULE(usbus, at91_udp, usb2_driver, usb2_devclass, 0, 0); 9802ac6454SAndrew Thompson DRIVER_MODULE(usbus, uss820, usb2_driver, usb2_devclass, 0, 0); 9902ac6454SAndrew Thompson 10002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 10102ac6454SAndrew Thompson * usb2_probe 10202ac6454SAndrew Thompson * 10302ac6454SAndrew Thompson * This function is called from "{ehci,ohci,uhci}_pci_attach()". 10402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 10502ac6454SAndrew Thompson static int 10602ac6454SAndrew Thompson usb2_probe(device_t dev) 10702ac6454SAndrew Thompson { 10802ac6454SAndrew Thompson DPRINTF("\n"); 10902ac6454SAndrew Thompson return (0); 11002ac6454SAndrew Thompson } 11102ac6454SAndrew Thompson 11202ac6454SAndrew Thompson /*------------------------------------------------------------------------* 11302ac6454SAndrew Thompson * usb2_attach 11402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 11502ac6454SAndrew Thompson static int 11602ac6454SAndrew Thompson usb2_attach(device_t dev) 11702ac6454SAndrew Thompson { 11802ac6454SAndrew Thompson struct usb2_bus *bus = device_get_ivars(dev); 11902ac6454SAndrew Thompson 12002ac6454SAndrew Thompson DPRINTF("\n"); 12102ac6454SAndrew Thompson 12202ac6454SAndrew Thompson if (bus == NULL) { 12302ac6454SAndrew Thompson DPRINTFN(0, "USB device has no ivars\n"); 12402ac6454SAndrew Thompson return (ENXIO); 12502ac6454SAndrew Thompson } 12602ac6454SAndrew Thompson 12702ac6454SAndrew Thompson /* delay vfs_mountroot until the bus is explored */ 12802ac6454SAndrew Thompson bus->bus_roothold = root_mount_hold(device_get_nameunit(dev)); 12902ac6454SAndrew Thompson 13002ac6454SAndrew Thompson if (usb2_post_init_called) { 13102ac6454SAndrew Thompson mtx_lock(&Giant); 13202ac6454SAndrew Thompson usb2_attach_sub(dev, bus); 13302ac6454SAndrew Thompson mtx_unlock(&Giant); 13402ac6454SAndrew Thompson usb2_needs_explore(bus, 1); 13502ac6454SAndrew Thompson } 13602ac6454SAndrew Thompson return (0); /* return success */ 13702ac6454SAndrew Thompson } 13802ac6454SAndrew Thompson 13902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 14002ac6454SAndrew Thompson * usb2_detach 14102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 14202ac6454SAndrew Thompson static int 14302ac6454SAndrew Thompson usb2_detach(device_t dev) 14402ac6454SAndrew Thompson { 14502ac6454SAndrew Thompson struct usb2_bus *bus = device_get_softc(dev); 14602ac6454SAndrew Thompson 14702ac6454SAndrew Thompson DPRINTF("\n"); 14802ac6454SAndrew Thompson 14902ac6454SAndrew Thompson if (bus == NULL) { 15002ac6454SAndrew Thompson /* was never setup properly */ 15102ac6454SAndrew Thompson return (0); 15202ac6454SAndrew Thompson } 15302ac6454SAndrew Thompson /* Stop power watchdog */ 15402ac6454SAndrew Thompson usb2_callout_drain(&bus->power_wdog); 15502ac6454SAndrew Thompson 15602ac6454SAndrew Thompson /* Let the USB explore process detach all devices. */ 15702ac6454SAndrew Thompson if (bus->bus_roothold != NULL) { 15802ac6454SAndrew Thompson root_mount_rel(bus->bus_roothold); 15902ac6454SAndrew Thompson bus->bus_roothold = NULL; 16002ac6454SAndrew Thompson } 16102ac6454SAndrew Thompson 16202ac6454SAndrew Thompson USB_BUS_LOCK(bus); 16302ac6454SAndrew Thompson if (usb2_proc_msignal(&bus->explore_proc, 16402ac6454SAndrew Thompson &bus->detach_msg[0], &bus->detach_msg[1])) { 16502ac6454SAndrew Thompson /* ignore */ 16602ac6454SAndrew Thompson } 16702ac6454SAndrew Thompson /* Wait for detach to complete */ 16802ac6454SAndrew Thompson 16902ac6454SAndrew Thompson usb2_proc_mwait(&bus->explore_proc, 17002ac6454SAndrew Thompson &bus->detach_msg[0], &bus->detach_msg[1]); 17102ac6454SAndrew Thompson 17202ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 17302ac6454SAndrew Thompson 17402ac6454SAndrew Thompson /* Get rid of USB callback processes */ 17502ac6454SAndrew Thompson 17602ac6454SAndrew Thompson usb2_proc_free(&bus->giant_callback_proc); 17702ac6454SAndrew Thompson usb2_proc_free(&bus->non_giant_callback_proc); 17802ac6454SAndrew Thompson 17902ac6454SAndrew Thompson /* Get rid of USB roothub process */ 18002ac6454SAndrew Thompson 18102ac6454SAndrew Thompson usb2_proc_free(&bus->roothub_proc); 18202ac6454SAndrew Thompson 18302ac6454SAndrew Thompson /* Get rid of USB explore process */ 18402ac6454SAndrew Thompson 18502ac6454SAndrew Thompson usb2_proc_free(&bus->explore_proc); 18602ac6454SAndrew Thompson 18702ac6454SAndrew Thompson return (0); 18802ac6454SAndrew Thompson } 18902ac6454SAndrew Thompson 19002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 19102ac6454SAndrew Thompson * usb2_bus_explore 19202ac6454SAndrew Thompson * 19302ac6454SAndrew Thompson * This function is used to explore the device tree from the root. 19402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 19502ac6454SAndrew Thompson static void 19602ac6454SAndrew Thompson usb2_bus_explore(struct usb2_proc_msg *pm) 19702ac6454SAndrew Thompson { 19802ac6454SAndrew Thompson struct usb2_bus *bus; 19902ac6454SAndrew Thompson struct usb2_device *udev; 20002ac6454SAndrew Thompson 20102ac6454SAndrew Thompson bus = ((struct usb2_bus_msg *)pm)->bus; 20202ac6454SAndrew Thompson udev = bus->devices[USB_ROOT_HUB_ADDR]; 20302ac6454SAndrew Thompson 20402ac6454SAndrew Thompson if (udev && udev->hub) { 20502ac6454SAndrew Thompson 20602ac6454SAndrew Thompson if (bus->do_probe) { 20702ac6454SAndrew Thompson bus->do_probe = 0; 20802ac6454SAndrew Thompson bus->driver_added_refcount++; 20902ac6454SAndrew Thompson } 21002ac6454SAndrew Thompson if (bus->driver_added_refcount == 0) { 21102ac6454SAndrew Thompson /* avoid zero, hence that is memory default */ 21202ac6454SAndrew Thompson bus->driver_added_refcount = 1; 21302ac6454SAndrew Thompson } 21402ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 21502ac6454SAndrew Thompson 21602ac6454SAndrew Thompson mtx_lock(&Giant); 21702ac6454SAndrew Thompson 21802ac6454SAndrew Thompson /* 21902ac6454SAndrew Thompson * First update the USB power state! 22002ac6454SAndrew Thompson */ 22102ac6454SAndrew Thompson usb2_bus_powerd(bus); 22202ac6454SAndrew Thompson 22302ac6454SAndrew Thompson /* 22402ac6454SAndrew Thompson * Explore the Root USB HUB. This call can sleep, 22502ac6454SAndrew Thompson * exiting Giant, which is actually Giant. 22602ac6454SAndrew Thompson */ 22702ac6454SAndrew Thompson (udev->hub->explore) (udev); 22802ac6454SAndrew Thompson 22902ac6454SAndrew Thompson mtx_unlock(&Giant); 23002ac6454SAndrew Thompson 23102ac6454SAndrew Thompson USB_BUS_LOCK(bus); 23202ac6454SAndrew Thompson } 23302ac6454SAndrew Thompson if (bus->bus_roothold != NULL) { 23402ac6454SAndrew Thompson root_mount_rel(bus->bus_roothold); 23502ac6454SAndrew Thompson bus->bus_roothold = NULL; 23602ac6454SAndrew Thompson } 23702ac6454SAndrew Thompson } 23802ac6454SAndrew Thompson 23902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 24002ac6454SAndrew Thompson * usb2_bus_detach 24102ac6454SAndrew Thompson * 24202ac6454SAndrew Thompson * This function is used to detach the device tree from the root. 24302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 24402ac6454SAndrew Thompson static void 24502ac6454SAndrew Thompson usb2_bus_detach(struct usb2_proc_msg *pm) 24602ac6454SAndrew Thompson { 24702ac6454SAndrew Thompson struct usb2_bus *bus; 24802ac6454SAndrew Thompson struct usb2_device *udev; 24902ac6454SAndrew Thompson device_t dev; 25002ac6454SAndrew Thompson 25102ac6454SAndrew Thompson bus = ((struct usb2_bus_msg *)pm)->bus; 25202ac6454SAndrew Thompson udev = bus->devices[USB_ROOT_HUB_ADDR]; 25302ac6454SAndrew Thompson dev = bus->bdev; 25402ac6454SAndrew Thompson /* clear the softc */ 25502ac6454SAndrew Thompson device_set_softc(dev, NULL); 25602ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 25702ac6454SAndrew Thompson 25802ac6454SAndrew Thompson mtx_lock(&Giant); 25902ac6454SAndrew Thompson 26002ac6454SAndrew Thompson /* detach children first */ 26102ac6454SAndrew Thompson bus_generic_detach(dev); 26202ac6454SAndrew Thompson 26302ac6454SAndrew Thompson /* 26402ac6454SAndrew Thompson * Free USB Root device, but not any sub-devices, hence they 26502ac6454SAndrew Thompson * are freed by the caller of this function: 26602ac6454SAndrew Thompson */ 26702ac6454SAndrew Thompson usb2_detach_device(udev, USB_IFACE_INDEX_ANY, 0); 26802ac6454SAndrew Thompson usb2_free_device(udev); 26902ac6454SAndrew Thompson 27002ac6454SAndrew Thompson mtx_unlock(&Giant); 27102ac6454SAndrew Thompson USB_BUS_LOCK(bus); 27202ac6454SAndrew Thompson /* clear bdev variable last */ 27302ac6454SAndrew Thompson bus->bdev = NULL; 27402ac6454SAndrew Thompson } 27502ac6454SAndrew Thompson 27602ac6454SAndrew Thompson static void 27702ac6454SAndrew Thompson usb2_power_wdog(void *arg) 27802ac6454SAndrew Thompson { 27902ac6454SAndrew Thompson struct usb2_bus *bus = arg; 28002ac6454SAndrew Thompson 28102ac6454SAndrew Thompson USB_BUS_LOCK_ASSERT(bus, MA_OWNED); 28202ac6454SAndrew Thompson 28302ac6454SAndrew Thompson usb2_callout_reset(&bus->power_wdog, 28402ac6454SAndrew Thompson 4 * hz, usb2_power_wdog, arg); 28502ac6454SAndrew Thompson 28602ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 28702ac6454SAndrew Thompson 28802ac6454SAndrew Thompson usb2_bus_power_update(bus); 28902ac6454SAndrew Thompson 29002ac6454SAndrew Thompson return; 29102ac6454SAndrew Thompson } 29202ac6454SAndrew Thompson 29302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 29402ac6454SAndrew Thompson * usb2_bus_attach 29502ac6454SAndrew Thompson * 29602ac6454SAndrew Thompson * This function attaches USB in context of the explore thread. 29702ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 29802ac6454SAndrew Thompson static void 29902ac6454SAndrew Thompson usb2_bus_attach(struct usb2_proc_msg *pm) 30002ac6454SAndrew Thompson { 30102ac6454SAndrew Thompson struct usb2_bus *bus; 30202ac6454SAndrew Thompson struct usb2_device *child; 30302ac6454SAndrew Thompson device_t dev; 30402ac6454SAndrew Thompson usb2_error_t err; 30502ac6454SAndrew Thompson uint8_t speed; 30602ac6454SAndrew Thompson 30702ac6454SAndrew Thompson bus = ((struct usb2_bus_msg *)pm)->bus; 30802ac6454SAndrew Thompson dev = bus->bdev; 30902ac6454SAndrew Thompson 31002ac6454SAndrew Thompson DPRINTF("\n"); 31102ac6454SAndrew Thompson 31202ac6454SAndrew Thompson switch (bus->usbrev) { 31302ac6454SAndrew Thompson case USB_REV_1_0: 31402ac6454SAndrew Thompson speed = USB_SPEED_FULL; 31502ac6454SAndrew Thompson device_printf(bus->bdev, "12Mbps Full Speed USB v1.0\n"); 31602ac6454SAndrew Thompson break; 31702ac6454SAndrew Thompson 31802ac6454SAndrew Thompson case USB_REV_1_1: 31902ac6454SAndrew Thompson speed = USB_SPEED_FULL; 32002ac6454SAndrew Thompson device_printf(bus->bdev, "12Mbps Full Speed USB v1.1\n"); 32102ac6454SAndrew Thompson break; 32202ac6454SAndrew Thompson 32302ac6454SAndrew Thompson case USB_REV_2_0: 32402ac6454SAndrew Thompson speed = USB_SPEED_HIGH; 32502ac6454SAndrew Thompson device_printf(bus->bdev, "480Mbps High Speed USB v2.0\n"); 32602ac6454SAndrew Thompson break; 32702ac6454SAndrew Thompson 32802ac6454SAndrew Thompson case USB_REV_2_5: 32902ac6454SAndrew Thompson speed = USB_SPEED_VARIABLE; 33002ac6454SAndrew Thompson device_printf(bus->bdev, "480Mbps Wireless USB v2.5\n"); 33102ac6454SAndrew Thompson break; 33202ac6454SAndrew Thompson 33302ac6454SAndrew Thompson default: 33402ac6454SAndrew Thompson device_printf(bus->bdev, "Unsupported USB revision!\n"); 33502ac6454SAndrew Thompson return; 33602ac6454SAndrew Thompson } 33702ac6454SAndrew Thompson 33802ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 33902ac6454SAndrew Thompson mtx_lock(&Giant); /* XXX not required by USB */ 34002ac6454SAndrew Thompson 34102ac6454SAndrew Thompson /* Allocate the Root USB device */ 34202ac6454SAndrew Thompson 34302ac6454SAndrew Thompson child = usb2_alloc_device(bus->bdev, bus, NULL, 0, 0, 1, 34402ac6454SAndrew Thompson speed, USB_MODE_HOST); 34502ac6454SAndrew Thompson if (child) { 34602ac6454SAndrew Thompson err = usb2_probe_and_attach(child, 34702ac6454SAndrew Thompson USB_IFACE_INDEX_ANY); 34802ac6454SAndrew Thompson if (!err) { 3491be5bf51SAndrew Thompson if ((bus->devices[USB_ROOT_HUB_ADDR] == NULL) || 3501be5bf51SAndrew Thompson (bus->devices[USB_ROOT_HUB_ADDR]->hub == NULL)) { 35102ac6454SAndrew Thompson err = USB_ERR_NO_ROOT_HUB; 35202ac6454SAndrew Thompson } 35302ac6454SAndrew Thompson } 35402ac6454SAndrew Thompson } else { 35502ac6454SAndrew Thompson err = USB_ERR_NOMEM; 35602ac6454SAndrew Thompson } 35702ac6454SAndrew Thompson 35802ac6454SAndrew Thompson mtx_unlock(&Giant); 35902ac6454SAndrew Thompson USB_BUS_LOCK(bus); 36002ac6454SAndrew Thompson 36102ac6454SAndrew Thompson if (err) { 36202ac6454SAndrew Thompson device_printf(bus->bdev, "Root HUB problem, error=%s\n", 36302ac6454SAndrew Thompson usb2_errstr(err)); 36402ac6454SAndrew Thompson } 36502ac6454SAndrew Thompson 36602ac6454SAndrew Thompson /* set softc - we are ready */ 36702ac6454SAndrew Thompson device_set_softc(dev, bus); 36802ac6454SAndrew Thompson 36902ac6454SAndrew Thompson /* start watchdog - this function will unlock the BUS lock ! */ 37002ac6454SAndrew Thompson usb2_power_wdog(bus); 37102ac6454SAndrew Thompson 37202ac6454SAndrew Thompson /* need to return locked */ 37302ac6454SAndrew Thompson USB_BUS_LOCK(bus); 37402ac6454SAndrew Thompson } 37502ac6454SAndrew Thompson 37602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 37702ac6454SAndrew Thompson * usb2_attach_sub 37802ac6454SAndrew Thompson * 37902ac6454SAndrew Thompson * This function creates a thread which runs the USB attach code. It 38002ac6454SAndrew Thompson * is factored out, hence it can be called at two different places in 38102ac6454SAndrew Thompson * time. During bootup this function is called from 38202ac6454SAndrew Thompson * "usb2_post_init". During hot-plug it is called directly from the 38302ac6454SAndrew Thompson * "usb2_attach()" method. 38402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 38502ac6454SAndrew Thompson static void 38602ac6454SAndrew Thompson usb2_attach_sub(device_t dev, struct usb2_bus *bus) 38702ac6454SAndrew Thompson { 38802ac6454SAndrew Thompson const char *pname = device_get_nameunit(dev); 38902ac6454SAndrew Thompson 39002ac6454SAndrew Thompson /* Initialise USB process messages */ 39102ac6454SAndrew Thompson bus->explore_msg[0].hdr.pm_callback = &usb2_bus_explore; 39202ac6454SAndrew Thompson bus->explore_msg[0].bus = bus; 39302ac6454SAndrew Thompson bus->explore_msg[1].hdr.pm_callback = &usb2_bus_explore; 39402ac6454SAndrew Thompson bus->explore_msg[1].bus = bus; 39502ac6454SAndrew Thompson 39602ac6454SAndrew Thompson bus->detach_msg[0].hdr.pm_callback = &usb2_bus_detach; 39702ac6454SAndrew Thompson bus->detach_msg[0].bus = bus; 39802ac6454SAndrew Thompson bus->detach_msg[1].hdr.pm_callback = &usb2_bus_detach; 39902ac6454SAndrew Thompson bus->detach_msg[1].bus = bus; 40002ac6454SAndrew Thompson 40102ac6454SAndrew Thompson bus->attach_msg[0].hdr.pm_callback = &usb2_bus_attach; 40202ac6454SAndrew Thompson bus->attach_msg[0].bus = bus; 40302ac6454SAndrew Thompson bus->attach_msg[1].hdr.pm_callback = &usb2_bus_attach; 40402ac6454SAndrew Thompson bus->attach_msg[1].bus = bus; 40502ac6454SAndrew Thompson 40602ac6454SAndrew Thompson bus->roothub_msg[0].hdr.pm_callback = &usb2_bus_roothub; 40702ac6454SAndrew Thompson bus->roothub_msg[0].bus = bus; 40802ac6454SAndrew Thompson bus->roothub_msg[1].hdr.pm_callback = &usb2_bus_roothub; 40902ac6454SAndrew Thompson bus->roothub_msg[1].bus = bus; 41002ac6454SAndrew Thompson 41102ac6454SAndrew Thompson /* Create USB explore, roothub and callback processes */ 41202ac6454SAndrew Thompson 41302ac6454SAndrew Thompson if (usb2_proc_create(&bus->giant_callback_proc, 41402ac6454SAndrew Thompson &bus->bus_mtx, pname, USB_PRI_MED)) { 41502ac6454SAndrew Thompson printf("WARNING: Creation of USB Giant " 41602ac6454SAndrew Thompson "callback process failed.\n"); 41702ac6454SAndrew Thompson } else if (usb2_proc_create(&bus->non_giant_callback_proc, 41802ac6454SAndrew Thompson &bus->bus_mtx, pname, USB_PRI_HIGH)) { 41902ac6454SAndrew Thompson printf("WARNING: Creation of USB non-Giant " 42002ac6454SAndrew Thompson "callback process failed.\n"); 42102ac6454SAndrew Thompson } else if (usb2_proc_create(&bus->roothub_proc, 42202ac6454SAndrew Thompson &bus->bus_mtx, pname, USB_PRI_HIGH)) { 42302ac6454SAndrew Thompson printf("WARNING: Creation of USB roothub " 42402ac6454SAndrew Thompson "process failed.\n"); 42502ac6454SAndrew Thompson } else if (usb2_proc_create(&bus->explore_proc, 42602ac6454SAndrew Thompson &bus->bus_mtx, pname, USB_PRI_MED)) { 42702ac6454SAndrew Thompson printf("WARNING: Creation of USB explore " 42802ac6454SAndrew Thompson "process failed.\n"); 42902ac6454SAndrew Thompson } else { 43002ac6454SAndrew Thompson /* Get final attach going */ 43102ac6454SAndrew Thompson USB_BUS_LOCK(bus); 43202ac6454SAndrew Thompson if (usb2_proc_msignal(&bus->explore_proc, 43302ac6454SAndrew Thompson &bus->attach_msg[0], &bus->attach_msg[1])) { 43402ac6454SAndrew Thompson /* ignore */ 43502ac6454SAndrew Thompson } 43602ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 43702ac6454SAndrew Thompson } 43802ac6454SAndrew Thompson } 43902ac6454SAndrew Thompson 44002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 44102ac6454SAndrew Thompson * usb2_post_init 44202ac6454SAndrew Thompson * 44302ac6454SAndrew Thompson * This function is called to attach all USB busses that were found 44402ac6454SAndrew Thompson * during bootup. 44502ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 44602ac6454SAndrew Thompson static void 44702ac6454SAndrew Thompson usb2_post_init(void *arg) 44802ac6454SAndrew Thompson { 44902ac6454SAndrew Thompson struct usb2_bus *bus; 45002ac6454SAndrew Thompson devclass_t dc; 45102ac6454SAndrew Thompson device_t dev; 45202ac6454SAndrew Thompson int max; 45302ac6454SAndrew Thompson int n; 45402ac6454SAndrew Thompson 45502ac6454SAndrew Thompson mtx_lock(&Giant); 45602ac6454SAndrew Thompson 45702ac6454SAndrew Thompson usb2_devclass_ptr = devclass_find("usbus"); 45802ac6454SAndrew Thompson 45902ac6454SAndrew Thompson dc = usb2_devclass_ptr; 46002ac6454SAndrew Thompson if (dc) { 46102ac6454SAndrew Thompson max = devclass_get_maxunit(dc) + 1; 46202ac6454SAndrew Thompson for (n = 0; n != max; n++) { 46302ac6454SAndrew Thompson dev = devclass_get_device(dc, n); 46402ac6454SAndrew Thompson if (dev && device_is_attached(dev)) { 46502ac6454SAndrew Thompson bus = device_get_ivars(dev); 46602ac6454SAndrew Thompson if (bus) { 46702ac6454SAndrew Thompson mtx_lock(&Giant); 46802ac6454SAndrew Thompson usb2_attach_sub(dev, bus); 46902ac6454SAndrew Thompson mtx_unlock(&Giant); 47002ac6454SAndrew Thompson } 47102ac6454SAndrew Thompson } 47202ac6454SAndrew Thompson } 47302ac6454SAndrew Thompson } else { 47402ac6454SAndrew Thompson DPRINTFN(0, "no devclass\n"); 47502ac6454SAndrew Thompson } 47602ac6454SAndrew Thompson usb2_post_init_called = 1; 47702ac6454SAndrew Thompson 47802ac6454SAndrew Thompson /* explore all USB busses in parallell */ 47902ac6454SAndrew Thompson 48002ac6454SAndrew Thompson usb2_needs_explore_all(); 48102ac6454SAndrew Thompson 48202ac6454SAndrew Thompson mtx_unlock(&Giant); 48302ac6454SAndrew Thompson } 48402ac6454SAndrew Thompson 48502ac6454SAndrew Thompson SYSINIT(usb2_post_init, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb2_post_init, NULL); 48602ac6454SAndrew Thompson SYSUNINIT(usb2_bus_unload, SI_SUB_KLD, SI_ORDER_ANY, usb2_bus_unload, NULL); 48702ac6454SAndrew Thompson 48802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 48902ac6454SAndrew Thompson * usb2_bus_mem_flush_all_cb 49002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 49102ac6454SAndrew Thompson static void 49202ac6454SAndrew Thompson usb2_bus_mem_flush_all_cb(struct usb2_bus *bus, struct usb2_page_cache *pc, 49302ac6454SAndrew Thompson struct usb2_page *pg, uint32_t size, uint32_t align) 49402ac6454SAndrew Thompson { 49502ac6454SAndrew Thompson usb2_pc_cpu_flush(pc); 49602ac6454SAndrew Thompson } 49702ac6454SAndrew Thompson 49802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 49902ac6454SAndrew Thompson * usb2_bus_mem_flush_all - factored out code 50002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 50102ac6454SAndrew Thompson void 50202ac6454SAndrew Thompson usb2_bus_mem_flush_all(struct usb2_bus *bus, usb2_bus_mem_cb_t *cb) 50302ac6454SAndrew Thompson { 50402ac6454SAndrew Thompson if (cb) { 50502ac6454SAndrew Thompson cb(bus, &usb2_bus_mem_flush_all_cb); 50602ac6454SAndrew Thompson } 50702ac6454SAndrew Thompson } 50802ac6454SAndrew Thompson 50902ac6454SAndrew Thompson /*------------------------------------------------------------------------* 51002ac6454SAndrew Thompson * usb2_bus_mem_alloc_all_cb 51102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 51202ac6454SAndrew Thompson static void 51302ac6454SAndrew Thompson usb2_bus_mem_alloc_all_cb(struct usb2_bus *bus, struct usb2_page_cache *pc, 51402ac6454SAndrew Thompson struct usb2_page *pg, uint32_t size, uint32_t align) 51502ac6454SAndrew Thompson { 51602ac6454SAndrew Thompson /* need to initialize the page cache */ 51702ac6454SAndrew Thompson pc->tag_parent = bus->dma_parent_tag; 51802ac6454SAndrew Thompson 51902ac6454SAndrew Thompson if (usb2_pc_alloc_mem(pc, pg, size, align)) { 52002ac6454SAndrew Thompson bus->alloc_failed = 1; 52102ac6454SAndrew Thompson } 52202ac6454SAndrew Thompson } 52302ac6454SAndrew Thompson 52402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 52502ac6454SAndrew Thompson * usb2_bus_mem_alloc_all - factored out code 52602ac6454SAndrew Thompson * 52702ac6454SAndrew Thompson * Returns: 52802ac6454SAndrew Thompson * 0: Success 52902ac6454SAndrew Thompson * Else: Failure 53002ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 53102ac6454SAndrew Thompson uint8_t 53202ac6454SAndrew Thompson usb2_bus_mem_alloc_all(struct usb2_bus *bus, bus_dma_tag_t dmat, 53302ac6454SAndrew Thompson usb2_bus_mem_cb_t *cb) 53402ac6454SAndrew Thompson { 53502ac6454SAndrew Thompson bus->alloc_failed = 0; 53602ac6454SAndrew Thompson 53702ac6454SAndrew Thompson mtx_init(&bus->bus_mtx, device_get_nameunit(bus->parent), 53802ac6454SAndrew Thompson NULL, MTX_DEF | MTX_RECURSE); 53902ac6454SAndrew Thompson 54002ac6454SAndrew Thompson usb2_callout_init_mtx(&bus->power_wdog, 54102ac6454SAndrew Thompson &bus->bus_mtx, CALLOUT_RETURNUNLOCKED); 54202ac6454SAndrew Thompson 54302ac6454SAndrew Thompson TAILQ_INIT(&bus->intr_q.head); 54402ac6454SAndrew Thompson 54502ac6454SAndrew Thompson usb2_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags, 54602ac6454SAndrew Thompson dmat, &bus->bus_mtx, NULL, NULL, 32, USB_BUS_DMA_TAG_MAX); 54702ac6454SAndrew Thompson 54802ac6454SAndrew Thompson if ((bus->devices_max > USB_MAX_DEVICES) || 54902ac6454SAndrew Thompson (bus->devices_max < USB_MIN_DEVICES) || 55002ac6454SAndrew Thompson (bus->devices == NULL)) { 55102ac6454SAndrew Thompson DPRINTFN(0, "Devices field has not been " 55202ac6454SAndrew Thompson "initialised properly!\n"); 55302ac6454SAndrew Thompson bus->alloc_failed = 1; /* failure */ 55402ac6454SAndrew Thompson } 55502ac6454SAndrew Thompson if (cb) { 55602ac6454SAndrew Thompson cb(bus, &usb2_bus_mem_alloc_all_cb); 55702ac6454SAndrew Thompson } 55802ac6454SAndrew Thompson if (bus->alloc_failed) { 55902ac6454SAndrew Thompson usb2_bus_mem_free_all(bus, cb); 56002ac6454SAndrew Thompson } 56102ac6454SAndrew Thompson return (bus->alloc_failed); 56202ac6454SAndrew Thompson } 56302ac6454SAndrew Thompson 56402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 56502ac6454SAndrew Thompson * usb2_bus_mem_free_all_cb 56602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 56702ac6454SAndrew Thompson static void 56802ac6454SAndrew Thompson usb2_bus_mem_free_all_cb(struct usb2_bus *bus, struct usb2_page_cache *pc, 56902ac6454SAndrew Thompson struct usb2_page *pg, uint32_t size, uint32_t align) 57002ac6454SAndrew Thompson { 57102ac6454SAndrew Thompson usb2_pc_free_mem(pc); 57202ac6454SAndrew Thompson } 57302ac6454SAndrew Thompson 57402ac6454SAndrew Thompson /*------------------------------------------------------------------------* 57502ac6454SAndrew Thompson * usb2_bus_mem_free_all - factored out code 57602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 57702ac6454SAndrew Thompson void 57802ac6454SAndrew Thompson usb2_bus_mem_free_all(struct usb2_bus *bus, usb2_bus_mem_cb_t *cb) 57902ac6454SAndrew Thompson { 58002ac6454SAndrew Thompson if (cb) { 58102ac6454SAndrew Thompson cb(bus, &usb2_bus_mem_free_all_cb); 58202ac6454SAndrew Thompson } 58302ac6454SAndrew Thompson usb2_dma_tag_unsetup(bus->dma_parent_tag); 58402ac6454SAndrew Thompson 58502ac6454SAndrew Thompson mtx_destroy(&bus->bus_mtx); 58602ac6454SAndrew Thompson } 58702ac6454SAndrew Thompson 58802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 58902ac6454SAndrew Thompson * usb2_bus_roothub 59002ac6454SAndrew Thompson * 59102ac6454SAndrew Thompson * This function is used to execute roothub control requests on the 59202ac6454SAndrew Thompson * roothub and is called from the roothub process. 59302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 59402ac6454SAndrew Thompson static void 59502ac6454SAndrew Thompson usb2_bus_roothub(struct usb2_proc_msg *pm) 59602ac6454SAndrew Thompson { 59702ac6454SAndrew Thompson struct usb2_bus *bus; 59802ac6454SAndrew Thompson 59902ac6454SAndrew Thompson bus = ((struct usb2_bus_msg *)pm)->bus; 60002ac6454SAndrew Thompson 60102ac6454SAndrew Thompson USB_BUS_LOCK_ASSERT(bus, MA_OWNED); 60202ac6454SAndrew Thompson 60302ac6454SAndrew Thompson (bus->methods->roothub_exec) (bus); 60402ac6454SAndrew Thompson } 60502ac6454SAndrew Thompson 60602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 60702ac6454SAndrew Thompson * usb2_bus_roothub_exec 60802ac6454SAndrew Thompson * 60902ac6454SAndrew Thompson * This function is used to schedule the "roothub_done" bus callback 61002ac6454SAndrew Thompson * method. The bus lock must be locked when calling this function. 61102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 61202ac6454SAndrew Thompson void 61302ac6454SAndrew Thompson usb2_bus_roothub_exec(struct usb2_bus *bus) 61402ac6454SAndrew Thompson { 61502ac6454SAndrew Thompson USB_BUS_LOCK_ASSERT(bus, MA_OWNED); 61602ac6454SAndrew Thompson 61702ac6454SAndrew Thompson if (usb2_proc_msignal(&bus->roothub_proc, 61802ac6454SAndrew Thompson &bus->roothub_msg[0], &bus->roothub_msg[1])) { 61902ac6454SAndrew Thompson /* ignore */ 62002ac6454SAndrew Thompson } 62102ac6454SAndrew Thompson } 622