439 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			439 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /*
 | |
|  * Copyright (C) 2015 Google, Inc
 | |
|  */
 | |
| 
 | |
| #include <common.h>
 | |
| #include <console.h>
 | |
| #include <dm.h>
 | |
| #include <part.h>
 | |
| #include <usb.h>
 | |
| #include <asm/io.h>
 | |
| #include <asm/state.h>
 | |
| #include <asm/test.h>
 | |
| #include <dm/device-internal.h>
 | |
| #include <dm/test.h>
 | |
| #include <dm/uclass-internal.h>
 | |
| #include <test/test.h>
 | |
| #include <test/ut.h>
 | |
| 
 | |
| struct keyboard_test_data {
 | |
| 	const char modifiers;
 | |
| 	const char scancode;
 | |
| 	const char result[6];
 | |
| };
 | |
| 
 | |
| /* Test that sandbox USB works correctly */
 | |
| static int dm_test_usb_base(struct unit_test_state *uts)
 | |
| {
 | |
| 	struct udevice *bus;
 | |
| 
 | |
| 	ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_USB, 0, &bus));
 | |
| 	ut_assertok(uclass_get_device(UCLASS_USB, 0, &bus));
 | |
| 	ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_USB, 2, &bus));
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| DM_TEST(dm_test_usb_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 | |
| 
 | |
| /*
 | |
|  * Test that we can use the flash stick. This is more of a functional test. It
 | |
|  * covers scanning the bug, setting up a hub and a flash stick and reading
 | |
|  * data from the flash stick.
 | |
|  */
 | |
| static int dm_test_usb_flash(struct unit_test_state *uts)
 | |
| {
 | |
| 	struct udevice *dev;
 | |
| 	struct blk_desc *dev_desc;
 | |
| 	char cmp[1024];
 | |
| 
 | |
| 	state_set_skip_delays(true);
 | |
| 	ut_assertok(usb_init());
 | |
| 	ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &dev));
 | |
| 	ut_assertok(blk_get_device_by_str("usb", "0", &dev_desc));
 | |
| 
 | |
| 	/* Read a few blocks and look for the string we expect */
 | |
| 	ut_asserteq(512, dev_desc->blksz);
 | |
| 	memset(cmp, '\0', sizeof(cmp));
 | |
| 	ut_asserteq(2, blk_dread(dev_desc, 0, 2, cmp));
 | |
| 	ut_assertok(strcmp(cmp, "this is a test"));
 | |
| 	ut_assertok(usb_stop());
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| DM_TEST(dm_test_usb_flash, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 | |
| 
 | |
| /* test that we can handle multiple storage devices */
 | |
| static int dm_test_usb_multi(struct unit_test_state *uts)
 | |
| {
 | |
| 	struct udevice *dev;
 | |
| 
 | |
| 	state_set_skip_delays(true);
 | |
| 	ut_assertok(usb_init());
 | |
| 	ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &dev));
 | |
| 	ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 1, &dev));
 | |
| 	ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 2, &dev));
 | |
| 	ut_assertok(usb_stop());
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| DM_TEST(dm_test_usb_multi, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 | |
| 
 | |
| /* test that we have an associated ofnode with the usb device */
 | |
| static int dm_test_usb_fdt_node(struct unit_test_state *uts)
 | |
| {
 | |
| 	struct udevice *dev;
 | |
| 	ofnode node;
 | |
| 
 | |
| 	state_set_skip_delays(true);
 | |
| 	ut_assertok(usb_init());
 | |
| 	ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &dev));
 | |
| 	node = ofnode_path("/usb@1/hub/usbstor@1");
 | |
| 	ut_asserteq(1, ofnode_equal(node, dev_ofnode(dev)));
 | |
| 	ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 1, &dev));
 | |
| 	ut_asserteq(1, ofnode_equal(ofnode_null(), dev_ofnode(dev)));
 | |
| 	ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 2, &dev));
 | |
| 	node = ofnode_path("/usb@1/hub/usbstor@3");
 | |
| 	ut_asserteq(1, ofnode_equal(node, dev_ofnode(dev)));
 | |
| 	ut_assertok(usb_stop());
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| DM_TEST(dm_test_usb_fdt_node, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 | |
| 
 | |
| static int count_usb_devices(void)
 | |
| {
 | |
| 	struct udevice *hub;
 | |
| 	struct uclass *uc;
 | |
| 	int count = 0;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = uclass_get(UCLASS_USB_HUB, &uc);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	uclass_foreach_dev(hub, uc) {
 | |
| 		struct udevice *dev;
 | |
| 
 | |
| 		count++;
 | |
| 		for (device_find_first_child(hub, &dev);
 | |
| 		     dev;
 | |
| 		     device_find_next_child(&dev)) {
 | |
| 			count++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return count;
 | |
| }
 | |
| 
 | |
| /* test that no USB devices are found after we stop the stack */
 | |
| static int dm_test_usb_stop(struct unit_test_state *uts)
 | |
| {
 | |
| 	struct udevice *dev;
 | |
| 
 | |
| 	/* Scan and check that all devices are present */
 | |
| 	state_set_skip_delays(true);
 | |
| 	ut_assertok(usb_init());
 | |
| 	ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &dev));
 | |
| 	ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 1, &dev));
 | |
| 	ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 2, &dev));
 | |
| 	ut_asserteq(6, count_usb_devices());
 | |
| 	ut_assertok(usb_stop());
 | |
| 	ut_asserteq(0, count_usb_devices());
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| DM_TEST(dm_test_usb_stop, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 | |
| 
 | |
| /**
 | |
|  * dm_test_usb_keyb() - test USB keyboard driver
 | |
|  *
 | |
|  * This test copies USB keyboard scan codes into the key buffer of the USB
 | |
|  * keyboard emulation driver. These are picked up during emulated interrupts
 | |
|  * by the USB keyboard driver and converted to characters and escape sequences.
 | |
|  * The test then reads and verifies these characters and escape sequences from
 | |
|  * the standard input.
 | |
|  *
 | |
|  * TODO: The following features are not yet tested:
 | |
|  *
 | |
|  * * LED status
 | |
|  * * caps-lock
 | |
|  * * num-lock
 | |
|  * * numerical pad keys
 | |
|  *
 | |
|  * TODO: The following features are not yet implemented by the USB keyboard
 | |
|  * driver and therefore not tested:
 | |
|  *
 | |
|  * * modifiers for non-alpha-numeric keys, e.g. <SHIFT><TAB> and <ALT><F4>
 | |
|  * * some special keys, e.g. <PRINT>
 | |
|  * * some modifiers, e.g. <ALT> and <META>
 | |
|  * * alternative keyboard layouts
 | |
|  *
 | |
|  * @uts:	unit test state
 | |
|  * Return:	0 on success
 | |
|  */
 | |
| static int dm_test_usb_keyb(struct unit_test_state *uts)
 | |
| {
 | |
| 	struct udevice *dev;
 | |
| 	const struct keyboard_test_data *pos;
 | |
| 	const struct keyboard_test_data kbd_test_data[] = {
 | |
| 		/* <A> */
 | |
| 		{0x00, 0x04, "a"},
 | |
| 		/* <B> */
 | |
| 		{0x00, 0x05, "b"},
 | |
| 		/* <C> */
 | |
| 		{0x00, 0x06, "c"},
 | |
| 		/* <D> */
 | |
| 		{0x00, 0x07, "d"},
 | |
| 		/* <E> */
 | |
| 		{0x00, 0x08, "e"},
 | |
| 		/* <F> */
 | |
| 		{0x00, 0x09, "f"},
 | |
| 		/* <G> */
 | |
| 		{0x00, 0x0a, "g"},
 | |
| 		/* <H> */
 | |
| 		{0x00, 0x0b, "h"},
 | |
| 		/* <I> */
 | |
| 		{0x00, 0x0c, "i"},
 | |
| 		/* <J> */
 | |
| 		{0x00, 0x0d, "j"},
 | |
| 		/* <K> */
 | |
| 		{0x00, 0x0e, "k"},
 | |
| 		/* <L> */
 | |
| 		{0x00, 0x0f, "l"},
 | |
| 		/* <M> */
 | |
| 		{0x00, 0x10, "m"},
 | |
| 		/* <N> */
 | |
| 		{0x00, 0x11, "n"},
 | |
| 		/* <O> */
 | |
| 		{0x00, 0x12, "o"},
 | |
| 		/* <P> */
 | |
| 		{0x00, 0x13, "p"},
 | |
| 		/* <Q> */
 | |
| 		{0x00, 0x14, "q"},
 | |
| 		/* <R> */
 | |
| 		{0x00, 0x15, "r"},
 | |
| 		/* <S> */
 | |
| 		{0x00, 0x16, "s"},
 | |
| 		/* <T> */
 | |
| 		{0x00, 0x17, "t"},
 | |
| 		/* <U> */
 | |
| 		{0x00, 0x18, "u"},
 | |
| 		/* <V> */
 | |
| 		{0x00, 0x19, "v"},
 | |
| 		/* <W> */
 | |
| 		{0x00, 0x1a, "w"},
 | |
| 		/* <X> */
 | |
| 		{0x00, 0x1b, "x"},
 | |
| 		/* <Y> */
 | |
| 		{0x00, 0x1c, "y"},
 | |
| 		/* <Z> */
 | |
| 		{0x00, 0x1d, "z"},
 | |
| 
 | |
| 		/* <LEFT-SHIFT><A> */
 | |
| 		{0x02, 0x04, "A"},
 | |
| 		/* <RIGHT-SHIFT><Z> */
 | |
| 		{0x20, 0x1d, "Z"},
 | |
| 
 | |
| 		/* <LEFT-CONTROL><A> */
 | |
| 		{0x01, 0x04, "\x01"},
 | |
| 		/* <RIGHT-CONTROL><Z> */
 | |
| 		{0x10, 0x1d, "\x1a"},
 | |
| 
 | |
| 		/* <1> */
 | |
| 		{0x00, 0x1e, "1"},
 | |
| 		/* <2> */
 | |
| 		{0x00, 0x1f, "2"},
 | |
| 		/* <3> */
 | |
| 		{0x00, 0x20, "3"},
 | |
| 		/* <4> */
 | |
| 		{0x00, 0x21, "4"},
 | |
| 		/* <5> */
 | |
| 		{0x00, 0x22, "5"},
 | |
| 		/* <6> */
 | |
| 		{0x00, 0x23, "6"},
 | |
| 		/* <7> */
 | |
| 		{0x00, 0x24, "7"},
 | |
| 		/* <8> */
 | |
| 		{0x00, 0x25, "8"},
 | |
| 		/* <9> */
 | |
| 		{0x00, 0x26, "9"},
 | |
| 		/* <0> */
 | |
| 		{0x00, 0x27, "0"},
 | |
| 
 | |
| 		/* <LEFT-SHIFT><1> */
 | |
| 		{0x02, 0x1e, "!"},
 | |
| 		/* <RIGHT-SHIFT><2> */
 | |
| 		{0x20, 0x1f, "@"},
 | |
| 		/* <LEFT-SHIFT><3> */
 | |
| 		{0x02, 0x20, "#"},
 | |
| 		/* <RIGHT-SHIFT><4> */
 | |
| 		{0x20, 0x21, "$"},
 | |
| 		/* <LEFT-SHIFT><5> */
 | |
| 		{0x02, 0x22, "%"},
 | |
| 		/* <RIGHT-SHIFT><6> */
 | |
| 		{0x20, 0x23, "^"},
 | |
| 		/* <LEFT-SHIFT><7> */
 | |
| 		{0x02, 0x24, "&"},
 | |
| 		/* <RIGHT-SHIFT><8> */
 | |
| 		{0x20, 0x25, "*"},
 | |
| 		/* <LEFT-SHIFT><9> */
 | |
| 		{0x02, 0x26, "("},
 | |
| 		/* <RIGHT-SHIFT><0> */
 | |
| 		{0x20, 0x27, ")"},
 | |
| 
 | |
| 		/* <ENTER> */
 | |
| 		{0x00, 0x28, "\r"},
 | |
| 		/* <ESCAPE> */
 | |
| 		{0x00, 0x29, "\x1b"},
 | |
| 		/* <BACKSPACE> */
 | |
| 		{0x00, 0x2a, "\x08"},
 | |
| 		/* <TAB> */
 | |
| 		{0x00, 0x2b, "\x09"},
 | |
| 		/* <SPACE> */
 | |
| 		{0x00, 0x2c, " "},
 | |
| 		/* <MINUS> */
 | |
| 		{0x00, 0x2d, "-"},
 | |
| 		/* <EQUAL> */
 | |
| 		{0x00, 0x2e, "="},
 | |
| 		/* <LEFT BRACE> */
 | |
| 		{0x00, 0x2f, "["},
 | |
| 		/* <RIGHT BRACE> */
 | |
| 		{0x00, 0x30, "]"},
 | |
| 		/* <BACKSLASH> */
 | |
| 		{0x00, 0x31, "\\"},
 | |
| 		/* <HASH-TILDE> */
 | |
| 		{0x00, 0x32, "#"},
 | |
| 		/* <SEMICOLON> */
 | |
| 		{0x00, 0x33, ";"},
 | |
| 		/* <APOSTROPHE> */
 | |
| 		{0x00, 0x34, "'"},
 | |
| 		/* <GRAVE> */
 | |
| 		{0x00, 0x35, "`"},
 | |
| 		/* <COMMA> */
 | |
| 		{0x00, 0x36, ","},
 | |
| 		/* <DOT> */
 | |
| 		{0x00, 0x37, "."},
 | |
| 		/* <SLASH> */
 | |
| 		{0x00, 0x38, "/"},
 | |
| 
 | |
| 		/* <LEFT-SHIFT><ENTER> */
 | |
| 		{0x02, 0x28, "\r"},
 | |
| 		/* <RIGHT-SHIFT><ESCAPE> */
 | |
| 		{0x20, 0x29, "\x1b"},
 | |
| 		/* <LEFT-SHIFT><BACKSPACE> */
 | |
| 		{0x02, 0x2a, "\x08"},
 | |
| 		/* <RIGHT-SHIFT><TAB> */
 | |
| 		{0x20, 0x2b, "\x09"},
 | |
| 		/* <LEFT-SHIFT><SPACE> */
 | |
| 		{0x02, 0x2c, " "},
 | |
| 		/* <MINUS> */
 | |
| 		{0x20, 0x2d, "_"},
 | |
| 		/* <LEFT-SHIFT><EQUAL> */
 | |
| 		{0x02, 0x2e, "+"},
 | |
| 		/* <RIGHT-SHIFT><LEFT BRACE> */
 | |
| 		{0x20, 0x2f, "{"},
 | |
| 		/* <LEFT-SHIFT><RIGHT BRACE> */
 | |
| 		{0x02, 0x30, "}"},
 | |
| 		/* <RIGHT-SHIFT><BACKSLASH> */
 | |
| 		{0x20, 0x31, "|"},
 | |
| 		/* <LEFT-SHIFT><HASH-TILDE> */
 | |
| 		{0x02, 0x32, "~"},
 | |
| 		/* <RIGHT-SHIFT><SEMICOLON> */
 | |
| 		{0x20, 0x33, ":"},
 | |
| 		/* <LEFT-SHIFT><APOSTROPHE> */
 | |
| 		{0x02, 0x34, "\""},
 | |
| 		/* <RIGHT-SHIFT><GRAVE> */
 | |
| 		{0x20, 0x35, "~"},
 | |
| 		/* <LEFT-SHIFT><COMMA> */
 | |
| 		{0x02, 0x36, "<"},
 | |
| 		/* <RIGHT-SHIFT><DOT> */
 | |
| 		{0x20, 0x37, ">"},
 | |
| 		/* <LEFT-SHIFT><SLASH> */
 | |
| 		{0x02, 0x38, "?"},
 | |
| #ifdef CONFIG_USB_KEYBOARD_FN_KEYS
 | |
| 		/* <F1> */
 | |
| 		{0x00, 0x3a, "\x1bOP"},
 | |
| 		/* <F2> */
 | |
| 		{0x00, 0x3b, "\x1bOQ"},
 | |
| 		/* <F3> */
 | |
| 		{0x00, 0x3c, "\x1bOR"},
 | |
| 		/* <F4> */
 | |
| 		{0x00, 0x3d, "\x1bOS"},
 | |
| 		/* <F5> */
 | |
| 		{0x00, 0x3e, "\x1b[15~"},
 | |
| 		/* <F6> */
 | |
| 		{0x00, 0x3f, "\x1b[17~"},
 | |
| 		/* <F7> */
 | |
| 		{0x00, 0x40, "\x1b[18~"},
 | |
| 		/* <F8> */
 | |
| 		{0x00, 0x41, "\x1b[19~"},
 | |
| 		/* <F9> */
 | |
| 		{0x00, 0x42, "\x1b[20~"},
 | |
| 		/* <F10> */
 | |
| 		{0x00, 0x43, "\x1b[21~"},
 | |
| 		/* <F11> */
 | |
| 		{0x00, 0x44, "\x1b[23~"},
 | |
| 		/* <F12> */
 | |
| 		{0x00, 0x45, "\x1b[24~"},
 | |
| 		/* <INSERT> */
 | |
| 		{0x00, 0x49, "\x1b[2~"},
 | |
| 		/* <HOME> */
 | |
| 		{0x00, 0x4a, "\x1b[H"},
 | |
| 		/* <PAGE UP> */
 | |
| 		{0x00, 0x4b, "\x1b[5~"},
 | |
| 		/* <DELETE> */
 | |
| 		{0x00, 0x4c, "\x1b[3~"},
 | |
| 		/* <END> */
 | |
| 		{0x00, 0x4d, "\x1b[F"},
 | |
| 		/* <PAGE DOWN> */
 | |
| 		{0x00, 0x4e, "\x1b[6~"},
 | |
| 		/* <RIGHT> */
 | |
| 		{0x00, 0x4f, "\x1b[C"},
 | |
| 		/* <LEFT> */
 | |
| 		{0x00, 0x50, "\x1b[D"},
 | |
| 		/* <DOWN> */
 | |
| 		{0x00, 0x51, "\x1b[B"},
 | |
| 		/* <UP> */
 | |
| 		{0x00, 0x52, "\x1b[A"},
 | |
| #endif /* CONFIG_USB_KEYBOARD_FN_KEYS */
 | |
| 
 | |
| 		/* End of list */
 | |
| 		{0x00, 0x00, "\0"}
 | |
| 	};
 | |
| 
 | |
| 
 | |
| 	state_set_skip_delays(true);
 | |
| 	ut_assertok(usb_init());
 | |
| 
 | |
| 	/* Initially there should be no characters */
 | |
| 	ut_asserteq(0, tstc());
 | |
| 
 | |
| 	ut_assertok(uclass_get_device_by_name(UCLASS_USB_EMUL, "keyb@3",
 | |
| 					      &dev));
 | |
| 
 | |
| 	/*
 | |
| 	 * Add scan codes to the USB keyboard buffer. They should appear as
 | |
| 	 * corresponding characters and escape sequences in stdin.
 | |
| 	 */
 | |
| 	for (pos = kbd_test_data; pos->scancode; ++pos) {
 | |
| 		const char *c;
 | |
| 		char scancodes[USB_KBD_BOOT_REPORT_SIZE] = {0};
 | |
| 
 | |
| 		scancodes[0] = pos->modifiers;
 | |
| 		scancodes[2] = pos->scancode;
 | |
| 
 | |
| 		ut_assertok(sandbox_usb_keyb_add_string(dev, scancodes));
 | |
| 
 | |
| 		for (c = pos->result; *c; ++c) {
 | |
| 			ut_asserteq(1, tstc());
 | |
| 			ut_asserteq(*c, getchar());
 | |
| 		}
 | |
| 		ut_asserteq(0, tstc());
 | |
| 	}
 | |
| 	ut_assertok(usb_stop());
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| DM_TEST(dm_test_usb_keyb, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
 |