Please, I would feel much more comfortable accepting this if it was
split out into logical chunks.

OK here's a third chunk, depending on


 - moving more init logic to usb_alloc_dev (reset-1230.patch)
 - an unlocked usb_set_configuration (setconf.patch)

This patch

 * Moves the first half of the usb_new_device() logic into a
   new hub_port_init() routine, which will be shared between
   enumeration (khubd) and usb_reset_device().

* Khubd uses that new code in hub_port_connect_change().

* Now usb_new_device() cleans up better after faults.

 * Has related minor cleanups including commenting some of
   the curious request sequences coming from khubd.

Refactoring in that way gets rid of address0_sem deadlocks from
probe routines that download firmware need to reset the device,
or when a usb-storage device somehow triggers a bus reset during
that probe (I saw that once).

Please merge.

- Dave
--- 1.76/drivers/usb/core/hcd.c Sat Jan  3 16:18:59 2004
+++ edited/drivers/usb/core/hcd.c       Sun Jan  4 10:58:02 2004
@@ -750,6 +750,14 @@
        set_bit (devnum, usb_dev->bus->devmap.devicemap);
        usb_dev->state = USB_STATE_ADDRESS;
 
+       usb_dev->epmaxpacketin[0] = usb_dev->epmaxpacketout[0] = 64;
+       retval = usb_get_device_descriptor(usb_dev);
+       if (retval != sizeof usb_dev->descriptor) {
+               dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
+                               usb_dev->dev.bus_id, retval);
+               return (retval < 0) ? retval : -EMSGSIZE;
+       }
+
        retval = usb_new_device (usb_dev);
        if (retval)
                dev_err (parent_dev, "can't register root hub for %s, %d\n",
--- 1.84/drivers/usb/core/hub.c Sat Jan  3 16:18:59 2004
+++ edited/drivers/usb/core/hub.c       Sun Jan  4 10:58:02 2004
@@ -33,6 +33,8 @@
 
 #include "hcd.h"
 #include "hub.h"
+#include "usb.h"
+
 
 /* Wakes up khubd */
 static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED;
@@ -718,8 +720,11 @@
        return ret;
 }
 
-#define HUB_RESET_TRIES                5
-#define HUB_PROBE_TRIES                2
+#define PORT_RESET_TRIES               5
+#define SET_ADDRESS_TRIES      2
+#define GET_DESCRIPTOR_TRIES   2
+#define SET_CONFIG_TRIES       2
+
 #define HUB_ROOT_RESET_TIME    50      /* times are in msec */
 #define HUB_SHORT_RESET_TIME   10
 #define HUB_LONG_RESET_TIME    200
@@ -784,7 +789,7 @@
        int i, status;
 
        /* Reset the port */
-       for (i = 0; i < HUB_RESET_TRIES; i++) {
+       for (i = 0; i < PORT_RESET_TRIES; i++) {
                set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);
 
                /* return on disconnect or reset */
@@ -811,7 +816,7 @@
        return -1;
 }
 
-int hub_port_disable(struct usb_device *hub, int port)
+static int hub_port_disable(struct usb_device *hub, int port)
 {
        int ret;
 
@@ -880,40 +885,19 @@
        return ((portstatus&USB_PORT_STAT_CONNECTION)) ? 0 : 1;
 }
 
-static void hub_port_connect_change(struct usb_hub *hubstate, int port,
-                                       u16 portstatus, u16 portchange)
+/* reset device, (re)assign address, get device descriptor.
+ * device connection is stable, no more debouncing needed.
+ * returns device in USB_STATE_ADDRESS, except on error.
+ *
+ * caller owns dev->serialize for the device, guarding against
+ * config changes and disconnect processing.
+ */
+static int
+hub_port_init (struct usb_device *hub, struct usb_device *dev, int port)
 {
-       struct usb_device *hub = interface_to_usbdev(hubstate->intf);
-       struct usb_device *dev;
-       unsigned int delay = HUB_SHORT_RESET_TIME;
-       int i;
-
-       dev_dbg (&hubstate->intf->dev,
-               "port %d, status %x, change %x, %s\n",
-               port + 1, portstatus, portchange, portspeed (portstatus));
-
-       /* Clear the connection change status */
-       clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION);
-
-       /* Disconnect any existing devices under this port */
-       if (hub->children[port])
-               usb_disconnect(&hub->children[port]);
-
-       /* Return now if nothing is connected */
-       if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
-               if (portstatus & USB_PORT_STAT_ENABLE)
-                       hub_port_disable(hub, port);
-
-               return;
-       }
-
-       if (hub_port_debounce(hub, port)) {
-               dev_err (&hubstate->intf->dev,
-                       "connect-debounce failed, port %d disabled\n",
-                       port+1);
-               hub_port_disable(hub, port);
-               return;
-       }
+       int                     i, j, retval = -ENODEV;
+       unsigned                delay = HUB_SHORT_RESET_TIME;
+       enum usb_device_speed   oldspeed = dev->speed;
 
        /* root hub ports have a slightly longer reset period
         * (from USB 2.0 spec, section 7.1.7.5)
@@ -923,31 +907,47 @@
 
        /* Some low speed devices have problems with the quick delay, so */
        /*  be a bit pessimistic with those devices. RHbug #23670 */
-       if (portstatus & USB_PORT_STAT_LOW_SPEED)
+       if (oldspeed == USB_SPEED_LOW)
                delay = HUB_LONG_RESET_TIME;
 
        down(&usb_address0_sem);
 
-       for (i = 0; i < HUB_PROBE_TRIES; i++) {
-
-               /* Allocate a new device struct */
-               dev = usb_alloc_dev(hub, hub->bus, port);
-               if (!dev) {
-                       dev_err (&hubstate->intf->dev,
-                               "couldn't allocate usb_device\n");
-                       break;
-               }
-
-               dev->state = USB_STATE_POWERED;
-
-               /* Reset the device, and detect its speed */
-               if (hub_port_reset(hub, port, dev, delay)) {
-                       usb_put_dev(dev);
-                       break;
-               }
-
-               /* Find a new address for it */
+       /* Reset the device; full speed may morph to high speed */
+       retval = hub_port_reset(hub, port, dev, delay);
+       if (retval < 0)
+               goto fail;
+       if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != dev->speed) {
+               dev_dbg(&dev->dev, "device reset changed speed!\n");
+               goto fail;
+       }
+  
+       /* 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;
+ 
+       /* set the address */
+       if (dev->devnum <= 0) {
                usb_choose_address(dev);
+               if (dev->devnum <= 0)
+                       goto fail;
 
                /* Set up TT records, if needed  */
                if (hub->tt) {
@@ -955,12 +955,21 @@
                        dev->ttport = hub->ttport;
                } else if (dev->speed != USB_SPEED_HIGH
                                && hub->speed == USB_SPEED_HIGH) {
+                       struct usb_hub  *hubstate;
+ 
+                       hubstate = usb_get_intfdata (hub->actconfig
+                                                       ->interface[0]);
                        dev->tt = &hubstate->tt;
                        dev->ttport = port + 1;
                }
 
-               dev_info (&dev->dev,
-                       "new %s speed USB device using address %d\n",
+               /* force the right log message (below) at low speed */
+               oldspeed = USB_SPEED_UNKNOWN;
+       }
+ 
+       dev_info (&dev->dev,
+                       "%s %s speed USB device using address %d\n",
+                       (oldspeed == USB_SPEED_UNKNOWN) ? "new" : "reset",
                        ({ char *speed; switch (dev->speed) {
                        case USB_SPEED_LOW:     speed = "low";  break;
                        case USB_SPEED_FULL:    speed = "full"; break;
@@ -968,23 +977,149 @@
                        default:                speed = "?";    break;
                        }; speed;}),
                        dev->devnum);
-
-               /* Run it through the hoops (find a driver, etc) */
-               if (usb_new_device(dev) == 0) {
-                       hub->children[port] = dev;
-                       goto done;
+ 
+       /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
+        * Because device hardware and firmware is sometimes buggy in
+        * this area, and this is how Linux has done it for ages.
+        * Change it cautiously.
+        *
+        * NOTE:  Windows gets the descriptor first, seemingly to help
+        * work around device bugs like "can't use addresses with bit 3
+        * set in certain configurations".  Yes, really.
+        */
+       for (i = 0; i < GET_DESCRIPTOR_TRIES; ++i) {
+               for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
+                       retval = usb_set_address(dev);
+                       if (retval >= 0)
+                               break;
+                       wait_ms(200);
                }
-
-               /* Free the configuration if there was an error */
-               usb_put_dev(dev);
-
-               /* Switch to a long reset time */
-               delay = HUB_LONG_RESET_TIME;
+               if (retval < 0) {
+                       dev_err(&dev->dev,
+                               "device not accepting address %d, error %d\n",
+                               dev->devnum, retval);
+ fail:
+                       hub_port_disable(hub, port);
+                       clear_bit(dev->devnum, dev->bus->devmap.devicemap);
+                       dev->devnum = -1;
+                       up(&usb_address0_sem);
+                       return retval;
+               }
+ 
+               /* cope with hardware quirkiness:
+                *  - let SET_ADDRESS settle, some device hardware wants it
+                *  - read ep0 maxpacket even for high and low speed,
+                *
+                * FIXME assumes device->descriptor cacheline is dma-coherent
+                * and so does usb_get_device_descriptor
+                */
+               wait_ms(10);
+               retval = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
+                               &dev->descriptor, 8);
+               if (retval >= 8)
+                       break;
+               wait_ms(100);
+       }
+       if (retval != 8) {
+               dev_err(&dev->dev, "device descriptor read/%s, error %d\n",
+                               "8", retval);
+               if (retval >= 0)
+                       retval = -EMSGSIZE;
+               goto fail;
+       }
+       if (dev->speed == USB_SPEED_FULL
+                       && (dev->epmaxpacketin [0]
+                               != dev->descriptor.bMaxPacketSize0)) {
+               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;
+       }
+  
+       retval = usb_get_device_descriptor(dev);
+       if (retval < (signed)sizeof(dev->descriptor)) {
+               dev_err(&dev->dev, "device descriptor read/%s, error %d\n",
+                       "all", retval);
+               goto fail;
+       }
+  
+       up(&usb_address0_sem);
+       return 0;
+}
+ 
+static void hub_port_connect_change(struct usb_hub *hubstate, int port,
+                                       u16 portstatus, u16 portchange)
+{
+       struct usb_device *hub = interface_to_usbdev(hubstate->intf);
+       struct usb_device *dev;
+       int status, i;
+ 
+       dev_dbg (&hubstate->intf->dev,
+               "port %d, status %x, change %x, %s\n",
+               port + 1, portstatus, portchange, portspeed (portstatus));
+ 
+       /* Clear the connection change status */
+       clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION);
+ 
+       /* Disconnect any existing devices under this port */
+       if (hub->children[port])
+               usb_disconnect(&hub->children[port]);
+ 
+       /* Return now if nothing is connected */
+       if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
+               if (portstatus & USB_PORT_STAT_ENABLE)
+                       goto done;
+               return;
+       }
+  
+       if (hub_port_debounce(hub, port)) {
+               dev_err (&hubstate->intf->dev,
+                       "connect-debounce failed, port %d disabled\n",
+                       port+1);
+               goto done;
        }
+  
+       dev = usb_alloc_dev(hub, hub->bus, port);
+       if (!dev) {
+               dev_err (&hubstate->intf->dev,
+                       "couldn't allocate port %d usb_device\n", port+1);
+               goto done;
+       }
+       dev->state = USB_STATE_POWERED;
+       down (&dev->serialize);
+  
+       /* hub can tell if it's lowspeed already:  D- pullup (not D+) */
+       if (portstatus & USB_PORT_STAT_LOW_SPEED)
+               dev->speed = USB_SPEED_LOW;
+       else
+               dev->speed = USB_SPEED_UNKNOWN;
 
-       hub_port_disable(hub, port);
+ 
+       for (i = 0; i < SET_CONFIG_TRIES; i++) {
+               status = hub_port_init(hub, dev, port);
+               if (status < 0)
+                       continue;
+ 
+               /* Run it through the hoops (find a driver, etc) */
+               status = usb_new_device(dev);
+               if (status != 0)
+                       continue;
+
+               /* FIXME the device was visible in sysfs before now;
+                * this delay just created a brief funky state ...
+                */
+               hub->children[port] = dev;
+               up (&dev->serialize);
+               return;
+       }
+       up (&dev->serialize);
+ 
+       if (dev->devnum != -1)
+               clear_bit(dev->devnum, dev->bus->devmap.devicemap);
+       usb_put_dev(dev);
 done:
-       up(&usb_address0_sem);
+       hub_port_disable(hub, port);
 }
 
 static void hub_events(void)
--- 1.149/drivers/usb/core/usb.c        Sat Jan  3 16:18:59 2004
+++ edited/drivers/usb/core/usb.c       Sun Jan  4 10:58:02 2004
@@ -1011,93 +1011,31 @@
        return retval;
 }
 
+extern int __usb_set_configuration(struct usb_device *, int);
+
 /*
- * 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)
+ *
+ * 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, != 0 for error.
+ * Returns 0 for success (device is configured and listed, with its
+ * interfaces, in sysfs); else a negative errno value.
  *
  * 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 err;
        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_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 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);
-       if (err < (signed)sizeof(dev->descriptor)) {
-               dev_err(&dev->dev, "device descriptor read/all, error %d\n", err);
-               goto fail;
-       }
-
        err = usb_get_configuration(dev);
        if (err < 0) {
                dev_err(&dev->dev, "can't read configurations, error %d\n",
@@ -1148,7 +1086,7 @@
                        config,
                        dev->descriptor.bNumConfigurations);
        }
-       err = usb_set_configuration(dev, config);
+       err = __usb_set_configuration(dev, config);
        if (err) {
                dev_err(&dev->dev, "can't set config #%d, error %d\n",
                        config, err);
@@ -1163,9 +1101,7 @@
 
        return 0;
 fail:
-       dev->state = USB_STATE_DEFAULT;
-       clear_bit(dev->devnum, dev->bus->devmap.devicemap);
-       dev->devnum = -1;
+       usb_destroy_configuration (dev);
        return err;
 }
 

Reply via email to