aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/core
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/Kconfig102
-rw-r--r--drivers/usb/core/Makefile5
-rw-r--r--drivers/usb/core/buffer.c7
-rw-r--r--drivers/usb/core/config.c18
-rw-r--r--drivers/usb/core/devices.c42
-rw-r--r--drivers/usb/core/devio.c670
-rw-r--r--drivers/usb/core/driver.c564
-rw-r--r--drivers/usb/core/endpoint.c47
-rw-r--r--drivers/usb/core/file.c34
-rw-r--r--drivers/usb/core/generic.c12
-rw-r--r--drivers/usb/core/hcd-pci.c245
-rw-r--r--drivers/usb/core/hcd.c643
-rw-r--r--drivers/usb/core/hub.c3206
-rw-r--r--drivers/usb/core/hub.h156
-rw-r--r--drivers/usb/core/inode.c773
-rw-r--r--drivers/usb/core/message.c223
-rw-r--r--drivers/usb/core/port.c484
-rw-r--r--drivers/usb/core/quirks.c222
-rw-r--r--drivers/usb/core/sysfs.c444
-rw-r--r--drivers/usb/core/urb.c213
-rw-r--r--drivers/usb/core/usb-acpi.c228
-rw-r--r--drivers/usb/core/usb.c127
-rw-r--r--drivers/usb/core/usb.h60
23 files changed, 5659 insertions, 2866 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index 18d02e32a3d..1060657ca1b 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -1,18 +1,8 @@
#
# USB Core configuration
#
-config USB_DEBUG
- bool "USB verbose debug messages"
- depends on USB
- help
- Say Y here if you want the USB core & hub drivers to produce a bunch
- of debug messages to the system log. Select this if you are having a
- problem with USB support and want to see more of what is going on.
-
config USB_ANNOUNCE_NEW_DEVICES
bool "USB announce new devices"
- depends on USB
- default N
help
Say Y here if you want the USB core to always announce the
idVendor, idProduct, Manufacturer, Product, and SerialNumber
@@ -25,63 +15,24 @@ config USB_ANNOUNCE_NEW_DEVICES
log, or have any doubts about this, say N here.
comment "Miscellaneous USB options"
- depends on USB
-
-config USB_DEVICEFS
- bool "USB device filesystem (DEPRECATED)"
- depends on USB
- ---help---
- If you say Y here (and to "/proc file system support" in the "File
- systems" section, above), you will get a file /proc/bus/usb/devices
- which lists the devices currently connected to your USB bus or
- busses, and for every connected device a file named
- "/proc/bus/usb/xxx/yyy", where xxx is the bus number and yyy the
- device number; the latter files can be used by user space programs
- to talk directly to the device. These files are "virtual", meaning
- they are generated on the fly and not stored on the hard drive.
-
- You may need to mount the usbfs file system to see the files, use
- mount -t usbfs none /proc/bus/usb
-
- For the format of the various /proc/bus/usb/ files, please read
- <file:Documentation/usb/proc_usb_info.txt>.
-
- Modern Linux systems do not use this.
-
- Usbfs entries are files and not character devices; usbfs can't
- handle Access Control Lists (ACL) which are the default way to
- grant access to USB devices for untrusted users of a desktop
- system.
-
- The usbfs functionality is replaced by real device-nodes managed by
- udev. These nodes lived in /dev/bus/usb and are used by libusb.
-config USB_DEVICE_CLASS
- bool "USB device class-devices (DEPRECATED)"
- depends on USB
+config USB_DEFAULT_PERSIST
+ bool "Enable USB persist by default"
default y
- ---help---
- Userspace access to USB devices is granted by device-nodes exported
- directly from the usbdev in sysfs. Old versions of the driver
- core and udev needed additional class devices to export device nodes.
-
- These additional devices are difficult to handle in userspace, if
- information about USB interfaces must be available. One device
- contains the device node, the other device contains the interface
- data. Both devices are at the same level in sysfs (siblings) and one
- can't access the other. The device node created directly by the
- usb device is the parent device of the interface and therefore
- easily accessible from the interface event.
+ help
+ Say N here if you don't want USB power session persistence
+ enabled by default. If you say N it will make suspended USB
+ devices that lose power get reenumerated as if they had been
+ unplugged, causing any mounted filesystems to be lost. The
+ persist feature can still be enabled for individual devices
+ through the power/persist sysfs node. See
+ Documentation/usb/persist.txt for more info.
- This option provides backward compatibility for libusb device
- nodes (lsusb) when usbfs is not used, and the following udev rule
- doesn't exist:
- SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", \
- NAME="bus/usb/$env{BUSNUM}/$env{DEVNUM}", MODE="0644"
+ If you have any questions about this, say Y here, only say N
+ if you know exactly what you are doing.
config USB_DYNAMIC_MINORS
bool "Dynamic USB minor allocation"
- depends on USB
help
If you say Y here, the USB subsystem will use dynamic minor
allocation for any device that uses the USB major number.
@@ -90,26 +41,9 @@ config USB_DYNAMIC_MINORS
If you are unsure about this, say N here.
-config USB_SUSPEND
- bool "USB runtime power management (autosuspend) and wakeup"
- depends on USB && PM_RUNTIME
- help
- If you say Y here, you can use driver calls or the sysfs
- "power/control" file to enable or disable autosuspend for
- individual USB peripherals (see
- Documentation/usb/power-management.txt for more details).
-
- Also, USB "remote wakeup" signaling is supported, whereby some
- USB devices (like keyboards and network adapters) can wake up
- their parent hub. That wakeup cascades up the USB tree, and
- could wake the system from states like suspend-to-RAM.
-
- If you are unsure about this, say N here.
-
config USB_OTG
bool "OTG support"
- depends on USB && EXPERIMENTAL
- depends on USB_SUSPEND
+ depends on PM_RUNTIME
default n
help
The most notable feature of USB OTG is support for a
@@ -125,7 +59,6 @@ config USB_OTG_WHITELIST
bool "Rely on OTG Targeted Peripherals List"
depends on USB_OTG || EXPERT
default y if USB_OTG
- default n if EXPERT
help
If you say Y here, the "otg_whitelist.h" file will be used as a
product whitelist, so USB peripherals not listed there will be
@@ -148,3 +81,12 @@ config USB_OTG_BLACKLIST_HUB
and software costs by not supporting external hubs. So
are "Embedded Hosts" that don't offer OTG support.
+config USB_OTG_FSM
+ tristate "USB 2.0 OTG FSM implementation"
+ depends on USB
+ select USB_OTG
+ select USB_PHY
+ help
+ Implements OTG Finite State Machine as specified in On-The-Go
+ and Embedded Host Supplement to the USB Revision 2.0 Specification.
+
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
index 507a4e1b636..2f6f9322004 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -2,13 +2,12 @@
# Makefile for USB Core files and filesystem
#
-ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG
-
usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o
usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o
usbcore-y += devio.o notify.o generic.o quirks.o devices.o
+usbcore-y += port.o
usbcore-$(CONFIG_PCI) += hcd-pci.o
-usbcore-$(CONFIG_USB_DEVICEFS) += inode.o
+usbcore-$(CONFIG_ACPI) += usb-acpi.o
obj-$(CONFIG_USB) += usbcore.o
diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c
index b0585e623ba..684ef70dc09 100644
--- a/drivers/usb/core/buffer.c
+++ b/drivers/usb/core/buffer.c
@@ -2,7 +2,7 @@
* DMA memory management for framework level HCD code (hc_driver)
*
* This implementation plugs in through generic "usb_bus" level methods,
- * and should work with all USB controllers, regardles of bus type.
+ * and should work with all USB controllers, regardless of bus type.
*/
#include <linux/module.h>
@@ -43,10 +43,11 @@ static const size_t pool_max[HCD_BUFFER_POOLS] = {
*
* Call this as part of initializing a host controller that uses the dma
* memory allocators. It initializes some pools of dma-coherent memory that
- * will be shared by all drivers using that controller, or returns a negative
- * errno value on error.
+ * will be shared by all drivers using that controller.
*
* Call hcd_buffer_destroy() to clean up after using those pools.
+ *
+ * Return: 0 if successful. A negative errno value otherwise.
*/
int hcd_buffer_create(struct usb_hcd *hcd)
{
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index f4bdd0ce8d5..1ab4df1de2d 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -3,7 +3,6 @@
#include <linux/usb/hcd.h>
#include <linux/usb/quirks.h>
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <asm/byteorder.h>
@@ -11,7 +10,6 @@
#define USB_MAXALTSETTING 128 /* Hard limit */
-#define USB_MAXENDPOINTS 30 /* Hard limit */
#define USB_MAXCONFIG 8 /* Arbitrary limit */
@@ -424,7 +422,8 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
if (config->desc.bDescriptorType != USB_DT_CONFIG ||
- config->desc.bLength < USB_DT_CONFIG_SIZE) {
+ config->desc.bLength < USB_DT_CONFIG_SIZE ||
+ config->desc.bLength > size) {
dev_err(ddev, "invalid descriptor for config index %d: "
"type = 0x%X, length = %d\n", cfgidx,
config->desc.bDescriptorType, config->desc.bLength);
@@ -650,10 +649,6 @@ void usb_destroy_configuration(struct usb_device *dev)
*
* hub-only!! ... and only in reset path, or usb_new_device()
* (used by real hubs and virtual root hubs)
- *
- * NOTE: if this is a WUSB device and is not authorized, we skip the
- * whole thing. A non-authorized USB device has no
- * configurations.
*/
int usb_get_configuration(struct usb_device *dev)
{
@@ -665,8 +660,6 @@ int usb_get_configuration(struct usb_device *dev)
struct usb_config_descriptor *desc;
cfgno = 0;
- if (dev->authorized == 0) /* Not really an error */
- goto out_not_authorized;
result = -ENOMEM;
if (ncfg > USB_MAXCONFIG) {
dev_warn(ddev, "too many configurations: %d, "
@@ -702,6 +695,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;
@@ -721,6 +716,10 @@ int usb_get_configuration(struct usb_device *dev)
result = -ENOMEM;
goto err;
}
+
+ if (dev->quirks & USB_QUIRK_DELAY_INIT)
+ msleep(100);
+
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
bigbuffer, length);
if (result < 0) {
@@ -748,7 +747,6 @@ int usb_get_configuration(struct usb_device *dev)
err:
kfree(desc);
-out_not_authorized:
dev->descriptor.bNumConfigurations = cfgno;
err2:
if (result == -ENOMEM)
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index d9569658476..2a3bbdf7eb9 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -316,17 +316,23 @@ static char *usb_dump_iad_descriptor(char *start, char *end,
*/
static char *usb_dump_config_descriptor(char *start, char *end,
const struct usb_config_descriptor *desc,
- int active)
+ int active, int speed)
{
+ int mul;
+
if (start > end)
return start;
+ if (speed == USB_SPEED_SUPER)
+ mul = 8;
+ else
+ mul = 2;
start += sprintf(start, format_config,
/* mark active/actual/current cfg. */
active ? '*' : ' ',
desc->bNumInterfaces,
desc->bConfigurationValue,
desc->bmAttributes,
- desc->bMaxPower * 2);
+ desc->bMaxPower * mul);
return start;
}
@@ -342,7 +348,8 @@ static char *usb_dump_config(int speed, char *start, char *end,
if (!config)
/* getting these some in 2.3.7; none in 2.3.6 */
return start + sprintf(start, "(null Cfg. desc.)\n");
- start = usb_dump_config_descriptor(start, end, &config->desc, active);
+ start = usb_dump_config_descriptor(start, end, &config->desc, active,
+ speed);
for (i = 0; i < USB_MAXIADS; i++) {
if (config->intf_assoc[i] == NULL)
break;
@@ -496,6 +503,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,19 +597,15 @@ 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];
-
- if (childdev) {
- usb_lock_device(childdev);
- ret = usb_device_dump(buffer, nbytes, skip_bytes,
- file_offset, childdev, bus,
- level + 1, chix, ++cnt);
- usb_unlock_device(childdev);
- if (ret == -EFAULT)
- return total_written;
- total_written += ret;
- }
+ usb_hub_for_each_child(usbdev, chix, childdev) {
+ usb_lock_device(childdev);
+ ret = usb_device_dump(buffer, nbytes, skip_bytes,
+ file_offset, childdev, bus,
+ level + 1, chix - 1, ++cnt);
+ usb_unlock_device(childdev);
+ if (ret == -EFAULT)
+ return total_written;
+ total_written += ret;
}
return total_written;
}
@@ -624,7 +628,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf,
/* print devices for all busses */
list_for_each_entry(bus, &usb_bus_list, bus_list) {
/* recurse through all children of the root hub */
- if (!bus->root_hub)
+ if (!bus_to_hcd(bus)->rh_registered)
continue;
usb_lock_device(bus->root_hub);
ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos,
@@ -661,7 +665,7 @@ static loff_t usb_device_lseek(struct file *file, loff_t offset, int orig)
{
loff_t ret;
- mutex_lock(&file->f_dentry->d_inode->i_mutex);
+ mutex_lock(&file_inode(file)->i_mutex);
switch (orig) {
case 0:
@@ -677,7 +681,7 @@ static loff_t usb_device_lseek(struct file *file, loff_t offset, int orig)
ret = -EINVAL;
}
- mutex_unlock(&file->f_dentry->d_inode->i_mutex);
+ mutex_unlock(&file_inode(file)->i_mutex);
return ret;
}
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 8df4b76465a..257876ea03a 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -40,6 +40,7 @@
#include <linux/signal.h>
#include <linux/poll.h>
#include <linux/module.h>
+#include <linux/string.h>
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
#include <linux/usb/hcd.h> /* for usbcore internals */
@@ -47,19 +48,21 @@
#include <linux/notifier.h>
#include <linux/security.h>
#include <linux/user_namespace.h>
-#include <asm/uaccess.h>
+#include <linux/scatterlist.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 */
DEFINE_MUTEX(usbfs_mutex);
-struct dev_state {
+struct usb_dev_state {
struct list_head list; /* state list */
struct usb_device *dev;
struct file *file;
@@ -78,7 +81,7 @@ struct dev_state {
struct async {
struct list_head asynclist;
- struct dev_state *ps;
+ struct usb_dev_state *ps;
struct pid *pid;
const struct cred *cred;
unsigned int signr;
@@ -115,7 +118,7 @@ module_param(usbfs_memory_mb, uint, 0644);
MODULE_PARM_DESC(usbfs_memory_mb,
"maximum MB allowed for usbfs buffers (0 = no limit)");
-/* Hard limit, necessary to avoid aithmetic overflow */
+/* Hard limit, necessary to avoid arithmetic overflow */
#define USBFS_XFER_MAX (UINT_MAX / 2 - 1000000)
static atomic_t usbfs_memory_usage; /* Total memory currently allocated */
@@ -148,7 +151,7 @@ static void usbfs_decrease_memory_usage(unsigned amount)
atomic_sub(amount, &usbfs_memory_usage);
}
-static int connected(struct dev_state *ps)
+static int connected(struct usb_dev_state *ps)
{
return (!list_empty(&ps->list) &&
ps->dev->state != USB_STATE_NOTATTACHED);
@@ -158,7 +161,7 @@ static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
{
loff_t ret;
- mutex_lock(&file->f_dentry->d_inode->i_mutex);
+ mutex_lock(&file_inode(file)->i_mutex);
switch (orig) {
case 0:
@@ -174,14 +177,14 @@ static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
ret = -EINVAL;
}
- mutex_unlock(&file->f_dentry->d_inode->i_mutex);
+ mutex_unlock(&file_inode(file)->i_mutex);
return ret;
}
static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes,
loff_t *ppos)
{
- struct dev_state *ps = file->private_data;
+ struct usb_dev_state *ps = file->private_data;
struct usb_device *dev = ps->dev;
ssize_t ret = 0;
unsigned len;
@@ -285,9 +288,16 @@ static struct async *alloc_async(unsigned int numisoframes)
static void free_async(struct async *as)
{
+ int i;
+
put_pid(as->pid);
if (as->cred)
put_cred(as->cred);
+ for (i = 0; i < as->urb->num_sgs; i++) {
+ if (sg_page(&as->urb->sg[i]))
+ kfree(sg_virt(&as->urb->sg[i]));
+ }
+ kfree(as->urb->sg);
kfree(as->urb->transfer_buffer);
kfree(as->urb->setup_packet);
usb_free_urb(as->urb);
@@ -297,7 +307,7 @@ static void free_async(struct async *as)
static void async_newpending(struct async *as)
{
- struct dev_state *ps = as->ps;
+ struct usb_dev_state *ps = as->ps;
unsigned long flags;
spin_lock_irqsave(&ps->lock, flags);
@@ -307,7 +317,7 @@ static void async_newpending(struct async *as)
static void async_removepending(struct async *as)
{
- struct dev_state *ps = as->ps;
+ struct usb_dev_state *ps = as->ps;
unsigned long flags;
spin_lock_irqsave(&ps->lock, flags);
@@ -315,7 +325,7 @@ static void async_removepending(struct async *as)
spin_unlock_irqrestore(&ps->lock, flags);
}
-static struct async *async_getcompleted(struct dev_state *ps)
+static struct async *async_getcompleted(struct usb_dev_state *ps)
{
unsigned long flags;
struct async *as = NULL;
@@ -330,20 +340,17 @@ static struct async *async_getcompleted(struct dev_state *ps)
return as;
}
-static struct async *async_getpending(struct dev_state *ps,
+static struct async *async_getpending(struct usb_dev_state *ps,
void __user *userurb)
{
- unsigned long flags;
struct async *as;
- spin_lock_irqsave(&ps->lock, flags);
list_for_each_entry(as, &ps->async_pending, asynclist)
if (as->userurb == userurb) {
list_del_init(&as->asynclist);
- spin_unlock_irqrestore(&ps->lock, flags);
return as;
}
- spin_unlock_irqrestore(&ps->lock, flags);
+
return NULL;
}
@@ -391,13 +398,61 @@ static void snoop_urb(struct usb_device *udev,
}
}
+static void snoop_urb_data(struct urb *urb, unsigned len)
+{
+ int i, size;
+
+ if (!usbfs_snoop)
+ return;
+
+ if (urb->num_sgs == 0) {
+ print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1,
+ urb->transfer_buffer, len, 1);
+ return;
+ }
+
+ for (i = 0; i < urb->num_sgs && len; i++) {
+ size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len;
+ print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1,
+ sg_virt(&urb->sg[i]), size, 1);
+ len -= size;
+ }
+}
+
+static int copy_urb_data_to_user(u8 __user *userbuffer, struct urb *urb)
+{
+ unsigned i, len, size;
+
+ if (urb->number_of_packets > 0) /* Isochronous */
+ len = urb->transfer_buffer_length;
+ else /* Non-Isoc */
+ len = urb->actual_length;
+
+ if (urb->num_sgs == 0) {
+ if (copy_to_user(userbuffer, urb->transfer_buffer, len))
+ return -EFAULT;
+ return 0;
+ }
+
+ for (i = 0; i < urb->num_sgs && len; i++) {
+ size = (len > USB_SG_SIZE) ? USB_SG_SIZE : len;
+ if (copy_to_user(userbuffer, sg_virt(&urb->sg[i]), size))
+ return -EFAULT;
+ userbuffer += size;
+ len -= size;
+ }
+
+ return 0;
+}
+
#define AS_CONTINUATION 1
#define AS_UNLINK 2
-static void cancel_bulk_urbs(struct dev_state *ps, unsigned bulk_addr)
+static void cancel_bulk_urbs(struct usb_dev_state *ps, unsigned bulk_addr)
__releases(ps->lock)
__acquires(ps->lock)
{
+ struct urb *urb;
struct async *as;
/* Mark all the pending URBs that match bulk_addr, up to but not
@@ -420,8 +475,11 @@ __acquires(ps->lock)
list_for_each_entry(as, &ps->async_pending, asynclist) {
if (as->bulk_status == AS_UNLINK) {
as->bulk_status = 0; /* Only once */
+ urb = as->urb;
+ usb_get_urb(urb);
spin_unlock(&ps->lock); /* Allow completions */
- usb_unlink_urb(as->urb);
+ usb_unlink_urb(urb);
+ usb_put_urb(urb);
spin_lock(&ps->lock);
goto rescan;
}
@@ -431,7 +489,7 @@ __acquires(ps->lock)
static void async_completed(struct urb *urb)
{
struct async *as = urb->context;
- struct dev_state *ps = as->ps;
+ struct usb_dev_state *ps = as->ps;
struct siginfo sinfo;
struct pid *pid = NULL;
u32 secid = 0;
@@ -453,9 +511,10 @@ static void async_completed(struct urb *urb)
}
snoop(&urb->dev->dev, "urb complete\n");
snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length,
- as->status, COMPLETE,
- ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_OUT) ?
- NULL : urb->transfer_buffer, urb->actual_length);
+ as->status, COMPLETE, NULL, 0);
+ if ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_IN)
+ snoop_urb_data(urb, urb->actual_length);
+
if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET &&
as->status != -ENOENT)
cancel_bulk_urbs(ps, as->bulk_addr);
@@ -470,8 +529,9 @@ static void async_completed(struct urb *urb)
wake_up(&ps->wait);
}
-static void destroy_async(struct dev_state *ps, struct list_head *list)
+static void destroy_async(struct usb_dev_state *ps, struct list_head *list)
{
+ struct urb *urb;
struct async *as;
unsigned long flags;
@@ -479,16 +539,19 @@ static void destroy_async(struct dev_state *ps, struct list_head *list)
while (!list_empty(list)) {
as = list_entry(list->next, struct async, asynclist);
list_del_init(&as->asynclist);
+ urb = as->urb;
+ usb_get_urb(urb);
/* drop the spinlock so the completion handler can run */
spin_unlock_irqrestore(&ps->lock, flags);
- usb_kill_urb(as->urb);
+ usb_kill_urb(urb);
+ usb_put_urb(urb);
spin_lock_irqsave(&ps->lock, flags);
}
spin_unlock_irqrestore(&ps->lock, flags);
}
-static void destroy_async_on_interface(struct dev_state *ps,
+static void destroy_async_on_interface(struct usb_dev_state *ps,
unsigned int ifnum)
{
struct list_head *p, *q, hitlist;
@@ -503,7 +566,7 @@ static void destroy_async_on_interface(struct dev_state *ps,
destroy_async(ps, &hitlist);
}
-static void destroy_all_async(struct dev_state *ps)
+static void destroy_all_async(struct usb_dev_state *ps)
{
destroy_async(ps, &ps->async_pending);
}
@@ -522,7 +585,7 @@ static int driver_probe(struct usb_interface *intf,
static void driver_disconnect(struct usb_interface *intf)
{
- struct dev_state *ps = usb_get_intfdata(intf);
+ struct usb_dev_state *ps = usb_get_intfdata(intf);
unsigned int ifnum = intf->altsetting->desc.bInterfaceNumber;
if (!ps)
@@ -565,7 +628,7 @@ struct usb_driver usbfs_driver = {
.resume = driver_resume,
};
-static int claimintf(struct dev_state *ps, unsigned int ifnum)
+static int claimintf(struct usb_dev_state *ps, unsigned int ifnum)
{
struct usb_device *dev = ps->dev;
struct usb_interface *intf;
@@ -587,7 +650,7 @@ static int claimintf(struct dev_state *ps, unsigned int ifnum)
return err;
}
-static int releaseintf(struct dev_state *ps, unsigned int ifnum)
+static int releaseintf(struct usb_dev_state *ps, unsigned int ifnum)
{
struct usb_device *dev;
struct usb_interface *intf;
@@ -607,7 +670,7 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum)
return err;
}
-static int checkintf(struct dev_state *ps, unsigned int ifnum)
+static int checkintf(struct usb_dev_state *ps, unsigned int ifnum)
{
if (ps->dev->state != USB_STATE_CONFIGURED)
return -EHOSTUNREACH;
@@ -647,7 +710,7 @@ static int findintfep(struct usb_device *dev, unsigned int ep)
return -ENOENT;
}
-static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,
+static int check_ctrlrecip(struct usb_dev_state *ps, unsigned int requesttype,
unsigned int request, unsigned int index)
{
int ret = 0;
@@ -662,21 +725,39 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,
/*
* check for the special corner case 'get_device_id' in the printer
- * class specification, where wIndex is (interface << 8 | altsetting)
- * instead of just interface
+ * class specification, which we always want to allow as it is used
+ * to query things like ink level, etc.
*/
if (requesttype == 0xa1 && request == 0) {
alt_setting = usb_find_alt_setting(ps->dev->actconfig,
index >> 8, index & 0xff);
if (alt_setting
&& alt_setting->desc.bInterfaceClass == USB_CLASS_PRINTER)
- index >>= 8;
+ return 0;
}
index &= 0xff;
switch (requesttype & USB_RECIP_MASK) {
case USB_RECIP_ENDPOINT:
+ if ((index & ~USB_DIR_IN) == 0)
+ return 0;
ret = findintfep(ps->dev, index);
+ if (ret < 0) {
+ /*
+ * Some not fully compliant Win apps seem to get
+ * index wrong and have the endpoint number here
+ * rather than the endpoint address (with the
+ * correct direction). Win does let this through,
+ * so we'll not reject it here but leave it to
+ * the device to not break KVM. But we warn.
+ */
+ ret = findintfep(ps->dev, index ^ 0x80);
+ if (ret >= 0)
+ dev_info(&ps->dev->dev,
+ "%s: process %i (%s) requesting ep %02x but needs %02x\n",
+ __func__, task_pid_nr(current),
+ current->comm, index, index ^ 0x80);
+ }
if (ret >= 0)
ret = checkintf(ps, ret);
break;
@@ -688,6 +769,88 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,
return ret;
}
+static struct usb_host_endpoint *ep_to_host_endpoint(struct usb_device *dev,
+ unsigned char ep)
+{
+ if (ep & USB_ENDPOINT_DIR_MASK)
+ return dev->ep_in[ep & USB_ENDPOINT_NUMBER_MASK];
+ else
+ return dev->ep_out[ep & USB_ENDPOINT_NUMBER_MASK];
+}
+
+static int parse_usbdevfs_streams(struct usb_dev_state *ps,
+ struct usbdevfs_streams __user *streams,
+ unsigned int *num_streams_ret,
+ unsigned int *num_eps_ret,
+ struct usb_host_endpoint ***eps_ret,
+ struct usb_interface **intf_ret)
+{
+ unsigned int i, num_streams, num_eps;
+ struct usb_host_endpoint **eps;
+ struct usb_interface *intf = NULL;
+ unsigned char ep;
+ int ifnum, ret;
+
+ if (get_user(num_streams, &streams->num_streams) ||
+ get_user(num_eps, &streams->num_eps))
+ return -EFAULT;
+
+ if (num_eps < 1 || num_eps > USB_MAXENDPOINTS)
+ return -EINVAL;
+
+ /* The XHCI controller allows max 2 ^ 16 streams */
+ if (num_streams_ret && (num_streams < 2 || num_streams > 65536))
+ return -EINVAL;
+
+ eps = kmalloc(num_eps * sizeof(*eps), GFP_KERNEL);
+ if (!eps)
+ return -ENOMEM;
+
+ for (i = 0; i < num_eps; i++) {
+ if (get_user(ep, &streams->eps[i])) {
+ ret = -EFAULT;
+ goto error;
+ }
+ eps[i] = ep_to_host_endpoint(ps->dev, ep);
+ if (!eps[i]) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* usb_alloc/free_streams operate on an usb_interface */
+ ifnum = findintfep(ps->dev, ep);
+ if (ifnum < 0) {
+ ret = ifnum;
+ goto error;
+ }
+
+ if (i == 0) {
+ ret = checkintf(ps, ifnum);
+ if (ret < 0)
+ goto error;
+ intf = usb_ifnum_to_if(ps->dev, ifnum);
+ } else {
+ /* Verify all eps belong to the same interface */
+ if (ifnum != intf->altsetting->desc.bInterfaceNumber) {
+ ret = -EINVAL;
+ goto error;
+ }
+ }
+ }
+
+ if (num_streams_ret)
+ *num_streams_ret = num_streams;
+ *num_eps_ret = num_eps;
+ *eps_ret = eps;
+ *intf_ret = intf;
+
+ return 0;
+
+error:
+ kfree(eps);
+ return ret;
+}
+
static int match_devt(struct device *dev, void *data)
{
return dev->devt == (dev_t) (unsigned long) data;
@@ -710,11 +873,11 @@ static struct usb_device *usbdev_lookup_by_devt(dev_t devt)
static int usbdev_open(struct inode *inode, struct file *file)
{
struct usb_device *dev = NULL;
- struct dev_state *ps;
+ struct usb_dev_state *ps;
int ret;
ret = -ENOMEM;
- ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL);
+ ps = kmalloc(sizeof(struct usb_dev_state), GFP_KERNEL);
if (!ps)
goto out_free_ps;
@@ -727,17 +890,6 @@ static int usbdev_open(struct inode *inode, struct file *file)
if (imajor(inode) == USB_DEVICE_MAJOR)
dev = usbdev_lookup_by_devt(inode->i_rdev);
-#ifdef CONFIG_USB_DEVICEFS
- /* procfs file */
- if (!dev) {
- dev = inode->i_private;
- if (dev && dev->usbfs_dentry &&
- dev->usbfs_dentry->d_inode == inode)
- usb_get_dev(dev);
- else
- dev = NULL;
- }
-#endif
mutex_unlock(&usbfs_mutex);
if (!dev)
@@ -782,7 +934,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
static int usbdev_release(struct inode *inode, struct file *file)
{
- struct dev_state *ps = file->private_data;
+ struct usb_dev_state *ps = file->private_data;
struct usb_device *dev = ps->dev;
unsigned int ifnum;
struct async *as;
@@ -813,7 +965,7 @@ static int usbdev_release(struct inode *inode, struct file *file)
return 0;
}
-static int proc_control(struct dev_state *ps, void __user *arg)
+static int proc_control(struct usb_dev_state *ps, void __user *arg)
{
struct usb_device *dev = ps->dev;
struct usbdevfs_ctrltransfer ctrl;
@@ -844,10 +996,8 @@ static int proc_control(struct dev_state *ps, void __user *arg)
snoop(&dev->dev, "control urb: bRequestType=%02x "
"bRequest=%02x wValue=%04x "
"wIndex=%04x wLength=%04x\n",
- ctrl.bRequestType, ctrl.bRequest,
- __le16_to_cpup(&ctrl.wValue),
- __le16_to_cpup(&ctrl.wIndex),
- __le16_to_cpup(&ctrl.wLength));
+ ctrl.bRequestType, ctrl.bRequest, ctrl.wValue,
+ ctrl.wIndex, ctrl.wLength);
if (ctrl.bRequestType & 0x80) {
if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data,
ctrl.wLength)) {
@@ -902,7 +1052,7 @@ static int proc_control(struct dev_state *ps, void __user *arg)
return ret;
}
-static int proc_bulk(struct dev_state *ps, void __user *arg)
+static int proc_bulk(struct usb_dev_state *ps, void __user *arg)
{
struct usb_device *dev = ps->dev;
struct usbdevfs_bulktransfer bulk;
@@ -975,7 +1125,21 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
return ret;
}
-static int proc_resetep(struct dev_state *ps, void __user *arg)
+static void check_reset_of_active_ep(struct usb_device *udev,
+ unsigned int epnum, char *ioctl_name)
+{
+ struct usb_host_endpoint **eps;
+ struct usb_host_endpoint *ep;
+
+ eps = (epnum & USB_DIR_IN) ? udev->ep_in : udev->ep_out;
+ ep = eps[epnum & 0x0f];
+ if (ep && !list_empty(&ep->urb_list))
+ dev_warn(&udev->dev, "Process %d (%s) called USBDEVFS_%s for active endpoint 0x%02x\n",
+ task_pid_nr(current), current->comm,
+ ioctl_name, epnum);
+}
+
+static int proc_resetep(struct usb_dev_state *ps, void __user *arg)
{
unsigned int ep;
int ret;
@@ -988,11 +1152,12 @@ static int proc_resetep(struct dev_state *ps, void __user *arg)
ret = checkintf(ps, ret);
if (ret)
return ret;
+ check_reset_of_active_ep(ps->dev, ep, "RESETEP");
usb_reset_endpoint(ps->dev, ep);
return 0;
}
-static int proc_clearhalt(struct dev_state *ps, void __user *arg)
+static int proc_clearhalt(struct usb_dev_state *ps, void __user *arg)
{
unsigned int ep;
int pipe;
@@ -1006,6 +1171,7 @@ static int proc_clearhalt(struct dev_state *ps, void __user *arg)
ret = checkintf(ps, ret);
if (ret)
return ret;
+ check_reset_of_active_ep(ps->dev, ep, "CLEAR_HALT");
if (ep & USB_DIR_IN)
pipe = usb_rcvbulkpipe(ps->dev, ep & 0x7f);
else
@@ -1014,7 +1180,7 @@ static int proc_clearhalt(struct dev_state *ps, void __user *arg)
return usb_clear_halt(ps->dev, pipe);
}
-static int proc_getdriver(struct dev_state *ps, void __user *arg)
+static int proc_getdriver(struct usb_dev_state *ps, void __user *arg)
{
struct usbdevfs_getdriver gd;
struct usb_interface *intf;
@@ -1026,14 +1192,14 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg)
if (!intf || !intf->dev.driver)
ret = -ENODATA;
else {
- strncpy(gd.driver, intf->dev.driver->name,
+ strlcpy(gd.driver, intf->dev.driver->name,
sizeof(gd.driver));
ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0);
}
return ret;
}
-static int proc_connectinfo(struct dev_state *ps, void __user *arg)
+static int proc_connectinfo(struct usb_dev_state *ps, void __user *arg)
{
struct usbdevfs_connectinfo ci = {
.devnum = ps->dev->devnum,
@@ -1045,12 +1211,12 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg)
return 0;
}
-static int proc_resetdevice(struct dev_state *ps)
+static int proc_resetdevice(struct usb_dev_state *ps)
{
return usb_reset_device(ps->dev);
}
-static int proc_setintf(struct dev_state *ps, void __user *arg)
+static int proc_setintf(struct usb_dev_state *ps, void __user *arg)
{
struct usbdevfs_setinterface setintf;
int ret;
@@ -1059,11 +1225,14 @@ static int proc_setintf(struct dev_state *ps, void __user *arg)
return -EFAULT;
if ((ret = checkintf(ps, setintf.interface)))
return ret;
+
+ destroy_async_on_interface(ps, setintf.interface);
+
return usb_set_interface(ps->dev, setintf.interface,
setintf.altsetting);
}
-static int proc_setconfig(struct dev_state *ps, void __user *arg)
+static int proc_setconfig(struct usb_dev_state *ps, void __user *arg)
{
int u;
int status = 0;
@@ -1111,7 +1280,7 @@ static int proc_setconfig(struct dev_state *ps, void __user *arg)
return status;
}
-static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
+static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb,
struct usbdevfs_iso_packet_desc __user *iso_frame_desc,
void __user *arg)
{
@@ -1120,8 +1289,10 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
struct async *as = NULL;
struct usb_ctrlrequest *dr = NULL;
unsigned int u, totlen, isofrmlen;
- int ret, ifnum = -1;
- int is_in;
+ int i, ret, is_in, num_sgs = 0, ifnum = -1;
+ int number_of_packets = 0;
+ unsigned int stream_id = 0;
+ void *buf;
if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP |
USBDEVFS_URB_SHORT_NOT_OK |
@@ -1141,15 +1312,10 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
if (ret)
return ret;
}
- if ((uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0) {
- is_in = 1;
- ep = ps->dev->ep_in[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
- } else {
- is_in = 0;
- ep = ps->dev->ep_out[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
- }
+ ep = ep_to_host_endpoint(ps->dev, uurb->endpoint);
if (!ep)
return -ENOENT;
+ is_in = (uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0;
u = 0;
switch(uurb->type) {
@@ -1174,7 +1340,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
le16_to_cpup(&dr->wIndex));
if (ret)
goto error;
- uurb->number_of_packets = 0;
uurb->buffer_length = le16_to_cpup(&dr->wLength);
uurb->buffer += 8;
if ((dr->bRequestType & USB_DIR_IN) && uurb->buffer_length) {
@@ -1204,14 +1369,17 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
uurb->type = USBDEVFS_URB_TYPE_INTERRUPT;
goto interrupt_urb;
}
- uurb->number_of_packets = 0;
+ num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE);
+ if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize)
+ num_sgs = 0;
+ if (ep->streams)
+ stream_id = uurb->stream_id;
break;
case USBDEVFS_URB_TYPE_INTERRUPT:
if (!usb_endpoint_xfer_int(&ep->desc))
return -EINVAL;
interrupt_urb:
- uurb->number_of_packets = 0;
break;
case USBDEVFS_URB_TYPE_ISO:
@@ -1221,18 +1389,23 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
return -EINVAL;
if (!usb_endpoint_xfer_isoc(&ep->desc))
return -EINVAL;
+ number_of_packets = uurb->number_of_packets;
isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) *
- uurb->number_of_packets;
+ number_of_packets;
if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL)))
return -ENOMEM;
if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) {
ret = -EFAULT;
goto error;
}
- for (totlen = u = 0; u < uurb->number_of_packets; u++) {
- /* arbitrary limit,
- * sufficient for USB 2.0 high-bandwidth iso */
- if (isopkt[u].length > 8192) {
+ for (totlen = u = 0; u < number_of_packets; u++) {
+ /*
+ * arbitrary limit need for USB 3.0
+ * bMaxBurst (0~15 allowed, 1~16 packets)
+ * bmAttributes (bit 1:0, mult 0~2, 1~3 packets)
+ * sizemax: 1024 * 16 * 3 = 49152
+ */
+ if (isopkt[u].length > 49152) {
ret = -EINVAL;
goto error;
}
@@ -1256,31 +1429,73 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
ret = -EFAULT;
goto error;
}
- as = alloc_async(uurb->number_of_packets);
+ as = alloc_async(number_of_packets);
if (!as) {
ret = -ENOMEM;
goto error;
}
- u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length;
+
+ u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length +
+ num_sgs * sizeof(struct scatterlist);
ret = usbfs_increase_memory_usage(u);
if (ret)
goto error;
as->mem_usage = u;
- if (uurb->buffer_length > 0) {
+ if (num_sgs) {
+ as->urb->sg = kmalloc(num_sgs * sizeof(struct scatterlist),
+ GFP_KERNEL);
+ if (!as->urb->sg) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ as->urb->num_sgs = num_sgs;
+ sg_init_table(as->urb->sg, as->urb->num_sgs);
+
+ totlen = uurb->buffer_length;
+ for (i = 0; i < as->urb->num_sgs; i++) {
+ u = (totlen > USB_SG_SIZE) ? USB_SG_SIZE : totlen;
+ buf = kmalloc(u, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ sg_set_buf(&as->urb->sg[i], buf, u);
+
+ if (!is_in) {
+ if (copy_from_user(buf, uurb->buffer, u)) {
+ ret = -EFAULT;
+ goto error;
+ }
+ uurb->buffer += u;
+ }
+ totlen -= u;
+ }
+ } else if (uurb->buffer_length > 0) {
as->urb->transfer_buffer = kmalloc(uurb->buffer_length,
GFP_KERNEL);
if (!as->urb->transfer_buffer) {
ret = -ENOMEM;
goto error;
}
- /* Isochronous input data may end up being discontiguous
- * if some of the packets are short. Clear the buffer so
- * that the gaps don't leak kernel data to userspace.
- */
- if (is_in && uurb->type == USBDEVFS_URB_TYPE_ISO)
+
+ if (!is_in) {
+ if (copy_from_user(as->urb->transfer_buffer,
+ uurb->buffer,
+ uurb->buffer_length)) {
+ ret = -EFAULT;
+ goto error;
+ }
+ } else if (uurb->type == USBDEVFS_URB_TYPE_ISO) {
+ /*
+ * Isochronous input data may end up being
+ * discontiguous if some of the packets are short.
+ * Clear the buffer so that the gaps don't leak
+ * kernel data to userspace.
+ */
memset(as->urb->transfer_buffer, 0,
uurb->buffer_length);
+ }
}
as->urb->dev = ps->dev;
as->urb->pipe = (uurb->type << 30) |
@@ -1308,7 +1523,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
as->urb->setup_packet = (unsigned char *)dr;
dr = NULL;
as->urb->start_frame = uurb->start_frame;
- as->urb->number_of_packets = uurb->number_of_packets;
+ as->urb->number_of_packets = number_of_packets;
+ as->urb->stream_id = stream_id;
if (uurb->type == USBDEVFS_URB_TYPE_ISO ||
ps->dev->speed == USB_SPEED_HIGH)
as->urb->interval = 1 << min(15, ep->desc.bInterval - 1);
@@ -1316,7 +1532,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
as->urb->interval = ep->desc.bInterval;
as->urb->context = as;
as->urb->complete = async_completed;
- for (totlen = u = 0; u < uurb->number_of_packets; u++) {
+ for (totlen = u = 0; u < number_of_packets; u++) {
as->urb->iso_frame_desc[u].offset = totlen;
as->urb->iso_frame_desc[u].length = isopkt[u].length;
totlen += isopkt[u].length;
@@ -1334,17 +1550,12 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
as->pid = get_pid(task_pid(current));
as->cred = get_current_cred();
security_task_getsecid(current, &as->secid);
- if (!is_in && uurb->buffer_length > 0) {
- if (copy_from_user(as->urb->transfer_buffer, uurb->buffer,
- uurb->buffer_length)) {
- ret = -EFAULT;
- goto error;
- }
- }
snoop_urb(ps->dev, as->userurb, as->urb->pipe,
as->urb->transfer_buffer_length, 0, SUBMIT,
- is_in ? NULL : as->urb->transfer_buffer,
- uurb->buffer_length);
+ NULL, 0);
+ if (!is_in)
+ snoop_urb_data(as->urb, as->urb->transfer_buffer_length);
+
async_newpending(as);
if (usb_endpoint_xfer_bulk(&ep->desc)) {
@@ -1396,7 +1607,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
return ret;
}
-static int proc_submiturb(struct dev_state *ps, void __user *arg)
+static int proc_submiturb(struct usb_dev_state *ps, void __user *arg)
{
struct usbdevfs_urb uurb;
@@ -1408,14 +1619,26 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg)
arg);
}
-static int proc_unlinkurb(struct dev_state *ps, void __user *arg)
+static int proc_unlinkurb(struct usb_dev_state *ps, void __user *arg)
{
+ struct urb *urb;
struct async *as;
+ unsigned long flags;
+ spin_lock_irqsave(&ps->lock, flags);
as = async_getpending(ps, arg);
- if (!as)
+ if (!as) {
+ spin_unlock_irqrestore(&ps->lock, flags);
return -EINVAL;
- usb_kill_urb(as->urb);
+ }
+
+ urb = as->urb;
+ usb_get_urb(urb);
+ spin_unlock_irqrestore(&ps->lock, flags);
+
+ usb_kill_urb(urb);
+ usb_put_urb(urb);
+
return 0;
}
@@ -1427,11 +1650,7 @@ static int processcompl(struct async *as, void __user * __user *arg)
unsigned int i;
if (as->userbuffer && urb->actual_length) {
- if (urb->number_of_packets > 0) /* Isochronous */
- i = urb->transfer_buffer_length;
- else /* Non-Isoc */
- i = urb->actual_length;
- if (copy_to_user(as->userbuffer, urb->transfer_buffer, i))
+ if (copy_urb_data_to_user(as->userbuffer, urb))
goto err_out;
}
if (put_user(as->status, &userurb->status))
@@ -1460,7 +1679,7 @@ err_out:
return -EFAULT;
}
-static struct async *reap_as(struct dev_state *ps)
+static struct async *reap_as(struct usb_dev_state *ps)
{
DECLARE_WAITQUEUE(wait, current);
struct async *as = NULL;
@@ -1483,7 +1702,7 @@ static struct async *reap_as(struct dev_state *ps)
return as;
}
-static int proc_reapurb(struct dev_state *ps, void __user *arg)
+static int proc_reapurb(struct usb_dev_state *ps, void __user *arg)
{
struct async *as = reap_as(ps);
if (as) {
@@ -1496,7 +1715,7 @@ static int proc_reapurb(struct dev_state *ps, void __user *arg)
return -EIO;
}
-static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg)
+static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg)
{
int retval;
struct async *as;
@@ -1511,37 +1730,37 @@ static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg)
}
#ifdef CONFIG_COMPAT
-static int proc_control_compat(struct dev_state *ps,
+static int proc_control_compat(struct usb_dev_state *ps,
struct usbdevfs_ctrltransfer32 __user *p32)
{
- struct usbdevfs_ctrltransfer __user *p;
- __u32 udata;
- p = compat_alloc_user_space(sizeof(*p));
- if (copy_in_user(p, p32, (sizeof(*p32) - sizeof(compat_caddr_t))) ||
- get_user(udata, &p32->data) ||
+ struct usbdevfs_ctrltransfer __user *p;
+ __u32 udata;
+ p = compat_alloc_user_space(sizeof(*p));
+ if (copy_in_user(p, p32, (sizeof(*p32) - sizeof(compat_caddr_t))) ||
+ get_user(udata, &p32->data) ||
put_user(compat_ptr(udata), &p->data))
return -EFAULT;
- return proc_control(ps, p);
+ return proc_control(ps, p);
}
-static int proc_bulk_compat(struct dev_state *ps,
+static int proc_bulk_compat(struct usb_dev_state *ps,
struct usbdevfs_bulktransfer32 __user *p32)
{
- struct usbdevfs_bulktransfer __user *p;
- compat_uint_t n;
- compat_caddr_t addr;
+ struct usbdevfs_bulktransfer __user *p;
+ compat_uint_t n;
+ compat_caddr_t addr;
- p = compat_alloc_user_space(sizeof(*p));
+ p = compat_alloc_user_space(sizeof(*p));
- if (get_user(n, &p32->ep) || put_user(n, &p->ep) ||
- get_user(n, &p32->len) || put_user(n, &p->len) ||
- get_user(n, &p32->timeout) || put_user(n, &p->timeout) ||
- get_user(addr, &p32->data) || put_user(compat_ptr(addr), &p->data))
- return -EFAULT;
+ if (get_user(n, &p32->ep) || put_user(n, &p->ep) ||
+ get_user(n, &p32->len) || put_user(n, &p->len) ||
+ get_user(n, &p32->timeout) || put_user(n, &p->timeout) ||
+ get_user(addr, &p32->data) || put_user(compat_ptr(addr), &p->data))
+ return -EFAULT;
- return proc_bulk(ps, p);
+ return proc_bulk(ps, p);
}
-static int proc_disconnectsignal_compat(struct dev_state *ps, void __user *arg)
+static int proc_disconnectsignal_compat(struct usb_dev_state *ps, void __user *arg)
{
struct usbdevfs_disconnectsignal32 ds;
@@ -1579,7 +1798,7 @@ static int get_urb32(struct usbdevfs_urb *kurb,
return 0;
}
-static int proc_submiturb_compat(struct dev_state *ps, void __user *arg)
+static int proc_submiturb_compat(struct usb_dev_state *ps, void __user *arg)
{
struct usbdevfs_urb uurb;
@@ -1598,10 +1817,10 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
void __user *addr = as->userurb;
unsigned int i;
- if (as->userbuffer && urb->actual_length)
- if (copy_to_user(as->userbuffer, urb->transfer_buffer,
- urb->actual_length))
+ if (as->userbuffer && urb->actual_length) {
+ if (copy_urb_data_to_user(as->userbuffer, urb))
return -EFAULT;
+ }
if (put_user(as->status, &userurb->status))
return -EFAULT;
if (put_user(urb->actual_length, &userurb->actual_length))
@@ -1625,7 +1844,7 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
return 0;
}
-static int proc_reapurb_compat(struct dev_state *ps, void __user *arg)
+static int proc_reapurb_compat(struct usb_dev_state *ps, void __user *arg)
{
struct async *as = reap_as(ps);
if (as) {
@@ -1638,7 +1857,7 @@ static int proc_reapurb_compat(struct dev_state *ps, void __user *arg)
return -EIO;
}
-static int proc_reapurbnonblock_compat(struct dev_state *ps, void __user *arg)
+static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *arg)
{
int retval;
struct async *as;
@@ -1655,7 +1874,7 @@ static int proc_reapurbnonblock_compat(struct dev_state *ps, void __user *arg)
#endif
-static int proc_disconnectsignal(struct dev_state *ps, void __user *arg)
+static int proc_disconnectsignal(struct usb_dev_state *ps, void __user *arg)
{
struct usbdevfs_disconnectsignal ds;
@@ -1666,7 +1885,7 @@ static int proc_disconnectsignal(struct dev_state *ps, void __user *arg)
return 0;
}
-static int proc_claiminterface(struct dev_state *ps, void __user *arg)
+static int proc_claiminterface(struct usb_dev_state *ps, void __user *arg)
{
unsigned int ifnum;
@@ -1675,7 +1894,7 @@ static int proc_claiminterface(struct dev_state *ps, void __user *arg)
return claimintf(ps, ifnum);
}
-static int proc_releaseinterface(struct dev_state *ps, void __user *arg)
+static int proc_releaseinterface(struct usb_dev_state *ps, void __user *arg)
{
unsigned int ifnum;
int ret;
@@ -1688,7 +1907,7 @@ static int proc_releaseinterface(struct dev_state *ps, void __user *arg)
return 0;
}
-static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
+static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl)
{
int size;
void *buf = NULL;
@@ -1698,7 +1917,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)) {
@@ -1763,7 +1983,7 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
return retval;
}
-static int proc_ioctl_default(struct dev_state *ps, void __user *arg)
+static int proc_ioctl_default(struct usb_dev_state *ps, void __user *arg)
{
struct usbdevfs_ioctl ctrl;
@@ -1773,7 +1993,7 @@ static int proc_ioctl_default(struct dev_state *ps, void __user *arg)
}
#ifdef CONFIG_COMPAT
-static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg)
+static int proc_ioctl_compat(struct usb_dev_state *ps, compat_uptr_t arg)
{
struct usbdevfs_ioctl32 __user *uioc;
struct usbdevfs_ioctl ctrl;
@@ -1791,7 +2011,7 @@ static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg)
}
#endif
-static int proc_claim_port(struct dev_state *ps, void __user *arg)
+static int proc_claim_port(struct usb_dev_state *ps, void __user *arg)
{
unsigned portnum;
int rc;
@@ -1805,7 +2025,7 @@ static int proc_claim_port(struct dev_state *ps, void __user *arg)
return rc;
}
-static int proc_release_port(struct dev_state *ps, void __user *arg)
+static int proc_release_port(struct usb_dev_state *ps, void __user *arg)
{
unsigned portnum;
@@ -1814,6 +2034,93 @@ static int proc_release_port(struct dev_state *ps, void __user *arg)
return usb_hub_release_port(ps->dev, portnum, ps);
}
+static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg)
+{
+ __u32 caps;
+
+ caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM;
+ if (!ps->dev->bus->no_stop_on_short)
+ caps |= USBDEVFS_CAP_BULK_CONTINUATION;
+ if (ps->dev->bus->sg_tablesize)
+ caps |= USBDEVFS_CAP_BULK_SCATTER_GATHER;
+
+ if (put_user(caps, (__u32 __user *)arg))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int proc_disconnect_claim(struct usb_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);
+}
+
+static int proc_alloc_streams(struct usb_dev_state *ps, void __user *arg)
+{
+ unsigned num_streams, num_eps;
+ struct usb_host_endpoint **eps;
+ struct usb_interface *intf;
+ int r;
+
+ r = parse_usbdevfs_streams(ps, arg, &num_streams, &num_eps,
+ &eps, &intf);
+ if (r)
+ return r;
+
+ destroy_async_on_interface(ps,
+ intf->altsetting[0].desc.bInterfaceNumber);
+
+ r = usb_alloc_streams(intf, eps, num_eps, num_streams, GFP_KERNEL);
+ kfree(eps);
+ return r;
+}
+
+static int proc_free_streams(struct usb_dev_state *ps, void __user *arg)
+{
+ unsigned num_eps;
+ struct usb_host_endpoint **eps;
+ struct usb_interface *intf;
+ int r;
+
+ r = parse_usbdevfs_streams(ps, arg, NULL, &num_eps, &eps, &intf);
+ if (r)
+ return r;
+
+ destroy_async_on_interface(ps,
+ intf->altsetting[0].desc.bInterfaceNumber);
+
+ r = usb_free_streams(intf, eps, num_eps, GFP_KERNEL);
+ kfree(eps);
+ return r;
+}
+
/*
* NOTE: All requests here that have interface numbers as parameters
* are assuming that somehow the configuration has been prevented from
@@ -1822,8 +2129,8 @@ static int proc_release_port(struct dev_state *ps, void __user *arg)
static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
void __user *p)
{
- struct dev_state *ps = file->private_data;
- struct inode *inode = file->f_path.dentry->d_inode;
+ struct usb_dev_state *ps = file->private_data;
+ struct inode *inode = file_inode(file);
struct usb_device *dev = ps->dev;
int ret = -ENOTTY;
@@ -1984,6 +2291,18 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__);
ret = proc_release_port(ps, p);
break;
+ case USBDEVFS_GET_CAPABILITIES:
+ ret = proc_get_capabilities(ps, p);
+ break;
+ case USBDEVFS_DISCONNECT_CLAIM:
+ ret = proc_disconnect_claim(ps, p);
+ break;
+ case USBDEVFS_ALLOC_STREAMS:
+ ret = proc_alloc_streams(ps, p);
+ break;
+ case USBDEVFS_FREE_STREAMS:
+ ret = proc_free_streams(ps, p);
+ break;
}
usb_unlock_device(dev);
if (ret >= 0)
@@ -2017,7 +2336,7 @@ static long usbdev_compat_ioctl(struct file *file, unsigned int cmd,
static unsigned int usbdev_poll(struct file *file,
struct poll_table_struct *wait)
{
- struct dev_state *ps = file->private_data;
+ struct usb_dev_state *ps = file->private_data;
unsigned int mask = 0;
poll_wait(file, &ps->wait, wait);
@@ -2043,11 +2362,11 @@ const struct file_operations usbdev_file_operations = {
static void usbdev_remove(struct usb_device *udev)
{
- struct dev_state *ps;
+ struct usb_dev_state *ps;
struct siginfo sinfo;
while (!list_empty(&udev->filelist)) {
- ps = list_entry(udev->filelist.next, struct dev_state, list);
+ ps = list_entry(udev->filelist.next, struct usb_dev_state, list);
destroy_all_async(ps);
wake_up_all(&ps->wait);
list_del_init(&ps->list);
@@ -2062,44 +2381,13 @@ static void usbdev_remove(struct usb_device *udev)
}
}
-#ifdef CONFIG_USB_DEVICE_CLASS
-static struct class *usb_classdev_class;
-
-static int usb_classdev_add(struct usb_device *dev)
-{
- struct device *cldev;
-
- cldev = device_create(usb_classdev_class, &dev->dev, dev->dev.devt,
- NULL, "usbdev%d.%d", dev->bus->busnum,
- dev->devnum);
- if (IS_ERR(cldev))
- return PTR_ERR(cldev);
- dev->usb_classdev = cldev;
- return 0;
-}
-
-static void usb_classdev_remove(struct usb_device *dev)
-{
- if (dev->usb_classdev)
- device_unregister(dev->usb_classdev);
-}
-
-#else
-#define usb_classdev_add(dev) 0
-#define usb_classdev_remove(dev) do {} while (0)
-
-#endif
-
static int usbdev_notify(struct notifier_block *self,
unsigned long action, void *dev)
{
switch (action) {
case USB_DEVICE_ADD:
- if (usb_classdev_add(dev))
- return NOTIFY_BAD;
break;
case USB_DEVICE_REMOVE:
- usb_classdev_remove(dev);
usbdev_remove(dev);
break;
}
@@ -2129,21 +2417,6 @@ int __init usb_devio_init(void)
USB_DEVICE_MAJOR);
goto error_cdev;
}
-#ifdef CONFIG_USB_DEVICE_CLASS
- usb_classdev_class = class_create(THIS_MODULE, "usb_device");
- if (IS_ERR(usb_classdev_class)) {
- printk(KERN_ERR "Unable to register usb_device class\n");
- retval = PTR_ERR(usb_classdev_class);
- cdev_del(&usb_device_cdev);
- usb_classdev_class = NULL;
- goto out;
- }
- /* devices of this class shadow the major:minor of their parent
- * device, so clear ->dev_kobj to prevent adding duplicate entries
- * to /sys/dev
- */
- usb_classdev_class->dev_kobj = NULL;
-#endif
usb_register_notify(&usbdev_nb);
out:
return retval;
@@ -2156,9 +2429,6 @@ error_cdev:
void usb_devio_cleanup(void)
{
usb_unregister_notify(&usbdev_nb);
-#ifdef CONFIG_USB_DEVICE_CLASS
- class_destroy(usb_classdev_class);
-#endif
cdev_del(&usb_device_cdev);
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
}
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index d40ff956881..4aeb10034de 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -32,13 +32,12 @@
#include "usb.h"
-#ifdef CONFIG_HOTPLUG
-
/*
* Adds a new dynamic USBdevice ID to this driver,
* and cause the driver to probe for all devices again.
*/
ssize_t usb_store_new_id(struct usb_dynids *dynids,
+ const struct usb_device_id *id_table,
struct device_driver *driver,
const char *buf, size_t count)
{
@@ -46,11 +45,12 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids,
u32 idVendor = 0;
u32 idProduct = 0;
unsigned int bInterfaceClass = 0;
+ u32 refVendor, refProduct;
int fields = 0;
int retval = 0;
- fields = sscanf(buf, "%x %x %x", &idVendor, &idProduct,
- &bInterfaceClass);
+ fields = sscanf(buf, "%x %x %x %x %x", &idVendor, &idProduct,
+ &bInterfaceClass, &refVendor, &refProduct);
if (fields < 2)
return -EINVAL;
@@ -62,52 +62,96 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids,
dynid->id.idVendor = idVendor;
dynid->id.idProduct = idProduct;
dynid->id.match_flags = USB_DEVICE_ID_MATCH_DEVICE;
- if (fields == 3) {
+ if (fields > 2 && bInterfaceClass) {
+ if (bInterfaceClass > 255) {
+ retval = -EINVAL;
+ goto fail;
+ }
+
dynid->id.bInterfaceClass = (u8)bInterfaceClass;
dynid->id.match_flags |= USB_DEVICE_ID_MATCH_INT_CLASS;
}
+ if (fields > 4) {
+ const struct usb_device_id *id = id_table;
+
+ if (!id) {
+ retval = -ENODEV;
+ goto fail;
+ }
+
+ for (; id->match_flags; id++)
+ if (id->idVendor == refVendor && id->idProduct == refProduct)
+ break;
+
+ if (id->match_flags) {
+ dynid->id.driver_info = id->driver_info;
+ } else {
+ retval = -ENODEV;
+ goto fail;
+ }
+ }
+
spin_lock(&dynids->lock);
list_add_tail(&dynid->node, &dynids->list);
spin_unlock(&dynids->lock);
- if (get_driver(driver)) {
- retval = driver_attach(driver);
- put_driver(driver);
- }
+ retval = driver_attach(driver);
if (retval)
return retval;
return count;
+
+fail:
+ kfree(dynid);
+ return retval;
}
EXPORT_SYMBOL_GPL(usb_store_new_id);
-static ssize_t store_new_id(struct device_driver *driver,
+ssize_t usb_show_dynids(struct usb_dynids *dynids, char *buf)
+{
+ struct usb_dynid *dynid;
+ size_t count = 0;
+
+ list_for_each_entry(dynid, &dynids->list, node)
+ if (dynid->id.bInterfaceClass != 0)
+ count += scnprintf(&buf[count], PAGE_SIZE - count, "%04x %04x %02x\n",
+ dynid->id.idVendor, dynid->id.idProduct,
+ dynid->id.bInterfaceClass);
+ else
+ count += scnprintf(&buf[count], PAGE_SIZE - count, "%04x %04x\n",
+ dynid->id.idVendor, dynid->id.idProduct);
+ return count;
+}
+EXPORT_SYMBOL_GPL(usb_show_dynids);
+
+static ssize_t new_id_show(struct device_driver *driver, char *buf)
+{
+ struct usb_driver *usb_drv = to_usb_driver(driver);
+
+ return usb_show_dynids(&usb_drv->dynids, buf);
+}
+
+static ssize_t new_id_store(struct device_driver *driver,
const char *buf, size_t count)
{
struct usb_driver *usb_drv = to_usb_driver(driver);
- return usb_store_new_id(&usb_drv->dynids, driver, buf, count);
+ return usb_store_new_id(&usb_drv->dynids, usb_drv->id_table, driver, buf, count);
}
-static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
+static DRIVER_ATTR_RW(new_id);
-/**
- * store_remove_id - remove a USB device ID from this driver
- * @driver: target device driver
- * @buf: buffer for scanning device ID data
- * @count: input size
- *
- * Removes a dynamic usb device ID from this driver.
+/*
+ * Remove a USB device ID from this driver
*/
-static ssize_t
-store_remove_id(struct device_driver *driver, const char *buf, size_t count)
+static ssize_t remove_id_store(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)
@@ -120,55 +164,52 @@ 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_IWUSR, NULL, store_remove_id);
-static int usb_create_newid_file(struct usb_driver *usb_drv)
+static ssize_t remove_id_show(struct device_driver *driver, char *buf)
+{
+ return new_id_show(driver, buf);
+}
+static DRIVER_ATTR_RW(remove_id);
+
+static int usb_create_newid_files(struct usb_driver *usb_drv)
{
int error = 0;
if (usb_drv->no_dynamic_id)
goto exit;
- if (usb_drv->probe != NULL)
+ if (usb_drv->probe != NULL) {
error = driver_create_file(&usb_drv->drvwrap.driver,
&driver_attr_new_id);
+ if (error == 0) {
+ error = driver_create_file(&usb_drv->drvwrap.driver,
+ &driver_attr_remove_id);
+ if (error)
+ driver_remove_file(&usb_drv->drvwrap.driver,
+ &driver_attr_new_id);
+ }
+ }
exit:
return error;
}
-static void usb_remove_newid_file(struct usb_driver *usb_drv)
+static void usb_remove_newid_files(struct usb_driver *usb_drv)
{
if (usb_drv->no_dynamic_id)
return;
- if (usb_drv->probe != NULL)
+ if (usb_drv->probe != NULL) {
driver_remove_file(&usb_drv->drvwrap.driver,
- &driver_attr_new_id);
-}
-
-static int
-usb_create_removeid_file(struct usb_driver *drv)
-{
- int error = 0;
- if (drv->probe != NULL)
- error = driver_create_file(&drv->drvwrap.driver,
&driver_attr_remove_id);
- return error;
-}
-
-static void usb_remove_removeid_file(struct usb_driver *drv)
-{
- driver_remove_file(&drv->drvwrap.driver, &driver_attr_remove_id);
+ driver_remove_file(&usb_drv->drvwrap.driver,
+ &driver_attr_new_id);
+ }
}
static void usb_free_dynids(struct usb_driver *usb_drv)
@@ -182,30 +223,6 @@ static void usb_free_dynids(struct usb_driver *usb_drv)
}
spin_unlock(&usb_drv->dynids.lock);
}
-#else
-static inline int usb_create_newid_file(struct usb_driver *usb_drv)
-{
- return 0;
-}
-
-static void usb_remove_newid_file(struct usb_driver *usb_drv)
-{
-}
-
-static int
-usb_create_removeid_file(struct usb_driver *drv)
-{
- return 0;
-}
-
-static void usb_remove_removeid_file(struct usb_driver *drv)
-{
-}
-
-static inline void usb_free_dynids(struct usb_driver *usb_drv)
-{
-}
-#endif
static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *intf,
struct usb_driver *drv)
@@ -236,7 +253,7 @@ static int usb_probe_device(struct device *dev)
/* TODO: Add real matching code */
/* The device should always appear to be in use
- * unless the driver suports autosuspend.
+ * unless the driver supports autosuspend.
*/
if (!udriver->supports_autosuspend)
error = usb_autoresume_device(udev);
@@ -281,6 +298,7 @@ static int usb_probe_interface(struct device *dev)
struct usb_device *udev = interface_to_usbdev(intf);
const struct usb_device_id *id;
int error = -ENODEV;
+ int lpm_disable_error;
dev_dbg(dev, "%s\n", __func__);
@@ -294,9 +312,9 @@ static int usb_probe_interface(struct device *dev)
return error;
}
- id = usb_match_id(intf, driver->id_table);
+ id = usb_match_dynamic_id(intf, driver);
if (!id)
- id = usb_match_dynamic_id(intf, driver);
+ id = usb_match_id(intf, driver->id_table);
if (!id)
return error;
@@ -317,6 +335,25 @@ static int usb_probe_interface(struct device *dev)
if (driver->supports_autosuspend)
pm_runtime_enable(dev);
+ /* If the new driver doesn't allow hub-initiated LPM, and we can't
+ * disable hub-initiated LPM, then fail the probe.
+ *
+ * Otherwise, leaving LPM enabled should be harmless, because the
+ * endpoint intervals should remain the same, and the U1/U2 timeouts
+ * should remain the same.
+ *
+ * If we need to install alt setting 0 before probe, or another alt
+ * setting during probe, that should also be fine. usb_set_interface()
+ * will attempt to disable LPM, and fail if it can't disable it.
+ */
+ lpm_disable_error = usb_unlocked_disable_lpm(udev);
+ if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
+ dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.",
+ __func__, driver->name);
+ error = lpm_disable_error;
+ goto err;
+ }
+
/* Carry out a deferred switch to altsetting 0 */
if (intf->needs_altsetting0) {
error = usb_set_interface(udev, intf->altsetting[0].
@@ -331,14 +368,24 @@ static int usb_probe_interface(struct device *dev)
goto err;
intf->condition = USB_INTERFACE_BOUND;
+
+ /* If the LPM disable succeeded, balance the ref counts. */
+ if (!lpm_disable_error)
+ usb_unlocked_enable_lpm(udev);
+
usb_autosuspend_device(udev);
return error;
err:
+ usb_set_intfdata(intf, NULL);
intf->needs_remote_wakeup = 0;
intf->condition = USB_INTERFACE_UNBOUND;
usb_cancel_queued_reset(intf);
+ /* If the LPM disable succeeded, balance the ref counts. */
+ if (!lpm_disable_error)
+ usb_unlocked_enable_lpm(udev);
+
/* Unbound interfaces are always runtime-PM-disabled and -suspended */
if (driver->supports_autosuspend)
pm_runtime_disable(dev);
@@ -353,8 +400,9 @@ static int usb_unbind_interface(struct device *dev)
{
struct usb_driver *driver = to_usb_driver(dev->driver);
struct usb_interface *intf = to_usb_interface(dev);
+ struct usb_host_endpoint *ep, **eps = NULL;
struct usb_device *udev;
- int error, r;
+ int i, j, error, r, lpm_disable_error;
intf->condition = USB_INTERFACE_UNBINDING;
@@ -362,6 +410,13 @@ static int usb_unbind_interface(struct device *dev)
udev = interface_to_usbdev(intf);
error = usb_autoresume_device(udev);
+ /* Hub-initiated LPM policy may change, so attempt to disable LPM until
+ * the driver is unbound. If LPM isn't disabled, that's fine because it
+ * wouldn't be enabled unless all the bound interfaces supported
+ * hub-initiated LPM.
+ */
+ lpm_disable_error = usb_unlocked_disable_lpm(udev);
+
/* Terminate all URBs for this interface unless the driver
* supports "soft" unbinding.
*/
@@ -371,6 +426,26 @@ static int usb_unbind_interface(struct device *dev)
driver->disconnect(intf);
usb_cancel_queued_reset(intf);
+ /* Free streams */
+ for (i = 0, j = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+ ep = &intf->cur_altsetting->endpoint[i];
+ if (ep->streams == 0)
+ continue;
+ if (j == 0) {
+ eps = kmalloc(USB_MAXENDPOINTS * sizeof(void *),
+ GFP_KERNEL);
+ if (!eps) {
+ dev_warn(dev, "oom, leaking streams\n");
+ break;
+ }
+ }
+ eps[j++] = ep;
+ }
+ if (j) {
+ usb_free_streams(intf, eps, j, GFP_KERNEL);
+ kfree(eps);
+ }
+
/* Reset other interface state.
* We cannot do a Set-Interface if the device is suspended or
* if it is prepared for a system sleep (since installing a new
@@ -395,6 +470,10 @@ static int usb_unbind_interface(struct device *dev)
intf->condition = USB_INTERFACE_UNBOUND;
intf->needs_remote_wakeup = 0;
+ /* Attempt to re-enable USB3 LPM, if the disable succeeded. */
+ if (!lpm_disable_error)
+ usb_unlocked_enable_lpm(udev);
+
/* Unbound interfaces are always runtime-PM-disabled and -suspended */
if (driver->supports_autosuspend)
pm_runtime_disable(dev);
@@ -430,22 +509,36 @@ static int usb_unbind_interface(struct device *dev)
* Callers must own the device lock, so driver probe() entries don't need
* extra locking, but other call contexts may need to explicitly claim that
* lock.
+ *
+ * Return: 0 on success.
*/
int usb_driver_claim_interface(struct usb_driver *driver,
struct usb_interface *iface, void *priv)
{
struct device *dev = &iface->dev;
+ struct usb_device *udev;
int retval = 0;
+ int lpm_disable_error;
if (dev->driver)
return -EBUSY;
+ udev = interface_to_usbdev(iface);
+
dev->driver = &driver->drvwrap.driver;
usb_set_intfdata(iface, priv);
iface->needs_binding = 0;
iface->condition = USB_INTERFACE_BOUND;
+ /* Disable LPM until this driver is bound. */
+ lpm_disable_error = usb_unlocked_disable_lpm(udev);
+ if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
+ dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n.",
+ __func__, driver->name);
+ return -ENOMEM;
+ }
+
/* Claimed interfaces are initially inactive (suspended) and
* runtime-PM-enabled, but only if the driver has autosuspend
* support. Otherwise they are marked active, to prevent the
@@ -464,6 +557,10 @@ int usb_driver_claim_interface(struct usb_driver *driver,
if (device_is_registered(dev))
retval = device_bind_driver(dev);
+ /* Attempt to re-enable USB3 LPM, if the disable was successful. */
+ if (!lpm_disable_error)
+ usb_unlocked_enable_lpm(udev);
+
return retval;
}
EXPORT_SYMBOL_GPL(usb_driver_claim_interface);
@@ -547,30 +644,19 @@ int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
}
/* returns 0 if no match, 1 if match */
-int usb_match_one_id(struct usb_interface *interface,
- const struct usb_device_id *id)
+int usb_match_one_id_intf(struct usb_device *dev,
+ struct usb_host_interface *intf,
+ const struct usb_device_id *id)
{
- struct usb_host_interface *intf;
- struct usb_device *dev;
-
- /* proc_connectinfo in devio.c may call us with id == NULL. */
- if (id == NULL)
- return 0;
-
- intf = interface->cur_altsetting;
- dev = interface_to_usbdev(interface);
-
- if (!usb_match_device(dev, id))
- return 0;
-
- /* The interface class, subclass, and protocol should never be
+ /* The interface class, subclass, protocol and number should never be
* checked for a match if the device class is Vendor Specific,
* unless the match record specifies the Vendor ID. */
if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC &&
!(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
(id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |
USB_DEVICE_ID_MATCH_INT_SUBCLASS |
- USB_DEVICE_ID_MATCH_INT_PROTOCOL)))
+ USB_DEVICE_ID_MATCH_INT_PROTOCOL |
+ USB_DEVICE_ID_MATCH_INT_NUMBER)))
return 0;
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
@@ -585,8 +671,32 @@ int usb_match_one_id(struct usb_interface *interface,
(id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
return 0;
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) &&
+ (id->bInterfaceNumber != intf->desc.bInterfaceNumber))
+ return 0;
+
return 1;
}
+
+/* returns 0 if no match, 1 if match */
+int usb_match_one_id(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_host_interface *intf;
+ struct usb_device *dev;
+
+ /* proc_connectinfo in devio.c may call us with id == NULL. */
+ if (id == NULL)
+ return 0;
+
+ intf = interface->cur_altsetting;
+ dev = interface_to_usbdev(interface);
+
+ if (!usb_match_device(dev, id))
+ return 0;
+
+ return usb_match_one_id_intf(dev, intf, id);
+}
EXPORT_SYMBOL_GPL(usb_match_one_id);
/**
@@ -602,6 +712,8 @@ EXPORT_SYMBOL_GPL(usb_match_one_id);
* These device tables are exported with MODULE_DEVICE_TABLE, through
* modutils, to support the driver loading functionality of USB hotplugging.
*
+ * Return: The first matching usb_device_id, or %NULL.
+ *
* What Matches:
*
* The "match_flags" element in a usb_device_id controls which
@@ -718,7 +830,6 @@ static int usb_device_match(struct device *dev, struct device_driver *drv)
return 0;
}
-#ifdef CONFIG_HOTPLUG
static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct usb_device *usb_dev;
@@ -743,16 +854,6 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
return -ENODEV;
}
-#ifdef CONFIG_USB_DEVICEFS
- /* If this is available, userspace programs can directly read
- * all the device descriptors we don't tell them about. Or
- * act as usermode drivers.
- */
- if (add_uevent_var(env, "DEVICE=/proc/bus/usb/%03d/%03d",
- usb_dev->bus->busnum, usb_dev->devnum))
- return -ENOMEM;
-#endif
-
/* per-device configurations are common */
if (add_uevent_var(env, "PRODUCT=%x/%x/%x",
le16_to_cpu(usb_dev->descriptor.idVendor),
@@ -770,14 +871,6 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
return 0;
}
-#else
-
-static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
-{
- return -ENODEV;
-}
-#endif /* CONFIG_HOTPLUG */
-
/**
* usb_register_device_driver - register a USB device (not interface) driver
* @new_udriver: USB operations for the device driver
@@ -786,7 +879,8 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
* Registers a USB device driver with the USB core. The list of
* unattached devices will be rescanned whenever a new driver is
* added, allowing the new driver to attach to any recognized devices.
- * Returns a negative error code on failure and 0 on success.
+ *
+ * Return: A negative error code on failure and 0 on success.
*/
int usb_register_device_driver(struct usb_device_driver *new_udriver,
struct module *owner)
@@ -797,7 +891,7 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver,
return -ENODEV;
new_udriver->drvwrap.for_devices = 1;
- new_udriver->drvwrap.driver.name = (char *) new_udriver->name;
+ new_udriver->drvwrap.driver.name = new_udriver->name;
new_udriver->drvwrap.driver.bus = &usb_bus_type;
new_udriver->drvwrap.driver.probe = usb_probe_device;
new_udriver->drvwrap.driver.remove = usb_unbind_device;
@@ -805,15 +899,13 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver,
retval = driver_register(&new_udriver->drvwrap.driver);
- if (!retval) {
+ if (!retval)
pr_info("%s: registered new device driver %s\n",
usbcore_name, new_udriver->name);
- usbfs_update_special();
- } else {
+ else
printk(KERN_ERR "%s: error %d registering device "
" driver %s\n",
usbcore_name, retval, new_udriver->name);
- }
return retval;
}
@@ -832,7 +924,6 @@ void usb_deregister_device_driver(struct usb_device_driver *udriver)
usbcore_name, udriver->name);
driver_unregister(&udriver->drvwrap.driver);
- usbfs_update_special();
}
EXPORT_SYMBOL_GPL(usb_deregister_device_driver);
@@ -845,7 +936,8 @@ EXPORT_SYMBOL_GPL(usb_deregister_device_driver);
* Registers a USB interface driver with the USB core. The list of
* unattached interfaces will be rescanned whenever a new driver is
* added, allowing the new driver to attach to any recognized interfaces.
- * Returns a negative error code on failure and 0 on success.
+ *
+ * Return: A negative error code on failure and 0 on success.
*
* NOTE: if you want your driver to use the USB major number, you must call
* usb_register_dev() to enable that functionality. This function no longer
@@ -860,7 +952,7 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
return -ENODEV;
new_driver->drvwrap.for_devices = 0;
- new_driver->drvwrap.driver.name = (char *) new_driver->name;
+ new_driver->drvwrap.driver.name = new_driver->name;
new_driver->drvwrap.driver.bus = &usb_bus_type;
new_driver->drvwrap.driver.probe = usb_probe_interface;
new_driver->drvwrap.driver.remove = usb_unbind_interface;
@@ -873,24 +965,16 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
if (retval)
goto out;
- usbfs_update_special();
-
- retval = usb_create_newid_file(new_driver);
+ retval = usb_create_newid_files(new_driver);
if (retval)
goto out_newid;
- retval = usb_create_removeid_file(new_driver);
- if (retval)
- goto out_removeid;
-
pr_info("%s: registered new interface driver %s\n",
usbcore_name, new_driver->name);
out:
return retval;
-out_removeid:
- usb_remove_newid_file(new_driver);
out_newid:
driver_unregister(&new_driver->drvwrap.driver);
@@ -917,12 +1001,9 @@ void usb_deregister(struct usb_driver *driver)
pr_info("%s: deregistering interface driver %s\n",
usbcore_name, driver->name);
- usb_remove_removeid_file(driver);
- usb_remove_newid_file(driver);
- usb_free_dynids(driver);
+ usb_remove_newid_files(driver);
driver_unregister(&driver->drvwrap.driver);
-
- usbfs_update_special();
+ usb_free_dynids(driver);
}
EXPORT_SYMBOL_GPL(usb_deregister);
@@ -930,8 +1011,7 @@ EXPORT_SYMBOL_GPL(usb_deregister);
* it doesn't support pre_reset/post_reset/reset_resume or
* because it doesn't support suspend/resume.
*
- * The caller must hold @intf's device's lock, but not its pm_mutex
- * and not @intf->dev.sem.
+ * The caller must hold @intf's device's lock, but not @intf's lock.
*/
void usb_forced_unbind_intf(struct usb_interface *intf)
{
@@ -944,27 +1024,43 @@ void usb_forced_unbind_intf(struct usb_interface *intf)
intf->needs_binding = 1;
}
+/*
+ * Unbind drivers for @udev's marked interfaces. These interfaces have
+ * the needs_binding flag set, for example by usb_resume_interface().
+ *
+ * The caller must hold @udev's device lock.
+ */
+static void unbind_marked_interfaces(struct usb_device *udev)
+{
+ struct usb_host_config *config;
+ int i;
+ struct usb_interface *intf;
+
+ config = udev->actconfig;
+ if (config) {
+ for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+ intf = config->interface[i];
+ if (intf->dev.driver && intf->needs_binding)
+ usb_forced_unbind_intf(intf);
+ }
+ }
+}
+
/* Delayed forced unbinding of a USB interface driver and scan
* for rebinding.
*
- * The caller must hold @intf's device's lock, but not its pm_mutex
- * and not @intf->dev.sem.
+ * The caller must hold @intf's device's lock, but not @intf's lock.
*
* Note: Rebinds will be skipped if a system sleep transition is in
* progress and the PM "complete" callback hasn't occurred yet.
*/
-void usb_rebind_intf(struct usb_interface *intf)
+static void usb_rebind_intf(struct usb_interface *intf)
{
int rc;
/* Delayed unbind of an existing driver */
- if (intf->dev.driver) {
- struct usb_driver *driver =
- to_usb_driver(intf->dev.driver);
-
- dev_dbg(&intf->dev, "forced unbind\n");
- usb_driver_release_interface(driver, intf);
- }
+ if (intf->dev.driver)
+ usb_forced_unbind_intf(intf);
/* Try to rebind the interface */
if (!intf->dev.power.is_prepared) {
@@ -975,17 +1071,50 @@ void usb_rebind_intf(struct usb_interface *intf)
}
}
-#ifdef CONFIG_PM
+/*
+ * Rebind drivers to @udev's marked interfaces. These interfaces have
+ * the needs_binding flag set.
+ *
+ * The caller must hold @udev's device lock.
+ */
+static void rebind_marked_interfaces(struct usb_device *udev)
+{
+ struct usb_host_config *config;
+ int i;
+ struct usb_interface *intf;
+
+ config = udev->actconfig;
+ if (config) {
+ for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+ intf = config->interface[i];
+ if (intf->needs_binding)
+ usb_rebind_intf(intf);
+ }
+ }
+}
+
+/*
+ * Unbind all of @udev's marked interfaces and then rebind all of them.
+ * This ordering is necessary because some drivers claim several interfaces
+ * when they are first probed.
+ *
+ * The caller must hold @udev's device lock.
+ */
+void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev)
+{
+ unbind_marked_interfaces(udev);
+ rebind_marked_interfaces(udev);
+}
-#define DO_UNBIND 0
-#define DO_REBIND 1
+#ifdef CONFIG_PM
-/* Unbind drivers for @udev's interfaces that don't support suspend/resume,
- * or rebind interfaces that have been unbound, according to @action.
+/* Unbind drivers for @udev's interfaces that don't support suspend/resume
+ * There is no check for reset_resume here because it can be determined
+ * only during resume whether reset_resume is needed.
*
* The caller must hold @udev's device lock.
*/
-static void do_unbind_rebind(struct usb_device *udev, int action)
+static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
{
struct usb_host_config *config;
int i;
@@ -996,18 +1125,11 @@ static void do_unbind_rebind(struct usb_device *udev, int action)
if (config) {
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
intf = config->interface[i];
- switch (action) {
- case DO_UNBIND:
- if (intf->dev.driver) {
- drv = to_usb_driver(intf->dev.driver);
- if (!drv->suspend || !drv->resume)
- usb_forced_unbind_intf(intf);
- }
- break;
- case DO_REBIND:
- if (intf->needs_binding)
- usb_rebind_intf(intf);
- break;
+
+ if (intf->dev.driver) {
+ drv = to_usb_driver(intf->dev.driver);
+ if (!drv->suspend || !drv->resume)
+ usb_forced_unbind_intf(intf);
}
}
}
@@ -1127,8 +1249,8 @@ static int usb_resume_interface(struct usb_device *udev,
"reset_resume", status);
} else {
intf->needs_binding = 1;
- dev_warn(&intf->dev, "no %s for driver %s?\n",
- "reset_resume", driver->name);
+ dev_dbg(&intf->dev, "no reset_resume for driver %s?\n",
+ driver->name);
}
} else {
status = driver->resume(intf);
@@ -1150,9 +1272,14 @@ done:
*
* This is the central routine for suspending USB devices. It calls the
* suspend methods for all the interface drivers in @udev and then calls
- * the suspend method for @udev itself. If an error occurs at any stage,
- * all the interfaces which were suspended are resumed so that they remain
- * in the same state as the device.
+ * the suspend method for @udev itself. When the routine is called in
+ * autosuspend, if an error occurs at any stage, all the interfaces
+ * which were suspended are resumed so that they remain in the same
+ * state as the device, but when called from system sleep, all error
+ * from suspend methods of interfaces and the non-root-hub device itself
+ * are simply ignored, so all suspended interfaces are only resumed
+ * to the device's state when @udev is root-hub and its suspend method
+ * returns failure.
*
* Autosuspend requests originating from a child device or an interface
* driver may be made without the protection of @udev's device lock, but
@@ -1162,6 +1289,8 @@ done:
* unpredictable times.
*
* This routine can run only in process context.
+ *
+ * Return: 0 if the suspend succeeded.
*/
static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
{
@@ -1190,17 +1319,24 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
if (status == 0) {
status = usb_suspend_device(udev, msg);
- /* Again, ignore errors during system sleep transitions */
- if (!PMSG_IS_AUTO(msg))
+ /*
+ * Ignore errors from non-root-hub devices during
+ * system sleep transitions. For the most part,
+ * these devices should go to low power anyway when
+ * the entire bus is suspended.
+ */
+ if (udev->parent && !PMSG_IS_AUTO(msg))
status = 0;
}
/* If the suspend failed, resume interfaces that did get suspended */
if (status != 0) {
- msg.event ^= (PM_EVENT_SUSPEND | PM_EVENT_RESUME);
- while (++i < n) {
- intf = udev->actconfig->interface[i];
- usb_resume_interface(udev, intf, msg, 0);
+ if (udev->actconfig) {
+ msg.event ^= (PM_EVENT_SUSPEND | PM_EVENT_RESUME);
+ while (++i < n) {
+ intf = udev->actconfig->interface[i];
+ usb_resume_interface(udev, intf, msg, 0);
+ }
}
/* If the suspend succeeded then prevent any more URB submissions
@@ -1236,6 +1372,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
* unpredictable times.
*
* This routine can run only in process context.
+ *
+ * Return: 0 on success.
*/
static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
{
@@ -1302,35 +1440,48 @@ int usb_suspend(struct device *dev, pm_message_t msg)
{
struct usb_device *udev = to_usb_device(dev);
- do_unbind_rebind(udev, DO_UNBIND);
+ unbind_no_pm_drivers_interfaces(udev);
+
+ /* From now on we are sure all drivers support suspend/resume
+ * but not necessarily reset_resume()
+ * so we may still need to unbind and rebind upon resume
+ */
choose_wakeup(udev, msg);
return usb_suspend_both(udev, msg);
}
/* The device lock is held by the PM core */
+int usb_resume_complete(struct device *dev)
+{
+ struct usb_device *udev = to_usb_device(dev);
+
+ /* For PM complete calls, all we do is rebind interfaces
+ * whose needs_binding flag is set
+ */
+ if (udev->state != USB_STATE_NOTATTACHED)
+ rebind_marked_interfaces(udev);
+ return 0;
+}
+
+/* The device lock is held by the PM core */
int usb_resume(struct device *dev, pm_message_t msg)
{
struct usb_device *udev = to_usb_device(dev);
int status;
- /* For PM complete calls, all we do is rebind interfaces */
- if (msg.event == PM_EVENT_ON) {
- if (udev->state != USB_STATE_NOTATTACHED)
- do_unbind_rebind(udev, DO_REBIND);
- status = 0;
-
- /* For all other calls, take the device back to full power and
+ /* For all calls, take the device back to full power and
* tell the PM core in case it was autosuspended previously.
- * Unbind the interfaces that will need rebinding later.
+ * Unbind the interfaces that will need rebinding later,
+ * because they fail to support reset_resume.
+ * (This can't be done in usb_resume_interface()
+ * above because it doesn't own the right set of locks.)
*/
- } else {
- status = usb_resume_both(udev, msg);
- if (status == 0) {
- pm_runtime_disable(dev);
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
- do_unbind_rebind(udev, DO_REBIND);
- }
+ status = usb_resume_both(udev, msg);
+ if (status == 0) {
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ unbind_marked_interfaces(udev);
}
/* Avoid PM error messages for devices disconnected while suspended
@@ -1343,7 +1494,7 @@ int usb_resume(struct device *dev, pm_message_t msg)
#endif /* CONFIG_PM */
-#ifdef CONFIG_USB_SUSPEND
+#ifdef CONFIG_PM_RUNTIME
/**
* usb_enable_autosuspend - allow a USB device to be autosuspended
@@ -1420,6 +1571,8 @@ void usb_autosuspend_device(struct usb_device *udev)
* The caller must hold @udev's device lock.
*
* This routine can run only in process context.
+ *
+ * Return: 0 on success. A negative error code otherwise.
*/
int usb_autoresume_device(struct usb_device *udev)
{
@@ -1529,6 +1682,8 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface_no_suspend);
* However if the autoresume fails then the counter is re-decremented.
*
* This routine can run only in process context.
+ *
+ * Return: 0 on success.
*/
int usb_autopm_get_interface(struct usb_interface *intf)
{
@@ -1562,6 +1717,8 @@ EXPORT_SYMBOL_GPL(usb_autopm_get_interface);
* resumed.
*
* This routine can run in atomic context.
+ *
+ * Return: 0 on success. A negative error code otherwise.
*/
int usb_autopm_get_interface_async(struct usb_interface *intf)
{
@@ -1665,10 +1822,13 @@ int usb_runtime_suspend(struct device *dev)
if (status == -EAGAIN || status == -EBUSY)
usb_mark_last_busy(udev);
- /* The PM core reacts badly unless the return code is 0,
- * -EAGAIN, or -EBUSY, so always return -EBUSY on an error.
+ /*
+ * The PM core reacts badly unless the return code is 0,
+ * -EAGAIN, or -EBUSY, so always return -EBUSY on an error
+ * (except for root hubs, because they don't suspend through
+ * an upstream port like other USB devices).
*/
- if (status != 0)
+ if (status != 0 && udev->parent)
return -EBUSY;
return status;
}
@@ -1694,7 +1854,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)
@@ -1702,6 +1863,9 @@ int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
int ret = -EPERM;
+ if (enable && !udev->usb2_hw_lpm_allowed)
+ return 0;
+
if (hcd->driver->set_usb2_hw_lpm) {
ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable);
if (!ret)
@@ -1711,7 +1875,7 @@ int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
return ret;
}
-#endif /* CONFIG_USB_SUSPEND */
+#endif /* CONFIG_PM_RUNTIME */
struct bus_type usb_bus_type = {
.name = "usb",
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index db7fe50c23d..39a24021fe4 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -12,7 +12,6 @@
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
-#include <linux/idr.h>
#include <linux/usb.h>
#include "usb.h"
@@ -24,10 +23,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 *,
@@ -37,31 +32,31 @@ struct ep_attribute {
container_of(_attr, struct ep_attribute, attr)
#define usb_ep_attr(field, format_string) \
-static ssize_t show_ep_##field(struct device *dev, \
+static ssize_t field##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct ep_device *ep = to_ep_device(dev); \
return sprintf(buf, format_string, ep->desc->field); \
} \
-static DEVICE_ATTR(field, S_IRUGO, show_ep_##field, NULL);
+static DEVICE_ATTR_RO(field)
-usb_ep_attr(bLength, "%02x\n")
-usb_ep_attr(bEndpointAddress, "%02x\n")
-usb_ep_attr(bmAttributes, "%02x\n")
-usb_ep_attr(bInterval, "%02x\n")
+usb_ep_attr(bLength, "%02x\n");
+usb_ep_attr(bEndpointAddress, "%02x\n");
+usb_ep_attr(bmAttributes, "%02x\n");
+usb_ep_attr(bInterval, "%02x\n");
-static ssize_t show_ep_wMaxPacketSize(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t wMaxPacketSize_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct ep_device *ep = to_ep_device(dev);
return sprintf(buf, "%04x\n",
usb_endpoint_maxp(ep->desc) & 0x07ff);
}
-static DEVICE_ATTR(wMaxPacketSize, S_IRUGO, show_ep_wMaxPacketSize, NULL);
+static DEVICE_ATTR_RO(wMaxPacketSize);
-static ssize_t show_ep_type(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t type_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct ep_device *ep = to_ep_device(dev);
char *type = "unknown";
@@ -82,10 +77,10 @@ static ssize_t show_ep_type(struct device *dev, struct device_attribute *attr,
}
return sprintf(buf, "%s\n", type);
}
-static DEVICE_ATTR(type, S_IRUGO, show_ep_type, NULL);
+static DEVICE_ATTR_RO(type);
-static ssize_t show_ep_interval(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t interval_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct ep_device *ep = to_ep_device(dev);
char unit;
@@ -128,10 +123,10 @@ static ssize_t show_ep_interval(struct device *dev,
return sprintf(buf, "%d%cs\n", interval, unit);
}
-static DEVICE_ATTR(interval, S_IRUGO, show_ep_interval, NULL);
+static DEVICE_ATTR_RO(interval);
-static ssize_t show_ep_direction(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t direction_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct ep_device *ep = to_ep_device(dev);
char *direction;
@@ -144,7 +139,7 @@ static ssize_t show_ep_direction(struct device *dev,
direction = "out";
return sprintf(buf, "%s\n", direction);
}
-static DEVICE_ATTR(direction, S_IRUGO, show_ep_direction, NULL);
+static DEVICE_ATTR_RO(direction);
static struct attribute *ep_dev_attrs[] = {
&dev_attr_bLength.attr,
@@ -172,6 +167,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 +190,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/file.c b/drivers/usb/core/file.c
index d95760de9e8..ea337a718cc 100644
--- a/drivers/usb/core/file.c
+++ b/drivers/usb/core/file.c
@@ -8,7 +8,7 @@
* (C) Copyright Deti Fliegl 1999 (new USB architecture)
* (C) Copyright Randy Dunlap 2000
* (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id,
- more docs, etc)
+ * more docs, etc)
* (C) Copyright Yggdrasil Computing, Inc. 2000
* (usb_device_id matching changes by Adam J. Richter)
* (C) Copyright Greg Kroah-Hartman 2002-2003
@@ -27,29 +27,21 @@
static const struct file_operations *usb_minors[MAX_USB_MINORS];
static DECLARE_RWSEM(minor_rwsem);
-static int usb_open(struct inode * inode, struct file * file)
+static int usb_open(struct inode *inode, struct file *file)
{
- int minor = iminor(inode);
- const struct file_operations *c;
int err = -ENODEV;
- const struct file_operations *old_fops, *new_fops = NULL;
+ const struct file_operations *new_fops;
down_read(&minor_rwsem);
- c = usb_minors[minor];
+ new_fops = fops_get(usb_minors[iminor(inode)]);
- if (!c || !(new_fops = fops_get(c)))
+ if (!new_fops)
goto done;
- old_fops = file->f_op;
- file->f_op = new_fops;
+ replace_fops(file, new_fops);
/* Curiouser and curiouser... NULL ->open() as "no device" ? */
if (file->f_op->open)
- err = file->f_op->open(inode,file);
- if (err) {
- fops_put(file->f_op);
- file->f_op = fops_get(old_fops);
- }
- fops_put(old_fops);
+ err = file->f_op->open(inode, file);
done:
up_read(&minor_rwsem);
return err;
@@ -92,9 +84,9 @@ static int init_usb_class(void)
}
kref_init(&usb_class->kref);
- usb_class->class = class_create(THIS_MODULE, "usb");
+ 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;
@@ -153,7 +145,7 @@ void usb_major_cleanup(void)
* usb_deregister_dev() must be called when the driver is done with
* the minor numbers given out by this function.
*
- * Returns -EINVAL if something bad happens with trying to register a
+ * Return: -EINVAL if something bad happens with trying to register a
* device, and 0 on success.
*/
int usb_register_dev(struct usb_interface *intf,
@@ -166,7 +158,7 @@ int usb_register_dev(struct usb_interface *intf,
char *temp;
#ifdef CONFIG_USB_DYNAMIC_MINORS
- /*
+ /*
* We don't care what the device tries to start at, we want to start
* at zero to pack the devices into the smallest available space with
* no holes in the minor range.
@@ -183,7 +175,7 @@ int usb_register_dev(struct usb_interface *intf,
if (retval)
return retval;
- dev_dbg(&intf->dev, "looking for a minor, starting at %d", minor_base);
+ dev_dbg(&intf->dev, "looking for a minor, starting at %d\n", minor_base);
down_write(&minor_rwsem);
for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) {
@@ -239,7 +231,7 @@ void usb_deregister_dev(struct usb_interface *intf,
if (intf->minor == -1)
return;
- dbg ("removing %d minor", intf->minor);
+ dev_dbg(&intf->dev, "removing %d minor\n", intf->minor);
down_write(&minor_rwsem);
usb_minors[intf->minor] = NULL;
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 69ecd3c9231..358ca8dd784 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -47,6 +47,9 @@ int usb_choose_configuration(struct usb_device *udev)
int insufficient_power = 0;
struct usb_host_config *c, *best;
+ if (usb_device_is_owned(udev))
+ return 0;
+
best = NULL;
c = udev->config;
num_configs = udev->descriptor.bNumConfigurations;
@@ -97,7 +100,7 @@ int usb_choose_configuration(struct usb_device *udev)
*/
/* Rule out configs that draw too much bus current */
- if (c->desc.bMaxPower * 2 > udev->bus_mA) {
+ if (usb_get_max_power(udev, c) > udev->bus_mA) {
insufficient_power++;
continue;
}
@@ -152,6 +155,7 @@ int usb_choose_configuration(struct usb_device *udev)
}
return i;
}
+EXPORT_SYMBOL_GPL(usb_choose_configuration);
static int generic_probe(struct usb_device *udev)
{
@@ -160,15 +164,13 @@ static int generic_probe(struct usb_device *udev)
/* Choose and set the configuration. This registers the interfaces
* with the driver core and lets interface drivers bind to them.
*/
- if (usb_device_is_owned(udev))
- ; /* Don't configure if the device is owned */
- else if (udev->authorized == 0)
+ if (udev->authorized == 0)
dev_err(&udev->dev, "Device is not authorized for usage\n");
else {
c = usb_choose_configuration(udev);
if (c >= 0) {
err = usb_set_configuration(udev, c);
- if (err) {
+ if (err && err != -ENODEV) {
dev_err(&udev->dev, "can't set config #%d, error %d\n",
c, err);
/* This need not be fatal. The user can try to
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index d136b8f4c8a..82044b5d611 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -37,119 +37,123 @@
/* PCI-based HCs are common, but plenty of non-PCI HCs are used too */
-#ifdef CONFIG_PM_SLEEP
-
-/* Coordinate handoffs between EHCI and companion controllers
- * during system resume
+/*
+ * Coordinate handoffs between EHCI and companion controllers
+ * during EHCI probing and system resume.
*/
-static DEFINE_MUTEX(companions_mutex);
+static DECLARE_RWSEM(companions_rwsem);
#define CL_UHCI PCI_CLASS_SERIAL_USB_UHCI
#define CL_OHCI PCI_CLASS_SERIAL_USB_OHCI
#define CL_EHCI PCI_CLASS_SERIAL_USB_EHCI
-enum companion_action {
- SET_HS_COMPANION, CLEAR_HS_COMPANION, WAIT_FOR_COMPANIONS
-};
+static inline int is_ohci_or_uhci(struct pci_dev *pdev)
+{
+ return pdev->class == CL_OHCI || pdev->class == CL_UHCI;
+}
+
+typedef void (*companion_fn)(struct pci_dev *pdev, struct usb_hcd *hcd,
+ struct pci_dev *companion, struct usb_hcd *companion_hcd);
-static void companion_common(struct pci_dev *pdev, struct usb_hcd *hcd,
- enum companion_action action)
+/* Iterate over PCI devices in the same slot as pdev and call fn for each */
+static void for_each_companion(struct pci_dev *pdev, struct usb_hcd *hcd,
+ companion_fn fn)
{
struct pci_dev *companion;
struct usb_hcd *companion_hcd;
unsigned int slot = PCI_SLOT(pdev->devfn);
- /* Iterate through other PCI functions in the same slot.
- * If pdev is OHCI or UHCI then we are looking for EHCI, and
- * vice versa.
+ /*
+ * Iterate through other PCI functions in the same slot.
+ * If the function's drvdata isn't set then it isn't bound to
+ * a USB host controller driver, so skip it.
*/
companion = NULL;
for_each_pci_dev(companion) {
if (companion->bus != pdev->bus ||
PCI_SLOT(companion->devfn) != slot)
continue;
-
companion_hcd = pci_get_drvdata(companion);
- if (!companion_hcd)
+ if (!companion_hcd || !companion_hcd->self.root_hub)
continue;
-
- /* For SET_HS_COMPANION, store a pointer to the EHCI bus in
- * the OHCI/UHCI companion bus structure.
- * For CLEAR_HS_COMPANION, clear the pointer to the EHCI bus
- * in the OHCI/UHCI companion bus structure.
- * For WAIT_FOR_COMPANIONS, wait until the OHCI/UHCI
- * companion controllers have fully resumed.
- */
-
- if ((pdev->class == CL_OHCI || pdev->class == CL_UHCI) &&
- companion->class == CL_EHCI) {
- /* action must be SET_HS_COMPANION */
- dev_dbg(&companion->dev, "HS companion for %s\n",
- dev_name(&pdev->dev));
- hcd->self.hs_companion = &companion_hcd->self;
-
- } else if (pdev->class == CL_EHCI &&
- (companion->class == CL_OHCI ||
- companion->class == CL_UHCI)) {
- switch (action) {
- case SET_HS_COMPANION:
- dev_dbg(&pdev->dev, "HS companion for %s\n",
- dev_name(&companion->dev));
- companion_hcd->self.hs_companion = &hcd->self;
- break;
- case CLEAR_HS_COMPANION:
- companion_hcd->self.hs_companion = NULL;
- break;
- case WAIT_FOR_COMPANIONS:
- device_pm_wait_for_dev(&pdev->dev,
- &companion->dev);
- break;
- }
- }
+ fn(pdev, hcd, companion, companion_hcd);
}
}
-static void set_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd)
+/*
+ * We're about to add an EHCI controller, which will unceremoniously grab
+ * all the port connections away from its companions. To prevent annoying
+ * error messages, lock the companion's root hub and gracefully unconfigure
+ * it beforehand. Leave it locked until the EHCI controller is all set.
+ */
+static void ehci_pre_add(struct pci_dev *pdev, struct usb_hcd *hcd,
+ struct pci_dev *companion, struct usb_hcd *companion_hcd)
{
- mutex_lock(&companions_mutex);
- dev_set_drvdata(&pdev->dev, hcd);
- companion_common(pdev, hcd, SET_HS_COMPANION);
- mutex_unlock(&companions_mutex);
+ struct usb_device *udev;
+
+ if (is_ohci_or_uhci(companion)) {
+ udev = companion_hcd->self.root_hub;
+ usb_lock_device(udev);
+ usb_set_configuration(udev, 0);
+ }
}
-static void clear_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd)
+/*
+ * Adding the EHCI controller has either succeeded or failed. Set the
+ * companion pointer accordingly, and in either case, reconfigure and
+ * unlock the root hub.
+ */
+static void ehci_post_add(struct pci_dev *pdev, struct usb_hcd *hcd,
+ struct pci_dev *companion, struct usb_hcd *companion_hcd)
{
- mutex_lock(&companions_mutex);
- dev_set_drvdata(&pdev->dev, NULL);
+ struct usb_device *udev;
- /* If pdev is OHCI or UHCI, just clear its hs_companion pointer */
- if (pdev->class == CL_OHCI || pdev->class == CL_UHCI)
- hcd->self.hs_companion = NULL;
+ if (is_ohci_or_uhci(companion)) {
+ if (dev_get_drvdata(&pdev->dev)) { /* Succeeded */
+ dev_dbg(&pdev->dev, "HS companion for %s\n",
+ dev_name(&companion->dev));
+ companion_hcd->self.hs_companion = &hcd->self;
+ }
+ udev = companion_hcd->self.root_hub;
+ usb_set_configuration(udev, 1);
+ usb_unlock_device(udev);
+ }
+}
- /* Otherwise search for companion buses and clear their pointers */
- else
- companion_common(pdev, hcd, CLEAR_HS_COMPANION);
- mutex_unlock(&companions_mutex);
+/*
+ * We just added a non-EHCI controller. Find the EHCI controller to
+ * which it is a companion, and store a pointer to the bus structure.
+ */
+static void non_ehci_add(struct pci_dev *pdev, struct usb_hcd *hcd,
+ struct pci_dev *companion, struct usb_hcd *companion_hcd)
+{
+ if (is_ohci_or_uhci(pdev) && companion->class == CL_EHCI) {
+ dev_dbg(&pdev->dev, "FS/LS companion for %s\n",
+ dev_name(&companion->dev));
+ hcd->self.hs_companion = &companion_hcd->self;
+ }
}
-static void wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd)
+/* We are removing an EHCI controller. Clear the companions' pointers. */
+static void ehci_remove(struct pci_dev *pdev, struct usb_hcd *hcd,
+ struct pci_dev *companion, struct usb_hcd *companion_hcd)
{
- /* Only EHCI controllers need to wait.
- * No locking is needed because a controller cannot be resumed
- * while one of its companions is getting unbound.
- */
- if (pdev->class == CL_EHCI)
- companion_common(pdev, hcd, WAIT_FOR_COMPANIONS);
+ if (is_ohci_or_uhci(companion))
+ companion_hcd->self.hs_companion = NULL;
}
-#else /* !CONFIG_PM_SLEEP */
+#ifdef CONFIG_PM
-static inline void set_hs_companion(struct pci_dev *d, struct usb_hcd *h) {}
-static inline void clear_hs_companion(struct pci_dev *d, struct usb_hcd *h) {}
-static inline void wait_for_companions(struct pci_dev *d, struct usb_hcd *h) {}
+/* An EHCI controller must wait for its companions before resuming. */
+static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd,
+ struct pci_dev *companion, struct usb_hcd *companion_hcd)
+{
+ if (is_ohci_or_uhci(companion))
+ device_pm_wait_for_dev(&pdev->dev, &companion->dev);
+}
-#endif /* !CONFIG_PM_SLEEP */
+#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
@@ -167,12 +171,15 @@ static inline void wait_for_companions(struct pci_dev *d, struct usb_hcd *h) {}
* through the hotplug entry's driver_data.
*
* Store this function in the HCD's struct pci_driver as probe().
+ *
+ * Return: 0 if successful.
*/
int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct hc_driver *driver;
struct usb_hcd *hcd;
int retval;
+ int hcd_irq = 0;
if (usb_disabled())
return -ENODEV;
@@ -185,14 +192,20 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (pci_enable_device(dev) < 0)
return -ENODEV;
- dev->current_state = PCI_D0;
- if (!dev->irq) {
- dev_err(&dev->dev,
- "Found HC with no IRQ. Check BIOS/PCI %s setup!\n",
- pci_name(dev));
- retval = -ENODEV;
- goto disable_pci;
+ /*
+ * The xHCI driver has its own irq management
+ * make sure irq setup is not touched for xhci in generic hcd code
+ */
+ if ((driver->flags & HCD_MASK) != HCD_USB3) {
+ if (!dev->irq) {
+ dev_err(&dev->dev,
+ "Found HC with no IRQ. Check BIOS/PCI %s setup!\n",
+ pci_name(dev));
+ retval = -ENODEV;
+ goto disable_pci;
+ }
+ hcd_irq = dev->irq;
}
hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev));
@@ -201,6 +214,9 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
goto disable_pci;
}
+ hcd->amd_resume_bug = (usb_hcd_amd_remote_wakeup_quirk(dev) &&
+ driver->flags & (HCD_USB11 | HCD_USB3)) ? 1 : 0;
+
if (driver->flags & HCD_MEMORY) {
/* EHCI, OHCI */
hcd->rsrc_start = pci_resource_start(dev, 0);
@@ -209,7 +225,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
driver->description)) {
dev_dbg(&dev->dev, "controller already in use\n");
retval = -EBUSY;
- goto clear_companion;
+ goto put_hcd;
}
hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
if (hcd->regs == NULL) {
@@ -236,16 +252,36 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (region == PCI_ROM_RESOURCE) {
dev_dbg(&dev->dev, "no i/o regions available\n");
retval = -EBUSY;
- goto clear_companion;
+ goto put_hcd;
}
}
pci_set_master(dev);
- retval = usb_add_hcd(hcd, dev->irq, IRQF_SHARED);
+ /* Note: dev_set_drvdata must be called while holding the rwsem */
+ if (dev->class == CL_EHCI) {
+ down_write(&companions_rwsem);
+ dev_set_drvdata(&dev->dev, hcd);
+ for_each_companion(dev, hcd, ehci_pre_add);
+ retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED);
+ if (retval != 0)
+ dev_set_drvdata(&dev->dev, NULL);
+ for_each_companion(dev, hcd, ehci_post_add);
+ up_write(&companions_rwsem);
+ } else {
+ down_read(&companions_rwsem);
+ dev_set_drvdata(&dev->dev, hcd);
+ retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED);
+ if (retval != 0)
+ dev_set_drvdata(&dev->dev, NULL);
+ else
+ for_each_companion(dev, hcd, non_ehci_add);
+ up_read(&companions_rwsem);
+ }
+
if (retval != 0)
goto unmap_registers;
- set_hs_companion(dev, hcd);
+ device_wakeup_enable(hcd->self.controller);
if (pci_dev_run_wake(dev))
pm_runtime_put_noidle(&dev->dev);
@@ -258,8 +294,7 @@ release_mem_region:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
} else
release_region(hcd->rsrc_start, hcd->rsrc_len);
-clear_companion:
- clear_hs_companion(dev, hcd);
+put_hcd:
usb_put_hcd(hcd);
disable_pci:
pci_disable_device(dev);
@@ -302,14 +337,29 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
usb_hcd_irq(0, hcd);
local_irq_enable();
- usb_remove_hcd(hcd);
+ /* Note: dev_set_drvdata must be called while holding the rwsem */
+ if (dev->class == CL_EHCI) {
+ down_write(&companions_rwsem);
+ for_each_companion(dev, hcd, ehci_remove);
+ usb_remove_hcd(hcd);
+ dev_set_drvdata(&dev->dev, NULL);
+ up_write(&companions_rwsem);
+ } else {
+ /* Not EHCI; just clear the companion pointer */
+ down_read(&companions_rwsem);
+ hcd->self.hs_companion = NULL;
+ usb_remove_hcd(hcd);
+ dev_set_drvdata(&dev->dev, NULL);
+ up_read(&companions_rwsem);
+ }
+
if (hcd->driver->flags & HCD_MEMORY) {
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
} else {
release_region(hcd->rsrc_start, hcd->rsrc_len);
}
- clear_hs_companion(dev, hcd);
+
usb_put_hcd(hcd);
pci_disable_device(dev);
}
@@ -377,6 +427,7 @@ static int check_root_hub_suspended(struct device *dev)
return 0;
}
+#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME)
static int suspend_common(struct device *dev, bool do_wakeup)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
@@ -454,8 +505,15 @@ static int resume_common(struct device *dev, int event)
pci_set_master(pci_dev);
if (hcd->driver->pci_resume && !HCD_DEAD(hcd)) {
- if (event != PM_EVENT_AUTO_RESUME)
- wait_for_companions(pci_dev, hcd);
+
+ /*
+ * Only EHCI controllers have to wait for their companions.
+ * No locking is needed because PCI controller drivers do not
+ * get unbound during system resume.
+ */
+ if (pci_dev->class == CL_EHCI && event != PM_EVENT_AUTO_RESUME)
+ for_each_companion(pci_dev, hcd,
+ ehci_wait_for_companions);
retval = hcd->driver->pci_resume(hcd,
event == PM_EVENT_RESTORE);
@@ -468,6 +526,7 @@ static int resume_common(struct device *dev, int event)
}
return retval;
}
+#endif /* SLEEP || RUNTIME */
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index eb19cba34ac..bec31e2efb8 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -6,7 +6,7 @@
* (C) Copyright Deti Fliegl 1999
* (C) Copyright Randy Dunlap 2000
* (C) Copyright David Brownell 2000-2002
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -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>
@@ -38,9 +39,12 @@
#include <asm/unaligned.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
+#include <linux/types.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
+#include <linux/usb/phy.h>
#include "usb.h"
@@ -90,10 +94,7 @@ EXPORT_SYMBOL_GPL (usb_bus_list);
/* used when allocating bus numbers */
#define USB_MAXBUS 64
-struct usb_busmap {
- unsigned long busmap [USB_MAXBUS / (8*sizeof (unsigned long))];
-};
-static struct usb_busmap busmap;
+static DECLARE_BITMAP(busmap, USB_MAXBUS);
/* used when updating list of hcds */
DEFINE_MUTEX(usb_bus_list_lock); /* exported only for usbfs */
@@ -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] = {
@@ -138,7 +138,7 @@ static const u8 usb3_rh_dev_descriptor[18] = {
0x03, /* __u8 bDeviceProtocol; USB 3.0 hub */
0x09, /* __u8 bMaxPacketSize0; 2^9 = 512 Bytes */
- 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation */
+ 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */
0x03, 0x00, /* __le16 idProduct; device 0x0003 */
KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */
@@ -148,8 +148,29 @@ 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] = {
+static const u8 usb2_rh_dev_descriptor[18] = {
0x12, /* __u8 bLength; */
0x01, /* __u8 bDescriptorType; Device */
0x00, 0x02, /* __le16 bcdUSB; v2.0 */
@@ -159,7 +180,7 @@ static const u8 usb2_rh_dev_descriptor [18] = {
0x00, /* __u8 bDeviceProtocol; [ usb 2.0 no TT ] */
0x40, /* __u8 bMaxPacketSize0; 64 Bytes */
- 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation */
+ 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */
0x02, 0x00, /* __le16 idProduct; device 0x0002 */
KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */
@@ -172,7 +193,7 @@ static const u8 usb2_rh_dev_descriptor [18] = {
/* no usb 2.0 root hub "device qualifier" descriptor: one speed only */
/* usb 1.1 root hub device descriptor */
-static const u8 usb11_rh_dev_descriptor [18] = {
+static const u8 usb11_rh_dev_descriptor[18] = {
0x12, /* __u8 bLength; */
0x01, /* __u8 bDescriptorType; Device */
0x10, 0x01, /* __le16 bcdUSB; v1.1 */
@@ -182,7 +203,7 @@ static const u8 usb11_rh_dev_descriptor [18] = {
0x00, /* __u8 bDeviceProtocol; [ low/full speeds only ] */
0x40, /* __u8 bMaxPacketSize0; 64 Bytes */
- 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation */
+ 0x6b, 0x1d, /* __le16 idVendor; Linux Foundation 0x1d6b */
0x01, 0x00, /* __le16 idProduct; device 0x0001 */
KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */
@@ -197,7 +218,7 @@ static const u8 usb11_rh_dev_descriptor [18] = {
/* Configuration descriptors for our root hubs */
-static const u8 fs_rh_config_descriptor [] = {
+static const u8 fs_rh_config_descriptor[] = {
/* one configuration */
0x09, /* __u8 bLength; */
@@ -206,13 +227,13 @@ static const u8 fs_rh_config_descriptor [] = {
0x01, /* __u8 bNumInterfaces; (1) */
0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */
- 0xc0, /* __u8 bmAttributes;
+ 0xc0, /* __u8 bmAttributes;
Bit 7: must be set,
6: Self-powered,
5: Remote wakeup,
4..0: resvd */
0x00, /* __u8 MaxPower; */
-
+
/* USB 1.1:
* USB 2.0, single TT organization (mandatory):
* one interface, protocol 0
@@ -234,17 +255,17 @@ static const u8 fs_rh_config_descriptor [] = {
0x00, /* __u8 if_bInterfaceSubClass; */
0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
0x00, /* __u8 if_iInterface; */
-
+
/* one endpoint (status change endpoint) */
0x07, /* __u8 ep_bLength; */
0x05, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
- 0x03, /* __u8 ep_bmAttributes; Interrupt */
- 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+ 0x03, /* __u8 ep_bmAttributes; Interrupt */
+ 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
0xff /* __u8 ep_bInterval; (255ms -- usb 2.0 spec) */
};
-static const u8 hs_rh_config_descriptor [] = {
+static const u8 hs_rh_config_descriptor[] = {
/* one configuration */
0x09, /* __u8 bLength; */
@@ -253,13 +274,13 @@ static const u8 hs_rh_config_descriptor [] = {
0x01, /* __u8 bNumInterfaces; (1) */
0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */
- 0xc0, /* __u8 bmAttributes;
+ 0xc0, /* __u8 bmAttributes;
Bit 7: must be set,
6: Self-powered,
5: Remote wakeup,
4..0: resvd */
0x00, /* __u8 MaxPower; */
-
+
/* USB 1.1:
* USB 2.0, single TT organization (mandatory):
* one interface, protocol 0
@@ -281,12 +302,12 @@ static const u8 hs_rh_config_descriptor [] = {
0x00, /* __u8 if_bInterfaceSubClass; */
0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
0x00, /* __u8 if_iInterface; */
-
+
/* one endpoint (status change endpoint) */
0x07, /* __u8 ep_bLength; */
0x05, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
- 0x03, /* __u8 ep_bmAttributes; Interrupt */
+ 0x03, /* __u8 ep_bmAttributes; Interrupt */
/* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
* see hub.c:hub_configure() for details. */
(USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
@@ -356,9 +377,10 @@ MODULE_PARM_DESC(authorized_default,
* @buf: Buffer for USB string descriptor (header + UTF-16LE)
* @len: Length (in bytes; may be odd) of descriptor buffer.
*
- * The return value is the number of bytes filled in: 2 + 2*strlen(s) or
- * buflen, whichever is less.
+ * Return: The number of bytes filled in: 2 + 2*strlen(s) or @len,
+ * whichever is less.
*
+ * Note:
* USB String descriptors can contain at most 126 characters; input
* strings longer than that are truncated.
*/
@@ -394,7 +416,8 @@ ascii2desc(char const *s, u8 *buf, unsigned len)
*
* Produces either a manufacturer, product or serial number string for the
* virtual root hub device.
- * Returns the number of bytes filled in: the length of the descriptor or
+ *
+ * Return: The number of bytes filled in: the length of the descriptor or
* of the provided buffer, whichever is less.
*/
static unsigned
@@ -404,7 +427,7 @@ rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len)
char const *s;
static char const langids[4] = {4, USB_DT_STRING, 0x09, 0x04};
- // language ids
+ /* language ids */
switch (id) {
case 0:
/* Array of LANGID codes (0x0409 is MSFT-speak for "en-us") */
@@ -440,19 +463,15 @@ rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len)
static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
{
struct usb_ctrlrequest *cmd;
- u16 typeReq, wValue, wIndex, wLength;
+ u16 typeReq, wValue, wIndex, wLength;
u8 *ubuf = urb->transfer_buffer;
- /*
- * tbuf should be as big as the BOS descriptor and
- * the USB hub descriptor.
- */
- u8 tbuf[USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE]
- __attribute__((aligned(4)));
- const u8 *bufp = tbuf;
unsigned len = 0;
int status;
u8 patch_wakeup = 0;
u8 patch_protocol = 0;
+ u16 tbuf_size;
+ u8 *tbuf = NULL;
+ const u8 *bufp;
might_sleep();
@@ -472,6 +491,18 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
if (wLength > urb->transfer_buffer_length)
goto error;
+ /*
+ * tbuf should be at least as big as the
+ * USB hub descriptor.
+ */
+ tbuf_size = max_t(u16, sizeof(struct usb_hub_descriptor), wLength);
+ tbuf = kzalloc(tbuf_size, GFP_KERNEL);
+ if (!tbuf)
+ return -ENOMEM;
+
+ bufp = tbuf;
+
+
urb->actual_length = 0;
switch (typeReq) {
@@ -494,10 +525,10 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
*/
case DeviceRequest | USB_REQ_GET_STATUS:
- tbuf [0] = (device_may_wakeup(&hcd->self.root_hub->dev)
+ tbuf[0] = (device_may_wakeup(&hcd->self.root_hub->dev)
<< USB_DEVICE_REMOTE_WAKEUP)
| (1 << USB_DEVICE_SELF_POWERED);
- tbuf [1] = 0;
+ tbuf[1] = 0;
len = 2;
break;
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
@@ -514,7 +545,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
goto error;
break;
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
- tbuf [0] = 1;
+ tbuf[0] = 1;
len = 1;
/* FALLTHROUGH */
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
@@ -526,6 +557,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;
@@ -545,6 +579,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;
@@ -573,13 +608,13 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
}
break;
case DeviceRequest | USB_REQ_GET_INTERFACE:
- tbuf [0] = 0;
+ tbuf[0] = 0;
len = 1;
/* FALLTHROUGH */
case DeviceOutRequest | USB_REQ_SET_INTERFACE:
break;
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
- // wValue == urb->dev->devaddr
+ /* wValue == urb->dev->devaddr */
dev_dbg (hcd->self.controller, "root hub device address %d\n",
wValue);
break;
@@ -589,9 +624,9 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
/* ENDPOINT REQUESTS */
case EndpointRequest | USB_REQ_GET_STATUS:
- // ENDPOINT_HALT flag
- tbuf [0] = 0;
- tbuf [1] = 0;
+ /* ENDPOINT_HALT flag */
+ tbuf[0] = 0;
+ tbuf[1] = 0;
len = 2;
/* FALLTHROUGH */
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
@@ -619,6 +654,10 @@ nongeneric:
status = hcd->driver->hub_control (hcd,
typeReq, wValue, wIndex,
tbuf, wLength);
+
+ if (typeReq == GetHubDescriptor)
+ usb_hub_adjust_deviceremovable(hcd->self.root_hub,
+ (struct usb_hub_descriptor *)tbuf);
break;
error:
/* "protocol stall" on error */
@@ -643,7 +682,7 @@ error:
if (urb->transfer_buffer_length < len)
len = urb->transfer_buffer_length;
urb->actual_length = len;
- // always USB_DIR_IN, toward host
+ /* always USB_DIR_IN, toward host */
memcpy (ubuf, bufp, len);
/* report whether RH hardware supports remote wakeup */
@@ -661,18 +700,12 @@ error:
bDeviceProtocol = USB_HUB_PR_HS_SINGLE_TT;
}
+ kfree(tbuf);
+
/* any errors get returned through the urb completion */
spin_lock_irq(&hcd_root_hub_lock);
usb_hcd_unlink_urb_from_ep(hcd, urb);
-
- /* This peculiar use of spinlocks echoes what real HC drivers do.
- * Avoiding calls to local_irq_disable/enable makes the code
- * RT-friendly.
- */
- spin_unlock(&hcd_root_hub_lock);
usb_hcd_giveback_urb(hcd, urb, status);
- spin_lock(&hcd_root_hub_lock);
-
spin_unlock_irq(&hcd_root_hub_lock);
return 0;
}
@@ -712,9 +745,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
memcpy(urb->transfer_buffer, buffer, length);
usb_hcd_unlink_urb_from_ep(hcd, urb);
- spin_unlock(&hcd_root_hub_lock);
usb_hcd_giveback_urb(hcd, urb, 0);
- spin_lock(&hcd_root_hub_lock);
} else {
length = 0;
set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
@@ -804,10 +835,7 @@ static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
if (urb == hcd->status_urb) {
hcd->status_urb = NULL;
usb_hcd_unlink_urb_from_ep(hcd, urb);
-
- spin_unlock(&hcd_root_hub_lock);
usb_hcd_giveback_urb(hcd, urb, status);
- spin_lock(&hcd_root_hub_lock);
}
}
done:
@@ -820,9 +848,8 @@ static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
/*
* Show & store the current value of authorized_default
*/
-static ssize_t usb_host_authorized_default_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static ssize_t authorized_default_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct usb_device *rh_usb_dev = to_usb_device(dev);
struct usb_bus *usb_bus = rh_usb_dev->bus;
@@ -834,9 +861,9 @@ static ssize_t usb_host_authorized_default_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%u\n", usb_hcd->authorized_default);
}
-static ssize_t usb_host_authorized_default_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
+static ssize_t authorized_default_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
{
ssize_t result;
unsigned val;
@@ -849,18 +876,14 @@ static ssize_t usb_host_authorized_default_store(struct device *dev,
usb_hcd = bus_to_hcd(usb_bus);
result = sscanf(buf, "%u\n", &val);
if (result == 1) {
- usb_hcd->authorized_default = val? 1 : 0;
+ usb_hcd->authorized_default = val ? 1 : 0;
result = size;
- }
- else
+ } else {
result = -EINVAL;
+ }
return result;
}
-
-static DEVICE_ATTR(authorized_default, 0644,
- usb_host_authorized_default_show,
- usb_host_authorized_default_store);
-
+static DEVICE_ATTR_RW(authorized_default);
/* Group all the USB bus attributes */
static struct attribute *usb_bus_attrs[] = {
@@ -895,6 +918,7 @@ static void usb_bus_init (struct usb_bus *bus)
bus->bandwidth_allocated = 0;
bus->bandwidth_int_reqs = 0;
bus->bandwidth_isoc_reqs = 0;
+ mutex_init(&bus->usb_address0_mutex);
INIT_LIST_HEAD (&bus->bus_list);
}
@@ -908,6 +932,8 @@ static void usb_bus_init (struct usb_bus *bus)
*
* Assigns a bus number, and links the controller into usbcore data
* structures so that it can be seen by scanning the bus list.
+ *
+ * Return: 0 if successful. A negative error code otherwise.
*/
static int usb_register_bus(struct usb_bus *bus)
{
@@ -915,12 +941,12 @@ static int usb_register_bus(struct usb_bus *bus)
int busnum;
mutex_lock(&usb_bus_list_lock);
- busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);
+ busnum = find_next_zero_bit(busmap, USB_MAXBUS, 1);
if (busnum >= USB_MAXBUS) {
printk (KERN_ERR "%s: too many buses\n", usbcore_name);
goto error_find_busnum;
}
- set_bit (busnum, busmap.busmap);
+ set_bit(busnum, busmap);
bus->busnum = busnum;
/* Add it to the local list of buses */
@@ -961,7 +987,7 @@ static void usb_deregister_bus (struct usb_bus *bus)
usb_notify_remove_bus(bus);
- clear_bit (bus->busnum, busmap.busmap);
+ clear_bit(bus->busnum, busmap);
}
/**
@@ -972,6 +998,8 @@ static void usb_deregister_bus (struct usb_bus *bus)
* the device properly in the device tree and then calls usb_new_device()
* to register the usb device. It also assigns the root hub's USB address
* (always 1).
+ *
+ * Return: 0 if successful. A negative error code otherwise.
*/
static int register_root_hub(struct usb_hcd *hcd)
{
@@ -997,15 +1025,21 @@ static int register_root_hub(struct usb_hcd *hcd)
dev_name(&usb_dev->dev), retval);
return (retval < 0) ? retval : -EMSGSIZE;
}
+ if (usb_dev->speed == USB_SPEED_SUPER) {
+ retval = usb_get_bos_descriptor(usb_dev);
+ if (retval < 0) {
+ mutex_unlock(&usb_bus_list_lock);
+ dev_dbg(parent_dev, "can't read %s bos descriptor %d\n",
+ dev_name(&usb_dev->dev), retval);
+ return retval;
+ }
+ }
retval = usb_new_device (usb_dev);
if (retval) {
dev_err (parent_dev, "can't register root hub for %s, %d\n",
dev_name(&usb_dev->dev), retval);
- }
- mutex_unlock(&usb_bus_list_lock);
-
- if (retval == 0) {
+ } else {
spin_lock_irq (&hcd_root_hub_lock);
hcd->rh_registered = 1;
spin_unlock_irq (&hcd_root_hub_lock);
@@ -1014,10 +1048,54 @@ static int register_root_hub(struct usb_hcd *hcd)
if (HCD_DEAD(hcd))
usb_hc_died (hcd); /* This time clean up */
}
+ mutex_unlock(&usb_bus_list_lock);
return retval;
}
+/*
+ * usb_hcd_start_port_resume - a root-hub port is sending a resume signal
+ * @bus: the bus which the root hub belongs to
+ * @portnum: the port which is being resumed
+ *
+ * HCDs should call this function when they know that a resume signal is
+ * being sent to a root-hub port. The root hub will be prevented from
+ * going into autosuspend until usb_hcd_end_port_resume() is called.
+ *
+ * The bus's private lock must be held by the caller.
+ */
+void usb_hcd_start_port_resume(struct usb_bus *bus, int portnum)
+{
+ unsigned bit = 1 << portnum;
+
+ if (!(bus->resuming_ports & bit)) {
+ bus->resuming_ports |= bit;
+ pm_runtime_get_noresume(&bus->root_hub->dev);
+ }
+}
+EXPORT_SYMBOL_GPL(usb_hcd_start_port_resume);
+
+/*
+ * usb_hcd_end_port_resume - a root-hub port has stopped sending a resume signal
+ * @bus: the bus which the root hub belongs to
+ * @portnum: the port which is being resumed
+ *
+ * HCDs should call this function when they know that a resume signal has
+ * stopped being sent to a root-hub port. The root hub will be allowed to
+ * autosuspend again.
+ *
+ * The bus's private lock must be held by the caller.
+ */
+void usb_hcd_end_port_resume(struct usb_bus *bus, int portnum)
+{
+ unsigned bit = 1 << portnum;
+
+ if (bus->resuming_ports & bit) {
+ bus->resuming_ports &= ~bit;
+ pm_runtime_put_noidle(&bus->root_hub->dev);
+ }
+}
+EXPORT_SYMBOL_GPL(usb_hcd_end_port_resume);
/*-------------------------------------------------------------------------*/
@@ -1028,7 +1106,9 @@ static int register_root_hub(struct usb_hcd *hcd)
* @isoc: true for isochronous transactions, false for interrupt ones
* @bytecount: how many bytes in the transaction.
*
- * Returns approximate bus time in nanoseconds for a periodic transaction.
+ * Return: Approximate bus time in nanoseconds for a periodic transaction.
+ *
+ * Note:
* See USB 2.0 spec section 5.11.3; only periodic transfers need to be
* scheduled in software, this function is only used for such scheduling.
*/
@@ -1040,21 +1120,21 @@ long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
case USB_SPEED_LOW: /* INTR only */
if (is_input) {
tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
- return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+ return 64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp;
} else {
tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
- return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+ return 64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp;
}
case USB_SPEED_FULL: /* ISOC or INTR */
if (isoc) {
tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
- return (((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
+ return ((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp;
} else {
tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
- return (9107L + BW_HOST_DELAY + tmp);
+ return 9107L + BW_HOST_DELAY + tmp;
}
case USB_SPEED_HIGH: /* ISOC or INTR */
- // FIXME adjust for input vs output
+ /* FIXME adjust for input vs output */
if (isoc)
tmp = HS_NSECS_ISO (bytecount);
else
@@ -1086,7 +1166,7 @@ EXPORT_SYMBOL_GPL(usb_calc_bus_time);
* be disabled. The actions carried out here are required for URB
* submission, as well as for endpoint shutdown and for usb_kill_urb.
*
- * Returns 0 for no error, otherwise a negative error code (in which case
+ * Return: 0 for no error, otherwise a negative error code (in which case
* the enqueue() method must fail). If no error occurs but enqueue() fails
* anyway, it must call usb_hcd_unlink_urb_from_ep() before releasing
* the private spinlock and returning.
@@ -1141,7 +1221,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_link_urb_to_ep);
* be disabled. The actions carried out here are required for making
* sure than an unlink is valid.
*
- * Returns 0 for no error, otherwise a negative error code (in which case
+ * Return: 0 for no error, otherwise a negative error code (in which case
* the dequeue() method must fail). The possible error codes are:
*
* -EIDRM: @urb was not submitted or has already completed.
@@ -1218,7 +1298,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_unlink_urb_from_ep);
* DMA framework is dma_declare_coherent_memory()
*
* - So we use that, even though the primary requirement
- * is that the memory be "local" (hence addressible
+ * is that the memory be "local" (hence addressable
* by that device), not "coherent".
*
*/
@@ -1389,7 +1469,15 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
&& !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
if (hcd->self.uses_dma) {
if (urb->num_sgs) {
- int n = dma_map_sg(
+ int n;
+
+ /* We don't support sg for isoc transfers ! */
+ if (usb_endpoint_xfer_isoc(&urb->ep->desc)) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ n = dma_map_sg(
hcd->self.controller,
urb->sg,
urb->num_sgs,
@@ -1415,6 +1503,9 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
ret = -EAGAIN;
else
urb->transfer_flags |= URB_DMA_MAP_PAGE;
+ } else if (is_vmalloc_addr(urb->transfer_buffer)) {
+ WARN_ONCE(1, "transfer buffer not dma capable\n");
+ ret = -EAGAIN;
} else {
urb->transfer_dma = dma_map_single(
hcd->self.controller,
@@ -1560,6 +1651,77 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
/*-------------------------------------------------------------------------*/
+static void __usb_hcd_giveback_urb(struct urb *urb)
+{
+ struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
+ struct usb_anchor *anchor = urb->anchor;
+ int status = urb->unlinked;
+ unsigned long flags;
+
+ urb->hcpriv = NULL;
+ if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
+ urb->actual_length < urb->transfer_buffer_length &&
+ !status))
+ status = -EREMOTEIO;
+
+ unmap_urb_for_dma(hcd, urb);
+ usbmon_urb_complete(&hcd->self, urb, status);
+ usb_anchor_suspend_wakeups(anchor);
+ usb_unanchor_urb(urb);
+
+ /* pass ownership to the completion handler */
+ urb->status = status;
+
+ /*
+ * We disable local IRQs here avoid possible deadlock because
+ * drivers may call spin_lock() to hold lock which might be
+ * acquired in one hard interrupt handler.
+ *
+ * The local_irq_save()/local_irq_restore() around complete()
+ * will be removed if current USB drivers have been cleaned up
+ * and no one may trigger the above deadlock situation when
+ * running complete() in tasklet.
+ */
+ local_irq_save(flags);
+ urb->complete(urb);
+ local_irq_restore(flags);
+
+ usb_anchor_resume_wakeups(anchor);
+ atomic_dec(&urb->use_count);
+ if (unlikely(atomic_read(&urb->reject)))
+ wake_up(&usb_kill_urb_queue);
+ usb_put_urb(urb);
+}
+
+static void usb_giveback_urb_bh(unsigned long param)
+{
+ struct giveback_urb_bh *bh = (struct giveback_urb_bh *)param;
+ struct list_head local_list;
+
+ spin_lock_irq(&bh->lock);
+ bh->running = true;
+ restart:
+ list_replace_init(&bh->head, &local_list);
+ spin_unlock_irq(&bh->lock);
+
+ while (!list_empty(&local_list)) {
+ struct urb *urb;
+
+ urb = list_entry(local_list.next, struct urb, urb_list);
+ list_del_init(&urb->urb_list);
+ bh->completing_ep = urb->ep;
+ __usb_hcd_giveback_urb(urb);
+ bh->completing_ep = NULL;
+ }
+
+ /* check if there are new URBs to giveback */
+ spin_lock_irq(&bh->lock);
+ if (!list_empty(&bh->head))
+ goto restart;
+ bh->running = false;
+ spin_unlock_irq(&bh->lock);
+}
+
/**
* usb_hcd_giveback_urb - return URB from HCD to device driver
* @hcd: host controller returning the URB
@@ -1579,25 +1741,37 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
*/
void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
{
- urb->hcpriv = NULL;
- if (unlikely(urb->unlinked))
- status = urb->unlinked;
- else if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
- urb->actual_length < urb->transfer_buffer_length &&
- !status))
- status = -EREMOTEIO;
+ struct giveback_urb_bh *bh;
+ bool running, high_prio_bh;
- unmap_urb_for_dma(hcd, urb);
- usbmon_urb_complete(&hcd->self, urb, status);
- usb_unanchor_urb(urb);
+ /* pass status to tasklet via unlinked */
+ if (likely(!urb->unlinked))
+ urb->unlinked = status;
- /* pass ownership to the completion handler */
- urb->status = status;
- urb->complete (urb);
- atomic_dec (&urb->use_count);
- if (unlikely(atomic_read(&urb->reject)))
- wake_up (&usb_kill_urb_queue);
- usb_put_urb (urb);
+ if (!hcd_giveback_urb_in_bh(hcd) && !is_root_hub(urb->dev)) {
+ __usb_hcd_giveback_urb(urb);
+ return;
+ }
+
+ if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)) {
+ bh = &hcd->high_prio_bh;
+ high_prio_bh = true;
+ } else {
+ bh = &hcd->low_prio_bh;
+ high_prio_bh = false;
+ }
+
+ spin_lock(&bh->lock);
+ list_add_tail(&urb->urb_list, &bh->head);
+ running = bh->running;
+ spin_unlock(&bh->lock);
+
+ if (running)
+ ;
+ else if (high_prio_bh)
+ tasklet_hi_schedule(&bh->bh);
+ else
+ tasklet_schedule(&bh->bh);
}
EXPORT_SYMBOL_GPL(usb_hcd_giveback_urb);
@@ -1646,7 +1820,7 @@ rescan:
case USB_ENDPOINT_XFER_INT:
s = "-intr"; break;
default:
- s = "-iso"; break;
+ s = "-iso"; break;
};
s;
}));
@@ -1696,7 +1870,7 @@ rescan:
* pass in the current alternate interface setting in cur_alt,
* and pass in the new alternate interface setting in new_alt.
*
- * Returns an error if the requested bandwidth change exceeds the
+ * Return: An error if the requested bandwidth change exceeds the
* bus bandwidth or host controller internal resources.
*/
int usb_hcd_alloc_bandwidth(struct usb_device *udev,
@@ -1866,9 +2040,12 @@ void usb_hcd_reset_endpoint(struct usb_device *udev,
* @num_streams: number of streams to allocate.
* @mem_flags: flags hcd should use to allocate memory.
*
- * Sets up a group of bulk endpoints to have num_streams stream IDs available.
+ * Sets up a group of bulk endpoints to have @num_streams stream IDs available.
* Drivers may queue multiple transfers to different stream IDs, which may
* complete in a different order than they were queued.
+ *
+ * Return: On success, the number of allocated streams. On failure, a negative
+ * error code.
*/
int usb_alloc_streams(struct usb_interface *interface,
struct usb_host_endpoint **eps, unsigned int num_eps,
@@ -1876,7 +2053,7 @@ int usb_alloc_streams(struct usb_interface *interface,
{
struct usb_hcd *hcd;
struct usb_device *dev;
- int i;
+ int i, ret;
dev = interface_to_usbdev(interface);
hcd = bus_to_hcd(dev->bus);
@@ -1885,13 +2062,24 @@ int usb_alloc_streams(struct usb_interface *interface,
if (dev->speed != USB_SPEED_SUPER)
return -EINVAL;
- /* Streams only apply to bulk endpoints. */
- for (i = 0; i < num_eps; i++)
+ for (i = 0; i < num_eps; i++) {
+ /* Streams only apply to bulk endpoints. */
if (!usb_endpoint_xfer_bulk(&eps[i]->desc))
return -EINVAL;
+ /* Re-alloc is not allowed */
+ if (eps[i]->streams)
+ return -EINVAL;
+ }
- return hcd->driver->alloc_streams(hcd, dev, eps, num_eps,
+ ret = hcd->driver->alloc_streams(hcd, dev, eps, num_eps,
num_streams, mem_flags);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < num_eps; i++)
+ eps[i]->streams = ret;
+
+ return ret;
}
EXPORT_SYMBOL_GPL(usb_alloc_streams);
@@ -1904,26 +2092,35 @@ EXPORT_SYMBOL_GPL(usb_alloc_streams);
*
* Reverts a group of bulk endpoints back to not using stream IDs.
* Can fail if we are given bad arguments, or HCD is broken.
+ *
+ * Return: 0 on success. On failure, a negative error code.
*/
-void usb_free_streams(struct usb_interface *interface,
+int usb_free_streams(struct usb_interface *interface,
struct usb_host_endpoint **eps, unsigned int num_eps,
gfp_t mem_flags)
{
struct usb_hcd *hcd;
struct usb_device *dev;
- int i;
+ int i, ret;
dev = interface_to_usbdev(interface);
hcd = bus_to_hcd(dev->bus);
if (dev->speed != USB_SPEED_SUPER)
- return;
+ return -EINVAL;
- /* Streams only apply to bulk endpoints. */
+ /* Double-free is not allowed */
for (i = 0; i < num_eps; i++)
- if (!eps[i] || !usb_endpoint_xfer_bulk(&eps[i]->desc))
- return;
+ if (!eps[i] || !eps[i]->streams)
+ return -EINVAL;
+
+ ret = hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
+ if (ret < 0)
+ return ret;
- hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
+ for (i = 0; i < num_eps; i++)
+ eps[i]->streams = 0;
+
+ return ret;
}
EXPORT_SYMBOL_GPL(usb_free_streams);
@@ -1978,6 +2175,18 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
if (status == 0) {
usb_set_device_state(rhdev, USB_STATE_SUSPENDED);
hcd->state = HC_STATE_SUSPENDED;
+
+ /* Did we race with a root-hub wakeup event? */
+ if (rhdev->do_remote_wakeup) {
+ char buffer[6];
+
+ status = hcd->driver->hub_status_data(hcd, buffer);
+ if (status != 0) {
+ dev_dbg(&rhdev->dev, "suspend raced with wakeup event\n");
+ hcd_bus_resume(rhdev, PMSG_AUTO_RESUME);
+ status = -EBUSY;
+ }
+ }
} else {
spin_lock_irq(&hcd_root_hub_lock);
if (!HCD_DEAD(hcd)) {
@@ -2012,8 +2221,9 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
status = hcd->driver->bus_resume(hcd);
clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
if (status == 0) {
- /* TRSMRCY = 10 msec */
- msleep(10);
+ struct usb_device *udev;
+ int port1;
+
spin_lock_irq(&hcd_root_hub_lock);
if (!HCD_DEAD(hcd)) {
usb_set_device_state(rhdev, rhdev->actconfig
@@ -2023,6 +2233,20 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
hcd->state = HC_STATE_RUNNING;
}
spin_unlock_irq(&hcd_root_hub_lock);
+
+ /*
+ * Check whether any of the enabled ports on the root hub are
+ * unsuspended. If they are then a TRSMRCY delay is needed
+ * (this is what the USB-2 spec calls a "global resume").
+ * Otherwise we can skip the delay.
+ */
+ usb_hub_for_each_child(rhdev, port1, udev) {
+ if (udev->state != USB_STATE_NOTATTACHED &&
+ !udev->port_is_suspended) {
+ usleep_range(10000, 11000); /* TRSMRCY */
+ break;
+ }
+ }
} else {
hcd->state = old_state;
dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
@@ -2035,7 +2259,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
#endif /* CONFIG_PM */
-#ifdef CONFIG_USB_SUSPEND
+#ifdef CONFIG_PM_RUNTIME
/* Workqueue routine for root-hub remote wakeup */
static void hcd_resume_work(struct work_struct *work)
@@ -2043,13 +2267,11 @@ static void hcd_resume_work(struct work_struct *work)
struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work);
struct usb_device *udev = hcd->self.root_hub;
- usb_lock_device(udev);
usb_remote_wakeup(udev);
- usb_unlock_device(udev);
}
/**
- * usb_hcd_resume_root_hub - called by HCD to resume its root hub
+ * usb_hcd_resume_root_hub - called by HCD to resume its root hub
* @hcd: host controller for this root hub
*
* The USB host controller calls this function when its root hub is
@@ -2070,7 +2292,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
}
EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
-#endif /* CONFIG_USB_SUSPEND */
+#endif /* CONFIG_PM_RUNTIME */
/*-------------------------------------------------------------------------*/
@@ -2086,6 +2308,8 @@ EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
* khubd identifying and possibly configuring the device.
* This is needed by OTG controller drivers, where it helps meet
* HNP protocol timing requirements for starting a port reset.
+ *
+ * Return: 0 if successful.
*/
int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num)
{
@@ -2120,19 +2344,14 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum);
*
* If the controller isn't HALTed, calls the driver's irq handler.
* Checks whether the controller is now dead.
+ *
+ * Return: %IRQ_HANDLED if the IRQ was handled. %IRQ_NONE otherwise.
*/
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)
@@ -2140,7 +2359,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);
@@ -2192,6 +2410,14 @@ EXPORT_SYMBOL_GPL (usb_hc_died);
/*-------------------------------------------------------------------------*/
+static void init_giveback_urb_bh(struct giveback_urb_bh *bh)
+{
+
+ spin_lock_init(&bh->lock);
+ INIT_LIST_HEAD(&bh->head);
+ tasklet_init(&bh->bh, usb_giveback_urb_bh, (unsigned long)bh);
+}
+
/**
* usb_create_shared_hcd - create and initialize an HCD structure
* @driver: HC driver that will use this hcd
@@ -2205,7 +2431,8 @@ EXPORT_SYMBOL_GPL (usb_hc_died);
* HC driver's private data. Initialize the generic members of the
* hcd structure.
*
- * If memory is unavailable, returns NULL.
+ * Return: On success, a pointer to the created and initialized HCD structure.
+ * On failure (e.g. if memory is unavailable), %NULL.
*/
struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
struct device *dev, const char *bus_name,
@@ -2229,11 +2456,13 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
mutex_init(hcd->bandwidth_mutex);
dev_set_drvdata(dev, hcd);
} else {
+ mutex_lock(&usb_port_peer_mutex);
hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex;
hcd->primary_hcd = primary_hcd;
primary_hcd->primary_hcd = primary_hcd;
hcd->shared_hcd = primary_hcd;
primary_hcd->shared_hcd = hcd;
+ mutex_unlock(&usb_port_peer_mutex);
}
kref_init(&hcd->kref);
@@ -2246,7 +2475,7 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
init_timer(&hcd->rh_timer);
hcd->rh_timer.function = rh_timer_func;
hcd->rh_timer.data = (unsigned long) hcd;
-#ifdef CONFIG_USB_SUSPEND
+#ifdef CONFIG_PM_RUNTIME
INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
#endif
@@ -2269,7 +2498,8 @@ EXPORT_SYMBOL_GPL(usb_create_shared_hcd);
* HC driver's private data. Initialize the generic members of the
* hcd structure.
*
- * If memory is unavailable, returns NULL.
+ * Return: On success, a pointer to the created and initialized HCD
+ * structure. On failure (e.g. if memory is unavailable), %NULL.
*/
struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
struct device *dev, const char *bus_name)
@@ -2284,18 +2514,25 @@ EXPORT_SYMBOL_GPL(usb_create_hcd);
* deallocated.
*
* Make sure to only deallocate the bandwidth_mutex when the primary HCD is
- * freed. When hcd_release() is called for the non-primary HCD, set the
- * primary_hcd's shared_hcd pointer to null (since the non-primary HCD will be
- * freed shortly).
+ * freed. When hcd_release() is called for either hcd in a peer set
+ * invalidate the peer's ->shared_hcd and ->primary_hcd pointers to
+ * block new peering attempts
*/
-static void hcd_release (struct kref *kref)
+static void hcd_release(struct kref *kref)
{
struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
+ mutex_lock(&usb_port_peer_mutex);
if (usb_hcd_is_primary_hcd(hcd))
kfree(hcd->bandwidth_mutex);
- else
- hcd->shared_hcd->shared_hcd = NULL;
+ if (hcd->shared_hcd) {
+ struct usb_hcd *peer = hcd->shared_hcd;
+
+ peer->shared_hcd = NULL;
+ if (peer->primary_hcd == hcd)
+ peer->primary_hcd = NULL;
+ }
+ mutex_unlock(&usb_port_peer_mutex);
kfree(hcd);
}
@@ -2322,6 +2559,14 @@ int usb_hcd_is_primary_hcd(struct usb_hcd *hcd)
}
EXPORT_SYMBOL_GPL(usb_hcd_is_primary_hcd);
+int usb_hcd_find_raw_port_number(struct usb_hcd *hcd, int port1)
+{
+ if (!hcd->driver->find_raw_port_number)
+ return port1;
+
+ return hcd->driver->find_raw_port_number(hcd, port1);
+}
+
static int usb_hcd_request_irqs(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags)
{
@@ -2329,13 +2574,6 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd,
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,
@@ -2352,7 +2590,7 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd,
"io mem" : "io base",
(unsigned long long)hcd->rsrc_start);
} else {
- hcd->irq = -1;
+ hcd->irq = 0;
if (hcd->rsrc_start)
dev_info(hcd->self.controller, "%s 0x%08llx\n",
(hcd->driver->flags & HCD_MEMORY) ?
@@ -2362,6 +2600,21 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd,
return 0;
}
+/*
+ * Before we free this root hub, flush in-flight peering attempts
+ * and disable peer lookups
+ */
+static void usb_put_invalidate_rhdev(struct usb_hcd *hcd)
+{
+ struct usb_device *rhdev;
+
+ mutex_lock(&usb_port_peer_mutex);
+ rhdev = hcd->self.root_hub;
+ hcd->self.root_hub = NULL;
+ mutex_unlock(&usb_port_peer_mutex);
+ usb_put_dev(rhdev);
+}
+
/**
* usb_add_hcd - finish generic HCD structure initialization and register
* @hcd: the usb_hcd structure to initialize
@@ -2378,11 +2631,29 @@ int usb_add_hcd(struct usb_hcd *hcd,
int retval;
struct usb_device *rhdev;
+ if (IS_ENABLED(CONFIG_USB_PHY) && !hcd->phy) {
+ struct usb_phy *phy = usb_get_phy_dev(hcd->self.controller, 0);
+
+ if (IS_ERR(phy)) {
+ retval = PTR_ERR(phy);
+ if (retval == -EPROBE_DEFER)
+ return retval;
+ } else {
+ retval = usb_phy_init(phy);
+ if (retval) {
+ usb_put_phy(phy);
+ return retval;
+ }
+ hcd->phy = phy;
+ hcd->remove_phy = 1;
+ }
+ }
+
dev_info(hcd->self.controller, "%s\n", hcd->product_desc);
/* Keep old behaviour if authorized_default is not in [0, 1]. */
if (authorized_default < 0 || authorized_default > 1)
- hcd->authorized_default = hcd->wireless? 0 : 1;
+ hcd->authorized_default = hcd->wireless ? 0 : 1;
else
hcd->authorized_default = authorized_default;
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
@@ -2393,7 +2664,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
*/
if ((retval = hcd_buffer_create(hcd)) != 0) {
dev_dbg(hcd->self.controller, "pool alloc failed\n");
- return retval;
+ goto err_remove_phy;
}
if ((retval = usb_register_bus(&hcd->self)) < 0)
@@ -2404,7 +2675,9 @@ int usb_add_hcd(struct usb_hcd *hcd,
retval = -ENOMEM;
goto err_allocate_root_hub;
}
+ mutex_lock(&usb_port_peer_mutex);
hcd->self.root_hub = rhdev;
+ mutex_unlock(&usb_port_peer_mutex);
switch (hcd->speed) {
case HCD_USB11:
@@ -2413,6 +2686,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;
@@ -2437,7 +2713,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
* should already have been reset (and boot firmware kicked off etc).
*/
if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {
- dev_err(hcd->self.controller, "can't setup\n");
+ dev_err(hcd->self.controller, "can't setup: %d\n", retval);
goto err_hcd_driver_setup;
}
hcd->rh_pollable = 1;
@@ -2447,8 +2723,14 @@ int usb_add_hcd(struct usb_hcd *hcd,
&& device_can_wakeup(&hcd->self.root_hub->dev))
dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");
- /* enable irqs just before we start the controller */
- if (usb_hcd_is_primary_hcd(hcd)) {
+ /* initialize tasklets */
+ init_giveback_urb_bh(&hcd->high_prio_bh);
+ init_giveback_urb_bh(&hcd->low_prio_bh);
+
+ /* enable irqs just before we start the controller,
+ * if the BIOS provides legacy PCI irqs.
+ */
+ if (usb_hcd_is_primary_hcd(hcd) && irqnum) {
retval = usb_hcd_request_irqs(hcd, irqnum, irqflags);
if (retval)
goto err_request_irq;
@@ -2462,7 +2744,6 @@ int usb_add_hcd(struct usb_hcd *hcd,
}
/* starting here, usbcore will pay attention to this root hub */
- rhdev->bus_mA = min(500u, hcd->power_budget);
if ((retval = register_root_hub(hcd)) != 0)
goto err_register_root_hub;
@@ -2475,12 +2756,6 @@ int usb_add_hcd(struct usb_hcd *hcd,
if (hcd->uses_new_polling && HCD_POLL_RH(hcd))
usb_hcd_poll_rh_status(hcd);
- /*
- * Host controllers don't generate their own wakeup requests;
- * they only forward requests from the root hub. Therefore
- * controllers should always be enabled for remote wakeup.
- */
- device_wakeup_enable(hcd->self.controller);
return retval;
error_create_attr_group:
@@ -2491,7 +2766,7 @@ error_create_attr_group:
hcd->rh_registered = 0;
spin_unlock_irq(&hcd_root_hub_lock);
-#ifdef CONFIG_USB_SUSPEND
+#ifdef CONFIG_PM_RUNTIME
cancel_work_sync(&hcd->wakeup_work);
#endif
mutex_lock(&usb_bus_list_lock);
@@ -2506,18 +2781,24 @@ err_register_root_hub:
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
del_timer_sync(&hcd->rh_timer);
err_hcd_driver_start:
- if (usb_hcd_is_primary_hcd(hcd) && hcd->irq >= 0)
+ if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0)
free_irq(irqnum, hcd);
err_request_irq:
err_hcd_driver_setup:
err_set_rh_speed:
- usb_put_dev(hcd->self.root_hub);
+ usb_put_invalidate_rhdev(hcd);
err_allocate_root_hub:
usb_deregister_bus(&hcd->self);
err_register_bus:
hcd_buffer_destroy(hcd);
+err_remove_phy:
+ if (hcd->remove_phy && hcd->phy) {
+ usb_phy_shutdown(hcd->phy);
+ usb_put_phy(hcd->phy);
+ hcd->phy = NULL;
+ }
return retval;
-}
+}
EXPORT_SYMBOL_GPL(usb_add_hcd);
/**
@@ -2546,7 +2827,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
hcd->rh_registered = 0;
spin_unlock_irq (&hcd_root_hub_lock);
-#ifdef CONFIG_USB_SUSPEND
+#ifdef CONFIG_PM_RUNTIME
cancel_work_sync(&hcd->wakeup_work);
#endif
@@ -2554,6 +2835,16 @@ void usb_remove_hcd(struct usb_hcd *hcd)
usb_disconnect(&rhdev); /* Sets rhdev to NULL */
mutex_unlock(&usb_bus_list_lock);
+ /*
+ * tasklet_kill() isn't needed here because:
+ * - driver's disconnect() called from usb_disconnect() should
+ * make sure its URBs are completed during the disconnect()
+ * callback
+ *
+ * - it is too late to run complete() here since driver may have
+ * been removed already now
+ */
+
/* Prevent any more root-hub status calls from the timer.
* The HCD might still restart the timer (if a port status change
* interrupt occurs), but usb_hcd_poll_rh_status() won't invoke
@@ -2571,18 +2862,24 @@ void usb_remove_hcd(struct usb_hcd *hcd)
del_timer_sync(&hcd->rh_timer);
if (usb_hcd_is_primary_hcd(hcd)) {
- if (hcd->irq >= 0)
+ if (hcd->irq > 0)
free_irq(hcd->irq, hcd);
}
- usb_put_dev(hcd->self.root_hub);
usb_deregister_bus(&hcd->self);
hcd_buffer_destroy(hcd);
+ if (hcd->remove_phy && hcd->phy) {
+ usb_phy_shutdown(hcd->phy);
+ usb_put_phy(hcd->phy);
+ hcd->phy = NULL;
+ }
+
+ usb_put_invalidate_rhdev(hcd);
}
EXPORT_SYMBOL_GPL(usb_remove_hcd);
void
-usb_hcd_platform_shutdown(struct platform_device* dev)
+usb_hcd_platform_shutdown(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
@@ -2604,7 +2901,7 @@ struct usb_mon_operations *mon_ops;
* Notice that the code is minimally error-proof. Because usbmon needs
* symbols from usbcore, usbcore gets referenced and cannot be unloaded first.
*/
-
+
int usb_mon_register (struct usb_mon_operations *ops)
{
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index a0613d8f9be..0e950ad8cb2 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -20,72 +20,21 @@
#include <linux/usb.h>
#include <linux/usbdevice_fs.h>
#include <linux/usb/hcd.h>
+#include <linux/usb/otg.h>
#include <linux/usb/quirks.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/freezer.h>
+#include <linux/random.h>
+#include <linux/pm_qos.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
-#include "usb.h"
+#include "hub.h"
-/* if we are in debug mode, always announce new devices */
-#ifdef DEBUG
-#ifndef CONFIG_USB_ANNOUNCE_NEW_DEVICES
-#define CONFIG_USB_ANNOUNCE_NEW_DEVICES
-#endif
-#endif
-
-struct usb_hub {
- struct device *intfdev; /* the "interface" device */
- struct usb_device *hdev;
- struct kref kref;
- struct urb *urb; /* for interrupt polling pipe */
-
- /* buffer for urb ... with extra space in case of babble */
- char (*buffer)[8];
- union {
- struct usb_hub_status hub;
- struct usb_port_status port;
- } *status; /* buffer for status reports */
- struct mutex status_mutex; /* for the status buffer */
-
- int error; /* last reported error */
- int nerrors; /* track consecutive errors */
-
- struct list_head event_list; /* hubs w/data or errs ready */
- unsigned long event_bits[1]; /* status change bitmask */
- unsigned long change_bits[1]; /* ports with logical connect
- status change */
- unsigned long busy_bits[1]; /* ports being reset or
- resumed */
- unsigned long removed_bits[1]; /* ports with a "removed"
- device present */
-#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
-#error event_bits[] is too short!
-#endif
-
- struct usb_hub_descriptor *descriptor; /* class descriptor */
- struct usb_tt tt; /* Transaction Translator */
-
- unsigned mA_per_port; /* current for each child */
-
- unsigned limited_power:1;
- unsigned quiescing:1;
- unsigned disconnected:1;
-
- unsigned has_indicators:1;
- u8 indicator[USB_MAXCHILDREN];
- struct delayed_work leds;
- struct delayed_work init_work;
- void **port_owners;
-};
-
-static inline int hub_is_superspeed(struct usb_device *hdev)
-{
- return (hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS);
-}
+#define USB_VENDOR_GENESYS_LOGIC 0x05e3
+#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01
/* Protect struct usb_device->state and ->children members
* Note: Both are also protected by ->dev.sem, except that ->state can
@@ -101,6 +50,9 @@ static DECLARE_WAIT_QUEUE_HEAD(khubd_wait);
static struct task_struct *khubd_task;
+/* synchronize hub-port add/remove and peering operations */
+DEFINE_MUTEX(usb_port_peer_mutex);
+
/* cycle leds on hubs that aren't blinking for attention */
static bool blinkenlights = 0;
module_param (blinkenlights, bool, S_IRUGO);
@@ -148,11 +100,10 @@ MODULE_PARM_DESC(use_both_schemes,
DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
-#define HUB_DEBOUNCE_TIMEOUT 1500
+#define HUB_DEBOUNCE_TIMEOUT 2000
#define HUB_DEBOUNCE_STEP 25
#define HUB_DEBOUNCE_STABLE 100
-
static int usb_reset_and_verify_device(struct usb_device *udev);
static inline char *portspeed(struct usb_hub *hub, int portstatus)
@@ -160,7 +111,7 @@ static inline char *portspeed(struct usb_hub *hub, int portstatus)
if (hub_is_superspeed(hub->hdev))
return "5.0 Gb/s";
if (portstatus & USB_PORT_STAT_HIGH_SPEED)
- return "480 Mb/s";
+ return "480 Mb/s";
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
return "1.5 Mb/s";
else
@@ -168,13 +119,243 @@ 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)
+struct usb_hub *usb_hub_to_struct_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]);
}
+static int usb_device_supports_lpm(struct usb_device *udev)
+{
+ /* USB 2.1 (and greater) devices indicate LPM support through
+ * their USB 2.0 Extended Capabilities BOS descriptor.
+ */
+ if (udev->speed == USB_SPEED_HIGH) {
+ if (udev->bos->ext_cap &&
+ (USB_LPM_SUPPORT &
+ le32_to_cpu(udev->bos->ext_cap->bmAttributes)))
+ return 1;
+ return 0;
+ }
+
+ /*
+ * According to the USB 3.0 spec, all USB 3.0 devices must support LPM.
+ * However, there are some that don't, and they set the U1/U2 exit
+ * latencies to zero.
+ */
+ if (!udev->bos->ss_cap) {
+ dev_info(&udev->dev, "No LPM exit latency info found, disabling LPM.\n");
+ return 0;
+ }
+
+ if (udev->bos->ss_cap->bU1devExitLat == 0 &&
+ udev->bos->ss_cap->bU2DevExitLat == 0) {
+ if (udev->parent)
+ dev_info(&udev->dev, "LPM exit latency is zeroed, disabling LPM.\n");
+ else
+ dev_info(&udev->dev, "We don't know the algorithms for LPM for this host, disabling LPM.\n");
+ return 0;
+ }
+
+ if (!udev->parent || udev->parent->lpm_capable)
+ return 1;
+ return 0;
+}
+
+/*
+ * Set the Maximum Exit Latency (MEL) for the host to initiate a transition from
+ * either U1 or U2.
+ */
+static void usb_set_lpm_mel(struct usb_device *udev,
+ struct usb3_lpm_parameters *udev_lpm_params,
+ unsigned int udev_exit_latency,
+ struct usb_hub *hub,
+ struct usb3_lpm_parameters *hub_lpm_params,
+ unsigned int hub_exit_latency)
+{
+ unsigned int total_mel;
+ unsigned int device_mel;
+ unsigned int hub_mel;
+
+ /*
+ * Calculate the time it takes to transition all links from the roothub
+ * to the parent hub into U0. The parent hub must then decode the
+ * packet (hub header decode latency) to figure out which port it was
+ * bound for.
+ *
+ * The Hub Header decode latency is expressed in 0.1us intervals (0x1
+ * means 0.1us). Multiply that by 100 to get nanoseconds.
+ */
+ total_mel = hub_lpm_params->mel +
+ (hub->descriptor->u.ss.bHubHdrDecLat * 100);
+
+ /*
+ * How long will it take to transition the downstream hub's port into
+ * U0? The greater of either the hub exit latency or the device exit
+ * latency.
+ *
+ * The BOS U1/U2 exit latencies are expressed in 1us intervals.
+ * Multiply that by 1000 to get nanoseconds.
+ */
+ device_mel = udev_exit_latency * 1000;
+ hub_mel = hub_exit_latency * 1000;
+ if (device_mel > hub_mel)
+ total_mel += device_mel;
+ else
+ total_mel += hub_mel;
+
+ udev_lpm_params->mel = total_mel;
+}
+
+/*
+ * Set the maximum Device to Host Exit Latency (PEL) for the device to initiate
+ * a transition from either U1 or U2.
+ */
+static void usb_set_lpm_pel(struct usb_device *udev,
+ struct usb3_lpm_parameters *udev_lpm_params,
+ unsigned int udev_exit_latency,
+ struct usb_hub *hub,
+ struct usb3_lpm_parameters *hub_lpm_params,
+ unsigned int hub_exit_latency,
+ unsigned int port_to_port_exit_latency)
+{
+ unsigned int first_link_pel;
+ unsigned int hub_pel;
+
+ /*
+ * First, the device sends an LFPS to transition the link between the
+ * device and the parent hub into U0. The exit latency is the bigger of
+ * the device exit latency or the hub exit latency.
+ */
+ if (udev_exit_latency > hub_exit_latency)
+ first_link_pel = udev_exit_latency * 1000;
+ else
+ first_link_pel = hub_exit_latency * 1000;
+
+ /*
+ * When the hub starts to receive the LFPS, there is a slight delay for
+ * it to figure out that one of the ports is sending an LFPS. Then it
+ * will forward the LFPS to its upstream link. The exit latency is the
+ * delay, plus the PEL that we calculated for this hub.
+ */
+ hub_pel = port_to_port_exit_latency * 1000 + hub_lpm_params->pel;
+
+ /*
+ * According to figure C-7 in the USB 3.0 spec, the PEL for this device
+ * is the greater of the two exit latencies.
+ */
+ if (first_link_pel > hub_pel)
+ udev_lpm_params->pel = first_link_pel;
+ else
+ udev_lpm_params->pel = hub_pel;
+}
+
+/*
+ * Set the System Exit Latency (SEL) to indicate the total worst-case time from
+ * when a device initiates a transition to U0, until when it will receive the
+ * first packet from the host controller.
+ *
+ * Section C.1.5.1 describes the four components to this:
+ * - t1: device PEL
+ * - t2: time for the ERDY to make it from the device to the host.
+ * - t3: a host-specific delay to process the ERDY.
+ * - t4: time for the packet to make it from the host to the device.
+ *
+ * t3 is specific to both the xHCI host and the platform the host is integrated
+ * into. The Intel HW folks have said it's negligible, FIXME if a different
+ * vendor says otherwise.
+ */
+static void usb_set_lpm_sel(struct usb_device *udev,
+ struct usb3_lpm_parameters *udev_lpm_params)
+{
+ struct usb_device *parent;
+ unsigned int num_hubs;
+ unsigned int total_sel;
+
+ /* t1 = device PEL */
+ total_sel = udev_lpm_params->pel;
+ /* How many external hubs are in between the device & the root port. */
+ for (parent = udev->parent, num_hubs = 0; parent->parent;
+ parent = parent->parent)
+ num_hubs++;
+ /* t2 = 2.1us + 250ns * (num_hubs - 1) */
+ if (num_hubs > 0)
+ total_sel += 2100 + 250 * (num_hubs - 1);
+
+ /* t4 = 250ns * num_hubs */
+ total_sel += 250 * num_hubs;
+
+ udev_lpm_params->sel = total_sel;
+}
+
+static void usb_set_lpm_parameters(struct usb_device *udev)
+{
+ struct usb_hub *hub;
+ unsigned int port_to_port_delay;
+ unsigned int udev_u1_del;
+ unsigned int udev_u2_del;
+ unsigned int hub_u1_del;
+ unsigned int hub_u2_del;
+
+ if (!udev->lpm_capable || udev->speed != USB_SPEED_SUPER)
+ return;
+
+ hub = usb_hub_to_struct_hub(udev->parent);
+ /* It doesn't take time to transition the roothub into U0, since it
+ * doesn't have an upstream link.
+ */
+ if (!hub)
+ return;
+
+ udev_u1_del = udev->bos->ss_cap->bU1devExitLat;
+ udev_u2_del = le16_to_cpu(udev->bos->ss_cap->bU2DevExitLat);
+ hub_u1_del = udev->parent->bos->ss_cap->bU1devExitLat;
+ hub_u2_del = le16_to_cpu(udev->parent->bos->ss_cap->bU2DevExitLat);
+
+ usb_set_lpm_mel(udev, &udev->u1_params, udev_u1_del,
+ hub, &udev->parent->u1_params, hub_u1_del);
+
+ usb_set_lpm_mel(udev, &udev->u2_params, udev_u2_del,
+ hub, &udev->parent->u2_params, hub_u2_del);
+
+ /*
+ * Appendix C, section C.2.2.2, says that there is a slight delay from
+ * when the parent hub notices the downstream port is trying to
+ * transition to U0 to when the hub initiates a U0 transition on its
+ * upstream port. The section says the delays are tPort2PortU1EL and
+ * tPort2PortU2EL, but it doesn't define what they are.
+ *
+ * The hub chapter, sections 10.4.2.4 and 10.4.2.5 seem to be talking
+ * about the same delays. Use the maximum delay calculations from those
+ * sections. For U1, it's tHubPort2PortExitLat, which is 1us max. For
+ * U2, it's tHubPort2PortExitLat + U2DevExitLat - U1DevExitLat. I
+ * assume the device exit latencies they are talking about are the hub
+ * exit latencies.
+ *
+ * What do we do if the U2 exit latency is less than the U1 exit
+ * latency? It's possible, although not likely...
+ */
+ port_to_port_delay = 1;
+
+ usb_set_lpm_pel(udev, &udev->u1_params, udev_u1_del,
+ hub, &udev->parent->u1_params, hub_u1_del,
+ port_to_port_delay);
+
+ if (hub_u2_del > hub_u1_del)
+ port_to_port_delay = 1 + hub_u2_del - hub_u1_del;
+ else
+ port_to_port_delay = 1 + hub_u1_del;
+
+ usb_set_lpm_pel(udev, &udev->u2_params, udev_u2_del,
+ hub, &udev->parent->u2_params, hub_u2_del,
+ port_to_port_delay);
+
+ /* Now that we've got PEL, calculate SEL. */
+ usb_set_lpm_sel(udev, &udev->u1_params);
+ usb_set_lpm_sel(udev, &udev->u2_params);
+}
+
/* USB 2.0 spec Section 11.24.4.5 */
static int get_hub_descriptor(struct usb_device *hdev, void *data)
{
@@ -212,7 +393,7 @@ static int clear_hub_feature(struct usb_device *hdev, int feature)
/*
* USB 2.0 spec Section 11.24.2.2
*/
-static int clear_port_feature(struct usb_device *hdev, int port1, int feature)
+int usb_clear_port_feature(struct usb_device *hdev, int port1, int feature)
{
return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,
@@ -229,30 +410,35 @@ static int set_port_feature(struct usb_device *hdev, int port1, int feature)
NULL, 0, 1000);
}
+static char *to_led_name(int selector)
+{
+ switch (selector) {
+ case HUB_LED_AMBER:
+ return "amber";
+ case HUB_LED_GREEN:
+ return "green";
+ case HUB_LED_OFF:
+ return "off";
+ case HUB_LED_AUTO:
+ return "auto";
+ default:
+ return "??";
+ }
+}
+
/*
* USB 2.0 spec Section 11.24.2.7.1.10 and table 11-7
* for info about using port indicators
*/
-static void set_port_led(
- struct usb_hub *hub,
- int port1,
- int selector
-)
+static void set_port_led(struct usb_hub *hub, int port1, int selector)
{
- int status = set_port_feature(hub->hdev, (selector << 8) | port1,
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+ int status;
+
+ status = set_port_feature(hub->hdev, (selector << 8) | port1,
USB_PORT_FEAT_INDICATOR);
- if (status < 0)
- dev_dbg (hub->intfdev,
- "port %d indicator %s status %d\n",
- port1,
- ({ char *s; switch (selector) {
- case HUB_LED_AMBER: s = "amber"; break;
- case HUB_LED_GREEN: s = "green"; break;
- case HUB_LED_OFF: s = "off"; break;
- case HUB_LED_AUTO: s = "auto"; break;
- default: s = "??"; break;
- }; s; }),
- status);
+ dev_dbg(&port_dev->dev, "indicator %s status %d\n",
+ to_led_name(selector), status);
}
#define LED_CYCLE_PERIOD ((2*HZ)/3)
@@ -269,7 +455,7 @@ static void led_work (struct work_struct *work)
if (hdev->state != USB_STATE_CONFIGURED || hub->quiescing)
return;
- for (i = 0; i < hub->descriptor->bNbrPorts; i++) {
+ for (i = 0; i < hdev->maxchild; i++) {
unsigned selector, mode;
/* 30%-50% duty cycle */
@@ -318,13 +504,14 @@ static void led_work (struct work_struct *work)
}
if (!changed && blinkenlights) {
cursor++;
- cursor %= hub->descriptor->bNbrPorts;
+ cursor %= hdev->maxchild;
set_port_led(hub, cursor + 1, HUB_LED_GREEN);
hub->indicator[cursor] = INDICATOR_CYCLE;
changed++;
}
if (changed)
- schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
+ queue_delayed_work(system_power_efficient_wq,
+ &hub->leds, LED_CYCLE_PERIOD);
}
/* use a short timeout for hub/port status fetches */
@@ -373,8 +560,9 @@ static int hub_port_status(struct usb_hub *hub, int port1,
mutex_lock(&hub->status_mutex);
ret = get_port_status(hub->hdev, port1, &hub->status->port);
if (ret < 4) {
- dev_err(hub->intfdev,
- "%s failed (err = %d)\n", __func__, ret);
+ if (ret != -ENODEV)
+ dev_err(hub->intfdev,
+ "%s failed (err = %d)\n", __func__, ret);
if (ret >= 0)
ret = -EIO;
} else {
@@ -405,12 +593,35 @@ static void kick_khubd(struct usb_hub *hub)
void usb_kick_khubd(struct usb_device *hdev)
{
- struct usb_hub *hub = hdev_to_hub(hdev);
+ struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
if (hub)
kick_khubd(hub);
}
+/*
+ * Let the USB core know that a USB 3.0 device has sent a Function Wake Device
+ * Notification, which indicates it had initiated remote wakeup.
+ *
+ * USB 3.0 hubs do not report the port link state change from U3 to U0 when the
+ * device initiates resume, so the USB core will not receive notice of the
+ * resume through the normal hub interrupt URB.
+ */
+void usb_wakeup_notification(struct usb_device *hdev,
+ unsigned int portnum)
+{
+ struct usb_hub *hub;
+
+ if (!hdev)
+ return;
+
+ hub = usb_hub_to_struct_hub(hdev);
+ if (hub) {
+ set_bit(portnum, hub->wakeup_bits);
+ kick_khubd(hub);
+ }
+}
+EXPORT_SYMBOL_GPL(usb_wakeup_notification);
/* completion function, fires on port status changes and various faults */
static void hub_irq(struct urb *urb)
@@ -462,6 +673,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);
@@ -478,10 +698,9 @@ static void hub_tt_work(struct work_struct *work)
struct usb_hub *hub =
container_of(work, struct usb_hub, tt.clear_work);
unsigned long flags;
- int limit = 100;
spin_lock_irqsave (&hub->tt.lock, flags);
- while (--limit && !list_empty (&hub->tt.clear_list)) {
+ while (!list_empty(&hub->tt.clear_list)) {
struct list_head *next;
struct usb_tt_clear *clear;
struct usb_device *hdev = hub->hdev;
@@ -495,7 +714,7 @@ static void hub_tt_work(struct work_struct *work)
/* drop lock so HCD can concurrently report other TT errors */
spin_unlock_irqrestore (&hub->tt.lock, flags);
status = hub_clear_tt_buffer (hdev, clear->devinfo, clear->tt);
- if (status)
+ if (status && status != -ENODEV)
dev_err (&hdev->dev,
"clear tt %d (%04x) error %d\n",
clear->tt, clear->devinfo, status);
@@ -512,6 +731,38 @@ static void hub_tt_work(struct work_struct *work)
}
/**
+ * usb_hub_set_port_power - control hub port's power state
+ * @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.
+ *
+ * Return: 0 if successful. A negative error code otherwise.
+ */
+int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub,
+ int port1, bool set)
+{
+ int ret;
+
+ if (set)
+ ret = set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
+ else
+ ret = usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
+
+ if (ret)
+ return ret;
+
+ if (set)
+ set_bit(port1, hub->power_bits);
+ else
+ clear_bit(port1, hub->power_bits);
+ return 0;
+}
+
+/**
* usb_hub_clear_tt_buffer - clear control/bulk TT state in high speed hub
* @urb: an URB associated with the failed or incomplete split transaction
*
@@ -522,6 +773,8 @@ static void hub_tt_work(struct work_struct *work)
*
* It may not be possible for that hub to handle additional full (or low)
* speed transactions until that state is fully cleared out.
+ *
+ * Return: 0 if successful. A negative error code otherwise.
*/
int usb_hub_clear_tt_buffer(struct urb *urb)
{
@@ -564,16 +817,9 @@ int usb_hub_clear_tt_buffer(struct urb *urb)
}
EXPORT_SYMBOL_GPL(usb_hub_clear_tt_buffer);
-/* If do_delay is false, return the number of milliseconds the caller
- * needs to delay.
- */
-static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
+static void hub_power_on(struct usb_hub *hub, bool do_delay)
{
int port1;
- unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
- unsigned delay;
- u16 wHubCharacteristics =
- le16_to_cpu(hub->descriptor->wHubCharacteristics);
/* Enable power on each port. Some hubs have reserved values
* of LPSM (> 2) in their descriptors, even though they are
@@ -581,19 +827,19 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
* but only emulate it. In all cases, the ports won't work
* unless we send these messages to the hub.
*/
- if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2)
+ if (hub_is_port_power_switchable(hub))
dev_dbg(hub->intfdev, "enabling power on all ports\n");
else
dev_dbg(hub->intfdev, "trying to enable port power on "
"non-switchable hub\n");
- for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++)
- set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
-
- /* Wait at least 100 msec for power to become stable */
- delay = max(pgood_delay, (unsigned) 100);
+ for (port1 = 1; port1 <= hub->hdev->maxchild; port1++)
+ if (test_bit(port1, hub->power_bits))
+ set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
+ else
+ usb_clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_POWER);
if (do_delay)
- msleep(delay);
- return delay;
+ msleep(hub_power_on_good_delay(hub));
}
static int hub_hub_status(struct usb_hub *hub,
@@ -603,31 +849,106 @@ static int hub_hub_status(struct usb_hub *hub,
mutex_lock(&hub->status_mutex);
ret = get_hub_status(hub->hdev, &hub->status->hub);
- if (ret < 0)
- dev_err (hub->intfdev,
- "%s failed (err = %d)\n", __func__, ret);
- else {
+ if (ret < 0) {
+ if (ret != -ENODEV)
+ dev_err(hub->intfdev,
+ "%s failed (err = %d)\n", __func__, ret);
+ } else {
*status = le16_to_cpu(hub->status->hub.wHubStatus);
- *change = le16_to_cpu(hub->status->hub.wHubChange);
+ *change = le16_to_cpu(hub->status->hub.wHubChange);
ret = 0;
}
mutex_unlock(&hub->status_mutex);
return ret;
}
+static int hub_set_port_link_state(struct usb_hub *hub, int port1,
+ unsigned int link_status)
+{
+ return set_port_feature(hub->hdev,
+ port1 | (link_status << 3),
+ USB_PORT_FEAT_LINK_STATE);
+}
+
+/*
+ * If USB 3.0 ports are placed into the Disabled state, they will no longer
+ * detect any device connects or disconnects. This is generally not what the
+ * USB core wants, since it expects a disabled port to produce a port status
+ * change event when a new device connects.
+ *
+ * Instead, set the link state to Disabled, wait for the link to settle into
+ * that state, clear any change bits, and then put the port into the RxDetect
+ * state.
+ */
+static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
+{
+ int ret;
+ int total_time;
+ u16 portchange, portstatus;
+
+ if (!hub_is_superspeed(hub->hdev))
+ return -EINVAL;
+
+ ret = hub_port_status(hub, port1, &portstatus, &portchange);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * USB controller Advanced Micro Devices, Inc. [AMD] FCH USB XHCI
+ * Controller [1022:7814] will have spurious result making the following
+ * usb 3.0 device hotplugging route to the 2.0 root hub and recognized
+ * as high-speed device if we set the usb 3.0 port link state to
+ * Disabled. Since it's already in USB_SS_PORT_LS_RX_DETECT state, we
+ * check the state here to avoid the bug.
+ */
+ if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
+ USB_SS_PORT_LS_RX_DETECT) {
+ dev_dbg(&hub->ports[port1 - 1]->dev,
+ "Not disabling port; link state is RxDetect\n");
+ return ret;
+ }
+
+ ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED);
+ if (ret)
+ return ret;
+
+ /* Wait for the link to enter the disabled state. */
+ for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
+ ret = hub_port_status(hub, port1, &portstatus, &portchange);
+ if (ret < 0)
+ return ret;
+
+ if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
+ USB_SS_PORT_LS_SS_DISABLED)
+ break;
+ if (total_time >= HUB_DEBOUNCE_TIMEOUT)
+ break;
+ msleep(HUB_DEBOUNCE_STEP);
+ }
+ if (total_time >= HUB_DEBOUNCE_TIMEOUT)
+ dev_warn(&hub->ports[port1 - 1]->dev,
+ "Could not disable after %d ms\n", total_time);
+
+ return hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT);
+}
+
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
{
+ struct usb_port *port_dev = hub->ports[port1 - 1];
struct usb_device *hdev = hub->hdev;
int ret = 0;
- if (hdev->children[port1-1] && set_state)
- usb_set_device_state(hdev->children[port1-1],
- USB_STATE_NOTATTACHED);
- if (!hub->error && !hub_is_superspeed(hub->hdev))
- ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
- if (ret)
- dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
- port1, ret);
+ if (port_dev->child && set_state)
+ usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);
+ if (!hub->error) {
+ if (hub_is_superspeed(hub->hdev))
+ ret = hub_usb3_port_disable(hub, port1);
+ else
+ ret = usb_clear_port_feature(hdev, port1,
+ USB_PORT_FEAT_ENABLE);
+ }
+ if (ret && ret != -ENODEV)
+ dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret);
return ret;
}
@@ -638,7 +959,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
*/
static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
{
- dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1);
+ dev_dbg(&hub->ports[port1 - 1]->dev, "logical disconnect\n");
hub_port_disable(hub, port1, 1);
/* FIXME let caller ask to power down the port:
@@ -651,7 +972,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
*/
set_bit(port1, hub->change_bits);
- kick_khubd(hub);
+ kick_khubd(hub);
}
/**
@@ -663,6 +984,8 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
* see that the device has been disconnected. When the device is
* physically unplugged and something is plugged in, the events will
* be received and processed normally.
+ *
+ * Return: 0 if successful. A negative error code otherwise.
*/
int usb_remove_device(struct usb_device *udev)
{
@@ -671,7 +994,7 @@ int usb_remove_device(struct usb_device *udev)
if (!udev->parent) /* Can't remove a root hub */
return -EINVAL;
- hub = hdev_to_hub(udev->parent);
+ hub = usb_hub_to_struct_hub(udev->parent);
intf = to_usb_interface(hub->intfdev);
usb_autopm_get_interface(intf);
@@ -705,10 +1028,26 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
if (type == HUB_INIT3)
goto init3;
- /* After a resume, port power should still be on.
+ /* The superspeed hub except for root hub has to use Hub Depth
+ * value as an offset into the route string to locate the bits
+ * it uses to determine the downstream port number. So hub driver
+ * should send a set hub depth request to superspeed hub after
+ * the superspeed hub is set configuration in initialization or
+ * reset procedure.
+ *
+ * After a resume, port power should still be on.
* For any other type of activation, turn it on.
*/
if (type != HUB_RESUME) {
+ if (hdev->parent && hub_is_superspeed(hdev)) {
+ ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+ HUB_SET_DEPTH, USB_RT_HUB,
+ hdev->level - 1, 0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+ if (ret < 0)
+ dev_err(hub->intfdev,
+ "set hub depth failed\n");
+ }
/* Speed up system boot by using a delayed_work for the
* hub's initial power-up delays. This is pretty awkward
@@ -723,9 +1062,12 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
* for HUB_POST_RESET, but it's easier not to.
*/
if (type == HUB_INIT) {
- delay = hub_power_on(hub, false);
- PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2);
- schedule_delayed_work(&hub->init_work,
+ unsigned delay = hub_power_on_good_delay(hub);
+
+ hub_power_on(hub, false);
+ INIT_DELAYED_WORK(&hub->init_work, hub_init_func2);
+ queue_delayed_work(system_power_efficient_wq,
+ &hub->init_work,
msecs_to_jiffies(delay));
/* Suppress autosuspend until init is done */
@@ -757,21 +1099,23 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
}
init2:
- /* Check each port and set hub->change_bits to let khubd know
+ /*
+ * Check each port and set hub->change_bits to let khubd know
* which ports need attention.
*/
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
- struct usb_device *udev = hdev->children[port1-1];
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+ struct usb_device *udev = port_dev->child;
u16 portstatus, portchange;
portstatus = portchange = 0;
status = hub_port_status(hub, port1, &portstatus, &portchange);
if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
- dev_dbg(hub->intfdev,
- "port %d: status %04x change %04x\n",
- port1, portstatus, portchange);
+ dev_dbg(&port_dev->dev, "status %04x change %04x\n",
+ portstatus, portchange);
- /* After anything other than HUB_RESUME (i.e., initialization
+ /*
+ * After anything other than HUB_RESUME (i.e., initialization
* or any sort of reset), every port should be disabled.
* Unconnected ports should likewise be disabled (paranoia),
* and so should ports for which we have no usb_device.
@@ -784,39 +1128,35 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
/*
* USB3 protocol ports will automatically transition
* to Enabled state when detect an USB3.0 device attach.
- * Do not disable USB3 protocol ports.
+ * Do not disable USB3 protocol ports, just pretend
+ * power was lost
*/
- if (!hub_is_superspeed(hdev)) {
- clear_port_feature(hdev, port1,
+ portstatus &= ~USB_PORT_STAT_ENABLE;
+ if (!hub_is_superspeed(hdev))
+ usb_clear_port_feature(hdev, port1,
USB_PORT_FEAT_ENABLE);
- portstatus &= ~USB_PORT_STAT_ENABLE;
- } else {
- /* Pretend that power was lost for USB3 devs */
- portstatus &= ~USB_PORT_STAT_ENABLE;
- }
}
/* Clear status-change flags; we'll debounce later */
if (portchange & USB_PORT_STAT_C_CONNECTION) {
need_debounce_delay = true;
- clear_port_feature(hub->hdev, port1,
+ usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
}
if (portchange & USB_PORT_STAT_C_ENABLE) {
need_debounce_delay = true;
- clear_port_feature(hub->hdev, port1,
+ usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_ENABLE);
}
- if (portchange & USB_PORT_STAT_C_LINK_STATE) {
+ if (portchange & USB_PORT_STAT_C_RESET) {
need_debounce_delay = true;
- clear_port_feature(hub->hdev, port1,
- USB_PORT_FEAT_C_PORT_LINK_STATE);
+ usb_clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_RESET);
}
-
if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
hub_is_superspeed(hub->hdev)) {
need_debounce_delay = true;
- clear_port_feature(hub->hdev, port1,
+ usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_BH_PORT_RESET);
}
/* We can forget about a "removed" device when there's a
@@ -830,23 +1170,35 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
/* Tell khubd to disconnect the device or
* check for a new connection
*/
- if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
+ if (udev || (portstatus & USB_PORT_STAT_CONNECTION) ||
+ (portstatus & USB_PORT_STAT_OVERCURRENT))
set_bit(port1, hub->change_bits);
} else if (portstatus & USB_PORT_STAT_ENABLE) {
+ bool port_resumed = (portstatus &
+ USB_PORT_STAT_LINK_STATE) ==
+ USB_SS_PORT_LS_U0;
/* The power session apparently survived the resume.
* If there was an overcurrent or suspend change
* (i.e., remote wakeup request), have khubd
- * take care of it.
+ * take care of it. Look at the port link state
+ * for USB 3.0 hubs, since they don't have a suspend
+ * change bit, and they don't set the port link change
+ * bit on device-initiated resume.
*/
- if (portchange)
+ if (portchange || (hub_is_superspeed(hub->hdev) &&
+ port_resumed))
set_bit(port1, hub->change_bits);
} else if (udev->persist_enabled) {
#ifdef CONFIG_PM
udev->reset_resume = 1;
#endif
- set_bit(port1, hub->change_bits);
+ /* Don't set the change_bits when the device
+ * was powered off.
+ */
+ if (test_bit(port1, hub->power_bits))
+ set_bit(port1, hub->change_bits);
} else {
/* The power session is gone; tell khubd */
@@ -868,8 +1220,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
/* Don't do a long sleep inside a workqueue routine */
if (type == HUB_INIT2) {
- PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func3);
- schedule_delayed_work(&hub->init_work,
+ INIT_DELAYED_WORK(&hub->init_work, hub_init_func3);
+ queue_delayed_work(system_power_efficient_wq,
+ &hub->init_work,
msecs_to_jiffies(delay));
return; /* Continues at init3: below */
} else {
@@ -883,7 +1236,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
if (status < 0)
dev_err(hub->intfdev, "activate --> %d\n", status);
if (hub->has_indicators && blinkenlights)
- schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
+ queue_delayed_work(system_power_efficient_wq,
+ &hub->leds, LED_CYCLE_PERIOD);
/* Scan all ports that need attention */
kick_khubd(hub);
@@ -925,8 +1279,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);
}
}
@@ -935,7 +1289,15 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
if (hub->has_indicators)
cancel_delayed_work_sync(&hub->leds);
if (hub->tt.hub)
- cancel_work_sync(&hub->tt.clear_work);
+ flush_work(&hub->tt.clear_work);
+}
+
+static void hub_pm_barrier_for_all_ports(struct usb_hub *hub)
+{
+ int i;
+
+ for (i = 0; i < hub->hdev->maxchild; ++i)
+ pm_runtime_barrier(&hub->ports[i]->dev);
}
/* caller has locked the hub device */
@@ -944,6 +1306,8 @@ static int hub_pre_reset(struct usb_interface *intf)
struct usb_hub *hub = usb_get_intfdata(intf);
hub_quiesce(hub, HUB_PRE_RESET);
+ hub->in_reset = 1;
+ hub_pm_barrier_for_all_ports(hub);
return 0;
}
@@ -952,6 +1316,8 @@ static int hub_post_reset(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata(intf);
+ hub->in_reset = 0;
+ hub_pm_barrier_for_all_ports(hub);
hub_activate(hub, HUB_POST_RESET);
return 0;
}
@@ -965,8 +1331,11 @@ 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";
+ unsigned unit_load;
+ unsigned full_load;
+ unsigned maxchild;
hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
if (!hub->buffer) {
@@ -987,18 +1356,6 @@ static int hub_configure(struct usb_hub *hub,
goto fail;
}
- if (hub_is_superspeed(hdev) && (hdev->parent != NULL)) {
- ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
- HUB_SET_DEPTH, USB_RT_HUB,
- hdev->level - 1, 0, NULL, 0,
- USB_CTRL_SET_TIMEOUT);
-
- if (ret < 0) {
- message = "can't set hub depth";
- goto fail;
- }
- }
-
/* Request the entire hub descriptor.
* hub->descriptor can handle USB_MAXCHILDREN ports,
* but the hub can/will return fewer bytes here.
@@ -1011,31 +1368,42 @@ static int hub_configure(struct usb_hub *hub,
message = "hub has too many ports!";
ret = -ENODEV;
goto fail;
+ } else if (hub->descriptor->bNbrPorts == 0) {
+ message = "hub doesn't have any ports!";
+ ret = -ENODEV;
+ goto fail;
}
- hdev->maxchild = hub->descriptor->bNbrPorts;
- dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,
- (hdev->maxchild == 1) ? "" : "s");
+ maxchild = hub->descriptor->bNbrPorts;
+ dev_info(hub_dev, "%d port%s detected\n", maxchild,
+ (maxchild == 1) ? "" : "s");
- hub->port_owners = kzalloc(hdev->maxchild * sizeof(void *), GFP_KERNEL);
- if (!hub->port_owners) {
+ hub->ports = kzalloc(maxchild * sizeof(struct usb_port *), GFP_KERNEL);
+ if (!hub->ports) {
ret = -ENOMEM;
goto fail;
}
wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
+ if (hub_is_superspeed(hdev)) {
+ unit_load = 150;
+ full_load = 900;
+ } else {
+ unit_load = 100;
+ full_load = 500;
+ }
/* FIXME for USB 3.0, skip for now */
if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&
!(hub_is_superspeed(hdev))) {
int i;
- char portstr [USB_MAXCHILDREN + 1];
+ char portstr[USB_MAXCHILDREN + 1];
- for (i = 0; i < hdev->maxchild; i++)
+ for (i = 0; i < maxchild; i++)
portstr[i] = hub->descriptor->u.hs.DeviceRemovable
[((i + 1) / 8)] & (1 << ((i + 1) % 8))
? 'F' : 'R';
- portstr[hdev->maxchild] = 0;
+ portstr[maxchild] = 0;
dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr);
} else
dev_dbg(hub_dev, "standalone hub\n");
@@ -1097,32 +1465,32 @@ static int hub_configure(struct usb_hub *hub,
/* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
switch (wHubCharacteristics & HUB_CHAR_TTTT) {
- case HUB_TTTT_8_BITS:
- if (hdev->descriptor.bDeviceProtocol != 0) {
- hub->tt.think_time = 666;
- dev_dbg(hub_dev, "TT requires at most %d "
- "FS bit times (%d ns)\n",
- 8, hub->tt.think_time);
- }
- break;
- case HUB_TTTT_16_BITS:
- hub->tt.think_time = 666 * 2;
- dev_dbg(hub_dev, "TT requires at most %d "
- "FS bit times (%d ns)\n",
- 16, hub->tt.think_time);
- break;
- case HUB_TTTT_24_BITS:
- hub->tt.think_time = 666 * 3;
- dev_dbg(hub_dev, "TT requires at most %d "
- "FS bit times (%d ns)\n",
- 24, hub->tt.think_time);
- break;
- case HUB_TTTT_32_BITS:
- hub->tt.think_time = 666 * 4;
+ case HUB_TTTT_8_BITS:
+ if (hdev->descriptor.bDeviceProtocol != 0) {
+ hub->tt.think_time = 666;
dev_dbg(hub_dev, "TT requires at most %d "
"FS bit times (%d ns)\n",
- 32, hub->tt.think_time);
- break;
+ 8, hub->tt.think_time);
+ }
+ break;
+ case HUB_TTTT_16_BITS:
+ hub->tt.think_time = 666 * 2;
+ dev_dbg(hub_dev, "TT requires at most %d "
+ "FS bit times (%d ns)\n",
+ 16, hub->tt.think_time);
+ break;
+ case HUB_TTTT_24_BITS:
+ hub->tt.think_time = 666 * 3;
+ dev_dbg(hub_dev, "TT requires at most %d "
+ "FS bit times (%d ns)\n",
+ 24, hub->tt.think_time);
+ break;
+ case HUB_TTTT_32_BITS:
+ hub->tt.think_time = 666 * 4;
+ dev_dbg(hub_dev, "TT requires at most %d "
+ "FS bit times (%d ns)\n",
+ 32, hub->tt.think_time);
+ break;
}
/* probe() zeroes hub->indicator[] */
@@ -1138,54 +1506,45 @@ static int hub_configure(struct usb_hub *hub,
* and battery-powered root hubs (may provide just 8 mA).
*/
ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);
- if (ret < 2) {
+ if (ret) {
message = "can't get hub status";
goto fail;
}
- le16_to_cpus(&hubstatus);
+ hcd = bus_to_hcd(hdev->bus);
if (hdev == hdev->bus->root_hub) {
- if (hdev->bus_mA == 0 || hdev->bus_mA >= 500)
- hub->mA_per_port = 500;
+ if (hcd->power_budget > 0)
+ hdev->bus_mA = hcd->power_budget;
+ else
+ hdev->bus_mA = full_load * maxchild;
+ if (hdev->bus_mA >= full_load)
+ hub->mA_per_port = full_load;
else {
hub->mA_per_port = hdev->bus_mA;
hub->limited_power = 1;
}
} else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
+ int remaining = hdev->bus_mA -
+ hub->descriptor->bHubContrCurrent;
+
dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",
hub->descriptor->bHubContrCurrent);
hub->limited_power = 1;
- if (hdev->maxchild > 0) {
- int remaining = hdev->bus_mA -
- hub->descriptor->bHubContrCurrent;
- if (remaining < hdev->maxchild * 100)
- dev_warn(hub_dev,
+ if (remaining < maxchild * unit_load)
+ dev_warn(hub_dev,
"insufficient power available "
"to use all downstream ports\n");
- hub->mA_per_port = 100; /* 7.2.1.1 */
- }
+ hub->mA_per_port = unit_load; /* 7.2.1 */
+
} else { /* Self-powered external hub */
/* FIXME: What about battery-powered external hubs that
* provide less current per port? */
- hub->mA_per_port = 500;
+ hub->mA_per_port = full_load;
}
- if (hub->mA_per_port < 500)
+ if (hub->mA_per_port < full_load)
dev_dbg(hub_dev, "%umA bus power budget for each child\n",
hub->mA_per_port);
- /* Update the HCD's internal representation of this hub before khubd
- * starts getting port status changes for devices under the hub.
- */
- hcd = bus_to_hcd(hdev->bus);
- if (hcd->driver->update_hub_device) {
- ret = hcd->driver->update_hub_device(hcd, hdev,
- &hub->tt, GFP_KERNEL);
- if (ret < 0) {
- message = "can't update HCD hub info";
- goto fail;
- }
- }
-
ret = hub_hub_status(hub, &hubstatus, &hubchange);
if (ret < 0) {
message = "can't get hub status";
@@ -1225,7 +1584,41 @@ static int hub_configure(struct usb_hub *hub,
/* maybe cycle the hub leds */
if (hub->has_indicators && blinkenlights)
- hub->indicator [0] = INDICATOR_CYCLE;
+ hub->indicator[0] = INDICATOR_CYCLE;
+
+ mutex_lock(&usb_port_peer_mutex);
+ for (i = 0; i < maxchild; i++) {
+ ret = usb_hub_create_port_device(hub, i + 1);
+ if (ret < 0) {
+ dev_err(hub->intfdev,
+ "couldn't create port%d device.\n", i + 1);
+ break;
+ }
+ }
+ hdev->maxchild = i;
+ for (i = 0; i < hdev->maxchild; i++) {
+ struct usb_port *port_dev = hub->ports[i];
+
+ pm_runtime_put(&port_dev->dev);
+ }
+
+ mutex_unlock(&usb_port_peer_mutex);
+ if (ret < 0)
+ goto fail;
+
+ /* Update the HCD's internal representation of this hub before khubd
+ * starts getting port status changes for devices under the hub.
+ */
+ if (hcd->driver->update_hub_device) {
+ ret = hcd->driver->update_hub_device(hcd, hdev,
+ &hub->tt, GFP_KERNEL);
+ if (ret < 0) {
+ message = "can't update HCD hub info";
+ goto fail;
+ }
+ }
+
+ usb_hub_adjust_deviceremovable(hdev, hub->descriptor);
hub_activate(hub, HUB_INIT);
return 0;
@@ -1249,7 +1642,9 @@ static unsigned highspeed_hubs;
static void hub_disconnect(struct usb_interface *intf)
{
- struct usb_hub *hub = usb_get_intfdata (intf);
+ struct usb_hub *hub = usb_get_intfdata(intf);
+ struct usb_device *hdev = interface_to_usbdev(intf);
+ int port1;
/* Take the hub off the event list and don't let it be added again */
spin_lock_irq(&hub_event_lock);
@@ -1264,18 +1659,30 @@ static void hub_disconnect(struct usb_interface *intf)
hub->error = 0;
hub_quiesce(hub, HUB_DISCONNECT);
- usb_set_intfdata (intf, NULL);
- hub->hdev->maxchild = 0;
+ mutex_lock(&usb_port_peer_mutex);
+
+ /* Avoid races with recursively_mark_NOTATTACHED() */
+ spin_lock_irq(&device_state_lock);
+ port1 = hdev->maxchild;
+ hdev->maxchild = 0;
+ usb_set_intfdata(intf, NULL);
+ spin_unlock_irq(&device_state_lock);
+
+ for (; port1 > 0; --port1)
+ usb_hub_remove_port_device(hub, port1);
+
+ mutex_unlock(&usb_port_peer_mutex);
if (hub->hdev->speed == USB_SPEED_HIGH)
highspeed_hubs--;
usb_free_urb(hub->urb);
- kfree(hub->port_owners);
+ kfree(hub->ports);
kfree(hub->descriptor);
kfree(hub->status);
kfree(hub->buffer);
+ pm_suspend_ignore_children(&intf->dev, false);
kref_put(&hub->kref, hub_release);
}
@@ -1289,14 +1696,54 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
desc = intf->cur_altsetting;
hdev = interface_to_usbdev(intf);
- /* Hubs have proper suspend/resume support. USB 3.0 device suspend is
- * different from USB 2.0/1.1 device suspend, and unfortunately we
- * don't support it yet. So leave autosuspend disabled for USB 3.0
- * external hubs for now. Enable autosuspend for USB 3.0 roothubs,
- * since that isn't a "real" hub.
+ /*
+ * Set default autosuspend delay as 0 to speedup bus suspend,
+ * based on the below considerations:
+ *
+ * - Unlike other drivers, the hub driver does not rely on the
+ * autosuspend delay to provide enough time to handle a wakeup
+ * event, and the submitted status URB is just to check future
+ * change on hub downstream ports, so it is safe to do it.
+ *
+ * - The patch might cause one or more auto supend/resume for
+ * below very rare devices when they are plugged into hub
+ * first time:
+ *
+ * devices having trouble initializing, and disconnect
+ * themselves from the bus and then reconnect a second
+ * or so later
+ *
+ * devices just for downloading firmware, and disconnects
+ * themselves after completing it
+ *
+ * For these quite rare devices, their drivers may change the
+ * autosuspend delay of their parent hub in the probe() to one
+ * appropriate value to avoid the subtle problem if someone
+ * does care it.
+ *
+ * - The patch may cause one or more auto suspend/resume on
+ * hub during running 'lsusb', but it is probably too
+ * infrequent to worry about.
+ *
+ * - Change autosuspend delay of hub can avoid unnecessary auto
+ * suspend timer for hub, also may decrease power consumption
+ * of USB bus.
+ */
+ pm_runtime_set_autosuspend_delay(&hdev->dev, 0);
+
+ /*
+ * Hubs have proper suspend/resume support, except for root hubs
+ * where the controller driver doesn't have bus_suspend and
+ * bus_resume methods.
*/
- if (!hub_is_superspeed(hdev) || !hdev->parent)
+ if (hdev->parent) { /* normal device */
usb_enable_autosuspend(hdev);
+ } else { /* root hub */
+ const struct hc_driver *drv = bus_to_hcd(hdev->bus)->driver;
+
+ if (drv->bus_suspend && drv->bus_resume)
+ usb_enable_autosuspend(hdev);
+ }
if (hdev->level == MAX_TOPO_LEVEL) {
dev_err(&intf->dev,
@@ -1349,10 +1796,14 @@ descriptor_error:
usb_set_intfdata (intf, hub);
intf->needs_remote_wakeup = 1;
+ pm_suspend_ignore_children(&intf->dev, true);
if (hdev->speed == USB_SPEED_HIGH)
highspeed_hubs++;
+ if (id->driver_info & HUB_QUIRK_CHECK_PORT_AUTOSUSPEND)
+ hub->quirk_check_port_auto_suspend = 1;
+
if (hub_configure(hub, endpoint) >= 0)
return 0;
@@ -1364,6 +1815,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 = usb_hub_to_struct_hub(hdev);
/* assert ifno == 0 (part of hub spec) */
switch (code) {
@@ -1377,11 +1829,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);
@@ -1399,25 +1851,28 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
* to one of these "claimed" ports, the program will "own" the device.
*/
static int find_port_owner(struct usb_device *hdev, unsigned port1,
- void ***ppowner)
+ struct usb_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 = &(hdev_to_hub(hdev)->port_owners[port1 - 1]);
+ *ppowner = &(hub->ports[port1 - 1]->port_owner);
return 0;
}
/* In the following three functions, the caller must hold hdev's lock */
-int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner)
+int usb_hub_claim_port(struct usb_device *hdev, unsigned port1,
+ struct usb_dev_state *owner)
{
int rc;
- void **powner;
+ struct usb_dev_state **powner;
rc = find_port_owner(hdev, port1, &powner);
if (rc)
@@ -1427,11 +1882,13 @@ int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner)
*powner = owner;
return rc;
}
+EXPORT_SYMBOL_GPL(usb_hub_claim_port);
-int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner)
+int usb_hub_release_port(struct usb_device *hdev, unsigned port1,
+ struct usb_dev_state *owner)
{
int rc;
- void **powner;
+ struct usb_dev_state **powner;
rc = find_port_owner(hdev, port1, &powner);
if (rc)
@@ -1441,19 +1898,18 @@ int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner)
*powner = NULL;
return rc;
}
+EXPORT_SYMBOL_GPL(usb_hub_release_port);
-void usb_hub_release_all_ports(struct usb_device *hdev, void *owner)
+void usb_hub_release_all_ports(struct usb_device *hdev, struct usb_dev_state *owner)
{
+ struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
int n;
- void **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 */
@@ -1463,18 +1919,18 @@ 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];
+ hub = usb_hub_to_struct_hub(udev->parent);
+ return !!hub->ports[udev->portnum - 1]->port_owner;
}
-
static void recursively_mark_NOTATTACHED(struct usb_device *udev)
{
+ struct usb_hub *hub = usb_hub_to_struct_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;
@@ -1587,7 +2043,7 @@ static void choose_devnum(struct usb_device *udev)
if (devnum >= 128)
devnum = find_next_zero_bit(bus->devmap.devicemap,
128, 1);
- bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
+ bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1);
}
if (devnum < 128) {
set_bit(devnum, bus->devmap.devicemap);
@@ -1619,6 +2075,18 @@ static void hub_free_dev(struct usb_device *udev)
hcd->driver->free_dev(hcd, udev);
}
+static void hub_disconnect_children(struct usb_device *udev)
+{
+ struct usb_hub *hub = usb_hub_to_struct_hub(udev);
+ int i;
+
+ /* Free up all the children before we remove this device */
+ for (i = 0; i < udev->maxchild; i++) {
+ if (hub->ports[i]->child)
+ usb_disconnect(&hub->ports[i]->child);
+ }
+}
+
/**
* usb_disconnect - disconnect a device (usbcore-internal)
* @pdev: pointer to device being disconnected
@@ -1627,8 +2095,8 @@ static void hub_free_dev(struct usb_device *udev)
* Something got disconnected. Get rid of it and all of its children.
*
* If *pdev is a normal device then the parent hub must already be locked.
- * If *pdev is a root hub then this routine will acquire the
- * usb_bus_list_lock on behalf of the caller.
+ * If *pdev is a root hub then the caller must hold the usb_bus_list_lock,
+ * which protects the set of root hubs as well as the list of buses.
*
* Only hub drivers (including virtual root hub drivers for host
* controllers) should ever call this.
@@ -1637,9 +2105,10 @@ static void hub_free_dev(struct usb_device *udev)
*/
void usb_disconnect(struct usb_device **pdev)
{
- struct usb_device *udev = *pdev;
- int i;
- struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+ struct usb_port *port_dev = NULL;
+ struct usb_device *udev = *pdev;
+ struct usb_hub *hub;
+ int port1;
/* mark the device as inactive, so any further urb submissions for
* this device (and any of its children) will fail immediately.
@@ -1651,22 +2120,32 @@ void usb_disconnect(struct usb_device **pdev)
usb_lock_device(udev);
- /* Free up all the children before we remove this device */
- for (i = 0; i < USB_MAXCHILDREN; i++) {
- if (udev->children[i])
- usb_disconnect(&udev->children[i]);
- }
+ hub_disconnect_children(udev);
/* deallocate hcd/hardware state ... nuking all pending urbs and
* cleaning up all state associated with the current configuration
* so that the hardware is now fully quiesced.
*/
dev_dbg (&udev->dev, "unregistering device\n");
- mutex_lock(hcd->bandwidth_mutex);
usb_disable_device(udev, 0);
- mutex_unlock(hcd->bandwidth_mutex);
usb_hcd_synchronize_unlinks(udev);
+ if (udev->parent) {
+ port1 = udev->portnum;
+ hub = usb_hub_to_struct_hub(udev->parent);
+ port_dev = hub->ports[port1 - 1];
+
+ sysfs_remove_link(&udev->dev.kobj, "port");
+ sysfs_remove_link(&port_dev->dev.kobj, "device");
+
+ /*
+ * As usb_port_runtime_resume() de-references udev, make
+ * sure no resumes occur during removal
+ */
+ if (!test_and_set_bit(port1, hub->child_usage_bits))
+ pm_runtime_get_sync(&port_dev->dev);
+ }
+
usb_remove_ep_devs(&udev->ep0);
usb_unlock_device(udev);
@@ -1686,6 +2165,9 @@ void usb_disconnect(struct usb_device **pdev)
*pdev = NULL;
spin_unlock_irq(&device_state_lock);
+ if (port_dev && test_and_clear_bit(port1, hub->child_usage_bits))
+ pm_runtime_put(&port_dev->dev);
+
hub_free_dev(udev);
put_device(&udev->dev);
@@ -1696,7 +2178,7 @@ static void show_string(struct usb_device *udev, char *id, char *string)
{
if (!string)
return;
- dev_printk(KERN_INFO, &udev->dev, "%s: %s\n", id, string);
+ dev_info(&udev->dev, "%s: %s\n", id, string);
}
static void announce_device(struct usb_device *udev)
@@ -1726,6 +2208,8 @@ static inline void announce_device(struct usb_device *udev) { }
* @udev: newly addressed device (in ADDRESS state)
*
* Finish enumeration for On-The-Go devices
+ *
+ * Return: 0 if successful. A negative error code otherwise.
*/
static int usb_enumerate_device_otg(struct usb_device *udev)
{
@@ -1808,6 +2292,8 @@ fail:
* If the device is WUSB and not authorized, we don't attempt to read
* the string descriptors, as they will be errored out by the device
* until it has been authorized.
+ *
+ * Return: 0 if successful. A negative error code otherwise.
*/
static int usb_enumerate_device(struct usb_device *udev)
{
@@ -1816,28 +2302,76 @@ static int usb_enumerate_device(struct usb_device *udev)
if (udev->config == NULL) {
err = usb_get_configuration(udev);
if (err < 0) {
- dev_err(&udev->dev, "can't read configurations, error %d\n",
- err);
- goto fail;
+ if (err != -ENODEV)
+ dev_err(&udev->dev, "can't read configurations, error %d\n",
+ err);
+ return err;
}
}
- if (udev->wusb == 1 && udev->authorized == 0) {
- udev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
- udev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
- udev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
- }
- else {
- /* read the standard strings and cache them if present */
- udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
- udev->manufacturer = usb_cache_string(udev,
- udev->descriptor.iManufacturer);
- udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
- }
+
+ /* read the standard strings and cache them if present */
+ udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
+ udev->manufacturer = usb_cache_string(udev,
+ udev->descriptor.iManufacturer);
+ udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
+
err = usb_enumerate_device_otg(udev);
-fail:
- return err;
+ if (err < 0)
+ return err;
+
+ usb_detect_interface_quirks(udev);
+
+ return 0;
}
+static void set_usb_port_removable(struct usb_device *udev)
+{
+ struct usb_device *hdev = udev->parent;
+ struct usb_hub *hub;
+ u8 port = udev->portnum;
+ u16 wHubCharacteristics;
+ bool removable = true;
+
+ if (!hdev)
+ return;
+
+ hub = usb_hub_to_struct_hub(udev->parent);
+
+ wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
+
+ if (!(wHubCharacteristics & HUB_CHAR_COMPOUND))
+ return;
+
+ if (hub_is_superspeed(hdev)) {
+ 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)))
+ removable = false;
+ }
+
+ if (removable)
+ udev->removable = USB_DEVICE_REMOVABLE;
+ else
+ udev->removable = USB_DEVICE_FIXED;
+
+ /*
+ * Platform firmware may have populated an alternative value for
+ * removable. If the parent port has a known connect_type use
+ * that instead.
+ */
+ switch (hub->ports[udev->portnum - 1]->connect_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: /* use what was set above */
+ break;
+ }
+}
/**
* usb_new_device - perform initial device setup (usbcore-internal)
@@ -1851,13 +2385,14 @@ fail:
* udev has already been installed, but udev is not yet visible through
* sysfs or other filesystem code.
*
- * It will return if the device is configured properly or not. Zero if
- * the interface was registered with the driver core; else a negative
- * errno value.
- *
* This call is synchronous, and may not be used in an interrupt context.
*
* Only the hub driver or root-hub registrar should ever call this.
+ *
+ * Return: Whether the device is configured properly or not. Zero if the
+ * interface was registered with the driver core; else a negative errno
+ * value.
+ *
*/
int usb_new_device(struct usb_device *udev)
{
@@ -1895,7 +2430,20 @@ int usb_new_device(struct usb_device *udev)
/* Tell the world! */
announce_device(udev);
+ if (udev->serial)
+ add_device_randomness(udev->serial, strlen(udev->serial));
+ if (udev->product)
+ add_device_randomness(udev->product, strlen(udev->product));
+ if (udev->manufacturer)
+ add_device_randomness(udev->manufacturer,
+ strlen(udev->manufacturer));
+
device_enable_async_suspend(&udev->dev);
+
+ /* check whether the hub or firmware marks this port as non-removable */
+ if (udev->parent)
+ set_usb_port_removable(udev);
+
/* Register the device. The device driver is responsible
* for configuring the device and invoking the add-device
* notifier chain (used by usbfs and possibly others).
@@ -1906,6 +2454,28 @@ int usb_new_device(struct usb_device *udev)
goto fail;
}
+ /* Create link files between child device and usb port device. */
+ if (udev->parent) {
+ struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
+ int port1 = udev->portnum;
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+
+ err = sysfs_create_link(&udev->dev.kobj,
+ &port_dev->dev.kobj, "port");
+ if (err)
+ goto fail;
+
+ err = sysfs_create_link(&port_dev->dev.kobj,
+ &udev->dev.kobj, "device");
+ if (err) {
+ sysfs_remove_link(&udev->dev.kobj, "port");
+ goto fail;
+ }
+
+ if (!test_and_set_bit(port1, hub->child_usage_bits))
+ pm_runtime_get_sync(&port_dev->dev);
+ }
+
(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
usb_mark_last_busy(udev);
pm_runtime_put_sync_autosuspend(&udev->dev);
@@ -1928,6 +2498,8 @@ fail:
*
* We share a lock (that we have) with device_del(), so we need to
* defer its call.
+ *
+ * Return: 0.
*/
int usb_deauthorize_device(struct usb_device *usb_dev)
{
@@ -1938,16 +2510,6 @@ int usb_deauthorize_device(struct usb_device *usb_dev)
usb_dev->authorized = 0;
usb_set_configuration(usb_dev, -1);
- kfree(usb_dev->product);
- usb_dev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
- kfree(usb_dev->manufacturer);
- usb_dev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
- kfree(usb_dev->serial);
- usb_dev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
-
- usb_destroy_configuration(usb_dev);
- usb_dev->descriptor.bNumConfigurations = 0;
-
out_unauthorized:
usb_unlock_device(usb_dev);
return 0;
@@ -1975,17 +2537,7 @@ int usb_authorize_device(struct usb_device *usb_dev)
goto error_device_descriptor;
}
- kfree(usb_dev->product);
- usb_dev->product = NULL;
- kfree(usb_dev->manufacturer);
- usb_dev->manufacturer = NULL;
- kfree(usb_dev->serial);
- usb_dev->serial = NULL;
-
usb_dev->authorized = 1;
- result = usb_enumerate_device(usb_dev);
- if (result < 0)
- goto error_enumerate;
/* Choose and set the configuration. This registers the interfaces
* with the driver core and lets interface drivers bind to them.
*/
@@ -2001,12 +2553,11 @@ int usb_authorize_device(struct usb_device *usb_dev)
}
dev_info(&usb_dev->dev, "authorized to connect\n");
-error_enumerate:
error_device_descriptor:
usb_autosuspend_device(usb_dev);
error_autoresume:
out_authorized:
- usb_unlock_device(usb_dev); // complements locktree
+ usb_unlock_device(usb_dev); /* complements locktree */
return result;
}
@@ -2032,17 +2583,36 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
#define HUB_SHORT_RESET_TIME 10
#define HUB_BH_RESET_TIME 50
#define HUB_LONG_RESET_TIME 200
-#define HUB_RESET_TIMEOUT 500
+#define HUB_RESET_TIMEOUT 800
+
+/*
+ * "New scheme" enumeration causes an extra state transition to be
+ * exposed to an xhci host and causes USB3 devices to receive control
+ * commands in the default state. This has been seen to cause
+ * enumeration failures, so disable this enumeration scheme for USB3
+ * devices.
+ */
+static bool use_new_scheme(struct usb_device *udev, int retry)
+{
+ if (udev->speed == USB_SPEED_SUPER)
+ return false;
+
+ return USE_NEW_SCHEME(retry);
+}
static int hub_port_reset(struct usb_hub *hub, int port1,
struct usb_device *udev, unsigned int delay, bool warm);
-/* Is a USB 3.0 port in the Inactive state? */
-static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus)
+/* Is a USB 3.0 port in the Inactive or Compliance Mode state?
+ * Port worm reset is required to recover
+ */
+static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus)
{
return hub_is_superspeed(hub->hdev) &&
- (portstatus & USB_PORT_STAT_LINK_STATE) ==
- USB_SS_PORT_LS_SS_INACTIVE;
+ (((portstatus & USB_PORT_STAT_LINK_STATE) ==
+ USB_SS_PORT_LS_SS_INACTIVE) ||
+ ((portstatus & USB_PORT_STAT_LINK_STATE) ==
+ USB_SS_PORT_LS_COMP_MOD)) ;
}
static int hub_port_wait_reset(struct usb_hub *hub, int port1,
@@ -2063,121 +2633,90 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
if (ret < 0)
return ret;
- /*
- * Some buggy devices require a warm reset to be issued even
- * when the port appears not to be connected.
- */
- if (!warm) {
- /*
- * Some buggy devices can cause an NEC host controller
- * to transition to the "Error" state after a hot port
- * reset. This will show up as the port state in
- * "Inactive", and the port may also report a
- * disconnect. Forcing a warm port reset seems to make
- * the device work.
- *
- * See https://bugzilla.kernel.org/show_bug.cgi?id=41752
- */
- if (hub_port_inactive(hub, portstatus)) {
- int ret;
-
- if ((portchange & USB_PORT_STAT_C_CONNECTION))
- clear_port_feature(hub->hdev, port1,
- USB_PORT_FEAT_C_CONNECTION);
- if (portchange & USB_PORT_STAT_C_LINK_STATE)
- clear_port_feature(hub->hdev, port1,
- USB_PORT_FEAT_C_PORT_LINK_STATE);
- if (portchange & USB_PORT_STAT_C_RESET)
- clear_port_feature(hub->hdev, port1,
- USB_PORT_FEAT_C_RESET);
- dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
- port1);
- ret = hub_port_reset(hub, port1,
- udev, HUB_BH_RESET_TIME,
- true);
- if ((portchange & USB_PORT_STAT_C_CONNECTION))
- clear_port_feature(hub->hdev, port1,
- USB_PORT_FEAT_C_CONNECTION);
- return ret;
- }
- /* Device went away? */
- if (!(portstatus & USB_PORT_STAT_CONNECTION))
- return -ENOTCONN;
-
- /* bomb out completely if the connection bounced */
- if ((portchange & USB_PORT_STAT_C_CONNECTION))
- return -ENOTCONN;
-
- /* if we`ve finished resetting, then break out of
- * the loop
- */
- if (!(portstatus & USB_PORT_STAT_RESET) &&
- (portstatus & USB_PORT_STAT_ENABLE)) {
- if (hub_is_wusb(hub))
- udev->speed = USB_SPEED_WIRELESS;
- else if (hub_is_superspeed(hub->hdev))
- udev->speed = USB_SPEED_SUPER;
- else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
- udev->speed = USB_SPEED_HIGH;
- else if (portstatus & USB_PORT_STAT_LOW_SPEED)
- udev->speed = USB_SPEED_LOW;
- else
- udev->speed = USB_SPEED_FULL;
- return 0;
- }
- } else {
- if (portchange & USB_PORT_STAT_C_BH_RESET)
- return 0;
- }
+ /* The port state is unknown until the reset completes. */
+ if (!(portstatus & USB_PORT_STAT_RESET))
+ break;
/* switch to the long delay after two short delay failures */
if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
delay = HUB_LONG_RESET_TIME;
- dev_dbg (hub->intfdev,
- "port %d not %sreset yet, waiting %dms\n",
- port1, warm ? "warm " : "", delay);
+ dev_dbg(&hub->ports[port1 - 1]->dev,
+ "not %sreset yet, waiting %dms\n",
+ warm ? "warm " : "", delay);
}
- return -EBUSY;
+ if ((portstatus & USB_PORT_STAT_RESET))
+ return -EBUSY;
+
+ if (hub_port_warm_reset_required(hub, portstatus))
+ return -ENOTCONN;
+
+ /* Device went away? */
+ if (!(portstatus & USB_PORT_STAT_CONNECTION))
+ return -ENOTCONN;
+
+ /* bomb out completely if the connection bounced. A USB 3.0
+ * connection may bounce if multiple warm resets were issued,
+ * but the device may have successfully re-connected. Ignore it.
+ */
+ if (!hub_is_superspeed(hub->hdev) &&
+ (portchange & USB_PORT_STAT_C_CONNECTION))
+ return -ENOTCONN;
+
+ if (!(portstatus & USB_PORT_STAT_ENABLE))
+ return -EBUSY;
+
+ if (!udev)
+ return 0;
+
+ if (hub_is_wusb(hub))
+ udev->speed = USB_SPEED_WIRELESS;
+ else if (hub_is_superspeed(hub->hdev))
+ udev->speed = USB_SPEED_SUPER;
+ else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
+ udev->speed = USB_SPEED_HIGH;
+ else if (portstatus & USB_PORT_STAT_LOW_SPEED)
+ udev->speed = USB_SPEED_LOW;
+ else
+ udev->speed = USB_SPEED_FULL;
+ return 0;
}
static void hub_port_finish_reset(struct usb_hub *hub, int port1,
- struct usb_device *udev, int *status, bool warm)
+ struct usb_device *udev, int *status)
{
switch (*status) {
case 0:
- if (!warm) {
- struct usb_hcd *hcd;
- /* TRSTRCY = 10 ms; plus some extra */
- msleep(10 + 40);
+ /* TRSTRCY = 10 ms; plus some extra */
+ msleep(10 + 40);
+ if (udev) {
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
update_devnum(udev, 0);
- hcd = bus_to_hcd(udev->bus);
- if (hcd->driver->reset_device) {
- *status = hcd->driver->reset_device(hcd, udev);
- if (*status < 0) {
- dev_err(&udev->dev, "Cannot reset "
- "HCD device state\n");
- break;
- }
- }
+ /* The xHC may think the device is already reset,
+ * so ignore the status.
+ */
+ if (hcd->driver->reset_device)
+ hcd->driver->reset_device(hcd, udev);
}
/* FALL THROUGH */
case -ENOTCONN:
case -ENODEV:
- clear_port_feature(hub->hdev,
+ usb_clear_port_feature(hub->hdev,
port1, USB_PORT_FEAT_C_RESET);
- /* FIXME need disconnect() for NOTATTACHED device */
- if (warm) {
- clear_port_feature(hub->hdev, port1,
+ if (hub_is_superspeed(hub->hdev)) {
+ usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_BH_PORT_RESET);
- clear_port_feature(hub->hdev, port1,
+ usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_PORT_LINK_STATE);
- } else {
+ usb_clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_CONNECTION);
+ }
+ if (udev)
usb_set_device_state(udev, *status
? USB_STATE_NOTATTACHED
: USB_STATE_DEFAULT);
- }
break;
}
}
@@ -2187,18 +2726,31 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
struct usb_device *udev, unsigned int delay, bool warm)
{
int i, status;
+ u16 portchange, portstatus;
+ struct usb_port *port_dev = hub->ports[port1 - 1];
- if (!warm) {
- /* Block EHCI CF initialization during the port reset.
- * Some companion controllers don't like it when they mix.
- */
- down_read(&ehci_cf_port_reset_rwsem);
- } else {
- if (!hub_is_superspeed(hub->hdev)) {
+ if (!hub_is_superspeed(hub->hdev)) {
+ if (warm) {
dev_err(hub->intfdev, "only USB3 hub support "
"warm reset\n");
return -EINVAL;
}
+ /* Block EHCI CF initialization during the port reset.
+ * Some companion controllers don't like it when they mix.
+ */
+ down_read(&ehci_cf_port_reset_rwsem);
+ } else if (!warm) {
+ /*
+ * If the caller hasn't explicitly requested a warm reset,
+ * double check and see if one is needed.
+ */
+ status = hub_port_status(hub, port1,
+ &portstatus, &portchange);
+ if (status < 0)
+ goto done;
+
+ if (hub_port_warm_reset_required(hub, portstatus))
+ warm = true;
}
/* Reset the port */
@@ -2206,37 +2758,60 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
status = set_port_feature(hub->hdev, port1, (warm ?
USB_PORT_FEAT_BH_PORT_RESET :
USB_PORT_FEAT_RESET));
- if (status) {
- dev_err(hub->intfdev,
- "cannot %sreset port %d (err = %d)\n",
- warm ? "warm " : "", port1, status);
+ if (status == -ENODEV) {
+ ; /* The hub is gone */
+ } else if (status) {
+ dev_err(&port_dev->dev,
+ "cannot %sreset (err = %d)\n",
+ warm ? "warm " : "", status);
} else {
status = hub_port_wait_reset(hub, port1, udev, delay,
warm);
- if (status && status != -ENOTCONN)
+ if (status && status != -ENOTCONN && status != -ENODEV)
dev_dbg(hub->intfdev,
"port_wait_reset: err = %d\n",
status);
}
- /* return on disconnect or reset */
+ /* Check for disconnect or reset */
if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
- hub_port_finish_reset(hub, port1, udev, &status, warm);
- goto done;
+ hub_port_finish_reset(hub, port1, udev, &status);
+
+ if (!hub_is_superspeed(hub->hdev))
+ goto done;
+
+ /*
+ * If a USB 3.0 device migrates from reset to an error
+ * state, re-issue the warm reset.
+ */
+ if (hub_port_status(hub, port1,
+ &portstatus, &portchange) < 0)
+ goto done;
+
+ if (!hub_port_warm_reset_required(hub, portstatus))
+ goto done;
+
+ /*
+ * If the port is in SS.Inactive or Compliance Mode, the
+ * hot or warm reset failed. Try another warm reset.
+ */
+ if (!warm) {
+ dev_dbg(&port_dev->dev,
+ "hot reset failed, warm reset\n");
+ warm = true;
+ }
}
- dev_dbg (hub->intfdev,
- "port %d not enabled, trying %sreset again...\n",
- port1, warm ? "warm " : "");
+ dev_dbg(&port_dev->dev,
+ "not enabled, trying %sreset again...\n",
+ warm ? "warm " : "");
delay = HUB_LONG_RESET_TIME;
}
- dev_err (hub->intfdev,
- "Cannot enable port %i. Maybe the USB cable is bad?\n",
- port1);
+ dev_err(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n");
done:
- if (!warm)
+ if (!hub_is_superspeed(hub->hdev))
up_read(&ehci_cf_port_reset_rwsem);
return status;
@@ -2258,6 +2833,20 @@ static int port_is_power_on(struct usb_hub *hub, unsigned portstatus)
return ret;
}
+static void usb_lock_port(struct usb_port *port_dev)
+ __acquires(&port_dev->status_lock)
+{
+ mutex_lock(&port_dev->status_lock);
+ __acquire(&port_dev->status_lock);
+}
+
+static void usb_unlock_port(struct usb_port *port_dev)
+ __releases(&port_dev->status_lock)
+{
+ mutex_unlock(&port_dev->status_lock);
+ __release(&port_dev->status_lock);
+}
+
#ifdef CONFIG_PM
/* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */
@@ -2284,6 +2873,8 @@ static int check_port_resume_type(struct usb_device *udev,
struct usb_hub *hub, int port1,
int status, unsigned portchange, unsigned portstatus)
{
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+
/* Is the device still present? */
if (status || port_is_suspended(hub, portstatus) ||
!port_is_power_on(hub, portstatus) ||
@@ -2303,24 +2894,124 @@ static int check_port_resume_type(struct usb_device *udev,
}
if (status) {
- dev_dbg(hub->intfdev,
- "port %d status %04x.%04x after resume, %d\n",
- port1, portchange, portstatus, status);
+ dev_dbg(&port_dev->dev, "status %04x.%04x after resume, %d\n",
+ portchange, portstatus, status);
} else if (udev->reset_resume) {
/* Late port handoff can set status-change bits */
if (portchange & USB_PORT_STAT_C_CONNECTION)
- clear_port_feature(hub->hdev, port1,
+ usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
if (portchange & USB_PORT_STAT_C_ENABLE)
- clear_port_feature(hub->hdev, port1,
+ usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_ENABLE);
}
return status;
}
-#ifdef CONFIG_USB_SUSPEND
+int usb_disable_ltm(struct usb_device *udev)
+{
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+ /* Check if the roothub and device supports LTM. */
+ if (!usb_device_supports_ltm(hcd->self.root_hub) ||
+ !usb_device_supports_ltm(udev))
+ return 0;
+
+ /* Clear Feature LTM Enable can only be sent if the device is
+ * configured.
+ */
+ if (!udev->actconfig)
+ return 0;
+
+ return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
+ USB_DEVICE_LTM_ENABLE, 0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+}
+EXPORT_SYMBOL_GPL(usb_disable_ltm);
+
+void usb_enable_ltm(struct usb_device *udev)
+{
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+ /* Check if the roothub and device supports LTM. */
+ if (!usb_device_supports_ltm(hcd->self.root_hub) ||
+ !usb_device_supports_ltm(udev))
+ return;
+
+ /* Set Feature LTM Enable can only be sent if the device is
+ * configured.
+ */
+ if (!udev->actconfig)
+ return;
+
+ usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
+ USB_DEVICE_LTM_ENABLE, 0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+}
+EXPORT_SYMBOL_GPL(usb_enable_ltm);
+
+/*
+ * usb_enable_remote_wakeup - enable remote wakeup for a device
+ * @udev: target device
+ *
+ * For USB-2 devices: Set the device's remote wakeup feature.
+ *
+ * For USB-3 devices: Assume there's only one function on the device and
+ * enable remote wake for the first interface. FIXME if the interface
+ * association descriptor shows there's more than one function.
+ */
+static int usb_enable_remote_wakeup(struct usb_device *udev)
+{
+ if (udev->speed < USB_SPEED_SUPER)
+ return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
+ USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+ else
+ return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_SET_FEATURE, USB_RECIP_INTERFACE,
+ USB_INTRF_FUNC_SUSPEND,
+ USB_INTRF_FUNC_SUSPEND_RW |
+ USB_INTRF_FUNC_SUSPEND_LP,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+/*
+ * usb_disable_remote_wakeup - disable remote wakeup for a device
+ * @udev: target device
+ *
+ * For USB-2 devices: Clear the device's remote wakeup feature.
+ *
+ * For USB-3 devices: Assume there's only one function on the device and
+ * disable remote wake for the first interface. FIXME if the interface
+ * association descriptor shows there's more than one function.
+ */
+static int usb_disable_remote_wakeup(struct usb_device *udev)
+{
+ if (udev->speed < USB_SPEED_SUPER)
+ return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
+ USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+ else
+ return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_CLEAR_FEATURE, USB_RECIP_INTERFACE,
+ USB_INTRF_FUNC_SUSPEND, 0, NULL, 0,
+ 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
@@ -2362,17 +3053,23 @@ static int check_port_resume_type(struct usb_device *udev,
* Linux (2.6) currently has NO mechanisms to initiate that: no khubd
* timer, no SRP, no requests through sysfs.
*
- * If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when
- * the root hub for their bus goes into global suspend ... so we don't
- * (falsely) update the device power state to say it suspended.
+ * 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.
*
* Returns 0 on success, else negative errno.
*/
int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
{
- struct usb_hub *hub = hdev_to_hub(udev->parent);
+ struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
+ struct usb_port *port_dev = hub->ports[udev->portnum - 1];
int port1 = udev->portnum;
int status;
+ bool really_suspend = true;
+
+ usb_lock_port(port_dev);
/* enable remote wakeup when appropriate; this lets the device
* wake up the upstream hub (including maybe the root hub).
@@ -2381,17 +3078,13 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
* we don't explicitly enable it here.
*/
if (udev->do_remote_wakeup) {
- status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
- USB_DEVICE_REMOTE_WAKEUP, 0,
- NULL, 0,
- USB_CTRL_SET_TIMEOUT);
+ status = usb_enable_remote_wakeup(udev);
if (status) {
dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
status);
/* bail if autosuspend is requested */
if (PMSG_IS_AUTO(msg))
- return status;
+ goto err_wakeup;
}
}
@@ -2399,37 +3092,80 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
if (udev->usb2_hw_lpm_enabled == 1)
usb_set_usb2_hardware_lpm(udev, 0);
+ if (usb_disable_ltm(udev)) {
+ dev_err(&udev->dev, "Failed to disable LTM before suspend\n.");
+ status = -ENOMEM;
+ if (PMSG_IS_AUTO(msg))
+ goto err_ltm;
+ }
+ if (usb_unlocked_disable_lpm(udev)) {
+ dev_err(&udev->dev, "Failed to disable LPM before suspend\n.");
+ status = -ENOMEM;
+ if (PMSG_IS_AUTO(msg))
+ goto err_lpm3;
+ }
+
/* see 7.1.7.6 */
if (hub_is_superspeed(hub->hdev))
- status = set_port_feature(hub->hdev,
- port1 | (USB_SS_PORT_LS_U3 << 3),
- USB_PORT_FEAT_LINK_STATE);
- else
+ status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3);
+
+ /*
+ * 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);
+ USB_PORT_FEAT_SUSPEND);
+ else {
+ really_suspend = false;
+ status = 0;
+ }
if (status) {
- dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
- port1, status);
- /* paranoia: "should not happen" */
+ dev_dbg(&port_dev->dev, "can't suspend, status %d\n", status);
+
+ /* Try to enable USB3 LPM and LTM again */
+ usb_unlocked_enable_lpm(udev);
+ err_lpm3:
+ usb_enable_ltm(udev);
+ err_ltm:
+ /* Try to enable USB2 hardware LPM again */
+ if (udev->usb2_hw_lpm_capable == 1)
+ usb_set_usb2_hardware_lpm(udev, 1);
+
if (udev->do_remote_wakeup)
- (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
- USB_DEVICE_REMOTE_WAKEUP, 0,
- NULL, 0,
- USB_CTRL_SET_TIMEOUT);
+ (void) usb_disable_remote_wakeup(udev);
+ err_wakeup:
/* System sleep transitions should never fail */
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);
+ 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);
- msleep(10);
}
+
+ if (status == 0 && !udev->do_remote_wakeup && udev->persist_enabled
+ && test_and_clear_bit(port1, hub->child_usage_bits))
+ pm_runtime_put_sync(&port_dev->dev);
+
usb_mark_last_busy(hub->hdev);
+
+ usb_unlock_port(port_dev);
return status;
}
@@ -2447,7 +3183,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
static int finish_port_resume(struct usb_device *udev)
{
int status = 0;
- u16 devstatus;
+ u16 devstatus = 0;
/* caller owns the udev device lock */
dev_dbg(&udev->dev, "%s\n",
@@ -2467,19 +3203,27 @@ static int finish_port_resume(struct usb_device *udev)
* operation is carried out here, after the port has been
* resumed.
*/
- if (udev->reset_resume)
+ if (udev->reset_resume) {
+ /*
+ * If the device morphs or switches modes when it is reset,
+ * we don't want to perform a reset-resume. We'll fail the
+ * resume, which will cause a logical disconnect, and then
+ * the device will be rediscovered.
+ */
retry_reset_resume:
- status = usb_reset_and_verify_device(udev);
+ if (udev->quirks & USB_QUIRK_RESET)
+ status = -ENODEV;
+ else
+ status = usb_reset_and_verify_device(udev);
+ }
- /* 10.5.4.5 says be sure devices in the tree are still there.
- * For now let's assume the device didn't go crazy on resume,
+ /* 10.5.4.5 says be sure devices in the tree are still there.
+ * For now let's assume the device didn't go crazy on resume,
* and device drivers will know about any resume quirks.
*/
if (status == 0) {
devstatus = 0;
status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
- if (status >= 0)
- status = (status > 0 ? 0 : -ENODEV);
/* If a normal resume failed, try doing a reset-resume */
if (status && !udev->reset_resume && udev->persist_enabled) {
@@ -2492,21 +3236,28 @@ static int finish_port_resume(struct usb_device *udev)
if (status) {
dev_dbg(&udev->dev, "gone after usb resume? status %d\n",
status);
- } else if (udev->actconfig) {
- le16_to_cpus(&devstatus);
- if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
- status = usb_control_msg(udev,
- usb_sndctrlpipe(udev, 0),
- USB_REQ_CLEAR_FEATURE,
- USB_RECIP_DEVICE,
- USB_DEVICE_REMOTE_WAKEUP, 0,
- NULL, 0,
- USB_CTRL_SET_TIMEOUT);
- if (status)
- dev_dbg(&udev->dev,
- "disable remote wakeup, status %d\n",
- status);
+ /*
+ * There are a few quirky devices which violate the standard
+ * by claiming to have remote wakeup enabled after a reset,
+ * which crash if the feature is cleared, hence check for
+ * udev->reset_resume
+ */
+ } else if (udev->actconfig && !udev->reset_resume) {
+ if (udev->speed < USB_SPEED_SUPER) {
+ if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
+ status = usb_disable_remote_wakeup(udev);
+ } else {
+ status = usb_get_status(udev, USB_RECIP_INTERFACE, 0,
+ &devstatus);
+ if (!status && devstatus & (USB_INTRF_STAT_FUNC_RW_CAP
+ | USB_INTRF_STAT_FUNC_RW))
+ status = usb_disable_remote_wakeup(udev);
}
+
+ if (status)
+ dev_dbg(&udev->dev,
+ "disable remote wakeup, status %d\n",
+ status);
status = 0;
}
return status;
@@ -2548,31 +3299,36 @@ static int finish_port_resume(struct usb_device *udev)
*/
int usb_port_resume(struct usb_device *udev, pm_message_t msg)
{
- struct usb_hub *hub = hdev_to_hub(udev->parent);
+ struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
+ struct usb_port *port_dev = hub->ports[udev->portnum - 1];
int port1 = udev->portnum;
int status;
u16 portchange, portstatus;
+ if (!test_and_set_bit(port1, hub->child_usage_bits)) {
+ status = pm_runtime_get_sync(&port_dev->dev);
+ if (status < 0) {
+ dev_dbg(&udev->dev, "can't resume usb port, status %d\n",
+ status);
+ return status;
+ }
+ }
+
+ usb_lock_port(port_dev);
+
/* Skip the initial Clear-Suspend step for a remote wakeup */
status = hub_port_status(hub, port1, &portstatus, &portchange);
if (status == 0 && !port_is_suspended(hub, portstatus))
goto SuspendCleared;
- // dev_dbg(hub->intfdev, "resume port %d\n", port1);
-
- set_bit(port1, hub->busy_bits);
-
/* see 7.1.7.7; affects power usage, but not budgeting */
if (hub_is_superspeed(hub->hdev))
- status = set_port_feature(hub->hdev,
- port1 | (USB_SS_PORT_LS_U0 << 3),
- USB_PORT_FEAT_LINK_STATE);
+ status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U0);
else
- status = clear_port_feature(hub->hdev,
+ status = usb_clear_port_feature(hub->hdev,
port1, USB_PORT_FEAT_SUSPEND);
if (status) {
- dev_dbg(hub->intfdev, "can't resume port %d, status %d\n",
- port1, status);
+ dev_dbg(&port_dev->dev, "can't resume, status %d\n", status);
} else {
/* drive resume for at least 20 msec */
dev_dbg(&udev->dev, "usb %sresume\n",
@@ -2591,19 +3347,18 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
SuspendCleared:
if (status == 0) {
+ udev->port_is_suspended = 0;
if (hub_is_superspeed(hub->hdev)) {
if (portchange & USB_PORT_STAT_C_LINK_STATE)
- clear_port_feature(hub->hdev, port1,
+ usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_PORT_LINK_STATE);
} else {
if (portchange & USB_PORT_STAT_C_SUSPEND)
- clear_port_feature(hub->hdev, port1,
+ usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_SUSPEND);
}
}
- clear_bit(port1, hub->busy_bits);
-
status = check_port_resume_type(udev,
hub, port1, status, portchange, portstatus);
if (status == 0)
@@ -2615,16 +3370,24 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
/* Try to enable USB2 hardware LPM */
if (udev->usb2_hw_lpm_capable == 1)
usb_set_usb2_hardware_lpm(udev, 1);
+
+ /* Try to enable USB3 LTM and LPM */
+ usb_enable_ltm(udev);
+ usb_unlocked_enable_lpm(udev);
}
+ usb_unlock_port(port_dev);
+
return status;
}
-/* caller has locked udev */
+#ifdef CONFIG_PM_RUNTIME
+
int usb_remote_wakeup(struct usb_device *udev)
{
int status = 0;
+ usb_lock_device(udev);
if (udev->state == USB_STATE_SUSPENDED) {
dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
status = usb_autoresume_device(udev);
@@ -2633,58 +3396,121 @@ int usb_remote_wakeup(struct usb_device *udev)
usb_autosuspend_device(udev);
}
}
+ usb_unlock_device(udev);
return status;
}
-#else /* CONFIG_USB_SUSPEND */
+/* Returns 1 if there was a remote wakeup and a connect status change. */
+static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
+ u16 portstatus, u16 portchange)
+ __must_hold(&port_dev->status_lock)
+{
+ struct usb_port *port_dev = hub->ports[port - 1];
+ struct usb_device *hdev;
+ struct usb_device *udev;
+ int connect_change = 0;
+ int ret;
+
+ hdev = hub->hdev;
+ udev = port_dev->child;
+ if (!hub_is_superspeed(hdev)) {
+ if (!(portchange & USB_PORT_STAT_C_SUSPEND))
+ return 0;
+ usb_clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND);
+ } else {
+ if (!udev || udev->state != USB_STATE_SUSPENDED ||
+ (portstatus & USB_PORT_STAT_LINK_STATE) !=
+ USB_SS_PORT_LS_U0)
+ return 0;
+ }
-/* When CONFIG_USB_SUSPEND isn't set, we never suspend or resume any ports. */
+ if (udev) {
+ /* TRSMRCY = 10 msec */
+ msleep(10);
-int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
+ usb_unlock_port(port_dev);
+ ret = usb_remote_wakeup(udev);
+ usb_lock_port(port_dev);
+ if (ret < 0)
+ connect_change = 1;
+ } else {
+ ret = -ENODEV;
+ hub_port_disable(hub, port, 1);
+ }
+ dev_dbg(&port_dev->dev, "resume, status %d\n", ret);
+ return connect_change;
+}
+
+#else
+
+static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
+ u16 portstatus, u16 portchange)
{
return 0;
}
-/* However we may need to do a reset-resume */
+#endif
-int usb_port_resume(struct usb_device *udev, pm_message_t msg)
+static int check_ports_changed(struct usb_hub *hub)
{
- struct usb_hub *hub = hdev_to_hub(udev->parent);
- int port1 = udev->portnum;
- int status;
- u16 portchange, portstatus;
+ int port1;
- status = hub_port_status(hub, port1, &portstatus, &portchange);
- status = check_port_resume_type(udev,
- hub, port1, status, portchange, portstatus);
+ for (port1 = 1; port1 <= hub->hdev->maxchild; ++port1) {
+ u16 portstatus, portchange;
+ int status;
- if (status) {
- dev_dbg(&udev->dev, "can't resume, status %d\n", status);
- hub_port_logical_disconnect(hub, port1);
- } else if (udev->reset_resume) {
- dev_dbg(&udev->dev, "reset-resume\n");
- status = usb_reset_and_verify_device(udev);
+ status = hub_port_status(hub, port1, &portstatus, &portchange);
+ if (!status && portchange)
+ return 1;
}
- return status;
+ return 0;
}
-#endif
-
static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
{
struct usb_hub *hub = usb_get_intfdata (intf);
struct usb_device *hdev = hub->hdev;
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;
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+ struct usb_device *udev = port_dev->child;
- udev = hdev->children [port1-1];
if (udev && udev->can_submit) {
- dev_warn(&intf->dev, "port %d nyet suspended\n", port1);
+ dev_warn(&port_dev->dev, "device %s not suspended yet\n",
+ dev_name(&udev->dev));
+ 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) {
+ /* check if there are changes pending on hub ports */
+ if (check_ports_changed(hub)) {
if (PMSG_IS_AUTO(msg))
return -EBUSY;
+ pm_wakeup_event(&hdev->dev, 2000);
+ }
+ }
+
+ if (hub_is_superspeed(hdev) && hdev->do_remote_wakeup) {
+ /* Enable hub to send remote wakeup for all ports. */
+ for (port1 = 1; port1 <= hdev->maxchild; port1++) {
+ status = set_port_feature(hdev,
+ port1 |
+ USB_PORT_FEAT_REMOTE_WAKE_CONNECT |
+ USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT |
+ USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT,
+ USB_PORT_FEAT_REMOTE_WAKE_MASK);
}
}
@@ -2731,12 +3557,459 @@ void usb_root_hub_lost_power(struct usb_device *rhdev)
}
EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
+static const char * const usb3_lpm_names[] = {
+ "U0",
+ "U1",
+ "U2",
+ "U3",
+};
+
+/*
+ * Send a Set SEL control transfer to the device, prior to enabling
+ * device-initiated U1 or U2. This lets the device know the exit latencies from
+ * the time the device initiates a U1 or U2 exit, to the time it will receive a
+ * packet from the host.
+ *
+ * This function will fail if the SEL or PEL values for udev are greater than
+ * the maximum allowed values for the link state to be enabled.
+ */
+static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state)
+{
+ struct usb_set_sel_req *sel_values;
+ unsigned long long u1_sel;
+ unsigned long long u1_pel;
+ unsigned long long u2_sel;
+ unsigned long long u2_pel;
+ int ret;
+
+ if (udev->state != USB_STATE_CONFIGURED)
+ return 0;
+
+ /* Convert SEL and PEL stored in ns to us */
+ u1_sel = DIV_ROUND_UP(udev->u1_params.sel, 1000);
+ u1_pel = DIV_ROUND_UP(udev->u1_params.pel, 1000);
+ u2_sel = DIV_ROUND_UP(udev->u2_params.sel, 1000);
+ u2_pel = DIV_ROUND_UP(udev->u2_params.pel, 1000);
+
+ /*
+ * Make sure that the calculated SEL and PEL values for the link
+ * state we're enabling aren't bigger than the max SEL/PEL
+ * value that will fit in the SET SEL control transfer.
+ * Otherwise the device would get an incorrect idea of the exit
+ * latency for the link state, and could start a device-initiated
+ * U1/U2 when the exit latencies are too high.
+ */
+ if ((state == USB3_LPM_U1 &&
+ (u1_sel > USB3_LPM_MAX_U1_SEL_PEL ||
+ u1_pel > USB3_LPM_MAX_U1_SEL_PEL)) ||
+ (state == USB3_LPM_U2 &&
+ (u2_sel > USB3_LPM_MAX_U2_SEL_PEL ||
+ u2_pel > USB3_LPM_MAX_U2_SEL_PEL))) {
+ dev_dbg(&udev->dev, "Device-initiated %s disabled due to long SEL %llu us or PEL %llu us\n",
+ usb3_lpm_names[state], u1_sel, u1_pel);
+ return -EINVAL;
+ }
+
+ /*
+ * If we're enabling device-initiated LPM for one link state,
+ * but the other link state has a too high SEL or PEL value,
+ * just set those values to the max in the Set SEL request.
+ */
+ if (u1_sel > USB3_LPM_MAX_U1_SEL_PEL)
+ u1_sel = USB3_LPM_MAX_U1_SEL_PEL;
+
+ if (u1_pel > USB3_LPM_MAX_U1_SEL_PEL)
+ u1_pel = USB3_LPM_MAX_U1_SEL_PEL;
+
+ if (u2_sel > USB3_LPM_MAX_U2_SEL_PEL)
+ u2_sel = USB3_LPM_MAX_U2_SEL_PEL;
+
+ if (u2_pel > USB3_LPM_MAX_U2_SEL_PEL)
+ u2_pel = USB3_LPM_MAX_U2_SEL_PEL;
+
+ /*
+ * usb_enable_lpm() can be called as part of a failed device reset,
+ * which may be initiated by an error path of a mass storage driver.
+ * Therefore, use GFP_NOIO.
+ */
+ sel_values = kmalloc(sizeof *(sel_values), GFP_NOIO);
+ if (!sel_values)
+ return -ENOMEM;
+
+ sel_values->u1_sel = u1_sel;
+ sel_values->u1_pel = u1_pel;
+ sel_values->u2_sel = cpu_to_le16(u2_sel);
+ sel_values->u2_pel = cpu_to_le16(u2_pel);
+
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_SET_SEL,
+ USB_RECIP_DEVICE,
+ 0, 0,
+ sel_values, sizeof *(sel_values),
+ USB_CTRL_SET_TIMEOUT);
+ kfree(sel_values);
+ return ret;
+}
+
+/*
+ * Enable or disable device-initiated U1 or U2 transitions.
+ */
+static int usb_set_device_initiated_lpm(struct usb_device *udev,
+ enum usb3_link_state state, bool enable)
+{
+ int ret;
+ int feature;
+
+ switch (state) {
+ case USB3_LPM_U1:
+ feature = USB_DEVICE_U1_ENABLE;
+ break;
+ case USB3_LPM_U2:
+ feature = USB_DEVICE_U2_ENABLE;
+ break;
+ default:
+ dev_warn(&udev->dev, "%s: Can't %s non-U1 or U2 state.\n",
+ __func__, enable ? "enable" : "disable");
+ return -EINVAL;
+ }
+
+ if (udev->state != USB_STATE_CONFIGURED) {
+ dev_dbg(&udev->dev, "%s: Can't %s %s state "
+ "for unconfigured device.\n",
+ __func__, enable ? "enable" : "disable",
+ usb3_lpm_names[state]);
+ return 0;
+ }
+
+ if (enable) {
+ /*
+ * Now send the control transfer to enable device-initiated LPM
+ * for either U1 or U2.
+ */
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_SET_FEATURE,
+ USB_RECIP_DEVICE,
+ feature,
+ 0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+ } else {
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_CLEAR_FEATURE,
+ USB_RECIP_DEVICE,
+ feature,
+ 0, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+ }
+ if (ret < 0) {
+ dev_warn(&udev->dev, "%s of device-initiated %s failed.\n",
+ enable ? "Enable" : "Disable",
+ usb3_lpm_names[state]);
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static int usb_set_lpm_timeout(struct usb_device *udev,
+ enum usb3_link_state state, int timeout)
+{
+ int ret;
+ int feature;
+
+ switch (state) {
+ case USB3_LPM_U1:
+ feature = USB_PORT_FEAT_U1_TIMEOUT;
+ break;
+ case USB3_LPM_U2:
+ feature = USB_PORT_FEAT_U2_TIMEOUT;
+ break;
+ default:
+ dev_warn(&udev->dev, "%s: Can't set timeout for non-U1 or U2 state.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (state == USB3_LPM_U1 && timeout > USB3_LPM_U1_MAX_TIMEOUT &&
+ timeout != USB3_LPM_DEVICE_INITIATED) {
+ dev_warn(&udev->dev, "Failed to set %s timeout to 0x%x, "
+ "which is a reserved value.\n",
+ usb3_lpm_names[state], timeout);
+ return -EINVAL;
+ }
+
+ ret = set_port_feature(udev->parent,
+ USB_PORT_LPM_TIMEOUT(timeout) | udev->portnum,
+ feature);
+ if (ret < 0) {
+ dev_warn(&udev->dev, "Failed to set %s timeout to 0x%x,"
+ "error code %i\n", usb3_lpm_names[state],
+ timeout, ret);
+ return -EBUSY;
+ }
+ if (state == USB3_LPM_U1)
+ udev->u1_params.timeout = timeout;
+ else
+ udev->u2_params.timeout = timeout;
+ return 0;
+}
+
+/*
+ * Enable the hub-initiated U1/U2 idle timeouts, and enable device-initiated
+ * U1/U2 entry.
+ *
+ * We will attempt to enable U1 or U2, but there are no guarantees that the
+ * control transfers to set the hub timeout or enable device-initiated U1/U2
+ * will be successful.
+ *
+ * If we cannot set the parent hub U1/U2 timeout, we attempt to let the xHCI
+ * driver know about it. If that call fails, it should be harmless, and just
+ * take up more slightly more bus bandwidth for unnecessary U1/U2 exit latency.
+ */
+static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
+ enum usb3_link_state state)
+{
+ int timeout, ret;
+ __u8 u1_mel = udev->bos->ss_cap->bU1devExitLat;
+ __le16 u2_mel = udev->bos->ss_cap->bU2DevExitLat;
+
+ /* If the device says it doesn't have *any* exit latency to come out of
+ * U1 or U2, it's probably lying. Assume it doesn't implement that link
+ * state.
+ */
+ if ((state == USB3_LPM_U1 && u1_mel == 0) ||
+ (state == USB3_LPM_U2 && u2_mel == 0))
+ return;
+
+ /*
+ * First, let the device know about the exit latencies
+ * associated with the link state we're about to enable.
+ */
+ ret = usb_req_set_sel(udev, state);
+ if (ret < 0) {
+ dev_warn(&udev->dev, "Set SEL for device-initiated %s failed.\n",
+ usb3_lpm_names[state]);
+ return;
+ }
+
+ /* We allow the host controller to set the U1/U2 timeout internally
+ * first, so that it can change its schedule to account for the
+ * additional latency to send data to a device in a lower power
+ * link state.
+ */
+ timeout = hcd->driver->enable_usb3_lpm_timeout(hcd, udev, state);
+
+ /* xHCI host controller doesn't want to enable this LPM state. */
+ if (timeout == 0)
+ return;
+
+ if (timeout < 0) {
+ dev_warn(&udev->dev, "Could not enable %s link state, "
+ "xHCI error %i.\n", usb3_lpm_names[state],
+ timeout);
+ return;
+ }
+
+ if (usb_set_lpm_timeout(udev, state, timeout))
+ /* If we can't set the parent hub U1/U2 timeout,
+ * device-initiated LPM won't be allowed either, so let the xHCI
+ * host know that this link state won't be enabled.
+ */
+ hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
+
+ /* Only a configured device will accept the Set Feature U1/U2_ENABLE */
+ else if (udev->actconfig)
+ usb_set_device_initiated_lpm(udev, state, true);
+
+}
+
+/*
+ * Disable the hub-initiated U1/U2 idle timeouts, and disable device-initiated
+ * U1/U2 entry.
+ *
+ * If this function returns -EBUSY, the parent hub will still allow U1/U2 entry.
+ * If zero is returned, the parent will not allow the link to go into U1/U2.
+ *
+ * If zero is returned, device-initiated U1/U2 entry may still be enabled, but
+ * it won't have an effect on the bus link state because the parent hub will
+ * still disallow device-initiated U1/U2 entry.
+ *
+ * If zero is returned, the xHCI host controller may still think U1/U2 entry is
+ * possible. The result will be slightly more bus bandwidth will be taken up
+ * (to account for U1/U2 exit latency), but it should be harmless.
+ */
+static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
+ enum usb3_link_state state)
+{
+ int feature;
+
+ switch (state) {
+ case USB3_LPM_U1:
+ feature = USB_PORT_FEAT_U1_TIMEOUT;
+ break;
+ case USB3_LPM_U2:
+ feature = USB_PORT_FEAT_U2_TIMEOUT;
+ break;
+ default:
+ dev_warn(&udev->dev, "%s: Can't disable non-U1 or U2 state.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (usb_set_lpm_timeout(udev, state, 0))
+ return -EBUSY;
+
+ usb_set_device_initiated_lpm(udev, state, false);
+
+ if (hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state))
+ dev_warn(&udev->dev, "Could not disable xHCI %s timeout, "
+ "bus schedule bandwidth may be impacted.\n",
+ usb3_lpm_names[state]);
+ return 0;
+}
+
+/*
+ * Disable hub-initiated and device-initiated U1 and U2 entry.
+ * Caller must own the bandwidth_mutex.
+ *
+ * This will call usb_enable_lpm() on failure, which will decrement
+ * lpm_disable_count, and will re-enable LPM if lpm_disable_count reaches zero.
+ */
+int usb_disable_lpm(struct usb_device *udev)
+{
+ struct usb_hcd *hcd;
+
+ if (!udev || !udev->parent ||
+ udev->speed != USB_SPEED_SUPER ||
+ !udev->lpm_capable)
+ return 0;
+
+ hcd = bus_to_hcd(udev->bus);
+ if (!hcd || !hcd->driver->disable_usb3_lpm_timeout)
+ return 0;
+
+ udev->lpm_disable_count++;
+ if ((udev->u1_params.timeout == 0 && udev->u2_params.timeout == 0))
+ return 0;
+
+ /* If LPM is enabled, attempt to disable it. */
+ if (usb_disable_link_state(hcd, udev, USB3_LPM_U1))
+ goto enable_lpm;
+ if (usb_disable_link_state(hcd, udev, USB3_LPM_U2))
+ goto enable_lpm;
+
+ return 0;
+
+enable_lpm:
+ usb_enable_lpm(udev);
+ return -EBUSY;
+}
+EXPORT_SYMBOL_GPL(usb_disable_lpm);
+
+/* Grab the bandwidth_mutex before calling usb_disable_lpm() */
+int usb_unlocked_disable_lpm(struct usb_device *udev)
+{
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+ int ret;
+
+ if (!hcd)
+ return -EINVAL;
+
+ mutex_lock(hcd->bandwidth_mutex);
+ ret = usb_disable_lpm(udev);
+ mutex_unlock(hcd->bandwidth_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
+
+/*
+ * Attempt to enable device-initiated and hub-initiated U1 and U2 entry. The
+ * xHCI host policy may prevent U1 or U2 from being enabled.
+ *
+ * Other callers may have disabled link PM, so U1 and U2 entry will be disabled
+ * until the lpm_disable_count drops to zero. Caller must own the
+ * bandwidth_mutex.
+ */
+void usb_enable_lpm(struct usb_device *udev)
+{
+ struct usb_hcd *hcd;
+
+ if (!udev || !udev->parent ||
+ udev->speed != USB_SPEED_SUPER ||
+ !udev->lpm_capable)
+ return;
+
+ udev->lpm_disable_count--;
+ hcd = bus_to_hcd(udev->bus);
+ /* Double check that we can both enable and disable LPM.
+ * Device must be configured to accept set feature U1/U2 timeout.
+ */
+ if (!hcd || !hcd->driver->enable_usb3_lpm_timeout ||
+ !hcd->driver->disable_usb3_lpm_timeout)
+ return;
+
+ if (udev->lpm_disable_count > 0)
+ return;
+
+ usb_enable_link_state(hcd, udev, USB3_LPM_U1);
+ usb_enable_link_state(hcd, udev, USB3_LPM_U2);
+}
+EXPORT_SYMBOL_GPL(usb_enable_lpm);
+
+/* Grab the bandwidth_mutex before calling usb_enable_lpm() */
+void usb_unlocked_enable_lpm(struct usb_device *udev)
+{
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+ if (!hcd)
+ return;
+
+ mutex_lock(hcd->bandwidth_mutex);
+ usb_enable_lpm(udev);
+ mutex_unlock(hcd->bandwidth_mutex);
+}
+EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
+
+
#else /* CONFIG_PM */
#define hub_suspend NULL
#define hub_resume NULL
#define hub_reset_resume NULL
-#endif
+
+int usb_disable_lpm(struct usb_device *udev)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_disable_lpm);
+
+void usb_enable_lpm(struct usb_device *udev) { }
+EXPORT_SYMBOL_GPL(usb_enable_lpm);
+
+int usb_unlocked_disable_lpm(struct usb_device *udev)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
+
+void usb_unlocked_enable_lpm(struct usb_device *udev) { }
+EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
+
+int usb_disable_ltm(struct usb_device *udev)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_disable_ltm);
+
+void usb_enable_ltm(struct usb_device *udev) { }
+EXPORT_SYMBOL_GPL(usb_enable_ltm);
+
+static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
+ u16 portstatus, u16 portchange)
+{
+ return 0;
+}
+
+#endif /* CONFIG_PM */
/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
@@ -2744,7 +4017,7 @@ EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
* Between connect detection and reset signaling there must be a delay
* of 100ms at least for debounce and power-settling. The corresponding
* timer shall restart whenever the downstream port detects a disconnect.
- *
+ *
* Apparently there are some bluetooth and irda-dongles and a number of
* low-speed devices for which this debounce period may last over a second.
* Not covered by the spec - but easy to deal with.
@@ -2754,12 +4027,13 @@ EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
* every 25ms for transient disconnects. When the port status has been
* unchanged for 100ms it returns the port status.
*/
-static int hub_port_debounce(struct usb_hub *hub, int port1)
+int hub_port_debounce(struct usb_hub *hub, int port1, bool must_be_connected)
{
int ret;
- int total_time, stable_time = 0;
u16 portchange, portstatus;
unsigned connection = 0xffff;
+ int total_time, stable_time = 0;
+ struct usb_port *port_dev = hub->ports[port1 - 1];
for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
ret = hub_port_status(hub, port1, &portstatus, &portchange);
@@ -2768,7 +4042,9 @@ static int hub_port_debounce(struct usb_hub *hub, int port1)
if (!(portchange & USB_PORT_STAT_C_CONNECTION) &&
(portstatus & USB_PORT_STAT_CONNECTION) == connection) {
- stable_time += HUB_DEBOUNCE_STEP;
+ if (!must_be_connected ||
+ (connection == USB_PORT_STAT_CONNECTION))
+ stable_time += HUB_DEBOUNCE_STEP;
if (stable_time >= HUB_DEBOUNCE_STABLE)
break;
} else {
@@ -2777,7 +4053,7 @@ static int hub_port_debounce(struct usb_hub *hub, int port1)
}
if (portchange & USB_PORT_STAT_C_CONNECTION) {
- clear_port_feature(hub->hdev, port1,
+ usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
}
@@ -2786,9 +4062,8 @@ static int hub_port_debounce(struct usb_hub *hub, int port1)
msleep(HUB_DEBOUNCE_STEP);
}
- dev_dbg (hub->intfdev,
- "debounce: port %d: total %dms stable %dms status 0x%x\n",
- port1, total_time, stable_time, portstatus);
+ dev_dbg(&port_dev->dev, "debounce total %dms stable %dms status 0x%x\n",
+ total_time, stable_time, portstatus);
if (stable_time < HUB_DEBOUNCE_STABLE)
return -ETIMEDOUT;
@@ -2836,21 +4111,61 @@ static int hub_set_address(struct usb_device *udev, int devnum)
return retval;
}
+/*
+ * There are reports of USB 3.0 devices that say they support USB 2.0 Link PM
+ * when they're plugged into a USB 2.0 port, but they don't work when LPM is
+ * enabled.
+ *
+ * Only enable USB 2.0 Link PM if the port is internal (hardwired), or the
+ * device says it supports the new USB 2.0 Link PM errata by setting the BESL
+ * support bit in the BOS descriptor.
+ */
+static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev)
+{
+ struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
+ int connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN;
+
+ if (!udev->usb2_hw_lpm_capable)
+ return;
+
+ if (hub)
+ connect_type = hub->ports[udev->portnum - 1]->connect_type;
+
+ if ((udev->bos->ext_cap->bmAttributes & cpu_to_le32(USB_BESL_SUPPORT)) ||
+ connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) {
+ udev->usb2_hw_lpm_allowed = 1;
+ usb_set_usb2_hardware_lpm(udev, 1);
+ }
+}
+
+static int hub_enable_device(struct usb_device *udev)
+{
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+ if (!hcd->driver->enable_device)
+ return 0;
+ if (udev->state == USB_STATE_ADDRESS)
+ return 0;
+ if (udev->state != USB_STATE_DEFAULT)
+ return -EINVAL;
+
+ return hcd->driver->enable_device(hcd, udev);
+}
+
/* Reset device, (re)assign address, get device descriptor.
* Device connection must be stable, no more debouncing needed.
* Returns device in USB_STATE_ADDRESS, except on error.
*
* If this is called for an already-existing device (as part of
- * usb_reset_and_verify_device), the caller must own the device lock. For a
- * newly detected device that is not accessible through any global
- * pointers, it's not necessary to lock the device.
+ * usb_reset_and_verify_device), the caller must own the device lock and
+ * the port lock. For a newly detected device that is not accessible
+ * through any global pointers, it's not necessary to lock the device,
+ * but it is still necessary to lock the port.
*/
static int
hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
int retry_counter)
{
- static DEFINE_MUTEX(usb_address0_mutex);
-
struct usb_device *hdev = hub->hdev;
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
int i, j, retval;
@@ -2873,7 +4188,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
if (oldspeed == USB_SPEED_LOW)
delay = HUB_LONG_RESET_TIME;
- mutex_lock(&usb_address0_mutex);
+ mutex_lock(&hdev->bus->usb_address0_mutex);
/* Reset the device; full speed may morph to high speed */
/* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
@@ -2942,13 +4257,13 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
udev->tt = &hub->tt;
udev->ttport = port1;
}
-
+
/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
* Because device hardware and firmware is sometimes buggy in
* this area, and this is how Linux has done it for ages.
* Change it cautiously.
*
- * NOTE: If USE_NEW_SCHEME() is true we will start by issuing
+ * NOTE: If use_new_scheme() is true we will start by issuing
* a 64-byte GET_DESCRIPTOR request. This is what Windows does,
* so it may help with some non-standards-compliant devices.
* Otherwise we start with SET_ADDRESS and then try to read the
@@ -2956,10 +4271,21 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
* value.
*/
for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
- if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) {
+ bool did_new_scheme = false;
+
+ if (use_new_scheme(udev, retry_counter)) {
struct usb_device_descriptor *buf;
int r = 0;
+ did_new_scheme = true;
+ retval = hub_enable_device(udev);
+ if (retval < 0) {
+ dev_err(&udev->dev,
+ "hub failed to enable device, error %d\n",
+ retval);
+ goto fail;
+ }
+
#define GET_DESCRIPTOR_BUFSIZE 64
buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
if (!buf) {
@@ -3008,20 +4334,20 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
goto fail;
}
if (r) {
- dev_err(&udev->dev,
- "device descriptor read/64, error %d\n",
- r);
+ if (r != -ENODEV)
+ dev_err(&udev->dev, "device descriptor read/64, error %d\n",
+ r);
retval = -EMSGSIZE;
continue;
}
#undef GET_DESCRIPTOR_BUFSIZE
}
- /*
- * If device is WUSB, we already assigned an
- * unauthorized address in the Connect Ack sequence;
- * authorization will assign the final address.
- */
+ /*
+ * If device is WUSB, we already assigned an
+ * unauthorized address in the Connect Ack sequence;
+ * authorization will assign the final address.
+ */
if (udev->wusb == 0) {
for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
retval = hub_set_address(udev, devnum);
@@ -3030,9 +4356,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
msleep(200);
}
if (retval < 0) {
- dev_err(&udev->dev,
- "device not accepting address %d, error %d\n",
- devnum, retval);
+ if (retval != -ENODEV)
+ dev_err(&udev->dev, "device not accepting address %d, error %d\n",
+ devnum, retval);
goto fail;
}
if (udev->speed == USB_SPEED_SUPER) {
@@ -3048,13 +4374,18 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
* - read ep0 maxpacket even for high and low speed,
*/
msleep(10);
- if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3))
+ /* use_new_scheme() checks the speed which may have
+ * changed since the initial look so we cache the result
+ * in did_new_scheme
+ */
+ if (did_new_scheme)
break;
- }
+ }
retval = usb_get_device_descriptor(udev, 8);
if (retval < 8) {
- dev_err(&udev->dev,
+ if (retval != -ENODEV)
+ dev_err(&udev->dev,
"device descriptor read/8, error %d\n",
retval);
if (retval >= 0)
@@ -3067,6 +4398,25 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
if (retval)
goto fail;
+ if (hcd->phy && !hdev->parent)
+ usb_phy_notify_connect(hcd->phy, udev->speed);
+
+ /*
+ * Some superspeed devices have finished the link training process
+ * and attached to a superspeed hub port, but the device descriptor
+ * got from those devices show they aren't superspeed devices. Warm
+ * reset the port attached by the devices can fix them.
+ */
+ if ((udev->speed == USB_SPEED_SUPER) &&
+ (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
+ dev_err(&udev->dev, "got a wrong device descriptor, "
+ "warm reset device\n");
+ hub_port_reset(hub, port1, udev,
+ HUB_BH_RESET_TIME, true);
+ retval = -EINVAL;
+ goto fail;
+ }
+
if (udev->descriptor.bMaxPacketSize0 == 0xff ||
udev->speed == USB_SPEED_SUPER)
i = 512;
@@ -3086,11 +4436,12 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
usb_ep0_reinit(udev);
}
-
+
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
if (retval < (signed)sizeof(udev->descriptor)) {
- dev_err(&udev->dev, "device descriptor read/all, error %d\n",
- retval);
+ if (retval != -ENODEV)
+ dev_err(&udev->dev, "device descriptor read/all, error %d\n",
+ retval);
if (retval >= 0)
retval = -ENOMSG;
goto fail;
@@ -3099,9 +4450,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
retval = usb_get_bos_descriptor(udev);
if (!retval) {
- if (udev->bos->ext_cap && (USB_LPM_SUPPORT &
- le32_to_cpu(udev->bos->ext_cap->bmAttributes)))
- udev->lpm_capable = 1;
+ udev->lpm_capable = usb_device_supports_lpm(udev);
+ usb_set_lpm_parameters(udev);
}
}
@@ -3109,12 +4459,13 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
/* notify HCD that we have a device connected and addressed */
if (hcd->driver->update_device)
hcd->driver->update_device(hcd, udev);
+ hub_set_initial_usb2_lpm_policy(udev);
fail:
if (retval) {
hub_port_disable(hub, port1, 0);
update_devnum(udev, devnum); /* for disconnect processing */
}
- mutex_unlock(&usb_address0_mutex);
+ mutex_unlock(&hdev->bus->usb_address0_mutex);
return retval;
}
@@ -3136,7 +4487,8 @@ check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1)
/* hub LEDs are probably harder to miss than syslog */
if (hub->has_indicators) {
hub->indicator[port1-1] = INDICATOR_GREEN_BLINK;
- schedule_delayed_work (&hub->leds, 0);
+ queue_delayed_work(system_power_efficient_wq,
+ &hub->leds, 0);
}
}
kfree(qual);
@@ -3154,101 +4506,58 @@ 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];
- int delta;
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+ struct usb_device *udev = port_dev->child;
+ unsigned unit_load;
+ int delta;
if (!udev)
continue;
+ if (hub_is_superspeed(udev))
+ unit_load = 150;
+ else
+ unit_load = 100;
- /* Unconfigured devices may not use more than 100mA,
- * or 8mA for OTG ports */
+ /*
+ * Unconfigured devices may not use more than one unit load,
+ * or 8mA for OTG ports
+ */
if (udev->actconfig)
- delta = udev->actconfig->desc.bMaxPower * 2;
+ delta = usb_get_max_power(udev, udev->actconfig);
else if (port1 != udev->bus->otg_port || hdev->parent)
- delta = 100;
+ delta = unit_load;
else
delta = 8;
if (delta > hub->mA_per_port)
- dev_warn(&udev->dev,
- "%dmA is over %umA budget for port %d!\n",
- delta, hub->mA_per_port, port1);
+ dev_warn(&port_dev->dev, "%dmA is over %umA budget!\n",
+ delta, hub->mA_per_port);
remaining -= delta;
}
if (remaining < 0) {
dev_warn(hub->intfdev, "%dmA over power budget!\n",
- - remaining);
+ -remaining);
remaining = 0;
}
return remaining;
}
-/* Handle physical or logical connection change events.
- * This routine is called when:
- * a port connection-change occurs;
- * a port enable-change occurs (often caused by EMI);
- * usb_reset_and_verify_device() encounters changed descriptors (as from
- * a firmware download)
- * caller already locked the hub
- */
-static void hub_port_connect_change(struct usb_hub *hub, int port1,
- u16 portstatus, u16 portchange)
+static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
+ u16 portchange)
{
+ int status, i;
+ unsigned unit_load;
struct usb_device *hdev = hub->hdev;
- struct device *hub_dev = hub->intfdev;
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
- unsigned wHubCharacteristics =
- le16_to_cpu(hub->descriptor->wHubCharacteristics);
- struct usb_device *udev;
- int status, i;
-
- dev_dbg (hub_dev,
- "port %d, status %04x, change %04x, %s\n",
- port1, portstatus, portchange, portspeed(hub, portstatus));
-
- if (hub->has_indicators) {
- set_port_led(hub, port1, HUB_LED_AUTO);
- hub->indicator[port1-1] = INDICATOR_AUTO;
- }
-
-#ifdef CONFIG_USB_OTG
- /* during HNP, don't repeat the debounce */
- if (hdev->bus->is_b_host)
- portchange &= ~(USB_PORT_STAT_C_CONNECTION |
- USB_PORT_STAT_C_ENABLE);
-#endif
-
- /* Try to resuscitate an existing device */
- udev = hdev->children[port1-1];
- if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
- udev->state != USB_STATE_NOTATTACHED) {
- usb_lock_device(udev);
- if (portstatus & USB_PORT_STAT_ENABLE) {
- status = 0; /* Nothing to do */
-
-#ifdef CONFIG_USB_SUSPEND
- } else if (udev->state == USB_STATE_SUSPENDED &&
- udev->persist_enabled) {
- /* For a suspended device, treat this as a
- * remote wakeup event.
- */
- status = usb_remote_wakeup(udev);
-#endif
-
- } else {
- status = -ENODEV; /* Don't resuscitate */
- }
- usb_unlock_device(udev);
-
- if (status == 0) {
- clear_bit(port1, hub->change_bits);
- return;
- }
- }
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+ struct usb_device *udev = port_dev->child;
/* Disconnect any existing devices under this port */
- if (udev)
- usb_disconnect(&hdev->children[port1-1]);
- clear_bit(port1, hub->change_bits);
+ if (udev) {
+ if (hcd->phy && !hdev->parent &&
+ !(portstatus & USB_PORT_STAT_CONNECTION))
+ usb_phy_notify_disconnect(hcd->phy, udev->speed);
+ usb_disconnect(&port_dev->child);
+ }
/* We can forget about a "removed" device when there's a physical
* disconnect or the connect status changes.
@@ -3259,11 +4568,11 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
if (portchange & (USB_PORT_STAT_C_CONNECTION |
USB_PORT_STAT_C_ENABLE)) {
- status = hub_port_debounce(hub, port1);
+ status = hub_port_debounce_be_stable(hub, port1);
if (status < 0) {
- if (printk_ratelimit())
- dev_err(hub_dev, "connect-debounce failed, "
- "port %d disabled\n", port1);
+ if (status != -ENODEV && printk_ratelimit())
+ dev_err(&port_dev->dev,
+ "connect-debounce failed\n");
portstatus &= ~USB_PORT_STAT_CONNECTION;
} else {
portstatus = status;
@@ -3277,15 +4586,20 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
test_bit(port1, hub->removed_bits)) {
/* maybe switch power back on (e.g. root hub was reset) */
- if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
+ if (hub_is_port_power_switchable(hub)
&& !port_is_power_on(hub, portstatus))
set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
if (portstatus & USB_PORT_STAT_ENABLE)
- goto done;
+ goto done;
return;
}
+ if (hub_is_superspeed(hub->hdev))
+ unit_load = 150;
+ else
+ unit_load = 100;
+ status = 0;
for (i = 0; i < SET_CONFIG_TRIES; i++) {
/* reallocate for each attempt, since references
@@ -3293,14 +4607,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
*/
udev = usb_alloc_dev(hdev, hdev->bus, port1);
if (!udev) {
- dev_err (hub_dev,
- "couldn't allocate port %d usb_device\n",
- port1);
+ dev_err(&port_dev->dev,
+ "couldn't allocate usb_device\n");
goto done;
}
usb_set_device_state(udev, USB_STATE_POWERED);
- udev->bus_mA = hub->mA_per_port;
+ udev->bus_mA = hub->mA_per_port;
udev->level = hdev->level + 1;
udev->wusb = hub_is_wusb(hub);
@@ -3317,7 +4630,9 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
}
/* reset (non-USB 3.0 devices) and get descriptor */
+ usb_lock_port(port_dev);
status = hub_port_init(hub, udev, port1, i);
+ usb_unlock_port(port_dev);
if (status < 0)
goto loop;
@@ -3332,16 +4647,15 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
* on the parent.
*/
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
- && udev->bus_mA <= 100) {
+ && udev->bus_mA <= unit_load) {
u16 devstat;
status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
&devstat);
- if (status < 2) {
+ if (status) {
dev_dbg(&udev->dev, "get status %d ?\n", status);
goto loop_disable;
}
- le16_to_cpus(&devstat);
if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
dev_err(&udev->dev,
"can't connect bus-powered hub "
@@ -3349,13 +4663,15 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
if (hub->has_indicators) {
hub->indicator[port1-1] =
INDICATOR_AMBER_BLINK;
- schedule_delayed_work (&hub->leds, 0);
+ queue_delayed_work(
+ system_power_efficient_wq,
+ &hub->leds, 0);
}
status = -ENOTCONN; /* Don't retry */
goto loop_disable;
}
}
-
+
/* check for devices running slower than they could */
if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
&& udev->speed == USB_SPEED_FULL
@@ -3368,6 +4684,8 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
*/
status = 0;
+ mutex_lock(&usb_port_peer_mutex);
+
/* We mustn't add new devices if the parent hub has
* been disconnected; we would race with the
* recursively_mark_NOTATTACHED() routine.
@@ -3376,16 +4694,19 @@ 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;
+ port_dev->child = udev;
spin_unlock_irq(&device_state_lock);
+ mutex_unlock(&usb_port_peer_mutex);
/* Run it through the hoops (find a driver, etc) */
if (!status) {
status = usb_new_device(udev);
if (status) {
+ mutex_lock(&usb_port_peer_mutex);
spin_lock_irq(&device_state_lock);
- hdev->children[port1-1] = NULL;
+ port_dev->child = NULL;
spin_unlock_irq(&device_state_lock);
+ mutex_unlock(&usb_port_peer_mutex);
}
}
@@ -3394,7 +4715,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
status = hub_power_remaining(hub);
if (status)
- dev_dbg(hub_dev, "%dmA power budget left\n", status);
+ dev_dbg(hub->intfdev, "%dmA power budget left\n", status);
return;
@@ -3410,16 +4731,202 @@ loop:
}
if (hub->hdev->parent ||
!hcd->driver->port_handed_over ||
- !(hcd->driver->port_handed_over)(hcd, port1))
- dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
- port1);
-
+ !(hcd->driver->port_handed_over)(hcd, port1)) {
+ if (status != -ENOTCONN && status != -ENODEV)
+ dev_err(&port_dev->dev,
+ "unable to enumerate USB device\n");
+ }
+
done:
hub_port_disable(hub, port1, 1);
if (hcd->driver->relinquish_port && !hub->hdev->parent)
hcd->driver->relinquish_port(hcd, port1);
+
}
+/* Handle physical or logical connection change events.
+ * This routine is called when:
+ * a port connection-change occurs;
+ * a port enable-change occurs (often caused by EMI);
+ * usb_reset_and_verify_device() encounters changed descriptors (as from
+ * a firmware download)
+ * caller already locked the hub
+ */
+static void hub_port_connect_change(struct usb_hub *hub, int port1,
+ u16 portstatus, u16 portchange)
+ __must_hold(&port_dev->status_lock)
+{
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+ struct usb_device *udev = port_dev->child;
+ int status = -ENODEV;
+
+ dev_dbg(&port_dev->dev, "status %04x, change %04x, %s\n", portstatus,
+ portchange, portspeed(hub, portstatus));
+
+ if (hub->has_indicators) {
+ set_port_led(hub, port1, HUB_LED_AUTO);
+ hub->indicator[port1-1] = INDICATOR_AUTO;
+ }
+
+#ifdef CONFIG_USB_OTG
+ /* during HNP, don't repeat the debounce */
+ if (hub->hdev->bus->is_b_host)
+ portchange &= ~(USB_PORT_STAT_C_CONNECTION |
+ USB_PORT_STAT_C_ENABLE);
+#endif
+
+ /* Try to resuscitate an existing device */
+ if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
+ udev->state != USB_STATE_NOTATTACHED) {
+ if (portstatus & USB_PORT_STAT_ENABLE) {
+ status = 0; /* Nothing to do */
+#ifdef CONFIG_PM_RUNTIME
+ } else if (udev->state == USB_STATE_SUSPENDED &&
+ udev->persist_enabled) {
+ /* For a suspended device, treat this as a
+ * remote wakeup event.
+ */
+ usb_unlock_port(port_dev);
+ status = usb_remote_wakeup(udev);
+ usb_lock_port(port_dev);
+#endif
+ } else {
+ /* Don't resuscitate */;
+ }
+ }
+ clear_bit(port1, hub->change_bits);
+
+ /* successfully revalidated the connection */
+ if (status == 0)
+ return;
+
+ usb_unlock_port(port_dev);
+ hub_port_connect(hub, port1, portstatus, portchange);
+ usb_lock_port(port_dev);
+}
+
+static void port_event(struct usb_hub *hub, int port1)
+ __must_hold(&port_dev->status_lock)
+{
+ int connect_change, reset_device = 0;
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+ struct usb_device *udev = port_dev->child;
+ struct usb_device *hdev = hub->hdev;
+ u16 portstatus, portchange;
+
+ connect_change = test_bit(port1, hub->change_bits);
+ clear_bit(port1, hub->event_bits);
+ clear_bit(port1, hub->wakeup_bits);
+
+ if (hub_port_status(hub, port1, &portstatus, &portchange) < 0)
+ return;
+
+ if (portchange & USB_PORT_STAT_C_CONNECTION) {
+ usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
+ connect_change = 1;
+ }
+
+ if (portchange & USB_PORT_STAT_C_ENABLE) {
+ if (!connect_change)
+ dev_dbg(&port_dev->dev, "enable change, status %08x\n",
+ portstatus);
+ usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
+
+ /*
+ * EM interference sometimes causes badly shielded USB devices
+ * to be shutdown by the hub, this hack enables them again.
+ * Works at least with mouse driver.
+ */
+ if (!(portstatus & USB_PORT_STAT_ENABLE)
+ && !connect_change && udev) {
+ dev_err(&port_dev->dev, "disabled by hub (EMI?), re-enabling...\n");
+ connect_change = 1;
+ }
+ }
+
+ if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
+ u16 status = 0, unused;
+
+ dev_dbg(&port_dev->dev, "over-current change\n");
+ usb_clear_port_feature(hdev, port1,
+ USB_PORT_FEAT_C_OVER_CURRENT);
+ msleep(100); /* Cool down */
+ hub_power_on(hub, true);
+ hub_port_status(hub, port1, &status, &unused);
+ if (status & USB_PORT_STAT_OVERCURRENT)
+ dev_err(&port_dev->dev, "over-current condition\n");
+ }
+
+ if (portchange & USB_PORT_STAT_C_RESET) {
+ dev_dbg(&port_dev->dev, "reset change\n");
+ usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_RESET);
+ }
+ if ((portchange & USB_PORT_STAT_C_BH_RESET)
+ && hub_is_superspeed(hdev)) {
+ dev_dbg(&port_dev->dev, "warm reset change\n");
+ usb_clear_port_feature(hdev, port1,
+ USB_PORT_FEAT_C_BH_PORT_RESET);
+ }
+ if (portchange & USB_PORT_STAT_C_LINK_STATE) {
+ dev_dbg(&port_dev->dev, "link state change\n");
+ usb_clear_port_feature(hdev, port1,
+ USB_PORT_FEAT_C_PORT_LINK_STATE);
+ }
+ if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
+ dev_warn(&port_dev->dev, "config error\n");
+ usb_clear_port_feature(hdev, port1,
+ USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
+ }
+
+ /* skip port actions that require the port to be powered on */
+ if (!pm_runtime_active(&port_dev->dev))
+ return;
+
+ if (hub_handle_remote_wakeup(hub, port1, portstatus, portchange))
+ connect_change = 1;
+
+ /*
+ * Warm reset a USB3 protocol port if it's in
+ * SS.Inactive state.
+ */
+ if (hub_port_warm_reset_required(hub, portstatus)) {
+ dev_dbg(&port_dev->dev, "do warm reset\n");
+ if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
+ || udev->state == USB_STATE_NOTATTACHED) {
+ if (hub_port_reset(hub, port1, NULL,
+ HUB_BH_RESET_TIME, true) < 0)
+ hub_port_disable(hub, port1, 1);
+ } else
+ reset_device = 1;
+ }
+
+ /*
+ * On disconnect USB3 protocol ports transit from U0 to
+ * SS.Inactive to Rx.Detect. If this happens a warm-
+ * reset is not needed, but a (re)connect may happen
+ * before khubd runs and sees the disconnect, and the
+ * device may be an unknown state.
+ *
+ * If the port went through SS.Inactive without khubd
+ * seeing it the C_LINK_STATE change flag will be set,
+ * and we reset the dev to put it in a known state.
+ */
+ if (reset_device || (udev && hub_is_superspeed(hub->hdev)
+ && (portchange & USB_PORT_STAT_C_LINK_STATE)
+ && (portstatus & USB_PORT_STAT_CONNECTION))) {
+ usb_unlock_port(port_dev);
+ usb_lock_device(udev);
+ usb_reset_device(udev);
+ usb_unlock_device(udev);
+ usb_lock_port(port_dev);
+ connect_change = 0;
+ }
+
+ if (connect_change)
+ hub_port_connect_change(hub, port1, portstatus, portchange);
+}
+
+
static void hub_events(void)
{
struct list_head *tmp;
@@ -3429,10 +4936,7 @@ static void hub_events(void)
struct device *hub_dev;
u16 hubstatus;
u16 hubchange;
- u16 portstatus;
- u16 portchange;
int i, ret;
- int connect_change;
/*
* We restart the list every time to avoid a deadlock with
@@ -3460,9 +4964,7 @@ static void hub_events(void)
hub_dev = hub->intfdev;
intf = to_usb_interface(hub_dev);
dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
- hdev->state, hub->descriptor
- ? hub->descriptor->bNbrPorts
- : 0,
+ hdev->state, hdev->maxchild,
/* NOTE: expects max 15 ports... */
(u16) hub->change_bits[0],
(u16) hub->event_bits[0]);
@@ -3507,135 +5009,29 @@ static void hub_events(void)
}
/* deal with port status changes */
- for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
- if (test_bit(i, hub->busy_bits))
- continue;
- connect_change = test_bit(i, hub->change_bits);
- if (!test_and_clear_bit(i, hub->event_bits) &&
- !connect_change)
- continue;
-
- ret = hub_port_status(hub, i,
- &portstatus, &portchange);
- if (ret < 0)
- continue;
-
- if (portchange & USB_PORT_STAT_C_CONNECTION) {
- clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_CONNECTION);
- connect_change = 1;
- }
-
- if (portchange & USB_PORT_STAT_C_ENABLE) {
- if (!connect_change)
- dev_dbg (hub_dev,
- "port %d enable change, "
- "status %08x\n",
- i, portstatus);
- clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_ENABLE);
+ for (i = 1; i <= hdev->maxchild; i++) {
+ struct usb_port *port_dev = hub->ports[i - 1];
+ if (test_bit(i, hub->event_bits)
+ || test_bit(i, hub->change_bits)
+ || test_bit(i, hub->wakeup_bits)) {
/*
- * EM interference sometimes causes badly
- * shielded USB devices to be shutdown by
- * the hub, this hack enables them again.
- * Works at least with mouse driver.
+ * The get_noresume and barrier ensure that if
+ * the port was in the process of resuming, we
+ * flush that work and keep the port active for
+ * the duration of the port_event(). However,
+ * if the port is runtime pm suspended
+ * (powered-off), we leave it in that state, run
+ * an abbreviated port_event(), and move on.
*/
- if (!(portstatus & USB_PORT_STAT_ENABLE)
- && !connect_change
- && hdev->children[i-1]) {
- dev_err (hub_dev,
- "port %i "
- "disabled by hub (EMI?), "
- "re-enabling...\n",
- i);
- connect_change = 1;
- }
- }
-
- if (portchange & USB_PORT_STAT_C_SUSPEND) {
- struct usb_device *udev;
-
- clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_SUSPEND);
- udev = hdev->children[i-1];
- if (udev) {
- /* TRSMRCY = 10 msec */
- msleep(10);
-
- usb_lock_device(udev);
- ret = usb_remote_wakeup(hdev->
- children[i-1]);
- usb_unlock_device(udev);
- if (ret < 0)
- connect_change = 1;
- } else {
- ret = -ENODEV;
- hub_port_disable(hub, i, 1);
- }
- dev_dbg (hub_dev,
- "resume on port %d, status %d\n",
- i, ret);
- }
-
- if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
- u16 status = 0;
- u16 unused;
-
- dev_dbg(hub_dev, "over-current change on port "
- "%d\n", i);
- clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_OVER_CURRENT);
- msleep(100); /* Cool down */
- hub_power_on(hub, true);
- hub_port_status(hub, i, &status, &unused);
- if (status & USB_PORT_STAT_OVERCURRENT)
- dev_err(hub_dev, "over-current "
- "condition on port %d\n", i);
- }
-
- if (portchange & USB_PORT_STAT_C_RESET) {
- dev_dbg (hub_dev,
- "reset change on port %d\n",
- i);
- clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_RESET);
- }
- if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
- hub_is_superspeed(hub->hdev)) {
- dev_dbg(hub_dev,
- "warm reset change on port %d\n",
- i);
- clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_BH_PORT_RESET);
+ pm_runtime_get_noresume(&port_dev->dev);
+ pm_runtime_barrier(&port_dev->dev);
+ usb_lock_port(port_dev);
+ port_event(hub, i);
+ usb_unlock_port(port_dev);
+ pm_runtime_put_sync(&port_dev->dev);
}
- if (portchange & USB_PORT_STAT_C_LINK_STATE) {
- clear_port_feature(hub->hdev, i,
- USB_PORT_FEAT_C_PORT_LINK_STATE);
- }
- if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
- dev_warn(hub_dev,
- "config error on port %d\n",
- i);
- clear_port_feature(hub->hdev, i,
- USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
- }
-
- /* Warm reset a USB3 protocol port if it's in
- * SS.Inactive state.
- */
- if (hub_is_superspeed(hub->hdev) &&
- (portstatus & USB_PORT_STAT_LINK_STATE)
- == USB_SS_PORT_LS_SS_INACTIVE) {
- dev_dbg(hub_dev, "warm reset port %d\n", i);
- hub_port_reset(hub, i, NULL,
- HUB_BH_RESET_TIME, true);
- }
-
- if (connect_change)
- hub_port_connect_change(hub, i,
- portstatus, portchange);
- } /* end for i */
+ }
/* deal with hub status changes */
if (test_and_clear_bit(0, hub->event_bits) == 0)
@@ -3659,7 +5055,7 @@ static void hub_events(void)
dev_dbg(hub_dev, "over-current change\n");
clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
msleep(500); /* Cool down */
- hub_power_on(hub, true);
+ hub_power_on(hub, true);
hub_hub_status(hub, &status, &unused);
if (status & HUB_STATUS_OVERCURRENT)
dev_err(hub_dev, "over-current "
@@ -3679,12 +5075,12 @@ static void hub_events(void)
usb_unlock_device(hdev);
kref_put(&hub->kref, hub_release);
- } /* end while (1) */
+ } /* end while (1) */
}
static int hub_thread(void *__unused)
{
- /* khubd needs to be freezable to avoid intefering with USB-PERSIST
+ /* khubd needs to be freezable to avoid interfering with USB-PERSIST
* port handover. Otherwise it might see that a full-speed device
* was gone before the EHCI controller had handed its port over to
* the companion full-speed controller.
@@ -3703,6 +5099,11 @@ static int hub_thread(void *__unused)
}
static const struct usb_device_id hub_id_table[] = {
+ { .match_flags = USB_DEVICE_ID_MATCH_VENDOR
+ | USB_DEVICE_ID_MATCH_INT_CLASS,
+ .idVendor = USB_VENDOR_GENESYS_LOGIC,
+ .bInterfaceClass = USB_CLASS_HUB,
+ .driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},
{ .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
.bDeviceClass = USB_CLASS_HUB},
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
@@ -3760,7 +5161,8 @@ void usb_hub_cleanup(void)
} /* usb_hub_cleanup() */
static int descriptors_changed(struct usb_device *udev,
- struct usb_device_descriptor *old_device_descriptor)
+ struct usb_device_descriptor *old_device_descriptor,
+ struct usb_host_bos *old_bos)
{
int changed = 0;
unsigned index;
@@ -3774,6 +5176,16 @@ static int descriptors_changed(struct usb_device *udev,
sizeof(*old_device_descriptor)) != 0)
return 1;
+ if ((old_bos && !udev->bos) || (!old_bos && udev->bos))
+ return 1;
+ if (udev->bos) {
+ len = le16_to_cpu(udev->bos->desc->wTotalLength);
+ if (len != le16_to_cpu(old_bos->desc->wTotalLength))
+ return 1;
+ if (memcmp(udev->bos->desc, old_bos->desc, len))
+ return 1;
+ }
+
/* Since the idVendor, idProduct, and bcdDevice values in the
* device descriptor haven't changed, we will assume the
* Manufacturer and Product strings haven't changed either.
@@ -3849,19 +5261,23 @@ static int descriptors_changed(struct usb_device *udev,
* re-connected. All drivers will be unbound, and the device will be
* re-enumerated and probed all over again.
*
- * Returns 0 if the reset succeeded, -ENODEV if the device has been
+ * Return: 0 if the reset succeeded, -ENODEV if the device has been
* flagged for logical disconnection, or some other negative error code
* if the reset wasn't even attempted.
*
- * The caller must own the device lock. For example, it's safe to use
- * this from a driver probe() routine after downloading new firmware.
- * For calls that might not occur during probe(), drivers should lock
- * the device using usb_lock_device_for_reset().
+ * Note:
+ * The caller must own the device lock and the port lock, the latter is
+ * taken by usb_reset_device(). For example, it's safe to use
+ * usb_reset_device() from a driver probe() routine after downloading
+ * new firmware. For calls that might not occur during probe(), drivers
+ * should lock the device using usb_lock_device_for_reset().
*
* Locking exception: This routine may also be called from within an
* autoresume handler. Such usage won't conflict with other tasks
* holding the device lock because these tasks should always call
- * usb_autopm_resume_device(), thereby preventing any unwanted autoresume.
+ * usb_autopm_resume_device(), thereby preventing any unwanted
+ * autoresume. The autoresume handler is expected to have already
+ * acquired the port lock before calling this routine.
*/
static int usb_reset_and_verify_device(struct usb_device *udev)
{
@@ -3869,7 +5285,8 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
struct usb_hub *parent_hub;
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
struct usb_device_descriptor descriptor = udev->descriptor;
- int i, ret = 0;
+ struct usb_host_bos *bos;
+ int i, j, ret = 0;
int port1 = udev->portnum;
if (udev->state == USB_STATE_NOTATTACHED ||
@@ -3879,14 +5296,37 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
return -EINVAL;
}
- if (!parent_hdev) {
- /* this requires hcd-specific logic; see ohci_restart() */
- dev_dbg(&udev->dev, "%s for root hub!\n", __func__);
+ if (!parent_hdev)
return -EISDIR;
+
+ parent_hub = usb_hub_to_struct_hub(parent_hdev);
+
+ /* Disable USB2 hardware LPM.
+ * It will be re-enabled by the enumeration process.
+ */
+ if (udev->usb2_hw_lpm_enabled == 1)
+ usb_set_usb2_hardware_lpm(udev, 0);
+
+ bos = udev->bos;
+ udev->bos = NULL;
+
+ /* Disable LPM and LTM while we reset the device and reinstall the alt
+ * settings. Device-initiated LPM settings, and system exit latency
+ * settings are cleared when the device is reset, so we have to set
+ * them up again.
+ */
+ ret = usb_unlocked_disable_lpm(udev);
+ if (ret) {
+ dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
+ goto re_enumerate;
+ }
+ ret = usb_disable_ltm(udev);
+ if (ret) {
+ dev_err(&udev->dev, "%s Failed to disable LTM\n.",
+ __func__);
+ goto re_enumerate;
}
- parent_hub = hdev_to_hub(parent_hdev);
- set_bit(port1, parent_hub->busy_bits);
for (i = 0; i < SET_CONFIG_TRIES; ++i) {
/* ep0 maxpacket size may change; let the HCD know about it.
@@ -3896,17 +5336,16 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
break;
}
- clear_bit(port1, parent_hub->busy_bits);
if (ret < 0)
goto re_enumerate;
-
+
/* Device might have changed firmware (DFU or similar) */
- if (descriptors_changed(udev, &descriptor)) {
+ if (descriptors_changed(udev, &descriptor, bos)) {
dev_info(&udev->dev, "device firmware changed\n");
udev->descriptor = descriptor; /* for disconnect() calls */
goto re_enumerate;
- }
+ }
/* Restore the device's previous configuration */
if (!udev->actconfig)
@@ -3931,7 +5370,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
udev->actconfig->desc.bConfigurationValue, ret);
mutex_unlock(hcd->bandwidth_mutex);
goto re_enumerate;
- }
+ }
mutex_unlock(hcd->bandwidth_mutex);
usb_set_device_state(udev, USB_STATE_CONFIGURED);
@@ -3969,13 +5408,25 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
ret);
goto re_enumerate;
}
+ /* Resetting also frees any allocated streams */
+ for (j = 0; j < intf->cur_altsetting->desc.bNumEndpoints; j++)
+ intf->cur_altsetting->endpoint[j].streams = 0;
}
done:
+ /* Now that the alt settings are re-installed, enable LTM and LPM. */
+ usb_set_usb2_hardware_lpm(udev, 1);
+ usb_unlocked_enable_lpm(udev);
+ usb_enable_ltm(udev);
+ usb_release_bos_descriptor(udev);
+ udev->bos = bos;
return 0;
-
+
re_enumerate:
+ /* LPM state doesn't matter when we're about to destroy the device. */
hub_port_logical_disconnect(parent_hub, port1);
+ usb_release_bos_descriptor(udev);
+ udev->bos = bos;
return -ENODEV;
}
@@ -3987,8 +5438,9 @@ re_enumerate:
* method), performs the port reset, and then lets the drivers know that
* the reset is over (using their post_reset method).
*
- * Return value is the same as for usb_reset_and_verify_device().
+ * Return: The same as for usb_reset_and_verify_device().
*
+ * Note:
* The caller must own the device lock. For example, it's safe to use
* this from a driver probe() routine after downloading new firmware.
* For calls that might not occur during probe(), drivers should lock
@@ -4003,7 +5455,10 @@ int usb_reset_device(struct usb_device *udev)
{
int ret;
int i;
+ unsigned int noio_flag;
+ struct usb_port *port_dev;
struct usb_host_config *config = udev->actconfig;
+ struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
if (udev->state == USB_STATE_NOTATTACHED ||
udev->state == USB_STATE_SUSPENDED) {
@@ -4012,6 +5467,25 @@ int usb_reset_device(struct usb_device *udev)
return -EINVAL;
}
+ if (!udev->parent) {
+ /* this requires hcd-specific logic; see ohci_restart() */
+ dev_dbg(&udev->dev, "%s for root hub!\n", __func__);
+ return -EISDIR;
+ }
+
+ port_dev = hub->ports[udev->portnum - 1];
+
+ /*
+ * Don't allocate memory with GFP_KERNEL in current
+ * context to avoid possible deadlock if usb mass
+ * storage interface or usbnet interface(iSCSI case)
+ * is included in current configuration. The easist
+ * approach is to do it for every device reset,
+ * because the device 'memalloc_noio' flag may have
+ * not been set before reseting the usb device.
+ */
+ noio_flag = memalloc_noio_save();
+
/* Prevent autosuspend during the reset */
usb_autoresume_device(udev);
@@ -4034,7 +5508,9 @@ int usb_reset_device(struct usb_device *udev)
}
}
+ usb_lock_port(port_dev);
ret = usb_reset_and_verify_device(udev);
+ usb_unlock_port(port_dev);
if (config) {
for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) {
@@ -4049,13 +5525,15 @@ int usb_reset_device(struct usb_device *udev)
else if (cintf->condition ==
USB_INTERFACE_BOUND)
rebind = 1;
+ if (rebind)
+ cintf->needs_binding = 1;
}
- if (ret == 0 && rebind)
- usb_rebind_intf(cintf);
}
+ usb_unbind_and_rebind_marked_interfaces(udev);
}
usb_autosuspend_device(udev);
+ memalloc_noio_restore(noio_flag);
return ret;
}
EXPORT_SYMBOL_GPL(usb_reset_device);
@@ -4102,3 +5580,93 @@ 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 = usb_hub_to_struct_hub(hdev);
+
+ if (port1 < 1 || port1 > hdev->maxchild)
+ return NULL;
+ return hub->ports[port1 - 1]->child;
+}
+EXPORT_SYMBOL_GPL(usb_hub_find_child);
+
+void usb_hub_adjust_deviceremovable(struct usb_device *hdev,
+ struct usb_hub_descriptor *desc)
+{
+ struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
+ enum usb_port_connect_type connect_type;
+ int i;
+
+ if (!hub)
+ return;
+
+ if (!hub_is_superspeed(hdev)) {
+ for (i = 1; i <= hdev->maxchild; i++) {
+ struct usb_port *port_dev = hub->ports[i - 1];
+
+ connect_type = port_dev->connect_type;
+ if (connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) {
+ u8 mask = 1 << (i%8);
+
+ if (!(desc->u.hs.DeviceRemovable[i/8] & mask)) {
+ dev_dbg(&port_dev->dev, "DeviceRemovable is changed to 1 according to platform information.\n");
+ desc->u.hs.DeviceRemovable[i/8] |= mask;
+ }
+ }
+ }
+ } else {
+ u16 port_removable = le16_to_cpu(desc->u.ss.DeviceRemovable);
+
+ for (i = 1; i <= hdev->maxchild; i++) {
+ struct usb_port *port_dev = hub->ports[i - 1];
+
+ connect_type = port_dev->connect_type;
+ if (connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) {
+ u16 mask = 1 << i;
+
+ if (!(port_removable & mask)) {
+ dev_dbg(&port_dev->dev, "DeviceRemovable is changed to 1 according to platform information.\n");
+ port_removable |= mask;
+ }
+ }
+ }
+
+ desc->u.ss.DeviceRemovable = cpu_to_le16(port_removable);
+ }
+}
+
+#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
+ * invalid.
+ */
+acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev,
+ int port1)
+{
+ struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
+
+ if (!hub)
+ return NULL;
+
+ return ACPI_HANDLE(&hub->ports[port1 - 1]->dev);
+}
+#endif
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
new file mode 100644
index 00000000000..326308e5396
--- /dev/null
+++ b/drivers/usb/core/hub.h
@@ -0,0 +1,156 @@
+/*
+ * usb hub driver head file
+ *
+ * Copyright (C) 1999 Linus Torvalds
+ * Copyright (C) 1999 Johannes Erdfelt
+ * Copyright (C) 1999 Gregory P. Smith
+ * Copyright (C) 2001 Brad Hards (bhards@bigpond.net.au)
+ * Copyright (C) 2012 Intel Corp (tianyu.lan@intel.com)
+ *
+ * move struct usb_hub to this file.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <linux/usb.h>
+#include <linux/usb/ch11.h>
+#include <linux/usb/hcd.h>
+#include "usb.h"
+
+struct usb_hub {
+ struct device *intfdev; /* the "interface" device */
+ struct usb_device *hdev;
+ struct kref kref;
+ struct urb *urb; /* for interrupt polling pipe */
+
+ /* buffer for urb ... with extra space in case of babble */
+ u8 (*buffer)[8];
+ union {
+ struct usb_hub_status hub;
+ struct usb_port_status port;
+ } *status; /* buffer for status reports */
+ struct mutex status_mutex; /* for the status buffer */
+
+ int error; /* last reported error */
+ int nerrors; /* track consecutive errors */
+
+ struct list_head event_list; /* hubs w/data or errs ready */
+ unsigned long event_bits[1]; /* status change bitmask */
+ unsigned long change_bits[1]; /* ports with logical connect
+ status change */
+ unsigned long removed_bits[1]; /* ports with a "removed"
+ device present */
+ unsigned long wakeup_bits[1]; /* ports that have signaled
+ remote wakeup */
+ unsigned long power_bits[1]; /* ports that are powered */
+ unsigned long child_usage_bits[1]; /* ports powered on for
+ children */
+#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
+#error event_bits[] is too short!
+#endif
+
+ struct usb_hub_descriptor *descriptor; /* class descriptor */
+ 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;
+ unsigned disconnected:1;
+ unsigned in_reset:1;
+
+ unsigned quirk_check_port_auto_suspend:1;
+
+ unsigned has_indicators:1;
+ u8 indicator[USB_MAXCHILDREN];
+ struct delayed_work leds;
+ struct delayed_work init_work;
+ struct usb_port **ports;
+};
+
+/**
+ * struct usb port - kernel's representation of a usb port
+ * @child: usb device attached to the port
+ * @dev: generic device interface
+ * @port_owner: port's owner
+ * @peer: related usb2 and usb3 ports (share the same connector)
+ * @req: default pm qos request for hubs without port power control
+ * @connect_type: port's connect type
+ * @location: opaque representation of platform connector location
+ * @status_lock: synchronize port_event() vs usb_port_{suspend|resume}
+ * @portnum: port index num based one
+ * @is_superspeed cache super-speed status
+ */
+struct usb_port {
+ struct usb_device *child;
+ struct device dev;
+ struct usb_dev_state *port_owner;
+ struct usb_port *peer;
+ struct dev_pm_qos_request *req;
+ enum usb_port_connect_type connect_type;
+ usb_port_location_t location;
+ struct mutex status_lock;
+ u8 portnum;
+ unsigned int is_superspeed:1;
+};
+
+#define to_usb_port(_dev) \
+ container_of(_dev, struct usb_port, dev)
+
+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, 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,
+ bool must_be_connected);
+extern int usb_clear_port_feature(struct usb_device *hdev,
+ int port1, int feature);
+
+static inline bool hub_is_port_power_switchable(struct usb_hub *hub)
+{
+ __le16 hcs;
+
+ if (!hub)
+ return false;
+ hcs = hub->descriptor->wHubCharacteristics;
+ return (le16_to_cpu(hcs) & HUB_CHAR_LPSM) < HUB_CHAR_NO_LPSM;
+}
+
+static inline int hub_is_superspeed(struct usb_device *hdev)
+{
+ return hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS;
+}
+
+static inline unsigned hub_power_on_good_delay(struct usb_hub *hub)
+{
+ unsigned delay = hub->descriptor->bPwrOn2PwrGood * 2;
+
+ /* Wait at least 100 msec for power to become stable */
+ return max(delay, 100U);
+}
+
+static inline int hub_port_debounce_be_connected(struct usb_hub *hub,
+ int port1)
+{
+ return hub_port_debounce(hub, port1, true);
+}
+
+static inline int hub_port_debounce_be_stable(struct usb_hub *hub,
+ int port1)
+{
+ return hub_port_debounce(hub, port1, false);
+}
+
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
deleted file mode 100644
index 9e186f3da83..00000000000
--- a/drivers/usb/core/inode.c
+++ /dev/null
@@ -1,773 +0,0 @@
-/*****************************************************************************/
-
-/*
- * inode.c -- Inode/Dentry functions for the USB device file system.
- *
- * Copyright (C) 2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
- * Copyright (C) 2001,2002,2004 Greg Kroah-Hartman (greg@kroah.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * History:
- * 0.1 04.01.2000 Created
- * 0.2 10.12.2001 converted to use the vfs layer better
- */
-
-/*****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/mount.h>
-#include <linux/pagemap.h>
-#include <linux/init.h>
-#include <linux/proc_fs.h>
-#include <linux/usb.h>
-#include <linux/namei.h>
-#include <linux/usbdevice_fs.h>
-#include <linux/parser.h>
-#include <linux/notifier.h>
-#include <linux/seq_file.h>
-#include <linux/usb/hcd.h>
-#include <asm/byteorder.h>
-#include "usb.h"
-
-#define USBFS_DEFAULT_DEVMODE (S_IWUSR | S_IRUGO)
-#define USBFS_DEFAULT_BUSMODE (S_IXUGO | S_IRUGO)
-#define USBFS_DEFAULT_LISTMODE S_IRUGO
-
-static const struct file_operations default_file_operations;
-static struct vfsmount *usbfs_mount;
-static int usbfs_mount_count; /* = 0 */
-static int ignore_mount = 0;
-
-static struct dentry *devices_usbfs_dentry;
-static int num_buses; /* = 0 */
-
-static uid_t devuid; /* = 0 */
-static uid_t busuid; /* = 0 */
-static uid_t listuid; /* = 0 */
-static gid_t devgid; /* = 0 */
-static gid_t busgid; /* = 0 */
-static gid_t listgid; /* = 0 */
-static umode_t devmode = USBFS_DEFAULT_DEVMODE;
-static umode_t busmode = USBFS_DEFAULT_BUSMODE;
-static umode_t listmode = USBFS_DEFAULT_LISTMODE;
-
-static int usbfs_show_options(struct seq_file *seq, struct dentry *root)
-{
- if (devuid != 0)
- seq_printf(seq, ",devuid=%u", devuid);
- if (devgid != 0)
- seq_printf(seq, ",devgid=%u", devgid);
- if (devmode != USBFS_DEFAULT_DEVMODE)
- seq_printf(seq, ",devmode=%o", devmode);
- if (busuid != 0)
- seq_printf(seq, ",busuid=%u", busuid);
- if (busgid != 0)
- seq_printf(seq, ",busgid=%u", busgid);
- if (busmode != USBFS_DEFAULT_BUSMODE)
- seq_printf(seq, ",busmode=%o", busmode);
- if (listuid != 0)
- seq_printf(seq, ",listuid=%u", listuid);
- if (listgid != 0)
- seq_printf(seq, ",listgid=%u", listgid);
- if (listmode != USBFS_DEFAULT_LISTMODE)
- seq_printf(seq, ",listmode=%o", listmode);
-
- return 0;
-}
-
-enum {
- Opt_devuid, Opt_devgid, Opt_devmode,
- Opt_busuid, Opt_busgid, Opt_busmode,
- Opt_listuid, Opt_listgid, Opt_listmode,
- Opt_err,
-};
-
-static const match_table_t tokens = {
- {Opt_devuid, "devuid=%u"},
- {Opt_devgid, "devgid=%u"},
- {Opt_devmode, "devmode=%o"},
- {Opt_busuid, "busuid=%u"},
- {Opt_busgid, "busgid=%u"},
- {Opt_busmode, "busmode=%o"},
- {Opt_listuid, "listuid=%u"},
- {Opt_listgid, "listgid=%u"},
- {Opt_listmode, "listmode=%o"},
- {Opt_err, NULL}
-};
-
-static int parse_options(struct super_block *s, char *data)
-{
- char *p;
- int option;
-
- /* (re)set to defaults. */
- devuid = 0;
- busuid = 0;
- listuid = 0;
- devgid = 0;
- busgid = 0;
- listgid = 0;
- devmode = USBFS_DEFAULT_DEVMODE;
- busmode = USBFS_DEFAULT_BUSMODE;
- listmode = USBFS_DEFAULT_LISTMODE;
-
- while ((p = strsep(&data, ",")) != NULL) {
- substring_t args[MAX_OPT_ARGS];
- int token;
- if (!*p)
- continue;
-
- token = match_token(p, tokens, args);
- switch (token) {
- case Opt_devuid:
- if (match_int(&args[0], &option))
- return -EINVAL;
- devuid = option;
- break;
- case Opt_devgid:
- if (match_int(&args[0], &option))
- return -EINVAL;
- devgid = option;
- break;
- case Opt_devmode:
- if (match_octal(&args[0], &option))
- return -EINVAL;
- devmode = option & S_IRWXUGO;
- break;
- case Opt_busuid:
- if (match_int(&args[0], &option))
- return -EINVAL;
- busuid = option;
- break;
- case Opt_busgid:
- if (match_int(&args[0], &option))
- return -EINVAL;
- busgid = option;
- break;
- case Opt_busmode:
- if (match_octal(&args[0], &option))
- return -EINVAL;
- busmode = option & S_IRWXUGO;
- break;
- case Opt_listuid:
- if (match_int(&args[0], &option))
- return -EINVAL;
- listuid = option;
- break;
- case Opt_listgid:
- if (match_int(&args[0], &option))
- return -EINVAL;
- listgid = option;
- break;
- case Opt_listmode:
- if (match_octal(&args[0], &option))
- return -EINVAL;
- listmode = option & S_IRWXUGO;
- break;
- default:
- printk(KERN_ERR "usbfs: unrecognised mount option "
- "\"%s\" or missing value\n", p);
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-static void update_special(struct dentry *special)
-{
- special->d_inode->i_uid = listuid;
- special->d_inode->i_gid = listgid;
- special->d_inode->i_mode = S_IFREG | listmode;
-}
-
-static void update_dev(struct dentry *dev)
-{
- dev->d_inode->i_uid = devuid;
- dev->d_inode->i_gid = devgid;
- dev->d_inode->i_mode = S_IFREG | devmode;
-}
-
-static void update_bus(struct dentry *bus)
-{
- struct dentry *dev = NULL;
-
- bus->d_inode->i_uid = busuid;
- bus->d_inode->i_gid = busgid;
- bus->d_inode->i_mode = S_IFDIR | busmode;
-
- mutex_lock(&bus->d_inode->i_mutex);
-
- list_for_each_entry(dev, &bus->d_subdirs, d_u.d_child)
- if (dev->d_inode)
- update_dev(dev);
-
- mutex_unlock(&bus->d_inode->i_mutex);
-}
-
-static void update_sb(struct super_block *sb)
-{
- struct dentry *root = sb->s_root;
- struct dentry *bus = NULL;
-
- if (!root)
- return;
-
- mutex_lock_nested(&root->d_inode->i_mutex, I_MUTEX_PARENT);
-
- list_for_each_entry(bus, &root->d_subdirs, d_u.d_child) {
- if (bus->d_inode) {
- switch (S_IFMT & bus->d_inode->i_mode) {
- case S_IFDIR:
- update_bus(bus);
- break;
- case S_IFREG:
- update_special(bus);
- break;
- default:
- printk(KERN_WARNING "usbfs: Unknown node %s "
- "mode %x found on remount!\n",
- bus->d_name.name, bus->d_inode->i_mode);
- break;
- }
- }
- }
-
- mutex_unlock(&root->d_inode->i_mutex);
-}
-
-static int remount(struct super_block *sb, int *flags, char *data)
-{
- /* If this is not a real mount,
- * i.e. it's a simple_pin_fs from create_special_files,
- * then ignore it.
- */
- if (ignore_mount)
- return 0;
-
- if (parse_options(sb, data)) {
- printk(KERN_WARNING "usbfs: mount parameter error.\n");
- return -EINVAL;
- }
-
- if (usbfs_mount)
- update_sb(usbfs_mount->mnt_sb);
-
- return 0;
-}
-
-static struct inode *usbfs_get_inode (struct super_block *sb, umode_t mode, dev_t dev)
-{
- struct inode *inode = new_inode(sb);
-
- if (inode) {
- inode->i_ino = get_next_ino();
- inode_init_owner(inode, NULL, mode);
- inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- switch (mode & S_IFMT) {
- default:
- init_special_inode(inode, mode, dev);
- break;
- case S_IFREG:
- inode->i_fop = &default_file_operations;
- break;
- case S_IFDIR:
- inode->i_op = &simple_dir_inode_operations;
- inode->i_fop = &simple_dir_operations;
-
- /* directory inodes start off with i_nlink == 2 (for "." entry) */
- inc_nlink(inode);
- break;
- }
- }
- return inode;
-}
-
-/* SMP-safe */
-static int usbfs_mknod (struct inode *dir, struct dentry *dentry, umode_t mode,
- dev_t dev)
-{
- struct inode *inode = usbfs_get_inode(dir->i_sb, mode, dev);
- int error = -EPERM;
-
- if (dentry->d_inode)
- return -EEXIST;
-
- if (inode) {
- d_instantiate(dentry, inode);
- dget(dentry);
- error = 0;
- }
- return error;
-}
-
-static int usbfs_mkdir (struct inode *dir, struct dentry *dentry, umode_t mode)
-{
- int res;
-
- mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
- res = usbfs_mknod (dir, dentry, mode, 0);
- if (!res)
- inc_nlink(dir);
- return res;
-}
-
-static int usbfs_create (struct inode *dir, struct dentry *dentry, umode_t mode)
-{
- mode = (mode & S_IALLUGO) | S_IFREG;
- return usbfs_mknod (dir, dentry, mode, 0);
-}
-
-static inline int usbfs_positive (struct dentry *dentry)
-{
- return dentry->d_inode && !d_unhashed(dentry);
-}
-
-static int usbfs_empty (struct dentry *dentry)
-{
- struct list_head *list;
-
- spin_lock(&dentry->d_lock);
- list_for_each(list, &dentry->d_subdirs) {
- struct dentry *de = list_entry(list, struct dentry, d_u.d_child);
-
- spin_lock_nested(&de->d_lock, DENTRY_D_LOCK_NESTED);
- if (usbfs_positive(de)) {
- spin_unlock(&de->d_lock);
- spin_unlock(&dentry->d_lock);
- return 0;
- }
- spin_unlock(&de->d_lock);
- }
- spin_unlock(&dentry->d_lock);
- return 1;
-}
-
-static int usbfs_unlink (struct inode *dir, struct dentry *dentry)
-{
- struct inode *inode = dentry->d_inode;
- mutex_lock(&inode->i_mutex);
- drop_nlink(dentry->d_inode);
- dput(dentry);
- mutex_unlock(&inode->i_mutex);
- d_delete(dentry);
- return 0;
-}
-
-static int usbfs_rmdir(struct inode *dir, struct dentry *dentry)
-{
- int error = -ENOTEMPTY;
- struct inode * inode = dentry->d_inode;
-
- mutex_lock(&inode->i_mutex);
- dentry_unhash(dentry);
- if (usbfs_empty(dentry)) {
- dont_mount(dentry);
- drop_nlink(dentry->d_inode);
- drop_nlink(dentry->d_inode);
- dput(dentry);
- inode->i_flags |= S_DEAD;
- drop_nlink(dir);
- error = 0;
- }
- mutex_unlock(&inode->i_mutex);
- if (!error)
- d_delete(dentry);
- return error;
-}
-
-
-/* default file operations */
-static ssize_t default_read_file (struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- return 0;
-}
-
-static ssize_t default_write_file (struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- return count;
-}
-
-static loff_t default_file_lseek (struct file *file, loff_t offset, int orig)
-{
- loff_t retval = -EINVAL;
-
- mutex_lock(&file->f_path.dentry->d_inode->i_mutex);
- switch(orig) {
- case 0:
- if (offset > 0) {
- file->f_pos = offset;
- retval = file->f_pos;
- }
- break;
- case 1:
- if ((offset + file->f_pos) > 0) {
- file->f_pos += offset;
- retval = file->f_pos;
- }
- break;
- default:
- break;
- }
- mutex_unlock(&file->f_path.dentry->d_inode->i_mutex);
- return retval;
-}
-
-static int default_open (struct inode *inode, struct file *file)
-{
- if (inode->i_private)
- file->private_data = inode->i_private;
-
- return 0;
-}
-
-static const struct file_operations default_file_operations = {
- .read = default_read_file,
- .write = default_write_file,
- .open = default_open,
- .llseek = default_file_lseek,
-};
-
-static const struct super_operations usbfs_ops = {
- .statfs = simple_statfs,
- .drop_inode = generic_delete_inode,
- .remount_fs = remount,
- .show_options = usbfs_show_options,
-};
-
-static int usbfs_fill_super(struct super_block *sb, void *data, int silent)
-{
- struct inode *inode;
- struct dentry *root;
-
- sb->s_blocksize = PAGE_CACHE_SIZE;
- sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
- sb->s_magic = USBDEVICE_SUPER_MAGIC;
- sb->s_op = &usbfs_ops;
- sb->s_time_gran = 1;
- inode = usbfs_get_inode(sb, S_IFDIR | 0755, 0);
-
- if (!inode) {
- dbg("%s: could not get inode!",__func__);
- return -ENOMEM;
- }
-
- root = d_alloc_root(inode);
- if (!root) {
- dbg("%s: could not get root dentry!",__func__);
- iput(inode);
- return -ENOMEM;
- }
- sb->s_root = root;
- return 0;
-}
-
-/*
- * fs_create_by_name - create a file, given a name
- * @name: name of file
- * @mode: type of file
- * @parent: dentry of directory to create it in
- * @dentry: resulting dentry of file
- *
- * This function handles both regular files and directories.
- */
-static int fs_create_by_name (const char *name, umode_t mode,
- struct dentry *parent, struct dentry **dentry)
-{
- int error = 0;
-
- /* If the parent is not specified, we create it in the root.
- * We need the root dentry to do this, which is in the super
- * block. A pointer to that is in the struct vfsmount that we
- * have around.
- */
- if (!parent ) {
- if (usbfs_mount)
- parent = usbfs_mount->mnt_root;
- }
-
- if (!parent) {
- dbg("Ah! can not find a parent!");
- return -EFAULT;
- }
-
- *dentry = NULL;
- mutex_lock(&parent->d_inode->i_mutex);
- *dentry = lookup_one_len(name, parent, strlen(name));
- if (!IS_ERR(*dentry)) {
- if (S_ISDIR(mode))
- error = usbfs_mkdir (parent->d_inode, *dentry, mode);
- else
- error = usbfs_create (parent->d_inode, *dentry, mode);
- } else
- error = PTR_ERR(*dentry);
- mutex_unlock(&parent->d_inode->i_mutex);
-
- return error;
-}
-
-static struct dentry *fs_create_file (const char *name, umode_t mode,
- struct dentry *parent, void *data,
- const struct file_operations *fops,
- uid_t uid, gid_t gid)
-{
- struct dentry *dentry;
- int error;
-
- dbg("creating file '%s'",name);
-
- error = fs_create_by_name (name, mode, parent, &dentry);
- if (error) {
- dentry = NULL;
- } else {
- if (dentry->d_inode) {
- if (data)
- dentry->d_inode->i_private = data;
- if (fops)
- dentry->d_inode->i_fop = fops;
- dentry->d_inode->i_uid = uid;
- dentry->d_inode->i_gid = gid;
- }
- }
-
- return dentry;
-}
-
-static void fs_remove_file (struct dentry *dentry)
-{
- struct dentry *parent = dentry->d_parent;
-
- if (!parent || !parent->d_inode)
- return;
-
- mutex_lock_nested(&parent->d_inode->i_mutex, I_MUTEX_PARENT);
- if (usbfs_positive(dentry)) {
- if (dentry->d_inode) {
- if (S_ISDIR(dentry->d_inode->i_mode))
- usbfs_rmdir(parent->d_inode, dentry);
- else
- usbfs_unlink(parent->d_inode, dentry);
- dput(dentry);
- }
- }
- mutex_unlock(&parent->d_inode->i_mutex);
-}
-
-/* --------------------------------------------------------------------- */
-
-static struct dentry *usb_mount(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data)
-{
- return mount_single(fs_type, flags, data, usbfs_fill_super);
-}
-
-static struct file_system_type usb_fs_type = {
- .owner = THIS_MODULE,
- .name = "usbfs",
- .mount = usb_mount,
- .kill_sb = kill_litter_super,
-};
-
-/* --------------------------------------------------------------------- */
-
-static int create_special_files (void)
-{
- struct dentry *parent;
- int retval;
-
- /* the simple_pin_fs calls will call remount with no options
- * without this flag that would overwrite the real mount options (if any)
- */
- ignore_mount = 1;
-
- /* create the devices special file */
- retval = simple_pin_fs(&usb_fs_type, &usbfs_mount, &usbfs_mount_count);
- if (retval) {
- printk(KERN_ERR "Unable to get usbfs mount\n");
- goto exit;
- }
-
- ignore_mount = 0;
-
- parent = usbfs_mount->mnt_root;
- devices_usbfs_dentry = fs_create_file ("devices",
- listmode | S_IFREG, parent,
- NULL, &usbfs_devices_fops,
- listuid, listgid);
- if (devices_usbfs_dentry == NULL) {
- printk(KERN_ERR "Unable to create devices usbfs file\n");
- retval = -ENODEV;
- goto error_clean_mounts;
- }
-
- goto exit;
-
-error_clean_mounts:
- simple_release_fs(&usbfs_mount, &usbfs_mount_count);
-exit:
- return retval;
-}
-
-static void remove_special_files (void)
-{
- if (devices_usbfs_dentry)
- fs_remove_file (devices_usbfs_dentry);
- devices_usbfs_dentry = NULL;
- simple_release_fs(&usbfs_mount, &usbfs_mount_count);
-}
-
-void usbfs_update_special (void)
-{
- struct inode *inode;
-
- if (devices_usbfs_dentry) {
- inode = devices_usbfs_dentry->d_inode;
- if (inode)
- inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- }
-}
-
-static void usbfs_add_bus(struct usb_bus *bus)
-{
- struct dentry *parent;
- char name[8];
- int retval;
-
- /* create the special files if this is the first bus added */
- if (num_buses == 0) {
- retval = create_special_files();
- if (retval)
- return;
- }
- ++num_buses;
-
- sprintf (name, "%03d", bus->busnum);
-
- parent = usbfs_mount->mnt_root;
- bus->usbfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent,
- bus, NULL, busuid, busgid);
- if (bus->usbfs_dentry == NULL) {
- printk(KERN_ERR "Error creating usbfs bus entry\n");
- return;
- }
-}
-
-static void usbfs_remove_bus(struct usb_bus *bus)
-{
- if (bus->usbfs_dentry) {
- fs_remove_file (bus->usbfs_dentry);
- bus->usbfs_dentry = NULL;
- }
-
- --num_buses;
- if (num_buses <= 0) {
- remove_special_files();
- num_buses = 0;
- }
-}
-
-static void usbfs_add_device(struct usb_device *dev)
-{
- char name[8];
- int i;
- int i_size;
-
- sprintf (name, "%03d", dev->devnum);
- dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG,
- dev->bus->usbfs_dentry, dev,
- &usbdev_file_operations,
- devuid, devgid);
- if (dev->usbfs_dentry == NULL) {
- printk(KERN_ERR "Error creating usbfs device entry\n");
- return;
- }
-
- /* Set the size of the device's file to be
- * equal to the size of the device descriptors. */
- i_size = sizeof (struct usb_device_descriptor);
- for (i = 0; i < dev->descriptor.bNumConfigurations; ++i) {
- struct usb_config_descriptor *config =
- (struct usb_config_descriptor *)dev->rawdescriptors[i];
- i_size += le16_to_cpu(config->wTotalLength);
- }
- if (dev->usbfs_dentry->d_inode)
- dev->usbfs_dentry->d_inode->i_size = i_size;
-}
-
-static void usbfs_remove_device(struct usb_device *dev)
-{
- if (dev->usbfs_dentry) {
- fs_remove_file (dev->usbfs_dentry);
- dev->usbfs_dentry = NULL;
- }
-}
-
-static int usbfs_notify(struct notifier_block *self, unsigned long action, void *dev)
-{
- switch (action) {
- case USB_DEVICE_ADD:
- usbfs_add_device(dev);
- break;
- case USB_DEVICE_REMOVE:
- usbfs_remove_device(dev);
- break;
- case USB_BUS_ADD:
- usbfs_add_bus(dev);
- break;
- case USB_BUS_REMOVE:
- usbfs_remove_bus(dev);
- }
-
- usbfs_update_special();
- usbfs_conn_disc_event();
- return NOTIFY_OK;
-}
-
-static struct notifier_block usbfs_nb = {
- .notifier_call = usbfs_notify,
-};
-
-/* --------------------------------------------------------------------- */
-
-static struct proc_dir_entry *usbdir = NULL;
-
-int __init usbfs_init(void)
-{
- int retval;
-
- retval = register_filesystem(&usb_fs_type);
- if (retval)
- return retval;
-
- usb_register_notify(&usbfs_nb);
-
- /* create mount point for usbfs */
- usbdir = proc_mkdir("bus/usb", NULL);
-
- return 0;
-}
-
-void usbfs_cleanup(void)
-{
- usb_unregister_notify(&usbfs_nb);
- unregister_filesystem(&usb_fs_type);
- if (usbdir)
- remove_proc_entry("bus/usb", NULL);
-}
-
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index b3bdfede45e..0c8a7fc4dad 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -6,7 +6,6 @@
#include <linux/usb.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/mm.h>
#include <linux/timer.h>
#include <linux/ctype.h>
@@ -119,15 +118,15 @@ static int usb_internal_control_msg(struct usb_device *usb_dev,
* This function sends a simple control message to a specified endpoint and
* waits for the message to complete, or timeout.
*
- * If successful, it returns the number of bytes transferred, otherwise a
- * negative error number.
- *
* Don't use this function from within an interrupt context, like a bottom half
* handler. If you need an asynchronous message, or need to send a message
* from within interrupt context, use usb_submit_urb().
* If a thread in your driver uses this call, make sure your disconnect()
* method can wait for it to complete. Since you don't have a handle on the
* URB used, you can't cancel the request.
+ *
+ * Return: If successful, the number of bytes transferred. Otherwise, a negative
+ * error number.
*/
int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
__u8 requesttype, __u16 value, __u16 index, void *data,
@@ -146,8 +145,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);
@@ -172,15 +169,16 @@ EXPORT_SYMBOL_GPL(usb_control_msg);
* This function sends a simple interrupt message to a specified endpoint and
* waits for the message to complete, or timeout.
*
- * If successful, it returns 0, otherwise a negative error number. The number
- * of actual bytes transferred will be stored in the actual_length paramater.
- *
* Don't use this function from within an interrupt context, like a bottom half
* handler. If you need an asynchronous message, or need to send a message
* from within interrupt context, use usb_submit_urb() If a thread in your
* driver uses this call, make sure your disconnect() method can wait for it to
* complete. Since you don't have a handle on the URB used, you can't cancel
* the request.
+ *
+ * Return:
+ * If successful, 0. Otherwise a negative error number. The number of actual
+ * bytes transferred will be stored in the @actual_length parameter.
*/
int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length, int timeout)
@@ -205,9 +203,6 @@ EXPORT_SYMBOL_GPL(usb_interrupt_msg);
* This function sends a simple bulk message to a specified endpoint
* and waits for the message to complete, or timeout.
*
- * If successful, it returns 0, otherwise a negative error number. The number
- * of actual bytes transferred will be stored in the actual_length paramater.
- *
* Don't use this function from within an interrupt context, like a bottom half
* handler. If you need an asynchronous message, or need to send a message
* from within interrupt context, use usb_submit_urb() If a thread in your
@@ -219,6 +214,11 @@ EXPORT_SYMBOL_GPL(usb_interrupt_msg);
* users are forced to abuse this routine by using it to submit URBs for
* interrupt endpoints. We will take the liberty of creating an interrupt URB
* (with the default interval) if the target is an interrupt endpoint.
+ *
+ * Return:
+ * If successful, 0. Otherwise a negative error number. The number of actual
+ * bytes transferred will be stored in the @actual_length parameter.
+ *
*/
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length, int timeout)
@@ -254,7 +254,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;
}
@@ -302,22 +302,22 @@ 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)
+ retval != -EBUSY &&
+ retval != -EIDRM)
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);
}
- urb->dev = NULL;
/* on the last completion, signal usb_sg_wait() */
io->bytes += urb->actual_length;
@@ -343,9 +343,9 @@ static void sg_complete(struct urb *urb)
* send every byte identified in the list.
* @mem_flags: SLAB_* flags affecting memory allocations in this call
*
- * Returns zero for success, else a negative errno value. This initializes a
- * scatter/gather request, allocating resources such as I/O mappings and urb
- * memory (except maybe memory used by USB controller drivers).
+ * This initializes a scatter/gather request, allocating resources such as
+ * I/O mappings and urb memory (except maybe memory used by USB controller
+ * drivers).
*
* The request must be issued using usb_sg_wait(), which waits for the I/O to
* complete (or to be canceled) and then cleans up all resources allocated by
@@ -353,6 +353,8 @@ static void sg_complete(struct urb *urb)
*
* The request may be canceled with usb_sg_cancel(), either before or after
* usb_sg_wait() is called.
+ *
+ * Return: Zero for success, else a negative errno value.
*/
int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
unsigned pipe, unsigned period, struct scatterlist *sg,
@@ -381,7 +383,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;
@@ -513,9 +515,9 @@ 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;
+ /* after we submit, let completions or cancellations fire;
* we handshake using io->status.
*/
spin_unlock_irq(&io->lock);
@@ -524,7 +526,6 @@ void usb_sg_wait(struct usb_sg_request *io)
case -ENXIO: /* hc didn't queue this one */
case -EAGAIN:
case -ENOMEM:
- io->urbs[i]->dev = NULL;
retval = 0;
yield();
break;
@@ -542,7 +543,6 @@ void usb_sg_wait(struct usb_sg_request *io)
/* fail any uncompleted urbs */
default:
- io->urbs[i]->dev = NULL;
io->urbs[i]->status = retval;
dev_dbg(&io->dev->dev, "%s, submit --> %d\n",
__func__, retval);
@@ -590,10 +590,13 @@ 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]);
- if (retval != -EINPROGRESS && retval != -EBUSY)
+ retval = usb_unlink_urb(io->urbs[i]);
+ if (retval != -EINPROGRESS
+ && retval != -ENODEV
+ && retval != -EBUSY
+ && retval != -EIDRM)
dev_warn(&io->dev->dev, "%s, unlink --> %d\n",
__func__, retval);
}
@@ -624,7 +627,7 @@ EXPORT_SYMBOL_GPL(usb_sg_cancel);
*
* This call is synchronous, and may not be used in an interrupt context.
*
- * Returns the number of bytes received on success, or else the status code
+ * Return: The number of bytes received on success, or else the status code
* returned by the underlying usb_control_msg() call.
*/
int usb_get_descriptor(struct usb_device *dev, unsigned char type,
@@ -672,7 +675,7 @@ EXPORT_SYMBOL_GPL(usb_get_descriptor);
*
* This call is synchronous, and may not be used in an interrupt context.
*
- * Returns the number of bytes received on success, or else the status code
+ * Return: The number of bytes received on success, or else the status code
* returned by the underlying usb_control_msg() call.
*/
static int usb_get_string(struct usb_device *dev, unsigned short langid,
@@ -806,7 +809,7 @@ static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf)
*
* This call is synchronous, and may not be used in an interrupt context.
*
- * Returns length of the string (>= 0) or usb_control_msg status (< 0).
+ * Return: length of the string (>= 0) or usb_control_msg status (< 0).
*/
int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
{
@@ -854,8 +857,8 @@ EXPORT_SYMBOL_GPL(usb_string);
* @udev: the device whose string descriptor is being read
* @index: the descriptor index
*
- * Returns a pointer to a kmalloc'ed buffer containing the descriptor string,
- * or NULL if the index is 0 or the string could not be read.
+ * Return: A pointer to a kmalloc'ed buffer containing the descriptor string,
+ * or %NULL if the index is 0 or the string could not be read.
*/
char *usb_cache_string(struct usb_device *udev, int index)
{
@@ -895,7 +898,7 @@ char *usb_cache_string(struct usb_device *udev, int index)
*
* This call is synchronous, and may not be used in an interrupt context.
*
- * Returns the number of bytes received on success, or else the status code
+ * Return: The number of bytes received on success, or else the status code
* returned by the underlying usb_control_msg() call.
*/
int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
@@ -935,13 +938,13 @@ int usb_get_device_descriptor(struct usb_device *dev, unsigned int size)
*
* This call is synchronous, and may not be used in an interrupt context.
*
- * Returns the number of bytes received on success, or else the status code
- * returned by the underlying usb_control_msg() call.
+ * Returns 0 and the status value in *@data (in host byte order) on success,
+ * or else the status code from the underlying usb_control_msg() call.
*/
int usb_get_status(struct usb_device *dev, int type, int target, void *data)
{
int ret;
- u16 *status = kmalloc(sizeof(*status), GFP_KERNEL);
+ __le16 *status = kmalloc(sizeof(*status), GFP_KERNEL);
if (!status)
return -ENOMEM;
@@ -950,7 +953,12 @@ int usb_get_status(struct usb_device *dev, int type, int target, void *data)
USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, status,
sizeof(*status), USB_CTRL_GET_TIMEOUT);
- *(u16 *)data = *status;
+ if (ret == 2) {
+ *(u16 *) data = le16_to_cpu(*status);
+ ret = 0;
+ } else if (ret >= 0) {
+ ret = -EIO;
+ }
kfree(status);
return ret;
}
@@ -976,7 +984,7 @@ EXPORT_SYMBOL_GPL(usb_get_status);
*
* This call is synchronous, and may not be used in an interrupt context.
*
- * Returns zero on success, or else the status code returned by the
+ * Return: Zero on success, or else the status code returned by the
* underlying usb_control_msg() call.
*/
int usb_clear_halt(struct usb_device *dev, int pipe)
@@ -1135,8 +1143,6 @@ void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf,
* Deallocates hcd/hardware state for the endpoints (nuking all or most
* pending urbs) and usbcore state for the interfaces, so that usbcore
* must usb_set_configuration() before any interfaces could be used.
- *
- * Must be called with hcd->bandwidth_mutex held.
*/
void usb_disable_device(struct usb_device *dev, int skip_ep0)
{
@@ -1175,6 +1181,12 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
put_device(&dev->actconfig->interface[i]->dev);
dev->actconfig->interface[i] = NULL;
}
+
+ if (dev->usb2_hw_lpm_enabled == 1)
+ usb_set_usb2_hardware_lpm(dev, 0);
+ usb_unlocked_disable_lpm(dev);
+ usb_disable_ltm(dev);
+
dev->actconfig = NULL;
if (dev->state == USB_STATE_CONFIGURED)
usb_set_device_state(dev, USB_STATE_ADDRESS);
@@ -1189,7 +1201,9 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
usb_disable_endpoint(dev, i + USB_DIR_IN, false);
}
/* Remove endpoints from the host controller internal state */
+ mutex_lock(hcd->bandwidth_mutex);
usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
+ mutex_unlock(hcd->bandwidth_mutex);
/* Second pass: remove endpoint pointers */
}
for (i = skip_ep0; i < 16; ++i) {
@@ -1271,7 +1285,7 @@ void usb_enable_interface(struct usb_device *dev,
* endpoints in that interface; all such urbs must first be completed
* (perhaps forced by unlinking).
*
- * Returns zero on success, or else the status code returned by the
+ * Return: Zero on success, or else the status code returned by the
* underlying usb_control_msg() call.
*/
int usb_set_interface(struct usb_device *dev, int interface, int alternate)
@@ -1279,8 +1293,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
struct usb_interface *iface;
struct usb_host_interface *alt;
struct usb_hcd *hcd = bus_to_hcd(dev->bus);
- int ret;
- int manual = 0;
+ int i, ret, manual = 0;
unsigned int epaddr;
unsigned int pipe;
@@ -1307,10 +1320,23 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
* Remove the current alt setting and add the new alt setting.
*/
mutex_lock(hcd->bandwidth_mutex);
+ /* Disable LPM, and re-enable it once the new alt setting is installed,
+ * so that the xHCI driver can recalculate the U1/U2 timeouts.
+ */
+ if (usb_disable_lpm(dev)) {
+ dev_err(&iface->dev, "%s Failed to disable LPM\n.", __func__);
+ mutex_unlock(hcd->bandwidth_mutex);
+ return -ENOMEM;
+ }
+ /* Changing alt-setting also frees any allocated streams */
+ for (i = 0; i < iface->cur_altsetting->desc.bNumEndpoints; i++)
+ iface->cur_altsetting->endpoint[i].streams = 0;
+
ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt);
if (ret < 0) {
dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n",
alternate);
+ usb_enable_lpm(dev);
mutex_unlock(hcd->bandwidth_mutex);
return ret;
}
@@ -1333,6 +1359,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
} else if (ret < 0) {
/* Re-instate the old alt setting */
usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting);
+ usb_enable_lpm(dev);
mutex_unlock(hcd->bandwidth_mutex);
return ret;
}
@@ -1353,6 +1380,9 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
iface->cur_altsetting = alt;
+ /* Now that the interface is installed, re-enable LPM. */
+ usb_unlocked_enable_lpm(dev);
+
/* If the interface only has one altsetting and the device didn't
* accept the request, we attempt to carry out the equivalent action
* by manually clearing the HALT feature for each endpoint in the
@@ -1412,7 +1442,7 @@ EXPORT_SYMBOL_GPL(usb_set_interface);
*
* The caller must own the device lock.
*
- * Returns zero on success, else a negative error code.
+ * Return: Zero on success, else a negative error code.
*/
int usb_reset_configuration(struct usb_device *dev)
{
@@ -1436,6 +1466,14 @@ int usb_reset_configuration(struct usb_device *dev)
config = dev->actconfig;
retval = 0;
mutex_lock(hcd->bandwidth_mutex);
+ /* Disable LPM, and re-enable it once the configuration is reset, so
+ * that the xHCI driver can recalculate the U1/U2 timeouts.
+ */
+ if (usb_disable_lpm(dev)) {
+ dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);
+ mutex_unlock(hcd->bandwidth_mutex);
+ return -ENOMEM;
+ }
/* Make sure we have enough bandwidth for each alternate setting 0 */
for (i = 0; i < config->desc.bNumInterfaces; i++) {
struct usb_interface *intf = config->interface[i];
@@ -1464,6 +1502,7 @@ reset_old_alts:
usb_hcd_alloc_bandwidth(dev, NULL,
alt, intf->cur_altsetting);
}
+ usb_enable_lpm(dev);
mutex_unlock(hcd->bandwidth_mutex);
return retval;
}
@@ -1501,6 +1540,8 @@ reset_old_alts:
create_intf_ep_devs(intf);
}
}
+ /* Now that the interfaces are installed, re-enable LPM. */
+ usb_unlocked_enable_lpm(dev);
return 0;
}
EXPORT_SYMBOL_GPL(usb_reset_configuration);
@@ -1515,7 +1556,6 @@ static void usb_release_interface(struct device *dev)
kfree(intf);
}
-#ifdef CONFIG_HOTPLUG
static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct usb_device *usb_dev;
@@ -1534,7 +1574,7 @@ static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env)
if (add_uevent_var(env,
"MODALIAS=usb:"
- "v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
+ "v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02Xin%02X",
le16_to_cpu(usb_dev->descriptor.idVendor),
le16_to_cpu(usb_dev->descriptor.idProduct),
le16_to_cpu(usb_dev->descriptor.bcdDevice),
@@ -1543,20 +1583,13 @@ static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env)
usb_dev->descriptor.bDeviceProtocol,
alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass,
- alt->desc.bInterfaceProtocol))
+ alt->desc.bInterfaceProtocol,
+ alt->desc.bInterfaceNumber))
return -ENOMEM;
return 0;
}
-#else
-
-static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env)
-{
- return -ENODEV;
-}
-#endif /* CONFIG_HOTPLUG */
-
struct device_type usb_if_device_type = {
.name = "usb_interface",
.release = usb_release_interface,
@@ -1734,7 +1767,7 @@ free_interfaces:
}
}
- i = dev->bus_mA - cp->desc.bMaxPower * 2;
+ i = dev->bus_mA - usb_get_max_power(dev, cp);
if (i < 0)
dev_warn(&dev->dev, "new config #%d exceeds power "
"limit by %dmA\n",
@@ -1749,7 +1782,6 @@ free_interfaces:
/* if it's already configured, clear out old state first.
* getting rid of old interfaces means unbinding their drivers.
*/
- mutex_lock(hcd->bandwidth_mutex);
if (dev->state != USB_STATE_ADDRESS)
usb_disable_device(dev, 1); /* Skip ep0 */
@@ -1762,35 +1794,28 @@ free_interfaces:
* host controller will not allow submissions to dropped endpoints. If
* this call fails, the device state is unchanged.
*/
- ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);
- if (ret < 0) {
+ mutex_lock(hcd->bandwidth_mutex);
+ /* Disable LPM, and re-enable it once the new configuration is
+ * installed, so that the xHCI driver can recalculate the U1/U2
+ * timeouts.
+ */
+ if (dev->actconfig && usb_disable_lpm(dev)) {
+ dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);
mutex_unlock(hcd->bandwidth_mutex);
- usb_autosuspend_device(dev);
+ ret = -ENOMEM;
goto free_interfaces;
}
-
- ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
- NULL, 0, USB_CTRL_SET_TIMEOUT);
+ ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);
if (ret < 0) {
- /* All the old state is gone, so what else can we do?
- * The device is probably useless now anyway.
- */
- cp = NULL;
- }
-
- dev->actconfig = cp;
- if (!cp) {
- usb_set_device_state(dev, USB_STATE_ADDRESS);
- usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
+ if (dev->actconfig)
+ usb_enable_lpm(dev);
mutex_unlock(hcd->bandwidth_mutex);
usb_autosuspend_device(dev);
goto free_interfaces;
}
- mutex_unlock(hcd->bandwidth_mutex);
- usb_set_device_state(dev, USB_STATE_CONFIGURED);
- /* Initialize the new interface structures and the
+ /*
+ * Initialize the new interface structures and the
* hc/hcd/usbcore interface/endpoint state.
*/
for (i = 0; i < nintf; ++i) {
@@ -1802,7 +1827,6 @@ free_interfaces:
intfc = cp->intf_cache[i];
intf->altsetting = intfc->altsetting;
intf->num_altsetting = intfc->num_altsetting;
- intf->intf_assoc = find_iad(dev, cp, i);
kref_get(&intfc->ref);
alt = usb_altnum_to_altsetting(intf, 0);
@@ -1815,6 +1839,8 @@ free_interfaces:
if (!alt)
alt = &intf->altsetting[0];
+ intf->intf_assoc =
+ find_iad(dev, cp, alt->desc.bInterfaceNumber);
intf->cur_altsetting = alt;
usb_enable_interface(dev, intf, true);
intf->dev.parent = &dev->dev;
@@ -1833,10 +1859,44 @@ free_interfaces:
}
kfree(new_interfaces);
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (ret < 0 && cp) {
+ /*
+ * All the old state is gone, so what else can we do?
+ * The device is probably useless now anyway.
+ */
+ usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
+ for (i = 0; i < nintf; ++i) {
+ usb_disable_interface(dev, cp->interface[i], true);
+ put_device(&cp->interface[i]->dev);
+ cp->interface[i] = NULL;
+ }
+ cp = NULL;
+ }
+
+ dev->actconfig = cp;
+ mutex_unlock(hcd->bandwidth_mutex);
+
+ if (!cp) {
+ usb_set_device_state(dev, USB_STATE_ADDRESS);
+
+ /* Leave LPM disabled while the device is unconfigured. */
+ usb_autosuspend_device(dev);
+ return ret;
+ }
+ usb_set_device_state(dev, USB_STATE_CONFIGURED);
+
if (cp->string == NULL &&
!(dev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS))
cp->string = usb_cache_string(dev, cp->desc.iConfiguration);
+ /* Now that the interfaces are installed, re-enable LPM. */
+ usb_unlocked_enable_lpm(dev);
+ /* Enable LTM if it was turned off by usb_disable_device. */
+ usb_enable_ltm(dev);
+
/* Now that all the interfaces are set up, register them
* to trigger binding of drivers to interfaces. probe()
* routines may install different altsettings and may
@@ -1863,6 +1923,7 @@ free_interfaces:
usb_autosuspend_device(dev);
return 0;
}
+EXPORT_SYMBOL_GPL(usb_set_configuration);
static LIST_HEAD(set_config_list);
static DEFINE_SPINLOCK(set_config_lock);
@@ -1924,7 +1985,7 @@ static void cancel_async_set_config(struct usb_device *udev)
* routine gets around the normal restrictions by using a work thread to
* submit the change-config request.
*
- * Returns 0 if the request was successfully queued, error code otherwise.
+ * Return: 0 if the request was successfully queued, error code otherwise.
* The caller has no way to know whether the queued request will eventually
* succeed.
*/
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
new file mode 100644
index 00000000000..fe1b6d0967e
--- /dev/null
+++ b/drivers/usb/core/port.c
@@ -0,0 +1,484 @@
+/*
+ * usb port device code
+ *
+ * Copyright (C) 2012 Intel Corp
+ *
+ * Author: Lan Tianyu <tianyu.lan@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/pm_qos.h>
+
+#include "hub.h"
+
+static int usb_port_block_power_off;
+
+static const struct attribute_group *port_dev_group[];
+
+static ssize_t connect_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+ char *result;
+
+ switch (port_dev->connect_type) {
+ case USB_PORT_CONNECT_TYPE_HOT_PLUG:
+ result = "hotplug";
+ break;
+ case USB_PORT_CONNECT_TYPE_HARD_WIRED:
+ result = "hardwired";
+ break;
+ case USB_PORT_NOT_USED:
+ result = "not used";
+ break;
+ default:
+ result = "unknown";
+ break;
+ }
+
+ return sprintf(buf, "%s\n", result);
+}
+static DEVICE_ATTR_RO(connect_type);
+
+static struct attribute *port_dev_attrs[] = {
+ &dev_attr_connect_type.attr,
+ NULL,
+};
+
+static struct attribute_group port_dev_attr_grp = {
+ .attrs = port_dev_attrs,
+};
+
+static const struct attribute_group *port_dev_group[] = {
+ &port_dev_attr_grp,
+ NULL,
+};
+
+static void usb_port_device_release(struct device *dev)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+
+ kfree(port_dev->req);
+ kfree(port_dev);
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int usb_port_runtime_resume(struct device *dev)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+ struct usb_device *hdev = to_usb_device(dev->parent->parent);
+ struct usb_interface *intf = to_usb_interface(dev->parent);
+ struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
+ struct usb_device *udev = port_dev->child;
+ struct usb_port *peer = port_dev->peer;
+ int port1 = port_dev->portnum;
+ int retval;
+
+ if (!hub)
+ return -EINVAL;
+ if (hub->in_reset) {
+ set_bit(port1, hub->power_bits);
+ return 0;
+ }
+
+ /*
+ * Power on our usb3 peer before this usb2 port to prevent a usb3
+ * device from degrading to its usb2 connection
+ */
+ if (!port_dev->is_superspeed && peer)
+ pm_runtime_get_sync(&peer->dev);
+
+ usb_autopm_get_interface(intf);
+ retval = usb_hub_set_port_power(hdev, hub, port1, true);
+ msleep(hub_power_on_good_delay(hub));
+ if (udev && !retval) {
+ /*
+ * Attempt to wait for usb hub port to be reconnected in order
+ * to make the resume procedure successful. The device may have
+ * disconnected while the port was powered off, so ignore the
+ * return status.
+ */
+ retval = hub_port_debounce_be_connected(hub, port1);
+ if (retval < 0)
+ dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n",
+ retval);
+ retval = 0;
+
+ /* Force the child awake to revalidate after the power loss. */
+ if (!test_and_set_bit(port1, hub->child_usage_bits)) {
+ pm_runtime_get_noresume(&port_dev->dev);
+ pm_request_resume(&udev->dev);
+ }
+ }
+
+ usb_autopm_put_interface(intf);
+
+ return retval;
+}
+
+static int usb_port_runtime_suspend(struct device *dev)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+ struct usb_device *hdev = to_usb_device(dev->parent->parent);
+ struct usb_interface *intf = to_usb_interface(dev->parent);
+ struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
+ struct usb_port *peer = port_dev->peer;
+ int port1 = port_dev->portnum;
+ int retval;
+
+ if (!hub)
+ return -EINVAL;
+ if (hub->in_reset)
+ return -EBUSY;
+
+ if (dev_pm_qos_flags(&port_dev->dev, PM_QOS_FLAG_NO_POWER_OFF)
+ == PM_QOS_FLAGS_ALL)
+ return -EAGAIN;
+
+ if (usb_port_block_power_off)
+ return -EBUSY;
+
+ usb_autopm_get_interface(intf);
+ retval = usb_hub_set_port_power(hdev, hub, port1, false);
+ usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
+ if (!port_dev->is_superspeed)
+ usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
+ usb_autopm_put_interface(intf);
+
+ /*
+ * Our peer usb3 port may now be able to suspend, so
+ * asynchronously queue a suspend request to observe that this
+ * usb2 port is now off.
+ */
+ if (!port_dev->is_superspeed && peer)
+ pm_runtime_put(&peer->dev);
+
+ return retval;
+}
+#endif
+
+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,
+#endif
+};
+
+struct device_type usb_port_device_type = {
+ .name = "usb_port",
+ .release = usb_port_device_release,
+ .pm = &usb_port_pm_ops,
+};
+
+static struct device_driver usb_port_driver = {
+ .name = "usb",
+ .owner = THIS_MODULE,
+};
+
+static int link_peers(struct usb_port *left, struct usb_port *right)
+{
+ struct usb_port *ss_port, *hs_port;
+ int rc;
+
+ if (left->peer == right && right->peer == left)
+ return 0;
+
+ if (left->peer || right->peer) {
+ struct usb_port *lpeer = left->peer;
+ struct usb_port *rpeer = right->peer;
+ char *method;
+
+ if (left->location && left->location == right->location)
+ method = "location";
+ else
+ method = "default";
+
+ pr_warn("usb: failed to peer %s and %s by %s (%s:%s) (%s:%s)\n",
+ dev_name(&left->dev), dev_name(&right->dev), method,
+ dev_name(&left->dev),
+ lpeer ? dev_name(&lpeer->dev) : "none",
+ dev_name(&right->dev),
+ rpeer ? dev_name(&rpeer->dev) : "none");
+ return -EBUSY;
+ }
+
+ rc = sysfs_create_link(&left->dev.kobj, &right->dev.kobj, "peer");
+ if (rc)
+ return rc;
+ rc = sysfs_create_link(&right->dev.kobj, &left->dev.kobj, "peer");
+ if (rc) {
+ sysfs_remove_link(&left->dev.kobj, "peer");
+ return rc;
+ }
+
+ /*
+ * We need to wake the HiSpeed port to make sure we don't race
+ * setting ->peer with usb_port_runtime_suspend(). Otherwise we
+ * may miss a suspend event for the SuperSpeed port.
+ */
+ if (left->is_superspeed) {
+ ss_port = left;
+ WARN_ON(right->is_superspeed);
+ hs_port = right;
+ } else {
+ ss_port = right;
+ WARN_ON(!right->is_superspeed);
+ hs_port = left;
+ }
+ pm_runtime_get_sync(&hs_port->dev);
+
+ left->peer = right;
+ right->peer = left;
+
+ /*
+ * The SuperSpeed reference is dropped when the HiSpeed port in
+ * this relationship suspends, i.e. when it is safe to allow a
+ * SuperSpeed connection to drop since there is no risk of a
+ * device degrading to its powered-off HiSpeed connection.
+ *
+ * Also, drop the HiSpeed ref taken above.
+ */
+ pm_runtime_get_sync(&ss_port->dev);
+ pm_runtime_put(&hs_port->dev);
+
+ return 0;
+}
+
+static void link_peers_report(struct usb_port *left, struct usb_port *right)
+{
+ int rc;
+
+ rc = link_peers(left, right);
+ if (rc == 0) {
+ dev_dbg(&left->dev, "peered to %s\n", dev_name(&right->dev));
+ } else {
+ dev_warn(&left->dev, "failed to peer to %s (%d)\n",
+ dev_name(&right->dev), rc);
+ pr_warn_once("usb: port power management may be unreliable\n");
+ usb_port_block_power_off = 1;
+ }
+}
+
+static void unlink_peers(struct usb_port *left, struct usb_port *right)
+{
+ struct usb_port *ss_port, *hs_port;
+
+ WARN(right->peer != left || left->peer != right,
+ "%s and %s are not peers?\n",
+ dev_name(&left->dev), dev_name(&right->dev));
+
+ /*
+ * We wake the HiSpeed port to make sure we don't race its
+ * usb_port_runtime_resume() event which takes a SuperSpeed ref
+ * when ->peer is !NULL.
+ */
+ if (left->is_superspeed) {
+ ss_port = left;
+ hs_port = right;
+ } else {
+ ss_port = right;
+ hs_port = left;
+ }
+
+ pm_runtime_get_sync(&hs_port->dev);
+
+ sysfs_remove_link(&left->dev.kobj, "peer");
+ right->peer = NULL;
+ sysfs_remove_link(&right->dev.kobj, "peer");
+ left->peer = NULL;
+
+ /* Drop the SuperSpeed ref held on behalf of the active HiSpeed port */
+ pm_runtime_put(&ss_port->dev);
+
+ /* Drop the ref taken above */
+ pm_runtime_put(&hs_port->dev);
+}
+
+/*
+ * For each usb hub device in the system check to see if it is in the
+ * peer domain of the given port_dev, and if it is check to see if it
+ * has a port that matches the given port by location
+ */
+static int match_location(struct usb_device *peer_hdev, void *p)
+{
+ int port1;
+ struct usb_hcd *hcd, *peer_hcd;
+ struct usb_port *port_dev = p, *peer;
+ struct usb_hub *peer_hub = usb_hub_to_struct_hub(peer_hdev);
+ struct usb_device *hdev = to_usb_device(port_dev->dev.parent->parent);
+
+ if (!peer_hub)
+ return 0;
+
+ hcd = bus_to_hcd(hdev->bus);
+ peer_hcd = bus_to_hcd(peer_hdev->bus);
+ /* peer_hcd is provisional until we verify it against the known peer */
+ if (peer_hcd != hcd->shared_hcd)
+ return 0;
+
+ for (port1 = 1; port1 <= peer_hdev->maxchild; port1++) {
+ peer = peer_hub->ports[port1 - 1];
+ if (peer && peer->location == port_dev->location) {
+ link_peers_report(port_dev, peer);
+ return 1; /* done */
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Find the peer port either via explicit platform firmware "location"
+ * data, the peer hcd for root hubs, or the upstream peer relationship
+ * for all other hubs.
+ */
+static void find_and_link_peer(struct usb_hub *hub, int port1)
+{
+ struct usb_port *port_dev = hub->ports[port1 - 1], *peer;
+ struct usb_device *hdev = hub->hdev;
+ struct usb_device *peer_hdev;
+ struct usb_hub *peer_hub;
+
+ /*
+ * If location data is available then we can only peer this port
+ * by a location match, not the default peer (lest we create a
+ * situation where we need to go back and undo a default peering
+ * when the port is later peered by location data)
+ */
+ if (port_dev->location) {
+ /* we link the peer in match_location() if found */
+ usb_for_each_dev(port_dev, match_location);
+ return;
+ } else if (!hdev->parent) {
+ struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
+ struct usb_hcd *peer_hcd = hcd->shared_hcd;
+
+ if (!peer_hcd)
+ return;
+
+ peer_hdev = peer_hcd->self.root_hub;
+ } else {
+ struct usb_port *upstream;
+ struct usb_device *parent = hdev->parent;
+ struct usb_hub *parent_hub = usb_hub_to_struct_hub(parent);
+
+ if (!parent_hub)
+ return;
+
+ upstream = parent_hub->ports[hdev->portnum - 1];
+ if (!upstream || !upstream->peer)
+ return;
+
+ peer_hdev = upstream->peer->child;
+ }
+
+ peer_hub = usb_hub_to_struct_hub(peer_hdev);
+ if (!peer_hub || port1 > peer_hdev->maxchild)
+ return;
+
+ /*
+ * we found a valid default peer, last check is to make sure it
+ * does not have location data
+ */
+ peer = peer_hub->ports[port1 - 1];
+ if (peer && peer->location == 0)
+ link_peers_report(port_dev, peer);
+}
+
+int usb_hub_create_port_device(struct usb_hub *hub, int port1)
+{
+ struct usb_port *port_dev;
+ int retval;
+
+ port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
+ if (!port_dev)
+ return -ENOMEM;
+
+ port_dev->req = kzalloc(sizeof(*(port_dev->req)), GFP_KERNEL);
+ if (!port_dev->req) {
+ kfree(port_dev);
+ return -ENOMEM;
+ }
+
+ hub->ports[port1 - 1] = port_dev;
+ port_dev->portnum = port1;
+ set_bit(port1, hub->power_bits);
+ port_dev->dev.parent = hub->intfdev;
+ port_dev->dev.groups = port_dev_group;
+ port_dev->dev.type = &usb_port_device_type;
+ port_dev->dev.driver = &usb_port_driver;
+ if (hub_is_superspeed(hub->hdev))
+ port_dev->is_superspeed = 1;
+ dev_set_name(&port_dev->dev, "%s-port%d", dev_name(&hub->hdev->dev),
+ port1);
+ mutex_init(&port_dev->status_lock);
+ retval = device_register(&port_dev->dev);
+ if (retval) {
+ put_device(&port_dev->dev);
+ return retval;
+ }
+
+ /* Set default policy of port-poweroff disabled. */
+ retval = dev_pm_qos_add_request(&port_dev->dev, port_dev->req,
+ DEV_PM_QOS_FLAGS, PM_QOS_FLAG_NO_POWER_OFF);
+ if (retval < 0) {
+ device_unregister(&port_dev->dev);
+ return retval;
+ }
+
+ find_and_link_peer(hub, port1);
+
+ /*
+ * Enable runtime pm and hold a refernce that hub_configure()
+ * will drop once the PM_QOS_NO_POWER_OFF flag state has been set
+ * and the hub has been fully registered (hdev->maxchild set).
+ */
+ pm_runtime_set_active(&port_dev->dev);
+ pm_runtime_get_noresume(&port_dev->dev);
+ pm_runtime_enable(&port_dev->dev);
+ device_enable_async_suspend(&port_dev->dev);
+
+ /*
+ * Keep hidden the ability to enable port-poweroff if the hub
+ * does not support power switching.
+ */
+ if (!hub_is_port_power_switchable(hub))
+ return 0;
+
+ /* Attempt to let userspace take over the policy. */
+ retval = dev_pm_qos_expose_flags(&port_dev->dev,
+ PM_QOS_FLAG_NO_POWER_OFF);
+ if (retval < 0) {
+ dev_warn(&port_dev->dev, "failed to expose pm_qos_no_poweroff\n");
+ return 0;
+ }
+
+ /* Userspace owns the policy, drop the kernel 'no_poweroff' request. */
+ retval = dev_pm_qos_remove_request(port_dev->req);
+ if (retval >= 0) {
+ kfree(port_dev->req);
+ port_dev->req = NULL;
+ }
+ return 0;
+}
+
+void usb_hub_remove_port_device(struct usb_hub *hub, int port1)
+{
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+ struct usb_port *peer;
+
+ peer = port_dev->peer;
+ if (peer)
+ unlink_peers(port_dev, peer);
+ device_unregister(&port_dev->dev);
+}
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 4c65eb6a867..739ee8e8bdf 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -13,19 +13,25 @@
#include <linux/usb.h>
#include <linux/usb/quirks.h>
+#include <linux/usb/hcd.h>
#include "usb.h"
-/* List of quirky USB devices. Please keep this list ordered by:
+/* Lists of quirky USB devices, split in device quirks and interface quirks.
+ * Device quirks are applied at the very beginning of the enumeration process,
+ * right after reading the device descriptor. They can thus only match on device
+ * information.
+ *
+ * Interface quirks are applied after reading all the configuration descriptors.
+ * They can match on both device and interface information.
+ *
+ * Note that the DELAY_INIT and HONOR_BNUMINTERFACES quirks do not make sense as
+ * interface quirks, as they only influence the enumeration process which is run
+ * before processing the interface quirks.
+ *
+ * Please keep the lists ordered by:
* 1) Vendor ID
* 2) Product ID
* 3) Class ID
- *
- * as we want specific devices to be overridden first, and only after that, any
- * class specific quirks.
- *
- * Right now the logic aborts if it finds a valid device in the table, we might
- * want to change that in the future if it turns out that a whole class of
- * devices is broken...
*/
static const struct usb_device_id usb_quirk_list[] = {
/* CBM - Flash disk */
@@ -38,53 +44,30 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Creative SB Audigy 2 NX */
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
- /* Logitech Webcam C200 */
- { USB_DEVICE(0x046d, 0x0802), .driver_info = USB_QUIRK_RESET_RESUME },
-
- /* Logitech Webcam C250 */
- { USB_DEVICE(0x046d, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME },
-
- /* Logitech Webcam C300 */
- { USB_DEVICE(0x046d, 0x0805), .driver_info = USB_QUIRK_RESET_RESUME },
-
- /* Logitech Webcam B/C500 */
- { USB_DEVICE(0x046d, 0x0807), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Microsoft LifeCam-VX700 v2.0 */
+ { USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME },
- /* Logitech Webcam C600 */
- { USB_DEVICE(0x046d, 0x0808), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Logitech HD Pro Webcams C920 and C930e */
+ { USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT },
+ { USB_DEVICE(0x046d, 0x0843), .driver_info = USB_QUIRK_DELAY_INIT },
- /* Logitech Webcam Pro 9000 */
- { USB_DEVICE(0x046d, 0x0809), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Logitech Quickcam Fusion */
+ { USB_DEVICE(0x046d, 0x08c1), .driver_info = USB_QUIRK_RESET_RESUME },
- /* Logitech Webcam C905 */
- { USB_DEVICE(0x046d, 0x080a), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Logitech Quickcam Orbit MP */
+ { USB_DEVICE(0x046d, 0x08c2), .driver_info = USB_QUIRK_RESET_RESUME },
- /* Logitech Webcam C210 */
- { USB_DEVICE(0x046d, 0x0819), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Logitech Quickcam Pro for Notebook */
+ { USB_DEVICE(0x046d, 0x08c3), .driver_info = USB_QUIRK_RESET_RESUME },
- /* Logitech Webcam C260 */
- { USB_DEVICE(0x046d, 0x081a), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Logitech Quickcam Pro 5000 */
+ { USB_DEVICE(0x046d, 0x08c5), .driver_info = USB_QUIRK_RESET_RESUME },
- /* Logitech Webcam C310 */
- { USB_DEVICE(0x046d, 0x081b), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Logitech Quickcam OEM Dell Notebook */
+ { USB_DEVICE(0x046d, 0x08c6), .driver_info = USB_QUIRK_RESET_RESUME },
- /* Logitech Webcam C910 */
- { USB_DEVICE(0x046d, 0x0821), .driver_info = USB_QUIRK_RESET_RESUME },
-
- /* Logitech Webcam C160 */
- { USB_DEVICE(0x046d, 0x0824), .driver_info = USB_QUIRK_RESET_RESUME },
-
- /* Logitech Webcam C270 */
- { USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME },
-
- /* Logitech Quickcam Pro 9000 */
- { USB_DEVICE(0x046d, 0x0990), .driver_info = USB_QUIRK_RESET_RESUME },
-
- /* Logitech Quickcam E3500 */
- { USB_DEVICE(0x046d, 0x09a4), .driver_info = USB_QUIRK_RESET_RESUME },
-
- /* Logitech Quickcam Vision Pro */
- { USB_DEVICE(0x046d, 0x09a6), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Logitech Quickcam OEM Cisco VT Camera II */
+ { USB_DEVICE(0x046d, 0x08c7), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Harmony 700-series */
{ USB_DEVICE(0x046d, 0xc122), .driver_info = USB_QUIRK_DELAY_INIT },
@@ -96,6 +79,16 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x04b4, 0x0526), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
+ /* Microchip Joss Optical infrared touchboard device */
+ { 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 },
@@ -106,6 +99,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Edirol SD-20 */
{ USB_DEVICE(0x0582, 0x0027), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Alcor Micro Corp. Hub */
+ { USB_DEVICE(0x058f, 0x9254), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* appletouch */
{ USB_DEVICE(0x05ac, 0x021a), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -123,6 +119,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Guillemot Webcam Hercules Dualpix Exchange*/
{ USB_DEVICE(0x06f8, 0x3005), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Midiman M-Audio Keystation 88es */
+ { USB_DEVICE(0x0763, 0x0192), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* M-Systems Flash Disk Pioneers */
{ USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -136,6 +135,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Broadcom BCM92035DGROM BT dongle */
{ USB_DEVICE(0x0a5c, 0x2021), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* MAYA44USB sound device */
+ { USB_DEVICE(0x0a92, 0x0091), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* Action Semiconductor flash disk */
{ USB_DEVICE(0x10d6, 0x2200), .driver_info =
USB_QUIRK_STRING_FETCH_255 },
@@ -153,16 +155,84 @@ static const struct usb_device_id usb_quirk_list[] = {
{ } /* terminating entry must be last */
};
-static const struct usb_device_id *find_id(struct usb_device *udev)
+static const struct usb_device_id usb_interface_quirk_list[] = {
+ /* Logitech UVC Cameras */
+ { USB_VENDOR_AND_INTERFACE_INFO(0x046d, USB_CLASS_VIDEO, 1, 0),
+ .driver_info = USB_QUIRK_RESET_RESUME },
+
+ { } /* terminating entry must be last */
+};
+
+static const struct usb_device_id usb_amd_resume_quirk_list[] = {
+ /* Lenovo Mouse with Pixart controller */
+ { USB_DEVICE(0x17ef, 0x602e), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Pixart Mouse */
+ { USB_DEVICE(0x093a, 0x2500), .driver_info = USB_QUIRK_RESET_RESUME },
+ { USB_DEVICE(0x093a, 0x2510), .driver_info = USB_QUIRK_RESET_RESUME },
+ { USB_DEVICE(0x093a, 0x2521), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Logitech Optical Mouse M90/M100 */
+ { USB_DEVICE(0x046d, 0xc05a), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ { } /* terminating entry must be last */
+};
+
+static bool usb_match_any_interface(struct usb_device *udev,
+ const struct usb_device_id *id)
{
- const struct usb_device_id *id = usb_quirk_list;
+ unsigned int i;
+
+ for (i = 0; i < udev->descriptor.bNumConfigurations; ++i) {
+ struct usb_host_config *cfg = &udev->config[i];
+ unsigned int j;
+
+ for (j = 0; j < cfg->desc.bNumInterfaces; ++j) {
+ struct usb_interface_cache *cache;
+ struct usb_host_interface *intf;
+
+ cache = cfg->intf_cache[j];
+ if (cache->num_altsetting == 0)
+ continue;
- for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass ||
- id->driver_info; id++) {
- if (usb_match_device(udev, id))
- return id;
+ intf = &cache->altsetting[0];
+ if (usb_match_one_id_intf(udev, intf, id))
+ return true;
+ }
}
- return NULL;
+
+ return false;
+}
+
+static int usb_amd_resume_quirk(struct usb_device *udev)
+{
+ struct usb_hcd *hcd;
+
+ hcd = bus_to_hcd(udev->bus);
+ /* The device should be attached directly to root hub */
+ if (udev->level == 1 && hcd->amd_resume_bug == 1)
+ return 1;
+
+ return 0;
+}
+
+static u32 __usb_detect_quirks(struct usb_device *udev,
+ const struct usb_device_id *id)
+{
+ u32 quirks = 0;
+
+ for (; id->match_flags; id++) {
+ if (!usb_match_device(udev, id))
+ continue;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_INFO) &&
+ !usb_match_any_interface(udev, id))
+ continue;
+
+ quirks |= (u32)(id->driver_info);
+ }
+
+ return quirks;
}
/*
@@ -170,27 +240,39 @@ static const struct usb_device_id *find_id(struct usb_device *udev)
*/
void usb_detect_quirks(struct usb_device *udev)
{
- const struct usb_device_id *id = usb_quirk_list;
+ udev->quirks = __usb_detect_quirks(udev, usb_quirk_list);
+
+ /*
+ * Pixart-based mice would trigger remote wakeup issue on AMD
+ * Yangtze chipset, so set them as RESET_RESUME flag.
+ */
+ if (usb_amd_resume_quirk(udev))
+ udev->quirks |= __usb_detect_quirks(udev,
+ usb_amd_resume_quirk_list);
- id = find_id(udev);
- if (id)
- udev->quirks = (u32)(id->driver_info);
if (udev->quirks)
dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
- udev->quirks);
+ udev->quirks);
- /* For the present, all devices default to USB-PERSIST enabled */
-#if 0 /* was: #ifdef CONFIG_PM */
+#ifdef CONFIG_USB_DEFAULT_PERSIST
+ if (!(udev->quirks & USB_QUIRK_RESET))
+ udev->persist_enabled = 1;
+#else
/* Hubs are automatically enabled for USB-PERSIST */
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB)
udev->persist_enabled = 1;
+#endif /* CONFIG_USB_DEFAULT_PERSIST */
+}
-#else
- /* In the absence of PM, we can safely enable USB-PERSIST
- * for all devices. It will affect things like hub resets
- * and EMF-related port disables.
- */
- if (!(udev->quirks & USB_QUIRK_RESET_MORPHS))
- udev->persist_enabled = 1;
-#endif /* CONFIG_PM */
+void usb_detect_interface_quirks(struct usb_device *udev)
+{
+ u32 quirks;
+
+ quirks = __usb_detect_quirks(udev, usb_interface_quirk_list);
+ if (quirks == 0)
+ return;
+
+ dev_dbg(&udev->dev, "USB interface quirks for this device: %x\n",
+ quirks);
+ udev->quirks |= quirks;
}
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 9e491ca2e5c..1236c6011c7 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -17,50 +17,71 @@
#include "usb.h"
/* Active configuration fields */
-#define usb_actconfig_show(field, multiplier, format_string) \
-static ssize_t show_##field(struct device *dev, \
- struct device_attribute *attr, char *buf) \
+#define usb_actconfig_show(field, format_string) \
+static ssize_t field##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
{ \
struct usb_device *udev; \
struct usb_host_config *actconfig; \
+ ssize_t rc = 0; \
\
udev = to_usb_device(dev); \
+ usb_lock_device(udev); \
actconfig = udev->actconfig; \
if (actconfig) \
- return sprintf(buf, format_string, \
- actconfig->desc.field * multiplier); \
- else \
- return 0; \
+ rc = sprintf(buf, format_string, \
+ actconfig->desc.field); \
+ usb_unlock_device(udev); \
+ return rc; \
} \
-#define usb_actconfig_attr(field, multiplier, format_string) \
-usb_actconfig_show(field, multiplier, format_string) \
-static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
+#define usb_actconfig_attr(field, format_string) \
+ usb_actconfig_show(field, format_string) \
+ static DEVICE_ATTR_RO(field)
-usb_actconfig_attr(bNumInterfaces, 1, "%2d\n")
-usb_actconfig_attr(bmAttributes, 1, "%2x\n")
-usb_actconfig_attr(bMaxPower, 2, "%3dmA\n")
+usb_actconfig_attr(bNumInterfaces, "%2d\n");
+usb_actconfig_attr(bmAttributes, "%2x\n");
-static ssize_t show_configuration_string(struct device *dev,
+static ssize_t bMaxPower_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_device *udev;
struct usb_host_config *actconfig;
+ ssize_t rc = 0;
udev = to_usb_device(dev);
+ usb_lock_device(udev);
actconfig = udev->actconfig;
- if ((!actconfig) || (!actconfig->string))
- return 0;
- return sprintf(buf, "%s\n", actconfig->string);
+ if (actconfig)
+ rc = sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
+ usb_unlock_device(udev);
+ return rc;
+}
+static DEVICE_ATTR_RO(bMaxPower);
+
+static ssize_t configuration_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_device *udev;
+ struct usb_host_config *actconfig;
+ ssize_t rc = 0;
+
+ udev = to_usb_device(dev);
+ usb_lock_device(udev);
+ actconfig = udev->actconfig;
+ if (actconfig && actconfig->string)
+ rc = sprintf(buf, "%s\n", actconfig->string);
+ usb_unlock_device(udev);
+ return rc;
}
-static DEVICE_ATTR(configuration, S_IRUGO, show_configuration_string, NULL);
+static DEVICE_ATTR_RO(configuration);
/* configuration value is always present, and r/w */
-usb_actconfig_show(bConfigurationValue, 1, "%u\n");
+usb_actconfig_show(bConfigurationValue, "%u\n");
-static ssize_t
-set_bConfigurationValue(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t bConfigurationValue_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct usb_device *udev = to_usb_device(dev);
int config, value;
@@ -72,13 +93,12 @@ set_bConfigurationValue(struct device *dev, struct device_attribute *attr,
usb_unlock_device(udev);
return (value < 0) ? value : count;
}
-
-static DEVICE_ATTR(bConfigurationValue, S_IRUGO | S_IWUSR,
- show_bConfigurationValue, set_bConfigurationValue);
+static DEVICE_ATTR_IGNORE_LOCKDEP(bConfigurationValue, S_IRUGO | S_IWUSR,
+ bConfigurationValue_show, bConfigurationValue_store);
/* String fields */
#define usb_string_attr(name) \
-static ssize_t show_##name(struct device *dev, \
+static ssize_t name##_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct usb_device *udev; \
@@ -90,14 +110,14 @@ static ssize_t show_##name(struct device *dev, \
usb_unlock_device(udev); \
return retval; \
} \
-static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
+static DEVICE_ATTR_RO(name)
usb_string_attr(product);
usb_string_attr(manufacturer);
usb_string_attr(serial);
-static ssize_t
-show_speed(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t speed_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct usb_device *udev;
char *speed;
@@ -126,40 +146,40 @@ show_speed(struct device *dev, struct device_attribute *attr, char *buf)
}
return sprintf(buf, "%s\n", speed);
}
-static DEVICE_ATTR(speed, S_IRUGO, show_speed, NULL);
+static DEVICE_ATTR_RO(speed);
-static ssize_t
-show_busnum(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t busnum_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct usb_device *udev;
udev = to_usb_device(dev);
return sprintf(buf, "%d\n", udev->bus->busnum);
}
-static DEVICE_ATTR(busnum, S_IRUGO, show_busnum, NULL);
+static DEVICE_ATTR_RO(busnum);
-static ssize_t
-show_devnum(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t devnum_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct usb_device *udev;
udev = to_usb_device(dev);
return sprintf(buf, "%d\n", udev->devnum);
}
-static DEVICE_ATTR(devnum, S_IRUGO, show_devnum, NULL);
+static DEVICE_ATTR_RO(devnum);
-static ssize_t
-show_devpath(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t devpath_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct usb_device *udev;
udev = to_usb_device(dev);
return sprintf(buf, "%s\n", udev->devpath);
}
-static DEVICE_ATTR(devpath, S_IRUGO, show_devpath, NULL);
+static DEVICE_ATTR_RO(devpath);
-static ssize_t
-show_version(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t version_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct usb_device *udev;
u16 bcdUSB;
@@ -168,82 +188,110 @@ show_version(struct device *dev, struct device_attribute *attr, char *buf)
bcdUSB = le16_to_cpu(udev->descriptor.bcdUSB);
return sprintf(buf, "%2x.%02x\n", bcdUSB >> 8, bcdUSB & 0xff);
}
-static DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
+static DEVICE_ATTR_RO(version);
-static ssize_t
-show_maxchild(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t maxchild_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct usb_device *udev;
udev = to_usb_device(dev);
return sprintf(buf, "%d\n", udev->maxchild);
}
-static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL);
+static DEVICE_ATTR_RO(maxchild);
-static ssize_t
-show_quirks(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t quirks_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct usb_device *udev;
udev = to_usb_device(dev);
return sprintf(buf, "0x%x\n", udev->quirks);
}
-static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL);
+static DEVICE_ATTR_RO(quirks);
-static ssize_t
-show_avoid_reset_quirk(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t avoid_reset_quirk_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
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
-set_avoid_reset_quirk(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t avoid_reset_quirk_store(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;
}
+static DEVICE_ATTR_RW(avoid_reset_quirk);
-static DEVICE_ATTR(avoid_reset_quirk, S_IRUGO | S_IWUSR,
- show_avoid_reset_quirk, set_avoid_reset_quirk);
-
-static ssize_t
-show_urbnum(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t urbnum_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct usb_device *udev;
udev = to_usb_device(dev);
return sprintf(buf, "%d\n", atomic_read(&udev->urbnum));
}
-static DEVICE_ATTR(urbnum, S_IRUGO, show_urbnum, NULL);
+static DEVICE_ATTR_RO(urbnum);
+
+static ssize_t removable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_device *udev;
+ char *state;
+
+ udev = to_usb_device(dev);
+
+ switch (udev->removable) {
+ case USB_DEVICE_REMOVABLE:
+ state = "removable";
+ break;
+ case USB_DEVICE_FIXED:
+ state = "fixed";
+ break;
+ default:
+ state = "unknown";
+ }
+
+ return sprintf(buf, "%s\n", state);
+}
+static DEVICE_ATTR_RO(removable);
+static ssize_t ltm_capable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (usb_device_supports_ltm(to_usb_device(dev)))
+ return sprintf(buf, "%s\n", "yes");
+ return sprintf(buf, "%s\n", "no");
+}
+static DEVICE_ATTR_RO(ltm_capable);
#ifdef CONFIG_PM
-static ssize_t
-show_persist(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t persist_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct usb_device *udev = to_usb_device(dev);
return sprintf(buf, "%d\n", udev->persist_enabled);
}
-static ssize_t
-set_persist(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t persist_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct usb_device *udev = to_usb_device(dev);
int value;
@@ -260,8 +308,7 @@ set_persist(struct device *dev, struct device_attribute *attr,
usb_unlock_device(udev);
return count;
}
-
-static DEVICE_ATTR(persist, S_IRUGO | S_IWUSR, show_persist, set_persist);
+static DEVICE_ATTR_RW(persist);
static int add_persist_attributes(struct device *dev)
{
@@ -294,19 +341,17 @@ static void remove_persist_attributes(struct device *dev)
#endif /* CONFIG_PM */
-#ifdef CONFIG_USB_SUSPEND
+#ifdef CONFIG_PM_RUNTIME
-static ssize_t
-show_connected_duration(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t connected_duration_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct usb_device *udev = to_usb_device(dev);
return sprintf(buf, "%u\n",
jiffies_to_msecs(jiffies - udev->connect_time));
}
-
-static DEVICE_ATTR(connected_duration, S_IRUGO, show_connected_duration, NULL);
+static DEVICE_ATTR_RO(connected_duration);
/*
* If the device is resumed, the last time the device was suspended has
@@ -315,9 +360,8 @@ static DEVICE_ATTR(connected_duration, S_IRUGO, show_connected_duration, NULL);
*
* If the device is suspended, the active_duration is up-to-date.
*/
-static ssize_t
-show_active_duration(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t active_duration_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct usb_device *udev = to_usb_device(dev);
int duration;
@@ -328,18 +372,17 @@ show_active_duration(struct device *dev, struct device_attribute *attr,
duration = jiffies_to_msecs(udev->active_duration);
return sprintf(buf, "%u\n", duration);
}
+static DEVICE_ATTR_RO(active_duration);
-static DEVICE_ATTR(active_duration, S_IRUGO, show_active_duration, NULL);
-
-static ssize_t
-show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t autosuspend_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", dev->power.autosuspend_delay / 1000);
}
-static ssize_t
-set_autosuspend(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t autosuspend_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
{
int value;
@@ -350,14 +393,13 @@ set_autosuspend(struct device *dev, struct device_attribute *attr,
pm_runtime_set_autosuspend_delay(dev, value * 1000);
return count;
}
-
-static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR,
- show_autosuspend, set_autosuspend);
+static DEVICE_ATTR_RW(autosuspend);
static const char on_string[] = "on";
static const char auto_string[] = "auto";
-static void warn_level(void) {
+static void warn_level(void)
+{
static int level_warned;
if (!level_warned) {
@@ -367,8 +409,8 @@ static void warn_level(void) {
}
}
-static ssize_t
-show_level(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t level_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct usb_device *udev = to_usb_device(dev);
const char *p = auto_string;
@@ -379,9 +421,8 @@ show_level(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%s\n", p);
}
-static ssize_t
-set_level(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t level_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct usb_device *udev = to_usb_device(dev);
int len = count;
@@ -409,17 +450,15 @@ set_level(struct device *dev, struct device_attribute *attr,
usb_unlock_device(udev);
return rc;
}
+static DEVICE_ATTR_RW(level);
-static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level);
-
-static ssize_t
-show_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t usb2_hardware_lpm_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct usb_device *udev = to_usb_device(dev);
const char *p;
- if (udev->usb2_hw_lpm_enabled == 1)
+ if (udev->usb2_hw_lpm_allowed == 1)
p = "enabled";
else
p = "disabled";
@@ -427,9 +466,9 @@ show_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%s\n", p);
}
-static ssize_t
-set_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t usb2_hardware_lpm_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct usb_device *udev = to_usb_device(dev);
bool value;
@@ -439,8 +478,10 @@ set_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr,
ret = strtobool(buf, &value);
- if (!ret)
+ if (!ret) {
+ udev->usb2_hw_lpm_allowed = value;
ret = usb_set_usb2_hardware_lpm(udev, value);
+ }
usb_unlock_device(udev);
@@ -449,12 +490,59 @@ set_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr,
return ret;
}
+static DEVICE_ATTR_RW(usb2_hardware_lpm);
+
+static ssize_t usb2_lpm_l1_timeout_show(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 usb2_lpm_l1_timeout_store(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_RW(usb2_lpm_l1_timeout);
+
+static ssize_t usb2_lpm_besl_show(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 usb2_lpm_besl_store(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;
-static DEVICE_ATTR(usb2_hardware_lpm, S_IRUGO | S_IWUSR, show_usb2_hardware_lpm,
- set_usb2_hardware_lpm);
+ udev->l1_params.besl = besl;
+
+ return count;
+}
+static DEVICE_ATTR_RW(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 = {
@@ -500,13 +588,13 @@ static void remove_power_attributes(struct device *dev)
#define add_power_attributes(dev) 0
#define remove_power_attributes(dev) do {} while (0)
-#endif /* CONFIG_USB_SUSPEND */
+#endif /* CONFIG_PM_RUNTIME */
/* Descriptor fields */
#define usb_descriptor_attr_le16(field, format_string) \
static ssize_t \
-show_##field(struct device *dev, struct device_attribute *attr, \
+field##_show(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
struct usb_device *udev; \
@@ -515,15 +603,15 @@ show_##field(struct device *dev, struct device_attribute *attr, \
return sprintf(buf, format_string, \
le16_to_cpu(udev->descriptor.field)); \
} \
-static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
+static DEVICE_ATTR_RO(field)
-usb_descriptor_attr_le16(idVendor, "%04x\n")
-usb_descriptor_attr_le16(idProduct, "%04x\n")
-usb_descriptor_attr_le16(bcdDevice, "%04x\n")
+usb_descriptor_attr_le16(idVendor, "%04x\n");
+usb_descriptor_attr_le16(idProduct, "%04x\n");
+usb_descriptor_attr_le16(bcdDevice, "%04x\n");
#define usb_descriptor_attr(field, format_string) \
static ssize_t \
-show_##field(struct device *dev, struct device_attribute *attr, \
+field##_show(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
struct usb_device *udev; \
@@ -531,34 +619,31 @@ show_##field(struct device *dev, struct device_attribute *attr, \
udev = to_usb_device(dev); \
return sprintf(buf, format_string, udev->descriptor.field); \
} \
-static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
-
-usb_descriptor_attr(bDeviceClass, "%02x\n")
-usb_descriptor_attr(bDeviceSubClass, "%02x\n")
-usb_descriptor_attr(bDeviceProtocol, "%02x\n")
-usb_descriptor_attr(bNumConfigurations, "%d\n")
-usb_descriptor_attr(bMaxPacketSize0, "%d\n")
+static DEVICE_ATTR_RO(field)
+usb_descriptor_attr(bDeviceClass, "%02x\n");
+usb_descriptor_attr(bDeviceSubClass, "%02x\n");
+usb_descriptor_attr(bDeviceProtocol, "%02x\n");
+usb_descriptor_attr(bNumConfigurations, "%d\n");
+usb_descriptor_attr(bMaxPacketSize0, "%d\n");
/* show if the device is authorized (1) or not (0) */
-static ssize_t usb_dev_authorized_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static ssize_t authorized_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct usb_device *usb_dev = to_usb_device(dev);
return snprintf(buf, PAGE_SIZE, "%u\n", usb_dev->authorized);
}
-
/*
* Authorize a device to be used in the system
*
* Writing a 0 deauthorizes the device, writing a 1 authorizes it.
*/
-static ssize_t usb_dev_authorized_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
+static ssize_t authorized_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t size)
{
ssize_t result;
struct usb_device *usb_dev = to_usb_device(dev);
@@ -570,16 +655,14 @@ static ssize_t usb_dev_authorized_store(struct device *dev,
result = usb_deauthorize_device(usb_dev);
else
result = usb_authorize_device(usb_dev);
- return result < 0? result : size;
+ return result < 0 ? result : size;
}
-
-static DEVICE_ATTR(authorized, 0644,
- usb_dev_authorized_show, usb_dev_authorized_store);
+static DEVICE_ATTR_IGNORE_LOCKDEP(authorized, S_IRUGO | S_IWUSR,
+ authorized_show, authorized_store);
/* "Safely remove a device" */
-static ssize_t usb_remove_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t remove_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct usb_device *udev = to_usb_device(dev);
int rc = 0;
@@ -596,7 +679,7 @@ static ssize_t usb_remove_store(struct device *dev,
usb_unlock_device(udev);
return rc;
}
-static DEVICE_ATTR(remove, 0200, NULL, usb_remove_store);
+static DEVICE_ATTR_IGNORE_LOCKDEP(remove, S_IWUSR, NULL, remove_store);
static struct attribute *dev_attrs[] = {
@@ -626,6 +709,8 @@ static struct attribute *dev_attrs[] = {
&dev_attr_avoid_reset_quirk.attr,
&dev_attr_authorized.attr,
&dev_attr_remove.attr,
+ &dev_attr_removable.attr,
+ &dev_attr_ltm_capable.attr,
NULL,
};
static struct attribute_group dev_attr_grp = {
@@ -690,6 +775,7 @@ read_descriptors(struct file *filp, struct kobject *kobj,
* Following that are the raw descriptor entries for all the
* configurations (config plus subsidiary descriptors).
*/
+ usb_lock_device(udev);
for (cfgno = -1; cfgno < udev->descriptor.bNumConfigurations &&
nleft > 0; ++cfgno) {
if (cfgno < 0) {
@@ -710,6 +796,7 @@ read_descriptors(struct file *filp, struct kobject *kobj,
off -= srclen;
}
}
+ usb_unlock_device(udev);
return count - nleft;
}
@@ -750,10 +837,10 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev)
device_remove_bin_file(dev, &dev_bin_attr_descriptors);
}
-/* Interface Accociation Descriptor fields */
+/* Interface Association Descriptor fields */
#define usb_intf_assoc_attr(field, format_string) \
static ssize_t \
-show_iad_##field(struct device *dev, struct device_attribute *attr, \
+iad_##field##_show(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
@@ -761,18 +848,18 @@ show_iad_##field(struct device *dev, struct device_attribute *attr, \
return sprintf(buf, format_string, \
intf->intf_assoc->field); \
} \
-static DEVICE_ATTR(iad_##field, S_IRUGO, show_iad_##field, NULL);
+static DEVICE_ATTR_RO(iad_##field)
-usb_intf_assoc_attr(bFirstInterface, "%02x\n")
-usb_intf_assoc_attr(bInterfaceCount, "%02d\n")
-usb_intf_assoc_attr(bFunctionClass, "%02x\n")
-usb_intf_assoc_attr(bFunctionSubClass, "%02x\n")
-usb_intf_assoc_attr(bFunctionProtocol, "%02x\n")
+usb_intf_assoc_attr(bFirstInterface, "%02x\n");
+usb_intf_assoc_attr(bInterfaceCount, "%02d\n");
+usb_intf_assoc_attr(bFunctionClass, "%02x\n");
+usb_intf_assoc_attr(bFunctionSubClass, "%02x\n");
+usb_intf_assoc_attr(bFunctionProtocol, "%02x\n");
/* Interface fields */
#define usb_intf_attr(field, format_string) \
static ssize_t \
-show_##field(struct device *dev, struct device_attribute *attr, \
+field##_show(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
@@ -780,33 +867,31 @@ show_##field(struct device *dev, struct device_attribute *attr, \
return sprintf(buf, format_string, \
intf->cur_altsetting->desc.field); \
} \
-static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
+static DEVICE_ATTR_RO(field)
-usb_intf_attr(bInterfaceNumber, "%02x\n")
-usb_intf_attr(bAlternateSetting, "%2d\n")
-usb_intf_attr(bNumEndpoints, "%02x\n")
-usb_intf_attr(bInterfaceClass, "%02x\n")
-usb_intf_attr(bInterfaceSubClass, "%02x\n")
-usb_intf_attr(bInterfaceProtocol, "%02x\n")
+usb_intf_attr(bInterfaceNumber, "%02x\n");
+usb_intf_attr(bAlternateSetting, "%2d\n");
+usb_intf_attr(bNumEndpoints, "%02x\n");
+usb_intf_attr(bInterfaceClass, "%02x\n");
+usb_intf_attr(bInterfaceSubClass, "%02x\n");
+usb_intf_attr(bInterfaceProtocol, "%02x\n");
-static ssize_t show_interface_string(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t interface_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct usb_interface *intf;
char *string;
intf = to_usb_interface(dev);
- string = intf->cur_altsetting->string;
- barrier(); /* The altsetting might change! */
-
+ string = ACCESS_ONCE(intf->cur_altsetting->string);
if (!string)
return 0;
return sprintf(buf, "%s\n", string);
}
-static DEVICE_ATTR(interface, S_IRUGO, show_interface_string, NULL);
+static DEVICE_ATTR_RO(interface);
-static ssize_t show_modalias(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct usb_interface *intf;
struct usb_device *udev;
@@ -814,10 +899,10 @@ static ssize_t show_modalias(struct device *dev,
intf = to_usb_interface(dev);
udev = interface_to_usbdev(intf);
- alt = intf->cur_altsetting;
+ alt = ACCESS_ONCE(intf->cur_altsetting);
return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X"
- "ic%02Xisc%02Xip%02X\n",
+ "ic%02Xisc%02Xip%02Xin%02X\n",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct),
le16_to_cpu(udev->descriptor.bcdDevice),
@@ -826,32 +911,25 @@ static ssize_t show_modalias(struct device *dev,
udev->descriptor.bDeviceProtocol,
alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass,
- alt->desc.bInterfaceProtocol);
+ alt->desc.bInterfaceProtocol,
+ alt->desc.bInterfaceNumber);
}
-static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
+static DEVICE_ATTR_RO(modalias);
-static ssize_t show_supports_autosuspend(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t supports_autosuspend_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct usb_interface *intf;
- struct usb_device *udev;
- int ret;
+ int s;
- intf = to_usb_interface(dev);
- udev = interface_to_usbdev(intf);
-
- usb_lock_device(udev);
+ device_lock(dev);
/* Devices will be autosuspended even when an interface isn't claimed */
- if (!intf->dev.driver ||
- to_usb_driver(intf->dev.driver)->supports_autosuspend)
- ret = sprintf(buf, "%u\n", 1);
- else
- ret = sprintf(buf, "%u\n", 0);
- usb_unlock_device(udev);
+ s = (!dev->driver || to_usb_driver(dev->driver)->supports_autosuspend);
+ device_unlock(dev);
- return ret;
+ return sprintf(buf, "%u\n", s);
}
-static DEVICE_ATTR(supports_autosuspend, S_IRUGO, show_supports_autosuspend, NULL);
+static DEVICE_ATTR_RO(supports_autosuspend);
static struct attribute *intf_attrs[] = {
&dev_attr_bInterfaceNumber.attr,
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 909625b91eb..991386ceb4e 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -2,11 +2,11 @@
#include <linux/string.h>
#include <linux/bitops.h>
#include <linux/slab.h>
-#include <linux/init.h>
#include <linux/log2.h>
#include <linux/usb.h>
#include <linux/wait.h>
#include <linux/usb/hcd.h>
+#include <linux/scatterlist.h>
#define to_urb(d) container_of(d, struct urb, kref)
@@ -52,14 +52,14 @@ EXPORT_SYMBOL_GPL(usb_init_urb);
* valid options for this.
*
* Creates an urb for the USB driver to use, initializes a few internal
- * structures, incrementes the usage counter, and returns a pointer to it.
- *
- * If no memory is available, NULL is returned.
+ * structures, increments the usage counter, and returns a pointer to it.
*
* If the driver want to use this urb for interrupt, control, or bulk
* endpoints, pass '0' as the number of iso packets.
*
* The driver must call usb_free_urb() when it is finished with the urb.
+ *
+ * Return: A pointer to the new urb, or %NULL if no memory is available.
*/
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
{
@@ -102,7 +102,7 @@ EXPORT_SYMBOL_GPL(usb_free_urb);
* host controller driver. This allows proper reference counting to happen
* for urbs.
*
- * A pointer to the urb with the incremented reference counter is returned.
+ * Return: A pointer to the urb with the incremented reference counter.
*/
struct urb *usb_get_urb(struct urb *urb)
{
@@ -137,13 +137,19 @@ void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
}
EXPORT_SYMBOL_GPL(usb_anchor_urb);
+static int usb_anchor_check_wakeup(struct usb_anchor *anchor)
+{
+ return atomic_read(&anchor->suspend_wakeups) == 0 &&
+ list_empty(&anchor->urb_list);
+}
+
/* Callers must hold anchor->lock */
static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor)
{
urb->anchor = NULL;
list_del(&urb->anchor_list);
usb_put_urb(urb);
- if (list_empty(&anchor->urb_list))
+ if (usb_anchor_check_wakeup(anchor))
wake_up(&anchor->wait);
}
@@ -199,13 +205,12 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb);
* the particular kind of transfer, although they will not initialize
* any transfer flags.
*
- * Successful submissions return 0; otherwise this routine returns a
- * negative error number. If the submission is successful, the complete()
- * callback from the URB will be called exactly once, when the USB core and
- * Host Controller Driver (HCD) are finished with the URB. When the completion
- * function is called, control of the URB is returned to the device
- * driver which issued the request. The completion handler may then
- * immediately free or reuse that URB.
+ * If the submission is successful, the complete() callback from the URB
+ * will be called exactly once, when the USB core and Host Controller Driver
+ * (HCD) are finished with the URB. When the completion function is called,
+ * control of the URB is returned to the device driver which issued the
+ * request. The completion handler may then immediately free or reuse that
+ * URB.
*
* With few exceptions, USB device drivers should never access URB fields
* provided by usbcore or the HCD until its complete() is called.
@@ -214,9 +219,25 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb);
* urb->interval is modified to reflect the actual transfer period used
* (normally some power of two units). And for isochronous urbs,
* urb->start_frame is modified to reflect when the URB's transfers were
- * scheduled to start. Not all isochronous transfer scheduling policies
- * will work, but most host controller drivers should easily handle ISO
- * queues going from now until 10-200 msec into the future.
+ * scheduled to start.
+ *
+ * Not all isochronous transfer scheduling policies will work, but most
+ * host controller drivers should easily handle ISO queues going from now
+ * until 10-200 msec into the future. Drivers should try to keep at
+ * least one or two msec of data in the queue; many controllers require
+ * that new transfers start at least 1 msec in the future when they are
+ * added. If the driver is unable to keep up and the queue empties out,
+ * the behavior for new submissions is governed by the URB_ISO_ASAP flag.
+ * If the flag is set, or if the queue is idle, then the URB is always
+ * assigned to the first available (and not yet expired) slot in the
+ * endpoint's schedule. If the flag is not set and the queue is active
+ * then the URB is always assigned to the next slot in the schedule
+ * following the end of the endpoint's previous URB, even if that slot is
+ * in the past. When a packet is assigned in this way to a slot that has
+ * already expired, the packet is not transmitted and the corresponding
+ * usb_iso_packet_descriptor's status field will return -EXDEV. If this
+ * would happen to all the packets in the URB, submission fails with a
+ * -EXDEV error code.
*
* For control endpoints, the synchronous usb_control_msg() call is
* often used (in non-interrupt context) instead of this call.
@@ -224,6 +245,9 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb);
* that are standardized in the USB 2.0 specification. For bulk
* endpoints, a synchronous usb_bulk_msg() call is available.
*
+ * Return:
+ * 0 on successful submissions. A negative error number otherwise.
+ *
* Request Queuing:
*
* URBs may be submitted to endpoints before previous ones complete, to
@@ -256,7 +280,7 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb);
*
* Device drivers must explicitly request that repetition, by ensuring that
* some URB is always on the endpoint's queue (except possibly for short
- * periods during completion callacks). When there is no longer an urb
+ * periods during completion callbacks). When there is no longer an urb
* queued, the endpoint's bandwidth reservation is canceled. This means
* drivers can use their completion handlers to ensure they keep bandwidth
* they need, by reinitializing and resubmitting the just-completed urb
@@ -300,13 +324,22 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb);
*/
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
{
+ static int pipetypes[4] = {
+ PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT
+ };
int xfertype, max;
struct usb_device *dev;
struct usb_host_endpoint *ep;
int is_out;
+ unsigned int allowed;
- if (!urb || urb->hcpriv || !urb->complete)
+ if (!urb || !urb->complete)
return -EINVAL;
+ if (urb->hcpriv) {
+ WARN_ONCE(1, "URB %p submitted while active\n", urb);
+ return -EBUSY;
+ }
+
dev = urb->dev;
if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED))
return -ENODEV;
@@ -392,31 +425,31 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
urb->iso_frame_desc[n].status = -EXDEV;
urb->iso_frame_desc[n].actual_length = 0;
}
+ } else if (urb->num_sgs && !urb->dev->bus->no_sg_constraint &&
+ dev->speed != USB_SPEED_WIRELESS) {
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(urb->sg, sg, urb->num_sgs - 1, i)
+ if (sg->length % max)
+ return -EINVAL;
}
/* the I/O buffer must be mapped/unmapped, except when length=0 */
if (urb->transfer_buffer_length > INT_MAX)
return -EMSGSIZE;
-#ifdef DEBUG
- /* stuff that drivers shouldn't do, but which shouldn't
+ /*
+ * stuff that drivers shouldn't do, but which shouldn't
* cause problems in HCDs if they get it wrong.
*/
- {
- unsigned int orig_flags = urb->transfer_flags;
- unsigned int allowed;
- static int pipetypes[4] = {
- PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT
- };
/* Check that the pipe's type matches the endpoint's type */
- if (usb_pipetype(urb->pipe) != pipetypes[xfertype]) {
- dev_err(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
+ if (usb_pipetype(urb->pipe) != pipetypes[xfertype])
+ dev_WARN(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n",
usb_pipetype(urb->pipe), pipetypes[xfertype]);
- return -EPIPE; /* The most suitable error code :-) */
- }
- /* enforce simple/standard policy */
+ /* Check against a simple/standard policy */
allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | URB_DIR_MASK |
URB_FREE_BUFFER);
switch (xfertype) {
@@ -435,16 +468,13 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
allowed |= URB_ISO_ASAP;
break;
}
- urb->transfer_flags &= allowed;
+ allowed &= urb->transfer_flags;
+
+ /* warn if submitter gave bogus flags */
+ if (allowed != urb->transfer_flags)
+ dev_WARN(&dev->dev, "BOGUS urb flags, %x --> %x\n",
+ urb->transfer_flags, allowed);
- /* fail if submitter gave bogus flags */
- if (urb->transfer_flags != orig_flags) {
- dev_err(&dev->dev, "BOGUS urb flags, %x --> %x\n",
- orig_flags, urb->transfer_flags);
- return -EINVAL;
- }
- }
-#endif
/*
* Force periodic transfer intervals to be legal values that are
* a power of two (so HCDs don't need to).
@@ -459,9 +489,9 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
/* too small? */
switch (dev->speed) {
case USB_SPEED_WIRELESS:
- if (urb->interval < 6)
+ if ((urb->interval < 6)
+ && (xfertype == USB_ENDPOINT_XFER_INT))
return -EINVAL;
- break;
default:
if (urb->interval <= 0)
return -EINVAL;
@@ -532,15 +562,25 @@ EXPORT_SYMBOL_GPL(usb_submit_urb);
* a driver's I/O routines to insure that all URB-related activity has
* completed before it returns.
*
- * This request is always asynchronous. Success is indicated by
- * returning -EINPROGRESS, at which time the URB will probably not yet
- * have been given back to the device driver. When it is eventually
- * called, the completion function will see @urb->status == -ECONNRESET.
+ * This request is asynchronous, however the HCD might call the ->complete()
+ * callback during unlink. Therefore when drivers call usb_unlink_urb(), they
+ * must not hold any locks that may be taken by the completion function.
+ * Success is indicated by returning -EINPROGRESS, at which time the URB will
+ * probably not yet have been given back to the device driver. When it is
+ * eventually called, the completion function will see @urb->status ==
+ * -ECONNRESET.
* Failure is indicated by usb_unlink_urb() returning any other value.
* Unlinking will fail when @urb is not currently "linked" (i.e., it was
* never submitted, or it was unlinked before, or the hardware is already
* finished with it), even if the completion handler has not yet run.
*
+ * The URB must not be deallocated while this routine is running. In
+ * particular, when a driver calls this routine, it must insure that the
+ * completion handler cannot deallocate the URB.
+ *
+ * Return: -EINPROGRESS on success. See description for other values on
+ * failure.
+ *
* Unlinking and Endpoint Queues:
*
* [The behaviors and guarantees described below do not apply to virtual
@@ -605,6 +645,10 @@ EXPORT_SYMBOL_GPL(usb_unlink_urb);
* with error -EPERM. Thus even if the URB's completion handler always
* tries to resubmit, it will not succeed and the URB will become idle.
*
+ * The URB must not be deallocated while this routine is running. In
+ * particular, when a driver calls this routine, it must insure that the
+ * completion handler cannot deallocate the URB.
+ *
* This routine may not be used in an interrupt context (such as a bottom
* half or a completion handler), or when holding a spinlock, or in other
* situations where the caller can't schedule().
@@ -642,6 +686,10 @@ EXPORT_SYMBOL_GPL(usb_kill_urb);
* with error -EPERM. Thus even if the URB's completion handler always
* tries to resubmit, it will not succeed and the URB will become idle.
*
+ * The URB must not be deallocated while this routine is running. In
+ * particular, when a driver calls this routine, it must insure that the
+ * completion handler cannot deallocate the URB.
+ *
* This routine may not be used in an interrupt context (such as a bottom
* half or a completion handler), or when holding a spinlock, or in other
* situations where the caller can't schedule().
@@ -652,10 +700,13 @@ EXPORT_SYMBOL_GPL(usb_kill_urb);
void usb_poison_urb(struct urb *urb)
{
might_sleep();
- if (!(urb && urb->dev && urb->ep))
+ if (!urb)
return;
atomic_inc(&urb->reject);
+ if (!urb->dev || !urb->ep)
+ return;
+
usb_hcd_unlink_urb(urb, -ENOENT);
wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
}
@@ -671,6 +722,27 @@ void usb_unpoison_urb(struct urb *urb)
EXPORT_SYMBOL_GPL(usb_unpoison_urb);
/**
+ * usb_block_urb - reliably prevent further use of an URB
+ * @urb: pointer to URB to be blocked, may be NULL
+ *
+ * After the routine has run, attempts to resubmit the URB will fail
+ * with error -EPERM. Thus even if the URB's completion handler always
+ * tries to resubmit, it will not succeed and the URB will become idle.
+ *
+ * The URB must not be deallocated while this routine is running. In
+ * particular, when a driver calls this routine, it must insure that the
+ * completion handler cannot deallocate the URB.
+ */
+void usb_block_urb(struct urb *urb)
+{
+ if (!urb)
+ return;
+
+ atomic_inc(&urb->reject);
+}
+EXPORT_SYMBOL_GPL(usb_block_urb);
+
+/**
* usb_kill_anchored_urbs - cancel transfer requests en masse
* @anchor: anchor the requests are bound to
*
@@ -759,7 +831,7 @@ EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs);
*
* this allows all outstanding URBs to be unlinked starting
* from the back of the queue. This function is asynchronous.
- * The unlinking is just tiggered. It may happen after this
+ * The unlinking is just triggered. It may happen after this
* function has returned.
*
* This routine should not be called by a driver after its disconnect
@@ -777,17 +849,53 @@ void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
/**
+ * usb_anchor_suspend_wakeups
+ * @anchor: the anchor you want to suspend wakeups on
+ *
+ * Call this to stop the last urb being unanchored from waking up any
+ * usb_wait_anchor_empty_timeout waiters. This is used in the hcd urb give-
+ * back path to delay waking up until after the completion handler has run.
+ */
+void usb_anchor_suspend_wakeups(struct usb_anchor *anchor)
+{
+ if (anchor)
+ atomic_inc(&anchor->suspend_wakeups);
+}
+EXPORT_SYMBOL_GPL(usb_anchor_suspend_wakeups);
+
+/**
+ * usb_anchor_resume_wakeups
+ * @anchor: the anchor you want to resume wakeups on
+ *
+ * Allow usb_wait_anchor_empty_timeout waiters to be woken up again, and
+ * wake up any current waiters if the anchor is empty.
+ */
+void usb_anchor_resume_wakeups(struct usb_anchor *anchor)
+{
+ if (!anchor)
+ return;
+
+ atomic_dec(&anchor->suspend_wakeups);
+ if (usb_anchor_check_wakeup(anchor))
+ wake_up(&anchor->wait);
+}
+EXPORT_SYMBOL_GPL(usb_anchor_resume_wakeups);
+
+/**
* usb_wait_anchor_empty_timeout - wait for an anchor to be unused
* @anchor: the anchor you want to become unused
* @timeout: how long you are willing to wait in milliseconds
*
* Call this is you want to be sure all an anchor's
* URBs have finished
+ *
+ * Return: Non-zero if the anchor became unused. Zero on timeout.
*/
int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
unsigned int timeout)
{
- return wait_event_timeout(anchor->wait, list_empty(&anchor->urb_list),
+ return wait_event_timeout(anchor->wait,
+ usb_anchor_check_wakeup(anchor),
msecs_to_jiffies(timeout));
}
EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
@@ -796,8 +904,11 @@ EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
* usb_get_from_anchor - get an anchor's oldest urb
* @anchor: the anchor whose urb you want
*
- * this will take the oldest urb from an anchor,
+ * This will take the oldest urb from an anchor,
* unanchor and return it
+ *
+ * Return: The oldest urb from @anchor, or %NULL if @anchor has no
+ * urbs associated with it.
*/
struct urb *usb_get_from_anchor(struct usb_anchor *anchor)
{
@@ -846,7 +957,7 @@ EXPORT_SYMBOL_GPL(usb_scuttle_anchored_urbs);
* usb_anchor_empty - is an anchor empty
* @anchor: the anchor you want to query
*
- * returns 1 if the anchor has no urbs associated with it
+ * Return: 1 if the anchor has no urbs associated with it.
*/
int usb_anchor_empty(struct usb_anchor *anchor)
{
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
new file mode 100644
index 00000000000..2776cfe64c0
--- /dev/null
+++ b/drivers/usb/core/usb-acpi.c
@@ -0,0 +1,228 @@
+/*
+ * USB-ACPI glue code
+ *
+ * Copyright 2012 Red Hat <mjg@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, version 2.
+ *
+ */
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/acpi.h>
+#include <linux/pci.h>
+#include <linux/usb/hcd.h>
+
+#include "hub.h"
+
+/**
+ * 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)
+{
+ struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
+ struct usb_port *port_dev;
+ acpi_handle port_handle;
+ unsigned char state;
+ int port1 = index + 1;
+ int error = -EINVAL;
+
+ if (!hub)
+ return -ENODEV;
+ port_dev = hub->ports[port1 - 1];
+
+ 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(&port_dev->dev, "acpi: power was set to %d\n", enable);
+ else
+ dev_dbg(&port_dev->dev, "acpi: power failed to be set\n");
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(usb_acpi_set_power_state);
+
+static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle,
+ struct acpi_pld_info *pld)
+{
+ enum usb_port_connect_type connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *upc;
+ acpi_status status;
+
+ /*
+ * According 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_evaluate_object(handle, "_UPC", NULL, &buffer);
+ upc = buffer.pointer;
+ if (!upc || (upc->type != ACPI_TYPE_PACKAGE)
+ || upc->package.count != 4) {
+ goto out;
+ }
+
+ if (upc->package.elements[0].integer.value)
+ if (pld->user_visible)
+ connect_type = USB_PORT_CONNECT_TYPE_HOT_PLUG;
+ else
+ connect_type = USB_PORT_CONNECT_TYPE_HARD_WIRED;
+ else if (!pld->user_visible)
+ connect_type = USB_PORT_NOT_USED;
+out:
+ kfree(upc);
+ return connect_type;
+}
+
+
+/*
+ * Private to usb-acpi, all the core needs to know is that
+ * port_dev->location is non-zero when it has been set by the firmware.
+ */
+#define USB_ACPI_LOCATION_VALID (1 << 31)
+
+static struct acpi_device *usb_acpi_find_companion(struct device *dev)
+{
+ struct usb_device *udev;
+ struct acpi_device *adev;
+ acpi_handle *parent_handle;
+
+ /*
+ * 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 (is_usb_device(dev)) {
+ udev = to_usb_device(dev);
+ if (udev->parent)
+ return NULL;
+
+ /* root hub is only child (_ADR=0) under its parent, the HC */
+ adev = ACPI_COMPANION(dev->parent);
+ return acpi_find_child_device(adev, 0, false);
+ } else if (is_usb_port(dev)) {
+ struct usb_port *port_dev = to_usb_port(dev);
+ int port1 = port_dev->portnum;
+ struct acpi_pld_info *pld;
+ acpi_handle *handle;
+ acpi_status status;
+
+ /* 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) {
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+ int raw;
+
+ raw = usb_hcd_find_raw_port_number(hcd, port1);
+ adev = acpi_find_child_device(ACPI_COMPANION(&udev->dev),
+ raw, false);
+ if (!adev)
+ return NULL;
+ } else {
+ parent_handle =
+ usb_get_hub_port_acpi_handle(udev->parent,
+ udev->portnum);
+ if (!parent_handle)
+ return NULL;
+
+ acpi_bus_get_device(parent_handle, &adev);
+ adev = acpi_find_child_device(adev, port1, false);
+ if (!adev)
+ return NULL;
+ }
+ handle = adev->handle;
+ status = acpi_get_physical_device_location(handle, &pld);
+ if (ACPI_FAILURE(status) || !pld)
+ return adev;
+
+ port_dev->location = USB_ACPI_LOCATION_VALID
+ | pld->group_token << 8 | pld->group_position;
+ port_dev->connect_type = usb_acpi_get_connect_type(handle, pld);
+ ACPI_FREE(pld);
+
+ return adev;
+ }
+
+ return NULL;
+}
+
+static bool usb_acpi_bus_match(struct device *dev)
+{
+ return is_usb_device(dev) || is_usb_port(dev);
+}
+
+static struct acpi_bus_type usb_acpi_bus = {
+ .name = "USB",
+ .match = usb_acpi_bus_match,
+ .find_companion = usb_acpi_find_companion,
+};
+
+int usb_acpi_register(void)
+{
+ return register_acpi_bus_type(&usb_acpi_bus);
+}
+
+void usb_acpi_unregister(void)
+{
+ unregister_acpi_bus_type(&usb_acpi_bus);
+}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 8ca9f994a28..4d1144990d4 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -49,7 +49,7 @@ const char *usbcore_name = "usbcore";
static bool nousb; /* Disable USB when built into kernel image */
-#ifdef CONFIG_USB_SUSPEND
+#ifdef CONFIG_PM_RUNTIME
static int usb_autosuspend_delay = 2; /* Default delay value,
* in seconds */
module_param_named(autosuspend, usb_autosuspend_delay, int, 0644);
@@ -68,6 +68,8 @@ MODULE_PARM_DESC(autosuspend, "default autosuspend delay");
* @alt_num: alternate interface setting number to search for.
*
* Search the configuration's interface cache for the given alt setting.
+ *
+ * Return: The alternate setting, if found. %NULL otherwise.
*/
struct usb_host_interface *usb_find_alt_setting(
struct usb_host_config *config,
@@ -103,8 +105,7 @@ EXPORT_SYMBOL_GPL(usb_find_alt_setting);
* @ifnum: the desired interface
*
* This walks the device descriptor for the currently active configuration
- * and returns a pointer to the interface with that particular interface
- * number, or null.
+ * to find the interface object with the particular interface number.
*
* Note that configuration descriptors are not required to assign interface
* numbers sequentially, so that it would be incorrect to assume that
@@ -115,6 +116,9 @@ EXPORT_SYMBOL_GPL(usb_find_alt_setting);
*
* Don't call this function unless you are bound to one of the interfaces
* on this device or you have locked the device!
+ *
+ * Return: A pointer to the interface that has @ifnum as interface number,
+ * if found. %NULL otherwise.
*/
struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev,
unsigned ifnum)
@@ -139,8 +143,7 @@ EXPORT_SYMBOL_GPL(usb_ifnum_to_if);
* @altnum: the desired alternate setting number
*
* This searches the altsetting array of the specified interface for
- * an entry with the correct bAlternateSetting value and returns a pointer
- * to that entry, or null.
+ * an entry with the correct bAlternateSetting value.
*
* Note that altsettings need not be stored sequentially by number, so
* it would be incorrect to assume that the first altsetting entry in
@@ -149,6 +152,9 @@ EXPORT_SYMBOL_GPL(usb_ifnum_to_if);
*
* Don't call this function unless you are bound to the intf interface
* or you have locked the device!
+ *
+ * Return: A pointer to the entry of the altsetting array of @intf that
+ * has @altnum as the alternate setting number. %NULL if not found.
*/
struct usb_host_interface *usb_altnum_to_altsetting(
const struct usb_interface *intf,
@@ -191,6 +197,8 @@ static int __find_interface(struct device *dev, void *data)
* This walks the bus device list and returns a pointer to the interface
* with the matching minor and driver. Note, this only works for devices
* that share the USB major number.
+ *
+ * Return: A pointer to the interface with the matching major and @minor.
*/
struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
{
@@ -209,6 +217,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
@@ -233,7 +274,6 @@ static void usb_release_dev(struct device *dev)
kfree(udev);
}
-#ifdef CONFIG_HOTPLUG
static int usb_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct usb_device *usb_dev;
@@ -249,14 +289,6 @@ static int usb_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
return 0;
}
-#else
-
-static int usb_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
-{
- return -ENODEV;
-}
-#endif /* CONFIG_HOTPLUG */
-
#ifdef CONFIG_PM
/* USB device Power-Management thunks.
@@ -274,7 +306,7 @@ static int usb_dev_prepare(struct device *dev)
static void usb_dev_complete(struct device *dev)
{
/* Currently used only for rebinding interfaces */
- usb_resume(dev, PMSG_ON); /* FIXME: change to PMSG_COMPLETE */
+ usb_resume_complete(dev);
}
static int usb_dev_suspend(struct device *dev)
@@ -316,7 +348,7 @@ static const struct dev_pm_ops usb_device_pm_ops = {
.thaw = usb_dev_thaw,
.poweroff = usb_dev_poweroff,
.restore = usb_dev_restore,
-#ifdef CONFIG_USB_SUSPEND
+#ifdef CONFIG_PM_RUNTIME
.runtime_suspend = usb_runtime_suspend,
.runtime_resume = usb_runtime_resume,
.runtime_idle = usb_runtime_idle,
@@ -326,7 +358,8 @@ static const struct dev_pm_ops usb_device_pm_ops = {
#endif /* CONFIG_PM */
-static char *usb_devnode(struct device *dev, umode_t *mode)
+static char *usb_devnode(struct device *dev,
+ umode_t *mode, kuid_t *uid, kgid_t *gid)
{
struct usb_device *usb_dev;
@@ -365,19 +398,22 @@ static unsigned usb_bus_is_wusb(struct usb_bus *bus)
* controllers) should ever call this.
*
* This call may not be used in a non-sleeping context.
+ *
+ * Return: On success, a pointer to the allocated usb device. %NULL on
+ * failure.
*/
struct usb_device *usb_alloc_dev(struct usb_device *parent,
struct usb_bus *bus, unsigned port1)
{
struct usb_device *dev;
- struct usb_hcd *usb_hcd = container_of(bus, struct usb_hcd, self);
+ struct usb_hcd *usb_hcd = bus_to_hcd(bus);
unsigned root_hub = 0;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return NULL;
- if (!usb_get_hcd(bus_to_hcd(bus))) {
+ if (!usb_get_hcd(usb_hcd)) {
kfree(dev);
return NULL;
}
@@ -396,6 +432,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->dev.dma_mask = bus->controller->dma_mask;
set_dev_node(&dev->dev, dev_to_node(bus->controller));
dev->state = USB_STATE_ATTACHED;
+ dev->lpm_disable_count = 1;
atomic_set(&dev->urbnum, 0);
INIT_LIST_HEAD(&dev->ep0.urb_list);
@@ -460,7 +497,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->authorized = 1;
else {
dev->authorized = usb_hcd->authorized_default;
- dev->wusb = usb_bus_is_wusb(bus)? 1 : 0;
+ dev->wusb = usb_bus_is_wusb(bus) ? 1 : 0;
}
return dev;
}
@@ -475,7 +512,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
* their probe() methods, when they bind to an interface, and release
* them by calling usb_put_dev(), in their disconnect() methods.
*
- * A pointer to the device with the incremented reference counter is returned.
+ * Return: A pointer to the device with the incremented reference counter.
*/
struct usb_device *usb_get_dev(struct usb_device *dev)
{
@@ -509,8 +546,7 @@ EXPORT_SYMBOL_GPL(usb_put_dev);
* their probe() methods, when they bind to an interface, and release
* them by calling usb_put_intf(), in their disconnect() methods.
*
- * A pointer to the interface with the incremented reference counter is
- * returned.
+ * Return: A pointer to the interface with the incremented reference counter.
*/
struct usb_interface *usb_get_intf(struct usb_interface *intf)
{
@@ -563,7 +599,7 @@ EXPORT_SYMBOL_GPL(usb_put_intf);
* disconnect; in some drivers (such as usb-storage) the disconnect()
* or suspend() method will block waiting for a device reset to complete.
*
- * Returns a negative error code for failure, otherwise 0.
+ * Return: A negative error code for failure, otherwise 0.
*/
int usb_lock_device_for_reset(struct usb_device *udev,
const struct usb_interface *iface)
@@ -602,14 +638,15 @@ EXPORT_SYMBOL_GPL(usb_lock_device_for_reset);
* usb_get_current_frame_number - return current bus frame number
* @dev: the device whose bus is being queried
*
- * Returns the current frame number for the USB host controller
- * used with the given USB device. This can be used when scheduling
+ * Return: The current frame number for the USB host controller used
+ * with the given USB device. This can be used when scheduling
* isochronous requests.
*
- * Note that different kinds of host controller have different
- * "scheduling horizons". While one type might support scheduling only
- * 32 frames into the future, others could support scheduling up to
- * 1024 frames into the future.
+ * Note: Different kinds of host controller have different "scheduling
+ * horizons". While one type might support scheduling only 32 frames
+ * into the future, others could support scheduling up to 1024 frames
+ * into the future.
+ *
*/
int usb_get_current_frame_number(struct usb_device *dev)
{
@@ -659,11 +696,12 @@ EXPORT_SYMBOL_GPL(__usb_get_extra_descriptor);
* @mem_flags: affect whether allocation may block
* @dma: used to return DMA address of buffer
*
- * Return value is either null (indicating no buffer could be allocated), or
- * the cpu-space pointer to a buffer that may be used to perform DMA to the
+ * Return: Either null (indicating no buffer could be allocated), or the
+ * cpu-space pointer to a buffer that may be used to perform DMA to the
* specified device. Such cpu-space buffers are returned along with the DMA
* address (through the pointer provided).
*
+ * Note:
* These buffers are used with URB_NO_xxx_DMA_MAP set in urb->transfer_flags
* to avoid behaviors like using "DMA bounce buffers", or thrashing IOMMU
* hardware during URB completion/resubmit. The implementation varies between
@@ -709,17 +747,18 @@ EXPORT_SYMBOL_GPL(usb_free_coherent);
* usb_buffer_map - create DMA mapping(s) for an urb
* @urb: urb whose transfer_buffer/setup_packet will be mapped
*
- * Return value is either null (indicating no buffer could be mapped), or
- * the parameter. URB_NO_TRANSFER_DMA_MAP is
- * added to urb->transfer_flags if the operation succeeds. If the device
- * is connected to this system through a non-DMA controller, this operation
- * always succeeds.
+ * URB_NO_TRANSFER_DMA_MAP is added to urb->transfer_flags if the operation
+ * succeeds. If the device is connected to this system through a non-DMA
+ * controller, this operation always succeeds.
*
* This call would normally be used for an urb which is reused, perhaps
* as the target of a large periodic transfer, with usb_buffer_dmasync()
* calls to synchronize memory and dma state.
*
* Reverse the effect of this call with usb_buffer_unmap().
+ *
+ * Return: Either %NULL (indicating no buffer could be mapped), or @urb.
+ *
*/
#if 0
struct urb *usb_buffer_map(struct urb *urb)
@@ -824,9 +863,10 @@ EXPORT_SYMBOL_GPL(usb_buffer_unmap);
* @sg: the scatterlist to map
* @nents: the number of entries in the scatterlist
*
- * Return value is either < 0 (indicating no buffers could be mapped), or
- * the number of DMA mapping array entries in the scatterlist.
+ * Return: Either < 0 (indicating no buffers could be mapped), or the
+ * number of DMA mapping array entries in the scatterlist.
*
+ * Note:
* The caller is responsible for placing the resulting DMA addresses from
* the scatterlist into URB transfer buffer pointers, and for setting the
* URB_NO_TRANSFER_DMA_MAP transfer flag in each of those URBs.
@@ -1015,6 +1055,7 @@ static int __init usb_init(void)
if (retval)
goto out;
+ usb_acpi_register();
retval = bus_register(&usb_bus_type);
if (retval)
goto bus_register_failed;
@@ -1030,9 +1071,6 @@ static int __init usb_init(void)
retval = usb_devio_init();
if (retval)
goto usb_devio_init_failed;
- retval = usbfs_init();
- if (retval)
- goto fs_init_failed;
retval = usb_hub_init();
if (retval)
goto hub_init_failed;
@@ -1042,8 +1080,6 @@ static int __init usb_init(void)
usb_hub_cleanup();
hub_init_failed:
- usbfs_cleanup();
-fs_init_failed:
usb_devio_cleanup();
usb_devio_init_failed:
usb_deregister(&usbfs_driver);
@@ -1054,6 +1090,7 @@ major_init_failed:
bus_notifier_failed:
bus_unregister(&usb_bus_type);
bus_register_failed:
+ usb_acpi_unregister();
usb_debugfs_cleanup();
out:
return retval;
@@ -1070,12 +1107,12 @@ static void __exit usb_exit(void)
usb_deregister_device_driver(&usb_generic_driver);
usb_major_cleanup();
- usbfs_cleanup();
usb_deregister(&usbfs_driver);
usb_devio_cleanup();
usb_hub_cleanup();
bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
bus_unregister(&usb_bus_type);
+ usb_acpi_unregister();
usb_debugfs_cleanup();
}
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 45e8479c377..d9d08720c38 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -1,4 +1,8 @@
#include <linux/pm.h>
+#include <linux/acpi.h>
+
+struct usb_hub_descriptor;
+struct usb_dev_state;
/* Functions local to drivers/usb/core/ */
@@ -24,6 +28,7 @@ extern void usb_disable_device(struct usb_device *dev, int skip_ep0);
extern int usb_deauthorize_device(struct usb_device *);
extern int usb_authorize_device(struct usb_device *);
extern void usb_detect_quirks(struct usb_device *udev);
+extern void usb_detect_interface_quirks(struct usb_device *udev);
extern int usb_remove_device(struct usb_device *udev);
extern int usb_get_device_descriptor(struct usb_device *dev,
@@ -34,17 +39,26 @@ extern char *usb_cache_string(struct usb_device *udev, int index);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
extern int usb_choose_configuration(struct usb_device *udev);
+static inline unsigned usb_get_max_power(struct usb_device *udev,
+ struct usb_host_config *c)
+{
+ /* SuperSpeed power is in 8 mA units; others are in 2 mA units */
+ unsigned mul = (udev->speed == USB_SPEED_SUPER ? 8 : 2);
+
+ return c->desc.bMaxPower * mul;
+}
+
extern void usb_kick_khubd(struct usb_device *dev);
+extern int usb_match_one_id_intf(struct usb_device *dev,
+ struct usb_host_interface *intf,
+ const struct usb_device_id *id);
extern int usb_match_device(struct usb_device *dev,
const struct usb_device_id *id);
extern void usb_forced_unbind_intf(struct usb_interface *intf);
-extern void usb_rebind_intf(struct usb_interface *intf);
+extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev);
-extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port,
- void *owner);
-extern int usb_hub_release_port(struct usb_device *hdev, unsigned port,
- void *owner);
-extern void usb_hub_release_all_ports(struct usb_device *hdev, void *owner);
+extern void usb_hub_release_all_ports(struct usb_device *hdev,
+ struct usb_dev_state *owner);
extern bool usb_device_is_owned(struct usb_device *udev);
extern int usb_hub_init(void);
@@ -56,6 +70,7 @@ extern void usb_major_cleanup(void);
extern int usb_suspend(struct device *dev, pm_message_t msg);
extern int usb_resume(struct device *dev, pm_message_t msg);
+extern int usb_resume_complete(struct device *dev);
extern int usb_port_suspend(struct usb_device *dev, pm_message_t msg);
extern int usb_port_resume(struct usb_device *dev, pm_message_t msg);
@@ -74,7 +89,7 @@ static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg)
#endif
-#ifdef CONFIG_USB_SUSPEND
+#ifdef CONFIG_PM_RUNTIME
extern void usb_autosuspend_device(struct usb_device *udev);
extern int usb_autoresume_device(struct usb_device *udev);
@@ -92,11 +107,6 @@ static inline int usb_autoresume_device(struct usb_device *udev)
return 0;
}
-static inline int usb_remote_wakeup(struct usb_device *udev)
-{
- return 0;
-}
-
static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
{
return 0;
@@ -104,9 +114,11 @@ static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
#endif
extern struct bus_type usb_bus_type;
+extern struct mutex usb_port_peer_mutex;
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)
@@ -124,6 +136,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)
@@ -149,9 +166,26 @@ extern void usbfs_conn_disc_event(void);
extern int usb_devio_init(void);
extern void usb_devio_cleanup(void);
+/*
+ * Firmware specific cookie identifying a port's location. '0' == no location
+ * data available
+ */
+typedef u32 usb_port_location_t;
+
/* internal notify stuff */
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 void usb_hub_adjust_deviceremovable(struct usb_device *hdev,
+ struct usb_hub_descriptor *desc);
+
+#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) { };
+#endif