12e192b24SSimon Glass /* 22e192b24SSimon Glass * (C) Copyright 2001 32e192b24SSimon Glass * Denis Peter, MPL AG Switzerland 42e192b24SSimon Glass * 52e192b24SSimon Glass * Adapted for U-Boot driver model 62e192b24SSimon Glass * (C) Copyright 2015 Google, Inc 72e192b24SSimon Glass * 82e192b24SSimon Glass * Most of this source has been derived from the Linux USB 92e192b24SSimon Glass * project. 102e192b24SSimon Glass * 112e192b24SSimon Glass * SPDX-License-Identifier: GPL-2.0+ 122e192b24SSimon Glass */ 132e192b24SSimon Glass 142e192b24SSimon Glass #include <common.h> 152e192b24SSimon Glass #include <command.h> 162e192b24SSimon Glass #include <console.h> 172e192b24SSimon Glass #include <dm.h> 18192eab93SHans de Goede #include <dm/uclass-internal.h> 192e192b24SSimon Glass #include <memalign.h> 202e192b24SSimon Glass #include <asm/byteorder.h> 212e192b24SSimon Glass #include <asm/unaligned.h> 222e192b24SSimon Glass #include <part.h> 232e192b24SSimon Glass #include <usb.h> 242e192b24SSimon Glass 252e192b24SSimon Glass #ifdef CONFIG_USB_STORAGE 262e192b24SSimon Glass static int usb_stor_curr_dev = -1; /* current device */ 272e192b24SSimon Glass #endif 282e192b24SSimon Glass #if defined(CONFIG_USB_HOST_ETHER) && !defined(CONFIG_DM_ETH) 292e192b24SSimon Glass static int __maybe_unused usb_ether_curr_dev = -1; /* current ethernet device */ 302e192b24SSimon Glass #endif 312e192b24SSimon Glass 322e192b24SSimon Glass /* some display routines (info command) */ 332e192b24SSimon Glass static char *usb_get_class_desc(unsigned char dclass) 342e192b24SSimon Glass { 352e192b24SSimon Glass switch (dclass) { 362e192b24SSimon Glass case USB_CLASS_PER_INTERFACE: 372e192b24SSimon Glass return "See Interface"; 382e192b24SSimon Glass case USB_CLASS_AUDIO: 392e192b24SSimon Glass return "Audio"; 402e192b24SSimon Glass case USB_CLASS_COMM: 412e192b24SSimon Glass return "Communication"; 422e192b24SSimon Glass case USB_CLASS_HID: 432e192b24SSimon Glass return "Human Interface"; 442e192b24SSimon Glass case USB_CLASS_PRINTER: 452e192b24SSimon Glass return "Printer"; 462e192b24SSimon Glass case USB_CLASS_MASS_STORAGE: 472e192b24SSimon Glass return "Mass Storage"; 482e192b24SSimon Glass case USB_CLASS_HUB: 492e192b24SSimon Glass return "Hub"; 502e192b24SSimon Glass case USB_CLASS_DATA: 512e192b24SSimon Glass return "CDC Data"; 522e192b24SSimon Glass case USB_CLASS_VENDOR_SPEC: 532e192b24SSimon Glass return "Vendor specific"; 542e192b24SSimon Glass default: 552e192b24SSimon Glass return ""; 562e192b24SSimon Glass } 572e192b24SSimon Glass } 582e192b24SSimon Glass 592e192b24SSimon Glass static void usb_display_class_sub(unsigned char dclass, unsigned char subclass, 602e192b24SSimon Glass unsigned char proto) 612e192b24SSimon Glass { 622e192b24SSimon Glass switch (dclass) { 632e192b24SSimon Glass case USB_CLASS_PER_INTERFACE: 642e192b24SSimon Glass printf("See Interface"); 652e192b24SSimon Glass break; 662e192b24SSimon Glass case USB_CLASS_HID: 672e192b24SSimon Glass printf("Human Interface, Subclass: "); 682e192b24SSimon Glass switch (subclass) { 692e192b24SSimon Glass case USB_SUB_HID_NONE: 702e192b24SSimon Glass printf("None"); 712e192b24SSimon Glass break; 722e192b24SSimon Glass case USB_SUB_HID_BOOT: 732e192b24SSimon Glass printf("Boot "); 742e192b24SSimon Glass switch (proto) { 752e192b24SSimon Glass case USB_PROT_HID_NONE: 762e192b24SSimon Glass printf("None"); 772e192b24SSimon Glass break; 782e192b24SSimon Glass case USB_PROT_HID_KEYBOARD: 792e192b24SSimon Glass printf("Keyboard"); 802e192b24SSimon Glass break; 812e192b24SSimon Glass case USB_PROT_HID_MOUSE: 822e192b24SSimon Glass printf("Mouse"); 832e192b24SSimon Glass break; 842e192b24SSimon Glass default: 852e192b24SSimon Glass printf("reserved"); 862e192b24SSimon Glass break; 872e192b24SSimon Glass } 882e192b24SSimon Glass break; 892e192b24SSimon Glass default: 902e192b24SSimon Glass printf("reserved"); 912e192b24SSimon Glass break; 922e192b24SSimon Glass } 932e192b24SSimon Glass break; 942e192b24SSimon Glass case USB_CLASS_MASS_STORAGE: 952e192b24SSimon Glass printf("Mass Storage, "); 962e192b24SSimon Glass switch (subclass) { 972e192b24SSimon Glass case US_SC_RBC: 982e192b24SSimon Glass printf("RBC "); 992e192b24SSimon Glass break; 1002e192b24SSimon Glass case US_SC_8020: 1012e192b24SSimon Glass printf("SFF-8020i (ATAPI)"); 1022e192b24SSimon Glass break; 1032e192b24SSimon Glass case US_SC_QIC: 1042e192b24SSimon Glass printf("QIC-157 (Tape)"); 1052e192b24SSimon Glass break; 1062e192b24SSimon Glass case US_SC_UFI: 1072e192b24SSimon Glass printf("UFI"); 1082e192b24SSimon Glass break; 1092e192b24SSimon Glass case US_SC_8070: 1102e192b24SSimon Glass printf("SFF-8070"); 1112e192b24SSimon Glass break; 1122e192b24SSimon Glass case US_SC_SCSI: 1132e192b24SSimon Glass printf("Transp. SCSI"); 1142e192b24SSimon Glass break; 1152e192b24SSimon Glass default: 1162e192b24SSimon Glass printf("reserved"); 1172e192b24SSimon Glass break; 1182e192b24SSimon Glass } 1192e192b24SSimon Glass printf(", "); 1202e192b24SSimon Glass switch (proto) { 1212e192b24SSimon Glass case US_PR_CB: 1222e192b24SSimon Glass printf("Command/Bulk"); 1232e192b24SSimon Glass break; 1242e192b24SSimon Glass case US_PR_CBI: 1252e192b24SSimon Glass printf("Command/Bulk/Int"); 1262e192b24SSimon Glass break; 1272e192b24SSimon Glass case US_PR_BULK: 1282e192b24SSimon Glass printf("Bulk only"); 1292e192b24SSimon Glass break; 1302e192b24SSimon Glass default: 1312e192b24SSimon Glass printf("reserved"); 1322e192b24SSimon Glass break; 1332e192b24SSimon Glass } 1342e192b24SSimon Glass break; 1352e192b24SSimon Glass default: 1362e192b24SSimon Glass printf("%s", usb_get_class_desc(dclass)); 1372e192b24SSimon Glass break; 1382e192b24SSimon Glass } 1392e192b24SSimon Glass } 1402e192b24SSimon Glass 1412e192b24SSimon Glass static void usb_display_string(struct usb_device *dev, int index) 1422e192b24SSimon Glass { 1432e192b24SSimon Glass ALLOC_CACHE_ALIGN_BUFFER(char, buffer, 256); 1442e192b24SSimon Glass 1452e192b24SSimon Glass if (index != 0) { 1462e192b24SSimon Glass if (usb_string(dev, index, &buffer[0], 256) > 0) 1472e192b24SSimon Glass printf("String: \"%s\"", buffer); 1482e192b24SSimon Glass } 1492e192b24SSimon Glass } 1502e192b24SSimon Glass 1512e192b24SSimon Glass static void usb_display_desc(struct usb_device *dev) 1522e192b24SSimon Glass { 15378abf14fSBin Meng uint packet_size = dev->descriptor.bMaxPacketSize0; 15478abf14fSBin Meng 1552e192b24SSimon Glass if (dev->descriptor.bDescriptorType == USB_DT_DEVICE) { 1562e192b24SSimon Glass printf("%d: %s, USB Revision %x.%x\n", dev->devnum, 1572e192b24SSimon Glass usb_get_class_desc(dev->config.if_desc[0].desc.bInterfaceClass), 1582e192b24SSimon Glass (dev->descriptor.bcdUSB>>8) & 0xff, 1592e192b24SSimon Glass dev->descriptor.bcdUSB & 0xff); 1602e192b24SSimon Glass 1612e192b24SSimon Glass if (strlen(dev->mf) || strlen(dev->prod) || 1622e192b24SSimon Glass strlen(dev->serial)) 1632e192b24SSimon Glass printf(" - %s %s %s\n", dev->mf, dev->prod, 1642e192b24SSimon Glass dev->serial); 1652e192b24SSimon Glass if (dev->descriptor.bDeviceClass) { 1662e192b24SSimon Glass printf(" - Class: "); 1672e192b24SSimon Glass usb_display_class_sub(dev->descriptor.bDeviceClass, 1682e192b24SSimon Glass dev->descriptor.bDeviceSubClass, 1692e192b24SSimon Glass dev->descriptor.bDeviceProtocol); 1702e192b24SSimon Glass printf("\n"); 1712e192b24SSimon Glass } else { 1722e192b24SSimon Glass printf(" - Class: (from Interface) %s\n", 1732e192b24SSimon Glass usb_get_class_desc( 1742e192b24SSimon Glass dev->config.if_desc[0].desc.bInterfaceClass)); 1752e192b24SSimon Glass } 17678abf14fSBin Meng if (dev->descriptor.bcdUSB >= cpu_to_le16(0x0300)) 17778abf14fSBin Meng packet_size = 1 << packet_size; 1782e192b24SSimon Glass printf(" - PacketSize: %d Configurations: %d\n", 17978abf14fSBin Meng packet_size, dev->descriptor.bNumConfigurations); 1802e192b24SSimon Glass printf(" - Vendor: 0x%04x Product 0x%04x Version %d.%d\n", 1812e192b24SSimon Glass dev->descriptor.idVendor, dev->descriptor.idProduct, 1822e192b24SSimon Glass (dev->descriptor.bcdDevice>>8) & 0xff, 1832e192b24SSimon Glass dev->descriptor.bcdDevice & 0xff); 1842e192b24SSimon Glass } 1852e192b24SSimon Glass 1862e192b24SSimon Glass } 1872e192b24SSimon Glass 1882e192b24SSimon Glass static void usb_display_conf_desc(struct usb_config_descriptor *config, 1892e192b24SSimon Glass struct usb_device *dev) 1902e192b24SSimon Glass { 1912e192b24SSimon Glass printf(" Configuration: %d\n", config->bConfigurationValue); 1922e192b24SSimon Glass printf(" - Interfaces: %d %s%s%dmA\n", config->bNumInterfaces, 1932e192b24SSimon Glass (config->bmAttributes & 0x40) ? "Self Powered " : "Bus Powered ", 1942e192b24SSimon Glass (config->bmAttributes & 0x20) ? "Remote Wakeup " : "", 1952e192b24SSimon Glass config->bMaxPower*2); 1962e192b24SSimon Glass if (config->iConfiguration) { 1972e192b24SSimon Glass printf(" - "); 1982e192b24SSimon Glass usb_display_string(dev, config->iConfiguration); 1992e192b24SSimon Glass printf("\n"); 2002e192b24SSimon Glass } 2012e192b24SSimon Glass } 2022e192b24SSimon Glass 2032e192b24SSimon Glass static void usb_display_if_desc(struct usb_interface_descriptor *ifdesc, 2042e192b24SSimon Glass struct usb_device *dev) 2052e192b24SSimon Glass { 2062e192b24SSimon Glass printf(" Interface: %d\n", ifdesc->bInterfaceNumber); 2072e192b24SSimon Glass printf(" - Alternate Setting %d, Endpoints: %d\n", 2082e192b24SSimon Glass ifdesc->bAlternateSetting, ifdesc->bNumEndpoints); 2092e192b24SSimon Glass printf(" - Class "); 2102e192b24SSimon Glass usb_display_class_sub(ifdesc->bInterfaceClass, 2112e192b24SSimon Glass ifdesc->bInterfaceSubClass, ifdesc->bInterfaceProtocol); 2122e192b24SSimon Glass printf("\n"); 2132e192b24SSimon Glass if (ifdesc->iInterface) { 2142e192b24SSimon Glass printf(" - "); 2152e192b24SSimon Glass usb_display_string(dev, ifdesc->iInterface); 2162e192b24SSimon Glass printf("\n"); 2172e192b24SSimon Glass } 2182e192b24SSimon Glass } 2192e192b24SSimon Glass 2202e192b24SSimon Glass static void usb_display_ep_desc(struct usb_endpoint_descriptor *epdesc) 2212e192b24SSimon Glass { 2222e192b24SSimon Glass printf(" - Endpoint %d %s ", epdesc->bEndpointAddress & 0xf, 2232e192b24SSimon Glass (epdesc->bEndpointAddress & 0x80) ? "In" : "Out"); 2242e192b24SSimon Glass switch ((epdesc->bmAttributes & 0x03)) { 2252e192b24SSimon Glass case 0: 2262e192b24SSimon Glass printf("Control"); 2272e192b24SSimon Glass break; 2282e192b24SSimon Glass case 1: 2292e192b24SSimon Glass printf("Isochronous"); 2302e192b24SSimon Glass break; 2312e192b24SSimon Glass case 2: 2322e192b24SSimon Glass printf("Bulk"); 2332e192b24SSimon Glass break; 2342e192b24SSimon Glass case 3: 2352e192b24SSimon Glass printf("Interrupt"); 2362e192b24SSimon Glass break; 2372e192b24SSimon Glass } 2382e192b24SSimon Glass printf(" MaxPacket %d", get_unaligned(&epdesc->wMaxPacketSize)); 2392e192b24SSimon Glass if ((epdesc->bmAttributes & 0x03) == 0x3) 2402e192b24SSimon Glass printf(" Interval %dms", epdesc->bInterval); 2412e192b24SSimon Glass printf("\n"); 2422e192b24SSimon Glass } 2432e192b24SSimon Glass 2442e192b24SSimon Glass /* main routine to diasplay the configs, interfaces and endpoints */ 2452e192b24SSimon Glass static void usb_display_config(struct usb_device *dev) 2462e192b24SSimon Glass { 2472e192b24SSimon Glass struct usb_config *config; 2482e192b24SSimon Glass struct usb_interface *ifdesc; 2492e192b24SSimon Glass struct usb_endpoint_descriptor *epdesc; 2502e192b24SSimon Glass int i, ii; 2512e192b24SSimon Glass 2522e192b24SSimon Glass config = &dev->config; 2532e192b24SSimon Glass usb_display_conf_desc(&config->desc, dev); 2542e192b24SSimon Glass for (i = 0; i < config->no_of_if; i++) { 2552e192b24SSimon Glass ifdesc = &config->if_desc[i]; 2562e192b24SSimon Glass usb_display_if_desc(&ifdesc->desc, dev); 2572e192b24SSimon Glass for (ii = 0; ii < ifdesc->no_of_ep; ii++) { 2582e192b24SSimon Glass epdesc = &ifdesc->ep_desc[ii]; 2592e192b24SSimon Glass usb_display_ep_desc(epdesc); 2602e192b24SSimon Glass } 2612e192b24SSimon Glass } 2622e192b24SSimon Glass printf("\n"); 2632e192b24SSimon Glass } 2642e192b24SSimon Glass 2652e192b24SSimon Glass /* 2662e192b24SSimon Glass * With driver model this isn't right since we can have multiple controllers 2672e192b24SSimon Glass * and the device numbering starts at 1 on each bus. 2682e192b24SSimon Glass * TODO(sjg@chromium.org): Add a way to specify the controller/bus. 2692e192b24SSimon Glass */ 2702e192b24SSimon Glass static struct usb_device *usb_find_device(int devnum) 2712e192b24SSimon Glass { 2722e192b24SSimon Glass #ifdef CONFIG_DM_USB 2732e192b24SSimon Glass struct usb_device *udev; 2742e192b24SSimon Glass struct udevice *hub; 2752e192b24SSimon Glass struct uclass *uc; 2762e192b24SSimon Glass int ret; 2772e192b24SSimon Glass 2782e192b24SSimon Glass /* Device addresses start at 1 */ 2792e192b24SSimon Glass devnum++; 2802e192b24SSimon Glass ret = uclass_get(UCLASS_USB_HUB, &uc); 2812e192b24SSimon Glass if (ret) 2822e192b24SSimon Glass return NULL; 2832e192b24SSimon Glass 2842e192b24SSimon Glass uclass_foreach_dev(hub, uc) { 2852e192b24SSimon Glass struct udevice *dev; 2862e192b24SSimon Glass 2872e192b24SSimon Glass if (!device_active(hub)) 2882e192b24SSimon Glass continue; 2892e192b24SSimon Glass udev = dev_get_parent_priv(hub); 2902e192b24SSimon Glass if (udev->devnum == devnum) 2912e192b24SSimon Glass return udev; 2922e192b24SSimon Glass 2932e192b24SSimon Glass for (device_find_first_child(hub, &dev); 2942e192b24SSimon Glass dev; 2952e192b24SSimon Glass device_find_next_child(&dev)) { 2962e192b24SSimon Glass if (!device_active(hub)) 2972e192b24SSimon Glass continue; 2982e192b24SSimon Glass 2992e192b24SSimon Glass udev = dev_get_parent_priv(dev); 3002e192b24SSimon Glass if (udev->devnum == devnum) 3012e192b24SSimon Glass return udev; 3022e192b24SSimon Glass } 3032e192b24SSimon Glass } 3042e192b24SSimon Glass #else 3052e192b24SSimon Glass struct usb_device *udev; 3062e192b24SSimon Glass int d; 3072e192b24SSimon Glass 3082e192b24SSimon Glass for (d = 0; d < USB_MAX_DEVICE; d++) { 3092e192b24SSimon Glass udev = usb_get_dev_index(d); 3102e192b24SSimon Glass if (udev == NULL) 3112e192b24SSimon Glass return NULL; 3122e192b24SSimon Glass if (udev->devnum == devnum) 3132e192b24SSimon Glass return udev; 3142e192b24SSimon Glass } 3152e192b24SSimon Glass #endif 3162e192b24SSimon Glass 3172e192b24SSimon Glass return NULL; 3182e192b24SSimon Glass } 3192e192b24SSimon Glass 3202e192b24SSimon Glass static inline char *portspeed(int speed) 3212e192b24SSimon Glass { 3222e192b24SSimon Glass char *speed_str; 3232e192b24SSimon Glass 3242e192b24SSimon Glass switch (speed) { 3252e192b24SSimon Glass case USB_SPEED_SUPER: 3262e192b24SSimon Glass speed_str = "5 Gb/s"; 3272e192b24SSimon Glass break; 3282e192b24SSimon Glass case USB_SPEED_HIGH: 3292e192b24SSimon Glass speed_str = "480 Mb/s"; 3302e192b24SSimon Glass break; 3312e192b24SSimon Glass case USB_SPEED_LOW: 3322e192b24SSimon Glass speed_str = "1.5 Mb/s"; 3332e192b24SSimon Glass break; 3342e192b24SSimon Glass default: 3352e192b24SSimon Glass speed_str = "12 Mb/s"; 3362e192b24SSimon Glass break; 3372e192b24SSimon Glass } 3382e192b24SSimon Glass 3392e192b24SSimon Glass return speed_str; 3402e192b24SSimon Glass } 3412e192b24SSimon Glass 3422e192b24SSimon Glass /* shows the device tree recursively */ 3432e192b24SSimon Glass static void usb_show_tree_graph(struct usb_device *dev, char *pre) 3442e192b24SSimon Glass { 3452e192b24SSimon Glass int index; 3462e192b24SSimon Glass int has_child, last_child; 3472e192b24SSimon Glass 3482e192b24SSimon Glass index = strlen(pre); 3492e192b24SSimon Glass printf(" %s", pre); 3502e192b24SSimon Glass #ifdef CONFIG_DM_USB 3512e192b24SSimon Glass has_child = device_has_active_children(dev->dev); 3522e192b24SSimon Glass #else 3532e192b24SSimon Glass /* check if the device has connected children */ 3542e192b24SSimon Glass int i; 3552e192b24SSimon Glass 3562e192b24SSimon Glass has_child = 0; 3572e192b24SSimon Glass for (i = 0; i < dev->maxchild; i++) { 3582e192b24SSimon Glass if (dev->children[i] != NULL) 3592e192b24SSimon Glass has_child = 1; 3602e192b24SSimon Glass } 3612e192b24SSimon Glass #endif 3622e192b24SSimon Glass /* check if we are the last one */ 3632e192b24SSimon Glass #ifdef CONFIG_DM_USB 3642e192b24SSimon Glass /* Not the root of the usb tree? */ 3652e192b24SSimon Glass if (device_get_uclass_id(dev->dev->parent) != UCLASS_USB) { 3662e192b24SSimon Glass last_child = device_is_last_sibling(dev->dev); 3672e192b24SSimon Glass #else 3682e192b24SSimon Glass if (dev->parent != NULL) { /* not root? */ 3692e192b24SSimon Glass last_child = 1; 3702e192b24SSimon Glass for (i = 0; i < dev->parent->maxchild; i++) { 3712e192b24SSimon Glass /* search for children */ 3722e192b24SSimon Glass if (dev->parent->children[i] == dev) { 3732e192b24SSimon Glass /* found our pointer, see if we have a 3742e192b24SSimon Glass * little sister 3752e192b24SSimon Glass */ 3762e192b24SSimon Glass while (i++ < dev->parent->maxchild) { 3772e192b24SSimon Glass if (dev->parent->children[i] != NULL) { 3782e192b24SSimon Glass /* found a sister */ 3792e192b24SSimon Glass last_child = 0; 3802e192b24SSimon Glass break; 3812e192b24SSimon Glass } /* if */ 3822e192b24SSimon Glass } /* while */ 3832e192b24SSimon Glass } /* device found */ 3842e192b24SSimon Glass } /* for all children of the parent */ 3852e192b24SSimon Glass #endif 3862e192b24SSimon Glass printf("\b+-"); 3872e192b24SSimon Glass /* correct last child */ 3882e192b24SSimon Glass if (last_child && index) 3892e192b24SSimon Glass pre[index-1] = ' '; 3902e192b24SSimon Glass } /* if not root hub */ 3912e192b24SSimon Glass else 3922e192b24SSimon Glass printf(" "); 3932e192b24SSimon Glass printf("%d ", dev->devnum); 3942e192b24SSimon Glass pre[index++] = ' '; 3952e192b24SSimon Glass pre[index++] = has_child ? '|' : ' '; 3962e192b24SSimon Glass pre[index] = 0; 3972e192b24SSimon Glass printf(" %s (%s, %dmA)\n", usb_get_class_desc( 3982e192b24SSimon Glass dev->config.if_desc[0].desc.bInterfaceClass), 3992e192b24SSimon Glass portspeed(dev->speed), 4002e192b24SSimon Glass dev->config.desc.bMaxPower * 2); 4012e192b24SSimon Glass if (strlen(dev->mf) || strlen(dev->prod) || strlen(dev->serial)) 4022e192b24SSimon Glass printf(" %s %s %s %s\n", pre, dev->mf, dev->prod, dev->serial); 4032e192b24SSimon Glass printf(" %s\n", pre); 4042e192b24SSimon Glass #ifdef CONFIG_DM_USB 4052e192b24SSimon Glass struct udevice *child; 4062e192b24SSimon Glass 4072e192b24SSimon Glass for (device_find_first_child(dev->dev, &child); 4082e192b24SSimon Glass child; 4092e192b24SSimon Glass device_find_next_child(&child)) { 4102e192b24SSimon Glass struct usb_device *udev; 4112e192b24SSimon Glass 4122e192b24SSimon Glass if (!device_active(child)) 4132e192b24SSimon Glass continue; 4142e192b24SSimon Glass 4152e192b24SSimon Glass udev = dev_get_parent_priv(child); 4162e192b24SSimon Glass 4172e192b24SSimon Glass /* Ignore emulators, we only want real devices */ 4182e192b24SSimon Glass if (device_get_uclass_id(child) != UCLASS_USB_EMUL) { 4192e192b24SSimon Glass usb_show_tree_graph(udev, pre); 4202e192b24SSimon Glass pre[index] = 0; 4212e192b24SSimon Glass } 4222e192b24SSimon Glass } 4232e192b24SSimon Glass #else 4242e192b24SSimon Glass if (dev->maxchild > 0) { 4252e192b24SSimon Glass for (i = 0; i < dev->maxchild; i++) { 4262e192b24SSimon Glass if (dev->children[i] != NULL) { 4272e192b24SSimon Glass usb_show_tree_graph(dev->children[i], pre); 4282e192b24SSimon Glass pre[index] = 0; 4292e192b24SSimon Glass } 4302e192b24SSimon Glass } 4312e192b24SSimon Glass } 4322e192b24SSimon Glass #endif 4332e192b24SSimon Glass } 4342e192b24SSimon Glass 4352e192b24SSimon Glass /* main routine for the tree command */ 4362e192b24SSimon Glass static void usb_show_subtree(struct usb_device *dev) 4372e192b24SSimon Glass { 4382e192b24SSimon Glass char preamble[32]; 4392e192b24SSimon Glass 4402e192b24SSimon Glass memset(preamble, '\0', sizeof(preamble)); 4412e192b24SSimon Glass usb_show_tree_graph(dev, &preamble[0]); 4422e192b24SSimon Glass } 4432e192b24SSimon Glass 4442e192b24SSimon Glass #ifdef CONFIG_DM_USB 4452138fd6dSHans de Goede typedef void (*usb_dev_func_t)(struct usb_device *udev); 4462138fd6dSHans de Goede 4472138fd6dSHans de Goede static void usb_for_each_root_dev(usb_dev_func_t func) 4482138fd6dSHans de Goede { 4492e192b24SSimon Glass struct udevice *bus; 4502e192b24SSimon Glass 451192eab93SHans de Goede for (uclass_find_first_device(UCLASS_USB, &bus); 4522e192b24SSimon Glass bus; 453192eab93SHans de Goede uclass_find_next_device(&bus)) { 4542e192b24SSimon Glass struct usb_device *udev; 4552e192b24SSimon Glass struct udevice *dev; 4562e192b24SSimon Glass 457192eab93SHans de Goede if (!device_active(bus)) 458192eab93SHans de Goede continue; 459192eab93SHans de Goede 4602e192b24SSimon Glass device_find_first_child(bus, &dev); 4612e192b24SSimon Glass if (dev && device_active(dev)) { 4622e192b24SSimon Glass udev = dev_get_parent_priv(dev); 4632138fd6dSHans de Goede func(udev); 4642e192b24SSimon Glass } 4652e192b24SSimon Glass } 4662138fd6dSHans de Goede } 4672138fd6dSHans de Goede #endif 4682138fd6dSHans de Goede 4692138fd6dSHans de Goede void usb_show_tree(void) 4702138fd6dSHans de Goede { 4712138fd6dSHans de Goede #ifdef CONFIG_DM_USB 4722138fd6dSHans de Goede usb_for_each_root_dev(usb_show_subtree); 4732e192b24SSimon Glass #else 4742e192b24SSimon Glass struct usb_device *udev; 4752e192b24SSimon Glass int i; 4762e192b24SSimon Glass 4772e192b24SSimon Glass for (i = 0; i < USB_MAX_DEVICE; i++) { 4782e192b24SSimon Glass udev = usb_get_dev_index(i); 4792e192b24SSimon Glass if (udev == NULL) 4802e192b24SSimon Glass break; 4812e192b24SSimon Glass if (udev->parent == NULL) 4822e192b24SSimon Glass usb_show_subtree(udev); 4832e192b24SSimon Glass } 4842e192b24SSimon Glass #endif 4852e192b24SSimon Glass } 4862e192b24SSimon Glass 4872e192b24SSimon Glass static int usb_test(struct usb_device *dev, int port, char* arg) 4882e192b24SSimon Glass { 4892e192b24SSimon Glass int mode; 4902e192b24SSimon Glass 4912e192b24SSimon Glass if (port > dev->maxchild) { 4922e192b24SSimon Glass printf("Device is no hub or does not have %d ports.\n", port); 4932e192b24SSimon Glass return 1; 4942e192b24SSimon Glass } 4952e192b24SSimon Glass 4962e192b24SSimon Glass switch (arg[0]) { 4972e192b24SSimon Glass case 'J': 4982e192b24SSimon Glass case 'j': 4992e192b24SSimon Glass printf("Setting Test_J mode"); 5002e192b24SSimon Glass mode = USB_TEST_MODE_J; 5012e192b24SSimon Glass break; 5022e192b24SSimon Glass case 'K': 5032e192b24SSimon Glass case 'k': 5042e192b24SSimon Glass printf("Setting Test_K mode"); 5052e192b24SSimon Glass mode = USB_TEST_MODE_K; 5062e192b24SSimon Glass break; 5072e192b24SSimon Glass case 'S': 5082e192b24SSimon Glass case 's': 5092e192b24SSimon Glass printf("Setting Test_SE0_NAK mode"); 5102e192b24SSimon Glass mode = USB_TEST_MODE_SE0_NAK; 5112e192b24SSimon Glass break; 5122e192b24SSimon Glass case 'P': 5132e192b24SSimon Glass case 'p': 5142e192b24SSimon Glass printf("Setting Test_Packet mode"); 5152e192b24SSimon Glass mode = USB_TEST_MODE_PACKET; 5162e192b24SSimon Glass break; 5172e192b24SSimon Glass case 'F': 5182e192b24SSimon Glass case 'f': 5192e192b24SSimon Glass printf("Setting Test_Force_Enable mode"); 5202e192b24SSimon Glass mode = USB_TEST_MODE_FORCE_ENABLE; 5212e192b24SSimon Glass break; 5222e192b24SSimon Glass default: 5232e192b24SSimon Glass printf("Unrecognized test mode: %s\nAvailable modes: " 5242e192b24SSimon Glass "J, K, S[E0_NAK], P[acket], F[orce_Enable]\n", arg); 5252e192b24SSimon Glass return 1; 5262e192b24SSimon Glass } 5272e192b24SSimon Glass 5282e192b24SSimon Glass if (port) 5292e192b24SSimon Glass printf(" on downstream facing port %d...\n", port); 5302e192b24SSimon Glass else 5312e192b24SSimon Glass printf(" on upstream facing port...\n"); 5322e192b24SSimon Glass 5332e192b24SSimon Glass if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_FEATURE, 5342e192b24SSimon Glass port ? USB_RT_PORT : USB_RECIP_DEVICE, 5352e192b24SSimon Glass port ? USB_PORT_FEAT_TEST : USB_FEAT_TEST, 5362e192b24SSimon Glass (mode << 8) | port, 5372e192b24SSimon Glass NULL, 0, USB_CNTL_TIMEOUT) == -1) { 5382e192b24SSimon Glass printf("Error during SET_FEATURE.\n"); 5392e192b24SSimon Glass return 1; 5402e192b24SSimon Glass } else { 5412e192b24SSimon Glass printf("Test mode successfully set. Use 'usb start' " 5422e192b24SSimon Glass "to return to normal operation.\n"); 5432e192b24SSimon Glass return 0; 5442e192b24SSimon Glass } 5452e192b24SSimon Glass } 5462e192b24SSimon Glass 5472e192b24SSimon Glass 5482e192b24SSimon Glass /****************************************************************************** 5492e192b24SSimon Glass * usb boot command intepreter. Derived from diskboot 5502e192b24SSimon Glass */ 5512e192b24SSimon Glass #ifdef CONFIG_USB_STORAGE 5522e192b24SSimon Glass static int do_usbboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 5532e192b24SSimon Glass { 5542e192b24SSimon Glass return common_diskboot(cmdtp, "usb", argc, argv); 5552e192b24SSimon Glass } 5562e192b24SSimon Glass #endif /* CONFIG_USB_STORAGE */ 5572e192b24SSimon Glass 5582e192b24SSimon Glass static int do_usb_stop_keyboard(int force) 5592e192b24SSimon Glass { 5609a80e714SHans de Goede #if !defined CONFIG_DM_USB && defined CONFIG_USB_KEYBOARD 5612e192b24SSimon Glass if (usb_kbd_deregister(force) != 0) { 5622e192b24SSimon Glass printf("USB not stopped: usbkbd still using USB\n"); 5632e192b24SSimon Glass return 1; 5642e192b24SSimon Glass } 5652e192b24SSimon Glass #endif 5662e192b24SSimon Glass return 0; 5672e192b24SSimon Glass } 5682e192b24SSimon Glass 5692e192b24SSimon Glass static void do_usb_start(void) 5702e192b24SSimon Glass { 5712e192b24SSimon Glass bootstage_mark_name(BOOTSTAGE_ID_USB_START, "usb_start"); 5722e192b24SSimon Glass 5732e192b24SSimon Glass if (usb_init() < 0) 5742e192b24SSimon Glass return; 5752e192b24SSimon Glass 5762e192b24SSimon Glass /* Driver model will probe the devices as they are found */ 5772e192b24SSimon Glass # ifdef CONFIG_USB_STORAGE 5782e192b24SSimon Glass /* try to recognize storage devices immediately */ 5792e192b24SSimon Glass usb_stor_curr_dev = usb_stor_scan(1); 5802e192b24SSimon Glass # endif 581b984700cSMichal Simek #ifndef CONFIG_DM_USB 5822e192b24SSimon Glass # ifdef CONFIG_USB_KEYBOARD 5832e192b24SSimon Glass drv_usb_kbd_init(); 5842e192b24SSimon Glass # endif 5852e192b24SSimon Glass #endif /* !CONFIG_DM_USB */ 5862e192b24SSimon Glass #ifdef CONFIG_USB_HOST_ETHER 5872e192b24SSimon Glass # ifdef CONFIG_DM_ETH 5882e192b24SSimon Glass # ifndef CONFIG_DM_USB 5892e192b24SSimon Glass # error "You must use CONFIG_DM_USB if you want to use CONFIG_USB_HOST_ETHER with CONFIG_DM_ETH" 5902e192b24SSimon Glass # endif 5912e192b24SSimon Glass # else 5922e192b24SSimon Glass /* try to recognize ethernet devices immediately */ 5932e192b24SSimon Glass usb_ether_curr_dev = usb_host_eth_scan(1); 5942e192b24SSimon Glass # endif 5952e192b24SSimon Glass #endif 5962e192b24SSimon Glass } 5972e192b24SSimon Glass 5982e192b24SSimon Glass #ifdef CONFIG_DM_USB 599e6e188f5SHans de Goede static void usb_show_info(struct usb_device *udev) 6002e192b24SSimon Glass { 6012e192b24SSimon Glass struct udevice *child; 6022e192b24SSimon Glass 6032e192b24SSimon Glass usb_display_desc(udev); 6042e192b24SSimon Glass usb_display_config(udev); 605e6e188f5SHans de Goede for (device_find_first_child(udev->dev, &child); 6062e192b24SSimon Glass child; 6072e192b24SSimon Glass device_find_next_child(&child)) { 608e6e188f5SHans de Goede if (device_active(child)) { 609e6e188f5SHans de Goede udev = dev_get_parent_priv(child); 610e6e188f5SHans de Goede usb_show_info(udev); 6112e192b24SSimon Glass } 6122e192b24SSimon Glass } 6132e192b24SSimon Glass } 6142e192b24SSimon Glass #endif 6152e192b24SSimon Glass 6162e192b24SSimon Glass /****************************************************************************** 6172e192b24SSimon Glass * usb command intepreter 6182e192b24SSimon Glass */ 6192e192b24SSimon Glass static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 6202e192b24SSimon Glass { 6212e192b24SSimon Glass struct usb_device *udev = NULL; 6222e192b24SSimon Glass int i; 6232e192b24SSimon Glass extern char usb_started; 6242e192b24SSimon Glass 6252e192b24SSimon Glass if (argc < 2) 6262e192b24SSimon Glass return CMD_RET_USAGE; 6272e192b24SSimon Glass 6282e192b24SSimon Glass if (strncmp(argv[1], "start", 5) == 0) { 6292e192b24SSimon Glass if (usb_started) 6302e192b24SSimon Glass return 0; /* Already started */ 6312e192b24SSimon Glass printf("starting USB...\n"); 6322e192b24SSimon Glass do_usb_start(); 6332e192b24SSimon Glass return 0; 6342e192b24SSimon Glass } 6352e192b24SSimon Glass 6362e192b24SSimon Glass if (strncmp(argv[1], "reset", 5) == 0) { 6372e192b24SSimon Glass printf("resetting USB...\n"); 6382e192b24SSimon Glass if (do_usb_stop_keyboard(1) != 0) 6392e192b24SSimon Glass return 1; 6402e192b24SSimon Glass usb_stop(); 6412e192b24SSimon Glass do_usb_start(); 6422e192b24SSimon Glass return 0; 6432e192b24SSimon Glass } 6442e192b24SSimon Glass if (strncmp(argv[1], "stop", 4) == 0) { 6452e192b24SSimon Glass if (argc != 2) 6462e192b24SSimon Glass console_assign(stdin, "serial"); 6472e192b24SSimon Glass if (do_usb_stop_keyboard(0) != 0) 6482e192b24SSimon Glass return 1; 6492e192b24SSimon Glass printf("stopping USB..\n"); 6502e192b24SSimon Glass usb_stop(); 6512e192b24SSimon Glass return 0; 6522e192b24SSimon Glass } 6532e192b24SSimon Glass if (!usb_started) { 6542e192b24SSimon Glass printf("USB is stopped. Please issue 'usb start' first.\n"); 6552e192b24SSimon Glass return 1; 6562e192b24SSimon Glass } 6572e192b24SSimon Glass if (strncmp(argv[1], "tree", 4) == 0) { 6582e192b24SSimon Glass puts("USB device tree:\n"); 6592e192b24SSimon Glass usb_show_tree(); 6602e192b24SSimon Glass return 0; 6612e192b24SSimon Glass } 6622e192b24SSimon Glass if (strncmp(argv[1], "inf", 3) == 0) { 6632e192b24SSimon Glass if (argc == 2) { 6642e192b24SSimon Glass #ifdef CONFIG_DM_USB 665e6e188f5SHans de Goede usb_for_each_root_dev(usb_show_info); 6662e192b24SSimon Glass #else 6672e192b24SSimon Glass int d; 6682e192b24SSimon Glass for (d = 0; d < USB_MAX_DEVICE; d++) { 6692e192b24SSimon Glass udev = usb_get_dev_index(d); 6702e192b24SSimon Glass if (udev == NULL) 6712e192b24SSimon Glass break; 6722e192b24SSimon Glass usb_display_desc(udev); 6732e192b24SSimon Glass usb_display_config(udev); 6742e192b24SSimon Glass } 6752e192b24SSimon Glass #endif 6762e192b24SSimon Glass return 0; 6772e192b24SSimon Glass } else { 6782e192b24SSimon Glass /* 6792e192b24SSimon Glass * With driver model this isn't right since we can 6802e192b24SSimon Glass * have multiple controllers and the device numbering 6812e192b24SSimon Glass * starts at 1 on each bus. 6822e192b24SSimon Glass */ 6832e192b24SSimon Glass i = simple_strtoul(argv[2], NULL, 10); 6842e192b24SSimon Glass printf("config for device %d\n", i); 6852e192b24SSimon Glass udev = usb_find_device(i); 6862e192b24SSimon Glass if (udev == NULL) { 6872e192b24SSimon Glass printf("*** No device available ***\n"); 6882e192b24SSimon Glass return 0; 6892e192b24SSimon Glass } else { 6902e192b24SSimon Glass usb_display_desc(udev); 6912e192b24SSimon Glass usb_display_config(udev); 6922e192b24SSimon Glass } 6932e192b24SSimon Glass } 6942e192b24SSimon Glass return 0; 6952e192b24SSimon Glass } 6962e192b24SSimon Glass if (strncmp(argv[1], "test", 4) == 0) { 6972e192b24SSimon Glass if (argc < 5) 6982e192b24SSimon Glass return CMD_RET_USAGE; 6992e192b24SSimon Glass i = simple_strtoul(argv[2], NULL, 10); 7002e192b24SSimon Glass udev = usb_find_device(i); 7012e192b24SSimon Glass if (udev == NULL) { 7022e192b24SSimon Glass printf("Device %d does not exist.\n", i); 7032e192b24SSimon Glass return 1; 7042e192b24SSimon Glass } 7052e192b24SSimon Glass i = simple_strtoul(argv[3], NULL, 10); 7062e192b24SSimon Glass return usb_test(udev, i, argv[4]); 7072e192b24SSimon Glass } 7082e192b24SSimon Glass #ifdef CONFIG_USB_STORAGE 7092e192b24SSimon Glass if (strncmp(argv[1], "stor", 4) == 0) 7102e192b24SSimon Glass return usb_stor_info(); 7112e192b24SSimon Glass 712*c16ad675SSimon Glass return blk_common_cmd(argc, argv, IF_TYPE_USB, &usb_stor_curr_dev); 713*c16ad675SSimon Glass #else 7142e192b24SSimon Glass return CMD_RET_USAGE; 715*c16ad675SSimon Glass #endif /* CONFIG_USB_STORAGE */ 7162e192b24SSimon Glass } 7172e192b24SSimon Glass 7182e192b24SSimon Glass U_BOOT_CMD( 7192e192b24SSimon Glass usb, 5, 1, do_usb, 7202e192b24SSimon Glass "USB sub-system", 7212e192b24SSimon Glass "start - start (scan) USB controller\n" 7222e192b24SSimon Glass "usb reset - reset (rescan) USB controller\n" 7232e192b24SSimon Glass "usb stop [f] - stop USB [f]=force stop\n" 7242e192b24SSimon Glass "usb tree - show USB device tree\n" 7252e192b24SSimon Glass "usb info [dev] - show available USB devices\n" 7262e192b24SSimon Glass "usb test [dev] [port] [mode] - set USB 2.0 test mode\n" 7272e192b24SSimon Glass " (specify port 0 to indicate the device's upstream port)\n" 7282e192b24SSimon Glass " Available modes: J, K, S[E0_NAK], P[acket], F[orce_Enable]\n" 7292e192b24SSimon Glass #ifdef CONFIG_USB_STORAGE 7302e192b24SSimon Glass "usb storage - show details of USB storage devices\n" 7312e192b24SSimon Glass "usb dev [dev] - show or set current USB storage device\n" 7322e192b24SSimon Glass "usb part [dev] - print partition table of one or all USB storage" 7332e192b24SSimon Glass " devices\n" 7342e192b24SSimon Glass "usb read addr blk# cnt - read `cnt' blocks starting at block `blk#'\n" 7352e192b24SSimon Glass " to memory address `addr'\n" 7362e192b24SSimon Glass "usb write addr blk# cnt - write `cnt' blocks starting at block `blk#'\n" 7372e192b24SSimon Glass " from memory address `addr'" 7382e192b24SSimon Glass #endif /* CONFIG_USB_STORAGE */ 7392e192b24SSimon Glass ); 7402e192b24SSimon Glass 7412e192b24SSimon Glass 7422e192b24SSimon Glass #ifdef CONFIG_USB_STORAGE 7432e192b24SSimon Glass U_BOOT_CMD( 7442e192b24SSimon Glass usbboot, 3, 1, do_usbboot, 7452e192b24SSimon Glass "boot from USB device", 7462e192b24SSimon Glass "loadAddr dev:part" 7472e192b24SSimon Glass ); 7482e192b24SSimon Glass #endif /* CONFIG_USB_STORAGE */ 749