diff mbox

[6/6] USB / PM: Allow USB devices to remain runtime-suspended when sleeping

Message ID 1427818501-10201-7-git-send-email-tomeu.vizoso@collabora.com (mailing list archive)
State RFC, archived
Headers show

Commit Message

Tomeu Vizoso March 31, 2015, 4:14 p.m. UTC
Have dev_pm_ops.prepare return 1 for USB devices, interfaces, endpoints
and ports so that USB devices can remain runtime-suspended when the
system goes to a sleep state.

Also enable runtime PM for endpoints, which is another requirement for
the above to work.

Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
---
 drivers/usb/core/endpoint.c | 17 +++++++++++++++++
 drivers/usb/core/message.c  | 16 ++++++++++++++++
 drivers/usb/core/port.c     |  6 ++++++
 drivers/usb/core/usb.c      |  2 +-
 4 files changed, 40 insertions(+), 1 deletion(-)

Comments

Alan Stern March 31, 2015, 5:09 p.m. UTC | #1
On Tue, 31 Mar 2015, Tomeu Vizoso wrote:

> Have dev_pm_ops.prepare return 1 for USB devices, interfaces, endpoints
> and ports so that USB devices can remain runtime-suspended when the
> system goes to a sleep state.
> 
> Also enable runtime PM for endpoints, which is another requirement for
> the above to work.
> 
> Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>

...

> diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
> index b1fb9ae..1498faa 100644
> --- a/drivers/usb/core/usb.c
> +++ b/drivers/usb/core/usb.c
> @@ -300,7 +300,7 @@ static int usb_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
>  
>  static int usb_dev_prepare(struct device *dev)
>  {
> -	return 0;		/* Implement eventually? */
> +	return 1;		/* Implement eventually? */
>  }

The rest of the patch is okay, but this part is wrong.  The documented
requirement is that the prepare callback should return 1 if the device
is already in the appropriate state.  This means it has to have the
right wakeup setting.

In other words, if the device is currently in runtime suspend with 
remote wakeup enabled, but device_may_wakeup() returns 0 (so that the 
device should be disabled for wakeup when the system goes into 
suspend), then the prepare callback has to return 0.

Therefore what you need to do here is something like this:

	struct usb_device	*udev = to_usb_device(dev);

	/* Return 0 if the current wakeup setting is wrong, otherwise 1 */
	if (udev->do_remote_wakeup && !device_may_wakeup(dev))
		return 0;
	return 1;

Aside from this issue, I like the patch set.  Do you think you can do 
something similar for drivers/scsi/scsi_pm.c?  I'm not aware of any 
wakeup-capable SCSI devices -- not disk or CD/DVD drives, anyway.

Alan Stern

--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Oliver Neukum April 1, 2015, 12:40 p.m. UTC | #2
On Tue, 2015-03-31 at 13:09 -0400, Alan Stern wrote:
> In other words, if the device is currently in runtime suspend with 
> remote wakeup enabled, but device_may_wakeup() returns 0 (so that the 
> device should be disabled for wakeup when the system goes into 
> suspend), then the prepare callback has to return 0.
> 
> Therefore what you need to do here is something like this:
> 
>         struct usb_device       *udev = to_usb_device(dev);
> 
>         /* Return 0 if the current wakeup setting is wrong, otherwise
> 1 */

And the other way round?

	Regards
		Oliver



--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Alan Stern April 1, 2015, 2:01 p.m. UTC | #3
On Wed, 1 Apr 2015, Oliver Neukum wrote:

> On Tue, 2015-03-31 at 13:09 -0400, Alan Stern wrote:
> > In other words, if the device is currently in runtime suspend with 
> > remote wakeup enabled, but device_may_wakeup() returns 0 (so that the 
> > device should be disabled for wakeup when the system goes into 
> > suspend), then the prepare callback has to return 0.
> > 
> > Therefore what you need to do here is something like this:
> > 
> >         struct usb_device       *udev = to_usb_device(dev);
> > 
> >         /* Return 0 if the current wakeup setting is wrong, otherwise
> > 1 */
> 
> And the other way round?

Your meaning isn't clear.  Are you asking what should happen if the 
device is in runtime suspend with remote wakeup disabled, but 
device_may_wakeup() returns 1?  That case should never happen -- but 
if it does then the prepare callback should return 0.

Alan Stern

--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Oliver Neukum April 1, 2015, 2:26 p.m. UTC | #4
On Wed, 2015-04-01 at 10:01 -0400, Alan Stern wrote:
> On Wed, 1 Apr 2015, Oliver Neukum wrote:
> 
> > On Tue, 2015-03-31 at 13:09 -0400, Alan Stern wrote:
> > > In other words, if the device is currently in runtime suspend with 
> > > remote wakeup enabled, but device_may_wakeup() returns 0 (so that the 
> > > device should be disabled for wakeup when the system goes into 
> > > suspend), then the prepare callback has to return 0.
> > > 
> > > Therefore what you need to do here is something like this:
> > > 
> > >         struct usb_device       *udev = to_usb_device(dev);
> > > 
> > >         /* Return 0 if the current wakeup setting is wrong, otherwise
> > > 1 */
> > 
> > And the other way round?
> 
> Your meaning isn't clear.  Are you asking what should happen if the 
> device is in runtime suspend with remote wakeup disabled, but 
> device_may_wakeup() returns 1?

Yes. I was thinking about that case.

>   That case should never happen -- but 
> if it does then the prepare callback should return 0.

Exactly. It didn't seem to do so.

	Regards
		Oliver


	


--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Alan Stern April 1, 2015, 2:36 p.m. UTC | #5
On Wed, 1 Apr 2015, Oliver Neukum wrote:

> On Wed, 2015-04-01 at 10:01 -0400, Alan Stern wrote:
> > On Wed, 1 Apr 2015, Oliver Neukum wrote:
> > 
> > > On Tue, 2015-03-31 at 13:09 -0400, Alan Stern wrote:
> > > > In other words, if the device is currently in runtime suspend with 
> > > > remote wakeup enabled, but device_may_wakeup() returns 0 (so that the 
> > > > device should be disabled for wakeup when the system goes into 
> > > > suspend), then the prepare callback has to return 0.
> > > > 
> > > > Therefore what you need to do here is something like this:
> > > > 
> > > >         struct usb_device       *udev = to_usb_device(dev);
> > > > 
> > > >         /* Return 0 if the current wakeup setting is wrong, otherwise
> > > > 1 */
> > > 
> > > And the other way round?
> > 
> > Your meaning isn't clear.  Are you asking what should happen if the 
> > device is in runtime suspend with remote wakeup disabled, but 
> > device_may_wakeup() returns 1?
> 
> Yes. I was thinking about that case.
> 
> >   That case should never happen -- but 
> > if it does then the prepare callback should return 0.
> 
> Exactly. It didn't seem to do so.

Well, Tomeu can fix it up to handle both cases properly.

Alan Stern

--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Tomeu Vizoso April 3, 2015, 1:05 p.m. UTC | #6
On 31 March 2015 at 19:09, Alan Stern <stern@rowland.harvard.edu> wrote:
> On Tue, 31 Mar 2015, Tomeu Vizoso wrote:
>
>> Have dev_pm_ops.prepare return 1 for USB devices, interfaces, endpoints
>> and ports so that USB devices can remain runtime-suspended when the
>> system goes to a sleep state.
>>
>> Also enable runtime PM for endpoints, which is another requirement for
>> the above to work.
>>
>> Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
>
> ...
>
>> diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
>> index b1fb9ae..1498faa 100644
>> --- a/drivers/usb/core/usb.c
>> +++ b/drivers/usb/core/usb.c
>> @@ -300,7 +300,7 @@ static int usb_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
>>
>>  static int usb_dev_prepare(struct device *dev)
>>  {
>> -     return 0;               /* Implement eventually? */
>> +     return 1;               /* Implement eventually? */
>>  }
>
> The rest of the patch is okay, but this part is wrong.  The documented
> requirement is that the prepare callback should return 1 if the device
> is already in the appropriate state.  This means it has to have the
> right wakeup setting.
>
> In other words, if the device is currently in runtime suspend with
> remote wakeup enabled, but device_may_wakeup() returns 0 (so that the
> device should be disabled for wakeup when the system goes into
> suspend), then the prepare callback has to return 0.
>
> Therefore what you need to do here is something like this:
>
>         struct usb_device       *udev = to_usb_device(dev);
>
>         /* Return 0 if the current wakeup setting is wrong, otherwise 1 */
>         if (udev->do_remote_wakeup && !device_may_wakeup(dev))
>                 return 0;
>         return 1;

Thanks, in v2 I have also covered the other case, as suggested by Oliver.

> Aside from this issue, I like the patch set.  Do you think you can do
> something similar for drivers/scsi/scsi_pm.c?  I'm not aware of any
> wakeup-capable SCSI devices -- not disk or CD/DVD drives, anyway.

I think I will be able to allocate some time on monday to look at this.

Thanks,

Tomeu

> Alan Stern
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index 39a2402..7c82bb7 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -160,6 +160,19 @@  static const struct attribute_group *ep_dev_groups[] = {
 	NULL
 };
 
+#ifdef	CONFIG_PM
+
+static int usb_ep_device_prepare(struct device *dev)
+{
+	return 1;
+}
+
+static const struct dev_pm_ops usb_ep_device_pm_ops = {
+	.prepare =	usb_ep_device_prepare,
+};
+
+#endif	/* CONFIG_PM */
+
 static void ep_device_release(struct device *dev)
 {
 	struct ep_device *ep_dev = to_ep_device(dev);
@@ -170,6 +183,9 @@  static void ep_device_release(struct device *dev)
 struct device_type usb_ep_device_type = {
 	.name =		"usb_endpoint",
 	.release = ep_device_release,
+#ifdef CONFIG_PM
+	.pm =		&usb_ep_device_pm_ops,
+#endif
 };
 
 int usb_create_ep_devs(struct device *parent,
@@ -197,6 +213,7 @@  int usb_create_ep_devs(struct device *parent,
 		goto error_register;
 
 	device_enable_async_suspend(&ep_dev->dev);
+	pm_runtime_enable(&ep_dev->dev);
 	endpoint->ep_dev = ep_dev;
 	return retval;
 
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index f368d20..9041aee 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1589,10 +1589,26 @@  static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env)
 	return 0;
 }
 
+#ifdef	CONFIG_PM
+
+static int usb_if_prepare(struct device *dev)
+{
+	return 1;
+}
+
+static const struct dev_pm_ops usb_if_pm_ops = {
+	.prepare =	usb_if_prepare,
+};
+
+#endif	/* CONFIG_PM */
+
 struct device_type usb_if_device_type = {
 	.name =		"usb_interface",
 	.release =	usb_release_interface,
 	.uevent =	usb_if_uevent,
+#ifdef CONFIG_PM
+	.pm =		&usb_if_pm_ops,
+#endif
 };
 
 static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 2106183..f49707d 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -168,12 +168,18 @@  static int usb_port_runtime_suspend(struct device *dev)
 
 	return retval;
 }
+
+static int usb_port_prepare(struct device *dev)
+{
+	return 1;
+}
 #endif
 
 static const struct dev_pm_ops usb_port_pm_ops = {
 #ifdef CONFIG_PM
 	.runtime_suspend =	usb_port_runtime_suspend,
 	.runtime_resume =	usb_port_runtime_resume,
+	.prepare =		usb_port_prepare,
 #endif
 };
 
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index b1fb9ae..1498faa 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -300,7 +300,7 @@  static int usb_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
 
 static int usb_dev_prepare(struct device *dev)
 {
-	return 0;		/* Implement eventually? */
+	return 1;		/* Implement eventually? */
 }
 
 static void usb_dev_complete(struct device *dev)