diff options
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/Kconfig | 2 | ||||
-rw-r--r-- | drivers/usb/core/config.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/devices.c | 7 | ||||
-rw-r--r-- | drivers/usb/core/devio.c | 35 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 11 | ||||
-rw-r--r-- | drivers/usb/core/endpoint.c | 10 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 22 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 214 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/quirks.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 12 | ||||
-rw-r--r-- | drivers/usb/core/usb-acpi.c | 205 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 13 |
13 files changed, 403 insertions, 134 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 9981984b365b..f70c1a1694ad 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -56,7 +56,7 @@ config USB_SUSPEND config USB_OTG bool "OTG support" - depends on USB && EXPERIMENTAL + depends on USB depends on USB_SUSPEND default n help diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index f4bdd0ce8d56..7199adccf444 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -702,6 +702,8 @@ int usb_get_configuration(struct usb_device *dev) if (result < 0) { dev_err(ddev, "unable to read config index %d " "descriptor/%s: %d\n", cfgno, "start", result); + if (result != -EPIPE) + goto err; dev_err(ddev, "chopping to %d config(s)\n", cfgno); dev->descriptor.bNumConfigurations = cfgno; break; diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 3440812b4a84..f460de31acee 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -496,6 +496,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, char *pages_start, *data_end, *speed; unsigned int length; ssize_t total_written = 0; + struct usb_device *childdev = NULL; /* don't bother with anything else if we're not writing any data */ if (*nbytes <= 0) @@ -589,14 +590,12 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, free_pages((unsigned long)pages_start, 1); /* Now look at all of this device's children. */ - for (chix = 0; chix < usbdev->maxchild; chix++) { - struct usb_device *childdev = usbdev->children[chix]; - + usb_hub_for_each_child(usbdev, chix, childdev) { if (childdev) { usb_lock_device(childdev); ret = usb_device_dump(buffer, nbytes, skip_bytes, file_offset, childdev, bus, - level + 1, chix, ++cnt); + level + 1, chix - 1, ++cnt); usb_unlock_device(childdev); if (ret == -EFAULT) return total_written; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index ebb8a9de8b5f..e0356cb859b5 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1928,6 +1928,38 @@ static int proc_get_capabilities(struct dev_state *ps, void __user *arg) return 0; } +static int proc_disconnect_claim(struct dev_state *ps, void __user *arg) +{ + struct usbdevfs_disconnect_claim dc; + struct usb_interface *intf; + + if (copy_from_user(&dc, arg, sizeof(dc))) + return -EFAULT; + + intf = usb_ifnum_to_if(ps->dev, dc.interface); + if (!intf) + return -EINVAL; + + if (intf->dev.driver) { + struct usb_driver *driver = to_usb_driver(intf->dev.driver); + + if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER) && + strncmp(dc.driver, intf->dev.driver->name, + sizeof(dc.driver)) != 0) + return -EBUSY; + + if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER) && + strncmp(dc.driver, intf->dev.driver->name, + sizeof(dc.driver)) == 0) + return -EBUSY; + + dev_dbg(&intf->dev, "disconnect by usbfs\n"); + usb_driver_release_interface(driver, intf); + } + + return claimintf(ps, dc.interface); +} + /* * NOTE: All requests here that have interface numbers as parameters * are assuming that somehow the configuration has been prevented from @@ -2101,6 +2133,9 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, case USBDEVFS_GET_CAPABILITIES: ret = proc_get_capabilities(ps, p); break; + case USBDEVFS_DISCONNECT_CLAIM: + ret = proc_disconnect_claim(ps, p); + break; } usb_unlock_device(dev); if (ret >= 0) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 445455a4429b..ddd820d25288 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -125,10 +125,9 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count) { struct usb_dynid *dynid, *n; struct usb_driver *usb_driver = to_usb_driver(driver); - u32 idVendor = 0; - u32 idProduct = 0; - int fields = 0; - int retval = 0; + u32 idVendor; + u32 idProduct; + int fields; fields = sscanf(buf, "%x %x", &idVendor, &idProduct); if (fields < 2) @@ -141,14 +140,10 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count) (id->idProduct == idProduct)) { list_del(&dynid->node); kfree(dynid); - retval = 0; break; } } spin_unlock(&usb_driver->dynids.lock); - - if (retval) - return retval; return count; } static DRIVER_ATTR(remove_id, S_IRUGO | S_IWUSR, show_dynids, store_remove_id); diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index db7fe50c23d4..68cc6532e749 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -24,10 +24,6 @@ struct ep_device { #define to_ep_device(_dev) \ container_of(_dev, struct ep_device, dev) -struct device_type usb_ep_device_type = { - .name = "usb_endpoint", -}; - struct ep_attribute { struct attribute attr; ssize_t (*show)(struct usb_device *, @@ -172,6 +168,11 @@ static void ep_device_release(struct device *dev) kfree(ep_dev); } +struct device_type usb_ep_device_type = { + .name = "usb_endpoint", + .release = ep_device_release, +}; + int usb_create_ep_devs(struct device *parent, struct usb_host_endpoint *endpoint, struct usb_device *udev) @@ -190,7 +191,6 @@ int usb_create_ep_devs(struct device *parent, ep_dev->dev.groups = ep_dev_groups; ep_dev->dev.type = &usb_ep_device_type; ep_dev->dev.parent = parent; - ep_dev->dev.release = ep_device_release; dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress); retval = device_register(&ep_dev->dev); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 75ba2091f9b4..1e741bca0265 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -22,6 +22,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/bcd.h> #include <linux/module.h> #include <linux/version.h> #include <linux/kernel.h> @@ -123,9 +124,8 @@ static inline int is_root_hub(struct usb_device *udev) */ /*-------------------------------------------------------------------------*/ - -#define KERNEL_REL ((LINUX_VERSION_CODE >> 16) & 0x0ff) -#define KERNEL_VER ((LINUX_VERSION_CODE >> 8) & 0x0ff) +#define KERNEL_REL bin2bcd(((LINUX_VERSION_CODE >> 16) & 0x0ff)) +#define KERNEL_VER bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff)) /* usb 3.0 root hub device descriptor */ static const u8 usb3_rh_dev_descriptor[18] = { @@ -2151,15 +2151,8 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum); irqreturn_t usb_hcd_irq (int irq, void *__hcd) { struct usb_hcd *hcd = __hcd; - unsigned long flags; irqreturn_t rc; - /* IRQF_DISABLED doesn't work correctly with shared IRQs - * when the first handler doesn't use it. So let's just - * assume it's never used. - */ - local_irq_save(flags); - if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd))) rc = IRQ_NONE; else if (hcd->driver->irq(hcd) == IRQ_NONE) @@ -2167,7 +2160,6 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd) else rc = IRQ_HANDLED; - local_irq_restore(flags); return rc; } EXPORT_SYMBOL_GPL(usb_hcd_irq); @@ -2355,14 +2347,6 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd, int retval; if (hcd->driver->irq) { - - /* IRQF_DISABLED doesn't work as advertised when used together - * with IRQF_SHARED. As usb_hcd_irq() will always disable - * interrupts we can remove it here. - */ - if (irqflags & IRQF_SHARED) - irqflags &= ~IRQF_DISABLED; - snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d", hcd->driver->description, hcd->self.busnum); retval = request_irq(irqnum, &usb_hcd_irq, irqflags, diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 128a804c42f4..673ee4696262 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -39,6 +39,13 @@ #endif #endif +struct usb_port { + struct usb_device *child; + struct device dev; + struct dev_state *port_owner; + enum usb_port_connect_type connect_type; +}; + struct usb_hub { struct device *intfdev; /* the "interface" device */ struct usb_device *hdev; @@ -83,7 +90,7 @@ struct usb_hub { u8 indicator[USB_MAXCHILDREN]; struct delayed_work leds; struct delayed_work init_work; - struct dev_state **port_owners; + struct usb_port **ports; }; static inline int hub_is_superspeed(struct usb_device *hdev) @@ -156,6 +163,8 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem); #define HUB_DEBOUNCE_STEP 25 #define HUB_DEBOUNCE_STABLE 100 +#define to_usb_port(_dev) \ + container_of(_dev, struct usb_port, dev) static int usb_reset_and_verify_device(struct usb_device *udev); @@ -174,7 +183,7 @@ static inline char *portspeed(struct usb_hub *hub, int portstatus) /* Note that hdev or one of its children must be locked! */ static struct usb_hub *hdev_to_hub(struct usb_device *hdev) { - if (!hdev || !hdev->actconfig) + if (!hdev || !hdev->actconfig || !hdev->maxchild) return NULL; return usb_get_intfdata(hdev->actconfig->interface[0]); } @@ -869,8 +878,8 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) struct usb_device *hdev = hub->hdev; int ret = 0; - if (hdev->children[port1-1] && set_state) - usb_set_device_state(hdev->children[port1-1], + if (hub->ports[port1 - 1]->child && set_state) + usb_set_device_state(hub->ports[port1 - 1]->child, USB_STATE_NOTATTACHED); if (!hub->error && !hub_is_superspeed(hub->hdev)) ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); @@ -1026,7 +1035,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) * which ports need attention. */ for (port1 = 1; port1 <= hdev->maxchild; ++port1) { - struct usb_device *udev = hdev->children[port1-1]; + struct usb_device *udev = hub->ports[port1 - 1]->child; u16 portstatus, portchange; portstatus = portchange = 0; @@ -1191,8 +1200,8 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) if (type != HUB_SUSPEND) { /* Disconnect all the children */ for (i = 0; i < hdev->maxchild; ++i) { - if (hdev->children[i]) - usb_disconnect(&hdev->children[i]); + if (hub->ports[i]->child) + usb_disconnect(&hub->ports[i]->child); } } @@ -1222,6 +1231,52 @@ static int hub_post_reset(struct usb_interface *intf) return 0; } +static void usb_port_device_release(struct device *dev) +{ + struct usb_port *port_dev = to_usb_port(dev); + + kfree(port_dev); +} + +static void usb_hub_remove_port_device(struct usb_hub *hub, + int port1) +{ + device_unregister(&hub->ports[port1 - 1]->dev); +} + +struct device_type usb_port_device_type = { + .name = "usb_port", + .release = usb_port_device_release, +}; + +static int usb_hub_create_port_device(struct usb_hub *hub, + int port1) +{ + struct usb_port *port_dev = NULL; + int retval; + + port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL); + if (!port_dev) { + retval = -ENOMEM; + goto exit; + } + + hub->ports[port1 - 1] = port_dev; + port_dev->dev.parent = hub->intfdev; + port_dev->dev.type = &usb_port_device_type; + dev_set_name(&port_dev->dev, "port%d", port1); + + retval = device_register(&port_dev->dev); + if (retval) + goto error_register; + return 0; + +error_register: + put_device(&port_dev->dev); +exit: + return retval; +} + static int hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint) { @@ -1231,7 +1286,7 @@ static int hub_configure(struct usb_hub *hub, u16 hubstatus, hubchange; u16 wHubCharacteristics; unsigned int pipe; - int maxp, ret; + int maxp, ret, i; char *message = "out of memory"; hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL); @@ -1271,11 +1326,9 @@ static int hub_configure(struct usb_hub *hub, dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild, (hdev->maxchild == 1) ? "" : "s"); - hdev->children = kzalloc(hdev->maxchild * - sizeof(struct usb_device *), GFP_KERNEL); - hub->port_owners = kzalloc(hdev->maxchild * sizeof(struct dev_state *), - GFP_KERNEL); - if (!hdev->children || !hub->port_owners) { + hub->ports = kzalloc(hdev->maxchild * sizeof(struct usb_port *), + GFP_KERNEL); + if (!hub->ports) { ret = -ENOMEM; goto fail; } @@ -1484,6 +1537,11 @@ static int hub_configure(struct usb_hub *hub, if (hub->has_indicators && blinkenlights) hub->indicator [0] = INDICATOR_CYCLE; + for (i = 0; i < hdev->maxchild; i++) + if (usb_hub_create_port_device(hub, i + 1) < 0) + dev_err(hub->intfdev, + "couldn't create port%d device.\n", i + 1); + hub_activate(hub, HUB_INIT); return 0; @@ -1508,6 +1566,7 @@ static void hub_disconnect(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata(intf); struct usb_device *hdev = interface_to_usbdev(intf); + int i; /* Take the hub off the event list and don't let it be added again */ spin_lock_irq(&hub_event_lock); @@ -1523,14 +1582,16 @@ static void hub_disconnect(struct usb_interface *intf) hub_quiesce(hub, HUB_DISCONNECT); usb_set_intfdata (intf, NULL); + + for (i = 0; i < hdev->maxchild; i++) + usb_hub_remove_port_device(hub, i + 1); hub->hdev->maxchild = 0; if (hub->hdev->speed == USB_SPEED_HIGH) highspeed_hubs--; usb_free_urb(hub->urb); - kfree(hdev->children); - kfree(hub->port_owners); + kfree(hub->ports); kfree(hub->descriptor); kfree(hub->status); kfree(hub->buffer); @@ -1617,6 +1678,7 @@ static int hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) { struct usb_device *hdev = interface_to_usbdev (intf); + struct usb_hub *hub = hdev_to_hub(hdev); /* assert ifno == 0 (part of hub spec) */ switch (code) { @@ -1630,11 +1692,11 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) else { info->nports = hdev->maxchild; for (i = 0; i < info->nports; i++) { - if (hdev->children[i] == NULL) + if (hub->ports[i]->child == NULL) info->port[i] = 0; else info->port[i] = - hdev->children[i]->devnum; + hub->ports[i]->child->devnum; } } spin_unlock_irq(&device_state_lock); @@ -1662,7 +1724,7 @@ static int find_port_owner(struct usb_device *hdev, unsigned port1, /* This assumes that devices not managed by the hub driver * will always have maxchild equal to 0. */ - *ppowner = &(hdev_to_hub(hdev)->port_owners[port1 - 1]); + *ppowner = &(hdev_to_hub(hdev)->ports[port1 - 1]->port_owner); return 0; } @@ -1699,16 +1761,14 @@ int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void usb_hub_release_all_ports(struct usb_device *hdev, struct dev_state *owner) { + struct usb_hub *hub = hdev_to_hub(hdev); int n; - struct dev_state **powner; - n = find_port_owner(hdev, 1, &powner); - if (n == 0) { - for (; n < hdev->maxchild; (++n, ++powner)) { - if (*powner == owner) - *powner = NULL; - } + for (n = 0; n < hdev->maxchild; n++) { + if (hub->ports[n]->port_owner == owner) + hub->ports[n]->port_owner = NULL; } + } /* The caller must hold udev's lock */ @@ -1719,17 +1779,17 @@ bool usb_device_is_owned(struct usb_device *udev) if (udev->state == USB_STATE_NOTATTACHED || !udev->parent) return false; hub = hdev_to_hub(udev->parent); - return !!hub->port_owners[udev->portnum - 1]; + return !!hub->ports[udev->portnum - 1]->port_owner; } - static void recursively_mark_NOTATTACHED(struct usb_device *udev) { + struct usb_hub *hub = hdev_to_hub(udev); int i; for (i = 0; i < udev->maxchild; ++i) { - if (udev->children[i]) - recursively_mark_NOTATTACHED(udev->children[i]); + if (hub->ports[i]->child) + recursively_mark_NOTATTACHED(hub->ports[i]->child); } if (udev->state == USB_STATE_SUSPENDED) udev->active_duration -= jiffies; @@ -1893,6 +1953,7 @@ static void hub_free_dev(struct usb_device *udev) void usb_disconnect(struct usb_device **pdev) { struct usb_device *udev = *pdev; + struct usb_hub *hub = hdev_to_hub(udev); int i; /* mark the device as inactive, so any further urb submissions for @@ -1907,8 +1968,8 @@ void usb_disconnect(struct usb_device **pdev) /* Free up all the children before we remove this device */ for (i = 0; i < udev->maxchild; i++) { - if (udev->children[i]) - usb_disconnect(&udev->children[i]); + if (hub->ports[i]->child) + usb_disconnect(&hub->ports[i]->child); } /* deallocate hcd/hardware state ... nuking all pending urbs and @@ -2113,7 +2174,8 @@ static void set_usb_port_removable(struct usb_device *udev) return; if (hub_is_superspeed(hdev)) { - if (hub->descriptor->u.ss.DeviceRemovable & (1 << port)) + if (le16_to_cpu(hub->descriptor->u.ss.DeviceRemovable) + & (1 << port)) removable = false; } else { if (hub->descriptor->u.hs.DeviceRemovable[port / 8] & (1 << (port % 8))) @@ -3072,7 +3134,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) for (port1 = 1; port1 <= hdev->maxchild; port1++) { struct usb_device *udev; - udev = hdev->children [port1-1]; + udev = hub->ports[port1 - 1]->child; if (udev && udev->can_submit) { dev_warn(&intf->dev, "port %d nyet suspended\n", port1); if (PMSG_IS_AUTO(msg)) @@ -3999,7 +4061,7 @@ hub_power_remaining (struct usb_hub *hub) remaining = hdev->bus_mA - hub->descriptor->bHubContrCurrent; for (port1 = 1; port1 <= hdev->maxchild; ++port1) { - struct usb_device *udev = hdev->children[port1 - 1]; + struct usb_device *udev = hub->ports[port1 - 1]->child; int delta; if (!udev) @@ -4063,7 +4125,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, #endif /* Try to resuscitate an existing device */ - udev = hdev->children[port1-1]; + udev = hub->ports[port1 - 1]->child; if ((portstatus & USB_PORT_STAT_CONNECTION) && udev && udev->state != USB_STATE_NOTATTACHED) { usb_lock_device(udev); @@ -4092,7 +4154,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, /* Disconnect any existing devices under this port */ if (udev) - usb_disconnect(&hdev->children[port1-1]); + usb_disconnect(&hub->ports[port1 - 1]->child); clear_bit(port1, hub->change_bits); /* We can forget about a "removed" device when there's a physical @@ -4228,7 +4290,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, if (hdev->state == USB_STATE_NOTATTACHED) status = -ENOTCONN; else - hdev->children[port1-1] = udev; + hub->ports[port1 - 1]->child = udev; spin_unlock_irq(&device_state_lock); /* Run it through the hoops (find a driver, etc) */ @@ -4236,7 +4298,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, status = usb_new_device(udev); if (status) { spin_lock_irq(&device_state_lock); - hdev->children[port1-1] = NULL; + hub->ports[port1 - 1]->child = NULL; spin_unlock_irq(&device_state_lock); } } @@ -4282,7 +4344,7 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, int ret; hdev = hub->hdev; - udev = hdev->children[port-1]; + udev = hub->ports[port - 1]->child; if (!hub_is_superspeed(hdev)) { if (!(portchange & USB_PORT_STAT_C_SUSPEND)) return 0; @@ -4436,7 +4498,7 @@ static void hub_events(void) */ if (!(portstatus & USB_PORT_STAT_ENABLE) && !connect_change - && hdev->children[i-1]) { + && hub->ports[i - 1]->child) { dev_err (hub_dev, "port %i " "disabled by hub (EMI?), " @@ -4993,3 +5055,75 @@ void usb_queue_reset_device(struct usb_interface *iface) schedule_work(&iface->reset_ws); } EXPORT_SYMBOL_GPL(usb_queue_reset_device); + +/** + * usb_hub_find_child - Get the pointer of child device + * attached to the port which is specified by @port1. + * @hdev: USB device belonging to the usb hub + * @port1: port num to indicate which port the child device + * is attached to. + * + * USB drivers call this function to get hub's child device + * pointer. + * + * Return NULL if input param is invalid and + * child's usb_device pointer if non-NULL. + */ +struct usb_device *usb_hub_find_child(struct usb_device *hdev, + int port1) +{ + struct usb_hub *hub = hdev_to_hub(hdev); + + if (port1 < 1 || port1 > hdev->maxchild) + return NULL; + return hub->ports[port1 - 1]->child; +} +EXPORT_SYMBOL_GPL(usb_hub_find_child); + +/** + * usb_set_hub_port_connect_type - set hub port connect type. + * @hdev: USB device belonging to the usb hub + * @port1: port num of the port + * @type: connect type of the port + */ +void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1, + enum usb_port_connect_type type) +{ + struct usb_hub *hub = hdev_to_hub(hdev); + + hub->ports[port1 - 1]->connect_type = type; +} + +/** + * usb_get_hub_port_connect_type - Get the port's connect type + * @hdev: USB device belonging to the usb hub + * @port1: port num of the port + * + * Return connect type of the port and if input params are + * invalid, return USB_PORT_CONNECT_TYPE_UNKNOWN. + */ +enum usb_port_connect_type +usb_get_hub_port_connect_type(struct usb_device *hdev, int port1) +{ + struct usb_hub *hub = hdev_to_hub(hdev); + + return hub->ports[port1 - 1]->connect_type; +} + +#ifdef CONFIG_ACPI +/** + * usb_get_hub_port_acpi_handle - Get the usb port's acpi handle + * @hdev: USB device belonging to the usb hub + * @port1: port num of the port + * + * Return port's acpi handle if successful, NULL if params are + * invaild. + */ +acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev, + int port1) +{ + struct usb_hub *hub = hdev_to_hub(hdev); + + return DEVICE_ACPI_HANDLE(&hub->ports[port1 - 1]->dev); +} +#endif diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 0ab7da2283e3..1ed5afd91e6d 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -146,8 +146,6 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, dr->wIndex = cpu_to_le16(index); dr->wLength = cpu_to_le16(size); - /* dbg("usb_control_msg"); */ - ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout); kfree(dr); diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index e77a8e8eaa23..fdefd9c7f7af 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -209,7 +209,7 @@ void usb_detect_quirks(struct usb_device *udev) * for all devices. It will affect things like hub resets * and EMF-related port disables. */ - if (!(udev->quirks & USB_QUIRK_RESET_MORPHS)) + if (!(udev->quirks & USB_QUIRK_RESET)) udev->persist_enabled = 1; #endif /* CONFIG_PM */ } diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 682e8256b95d..818e4a024d0d 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -196,7 +196,7 @@ show_avoid_reset_quirk(struct device *dev, struct device_attribute *attr, char * struct usb_device *udev; udev = to_usb_device(dev); - return sprintf(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET_MORPHS)); + return sprintf(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET)); } static ssize_t @@ -204,15 +204,15 @@ set_avoid_reset_quirk(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct usb_device *udev = to_usb_device(dev); - int config; + int val; - if (sscanf(buf, "%d", &config) != 1 || config < 0 || config > 1) + if (sscanf(buf, "%d", &val) != 1 || val < 0 || val > 1) return -EINVAL; usb_lock_device(udev); - if (config) - udev->quirks |= USB_QUIRK_RESET_MORPHS; + if (val) + udev->quirks |= USB_QUIRK_RESET; else - udev->quirks &= ~USB_QUIRK_RESET_MORPHS; + udev->quirks &= ~USB_QUIRK_RESET; usb_unlock_device(udev); return count; } diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index 8947b203d5a4..0ef7d42d8abe 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -19,20 +19,91 @@ #include "usb.h" -static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle) +/** + * usb_acpi_power_manageable - check whether usb port has + * acpi power resource. + * @hdev: USB device belonging to the usb hub + * @index: port index based zero + * + * Return true if the port has acpi power resource and false if no. + */ +bool usb_acpi_power_manageable(struct usb_device *hdev, int index) +{ + acpi_handle port_handle; + int port1 = index + 1; + + port_handle = usb_get_hub_port_acpi_handle(hdev, + port1); + if (port_handle) + return acpi_bus_power_manageable(port_handle); + else + return false; +} +EXPORT_SYMBOL_GPL(usb_acpi_power_manageable); + +/** + * usb_acpi_set_power_state - control usb port's power via acpi power + * resource + * @hdev: USB device belonging to the usb hub + * @index: port index based zero + * @enable: power state expected to be set + * + * Notice to use usb_acpi_power_manageable() to check whether the usb port + * has acpi power resource before invoking this function. + * + * Returns 0 on success, else negative errno. + */ +int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable) +{ + acpi_handle port_handle; + unsigned char state; + int port1 = index + 1; + int error = -EINVAL; + + port_handle = (acpi_handle)usb_get_hub_port_acpi_handle(hdev, + port1); + if (!port_handle) + return error; + + if (enable) + state = ACPI_STATE_D0; + else + state = ACPI_STATE_D3_COLD; + + error = acpi_bus_set_power(port_handle, state); + if (!error) + dev_dbg(&hdev->dev, "The power of hub port %d was set to %d\n", + port1, enable); + else + dev_dbg(&hdev->dev, "The power of hub port failed to be set\n"); + + return error; +} +EXPORT_SYMBOL_GPL(usb_acpi_set_power_state); + +static int usb_acpi_check_port_connect_type(struct usb_device *hdev, + acpi_handle handle, int port1) { acpi_status status; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *upc; + struct acpi_pld pld; int ret = 0; - status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer); - + /* + * Accoding to ACPI Spec 9.13. PLD indicates whether usb port is + * user visible and _UPC indicates whether it is connectable. If + * the port was visible and connectable, it could be freely connected + * and disconnected with USB devices. If no visible and connectable, + * a usb device is directly hard-wired to the port. If no visible and + * no connectable, the port would be not used. + */ + status = acpi_get_physical_device_location(handle, &pld); if (ACPI_FAILURE(status)) return -ENODEV; + status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer); upc = buffer.pointer; - if (!upc || (upc->type != ACPI_TYPE_PACKAGE) || upc->package.count != 4) { ret = -EINVAL; @@ -40,69 +111,107 @@ static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle) } if (upc->package.elements[0].integer.value) - udev->removable = USB_DEVICE_REMOVABLE; - else - udev->removable = USB_DEVICE_FIXED; + if (pld.user_visible) + usb_set_hub_port_connect_type(hdev, port1, + USB_PORT_CONNECT_TYPE_HOT_PLUG); + else + usb_set_hub_port_connect_type(hdev, port1, + USB_PORT_CONNECT_TYPE_HARD_WIRED); + else if (!pld.user_visible) + usb_set_hub_port_connect_type(hdev, port1, USB_PORT_NOT_USED); out: kfree(upc); return ret; } -static int usb_acpi_check_pld(struct usb_device *udev, acpi_handle handle) -{ - acpi_status status; - struct acpi_pld pld; - - status = acpi_get_physical_device_location(handle, &pld); - - if (ACPI_FAILURE(status)) - return -ENODEV; - - if (pld.user_visible) - udev->removable = USB_DEVICE_REMOVABLE; - else - udev->removable = USB_DEVICE_FIXED; - - return 0; -} - static int usb_acpi_find_device(struct device *dev, acpi_handle *handle) { struct usb_device *udev; - struct device *parent; acpi_handle *parent_handle; - - if (!is_usb_device(dev)) - return -ENODEV; - - udev = to_usb_device(dev); - parent = dev->parent; - parent_handle = DEVICE_ACPI_HANDLE(parent); - - if (!parent_handle) - return -ENODEV; - - *handle = acpi_get_child(parent_handle, udev->portnum); - - if (!*handle) - return -ENODEV; + int port_num; /* - * PLD will tell us whether a port is removable to the user or - * not. If we don't get an answer from PLD (it's not present - * or it's malformed) then try to infer it from UPC. If a - * device isn't connectable then it's probably not removable. + * In the ACPI DSDT table, only usb root hub and usb ports are + * acpi device nodes. The hierarchy like following. + * Device (EHC1) + * Device (HUBN) + * Device (PR01) + * Device (PR11) + * Device (PR12) + * Device (PR13) + * ... + * So all binding process is divided into two parts. binding + * root hub and usb ports. */ - if (usb_acpi_check_pld(udev, *handle) != 0) - usb_acpi_check_upc(udev, *handle); + if (is_usb_device(dev)) { + udev = to_usb_device(dev); + if (udev->parent) { + enum usb_port_connect_type type; + + /* + * According usb port's connect type to set usb device's + * removability. + */ + type = usb_get_hub_port_connect_type(udev->parent, + udev->portnum); + switch (type) { + case USB_PORT_CONNECT_TYPE_HOT_PLUG: + udev->removable = USB_DEVICE_REMOVABLE; + break; + case USB_PORT_CONNECT_TYPE_HARD_WIRED: + udev->removable = USB_DEVICE_FIXED; + break; + default: + udev->removable = USB_DEVICE_REMOVABLE_UNKNOWN; + break; + } + + return -ENODEV; + } + + /* root hub's parent is the usb hcd. */ + parent_handle = DEVICE_ACPI_HANDLE(dev->parent); + *handle = acpi_get_child(parent_handle, udev->portnum); + if (!*handle) + return -ENODEV; + return 0; + } else if (is_usb_port(dev)) { + sscanf(dev_name(dev), "port%d", &port_num); + /* Get the struct usb_device point of port's hub */ + udev = to_usb_device(dev->parent->parent); + + /* + * The root hub ports' parent is the root hub. The non-root-hub + * ports' parent is the parent hub port which the hub is + * connected to. + */ + if (!udev->parent) { + *handle = acpi_get_child(DEVICE_ACPI_HANDLE(&udev->dev), + port_num); + if (!*handle) + return -ENODEV; + } else { + parent_handle = + usb_get_hub_port_acpi_handle(udev->parent, + udev->portnum); + if (!parent_handle) + return -ENODEV; + + *handle = acpi_get_child(parent_handle, port_num); + if (!*handle) + return -ENODEV; + } + usb_acpi_check_port_connect_type(udev, *handle, port_num); + } else + return -ENODEV; return 0; } static struct acpi_bus_type usb_acpi_bus = { .bus = &usb_bus_type, - .find_bridge = NULL, + .find_bridge = usb_acpi_find_device, .find_device = usb_acpi_find_device, }; diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index acb103c5c391..1c528c1bf0be 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -1,4 +1,5 @@ #include <linux/pm.h> +#include <linux/acpi.h> struct dev_state; @@ -115,6 +116,7 @@ extern struct bus_type usb_bus_type; extern struct device_type usb_device_type; extern struct device_type usb_if_device_type; extern struct device_type usb_ep_device_type; +extern struct device_type usb_port_device_type; extern struct usb_device_driver usb_generic_driver; static inline int is_usb_device(const struct device *dev) @@ -132,6 +134,11 @@ static inline int is_usb_endpoint(const struct device *dev) return dev->type == &usb_ep_device_type; } +static inline int is_usb_port(const struct device *dev) +{ + return dev->type == &usb_port_device_type; +} + /* Do the same for device drivers and interface drivers. */ static inline int is_usb_device_driver(struct device_driver *drv) @@ -162,10 +169,16 @@ extern void usb_notify_add_device(struct usb_device *udev); extern void usb_notify_remove_device(struct usb_device *udev); extern void usb_notify_add_bus(struct usb_bus *ubus); extern void usb_notify_remove_bus(struct usb_bus *ubus); +extern enum usb_port_connect_type + usb_get_hub_port_connect_type(struct usb_device *hdev, int port1); +extern void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1, + enum usb_port_connect_type type); #ifdef CONFIG_ACPI extern int usb_acpi_register(void); extern void usb_acpi_unregister(void); +extern acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev, + int port1); #else static inline int usb_acpi_register(void) { return 0; }; static inline void usb_acpi_unregister(void) { }; |