Commit 54515fe528d8c6f9bfaf7d0b9fffb908deecad78

Authored by Alan Stern
Committed by Greg Kroah-Hartman
1 parent f07600cf9e

USB: unify reset_resume and normal resume

This patch (as919) unifies the code paths used for normal resume and
for reset-resume.  Earlier I had failed to note a section in the USB
spec which requires the host to resume a suspended port before
resetting it if the attached device is enabled for remote wakeup.
Since the port has to be resumed anyway, we might as well reuse the
existing code.

The main changes are:

	usb_reset_suspended_device() is eliminated.

	usb_root_hub_lost_power() is moved down next to the
	hub_reset_resume() routine, to which it is logically
	related.

	finish_port_resume() does a port reset() if the device's
	reset_resume flag is set.

	usb_port_resume() doesn't check whether the port is initially
	enabled if this is a USB-Persist sort of resume.

	Code to perform the port reset is added to the resume pathway
	for the non-CONFIG_USB_SUSPEND case.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

Showing 3 changed files with 93 additions and 94 deletions Side-by-side Diff

drivers/usb/core/generic.c
... ... @@ -219,8 +219,6 @@
219 219 */
220 220 if (!udev->parent)
221 221 rc = hcd_bus_resume(udev);
222   - else if (udev->reset_resume)
223   - rc = usb_reset_suspended_device(udev);
224 222 else
225 223 rc = usb_port_resume(udev);
226 224 return rc;
drivers/usb/core/hub.c
... ... @@ -31,6 +31,12 @@
31 31 #include "hcd.h"
32 32 #include "hub.h"
33 33  
  34 +#ifdef CONFIG_USB_PERSIST
  35 +#define USB_PERSIST 1
  36 +#else
  37 +#define USB_PERSIST 0
  38 +#endif
  39 +
34 40 struct usb_hub {
35 41 struct device *intfdev; /* the "interface" device */
36 42 struct usb_device *hdev;
... ... @@ -1080,72 +1086,6 @@
1080 1086 spin_unlock_irqrestore(&device_state_lock, flags);
1081 1087 }
1082 1088  
1083   -
1084   -#ifdef CONFIG_PM
1085   -
1086   -/**
1087   - * usb_reset_suspended_device - reset a suspended device instead of resuming it
1088   - * @udev: device to be reset instead of resumed
1089   - *
1090   - * If a host controller doesn't maintain VBUS suspend current during a
1091   - * system sleep or is reset when the system wakes up, all the USB
1092   - * power sessions below it will be broken. This is especially troublesome
1093   - * for mass-storage devices containing mounted filesystems, since the
1094   - * device will appear to have disconnected and all the memory mappings
1095   - * to it will be lost.
1096   - *
1097   - * As an alternative, this routine attempts to recover power sessions for
1098   - * devices that are still present by resetting them instead of resuming
1099   - * them. If all goes well, the devices will appear to persist across the
1100   - * the interruption of the power sessions.
1101   - *
1102   - * This facility is inherently dangerous. Although usb_reset_device()
1103   - * makes every effort to insure that the same device is present after the
1104   - * reset as before, it cannot provide a 100% guarantee. Furthermore it's
1105   - * quite possible for a device to remain unaltered but its media to be
1106   - * changed. If the user replaces a flash memory card while the system is
1107   - * asleep, he will have only himself to blame when the filesystem on the
1108   - * new card is corrupted and the system crashes.
1109   - */
1110   -int usb_reset_suspended_device(struct usb_device *udev)
1111   -{
1112   - int rc = 0;
1113   -
1114   - dev_dbg(&udev->dev, "usb %sresume\n", "reset-");
1115   -
1116   - /* After we're done the device won't be suspended any more.
1117   - * In addition, the reset won't work if udev->state is SUSPENDED.
1118   - */
1119   - usb_set_device_state(udev, udev->actconfig
1120   - ? USB_STATE_CONFIGURED
1121   - : USB_STATE_ADDRESS);
1122   -
1123   - /* Root hubs don't need to be (and can't be) reset */
1124   - if (udev->parent)
1125   - rc = usb_reset_device(udev);
1126   - return rc;
1127   -}
1128   -
1129   -/**
1130   - * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
1131   - * @rhdev: struct usb_device for the root hub
1132   - *
1133   - * The USB host controller driver calls this function when its root hub
1134   - * is resumed and Vbus power has been interrupted or the controller
1135   - * has been reset. The routine marks @rhdev as having lost power. When
1136   - * the hub driver is resumed it will take notice; if CONFIG_USB_PERSIST
1137   - * is enabled then it will carry out power-session recovery, otherwise
1138   - * it will disconnect all the child devices.
1139   - */
1140   -void usb_root_hub_lost_power(struct usb_device *rhdev)
1141   -{
1142   - dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
1143   - rhdev->reset_resume = 1;
1144   -}
1145   -EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
1146   -
1147   -#endif /* CONFIG_PM */
1148   -
1149 1089 static void choose_address(struct usb_device *udev)
1150 1090 {
1151 1091 int devnum;
1152 1092  
1153 1093  
1154 1094  
... ... @@ -1672,18 +1612,22 @@
1672 1612 /*
1673 1613 * If the USB "suspend" state is in use (rather than "global suspend"),
1674 1614 * many devices will be individually taken out of suspend state using
1675   - * special" resume" signaling. These routines kick in shortly after
  1615 + * special "resume" signaling. This routine kicks in shortly after
1676 1616 * hardware resume signaling is finished, either because of selective
1677 1617 * resume (by host) or remote wakeup (by device) ... now see what changed
1678 1618 * in the tree that's rooted at this device.
  1619 + *
  1620 + * If @udev->reset_resume is set then the device is reset before the
  1621 + * status check is done.
1679 1622 */
1680 1623 static int finish_port_resume(struct usb_device *udev)
1681 1624 {
1682   - int status;
  1625 + int status = 0;
1683 1626 u16 devstatus;
1684 1627  
1685 1628 /* caller owns the udev device lock */
1686   - dev_dbg(&udev->dev, "finish resume\n");
  1629 + dev_dbg(&udev->dev, "finish %sresume\n",
  1630 + udev->reset_resume ? "reset-" : "");
1687 1631  
1688 1632 /* usb ch9 identifies four variants of SUSPENDED, based on what
1689 1633 * state the device resumes to. Linux currently won't see the
1690 1634  
... ... @@ -1694,13 +1638,23 @@
1694 1638 ? USB_STATE_CONFIGURED
1695 1639 : USB_STATE_ADDRESS);
1696 1640  
  1641 + /* 10.5.4.5 says not to reset a suspended port if the attached
  1642 + * device is enabled for remote wakeup. Hence the reset
  1643 + * operation is carried out here, after the port has been
  1644 + * resumed.
  1645 + */
  1646 + if (udev->reset_resume)
  1647 + status = usb_reset_device(udev);
  1648 +
1697 1649 /* 10.5.4.5 says be sure devices in the tree are still there.
1698 1650 * For now let's assume the device didn't go crazy on resume,
1699 1651 * and device drivers will know about any resume quirks.
1700 1652 */
1701   - status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
1702   - if (status >= 0)
1703   - status = (status == 2 ? 0 : -ENODEV);
  1653 + if (status == 0) {
  1654 + status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
  1655 + if (status >= 0)
  1656 + status = (status == 2 ? 0 : -ENODEV);
  1657 + }
1704 1658  
1705 1659 if (status) {
1706 1660 dev_dbg(&udev->dev, "gone after usb resume? status %d\n",
... ... @@ -1735,6 +1689,28 @@
1735 1689 * the host and the device is the same as it was when the device
1736 1690 * suspended.
1737 1691 *
  1692 + * If CONFIG_USB_PERSIST and @udev->reset_resume are both set then this
  1693 + * routine won't check that the port is still enabled. Furthermore,
  1694 + * if @udev->reset_resume is set then finish_port_resume() above will
  1695 + * reset @udev. The end result is that a broken power session can be
  1696 + * recovered and @udev will appear to persist across a loss of VBUS power.
  1697 + *
  1698 + * For example, if a host controller doesn't maintain VBUS suspend current
  1699 + * during a system sleep or is reset when the system wakes up, all the USB
  1700 + * power sessions below it will be broken. This is especially troublesome
  1701 + * for mass-storage devices containing mounted filesystems, since the
  1702 + * device will appear to have disconnected and all the memory mappings
  1703 + * to it will be lost. Using the USB_PERSIST facility, the device can be
  1704 + * made to appear as if it had not disconnected.
  1705 + *
  1706 + * This facility is inherently dangerous. Although usb_reset_device()
  1707 + * makes every effort to insure that the same device is present after the
  1708 + * reset as before, it cannot provide a 100% guarantee. Furthermore it's
  1709 + * quite possible for a device to remain unaltered but its media to be
  1710 + * changed. If the user replaces a flash memory card while the system is
  1711 + * asleep, he will have only himself to blame when the filesystem on the
  1712 + * new card is corrupted and the system crashes.
  1713 + *
1738 1714 * Returns 0 on success, else negative errno.
1739 1715 */
1740 1716 int usb_port_resume(struct usb_device *udev)
... ... @@ -1743,6 +1719,7 @@
1743 1719 int port1 = udev->portnum;
1744 1720 int status;
1745 1721 u16 portchange, portstatus;
  1722 + unsigned mask_flags, want_flags;
1746 1723  
1747 1724 /* Skip the initial Clear-Suspend step for a remote wakeup */
1748 1725 status = hub_port_status(hub, port1, &portstatus, &portchange);
1749 1726  
... ... @@ -1765,20 +1742,23 @@
1765 1742 udev->auto_pm ? "auto-" : "");
1766 1743 msleep(25);
1767 1744  
1768   -#define LIVE_FLAGS ( USB_PORT_STAT_POWER \
1769   - | USB_PORT_STAT_ENABLE \
1770   - | USB_PORT_STAT_CONNECTION)
1771   -
1772 1745 /* Virtual root hubs can trigger on GET_PORT_STATUS to
1773 1746 * stop resume signaling. Then finish the resume
1774 1747 * sequence.
1775 1748 */
1776 1749 status = hub_port_status(hub, port1, &portstatus, &portchange);
1777   -SuspendCleared:
1778   - if (status < 0
1779   - || (portstatus & LIVE_FLAGS) != LIVE_FLAGS
1780   - || (portstatus & USB_PORT_STAT_SUSPEND) != 0
1781   - ) {
  1750 +
  1751 + SuspendCleared:
  1752 + if (USB_PERSIST && udev->reset_resume)
  1753 + want_flags = USB_PORT_STAT_POWER
  1754 + | USB_PORT_STAT_CONNECTION;
  1755 + else
  1756 + want_flags = USB_PORT_STAT_POWER
  1757 + | USB_PORT_STAT_CONNECTION
  1758 + | USB_PORT_STAT_ENABLE;
  1759 + mask_flags = want_flags | USB_PORT_STAT_SUSPEND;
  1760 +
  1761 + if (status < 0 || (portstatus & mask_flags) != want_flags) {
1782 1762 dev_dbg(hub->intfdev,
1783 1763 "port %d status %04x.%04x after resume, %d\n",
1784 1764 port1, portchange, portstatus, status);
1785 1765  
1786 1766  
... ... @@ -1790,18 +1770,19 @@
1790 1770 USB_PORT_FEAT_C_SUSPEND);
1791 1771 /* TRSMRCY = 10 msec */
1792 1772 msleep(10);
1793   - status = finish_port_resume(udev);
1794 1773 }
1795 1774 }
1796   - if (status < 0) {
1797   - dev_dbg(&udev->dev, "can't resume, status %d\n", status);
1798   - hub_port_logical_disconnect(hub, port1);
1799   - }
1800 1775  
1801 1776 clear_bit(port1, hub->busy_bits);
1802 1777 if (!hub->hdev->parent && !hub->busy_bits[0])
1803 1778 usb_enable_root_hub_irq(hub->hdev->bus);
1804 1779  
  1780 + if (status == 0)
  1781 + status = finish_port_resume(udev);
  1782 + if (status < 0) {
  1783 + dev_dbg(&udev->dev, "can't resume, status %d\n", status);
  1784 + hub_port_logical_disconnect(hub, port1);
  1785 + }
1805 1786 return status;
1806 1787 }
1807 1788  
... ... @@ -1830,7 +1811,14 @@
1830 1811  
1831 1812 int usb_port_resume(struct usb_device *udev)
1832 1813 {
1833   - return 0;
  1814 + int status = 0;
  1815 +
  1816 + /* However we may need to do a reset-resume */
  1817 + if (udev->reset_resume) {
  1818 + dev_dbg(&udev->dev, "reset-resume\n");
  1819 + status = usb_reset_device(udev);
  1820 + }
  1821 + return status;
1834 1822 }
1835 1823  
1836 1824 static inline int remote_wakeup(struct usb_device *udev)
... ... @@ -1886,8 +1874,6 @@
1886 1874  
1887 1875 #ifdef CONFIG_USB_PERSIST
1888 1876  
1889   -#define USB_PERSIST 1
1890   -
1891 1877 /* For "persistent-device" resets we must mark the child devices for reset
1892 1878 * and turn off a possible connect-change status (so khubd won't disconnect
1893 1879 * them later).
... ... @@ -1910,8 +1896,6 @@
1910 1896  
1911 1897 #else
1912 1898  
1913   -#define USB_PERSIST 0
1914   -
1915 1899 static inline void mark_children_for_reset_resume(struct usb_hub *hub)
1916 1900 { }
1917 1901  
... ... @@ -1935,6 +1919,24 @@
1935 1919 hub_activate(hub);
1936 1920 return 0;
1937 1921 }
  1922 +
  1923 +/**
  1924 + * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
  1925 + * @rhdev: struct usb_device for the root hub
  1926 + *
  1927 + * The USB host controller driver calls this function when its root hub
  1928 + * is resumed and Vbus power has been interrupted or the controller
  1929 + * has been reset. The routine marks @rhdev as having lost power. When
  1930 + * the hub driver is resumed it will take notice; if CONFIG_USB_PERSIST
  1931 + * is enabled then it will carry out power-session recovery, otherwise
  1932 + * it will disconnect all the child devices.
  1933 + */
  1934 +void usb_root_hub_lost_power(struct usb_device *rhdev)
  1935 +{
  1936 + dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
  1937 + rhdev->reset_resume = 1;
  1938 +}
  1939 +EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
1938 1940  
1939 1941 #else /* CONFIG_PM */
1940 1942  
drivers/usb/core/usb.h
... ... @@ -36,7 +36,6 @@
36 36 extern void usb_autosuspend_work(struct work_struct *work);
37 37 extern int usb_port_suspend(struct usb_device *dev);
38 38 extern int usb_port_resume(struct usb_device *dev);
39   -extern int usb_reset_suspended_device(struct usb_device *udev);
40 39 extern int usb_external_suspend_device(struct usb_device *udev,
41 40 pm_message_t msg);
42 41 extern int usb_external_resume_device(struct usb_device *udev);