aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/core
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/Kconfig19
-rw-r--r--drivers/usb/core/Makefile2
-rw-r--r--drivers/usb/core/buffer.c2
-rw-r--r--drivers/usb/core/config.c13
-rw-r--r--drivers/usb/core/devio.c342
-rw-r--r--drivers/usb/core/driver.c180
-rw-r--r--drivers/usb/core/file.c24
-rw-r--r--drivers/usb/core/generic.c1
-rw-r--r--drivers/usb/core/hcd-pci.c7
-rw-r--r--drivers/usb/core/hcd.c241
-rw-r--r--drivers/usb/core/hub.c1369
-rw-r--r--drivers/usb/core/hub.h47
-rw-r--r--drivers/usb/core/message.c19
-rw-r--r--drivers/usb/core/port.c354
-rw-r--r--drivers/usb/core/quirks.c44
-rw-r--r--drivers/usb/core/sysfs.c66
-rw-r--r--drivers/usb/core/urb.c71
-rw-r--r--drivers/usb/core/usb-acpi.c140
-rw-r--r--drivers/usb/core/usb.c2
-rw-r--r--drivers/usb/core/usb.h26
20 files changed, 1928 insertions, 1041 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index db535b0aa17..1060657ca1b 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -1,16 +1,8 @@
#
# USB Core configuration
#
-config USB_DEBUG
- bool "USB verbose debug messages"
- 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"
- default N
help
Say Y here if you want the USB core to always announce the
idVendor, idProduct, Manufacturer, Product, and SerialNumber
@@ -28,7 +20,7 @@ config USB_DEFAULT_PERSIST
bool "Enable USB persist by default"
default y
help
- Say N here if you don't want USB power session persistance
+ 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
@@ -89,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 5e847ad2f58..2f6f9322004 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -2,8 +2,6 @@
# 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
diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c
index 23559746be9..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>
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index a6b2cabe793..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 */
@@ -651,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)
{
@@ -666,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, "
@@ -724,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) {
@@ -751,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/devio.c b/drivers/usb/core/devio.c
index 737e3c19967..257876ea03a 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -62,7 +62,7 @@
/* 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;
@@ -81,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;
@@ -118,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 */
@@ -151,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);
@@ -184,7 +184,7 @@ static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
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;
@@ -307,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);
@@ -317,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);
@@ -325,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;
@@ -340,7 +340,7 @@ 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)
{
struct async *as;
@@ -448,7 +448,7 @@ static int copy_urb_data_to_user(u8 __user *userbuffer, struct urb *urb)
#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)
{
@@ -489,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;
@@ -529,7 +529,7 @@ 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;
@@ -551,7 +551,7 @@ static void destroy_async(struct dev_state *ps, struct list_head *list)
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;
@@ -566,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);
}
@@ -585,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)
@@ -628,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;
@@ -650,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;
@@ -670,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;
@@ -710,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;
@@ -742,6 +742,22 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,
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;
@@ -753,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;
@@ -775,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;
@@ -836,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;
@@ -867,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;
@@ -898,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)) {
@@ -956,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;
@@ -1029,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;
@@ -1042,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;
@@ -1060,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
@@ -1068,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;
@@ -1087,7 +1199,7 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg)
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,
@@ -1099,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;
@@ -1113,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;
@@ -1165,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)
{
@@ -1175,6 +1290,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
struct usb_ctrlrequest *dr = NULL;
unsigned int u, totlen, isofrmlen;
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 |
@@ -1195,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) {
@@ -1228,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) {
@@ -1258,17 +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:
@@ -1278,15 +1389,16 @@ 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++) {
+ for (totlen = u = 0; u < number_of_packets; u++) {
/*
* arbitrary limit need for USB 3.0
* bMaxBurst (0~15 allowed, 1~16 packets)
@@ -1317,7 +1429,7 @@ 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;
@@ -1411,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);
@@ -1419,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;
@@ -1494,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;
@@ -1506,7 +1619,7 @@ 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;
@@ -1566,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;
@@ -1589,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) {
@@ -1602,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;
@@ -1617,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;
@@ -1685,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;
@@ -1731,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) {
@@ -1744,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;
@@ -1761,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;
@@ -1772,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;
@@ -1781,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;
@@ -1794,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;
@@ -1870,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;
@@ -1880,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;
@@ -1898,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;
@@ -1912,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;
@@ -1921,7 +2034,7 @@ 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 dev_state *ps, void __user *arg)
+static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg)
{
__u32 caps;
@@ -1937,7 +2050,7 @@ static int proc_get_capabilities(struct dev_state *ps, void __user *arg)
return 0;
}
-static int proc_disconnect_claim(struct dev_state *ps, void __user *arg)
+static int proc_disconnect_claim(struct usb_dev_state *ps, void __user *arg)
{
struct usbdevfs_disconnect_claim dc;
struct usb_interface *intf;
@@ -1969,6 +2082,45 @@ static int proc_disconnect_claim(struct dev_state *ps, void __user *arg)
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
@@ -1977,7 +2129,7 @@ static int proc_disconnect_claim(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 usb_dev_state *ps = file->private_data;
struct inode *inode = file_inode(file);
struct usb_device *dev = ps->dev;
int ret = -ENOTTY;
@@ -2145,6 +2297,12 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
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)
@@ -2178,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);
@@ -2204,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);
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index f7841d44fed..4aeb10034de 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -37,6 +37,7 @@
* 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)
{
@@ -44,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;
@@ -60,11 +62,36 @@ 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);
@@ -74,6 +101,10 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids,
if (retval)
return retval;
return count;
+
+fail:
+ kfree(dynid);
+ return retval;
}
EXPORT_SYMBOL_GPL(usb_store_new_id);
@@ -106,7 +137,7 @@ static ssize_t new_id_store(struct device_driver *driver,
{
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_RW(new_id);
@@ -281,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;
@@ -369,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, lpm_disable_error;
+ int i, j, error, r, lpm_disable_error;
intf->condition = USB_INTERFACE_UNBINDING;
@@ -394,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
@@ -839,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;
@@ -900,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;
@@ -959,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)
{
@@ -973,16 +1024,37 @@ 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;
@@ -999,68 +1071,66 @@ void usb_rebind_intf(struct usb_interface *intf)
}
}
-#ifdef CONFIG_PM
-
-/* 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.
+/*
+ * 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 unbind_no_pm_drivers_interfaces(struct usb_device *udev)
+static void rebind_marked_interfaces(struct usb_device *udev)
{
struct usb_host_config *config;
int i;
struct usb_interface *intf;
- struct usb_driver *drv;
config = udev->actconfig;
if (config) {
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
intf = config->interface[i];
-
- if (intf->dev.driver) {
- drv = to_usb_driver(intf->dev.driver);
- if (!drv->suspend || !drv->resume)
- usb_forced_unbind_intf(intf);
- }
+ if (intf->needs_binding)
+ usb_rebind_intf(intf);
}
}
}
-/* Unbind drivers for @udev's interfaces that failed to support reset-resume.
- * These interfaces have the needs_binding flag set by usb_resume_interface().
+/*
+ * 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.
*/
-static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev)
+void usb_unbind_and_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->dev.driver && intf->needs_binding)
- usb_forced_unbind_intf(intf);
- }
- }
+ unbind_marked_interfaces(udev);
+ rebind_marked_interfaces(udev);
}
-static void do_rebind_interfaces(struct usb_device *udev)
+#ifdef CONFIG_PM
+
+/* 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 unbind_no_pm_drivers_interfaces(struct usb_device *udev)
{
struct usb_host_config *config;
int i;
struct usb_interface *intf;
+ struct usb_driver *drv;
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);
+
+ if (intf->dev.driver) {
+ drv = to_usb_driver(intf->dev.driver);
+ if (!drv->suspend || !drv->resume)
+ usb_forced_unbind_intf(intf);
+ }
}
}
}
@@ -1179,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);
@@ -1389,7 +1459,7 @@ int usb_resume_complete(struct device *dev)
* whose needs_binding flag is set
*/
if (udev->state != USB_STATE_NOTATTACHED)
- do_rebind_interfaces(udev);
+ rebind_marked_interfaces(udev);
return 0;
}
@@ -1411,7 +1481,7 @@ int usb_resume(struct device *dev, pm_message_t msg)
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
- unbind_no_reset_resume_drivers_interfaces(udev);
+ unbind_marked_interfaces(udev);
}
/* Avoid PM error messages for devices disconnected while suspended
@@ -1752,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;
}
@@ -1790,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)
diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c
index 7421888087a..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;
@@ -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.
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index acbfeb0a011..358ca8dd784 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -155,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)
{
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index b9d3c43e385..82044b5d611 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -75,7 +75,7 @@ static void for_each_companion(struct pci_dev *pdev, struct usb_hcd *hcd,
PCI_SLOT(companion->devfn) != slot)
continue;
companion_hcd = pci_get_drvdata(companion);
- if (!companion_hcd)
+ if (!companion_hcd || !companion_hcd->self.root_hub)
continue;
fn(pdev, hcd, companion, companion_hcd);
}
@@ -192,7 +192,6 @@ 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;
/*
* The xHCI driver has its own irq management
@@ -215,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);
@@ -279,6 +281,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (retval != 0)
goto unmap_registers;
+ device_wakeup_enable(hcd->self.controller);
if (pci_dev_run_wake(dev))
pm_runtime_put_noidle(&dev->dev);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index d6a8d23f047..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
@@ -40,9 +40,11 @@
#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"
@@ -92,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 */
@@ -171,7 +170,7 @@ static const u8 usb25_rh_dev_descriptor[18] = {
};
/* 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 */
@@ -194,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 */
@@ -219,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; */
@@ -228,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
@@ -256,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; */
@@ -275,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
@@ -303,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,
@@ -428,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") */
@@ -464,7 +463,7 @@ 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;
unsigned len = 0;
int status;
@@ -526,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:
@@ -546,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:
@@ -609,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;
@@ -625,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:
@@ -683,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 */
@@ -877,11 +876,11 @@ static ssize_t 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_RW(authorized_default);
@@ -919,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);
}
@@ -941,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 */
@@ -987,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);
}
/**
@@ -1120,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
@@ -1298,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".
*
*/
@@ -1503,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,
@@ -1651,6 +1654,7 @@ 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;
@@ -1662,6 +1666,7 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
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 */
@@ -1681,6 +1686,7 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
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);
@@ -1703,7 +1709,9 @@ static void usb_giveback_urb_bh(unsigned long param)
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 */
@@ -1812,7 +1820,7 @@ rescan:
case USB_ENDPOINT_XFER_INT:
s = "-intr"; break;
default:
- s = "-iso"; break;
+ s = "-iso"; break;
};
s;
}));
@@ -2045,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);
@@ -2054,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);
@@ -2073,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);
@@ -2239,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
@@ -2324,15 +2350,8 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum);
irqreturn_t usb_hcd_irq (int irq, void *__hcd)
{
struct usb_hcd *hcd = __hcd;
- unsigned long flags;
irqreturn_t rc;
- /* IRQF_DISABLED doesn't work correctly with shared IRQs
- * when the first handler doesn't use it. So let's just
- * assume it's never used.
- */
- local_irq_save(flags);
-
if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd)))
rc = IRQ_NONE;
else if (hcd->driver->irq(hcd) == IRQ_NONE)
@@ -2340,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);
@@ -2438,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);
@@ -2494,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);
}
@@ -2547,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,
@@ -2580,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
@@ -2596,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);
@@ -2611,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)
@@ -2622,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:
@@ -2701,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:
@@ -2737,13 +2786,19 @@ err_hcd_driver_start:
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);
/**
@@ -2811,14 +2866,20 @@ void usb_remove_hcd(struct usb_hcd *hcd)
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);
@@ -2840,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 dde4c83516a..0e950ad8cb2 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -33,21 +33,9 @@
#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
-
#define USB_VENDOR_GENESYS_LOGIC 0x05e3
#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01
-static inline int hub_is_superspeed(struct usb_device *hdev)
-{
- return (hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS);
-}
-
/* Protect struct usb_device->state and ->children members
* Note: Both are also protected by ->dev.sem, except that ->state can
* change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
@@ -62,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);
@@ -120,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
@@ -148,19 +139,27 @@ static int usb_device_supports_lpm(struct usb_device *udev)
return 0;
}
- /* All USB 3.0 must support LPM, but we need their max exit latency
- * information from the SuperSpeed Extended Capabilities BOS descriptor.
+ /*
+ * 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_warn(&udev->dev, "No LPM exit latency info found. "
- "Power management will be impacted.\n");
+ dev_info(&udev->dev, "No LPM exit latency info found, disabling LPM.\n");
return 0;
}
- if (udev->parent->lpm_capable)
- return 1;
- dev_warn(&udev->dev, "Parent hub missing LPM exit latency info. "
- "Power management will be impacted.\n");
+ 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;
}
@@ -310,9 +309,9 @@ static void usb_set_lpm_parameters(struct usb_device *udev)
return;
udev_u1_del = udev->bos->ss_cap->bU1devExitLat;
- udev_u2_del = udev->bos->ss_cap->bU2DevExitLat;
+ udev_u2_del = le16_to_cpu(udev->bos->ss_cap->bU2DevExitLat);
hub_u1_del = udev->parent->bos->ss_cap->bU1devExitLat;
- hub_u2_del = udev->parent->bos->ss_cap->bU2DevExitLat;
+ 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);
@@ -411,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)
@@ -506,7 +510,8 @@ static void led_work (struct work_struct *work)
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 */
@@ -741,16 +746,20 @@ int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub,
int port1, bool set)
{
int ret;
- struct usb_port *port_dev = hub->ports[port1 - 1];
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)
- port_dev->power_is_on = set;
- return ret;
+ if (ret)
+ return ret;
+
+ if (set)
+ set_bit(port1, hub->power_bits);
+ else
+ clear_bit(port1, hub->power_bits);
+ return 0;
}
/**
@@ -808,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
@@ -825,23 +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->hdev->maxchild; port1++)
- if (hub->ports[port1 - 1]->power_is_on)
+ 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);
-
- /* Wait at least 100 msec for power to become stable */
- delay = max(pgood_delay, (unsigned) 100);
if (do_delay)
- msleep(delay);
- return delay;
+ msleep(hub_power_on_good_delay(hub));
}
static int hub_hub_status(struct usb_hub *hub,
@@ -857,7 +855,7 @@ static int hub_hub_status(struct usb_hub *hub,
"%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);
@@ -891,6 +889,25 @@ static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
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;
@@ -909,20 +926,20 @@ static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
msleep(HUB_DEBOUNCE_STEP);
}
if (total_time >= HUB_DEBOUNCE_TIMEOUT)
- dev_warn(hub->intfdev, "Could not disable port %d after %d ms\n",
- port1, total_time);
+ 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 (hub->ports[port1 - 1]->child && set_state)
- usb_set_device_state(hub->ports[port1 - 1]->child,
- USB_STATE_NOTATTACHED);
+ 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);
@@ -931,8 +948,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
USB_PORT_FEAT_ENABLE);
}
if (ret && ret != -ENODEV)
- dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
- port1, ret);
+ dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret);
return ret;
}
@@ -943,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:
@@ -956,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);
}
/**
@@ -1046,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 */
@@ -1080,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 = hub->ports[port1 - 1]->child;
+ 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.
@@ -1107,16 +1128,13 @@ 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)) {
+ 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 */
@@ -1130,6 +1148,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_ENABLE);
}
+ if (portchange & USB_PORT_STAT_C_RESET) {
+ need_debounce_delay = true;
+ 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;
@@ -1147,7 +1170,8 @@ 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) {
@@ -1167,15 +1191,13 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
set_bit(port1, hub->change_bits);
} else if (udev->persist_enabled) {
- struct usb_port *port_dev = hub->ports[port1 - 1];
-
#ifdef CONFIG_PM
udev->reset_resume = 1;
#endif
/* Don't set the change_bits when the device
* was powered off.
*/
- if (port_dev->power_is_on)
+ if (test_bit(port1, hub->power_bits))
set_bit(port1, hub->change_bits);
} else {
@@ -1198,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 {
@@ -1213,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);
@@ -1268,12 +1292,22 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
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 */
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;
}
@@ -1282,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;
}
@@ -1299,6 +1335,7 @@ static int hub_configure(struct usb_hub *hub,
char *message = "out of memory";
unsigned unit_load;
unsigned full_load;
+ unsigned maxchild;
hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
if (!hub->buffer) {
@@ -1337,12 +1374,11 @@ static int hub_configure(struct usb_hub *hub,
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->ports = kzalloc(hdev->maxchild * sizeof(struct usb_port *),
- GFP_KERNEL);
+ hub->ports = kzalloc(maxchild * sizeof(struct usb_port *), GFP_KERNEL);
if (!hub->ports) {
ret = -ENOMEM;
goto fail;
@@ -1361,13 +1397,13 @@ static int hub_configure(struct usb_hub *hub,
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");
@@ -1429,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[] */
@@ -1479,7 +1515,7 @@ static int hub_configure(struct usb_hub *hub,
if (hcd->power_budget > 0)
hdev->bus_mA = hcd->power_budget;
else
- hdev->bus_mA = full_load * hdev->maxchild;
+ hdev->bus_mA = full_load * maxchild;
if (hdev->bus_mA >= full_load)
hub->mA_per_port = full_load;
else {
@@ -1494,7 +1530,7 @@ static int hub_configure(struct usb_hub *hub,
hub->descriptor->bHubContrCurrent);
hub->limited_power = 1;
- if (remaining < hdev->maxchild * unit_load)
+ if (remaining < maxchild * unit_load)
dev_warn(hub_dev,
"insufficient power available "
"to use all downstream ports\n");
@@ -1509,18 +1545,6 @@ static int hub_configure(struct usb_hub *hub,
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.
- */
- 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";
@@ -1560,15 +1584,37 @@ 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;
- for (i = 0; i < hdev->maxchild; i++) {
+ 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);
- hdev->maxchild = i;
- goto fail_keep_maxchild;
+ 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;
}
}
@@ -1578,8 +1624,6 @@ static int hub_configure(struct usb_hub *hub,
return 0;
fail:
- hdev->maxchild = 0;
-fail_keep_maxchild:
dev_err (hub_dev, "config failed, %s (err %d)\n",
message, ret);
/* hub_disconnect() frees urb and descriptor */
@@ -1600,7 +1644,7 @@ static void hub_disconnect(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata(intf);
struct usb_device *hdev = interface_to_usbdev(intf);
- int i;
+ int port1;
/* Take the hub off the event list and don't let it be added again */
spin_lock_irq(&hub_event_lock);
@@ -1615,11 +1659,19 @@ static void hub_disconnect(struct usb_interface *intf)
hub->error = 0;
hub_quiesce(hub, HUB_DISCONNECT);
- usb_set_intfdata (intf, NULL);
+ mutex_lock(&usb_port_peer_mutex);
- for (i = 0; i < hdev->maxchild; i++)
- usb_hub_remove_port_device(hub, i + 1);
- hub->hdev->maxchild = 0;
+ /* 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--;
@@ -1679,8 +1731,19 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
*/
pm_runtime_set_autosuspend_delay(&hdev->dev, 0);
- /* Hubs have proper suspend/resume support. */
- usb_enable_autosuspend(hdev);
+ /*
+ * Hubs have proper suspend/resume support, except for root hubs
+ * where the controller driver doesn't have bus_suspend and
+ * bus_resume methods.
+ */
+ 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,
@@ -1788,7 +1851,7 @@ 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,
- struct dev_state ***ppowner)
+ struct usb_dev_state ***ppowner)
{
struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
@@ -1806,10 +1869,10 @@ static int find_port_owner(struct usb_device *hdev, unsigned port1,
/* In the following three functions, the caller must hold hdev's lock */
int usb_hub_claim_port(struct usb_device *hdev, unsigned port1,
- struct dev_state *owner)
+ struct usb_dev_state *owner)
{
int rc;
- struct dev_state **powner;
+ struct usb_dev_state **powner;
rc = find_port_owner(hdev, port1, &powner);
if (rc)
@@ -1819,12 +1882,13 @@ int usb_hub_claim_port(struct usb_device *hdev, unsigned port1,
*powner = owner;
return rc;
}
+EXPORT_SYMBOL_GPL(usb_hub_claim_port);
int usb_hub_release_port(struct usb_device *hdev, unsigned port1,
- struct dev_state *owner)
+ struct usb_dev_state *owner)
{
int rc;
- struct dev_state **powner;
+ struct usb_dev_state **powner;
rc = find_port_owner(hdev, port1, &powner);
if (rc)
@@ -1834,8 +1898,9 @@ int usb_hub_release_port(struct usb_device *hdev, unsigned port1,
*powner = NULL;
return rc;
}
+EXPORT_SYMBOL_GPL(usb_hub_release_port);
-void usb_hub_release_all_ports(struct usb_device *hdev, struct dev_state *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;
@@ -1978,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);
@@ -2010,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
@@ -2018,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.
@@ -2028,9 +2105,10 @@ static void hub_free_dev(struct usb_device *udev)
*/
void usb_disconnect(struct usb_device **pdev)
{
- struct usb_device *udev = *pdev;
- struct usb_hub *hub = usb_hub_to_struct_hub(udev);
- int i;
+ 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.
@@ -2042,11 +2120,7 @@ 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 < udev->maxchild; i++) {
- if (hub->ports[i]->child)
- usb_disconnect(&hub->ports[i]->child);
- }
+ hub_disconnect_children(udev);
/* deallocate hcd/hardware state ... nuking all pending urbs and
* cleaning up all state associated with the current configuration
@@ -2057,16 +2131,19 @@ void usb_disconnect(struct usb_device **pdev)
usb_hcd_synchronize_unlinks(udev);
if (udev->parent) {
- struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
- struct usb_port *port_dev = hub->ports[udev->portnum - 1];
+ 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");
- if (!port_dev->did_runtime_put)
- pm_runtime_put(&port_dev->dev);
- else
- port_dev->did_runtime_put = false;
+ /*
+ * 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);
@@ -2088,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);
@@ -2228,18 +2308,13 @@ static int usb_enumerate_device(struct usb_device *udev)
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);
if (err < 0)
return err;
@@ -2280,6 +2355,22 @@ static void set_usb_port_removable(struct usb_device *udev)
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;
+ }
}
/**
@@ -2349,11 +2440,7 @@ int usb_new_device(struct usb_device *udev)
device_enable_async_suspend(&udev->dev);
- /*
- * check whether the hub marks this port as non-removable. Do it
- * now so that platform-specific data can override it in
- * device_add()
- */
+ /* check whether the hub or firmware marks this port as non-removable */
if (udev->parent)
set_usb_port_removable(udev);
@@ -2370,7 +2457,8 @@ int usb_new_device(struct usb_device *udev)
/* Create link files between child device and usb port device. */
if (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;
+ struct usb_port *port_dev = hub->ports[port1 - 1];
err = sysfs_create_link(&udev->dev.kobj,
&port_dev->dev.kobj, "port");
@@ -2384,7 +2472,8 @@ int usb_new_device(struct usb_device *udev)
goto fail;
}
- pm_runtime_get_sync(&port_dev->dev);
+ 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);
@@ -2421,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;
@@ -2458,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.
*/
@@ -2484,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;
}
@@ -2517,10 +2585,25 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
#define HUB_LONG_RESET_TIME 200
#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 or Complinance Mode state?
+/* 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)
@@ -2558,9 +2641,9 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
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);
}
if ((portstatus & USB_PORT_STAT_RESET))
@@ -2644,6 +2727,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
{
int i, status;
u16 portchange, portstatus;
+ struct usb_port *port_dev = hub->ports[port1 - 1];
if (!hub_is_superspeed(hub->hdev)) {
if (warm) {
@@ -2677,9 +2761,9 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
if (status == -ENODEV) {
; /* The hub is gone */
} else if (status) {
- dev_err(hub->intfdev,
- "cannot %sreset port %d (err = %d)\n",
- warm ? "warm " : "", port1, 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);
@@ -2712,21 +2796,19 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
* hot or warm reset failed. Try another warm reset.
*/
if (!warm) {
- dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
- port1);
+ 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 (!hub_is_superspeed(hub->hdev))
@@ -2751,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) */
@@ -2777,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) ||
@@ -2796,9 +2894,8 @@ 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 */
@@ -2972,6 +3069,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
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).
*
@@ -3029,8 +3128,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
status = 0;
}
if (status) {
- dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
- port1, status);
+ 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);
@@ -3061,12 +3159,13 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
usb_set_device_state(udev, USB_STATE_SUSPENDED);
}
- if (status == 0 && !udev->do_remote_wakeup && udev->persist_enabled) {
+ 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);
- port_dev->did_runtime_put = true;
- }
usb_mark_last_busy(hub->hdev);
+
+ usb_unlock_port(port_dev);
return status;
}
@@ -3104,12 +3203,22 @@ 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) {
@@ -3196,9 +3305,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
int status;
u16 portchange, portstatus;
- if (port_dev->did_runtime_put) {
+ if (!test_and_set_bit(port1, hub->child_usage_bits)) {
status = pm_runtime_get_sync(&port_dev->dev);
- port_dev->did_runtime_put = false;
if (status < 0) {
dev_dbg(&udev->dev, "can't resume usb port, status %d\n",
status);
@@ -3206,15 +3314,13 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
}
}
+ 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 = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U0);
@@ -3222,8 +3328,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
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",
@@ -3254,8 +3359,6 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
}
}
- clear_bit(port1, hub->busy_bits);
-
status = check_port_resume_type(udev,
hub, port1, status, portchange, portstatus);
if (status == 0)
@@ -3273,16 +3376,18 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
usb_unlocked_enable_lpm(udev);
}
+ usb_unlock_port(port_dev);
+
return status;
}
#ifdef CONFIG_PM_RUNTIME
-/* caller has locked udev */
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);
@@ -3291,9 +3396,59 @@ int usb_remote_wakeup(struct usb_device *udev)
usb_autosuspend_device(udev);
}
}
+ usb_unlock_device(udev);
return status;
}
+/* 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;
+ }
+
+ if (udev) {
+ /* TRSMRCY = 10 msec */
+ msleep(10);
+
+ 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;
+}
+
#endif
static int check_ports_changed(struct usb_hub *hub)
@@ -3324,11 +3479,12 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
*/
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 = hub->ports[port1 - 1]->child;
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;
}
@@ -3426,6 +3582,9 @@ static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state)
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);
@@ -3844,6 +4003,12 @@ 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 */
@@ -3852,7 +4017,7 @@ EXPORT_SYMBOL_GPL(usb_enable_ltm);
* 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.
@@ -3865,9 +4030,10 @@ EXPORT_SYMBOL_GPL(usb_enable_ltm);
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);
@@ -3896,9 +4062,8 @@ int hub_port_debounce(struct usb_hub *hub, int port1, bool must_be_connected)
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;
@@ -3946,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;
@@ -3983,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. */
@@ -4052,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
@@ -4066,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) {
@@ -4127,11 +4343,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
#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);
@@ -4158,9 +4374,13 @@ 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) {
@@ -4216,7 +4436,7 @@ 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)) {
if (retval != -ENODEV)
@@ -4239,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;
}
@@ -4266,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);
@@ -4284,9 +4506,10 @@ 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 = hub->ports[port1 - 1]->child;
- int delta;
- unsigned unit_load;
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+ struct usb_device *udev = port_dev->child;
+ unsigned unit_load;
+ int delta;
if (!udev)
continue;
@@ -4306,91 +4529,35 @@ hub_power_remaining (struct usb_hub *hub)
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)
{
- 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;
unsigned unit_load;
-
- 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 = hub->ports[port1 - 1]->child;
- 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_PM_RUNTIME
- } 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_device *hdev = hub->hdev;
+ struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
+ 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) {
if (hcd->phy && !hdev->parent &&
!(portstatus & USB_PORT_STAT_CONNECTION))
usb_phy_notify_disconnect(hcd->phy, udev->speed);
- usb_disconnect(&hub->ports[port1 - 1]->child);
+ usb_disconnect(&port_dev->child);
}
- clear_bit(port1, hub->change_bits);
/* We can forget about a "removed" device when there's a physical
* disconnect or the connect status changes.
@@ -4404,8 +4571,8 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
status = hub_port_debounce_be_stable(hub, port1);
if (status < 0) {
if (status != -ENODEV && printk_ratelimit())
- dev_err(hub_dev, "connect-debounce failed, "
- "port %d disabled\n", port1);
+ dev_err(&port_dev->dev,
+ "connect-debounce failed\n");
portstatus &= ~USB_PORT_STAT_CONNECTION;
} else {
portstatus = status;
@@ -4419,12 +4586,12 @@ 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))
@@ -4440,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);
@@ -4464,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;
@@ -4495,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
@@ -4514,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.
@@ -4522,16 +4694,19 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
if (hdev->state == USB_STATE_NOTATTACHED)
status = -ENOTCONN;
else
- hub->ports[port1 - 1]->child = 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);
- hub->ports[port1 - 1]->child = NULL;
+ port_dev->child = NULL;
spin_unlock_irq(&device_state_lock);
+ mutex_unlock(&usb_port_peer_mutex);
}
}
@@ -4540,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;
@@ -4558,56 +4733,200 @@ loop:
!hcd->driver->port_handed_over ||
!(hcd->driver->port_handed_over)(hcd, port1)) {
if (status != -ENOTCONN && status != -ENODEV)
- dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
- port1);
+ 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);
+
}
-/* 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)
+/* 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_device *hdev;
- struct usb_device *udev;
- int connect_change = 0;
- int ret;
+ struct usb_port *port_dev = hub->ports[port1 - 1];
+ struct usb_device *udev = port_dev->child;
+ int status = -ENODEV;
- hdev = hub->hdev;
- udev = hub->ports[port - 1]->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;
+ 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;
}
- if (udev) {
- /* TRSMRCY = 10 msec */
- msleep(10);
+#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);
- ret = usb_remote_wakeup(udev);
+ usb_reset_device(udev);
usb_unlock_device(udev);
- if (ret < 0)
- connect_change = 1;
- } else {
- ret = -ENODEV;
- hub_port_disable(hub, port, 1);
+ usb_lock_port(port_dev);
+ connect_change = 0;
}
- dev_dbg(hub->intfdev, "resume on port %d, status %d\n",
- port, ret);
- return connect_change;
+
+ if (connect_change)
+ hub_port_connect_change(hub, port1, portstatus, portchange);
}
+
static void hub_events(void)
{
struct list_head *tmp;
@@ -4617,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, wakeup_change;
/*
* We restart the list every time to avoid a deadlock with
@@ -4694,127 +5010,28 @@ static void hub_events(void)
/* deal with port status changes */
for (i = 1; i <= hdev->maxchild; i++) {
- if (test_bit(i, hub->busy_bits))
- continue;
- connect_change = test_bit(i, hub->change_bits);
- wakeup_change = test_and_clear_bit(i, hub->wakeup_bits);
- if (!test_and_clear_bit(i, hub->event_bits) &&
- !connect_change && !wakeup_change)
- continue;
-
- ret = hub_port_status(hub, i,
- &portstatus, &portchange);
- if (ret < 0)
- continue;
-
- if (portchange & USB_PORT_STAT_C_CONNECTION) {
- usb_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);
- usb_clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_ENABLE);
+ 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
- && hub->ports[i - 1]->child) {
- dev_err (hub_dev,
- "port %i "
- "disabled by hub (EMI?), "
- "re-enabling...\n",
- i);
- connect_change = 1;
- }
+ 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 (hub_handle_remote_wakeup(hub, i,
- portstatus, portchange))
- connect_change = 1;
-
- if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
- u16 status = 0;
- u16 unused;
-
- dev_dbg(hub_dev, "over-current change on port "
- "%d\n", i);
- usb_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);
- usb_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);
- usb_clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_BH_PORT_RESET);
- }
- if (portchange & USB_PORT_STAT_C_LINK_STATE) {
- usb_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);
- usb_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_port_warm_reset_required(hub, portstatus)) {
- int status;
- struct usb_device *udev =
- hub->ports[i - 1]->child;
-
- dev_dbg(hub_dev, "warm reset port %d\n", i);
- if (!udev || !(portstatus &
- USB_PORT_STAT_CONNECTION)) {
- status = hub_port_reset(hub, i,
- NULL, HUB_BH_RESET_TIME,
- true);
- if (status < 0)
- hub_port_disable(hub, i, 1);
- } else {
- usb_lock_device(udev);
- status = usb_reset_device(udev);
- usb_unlock_device(udev);
- connect_change = 0;
- }
- }
-
- 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)
@@ -4838,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 "
@@ -4858,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.
@@ -4883,7 +5100,7 @@ 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,
+ | USB_DEVICE_ID_MATCH_INT_CLASS,
.idVendor = USB_VENDOR_GENESYS_LOGIC,
.bInterfaceClass = USB_CLASS_HUB,
.driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},
@@ -5049,15 +5266,18 @@ static int descriptors_changed(struct usb_device *udev,
* if the reset wasn't even attempted.
*
* 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
- * the device using usb_lock_device_for_reset().
+ * 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)
{
@@ -5066,7 +5286,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
struct usb_device_descriptor descriptor = udev->descriptor;
struct usb_host_bos *bos;
- int i, ret = 0;
+ int i, j, ret = 0;
int port1 = udev->portnum;
if (udev->state == USB_STATE_NOTATTACHED ||
@@ -5076,13 +5296,17 @@ 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;
@@ -5103,7 +5327,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
goto re_enumerate;
}
- 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.
@@ -5113,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, 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)
@@ -5148,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);
@@ -5186,16 +5408,20 @@ 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);
@@ -5230,7 +5456,9 @@ 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) {
@@ -5239,6 +5467,14 @@ 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
@@ -5272,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) {
@@ -5287,10 +5525,11 @@ 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);
@@ -5366,56 +5605,26 @@ struct usb_device *usb_hub_find_child(struct usb_device *hdev,
}
EXPORT_SYMBOL_GPL(usb_hub_find_child);
-/**
- * usb_set_hub_port_connect_type - set hub port connect type.
- * @hdev: USB device belonging to the usb hub
- * @port1: port num of the port
- * @type: connect type of the port
- */
-void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1,
- enum usb_port_connect_type type)
-{
- struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
-
- if (hub)
- hub->ports[port1 - 1]->connect_type = type;
-}
-
-/**
- * usb_get_hub_port_connect_type - Get the port's connect type
- * @hdev: USB device belonging to the usb hub
- * @port1: port num of the port
- *
- * Return: The connect type of the port if successful. Or
- * USB_PORT_CONNECT_TYPE_UNKNOWN if input params are invalid.
- */
-enum usb_port_connect_type
-usb_get_hub_port_connect_type(struct usb_device *hdev, int port1)
-{
- struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
-
- if (!hub)
- return USB_PORT_CONNECT_TYPE_UNKNOWN;
-
- return hub->ports[port1 - 1]->connect_type;
-}
-
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++) {
- connect_type = usb_get_hub_port_connect_type(hdev, 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(&hdev->dev, "usb port%d's DeviceRemovable is changed to 1 according to platform information.\n",
- i);
+ dev_dbg(&port_dev->dev, "DeviceRemovable is changed to 1 according to platform information.\n");
desc->u.hs.DeviceRemovable[i/8] |= mask;
}
}
@@ -5424,14 +5633,14 @@ void usb_hub_adjust_deviceremovable(struct usb_device *hdev,
u16 port_removable = le16_to_cpu(desc->u.ss.DeviceRemovable);
for (i = 1; i <= hdev->maxchild; i++) {
- connect_type = usb_get_hub_port_connect_type(hdev, 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(&hdev->dev, "usb port%d's DeviceRemovable is changed to 1 according to platform information.\n",
- i);
+ dev_dbg(&port_dev->dev, "DeviceRemovable is changed to 1 according to platform information.\n");
port_removable |= mask;
}
}
@@ -5458,6 +5667,6 @@ acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev,
if (!hub)
return NULL;
- return DEVICE_ACPI_HANDLE(&hub->ports[port1 - 1]->dev);
+ return ACPI_HANDLE(&hub->ports[port1 - 1]->dev);
}
#endif
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 4e4790dea34..326308e5396 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -45,12 +45,13 @@ struct usb_hub {
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 */
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
@@ -66,6 +67,7 @@ struct usb_hub {
unsigned limited_power:1;
unsigned quiescing:1;
unsigned disconnected:1;
+ unsigned in_reset:1;
unsigned quirk_check_port_auto_suspend:1;
@@ -78,22 +80,28 @@ struct usb_hub {
/**
* struct usb port - kernel's representation of a usb port
- * @child: usb device attatched to the 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
- * @power_is_on: port's power state
- * @did_runtime_put: port has done pm_runtime_put().
+ * @is_superspeed cache super-speed status
*/
struct usb_port {
struct usb_device *child;
struct device dev;
- struct dev_state *port_owner;
+ 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 power_is_on:1;
- unsigned did_runtime_put:1;
+ unsigned int is_superspeed:1;
};
#define to_usb_port(_dev) \
@@ -111,6 +119,29 @@ extern int hub_port_debounce(struct usb_hub *hub, int port1,
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)
{
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 82927e1ed27..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>
@@ -179,7 +178,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg);
*
* Return:
* If successful, 0. Otherwise a negative error number. The number of actual
- * bytes transferred will be stored in the @actual_length paramater.
+ * 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)
@@ -218,7 +217,7 @@ EXPORT_SYMBOL_GPL(usb_interrupt_msg);
*
* Return:
* If successful, 0. Otherwise a negative error number. The number of actual
- * bytes transferred will be stored in the @actual_length paramater.
+ * bytes transferred will be stored in the @actual_length parameter.
*
*/
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
@@ -518,7 +517,7 @@ void usb_sg_wait(struct usb_sg_request *io)
io->urbs[i]->dev = io->dev;
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);
@@ -1182,8 +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);
@@ -1290,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;
@@ -1326,6 +1328,10 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
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",
@@ -1917,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);
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 51542f85239..fe1b6d0967e 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -21,6 +21,8 @@
#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,
@@ -66,6 +68,7 @@ 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);
}
@@ -76,17 +79,29 @@ static int usb_port_runtime_resume(struct device *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;
+ }
- usb_autopm_get_interface(intf);
- set_bit(port1, hub->busy_bits);
+ /*
+ * 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);
- if (port_dev->child && !retval) {
+ 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
@@ -97,12 +112,17 @@ static int usb_port_runtime_resume(struct device *dev)
if (retval < 0)
dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n",
retval);
- usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
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);
+ }
}
- clear_bit(port1, hub->busy_bits);
usb_autopm_put_interface(intf);
+
return retval;
}
@@ -112,23 +132,37 @@ static int usb_port_runtime_suspend(struct device *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);
- set_bit(port1, hub->busy_bits);
retval = usb_hub_set_port_power(hdev, hub, port1, false);
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
- usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
- clear_bit(port1, hub->busy_bits);
+ 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
@@ -146,51 +180,305 @@ struct device_type usb_port_device_type = {
.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 = NULL;
+ struct usb_port *port_dev;
int retval;
port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
- if (!port_dev) {
- retval = -ENOMEM;
- goto exit;
+ 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;
- port_dev->power_is_on = true;
+ 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;
- dev_set_name(&port_dev->dev, "port%d", port1);
-
+ 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)
- goto error_register;
+ 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);
- /* It would be dangerous if user space couldn't
- * prevent usb device from being powered off. So don't
- * enable port runtime pm if failed to expose port's pm qos.
+ /*
+ * Keep hidden the ability to enable port-poweroff if the hub
+ * does not support power switching.
*/
- if (!dev_pm_qos_expose_flags(&port_dev->dev,
- PM_QOS_FLAG_NO_POWER_OFF))
- pm_runtime_enable(&port_dev->dev);
+ 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;
+ }
- device_enable_async_suspend(&port_dev->dev);
+ /* 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;
-
-error_register:
- put_device(&port_dev->dev);
-exit:
- return retval;
}
-void usb_hub_remove_port_device(struct usb_hub *hub,
- int port1)
+void usb_hub_remove_port_device(struct usb_hub *hub, int port1)
{
- device_unregister(&hub->ports[port1 - 1]->dev);
-}
+ 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 5b44cd47da5..739ee8e8bdf 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -13,6 +13,7 @@
#include <linux/usb.h>
#include <linux/usb/quirks.h>
+#include <linux/usb/hcd.h>
#include "usb.h"
/* Lists of quirky USB devices, split in device quirks and interface quirks.
@@ -46,6 +47,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Microsoft LifeCam-VX700 v2.0 */
{ USB_DEVICE(0x045e, 0x0770), .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 Quickcam Fusion */
{ USB_DEVICE(0x046d, 0x08c1), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -130,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 },
@@ -155,6 +163,21 @@ static const struct usb_device_id usb_interface_quirk_list[] = {
{ } /* 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)
{
@@ -181,6 +204,18 @@ static bool usb_match_any_interface(struct usb_device *udev,
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)
{
@@ -206,6 +241,15 @@ static u32 __usb_detect_quirks(struct usb_device *udev,
void usb_detect_quirks(struct usb_device *udev)
{
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);
+
if (udev->quirks)
dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
udev->quirks);
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 6d2c8edb1ff..1236c6011c7 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -23,14 +23,16 @@ static ssize_t field##_show(struct device *dev, \
{ \
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, \
+ rc = sprintf(buf, format_string, \
actconfig->desc.field); \
- else \
- return 0; \
+ usb_unlock_device(udev); \
+ return rc; \
} \
#define usb_actconfig_attr(field, format_string) \
@@ -45,12 +47,15 @@ static ssize_t bMaxPower_show(struct device *dev,
{
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 0;
- return sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
+ if (actconfig)
+ rc = sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
+ usb_unlock_device(udev);
+ return rc;
}
static DEVICE_ATTR_RO(bMaxPower);
@@ -59,12 +64,15 @@ static ssize_t configuration_show(struct device *dev,
{
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 && actconfig->string)
+ rc = sprintf(buf, "%s\n", actconfig->string);
+ usb_unlock_device(udev);
+ return rc;
}
static DEVICE_ATTR_RO(configuration);
@@ -390,7 +398,8 @@ 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) {
@@ -449,7 +458,7 @@ static ssize_t usb2_hardware_lpm_show(struct device *dev,
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";
@@ -469,8 +478,10 @@ static ssize_t usb2_hardware_lpm_store(struct device *dev,
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);
@@ -644,7 +655,7 @@ static ssize_t 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_IGNORE_LOCKDEP(authorized, S_IRUGO | S_IWUSR,
authorized_show, authorized_store);
@@ -764,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) {
@@ -784,6 +796,7 @@ read_descriptors(struct file *filp, struct kobject *kobj,
off -= srclen;
}
}
+ usb_unlock_device(udev);
return count - nleft;
}
@@ -824,7 +837,7 @@ 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 \
iad_##field##_show(struct device *dev, struct device_attribute *attr, \
@@ -870,9 +883,7 @@ static ssize_t interface_show(struct device *dev, struct device_attribute *attr,
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);
@@ -888,7 +899,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
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%02Xin%02X\n",
@@ -909,23 +920,14 @@ 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_RO(supports_autosuspend);
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index c12bc790a6a..991386ceb4e 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -2,7 +2,6 @@
#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>
@@ -53,7 +52,7 @@ 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.
+ * 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.
@@ -138,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);
}
@@ -275,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
@@ -319,10 +324,14 @@ 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->complete)
return -EINVAL;
@@ -430,15 +439,10 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
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 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])
@@ -470,8 +474,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
if (allowed != urb->transfer_flags)
dev_WARN(&dev->dev, "BOGUS urb flags, %x --> %x\n",
urb->transfer_flags, allowed);
- }
-#endif
+
/*
* Force periodic transfer intervals to be legal values that are
* a power of two (so HCDs don't need to).
@@ -486,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;
@@ -828,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
@@ -846,6 +849,39 @@ 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
@@ -858,7 +894,8 @@ EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
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);
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index 255c14464bf..2776cfe64c0 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -16,9 +16,8 @@
#include <linux/acpi.h>
#include <linux/pci.h>
#include <linux/usb/hcd.h>
-#include <acpi/acpi_bus.h>
-#include "usb.h"
+#include "hub.h"
/**
* usb_acpi_power_manageable - check whether usb port has
@@ -56,13 +55,18 @@ EXPORT_SYMBOL_GPL(usb_acpi_power_manageable);
*/
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;
- port_handle = (acpi_handle)usb_get_hub_port_acpi_handle(hdev,
- port1);
+ 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;
@@ -73,65 +77,61 @@ int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable)
error = acpi_bus_set_power(port_handle, state);
if (!error)
- dev_dbg(&hdev->dev, "The power of hub port %d was set to %d\n",
- port1, enable);
+ dev_dbg(&port_dev->dev, "acpi: power was set to %d\n", enable);
else
- dev_dbg(&hdev->dev, "The power of hub port failed to be set\n");
+ dev_dbg(&port_dev->dev, "acpi: power failed to be set\n");
return error;
}
EXPORT_SYMBOL_GPL(usb_acpi_set_power_state);
-static int usb_acpi_check_port_connect_type(struct usb_device *hdev,
- acpi_handle handle, int port1)
+static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle,
+ struct acpi_pld_info *pld)
{
- acpi_status status;
+ enum usb_port_connect_type connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *upc;
- struct acpi_pld_info *pld;
- int ret = 0;
+ acpi_status status;
/*
- * Accoding to ACPI Spec 9.13. PLD indicates whether usb port is
+ * 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_get_physical_device_location(handle, &pld);
- if (ACPI_FAILURE(status))
- return -ENODEV;
-
status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
upc = buffer.pointer;
if (!upc || (upc->type != ACPI_TYPE_PACKAGE)
|| upc->package.count != 4) {
- ret = -EINVAL;
goto out;
}
if (upc->package.elements[0].integer.value)
if (pld->user_visible)
- usb_set_hub_port_connect_type(hdev, port1,
- USB_PORT_CONNECT_TYPE_HOT_PLUG);
+ connect_type = USB_PORT_CONNECT_TYPE_HOT_PLUG;
else
- usb_set_hub_port_connect_type(hdev, port1,
- USB_PORT_CONNECT_TYPE_HARD_WIRED);
+ connect_type = USB_PORT_CONNECT_TYPE_HARD_WIRED;
else if (!pld->user_visible)
- usb_set_hub_port_connect_type(hdev, port1, USB_PORT_NOT_USED);
-
+ connect_type = USB_PORT_NOT_USED;
out:
- ACPI_FREE(pld);
kfree(upc);
- return ret;
+ return connect_type;
}
-static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
+
+/*
+ * 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;
- int port_num;
/*
* In the ACPI DSDT table, only usb root hub and usb ports are
@@ -148,38 +148,19 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
*/
if (is_usb_device(dev)) {
udev = to_usb_device(dev);
- if (udev->parent) {
- enum usb_port_connect_type type;
-
- /*
- * According usb port's connect type to set usb device's
- * removability.
- */
- type = usb_get_hub_port_connect_type(udev->parent,
- udev->portnum);
- switch (type) {
- case USB_PORT_CONNECT_TYPE_HOT_PLUG:
- udev->removable = USB_DEVICE_REMOVABLE;
- break;
- case USB_PORT_CONNECT_TYPE_HARD_WIRED:
- udev->removable = USB_DEVICE_FIXED;
- break;
- default:
- udev->removable = USB_DEVICE_REMOVABLE_UNKNOWN;
- break;
- }
-
- return -ENODEV;
- }
+ if (udev->parent)
+ return NULL;
- /* root hub's parent is the usb hcd. */
- parent_handle = DEVICE_ACPI_HANDLE(dev->parent);
- *handle = acpi_get_child(parent_handle, udev->portnum);
- if (!*handle)
- return -ENODEV;
- return 0;
+ /* 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)) {
- sscanf(dev_name(dev), "port%d", &port_num);
+ 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);
@@ -190,30 +171,39 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
*/
if (!udev->parent) {
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
- int raw_port_num;
-
- raw_port_num = usb_hcd_find_raw_port_number(hcd,
- port_num);
- *handle = acpi_get_child(DEVICE_ACPI_HANDLE(&udev->dev),
- raw_port_num);
- if (!*handle)
- return -ENODEV;
+ 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 -ENODEV;
+ return NULL;
- *handle = acpi_get_child(parent_handle, port_num);
- if (!*handle)
- return -ENODEV;
+ acpi_bus_get_device(parent_handle, &adev);
+ adev = acpi_find_child_device(adev, port1, false);
+ if (!adev)
+ return NULL;
}
- usb_acpi_check_port_connect_type(udev, *handle, port_num);
- } else
- return -ENODEV;
+ 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 0;
+ return NULL;
}
static bool usb_acpi_bus_match(struct device *dev)
@@ -224,7 +214,7 @@ static bool usb_acpi_bus_match(struct device *dev)
static struct acpi_bus_type usb_acpi_bus = {
.name = "USB",
.match = usb_acpi_bus_match,
- .find_device = usb_acpi_find_device,
+ .find_companion = usb_acpi_find_companion,
};
int usb_acpi_register(void)
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 0a6ee2e70b2..4d1144990d4 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -497,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;
}
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 823857767a1..d9d08720c38 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -2,7 +2,7 @@
#include <linux/acpi.h>
struct usb_hub_descriptor;
-struct dev_state;
+struct usb_dev_state;
/* Functions local to drivers/usb/core/ */
@@ -55,14 +55,10 @@ extern int usb_match_one_id_intf(struct usb_device *dev,
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,
- struct dev_state *owner);
-extern int usb_hub_release_port(struct usb_device *hdev, unsigned port,
- struct dev_state *owner);
extern void usb_hub_release_all_ports(struct usb_device *hdev,
- struct dev_state *owner);
+ struct usb_dev_state *owner);
extern bool usb_device_is_owned(struct usb_device *udev);
extern int usb_hub_init(void);
@@ -111,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;
@@ -123,6 +114,7 @@ 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;
@@ -174,15 +166,17 @@ 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 enum usb_port_connect_type
- usb_get_hub_port_connect_type(struct usb_device *hdev, int port1);
-extern void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1,
- enum usb_port_connect_type type);
extern void usb_hub_adjust_deviceremovable(struct usb_device *hdev,
struct usb_hub_descriptor *desc);