Discussion about implementation of usb port power off mechanism for port with device

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux