summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2010-10-04 23:18:05 +0000
committerhselasky <hselasky@FreeBSD.org>2010-10-04 23:18:05 +0000
commitf58df490d49462ae839658025d0adc79cf5848ad (patch)
treec082c15f2b4af936b99eda2cd4c67777fdf391ee
parentcacc63611c920df4de9b0e17b85172a00c979e75 (diff)
downloadFreeBSD-src-f58df490d49462ae839658025d0adc79cf5848ad.zip
FreeBSD-src-f58df490d49462ae839658025d0adc79cf5848ad.tar.gz
This commit adds full support for USB 3.0 devices in host and device
mode in the USB core. The patch mostly consists of updating the USB HUB code to support USB 3.0 HUBs. This patch also add some more USB controller methods to support more active-alike USB controllers like the XHCI which needs to be informed about various device state events. USB 3.0 HUBs are not tested yet, due to lack of hardware, but are believed to work. After this update the initial device descriptor is only read twice when we know that the bMaxPacketSize is too small for a single packet transfer of this descriptor. Approved by: thompsa (mentor)
-rw-r--r--sys/dev/usb/controller/usb_controller.c6
-rw-r--r--sys/dev/usb/usb_controller.h25
-rw-r--r--sys/dev/usb/usb_device.c117
-rw-r--r--sys/dev/usb/usb_device.h13
-rw-r--r--sys/dev/usb/usb_hub.c430
-rw-r--r--sys/dev/usb/usb_hub.h1
-rw-r--r--sys/dev/usb/usb_parse.c38
-rw-r--r--sys/dev/usb/usb_request.c211
-rw-r--r--sys/dev/usb/usb_request.h16
-rw-r--r--sys/dev/usb/usb_transfer.c182
-rw-r--r--sys/dev/usb/usb_transfer.h5
-rw-r--r--sys/dev/usb/usbdi.h1
-rw-r--r--sys/dev/usb/usbdi_util.h3
13 files changed, 845 insertions, 203 deletions
diff --git a/sys/dev/usb/controller/usb_controller.c b/sys/dev/usb/controller/usb_controller.c
index d1a3da2..71e7ef1 100644
--- a/sys/dev/usb/controller/usb_controller.c
+++ b/sys/dev/usb/controller/usb_controller.c
@@ -107,6 +107,7 @@ static driver_t usb_driver = {
DRIVER_MODULE(usbus, ohci, usb_driver, usb_devclass, 0, 0);
DRIVER_MODULE(usbus, uhci, usb_driver, usb_devclass, 0, 0);
DRIVER_MODULE(usbus, ehci, usb_driver, usb_devclass, 0, 0);
+DRIVER_MODULE(usbus, xhci, usb_driver, usb_devclass, 0, 0);
/* Device Only Drivers */
DRIVER_MODULE(usbus, at91_udp, usb_driver, usb_devclass, 0, 0);
@@ -359,6 +360,11 @@ usb_bus_attach(struct usb_proc_msg *pm)
device_printf(bus->bdev, "480Mbps Wireless USB v2.5\n");
break;
+ case USB_REV_3_0:
+ speed = USB_SPEED_SUPER;
+ device_printf(bus->bdev, "4.8Gbps Super Speed USB v3.0\n");
+ break;
+
default:
device_printf(bus->bdev, "Unsupported USB revision\n");
usb_root_mount_rel(bus);
diff --git a/sys/dev/usb/usb_controller.h b/sys/dev/usb/usb_controller.h
index 0f7ffac..6b15dab 100644
--- a/sys/dev/usb/usb_controller.h
+++ b/sys/dev/usb/usb_controller.h
@@ -97,6 +97,9 @@ struct usb_bus_methods {
void (*get_hw_ep_profile) (struct usb_device *udev, const struct usb_hw_ep_profile **ppf, uint8_t ep_addr);
void (*set_stall) (struct usb_device *udev, struct usb_xfer *xfer, struct usb_endpoint *ep, uint8_t *did_stall);
+
+ /* USB Device mode mandatory. USB Host mode optional. */
+
void (*clear_stall) (struct usb_device *udev, struct usb_endpoint *ep);
/* Optional transfer polling support */
@@ -106,6 +109,28 @@ struct usb_bus_methods {
/* Optional fixed power mode support */
void (*get_power_mode) (struct usb_device *udev, int8_t *pmode);
+
+ /* Optional endpoint uninit */
+
+ void (*endpoint_uninit) (struct usb_device *, struct usb_endpoint *);
+
+ /* Optional device init */
+
+ usb_error_t (*device_init) (struct usb_device *);
+
+ /* Optional device uninit */
+
+ void (*device_uninit) (struct usb_device *);
+
+ /* Optional for device and host mode */
+
+ void (*start_dma_delay) (struct usb_xfer *);
+
+ void (*device_state_change) (struct usb_device *);
+
+ /* Optional for host mode */
+
+ usb_error_t (*set_address) (struct usb_device *, struct mtx *, uint16_t);
};
/*
diff --git a/sys/dev/usb/usb_device.c b/sys/dev/usb/usb_device.c
index fcbd2c0..b68092d 100644
--- a/sys/dev/usb/usb_device.c
+++ b/sys/dev/usb/usb_device.c
@@ -45,12 +45,16 @@
#include <sys/priv.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
-#include <sys/sbuf.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/usb_ioctl.h>
+
+#if USB_HAVE_UGEN
+#include <sys/sbuf.h>
+#endif
+
#include "usbdevs.h"
#define USB_DEBUG_VAR usb_debug
@@ -79,7 +83,9 @@
/* function prototypes */
static void usb_init_endpoint(struct usb_device *, uint8_t,
- struct usb_endpoint_descriptor *, struct usb_endpoint *);
+ struct usb_endpoint_descriptor *,
+ struct usb_endpoint_ss_comp_descriptor *,
+ struct usb_endpoint *);
static void usb_unconfigure(struct usb_device *, uint8_t);
static void usb_detach_device_sub(struct usb_device *, device_t *,
uint8_t);
@@ -90,7 +96,7 @@ static void usb_init_attach_arg(struct usb_device *,
static void usb_suspend_resume_sub(struct usb_device *, device_t,
uint8_t);
static void usbd_clear_stall_proc(struct usb_proc_msg *_pm);
-usb_error_t usb_config_parse(struct usb_device *, uint8_t, uint8_t);
+static usb_error_t usb_config_parse(struct usb_device *, uint8_t, uint8_t);
static void usbd_set_device_strings(struct usb_device *);
#if USB_HAVE_UGEN
static void usb_notify_addq(const char *type, struct usb_device *);
@@ -360,7 +366,9 @@ usbd_interface_count(struct usb_device *udev, uint8_t *count)
*------------------------------------------------------------------------*/
static void
usb_init_endpoint(struct usb_device *udev, uint8_t iface_index,
- struct usb_endpoint_descriptor *edesc, struct usb_endpoint *ep)
+ struct usb_endpoint_descriptor *edesc,
+ struct usb_endpoint_ss_comp_descriptor *ecomp,
+ struct usb_endpoint *ep)
{
struct usb_bus_methods *methods;
@@ -370,6 +378,7 @@ usb_init_endpoint(struct usb_device *udev, uint8_t iface_index,
/* initialise USB endpoint structure */
ep->edesc = edesc;
+ ep->ecomp = ecomp;
ep->iface_index = iface_index;
TAILQ_INIT(&ep->endpoint_q.head);
ep->endpoint_q.command = &usbd_pipe_start;
@@ -640,7 +649,7 @@ done:
* 0: Success
* Else: Failure
*------------------------------------------------------------------------*/
-usb_error_t
+static usb_error_t
usb_config_parse(struct usb_device *udev, uint8_t iface_index, uint8_t cmd)
{
struct usb_idesc_parse_state ips;
@@ -763,8 +772,14 @@ usb_config_parse(struct usb_device *udev, uint8_t iface_index, uint8_t cmd)
ep = udev->endpoints + temp;
if (do_init) {
+ void *ecomp;
+
+ ecomp = usb_ed_comp_foreach(udev->cdesc, (void *)ed);
+ if (ecomp != NULL)
+ DPRINTFN(5, "Found endpoint companion descriptor\n");
+
usb_init_endpoint(udev,
- ips.iface_index, ed, ep);
+ ips.iface_index, ed, ecomp, ep);
}
temp ++;
@@ -904,8 +919,8 @@ done:
/*------------------------------------------------------------------------*
* usbd_set_endpoint_stall
*
- * This function is used to make a BULK or INTERRUPT endpoint
- * send STALL tokens.
+ * This function is used to make a BULK or INTERRUPT endpoint send
+ * STALL tokens in USB device mode.
*
* Returns:
* 0: Success
@@ -1536,7 +1551,6 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus,
udev->bus = bus;
udev->address = USB_START_ADDR; /* default value */
udev->plugtime = (usb_ticks_t)ticks;
- usb_set_device_state(udev, USB_STATE_POWERED);
/*
* We need to force the power mode to "on" because there are plenty
* of USB devices out there that do not work very well with
@@ -1555,6 +1569,11 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus,
udev->ctrl_ep_desc.wMaxPacketSize[0] = USB_MAX_IPACKET;
udev->ctrl_ep_desc.wMaxPacketSize[1] = 0;
udev->ctrl_ep_desc.bInterval = 0;
+
+ /* set up default endpoint companion descriptor */
+ udev->ctrl_ep_comp_desc.bLength = sizeof(udev->ctrl_ep_comp_desc);
+ udev->ctrl_ep_comp_desc.bDescriptorType = UDESC_ENDPOINT_SS_COMP;
+
udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET;
udev->speed = speed;
@@ -1579,6 +1598,7 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus,
/* init the default endpoint */
usb_init_endpoint(udev, 0,
&udev->ctrl_ep_desc,
+ &udev->ctrl_ep_comp_desc,
&udev->ctrl_ep);
/* set device index */
@@ -1597,13 +1617,29 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus,
/* Create a link from /dev/ugenX.X to the default endpoint */
make_dev_alias(udev->ctrl_dev, "%s", udev->ugen_name);
#endif
+ /* Initialise device */
+ if (bus->methods->device_init != NULL) {
+ err = (bus->methods->device_init) (udev);
+ if (err != 0) {
+ DPRINTFN(0, "device init %d failed "
+ "(%s, ignored)\n", device_index,
+ usbd_errstr(err));
+ goto done;
+ }
+ }
+ /* set powered device state after device init is complete */
+ usb_set_device_state(udev, USB_STATE_POWERED);
+
if (udev->flags.usb_mode == USB_MODE_HOST) {
err = usbd_req_set_address(udev, NULL, device_index);
- /* This is the new USB device address from now on */
-
- udev->address = device_index;
+ /*
+ * This is the new USB device address from now on, if
+ * the set address request didn't set it already.
+ */
+ if (udev->address == USB_START_ADDR)
+ udev->address = device_index;
/*
* We ignore any set-address errors, hence there are
@@ -1619,9 +1655,6 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus,
"(%s, ignored)\n", udev->address,
usbd_errstr(err));
}
- /* allow device time to set new address */
- usb_pause_mtx(NULL,
- USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE));
} else {
/* We are not self powered */
udev->flags.self_powered = 0;
@@ -1640,45 +1673,16 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus,
}
usb_set_device_state(udev, USB_STATE_ADDRESSED);
- /*
- * Get the first 8 bytes of the device descriptor !
- *
- * NOTE: "usbd_do_request" will check the device descriptor
- * next time we do a request to see if the maximum packet size
- * changed! The 8 first bytes of the device descriptor
- * contains the maximum packet size to use on control endpoint
- * 0. If this value is different from "USB_MAX_IPACKET" a new
- * USB control request will be setup!
- */
- err = usbd_req_get_desc(udev, NULL, NULL, &udev->ddesc,
- USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0);
- if (err) {
- DPRINTFN(0, "getting device descriptor "
- "at addr %d failed, %s\n", udev->address,
- usbd_errstr(err));
+ /* setup the device descriptor and the initial "wMaxPacketSize" */
+ err = usbd_setup_device_desc(udev, NULL);
+
+ if (err != 0) {
/* XXX try to re-enumerate the device */
err = usbd_req_re_enumerate(udev, NULL);
- if (err) {
+ if (err)
goto done;
- }
}
- DPRINTF("adding unit addr=%d, rev=%02x, class=%d, "
- "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n",
- udev->address, UGETW(udev->ddesc.bcdUSB),
- udev->ddesc.bDeviceClass,
- udev->ddesc.bDeviceSubClass,
- udev->ddesc.bDeviceProtocol,
- udev->ddesc.bMaxPacketSize,
- udev->ddesc.bLength,
- udev->speed);
- /* get the full device descriptor */
- err = usbd_req_get_device_desc(udev, NULL, &udev->ddesc);
- if (err) {
- DPRINTF("addr=%d, getting full desc failed\n",
- udev->address);
- goto done;
- }
/*
* Setup temporary USB attach args so that we can figure out some
* basic quirks for this device.
@@ -2068,6 +2072,10 @@ usb_free_device(struct usb_device *udev, uint8_t flag)
KASSERT(LIST_FIRST(&udev->pd_list) == NULL, ("leaked cdev entries"));
#endif
+ /* Uninitialise device */
+ if (bus->methods->device_uninit != NULL)
+ (bus->methods->device_uninit) (udev);
+
/* free device */
free(udev->serial, M_USB);
free(udev->manufacturer, M_USB);
@@ -2598,6 +2606,17 @@ usb_set_device_state(struct usb_device *udev, enum usb_dev_state state)
DPRINTF("udev %p state %s -> %s\n", udev,
usb_statestr(udev->state), usb_statestr(state));
udev->state = state;
+
+ if (udev->bus->methods->device_state_change != NULL)
+ (udev->bus->methods->device_state_change) (udev);
+}
+
+enum usb_dev_state
+usb_get_device_state(struct usb_device *udev)
+{
+ if (udev == NULL)
+ return (USB_STATE_DETACHED);
+ return (udev->state);
}
uint8_t
diff --git a/sys/dev/usb/usb_device.h b/sys/dev/usb/usb_device.h
index e53afa2..c8bc5eb 100644
--- a/sys/dev/usb/usb_device.h
+++ b/sys/dev/usb/usb_device.h
@@ -151,6 +151,7 @@ struct usb_device {
uint8_t address; /* device addess */
uint8_t device_index; /* device index in "bus->devices" */
+ uint8_t controller_slot_id; /* controller specific value */
uint8_t curr_config_index; /* current configuration index */
uint8_t curr_config_no; /* current configuration number */
uint8_t depth; /* distance from root HUB */
@@ -169,11 +170,12 @@ struct usb_device {
struct usb_device_flags flags;
struct usb_endpoint_descriptor ctrl_ep_desc; /* for endpoint 0 */
+ struct usb_endpoint_ss_comp_descriptor ctrl_ep_comp_desc; /* for endpoint 0 */
struct usb_device_descriptor ddesc; /* device descriptor */
- char *serial; /* serial number */
- char *manufacturer; /* manufacturer string */
- char *product; /* product string */
+ char *serial; /* serial number, can be NULL */
+ char *manufacturer; /* manufacturer string, can be NULL */
+ char *product; /* product string, can be NULL */
#if USB_HAVE_COMPAT_LINUX
/* Linux compat */
@@ -213,8 +215,9 @@ void usb_free_device(struct usb_device *, uint8_t);
void usb_linux_free_device(struct usb_device *dev);
uint8_t usb_peer_can_wakeup(struct usb_device *udev);
struct usb_endpoint *usb_endpoint_foreach(struct usb_device *udev, struct usb_endpoint *ep);
-void usb_set_device_state(struct usb_device *udev,
- enum usb_dev_state state);
+void usb_set_device_state(struct usb_device *, enum usb_dev_state);
+enum usb_dev_state usb_get_device_state(struct usb_device *);
+
void usbd_enum_lock(struct usb_device *);
void usbd_enum_unlock(struct usb_device *);
void usbd_sr_lock(struct usb_device *);
diff --git a/sys/dev/usb/usb_hub.c b/sys/dev/usb/usb_hub.c
index b8c4d0c..afeadaa 100644
--- a/sys/dev/usb/usb_hub.c
+++ b/sys/dev/usb/usb_hub.c
@@ -2,7 +2,7 @@
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved.
* Copyright (c) 1998 Lennart Augustsson. All rights reserved.
- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
+ * Copyright (c) 2008-2010 Hans Petter Selasky. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -110,6 +110,7 @@ struct uhub_softc {
#define UHUB_PROTO(sc) ((sc)->sc_udev->ddesc.bDeviceProtocol)
#define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB)
#define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT)
+#define UHUB_IS_SUPER_SPEED(sc) (UHUB_PROTO(sc) == UDPROTO_SSHUB)
/* prototypes for type checking: */
@@ -236,6 +237,7 @@ uhub_explore_sub(struct uhub_softc *sc, struct usb_port *up)
/* nothing to do */
goto done;
}
+
/* check if device should be re-enumerated */
if (child->flags.usb_mode == USB_MODE_HOST) {
@@ -268,14 +270,14 @@ uhub_explore_sub(struct uhub_softc *sc, struct usb_port *up)
}
/* start control transfer, if device mode */
- if (child->flags.usb_mode == USB_MODE_DEVICE) {
+ if (child->flags.usb_mode == USB_MODE_DEVICE)
usbd_ctrl_transfer_setup(child);
- }
+
/* if a HUB becomes present, do a recursive HUB explore */
- if (child->hub) {
+ if (child->hub)
err = (child->hub->explore) (child);
- }
+
done:
return (err);
}
@@ -374,11 +376,17 @@ repeat:
DPRINTF("Port %d is in Host Mode\n", portno);
if (sc->sc_st.port_status & UPS_SUSPEND) {
+ /*
+ * NOTE: Should not get here in SuperSpeed
+ * mode, because the HUB should report this
+ * bit as zero.
+ */
DPRINTF("Port %d was still "
"suspended, clearing.\n", portno);
- err = usbd_req_clear_port_feature(sc->sc_udev,
+ err = usbd_req_clear_port_feature(udev,
NULL, portno, UHF_PORT_SUSPEND);
}
+
/* USB Host Mode */
/* wait for maximum device power up time */
@@ -439,11 +447,49 @@ repeat:
case USB_SPEED_LOW:
speed = USB_SPEED_LOW;
break;
+ case USB_SPEED_SUPER:
+ if (udev->parent_hub == NULL) {
+ /* Root HUB - special case */
+ switch (sc->sc_st.port_status & UPS_OTHER_SPEED) {
+ case 0:
+ speed = USB_SPEED_FULL;
+ break;
+ case UPS_LOW_SPEED:
+ speed = USB_SPEED_LOW;
+ break;
+ case UPS_HIGH_SPEED:
+ speed = USB_SPEED_HIGH;
+ break;
+ default:
+ speed = USB_SPEED_SUPER;
+ break;
+ }
+ } else {
+ speed = USB_SPEED_SUPER;
+ }
+ break;
default:
/* same speed like parent */
speed = udev->speed;
break;
}
+ if (speed == USB_SPEED_SUPER) {
+ err = usbd_req_set_hub_u1_timeout(udev, NULL,
+ portno, 128 - (2 * udev->depth));
+ if (err) {
+ DPRINTFN(0, "port %d U1 timeout "
+ "failed, error=%s\n",
+ portno, usbd_errstr(err));
+ }
+ err = usbd_req_set_hub_u2_timeout(udev, NULL,
+ portno, 128 - (2 * udev->depth));
+ if (err) {
+ DPRINTFN(0, "port %d U2 timeout "
+ "failed, error=%s\n",
+ portno, usbd_errstr(err));
+ }
+ }
+
/*
* Figure out the device mode
*
@@ -486,6 +532,28 @@ error:
}
/*------------------------------------------------------------------------*
+ * usb_device_20_compatible
+ *
+ * Returns:
+ * 0: HUB does not support suspend and resume
+ * Else: HUB supports suspend and resume
+ *------------------------------------------------------------------------*/
+static uint8_t
+usb_device_20_compatible(struct usb_device *udev)
+{
+ if (udev == NULL)
+ return (0);
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ return (1);
+ default:
+ return (0);
+ }
+}
+
+/*------------------------------------------------------------------------*
* uhub_suspend_resume_port
*
* Returns:
@@ -508,8 +576,14 @@ uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno)
/* first clear the port suspend change bit */
- err = usbd_req_clear_port_feature(udev, NULL,
- portno, UHF_C_PORT_SUSPEND);
+ if (usb_device_20_compatible(udev)) {
+ err = usbd_req_clear_port_feature(udev, NULL,
+ portno, UHF_C_PORT_SUSPEND);
+ } else {
+ err = usbd_req_clear_port_feature(udev, NULL,
+ portno, UHF_C_PORT_LINK_STATE);
+ }
+
if (err) {
DPRINTF("clearing suspend failed.\n");
goto done;
@@ -521,12 +595,24 @@ uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno)
DPRINTF("reading port status failed.\n");
goto done;
}
- /* get current state */
+ /* convert current state */
- if (sc->sc_st.port_status & UPS_SUSPEND) {
- is_suspend = 1;
+ if (usb_device_20_compatible(udev)) {
+ if (sc->sc_st.port_status & UPS_SUSPEND) {
+ is_suspend = 1;
+ } else {
+ is_suspend = 0;
+ }
} else {
- is_suspend = 0;
+ switch (UPS_PORT_LINK_STATE_GET(sc->sc_st.port_status)) {
+ case UPS_PORT_LS_U0:
+ case UPS_PORT_LS_U1:
+ is_suspend = 0;
+ break;
+ default:
+ is_suspend = 1;
+ break;
+ }
}
DPRINTF("suspended=%u\n", is_suspend);
@@ -541,7 +627,8 @@ uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno)
*/
if (is_suspend == 0)
usb_dev_resume_peer(child);
- else if (child->flags.usb_mode == USB_MODE_DEVICE)
+ else if ((child->flags.usb_mode == USB_MODE_DEVICE) ||
+ (usb_device_20_compatible(child) == 0))
usb_dev_suspend_peer(child);
}
done:
@@ -563,6 +650,26 @@ uhub_root_intr(struct usb_bus *bus, const uint8_t *ptr, uint8_t len)
usb_needs_explore(bus, 0);
}
+static uint8_t
+uhub_is_too_deep(struct usb_device *udev)
+{
+ switch (udev->speed) {
+ case USB_SPEED_FULL:
+ case USB_SPEED_LOW:
+ case USB_SPEED_HIGH:
+ if (udev->depth > USB_HUB_MAX_DEPTH)
+ return (1);
+ break;
+ case USB_SPEED_SUPER:
+ if (udev->depth > USB_SS_HUB_DEPTH_MAX)
+ return (1);
+ break;
+ default:
+ break;
+ }
+ return (0);
+}
+
/*------------------------------------------------------------------------*
* uhub_explore
*
@@ -585,11 +692,11 @@ uhub_explore(struct usb_device *udev)
DPRINTFN(11, "udev=%p addr=%d\n", udev, udev->address);
- /* ignore hubs that are too deep */
- if (udev->depth > USB_HUB_MAX_DEPTH) {
+ /* ignore devices that are too deep */
+ if (uhub_is_too_deep(udev))
return (USB_ERR_TOO_DEEP);
- }
+ /* check if device is suspended */
if (udev->flags.self_suspended) {
/* need to wait until the child signals resume */
DPRINTF("Device is suspended!\n");
@@ -656,7 +763,7 @@ uhub_explore(struct usb_device *udev)
break;
}
}
- if (sc->sc_st.port_change & UPS_C_SUSPEND) {
+ if (sc->sc_st.port_change & (UPS_C_SUSPEND | UPS_C_PORT_LINK_STATE)) {
err = uhub_suspend_resume_port(sc, portno);
if (err) {
/* most likely the HUB is gone */
@@ -684,20 +791,81 @@ uhub_probe(device_t dev)
{
struct usb_attach_arg *uaa = device_get_ivars(dev);
- if (uaa->usb_mode != USB_MODE_HOST) {
+ if (uaa->usb_mode != USB_MODE_HOST)
return (ENXIO);
- }
+
/*
- * The subclass for USB HUBs is ignored because it is 0 for
- * some and 1 for others.
+ * The subclass for USB HUBs is currently ignored because it
+ * is 0 for some and 1 for others.
*/
- if ((uaa->info.bConfigIndex == 0) &&
- (uaa->info.bDeviceClass == UDCLASS_HUB)) {
+ if (uaa->info.bConfigIndex == 0 &&
+ uaa->info.bDeviceClass == UDCLASS_HUB)
return (0);
- }
+
return (ENXIO);
}
+/* NOTE: The information returned by this function can be wrong. */
+usb_error_t
+uhub_query_info(struct usb_device *udev, uint8_t *pnports, uint8_t *ptt)
+{
+ struct usb_hub_descriptor hubdesc20;
+ struct usb_hub_ss_descriptor hubdesc30;
+ usb_error_t err;
+ uint8_t nports;
+ uint8_t tt;
+
+ if (udev->ddesc.bDeviceClass != UDCLASS_HUB)
+ return (USB_ERR_INVAL);
+
+ nports = 0;
+ tt = 0;
+
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ /* assuming that there is one port */
+ err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, 1);
+ if (err) {
+ DPRINTFN(0, "getting USB 2.0 HUB descriptor failed,"
+ "error=%s\n", usbd_errstr(err));
+ break;
+ }
+ nports = hubdesc20.bNbrPorts;
+ if (nports > 127)
+ nports = 127;
+
+ if (udev->speed == USB_SPEED_HIGH)
+ tt = (UGETW(hubdesc20.wHubCharacteristics) >> 5) & 3;
+ break;
+
+ case USB_SPEED_SUPER:
+ err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, 1);
+ if (err) {
+ DPRINTFN(0, "Getting USB 3.0 HUB descriptor failed,"
+ "error=%s\n", usbd_errstr(err));
+ break;
+ }
+ nports = hubdesc30.bNbrPorts;
+ if (nports > 16)
+ nports = 16;
+ break;
+
+ default:
+ err = USB_ERR_INVAL;
+ break;
+ }
+
+ if (pnports != NULL)
+ *pnports = nports;
+
+ if (ptt != NULL)
+ *ptt = tt;
+
+ return (err);
+}
+
static int
uhub_attach(device_t dev)
{
@@ -706,7 +874,8 @@ uhub_attach(device_t dev)
struct usb_device *udev = uaa->device;
struct usb_device *parent_hub = udev->parent_hub;
struct usb_hub *hub;
- struct usb_hub_descriptor hubdesc;
+ struct usb_hub_descriptor hubdesc20;
+ struct usb_hub_ss_descriptor hubdesc30;
uint16_t pwrdly;
uint8_t x;
uint8_t nports;
@@ -733,38 +902,114 @@ uhub_attach(device_t dev)
parent_hub ?
parent_hub->flags.self_powered : 0);
- if (udev->depth > USB_HUB_MAX_DEPTH) {
- DPRINTFN(0, "hub depth, %d, exceeded. HUB ignored\n",
- USB_HUB_MAX_DEPTH);
+ if (uhub_is_too_deep(udev)) {
+ DPRINTFN(0, "HUB at depth %d, "
+ "exceeds maximum. HUB ignored\n", (int)udev->depth);
goto error;
}
+
if (!udev->flags.self_powered && parent_hub &&
- (!parent_hub->flags.self_powered)) {
- DPRINTFN(0, "bus powered HUB connected to "
+ !parent_hub->flags.self_powered) {
+ DPRINTFN(0, "Bus powered HUB connected to "
"bus powered HUB. HUB ignored\n");
goto error;
}
/* get HUB descriptor */
- DPRINTFN(2, "getting HUB descriptor\n");
+ DPRINTFN(2, "Getting HUB descriptor\n");
- /* assuming that there is one port */
- err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc, 1);
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ /* assuming that there is one port */
+ err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, 1);
+ if (err) {
+ DPRINTFN(0, "getting USB 2.0 HUB descriptor failed,"
+ "error=%s\n", usbd_errstr(err));
+ goto error;
+ }
+ /* get number of ports */
+ nports = hubdesc20.bNbrPorts;
- nports = hubdesc.bNbrPorts;
+ /* get power delay */
+ pwrdly = ((hubdesc20.bPwrOn2PwrGood * UHD_PWRON_FACTOR) +
+ USB_EXTRA_POWER_UP_TIME);
- if (!err && (nports >= 8)) {
/* get complete HUB descriptor */
- err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc, nports);
- }
- if (err) {
- DPRINTFN(0, "getting hub descriptor failed,"
- "error=%s\n", usbd_errstr(err));
- goto error;
- }
- if (hubdesc.bNbrPorts != nports) {
- DPRINTFN(0, "number of ports changed\n");
- goto error;
+ if (nports >= 8) {
+ /* check number of ports */
+ if (nports > 127) {
+ DPRINTFN(0, "Invalid number of USB 2.0 ports,"
+ "error=%s\n", usbd_errstr(err));
+ goto error;
+ }
+ /* get complete HUB descriptor */
+ err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, nports);
+
+ if (err) {
+ DPRINTFN(0, "Getting USB 2.0 HUB descriptor failed,"
+ "error=%s\n", usbd_errstr(err));
+ goto error;
+ }
+ if (hubdesc20.bNbrPorts != nports) {
+ DPRINTFN(0, "Number of ports changed\n");
+ goto error;
+ }
+ }
+ break;
+ case USB_SPEED_SUPER:
+ if (udev->parent_hub != NULL) {
+ err = usbd_req_set_hub_depth(udev, NULL,
+ udev->depth - 1);
+ if (err) {
+ DPRINTFN(0, "Setting USB 3.0 HUB depth failed,"
+ "error=%s\n", usbd_errstr(err));
+ goto error;
+ }
+ }
+ err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, 1);
+ if (err) {
+ DPRINTFN(0, "Getting USB 3.0 HUB descriptor failed,"
+ "error=%s\n", usbd_errstr(err));
+ goto error;
+ }
+ /* get number of ports */
+ nports = hubdesc30.bNbrPorts;
+
+ /* get power delay */
+ pwrdly = ((hubdesc30.bPwrOn2PwrGood * UHD_PWRON_FACTOR) +
+ USB_EXTRA_POWER_UP_TIME);
+
+ /* get complete HUB descriptor */
+ if (nports >= 8) {
+ /* check number of ports */
+ if (nports > ((udev->parent_hub != NULL) ? 15 : 127)) {
+ DPRINTFN(0, "Invalid number of USB 3.0 ports,"
+ "error=%s\n", usbd_errstr(err));
+ goto error;
+ }
+ /* get complete HUB descriptor */
+ err = usbd_req_get_ss_hub_descriptor(udev, NULL, &hubdesc30, nports);
+
+ if (err) {
+ DPRINTFN(0, "Getting USB 2.0 HUB descriptor failed,"
+ "error=%s\n", usbd_errstr(err));
+ goto error;
+ }
+ if (hubdesc30.bNbrPorts != nports) {
+ DPRINTFN(0, "Number of ports changed\n");
+ goto error;
+ }
+ }
+ break;
+ default:
+ DPRINTF("Assuming HUB has only one port\n");
+ /* default number of ports */
+ nports = 1;
+ /* default power delay */
+ pwrdly = ((10 * UHD_PWRON_FACTOR) + USB_EXTRA_POWER_UP_TIME);
+ break;
}
if (nports == 0) {
DPRINTFN(0, "portless HUB\n");
@@ -785,7 +1030,7 @@ uhub_attach(device_t dev)
/* initialize HUB structure */
hub->hubsoftc = sc;
hub->explore = &uhub_explore;
- hub->nports = hubdesc.bNbrPorts;
+ hub->nports = nports;
hub->hubudev = udev;
/* if self powered hub, give ports maximum current */
@@ -841,8 +1086,6 @@ uhub_attach(device_t dev)
/* XXX should check for none, individual, or ganged power? */
removable = 0;
- pwrdly = ((hubdesc.bPwrOn2PwrGood * UHD_PWRON_FACTOR) +
- USB_EXTRA_POWER_UP_TIME);
for (x = 0; x != nports; x++) {
/* set up data structures */
@@ -853,8 +1096,21 @@ uhub_attach(device_t dev)
portno = x + 1;
/* check if port is removable */
- if (!UHD_NOT_REMOV(&hubdesc, portno)) {
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ if (!UHD_NOT_REMOV(&hubdesc20, portno))
+ removable++;
+ break;
+ case USB_SPEED_SUPER:
+ if (!UHD_NOT_REMOV(&hubdesc30, portno))
+ removable++;
+ break;
+ default:
+ DPRINTF("Assuming removable port\n");
removable++;
+ break;
}
if (!err) {
/* turn the power on */
@@ -915,9 +1171,8 @@ uhub_detach(device_t dev)
struct usb_device *child;
uint8_t x;
- if (hub == NULL) { /* must be partially working */
+ if (hub == NULL) /* must be partially working */
return (0);
- }
/* Make sure interrupt transfer is gone. */
usbd_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER);
@@ -1789,6 +2044,7 @@ usb_peer_should_wakeup(struct usb_device *udev)
(udev->pwr_save.write_refs != 0) ||
((udev->pwr_save.read_refs != 0) &&
(udev->flags.usb_mode == USB_MODE_HOST) &&
+ (usb_device_20_compatible(udev) != 0) &&
(usb_peer_can_wakeup(udev) == 0)));
}
@@ -1960,13 +2216,16 @@ usb_dev_resume_peer(struct usb_device *udev)
/* reduce chance of instant resume failure by waiting a little bit */
usb_pause_mtx(NULL, USB_MS_TO_TICKS(20));
- /* resume current port (Valid in Host and Device Mode) */
- err = usbd_req_clear_port_feature(udev->parent_hub,
- NULL, udev->port_no, UHF_PORT_SUSPEND);
- if (err) {
- DPRINTFN(0, "Resuming port failed\n");
- return;
+ if (usb_device_20_compatible(udev)) {
+ /* resume current port (Valid in Host and Device Mode) */
+ err = usbd_req_clear_port_feature(udev->parent_hub,
+ NULL, udev->port_no, UHF_PORT_SUSPEND);
+ if (err) {
+ DPRINTFN(0, "Resuming port failed\n");
+ return;
+ }
}
+
/* resume settle time */
usb_pause_mtx(NULL, USB_MS_TO_TICKS(USB_PORT_RESUME_DELAY));
@@ -2006,7 +2265,8 @@ usb_dev_resume_peer(struct usb_device *udev)
usbd_sr_unlock(udev);
/* check if peer has wakeup capability */
- if (usb_peer_can_wakeup(udev)) {
+ if (usb_peer_can_wakeup(udev) &&
+ usb_device_20_compatible(udev)) {
/* clear remote wakeup */
err = usbd_req_clear_device_feature(udev,
NULL, UF_DEVICE_REMOTE_WAKEUP);
@@ -2016,7 +2276,6 @@ usb_dev_resume_peer(struct usb_device *udev)
usbd_errstr(err));
}
}
- return;
}
/*------------------------------------------------------------------------*
@@ -2054,7 +2313,6 @@ repeat:
/* check if all devices on the HUB are suspended */
for (x = 0; x != nports; x++) {
-
child = usb_bus_port_get_device(udev->bus,
udev->hub->ports + x);
@@ -2069,6 +2327,22 @@ repeat:
}
}
+ if (usb_peer_can_wakeup(udev) &&
+ usb_device_20_compatible(udev)) {
+ /*
+ * This request needs to be done before we set
+ * "udev->flags.self_suspended":
+ */
+
+ /* allow device to do remote wakeup */
+ err = usbd_req_set_device_feature(udev,
+ NULL, UF_DEVICE_REMOTE_WAKEUP);
+ if (err) {
+ DPRINTFN(0, "Setting device "
+ "remote wakeup failed\n");
+ }
+ }
+
USB_BUS_LOCK(udev->bus);
/*
* Checking for suspend condition and setting suspended bit
@@ -2086,6 +2360,17 @@ repeat:
USB_BUS_UNLOCK(udev->bus);
if (err != 0) {
+ if (usb_peer_can_wakeup(udev) &&
+ usb_device_20_compatible(udev)) {
+ /* allow device to do remote wakeup */
+ err = usbd_req_clear_device_feature(udev,
+ NULL, UF_DEVICE_REMOTE_WAKEUP);
+ if (err) {
+ DPRINTFN(0, "Setting device "
+ "remote wakeup failed\n");
+ }
+ }
+
if (udev->flags.usb_mode == USB_MODE_DEVICE) {
/* resume parent HUB first */
usb_dev_resume_peer(udev->parent_hub);
@@ -2111,16 +2396,6 @@ repeat:
usbd_sr_unlock(udev);
- if (usb_peer_can_wakeup(udev)) {
- /* allow device to do remote wakeup */
- err = usbd_req_set_device_feature(udev,
- NULL, UF_DEVICE_REMOTE_WAKEUP);
- if (err) {
- DPRINTFN(0, "Setting device "
- "remote wakeup failed\n");
- }
- }
-
if (udev->bus->methods->device_suspend != NULL) {
usb_timeout_t temp;
@@ -2133,12 +2408,15 @@ repeat:
usb_pause_mtx(NULL, USB_MS_TO_TICKS(temp));
}
- /* suspend current port */
- err = usbd_req_set_port_feature(udev->parent_hub,
- NULL, udev->port_no, UHF_PORT_SUSPEND);
- if (err) {
- DPRINTFN(0, "Suspending port failed\n");
- return;
+
+ if (usb_device_20_compatible(udev)) {
+ /* suspend current port */
+ err = usbd_req_set_port_feature(udev->parent_hub,
+ NULL, udev->port_no, UHF_PORT_SUSPEND);
+ if (err) {
+ DPRINTFN(0, "Suspending port failed\n");
+ return;
+ }
}
udev = udev->parent_hub;
diff --git a/sys/dev/usb/usb_hub.h b/sys/dev/usb/usb_hub.h
index 5b8dedf..0f59599 100644
--- a/sys/dev/usb/usb_hub.h
+++ b/sys/dev/usb/usb_hub.h
@@ -78,5 +78,6 @@ void usb_needs_explore_all(void);
void usb_bus_power_update(struct usb_bus *bus);
void usb_bus_powerd(struct usb_bus *bus);
void uhub_root_intr(struct usb_bus *, const uint8_t *, uint8_t);
+usb_error_t uhub_query_info(struct usb_device *, uint8_t *, uint8_t *);
#endif /* _USB_HUB_H_ */
diff --git a/sys/dev/usb/usb_parse.c b/sys/dev/usb/usb_parse.c
index deb52ea..8663c1e 100644
--- a/sys/dev/usb/usb_parse.c
+++ b/sys/dev/usb/usb_parse.c
@@ -180,7 +180,7 @@ usb_edesc_foreach(struct usb_config_descriptor *cd,
}
if (desc->bDescriptorType == UDESC_ENDPOINT) {
if (desc->bLength < sizeof(*ped)) {
- /* endpoint index is invalid */
+ /* endpoint descriptor is invalid */
break;
}
return ((struct usb_endpoint_descriptor *)desc);
@@ -190,6 +190,42 @@ usb_edesc_foreach(struct usb_config_descriptor *cd,
}
/*------------------------------------------------------------------------*
+ * usb_ed_comp_foreach
+ *
+ * This function will iterate all the endpoint companion descriptors
+ * within an endpoint descriptor in an interface descriptor. Starting
+ * value for the "ped" argument should be a valid endpoint companion
+ * descriptor.
+ *
+ * Return values:
+ * NULL: End of descriptors
+ * Else: A valid endpoint companion descriptor
+ *------------------------------------------------------------------------*/
+struct usb_endpoint_ss_comp_descriptor *
+usb_ed_comp_foreach(struct usb_config_descriptor *cd,
+ struct usb_endpoint_ss_comp_descriptor *ped)
+{
+ struct usb_descriptor *desc;
+
+ desc = ((struct usb_descriptor *)ped);
+
+ while ((desc = usb_desc_foreach(cd, desc))) {
+ if (desc->bDescriptorType == UDESC_INTERFACE)
+ break;
+ if (desc->bDescriptorType == UDESC_ENDPOINT)
+ break;
+ if (desc->bDescriptorType == UDESC_ENDPOINT_SS_COMP) {
+ if (desc->bLength < sizeof(*ped)) {
+ /* endpoint companion descriptor is invalid */
+ break;
+ }
+ return ((struct usb_endpoint_ss_comp_descriptor *)desc);
+ }
+ }
+ return (NULL);
+}
+
+/*------------------------------------------------------------------------*
* usbd_get_no_descriptors
*
* This function will count the total number of descriptors in the
diff --git a/sys/dev/usb/usb_request.c b/sys/dev/usb/usb_request.c
index 9f16725..859af69 100644
--- a/sys/dev/usb/usb_request.c
+++ b/sys/dev/usb/usb_request.c
@@ -245,6 +245,8 @@ usb_do_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error)
ep->is_stalled) {
ep->toggle_next = 0;
ep->is_stalled = 0;
+ /* some hardware needs a callback to clear the data toggle */
+ usbd_clear_stall_locked(udev, ep);
/* start up the current or next transfer, if any */
usb_command_wrapper(&ep->endpoint_q,
ep->endpoint_q.curr);
@@ -1300,6 +1302,28 @@ usbd_req_get_hub_descriptor(struct usb_device *udev, struct mtx *mtx,
}
/*------------------------------------------------------------------------*
+ * usbd_req_get_ss_hub_descriptor
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb_error_t
+usbd_req_get_ss_hub_descriptor(struct usb_device *udev, struct mtx *mtx,
+ struct usb_hub_ss_descriptor *hd, uint8_t nports)
+{
+ struct usb_device_request req;
+ uint16_t len = sizeof(*hd) - 32 + 1 + ((nports + 7) / 8);
+
+ req.bmRequestType = UT_READ_CLASS_DEVICE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, UDESC_SS_HUB, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+ return (usbd_do_request(udev, mtx, &req, hd));
+}
+
+/*------------------------------------------------------------------------*
* usbd_req_get_hub_status
*
* Returns:
@@ -1334,6 +1358,7 @@ usb_error_t
usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr)
{
struct usb_device_request req;
+ usb_error_t err;
DPRINTFN(6, "setting device address=%d\n", addr);
@@ -1343,9 +1368,25 @@ usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr)
USETW(req.wIndex, 0);
USETW(req.wLength, 0);
+ err = USB_ERR_INVAL;
+
+ /* check if USB controller handles set address */
+ if (udev->bus->methods->set_address != NULL)
+ err = (udev->bus->methods->set_address) (udev, mtx, addr);
+
+ if (err != USB_ERR_INVAL)
+ goto done;
+
/* Setting the address should not take more than 1 second ! */
- return (usbd_do_request_flags(udev, mtx, &req, NULL,
- USB_DELAY_STATUS_STAGE, NULL, 1000));
+ err = usbd_do_request_flags(udev, mtx, &req, NULL,
+ USB_DELAY_STATUS_STAGE, NULL, 1000);
+
+done:
+ /* allow device time to set new address */
+ usb_pause_mtx(mtx,
+ USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE));
+
+ return (err);
}
/*------------------------------------------------------------------------*
@@ -1413,6 +1454,71 @@ usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx,
}
/*------------------------------------------------------------------------*
+ * usbd_req_set_hub_u1_timeout
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb_error_t
+usbd_req_set_hub_u1_timeout(struct usb_device *udev, struct mtx *mtx,
+ uint8_t port, uint8_t timeout)
+{
+ struct usb_device_request req;
+
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ req.bRequest = UR_SET_FEATURE;
+ USETW(req.wValue, UHF_PORT_U1_TIMEOUT);
+ req.wIndex[0] = port;
+ req.wIndex[1] = timeout;
+ USETW(req.wLength, 0);
+ return (usbd_do_request(udev, mtx, &req, 0));
+}
+
+/*------------------------------------------------------------------------*
+ * usbd_req_set_hub_u2_timeout
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb_error_t
+usbd_req_set_hub_u2_timeout(struct usb_device *udev, struct mtx *mtx,
+ uint8_t port, uint8_t timeout)
+{
+ struct usb_device_request req;
+
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ req.bRequest = UR_SET_FEATURE;
+ USETW(req.wValue, UHF_PORT_U2_TIMEOUT);
+ req.wIndex[0] = port;
+ req.wIndex[1] = timeout;
+ USETW(req.wLength, 0);
+ return (usbd_do_request(udev, mtx, &req, 0));
+}
+
+/*------------------------------------------------------------------------*
+ * usbd_req_set_hub_depth
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb_error_t
+usbd_req_set_hub_depth(struct usb_device *udev, struct mtx *mtx,
+ uint16_t depth)
+{
+ struct usb_device_request req;
+
+ req.bmRequestType = UT_WRITE_CLASS_DEVICE;
+ req.bRequest = UR_SET_HUB_DEPTH;
+ USETW(req.wValue, depth);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return (usbd_do_request(udev, mtx, &req, 0));
+}
+
+/*------------------------------------------------------------------------*
* usbd_req_clear_port_feature
*
* Returns:
@@ -1645,6 +1751,68 @@ usbd_req_get_config(struct usb_device *udev, struct mtx *mtx, uint8_t *pconf)
}
/*------------------------------------------------------------------------*
+ * usbd_setup_device_desc
+ *------------------------------------------------------------------------*/
+usb_error_t
+usbd_setup_device_desc(struct usb_device *udev, struct mtx *mtx)
+{
+ usb_error_t err;
+
+ /*
+ * Get the first 8 bytes of the device descriptor !
+ *
+ * NOTE: "usbd_do_request()" will check the device descriptor
+ * next time we do a request to see if the maximum packet size
+ * changed! The 8 first bytes of the device descriptor
+ * contains the maximum packet size to use on control endpoint
+ * 0. If this value is different from "USB_MAX_IPACKET" a new
+ * USB control request will be setup!
+ */
+ switch (udev->speed) {
+ case USB_SPEED_FULL:
+ case USB_SPEED_LOW:
+ err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc,
+ USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0);
+ if (err != 0) {
+ DPRINTFN(0, "getting device descriptor "
+ "at addr %d failed, %s\n", udev->address,
+ usbd_errstr(err));
+ return (err);
+ }
+ break;
+ default:
+ DPRINTF("Minimum MaxPacketSize is large enough "
+ "to hold the complete device descriptor\n");
+ break;
+ }
+
+ /* get the full device descriptor */
+ err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
+
+ /* try one more time, if error */
+ if (err)
+ err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
+
+ if (err) {
+ DPRINTF("addr=%d, getting full desc failed\n",
+ udev->address);
+ return (err);
+ }
+
+ DPRINTF("adding unit addr=%d, rev=%02x, class=%d, "
+ "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n",
+ udev->address, UGETW(udev->ddesc.bcdUSB),
+ udev->ddesc.bDeviceClass,
+ udev->ddesc.bDeviceSubClass,
+ udev->ddesc.bDeviceProtocol,
+ udev->ddesc.bMaxPacketSize,
+ udev->ddesc.bLength,
+ udev->speed);
+
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
* usbd_req_re_enumerate
*
* NOTE: After this function returns the hardware is in the
@@ -1678,6 +1846,7 @@ retry:
old_addr, usbd_errstr(err));
goto done;
}
+
/*
* After that the port has been reset our device should be at
* address zero:
@@ -1687,6 +1856,9 @@ retry:
/* reset "bMaxPacketSize" */
udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET;
+ /* reset USB state */
+ usb_set_device_state(udev, USB_STATE_POWERED);
+
/*
* Restore device address:
*/
@@ -1696,29 +1868,16 @@ retry:
DPRINTFN(0, "addr=%d, set address failed! (%s, ignored)\n",
old_addr, usbd_errstr(err));
}
- /* restore device address */
- udev->address = old_addr;
+ /*
+ * Restore device address, if the controller driver did not
+ * set a new one:
+ */
+ if (udev->address == USB_START_ADDR)
+ udev->address = old_addr;
- /* allow device time to set new address */
- usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE));
+ /* setup the device descriptor and the initial "wMaxPacketSize" */
+ err = usbd_setup_device_desc(udev, mtx);
- /* get the device descriptor */
- err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc,
- USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0);
- if (err) {
- DPRINTFN(0, "getting device descriptor "
- "at addr %d failed, %s\n", udev->address,
- usbd_errstr(err));
- goto done;
- }
- /* get the full device descriptor */
- err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
- if (err) {
- DPRINTFN(0, "addr=%d, getting device "
- "descriptor failed, %s\n", old_addr,
- usbd_errstr(err));
- goto done;
- }
done:
if (err && do_retry) {
/* give the USB firmware some time to load */
@@ -1729,7 +1888,11 @@ done:
goto retry;
}
/* restore address */
- udev->address = old_addr;
+ if (udev->address == USB_START_ADDR)
+ udev->address = old_addr;
+ /* update state, if successful */
+ if (err == 0)
+ usb_set_device_state(udev, USB_STATE_ADDRESSED);
return (err);
}
diff --git a/sys/dev/usb/usb_request.h b/sys/dev/usb/usb_request.h
index 2158565..1ce8b56 100644
--- a/sys/dev/usb/usb_request.h
+++ b/sys/dev/usb/usb_request.h
@@ -56,6 +56,9 @@ usb_error_t usbd_req_get_device_status(struct usb_device *udev,
usb_error_t usbd_req_get_hub_descriptor(struct usb_device *udev,
struct mtx *mtx, struct usb_hub_descriptor *hd,
uint8_t nports);
+usb_error_t usbd_req_get_ss_hub_descriptor(struct usb_device *udev,
+ struct mtx *mtx, struct usb_hub_ss_descriptor *hd,
+ uint8_t nports);
usb_error_t usbd_req_get_hub_status(struct usb_device *udev, struct mtx *mtx,
struct usb_hub_status *st);
usb_error_t usbd_req_get_port_status(struct usb_device *udev, struct mtx *mtx,
@@ -68,8 +71,17 @@ usb_error_t usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx,
uint16_t sel);
usb_error_t usbd_req_set_port_feature(struct usb_device *udev,
struct mtx *mtx, uint8_t port, uint16_t sel);
+usb_error_t usbd_setup_device_desc(struct usb_device *udev, struct mtx *mtx);
usb_error_t usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx);
-usb_error_t usbd_req_clear_device_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel);
-usb_error_t usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx, uint16_t sel);
+usb_error_t usbd_req_clear_device_feature(struct usb_device *udev,
+ struct mtx *mtx, uint16_t sel);
+usb_error_t usbd_req_set_device_feature(struct usb_device *udev,
+ struct mtx *mtx, uint16_t sel);
+usb_error_t usbd_req_set_hub_u1_timeout(struct usb_device *udev,
+ struct mtx *mtx, uint8_t port, uint8_t timeout);
+usb_error_t usbd_req_set_hub_u2_timeout(struct usb_device *udev,
+ struct mtx *mtx, uint8_t port, uint8_t timeout);
+usb_error_t usbd_req_set_hub_depth(struct usb_device *udev,
+ struct mtx *mtx, uint16_t depth);
#endif /* _USB_REQUEST_H_ */
diff --git a/sys/dev/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c
index af83e5a..e0f5a3b 100644
--- a/sys/dev/usb/usb_transfer.c
+++ b/sys/dev/usb/usb_transfer.c
@@ -109,7 +109,6 @@ static int usbd_setup_ctrl_transfer(struct usb_xfer *);
static void usb_callback_proc(struct usb_proc_msg *);
static void usbd_callback_ss_done_defer(struct usb_xfer *);
static void usbd_callback_wrapper(struct usb_xfer_queue *);
-static void usb_dma_delay_done_cb(void *);
static void usbd_transfer_start_cb(void *);
static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *);
static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr,
@@ -137,14 +136,10 @@ static void
usbd_update_max_frame_size(struct usb_xfer *xfer)
{
/* compute maximum frame size */
+ /* this computation should not overflow 16-bit */
+ /* max = 15 * 1024 */
- if (xfer->max_packet_count == 2) {
- xfer->max_frame_size = 2 * xfer->max_packet_size;
- } else if (xfer->max_packet_count == 3) {
- xfer->max_frame_size = 3 * xfer->max_packet_size;
- } else {
- xfer->max_frame_size = xfer->max_packet_size;
- }
+ xfer->max_frame_size = xfer->max_packet_size * xfer->max_packet_count;
}
/*------------------------------------------------------------------------*
@@ -320,6 +315,7 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm)
};
struct usb_xfer *xfer = parm->curr_xfer;
const struct usb_config *setup = parm->curr_setup;
+ struct usb_endpoint_ss_comp_descriptor *ecomp;
struct usb_endpoint_descriptor *edesc;
struct usb_std_packet_size std_size;
usb_frcount_t n_frlengths;
@@ -339,6 +335,7 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm)
goto done;
}
edesc = xfer->endpoint->edesc;
+ ecomp = xfer->endpoint->ecomp;
type = (edesc->bmAttributes & UE_XFERTYPE);
@@ -355,9 +352,54 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm)
parm->bufsize = setup->bufsize;
- if (parm->speed == USB_SPEED_HIGH) {
+ switch (parm->speed) {
+ case USB_SPEED_HIGH:
+ switch (type) {
+ case UE_ISOCHRONOUS:
+ case UE_INTERRUPT:
+ xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3;
+
+ /* check for invalid max packet count */
+ if (xfer->max_packet_count > 3)
+ xfer->max_packet_count = 3;
+ break;
+ default:
+ break;
+ }
+ xfer->max_packet_size &= 0x7FF;
+ break;
+ case USB_SPEED_SUPER:
xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3;
+
+ if (ecomp != NULL)
+ xfer->max_packet_count += ecomp->bMaxBurst;
+
+ if ((xfer->max_packet_count == 0) ||
+ (xfer->max_packet_count > 16))
+ xfer->max_packet_count = 16;
+
+ switch (type) {
+ case UE_CONTROL:
+ xfer->max_packet_count = 1;
+ break;
+ case UE_ISOCHRONOUS:
+ if (ecomp != NULL) {
+ uint8_t mult;
+
+ mult = (ecomp->bmAttributes & 3) + 1;
+ if (mult > 3)
+ mult = 3;
+
+ xfer->max_packet_count *= mult;
+ }
+ break;
+ default:
+ break;
+ }
xfer->max_packet_size &= 0x7FF;
+ break;
+ default:
+ break;
}
/* range check "max_packet_count" */
@@ -450,42 +492,58 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm)
} else {
/*
- * if a value is specified use that else check the endpoint
- * descriptor
+ * If a value is specified use that else check the
+ * endpoint descriptor!
*/
- if (xfer->interval == 0) {
+ if (type == UE_INTERRUPT) {
- if (type == UE_INTERRUPT) {
+ uint32_t temp;
+
+ if (xfer->interval == 0) {
xfer->interval = edesc->bInterval;
switch (parm->speed) {
- case USB_SPEED_SUPER:
- case USB_SPEED_VARIABLE:
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ break;
+ default:
/* 125us -> 1ms */
if (xfer->interval < 4)
xfer->interval = 1;
else if (xfer->interval > 16)
- xfer->interval = (1<<(16-4));
+ xfer->interval = (1 << (16 - 4));
else
xfer->interval =
- (1 << (xfer->interval-4));
- break;
- case USB_SPEED_HIGH:
- /* 125us -> 1ms */
- xfer->interval /= 8;
+ (1 << (xfer->interval - 4));
break;
- default:
- break;
- }
- if (xfer->interval == 0) {
- /*
- * One millisecond is the smallest
- * interval we support:
- */
- xfer->interval = 1;
}
}
+
+ if (xfer->interval == 0) {
+ /*
+ * One millisecond is the smallest
+ * interval we support:
+ */
+ xfer->interval = 1;
+ }
+
+ xfer->fps_shift = 0;
+ temp = 1;
+
+ while ((temp != 0) && (temp < xfer->interval)) {
+ xfer->fps_shift++;
+ temp *= 2;
+ }
+
+ switch (parm->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ break;
+ default:
+ xfer->fps_shift += 3;
+ break;
+ }
}
}
@@ -2185,11 +2243,9 @@ done:
* transfer. This code path is ususally only used when there is an USB
* error like USB_ERR_CANCELLED.
*------------------------------------------------------------------------*/
-static void
-usb_dma_delay_done_cb(void *arg)
+void
+usb_dma_delay_done_cb(struct usb_xfer *xfer)
{
- struct usb_xfer *xfer = arg;
-
USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
DPRINTFN(3, "Completed %p\n", xfer);
@@ -2555,14 +2611,17 @@ static uint8_t
usbd_callback_wrapper_sub(struct usb_xfer *xfer)
{
struct usb_endpoint *ep;
+ struct usb_bus *bus;
usb_frcount_t x;
+ bus = xfer->xroot->bus;
+
if ((!xfer->flags_int.open) &&
(!xfer->flags_int.did_close)) {
DPRINTF("close\n");
- USB_BUS_LOCK(xfer->xroot->bus);
+ USB_BUS_LOCK(bus);
(xfer->endpoint->methods->close) (xfer);
- USB_BUS_UNLOCK(xfer->xroot->bus);
+ USB_BUS_UNLOCK(bus);
/* only close once */
xfer->flags_int.did_close = 1;
return (1); /* wait for new callback */
@@ -2571,9 +2630,10 @@ usbd_callback_wrapper_sub(struct usb_xfer *xfer)
* If we have a non-hardware induced error we
* need to do the DMA delay!
*/
- if (((xfer->error == USB_ERR_CANCELLED) ||
- (xfer->error == USB_ERR_TIMEOUT)) &&
- (!xfer->flags_int.did_dma_delay)) {
+ if (xfer->error != 0 && !xfer->flags_int.did_dma_delay &&
+ (xfer->error == USB_ERR_CANCELLED ||
+ xfer->error == USB_ERR_TIMEOUT ||
+ bus->methods->start_dma_delay != NULL)) {
usb_timeout_t temp;
@@ -2589,10 +2649,20 @@ usbd_callback_wrapper_sub(struct usb_xfer *xfer)
"on %p\n", temp, xfer);
if (temp != 0) {
- USB_BUS_LOCK(xfer->xroot->bus);
- usbd_transfer_timeout_ms(xfer,
- &usb_dma_delay_done_cb, temp);
- USB_BUS_UNLOCK(xfer->xroot->bus);
+ USB_BUS_LOCK(bus);
+ /*
+ * Some hardware solutions have dedicated
+ * events when it is safe to free DMA'ed
+ * memory. For the other hardware platforms we
+ * use a static delay.
+ */
+ if (bus->methods->start_dma_delay != NULL) {
+ (bus->methods->start_dma_delay) (xfer);
+ } else {
+ usbd_transfer_timeout_ms(xfer,
+ (void *)&usb_dma_delay_done_cb, temp);
+ }
+ USB_BUS_UNLOCK(bus);
return (1); /* wait for new callback */
}
}
@@ -2684,7 +2754,7 @@ usbd_callback_wrapper_sub(struct usb_xfer *xfer)
* If the current USB transfer is completing we need to start the
* next one:
*/
- USB_BUS_LOCK(xfer->xroot->bus);
+ USB_BUS_LOCK(bus);
if (ep->endpoint_q.curr == xfer) {
usb_command_wrapper(&ep->endpoint_q, NULL);
@@ -2696,7 +2766,7 @@ usbd_callback_wrapper_sub(struct usb_xfer *xfer)
xfer->endpoint->is_synced = 0;
}
}
- USB_BUS_UNLOCK(xfer->xroot->bus);
+ USB_BUS_UNLOCK(bus);
done:
return (0);
}
@@ -2842,12 +2912,34 @@ repeat:
* data toggle.
*------------------------------------------------------------------------*/
void
+usbd_clear_stall_locked(struct usb_device *udev, struct usb_endpoint *ep)
+{
+ USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
+
+ /* check that we have a valid case */
+ if (udev->flags.usb_mode == USB_MODE_HOST &&
+ udev->parent_hub != NULL &&
+ udev->bus->methods->clear_stall != NULL &&
+ ep->methods != NULL) {
+ (udev->bus->methods->clear_stall) (udev, ep);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usbd_clear_data_toggle - factored out code
+ *
+ * NOTE: the intention of this function is not to reset the hardware
+ * data toggle on the USB device side.
+ *------------------------------------------------------------------------*/
+void
usbd_clear_data_toggle(struct usb_device *udev, struct usb_endpoint *ep)
{
DPRINTFN(5, "udev=%p endpoint=%p\n", udev, ep);
USB_BUS_LOCK(udev->bus);
ep->toggle_next = 0;
+ /* some hardware needs a callback to clear the data toggle */
+ usbd_clear_stall_locked(udev, ep);
USB_BUS_UNLOCK(udev->bus);
}
diff --git a/sys/dev/usb/usb_transfer.h b/sys/dev/usb/usb_transfer.h
index 2c5fe6f..71157ca 100644
--- a/sys/dev/usb/usb_transfer.h
+++ b/sys/dev/usb/usb_transfer.h
@@ -101,7 +101,7 @@ struct usb_setup_params {
usb_frlength_t bufsize;
usb_frlength_t bufsize_max;
- uint16_t hc_max_frame_size;
+ uint32_t hc_max_frame_size;
uint16_t hc_max_packet_size;
uint8_t hc_max_packet_count;
enum usb_dev_speed speed;
@@ -114,6 +114,7 @@ struct usb_setup_params {
uint8_t usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm,
struct usb_page_cache **ppc, usb_size_t size, usb_size_t align,
usb_size_t count);
+void usb_dma_delay_done_cb(struct usb_xfer *);
void usb_command_wrapper(struct usb_xfer_queue *pq,
struct usb_xfer *xfer);
void usbd_pipe_enter(struct usb_xfer *xfer);
@@ -124,6 +125,8 @@ void usbd_transfer_enqueue(struct usb_xfer_queue *pq,
struct usb_xfer *xfer);
void usbd_transfer_setup_sub(struct usb_setup_params *parm);
void usbd_ctrl_transfer_setup(struct usb_device *udev);
+void usbd_clear_stall_locked(struct usb_device *udev,
+ struct usb_endpoint *ep);
void usbd_clear_data_toggle(struct usb_device *udev,
struct usb_endpoint *ep);
usb_callback_t usbd_do_request_callback;
diff --git a/sys/dev/usb/usbdi.h b/sys/dev/usb/usbdi.h
index 79b73ca..1c2d412 100644
--- a/sys/dev/usb/usbdi.h
+++ b/sys/dev/usb/usbdi.h
@@ -135,6 +135,7 @@ struct usb_endpoint {
struct usb_xfer_queue endpoint_q; /* queue of USB transfers */
struct usb_endpoint_descriptor *edesc;
+ struct usb_endpoint_ss_comp_descriptor *ecomp;
struct usb_pipe_methods *methods; /* set by HC driver */
uint16_t isoc_next;
diff --git a/sys/dev/usb/usbdi_util.h b/sys/dev/usb/usbdi_util.h
index 352f3ff..1e450f8 100644
--- a/sys/dev/usb/usbdi_util.h
+++ b/sys/dev/usb/usbdi_util.h
@@ -51,6 +51,9 @@ struct usb_interface_descriptor *usb_idesc_foreach(
struct usb_endpoint_descriptor *usb_edesc_foreach(
struct usb_config_descriptor *cd,
struct usb_endpoint_descriptor *ped);
+struct usb_endpoint_ss_comp_descriptor *usb_ed_comp_foreach(
+ struct usb_config_descriptor *cd,
+ struct usb_endpoint_ss_comp_descriptor *ped);
uint8_t usbd_get_no_descriptors(struct usb_config_descriptor *cd,
uint8_t type);
uint8_t usbd_get_no_alts(struct usb_config_descriptor *cd,
OpenPOWER on IntegriCloud