summaryrefslogtreecommitdiff
path: root/drivers/usb/host/usb-uclass.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/usb-uclass.c')
-rw-r--r--drivers/usb/host/usb-uclass.c180
1 files changed, 132 insertions, 48 deletions
diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c
index 714bc0e..963464c 100644
--- a/drivers/usb/host/usb-uclass.c
+++ b/drivers/usb/host/usb-uclass.c
@@ -21,6 +21,10 @@ DECLARE_GLOBAL_DATA_PTR;
extern bool usb_started; /* flag for the started/stopped USB status */
static bool asynch_allowed;
+struct usb_uclass_priv {
+ int companion_device_count;
+};
+
int usb_disable_asynch(int disable)
{
int old_value = asynch_allowed;
@@ -46,11 +50,22 @@ int submit_control_msg(struct usb_device *udev, unsigned long pipe,
{
struct udevice *bus = udev->controller_dev;
struct dm_usb_ops *ops = usb_get_ops(bus);
+ struct usb_uclass_priv *uc_priv = bus->uclass->priv;
+ int err;
if (!ops->control)
return -ENOSYS;
- return ops->control(bus, udev, pipe, buffer, length, setup);
+ err = ops->control(bus, udev, pipe, buffer, length, setup);
+ if (setup->request == USB_REQ_SET_FEATURE &&
+ setup->requesttype == USB_RT_PORT &&
+ setup->value == cpu_to_le16(USB_PORT_FEAT_RESET) &&
+ err == -ENXIO) {
+ /* Device handed over to companion after port reset */
+ uc_priv->companion_device_count++;
+ }
+
+ return err;
}
int submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
@@ -65,6 +80,42 @@ int submit_bulk_msg(struct usb_device *udev, unsigned long pipe, void *buffer,
return ops->bulk(bus, udev, pipe, buffer, length);
}
+struct int_queue *create_int_queue(struct usb_device *udev,
+ unsigned long pipe, int queuesize, int elementsize,
+ void *buffer, int interval)
+{
+ struct udevice *bus = udev->controller_dev;
+ struct dm_usb_ops *ops = usb_get_ops(bus);
+
+ if (!ops->create_int_queue)
+ return NULL;
+
+ return ops->create_int_queue(bus, udev, pipe, queuesize, elementsize,
+ buffer, interval);
+}
+
+void *poll_int_queue(struct usb_device *udev, struct int_queue *queue)
+{
+ struct udevice *bus = udev->controller_dev;
+ struct dm_usb_ops *ops = usb_get_ops(bus);
+
+ if (!ops->poll_int_queue)
+ return NULL;
+
+ return ops->poll_int_queue(bus, udev, queue);
+}
+
+int destroy_int_queue(struct usb_device *udev, struct int_queue *queue)
+{
+ struct udevice *bus = udev->controller_dev;
+ struct dm_usb_ops *ops = usb_get_ops(bus);
+
+ if (!ops->destroy_int_queue)
+ return -ENOSYS;
+
+ return ops->destroy_int_queue(bus, udev, queue);
+}
+
int usb_alloc_device(struct usb_device *udev)
{
struct udevice *bus = udev->controller_dev;
@@ -81,12 +132,16 @@ int usb_stop(void)
{
struct udevice *bus;
struct uclass *uc;
+ struct usb_uclass_priv *uc_priv;
int err = 0, ret;
/* De-activate any devices that have been activated */
ret = uclass_get(UCLASS_USB, &uc);
if (ret)
return ret;
+
+ uc_priv = uc->priv;
+
uclass_foreach_dev(bus, uc) {
ret = device_remove(bus);
if (ret && !err)
@@ -106,12 +161,13 @@ int usb_stop(void)
#endif
usb_stor_reset();
usb_hub_reset();
+ uc_priv->companion_device_count = 0;
usb_started = 0;
return err;
}
-static int usb_scan_bus(struct udevice *bus, bool recurse)
+static void usb_scan_bus(struct udevice *bus, bool recurse)
{
struct usb_bus_priv *priv;
struct udevice *dev;
@@ -121,16 +177,22 @@ static int usb_scan_bus(struct udevice *bus, bool recurse)
assert(recurse); /* TODO: Support non-recusive */
+ printf("scanning bus %d for devices... ", bus->seq);
+ debug("\n");
ret = usb_scan_device(bus, 0, USB_SPEED_FULL, &dev);
if (ret)
- return ret;
-
- return priv->next_addr;
+ printf("failed, error %d\n", ret);
+ else if (priv->next_addr == 0)
+ printf("No USB Device found\n");
+ else
+ printf("%d USB Device(s) found\n", priv->next_addr);
}
int usb_init(void)
{
int controllers_initialized = 0;
+ struct usb_uclass_priv *uc_priv;
+ struct usb_bus_priv *priv;
struct udevice *bus;
struct uclass *uc;
int count = 0;
@@ -143,11 +205,12 @@ int usb_init(void)
if (ret)
return ret;
+ uc_priv = uc->priv;
+
uclass_foreach_dev(bus, uc) {
/* init low_level USB */
+ printf("USB%d: ", count);
count++;
- printf("USB");
- printf("%d: ", bus->seq);
ret = device_probe(bus);
if (ret == -ENODEV) { /* No such device. */
puts("Port not available.\n");
@@ -159,23 +222,39 @@ int usb_init(void)
printf("probe failed, error %d\n", ret);
continue;
}
- /*
- * lowlevel init is OK, now scan the bus for devices
- * i.e. search HUBs and configure them
- */
controllers_initialized++;
- printf("scanning bus %d for devices... ", bus->seq);
- debug("\n");
- ret = usb_scan_bus(bus, true);
- if (ret < 0)
- printf("failed, error %d\n", ret);
- else if (!ret)
- printf("No USB Device found\n");
- else
- printf("%d USB Device(s) found\n", ret);
usb_started = true;
}
+ /*
+ * lowlevel init done, now scan the bus for devices i.e. search HUBs
+ * and configure them, first scan primary controllers.
+ */
+ uclass_foreach_dev(bus, uc) {
+ if (!device_active(bus))
+ continue;
+
+ priv = dev_get_uclass_priv(bus);
+ if (!priv->companion)
+ usb_scan_bus(bus, true);
+ }
+
+ /*
+ * Now that the primary controllers have been scanned and have handed
+ * over any devices they do not understand to their companions, scan
+ * the companions if necessary.
+ */
+ if (uc_priv->companion_device_count) {
+ uclass_foreach_dev(bus, uc) {
+ if (!device_active(bus))
+ continue;
+
+ priv = dev_get_uclass_priv(bus);
+ if (priv->companion)
+ usb_scan_bus(bus, true);
+ }
+ }
+
debug("scan end\n");
/* if we were not able to find at least one working bus, bail out */
if (!count)
@@ -477,9 +556,7 @@ int usb_scan_device(struct udevice *parent, int port,
*devp = NULL;
memset(udev, '\0', sizeof(*udev));
- ret = usb_get_bus(parent, &udev->controller_dev);
- if (ret)
- return ret;
+ udev->controller_dev = usb_get_bus(parent);
priv = dev_get_uclass_priv(udev->controller_dev);
/*
@@ -536,11 +613,7 @@ int usb_scan_device(struct udevice *parent, int port,
plat = dev_get_parent_platdata(dev);
debug("%s: Probing '%s', plat=%p\n", __func__, dev->name, plat);
plat->devnum = udev->devnum;
- plat->speed = udev->speed;
- plat->slot_id = udev->slot_id;
- plat->portnr = port;
- debug("** device '%s': stashing slot_id=%d\n", dev->name,
- plat->slot_id);
+ plat->udev = udev;
priv->next_addr++;
ret = device_probe(dev);
if (ret) {
@@ -579,45 +652,55 @@ int usb_child_post_bind(struct udevice *dev)
return 0;
}
-int usb_get_bus(struct udevice *dev, struct udevice **busp)
+struct udevice *usb_get_bus(struct udevice *dev)
{
struct udevice *bus;
- *busp = NULL;
for (bus = dev; bus && device_get_uclass_id(bus) != UCLASS_USB; )
bus = bus->parent;
if (!bus) {
/* By design this cannot happen */
assert(bus);
debug("USB HUB '%s' does not have a controller\n", dev->name);
- return -EXDEV;
}
- *busp = bus;
- return 0;
+ return bus;
}
int usb_child_pre_probe(struct udevice *dev)
{
- struct udevice *bus;
struct usb_device *udev = dev_get_parentdata(dev);
struct usb_dev_platdata *plat = dev_get_parent_platdata(dev);
int ret;
- ret = usb_get_bus(dev, &bus);
- if (ret)
- return ret;
- udev->controller_dev = bus;
- udev->dev = dev;
- udev->devnum = plat->devnum;
- udev->slot_id = plat->slot_id;
- udev->portnr = plat->portnr;
- udev->speed = plat->speed;
- debug("** device '%s': getting slot_id=%d\n", dev->name, plat->slot_id);
-
- ret = usb_select_config(udev);
- if (ret)
- return ret;
+ if (plat->udev) {
+ /*
+ * Copy over all the values set in the on stack struct
+ * usb_device in usb_scan_device() to our final struct
+ * usb_device for this dev.
+ */
+ *udev = *(plat->udev);
+ /* And clear plat->udev as it will not be valid for long */
+ plat->udev = NULL;
+ udev->dev = dev;
+ } else {
+ /*
+ * This happens with devices which are explicitly bound
+ * instead of being discovered through usb_scan_device()
+ * such as sandbox emul devices.
+ */
+ udev->dev = dev;
+ udev->controller_dev = usb_get_bus(dev);
+ udev->devnum = plat->devnum;
+
+ /*
+ * udev did not go through usb_scan_device(), so we need to
+ * select the config and read the config descriptors.
+ */
+ ret = usb_select_config(udev);
+ if (ret)
+ return ret;
+ }
return 0;
}
@@ -627,6 +710,7 @@ UCLASS_DRIVER(usb) = {
.name = "usb",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.post_bind = usb_post_bind,
+ .priv_auto_alloc_size = sizeof(struct usb_uclass_priv),
.per_child_auto_alloc_size = sizeof(struct usb_device),
.per_device_auto_alloc_size = sizeof(struct usb_bus_priv),
.child_post_bind = usb_child_post_bind,