X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fcore%2Fhub.c;h=90b8d43c6b339eb233045dc8d550176ea67192f6;hb=9464c7cf61b9433057924c36e6e02f303a00e768;hp=26c8cb5f3e6722467379204762fb5d8a89b938a0;hpb=41689045f6a3cbe0550e1d34e9cc20d2e8c432ba;p=linux-2.6.git diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 26c8cb5f3..90b8d43c6 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -8,6 +8,7 @@ * */ +#include #include #include #include @@ -431,22 +432,15 @@ static void hub_power_on(struct usb_hub *hub) { int port1; unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2; - u16 wHubCharacteristics = - le16_to_cpu(hub->descriptor->wHubCharacteristics); - - /* Enable power on each port. Some hubs have reserved values - * of LPSM (> 2) in their descriptors, even though they are - * USB 2.0 hubs. Some hubs do not implement port-power switching - * but only emulate it. In all cases, the ports won't work - * unless we send these messages to the hub. - */ - if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2) + u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); + + /* if hub supports power switching, enable power on each port */ + if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2) { dev_dbg(hub->intfdev, "enabling power on all ports\n"); - else - dev_dbg(hub->intfdev, "trying to enable port power on " - "non-switchable hub\n"); - for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++) - set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER); + for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++) + set_port_feature(hub->hdev, port1, + USB_PORT_FEAT_POWER); + } /* Wait at least 100 msec for power to become stable */ msleep(max(pgood_delay, (unsigned) 100)); @@ -524,16 +518,15 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) /* caller has locked the hub device */ -static void hub_pre_reset(struct usb_interface *intf) +static void hub_pre_reset(struct usb_hub *hub, int disable_ports) { - struct usb_hub *hub = usb_get_intfdata(intf); 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 (hub->error == 0) + if (disable_ports) hub_port_disable(hub, port1, 0); } } @@ -541,10 +534,8 @@ static void hub_pre_reset(struct usb_interface *intf) } /* caller has locked the hub device */ -static void hub_post_reset(struct usb_interface *intf) +static void hub_post_reset(struct usb_hub *hub) { - struct usb_hub *hub = usb_get_intfdata(intf); - hub_activate(hub); hub_power_on(hub); } @@ -804,16 +795,15 @@ static void hub_disconnect(struct usb_interface *intf) struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev; - /* Disconnect all children and quiesce the hub */ - hub->error = 0; - hub_pre_reset(intf); - usb_set_intfdata (intf, NULL); hdev = hub->hdev; if (hdev->speed == USB_SPEED_HIGH) highspeed_hubs--; + /* Disconnect all children and quiesce the hub */ + hub_pre_reset(hub, 1); + usb_free_urb(hub->urb); hub->urb = NULL; @@ -1179,7 +1169,6 @@ static int choose_configuration(struct usb_device *udev) { int i; int num_configs; - int insufficient_power = 0; struct usb_host_config *c, *best; best = NULL; @@ -1232,10 +1221,8 @@ static int choose_configuration(struct usb_device *udev) */ /* Rule out configs that draw too much bus current */ - if (c->desc.bMaxPower * 2 > udev->bus_mA) { - insufficient_power++; + if (c->desc.bMaxPower * 2 > udev->bus_mA) continue; - } /* If the first config's first interface is COMM/2/0xff * (MSFT RNDIS), rule it out unless Linux has host-side @@ -1244,7 +1231,7 @@ static int choose_configuration(struct usb_device *udev) && desc->bInterfaceClass == USB_CLASS_COMM && desc->bInterfaceSubClass == 2 && desc->bInterfaceProtocol == 0xff) { -#ifndef CONFIG_USB_NET_RNDIS_HOST +#ifndef CONFIG_USB_NET_RNDIS continue; #else best = c; @@ -1269,11 +1256,6 @@ static int choose_configuration(struct usb_device *udev) best = c; } - if (insufficient_power > 0) - dev_info(&udev->dev, "rejected %d configuration%s " - "due to insufficient available bus power\n", - insufficient_power, plural(insufficient_power)); - if (best) { i = best->desc.bConfigurationValue; dev_info(&udev->dev, @@ -1790,10 +1772,7 @@ static int finish_device_resume(struct usb_device *udev) * and device drivers will know about any resume quirks. */ status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus); - if (status >= 0) - status = (status == 2 ? 0 : -ENODEV); - - if (status) + if (status < 2) dev_dbg(&udev->dev, "gone after usb resume? status %d\n", status); @@ -1882,12 +1861,7 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) dev_dbg(hub->intfdev, "port %d status %04x.%04x after resume, %d\n", port1, portchange, devstatus, status); - if (status >= 0) - status = -ENODEV; } else { - if (portchange & USB_PORT_STAT_C_SUSPEND) - clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_SUSPEND); /* TRSMRCY = 10 msec */ msleep(10); if (udev) @@ -2758,8 +2732,7 @@ static void hub_events(void) /* If the hub has died, clean up after it */ if (hdev->state == USB_STATE_NOTATTACHED) { - hub->error = -ENODEV; - hub_pre_reset(intf); + hub_pre_reset(hub, 0); goto loop; } @@ -2771,7 +2744,7 @@ static void hub_events(void) dev_dbg (hub_dev, "resetting for error %d\n", hub->error); - ret = usb_reset_composite_device(hdev, intf); + ret = usb_reset_device(hdev); if (ret) { dev_dbg (hub_dev, "error resetting hub: %d\n", ret); @@ -2940,8 +2913,6 @@ static struct usb_driver hub_driver = { .disconnect = hub_disconnect, .suspend = hub_suspend, .resume = hub_resume, - .pre_reset = hub_pre_reset, - .post_reset = hub_post_reset, .ioctl = hub_ioctl, .id_table = hub_id_table, }; @@ -3021,9 +2992,9 @@ static int config_descriptors_changed(struct usb_device *udev) * usb_reset_device - perform a USB port reset to reinitialize a device * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) * - * WARNING - don't use this routine to reset a composite device - * (one with multiple interfaces owned by separate drivers)! - * Use usb_reset_composite_device() instead. + * WARNING - don't reset any device unless drivers for all of its + * interfaces are expecting that reset! Maybe some driver->reset() + * method should eventually help ensure sufficient cooperation. * * Do a port reset, reassign the device's address, and establish its * former operating configuration. If the reset fails, or the device's @@ -3047,6 +3018,7 @@ int usb_reset_device(struct usb_device *udev) struct usb_device *parent_hdev = udev->parent; struct usb_hub *parent_hub; struct usb_device_descriptor descriptor = udev->descriptor; + struct usb_hub *hub = NULL; int i, ret = 0; int port1 = udev->portnum; @@ -3064,6 +3036,14 @@ int usb_reset_device(struct usb_device *udev) } parent_hub = hdev_to_hub(parent_hdev); + /* If we're resetting an active hub, take some special actions */ + if (udev->actconfig && udev->actconfig->desc.bNumInterfaces > 0 && + udev->actconfig->interface[0]->dev.driver == + &hub_driver.driver && + (hub = hdev_to_hub(udev)) != NULL) { + hub_pre_reset(hub, 0); + } + set_bit(port1, parent_hub->busy_bits); for (i = 0; i < SET_CONFIG_TRIES; ++i) { @@ -3122,87 +3102,11 @@ int usb_reset_device(struct usb_device *udev) } done: + if (hub) + hub_post_reset(hub); return 0; re_enumerate: hub_port_logical_disconnect(parent_hub, port1); return -ENODEV; } - -/** - * usb_reset_composite_device - warn interface drivers and perform a USB port reset - * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) - * @iface: interface bound to the driver making the request (optional) - * - * Warns all drivers bound to registered interfaces (using their pre_reset - * method), performs the port reset, and then lets the drivers know that - * the reset is over (using their post_reset method). - * - * Return value is the same as for usb_reset_device(). - * - * The caller must own the device lock. For example, it's safe to use - * this from a driver probe() routine after downloading new firmware. - * For calls that might not occur during probe(), drivers should lock - * the device using usb_lock_device_for_reset(). - * - * The interface locks are acquired during the pre_reset stage and released - * during the post_reset stage. However if iface is not NULL and is - * currently being probed, we assume that the caller already owns its - * lock. - */ -int usb_reset_composite_device(struct usb_device *udev, - struct usb_interface *iface) -{ - int ret; - struct usb_host_config *config = udev->actconfig; - - if (udev->state == USB_STATE_NOTATTACHED || - udev->state == USB_STATE_SUSPENDED) { - dev_dbg(&udev->dev, "device reset not allowed in state %d\n", - udev->state); - return -EINVAL; - } - - if (iface && iface->condition != USB_INTERFACE_BINDING) - iface = NULL; - - if (config) { - int i; - struct usb_interface *cintf; - struct usb_driver *drv; - - for (i = 0; i < config->desc.bNumInterfaces; ++i) { - cintf = config->interface[i]; - if (cintf != iface) - down(&cintf->dev.sem); - if (device_is_registered(&cintf->dev) && - cintf->dev.driver) { - drv = to_usb_driver(cintf->dev.driver); - if (drv->pre_reset) - (drv->pre_reset)(cintf); - } - } - } - - ret = usb_reset_device(udev); - - if (config) { - int i; - struct usb_interface *cintf; - struct usb_driver *drv; - - for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) { - cintf = config->interface[i]; - if (device_is_registered(&cintf->dev) && - cintf->dev.driver) { - drv = to_usb_driver(cintf->dev.driver); - if (drv->post_reset) - (drv->post_reset)(cintf); - } - if (cintf != iface) - up(&cintf->dev.sem); - } - } - - return ret; -}