diff options
author | Tom Rini <trini@konsulko.com> | 2017-10-01 18:06:53 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2017-10-01 18:06:53 -0400 |
commit | 4d1c166fee4a34e8a7d8d05b2a4102c7c668726d (patch) | |
tree | d6a7432c7ae651ae1243a86d6abe2de1ef08c764 | |
parent | 16d4ff76c524fa8b0adb5328a9977b6939cd5416 (diff) | |
parent | dc04b35ef2c8c04cb362758ec467777348ef3f15 (diff) | |
download | u-boot-4d1c166fee4a34e8a7d8d05b2a4102c7c668726d.zip u-boot-4d1c166fee4a34e8a7d8d05b2a4102c7c668726d.tar.gz u-boot-4d1c166fee4a34e8a7d8d05b2a4102c7c668726d.tar.bz2 |
Merge git://git.denx.de/u-boot-usb
-rw-r--r-- | common/usb.c | 45 | ||||
-rw-r--r-- | common/usb_hub.c | 16 | ||||
-rw-r--r-- | common/usb_storage.c | 2 | ||||
-rw-r--r-- | drivers/usb/dwc3/Kconfig | 7 | ||||
-rw-r--r-- | drivers/usb/dwc3/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-uniphier.c | 120 | ||||
-rw-r--r-- | drivers/usb/emul/sandbox_flash.c | 3 | ||||
-rw-r--r-- | drivers/usb/emul/sandbox_hub.c | 38 | ||||
-rw-r--r-- | drivers/usb/emul/sandbox_keyb.c | 3 | ||||
-rw-r--r-- | drivers/usb/emul/usb-emul-uclass.c | 59 | ||||
-rw-r--r-- | drivers/usb/host/usb-sandbox.c | 30 | ||||
-rw-r--r-- | drivers/usb/host/usb-uclass.c | 43 | ||||
-rw-r--r-- | drivers/usb/host/xhci-mem.c | 24 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 249 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 5 | ||||
-rw-r--r-- | include/linux/usb/ch9.h | 20 | ||||
-rw-r--r-- | include/usb.h | 35 | ||||
-rw-r--r-- | test/dm/usb.c | 163 |
18 files changed, 612 insertions, 251 deletions
diff --git a/common/usb.c b/common/usb.c index 0904259..8d27bc7 100644 --- a/common/usb.c +++ b/common/usb.c @@ -437,12 +437,13 @@ static int usb_parse_config(struct usb_device *dev, } break; case USB_DT_ENDPOINT: - if (head->bLength != USB_DT_ENDPOINT_SIZE) { + if (head->bLength != USB_DT_ENDPOINT_SIZE && + head->bLength != USB_DT_ENDPOINT_AUDIO_SIZE) { printf("ERROR: Invalid USB EP length (%d)\n", head->bLength); break; } - if (index + USB_DT_ENDPOINT_SIZE > + if (index + head->bLength > dev->config.desc.wTotalLength) { puts("USB EP descriptor overflowed buffer!\n"); break; @@ -969,23 +970,24 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read) dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0; dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; - if (do_read) { + if (do_read && dev->speed == USB_SPEED_FULL) { int err; /* - * Validate we've received only at least 8 bytes, not that we've - * received the entire descriptor. The reasoning is: - * - The code only uses fields in the first 8 bytes, so that's all we - * need to have fetched at this stage. - * - The smallest maxpacket size is 8 bytes. Before we know the actual - * maxpacket the device uses, the USB controller may only accept a - * single packet. Consequently we are only guaranteed to receive 1 - * packet (at least 8 bytes) even in a non-error case. + * Validate we've received only at least 8 bytes, not that + * we've received the entire descriptor. The reasoning is: + * - The code only uses fields in the first 8 bytes, so + * that's all we need to have fetched at this stage. + * - The smallest maxpacket size is 8 bytes. Before we know + * the actual maxpacket the device uses, the USB controller + * may only accept a single packet. Consequently we are only + * guaranteed to receive 1 packet (at least 8 bytes) even in + * a non-error case. * - * At least the DWC2 controller needs to be programmed with the number - * of packets in addition to the number of bytes. A request for 64 - * bytes of data with the maxpacket guessed as 64 (above) yields a - * request for 1 packet. + * At least the DWC2 controller needs to be programmed with + * the number of packets in addition to the number of bytes. + * A request for 64 bytes of data with the maxpacket guessed + * as 64 (above) yields a request for 1 packet. */ err = get_descriptor_len(dev, 64, 8); if (err) @@ -1008,7 +1010,7 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read) dev->maxpacketsize = PACKET_SIZE_64; break; default: - printf("usb_new_device: invalid max packet size\n"); + printf("%s: invalid max packet size\n", __func__); return -EIO; } @@ -1050,6 +1052,17 @@ static int usb_prepare_device(struct usb_device *dev, int addr, bool do_read, mdelay(10); /* Let the SET_ADDRESS settle */ + /* + * If we haven't read device descriptor before, read it here + * after device is assigned an address. This is only applicable + * to xHCI so far. + */ + if (!do_read) { + err = usb_setup_descriptor(dev, true); + if (err) + return err; + } + return 0; } diff --git a/common/usb_hub.c b/common/usb_hub.c index 86a3477..325d16d 100644 --- a/common/usb_hub.c +++ b/common/usb_hub.c @@ -489,6 +489,17 @@ static int usb_scan_port(struct usb_device_scan *usb_scan) return 0; } + if (portchange & USB_PORT_STAT_C_RESET) { + debug("port %d reset change\n", i + 1); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); + } + + if ((portchange & USB_SS_PORT_STAT_C_BH_RESET) && + usb_hub_is_superspeed(dev)) { + debug("port %d BH reset change\n", i + 1); + usb_clear_port_feature(dev, i + 1, USB_SS_PORT_FEAT_C_BH_RESET); + } + /* A new USB device is ready at this point */ debug("devnum=%d port=%d: USB dev found\n", dev->devnum, i + 1); @@ -543,11 +554,6 @@ static int usb_scan_port(struct usb_device_scan *usb_scan) hub->overcurrent_count[i]); } - if (portchange & USB_PORT_STAT_C_RESET) { - debug("port %d reset change\n", i + 1); - usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); - } - /* * We're done with this device, so let's remove this device from * scanning list diff --git a/common/usb_storage.c b/common/usb_storage.c index a57570b..a91b1c0 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -964,7 +964,7 @@ static void usb_stor_set_max_xfer_blk(struct usb_device *udev, blk = 20; } else { if (size > USHRT_MAX * 512) - blk = USHRT_MAX; + size = USHRT_MAX * 512; blk = size / 512; } #endif diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index a291ceb..ae7fc1c 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -37,6 +37,13 @@ config USB_DWC3_OMAP Say 'Y' here if you have one such device +config USB_DWC3_UNIPHIER + bool "DesignWare USB3 Host Support on UniPhier Platforms" + depends on ARCH_UNIPHIER && USB_XHCI_DWC3 + help + Support of USB2/3 functionality in Socionext UniPhier platforms. + Say 'Y' here if you have one such device. + menu "PHY Subsystem" config USB_DWC3_PHY_OMAP diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 2964bae..5149776 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -9,5 +9,6 @@ dwc3-y := core.o obj-$(CONFIG_USB_DWC3_GADGET) += gadget.o ep0.o obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o +obj-$(CONFIG_USB_DWC3_UNIPHIER) += dwc3-uniphier.o obj-$(CONFIG_USB_DWC3_PHY_OMAP) += ti_usb_phy.o obj-$(CONFIG_USB_DWC3_PHY_SAMSUNG) += samsung_usb_phy.o diff --git a/drivers/usb/dwc3/dwc3-uniphier.c b/drivers/usb/dwc3/dwc3-uniphier.c new file mode 100644 index 0000000..0d13770 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-uniphier.c @@ -0,0 +1,120 @@ +/* + * UniPhier Specific Glue Layer for DWC3 + * + * Copyright (C) 2016-2017 Socionext Inc. + * Author: Masahiro Yamada <yamada.masahiro@socionext.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/sizes.h> + +#define UNIPHIER_PRO4_DWC3_RESET 0x40 +#define UNIPHIER_PRO4_DWC3_RESET_XIOMMU BIT(5) +#define UNIPHIER_PRO4_DWC3_RESET_XLINK BIT(4) +#define UNIPHIER_PRO4_DWC3_RESET_PHY_SS BIT(2) + +#define UNIPHIER_PRO5_DWC3_RESET 0x00 +#define UNIPHIER_PRO5_DWC3_RESET_PHY_S1 BIT(17) +#define UNIPHIER_PRO5_DWC3_RESET_PHY_S0 BIT(16) +#define UNIPHIER_PRO5_DWC3_RESET_XLINK BIT(15) +#define UNIPHIER_PRO5_DWC3_RESET_XIOMMU BIT(14) + +#define UNIPHIER_PXS2_DWC3_RESET 0x00 +#define UNIPHIER_PXS2_DWC3_RESET_XLINK BIT(15) + +static int uniphier_pro4_dwc3_init(void __iomem *regs) +{ + u32 tmp; + + tmp = readl(regs + UNIPHIER_PRO4_DWC3_RESET); + tmp &= ~UNIPHIER_PRO4_DWC3_RESET_PHY_SS; + tmp |= UNIPHIER_PRO4_DWC3_RESET_XIOMMU | UNIPHIER_PRO4_DWC3_RESET_XLINK; + writel(tmp, regs + UNIPHIER_PRO4_DWC3_RESET); + + return 0; +} + +static int uniphier_pro5_dwc3_init(void __iomem *regs) +{ + u32 tmp; + + tmp = readl(regs + UNIPHIER_PRO5_DWC3_RESET); + tmp &= ~(UNIPHIER_PRO5_DWC3_RESET_PHY_S1 | + UNIPHIER_PRO5_DWC3_RESET_PHY_S0); + tmp |= UNIPHIER_PRO5_DWC3_RESET_XLINK | UNIPHIER_PRO5_DWC3_RESET_XIOMMU; + writel(tmp, regs + UNIPHIER_PRO5_DWC3_RESET); + + return 0; +} + +static int uniphier_pxs2_dwc3_init(void __iomem *regs) +{ + u32 tmp; + + tmp = readl(regs + UNIPHIER_PXS2_DWC3_RESET); + tmp |= UNIPHIER_PXS2_DWC3_RESET_XLINK; + writel(tmp, regs + UNIPHIER_PXS2_DWC3_RESET); + + return 0; +} + +static int uniphier_dwc3_probe(struct udevice *dev) +{ + fdt_addr_t base; + void __iomem *regs; + int (*init)(void __iomem *regs); + int ret; + + base = devfdt_get_addr(dev); + if (base == FDT_ADDR_T_NONE) + return -EINVAL; + + regs = ioremap(base, SZ_32K); + if (!regs) + return -ENOMEM; + + init = (typeof(init))dev_get_driver_data(dev); + ret = init(regs); + if (ret) + dev_err(dev, "failed to init glue layer\n"); + + iounmap(regs); + + return ret; +} + +static const struct udevice_id uniphier_dwc3_match[] = { + { + .compatible = "socionext,uniphier-pro4-dwc3", + .data = (ulong)uniphier_pro4_dwc3_init, + }, + { + .compatible = "socionext,uniphier-pro5-dwc3", + .data = (ulong)uniphier_pro5_dwc3_init, + }, + { + .compatible = "socionext,uniphier-pxs2-dwc3", + .data = (ulong)uniphier_pxs2_dwc3_init, + }, + { + .compatible = "socionext,uniphier-ld20-dwc3", + .data = (ulong)uniphier_pxs2_dwc3_init, + }, + { + .compatible = "socionext,uniphier-pxs3-dwc3", + .data = (ulong)uniphier_pxs2_dwc3_init, + }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(usb_xhci) = { + .name = "uniphier-dwc3", + .id = UCLASS_SIMPLE_BUS, + .of_match = uniphier_dwc3_match, + .probe = uniphier_dwc3_probe, +}; diff --git a/drivers/usb/emul/sandbox_flash.c b/drivers/usb/emul/sandbox_flash.c index 98d20c0..2f84b36 100644 --- a/drivers/usb/emul/sandbox_flash.c +++ b/drivers/usb/emul/sandbox_flash.c @@ -390,8 +390,7 @@ static int sandbox_flash_bind(struct udevice *dev) fs[2].id = STRINGID_SERIAL; fs[2].s = dev->name; - return usb_emul_setup_device(dev, PACKET_SIZE_64, plat->flash_strings, - flash_desc_list); + return usb_emul_setup_device(dev, plat->flash_strings, flash_desc_list); } static int sandbox_flash_probe(struct udevice *dev) diff --git a/drivers/usb/emul/sandbox_hub.c b/drivers/usb/emul/sandbox_hub.c index 1432858..9a0f47b 100644 --- a/drivers/usb/emul/sandbox_hub.c +++ b/drivers/usb/emul/sandbox_hub.c @@ -121,9 +121,12 @@ struct sandbox_hub_priv { int change[SANDBOX_NUM_PORTS]; }; -static struct udevice *hub_find_device(struct udevice *hub, int port) +static struct udevice *hub_find_device(struct udevice *hub, int port, + enum usb_device_speed *speed) { struct udevice *dev; + struct usb_generic_descriptor **gen_desc; + struct usb_device_descriptor **dev_desc; for (device_find_first_child(hub, &dev); dev; @@ -131,8 +134,27 @@ static struct udevice *hub_find_device(struct udevice *hub, int port) struct sandbox_hub_platdata *plat; plat = dev_get_parent_platdata(dev); - if (plat->port == port) + if (plat->port == port) { + gen_desc = plat->plat.desc_list; + gen_desc = usb_emul_find_descriptor(gen_desc, + USB_DT_DEVICE, 0); + dev_desc = (struct usb_device_descriptor **)gen_desc; + + switch (le16_to_cpu((*dev_desc)->bcdUSB)) { + case 0x0100: + *speed = USB_SPEED_LOW; + break; + case 0x0101: + *speed = USB_SPEED_FULL; + break; + case 0x0200: + default: + *speed = USB_SPEED_HIGH; + break; + } + return dev; + } } return NULL; @@ -146,7 +168,8 @@ static int clrset_post_state(struct udevice *hub, int port, int clear, int set) int ret = 0; if ((clear | set) & USB_PORT_STAT_POWER) { - struct udevice *dev = hub_find_device(hub, port); + enum usb_device_speed speed; + struct udevice *dev = hub_find_device(hub, port, &speed); if (dev) { if (set & USB_PORT_STAT_POWER) { @@ -156,6 +179,10 @@ static int clrset_post_state(struct udevice *hub, int port, int clear, int set) if (!ret) { set |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE; + if (speed == USB_SPEED_LOW) + set |= USB_PORT_STAT_LOW_SPEED; + else if (speed == USB_SPEED_HIGH) + set |= USB_PORT_STAT_HIGH_SPEED; } } else if (clear & USB_PORT_STAT_POWER) { @@ -274,15 +301,16 @@ static int sandbox_hub_submit_control_msg(struct udevice *bus, static int sandbox_hub_bind(struct udevice *dev) { - return usb_emul_setup_device(dev, PACKET_SIZE_64, hub_strings, - hub_desc_list); + return usb_emul_setup_device(dev, hub_strings, hub_desc_list); } static int sandbox_child_post_bind(struct udevice *dev) { struct sandbox_hub_platdata *plat = dev_get_parent_platdata(dev); + struct usb_emul_platdata *emul = dev_get_uclass_platdata(dev); plat->port = dev_read_u32_default(dev, "reg", -1); + emul->port1 = plat->port + 1; return 0; } diff --git a/drivers/usb/emul/sandbox_keyb.c b/drivers/usb/emul/sandbox_keyb.c index 2735985..cff0176 100644 --- a/drivers/usb/emul/sandbox_keyb.c +++ b/drivers/usb/emul/sandbox_keyb.c @@ -208,8 +208,7 @@ static int sandbox_keyb_bind(struct udevice *dev) fs[2].id = STRINGID_SERIAL; fs[2].s = dev->name; - return usb_emul_setup_device(dev, PACKET_SIZE_8, plat->keyb_strings, - keyb_desc_list); + return usb_emul_setup_device(dev, plat->keyb_strings, keyb_desc_list); } static int sandbox_keyb_probe(struct udevice *dev) diff --git a/drivers/usb/emul/usb-emul-uclass.c b/drivers/usb/emul/usb-emul-uclass.c index 6e03c1e..fbe11f3 100644 --- a/drivers/usb/emul/usb-emul-uclass.c +++ b/drivers/usb/emul/usb-emul-uclass.c @@ -52,7 +52,7 @@ static int usb_emul_get_string(struct usb_string *strings, int index, return -EINVAL; } -static struct usb_generic_descriptor **find_descriptor( +struct usb_generic_descriptor **usb_emul_find_descriptor( struct usb_generic_descriptor **ptr, int type, int index) { debug("%s: type=%x, index=%d\n", __func__, type, index); @@ -91,8 +91,7 @@ static int usb_emul_get_descriptor(struct usb_dev_platdata *plat, int value, length); } - ptr = find_descriptor((struct usb_generic_descriptor **)plat->desc_list, - type, index); + ptr = usb_emul_find_descriptor(plat->desc_list, type, index); if (!ptr) { debug("%s: Could not find descriptor type %d, index %d\n", __func__, type, index); @@ -107,7 +106,7 @@ static int usb_emul_get_descriptor(struct usb_dev_platdata *plat, int value, return upto ? upto : length ? -EIO : 0; } -static int usb_emul_find_devnum(int devnum, struct udevice **emulp) +static int usb_emul_find_devnum(int devnum, int port1, struct udevice **emulp) { struct udevice *dev; struct uclass *uc; @@ -120,7 +119,37 @@ static int usb_emul_find_devnum(int devnum, struct udevice **emulp) uclass_foreach_dev(dev, uc) { struct usb_dev_platdata *udev = dev_get_parent_platdata(dev); - if (udev->devnum == devnum) { + /* + * devnum is initialzied to zero at the beginning of the + * enumeration process in usb_setup_device(). At this + * point, udev->devnum has not been assigned to any valid + * USB address either, so we can't rely on the comparison + * result between udev->devnum and devnum to select an + * emulator device. + */ + if (!devnum) { + struct usb_emul_platdata *plat; + + /* + * If the parent is sandbox USB controller, we are + * the root hub. And there is only one root hub + * in the system. + */ + if (device_get_uclass_id(dev->parent) == UCLASS_USB) { + debug("%s: Found emulator '%s'\n", + __func__, dev->name); + *emulp = dev; + return 0; + } + + plat = dev_get_uclass_platdata(dev); + if (plat->port1 == port1) { + debug("%s: Found emulator '%s', port %d\n", + __func__, dev->name, port1); + *emulp = dev; + return 0; + } + } else if (udev->devnum == devnum) { debug("%s: Found emulator '%s', addr %d\n", __func__, dev->name, udev->devnum); *emulp = dev; @@ -132,18 +161,19 @@ static int usb_emul_find_devnum(int devnum, struct udevice **emulp) return -ENOENT; } -int usb_emul_find(struct udevice *bus, ulong pipe, struct udevice **emulp) +int usb_emul_find(struct udevice *bus, ulong pipe, int port1, + struct udevice **emulp) { int devnum = usb_pipedevice(pipe); - return usb_emul_find_devnum(devnum, emulp); + return usb_emul_find_devnum(devnum, port1, emulp); } int usb_emul_find_for_dev(struct udevice *dev, struct udevice **emulp) { struct usb_dev_platdata *udev = dev_get_parent_platdata(dev); - return usb_emul_find_devnum(udev->devnum, emulp); + return usb_emul_find_devnum(udev->devnum, 0, emulp); } int usb_emul_control(struct udevice *emul, struct usb_device *udev, @@ -229,8 +259,8 @@ int usb_emul_int(struct udevice *emul, struct usb_device *udev, return ops->interrupt(emul, udev, pipe, buffer, length, interval); } -int usb_emul_setup_device(struct udevice *dev, int maxpacketsize, - struct usb_string *strings, void **desc_list) +int usb_emul_setup_device(struct udevice *dev, struct usb_string *strings, + void **desc_list) { struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); struct usb_generic_descriptor **ptr; @@ -264,18 +294,11 @@ int usb_emul_setup_device(struct udevice *dev, int maxpacketsize, return 0; } -void usb_emul_reset(struct udevice *dev) -{ - struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); - - plat->devnum = 0; - plat->configno = 0; -} - UCLASS_DRIVER(usb_emul) = { .id = UCLASS_USB_EMUL, .name = "usb_emul", .post_bind = dm_scan_fdt_dev, + .per_device_platdata_auto_alloc_size = sizeof(struct usb_emul_platdata), .per_child_auto_alloc_size = sizeof(struct usb_device), .per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata), }; diff --git a/drivers/usb/host/usb-sandbox.c b/drivers/usb/host/usb-sandbox.c index 5e3d96c..15055b3 100644 --- a/drivers/usb/host/usb-sandbox.c +++ b/drivers/usb/host/usb-sandbox.c @@ -12,6 +12,10 @@ DECLARE_GLOBAL_DATA_PTR; +struct sandbox_usb_ctrl { + int rootdev; +}; + static void usbmon_trace(struct udevice *bus, ulong pipe, struct devrequest *setup, struct udevice *emul) { @@ -40,15 +44,24 @@ static int sandbox_submit_control(struct udevice *bus, void *buffer, int length, struct devrequest *setup) { + struct sandbox_usb_ctrl *ctrl = dev_get_priv(bus); struct udevice *emul; int ret; /* Just use child of dev as emulator? */ debug("%s: bus=%s\n", __func__, bus->name); - ret = usb_emul_find(bus, pipe, &emul); + ret = usb_emul_find(bus, pipe, udev->portnr, &emul); usbmon_trace(bus, pipe, setup, emul); if (ret) return ret; + + if (usb_pipedevice(pipe) == ctrl->rootdev) { + if (setup->request == USB_REQ_SET_ADDRESS) { + debug("%s: Set root hub's USB address\n", __func__); + ctrl->rootdev = le16_to_cpu(setup->value); + } + } + ret = usb_emul_control(emul, udev, pipe, buffer, length, setup); if (ret < 0) { debug("ret=%d\n", ret); @@ -70,7 +83,7 @@ static int sandbox_submit_bulk(struct udevice *bus, struct usb_device *udev, /* Just use child of dev as emulator? */ debug("%s: bus=%s\n", __func__, bus->name); - ret = usb_emul_find(bus, pipe, &emul); + ret = usb_emul_find(bus, pipe, udev->portnr, &emul); usbmon_trace(bus, pipe, NULL, emul); if (ret) return ret; @@ -96,7 +109,7 @@ static int sandbox_submit_int(struct udevice *bus, struct usb_device *udev, /* Just use child of dev as emulator? */ debug("%s: bus=%s\n", __func__, bus->name); - ret = usb_emul_find(bus, pipe, &emul); + ret = usb_emul_find(bus, pipe, udev->portnr, &emul); usbmon_trace(bus, pipe, NULL, emul); if (ret) return ret; @@ -107,6 +120,16 @@ static int sandbox_submit_int(struct udevice *bus, struct usb_device *udev, static int sandbox_alloc_device(struct udevice *dev, struct usb_device *udev) { + struct sandbox_usb_ctrl *ctrl = dev_get_priv(dev); + + /* + * Root hub will be the first device to be initailized. + * If this device is a root hub, initialize its device speed + * to high speed as we are a USB 2.0 controller. + */ + if (ctrl->rootdev == 0) + udev->speed = USB_SPEED_HIGH; + return 0; } @@ -133,4 +156,5 @@ U_BOOT_DRIVER(usb_sandbox) = { .of_match = sandbox_usb_ids, .probe = sandbox_usb_probe, .ops = &sandbox_usb_ops, + .priv_auto_alloc_size = sizeof(struct sandbox_usb_ctrl), }; diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index bc44fc3..4e40f4b 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -164,6 +164,7 @@ int usb_get_max_xfer_size(struct usb_device *udev, size_t *size) int usb_stop(void) { struct udevice *bus; + struct udevice *rh; struct uclass *uc; struct usb_uclass_priv *uc_priv; int err = 0, ret; @@ -179,23 +180,20 @@ int usb_stop(void) ret = device_remove(bus, DM_REMOVE_NORMAL); if (ret && !err) err = ret; - } -#ifdef CONFIG_BLK - ret = blk_unbind_all(IF_TYPE_USB); - if (ret && !err) - err = ret; -#endif -#ifdef CONFIG_SANDBOX - struct udevice *dev; - /* Reset all enulation devices */ - ret = uclass_get(UCLASS_USB_EMUL, &uc); - if (ret) - return ret; + /* Locate root hub device */ + device_find_first_child(bus, &rh); + if (rh) { + /* + * All USB devices are children of root hub. + * Unbinding root hub will unbind all of its children. + */ + ret = device_unbind(rh); + if (ret && !err) + err = ret; + } + } - uclass_foreach_dev(dev, uc) - usb_emul_reset(dev); -#endif #ifdef CONFIG_USB_STORAGE usb_stor_reset(); #endif @@ -262,6 +260,21 @@ int usb_init(void) /* init low_level USB */ printf("USB%d: ", count); count++; + +#ifdef CONFIG_SANDBOX + /* + * For Sandbox, we need scan the device tree each time when we + * start the USB stack, in order to re-create the emulated USB + * devices and bind drivers for them before we actually do the + * driver probe. + */ + ret = dm_scan_fdt_dev(bus); + if (ret) { + printf("Sandbox USB device scan failed (%d)\n", ret); + continue; + } +#endif + ret = device_probe(bus); if (ret == -ENODEV) { /* No such device. */ puts("Port not available.\n"); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index d5eab3a..0582a9b 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -786,12 +786,22 @@ void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, #ifdef CONFIG_DM_USB /* Set up TT fields to support FS/LS devices */ if (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL) { - dev = dev_get_parent_priv(udev->dev); - if (dev->speed == USB_SPEED_HIGH) { - hub = dev_get_uclass_priv(udev->dev); + struct udevice *parent = udev->dev; + + dev = udev; + do { + port_num = dev->portnr; + dev = dev_get_parent_priv(parent); + if (usb_hub_is_root_hub(dev->dev)) + break; + parent = dev->dev->parent; + } while (dev->speed != USB_SPEED_HIGH); + + if (!usb_hub_is_root_hub(dev->dev)) { + hub = dev_get_uclass_priv(dev->dev); if (hub->tt.multi) slot_ctx->dev_info |= cpu_to_le32(DEV_MTT); - slot_ctx->tt_info |= cpu_to_le32(TT_PORT(udev->portnr)); + slot_ctx->tt_info |= cpu_to_le32(TT_PORT(port_num)); slot_ctx->tt_info |= cpu_to_le32(TT_SLOT(dev->slot_id)); } } @@ -840,6 +850,12 @@ void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl, trb_64 = (uintptr_t)virt_dev->eps[0].ring->first_seg->trbs; ep0_ctx->deq = cpu_to_le64(trb_64 | virt_dev->eps[0].ring->cycle_state); + /* + * xHCI spec 6.2.3: + * software shall set 'Average TRB Length' to 8 for control endpoints. + */ + ep0_ctx->tx_info = cpu_to_le32(EP_AVG_TRB_LENGTH(8)); + /* Steps 7 and 8 were done in xhci_alloc_virt_device() */ xhci_flush_cache((uintptr_t)ep0_ctx, sizeof(struct xhci_ep_ctx)); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 04eb1eb..4673738 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -257,6 +257,188 @@ static unsigned int xhci_get_ep_index(struct usb_endpoint_descriptor *desc) return index; } +/* + * Convert bInterval expressed in microframes (in 1-255 range) to exponent of + * microframes, rounded down to nearest power of 2. + */ +static unsigned int xhci_microframes_to_exponent(unsigned int desc_interval, + unsigned int min_exponent, + unsigned int max_exponent) +{ + unsigned int interval; + + interval = fls(desc_interval) - 1; + interval = clamp_val(interval, min_exponent, max_exponent); + if ((1 << interval) != desc_interval) + debug("rounding interval to %d microframes, "\ + "ep desc says %d microframes\n", + 1 << interval, desc_interval); + + return interval; +} + +static unsigned int xhci_parse_microframe_interval(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc) +{ + if (endpt_desc->bInterval == 0) + return 0; + + return xhci_microframes_to_exponent(endpt_desc->bInterval, 0, 15); +} + +static unsigned int xhci_parse_frame_interval(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc) +{ + return xhci_microframes_to_exponent(endpt_desc->bInterval * 8, 3, 10); +} + +/* + * Convert interval expressed as 2^(bInterval - 1) == interval into + * straight exponent value 2^n == interval. + */ +static unsigned int xhci_parse_exponent_interval(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc) +{ + unsigned int interval; + + interval = clamp_val(endpt_desc->bInterval, 1, 16) - 1; + if (interval != endpt_desc->bInterval - 1) + debug("ep %#x - rounding interval to %d %sframes\n", + endpt_desc->bEndpointAddress, 1 << interval, + udev->speed == USB_SPEED_FULL ? "" : "micro"); + + if (udev->speed == USB_SPEED_FULL) { + /* + * Full speed isoc endpoints specify interval in frames, + * not microframes. We are using microframes everywhere, + * so adjust accordingly. + */ + interval += 3; /* 1 frame = 2^3 uframes */ + } + + return interval; +} + +/* + * Return the polling or NAK interval. + * + * The polling interval is expressed in "microframes". If xHCI's Interval field + * is set to N, it will service the endpoint every 2^(Interval)*125us. + * + * The NAK interval is one NAK per 1 to 255 microframes, or no NAKs if interval + * is set to 0. + */ +static unsigned int xhci_get_endpoint_interval(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc) +{ + unsigned int interval = 0; + + switch (udev->speed) { + case USB_SPEED_HIGH: + /* Max NAK rate */ + if (usb_endpoint_xfer_control(endpt_desc) || + usb_endpoint_xfer_bulk(endpt_desc)) { + interval = xhci_parse_microframe_interval(udev, + endpt_desc); + break; + } + /* Fall through - SS and HS isoc/int have same decoding */ + + case USB_SPEED_SUPER: + if (usb_endpoint_xfer_int(endpt_desc) || + usb_endpoint_xfer_isoc(endpt_desc)) { + interval = xhci_parse_exponent_interval(udev, + endpt_desc); + } + break; + + case USB_SPEED_FULL: + if (usb_endpoint_xfer_isoc(endpt_desc)) { + interval = xhci_parse_exponent_interval(udev, + endpt_desc); + break; + } + /* + * Fall through for interrupt endpoint interval decoding + * since it uses the same rules as low speed interrupt + * endpoints. + */ + + case USB_SPEED_LOW: + if (usb_endpoint_xfer_int(endpt_desc) || + usb_endpoint_xfer_isoc(endpt_desc)) { + interval = xhci_parse_frame_interval(udev, endpt_desc); + } + break; + + default: + BUG(); + } + + return interval; +} + +/* + * The "Mult" field in the endpoint context is only set for SuperSpeed isoc eps. + * High speed endpoint descriptors can define "the number of additional + * transaction opportunities per microframe", but that goes in the Max Burst + * endpoint context field. + */ +static u32 xhci_get_endpoint_mult(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc, + struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc) +{ + if (udev->speed < USB_SPEED_SUPER || + !usb_endpoint_xfer_isoc(endpt_desc)) + return 0; + + return ss_ep_comp_desc->bmAttributes; +} + +static u32 xhci_get_endpoint_max_burst(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc, + struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc) +{ + /* Super speed and Plus have max burst in ep companion desc */ + if (udev->speed >= USB_SPEED_SUPER) + return ss_ep_comp_desc->bMaxBurst; + + if (udev->speed == USB_SPEED_HIGH && + (usb_endpoint_xfer_isoc(endpt_desc) || + usb_endpoint_xfer_int(endpt_desc))) + return usb_endpoint_maxp_mult(endpt_desc) - 1; + + return 0; +} + +/* + * Return the maximum endpoint service interval time (ESIT) payload. + * Basically, this is the maxpacket size, multiplied by the burst size + * and mult size. + */ +static u32 xhci_get_max_esit_payload(struct usb_device *udev, + struct usb_endpoint_descriptor *endpt_desc, + struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc) +{ + int max_burst; + int max_packet; + + /* Only applies for interrupt or isochronous endpoints */ + if (usb_endpoint_xfer_control(endpt_desc) || + usb_endpoint_xfer_bulk(endpt_desc)) + return 0; + + /* SuperSpeed Isoc ep with less than 48k per esit */ + if (udev->speed >= USB_SPEED_SUPER) + return le16_to_cpu(ss_ep_comp_desc->wBytesPerInterval); + + max_packet = usb_endpoint_maxp(endpt_desc); + max_burst = usb_endpoint_maxp_mult(endpt_desc); + + /* A 0 in max burst means 1 transfer per ESIT */ + return max_packet * max_burst; +} + /** * Issue a configure endpoint command or evaluate context command * and wait for it to finish. @@ -324,6 +506,12 @@ static int xhci_set_configuration(struct usb_device *udev) int slot_id = udev->slot_id; struct xhci_virt_device *virt_dev = ctrl->devs[slot_id]; struct usb_interface *ifdesc; + u32 max_esit_payload; + unsigned int interval; + unsigned int mult; + unsigned int max_burst; + unsigned int avg_trb_len; + unsigned int err_count = 0; out_ctx = virt_dev->out_ctx; in_ctx = virt_dev->in_ctx; @@ -357,10 +545,28 @@ static int xhci_set_configuration(struct usb_device *udev) /* filling up ep contexts */ for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) { struct usb_endpoint_descriptor *endpt_desc = NULL; + struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc = NULL; endpt_desc = &ifdesc->ep_desc[cur_ep]; + ss_ep_comp_desc = &ifdesc->ss_ep_comp_desc[cur_ep]; trb_64 = 0; + /* + * Get values to fill the endpoint context, mostly from ep + * descriptor. The average TRB buffer lengt for bulk endpoints + * is unclear as we have no clue on scatter gather list entry + * size. For Isoc and Int, set it to max available. + * See xHCI 1.1 spec 4.14.1.1 for details. + */ + max_esit_payload = xhci_get_max_esit_payload(udev, endpt_desc, + ss_ep_comp_desc); + interval = xhci_get_endpoint_interval(udev, endpt_desc); + mult = xhci_get_endpoint_mult(udev, endpt_desc, + ss_ep_comp_desc); + max_burst = xhci_get_endpoint_max_burst(udev, endpt_desc, + ss_ep_comp_desc); + avg_trb_len = max_esit_payload; + ep_index = xhci_get_ep_index(endpt_desc); ep_ctx[ep_index] = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); @@ -372,20 +578,38 @@ static int xhci_set_configuration(struct usb_device *udev) /*NOTE: ep_desc[0] actually represents EP1 and so on */ dir = (((endpt_desc->bEndpointAddress) & (0x80)) >> 7); ep_type = (((endpt_desc->bmAttributes) & (0x3)) | (dir << 2)); + + ep_ctx[ep_index]->ep_info = + cpu_to_le32(EP_MAX_ESIT_PAYLOAD_HI(max_esit_payload) | + EP_INTERVAL(interval) | EP_MULT(mult)); + ep_ctx[ep_index]->ep_info2 = cpu_to_le32(ep_type << EP_TYPE_SHIFT); ep_ctx[ep_index]->ep_info2 |= cpu_to_le32(MAX_PACKET (get_unaligned(&endpt_desc->wMaxPacketSize))); + /* Allow 3 retries for everything but isoc, set CErr = 3 */ + if (!usb_endpoint_xfer_isoc(endpt_desc)) + err_count = 3; ep_ctx[ep_index]->ep_info2 |= - cpu_to_le32(((0 & MAX_BURST_MASK) << MAX_BURST_SHIFT) | - ((3 & ERROR_COUNT_MASK) << ERROR_COUNT_SHIFT)); + cpu_to_le32(MAX_BURST(max_burst) | + ERROR_COUNT(err_count)); trb_64 = (uintptr_t) virt_dev->eps[ep_index].ring->enqueue; ep_ctx[ep_index]->deq = cpu_to_le64(trb_64 | virt_dev->eps[ep_index].ring->cycle_state); + + /* + * xHCI spec 6.2.3: + * 'Average TRB Length' should be 8 for control endpoints. + */ + if (usb_endpoint_xfer_control(endpt_desc)) + avg_trb_len = 8; + ep_ctx[ep_index]->tx_info = + cpu_to_le32(EP_MAX_ESIT_PAYLOAD_LO(max_esit_payload) | + EP_AVG_TRB_LENGTH(avg_trb_len)); } return xhci_configure_endpoints(udev, false); @@ -546,16 +770,13 @@ int xhci_check_maxpacket(struct usb_device *udev) int max_packet_size; int hw_max_packet_size; int ret = 0; - struct usb_interface *ifdesc; - - ifdesc = &udev->config.if_desc[0]; out_ctx = ctrl->devs[slot_id]->out_ctx; xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size); ep_ctx = xhci_get_ep_ctx(ctrl, out_ctx, ep_index); hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2)); - max_packet_size = usb_endpoint_maxp(&ifdesc->ep_desc[0]); + max_packet_size = udev->epmaxpacketin[0]; if (hw_max_packet_size != max_packet_size) { debug("Max Packet Size for ep 0 changed.\n"); debug("Max packet size in usb_device = %d\n", max_packet_size); @@ -567,7 +788,8 @@ int xhci_check_maxpacket(struct usb_device *udev) ctrl->devs[slot_id]->out_ctx, ep_index); in_ctx = ctrl->devs[slot_id]->in_ctx; ep_ctx = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); - ep_ctx->ep_info2 &= cpu_to_le32(~MAX_PACKET_MASK); + ep_ctx->ep_info2 &= cpu_to_le32(~((0xffff & MAX_PACKET_MASK) + << MAX_PACKET_SHIFT)); ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet_size)); /* @@ -890,11 +1112,18 @@ unknown: static int _xhci_submit_int_msg(struct usb_device *udev, unsigned long pipe, void *buffer, int length, int interval) { + if (usb_pipetype(pipe) != PIPE_INTERRUPT) { + printf("non-interrupt pipe (type=%lu)", usb_pipetype(pipe)); + return -EINVAL; + } + /* - * TODO: Not addressing any interrupt type transfer requests - * Add support for it later. + * xHCI uses normal TRBs for both bulk and interrupt. When the + * interrupt endpoint is to be serviced, the xHC will consume + * (at most) one TD. A TD (comprised of sg list entries) can + * take several service intervals to transmit. */ - return -EINVAL; + return xhci_bulk_tx(udev, pipe, length, buffer); } /** diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 3377450..ba5f650 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -663,8 +663,9 @@ struct xhci_ep_ctx { #define GET_MAX_PACKET(p) ((p) & 0x7ff) /* tx_info bitmasks */ -#define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff) -#define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16) +#define EP_AVG_TRB_LENGTH(p) ((p) & 0xffff) +#define EP_MAX_ESIT_PAYLOAD_LO(p) (((p) & 0xffff) << 16) +#define EP_MAX_ESIT_PAYLOAD_HI(p) ((((p) >> 16) & 0xff) << 24) #define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff) /* deq bitmasks */ diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 0ad4782..264c971 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -418,6 +418,12 @@ struct __packed usb_class_report_descriptor { #define USB_ENDPOINT_XFER_INT 3 #define USB_ENDPOINT_MAX_ADJUSTABLE 0x80 +#define USB_ENDPOINT_MAXP_MASK 0x07ff +#define USB_EP_MAXP_MULT_SHIFT 11 +#define USB_EP_MAXP_MULT_MASK (3 << USB_EP_MAXP_MULT_SHIFT) +#define USB_EP_MAXP_MULT(m) \ + (((m) & USB_EP_MAXP_MULT_MASK) >> USB_EP_MAXP_MULT_SHIFT) + /* The USB 3.0 spec redefines bits 5:4 of bmAttributes as interrupt ep type. */ #define USB_ENDPOINT_INTRTYPE 0x30 #define USB_ENDPOINT_INTR_PERIODIC (0 << 4) @@ -625,6 +631,20 @@ static inline int usb_endpoint_maxp(const struct usb_endpoint_descriptor *epd) return __le16_to_cpu(get_unaligned(&epd->wMaxPacketSize)); } +/** + * usb_endpoint_maxp_mult - get endpoint's transactional opportunities + * @epd: endpoint to be checked + * + * Return @epd's wMaxPacketSize[12:11] + 1 + */ +static inline int +usb_endpoint_maxp_mult(const struct usb_endpoint_descriptor *epd) +{ + int maxp = __le16_to_cpu(epd->wMaxPacketSize); + + return USB_EP_MAXP_MULT(maxp) + 1; +} + static inline int usb_endpoint_interrupt_type( const struct usb_endpoint_descriptor *epd) { diff --git a/include/usb.h b/include/usb.h index 0ddc082..57a7d8d 100644 --- a/include/usb.h +++ b/include/usb.h @@ -653,6 +653,18 @@ struct usb_bus_priv { }; /** + * struct usb_emul_platdata - platform data about the USB emulator + * + * Given a USB emulator (UCLASS_USB_EMUL) 'dev', this is + * dev_get_uclass_platdata(dev). + * + * @port1: USB emulator device port number on the parent hub + */ +struct usb_emul_platdata { + int port1; /* Port number (numbered from 1) */ +}; + +/** * struct dm_usb_ops - USB controller operations * * This defines the operations supoorted on a USB controller. Common @@ -976,7 +988,6 @@ int usb_get_max_xfer_size(struct usb_device *dev, size_t *size); * the USB emulation uclass about the features of the emulator. * * @dev: Emulation device - * @maxpacketsize: Maximum packet size (e.g. PACKET_SIZE_64) * @strings: List of USB string descriptors, terminated by a NULL * entry * @desc_list: List of points or USB descriptors, terminated by NULL. @@ -984,8 +995,8 @@ int usb_get_max_xfer_size(struct usb_device *dev, size_t *size); * and others follow on after that. * @return 0 if OK, -ENOSYS if not implemented, other -ve on error */ -int usb_emul_setup_device(struct udevice *dev, int maxpacketsize, - struct usb_string *strings, void **desc_list); +int usb_emul_setup_device(struct udevice *dev, struct usb_string *strings, + void **desc_list); /** * usb_emul_control() - Send a control packet to an emulator @@ -1024,19 +1035,20 @@ int usb_emul_int(struct udevice *emul, struct usb_device *udev, /** * usb_emul_find() - Find an emulator for a particular device * - * Check @pipe to find a device number on bus @bus and return it. + * Check @pipe and @port1 to find a device number on bus @bus and return it. * * @bus: USB bus (controller) * @pipe: Describes pipe being used, and includes the device number + * @port1: Describes port number on the parent hub * @emulp: Returns pointer to emulator, or NULL if not found * @return 0 if found, -ve on error */ -int usb_emul_find(struct udevice *bus, ulong pipe, struct udevice **emulp); +int usb_emul_find(struct udevice *bus, ulong pipe, int port1, + struct udevice **emulp); /** * usb_emul_find_for_dev() - Find an emulator for a particular device * - * @bus: USB bus (controller) * @dev: USB device to check * @emulp: Returns pointer to emulator, or NULL if not found * @return 0 if found, -ve on error @@ -1044,12 +1056,15 @@ int usb_emul_find(struct udevice *bus, ulong pipe, struct udevice **emulp); int usb_emul_find_for_dev(struct udevice *dev, struct udevice **emulp); /** - * usb_emul_reset() - Reset all emulators ready for use + * usb_emul_find_descriptor() - Find a USB descriptor of a particular device * - * Clear out any address information in the emulators and make then ready for - * a new USB scan + * @ptr: a pointer to a list of USB descriptor pointers + * @type: type of USB descriptor to find + * @index: if @type is USB_DT_CONFIG, this is the configuration value + * @return a pointer to the USB descriptor found, NULL if not found */ -void usb_emul_reset(struct udevice *dev); +struct usb_generic_descriptor **usb_emul_find_descriptor( + struct usb_generic_descriptor **ptr, int type, int index); /** * usb_show_tree() - show the USB device tree diff --git a/test/dm/usb.c b/test/dm/usb.c index b46ae60..4fd249b 100644 --- a/test/dm/usb.c +++ b/test/dm/usb.c @@ -99,10 +99,10 @@ static int count_usb_devices(void) return count; } -/* test that we can remove an emulated device and it is then not found */ -static int dm_test_usb_remove(struct unit_test_state *uts) +/* 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, *emul; + struct udevice *dev; /* Scan and check that all devices are present */ state_set_skip_delays(true); @@ -112,164 +112,11 @@ static int dm_test_usb_remove(struct unit_test_state *uts) ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 2, &dev)); ut_asserteq(6, count_usb_devices()); ut_assertok(usb_stop()); - ut_asserteq(6, count_usb_devices()); - - /* Remove the second emulation device */ - ut_assertok(uclass_find_device_by_name(UCLASS_USB_EMUL, "flash-stick@1", - &dev)); - ut_assertok(device_unbind(dev)); - - /* Rescan - only the first and third should be present */ - ut_assertok(usb_init()); - ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &dev)); - ut_assertok(usb_emul_find_for_dev(dev, &emul)); - ut_asserteq_str("flash-stick@0", emul->name); - ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 1, &dev)); - ut_assertok(usb_emul_find_for_dev(dev, &emul)); - ut_asserteq_str("flash-stick@2", emul->name); - - ut_asserteq(-ENODEV, uclass_get_device(UCLASS_MASS_STORAGE, 2, &dev)); - - ut_asserteq(5, count_usb_devices()); - ut_assertok(usb_stop()); - ut_asserteq(5, count_usb_devices()); - - return 0; -} -DM_TEST(dm_test_usb_remove, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); - -const char usb_tree_base[] = -" 1 Hub (12 Mb/s, 100mA)\n" -" | sandbox hub 2345\n" -" |\n" -" |\b+-2 Mass Storage (12 Mb/s, 100mA)\n" -" | sandbox flash flash-stick@0\n" -" | \n" -" |\b+-3 Mass Storage (12 Mb/s, 100mA)\n" -" | sandbox flash flash-stick@1\n" -" | \n" -" |\b+-4 Mass Storage (12 Mb/s, 100mA)\n" -" | sandbox flash flash-stick@2\n" -" | \n" -" |\b+-5 Human Interface (12 Mb/s, 100mA)\n" -" sandbox keyboard keyb@3\n" -" \n"; - -/* test that the 'usb tree' command output looks correct */ -static int dm_test_usb_tree(struct unit_test_state *uts) -{ - char *data; - int len; - - state_set_skip_delays(true); - ut_assertok(usb_init()); - console_record_reset_enable(); - usb_show_tree(); - len = membuff_getraw(&gd->console_out, -1, true, &data); - if (len) - data[len] = '\0'; - ut_asserteq_str(usb_tree_base, data); - ut_assertok(usb_stop()); - - return 0; -} -DM_TEST(dm_test_usb_tree, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); - -const char usb_tree_remove[] = -" 1 Hub (12 Mb/s, 100mA)\n" -" | sandbox hub 2345\n" -" |\n" -" |\b+-2 Mass Storage (12 Mb/s, 100mA)\n" -" | sandbox flash flash-stick@0\n" -" | \n" -" |\b+-3 Mass Storage (12 Mb/s, 100mA)\n" -" | sandbox flash flash-stick@2\n" -" | \n" -" |\b+-4 Human Interface (12 Mb/s, 100mA)\n" -" sandbox keyboard keyb@3\n" -" \n"; - -/* - * test that the 'usb tree' command output looks correct when we remove a - * device - */ -static int dm_test_usb_tree_remove(struct unit_test_state *uts) -{ - struct udevice *dev; - char *data; - int len; - - /* Remove the second emulation device */ - ut_assertok(uclass_find_device_by_name(UCLASS_USB_EMUL, "flash-stick@1", - &dev)); - ut_assertok(device_unbind(dev)); - - state_set_skip_delays(true); - ut_assertok(usb_init()); - console_record_reset_enable(); - usb_show_tree(); - len = membuff_getraw(&gd->console_out, -1, true, &data); - if (len) - data[len] = '\0'; - ut_asserteq_str(usb_tree_remove, data); - ut_assertok(usb_stop()); - - return 0; -} -DM_TEST(dm_test_usb_tree_remove, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); - -const char usb_tree_reorder[] = -" 1 Hub (12 Mb/s, 100mA)\n" -" | sandbox hub 2345\n" -" |\n" -" |\b+-2 Mass Storage (12 Mb/s, 100mA)\n" -" | sandbox flash flash-stick@0\n" -" | \n" -" |\b+-3 Mass Storage (12 Mb/s, 100mA)\n" -" | sandbox flash flash-stick@2\n" -" | \n" -" |\b+-4 Human Interface (12 Mb/s, 100mA)\n" -" | sandbox keyboard keyb@3\n" -" | \n" -" |\b+-5 Mass Storage (12 Mb/s, 100mA)\n" -" sandbox flash flash-stick@1\n" -" \n"; - -/* - * test that the 'usb tree' command output looks correct when we reorder two - * devices. - */ -static int dm_test_usb_tree_reorder(struct unit_test_state *uts) -{ - struct udevice *dev, *parent; - char *data; - int len; - - /* Remove the second emulation device */ - ut_assertok(uclass_find_device_by_name(UCLASS_USB_EMUL, "flash-stick@1", - &dev)); - parent = dev->parent; - - /* Reorder the devices in the parent list and uclass list */ - list_del(&dev->sibling_node); - list_add_tail(&dev->sibling_node, &parent->child_head); - - list_del(&dev->uclass_node); - list_add_tail(&dev->uclass_node, &dev->uclass->dev_head); - - state_set_skip_delays(true); - ut_assertok(usb_init()); - console_record_reset_enable(); - usb_show_tree(); - len = membuff_getraw(&gd->console_out, -1, true, &data); - if (len) - data[len] = '\0'; - ut_asserteq_str(usb_tree_reorder, data); - ut_assertok(usb_stop()); + ut_asserteq(0, count_usb_devices()); return 0; } -DM_TEST(dm_test_usb_tree_reorder, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); +DM_TEST(dm_test_usb_stop, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); static int dm_test_usb_keyb(struct unit_test_state *uts) { |