diff options
Diffstat (limited to 'sys/dev/usb2/core')
| -rw-r--r-- | sys/dev/usb2/core/usb2_busdma.c | 25 | ||||
| -rw-r--r-- | sys/dev/usb2/core/usb2_core.h | 2 | ||||
| -rw-r--r-- | sys/dev/usb2/core/usb2_device.c | 80 | ||||
| -rw-r--r-- | sys/dev/usb2/core/usb2_device.h | 14 | ||||
| -rw-r--r-- | sys/dev/usb2/core/usb2_dynamic.c | 23 | ||||
| -rw-r--r-- | sys/dev/usb2/core/usb2_dynamic.h | 4 | ||||
| -rw-r--r-- | sys/dev/usb2/core/usb2_generic.c | 16 | ||||
| -rw-r--r-- | sys/dev/usb2/core/usb2_handle_request.c | 6 | ||||
| -rw-r--r-- | sys/dev/usb2/core/usb2_hub.c | 530 | ||||
| -rw-r--r-- | sys/dev/usb2/core/usb2_hub.h | 2 | ||||
| -rw-r--r-- | sys/dev/usb2/core/usb2_msctest.c | 161 | ||||
| -rw-r--r-- | sys/dev/usb2/core/usb2_msctest.h | 25 | ||||
| -rw-r--r-- | sys/dev/usb2/core/usb2_request.c | 58 | ||||
| -rw-r--r-- | sys/dev/usb2/core/usb2_request.h | 2 | ||||
| -rw-r--r-- | sys/dev/usb2/core/usb2_transfer.c | 9 | ||||
| -rw-r--r-- | sys/dev/usb2/core/usb2_transfer.h | 2 |
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_ */ |
