Fix for USB sticks not working on ARM while using GCC 4.x compilers
[platform/kernel/u-boot.git] / common / usb.c
index a5b29a5..4d64ccb 100644 (file)
@@ -1,9 +1,19 @@
 /*
- * (C) Copyright 2001
- * Denis Peter, MPL AG Switzerland
  *
  * Most of this source has been derived from the Linux USB
- * project.
+ * project:
+ * (C) Copyright Linus Torvalds 1999
+ * (C) Copyright Johannes Erdfelt 1999-2001
+ * (C) Copyright Andreas Gal 1999
+ * (C) Copyright Gregory P. Smith 1999
+ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
+ * (C) Copyright Randy Dunlap 2000
+ * (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id)
+ * (C) Copyright Yggdrasil Computing, Inc. 2000
+ *     (usb_device_id matching changes by Adam J. Richter)
+ *
+ * Adapted for U-Boot:
+ * (C) Copyright 2001 Denis Peter, MPL AG Switzerland
  *
  * See file CREDITS for list of people who contributed to this
  * project.
@@ -25,7 +35,6 @@
  *
  */
 
-
 /*
  * How it works:
  *
 #include <common.h>
 #include <command.h>
 #include <asm/processor.h>
-
-#if (CONFIG_COMMANDS & CFG_CMD_USB)
+#include <linux/ctype.h>
+#include <asm/byteorder.h>
 
 #include <usb.h>
 #ifdef CONFIG_4xx
-#include <405gp_pci.h>
+#include <asm/4xx_pci.h>
 #endif
 
-
-
 #undef USB_DEBUG
 
 #ifdef USB_DEBUG
 #define USB_PRINTF(fmt,args...)
 #endif
 
+#define USB_BUFSIZ     512
+
 static struct usb_device usb_dev[USB_MAX_DEVICE];
 static int dev_index;
 static int running;
 static int asynch_allowed;
 static struct devrequest setup_packet;
 
+char usb_started; /* flag for the started/stopped USB status */
+
 /**********************************************************************
  * some forward declerations...
  */
@@ -70,6 +81,7 @@ void usb_scan_devices(void);
 int usb_hub_probe(struct usb_device *dev, int ifnum);
 void usb_hub_reset(void);
 
+
 /***********************************************************************
  * wait_ms
  */
@@ -99,10 +111,12 @@ int usb_init(void)
                printf("scanning bus for devices... ");
                running=1;
                usb_scan_devices();
+               usb_started = 1;
                return 0;
        }
        else {
                printf("Error, couldn't init Lowlevel part\n");
+               usb_started = 0;
                return -1;
        }
 }
@@ -112,9 +126,15 @@ int usb_init(void)
  */
 int usb_stop(void)
 {
-       asynch_allowed=1;
-       usb_hub_reset();
-       return usb_lowlevel_stop();
+       int res = 0;
+
+       if (usb_started) {
+               asynch_allowed = 1;
+               usb_started = 0;
+               usb_hub_reset();
+               res = usb_lowlevel_stop();
+       }
+       return res;
 }
 
 /*
@@ -157,13 +177,14 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe,
 {
        if((timeout==0)&&(!asynch_allowed)) /* request for a asynch control pipe is not allowed */
                return -1;
+
        /* set setup command */
        setup_packet.requesttype = requesttype;
        setup_packet.request = request;
-       setup_packet.value = swap_16(value);
-       setup_packet.index = swap_16(index);
-       setup_packet.length = swap_16(size);
-       USB_PRINTF("usb_control_msg: request: 0x%X, requesttype: 0x%X\nvalue 0x%X index 0x%X length 0x%X\n",
+       setup_packet.value = cpu_to_le16(value);
+       setup_packet.index = cpu_to_le16(index);
+       setup_packet.length = cpu_to_le16(size);
+       USB_PRINTF("usb_control_msg: request: 0x%X, requesttype: 0x%Xvalue 0x%X index 0x%X length 0x%X\n",
                request,requesttype,value,index,size);
        dev->status=USB_ST_NOT_PROC; /*not yet processed */
 
@@ -224,40 +245,59 @@ int usb_maxpacket(struct usb_device *dev,unsigned long pipe)
                return(dev->epmaxpacketin[((pipe>>15) & 0xf)]);
 }
 
+/* The routine usb_set_maxpacket_ep() is extracted from the loop of routine
+ * usb_set_maxpacket(), because the optimizer of GCC 4.x chokes on this routine
+ * when it is inlined in 1 single routine. What happens is that the register r3
+ * is used as loop-count 'i', but gets overwritten later on.
+ * This is clearly a compiler bug, but it is easier to workaround it here than
+ * to update the compiler (Occurs with at least several GCC 4.{1,2},x
+ * CodeSourcery compilers like e.g. 2007q3, 2008q1, 2008q3 lite editions on ARM)
+ */
+static void  __attribute__((noinline))
+usb_set_maxpacket_ep(struct usb_device *dev, struct usb_endpoint_descriptor *ep)
+{
+       int b;
+
+       b = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+
+       if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+                                               USB_ENDPOINT_XFER_CONTROL) {
+               /* Control => bidirectional */
+               dev->epmaxpacketout[b] = ep->wMaxPacketSize;
+               dev->epmaxpacketin [b] = ep->wMaxPacketSize;
+               USB_PRINTF("##Control EP epmaxpacketout/in[%d] = %d\n",
+                          b, dev->epmaxpacketin[b]);
+       } else {
+               if ((ep->bEndpointAddress & 0x80) == 0) {
+                       /* OUT Endpoint */
+                       if (ep->wMaxPacketSize > dev->epmaxpacketout[b]) {
+                               dev->epmaxpacketout[b] = ep->wMaxPacketSize;
+                               USB_PRINTF("##EP epmaxpacketout[%d] = %d\n",
+                                          b, dev->epmaxpacketout[b]);
+                       }
+               } else {
+                       /* IN Endpoint */
+                       if (ep->wMaxPacketSize > dev->epmaxpacketin[b]) {
+                               dev->epmaxpacketin[b] = ep->wMaxPacketSize;
+                               USB_PRINTF("##EP epmaxpacketin[%d] = %d\n",
+                                          b, dev->epmaxpacketin[b]);
+                       }
+               } /* if out */
+       } /* if control */
+}
+
 /*
  * set the max packed value of all endpoints in the given configuration
  */
 int usb_set_maxpacket(struct usb_device *dev)
 {
-       int i,ii,b;
-       struct usb_endpoint_descriptor *ep;
+       int i, ii;
 
-       for(i=0; i<dev->config.bNumInterfaces;i++) {
-               for(ii=0; ii<dev->config.if_desc[i].bNumEndpoints; ii++) {
-                       ep=&dev->config.if_desc[i].ep_desc[ii];
-                       b=ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+       for (i = 0; i < dev->config.bNumInterfaces; i++)
+               for (ii = 0; ii < dev->config.if_desc[i].bNumEndpoints; ii++)
+                       usb_set_maxpacket_ep(dev,
+                                         &dev->config.if_desc[i].ep_desc[ii]);
 
-                       if((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)==USB_ENDPOINT_XFER_CONTROL) {        /* Control => bidirectional */
-                               dev->epmaxpacketout[b] = ep->wMaxPacketSize;
-                               dev->epmaxpacketin [b] = ep->wMaxPacketSize;
-                               USB_PRINTF("##Control EP epmaxpacketout/in[%d] = %d\n",b,dev->epmaxpacketin[b]);
-                       }
-                       else {
-                               if ((ep->bEndpointAddress & 0x80)==0) { /* OUT Endpoint */
-                                       if(ep->wMaxPacketSize > dev->epmaxpacketout[b]) {
-                                               dev->epmaxpacketout[b] = ep->wMaxPacketSize;
-                                               USB_PRINTF("##EP epmaxpacketout[%d] = %d\n",b,dev->epmaxpacketout[b]);
-                                       }
-                               }
-                               else  { /* IN Endpoint */
-                                       if(ep->wMaxPacketSize > dev->epmaxpacketin[b]) {
-                                               dev->epmaxpacketin[b] = ep->wMaxPacketSize;
-                                               USB_PRINTF("##EP epmaxpacketin[%d] = %d\n",b,dev->epmaxpacketin[b]);
-                                       }
-                               } /* if out */
-                       } /* if control */
-               } /* for each endpoint */
-       }
        return 0;
 }
 
@@ -268,56 +308,67 @@ int usb_set_maxpacket(struct usb_device *dev)
 int usb_parse_config(struct usb_device *dev, unsigned char *buffer, int cfgno)
 {
        struct usb_descriptor_header *head;
-       int index,ifno,epno;
-       ifno=-1;
-       epno=-1;
-
-       dev->configno=cfgno;
-       head =(struct usb_descriptor_header *)&buffer[0];
-       if(head->bDescriptorType!=USB_DT_CONFIG) {
-               printf(" ERROR: NOT USB_CONFIG_DESC %x\n",head->bDescriptorType);
+       int index, ifno, epno, curr_if_num;
+       int i;
+       unsigned char *ch;
+
+       ifno = -1;
+       epno = -1;
+       curr_if_num = -1;
+
+       dev->configno = cfgno;
+       head = (struct usb_descriptor_header *) &buffer[0];
+       if(head->bDescriptorType != USB_DT_CONFIG) {
+               printf(" ERROR: NOT USB_CONFIG_DESC %x\n", head->bDescriptorType);
                return -1;
        }
-       memcpy(&dev->config,buffer,buffer[0]);
-       dev->config.wTotalLength=swap_16(dev->config.wTotalLength);
-       dev->config.no_of_if=0;
+       memcpy(&dev->config, buffer, buffer[0]);
+       le16_to_cpus(&(dev->config.wTotalLength));
+       dev->config.no_of_if = 0;
 
-       index=dev->config.bLength;
+       index = dev->config.bLength;
        /* Ok the first entry must be a configuration entry, now process the others */
-       head=(struct usb_descriptor_header *)&buffer[index];
-       while(index+1 < dev->config.wTotalLength) {
+       head = (struct usb_descriptor_header *) &buffer[index];
+       while(index + 1 < dev->config.wTotalLength) {
                switch(head->bDescriptorType) {
                        case USB_DT_INTERFACE:
-                               ifno=dev->config.no_of_if;
-                               dev->config.no_of_if++; /* found an interface desc, increase numbers */
-                               memcpy(&dev->config.if_desc[ifno],&buffer[index],buffer[index]); /* copy new desc */
-                               dev->config.if_desc[ifno].no_of_ep=0;
-
+                               if(((struct usb_interface_descriptor *) &buffer[index])->
+                                       bInterfaceNumber != curr_if_num) {
+                                       /* this is a new interface, copy new desc */
+                                       ifno = dev->config.no_of_if;
+                                       dev->config.no_of_if++;
+                                       memcpy(&dev->config.if_desc[ifno],
+                                               &buffer[index], buffer[index]);
+                                       dev->config.if_desc[ifno].no_of_ep = 0;
+                                       dev->config.if_desc[ifno].num_altsetting = 1;
+                                       curr_if_num = dev->config.if_desc[ifno].bInterfaceNumber;
+                               } else {
+                                       /* found alternate setting for the interface */
+                                       dev->config.if_desc[ifno].num_altsetting++;
+                               }
                                break;
                        case USB_DT_ENDPOINT:
-                               epno=dev->config.if_desc[ifno].no_of_ep;
+                               epno = dev->config.if_desc[ifno].no_of_ep;
                                dev->config.if_desc[ifno].no_of_ep++; /* found an endpoint */
-                               memcpy(&dev->config.if_desc[ifno].ep_desc[epno],&buffer[index],buffer[index]);
-                               dev->config.if_desc[ifno].ep_desc[epno].wMaxPacketSize
-                                       =swap_16(dev->config.if_desc[ifno].ep_desc[epno].wMaxPacketSize);
-                               USB_PRINTF("if %d, ep %d\n",ifno,epno);
+                               memcpy(&dev->config.if_desc[ifno].ep_desc[epno],
+                                       &buffer[index], buffer[index]);
+                               le16_to_cpus(&(dev->config.if_desc[ifno].ep_desc[epno].wMaxPacketSize));
+                               USB_PRINTF("if %d, ep %d\n", ifno, epno);
                                break;
                        default:
-                               if(head->bLength==0)
+                               if(head->bLength == 0)
                                        return 1;
-                               USB_PRINTF("unknown Description Type : %x\n",head->bDescriptorType);
+                               USB_PRINTF("unknown Description Type : %x\n", head->bDescriptorType);
                                {
-                                       int i;
-                                       unsigned char *ch;
-                                       ch=(unsigned char *)head;
-                                       for(i=0;i<head->bLength; i++)
-                                               USB_PRINTF("%02X ",*ch++);
+                                       ch = (unsigned char *)head;
+                                       for(i = 0; i < head->bLength; i++)
+                                               USB_PRINTF("%02X ", *ch++);
                                        USB_PRINTF("\n\n\n");
                                }
                                break;
                }
-               index+=head->bLength;
-               head=(struct usb_descriptor_header *)&buffer[index];
+               index += head->bLength;
+               head = (struct usb_descriptor_header *)&buffer[index];
        }
        return 1;
 }
@@ -330,8 +381,7 @@ int usb_parse_config(struct usb_device *dev, unsigned char *buffer, int cfgno)
 int usb_clear_halt(struct usb_device *dev, int pipe)
 {
        int result;
-       unsigned short status;
-       int endp=usb_pipeendpoint(pipe)|(usb_pipein(pipe)<<7);
+       int endp = usb_pipeendpoint(pipe)|(usb_pipein(pipe)<<7);
 
        result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, endp, NULL, 0, USB_CNTL_TIMEOUT * 3);
@@ -339,15 +389,14 @@ int usb_clear_halt(struct usb_device *dev, int pipe)
        /* don't clear if failed */
        if (result < 0)
                return result;
-       result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
-               USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_ENDPOINT, 0, endp,
-               &status, sizeof(status), USB_CNTL_TIMEOUT * 3);
-       if (result < 0)
-               return result;
-       USB_PRINTF("usb_clear_halt: status 0x%x\n",status);
-       if (status & 1)
-               return -1;              /* still halted */
+
+       /*
+        * NOTE: we do not get status and verify reset was successful
+        * as some devices are reported to lock up upon this check..
+        */
+
        usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
+
        /* toggle is reset on clear */
        usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0);
        return 0;
@@ -360,7 +409,7 @@ int usb_clear_halt(struct usb_device *dev, int pipe)
 int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)
 {
        int res;
-       res = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+       res = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
                        USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
                        (type << 8) + index, 0,
                        buf, size, USB_CNTL_TIMEOUT);
@@ -372,7 +421,7 @@ int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char
  */
 int usb_get_configuration_no(struct usb_device *dev,unsigned char *buffer,int cfgno)
 {
-       int result;
+       int result;
        unsigned int tmp;
        struct usb_config_descriptor *config;
 
@@ -386,7 +435,13 @@ int usb_get_configuration_no(struct usb_device *dev,unsigned char *buffer,int cf
                        printf("config descriptor too short (expected %i, got %i)\n",8,result);
                return -1;
        }
-       tmp=swap_16(config->wTotalLength);
+       tmp = le16_to_cpu(config->wTotalLength);
+
+       if (tmp > USB_BUFSIZ) {
+               USB_PRINTF("usb_get_configuration_no: failed to get descriptor - too long: %d\n",
+                       tmp);
+               return -1;
+       }
 
        result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, tmp);
        USB_PRINTF("get_conf_no %d Result %d, wLength %d\n",cfgno,result,tmp);
@@ -417,7 +472,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
        struct usb_interface_descriptor *if_face = NULL;
        int ret, i;
 
-       for (i=0; i<dev->config.bNumInterfaces; i++) {
+       for (i = 0; i < dev->config.bNumInterfaces; i++) {
                if (dev->config.if_desc[i].bInterfaceNumber == interface) {
                        if_face = &dev->config.if_desc[i];
                        break;
@@ -427,14 +482,20 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
                printf("selecting invalid interface %d", interface);
                return -1;
        }
+       /*
+        * We should return now for devices with only one alternate setting.
+        * According to 9.4.10 of the Universal Serial Bus Specification Revision 2.0
+        * such devices can return with a STALL. This results in some USB sticks
+        * timeouting during initialization and then being unusable in U-Boot.
+        */
+       if (if_face->num_altsetting == 1)
+               return 0;
 
        if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
            USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, alternate,
            interface, NULL, 0, USB_CNTL_TIMEOUT * 5)) < 0)
                return ret;
 
-       if_face->act_altsetting = (unsigned char)alternate;
-       usb_set_maxpacket(dev);
        return 0;
 }
 
@@ -505,11 +566,74 @@ int usb_get_class_descriptor(struct usb_device *dev, int ifnum,
  */
 int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size)
 {
-       return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
-               USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
-               (USB_DT_STRING << 8) + index, langid, buf, size, USB_CNTL_TIMEOUT);
+       int i;
+       int result;
+
+       for (i = 0; i < 3; ++i) {
+               /* some devices are flaky */
+               result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                       USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+                       (USB_DT_STRING << 8) + index, langid, buf, size,
+                       USB_CNTL_TIMEOUT);
+
+               if (result > 0)
+                       break;
+       }
+
+       return result;
+}
+
+
+static void usb_try_string_workarounds(unsigned char *buf, int *length)
+{
+       int newlength, oldlength = *length;
+
+       for (newlength = 2; newlength + 1 < oldlength; newlength += 2)
+               if (!isprint(buf[newlength]) || buf[newlength + 1])
+                       break;
+
+       if (newlength > 2) {
+               buf[0] = newlength;
+               *length = newlength;
+       }
+}
+
+
+static int usb_string_sub(struct usb_device *dev, unsigned int langid,
+               unsigned int index, unsigned char *buf)
+{
+       int rc;
+
+       /* Try to read the string descriptor by asking for the maximum
+        * possible number of bytes */
+       rc = usb_get_string(dev, langid, index, buf, 255);
+
+       /* If that failed try to read the descriptor length, then
+        * ask for just that many bytes */
+       if (rc < 2) {
+               rc = usb_get_string(dev, langid, index, buf, 2);
+               if (rc == 2)
+                       rc = usb_get_string(dev, langid, index, buf, buf[0]);
+       }
+
+       if (rc >= 2) {
+               if (!buf[0] && !buf[1])
+                       usb_try_string_workarounds(buf, &rc);
+
+               /* There might be extra junk at the end of the descriptor */
+               if (buf[0] < rc)
+                       rc = buf[0];
+
+               rc = rc - (rc & 1); /* force a multiple of two */
+       }
+
+       if (rc < 2)
+               rc = -1;
+
+       return rc;
 }
 
+
 /********************************************************************
  * usb_string:
  * Get string index and translate it to ascii.
@@ -517,8 +641,7 @@ int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char
  */
 int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
 {
-
-       unsigned char mybuf[256];
+       unsigned char mybuf[USB_BUFSIZ];
        unsigned char *tbuf;
        int err;
        unsigned int u, idx;
@@ -526,11 +649,11 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
        if (size <= 0 || !buf || !index)
                return -1;
        buf[0] = 0;
-       tbuf=&mybuf[0];
+       tbuf = &mybuf[0];
 
        /* get langid for strings if it's not yet known */
        if (!dev->have_langid) {
-               err = usb_get_string(dev, 0, 0, tbuf, 4);
+               err = usb_string_sub(dev, 0, 0, tbuf);
                if (err < 0) {
                        USB_PRINTF("error getting string descriptor 0 (error=%x)\n",dev->status);
                        return -1;
@@ -545,16 +668,11 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
                                dev->devnum, dev->string_langid);
                }
        }
-       /* Just ask for a maximum length string and then take the length
-        * that was returned. */
-       err = usb_get_string(dev, dev->string_langid, index, tbuf, 4);
-       if (err < 0)
-               return err;
-       u=tbuf[0];
-       USB_PRINTF("Strn Len %d, index %d\n",u,index);
-       err = usb_get_string(dev, dev->string_langid, index, tbuf, u);
+
+       err = usb_string_sub(dev, dev->string_langid, index, tbuf);
        if (err < 0)
                return err;
+
        size--;         /* leave room for trailing NULL char in output buffer */
        for (idx = 0, u = 2; u < err; u += 2) {
                if (idx >= size)
@@ -596,7 +714,7 @@ struct usb_device * usb_alloc_new_device(void)
        int i;
        USB_PRINTF("New Device %d\n",dev_index);
        if(dev_index==USB_MAX_DEVICE) {
-               printf("ERROR, to many USB Devices max=%d\n",USB_MAX_DEVICE);
+               printf("ERROR, too many USB Devices, max=%d\n",USB_MAX_DEVICE);
                return NULL;
        }
        usb_dev[dev_index].devnum=dev_index+1; /* default Address is 0, real addresses start with 1 */
@@ -620,7 +738,7 @@ int usb_new_device(struct usb_device *dev)
 {
        int addr, err;
        int tmp;
-       unsigned char tmpbuf[256];
+       unsigned char tmpbuf[USB_BUFSIZ];
 
        dev->descriptor.bMaxPacketSize0 = 8;  /* Start off at 8 bytes  */
        dev->maxpacketsize = 0;         /* Default to 8 byte max packet size */
@@ -630,11 +748,66 @@ int usb_new_device(struct usb_device *dev)
        /* We still haven't set the Address yet */
        addr = dev->devnum;
        dev->devnum = 0;
+
+#undef NEW_INIT_SEQ
+#ifdef NEW_INIT_SEQ
+       /* this is a Windows scheme of initialization sequence, with double
+        * reset of the device. Some equipment is said to work only with such
+        * init sequence; this patch is based on the work by Alan Stern:
+        * http://sourceforge.net/mailarchive/forum.php?thread_id=5729457&forum_id=5398
+        */
+       int j;
+       struct usb_device_descriptor *desc;
+       int port = -1;
+       struct usb_device *parent = dev->parent;
+       unsigned short portstatus;
+
+       /* send 64-byte GET-DEVICE-DESCRIPTOR request.  Since the descriptor is
+        * only 18 bytes long, this will terminate with a short packet.  But if
+        * the maxpacket size is 8 or 16 the device may be waiting to transmit
+        * some more. */
+
+       desc = (struct usb_device_descriptor *)tmpbuf;
+       desc->bMaxPacketSize0 = 0;
+       for (j = 0; j < 3; ++j) {
+               err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, 64);
+               if (err < 0) {
+                       USB_PRINTF("usb_new_device: 64 byte descr\n");
+                       break;
+               }
+       }
+       dev->descriptor.bMaxPacketSize0 = desc->bMaxPacketSize0;
+
+       /* find the port number we're at */
+       if (parent) {
+
+               for (j = 0; j < parent->maxchild; j++) {
+                       if (parent->children[j] == dev) {
+                               port = j;
+                               break;
+                       }
+               }
+               if (port < 0) {
+                       printf("usb_new_device: cannot locate device's port..\n");
+                       return 1;
+               }
+
+               /* reset the port for the second time */
+               err = hub_port_reset(dev->parent, port, &portstatus);
+               if (err < 0) {
+                       printf("\n     Couldn't reset port %i\n", port);
+                       return 1;
+               }
+       }
+#else
+       /* and this is the old and known way of initializing devices */
        err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8);
        if (err < 8) {
                printf("\n      USB device not responding, giving up (status=%lX)\n",dev->status);
                return 1;
        }
+#endif
+
        dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0;
        dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
        switch (dev->descriptor.bMaxPacketSize0) {
@@ -665,10 +838,10 @@ int usb_new_device(struct usb_device *dev)
                return 1;
        }
        /* correct le values */
-       dev->descriptor.bcdUSB=swap_16(dev->descriptor.bcdUSB);
-       dev->descriptor.idVendor=swap_16(dev->descriptor.idVendor);
-       dev->descriptor.idProduct=swap_16(dev->descriptor.idProduct);
-       dev->descriptor.bcdDevice=swap_16(dev->descriptor.bcdDevice);
+       le16_to_cpus(&dev->descriptor.bcdUSB);
+       le16_to_cpus(&dev->descriptor.idVendor);
+       le16_to_cpus(&dev->descriptor.idProduct);
+       le16_to_cpus(&dev->descriptor.bcdDevice);
        /* only support for one config for now */
        usb_get_configuration_no(dev,&tmpbuf[0],0);
        usb_parse_config(dev,&tmpbuf[0],0);
@@ -706,13 +879,13 @@ void usb_scan_devices(void)
        /* first make all devices unknown */
        for(i=0;i<USB_MAX_DEVICE;i++) {
                memset(&usb_dev[i],0,sizeof(struct usb_device));
-               usb_dev[i].devnum=-1;
+               usb_dev[i].devnum = -1;
        }
        dev_index=0;
        /* device 0 is always present (root hub, so let it analyze) */
        dev=usb_alloc_new_device();
        usb_new_device(dev);
-       printf("%d USB Devices found\n",dev_index);
+       printf("%d USB Device(s) found\n",dev_index);
        /* insert "driver" if possible */
 #ifdef CONFIG_USB_KEYBOARD
        drv_usb_kbd_init();
@@ -810,39 +983,15 @@ struct usb_hub_device *usb_hub_allocate(void)
 
 #define MAX_TRIES 5
 
-void usb_hub_port_connect_change(struct usb_device *dev, int port)
+static int hub_port_reset(struct usb_device *dev, int port,
+                       unsigned short *portstat)
 {
-       struct usb_device *usb;
+       int tries;
        struct usb_port_status portsts;
        unsigned short portstatus, portchange;
-       int tries;
-
-       /* Check status */
-       if (usb_get_port_status(dev, port + 1, &portsts)<0) {
-               USB_HUB_PRINTF("get_port_status failed\n");
-               return;
-       }
-
-       portstatus = swap_16(portsts.wPortStatus);
-       portchange = swap_16(portsts.wPortChange);
-       USB_HUB_PRINTF("portstatus %x, change %x, %s\n", portstatus, portchange,
-               portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low Speed" : "High Speed");
-
-       /* Clear the connection change status */
-       usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION);
-
-       /* Disconnect any existing devices under this port */
-       if (((!(portstatus & USB_PORT_STAT_CONNECTION)) &&
-            (!(portstatus & USB_PORT_STAT_ENABLE)))|| (dev->children[port])) {
-               USB_HUB_PRINTF("usb_disconnect(&hub->children[port]);\n");
-               /* Return now if nothing is connected */
-               if (!(portstatus & USB_PORT_STAT_CONNECTION))
-                       return;
-       }
-       wait_ms(200);
 
-       /* Reset the port */
 
+       USB_HUB_PRINTF("hub_port_reset: resetting port %d...\n", port);
        for(tries=0;tries<MAX_TRIES;tries++) {
 
                usb_set_port_feature(dev, port + 1, USB_PORT_FEAT_RESET);
@@ -850,10 +999,10 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port)
 
                if (usb_get_port_status(dev, port + 1, &portsts)<0) {
                        USB_HUB_PRINTF("get_port_status failed status %lX\n",dev->status);
-                       return;
+                       return -1;
                }
-               portstatus = swap_16(portsts.wPortStatus);
-               portchange = swap_16(portsts.wPortChange);
+               portstatus = le16_to_cpu(portsts.wPortStatus);
+               portchange = le16_to_cpu(portsts.wPortChange);
                USB_HUB_PRINTF("portstatus %x, change %x, %s\n", portstatus ,portchange,
                        portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low Speed" : "High Speed");
                USB_HUB_PRINTF("STAT_C_CONNECTION = %d STAT_CONNECTION = %d  USB_PORT_STAT_ENABLE %d\n",
@@ -862,10 +1011,12 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port)
                        (portstatus & USB_PORT_STAT_ENABLE) ? 1 : 0);
                if ((portchange & USB_PORT_STAT_C_CONNECTION) ||
                    !(portstatus & USB_PORT_STAT_CONNECTION))
-                       return;
+                       return -1;
+
+               if (portstatus & USB_PORT_STAT_ENABLE) {
 
-               if (portstatus & USB_PORT_STAT_ENABLE)
                        break;
+               }
 
                wait_ms(200);
        }
@@ -873,10 +1024,52 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port)
        if (tries==MAX_TRIES) {
                USB_HUB_PRINTF("Cannot enable port %i after %i retries, disabling port.\n", port+1, MAX_TRIES);
                USB_HUB_PRINTF("Maybe the USB cable is bad?\n");
-               return;
+               return -1;
        }
 
        usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_RESET);
+       *portstat = portstatus;
+       return 0;
+
+}
+
+
+void usb_hub_port_connect_change(struct usb_device *dev, int port)
+{
+       struct usb_device *usb;
+       struct usb_port_status portsts;
+       unsigned short portstatus, portchange;
+
+       /* Check status */
+       if (usb_get_port_status(dev, port + 1, &portsts)<0) {
+               USB_HUB_PRINTF("get_port_status failed\n");
+               return;
+       }
+
+       portstatus = le16_to_cpu(portsts.wPortStatus);
+       portchange = le16_to_cpu(portsts.wPortChange);
+       USB_HUB_PRINTF("portstatus %x, change %x, %s\n", portstatus, portchange,
+               portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low Speed" : "High Speed");
+
+       /* Clear the connection change status */
+       usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION);
+
+       /* Disconnect any existing devices under this port */
+       if (((!(portstatus & USB_PORT_STAT_CONNECTION)) &&
+            (!(portstatus & USB_PORT_STAT_ENABLE)))|| (dev->children[port])) {
+               USB_HUB_PRINTF("usb_disconnect(&hub->children[port]);\n");
+               /* Return now if nothing is connected */
+               if (!(portstatus & USB_PORT_STAT_CONNECTION))
+                       return;
+       }
+       wait_ms(200);
+
+       /* Reset the port */
+       if (hub_port_reset(dev, port, &portstatus) < 0) {
+               printf("cannot reset port %i!?\n", port + 1);
+               return;
+       }
+
        wait_ms(200);
 
        /* Allocate a new device struct for it */
@@ -896,7 +1089,7 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port)
 
 int usb_hub_configure(struct usb_device *dev)
 {
-       unsigned char buffer[256], *bitmap;
+       unsigned char buffer[USB_BUFSIZ], *bitmap;
        struct usb_hub_descriptor *descriptor;
        struct usb_hub_status *hubsts;
        int i;
@@ -913,13 +1106,22 @@ int usb_hub_configure(struct usb_device *dev)
                return -1;
        }
        descriptor = (struct usb_hub_descriptor *)buffer;
+
+       /* silence compiler warning if USB_BUFSIZ is > 256 [= sizeof(char)] */
+       i = descriptor->bLength;
+       if (i > USB_BUFSIZ) {
+               USB_HUB_PRINTF("usb_hub_configure: failed to get hub descriptor - too long: %d\n",
+                       descriptor->bLength);
+               return -1;
+       }
+
        if (usb_get_hub_descriptor(dev, buffer, descriptor->bLength) < 0) {
                USB_HUB_PRINTF("usb_hub_configure: failed to get hub descriptor 2nd giving up %lX\n",dev->status);
                return -1;
        }
        memcpy((unsigned char *)&hub->desc,buffer,descriptor->bLength);
        /* adjust 16bit values */
-       hub->desc.wHubCharacteristics=swap_16(descriptor->wHubCharacteristics);
+       hub->desc.wHubCharacteristics = le16_to_cpu(descriptor->wHubCharacteristics);
        /* set the bitmap */
        bitmap=(unsigned char *)&hub->desc.DeviceRemovable[0];
        memset(bitmap,0xff,(USB_MAXCHILDREN+1+7)/8); /* devices not removable by default */
@@ -969,17 +1171,23 @@ int usb_hub_configure(struct usb_device *dev)
        for (i = 0; i < dev->maxchild; i++)
                USB_HUB_PRINTF("port %d is%s removable\n", i + 1,
                        hub->desc.DeviceRemovable[(i + 1)/8] & (1 << ((i + 1)%8)) ? " not" : "");
+       if (sizeof(struct usb_hub_status) > USB_BUFSIZ) {
+               USB_HUB_PRINTF("usb_hub_configure: failed to get Status - too long: %d\n",
+                       descriptor->bLength);
+               return -1;
+       }
+
        if (usb_get_hub_status(dev, buffer) < 0) {
                USB_HUB_PRINTF("usb_hub_configure: failed to get Status %lX\n",dev->status);
                return -1;
        }
        hubsts = (struct usb_hub_status *)buffer;
        USB_HUB_PRINTF("get_hub_status returned status %X, change %X\n",
-               swap_16(hubsts->wHubStatus),swap_16(hubsts->wHubChange));
+               le16_to_cpu(hubsts->wHubStatus),le16_to_cpu(hubsts->wHubChange));
        USB_HUB_PRINTF("local power source is %s\n",
-               (swap_16(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good");
+               (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good");
        USB_HUB_PRINTF("%sover-current condition exists\n",
-               (swap_16(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? "" : "no ");
+               (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? "" : "no ");
        usb_hub_power_on(hub);
        for (i = 0; i < dev->maxchild; i++) {
                struct usb_port_status portsts;
@@ -989,8 +1197,8 @@ int usb_hub_configure(struct usb_device *dev)
                        USB_HUB_PRINTF("get_port_status failed\n");
                        continue;
                }
-               portstatus = swap_16(portsts.wPortStatus);
-               portchange = swap_16(portsts.wPortChange);
+               portstatus = le16_to_cpu(portsts.wPortStatus);
+               portchange = le16_to_cpu(portsts.wPortChange);
                USB_HUB_PRINTF("Port %d Status %X Change %X\n",i+1,portstatus,portchange);
                if (portchange & USB_PORT_STAT_C_CONNECTION) {
                        USB_HUB_PRINTF("port %d connection change\n", i + 1);
@@ -1061,6 +1269,4 @@ int usb_hub_probe(struct usb_device *dev, int ifnum)
        return ret;
 }
 
-#endif /* (CONFIG_COMMANDS & CFG_CMD_USB) */
-
 /* EOF */