X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=common%2Fusb.c;h=03eccf8b3dd08c09e0eccc08f2c952ed57a6f315;hb=4b1d95d96a39a71eddd088bb5e0e9e699035c9bf;hp=9474abee4d5640cad68a6cbcadb84a16be85f909;hpb=8bde7f776c77b343aca29b8c7b58464d915ac245;p=platform%2Fkernel%2Fu-boot.git diff --git a/common/usb.c b/common/usb.c index 9474abe..03eccf8 100644 --- a/common/usb.c +++ b/common/usb.c @@ -25,7 +25,6 @@ * */ - /* * How it works: * @@ -38,6 +37,7 @@ #include #include #include +#include #if (CONFIG_COMMANDS & CFG_CMD_USB) @@ -46,7 +46,6 @@ #include <405gp_pci.h> #endif - #undef USB_DEBUG #ifdef USB_DEBUG @@ -55,6 +54,8 @@ #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; @@ -69,6 +70,7 @@ void usb_scan_devices(void); int usb_hub_probe(struct usb_device *dev, int ifnum); void usb_hub_reset(void); + /*********************************************************************** * wait_ms */ @@ -156,6 +158,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; @@ -329,8 +332,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); @@ -338,15 +340,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; @@ -387,6 +388,12 @@ int usb_get_configuration_no(struct usb_device *dev,unsigned char *buffer,int cf } tmp=swap_16(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); return result; @@ -416,7 +423,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; iconfig.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; @@ -432,8 +439,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; } @@ -504,11 +509,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. @@ -516,8 +584,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; @@ -529,7 +596,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; @@ -544,16 +611,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) @@ -595,7 +657,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 */ @@ -619,7 +681,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 */ @@ -629,11 +691,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) { @@ -711,7 +828,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(); @@ -809,39 +926,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<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;triesstatus); - return; + return -1; } portstatus = swap_16(portsts.wPortStatus); portchange = swap_16(portsts.wPortChange); @@ -861,10 +954,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); } @@ -872,10 +967,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<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 */ @@ -895,7 +1032,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; @@ -912,6 +1049,15 @@ 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; @@ -968,6 +1114,12 @@ 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;