Fix low-level OHCI transfers for ARM920t and MPC5xxx

A new, Windows compatible init sequence was also backported from Linux 2.6,
but disabled with #undef NEW_INIT_SEQ as it wouldn't change the behaviour
of the memopry sticks we tested. Maybe it's not relevant for mass storage
devices. For recerence, see file common/usb.c, function usb_new_device(),
section #ifdef NEW_INIT_SEQ.
diff --git a/common/usb.c b/common/usb.c
index 4136f8d..1738d95 100644
--- a/common/usb.c
+++ b/common/usb.c
@@ -37,6 +37,7 @@
 #include <common.h>
 #include <command.h>
 #include <asm/processor.h>
+#include <linux/ctype.h>
 
 #if (CONFIG_COMMANDS & CFG_CMD_USB)
 
@@ -46,7 +47,7 @@
 #endif
 
 
-/* #define USB_DEBUG */
+#undef USB_DEBUG 
 
 #ifdef	USB_DEBUG
 #define	USB_PRINTF(fmt,args...)	printf (fmt ,##args)
@@ -70,6 +71,7 @@
 int usb_hub_probe(struct usb_device *dev, int ifnum);
 void usb_hub_reset(void);
 
+
 /***********************************************************************
  * wait_ms
  */
@@ -157,6 +159,7 @@
 {
 	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;
@@ -330,8 +333,7 @@
 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 +341,14 @@
 	/* 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;
@@ -423,7 +424,7 @@
 	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;
@@ -439,8 +440,6 @@
 	    interface, NULL, 0, USB_CNTL_TIMEOUT * 5)) < 0)
 		return ret;
 
-	if_face->act_altsetting = (unsigned char)alternate;
-	usb_set_maxpacket(dev);
 	return 0;
 }
 
@@ -511,11 +510,74 @@
  */
 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.
@@ -535,7 +597,7 @@
 
 	/* 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;
@@ -550,22 +612,11 @@
 				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);
 
-	if (u > USB_BUFSIZ) {
-		USB_PRINTF("usb_string: failed to get string - too long: %d\n", u);
-		return -1;
-	}
-
-	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)
@@ -641,11 +692,66 @@
 	/* 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) {
@@ -723,7 +829,7 @@
 	/* 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();
@@ -821,39 +927,15 @@
 
 #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);
@@ -861,7 +943,7 @@
 
 		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);
@@ -873,10 +955,12 @@
 			(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);
 	}
@@ -884,10 +968,52 @@
 	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 = 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 */
+	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 */