]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/usb/core/hub.c
usb: hub_handle_remote_wakeup() only exists for CONFIG_PM=y
[karo-tx-linux.git] / drivers / usb / core / hub.c
index d43054e8e257706391f2d797a0c09fa4baeb3cb9..db6287025c06148adcd86608a0f7c38743be7a36 100644 (file)
@@ -2039,6 +2039,18 @@ static void hub_free_dev(struct usb_device *udev)
                hcd->driver->free_dev(hcd, udev);
 }
 
+static void hub_disconnect_children(struct usb_device *udev)
+{
+       struct usb_hub *hub = usb_hub_to_struct_hub(udev);
+       int i;
+
+       /* Free up all the children before we remove this device */
+       for (i = 0; i < udev->maxchild; i++) {
+               if (hub->ports[i]->child)
+                       usb_disconnect(&hub->ports[i]->child);
+       }
+}
+
 /**
  * usb_disconnect - disconnect a device (usbcore-internal)
  * @pdev: pointer to device being disconnected
@@ -2057,9 +2069,10 @@ static void hub_free_dev(struct usb_device *udev)
  */
 void usb_disconnect(struct usb_device **pdev)
 {
-       struct usb_device       *udev = *pdev;
-       struct usb_hub          *hub = usb_hub_to_struct_hub(udev);
-       int                     i;
+       struct usb_port *port_dev = NULL;
+       struct usb_device *udev = *pdev;
+       struct usb_hub *hub;
+       int port1;
 
        /* mark the device as inactive, so any further urb submissions for
         * this device (and any of its children) will fail immediately.
@@ -2071,11 +2084,7 @@ void usb_disconnect(struct usb_device **pdev)
 
        usb_lock_device(udev);
 
-       /* Free up all the children before we remove this device */
-       for (i = 0; i < udev->maxchild; i++) {
-               if (hub->ports[i]->child)
-                       usb_disconnect(&hub->ports[i]->child);
-       }
+       hub_disconnect_children(udev);
 
        /* deallocate hcd/hardware state ... nuking all pending urbs and
         * cleaning up all state associated with the current configuration
@@ -2086,15 +2095,19 @@ void usb_disconnect(struct usb_device **pdev)
        usb_hcd_synchronize_unlinks(udev);
 
        if (udev->parent) {
-               int port1 = udev->portnum;
-               struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
-               struct usb_port *port_dev = hub->ports[port1 - 1];
+               port1 = udev->portnum;
+               hub = usb_hub_to_struct_hub(udev->parent);
+               port_dev = hub->ports[port1 - 1];
 
                sysfs_remove_link(&udev->dev.kobj, "port");
                sysfs_remove_link(&port_dev->dev.kobj, "device");
 
-               if (test_and_clear_bit(port1, hub->child_usage_bits))
-                       pm_runtime_put(&port_dev->dev);
+               /*
+                * As usb_port_runtime_resume() de-references udev, make
+                * sure no resumes occur during removal
+                */
+               if (!test_and_set_bit(port1, hub->child_usage_bits))
+                       pm_runtime_get_sync(&port_dev->dev);
        }
 
        usb_remove_ep_devs(&udev->ep0);
@@ -2116,6 +2129,9 @@ void usb_disconnect(struct usb_device **pdev)
        *pdev = NULL;
        spin_unlock_irq(&device_state_lock);
 
+       if (port_dev && test_and_clear_bit(port1, hub->child_usage_bits))
+               pm_runtime_put(&port_dev->dev);
+
        hub_free_dev(udev);
 
        put_device(&udev->dev);
@@ -3348,6 +3364,55 @@ int usb_remote_wakeup(struct usb_device *udev)
        return status;
 }
 
+/* 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)
+               __must_hold(&port_dev->status_lock)
+{
+       struct usb_port *port_dev = hub->ports[port - 1];
+       struct usb_device *hdev;
+       struct usb_device *udev;
+       int connect_change = 0;
+       int ret;
+
+       hdev = hub->hdev;
+       udev = port_dev->child;
+       if (!hub_is_superspeed(hdev)) {
+               if (!(portchange & USB_PORT_STAT_C_SUSPEND))
+                       return 0;
+               usb_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_unlock_port(port_dev);
+               ret = usb_remote_wakeup(udev);
+               usb_lock_port(port_dev);
+               if (ret < 0)
+                       connect_change = 1;
+       } else {
+               ret = -ENODEV;
+               hub_port_disable(hub, port, 1);
+       }
+       dev_dbg(&port_dev->dev, "resume, status %d\n", ret);
+       return connect_change;
+}
+
+#else
+
+static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
+               u16 portstatus, u16 portchange)
+{
+       return 0;
+}
+
 #endif
 
 static int check_ports_changed(struct usb_hub *hub)
@@ -3901,6 +3966,12 @@ EXPORT_SYMBOL_GPL(usb_disable_ltm);
 void usb_enable_ltm(struct usb_device *udev) { }
 EXPORT_SYMBOL_GPL(usb_enable_ltm);
 
+static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
+               u16 portstatus, u16 portchange)
+{
+       return 0;
+}
+
 #endif /* CONFIG_PM */
 
 
@@ -4697,47 +4768,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
        usb_lock_port(port_dev);
 }
 
-/* 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)
-               __must_hold(&port_dev->status_lock)
-{
-       struct usb_port *port_dev = hub->ports[port - 1];
-       struct usb_device *hdev;
-       struct usb_device *udev;
-       int connect_change = 0;
-       int ret;
-
-       hdev = hub->hdev;
-       udev = port_dev->child;
-       if (!hub_is_superspeed(hdev)) {
-               if (!(portchange & USB_PORT_STAT_C_SUSPEND))
-                       return 0;
-               usb_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_unlock_port(port_dev);
-               ret = usb_remote_wakeup(udev);
-               usb_lock_port(port_dev);
-               if (ret < 0)
-                       connect_change = 1;
-       } else {
-               ret = -ENODEV;
-               hub_port_disable(hub, port, 1);
-       }
-       dev_dbg(&port_dev->dev, "resume, status %d\n", ret);
-       return connect_change;
-}
-
 static void port_event(struct usb_hub *hub, int port1)
                __must_hold(&port_dev->status_lock)
 {