summaryrefslogtreecommitdiff
path: root/drivers/usb/core
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/config.c111
-rw-r--r--drivers/usb/core/devices.c4
-rw-r--r--drivers/usb/core/devio.c57
-rw-r--r--drivers/usb/core/driver.c16
-rw-r--r--drivers/usb/core/endpoint.c2
-rw-r--r--drivers/usb/core/hcd-pci.c2
-rw-r--r--drivers/usb/core/hcd.c27
-rw-r--r--drivers/usb/core/hub.c311
-rw-r--r--drivers/usb/core/message.c2
-rw-r--r--drivers/usb/core/quirks.c24
-rw-r--r--drivers/usb/core/sysfs.c59
-rw-r--r--drivers/usb/core/urb.c2
-rw-r--r--drivers/usb/core/usb.c1
-rw-r--r--drivers/usb/core/usb.h7
14 files changed, 460 insertions, 165 deletions
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 26678ca..f4bdd0c 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -124,9 +124,9 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
if (usb_endpoint_xfer_isoc(&ep->desc))
max_tx = (desc->bMaxBurst + 1) * (desc->bmAttributes + 1) *
- le16_to_cpu(ep->desc.wMaxPacketSize);
+ usb_endpoint_maxp(&ep->desc);
else if (usb_endpoint_xfer_int(&ep->desc))
- max_tx = le16_to_cpu(ep->desc.wMaxPacketSize) *
+ max_tx = usb_endpoint_maxp(&ep->desc) *
(desc->bMaxBurst + 1);
else
max_tx = 999999;
@@ -241,7 +241,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
cfgno, inum, asnum, d->bEndpointAddress);
endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT;
endpoint->desc.bInterval = 1;
- if (le16_to_cpu(endpoint->desc.wMaxPacketSize) > 8)
+ if (usb_endpoint_maxp(&endpoint->desc) > 8)
endpoint->desc.wMaxPacketSize = cpu_to_le16(8);
}
@@ -254,7 +254,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
&& usb_endpoint_xfer_bulk(d)) {
unsigned maxp;
- maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize) & 0x07ff;
+ maxp = usb_endpoint_maxp(&endpoint->desc) & 0x07ff;
if (maxp != 512)
dev_warn(ddev, "config %d interface %d altsetting %d "
"bulk endpoint 0x%X has invalid maxpacket %d\n",
@@ -755,3 +755,106 @@ err2:
dev_err(ddev, "out of memory\n");
return result;
}
+
+void usb_release_bos_descriptor(struct usb_device *dev)
+{
+ if (dev->bos) {
+ kfree(dev->bos->desc);
+ kfree(dev->bos);
+ dev->bos = NULL;
+ }
+}
+
+/* Get BOS descriptor set */
+int usb_get_bos_descriptor(struct usb_device *dev)
+{
+ struct device *ddev = &dev->dev;
+ struct usb_bos_descriptor *bos;
+ struct usb_dev_cap_header *cap;
+ unsigned char *buffer;
+ int length, total_len, num, i;
+ int ret;
+
+ bos = kzalloc(sizeof(struct usb_bos_descriptor), GFP_KERNEL);
+ if (!bos)
+ return -ENOMEM;
+
+ /* Get BOS descriptor */
+ ret = usb_get_descriptor(dev, USB_DT_BOS, 0, bos, USB_DT_BOS_SIZE);
+ if (ret < USB_DT_BOS_SIZE) {
+ dev_err(ddev, "unable to get BOS descriptor\n");
+ if (ret >= 0)
+ ret = -ENOMSG;
+ kfree(bos);
+ return ret;
+ }
+
+ length = bos->bLength;
+ total_len = le16_to_cpu(bos->wTotalLength);
+ num = bos->bNumDeviceCaps;
+ kfree(bos);
+ if (total_len < length)
+ return -EINVAL;
+
+ dev->bos = kzalloc(sizeof(struct usb_host_bos), GFP_KERNEL);
+ if (!dev->bos)
+ return -ENOMEM;
+
+ /* Now let's get the whole BOS descriptor set */
+ buffer = kzalloc(total_len, GFP_KERNEL);
+ if (!buffer) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev->bos->desc = (struct usb_bos_descriptor *)buffer;
+
+ ret = usb_get_descriptor(dev, USB_DT_BOS, 0, buffer, total_len);
+ if (ret < total_len) {
+ dev_err(ddev, "unable to get BOS descriptor set\n");
+ if (ret >= 0)
+ ret = -ENOMSG;
+ goto err;
+ }
+ total_len -= length;
+
+ for (i = 0; i < num; i++) {
+ buffer += length;
+ cap = (struct usb_dev_cap_header *)buffer;
+ length = cap->bLength;
+
+ if (total_len < length)
+ break;
+ total_len -= length;
+
+ if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) {
+ dev_warn(ddev, "descriptor type invalid, skip\n");
+ continue;
+ }
+
+ switch (cap->bDevCapabilityType) {
+ case USB_CAP_TYPE_WIRELESS_USB:
+ /* Wireless USB cap descriptor is handled by wusb */
+ break;
+ case USB_CAP_TYPE_EXT:
+ dev->bos->ext_cap =
+ (struct usb_ext_cap_descriptor *)buffer;
+ break;
+ case USB_SS_CAP_TYPE:
+ dev->bos->ss_cap =
+ (struct usb_ss_cap_descriptor *)buffer;
+ break;
+ case CONTAINER_ID_TYPE:
+ dev->bos->ss_id =
+ (struct usb_ss_container_id_descriptor *)buffer;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+
+err:
+ usb_release_bos_descriptor(dev);
+ return ret;
+}
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index 0149c09..d956965 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -190,7 +190,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
dir = usb_endpoint_dir_in(desc) ? 'I' : 'O';
if (speed == USB_SPEED_HIGH) {
- switch (le16_to_cpu(desc->wMaxPacketSize) & (0x03 << 11)) {
+ switch (usb_endpoint_maxp(desc) & (0x03 << 11)) {
case 1 << 11:
bandwidth = 2; break;
case 2 << 11:
@@ -240,7 +240,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end,
start += sprintf(start, format_endpt, desc->bEndpointAddress, dir,
desc->bmAttributes, type,
- (le16_to_cpu(desc->wMaxPacketSize) & 0x07ff) *
+ (usb_endpoint_maxp(desc) & 0x07ff) *
bandwidth,
interval, unit);
return start;
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 37518df..e3beaf2 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -46,6 +46,7 @@
#include <linux/cdev.h>
#include <linux/notifier.h>
#include <linux/security.h>
+#include <linux/user_namespace.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
#include <linux/moduleparam.h>
@@ -68,7 +69,7 @@ struct dev_state {
wait_queue_head_t wait; /* wake up if a request completed */
unsigned int discsignr;
struct pid *disc_pid;
- uid_t disc_uid, disc_euid;
+ const struct cred *cred;
void __user *disccontext;
unsigned long ifclaimed;
u32 secid;
@@ -79,7 +80,7 @@ struct async {
struct list_head asynclist;
struct dev_state *ps;
struct pid *pid;
- uid_t uid, euid;
+ const struct cred *cred;
unsigned int signr;
unsigned int ifnum;
void __user *userbuffer;
@@ -248,6 +249,7 @@ static struct async *alloc_async(unsigned int numisoframes)
static void free_async(struct async *as)
{
put_pid(as->pid);
+ put_cred(as->cred);
kfree(as->urb->transfer_buffer);
kfree(as->urb->setup_packet);
usb_free_urb(as->urb);
@@ -393,9 +395,8 @@ static void async_completed(struct urb *urb)
struct dev_state *ps = as->ps;
struct siginfo sinfo;
struct pid *pid = NULL;
- uid_t uid = 0;
- uid_t euid = 0;
u32 secid = 0;
+ const struct cred *cred = NULL;
int signr;
spin_lock(&ps->lock);
@@ -407,9 +408,8 @@ static void async_completed(struct urb *urb)
sinfo.si_errno = as->status;
sinfo.si_code = SI_ASYNCIO;
sinfo.si_addr = as->userurb;
- pid = as->pid;
- uid = as->uid;
- euid = as->euid;
+ pid = get_pid(as->pid);
+ cred = get_cred(as->cred);
secid = as->secid;
}
snoop(&urb->dev->dev, "urb complete\n");
@@ -422,9 +422,11 @@ static void async_completed(struct urb *urb)
cancel_bulk_urbs(ps, as->bulk_addr);
spin_unlock(&ps->lock);
- if (signr)
- kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid,
- euid, secid);
+ if (signr) {
+ kill_pid_info_as_cred(sinfo.si_signo, &sinfo, pid, cred, secid);
+ put_pid(pid);
+ put_cred(cred);
+ }
wake_up(&ps->wait);
}
@@ -607,9 +609,10 @@ static int findintfep(struct usb_device *dev, unsigned int ep)
}
static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,
- unsigned int index)
+ unsigned int request, unsigned int index)
{
int ret = 0;
+ struct usb_host_interface *alt_setting;
if (ps->dev->state != USB_STATE_UNAUTHENTICATED
&& ps->dev->state != USB_STATE_ADDRESS
@@ -618,6 +621,19 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,
if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype))
return 0;
+ /*
+ * check for the special corner case 'get_device_id' in the printer
+ * class specification, where wIndex is (interface << 8 | altsetting)
+ * instead of just interface
+ */
+ if (requesttype == 0xa1 && request == 0) {
+ alt_setting = usb_find_alt_setting(ps->dev->actconfig,
+ index >> 8, index & 0xff);
+ if (alt_setting
+ && alt_setting->desc.bInterfaceClass == USB_CLASS_PRINTER)
+ index >>= 8;
+ }
+
index &= 0xff;
switch (requesttype & USB_RECIP_MASK) {
case USB_RECIP_ENDPOINT:
@@ -656,7 +672,6 @@ static int usbdev_open(struct inode *inode, struct file *file)
{
struct usb_device *dev = NULL;
struct dev_state *ps;
- const struct cred *cred = current_cred();
int ret;
ret = -ENOMEM;
@@ -706,8 +721,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
init_waitqueue_head(&ps->wait);
ps->discsignr = 0;
ps->disc_pid = get_pid(task_pid(current));
- ps->disc_uid = cred->uid;
- ps->disc_euid = cred->euid;
+ ps->cred = get_current_cred();
ps->disccontext = NULL;
ps->ifclaimed = 0;
security_task_getsecid(current, &ps->secid);
@@ -749,6 +763,7 @@ static int usbdev_release(struct inode *inode, struct file *file)
usb_unlock_device(dev);
usb_put_dev(dev);
put_pid(ps->disc_pid);
+ put_cred(ps->cred);
as = async_getcompleted(ps);
while (as) {
@@ -770,7 +785,8 @@ static int proc_control(struct dev_state *ps, void __user *arg)
if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
return -EFAULT;
- ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.wIndex);
+ ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.bRequest,
+ ctrl.wIndex);
if (ret)
return ret;
wLength = ctrl.wLength; /* To suppress 64k PAGE_SIZE warning */
@@ -1048,7 +1064,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
struct usb_host_endpoint *ep;
struct async *as;
struct usb_ctrlrequest *dr = NULL;
- const struct cred *cred = current_cred();
unsigned int u, totlen, isofrmlen;
int ret, ifnum = -1;
int is_in;
@@ -1100,7 +1115,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
kfree(dr);
return -EINVAL;
}
- ret = check_ctrlrecip(ps, dr->bRequestType,
+ ret = check_ctrlrecip(ps, dr->bRequestType, dr->bRequest,
le16_to_cpup(&dr->wIndex));
if (ret) {
kfree(dr);
@@ -1262,8 +1277,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
as->signr = uurb->signr;
as->ifnum = ifnum;
as->pid = get_pid(task_pid(current));
- as->uid = cred->uid;
- as->euid = cred->euid;
+ as->cred = get_current_cred();
security_task_getsecid(current, &as->secid);
if (!is_in && uurb->buffer_length > 0) {
if (copy_from_user(as->urb->transfer_buffer, uurb->buffer,
@@ -1981,9 +1995,8 @@ static void usbdev_remove(struct usb_device *udev)
sinfo.si_errno = EPIPE;
sinfo.si_code = SI_ASYNCIO;
sinfo.si_addr = ps->disccontext;
- kill_pid_info_as_uid(ps->discsignr, &sinfo,
- ps->disc_pid, ps->disc_uid,
- ps->disc_euid, ps->secid);
+ kill_pid_info_as_cred(ps->discsignr, &sinfo,
+ ps->disc_pid, ps->cred, ps->secid);
}
}
}
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 34e3da5..adf5ca8 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1583,7 +1583,7 @@ int usb_autopm_get_interface_async(struct usb_interface *intf)
dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n",
__func__, atomic_read(&intf->dev.power.usage_count),
status);
- if (status > 0)
+ if (status > 0 || status == -EINPROGRESS)
status = 0;
return status;
}
@@ -1700,6 +1700,20 @@ int usb_runtime_idle(struct device *dev)
return 0;
}
+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 (hcd->driver->set_usb2_hw_lpm) {
+ ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable);
+ if (!ret)
+ udev->usb2_hw_lpm_enabled = enable;
+ }
+
+ return ret;
+}
+
#endif /* CONFIG_USB_SUSPEND */
struct bus_type usb_bus_type = {
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index df502a9..db7fe50 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -56,7 +56,7 @@ static ssize_t show_ep_wMaxPacketSize(struct device *dev,
{
struct ep_device *ep = to_ep_device(dev);
return sprintf(buf, "%04x\n",
- le16_to_cpu(ep->desc->wMaxPacketSize) & 0x07ff);
+ usb_endpoint_maxp(ep->desc) & 0x07ff);
}
static DEVICE_ATTR(wMaxPacketSize, S_IRUGO, show_ep_wMaxPacketSize, NULL);
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index ce22f4a..a004db3 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -242,7 +242,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
pci_set_master(dev);
- retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED);
+ retval = usb_add_hcd(hcd, dev->irq, IRQF_SHARED);
if (retval != 0)
goto unmap_registers;
set_hs_companion(dev, hcd);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 73cbbd8..b3b7d06 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -442,7 +442,11 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
struct usb_ctrlrequest *cmd;
u16 typeReq, wValue, wIndex, wLength;
u8 *ubuf = urb->transfer_buffer;
- u8 tbuf [sizeof (struct usb_hub_descriptor)]
+ /*
+ * tbuf should be as big as the BOS descriptor and
+ * the USB hub descriptor.
+ */
+ u8 tbuf[USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE]
__attribute__((aligned(4)));
const u8 *bufp = tbuf;
unsigned len = 0;
@@ -562,6 +566,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
else /* unsupported IDs --> "protocol stall" */
goto error;
break;
+ case USB_DT_BOS << 8:
+ goto nongeneric;
default:
goto error;
}
@@ -596,6 +602,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
/* CLASS REQUESTS (and errors) */
default:
+nongeneric:
/* non-generic request */
switch (typeReq) {
case GetHubStatus:
@@ -605,6 +612,9 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
case GetHubDescriptor:
len = sizeof (struct usb_hub_descriptor);
break;
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ /* len is returned by hub_control */
+ break;
}
status = hcd->driver->hub_control (hcd,
typeReq, wValue, wIndex,
@@ -615,7 +625,7 @@ error:
status = -EPIPE;
}
- if (status) {
+ if (status < 0) {
len = 0;
if (status != -EPIPE) {
dev_dbg (hcd->self.controller,
@@ -624,6 +634,10 @@ error:
typeReq, wValue, wIndex,
wLength, status);
}
+ } else if (status > 0) {
+ /* hub_control may return the length of data copied. */
+ len = status;
+ status = 0;
}
if (len) {
if (urb->transfer_buffer_length < len)
@@ -2429,7 +2443,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
* but drivers can override it in reset() if needed, along with
* recording the overall controller's system wakeup capability.
*/
- device_init_wakeup(&rhdev->dev, 1);
+ device_set_wakeup_capable(&rhdev->dev, 1);
/* HCD_FLAG_RH_RUNNING doesn't matter until the root hub is
* registered. But since the controller can die at any time,
@@ -2478,6 +2492,13 @@ 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:
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index a428aa0..d6cc832 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1636,11 +1636,6 @@ void usb_disconnect(struct usb_device **pdev)
int i;
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
- if (!udev) {
- pr_debug ("%s nodev\n", __func__);
- return;
- }
-
/* mark the device as inactive, so any further urb submissions for
* this device (and any of its children) will fail immediately.
* this quiesces everything except pending urbs.
@@ -2030,11 +2025,23 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
#define HUB_ROOT_RESET_TIME 50 /* times are in msec */
#define HUB_SHORT_RESET_TIME 10
+#define HUB_BH_RESET_TIME 50
#define HUB_LONG_RESET_TIME 200
#define HUB_RESET_TIMEOUT 500
+static int hub_port_reset(struct usb_hub *hub, int port1,
+ struct usb_device *udev, unsigned int delay, bool warm);
+
+/* Is a USB 3.0 port in the Inactive state? */
+static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus)
+{
+ return hub_is_superspeed(hub->hdev) &&
+ (portstatus & USB_PORT_STAT_LINK_STATE) ==
+ USB_SS_PORT_LS_SS_INACTIVE;
+}
+
static int hub_port_wait_reset(struct usb_hub *hub, int port1,
- struct usb_device *udev, unsigned int delay)
+ struct usb_device *udev, unsigned int delay, bool warm)
{
int delay_time, ret;
u16 portstatus;
@@ -2051,28 +2058,71 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
if (ret < 0)
return ret;
- /* Device went away? */
- if (!(portstatus & USB_PORT_STAT_CONNECTION))
- return -ENOTCONN;
-
- /* bomb out completely if the connection bounced */
- if ((portchange & USB_PORT_STAT_C_CONNECTION))
- return -ENOTCONN;
-
- /* if we`ve finished resetting, then break out of the loop */
- if (!(portstatus & USB_PORT_STAT_RESET) &&
- (portstatus & USB_PORT_STAT_ENABLE)) {
- if (hub_is_wusb(hub))
- udev->speed = USB_SPEED_WIRELESS;
- else if (hub_is_superspeed(hub->hdev))
- udev->speed = USB_SPEED_SUPER;
- else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
- udev->speed = USB_SPEED_HIGH;
- else if (portstatus & USB_PORT_STAT_LOW_SPEED)
- udev->speed = USB_SPEED_LOW;
- else
- udev->speed = USB_SPEED_FULL;
- return 0;
+ /*
+ * Some buggy devices require a warm reset to be issued even
+ * when the port appears not to be connected.
+ */
+ if (!warm) {
+ /*
+ * Some buggy devices can cause an NEC host controller
+ * to transition to the "Error" state after a hot port
+ * reset. This will show up as the port state in
+ * "Inactive", and the port may also report a
+ * disconnect. Forcing a warm port reset seems to make
+ * the device work.
+ *
+ * See https://bugzilla.kernel.org/show_bug.cgi?id=41752
+ */
+ if (hub_port_inactive(hub, portstatus)) {
+ int ret;
+
+ if ((portchange & USB_PORT_STAT_C_CONNECTION))
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_CONNECTION);
+ if (portchange & USB_PORT_STAT_C_LINK_STATE)
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_PORT_LINK_STATE);
+ if (portchange & USB_PORT_STAT_C_RESET)
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_RESET);
+ dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
+ port1);
+ ret = hub_port_reset(hub, port1,
+ udev, HUB_BH_RESET_TIME,
+ true);
+ if ((portchange & USB_PORT_STAT_C_CONNECTION))
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_CONNECTION);
+ return ret;
+ }
+ /* Device went away? */
+ if (!(portstatus & USB_PORT_STAT_CONNECTION))
+ return -ENOTCONN;
+
+ /* bomb out completely if the connection bounced */
+ if ((portchange & USB_PORT_STAT_C_CONNECTION))
+ return -ENOTCONN;
+
+ /* if we`ve finished resetting, then break out of
+ * the loop
+ */
+ if (!(portstatus & USB_PORT_STAT_RESET) &&
+ (portstatus & USB_PORT_STAT_ENABLE)) {
+ if (hub_is_wusb(hub))
+ udev->speed = USB_SPEED_WIRELESS;
+ else if (hub_is_superspeed(hub->hdev))
+ udev->speed = USB_SPEED_SUPER;
+ else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
+ udev->speed = USB_SPEED_HIGH;
+ else if (portstatus & USB_PORT_STAT_LOW_SPEED)
+ udev->speed = USB_SPEED_LOW;
+ else
+ udev->speed = USB_SPEED_FULL;
+ return 0;
+ }
+ } else {
+ if (portchange & USB_PORT_STAT_C_BH_RESET)
+ return 0;
}
/* switch to the long delay after two short delay failures */
@@ -2080,35 +2130,84 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
delay = HUB_LONG_RESET_TIME;
dev_dbg (hub->intfdev,
- "port %d not reset yet, waiting %dms\n",
- port1, delay);
+ "port %d not %sreset yet, waiting %dms\n",
+ port1, warm ? "warm " : "", delay);
}
return -EBUSY;
}
+static void hub_port_finish_reset(struct usb_hub *hub, int port1,
+ struct usb_device *udev, int *status, bool warm)
+{
+ switch (*status) {
+ case 0:
+ if (!warm) {
+ struct usb_hcd *hcd;
+ /* TRSTRCY = 10 ms; plus some extra */
+ msleep(10 + 40);
+ update_devnum(udev, 0);
+ hcd = bus_to_hcd(udev->bus);
+ if (hcd->driver->reset_device) {
+ *status = hcd->driver->reset_device(hcd, udev);
+ if (*status < 0) {
+ dev_err(&udev->dev, "Cannot reset "
+ "HCD device state\n");
+ break;
+ }
+ }
+ }
+ /* FALL THROUGH */
+ case -ENOTCONN:
+ case -ENODEV:
+ clear_port_feature(hub->hdev,
+ port1, USB_PORT_FEAT_C_RESET);
+ /* FIXME need disconnect() for NOTATTACHED device */
+ if (warm) {
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_BH_PORT_RESET);
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_PORT_LINK_STATE);
+ } else {
+ usb_set_device_state(udev, *status
+ ? USB_STATE_NOTATTACHED
+ : USB_STATE_DEFAULT);
+ }
+ break;
+ }
+}
+
+/* Handle port reset and port warm(BH) reset (for USB3 protocol ports) */
static int hub_port_reset(struct usb_hub *hub, int port1,
- struct usb_device *udev, unsigned int delay)
+ struct usb_device *udev, unsigned int delay, bool warm)
{
int i, status;
- struct usb_hcd *hcd;
- hcd = bus_to_hcd(udev->bus);
- /* Block EHCI CF initialization during the port reset.
- * Some companion controllers don't like it when they mix.
- */
- down_read(&ehci_cf_port_reset_rwsem);
+ if (!warm) {
+ /* Block EHCI CF initialization during the port reset.
+ * Some companion controllers don't like it when they mix.
+ */
+ down_read(&ehci_cf_port_reset_rwsem);
+ } else {
+ if (!hub_is_superspeed(hub->hdev)) {
+ dev_err(hub->intfdev, "only USB3 hub support "
+ "warm reset\n");
+ return -EINVAL;
+ }
+ }
/* Reset the port */
for (i = 0; i < PORT_RESET_TRIES; i++) {
- status = set_port_feature(hub->hdev,
- port1, USB_PORT_FEAT_RESET);
- if (status)
+ status = set_port_feature(hub->hdev, port1, (warm ?
+ USB_PORT_FEAT_BH_PORT_RESET :
+ USB_PORT_FEAT_RESET));
+ if (status) {
dev_err(hub->intfdev,
- "cannot reset port %d (err = %d)\n",
- port1, status);
- else {
- status = hub_port_wait_reset(hub, port1, udev, delay);
+ "cannot %sreset port %d (err = %d)\n",
+ warm ? "warm " : "", port1, status);
+ } else {
+ status = hub_port_wait_reset(hub, port1, udev, delay,
+ warm);
if (status && status != -ENOTCONN)
dev_dbg(hub->intfdev,
"port_wait_reset: err = %d\n",
@@ -2116,34 +2215,14 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
}
/* return on disconnect or reset */
- switch (status) {
- case 0:
- /* TRSTRCY = 10 ms; plus some extra */
- msleep(10 + 40);
- update_devnum(udev, 0);
- if (hcd->driver->reset_device) {
- status = hcd->driver->reset_device(hcd, udev);
- if (status < 0) {
- dev_err(&udev->dev, "Cannot reset "
- "HCD device state\n");
- break;
- }
- }
- /* FALL THROUGH */
- case -ENOTCONN:
- case -ENODEV:
- clear_port_feature(hub->hdev,
- port1, USB_PORT_FEAT_C_RESET);
- /* FIXME need disconnect() for NOTATTACHED device */
- usb_set_device_state(udev, status
- ? USB_STATE_NOTATTACHED
- : USB_STATE_DEFAULT);
+ if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
+ hub_port_finish_reset(hub, port1, udev, &status, warm);
goto done;
}
dev_dbg (hub->intfdev,
- "port %d not enabled, trying reset again...\n",
- port1);
+ "port %d not enabled, trying %sreset again...\n",
+ port1, warm ? "warm " : "");
delay = HUB_LONG_RESET_TIME;
}
@@ -2151,45 +2230,11 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
"Cannot enable port %i. Maybe the USB cable is bad?\n",
port1);
- done:
- up_read(&ehci_cf_port_reset_rwsem);
- return status;
-}
-
-/* Warm reset a USB3 protocol port */
-static int hub_port_warm_reset(struct usb_hub *hub, int port)
-{
- int ret;
- u16 portstatus, portchange;
-
- if (!hub_is_superspeed(hub->hdev)) {
- dev_err(hub->intfdev, "only USB3 hub support warm reset\n");
- return -EINVAL;
- }
-
- /* Warm reset the port */
- ret = set_port_feature(hub->hdev,
- port, USB_PORT_FEAT_BH_PORT_RESET);
- if (ret) {
- dev_err(hub->intfdev, "cannot warm reset port %d\n", port);
- return ret;
- }
-
- msleep(20);
- ret = hub_port_status(hub, port, &portstatus, &portchange);
-
- if (portchange & USB_PORT_STAT_C_RESET)
- clear_port_feature(hub->hdev, port, USB_PORT_FEAT_C_RESET);
-
- if (portchange & USB_PORT_STAT_C_BH_RESET)
- clear_port_feature(hub->hdev, port,
- USB_PORT_FEAT_C_BH_PORT_RESET);
-
- if (portchange & USB_PORT_STAT_C_LINK_STATE)
- clear_port_feature(hub->hdev, port,
- USB_PORT_FEAT_C_PORT_LINK_STATE);
+done:
+ if (!warm)
+ up_read(&ehci_cf_port_reset_rwsem);
- return ret;
+ return status;
}
/* Check if a port is power on */
@@ -2347,6 +2392,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
}
}
+ /* disable USB2 hardware LPM */
+ if (udev->usb2_hw_lpm_enabled == 1)
+ usb_set_usb2_hardware_lpm(udev, 0);
+
/* see 7.1.7.6 */
if (hub_is_superspeed(hub->hdev))
status = set_port_feature(hub->hdev,
@@ -2558,7 +2607,12 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
if (status < 0) {
dev_dbg(&udev->dev, "can't resume, status %d\n", status);
hub_port_logical_disconnect(hub, port1);
+ } else {
+ /* Try to enable USB2 hardware LPM */
+ if (udev->usb2_hw_lpm_capable == 1)
+ usb_set_usb2_hardware_lpm(udev, 1);
}
+
return status;
}
@@ -2798,7 +2852,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
int i, j, retval;
unsigned delay = HUB_SHORT_RESET_TIME;
enum usb_device_speed oldspeed = udev->speed;
- char *speed, *type;
+ const char *speed;
int devnum = udev->devnum;
/* root hub ports have a slightly longer reset period
@@ -2819,7 +2873,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
/* Reset the device; full speed may morph to high speed */
/* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
- retval = hub_port_reset(hub, port1, udev, delay);
+ retval = hub_port_reset(hub, port1, udev, delay, false);
if (retval < 0) /* error or disconnect */
goto fail;
/* success, speed is known */
@@ -2858,25 +2912,16 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
default:
goto fail;
}
-
- type = "";
- switch (udev->speed) {
- case USB_SPEED_LOW: speed = "low"; break;
- case USB_SPEED_FULL: speed = "full"; break;
- case USB_SPEED_HIGH: speed = "high"; break;
- case USB_SPEED_SUPER:
- speed = "super";
- break;
- case USB_SPEED_WIRELESS:
- speed = "variable";
- type = "Wireless ";
- break;
- default: speed = "?"; break;
- }
+
+ if (udev->speed == USB_SPEED_WIRELESS)
+ speed = "variable speed Wireless";
+ else
+ speed = usb_speed_string(udev->speed);
+
if (udev->speed != USB_SPEED_SUPER)
dev_info(&udev->dev,
- "%s %s speed %sUSB device number %d using %s\n",
- (udev->config) ? "reset" : "new", speed, type,
+ "%s %s USB device number %d using %s\n",
+ (udev->config) ? "reset" : "new", speed,
devnum, udev->bus->controller->driver->name);
/* Set up TT records, if needed */
@@ -2949,7 +2994,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
buf->bMaxPacketSize0;
kfree(buf);
- retval = hub_port_reset(hub, port1, udev, delay);
+ retval = hub_port_reset(hub, port1, udev, delay, false);
if (retval < 0) /* error or disconnect */
goto fail;
if (oldspeed != udev->speed) {
@@ -3023,7 +3068,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
i = 512;
else
i = udev->descriptor.bMaxPacketSize0;
- if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
+ if (usb_endpoint_maxp(&udev->ep0.desc) != i) {
if (udev->speed == USB_SPEED_LOW ||
!(i == 8 || i == 16 || i == 32 || i == 64)) {
dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i);
@@ -3047,6 +3092,15 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
goto fail;
}
+ if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
+ retval = usb_get_bos_descriptor(udev);
+ if (!retval) {
+ if (udev->bos->ext_cap && (USB_LPM_SUPPORT &
+ le32_to_cpu(udev->bos->ext_cap->bmAttributes)))
+ udev->lpm_capable = 1;
+ }
+ }
+
retval = 0;
/* notify HCD that we have a device connected and addressed */
if (hcd->driver->update_device)
@@ -3570,7 +3624,8 @@ static void hub_events(void)
(portstatus & USB_PORT_STAT_LINK_STATE)
== USB_SS_PORT_LS_SS_INACTIVE) {
dev_dbg(hub_dev, "warm reset port %d\n", i);
- hub_port_warm_reset(hub, i);
+ hub_port_reset(hub, i, NULL,
+ HUB_BH_RESET_TIME, true);
}
if (connect_change)
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 0b5ec23..b3bdfed 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -435,7 +435,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
len = sg->length;
if (length) {
- len = min_t(unsigned, len, length);
+ len = min_t(size_t, len, length);
length -= len;
if (length == 0)
io->entries = i + 1;
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 81ce6a8..d6a8d82 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -38,6 +38,27 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Creative SB Audigy 2 NX */
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Logitech Webcam C200 */
+ { USB_DEVICE(0x046d, 0x0802), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Logitech Webcam C250 */
+ { USB_DEVICE(0x046d, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Logitech Webcam C300 */
+ { USB_DEVICE(0x046d, 0x0805), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Logitech Webcam B/C500 */
+ { USB_DEVICE(0x046d, 0x0807), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Logitech Webcam Pro 9000 */
+ { USB_DEVICE(0x046d, 0x0809), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Logitech Webcam C310 */
+ { USB_DEVICE(0x046d, 0x081b), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Logitech Webcam C270 */
+ { USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* Logitech Harmony 700-series */
{ USB_DEVICE(0x046d, 0xc122), .driver_info = USB_QUIRK_DELAY_INIT },
@@ -69,6 +90,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x06a3, 0x0006), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
+ /* Guillemot Webcam Hercules Dualpix Exchange*/
+ { USB_DEVICE(0x06f8, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* M-Systems Flash Disk Pioneers */
{ USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME },
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index cf05b97..662c0cf 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -412,6 +412,56 @@ set_level(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level);
+static ssize_t
+show_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_device *udev = to_usb_device(dev);
+ const char *p;
+
+ if (udev->usb2_hw_lpm_enabled == 1)
+ p = "enabled";
+ else
+ p = "disabled";
+
+ return sprintf(buf, "%s\n", p);
+}
+
+static ssize_t
+set_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_device *udev = to_usb_device(dev);
+ bool value;
+ int ret;
+
+ usb_lock_device(udev);
+
+ ret = strtobool(buf, &value);
+
+ if (!ret)
+ ret = usb_set_usb2_hardware_lpm(udev, value);
+
+ usb_unlock_device(udev);
+
+ if (!ret)
+ return count;
+
+ return ret;
+}
+
+static DEVICE_ATTR(usb2_hardware_lpm, S_IRUGO | S_IWUSR, show_usb2_hardware_lpm,
+ set_usb2_hardware_lpm);
+
+static struct attribute *usb2_hardware_lpm_attr[] = {
+ &dev_attr_usb2_hardware_lpm.attr,
+ NULL,
+};
+static struct attribute_group usb2_hardware_lpm_attr_group = {
+ .name = power_group_name,
+ .attrs = usb2_hardware_lpm_attr,
+};
+
static struct attribute *power_attrs[] = {
&dev_attr_autosuspend.attr,
&dev_attr_level.attr,
@@ -428,13 +478,20 @@ static int add_power_attributes(struct device *dev)
{
int rc = 0;
- if (is_usb_device(dev))
+ if (is_usb_device(dev)) {
+ struct usb_device *udev = to_usb_device(dev);
rc = sysfs_merge_group(&dev->kobj, &power_attr_group);
+ if (udev->usb2_hw_lpm_capable == 1)
+ rc = sysfs_merge_group(&dev->kobj,
+ &usb2_hardware_lpm_attr_group);
+ }
+
return rc;
}
static void remove_power_attributes(struct device *dev)
{
+ sysfs_unmerge_group(&dev->kobj, &usb2_hardware_lpm_attr_group);
sysfs_unmerge_group(&dev->kobj, &power_attr_group);
}
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index ae334b0..909625b 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -350,7 +350,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
dev->state < USB_STATE_CONFIGURED)
return -ENODEV;
- max = le16_to_cpu(ep->desc.wMaxPacketSize);
+ max = usb_endpoint_maxp(&ep->desc);
if (max <= 0) {
dev_dbg(&dev->dev,
"bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 8706fc9..73cd900 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -225,6 +225,7 @@ static void usb_release_dev(struct device *dev)
hcd = bus_to_hcd(udev->bus);
usb_destroy_configuration(udev);
+ usb_release_bos_descriptor(udev);
usb_put_hcd(hcd);
kfree(udev->product);
kfree(udev->manufacturer);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index d44d4b7..3888778 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -28,6 +28,8 @@ extern int usb_remove_device(struct usb_device *udev);
extern int usb_get_device_descriptor(struct usb_device *dev,
unsigned int size);
+extern int usb_get_bos_descriptor(struct usb_device *dev);
+extern void usb_release_bos_descriptor(struct usb_device *dev);
extern char *usb_cache_string(struct usb_device *udev, int index);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
extern int usb_choose_configuration(struct usb_device *udev);
@@ -80,6 +82,7 @@ extern int usb_remote_wakeup(struct usb_device *dev);
extern int usb_runtime_suspend(struct device *dev);
extern int usb_runtime_resume(struct device *dev);
extern int usb_runtime_idle(struct device *dev);
+extern int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable);
#else
@@ -94,6 +97,10 @@ 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;
+}
#endif
extern struct bus_type usb_bus_type;