Merge git://git.denx.de/u-boot-usb

This commit is contained in:
Tom Rini 2017-10-01 18:06:53 -04:00
commit 4d1c166fee
18 changed files with 612 additions and 251 deletions

View file

@ -437,12 +437,13 @@ static int usb_parse_config(struct usb_device *dev,
} }
break; break;
case USB_DT_ENDPOINT: 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", printf("ERROR: Invalid USB EP length (%d)\n",
head->bLength); head->bLength);
break; break;
} }
if (index + USB_DT_ENDPOINT_SIZE > if (index + head->bLength >
dev->config.desc.wTotalLength) { dev->config.desc.wTotalLength) {
puts("USB EP descriptor overflowed buffer!\n"); puts("USB EP descriptor overflowed buffer!\n");
break; break;
@ -969,23 +970,24 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read)
dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0; dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0;
dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
if (do_read) { if (do_read && dev->speed == USB_SPEED_FULL) {
int err; int err;
/* /*
* Validate we've received only at least 8 bytes, not that we've * Validate we've received only at least 8 bytes, not that
* received the entire descriptor. The reasoning is: * we've received the entire descriptor. The reasoning is:
* - The code only uses fields in the first 8 bytes, so that's all we * - The code only uses fields in the first 8 bytes, so
* need to have fetched at this stage. * that's all we need to have fetched at this stage.
* - The smallest maxpacket size is 8 bytes. Before we know the actual * - The smallest maxpacket size is 8 bytes. Before we know
* maxpacket the device uses, the USB controller may only accept a * the actual maxpacket the device uses, the USB controller
* single packet. Consequently we are only guaranteed to receive 1 * may only accept a single packet. Consequently we are only
* packet (at least 8 bytes) even in a non-error case. * 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 * At least the DWC2 controller needs to be programmed with
* of packets in addition to the number of bytes. A request for 64 * the number of packets in addition to the number of bytes.
* bytes of data with the maxpacket guessed as 64 (above) yields a * A request for 64 bytes of data with the maxpacket guessed
* request for 1 packet. * as 64 (above) yields a request for 1 packet.
*/ */
err = get_descriptor_len(dev, 64, 8); err = get_descriptor_len(dev, 64, 8);
if (err) if (err)
@ -1008,7 +1010,7 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read)
dev->maxpacketsize = PACKET_SIZE_64; dev->maxpacketsize = PACKET_SIZE_64;
break; break;
default: default:
printf("usb_new_device: invalid max packet size\n"); printf("%s: invalid max packet size\n", __func__);
return -EIO; 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 */ 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; return 0;
} }

View file

@ -489,6 +489,17 @@ static int usb_scan_port(struct usb_device_scan *usb_scan)
return 0; 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 */ /* A new USB device is ready at this point */
debug("devnum=%d port=%d: USB dev found\n", dev->devnum, i + 1); 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]); 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 * We're done with this device, so let's remove this device from
* scanning list * scanning list

View file

@ -964,7 +964,7 @@ static void usb_stor_set_max_xfer_blk(struct usb_device *udev,
blk = 20; blk = 20;
} else { } else {
if (size > USHRT_MAX * 512) if (size > USHRT_MAX * 512)
blk = USHRT_MAX; size = USHRT_MAX * 512;
blk = size / 512; blk = size / 512;
} }
#endif #endif

View file

@ -37,6 +37,13 @@ config USB_DWC3_OMAP
Say 'Y' here if you have one such device 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" menu "PHY Subsystem"
config USB_DWC3_PHY_OMAP config USB_DWC3_PHY_OMAP

View file

@ -9,5 +9,6 @@ dwc3-y := core.o
obj-$(CONFIG_USB_DWC3_GADGET) += gadget.o ep0.o obj-$(CONFIG_USB_DWC3_GADGET) += gadget.o ep0.o
obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.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_OMAP) += ti_usb_phy.o
obj-$(CONFIG_USB_DWC3_PHY_SAMSUNG) += samsung_usb_phy.o obj-$(CONFIG_USB_DWC3_PHY_SAMSUNG) += samsung_usb_phy.o

View file

@ -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,
};

View file

@ -390,8 +390,7 @@ static int sandbox_flash_bind(struct udevice *dev)
fs[2].id = STRINGID_SERIAL; fs[2].id = STRINGID_SERIAL;
fs[2].s = dev->name; fs[2].s = dev->name;
return usb_emul_setup_device(dev, PACKET_SIZE_64, plat->flash_strings, return usb_emul_setup_device(dev, plat->flash_strings, flash_desc_list);
flash_desc_list);
} }
static int sandbox_flash_probe(struct udevice *dev) static int sandbox_flash_probe(struct udevice *dev)

View file

@ -121,9 +121,12 @@ struct sandbox_hub_priv {
int change[SANDBOX_NUM_PORTS]; 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 udevice *dev;
struct usb_generic_descriptor **gen_desc;
struct usb_device_descriptor **dev_desc;
for (device_find_first_child(hub, &dev); for (device_find_first_child(hub, &dev);
dev; dev;
@ -131,8 +134,27 @@ static struct udevice *hub_find_device(struct udevice *hub, int port)
struct sandbox_hub_platdata *plat; struct sandbox_hub_platdata *plat;
plat = dev_get_parent_platdata(dev); 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 dev;
}
} }
return NULL; return NULL;
@ -146,7 +168,8 @@ static int clrset_post_state(struct udevice *hub, int port, int clear, int set)
int ret = 0; int ret = 0;
if ((clear | set) & USB_PORT_STAT_POWER) { 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 (dev) {
if (set & USB_PORT_STAT_POWER) { 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) { if (!ret) {
set |= USB_PORT_STAT_CONNECTION | set |= USB_PORT_STAT_CONNECTION |
USB_PORT_STAT_ENABLE; 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) { } 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) static int sandbox_hub_bind(struct udevice *dev)
{ {
return usb_emul_setup_device(dev, PACKET_SIZE_64, hub_strings, return usb_emul_setup_device(dev, hub_strings, hub_desc_list);
hub_desc_list);
} }
static int sandbox_child_post_bind(struct udevice *dev) static int sandbox_child_post_bind(struct udevice *dev)
{ {
struct sandbox_hub_platdata *plat = dev_get_parent_platdata(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); plat->port = dev_read_u32_default(dev, "reg", -1);
emul->port1 = plat->port + 1;
return 0; return 0;
} }

View file

@ -208,8 +208,7 @@ static int sandbox_keyb_bind(struct udevice *dev)
fs[2].id = STRINGID_SERIAL; fs[2].id = STRINGID_SERIAL;
fs[2].s = dev->name; fs[2].s = dev->name;
return usb_emul_setup_device(dev, PACKET_SIZE_8, plat->keyb_strings, return usb_emul_setup_device(dev, plat->keyb_strings, keyb_desc_list);
keyb_desc_list);
} }
static int sandbox_keyb_probe(struct udevice *dev) static int sandbox_keyb_probe(struct udevice *dev)

View file

@ -52,7 +52,7 @@ static int usb_emul_get_string(struct usb_string *strings, int index,
return -EINVAL; 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) struct usb_generic_descriptor **ptr, int type, int index)
{ {
debug("%s: type=%x, index=%d\n", __func__, type, 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); length);
} }
ptr = find_descriptor((struct usb_generic_descriptor **)plat->desc_list, ptr = usb_emul_find_descriptor(plat->desc_list, type, index);
type, index);
if (!ptr) { if (!ptr) {
debug("%s: Could not find descriptor type %d, index %d\n", debug("%s: Could not find descriptor type %d, index %d\n",
__func__, type, index); __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; 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 udevice *dev;
struct uclass *uc; struct uclass *uc;
@ -120,7 +119,37 @@ static int usb_emul_find_devnum(int devnum, struct udevice **emulp)
uclass_foreach_dev(dev, uc) { uclass_foreach_dev(dev, uc) {
struct usb_dev_platdata *udev = dev_get_parent_platdata(dev); 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__, debug("%s: Found emulator '%s', addr %d\n", __func__,
dev->name, udev->devnum); dev->name, udev->devnum);
*emulp = dev; *emulp = dev;
@ -132,18 +161,19 @@ static int usb_emul_find_devnum(int devnum, struct udevice **emulp)
return -ENOENT; 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); 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) int usb_emul_find_for_dev(struct udevice *dev, struct udevice **emulp)
{ {
struct usb_dev_platdata *udev = dev_get_parent_platdata(dev); 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, 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); return ops->interrupt(emul, udev, pipe, buffer, length, interval);
} }
int usb_emul_setup_device(struct udevice *dev, int maxpacketsize, int usb_emul_setup_device(struct udevice *dev, struct usb_string *strings,
struct usb_string *strings, void **desc_list) void **desc_list)
{ {
struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); struct usb_dev_platdata *plat = dev_get_parent_platdata(dev);
struct usb_generic_descriptor **ptr; struct usb_generic_descriptor **ptr;
@ -264,18 +294,11 @@ int usb_emul_setup_device(struct udevice *dev, int maxpacketsize,
return 0; 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) = { UCLASS_DRIVER(usb_emul) = {
.id = UCLASS_USB_EMUL, .id = UCLASS_USB_EMUL,
.name = "usb_emul", .name = "usb_emul",
.post_bind = dm_scan_fdt_dev, .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_auto_alloc_size = sizeof(struct usb_device),
.per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata), .per_child_platdata_auto_alloc_size = sizeof(struct usb_dev_platdata),
}; };

View file

@ -12,6 +12,10 @@
DECLARE_GLOBAL_DATA_PTR; DECLARE_GLOBAL_DATA_PTR;
struct sandbox_usb_ctrl {
int rootdev;
};
static void usbmon_trace(struct udevice *bus, ulong pipe, static void usbmon_trace(struct udevice *bus, ulong pipe,
struct devrequest *setup, struct udevice *emul) struct devrequest *setup, struct udevice *emul)
{ {
@ -40,15 +44,24 @@ static int sandbox_submit_control(struct udevice *bus,
void *buffer, int length, void *buffer, int length,
struct devrequest *setup) struct devrequest *setup)
{ {
struct sandbox_usb_ctrl *ctrl = dev_get_priv(bus);
struct udevice *emul; struct udevice *emul;
int ret; int ret;
/* Just use child of dev as emulator? */ /* Just use child of dev as emulator? */
debug("%s: bus=%s\n", __func__, bus->name); 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); usbmon_trace(bus, pipe, setup, emul);
if (ret) if (ret)
return 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); ret = usb_emul_control(emul, udev, pipe, buffer, length, setup);
if (ret < 0) { if (ret < 0) {
debug("ret=%d\n", ret); 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? */ /* Just use child of dev as emulator? */
debug("%s: bus=%s\n", __func__, bus->name); 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); usbmon_trace(bus, pipe, NULL, emul);
if (ret) if (ret)
return 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? */ /* Just use child of dev as emulator? */
debug("%s: bus=%s\n", __func__, bus->name); 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); usbmon_trace(bus, pipe, NULL, emul);
if (ret) if (ret)
return 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) 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; return 0;
} }
@ -133,4 +156,5 @@ U_BOOT_DRIVER(usb_sandbox) = {
.of_match = sandbox_usb_ids, .of_match = sandbox_usb_ids,
.probe = sandbox_usb_probe, .probe = sandbox_usb_probe,
.ops = &sandbox_usb_ops, .ops = &sandbox_usb_ops,
.priv_auto_alloc_size = sizeof(struct sandbox_usb_ctrl),
}; };

View file

@ -164,6 +164,7 @@ int usb_get_max_xfer_size(struct usb_device *udev, size_t *size)
int usb_stop(void) int usb_stop(void)
{ {
struct udevice *bus; struct udevice *bus;
struct udevice *rh;
struct uclass *uc; struct uclass *uc;
struct usb_uclass_priv *uc_priv; struct usb_uclass_priv *uc_priv;
int err = 0, ret; int err = 0, ret;
@ -179,23 +180,20 @@ int usb_stop(void)
ret = device_remove(bus, DM_REMOVE_NORMAL); ret = device_remove(bus, DM_REMOVE_NORMAL);
if (ret && !err) if (ret && !err)
err = ret; err = 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;
}
} }
#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;
uclass_foreach_dev(dev, uc)
usb_emul_reset(dev);
#endif
#ifdef CONFIG_USB_STORAGE #ifdef CONFIG_USB_STORAGE
usb_stor_reset(); usb_stor_reset();
#endif #endif
@ -262,6 +260,21 @@ int usb_init(void)
/* init low_level USB */ /* init low_level USB */
printf("USB%d: ", count); printf("USB%d: ", count);
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); ret = device_probe(bus);
if (ret == -ENODEV) { /* No such device. */ if (ret == -ENODEV) { /* No such device. */
puts("Port not available.\n"); puts("Port not available.\n");

View file

@ -786,12 +786,22 @@ void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl,
#ifdef CONFIG_DM_USB #ifdef CONFIG_DM_USB
/* Set up TT fields to support FS/LS devices */ /* Set up TT fields to support FS/LS devices */
if (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL) { if (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL) {
dev = dev_get_parent_priv(udev->dev); struct udevice *parent = udev->dev;
if (dev->speed == USB_SPEED_HIGH) {
hub = dev_get_uclass_priv(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) if (hub->tt.multi)
slot_ctx->dev_info |= cpu_to_le32(DEV_MTT); 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)); 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; 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); 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() */ /* Steps 7 and 8 were done in xhci_alloc_virt_device() */
xhci_flush_cache((uintptr_t)ep0_ctx, sizeof(struct xhci_ep_ctx)); xhci_flush_cache((uintptr_t)ep0_ctx, sizeof(struct xhci_ep_ctx));

View file

@ -257,6 +257,188 @@ static unsigned int xhci_get_ep_index(struct usb_endpoint_descriptor *desc)
return index; 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 * Issue a configure endpoint command or evaluate context command
* and wait for it to finish. * 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; int slot_id = udev->slot_id;
struct xhci_virt_device *virt_dev = ctrl->devs[slot_id]; struct xhci_virt_device *virt_dev = ctrl->devs[slot_id];
struct usb_interface *ifdesc; 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; out_ctx = virt_dev->out_ctx;
in_ctx = virt_dev->in_ctx; in_ctx = virt_dev->in_ctx;
@ -357,10 +545,28 @@ static int xhci_set_configuration(struct usb_device *udev)
/* filling up ep contexts */ /* filling up ep contexts */
for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) { for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) {
struct usb_endpoint_descriptor *endpt_desc = NULL; 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]; endpt_desc = &ifdesc->ep_desc[cur_ep];
ss_ep_comp_desc = &ifdesc->ss_ep_comp_desc[cur_ep];
trb_64 = 0; 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_index = xhci_get_ep_index(endpt_desc);
ep_ctx[ep_index] = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); 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 */ /*NOTE: ep_desc[0] actually represents EP1 and so on */
dir = (((endpt_desc->bEndpointAddress) & (0x80)) >> 7); dir = (((endpt_desc->bEndpointAddress) & (0x80)) >> 7);
ep_type = (((endpt_desc->bmAttributes) & (0x3)) | (dir << 2)); 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 = ep_ctx[ep_index]->ep_info2 =
cpu_to_le32(ep_type << EP_TYPE_SHIFT); cpu_to_le32(ep_type << EP_TYPE_SHIFT);
ep_ctx[ep_index]->ep_info2 |= ep_ctx[ep_index]->ep_info2 |=
cpu_to_le32(MAX_PACKET cpu_to_le32(MAX_PACKET
(get_unaligned(&endpt_desc->wMaxPacketSize))); (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 |= ep_ctx[ep_index]->ep_info2 |=
cpu_to_le32(((0 & MAX_BURST_MASK) << MAX_BURST_SHIFT) | cpu_to_le32(MAX_BURST(max_burst) |
((3 & ERROR_COUNT_MASK) << ERROR_COUNT_SHIFT)); ERROR_COUNT(err_count));
trb_64 = (uintptr_t) trb_64 = (uintptr_t)
virt_dev->eps[ep_index].ring->enqueue; virt_dev->eps[ep_index].ring->enqueue;
ep_ctx[ep_index]->deq = cpu_to_le64(trb_64 | ep_ctx[ep_index]->deq = cpu_to_le64(trb_64 |
virt_dev->eps[ep_index].ring->cycle_state); 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); return xhci_configure_endpoints(udev, false);
@ -546,16 +770,13 @@ int xhci_check_maxpacket(struct usb_device *udev)
int max_packet_size; int max_packet_size;
int hw_max_packet_size; int hw_max_packet_size;
int ret = 0; int ret = 0;
struct usb_interface *ifdesc;
ifdesc = &udev->config.if_desc[0];
out_ctx = ctrl->devs[slot_id]->out_ctx; out_ctx = ctrl->devs[slot_id]->out_ctx;
xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size); xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size);
ep_ctx = xhci_get_ep_ctx(ctrl, out_ctx, ep_index); 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)); 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) { if (hw_max_packet_size != max_packet_size) {
debug("Max Packet Size for ep 0 changed.\n"); debug("Max Packet Size for ep 0 changed.\n");
debug("Max packet size in usb_device = %d\n", max_packet_size); 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); ctrl->devs[slot_id]->out_ctx, ep_index);
in_ctx = ctrl->devs[slot_id]->in_ctx; in_ctx = ctrl->devs[slot_id]->in_ctx;
ep_ctx = xhci_get_ep_ctx(ctrl, in_ctx, ep_index); 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)); 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, static int _xhci_submit_int_msg(struct usb_device *udev, unsigned long pipe,
void *buffer, int length, int interval) 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 * xHCI uses normal TRBs for both bulk and interrupt. When the
* Add support for it later. * 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);
} }
/** /**

View file

@ -663,8 +663,9 @@ struct xhci_ep_ctx {
#define GET_MAX_PACKET(p) ((p) & 0x7ff) #define GET_MAX_PACKET(p) ((p) & 0x7ff)
/* tx_info bitmasks */ /* tx_info bitmasks */
#define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff) #define EP_AVG_TRB_LENGTH(p) ((p) & 0xffff)
#define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16) #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) #define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff)
/* deq bitmasks */ /* deq bitmasks */

View file

@ -418,6 +418,12 @@ struct __packed usb_class_report_descriptor {
#define USB_ENDPOINT_XFER_INT 3 #define USB_ENDPOINT_XFER_INT 3
#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80 #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. */ /* The USB 3.0 spec redefines bits 5:4 of bmAttributes as interrupt ep type. */
#define USB_ENDPOINT_INTRTYPE 0x30 #define USB_ENDPOINT_INTRTYPE 0x30
#define USB_ENDPOINT_INTR_PERIODIC (0 << 4) #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)); 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( static inline int usb_endpoint_interrupt_type(
const struct usb_endpoint_descriptor *epd) const struct usb_endpoint_descriptor *epd)
{ {

View file

@ -652,6 +652,18 @@ struct usb_bus_priv {
bool companion; bool companion;
}; };
/**
* 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 * struct dm_usb_ops - USB controller operations
* *
@ -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. * the USB emulation uclass about the features of the emulator.
* *
* @dev: Emulation device * @dev: Emulation device
* @maxpacketsize: Maximum packet size (e.g. PACKET_SIZE_64)
* @strings: List of USB string descriptors, terminated by a NULL * @strings: List of USB string descriptors, terminated by a NULL
* entry * entry
* @desc_list: List of points or USB descriptors, terminated by NULL. * @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. * and others follow on after that.
* @return 0 if OK, -ENOSYS if not implemented, other -ve on error * @return 0 if OK, -ENOSYS if not implemented, other -ve on error
*/ */
int usb_emul_setup_device(struct udevice *dev, int maxpacketsize, int usb_emul_setup_device(struct udevice *dev, struct usb_string *strings,
struct usb_string *strings, void **desc_list); void **desc_list);
/** /**
* usb_emul_control() - Send a control packet to an emulator * 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 * 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) * @bus: USB bus (controller)
* @pipe: Describes pipe being used, and includes the device number * @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 * @emulp: Returns pointer to emulator, or NULL if not found
* @return 0 if found, -ve on error * @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 * usb_emul_find_for_dev() - Find an emulator for a particular device
* *
* @bus: USB bus (controller)
* @dev: USB device to check * @dev: USB device to check
* @emulp: Returns pointer to emulator, or NULL if not found * @emulp: Returns pointer to emulator, or NULL if not found
* @return 0 if found, -ve on error * @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); 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 * @ptr: a pointer to a list of USB descriptor pointers
* a new USB scan * @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 * usb_show_tree() - show the USB device tree

View file

@ -99,10 +99,10 @@ static int count_usb_devices(void)
return count; return count;
} }
/* test that we can remove an emulated device and it is then not found */ /* test that no USB devices are found after we stop the stack */
static int dm_test_usb_remove(struct unit_test_state *uts) 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 */ /* Scan and check that all devices are present */
state_set_skip_delays(true); 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_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 2, &dev));
ut_asserteq(6, count_usb_devices()); ut_asserteq(6, count_usb_devices());
ut_assertok(usb_stop()); ut_assertok(usb_stop());
ut_asserteq(6, count_usb_devices()); ut_asserteq(0, 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; return 0;
} }
DM_TEST(dm_test_usb_remove, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); DM_TEST(dm_test_usb_stop, 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());
return 0;
}
DM_TEST(dm_test_usb_tree_reorder, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
static int dm_test_usb_keyb(struct unit_test_state *uts) static int dm_test_usb_keyb(struct unit_test_state *uts)
{ {