1184610Salfred/* $FreeBSD$ */ 2184610Salfred/*- 3184610Salfred * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 4184610Salfred * 5184610Salfred * Redistribution and use in source and binary forms, with or without 6184610Salfred * modification, are permitted provided that the following conditions 7184610Salfred * are met: 8184610Salfred * 1. Redistributions of source code must retain the above copyright 9184610Salfred * notice, this list of conditions and the following disclaimer. 10184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 11184610Salfred * notice, this list of conditions and the following disclaimer in the 12184610Salfred * documentation and/or other materials provided with the distribution. 13184610Salfred * 14184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24184610Salfred * SUCH DAMAGE. 25184610Salfred */ 26184610Salfred 27205005Sthompsa#include "opt_ddb.h" 28205005Sthompsa 29194677Sthompsa#include <sys/stdint.h> 30194677Sthompsa#include <sys/stddef.h> 31194677Sthompsa#include <sys/param.h> 32194677Sthompsa#include <sys/queue.h> 33194677Sthompsa#include <sys/types.h> 34194677Sthompsa#include <sys/systm.h> 35194677Sthompsa#include <sys/kernel.h> 36194677Sthompsa#include <sys/bus.h> 37194677Sthompsa#include <sys/module.h> 38194677Sthompsa#include <sys/lock.h> 39194677Sthompsa#include <sys/mutex.h> 40194677Sthompsa#include <sys/condvar.h> 41194677Sthompsa#include <sys/sysctl.h> 42194677Sthompsa#include <sys/sx.h> 43194677Sthompsa#include <sys/unistd.h> 44194677Sthompsa#include <sys/callout.h> 45194677Sthompsa#include <sys/malloc.h> 46194677Sthompsa#include <sys/priv.h> 47194677Sthompsa 48188942Sthompsa#include <dev/usb/usb.h> 49194677Sthompsa#include <dev/usb/usbdi.h> 50184610Salfred 51194228Sthompsa#define USB_DEBUG_VAR usb_ctrl_debug 52184610Salfred 53188942Sthompsa#include <dev/usb/usb_core.h> 54188942Sthompsa#include <dev/usb/usb_debug.h> 55188942Sthompsa#include <dev/usb/usb_process.h> 56188942Sthompsa#include <dev/usb/usb_busdma.h> 57188942Sthompsa#include <dev/usb/usb_dynamic.h> 58188942Sthompsa#include <dev/usb/usb_device.h> 59188942Sthompsa#include <dev/usb/usb_hub.h> 60184610Salfred 61188942Sthompsa#include <dev/usb/usb_controller.h> 62188942Sthompsa#include <dev/usb/usb_bus.h> 63215649Sweongyo#include <dev/usb/usb_pf.h> 64229096Shselasky#include "usb_if.h" 65184610Salfred 66190754Sthompsa/* function prototypes */ 67184610Salfred 68194228Sthompsastatic device_probe_t usb_probe; 69194228Sthompsastatic device_attach_t usb_attach; 70194228Sthompsastatic device_detach_t usb_detach; 71229096Shselaskystatic device_suspend_t usb_suspend; 72229096Shselaskystatic device_resume_t usb_resume; 73229096Shselaskystatic device_shutdown_t usb_shutdown; 74184610Salfred 75194228Sthompsastatic void usb_attach_sub(device_t, struct usb_bus *); 76184610Salfred 77184610Salfred/* static variables */ 78184610Salfred 79194677Sthompsa#ifdef USB_DEBUG 80194228Sthompsastatic int usb_ctrl_debug = 0; 81184610Salfred 82248085Smariusstatic SYSCTL_NODE(_hw_usb, OID_AUTO, ctrl, CTLFLAG_RW, 0, "USB controller"); 83194228SthompsaSYSCTL_INT(_hw_usb_ctrl, OID_AUTO, debug, CTLFLAG_RW, &usb_ctrl_debug, 0, 84184610Salfred "Debug level"); 85184610Salfred#endif 86184610Salfred 87195965Salfredstatic int usb_no_boot_wait = 0; 88195965SalfredTUNABLE_INT("hw.usb.no_boot_wait", &usb_no_boot_wait); 89242775ShselaskySYSCTL_INT(_hw_usb, OID_AUTO, no_boot_wait, CTLFLAG_RD|CTLFLAG_TUN, &usb_no_boot_wait, 0, 90229100Shselasky "No USB device enumerate waiting at boot."); 91195965Salfred 92232870Shselaskystatic int usb_no_suspend_wait = 0; 93232870ShselaskyTUNABLE_INT("hw.usb.no_suspend_wait", &usb_no_suspend_wait); 94232870ShselaskySYSCTL_INT(_hw_usb, OID_AUTO, no_suspend_wait, CTLFLAG_RW|CTLFLAG_TUN, 95232870Shselasky &usb_no_suspend_wait, 0, "No USB device waiting at system suspend."); 96232870Shselasky 97229100Shselaskystatic int usb_no_shutdown_wait = 0; 98229100ShselaskyTUNABLE_INT("hw.usb.no_shutdown_wait", &usb_no_shutdown_wait); 99232870ShselaskySYSCTL_INT(_hw_usb, OID_AUTO, no_shutdown_wait, CTLFLAG_RW|CTLFLAG_TUN, 100232870Shselasky &usb_no_shutdown_wait, 0, "No USB device waiting at system shutdown."); 101229100Shselasky 102194228Sthompsastatic devclass_t usb_devclass; 103184610Salfred 104194228Sthompsastatic device_method_t usb_methods[] = { 105194228Sthompsa DEVMETHOD(device_probe, usb_probe), 106194228Sthompsa DEVMETHOD(device_attach, usb_attach), 107194228Sthompsa DEVMETHOD(device_detach, usb_detach), 108229096Shselasky DEVMETHOD(device_suspend, usb_suspend), 109229096Shselasky DEVMETHOD(device_resume, usb_resume), 110229096Shselasky DEVMETHOD(device_shutdown, usb_shutdown), 111184610Salfred {0, 0} 112184610Salfred}; 113184610Salfred 114194228Sthompsastatic driver_t usb_driver = { 115184610Salfred .name = "usbus", 116194228Sthompsa .methods = usb_methods, 117184610Salfred .size = 0, 118184610Salfred}; 119184610Salfred 120213426Shselasky/* Host Only Drivers */ 121194228SthompsaDRIVER_MODULE(usbus, ohci, usb_driver, usb_devclass, 0, 0); 122194228SthompsaDRIVER_MODULE(usbus, uhci, usb_driver, usb_devclass, 0, 0); 123194228SthompsaDRIVER_MODULE(usbus, ehci, usb_driver, usb_devclass, 0, 0); 124213435ShselaskyDRIVER_MODULE(usbus, xhci, usb_driver, usb_devclass, 0, 0); 125213426Shselasky 126213426Shselasky/* Device Only Drivers */ 127194228SthompsaDRIVER_MODULE(usbus, at91_udp, usb_driver, usb_devclass, 0, 0); 128213426ShselaskyDRIVER_MODULE(usbus, musbotg, usb_driver, usb_devclass, 0, 0); 129194228SthompsaDRIVER_MODULE(usbus, uss820, usb_driver, usb_devclass, 0, 0); 130184610Salfred 131184610Salfred/*------------------------------------------------------------------------* 132194228Sthompsa * usb_probe 133184610Salfred * 134184610Salfred * This function is called from "{ehci,ohci,uhci}_pci_attach()". 135184610Salfred *------------------------------------------------------------------------*/ 136184610Salfredstatic int 137194228Sthompsausb_probe(device_t dev) 138184610Salfred{ 139184610Salfred DPRINTF("\n"); 140184610Salfred return (0); 141184610Salfred} 142184610Salfred 143213425Shselaskystatic void 144213425Shselaskyusb_root_mount_rel(struct usb_bus *bus) 145213425Shselasky{ 146213425Shselasky if (bus->bus_roothold != NULL) { 147213425Shselasky DPRINTF("Releasing root mount hold %p\n", bus->bus_roothold); 148213425Shselasky root_mount_rel(bus->bus_roothold); 149213425Shselasky bus->bus_roothold = NULL; 150213425Shselasky } 151213425Shselasky} 152213425Shselasky 153184610Salfred/*------------------------------------------------------------------------* 154194228Sthompsa * usb_attach 155184610Salfred *------------------------------------------------------------------------*/ 156184610Salfredstatic int 157194228Sthompsausb_attach(device_t dev) 158184610Salfred{ 159192984Sthompsa struct usb_bus *bus = device_get_ivars(dev); 160184610Salfred 161184610Salfred DPRINTF("\n"); 162184610Salfred 163184610Salfred if (bus == NULL) { 164199816Sthompsa device_printf(dev, "USB device has no ivars\n"); 165184610Salfred return (ENXIO); 166184610Salfred } 167188907Sthompsa 168195965Salfred if (usb_no_boot_wait == 0) { 169195965Salfred /* delay vfs_mountroot until the bus is explored */ 170195965Salfred bus->bus_roothold = root_mount_hold(device_get_nameunit(dev)); 171195965Salfred } 172188907Sthompsa 173196488Salfred usb_attach_sub(dev, bus); 174196488Salfred 175184610Salfred return (0); /* return success */ 176184610Salfred} 177184610Salfred 178184610Salfred/*------------------------------------------------------------------------* 179194228Sthompsa * usb_detach 180184610Salfred *------------------------------------------------------------------------*/ 181184610Salfredstatic int 182194228Sthompsausb_detach(device_t dev) 183184610Salfred{ 184192984Sthompsa struct usb_bus *bus = device_get_softc(dev); 185184610Salfred 186184610Salfred DPRINTF("\n"); 187184610Salfred 188184610Salfred if (bus == NULL) { 189184610Salfred /* was never setup properly */ 190184610Salfred return (0); 191184610Salfred } 192186730Salfred /* Stop power watchdog */ 193194228Sthompsa usb_callout_drain(&bus->power_wdog); 194186730Salfred 195184610Salfred /* Let the USB explore process detach all devices. */ 196213425Shselasky usb_root_mount_rel(bus); 197184610Salfred 198184824Sthompsa USB_BUS_LOCK(bus); 199229096Shselasky 200229096Shselasky /* Queue detach job */ 201229096Shselasky usb_proc_msignal(&bus->explore_proc, 202229096Shselasky &bus->detach_msg[0], &bus->detach_msg[1]); 203229096Shselasky 204184610Salfred /* Wait for detach to complete */ 205194228Sthompsa usb_proc_mwait(&bus->explore_proc, 206184610Salfred &bus->detach_msg[0], &bus->detach_msg[1]); 207184610Salfred 208184824Sthompsa USB_BUS_UNLOCK(bus); 209184610Salfred 210187174Sthompsa /* Get rid of USB callback processes */ 211187174Sthompsa 212194228Sthompsa usb_proc_free(&bus->giant_callback_proc); 213194228Sthompsa usb_proc_free(&bus->non_giant_callback_proc); 214187174Sthompsa 215184610Salfred /* Get rid of USB explore process */ 216184610Salfred 217194228Sthompsa usb_proc_free(&bus->explore_proc); 218184610Salfred 219191400Sthompsa /* Get rid of control transfer process */ 220191400Sthompsa 221194228Sthompsa usb_proc_free(&bus->control_xfer_proc); 222191400Sthompsa 223217558Shselasky#if USB_HAVE_PF 224215812Sweongyo usbpf_detach(bus); 225217558Shselasky#endif 226184610Salfred return (0); 227184610Salfred} 228184610Salfred 229184610Salfred/*------------------------------------------------------------------------* 230229096Shselasky * usb_suspend 231229096Shselasky *------------------------------------------------------------------------*/ 232229096Shselaskystatic int 233229096Shselaskyusb_suspend(device_t dev) 234229096Shselasky{ 235229096Shselasky struct usb_bus *bus = device_get_softc(dev); 236229096Shselasky 237229096Shselasky DPRINTF("\n"); 238229096Shselasky 239229096Shselasky if (bus == NULL) { 240229096Shselasky /* was never setup properly */ 241229096Shselasky return (0); 242229096Shselasky } 243229096Shselasky 244229096Shselasky USB_BUS_LOCK(bus); 245229096Shselasky usb_proc_msignal(&bus->explore_proc, 246229096Shselasky &bus->suspend_msg[0], &bus->suspend_msg[1]); 247232870Shselasky if (usb_no_suspend_wait == 0) { 248232870Shselasky /* wait for suspend callback to be executed */ 249232870Shselasky usb_proc_mwait(&bus->explore_proc, 250232870Shselasky &bus->suspend_msg[0], &bus->suspend_msg[1]); 251232870Shselasky } 252229096Shselasky USB_BUS_UNLOCK(bus); 253229096Shselasky 254229096Shselasky return (0); 255229096Shselasky} 256229096Shselasky 257229096Shselasky/*------------------------------------------------------------------------* 258229096Shselasky * usb_resume 259229096Shselasky *------------------------------------------------------------------------*/ 260229096Shselaskystatic int 261229096Shselaskyusb_resume(device_t dev) 262229096Shselasky{ 263229096Shselasky struct usb_bus *bus = device_get_softc(dev); 264229096Shselasky 265229096Shselasky DPRINTF("\n"); 266229096Shselasky 267229096Shselasky if (bus == NULL) { 268229096Shselasky /* was never setup properly */ 269229096Shselasky return (0); 270229096Shselasky } 271229096Shselasky 272229096Shselasky USB_BUS_LOCK(bus); 273229096Shselasky usb_proc_msignal(&bus->explore_proc, 274229096Shselasky &bus->resume_msg[0], &bus->resume_msg[1]); 275229096Shselasky USB_BUS_UNLOCK(bus); 276229096Shselasky 277229096Shselasky return (0); 278229096Shselasky} 279229096Shselasky 280229096Shselasky/*------------------------------------------------------------------------* 281259602Shselasky * usb_bus_reset_async_locked 282259602Shselasky *------------------------------------------------------------------------*/ 283259602Shselaskyvoid 284259602Shselaskyusb_bus_reset_async_locked(struct usb_bus *bus) 285259602Shselasky{ 286259602Shselasky USB_BUS_LOCK_ASSERT(bus, MA_OWNED); 287259602Shselasky 288259602Shselasky DPRINTF("\n"); 289259602Shselasky 290259602Shselasky if (bus->reset_msg[0].hdr.pm_qentry.tqe_prev != NULL || 291259602Shselasky bus->reset_msg[1].hdr.pm_qentry.tqe_prev != NULL) { 292259602Shselasky DPRINTF("Reset already pending\n"); 293259602Shselasky return; 294259602Shselasky } 295259602Shselasky 296259602Shselasky device_printf(bus->parent, "Resetting controller\n"); 297259602Shselasky 298259602Shselasky usb_proc_msignal(&bus->explore_proc, 299259602Shselasky &bus->reset_msg[0], &bus->reset_msg[1]); 300259602Shselasky} 301259602Shselasky 302259602Shselasky/*------------------------------------------------------------------------* 303229096Shselasky * usb_shutdown 304229096Shselasky *------------------------------------------------------------------------*/ 305229096Shselaskystatic int 306229096Shselaskyusb_shutdown(device_t dev) 307229096Shselasky{ 308229096Shselasky struct usb_bus *bus = device_get_softc(dev); 309229096Shselasky 310229096Shselasky DPRINTF("\n"); 311229096Shselasky 312229096Shselasky if (bus == NULL) { 313229096Shselasky /* was never setup properly */ 314229096Shselasky return (0); 315229096Shselasky } 316229096Shselasky 317229100Shselasky device_printf(bus->bdev, "Controller shutdown\n"); 318229100Shselasky 319229096Shselasky USB_BUS_LOCK(bus); 320229096Shselasky usb_proc_msignal(&bus->explore_proc, 321229096Shselasky &bus->shutdown_msg[0], &bus->shutdown_msg[1]); 322229100Shselasky if (usb_no_shutdown_wait == 0) { 323229100Shselasky /* wait for shutdown callback to be executed */ 324229100Shselasky usb_proc_mwait(&bus->explore_proc, 325229100Shselasky &bus->shutdown_msg[0], &bus->shutdown_msg[1]); 326229100Shselasky } 327229096Shselasky USB_BUS_UNLOCK(bus); 328229096Shselasky 329229100Shselasky device_printf(bus->bdev, "Controller shutdown complete\n"); 330229100Shselasky 331229096Shselasky return (0); 332229096Shselasky} 333229096Shselasky 334229096Shselasky/*------------------------------------------------------------------------* 335194228Sthompsa * usb_bus_explore 336184610Salfred * 337184610Salfred * This function is used to explore the device tree from the root. 338184610Salfred *------------------------------------------------------------------------*/ 339184610Salfredstatic void 340194228Sthompsausb_bus_explore(struct usb_proc_msg *pm) 341184610Salfred{ 342192984Sthompsa struct usb_bus *bus; 343192984Sthompsa struct usb_device *udev; 344184610Salfred 345192984Sthompsa bus = ((struct usb_bus_msg *)pm)->bus; 346184610Salfred udev = bus->devices[USB_ROOT_HUB_ADDR]; 347184610Salfred 348229096Shselasky if (bus->no_explore != 0) 349229096Shselasky return; 350229096Shselasky 351267350Shselasky if (udev != NULL) { 352267350Shselasky USB_BUS_UNLOCK(bus); 353267350Shselasky uhub_explore_handle_re_enumerate(udev); 354267350Shselasky USB_BUS_LOCK(bus); 355267350Shselasky } 356184610Salfred 357267350Shselasky if (udev != NULL && udev->hub != NULL) { 358267350Shselasky 359184610Salfred if (bus->do_probe) { 360184610Salfred bus->do_probe = 0; 361184610Salfred bus->driver_added_refcount++; 362184610Salfred } 363184610Salfred if (bus->driver_added_refcount == 0) { 364184610Salfred /* avoid zero, hence that is memory default */ 365184610Salfred bus->driver_added_refcount = 1; 366184610Salfred } 367196488Salfred 368205005Sthompsa#ifdef DDB 369196488Salfred /* 370196488Salfred * The following three lines of code are only here to 371196488Salfred * recover from DDB: 372196488Salfred */ 373196488Salfred usb_proc_rewakeup(&bus->control_xfer_proc); 374196488Salfred usb_proc_rewakeup(&bus->giant_callback_proc); 375196488Salfred usb_proc_rewakeup(&bus->non_giant_callback_proc); 376205005Sthompsa#endif 377196488Salfred 378184824Sthompsa USB_BUS_UNLOCK(bus); 379184610Salfred 380208013Sthompsa#if USB_HAVE_POWERD 381184610Salfred /* 382186730Salfred * First update the USB power state! 383186730Salfred */ 384194228Sthompsa usb_bus_powerd(bus); 385208013Sthompsa#endif 386196488Salfred /* Explore the Root USB HUB. */ 387196403Sjhb (udev->hub->explore) (udev); 388184824Sthompsa USB_BUS_LOCK(bus); 389184610Salfred } 390213425Shselasky usb_root_mount_rel(bus); 391184610Salfred} 392184610Salfred 393184610Salfred/*------------------------------------------------------------------------* 394194228Sthompsa * usb_bus_detach 395184610Salfred * 396184610Salfred * This function is used to detach the device tree from the root. 397184610Salfred *------------------------------------------------------------------------*/ 398184610Salfredstatic void 399194228Sthompsausb_bus_detach(struct usb_proc_msg *pm) 400184610Salfred{ 401192984Sthompsa struct usb_bus *bus; 402192984Sthompsa struct usb_device *udev; 403184610Salfred device_t dev; 404184610Salfred 405192984Sthompsa bus = ((struct usb_bus_msg *)pm)->bus; 406184610Salfred udev = bus->devices[USB_ROOT_HUB_ADDR]; 407184610Salfred dev = bus->bdev; 408184610Salfred /* clear the softc */ 409184610Salfred device_set_softc(dev, NULL); 410184824Sthompsa USB_BUS_UNLOCK(bus); 411184610Salfred 412196488Salfred /* detach children first */ 413196403Sjhb mtx_lock(&Giant); 414184610Salfred bus_generic_detach(dev); 415196488Salfred mtx_unlock(&Giant); 416184610Salfred 417184610Salfred /* 418197553Sthompsa * Free USB device and all subdevices, if any. 419184610Salfred */ 420197553Sthompsa usb_free_device(udev, 0); 421184610Salfred 422184824Sthompsa USB_BUS_LOCK(bus); 423184610Salfred /* clear bdev variable last */ 424184610Salfred bus->bdev = NULL; 425184610Salfred} 426184610Salfred 427229096Shselasky/*------------------------------------------------------------------------* 428229096Shselasky * usb_bus_suspend 429229096Shselasky * 430259602Shselasky * This function is used to suspend the USB controller. 431229096Shselasky *------------------------------------------------------------------------*/ 432186730Salfredstatic void 433229096Shselaskyusb_bus_suspend(struct usb_proc_msg *pm) 434229096Shselasky{ 435229096Shselasky struct usb_bus *bus; 436229096Shselasky struct usb_device *udev; 437229096Shselasky usb_error_t err; 438247090Shselasky uint8_t do_unlock; 439229096Shselasky 440259602Shselasky DPRINTF("\n"); 441259602Shselasky 442229096Shselasky bus = ((struct usb_bus_msg *)pm)->bus; 443229096Shselasky udev = bus->devices[USB_ROOT_HUB_ADDR]; 444229096Shselasky 445229096Shselasky if (udev == NULL || bus->bdev == NULL) 446229096Shselasky return; 447229096Shselasky 448229100Shselasky USB_BUS_UNLOCK(bus); 449229100Shselasky 450232870Shselasky /* 451232870Shselasky * We use the shutdown event here because the suspend and 452232870Shselasky * resume events are reserved for the USB port suspend and 453232870Shselasky * resume. The USB system suspend is implemented like full 454232870Shselasky * shutdown and all connected USB devices will be disconnected 455232870Shselasky * subsequently. At resume all USB devices will be 456232870Shselasky * re-connected again. 457232870Shselasky */ 458232870Shselasky 459229096Shselasky bus_generic_shutdown(bus->bdev); 460229096Shselasky 461247090Shselasky do_unlock = usbd_enum_lock(udev); 462229096Shselasky 463229096Shselasky err = usbd_set_config_index(udev, USB_UNCONFIG_INDEX); 464229096Shselasky if (err) 465229096Shselasky device_printf(bus->bdev, "Could not unconfigure root HUB\n"); 466229096Shselasky 467229096Shselasky USB_BUS_LOCK(bus); 468229096Shselasky bus->hw_power_state = 0; 469229096Shselasky bus->no_explore = 1; 470229096Shselasky USB_BUS_UNLOCK(bus); 471229096Shselasky 472229096Shselasky if (bus->methods->set_hw_power != NULL) 473229096Shselasky (bus->methods->set_hw_power) (bus); 474229096Shselasky 475229096Shselasky if (bus->methods->set_hw_power_sleep != NULL) 476229096Shselasky (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_SUSPEND); 477229096Shselasky 478247090Shselasky if (do_unlock) 479247090Shselasky usbd_enum_unlock(udev); 480229100Shselasky 481229100Shselasky USB_BUS_LOCK(bus); 482229096Shselasky} 483229096Shselasky 484229096Shselasky/*------------------------------------------------------------------------* 485229096Shselasky * usb_bus_resume 486229096Shselasky * 487259602Shselasky * This function is used to resume the USB controller. 488229096Shselasky *------------------------------------------------------------------------*/ 489229096Shselaskystatic void 490229096Shselaskyusb_bus_resume(struct usb_proc_msg *pm) 491229096Shselasky{ 492229096Shselasky struct usb_bus *bus; 493229096Shselasky struct usb_device *udev; 494229096Shselasky usb_error_t err; 495247090Shselasky uint8_t do_unlock; 496229096Shselasky 497259602Shselasky DPRINTF("\n"); 498259602Shselasky 499229096Shselasky bus = ((struct usb_bus_msg *)pm)->bus; 500229096Shselasky udev = bus->devices[USB_ROOT_HUB_ADDR]; 501229096Shselasky 502229096Shselasky if (udev == NULL || bus->bdev == NULL) 503229096Shselasky return; 504229096Shselasky 505229100Shselasky USB_BUS_UNLOCK(bus); 506229100Shselasky 507247090Shselasky do_unlock = usbd_enum_lock(udev); 508229096Shselasky#if 0 509229096Shselasky DEVMETHOD(usb_take_controller, NULL); /* dummy */ 510229096Shselasky#endif 511229096Shselasky USB_TAKE_CONTROLLER(device_get_parent(bus->bdev)); 512229096Shselasky 513229096Shselasky USB_BUS_LOCK(bus); 514229096Shselasky bus->hw_power_state = 515229096Shselasky USB_HW_POWER_CONTROL | 516229096Shselasky USB_HW_POWER_BULK | 517229096Shselasky USB_HW_POWER_INTERRUPT | 518229096Shselasky USB_HW_POWER_ISOC | 519229096Shselasky USB_HW_POWER_NON_ROOT_HUB; 520229096Shselasky bus->no_explore = 0; 521229096Shselasky USB_BUS_UNLOCK(bus); 522229096Shselasky 523229096Shselasky if (bus->methods->set_hw_power_sleep != NULL) 524229096Shselasky (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_RESUME); 525229096Shselasky 526229096Shselasky if (bus->methods->set_hw_power != NULL) 527229096Shselasky (bus->methods->set_hw_power) (bus); 528229096Shselasky 529229369Shselasky /* restore USB configuration to index 0 */ 530229096Shselasky err = usbd_set_config_index(udev, 0); 531229096Shselasky if (err) 532229096Shselasky device_printf(bus->bdev, "Could not configure root HUB\n"); 533229096Shselasky 534229369Shselasky /* probe and attach */ 535229369Shselasky err = usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY); 536229369Shselasky if (err) { 537229369Shselasky device_printf(bus->bdev, "Could not probe and " 538229369Shselasky "attach root HUB\n"); 539229369Shselasky } 540229369Shselasky 541247090Shselasky if (do_unlock) 542247090Shselasky usbd_enum_unlock(udev); 543229100Shselasky 544229100Shselasky USB_BUS_LOCK(bus); 545229096Shselasky} 546229096Shselasky 547229096Shselasky/*------------------------------------------------------------------------* 548259602Shselasky * usb_bus_reset 549259602Shselasky * 550259602Shselasky * This function is used to reset the USB controller. 551259602Shselasky *------------------------------------------------------------------------*/ 552259602Shselaskystatic void 553259602Shselaskyusb_bus_reset(struct usb_proc_msg *pm) 554259602Shselasky{ 555259602Shselasky struct usb_bus *bus; 556259602Shselasky 557259602Shselasky DPRINTF("\n"); 558259602Shselasky 559259602Shselasky bus = ((struct usb_bus_msg *)pm)->bus; 560259602Shselasky 561259602Shselasky if (bus->bdev == NULL || bus->no_explore != 0) 562259602Shselasky return; 563259602Shselasky 564259602Shselasky /* a suspend and resume will reset the USB controller */ 565259602Shselasky usb_bus_suspend(pm); 566259602Shselasky usb_bus_resume(pm); 567259602Shselasky} 568259602Shselasky 569259602Shselasky/*------------------------------------------------------------------------* 570229096Shselasky * usb_bus_shutdown 571229096Shselasky * 572259602Shselasky * This function is used to shutdown the USB controller. 573229096Shselasky *------------------------------------------------------------------------*/ 574229096Shselaskystatic void 575229096Shselaskyusb_bus_shutdown(struct usb_proc_msg *pm) 576229096Shselasky{ 577229096Shselasky struct usb_bus *bus; 578229096Shselasky struct usb_device *udev; 579229096Shselasky usb_error_t err; 580247090Shselasky uint8_t do_unlock; 581229096Shselasky 582229096Shselasky bus = ((struct usb_bus_msg *)pm)->bus; 583229096Shselasky udev = bus->devices[USB_ROOT_HUB_ADDR]; 584229096Shselasky 585229096Shselasky if (udev == NULL || bus->bdev == NULL) 586229096Shselasky return; 587229096Shselasky 588229100Shselasky USB_BUS_UNLOCK(bus); 589229100Shselasky 590229096Shselasky bus_generic_shutdown(bus->bdev); 591229096Shselasky 592247090Shselasky do_unlock = usbd_enum_lock(udev); 593229096Shselasky 594229096Shselasky err = usbd_set_config_index(udev, USB_UNCONFIG_INDEX); 595229096Shselasky if (err) 596229096Shselasky device_printf(bus->bdev, "Could not unconfigure root HUB\n"); 597229096Shselasky 598229096Shselasky USB_BUS_LOCK(bus); 599229096Shselasky bus->hw_power_state = 0; 600229096Shselasky bus->no_explore = 1; 601229096Shselasky USB_BUS_UNLOCK(bus); 602229096Shselasky 603229096Shselasky if (bus->methods->set_hw_power != NULL) 604229096Shselasky (bus->methods->set_hw_power) (bus); 605229096Shselasky 606229096Shselasky if (bus->methods->set_hw_power_sleep != NULL) 607229096Shselasky (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_SHUTDOWN); 608229096Shselasky 609247090Shselasky if (do_unlock) 610247090Shselasky usbd_enum_unlock(udev); 611229100Shselasky 612229100Shselasky USB_BUS_LOCK(bus); 613229096Shselasky} 614229096Shselasky 615229096Shselaskystatic void 616194228Sthompsausb_power_wdog(void *arg) 617186730Salfred{ 618192984Sthompsa struct usb_bus *bus = arg; 619186730Salfred 620186730Salfred USB_BUS_LOCK_ASSERT(bus, MA_OWNED); 621186730Salfred 622194228Sthompsa usb_callout_reset(&bus->power_wdog, 623194228Sthompsa 4 * hz, usb_power_wdog, arg); 624186730Salfred 625205005Sthompsa#ifdef DDB 626196488Salfred /* 627196488Salfred * The following line of code is only here to recover from 628196488Salfred * DDB: 629196488Salfred */ 630196488Salfred usb_proc_rewakeup(&bus->explore_proc); /* recover from DDB */ 631205005Sthompsa#endif 632196488Salfred 633208013Sthompsa#if USB_HAVE_POWERD 634186730Salfred USB_BUS_UNLOCK(bus); 635186730Salfred 636194228Sthompsa usb_bus_power_update(bus); 637186730Salfred 638190724Sthompsa USB_BUS_LOCK(bus); 639208013Sthompsa#endif 640186730Salfred} 641186730Salfred 642184610Salfred/*------------------------------------------------------------------------* 643194228Sthompsa * usb_bus_attach 644184610Salfred * 645186730Salfred * This function attaches USB in context of the explore thread. 646184610Salfred *------------------------------------------------------------------------*/ 647184610Salfredstatic void 648194228Sthompsausb_bus_attach(struct usb_proc_msg *pm) 649184610Salfred{ 650192984Sthompsa struct usb_bus *bus; 651192984Sthompsa struct usb_device *child; 652186730Salfred device_t dev; 653193045Sthompsa usb_error_t err; 654192500Sthompsa enum usb_dev_speed speed; 655184610Salfred 656192984Sthompsa bus = ((struct usb_bus_msg *)pm)->bus; 657186730Salfred dev = bus->bdev; 658186730Salfred 659184610Salfred DPRINTF("\n"); 660184610Salfred 661184610Salfred switch (bus->usbrev) { 662184610Salfred case USB_REV_1_0: 663184610Salfred speed = USB_SPEED_FULL; 664184610Salfred device_printf(bus->bdev, "12Mbps Full Speed USB v1.0\n"); 665184610Salfred break; 666184610Salfred 667184610Salfred case USB_REV_1_1: 668184610Salfred speed = USB_SPEED_FULL; 669184610Salfred device_printf(bus->bdev, "12Mbps Full Speed USB v1.1\n"); 670184610Salfred break; 671184610Salfred 672184610Salfred case USB_REV_2_0: 673184610Salfred speed = USB_SPEED_HIGH; 674184610Salfred device_printf(bus->bdev, "480Mbps High Speed USB v2.0\n"); 675184610Salfred break; 676184610Salfred 677184610Salfred case USB_REV_2_5: 678184610Salfred speed = USB_SPEED_VARIABLE; 679184610Salfred device_printf(bus->bdev, "480Mbps Wireless USB v2.5\n"); 680184610Salfred break; 681184610Salfred 682213435Shselasky case USB_REV_3_0: 683213435Shselasky speed = USB_SPEED_SUPER; 684219048Shselasky device_printf(bus->bdev, "5.0Gbps Super Speed USB v3.0\n"); 685213435Shselasky break; 686213435Shselasky 687184610Salfred default: 688199816Sthompsa device_printf(bus->bdev, "Unsupported USB revision\n"); 689213425Shselasky usb_root_mount_rel(bus); 690184610Salfred return; 691184610Salfred } 692184610Salfred 693190734Sthompsa /* default power_mask value */ 694190734Sthompsa bus->hw_power_state = 695190734Sthompsa USB_HW_POWER_CONTROL | 696190734Sthompsa USB_HW_POWER_BULK | 697190734Sthompsa USB_HW_POWER_INTERRUPT | 698190734Sthompsa USB_HW_POWER_ISOC | 699190734Sthompsa USB_HW_POWER_NON_ROOT_HUB; 700190734Sthompsa 701229096Shselasky USB_BUS_UNLOCK(bus); 702229096Shselasky 703190734Sthompsa /* make sure power is set at least once */ 704190734Sthompsa 705190734Sthompsa if (bus->methods->set_hw_power != NULL) { 706190734Sthompsa (bus->methods->set_hw_power) (bus); 707190734Sthompsa } 708190734Sthompsa 709229096Shselasky /* allocate the Root USB device */ 710184610Salfred 711194228Sthompsa child = usb_alloc_device(bus->bdev, bus, NULL, 0, 0, 1, 712184610Salfred speed, USB_MODE_HOST); 713184610Salfred if (child) { 714194228Sthompsa err = usb_probe_and_attach(child, 715184610Salfred USB_IFACE_INDEX_ANY); 716184610Salfred if (!err) { 717189676Sthompsa if ((bus->devices[USB_ROOT_HUB_ADDR] == NULL) || 718189676Sthompsa (bus->devices[USB_ROOT_HUB_ADDR]->hub == NULL)) { 719184610Salfred err = USB_ERR_NO_ROOT_HUB; 720184610Salfred } 721184610Salfred } 722184610Salfred } else { 723184610Salfred err = USB_ERR_NOMEM; 724184610Salfred } 725184610Salfred 726186730Salfred USB_BUS_LOCK(bus); 727186730Salfred 728184610Salfred if (err) { 729184610Salfred device_printf(bus->bdev, "Root HUB problem, error=%s\n", 730194228Sthompsa usbd_errstr(err)); 731213425Shselasky usb_root_mount_rel(bus); 732184610Salfred } 733186730Salfred 734186730Salfred /* set softc - we are ready */ 735186730Salfred device_set_softc(dev, bus); 736186730Salfred 737190724Sthompsa /* start watchdog */ 738194228Sthompsa usb_power_wdog(bus); 739186730Salfred} 740186730Salfred 741186730Salfred/*------------------------------------------------------------------------* 742194228Sthompsa * usb_attach_sub 743186730Salfred * 744196488Salfred * This function creates a thread which runs the USB attach code. 745186730Salfred *------------------------------------------------------------------------*/ 746186730Salfredstatic void 747194228Sthompsausb_attach_sub(device_t dev, struct usb_bus *bus) 748186730Salfred{ 749188409Sthompsa const char *pname = device_get_nameunit(dev); 750188409Sthompsa 751196488Salfred mtx_lock(&Giant); 752196488Salfred if (usb_devclass_ptr == NULL) 753196488Salfred usb_devclass_ptr = devclass_find("usbus"); 754196488Salfred mtx_unlock(&Giant); 755196488Salfred 756217558Shselasky#if USB_HAVE_PF 757215812Sweongyo usbpf_attach(bus); 758217558Shselasky#endif 759184610Salfred /* Initialise USB process messages */ 760194228Sthompsa bus->explore_msg[0].hdr.pm_callback = &usb_bus_explore; 761184610Salfred bus->explore_msg[0].bus = bus; 762194228Sthompsa bus->explore_msg[1].hdr.pm_callback = &usb_bus_explore; 763184610Salfred bus->explore_msg[1].bus = bus; 764184610Salfred 765194228Sthompsa bus->detach_msg[0].hdr.pm_callback = &usb_bus_detach; 766184610Salfred bus->detach_msg[0].bus = bus; 767194228Sthompsa bus->detach_msg[1].hdr.pm_callback = &usb_bus_detach; 768184610Salfred bus->detach_msg[1].bus = bus; 769184610Salfred 770194228Sthompsa bus->attach_msg[0].hdr.pm_callback = &usb_bus_attach; 771186730Salfred bus->attach_msg[0].bus = bus; 772194228Sthompsa bus->attach_msg[1].hdr.pm_callback = &usb_bus_attach; 773186730Salfred bus->attach_msg[1].bus = bus; 774186730Salfred 775229096Shselasky bus->suspend_msg[0].hdr.pm_callback = &usb_bus_suspend; 776229096Shselasky bus->suspend_msg[0].bus = bus; 777229096Shselasky bus->suspend_msg[1].hdr.pm_callback = &usb_bus_suspend; 778229096Shselasky bus->suspend_msg[1].bus = bus; 779229096Shselasky 780229096Shselasky bus->resume_msg[0].hdr.pm_callback = &usb_bus_resume; 781229096Shselasky bus->resume_msg[0].bus = bus; 782229096Shselasky bus->resume_msg[1].hdr.pm_callback = &usb_bus_resume; 783229096Shselasky bus->resume_msg[1].bus = bus; 784229096Shselasky 785259602Shselasky bus->reset_msg[0].hdr.pm_callback = &usb_bus_reset; 786259602Shselasky bus->reset_msg[0].bus = bus; 787259602Shselasky bus->reset_msg[1].hdr.pm_callback = &usb_bus_reset; 788259602Shselasky bus->reset_msg[1].bus = bus; 789259602Shselasky 790229096Shselasky bus->shutdown_msg[0].hdr.pm_callback = &usb_bus_shutdown; 791229096Shselasky bus->shutdown_msg[0].bus = bus; 792229096Shselasky bus->shutdown_msg[1].hdr.pm_callback = &usb_bus_shutdown; 793229096Shselasky bus->shutdown_msg[1].bus = bus; 794229096Shselasky 795190735Sthompsa /* Create USB explore and callback processes */ 796187172Sthompsa 797194228Sthompsa if (usb_proc_create(&bus->giant_callback_proc, 798188409Sthompsa &bus->bus_mtx, pname, USB_PRI_MED)) { 799218475Shselasky device_printf(dev, "WARNING: Creation of USB Giant " 800187174Sthompsa "callback process failed.\n"); 801194228Sthompsa } else if (usb_proc_create(&bus->non_giant_callback_proc, 802188409Sthompsa &bus->bus_mtx, pname, USB_PRI_HIGH)) { 803218475Shselasky device_printf(dev, "WARNING: Creation of USB non-Giant " 804187174Sthompsa "callback process failed.\n"); 805194228Sthompsa } else if (usb_proc_create(&bus->explore_proc, 806188409Sthompsa &bus->bus_mtx, pname, USB_PRI_MED)) { 807218475Shselasky device_printf(dev, "WARNING: Creation of USB explore " 808187172Sthompsa "process failed.\n"); 809194228Sthompsa } else if (usb_proc_create(&bus->control_xfer_proc, 810191400Sthompsa &bus->bus_mtx, pname, USB_PRI_MED)) { 811218475Shselasky device_printf(dev, "WARNING: Creation of USB control transfer " 812191400Sthompsa "process failed.\n"); 813186730Salfred } else { 814186730Salfred /* Get final attach going */ 815186730Salfred USB_BUS_LOCK(bus); 816229096Shselasky usb_proc_msignal(&bus->explore_proc, 817229096Shselasky &bus->attach_msg[0], &bus->attach_msg[1]); 818186730Salfred USB_BUS_UNLOCK(bus); 819184610Salfred 820196488Salfred /* Do initial explore */ 821196488Salfred usb_needs_explore(bus, 1); 822184610Salfred } 823184610Salfred} 824184610Salfred 825194228SthompsaSYSUNINIT(usb_bus_unload, SI_SUB_KLD, SI_ORDER_ANY, usb_bus_unload, NULL); 826184610Salfred 827184610Salfred/*------------------------------------------------------------------------* 828194228Sthompsa * usb_bus_mem_flush_all_cb 829184610Salfred *------------------------------------------------------------------------*/ 830190180Sthompsa#if USB_HAVE_BUSDMA 831184610Salfredstatic void 832194228Sthompsausb_bus_mem_flush_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, 833193074Sthompsa struct usb_page *pg, usb_size_t size, usb_size_t align) 834184610Salfred{ 835194228Sthompsa usb_pc_cpu_flush(pc); 836184610Salfred} 837190180Sthompsa#endif 838184610Salfred 839184610Salfred/*------------------------------------------------------------------------* 840194228Sthompsa * usb_bus_mem_flush_all - factored out code 841184610Salfred *------------------------------------------------------------------------*/ 842190180Sthompsa#if USB_HAVE_BUSDMA 843184610Salfredvoid 844194228Sthompsausb_bus_mem_flush_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb) 845184610Salfred{ 846184610Salfred if (cb) { 847194228Sthompsa cb(bus, &usb_bus_mem_flush_all_cb); 848184610Salfred } 849184610Salfred} 850190180Sthompsa#endif 851184610Salfred 852184610Salfred/*------------------------------------------------------------------------* 853194228Sthompsa * usb_bus_mem_alloc_all_cb 854184610Salfred *------------------------------------------------------------------------*/ 855190180Sthompsa#if USB_HAVE_BUSDMA 856184610Salfredstatic void 857194228Sthompsausb_bus_mem_alloc_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, 858193074Sthompsa struct usb_page *pg, usb_size_t size, usb_size_t align) 859184610Salfred{ 860184610Salfred /* need to initialize the page cache */ 861184610Salfred pc->tag_parent = bus->dma_parent_tag; 862184610Salfred 863194228Sthompsa if (usb_pc_alloc_mem(pc, pg, size, align)) { 864184610Salfred bus->alloc_failed = 1; 865184610Salfred } 866184610Salfred} 867190180Sthompsa#endif 868184610Salfred 869184610Salfred/*------------------------------------------------------------------------* 870194228Sthompsa * usb_bus_mem_alloc_all - factored out code 871184610Salfred * 872184610Salfred * Returns: 873184610Salfred * 0: Success 874184610Salfred * Else: Failure 875184610Salfred *------------------------------------------------------------------------*/ 876184610Salfreduint8_t 877194228Sthompsausb_bus_mem_alloc_all(struct usb_bus *bus, bus_dma_tag_t dmat, 878193045Sthompsa usb_bus_mem_cb_t *cb) 879184610Salfred{ 880184610Salfred bus->alloc_failed = 0; 881184610Salfred 882186439Sthompsa mtx_init(&bus->bus_mtx, device_get_nameunit(bus->parent), 883184610Salfred NULL, MTX_DEF | MTX_RECURSE); 884184610Salfred 885194228Sthompsa usb_callout_init_mtx(&bus->power_wdog, 886190724Sthompsa &bus->bus_mtx, 0); 887186730Salfred 888184610Salfred TAILQ_INIT(&bus->intr_q.head); 889184610Salfred 890190180Sthompsa#if USB_HAVE_BUSDMA 891194228Sthompsa usb_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags, 892190180Sthompsa dmat, &bus->bus_mtx, NULL, 32, USB_BUS_DMA_TAG_MAX); 893190180Sthompsa#endif 894187170Sthompsa if ((bus->devices_max > USB_MAX_DEVICES) || 895187170Sthompsa (bus->devices_max < USB_MIN_DEVICES) || 896187170Sthompsa (bus->devices == NULL)) { 897187170Sthompsa DPRINTFN(0, "Devices field has not been " 898199816Sthompsa "initialised properly\n"); 899187170Sthompsa bus->alloc_failed = 1; /* failure */ 900187170Sthompsa } 901190180Sthompsa#if USB_HAVE_BUSDMA 902184610Salfred if (cb) { 903194228Sthompsa cb(bus, &usb_bus_mem_alloc_all_cb); 904184610Salfred } 905190180Sthompsa#endif 906184610Salfred if (bus->alloc_failed) { 907194228Sthompsa usb_bus_mem_free_all(bus, cb); 908184610Salfred } 909184610Salfred return (bus->alloc_failed); 910184610Salfred} 911184610Salfred 912184610Salfred/*------------------------------------------------------------------------* 913194228Sthompsa * usb_bus_mem_free_all_cb 914184610Salfred *------------------------------------------------------------------------*/ 915190180Sthompsa#if USB_HAVE_BUSDMA 916184610Salfredstatic void 917194228Sthompsausb_bus_mem_free_all_cb(struct usb_bus *bus, struct usb_page_cache *pc, 918193074Sthompsa struct usb_page *pg, usb_size_t size, usb_size_t align) 919184610Salfred{ 920194228Sthompsa usb_pc_free_mem(pc); 921184610Salfred} 922190180Sthompsa#endif 923184610Salfred 924184610Salfred/*------------------------------------------------------------------------* 925194228Sthompsa * usb_bus_mem_free_all - factored out code 926184610Salfred *------------------------------------------------------------------------*/ 927184610Salfredvoid 928194228Sthompsausb_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb) 929184610Salfred{ 930190180Sthompsa#if USB_HAVE_BUSDMA 931184610Salfred if (cb) { 932194228Sthompsa cb(bus, &usb_bus_mem_free_all_cb); 933184610Salfred } 934194228Sthompsa usb_dma_tag_unsetup(bus->dma_parent_tag); 935190180Sthompsa#endif 936184610Salfred 937184824Sthompsa mtx_destroy(&bus->bus_mtx); 938184610Salfred} 939250560Shselasky 940250560Shselasky/* convenience wrappers */ 941250560Shselaskyvoid 942250560Shselaskyusb_proc_explore_mwait(struct usb_device *udev, void *pm1, void *pm2) 943250560Shselasky{ 944250560Shselasky usb_proc_mwait(&udev->bus->explore_proc, pm1, pm2); 945250560Shselasky} 946250560Shselasky 947250560Shselaskyvoid * 948250560Shselaskyusb_proc_explore_msignal(struct usb_device *udev, void *pm1, void *pm2) 949250560Shselasky{ 950250560Shselasky return (usb_proc_msignal(&udev->bus->explore_proc, pm1, pm2)); 951250560Shselasky} 952250560Shselasky 953250560Shselaskyvoid 954250560Shselaskyusb_proc_explore_lock(struct usb_device *udev) 955250560Shselasky{ 956250560Shselasky USB_BUS_LOCK(udev->bus); 957250560Shselasky} 958250560Shselasky 959250560Shselaskyvoid 960250560Shselaskyusb_proc_explore_unlock(struct usb_device *udev) 961250560Shselasky{ 962250560Shselasky USB_BUS_UNLOCK(udev->bus); 963250560Shselasky} 964