Martin:

I recently got some information about how Windows assigns device addresses 
and reads device descriptors.  Below is an experimental patch for 2.6.7; 
it changes the hub driver so that the second time it tries to assign the 
address it will use the Windows strategy.  Please try it out and post the 
USB debugging output you get.

Alan Stern


--- 2.6.7/drivers/usb/core/hub.c.orig   Fri Jul 23 15:46:02 2004
+++ 2.6.7/drivers/usb/core/hub.c        Fri Jul 23 15:48:54 2004
@@ -1342,6 +1342,7 @@
        int                     i, j, retval;
        unsigned                delay = HUB_SHORT_RESET_TIME;
        enum usb_device_speed   oldspeed = udev->speed;
+       int                     get_descriptor_first = 0;
 
        /* root hub ports have a slightly longer reset period
         * (from USB 2.0 spec, section 7.1.7.5)
@@ -1376,19 +1377,18 @@
                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.
+               /* to determine the ep0 maxpacket size, try to read
+                * the device descriptor to get bMaxPacketSize0 and
+                * then correct our initial guess.
                 */
-               // FALLTHROUGH
+               i = 64;
+               break;
        case USB_SPEED_LOW:             /* fixed at 8 */
                i = 8;
                break;
        default:
                goto fail;
        }
-       udev->epmaxpacketin [0] = i;
-       udev->epmaxpacketout[0] = i;
  
        dev_info (&udev->dev,
                        "%s %s speed USB device using address %d\n",
@@ -1413,17 +1413,60 @@
                udev->tt = &hub->tt;
                udev->ttport = port + 1;
        }
+
+       /* Clear out any stale HC state pertaining to device 0 endpoint 0 */
+       j = udev->devnum;
+       udev->devnum = 0;
+       usb_disable_endpoint(udev, 0 + USB_DIR_IN);
+       usb_disable_endpoint(udev, 0 + USB_DIR_OUT);
+       udev->devnum = j;
+       udev->epmaxpacketin [0] = i;
+       udev->epmaxpacketout[0] = i;
+       usb_endpoint_running(udev, 0, 1);
+       usb_endpoint_running(udev, 0, 0);
  
        /* 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.
+        * NOTE:  For full-speed devices, if the bMaxPacketSize0 field
+        * is initially set to 1 we will get the descriptor first,
+        * using a 64-byte transfer request.  This is what Windows does,
+        * so it may help with some non-standards-compliant devices.
         */
-       for (i = 0; i < GET_DESCRIPTOR_TRIES; ++i) {
+       if (udev->descriptor.bMaxPacketSize0 == 1)
+               get_descriptor_first = 1;
+
+       for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
+               if (get_descriptor_first) {
+                       struct usb_device_descriptor *buf;
+
+                       buf = kmalloc(64, GFP_NOIO);
+                       if (!buf) {
+                               retval = -ENOMEM;
+                               continue;
+                       }
+                       memset(buf, 0, 64);
+                       (void) usb_control_msg(udev,
+                                               /* Address 0 */
+                                       (PIPE_CONTROL << 30) | USB_DIR_IN,
+                                       USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+                                       USB_DT_DEVICE << 8, 0,
+                                       buf, 64,
+                                       HZ * USB_CTRL_GET_TIMEOUT);
+                       udev->descriptor = *buf;
+                       kfree(buf);
+
+                       retval = hub_port_reset(hdev, port, udev, delay);
+                       if (retval < 0)         /* error or disconnect */
+                               goto fail;
+                       if (udev->descriptor.bMaxPacketSize0 < 8) {
+                               retval = -EMSGSIZE;
+                               continue;
+                       }
+               }
+
                for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
                        retval = hub_set_address(udev);
                        if (retval >= 0)
@@ -1442,10 +1485,12 @@
                 *  - read ep0 maxpacket even for high and low speed,
                 */
                msleep(10);
-               retval = usb_get_device_descriptor(udev, 8);
+               if (get_descriptor_first)
+                       retval = 8;
+               else
+                       retval = usb_get_device_descriptor(udev, 8);
                if (retval >= 8)
                        break;
-               msleep(100);
        }
        if (retval != 8) {
                dev_err(&udev->dev, "device descriptor read/%s, error %d\n",
@@ -1619,6 +1664,7 @@
                }
 
                /* reset and get descriptor */
+               udev->descriptor.bMaxPacketSize0 = (i & 1);
                status = hub_port_init(hdev, udev, port);
                if (status < 0)
                        goto loop;




-------------------------------------------------------
This SF.Net email is sponsored by BEA Weblogic Workshop
FREE Java Enterprise J2EE developer tools!
Get your free copy of BEA WebLogic Workshop 8.1 today.
http://ads.osdn.com/?ad_id=4721&alloc_id=10040&op=click
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to