]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/core/hub.c
[PATCH] USB: Fix usb hub build
[linux-2.6-omap-h63xx.git] / drivers / usb / core / hub.c
index c9412daff682431a820d11c3595cde972929377d..6600644667912c8fd6a670b838309eb43b003a7d 100644 (file)
@@ -435,6 +435,7 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe)
 static void hub_power_on(struct usb_hub *hub)
 {
        int port1;
+       unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
 
        /* if hub supports power switching, enable power on each port */
        if ((hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) < 2) {
@@ -444,14 +445,15 @@ static void hub_power_on(struct usb_hub *hub)
                                        USB_PORT_FEAT_POWER);
        }
 
-       /* Wait for power to be enabled */
-       msleep(hub->descriptor->bPwrOn2PwrGood * 2);
+       /* Wait at least 100 msec for power to become stable */
+       msleep(max(pgood_delay, (unsigned) 100));
 }
 
 static void hub_quiesce(struct usb_hub *hub)
 {
        /* stop khubd and related activity */
        hub->quiescing = 1;
+       hub->activating = 0;
        usb_kill_urb(hub->urb);
        if (hub->has_indicators)
                cancel_delayed_work(&hub->leds);
@@ -492,6 +494,23 @@ static int hub_hub_status(struct usb_hub *hub,
        return ret;
 }
 
+static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
+{
+       struct usb_device *hdev = hub->hdev;
+       int ret;
+
+       if (hdev->children[port1-1] && set_state) {
+               usb_set_device_state(hdev->children[port1-1],
+                               USB_STATE_NOTATTACHED);
+       }
+       ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
+       if (ret)
+               dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
+                       port1, ret);
+
+       return ret;
+}
+
 static int hub_configure(struct usb_hub *hub,
        struct usb_endpoint_descriptor *endpoint)
 {
@@ -610,19 +629,33 @@ static int hub_configure(struct usb_hub *hub,
                        break;
        }
 
+       /* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
        switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_TTTT) {
-               case 0x00:
-                       if (hdev->descriptor.bDeviceProtocol != 0)
-                               dev_dbg(hub_dev, "TT requires at most 8 FS bit times\n");
+               case HUB_TTTT_8_BITS:
+                       if (hdev->descriptor.bDeviceProtocol != 0) {
+                               hub->tt.think_time = 666;
+                               dev_dbg(hub_dev, "TT requires at most %d "
+                                               "FS bit times (%d ns)\n",
+                                       8, hub->tt.think_time);
+                       }
                        break;
-               case 0x20:
-                       dev_dbg(hub_dev, "TT requires at most 16 FS bit times\n");
+               case HUB_TTTT_16_BITS:
+                       hub->tt.think_time = 666 * 2;
+                       dev_dbg(hub_dev, "TT requires at most %d "
+                                       "FS bit times (%d ns)\n",
+                               16, hub->tt.think_time);
                        break;
-               case 0x40:
-                       dev_dbg(hub_dev, "TT requires at most 24 FS bit times\n");
+               case HUB_TTTT_24_BITS:
+                       hub->tt.think_time = 666 * 3;
+                       dev_dbg(hub_dev, "TT requires at most %d "
+                                       "FS bit times (%d ns)\n",
+                               24, hub->tt.think_time);
                        break;
-               case 0x60:
-                       dev_dbg(hub_dev, "TT requires at most 32 FS bit times\n");
+               case HUB_TTTT_32_BITS:
+                       hub->tt.think_time = 666 * 4;
+                       dev_dbg(hub_dev, "TT requires at most %d "
+                                       "FS bit times (%d ns)\n",
+                               32, hub->tt.think_time);
                        break;
        }
 
@@ -712,20 +745,36 @@ fail:
 
 static unsigned highspeed_hubs;
 
+/* Called after the hub driver is unbound from a hub with children */
+static void hub_remove_children_work(void *__hub)
+{
+       struct usb_hub          *hub = __hub;
+       struct usb_device       *hdev = hub->hdev;
+       int                     i;
+
+       kfree(hub);
+
+       usb_lock_device(hdev);
+       for (i = 0; i < hdev->maxchild; ++i) {
+               if (hdev->children[i])
+                       usb_disconnect(&hdev->children[i]);
+       }
+       usb_unlock_device(hdev);
+       usb_put_dev(hdev);
+}
+
 static void hub_disconnect(struct usb_interface *intf)
 {
        struct usb_hub *hub = usb_get_intfdata (intf);
        struct usb_device *hdev;
+       int n, port1;
 
-       if (!hub)
-               return;
+       usb_set_intfdata (intf, NULL);
        hdev = hub->hdev;
 
        if (hdev->speed == USB_SPEED_HIGH)
                highspeed_hubs--;
 
-       usb_set_intfdata (intf, NULL);
-
        hub_quiesce(hub);
        usb_free_urb(hub->urb);
        hub->urb = NULL;
@@ -746,8 +795,27 @@ static void hub_disconnect(struct usb_interface *intf)
                hub->buffer = NULL;
        }
 
-       /* Free the memory */
-       kfree(hub);
+       /* If there are any children then this is an unbind only, not a
+        * physical disconnection.  The active ports must be disabled
+        * and later on we must call usb_disconnect().  We can't call
+        * it now because we may not hold the hub's device lock.
+        */
+       n = 0;
+       for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
+               if (hdev->children[port1 - 1]) {
+                       ++n;
+                       hub_port_disable(hub, port1, 1);
+               }
+       }
+
+       if (n == 0)
+               kfree(hub);
+       else {
+               /* Reuse the hub->leds work_struct for our own purposes */
+               INIT_WORK(&hub->leds, hub_remove_children_work, hub);
+               schedule_work(&hub->leds);
+               usb_get_dev(hdev);
+       }
 }
 
 static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
@@ -953,9 +1021,15 @@ void usb_set_device_state(struct usb_device *udev,
        spin_lock_irqsave(&device_state_lock, flags);
        if (udev->state == USB_STATE_NOTATTACHED)
                ;       /* do nothing */
-       else if (new_state != USB_STATE_NOTATTACHED)
+       else if (new_state != USB_STATE_NOTATTACHED) {
                udev->state = new_state;
-       else
+               if (new_state == USB_STATE_CONFIGURED)
+                       device_init_wakeup(&udev->dev,
+                               (udev->actconfig->desc.bmAttributes
+                                & USB_CONFIG_ATT_WAKEUP));
+               else if (new_state != USB_STATE_SUSPENDED)
+                       device_init_wakeup(&udev->dev, 0);
+       } else
                recursively_mark_NOTATTACHED(udev);
        spin_unlock_irqrestore(&device_state_lock, flags);
 }
@@ -1051,6 +1125,7 @@ void usb_disconnect(struct usb_device **pdev)
        dev_dbg (&udev->dev, "unregistering device\n");
        release_address(udev);
        usbfs_remove_device(udev);
+       usbdev_remove(udev);
        usb_remove_sysfs_dev_files(udev);
 
        /* Avoid races with recursively_mark_NOTATTACHED() */
@@ -1248,11 +1323,9 @@ int usb_new_device(struct usb_device *udev)
                 * (Includes HNP test device.)
                 */
                if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
-                       static int __usb_suspend_device (struct usb_device *,
-                                               int port1, pm_message_t state);
-                       err = __usb_suspend_device(udev,
-                                       udev->bus->otg_port,
-                                       PMSG_SUSPEND);
+                       static int __usb_suspend_device(struct usb_device *,
+                                               int port1);
+                       err = __usb_suspend_device(udev, udev->bus->otg_port);
                        if (err < 0)
                                dev_dbg(&udev->dev, "HNP fail, %d\n", err);
                }
@@ -1290,6 +1363,7 @@ int usb_new_device(struct usb_device *udev)
        /* USB device state == configured ... usable */
 
        /* add a /proc/bus/usb entry */
+       usbdev_add(udev);
        usbfs_add_device(udev);
        return 0;
 
@@ -1392,7 +1466,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                                        port1, status);
                else {
                        status = hub_port_wait_reset(hub, port1, udev, delay);
-                       if (status)
+                       if (status && status != -ENOTCONN)
                                dev_dbg(hub->intfdev,
                                                "port_wait_reset: err = %d\n",
                                                status);
@@ -1401,8 +1475,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                /* return on disconnect or reset */
                switch (status) {
                case 0:
-                       /* TRSTRCY = 10 ms */
-                       msleep(10);
+                       /* TRSTRCY = 10 ms; plus some extra */
+                       msleep(10 + 40);
                        /* FALL THROUGH */
                case -ENOTCONN:
                case -ENODEV:
@@ -1428,23 +1502,6 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
        return status;
 }
 
-static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
-{
-       struct usb_device *hdev = hub->hdev;
-       int ret;
-
-       if (hdev->children[port1-1] && set_state) {
-               usb_set_device_state(hdev->children[port1-1],
-                               USB_STATE_NOTATTACHED);
-       }
-       ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
-       if (ret)
-               dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
-                       port1, ret);
-
-       return ret;
-}
-
 /*
  * Disable a port and mark a logical connnect-change event, so that some
  * time later khubd will disconnect() any existing usb_device on the port
@@ -1458,7 +1515,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
        /* FIXME let caller ask to power down the port:
         *  - some devices won't enumerate without a VBUS power cycle
         *  - SRP saves power that way
-        *  - usb_suspend_device(dev, PMSG_SUSPEND)
+        *  - ... new call, TBD ...
         * That's easy if this hub can switch power per-port, and
         * khubd reactivates the port later (timer, SRP, etc).
         * Powerdown must be optional, because of reset/DFU.
@@ -1494,11 +1551,7 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
         * NOTE:  OTG devices may issue remote wakeup (or SRP) even when
         * we don't explicitly enable it here.
         */
-       if (udev->actconfig
-                       // && FIXME (remote wakeup enabled on this bus)
-                       // ... currently assuming it's always appropriate
-                       && (udev->actconfig->desc.bmAttributes
-                               & USB_CONFIG_ATT_WAKEUP) != 0) {
+       if (device_may_wakeup(&udev->dev)) {
                status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                                USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
                                USB_DEVICE_REMOTE_WAKEUP, 0,
@@ -1544,9 +1597,12 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
  * Other than re-initializing the hub (plug/unplug, except for root hubs),
  * Linux (2.6) currently has NO mechanisms to initiate that:  no khubd
  * timer, no SRP, no requests through sysfs.
+ *
+ * If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when
+ * the root hub for their bus goes into global suspend ... so we don't
+ * (falsely) update the device power state to say it suspended.
  */
-static int __usb_suspend_device (struct usb_device *udev, int port1,
-                                pm_message_t state)
+static int __usb_suspend_device (struct usb_device *udev, int port1)
 {
        int     status;
 
@@ -1559,67 +1615,21 @@ static int __usb_suspend_device (struct usb_device *udev, int port1,
                return 0;
        }
 
-       /* suspend interface drivers; if this is a hub, it
-        * suspends the child devices
-        */
+       /* all interfaces must already be suspended */
        if (udev->actconfig) {
                int     i;
 
                for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
                        struct usb_interface    *intf;
-                       struct usb_driver       *driver;
 
                        intf = udev->actconfig->interface[i];
-                       if (state.event <= intf->dev.power.power_state.event)
-                               continue;
-                       if (!intf->dev.driver)
-                               continue;
-                       driver = to_usb_driver(intf->dev.driver);
-
-                       if (driver->suspend) {
-                               status = driver->suspend(intf, state);
-                               if (intf->dev.power.power_state.event != state.event
-                                               || status)
-                                       dev_err(&intf->dev,
-                                               "suspend %d fail, code %d\n",
-                                               state.event, status);
-                       }
-
-                       /* only drivers with suspend() can ever resume();
-                        * and after power loss, even they won't.
-                        * bus_rescan_devices() can rebind drivers later.
-                        *
-                        * FIXME the PM core self-deadlocks when unbinding
-                        * drivers during suspend/resume ... everything grabs
-                        * dpm_sem (not a spinlock, ugh).  we want to unbind,
-                        * since we know every driver's probe/disconnect works
-                        * even for drivers that can't suspend.
-                        */
-                       if (!driver->suspend || state.event > PM_EVENT_FREEZE) {
-#if 1
-                               dev_warn(&intf->dev, "resume is unsafe!\n");
-#else
-                               down_write(&usb_bus_type.rwsem);
-                               device_release_driver(&intf->dev);
-                               up_write(&usb_bus_type.rwsem);
-#endif
+                       if (is_active(intf)) {
+                               dev_dbg(&intf->dev, "nyet suspended\n");
+                               return -EBUSY;
                        }
                }
        }
 
-       /*
-        * FIXME this needs port power off call paths too, to help force
-        * USB into the "generic" PM model.  At least for devices on
-        * ports that aren't using ganged switching (usually root hubs).
-        *
-        * NOTE: SRP-capable links should adopt more aggressive poweroff
-        * policies (when HNP doesn't apply) once we have mechanisms to
-        * turn power back on!  (Likely not before 2.7...)
-        */
-       if (state.event > PM_EVENT_FREEZE) {
-               dev_warn(&udev->dev, "no poweroff yet, suspending instead\n");
-       }
-
        /* "global suspend" of the HC-to-USB interface (root hub), or
         * "selective suspend" of just one hub-device link.
         */
@@ -1639,14 +1649,13 @@ static int __usb_suspend_device (struct usb_device *udev, int port1,
                                udev);
 
        if (status == 0)
-               udev->dev.power.power_state = state;
+               udev->dev.power.power_state = PMSG_SUSPEND;
        return status;
 }
 
 /**
  * usb_suspend_device - suspend a usb device
  * @udev: device that's no longer in active use
- * @state: PMSG_SUSPEND to suspend
  * Context: must be able to sleep; device not locked
  *
  * Suspends a USB device that isn't in active use, conserving power.
@@ -1655,13 +1664,16 @@ static int __usb_suspend_device (struct usb_device *udev, int port1,
  * suspend by the host, using usb_resume_device().  It's also routine
  * to disconnect devices while they are suspended.
  *
+ * This only affects the USB hardware for a device; its interfaces
+ * (and, for hubs, child devices) must already have been suspended.
+ *
  * Suspending OTG devices may trigger HNP, if that's been enabled
  * between a pair of dual-role devices.  That will change roles, such
  * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral.
  *
  * Returns 0 on success, else negative errno.
  */
-int usb_suspend_device(struct usb_device *udev, pm_message_t state)
+int usb_suspend_device(struct usb_device *udev)
 {
        int     port1, status;
 
@@ -1669,12 +1681,15 @@ int usb_suspend_device(struct usb_device *udev, pm_message_t state)
        if (port1 < 0)
                return port1;
 
-       status = __usb_suspend_device(udev, port1, state);
+       status = __usb_suspend_device(udev, port1);
        usb_unlock_device(udev);
        return status;
 }
 
 /*
+ * If the USB "suspend" state is in use (rather than "global suspend"),
+ * many devices will be individually taken out of suspend state using
+ * special" resume" signaling.  These routines kick in shortly after
  * hardware resume signaling is finished, either because of selective
  * resume (by host) or remote wakeup (by device) ... now see what changed
  * in the tree that's rooted at this device.
@@ -1708,6 +1723,7 @@ static int finish_port_resume(struct usb_device *udev)
                        status);
        else if (udev->actconfig) {
                unsigned        i;
+               int             (*resume)(struct device *);
 
                le16_to_cpus(&devstatus);
                if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
@@ -1726,33 +1742,11 @@ static int finish_port_resume(struct usb_device *udev)
                }
 
                /* resume interface drivers; if this is a hub, it
-                * resumes the child devices
+                * may have a child resume event to deal with soon
                 */
-               for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
-                       struct usb_interface    *intf;
-                       struct usb_driver       *driver;
-
-                       intf = udev->actconfig->interface[i];
-                       if (intf->dev.power.power_state.event == PM_EVENT_ON)
-                               continue;
-                       if (!intf->dev.driver) {
-                               /* FIXME maybe force to alt 0 */
-                               continue;
-                       }
-                       driver = to_usb_driver(intf->dev.driver);
-
-                       /* bus_rescan_devices() may rebind drivers */
-                       if (!driver->resume)
-                               continue;
-
-                       /* can we do better than just logging errors? */
-                       status = driver->resume(intf);
-                       if (intf->dev.power.power_state.event != PM_EVENT_ON
-                                       || status)
-                               dev_dbg(&intf->dev,
-                                       "resume fail, state %d code %d\n",
-                                       intf->dev.power.power_state.event, status);
-               }
+               resume = udev->dev.bus->resume;
+               for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++)
+                       (void) resume(&udev->actconfig->interface[i]->dev);
                status = 0;
 
        } else if (udev->devnum <= 0) {
@@ -1898,32 +1892,25 @@ static int remote_wakeup(struct usb_device *udev)
        return status;
 }
 
-static int hub_suspend(struct usb_interface *intf, pm_message_t state)
+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;
-
-       /* stop khubd and related activity */
-       hub_quiesce(hub);
 
-       /* then suspend every port */
+       /* fail if children aren't already suspended */
        for (port1 = 1; port1 <= hdev->maxchild; port1++) {
                struct usb_device       *udev;
 
                udev = hdev->children [port1-1];
-               if (!udev)
-                       continue;
-               down(&udev->serialize);
-               status = __usb_suspend_device(udev, port1, state);
-               up(&udev->serialize);
-               if (status < 0)
-                       dev_dbg(&intf->dev, "suspend port %d --> %d\n",
-                               port1, status);
+               if (udev && udev->state != USB_STATE_SUSPENDED) {
+                       dev_dbg(&intf->dev, "port %d nyet suspended\n", port1);
+                       return -EBUSY;
+               }
        }
 
-       intf->dev.power.power_state = state;
+       /* stop khubd and related activity */
+       hub_quiesce(hub);
        return 0;
 }
 
@@ -1934,9 +1921,6 @@ static int hub_resume(struct usb_interface *intf)
        unsigned                port1;
        int                     status;
 
-       if (intf->dev.power.power_state.event == PM_EVENT_ON)
-               return 0;
-
        for (port1 = 1; port1 <= hdev->maxchild; port1++) {
                struct usb_device       *udev;
                u16                     portstat, portchange;
@@ -1970,8 +1954,6 @@ static int hub_resume(struct usb_interface *intf)
                }
                up(&udev->serialize);
        }
-       intf->dev.power.power_state = PMSG_ON;
-
        hub->resume_root_hub = 0;
        hub_activate(hub);
        return 0;
@@ -1987,13 +1969,15 @@ void usb_resume_root_hub(struct usb_device *hdev)
 
 #else  /* !CONFIG_USB_SUSPEND */
 
-int usb_suspend_device(struct usb_device *udev, pm_message_t state)
+int usb_suspend_device(struct usb_device *udev)
 {
+       /* state does NOT lie by saying it's USB_STATE_SUSPENDED! */
        return 0;
 }
 
 int usb_resume_device(struct usb_device *udev)
 {
+       udev->dev.power.power_state.event = PM_EVENT_ON;
        return 0;
 }