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