USB fixes for 3.4-rc2

Here are a number of fixes for the USB core and drivers for 3.4-rc2
 
 Lots of tiny xhci fixes here, a few usb-serial driver fixes and new device ids,
 and a smattering of other minor fixes in different USB drivers.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.18 (GNU/Linux)
 
 iEYEABECAAYFAk+HVPUACgkQMUfUDdst+ykskwCfV0ypvXzg0JMTBqNDHb8BDmcZ
 Ca4An0pZyzO2JsUHTfGUAvm6SFANqCnk
 =KB61
 -----END PGP SIGNATURE-----

Merge tag 'usb-3.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB fixes from Greg KH:
 "Here are a number of fixes for the USB core and drivers for 3.4-rc2

  Lots of tiny xhci fixes here, a few usb-serial driver fixes and new
  device ids, and a smattering of other minor fixes in different USB
  drivers.

  Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>"

* tag 'usb-3.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (30 commits)
  USB: update usbtmc api documentation
  xHCI: Correct the #define XHCI_LEGACY_DISABLE_SMI
  xHCI: use gfp flags from caller instead of GFP_ATOMIC
  xHCI: add XHCI_RESET_ON_RESUME quirk for VIA xHCI host
  USB: fix bug of device descriptor got from superspeed device
  xhci: Fix register save/restore order.
  xhci: Restore event ring dequeue pointer on resume.
  xhci: Don't write zeroed pointers to xHC registers.
  xhci: Warn when hosts don't halt.
  xhci: don't re-enable IE constantly
  usb: xhci: fix section mismatch in linux-next
  xHCI: correct to print the true HSEE of USBCMD
  USB: serial: fix race between probe and open
  UHCI: hub_status_data should indicate if ports are resuming
  EHCI: keep track of ports being resumed and indicate in hub_status_data
  USB: fix race between root-hub suspend and remote wakeup
  USB: sierra: add support for Sierra Wireless MC7710
  USB: ftdi_sio: fix race condition in TIOCMIWAIT, and abort of TIOCMIWAIT when the device is removed
  USB: ftdi_sio: fix status line change handling for TIOCMIWAIT and TIOCGICOUNT
  USB: don't ignore suspend errors for root hubs
  ...
This commit is contained in:
Linus Torvalds 2012-04-12 15:37:21 -07:00
commit 7c427f4550
32 changed files with 216 additions and 118 deletions

View file

@ -1,5 +1,5 @@
What: /sys/bus/usb/drivers/usbtmc/devices/*/interface_capabilities What: /sys/bus/usb/drivers/usbtmc/*/interface_capabilities
What: /sys/bus/usb/drivers/usbtmc/devices/*/device_capabilities What: /sys/bus/usb/drivers/usbtmc/*/device_capabilities
Date: August 2008 Date: August 2008
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Description: Description:
@ -12,8 +12,8 @@ Description:
The files are read only. The files are read only.
What: /sys/bus/usb/drivers/usbtmc/devices/*/usb488_interface_capabilities What: /sys/bus/usb/drivers/usbtmc/*/usb488_interface_capabilities
What: /sys/bus/usb/drivers/usbtmc/devices/*/usb488_device_capabilities What: /sys/bus/usb/drivers/usbtmc/*/usb488_device_capabilities
Date: August 2008 Date: August 2008
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Description: Description:
@ -27,7 +27,7 @@ Description:
The files are read only. The files are read only.
What: /sys/bus/usb/drivers/usbtmc/devices/*/TermChar What: /sys/bus/usb/drivers/usbtmc/*/TermChar
Date: August 2008 Date: August 2008
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Description: Description:
@ -40,7 +40,7 @@ Description:
sent to the device or not. sent to the device or not.
What: /sys/bus/usb/drivers/usbtmc/devices/*/TermCharEnabled What: /sys/bus/usb/drivers/usbtmc/*/TermCharEnabled
Date: August 2008 Date: August 2008
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Description: Description:
@ -51,7 +51,7 @@ Description:
published by the USB-IF. published by the USB-IF.
What: /sys/bus/usb/drivers/usbtmc/devices/*/auto_abort What: /sys/bus/usb/drivers/usbtmc/*/auto_abort
Date: August 2008 Date: August 2008
Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Contact: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Description: Description:

View file

@ -168,6 +168,28 @@ that if the completion handler or anyone else tries to resubmit it
they will get a -EPERM error. Thus you can be sure that when they will get a -EPERM error. Thus you can be sure that when
usb_kill_urb() returns, the URB is totally idle. usb_kill_urb() returns, the URB is totally idle.
There is a lifetime issue to consider. An URB may complete at any
time, and the completion handler may free the URB. If this happens
while usb_unlink_urb or usb_kill_urb is running, it will cause a
memory-access violation. The driver is responsible for avoiding this,
which often means some sort of lock will be needed to prevent the URB
from being deallocated while it is still in use.
On the other hand, since usb_unlink_urb may end up calling the
completion handler, the handler must not take any lock that is held
when usb_unlink_urb is invoked. The general solution to this problem
is to increment the URB's reference count while holding the lock, then
drop the lock and call usb_unlink_urb or usb_kill_urb, and then
decrement the URB's reference count. You increment the reference
count by calling
struct urb *usb_get_urb(struct urb *urb)
(ignore the return value; it is the same as the argument) and
decrement the reference count by calling usb_free_urb. Of course,
none of this is necessary if there's no danger of the URB being freed
by the completion handler.
1.7. What about the completion handler? 1.7. What about the completion handler?

View file

@ -183,10 +183,10 @@ An input control transfer to get a port status.
d5ea89a0 3575914555 S Ci:1:001:0 s a3 00 0000 0003 0004 4 < d5ea89a0 3575914555 S Ci:1:001:0 s a3 00 0000 0003 0004 4 <
d5ea89a0 3575914560 C Ci:1:001:0 0 4 = 01050000 d5ea89a0 3575914560 C Ci:1:001:0 0 4 = 01050000
An output bulk transfer to send a SCSI command 0x5E in a 31-byte Bulk wrapper An output bulk transfer to send a SCSI command 0x28 (READ_10) in a 31-byte
to a storage device at address 5: Bulk wrapper to a storage device at address 5:
dd65f0e8 4128379752 S Bo:1:005:2 -115 31 = 55534243 5e000000 00000000 00000600 00000000 00000000 00000000 000000 dd65f0e8 4128379752 S Bo:1:005:2 -115 31 = 55534243 ad000000 00800000 80010a28 20000000 20000040 00000000 000000
dd65f0e8 4128379808 C Bo:1:005:2 0 31 > dd65f0e8 4128379808 C Bo:1:005:2 0 31 >
* Raw binary format and API * Raw binary format and API

View file

@ -2,14 +2,6 @@
# USB device configuration # USB device configuration
# #
menuconfig USB_SUPPORT
bool "USB support"
depends on HAS_IOMEM
default y
---help---
This option adds core support for Universal Serial Bus (USB).
You will also need drivers from the following menu to make use of it.
# many non-PCI SOC chips embed OHCI # many non-PCI SOC chips embed OHCI
config USB_ARCH_HAS_OHCI config USB_ARCH_HAS_OHCI
boolean boolean
@ -63,6 +55,14 @@ config USB_ARCH_HAS_XHCI
boolean boolean
default PCI default PCI
menuconfig USB_SUPPORT
bool "USB support"
depends on HAS_IOMEM
default y
---help---
This option adds core support for Universal Serial Bus (USB).
You will also need drivers from the following menu to make use of it.
if USB_SUPPORT if USB_SUPPORT
config USB_COMMON config USB_COMMON

View file

@ -1189,8 +1189,13 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
if (status == 0) { if (status == 0) {
status = usb_suspend_device(udev, msg); status = usb_suspend_device(udev, msg);
/* Again, ignore errors during system sleep transitions */ /*
if (!PMSG_IS_AUTO(msg)) * Ignore errors from non-root-hub devices during
* system sleep transitions. For the most part,
* these devices should go to low power anyway when
* the entire bus is suspended.
*/
if (udev->parent && !PMSG_IS_AUTO(msg))
status = 0; status = 0;
} }

View file

@ -1978,6 +1978,18 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
if (status == 0) { if (status == 0) {
usb_set_device_state(rhdev, USB_STATE_SUSPENDED); usb_set_device_state(rhdev, USB_STATE_SUSPENDED);
hcd->state = HC_STATE_SUSPENDED; hcd->state = HC_STATE_SUSPENDED;
/* Did we race with a root-hub wakeup event? */
if (rhdev->do_remote_wakeup) {
char buffer[6];
status = hcd->driver->hub_status_data(hcd, buffer);
if (status != 0) {
dev_dbg(&rhdev->dev, "suspend raced with wakeup event\n");
hcd_bus_resume(rhdev, PMSG_AUTO_RESUME);
status = -EBUSY;
}
}
} else { } else {
spin_lock_irq(&hcd_root_hub_lock); spin_lock_irq(&hcd_root_hub_lock);
if (!HCD_DEAD(hcd)) { if (!HCD_DEAD(hcd)) {

View file

@ -3163,6 +3163,22 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
if (retval) if (retval)
goto fail; goto fail;
/*
* Some superspeed devices have finished the link training process
* and attached to a superspeed hub port, but the device descriptor
* got from those devices show they aren't superspeed devices. Warm
* reset the port attached by the devices can fix them.
*/
if ((udev->speed == USB_SPEED_SUPER) &&
(le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
dev_err(&udev->dev, "got a wrong device descriptor, "
"warm reset device\n");
hub_port_reset(hub, port1, udev,
HUB_BH_RESET_TIME, true);
retval = -EINVAL;
goto fail;
}
if (udev->descriptor.bMaxPacketSize0 == 0xff || if (udev->descriptor.bMaxPacketSize0 == 0xff ||
udev->speed == USB_SPEED_SUPER) udev->speed == USB_SPEED_SUPER)
i = 512; i = 512;

View file

@ -308,7 +308,8 @@ static void sg_complete(struct urb *urb)
retval = usb_unlink_urb(io->urbs [i]); retval = usb_unlink_urb(io->urbs [i]);
if (retval != -EINPROGRESS && if (retval != -EINPROGRESS &&
retval != -ENODEV && retval != -ENODEV &&
retval != -EBUSY) retval != -EBUSY &&
retval != -EIDRM)
dev_err(&io->dev->dev, dev_err(&io->dev->dev,
"%s, unlink --> %d\n", "%s, unlink --> %d\n",
__func__, retval); __func__, retval);
@ -317,7 +318,6 @@ static void sg_complete(struct urb *urb)
} }
spin_lock(&io->lock); spin_lock(&io->lock);
} }
urb->dev = NULL;
/* on the last completion, signal usb_sg_wait() */ /* on the last completion, signal usb_sg_wait() */
io->bytes += urb->actual_length; io->bytes += urb->actual_length;
@ -524,7 +524,6 @@ void usb_sg_wait(struct usb_sg_request *io)
case -ENXIO: /* hc didn't queue this one */ case -ENXIO: /* hc didn't queue this one */
case -EAGAIN: case -EAGAIN:
case -ENOMEM: case -ENOMEM:
io->urbs[i]->dev = NULL;
retval = 0; retval = 0;
yield(); yield();
break; break;
@ -542,7 +541,6 @@ void usb_sg_wait(struct usb_sg_request *io)
/* fail any uncompleted urbs */ /* fail any uncompleted urbs */
default: default:
io->urbs[i]->dev = NULL;
io->urbs[i]->status = retval; io->urbs[i]->status = retval;
dev_dbg(&io->dev->dev, "%s, submit --> %d\n", dev_dbg(&io->dev->dev, "%s, submit --> %d\n",
__func__, retval); __func__, retval);
@ -593,7 +591,10 @@ void usb_sg_cancel(struct usb_sg_request *io)
if (!io->urbs [i]->dev) if (!io->urbs [i]->dev)
continue; continue;
retval = usb_unlink_urb(io->urbs [i]); retval = usb_unlink_urb(io->urbs [i]);
if (retval != -EINPROGRESS && retval != -EBUSY) if (retval != -EINPROGRESS
&& retval != -ENODEV
&& retval != -EBUSY
&& retval != -EIDRM)
dev_warn(&io->dev->dev, "%s, unlink --> %d\n", dev_warn(&io->dev->dev, "%s, unlink --> %d\n",
__func__, retval); __func__, retval);
} }

View file

@ -539,6 +539,10 @@ EXPORT_SYMBOL_GPL(usb_submit_urb);
* never submitted, or it was unlinked before, or the hardware is already * never submitted, or it was unlinked before, or the hardware is already
* finished with it), even if the completion handler has not yet run. * finished with it), even if the completion handler has not yet run.
* *
* The URB must not be deallocated while this routine is running. In
* particular, when a driver calls this routine, it must insure that the
* completion handler cannot deallocate the URB.
*
* Unlinking and Endpoint Queues: * Unlinking and Endpoint Queues:
* *
* [The behaviors and guarantees described below do not apply to virtual * [The behaviors and guarantees described below do not apply to virtual
@ -603,6 +607,10 @@ EXPORT_SYMBOL_GPL(usb_unlink_urb);
* with error -EPERM. Thus even if the URB's completion handler always * with error -EPERM. Thus even if the URB's completion handler always
* tries to resubmit, it will not succeed and the URB will become idle. * tries to resubmit, it will not succeed and the URB will become idle.
* *
* The URB must not be deallocated while this routine is running. In
* particular, when a driver calls this routine, it must insure that the
* completion handler cannot deallocate the URB.
*
* This routine may not be used in an interrupt context (such as a bottom * This routine may not be used in an interrupt context (such as a bottom
* half or a completion handler), or when holding a spinlock, or in other * half or a completion handler), or when holding a spinlock, or in other
* situations where the caller can't schedule(). * situations where the caller can't schedule().
@ -640,6 +648,10 @@ EXPORT_SYMBOL_GPL(usb_kill_urb);
* with error -EPERM. Thus even if the URB's completion handler always * with error -EPERM. Thus even if the URB's completion handler always
* tries to resubmit, it will not succeed and the URB will become idle. * tries to resubmit, it will not succeed and the URB will become idle.
* *
* The URB must not be deallocated while this routine is running. In
* particular, when a driver calls this routine, it must insure that the
* completion handler cannot deallocate the URB.
*
* This routine may not be used in an interrupt context (such as a bottom * This routine may not be used in an interrupt context (such as a bottom
* half or a completion handler), or when holding a spinlock, or in other * half or a completion handler), or when holding a spinlock, or in other
* situations where the caller can't schedule(). * situations where the caller can't schedule().

View file

@ -1574,7 +1574,6 @@ static void destroy_ep_files (struct dev_data *dev)
DBG (dev, "%s %d\n", __func__, dev->state); DBG (dev, "%s %d\n", __func__, dev->state);
/* dev->state must prevent interference */ /* dev->state must prevent interference */
restart:
spin_lock_irq (&dev->lock); spin_lock_irq (&dev->lock);
while (!list_empty(&dev->epfiles)) { while (!list_empty(&dev->epfiles)) {
struct ep_data *ep; struct ep_data *ep;

View file

@ -347,6 +347,8 @@ static int ehci_reset (struct ehci_hcd *ehci)
if (ehci->debug) if (ehci->debug)
dbgp_external_startup(); dbgp_external_startup();
ehci->port_c_suspend = ehci->suspended_ports =
ehci->resuming_ports = 0;
return retval; return retval;
} }
@ -939,6 +941,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
* like usb_port_resume() does. * like usb_port_resume() does.
*/ */
ehci->reset_done[i] = jiffies + msecs_to_jiffies(25); 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); ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
mod_timer(&hcd->rh_timer, ehci->reset_done[i]); mod_timer(&hcd->rh_timer, ehci->reset_done[i]);
} }

View file

@ -223,17 +223,12 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
* remote wakeup, we must fail the suspend. * remote wakeup, we must fail the suspend.
*/ */
if (hcd->self.root_hub->do_remote_wakeup) { if (hcd->self.root_hub->do_remote_wakeup) {
port = HCS_N_PORTS(ehci->hcs_params); if (ehci->resuming_ports) {
while (port--) {
if (ehci->reset_done[port] != 0) {
spin_unlock_irq(&ehci->lock); spin_unlock_irq(&ehci->lock);
ehci_dbg(ehci, "suspend failed because " ehci_dbg(ehci, "suspend failed because a port is resuming\n");
"port %d is resuming\n",
port + 1);
return -EBUSY; return -EBUSY;
} }
} }
}
/* stop schedules, clean any completed work */ /* stop schedules, clean any completed work */
if (ehci->rh_state == EHCI_RH_RUNNING) if (ehci->rh_state == EHCI_RH_RUNNING)
@ -554,16 +549,12 @@ static int
ehci_hub_status_data (struct usb_hcd *hcd, char *buf) ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
{ {
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp, status = 0; u32 temp, status;
u32 mask; u32 mask;
int ports, i, retval = 1; int ports, i, retval = 1;
unsigned long flags; unsigned long flags;
u32 ppcd = 0; u32 ppcd = 0;
/* if !USB_SUSPEND, root hub timers won't get shut down ... */
if (ehci->rh_state != EHCI_RH_RUNNING)
return 0;
/* init status to no-changes */ /* init status to no-changes */
buf [0] = 0; buf [0] = 0;
ports = HCS_N_PORTS (ehci->hcs_params); ports = HCS_N_PORTS (ehci->hcs_params);
@ -572,6 +563,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
retval++; retval++;
} }
/* Inform the core about resumes-in-progress by returning
* a non-zero value even if there are no status changes.
*/
status = ehci->resuming_ports;
/* Some boards (mostly VIA?) report bogus overcurrent indications, /* Some boards (mostly VIA?) report bogus overcurrent indications,
* causing massive log spam unless we completely ignore them. It * causing massive log spam unless we completely ignore them. It
* may be relevant that VIA VT8235 controllers, where PORT_POWER is * may be relevant that VIA VT8235 controllers, where PORT_POWER is
@ -846,6 +842,7 @@ static int ehci_hub_control (
ehci_writel(ehci, ehci_writel(ehci,
temp & ~(PORT_RWC_BITS | PORT_RESUME), temp & ~(PORT_RWC_BITS | PORT_RESUME),
status_reg); status_reg);
clear_bit(wIndex, &ehci->resuming_ports);
retval = handshake(ehci, status_reg, retval = handshake(ehci, status_reg,
PORT_RESUME, 0, 2000 /* 2msec */); PORT_RESUME, 0, 2000 /* 2msec */);
if (retval != 0) { if (retval != 0) {
@ -864,6 +861,7 @@ static int ehci_hub_control (
ehci->reset_done[wIndex])) { ehci->reset_done[wIndex])) {
status |= USB_PORT_STAT_C_RESET << 16; status |= USB_PORT_STAT_C_RESET << 16;
ehci->reset_done [wIndex] = 0; ehci->reset_done [wIndex] = 0;
clear_bit(wIndex, &ehci->resuming_ports);
/* force reset to complete */ /* force reset to complete */
ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET), ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET),
@ -884,8 +882,10 @@ static int ehci_hub_control (
ehci_readl(ehci, status_reg)); ehci_readl(ehci, status_reg));
} }
if (!(temp & (PORT_RESUME|PORT_RESET))) if (!(temp & (PORT_RESUME|PORT_RESET))) {
ehci->reset_done[wIndex] = 0; ehci->reset_done[wIndex] = 0;
clear_bit(wIndex, &ehci->resuming_ports);
}
/* transfer dedicated ports to the companion hc */ /* transfer dedicated ports to the companion hc */
if ((temp & PORT_CONNECT) && if ((temp & PORT_CONNECT) &&
@ -920,6 +920,7 @@ static int ehci_hub_control (
status |= USB_PORT_STAT_SUSPEND; status |= USB_PORT_STAT_SUSPEND;
} else if (test_bit(wIndex, &ehci->suspended_ports)) { } else if (test_bit(wIndex, &ehci->suspended_ports)) {
clear_bit(wIndex, &ehci->suspended_ports); clear_bit(wIndex, &ehci->suspended_ports);
clear_bit(wIndex, &ehci->resuming_ports);
ehci->reset_done[wIndex] = 0; ehci->reset_done[wIndex] = 0;
if (temp & PORT_PE) if (temp & PORT_PE)
set_bit(wIndex, &ehci->port_c_suspend); set_bit(wIndex, &ehci->port_c_suspend);

View file

@ -224,6 +224,7 @@ static int tegra_ehci_hub_control(
temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
/* start resume signalling */ /* start resume signalling */
ehci_writel(ehci, temp | PORT_RESUME, status_reg); ehci_writel(ehci, temp | PORT_RESUME, status_reg);
set_bit(wIndex-1, &ehci->resuming_ports);
spin_unlock_irqrestore(&ehci->lock, flags); spin_unlock_irqrestore(&ehci->lock, flags);
msleep(20); msleep(20);
@ -236,6 +237,7 @@ static int tegra_ehci_hub_control(
pr_err("%s: timeout waiting for SUSPEND\n", __func__); pr_err("%s: timeout waiting for SUSPEND\n", __func__);
ehci->reset_done[wIndex-1] = 0; ehci->reset_done[wIndex-1] = 0;
clear_bit(wIndex-1, &ehci->resuming_ports);
tegra->port_resuming = 1; tegra->port_resuming = 1;
goto done; goto done;

View file

@ -117,6 +117,8 @@ struct ehci_hcd { /* one per controller */
the change-suspend feature turned on */ the change-suspend feature turned on */
unsigned long suspended_ports; /* which ports are unsigned long suspended_ports; /* which ports are
suspended */ suspended */
unsigned long resuming_ports; /* which ports have
started to resume */
/* per-HC memory pools (could be per-bus, but ...) */ /* per-HC memory pools (could be per-bus, but ...) */
struct dma_pool *qh_pool; /* qh per active urb */ struct dma_pool *qh_pool; /* qh per active urb */

View file

@ -825,9 +825,13 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
} }
} }
/* Disable any BIOS SMIs */ val = readl(base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET);
writel(XHCI_LEGACY_DISABLE_SMI, /* Mask off (turn off) any enabled SMIs */
base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET); val &= XHCI_LEGACY_DISABLE_SMI;
/* Mask all SMI events bits, RW1C */
val |= XHCI_LEGACY_SMI_EVENTS;
/* Disable any BIOS SMIs and clear all SMI events*/
writel(val, base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET);
if (usb_is_intel_switchable_xhci(pdev)) if (usb_is_intel_switchable_xhci(pdev))
usb_enable_xhci_ports(pdev); usb_enable_xhci_ports(pdev);

View file

@ -196,11 +196,12 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
status = get_hub_status_data(uhci, buf); status = get_hub_status_data(uhci, buf);
switch (uhci->rh_state) { switch (uhci->rh_state) {
case UHCI_RH_SUSPENDING:
case UHCI_RH_SUSPENDED: case UHCI_RH_SUSPENDED:
/* if port change, ask to be resumed */ /* if port change, ask to be resumed */
if (status || uhci->resuming_ports) if (status || uhci->resuming_ports) {
status = 1;
usb_hcd_resume_root_hub(hcd); usb_hcd_resume_root_hub(hcd);
}
break; break;
case UHCI_RH_AUTO_STOPPED: case UHCI_RH_AUTO_STOPPED:

View file

@ -119,7 +119,7 @@ static void xhci_print_command_reg(struct xhci_hcd *xhci)
xhci_dbg(xhci, " Event Interrupts %s\n", xhci_dbg(xhci, " Event Interrupts %s\n",
(temp & CMD_EIE) ? "enabled " : "disabled"); (temp & CMD_EIE) ? "enabled " : "disabled");
xhci_dbg(xhci, " Host System Error Interrupts %s\n", xhci_dbg(xhci, " Host System Error Interrupts %s\n",
(temp & CMD_EIE) ? "enabled " : "disabled"); (temp & CMD_HSEIE) ? "enabled " : "disabled");
xhci_dbg(xhci, " HC has %sfinished light reset\n", xhci_dbg(xhci, " HC has %sfinished light reset\n",
(temp & CMD_LRESET) ? "not " : ""); (temp & CMD_LRESET) ? "not " : "");
} }

View file

@ -62,8 +62,9 @@
/* USB Legacy Support Control and Status Register - section 7.1.2 */ /* USB Legacy Support Control and Status Register - section 7.1.2 */
/* Add this offset, plus the value of xECP in HCCPARAMS to the base address */ /* Add this offset, plus the value of xECP in HCCPARAMS to the base address */
#define XHCI_LEGACY_CONTROL_OFFSET (0x04) #define XHCI_LEGACY_CONTROL_OFFSET (0x04)
/* bits 1:2, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */ /* bits 1:3, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */
#define XHCI_LEGACY_DISABLE_SMI ((0x3 << 1) + (0xff << 5) + (0x7 << 17)) #define XHCI_LEGACY_DISABLE_SMI ((0x7 << 1) + (0xff << 5) + (0x7 << 17))
#define XHCI_LEGACY_SMI_EVENTS (0x7 << 29)
/* USB 2.0 xHCI 0.96 L1C capability - section 7.2.2.1.3.2 */ /* USB 2.0 xHCI 0.96 L1C capability - section 7.2.2.1.3.2 */
#define XHCI_L1C (1 << 16) #define XHCI_L1C (1 << 16)

View file

@ -1796,11 +1796,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
int i; int i;
/* Free the Event Ring Segment Table and the actual Event Ring */ /* Free the Event Ring Segment Table and the actual Event Ring */
if (xhci->ir_set) {
xhci_writel(xhci, 0, &xhci->ir_set->erst_size);
xhci_write_64(xhci, 0, &xhci->ir_set->erst_base);
xhci_write_64(xhci, 0, &xhci->ir_set->erst_dequeue);
}
size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries); size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries);
if (xhci->erst.entries) if (xhci->erst.entries)
dma_free_coherent(&pdev->dev, size, dma_free_coherent(&pdev->dev, size,
@ -1812,7 +1807,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci->event_ring = NULL; xhci->event_ring = NULL;
xhci_dbg(xhci, "Freed event ring\n"); xhci_dbg(xhci, "Freed event ring\n");
xhci_write_64(xhci, 0, &xhci->op_regs->cmd_ring);
if (xhci->cmd_ring) if (xhci->cmd_ring)
xhci_ring_free(xhci, xhci->cmd_ring); xhci_ring_free(xhci, xhci->cmd_ring);
xhci->cmd_ring = NULL; xhci->cmd_ring = NULL;
@ -1841,7 +1835,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci->medium_streams_pool = NULL; xhci->medium_streams_pool = NULL;
xhci_dbg(xhci, "Freed medium stream array pool\n"); xhci_dbg(xhci, "Freed medium stream array pool\n");
xhci_write_64(xhci, 0, &xhci->op_regs->dcbaa_ptr);
if (xhci->dcbaa) if (xhci->dcbaa)
dma_free_coherent(&pdev->dev, sizeof(*xhci->dcbaa), dma_free_coherent(&pdev->dev, sizeof(*xhci->dcbaa),
xhci->dcbaa, xhci->dcbaa->dma); xhci->dcbaa, xhci->dcbaa->dma);
@ -2459,6 +2452,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
fail: fail:
xhci_warn(xhci, "Couldn't initialize memory\n"); xhci_warn(xhci, "Couldn't initialize memory\n");
xhci_halt(xhci);
xhci_reset(xhci);
xhci_mem_cleanup(xhci); xhci_mem_cleanup(xhci);
return -ENOMEM; return -ENOMEM;
} }

View file

@ -95,6 +95,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_RESET_ON_RESUME; xhci->quirks |= XHCI_RESET_ON_RESUME;
xhci_dbg(xhci, "QUIRK: Resetting on resume\n"); xhci_dbg(xhci, "QUIRK: Resetting on resume\n");
} }
if (pdev->vendor == PCI_VENDOR_ID_VIA)
xhci->quirks |= XHCI_RESET_ON_RESUME;
} }
/* called during probe() after chip reset completes */ /* called during probe() after chip reset completes */
@ -326,7 +328,7 @@ int __init xhci_register_pci(void)
return pci_register_driver(&xhci_pci_driver); return pci_register_driver(&xhci_pci_driver);
} }
void __exit xhci_unregister_pci(void) void xhci_unregister_pci(void)
{ {
pci_unregister_driver(&xhci_pci_driver); pci_unregister_driver(&xhci_pci_driver);
} }

View file

@ -2417,7 +2417,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
u32 irq_pending; u32 irq_pending;
/* Acknowledge the PCI interrupt */ /* Acknowledge the PCI interrupt */
irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending); irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending);
irq_pending |= 0x3; irq_pending |= IMAN_IP;
xhci_writel(xhci, irq_pending, &xhci->ir_set->irq_pending); xhci_writel(xhci, irq_pending, &xhci->ir_set->irq_pending);
} }
@ -2734,7 +2734,7 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
urb->dev->speed == USB_SPEED_FULL) urb->dev->speed == USB_SPEED_FULL)
urb->interval /= 8; urb->interval /= 8;
} }
return xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); return xhci_queue_bulk_tx(xhci, mem_flags, urb, slot_id, ep_index);
} }
/* /*
@ -3514,7 +3514,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
} }
ep_ring->num_trbs_free_temp = ep_ring->num_trbs_free; ep_ring->num_trbs_free_temp = ep_ring->num_trbs_free;
return xhci_queue_isoc_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index);
} }
/**** Command Ring Operations ****/ /**** Command Ring Operations ****/

View file

@ -106,6 +106,9 @@ int xhci_halt(struct xhci_hcd *xhci)
STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC); STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC);
if (!ret) if (!ret)
xhci->xhc_state |= XHCI_STATE_HALTED; xhci->xhc_state |= XHCI_STATE_HALTED;
else
xhci_warn(xhci, "Host not halted after %u microseconds.\n",
XHCI_MAX_HALT_USEC);
return ret; return ret;
} }
@ -664,11 +667,11 @@ static void xhci_save_registers(struct xhci_hcd *xhci)
xhci->s3.dev_nt = xhci_readl(xhci, &xhci->op_regs->dev_notification); xhci->s3.dev_nt = xhci_readl(xhci, &xhci->op_regs->dev_notification);
xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
xhci->s3.config_reg = xhci_readl(xhci, &xhci->op_regs->config_reg); xhci->s3.config_reg = xhci_readl(xhci, &xhci->op_regs->config_reg);
xhci->s3.irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending);
xhci->s3.irq_control = xhci_readl(xhci, &xhci->ir_set->irq_control);
xhci->s3.erst_size = xhci_readl(xhci, &xhci->ir_set->erst_size); xhci->s3.erst_size = xhci_readl(xhci, &xhci->ir_set->erst_size);
xhci->s3.erst_base = xhci_read_64(xhci, &xhci->ir_set->erst_base); xhci->s3.erst_base = xhci_read_64(xhci, &xhci->ir_set->erst_base);
xhci->s3.erst_dequeue = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); xhci->s3.erst_dequeue = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
xhci->s3.irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending);
xhci->s3.irq_control = xhci_readl(xhci, &xhci->ir_set->irq_control);
} }
static void xhci_restore_registers(struct xhci_hcd *xhci) static void xhci_restore_registers(struct xhci_hcd *xhci)
@ -677,10 +680,11 @@ static void xhci_restore_registers(struct xhci_hcd *xhci)
xhci_writel(xhci, xhci->s3.dev_nt, &xhci->op_regs->dev_notification); xhci_writel(xhci, xhci->s3.dev_nt, &xhci->op_regs->dev_notification);
xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr); xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr);
xhci_writel(xhci, xhci->s3.config_reg, &xhci->op_regs->config_reg); xhci_writel(xhci, xhci->s3.config_reg, &xhci->op_regs->config_reg);
xhci_writel(xhci, xhci->s3.irq_pending, &xhci->ir_set->irq_pending);
xhci_writel(xhci, xhci->s3.irq_control, &xhci->ir_set->irq_control);
xhci_writel(xhci, xhci->s3.erst_size, &xhci->ir_set->erst_size); xhci_writel(xhci, xhci->s3.erst_size, &xhci->ir_set->erst_size);
xhci_write_64(xhci, xhci->s3.erst_base, &xhci->ir_set->erst_base); xhci_write_64(xhci, xhci->s3.erst_base, &xhci->ir_set->erst_base);
xhci_write_64(xhci, xhci->s3.erst_dequeue, &xhci->ir_set->erst_dequeue);
xhci_writel(xhci, xhci->s3.irq_pending, &xhci->ir_set->irq_pending);
xhci_writel(xhci, xhci->s3.irq_control, &xhci->ir_set->irq_control);
} }
static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci) static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci)

View file

@ -205,6 +205,10 @@ struct xhci_op_regs {
#define CMD_PM_INDEX (1 << 11) #define CMD_PM_INDEX (1 << 11)
/* bits 12:31 are reserved (and should be preserved on writes). */ /* bits 12:31 are reserved (and should be preserved on writes). */
/* IMAN - Interrupt Management Register */
#define IMAN_IP (1 << 1)
#define IMAN_IE (1 << 0)
/* USBSTS - USB status - status bitmasks */ /* USBSTS - USB status - status bitmasks */
/* HC not running - set to 1 when run/stop bit is cleared. */ /* HC not running - set to 1 when run/stop bit is cleared. */
#define STS_HALT XHCI_STS_HALT #define STS_HALT XHCI_STS_HALT

View file

@ -60,8 +60,6 @@ static int usb_serial_device_probe(struct device *dev)
retval = -ENODEV; retval = -ENODEV;
goto exit; goto exit;
} }
if (port->dev_state != PORT_REGISTERING)
goto exit;
driver = port->serial->type; driver = port->serial->type;
if (driver->port_probe) { if (driver->port_probe) {
@ -98,9 +96,6 @@ static int usb_serial_device_remove(struct device *dev)
if (!port) if (!port)
return -ENODEV; return -ENODEV;
if (port->dev_state != PORT_UNREGISTERING)
return retval;
device_remove_file(&port->dev, &dev_attr_port_number); device_remove_file(&port->dev, &dev_attr_port_number);
driver = port->serial->type; driver = port->serial->type;

View file

@ -75,7 +75,8 @@ struct ftdi_private {
unsigned long last_dtr_rts; /* saved modem control outputs */ unsigned long last_dtr_rts; /* saved modem control outputs */
struct async_icount icount; struct async_icount icount;
wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
char prev_status, diff_status; /* Used for TIOCMIWAIT */ char prev_status; /* Used for TIOCMIWAIT */
bool dev_gone; /* Used to abort TIOCMIWAIT */
char transmit_empty; /* If transmitter is empty or not */ char transmit_empty; /* If transmitter is empty or not */
struct usb_serial_port *port; struct usb_serial_port *port;
__u16 interface; /* FT2232C, FT2232H or FT4232H port interface __u16 interface; /* FT2232C, FT2232H or FT4232H port interface
@ -1681,6 +1682,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
init_waitqueue_head(&priv->delta_msr_wait); init_waitqueue_head(&priv->delta_msr_wait);
priv->flags = ASYNC_LOW_LATENCY; priv->flags = ASYNC_LOW_LATENCY;
priv->dev_gone = false;
if (quirk && quirk->port_probe) if (quirk && quirk->port_probe)
quirk->port_probe(priv); quirk->port_probe(priv);
@ -1839,6 +1841,9 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port)
dbg("%s", __func__); dbg("%s", __func__);
priv->dev_gone = true;
wake_up_interruptible_all(&priv->delta_msr_wait);
remove_sysfs_attrs(port); remove_sysfs_attrs(port);
kref_put(&priv->kref, ftdi_sio_priv_release); kref_put(&priv->kref, ftdi_sio_priv_release);
@ -1982,17 +1987,19 @@ static int ftdi_process_packet(struct tty_struct *tty,
N.B. packet may be processed more than once, but differences N.B. packet may be processed more than once, but differences
are only processed once. */ are only processed once. */
status = packet[0] & FTDI_STATUS_B0_MASK; status = packet[0] & FTDI_STATUS_B0_MASK;
if (status & FTDI_RS0_CTS)
priv->icount.cts++;
if (status & FTDI_RS0_DSR)
priv->icount.dsr++;
if (status & FTDI_RS0_RI)
priv->icount.rng++;
if (status & FTDI_RS0_RLSD)
priv->icount.dcd++;
if (status != priv->prev_status) { if (status != priv->prev_status) {
priv->diff_status |= status ^ priv->prev_status; char diff_status = status ^ priv->prev_status;
wake_up_interruptible(&priv->delta_msr_wait);
if (diff_status & FTDI_RS0_CTS)
priv->icount.cts++;
if (diff_status & FTDI_RS0_DSR)
priv->icount.dsr++;
if (diff_status & FTDI_RS0_RI)
priv->icount.rng++;
if (diff_status & FTDI_RS0_RLSD)
priv->icount.dcd++;
wake_up_interruptible_all(&priv->delta_msr_wait);
priv->prev_status = status; priv->prev_status = status;
} }
@ -2395,15 +2402,12 @@ static int ftdi_ioctl(struct tty_struct *tty,
*/ */
case TIOCMIWAIT: case TIOCMIWAIT:
cprev = priv->icount; cprev = priv->icount;
while (1) { while (!priv->dev_gone) {
interruptible_sleep_on(&priv->delta_msr_wait); interruptible_sleep_on(&priv->delta_msr_wait);
/* see if a signal did it */ /* see if a signal did it */
if (signal_pending(current)) if (signal_pending(current))
return -ERESTARTSYS; return -ERESTARTSYS;
cnow = priv->icount; cnow = priv->icount;
if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
return -EIO; /* no change => error */
if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
@ -2412,7 +2416,7 @@ static int ftdi_ioctl(struct tty_struct *tty,
} }
cprev = cnow; cprev = cnow;
} }
/* not reached */ return -EIO;
break; break;
case TIOCSERGETLSR: case TIOCSERGETLSR:
return get_lsr_info(port, (struct serial_struct __user *)arg); return get_lsr_info(port, (struct serial_struct __user *)arg);

View file

@ -27,8 +27,8 @@
/* Product information. */ /* Product information. */
#define FOCUS_VENDOR_ID 0x0C2E #define FOCUS_VENDOR_ID 0x0C2E
#define FOCUS_PRODUCT_ID 0x0720 #define FOCUS_PRODUCT_ID_BI 0x0720
#define FOCUS_PRODUCT_ID_UNI 0x0710 #define FOCUS_PRODUCT_ID_UNI 0x0700
#define METROUSB_SET_REQUEST_TYPE 0x40 #define METROUSB_SET_REQUEST_TYPE 0x40
#define METROUSB_SET_MODEM_CTRL_REQUEST 10 #define METROUSB_SET_MODEM_CTRL_REQUEST 10
@ -47,7 +47,7 @@ struct metrousb_private {
/* Device table list. */ /* Device table list. */
static struct usb_device_id id_table[] = { static struct usb_device_id id_table[] = {
{ USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID) }, { USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_BI) },
{ USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_UNI) }, { USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_UNI) },
{ }, /* Terminating entry. */ { }, /* Terminating entry. */
}; };

View file

@ -708,6 +708,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_FULLSPEED) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_FULLSPEED) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_HIGHSPEED) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_HIGHSPEED) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED3) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED3) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED4) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED4) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED5) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_HIGHSPEED5) },

View file

@ -420,7 +420,7 @@ static void pl2303_set_termios(struct tty_struct *tty,
control = priv->line_control; control = priv->line_control;
if ((cflag & CBAUD) == B0) if ((cflag & CBAUD) == B0)
priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS); priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
else else if ((old_termios->c_cflag & CBAUD) == B0)
priv->line_control |= (CONTROL_DTR | CONTROL_RTS); priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
if (control != priv->line_control) { if (control != priv->line_control) {
control = priv->line_control; control = priv->line_control;

View file

@ -289,6 +289,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x1199, 0x6856) }, /* Sierra Wireless AirCard 881 U */ { USB_DEVICE(0x1199, 0x6856) }, /* Sierra Wireless AirCard 881 U */
{ USB_DEVICE(0x1199, 0x6859) }, /* Sierra Wireless AirCard 885 E */ { USB_DEVICE(0x1199, 0x6859) }, /* Sierra Wireless AirCard 885 E */
{ USB_DEVICE(0x1199, 0x685A) }, /* Sierra Wireless AirCard 885 E */ { USB_DEVICE(0x1199, 0x685A) }, /* Sierra Wireless AirCard 885 E */
{ USB_DEVICE(0x1199, 0x68A2) }, /* Sierra Wireless MC7710 */
/* Sierra Wireless C885 */ /* Sierra Wireless C885 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6880, 0xFF, 0xFF, 0xFF)}, { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6880, 0xFF, 0xFF, 0xFF)},
/* Sierra Wireless C888, Air Card 501, USB 303, USB 304 */ /* Sierra Wireless C888, Air Card 501, USB 303, USB 304 */

View file

@ -1059,6 +1059,12 @@ int usb_serial_probe(struct usb_interface *interface,
serial->attached = 1; serial->attached = 1;
} }
/* Avoid race with tty_open and serial_install by setting the
* disconnected flag and not clearing it until all ports have been
* registered.
*/
serial->disconnected = 1;
if (get_free_serial(serial, num_ports, &minor) == NULL) { if (get_free_serial(serial, num_ports, &minor) == NULL) {
dev_err(&interface->dev, "No more free serial devices\n"); dev_err(&interface->dev, "No more free serial devices\n");
goto probe_error; goto probe_error;
@ -1070,19 +1076,16 @@ int usb_serial_probe(struct usb_interface *interface,
port = serial->port[i]; port = serial->port[i];
dev_set_name(&port->dev, "ttyUSB%d", port->number); dev_set_name(&port->dev, "ttyUSB%d", port->number);
dbg ("%s - registering %s", __func__, dev_name(&port->dev)); dbg ("%s - registering %s", __func__, dev_name(&port->dev));
port->dev_state = PORT_REGISTERING;
device_enable_async_suspend(&port->dev); device_enable_async_suspend(&port->dev);
retval = device_add(&port->dev); retval = device_add(&port->dev);
if (retval) { if (retval)
dev_err(&port->dev, "Error registering port device, " dev_err(&port->dev, "Error registering port device, "
"continuing\n"); "continuing\n");
port->dev_state = PORT_UNREGISTERED;
} else {
port->dev_state = PORT_REGISTERED;
}
} }
serial->disconnected = 0;
usb_serial_console_init(debug, minor); usb_serial_console_init(debug, minor);
exit: exit:
@ -1124,22 +1127,8 @@ void usb_serial_disconnect(struct usb_interface *interface)
} }
kill_traffic(port); kill_traffic(port);
cancel_work_sync(&port->work); cancel_work_sync(&port->work);
if (port->dev_state == PORT_REGISTERED) { if (device_is_registered(&port->dev))
/* Make sure the port is bound so that the
* driver's port_remove method is called.
*/
if (!port->dev.driver) {
int rc;
port->dev.driver =
&serial->type->driver;
rc = device_bind_driver(&port->dev);
}
port->dev_state = PORT_UNREGISTERING;
device_del(&port->dev); device_del(&port->dev);
port->dev_state = PORT_UNREGISTERED;
}
} }
} }
serial->type->disconnect(serial); serial->type->disconnect(serial);

View file

@ -132,6 +132,35 @@ static struct us_unusual_dev for_dynamic_ids =
#undef COMPLIANT_DEV #undef COMPLIANT_DEV
#undef USUAL_DEV #undef USUAL_DEV
#ifdef CONFIG_LOCKDEP
static struct lock_class_key us_interface_key[USB_MAXINTERFACES];
static void us_set_lock_class(struct mutex *mutex,
struct usb_interface *intf)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_host_config *config = udev->actconfig;
int i;
for (i = 0; i < config->desc.bNumInterfaces; i++) {
if (config->interface[i] == intf)
break;
}
BUG_ON(i == config->desc.bNumInterfaces);
lockdep_set_class(mutex, &us_interface_key[i]);
}
#else
static void us_set_lock_class(struct mutex *mutex,
struct usb_interface *intf)
{
}
#endif
#ifdef CONFIG_PM /* Minimal support for suspend and resume */ #ifdef CONFIG_PM /* Minimal support for suspend and resume */
@ -895,6 +924,7 @@ int usb_stor_probe1(struct us_data **pus,
*pus = us = host_to_us(host); *pus = us = host_to_us(host);
memset(us, 0, sizeof(struct us_data)); memset(us, 0, sizeof(struct us_data));
mutex_init(&(us->dev_mutex)); mutex_init(&(us->dev_mutex));
us_set_lock_class(&us->dev_mutex, intf);
init_completion(&us->cmnd_ready); init_completion(&us->cmnd_ready);
init_completion(&(us->notify)); init_completion(&(us->notify));
init_waitqueue_head(&us->delay_wait); init_waitqueue_head(&us->delay_wait);

View file

@ -28,13 +28,6 @@
/* parity check flag */ /* parity check flag */
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) #define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
enum port_dev_state {
PORT_UNREGISTERED,
PORT_REGISTERING,
PORT_REGISTERED,
PORT_UNREGISTERING,
};
/* USB serial flags */ /* USB serial flags */
#define USB_SERIAL_WRITE_BUSY 0 #define USB_SERIAL_WRITE_BUSY 0
@ -124,7 +117,6 @@ struct usb_serial_port {
char throttle_req; char throttle_req;
unsigned long sysrq; /* sysrq timeout */ unsigned long sysrq; /* sysrq timeout */
struct device dev; struct device dev;
enum port_dev_state dev_state;
}; };
#define to_usb_serial_port(d) container_of(d, struct usb_serial_port, dev) #define to_usb_serial_port(d) container_of(d, struct usb_serial_port, dev)