diff options
| author | Wolfgang Denk <wd@castor.denx.de> | 2005-07-21 11:57:57 +0200 | 
|---|---|---|
| committer | Wolfgang Denk <wd@castor.denx.de> | 2005-07-21 11:57:57 +0200 | 
| commit | 9c998aa83148c75cd734a18958218926313bd54c (patch) | |
| tree | c6d523801214c37337d3995dd1c405a7c329b37d /common/usb.c | |
| parent | f530187dbd69b0534e84b553f9a9803b16ed4999 (diff) | |
| download | olio-uboot-2014.01-9c998aa83148c75cd734a18958218926313bd54c.tar.xz olio-uboot-2014.01-9c998aa83148c75cd734a18958218926313bd54c.zip | |
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.
Diffstat (limited to 'common/usb.c')
| -rw-r--r-- | common/usb.c | 254 | 
1 files changed, 190 insertions, 64 deletions
| diff --git a/common/usb.c b/common/usb.c index 4136f8d77..1738d9506 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 @@ void usb_scan_devices(void);  int usb_hub_probe(struct usb_device *dev, int ifnum);  void usb_hub_reset(void); +  /***********************************************************************   * wait_ms   */ @@ -157,6 +159,7 @@ 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; @@ -330,8 +333,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 +341,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; @@ -423,7 +424,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; @@ -439,8 +440,6 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)  	    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_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. @@ -535,7 +597,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)  	/* 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 @@ 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); -	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 @@ 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) { @@ -723,7 +829,7 @@ void usb_scan_devices(void)  	/* 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 @@ 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); @@ -861,7 +943,7 @@ 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); @@ -873,10 +955,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);  	} @@ -884,10 +968,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 = 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 */ |