xref: /freebsd/sys/dev/usb/controller/usb_controller.c (revision d88688c7da13abec2a923bb954c71fcdecf6d94d)
102ac6454SAndrew Thompson /* $FreeBSD$ */
202ac6454SAndrew Thompson /*-
302ac6454SAndrew Thompson  * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
402ac6454SAndrew Thompson  *
502ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
602ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
702ac6454SAndrew Thompson  * are met:
802ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
902ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
1002ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
1102ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
1202ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
1302ac6454SAndrew Thompson  *
1402ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1502ac6454SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1602ac6454SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1702ac6454SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1802ac6454SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1902ac6454SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2002ac6454SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2102ac6454SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2202ac6454SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2302ac6454SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2402ac6454SAndrew Thompson  * SUCH DAMAGE.
2502ac6454SAndrew Thompson  */
2602ac6454SAndrew Thompson 
27ed6d949aSAndrew Thompson #include <sys/stdint.h>
28ed6d949aSAndrew Thompson #include <sys/stddef.h>
29ed6d949aSAndrew Thompson #include <sys/param.h>
30ed6d949aSAndrew Thompson #include <sys/queue.h>
31ed6d949aSAndrew Thompson #include <sys/types.h>
32ed6d949aSAndrew Thompson #include <sys/systm.h>
33ed6d949aSAndrew Thompson #include <sys/kernel.h>
34ed6d949aSAndrew Thompson #include <sys/bus.h>
35ed6d949aSAndrew Thompson #include <sys/linker_set.h>
36ed6d949aSAndrew Thompson #include <sys/module.h>
37ed6d949aSAndrew Thompson #include <sys/lock.h>
38ed6d949aSAndrew Thompson #include <sys/mutex.h>
39ed6d949aSAndrew Thompson #include <sys/condvar.h>
40ed6d949aSAndrew Thompson #include <sys/sysctl.h>
41ed6d949aSAndrew Thompson #include <sys/sx.h>
42ed6d949aSAndrew Thompson #include <sys/unistd.h>
43ed6d949aSAndrew Thompson #include <sys/callout.h>
44ed6d949aSAndrew Thompson #include <sys/malloc.h>
45ed6d949aSAndrew Thompson #include <sys/priv.h>
46ed6d949aSAndrew Thompson 
4702ac6454SAndrew Thompson #include <dev/usb/usb.h>
48ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
4902ac6454SAndrew Thompson 
50a593f6b8SAndrew Thompson #define	USB_DEBUG_VAR usb_ctrl_debug
5102ac6454SAndrew Thompson 
5202ac6454SAndrew Thompson #include <dev/usb/usb_core.h>
5302ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
5402ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
5502ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h>
5602ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h>
5702ac6454SAndrew Thompson #include <dev/usb/usb_device.h>
5802ac6454SAndrew Thompson #include <dev/usb/usb_hub.h>
5902ac6454SAndrew Thompson 
6002ac6454SAndrew Thompson #include <dev/usb/usb_controller.h>
6102ac6454SAndrew Thompson #include <dev/usb/usb_bus.h>
6202ac6454SAndrew Thompson 
6302ac6454SAndrew Thompson /* function prototypes  */
6402ac6454SAndrew Thompson 
65a593f6b8SAndrew Thompson static device_probe_t usb_probe;
66a593f6b8SAndrew Thompson static device_attach_t usb_attach;
67a593f6b8SAndrew Thompson static device_detach_t usb_detach;
6802ac6454SAndrew Thompson 
69a593f6b8SAndrew Thompson static void	usb_attach_sub(device_t, struct usb_bus *);
7002ac6454SAndrew Thompson 
7102ac6454SAndrew Thompson /* static variables */
7202ac6454SAndrew Thompson 
73ed6d949aSAndrew Thompson #ifdef USB_DEBUG
74a593f6b8SAndrew Thompson static int usb_ctrl_debug = 0;
7502ac6454SAndrew Thompson 
769360ae40SAndrew Thompson SYSCTL_NODE(_hw_usb, OID_AUTO, ctrl, CTLFLAG_RW, 0, "USB controller");
77a593f6b8SAndrew Thompson SYSCTL_INT(_hw_usb_ctrl, OID_AUTO, debug, CTLFLAG_RW, &usb_ctrl_debug, 0,
7802ac6454SAndrew Thompson     "Debug level");
7902ac6454SAndrew Thompson #endif
8002ac6454SAndrew Thompson 
81a0c61406SAlfred Perlstein static int usb_no_boot_wait = 0;
82a0c61406SAlfred Perlstein TUNABLE_INT("hw.usb.no_boot_wait", &usb_no_boot_wait);
83a0c61406SAlfred Perlstein SYSCTL_INT(_hw_usb, OID_AUTO, no_boot_wait, CTLFLAG_RDTUN, &usb_no_boot_wait, 0,
84a0c61406SAlfred Perlstein     "No device enumerate waiting at boot.");
85a0c61406SAlfred Perlstein 
86a593f6b8SAndrew Thompson static devclass_t usb_devclass;
8702ac6454SAndrew Thompson 
88a593f6b8SAndrew Thompson static device_method_t usb_methods[] = {
89a593f6b8SAndrew Thompson 	DEVMETHOD(device_probe, usb_probe),
90a593f6b8SAndrew Thompson 	DEVMETHOD(device_attach, usb_attach),
91a593f6b8SAndrew Thompson 	DEVMETHOD(device_detach, usb_detach),
9202ac6454SAndrew Thompson 	DEVMETHOD(device_suspend, bus_generic_suspend),
9302ac6454SAndrew Thompson 	DEVMETHOD(device_resume, bus_generic_resume),
9402ac6454SAndrew Thompson 	DEVMETHOD(device_shutdown, bus_generic_shutdown),
9502ac6454SAndrew Thompson 	{0, 0}
9602ac6454SAndrew Thompson };
9702ac6454SAndrew Thompson 
98a593f6b8SAndrew Thompson static driver_t usb_driver = {
9902ac6454SAndrew Thompson 	.name = "usbus",
100a593f6b8SAndrew Thompson 	.methods = usb_methods,
10102ac6454SAndrew Thompson 	.size = 0,
10202ac6454SAndrew Thompson };
10302ac6454SAndrew Thompson 
104a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, ohci, usb_driver, usb_devclass, 0, 0);
105a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, uhci, usb_driver, usb_devclass, 0, 0);
106a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, ehci, usb_driver, usb_devclass, 0, 0);
107a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, at91_udp, usb_driver, usb_devclass, 0, 0);
108a593f6b8SAndrew Thompson DRIVER_MODULE(usbus, uss820, usb_driver, usb_devclass, 0, 0);
10902ac6454SAndrew Thompson 
11002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
111a593f6b8SAndrew Thompson  *	usb_probe
11202ac6454SAndrew Thompson  *
11302ac6454SAndrew Thompson  * This function is called from "{ehci,ohci,uhci}_pci_attach()".
11402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
11502ac6454SAndrew Thompson static int
116a593f6b8SAndrew Thompson usb_probe(device_t dev)
11702ac6454SAndrew Thompson {
11802ac6454SAndrew Thompson 	DPRINTF("\n");
11902ac6454SAndrew Thompson 	return (0);
12002ac6454SAndrew Thompson }
12102ac6454SAndrew Thompson 
12202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
123a593f6b8SAndrew Thompson  *	usb_attach
12402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
12502ac6454SAndrew Thompson static int
126a593f6b8SAndrew Thompson usb_attach(device_t dev)
12702ac6454SAndrew Thompson {
128760bc48eSAndrew Thompson 	struct usb_bus *bus = device_get_ivars(dev);
12902ac6454SAndrew Thompson 
13002ac6454SAndrew Thompson 	DPRINTF("\n");
13102ac6454SAndrew Thompson 
13202ac6454SAndrew Thompson 	if (bus == NULL) {
13302ac6454SAndrew Thompson 		DPRINTFN(0, "USB device has no ivars\n");
13402ac6454SAndrew Thompson 		return (ENXIO);
13502ac6454SAndrew Thompson 	}
13602ac6454SAndrew Thompson 
137a0c61406SAlfred Perlstein 	if (usb_no_boot_wait == 0) {
13802ac6454SAndrew Thompson 		/* delay vfs_mountroot until the bus is explored */
139853a10a5SAndrew Thompson 		bus->bus_roothold = root_mount_hold(device_get_nameunit(dev));
140a0c61406SAlfred Perlstein 	}
14102ac6454SAndrew Thompson 
142a593f6b8SAndrew Thompson 	usb_attach_sub(dev, bus);
14334b48722SAlfred Perlstein 
14402ac6454SAndrew Thompson 	return (0);			/* return success */
14502ac6454SAndrew Thompson }
14602ac6454SAndrew Thompson 
14702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
148a593f6b8SAndrew Thompson  *	usb_detach
14902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
15002ac6454SAndrew Thompson static int
151a593f6b8SAndrew Thompson usb_detach(device_t dev)
15202ac6454SAndrew Thompson {
153760bc48eSAndrew Thompson 	struct usb_bus *bus = device_get_softc(dev);
15402ac6454SAndrew Thompson 
15502ac6454SAndrew Thompson 	DPRINTF("\n");
15602ac6454SAndrew Thompson 
15702ac6454SAndrew Thompson 	if (bus == NULL) {
15802ac6454SAndrew Thompson 		/* was never setup properly */
15902ac6454SAndrew Thompson 		return (0);
16002ac6454SAndrew Thompson 	}
16102ac6454SAndrew Thompson 	/* Stop power watchdog */
162a593f6b8SAndrew Thompson 	usb_callout_drain(&bus->power_wdog);
16302ac6454SAndrew Thompson 
16402ac6454SAndrew Thompson 	/* Let the USB explore process detach all devices. */
16502ac6454SAndrew Thompson 	if (bus->bus_roothold != NULL) {
16602ac6454SAndrew Thompson 		root_mount_rel(bus->bus_roothold);
16702ac6454SAndrew Thompson 		bus->bus_roothold = NULL;
16802ac6454SAndrew Thompson 	}
16902ac6454SAndrew Thompson 
17002ac6454SAndrew Thompson 	USB_BUS_LOCK(bus);
171a593f6b8SAndrew Thompson 	if (usb_proc_msignal(&bus->explore_proc,
17202ac6454SAndrew Thompson 	    &bus->detach_msg[0], &bus->detach_msg[1])) {
17302ac6454SAndrew Thompson 		/* ignore */
17402ac6454SAndrew Thompson 	}
17502ac6454SAndrew Thompson 	/* Wait for detach to complete */
17602ac6454SAndrew Thompson 
177a593f6b8SAndrew Thompson 	usb_proc_mwait(&bus->explore_proc,
17802ac6454SAndrew Thompson 	    &bus->detach_msg[0], &bus->detach_msg[1]);
17902ac6454SAndrew Thompson 
18002ac6454SAndrew Thompson 	USB_BUS_UNLOCK(bus);
18102ac6454SAndrew Thompson 
18202ac6454SAndrew Thompson 	/* Get rid of USB callback processes */
18302ac6454SAndrew Thompson 
184a593f6b8SAndrew Thompson 	usb_proc_free(&bus->giant_callback_proc);
185a593f6b8SAndrew Thompson 	usb_proc_free(&bus->non_giant_callback_proc);
18602ac6454SAndrew Thompson 
18702ac6454SAndrew Thompson 	/* Get rid of USB explore process */
18802ac6454SAndrew Thompson 
189a593f6b8SAndrew Thompson 	usb_proc_free(&bus->explore_proc);
19002ac6454SAndrew Thompson 
191672c9965SAndrew Thompson 	/* Get rid of control transfer process */
192672c9965SAndrew Thompson 
193a593f6b8SAndrew Thompson 	usb_proc_free(&bus->control_xfer_proc);
194672c9965SAndrew Thompson 
19502ac6454SAndrew Thompson 	return (0);
19602ac6454SAndrew Thompson }
19702ac6454SAndrew Thompson 
19802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
199a593f6b8SAndrew Thompson  *	usb_bus_explore
20002ac6454SAndrew Thompson  *
20102ac6454SAndrew Thompson  * This function is used to explore the device tree from the root.
20202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
20302ac6454SAndrew Thompson static void
204a593f6b8SAndrew Thompson usb_bus_explore(struct usb_proc_msg *pm)
20502ac6454SAndrew Thompson {
206760bc48eSAndrew Thompson 	struct usb_bus *bus;
207760bc48eSAndrew Thompson 	struct usb_device *udev;
20802ac6454SAndrew Thompson 
209760bc48eSAndrew Thompson 	bus = ((struct usb_bus_msg *)pm)->bus;
21002ac6454SAndrew Thompson 	udev = bus->devices[USB_ROOT_HUB_ADDR];
21102ac6454SAndrew Thompson 
21202ac6454SAndrew Thompson 	if (udev && udev->hub) {
21302ac6454SAndrew Thompson 
21402ac6454SAndrew Thompson 		if (bus->do_probe) {
21502ac6454SAndrew Thompson 			bus->do_probe = 0;
21602ac6454SAndrew Thompson 			bus->driver_added_refcount++;
21702ac6454SAndrew Thompson 		}
21802ac6454SAndrew Thompson 		if (bus->driver_added_refcount == 0) {
21902ac6454SAndrew Thompson 			/* avoid zero, hence that is memory default */
22002ac6454SAndrew Thompson 			bus->driver_added_refcount = 1;
22102ac6454SAndrew Thompson 		}
22202ac6454SAndrew Thompson 
22334b48722SAlfred Perlstein 		/*
22434b48722SAlfred Perlstein 		 * The following three lines of code are only here to
22534b48722SAlfred Perlstein 		 * recover from DDB:
22634b48722SAlfred Perlstein 		 */
22734b48722SAlfred Perlstein 		usb_proc_rewakeup(&bus->control_xfer_proc);
22834b48722SAlfred Perlstein 		usb_proc_rewakeup(&bus->giant_callback_proc);
22934b48722SAlfred Perlstein 		usb_proc_rewakeup(&bus->non_giant_callback_proc);
23034b48722SAlfred Perlstein 
23134b48722SAlfred Perlstein 		USB_BUS_UNLOCK(bus);
232a56fe095SJohn Baldwin 
23302ac6454SAndrew Thompson 		/*
23402ac6454SAndrew Thompson 		 * First update the USB power state!
23502ac6454SAndrew Thompson 		 */
236a593f6b8SAndrew Thompson 		usb_bus_powerd(bus);
23734b48722SAlfred Perlstein 
23834b48722SAlfred Perlstein 		 /* Explore the Root USB HUB. */
23902ac6454SAndrew Thompson 		(udev->hub->explore) (udev);
24002ac6454SAndrew Thompson 		USB_BUS_LOCK(bus);
24102ac6454SAndrew Thompson 	}
24202ac6454SAndrew Thompson 	if (bus->bus_roothold != NULL) {
24302ac6454SAndrew Thompson 		root_mount_rel(bus->bus_roothold);
24402ac6454SAndrew Thompson 		bus->bus_roothold = NULL;
24502ac6454SAndrew Thompson 	}
24602ac6454SAndrew Thompson }
24702ac6454SAndrew Thompson 
24802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
249a593f6b8SAndrew Thompson  *	usb_bus_detach
25002ac6454SAndrew Thompson  *
25102ac6454SAndrew Thompson  * This function is used to detach the device tree from the root.
25202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
25302ac6454SAndrew Thompson static void
254a593f6b8SAndrew Thompson usb_bus_detach(struct usb_proc_msg *pm)
25502ac6454SAndrew Thompson {
256760bc48eSAndrew Thompson 	struct usb_bus *bus;
257760bc48eSAndrew Thompson 	struct usb_device *udev;
25802ac6454SAndrew Thompson 	device_t dev;
25902ac6454SAndrew Thompson 
260760bc48eSAndrew Thompson 	bus = ((struct usb_bus_msg *)pm)->bus;
26102ac6454SAndrew Thompson 	udev = bus->devices[USB_ROOT_HUB_ADDR];
26202ac6454SAndrew Thompson 	dev = bus->bdev;
26302ac6454SAndrew Thompson 	/* clear the softc */
26402ac6454SAndrew Thompson 	device_set_softc(dev, NULL);
26502ac6454SAndrew Thompson 	USB_BUS_UNLOCK(bus);
26602ac6454SAndrew Thompson 
26702ac6454SAndrew Thompson 	/* detach children first */
26834b48722SAlfred Perlstein 	mtx_lock(&Giant);
26902ac6454SAndrew Thompson 	bus_generic_detach(dev);
27034b48722SAlfred Perlstein 	mtx_unlock(&Giant);
27102ac6454SAndrew Thompson 
27202ac6454SAndrew Thompson 	/*
273d88688c7SAndrew Thompson 	 * Free USB device and all subdevices, if any.
27402ac6454SAndrew Thompson 	 */
275d88688c7SAndrew Thompson 	usb_free_device(udev, 0);
27602ac6454SAndrew Thompson 
27702ac6454SAndrew Thompson 	USB_BUS_LOCK(bus);
27802ac6454SAndrew Thompson 	/* clear bdev variable last */
27902ac6454SAndrew Thompson 	bus->bdev = NULL;
28002ac6454SAndrew Thompson }
28102ac6454SAndrew Thompson 
28202ac6454SAndrew Thompson static void
283a593f6b8SAndrew Thompson usb_power_wdog(void *arg)
28402ac6454SAndrew Thompson {
285760bc48eSAndrew Thompson 	struct usb_bus *bus = arg;
28602ac6454SAndrew Thompson 
28702ac6454SAndrew Thompson 	USB_BUS_LOCK_ASSERT(bus, MA_OWNED);
28802ac6454SAndrew Thompson 
289a593f6b8SAndrew Thompson 	usb_callout_reset(&bus->power_wdog,
290a593f6b8SAndrew Thompson 	    4 * hz, usb_power_wdog, arg);
29102ac6454SAndrew Thompson 
29234b48722SAlfred Perlstein 	/*
29334b48722SAlfred Perlstein 	 * The following line of code is only here to recover from
29434b48722SAlfred Perlstein 	 * DDB:
29534b48722SAlfred Perlstein 	 */
29634b48722SAlfred Perlstein 	usb_proc_rewakeup(&bus->explore_proc);	/* recover from DDB */
29734b48722SAlfred Perlstein 
29802ac6454SAndrew Thompson 	USB_BUS_UNLOCK(bus);
29902ac6454SAndrew Thompson 
300a593f6b8SAndrew Thompson 	usb_bus_power_update(bus);
30102ac6454SAndrew Thompson 
302684e3f22SAndrew Thompson 	USB_BUS_LOCK(bus);
30302ac6454SAndrew Thompson }
30402ac6454SAndrew Thompson 
30502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
306a593f6b8SAndrew Thompson  *	usb_bus_attach
30702ac6454SAndrew Thompson  *
30802ac6454SAndrew Thompson  * This function attaches USB in context of the explore thread.
30902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
31002ac6454SAndrew Thompson static void
311a593f6b8SAndrew Thompson usb_bus_attach(struct usb_proc_msg *pm)
31202ac6454SAndrew Thompson {
313760bc48eSAndrew Thompson 	struct usb_bus *bus;
314760bc48eSAndrew Thompson 	struct usb_device *child;
31502ac6454SAndrew Thompson 	device_t dev;
316e0a69b51SAndrew Thompson 	usb_error_t err;
3178d2dd5ddSAndrew Thompson 	enum usb_dev_speed speed;
31802ac6454SAndrew Thompson 
319760bc48eSAndrew Thompson 	bus = ((struct usb_bus_msg *)pm)->bus;
32002ac6454SAndrew Thompson 	dev = bus->bdev;
32102ac6454SAndrew Thompson 
32202ac6454SAndrew Thompson 	DPRINTF("\n");
32302ac6454SAndrew Thompson 
32402ac6454SAndrew Thompson 	switch (bus->usbrev) {
32502ac6454SAndrew Thompson 	case USB_REV_1_0:
32602ac6454SAndrew Thompson 		speed = USB_SPEED_FULL;
32702ac6454SAndrew Thompson 		device_printf(bus->bdev, "12Mbps Full Speed USB v1.0\n");
32802ac6454SAndrew Thompson 		break;
32902ac6454SAndrew Thompson 
33002ac6454SAndrew Thompson 	case USB_REV_1_1:
33102ac6454SAndrew Thompson 		speed = USB_SPEED_FULL;
33202ac6454SAndrew Thompson 		device_printf(bus->bdev, "12Mbps Full Speed USB v1.1\n");
33302ac6454SAndrew Thompson 		break;
33402ac6454SAndrew Thompson 
33502ac6454SAndrew Thompson 	case USB_REV_2_0:
33602ac6454SAndrew Thompson 		speed = USB_SPEED_HIGH;
33702ac6454SAndrew Thompson 		device_printf(bus->bdev, "480Mbps High Speed USB v2.0\n");
33802ac6454SAndrew Thompson 		break;
33902ac6454SAndrew Thompson 
34002ac6454SAndrew Thompson 	case USB_REV_2_5:
34102ac6454SAndrew Thompson 		speed = USB_SPEED_VARIABLE;
34202ac6454SAndrew Thompson 		device_printf(bus->bdev, "480Mbps Wireless USB v2.5\n");
34302ac6454SAndrew Thompson 		break;
34402ac6454SAndrew Thompson 
34502ac6454SAndrew Thompson 	default:
34602ac6454SAndrew Thompson 		device_printf(bus->bdev, "Unsupported USB revision!\n");
34702ac6454SAndrew Thompson 		return;
34802ac6454SAndrew Thompson 	}
34902ac6454SAndrew Thompson 
35002ac6454SAndrew Thompson 	USB_BUS_UNLOCK(bus);
35102ac6454SAndrew Thompson 
3524eae601eSAndrew Thompson 	/* default power_mask value */
3534eae601eSAndrew Thompson 	bus->hw_power_state =
3544eae601eSAndrew Thompson 	  USB_HW_POWER_CONTROL |
3554eae601eSAndrew Thompson 	  USB_HW_POWER_BULK |
3564eae601eSAndrew Thompson 	  USB_HW_POWER_INTERRUPT |
3574eae601eSAndrew Thompson 	  USB_HW_POWER_ISOC |
3584eae601eSAndrew Thompson 	  USB_HW_POWER_NON_ROOT_HUB;
3594eae601eSAndrew Thompson 
3604eae601eSAndrew Thompson 	/* make sure power is set at least once */
3614eae601eSAndrew Thompson 
3624eae601eSAndrew Thompson 	if (bus->methods->set_hw_power != NULL) {
3634eae601eSAndrew Thompson 		(bus->methods->set_hw_power) (bus);
3644eae601eSAndrew Thompson 	}
3654eae601eSAndrew Thompson 
36602ac6454SAndrew Thompson 	/* Allocate the Root USB device */
36702ac6454SAndrew Thompson 
368a593f6b8SAndrew Thompson 	child = usb_alloc_device(bus->bdev, bus, NULL, 0, 0, 1,
36902ac6454SAndrew Thompson 	    speed, USB_MODE_HOST);
37002ac6454SAndrew Thompson 	if (child) {
371a593f6b8SAndrew Thompson 		err = usb_probe_and_attach(child,
37202ac6454SAndrew Thompson 		    USB_IFACE_INDEX_ANY);
37302ac6454SAndrew Thompson 		if (!err) {
3741be5bf51SAndrew Thompson 			if ((bus->devices[USB_ROOT_HUB_ADDR] == NULL) ||
3751be5bf51SAndrew Thompson 			    (bus->devices[USB_ROOT_HUB_ADDR]->hub == NULL)) {
37602ac6454SAndrew Thompson 				err = USB_ERR_NO_ROOT_HUB;
37702ac6454SAndrew Thompson 			}
37802ac6454SAndrew Thompson 		}
37902ac6454SAndrew Thompson 	} else {
38002ac6454SAndrew Thompson 		err = USB_ERR_NOMEM;
38102ac6454SAndrew Thompson 	}
38202ac6454SAndrew Thompson 
38302ac6454SAndrew Thompson 	USB_BUS_LOCK(bus);
38402ac6454SAndrew Thompson 
38502ac6454SAndrew Thompson 	if (err) {
38602ac6454SAndrew Thompson 		device_printf(bus->bdev, "Root HUB problem, error=%s\n",
387a593f6b8SAndrew Thompson 		    usbd_errstr(err));
38802ac6454SAndrew Thompson 	}
38902ac6454SAndrew Thompson 
39002ac6454SAndrew Thompson 	/* set softc - we are ready */
39102ac6454SAndrew Thompson 	device_set_softc(dev, bus);
39202ac6454SAndrew Thompson 
393684e3f22SAndrew Thompson 	/* start watchdog */
394a593f6b8SAndrew Thompson 	usb_power_wdog(bus);
39502ac6454SAndrew Thompson }
39602ac6454SAndrew Thompson 
39702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
398a593f6b8SAndrew Thompson  *	usb_attach_sub
39902ac6454SAndrew Thompson  *
40034b48722SAlfred Perlstein  * This function creates a thread which runs the USB attach code.
40102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
40202ac6454SAndrew Thompson static void
403a593f6b8SAndrew Thompson usb_attach_sub(device_t dev, struct usb_bus *bus)
40402ac6454SAndrew Thompson {
40502ac6454SAndrew Thompson 	const char *pname = device_get_nameunit(dev);
40602ac6454SAndrew Thompson 
40734b48722SAlfred Perlstein 	mtx_lock(&Giant);
40834b48722SAlfred Perlstein 	if (usb_devclass_ptr == NULL)
40934b48722SAlfred Perlstein 		usb_devclass_ptr = devclass_find("usbus");
41034b48722SAlfred Perlstein 	mtx_unlock(&Giant);
41134b48722SAlfred Perlstein 
41202ac6454SAndrew Thompson 	/* Initialise USB process messages */
413a593f6b8SAndrew Thompson 	bus->explore_msg[0].hdr.pm_callback = &usb_bus_explore;
41402ac6454SAndrew Thompson 	bus->explore_msg[0].bus = bus;
415a593f6b8SAndrew Thompson 	bus->explore_msg[1].hdr.pm_callback = &usb_bus_explore;
41602ac6454SAndrew Thompson 	bus->explore_msg[1].bus = bus;
41702ac6454SAndrew Thompson 
418a593f6b8SAndrew Thompson 	bus->detach_msg[0].hdr.pm_callback = &usb_bus_detach;
41902ac6454SAndrew Thompson 	bus->detach_msg[0].bus = bus;
420a593f6b8SAndrew Thompson 	bus->detach_msg[1].hdr.pm_callback = &usb_bus_detach;
42102ac6454SAndrew Thompson 	bus->detach_msg[1].bus = bus;
42202ac6454SAndrew Thompson 
423a593f6b8SAndrew Thompson 	bus->attach_msg[0].hdr.pm_callback = &usb_bus_attach;
42402ac6454SAndrew Thompson 	bus->attach_msg[0].bus = bus;
425a593f6b8SAndrew Thompson 	bus->attach_msg[1].hdr.pm_callback = &usb_bus_attach;
42602ac6454SAndrew Thompson 	bus->attach_msg[1].bus = bus;
42702ac6454SAndrew Thompson 
42839307315SAndrew Thompson 	/* Create USB explore and callback processes */
42902ac6454SAndrew Thompson 
430a593f6b8SAndrew Thompson 	if (usb_proc_create(&bus->giant_callback_proc,
43102ac6454SAndrew Thompson 	    &bus->bus_mtx, pname, USB_PRI_MED)) {
43202ac6454SAndrew Thompson 		printf("WARNING: Creation of USB Giant "
43302ac6454SAndrew Thompson 		    "callback process failed.\n");
434a593f6b8SAndrew Thompson 	} else if (usb_proc_create(&bus->non_giant_callback_proc,
43502ac6454SAndrew Thompson 	    &bus->bus_mtx, pname, USB_PRI_HIGH)) {
43602ac6454SAndrew Thompson 		printf("WARNING: Creation of USB non-Giant "
43702ac6454SAndrew Thompson 		    "callback process failed.\n");
438a593f6b8SAndrew Thompson 	} else if (usb_proc_create(&bus->explore_proc,
43902ac6454SAndrew Thompson 	    &bus->bus_mtx, pname, USB_PRI_MED)) {
44002ac6454SAndrew Thompson 		printf("WARNING: Creation of USB explore "
44102ac6454SAndrew Thompson 		    "process failed.\n");
442a593f6b8SAndrew Thompson 	} else if (usb_proc_create(&bus->control_xfer_proc,
443672c9965SAndrew Thompson 	    &bus->bus_mtx, pname, USB_PRI_MED)) {
444672c9965SAndrew Thompson 		printf("WARNING: Creation of USB control transfer "
445672c9965SAndrew Thompson 		    "process failed.\n");
44602ac6454SAndrew Thompson 	} else {
44702ac6454SAndrew Thompson 		/* Get final attach going */
44802ac6454SAndrew Thompson 		USB_BUS_LOCK(bus);
449a593f6b8SAndrew Thompson 		if (usb_proc_msignal(&bus->explore_proc,
45002ac6454SAndrew Thompson 		    &bus->attach_msg[0], &bus->attach_msg[1])) {
45102ac6454SAndrew Thompson 			/* ignore */
45202ac6454SAndrew Thompson 		}
45302ac6454SAndrew Thompson 		USB_BUS_UNLOCK(bus);
45434b48722SAlfred Perlstein 
45534b48722SAlfred Perlstein 		/* Do initial explore */
45634b48722SAlfred Perlstein 		usb_needs_explore(bus, 1);
45702ac6454SAndrew Thompson 	}
45802ac6454SAndrew Thompson }
45902ac6454SAndrew Thompson 
460a593f6b8SAndrew Thompson SYSUNINIT(usb_bus_unload, SI_SUB_KLD, SI_ORDER_ANY, usb_bus_unload, NULL);
46102ac6454SAndrew Thompson 
46202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
463a593f6b8SAndrew Thompson  *	usb_bus_mem_flush_all_cb
46402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
465bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA
46602ac6454SAndrew Thompson static void
467a593f6b8SAndrew Thompson usb_bus_mem_flush_all_cb(struct usb_bus *bus, struct usb_page_cache *pc,
468f9cb546cSAndrew Thompson     struct usb_page *pg, usb_size_t size, usb_size_t align)
46902ac6454SAndrew Thompson {
470a593f6b8SAndrew Thompson 	usb_pc_cpu_flush(pc);
47102ac6454SAndrew Thompson }
472bdc081c6SAndrew Thompson #endif
47302ac6454SAndrew Thompson 
47402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
475a593f6b8SAndrew Thompson  *	usb_bus_mem_flush_all - factored out code
47602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
477bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA
47802ac6454SAndrew Thompson void
479a593f6b8SAndrew Thompson usb_bus_mem_flush_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb)
48002ac6454SAndrew Thompson {
48102ac6454SAndrew Thompson 	if (cb) {
482a593f6b8SAndrew Thompson 		cb(bus, &usb_bus_mem_flush_all_cb);
48302ac6454SAndrew Thompson 	}
48402ac6454SAndrew Thompson }
485bdc081c6SAndrew Thompson #endif
48602ac6454SAndrew Thompson 
48702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
488a593f6b8SAndrew Thompson  *	usb_bus_mem_alloc_all_cb
48902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
490bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA
49102ac6454SAndrew Thompson static void
492a593f6b8SAndrew Thompson usb_bus_mem_alloc_all_cb(struct usb_bus *bus, struct usb_page_cache *pc,
493f9cb546cSAndrew Thompson     struct usb_page *pg, usb_size_t size, usb_size_t align)
49402ac6454SAndrew Thompson {
49502ac6454SAndrew Thompson 	/* need to initialize the page cache */
49602ac6454SAndrew Thompson 	pc->tag_parent = bus->dma_parent_tag;
49702ac6454SAndrew Thompson 
498a593f6b8SAndrew Thompson 	if (usb_pc_alloc_mem(pc, pg, size, align)) {
49902ac6454SAndrew Thompson 		bus->alloc_failed = 1;
50002ac6454SAndrew Thompson 	}
50102ac6454SAndrew Thompson }
502bdc081c6SAndrew Thompson #endif
50302ac6454SAndrew Thompson 
50402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
505a593f6b8SAndrew Thompson  *	usb_bus_mem_alloc_all - factored out code
50602ac6454SAndrew Thompson  *
50702ac6454SAndrew Thompson  * Returns:
50802ac6454SAndrew Thompson  *    0: Success
50902ac6454SAndrew Thompson  * Else: Failure
51002ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
51102ac6454SAndrew Thompson uint8_t
512a593f6b8SAndrew Thompson usb_bus_mem_alloc_all(struct usb_bus *bus, bus_dma_tag_t dmat,
513e0a69b51SAndrew Thompson     usb_bus_mem_cb_t *cb)
51402ac6454SAndrew Thompson {
51502ac6454SAndrew Thompson 	bus->alloc_failed = 0;
51602ac6454SAndrew Thompson 
51702ac6454SAndrew Thompson 	mtx_init(&bus->bus_mtx, device_get_nameunit(bus->parent),
51802ac6454SAndrew Thompson 	    NULL, MTX_DEF | MTX_RECURSE);
51902ac6454SAndrew Thompson 
520a593f6b8SAndrew Thompson 	usb_callout_init_mtx(&bus->power_wdog,
521684e3f22SAndrew Thompson 	    &bus->bus_mtx, 0);
52202ac6454SAndrew Thompson 
52302ac6454SAndrew Thompson 	TAILQ_INIT(&bus->intr_q.head);
52402ac6454SAndrew Thompson 
525bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA
526a593f6b8SAndrew Thompson 	usb_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags,
527bdc081c6SAndrew Thompson 	    dmat, &bus->bus_mtx, NULL, 32, USB_BUS_DMA_TAG_MAX);
528bdc081c6SAndrew Thompson #endif
52902ac6454SAndrew Thompson 	if ((bus->devices_max > USB_MAX_DEVICES) ||
53002ac6454SAndrew Thompson 	    (bus->devices_max < USB_MIN_DEVICES) ||
53102ac6454SAndrew Thompson 	    (bus->devices == NULL)) {
53202ac6454SAndrew Thompson 		DPRINTFN(0, "Devices field has not been "
53302ac6454SAndrew Thompson 		    "initialised properly!\n");
53402ac6454SAndrew Thompson 		bus->alloc_failed = 1;		/* failure */
53502ac6454SAndrew Thompson 	}
536bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA
53702ac6454SAndrew Thompson 	if (cb) {
538a593f6b8SAndrew Thompson 		cb(bus, &usb_bus_mem_alloc_all_cb);
53902ac6454SAndrew Thompson 	}
540bdc081c6SAndrew Thompson #endif
54102ac6454SAndrew Thompson 	if (bus->alloc_failed) {
542a593f6b8SAndrew Thompson 		usb_bus_mem_free_all(bus, cb);
54302ac6454SAndrew Thompson 	}
54402ac6454SAndrew Thompson 	return (bus->alloc_failed);
54502ac6454SAndrew Thompson }
54602ac6454SAndrew Thompson 
54702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
548a593f6b8SAndrew Thompson  *	usb_bus_mem_free_all_cb
54902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
550bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA
55102ac6454SAndrew Thompson static void
552a593f6b8SAndrew Thompson usb_bus_mem_free_all_cb(struct usb_bus *bus, struct usb_page_cache *pc,
553f9cb546cSAndrew Thompson     struct usb_page *pg, usb_size_t size, usb_size_t align)
55402ac6454SAndrew Thompson {
555a593f6b8SAndrew Thompson 	usb_pc_free_mem(pc);
55602ac6454SAndrew Thompson }
557bdc081c6SAndrew Thompson #endif
55802ac6454SAndrew Thompson 
55902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
560a593f6b8SAndrew Thompson  *	usb_bus_mem_free_all - factored out code
56102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
56202ac6454SAndrew Thompson void
563a593f6b8SAndrew Thompson usb_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb)
56402ac6454SAndrew Thompson {
565bdc081c6SAndrew Thompson #if USB_HAVE_BUSDMA
56602ac6454SAndrew Thompson 	if (cb) {
567a593f6b8SAndrew Thompson 		cb(bus, &usb_bus_mem_free_all_cb);
56802ac6454SAndrew Thompson 	}
569a593f6b8SAndrew Thompson 	usb_dma_tag_unsetup(bus->dma_parent_tag);
570bdc081c6SAndrew Thompson #endif
57102ac6454SAndrew Thompson 
57202ac6454SAndrew Thompson 	mtx_destroy(&bus->bus_mtx);
57302ac6454SAndrew Thompson }
574