usb.c 20 KB
Newer Older
wdenk's avatar
wdenk committed
1 2 3 4
/*
 * (C) Copyright 2001
 * Denis Peter, MPL AG Switzerland
 *
5 6 7
 * Adapted for U-Boot driver model
 * (C) Copyright 2015 Google, Inc
 *
wdenk's avatar
wdenk committed
8 9 10
 * Most of this source has been derived from the Linux USB
 * project.
 *
11
 * SPDX-License-Identifier:	GPL-2.0+
wdenk's avatar
wdenk committed
12 13 14 15
 */

#include <common.h>
#include <command.h>
16
#include <console.h>
17
#include <dm.h>
18
#include <dm/uclass-internal.h>
19
#include <memalign.h>
wdenk's avatar
wdenk committed
20
#include <asm/byteorder.h>
21
#include <asm/unaligned.h>
22
#include <part.h>
wdenk's avatar
wdenk committed
23 24
#include <usb.h>

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
wdenk's avatar
wdenk committed
31

32
/* some display routines (info command) */
33
static char *usb_get_class_desc(unsigned char dclass)
wdenk's avatar
wdenk committed
34
{
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
	switch (dclass) {
	case USB_CLASS_PER_INTERFACE:
		return "See Interface";
	case USB_CLASS_AUDIO:
		return "Audio";
	case USB_CLASS_COMM:
		return "Communication";
	case USB_CLASS_HID:
		return "Human Interface";
	case USB_CLASS_PRINTER:
		return "Printer";
	case USB_CLASS_MASS_STORAGE:
		return "Mass Storage";
	case USB_CLASS_HUB:
		return "Hub";
	case USB_CLASS_DATA:
		return "CDC Data";
	case USB_CLASS_VENDOR_SPEC:
		return "Vendor specific";
	default:
		return "";
wdenk's avatar
wdenk committed
56 57 58
	}
}

59 60
static void usb_display_class_sub(unsigned char dclass, unsigned char subclass,
				  unsigned char proto)
wdenk's avatar
wdenk committed
61
{
62 63 64 65 66 67 68 69 70
	switch (dclass) {
	case USB_CLASS_PER_INTERFACE:
		printf("See Interface");
		break;
	case USB_CLASS_HID:
		printf("Human Interface, Subclass: ");
		switch (subclass) {
		case USB_SUB_HID_NONE:
			printf("None");
wdenk's avatar
wdenk committed
71
			break;
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
		case USB_SUB_HID_BOOT:
			printf("Boot ");
			switch (proto) {
			case USB_PROT_HID_NONE:
				printf("None");
				break;
			case USB_PROT_HID_KEYBOARD:
				printf("Keyboard");
				break;
			case USB_PROT_HID_MOUSE:
				printf("Mouse");
				break;
			default:
				printf("reserved");
				break;
wdenk's avatar
wdenk committed
87 88
			}
			break;
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
		default:
			printf("reserved");
			break;
		}
		break;
	case USB_CLASS_MASS_STORAGE:
		printf("Mass Storage, ");
		switch (subclass) {
		case US_SC_RBC:
			printf("RBC ");
			break;
		case US_SC_8020:
			printf("SFF-8020i (ATAPI)");
			break;
		case US_SC_QIC:
			printf("QIC-157 (Tape)");
			break;
		case US_SC_UFI:
			printf("UFI");
			break;
		case US_SC_8070:
			printf("SFF-8070");
			break;
		case US_SC_SCSI:
			printf("Transp. SCSI");
			break;
		default:
			printf("reserved");
			break;
		}
		printf(", ");
		switch (proto) {
		case US_PR_CB:
			printf("Command/Bulk");
			break;
		case US_PR_CBI:
			printf("Command/Bulk/Int");
			break;
		case US_PR_BULK:
			printf("Bulk only");
wdenk's avatar
wdenk committed
129 130
			break;
		default:
131 132 133 134 135 136 137
			printf("reserved");
			break;
		}
		break;
	default:
		printf("%s", usb_get_class_desc(dclass));
		break;
wdenk's avatar
wdenk committed
138 139 140
	}
}

141
static void usb_display_string(struct usb_device *dev, int index)
wdenk's avatar
wdenk committed
142
{
143 144
	ALLOC_CACHE_ALIGN_BUFFER(char, buffer, 256);

145 146 147
	if (index != 0) {
		if (usb_string(dev, index, &buffer[0], 256) > 0)
			printf("String: \"%s\"", buffer);
wdenk's avatar
wdenk committed
148 149 150
	}
}

151
static void usb_display_desc(struct usb_device *dev)
wdenk's avatar
wdenk committed
152
{
153 154
	if (dev->descriptor.bDescriptorType == USB_DT_DEVICE) {
		printf("%d: %s,  USB Revision %x.%x\n", dev->devnum,
155
		usb_get_class_desc(dev->config.if_desc[0].desc.bInterfaceClass),
156 157 158 159 160 161 162
				   (dev->descriptor.bcdUSB>>8) & 0xff,
				   dev->descriptor.bcdUSB & 0xff);

		if (strlen(dev->mf) || strlen(dev->prod) ||
		    strlen(dev->serial))
			printf(" - %s %s %s\n", dev->mf, dev->prod,
				dev->serial);
wdenk's avatar
wdenk committed
163 164
		if (dev->descriptor.bDeviceClass) {
			printf(" - Class: ");
165 166 167
			usb_display_class_sub(dev->descriptor.bDeviceClass,
					      dev->descriptor.bDeviceSubClass,
					      dev->descriptor.bDeviceProtocol);
wdenk's avatar
wdenk committed
168
			printf("\n");
169 170 171
		} else {
			printf(" - Class: (from Interface) %s\n",
			       usb_get_class_desc(
172
				dev->config.if_desc[0].desc.bInterfaceClass));
wdenk's avatar
wdenk committed
173
		}
174 175 176 177 178 179 180
		printf(" - PacketSize: %d  Configurations: %d\n",
			dev->descriptor.bMaxPacketSize0,
			dev->descriptor.bNumConfigurations);
		printf(" - Vendor: 0x%04x  Product 0x%04x Version %d.%d\n",
			dev->descriptor.idVendor, dev->descriptor.idProduct,
			(dev->descriptor.bcdDevice>>8) & 0xff,
			dev->descriptor.bcdDevice & 0xff);
wdenk's avatar
wdenk committed
181 182 183 184
	}

}

185
static void usb_display_conf_desc(struct usb_config_descriptor *config,
186
				  struct usb_device *dev)
wdenk's avatar
wdenk committed
187
{
188 189 190 191
	printf("   Configuration: %d\n", config->bConfigurationValue);
	printf("   - Interfaces: %d %s%s%dmA\n", config->bNumInterfaces,
	       (config->bmAttributes & 0x40) ? "Self Powered " : "Bus Powered ",
	       (config->bmAttributes & 0x20) ? "Remote Wakeup " : "",
192
		config->bMaxPower*2);
wdenk's avatar
wdenk committed
193 194
	if (config->iConfiguration) {
		printf("   - ");
195
		usb_display_string(dev, config->iConfiguration);
wdenk's avatar
wdenk committed
196 197 198 199
		printf("\n");
	}
}

200 201
static void usb_display_if_desc(struct usb_interface_descriptor *ifdesc,
				struct usb_device *dev)
wdenk's avatar
wdenk committed
202
{
203 204 205
	printf("     Interface: %d\n", ifdesc->bInterfaceNumber);
	printf("     - Alternate Setting %d, Endpoints: %d\n",
		ifdesc->bAlternateSetting, ifdesc->bNumEndpoints);
wdenk's avatar
wdenk committed
206
	printf("     - Class ");
207 208
	usb_display_class_sub(ifdesc->bInterfaceClass,
		ifdesc->bInterfaceSubClass, ifdesc->bInterfaceProtocol);
wdenk's avatar
wdenk committed
209 210 211
	printf("\n");
	if (ifdesc->iInterface) {
		printf("     - ");
212
		usb_display_string(dev, ifdesc->iInterface);
wdenk's avatar
wdenk committed
213 214 215 216
		printf("\n");
	}
}

217
static void usb_display_ep_desc(struct usb_endpoint_descriptor *epdesc)
wdenk's avatar
wdenk committed
218
{
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
	printf("     - Endpoint %d %s ", epdesc->bEndpointAddress & 0xf,
		(epdesc->bEndpointAddress & 0x80) ? "In" : "Out");
	switch ((epdesc->bmAttributes & 0x03)) {
	case 0:
		printf("Control");
		break;
	case 1:
		printf("Isochronous");
		break;
	case 2:
		printf("Bulk");
		break;
	case 3:
		printf("Interrupt");
		break;
wdenk's avatar
wdenk committed
234
	}
235
	printf(" MaxPacket %d", get_unaligned(&epdesc->wMaxPacketSize));
236 237
	if ((epdesc->bmAttributes & 0x03) == 0x3)
		printf(" Interval %dms", epdesc->bInterval);
wdenk's avatar
wdenk committed
238 239 240 241
	printf("\n");
}

/* main routine to diasplay the configs, interfaces and endpoints */
242
static void usb_display_config(struct usb_device *dev)
wdenk's avatar
wdenk committed
243
{
244 245
	struct usb_config *config;
	struct usb_interface *ifdesc;
wdenk's avatar
wdenk committed
246
	struct usb_endpoint_descriptor *epdesc;
247 248 249
	int i, ii;

	config = &dev->config;
250
	usb_display_conf_desc(&config->desc, dev);
251 252
	for (i = 0; i < config->no_of_if; i++) {
		ifdesc = &config->if_desc[i];
253
		usb_display_if_desc(&ifdesc->desc, dev);
254 255
		for (ii = 0; ii < ifdesc->no_of_ep; ii++) {
			epdesc = &ifdesc->ep_desc[ii];
wdenk's avatar
wdenk committed
256 257 258 259 260 261
			usb_display_ep_desc(epdesc);
		}
	}
	printf("\n");
}

262 263 264 265 266
/*
 * With driver model this isn't right since we can have multiple controllers
 * and the device numbering starts at 1 on each bus.
 * TODO(sjg@chromium.org): Add a way to specify the controller/bus.
 */
267 268
static struct usb_device *usb_find_device(int devnum)
{
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
#ifdef CONFIG_DM_USB
	struct usb_device *udev;
	struct udevice *hub;
	struct uclass *uc;
	int ret;

	/* Device addresses start at 1 */
	devnum++;
	ret = uclass_get(UCLASS_USB_HUB, &uc);
	if (ret)
		return NULL;

	uclass_foreach_dev(hub, uc) {
		struct udevice *dev;

		if (!device_active(hub))
			continue;
286
		udev = dev_get_parent_priv(hub);
287 288 289 290 291 292 293 294 295
		if (udev->devnum == devnum)
			return udev;

		for (device_find_first_child(hub, &dev);
		     dev;
		     device_find_next_child(&dev)) {
			if (!device_active(hub))
				continue;

296
			udev = dev_get_parent_priv(dev);
297 298 299 300 301
			if (udev->devnum == devnum)
				return udev;
		}
	}
#else
302
	struct usb_device *udev;
303 304 305
	int d;

	for (d = 0; d < USB_MAX_DEVICE; d++) {
306 307
		udev = usb_get_dev_index(d);
		if (udev == NULL)
308
			return NULL;
309 310
		if (udev->devnum == devnum)
			return udev;
311
	}
312
#endif
313 314 315 316

	return NULL;
}

317 318
static inline char *portspeed(int speed)
{
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
	char *speed_str;

	switch (speed) {
	case USB_SPEED_SUPER:
		speed_str = "5 Gb/s";
		break;
	case USB_SPEED_HIGH:
		speed_str = "480 Mb/s";
		break;
	case USB_SPEED_LOW:
		speed_str = "1.5 Mb/s";
		break;
	default:
		speed_str = "12 Mb/s";
		break;
	}

	return speed_str;
337 338
}

wdenk's avatar
wdenk committed
339
/* shows the device tree recursively */
340
static void usb_show_tree_graph(struct usb_device *dev, char *pre)
wdenk's avatar
wdenk committed
341
{
342
	int index;
343
	int has_child, last_child;
wdenk's avatar
wdenk committed
344

345 346
	index = strlen(pre);
	printf(" %s", pre);
347 348 349
#ifdef CONFIG_DM_USB
	has_child = device_has_active_children(dev->dev);
#else
wdenk's avatar
wdenk committed
350
	/* check if the device has connected children */
351 352
	int i;

353 354 355 356
	has_child = 0;
	for (i = 0; i < dev->maxchild; i++) {
		if (dev->children[i] != NULL)
			has_child = 1;
wdenk's avatar
wdenk committed
357
	}
358
#endif
wdenk's avatar
wdenk committed
359
	/* check if we are the last one */
360
#ifdef CONFIG_DM_USB
361 362 363
	/* Not the root of the usb tree? */
	if (device_get_uclass_id(dev->dev->parent) != UCLASS_USB) {
		last_child = device_is_last_sibling(dev->dev);
364
#else
365 366
	if (dev->parent != NULL) { /* not root? */
		last_child = 1;
367
		for (i = 0; i < dev->parent->maxchild; i++) {
wdenk's avatar
wdenk committed
368
			/* search for children */
369 370 371 372 373 374
			if (dev->parent->children[i] == dev) {
				/* found our pointer, see if we have a
				 * little sister
				 */
				while (i++ < dev->parent->maxchild) {
					if (dev->parent->children[i] != NULL) {
wdenk's avatar
wdenk committed
375
						/* found a sister */
376
						last_child = 0;
wdenk's avatar
wdenk committed
377 378 379 380 381
						break;
					} /* if */
				} /* while */
			} /* device found */
		} /* for all children of the parent */
382
#endif
wdenk's avatar
wdenk committed
383 384
		printf("\b+-");
		/* correct last child */
385
		if (last_child && index)
386
			pre[index-1] = ' ';
wdenk's avatar
wdenk committed
387 388 389
	} /* if not root hub */
	else
		printf(" ");
390 391 392 393 394
	printf("%d ", dev->devnum);
	pre[index++] = ' ';
	pre[index++] = has_child ? '|' : ' ';
	pre[index] = 0;
	printf(" %s (%s, %dmA)\n", usb_get_class_desc(
395
					dev->config.if_desc[0].desc.bInterfaceClass),
396
					portspeed(dev->speed),
397
					dev->config.desc.bMaxPower * 2);
398 399 400
	if (strlen(dev->mf) || strlen(dev->prod) || strlen(dev->serial))
		printf(" %s  %s %s %s\n", pre, dev->mf, dev->prod, dev->serial);
	printf(" %s\n", pre);
401 402 403 404 405 406 407 408 409 410 411
#ifdef CONFIG_DM_USB
	struct udevice *child;

	for (device_find_first_child(dev->dev, &child);
	     child;
	     device_find_next_child(&child)) {
		struct usb_device *udev;

		if (!device_active(child))
			continue;

412
		udev = dev_get_parent_priv(child);
413 414 415 416 417 418 419 420

		/* Ignore emulators, we only want real devices */
		if (device_get_uclass_id(child) != UCLASS_USB_EMUL) {
			usb_show_tree_graph(udev, pre);
			pre[index] = 0;
		}
	}
#else
421 422 423 424 425
	if (dev->maxchild > 0) {
		for (i = 0; i < dev->maxchild; i++) {
			if (dev->children[i] != NULL) {
				usb_show_tree_graph(dev->children[i], pre);
				pre[index] = 0;
wdenk's avatar
wdenk committed
426 427 428
			}
		}
	}
429
#endif
wdenk's avatar
wdenk committed
430 431 432
}

/* main routine for the tree command */
433
static void usb_show_subtree(struct usb_device *dev)
wdenk's avatar
wdenk committed
434 435 436
{
	char preamble[32];

437
	memset(preamble, '\0', sizeof(preamble));
438
	usb_show_tree_graph(dev, &preamble[0]);
wdenk's avatar
wdenk committed
439 440
}

441 442 443 444 445
void usb_show_tree(void)
{
#ifdef CONFIG_DM_USB
	struct udevice *bus;

446
	for (uclass_find_first_device(UCLASS_USB, &bus);
447
		bus;
448
		uclass_find_next_device(&bus)) {
449 450 451
		struct usb_device *udev;
		struct udevice *dev;

452 453 454
		if (!device_active(bus))
			continue;

455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
		device_find_first_child(bus, &dev);
		if (dev && device_active(dev)) {
			udev = dev_get_parent_priv(dev);
			usb_show_subtree(udev);
		}
	}
#else
	struct usb_device *udev;
	int i;

	for (i = 0; i < USB_MAX_DEVICE; i++) {
		udev = usb_get_dev_index(i);
		if (udev == NULL)
			break;
		if (udev->parent == NULL)
			usb_show_subtree(udev);
	}
#endif
}

475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
static int usb_test(struct usb_device *dev, int port, char* arg)
{
	int mode;

	if (port > dev->maxchild) {
		printf("Device is no hub or does not have %d ports.\n", port);
		return 1;
	}

	switch (arg[0]) {
	case 'J':
	case 'j':
		printf("Setting Test_J mode");
		mode = USB_TEST_MODE_J;
		break;
	case 'K':
	case 'k':
		printf("Setting Test_K mode");
		mode = USB_TEST_MODE_K;
		break;
	case 'S':
	case 's':
		printf("Setting Test_SE0_NAK mode");
		mode = USB_TEST_MODE_SE0_NAK;
		break;
	case 'P':
	case 'p':
		printf("Setting Test_Packet mode");
		mode = USB_TEST_MODE_PACKET;
		break;
	case 'F':
	case 'f':
		printf("Setting Test_Force_Enable mode");
		mode = USB_TEST_MODE_FORCE_ENABLE;
		break;
	default:
		printf("Unrecognized test mode: %s\nAvailable modes: "
		       "J, K, S[E0_NAK], P[acket], F[orce_Enable]\n", arg);
		return 1;
	}

	if (port)
		printf(" on downstream facing port %d...\n", port);
	else
		printf(" on upstream facing port...\n");

	if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_FEATURE,
			    port ? USB_RT_PORT : USB_RECIP_DEVICE,
			    port ? USB_PORT_FEAT_TEST : USB_FEAT_TEST,
			    (mode << 8) | port,
			    NULL, 0, USB_CNTL_TIMEOUT) == -1) {
		printf("Error during SET_FEATURE.\n");
		return 1;
	} else {
		printf("Test mode successfully set. Use 'usb start' "
		       "to return to normal operation.\n");
		return 0;
	}
}

wdenk's avatar
wdenk committed
535 536 537 538 539

/******************************************************************************
 * usb boot command intepreter. Derived from diskboot
 */
#ifdef CONFIG_USB_STORAGE
540
static int do_usbboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
wdenk's avatar
wdenk committed
541
{
542
	return common_diskboot(cmdtp, "usb", argc, argv);
wdenk's avatar
wdenk committed
543 544 545
}
#endif /* CONFIG_USB_STORAGE */

546
static int do_usb_stop_keyboard(int force)
547
{
548
#if !defined CONFIG_DM_USB && defined CONFIG_USB_KEYBOARD
549
	if (usb_kbd_deregister(force) != 0) {
550 551 552 553 554 555
		printf("USB not stopped: usbkbd still using USB\n");
		return 1;
	}
#endif
	return 0;
}
wdenk's avatar
wdenk committed
556

557 558 559 560 561 562 563
static void do_usb_start(void)
{
	bootstage_mark_name(BOOTSTAGE_ID_USB_START, "usb_start");

	if (usb_init() < 0)
		return;

564 565
	/* Driver model will probe the devices as they are found */
#ifndef CONFIG_DM_USB
566
# ifdef CONFIG_USB_STORAGE
567 568
	/* try to recognize storage devices immediately */
	usb_stor_curr_dev = usb_stor_scan(1);
569 570 571 572 573
# endif
# ifdef CONFIG_USB_KEYBOARD
	drv_usb_kbd_init();
# endif
#endif /* !CONFIG_DM_USB */
574
#ifdef CONFIG_USB_HOST_ETHER
575
# ifdef CONFIG_DM_ETH
576 577 578 579
#  ifndef CONFIG_DM_USB
#   error "You must use CONFIG_DM_USB if you want to use CONFIG_USB_HOST_ETHER with CONFIG_DM_ETH"
#  endif
# else
580 581
	/* try to recognize ethernet devices immediately */
	usb_ether_curr_dev = usb_host_eth_scan(1);
582
# endif
583
#endif
584 585
}

586 587 588 589 590 591
#ifdef CONFIG_DM_USB
static void show_info(struct udevice *dev)
{
	struct udevice *child;
	struct usb_device *udev;

592
	udev = dev_get_parent_priv(dev);
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
	usb_display_desc(udev);
	usb_display_config(udev);
	for (device_find_first_child(dev, &child);
	     child;
	     device_find_next_child(&child)) {
		if (device_active(child))
			show_info(child);
	}
}

static int usb_device_info(void)
{
	struct udevice *bus;

	for (uclass_first_device(UCLASS_USB, &bus);
	     bus;
	     uclass_next_device(&bus)) {
		struct udevice *hub;

		device_find_first_child(bus, &hub);
		if (device_get_uclass_id(hub) == UCLASS_USB_HUB &&
		    device_active(hub)) {
			show_info(hub);
		}
	}

	return 0;
}
#endif

623
/******************************************************************************
wdenk's avatar
wdenk committed
624 625
 * usb command intepreter
 */
626
static int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
wdenk's avatar
wdenk committed
627
{
628
	struct usb_device *udev = NULL;
wdenk's avatar
wdenk committed
629
	int i;
630
	extern char usb_started;
631
#ifdef CONFIG_USB_STORAGE
632
	struct blk_desc *stor_dev;
633
#endif
wdenk's avatar
wdenk committed
634

635
	if (argc < 2)
636
		return CMD_RET_USAGE;
637

638 639 640 641 642 643 644 645 646 647
	if (strncmp(argv[1], "start", 5) == 0) {
		if (usb_started)
			return 0; /* Already started */
		printf("starting USB...\n");
		do_usb_start();
		return 0;
	}

	if (strncmp(argv[1], "reset", 5) == 0) {
		printf("resetting USB...\n");
648
		if (do_usb_stop_keyboard(1) != 0)
649
			return 1;
wdenk's avatar
wdenk committed
650
		usb_stop();
651
		do_usb_start();
wdenk's avatar
wdenk committed
652 653
		return 0;
	}
654
	if (strncmp(argv[1], "stop", 4) == 0) {
655
		if (argc != 2)
656
			console_assign(stdin, "serial");
657
		if (do_usb_stop_keyboard(0) != 0)
658
			return 1;
wdenk's avatar
wdenk committed
659 660 661 662
		printf("stopping USB..\n");
		usb_stop();
		return 0;
	}
663 664 665 666
	if (!usb_started) {
		printf("USB is stopped. Please issue 'usb start' first.\n");
		return 1;
	}
667
	if (strncmp(argv[1], "tree", 4) == 0) {
668
		puts("USB device tree:\n");
669
		usb_show_tree();
wdenk's avatar
wdenk committed
670 671
		return 0;
	}
672 673
	if (strncmp(argv[1], "inf", 3) == 0) {
		if (argc == 2) {
674 675 676 677
#ifdef CONFIG_DM_USB
			usb_device_info();
#else
			int d;
678
			for (d = 0; d < USB_MAX_DEVICE; d++) {
679 680
				udev = usb_get_dev_index(d);
				if (udev == NULL)
wdenk's avatar
wdenk committed
681
					break;
682 683
				usb_display_desc(udev);
				usb_display_config(udev);
wdenk's avatar
wdenk committed
684
			}
685
#endif
wdenk's avatar
wdenk committed
686
			return 0;
687
		} else {
688 689 690 691 692
			/*
			 * With driver model this isn't right since we can
			 * have multiple controllers and the device numbering
			 * starts at 1 on each bus.
			 */
693
			i = simple_strtoul(argv[2], NULL, 10);
694
			printf("config for device %d\n", i);
695 696
			udev = usb_find_device(i);
			if (udev == NULL) {
697
				printf("*** No device available ***\n");
wdenk's avatar
wdenk committed
698
				return 0;
699
			} else {
700 701
				usb_display_desc(udev);
				usb_display_config(udev);
wdenk's avatar
wdenk committed
702 703 704 705
			}
		}
		return 0;
	}
706 707 708 709
	if (strncmp(argv[1], "test", 4) == 0) {
		if (argc < 5)
			return CMD_RET_USAGE;
		i = simple_strtoul(argv[2], NULL, 10);
710 711
		udev = usb_find_device(i);
		if (udev == NULL) {
712 713 714 715
			printf("Device %d does not exist.\n", i);
			return 1;
		}
		i = simple_strtoul(argv[3], NULL, 10);
716
		return usb_test(udev, i, argv[4]);
717
	}
wdenk's avatar
wdenk committed
718
#ifdef CONFIG_USB_STORAGE
719
	if (strncmp(argv[1], "stor", 4) == 0)
720
		return usb_stor_info();
721

722
	if (strncmp(argv[1], "part", 4) == 0) {
723
		int devno, ok = 0;
724
		if (argc == 2) {
Kim B. Heino's avatar
Kim B. Heino committed
725
			for (devno = 0; ; ++devno) {
726
				stor_dev = usb_stor_get_dev(devno);
Kim B. Heino's avatar
Kim B. Heino committed
727 728
				if (stor_dev == NULL)
					break;
729
				if (stor_dev->type != DEV_TYPE_UNKNOWN) {
730 731 732
					ok++;
					if (devno)
						printf("\n");
733
					debug("print_part of %x\n", devno);
734
					part_print(stor_dev);
735 736
				}
			}
737 738 739
		} else {
			devno = simple_strtoul(argv[2], NULL, 16);
			stor_dev = usb_stor_get_dev(devno);
Kim B. Heino's avatar
Kim B. Heino committed
740 741
			if (stor_dev != NULL &&
			    stor_dev->type != DEV_TYPE_UNKNOWN) {
wdenk's avatar
wdenk committed
742
				ok++;
743
				debug("print_part of %x\n", devno);
744
				part_print(stor_dev);
wdenk's avatar
wdenk committed
745 746 747 748 749 750 751 752
			}
		}
		if (!ok) {
			printf("\nno USB devices available\n");
			return 1;
		}
		return 0;
	}
753 754
	if (strcmp(argv[1], "read") == 0) {
		if (usb_stor_curr_dev < 0) {
wdenk's avatar
wdenk committed
755 756 757
			printf("no current device selected\n");
			return 1;
		}
758
		if (argc == 5) {
wdenk's avatar
wdenk committed
759 760 761 762
			unsigned long addr = simple_strtoul(argv[2], NULL, 16);
			unsigned long blk  = simple_strtoul(argv[3], NULL, 16);
			unsigned long cnt  = simple_strtoul(argv[4], NULL, 16);
			unsigned long n;
763 764 765
			printf("\nUSB read: device %d block # %ld, count %ld"
				" ... ", usb_stor_curr_dev, blk, cnt);
			stor_dev = usb_stor_get_dev(usb_stor_curr_dev);
766
			n = blk_dread(stor_dev, blk, cnt, (ulong *)addr);
767 768 769
			printf("%ld blocks read: %s\n", n,
				(n == cnt) ? "OK" : "ERROR");
			if (n == cnt)
wdenk's avatar
wdenk committed
770 771 772 773
				return 0;
			return 1;
		}
	}
774 775 776 777 778 779 780 781 782 783 784 785 786
	if (strcmp(argv[1], "write") == 0) {
		if (usb_stor_curr_dev < 0) {
			printf("no current device selected\n");
			return 1;
		}
		if (argc == 5) {
			unsigned long addr = simple_strtoul(argv[2], NULL, 16);
			unsigned long blk  = simple_strtoul(argv[3], NULL, 16);
			unsigned long cnt  = simple_strtoul(argv[4], NULL, 16);
			unsigned long n;
			printf("\nUSB write: device %d block # %ld, count %ld"
				" ... ", usb_stor_curr_dev, blk, cnt);
			stor_dev = usb_stor_get_dev(usb_stor_curr_dev);
787
			n = blk_dwrite(stor_dev, blk, cnt, (ulong *)addr);
788 789 790 791 792 793 794
			printf("%ld blocks write: %s\n", n,
				(n == cnt) ? "OK" : "ERROR");
			if (n == cnt)
				return 0;
			return 1;
		}
	}
795 796
	if (strncmp(argv[1], "dev", 3) == 0) {
		if (argc == 3) {
wdenk's avatar
wdenk committed
797
			int dev = (int)simple_strtoul(argv[2], NULL, 10);
798
			printf("\nUSB device %d: ", dev);
Kim B. Heino's avatar
Kim B. Heino committed
799 800
			stor_dev = usb_stor_get_dev(dev);
			if (stor_dev == NULL) {
wdenk's avatar
wdenk committed
801 802 803
				printf("unknown device\n");
				return 1;
			}
804
			printf("\n    Device %d: ", dev);
wdenk's avatar
wdenk committed
805
			dev_print(stor_dev);
806
			if (stor_dev->type == DEV_TYPE_UNKNOWN)
wdenk's avatar
wdenk committed
807 808 809 810
				return 1;
			usb_stor_curr_dev = dev;
			printf("... is now current device\n");
			return 0;
811 812 813
		} else {
			printf("\nUSB device %d: ", usb_stor_curr_dev);
			stor_dev = usb_stor_get_dev(usb_stor_curr_dev);
wdenk's avatar
wdenk committed
814
			dev_print(stor_dev);
815
			if (stor_dev->type == DEV_TYPE_UNKNOWN)
wdenk's avatar
wdenk committed
816 817 818 819 820 821
				return 1;
			return 0;
		}
		return 0;
	}
#endif /* CONFIG_USB_STORAGE */
822
	return CMD_RET_USAGE;
wdenk's avatar
wdenk committed
823 824
}

825 826
U_BOOT_CMD(
	usb,	5,	1,	do_usb,
Peter Tyser's avatar
Peter Tyser committed
827
	"USB sub-system",
828 829
	"start - start (scan) USB controller\n"
	"usb reset - reset (rescan) USB controller\n"
830 831
	"usb stop [f] - stop USB [f]=force stop\n"
	"usb tree - show USB device tree\n"
832
	"usb info [dev] - show available USB devices\n"
833 834 835 836
	"usb test [dev] [port] [mode] - set USB 2.0 test mode\n"
	"    (specify port 0 to indicate the device's upstream port)\n"
	"    Available modes: J, K, S[E0_NAK], P[acket], F[orce_Enable]\n"
#ifdef CONFIG_USB_STORAGE
837
	"usb storage - show details of USB storage devices\n"
838
	"usb dev [dev] - show or set current USB storage device\n"
839
	"usb part [dev] - print partition table of one or all USB storage"
840
	"    devices\n"
841
	"usb read addr blk# cnt - read `cnt' blocks starting at block `blk#'\n"
842
	"    to memory address `addr'\n"
843 844
	"usb write addr blk# cnt - write `cnt' blocks starting at block `blk#'\n"
	"    from memory address `addr'"
845
#endif /* CONFIG_USB_STORAGE */
wdenk's avatar
wdenk committed
846 847 848
);


849
#ifdef CONFIG_USB_STORAGE
850 851
U_BOOT_CMD(
	usbboot,	3,	1,	do_usbboot,
Peter Tyser's avatar
Peter Tyser committed
852
	"boot from USB device",
853
	"loadAddr dev:part"
wdenk's avatar
wdenk committed
854
);
855
#endif /* CONFIG_USB_STORAGE */