Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0f632118 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull USB fixes from Greg Kroah-Hartman:
 "Here are a few tiny USB fixes for 3.8-rc6.

  Nothing major here, some host controller bug fixes to resolve a number
  of bugs that people have reported, and a bunch of additional device
  ids are added to a number of drivers (which caused code to be deleted
  from the usb-storage driver, always nice)"

* tag 'usb-3.8-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (22 commits)
  USB: storage: optimize to match the Huawei USB storage devices and support new switch command
  USB: storage: Define a new macro for USB storage match rules
  USB: ftdi_sio: add Zolix FTDI PID
  USB: option: add Changhong CH690
  USB: ftdi_sio: add PID/VID entries for ELV WS 300 PC II
  USB: add OWL CM-160 support to cp210x driver
  USB: EHCI: fix bug in scheduling periodic split transfers
  USB: EHCI: fix for leaking isochronous data
  USB: option: add support for Telit LE920
  USB: qcserial: add Telit Gobi QDL device
  USB: EHCI: fix timer bug affecting port resume
  USB: UHCI: notify usbcore about port resumes
  USB: EHCI: notify usbcore about port resumes
  USB: add usb_hcd_{start,end}_port_resume
  USB: EHCI: unlink one async QH at a time
  USB: EHCI: remove ASS/PSS polling timeout
  usb: Using correct way to clear usb3.0 device's remote wakeup feature.
  usb: Prevent dead ports when xhci is not enabled
  USB: XHCI: fix memory leak of URB-private data
  drivers: xhci: fix incorrect bit test
  ...
parents 85e28c76 200e0d99
Loading
Loading
Loading
Loading
+44 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@
#include <asm/unaligned.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/pm_runtime.h>

#include <linux/usb.h>
#include <linux/usb/hcd.h>
@@ -1025,6 +1026,49 @@ static int register_root_hub(struct usb_hcd *hcd)
	return retval;
}

/*
 * usb_hcd_start_port_resume - a root-hub port is sending a resume signal
 * @bus: the bus which the root hub belongs to
 * @portnum: the port which is being resumed
 *
 * HCDs should call this function when they know that a resume signal is
 * being sent to a root-hub port.  The root hub will be prevented from
 * going into autosuspend until usb_hcd_end_port_resume() is called.
 *
 * The bus's private lock must be held by the caller.
 */
void usb_hcd_start_port_resume(struct usb_bus *bus, int portnum)
{
	unsigned bit = 1 << portnum;

	if (!(bus->resuming_ports & bit)) {
		bus->resuming_ports |= bit;
		pm_runtime_get_noresume(&bus->root_hub->dev);
	}
}
EXPORT_SYMBOL_GPL(usb_hcd_start_port_resume);

/*
 * usb_hcd_end_port_resume - a root-hub port has stopped sending a resume signal
 * @bus: the bus which the root hub belongs to
 * @portnum: the port which is being resumed
 *
 * HCDs should call this function when they know that a resume signal has
 * stopped being sent to a root-hub port.  The root hub will be allowed to
 * autosuspend again.
 *
 * The bus's private lock must be held by the caller.
 */
void usb_hcd_end_port_resume(struct usb_bus *bus, int portnum)
{
	unsigned bit = 1 << portnum;

	if (bus->resuming_ports & bit) {
		bus->resuming_ports &= ~bit;
		pm_runtime_put_noidle(&bus->root_hub->dev);
	}
}
EXPORT_SYMBOL_GPL(usb_hcd_end_port_resume);

/*-------------------------------------------------------------------------*/

+52 −18
Original line number Diff line number Diff line
@@ -2838,6 +2838,23 @@ void usb_enable_ltm(struct usb_device *udev)
EXPORT_SYMBOL_GPL(usb_enable_ltm);

#ifdef	CONFIG_USB_SUSPEND
/*
 * usb_disable_function_remotewakeup - disable usb3.0
 * device's function remote wakeup
 * @udev: target device
 *
 * Assume there's only one function on the USB 3.0
 * device and disable remote wake for the first
 * interface. FIXME if the interface association
 * descriptor shows there's more than one function.
 */
static int usb_disable_function_remotewakeup(struct usb_device *udev)
{
	return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
				USB_REQ_CLEAR_FEATURE, USB_RECIP_INTERFACE,
				USB_INTRF_FUNC_SUSPEND,	0, NULL, 0,
				USB_CTRL_SET_TIMEOUT);
}

/*
 * usb_port_suspend - suspend a usb device's upstream port
@@ -2955,12 +2972,19 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
		dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
				port1, status);
		/* paranoia:  "should not happen" */
		if (udev->do_remote_wakeup)
			(void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
				USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
		if (udev->do_remote_wakeup) {
			if (!hub_is_superspeed(hub->hdev)) {
				(void) usb_control_msg(udev,
						usb_sndctrlpipe(udev, 0),
						USB_REQ_CLEAR_FEATURE,
						USB_RECIP_DEVICE,
						USB_DEVICE_REMOTE_WAKEUP, 0,
						NULL, 0,
						USB_CTRL_SET_TIMEOUT);
			} else
				(void) usb_disable_function_remotewakeup(udev);

		}

		/* Try to enable USB2 hardware LPM again */
		if (udev->usb2_hw_lpm_capable == 1)
@@ -3052,8 +3076,9 @@ static int finish_port_resume(struct usb_device *udev)
	 * udev->reset_resume
	 */
	} else if (udev->actconfig && !udev->reset_resume) {
		if (!hub_is_superspeed(udev->parent)) {
			le16_to_cpus(&devstatus);
		if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
			if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
				status = usb_control_msg(udev,
						usb_sndctrlpipe(udev, 0),
						USB_REQ_CLEAR_FEATURE,
@@ -3061,11 +3086,20 @@ static int finish_port_resume(struct usb_device *udev)
						USB_DEVICE_REMOTE_WAKEUP, 0,
						NULL, 0,
						USB_CTRL_SET_TIMEOUT);
		} else {
			status = usb_get_status(udev, USB_RECIP_INTERFACE, 0,
					&devstatus);
			le16_to_cpus(&devstatus);
			if (!status && devstatus & (USB_INTRF_STAT_FUNC_RW_CAP
					| USB_INTRF_STAT_FUNC_RW))
				status =
					usb_disable_function_remotewakeup(udev);
		}

		if (status)
			dev_dbg(&udev->dev,
				"disable remote wakeup, status %d\n",
				status);
		}
		status = 0;
	}
	return status;
+1 −0
Original line number Diff line number Diff line
@@ -797,6 +797,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
			ehci->reset_done[i] = jiffies + msecs_to_jiffies(25);
			set_bit(i, &ehci->resuming_ports);
			ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
			usb_hcd_start_port_resume(&hcd->self, i);
			mod_timer(&hcd->rh_timer, ehci->reset_done[i]);
		}
	}
+8 −1
Original line number Diff line number Diff line
@@ -649,7 +649,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
			status = STS_PCD;
		}
	}
	/* FIXME autosuspend idle root hubs */

	/* If a resume is in progress, make sure it can finish */
	if (ehci->resuming_ports)
		mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(25));

	spin_unlock_irqrestore (&ehci->lock, flags);
	return status ? retval : 0;
}
@@ -851,6 +855,7 @@ static int ehci_hub_control (
				/* resume signaling for 20 msec */
				ehci->reset_done[wIndex] = jiffies
						+ msecs_to_jiffies(20);
				usb_hcd_start_port_resume(&hcd->self, wIndex);
				/* check the port again */
				mod_timer(&ehci_to_hcd(ehci)->rh_timer,
						ehci->reset_done[wIndex]);
@@ -862,6 +867,7 @@ static int ehci_hub_control (
				clear_bit(wIndex, &ehci->suspended_ports);
				set_bit(wIndex, &ehci->port_c_suspend);
				ehci->reset_done[wIndex] = 0;
				usb_hcd_end_port_resume(&hcd->self, wIndex);

				/* stop resume signaling */
				temp = ehci_readl(ehci, status_reg);
@@ -950,6 +956,7 @@ static int ehci_hub_control (
			ehci->reset_done[wIndex] = 0;
			if (temp & PORT_PE)
				set_bit(wIndex, &ehci->port_c_suspend);
			usb_hcd_end_port_resume(&hcd->self, wIndex);
		}

		if (temp & PORT_OC)
+30 −20
Original line number Diff line number Diff line
@@ -1197,17 +1197,26 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested)
	if (ehci->async_iaa || ehci->async_unlinking)
		return;

	/* Do all the waiting QHs at once */
	/* If the controller isn't running, we don't have to wait for it */
	if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) {

		/* Do all the waiting QHs */
		ehci->async_iaa = ehci->async_unlink;
		ehci->async_unlink = NULL;

	/* If the controller isn't running, we don't have to wait for it */
	if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) {
		if (!nested)		/* Avoid recursion */
			end_unlink_async(ehci);

	/* Otherwise start a new IAA cycle */
	} else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) {
		struct ehci_qh		*qh;

		/* Do only the first waiting QH (nVidia bug?) */
		qh = ehci->async_unlink;
		ehci->async_iaa = qh;
		ehci->async_unlink = qh->unlink_next;
		qh->unlink_next = NULL;

		/* Make sure the unlinks are all visible to the hardware */
		wmb();

@@ -1255,34 +1264,35 @@ static void end_unlink_async(struct ehci_hcd *ehci)
	}
}

static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh);

static void unlink_empty_async(struct ehci_hcd *ehci)
{
	struct ehci_qh		*qh, *next;
	bool			stopped = (ehci->rh_state < EHCI_RH_RUNNING);
	struct ehci_qh		*qh;
	struct ehci_qh		*qh_to_unlink = NULL;
	bool			check_unlinks_later = false;
	int			count = 0;

	/* Unlink all the async QHs that have been empty for a timer cycle */
	next = ehci->async->qh_next.qh;
	while (next) {
		qh = next;
		next = qh->qh_next.qh;

	/* Find the last async QH which has been empty for a timer cycle */
	for (qh = ehci->async->qh_next.qh; qh; qh = qh->qh_next.qh) {
		if (list_empty(&qh->qtd_list) &&
				qh->qh_state == QH_STATE_LINKED) {
			if (!stopped && qh->unlink_cycle ==
					ehci->async_unlink_cycle)
			++count;
			if (qh->unlink_cycle == ehci->async_unlink_cycle)
				check_unlinks_later = true;
			else
				single_unlink_async(ehci, qh);
				qh_to_unlink = qh;
		}
	}

	/* Start a new IAA cycle if any QHs are waiting for it */
	if (ehci->async_unlink)
		start_iaa_cycle(ehci, false);
	/* If nothing else is being unlinked, unlink the last empty QH */
	if (!ehci->async_iaa && !ehci->async_unlink && qh_to_unlink) {
		start_unlink_async(ehci, qh_to_unlink);
		--count;
	}

	/* QHs that haven't been empty for long enough will be handled later */
	if (check_unlinks_later) {
	/* Other QHs will be handled later */
	if (count > 0) {
		ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true);
		++ehci->async_unlink_cycle;
	}
Loading