aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/core/hub.c
diff options
context:
space:
mode:
authorDmitry Torokhov <dtor_core@ameritech.net>2006-01-05 22:25:13 -0500
committerDmitry Torokhov <dtor_core@ameritech.net>2006-01-05 22:25:13 -0500
commitfed8bf19ec20efc0641740b2d10aa589dbd6d1ab (patch)
treec104a13c7b8cb7f4152b15729dc5aede7c063e91 /drivers/usb/core/hub.c
parente7c3aad53dba54d375b632f2a21b680546828dec (diff)
parent46f25dffbaba48c571d75f5f574f31978287b8d2 (diff)
Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r--drivers/usb/core/hub.c486
1 files changed, 265 insertions, 221 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index f78bd124d29..650d5ee5871 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -32,7 +32,7 @@
#include "hub.h"
/* Protect struct usb_device->state and ->children members
- * Note: Both are also protected by ->serialize, except that ->state can
+ * Note: Both are also protected by ->dev.sem, except that ->state can
* change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
static DEFINE_SPINLOCK(device_state_lock);
@@ -515,6 +515,31 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
return ret;
}
+
+/* caller has locked the hub device */
+static void hub_pre_reset(struct usb_hub *hub, int disable_ports)
+{
+ struct usb_device *hdev = hub->hdev;
+ int port1;
+
+ for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
+ if (hdev->children[port1 - 1]) {
+ usb_disconnect(&hdev->children[port1 - 1]);
+ if (disable_ports)
+ hub_port_disable(hub, port1, 0);
+ }
+ }
+ hub_quiesce(hub);
+}
+
+/* caller has locked the hub device */
+static void hub_post_reset(struct usb_hub *hub)
+{
+ hub_activate(hub);
+ hub_power_on(hub);
+}
+
+
static int hub_configure(struct usb_hub *hub,
struct usb_endpoint_descriptor *endpoint)
{
@@ -677,26 +702,40 @@ static int hub_configure(struct usb_hub *hub,
* and battery-powered root hubs (may provide just 8 mA).
*/
ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);
- if (ret < 0) {
+ if (ret < 2) {
message = "can't get hub status";
goto fail;
}
le16_to_cpus(&hubstatus);
if (hdev == hdev->bus->root_hub) {
- struct usb_hcd *hcd =
- container_of(hdev->bus, struct usb_hcd, self);
-
- hub->power_budget = min(500u, hcd->power_budget) / 2;
+ if (hdev->bus_mA == 0 || hdev->bus_mA >= 500)
+ hub->mA_per_port = 500;
+ else {
+ hub->mA_per_port = hdev->bus_mA;
+ hub->limited_power = 1;
+ }
} else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",
hub->descriptor->bHubContrCurrent);
- hub->power_budget = (501 - hub->descriptor->bHubContrCurrent)
- / 2;
+ hub->limited_power = 1;
+ if (hdev->maxchild > 0) {
+ int remaining = hdev->bus_mA -
+ hub->descriptor->bHubContrCurrent;
+
+ if (remaining < hdev->maxchild * 100)
+ dev_warn(hub_dev,
+ "insufficient power available "
+ "to use all downstream ports\n");
+ hub->mA_per_port = 100; /* 7.2.1.1 */
+ }
+ } else { /* Self-powered external hub */
+ /* FIXME: What about battery-powered external hubs that
+ * provide less current per port? */
+ hub->mA_per_port = 500;
}
- if (hub->power_budget)
- dev_dbg(hub_dev, "%dmA bus power budget for children\n",
- hub->power_budget * 2);
-
+ if (hub->mA_per_port < 500)
+ dev_dbg(hub_dev, "%umA bus power budget for each child\n",
+ hub->mA_per_port);
ret = hub_hub_status(hub, &hubstatus, &hubchange);
if (ret < 0) {
@@ -750,29 +789,10 @@ 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;
usb_set_intfdata (intf, NULL);
hdev = hub->hdev;
@@ -780,7 +800,9 @@ static void hub_disconnect(struct usb_interface *intf)
if (hdev->speed == USB_SPEED_HIGH)
highspeed_hubs--;
- hub_quiesce(hub);
+ /* Disconnect all children and quiesce the hub */
+ hub_pre_reset(hub, 1);
+
usb_free_urb(hub->urb);
hub->urb = NULL;
@@ -800,27 +822,7 @@ static void hub_disconnect(struct usb_interface *intf)
hub->buffer = NULL;
}
- /* 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);
- }
+ kfree(hub);
}
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
@@ -917,26 +919,6 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
}
}
-/* caller has locked the hub device */
-static void hub_pre_reset(struct usb_hub *hub)
-{
- struct usb_device *hdev = hub->hdev;
- int i;
-
- for (i = 0; i < hdev->maxchild; ++i) {
- if (hdev->children[i])
- usb_disconnect(&hdev->children[i]);
- }
- hub_quiesce(hub);
-}
-
-/* caller has locked the hub device */
-static void hub_post_reset(struct usb_hub *hub)
-{
- hub_activate(hub);
- hub_power_on(hub);
-}
-
/* grab device/port lock, returning index of that port (zero based).
* protects the upstream link used by this device from concurrent
@@ -964,24 +946,21 @@ static int locktree(struct usb_device *udev)
t = locktree(hdev);
if (t < 0)
return t;
- for (t = 0; t < hdev->maxchild; t++) {
- if (hdev->children[t] == udev) {
- /* everything is fail-fast once disconnect
- * processing starts
- */
- if (udev->state == USB_STATE_NOTATTACHED)
- break;
- /* when everyone grabs locks top->bottom,
- * non-overlapping work may be concurrent
- */
- down(&udev->serialize);
- up(&hdev->serialize);
- return t + 1;
- }
+ /* everything is fail-fast once disconnect
+ * processing starts
+ */
+ if (udev->state == USB_STATE_NOTATTACHED) {
+ usb_unlock_device(hdev);
+ return -ENODEV;
}
+
+ /* when everyone grabs locks top->bottom,
+ * non-overlapping work may be concurrent
+ */
+ usb_lock_device(udev);
usb_unlock_device(hdev);
- return -ENODEV;
+ return udev->portnum;
}
static void recursively_mark_NOTATTACHED(struct usb_device *udev)
@@ -1039,6 +1018,39 @@ void usb_set_device_state(struct usb_device *udev,
EXPORT_SYMBOL(usb_set_device_state);
+#ifdef CONFIG_PM
+
+/**
+ * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
+ * @rhdev: struct usb_device for the root hub
+ *
+ * The USB host controller driver calls this function when its root hub
+ * is resumed and Vbus power has been interrupted or the controller
+ * has been reset. The routine marks all the children of the root hub
+ * as NOTATTACHED and marks logical connect-change events on their ports.
+ */
+void usb_root_hub_lost_power(struct usb_device *rhdev)
+{
+ struct usb_hub *hub;
+ int port1;
+ unsigned long flags;
+
+ dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
+ spin_lock_irqsave(&device_state_lock, flags);
+ hub = hdev_to_hub(rhdev);
+ for (port1 = 1; port1 <= rhdev->maxchild; ++port1) {
+ if (rhdev->children[port1 - 1]) {
+ recursively_mark_NOTATTACHED(
+ rhdev->children[port1 - 1]);
+ set_bit(port1, hub->change_bits);
+ }
+ }
+ spin_unlock_irqrestore(&device_state_lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
+
+#endif
+
static void choose_address(struct usb_device *udev)
{
int devnum;
@@ -1099,16 +1111,10 @@ void usb_disconnect(struct usb_device **pdev)
* this quiesces everyting except pending urbs.
*/
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
-
- /* lock the bus list on behalf of HCDs unregistering their root hubs */
- if (!udev->parent) {
- down(&usb_bus_list_lock);
- usb_lock_device(udev);
- } else
- down(&udev->serialize);
-
dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum);
+ usb_lock_device(udev);
+
/* Free up all the children before we remove this device */
for (i = 0; i < USB_MAXCHILDREN; i++) {
if (udev->children[i])
@@ -1136,54 +1142,112 @@ void usb_disconnect(struct usb_device **pdev)
*pdev = NULL;
spin_unlock_irq(&device_state_lock);
- if (!udev->parent) {
- usb_unlock_device(udev);
- up(&usb_bus_list_lock);
- } else
- up(&udev->serialize);
+ usb_unlock_device(udev);
device_unregister(&udev->dev);
}
+static inline const char *plural(int n)
+{
+ return (n == 1 ? "" : "s");
+}
+
static int choose_configuration(struct usb_device *udev)
{
- int c, i;
+ int i;
+ u16 devstatus;
+ int bus_powered;
+ int num_configs;
+ struct usb_host_config *c, *best;
+
+ /* If this fails, assume the device is bus-powered */
+ devstatus = 0;
+ usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
+ le16_to_cpus(&devstatus);
+ bus_powered = ((devstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0);
+ dev_dbg(&udev->dev, "device is %s-powered\n",
+ bus_powered ? "bus" : "self");
+
+ best = NULL;
+ c = udev->config;
+ num_configs = udev->descriptor.bNumConfigurations;
+ for (i = 0; i < num_configs; (i++, c++)) {
+ struct usb_interface_descriptor *desc =
+ &c->intf_cache[0]->altsetting->desc;
+
+ /*
+ * HP's USB bus-powered keyboard has only one configuration
+ * and it claims to be self-powered; other devices may have
+ * similar errors in their descriptors. If the next test
+ * were allowed to execute, such configurations would always
+ * be rejected and the devices would not work as expected.
+ */
+#if 0
+ /* Rule out self-powered configs for a bus-powered device */
+ if (bus_powered && (c->desc.bmAttributes &
+ USB_CONFIG_ATT_SELFPOWER))
+ continue;
+#endif
- /* NOTE: this should interact with hub power budgeting */
+ /*
+ * The next test may not be as effective as it should be.
+ * Some hubs have errors in their descriptor, claiming
+ * to be self-powered when they are really bus-powered.
+ * We will overestimate the amount of current such hubs
+ * make available for each port.
+ *
+ * This is a fairly benign sort of failure. It won't
+ * cause us to reject configurations that we should have
+ * accepted.
+ */
- c = udev->config[0].desc.bConfigurationValue;
- if (udev->descriptor.bNumConfigurations != 1) {
- for (i = 0; i < udev->descriptor.bNumConfigurations; i++) {
- struct usb_interface_descriptor *desc;
+ /* Rule out configs that draw too much bus current */
+ if (c->desc.bMaxPower * 2 > udev->bus_mA)
+ continue;
- /* heuristic: Linux is more likely to have class
- * drivers, so avoid vendor-specific interfaces.
- */
- desc = &udev->config[i].intf_cache[0]
- ->altsetting->desc;
- if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC)
- continue;
- /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS.
- * MSFT needs this to be the first config; never use
- * it as the default unless Linux has host-side RNDIS.
- * A second config would ideally be CDC-Ethernet, but
- * may instead be the "vendor specific" CDC subset
- * long used by ARM Linux for sa1100 or pxa255.
- */
- if (desc->bInterfaceClass == USB_CLASS_COMM
- && desc->bInterfaceSubClass == 2
- && desc->bInterfaceProtocol == 0xff) {
- c = udev->config[1].desc.bConfigurationValue;
- continue;
- }
- c = udev->config[i].desc.bConfigurationValue;
+ /* If the first config's first interface is COMM/2/0xff
+ * (MSFT RNDIS), rule it out unless Linux has host-side
+ * RNDIS support. */
+ if (i == 0 && desc->bInterfaceClass == USB_CLASS_COMM
+ && desc->bInterfaceSubClass == 2
+ && desc->bInterfaceProtocol == 0xff) {
+#ifndef CONFIG_USB_NET_RNDIS
+ continue;
+#else
+ best = c;
+#endif
+ }
+
+ /* From the remaining configs, choose the first one whose
+ * first interface is for a non-vendor-specific class.
+ * Reason: Linux is more likely to have a class driver
+ * than a vendor-specific driver. */
+ else if (udev->descriptor.bDeviceClass !=
+ USB_CLASS_VENDOR_SPEC &&
+ desc->bInterfaceClass !=
+ USB_CLASS_VENDOR_SPEC) {
+ best = c;
break;
}
+
+ /* If all the remaining configs are vendor-specific,
+ * choose the first one. */
+ else if (!best)
+ best = c;
+ }
+
+ if (best) {
+ i = best->desc.bConfigurationValue;
dev_info(&udev->dev,
- "configuration #%d chosen from %d choices\n",
- c, udev->descriptor.bNumConfigurations);
+ "configuration #%d chosen from %d choice%s\n",
+ i, num_configs, plural(num_configs));
+ } else {
+ i = -1;
+ dev_warn(&udev->dev,
+ "no configuration chosen from %d choice%s\n",
+ num_configs, plural(num_configs));
}
- return c;
+ return i;
}
#ifdef DEBUG
@@ -1210,8 +1274,8 @@ static inline void show_string(struct usb_device *udev, char *id, char *string)
*
* This is called with devices which have been enumerated, but not yet
* configured. The device descriptor is available, but not descriptors
- * for any device configuration. The caller must have locked udev and
- * either the parent hub (if udev is a normal device) or else the
+ * for any device configuration. The caller must have locked either
+ * the parent hub (if udev is a normal device) or else the
* usb_bus_list_lock (if udev is a root hub). The parent's pointer to
* udev has already been installed, but udev is not yet visible through
* sysfs or other filesystem code.
@@ -1221,8 +1285,7 @@ static inline void show_string(struct usb_device *udev, char *id, char *string)
*
* This call is synchronous, and may not be used in an interrupt context.
*
- * Only the hub driver should ever call this; root hub registration
- * uses it indirectly.
+ * Only the hub driver or root-hub registrar should ever call this.
*/
int usb_new_device(struct usb_device *udev)
{
@@ -1269,15 +1332,9 @@ int usb_new_device(struct usb_device *udev)
le16_to_cpu(udev->config[0].desc.wTotalLength),
USB_DT_OTG, (void **) &desc) == 0) {
if (desc->bmAttributes & USB_OTG_HNP) {
- unsigned port1;
+ unsigned port1 = udev->portnum;
struct usb_device *root = udev->parent;
- for (port1 = 1; port1 <= root->maxchild;
- port1++) {
- if (root->children[port1-1] == udev)
- break;
- }
-
dev_info(&udev->dev,
"Dual-Role OTG device on %sHNP port\n",
(port1 == bus->otg_port)
@@ -1331,27 +1388,27 @@ int usb_new_device(struct usb_device *udev)
}
usb_create_sysfs_dev_files (udev);
+ usb_lock_device(udev);
+
/* choose and set the configuration. that registers the interfaces
* with the driver core, and lets usb device drivers bind to them.
*/
c = choose_configuration(udev);
- if (c < 0)
- dev_warn(&udev->dev,
- "can't choose an initial configuration\n");
- else {
+ if (c >= 0) {
err = usb_set_configuration(udev, c);
if (err) {
dev_err(&udev->dev, "can't set config #%d, error %d\n",
c, err);
- usb_remove_sysfs_dev_files(udev);
- device_del(&udev->dev);
- goto fail;
+ /* This need not be fatal. The user can try to
+ * set other configurations. */
}
}
/* USB device state == configured ... usable */
usb_notify_add_device(udev);
+ usb_unlock_device(udev);
+
return 0;
fail:
@@ -1654,15 +1711,9 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
int usb_suspend_device(struct usb_device *udev)
{
#ifdef CONFIG_USB_SUSPEND
- int port1, status;
-
- port1 = locktree(udev);
- if (port1 < 0)
- return port1;
-
- status = __usb_suspend_device(udev, port1);
- usb_unlock_device(udev);
- return status;
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return -ENODEV;
+ return __usb_suspend_device(udev, udev->portnum);
#else
/* NOTE: udev->state unchanged, it's not lying ... */
udev->dev.power.power_state = PMSG_SUSPEND;
@@ -1694,13 +1745,14 @@ static int finish_device_resume(struct usb_device *udev)
usb_set_device_state(udev, udev->actconfig
? USB_STATE_CONFIGURED
: USB_STATE_ADDRESS);
+ udev->dev.power.power_state = PMSG_ON;
/* 10.5.4.5 says be sure devices in the tree are still there.
* For now let's assume the device didn't go crazy on resume,
* and device drivers will know about any resume quirks.
*/
status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
- if (status < 0)
+ if (status < 2)
dev_dbg(&udev->dev,
"gone after usb resume? status %d\n",
status);
@@ -1709,7 +1761,7 @@ static int finish_device_resume(struct usb_device *udev)
int (*resume)(struct device *);
le16_to_cpus(&devstatus);
- if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)
+ if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
&& udev->parent) {
status = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
@@ -1729,8 +1781,14 @@ static int finish_device_resume(struct usb_device *udev)
* may have a child resume event to deal with soon
*/
resume = udev->dev.bus->resume;
- for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++)
- (void) resume(&udev->actconfig->interface[i]->dev);
+ for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+ struct device *dev =
+ &udev->actconfig->interface[i]->dev;
+
+ down(&dev->sem);
+ (void) resume(dev);
+ up(&dev->sem);
+ }
status = 0;
} else if (udev->devnum <= 0) {
@@ -1813,11 +1871,10 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
*/
int usb_resume_device(struct usb_device *udev)
{
- int port1, status;
+ int status;
- port1 = locktree(udev);
- if (port1 < 0)
- return port1;
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return -ENODEV;
#ifdef CONFIG_USB_SUSPEND
/* selective resume of one downstream hub-to-device port */
@@ -1826,7 +1883,7 @@ int usb_resume_device(struct usb_device *udev)
// NOTE swsusp may bork us, device state being wrong...
// NOTE this fails if parent is also suspended...
status = hub_port_resume(hdev_to_hub(udev->parent),
- port1, udev);
+ udev->portnum, udev);
} else
status = 0;
} else
@@ -1836,13 +1893,11 @@ int usb_resume_device(struct usb_device *udev)
dev_dbg(&udev->dev, "can't resume, status %d\n",
status);
- usb_unlock_device(udev);
-
/* rebind drivers that had no suspend() */
if (status == 0) {
- usb_lock_all_devices();
+ usb_unlock_device(udev);
bus_rescan_devices(&usb_bus_type);
- usb_unlock_all_devices();
+ usb_lock_device(udev);
}
return status;
}
@@ -1856,14 +1911,14 @@ static int remote_wakeup(struct usb_device *udev)
/* don't repeat RESUME sequence if this device
* was already woken up by some other task
*/
- down(&udev->serialize);
+ usb_lock_device(udev);
if (udev->state == USB_STATE_SUSPENDED) {
dev_dbg(&udev->dev, "RESUME (wakeup)\n");
/* TRSMRCY = 10 msec */
msleep(10);
status = finish_device_resume(udev);
}
- up(&udev->serialize);
+ usb_unlock_device(udev);
#endif
return status;
}
@@ -1964,7 +2019,7 @@ static int hub_resume(struct usb_interface *intf)
if (!udev || status < 0)
continue;
- down (&udev->serialize);
+ usb_lock_device(udev);
if (portstat & USB_PORT_STAT_SUSPEND)
status = hub_port_resume(hub, port1, udev);
else {
@@ -1975,7 +2030,7 @@ static int hub_resume(struct usb_interface *intf)
hub_port_logical_disconnect(hub, port1);
}
}
- up(&udev->serialize);
+ usb_unlock_device(udev);
}
}
#endif
@@ -2359,39 +2414,36 @@ hub_power_remaining (struct usb_hub *hub)
{
struct usb_device *hdev = hub->hdev;
int remaining;
- unsigned i;
+ int port1;
- remaining = hub->power_budget;
- if (!remaining) /* self-powered */
+ if (!hub->limited_power)
return 0;
- for (i = 0; i < hdev->maxchild; i++) {
- struct usb_device *udev = hdev->children[i];
- int delta, ceiling;
+ remaining = hdev->bus_mA - hub->descriptor->bHubContrCurrent;
+ for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
+ struct usb_device *udev = hdev->children[port1 - 1];
+ int delta;
if (!udev)
continue;
- /* 100mA per-port ceiling, or 8mA for OTG ports */
- if (i != (udev->bus->otg_port - 1) || hdev->parent)
- ceiling = 50;
- else
- ceiling = 4;
-
+ /* Unconfigured devices may not use more than 100mA,
+ * or 8mA for OTG ports */
if (udev->actconfig)
- delta = udev->actconfig->desc.bMaxPower;
+ delta = udev->actconfig->desc.bMaxPower * 2;
+ else if (port1 != udev->bus->otg_port || hdev->parent)
+ delta = 100;
else
- delta = ceiling;
- // dev_dbg(&udev->dev, "budgeted %dmA\n", 2 * delta);
- if (delta > ceiling)
- dev_warn(&udev->dev, "%dmA over %dmA budget!\n",
- 2 * (delta - ceiling), 2 * ceiling);
+ delta = 8;
+ if (delta > hub->mA_per_port)
+ dev_warn(&udev->dev, "%dmA is over %umA budget "
+ "for port %d!\n",
+ delta, hub->mA_per_port, port1);
remaining -= delta;
}
if (remaining < 0) {
- dev_warn(hub->intfdev,
- "%dmA over power budget!\n",
- -2 * remaining);
+ dev_warn(hub->intfdev, "%dmA over power budget!\n",
+ - remaining);
remaining = 0;
}
return remaining;
@@ -2486,7 +2538,8 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
usb_set_device_state(udev, USB_STATE_POWERED);
udev->speed = USB_SPEED_UNKNOWN;
-
+ udev->bus_mA = hub->mA_per_port;
+
/* set the address */
choose_address(udev);
if (udev->devnum <= 0) {
@@ -2506,16 +2559,16 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
* on the parent.
*/
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
- && hub->power_budget) {
+ && udev->bus_mA <= 100) {
u16 devstat;
status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
&devstat);
- if (status < 0) {
+ if (status < 2) {
dev_dbg(&udev->dev, "get status %d ?\n", status);
goto loop_disable;
}
- cpu_to_le16s(&devstat);
+ le16_to_cpus(&devstat);
if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
dev_err(&udev->dev,
"can't connect bus-powered hub "
@@ -2540,7 +2593,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
* udev becomes globally accessible, although presumably
* no one will look at it until hdev is unlocked.
*/
- down (&udev->serialize);
status = 0;
/* We mustn't add new devices if the parent hub has
@@ -2564,15 +2616,12 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
}
}
- up (&udev->serialize);
if (status)
goto loop_disable;
status = hub_power_remaining(hub);
if (status)
- dev_dbg(hub_dev,
- "%dmA power budget left\n",
- 2 * status);
+ dev_dbg(hub_dev, "%dmA power budget left\n", status);
return;
@@ -2648,6 +2697,8 @@ static void hub_events(void)
if (i) {
dpm_runtime_resume(&hdev->dev);
dpm_runtime_resume(&intf->dev);
+ usb_put_intf(intf);
+ continue;
}
/* Lock the device, then check to see if we were
@@ -2661,7 +2712,7 @@ static void hub_events(void)
/* If the hub has died, clean up after it */
if (hdev->state == USB_STATE_NOTATTACHED) {
- hub_pre_reset(hub);
+ hub_pre_reset(hub, 0);
goto loop;
}
@@ -2784,6 +2835,11 @@ static void hub_events(void)
if (hubchange & HUB_CHANGE_LOCAL_POWER) {
dev_dbg (hub_dev, "power change\n");
clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
+ if (hubstatus & HUB_STATUS_LOCAL_POWER)
+ /* FIXME: Is this always true? */
+ hub->limited_power = 0;
+ else
+ hub->limited_power = 1;
}
if (hubchange & HUB_CHANGE_OVERCURRENT) {
dev_dbg (hub_dev, "overcurrent change\n");
@@ -2832,7 +2888,6 @@ static struct usb_device_id hub_id_table [] = {
MODULE_DEVICE_TABLE (usb, hub_id_table);
static struct usb_driver hub_driver = {
- .owner = THIS_MODULE,
.name = "hub",
.probe = hub_probe,
.disconnect = hub_disconnect,
@@ -2944,7 +2999,8 @@ int usb_reset_device(struct usb_device *udev)
struct usb_hub *parent_hub;
struct usb_device_descriptor descriptor = udev->descriptor;
struct usb_hub *hub = NULL;
- int i, ret = 0, port1 = -1;
+ int i, ret = 0;
+ int port1 = udev->portnum;
if (udev->state == USB_STATE_NOTATTACHED ||
udev->state == USB_STATE_SUSPENDED) {
@@ -2958,18 +3014,6 @@ int usb_reset_device(struct usb_device *udev)
dev_dbg(&udev->dev, "%s for root hub!\n", __FUNCTION__);
return -EISDIR;
}
-
- for (i = 0; i < parent_hdev->maxchild; i++)
- if (parent_hdev->children[i] == udev) {
- port1 = i + 1;
- break;
- }
-
- if (port1 < 0) {
- /* If this ever happens, it's very bad */
- dev_err(&udev->dev, "Can't locate device's port!\n");
- return -ENOENT;
- }
parent_hub = hdev_to_hub(parent_hdev);
/* If we're resetting an active hub, take some special actions */
@@ -2977,7 +3021,7 @@ int usb_reset_device(struct usb_device *udev)
udev->actconfig->interface[0]->dev.driver ==
&hub_driver.driver &&
(hub = hdev_to_hub(udev)) != NULL) {
- hub_pre_reset(hub);
+ hub_pre_reset(hub, 0);
}
set_bit(port1, parent_hub->busy_bits);