1184610Salfred/* $FreeBSD$ */
2184610Salfred/*-
3184610Salfred * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved.
4184610Salfred * Copyright (c) 1998 Lennart Augustsson. All rights reserved.
5213435Shselasky * Copyright (c) 2008-2010 Hans Petter Selasky. All rights reserved.
6184610Salfred *
7184610Salfred * Redistribution and use in source and binary forms, with or without
8184610Salfred * modification, are permitted provided that the following conditions
9184610Salfred * are met:
10184610Salfred * 1. Redistributions of source code must retain the above copyright
11184610Salfred *    notice, this list of conditions and the following disclaimer.
12184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
13184610Salfred *    notice, this list of conditions and the following disclaimer in the
14184610Salfred *    documentation and/or other materials provided with the distribution.
15184610Salfred *
16184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26184610Salfred * SUCH DAMAGE.
27184610Salfred */
28184610Salfred
29184610Salfred/*
30190754Sthompsa * USB spec: http://www.usb.org/developers/docs/usbspec.zip
31184610Salfred */
32184610Salfred
33194677Sthompsa#include <sys/stdint.h>
34194677Sthompsa#include <sys/stddef.h>
35194677Sthompsa#include <sys/param.h>
36194677Sthompsa#include <sys/queue.h>
37194677Sthompsa#include <sys/types.h>
38194677Sthompsa#include <sys/systm.h>
39194677Sthompsa#include <sys/kernel.h>
40194677Sthompsa#include <sys/bus.h>
41194677Sthompsa#include <sys/module.h>
42194677Sthompsa#include <sys/lock.h>
43194677Sthompsa#include <sys/mutex.h>
44194677Sthompsa#include <sys/condvar.h>
45194677Sthompsa#include <sys/sysctl.h>
46194677Sthompsa#include <sys/sx.h>
47194677Sthompsa#include <sys/unistd.h>
48194677Sthompsa#include <sys/callout.h>
49194677Sthompsa#include <sys/malloc.h>
50194677Sthompsa#include <sys/priv.h>
51194677Sthompsa
52188942Sthompsa#include <dev/usb/usb.h>
53194677Sthompsa#include <dev/usb/usbdi.h>
54212136Sthompsa#include <dev/usb/usbdi_util.h>
55184610Salfred
56184610Salfred#define	USB_DEBUG_VAR uhub_debug
57184610Salfred
58188942Sthompsa#include <dev/usb/usb_core.h>
59188942Sthompsa#include <dev/usb/usb_process.h>
60188942Sthompsa#include <dev/usb/usb_device.h>
61188942Sthompsa#include <dev/usb/usb_request.h>
62188942Sthompsa#include <dev/usb/usb_debug.h>
63188942Sthompsa#include <dev/usb/usb_hub.h>
64188942Sthompsa#include <dev/usb/usb_util.h>
65188942Sthompsa#include <dev/usb/usb_busdma.h>
66188942Sthompsa#include <dev/usb/usb_transfer.h>
67188942Sthompsa#include <dev/usb/usb_dynamic.h>
68184610Salfred
69188942Sthompsa#include <dev/usb/usb_controller.h>
70188942Sthompsa#include <dev/usb/usb_bus.h>
71184610Salfred
72184610Salfred#define	UHUB_INTR_INTERVAL 250		/* ms */
73261106Shselaskyenum {
74261106Shselasky	UHUB_INTR_TRANSFER,
75261106Shselasky#if USB_HAVE_TT_SUPPORT
76261106Shselasky	UHUB_RESET_TT_TRANSFER,
77261106Shselasky#endif
78261106Shselasky	UHUB_N_TRANSFER,
79261106Shselasky};
80184610Salfred
81194677Sthompsa#ifdef USB_DEBUG
82184610Salfredstatic int uhub_debug = 0;
83184610Salfred
84248085Smariusstatic SYSCTL_NODE(_hw_usb, OID_AUTO, uhub, CTLFLAG_RW, 0, "USB HUB");
85242775ShselaskySYSCTL_INT(_hw_usb_uhub, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, &uhub_debug, 0,
86184610Salfred    "Debug level");
87199675SthompsaTUNABLE_INT("hw.usb.uhub.debug", &uhub_debug);
88184610Salfred#endif
89184610Salfred
90190735Sthompsa#if USB_HAVE_POWERD
91194228Sthompsastatic int usb_power_timeout = 30;	/* seconds */
92186730Salfred
93192502SthompsaSYSCTL_INT(_hw_usb, OID_AUTO, power_timeout, CTLFLAG_RW,
94194228Sthompsa    &usb_power_timeout, 0, "USB power timeout");
95190735Sthompsa#endif
96186730Salfred
97184610Salfredstruct uhub_current_state {
98184610Salfred	uint16_t port_change;
99184610Salfred	uint16_t port_status;
100184610Salfred};
101184610Salfred
102184610Salfredstruct uhub_softc {
103184610Salfred	struct uhub_current_state sc_st;/* current state */
104184610Salfred	device_t sc_dev;		/* base device */
105196498Salfred	struct mtx sc_mtx;		/* our mutex */
106192984Sthompsa	struct usb_device *sc_udev;	/* USB device */
107192984Sthompsa	struct usb_xfer *sc_xfer[UHUB_N_TRANSFER];	/* interrupt xfer */
108184610Salfred	uint8_t	sc_flags;
109185087Salfred#define	UHUB_FLAG_DID_EXPLORE 0x01
110184610Salfred	char	sc_name[32];
111184610Salfred};
112184610Salfred
113184610Salfred#define	UHUB_PROTO(sc) ((sc)->sc_udev->ddesc.bDeviceProtocol)
114184610Salfred#define	UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB)
115184610Salfred#define	UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT)
116235001Shselasky#define	UHUB_IS_MULTI_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBMTT)
117213435Shselasky#define	UHUB_IS_SUPER_SPEED(sc) (UHUB_PROTO(sc) == UDPROTO_SSHUB)
118184610Salfred
119184610Salfred/* prototypes for type checking: */
120184610Salfred
121184610Salfredstatic device_probe_t uhub_probe;
122184610Salfredstatic device_attach_t uhub_attach;
123184610Salfredstatic device_detach_t uhub_detach;
124186730Salfredstatic device_suspend_t uhub_suspend;
125186730Salfredstatic device_resume_t uhub_resume;
126184610Salfred
127184610Salfredstatic bus_driver_added_t uhub_driver_added;
128184610Salfredstatic bus_child_location_str_t uhub_child_location_string;
129184610Salfredstatic bus_child_pnpinfo_str_t uhub_child_pnpinfo_string;
130184610Salfred
131193045Sthompsastatic usb_callback_t uhub_intr_callback;
132261106Shselasky#if USB_HAVE_TT_SUPPORT
133261106Shselaskystatic usb_callback_t uhub_reset_tt_callback;
134261106Shselasky#endif
135184610Salfred
136194228Sthompsastatic void usb_dev_resume_peer(struct usb_device *udev);
137194228Sthompsastatic void usb_dev_suspend_peer(struct usb_device *udev);
138208008Sthompsastatic uint8_t usb_peer_should_wakeup(struct usb_device *udev);
139186730Salfred
140192984Sthompsastatic const struct usb_config uhub_config[UHUB_N_TRANSFER] = {
141184610Salfred
142261106Shselasky	[UHUB_INTR_TRANSFER] = {
143184610Salfred		.type = UE_INTERRUPT,
144184610Salfred		.endpoint = UE_ADDR_ANY,
145184610Salfred		.direction = UE_DIR_ANY,
146190734Sthompsa		.timeout = 0,
147190734Sthompsa		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
148190734Sthompsa		.bufsize = 0,	/* use wMaxPacketSize */
149190734Sthompsa		.callback = &uhub_intr_callback,
150190734Sthompsa		.interval = UHUB_INTR_INTERVAL,
151184610Salfred	},
152261106Shselasky#if USB_HAVE_TT_SUPPORT
153261106Shselasky	[UHUB_RESET_TT_TRANSFER] = {
154261106Shselasky		.type = UE_CONTROL,
155261106Shselasky		.endpoint = 0x00,	/* Control pipe */
156261106Shselasky		.direction = UE_DIR_ANY,
157261106Shselasky		.bufsize = sizeof(struct usb_device_request),
158261106Shselasky		.callback = &uhub_reset_tt_callback,
159261106Shselasky		.timeout = 1000,	/* 1 second */
160261106Shselasky		.usb_mode = USB_MODE_HOST,
161261106Shselasky	},
162261106Shselasky#endif
163184610Salfred};
164184610Salfred
165184610Salfred/*
166184610Salfred * driver instance for "hub" connected to "usb"
167184610Salfred * and "hub" connected to "hub"
168184610Salfred */
169184610Salfredstatic devclass_t uhub_devclass;
170184610Salfred
171190735Sthompsastatic device_method_t uhub_methods[] = {
172190735Sthompsa	DEVMETHOD(device_probe, uhub_probe),
173190735Sthompsa	DEVMETHOD(device_attach, uhub_attach),
174190735Sthompsa	DEVMETHOD(device_detach, uhub_detach),
175184610Salfred
176190735Sthompsa	DEVMETHOD(device_suspend, uhub_suspend),
177190735Sthompsa	DEVMETHOD(device_resume, uhub_resume),
178184610Salfred
179190735Sthompsa	DEVMETHOD(bus_child_location_str, uhub_child_location_string),
180190735Sthompsa	DEVMETHOD(bus_child_pnpinfo_str, uhub_child_pnpinfo_string),
181190735Sthompsa	DEVMETHOD(bus_driver_added, uhub_driver_added),
182190735Sthompsa	{0, 0}
183190735Sthompsa};
184190735Sthompsa
185190735Sthompsastatic driver_t uhub_driver = {
186190735Sthompsa	.name = "uhub",
187190735Sthompsa	.methods = uhub_methods,
188184610Salfred	.size = sizeof(struct uhub_softc)
189184610Salfred};
190184610Salfred
191189275SthompsaDRIVER_MODULE(uhub, usbus, uhub_driver, uhub_devclass, 0, 0);
192189275SthompsaDRIVER_MODULE(uhub, uhub, uhub_driver, uhub_devclass, NULL, 0);
193212122SthompsaMODULE_VERSION(uhub, 1);
194184610Salfred
195184610Salfredstatic void
196194677Sthompsauhub_intr_callback(struct usb_xfer *xfer, usb_error_t error)
197184610Salfred{
198194677Sthompsa	struct uhub_softc *sc = usbd_xfer_softc(xfer);
199184610Salfred
200184610Salfred	switch (USB_GET_STATE(xfer)) {
201184610Salfred	case USB_ST_TRANSFERRED:
202184610Salfred		DPRINTFN(2, "\n");
203184610Salfred		/*
204184610Salfred		 * This is an indication that some port
205184610Salfred		 * has changed status. Notify the bus
206184610Salfred		 * event handler thread that we need
207184610Salfred		 * to be explored again:
208184610Salfred		 */
209194228Sthompsa		usb_needs_explore(sc->sc_udev->bus, 0);
210184610Salfred
211184610Salfred	case USB_ST_SETUP:
212194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
213194228Sthompsa		usbd_transfer_submit(xfer);
214187178Sthompsa		break;
215184610Salfred
216184610Salfred	default:			/* Error */
217184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
218187178Sthompsa			/*
219187178Sthompsa			 * Do a clear-stall. The "stall_pipe" flag
220187178Sthompsa			 * will get cleared before next callback by
221187178Sthompsa			 * the USB stack.
222187178Sthompsa			 */
223194677Sthompsa			usbd_xfer_set_stall(xfer);
224194677Sthompsa			usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
225194228Sthompsa			usbd_transfer_submit(xfer);
226184610Salfred		}
227187178Sthompsa		break;
228184610Salfred	}
229184610Salfred}
230184610Salfred
231184610Salfred/*------------------------------------------------------------------------*
232261106Shselasky *      uhub_reset_tt_proc
233261106Shselasky *
234261106Shselasky * This function starts the TT reset USB request
235261106Shselasky *------------------------------------------------------------------------*/
236261106Shselasky#if USB_HAVE_TT_SUPPORT
237261106Shselaskystatic void
238261106Shselaskyuhub_reset_tt_proc(struct usb_proc_msg *_pm)
239261106Shselasky{
240261106Shselasky	struct usb_udev_msg *pm = (void *)_pm;
241261106Shselasky	struct usb_device *udev = pm->udev;
242261106Shselasky	struct usb_hub *hub;
243261106Shselasky	struct uhub_softc *sc;
244261106Shselasky
245261106Shselasky	hub = udev->hub;
246261106Shselasky	if (hub == NULL)
247261106Shselasky		return;
248261106Shselasky	sc = hub->hubsoftc;
249261106Shselasky	if (sc == NULL)
250261106Shselasky		return;
251261106Shselasky
252261106Shselasky	/* Change lock */
253261106Shselasky	USB_BUS_UNLOCK(udev->bus);
254261106Shselasky	mtx_lock(&sc->sc_mtx);
255261106Shselasky	/* Start transfer */
256261106Shselasky	usbd_transfer_start(sc->sc_xfer[UHUB_RESET_TT_TRANSFER]);
257261106Shselasky	/* Change lock */
258261106Shselasky	mtx_unlock(&sc->sc_mtx);
259261106Shselasky	USB_BUS_LOCK(udev->bus);
260261106Shselasky}
261261106Shselasky#endif
262261106Shselasky
263261106Shselasky/*------------------------------------------------------------------------*
264261106Shselasky *      uhub_tt_buffer_reset_async_locked
265261106Shselasky *
266261106Shselasky * This function queues a TT reset for the given USB device and endpoint.
267261106Shselasky *------------------------------------------------------------------------*/
268261106Shselasky#if USB_HAVE_TT_SUPPORT
269261106Shselaskyvoid
270261106Shselaskyuhub_tt_buffer_reset_async_locked(struct usb_device *child, struct usb_endpoint *ep)
271261106Shselasky{
272261106Shselasky	struct usb_device_request req;
273261106Shselasky	struct usb_device *udev;
274261106Shselasky	struct usb_hub *hub;
275261106Shselasky	struct usb_port *up;
276261106Shselasky	uint16_t wValue;
277261106Shselasky	uint8_t port;
278261106Shselasky
279261106Shselasky	if (child == NULL || ep == NULL)
280261106Shselasky		return;
281261106Shselasky
282261106Shselasky	udev = child->parent_hs_hub;
283261106Shselasky	port = child->hs_port_no;
284261106Shselasky
285261106Shselasky	if (udev == NULL)
286261106Shselasky		return;
287261106Shselasky
288261106Shselasky	hub = udev->hub;
289261106Shselasky	if ((hub == NULL) ||
290261106Shselasky	    (udev->speed != USB_SPEED_HIGH) ||
291261106Shselasky	    (child->speed != USB_SPEED_LOW &&
292261106Shselasky	     child->speed != USB_SPEED_FULL) ||
293261106Shselasky	    (child->flags.usb_mode != USB_MODE_HOST) ||
294261106Shselasky	    (port == 0) || (ep->edesc == NULL)) {
295261106Shselasky		/* not applicable */
296261106Shselasky		return;
297261106Shselasky	}
298261106Shselasky
299261106Shselasky	USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
300261106Shselasky
301261106Shselasky	up = hub->ports + port - 1;
302261106Shselasky
303261106Shselasky	if (udev->ddesc.bDeviceClass == UDCLASS_HUB &&
304261106Shselasky	    udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT)
305261106Shselasky		port = 1;
306261106Shselasky
307261106Shselasky	/* if we already received a clear buffer request, reset the whole TT */
308261106Shselasky	if (up->req_reset_tt.bRequest != 0) {
309261106Shselasky		req.bmRequestType = UT_WRITE_CLASS_OTHER;
310261106Shselasky		req.bRequest = UR_RESET_TT;
311261106Shselasky		USETW(req.wValue, 0);
312261106Shselasky		req.wIndex[0] = port;
313261106Shselasky		req.wIndex[1] = 0;
314261106Shselasky		USETW(req.wLength, 0);
315261106Shselasky	} else {
316261106Shselasky		wValue = (ep->edesc->bEndpointAddress & 0xF) |
317261106Shselasky		      ((child->address & 0x7F) << 4) |
318261106Shselasky		      ((ep->edesc->bEndpointAddress & 0x80) << 8) |
319261106Shselasky		      ((ep->edesc->bmAttributes & 3) << 12);
320261106Shselasky
321261106Shselasky		req.bmRequestType = UT_WRITE_CLASS_OTHER;
322261106Shselasky		req.bRequest = UR_CLEAR_TT_BUFFER;
323261106Shselasky		USETW(req.wValue, wValue);
324261106Shselasky		req.wIndex[0] = port;
325261106Shselasky		req.wIndex[1] = 0;
326261106Shselasky		USETW(req.wLength, 0);
327261106Shselasky	}
328261106Shselasky	up->req_reset_tt = req;
329261106Shselasky	/* get reset transfer started */
330261106Shselasky	usb_proc_msignal(&udev->bus->non_giant_callback_proc,
331261106Shselasky	    &hub->tt_msg[0], &hub->tt_msg[1]);
332261106Shselasky}
333261106Shselasky#endif
334261106Shselasky
335261106Shselasky#if USB_HAVE_TT_SUPPORT
336261106Shselaskystatic void
337261106Shselaskyuhub_reset_tt_callback(struct usb_xfer *xfer, usb_error_t error)
338261106Shselasky{
339261106Shselasky	struct uhub_softc *sc;
340261106Shselasky	struct usb_device *udev;
341261106Shselasky	struct usb_port *up;
342261106Shselasky	uint8_t x;
343261106Shselasky
344261106Shselasky	DPRINTF("TT buffer reset\n");
345261106Shselasky
346261106Shselasky	sc = usbd_xfer_softc(xfer);
347261106Shselasky	udev = sc->sc_udev;
348261106Shselasky
349261106Shselasky	switch (USB_GET_STATE(xfer)) {
350261106Shselasky	case USB_ST_TRANSFERRED:
351261106Shselasky	case USB_ST_SETUP:
352261106Shselaskytr_setup:
353261106Shselasky		USB_BUS_LOCK(udev->bus);
354261106Shselasky		/* find first port which needs a TT reset */
355261106Shselasky		for (x = 0; x != udev->hub->nports; x++) {
356261106Shselasky			up = udev->hub->ports + x;
357261106Shselasky
358261106Shselasky			if (up->req_reset_tt.bRequest == 0)
359261106Shselasky				continue;
360261106Shselasky
361261106Shselasky			/* copy in the transfer */
362261106Shselasky			usbd_copy_in(xfer->frbuffers, 0, &up->req_reset_tt,
363261106Shselasky			    sizeof(up->req_reset_tt));
364261106Shselasky			/* reset buffer */
365261106Shselasky			memset(&up->req_reset_tt, 0, sizeof(up->req_reset_tt));
366261106Shselasky
367261106Shselasky			/* set length */
368261106Shselasky			usbd_xfer_set_frame_len(xfer, 0, sizeof(up->req_reset_tt));
369261106Shselasky			xfer->nframes = 1;
370261106Shselasky			USB_BUS_UNLOCK(udev->bus);
371261106Shselasky
372261106Shselasky			usbd_transfer_submit(xfer);
373261106Shselasky			return;
374261106Shselasky		}
375261106Shselasky		USB_BUS_UNLOCK(udev->bus);
376261106Shselasky		break;
377261106Shselasky
378261106Shselasky	default:
379261106Shselasky		if (error == USB_ERR_CANCELLED)
380261106Shselasky			break;
381261106Shselasky
382261106Shselasky		DPRINTF("TT buffer reset failed (%s)\n", usbd_errstr(error));
383261106Shselasky		goto tr_setup;
384261106Shselasky	}
385261106Shselasky}
386261106Shselasky#endif
387261106Shselasky
388261106Shselasky/*------------------------------------------------------------------------*
389261106Shselasky *      uhub_count_active_host_ports
390261106Shselasky *
391261106Shselasky * This function counts the number of active ports at the given speed.
392261106Shselasky *------------------------------------------------------------------------*/
393261106Shselaskyuint8_t
394261106Shselaskyuhub_count_active_host_ports(struct usb_device *udev, enum usb_dev_speed speed)
395261106Shselasky{
396261106Shselasky	struct uhub_softc *sc;
397261106Shselasky	struct usb_device *child;
398261106Shselasky	struct usb_hub *hub;
399261106Shselasky	struct usb_port *up;
400261106Shselasky	uint8_t retval = 0;
401261106Shselasky	uint8_t x;
402261106Shselasky
403261106Shselasky	if (udev == NULL)
404261106Shselasky		goto done;
405261106Shselasky	hub = udev->hub;
406261106Shselasky	if (hub == NULL)
407261106Shselasky		goto done;
408261106Shselasky	sc = hub->hubsoftc;
409261106Shselasky	if (sc == NULL)
410261106Shselasky		goto done;
411261106Shselasky
412261106Shselasky	for (x = 0; x != hub->nports; x++) {
413261106Shselasky		up = hub->ports + x;
414261106Shselasky		child = usb_bus_port_get_device(udev->bus, up);
415261106Shselasky		if (child != NULL &&
416261106Shselasky		    child->flags.usb_mode == USB_MODE_HOST &&
417261106Shselasky		    child->speed == speed)
418261106Shselasky			retval++;
419261106Shselasky	}
420261106Shselaskydone:
421261106Shselasky	return (retval);
422261106Shselasky}
423261106Shselasky
424267350Shselaskyvoid
425267350Shselaskyuhub_explore_handle_re_enumerate(struct usb_device *child)
426267350Shselasky{
427267350Shselasky	uint8_t do_unlock;
428267350Shselasky	usb_error_t err;
429267350Shselasky
430267350Shselasky	/* check if device should be re-enumerated */
431267350Shselasky	if (child->flags.usb_mode != USB_MODE_HOST)
432267350Shselasky		return;
433267350Shselasky
434267350Shselasky	do_unlock = usbd_enum_lock(child);
435267350Shselasky	switch (child->re_enumerate_wait) {
436267350Shselasky	case USB_RE_ENUM_START:
437267350Shselasky		err = usbd_set_config_index(child,
438267350Shselasky		    USB_UNCONFIG_INDEX);
439267350Shselasky		if (err != 0) {
440267350Shselasky			DPRINTF("Unconfigure failed: %s: Ignored.\n",
441267350Shselasky			    usbd_errstr(err));
442267350Shselasky		}
443267350Shselasky		if (child->parent_hub == NULL) {
444267350Shselasky			/* the root HUB cannot be re-enumerated */
445267350Shselasky			DPRINTFN(6, "cannot reset root HUB\n");
446267350Shselasky			err = 0;
447267350Shselasky		} else {
448267350Shselasky			err = usbd_req_re_enumerate(child, NULL);
449267350Shselasky		}
450267350Shselasky		if (err == 0)
451267350Shselasky			err = usbd_set_config_index(child, 0);
452267350Shselasky		if (err == 0) {
453267350Shselasky			err = usb_probe_and_attach(child,
454267350Shselasky			    USB_IFACE_INDEX_ANY);
455267350Shselasky		}
456267350Shselasky		child->re_enumerate_wait = USB_RE_ENUM_DONE;
457267350Shselasky		break;
458267350Shselasky
459267350Shselasky	case USB_RE_ENUM_PWR_OFF:
460267350Shselasky		/* get the device unconfigured */
461267350Shselasky		err = usbd_set_config_index(child,
462267350Shselasky		    USB_UNCONFIG_INDEX);
463267350Shselasky		if (err) {
464267350Shselasky			DPRINTFN(0, "Could not unconfigure "
465267350Shselasky			    "device (ignored)\n");
466267350Shselasky		}
467267350Shselasky		if (child->parent_hub == NULL) {
468267350Shselasky			/* the root HUB cannot be re-enumerated */
469267350Shselasky			DPRINTFN(6, "cannot set port feature\n");
470267350Shselasky			err = 0;
471267350Shselasky		} else {
472267350Shselasky			/* clear port enable */
473267350Shselasky			err = usbd_req_clear_port_feature(child->parent_hub,
474267350Shselasky			    NULL, child->port_no, UHF_PORT_ENABLE);
475267350Shselasky			if (err) {
476267350Shselasky				DPRINTFN(0, "Could not disable port "
477267350Shselasky				    "(ignored)\n");
478267350Shselasky			}
479267350Shselasky		}
480267350Shselasky		child->re_enumerate_wait = USB_RE_ENUM_DONE;
481267350Shselasky		break;
482267350Shselasky
483267350Shselasky	case USB_RE_ENUM_SET_CONFIG:
484267350Shselasky		err = usbd_set_config_index(child,
485267350Shselasky		    child->next_config_index);
486267350Shselasky		if (err != 0) {
487267350Shselasky			DPRINTF("Configure failed: %s: Ignored.\n",
488267350Shselasky			    usbd_errstr(err));
489267350Shselasky		} else {
490267350Shselasky			err = usb_probe_and_attach(child,
491267350Shselasky			    USB_IFACE_INDEX_ANY);
492267350Shselasky		}
493267350Shselasky		child->re_enumerate_wait = USB_RE_ENUM_DONE;
494267350Shselasky		break;
495267350Shselasky
496267350Shselasky	default:
497267350Shselasky		child->re_enumerate_wait = USB_RE_ENUM_DONE;
498267350Shselasky		break;
499267350Shselasky	}
500267350Shselasky	if (do_unlock)
501267350Shselasky		usbd_enum_unlock(child);
502267350Shselasky}
503267350Shselasky
504261106Shselasky/*------------------------------------------------------------------------*
505184610Salfred *	uhub_explore_sub - subroutine
506184610Salfred *
507184610Salfred * Return values:
508184610Salfred *    0: Success
509184610Salfred * Else: A control transaction failed
510184610Salfred *------------------------------------------------------------------------*/
511193045Sthompsastatic usb_error_t
512192984Sthompsauhub_explore_sub(struct uhub_softc *sc, struct usb_port *up)
513184610Salfred{
514192984Sthompsa	struct usb_bus *bus;
515192984Sthompsa	struct usb_device *child;
516184610Salfred	uint8_t refcount;
517193045Sthompsa	usb_error_t err;
518184610Salfred
519184610Salfred	bus = sc->sc_udev->bus;
520184610Salfred	err = 0;
521184610Salfred
522184610Salfred	/* get driver added refcount from USB bus */
523184610Salfred	refcount = bus->driver_added_refcount;
524184610Salfred
525184610Salfred	/* get device assosiated with the given port */
526194228Sthompsa	child = usb_bus_port_get_device(bus, up);
527184610Salfred	if (child == NULL) {
528184610Salfred		/* nothing to do */
529184610Salfred		goto done;
530184610Salfred	}
531213435Shselasky
532267350Shselasky	uhub_explore_handle_re_enumerate(child);
533213432Shselasky
534184610Salfred	/* check if probe and attach should be done */
535184610Salfred
536184610Salfred	if (child->driver_added_refcount != refcount) {
537184610Salfred		child->driver_added_refcount = refcount;
538194228Sthompsa		err = usb_probe_and_attach(child,
539184610Salfred		    USB_IFACE_INDEX_ANY);
540184610Salfred		if (err) {
541184610Salfred			goto done;
542184610Salfred		}
543184610Salfred	}
544184610Salfred	/* start control transfer, if device mode */
545184610Salfred
546213435Shselasky	if (child->flags.usb_mode == USB_MODE_DEVICE)
547207080Sthompsa		usbd_ctrl_transfer_setup(child);
548213435Shselasky
549184610Salfred	/* if a HUB becomes present, do a recursive HUB explore */
550184610Salfred
551213435Shselasky	if (child->hub)
552184610Salfred		err = (child->hub->explore) (child);
553213435Shselasky
554184610Salfreddone:
555184610Salfred	return (err);
556184610Salfred}
557184610Salfred
558184610Salfred/*------------------------------------------------------------------------*
559184610Salfred *	uhub_read_port_status - factored out code
560184610Salfred *------------------------------------------------------------------------*/
561193045Sthompsastatic usb_error_t
562184610Salfreduhub_read_port_status(struct uhub_softc *sc, uint8_t portno)
563184610Salfred{
564192984Sthompsa	struct usb_port_status ps;
565193045Sthompsa	usb_error_t err;
566184610Salfred
567194228Sthompsa	err = usbd_req_get_port_status(
568188986Sthompsa	    sc->sc_udev, NULL, &ps, portno);
569184610Salfred
570184610Salfred	/* update status regardless of error */
571184610Salfred
572184610Salfred	sc->sc_st.port_status = UGETW(ps.wPortStatus);
573184610Salfred	sc->sc_st.port_change = UGETW(ps.wPortChange);
574184610Salfred
575184610Salfred	/* debugging print */
576184610Salfred
577184610Salfred	DPRINTFN(4, "port %d, wPortStatus=0x%04x, "
578184610Salfred	    "wPortChange=0x%04x, err=%s\n",
579184610Salfred	    portno, sc->sc_st.port_status,
580194228Sthompsa	    sc->sc_st.port_change, usbd_errstr(err));
581184610Salfred	return (err);
582184610Salfred}
583184610Salfred
584184610Salfred/*------------------------------------------------------------------------*
585184610Salfred *	uhub_reattach_port
586184610Salfred *
587184610Salfred * Returns:
588184610Salfred *    0: Success
589184610Salfred * Else: A control transaction failed
590184610Salfred *------------------------------------------------------------------------*/
591193045Sthompsastatic usb_error_t
592184610Salfreduhub_reattach_port(struct uhub_softc *sc, uint8_t portno)
593184610Salfred{
594192984Sthompsa	struct usb_device *child;
595192984Sthompsa	struct usb_device *udev;
596192500Sthompsa	enum usb_dev_speed speed;
597192500Sthompsa	enum usb_hc_mode mode;
598193045Sthompsa	usb_error_t err;
599230302Shselasky	uint16_t power_mask;
600184610Salfred	uint8_t timeout;
601184610Salfred
602184610Salfred	DPRINTF("reattaching port %d\n", portno);
603184610Salfred
604184610Salfred	err = 0;
605184610Salfred	timeout = 0;
606184610Salfred	udev = sc->sc_udev;
607194228Sthompsa	child = usb_bus_port_get_device(udev->bus,
608184610Salfred	    udev->hub->ports + portno - 1);
609184610Salfred
610184610Salfredrepeat:
611184610Salfred
612184610Salfred	/* first clear the port connection change bit */
613184610Salfred
614194228Sthompsa	err = usbd_req_clear_port_feature(udev, NULL,
615186730Salfred	    portno, UHF_C_PORT_CONNECTION);
616184610Salfred
617184610Salfred	if (err) {
618184610Salfred		goto error;
619184610Salfred	}
620197553Sthompsa	/* check if there is a child */
621184610Salfred
622197553Sthompsa	if (child != NULL) {
623197553Sthompsa		/*
624197553Sthompsa		 * Free USB device and all subdevices, if any.
625197553Sthompsa		 */
626197553Sthompsa		usb_free_device(child, 0);
627184610Salfred		child = NULL;
628184610Salfred	}
629184610Salfred	/* get fresh status */
630184610Salfred
631184610Salfred	err = uhub_read_port_status(sc, portno);
632184610Salfred	if (err) {
633184610Salfred		goto error;
634184610Salfred	}
635184610Salfred	/* check if nothing is connected to the port */
636184610Salfred
637184610Salfred	if (!(sc->sc_st.port_status & UPS_CURRENT_CONNECT_STATUS)) {
638184610Salfred		goto error;
639184610Salfred	}
640184610Salfred	/* check if there is no power on the port and print a warning */
641184610Salfred
642230302Shselasky	switch (udev->speed) {
643230302Shselasky	case USB_SPEED_HIGH:
644230302Shselasky	case USB_SPEED_FULL:
645230302Shselasky	case USB_SPEED_LOW:
646230302Shselasky		power_mask = UPS_PORT_POWER;
647230302Shselasky		break;
648230302Shselasky	case USB_SPEED_SUPER:
649230302Shselasky		if (udev->parent_hub == NULL)
650230302Shselasky			power_mask = UPS_PORT_POWER;
651230302Shselasky		else
652230302Shselasky			power_mask = UPS_PORT_POWER_SS;
653230302Shselasky		break;
654230302Shselasky	default:
655230302Shselasky		power_mask = 0;
656230302Shselasky		break;
657230302Shselasky	}
658230302Shselasky	if (!(sc->sc_st.port_status & power_mask)) {
659184610Salfred		DPRINTF("WARNING: strange, connected port %d "
660184610Salfred		    "has no power\n", portno);
661184610Salfred	}
662230302Shselasky
663184610Salfred	/* check if the device is in Host Mode */
664184610Salfred
665184610Salfred	if (!(sc->sc_st.port_status & UPS_PORT_MODE_DEVICE)) {
666184610Salfred
667184610Salfred		DPRINTF("Port %d is in Host Mode\n", portno);
668184610Salfred
669186730Salfred		if (sc->sc_st.port_status & UPS_SUSPEND) {
670213435Shselasky			/*
671213435Shselasky			 * NOTE: Should not get here in SuperSpeed
672213435Shselasky			 * mode, because the HUB should report this
673213435Shselasky			 * bit as zero.
674213435Shselasky			 */
675186730Salfred			DPRINTF("Port %d was still "
676186730Salfred			    "suspended, clearing.\n", portno);
677213435Shselasky			err = usbd_req_clear_port_feature(udev,
678188986Sthompsa			    NULL, portno, UHF_PORT_SUSPEND);
679186730Salfred		}
680213435Shselasky
681184610Salfred		/* USB Host Mode */
682184610Salfred
683184610Salfred		/* wait for maximum device power up time */
684184610Salfred
685194228Sthompsa		usb_pause_mtx(NULL,
686242775Shselasky		    USB_MS_TO_TICKS(usb_port_powerup_delay));
687184610Salfred
688184610Salfred		/* reset port, which implies enabling it */
689184610Salfred
690194228Sthompsa		err = usbd_req_reset_port(udev, NULL, portno);
691184610Salfred
692184610Salfred		if (err) {
693184610Salfred			DPRINTFN(0, "port %d reset "
694184610Salfred			    "failed, error=%s\n",
695194228Sthompsa			    portno, usbd_errstr(err));
696184610Salfred			goto error;
697184610Salfred		}
698184610Salfred		/* get port status again, it might have changed during reset */
699184610Salfred
700184610Salfred		err = uhub_read_port_status(sc, portno);
701184610Salfred		if (err) {
702184610Salfred			goto error;
703184610Salfred		}
704184610Salfred		/* check if something changed during port reset */
705184610Salfred
706184610Salfred		if ((sc->sc_st.port_change & UPS_C_CONNECT_STATUS) ||
707184610Salfred		    (!(sc->sc_st.port_status & UPS_CURRENT_CONNECT_STATUS))) {
708184610Salfred			if (timeout) {
709184610Salfred				DPRINTFN(0, "giving up port reset "
710199816Sthompsa				    "- device vanished\n");
711184610Salfred				goto error;
712184610Salfred			}
713184610Salfred			timeout = 1;
714184610Salfred			goto repeat;
715184610Salfred		}
716184610Salfred	} else {
717184610Salfred		DPRINTF("Port %d is in Device Mode\n", portno);
718184610Salfred	}
719184610Salfred
720184610Salfred	/*
721184610Salfred	 * Figure out the device speed
722184610Salfred	 */
723187180Sthompsa	switch (udev->speed) {
724187180Sthompsa	case USB_SPEED_HIGH:
725187180Sthompsa		if (sc->sc_st.port_status & UPS_HIGH_SPEED)
726187180Sthompsa			speed = USB_SPEED_HIGH;
727187180Sthompsa		else if (sc->sc_st.port_status & UPS_LOW_SPEED)
728187180Sthompsa			speed = USB_SPEED_LOW;
729187180Sthompsa		else
730187180Sthompsa			speed = USB_SPEED_FULL;
731187180Sthompsa		break;
732187180Sthompsa	case USB_SPEED_FULL:
733187180Sthompsa		if (sc->sc_st.port_status & UPS_LOW_SPEED)
734187180Sthompsa			speed = USB_SPEED_LOW;
735187180Sthompsa		else
736187180Sthompsa			speed = USB_SPEED_FULL;
737187180Sthompsa		break;
738187180Sthompsa	case USB_SPEED_LOW:
739187180Sthompsa		speed = USB_SPEED_LOW;
740187180Sthompsa		break;
741213435Shselasky	case USB_SPEED_SUPER:
742213435Shselasky		if (udev->parent_hub == NULL) {
743213435Shselasky			/* Root HUB - special case */
744213435Shselasky			switch (sc->sc_st.port_status & UPS_OTHER_SPEED) {
745213435Shselasky			case 0:
746213435Shselasky				speed = USB_SPEED_FULL;
747213435Shselasky				break;
748213435Shselasky			case UPS_LOW_SPEED:
749213435Shselasky				speed = USB_SPEED_LOW;
750213435Shselasky				break;
751213435Shselasky			case UPS_HIGH_SPEED:
752213435Shselasky				speed = USB_SPEED_HIGH;
753213435Shselasky				break;
754213435Shselasky			default:
755213435Shselasky				speed = USB_SPEED_SUPER;
756213435Shselasky				break;
757213435Shselasky			}
758213435Shselasky		} else {
759213435Shselasky			speed = USB_SPEED_SUPER;
760213435Shselasky		}
761213435Shselasky		break;
762187180Sthompsa	default:
763187180Sthompsa		/* same speed like parent */
764187180Sthompsa		speed = udev->speed;
765187180Sthompsa		break;
766187180Sthompsa	}
767213435Shselasky	if (speed == USB_SPEED_SUPER) {
768213435Shselasky		err = usbd_req_set_hub_u1_timeout(udev, NULL,
769213435Shselasky		    portno, 128 - (2 * udev->depth));
770213435Shselasky		if (err) {
771213435Shselasky			DPRINTFN(0, "port %d U1 timeout "
772213435Shselasky			    "failed, error=%s\n",
773213435Shselasky			    portno, usbd_errstr(err));
774213435Shselasky		}
775213435Shselasky		err = usbd_req_set_hub_u2_timeout(udev, NULL,
776213435Shselasky		    portno, 128 - (2 * udev->depth));
777213435Shselasky		if (err) {
778213435Shselasky			DPRINTFN(0, "port %d U2 timeout "
779213435Shselasky			    "failed, error=%s\n",
780213435Shselasky			    portno, usbd_errstr(err));
781213435Shselasky		}
782213435Shselasky	}
783213435Shselasky
784184610Salfred	/*
785184610Salfred	 * Figure out the device mode
786184610Salfred	 *
787184610Salfred	 * NOTE: This part is currently FreeBSD specific.
788184610Salfred	 */
789257040Shselasky	if (udev->parent_hub != NULL) {
790257040Shselasky		/* inherit mode from the parent HUB */
791257040Shselasky		mode = udev->parent_hub->flags.usb_mode;
792257040Shselasky	} else if (sc->sc_st.port_status & UPS_PORT_MODE_DEVICE)
793192499Sthompsa		mode = USB_MODE_DEVICE;
794187180Sthompsa	else
795192499Sthompsa		mode = USB_MODE_HOST;
796184610Salfred
797184610Salfred	/* need to create a new child */
798194228Sthompsa	child = usb_alloc_device(sc->sc_dev, udev->bus, udev,
799192499Sthompsa	    udev->depth + 1, portno - 1, portno, speed, mode);
800184610Salfred	if (child == NULL) {
801199816Sthompsa		DPRINTFN(0, "could not allocate new device\n");
802184610Salfred		goto error;
803184610Salfred	}
804184610Salfred	return (0);			/* success */
805184610Salfred
806184610Salfrederror:
807197553Sthompsa	if (child != NULL) {
808197553Sthompsa		/*
809197553Sthompsa		 * Free USB device and all subdevices, if any.
810197553Sthompsa		 */
811197553Sthompsa		usb_free_device(child, 0);
812184610Salfred		child = NULL;
813184610Salfred	}
814184610Salfred	if (err == 0) {
815184610Salfred		if (sc->sc_st.port_status & UPS_PORT_ENABLED) {
816194228Sthompsa			err = usbd_req_clear_port_feature(
817188986Sthompsa			    sc->sc_udev, NULL,
818184610Salfred			    portno, UHF_PORT_ENABLE);
819184610Salfred		}
820184610Salfred	}
821184610Salfred	if (err) {
822184610Salfred		DPRINTFN(0, "device problem (%s), "
823194228Sthompsa		    "disabling port %d\n", usbd_errstr(err), portno);
824184610Salfred	}
825184610Salfred	return (err);
826184610Salfred}
827184610Salfred
828184610Salfred/*------------------------------------------------------------------------*
829213435Shselasky *	usb_device_20_compatible
830213435Shselasky *
831213435Shselasky * Returns:
832213435Shselasky *    0: HUB does not support suspend and resume
833213435Shselasky * Else: HUB supports suspend and resume
834213435Shselasky *------------------------------------------------------------------------*/
835213435Shselaskystatic uint8_t
836213435Shselaskyusb_device_20_compatible(struct usb_device *udev)
837213435Shselasky{
838213435Shselasky	if (udev == NULL)
839213435Shselasky		return (0);
840213435Shselasky	switch (udev->speed) {
841213435Shselasky	case USB_SPEED_LOW:
842213435Shselasky	case USB_SPEED_FULL:
843213435Shselasky	case USB_SPEED_HIGH:
844213435Shselasky		return (1);
845213435Shselasky	default:
846213435Shselasky		return (0);
847213435Shselasky	}
848213435Shselasky}
849213435Shselasky
850213435Shselasky/*------------------------------------------------------------------------*
851184610Salfred *	uhub_suspend_resume_port
852184610Salfred *
853184610Salfred * Returns:
854184610Salfred *    0: Success
855184610Salfred * Else: A control transaction failed
856184610Salfred *------------------------------------------------------------------------*/
857193045Sthompsastatic usb_error_t
858184610Salfreduhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno)
859184610Salfred{
860192984Sthompsa	struct usb_device *child;
861192984Sthompsa	struct usb_device *udev;
862184610Salfred	uint8_t is_suspend;
863193045Sthompsa	usb_error_t err;
864184610Salfred
865184610Salfred	DPRINTF("port %d\n", portno);
866184610Salfred
867184610Salfred	udev = sc->sc_udev;
868194228Sthompsa	child = usb_bus_port_get_device(udev->bus,
869184610Salfred	    udev->hub->ports + portno - 1);
870184610Salfred
871184610Salfred	/* first clear the port suspend change bit */
872184610Salfred
873213435Shselasky	if (usb_device_20_compatible(udev)) {
874213435Shselasky		err = usbd_req_clear_port_feature(udev, NULL,
875213435Shselasky		    portno, UHF_C_PORT_SUSPEND);
876213435Shselasky	} else {
877213435Shselasky		err = usbd_req_clear_port_feature(udev, NULL,
878213435Shselasky		    portno, UHF_C_PORT_LINK_STATE);
879213435Shselasky	}
880213435Shselasky
881184610Salfred	if (err) {
882186730Salfred		DPRINTF("clearing suspend failed.\n");
883184610Salfred		goto done;
884184610Salfred	}
885184610Salfred	/* get fresh status */
886184610Salfred
887184610Salfred	err = uhub_read_port_status(sc, portno);
888184610Salfred	if (err) {
889186730Salfred		DPRINTF("reading port status failed.\n");
890184610Salfred		goto done;
891184610Salfred	}
892213435Shselasky	/* convert current state */
893184610Salfred
894213435Shselasky	if (usb_device_20_compatible(udev)) {
895213435Shselasky		if (sc->sc_st.port_status & UPS_SUSPEND) {
896213435Shselasky			is_suspend = 1;
897213435Shselasky		} else {
898213435Shselasky			is_suspend = 0;
899213435Shselasky		}
900184610Salfred	} else {
901213435Shselasky		switch (UPS_PORT_LINK_STATE_GET(sc->sc_st.port_status)) {
902230302Shselasky		case UPS_PORT_LS_U3:
903230302Shselasky			is_suspend = 1;
904230302Shselasky			break;
905230302Shselasky		case UPS_PORT_LS_SS_INA:
906230302Shselasky			usbd_req_warm_reset_port(udev, NULL, portno);
907213435Shselasky			is_suspend = 0;
908213435Shselasky			break;
909213435Shselasky		default:
910230302Shselasky			is_suspend = 0;
911213435Shselasky			break;
912213435Shselasky		}
913184610Salfred	}
914186730Salfred
915186730Salfred	DPRINTF("suspended=%u\n", is_suspend);
916186730Salfred
917184610Salfred	/* do the suspend or resume */
918184610Salfred
919184610Salfred	if (child) {
920186730Salfred		/*
921186730Salfred		 * This code handle two cases: 1) Host Mode - we can only
922186730Salfred		 * receive resume here 2) Device Mode - we can receive
923186730Salfred		 * suspend and resume here
924186730Salfred		 */
925186730Salfred		if (is_suspend == 0)
926194228Sthompsa			usb_dev_resume_peer(child);
927230302Shselasky		else if (child->flags.usb_mode == USB_MODE_DEVICE)
928194228Sthompsa			usb_dev_suspend_peer(child);
929184610Salfred	}
930184610Salfreddone:
931184610Salfred	return (err);
932184610Salfred}
933184610Salfred
934184610Salfred/*------------------------------------------------------------------------*
935190735Sthompsa *	uhub_root_interrupt
936190735Sthompsa *
937190735Sthompsa * This function is called when a Root HUB interrupt has
938190735Sthompsa * happened. "ptr" and "len" makes up the Root HUB interrupt
939190735Sthompsa * packet. This function is called having the "bus_mtx" locked.
940190735Sthompsa *------------------------------------------------------------------------*/
941190735Sthompsavoid
942192984Sthompsauhub_root_intr(struct usb_bus *bus, const uint8_t *ptr, uint8_t len)
943190735Sthompsa{
944190735Sthompsa	USB_BUS_LOCK_ASSERT(bus, MA_OWNED);
945190735Sthompsa
946194228Sthompsa	usb_needs_explore(bus, 0);
947190735Sthompsa}
948190735Sthompsa
949213435Shselaskystatic uint8_t
950213435Shselaskyuhub_is_too_deep(struct usb_device *udev)
951213435Shselasky{
952213435Shselasky	switch (udev->speed) {
953213435Shselasky	case USB_SPEED_FULL:
954213435Shselasky	case USB_SPEED_LOW:
955213435Shselasky	case USB_SPEED_HIGH:
956213435Shselasky		if (udev->depth > USB_HUB_MAX_DEPTH)
957213435Shselasky			return (1);
958213435Shselasky		break;
959213435Shselasky	case USB_SPEED_SUPER:
960213435Shselasky		if (udev->depth > USB_SS_HUB_DEPTH_MAX)
961213435Shselasky			return (1);
962213435Shselasky		break;
963213435Shselasky	default:
964213435Shselasky		break;
965213435Shselasky	}
966213435Shselasky	return (0);
967213435Shselasky}
968213435Shselasky
969190735Sthompsa/*------------------------------------------------------------------------*
970184610Salfred *	uhub_explore
971184610Salfred *
972184610Salfred * Returns:
973184610Salfred *     0: Success
974184610Salfred *  Else: Failure
975184610Salfred *------------------------------------------------------------------------*/
976193045Sthompsastatic usb_error_t
977192984Sthompsauhub_explore(struct usb_device *udev)
978184610Salfred{
979192984Sthompsa	struct usb_hub *hub;
980184610Salfred	struct uhub_softc *sc;
981192984Sthompsa	struct usb_port *up;
982193045Sthompsa	usb_error_t err;
983184610Salfred	uint8_t portno;
984184610Salfred	uint8_t x;
985247090Shselasky	uint8_t do_unlock;
986184610Salfred
987184610Salfred	hub = udev->hub;
988184610Salfred	sc = hub->hubsoftc;
989184610Salfred
990184610Salfred	DPRINTFN(11, "udev=%p addr=%d\n", udev, udev->address);
991184610Salfred
992213435Shselasky	/* ignore devices that are too deep */
993213435Shselasky	if (uhub_is_too_deep(udev))
994184610Salfred		return (USB_ERR_TOO_DEEP);
995191824Sthompsa
996213435Shselasky	/* check if device is suspended */
997191824Sthompsa	if (udev->flags.self_suspended) {
998186730Salfred		/* need to wait until the child signals resume */
999186730Salfred		DPRINTF("Device is suspended!\n");
1000186730Salfred		return (0);
1001186730Salfred	}
1002229097Shselasky
1003229097Shselasky	/*
1004229097Shselasky	 * Make sure we don't race against user-space applications
1005229097Shselasky	 * like LibUSB:
1006229097Shselasky	 */
1007247090Shselasky	do_unlock = usbd_enum_lock(udev);
1008229097Shselasky
1009184610Salfred	for (x = 0; x != hub->nports; x++) {
1010184610Salfred		up = hub->ports + x;
1011184610Salfred		portno = x + 1;
1012184610Salfred
1013184610Salfred		err = uhub_read_port_status(sc, portno);
1014184610Salfred		if (err) {
1015184610Salfred			/* most likely the HUB is gone */
1016184610Salfred			break;
1017184610Salfred		}
1018186730Salfred		if (sc->sc_st.port_change & UPS_C_OVERCURRENT_INDICATOR) {
1019186730Salfred			DPRINTF("Overcurrent on port %u.\n", portno);
1020194228Sthompsa			err = usbd_req_clear_port_feature(
1021188986Sthompsa			    udev, NULL, portno, UHF_C_PORT_OVER_CURRENT);
1022186730Salfred			if (err) {
1023186730Salfred				/* most likely the HUB is gone */
1024186730Salfred				break;
1025186730Salfred			}
1026186730Salfred		}
1027185087Salfred		if (!(sc->sc_flags & UHUB_FLAG_DID_EXPLORE)) {
1028185087Salfred			/*
1029185087Salfred			 * Fake a connect status change so that the
1030185087Salfred			 * status gets checked initially!
1031185087Salfred			 */
1032185087Salfred			sc->sc_st.port_change |=
1033185087Salfred			    UPS_C_CONNECT_STATUS;
1034185087Salfred		}
1035184610Salfred		if (sc->sc_st.port_change & UPS_C_PORT_ENABLED) {
1036194228Sthompsa			err = usbd_req_clear_port_feature(
1037188986Sthompsa			    udev, NULL, portno, UHF_C_PORT_ENABLE);
1038184610Salfred			if (err) {
1039184610Salfred				/* most likely the HUB is gone */
1040184610Salfred				break;
1041184610Salfred			}
1042184610Salfred			if (sc->sc_st.port_change & UPS_C_CONNECT_STATUS) {
1043184610Salfred				/*
1044184610Salfred				 * Ignore the port error if the device
1045184610Salfred				 * has vanished !
1046184610Salfred				 */
1047184610Salfred			} else if (sc->sc_st.port_status & UPS_PORT_ENABLED) {
1048184610Salfred				DPRINTFN(0, "illegal enable change, "
1049184610Salfred				    "port %d\n", portno);
1050184610Salfred			} else {
1051184610Salfred
1052184610Salfred				if (up->restartcnt == USB_RESTART_MAX) {
1053184610Salfred					/* XXX could try another speed ? */
1054184610Salfred					DPRINTFN(0, "port error, giving up "
1055184610Salfred					    "port %d\n", portno);
1056184610Salfred				} else {
1057185087Salfred					sc->sc_st.port_change |=
1058185087Salfred					    UPS_C_CONNECT_STATUS;
1059184610Salfred					up->restartcnt++;
1060184610Salfred				}
1061184610Salfred			}
1062184610Salfred		}
1063184610Salfred		if (sc->sc_st.port_change & UPS_C_CONNECT_STATUS) {
1064184610Salfred			err = uhub_reattach_port(sc, portno);
1065184610Salfred			if (err) {
1066184610Salfred				/* most likely the HUB is gone */
1067184610Salfred				break;
1068184610Salfred			}
1069184610Salfred		}
1070230302Shselasky		if (sc->sc_st.port_change & (UPS_C_SUSPEND |
1071230302Shselasky		    UPS_C_PORT_LINK_STATE)) {
1072184610Salfred			err = uhub_suspend_resume_port(sc, portno);
1073184610Salfred			if (err) {
1074184610Salfred				/* most likely the HUB is gone */
1075184610Salfred				break;
1076184610Salfred			}
1077184610Salfred		}
1078184610Salfred		err = uhub_explore_sub(sc, up);
1079184610Salfred		if (err) {
1080184610Salfred			/* no device(s) present */
1081184610Salfred			continue;
1082184610Salfred		}
1083184610Salfred		/* explore succeeded - reset restart counter */
1084184610Salfred		up->restartcnt = 0;
1085184610Salfred	}
1086185087Salfred
1087247090Shselasky	if (do_unlock)
1088247090Shselasky		usbd_enum_unlock(udev);
1089229097Shselasky
1090185087Salfred	/* initial status checked */
1091185087Salfred	sc->sc_flags |= UHUB_FLAG_DID_EXPLORE;
1092185087Salfred
1093185087Salfred	/* return success */
1094184610Salfred	return (USB_ERR_NORMAL_COMPLETION);
1095184610Salfred}
1096184610Salfred
1097184610Salfredstatic int
1098184610Salfreduhub_probe(device_t dev)
1099184610Salfred{
1100192984Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(dev);
1101184610Salfred
1102213435Shselasky	if (uaa->usb_mode != USB_MODE_HOST)
1103184610Salfred		return (ENXIO);
1104213435Shselasky
1105184610Salfred	/*
1106213435Shselasky	 * The subclass for USB HUBs is currently ignored because it
1107213435Shselasky	 * is 0 for some and 1 for others.
1108184610Salfred	 */
1109213435Shselasky	if (uaa->info.bConfigIndex == 0 &&
1110213435Shselasky	    uaa->info.bDeviceClass == UDCLASS_HUB)
1111184610Salfred		return (0);
1112213435Shselasky
1113184610Salfred	return (ENXIO);
1114184610Salfred}
1115184610Salfred
1116213435Shselasky/* NOTE: The information returned by this function can be wrong. */
1117213435Shselaskyusb_error_t
1118213435Shselaskyuhub_query_info(struct usb_device *udev, uint8_t *pnports, uint8_t *ptt)
1119213435Shselasky{
1120213435Shselasky	struct usb_hub_descriptor hubdesc20;
1121213435Shselasky	struct usb_hub_ss_descriptor hubdesc30;
1122213435Shselasky	usb_error_t err;
1123213435Shselasky	uint8_t nports;
1124213435Shselasky	uint8_t tt;
1125213435Shselasky
1126213435Shselasky	if (udev->ddesc.bDeviceClass != UDCLASS_HUB)
1127213435Shselasky		return (USB_ERR_INVAL);
1128213435Shselasky
1129213435Shselasky	nports = 0;
1130213435Shselasky	tt = 0;
1131213435Shselasky
1132213435Shselasky	switch (udev->speed) {
1133213435Shselasky	case USB_SPEED_LOW:
1134213435Shselasky	case USB_SPEED_FULL:
1135213435Shselasky	case USB_SPEED_HIGH:
1136213435Shselasky		/* assuming that there is one port */
1137213435Shselasky		err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, 1);
1138213435Shselasky		if (err) {
1139213435Shselasky			DPRINTFN(0, "getting USB 2.0 HUB descriptor failed,"
1140213435Shselasky			    "error=%s\n", usbd_errstr(err));
1141213435Shselasky			break;
1142213435Shselasky		}
1143213435Shselasky		nports = hubdesc20.bNbrPorts;
1144213435Shselasky		if (nports > 127)
1145213435Shselasky			nports = 127;
1146213435Shselasky
1147213435Shselasky		if (udev->speed == USB_SPEED_HIGH)
1148213435Shselasky			tt = (UGETW(hubdesc20.wHubCharacteristics) >> 5) & 3;
1149213435Shselasky		break;
1150213435Shselasky
1151213435Shselasky	case USB_SPEED_SUPER:
1152213435Shselasky		err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, 1);
1153213435Shselasky		if (err) {
1154213435Shselasky			DPRINTFN(0, "Getting USB 3.0 HUB descriptor failed,"
1155213435Shselasky			    "error=%s\n", usbd_errstr(err));
1156213435Shselasky			break;
1157213435Shselasky		}
1158213435Shselasky		nports = hubdesc30.bNbrPorts;
1159213435Shselasky		if (nports > 16)
1160213435Shselasky			nports = 16;
1161213435Shselasky		break;
1162213435Shselasky
1163213435Shselasky	default:
1164213435Shselasky		err = USB_ERR_INVAL;
1165213435Shselasky		break;
1166213435Shselasky	}
1167213435Shselasky
1168213435Shselasky	if (pnports != NULL)
1169213435Shselasky		*pnports = nports;
1170213435Shselasky
1171213435Shselasky	if (ptt != NULL)
1172213435Shselasky		*ptt = tt;
1173213435Shselasky
1174213435Shselasky	return (err);
1175213435Shselasky}
1176213435Shselasky
1177184610Salfredstatic int
1178184610Salfreduhub_attach(device_t dev)
1179184610Salfred{
1180184610Salfred	struct uhub_softc *sc = device_get_softc(dev);
1181192984Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(dev);
1182192984Sthompsa	struct usb_device *udev = uaa->device;
1183192984Sthompsa	struct usb_device *parent_hub = udev->parent_hub;
1184192984Sthompsa	struct usb_hub *hub;
1185213435Shselasky	struct usb_hub_descriptor hubdesc20;
1186213435Shselasky	struct usb_hub_ss_descriptor hubdesc30;
1187184610Salfred	uint16_t pwrdly;
1188184610Salfred	uint8_t x;
1189184610Salfred	uint8_t nports;
1190184610Salfred	uint8_t portno;
1191184610Salfred	uint8_t removable;
1192184610Salfred	uint8_t iface_index;
1193193045Sthompsa	usb_error_t err;
1194184610Salfred
1195184610Salfred	sc->sc_udev = udev;
1196184610Salfred	sc->sc_dev = dev;
1197184610Salfred
1198196498Salfred	mtx_init(&sc->sc_mtx, "USB HUB mutex", NULL, MTX_DEF);
1199196498Salfred
1200184610Salfred	snprintf(sc->sc_name, sizeof(sc->sc_name), "%s",
1201184610Salfred	    device_get_nameunit(dev));
1202184610Salfred
1203194228Sthompsa	device_set_usb_desc(dev);
1204184610Salfred
1205184610Salfred	DPRINTFN(2, "depth=%d selfpowered=%d, parent=%p, "
1206184610Salfred	    "parent->selfpowered=%d\n",
1207184610Salfred	    udev->depth,
1208184610Salfred	    udev->flags.self_powered,
1209184610Salfred	    parent_hub,
1210184610Salfred	    parent_hub ?
1211184610Salfred	    parent_hub->flags.self_powered : 0);
1212184610Salfred
1213213435Shselasky	if (uhub_is_too_deep(udev)) {
1214213435Shselasky		DPRINTFN(0, "HUB at depth %d, "
1215213435Shselasky		    "exceeds maximum. HUB ignored\n", (int)udev->depth);
1216184610Salfred		goto error;
1217184610Salfred	}
1218213435Shselasky
1219184610Salfred	if (!udev->flags.self_powered && parent_hub &&
1220213435Shselasky	    !parent_hub->flags.self_powered) {
1221213435Shselasky		DPRINTFN(0, "Bus powered HUB connected to "
1222199816Sthompsa		    "bus powered HUB. HUB ignored\n");
1223184610Salfred		goto error;
1224184610Salfred	}
1225235001Shselasky
1226235001Shselasky	if (UHUB_IS_MULTI_TT(sc)) {
1227235001Shselasky		err = usbd_set_alt_interface_index(udev, 0, 1);
1228235001Shselasky		if (err) {
1229235001Shselasky			device_printf(dev, "MTT could not be enabled\n");
1230235001Shselasky			goto error;
1231235001Shselasky		}
1232235001Shselasky		device_printf(dev, "MTT enabled\n");
1233235001Shselasky	}
1234235001Shselasky
1235184610Salfred	/* get HUB descriptor */
1236184610Salfred
1237213435Shselasky	DPRINTFN(2, "Getting HUB descriptor\n");
1238184610Salfred
1239213435Shselasky	switch (udev->speed) {
1240213435Shselasky	case USB_SPEED_LOW:
1241213435Shselasky	case USB_SPEED_FULL:
1242213435Shselasky	case USB_SPEED_HIGH:
1243213435Shselasky		/* assuming that there is one port */
1244213435Shselasky		err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, 1);
1245213435Shselasky		if (err) {
1246213435Shselasky			DPRINTFN(0, "getting USB 2.0 HUB descriptor failed,"
1247213435Shselasky			    "error=%s\n", usbd_errstr(err));
1248213435Shselasky			goto error;
1249213435Shselasky		}
1250213435Shselasky		/* get number of ports */
1251213435Shselasky		nports = hubdesc20.bNbrPorts;
1252184610Salfred
1253213435Shselasky		/* get power delay */
1254213435Shselasky		pwrdly = ((hubdesc20.bPwrOn2PwrGood * UHD_PWRON_FACTOR) +
1255242775Shselasky		    usb_extra_power_up_time);
1256184610Salfred
1257184610Salfred		/* get complete HUB descriptor */
1258213435Shselasky		if (nports >= 8) {
1259213435Shselasky			/* check number of ports */
1260213435Shselasky			if (nports > 127) {
1261213435Shselasky				DPRINTFN(0, "Invalid number of USB 2.0 ports,"
1262213435Shselasky				    "error=%s\n", usbd_errstr(err));
1263213435Shselasky				goto error;
1264213435Shselasky			}
1265213435Shselasky			/* get complete HUB descriptor */
1266213435Shselasky			err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, nports);
1267213435Shselasky
1268213435Shselasky			if (err) {
1269213435Shselasky				DPRINTFN(0, "Getting USB 2.0 HUB descriptor failed,"
1270213435Shselasky				    "error=%s\n", usbd_errstr(err));
1271213435Shselasky				goto error;
1272213435Shselasky			}
1273213435Shselasky			if (hubdesc20.bNbrPorts != nports) {
1274213435Shselasky				DPRINTFN(0, "Number of ports changed\n");
1275213435Shselasky				goto error;
1276213435Shselasky			}
1277213435Shselasky		}
1278213435Shselasky		break;
1279213435Shselasky	case USB_SPEED_SUPER:
1280213435Shselasky		if (udev->parent_hub != NULL) {
1281213435Shselasky			err = usbd_req_set_hub_depth(udev, NULL,
1282213435Shselasky			    udev->depth - 1);
1283213435Shselasky			if (err) {
1284213435Shselasky				DPRINTFN(0, "Setting USB 3.0 HUB depth failed,"
1285213435Shselasky				    "error=%s\n", usbd_errstr(err));
1286213435Shselasky				goto error;
1287213435Shselasky			}
1288213435Shselasky		}
1289213435Shselasky		err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, 1);
1290213435Shselasky		if (err) {
1291213435Shselasky			DPRINTFN(0, "Getting USB 3.0 HUB descriptor failed,"
1292213435Shselasky			    "error=%s\n", usbd_errstr(err));
1293213435Shselasky			goto error;
1294213435Shselasky		}
1295213435Shselasky		/* get number of ports */
1296213435Shselasky		nports = hubdesc30.bNbrPorts;
1297213435Shselasky
1298213435Shselasky		/* get power delay */
1299213435Shselasky		pwrdly = ((hubdesc30.bPwrOn2PwrGood * UHD_PWRON_FACTOR) +
1300242775Shselasky		    usb_extra_power_up_time);
1301213435Shselasky
1302213435Shselasky		/* get complete HUB descriptor */
1303213435Shselasky		if (nports >= 8) {
1304213435Shselasky			/* check number of ports */
1305213435Shselasky			if (nports > ((udev->parent_hub != NULL) ? 15 : 127)) {
1306213435Shselasky				DPRINTFN(0, "Invalid number of USB 3.0 ports,"
1307213435Shselasky				    "error=%s\n", usbd_errstr(err));
1308213435Shselasky				goto error;
1309213435Shselasky			}
1310213435Shselasky			/* get complete HUB descriptor */
1311213435Shselasky			err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, nports);
1312213435Shselasky
1313213435Shselasky			if (err) {
1314213435Shselasky				DPRINTFN(0, "Getting USB 2.0 HUB descriptor failed,"
1315213435Shselasky				    "error=%s\n", usbd_errstr(err));
1316213435Shselasky				goto error;
1317213435Shselasky			}
1318213435Shselasky			if (hubdesc30.bNbrPorts != nports) {
1319213435Shselasky				DPRINTFN(0, "Number of ports changed\n");
1320213435Shselasky				goto error;
1321213435Shselasky			}
1322213435Shselasky		}
1323213435Shselasky		break;
1324213435Shselasky	default:
1325213435Shselasky		DPRINTF("Assuming HUB has only one port\n");
1326213435Shselasky		/* default number of ports */
1327213435Shselasky		nports = 1;
1328213435Shselasky		/* default power delay */
1329242775Shselasky		pwrdly = ((10 * UHD_PWRON_FACTOR) + usb_extra_power_up_time);
1330213435Shselasky		break;
1331184610Salfred	}
1332184610Salfred	if (nports == 0) {
1333199816Sthompsa		DPRINTFN(0, "portless HUB\n");
1334184610Salfred		goto error;
1335184610Salfred	}
1336184610Salfred	hub = malloc(sizeof(hub[0]) + (sizeof(hub->ports[0]) * nports),
1337184610Salfred	    M_USBDEV, M_WAITOK | M_ZERO);
1338184610Salfred
1339184610Salfred	if (hub == NULL) {
1340184610Salfred		goto error;
1341184610Salfred	}
1342184610Salfred	udev->hub = hub;
1343184610Salfred
1344184610Salfred	/* initialize HUB structure */
1345184610Salfred	hub->hubsoftc = sc;
1346184610Salfred	hub->explore = &uhub_explore;
1347213435Shselasky	hub->nports = nports;
1348184610Salfred	hub->hubudev = udev;
1349261106Shselasky#if USB_HAVE_TT_SUPPORT
1350261106Shselasky	hub->tt_msg[0].hdr.pm_callback = &uhub_reset_tt_proc;
1351261106Shselasky	hub->tt_msg[0].udev = udev;
1352261106Shselasky	hub->tt_msg[1].hdr.pm_callback = &uhub_reset_tt_proc;
1353261106Shselasky	hub->tt_msg[1].udev = udev;
1354261106Shselasky#endif
1355184610Salfred	/* if self powered hub, give ports maximum current */
1356184610Salfred	if (udev->flags.self_powered) {
1357184610Salfred		hub->portpower = USB_MAX_POWER;
1358184610Salfred	} else {
1359184610Salfred		hub->portpower = USB_MIN_POWER;
1360184610Salfred	}
1361184610Salfred
1362184610Salfred	/* set up interrupt pipe */
1363184610Salfred	iface_index = 0;
1364190735Sthompsa	if (udev->parent_hub == NULL) {
1365190735Sthompsa		/* root HUB is special */
1366190735Sthompsa		err = 0;
1367190735Sthompsa	} else {
1368190735Sthompsa		/* normal HUB */
1369194228Sthompsa		err = usbd_transfer_setup(udev, &iface_index, sc->sc_xfer,
1370196498Salfred		    uhub_config, UHUB_N_TRANSFER, sc, &sc->sc_mtx);
1371190735Sthompsa	}
1372184610Salfred	if (err) {
1373184610Salfred		DPRINTFN(0, "cannot setup interrupt transfer, "
1374199816Sthompsa		    "errstr=%s\n", usbd_errstr(err));
1375184610Salfred		goto error;
1376184610Salfred	}
1377184610Salfred	/* wait with power off for a while */
1378194228Sthompsa	usb_pause_mtx(NULL, USB_MS_TO_TICKS(USB_POWER_DOWN_TIME));
1379184610Salfred
1380184610Salfred	/*
1381184610Salfred	 * To have the best chance of success we do things in the exact same
1382184610Salfred	 * order as Windoze98.  This should not be necessary, but some
1383184610Salfred	 * devices do not follow the USB specs to the letter.
1384184610Salfred	 *
1385184610Salfred	 * These are the events on the bus when a hub is attached:
1386184610Salfred	 *  Get device and config descriptors (see attach code)
1387184610Salfred	 *  Get hub descriptor (see above)
1388184610Salfred	 *  For all ports
1389184610Salfred	 *     turn on power
1390184610Salfred	 *     wait for power to become stable
1391184610Salfred	 * (all below happens in explore code)
1392184610Salfred	 *  For all ports
1393184610Salfred	 *     clear C_PORT_CONNECTION
1394184610Salfred	 *  For all ports
1395184610Salfred	 *     get port status
1396184610Salfred	 *     if device connected
1397184610Salfred	 *        wait 100 ms
1398184610Salfred	 *        turn on reset
1399184610Salfred	 *        wait
1400184610Salfred	 *        clear C_PORT_RESET
1401184610Salfred	 *        get port status
1402184610Salfred	 *        proceed with device attachment
1403184610Salfred	 */
1404184610Salfred
1405184610Salfred	/* XXX should check for none, individual, or ganged power? */
1406184610Salfred
1407184610Salfred	removable = 0;
1408184610Salfred
1409184610Salfred	for (x = 0; x != nports; x++) {
1410184610Salfred		/* set up data structures */
1411192984Sthompsa		struct usb_port *up = hub->ports + x;
1412184610Salfred
1413184610Salfred		up->device_index = 0;
1414184610Salfred		up->restartcnt = 0;
1415184610Salfred		portno = x + 1;
1416184610Salfred
1417184610Salfred		/* check if port is removable */
1418213435Shselasky		switch (udev->speed) {
1419213435Shselasky		case USB_SPEED_LOW:
1420213435Shselasky		case USB_SPEED_FULL:
1421213435Shselasky		case USB_SPEED_HIGH:
1422213435Shselasky			if (!UHD_NOT_REMOV(&hubdesc20, portno))
1423213435Shselasky				removable++;
1424213435Shselasky			break;
1425213435Shselasky		case USB_SPEED_SUPER:
1426213435Shselasky			if (!UHD_NOT_REMOV(&hubdesc30, portno))
1427213435Shselasky				removable++;
1428213435Shselasky			break;
1429213435Shselasky		default:
1430213435Shselasky			DPRINTF("Assuming removable port\n");
1431184610Salfred			removable++;
1432213435Shselasky			break;
1433184610Salfred		}
1434184610Salfred		if (!err) {
1435184610Salfred			/* turn the power on */
1436194228Sthompsa			err = usbd_req_set_port_feature(udev, NULL,
1437186730Salfred			    portno, UHF_PORT_POWER);
1438184610Salfred		}
1439184610Salfred		if (err) {
1440184610Salfred			DPRINTFN(0, "port %d power on failed, %s\n",
1441194228Sthompsa			    portno, usbd_errstr(err));
1442184610Salfred		}
1443184610Salfred		DPRINTF("turn on port %d power\n",
1444184610Salfred		    portno);
1445184610Salfred
1446184610Salfred		/* wait for stable power */
1447194228Sthompsa		usb_pause_mtx(NULL, USB_MS_TO_TICKS(pwrdly));
1448184610Salfred	}
1449184610Salfred
1450184610Salfred	device_printf(dev, "%d port%s with %d "
1451184610Salfred	    "removable, %s powered\n", nports, (nports != 1) ? "s" : "",
1452184610Salfred	    removable, udev->flags.self_powered ? "self" : "bus");
1453184610Salfred
1454190735Sthompsa	/* Start the interrupt endpoint, if any */
1455184610Salfred
1456261106Shselasky	mtx_lock(&sc->sc_mtx);
1457261106Shselasky	usbd_transfer_start(sc->sc_xfer[UHUB_INTR_TRANSFER]);
1458261106Shselasky	mtx_unlock(&sc->sc_mtx);
1459184610Salfred
1460186730Salfred	/* Enable automatic power save on all USB HUBs */
1461186730Salfred
1462194228Sthompsa	usbd_set_power_mode(udev, USB_POWER_MODE_SAVE);
1463186730Salfred
1464184610Salfred	return (0);
1465184610Salfred
1466184610Salfrederror:
1467194228Sthompsa	usbd_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER);
1468184610Salfred
1469184610Salfred	if (udev->hub) {
1470184610Salfred		free(udev->hub, M_USBDEV);
1471184610Salfred		udev->hub = NULL;
1472184610Salfred	}
1473196498Salfred
1474196498Salfred	mtx_destroy(&sc->sc_mtx);
1475196498Salfred
1476184610Salfred	return (ENXIO);
1477184610Salfred}
1478184610Salfred
1479184610Salfred/*
1480184610Salfred * Called from process context when the hub is gone.
1481184610Salfred * Detach all devices on active ports.
1482184610Salfred */
1483184610Salfredstatic int
1484184610Salfreduhub_detach(device_t dev)
1485184610Salfred{
1486184610Salfred	struct uhub_softc *sc = device_get_softc(dev);
1487192984Sthompsa	struct usb_hub *hub = sc->sc_udev->hub;
1488261106Shselasky	struct usb_bus *bus = sc->sc_udev->bus;
1489192984Sthompsa	struct usb_device *child;
1490184610Salfred	uint8_t x;
1491184610Salfred
1492213435Shselasky	if (hub == NULL)		/* must be partially working */
1493184610Salfred		return (0);
1494197553Sthompsa
1495197553Sthompsa	/* Make sure interrupt transfer is gone. */
1496197553Sthompsa	usbd_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER);
1497197553Sthompsa
1498197553Sthompsa	/* Detach all ports */
1499184610Salfred	for (x = 0; x != hub->nports; x++) {
1500184610Salfred
1501261106Shselasky		child = usb_bus_port_get_device(bus, hub->ports + x);
1502184610Salfred
1503184610Salfred		if (child == NULL) {
1504184610Salfred			continue;
1505184610Salfred		}
1506197553Sthompsa
1507184610Salfred		/*
1508197553Sthompsa		 * Free USB device and all subdevices, if any.
1509184610Salfred		 */
1510197553Sthompsa		usb_free_device(child, 0);
1511184610Salfred	}
1512184610Salfred
1513261106Shselasky#if USB_HAVE_TT_SUPPORT
1514261106Shselasky	/* Make sure our TT messages are not queued anywhere */
1515261106Shselasky	USB_BUS_LOCK(bus);
1516261106Shselasky	usb_proc_mwait(&bus->non_giant_callback_proc,
1517261106Shselasky	    &hub->tt_msg[0], &hub->tt_msg[1]);
1518261106Shselasky	USB_BUS_UNLOCK(bus);
1519261106Shselasky#endif
1520184610Salfred	free(hub, M_USBDEV);
1521184610Salfred	sc->sc_udev->hub = NULL;
1522196498Salfred
1523196498Salfred	mtx_destroy(&sc->sc_mtx);
1524196498Salfred
1525184610Salfred	return (0);
1526184610Salfred}
1527184610Salfred
1528186730Salfredstatic int
1529186730Salfreduhub_suspend(device_t dev)
1530186730Salfred{
1531186730Salfred	DPRINTF("\n");
1532186730Salfred	/* Sub-devices are not suspended here! */
1533186730Salfred	return (0);
1534186730Salfred}
1535186730Salfred
1536186730Salfredstatic int
1537186730Salfreduhub_resume(device_t dev)
1538186730Salfred{
1539186730Salfred	DPRINTF("\n");
1540186730Salfred	/* Sub-devices are not resumed here! */
1541186730Salfred	return (0);
1542186730Salfred}
1543186730Salfred
1544184610Salfredstatic void
1545184610Salfreduhub_driver_added(device_t dev, driver_t *driver)
1546184610Salfred{
1547194228Sthompsa	usb_needs_explore_all();
1548184610Salfred}
1549184610Salfred
1550184610Salfredstruct hub_result {
1551192984Sthompsa	struct usb_device *udev;
1552184610Salfred	uint8_t	portno;
1553184610Salfred	uint8_t	iface_index;
1554184610Salfred};
1555184610Salfred
1556184610Salfredstatic void
1557192984Sthompsauhub_find_iface_index(struct usb_hub *hub, device_t child,
1558184610Salfred    struct hub_result *res)
1559184610Salfred{
1560192984Sthompsa	struct usb_interface *iface;
1561192984Sthompsa	struct usb_device *udev;
1562184610Salfred	uint8_t nports;
1563184610Salfred	uint8_t x;
1564184610Salfred	uint8_t i;
1565184610Salfred
1566184610Salfred	nports = hub->nports;
1567184610Salfred	for (x = 0; x != nports; x++) {
1568194228Sthompsa		udev = usb_bus_port_get_device(hub->hubudev->bus,
1569184610Salfred		    hub->ports + x);
1570184610Salfred		if (!udev) {
1571184610Salfred			continue;
1572184610Salfred		}
1573184610Salfred		for (i = 0; i != USB_IFACE_MAX; i++) {
1574194228Sthompsa			iface = usbd_get_iface(udev, i);
1575184610Salfred			if (iface &&
1576184610Salfred			    (iface->subdev == child)) {
1577184610Salfred				res->iface_index = i;
1578184610Salfred				res->udev = udev;
1579184610Salfred				res->portno = x + 1;
1580184610Salfred				return;
1581184610Salfred			}
1582184610Salfred		}
1583184610Salfred	}
1584184610Salfred	res->iface_index = 0;
1585184610Salfred	res->udev = NULL;
1586184610Salfred	res->portno = 0;
1587184610Salfred}
1588184610Salfred
1589184610Salfredstatic int
1590184610Salfreduhub_child_location_string(device_t parent, device_t child,
1591184610Salfred    char *buf, size_t buflen)
1592184610Salfred{
1593197553Sthompsa	struct uhub_softc *sc;
1594197553Sthompsa	struct usb_hub *hub;
1595184610Salfred	struct hub_result res;
1596184610Salfred
1597197553Sthompsa	if (!device_is_attached(parent)) {
1598197553Sthompsa		if (buflen)
1599197553Sthompsa			buf[0] = 0;
1600197553Sthompsa		return (0);
1601197553Sthompsa	}
1602197553Sthompsa
1603197553Sthompsa	sc = device_get_softc(parent);
1604197553Sthompsa	hub = sc->sc_udev->hub;
1605197553Sthompsa
1606196403Sjhb	mtx_lock(&Giant);
1607184610Salfred	uhub_find_iface_index(hub, child, &res);
1608184610Salfred	if (!res.udev) {
1609184610Salfred		DPRINTF("device not on hub\n");
1610184610Salfred		if (buflen) {
1611184610Salfred			buf[0] = '\0';
1612184610Salfred		}
1613184610Salfred		goto done;
1614184610Salfred	}
1615208010Sthompsa	snprintf(buf, buflen, "bus=%u hubaddr=%u port=%u devaddr=%u interface=%u",
1616208010Sthompsa	    (res.udev->parent_hub != NULL) ? res.udev->parent_hub->device_index : 0,
1617208010Sthompsa	    res.portno, device_get_unit(res.udev->bus->bdev),
1618208010Sthompsa	    res.udev->device_index, res.iface_index);
1619184610Salfreddone:
1620196403Sjhb	mtx_unlock(&Giant);
1621184610Salfred
1622184610Salfred	return (0);
1623184610Salfred}
1624184610Salfred
1625184610Salfredstatic int
1626184610Salfreduhub_child_pnpinfo_string(device_t parent, device_t child,
1627184610Salfred    char *buf, size_t buflen)
1628184610Salfred{
1629197553Sthompsa	struct uhub_softc *sc;
1630197553Sthompsa	struct usb_hub *hub;
1631192984Sthompsa	struct usb_interface *iface;
1632184610Salfred	struct hub_result res;
1633184610Salfred
1634197553Sthompsa	if (!device_is_attached(parent)) {
1635197553Sthompsa		if (buflen)
1636197553Sthompsa			buf[0] = 0;
1637197553Sthompsa		return (0);
1638197553Sthompsa	}
1639197553Sthompsa
1640197553Sthompsa	sc = device_get_softc(parent);
1641197553Sthompsa	hub = sc->sc_udev->hub;
1642197553Sthompsa
1643196403Sjhb	mtx_lock(&Giant);
1644184610Salfred	uhub_find_iface_index(hub, child, &res);
1645184610Salfred	if (!res.udev) {
1646184610Salfred		DPRINTF("device not on hub\n");
1647184610Salfred		if (buflen) {
1648184610Salfred			buf[0] = '\0';
1649184610Salfred		}
1650184610Salfred		goto done;
1651184610Salfred	}
1652194228Sthompsa	iface = usbd_get_iface(res.udev, res.iface_index);
1653184610Salfred	if (iface && iface->idesc) {
1654184610Salfred		snprintf(buf, buflen, "vendor=0x%04x product=0x%04x "
1655184610Salfred		    "devclass=0x%02x devsubclass=0x%02x "
1656184610Salfred		    "sernum=\"%s\" "
1657195963Salfred		    "release=0x%04x "
1658223489Shselasky		    "mode=%s "
1659223489Shselasky		    "intclass=0x%02x intsubclass=0x%02x "
1660223489Shselasky		    "intprotocol=0x%02x " "%s%s",
1661184610Salfred		    UGETW(res.udev->ddesc.idVendor),
1662184610Salfred		    UGETW(res.udev->ddesc.idProduct),
1663184610Salfred		    res.udev->ddesc.bDeviceClass,
1664184610Salfred		    res.udev->ddesc.bDeviceSubClass,
1665212136Sthompsa		    usb_get_serial(res.udev),
1666195963Salfred		    UGETW(res.udev->ddesc.bcdDevice),
1667223489Shselasky		    (res.udev->flags.usb_mode == USB_MODE_HOST) ? "host" : "device",
1668184610Salfred		    iface->idesc->bInterfaceClass,
1669214429Shselasky		    iface->idesc->bInterfaceSubClass,
1670223489Shselasky		    iface->idesc->bInterfaceProtocol,
1671214429Shselasky		    iface->pnpinfo ? " " : "",
1672214429Shselasky		    iface->pnpinfo ? iface->pnpinfo : "");
1673184610Salfred	} else {
1674184610Salfred		if (buflen) {
1675184610Salfred			buf[0] = '\0';
1676184610Salfred		}
1677184610Salfred		goto done;
1678184610Salfred	}
1679184610Salfreddone:
1680196403Sjhb	mtx_unlock(&Giant);
1681184610Salfred
1682184610Salfred	return (0);
1683184610Salfred}
1684184610Salfred
1685184610Salfred/*
1686184610Salfred * The USB Transaction Translator:
1687184610Salfred * ===============================
1688184610Salfred *
1689184610Salfred * When doing LOW- and FULL-speed USB transfers accross a HIGH-speed
1690184610Salfred * USB HUB, bandwidth must be allocated for ISOCHRONOUS and INTERRUPT
1691184610Salfred * USB transfers. To utilize bandwidth dynamically the "scatter and
1692184610Salfred * gather" principle must be applied. This means that bandwidth must
1693184610Salfred * be divided into equal parts of bandwidth. With regard to USB all
1694184610Salfred * data is transferred in smaller packets with length
1695184610Salfred * "wMaxPacketSize". The problem however is that "wMaxPacketSize" is
1696184610Salfred * not a constant!
1697184610Salfred *
1698184610Salfred * The bandwidth scheduler which I have implemented will simply pack
1699184610Salfred * the USB transfers back to back until there is no more space in the
1700184610Salfred * schedule. Out of the 8 microframes which the USB 2.0 standard
1701184610Salfred * provides, only 6 are available for non-HIGH-speed devices. I have
1702184610Salfred * reserved the first 4 microframes for ISOCHRONOUS transfers. The
1703184610Salfred * last 2 microframes I have reserved for INTERRUPT transfers. Without
1704184610Salfred * this division, it is very difficult to allocate and free bandwidth
1705184610Salfred * dynamically.
1706184610Salfred *
1707184610Salfred * NOTE about the Transaction Translator in USB HUBs:
1708184610Salfred *
1709184610Salfred * USB HUBs have a very simple Transaction Translator, that will
1710184610Salfred * simply pipeline all the SPLIT transactions. That means that the
1711184610Salfred * transactions will be executed in the order they are queued!
1712184610Salfred *
1713184610Salfred */
1714184610Salfred
1715184610Salfred/*------------------------------------------------------------------------*
1716194228Sthompsa *	usb_intr_find_best_slot
1717184610Salfred *
1718184610Salfred * Return value:
1719184610Salfred *   The best Transaction Translation slot for an interrupt endpoint.
1720184610Salfred *------------------------------------------------------------------------*/
1721184610Salfredstatic uint8_t
1722199672Sthompsausb_intr_find_best_slot(usb_size_t *ptr, uint8_t start,
1723199672Sthompsa    uint8_t end, uint8_t mask)
1724184610Salfred{
1725235000Shselasky	usb_size_t min = (usb_size_t)-1;
1726199672Sthompsa	usb_size_t sum;
1727184610Salfred	uint8_t x;
1728184610Salfred	uint8_t y;
1729199672Sthompsa	uint8_t z;
1730184610Salfred
1731184610Salfred	y = 0;
1732184610Salfred
1733184610Salfred	/* find the last slot with lesser used bandwidth */
1734184610Salfred
1735184610Salfred	for (x = start; x < end; x++) {
1736199672Sthompsa
1737199672Sthompsa		sum = 0;
1738199672Sthompsa
1739199672Sthompsa		/* compute sum of bandwidth */
1740199672Sthompsa		for (z = x; z < end; z++) {
1741199672Sthompsa			if (mask & (1U << (z - x)))
1742199672Sthompsa				sum += ptr[z];
1743199672Sthompsa		}
1744199672Sthompsa
1745199672Sthompsa		/* check if the current multi-slot is more optimal */
1746199672Sthompsa		if (min >= sum) {
1747199672Sthompsa			min = sum;
1748184610Salfred			y = x;
1749184610Salfred		}
1750199672Sthompsa
1751199672Sthompsa		/* check if the mask is about to be shifted out */
1752199672Sthompsa		if (mask & (1U << (end - 1 - x)))
1753199672Sthompsa			break;
1754184610Salfred	}
1755184610Salfred	return (y);
1756184610Salfred}
1757184610Salfred
1758184610Salfred/*------------------------------------------------------------------------*
1759199672Sthompsa *	usb_hs_bandwidth_adjust
1760184610Salfred *
1761184610Salfred * This function will update the bandwith usage for the microframe
1762184610Salfred * having index "slot" by "len" bytes. "len" can be negative.  If the
1763184610Salfred * "slot" argument is greater or equal to "USB_HS_MICRO_FRAMES_MAX"
1764184610Salfred * the "slot" argument will be replaced by the slot having least used
1765199672Sthompsa * bandwidth. The "mask" argument is used for multi-slot allocations.
1766184610Salfred *
1767184610Salfred * Returns:
1768199672Sthompsa *    The slot in which the bandwidth update was done: 0..7
1769184610Salfred *------------------------------------------------------------------------*/
1770199672Sthompsastatic uint8_t
1771199672Sthompsausb_hs_bandwidth_adjust(struct usb_device *udev, int16_t len,
1772199672Sthompsa    uint8_t slot, uint8_t mask)
1773184610Salfred{
1774192984Sthompsa	struct usb_bus *bus = udev->bus;
1775192984Sthompsa	struct usb_hub *hub;
1776192500Sthompsa	enum usb_dev_speed speed;
1777199672Sthompsa	uint8_t x;
1778184610Salfred
1779184824Sthompsa	USB_BUS_LOCK_ASSERT(bus, MA_OWNED);
1780184610Salfred
1781194228Sthompsa	speed = usbd_get_speed(udev);
1782187180Sthompsa
1783187180Sthompsa	switch (speed) {
1784187180Sthompsa	case USB_SPEED_LOW:
1785187180Sthompsa	case USB_SPEED_FULL:
1786187180Sthompsa		if (speed == USB_SPEED_LOW) {
1787184610Salfred			len *= 8;
1788184610Salfred		}
1789184610Salfred		/*
1790184610Salfred	         * The Host Controller Driver should have
1791184610Salfred	         * performed checks so that the lookup
1792184610Salfred	         * below does not result in a NULL pointer
1793184610Salfred	         * access.
1794184610Salfred	         */
1795184610Salfred
1796191395Sthompsa		hub = udev->parent_hs_hub->hub;
1797184610Salfred		if (slot >= USB_HS_MICRO_FRAMES_MAX) {
1798194228Sthompsa			slot = usb_intr_find_best_slot(hub->uframe_usage,
1799199672Sthompsa			    USB_FS_ISOC_UFRAME_MAX, 6, mask);
1800184610Salfred		}
1801199672Sthompsa		for (x = slot; x < 8; x++) {
1802199672Sthompsa			if (mask & (1U << (x - slot))) {
1803199672Sthompsa				hub->uframe_usage[x] += len;
1804199672Sthompsa				bus->uframe_usage[x] += len;
1805199672Sthompsa			}
1806199672Sthompsa		}
1807187180Sthompsa		break;
1808187180Sthompsa	default:
1809187180Sthompsa		if (slot >= USB_HS_MICRO_FRAMES_MAX) {
1810194228Sthompsa			slot = usb_intr_find_best_slot(bus->uframe_usage, 0,
1811199672Sthompsa			    USB_HS_MICRO_FRAMES_MAX, mask);
1812187180Sthompsa		}
1813199672Sthompsa		for (x = slot; x < 8; x++) {
1814199672Sthompsa			if (mask & (1U << (x - slot))) {
1815199672Sthompsa				bus->uframe_usage[x] += len;
1816199672Sthompsa			}
1817199672Sthompsa		}
1818187180Sthompsa		break;
1819184610Salfred	}
1820184610Salfred	return (slot);
1821184610Salfred}
1822184610Salfred
1823184610Salfred/*------------------------------------------------------------------------*
1824199672Sthompsa *	usb_hs_bandwidth_alloc
1825199672Sthompsa *
1826199672Sthompsa * This function is a wrapper function for "usb_hs_bandwidth_adjust()".
1827199672Sthompsa *------------------------------------------------------------------------*/
1828199672Sthompsavoid
1829199672Sthompsausb_hs_bandwidth_alloc(struct usb_xfer *xfer)
1830199672Sthompsa{
1831199672Sthompsa	struct usb_device *udev;
1832199672Sthompsa	uint8_t slot;
1833199672Sthompsa	uint8_t mask;
1834199672Sthompsa	uint8_t speed;
1835199672Sthompsa
1836199672Sthompsa	udev = xfer->xroot->udev;
1837199672Sthompsa
1838199672Sthompsa	if (udev->flags.usb_mode != USB_MODE_HOST)
1839199672Sthompsa		return;		/* not supported */
1840199672Sthompsa
1841199672Sthompsa	xfer->endpoint->refcount_bw++;
1842199672Sthompsa	if (xfer->endpoint->refcount_bw != 1)
1843199672Sthompsa		return;		/* already allocated */
1844199672Sthompsa
1845199672Sthompsa	speed = usbd_get_speed(udev);
1846199672Sthompsa
1847199672Sthompsa	switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) {
1848199672Sthompsa	case UE_INTERRUPT:
1849199672Sthompsa		/* allocate a microframe slot */
1850199672Sthompsa
1851199672Sthompsa		mask = 0x01;
1852199672Sthompsa		slot = usb_hs_bandwidth_adjust(udev,
1853199672Sthompsa		    xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX, mask);
1854199672Sthompsa
1855199672Sthompsa		xfer->endpoint->usb_uframe = slot;
1856199672Sthompsa		xfer->endpoint->usb_smask = mask << slot;
1857199672Sthompsa
1858199672Sthompsa		if ((speed != USB_SPEED_FULL) &&
1859199672Sthompsa		    (speed != USB_SPEED_LOW)) {
1860199672Sthompsa			xfer->endpoint->usb_cmask = 0x00 ;
1861199672Sthompsa		} else {
1862199672Sthompsa			xfer->endpoint->usb_cmask = (-(0x04 << slot)) & 0xFE;
1863199672Sthompsa		}
1864199672Sthompsa		break;
1865199672Sthompsa
1866199672Sthompsa	case UE_ISOCHRONOUS:
1867199672Sthompsa		switch (usbd_xfer_get_fps_shift(xfer)) {
1868199672Sthompsa		case 0:
1869199672Sthompsa			mask = 0xFF;
1870199672Sthompsa			break;
1871199672Sthompsa		case 1:
1872199672Sthompsa			mask = 0x55;
1873199672Sthompsa			break;
1874199672Sthompsa		case 2:
1875199672Sthompsa			mask = 0x11;
1876199672Sthompsa			break;
1877199672Sthompsa		default:
1878199672Sthompsa			mask = 0x01;
1879199672Sthompsa			break;
1880199672Sthompsa		}
1881199672Sthompsa
1882199672Sthompsa		/* allocate a microframe multi-slot */
1883199672Sthompsa
1884199672Sthompsa		slot = usb_hs_bandwidth_adjust(udev,
1885199672Sthompsa		    xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX, mask);
1886199672Sthompsa
1887199672Sthompsa		xfer->endpoint->usb_uframe = slot;
1888199672Sthompsa		xfer->endpoint->usb_cmask = 0;
1889199672Sthompsa		xfer->endpoint->usb_smask = mask << slot;
1890199672Sthompsa		break;
1891199672Sthompsa
1892199672Sthompsa	default:
1893199672Sthompsa		xfer->endpoint->usb_uframe = 0;
1894199672Sthompsa		xfer->endpoint->usb_cmask = 0;
1895199672Sthompsa		xfer->endpoint->usb_smask = 0;
1896199672Sthompsa		break;
1897199672Sthompsa	}
1898199672Sthompsa
1899199672Sthompsa	DPRINTFN(11, "slot=%d, mask=0x%02x\n",
1900199672Sthompsa	    xfer->endpoint->usb_uframe,
1901199672Sthompsa	    xfer->endpoint->usb_smask >> xfer->endpoint->usb_uframe);
1902199672Sthompsa}
1903199672Sthompsa
1904199672Sthompsa/*------------------------------------------------------------------------*
1905199672Sthompsa *	usb_hs_bandwidth_free
1906199672Sthompsa *
1907199672Sthompsa * This function is a wrapper function for "usb_hs_bandwidth_adjust()".
1908199672Sthompsa *------------------------------------------------------------------------*/
1909199672Sthompsavoid
1910199672Sthompsausb_hs_bandwidth_free(struct usb_xfer *xfer)
1911199672Sthompsa{
1912199672Sthompsa	struct usb_device *udev;
1913199672Sthompsa	uint8_t slot;
1914199672Sthompsa	uint8_t mask;
1915199672Sthompsa
1916199672Sthompsa	udev = xfer->xroot->udev;
1917199672Sthompsa
1918199672Sthompsa	if (udev->flags.usb_mode != USB_MODE_HOST)
1919199672Sthompsa		return;		/* not supported */
1920199672Sthompsa
1921199672Sthompsa	xfer->endpoint->refcount_bw--;
1922199672Sthompsa	if (xfer->endpoint->refcount_bw != 0)
1923199672Sthompsa		return;		/* still allocated */
1924199672Sthompsa
1925199672Sthompsa	switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) {
1926199672Sthompsa	case UE_INTERRUPT:
1927199672Sthompsa	case UE_ISOCHRONOUS:
1928199672Sthompsa
1929199672Sthompsa		slot = xfer->endpoint->usb_uframe;
1930199672Sthompsa		mask = xfer->endpoint->usb_smask;
1931199672Sthompsa
1932199672Sthompsa		/* free microframe slot(s): */
1933199672Sthompsa		usb_hs_bandwidth_adjust(udev,
1934199672Sthompsa		    -xfer->max_frame_size, slot, mask >> slot);
1935199672Sthompsa
1936199672Sthompsa		DPRINTFN(11, "slot=%d, mask=0x%02x\n",
1937199672Sthompsa		    slot, mask >> slot);
1938199672Sthompsa
1939199672Sthompsa		xfer->endpoint->usb_uframe = 0;
1940199672Sthompsa		xfer->endpoint->usb_cmask = 0;
1941199672Sthompsa		xfer->endpoint->usb_smask = 0;
1942199672Sthompsa		break;
1943199672Sthompsa
1944199672Sthompsa	default:
1945199672Sthompsa		break;
1946199672Sthompsa	}
1947199672Sthompsa}
1948199672Sthompsa
1949199672Sthompsa/*------------------------------------------------------------------------*
1950194228Sthompsa *	usb_isoc_time_expand
1951184610Salfred *
1952184610Salfred * This function will expand the time counter from 7-bit to 16-bit.
1953184610Salfred *
1954184610Salfred * Returns:
1955184610Salfred *   16-bit isochronous time counter.
1956184610Salfred *------------------------------------------------------------------------*/
1957184610Salfreduint16_t
1958194228Sthompsausb_isoc_time_expand(struct usb_bus *bus, uint16_t isoc_time_curr)
1959184610Salfred{
1960184610Salfred	uint16_t rem;
1961184610Salfred
1962184824Sthompsa	USB_BUS_LOCK_ASSERT(bus, MA_OWNED);
1963184610Salfred
1964184610Salfred	rem = bus->isoc_time_last & (USB_ISOC_TIME_MAX - 1);
1965184610Salfred
1966184610Salfred	isoc_time_curr &= (USB_ISOC_TIME_MAX - 1);
1967184610Salfred
1968184610Salfred	if (isoc_time_curr < rem) {
1969184610Salfred		/* the time counter wrapped around */
1970184610Salfred		bus->isoc_time_last += USB_ISOC_TIME_MAX;
1971184610Salfred	}
1972184610Salfred	/* update the remainder */
1973184610Salfred
1974184610Salfred	bus->isoc_time_last &= ~(USB_ISOC_TIME_MAX - 1);
1975184610Salfred	bus->isoc_time_last |= isoc_time_curr;
1976184610Salfred
1977184610Salfred	return (bus->isoc_time_last);
1978184610Salfred}
1979184610Salfred
1980184610Salfred/*------------------------------------------------------------------------*
1981235001Shselasky *	usbd_fs_isoc_schedule_alloc_slot
1982184610Salfred *
1983235001Shselasky * This function will allocate bandwidth for an isochronous FULL speed
1984235001Shselasky * transaction in the FULL speed schedule.
1985184610Salfred *
1986184610Salfred * Returns:
1987235001Shselasky *    <8: Success
1988235001Shselasky * Else: Error
1989184610Salfred *------------------------------------------------------------------------*/
1990190734Sthompsa#if USB_HAVE_TT_SUPPORT
1991235001Shselaskyuint8_t
1992235001Shselaskyusbd_fs_isoc_schedule_alloc_slot(struct usb_xfer *isoc_xfer, uint16_t isoc_time)
1993184610Salfred{
1994235001Shselasky	struct usb_xfer *xfer;
1995235001Shselasky	struct usb_xfer *pipe_xfer;
1996235001Shselasky	struct usb_bus *bus;
1997235001Shselasky	usb_frlength_t len;
1998235001Shselasky	usb_frlength_t data_len;
1999235001Shselasky	uint16_t delta;
2000235001Shselasky	uint16_t slot;
2001235001Shselasky	uint8_t retval;
2002184610Salfred
2003235001Shselasky	data_len = 0;
2004235001Shselasky	slot = 0;
2005184610Salfred
2006235001Shselasky	bus = isoc_xfer->xroot->bus;
2007184610Salfred
2008235001Shselasky	TAILQ_FOREACH(xfer, &bus->intr_q.head, wait_entry) {
2009184610Salfred
2010235001Shselasky		/* skip self, if any */
2011184610Salfred
2012235001Shselasky		if (xfer == isoc_xfer)
2013235001Shselasky			continue;
2014184610Salfred
2015235001Shselasky		/* check if this USB transfer is going through the same TT */
2016184610Salfred
2017235001Shselasky		if (xfer->xroot->udev->parent_hs_hub !=
2018235001Shselasky		    isoc_xfer->xroot->udev->parent_hs_hub) {
2019235001Shselasky			continue;
2020235001Shselasky		}
2021235001Shselasky		if ((isoc_xfer->xroot->udev->parent_hs_hub->
2022235001Shselasky		    ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) &&
2023235001Shselasky		    (xfer->xroot->udev->hs_port_no !=
2024235001Shselasky		    isoc_xfer->xroot->udev->hs_port_no)) {
2025235001Shselasky			continue;
2026235001Shselasky		}
2027235001Shselasky		if (xfer->endpoint->methods != isoc_xfer->endpoint->methods)
2028235001Shselasky			continue;
2029184610Salfred
2030235001Shselasky		/* check if isoc_time is part of this transfer */
2031184610Salfred
2032235001Shselasky		delta = xfer->isoc_time_complete - isoc_time;
2033235001Shselasky		if (delta > 0 && delta <= xfer->nframes) {
2034235001Shselasky			delta = xfer->nframes - delta;
2035235001Shselasky
2036235001Shselasky			len = xfer->frlengths[delta];
2037235001Shselasky			len += 8;
2038235001Shselasky			len *= 7;
2039235001Shselasky			len /= 6;
2040235001Shselasky
2041235001Shselasky			data_len += len;
2042235001Shselasky		}
2043235001Shselasky
2044235001Shselasky		/* check double buffered transfers */
2045235001Shselasky
2046235001Shselasky		TAILQ_FOREACH(pipe_xfer, &xfer->endpoint->endpoint_q.head,
2047235001Shselasky		    wait_entry) {
2048235001Shselasky
2049235001Shselasky			/* skip self, if any */
2050235001Shselasky
2051235001Shselasky			if (pipe_xfer == isoc_xfer)
2052184610Salfred				continue;
2053235001Shselasky
2054235001Shselasky			/* check if isoc_time is part of this transfer */
2055235001Shselasky
2056235001Shselasky			delta = pipe_xfer->isoc_time_complete - isoc_time;
2057235001Shselasky			if (delta > 0 && delta <= pipe_xfer->nframes) {
2058235001Shselasky				delta = pipe_xfer->nframes - delta;
2059235001Shselasky
2060235001Shselasky				len = pipe_xfer->frlengths[delta];
2061235001Shselasky				len += 8;
2062235001Shselasky				len *= 7;
2063235001Shselasky				len /= 6;
2064235001Shselasky
2065235001Shselasky				data_len += len;
2066184610Salfred			}
2067184610Salfred		}
2068235001Shselasky	}
2069184610Salfred
2070235001Shselasky	while (data_len >= USB_FS_BYTES_PER_HS_UFRAME) {
2071235001Shselasky		data_len -= USB_FS_BYTES_PER_HS_UFRAME;
2072235001Shselasky		slot++;
2073184610Salfred	}
2074184610Salfred
2075235001Shselasky	/* check for overflow */
2076184610Salfred
2077235001Shselasky	if (slot >= USB_FS_ISOC_UFRAME_MAX)
2078235001Shselasky		return (255);
2079184610Salfred
2080235001Shselasky	retval = slot;
2081184610Salfred
2082235001Shselasky	delta = isoc_xfer->isoc_time_complete - isoc_time;
2083235001Shselasky	if (delta > 0 && delta <= isoc_xfer->nframes) {
2084235001Shselasky		delta = isoc_xfer->nframes - delta;
2085184610Salfred
2086235001Shselasky		len = isoc_xfer->frlengths[delta];
2087235001Shselasky		len += 8;
2088235001Shselasky		len *= 7;
2089235001Shselasky		len /= 6;
2090235001Shselasky
2091235001Shselasky		data_len += len;
2092184610Salfred	}
2093184610Salfred
2094235001Shselasky	while (data_len >= USB_FS_BYTES_PER_HS_UFRAME) {
2095235001Shselasky		data_len -= USB_FS_BYTES_PER_HS_UFRAME;
2096235001Shselasky		slot++;
2097235001Shselasky	}
2098184610Salfred
2099235001Shselasky	/* check for overflow */
2100184610Salfred
2101235001Shselasky	if (slot >= USB_FS_ISOC_UFRAME_MAX)
2102235001Shselasky		return (255);
2103235001Shselasky
2104235001Shselasky	return (retval);
2105184610Salfred}
2106190734Sthompsa#endif
2107184610Salfred
2108184610Salfred/*------------------------------------------------------------------------*
2109194228Sthompsa *	usb_bus_port_get_device
2110184610Salfred *
2111184610Salfred * This function is NULL safe.
2112184610Salfred *------------------------------------------------------------------------*/
2113192984Sthompsastruct usb_device *
2114194228Sthompsausb_bus_port_get_device(struct usb_bus *bus, struct usb_port *up)
2115184610Salfred{
2116184610Salfred	if ((bus == NULL) || (up == NULL)) {
2117184610Salfred		/* be NULL safe */
2118184610Salfred		return (NULL);
2119184610Salfred	}
2120184610Salfred	if (up->device_index == 0) {
2121184610Salfred		/* nothing to do */
2122184610Salfred		return (NULL);
2123184610Salfred	}
2124184610Salfred	return (bus->devices[up->device_index]);
2125184610Salfred}
2126184610Salfred
2127184610Salfred/*------------------------------------------------------------------------*
2128194228Sthompsa *	usb_bus_port_set_device
2129184610Salfred *
2130184610Salfred * This function is NULL safe.
2131184610Salfred *------------------------------------------------------------------------*/
2132184610Salfredvoid
2133194228Sthompsausb_bus_port_set_device(struct usb_bus *bus, struct usb_port *up,
2134192984Sthompsa    struct usb_device *udev, uint8_t device_index)
2135184610Salfred{
2136184610Salfred	if (bus == NULL) {
2137184610Salfred		/* be NULL safe */
2138184610Salfred		return;
2139184610Salfred	}
2140184610Salfred	/*
2141184610Salfred	 * There is only one case where we don't
2142184610Salfred	 * have an USB port, and that is the Root Hub!
2143184610Salfred         */
2144184610Salfred	if (up) {
2145184610Salfred		if (udev) {
2146184610Salfred			up->device_index = device_index;
2147184610Salfred		} else {
2148184610Salfred			device_index = up->device_index;
2149184610Salfred			up->device_index = 0;
2150184610Salfred		}
2151184610Salfred	}
2152184610Salfred	/*
2153184610Salfred	 * Make relationships to our new device
2154184610Salfred	 */
2155184610Salfred	if (device_index != 0) {
2156189599Sthompsa#if USB_HAVE_UGEN
2157194228Sthompsa		mtx_lock(&usb_ref_lock);
2158189599Sthompsa#endif
2159184610Salfred		bus->devices[device_index] = udev;
2160189599Sthompsa#if USB_HAVE_UGEN
2161194228Sthompsa		mtx_unlock(&usb_ref_lock);
2162189599Sthompsa#endif
2163184610Salfred	}
2164184610Salfred	/*
2165184610Salfred	 * Debug print
2166184610Salfred	 */
2167184610Salfred	DPRINTFN(2, "bus %p devices[%u] = %p\n", bus, device_index, udev);
2168184610Salfred}
2169184610Salfred
2170184610Salfred/*------------------------------------------------------------------------*
2171194228Sthompsa *	usb_needs_explore
2172184610Salfred *
2173184610Salfred * This functions is called when the USB event thread needs to run.
2174184610Salfred *------------------------------------------------------------------------*/
2175184610Salfredvoid
2176194228Sthompsausb_needs_explore(struct usb_bus *bus, uint8_t do_probe)
2177184610Salfred{
2178190735Sthompsa	uint8_t do_unlock;
2179190735Sthompsa
2180184610Salfred	DPRINTF("\n");
2181184610Salfred
2182184610Salfred	if (bus == NULL) {
2183184610Salfred		DPRINTF("No bus pointer!\n");
2184184610Salfred		return;
2185184610Salfred	}
2186190735Sthompsa	if ((bus->devices == NULL) ||
2187190735Sthompsa	    (bus->devices[USB_ROOT_HUB_ADDR] == NULL)) {
2188190735Sthompsa		DPRINTF("No root HUB\n");
2189190735Sthompsa		return;
2190190735Sthompsa	}
2191190735Sthompsa	if (mtx_owned(&bus->bus_mtx)) {
2192190735Sthompsa		do_unlock = 0;
2193190735Sthompsa	} else {
2194190735Sthompsa		USB_BUS_LOCK(bus);
2195190735Sthompsa		do_unlock = 1;
2196190735Sthompsa	}
2197184610Salfred	if (do_probe) {
2198184610Salfred		bus->do_probe = 1;
2199184610Salfred	}
2200194228Sthompsa	if (usb_proc_msignal(&bus->explore_proc,
2201184610Salfred	    &bus->explore_msg[0], &bus->explore_msg[1])) {
2202184610Salfred		/* ignore */
2203184610Salfred	}
2204190735Sthompsa	if (do_unlock) {
2205190735Sthompsa		USB_BUS_UNLOCK(bus);
2206190735Sthompsa	}
2207184610Salfred}
2208184610Salfred
2209184610Salfred/*------------------------------------------------------------------------*
2210194228Sthompsa *	usb_needs_explore_all
2211184610Salfred *
2212184610Salfred * This function is called whenever a new driver is loaded and will
2213184610Salfred * cause that all USB busses are re-explored.
2214184610Salfred *------------------------------------------------------------------------*/
2215184610Salfredvoid
2216194228Sthompsausb_needs_explore_all(void)
2217184610Salfred{
2218192984Sthompsa	struct usb_bus *bus;
2219184610Salfred	devclass_t dc;
2220184610Salfred	device_t dev;
2221184610Salfred	int max;
2222184610Salfred
2223184610Salfred	DPRINTFN(3, "\n");
2224184610Salfred
2225194228Sthompsa	dc = usb_devclass_ptr;
2226184610Salfred	if (dc == NULL) {
2227184610Salfred		DPRINTFN(0, "no devclass\n");
2228184610Salfred		return;
2229184610Salfred	}
2230184610Salfred	/*
2231184610Salfred	 * Explore all USB busses in parallell.
2232184610Salfred	 */
2233184610Salfred	max = devclass_get_maxunit(dc);
2234184610Salfred	while (max >= 0) {
2235184610Salfred		dev = devclass_get_device(dc, max);
2236184610Salfred		if (dev) {
2237184610Salfred			bus = device_get_softc(dev);
2238184610Salfred			if (bus) {
2239194228Sthompsa				usb_needs_explore(bus, 1);
2240184610Salfred			}
2241184610Salfred		}
2242184610Salfred		max--;
2243184610Salfred	}
2244184610Salfred}
2245186730Salfred
2246186730Salfred/*------------------------------------------------------------------------*
2247194228Sthompsa *	usb_bus_power_update
2248186730Salfred *
2249186730Salfred * This function will ensure that all USB devices on the given bus are
2250186730Salfred * properly suspended or resumed according to the device transfer
2251186730Salfred * state.
2252186730Salfred *------------------------------------------------------------------------*/
2253190734Sthompsa#if USB_HAVE_POWERD
2254186730Salfredvoid
2255194228Sthompsausb_bus_power_update(struct usb_bus *bus)
2256186730Salfred{
2257194228Sthompsa	usb_needs_explore(bus, 0 /* no probe */ );
2258186730Salfred}
2259190734Sthompsa#endif
2260186730Salfred
2261186730Salfred/*------------------------------------------------------------------------*
2262194228Sthompsa *	usbd_transfer_power_ref
2263186730Salfred *
2264186730Salfred * This function will modify the power save reference counts and
2265186730Salfred * wakeup the USB device associated with the given USB transfer, if
2266186730Salfred * needed.
2267186730Salfred *------------------------------------------------------------------------*/
2268190734Sthompsa#if USB_HAVE_POWERD
2269186730Salfredvoid
2270194228Sthompsausbd_transfer_power_ref(struct usb_xfer *xfer, int val)
2271186730Salfred{
2272193045Sthompsa	static const usb_power_mask_t power_mask[4] = {
2273186730Salfred		[UE_CONTROL] = USB_HW_POWER_CONTROL,
2274186730Salfred		[UE_BULK] = USB_HW_POWER_BULK,
2275186730Salfred		[UE_INTERRUPT] = USB_HW_POWER_INTERRUPT,
2276186730Salfred		[UE_ISOCHRONOUS] = USB_HW_POWER_ISOC,
2277186730Salfred	};
2278192984Sthompsa	struct usb_device *udev;
2279186730Salfred	uint8_t needs_explore;
2280186730Salfred	uint8_t needs_hw_power;
2281186730Salfred	uint8_t xfer_type;
2282186730Salfred
2283187173Sthompsa	udev = xfer->xroot->udev;
2284186730Salfred
2285186730Salfred	if (udev->device_index == USB_ROOT_HUB_ADDR) {
2286186730Salfred		/* no power save for root HUB */
2287186730Salfred		return;
2288186730Salfred	}
2289186730Salfred	USB_BUS_LOCK(udev->bus);
2290186730Salfred
2291193644Sthompsa	xfer_type = xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE;
2292186730Salfred
2293186730Salfred	udev->pwr_save.last_xfer_time = ticks;
2294186730Salfred	udev->pwr_save.type_refs[xfer_type] += val;
2295186730Salfred
2296186730Salfred	if (xfer->flags_int.control_xfr) {
2297186730Salfred		udev->pwr_save.read_refs += val;
2298192499Sthompsa		if (xfer->flags_int.usb_mode == USB_MODE_HOST) {
2299186730Salfred			/*
2300208008Sthompsa			 * It is not allowed to suspend during a
2301208008Sthompsa			 * control transfer:
2302186730Salfred			 */
2303186730Salfred			udev->pwr_save.write_refs += val;
2304186730Salfred		}
2305186730Salfred	} else if (USB_GET_DATA_ISREAD(xfer)) {
2306186730Salfred		udev->pwr_save.read_refs += val;
2307186730Salfred	} else {
2308186730Salfred		udev->pwr_save.write_refs += val;
2309186730Salfred	}
2310186730Salfred
2311208008Sthompsa	if (val > 0) {
2312208008Sthompsa		if (udev->flags.self_suspended)
2313208008Sthompsa			needs_explore = usb_peer_should_wakeup(udev);
2314208008Sthompsa		else
2315208008Sthompsa			needs_explore = 0;
2316186730Salfred
2317208008Sthompsa		if (!(udev->bus->hw_power_state & power_mask[xfer_type])) {
2318208008Sthompsa			DPRINTF("Adding type %u to power state\n", xfer_type);
2319208008Sthompsa			udev->bus->hw_power_state |= power_mask[xfer_type];
2320208008Sthompsa			needs_hw_power = 1;
2321208008Sthompsa		} else {
2322208008Sthompsa			needs_hw_power = 0;
2323208008Sthompsa		}
2324186730Salfred	} else {
2325208008Sthompsa		needs_explore = 0;
2326186730Salfred		needs_hw_power = 0;
2327186730Salfred	}
2328186730Salfred
2329186730Salfred	USB_BUS_UNLOCK(udev->bus);
2330186730Salfred
2331186730Salfred	if (needs_explore) {
2332186730Salfred		DPRINTF("update\n");
2333194228Sthompsa		usb_bus_power_update(udev->bus);
2334186730Salfred	} else if (needs_hw_power) {
2335186730Salfred		DPRINTF("needs power\n");
2336186730Salfred		if (udev->bus->methods->set_hw_power != NULL) {
2337186730Salfred			(udev->bus->methods->set_hw_power) (udev->bus);
2338186730Salfred		}
2339186730Salfred	}
2340186730Salfred}
2341190734Sthompsa#endif
2342186730Salfred
2343186730Salfred/*------------------------------------------------------------------------*
2344208008Sthompsa *	usb_peer_should_wakeup
2345208008Sthompsa *
2346208008Sthompsa * This function returns non-zero if the current device should wake up.
2347208008Sthompsa *------------------------------------------------------------------------*/
2348208008Sthompsastatic uint8_t
2349208008Sthompsausb_peer_should_wakeup(struct usb_device *udev)
2350208008Sthompsa{
2351255693Shselasky	return (((udev->power_mode == USB_POWER_MODE_ON) &&
2352255693Shselasky	    (udev->flags.usb_mode == USB_MODE_HOST)) ||
2353213432Shselasky	    (udev->driver_added_refcount != udev->bus->driver_added_refcount) ||
2354257373Shselasky	    (udev->re_enumerate_wait != USB_RE_ENUM_DONE) ||
2355208008Sthompsa	    (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) ||
2356208008Sthompsa	    (udev->pwr_save.write_refs != 0) ||
2357208008Sthompsa	    ((udev->pwr_save.read_refs != 0) &&
2358208008Sthompsa	    (udev->flags.usb_mode == USB_MODE_HOST) &&
2359208008Sthompsa	    (usb_peer_can_wakeup(udev) == 0)));
2360208008Sthompsa}
2361208008Sthompsa
2362208008Sthompsa/*------------------------------------------------------------------------*
2363194228Sthompsa *	usb_bus_powerd
2364186730Salfred *
2365186730Salfred * This function implements the USB power daemon and is called
2366186730Salfred * regularly from the USB explore thread.
2367186730Salfred *------------------------------------------------------------------------*/
2368190734Sthompsa#if USB_HAVE_POWERD
2369186730Salfredvoid
2370194228Sthompsausb_bus_powerd(struct usb_bus *bus)
2371186730Salfred{
2372192984Sthompsa	struct usb_device *udev;
2373193045Sthompsa	usb_ticks_t temp;
2374193045Sthompsa	usb_ticks_t limit;
2375193045Sthompsa	usb_ticks_t mintime;
2376193074Sthompsa	usb_size_t type_refs[5];
2377186730Salfred	uint8_t x;
2378186730Salfred
2379194228Sthompsa	limit = usb_power_timeout;
2380186730Salfred	if (limit == 0)
2381186730Salfred		limit = hz;
2382186730Salfred	else if (limit > 255)
2383186730Salfred		limit = 255 * hz;
2384186730Salfred	else
2385186730Salfred		limit = limit * hz;
2386186730Salfred
2387186730Salfred	DPRINTF("bus=%p\n", bus);
2388186730Salfred
2389186730Salfred	USB_BUS_LOCK(bus);
2390186730Salfred
2391186730Salfred	/*
2392186730Salfred	 * The root HUB device is never suspended
2393186730Salfred	 * and we simply skip it.
2394186730Salfred	 */
2395187171Sthompsa	for (x = USB_ROOT_HUB_ADDR + 1;
2396187171Sthompsa	    x != bus->devices_max; x++) {
2397186730Salfred
2398186730Salfred		udev = bus->devices[x];
2399186730Salfred		if (udev == NULL)
2400186730Salfred			continue;
2401186730Salfred
2402186730Salfred		temp = ticks - udev->pwr_save.last_xfer_time;
2403186730Salfred
2404208008Sthompsa		if (usb_peer_should_wakeup(udev)) {
2405186730Salfred			/* check if we are suspended */
2406191824Sthompsa			if (udev->flags.self_suspended != 0) {
2407186730Salfred				USB_BUS_UNLOCK(bus);
2408194228Sthompsa				usb_dev_resume_peer(udev);
2409186730Salfred				USB_BUS_LOCK(bus);
2410186730Salfred			}
2411208008Sthompsa		} else if ((temp >= limit) &&
2412208008Sthompsa		    (udev->flags.usb_mode == USB_MODE_HOST) &&
2413208008Sthompsa		    (udev->flags.self_suspended == 0)) {
2414208008Sthompsa			/* try to do suspend */
2415186730Salfred
2416208008Sthompsa			USB_BUS_UNLOCK(bus);
2417208008Sthompsa			usb_dev_suspend_peer(udev);
2418208008Sthompsa			USB_BUS_LOCK(bus);
2419186730Salfred		}
2420186730Salfred	}
2421186730Salfred
2422186730Salfred	/* reset counters */
2423186730Salfred
2424235000Shselasky	mintime = (usb_ticks_t)-1;
2425186730Salfred	type_refs[0] = 0;
2426186730Salfred	type_refs[1] = 0;
2427186730Salfred	type_refs[2] = 0;
2428186730Salfred	type_refs[3] = 0;
2429187730Sthompsa	type_refs[4] = 0;
2430186730Salfred
2431186730Salfred	/* Re-loop all the devices to get the actual state */
2432186730Salfred
2433187171Sthompsa	for (x = USB_ROOT_HUB_ADDR + 1;
2434187171Sthompsa	    x != bus->devices_max; x++) {
2435186730Salfred
2436186730Salfred		udev = bus->devices[x];
2437186730Salfred		if (udev == NULL)
2438186730Salfred			continue;
2439186730Salfred
2440187730Sthompsa		/* we found a non-Root-Hub USB device */
2441187730Sthompsa		type_refs[4] += 1;
2442187730Sthompsa
2443186730Salfred		/* "last_xfer_time" can be updated by a resume */
2444186730Salfred		temp = ticks - udev->pwr_save.last_xfer_time;
2445186730Salfred
2446186730Salfred		/*
2447186730Salfred		 * Compute minimum time since last transfer for the complete
2448186730Salfred		 * bus:
2449186730Salfred		 */
2450186730Salfred		if (temp < mintime)
2451186730Salfred			mintime = temp;
2452186730Salfred
2453191824Sthompsa		if (udev->flags.self_suspended == 0) {
2454186730Salfred			type_refs[0] += udev->pwr_save.type_refs[0];
2455186730Salfred			type_refs[1] += udev->pwr_save.type_refs[1];
2456186730Salfred			type_refs[2] += udev->pwr_save.type_refs[2];
2457186730Salfred			type_refs[3] += udev->pwr_save.type_refs[3];
2458186730Salfred		}
2459186730Salfred	}
2460186730Salfred
2461235000Shselasky	if (mintime >= (usb_ticks_t)(1 * hz)) {
2462186730Salfred		/* recompute power masks */
2463186730Salfred		DPRINTF("Recomputing power masks\n");
2464186730Salfred		bus->hw_power_state = 0;
2465186730Salfred		if (type_refs[UE_CONTROL] != 0)
2466186730Salfred			bus->hw_power_state |= USB_HW_POWER_CONTROL;
2467186730Salfred		if (type_refs[UE_BULK] != 0)
2468186730Salfred			bus->hw_power_state |= USB_HW_POWER_BULK;
2469186730Salfred		if (type_refs[UE_INTERRUPT] != 0)
2470186730Salfred			bus->hw_power_state |= USB_HW_POWER_INTERRUPT;
2471186730Salfred		if (type_refs[UE_ISOCHRONOUS] != 0)
2472186730Salfred			bus->hw_power_state |= USB_HW_POWER_ISOC;
2473187730Sthompsa		if (type_refs[4] != 0)
2474187730Sthompsa			bus->hw_power_state |= USB_HW_POWER_NON_ROOT_HUB;
2475186730Salfred	}
2476186730Salfred	USB_BUS_UNLOCK(bus);
2477186730Salfred
2478186730Salfred	if (bus->methods->set_hw_power != NULL) {
2479186730Salfred		/* always update hardware power! */
2480186730Salfred		(bus->methods->set_hw_power) (bus);
2481186730Salfred	}
2482186730Salfred	return;
2483186730Salfred}
2484190734Sthompsa#endif
2485186730Salfred
2486186730Salfred/*------------------------------------------------------------------------*
2487194228Sthompsa *	usb_dev_resume_peer
2488186730Salfred *
2489186730Salfred * This function will resume an USB peer and do the required USB
2490186730Salfred * signalling to get an USB device out of the suspended state.
2491186730Salfred *------------------------------------------------------------------------*/
2492186730Salfredstatic void
2493194228Sthompsausb_dev_resume_peer(struct usb_device *udev)
2494186730Salfred{
2495192984Sthompsa	struct usb_bus *bus;
2496186730Salfred	int err;
2497186730Salfred
2498186730Salfred	/* be NULL safe */
2499186730Salfred	if (udev == NULL)
2500186730Salfred		return;
2501186730Salfred
2502186730Salfred	/* check if already resumed */
2503191824Sthompsa	if (udev->flags.self_suspended == 0)
2504186730Salfred		return;
2505186730Salfred
2506186730Salfred	/* we need a parent HUB to do resume */
2507186730Salfred	if (udev->parent_hub == NULL)
2508186730Salfred		return;
2509186730Salfred
2510186730Salfred	DPRINTF("udev=%p\n", udev);
2511186730Salfred
2512192499Sthompsa	if ((udev->flags.usb_mode == USB_MODE_DEVICE) &&
2513186730Salfred	    (udev->flags.remote_wakeup == 0)) {
2514186730Salfred		/*
2515186730Salfred		 * If the host did not set the remote wakeup feature, we can
2516186730Salfred		 * not wake it up either!
2517186730Salfred		 */
2518186730Salfred		DPRINTF("remote wakeup is not set!\n");
2519186730Salfred		return;
2520186730Salfred	}
2521186730Salfred	/* get bus pointer */
2522186730Salfred	bus = udev->bus;
2523186730Salfred
2524186730Salfred	/* resume parent hub first */
2525194228Sthompsa	usb_dev_resume_peer(udev->parent_hub);
2526186730Salfred
2527208008Sthompsa	/* reduce chance of instant resume failure by waiting a little bit */
2528208008Sthompsa	usb_pause_mtx(NULL, USB_MS_TO_TICKS(20));
2529208008Sthompsa
2530213435Shselasky	if (usb_device_20_compatible(udev)) {
2531213435Shselasky		/* resume current port (Valid in Host and Device Mode) */
2532213435Shselasky		err = usbd_req_clear_port_feature(udev->parent_hub,
2533213435Shselasky		    NULL, udev->port_no, UHF_PORT_SUSPEND);
2534213435Shselasky		if (err) {
2535213435Shselasky			DPRINTFN(0, "Resuming port failed\n");
2536213435Shselasky			return;
2537213435Shselasky		}
2538230302Shselasky	} else {
2539230302Shselasky		/* resume current port (Valid in Host and Device Mode) */
2540230302Shselasky		err = usbd_req_set_port_link_state(udev->parent_hub,
2541230302Shselasky		    NULL, udev->port_no, UPS_PORT_LS_U0);
2542230302Shselasky		if (err) {
2543230302Shselasky			DPRINTFN(0, "Resuming port failed\n");
2544230302Shselasky			return;
2545230302Shselasky		}
2546186730Salfred	}
2547213435Shselasky
2548186730Salfred	/* resume settle time */
2549242775Shselasky	usb_pause_mtx(NULL, USB_MS_TO_TICKS(usb_port_resume_delay));
2550186730Salfred
2551186730Salfred	if (bus->methods->device_resume != NULL) {
2552186730Salfred		/* resume USB device on the USB controller */
2553186730Salfred		(bus->methods->device_resume) (udev);
2554186730Salfred	}
2555186730Salfred	USB_BUS_LOCK(bus);
2556186730Salfred	/* set that this device is now resumed */
2557191824Sthompsa	udev->flags.self_suspended = 0;
2558190734Sthompsa#if USB_HAVE_POWERD
2559186730Salfred	/* make sure that we don't go into suspend right away */
2560186730Salfred	udev->pwr_save.last_xfer_time = ticks;
2561186730Salfred
2562186730Salfred	/* make sure the needed power masks are on */
2563186730Salfred	if (udev->pwr_save.type_refs[UE_CONTROL] != 0)
2564186730Salfred		bus->hw_power_state |= USB_HW_POWER_CONTROL;
2565186730Salfred	if (udev->pwr_save.type_refs[UE_BULK] != 0)
2566186730Salfred		bus->hw_power_state |= USB_HW_POWER_BULK;
2567186730Salfred	if (udev->pwr_save.type_refs[UE_INTERRUPT] != 0)
2568186730Salfred		bus->hw_power_state |= USB_HW_POWER_INTERRUPT;
2569186730Salfred	if (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0)
2570186730Salfred		bus->hw_power_state |= USB_HW_POWER_ISOC;
2571190734Sthompsa#endif
2572186730Salfred	USB_BUS_UNLOCK(bus);
2573186730Salfred
2574186730Salfred	if (bus->methods->set_hw_power != NULL) {
2575186730Salfred		/* always update hardware power! */
2576186730Salfred		(bus->methods->set_hw_power) (bus);
2577186730Salfred	}
2578196498Salfred
2579208008Sthompsa	usbd_sr_lock(udev);
2580196498Salfred
2581186730Salfred	/* notify all sub-devices about resume */
2582194228Sthompsa	err = usb_suspend_resume(udev, 0);
2583186730Salfred
2584208008Sthompsa	usbd_sr_unlock(udev);
2585196498Salfred
2586186730Salfred	/* check if peer has wakeup capability */
2587230302Shselasky	if (usb_peer_can_wakeup(udev)) {
2588186730Salfred		/* clear remote wakeup */
2589194228Sthompsa		err = usbd_req_clear_device_feature(udev,
2590188986Sthompsa		    NULL, UF_DEVICE_REMOTE_WAKEUP);
2591186730Salfred		if (err) {
2592186730Salfred			DPRINTFN(0, "Clearing device "
2593199816Sthompsa			    "remote wakeup failed: %s\n",
2594194228Sthompsa			    usbd_errstr(err));
2595186730Salfred		}
2596186730Salfred	}
2597186730Salfred}
2598186730Salfred
2599186730Salfred/*------------------------------------------------------------------------*
2600194228Sthompsa *	usb_dev_suspend_peer
2601186730Salfred *
2602186730Salfred * This function will suspend an USB peer and do the required USB
2603186730Salfred * signalling to get an USB device into the suspended state.
2604186730Salfred *------------------------------------------------------------------------*/
2605186730Salfredstatic void
2606194228Sthompsausb_dev_suspend_peer(struct usb_device *udev)
2607186730Salfred{
2608192984Sthompsa	struct usb_device *child;
2609186730Salfred	int err;
2610186730Salfred	uint8_t x;
2611186730Salfred	uint8_t nports;
2612186730Salfred
2613186730Salfredrepeat:
2614186730Salfred	/* be NULL safe */
2615186730Salfred	if (udev == NULL)
2616186730Salfred		return;
2617186730Salfred
2618186730Salfred	/* check if already suspended */
2619191824Sthompsa	if (udev->flags.self_suspended)
2620186730Salfred		return;
2621186730Salfred
2622186730Salfred	/* we need a parent HUB to do suspend */
2623186730Salfred	if (udev->parent_hub == NULL)
2624186730Salfred		return;
2625186730Salfred
2626186730Salfred	DPRINTF("udev=%p\n", udev);
2627186730Salfred
2628191396Sthompsa	/* check if the current device is a HUB */
2629191396Sthompsa	if (udev->hub != NULL) {
2630191396Sthompsa		nports = udev->hub->nports;
2631186730Salfred
2632191396Sthompsa		/* check if all devices on the HUB are suspended */
2633186730Salfred		for (x = 0; x != nports; x++) {
2634194228Sthompsa			child = usb_bus_port_get_device(udev->bus,
2635191396Sthompsa			    udev->hub->ports + x);
2636186730Salfred
2637186730Salfred			if (child == NULL)
2638186730Salfred				continue;
2639186730Salfred
2640191824Sthompsa			if (child->flags.self_suspended)
2641186730Salfred				continue;
2642186730Salfred
2643191396Sthompsa			DPRINTFN(1, "Port %u is busy on the HUB!\n", x + 1);
2644191396Sthompsa			return;
2645186730Salfred		}
2646186730Salfred	}
2647186730Salfred
2648230302Shselasky	if (usb_peer_can_wakeup(udev)) {
2649213435Shselasky		/*
2650213435Shselasky		 * This request needs to be done before we set
2651213435Shselasky		 * "udev->flags.self_suspended":
2652213435Shselasky		 */
2653213435Shselasky
2654213435Shselasky		/* allow device to do remote wakeup */
2655213435Shselasky		err = usbd_req_set_device_feature(udev,
2656213435Shselasky		    NULL, UF_DEVICE_REMOTE_WAKEUP);
2657213435Shselasky		if (err) {
2658213435Shselasky			DPRINTFN(0, "Setting device "
2659213435Shselasky			    "remote wakeup failed\n");
2660213435Shselasky		}
2661213435Shselasky	}
2662213435Shselasky
2663208008Sthompsa	USB_BUS_LOCK(udev->bus);
2664208008Sthompsa	/*
2665208008Sthompsa	 * Checking for suspend condition and setting suspended bit
2666208008Sthompsa	 * must be atomic!
2667208008Sthompsa	 */
2668208008Sthompsa	err = usb_peer_should_wakeup(udev);
2669208008Sthompsa	if (err == 0) {
2670208008Sthompsa		/*
2671208008Sthompsa		 * Set that this device is suspended. This variable
2672208008Sthompsa		 * must be set before calling USB controller suspend
2673208008Sthompsa		 * callbacks.
2674208008Sthompsa		 */
2675208008Sthompsa		udev->flags.self_suspended = 1;
2676208008Sthompsa	}
2677208008Sthompsa	USB_BUS_UNLOCK(udev->bus);
2678196498Salfred
2679208008Sthompsa	if (err != 0) {
2680230302Shselasky		if (usb_peer_can_wakeup(udev)) {
2681213435Shselasky			/* allow device to do remote wakeup */
2682213435Shselasky			err = usbd_req_clear_device_feature(udev,
2683213435Shselasky			    NULL, UF_DEVICE_REMOTE_WAKEUP);
2684213435Shselasky			if (err) {
2685213435Shselasky				DPRINTFN(0, "Setting device "
2686213435Shselasky				    "remote wakeup failed\n");
2687213435Shselasky			}
2688213435Shselasky		}
2689213435Shselasky
2690208008Sthompsa		if (udev->flags.usb_mode == USB_MODE_DEVICE) {
2691208008Sthompsa			/* resume parent HUB first */
2692208008Sthompsa			usb_dev_resume_peer(udev->parent_hub);
2693208008Sthompsa
2694208008Sthompsa			/* reduce chance of instant resume failure by waiting a little bit */
2695208008Sthompsa			usb_pause_mtx(NULL, USB_MS_TO_TICKS(20));
2696208008Sthompsa
2697208008Sthompsa			/* resume current port (Valid in Host and Device Mode) */
2698208008Sthompsa			err = usbd_req_clear_port_feature(udev->parent_hub,
2699208008Sthompsa			    NULL, udev->port_no, UHF_PORT_SUSPEND);
2700208008Sthompsa
2701208008Sthompsa			/* resume settle time */
2702242775Shselasky			usb_pause_mtx(NULL, USB_MS_TO_TICKS(usb_port_resume_delay));
2703208008Sthompsa		}
2704208008Sthompsa		DPRINTF("Suspend was cancelled!\n");
2705208008Sthompsa		return;
2706208008Sthompsa	}
2707208008Sthompsa
2708208008Sthompsa	usbd_sr_lock(udev);
2709208008Sthompsa
2710186730Salfred	/* notify all sub-devices about suspend */
2711194228Sthompsa	err = usb_suspend_resume(udev, 1);
2712186730Salfred
2713208008Sthompsa	usbd_sr_unlock(udev);
2714196498Salfred
2715186730Salfred	if (udev->bus->methods->device_suspend != NULL) {
2716193045Sthompsa		usb_timeout_t temp;
2717186730Salfred
2718186730Salfred		/* suspend device on the USB controller */
2719186730Salfred		(udev->bus->methods->device_suspend) (udev);
2720186730Salfred
2721186730Salfred		/* do DMA delay */
2722212134Sthompsa		temp = usbd_get_dma_delay(udev);
2723212134Sthompsa		if (temp != 0)
2724212134Sthompsa			usb_pause_mtx(NULL, USB_MS_TO_TICKS(temp));
2725186730Salfred
2726186730Salfred	}
2727213435Shselasky
2728213435Shselasky	if (usb_device_20_compatible(udev)) {
2729213435Shselasky		/* suspend current port */
2730213435Shselasky		err = usbd_req_set_port_feature(udev->parent_hub,
2731213435Shselasky		    NULL, udev->port_no, UHF_PORT_SUSPEND);
2732213435Shselasky		if (err) {
2733213435Shselasky			DPRINTFN(0, "Suspending port failed\n");
2734213435Shselasky			return;
2735213435Shselasky		}
2736230302Shselasky	} else {
2737230302Shselasky		/* suspend current port */
2738230302Shselasky		err = usbd_req_set_port_link_state(udev->parent_hub,
2739230302Shselasky		    NULL, udev->port_no, UPS_PORT_LS_U3);
2740230302Shselasky		if (err) {
2741230302Shselasky			DPRINTFN(0, "Suspending port failed\n");
2742230302Shselasky			return;
2743230302Shselasky		}
2744186730Salfred	}
2745191396Sthompsa
2746191396Sthompsa	udev = udev->parent_hub;
2747191396Sthompsa	goto repeat;
2748186730Salfred}
2749186730Salfred
2750186730Salfred/*------------------------------------------------------------------------*
2751194228Sthompsa *	usbd_set_power_mode
2752186730Salfred *
2753186730Salfred * This function will set the power mode, see USB_POWER_MODE_XXX for a
2754186730Salfred * USB device.
2755186730Salfred *------------------------------------------------------------------------*/
2756186730Salfredvoid
2757194228Sthompsausbd_set_power_mode(struct usb_device *udev, uint8_t power_mode)
2758186730Salfred{
2759186730Salfred	/* filter input argument */
2760187164Sthompsa	if ((power_mode != USB_POWER_MODE_ON) &&
2761212135Sthompsa	    (power_mode != USB_POWER_MODE_OFF))
2762186730Salfred		power_mode = USB_POWER_MODE_SAVE;
2763212135Sthompsa
2764212135Sthompsa	power_mode = usbd_filter_power_mode(udev, power_mode);
2765212135Sthompsa
2766186730Salfred	udev->power_mode = power_mode;	/* update copy of power mode */
2767186730Salfred
2768190734Sthompsa#if USB_HAVE_POWERD
2769194228Sthompsa	usb_bus_power_update(udev->bus);
2770257373Shselasky#else
2771257373Shselasky	usb_needs_explore(udev->bus, 0 /* no probe */ );
2772190734Sthompsa#endif
2773186730Salfred}
2774212135Sthompsa
2775212135Sthompsa/*------------------------------------------------------------------------*
2776212135Sthompsa *	usbd_filter_power_mode
2777212135Sthompsa *
2778212135Sthompsa * This function filters the power mode based on hardware requirements.
2779212135Sthompsa *------------------------------------------------------------------------*/
2780212135Sthompsauint8_t
2781212135Sthompsausbd_filter_power_mode(struct usb_device *udev, uint8_t power_mode)
2782212135Sthompsa{
2783212135Sthompsa	struct usb_bus_methods *mtod;
2784212135Sthompsa	int8_t temp;
2785212135Sthompsa
2786212135Sthompsa	mtod = udev->bus->methods;
2787212135Sthompsa	temp = -1;
2788212135Sthompsa
2789212135Sthompsa	if (mtod->get_power_mode != NULL)
2790212135Sthompsa		(mtod->get_power_mode) (udev, &temp);
2791212135Sthompsa
2792212135Sthompsa	/* check if we should not filter */
2793212135Sthompsa	if (temp < 0)
2794212135Sthompsa		return (power_mode);
2795212135Sthompsa
2796212135Sthompsa	/* use fixed power mode given by hardware driver */
2797212135Sthompsa	return (temp);
2798212135Sthompsa}
2799222786Shselasky
2800222786Shselasky/*------------------------------------------------------------------------*
2801222786Shselasky *	usbd_start_re_enumerate
2802222786Shselasky *
2803222786Shselasky * This function starts re-enumeration of the given USB device. This
2804222786Shselasky * function does not need to be called BUS-locked. This function does
2805222786Shselasky * not wait until the re-enumeration is completed.
2806222786Shselasky *------------------------------------------------------------------------*/
2807222786Shselaskyvoid
2808222786Shselaskyusbd_start_re_enumerate(struct usb_device *udev)
2809222786Shselasky{
2810257373Shselasky	if (udev->re_enumerate_wait == USB_RE_ENUM_DONE) {
2811257373Shselasky		udev->re_enumerate_wait = USB_RE_ENUM_START;
2812222786Shselasky		usb_needs_explore(udev->bus, 0);
2813222786Shselasky	}
2814222786Shselasky}
2815267350Shselasky
2816267350Shselasky/*-----------------------------------------------------------------------*
2817267350Shselasky *	usbd_start_set_config
2818267350Shselasky *
2819267350Shselasky * This function starts setting a USB configuration. This function
2820267350Shselasky * does not need to be called BUS-locked. This function does not wait
2821267350Shselasky * until the set USB configuratino is completed.
2822267350Shselasky *------------------------------------------------------------------------*/
2823267350Shselaskyusb_error_t
2824267350Shselaskyusbd_start_set_config(struct usb_device *udev, uint8_t index)
2825267350Shselasky{
2826267350Shselasky	if (udev->re_enumerate_wait == USB_RE_ENUM_DONE) {
2827267350Shselasky		if (udev->curr_config_index == index) {
2828267350Shselasky			/* no change needed */
2829267350Shselasky			return (0);
2830267350Shselasky		}
2831267350Shselasky		udev->next_config_index = index;
2832267350Shselasky		udev->re_enumerate_wait = USB_RE_ENUM_SET_CONFIG;
2833267350Shselasky		usb_needs_explore(udev->bus, 0);
2834267350Shselasky		return (0);
2835267350Shselasky	} else if (udev->re_enumerate_wait == USB_RE_ENUM_SET_CONFIG) {
2836267350Shselasky		if (udev->next_config_index == index) {
2837267350Shselasky			/* no change needed */
2838267350Shselasky			return (0);
2839267350Shselasky		}
2840267350Shselasky	}
2841267350Shselasky	return (USB_ERR_PENDING_REQUESTS);
2842267350Shselasky}
2843