X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fusb-uclass.c;h=7f6a9a6d0548a44bf49efb2bbd377d3a5282b8fd;hb=60b25259a5540686add02cf6c94cd7494a3e2d23;hp=714bc0e9588fb99899d0c42fe8d5dc9c203baa7e;hpb=b939689c7b87773c44275a578ffc8674a867e39d;p=u-boot diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c index 714bc0e958..7f6a9a6d05 100644 --- a/drivers/usb/host/usb-uclass.c +++ b/drivers/usb/host/usb-uclass.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,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 +51,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 +81,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; @@ -77,20 +129,38 @@ int usb_alloc_device(struct usb_device *udev) return ops->alloc_device(bus, udev); } +int usb_reset_root_port(struct usb_device *udev) +{ + struct udevice *bus = udev->controller_dev; + struct dm_usb_ops *ops = usb_get_ops(bus); + + if (!ops->reset_root_port) + return -ENOSYS; + + return ops->reset_root_port(bus, udev); +} + 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) err = ret; + ret = device_unbind_children(bus); + if (ret && !err) + err = ret; } #ifdef CONFIG_SANDBOX @@ -104,14 +174,17 @@ int usb_stop(void) uclass_foreach_dev(dev, uc) usb_emul_reset(dev); #endif +#ifdef CONFIG_USB_STORAGE usb_stor_reset(); +#endif 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 +194,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 +222,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 +239,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) @@ -186,11 +282,6 @@ int usb_init(void) return usb_started ? 0 : -1; } -int usb_reset_root_port(void) -{ - return -ENOSYS; -} - static struct usb_device *find_child_devnum(struct udevice *parent, int devnum) { struct usb_device *udev; @@ -198,7 +289,7 @@ static struct usb_device *find_child_devnum(struct udevice *parent, int devnum) if (!device_active(parent)) return NULL; - udev = dev_get_parentdata(parent); + udev = dev_get_parent_priv(parent); if (udev->devnum == devnum) return udev; @@ -215,14 +306,14 @@ static struct usb_device *find_child_devnum(struct udevice *parent, int devnum) struct usb_device *usb_get_dev_index(struct udevice *bus, int index) { - struct udevice *hub; + struct udevice *dev; int devnum = index + 1; /* Addresses are allocated from 1 on USB */ - device_find_first_child(bus, &hub); - if (device_get_uclass_id(hub) == UCLASS_USB_HUB) - return find_child_devnum(hub, devnum); + device_find_first_child(bus, &dev); + if (!dev) + return NULL; - return NULL; + return find_child_devnum(dev, devnum); } int usb_post_bind(struct udevice *dev) @@ -231,35 +322,6 @@ int usb_post_bind(struct udevice *dev) return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); } -int usb_port_reset(struct usb_device *parent, int portnr) -{ - unsigned short portstatus; - int ret; - - debug("%s: start\n", __func__); - - if (parent) { - /* reset the port for the second time */ - assert(portnr > 0); - debug("%s: reset %d\n", __func__, portnr - 1); - ret = legacy_hub_port_reset(parent, portnr - 1, &portstatus); - if (ret < 0) { - printf("\n Couldn't reset port %i\n", portnr); - return ret; - } - } else { - debug("%s: reset root\n", __func__); - usb_reset_root_port(); - } - - return 0; -} - -int usb_legacy_port_reset(struct usb_device *parent, int portnr) -{ - return usb_port_reset(parent, portnr); -} - int usb_setup_ehci_gadget(struct ehci_ctrl **ctlrp) { struct usb_platdata *plat; @@ -432,15 +494,14 @@ error: } /** - * usb_find_child() - Find an existing device which matches our needs - * - * + * usb_find_emul_child() - Find an existing device for emulated devices */ -static int usb_find_child(struct udevice *parent, - struct usb_device_descriptor *desc, - struct usb_interface_descriptor *iface, - struct udevice **devp) +static int usb_find_emul_child(struct udevice *parent, + struct usb_device_descriptor *desc, + struct usb_interface_descriptor *iface, + struct udevice **devp) { +#ifdef CONFIG_SANDBOX struct udevice *dev; *devp = NULL; @@ -459,7 +520,7 @@ static int usb_find_child(struct udevice *parent, return 0; } } - +#endif return -ENOENT; } @@ -477,9 +538,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); /* @@ -516,13 +575,13 @@ int usb_scan_device(struct udevice *parent, int port, udev->portnr = port; debug("Calling usb_setup_device(), portnr=%d\n", udev->portnr); parent_udev = device_get_uclass_id(parent) == UCLASS_USB_HUB ? - dev_get_parentdata(parent) : NULL; - ret = usb_setup_device(udev, priv->desc_before_addr, parent_udev, port); + dev_get_parent_priv(parent) : NULL; + ret = usb_setup_device(udev, priv->desc_before_addr, parent_udev); debug("read_descriptor for '%s': ret=%d\n", parent->name, ret); if (ret) return ret; - ret = usb_find_child(parent, &udev->descriptor, iface, &dev); - debug("** usb_find_child returns %d\n", ret); + ret = usb_find_emul_child(parent, &udev->descriptor, iface, &dev); + debug("** usb_find_emul_child returns %d\n", ret); if (ret) { if (ret != -ENOENT) return ret; @@ -536,11 +595,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) { @@ -555,6 +610,49 @@ int usb_scan_device(struct udevice *parent, int port, return 0; } +/* + * Detect if a USB device has been plugged or unplugged. + */ +int usb_detect_change(void) +{ + struct udevice *hub; + struct uclass *uc; + int change = 0; + int ret; + + ret = uclass_get(UCLASS_USB_HUB, &uc); + if (ret) + return ret; + + uclass_foreach_dev(hub, uc) { + struct usb_device *udev; + struct udevice *dev; + + if (!device_active(hub)) + continue; + for (device_find_first_child(hub, &dev); + dev; + device_find_next_child(&dev)) { + struct usb_port_status status; + + if (!device_active(dev)) + continue; + + udev = dev_get_parent_priv(dev); + if (usb_get_port_status(udev, udev->portnr, &status) + < 0) + /* USB request failed */ + continue; + + if (le16_to_cpu(status.wPortChange) & + USB_PORT_STAT_C_CONNECTION) + change++; + } + } + + return change; +} + int usb_child_post_bind(struct udevice *dev) { struct usb_dev_platdata *plat = dev_get_parent_platdata(dev); @@ -579,45 +677,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_device *udev = dev_get_parent_priv(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 +735,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,