1 /* 2 * (C) Copyright 2001 3 * Denis Peter, MPL AG Switzerland 4 * 5 * Adapted for U-Boot driver model 6 * (C) Copyright 2015 Google, Inc 7 * 8 * Most of this source has been derived from the Linux USB 9 * project. 10 * 11 * SPDX-License-Identifier: GPL-2.0+ 12 */ 13 14 #include <common.h> 15 #include <command.h> 16 #include <console.h> 17 #include <dm.h> 18 #include <dm/uclass-internal.h> 19 #include <memalign.h> 20 #include <asm/byteorder.h> 21 #include <asm/unaligned.h> 22 #include <part.h> 23 #include <usb.h> 24 25 #ifdef CONFIG_USB_STORAGE 26 static int usb_stor_curr_dev = -1; /* current device */ 27 #endif 28 #if defined(CONFIG_USB_HOST_ETHER) && !defined(CONFIG_DM_ETH) 29 static int __maybe_unused usb_ether_curr_dev = -1; /* current ethernet device */ 30 #endif 31 32 /* some display routines (info command) */ 33 static char *usb_get_class_desc(unsigned char dclass) 34 { 35 switch (dclass) { 36 case USB_CLASS_PER_INTERFACE: 37 return "See Interface"; 38 case USB_CLASS_AUDIO: 39 return "Audio"; 40 case USB_CLASS_COMM: 41 return "Communication"; 42 case USB_CLASS_HID: 43 return "Human Interface"; 44 case USB_CLASS_PRINTER: 45 return "Printer"; 46 case USB_CLASS_MASS_STORAGE: 47 return "Mass Storage"; 48 case USB_CLASS_HUB: 49 return "Hub"; 50 case USB_CLASS_DATA: 51 return "CDC Data"; 52 case USB_CLASS_VENDOR_SPEC: 53 return "Vendor specific"; 54 default: 55 return ""; 56 } 57 } 58 59 static void usb_display_class_sub(unsigned char dclass, unsigned char subclass, 60 unsigned char proto) 61 { 62 switch (dclass) { 63 case USB_CLASS_PER_INTERFACE: 64 printf("See Interface"); 65 break; 66 case USB_CLASS_HID: 67 printf("Human Interface, Subclass: "); 68 switch (subclass) { 69 case USB_SUB_HID_NONE: 70 printf("None"); 71 break; 72 case USB_SUB_HID_BOOT: 73 printf("Boot "); 74 switch (proto) { 75 case USB_PROT_HID_NONE: 76 printf("None"); 77 break; 78 case USB_PROT_HID_KEYBOARD: 79 printf("Keyboard"); 80 break; 81 case USB_PROT_HID_MOUSE: 82 printf("Mouse"); 83 break; 84 default: 85 printf("reserved"); 86 break; 87 } 88 break; 89 default: 90 printf("reserved"); 91 break; 92 } 93 break; 94 case USB_CLASS_MASS_STORAGE: 95 printf("Mass Storage, "); 96 switch (subclass) { 97 case US_SC_RBC: 98 printf("RBC "); 99 break; 100 case US_SC_8020: 101 printf("SFF-8020i (ATAPI)"); 102 break; 103 case US_SC_QIC: 104 printf("QIC-157 (Tape)"); 105 break; 106 case US_SC_UFI: 107 printf("UFI"); 108 break; 109 case US_SC_8070: 110 printf("SFF-8070"); 111 break; 112 case US_SC_SCSI: 113 printf("Transp. SCSI"); 114 break; 115 default: 116 printf("reserved"); 117 break; 118 } 119 printf(", "); 120 switch (proto) { 121 case US_PR_CB: 122 printf("Command/Bulk"); 123 break; 124 case US_PR_CBI: 125 printf("Command/Bulk/Int"); 126 break; 127 case US_PR_BULK: 128 printf("Bulk only"); 129 break; 130 default: 131 printf("reserved"); 132 break; 133 } 134 break; 135 default: 136 printf("%s", usb_get_class_desc(dclass)); 137 break; 138 } 139 } 140 141 static void usb_display_string(struct usb_device *dev, int index) 142 { 143 ALLOC_CACHE_ALIGN_BUFFER(char, buffer, 256); 144 145 if (index != 0) { 146 if (usb_string(dev, index, &buffer[0], 256) > 0) 147 printf("String: \"%s\"", buffer); 148 } 149 } 150 151 static void usb_display_desc(struct usb_device *dev) 152 { 153 uint packet_size = dev->descriptor.bMaxPacketSize0; 154 155 if (dev->descriptor.bDescriptorType == USB_DT_DEVICE) { 156 printf("%d: %s, USB Revision %x.%x\n", dev->devnum, 157 usb_get_class_desc(dev->config.if_desc[0].desc.bInterfaceClass), 158 (dev->descriptor.bcdUSB>>8) & 0xff, 159 dev->descriptor.bcdUSB & 0xff); 160 161 if (strlen(dev->mf) || strlen(dev->prod) || 162 strlen(dev->serial)) 163 printf(" - %s %s %s\n", dev->mf, dev->prod, 164 dev->serial); 165 if (dev->descriptor.bDeviceClass) { 166 printf(" - Class: "); 167 usb_display_class_sub(dev->descriptor.bDeviceClass, 168 dev->descriptor.bDeviceSubClass, 169 dev->descriptor.bDeviceProtocol); 170 printf("\n"); 171 } else { 172 printf(" - Class: (from Interface) %s\n", 173 usb_get_class_desc( 174 dev->config.if_desc[0].desc.bInterfaceClass)); 175 } 176 if (dev->descriptor.bcdUSB >= cpu_to_le16(0x0300)) 177 packet_size = 1 << packet_size; 178 printf(" - PacketSize: %d Configurations: %d\n", 179 packet_size, dev->descriptor.bNumConfigurations); 180 printf(" - Vendor: 0x%04x Product 0x%04x Version %d.%d\n", 181 dev->descriptor.idVendor, dev->descriptor.idProduct, 182 (dev->descriptor.bcdDevice>>8) & 0xff, 183 dev->descriptor.bcdDevice & 0xff); 184 } 185 186 } 187 188 static void usb_display_conf_desc(struct usb_config_descriptor *config, 189 struct usb_device *dev) 190 { 191 printf(" Configuration: %d\n", config->bConfigurationValue); 192 printf(" - Interfaces: %d %s%s%dmA\n", config->bNumInterfaces, 193 (config->bmAttributes & 0x40) ? "Self Powered " : "Bus Powered ", 194 (config->bmAttributes & 0x20) ? "Remote Wakeup " : "", 195 config->bMaxPower*2); 196 if (config->iConfiguration) { 197 printf(" - "); 198 usb_display_string(dev, config->iConfiguration); 199 printf("\n"); 200 } 201 } 202 203 static void usb_display_if_desc(struct usb_interface_descriptor *ifdesc, 204 struct usb_device *dev) 205 { 206 printf(" Interface: %d\n", ifdesc->bInterfaceNumber); 207 printf(" - Alternate Setting %d, Endpoints: %d\n", 208 ifdesc->bAlternateSetting, ifdesc->bNumEndpoints); 209 printf(" - Class "); 210 usb_display_class_sub(ifdesc->bInterfaceClass, 211 ifdesc->bInterfaceSubClass, ifdesc->bInterfaceProtocol); 212 printf("\n"); 213 if (ifdesc->iInterface) { 214 printf(" - "); 215 usb_display_string(dev, ifdesc->iInterface); 216 printf("\n"); 217 } 218 } 219 220 static void usb_display_ep_desc(struct usb_endpoint_descriptor *epdesc) 221 { 222 printf(" - Endpoint %d %s ", epdesc->bEndpointAddress & 0xf, 223 (epdesc->bEndpointAddress & 0x80) ? "In" : "Out"); 224 switch ((epdesc->bmAttributes & 0x03)) { 225 case 0: 226 printf("Control"); 227 break; 228 case 1: 229 printf("Isochronous"); 230 break; 231 case 2: 232 printf("Bulk"); 233 break; 234 case 3: 235 printf("Interrupt"); 236 break; 237 } 238 printf(" MaxPacket %d", get_unaligned(&epdesc->wMaxPacketSize)); 239 if ((epdesc->bmAttributes & 0x03) == 0x3) 240 printf(" Interval %dms", epdesc->bInterval); 241 printf("\n"); 242 } 243 244 /* main routine to diasplay the configs, interfaces and endpoints */ 245 static void usb_display_config(struct usb_device *dev) 246 { 247 struct usb_config *config; 248 struct usb_interface *ifdesc; 249 struct usb_endpoint_descriptor *epdesc; 250 int i, ii; 251 252 config = &dev->config; 253 usb_display_conf_desc(&config->desc, dev); 254 for (i = 0; i < config->no_of_if; i++) { 255 ifdesc = &config->if_desc[i]; 256 usb_display_if_desc(&ifdesc->desc, dev); 257 for (ii = 0; ii < ifdesc->no_of_ep; ii++) { 258 epdesc = &ifdesc->ep_desc[ii]; 259 usb_display_ep_desc(epdesc); 260 } 261 } 262 printf("\n"); 263 } 264 265 /* 266 * With driver model this isn't right since we can have multiple controllers 267 * and the device numbering starts at 1 on each bus. 268 * TODO(sjg@chromium.org): Add a way to specify the controller/bus. 269 */ 270 static struct usb_device *usb_find_device(int devnum) 271 { 272 #ifdef CONFIG_DM_USB 273 struct usb_device *udev; 274 struct udevice *hub; 275 struct uclass *uc; 276 int ret; 277 278 /* Device addresses start at 1 */ 279 devnum++; 280 ret = uclass_get(UCLASS_USB_HUB, &uc); 281 if (ret) 282 return NULL; 283 284 uclass_foreach_dev(hub, uc) { 285 struct udevice *dev; 286 287 if (!device_active(hub)) 288 continue; 289 udev = dev_get_parent_priv(hub); 290 if (udev->devnum == devnum) 291 return udev; 292 293 for (device_find_first_child(hub, &dev); 294 dev; 295 device_find_next_child(&dev)) { 296 if (!device_active(hub)) 297 continue; 298 299 udev = dev_get_parent_priv(dev); 300 if (udev->devnum == devnum) 301 return udev; 302 } 303 } 304 #else 305 struct usb_device *udev; 306 int d; 307 308 for (d = 0; d < USB_MAX_DEVICE; d++) { 309 udev = usb_get_dev_index(d); 310 if (udev == NULL) 311 return NULL; 312 if (udev->devnum == devnum) 313 return udev; 314 } 315 #endif 316 317 return NULL; 318 } 319 320 static inline char *portspeed(int speed) 321 { 322 char *speed_str; 323 324 switch (speed) { 325 case USB_SPEED_SUPER: 326 speed_str = "5 Gb/s"; 327 break; 328 case USB_SPEED_HIGH: 329 speed_str = "480 Mb/s"; 330 break; 331 case USB_SPEED_LOW: 332 speed_str = "1.5 Mb/s"; 333 break; 334 default: 335 speed_str = "12 Mb/s"; 336 break; 337 } 338 339 return speed_str; 340 } 341 342 /* shows the device tree recursively */ 343 static void usb_show_tree_graph(struct usb_device *dev, char *pre) 344 { 345 int index; 346 int has_child, last_child; 347 348 index = strlen(pre); 349 printf(" %s", pre); 350 #ifdef CONFIG_DM_USB 351 has_child = device_has_active_children(dev->dev); 352 if (device_get_uclass_id(dev->dev) == UCLASS_MASS_STORAGE) { 353 struct udevice *child; 354 355 for (device_find_first_child(dev->dev, &child); 356 child; 357 device_find_next_child(&child)) { 358 if (device_get_uclass_id(child) == UCLASS_BLK) 359 has_child = 0; 360 } 361 } 362 #else 363 /* check if the device has connected children */ 364 int i; 365 366 has_child = 0; 367 for (i = 0; i < dev->maxchild; i++) { 368 if (dev->children[i] != NULL) 369 has_child = 1; 370 } 371 #endif 372 /* check if we are the last one */ 373 #ifdef CONFIG_DM_USB 374 /* Not the root of the usb tree? */ 375 if (device_get_uclass_id(dev->dev->parent) != UCLASS_USB) { 376 last_child = device_is_last_sibling(dev->dev); 377 #else 378 if (dev->parent != NULL) { /* not root? */ 379 last_child = 1; 380 for (i = 0; i < dev->parent->maxchild; i++) { 381 /* search for children */ 382 if (dev->parent->children[i] == dev) { 383 /* found our pointer, see if we have a 384 * little sister 385 */ 386 while (i++ < dev->parent->maxchild) { 387 if (dev->parent->children[i] != NULL) { 388 /* found a sister */ 389 last_child = 0; 390 break; 391 } /* if */ 392 } /* while */ 393 } /* device found */ 394 } /* for all children of the parent */ 395 #endif 396 printf("\b+-"); 397 /* correct last child */ 398 if (last_child && index) 399 pre[index-1] = ' '; 400 } /* if not root hub */ 401 else 402 printf(" "); 403 printf("%d ", dev->devnum); 404 pre[index++] = ' '; 405 pre[index++] = has_child ? '|' : ' '; 406 pre[index] = 0; 407 printf(" %s (%s, %dmA)\n", usb_get_class_desc( 408 dev->config.if_desc[0].desc.bInterfaceClass), 409 portspeed(dev->speed), 410 dev->config.desc.bMaxPower * 2); 411 if (strlen(dev->mf) || strlen(dev->prod) || strlen(dev->serial)) 412 printf(" %s %s %s %s\n", pre, dev->mf, dev->prod, dev->serial); 413 printf(" %s\n", pre); 414 #ifdef CONFIG_DM_USB 415 struct udevice *child; 416 417 for (device_find_first_child(dev->dev, &child); 418 child; 419 device_find_next_child(&child)) { 420 struct usb_device *udev; 421 422 if (!device_active(child)) 423 continue; 424 425 udev = dev_get_parent_priv(child); 426 427 /* 428 * Ignore emulators and block child devices, we only want 429 * real devices 430 */ 431 if ((device_get_uclass_id(child) != UCLASS_USB_EMUL) && 432 (device_get_uclass_id(child) != UCLASS_BLK)) { 433 usb_show_tree_graph(udev, pre); 434 pre[index] = 0; 435 } 436 } 437 #else 438 if (dev->maxchild > 0) { 439 for (i = 0; i < dev->maxchild; i++) { 440 if (dev->children[i] != NULL) { 441 usb_show_tree_graph(dev->children[i], pre); 442 pre[index] = 0; 443 } 444 } 445 } 446 #endif 447 } 448 449 /* main routine for the tree command */ 450 static void usb_show_subtree(struct usb_device *dev) 451 { 452 char preamble[32]; 453 454 memset(preamble, '\0', sizeof(preamble)); 455 usb_show_tree_graph(dev, &preamble[0]); 456 } 457 458 #ifdef CONFIG_DM_USB 459 typedef void (*usb_dev_func_t)(struct usb_device *udev); 460 461 static void usb_for_each_root_dev(usb_dev_func_t func) 462 { 463 struct udevice *bus; 464 465 for (uclass_find_first_device(UCLASS_USB, &bus); 466 bus; 467 uclass_find_next_device(&bus)) { 468 struct usb_device *udev; 469 struct udevice *dev; 470 471 if (!device_active(bus)) 472 continue; 473 474 device_find_first_child(bus, &dev); 475 if (dev && device_active(dev)) { 476 udev = dev_get_parent_priv(dev); 477 func(udev); 478 } 479 } 480 } 481 #endif 482 483 void usb_show_tree(void) 484 { 485 #ifdef CONFIG_DM_USB 486 usb_for_each_root_dev(usb_show_subtree); 487 #else 488 struct usb_device *udev; 489 int i; 490 491 for (i = 0; i < USB_MAX_DEVICE; i++) { 492 udev = usb_get_dev_index(i); 493 if (udev == NULL) 494 break; 495 if (udev->parent == NULL) 496 usb_show_subtree(udev); 497 } 498 #endif 499 } 500 501 static int usb_test(struct usb_device *dev, int port, char* arg) 502 { 503 int mode; 504 505 if (port > dev->maxchild) { 506 printf("Device is no hub or does not have %d ports.\n", port); 507 return 1; 508 } 509 510 switch (arg[0]) { 511 case 'J': 512 case 'j': 513 printf("Setting Test_J mode"); 514 mode = USB_TEST_MODE_J; 515 break; 516 case 'K': 517 case 'k': 518 printf("Setting Test_K mode"); 519 mode = USB_TEST_MODE_K; 520 break; 521 case 'S': 522 case 's': 523 printf("Setting Test_SE0_NAK mode"); 524 mode = USB_TEST_MODE_SE0_NAK; 525 break; 526 case 'P': 527 case 'p': 528 printf("Setting Test_Packet mode"); 529 mode = USB_TEST_MODE_PACKET; 530 break; 531 case 'F': 532 case 'f': 533 printf("Setting Test_Force_Enable mode"); 534 mode = USB_TEST_MODE_FORCE_ENABLE; 535 break; 536 default: 537 printf("Unrecognized test mode: %s\nAvailable modes: " 538 "J, K, S[E0_NAK], P[acket], F[orce_Enable]\n", arg); 539 return 1; 540 } 541 542 if (port) 543 printf(" on downstream facing port %d...\n", port); 544 else 545 printf(" on upstream facing port...\n"); 546 547 if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_FEATURE, 548 port ? USB_RT_PORT : USB_RECIP_DEVICE, 549 port ? USB_PORT_FEAT_TEST : USB_FEAT_TEST, 550 (mode << 8) | port, 551 NULL, 0, USB_CNTL_TIMEOUT) == -1) { 552 printf("Error during SET_FEATURE.\n"); 553 return 1; 554 } else { 555 printf("Test mode successfully set. Use 'usb start' " 556 "to return to normal operation.\n"); 557 return 0; 558 } 559 } 560 561 562 /****************************************************************************** 563 * usb boot command intepreter. Derived from diskboot 564 */ 565 #ifdef CONFIG_USB_STORAGE 566 static int do_usbboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 567 { 568 return common_diskboot(cmdtp, "usb", argc, argv); 569 } 570 #endif /* CONFIG_USB_STORAGE */ 571 572 static int do_usb_stop_keyboard(int force) 573 { 574 #if !defined CONFIG_DM_USB && defined CONFIG_USB_KEYBOARD 575 if (usb_kbd_deregister(force) != 0) { 576 printf("USB not stopped: usbkbd still using USB\n"); 577 return 1; 578 } 579 #endif 580 return 0; 581 } 582 583 static void do_usb_start(void) 584 { 585 bootstage_mark_name(BOOTSTAGE_ID_USB_START, "usb_start"); 586 587 if (usb_init() < 0) 588 return; 589 590 /* Driver model will probe the devices as they are found */ 591 # ifdef CONFIG_USB_STORAGE 592 /* try to recognize storage devices immediately */ 593 usb_stor_curr_dev = usb_stor_scan(1); 594 # endif 595 #ifndef CONFIG_DM_USB 596 # ifdef CONFIG_USB_KEYBOARD 597 drv_usb_kbd_init(); 598 # endif 599 #endif /* !CONFIG_DM_USB */ 600 #ifdef CONFIG_USB_HOST_ETHER 601 # ifdef CONFIG_DM_ETH 602 # ifndef CONFIG_DM_USB 603 # error "You must use CONFIG_DM_USB if you want to use CONFIG_USB_HOST_ETHER with CONFIG_DM_ETH" 604 # endif 605 # else 606 /* try to recognize ethernet devices immediately */ 607 usb_ether_curr_dev = usb_host_eth_scan(1); 608 # endif 609 #endif 610 } 611 612 #ifdef CONFIG_DM_USB 613 static void usb_show_info(struct usb_device *udev) 614 { 615 struct udevice *child; 616 617 usb_display_desc(udev); 618 usb_display_config(udev); 619 for (device_find_first_child(udev->dev, &child); 620 child; 621 device_find_next_child(&child)) { 622 if (device_active(child) && 623 (device_get_uclass_id(child) != UCLASS_USB_EMUL) && 624 (device_get_uclass_id(child) != UCLASS_BLK)) { 625 udev = dev_get_parent_priv(child); 626 usb_show_info(udev); 627 } 628 } 629 } 630 #endif 631 632 /****************************************************************************** 633 * usb command intepreter 634 */ 635 static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 636 { 637 struct usb_device *udev = NULL; 638 int i; 639 extern char usb_started; 640 641 if (argc < 2) 642 return CMD_RET_USAGE; 643 644 if (strncmp(argv[1], "start", 5) == 0) { 645 if (usb_started) 646 return 0; /* Already started */ 647 printf("starting USB...\n"); 648 do_usb_start(); 649 return 0; 650 } 651 652 if (strncmp(argv[1], "reset", 5) == 0) { 653 printf("resetting USB...\n"); 654 if (do_usb_stop_keyboard(1) != 0) 655 return 1; 656 usb_stop(); 657 do_usb_start(); 658 return 0; 659 } 660 if (strncmp(argv[1], "stop", 4) == 0) { 661 if (argc != 2) 662 console_assign(stdin, "serial"); 663 if (do_usb_stop_keyboard(0) != 0) 664 return 1; 665 printf("stopping USB..\n"); 666 usb_stop(); 667 return 0; 668 } 669 if (!usb_started) { 670 printf("USB is stopped. Please issue 'usb start' first.\n"); 671 return 1; 672 } 673 if (strncmp(argv[1], "tree", 4) == 0) { 674 puts("USB device tree:\n"); 675 usb_show_tree(); 676 return 0; 677 } 678 if (strncmp(argv[1], "inf", 3) == 0) { 679 if (argc == 2) { 680 #ifdef CONFIG_DM_USB 681 usb_for_each_root_dev(usb_show_info); 682 #else 683 int d; 684 for (d = 0; d < USB_MAX_DEVICE; d++) { 685 udev = usb_get_dev_index(d); 686 if (udev == NULL) 687 break; 688 usb_display_desc(udev); 689 usb_display_config(udev); 690 } 691 #endif 692 return 0; 693 } else { 694 /* 695 * With driver model this isn't right since we can 696 * have multiple controllers and the device numbering 697 * starts at 1 on each bus. 698 */ 699 i = simple_strtoul(argv[2], NULL, 10); 700 printf("config for device %d\n", i); 701 udev = usb_find_device(i); 702 if (udev == NULL) { 703 printf("*** No device available ***\n"); 704 return 0; 705 } else { 706 usb_display_desc(udev); 707 usb_display_config(udev); 708 } 709 } 710 return 0; 711 } 712 if (strncmp(argv[1], "test", 4) == 0) { 713 if (argc < 5) 714 return CMD_RET_USAGE; 715 i = simple_strtoul(argv[2], NULL, 10); 716 udev = usb_find_device(i); 717 if (udev == NULL) { 718 printf("Device %d does not exist.\n", i); 719 return 1; 720 } 721 i = simple_strtoul(argv[3], NULL, 10); 722 return usb_test(udev, i, argv[4]); 723 } 724 #ifdef CONFIG_USB_STORAGE 725 if (strncmp(argv[1], "stor", 4) == 0) 726 return usb_stor_info(); 727 728 return blk_common_cmd(argc, argv, IF_TYPE_USB, &usb_stor_curr_dev); 729 #else 730 return CMD_RET_USAGE; 731 #endif /* CONFIG_USB_STORAGE */ 732 } 733 734 U_BOOT_CMD( 735 usb, 5, 1, do_usb, 736 "USB sub-system", 737 "start - start (scan) USB controller\n" 738 "usb reset - reset (rescan) USB controller\n" 739 "usb stop [f] - stop USB [f]=force stop\n" 740 "usb tree - show USB device tree\n" 741 "usb info [dev] - show available USB devices\n" 742 "usb test [dev] [port] [mode] - set USB 2.0 test mode\n" 743 " (specify port 0 to indicate the device's upstream port)\n" 744 " Available modes: J, K, S[E0_NAK], P[acket], F[orce_Enable]\n" 745 #ifdef CONFIG_USB_STORAGE 746 "usb storage - show details of USB storage devices\n" 747 "usb dev [dev] - show or set current USB storage device\n" 748 "usb part [dev] - print partition table of one or all USB storage" 749 " devices\n" 750 "usb read addr blk# cnt - read `cnt' blocks starting at block `blk#'\n" 751 " to memory address `addr'\n" 752 "usb write addr blk# cnt - write `cnt' blocks starting at block `blk#'\n" 753 " from memory address `addr'" 754 #endif /* CONFIG_USB_STORAGE */ 755 ); 756 757 758 #ifdef CONFIG_USB_STORAGE 759 U_BOOT_CMD( 760 usbboot, 3, 1, do_usbboot, 761 "boot from USB device", 762 "loadAddr dev:part" 763 ); 764 #endif /* CONFIG_USB_STORAGE */ 765