mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-03-05 04:30:05 +07:00
[PATCH] root hub updates (greater half)
This patch associates hub suspend and resume logic (including for root hubs) with CONFIG_PM -- instead of CONFIG_USB_SUSPEND as before -- thereby unifying two troublesome versions of suspend logic into just one. It'll be easier to keep things right from now on. - Now usbcore _always_ calls hcd->hub_suspend as needed, instead of only when USB_SUSPEND is enabled: * Those root hub methods are now called from hub suspend/resume; no more skipping between layers during device suspend/resume; * It now handles cases allowed by sysfs or autosuspended root hubs, by forcing the hub interface to resume too. - All devices, including virtual root hubs, now get the same treatment on their resume paths ... including re-activating all their interfaces. Plus it gets rid of those stub copies of usb_{suspend,resume}_device(), and updates the Kconfig to match the new definition of USB_SUSPEND: it provides (a) selective suspend, downstream from hubs; and (b) remote wakeup, upstream from any device configuration which supports it. This calls for minor followup patches for most HCDs (and their PCI glue). Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> drivers/usb/core/Kconfig | 11 ++- drivers/usb/core/hub.c | 163 +++++++++++++++++++++++++---------------------- 2 files changed, 97 insertions(+), 77 deletions(-)
This commit is contained in:
parent
979d5199fe
commit
f3f3253d64
@ -61,14 +61,17 @@ config USB_DYNAMIC_MINORS
|
|||||||
If you are unsure about this, say N here.
|
If you are unsure about this, say N here.
|
||||||
|
|
||||||
config USB_SUSPEND
|
config USB_SUSPEND
|
||||||
bool "USB suspend/resume (EXPERIMENTAL)"
|
bool "USB selective suspend/resume and wakeup (EXPERIMENTAL)"
|
||||||
depends on USB && PM && EXPERIMENTAL
|
depends on USB && PM && EXPERIMENTAL
|
||||||
help
|
help
|
||||||
If you say Y here, you can use driver calls or the sysfs
|
If you say Y here, you can use driver calls or the sysfs
|
||||||
"power/state" file to suspend or resume individual USB
|
"power/state" file to suspend or resume individual USB
|
||||||
peripherals. There are many related features, such as
|
peripherals.
|
||||||
remote wakeup and driver-specific suspend processing, that
|
|
||||||
may not yet work as expected.
|
Also, USB "remote wakeup" signaling is supported, whereby some
|
||||||
|
USB devices (like keyboards and network adapters) can wake up
|
||||||
|
their parent hub. That wakeup cascades up the USB tree, and
|
||||||
|
could wake the system from states like suspend-to-RAM.
|
||||||
|
|
||||||
If you are unsure about this, say N here.
|
If you are unsure about this, say N here.
|
||||||
|
|
||||||
|
@ -1612,7 +1612,7 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
|
|||||||
*/
|
*/
|
||||||
static int __usb_suspend_device (struct usb_device *udev, int port1)
|
static int __usb_suspend_device (struct usb_device *udev, int port1)
|
||||||
{
|
{
|
||||||
int status;
|
int status = 0;
|
||||||
|
|
||||||
/* caller owns the udev device lock */
|
/* caller owns the udev device lock */
|
||||||
if (port1 < 0)
|
if (port1 < 0)
|
||||||
@ -1638,21 +1638,10 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* "global suspend" of the HC-to-USB interface (root hub), or
|
/* we only change a device's upstream USB link.
|
||||||
* "selective suspend" of just one hub-device link.
|
* root hubs have no upstream USB link.
|
||||||
*/
|
*/
|
||||||
if (!udev->parent) {
|
if (udev->parent)
|
||||||
struct usb_bus *bus = udev->bus;
|
|
||||||
if (bus && bus->op->hub_suspend) {
|
|
||||||
status = bus->op->hub_suspend (bus);
|
|
||||||
if (status == 0) {
|
|
||||||
dev_dbg(&udev->dev, "usb suspend\n");
|
|
||||||
usb_set_device_state(udev,
|
|
||||||
USB_STATE_SUSPENDED);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
status = -EOPNOTSUPP;
|
|
||||||
} else
|
|
||||||
status = hub_port_suspend(hdev_to_hub(udev->parent), port1,
|
status = hub_port_suspend(hdev_to_hub(udev->parent), port1,
|
||||||
udev);
|
udev);
|
||||||
|
|
||||||
@ -1661,6 +1650,8 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* usb_suspend_device - suspend a usb device
|
* usb_suspend_device - suspend a usb device
|
||||||
* @udev: device that's no longer in active use
|
* @udev: device that's no longer in active use
|
||||||
@ -1683,6 +1674,7 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
|
|||||||
*/
|
*/
|
||||||
int usb_suspend_device(struct usb_device *udev)
|
int usb_suspend_device(struct usb_device *udev)
|
||||||
{
|
{
|
||||||
|
#ifdef CONFIG_USB_SUSPEND
|
||||||
int port1, status;
|
int port1, status;
|
||||||
|
|
||||||
port1 = locktree(udev);
|
port1 = locktree(udev);
|
||||||
@ -1692,8 +1684,14 @@ int usb_suspend_device(struct usb_device *udev)
|
|||||||
status = __usb_suspend_device(udev, port1);
|
status = __usb_suspend_device(udev, port1);
|
||||||
usb_unlock_device(udev);
|
usb_unlock_device(udev);
|
||||||
return status;
|
return status;
|
||||||
|
#else
|
||||||
|
/* NOTE: udev->state unchanged, it's not lying ... */
|
||||||
|
udev->dev.power.power_state = PMSG_SUSPEND;
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the USB "suspend" state is in use (rather than "global suspend"),
|
* If the USB "suspend" state is in use (rather than "global suspend"),
|
||||||
* many devices will be individually taken out of suspend state using
|
* many devices will be individually taken out of suspend state using
|
||||||
@ -1702,13 +1700,13 @@ int usb_suspend_device(struct usb_device *udev)
|
|||||||
* resume (by host) or remote wakeup (by device) ... now see what changed
|
* resume (by host) or remote wakeup (by device) ... now see what changed
|
||||||
* in the tree that's rooted at this device.
|
* in the tree that's rooted at this device.
|
||||||
*/
|
*/
|
||||||
static int finish_port_resume(struct usb_device *udev)
|
static int finish_device_resume(struct usb_device *udev)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
u16 devstatus;
|
u16 devstatus;
|
||||||
|
|
||||||
/* caller owns the udev device lock */
|
/* caller owns the udev device lock */
|
||||||
dev_dbg(&udev->dev, "usb resume\n");
|
dev_dbg(&udev->dev, "finish resume\n");
|
||||||
|
|
||||||
/* usb ch9 identifies four variants of SUSPENDED, based on what
|
/* usb ch9 identifies four variants of SUSPENDED, based on what
|
||||||
* state the device resumes to. Linux currently won't see the
|
* state the device resumes to. Linux currently won't see the
|
||||||
@ -1718,7 +1716,6 @@ static int finish_port_resume(struct usb_device *udev)
|
|||||||
usb_set_device_state(udev, udev->actconfig
|
usb_set_device_state(udev, udev->actconfig
|
||||||
? USB_STATE_CONFIGURED
|
? USB_STATE_CONFIGURED
|
||||||
: USB_STATE_ADDRESS);
|
: USB_STATE_ADDRESS);
|
||||||
udev->dev.power.power_state = PMSG_ON;
|
|
||||||
|
|
||||||
/* 10.5.4.5 says be sure devices in the tree are still there.
|
/* 10.5.4.5 says be sure devices in the tree are still there.
|
||||||
* For now let's assume the device didn't go crazy on resume,
|
* For now let's assume the device didn't go crazy on resume,
|
||||||
@ -1734,7 +1731,8 @@ static int finish_port_resume(struct usb_device *udev)
|
|||||||
int (*resume)(struct device *);
|
int (*resume)(struct device *);
|
||||||
|
|
||||||
le16_to_cpus(&devstatus);
|
le16_to_cpus(&devstatus);
|
||||||
if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
|
if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)
|
||||||
|
&& udev->parent) {
|
||||||
status = usb_control_msg(udev,
|
status = usb_control_msg(udev,
|
||||||
usb_sndctrlpipe(udev, 0),
|
usb_sndctrlpipe(udev, 0),
|
||||||
USB_REQ_CLEAR_FEATURE,
|
USB_REQ_CLEAR_FEATURE,
|
||||||
@ -1764,6 +1762,8 @@ static int finish_port_resume(struct usb_device *udev)
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_USB_SUSPEND
|
||||||
|
|
||||||
static int
|
static int
|
||||||
hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
|
hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
|
||||||
{
|
{
|
||||||
@ -1809,7 +1809,7 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
|
|||||||
/* TRSMRCY = 10 msec */
|
/* TRSMRCY = 10 msec */
|
||||||
msleep(10);
|
msleep(10);
|
||||||
if (udev)
|
if (udev)
|
||||||
status = finish_port_resume(udev);
|
status = finish_device_resume(udev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
@ -1818,7 +1818,7 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hub_resume (struct usb_interface *intf);
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* usb_resume_device - re-activate a suspended usb device
|
* usb_resume_device - re-activate a suspended usb device
|
||||||
@ -1841,35 +1841,22 @@ int usb_resume_device(struct usb_device *udev)
|
|||||||
if (port1 < 0)
|
if (port1 < 0)
|
||||||
return port1;
|
return port1;
|
||||||
|
|
||||||
/* "global resume" of the HC-to-USB interface (root hub), or
|
#ifdef CONFIG_USB_SUSPEND
|
||||||
* selective resume of one hub-to-device port
|
/* selective resume of one downstream hub-to-device port */
|
||||||
*/
|
if (udev->parent) {
|
||||||
if (!udev->parent) {
|
if (udev->state == USB_STATE_SUSPENDED) {
|
||||||
struct usb_bus *bus = udev->bus;
|
// NOTE swsusp may bork us, device state being wrong...
|
||||||
if (bus && bus->op->hub_resume) {
|
// NOTE this fails if parent is also suspended...
|
||||||
status = bus->op->hub_resume (bus);
|
status = hub_port_resume(hdev_to_hub(udev->parent),
|
||||||
|
port1, udev);
|
||||||
} else
|
} else
|
||||||
status = -EOPNOTSUPP;
|
status = 0;
|
||||||
if (status == 0) {
|
} else
|
||||||
dev_dbg(&udev->dev, "usb resume\n");
|
#endif
|
||||||
/* TRSMRCY = 10 msec */
|
status = finish_device_resume(udev);
|
||||||
msleep(10);
|
if (status < 0)
|
||||||
usb_set_device_state (udev, USB_STATE_CONFIGURED);
|
|
||||||
udev->dev.power.power_state = PMSG_ON;
|
|
||||||
status = hub_resume (udev
|
|
||||||
->actconfig->interface[0]);
|
|
||||||
}
|
|
||||||
} else if (udev->state == USB_STATE_SUSPENDED) {
|
|
||||||
// NOTE this fails if parent is also suspended...
|
|
||||||
status = hub_port_resume(hdev_to_hub(udev->parent),
|
|
||||||
port1, udev);
|
|
||||||
} else {
|
|
||||||
status = 0;
|
|
||||||
}
|
|
||||||
if (status < 0) {
|
|
||||||
dev_dbg(&udev->dev, "can't resume, status %d\n",
|
dev_dbg(&udev->dev, "can't resume, status %d\n",
|
||||||
status);
|
status);
|
||||||
}
|
|
||||||
|
|
||||||
usb_unlock_device(udev);
|
usb_unlock_device(udev);
|
||||||
|
|
||||||
@ -1886,6 +1873,8 @@ static int remote_wakeup(struct usb_device *udev)
|
|||||||
{
|
{
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
||||||
|
#ifdef CONFIG_USB_SUSPEND
|
||||||
|
|
||||||
/* don't repeat RESUME sequence if this device
|
/* don't repeat RESUME sequence if this device
|
||||||
* was already woken up by some other task
|
* was already woken up by some other task
|
||||||
*/
|
*/
|
||||||
@ -1894,9 +1883,10 @@ static int remote_wakeup(struct usb_device *udev)
|
|||||||
dev_dbg(&udev->dev, "RESUME (wakeup)\n");
|
dev_dbg(&udev->dev, "RESUME (wakeup)\n");
|
||||||
/* TRSMRCY = 10 msec */
|
/* TRSMRCY = 10 msec */
|
||||||
msleep(10);
|
msleep(10);
|
||||||
status = finish_port_resume(udev);
|
status = finish_device_resume(udev);
|
||||||
}
|
}
|
||||||
up(&udev->serialize);
|
up(&udev->serialize);
|
||||||
|
#endif
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1911,12 +1901,32 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
|
|||||||
struct usb_device *udev;
|
struct usb_device *udev;
|
||||||
|
|
||||||
udev = hdev->children [port1-1];
|
udev = hdev->children [port1-1];
|
||||||
if (udev && udev->state != USB_STATE_SUSPENDED) {
|
if (udev && (udev->dev.power.power_state.event
|
||||||
|
== PM_EVENT_ON
|
||||||
|
#ifdef CONFIG_USB_SUSPEND
|
||||||
|
|| udev->state != USB_STATE_SUSPENDED
|
||||||
|
#endif
|
||||||
|
)) {
|
||||||
dev_dbg(&intf->dev, "port %d nyet suspended\n", port1);
|
dev_dbg(&intf->dev, "port %d nyet suspended\n", port1);
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* "global suspend" of the downstream HC-to-USB interface */
|
||||||
|
if (!hdev->parent) {
|
||||||
|
struct usb_bus *bus = hdev->bus;
|
||||||
|
if (bus && bus->op->hub_suspend) {
|
||||||
|
int status = bus->op->hub_suspend (bus);
|
||||||
|
|
||||||
|
if (status != 0) {
|
||||||
|
dev_dbg(&hdev->dev, "'global' suspend %d\n",
|
||||||
|
status);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
/* stop khubd and related activity */
|
/* stop khubd and related activity */
|
||||||
hub_quiesce(hub);
|
hub_quiesce(hub);
|
||||||
return 0;
|
return 0;
|
||||||
@ -1926,9 +1936,36 @@ static int hub_resume(struct usb_interface *intf)
|
|||||||
{
|
{
|
||||||
struct usb_device *hdev = interface_to_usbdev(intf);
|
struct usb_device *hdev = interface_to_usbdev(intf);
|
||||||
struct usb_hub *hub = usb_get_intfdata (intf);
|
struct usb_hub *hub = usb_get_intfdata (intf);
|
||||||
unsigned port1;
|
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
|
/* "global resume" of the downstream HC-to-USB interface */
|
||||||
|
if (!hdev->parent) {
|
||||||
|
struct usb_bus *bus = hdev->bus;
|
||||||
|
if (bus && bus->op->hub_resume) {
|
||||||
|
status = bus->op->hub_resume (bus);
|
||||||
|
if (status) {
|
||||||
|
dev_dbg(&intf->dev, "'global' resume %d\n",
|
||||||
|
status);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
if (status == 0) {
|
||||||
|
/* TRSMRCY = 10 msec */
|
||||||
|
msleep(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hub_activate(hub);
|
||||||
|
|
||||||
|
/* REVISIT: this recursion probably shouldn't exist. Remove
|
||||||
|
* this code sometime, after retesting with different root and
|
||||||
|
* external hubs.
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_USB_SUSPEND
|
||||||
|
{
|
||||||
|
unsigned port1;
|
||||||
|
|
||||||
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
|
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
|
||||||
struct usb_device *udev;
|
struct usb_device *udev;
|
||||||
u16 portstat, portchange;
|
u16 portstat, portchange;
|
||||||
@ -1953,7 +1990,7 @@ static int hub_resume(struct usb_interface *intf)
|
|||||||
if (portstat & USB_PORT_STAT_SUSPEND)
|
if (portstat & USB_PORT_STAT_SUSPEND)
|
||||||
status = hub_port_resume(hub, port1, udev);
|
status = hub_port_resume(hub, port1, udev);
|
||||||
else {
|
else {
|
||||||
status = finish_port_resume(udev);
|
status = finish_device_resume(udev);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
dev_dbg(&intf->dev, "resume port %d --> %d\n",
|
dev_dbg(&intf->dev, "resume port %d --> %d\n",
|
||||||
port1, status);
|
port1, status);
|
||||||
@ -1962,8 +1999,8 @@ static int hub_resume(struct usb_interface *intf)
|
|||||||
}
|
}
|
||||||
up(&udev->serialize);
|
up(&udev->serialize);
|
||||||
}
|
}
|
||||||
hub->resume_root_hub = 0;
|
}
|
||||||
hub_activate(hub);
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1987,26 +2024,6 @@ void usb_resume_root_hub(struct usb_device *hdev)
|
|||||||
kick_khubd(hub);
|
kick_khubd(hub);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else /* !CONFIG_USB_SUSPEND */
|
|
||||||
|
|
||||||
int usb_suspend_device(struct usb_device *udev)
|
|
||||||
{
|
|
||||||
/* state does NOT lie by saying it's USB_STATE_SUSPENDED! */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int usb_resume_device(struct usb_device *udev)
|
|
||||||
{
|
|
||||||
udev->dev.power.power_state.event = PM_EVENT_ON;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define hub_suspend NULL
|
|
||||||
#define hub_resume NULL
|
|
||||||
#define remote_wakeup(x) 0
|
|
||||||
|
|
||||||
#endif /* CONFIG_USB_SUSPEND */
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(usb_suspend_device);
|
EXPORT_SYMBOL(usb_suspend_device);
|
||||||
EXPORT_SYMBOL(usb_resume_device);
|
EXPORT_SYMBOL(usb_resume_device);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user