patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / drivers / usb / core / usb.c
index 0da70f7..3c2ddb5 100644 (file)
@@ -198,6 +198,9 @@ void usb_deregister(struct usb_driver *driver)
  * This routine helps device drivers avoid such mistakes.
  * However, you should make sure that you do the right thing with any
  * alternate settings available for this interfaces.
+ *
+ * Don't call this function unless you are bound to one of the interfaces
+ * on this device or you own the dev->serialize semaphore!
  */
 struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
 {
@@ -228,6 +231,9 @@ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
  * it would be incorrect to assume that the first altsetting entry in
  * the array corresponds to altsetting zero.  This routine helps device
  * drivers avoid such mistakes.
+ *
+ * Don't call this function unless you are bound to the intf interface
+ * or you own the device's ->serialize semaphore!
  */
 struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,
                unsigned int altnum)
@@ -992,12 +998,10 @@ void usb_disconnect(struct usb_device **pdev)
         */
        usb_disable_device(dev, 0);
 
-       dev_dbg (&dev->dev, "unregistering device\n");
        /* Free the device number and remove the /proc/bus/usb entry */
-       if (dev->devnum > 0) {
-               clear_bit(dev->devnum, dev->bus->devmap.devicemap);
-               usbfs_remove_device(dev);
-       }
+       dev_dbg (&dev->dev, "unregistering device\n");
+       usb_release_address(dev);
+       usbfs_remove_device(dev);
        up(&dev->serialize);
        device_unregister(&dev->dev);
 }
@@ -1032,24 +1036,23 @@ void usb_choose_address(struct usb_device *dev)
        }
 }
 
-
-// hub-only!! ... and only exported for reset/reinit path.
-// otherwise used internally, for usb_new_device()
-int usb_set_address(struct usb_device *dev)
+/**
+ * usb_release_address - deallocate device address (usbcore-internal)
+ * @dev: newly removed device
+ *
+ * Removes and deallocates the address assigned to a device.
+ * Only hub drivers (but not virtual root hub drivers for host
+ * controllers) should ever call this.
+ */
+void usb_release_address(struct usb_device *dev)
 {
-       int retval;
-
-       if (dev->devnum == 0)
-               return -EINVAL;
-       if (dev->state != USB_STATE_DEFAULT && dev->state != USB_STATE_ADDRESS)
-               return -EINVAL;
-       retval = usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS,
-               0, dev->devnum, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
-       if (retval == 0)
-               dev->state = USB_STATE_ADDRESS;
-       return retval;
+       if (dev->devnum > 0) {
+               clear_bit(dev->devnum, dev->bus->devmap.devicemap);
+               dev->devnum = -1;
+       }
 }
 
+
 static inline void usb_show_string(struct usb_device *dev, char *id, int index)
 {
        char *buf;
@@ -1063,92 +1066,59 @@ static inline void usb_show_string(struct usb_device *dev, char *id, int index)
        kfree(buf);
 }
 
+static int usb_choose_configuration(struct usb_device *dev)
+{
+       int c, i;
+
+       c = dev->config[0].desc.bConfigurationValue;
+       if (dev->descriptor.bNumConfigurations != 1) {
+               for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+                       struct usb_interface_descriptor *desc;
+
+                       /* heuristic:  Linux is more likely to have class
+                        * drivers, so avoid vendor-specific interfaces.
+                        */
+                       desc = &dev->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 */
+                       if (desc->bInterfaceClass == USB_CLASS_COMM
+                                       && desc->bInterfaceSubClass == 2
+                                       && desc->bInterfaceProtocol == 0xff)
+                               continue;
+                       c = dev->config[i].desc.bConfigurationValue;
+                       break;
+               }
+               dev_info(&dev->dev,
+                       "configuration #%d chosen from %d choices\n",
+                       c, dev->descriptor.bNumConfigurations);
+       }
+       return c;
+}
+
 /*
- * By the time we get here, we chose a new device address
- * and is in the default state. We need to identify the thing and
- * get the ball rolling..
+ * usb_new_device - perform initial device setup (usbcore-internal)
+ * @dev: newly addressed device (in ADDRESS state)
  *
- * Returns 0 for success, != 0 for error.
+ * 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 owns dev->serialize, and
+ * the device is not visible through sysfs or other filesystem code.
+ *
+ * Returns 0 for success (device is configured and listed, with its
+ * interfaces, in sysfs); else a negative errno value.  On error, one
+ * reference count to the device has been dropped.
  *
  * 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 only indirectly.
  */
-#define NEW_DEVICE_RETRYS      2
-#define SET_ADDRESS_RETRYS     2
 int usb_new_device(struct usb_device *dev)
 {
-       int err = -EINVAL;
-       int i;
-       int j;
-       int config;
-
-       /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
-        * it's fixed size except for full speed devices.
-        */
-       switch (dev->speed) {
-       case USB_SPEED_HIGH:            /* fixed at 64 */
-               i = 64;
-               break;
-       case USB_SPEED_FULL:            /* 8, 16, 32, or 64 */
-               /* to determine the ep0 maxpacket size, read the first 8
-                * bytes from the device descriptor to get bMaxPacketSize0;
-                * then correct our initial (small) guess.
-                */
-               // FALLTHROUGH
-       case USB_SPEED_LOW:             /* fixed at 8 */
-               i = 8;
-               break;
-       default:
-               goto fail;
-       }
-       dev->epmaxpacketin [0] = i;
-       dev->epmaxpacketout[0] = i;
-
-       for (i = 0; i < NEW_DEVICE_RETRYS; ++i) {
-
-               for (j = 0; j < SET_ADDRESS_RETRYS; ++j) {
-                       err = usb_set_address(dev);
-                       if (err >= 0)
-                               break;
-                       wait_ms(200);
-               }
-               if (err < 0) {
-                       dev_err(&dev->dev,
-                               "device not accepting address %d, error %d\n",
-                               dev->devnum, err);
-                       goto fail;
-               }
-
-               wait_ms(10);    /* Let the SET_ADDRESS settle */
-
-               /* high and low speed devices don't need this... */
-               err = usb_get_device_descriptor(dev, 8);
-               if (err >= 8)
-                       break;
-               wait_ms(100);
-       }
-
-       if (err < 8) {
-               dev_err(&dev->dev, "device descriptor read/8, error %d\n", err);
-               goto fail;
-       }
-       if (dev->speed == USB_SPEED_FULL) {
-               usb_disable_endpoint(dev, 0);
-               usb_endpoint_running(dev, 0, 1);
-               usb_endpoint_running(dev, 0, 0);
-               dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0;
-               dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
-       }
-
-       /* USB device state == addressed ... still not usable */
-
-       err = usb_get_device_descriptor(dev, sizeof(dev->descriptor));
-       if (err != (signed)sizeof(dev->descriptor)) {
-               dev_err(&dev->dev, "device descriptor read/all, error %d\n", err);
-               goto fail;
-       }
+       int err;
+       int c;
 
        err = usb_get_configuration(dev);
        if (err < 0) {
@@ -1170,51 +1140,22 @@ int usb_new_device(struct usb_device *dev)
                usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber);
 #endif
 
-       down(&dev->serialize);
-
        /* put device-specific files into sysfs */
        err = device_add (&dev->dev);
        if (err) {
                dev_err(&dev->dev, "can't device_add, error %d\n", err);
-               up(&dev->serialize);
                goto fail;
        }
-       usb_create_driverfs_dev_files (dev);
+       usb_create_sysfs_dev_files (dev);
 
        /* choose and set the configuration. that registers the interfaces
         * with the driver core, and lets usb device drivers bind to them.
         * NOTE:  should interact with hub power budgeting.
         */
-       config = dev->config[0].desc.bConfigurationValue;
-       if (dev->descriptor.bNumConfigurations != 1) {
-               for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
-                       struct usb_interface_descriptor *desc;
-
-                       /* heuristic:  Linux is more likely to have class
-                        * drivers, so avoid vendor-specific interfaces.
-                        */
-                       desc = &dev->config[i].interface[0]
-                                       ->altsetting->desc;
-                       if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC)
-                               continue;
-                       /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS */
-                       if (desc->bInterfaceClass == USB_CLASS_COMM
-                                       && desc->bInterfaceSubClass == 2
-                                       && desc->bInterfaceProtocol == 0xff)
-                               continue;
-                       config = dev->config[i].desc.bConfigurationValue;
-                       break;
-               }
-               dev_info(&dev->dev,
-                       "configuration #%d chosen from %d choices\n",
-                       config,
-                       dev->descriptor.bNumConfigurations);
-       }
-       err = usb_set_configuration(dev, config);
-       up(&dev->serialize);
+       c = usb_choose_configuration(dev);
+       err = usb_set_configuration(dev, c);
        if (err) {
-               dev_err(&dev->dev, "can't set config #%d, error %d\n",
-                       config, err);
+               dev_err(&dev->dev, "can't set config #%d, error %d\n", c, err);
                device_del(&dev->dev);
                goto fail;
        }
@@ -1226,9 +1167,9 @@ int usb_new_device(struct usb_device *dev)
 
        return 0;
 fail:
-       dev->state = USB_STATE_DEFAULT;
-       clear_bit(dev->devnum, dev->bus->devmap.devicemap);
-       dev->devnum = -1;
+       dev->state = USB_STATE_NOTATTACHED;
+       usb_release_address(dev);
+       usb_put_dev(dev);
        return err;
 }
 
@@ -1577,20 +1518,40 @@ int usb_disabled(void)
  */
 static int __init usb_init(void)
 {
+       int retval;
        if (nousb) {
                pr_info ("%s: USB support disabled\n", usbcore_name);
                return 0;
        }
 
-       bus_register(&usb_bus_type);
+       retval = bus_register(&usb_bus_type);
+       if (retval) 
+               goto out;
        usb_host_init();
-       usb_major_init();
-       usbfs_init();
-       usb_hub_init();
-
-       driver_register(&usb_generic_driver);
+       retval = usb_major_init();
+       if (retval)
+               goto major_init_failed;
+       retval = usbfs_init();
+       if (retval)
+               goto fs_init_failed;
+       retval = usb_hub_init();
+       if (retval)
+               goto hub_init_failed;
+
+       retval = driver_register(&usb_generic_driver);
+       if (!retval)
+               goto out;
 
-       return 0;
+       usb_hub_cleanup();
+hub_init_failed:
+       usbfs_cleanup();
+fs_init_failed:
+       usb_major_cleanup();    
+major_init_failed:
+       usb_host_cleanup();
+       bus_unregister(&usb_bus_type);
+out:
+       return retval;
 }
 
 /*