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)
This commit is contained in:
Hans Petter Selasky 2010-10-04 23:18:05 +00:00
parent 2ecb4e919f
commit 963169b4af
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=213435
13 changed files with 846 additions and 204 deletions

View file

@ -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);

View file

@ -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);
};
/*

View file

@ -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 @@ usbd_set_config_index(struct usb_device *udev, uint8_t index)
* 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 @@ usbd_set_alt_interface_index(struct usb_device *udev,
/*------------------------------------------------------------------------*
* 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,12 +1617,28 @@ 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 */
/*
* 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;
/*
@ -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

View file

@ -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 *);

View file

@ -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 @@ uhub_reattach_port(struct uhub_softc *sc, uint8_t portno)
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 @@ uhub_reattach_port(struct uhub_softc *sc, uint8_t portno)
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
*
@ -485,6 +531,28 @@ uhub_reattach_port(struct uhub_softc *sc, uint8_t portno)
return (err);
}
/*------------------------------------------------------------------------*
* 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
*
@ -508,8 +576,14 @@ uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno)
/* first clear the port suspend change bit */
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,13 +595,25 @@ 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 (usb_device_20_compatible(udev)) {
if (sc->sc_st.port_status & UPS_SUSPEND) {
is_suspend = 1;
} else {
is_suspend = 0;
}
} else {
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,39 +902,115 @@ 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");
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, &hubdesc, 1);
nports = hubdesc.bNbrPorts;
if (!err && (nports >= 8)) {
/* get complete HUB descriptor */
err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc, nports);
}
err = usbd_req_get_hub_descriptor(udev, NULL, &hubdesc20, 1);
if (err) {
DPRINTFN(0, "getting hub descriptor failed,"
DPRINTFN(0, "getting USB 2.0 HUB descriptor failed,"
"error=%s\n", usbd_errstr(err));
goto error;
}
if (hubdesc.bNbrPorts != nports) {
DPRINTFN(0, "number of ports changed\n");
/* get number of ports */
nports = hubdesc20.bNbrPorts;
/* get power delay */
pwrdly = ((hubdesc20.bPwrOn2PwrGood * UHD_PWRON_FACTOR) +
USB_EXTRA_POWER_UP_TIME);
/* get complete HUB descriptor */
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");
goto error;
@ -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,6 +2216,7 @@ 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));
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);
@ -1967,6 +2224,8 @@ usb_dev_resume_peer(struct usb_device *udev)
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 @@ usb_dev_suspend_peer(struct usb_device *udev)
/* 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 @@ usb_dev_suspend_peer(struct usb_device *udev)
}
}
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 @@ usb_dev_suspend_peer(struct usb_device *udev)
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 @@ usb_dev_suspend_peer(struct usb_device *udev)
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,6 +2408,8 @@ usb_dev_suspend_peer(struct usb_device *udev)
usb_pause_mtx(NULL, USB_MS_TO_TICKS(temp));
}
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);
@ -2140,6 +2417,7 @@ usb_dev_suspend_peer(struct usb_device *udev)
DPRINTFN(0, "Suspending port failed\n");
return;
}
}
udev = udev->parent_hub;
goto repeat;

View file

@ -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_ */

View file

@ -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);
@ -189,6 +189,42 @@ usb_edesc_foreach(struct usb_config_descriptor *cd,
return (NULL);
}
/*------------------------------------------------------------------------*
* 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
*

View file

@ -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);
@ -1299,6 +1301,28 @@ usbd_req_get_hub_descriptor(struct usb_device *udev, struct mtx *mtx,
return (usbd_do_request(udev, mtx, &req, hd));
}
/*------------------------------------------------------------------------*
* 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
*
@ -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);
}
/*------------------------------------------------------------------------*
@ -1412,6 +1453,71 @@ usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx,
return (usbd_do_request(udev, mtx, &req, 0));
}
/*------------------------------------------------------------------------*
* 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
*
@ -1644,6 +1750,68 @@ usbd_req_get_config(struct usb_device *udev, struct mtx *mtx, uint8_t *pconf)
return (usbd_do_request(udev, mtx, &req, 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
*
@ -1678,6 +1846,7 @@ usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx)
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 @@ usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx)
/* 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 @@ usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx)
DPRINTFN(0, "addr=%d, set address failed! (%s, ignored)\n",
old_addr, usbd_errstr(err));
}
/* restore device address */
/*
* 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 @@ usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx)
goto retry;
}
/* restore address */
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);
}

View file

@ -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_ */

View file

@ -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,34 +492,34 @@ 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) {
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;
break;
default:
(1 << (xfer->interval - 4));
break;
}
}
if (xfer->interval == 0) {
/*
* One millisecond is the smallest
@ -485,6 +527,22 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm)
*/
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 @@ usbd_callback_wrapper(struct usb_xfer_queue *pq)
* 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);
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,
&usb_dma_delay_done_cb, temp);
USB_BUS_UNLOCK(xfer->xroot->bus);
(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 @@ usbd_ctrl_transfer_setup(struct usb_device *udev)
* 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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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,