aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/usb2/core
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb2/core')
-rw-r--r--sys/dev/usb2/core/usb2_busdma.c25
-rw-r--r--sys/dev/usb2/core/usb2_core.h2
-rw-r--r--sys/dev/usb2/core/usb2_device.c80
-rw-r--r--sys/dev/usb2/core/usb2_device.h14
-rw-r--r--sys/dev/usb2/core/usb2_dynamic.c23
-rw-r--r--sys/dev/usb2/core/usb2_dynamic.h4
-rw-r--r--sys/dev/usb2/core/usb2_generic.c16
-rw-r--r--sys/dev/usb2/core/usb2_handle_request.c6
-rw-r--r--sys/dev/usb2/core/usb2_hub.c530
-rw-r--r--sys/dev/usb2/core/usb2_hub.h2
-rw-r--r--sys/dev/usb2/core/usb2_msctest.c161
-rw-r--r--sys/dev/usb2/core/usb2_msctest.h25
-rw-r--r--sys/dev/usb2/core/usb2_request.c58
-rw-r--r--sys/dev/usb2/core/usb2_request.h2
-rw-r--r--sys/dev/usb2/core/usb2_transfer.c9
-rw-r--r--sys/dev/usb2/core/usb2_transfer.h2
16 files changed, 731 insertions, 228 deletions
diff --git a/sys/dev/usb2/core/usb2_busdma.c b/sys/dev/usb2/core/usb2_busdma.c
index 33851f0f1431..be99e00d4abc 100644
--- a/sys/dev/usb2/core/usb2_busdma.c
+++ b/sys/dev/usb2/core/usb2_busdma.c
@@ -29,12 +29,15 @@
#include <dev/usb2/include/usb2_standard.h>
#include <dev/usb2/include/usb2_defs.h>
+#define USB_DEBUG_VAR usb2_debug
+
#include <dev/usb2/core/usb2_core.h>
#include <dev/usb2/core/usb2_busdma.h>
#include <dev/usb2/core/usb2_process.h>
#include <dev/usb2/core/usb2_transfer.h>
#include <dev/usb2/core/usb2_device.h>
#include <dev/usb2/core/usb2_util.h>
+#include <dev/usb2/core/usb2_debug.h>
#include <dev/usb2/controller/usb2_controller.h>
#include <dev/usb2/controller/usb2_bus.h>
@@ -418,7 +421,16 @@ usb2_pc_common_mem_cb(void *arg, bus_dma_segment_t *segs,
pc->page_offset_buf = rem;
pc->page_offset_end += rem;
nseg--;
-
+#if (USB_DEBUG != 0)
+ if (rem != (USB_P2U(pc->buffer) & (USB_PAGE_SIZE - 1))) {
+ /*
+ * This check verifies that the physical address is correct:
+ */
+ DPRINTFN(0, "Page offset was not preserved!\n");
+ error = 1;
+ goto done;
+ }
+#endif
while (nseg > 0) {
nseg--;
segs++;
@@ -788,7 +800,16 @@ usb2_pc_common_mem_cb(struct usb2_page_cache *pc, bus_dma_segment_t *segs,
ext_seg = 0;
}
nseg--;
-
+#if (USB_DEBUG != 0)
+ if (rem != (USB_P2U(pc->buffer) & (USB_PAGE_SIZE - 1))) {
+ /*
+ * This check verifies that the physical address is correct:
+ */
+ DPRINTFN(0, "Page offset was not preserved!\n");
+ error = 1;
+ goto done;
+ }
+#endif
while (nseg > 0) {
nseg--;
segs++;
diff --git a/sys/dev/usb2/core/usb2_core.h b/sys/dev/usb2/core/usb2_core.h
index acc30a88d2f3..f1e5586579e7 100644
--- a/sys/dev/usb2/core/usb2_core.h
+++ b/sys/dev/usb2/core/usb2_core.h
@@ -440,6 +440,7 @@ uint8_t usb2_get_interface_altindex(struct usb2_interface *iface);
usb2_error_t usb2_set_alt_interface_index(struct usb2_device *udev,
uint8_t iface_index, uint8_t alt_index);
uint8_t usb2_get_speed(struct usb2_device *udev);
+uint32_t usb2_get_isoc_fps(struct usb2_device *udev);
usb2_error_t usb2_transfer_setup(struct usb2_device *udev,
const uint8_t *ifaces, struct usb2_xfer **pxfer,
const struct usb2_config *setup_start, uint16_t n_setup,
@@ -464,5 +465,6 @@ void usb2_set_iface_perm(struct usb2_device *udev, uint8_t iface_index,
uint32_t uid, uint32_t gid, uint16_t mode);
uint8_t usb2_get_bus_index(struct usb2_device *udev);
uint8_t usb2_get_device_index(struct usb2_device *udev);
+void usb2_set_power_mode(struct usb2_device *udev, uint8_t power_mode);
#endif /* _USB2_CORE_H_ */
diff --git a/sys/dev/usb2/core/usb2_device.c b/sys/dev/usb2/core/usb2_device.c
index 2356cc58b37d..805de0a3cff4 100644
--- a/sys/dev/usb2/core/usb2_device.c
+++ b/sys/dev/usb2/core/usb2_device.c
@@ -1186,7 +1186,7 @@ usb2_suspend_resume_sub(struct usb2_device *udev, device_t dev, uint8_t do_suspe
}
/*------------------------------------------------------------------------*
- * usb2_suspend_resume_device
+ * usb2_suspend_resume
*
* The following function will suspend or resume the USB device.
*
@@ -1339,7 +1339,13 @@ usb2_alloc_device(device_t parent_dev, struct usb2_bus *bus,
udev->bus = bus;
udev->address = USB_START_ADDR; /* default value */
udev->plugtime = (uint32_t)ticks;
+ /*
+ * 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
+ * automatic suspend and resume!
+ */
udev->power_mode = USB_POWER_MODE_ON;
+ udev->pwr_save.last_xfer_time = ticks;
/* we are not ready yet */
udev->refcount = 1;
@@ -1450,7 +1456,11 @@ usb2_alloc_device(device_t parent_dev, struct usb2_bus *bus,
if (err) {
DPRINTFN(0, "getting device descriptor "
"at addr %d failed!\n", udev->address);
- goto done;
+ /* XXX try to re-enumerate the device */
+ err = usb2_req_re_enumerate(udev, &Giant);
+ if (err) {
+ goto done;
+ }
}
DPRINTF("adding unit addr=%d, rev=%02x, class=%d, "
"subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n",
@@ -1546,6 +1556,7 @@ usb2_alloc_device(device_t parent_dev, struct usb2_bus *bus,
if (udev->flags.usb2_mode == USB_MODE_HOST) {
uint8_t config_index;
uint8_t config_quirk;
+ uint8_t set_config_failed = 0;
/*
* Most USB devices should attach to config index 0 by
@@ -1580,11 +1591,27 @@ repeat_set_config:
err = usb2_set_config_index(udev, config_index);
sx_unlock(udev->default_sx + 1);
if (err) {
- DPRINTFN(0, "Failure selecting "
- "configuration index %u: %s, port %u, addr %u\n",
- config_index, usb2_errstr(err), udev->port_no,
- udev->address);
-
+ if (udev->ddesc.bNumConfigurations != 0) {
+ if (!set_config_failed) {
+ set_config_failed = 1;
+ /* XXX try to re-enumerate the device */
+ err = usb2_req_re_enumerate(
+ udev, &Giant);
+ if (err == 0)
+ goto repeat_set_config;
+ }
+ DPRINTFN(0, "Failure selecting "
+ "configuration index %u: %s, port %u, "
+ "addr %u (ignored)\n",
+ config_index, usb2_errstr(err), udev->port_no,
+ udev->address);
+ }
+ /*
+ * Some USB devices do not have any
+ * configurations. Ignore any set config
+ * failures!
+ */
+ err = 0;
} else if (config_quirk) {
/* user quirk selects configuration index */
} else if ((config_index + 1) < udev->ddesc.bNumConfigurations) {
@@ -1608,7 +1635,7 @@ repeat_set_config:
goto repeat_set_config;
}
}
- } else if (usb2_test_huawei(udev, &uaa) == 0) {
+ } else if (usb2_test_huawei_autoinst_p(udev, &uaa) == 0) {
DPRINTFN(0, "Found Huawei auto-install disk!\n");
err = USB_ERR_STALLED; /* fake an error */
}
@@ -1670,6 +1697,11 @@ usb2_free_device(struct usb2_device *udev)
bus = udev->bus;
+ printf("ugen%u.%u: <%s> at %s (disconnected)\n",
+ device_get_unit(bus->bdev),
+ udev->device_index, udev->manufacturer,
+ device_get_nameunit(bus->bdev));
+
/*
* Destroy UGEN symlink, if any
*/
@@ -1937,6 +1969,19 @@ usb2_get_speed(struct usb2_device *udev)
return (udev->speed);
}
+uint32_t
+usb2_get_isoc_fps(struct usb2_device *udev)
+{
+ ; /* indent fix */
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ return (1000);
+ default:
+ return (8000);
+ }
+}
+
struct usb2_device_descriptor *
usb2_get_device_descriptor(struct usb2_device *udev)
{
@@ -2123,3 +2168,22 @@ usb2_fifo_free_wrap(struct usb2_device *udev,
usb2_fifo_free(f);
}
}
+
+/*------------------------------------------------------------------------*
+ * usb2_peer_can_wakeup
+ *
+ * Return values:
+ * 0: Peer cannot do resume signalling.
+ * Else: Peer can do resume signalling.
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_peer_can_wakeup(struct usb2_device *udev)
+{
+ const struct usb2_config_descriptor *cdp;
+
+ cdp = udev->cdesc;
+ if ((cdp != NULL) && (udev->flags.usb2_mode == USB_MODE_HOST)) {
+ return (cdp->bmAttributes & UC_REMOTE_WAKEUP);
+ }
+ return (0); /* not supported */
+}
diff --git a/sys/dev/usb2/core/usb2_device.h b/sys/dev/usb2/core/usb2_device.h
index 96dcb50d2bc5..11686e2a1570 100644
--- a/sys/dev/usb2/core/usb2_device.h
+++ b/sys/dev/usb2/core/usb2_device.h
@@ -83,6 +83,18 @@ struct usb2_device_flags {
};
/*
+ * The following structure is used for power-save purposes. The data
+ * in this structure is protected by the USB BUS lock.
+ */
+struct usb2_power_save {
+ int last_xfer_time; /* copy of "ticks" */
+ uint32_t type_refs[4]; /* transfer reference count */
+ uint32_t read_refs; /* data read references */
+ uint32_t write_refs; /* data write references */
+ uint8_t suspended; /* set if USB device is suspended */
+};
+
+/*
* The following structure defines an USB device. There exists one of
* these structures for every USB device.
*/
@@ -96,6 +108,7 @@ struct usb2_device {
struct usb2_interface ifaces[USB_IFACE_MAX];
struct usb2_pipe default_pipe; /* Control Endpoint 0 */
struct usb2_pipe pipes[USB_EP_MAX];
+ struct usb2_power_save pwr_save;/* power save data */
struct usb2_bus *bus; /* our USB BUS */
device_t parent_dev; /* parent device */
@@ -169,5 +182,6 @@ void *usb2_find_descriptor(struct usb2_device *udev, void *id,
uint8_t iface_index, uint8_t type, uint8_t type_mask,
uint8_t subtype, uint8_t subtype_mask);
void usb_linux_free_device(struct usb_device *dev);
+uint8_t usb2_peer_can_wakeup(struct usb2_device *udev);
#endif /* _USB2_DEVICE_H_ */
diff --git a/sys/dev/usb2/core/usb2_dynamic.c b/sys/dev/usb2/core/usb2_dynamic.c
index bf7ce591937d..3e0bdf12b443 100644
--- a/sys/dev/usb2/core/usb2_dynamic.c
+++ b/sys/dev/usb2/core/usb2_dynamic.c
@@ -39,6 +39,7 @@ static usb2_temp_get_desc_t usb2_temp_get_desc_w;
static usb2_temp_setup_by_index_t usb2_temp_setup_by_index_w;
static usb2_temp_unsetup_t usb2_temp_unsetup_w;
static usb2_test_quirk_t usb2_test_quirk_w;
+static usb2_test_huawei_autoinst_t usb2_test_huawei_autoinst_w;
static usb2_quirk_ioctl_t usb2_quirk_ioctl_w;
/* global variables */
@@ -46,6 +47,7 @@ usb2_temp_get_desc_t *usb2_temp_get_desc_p = &usb2_temp_get_desc_w;
usb2_temp_setup_by_index_t *usb2_temp_setup_by_index_p = &usb2_temp_setup_by_index_w;
usb2_temp_unsetup_t *usb2_temp_unsetup_p = &usb2_temp_unsetup_w;
usb2_test_quirk_t *usb2_test_quirk_p = &usb2_test_quirk_w;
+usb2_test_huawei_autoinst_t *usb2_test_huawei_autoinst_p = &usb2_test_huawei_autoinst_w;
usb2_quirk_ioctl_t *usb2_quirk_ioctl_p = &usb2_quirk_ioctl_w;
devclass_t usb2_devclass_ptr = NULL;
@@ -86,6 +88,13 @@ usb2_temp_unsetup_w(struct usb2_device *udev)
}
}
+static uint8_t
+usb2_test_huawei_autoinst_w(struct usb2_device *udev,
+ struct usb2_attach_arg *uaa)
+{
+ return (USB_ERR_INVAL);
+}
+
void
usb2_quirk_unload(void *arg)
{
@@ -130,3 +139,17 @@ usb2_bus_unload(void *arg)
pause("WAIT", hz);
}
+
+void
+usb2_test_huawei_unload(void *arg)
+{
+ /* reset function pointers */
+
+ usb2_test_huawei_autoinst_p = &usb2_test_huawei_autoinst_w;
+
+ /* wait for CPU to exit the loaded functions, if any */
+
+ /* XXX this is a tradeoff */
+
+ pause("WAIT", 16*hz);
+}
diff --git a/sys/dev/usb2/core/usb2_dynamic.h b/sys/dev/usb2/core/usb2_dynamic.h
index 74969297ece0..2c45d093b8af 100644
--- a/sys/dev/usb2/core/usb2_dynamic.h
+++ b/sys/dev/usb2/core/usb2_dynamic.h
@@ -37,6 +37,8 @@ struct usb2_device_request;
typedef usb2_error_t (usb2_temp_setup_by_index_t)(struct usb2_device *udev,
uint16_t index);
+typedef usb2_error_t (usb2_test_huawei_autoinst_t)(struct usb2_device *udev,
+ struct usb2_attach_arg *uaa);
typedef uint8_t (usb2_test_quirk_t)(const struct usb2_lookup_info *info,
uint16_t quirk);
typedef int (usb2_quirk_ioctl_t)(unsigned long cmd, caddr_t data,
@@ -52,11 +54,13 @@ extern usb2_temp_get_desc_t *usb2_temp_get_desc_p;
extern usb2_temp_setup_by_index_t *usb2_temp_setup_by_index_p;
extern usb2_temp_unsetup_t *usb2_temp_unsetup_p;
extern usb2_test_quirk_t *usb2_test_quirk_p;
+extern usb2_test_huawei_autoinst_t *usb2_test_huawei_autoinst_p;
extern usb2_quirk_ioctl_t *usb2_quirk_ioctl_p;
extern devclass_t usb2_devclass_ptr;
/* function prototypes */
+void usb2_test_huawei_unload(void *);
void usb2_temp_unload(void *);
void usb2_quirk_unload(void *);
void usb2_bus_unload(void *);
diff --git a/sys/dev/usb2/core/usb2_generic.c b/sys/dev/usb2/core/usb2_generic.c
index fa09eef675b2..9e5b34b8cea4 100644
--- a/sys/dev/usb2/core/usb2_generic.c
+++ b/sys/dev/usb2/core/usb2_generic.c
@@ -1711,24 +1711,21 @@ ugen_set_power_mode(struct usb2_fifo *f, int mode)
break;
case USB_POWER_MODE_ON:
- /* enable port */
- err = usb2_req_set_port_feature(udev->parent_hub,
- NULL, udev->port_no, UHF_PORT_ENABLE);
-
- /* FALLTHROUGH */
-
case USB_POWER_MODE_SAVE:
+ break;
+
case USB_POWER_MODE_RESUME:
- /* TODO: implement USB power save */
err = usb2_req_clear_port_feature(udev->parent_hub,
NULL, udev->port_no, UHF_PORT_SUSPEND);
+ mode = USB_POWER_MODE_SAVE;
break;
case USB_POWER_MODE_SUSPEND:
- /* TODO: implement USB power save */
err = usb2_req_set_port_feature(udev->parent_hub,
NULL, udev->port_no, UHF_PORT_SUSPEND);
+ mode = USB_POWER_MODE_SAVE;
break;
+
default:
return (EINVAL);
}
@@ -1736,7 +1733,8 @@ ugen_set_power_mode(struct usb2_fifo *f, int mode)
if (err)
return (ENXIO); /* I/O failure */
- udev->power_mode = mode; /* update copy of power mode */
+ /* set new power mode */
+ usb2_set_power_mode(udev, mode);
return (0); /* success */
}
diff --git a/sys/dev/usb2/core/usb2_handle_request.c b/sys/dev/usb2/core/usb2_handle_request.c
index d5714b45fabf..c7659978b53a 100644
--- a/sys/dev/usb2/core/usb2_handle_request.c
+++ b/sys/dev/usb2/core/usb2_handle_request.c
@@ -390,10 +390,12 @@ usb2_handle_remote_wakeup(struct usb2_xfer *xfer, uint8_t is_on)
udev->flags.remote_wakeup = 0;
}
- (bus->methods->rem_wakeup_set) (xfer->udev, is_on);
-
USB_BUS_UNLOCK(bus);
+ /* In case we are out of sync, update the power state. */
+
+ usb2_bus_power_update(udev->bus);
+
return (0); /* success */
}
diff --git a/sys/dev/usb2/core/usb2_hub.c b/sys/dev/usb2/core/usb2_hub.c
index 34b275a40aa3..0d08b9484f41 100644
--- a/sys/dev/usb2/core/usb2_hub.c
+++ b/sys/dev/usb2/core/usb2_hub.c
@@ -34,6 +34,7 @@
#include <dev/usb2/include/usb2_mfunc.h>
#include <dev/usb2/include/usb2_error.h>
#include <dev/usb2/include/usb2_standard.h>
+#include <dev/usb2/include/usb2_ioctl.h>
#define USB_DEBUG_VAR uhub_debug
@@ -61,6 +62,11 @@ SYSCTL_INT(_hw_usb2_uhub, OID_AUTO, debug, CTLFLAG_RW, &uhub_debug, 0,
"Debug level");
#endif
+static int usb2_power_timeout = 30; /* seconds */
+
+SYSCTL_INT(_hw_usb2, OID_AUTO, power_timeout, CTLFLAG_RW,
+ &usb2_power_timeout, 0, "USB power timeout");
+
struct uhub_current_state {
uint16_t port_change;
uint16_t port_status;
@@ -86,6 +92,8 @@ struct uhub_softc {
static device_probe_t uhub_probe;
static device_attach_t uhub_attach;
static device_detach_t uhub_detach;
+static device_suspend_t uhub_suspend;
+static device_resume_t uhub_resume;
static bus_driver_added_t uhub_driver_added;
static bus_child_location_str_t uhub_child_location_string;
@@ -94,6 +102,9 @@ static bus_child_pnpinfo_str_t uhub_child_pnpinfo_string;
static usb2_callback_t uhub_intr_callback;
static usb2_callback_t uhub_intr_clear_stall_callback;
+static void usb2_dev_resume_peer(struct usb2_device *udev);
+static void usb2_dev_suspend_peer(struct usb2_device *udev);
+
static const struct usb2_config uhub_config[2] = {
[0] = {
@@ -133,8 +144,8 @@ static driver_t uhub_driver =
DEVMETHOD(device_attach, uhub_attach),
DEVMETHOD(device_detach, uhub_detach),
- DEVMETHOD(device_suspend, bus_generic_suspend),
- DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_suspend, uhub_suspend),
+ DEVMETHOD(device_resume, uhub_resume),
DEVMETHOD(device_shutdown, bus_generic_shutdown),
DEVMETHOD(bus_child_location_str, uhub_child_location_string),
@@ -302,8 +313,8 @@ repeat:
/* first clear the port connection change bit */
- err = usb2_req_clear_port_feature
- (udev, &Giant, portno, UHF_C_PORT_CONNECTION);
+ err = usb2_req_clear_port_feature(udev, &Giant,
+ portno, UHF_C_PORT_CONNECTION);
if (err) {
goto error;
@@ -338,6 +349,12 @@ repeat:
DPRINTF("Port %d is in Host Mode\n", portno);
+ if (sc->sc_st.port_status & UPS_SUSPEND) {
+ DPRINTF("Port %d was still "
+ "suspended, clearing.\n", portno);
+ err = usb2_req_clear_port_feature(sc->sc_udev,
+ &Giant, portno, UHF_PORT_SUSPEND);
+ }
/* USB Host Mode */
/* wait for maximum device power up time */
@@ -346,8 +363,7 @@ repeat:
/* reset port, which implies enabling it */
- err = usb2_req_reset_port
- (udev, &Giant, portno);
+ err = usb2_req_reset_port(udev, &Giant, portno);
if (err) {
DPRINTFN(0, "port %d reset "
@@ -411,8 +427,8 @@ error:
}
if (err == 0) {
if (sc->sc_st.port_status & UPS_PORT_ENABLED) {
- err = usb2_req_clear_port_feature
- (sc->sc_udev, &Giant,
+ err = usb2_req_clear_port_feature(
+ sc->sc_udev, &Giant,
portno, UHF_PORT_ENABLE);
}
}
@@ -446,16 +462,17 @@ uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno)
/* first clear the port suspend change bit */
- err = usb2_req_clear_port_feature
- (udev, &Giant, portno, UHF_C_PORT_SUSPEND);
-
+ err = usb2_req_clear_port_feature(udev, &Giant,
+ portno, UHF_C_PORT_SUSPEND);
if (err) {
+ DPRINTF("clearing suspend failed.\n");
goto done;
}
/* get fresh status */
err = uhub_read_port_status(sc, portno);
if (err) {
+ DPRINTF("reading port status failed.\n");
goto done;
}
/* get current state */
@@ -465,12 +482,21 @@ uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno)
} else {
is_suspend = 0;
}
+
+ DPRINTF("suspended=%u\n", is_suspend);
+
/* do the suspend or resume */
if (child) {
- sx_xlock(child->default_sx + 1);
- err = usb2_suspend_resume(child, is_suspend);
- sx_unlock(child->default_sx + 1);
+ /*
+ * This code handle two cases: 1) Host Mode - we can only
+ * receive resume here 2) Device Mode - we can receive
+ * suspend and resume here
+ */
+ if (is_suspend == 0)
+ usb2_dev_resume_peer(child);
+ else if (child->flags.usb2_mode == USB_MODE_DEVICE)
+ usb2_dev_suspend_peer(child);
}
done:
return (err);
@@ -502,6 +528,11 @@ uhub_explore(struct usb2_device *udev)
if (udev->depth > USB_HUB_MAX_DEPTH) {
return (USB_ERR_TOO_DEEP);
}
+ if (udev->pwr_save.suspended) {
+ /* need to wait until the child signals resume */
+ DPRINTF("Device is suspended!\n");
+ return (0);
+ }
for (x = 0; x != hub->nports; x++) {
up = hub->ports + x;
portno = x + 1;
@@ -511,6 +542,15 @@ uhub_explore(struct usb2_device *udev)
/* most likely the HUB is gone */
break;
}
+ if (sc->sc_st.port_change & UPS_C_OVERCURRENT_INDICATOR) {
+ DPRINTF("Overcurrent on port %u.\n", portno);
+ err = usb2_req_clear_port_feature(
+ udev, &Giant, portno, UHF_C_PORT_OVER_CURRENT);
+ if (err) {
+ /* most likely the HUB is gone */
+ break;
+ }
+ }
if (!(sc->sc_flags & UHUB_FLAG_DID_EXPLORE)) {
/*
* Fake a connect status change so that the
@@ -750,8 +790,8 @@ uhub_attach(device_t dev)
}
if (!err) {
/* turn the power on */
- err = usb2_req_set_port_feature
- (udev, &Giant, portno, UHF_PORT_POWER);
+ err = usb2_req_set_port_feature(udev, &Giant,
+ portno, UHF_PORT_POWER);
}
if (err) {
DPRINTFN(0, "port %d power on failed, %s\n",
@@ -774,6 +814,10 @@ uhub_attach(device_t dev)
usb2_transfer_start(sc->sc_xfer[0]);
USB_XFER_UNLOCK(sc->sc_xfer[0]);
+ /* Enable automatic power save on all USB HUBs */
+
+ usb2_set_power_mode(udev, USB_POWER_MODE_SAVE);
+
return (0);
error:
@@ -827,6 +871,22 @@ uhub_detach(device_t dev)
return (0);
}
+static int
+uhub_suspend(device_t dev)
+{
+ DPRINTF("\n");
+ /* Sub-devices are not suspended here! */
+ return (0);
+}
+
+static int
+uhub_resume(device_t dev)
+{
+ DPRINTF("\n");
+ /* Sub-devices are not resumed here! */
+ return (0);
+}
+
static void
uhub_driver_added(device_t dev, driver_t *driver)
{
@@ -1334,3 +1394,441 @@ usb2_needs_explore_all(void)
max--;
}
}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_power_update
+ *
+ * This function will ensure that all USB devices on the given bus are
+ * properly suspended or resumed according to the device transfer
+ * state.
+ *------------------------------------------------------------------------*/
+void
+usb2_bus_power_update(struct usb2_bus *bus)
+{
+ usb2_needs_explore(bus, 0 /* no probe */ );
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_power_ref
+ *
+ * This function will modify the power save reference counts and
+ * wakeup the USB device associated with the given USB transfer, if
+ * needed.
+ *------------------------------------------------------------------------*/
+void
+usb2_transfer_power_ref(struct usb2_xfer *xfer, int val)
+{
+ static const uint32_t power_mask[4] = {
+ [UE_CONTROL] = USB_HW_POWER_CONTROL,
+ [UE_BULK] = USB_HW_POWER_BULK,
+ [UE_INTERRUPT] = USB_HW_POWER_INTERRUPT,
+ [UE_ISOCHRONOUS] = USB_HW_POWER_ISOC,
+ };
+ struct usb2_device *udev;
+ uint8_t needs_explore;
+ uint8_t needs_hw_power;
+ uint8_t xfer_type;
+
+ udev = xfer->udev;
+
+ if (udev->device_index == USB_ROOT_HUB_ADDR) {
+ /* no power save for root HUB */
+ return;
+ }
+ USB_BUS_LOCK(udev->bus);
+
+ xfer_type = xfer->pipe->edesc->bmAttributes & UE_XFERTYPE;
+
+ udev->pwr_save.last_xfer_time = ticks;
+ udev->pwr_save.type_refs[xfer_type] += val;
+
+ if (xfer->flags_int.control_xfr) {
+ udev->pwr_save.read_refs += val;
+ if (xfer->flags_int.usb2_mode == USB_MODE_HOST) {
+ /*
+ * it is not allowed to suspend during a control
+ * transfer
+ */
+ udev->pwr_save.write_refs += val;
+ }
+ } else if (USB_GET_DATA_ISREAD(xfer)) {
+ udev->pwr_save.read_refs += val;
+ } else {
+ udev->pwr_save.write_refs += val;
+ }
+
+ if (udev->pwr_save.suspended)
+ needs_explore =
+ (udev->pwr_save.write_refs != 0) ||
+ ((udev->pwr_save.read_refs != 0) &&
+ (usb2_peer_can_wakeup(udev) == 0));
+ else
+ needs_explore = 0;
+
+ if (!(udev->bus->hw_power_state & power_mask[xfer_type])) {
+ DPRINTF("Adding type %u to power state\n", xfer_type);
+ udev->bus->hw_power_state |= power_mask[xfer_type];
+ needs_hw_power = 1;
+ } else {
+ needs_hw_power = 0;
+ }
+
+ USB_BUS_UNLOCK(udev->bus);
+
+ if (needs_explore) {
+ DPRINTF("update\n");
+ usb2_bus_power_update(udev->bus);
+ } else if (needs_hw_power) {
+ DPRINTF("needs power\n");
+ if (udev->bus->methods->set_hw_power != NULL) {
+ (udev->bus->methods->set_hw_power) (udev->bus);
+ }
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_powerd
+ *
+ * This function implements the USB power daemon and is called
+ * regularly from the USB explore thread.
+ *------------------------------------------------------------------------*/
+void
+usb2_bus_powerd(struct usb2_bus *bus)
+{
+ struct usb2_device *udev;
+ unsigned int temp;
+ unsigned int limit;
+ unsigned int mintime;
+ uint32_t type_refs[4];
+ uint8_t x;
+ uint8_t rem_wakeup;
+
+ limit = usb2_power_timeout;
+ if (limit == 0)
+ limit = hz;
+ else if (limit > 255)
+ limit = 255 * hz;
+ else
+ limit = limit * hz;
+
+ DPRINTF("bus=%p\n", bus);
+
+ USB_BUS_LOCK(bus);
+
+ /*
+ * The root HUB device is never suspended
+ * and we simply skip it.
+ */
+ for (x = USB_ROOT_HUB_ADDR + 1;
+ x != USB_MAX_DEVICES; x++) {
+
+ udev = bus->devices[x];
+ if (udev == NULL)
+ continue;
+
+ rem_wakeup = usb2_peer_can_wakeup(udev);
+
+ temp = ticks - udev->pwr_save.last_xfer_time;
+
+ if ((udev->power_mode == USB_POWER_MODE_ON) ||
+ (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) ||
+ (udev->pwr_save.write_refs != 0) ||
+ ((udev->pwr_save.read_refs != 0) &&
+ (rem_wakeup == 0))) {
+
+ /* check if we are suspended */
+ if (udev->pwr_save.suspended != 0) {
+ USB_BUS_UNLOCK(bus);
+ usb2_dev_resume_peer(udev);
+ USB_BUS_LOCK(bus);
+ }
+ } else if (temp >= limit) {
+
+ /* check if we are not suspended */
+ if (udev->pwr_save.suspended == 0) {
+ USB_BUS_UNLOCK(bus);
+ usb2_dev_suspend_peer(udev);
+ USB_BUS_LOCK(bus);
+ }
+ }
+ }
+
+ /* reset counters */
+
+ mintime = 0 - 1;
+ type_refs[0] = 0;
+ type_refs[1] = 0;
+ type_refs[2] = 0;
+ type_refs[3] = 0;
+
+ /* Re-loop all the devices to get the actual state */
+
+ for (x = USB_ROOT_HUB_ADDR + 1;
+ x != USB_MAX_DEVICES; x++) {
+
+ udev = bus->devices[x];
+ if (udev == NULL)
+ continue;
+
+ /* "last_xfer_time" can be updated by a resume */
+ temp = ticks - udev->pwr_save.last_xfer_time;
+
+ /*
+ * Compute minimum time since last transfer for the complete
+ * bus:
+ */
+ if (temp < mintime)
+ mintime = temp;
+
+ if (udev->pwr_save.suspended == 0) {
+ type_refs[0] += udev->pwr_save.type_refs[0];
+ type_refs[1] += udev->pwr_save.type_refs[1];
+ type_refs[2] += udev->pwr_save.type_refs[2];
+ type_refs[3] += udev->pwr_save.type_refs[3];
+ }
+ }
+
+ if (mintime >= (1 * hz)) {
+ /* recompute power masks */
+ DPRINTF("Recomputing power masks\n");
+ bus->hw_power_state = 0;
+ if (type_refs[UE_CONTROL] != 0)
+ bus->hw_power_state |= USB_HW_POWER_CONTROL;
+ if (type_refs[UE_BULK] != 0)
+ bus->hw_power_state |= USB_HW_POWER_BULK;
+ if (type_refs[UE_INTERRUPT] != 0)
+ bus->hw_power_state |= USB_HW_POWER_INTERRUPT;
+ if (type_refs[UE_ISOCHRONOUS] != 0)
+ bus->hw_power_state |= USB_HW_POWER_ISOC;
+ }
+ USB_BUS_UNLOCK(bus);
+
+ if (bus->methods->set_hw_power != NULL) {
+ /* always update hardware power! */
+ (bus->methods->set_hw_power) (bus);
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_dev_resume_peer
+ *
+ * This function will resume an USB peer and do the required USB
+ * signalling to get an USB device out of the suspended state.
+ *------------------------------------------------------------------------*/
+static void
+usb2_dev_resume_peer(struct usb2_device *udev)
+{
+ struct usb2_bus *bus;
+ int err;
+
+ /* be NULL safe */
+ if (udev == NULL)
+ return;
+
+ /* check if already resumed */
+ if (udev->pwr_save.suspended == 0)
+ return;
+
+ /* we need a parent HUB to do resume */
+ if (udev->parent_hub == NULL)
+ return;
+
+ DPRINTF("udev=%p\n", udev);
+
+ if ((udev->flags.usb2_mode == USB_MODE_DEVICE) &&
+ (udev->flags.remote_wakeup == 0)) {
+ /*
+ * If the host did not set the remote wakeup feature, we can
+ * not wake it up either!
+ */
+ DPRINTF("remote wakeup is not set!\n");
+ return;
+ }
+ /* get bus pointer */
+ bus = udev->bus;
+
+ /* resume parent hub first */
+ usb2_dev_resume_peer(udev->parent_hub);
+
+ /* resume current port (Valid in Host and Device Mode) */
+ err = usb2_req_clear_port_feature(udev->parent_hub,
+ &Giant, udev->port_no, UHF_PORT_SUSPEND);
+ if (err) {
+ DPRINTFN(0, "Resuming port failed!\n");
+ return;
+ }
+ /* resume settle time */
+ usb2_pause_mtx(&Giant, USB_PORT_RESUME_DELAY);
+
+ if (bus->methods->device_resume != NULL) {
+ /* resume USB device on the USB controller */
+ (bus->methods->device_resume) (udev);
+ }
+ USB_BUS_LOCK(bus);
+ /* set that this device is now resumed */
+ udev->pwr_save.suspended = 0;
+ /* make sure that we don't go into suspend right away */
+ udev->pwr_save.last_xfer_time = ticks;
+
+ /* make sure the needed power masks are on */
+ if (udev->pwr_save.type_refs[UE_CONTROL] != 0)
+ bus->hw_power_state |= USB_HW_POWER_CONTROL;
+ if (udev->pwr_save.type_refs[UE_BULK] != 0)
+ bus->hw_power_state |= USB_HW_POWER_BULK;
+ if (udev->pwr_save.type_refs[UE_INTERRUPT] != 0)
+ bus->hw_power_state |= USB_HW_POWER_INTERRUPT;
+ if (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0)
+ bus->hw_power_state |= USB_HW_POWER_ISOC;
+ USB_BUS_UNLOCK(bus);
+
+ if (bus->methods->set_hw_power != NULL) {
+ /* always update hardware power! */
+ (bus->methods->set_hw_power) (bus);
+ }
+ sx_xlock(udev->default_sx + 1);
+ /* notify all sub-devices about resume */
+ err = usb2_suspend_resume(udev, 0);
+ sx_unlock(udev->default_sx + 1);
+
+ /* check if peer has wakeup capability */
+ if (usb2_peer_can_wakeup(udev)) {
+ /* clear remote wakeup */
+ err = usb2_req_clear_device_feature(udev,
+ &Giant, UF_DEVICE_REMOTE_WAKEUP);
+ if (err) {
+ DPRINTFN(0, "Clearing device "
+ "remote wakeup failed: %s!\n",
+ usb2_errstr(err));
+ }
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_dev_suspend_peer
+ *
+ * This function will suspend an USB peer and do the required USB
+ * signalling to get an USB device into the suspended state.
+ *------------------------------------------------------------------------*/
+static void
+usb2_dev_suspend_peer(struct usb2_device *udev)
+{
+ struct usb2_device *hub;
+ struct usb2_device *child;
+ uint32_t temp;
+ int err;
+ uint8_t x;
+ uint8_t nports;
+ uint8_t suspend_parent;
+
+repeat:
+ /* be NULL safe */
+ if (udev == NULL)
+ return;
+
+ /* check if already suspended */
+ if (udev->pwr_save.suspended)
+ return;
+
+ /* we need a parent HUB to do suspend */
+ if (udev->parent_hub == NULL)
+ return;
+
+ DPRINTF("udev=%p\n", udev);
+
+ /* check if all devices on the parent hub are suspended */
+ hub = udev->parent_hub;
+ if (hub != NULL) {
+ nports = hub->hub->nports;
+ suspend_parent = 1;
+
+ for (x = 0; x != nports; x++) {
+
+ child = usb2_bus_port_get_device(hub->bus,
+ hub->hub->ports + x);
+
+ if (child == NULL)
+ continue;
+
+ if (child->pwr_save.suspended)
+ continue;
+
+ if (child == udev)
+ continue;
+
+ /* another device on the HUB is not suspended */
+ suspend_parent = 0;
+
+ break;
+ }
+ } else {
+ suspend_parent = 0;
+ }
+
+ sx_xlock(udev->default_sx + 1);
+ /* notify all sub-devices about suspend */
+ err = usb2_suspend_resume(udev, 1);
+ sx_unlock(udev->default_sx + 1);
+
+ if (usb2_peer_can_wakeup(udev)) {
+ /* allow device to do remote wakeup */
+ err = usb2_req_set_device_feature(udev,
+ &Giant, UF_DEVICE_REMOTE_WAKEUP);
+ if (err) {
+ DPRINTFN(0, "Setting device "
+ "remote wakeup failed!\n");
+ }
+ }
+ USB_BUS_LOCK(udev->bus);
+ /*
+ * Set that this device is suspended. This variable must be set
+ * before calling USB controller suspend callbacks.
+ */
+ udev->pwr_save.suspended = 1;
+ USB_BUS_UNLOCK(udev->bus);
+
+ if (udev->bus->methods->device_suspend != NULL) {
+
+ /* suspend device on the USB controller */
+ (udev->bus->methods->device_suspend) (udev);
+
+ /* do DMA delay */
+ temp = usb2_get_dma_delay(udev->bus);
+ usb2_pause_mtx(&Giant, temp);
+
+ }
+ /* suspend current port */
+ err = usb2_req_set_port_feature(udev->parent_hub,
+ &Giant, udev->port_no, UHF_PORT_SUSPEND);
+ if (err) {
+ DPRINTFN(0, "Suspending port failed\n");
+ return;
+ }
+ if (suspend_parent) {
+ udev = udev->parent_hub;
+ goto repeat;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_set_power_mode
+ *
+ * This function will set the power mode, see USB_POWER_MODE_XXX for a
+ * USB device.
+ *------------------------------------------------------------------------*/
+void
+usb2_set_power_mode(struct usb2_device *udev, uint8_t power_mode)
+{
+ /* filter input argument */
+ if (power_mode != USB_POWER_MODE_ON) {
+ power_mode = USB_POWER_MODE_SAVE;
+ }
+ udev->power_mode = power_mode; /* update copy of power mode */
+
+ usb2_bus_power_update(udev->bus);
+
+ return;
+}
diff --git a/sys/dev/usb2/core/usb2_hub.h b/sys/dev/usb2/core/usb2_hub.h
index 8ef618e203c9..87d85b357722 100644
--- a/sys/dev/usb2/core/usb2_hub.h
+++ b/sys/dev/usb2/core/usb2_hub.h
@@ -74,5 +74,7 @@ struct usb2_device *usb2_bus_port_get_device(struct usb2_bus *bus,
struct usb2_port *up);
void usb2_needs_explore(struct usb2_bus *bus, uint8_t do_probe);
void usb2_needs_explore_all(void);
+void usb2_bus_power_update(struct usb2_bus *bus);
+void usb2_bus_powerd(struct usb2_bus *bus);
#endif /* _USB2_HUB_H_ */
diff --git a/sys/dev/usb2/core/usb2_msctest.c b/sys/dev/usb2/core/usb2_msctest.c
index ef616ce4f1b1..452f38951df3 100644
--- a/sys/dev/usb2/core/usb2_msctest.c
+++ b/sys/dev/usb2/core/usb2_msctest.c
@@ -36,7 +36,6 @@
#include <dev/usb2/include/usb2_mfunc.h>
#include <dev/usb2/include/usb2_error.h>
#include <dev/usb2/include/usb2_standard.h>
-#include <dev/usb2/include/usb2_devid.h>
#define USB_DEBUG_VAR usb2_debug
@@ -577,163 +576,3 @@ done:
free(sc, M_USB);
return (err);
}
-
-/*
- * NOTE: The entries marked with XXX should be checked for the correct
- * speed indication to set the buffer sizes.
- */
-static const struct usb2_device_id u3g_devs[] = {
- /* OEM: Option */
- {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},
- {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},
- {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},
- {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))},
- {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAXHSUPA, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))},
- {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},
- /* OEM: Qualcomm, Inc. */
- {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_ZTE_STOR, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))},
- {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))},
- /* OEM: Huawei */
- {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE, U3GINFO(U3GSP_HSDPA, U3GFL_HUAWEI_INIT))},
- {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220, U3GINFO(U3GSP_HSPA, U3GFL_HUAWEI_INIT))},
- /* OEM: Novatel */
- {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_CDMA_MODEM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))},
- {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
- {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MC950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))},
- {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
- {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U727, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
- {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
- {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740_2, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
- {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U870, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
- {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
- {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V640, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
- {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
- {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
- {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_X950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))},
- {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_XU870, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
- {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ZEROCD, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))},
- {USB_VPI(USB_VENDOR_DELL, USB_PRODUCT_DELL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
- /* OEM: Merlin */
- {USB_VPI(USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- /* OEM: Sierra Wireless: */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
- /* Sierra TruInstaller device ID */
- {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_TRUINSTALL, U3GINFO(U3GSP_UMTS, U3GFL_SIERRA_INIT))},
-};
-
-static void
-u3g_sierra_init(struct usb2_device *udev)
-{
- struct usb2_device_request req;
-
- DPRINTFN(0, "\n");
-
- req.bmRequestType = UT_VENDOR;
- req.bRequest = UR_SET_INTERFACE;
- USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
- USETW(req.wIndex, UHF_PORT_CONNECTION);
- USETW(req.wLength, 0);
-
- if (usb2_do_request_flags(udev, NULL, &req,
- NULL, 0, NULL, USB_MS_HZ)) {
- /* ignore any errors */
- }
- return;
-}
-
-static void
-u3g_huawei_init(struct usb2_device *udev)
-{
- struct usb2_device_request req;
-
- DPRINTFN(0, "\n");
-
- req.bmRequestType = UT_WRITE_DEVICE;
- req.bRequest = UR_SET_FEATURE;
- USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
- USETW(req.wIndex, UHF_PORT_SUSPEND);
- USETW(req.wLength, 0);
-
- if (usb2_do_request_flags(udev, NULL, &req,
- NULL, 0, NULL, USB_MS_HZ)) {
- /* ignore any errors */
- }
- return;
-}
-
-int
-usb2_lookup_huawei(struct usb2_attach_arg *uaa)
-{
- /* Calling the lookup function will also set the driver info! */
- return (usb2_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa));
-}
-
-/*
- * The following function handles 3G modem devices (E220, Mobile,
- * etc.) with auto-install flash disks for Windows/MacOSX on the first
- * interface. After some command or some delay they change appearance
- * to a modem.
- */
-usb2_error_t
-usb2_test_huawei(struct usb2_device *udev, struct usb2_attach_arg *uaa)
-{
- struct usb2_interface *iface;
- struct usb2_interface_descriptor *id;
- uint32_t flags;
-
- if (udev == NULL) {
- return (USB_ERR_INVAL);
- }
- iface = usb2_get_iface(udev, 0);
- if (iface == NULL) {
- return (USB_ERR_INVAL);
- }
- id = iface->idesc;
- if (id == NULL) {
- return (USB_ERR_INVAL);
- }
- if (id->bInterfaceClass != UICLASS_MASS) {
- return (USB_ERR_INVAL);
- }
- if (usb2_lookup_huawei(uaa)) {
- /* no device match */
- return (USB_ERR_INVAL);
- }
- flags = USB_GET_DRIVER_INFO(uaa);
-
- if (flags & U3GFL_HUAWEI_INIT) {
- u3g_huawei_init(udev);
- } else if (flags & U3GFL_SCSI_EJECT) {
- return (usb2_test_autoinstall(udev, 0, 1));
- } else if (flags & U3GFL_SIERRA_INIT) {
- u3g_sierra_init(udev);
- } else {
- /* no quirks */
- return (USB_ERR_INVAL);
- }
- return (0); /* success */
-}
diff --git a/sys/dev/usb2/core/usb2_msctest.h b/sys/dev/usb2/core/usb2_msctest.h
index cdd335f92fbb..5bf64d0bb277 100644
--- a/sys/dev/usb2/core/usb2_msctest.h
+++ b/sys/dev/usb2/core/usb2_msctest.h
@@ -29,30 +29,5 @@
usb2_error_t usb2_test_autoinstall(struct usb2_device *udev,
uint8_t iface_index, uint8_t do_eject);
-usb2_error_t usb2_test_huawei(struct usb2_device *udev,
- struct usb2_attach_arg *uaa);
-int usb2_lookup_huawei(struct usb2_attach_arg *uaa);
-
-/* Huawei specific defines */
-
-#define U3GINFO(flag,speed) ((flag)|((speed) * 256))
-#define U3G_GET_SPEED(uaa) (USB_GET_DRIVER_INFO(uaa) / 256)
-
-#define U3GFL_NONE 0x00
-#define U3GFL_HUAWEI_INIT 0x01 /* Requires init command (Huawei
- * cards) */
-#define U3GFL_SCSI_EJECT 0x02 /* Requires SCSI eject command
- * (Novatel) */
-#define U3GFL_SIERRA_INIT 0x04 /* Requires init command (Sierra
- * cards) */
-
-#define U3GSP_GPRS 0
-#define U3GSP_EDGE 1
-#define U3GSP_CDMA 2
-#define U3GSP_UMTS 3
-#define U3GSP_HSDPA 4
-#define U3GSP_HSUPA 5
-#define U3GSP_HSPA 6
-#define U3GSP_MAX 7
#endif /* _USB2_MSCTEST_H_ */
diff --git a/sys/dev/usb2/core/usb2_request.c b/sys/dev/usb2/core/usb2_request.c
index 030a022c615a..b1eb81fa9b10 100644
--- a/sys/dev/usb2/core/usb2_request.c
+++ b/sys/dev/usb2/core/usb2_request.c
@@ -611,7 +611,8 @@ usb2_req_get_desc(struct usb2_device *udev, struct mtx *mtx, void *desc,
}
USETW(req.wLength, min_len);
- err = usb2_do_request(udev, mtx, &req, desc);
+ err = usb2_do_request_flags(udev, mtx, &req,
+ desc, 0, NULL, 1000);
if (err) {
if (!retries) {
@@ -1326,6 +1327,7 @@ usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx)
struct usb2_device *parent_hub;
usb2_error_t err;
uint8_t old_addr;
+ uint8_t do_retry = 1;
if (udev->flags.usb2_mode != USB_MODE_HOST) {
return (USB_ERR_INVAL);
@@ -1335,6 +1337,7 @@ usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx)
if (parent_hub == NULL) {
return (USB_ERR_INVAL);
}
+retry:
err = usb2_req_reset_port(parent_hub, mtx, udev->port_no);
if (err) {
DPRINTFN(0, "addr=%d, port reset failed\n", old_addr);
@@ -1355,9 +1358,8 @@ usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx)
err = usb2_req_set_address(udev, mtx, old_addr);
if (err) {
/* XXX ignore any errors! */
- DPRINTFN(0, "addr=%d, set address failed\n",
+ DPRINTFN(0, "addr=%d, set address failed! (ignored)\n",
old_addr);
- err = 0;
}
/* restore device address */
udev->address = old_addr;
@@ -1381,7 +1383,57 @@ usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx)
goto done;
}
done:
+ if (err && do_retry) {
+ /* give the USB firmware some time to load */
+ usb2_pause_mtx(mtx, 500);
+ /* no more retries after this retry */
+ do_retry = 0;
+ /* try again */
+ goto retry;
+ }
/* restore address */
udev->address = old_addr;
return (err);
}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_clear_device_feature
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_clear_device_feature(struct usb2_device *udev, struct mtx *mtx,
+ uint16_t sel)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_DEVICE;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, sel);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return (usb2_do_request(udev, mtx, &req, 0));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_set_device_feature
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_set_device_feature(struct usb2_device *udev, struct mtx *mtx,
+ uint16_t sel)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_DEVICE;
+ req.bRequest = UR_SET_FEATURE;
+ USETW(req.wValue, sel);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return (usb2_do_request(udev, mtx, &req, 0));
+}
diff --git a/sys/dev/usb2/core/usb2_request.h b/sys/dev/usb2/core/usb2_request.h
index 38699689f9d4..b33e0a1d6a15 100644
--- a/sys/dev/usb2/core/usb2_request.h
+++ b/sys/dev/usb2/core/usb2_request.h
@@ -89,6 +89,8 @@ usb2_error_t usb2_req_set_report(struct usb2_device *udev, struct mtx *mtx,
void *data, uint16_t len, uint8_t iface_index,
uint8_t type, uint8_t id);
usb2_error_t usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx);
+usb2_error_t usb2_req_clear_device_feature(struct usb2_device *udev, struct mtx *mtx, uint16_t sel);
+usb2_error_t usb2_req_set_device_feature(struct usb2_device *udev, struct mtx *mtx, uint16_t sel);
#define usb2_do_request(u,m,r,d) \
usb2_do_request_flags(u,m,r,d,0,NULL,USB_DEFAULT_TIMEOUT)
diff --git a/sys/dev/usb2/core/usb2_transfer.c b/sys/dev/usb2/core/usb2_transfer.c
index 1cf6956d962e..f574436e043c 100644
--- a/sys/dev/usb2/core/usb2_transfer.c
+++ b/sys/dev/usb2/core/usb2_transfer.c
@@ -120,7 +120,6 @@ static const struct usb2_config usb2_control_ep_cfg[USB_DEFAULT_XFER_MAX] = {
/* function prototypes */
static void usb2_update_max_frame_size(struct usb2_xfer *);
-static uint32_t usb2_get_dma_delay(struct usb2_bus *);
static void usb2_transfer_unsetup_sub(struct usb2_xfer_root *, uint8_t);
static void usb2_control_transfer_init(struct usb2_xfer *);
static uint8_t usb2_start_hardware_sub(struct usb2_xfer *);
@@ -161,7 +160,7 @@ usb2_update_max_frame_size(struct usb2_xfer *xfer)
* 0: no DMA delay required
* Else: milliseconds of DMA delay
*------------------------------------------------------------------------*/
-static uint32_t
+uint32_t
usb2_get_dma_delay(struct usb2_bus *bus)
{
uint32_t temp = 0;
@@ -1374,6 +1373,9 @@ usb2_start_hardware(struct usb2_xfer *xfer)
/* set "transferring" flag */
xfer->flags_int.transferring = 1;
+ /* increment power reference */
+ usb2_transfer_power_ref(xfer, 1);
+
/*
* Check if the transfer is waiting on a queue, most
* frequently the "done_q":
@@ -1886,6 +1888,9 @@ usb2_callback_wrapper(struct usb2_xfer_queue *pq)
USB_BUS_LOCK(xfer->udev->bus);
goto done;
}
+ /* decrement power reference */
+ usb2_transfer_power_ref(xfer, -1);
+
xfer->flags_int.transferring = 0;
if (xfer->error) {
diff --git a/sys/dev/usb2/core/usb2_transfer.h b/sys/dev/usb2/core/usb2_transfer.h
index 135104648ffd..698509c5e061 100644
--- a/sys/dev/usb2/core/usb2_transfer.h
+++ b/sys/dev/usb2/core/usb2_transfer.h
@@ -124,5 +124,7 @@ usb2_callback_t usb2_handle_request_callback;
usb2_callback_t usb2_do_clear_stall_callback;
void usb2_transfer_timeout_ms(struct usb2_xfer *xfer,
void (*cb) (void *arg), uint32_t ms);
+uint32_t usb2_get_dma_delay(struct usb2_bus *bus);
+void usb2_transfer_power_ref(struct usb2_xfer *xfer, int val);
#endif /* _USB2_TRANSFER_H_ */