xref: /freebsd/sys/dev/usb/controller/usb_controller.c (revision 1be5bf51d317e6ce935d9182e4e99635def8f989)
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