Skip to content

Commit

Permalink
Merge tag 'usb-3.8-rc6' of git://git.kernel.org/pub/scm/linux/kernel/…
Browse files Browse the repository at this point in the history
…git/gregkh/usb

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
  ...
  • Loading branch information
Linus Torvalds committed Feb 5, 2013
2 parents 85e28c7 + 200e0d9 commit 0f63211
Show file tree
Hide file tree
Showing 23 changed files with 310 additions and 392 deletions.
44 changes: 44 additions & 0 deletions drivers/usb/core/hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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);

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

Expand Down
70 changes: 52 additions & 18 deletions drivers/usb/core/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
USB_DEVICE_REMOTE_WAKEUP, 0,
NULL, 0,
USB_CTRL_SET_TIMEOUT);
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)
Expand Down Expand Up @@ -3052,20 +3076,30 @@ static int finish_port_resume(struct usb_device *udev)
* udev->reset_resume
*/
} else if (udev->actconfig && !udev->reset_resume) {
le16_to_cpus(&devstatus);
if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
status = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
USB_REQ_CLEAR_FEATURE,
if (!hub_is_superspeed(udev->parent)) {
le16_to_cpus(&devstatus);
if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
status = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
USB_REQ_CLEAR_FEATURE,
USB_RECIP_DEVICE,
USB_DEVICE_REMOTE_WAKEUP, 0,
NULL, 0,
USB_CTRL_SET_TIMEOUT);
if (status)
dev_dbg(&udev->dev,
"disable remote wakeup, status %d\n",
status);
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;
Expand Down
1 change: 1 addition & 0 deletions drivers/usb/host/ehci-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
}
}
Expand Down
9 changes: 8 additions & 1 deletion drivers/usb/host/ehci-hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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]);
Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
50 changes: 30 additions & 20 deletions drivers/usb/host/ehci-q.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
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)) {

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

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();

Expand Down Expand Up @@ -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;
}
Expand Down
9 changes: 6 additions & 3 deletions drivers/usb/host/ehci-sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __hc32 mask)
}

static const unsigned char
max_tt_usecs[] = { 125, 125, 125, 125, 125, 125, 30, 0 };
max_tt_usecs[] = { 125, 125, 125, 125, 125, 125, 125, 25 };

/* carryover low/fullspeed bandwidth that crosses uframe boundries */
static inline void carryover_tt_bandwidth(unsigned short tt_usecs[8])
Expand Down Expand Up @@ -2212,11 +2212,11 @@ static void scan_isoc(struct ehci_hcd *ehci)
}
ehci->now_frame = now_frame;

frame = ehci->last_iso_frame;
for (;;) {
union ehci_shadow q, *q_p;
__hc32 type, *hw_p;

frame = ehci->last_iso_frame;
restart:
/* scan each element in frame's queue for completions */
q_p = &ehci->pshadow [frame];
Expand Down Expand Up @@ -2321,6 +2321,9 @@ static void scan_isoc(struct ehci_hcd *ehci)
/* Stop when we have reached the current frame */
if (frame == now_frame)
break;
ehci->last_iso_frame = (frame + 1) & fmask;

/* The last frame may still have active siTDs */
ehci->last_iso_frame = frame;
frame = (frame + 1) & fmask;
}
}
29 changes: 15 additions & 14 deletions drivers/usb/host/ehci-timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,15 @@ static void ehci_poll_ASS(struct ehci_hcd *ehci)

if (want != actual) {

/* Poll again later, but give up after about 20 ms */
if (ehci->ASS_poll_count++ < 20) {
ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true);
return;
}
ehci_dbg(ehci, "Waited too long for the async schedule status (%x/%x), giving up\n",
want, actual);
/* Poll again later */
ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true);
++ehci->ASS_poll_count;
return;
}

if (ehci->ASS_poll_count > 20)
ehci_dbg(ehci, "ASS poll count reached %d\n",
ehci->ASS_poll_count);
ehci->ASS_poll_count = 0;

/* The status is up-to-date; restart or stop the schedule as needed */
Expand Down Expand Up @@ -159,14 +160,14 @@ static void ehci_poll_PSS(struct ehci_hcd *ehci)

if (want != actual) {

/* Poll again later, but give up after about 20 ms */
if (ehci->PSS_poll_count++ < 20) {
ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true);
return;
}
ehci_dbg(ehci, "Waited too long for the periodic schedule status (%x/%x), giving up\n",
want, actual);
/* Poll again later */
ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true);
return;
}

if (ehci->PSS_poll_count > 20)
ehci_dbg(ehci, "PSS poll count reached %d\n",
ehci->PSS_poll_count);
ehci->PSS_poll_count = 0;

/* The status is up-to-date; restart or stop the schedule as needed */
Expand Down
1 change: 1 addition & 0 deletions drivers/usb/host/pci-quirks.c
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,7 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
"defaulting to EHCI.\n");
dev_warn(&xhci_pdev->dev,
"USB 3.0 devices will work at USB 2.0 speeds.\n");
usb_disable_xhci_ports(xhci_pdev);
return;
}

Expand Down
3 changes: 3 additions & 0 deletions drivers/usb/host/uhci-hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
}
}
clear_bit(port, &uhci->resuming_ports);
usb_hcd_end_port_resume(&uhci_to_hcd(uhci)->self, port);
}

/* Wait for the UHCI controller in HP's iLO2 server management chip.
Expand Down Expand Up @@ -167,6 +168,8 @@ static void uhci_check_ports(struct uhci_hcd *uhci)
set_bit(port, &uhci->resuming_ports);
uhci->ports_timeout = jiffies +
msecs_to_jiffies(25);
usb_hcd_start_port_resume(
&uhci_to_hcd(uhci)->self, port);

/* Make sure we see the port again
* after the resuming period is over. */
Expand Down
Loading

0 comments on commit 0f63211

Please sign in to comment.