USB: make hcd.h public (drivers dependency)
[deliverable/linux.git] / drivers / usb / core / hub.c
index 35cc8b9ba1f5bfab21331d6241a529ca2b3de304..1883c3c7b69bba97e56a2ee1d84770218fdd4b6d 100644 (file)
 #include <linux/ioctl.h>
 #include <linux/usb.h>
 #include <linux/usbdevice_fs.h>
+#include <linux/usb/hcd.h>
 #include <linux/kthread.h>
 #include <linux/mutex.h>
 #include <linux/freezer.h>
+#include <linux/pm_runtime.h>
 
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 
 #include "usb.h"
-#include "hcd.h"
-#include "hub.h"
 
 /* if we are in debug mode, always announce new devices */
 #ifdef DEBUG
@@ -71,7 +71,6 @@ struct usb_hub {
 
        unsigned                mA_per_port;    /* current for each child */
 
-       unsigned                init_done:1;
        unsigned                limited_power:1;
        unsigned                quiescing:1;
        unsigned                disconnected:1;
@@ -820,7 +819,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
        }
  init3:
        hub->quiescing = 0;
-       hub->init_done = 1;
 
        status = usb_submit_urb(hub->urb, GFP_NOIO);
        if (status < 0)
@@ -861,11 +859,6 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
        int i;
 
        cancel_delayed_work_sync(&hub->init_work);
-       if (!hub->init_done) {
-               hub->init_done = 1;
-               usb_autopm_put_interface_no_suspend(
-                               to_usb_interface(hub->intfdev));
-       }
 
        /* khubd and related activity won't re-trigger */
        hub->quiescing = 1;
@@ -1224,6 +1217,9 @@ 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_enable_autosuspend(hdev);
+
        if (hdev->level == MAX_TOPO_LEVEL) {
                dev_err(&intf->dev,
                        "Unsupported bus topology: hub nested too deep\n");
@@ -1402,10 +1398,8 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev)
                if (udev->children[i])
                        recursively_mark_NOTATTACHED(udev->children[i]);
        }
-       if (udev->state == USB_STATE_SUSPENDED) {
-               udev->discon_suspended = 1;
+       if (udev->state == USB_STATE_SUSPENDED)
                udev->active_duration -= jiffies;
-       }
        udev->state = USB_STATE_NOTATTACHED;
 }
 
@@ -1448,11 +1442,11 @@ void usb_set_device_state(struct usb_device *udev,
                                        || new_state == USB_STATE_SUSPENDED)
                                ;       /* No change to wakeup settings */
                        else if (new_state == USB_STATE_CONFIGURED)
-                               device_init_wakeup(&udev->dev,
+                               device_set_wakeup_capable(&udev->dev,
                                        (udev->actconfig->desc.bmAttributes
                                         & USB_CONFIG_ATT_WAKEUP));
                        else
-                               device_init_wakeup(&udev->dev, 0);
+                               device_set_wakeup_capable(&udev->dev, 0);
                }
                if (udev->state == USB_STATE_SUSPENDED &&
                        new_state != USB_STATE_SUSPENDED)
@@ -1529,31 +1523,15 @@ static void update_address(struct usb_device *udev, int devnum)
                udev->devnum = devnum;
 }
 
-#ifdef CONFIG_USB_SUSPEND
-
-static void usb_stop_pm(struct usb_device *udev)
+static void hub_free_dev(struct usb_device *udev)
 {
-       /* Synchronize with the ksuspend thread to prevent any more
-        * autosuspend requests from being submitted, and decrement
-        * the parent's count of unsuspended children.
-        */
-       usb_pm_lock(udev);
-       if (udev->parent && !udev->discon_suspended)
-               usb_autosuspend_device(udev->parent);
-       usb_pm_unlock(udev);
+       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
 
-       /* Stop any autosuspend or autoresume requests already submitted */
-       cancel_delayed_work_sync(&udev->autosuspend);
-       cancel_work_sync(&udev->autoresume);
+       /* Root hubs aren't real devices, so don't free HCD resources */
+       if (hcd->driver->free_dev && udev->parent)
+               hcd->driver->free_dev(hcd, udev);
 }
 
-#else
-
-static inline void usb_stop_pm(struct usb_device *udev)
-{ }
-
-#endif
-
 /**
  * usb_disconnect - disconnect a device (usbcore-internal)
  * @pdev: pointer to device being disconnected
@@ -1622,7 +1600,7 @@ void usb_disconnect(struct usb_device **pdev)
        *pdev = NULL;
        spin_unlock_irq(&device_state_lock);
 
-       usb_stop_pm(udev);
+       hub_free_dev(udev);
 
        put_device(&udev->dev);
 }
@@ -1799,9 +1777,18 @@ int usb_new_device(struct usb_device *udev)
 {
        int err;
 
-       /* Increment the parent's count of unsuspended children */
-       if (udev->parent)
-               usb_autoresume_device(udev->parent);
+       if (udev->parent) {
+               /* Initialize non-root-hub device wakeup to disabled;
+                * device (un)configuration controls wakeup capable
+                * sysfs power/wakeup controls wakeup enabled/disabled
+                */
+               device_init_wakeup(&udev->dev, 0);
+               device_set_wakeup_enable(&udev->dev, 1);
+       }
+
+       /* Tell the runtime-PM framework the device is active */
+       pm_runtime_set_active(&udev->dev);
+       pm_runtime_enable(&udev->dev);
 
        usb_detect_quirks(udev);
        err = usb_enumerate_device(udev);       /* Read descriptors */
@@ -1817,6 +1804,7 @@ int usb_new_device(struct usb_device *udev)
        /* Tell the world! */
        announce_device(udev);
 
+       device_enable_async_suspend(&udev->dev);
        /* 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).
@@ -1832,7 +1820,8 @@ int usb_new_device(struct usb_device *udev)
 
 fail:
        usb_set_device_state(udev, USB_STATE_NOTATTACHED);
-       usb_stop_pm(udev);
+       pm_runtime_disable(&udev->dev);
+       pm_runtime_set_suspended(&udev->dev);
        return err;
 }
 
@@ -1981,7 +1970,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                if (!(portstatus & USB_PORT_STAT_RESET) &&
                    (portstatus & USB_PORT_STAT_ENABLE)) {
                        if (hub_is_wusb(hub))
-                               udev->speed = USB_SPEED_VARIABLE;
+                               udev->speed = USB_SPEED_WIRELESS;
                        else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
                                udev->speed = USB_SPEED_HIGH;
                        else if (portstatus & USB_PORT_STAT_LOW_SPEED)
@@ -2007,7 +1996,9 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                                struct usb_device *udev, unsigned int delay)
 {
        int i, status;
+       struct usb_hcd *hcd;
 
+       hcd = bus_to_hcd(udev->bus);
        /* Block EHCI CF initialization during the port reset.
         * Some companion controllers don't like it when they mix.
         */
@@ -2035,6 +2026,14 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                        /* TRSTRCY = 10 ms; plus some extra */
                        msleep(10 + 40);
                        update_address(udev, 0);
+                       if (hcd->driver->reset_device) {
+                               status = hcd->driver->reset_device(hcd, udev);
+                               if (status < 0) {
+                                       dev_err(&udev->dev, "Cannot reset "
+                                                       "HCD device state\n");
+                                       break;
+                               }
+                       }
                        /* FALL THROUGH */
                case -ENOTCONN:
                case -ENODEV:
@@ -2380,14 +2379,17 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
 }
 
 /* caller has locked udev */
-static int remote_wakeup(struct usb_device *udev)
+int usb_remote_wakeup(struct usb_device *udev)
 {
        int     status = 0;
 
        if (udev->state == USB_STATE_SUSPENDED) {
                dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
-               usb_mark_last_busy(udev);
-               status = usb_external_resume_device(udev, PMSG_REMOTE_RESUME);
+               status = usb_autoresume_device(udev);
+               if (status == 0) {
+                       /* Let the drivers do their thing, then... */
+                       usb_autosuspend_device(udev);
+               }
        }
        return status;
 }
@@ -2424,11 +2426,6 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
        return status;
 }
 
-static inline int remote_wakeup(struct usb_device *udev)
-{
-       return 0;
-}
-
 #endif
 
 static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
@@ -2495,11 +2492,6 @@ EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
 
 #else  /* CONFIG_PM */
 
-static inline int remote_wakeup(struct usb_device *udev)
-{
-       return 0;
-}
-
 #define hub_suspend            NULL
 #define hub_resume             NULL
 #define hub_reset_resume       NULL
@@ -2644,14 +2636,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 
        mutex_lock(&usb_address0_mutex);
 
-       if ((hcd->driver->flags & HCD_USB3) && udev->config) {
-               /* FIXME this will need special handling by the xHCI driver. */
-               dev_dbg(&udev->dev,
-                               "xHCI reset of configured device "
-                               "not supported yet.\n");
-               retval = -EINVAL;
-               goto fail;
-       } else if (!udev->config && oldspeed == USB_SPEED_SUPER) {
+       if (!udev->config && oldspeed == USB_SPEED_SUPER) {
                /* Don't reset USB 3.0 devices during an initial setup */
                usb_set_device_state(udev, USB_STATE_DEFAULT);
        } else {
@@ -2677,7 +2662,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
         */
        switch (udev->speed) {
        case USB_SPEED_SUPER:
-       case USB_SPEED_VARIABLE:        /* fixed at 512 */
+       case USB_SPEED_WIRELESS:        /* fixed at 512 */
                udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
                break;
        case USB_SPEED_HIGH:            /* fixed at 64 */
@@ -2705,7 +2690,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        case USB_SPEED_SUPER:
                                speed = "super";
                                break;
-       case USB_SPEED_VARIABLE:
+       case USB_SPEED_WIRELESS:
                                speed = "variable";
                                type = "Wireless ";
                                break;
@@ -3005,7 +2990,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                        /* For a suspended device, treat this as a
                         * remote wakeup event.
                         */
-                       status = remote_wakeup(udev);
+                       status = usb_remote_wakeup(udev);
 #endif
 
                } else {
@@ -3191,6 +3176,7 @@ loop_disable:
 loop:
                usb_ep0_reinit(udev);
                release_address(udev);
+               hub_free_dev(udev);
                usb_put_dev(udev);
                if ((status == -ENOTCONN) || (status == -ENOTSUPP))
                        break;
@@ -3258,7 +3244,7 @@ static void hub_events(void)
                 * disconnected while waiting for the lock to succeed. */
                usb_lock_device(hdev);
                if (unlikely(hub->disconnected))
-                       goto loop2;
+                       goto loop_disconnected;
 
                /* If the hub has died, clean up after it */
                if (hdev->state == USB_STATE_NOTATTACHED) {
@@ -3351,7 +3337,7 @@ static void hub_events(void)
                                        msleep(10);
 
                                        usb_lock_device(udev);
-                                       ret = remote_wakeup(hdev->
+                                       ret = usb_remote_wakeup(hdev->
                                                        children[i-1]);
                                        usb_unlock_device(udev);
                                        if (ret < 0)
@@ -3418,7 +3404,7 @@ static void hub_events(void)
                 * kick_khubd() and allow autosuspend.
                 */
                usb_autopm_put_interface(intf);
- loop2:
+ loop_disconnected:
                usb_unlock_device(hdev);
                kref_put(&hub->kref, hub_release);
 
@@ -3445,7 +3431,7 @@ static int hub_thread(void *__unused)
        return 0;
 }
 
-static struct usb_device_id hub_id_table [] = {
+static const struct usb_device_id hub_id_table[] = {
     { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
       .bDeviceClass = USB_CLASS_HUB},
     { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
This page took 0.03287 seconds and 5 git commands to generate.