Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-12-20 Thread Lan Tianyu
On 2012年12月19日 23:39, Alan Stern wrote:
 On Wed, 19 Dec 2012, lantianyu wrote:
 
 I just find busy_bits is set or clear in the usb_port_resume() and
 usb_reset_and_verify_device(). So currently we should keep my changes
 mutually exclusive with them, right?

 Don't forget about what happens when a device is removed.
 I am not very clear about this since busy_bits is not changed during device 
 being
 removed. Could you elaborate? Thanks.
 
 What happens if the device is unplugged while some thread is using 
 busy_bits?  Will the hub driver realize that the device is gone?
 
 Alan Stern
 
Hi Alan:
Ok. I get. The port resume/suspend only take place when device is
suspending, suspended and resuming. If the device was unplugged during
busy_bits is set, it would be disconnected when resuming the device
since the resume operation would be failed.
I refresh my patch according to our previous discussion. The
needs_debounce flag is not useful because we move debouce to port's
resume. I also add a check of port_dev-power_is_on before set
change_bits in the hub_activate(). When device is power off, the port
status and port change will be 0 and change_bits will be set in the
original hub_activate() code which will cause device to be disconnected.
So add check to keep change_bits unset during device have been power off.

Index: usb/drivers/usb/core/hub.c
===
--- usb.orig/drivers/usb/core/hub.c
+++ usb/drivers/usb/core/hub.c
@@ -26,6 +26,7 @@
 #include linux/mutex.h
 #include linux/freezer.h
 #include linux/random.h
+#include linux/pm_qos.h

 #include asm/uaccess.h
 #include asm/byteorder.h
@@ -108,7 +109,7 @@ MODULE_PARM_DESC(use_both_schemes,
 DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
 EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);

-#define HUB_DEBOUNCE_TIMEOUT   1500
+#define HUB_DEBOUNCE_TIMEOUT   2000
 #define HUB_DEBOUNCE_STEP25
 #define HUB_DEBOUNCE_STABLE 100

@@ -127,7 +128,7 @@ static inline char *portspeed(struct usb
 }

 /* Note that hdev or one of its children must be locked! */
-static struct usb_hub *hdev_to_hub(struct usb_device *hdev)
+struct usb_hub *hdev_to_hub(struct usb_device *hdev)
 {
if (!hdev || !hdev-actconfig || !hdev-maxchild)
return NULL;
@@ -393,7 +394,7 @@ static int clear_hub_feature(struct usb_
 /*
  * USB 2.0 spec Section 11.24.2.2
  */
-static int clear_port_feature(struct usb_device *hdev, int port1, int
feature)
+int clear_port_feature(struct usb_device *hdev, int port1, int feature)
 {
return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port1,
@@ -718,6 +719,9 @@ int usb_hub_set_port_power(struct usb_de
bool set)
 {
int ret;
+   struct usb_hub *hub = hdev_to_hub(hdev);
+   struct usb_port *port_dev
+   = hub-ports[port1 - 1];

if (set)
ret = set_port_feature(hdev, port1,
@@ -725,6 +729,9 @@ int usb_hub_set_port_power(struct usb_de
else
ret = clear_port_feature(hdev, port1,
USB_PORT_FEAT_POWER);
+
+   if (!ret)
+   port_dev-power_is_on = set;
return ret;
 }

@@ -804,7 +811,11 @@ static unsigned hub_power_on(struct usb_
dev_dbg(hub-intfdev, trying to enable port power on 
non-switchable hub\n);
for (port1 = 1; port1 = hub-descriptor-bNbrPorts; port1++)
-   set_port_feature(hub-hdev, port1, USB_PORT_FEAT_POWER);
+   if (hub-ports[port1 - 1]-power_is_on)
+   set_port_feature(hub-hdev, port1,
USB_PORT_FEAT_POWER);
+   else
+   clear_port_feature(hub-hdev, port1,
+   USB_PORT_FEAT_POWER);

/* Wait at least 100 msec for power to become stable */
delay = max(pgood_delay, (unsigned) 100);
@@ -1136,10 +1147,14 @@ static void hub_activate(struct usb_hub
set_bit(port1, hub-change_bits);

} else if (udev-persist_enabled) {
+   struct usb_port *port_dev = hub-ports[port1 - 1];
+
 #ifdef CONFIG_PM
udev-reset_resume = 1;
 #endif
-   set_bit(port1, hub-change_bits);
+   /* Don't set the change_bits when the device was
power off */
+   if (port_dev-power_is_on)
+   set_bit(port1, hub-change_bits);

} else {
/* The power session is gone; tell khubd */
@@ -2835,6 +2850,7 @@ EXPORT_SYMBOL_GPL(usb_enable_ltm);
 int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
 {
struct usb_hub  *hub = hdev_to_hub(udev-parent);
 {
struct usb_hub  *hub = hdev_to_hub(udev-parent);
+   struct usb_port *port_dev = hub-ports[udev-portnum - 1];
int  

Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-12-19 Thread lantianyu

于 2012/12/18 23:58, Alan Stern 写道:

On Tue, 18 Dec 2012, lantianyu wrote:


What you want here is sort of an alternate debounce routine.  The
normal routine waits until the connect status has been stable for 100
ms.  But you want to wait until the status has stable in the
connected state for 100 ms.

Yesh.

Maybe it would be better to do this by passing an extra argument to the
regular debounce routine.

How about this?


It would be easier to read a patch, particularly if it wasn't
whitespace-damaged.

Oh. Sorry. I didn't notice this. I replied via a newly installed
thunder bird and forgot to unset html format.



static int hub_port_debounce(struct usb_hub *hub, int port1, bool
must_connected)


This should be must_be_connected.


{
int ret;
int total_time, stable_time = 0;
u16 portchange, portstatus;
unsigned connection = 0x;

for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
ret = hub_port_status(hub, port1, portstatus, portchange);
if (ret  0)
return ret;

if (!(portchange  USB_PORT_STAT_C_CONNECTION) 
(portstatus  USB_PORT_STAT_CONNECTION) == connection){

if (!must_connected || (connection == USB_PORT_STAT_CONNECTION))
stable_time += HUB_DEBOUNCE_STEP;

if (stable_time = HUB_DEBOUNCE_STABLE)
break;
} else {
stable_time = 0;
connection = portstatus  USB_PORT_STAT_CONNECTION;
}

if (portchange  USB_PORT_STAT_C_CONNECTION) {
clear_port_feature(hub-hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
}

if (total_time = HUB_DEBOUNCE_TIMEOUT)
break;
msleep(HUB_DEBOUNCE_STEP);
}

dev_dbg (hub-intfdev,
debounce: port %d: total %dms stable %dms status 0x%x\n,
port1, total_time, stable_time, portstatus);

if (stable_time  HUB_DEBOUNCE_STABLE)
return -ETIMEDOUT;
return portstatus;
}


Yes, that seems okay.  You'll want to increase HUB_DEBOUNCE_TIMEOUT.


Yeah.




No, no, not at all.  The set_bit and clear_bit operations don't need
any protection -- they are atomic.  What we need to do is make sure
that two different threads don't try to manage the same port at the
same time.  For example, it should not be possible for one thread to
reset the port while another thread is trying to suspend it.

I'm pretty sure that this can't happen with the existing code.  The
only places where a port gets used are:

when a device is connected;

when a device is disconnected;

when a device is reset;

when a device is suspended or resumed.

We should check that these things really are mutually exclusive, and
that they remain mutually exclusive when your changes are added.

I just find busy_bits is set or clear in the usb_port_resume() and
usb_reset_and_verify_device(). So currently we should keep my changes
mutually exclusive with them, right?


Don't forget about what happens when a device is removed.

I am not very clear about this since busy_bits is not changed during device 
being
removed. Could you elaborate? Thanks.




My changes add two new places: when port is resumed and when is suspended.

Port's resume/suspend may occur in device's resume/suspend and Pm Qos
change by
pm_runtime_get_sync/put(port_dev).

The case in device's resume/suspend will not conflict with
usb_port_resume() and
usb_reset_and_verify_device() since they are all in the same thread or
will not occur at
the same time.


Yes.


For the case of Pm Qos change, actually it only happens when user set
NO_POWER_OFF flag
after the device being power off. The port will be resumed at that time.
One case is
that device resume and Pm Qos change take place at the same time. The
usb_port_resume()
is calling pm_runtime_get_sync(port_dev) and PM core also is doing the
same thing. So two
port resume will be executed in the parallel. Assuming that the one
triggered by usb_port_resume()
is finished firstly and it goes back to usb_port_resume() position where
just before set busy_bits. The
second, port resume operation will be executed. Will this happen? If
yes, there will be a race problem.
Just one case I have found.


The PM core guarantees that runtime PM callbacks are mutually
exclusive.  It also guarantees that the runtime_resume routine won't be
called if the device is currently active (and the runtime_suspend
routine won't be called if the device is currently suspended).

Yeah. I misread the code.


Therefore it suffices to make sure that usb_port_resume does
pm_runtime_get_sync(port_dev) before it touches busy_bits.

The other race, against usb_reset_and_verify_device, should also be
okay.  Aside from the resume pathway, the only place that routine gets
called is from usb_reset_device, which is careful to prevent the device
from being suspended during the reset.


Is it possble to add a lock to prevent the busy_bits from being cleared
by other thread?


It looks like we don't need it.

Alan Stern


--
To unsubscribe from this list: send the line unsubscribe linux-usb in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-12-19 Thread Alan Stern
On Wed, 19 Dec 2012, lantianyu wrote:

  I just find busy_bits is set or clear in the usb_port_resume() and
  usb_reset_and_verify_device(). So currently we should keep my changes
  mutually exclusive with them, right?
 
  Don't forget about what happens when a device is removed.
 I am not very clear about this since busy_bits is not changed during device 
 being
 removed. Could you elaborate? Thanks.

What happens if the device is unplugged while some thread is using 
busy_bits?  Will the hub driver realize that the device is gone?

Alan Stern

--
To unsubscribe from this list: send the line unsubscribe linux-usb in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-12-18 Thread lantianyu

于 2012/12/17 0:25, Alan Stern 写道:

On Sun, 16 Dec 2012, Lan Tianyu wrote:


On 2012/12/14 23:44, Alan Stern wrote:

On Fri, 14 Dec 2012, Lan Tianyu wrote:


Hi Alan:
debounce is still needed. If connect status was not stable, resume
operation will fail. So how about following?

Actually, I'm not sure that a debounce is needed.

Debouncing is used when a plug is physically inserted or removed,
because the electrical terminals make fleeting contact with one another
(they literally bounce on and off each other for a brief time).  But in
this case there is no physical insertion.  The terminals are in stable
physical contact with each other, so they can't bounce.

I have done a test without debounce but resume failed due to port status
bounce. So I add debounce again.

Okay.  That's not a physical bounce but an electrical problem in the
device.

What you want here is sort of an alternate debounce routine.  The
normal routine waits until the connect status has been stable for 100
ms.  But you want to wait until the status has stable in the
connected state for 100 ms.

Yesh.

Maybe it would be better to do this by passing an extra argument to the
regular debounce routine.

How about this?

static int hub_port_debounce(struct usb_hub *hub, int port1, bool 
must_connected)

{
int ret;
int total_time, stable_time = 0;
u16 portchange, portstatus;
unsigned connection = 0x;

for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
ret = hub_port_status(hub, port1, portstatus, portchange);
if (ret  0)
return ret;

if (!(portchange  USB_PORT_STAT_C_CONNECTION) 
(portstatus  USB_PORT_STAT_CONNECTION) == connection){

if (!must_connected || (connection == USB_PORT_STAT_CONNECTION))
stable_time += HUB_DEBOUNCE_STEP;

if (stable_time = HUB_DEBOUNCE_STABLE)
break;
} else {
stable_time = 0;
connection = portstatus  USB_PORT_STAT_CONNECTION;
}

if (portchange  USB_PORT_STAT_C_CONNECTION) {
clear_port_feature(hub-hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
}

if (total_time = HUB_DEBOUNCE_TIMEOUT)
break;
msleep(HUB_DEBOUNCE_STEP);
}

dev_dbg (hub-intfdev,
debounce: port %d: total %dms stable %dms status 0x%x\n,
port1, total_time, stable_time, portstatus);

if (stable_time  HUB_DEBOUNCE_STABLE)
return -ETIMEDOUT;
return portstatus;
}

Is there a case that the port status bounced and its status maintained
unconnected for a period which caused debounce returned un-connected
status? And wait for more time, the port's status would become connected
and stable.

That can happen.  It doesn't cause any problems, because we will detect
the device when it connects the second time.  It will then look like a
normal new device connection.


But actually we don't want the device to be disconnected, this will affect
some user space. E.g a usb key, the disc dev node /dev/sd* will be recreated
during this procedure. I think the new debouce routine can resolve the problem.





We're going to have to take a little more care with busy_bits in the
future.  It's not protected by any lock, and there's not much to
prevent two different threads from using the same bit at the same time.
Only the fact that this would mean they were trying to control the same
port at the same time.

Ok. So we need to add lock for busy_bits or a routine to change the
busy_bits with a lock protecting. Something likes this.

usb_hub_set_busy_bits(hub, port, set) {

up(lock);
if (set)
set_bit(port1, hub-busy_bits);
else
clear_bit(port1, hub-busy_bits);
down(lock);
}

No, no, not at all.  The set_bit and clear_bit operations don't need
any protection -- they are atomic.  What we need to do is make sure
that two different threads don't try to manage the same port at the
same time.  For example, it should not be possible for one thread to
reset the port while another thread is trying to suspend it.

I'm pretty sure that this can't happen with the existing code.  The
only places where a port gets used are:

when a device is connected;

when a device is disconnected;

when a device is reset;

when a device is suspended or resumed.

We should check that these things really are mutually exclusive, and
that they remain mutually exclusive when your changes are added.

I just find busy_bits is set or clear in the usb_port_resume() and
usb_reset_and_verify_device(). So currently we should keep my changes
mutually exclusive with them, right?
My changes add two new places: when port is resumed and when is suspended.

Port's resume/suspend may occur in device's resume/suspend and Pm Qos 
change by

pm_runtime_get_sync/put(port_dev).

The case in device's resume/suspend will not conflict with 
usb_port_resume() and
usb_reset_and_verify_device() since they are all in the same thread or 
will not occur at

the same time.

For the case of Pm Qos change, actually it only happens when user set 
NO_POWER_OFF flag
after the device being power off. The port will be resumed at that time. 
One 

Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-12-18 Thread Alan Stern
On Tue, 18 Dec 2012, lantianyu wrote:

  What you want here is sort of an alternate debounce routine.  The
  normal routine waits until the connect status has been stable for 100
  ms.  But you want to wait until the status has stable in the
  connected state for 100 ms.
 Yesh.
  Maybe it would be better to do this by passing an extra argument to the
  regular debounce routine.
 How about this?

It would be easier to read a patch, particularly if it wasn't 
whitespace-damaged.

 static int hub_port_debounce(struct usb_hub *hub, int port1, bool 
 must_connected)

This should be must_be_connected.

 {
 int ret;
 int total_time, stable_time = 0;
 u16 portchange, portstatus;
 unsigned connection = 0x;
 
 for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
 ret = hub_port_status(hub, port1, portstatus, portchange);
 if (ret  0)
 return ret;
 
 if (!(portchange  USB_PORT_STAT_C_CONNECTION) 
 (portstatus  USB_PORT_STAT_CONNECTION) == connection){
 
 if (!must_connected || (connection == USB_PORT_STAT_CONNECTION))
 stable_time += HUB_DEBOUNCE_STEP;
 
 if (stable_time = HUB_DEBOUNCE_STABLE)
 break;
 } else {
 stable_time = 0;
 connection = portstatus  USB_PORT_STAT_CONNECTION;
 }
 
 if (portchange  USB_PORT_STAT_C_CONNECTION) {
 clear_port_feature(hub-hdev, port1,
 USB_PORT_FEAT_C_CONNECTION);
 }
 
 if (total_time = HUB_DEBOUNCE_TIMEOUT)
 break;
 msleep(HUB_DEBOUNCE_STEP);
 }
 
 dev_dbg (hub-intfdev,
 debounce: port %d: total %dms stable %dms status 0x%x\n,
 port1, total_time, stable_time, portstatus);
 
 if (stable_time  HUB_DEBOUNCE_STABLE)
 return -ETIMEDOUT;
 return portstatus;
 }

Yes, that seems okay.  You'll want to increase HUB_DEBOUNCE_TIMEOUT.


  No, no, not at all.  The set_bit and clear_bit operations don't need
  any protection -- they are atomic.  What we need to do is make sure
  that two different threads don't try to manage the same port at the
  same time.  For example, it should not be possible for one thread to
  reset the port while another thread is trying to suspend it.
 
  I'm pretty sure that this can't happen with the existing code.  The
  only places where a port gets used are:
 
  when a device is connected;
 
  when a device is disconnected;
 
  when a device is reset;
 
  when a device is suspended or resumed.
 
  We should check that these things really are mutually exclusive, and
  that they remain mutually exclusive when your changes are added.
 I just find busy_bits is set or clear in the usb_port_resume() and
 usb_reset_and_verify_device(). So currently we should keep my changes
 mutually exclusive with them, right?

Don't forget about what happens when a device is removed.

 My changes add two new places: when port is resumed and when is suspended.
 
 Port's resume/suspend may occur in device's resume/suspend and Pm Qos 
 change by
 pm_runtime_get_sync/put(port_dev).
 
 The case in device's resume/suspend will not conflict with 
 usb_port_resume() and
 usb_reset_and_verify_device() since they are all in the same thread or 
 will not occur at
 the same time.

Yes.

 For the case of Pm Qos change, actually it only happens when user set 
 NO_POWER_OFF flag
 after the device being power off. The port will be resumed at that time. 
 One case is
 that device resume and Pm Qos change take place at the same time. The 
 usb_port_resume()
 is calling pm_runtime_get_sync(port_dev) and PM core also is doing the 
 same thing. So two
 port resume will be executed in the parallel. Assuming that the one 
 triggered by usb_port_resume()
 is finished firstly and it goes back to usb_port_resume() position where 
 just before set busy_bits. The
 second, port resume operation will be executed. Will this happen? If 
 yes, there will be a race problem.
 Just one case I have found.

The PM core guarantees that runtime PM callbacks are mutually
exclusive.  It also guarantees that the runtime_resume routine won't be
called if the device is currently active (and the runtime_suspend
routine won't be called if the device is currently suspended).

Therefore it suffices to make sure that usb_port_resume does 
pm_runtime_get_sync(port_dev) before it touches busy_bits.

The other race, against usb_reset_and_verify_device, should also be 
okay.  Aside from the resume pathway, the only place that routine gets 
called is from usb_reset_device, which is careful to prevent the device 
from being suspended during the reset.

 Is it possble to add a lock to prevent the busy_bits from being cleared 
 by other thread?

It looks like we don't need it.

Alan Stern

--
To unsubscribe from this list: send the line unsubscribe linux-usb in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-12-14 Thread Lan Tianyu
On 2012年12月12日 23:56, Alan Stern wrote:
 On Wed, 12 Dec 2012, Lan Tianyu wrote:
 
 I tested a usb ssd which consumes about 1s to makes usb port status
 enter into connect status after powering off and powering on the port.
 So I set the tries 20 and the longest latency is larger than 2s.
 The debounce routine only guarantees port status stable rather than
 enter into connect status.

 Then a better solution would be to first wait (up to 2 seconds) for a 
 connect status and then call the debounce routine.
 But some devices don't need to wait 2s such long time,  200ms is enough
 for them. So I try to check status everytime  after debounce. If it's
 connected, go away.
 
 You should test the connect status in a loop.  Each time through the 
 loop, if it's not connected wait another 20 or so.


Hi Alan:
debounce is still needed. If connect status was not stable, resume
operation will fail. So how about following?

int usb_port_wait_for_connected(struct usb_hub *hub, int port1)
{
u16 portchange, portstatus;
int total_time, ret;

for (total_time = 0; total_time  2000; total_time += 20) {
ret = hub_port_status(hub, port1, portstatus, portchange);
if (ret  0)
return ret;

if ((portstatus  USB_PORT_STAT_CONNECTION)
 (hub_port_debounce(hub, port1)  
USB_PORT_STAT_CONNECTION)) {
/*
 * just clear enable-change feature since debounce
 *  has cleared connect-change feature.
 */
clear_port_feature(hub-hdev, port1,
USB_PORT_FEAT_C_ENABLE);
return 0;
}
mdelay(20);
}
return -ETIMEDOUT;
}


 
 You need to store somewhere the fact that you made this call, so that 
 you will know whether or not to make the corresponding 
 pm_runtime_get_sync call in usb_port_resume.
 You mean I should add a new flag to keep the
 pm_runtime_put_sync/put(port_dev) being called paired, right?
 How about needs_resume?

 What you need isn't a resume; it's pm_runtime_get_sync.
 How about needs_runtime_get?
 
 Or maybe did_runtime_put.  Something like that.
 
 
 Even the power is still on but the PORT_POWER feature has been cleared.
 So there should be no event from port, right?

 should be -- but buggy hardware might send an event anyway.  Also,
 many root hubs don't pay attention to the power feature.  If this
 happens, you probably should handle the connect change properly.  I 
 don't see any point in ignoring it.
 How to deal with these connect change event or how to identify whether
 the connect change event is trigger by real power off or not?
 
 If you get a connect change immediately after turning off the port 
 power, there's no way to know whether it was because the power is now 
 off or because the device really disconnected.  So the best you can do 
 is turn on the busy_bits entry, turn off the power, clear the 
 connect-change and enable-change statuses, and turn off the busy_bits 
 entry.
 
You mean this?
static int usb_port_runtime_suspend(struct device *dev)
{
struct usb_port *port_dev = to_usb_port(dev);
struct usb_device *hdev =
to_usb_device(dev-parent-parent);
struct usb_hub *hub = hdev_to_hub(hdev);
int port1 = port_dev-portnum;
int retval;

if (dev_pm_qos_flags(port_dev-dev, PM_QOS_FLAG_NO_POWER_OFF)
== PM_QOS_FLAGS_ALL)
return -EAGAIN;

set_bit(port1, hub-busy_bits);
retval = usb_hub_set_port_power(hdev, port1, false);
clear_port_feature(hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
clear_port_feature(hdev, port1,
USB_PORT_FEAT_C_ENABLE);
clear_bit(port1, hub-busy_bits);
return retval;  
}

 Alan Stern
 


-- 
Best regards
Tianyu Lan
--
To unsubscribe from this list: send the line unsubscribe linux-usb in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-12-14 Thread Alan Stern
On Fri, 14 Dec 2012, Lan Tianyu wrote:

 Hi Alan:
   debounce is still needed. If connect status was not stable, resume
 operation will fail. So how about following?

Actually, I'm not sure that a debounce is needed.

Debouncing is used when a plug is physically inserted or removed, 
because the electrical terminals make fleeting contact with one another 
(they literally bounce on and off each other for a brief time).  But in 
this case there is no physical insertion.  The terminals are in stable 
physical contact with each other, so they can't bounce.

 int usb_port_wait_for_connected(struct usb_hub *hub, int port1)
 {
   u16 portchange, portstatus;
   int total_time, ret;
 
   for (total_time = 0; total_time  2000; total_time += 20) {
   ret = hub_port_status(hub, port1, portstatus, portchange);
   if (ret  0)
   return ret;
 
   if ((portstatus  USB_PORT_STAT_CONNECTION)
(hub_port_debounce(hub, port1)  
 USB_PORT_STAT_CONNECTION)) {
   /*
* just clear enable-change feature since debounce
*  has cleared connect-change feature.
*/
   clear_port_feature(hub-hdev, port1,
   USB_PORT_FEAT_C_ENABLE);
   return 0;
   }
   mdelay(20);
   }
   return -ETIMEDOUT;
 }

Why do you want to continue the loop when hub_port_debounce fails?

  If you get a connect change immediately after turning off the port 
  power, there's no way to know whether it was because the power is now 
  off or because the device really disconnected.  So the best you can do 
  is turn on the busy_bits entry, turn off the power, clear the 
  connect-change and enable-change statuses, and turn off the busy_bits 
  entry.
  
 You mean this?
 static int usb_port_runtime_suspend(struct device *dev)
 {
   struct usb_port *port_dev = to_usb_port(dev);
   struct usb_device *hdev =
   to_usb_device(dev-parent-parent);
   struct usb_hub *hub = hdev_to_hub(hdev);
   int port1 = port_dev-portnum;
   int retval;
 
   if (dev_pm_qos_flags(port_dev-dev, PM_QOS_FLAG_NO_POWER_OFF)
   == PM_QOS_FLAGS_ALL)
   return -EAGAIN;
 
   set_bit(port1, hub-busy_bits);
   retval = usb_hub_set_port_power(hdev, port1, false);
   clear_port_feature(hdev, port1,
   USB_PORT_FEAT_C_CONNECTION);
   clear_port_feature(hdev, port1,
   USB_PORT_FEAT_C_ENABLE);
   clear_bit(port1, hub-busy_bits);
   return retval;  
 }

Yes, something like that.

We're going to have to take a little more care with busy_bits in the
future.  It's not protected by any lock, and there's not much to
prevent two different threads from using the same bit at the same time.  
Only the fact that this would mean they were trying to control the same
port at the same time.

Alan Stern

--
To unsubscribe from this list: send the line unsubscribe linux-usb in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-12-12 Thread Lan Tianyu
On 2012年12月12日 00:35, Alan Stern wrote:
 On Tue, 11 Dec 2012, Lan Tianyu wrote:
 
 @@ -108,11 +109,14 @@ MODULE_PARM_DESC(use_both_schemes,
  DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
  EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);

 +#define HUB_PORT_RECONNECT_TRIES  20

 20 is an awful lot.  Do you really need any more than one try?  The 
 debounce routine already does its own looping.
 I tested a usb ssd which consumes about 1s to makes usb port status
 enter into connect status after powering off and powering on the port.
 So I set the tries 20 and the longest latency is larger than 2s.
 The debounce routine only guarantees port status stable rather than
 enter into connect status.
 
 Then a better solution would be to first wait (up to 2 seconds) for a 
 connect status and then call the debounce routine.
But some devices don't need to wait 2s such long time,  200ms is enough
for them. So I try to check status everytime  after debounce. If it's
connected, go away.
 
 @@ -718,13 +722,26 @@ int usb_hub_set_port_power(struct usb_device
 *hdev, int port1,
bool set)
  {
int ret;
 +  struct usb_hub *hub = hdev_to_hub(hdev);
 +  struct usb_port *port_dev
 +  = hub-ports[port1 - 1];
 +
 +  if (set) {
 +  if (port_dev-power_is_on)
 +  return 0;

 -  if (set)
ret = set_port_feature(hdev, port1,
USB_PORT_FEAT_POWER);
 -  else
 +  } else {
 +  if (!port_dev-power_is_on)
 +  return 0;
 +
ret = clear_port_feature(hdev, port1,
USB_PORT_FEAT_POWER);
 +  }

 Do we need these shortcuts?  How often will this routine be called when
 the port is already in the right state
 When the PM Qos NO_POWER_OFF is changed,
 pm_runtime_get_sync/put(port_dev) will be invoked. This routine will be
 called during port resume and suspend. If one device was suspended and
 power off, the port's usage_count would be 0. After then, the port will
 be resumed and suspend every time pm qos NO_POWER_OFF flag being
 changed. So this depends on the user space.
 
 You did not understand my question.  When usb_hub_set_port_power is 
 called, won't the Set-Feature request almost always be necessary?
 
Oh. Sorry. Thanks for reminders. :)

 For example, how often will it happen that set and 
 port_dev-power_is_on are both true?  Or both false?  It seems to me 
 that this will almost never happen.  So why bother testing for it?
Ok. I get. You are right and will remove the check.
 
 @@ -2862,6 +2884,18 @@ int usb_port_suspend(struct usb_device *udev,
 pm_message_t msg)
udev-port_is_suspended = 1;
msleep(10);
}
 +
 +  /*
 +   * Check whether current status is meeting requirement of
 +   * usb port power off mechanism
 +   */
 +  if (!udev-do_remote_wakeup
 +   (dev_pm_qos_flags(port_dev-dev,
 +  PM_QOS_FLAG_NO_POWER_OFF) != PM_QOS_FLAGS_ALL)
 +   udev-persist_enabled
 +   !status)
 +  pm_runtime_put_sync(port_dev-dev);

 You need to store somewhere the fact that you made this call, so that 
 you will know whether or not to make the corresponding 
 pm_runtime_get_sync call in usb_port_resume.
 You mean I should add a new flag to keep the
 pm_runtime_put_sync/put(port_dev) being called paired, right?
 How about needs_resume?
 
 What you need isn't a resume; it's pm_runtime_get_sync.
How about needs_runtime_get?
 
 @@ -2982,10 +3035,39 @@ static int finish_port_resume(struct usb_device
 *udev)
  int usb_port_resume(struct usb_device *udev, pm_message_t msg)
  {
struct usb_hub  *hub = hdev_to_hub(udev-parent);
 +  struct usb_port *port_dev = hub-ports[udev-portnum  - 1];
int port1 = udev-portnum;
int status;
u16 portchange, portstatus;

 +
 +  set_bit(port1, hub-busy_bits);
 +
 +  if (!port_dev-power_is_on) {

 This test is wrong.  It's possible that the port power is still on even 
 though you called pm_runtime_put_sync.
 Above, we need to check the new flag, right?
 
 Yes.
 
 +  status = pm_runtime_get_sync(port_dev-dev);
 +  if (status  0) {
 +  dev_dbg(udev-dev, can't resume usb port, status 
 %d\n,
 +  status);
 +  clear_bit(port1, hub-busy_bits);
 +  return status;
 +  }

 Don't you want to call usb_port_wait_for_connected right here?

 +  }
 +
 +
 +  /*
 +   * Wait for usb hub port to be reconnected in order to make
 +   * the resume procedure successful.
 +   */
 +  if (port_dev-needs_debounce) {
 +  status = usb_port_wait_for_connected(hub, port1);

 If you move this call earlier then you won't have to test
 needs_debounce.
 The port may have been power on when device is resumed. One case, after
 device being suspended and power off, user may set the NO_POWER_OFF flag
 and port will be power on before device being resumed. For this case,
 port 

Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-12-12 Thread Alan Stern
On Wed, 12 Dec 2012, Alan Stern wrote:

 You should test the connect status in a loop.  Each time through the 
 loop, if it's not connected wait another 20 or so.

Typo; I meant wait another 20 ms or so.

Alan Stern

--
To unsubscribe from this list: send the line unsubscribe linux-usb in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-12-11 Thread Alan Stern
On Tue, 11 Dec 2012, Lan Tianyu wrote:

  @@ -108,11 +109,14 @@ MODULE_PARM_DESC(use_both_schemes,
   DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
   EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
 
  +#define HUB_PORT_RECONNECT_TRIES  20
  
  20 is an awful lot.  Do you really need any more than one try?  The 
  debounce routine already does its own looping.
 I tested a usb ssd which consumes about 1s to makes usb port status
 enter into connect status after powering off and powering on the port.
 So I set the tries 20 and the longest latency is larger than 2s.
 The debounce routine only guarantees port status stable rather than
 enter into connect status.

Then a better solution would be to first wait (up to 2 seconds) for a 
connect status and then call the debounce routine.

  @@ -718,13 +722,26 @@ int usb_hub_set_port_power(struct usb_device
  *hdev, int port1,
 bool set)
   {
 int ret;
  +  struct usb_hub *hub = hdev_to_hub(hdev);
  +  struct usb_port *port_dev
  +  = hub-ports[port1 - 1];
  +
  +  if (set) {
  +  if (port_dev-power_is_on)
  +  return 0;
 
  -  if (set)
 ret = set_port_feature(hdev, port1,
 USB_PORT_FEAT_POWER);
  -  else
  +  } else {
  +  if (!port_dev-power_is_on)
  +  return 0;
  +
 ret = clear_port_feature(hdev, port1,
 USB_PORT_FEAT_POWER);
  +  }
  
  Do we need these shortcuts?  How often will this routine be called when
  the port is already in the right state
 When the PM Qos NO_POWER_OFF is changed,
 pm_runtime_get_sync/put(port_dev) will be invoked. This routine will be
 called during port resume and suspend. If one device was suspended and
 power off, the port's usage_count would be 0. After then, the port will
 be resumed and suspend every time pm qos NO_POWER_OFF flag being
 changed. So this depends on the user space.

You did not understand my question.  When usb_hub_set_port_power is 
called, won't the Set-Feature request almost always be necessary?

For example, how often will it happen that set and 
port_dev-power_is_on are both true?  Or both false?  It seems to me 
that this will almost never happen.  So why bother testing for it?

  @@ -2862,6 +2884,18 @@ int usb_port_suspend(struct usb_device *udev,
  pm_message_t msg)
 udev-port_is_suspended = 1;
 msleep(10);
 }
  +
  +  /*
  +   * Check whether current status is meeting requirement of
  +   * usb port power off mechanism
  +   */
  +  if (!udev-do_remote_wakeup
  +   (dev_pm_qos_flags(port_dev-dev,
  +  PM_QOS_FLAG_NO_POWER_OFF) != PM_QOS_FLAGS_ALL)
  +   udev-persist_enabled
  +   !status)
  +  pm_runtime_put_sync(port_dev-dev);
  
  You need to store somewhere the fact that you made this call, so that 
  you will know whether or not to make the corresponding 
  pm_runtime_get_sync call in usb_port_resume.
 You mean I should add a new flag to keep the
 pm_runtime_put_sync/put(port_dev) being called paired, right?
 How about needs_resume?

What you need isn't a resume; it's pm_runtime_get_sync.

  @@ -2982,10 +3035,39 @@ static int finish_port_resume(struct usb_device
  *udev)
   int usb_port_resume(struct usb_device *udev, pm_message_t msg)
   {
 struct usb_hub  *hub = hdev_to_hub(udev-parent);
  +  struct usb_port *port_dev = hub-ports[udev-portnum  - 1];
 int port1 = udev-portnum;
 int status;
 u16 portchange, portstatus;
 
  +
  +  set_bit(port1, hub-busy_bits);
  +
  +  if (!port_dev-power_is_on) {
  
  This test is wrong.  It's possible that the port power is still on even 
  though you called pm_runtime_put_sync.
 Above, we need to check the new flag, right?

Yes.

  +  status = pm_runtime_get_sync(port_dev-dev);
  +  if (status  0) {
  +  dev_dbg(udev-dev, can't resume usb port, status 
  %d\n,
  +  status);
  +  clear_bit(port1, hub-busy_bits);
  +  return status;
  +  }
  
  Don't you want to call usb_port_wait_for_connected right here?
  
  +  }
  +
  +
  +  /*
  +   * Wait for usb hub port to be reconnected in order to make
  +   * the resume procedure successful.
  +   */
  +  if (port_dev-needs_debounce) {
  +  status = usb_port_wait_for_connected(hub, port1);
  
  If you move this call earlier then you won't have to test
  needs_debounce.
 The port may have been power on when device is resumed. One case, after
 device being suspended and power off, user may set the NO_POWER_OFF flag
 and port will be power on before device being resumed. For this case,
 port doesn't need to be resumed in the usb_port_resume() since port
 already power on and wait for connected is enough. So I divide resume
 port and wait for connected into two pieces.

You are confusing pm_runtime_get_sync with resume.  They 

Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-12-10 Thread Alan Stern
On Mon, 10 Dec 2012, Lan Tianyu wrote:

 Hi Alan:
   I write a patch based on the needs_debounce flag. The flag will be set
 when port has child device and power on successfully. Otherwise, I
 separate resume port and wait for connect in the usb_port_resume().
 Device will not be disconnected when power_is_on is false or
 needs_debounce  is set. Welcome comments.
 
 diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
 index 86e595c..aa41d3a 100644
 --- a/drivers/usb/core/hub.c
 +++ b/drivers/usb/core/hub.c

 @@ -108,11 +109,14 @@ MODULE_PARM_DESC(use_both_schemes,
  DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
  EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
 
 +#define HUB_PORT_RECONNECT_TRIES 20

20 is an awful lot.  Do you really need any more than one try?  The 
debounce routine already does its own looping.

 @@ -718,13 +722,26 @@ int usb_hub_set_port_power(struct usb_device
 *hdev, int port1,
   bool set)
  {
   int ret;
 + struct usb_hub *hub = hdev_to_hub(hdev);
 + struct usb_port *port_dev
 + = hub-ports[port1 - 1];
 +
 + if (set) {
 + if (port_dev-power_is_on)
 + return 0;
 
 - if (set)
   ret = set_port_feature(hdev, port1,
   USB_PORT_FEAT_POWER);
 - else
 + } else {
 + if (!port_dev-power_is_on)
 + return 0;
 +
   ret = clear_port_feature(hdev, port1,
   USB_PORT_FEAT_POWER);
 + }

Do we need these shortcuts?  How often will this routine be called when
the port is already in the right state?

 @@ -2862,6 +2884,18 @@ int usb_port_suspend(struct usb_device *udev,
 pm_message_t msg)
   udev-port_is_suspended = 1;
   msleep(10);
   }
 +
 + /*
 +  * Check whether current status is meeting requirement of
 +  * usb port power off mechanism
 +  */
 + if (!udev-do_remote_wakeup
 +  (dev_pm_qos_flags(port_dev-dev,
 + PM_QOS_FLAG_NO_POWER_OFF) != PM_QOS_FLAGS_ALL)
 +  udev-persist_enabled
 +  !status)
 + pm_runtime_put_sync(port_dev-dev);

You need to store somewhere the fact that you made this call, so that 
you will know whether or not to make the corresponding 
pm_runtime_get_sync call in usb_port_resume.

 @@ -2982,10 +3035,39 @@ static int finish_port_resume(struct usb_device
 *udev)
  int usb_port_resume(struct usb_device *udev, pm_message_t msg)
  {
   struct usb_hub  *hub = hdev_to_hub(udev-parent);
 + struct usb_port *port_dev = hub-ports[udev-portnum  - 1];
   int port1 = udev-portnum;
   int status;
   u16 portchange, portstatus;
 
 +
 + set_bit(port1, hub-busy_bits);
 +
 + if (!port_dev-power_is_on) {

This test is wrong.  It's possible that the port power is still on even 
though you called pm_runtime_put_sync.

 + status = pm_runtime_get_sync(port_dev-dev);
 + if (status  0) {
 + dev_dbg(udev-dev, can't resume usb port, status 
 %d\n,
 + status);
 + clear_bit(port1, hub-busy_bits);
 + return status;
 + }

Don't you want to call usb_port_wait_for_connected right here?

 + }
 +
 +
 + /*
 +  * Wait for usb hub port to be reconnected in order to make
 +  * the resume procedure successful.
 +  */
 + if (port_dev-needs_debounce) {
 + status = usb_port_wait_for_connected(hub, port1);

If you move this call earlier then you won't have to test
needs_debounce.

 @@ -4175,6 +4256,11 @@ static void hub_port_connect_change(struct
 usb_hub *hub, int port1,
   }
   }
 
 + if (!port_dev-power_is_on || port_dev-needs_debounce) {
 + clear_bit(port1, hub-change_bits);
 + return;
 + }

Doesn't the busy_bits flag take care of this for you already?

Also, what if the port is ganged, so even though you think you turned
off the power, it really is still on?  When that happens you probably
don't want to ignore port events.

Alan Stern

--
To unsubscribe from this list: send the line unsubscribe linux-usb in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-12-10 Thread Lan Tianyu
On 2012年12月11日 00:53, Alan Stern wrote:
 On Mon, 10 Dec 2012, Lan Tianyu wrote:
 
 Hi Alan:
  I write a patch based on the needs_debounce flag. The flag will be set
 when port has child device and power on successfully. Otherwise, I
 separate resume port and wait for connect in the usb_port_resume().
 Device will not be disconnected when power_is_on is false or
 needs_debounce  is set. Welcome comments.

 diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
 index 86e595c..aa41d3a 100644
 --- a/drivers/usb/core/hub.c
 +++ b/drivers/usb/core/hub.c
 
 @@ -108,11 +109,14 @@ MODULE_PARM_DESC(use_both_schemes,
  DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
  EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);

 +#define HUB_PORT_RECONNECT_TRIES20
 
 20 is an awful lot.  Do you really need any more than one try?  The 
 debounce routine already does its own looping.
I tested a usb ssd which consumes about 1s to makes usb port status
enter into connect status after powering off and powering on the port.
So I set the tries 20 and the longest latency is larger than 2s.
The debounce routine only guarantees port status stable rather than
enter into connect status.

 
 @@ -718,13 +722,26 @@ int usb_hub_set_port_power(struct usb_device
 *hdev, int port1,
  bool set)
  {
  int ret;
 +struct usb_hub *hub = hdev_to_hub(hdev);
 +struct usb_port *port_dev
 += hub-ports[port1 - 1];
 +
 +if (set) {
 +if (port_dev-power_is_on)
 +return 0;

 -if (set)
  ret = set_port_feature(hdev, port1,
  USB_PORT_FEAT_POWER);
 -else
 +} else {
 +if (!port_dev-power_is_on)
 +return 0;
 +
  ret = clear_port_feature(hdev, port1,
  USB_PORT_FEAT_POWER);
 +}
 
 Do we need these shortcuts?  How often will this routine be called when
 the port is already in the right state
When the PM Qos NO_POWER_OFF is changed,
pm_runtime_get_sync/put(port_dev) will be invoked. This routine will be
called during port resume and suspend. If one device was suspended and
power off, the port's usage_count would be 0. After then, the port will
be resumed and suspend every time pm qos NO_POWER_OFF flag being
changed. So this depends on the user space.

 
 @@ -2862,6 +2884,18 @@ int usb_port_suspend(struct usb_device *udev,
 pm_message_t msg)
  udev-port_is_suspended = 1;
  msleep(10);
  }
 +
 +/*
 + * Check whether current status is meeting requirement of
 + * usb port power off mechanism
 + */
 +if (!udev-do_remote_wakeup
 + (dev_pm_qos_flags(port_dev-dev,
 +PM_QOS_FLAG_NO_POWER_OFF) != PM_QOS_FLAGS_ALL)
 + udev-persist_enabled
 + !status)
 +pm_runtime_put_sync(port_dev-dev);
 
 You need to store somewhere the fact that you made this call, so that 
 you will know whether or not to make the corresponding 
 pm_runtime_get_sync call in usb_port_resume.
You mean I should add a new flag to keep the
pm_runtime_put_sync/put(port_dev) being called paired, right?
How about needs_resume?
 
 @@ -2982,10 +3035,39 @@ static int finish_port_resume(struct usb_device
 *udev)
  int usb_port_resume(struct usb_device *udev, pm_message_t msg)
  {
  struct usb_hub  *hub = hdev_to_hub(udev-parent);
 +struct usb_port *port_dev = hub-ports[udev-portnum  - 1];
  int port1 = udev-portnum;
  int status;
  u16 portchange, portstatus;

 +
 +set_bit(port1, hub-busy_bits);
 +
 +if (!port_dev-power_is_on) {
 
 This test is wrong.  It's possible that the port power is still on even 
 though you called pm_runtime_put_sync.
Above, we need to check the new flag, right?

 
 +status = pm_runtime_get_sync(port_dev-dev);
 +if (status  0) {
 +dev_dbg(udev-dev, can't resume usb port, status 
 %d\n,
 +status);
 +clear_bit(port1, hub-busy_bits);
 +return status;
 +}
 
 Don't you want to call usb_port_wait_for_connected right here?
 
 +}
 +
 +
 +/*
 + * Wait for usb hub port to be reconnected in order to make
 + * the resume procedure successful.
 + */
 +if (port_dev-needs_debounce) {
 +status = usb_port_wait_for_connected(hub, port1);
 
 If you move this call earlier then you won't have to test
 needs_debounce.
The port may have been power on when device is resumed. One case, after
device being suspended and power off, user may set the NO_POWER_OFF flag
and port will be power on before device being resumed. For this case,
port doesn't need to be resumed in the usb_port_resume() since port
already power on and wait for connected is enough. So I divide resume
port and wait for connected into two pieces. But from your rely, I
realize we should 

Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-12-09 Thread Lan Tianyu
On 2012年12月07日 23:22, Alan Stern wrote:
 On Fri, 7 Dec 2012, Lan Tianyu wrote:
 
 Maybe you really need two flags.  Do whatever is best; I'm sure you can
 figure out a good scheme.
 Yeah. Two flags maybe good. In this situation, it should be call
 power_is_on, right? power_is_on can be used to avoid to sending
 redundant PORT_POWER request. The other flag is dedicated to prevent
 device from being disconnected. Something like hub-busy_bits. We can
 can child_busy, I am not very good at giving a name. So I'd like to
 see your opinion. :)
 
 How about needs_debounce?
 
 Alan Stern
 
Hi Alan:
I write a patch based on the needs_debounce flag. The flag will be set
when port has child device and power on successfully. Otherwise, I
separate resume port and wait for connect in the usb_port_resume().
Device will not be disconnected when power_is_on is false or
needs_debounce  is set. Welcome comments.

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 86e595c..aa41d3a 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -26,6 +26,7 @@
 #include linux/mutex.h
 #include linux/freezer.h
 #include linux/random.h
+#include linux/pm_qos.h

 #include asm/uaccess.h
 #include asm/byteorder.h
@@ -108,11 +109,14 @@ MODULE_PARM_DESC(use_both_schemes,
 DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
 EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);

+#define HUB_PORT_RECONNECT_TRIES   20
+
 #define HUB_DEBOUNCE_TIMEOUT   1500
 #define HUB_DEBOUNCE_STEP25
 #define HUB_DEBOUNCE_STABLE 100

 static int usb_reset_and_verify_device(struct usb_device *udev);
+static int hub_port_debounce(struct usb_hub *hub, int port1);

 static inline char *portspeed(struct usb_hub *hub, int portstatus)
 {
@@ -718,13 +722,26 @@ int usb_hub_set_port_power(struct usb_device
*hdev, int port1,
bool set)
 {
int ret;
+   struct usb_hub *hub = hdev_to_hub(hdev);
+   struct usb_port *port_dev
+   = hub-ports[port1 - 1];
+
+   if (set) {
+   if (port_dev-power_is_on)
+   return 0;

-   if (set)
ret = set_port_feature(hdev, port1,
USB_PORT_FEAT_POWER);
-   else
+   } else {
+   if (!port_dev-power_is_on)
+   return 0;
+
ret = clear_port_feature(hdev, port1,
USB_PORT_FEAT_POWER);
+   }
+
+   if (!ret)
+   port_dev-power_is_on = set;
return ret;
 }

@@ -804,7 +821,11 @@ static unsigned hub_power_on(struct usb_hub *hub,
bool do_delay)
dev_dbg(hub-intfdev, trying to enable port power on 
non-switchable hub\n);
for (port1 = 1; port1 = hub-descriptor-bNbrPorts; port1++)
-   set_port_feature(hub-hdev, port1, USB_PORT_FEAT_POWER);
+   if (hub-ports[port1 - 1]-power_is_on)
+   set_port_feature(hub-hdev, port1, USB_PORT_FEAT_POWER);
+   else
+   clear_port_feature(hub-hdev, port1,
+   USB_PORT_FEAT_POWER);

/* Wait at least 100 msec for power to become stable */
delay = max(pgood_delay, (unsigned) 100);
@@ -2768,6 +2789,7 @@ EXPORT_SYMBOL_GPL(usb_enable_ltm);
 int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
 {
struct usb_hub  *hub = hdev_to_hub(udev-parent);
+   struct usb_port *port_dev = hub-ports[udev-portnum - 1];
int port1 = udev-portnum;
int status;

@@ -2862,6 +2884,18 @@ int usb_port_suspend(struct usb_device *udev,
pm_message_t msg)
udev-port_is_suspended = 1;
msleep(10);
}
+
+   /*
+* Check whether current status is meeting requirement of
+* usb port power off mechanism
+*/
+   if (!udev-do_remote_wakeup
+(dev_pm_qos_flags(port_dev-dev,
+   PM_QOS_FLAG_NO_POWER_OFF) != PM_QOS_FLAGS_ALL)
+udev-persist_enabled
+!status)
+   pm_runtime_put_sync(port_dev-dev);
+
usb_mark_last_busy(hub-hdev);
return status;
 }
@@ -2945,6 +2979,25 @@ static int finish_port_resume(struct usb_device
*udev)
return status;
 }

+static int usb_port_wait_for_connected(struct usb_hub *hub, int port1)
+{
+   int status, i;
+
+   for (i = 0; i  HUB_PORT_RECONNECT_TRIES; i++) {
+   status = hub_port_debounce(hub, port1);
+   if (status  USB_PORT_STAT_CONNECTION) {
+   /*
+* just clear enable-change feature since debounce
+*  has cleared connect-change feature.
+*/
+   clear_port_feature(hub-hdev, port1,
+   USB_PORT_FEAT_C_ENABLE);
+   return 0;
+  

Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-12-07 Thread Alan Stern
On Fri, 7 Dec 2012, Lan Tianyu wrote:

  Maybe you really need two flags.  Do whatever is best; I'm sure you can
  figure out a good scheme.
 Yeah. Two flags maybe good. In this situation, it should be call
 power_is_on, right? power_is_on can be used to avoid to sending
 redundant PORT_POWER request. The other flag is dedicated to prevent
 device from being disconnected. Something like hub-busy_bits. We can
 can child_busy, I am not very good at giving a name. So I'd like to
 see your opinion. :)

How about needs_debounce?

Alan Stern

--
To unsubscribe from this list: send the line unsubscribe linux-usb in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-12-06 Thread Alan Stern
On Thu, 6 Dec 2012, Lan Tianyu wrote:

 Hi Alan:
   Doing port_dev-power_on = true in usb_hub_set_port_power() just after
 set PORT_POWER feature will cause device to be disconnected. If user set
 PM Qos NO_POWER_OFF flag after the device was power off, the port would
 be power on and do port_dev-power_on = true. But the port connect
 change event couldn't be ignored and the device would be disconnected.

The problem is that you chose a bad name for this flag.  Does 
power_on mean that the power _is_ on, that the power _was_ on, that 
the power _will be_ on, or that the power _should be_ on?

Maybe you really need two flags.  Do whatever is best; I'm sure you can
figure out a good scheme.

Alan Stern

--
To unsubscribe from this list: send the line unsubscribe linux-usb in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-12-05 Thread Lan Tianyu
On 2012年11月29日 03:37, Alan Stern wrote:
 On Sat, 17 Nov 2012, Lan Tianyu wrote:
 
 This patch is to add usb port auto power off mechanism.
 When usb device is suspending, usb core will suspend usb port and
 usb port runtime pm callback will clear PORT_POWER feature to
 power off port if all conditions were met. These conditions are
 remote wakeup disable, pm qos NO_POWER_OFF flag clear and persist
 enable. When device is suspended and power off, usb core
 will ignore port's connect and enable change event to keep the device
 not being disconnected. When it resumes, power on port again.
 
 --- a/drivers/usb/core/hub.c
 +++ b/drivers/usb/core/hub.c
 
 @@ -2861,6 +2869,19 @@ int usb_port_suspend(struct usb_device *udev, 
 pm_message_t msg)
  udev-port_is_suspended = 1;
  msleep(10);
  }
 +
 +/*
 + * Check whether current status is meeting requirement of
 + * usb port power off mechanism
 + */
 +if (!udev-do_remote_wakeup
 + (dev_pm_qos_flags(port_dev-dev,
 +PM_QOS_FLAG_NO_POWER_OFF) != PM_QOS_FLAGS_ALL)
 + udev-persist_enabled
 + !status)
 +if (!pm_runtime_put_sync(port_dev-dev))
 +port_dev-power_on = false;
 
 You shouldn't need to change port_dev-power_on here.
 
 @@ -2981,10 +3021,36 @@ static int finish_port_resume(struct usb_device 
 *udev)
  int usb_port_resume(struct usb_device *udev, pm_message_t msg)
  {
  struct usb_hub  *hub = hdev_to_hub(udev-parent);
 +struct usb_port *port_dev = hub-ports[udev-portnum  - 1];
  int port1 = udev-portnum;
  int status;
  u16 portchange, portstatus;
  
 +
 +set_bit(port1, hub-busy_bits);
 +
 +if (!port_dev-power_on) {
 +status = pm_runtime_get_sync(port_dev-dev);
 +if (status  0) {
 +dev_dbg(udev-dev, can't resume usb port, status 
 %d\n,
 +status);
 +return status;
 
 You must not return without clearing hub-busy_bits.  Same thing 
 applies later on.
 
 +}
 +
 +port_dev-power_on = true;
 
 This isn't necessary.

Hi Alan:
Doing port_dev-power_on = true in usb_hub_set_port_power() just after
set PORT_POWER feature will cause device to be disconnected. If user set
PM Qos NO_POWER_OFF flag after the device was power off, the port would
be power on and do port_dev-power_on = true. But the port connect
change event couldn't be ignored and the device would be disconnected.

 
 --- a/drivers/usb/core/port.c
 +++ b/drivers/usb/core/port.c
 @@ -72,6 +72,9 @@ static int usb_port_runtime_resume(struct device *dev)
  struct usb_device *hdev =
  to_usb_device(dev-parent-parent);
  
 +if (port_dev-power_on)
 +return 0;
 +
 
 Move these lines into usb_hub_set_port_power, and have that routine set 
 port_dev-power_on when the Set-Feature request succeeds.
 
  return usb_hub_set_port_power(hdev, port_dev-portnum,
  true);
  }
 @@ -81,13 +84,21 @@ static int usb_port_runtime_suspend(struct device *dev)
  struct usb_port *port_dev = to_usb_port(dev);
  struct usb_device *hdev =
  to_usb_device(dev-parent-parent);
 +int retval;
  
  if (dev_pm_qos_flags(port_dev-dev, PM_QOS_FLAG_NO_POWER_OFF)
  == PM_QOS_FLAGS_ALL)
  return -EAGAIN;
  
 -return usb_hub_set_port_power(hdev, port_dev-portnum,
 +if (!port_dev-power_on)
 +return 0;
 +
 +retval = usb_hub_set_port_power(hdev, port_dev-portnum,
  false);
 +if (!retval)
 +port_dev-power_on = false;
 +
 
 Move all this port_dev-power_on stuff into usb_hub_set_port_power.  
 Then no changes will be needed here.

 
 Alan Stern
 
 


-- 
Best regards
Tianyu Lan
--
To unsubscribe from this list: send the line unsubscribe linux-usb in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 08/10] usb: add usb port auto power off mechanism

2012-11-28 Thread Alan Stern
On Sat, 17 Nov 2012, Lan Tianyu wrote:

 This patch is to add usb port auto power off mechanism.
 When usb device is suspending, usb core will suspend usb port and
 usb port runtime pm callback will clear PORT_POWER feature to
 power off port if all conditions were met. These conditions are
 remote wakeup disable, pm qos NO_POWER_OFF flag clear and persist
 enable. When device is suspended and power off, usb core
 will ignore port's connect and enable change event to keep the device
 not being disconnected. When it resumes, power on port again.

 --- a/drivers/usb/core/hub.c
 +++ b/drivers/usb/core/hub.c

 @@ -2861,6 +2869,19 @@ int usb_port_suspend(struct usb_device *udev, 
 pm_message_t msg)
   udev-port_is_suspended = 1;
   msleep(10);
   }
 +
 + /*
 +  * Check whether current status is meeting requirement of
 +  * usb port power off mechanism
 +  */
 + if (!udev-do_remote_wakeup
 +  (dev_pm_qos_flags(port_dev-dev,
 + PM_QOS_FLAG_NO_POWER_OFF) != PM_QOS_FLAGS_ALL)
 +  udev-persist_enabled
 +  !status)
 + if (!pm_runtime_put_sync(port_dev-dev))
 + port_dev-power_on = false;

You shouldn't need to change port_dev-power_on here.

 @@ -2981,10 +3021,36 @@ static int finish_port_resume(struct usb_device *udev)
  int usb_port_resume(struct usb_device *udev, pm_message_t msg)
  {
   struct usb_hub  *hub = hdev_to_hub(udev-parent);
 + struct usb_port *port_dev = hub-ports[udev-portnum  - 1];
   int port1 = udev-portnum;
   int status;
   u16 portchange, portstatus;
  
 +
 + set_bit(port1, hub-busy_bits);
 +
 + if (!port_dev-power_on) {
 + status = pm_runtime_get_sync(port_dev-dev);
 + if (status  0) {
 + dev_dbg(udev-dev, can't resume usb port, status 
 %d\n,
 + status);
 + return status;

You must not return without clearing hub-busy_bits.  Same thing 
applies later on.

 + }
 +
 + port_dev-power_on = true;

This isn't necessary.

 --- a/drivers/usb/core/port.c
 +++ b/drivers/usb/core/port.c
 @@ -72,6 +72,9 @@ static int usb_port_runtime_resume(struct device *dev)
   struct usb_device *hdev =
   to_usb_device(dev-parent-parent);
  
 + if (port_dev-power_on)
 + return 0;
 +

Move these lines into usb_hub_set_port_power, and have that routine set 
port_dev-power_on when the Set-Feature request succeeds.

   return usb_hub_set_port_power(hdev, port_dev-portnum,
   true);
  }
 @@ -81,13 +84,21 @@ static int usb_port_runtime_suspend(struct device *dev)
   struct usb_port *port_dev = to_usb_port(dev);
   struct usb_device *hdev =
   to_usb_device(dev-parent-parent);
 + int retval;
  
   if (dev_pm_qos_flags(port_dev-dev, PM_QOS_FLAG_NO_POWER_OFF)
   == PM_QOS_FLAGS_ALL)
   return -EAGAIN;
  
 - return usb_hub_set_port_power(hdev, port_dev-portnum,
 + if (!port_dev-power_on)
 + return 0;
 +
 + retval = usb_hub_set_port_power(hdev, port_dev-portnum,
   false);
 + if (!retval)
 + port_dev-power_on = false;
 +

Move all this port_dev-power_on stuff into usb_hub_set_port_power.  
Then no changes will be needed here.

Alan Stern


--
To unsubscribe from this list: send the line unsubscribe linux-usb in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH 08/10] usb: add usb port auto power off mechanism

2012-11-17 Thread Lan Tianyu
This patch is to add usb port auto power off mechanism.
When usb device is suspending, usb core will suspend usb port and
usb port runtime pm callback will clear PORT_POWER feature to
power off port if all conditions were met. These conditions are
remote wakeup disable, pm qos NO_POWER_OFF flag clear and persist
enable. When device is suspended and power off, usb core
will ignore port's connect and enable change event to keep the device
not being disconnected. When it resumes, power on port again.

Acked-by: Rafael J. Wysocki rafael.j.wyso...@intel.com
Signed-off-by: Lan Tianyu tianyu@intel.com
---
 drivers/usb/core/hub.c  |   75 +--
 drivers/usb/core/hub.h  |2 ++
 drivers/usb/core/port.c |   14 -
 3 files changed, 87 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 3e69d66..862 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -88,11 +88,14 @@ MODULE_PARM_DESC(use_both_schemes,
 DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
 EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
 
+#define HUB_PORT_RECONNECT_TRIES   20
+
 #define HUB_DEBOUNCE_TIMEOUT   1500
 #define HUB_DEBOUNCE_STEP25
 #define HUB_DEBOUNCE_STABLE 100
 
 static int usb_reset_and_verify_device(struct usb_device *udev);
+static int hub_port_debounce(struct usb_hub *hub, int port1);
 
 static inline char *portspeed(struct usb_hub *hub, int portstatus)
 {
@@ -784,7 +787,11 @@ static unsigned hub_power_on(struct usb_hub *hub, bool 
do_delay)
dev_dbg(hub-intfdev, trying to enable port power on 
non-switchable hub\n);
for (port1 = 1; port1 = hub-descriptor-bNbrPorts; port1++)
-   set_port_feature(hub-hdev, port1, USB_PORT_FEAT_POWER);
+   if (hub-ports[port1 - 1]-power_on)
+   set_port_feature(hub-hdev, port1, USB_PORT_FEAT_POWER);
+   else
+   clear_port_feature(hub-hdev, port1,
+   USB_PORT_FEAT_POWER);
 
/* Wait at least 100 msec for power to become stable */
delay = max(pgood_delay, (unsigned) 100);
@@ -2767,6 +2774,7 @@ EXPORT_SYMBOL_GPL(usb_enable_ltm);
 int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
 {
struct usb_hub  *hub = hdev_to_hub(udev-parent);
+   struct usb_port *port_dev = hub-ports[udev-portnum - 1];
int port1 = udev-portnum;
int status;
 
@@ -2861,6 +2869,19 @@ int usb_port_suspend(struct usb_device *udev, 
pm_message_t msg)
udev-port_is_suspended = 1;
msleep(10);
}
+
+   /*
+* Check whether current status is meeting requirement of
+* usb port power off mechanism
+*/
+   if (!udev-do_remote_wakeup
+(dev_pm_qos_flags(port_dev-dev,
+   PM_QOS_FLAG_NO_POWER_OFF) != PM_QOS_FLAGS_ALL)
+udev-persist_enabled
+!status)
+   if (!pm_runtime_put_sync(port_dev-dev))
+   port_dev-power_on = false;
+
usb_mark_last_busy(hub-hdev);
return status;
 }
@@ -2944,6 +2965,25 @@ static int finish_port_resume(struct usb_device *udev)
return status;
 }
 
+static int usb_port_wait_for_connected(struct usb_hub *hub, int port1)
+{
+   int status, i;
+
+   for (i = 0; i  HUB_PORT_RECONNECT_TRIES; i++) {
+   status = hub_port_debounce(hub, port1);
+   if (status  USB_PORT_STAT_CONNECTION) {
+   /*
+* just clear enable-change feature since debounce
+*  has cleared connect-change feature.
+*/
+   clear_port_feature(hub-hdev, port1,
+   USB_PORT_FEAT_C_ENABLE);
+   return 0;
+   }
+   }
+   return -ETIMEDOUT;
+}
+
 /*
  * usb_port_resume - re-activate a suspended usb device's upstream port
  * @udev: device to re-activate, not a root hub
@@ -2981,10 +3021,36 @@ static int finish_port_resume(struct usb_device *udev)
 int usb_port_resume(struct usb_device *udev, pm_message_t msg)
 {
struct usb_hub  *hub = hdev_to_hub(udev-parent);
+   struct usb_port *port_dev = hub-ports[udev-portnum  - 1];
int port1 = udev-portnum;
int status;
u16 portchange, portstatus;
 
+
+   set_bit(port1, hub-busy_bits);
+
+   if (!port_dev-power_on) {
+   status = pm_runtime_get_sync(port_dev-dev);
+   if (status  0) {
+   dev_dbg(udev-dev, can't resume usb port, status 
%d\n,
+   status);
+   return status;
+   }
+
+   port_dev-power_on = true;
+
+   /*
+