aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-02-23 08:20:44 -0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-02-23 08:21:03 -0800
commitc69263c66e5b2a5d0c7e5a41c189b1846ae1de92 (patch)
tree81c6cf0f5b3b05739e2e73b0118077d9dd245182 /drivers/usb
parent45196cee28a5bcfb6ddbe2bffa4270cbed66ae4b (diff)
parent5407a3c3d942e75d4d123d213fd692bce5acc961 (diff)
Merge branch 'usb-3.3-rc4' into usb-next
This is to pull in the xhci changes and the other fixes and device id updates that were done in Linus's tree. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/Kconfig1
-rw-r--r--drivers/usb/class/cdc-wdm.c119
-rw-r--r--drivers/usb/core/driver.c120
-rw-r--r--drivers/usb/core/hub.c202
-rw-r--r--drivers/usb/core/sysfs.c23
-rw-r--r--drivers/usb/core/urb.c21
-rw-r--r--drivers/usb/core/usb.c2
-rw-r--r--drivers/usb/core/usb.h1
-rw-r--r--drivers/usb/host/ehci-fsl.c14
-rw-r--r--drivers/usb/host/ehci-fsl.h2
-rw-r--r--drivers/usb/host/ehci-hcd.c5
-rw-r--r--drivers/usb/host/ehci-hub.c8
-rw-r--r--drivers/usb/host/ehci-ls1x.c159
-rw-r--r--drivers/usb/host/imx21-dbg.c2
-rw-r--r--drivers/usb/host/uhci-hcd.c3
-rw-r--r--drivers/usb/host/xhci-hub.c41
-rw-r--r--drivers/usb/host/xhci-mem.c11
-rw-r--r--drivers/usb/host/xhci-ring.c74
-rw-r--r--drivers/usb/host/xhci.h1
-rw-r--r--drivers/usb/serial/cp210x.c13
-rw-r--r--drivers/usb/serial/cypress_m8.c2
-rw-r--r--drivers/usb/serial/digi_acceleport.c4
-rw-r--r--drivers/usb/serial/generic.c2
-rw-r--r--drivers/usb/serial/io_edgeport.c4
-rw-r--r--drivers/usb/serial/io_ti.c4
-rw-r--r--drivers/usb/serial/mos7720.c4
-rw-r--r--drivers/usb/serial/mos7840.c4
-rw-r--r--drivers/usb/serial/omninet.c2
-rw-r--r--drivers/usb/serial/oti6858.c6
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c5
-rw-r--r--drivers/usb/serial/whiteheat.c2
-rw-r--r--drivers/usb/storage/alauda.c1
-rw-r--r--drivers/usb/storage/cypress_atacb.c1
-rw-r--r--drivers/usb/storage/datafab.c1
-rw-r--r--drivers/usb/storage/ene_ub6250.c1
-rw-r--r--drivers/usb/storage/freecom.c1
-rw-r--r--drivers/usb/storage/isd200.c1
-rw-r--r--drivers/usb/storage/jumpshot.c1
-rw-r--r--drivers/usb/storage/karma.c1
-rw-r--r--drivers/usb/storage/onetouch.c1
-rw-r--r--drivers/usb/storage/realtek_cr.c1
-rw-r--r--drivers/usb/storage/scsiglue.c55
-rw-r--r--drivers/usb/storage/sddr09.c1
-rw-r--r--drivers/usb/storage/sddr55.c1
-rw-r--r--drivers/usb/storage/shuttle_usbat.c1
-rw-r--r--drivers/usb/storage/uas.c328
-rw-r--r--drivers/usb/storage/usb.c21
47 files changed, 930 insertions, 348 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 75823a1abeb..0b0afc81a54 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -76,6 +76,7 @@ config USB_ARCH_HAS_EHCI
default y if MICROBLAZE
default y if SPARC_LEON
default y if ARCH_MMP
+ default y if MACH_LOONGSON1
default PCI
# some non-PCI HCDs implement xHCI
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index d2b3cffca3f..b27bbb957e1 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -31,6 +31,8 @@
#define DRIVER_AUTHOR "Oliver Neukum"
#define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management"
+#define HUAWEI_VENDOR_ID 0x12D1
+
static const struct usb_device_id wdm_ids[] = {
{
.match_flags = USB_DEVICE_ID_MATCH_INT_CLASS |
@@ -38,6 +40,20 @@ static const struct usb_device_id wdm_ids[] = {
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_DMM
},
+ {
+ /*
+ * Huawei E392, E398 and possibly other Qualcomm based modems
+ * embed the Qualcomm QMI protocol inside CDC on CDC ECM like
+ * control interfaces. Userspace access to this is required
+ * to configure the accompanying data interface
+ */
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR |
+ USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = HUAWEI_VENDOR_ID,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 9, /* NOTE: CDC ECM control interface! */
+ },
{ }
};
@@ -54,6 +70,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);
#define WDM_POLL_RUNNING 6
#define WDM_RESPONDING 7
#define WDM_SUSPENDING 8
+#define WDM_RESETTING 9
#define WDM_MAX 16
@@ -82,7 +99,6 @@ struct wdm_device {
u16 bufsize;
u16 wMaxCommand;
u16 wMaxPacketSize;
- u16 bMaxPacketSize0;
__le16 inum;
int reslength;
int length;
@@ -162,11 +178,9 @@ static void wdm_int_callback(struct urb *urb)
int rv = 0;
int status = urb->status;
struct wdm_device *desc;
- struct usb_ctrlrequest *req;
struct usb_cdc_notification *dr;
desc = urb->context;
- req = desc->irq;
dr = (struct usb_cdc_notification *)desc->sbuf;
if (status) {
@@ -213,24 +227,6 @@ static void wdm_int_callback(struct urb *urb)
goto exit;
}
- req->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
- req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
- req->wValue = 0;
- req->wIndex = desc->inum;
- req->wLength = cpu_to_le16(desc->wMaxCommand);
-
- usb_fill_control_urb(
- desc->response,
- interface_to_usbdev(desc->intf),
- /* using common endpoint 0 */
- usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
- (unsigned char *)req,
- desc->inbuf,
- desc->wMaxCommand,
- wdm_in_callback,
- desc
- );
- desc->response->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
spin_lock(&desc->iuspin);
clear_bit(WDM_READ, &desc->flags);
set_bit(WDM_RESPONDING, &desc->flags);
@@ -279,14 +275,8 @@ static void free_urbs(struct wdm_device *desc)
static void cleanup(struct wdm_device *desc)
{
- usb_free_coherent(interface_to_usbdev(desc->intf),
- desc->wMaxPacketSize,
- desc->sbuf,
- desc->validity->transfer_dma);
- usb_free_coherent(interface_to_usbdev(desc->intf),
- desc->bMaxPacketSize0,
- desc->inbuf,
- desc->response->transfer_dma);
+ kfree(desc->sbuf);
+ kfree(desc->inbuf);
kfree(desc->orq);
kfree(desc->irq);
kfree(desc->ubuf);
@@ -351,6 +341,10 @@ static ssize_t wdm_write
else
if (test_bit(WDM_IN_USE, &desc->flags))
r = -EAGAIN;
+
+ if (test_bit(WDM_RESETTING, &desc->flags))
+ r = -EIO;
+
if (r < 0) {
kfree(buf);
goto out;
@@ -430,6 +424,10 @@ retry:
rv = -ENODEV;
goto err;
}
+ if (test_bit(WDM_RESETTING, &desc->flags)) {
+ rv = -EIO;
+ goto err;
+ }
usb_mark_last_busy(interface_to_usbdev(desc->intf));
if (rv < 0) {
rv = -ERESTARTSYS;
@@ -631,7 +629,6 @@ static void wdm_rxwork(struct work_struct *work)
static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
int rv = -EINVAL;
- struct usb_device *udev = interface_to_usbdev(intf);
struct wdm_device *desc;
struct usb_host_interface *iface;
struct usb_endpoint_descriptor *ep;
@@ -692,7 +689,6 @@ next_desc:
goto err;
desc->wMaxPacketSize = usb_endpoint_maxp(ep);
- desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0;
desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
if (!desc->orq)
@@ -717,19 +713,13 @@ next_desc:
if (!desc->ubuf)
goto err;
- desc->sbuf = usb_alloc_coherent(interface_to_usbdev(intf),
- desc->wMaxPacketSize,
- GFP_KERNEL,
- &desc->validity->transfer_dma);
+ desc->sbuf = kmalloc(desc->wMaxPacketSize, GFP_KERNEL);
if (!desc->sbuf)
goto err;
- desc->inbuf = usb_alloc_coherent(interface_to_usbdev(intf),
- desc->wMaxCommand,
- GFP_KERNEL,
- &desc->response->transfer_dma);
+ desc->inbuf = kmalloc(desc->wMaxCommand, GFP_KERNEL);
if (!desc->inbuf)
- goto err2;
+ goto err;
usb_fill_int_urb(
desc->validity,
@@ -741,30 +731,39 @@ next_desc:
desc,
ep->bInterval
);
- desc->validity->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ desc->irq->bRequestType = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
+ desc->irq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
+ desc->irq->wValue = 0;
+ desc->irq->wIndex = desc->inum;
+ desc->irq->wLength = cpu_to_le16(desc->wMaxCommand);
+
+ usb_fill_control_urb(
+ desc->response,
+ interface_to_usbdev(intf),
+ /* using common endpoint 0 */
+ usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
+ (unsigned char *)desc->irq,
+ desc->inbuf,
+ desc->wMaxCommand,
+ wdm_in_callback,
+ desc
+ );
usb_set_intfdata(intf, desc);
rv = usb_register_dev(intf, &wdm_class);
if (rv < 0)
- goto err3;
+ goto err2;
else
- dev_info(&intf->dev, "cdc-wdm%d: USB WDM device\n",
- intf->minor - WDM_MINOR_BASE);
+ dev_info(&intf->dev, "%s: USB WDM device\n", dev_name(intf->usb_dev));
out:
return rv;
-err3:
- usb_set_intfdata(intf, NULL);
- usb_free_coherent(interface_to_usbdev(desc->intf),
- desc->bMaxPacketSize0,
- desc->inbuf,
- desc->response->transfer_dma);
err2:
- usb_free_coherent(interface_to_usbdev(desc->intf),
- desc->wMaxPacketSize,
- desc->sbuf,
- desc->validity->transfer_dma);
+ usb_set_intfdata(intf, NULL);
err:
free_urbs(desc);
+ kfree(desc->inbuf);
+ kfree(desc->sbuf);
kfree(desc->ubuf);
kfree(desc->orq);
kfree(desc->irq);
@@ -869,10 +868,6 @@ static int wdm_pre_reset(struct usb_interface *intf)
{
struct wdm_device *desc = usb_get_intfdata(intf);
- mutex_lock(&desc->rlock);
- mutex_lock(&desc->wlock);
- kill_urbs(desc);
-
/*
* we notify everybody using poll of
* an exceptional situation
@@ -880,9 +875,16 @@ static int wdm_pre_reset(struct usb_interface *intf)
* message from the device is lost
*/
spin_lock_irq(&desc->iuspin);
+ set_bit(WDM_RESETTING, &desc->flags); /* inform read/write */
+ set_bit(WDM_READ, &desc->flags); /* unblock read */
+ clear_bit(WDM_IN_USE, &desc->flags); /* unblock write */
desc->rerr = -EINTR;
spin_unlock_irq(&desc->iuspin);
wake_up_all(&desc->wait);
+ mutex_lock(&desc->rlock);
+ mutex_lock(&desc->wlock);
+ kill_urbs(desc);
+ cancel_work_sync(&desc->rxwork);
return 0;
}
@@ -891,6 +893,7 @@ static int wdm_post_reset(struct usb_interface *intf)
struct wdm_device *desc = usb_get_intfdata(intf);
int rv;
+ clear_bit(WDM_RESETTING, &desc->flags);
rv = recover_from_urb_loss(desc);
mutex_unlock(&desc->wlock);
mutex_unlock(&desc->rlock);
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index d40ff956881..d77daf3683d 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -958,13 +958,8 @@ void usb_rebind_intf(struct usb_interface *intf)
int rc;
/* Delayed unbind of an existing driver */
- if (intf->dev.driver) {
- struct usb_driver *driver =
- to_usb_driver(intf->dev.driver);
-
- dev_dbg(&intf->dev, "forced unbind\n");
- usb_driver_release_interface(driver, intf);
- }
+ if (intf->dev.driver)
+ usb_forced_unbind_intf(intf);
/* Try to rebind the interface */
if (!intf->dev.power.is_prepared) {
@@ -977,15 +972,13 @@ void usb_rebind_intf(struct usb_interface *intf)
#ifdef CONFIG_PM
-#define DO_UNBIND 0
-#define DO_REBIND 1
-
-/* Unbind drivers for @udev's interfaces that don't support suspend/resume,
- * or rebind interfaces that have been unbound, according to @action.
+/* Unbind drivers for @udev's interfaces that don't support suspend/resume
+ * There is no check for reset_resume here because it can be determined
+ * only during resume whether reset_resume is needed.
*
* The caller must hold @udev's device lock.
*/
-static void do_unbind_rebind(struct usb_device *udev, int action)
+static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
{
struct usb_host_config *config;
int i;
@@ -996,23 +989,53 @@ static void do_unbind_rebind(struct usb_device *udev, int action)
if (config) {
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
intf = config->interface[i];
- switch (action) {
- case DO_UNBIND:
- if (intf->dev.driver) {
- drv = to_usb_driver(intf->dev.driver);
- if (!drv->suspend || !drv->resume)
- usb_forced_unbind_intf(intf);
- }
- break;
- case DO_REBIND:
- if (intf->needs_binding)
- usb_rebind_intf(intf);
- break;
+
+ if (intf->dev.driver) {
+ drv = to_usb_driver(intf->dev.driver);
+ if (!drv->suspend || !drv->resume)
+ usb_forced_unbind_intf(intf);
}
}
}
}
+/* Unbind drivers for @udev's interfaces that failed to support reset-resume.
+ * These interfaces have the needs_binding flag set by usb_resume_interface().
+ *
+ * The caller must hold @udev's device lock.
+ */
+static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev)
+{
+ struct usb_host_config *config;
+ int i;
+ struct usb_interface *intf;
+
+ config = udev->actconfig;
+ if (config) {
+ for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+ intf = config->interface[i];
+ if (intf->dev.driver && intf->needs_binding)
+ usb_forced_unbind_intf(intf);
+ }
+ }
+}
+
+static void do_rebind_interfaces(struct usb_device *udev)
+{
+ struct usb_host_config *config;
+ int i;
+ struct usb_interface *intf;
+
+ config = udev->actconfig;
+ if (config) {
+ for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+ intf = config->interface[i];
+ if (intf->needs_binding)
+ usb_rebind_intf(intf);
+ }
+ }
+}
+
static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
{
struct usb_device_driver *udriver;
@@ -1302,35 +1325,48 @@ int usb_suspend(struct device *dev, pm_message_t msg)
{
struct usb_device *udev = to_usb_device(dev);
- do_unbind_rebind(udev, DO_UNBIND);
+ unbind_no_pm_drivers_interfaces(udev);
+
+ /* From now on we are sure all drivers support suspend/resume
+ * but not necessarily reset_resume()
+ * so we may still need to unbind and rebind upon resume
+ */
choose_wakeup(udev, msg);
return usb_suspend_both(udev, msg);
}
/* The device lock is held by the PM core */
+int usb_resume_complete(struct device *dev)
+{
+ struct usb_device *udev = to_usb_device(dev);
+
+ /* For PM complete calls, all we do is rebind interfaces
+ * whose needs_binding flag is set
+ */
+ if (udev->state != USB_STATE_NOTATTACHED)
+ do_rebind_interfaces(udev);
+ return 0;
+}
+
+/* The device lock is held by the PM core */
int usb_resume(struct device *dev, pm_message_t msg)
{
struct usb_device *udev = to_usb_device(dev);
int status;
- /* For PM complete calls, all we do is rebind interfaces */
- if (msg.event == PM_EVENT_ON) {
- if (udev->state != USB_STATE_NOTATTACHED)
- do_unbind_rebind(udev, DO_REBIND);
- status = 0;
-
- /* For all other calls, take the device back to full power and
+ /* For all calls, take the device back to full power and
* tell the PM core in case it was autosuspended previously.
- * Unbind the interfaces that will need rebinding later.
+ * Unbind the interfaces that will need rebinding later,
+ * because they fail to support reset_resume.
+ * (This can't be done in usb_resume_interface()
+ * above because it doesn't own the right set of locks.)
*/
- } else {
- status = usb_resume_both(udev, msg);
- if (status == 0) {
- pm_runtime_disable(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
- do_unbind_rebind(udev, DO_REBIND);
- }
+ status = usb_resume_both(udev, msg);
+ if (status == 0) {
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ unbind_no_reset_resume_drivers_interfaces(udev);
}
/* Avoid PM error messages for devices disconnected while suspended
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 265c2f675d0..72c51cdce90 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -62,6 +62,8 @@ struct usb_hub {
resumed */
unsigned long removed_bits[1]; /* ports with a "removed"
device present */
+ unsigned long wakeup_bits[1]; /* ports that have signaled
+ remote wakeup */
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
#error event_bits[] is too short!
#endif
@@ -411,6 +413,29 @@ void usb_kick_khubd(struct usb_device *hdev)
kick_khubd(hub);
}
+/*
+ * Let the USB core know that a USB 3.0 device has sent a Function Wake Device
+ * Notification, which indicates it had initiated remote wakeup.
+ *
+ * USB 3.0 hubs do not report the port link state change from U3 to U0 when the
+ * device initiates resume, so the USB core will not receive notice of the
+ * resume through the normal hub interrupt URB.
+ */
+void usb_wakeup_notification(struct usb_device *hdev,
+ unsigned int portnum)
+{
+ struct usb_hub *hub;
+
+ if (!hdev)
+ return;
+
+ hub = hdev_to_hub(hdev);
+ if (hub) {
+ set_bit(portnum, hub->wakeup_bits);
+ kick_khubd(hub);
+ }
+}
+EXPORT_SYMBOL_GPL(usb_wakeup_notification);
/* completion function, fires on port status changes and various faults */
static void hub_irq(struct urb *urb)
@@ -823,12 +848,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_ENABLE);
}
- if (portchange & USB_PORT_STAT_C_LINK_STATE) {
- need_debounce_delay = true;
- clear_port_feature(hub->hdev, port1,
- USB_PORT_FEAT_C_PORT_LINK_STATE);
- }
-
if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
hub_is_superspeed(hub->hdev)) {
need_debounce_delay = true;
@@ -850,12 +869,19 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
set_bit(port1, hub->change_bits);
} else if (portstatus & USB_PORT_STAT_ENABLE) {
+ bool port_resumed = (portstatus &
+ USB_PORT_STAT_LINK_STATE) ==
+ USB_SS_PORT_LS_U0;
/* The power session apparently survived the resume.
* If there was an overcurrent or suspend change
* (i.e., remote wakeup request), have khubd
- * take care of it.
+ * take care of it. Look at the port link state
+ * for USB 3.0 hubs, since they don't have a suspend
+ * change bit, and they don't set the port link change
+ * bit on device-initiated resume.
*/
- if (portchange)
+ if (portchange || (hub_is_superspeed(hub->hdev) &&
+ port_resumed))
set_bit(port1, hub->change_bits);
} else if (udev->persist_enabled) {
@@ -1293,14 +1319,8 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
desc = intf->cur_altsetting;
hdev = interface_to_usbdev(intf);
- /* Hubs have proper suspend/resume support. USB 3.0 device suspend is
- * different from USB 2.0/1.1 device suspend, and unfortunately we
- * don't support it yet. So leave autosuspend disabled for USB 3.0
- * external hubs for now. Enable autosuspend for USB 3.0 roothubs,
- * since that isn't a "real" hub.
- */
- if (!hub_is_superspeed(hdev) || !hdev->parent)
- usb_enable_autosuspend(hdev);
+ /* Hubs have proper suspend/resume support. */
+ usb_enable_autosuspend(hdev);
if (hdev->level == MAX_TOPO_LEVEL) {
dev_err(&intf->dev,
@@ -1842,6 +1862,37 @@ fail:
return err;
}
+static void set_usb_port_removable(struct usb_device *udev)
+{
+ struct usb_device *hdev = udev->parent;
+ struct usb_hub *hub;
+ u8 port = udev->portnum;
+ u16 wHubCharacteristics;
+ bool removable = true;
+
+ if (!hdev)
+ return;
+
+ hub = hdev_to_hub(udev->parent);
+
+ wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
+
+ if (!(wHubCharacteristics & HUB_CHAR_COMPOUND))
+ return;
+
+ if (hub_is_superspeed(hdev)) {
+ if (hub->descriptor->u.ss.DeviceRemovable & (1 << port))
+ removable = false;
+ } else {
+ if (hub->descriptor->u.hs.DeviceRemovable[port / 8] & (1 << (port % 8)))
+ removable = false;
+ }
+
+ if (removable)
+ udev->removable = USB_DEVICE_REMOVABLE;
+ else
+ udev->removable = USB_DEVICE_FIXED;
+}
/**
* usb_new_device - perform initial device setup (usbcore-internal)
@@ -1900,6 +1951,15 @@ int usb_new_device(struct usb_device *udev)
announce_device(udev);
device_enable_async_suspend(&udev->dev);
+
+ /*
+ * check whether the hub marks this port as non-removable. Do it
+ * now so that platform-specific data can override it in
+ * device_add()
+ */
+ if (udev->parent)
+ set_usb_port_removable(udev);
+
/* Register the device. The device driver is responsible
* for configuring the device and invoking the add-device
* notifier chain (used by usbfs and possibly others).
@@ -2385,11 +2445,27 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
* we don't explicitly enable it here.
*/
if (udev->do_remote_wakeup) {
- status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
- USB_DEVICE_REMOTE_WAKEUP, 0,
- NULL, 0,
- USB_CTRL_SET_TIMEOUT);
+ if (!hub_is_superspeed(hub->hdev)) {
+ status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
+ USB_DEVICE_REMOTE_WAKEUP, 0,
+ NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+ } else {
+ /* Assume there's only one function on the USB 3.0
+ * device and enable remote wake for the first
+ * interface. FIXME if the interface association
+ * descriptor shows there's more than one function.
+ */
+ status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_SET_FEATURE,
+ USB_RECIP_INTERFACE,
+ USB_INTRF_FUNC_SUSPEND,
+ USB_INTRF_FUNC_SUSPEND_RW |
+ USB_INTRF_FUNC_SUSPEND_LP,
+ NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+ }
if (status) {
dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
status);
@@ -2679,6 +2755,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
struct usb_hub *hub = usb_get_intfdata (intf);
struct usb_device *hdev = hub->hdev;
unsigned port1;
+ int status;
/* Warn if children aren't already suspended */
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
@@ -2691,6 +2768,17 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
return -EBUSY;
}
}
+ if (hub_is_superspeed(hdev) && hdev->do_remote_wakeup) {
+ /* Enable hub to send remote wakeup for all ports. */
+ for (port1 = 1; port1 <= hdev->maxchild; port1++) {
+ status = set_port_feature(hdev,
+ port1 |
+ USB_PORT_FEAT_REMOTE_WAKE_CONNECT |
+ USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT |
+ USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT,
+ USB_PORT_FEAT_REMOTE_WAKE_MASK);
+ }
+ }
dev_dbg(&intf->dev, "%s\n", __func__);
@@ -3424,6 +3512,46 @@ done:
hcd->driver->relinquish_port(hcd, port1);
}
+/* Returns 1 if there was a remote wakeup and a connect status change. */
+static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
+ u16 portstatus, u16 portchange)
+{
+ struct usb_device *hdev;
+ struct usb_device *udev;
+ int connect_change = 0;
+ int ret;
+
+ hdev = hub->hdev;
+ udev = hdev->children[port-1];
+ if (!hub_is_superspeed(hdev)) {
+ if (!(portchange & USB_PORT_STAT_C_SUSPEND))
+ return 0;
+ clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND);
+ } else {
+ if (!udev || udev->state != USB_STATE_SUSPENDED ||
+ (portstatus & USB_PORT_STAT_LINK_STATE) !=
+ USB_SS_PORT_LS_U0)
+ return 0;
+ }
+
+ if (udev) {
+ /* TRSMRCY = 10 msec */
+ msleep(10);
+
+ usb_lock_device(udev);
+ ret = usb_remote_wakeup(udev);
+ usb_unlock_device(udev);
+ if (ret < 0)
+ connect_change = 1;
+ } else {
+ ret = -ENODEV;
+ hub_port_disable(hub, port, 1);
+ }
+ dev_dbg(hub->intfdev, "resume on port %d, status %d\n",
+ port, ret);
+ return connect_change;
+}
+
static void hub_events(void)
{
struct list_head *tmp;
@@ -3436,7 +3564,7 @@ static void hub_events(void)
u16 portstatus;
u16 portchange;
int i, ret;
- int connect_change;
+ int connect_change, wakeup_change;
/*
* We restart the list every time to avoid a deadlock with
@@ -3515,8 +3643,9 @@ static void hub_events(void)
if (test_bit(i, hub->busy_bits))
continue;
connect_change = test_bit(i, hub->change_bits);
+ wakeup_change = test_and_clear_bit(i, hub->wakeup_bits);
if (!test_and_clear_bit(i, hub->event_bits) &&
- !connect_change)
+ !connect_change && !wakeup_change)
continue;
ret = hub_port_status(hub, i,
@@ -3557,31 +3686,10 @@ static void hub_events(void)
}
}
- if (portchange & USB_PORT_STAT_C_SUSPEND) {
- struct usb_device *udev;
+ if (hub_handle_remote_wakeup(hub, i,
+ portstatus, portcha