145f67c02SMarkus Pfeiffer /* $FreeBSD: head/sys/dev/usb/usb_hub.c 276701 2015-01-05 15:04:17Z hselasky $ */
212bd3c8bSSascha Wildner /*-
312bd3c8bSSascha Wildner * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved.
412bd3c8bSSascha Wildner * Copyright (c) 1998 Lennart Augustsson. All rights reserved.
512bd3c8bSSascha Wildner * Copyright (c) 2008-2010 Hans Petter Selasky. All rights reserved.
612bd3c8bSSascha Wildner *
712bd3c8bSSascha Wildner * Redistribution and use in source and binary forms, with or without
812bd3c8bSSascha Wildner * modification, are permitted provided that the following conditions
912bd3c8bSSascha Wildner * are met:
1012bd3c8bSSascha Wildner * 1. Redistributions of source code must retain the above copyright
1112bd3c8bSSascha Wildner * notice, this list of conditions and the following disclaimer.
1212bd3c8bSSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright
1312bd3c8bSSascha Wildner * notice, this list of conditions and the following disclaimer in the
1412bd3c8bSSascha Wildner * documentation and/or other materials provided with the distribution.
1512bd3c8bSSascha Wildner *
1612bd3c8bSSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1712bd3c8bSSascha Wildner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1812bd3c8bSSascha Wildner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1912bd3c8bSSascha Wildner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2012bd3c8bSSascha Wildner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2112bd3c8bSSascha Wildner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2212bd3c8bSSascha Wildner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2312bd3c8bSSascha Wildner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2412bd3c8bSSascha Wildner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2512bd3c8bSSascha Wildner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2612bd3c8bSSascha Wildner * SUCH DAMAGE.
2712bd3c8bSSascha Wildner */
2812bd3c8bSSascha Wildner
2912bd3c8bSSascha Wildner /*
3012bd3c8bSSascha Wildner * USB spec: http://www.usb.org/developers/docs/usbspec.zip
3112bd3c8bSSascha Wildner */
3212bd3c8bSSascha Wildner
3312bd3c8bSSascha Wildner #include <sys/stdint.h>
3412bd3c8bSSascha Wildner #include <sys/param.h>
3512bd3c8bSSascha Wildner #include <sys/queue.h>
3612bd3c8bSSascha Wildner #include <sys/types.h>
3712bd3c8bSSascha Wildner #include <sys/systm.h>
3812bd3c8bSSascha Wildner #include <sys/kernel.h>
3912bd3c8bSSascha Wildner #include <sys/bus.h>
4012bd3c8bSSascha Wildner #include <sys/module.h>
4112bd3c8bSSascha Wildner #include <sys/lock.h>
4212bd3c8bSSascha Wildner #include <sys/condvar.h>
4312bd3c8bSSascha Wildner #include <sys/sysctl.h>
4412bd3c8bSSascha Wildner #include <sys/unistd.h>
4512bd3c8bSSascha Wildner #include <sys/callout.h>
4612bd3c8bSSascha Wildner #include <sys/malloc.h>
47*2b3f93eaSMatthew Dillon #include <sys/caps.h>
4812bd3c8bSSascha Wildner
49722d05c3SSascha Wildner #include <bus/u4b/usb.h>
50722d05c3SSascha Wildner #include <bus/u4b/usb_ioctl.h>
51722d05c3SSascha Wildner #include <bus/u4b/usbdi.h>
52722d05c3SSascha Wildner #include <bus/u4b/usbdi_util.h>
5312bd3c8bSSascha Wildner
5412bd3c8bSSascha Wildner #define USB_DEBUG_VAR uhub_debug
5512bd3c8bSSascha Wildner
56722d05c3SSascha Wildner #include <bus/u4b/usb_core.h>
57722d05c3SSascha Wildner #include <bus/u4b/usb_process.h>
58722d05c3SSascha Wildner #include <bus/u4b/usb_device.h>
59722d05c3SSascha Wildner #include <bus/u4b/usb_request.h>
60722d05c3SSascha Wildner #include <bus/u4b/usb_debug.h>
61722d05c3SSascha Wildner #include <bus/u4b/usb_hub.h>
62722d05c3SSascha Wildner #include <bus/u4b/usb_util.h>
63722d05c3SSascha Wildner #include <bus/u4b/usb_busdma.h>
64722d05c3SSascha Wildner #include <bus/u4b/usb_transfer.h>
65722d05c3SSascha Wildner #include <bus/u4b/usb_dynamic.h>
6612bd3c8bSSascha Wildner
67722d05c3SSascha Wildner #include <bus/u4b/usb_controller.h>
68722d05c3SSascha Wildner #include <bus/u4b/usb_bus.h>
6912bd3c8bSSascha Wildner
7012bd3c8bSSascha Wildner #define UHUB_INTR_INTERVAL 250 /* ms */
7145f67c02SMarkus Pfeiffer enum {
7245f67c02SMarkus Pfeiffer UHUB_INTR_TRANSFER,
7345f67c02SMarkus Pfeiffer #if USB_HAVE_TT_SUPPORT
7445f67c02SMarkus Pfeiffer UHUB_RESET_TT_TRANSFER,
7545f67c02SMarkus Pfeiffer #endif
7645f67c02SMarkus Pfeiffer UHUB_N_TRANSFER,
7745f67c02SMarkus Pfeiffer };
7812bd3c8bSSascha Wildner
7912bd3c8bSSascha Wildner #ifdef USB_DEBUG
8012bd3c8bSSascha Wildner static int uhub_debug = 0;
8112bd3c8bSSascha Wildner
8212bd3c8bSSascha Wildner static SYSCTL_NODE(_hw_usb, OID_AUTO, uhub, CTLFLAG_RW, 0, "USB HUB");
8312bd3c8bSSascha Wildner SYSCTL_INT(_hw_usb_uhub, OID_AUTO, debug, CTLFLAG_RW, &uhub_debug, 0,
8412bd3c8bSSascha Wildner "Debug level");
8512bd3c8bSSascha Wildner
8612bd3c8bSSascha Wildner TUNABLE_INT("hw.usb.uhub.debug", &uhub_debug);
8712bd3c8bSSascha Wildner #endif
8812bd3c8bSSascha Wildner
8912bd3c8bSSascha Wildner #if USB_HAVE_POWERD
9012bd3c8bSSascha Wildner static int usb_power_timeout = 30; /* seconds */
9112bd3c8bSSascha Wildner
9212bd3c8bSSascha Wildner SYSCTL_INT(_hw_usb, OID_AUTO, power_timeout, CTLFLAG_RW,
9312bd3c8bSSascha Wildner &usb_power_timeout, 0, "USB power timeout");
94dd681da6SMatthew Dillon TUNABLE_INT("hw.usb.power_timeout", &usb_power_timeout);
9512bd3c8bSSascha Wildner #endif
9612bd3c8bSSascha Wildner
9745f67c02SMarkus Pfeiffer #if USB_HAVE_DISABLE_ENUM
9845f67c02SMarkus Pfeiffer static int usb_disable_enumeration = 0;
995ea2ce29SSascha Wildner SYSCTL_INT(_hw_usb, OID_AUTO, disable_enumeration, CTLFLAG_RW,
10045f67c02SMarkus Pfeiffer &usb_disable_enumeration, 0, "Set to disable all USB device enumeration.");
10160e94610SSascha Wildner TUNABLE_INT("hw.usb.disable_enumeration", &usb_disable_enumeration);
10245f67c02SMarkus Pfeiffer
10345f67c02SMarkus Pfeiffer static int usb_disable_port_power = 0;
1045ea2ce29SSascha Wildner SYSCTL_INT(_hw_usb, OID_AUTO, disable_port_power, CTLFLAG_RW,
10545f67c02SMarkus Pfeiffer &usb_disable_port_power, 0, "Set to disable all USB port power.");
10660e94610SSascha Wildner TUNABLE_INT("hw.usb.disable_port_power", &usb_disable_port_power);
10745f67c02SMarkus Pfeiffer #endif
10845f67c02SMarkus Pfeiffer
10912bd3c8bSSascha Wildner struct uhub_current_state {
11012bd3c8bSSascha Wildner uint16_t port_change;
11112bd3c8bSSascha Wildner uint16_t port_status;
11212bd3c8bSSascha Wildner };
11312bd3c8bSSascha Wildner
11412bd3c8bSSascha Wildner struct uhub_softc {
11512bd3c8bSSascha Wildner struct uhub_current_state sc_st;/* current state */
11657bed822SMarkus Pfeiffer #if (USB_HAVE_FIXED_PORT != 0)
11757bed822SMarkus Pfeiffer struct usb_hub sc_hub;
11857bed822SMarkus Pfeiffer #endif
11912bd3c8bSSascha Wildner device_t sc_dev; /* base device */
120722d05c3SSascha Wildner struct lock sc_lock; /* our mutex */
12112bd3c8bSSascha Wildner struct usb_device *sc_udev; /* USB device */
12212bd3c8bSSascha Wildner struct usb_xfer *sc_xfer[UHUB_N_TRANSFER]; /* interrupt xfer */
12345f67c02SMarkus Pfeiffer #if USB_HAVE_DISABLE_ENUM
12445f67c02SMarkus Pfeiffer int sc_disable_enumeration;
12545f67c02SMarkus Pfeiffer int sc_disable_port_power;
12645f67c02SMarkus Pfeiffer #endif
12712bd3c8bSSascha Wildner uint8_t sc_flags;
12812bd3c8bSSascha Wildner #define UHUB_FLAG_DID_EXPLORE 0x01
12912bd3c8bSSascha Wildner };
13012bd3c8bSSascha Wildner
13112bd3c8bSSascha Wildner #define UHUB_PROTO(sc) ((sc)->sc_udev->ddesc.bDeviceProtocol)
13212bd3c8bSSascha Wildner #define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB)
13312bd3c8bSSascha Wildner #define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT)
1345e41ab93SMarkus Pfeiffer #define UHUB_IS_MULTI_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBMTT)
13512bd3c8bSSascha Wildner #define UHUB_IS_SUPER_SPEED(sc) (UHUB_PROTO(sc) == UDPROTO_SSHUB)
13612bd3c8bSSascha Wildner
13712bd3c8bSSascha Wildner /* prototypes for type checking: */
13812bd3c8bSSascha Wildner
13912bd3c8bSSascha Wildner static device_probe_t uhub_probe;
14012bd3c8bSSascha Wildner static device_attach_t uhub_attach;
14112bd3c8bSSascha Wildner static device_detach_t uhub_detach;
14212bd3c8bSSascha Wildner static device_suspend_t uhub_suspend;
14312bd3c8bSSascha Wildner static device_resume_t uhub_resume;
14412bd3c8bSSascha Wildner
14512bd3c8bSSascha Wildner static bus_driver_added_t uhub_driver_added;
14612bd3c8bSSascha Wildner static bus_child_location_str_t uhub_child_location_string;
14712bd3c8bSSascha Wildner static bus_child_pnpinfo_str_t uhub_child_pnpinfo_string;
14812bd3c8bSSascha Wildner
14912bd3c8bSSascha Wildner static usb_callback_t uhub_intr_callback;
15045f67c02SMarkus Pfeiffer #if USB_HAVE_TT_SUPPORT
15145f67c02SMarkus Pfeiffer static usb_callback_t uhub_reset_tt_callback;
15245f67c02SMarkus Pfeiffer #endif
15312bd3c8bSSascha Wildner
15412bd3c8bSSascha Wildner static void usb_dev_resume_peer(struct usb_device *udev);
15512bd3c8bSSascha Wildner static void usb_dev_suspend_peer(struct usb_device *udev);
15612bd3c8bSSascha Wildner static uint8_t usb_peer_should_wakeup(struct usb_device *udev);
15712bd3c8bSSascha Wildner
15812bd3c8bSSascha Wildner static const struct usb_config uhub_config[UHUB_N_TRANSFER] = {
15912bd3c8bSSascha Wildner
16045f67c02SMarkus Pfeiffer [UHUB_INTR_TRANSFER] = {
16112bd3c8bSSascha Wildner .type = UE_INTERRUPT,
16212bd3c8bSSascha Wildner .endpoint = UE_ADDR_ANY,
16312bd3c8bSSascha Wildner .direction = UE_DIR_ANY,
16412bd3c8bSSascha Wildner .timeout = 0,
16512bd3c8bSSascha Wildner .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
16612bd3c8bSSascha Wildner .bufsize = 0, /* use wMaxPacketSize */
16712bd3c8bSSascha Wildner .callback = &uhub_intr_callback,
16812bd3c8bSSascha Wildner .interval = UHUB_INTR_INTERVAL,
16912bd3c8bSSascha Wildner },
17045f67c02SMarkus Pfeiffer #if USB_HAVE_TT_SUPPORT
17145f67c02SMarkus Pfeiffer [UHUB_RESET_TT_TRANSFER] = {
17245f67c02SMarkus Pfeiffer .type = UE_CONTROL,
17345f67c02SMarkus Pfeiffer .endpoint = 0x00, /* Control pipe */
17445f67c02SMarkus Pfeiffer .direction = UE_DIR_ANY,
17545f67c02SMarkus Pfeiffer .bufsize = sizeof(struct usb_device_request),
17645f67c02SMarkus Pfeiffer .callback = &uhub_reset_tt_callback,
17745f67c02SMarkus Pfeiffer .timeout = 1000, /* 1 second */
17845f67c02SMarkus Pfeiffer .usb_mode = USB_MODE_HOST,
17945f67c02SMarkus Pfeiffer },
18045f67c02SMarkus Pfeiffer #endif
18112bd3c8bSSascha Wildner };
18212bd3c8bSSascha Wildner
18312bd3c8bSSascha Wildner /*
18412bd3c8bSSascha Wildner * driver instance for "hub" connected to "usb"
18512bd3c8bSSascha Wildner * and "hub" connected to "hub"
18612bd3c8bSSascha Wildner */
18712bd3c8bSSascha Wildner static devclass_t uhub_devclass;
18812bd3c8bSSascha Wildner
18912bd3c8bSSascha Wildner static device_method_t uhub_methods[] = {
19012bd3c8bSSascha Wildner DEVMETHOD(device_probe, uhub_probe),
19112bd3c8bSSascha Wildner DEVMETHOD(device_attach, uhub_attach),
19212bd3c8bSSascha Wildner DEVMETHOD(device_detach, uhub_detach),
19312bd3c8bSSascha Wildner
19412bd3c8bSSascha Wildner DEVMETHOD(device_suspend, uhub_suspend),
19512bd3c8bSSascha Wildner DEVMETHOD(device_resume, uhub_resume),
19612bd3c8bSSascha Wildner
19712bd3c8bSSascha Wildner DEVMETHOD(bus_child_location_str, uhub_child_location_string),
19812bd3c8bSSascha Wildner DEVMETHOD(bus_child_pnpinfo_str, uhub_child_pnpinfo_string),
19912bd3c8bSSascha Wildner DEVMETHOD(bus_driver_added, uhub_driver_added),
200d3c9c58eSSascha Wildner DEVMETHOD_END
20112bd3c8bSSascha Wildner };
20212bd3c8bSSascha Wildner
20312bd3c8bSSascha Wildner static driver_t uhub_driver = {
20412bd3c8bSSascha Wildner .name = "uhub",
20512bd3c8bSSascha Wildner .methods = uhub_methods,
20612bd3c8bSSascha Wildner .size = sizeof(struct uhub_softc)
20712bd3c8bSSascha Wildner };
20812bd3c8bSSascha Wildner
2093a25be87SSascha Wildner DRIVER_MODULE(uhub, usbus, uhub_driver, uhub_devclass, NULL, NULL);
2103a25be87SSascha Wildner DRIVER_MODULE(uhub, uhub, uhub_driver, uhub_devclass, NULL, NULL);
21112bd3c8bSSascha Wildner MODULE_VERSION(uhub, 1);
21212bd3c8bSSascha Wildner
21312bd3c8bSSascha Wildner static void
uhub_intr_callback(struct usb_xfer * xfer,usb_error_t error)21412bd3c8bSSascha Wildner uhub_intr_callback(struct usb_xfer *xfer, usb_error_t error)
21512bd3c8bSSascha Wildner {
21612bd3c8bSSascha Wildner struct uhub_softc *sc = usbd_xfer_softc(xfer);
21712bd3c8bSSascha Wildner
21812bd3c8bSSascha Wildner switch (USB_GET_STATE(xfer)) {
21912bd3c8bSSascha Wildner case USB_ST_TRANSFERRED:
22012bd3c8bSSascha Wildner DPRINTFN(2, "\n");
22112bd3c8bSSascha Wildner /*
22212bd3c8bSSascha Wildner * This is an indication that some port
22312bd3c8bSSascha Wildner * has changed status. Notify the bus
22412bd3c8bSSascha Wildner * event handler thread that we need
22512bd3c8bSSascha Wildner * to be explored again:
22612bd3c8bSSascha Wildner */
22712bd3c8bSSascha Wildner usb_needs_explore(sc->sc_udev->bus, 0);
22812bd3c8bSSascha Wildner
22912bd3c8bSSascha Wildner case USB_ST_SETUP:
23012bd3c8bSSascha Wildner usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
23112bd3c8bSSascha Wildner usbd_transfer_submit(xfer);
23212bd3c8bSSascha Wildner break;
23312bd3c8bSSascha Wildner
23412bd3c8bSSascha Wildner default: /* Error */
23512bd3c8bSSascha Wildner if (xfer->error != USB_ERR_CANCELLED) {
23612bd3c8bSSascha Wildner /*
23712bd3c8bSSascha Wildner * Do a clear-stall. The "stall_pipe" flag
23812bd3c8bSSascha Wildner * will get cleared before next callback by
23912bd3c8bSSascha Wildner * the USB stack.
24012bd3c8bSSascha Wildner */
24112bd3c8bSSascha Wildner usbd_xfer_set_stall(xfer);
24212bd3c8bSSascha Wildner usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
24312bd3c8bSSascha Wildner usbd_transfer_submit(xfer);
24412bd3c8bSSascha Wildner }
24512bd3c8bSSascha Wildner break;
24612bd3c8bSSascha Wildner }
24712bd3c8bSSascha Wildner }
24812bd3c8bSSascha Wildner
24912bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
25045f67c02SMarkus Pfeiffer * uhub_reset_tt_proc
25145f67c02SMarkus Pfeiffer *
25245f67c02SMarkus Pfeiffer * This function starts the TT reset USB request
25345f67c02SMarkus Pfeiffer *------------------------------------------------------------------------*/
25445f67c02SMarkus Pfeiffer #if USB_HAVE_TT_SUPPORT
25545f67c02SMarkus Pfeiffer static void
uhub_reset_tt_proc(struct usb_proc_msg * _pm)25645f67c02SMarkus Pfeiffer uhub_reset_tt_proc(struct usb_proc_msg *_pm)
25745f67c02SMarkus Pfeiffer {
25845f67c02SMarkus Pfeiffer struct usb_udev_msg *pm = (void *)_pm;
25945f67c02SMarkus Pfeiffer struct usb_device *udev = pm->udev;
26045f67c02SMarkus Pfeiffer struct usb_hub *hub;
26145f67c02SMarkus Pfeiffer struct uhub_softc *sc;
26245f67c02SMarkus Pfeiffer
26345f67c02SMarkus Pfeiffer hub = udev->hub;
26445f67c02SMarkus Pfeiffer if (hub == NULL)
26545f67c02SMarkus Pfeiffer return;
26645f67c02SMarkus Pfeiffer sc = hub->hubsoftc;
26745f67c02SMarkus Pfeiffer if (sc == NULL)
26845f67c02SMarkus Pfeiffer return;
26945f67c02SMarkus Pfeiffer
27045f67c02SMarkus Pfeiffer /* Change lock */
27145f67c02SMarkus Pfeiffer USB_BUS_UNLOCK(udev->bus);
27245f67c02SMarkus Pfeiffer lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
27345f67c02SMarkus Pfeiffer /* Start transfer */
27445f67c02SMarkus Pfeiffer usbd_transfer_start(sc->sc_xfer[UHUB_RESET_TT_TRANSFER]);
27545f67c02SMarkus Pfeiffer /* Change lock */
27645f67c02SMarkus Pfeiffer lockmgr(&sc->sc_lock, LK_RELEASE);
27745f67c02SMarkus Pfeiffer USB_BUS_LOCK(udev->bus);
27845f67c02SMarkus Pfeiffer }
27945f67c02SMarkus Pfeiffer #endif
28045f67c02SMarkus Pfeiffer
28145f67c02SMarkus Pfeiffer /*------------------------------------------------------------------------*
28245f67c02SMarkus Pfeiffer * uhub_tt_buffer_reset_async_locked
28345f67c02SMarkus Pfeiffer *
28445f67c02SMarkus Pfeiffer * This function queues a TT reset for the given USB device and endpoint.
28545f67c02SMarkus Pfeiffer *------------------------------------------------------------------------*/
28645f67c02SMarkus Pfeiffer #if USB_HAVE_TT_SUPPORT
28745f67c02SMarkus Pfeiffer void
uhub_tt_buffer_reset_async_locked(struct usb_device * child,struct usb_endpoint * ep)28845f67c02SMarkus Pfeiffer uhub_tt_buffer_reset_async_locked(struct usb_device *child, struct usb_endpoint *ep)
28945f67c02SMarkus Pfeiffer {
29045f67c02SMarkus Pfeiffer struct usb_device_request req;
29145f67c02SMarkus Pfeiffer struct usb_device *udev;
29245f67c02SMarkus Pfeiffer struct usb_hub *hub;
29345f67c02SMarkus Pfeiffer struct usb_port *up;
29445f67c02SMarkus Pfeiffer uint16_t wValue;
29545f67c02SMarkus Pfeiffer uint8_t port;
29645f67c02SMarkus Pfeiffer
29745f67c02SMarkus Pfeiffer if (child == NULL || ep == NULL)
29845f67c02SMarkus Pfeiffer return;
29945f67c02SMarkus Pfeiffer
30045f67c02SMarkus Pfeiffer udev = child->parent_hs_hub;
30145f67c02SMarkus Pfeiffer port = child->hs_port_no;
30245f67c02SMarkus Pfeiffer
30345f67c02SMarkus Pfeiffer if (udev == NULL)
30445f67c02SMarkus Pfeiffer return;
30545f67c02SMarkus Pfeiffer
30645f67c02SMarkus Pfeiffer hub = udev->hub;
30745f67c02SMarkus Pfeiffer if ((hub == NULL) ||
30845f67c02SMarkus Pfeiffer (udev->speed != USB_SPEED_HIGH) ||
30945f67c02SMarkus Pfeiffer (child->speed != USB_SPEED_LOW &&
31045f67c02SMarkus Pfeiffer child->speed != USB_SPEED_FULL) ||
31145f67c02SMarkus Pfeiffer (child->flags.usb_mode != USB_MODE_HOST) ||
31245f67c02SMarkus Pfeiffer (port == 0) || (ep->edesc == NULL)) {
31345f67c02SMarkus Pfeiffer /* not applicable */
31445f67c02SMarkus Pfeiffer return;
31545f67c02SMarkus Pfeiffer }
31645f67c02SMarkus Pfeiffer
31745f67c02SMarkus Pfeiffer USB_BUS_LOCK_ASSERT(udev->bus);
31845f67c02SMarkus Pfeiffer
31945f67c02SMarkus Pfeiffer up = hub->ports + port - 1;
32045f67c02SMarkus Pfeiffer
32145f67c02SMarkus Pfeiffer if (udev->ddesc.bDeviceClass == UDCLASS_HUB &&
32245f67c02SMarkus Pfeiffer udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT)
32345f67c02SMarkus Pfeiffer port = 1;
32445f67c02SMarkus Pfeiffer
32545f67c02SMarkus Pfeiffer /* if we already received a clear buffer request, reset the whole TT */
32645f67c02SMarkus Pfeiffer if (up->req_reset_tt.bRequest != 0) {
32745f67c02SMarkus Pfeiffer req.bmRequestType = UT_WRITE_CLASS_OTHER;
32845f67c02SMarkus Pfeiffer req.bRequest = UR_RESET_TT;
32945f67c02SMarkus Pfeiffer USETW(req.wValue, 0);
33045f67c02SMarkus Pfeiffer req.wIndex[0] = port;
33145f67c02SMarkus Pfeiffer req.wIndex[1] = 0;
33245f67c02SMarkus Pfeiffer USETW(req.wLength, 0);
33345f67c02SMarkus Pfeiffer } else {
33445f67c02SMarkus Pfeiffer wValue = (ep->edesc->bEndpointAddress & 0xF) |
33545f67c02SMarkus Pfeiffer ((child->address & 0x7F) << 4) |
33645f67c02SMarkus Pfeiffer ((ep->edesc->bEndpointAddress & 0x80) << 8) |
33745f67c02SMarkus Pfeiffer ((ep->edesc->bmAttributes & 3) << 12);
33845f67c02SMarkus Pfeiffer
33945f67c02SMarkus Pfeiffer req.bmRequestType = UT_WRITE_CLASS_OTHER;
34045f67c02SMarkus Pfeiffer req.bRequest = UR_CLEAR_TT_BUFFER;
34145f67c02SMarkus Pfeiffer USETW(req.wValue, wValue);
34245f67c02SMarkus Pfeiffer req.wIndex[0] = port;
34345f67c02SMarkus Pfeiffer req.wIndex[1] = 0;
34445f67c02SMarkus Pfeiffer USETW(req.wLength, 0);
34545f67c02SMarkus Pfeiffer }
34645f67c02SMarkus Pfeiffer up->req_reset_tt = req;
34745f67c02SMarkus Pfeiffer /* get reset transfer started */
34845f67c02SMarkus Pfeiffer usb_proc_msignal(USB_BUS_NON_GIANT_PROC(udev->bus),
34945f67c02SMarkus Pfeiffer &hub->tt_msg[0], &hub->tt_msg[1]);
35045f67c02SMarkus Pfeiffer }
35145f67c02SMarkus Pfeiffer #endif
35245f67c02SMarkus Pfeiffer
35345f67c02SMarkus Pfeiffer #if USB_HAVE_TT_SUPPORT
35445f67c02SMarkus Pfeiffer static void
uhub_reset_tt_callback(struct usb_xfer * xfer,usb_error_t error)35545f67c02SMarkus Pfeiffer uhub_reset_tt_callback(struct usb_xfer *xfer, usb_error_t error)
35645f67c02SMarkus Pfeiffer {
35745f67c02SMarkus Pfeiffer struct uhub_softc *sc;
35845f67c02SMarkus Pfeiffer struct usb_device *udev;
35945f67c02SMarkus Pfeiffer struct usb_port *up;
36045f67c02SMarkus Pfeiffer uint8_t x;
36145f67c02SMarkus Pfeiffer
36245f67c02SMarkus Pfeiffer DPRINTF("TT buffer reset\n");
36345f67c02SMarkus Pfeiffer
36445f67c02SMarkus Pfeiffer sc = usbd_xfer_softc(xfer);
36545f67c02SMarkus Pfeiffer udev = sc->sc_udev;
36645f67c02SMarkus Pfeiffer
36745f67c02SMarkus Pfeiffer switch (USB_GET_STATE(xfer)) {
36845f67c02SMarkus Pfeiffer case USB_ST_TRANSFERRED:
36945f67c02SMarkus Pfeiffer case USB_ST_SETUP:
37045f67c02SMarkus Pfeiffer tr_setup:
37145f67c02SMarkus Pfeiffer USB_BUS_LOCK(udev->bus);
37245f67c02SMarkus Pfeiffer /* find first port which needs a TT reset */
37345f67c02SMarkus Pfeiffer for (x = 0; x != udev->hub->nports; x++) {
37445f67c02SMarkus Pfeiffer up = udev->hub->ports + x;
37545f67c02SMarkus Pfeiffer
37645f67c02SMarkus Pfeiffer if (up->req_reset_tt.bRequest == 0)
37745f67c02SMarkus Pfeiffer continue;
37845f67c02SMarkus Pfeiffer
37945f67c02SMarkus Pfeiffer /* copy in the transfer */
38045f67c02SMarkus Pfeiffer usbd_copy_in(xfer->frbuffers, 0, &up->req_reset_tt,
38145f67c02SMarkus Pfeiffer sizeof(up->req_reset_tt));
38245f67c02SMarkus Pfeiffer /* reset buffer */
38345f67c02SMarkus Pfeiffer memset(&up->req_reset_tt, 0, sizeof(up->req_reset_tt));
38445f67c02SMarkus Pfeiffer
38545f67c02SMarkus Pfeiffer /* set length */
38645f67c02SMarkus Pfeiffer usbd_xfer_set_frame_len(xfer, 0, sizeof(up->req_reset_tt));
38745f67c02SMarkus Pfeiffer xfer->nframes = 1;
38845f67c02SMarkus Pfeiffer USB_BUS_UNLOCK(udev->bus);
38945f67c02SMarkus Pfeiffer
39045f67c02SMarkus Pfeiffer usbd_transfer_submit(xfer);
39145f67c02SMarkus Pfeiffer return;
39245f67c02SMarkus Pfeiffer }
39345f67c02SMarkus Pfeiffer USB_BUS_UNLOCK(udev->bus);
39445f67c02SMarkus Pfeiffer break;
39545f67c02SMarkus Pfeiffer
39645f67c02SMarkus Pfeiffer default:
39745f67c02SMarkus Pfeiffer if (error == USB_ERR_CANCELLED)
39845f67c02SMarkus Pfeiffer break;
39945f67c02SMarkus Pfeiffer
40045f67c02SMarkus Pfeiffer DPRINTF("TT buffer reset failed (%s)\n", usbd_errstr(error));
40145f67c02SMarkus Pfeiffer goto tr_setup;
40245f67c02SMarkus Pfeiffer }
40345f67c02SMarkus Pfeiffer }
40445f67c02SMarkus Pfeiffer #endif
40545f67c02SMarkus Pfeiffer
40645f67c02SMarkus Pfeiffer /*------------------------------------------------------------------------*
40745f67c02SMarkus Pfeiffer * uhub_count_active_host_ports
40845f67c02SMarkus Pfeiffer *
40945f67c02SMarkus Pfeiffer * This function counts the number of active ports at the given speed.
41045f67c02SMarkus Pfeiffer *------------------------------------------------------------------------*/
41145f67c02SMarkus Pfeiffer uint8_t
uhub_count_active_host_ports(struct usb_device * udev,enum usb_dev_speed speed)41245f67c02SMarkus Pfeiffer uhub_count_active_host_ports(struct usb_device *udev, enum usb_dev_speed speed)
41345f67c02SMarkus Pfeiffer {
41445f67c02SMarkus Pfeiffer struct uhub_softc *sc;
41545f67c02SMarkus Pfeiffer struct usb_device *child;
41645f67c02SMarkus Pfeiffer struct usb_hub *hub;
41745f67c02SMarkus Pfeiffer struct usb_port *up;
41845f67c02SMarkus Pfeiffer uint8_t retval = 0;
41945f67c02SMarkus Pfeiffer uint8_t x;
42045f67c02SMarkus Pfeiffer
42145f67c02SMarkus Pfeiffer if (udev == NULL)
42245f67c02SMarkus Pfeiffer goto done;
42345f67c02SMarkus Pfeiffer hub = udev->hub;
42445f67c02SMarkus Pfeiffer if (hub == NULL)
42545f67c02SMarkus Pfeiffer goto done;
42645f67c02SMarkus Pfeiffer sc = hub->hubsoftc;
42745f67c02SMarkus Pfeiffer if (sc == NULL)
42845f67c02SMarkus Pfeiffer goto done;
42945f67c02SMarkus Pfeiffer
43045f67c02SMarkus Pfeiffer for (x = 0; x != hub->nports; x++) {
43145f67c02SMarkus Pfeiffer up = hub->ports + x;
43245f67c02SMarkus Pfeiffer child = usb_bus_port_get_device(udev->bus, up);
43345f67c02SMarkus Pfeiffer if (child != NULL &&
43445f67c02SMarkus Pfeiffer child->flags.usb_mode == USB_MODE_HOST &&
43545f67c02SMarkus Pfeiffer child->speed == speed)
43645f67c02SMarkus Pfeiffer retval++;
43745f67c02SMarkus Pfeiffer }
43845f67c02SMarkus Pfeiffer done:
43945f67c02SMarkus Pfeiffer return (retval);
44045f67c02SMarkus Pfeiffer }
44145f67c02SMarkus Pfeiffer
44245f67c02SMarkus Pfeiffer void
uhub_explore_handle_re_enumerate(struct usb_device * child)44345f67c02SMarkus Pfeiffer uhub_explore_handle_re_enumerate(struct usb_device *child)
44445f67c02SMarkus Pfeiffer {
44545f67c02SMarkus Pfeiffer uint8_t do_unlock;
44645f67c02SMarkus Pfeiffer usb_error_t err;
44745f67c02SMarkus Pfeiffer
44845f67c02SMarkus Pfeiffer /* check if device should be re-enumerated */
44945f67c02SMarkus Pfeiffer if (child->flags.usb_mode != USB_MODE_HOST)
45045f67c02SMarkus Pfeiffer return;
45145f67c02SMarkus Pfeiffer
45245f67c02SMarkus Pfeiffer do_unlock = usbd_enum_lock(child);
45345f67c02SMarkus Pfeiffer switch (child->re_enumerate_wait) {
45445f67c02SMarkus Pfeiffer case USB_RE_ENUM_START:
45545f67c02SMarkus Pfeiffer err = usbd_set_config_index(child,
45645f67c02SMarkus Pfeiffer USB_UNCONFIG_INDEX);
45745f67c02SMarkus Pfeiffer if (err != 0) {
45845f67c02SMarkus Pfeiffer DPRINTF("Unconfigure failed: %s: Ignored.\n",
45945f67c02SMarkus Pfeiffer usbd_errstr(err));
46045f67c02SMarkus Pfeiffer }
46145f67c02SMarkus Pfeiffer if (child->parent_hub == NULL) {
46245f67c02SMarkus Pfeiffer /* the root HUB cannot be re-enumerated */
46345f67c02SMarkus Pfeiffer DPRINTFN(6, "cannot reset root HUB\n");
46445f67c02SMarkus Pfeiffer err = 0;
46545f67c02SMarkus Pfeiffer } else {
46645f67c02SMarkus Pfeiffer err = usbd_req_re_enumerate(child, NULL);
46745f67c02SMarkus Pfeiffer }
46845f67c02SMarkus Pfeiffer if (err == 0)
46945f67c02SMarkus Pfeiffer err = usbd_set_config_index(child, 0);
47045f67c02SMarkus Pfeiffer if (err == 0) {
47145f67c02SMarkus Pfeiffer err = usb_probe_and_attach(child,
47245f67c02SMarkus Pfeiffer USB_IFACE_INDEX_ANY);
47345f67c02SMarkus Pfeiffer }
47445f67c02SMarkus Pfeiffer child->re_enumerate_wait = USB_RE_ENUM_DONE;
47545f67c02SMarkus Pfeiffer break;
47645f67c02SMarkus Pfeiffer
47745f67c02SMarkus Pfeiffer case USB_RE_ENUM_PWR_OFF:
47845f67c02SMarkus Pfeiffer /* get the device unconfigured */
47945f67c02SMarkus Pfeiffer err = usbd_set_config_index(child,
48045f67c02SMarkus Pfeiffer USB_UNCONFIG_INDEX);
48145f67c02SMarkus Pfeiffer if (err) {
48245f67c02SMarkus Pfeiffer DPRINTFN(0, "Could not unconfigure "
48345f67c02SMarkus Pfeiffer "device (ignored)\n");
48445f67c02SMarkus Pfeiffer }
48545f67c02SMarkus Pfeiffer if (child->parent_hub == NULL) {
48645f67c02SMarkus Pfeiffer /* the root HUB cannot be re-enumerated */
48745f67c02SMarkus Pfeiffer DPRINTFN(6, "cannot set port feature\n");
48845f67c02SMarkus Pfeiffer err = 0;
48945f67c02SMarkus Pfeiffer } else {
49045f67c02SMarkus Pfeiffer /* clear port enable */
49145f67c02SMarkus Pfeiffer err = usbd_req_clear_port_feature(child->parent_hub,
49245f67c02SMarkus Pfeiffer NULL, child->port_no, UHF_PORT_ENABLE);
49345f67c02SMarkus Pfeiffer if (err) {
49445f67c02SMarkus Pfeiffer DPRINTFN(0, "Could not disable port "
49545f67c02SMarkus Pfeiffer "(ignored)\n");
49645f67c02SMarkus Pfeiffer }
49745f67c02SMarkus Pfeiffer }
49845f67c02SMarkus Pfeiffer child->re_enumerate_wait = USB_RE_ENUM_DONE;
49945f67c02SMarkus Pfeiffer break;
50045f67c02SMarkus Pfeiffer
50145f67c02SMarkus Pfeiffer case USB_RE_ENUM_SET_CONFIG:
50245f67c02SMarkus Pfeiffer err = usbd_set_config_index(child,
50345f67c02SMarkus Pfeiffer child->next_config_index);
50445f67c02SMarkus Pfeiffer if (err != 0) {
50545f67c02SMarkus Pfeiffer DPRINTF("Configure failed: %s: Ignored.\n",
50645f67c02SMarkus Pfeiffer usbd_errstr(err));
50745f67c02SMarkus Pfeiffer } else {
50845f67c02SMarkus Pfeiffer err = usb_probe_and_attach(child,
50945f67c02SMarkus Pfeiffer USB_IFACE_INDEX_ANY);
51045f67c02SMarkus Pfeiffer }
51145f67c02SMarkus Pfeiffer child->re_enumerate_wait = USB_RE_ENUM_DONE;
51245f67c02SMarkus Pfeiffer break;
51345f67c02SMarkus Pfeiffer
51445f67c02SMarkus Pfeiffer default:
51545f67c02SMarkus Pfeiffer child->re_enumerate_wait = USB_RE_ENUM_DONE;
51645f67c02SMarkus Pfeiffer break;
51745f67c02SMarkus Pfeiffer }
51845f67c02SMarkus Pfeiffer if (do_unlock)
51945f67c02SMarkus Pfeiffer usbd_enum_unlock(child);
52045f67c02SMarkus Pfeiffer }
52145f67c02SMarkus Pfeiffer
52245f67c02SMarkus Pfeiffer /*------------------------------------------------------------------------*
52312bd3c8bSSascha Wildner * uhub_explore_sub - subroutine
52412bd3c8bSSascha Wildner *
52512bd3c8bSSascha Wildner * Return values:
52612bd3c8bSSascha Wildner * 0: Success
52712bd3c8bSSascha Wildner * Else: A control transaction failed
52812bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
52912bd3c8bSSascha Wildner static usb_error_t
uhub_explore_sub(struct uhub_softc * sc,struct usb_port * up)53012bd3c8bSSascha Wildner uhub_explore_sub(struct uhub_softc *sc, struct usb_port *up)
53112bd3c8bSSascha Wildner {
53212bd3c8bSSascha Wildner struct usb_bus *bus;
53312bd3c8bSSascha Wildner struct usb_device *child;
53412bd3c8bSSascha Wildner uint8_t refcount;
53512bd3c8bSSascha Wildner usb_error_t err;
53612bd3c8bSSascha Wildner
53712bd3c8bSSascha Wildner bus = sc->sc_udev->bus;
53812bd3c8bSSascha Wildner err = 0;
53912bd3c8bSSascha Wildner
54012bd3c8bSSascha Wildner /* get driver added refcount from USB bus */
54112bd3c8bSSascha Wildner refcount = bus->driver_added_refcount;
54212bd3c8bSSascha Wildner
54312bd3c8bSSascha Wildner /* get device assosiated with the given port */
54412bd3c8bSSascha Wildner child = usb_bus_port_get_device(bus, up);
54512bd3c8bSSascha Wildner if (child == NULL) {
54612bd3c8bSSascha Wildner /* nothing to do */
54712bd3c8bSSascha Wildner goto done;
54812bd3c8bSSascha Wildner }
54912bd3c8bSSascha Wildner
55045f67c02SMarkus Pfeiffer uhub_explore_handle_re_enumerate(child);
55112bd3c8bSSascha Wildner
55212bd3c8bSSascha Wildner /* check if probe and attach should be done */
55312bd3c8bSSascha Wildner
55412bd3c8bSSascha Wildner if (child->driver_added_refcount != refcount) {
55512bd3c8bSSascha Wildner child->driver_added_refcount = refcount;
55612bd3c8bSSascha Wildner err = usb_probe_and_attach(child,
55712bd3c8bSSascha Wildner USB_IFACE_INDEX_ANY);
55812bd3c8bSSascha Wildner if (err) {
55912bd3c8bSSascha Wildner goto done;
56012bd3c8bSSascha Wildner }
56112bd3c8bSSascha Wildner }
56212bd3c8bSSascha Wildner /* start control transfer, if device mode */
56312bd3c8bSSascha Wildner
56412bd3c8bSSascha Wildner if (child->flags.usb_mode == USB_MODE_DEVICE)
56512bd3c8bSSascha Wildner usbd_ctrl_transfer_setup(child);
56612bd3c8bSSascha Wildner
56712bd3c8bSSascha Wildner /* if a HUB becomes present, do a recursive HUB explore */
56812bd3c8bSSascha Wildner
56912bd3c8bSSascha Wildner if (child->hub)
57012bd3c8bSSascha Wildner err = (child->hub->explore) (child);
57112bd3c8bSSascha Wildner
57212bd3c8bSSascha Wildner done:
57312bd3c8bSSascha Wildner return (err);
57412bd3c8bSSascha Wildner }
57512bd3c8bSSascha Wildner
57612bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
57712bd3c8bSSascha Wildner * uhub_read_port_status - factored out code
57812bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
57912bd3c8bSSascha Wildner static usb_error_t
uhub_read_port_status(struct uhub_softc * sc,uint8_t portno)58012bd3c8bSSascha Wildner uhub_read_port_status(struct uhub_softc *sc, uint8_t portno)
58112bd3c8bSSascha Wildner {
58212bd3c8bSSascha Wildner struct usb_port_status ps;
58312bd3c8bSSascha Wildner usb_error_t err;
58412bd3c8bSSascha Wildner
58512bd3c8bSSascha Wildner err = usbd_req_get_port_status(
58612bd3c8bSSascha Wildner sc->sc_udev, NULL, &ps, portno);
58712bd3c8bSSascha Wildner
58812bd3c8bSSascha Wildner /* update status regardless of error */
58912bd3c8bSSascha Wildner
59012bd3c8bSSascha Wildner sc->sc_st.port_status = UGETW(ps.wPortStatus);
59112bd3c8bSSascha Wildner sc->sc_st.port_change = UGETW(ps.wPortChange);
59212bd3c8bSSascha Wildner
59312bd3c8bSSascha Wildner /* debugging print */
59412bd3c8bSSascha Wildner
59512bd3c8bSSascha Wildner DPRINTFN(4, "port %d, wPortStatus=0x%04x, "
59612bd3c8bSSascha Wildner "wPortChange=0x%04x, err=%s\n",
59712bd3c8bSSascha Wildner portno, sc->sc_st.port_status,
59812bd3c8bSSascha Wildner sc->sc_st.port_change, usbd_errstr(err));
59912bd3c8bSSascha Wildner return (err);
60012bd3c8bSSascha Wildner }
60112bd3c8bSSascha Wildner
60212bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
60312bd3c8bSSascha Wildner * uhub_reattach_port
60412bd3c8bSSascha Wildner *
60512bd3c8bSSascha Wildner * Returns:
60612bd3c8bSSascha Wildner * 0: Success
60712bd3c8bSSascha Wildner * Else: A control transaction failed
60812bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
60912bd3c8bSSascha Wildner static usb_error_t
uhub_reattach_port(struct uhub_softc * sc,uint8_t portno)61012bd3c8bSSascha Wildner uhub_reattach_port(struct uhub_softc *sc, uint8_t portno)
61112bd3c8bSSascha Wildner {
61212bd3c8bSSascha Wildner struct usb_device *child;
61312bd3c8bSSascha Wildner struct usb_device *udev;
61412bd3c8bSSascha Wildner enum usb_dev_speed speed;
61512bd3c8bSSascha Wildner enum usb_hc_mode mode;
61612bd3c8bSSascha Wildner usb_error_t err;
61712bd3c8bSSascha Wildner uint16_t power_mask;
61812bd3c8bSSascha Wildner uint8_t timeout;
61912bd3c8bSSascha Wildner
62012bd3c8bSSascha Wildner DPRINTF("reattaching port %d\n", portno);
62112bd3c8bSSascha Wildner
62212bd3c8bSSascha Wildner timeout = 0;
62312bd3c8bSSascha Wildner udev = sc->sc_udev;
62412bd3c8bSSascha Wildner child = usb_bus_port_get_device(udev->bus,
62512bd3c8bSSascha Wildner udev->hub->ports + portno - 1);
62612bd3c8bSSascha Wildner
62712bd3c8bSSascha Wildner repeat:
62812bd3c8bSSascha Wildner
62912bd3c8bSSascha Wildner /* first clear the port connection change bit */
63012bd3c8bSSascha Wildner
63112bd3c8bSSascha Wildner err = usbd_req_clear_port_feature(udev, NULL,
63212bd3c8bSSascha Wildner portno, UHF_C_PORT_CONNECTION);
63312bd3c8bSSascha Wildner
63412bd3c8bSSascha Wildner if (err) {
63512bd3c8bSSascha Wildner goto error;
63612bd3c8bSSascha Wildner }
63712bd3c8bSSascha Wildner /* check if there is a child */
63812bd3c8bSSascha Wildner
63912bd3c8bSSascha Wildner if (child != NULL) {
64012bd3c8bSSascha Wildner /*
64112bd3c8bSSascha Wildner * Free USB device and all subdevices, if any.
64212bd3c8bSSascha Wildner */
64312bd3c8bSSascha Wildner usb_free_device(child, 0);
64412bd3c8bSSascha Wildner child = NULL;
64512bd3c8bSSascha Wildner }
64612bd3c8bSSascha Wildner /* get fresh status */
64712bd3c8bSSascha Wildner
64812bd3c8bSSascha Wildner err = uhub_read_port_status(sc, portno);
64945f67c02SMarkus Pfeiffer if (err)
65045f67c02SMarkus Pfeiffer goto error;
65145f67c02SMarkus Pfeiffer
65245f67c02SMarkus Pfeiffer #if USB_HAVE_DISABLE_ENUM
65345f67c02SMarkus Pfeiffer /* check if we should skip enumeration from this USB HUB */
65445f67c02SMarkus Pfeiffer if (usb_disable_enumeration != 0 ||
65545f67c02SMarkus Pfeiffer sc->sc_disable_enumeration != 0) {
65645f67c02SMarkus Pfeiffer DPRINTF("Enumeration is disabled!\n");
65712bd3c8bSSascha Wildner goto error;
65812bd3c8bSSascha Wildner }
65945f67c02SMarkus Pfeiffer #endif
66012bd3c8bSSascha Wildner /* check if nothing is connected to the port */
66112bd3c8bSSascha Wildner
66212bd3c8bSSascha Wildner if (!(sc->sc_st.port_status & UPS_CURRENT_CONNECT_STATUS)) {
66312bd3c8bSSascha Wildner goto error;
66412bd3c8bSSascha Wildner }
66512bd3c8bSSascha Wildner /* check if there is no power on the port and print a warning */
66612bd3c8bSSascha Wildner
66712bd3c8bSSascha Wildner switch (udev->speed) {
66812bd3c8bSSascha Wildner case USB_SPEED_HIGH:
66912bd3c8bSSascha Wildner case USB_SPEED_FULL:
67012bd3c8bSSascha Wildner case USB_SPEED_LOW:
67112bd3c8bSSascha Wildner power_mask = UPS_PORT_POWER;
67212bd3c8bSSascha Wildner break;
67312bd3c8bSSascha Wildner case USB_SPEED_SUPER:
67412bd3c8bSSascha Wildner if (udev->parent_hub == NULL)
67512bd3c8bSSascha Wildner power_mask = UPS_PORT_POWER;
67612bd3c8bSSascha Wildner else
67712bd3c8bSSascha Wildner power_mask = UPS_PORT_POWER_SS;
67812bd3c8bSSascha Wildner break;
67912bd3c8bSSascha Wildner default:
68012bd3c8bSSascha Wildner power_mask = 0;
68112bd3c8bSSascha Wildner break;
68212bd3c8bSSascha Wildner }
68312bd3c8bSSascha Wildner if (!(sc->sc_st.port_status & power_mask)) {
68412bd3c8bSSascha Wildner DPRINTF("WARNING: strange, connected port %d "
68512bd3c8bSSascha Wildner "has no power\n", portno);
68612bd3c8bSSascha Wildner }
68712bd3c8bSSascha Wildner
68812bd3c8bSSascha Wildner /* check if the device is in Host Mode */
68912bd3c8bSSascha Wildner
69012bd3c8bSSascha Wildner if (!(sc->sc_st.port_status & UPS_PORT_MODE_DEVICE)) {
69112bd3c8bSSascha Wildner
69212bd3c8bSSascha Wildner DPRINTF("Port %d is in Host Mode\n", portno);
69312bd3c8bSSascha Wildner
69412bd3c8bSSascha Wildner if (sc->sc_st.port_status & UPS_SUSPEND) {
69512bd3c8bSSascha Wildner /*
69612bd3c8bSSascha Wildner * NOTE: Should not get here in SuperSpeed
69712bd3c8bSSascha Wildner * mode, because the HUB should report this
69812bd3c8bSSascha Wildner * bit as zero.
69912bd3c8bSSascha Wildner */
70012bd3c8bSSascha Wildner DPRINTF("Port %d was still "
70112bd3c8bSSascha Wildner "suspended, clearing.\n", portno);
70212bd3c8bSSascha Wildner err = usbd_req_clear_port_feature(udev,
70312bd3c8bSSascha Wildner NULL, portno, UHF_PORT_SUSPEND);
70412bd3c8bSSascha Wildner }
70512bd3c8bSSascha Wildner
70612bd3c8bSSascha Wildner /* USB Host Mode */
70712bd3c8bSSascha Wildner
70812bd3c8bSSascha Wildner /* wait for maximum device power up time */
70912bd3c8bSSascha Wildner
71012bd3c8bSSascha Wildner usb_pause_mtx(NULL,
7115e41ab93SMarkus Pfeiffer USB_MS_TO_TICKS(usb_port_powerup_delay));
71212bd3c8bSSascha Wildner
71312bd3c8bSSascha Wildner /* reset port, which implies enabling it */
71412bd3c8bSSascha Wildner
71512bd3c8bSSascha Wildner err = usbd_req_reset_port(udev, NULL, portno);
71612bd3c8bSSascha Wildner
71712bd3c8bSSascha Wildner if (err) {
71812bd3c8bSSascha Wildner DPRINTFN(0, "port %d reset "
71912bd3c8bSSascha Wildner "failed, error=%s\n",
72012bd3c8bSSascha Wildner portno, usbd_errstr(err));
72112bd3c8bSSascha Wildner goto error;
72212bd3c8bSSascha Wildner }
72312bd3c8bSSascha Wildner /* get port status again, it might have changed during reset */
72412bd3c8bSSascha Wildner
72512bd3c8bSSascha Wildner err = uhub_read_port_status(sc, portno);
72612bd3c8bSSascha Wildner if (err) {
72712bd3c8bSSascha Wildner goto error;
72812bd3c8bSSascha Wildner }
72912bd3c8bSSascha Wildner /* check if something changed during port reset */
73012bd3c8bSSascha Wildner
73112bd3c8bSSascha Wildner if ((sc->sc_st.port_change & UPS_C_CONNECT_STATUS) ||
73212bd3c8bSSascha Wildner (!(sc->sc_st.port_status & UPS_CURRENT_CONNECT_STATUS))) {
73312bd3c8bSSascha Wildner if (timeout) {
73412bd3c8bSSascha Wildner DPRINTFN(0, "giving up port reset "
73512bd3c8bSSascha Wildner "- device vanished\n");
73612bd3c8bSSascha Wildner goto error;
73712bd3c8bSSascha Wildner }
73812bd3c8bSSascha Wildner timeout = 1;
73912bd3c8bSSascha Wildner goto repeat;
74012bd3c8bSSascha Wildner }
74112bd3c8bSSascha Wildner } else {
74212bd3c8bSSascha Wildner DPRINTF("Port %d is in Device Mode\n", portno);
74312bd3c8bSSascha Wildner }
74412bd3c8bSSascha Wildner
74512bd3c8bSSascha Wildner /*
74612bd3c8bSSascha Wildner * Figure out the device speed
74712bd3c8bSSascha Wildner */
74812bd3c8bSSascha Wildner switch (udev->speed) {
74912bd3c8bSSascha Wildner case USB_SPEED_HIGH:
75012bd3c8bSSascha Wildner if (sc->sc_st.port_status & UPS_HIGH_SPEED)
75112bd3c8bSSascha Wildner speed = USB_SPEED_HIGH;
75212bd3c8bSSascha Wildner else if (sc->sc_st.port_status & UPS_LOW_SPEED)
75312bd3c8bSSascha Wildner speed = USB_SPEED_LOW;
75412bd3c8bSSascha Wildner else
75512bd3c8bSSascha Wildner speed = USB_SPEED_FULL;
75612bd3c8bSSascha Wildner break;
75712bd3c8bSSascha Wildner case USB_SPEED_FULL:
75812bd3c8bSSascha Wildner if (sc->sc_st.port_status & UPS_LOW_SPEED)
75912bd3c8bSSascha Wildner speed = USB_SPEED_LOW;
76012bd3c8bSSascha Wildner else
76112bd3c8bSSascha Wildner speed = USB_SPEED_FULL;
76212bd3c8bSSascha Wildner break;
76312bd3c8bSSascha Wildner case USB_SPEED_LOW:
76412bd3c8bSSascha Wildner speed = USB_SPEED_LOW;
76512bd3c8bSSascha Wildner break;
76612bd3c8bSSascha Wildner case USB_SPEED_SUPER:
76712bd3c8bSSascha Wildner if (udev->parent_hub == NULL) {
76812bd3c8bSSascha Wildner /* Root HUB - special case */
76912bd3c8bSSascha Wildner switch (sc->sc_st.port_status & UPS_OTHER_SPEED) {
77012bd3c8bSSascha Wildner case 0:
77112bd3c8bSSascha Wildner speed = USB_SPEED_FULL;
77212bd3c8bSSascha Wildner break;
77312bd3c8bSSascha Wildner case UPS_LOW_SPEED:
77412bd3c8bSSascha Wildner speed = USB_SPEED_LOW;
77512bd3c8bSSascha Wildner break;
77612bd3c8bSSascha Wildner case UPS_HIGH_SPEED:
77712bd3c8bSSascha Wildner speed = USB_SPEED_HIGH;
77812bd3c8bSSascha Wildner break;
77912bd3c8bSSascha Wildner default:
78012bd3c8bSSascha Wildner speed = USB_SPEED_SUPER;
78112bd3c8bSSascha Wildner break;
78212bd3c8bSSascha Wildner }
78312bd3c8bSSascha Wildner } else {
78412bd3c8bSSascha Wildner speed = USB_SPEED_SUPER;
78512bd3c8bSSascha Wildner }
78612bd3c8bSSascha Wildner break;
78712bd3c8bSSascha Wildner default:
78812bd3c8bSSascha Wildner /* same speed like parent */
78912bd3c8bSSascha Wildner speed = udev->speed;
79012bd3c8bSSascha Wildner break;
79112bd3c8bSSascha Wildner }
79212bd3c8bSSascha Wildner if (speed == USB_SPEED_SUPER) {
79312bd3c8bSSascha Wildner err = usbd_req_set_hub_u1_timeout(udev, NULL,
79412bd3c8bSSascha Wildner portno, 128 - (2 * udev->depth));
79512bd3c8bSSascha Wildner if (err) {
79612bd3c8bSSascha Wildner DPRINTFN(0, "port %d U1 timeout "
79712bd3c8bSSascha Wildner "failed, error=%s\n",
79812bd3c8bSSascha Wildner portno, usbd_errstr(err));
79912bd3c8bSSascha Wildner }
80012bd3c8bSSascha Wildner err = usbd_req_set_hub_u2_timeout(udev, NULL,
80112bd3c8bSSascha Wildner portno, 128 - (2 * udev->depth));
80212bd3c8bSSascha Wildner if (err) {
80312bd3c8bSSascha Wildner DPRINTFN(0, "port %d U2 timeout "
80412bd3c8bSSascha Wildner "failed, error=%s\n",
80512bd3c8bSSascha Wildner portno, usbd_errstr(err));
80612bd3c8bSSascha Wildner }
80712bd3c8bSSascha Wildner }
80812bd3c8bSSascha Wildner
80912bd3c8bSSascha Wildner /*
81012bd3c8bSSascha Wildner * Figure out the device mode
81112bd3c8bSSascha Wildner *
81212bd3c8bSSascha Wildner * NOTE: This part is currently FreeBSD specific.
81312bd3c8bSSascha Wildner */
8145e41ab93SMarkus Pfeiffer if (udev->parent_hub != NULL) {
8155e41ab93SMarkus Pfeiffer /* inherit mode from the parent HUB */
8165e41ab93SMarkus Pfeiffer mode = udev->parent_hub->flags.usb_mode;
8175e41ab93SMarkus Pfeiffer } else if (sc->sc_st.port_status & UPS_PORT_MODE_DEVICE)
81812bd3c8bSSascha Wildner mode = USB_MODE_DEVICE;
81912bd3c8bSSascha Wildner else
82012bd3c8bSSascha Wildner mode = USB_MODE_HOST;
8218922de18SMarkus Pfeiffer
82212bd3c8bSSascha Wildner /* need to create a new child */
82312bd3c8bSSascha Wildner child = usb_alloc_device(sc->sc_dev, udev->bus, udev,
82412bd3c8bSSascha Wildner udev->depth + 1, portno - 1, portno, speed, mode);
82512bd3c8bSSascha Wildner if (child == NULL) {
82612bd3c8bSSascha Wildner DPRINTFN(0, "could not allocate new device\n");
82712bd3c8bSSascha Wildner goto error;
82812bd3c8bSSascha Wildner }
82912bd3c8bSSascha Wildner return (0); /* success */
83012bd3c8bSSascha Wildner
83112bd3c8bSSascha Wildner error:
83212bd3c8bSSascha Wildner if (child != NULL) {
83312bd3c8bSSascha Wildner /*
83412bd3c8bSSascha Wildner * Free USB device and all subdevices, if any.
83512bd3c8bSSascha Wildner */
83612bd3c8bSSascha Wildner usb_free_device(child, 0);
83712bd3c8bSSascha Wildner child = NULL;
83812bd3c8bSSascha Wildner }
83912bd3c8bSSascha Wildner if (err == 0) {
84012bd3c8bSSascha Wildner if (sc->sc_st.port_status & UPS_PORT_ENABLED) {
84112bd3c8bSSascha Wildner err = usbd_req_clear_port_feature(
84212bd3c8bSSascha Wildner sc->sc_udev, NULL,
84312bd3c8bSSascha Wildner portno, UHF_PORT_ENABLE);
84412bd3c8bSSascha Wildner }
84512bd3c8bSSascha Wildner }
84612bd3c8bSSascha Wildner if (err) {
84712bd3c8bSSascha Wildner DPRINTFN(0, "device problem (%s), "
84812bd3c8bSSascha Wildner "disabling port %d\n", usbd_errstr(err), portno);
84912bd3c8bSSascha Wildner }
85012bd3c8bSSascha Wildner return (err);
85112bd3c8bSSascha Wildner }
85212bd3c8bSSascha Wildner
85312bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
85412bd3c8bSSascha Wildner * usb_device_20_compatible
85512bd3c8bSSascha Wildner *
85612bd3c8bSSascha Wildner * Returns:
85712bd3c8bSSascha Wildner * 0: HUB does not support suspend and resume
85812bd3c8bSSascha Wildner * Else: HUB supports suspend and resume
85912bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
86012bd3c8bSSascha Wildner static uint8_t
usb_device_20_compatible(struct usb_device * udev)86112bd3c8bSSascha Wildner usb_device_20_compatible(struct usb_device *udev)
86212bd3c8bSSascha Wildner {
86312bd3c8bSSascha Wildner if (udev == NULL)
86412bd3c8bSSascha Wildner return (0);
86512bd3c8bSSascha Wildner switch (udev->speed) {
86612bd3c8bSSascha Wildner case USB_SPEED_LOW:
86712bd3c8bSSascha Wildner case USB_SPEED_FULL:
86812bd3c8bSSascha Wildner case USB_SPEED_HIGH:
86912bd3c8bSSascha Wildner return (1);
87012bd3c8bSSascha Wildner default:
87112bd3c8bSSascha Wildner return (0);
87212bd3c8bSSascha Wildner }
87312bd3c8bSSascha Wildner }
87412bd3c8bSSascha Wildner
87512bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
87612bd3c8bSSascha Wildner * uhub_suspend_resume_port
87712bd3c8bSSascha Wildner *
87812bd3c8bSSascha Wildner * Returns:
87912bd3c8bSSascha Wildner * 0: Success
88012bd3c8bSSascha Wildner * Else: A control transaction failed
88112bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
88212bd3c8bSSascha Wildner static usb_error_t
uhub_suspend_resume_port(struct uhub_softc * sc,uint8_t portno)88312bd3c8bSSascha Wildner uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno)
88412bd3c8bSSascha Wildner {
88512bd3c8bSSascha Wildner struct usb_device *child;
88612bd3c8bSSascha Wildner struct usb_device *udev;
88712bd3c8bSSascha Wildner uint8_t is_suspend;
88812bd3c8bSSascha Wildner usb_error_t err;
88912bd3c8bSSascha Wildner
89012bd3c8bSSascha Wildner DPRINTF("port %d\n", portno);
89112bd3c8bSSascha Wildner
89212bd3c8bSSascha Wildner udev = sc->sc_udev;
89312bd3c8bSSascha Wildner child = usb_bus_port_get_device(udev->bus,
89412bd3c8bSSascha Wildner udev->hub->ports + portno - 1);
89512bd3c8bSSascha Wildner
89612bd3c8bSSascha Wildner /* first clear the port suspend change bit */
89712bd3c8bSSascha Wildner
89812bd3c8bSSascha Wildner if (usb_device_20_compatible(udev)) {
89912bd3c8bSSascha Wildner err = usbd_req_clear_port_feature(udev, NULL,
90012bd3c8bSSascha Wildner portno, UHF_C_PORT_SUSPEND);
90112bd3c8bSSascha Wildner } else {
90212bd3c8bSSascha Wildner err = usbd_req_clear_port_feature(udev, NULL,
90312bd3c8bSSascha Wildner portno, UHF_C_PORT_LINK_STATE);
90412bd3c8bSSascha Wildner }
90512bd3c8bSSascha Wildner
90612bd3c8bSSascha Wildner if (err) {
90712bd3c8bSSascha Wildner DPRINTF("clearing suspend failed.\n");
90812bd3c8bSSascha Wildner goto done;
90912bd3c8bSSascha Wildner }
91012bd3c8bSSascha Wildner /* get fresh status */
91112bd3c8bSSascha Wildner
91212bd3c8bSSascha Wildner err = uhub_read_port_status(sc, portno);
91312bd3c8bSSascha Wildner if (err) {
91412bd3c8bSSascha Wildner DPRINTF("reading port status failed.\n");
91512bd3c8bSSascha Wildner goto done;
91612bd3c8bSSascha Wildner }
91712bd3c8bSSascha Wildner /* convert current state */
91812bd3c8bSSascha Wildner
91912bd3c8bSSascha Wildner if (usb_device_20_compatible(udev)) {
92012bd3c8bSSascha Wildner if (sc->sc_st.port_status & UPS_SUSPEND) {
92112bd3c8bSSascha Wildner is_suspend = 1;
92212bd3c8bSSascha Wildner } else {
92312bd3c8bSSascha Wildner is_suspend = 0;
92412bd3c8bSSascha Wildner }
92512bd3c8bSSascha Wildner } else {
92612bd3c8bSSascha Wildner switch (UPS_PORT_LINK_STATE_GET(sc->sc_st.port_status)) {
92712bd3c8bSSascha Wildner case UPS_PORT_LS_U3:
92812bd3c8bSSascha Wildner is_suspend = 1;
92912bd3c8bSSascha Wildner break;
93012bd3c8bSSascha Wildner case UPS_PORT_LS_SS_INA:
93112bd3c8bSSascha Wildner usbd_req_warm_reset_port(udev, NULL, portno);
93212bd3c8bSSascha Wildner is_suspend = 0;
93312bd3c8bSSascha Wildner break;
93412bd3c8bSSascha Wildner default:
93512bd3c8bSSascha Wildner is_suspend = 0;
93612bd3c8bSSascha Wildner break;
93712bd3c8bSSascha Wildner }
93812bd3c8bSSascha Wildner }
93912bd3c8bSSascha Wildner
94012bd3c8bSSascha Wildner DPRINTF("suspended=%u\n", is_suspend);
94112bd3c8bSSascha Wildner
94212bd3c8bSSascha Wildner /* do the suspend or resume */
94312bd3c8bSSascha Wildner
94412bd3c8bSSascha Wildner if (child) {
94512bd3c8bSSascha Wildner /*
94612bd3c8bSSascha Wildner * This code handle two cases: 1) Host Mode - we can only
94712bd3c8bSSascha Wildner * receive resume here 2) Device Mode - we can receive
94812bd3c8bSSascha Wildner * suspend and resume here
94912bd3c8bSSascha Wildner */
95012bd3c8bSSascha Wildner if (is_suspend == 0)
95112bd3c8bSSascha Wildner usb_dev_resume_peer(child);
95212bd3c8bSSascha Wildner else if (child->flags.usb_mode == USB_MODE_DEVICE)
95312bd3c8bSSascha Wildner usb_dev_suspend_peer(child);
95412bd3c8bSSascha Wildner }
95512bd3c8bSSascha Wildner done:
95612bd3c8bSSascha Wildner return (err);
95712bd3c8bSSascha Wildner }
95812bd3c8bSSascha Wildner
95912bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
96012bd3c8bSSascha Wildner * uhub_root_interrupt
96112bd3c8bSSascha Wildner *
96212bd3c8bSSascha Wildner * This function is called when a Root HUB interrupt has
96312bd3c8bSSascha Wildner * happened. "ptr" and "len" makes up the Root HUB interrupt
96412bd3c8bSSascha Wildner * packet. This function is called having the "bus_mtx" locked.
96512bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
96612bd3c8bSSascha Wildner void
uhub_root_intr(struct usb_bus * bus,const uint8_t * ptr,uint8_t len)96712bd3c8bSSascha Wildner uhub_root_intr(struct usb_bus *bus, const uint8_t *ptr, uint8_t len)
96812bd3c8bSSascha Wildner {
969722d05c3SSascha Wildner USB_BUS_LOCK_ASSERT(bus);
97012bd3c8bSSascha Wildner
97112bd3c8bSSascha Wildner usb_needs_explore(bus, 0);
97212bd3c8bSSascha Wildner }
97312bd3c8bSSascha Wildner
97412bd3c8bSSascha Wildner static uint8_t
uhub_is_too_deep(struct usb_device * udev)97512bd3c8bSSascha Wildner uhub_is_too_deep(struct usb_device *udev)
97612bd3c8bSSascha Wildner {
97712bd3c8bSSascha Wildner switch (udev->speed) {
97812bd3c8bSSascha Wildner case USB_SPEED_FULL:
97912bd3c8bSSascha Wildner case USB_SPEED_LOW:
98012bd3c8bSSascha Wildner case USB_SPEED_HIGH:
98112bd3c8bSSascha Wildner if (udev->depth > USB_HUB_MAX_DEPTH)
98212bd3c8bSSascha Wildner return (1);
98312bd3c8bSSascha Wildner break;
98412bd3c8bSSascha Wildner case USB_SPEED_SUPER:
98512bd3c8bSSascha Wildner if (udev->depth > USB_SS_HUB_DEPTH_MAX)
98612bd3c8bSSascha Wildner return (1);
98712bd3c8bSSascha Wildner break;
98812bd3c8bSSascha Wildner default:
98912bd3c8bSSascha Wildner break;
99012bd3c8bSSascha Wildner }
99112bd3c8bSSascha Wildner return (0);
99212bd3c8bSSascha Wildner }
99312bd3c8bSSascha Wildner
99412bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
99512bd3c8bSSascha Wildner * uhub_explore
99612bd3c8bSSascha Wildner *
99712bd3c8bSSascha Wildner * Returns:
99812bd3c8bSSascha Wildner * 0: Success
99912bd3c8bSSascha Wildner * Else: Failure
100012bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
100112bd3c8bSSascha Wildner static usb_error_t
uhub_explore(struct usb_device * udev)100212bd3c8bSSascha Wildner uhub_explore(struct usb_device *udev)
100312bd3c8bSSascha Wildner {
100412bd3c8bSSascha Wildner struct usb_hub *hub;
100512bd3c8bSSascha Wildner struct uhub_softc *sc;
100612bd3c8bSSascha Wildner struct usb_port *up;
100712bd3c8bSSascha Wildner usb_error_t err;
100812bd3c8bSSascha Wildner uint8_t portno;
100912bd3c8bSSascha Wildner uint8_t x;
101057bed822SMarkus Pfeiffer uint8_t do_unlock;
101112bd3c8bSSascha Wildner
101212bd3c8bSSascha Wildner hub = udev->hub;
101312bd3c8bSSascha Wildner sc = hub->hubsoftc;
101412bd3c8bSSascha Wildner
101512bd3c8bSSascha Wildner DPRINTFN(11, "udev=%p addr=%d\n", udev, udev->address);
101612bd3c8bSSascha Wildner
101712bd3c8bSSascha Wildner /* ignore devices that are too deep */
101812bd3c8bSSascha Wildner if (uhub_is_too_deep(udev))
101912bd3c8bSSascha Wildner return (USB_ERR_TOO_DEEP);
102012bd3c8bSSascha Wildner
102112bd3c8bSSascha Wildner /* check if device is suspended */
102212bd3c8bSSascha Wildner if (udev->flags.self_suspended) {
102312bd3c8bSSascha Wildner /* need to wait until the child signals resume */
102412bd3c8bSSascha Wildner DPRINTF("Device is suspended!\n");
102512bd3c8bSSascha Wildner return (0);
102612bd3c8bSSascha Wildner }
102712bd3c8bSSascha Wildner
102812bd3c8bSSascha Wildner /*
102912bd3c8bSSascha Wildner * Make sure we don't race against user-space applications
103012bd3c8bSSascha Wildner * like LibUSB:
103112bd3c8bSSascha Wildner */
103257bed822SMarkus Pfeiffer do_unlock = usbd_enum_lock(udev);
103312bd3c8bSSascha Wildner
103412bd3c8bSSascha Wildner for (x = 0; x != hub->nports; x++) {
103512bd3c8bSSascha Wildner up = hub->ports + x;
103612bd3c8bSSascha Wildner portno = x + 1;
103712bd3c8bSSascha Wildner
103812bd3c8bSSascha Wildner err = uhub_read_port_status(sc, portno);
103912bd3c8bSSascha Wildner if (err) {
104012bd3c8bSSascha Wildner /* most likely the HUB is gone */
104112bd3c8bSSascha Wildner break;
104212bd3c8bSSascha Wildner }
104312bd3c8bSSascha Wildner if (sc->sc_st.port_change & UPS_C_OVERCURRENT_INDICATOR) {
104412bd3c8bSSascha Wildner DPRINTF("Overcurrent on port %u.\n", portno);
104512bd3c8bSSascha Wildner err = usbd_req_clear_port_feature(
104612bd3c8bSSascha Wildner udev, NULL, portno, UHF_C_PORT_OVER_CURRENT);
104712bd3c8bSSascha Wildner if (err) {
104812bd3c8bSSascha Wildner /* most likely the HUB is gone */
104912bd3c8bSSascha Wildner break;
105012bd3c8bSSascha Wildner }
105112bd3c8bSSascha Wildner }
105212bd3c8bSSascha Wildner if (!(sc->sc_flags & UHUB_FLAG_DID_EXPLORE)) {
105312bd3c8bSSascha Wildner /*
105412bd3c8bSSascha Wildner * Fake a connect status change so that the
105512bd3c8bSSascha Wildner * status gets checked initially!
105612bd3c8bSSascha Wildner */
105712bd3c8bSSascha Wildner sc->sc_st.port_change |=
105812bd3c8bSSascha Wildner UPS_C_CONNECT_STATUS;
105912bd3c8bSSascha Wildner }
106012bd3c8bSSascha Wildner if (sc->sc_st.port_change & UPS_C_PORT_ENABLED) {
106112bd3c8bSSascha Wildner err = usbd_req_clear_port_feature(
106212bd3c8bSSascha Wildner udev, NULL, portno, UHF_C_PORT_ENABLE);
106312bd3c8bSSascha Wildner if (err) {
106412bd3c8bSSascha Wildner /* most likely the HUB is gone */
106512bd3c8bSSascha Wildner break;
106612bd3c8bSSascha Wildner }
106712bd3c8bSSascha Wildner if (sc->sc_st.port_change & UPS_C_CONNECT_STATUS) {
106812bd3c8bSSascha Wildner /*
106912bd3c8bSSascha Wildner * Ignore the port error if the device
107012bd3c8bSSascha Wildner * has vanished !
107112bd3c8bSSascha Wildner */
107212bd3c8bSSascha Wildner } else if (sc->sc_st.port_status & UPS_PORT_ENABLED) {
107312bd3c8bSSascha Wildner DPRINTFN(0, "illegal enable change, "
107412bd3c8bSSascha Wildner "port %d\n", portno);
107512bd3c8bSSascha Wildner } else {
107612bd3c8bSSascha Wildner
107712bd3c8bSSascha Wildner if (up->restartcnt == USB_RESTART_MAX) {
107812bd3c8bSSascha Wildner /* XXX could try another speed ? */
107912bd3c8bSSascha Wildner DPRINTFN(0, "port error, giving up "
108012bd3c8bSSascha Wildner "port %d\n", portno);
108112bd3c8bSSascha Wildner } else {
108212bd3c8bSSascha Wildner sc->sc_st.port_change |=
108312bd3c8bSSascha Wildner UPS_C_CONNECT_STATUS;
108412bd3c8bSSascha Wildner up->restartcnt++;
108512bd3c8bSSascha Wildner }
108612bd3c8bSSascha Wildner }
108712bd3c8bSSascha Wildner }
108812bd3c8bSSascha Wildner if (sc->sc_st.port_change & UPS_C_CONNECT_STATUS) {
108912bd3c8bSSascha Wildner err = uhub_reattach_port(sc, portno);
109012bd3c8bSSascha Wildner if (err) {
109112bd3c8bSSascha Wildner /* most likely the HUB is gone */
109212bd3c8bSSascha Wildner break;
109312bd3c8bSSascha Wildner }
109412bd3c8bSSascha Wildner }
109512bd3c8bSSascha Wildner if (sc->sc_st.port_change & (UPS_C_SUSPEND |
109612bd3c8bSSascha Wildner UPS_C_PORT_LINK_STATE)) {
109712bd3c8bSSascha Wildner err = uhub_suspend_resume_port(sc, portno);
109812bd3c8bSSascha Wildner if (err) {
109912bd3c8bSSascha Wildner /* most likely the HUB is gone */
110012bd3c8bSSascha Wildner break;
110112bd3c8bSSascha Wildner }
110212bd3c8bSSascha Wildner }
110312bd3c8bSSascha Wildner err = uhub_explore_sub(sc, up);
110412bd3c8bSSascha Wildner if (err) {
110512bd3c8bSSascha Wildner /* no device(s) present */
110612bd3c8bSSascha Wildner continue;
110712bd3c8bSSascha Wildner }
110812bd3c8bSSascha Wildner /* explore succeeded - reset restart counter */
110912bd3c8bSSascha Wildner up->restartcnt = 0;
111012bd3c8bSSascha Wildner }
111112bd3c8bSSascha Wildner
111257bed822SMarkus Pfeiffer if (do_unlock)
111312bd3c8bSSascha Wildner usbd_enum_unlock(udev);
111412bd3c8bSSascha Wildner
111512bd3c8bSSascha Wildner /* initial status checked */
111612bd3c8bSSascha Wildner sc->sc_flags |= UHUB_FLAG_DID_EXPLORE;
111712bd3c8bSSascha Wildner
111812bd3c8bSSascha Wildner /* return success */
111912bd3c8bSSascha Wildner return (USB_ERR_NORMAL_COMPLETION);
112012bd3c8bSSascha Wildner }
112112bd3c8bSSascha Wildner
112212bd3c8bSSascha Wildner static int
uhub_probe(device_t dev)112312bd3c8bSSascha Wildner uhub_probe(device_t dev)
112412bd3c8bSSascha Wildner {
112512bd3c8bSSascha Wildner struct usb_attach_arg *uaa = device_get_ivars(dev);
112612bd3c8bSSascha Wildner
112712bd3c8bSSascha Wildner if (uaa->usb_mode != USB_MODE_HOST)
112812bd3c8bSSascha Wildner return (ENXIO);
112912bd3c8bSSascha Wildner
113012bd3c8bSSascha Wildner /*
113112bd3c8bSSascha Wildner * The subclass for USB HUBs is currently ignored because it
113212bd3c8bSSascha Wildner * is 0 for some and 1 for others.
113312bd3c8bSSascha Wildner */
113412bd3c8bSSascha Wildner if (uaa->info.bConfigIndex == 0 &&
113512bd3c8bSSascha Wildner uaa->info.bDeviceClass == UDCLASS_HUB)
113612bd3c8bSSascha Wildner return (0);
113712bd3c8bSSascha Wildner
113812bd3c8bSSascha Wildner return (ENXIO);
113912bd3c8bSSascha Wildner }
114012bd3c8bSSascha Wildner
114112bd3c8bSSascha Wildner /* NOTE: The information returned by this function can be wrong. */
114212bd3c8bSSascha Wildner usb_error_t
uhub_query_info(struct usb_device * udev,uint8_t * pnports,uint8_t * ptt)114312bd3c8bSSascha Wildner uhub_query_info(struct usb_device *udev, uint8_t *pnports, uint8_t *ptt)
114412bd3c8bSSascha Wildner {
114512bd3c8bSSascha Wildner struct usb_hub_descriptor hubdesc20;
114612bd3c8bSSascha Wildner struct usb_hub_ss_descriptor hubdesc30;
114712bd3c8bSSascha Wildner usb_error_t err;
114812bd3c8bSSascha Wildner uint8_t nports;
114912bd3c8bSSascha Wildner uint8_t tt;
115012bd3c8bSSascha Wildner
115112bd3c8bSSascha Wildner if (udev->ddesc.bDeviceClass != UDCLASS_HUB)
115212bd3c8bSSascha Wildner return (USB_ERR_INVAL);
115312bd3c8bSSascha Wildner
115412bd3c8bSSascha Wildner nports = 0;
115512bd3c8bSSascha Wildner tt = 0;
115612bd3c8bSSascha Wildner
115712bd3c8bSSascha Wildner switch (udev->speed) {
115812bd3c8bSSascha Wildner case USB_SPEED_LOW:
115912bd3c8bSSascha Wildner case USB_SPEED_FULL:
116012bd3c8bSSascha Wildner case USB_SPEED_HIGH:
116112bd3c8bSSascha Wildner /* assuming that there is one port */
116212bd3c8bSSascha Wildner err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, 1);
116312bd3c8bSSascha Wildner if (err) {
116412bd3c8bSSascha Wildner DPRINTFN(0, "getting USB 2.0 HUB descriptor failed,"
116512bd3c8bSSascha Wildner "error=%s\n", usbd_errstr(err));
116612bd3c8bSSascha Wildner break;
116712bd3c8bSSascha Wildner }
116812bd3c8bSSascha Wildner nports = hubdesc20.bNbrPorts;
116912bd3c8bSSascha Wildner if (nports > 127)
117012bd3c8bSSascha Wildner nports = 127;
117112bd3c8bSSascha Wildner
117212bd3c8bSSascha Wildner if (udev->speed == USB_SPEED_HIGH)
117312bd3c8bSSascha Wildner tt = (UGETW(hubdesc20.wHubCharacteristics) >> 5) & 3;
117412bd3c8bSSascha Wildner break;
117512bd3c8bSSascha Wildner
117612bd3c8bSSascha Wildner case USB_SPEED_SUPER:
117712bd3c8bSSascha Wildner err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, 1);
117812bd3c8bSSascha Wildner if (err) {
117912bd3c8bSSascha Wildner DPRINTFN(0, "Getting USB 3.0 HUB descriptor failed,"
118012bd3c8bSSascha Wildner "error=%s\n", usbd_errstr(err));
118112bd3c8bSSascha Wildner break;
118212bd3c8bSSascha Wildner }
118312bd3c8bSSascha Wildner nports = hubdesc30.bNbrPorts;
118412bd3c8bSSascha Wildner if (nports > 16)
118512bd3c8bSSascha Wildner nports = 16;
118612bd3c8bSSascha Wildner break;
118712bd3c8bSSascha Wildner
118812bd3c8bSSascha Wildner default:
118912bd3c8bSSascha Wildner err = USB_ERR_INVAL;
119012bd3c8bSSascha Wildner break;
119112bd3c8bSSascha Wildner }
119212bd3c8bSSascha Wildner
119312bd3c8bSSascha Wildner if (pnports != NULL)
119412bd3c8bSSascha Wildner *pnports = nports;
119512bd3c8bSSascha Wildner
119612bd3c8bSSascha Wildner if (ptt != NULL)
119712bd3c8bSSascha Wildner *ptt = tt;
119812bd3c8bSSascha Wildner
119912bd3c8bSSascha Wildner return (err);
120012bd3c8bSSascha Wildner }
120112bd3c8bSSascha Wildner
120212bd3c8bSSascha Wildner static int
uhub_attach(device_t dev)120312bd3c8bSSascha Wildner uhub_attach(device_t dev)
120412bd3c8bSSascha Wildner {
120512bd3c8bSSascha Wildner struct uhub_softc *sc = device_get_softc(dev);
120612bd3c8bSSascha Wildner struct usb_attach_arg *uaa = device_get_ivars(dev);
120712bd3c8bSSascha Wildner struct usb_device *udev = uaa->device;
120812bd3c8bSSascha Wildner struct usb_device *parent_hub = udev->parent_hub;
120912bd3c8bSSascha Wildner struct usb_hub *hub;
121012bd3c8bSSascha Wildner struct usb_hub_descriptor hubdesc20;
121112bd3c8bSSascha Wildner struct usb_hub_ss_descriptor hubdesc30;
121245f67c02SMarkus Pfeiffer #if USB_HAVE_DISABLE_ENUM
121345f67c02SMarkus Pfeiffer struct sysctl_ctx_list *sysctl_ctx;
121445f67c02SMarkus Pfeiffer struct sysctl_oid *sysctl_tree;
121545f67c02SMarkus Pfeiffer #endif
121612bd3c8bSSascha Wildner uint16_t pwrdly;
121757bed822SMarkus Pfeiffer uint16_t nports;
121812bd3c8bSSascha Wildner uint8_t x;
121912bd3c8bSSascha Wildner uint8_t portno;
122012bd3c8bSSascha Wildner uint8_t removable;
122112bd3c8bSSascha Wildner uint8_t iface_index;
122212bd3c8bSSascha Wildner usb_error_t err;
122312bd3c8bSSascha Wildner
122412bd3c8bSSascha Wildner sc->sc_udev = udev;
122512bd3c8bSSascha Wildner sc->sc_dev = dev;
122612bd3c8bSSascha Wildner
1227722d05c3SSascha Wildner lockinit(&sc->sc_lock, "USB HUB mutex", 0, 0);
122812bd3c8bSSascha Wildner
122912bd3c8bSSascha Wildner device_set_usb_desc(dev);
123012bd3c8bSSascha Wildner
123112bd3c8bSSascha Wildner DPRINTFN(2, "depth=%d selfpowered=%d, parent=%p, "
123212bd3c8bSSascha Wildner "parent->selfpowered=%d\n",
123312bd3c8bSSascha Wildner udev->depth,
123412bd3c8bSSascha Wildner udev->flags.self_powered,
123512bd3c8bSSascha Wildner parent_hub,
123612bd3c8bSSascha Wildner parent_hub ?
123712bd3c8bSSascha Wildner parent_hub->flags.self_powered : 0);
123812bd3c8bSSascha Wildner
123912bd3c8bSSascha Wildner if (uhub_is_too_deep(udev)) {
124012bd3c8bSSascha Wildner DPRINTFN(0, "HUB at depth %d, "
124112bd3c8bSSascha Wildner "exceeds maximum. HUB ignored\n", (int)udev->depth);
124212bd3c8bSSascha Wildner goto error;
124312bd3c8bSSascha Wildner }
124412bd3c8bSSascha Wildner
124512bd3c8bSSascha Wildner if (!udev->flags.self_powered && parent_hub &&
124612bd3c8bSSascha Wildner !parent_hub->flags.self_powered) {
124712bd3c8bSSascha Wildner DPRINTFN(0, "Bus powered HUB connected to "
124812bd3c8bSSascha Wildner "bus powered HUB. HUB ignored\n");
124912bd3c8bSSascha Wildner goto error;
125012bd3c8bSSascha Wildner }
12515e41ab93SMarkus Pfeiffer
12525e41ab93SMarkus Pfeiffer if (UHUB_IS_MULTI_TT(sc)) {
12535e41ab93SMarkus Pfeiffer err = usbd_set_alt_interface_index(udev, 0, 1);
12545e41ab93SMarkus Pfeiffer if (err) {
12555e41ab93SMarkus Pfeiffer device_printf(dev, "MTT could not be enabled\n");
12565e41ab93SMarkus Pfeiffer goto error;
12575e41ab93SMarkus Pfeiffer }
12585e41ab93SMarkus Pfeiffer device_printf(dev, "MTT enabled\n");
12595e41ab93SMarkus Pfeiffer }
12605e41ab93SMarkus Pfeiffer
126112bd3c8bSSascha Wildner /* get HUB descriptor */
126212bd3c8bSSascha Wildner
126312bd3c8bSSascha Wildner DPRINTFN(2, "Getting HUB descriptor\n");
126412bd3c8bSSascha Wildner
126512bd3c8bSSascha Wildner switch (udev->speed) {
126612bd3c8bSSascha Wildner case USB_SPEED_LOW:
126712bd3c8bSSascha Wildner case USB_SPEED_FULL:
126812bd3c8bSSascha Wildner case USB_SPEED_HIGH:
126912bd3c8bSSascha Wildner /* assuming that there is one port */
127012bd3c8bSSascha Wildner err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, 1);
127112bd3c8bSSascha Wildner if (err) {
127212bd3c8bSSascha Wildner DPRINTFN(0, "getting USB 2.0 HUB descriptor failed,"
127312bd3c8bSSascha Wildner "error=%s\n", usbd_errstr(err));
127412bd3c8bSSascha Wildner goto error;
127512bd3c8bSSascha Wildner }
127612bd3c8bSSascha Wildner /* get number of ports */
127712bd3c8bSSascha Wildner nports = hubdesc20.bNbrPorts;
127812bd3c8bSSascha Wildner
127912bd3c8bSSascha Wildner /* get power delay */
128012bd3c8bSSascha Wildner pwrdly = ((hubdesc20.bPwrOn2PwrGood * UHD_PWRON_FACTOR) +
12815e41ab93SMarkus Pfeiffer usb_extra_power_up_time);
128212bd3c8bSSascha Wildner
128312bd3c8bSSascha Wildner /* get complete HUB descriptor */
128412bd3c8bSSascha Wildner if (nports >= 8) {
128512bd3c8bSSascha Wildner /* check number of ports */
128612bd3c8bSSascha Wildner if (nports > 127) {
128712bd3c8bSSascha Wildner DPRINTFN(0, "Invalid number of USB 2.0 ports,"
128812bd3c8bSSascha Wildner "error=%s\n", usbd_errstr(err));
128912bd3c8bSSascha Wildner goto error;
129012bd3c8bSSascha Wildner }
129112bd3c8bSSascha Wildner /* get complete HUB descriptor */
129212bd3c8bSSascha Wildner err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, nports);
129312bd3c8bSSascha Wildner
129412bd3c8bSSascha Wildner if (err) {
129512bd3c8bSSascha Wildner DPRINTFN(0, "Getting USB 2.0 HUB descriptor failed,"
129612bd3c8bSSascha Wildner "error=%s\n", usbd_errstr(err));
129712bd3c8bSSascha Wildner goto error;
129812bd3c8bSSascha Wildner }
129912bd3c8bSSascha Wildner if (hubdesc20.bNbrPorts != nports) {
130012bd3c8bSSascha Wildner DPRINTFN(0, "Number of ports changed\n");
130112bd3c8bSSascha Wildner goto error;
130212bd3c8bSSascha Wildner }
130312bd3c8bSSascha Wildner }
130412bd3c8bSSascha Wildner break;
130512bd3c8bSSascha Wildner case USB_SPEED_SUPER:
130612bd3c8bSSascha Wildner if (udev->parent_hub != NULL) {
130712bd3c8bSSascha Wildner err = usbd_req_set_hub_depth(udev, NULL,
130812bd3c8bSSascha Wildner udev->depth - 1);
130912bd3c8bSSascha Wildner if (err) {
131012bd3c8bSSascha Wildner DPRINTFN(0, "Setting USB 3.0 HUB depth failed,"
131112bd3c8bSSascha Wildner "error=%s\n", usbd_errstr(err));
131212bd3c8bSSascha Wildner goto error;
131312bd3c8bSSascha Wildner }
131412bd3c8bSSascha Wildner }
131512bd3c8bSSascha Wildner err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, 1);
131612bd3c8bSSascha Wildner if (err) {
131712bd3c8bSSascha Wildner DPRINTFN(0, "Getting USB 3.0 HUB descriptor failed,"
131812bd3c8bSSascha Wildner "error=%s\n", usbd_errstr(err));
131912bd3c8bSSascha Wildner goto error;
132012bd3c8bSSascha Wildner }
132112bd3c8bSSascha Wildner /* get number of ports */
132212bd3c8bSSascha Wildner nports = hubdesc30.bNbrPorts;
132312bd3c8bSSascha Wildner
132412bd3c8bSSascha Wildner /* get power delay */
132512bd3c8bSSascha Wildner pwrdly = ((hubdesc30.bPwrOn2PwrGood * UHD_PWRON_FACTOR) +
13265e41ab93SMarkus Pfeiffer usb_extra_power_up_time);
132712bd3c8bSSascha Wildner
132812bd3c8bSSascha Wildner /* get complete HUB descriptor */
132912bd3c8bSSascha Wildner if (nports >= 8) {
133012bd3c8bSSascha Wildner /* check number of ports */
133112bd3c8bSSascha Wildner if (nports > ((udev->parent_hub != NULL) ? 15 : 127)) {
133212bd3c8bSSascha Wildner DPRINTFN(0, "Invalid number of USB 3.0 ports,"
133312bd3c8bSSascha Wildner "error=%s\n", usbd_errstr(err));
133412bd3c8bSSascha Wildner goto error;
133512bd3c8bSSascha Wildner }
133612bd3c8bSSascha Wildner /* get complete HUB descriptor */
133712bd3c8bSSascha Wildner err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, nports);
133812bd3c8bSSascha Wildner
133912bd3c8bSSascha Wildner if (err) {
134012bd3c8bSSascha Wildner DPRINTFN(0, "Getting USB 2.0 HUB descriptor failed,"
134112bd3c8bSSascha Wildner "error=%s\n", usbd_errstr(err));
134212bd3c8bSSascha Wildner goto error;
134312bd3c8bSSascha Wildner }
134412bd3c8bSSascha Wildner if (hubdesc30.bNbrPorts != nports) {
134512bd3c8bSSascha Wildner DPRINTFN(0, "Number of ports changed\n");
134612bd3c8bSSascha Wildner goto error;
134712bd3c8bSSascha Wildner }
134812bd3c8bSSascha Wildner }
134912bd3c8bSSascha Wildner break;
135012bd3c8bSSascha Wildner default:
135112bd3c8bSSascha Wildner DPRINTF("Assuming HUB has only one port\n");
135212bd3c8bSSascha Wildner /* default number of ports */
135312bd3c8bSSascha Wildner nports = 1;
135412bd3c8bSSascha Wildner /* default power delay */
13555e41ab93SMarkus Pfeiffer pwrdly = ((10 * UHD_PWRON_FACTOR) + usb_extra_power_up_time);
135612bd3c8bSSascha Wildner break;
135712bd3c8bSSascha Wildner }
135812bd3c8bSSascha Wildner if (nports == 0) {
135912bd3c8bSSascha Wildner DPRINTFN(0, "portless HUB\n");
136012bd3c8bSSascha Wildner goto error;
136112bd3c8bSSascha Wildner }
136257bed822SMarkus Pfeiffer if (nports > USB_MAX_PORTS) {
136357bed822SMarkus Pfeiffer DPRINTF("Port limit exceeded\n");
136457bed822SMarkus Pfeiffer goto error;
136557bed822SMarkus Pfeiffer }
136657bed822SMarkus Pfeiffer #if (USB_HAVE_FIXED_PORT == 0)
1367722d05c3SSascha Wildner hub = kmalloc(sizeof(hub[0]) + (sizeof(hub->ports[0]) * nports),
136812bd3c8bSSascha Wildner M_USBDEV, M_WAITOK | M_ZERO);
136912bd3c8bSSascha Wildner
137057bed822SMarkus Pfeiffer if (hub == NULL)
137157bed822SMarkus Pfeiffer goto error;
137257bed822SMarkus Pfeiffer #else
137357bed822SMarkus Pfeiffer hub = &sc->sc_hub;
137457bed822SMarkus Pfeiffer #endif
137512bd3c8bSSascha Wildner udev->hub = hub;
137612bd3c8bSSascha Wildner
137712bd3c8bSSascha Wildner /* initialize HUB structure */
137812bd3c8bSSascha Wildner hub->hubsoftc = sc;
137912bd3c8bSSascha Wildner hub->explore = &uhub_explore;
138012bd3c8bSSascha Wildner hub->nports = nports;
138112bd3c8bSSascha Wildner hub->hubudev = udev;
138245f67c02SMarkus Pfeiffer #if USB_HAVE_TT_SUPPORT
138345f67c02SMarkus Pfeiffer hub->tt_msg[0].hdr.pm_callback = &uhub_reset_tt_proc;
138445f67c02SMarkus Pfeiffer hub->tt_msg[0].udev = udev;
138545f67c02SMarkus Pfeiffer hub->tt_msg[1].hdr.pm_callback = &uhub_reset_tt_proc;
138645f67c02SMarkus Pfeiffer hub->tt_msg[1].udev = udev;
138745f67c02SMarkus Pfeiffer #endif
138812bd3c8bSSascha Wildner /* if self powered hub, give ports maximum current */
138912bd3c8bSSascha Wildner if (udev->flags.self_powered) {
139012bd3c8bSSascha Wildner hub->portpower = USB_MAX_POWER;
139112bd3c8bSSascha Wildner } else {
139212bd3c8bSSascha Wildner hub->portpower = USB_MIN_POWER;
139312bd3c8bSSascha Wildner }
139412bd3c8bSSascha Wildner
139512bd3c8bSSascha Wildner /* set up interrupt pipe */
139612bd3c8bSSascha Wildner iface_index = 0;
139712bd3c8bSSascha Wildner if (udev->parent_hub == NULL) {
139812bd3c8bSSascha Wildner /* root HUB is special */
139912bd3c8bSSascha Wildner err = 0;
140012bd3c8bSSascha Wildner } else {
140112bd3c8bSSascha Wildner /* normal HUB */
140212bd3c8bSSascha Wildner err = usbd_transfer_setup(udev, &iface_index, sc->sc_xfer,
1403722d05c3SSascha Wildner uhub_config, UHUB_N_TRANSFER, sc, &sc->sc_lock);
140412bd3c8bSSascha Wildner }
140512bd3c8bSSascha Wildner if (err) {
140612bd3c8bSSascha Wildner DPRINTFN(0, "cannot setup interrupt transfer, "
140712bd3c8bSSascha Wildner "errstr=%s\n", usbd_errstr(err));
140812bd3c8bSSascha Wildner goto error;
140912bd3c8bSSascha Wildner }
141012bd3c8bSSascha Wildner /* wait with power off for a while */
141112bd3c8bSSascha Wildner usb_pause_mtx(NULL, USB_MS_TO_TICKS(USB_POWER_DOWN_TIME));
141212bd3c8bSSascha Wildner
141345f67c02SMarkus Pfeiffer #if USB_HAVE_DISABLE_ENUM
141445f67c02SMarkus Pfeiffer /* Add device sysctls */
141545f67c02SMarkus Pfeiffer
141645f67c02SMarkus Pfeiffer sysctl_ctx = device_get_sysctl_ctx(dev);
141745f67c02SMarkus Pfeiffer sysctl_tree = device_get_sysctl_tree(dev);
141845f67c02SMarkus Pfeiffer
141945f67c02SMarkus Pfeiffer if (sysctl_ctx != NULL && sysctl_tree != NULL) {
142045f67c02SMarkus Pfeiffer (void) SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
14215ea2ce29SSascha Wildner OID_AUTO, "disable_enumeration", CTLFLAG_RW,
142245f67c02SMarkus Pfeiffer &sc->sc_disable_enumeration, 0,
142345f67c02SMarkus Pfeiffer "Set to disable enumeration on this USB HUB.");
142445f67c02SMarkus Pfeiffer
142545f67c02SMarkus Pfeiffer (void) SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
14265ea2ce29SSascha Wildner OID_AUTO, "disable_port_power", CTLFLAG_RW,
142745f67c02SMarkus Pfeiffer &sc->sc_disable_port_power, 0,
142845f67c02SMarkus Pfeiffer "Set to disable USB port power on this USB HUB.");
142945f67c02SMarkus Pfeiffer }
143045f67c02SMarkus Pfeiffer #endif
143112bd3c8bSSascha Wildner /*
143212bd3c8bSSascha Wildner * To have the best chance of success we do things in the exact same
143312bd3c8bSSascha Wildner * order as Windoze98. This should not be necessary, but some
143412bd3c8bSSascha Wildner * devices do not follow the USB specs to the letter.
143512bd3c8bSSascha Wildner *
143612bd3c8bSSascha Wildner * These are the events on the bus when a hub is attached:
143712bd3c8bSSascha Wildner * Get device and config descriptors (see attach code)
143812bd3c8bSSascha Wildner * Get hub descriptor (see above)
143912bd3c8bSSascha Wildner * For all ports
144012bd3c8bSSascha Wildner * turn on power
144112bd3c8bSSascha Wildner * wait for power to become stable
144212bd3c8bSSascha Wildner * (all below happens in explore code)
144312bd3c8bSSascha Wildner * For all ports
144412bd3c8bSSascha Wildner * clear C_PORT_CONNECTION
144512bd3c8bSSascha Wildner * For all ports
144612bd3c8bSSascha Wildner * get port status
144712bd3c8bSSascha Wildner * if device connected
144812bd3c8bSSascha Wildner * wait 100 ms
144912bd3c8bSSascha Wildner * turn on reset
145012bd3c8bSSascha Wildner * wait
145112bd3c8bSSascha Wildner * clear C_PORT_RESET
145212bd3c8bSSascha Wildner * get port status
145312bd3c8bSSascha Wildner * proceed with device attachment
145412bd3c8bSSascha Wildner */
145512bd3c8bSSascha Wildner
145612bd3c8bSSascha Wildner /* XXX should check for none, individual, or ganged power? */
145712bd3c8bSSascha Wildner
145812bd3c8bSSascha Wildner removable = 0;
145912bd3c8bSSascha Wildner
146012bd3c8bSSascha Wildner for (x = 0; x != nports; x++) {
146112bd3c8bSSascha Wildner /* set up data structures */
146212bd3c8bSSascha Wildner struct usb_port *up = hub->ports + x;
146312bd3c8bSSascha Wildner
146412bd3c8bSSascha Wildner up->device_index = 0;
146512bd3c8bSSascha Wildner up->restartcnt = 0;
146612bd3c8bSSascha Wildner portno = x + 1;
146712bd3c8bSSascha Wildner
146812bd3c8bSSascha Wildner /* check if port is removable */
146912bd3c8bSSascha Wildner switch (udev->speed) {
147012bd3c8bSSascha Wildner case USB_SPEED_LOW:
147112bd3c8bSSascha Wildner case USB_SPEED_FULL:
147212bd3c8bSSascha Wildner case USB_SPEED_HIGH:
147312bd3c8bSSascha Wildner if (!UHD_NOT_REMOV(&hubdesc20, portno))
147412bd3c8bSSascha Wildner removable++;
147512bd3c8bSSascha Wildner break;
147612bd3c8bSSascha Wildner case USB_SPEED_SUPER:
147712bd3c8bSSascha Wildner if (!UHD_NOT_REMOV(&hubdesc30, portno))
147812bd3c8bSSascha Wildner removable++;
147912bd3c8bSSascha Wildner break;
148012bd3c8bSSascha Wildner default:
148112bd3c8bSSascha Wildner DPRINTF("Assuming removable port\n");
148212bd3c8bSSascha Wildner removable++;
148312bd3c8bSSascha Wildner break;
148412bd3c8bSSascha Wildner }
148545f67c02SMarkus Pfeiffer if (err == 0) {
148645f67c02SMarkus Pfeiffer #if USB_HAVE_DISABLE_ENUM
148745f67c02SMarkus Pfeiffer /* check if we should disable USB port power or not */
148845f67c02SMarkus Pfeiffer if (usb_disable_port_power != 0 ||
148945f67c02SMarkus Pfeiffer sc->sc_disable_port_power != 0) {
149045f67c02SMarkus Pfeiffer /* turn the power off */
149145f67c02SMarkus Pfeiffer DPRINTFN(2, "Turning port %d power off\n", portno);
149245f67c02SMarkus Pfeiffer err = usbd_req_clear_port_feature(udev, NULL,
149345f67c02SMarkus Pfeiffer portno, UHF_PORT_POWER);
149445f67c02SMarkus Pfeiffer } else {
149545f67c02SMarkus Pfeiffer #endif
149612bd3c8bSSascha Wildner /* turn the power on */
149745f67c02SMarkus Pfeiffer DPRINTFN(2, "Turning port %d power on\n", portno);
149812bd3c8bSSascha Wildner err = usbd_req_set_port_feature(udev, NULL,
149912bd3c8bSSascha Wildner portno, UHF_PORT_POWER);
150045f67c02SMarkus Pfeiffer #if USB_HAVE_DISABLE_ENUM
150112bd3c8bSSascha Wildner }
150245f67c02SMarkus Pfeiffer #endif
150345f67c02SMarkus Pfeiffer }
150445f67c02SMarkus Pfeiffer if (err != 0) {
150545f67c02SMarkus Pfeiffer DPRINTFN(0, "port %d power on or off failed, %s\n",
150612bd3c8bSSascha Wildner portno, usbd_errstr(err));
150712bd3c8bSSascha Wildner }
150812bd3c8bSSascha Wildner DPRINTF("turn on port %d power\n",
150912bd3c8bSSascha Wildner portno);
151012bd3c8bSSascha Wildner
151112bd3c8bSSascha Wildner /* wait for stable power */
151212bd3c8bSSascha Wildner usb_pause_mtx(NULL, USB_MS_TO_TICKS(pwrdly));
151312bd3c8bSSascha Wildner }
151412bd3c8bSSascha Wildner
151512bd3c8bSSascha Wildner device_printf(dev, "%d port%s with %d "
151612bd3c8bSSascha Wildner "removable, %s powered\n", nports, (nports != 1) ? "s" : "",
151712bd3c8bSSascha Wildner removable, udev->flags.self_powered ? "self" : "bus");
151812bd3c8bSSascha Wildner
151912bd3c8bSSascha Wildner /* Start the interrupt endpoint, if any */
152012bd3c8bSSascha Wildner
152112bd3c8bSSascha Wildner if (sc->sc_xfer[0] != NULL) {
1522722d05c3SSascha Wildner lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
152312bd3c8bSSascha Wildner usbd_transfer_start(sc->sc_xfer[0]);
1524722d05c3SSascha Wildner lockmgr(&sc->sc_lock, LK_RELEASE);
152512bd3c8bSSascha Wildner }
152612bd3c8bSSascha Wildner
152712bd3c8bSSascha Wildner /* Enable automatic power save on all USB HUBs */
152812bd3c8bSSascha Wildner
152912bd3c8bSSascha Wildner usbd_set_power_mode(udev, USB_POWER_MODE_SAVE);
153012bd3c8bSSascha Wildner
153112bd3c8bSSascha Wildner return (0);
153212bd3c8bSSascha Wildner
153312bd3c8bSSascha Wildner error:
153412bd3c8bSSascha Wildner usbd_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER);
153512bd3c8bSSascha Wildner
153612bd3c8bSSascha Wildner if (udev->hub) {
153757bed822SMarkus Pfeiffer #if (USB_HAVE_FIXED_PORT == 0)
1538722d05c3SSascha Wildner kfree(udev->hub, M_USBDEV);
153957bed822SMarkus Pfeiffer #endif
154012bd3c8bSSascha Wildner udev->hub = NULL;
154112bd3c8bSSascha Wildner }
1542722d05c3SSascha Wildner lockuninit(&sc->sc_lock);
154312bd3c8bSSascha Wildner
154412bd3c8bSSascha Wildner return (ENXIO);
154512bd3c8bSSascha Wildner }
154612bd3c8bSSascha Wildner
154712bd3c8bSSascha Wildner /*
154812bd3c8bSSascha Wildner * Called from process context when the hub is gone.
154912bd3c8bSSascha Wildner * Detach all devices on active ports.
155012bd3c8bSSascha Wildner */
155112bd3c8bSSascha Wildner static int
uhub_detach(device_t dev)155212bd3c8bSSascha Wildner uhub_detach(device_t dev)
155312bd3c8bSSascha Wildner {
155412bd3c8bSSascha Wildner struct uhub_softc *sc = device_get_softc(dev);
155512bd3c8bSSascha Wildner struct usb_hub *hub = sc->sc_udev->hub;
155645f67c02SMarkus Pfeiffer struct usb_bus *bus = sc->sc_udev->bus;
155712bd3c8bSSascha Wildner struct usb_device *child;
155812bd3c8bSSascha Wildner uint8_t x;
155912bd3c8bSSascha Wildner
156012bd3c8bSSascha Wildner if (hub == NULL) /* must be partially working */
156112bd3c8bSSascha Wildner return (0);
156212bd3c8bSSascha Wildner
156312bd3c8bSSascha Wildner /* Make sure interrupt transfer is gone. */
156412bd3c8bSSascha Wildner usbd_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER);
156512bd3c8bSSascha Wildner
156612bd3c8bSSascha Wildner /* Detach all ports */
156712bd3c8bSSascha Wildner for (x = 0; x != hub->nports; x++) {
156812bd3c8bSSascha Wildner
156945f67c02SMarkus Pfeiffer child = usb_bus_port_get_device(bus, hub->ports + x);
157012bd3c8bSSascha Wildner
157112bd3c8bSSascha Wildner if (child == NULL) {
157212bd3c8bSSascha Wildner continue;
157312bd3c8bSSascha Wildner }
157412bd3c8bSSascha Wildner
157512bd3c8bSSascha Wildner /*
157612bd3c8bSSascha Wildner * Free USB device and all subdevices, if any.
157712bd3c8bSSascha Wildner */
157812bd3c8bSSascha Wildner usb_free_device(child, 0);
157912bd3c8bSSascha Wildner }
158012bd3c8bSSascha Wildner
158145f67c02SMarkus Pfeiffer #if USB_HAVE_TT_SUPPORT
158245f67c02SMarkus Pfeiffer /* Make sure our TT messages are not queued anywhere */
158345f67c02SMarkus Pfeiffer USB_BUS_LOCK(bus);
158445f67c02SMarkus Pfeiffer usb_proc_mwait(USB_BUS_NON_GIANT_PROC(bus),
158545f67c02SMarkus Pfeiffer &hub->tt_msg[0], &hub->tt_msg[1]);
158645f67c02SMarkus Pfeiffer USB_BUS_UNLOCK(bus);
158745f67c02SMarkus Pfeiffer #endif
158845f67c02SMarkus Pfeiffer
158957bed822SMarkus Pfeiffer #if (USB_HAVE_FIXED_PORT == 0)
1590722d05c3SSascha Wildner kfree(hub, M_USBDEV);
159157bed822SMarkus Pfeiffer #endif
159212bd3c8bSSascha Wildner sc->sc_udev->hub = NULL;
159312bd3c8bSSascha Wildner
1594722d05c3SSascha Wildner lockuninit(&sc->sc_lock);
159512bd3c8bSSascha Wildner
159612bd3c8bSSascha Wildner return (0);
159712bd3c8bSSascha Wildner }
159812bd3c8bSSascha Wildner
159912bd3c8bSSascha Wildner static int
uhub_suspend(device_t dev)160012bd3c8bSSascha Wildner uhub_suspend(device_t dev)
160112bd3c8bSSascha Wildner {
160212bd3c8bSSascha Wildner DPRINTF("\n");
160312bd3c8bSSascha Wildner /* Sub-devices are not suspended here! */
160412bd3c8bSSascha Wildner return (0);
160512bd3c8bSSascha Wildner }
160612bd3c8bSSascha Wildner
160712bd3c8bSSascha Wildner static int
uhub_resume(device_t dev)160812bd3c8bSSascha Wildner uhub_resume(device_t dev)
160912bd3c8bSSascha Wildner {
161012bd3c8bSSascha Wildner DPRINTF("\n");
161112bd3c8bSSascha Wildner /* Sub-devices are not resumed here! */
161212bd3c8bSSascha Wildner return (0);
161312bd3c8bSSascha Wildner }
161412bd3c8bSSascha Wildner
161512bd3c8bSSascha Wildner static void
uhub_driver_added(device_t dev,driver_t * driver)161612bd3c8bSSascha Wildner uhub_driver_added(device_t dev, driver_t *driver)
161712bd3c8bSSascha Wildner {
161812bd3c8bSSascha Wildner usb_needs_explore_all();
161912bd3c8bSSascha Wildner }
162012bd3c8bSSascha Wildner
162112bd3c8bSSascha Wildner struct hub_result {
162212bd3c8bSSascha Wildner struct usb_device *udev;
162312bd3c8bSSascha Wildner uint8_t portno;
162412bd3c8bSSascha Wildner uint8_t iface_index;
162512bd3c8bSSascha Wildner };
162612bd3c8bSSascha Wildner
162712bd3c8bSSascha Wildner static void
uhub_find_iface_index(struct usb_hub * hub,device_t child,struct hub_result * res)162812bd3c8bSSascha Wildner uhub_find_iface_index(struct usb_hub *hub, device_t child,
162912bd3c8bSSascha Wildner struct hub_result *res)
163012bd3c8bSSascha Wildner {
163112bd3c8bSSascha Wildner struct usb_interface *iface;
163212bd3c8bSSascha Wildner struct usb_device *udev;
163312bd3c8bSSascha Wildner uint8_t nports;
163412bd3c8bSSascha Wildner uint8_t x;
163512bd3c8bSSascha Wildner uint8_t i;
163612bd3c8bSSascha Wildner
163712bd3c8bSSascha Wildner nports = hub->nports;
163812bd3c8bSSascha Wildner for (x = 0; x != nports; x++) {
163912bd3c8bSSascha Wildner udev = usb_bus_port_get_device(hub->hubudev->bus,
164012bd3c8bSSascha Wildner hub->ports + x);
164112bd3c8bSSascha Wildner if (!udev) {
164212bd3c8bSSascha Wildner continue;
164312bd3c8bSSascha Wildner }
164412bd3c8bSSascha Wildner for (i = 0; i != USB_IFACE_MAX; i++) {
164512bd3c8bSSascha Wildner iface = usbd_get_iface(udev, i);
164612bd3c8bSSascha Wildner if (iface &&
164712bd3c8bSSascha Wildner (iface->subdev == child)) {
164812bd3c8bSSascha Wildner res->iface_index = i;
164912bd3c8bSSascha Wildner res->udev = udev;
165012bd3c8bSSascha Wildner res->portno = x + 1;
165112bd3c8bSSascha Wildner return;
165212bd3c8bSSascha Wildner }
165312bd3c8bSSascha Wildner }
165412bd3c8bSSascha Wildner }
165512bd3c8bSSascha Wildner res->iface_index = 0;
165612bd3c8bSSascha Wildner res->udev = NULL;
165712bd3c8bSSascha Wildner res->portno = 0;
165812bd3c8bSSascha Wildner }
165912bd3c8bSSascha Wildner
166012bd3c8bSSascha Wildner static int
uhub_child_location_string(device_t parent,device_t child,char * buf,size_t buflen)166112bd3c8bSSascha Wildner uhub_child_location_string(device_t parent, device_t child,
166212bd3c8bSSascha Wildner char *buf, size_t buflen)
166312bd3c8bSSascha Wildner {
166412bd3c8bSSascha Wildner struct uhub_softc *sc;
166512bd3c8bSSascha Wildner struct usb_hub *hub;
166612bd3c8bSSascha Wildner struct hub_result res;
166712bd3c8bSSascha Wildner
166812bd3c8bSSascha Wildner if (!device_is_attached(parent)) {
166912bd3c8bSSascha Wildner if (buflen)
167012bd3c8bSSascha Wildner buf[0] = 0;
167112bd3c8bSSascha Wildner return (0);
167212bd3c8bSSascha Wildner }
167312bd3c8bSSascha Wildner
167412bd3c8bSSascha Wildner sc = device_get_softc(parent);
167512bd3c8bSSascha Wildner hub = sc->sc_udev->hub;
167612bd3c8bSSascha Wildner
167712bd3c8bSSascha Wildner uhub_find_iface_index(hub, child, &res);
167812bd3c8bSSascha Wildner if (!res.udev) {
167912bd3c8bSSascha Wildner DPRINTF("device not on hub\n");
168012bd3c8bSSascha Wildner if (buflen) {
168112bd3c8bSSascha Wildner buf[0] = '\0';
168212bd3c8bSSascha Wildner }
168312bd3c8bSSascha Wildner goto done;
168412bd3c8bSSascha Wildner }
1685dd681da6SMatthew Dillon ksnprintf(buf, buflen, "bus=%u hubaddr=%u port=%u devaddr=%u"
1686dd681da6SMatthew Dillon " interface=%u"
1687dd681da6SMatthew Dillon #if USB_HAVE_UGEN
1688dd681da6SMatthew Dillon " ugen=%s"
1689dd681da6SMatthew Dillon #endif
1690dd681da6SMatthew Dillon , device_get_unit(res.udev->bus->bdev)
1691dd681da6SMatthew Dillon , (res.udev->parent_hub != NULL) ?
1692dd681da6SMatthew Dillon res.udev->parent_hub->device_index : 0
1693dd681da6SMatthew Dillon , res.portno, res.udev->device_index, res.iface_index
1694dd681da6SMatthew Dillon #if USB_HAVE_UGEN
1695dd681da6SMatthew Dillon , res.udev->ugen_name
1696dd681da6SMatthew Dillon #endif
1697dd681da6SMatthew Dillon );
169812bd3c8bSSascha Wildner done:
169912bd3c8bSSascha Wildner return (0);
170012bd3c8bSSascha Wildner }
170112bd3c8bSSascha Wildner
170212bd3c8bSSascha Wildner static int
uhub_child_pnpinfo_string(device_t parent,device_t child,char * buf,size_t buflen)170312bd3c8bSSascha Wildner uhub_child_pnpinfo_string(device_t parent, device_t child,
170412bd3c8bSSascha Wildner char *buf, size_t buflen)
170512bd3c8bSSascha Wildner {
170612bd3c8bSSascha Wildner struct uhub_softc *sc;
170712bd3c8bSSascha Wildner struct usb_hub *hub;
170812bd3c8bSSascha Wildner struct usb_interface *iface;
170912bd3c8bSSascha Wildner struct hub_result res;
171012bd3c8bSSascha Wildner
171112bd3c8bSSascha Wildner if (!device_is_attached(parent)) {
171212bd3c8bSSascha Wildner if (buflen)
171312bd3c8bSSascha Wildner buf[0] = 0;
171412bd3c8bSSascha Wildner return (0);
171512bd3c8bSSascha Wildner }
171612bd3c8bSSascha Wildner
171712bd3c8bSSascha Wildner sc = device_get_softc(parent);
171812bd3c8bSSascha Wildner hub = sc->sc_udev->hub;
171912bd3c8bSSascha Wildner
172012bd3c8bSSascha Wildner uhub_find_iface_index(hub, child, &res);
172112bd3c8bSSascha Wildner if (!res.udev) {
172212bd3c8bSSascha Wildner DPRINTF("device not on hub\n");
172312bd3c8bSSascha Wildner if (buflen) {
172412bd3c8bSSascha Wildner buf[0] = '\0';
172512bd3c8bSSascha Wildner }
172612bd3c8bSSascha Wildner goto done;
172712bd3c8bSSascha Wildner }
172812bd3c8bSSascha Wildner iface = usbd_get_iface(res.udev, res.iface_index);
172912bd3c8bSSascha Wildner if (iface && iface->idesc) {
1730722d05c3SSascha Wildner ksnprintf(buf, buflen, "vendor=0x%04x product=0x%04x "
173112bd3c8bSSascha Wildner "devclass=0x%02x devsubclass=0x%02x "
173212bd3c8bSSascha Wildner "sernum=\"%s\" "
173312bd3c8bSSascha Wildner "release=0x%04x "
173412bd3c8bSSascha Wildner "mode=%s "
173512bd3c8bSSascha Wildner "intclass=0x%02x intsubclass=0x%02x "
173612bd3c8bSSascha Wildner "intprotocol=0x%02x" "%s%s",
173712bd3c8bSSascha Wildner UGETW(res.udev->ddesc.idVendor),
173812bd3c8bSSascha Wildner UGETW(res.udev->ddesc.idProduct),
173912bd3c8bSSascha Wildner res.udev->ddesc.bDeviceClass,
174012bd3c8bSSascha Wildner res.udev->ddesc.bDeviceSubClass,
174112bd3c8bSSascha Wildner usb_get_serial(res.udev),
174212bd3c8bSSascha Wildner UGETW(res.udev->ddesc.bcdDevice),
174312bd3c8bSSascha Wildner (res.udev->flags.usb_mode == USB_MODE_HOST) ? "host" : "device",
174412bd3c8bSSascha Wildner iface->idesc->bInterfaceClass,
174512bd3c8bSSascha Wildner iface->idesc->bInterfaceSubClass,
174612bd3c8bSSascha Wildner iface->idesc->bInterfaceProtocol,
174712bd3c8bSSascha Wildner iface->pnpinfo ? " " : "",
174812bd3c8bSSascha Wildner iface->pnpinfo ? iface->pnpinfo : "");
174912bd3c8bSSascha Wildner } else {
175012bd3c8bSSascha Wildner if (buflen) {
175112bd3c8bSSascha Wildner buf[0] = '\0';
175212bd3c8bSSascha Wildner }
175312bd3c8bSSascha Wildner goto done;
175412bd3c8bSSascha Wildner }
175512bd3c8bSSascha Wildner done:
175612bd3c8bSSascha Wildner return (0);
175712bd3c8bSSascha Wildner }
175812bd3c8bSSascha Wildner
175912bd3c8bSSascha Wildner /*
176012bd3c8bSSascha Wildner * The USB Transaction Translator:
176112bd3c8bSSascha Wildner * ===============================
176212bd3c8bSSascha Wildner *
176312bd3c8bSSascha Wildner * When doing LOW- and FULL-speed USB transfers accross a HIGH-speed
176412bd3c8bSSascha Wildner * USB HUB, bandwidth must be allocated for ISOCHRONOUS and INTERRUPT
176512bd3c8bSSascha Wildner * USB transfers. To utilize bandwidth dynamically the "scatter and
176612bd3c8bSSascha Wildner * gather" principle must be applied. This means that bandwidth must
176712bd3c8bSSascha Wildner * be divided into equal parts of bandwidth. With regard to USB all
176812bd3c8bSSascha Wildner * data is transferred in smaller packets with length
176912bd3c8bSSascha Wildner * "wMaxPacketSize". The problem however is that "wMaxPacketSize" is
177012bd3c8bSSascha Wildner * not a constant!
177112bd3c8bSSascha Wildner *
177212bd3c8bSSascha Wildner * The bandwidth scheduler which I have implemented will simply pack
177312bd3c8bSSascha Wildner * the USB transfers back to back until there is no more space in the
177412bd3c8bSSascha Wildner * schedule. Out of the 8 microframes which the USB 2.0 standard
177512bd3c8bSSascha Wildner * provides, only 6 are available for non-HIGH-speed devices. I have
177612bd3c8bSSascha Wildner * reserved the first 4 microframes for ISOCHRONOUS transfers. The
177712bd3c8bSSascha Wildner * last 2 microframes I have reserved for INTERRUPT transfers. Without
177812bd3c8bSSascha Wildner * this division, it is very difficult to allocate and free bandwidth
177912bd3c8bSSascha Wildner * dynamically.
178012bd3c8bSSascha Wildner *
178112bd3c8bSSascha Wildner * NOTE about the Transaction Translator in USB HUBs:
178212bd3c8bSSascha Wildner *
178312bd3c8bSSascha Wildner * USB HUBs have a very simple Transaction Translator, that will
178412bd3c8bSSascha Wildner * simply pipeline all the SPLIT transactions. That means that the
178512bd3c8bSSascha Wildner * transactions will be executed in the order they are queued!
178612bd3c8bSSascha Wildner *
178712bd3c8bSSascha Wildner */
178812bd3c8bSSascha Wildner
178912bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
179012bd3c8bSSascha Wildner * usb_intr_find_best_slot
179112bd3c8bSSascha Wildner *
179212bd3c8bSSascha Wildner * Return value:
179312bd3c8bSSascha Wildner * The best Transaction Translation slot for an interrupt endpoint.
179412bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
179512bd3c8bSSascha Wildner static uint8_t
usb_intr_find_best_slot(usb_size_t * ptr,uint8_t start,uint8_t end,uint8_t mask)179612bd3c8bSSascha Wildner usb_intr_find_best_slot(usb_size_t *ptr, uint8_t start,
179712bd3c8bSSascha Wildner uint8_t end, uint8_t mask)
179812bd3c8bSSascha Wildner {
17995e41ab93SMarkus Pfeiffer usb_size_t min = (usb_size_t)-1;
180012bd3c8bSSascha Wildner usb_size_t sum;
180112bd3c8bSSascha Wildner uint8_t x;
180212bd3c8bSSascha Wildner uint8_t y;
180312bd3c8bSSascha Wildner uint8_t z;
180412bd3c8bSSascha Wildner
180512bd3c8bSSascha Wildner y = 0;
180612bd3c8bSSascha Wildner
180712bd3c8bSSascha Wildner /* find the last slot with lesser used bandwidth */
180812bd3c8bSSascha Wildner
180912bd3c8bSSascha Wildner for (x = start; x < end; x++) {
181012bd3c8bSSascha Wildner
181112bd3c8bSSascha Wildner sum = 0;
181212bd3c8bSSascha Wildner
181312bd3c8bSSascha Wildner /* compute sum of bandwidth */
181412bd3c8bSSascha Wildner for (z = x; z < end; z++) {
181512bd3c8bSSascha Wildner if (mask & (1U << (z - x)))
181612bd3c8bSSascha Wildner sum += ptr[z];
181712bd3c8bSSascha Wildner }
181812bd3c8bSSascha Wildner
181912bd3c8bSSascha Wildner /* check if the current multi-slot is more optimal */
182012bd3c8bSSascha Wildner if (min >= sum) {
182112bd3c8bSSascha Wildner min = sum;
182212bd3c8bSSascha Wildner y = x;
182312bd3c8bSSascha Wildner }
182412bd3c8bSSascha Wildner
182512bd3c8bSSascha Wildner /* check if the mask is about to be shifted out */
182612bd3c8bSSascha Wildner if (mask & (1U << (end - 1 - x)))
182712bd3c8bSSascha Wildner break;
182812bd3c8bSSascha Wildner }
182912bd3c8bSSascha Wildner return (y);
183012bd3c8bSSascha Wildner }
183112bd3c8bSSascha Wildner
183212bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
183312bd3c8bSSascha Wildner * usb_hs_bandwidth_adjust
183412bd3c8bSSascha Wildner *
183512bd3c8bSSascha Wildner * This function will update the bandwith usage for the microframe
183612bd3c8bSSascha Wildner * having index "slot" by "len" bytes. "len" can be negative. If the
183712bd3c8bSSascha Wildner * "slot" argument is greater or equal to "USB_HS_MICRO_FRAMES_MAX"
183812bd3c8bSSascha Wildner * the "slot" argument will be replaced by the slot having least used
183912bd3c8bSSascha Wildner * bandwidth. The "mask" argument is used for multi-slot allocations.
184012bd3c8bSSascha Wildner *
184112bd3c8bSSascha Wildner * Returns:
184212bd3c8bSSascha Wildner * The slot in which the bandwidth update was done: 0..7
184312bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
184412bd3c8bSSascha Wildner static uint8_t
usb_hs_bandwidth_adjust(struct usb_device * udev,int16_t len,uint8_t slot,uint8_t mask)184512bd3c8bSSascha Wildner usb_hs_bandwidth_adjust(struct usb_device *udev, int16_t len,
184612bd3c8bSSascha Wildner uint8_t slot, uint8_t mask)
184712bd3c8bSSascha Wildner {
184812bd3c8bSSascha Wildner struct usb_bus *bus = udev->bus;
184912bd3c8bSSascha Wildner struct usb_hub *hub;
185012bd3c8bSSascha Wildner enum usb_dev_speed speed;
185112bd3c8bSSascha Wildner uint8_t x;
185212bd3c8bSSascha Wildner
1853722d05c3SSascha Wildner USB_BUS_LOCK_ASSERT(bus);
185463da4a34SSascha Wildner
185512bd3c8bSSascha Wildner speed = usbd_get_speed(udev);
185612bd3c8bSSascha Wildner
185712bd3c8bSSascha Wildner switch (speed) {
185812bd3c8bSSascha Wildner case USB_SPEED_LOW:
185912bd3c8bSSascha Wildner case USB_SPEED_FULL:
186012bd3c8bSSascha Wildner if (speed == USB_SPEED_LOW) {
186112bd3c8bSSascha Wildner len *= 8;
186212bd3c8bSSascha Wildner }
186312bd3c8bSSascha Wildner /*
186412bd3c8bSSascha Wildner * The Host Controller Driver should have
186512bd3c8bSSascha Wildner * performed checks so that the lookup
186612bd3c8bSSascha Wildner * below does not result in a NULL pointer
186712bd3c8bSSascha Wildner * access.
186812bd3c8bSSascha Wildner */
186912bd3c8bSSascha Wildner
187012bd3c8bSSascha Wildner hub = udev->parent_hs_hub->hub;
187112bd3c8bSSascha Wildner if (slot >= USB_HS_MICRO_FRAMES_MAX) {
187212bd3c8bSSascha Wildner slot = usb_intr_find_best_slot(hub->uframe_usage,
187312bd3c8bSSascha Wildner USB_FS_ISOC_UFRAME_MAX, 6, mask);
187412bd3c8bSSascha Wildner }
187512bd3c8bSSascha Wildner for (x = slot; x < 8; x++) {
187612bd3c8bSSascha Wildner if (mask & (1U << (x - slot))) {
187712bd3c8bSSascha Wildner hub->uframe_usage[x] += len;
187812bd3c8bSSascha Wildner bus->uframe_usage[x] += len;
187912bd3c8bSSascha Wildner }
188012bd3c8bSSascha Wildner }
188112bd3c8bSSascha Wildner break;
188212bd3c8bSSascha Wildner default:
188312bd3c8bSSascha Wildner if (slot >= USB_HS_MICRO_FRAMES_MAX) {
188412bd3c8bSSascha Wildner slot = usb_intr_find_best_slot(bus->uframe_usage, 0,
188512bd3c8bSSascha Wildner USB_HS_MICRO_FRAMES_MAX, mask);
188612bd3c8bSSascha Wildner }
188712bd3c8bSSascha Wildner for (x = slot; x < 8; x++) {
188812bd3c8bSSascha Wildner if (mask & (1U << (x - slot))) {
188912bd3c8bSSascha Wildner bus->uframe_usage[x] += len;
189012bd3c8bSSascha Wildner }
189112bd3c8bSSascha Wildner }
189212bd3c8bSSascha Wildner break;
189312bd3c8bSSascha Wildner }
189412bd3c8bSSascha Wildner return (slot);
189512bd3c8bSSascha Wildner }
189612bd3c8bSSascha Wildner
189712bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
189812bd3c8bSSascha Wildner * usb_hs_bandwidth_alloc
189912bd3c8bSSascha Wildner *
190012bd3c8bSSascha Wildner * This function is a wrapper function for "usb_hs_bandwidth_adjust()".
190112bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
190212bd3c8bSSascha Wildner void
usb_hs_bandwidth_alloc(struct usb_xfer * xfer)190312bd3c8bSSascha Wildner usb_hs_bandwidth_alloc(struct usb_xfer *xfer)
190412bd3c8bSSascha Wildner {
190512bd3c8bSSascha Wildner struct usb_device *udev;
190612bd3c8bSSascha Wildner uint8_t slot;
190712bd3c8bSSascha Wildner uint8_t mask;
190812bd3c8bSSascha Wildner uint8_t speed;
190912bd3c8bSSascha Wildner
191012bd3c8bSSascha Wildner udev = xfer->xroot->udev;
191112bd3c8bSSascha Wildner
191212bd3c8bSSascha Wildner if (udev->flags.usb_mode != USB_MODE_HOST)
191312bd3c8bSSascha Wildner return; /* not supported */
191412bd3c8bSSascha Wildner
191512bd3c8bSSascha Wildner xfer->endpoint->refcount_bw++;
191612bd3c8bSSascha Wildner if (xfer->endpoint->refcount_bw != 1)
191712bd3c8bSSascha Wildner return; /* already allocated */
191812bd3c8bSSascha Wildner
191912bd3c8bSSascha Wildner speed = usbd_get_speed(udev);
192012bd3c8bSSascha Wildner
192112bd3c8bSSascha Wildner switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) {
192212bd3c8bSSascha Wildner case UE_INTERRUPT:
192312bd3c8bSSascha Wildner /* allocate a microframe slot */
192412bd3c8bSSascha Wildner
192512bd3c8bSSascha Wildner mask = 0x01;
192612bd3c8bSSascha Wildner slot = usb_hs_bandwidth_adjust(udev,
192712bd3c8bSSascha Wildner xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX, mask);
192812bd3c8bSSascha Wildner
192912bd3c8bSSascha Wildner xfer->endpoint->usb_uframe = slot;
193012bd3c8bSSascha Wildner xfer->endpoint->usb_smask = mask << slot;
193112bd3c8bSSascha Wildner
193212bd3c8bSSascha Wildner if ((speed != USB_SPEED_FULL) &&
193312bd3c8bSSascha Wildner (speed != USB_SPEED_LOW)) {
193412bd3c8bSSascha Wildner xfer->endpoint->usb_cmask = 0x00 ;
193512bd3c8bSSascha Wildner } else {
193612bd3c8bSSascha Wildner xfer->endpoint->usb_cmask = (-(0x04 << slot)) & 0xFE;
193712bd3c8bSSascha Wildner }
193812bd3c8bSSascha Wildner break;
193912bd3c8bSSascha Wildner
194012bd3c8bSSascha Wildner case UE_ISOCHRONOUS:
194112bd3c8bSSascha Wildner switch (usbd_xfer_get_fps_shift(xfer)) {
194212bd3c8bSSascha Wildner case 0:
194312bd3c8bSSascha Wildner mask = 0xFF;
194412bd3c8bSSascha Wildner break;
194512bd3c8bSSascha Wildner case 1:
194612bd3c8bSSascha Wildner mask = 0x55;
194712bd3c8bSSascha Wildner break;
194812bd3c8bSSascha Wildner case 2:
194912bd3c8bSSascha Wildner mask = 0x11;
195012bd3c8bSSascha Wildner break;
195112bd3c8bSSascha Wildner default:
195212bd3c8bSSascha Wildner mask = 0x01;
195312bd3c8bSSascha Wildner break;
195412bd3c8bSSascha Wildner }
195512bd3c8bSSascha Wildner
195612bd3c8bSSascha Wildner /* allocate a microframe multi-slot */
195712bd3c8bSSascha Wildner
195812bd3c8bSSascha Wildner slot = usb_hs_bandwidth_adjust(udev,
195912bd3c8bSSascha Wildner xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX, mask);
196012bd3c8bSSascha Wildner
196112bd3c8bSSascha Wildner xfer->endpoint->usb_uframe = slot;
196212bd3c8bSSascha Wildner xfer->endpoint->usb_cmask = 0;
196312bd3c8bSSascha Wildner xfer->endpoint->usb_smask = mask << slot;
196412bd3c8bSSascha Wildner break;
196512bd3c8bSSascha Wildner
196612bd3c8bSSascha Wildner default:
196712bd3c8bSSascha Wildner xfer->endpoint->usb_uframe = 0;
196812bd3c8bSSascha Wildner xfer->endpoint->usb_cmask = 0;
196912bd3c8bSSascha Wildner xfer->endpoint->usb_smask = 0;
197012bd3c8bSSascha Wildner break;
197112bd3c8bSSascha Wildner }
197212bd3c8bSSascha Wildner
197312bd3c8bSSascha Wildner DPRINTFN(11, "slot=%d, mask=0x%02x\n",
197412bd3c8bSSascha Wildner xfer->endpoint->usb_uframe,
197512bd3c8bSSascha Wildner xfer->endpoint->usb_smask >> xfer->endpoint->usb_uframe);
197612bd3c8bSSascha Wildner }
197712bd3c8bSSascha Wildner
197812bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
197912bd3c8bSSascha Wildner * usb_hs_bandwidth_free
198012bd3c8bSSascha Wildner *
198112bd3c8bSSascha Wildner * This function is a wrapper function for "usb_hs_bandwidth_adjust()".
198212bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
198312bd3c8bSSascha Wildner void
usb_hs_bandwidth_free(struct usb_xfer * xfer)198412bd3c8bSSascha Wildner usb_hs_bandwidth_free(struct usb_xfer *xfer)
198512bd3c8bSSascha Wildner {
198612bd3c8bSSascha Wildner struct usb_device *udev;
198712bd3c8bSSascha Wildner uint8_t slot;
198812bd3c8bSSascha Wildner uint8_t mask;
198912bd3c8bSSascha Wildner
199012bd3c8bSSascha Wildner udev = xfer->xroot->udev;
199112bd3c8bSSascha Wildner
199212bd3c8bSSascha Wildner if (udev->flags.usb_mode != USB_MODE_HOST)
199312bd3c8bSSascha Wildner return; /* not supported */
199412bd3c8bSSascha Wildner
199512bd3c8bSSascha Wildner xfer->endpoint->refcount_bw--;
199612bd3c8bSSascha Wildner if (xfer->endpoint->refcount_bw != 0)
199712bd3c8bSSascha Wildner return; /* still allocated */
199812bd3c8bSSascha Wildner
199912bd3c8bSSascha Wildner switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) {
200012bd3c8bSSascha Wildner case UE_INTERRUPT:
200112bd3c8bSSascha Wildner case UE_ISOCHRONOUS:
200212bd3c8bSSascha Wildner
200312bd3c8bSSascha Wildner slot = xfer->endpoint->usb_uframe;
200412bd3c8bSSascha Wildner mask = xfer->endpoint->usb_smask;
200512bd3c8bSSascha Wildner
200612bd3c8bSSascha Wildner /* free microframe slot(s): */
200712bd3c8bSSascha Wildner usb_hs_bandwidth_adjust(udev,
200812bd3c8bSSascha Wildner -xfer->max_frame_size, slot, mask >> slot);
200912bd3c8bSSascha Wildner
201012bd3c8bSSascha Wildner DPRINTFN(11, "slot=%d, mask=0x%02x\n",
201112bd3c8bSSascha Wildner slot, mask >> slot);
201212bd3c8bSSascha Wildner
201312bd3c8bSSascha Wildner xfer->endpoint->usb_uframe = 0;
201412bd3c8bSSascha Wildner xfer->endpoint->usb_cmask = 0;
201512bd3c8bSSascha Wildner xfer->endpoint->usb_smask = 0;
201612bd3c8bSSascha Wildner break;
201712bd3c8bSSascha Wildner
201812bd3c8bSSascha Wildner default:
201912bd3c8bSSascha Wildner break;
202012bd3c8bSSascha Wildner }
202112bd3c8bSSascha Wildner }
202212bd3c8bSSascha Wildner
202312bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
202412bd3c8bSSascha Wildner * usb_isoc_time_expand
202512bd3c8bSSascha Wildner *
202612bd3c8bSSascha Wildner * This function will expand the time counter from 7-bit to 16-bit.
202712bd3c8bSSascha Wildner *
202812bd3c8bSSascha Wildner * Returns:
202912bd3c8bSSascha Wildner * 16-bit isochronous time counter.
203012bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
203112bd3c8bSSascha Wildner uint16_t
usb_isoc_time_expand(struct usb_bus * bus,uint16_t isoc_time_curr)203212bd3c8bSSascha Wildner usb_isoc_time_expand(struct usb_bus *bus, uint16_t isoc_time_curr)
203312bd3c8bSSascha Wildner {
203412bd3c8bSSascha Wildner uint16_t rem;
203512bd3c8bSSascha Wildner
2036722d05c3SSascha Wildner USB_BUS_LOCK_ASSERT(bus);
203712bd3c8bSSascha Wildner
203812bd3c8bSSascha Wildner rem = bus->isoc_time_last & (USB_ISOC_TIME_MAX - 1);
203912bd3c8bSSascha Wildner
204012bd3c8bSSascha Wildner isoc_time_curr &= (USB_ISOC_TIME_MAX - 1);
204112bd3c8bSSascha Wildner
204212bd3c8bSSascha Wildner if (isoc_time_curr < rem) {
204312bd3c8bSSascha Wildner /* the time counter wrapped around */
204412bd3c8bSSascha Wildner bus->isoc_time_last += USB_ISOC_TIME_MAX;
204512bd3c8bSSascha Wildner }
204612bd3c8bSSascha Wildner /* update the remainder */
204712bd3c8bSSascha Wildner
204812bd3c8bSSascha Wildner bus->isoc_time_last &= ~(USB_ISOC_TIME_MAX - 1);
204912bd3c8bSSascha Wildner bus->isoc_time_last |= isoc_time_curr;
205012bd3c8bSSascha Wildner
205112bd3c8bSSascha Wildner return (bus->isoc_time_last);
205212bd3c8bSSascha Wildner }
205312bd3c8bSSascha Wildner
205412bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
20555e41ab93SMarkus Pfeiffer * usbd_fs_isoc_schedule_alloc_slot
205612bd3c8bSSascha Wildner *
205712bd3c8bSSascha Wildner * This function will allocate bandwidth for an isochronous FULL speed
20585e41ab93SMarkus Pfeiffer * transaction in the FULL speed schedule.
205912bd3c8bSSascha Wildner *
206012bd3c8bSSascha Wildner * Returns:
20615e41ab93SMarkus Pfeiffer * <8: Success
206212bd3c8bSSascha Wildner * Else: Error
206312bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
206412bd3c8bSSascha Wildner #if USB_HAVE_TT_SUPPORT
206512bd3c8bSSascha Wildner uint8_t
usbd_fs_isoc_schedule_alloc_slot(struct usb_xfer * isoc_xfer,uint16_t isoc_time)20665e41ab93SMarkus Pfeiffer usbd_fs_isoc_schedule_alloc_slot(struct usb_xfer *isoc_xfer, uint16_t isoc_time)
206712bd3c8bSSascha Wildner {
20685e41ab93SMarkus Pfeiffer struct usb_xfer *xfer;
20695e41ab93SMarkus Pfeiffer struct usb_xfer *pipe_xfer;
20705e41ab93SMarkus Pfeiffer struct usb_bus *bus;
20715e41ab93SMarkus Pfeiffer usb_frlength_t len;
20725e41ab93SMarkus Pfeiffer usb_frlength_t data_len;
20735e41ab93SMarkus Pfeiffer uint16_t delta;
20745e41ab93SMarkus Pfeiffer uint16_t slot;
20755e41ab93SMarkus Pfeiffer uint8_t retval;
207612bd3c8bSSascha Wildner
20775e41ab93SMarkus Pfeiffer data_len = 0;
20785e41ab93SMarkus Pfeiffer slot = 0;
207912bd3c8bSSascha Wildner
20805e41ab93SMarkus Pfeiffer bus = isoc_xfer->xroot->bus;
20815e41ab93SMarkus Pfeiffer
20825e41ab93SMarkus Pfeiffer TAILQ_FOREACH(xfer, &bus->intr_q.head, wait_entry) {
20835e41ab93SMarkus Pfeiffer
20845e41ab93SMarkus Pfeiffer /* skip self, if any */
20855e41ab93SMarkus Pfeiffer
20865e41ab93SMarkus Pfeiffer if (xfer == isoc_xfer)
20875e41ab93SMarkus Pfeiffer continue;
20885e41ab93SMarkus Pfeiffer
20895e41ab93SMarkus Pfeiffer /* check if this USB transfer is going through the same TT */
20905e41ab93SMarkus Pfeiffer
20915e41ab93SMarkus Pfeiffer if (xfer->xroot->udev->parent_hs_hub !=
20925e41ab93SMarkus Pfeiffer isoc_xfer->xroot->udev->parent_hs_hub) {
20935e41ab93SMarkus Pfeiffer continue;
20945e41ab93SMarkus Pfeiffer }
20955e41ab93SMarkus Pfeiffer if ((isoc_xfer->xroot->udev->parent_hs_hub->
20965e41ab93SMarkus Pfeiffer ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) &&
20975e41ab93SMarkus Pfeiffer (xfer->xroot->udev->hs_port_no !=
20985e41ab93SMarkus Pfeiffer isoc_xfer->xroot->udev->hs_port_no)) {
20995e41ab93SMarkus Pfeiffer continue;
21005e41ab93SMarkus Pfeiffer }
21015e41ab93SMarkus Pfeiffer if (xfer->endpoint->methods != isoc_xfer->endpoint->methods)
21025e41ab93SMarkus Pfeiffer continue;
21035e41ab93SMarkus Pfeiffer
21045e41ab93SMarkus Pfeiffer /* check if isoc_time is part of this transfer */
21055e41ab93SMarkus Pfeiffer
21065e41ab93SMarkus Pfeiffer delta = xfer->isoc_time_complete - isoc_time;
21075e41ab93SMarkus Pfeiffer if (delta > 0 && delta <= xfer->nframes) {
21085e41ab93SMarkus Pfeiffer delta = xfer->nframes - delta;
21095e41ab93SMarkus Pfeiffer
21105e41ab93SMarkus Pfeiffer len = xfer->frlengths[delta];
211112bd3c8bSSascha Wildner len += 8;
211212bd3c8bSSascha Wildner len *= 7;
211312bd3c8bSSascha Wildner len /= 6;
211412bd3c8bSSascha Wildner
21155e41ab93SMarkus Pfeiffer data_len += len;
211612bd3c8bSSascha Wildner }
211712bd3c8bSSascha Wildner
21185e41ab93SMarkus Pfeiffer /*
21195e41ab93SMarkus Pfeiffer * Check double buffered transfers. Only stream ID
21205e41ab93SMarkus Pfeiffer * equal to zero is valid here!
21215e41ab93SMarkus Pfeiffer */
21225e41ab93SMarkus Pfeiffer TAILQ_FOREACH(pipe_xfer, &xfer->endpoint->endpoint_q[0].head,
21235e41ab93SMarkus Pfeiffer wait_entry) {
21245e41ab93SMarkus Pfeiffer
21255e41ab93SMarkus Pfeiffer /* skip self, if any */
21265e41ab93SMarkus Pfeiffer
21275e41ab93SMarkus Pfeiffer if (pipe_xfer == isoc_xfer)
21285e41ab93SMarkus Pfeiffer continue;
21295e41ab93SMarkus Pfeiffer
21305e41ab93SMarkus Pfeiffer /* check if isoc_time is part of this transfer */
21315e41ab93SMarkus Pfeiffer
21325e41ab93SMarkus Pfeiffer delta = pipe_xfer->isoc_time_complete - isoc_time;
21335e41ab93SMarkus Pfeiffer if (delta > 0 && delta <= pipe_xfer->nframes) {
21345e41ab93SMarkus Pfeiffer delta = pipe_xfer->nframes - delta;
21355e41ab93SMarkus Pfeiffer
21365e41ab93SMarkus Pfeiffer len = pipe_xfer->frlengths[delta];
21375e41ab93SMarkus Pfeiffer len += 8;
21385e41ab93SMarkus Pfeiffer len *= 7;
21395e41ab93SMarkus Pfeiffer len /= 6;
21405e41ab93SMarkus Pfeiffer
21415e41ab93SMarkus Pfeiffer data_len += len;
214212bd3c8bSSascha Wildner }
21435e41ab93SMarkus Pfeiffer }
21445e41ab93SMarkus Pfeiffer }
21455e41ab93SMarkus Pfeiffer
21465e41ab93SMarkus Pfeiffer while (data_len >= USB_FS_BYTES_PER_HS_UFRAME) {
21475e41ab93SMarkus Pfeiffer data_len -= USB_FS_BYTES_PER_HS_UFRAME;
21485e41ab93SMarkus Pfeiffer slot++;
21495e41ab93SMarkus Pfeiffer }
21505e41ab93SMarkus Pfeiffer
21515e41ab93SMarkus Pfeiffer /* check for overflow */
21525e41ab93SMarkus Pfeiffer
21535e41ab93SMarkus Pfeiffer if (slot >= USB_FS_ISOC_UFRAME_MAX)
21545e41ab93SMarkus Pfeiffer return (255);
21555e41ab93SMarkus Pfeiffer
21565e41ab93SMarkus Pfeiffer retval = slot;
21575e41ab93SMarkus Pfeiffer
21585e41ab93SMarkus Pfeiffer delta = isoc_xfer->isoc_time_complete - isoc_time;
21595e41ab93SMarkus Pfeiffer if (delta > 0 && delta <= isoc_xfer->nframes) {
21605e41ab93SMarkus Pfeiffer delta = isoc_xfer->nframes - delta;
21615e41ab93SMarkus Pfeiffer
21625e41ab93SMarkus Pfeiffer len = isoc_xfer->frlengths[delta];
21635e41ab93SMarkus Pfeiffer len += 8;
21645e41ab93SMarkus Pfeiffer len *= 7;
21655e41ab93SMarkus Pfeiffer len /= 6;
21665e41ab93SMarkus Pfeiffer
21675e41ab93SMarkus Pfeiffer data_len += len;
21685e41ab93SMarkus Pfeiffer }
21695e41ab93SMarkus Pfeiffer
21705e41ab93SMarkus Pfeiffer while (data_len >= USB_FS_BYTES_PER_HS_UFRAME) {
21715e41ab93SMarkus Pfeiffer data_len -= USB_FS_BYTES_PER_HS_UFRAME;
21725e41ab93SMarkus Pfeiffer slot++;
21735e41ab93SMarkus Pfeiffer }
21745e41ab93SMarkus Pfeiffer
21755e41ab93SMarkus Pfeiffer /* check for overflow */
21765e41ab93SMarkus Pfeiffer
21775e41ab93SMarkus Pfeiffer if (slot >= USB_FS_ISOC_UFRAME_MAX)
21785e41ab93SMarkus Pfeiffer return (255);
21795e41ab93SMarkus Pfeiffer
21805e41ab93SMarkus Pfeiffer return (retval);
218112bd3c8bSSascha Wildner }
218212bd3c8bSSascha Wildner #endif
218312bd3c8bSSascha Wildner
218412bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
218512bd3c8bSSascha Wildner * usb_bus_port_get_device
218612bd3c8bSSascha Wildner *
218712bd3c8bSSascha Wildner * This function is NULL safe.
218812bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
218912bd3c8bSSascha Wildner struct usb_device *
usb_bus_port_get_device(struct usb_bus * bus,struct usb_port * up)219012bd3c8bSSascha Wildner usb_bus_port_get_device(struct usb_bus *bus, struct usb_port *up)
219112bd3c8bSSascha Wildner {
219212bd3c8bSSascha Wildner if ((bus == NULL) || (up == NULL)) {
219312bd3c8bSSascha Wildner /* be NULL safe */
219412bd3c8bSSascha Wildner return (NULL);
219512bd3c8bSSascha Wildner }
219612bd3c8bSSascha Wildner if (up->device_index == 0) {
219712bd3c8bSSascha Wildner /* nothing to do */
219812bd3c8bSSascha Wildner return (NULL);
219912bd3c8bSSascha Wildner }
220012bd3c8bSSascha Wildner return (bus->devices[up->device_index]);
220112bd3c8bSSascha Wildner }
220212bd3c8bSSascha Wildner
220312bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
220412bd3c8bSSascha Wildner * usb_bus_port_set_device
220512bd3c8bSSascha Wildner *
220612bd3c8bSSascha Wildner * This function is NULL safe.
220712bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
220812bd3c8bSSascha Wildner void
usb_bus_port_set_device(struct usb_bus * bus,struct usb_port * up,struct usb_device * udev,uint8_t device_index)220912bd3c8bSSascha Wildner usb_bus_port_set_device(struct usb_bus *bus, struct usb_port *up,
221012bd3c8bSSascha Wildner struct usb_device *udev, uint8_t device_index)
221112bd3c8bSSascha Wildner {
221212bd3c8bSSascha Wildner if (bus == NULL) {
221312bd3c8bSSascha Wildner /* be NULL safe */
221412bd3c8bSSascha Wildner return;
221512bd3c8bSSascha Wildner }
221612bd3c8bSSascha Wildner /*
221712bd3c8bSSascha Wildner * There is only one case where we don't
221812bd3c8bSSascha Wildner * have an USB port, and that is the Root Hub!
221912bd3c8bSSascha Wildner */
222012bd3c8bSSascha Wildner if (up) {
222112bd3c8bSSascha Wildner if (udev) {
222212bd3c8bSSascha Wildner up->device_index = device_index;
222312bd3c8bSSascha Wildner } else {
222412bd3c8bSSascha Wildner device_index = up->device_index;
222512bd3c8bSSascha Wildner up->device_index = 0;
222612bd3c8bSSascha Wildner }
222712bd3c8bSSascha Wildner }
222812bd3c8bSSascha Wildner /*
222912bd3c8bSSascha Wildner * Make relationships to our new device
223012bd3c8bSSascha Wildner */
223112bd3c8bSSascha Wildner if (device_index != 0) {
223212bd3c8bSSascha Wildner #if USB_HAVE_UGEN
2233722d05c3SSascha Wildner lockmgr(&usb_ref_lock, LK_EXCLUSIVE);
223412bd3c8bSSascha Wildner #endif
223512bd3c8bSSascha Wildner bus->devices[device_index] = udev;
223612bd3c8bSSascha Wildner #if USB_HAVE_UGEN
2237722d05c3SSascha Wildner lockmgr(&usb_ref_lock, LK_RELEASE);
223812bd3c8bSSascha Wildner #endif
223912bd3c8bSSascha Wildner }
224012bd3c8bSSascha Wildner /*
224112bd3c8bSSascha Wildner * Debug print
224212bd3c8bSSascha Wildner */
224312bd3c8bSSascha Wildner DPRINTFN(2, "bus %p devices[%u] = %p\n", bus, device_index, udev);
224412bd3c8bSSascha Wildner }
224512bd3c8bSSascha Wildner
224612bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
224712bd3c8bSSascha Wildner * usb_needs_explore
224812bd3c8bSSascha Wildner *
224912bd3c8bSSascha Wildner * This functions is called when the USB event thread needs to run.
225012bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
225112bd3c8bSSascha Wildner void
usb_needs_explore(struct usb_bus * bus,uint8_t do_probe)225212bd3c8bSSascha Wildner usb_needs_explore(struct usb_bus *bus, uint8_t do_probe)
225312bd3c8bSSascha Wildner {
225412bd3c8bSSascha Wildner uint8_t do_unlock;
225512bd3c8bSSascha Wildner
225612bd3c8bSSascha Wildner DPRINTF("\n");
225712bd3c8bSSascha Wildner
225812bd3c8bSSascha Wildner if (bus == NULL) {
225912bd3c8bSSascha Wildner DPRINTF("No bus pointer!\n");
226012bd3c8bSSascha Wildner return;
226112bd3c8bSSascha Wildner }
226212bd3c8bSSascha Wildner if ((bus->devices == NULL) ||
226312bd3c8bSSascha Wildner (bus->devices[USB_ROOT_HUB_ADDR] == NULL)) {
226412bd3c8bSSascha Wildner DPRINTF("No root HUB\n");
226512bd3c8bSSascha Wildner return;
226612bd3c8bSSascha Wildner }
22673a76bbe8SSascha Wildner if (lockowned(&bus->bus_lock) != 0) {
226812bd3c8bSSascha Wildner do_unlock = 0;
226912bd3c8bSSascha Wildner } else {
227012bd3c8bSSascha Wildner USB_BUS_LOCK(bus);
227112bd3c8bSSascha Wildner do_unlock = 1;
227212bd3c8bSSascha Wildner }
227312bd3c8bSSascha Wildner if (do_probe) {
227412bd3c8bSSascha Wildner bus->do_probe = 1;
227512bd3c8bSSascha Wildner }
227657bed822SMarkus Pfeiffer if (usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus),
227712bd3c8bSSascha Wildner &bus->explore_msg[0], &bus->explore_msg[1])) {
227812bd3c8bSSascha Wildner /* ignore */
227912bd3c8bSSascha Wildner }
228012bd3c8bSSascha Wildner if (do_unlock) {
228112bd3c8bSSascha Wildner USB_BUS_UNLOCK(bus);
228212bd3c8bSSascha Wildner }
228312bd3c8bSSascha Wildner }
228412bd3c8bSSascha Wildner
228512bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
228612bd3c8bSSascha Wildner * usb_needs_explore_all
228712bd3c8bSSascha Wildner *
228812bd3c8bSSascha Wildner * This function is called whenever a new driver is loaded and will
228912bd3c8bSSascha Wildner * cause that all USB busses are re-explored.
229012bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
229112bd3c8bSSascha Wildner void
usb_needs_explore_all(void)229212bd3c8bSSascha Wildner usb_needs_explore_all(void)
229312bd3c8bSSascha Wildner {
229412bd3c8bSSascha Wildner struct usb_bus *bus;
229512bd3c8bSSascha Wildner devclass_t dc;
229612bd3c8bSSascha Wildner device_t dev;
229712bd3c8bSSascha Wildner int max;
229812bd3c8bSSascha Wildner
229912bd3c8bSSascha Wildner DPRINTFN(3, "\n");
230012bd3c8bSSascha Wildner
230112bd3c8bSSascha Wildner dc = usb_devclass_ptr;
230212bd3c8bSSascha Wildner if (dc == NULL) {
230312bd3c8bSSascha Wildner DPRINTFN(0, "no devclass\n");
230412bd3c8bSSascha Wildner return;
230512bd3c8bSSascha Wildner }
230612bd3c8bSSascha Wildner /*
230712bd3c8bSSascha Wildner * Explore all USB busses in parallell.
230812bd3c8bSSascha Wildner */
230912bd3c8bSSascha Wildner max = devclass_get_maxunit(dc);
231012bd3c8bSSascha Wildner while (max >= 0) {
231112bd3c8bSSascha Wildner dev = devclass_get_device(dc, max);
231212bd3c8bSSascha Wildner if (dev) {
231312bd3c8bSSascha Wildner bus = device_get_softc(dev);
231412bd3c8bSSascha Wildner if (bus) {
231512bd3c8bSSascha Wildner usb_needs_explore(bus, 1);
231612bd3c8bSSascha Wildner }
231712bd3c8bSSascha Wildner }
231812bd3c8bSSascha Wildner max--;
231912bd3c8bSSascha Wildner }
232012bd3c8bSSascha Wildner }
232112bd3c8bSSascha Wildner
232212bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
232312bd3c8bSSascha Wildner * usb_bus_power_update
232412bd3c8bSSascha Wildner *
232512bd3c8bSSascha Wildner * This function will ensure that all USB devices on the given bus are
232612bd3c8bSSascha Wildner * properly suspended or resumed according to the device transfer
232712bd3c8bSSascha Wildner * state.
232812bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
232912bd3c8bSSascha Wildner #if USB_HAVE_POWERD
233012bd3c8bSSascha Wildner void
usb_bus_power_update(struct usb_bus * bus)233112bd3c8bSSascha Wildner usb_bus_power_update(struct usb_bus *bus)
233212bd3c8bSSascha Wildner {
233312bd3c8bSSascha Wildner usb_needs_explore(bus, 0 /* no probe */ );
233412bd3c8bSSascha Wildner }
233512bd3c8bSSascha Wildner #endif
233612bd3c8bSSascha Wildner
233712bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
233812bd3c8bSSascha Wildner * usbd_transfer_power_ref
233912bd3c8bSSascha Wildner *
234012bd3c8bSSascha Wildner * This function will modify the power save reference counts and
234112bd3c8bSSascha Wildner * wakeup the USB device associated with the given USB transfer, if
234212bd3c8bSSascha Wildner * needed.
234312bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
234412bd3c8bSSascha Wildner #if USB_HAVE_POWERD
234512bd3c8bSSascha Wildner void
usbd_transfer_power_ref(struct usb_xfer * xfer,int val)234612bd3c8bSSascha Wildner usbd_transfer_power_ref(struct usb_xfer *xfer, int val)
234712bd3c8bSSascha Wildner {
234812bd3c8bSSascha Wildner static const usb_power_mask_t power_mask[4] = {
234912bd3c8bSSascha Wildner [UE_CONTROL] = USB_HW_POWER_CONTROL,
235012bd3c8bSSascha Wildner [UE_BULK] = USB_HW_POWER_BULK,
235112bd3c8bSSascha Wildner [UE_INTERRUPT] = USB_HW_POWER_INTERRUPT,
235212bd3c8bSSascha Wildner [UE_ISOCHRONOUS] = USB_HW_POWER_ISOC,
235312bd3c8bSSascha Wildner };
235412bd3c8bSSascha Wildner struct usb_device *udev;
235512bd3c8bSSascha Wildner uint8_t needs_explore;
235612bd3c8bSSascha Wildner uint8_t needs_hw_power;
235712bd3c8bSSascha Wildner uint8_t xfer_type;
235812bd3c8bSSascha Wildner
235912bd3c8bSSascha Wildner udev = xfer->xroot->udev;
236012bd3c8bSSascha Wildner
236112bd3c8bSSascha Wildner if (udev->device_index == USB_ROOT_HUB_ADDR) {
236212bd3c8bSSascha Wildner /* no power save for root HUB */
236312bd3c8bSSascha Wildner return;
236412bd3c8bSSascha Wildner }
236512bd3c8bSSascha Wildner USB_BUS_LOCK(udev->bus);
236612bd3c8bSSascha Wildner
236712bd3c8bSSascha Wildner xfer_type = xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE;
236812bd3c8bSSascha Wildner
236912bd3c8bSSascha Wildner udev->pwr_save.last_xfer_time = ticks;
237012bd3c8bSSascha Wildner udev->pwr_save.type_refs[xfer_type] += val;
237112bd3c8bSSascha Wildner
237212bd3c8bSSascha Wildner if (xfer->flags_int.control_xfr) {
237312bd3c8bSSascha Wildner udev->pwr_save.read_refs += val;
237412bd3c8bSSascha Wildner if (xfer->flags_int.usb_mode == USB_MODE_HOST) {
237512bd3c8bSSascha Wildner /*
237612bd3c8bSSascha Wildner * It is not allowed to suspend during a
237712bd3c8bSSascha Wildner * control transfer:
237812bd3c8bSSascha Wildner */
237912bd3c8bSSascha Wildner udev->pwr_save.write_refs += val;
238012bd3c8bSSascha Wildner }
238112bd3c8bSSascha Wildner } else if (USB_GET_DATA_ISREAD(xfer)) {
238212bd3c8bSSascha Wildner udev->pwr_save.read_refs += val;
238312bd3c8bSSascha Wildner } else {
238412bd3c8bSSascha Wildner udev->pwr_save.write_refs += val;
238512bd3c8bSSascha Wildner }
238612bd3c8bSSascha Wildner
238712bd3c8bSSascha Wildner if (val > 0) {
238812bd3c8bSSascha Wildner if (udev->flags.self_suspended)
238912bd3c8bSSascha Wildner needs_explore = usb_peer_should_wakeup(udev);
239012bd3c8bSSascha Wildner else
239112bd3c8bSSascha Wildner needs_explore = 0;
239212bd3c8bSSascha Wildner
239312bd3c8bSSascha Wildner if (!(udev->bus->hw_power_state & power_mask[xfer_type])) {
239412bd3c8bSSascha Wildner DPRINTF("Adding type %u to power state\n", xfer_type);
239512bd3c8bSSascha Wildner udev->bus->hw_power_state |= power_mask[xfer_type];
239612bd3c8bSSascha Wildner needs_hw_power = 1;
239712bd3c8bSSascha Wildner } else {
239812bd3c8bSSascha Wildner needs_hw_power = 0;
239912bd3c8bSSascha Wildner }
240012bd3c8bSSascha Wildner } else {
240112bd3c8bSSascha Wildner needs_explore = 0;
240212bd3c8bSSascha Wildner needs_hw_power = 0;
240312bd3c8bSSascha Wildner }
240412bd3c8bSSascha Wildner
240512bd3c8bSSascha Wildner USB_BUS_UNLOCK(udev->bus);
240612bd3c8bSSascha Wildner
240712bd3c8bSSascha Wildner if (needs_explore) {
240812bd3c8bSSascha Wildner DPRINTF("update\n");
240912bd3c8bSSascha Wildner usb_bus_power_update(udev->bus);
241012bd3c8bSSascha Wildner } else if (needs_hw_power) {
241112bd3c8bSSascha Wildner DPRINTF("needs power\n");
241212bd3c8bSSascha Wildner if (udev->bus->methods->set_hw_power != NULL) {
241312bd3c8bSSascha Wildner (udev->bus->methods->set_hw_power) (udev->bus);
241412bd3c8bSSascha Wildner }
241512bd3c8bSSascha Wildner }
241612bd3c8bSSascha Wildner }
241712bd3c8bSSascha Wildner #endif
241812bd3c8bSSascha Wildner
241912bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
242012bd3c8bSSascha Wildner * usb_peer_should_wakeup
242112bd3c8bSSascha Wildner *
242212bd3c8bSSascha Wildner * This function returns non-zero if the current device should wake up.
242312bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
242412bd3c8bSSascha Wildner static uint8_t
usb_peer_should_wakeup(struct usb_device * udev)242512bd3c8bSSascha Wildner usb_peer_should_wakeup(struct usb_device *udev)
242612bd3c8bSSascha Wildner {
24278922de18SMarkus Pfeiffer return (((udev->power_mode == USB_POWER_MODE_ON) &&
24288922de18SMarkus Pfeiffer (udev->flags.usb_mode == USB_MODE_HOST)) ||
242912bd3c8bSSascha Wildner (udev->driver_added_refcount != udev->bus->driver_added_refcount) ||
24308922de18SMarkus Pfeiffer (udev->re_enumerate_wait != USB_RE_ENUM_DONE) ||
243112bd3c8bSSascha Wildner (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) ||
243212bd3c8bSSascha Wildner (udev->pwr_save.write_refs != 0) ||
243312bd3c8bSSascha Wildner ((udev->pwr_save.read_refs != 0) &&
243412bd3c8bSSascha Wildner (udev->flags.usb_mode == USB_MODE_HOST) &&
243512bd3c8bSSascha Wildner (usb_peer_can_wakeup(udev) == 0)));
243612bd3c8bSSascha Wildner }
243712bd3c8bSSascha Wildner
243812bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
243912bd3c8bSSascha Wildner * usb_bus_powerd
244012bd3c8bSSascha Wildner *
244112bd3c8bSSascha Wildner * This function implements the USB power daemon and is called
244212bd3c8bSSascha Wildner * regularly from the USB explore thread.
244312bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
244412bd3c8bSSascha Wildner #if USB_HAVE_POWERD
244512bd3c8bSSascha Wildner void
usb_bus_powerd(struct usb_bus * bus)244612bd3c8bSSascha Wildner usb_bus_powerd(struct usb_bus *bus)
244712bd3c8bSSascha Wildner {
244812bd3c8bSSascha Wildner struct usb_device *udev;
244912bd3c8bSSascha Wildner usb_ticks_t temp;
245012bd3c8bSSascha Wildner usb_ticks_t limit;
245112bd3c8bSSascha Wildner usb_ticks_t mintime;
245212bd3c8bSSascha Wildner usb_size_t type_refs[5];
245312bd3c8bSSascha Wildner uint8_t x;
245412bd3c8bSSascha Wildner
245512bd3c8bSSascha Wildner limit = usb_power_timeout;
245612bd3c8bSSascha Wildner if (limit == 0)
245712bd3c8bSSascha Wildner limit = hz;
245812bd3c8bSSascha Wildner else if (limit > 255)
245912bd3c8bSSascha Wildner limit = 255 * hz;
246012bd3c8bSSascha Wildner else
246112bd3c8bSSascha Wildner limit = limit * hz;
246212bd3c8bSSascha Wildner
246312bd3c8bSSascha Wildner DPRINTF("bus=%p\n", bus);
246412bd3c8bSSascha Wildner
246512bd3c8bSSascha Wildner USB_BUS_LOCK(bus);
246612bd3c8bSSascha Wildner
246712bd3c8bSSascha Wildner /*
246812bd3c8bSSascha Wildner * The root HUB device is never suspended
246912bd3c8bSSascha Wildner * and we simply skip it.
247012bd3c8bSSascha Wildner */
247112bd3c8bSSascha Wildner for (x = USB_ROOT_HUB_ADDR + 1;
247212bd3c8bSSascha Wildner x != bus->devices_max; x++) {
247312bd3c8bSSascha Wildner
247412bd3c8bSSascha Wildner udev = bus->devices[x];
247512bd3c8bSSascha Wildner if (udev == NULL)
247612bd3c8bSSascha Wildner continue;
247712bd3c8bSSascha Wildner
247812bd3c8bSSascha Wildner temp = ticks - udev->pwr_save.last_xfer_time;
247912bd3c8bSSascha Wildner
248012bd3c8bSSascha Wildner if (usb_peer_should_wakeup(udev)) {
248112bd3c8bSSascha Wildner /* check if we are suspended */
248212bd3c8bSSascha Wildner if (udev->flags.self_suspended != 0) {
248312bd3c8bSSascha Wildner USB_BUS_UNLOCK(bus);
248412bd3c8bSSascha Wildner usb_dev_resume_peer(udev);
248512bd3c8bSSascha Wildner USB_BUS_LOCK(bus);
248612bd3c8bSSascha Wildner }
248712bd3c8bSSascha Wildner } else if ((temp >= limit) &&
248812bd3c8bSSascha Wildner (udev->flags.usb_mode == USB_MODE_HOST) &&
248912bd3c8bSSascha Wildner (udev->flags.self_suspended == 0)) {
249012bd3c8bSSascha Wildner /* try to do suspend */
249112bd3c8bSSascha Wildner
249212bd3c8bSSascha Wildner USB_BUS_UNLOCK(bus);
249312bd3c8bSSascha Wildner usb_dev_suspend_peer(udev);
249412bd3c8bSSascha Wildner USB_BUS_LOCK(bus);
249512bd3c8bSSascha Wildner }
249612bd3c8bSSascha Wildner }
249712bd3c8bSSascha Wildner
249812bd3c8bSSascha Wildner /* reset counters */
249912bd3c8bSSascha Wildner
25005e41ab93SMarkus Pfeiffer mintime = (usb_ticks_t)-1;
250112bd3c8bSSascha Wildner type_refs[0] = 0;
250212bd3c8bSSascha Wildner type_refs[1] = 0;
250312bd3c8bSSascha Wildner type_refs[2] = 0;
250412bd3c8bSSascha Wildner type_refs[3] = 0;
250512bd3c8bSSascha Wildner type_refs[4] = 0;
250612bd3c8bSSascha Wildner
250712bd3c8bSSascha Wildner /* Re-loop all the devices to get the actual state */
250812bd3c8bSSascha Wildner
250912bd3c8bSSascha Wildner for (x = USB_ROOT_HUB_ADDR + 1;
251012bd3c8bSSascha Wildner x != bus->devices_max; x++) {
251112bd3c8bSSascha Wildner
251212bd3c8bSSascha Wildner udev = bus->devices[x];
251312bd3c8bSSascha Wildner if (udev == NULL)
251412bd3c8bSSascha Wildner continue;
251512bd3c8bSSascha Wildner
251612bd3c8bSSascha Wildner /* we found a non-Root-Hub USB device */
251712bd3c8bSSascha Wildner type_refs[4] += 1;
251812bd3c8bSSascha Wildner
251912bd3c8bSSascha Wildner /* "last_xfer_time" can be updated by a resume */
252012bd3c8bSSascha Wildner temp = ticks - udev->pwr_save.last_xfer_time;
252112bd3c8bSSascha Wildner
252212bd3c8bSSascha Wildner /*
252312bd3c8bSSascha Wildner * Compute minimum time since last transfer for the complete
252412bd3c8bSSascha Wildner * bus:
252512bd3c8bSSascha Wildner */
252612bd3c8bSSascha Wildner if (temp < mintime)
252712bd3c8bSSascha Wildner mintime = temp;
252812bd3c8bSSascha Wildner
252912bd3c8bSSascha Wildner if (udev->flags.self_suspended == 0) {
253012bd3c8bSSascha Wildner type_refs[0] += udev->pwr_save.type_refs[0];
253112bd3c8bSSascha Wildner type_refs[1] += udev->pwr_save.type_refs[1];
253212bd3c8bSSascha Wildner type_refs[2] += udev->pwr_save.type_refs[2];
253312bd3c8bSSascha Wildner type_refs[3] += udev->pwr_save.type_refs[3];
253412bd3c8bSSascha Wildner }
253512bd3c8bSSascha Wildner }
253612bd3c8bSSascha Wildner
25375e41ab93SMarkus Pfeiffer if (mintime >= (usb_ticks_t)(1 * hz)) {
253812bd3c8bSSascha Wildner /* recompute power masks */
253912bd3c8bSSascha Wildner DPRINTF("Recomputing power masks\n");
254012bd3c8bSSascha Wildner bus->hw_power_state = 0;
254112bd3c8bSSascha Wildner if (type_refs[UE_CONTROL] != 0)
254212bd3c8bSSascha Wildner bus->hw_power_state |= USB_HW_POWER_CONTROL;
254312bd3c8bSSascha Wildner if (type_refs[UE_BULK] != 0)
254412bd3c8bSSascha Wildner bus->hw_power_state |= USB_HW_POWER_BULK;
254512bd3c8bSSascha Wildner if (type_refs[UE_INTERRUPT] != 0)
254612bd3c8bSSascha Wildner bus->hw_power_state |= USB_HW_POWER_INTERRUPT;
254712bd3c8bSSascha Wildner if (type_refs[UE_ISOCHRONOUS] != 0)
254812bd3c8bSSascha Wildner bus->hw_power_state |= USB_HW_POWER_ISOC;
254912bd3c8bSSascha Wildner if (type_refs[4] != 0)
255012bd3c8bSSascha Wildner bus->hw_power_state |= USB_HW_POWER_NON_ROOT_HUB;
255112bd3c8bSSascha Wildner }
255212bd3c8bSSascha Wildner USB_BUS_UNLOCK(bus);
255312bd3c8bSSascha Wildner
255412bd3c8bSSascha Wildner if (bus->methods->set_hw_power != NULL) {
255512bd3c8bSSascha Wildner /* always update hardware power! */
255612bd3c8bSSascha Wildner (bus->methods->set_hw_power) (bus);
255712bd3c8bSSascha Wildner }
255812bd3c8bSSascha Wildner return;
255912bd3c8bSSascha Wildner }
256012bd3c8bSSascha Wildner #endif
256112bd3c8bSSascha Wildner
256212bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
256312bd3c8bSSascha Wildner * usb_dev_resume_peer
256412bd3c8bSSascha Wildner *
256512bd3c8bSSascha Wildner * This function will resume an USB peer and do the required USB
256612bd3c8bSSascha Wildner * signalling to get an USB device out of the suspended state.
256712bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
256812bd3c8bSSascha Wildner static void
usb_dev_resume_peer(struct usb_device * udev)256912bd3c8bSSascha Wildner usb_dev_resume_peer(struct usb_device *udev)
257012bd3c8bSSascha Wildner {
257112bd3c8bSSascha Wildner struct usb_bus *bus;
257212bd3c8bSSascha Wildner int err;
257312bd3c8bSSascha Wildner
257412bd3c8bSSascha Wildner /* be NULL safe */
257512bd3c8bSSascha Wildner if (udev == NULL)
257612bd3c8bSSascha Wildner return;
257712bd3c8bSSascha Wildner
257812bd3c8bSSascha Wildner /* check if already resumed */
257912bd3c8bSSascha Wildner if (udev->flags.self_suspended == 0)
258012bd3c8bSSascha Wildner return;
258112bd3c8bSSascha Wildner
258212bd3c8bSSascha Wildner /* we need a parent HUB to do resume */
258312bd3c8bSSascha Wildner if (udev->parent_hub == NULL)
258412bd3c8bSSascha Wildner return;
258512bd3c8bSSascha Wildner
258612bd3c8bSSascha Wildner DPRINTF("udev=%p\n", udev);
258712bd3c8bSSascha Wildner
258812bd3c8bSSascha Wildner if ((udev->flags.usb_mode == USB_MODE_DEVICE) &&
258912bd3c8bSSascha Wildner (udev->flags.remote_wakeup == 0)) {
259012bd3c8bSSascha Wildner /*
259112bd3c8bSSascha Wildner * If the host did not set the remote wakeup feature, we can
259212bd3c8bSSascha Wildner * not wake it up either!
259312bd3c8bSSascha Wildner */
259412bd3c8bSSascha Wildner DPRINTF("remote wakeup is not set!\n");
259512bd3c8bSSascha Wildner return;
259612bd3c8bSSascha Wildner }
259712bd3c8bSSascha Wildner /* get bus pointer */
259812bd3c8bSSascha Wildner bus = udev->bus;
259912bd3c8bSSascha Wildner
260012bd3c8bSSascha Wildner /* resume parent hub first */
260112bd3c8bSSascha Wildner usb_dev_resume_peer(udev->parent_hub);
260212bd3c8bSSascha Wildner
260312bd3c8bSSascha Wildner /* reduce chance of instant resume failure by waiting a little bit */
260412bd3c8bSSascha Wildner usb_pause_mtx(NULL, USB_MS_TO_TICKS(20));
260512bd3c8bSSascha Wildner
260612bd3c8bSSascha Wildner if (usb_device_20_compatible(udev)) {
260712bd3c8bSSascha Wildner /* resume current port (Valid in Host and Device Mode) */
260812bd3c8bSSascha Wildner err = usbd_req_clear_port_feature(udev->parent_hub,
260912bd3c8bSSascha Wildner NULL, udev->port_no, UHF_PORT_SUSPEND);
261012bd3c8bSSascha Wildner if (err) {
261112bd3c8bSSascha Wildner DPRINTFN(0, "Resuming port failed\n");
261212bd3c8bSSascha Wildner return;
261312bd3c8bSSascha Wildner }
261412bd3c8bSSascha Wildner } else {
261512bd3c8bSSascha Wildner /* resume current port (Valid in Host and Device Mode) */
261612bd3c8bSSascha Wildner err = usbd_req_set_port_link_state(udev->parent_hub,
261712bd3c8bSSascha Wildner NULL, udev->port_no, UPS_PORT_LS_U0);
261812bd3c8bSSascha Wildner if (err) {
261912bd3c8bSSascha Wildner DPRINTFN(0, "Resuming port failed\n");
262012bd3c8bSSascha Wildner return;
262112bd3c8bSSascha Wildner }
262212bd3c8bSSascha Wildner }
262312bd3c8bSSascha Wildner
262412bd3c8bSSascha Wildner /* resume settle time */
26255e41ab93SMarkus Pfeiffer usb_pause_mtx(NULL, USB_MS_TO_TICKS(usb_port_resume_delay));
262612bd3c8bSSascha Wildner
262712bd3c8bSSascha Wildner if (bus->methods->device_resume != NULL) {
262812bd3c8bSSascha Wildner /* resume USB device on the USB controller */
262912bd3c8bSSascha Wildner (bus->methods->device_resume) (udev);
263012bd3c8bSSascha Wildner }
263112bd3c8bSSascha Wildner USB_BUS_LOCK(bus);
263212bd3c8bSSascha Wildner /* set that this device is now resumed */
263312bd3c8bSSascha Wildner udev->flags.self_suspended = 0;
263412bd3c8bSSascha Wildner #if USB_HAVE_POWERD
263512bd3c8bSSascha Wildner /* make sure that we don't go into suspend right away */
263612bd3c8bSSascha Wildner udev->pwr_save.last_xfer_time = ticks;
263712bd3c8bSSascha Wildner
263812bd3c8bSSascha Wildner /* make sure the needed power masks are on */
263912bd3c8bSSascha Wildner if (udev->pwr_save.type_refs[UE_CONTROL] != 0)
264012bd3c8bSSascha Wildner bus->hw_power_state |= USB_HW_POWER_CONTROL;
264112bd3c8bSSascha Wildner if (udev->pwr_save.type_refs[UE_BULK] != 0)
264212bd3c8bSSascha Wildner bus->hw_power_state |= USB_HW_POWER_BULK;
264312bd3c8bSSascha Wildner if (udev->pwr_save.type_refs[UE_INTERRUPT] != 0)
264412bd3c8bSSascha Wildner bus->hw_power_state |= USB_HW_POWER_INTERRUPT;
264512bd3c8bSSascha Wildner if (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0)
264612bd3c8bSSascha Wildner bus->hw_power_state |= USB_HW_POWER_ISOC;
264712bd3c8bSSascha Wildner #endif
264812bd3c8bSSascha Wildner USB_BUS_UNLOCK(bus);
264912bd3c8bSSascha Wildner
265012bd3c8bSSascha Wildner if (bus->methods->set_hw_power != NULL) {
265112bd3c8bSSascha Wildner /* always update hardware power! */
265212bd3c8bSSascha Wildner (bus->methods->set_hw_power) (bus);
265312bd3c8bSSascha Wildner }
265412bd3c8bSSascha Wildner
265512bd3c8bSSascha Wildner usbd_sr_lock(udev);
265612bd3c8bSSascha Wildner
265712bd3c8bSSascha Wildner /* notify all sub-devices about resume */
265812bd3c8bSSascha Wildner err = usb_suspend_resume(udev, 0);
265912bd3c8bSSascha Wildner
266012bd3c8bSSascha Wildner usbd_sr_unlock(udev);
266112bd3c8bSSascha Wildner
266212bd3c8bSSascha Wildner /* check if peer has wakeup capability */
266312bd3c8bSSascha Wildner if (usb_peer_can_wakeup(udev)) {
266412bd3c8bSSascha Wildner /* clear remote wakeup */
266512bd3c8bSSascha Wildner err = usbd_req_clear_device_feature(udev,
266612bd3c8bSSascha Wildner NULL, UF_DEVICE_REMOTE_WAKEUP);
266712bd3c8bSSascha Wildner if (err) {
266812bd3c8bSSascha Wildner DPRINTFN(0, "Clearing device "
266912bd3c8bSSascha Wildner "remote wakeup failed: %s\n",
267012bd3c8bSSascha Wildner usbd_errstr(err));
267112bd3c8bSSascha Wildner }
267212bd3c8bSSascha Wildner }
267312bd3c8bSSascha Wildner }
267412bd3c8bSSascha Wildner
267512bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
267612bd3c8bSSascha Wildner * usb_dev_suspend_peer
267712bd3c8bSSascha Wildner *
267812bd3c8bSSascha Wildner * This function will suspend an USB peer and do the required USB
267912bd3c8bSSascha Wildner * signalling to get an USB device into the suspended state.
268012bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
268112bd3c8bSSascha Wildner static void
usb_dev_suspend_peer(struct usb_device * udev)268212bd3c8bSSascha Wildner usb_dev_suspend_peer(struct usb_device *udev)
268312bd3c8bSSascha Wildner {
268412bd3c8bSSascha Wildner struct usb_device *child;
268512bd3c8bSSascha Wildner int err;
268612bd3c8bSSascha Wildner uint8_t x;
268712bd3c8bSSascha Wildner uint8_t nports;
268812bd3c8bSSascha Wildner
268912bd3c8bSSascha Wildner repeat:
269012bd3c8bSSascha Wildner /* be NULL safe */
269112bd3c8bSSascha Wildner if (udev == NULL)
269212bd3c8bSSascha Wildner return;
269312bd3c8bSSascha Wildner
269412bd3c8bSSascha Wildner /* check if already suspended */
269512bd3c8bSSascha Wildner if (udev->flags.self_suspended)
269612bd3c8bSSascha Wildner return;
269712bd3c8bSSascha Wildner
269812bd3c8bSSascha Wildner /* we need a parent HUB to do suspend */
269912bd3c8bSSascha Wildner if (udev->parent_hub == NULL)
270012bd3c8bSSascha Wildner return;
270112bd3c8bSSascha Wildner
270212bd3c8bSSascha Wildner DPRINTF("udev=%p\n", udev);
270312bd3c8bSSascha Wildner
270412bd3c8bSSascha Wildner /* check if the current device is a HUB */
270512bd3c8bSSascha Wildner if (udev->hub != NULL) {
270612bd3c8bSSascha Wildner nports = udev->hub->nports;
270712bd3c8bSSascha Wildner
270812bd3c8bSSascha Wildner /* check if all devices on the HUB are suspended */
270912bd3c8bSSascha Wildner for (x = 0; x != nports; x++) {
271012bd3c8bSSascha Wildner child = usb_bus_port_get_device(udev->bus,
271112bd3c8bSSascha Wildner udev->hub->ports + x);
271212bd3c8bSSascha Wildner
271312bd3c8bSSascha Wildner if (child == NULL)
271412bd3c8bSSascha Wildner continue;
271512bd3c8bSSascha Wildner
271612bd3c8bSSascha Wildner if (child->flags.self_suspended)
271712bd3c8bSSascha Wildner continue;
271812bd3c8bSSascha Wildner
271912bd3c8bSSascha Wildner DPRINTFN(1, "Port %u is busy on the HUB!\n", x + 1);
272012bd3c8bSSascha Wildner return;
272112bd3c8bSSascha Wildner }
272212bd3c8bSSascha Wildner }
272312bd3c8bSSascha Wildner
272412bd3c8bSSascha Wildner if (usb_peer_can_wakeup(udev)) {
272512bd3c8bSSascha Wildner /*
272612bd3c8bSSascha Wildner * This request needs to be done before we set
272712bd3c8bSSascha Wildner * "udev->flags.self_suspended":
272812bd3c8bSSascha Wildner */
272912bd3c8bSSascha Wildner
273012bd3c8bSSascha Wildner /* allow device to do remote wakeup */
273112bd3c8bSSascha Wildner err = usbd_req_set_device_feature(udev,
273212bd3c8bSSascha Wildner NULL, UF_DEVICE_REMOTE_WAKEUP);
273312bd3c8bSSascha Wildner if (err) {
273412bd3c8bSSascha Wildner DPRINTFN(0, "Setting device "
273512bd3c8bSSascha Wildner "remote wakeup failed\n");
273612bd3c8bSSascha Wildner }
273712bd3c8bSSascha Wildner }
273812bd3c8bSSascha Wildner
273912bd3c8bSSascha Wildner USB_BUS_LOCK(udev->bus);
274012bd3c8bSSascha Wildner /*
274112bd3c8bSSascha Wildner * Checking for suspend condition and setting suspended bit
274212bd3c8bSSascha Wildner * must be atomic!
274312bd3c8bSSascha Wildner */
274412bd3c8bSSascha Wildner err = usb_peer_should_wakeup(udev);
274512bd3c8bSSascha Wildner if (err == 0) {
274612bd3c8bSSascha Wildner /*
274712bd3c8bSSascha Wildner * Set that this device is suspended. This variable
274812bd3c8bSSascha Wildner * must be set before calling USB controller suspend
274912bd3c8bSSascha Wildner * callbacks.
275012bd3c8bSSascha Wildner */
275112bd3c8bSSascha Wildner udev->flags.self_suspended = 1;
275212bd3c8bSSascha Wildner }
275312bd3c8bSSascha Wildner USB_BUS_UNLOCK(udev->bus);
275412bd3c8bSSascha Wildner
275512bd3c8bSSascha Wildner if (err != 0) {
275612bd3c8bSSascha Wildner if (usb_peer_can_wakeup(udev)) {
275712bd3c8bSSascha Wildner /* allow device to do remote wakeup */
275812bd3c8bSSascha Wildner err = usbd_req_clear_device_feature(udev,
275912bd3c8bSSascha Wildner NULL, UF_DEVICE_REMOTE_WAKEUP);
276012bd3c8bSSascha Wildner if (err) {
276112bd3c8bSSascha Wildner DPRINTFN(0, "Setting device "
276212bd3c8bSSascha Wildner "remote wakeup failed\n");
276312bd3c8bSSascha Wildner }
276412bd3c8bSSascha Wildner }
276512bd3c8bSSascha Wildner
276612bd3c8bSSascha Wildner if (udev->flags.usb_mode == USB_MODE_DEVICE) {
276712bd3c8bSSascha Wildner /* resume parent HUB first */
276812bd3c8bSSascha Wildner usb_dev_resume_peer(udev->parent_hub);
276912bd3c8bSSascha Wildner
277012bd3c8bSSascha Wildner /* reduce chance of instant resume failure by waiting a little bit */
277112bd3c8bSSascha Wildner usb_pause_mtx(NULL, USB_MS_TO_TICKS(20));
277212bd3c8bSSascha Wildner
277312bd3c8bSSascha Wildner /* resume current port (Valid in Host and Device Mode) */
277412bd3c8bSSascha Wildner err = usbd_req_clear_port_feature(udev->parent_hub,
277512bd3c8bSSascha Wildner NULL, udev->port_no, UHF_PORT_SUSPEND);
277612bd3c8bSSascha Wildner
277712bd3c8bSSascha Wildner /* resume settle time */
27785e41ab93SMarkus Pfeiffer usb_pause_mtx(NULL, USB_MS_TO_TICKS(usb_port_resume_delay));
277912bd3c8bSSascha Wildner }
278012bd3c8bSSascha Wildner DPRINTF("Suspend was cancelled!\n");
278112bd3c8bSSascha Wildner return;
278212bd3c8bSSascha Wildner }
278312bd3c8bSSascha Wildner
278412bd3c8bSSascha Wildner usbd_sr_lock(udev);
278512bd3c8bSSascha Wildner
278612bd3c8bSSascha Wildner /* notify all sub-devices about suspend */
278712bd3c8bSSascha Wildner err = usb_suspend_resume(udev, 1);
278812bd3c8bSSascha Wildner
278912bd3c8bSSascha Wildner usbd_sr_unlock(udev);
279012bd3c8bSSascha Wildner
279112bd3c8bSSascha Wildner if (udev->bus->methods->device_suspend != NULL) {
279212bd3c8bSSascha Wildner usb_timeout_t temp;
279312bd3c8bSSascha Wildner
279412bd3c8bSSascha Wildner /* suspend device on the USB controller */
279512bd3c8bSSascha Wildner (udev->bus->methods->device_suspend) (udev);
279612bd3c8bSSascha Wildner
279712bd3c8bSSascha Wildner /* do DMA delay */
279812bd3c8bSSascha Wildner temp = usbd_get_dma_delay(udev);
279912bd3c8bSSascha Wildner if (temp != 0)
280012bd3c8bSSascha Wildner usb_pause_mtx(NULL, USB_MS_TO_TICKS(temp));
280112bd3c8bSSascha Wildner
280212bd3c8bSSascha Wildner }
280312bd3c8bSSascha Wildner
280412bd3c8bSSascha Wildner if (usb_device_20_compatible(udev)) {
280512bd3c8bSSascha Wildner /* suspend current port */
280612bd3c8bSSascha Wildner err = usbd_req_set_port_feature(udev->parent_hub,
280712bd3c8bSSascha Wildner NULL, udev->port_no, UHF_PORT_SUSPEND);
280812bd3c8bSSascha Wildner if (err) {
280912bd3c8bSSascha Wildner DPRINTFN(0, "Suspending port failed\n");
281012bd3c8bSSascha Wildner return;
281112bd3c8bSSascha Wildner }
281212bd3c8bSSascha Wildner } else {
281312bd3c8bSSascha Wildner /* suspend current port */
281412bd3c8bSSascha Wildner err = usbd_req_set_port_link_state(udev->parent_hub,
281512bd3c8bSSascha Wildner NULL, udev->port_no, UPS_PORT_LS_U3);
281612bd3c8bSSascha Wildner if (err) {
281712bd3c8bSSascha Wildner DPRINTFN(0, "Suspending port failed\n");
281812bd3c8bSSascha Wildner return;
281912bd3c8bSSascha Wildner }
282012bd3c8bSSascha Wildner }
282112bd3c8bSSascha Wildner
282212bd3c8bSSascha Wildner udev = udev->parent_hub;
282312bd3c8bSSascha Wildner goto repeat;
282412bd3c8bSSascha Wildner }
282512bd3c8bSSascha Wildner
282612bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
282712bd3c8bSSascha Wildner * usbd_set_power_mode
282812bd3c8bSSascha Wildner *
282912bd3c8bSSascha Wildner * This function will set the power mode, see USB_POWER_MODE_XXX for a
283012bd3c8bSSascha Wildner * USB device.
283112bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
283212bd3c8bSSascha Wildner void
usbd_set_power_mode(struct usb_device * udev,uint8_t power_mode)283312bd3c8bSSascha Wildner usbd_set_power_mode(struct usb_device *udev, uint8_t power_mode)
283412bd3c8bSSascha Wildner {
283512bd3c8bSSascha Wildner /* filter input argument */
283612bd3c8bSSascha Wildner if ((power_mode != USB_POWER_MODE_ON) &&
283712bd3c8bSSascha Wildner (power_mode != USB_POWER_MODE_OFF))
283812bd3c8bSSascha Wildner power_mode = USB_POWER_MODE_SAVE;
283912bd3c8bSSascha Wildner
284012bd3c8bSSascha Wildner power_mode = usbd_filter_power_mode(udev, power_mode);
284112bd3c8bSSascha Wildner
284212bd3c8bSSascha Wildner udev->power_mode = power_mode; /* update copy of power mode */
284312bd3c8bSSascha Wildner
284412bd3c8bSSascha Wildner #if USB_HAVE_POWERD
284512bd3c8bSSascha Wildner usb_bus_power_update(udev->bus);
28468922de18SMarkus Pfeiffer #else
28478922de18SMarkus Pfeiffer usb_needs_explore(udev->bus, 0 /* no probe */ );
284812bd3c8bSSascha Wildner #endif
284912bd3c8bSSascha Wildner }
285012bd3c8bSSascha Wildner
285112bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
285212bd3c8bSSascha Wildner * usbd_filter_power_mode
285312bd3c8bSSascha Wildner *
285412bd3c8bSSascha Wildner * This function filters the power mode based on hardware requirements.
285512bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
285612bd3c8bSSascha Wildner uint8_t
usbd_filter_power_mode(struct usb_device * udev,uint8_t power_mode)285712bd3c8bSSascha Wildner usbd_filter_power_mode(struct usb_device *udev, uint8_t power_mode)
285812bd3c8bSSascha Wildner {
28598922de18SMarkus Pfeiffer const struct usb_bus_methods *mtod;
286012bd3c8bSSascha Wildner int8_t temp;
286112bd3c8bSSascha Wildner
286212bd3c8bSSascha Wildner mtod = udev->bus->methods;
286312bd3c8bSSascha Wildner temp = -1;
286412bd3c8bSSascha Wildner
286512bd3c8bSSascha Wildner if (mtod->get_power_mode != NULL)
286612bd3c8bSSascha Wildner (mtod->get_power_mode) (udev, &temp);
286712bd3c8bSSascha Wildner
286812bd3c8bSSascha Wildner /* check if we should not filter */
286912bd3c8bSSascha Wildner if (temp < 0)
287012bd3c8bSSascha Wildner return (power_mode);
287112bd3c8bSSascha Wildner
287212bd3c8bSSascha Wildner /* use fixed power mode given by hardware driver */
287312bd3c8bSSascha Wildner return (temp);
287412bd3c8bSSascha Wildner }
287512bd3c8bSSascha Wildner
287612bd3c8bSSascha Wildner /*------------------------------------------------------------------------*
287712bd3c8bSSascha Wildner * usbd_start_re_enumerate
287812bd3c8bSSascha Wildner *
287912bd3c8bSSascha Wildner * This function starts re-enumeration of the given USB device. This
288012bd3c8bSSascha Wildner * function does not need to be called BUS-locked. This function does
288112bd3c8bSSascha Wildner * not wait until the re-enumeration is completed.
288212bd3c8bSSascha Wildner *------------------------------------------------------------------------*/
288312bd3c8bSSascha Wildner void
usbd_start_re_enumerate(struct usb_device * udev)288412bd3c8bSSascha Wildner usbd_start_re_enumerate(struct usb_device *udev)
288512bd3c8bSSascha Wildner {
28868922de18SMarkus Pfeiffer if (udev->re_enumerate_wait == USB_RE_ENUM_DONE) {
28878922de18SMarkus Pfeiffer udev->re_enumerate_wait = USB_RE_ENUM_START;
288812bd3c8bSSascha Wildner usb_needs_explore(udev->bus, 0);
288912bd3c8bSSascha Wildner }
289012bd3c8bSSascha Wildner }
289145f67c02SMarkus Pfeiffer
289245f67c02SMarkus Pfeiffer /*-----------------------------------------------------------------------*
289345f67c02SMarkus Pfeiffer * usbd_start_set_config
289445f67c02SMarkus Pfeiffer *
289545f67c02SMarkus Pfeiffer * This function starts setting a USB configuration. This function
289645f67c02SMarkus Pfeiffer * does not need to be called BUS-locked. This function does not wait
289745f67c02SMarkus Pfeiffer * until the set USB configuratino is completed.
289845f67c02SMarkus Pfeiffer *------------------------------------------------------------------------*/
289945f67c02SMarkus Pfeiffer usb_error_t
usbd_start_set_config(struct usb_device * udev,uint8_t index)290045f67c02SMarkus Pfeiffer usbd_start_set_config(struct usb_device *udev, uint8_t index)
290145f67c02SMarkus Pfeiffer {
290245f67c02SMarkus Pfeiffer if (udev->re_enumerate_wait == USB_RE_ENUM_DONE) {
290345f67c02SMarkus Pfeiffer if (udev->curr_config_index == index) {
290445f67c02SMarkus Pfeiffer /* no change needed */
290545f67c02SMarkus Pfeiffer return (0);
290645f67c02SMarkus Pfeiffer }
290745f67c02SMarkus Pfeiffer udev->next_config_index = index;
290845f67c02SMarkus Pfeiffer udev->re_enumerate_wait = USB_RE_ENUM_SET_CONFIG;
290945f67c02SMarkus Pfeiffer usb_needs_explore(udev->bus, 0);
291045f67c02SMarkus Pfeiffer return (0);
291145f67c02SMarkus Pfeiffer } else if (udev->re_enumerate_wait == USB_RE_ENUM_SET_CONFIG) {
291245f67c02SMarkus Pfeiffer if (udev->next_config_index == index) {
291345f67c02SMarkus Pfeiffer /* no change needed */
291445f67c02SMarkus Pfeiffer return (0);
291545f67c02SMarkus Pfeiffer }
291645f67c02SMarkus Pfeiffer }
291745f67c02SMarkus Pfeiffer return (USB_ERR_PENDING_REQUESTS);
291845f67c02SMarkus Pfeiffer }
2919