diff options
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/devio.c | 7 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 3 | ||||
-rw-r--r-- | drivers/usb/core/file.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 28 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 76 | ||||
-rw-r--r-- | drivers/usb/core/hub.h | 5 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 16 | ||||
-rw-r--r-- | drivers/usb/core/port.c | 5 | ||||
-rw-r--r-- | drivers/usb/core/quirks.c | 6 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 54 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 33 |
11 files changed, 201 insertions, 34 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index c88c4fb9459..05986507b58 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -49,14 +49,14 @@ #include <linux/security.h> #include <linux/user_namespace.h> #include <linux/scatterlist.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <asm/byteorder.h> #include <linux/moduleparam.h> #include "usb.h" #define USB_MAXBUS 64 -#define USB_DEVICE_MAX USB_MAXBUS * 128 +#define USB_DEVICE_MAX (USB_MAXBUS * 128) #define USB_SG_SIZE 16384 /* split-size for large txs */ /* Mutual exclusion for removal, open, and release */ @@ -1804,7 +1804,8 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl) /* alloc buffer */ if ((size = _IOC_SIZE(ctl->ioctl_code)) > 0) { - if ((buf = kmalloc(size, GFP_KERNEL)) == NULL) + buf = kmalloc(size, GFP_KERNEL); + if (buf == NULL) return -ENOMEM; if ((_IOC_DIR(ctl->ioctl_code) & _IOC_WRITE)) { if (copy_from_user(buf, ctl->data, size)) { diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 6eab440e154..7609ac4aed1 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1765,7 +1765,8 @@ int usb_runtime_idle(struct device *dev) */ if (autosuspend_check(udev) == 0) pm_runtime_autosuspend(dev); - return 0; + /* Tell the core not to suspend it, though. */ + return -EBUSY; } int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index e5387a47ef6..6a4c40766f0 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -94,7 +94,7 @@ static int init_usb_class(void) kref_init(&usb_class->kref); usb_class->class = class_create(THIS_MODULE, "usbmisc"); if (IS_ERR(usb_class->class)) { - result = IS_ERR(usb_class->class); + result = PTR_ERR(usb_class->class); printk(KERN_ERR "class_create failed for usb devices\n"); kfree(usb_class); usb_class = NULL; diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index d53547d2e4c..014dc996b4f 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -149,6 +149,27 @@ static const u8 usb3_rh_dev_descriptor[18] = { 0x01 /* __u8 bNumConfigurations; */ }; +/* usb 2.5 (wireless USB 1.0) root hub device descriptor */ +static const u8 usb25_rh_dev_descriptor[18] = { + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x50, 0x02, /* __le16 bcdUSB; v2.5 */ + + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; [ usb 2.0 no TT ] */ + 0xFF, /* __u8 bMaxPacketSize0; always 0xFF (WUSB Spec 7.4.1). */ + + 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */ + 0x02, 0x00, /* __le16 idProduct; device 0x0002 */ + KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */ + + 0x03, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + /* usb 2.0 root hub device descriptor */ static const u8 usb2_rh_dev_descriptor [18] = { 0x12, /* __u8 bLength; */ @@ -527,6 +548,9 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) case HCD_USB3: bufp = usb3_rh_dev_descriptor; break; + case HCD_USB25: + bufp = usb25_rh_dev_descriptor; + break; case HCD_USB2: bufp = usb2_rh_dev_descriptor; break; @@ -546,6 +570,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) bufp = ss_rh_config_descriptor; len = sizeof ss_rh_config_descriptor; break; + case HCD_USB25: case HCD_USB2: bufp = hs_rh_config_descriptor; len = sizeof hs_rh_config_descriptor; @@ -2511,6 +2536,9 @@ int usb_add_hcd(struct usb_hcd *hcd, case HCD_USB2: rhdev->speed = USB_SPEED_HIGH; break; + case HCD_USB25: + rhdev->speed = USB_SPEED_WIRELESS; + break; case HCD_USB3: rhdev->speed = USB_SPEED_SUPER; break; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index feef9351463..558313de491 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -668,6 +668,15 @@ resubmit: static inline int hub_clear_tt_buffer (struct usb_device *hdev, u16 devinfo, u16 tt) { + /* Need to clear both directions for control ep */ + if (((devinfo >> 11) & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_CONTROL) { + int status = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), + HUB_CLEAR_TT_BUFFER, USB_RT_PORT, + devinfo ^ 0x8000, tt, NULL, 0, 1000); + if (status) + return status; + } return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), HUB_CLEAR_TT_BUFFER, USB_RT_PORT, devinfo, tt, NULL, 0, 1000); @@ -718,18 +727,18 @@ static void hub_tt_work(struct work_struct *work) /** * usb_hub_set_port_power - control hub port's power state - * @hdev: target hub + * @hdev: USB device belonging to the usb hub + * @hub: target hub * @port1: port index * @set: expected status * * call this function to control port's power via setting or * clearing the port's PORT_POWER feature. */ -int usb_hub_set_port_power(struct usb_device *hdev, int port1, - bool set) +int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub, + int port1, bool set) { int ret; - struct usb_hub *hub = usb_hub_to_struct_hub(hdev); struct usb_port *port_dev = hub->ports[port1 - 1]; if (set) @@ -1769,15 +1778,17 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) static int find_port_owner(struct usb_device *hdev, unsigned port1, struct dev_state ***ppowner) { + struct usb_hub *hub = usb_hub_to_struct_hub(hdev); + if (hdev->state == USB_STATE_NOTATTACHED) return -ENODEV; if (port1 == 0 || port1 > hdev->maxchild) return -EINVAL; - /* This assumes that devices not managed by the hub driver + /* Devices not managed by the hub driver * will always have maxchild equal to 0. */ - *ppowner = &(usb_hub_to_struct_hub(hdev)->ports[port1 - 1]->port_owner); + *ppowner = &(hub->ports[port1 - 1]->port_owner); return 0; } @@ -2846,6 +2857,15 @@ static int usb_disable_function_remotewakeup(struct usb_device *udev) USB_CTRL_SET_TIMEOUT); } +/* Count of wakeup-enabled devices at or below udev */ +static unsigned wakeup_enabled_descendants(struct usb_device *udev) +{ + struct usb_hub *hub = usb_hub_to_struct_hub(udev); + + return udev->do_remote_wakeup + + (hub ? hub->wakeup_enabled_descendants : 0); +} + /* * usb_port_suspend - suspend a usb device's upstream port * @udev: device that's no longer in active use, not a root hub @@ -2886,8 +2906,8 @@ static int usb_disable_function_remotewakeup(struct usb_device *udev) * Linux (2.6) currently has NO mechanisms to initiate that: no khubd * timer, no SRP, no requests through sysfs. * - * If Runtime PM isn't enabled or used, non-SuperSpeed devices really get - * suspended only when their bus goes into global suspend (i.e., the root + * If Runtime PM isn't enabled or used, non-SuperSpeed devices may not get + * suspended until their bus goes into global suspend (i.e., the root * hub is suspended). Nevertheless, we change @udev->state to * USB_STATE_SUSPENDED as this is the device's "logical" state. The actual * upstream port setting is stored in @udev->port_is_suspended. @@ -2958,15 +2978,21 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) /* see 7.1.7.6 */ if (hub_is_superspeed(hub->hdev)) status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3); - else if (PMSG_IS_AUTO(msg)) - status = set_port_feature(hub->hdev, port1, - USB_PORT_FEAT_SUSPEND); + /* * For system suspend, we do not need to enable the suspend feature * on individual USB-2 ports. The devices will automatically go * into suspend a few ms after the root hub stops sending packets. * The USB 2.0 spec calls this "global suspend". + * + * However, many USB hubs have a bug: They don't relay wakeup requests + * from a downstream port if the port's suspend feature isn't on. + * Therefore we will turn on the suspend feature if udev or any of its + * descendants is enabled for remote wakeup. */ + else if (PMSG_IS_AUTO(msg) || wakeup_enabled_descendants(udev) > 0) + status = set_port_feature(hub->hdev, port1, + USB_PORT_FEAT_SUSPEND); else { really_suspend = false; status = 0; @@ -3001,15 +3027,16 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (!PMSG_IS_AUTO(msg)) status = 0; } else { - /* device has up to 10 msec to fully suspend */ dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n", (PMSG_IS_AUTO(msg) ? "auto-" : ""), udev->do_remote_wakeup); - usb_set_device_state(udev, USB_STATE_SUSPENDED); if (really_suspend) { udev->port_is_suspended = 1; + + /* device has up to 10 msec to fully suspend */ msleep(10); } + usb_set_device_state(udev, USB_STATE_SUSPENDED); } /* @@ -3291,7 +3318,11 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) unsigned port1; int status; - /* Warn if children aren't already suspended */ + /* + * Warn if children aren't already suspended. + * Also, add up the number of wakeup-enabled descendants. + */ + hub->wakeup_enabled_descendants = 0; for (port1 = 1; port1 <= hdev->maxchild; port1++) { struct usb_device *udev; @@ -3301,6 +3332,9 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) if (PMSG_IS_AUTO(msg)) return -EBUSY; } + if (udev) + hub->wakeup_enabled_descendants += + wakeup_enabled_descendants(udev); } if (hdev->do_remote_wakeup && hub->quirk_check_port_auto_suspend) { @@ -4764,7 +4798,8 @@ static void hub_events(void) hub->ports[i - 1]->child; dev_dbg(hub_dev, "warm reset port %d\n", i); - if (!udev) { + if (!udev || !(portstatus & + USB_PORT_STAT_CONNECTION)) { status = hub_port_reset(hub, i, NULL, HUB_BH_RESET_TIME, true); @@ -4774,8 +4809,8 @@ static void hub_events(void) usb_lock_device(udev); status = usb_reset_device(udev); usb_unlock_device(udev); + connect_change = 0; } - connect_change = 0; } if (connect_change) @@ -5323,7 +5358,8 @@ void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1, { struct usb_hub *hub = usb_hub_to_struct_hub(hdev); - hub->ports[port1 - 1]->connect_type = type; + if (hub) + hub->ports[port1 - 1]->connect_type = type; } /** @@ -5339,6 +5375,9 @@ usb_get_hub_port_connect_type(struct usb_device *hdev, int port1) { struct usb_hub *hub = usb_hub_to_struct_hub(hdev); + if (!hub) + return USB_PORT_CONNECT_TYPE_UNKNOWN; + return hub->ports[port1 - 1]->connect_type; } @@ -5397,6 +5436,9 @@ acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev, { struct usb_hub *hub = usb_hub_to_struct_hub(hdev); + if (!hub) + return NULL; + return DEVICE_ACPI_HANDLE(&hub->ports[port1 - 1]->dev); } #endif diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 80ab9ee0701..4e4790dea34 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -59,6 +59,9 @@ struct usb_hub { struct usb_tt tt; /* Transaction Translator */ unsigned mA_per_port; /* current for each child */ +#ifdef CONFIG_PM + unsigned wakeup_enabled_descendants; +#endif unsigned limited_power:1; unsigned quiescing:1; @@ -100,7 +103,7 @@ extern int usb_hub_create_port_device(struct usb_hub *hub, int port1); extern void usb_hub_remove_port_device(struct usb_hub *hub, int port1); -extern int usb_hub_set_port_power(struct usb_device *hdev, +extern int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub, int port1, bool set); extern struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev); extern int hub_port_debounce(struct usb_hub *hub, int port1, diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 444d30e3a78..e7ee1e45166 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -252,7 +252,7 @@ static void sg_clean(struct usb_sg_request *io) { if (io->urbs) { while (io->entries--) - usb_free_urb(io->urbs [io->entries]); + usb_free_urb(io->urbs[io->entries]); kfree(io->urbs); io->urbs = NULL; } @@ -300,10 +300,10 @@ static void sg_complete(struct urb *urb) */ spin_unlock(&io->lock); for (i = 0, found = 0; i < io->entries; i++) { - if (!io->urbs [i] || !io->urbs [i]->dev) + if (!io->urbs[i] || !io->urbs[i]->dev) continue; if (found) { - retval = usb_unlink_urb(io->urbs [i]); + retval = usb_unlink_urb(io->urbs[i]); if (retval != -EINPROGRESS && retval != -ENODEV && retval != -EBUSY && @@ -311,7 +311,7 @@ static void sg_complete(struct urb *urb) dev_err(&io->dev->dev, "%s, unlink --> %d\n", __func__, retval); - } else if (urb == io->urbs [i]) + } else if (urb == io->urbs[i]) found = 1; } spin_lock(&io->lock); @@ -379,7 +379,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, } /* initialize all the urbs we'll use */ - io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags); + io->urbs = kmalloc(io->entries * sizeof(*io->urbs), mem_flags); if (!io->urbs) goto nomem; @@ -511,7 +511,7 @@ void usb_sg_wait(struct usb_sg_request *io) int retval; io->urbs[i]->dev = io->dev; - retval = usb_submit_urb(io->urbs [i], GFP_ATOMIC); + retval = usb_submit_urb(io->urbs[i], GFP_ATOMIC); /* after we submit, let completions or cancelations fire; * we handshake using io->status. @@ -586,9 +586,9 @@ void usb_sg_cancel(struct usb_sg_request *io) for (i = 0; i < io->entries; i++) { int retval; - if (!io->urbs [i]->dev) + if (!io->urbs[i]->dev) continue; - retval = usb_unlink_urb(io->urbs [i]); + retval = usb_unlink_urb(io->urbs[i]); if (retval != -EINPROGRESS && retval != -ENODEV && retval != -EBUSY diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index b8bad294eeb..d6b0fadf53e 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -86,7 +86,7 @@ static int usb_port_runtime_resume(struct device *dev) usb_autopm_get_interface(intf); set_bit(port1, hub->busy_bits); - retval = usb_hub_set_port_power(hdev, port1, true); + retval = usb_hub_set_port_power(hdev, hub, port1, true); if (port_dev->child && !retval) { /* * Wait for usb hub port to be reconnected in order to make @@ -128,7 +128,7 @@ static int usb_port_runtime_suspend(struct device *dev) usb_autopm_get_interface(intf); set_bit(port1, hub->busy_bits); - retval = usb_hub_set_port_power(hdev, port1, false); + retval = usb_hub_set_port_power(hdev, hub, port1, false); usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION); usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE); clear_bit(port1, hub->busy_bits); @@ -141,7 +141,6 @@ static const struct dev_pm_ops usb_port_pm_ops = { #ifdef CONFIG_PM_RUNTIME .runtime_suspend = usb_port_runtime_suspend, .runtime_resume = usb_port_runtime_resume, - .runtime_idle = pm_generic_runtime_idle, #endif }; diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index a6359889507..5b44cd47da5 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -78,6 +78,12 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x04d8, 0x000c), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, + /* CarrolTouch 4000U */ + { USB_DEVICE(0x04e7, 0x0009), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* CarrolTouch 4500U */ + { USB_DEVICE(0x04e7, 0x0030), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Samsung Android phone modem - ID conflict with SPH-I500 */ { USB_DEVICE(0x04e8, 0x6601), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index aa38db44818..d9284b998bd 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -497,8 +497,62 @@ set_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(usb2_hardware_lpm, S_IRUGO | S_IWUSR, show_usb2_hardware_lpm, set_usb2_hardware_lpm); +static ssize_t +show_usb2_lpm_l1_timeout(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb_device *udev = to_usb_device(dev); + return sprintf(buf, "%d\n", udev->l1_params.timeout); +} + +static ssize_t +set_usb2_lpm_l1_timeout(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + u16 timeout; + + if (kstrtou16(buf, 0, &timeout)) + return -EINVAL; + + udev->l1_params.timeout = timeout; + + return count; +} + +static DEVICE_ATTR(usb2_lpm_l1_timeout, S_IRUGO | S_IWUSR, + show_usb2_lpm_l1_timeout, set_usb2_lpm_l1_timeout); + +static ssize_t +show_usb2_lpm_besl(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb_device *udev = to_usb_device(dev); + return sprintf(buf, "%d\n", udev->l1_params.besl); +} + +static ssize_t +set_usb2_lpm_besl(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + u8 besl; + + if (kstrtou8(buf, 0, &besl) || besl > 15) + return -EINVAL; + + udev->l1_params.besl = besl; + + return count; +} + +static DEVICE_ATTR(usb2_lpm_besl, S_IRUGO | S_IWUSR, + show_usb2_lpm_besl, set_usb2_lpm_besl); + static struct attribute *usb2_hardware_lpm_attr[] = { &dev_attr_usb2_hardware_lpm.attr, + &dev_attr_usb2_lpm_l1_timeout.attr, + &dev_attr_usb2_lpm_besl.attr, NULL, }; static struct attribute_group usb2_hardware_lpm_attr_group = { diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index b10da720f2b..7dad603dde4 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -209,6 +209,39 @@ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) } EXPORT_SYMBOL_GPL(usb_find_interface); +struct each_dev_arg { + void *data; + int (*fn)(struct usb_device *, void *); +}; + +static int __each_dev(struct device *dev, void *data) +{ + struct each_dev_arg *arg = (struct each_dev_arg *)data; + + /* There are struct usb_interface on the same bus, filter them out */ + if (!is_usb_device(dev)) + return 0; + + return arg->fn(container_of(dev, struct usb_device, dev), arg->data); +} + +/** + * usb_for_each_dev - iterate over all USB devices in the system + * @data: data pointer that will be handed to the callback function + * @fn: callback function to be called for each USB device + * + * Iterate over all USB devices and call @fn for each, passing it @data. If it + * returns anything other than 0, we break the iteration prematurely and return + * that value. + */ +int usb_for_each_dev(void *data, int (*fn)(struct usb_device *, void *)) +{ + struct each_dev_arg arg = {data, fn}; + + return bus_for_each_dev(&usb_bus_type, NULL, &arg, __each_dev); +} +EXPORT_SYMBOL_GPL(usb_for_each_dev); + /** * usb_release_dev - free a usb device structure when all users of it are finished. * @dev: device that's been disconnected |