hi alan:
I accord to your advice to implement usb port power off mechanism
for port with device (add "auto" option for portX/control to allow port
to be power off during device being suspended and power on when resuming).
http://marc.info/?l=linux-usb&m=133675841421390&w=2
I still don't see what the problem is. They don't have to be
synchronized; all you need to do is make sure that the port's state
remains set to "off" until the debouncing is finished and you have
turned off the connect-change and enable-change features.
But the device is still disconnected after powering on the port during
resuming. Caused by that port_power had been set to "on" when connect-change
event was processed. My patch is at the bottom of the mail. If something
wrong, please help me to correct. Thanks.
Following is output, I add some debug infos in my code when port's power_state
is set to "on" or "off" and device attached to port is disconnected.
[ 559.575298] usb 1-2: usb auto-suspend, wakeup 0
[ 559.586280] usb_port_suspend: 2926 set port2 power off
[ 559.586295] bus-0329 [4294967270] __acpi_bus_set_power : Device [HSP2]
transitioned to D3
[ 559.586302] usb usb1: The power of hub port 2 was set to 0
[ 561.728637] hub 1-0:1.0: hub_suspend
[ 561.728647] usb usb1: bus auto-suspend, wakeup 0
[ 898.124290] utils-0286 [4294967270] evaluate_integer : Return value
[3072]
[ 898.124297] bus-0263 [4294967270] __acpi_bus_set_power : Device is
already at D3
[ 898.124300] bus-0263 [4294967270] __acpi_bus_set_power : Device is
already at D3
[ 898.124303] bus-0263 [4294967270] __acpi_bus_set_power : Device is
already at D0
[ 898.124306] bus-0263 [4294967270] __acpi_bus_set_power : Device is
already at D0
[ 898.124309] bus-0263 [4294967270] __acpi_bus_set_power : Device is
already at D0
[ 898.124559] utils-0286 [4294967274] evaluate_integer : Return value
[3092]
[ 901.773438] usb usb1: usb auto-resume
[ 901.784546] hub 1-0:1.0: hub_resume
[ 901.784560] hub 1-0:1.0: port 1: status 0307 change 0000
[ 901.784564] hub 1-0:1.0: port 2: status 0000 change 0000
[ 901.784608] hub 1-0:1.0: state 7 ports 4 chg 0004 evt 0000
[ 901.784618] hub 1-0:1.0: port 2, status 0000, change 0000, 12 Mb/s
[ 901.805532] usb 1-1: usb auto-resume
[ 901.805580] hub 1-0:1.0: state 7 ports 4 chg 0000 evt 0002
[ 901.842482] usb 1-1: finish resume
[ 901.843776] bus-0329 [4294967274] __acpi_bus_set_power : Device [HSP2]
transitioned to D0
[ 901.843784] usb usb1: The power of hub port 2 was set to 1
[ 901.864999] hub 1-0:1.0: state 7 ports 4 chg 0000 evt 0004
[ 901.865015] hub 1-0:1.0: port 2, status 0101, change 0001, 12 Mb/s
[ 901.973316] hub 1-0:1.0: debounce: port 2: total 125ms stable 100ms status 0x101
[ 901.973324] usb_port_resume: port2 connect state on 4295567693
[ 901.973331] usb 1-2: finish reset-resume
[ 902.024255] hub 1-0:1.0: port 2 not reset yet, waiting 50ms
[ 902.126373] usb 1-2: reset high-speed USB device number 3 using xhci_hcd
[ 902.143173] xhci_hcd 0000:00:14.0: xHCI xhci_drop_endpoint called with
disabled ep ffff88014032ae80
[ 902.143188] xhci_hcd 0000:00:14.0: xHCI xhci_drop_endpoint called with
disabled ep ffff88014032aec0
[ 902.143195] usb 1-2: ep 0x1 - rounding interval to 32768 microframes, ep desc
says 0 microframes
[ 902.143198] usb 1-2: ep 0x82 - rounding interval to 32768 microframes, ep
desc says 0 microframes
[ 902.143324] usb 1-2: Successful Endpoint Configure command
[ 902.143716] hub_port_connect_change: port2 attached device disconnect 4295567863
[ 902.143720] usb 1-2: USB disconnect, device number 3
[ 902.143722] usb 1-2: unregistering device
[ 902.143724] usb 1-2: unregistering interface 1-2:1.0
[ 902.144514] usb 1-2: usb_disable_device nuking all URBs
[ 902.144576] usb 1-2: Successful Endpoint Configure command
[ 902.247875] hub 1-0:1.0: debounce: port 2: total 100ms stable 100ms status 0x503
[ 902.298888] hub 1-0:1.0: port 2 not reset yet, waiting 50ms
[ 902.400758] usb 1-2: new high-speed USB device number 4 using xhci_hcd
[ 902.419019] usb 1-2: default language 0x0409
[ 902.425862] usb 1-2: udev 4, busnum 1, minor = 3
[ 902.425867] usb 1-2: New USB device found, idVendor=0bda, idProduct=0158
[ 902.425869] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 902.425871] usb 1-2: Product: USB2.0-CRW
[ 902.425873] usb 1-2: Manufacturer: Generic
[ 902.425874] usb 1-2: SerialNumber: 20060413092100000
[ 902.426089] usb 1-2: usb_probe_device
[ 902.426093] usb 1-2: configuration #1 chosen from 1 choice
[ 902.426101] usb 1-2: ep 0x1 - rounding interval to 32768 microframes, ep desc
says 0 microframes
[ 902.426104] usb 1-2: ep 0x82 - rounding interval to 32768 microframes, ep
desc says 0 microframes
[ 902.426176] usb 1-2: Successful Endpoint Configure command
[ 902.426689] usb 1-2: adding 1-2:1.0 (config #1, interface 0)
[ 902.427409] uas 1-2:1.0: usb_probe_interface
[ 902.427411] uas 1-2:1.0: usb_probe_interface - got id
Index: usb/drivers/usb/core/hub.c
===================================================================
--- usb.orig/drivers/usb/core/hub.c
+++ usb/drivers/usb/core/hub.c
@@ -41,6 +41,7 @@
enum port_power_policy {
USB_PORT_POWER_ON = 0,
USB_PORT_POWER_OFF,
+ USB_PORT_POWER_AUTO,
};
struct usb_port {
@@ -49,6 +50,7 @@ struct usb_port {
struct dev_state *port_owner;
enum usb_port_connect_type connect_type;
enum port_power_policy port_power_policy;
+ unsigned power_state:2; /* the power state of usb port */
};
struct usb_hub {
@@ -104,6 +106,7 @@ struct device_type usb_port_device_type
static const char on_string[] = "on";
static const char off_string[] = "off";
+static const char auto_string[] = "auto";
static const struct attribute_group *port_dev_group[];
static inline int hub_is_superspeed(struct usb_device *hdev)
@@ -176,10 +179,17 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rws
#define HUB_DEBOUNCE_STEP 25
#define HUB_DEBOUNCE_STABLE 100
+#define USB_PORT_POWER_STATE_ON 0
+#define USB_PORT_POWER_STATE_OFF 1
+
+#define HUB_PORT_RECONNECT_TRIES 20
+
#define to_usb_port(_dev) \
container_of(_dev, struct usb_port, dev)
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)
{
@@ -2903,6 +2913,20 @@ int usb_port_suspend(struct usb_device *
usb_set_device_state(udev, USB_STATE_SUSPENDED);
msleep(10);
}
+
+ /*
+ * Check whether current status is meeting requirement of
+ * usb port power off mechanism
+ */
+ if (!udev->do_remote_wakeup
+ && hub->ports[port1 - 1]->port_power_policy
+ == USB_PORT_POWER_AUTO && udev->persist_enabled
+ && !status) {
+ pr_info("%s: %d set port%d power off\n", __func__, __LINE__, port1);
+ clear_port_feature(udev->parent, port1, USB_PORT_FEAT_POWER);
+ hub->ports[port1 - 1]->power_state = USB_PORT_POWER_STATE_OFF;
+ }
+
usb_mark_last_busy(hub->hdev);
return status;
}
@@ -2986,6 +3010,25 @@ static int finish_port_resume(struct usb
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_STAT_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
@@ -3027,6 +3070,24 @@ int usb_port_resume(struct usb_device *u
int status;
u16 portchange, portstatus;
+ if (hub->ports[port1 - 1]->port_power_policy == USB_PORT_POWER_AUTO
+ && hub->ports[port1 - 1]->power_state == USB_PORT_POWER_STATE_OFF) {
+ set_port_feature(udev->parent, port1, USB_PORT_FEAT_POWER);
+
+ /*
+ * Wait for usb hub port to be reconnected in order to make
+ * the resume procedure successful.
+ */
+ status = usb_port_wait_for_connected(hub, port1);
+ if (status < 0) {
+ dev_dbg(&udev->dev, "can't get reconnection after setting port " \
+ "power on, status %d\n", status);
+ return status;
+ }
+ hub->ports[port1 - 1]->power_state = USB_PORT_POWER_STATE_ON;
+ pr_info("%s: port%d connect state on %ld\n", __func__, port1, jiffies);
+ }
+
/* Skip the initial Clear-Suspend step for a remote wakeup */
status = hub_port_status(hub, port1, &portstatus, &portchange);
if (status == 0 && !port_is_suspended(hub, portstatus))
@@ -3058,7 +3119,6 @@ int usb_port_resume(struct usb_device *u
* sequence.
*/
status = hub_port_status(hub, port1, &portstatus, &portchange);
-
/* TRSMRCY = 10 msec */
msleep(10);
}
@@ -4177,6 +4237,13 @@ static void hub_port_connect_change(stru
}
}
+ if (hub->ports[port1 - 1]->port_power_policy == USB_PORT_POWER_AUTO
+ && hub->ports[port1 - 1]->power_state == USB_PORT_POWER_STATE_OFF) {
+ clear_bit(port1, hub->change_bits);
+ return;
+ }
+
+ pr_info("%s: port%d attached device disconnect %ld\n", __func__, port1, jiffies);
/* Disconnect any existing devices under this port */
if (udev)
usb_disconnect(&hub->ports[port1 - 1]->child);
@@ -4694,6 +4768,9 @@ static ssize_t show_port_power_control(s
case USB_PORT_POWER_OFF:
result = off_string;
break;
+ case USB_PORT_POWER_AUTO:
+ result = auto_string;
+ break;
default:
return -EINVAL;
}
@@ -4732,6 +4809,9 @@ static ssize_t store_port_power_control(
usb_autopm_put_interface(intf);
if (ret < 0)
return -EIO;
+ } else if (len == sizeof auto_string - 1
+ && strncmp(buf, auto_string, len) == 0) {
+ hub_port->port_power_policy = USB_PORT_POWER_AUTO;
} else
return -EINVAL;
--
Best Regards
Tianyu Lan
linux kernel enabling team
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html