|
|
|
[PATCH 3/3] usb : Add sysfs files to control usb port's power | |
| [Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] | |
This patch is to add two sys files for each usb hub ports to control port's power.
Control port's power through setting or clearing PORT_POWER feature.
The new sys files is under the usb hub interface sysfs portx directory.
ls /sys/bus/usb/devices/1-0:1.0
bAlternateSetting bInterfaceClass bInterfaceNumber bInterfaceProtocol
bInterfaceSubClass bNumEndpoints driver ep_81 modalias power
subsystem supports_autosuspend uevent port1 port2 port3 port4
ls /sys/bus/usb/devices/1-0:1.0/port1
control state
control has two options. "on" and "off"
"on" - port power must be on.
"off" - port power must be off.
state reports usb port's power state
"on" - power on
"off" - power off
"error" - can't get power state
Signed-off-by: Lan Tianyu <tianyu.lan@xxxxxxxxx>
---
Documentation/ABI/testing/sysfs-bus-usb | 33 ++++++
drivers/usb/core/hub.c | 168 ++++++++++++++++++++++++++++++-
2 files changed, 200 insertions(+), 1 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
index 6df4e6f..c2fb9d7 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -208,3 +208,36 @@ Description:
such as ACPI. This file will read either "removable" or
"fixed" if the information is available, and "unknown"
otherwise.
+
+What: /sys/bus/usb/devices/.../(hub interface)/portX
+Date: June 2012
+Contact Lan Tianyu <tianyu.lan@xxxxxxxxx>
+Description:
+ The /sys/bus/usb/device/.../(hub interface)/portX directory
+ contains attributes allowing user space to check and modify
+ properties of the associated USB port.
+
+What: /sys/bus/usb/devices/.../(hub interface)/portX/control
+Date: June 2012
+Contact Lan Tianyu <tianyu.lan@xxxxxxxxx>
+Description:
+ The /sys/bus/usb/devices/.../(hub interface)/portX/control
+ attribute allows user space to control the power policy on
+ the usb port.
+
+ All ports have one of the following two values for control
+ "on" - port power must be on.
+ "off" - port power must be off.
+
+What: /sys/bus/usb/devices/.../(hub interface)/portX/state
+Date: June 2012
+Contact Lan Tianyu <tianyu.lan@xxxxxxxxx>
+Description:
+ The /sys/bus/usb/devices/.../(hub interface)/portX/state
+ attribute allows user space to check hub port's power state.
+
+ All ports have three following states
+ "on" - port power on
+ "off" - port power off
+ "error" - can't get the hub port's power state
+
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 2ed5244..e83b0f5 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -41,12 +41,20 @@
#endif
#endif
+enum port_power_policy {
+ USB_PORT_POWER_ON = 0,
+ USB_PORT_POWER_OFF,
+};
+
struct usb_hub_port {
void *port_owner;
struct usb_device *child;
#ifdef CONFIG_ACPI
acpi_handle port_acpi_handle;
#endif
+ struct device_attribute port_control_attr;
+ struct device_attribute port_state_attr;
+ enum port_power_policy port_power_policy;
enum usb_port_connect_type connect_type;
};
@@ -97,6 +105,12 @@ struct usb_hub {
struct usb_hub_port *port_data;
};
+static const char on_string[] = "on";
+static const char off_string[] = "off";
+static char port_name[USB_MAXCHILDREN][8];
+static void usb_hub_port_sysfs_add(struct usb_hub *hub);
+static void usb_hub_port_sysfs_remove(struct usb_hub *hub);
+
static inline int hub_is_superspeed(struct usb_device *hdev)
{
return (hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS);
@@ -847,7 +861,9 @@ 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->port_data[port1 - 1].port_power_policy
+ == USB_PORT_POWER_ON)
+ set_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);
@@ -1493,6 +1509,7 @@ static int hub_configure(struct usb_hub *hub,
if (hub->has_indicators && blinkenlights)
hub->indicator [0] = INDICATOR_CYCLE;
+ usb_hub_port_sysfs_add(hub);
usb_acpi_bind_hub_ports(hdev);
hub_activate(hub, HUB_INIT);
return 0;
@@ -1531,6 +1548,7 @@ static void hub_disconnect(struct usb_interface *intf)
hub->error = 0;
hub_quiesce(hub, HUB_DISCONNECT);
+ usb_hub_port_sysfs_remove(hub);
usb_set_intfdata (intf, NULL);
hub->hdev->maxchild = 0;
@@ -2558,6 +2576,25 @@ static int port_is_power_on(struct usb_hub *hub, unsigned portstatus)
return ret;
}
+static int usb_get_hub_port_power_state(struct usb_device *hdev, int port1)
+{
+ struct usb_hub *hub = hdev_to_hub(hdev);
+ struct usb_port_status data;
+ u16 portstatus;
+ int ret;
+
+ ret = get_port_status(hub->hdev, port1, &data);
+ if (ret < 4) {
+ dev_err(hub->intfdev,
+ "%s failed (err = %d)\n", __func__, ret);
+ if (ret >= 0)
+ ret = -EIO;
+ return ret;
+ } else
+ portstatus = le16_to_cpu(data.wPortStatus);
+ return port_is_power_on(hub, portstatus);
+}
+
#ifdef CONFIG_PM
/* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */
@@ -4499,6 +4536,134 @@ static int hub_thread(void *__unused)
return 0;
}
+static ssize_t show_port_power_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_device *udev = to_usb_device(dev->parent);
+ struct usb_hub_port *hub_port = container_of(attr,
+ struct usb_hub_port, port_state_attr);
+ struct usb_hub *hub = dev_get_drvdata(dev);
+ int port1 = hub_port - hub->port_data + 1;
+ int power_state;
+ const char *result;
+
+ power_state = usb_get_hub_port_power_state(udev, port1);
+ if (power_state == 1)
+ result = on_string;
+ else if (!power_state)
+ result = off_string;
+ else
+ result = "error";
+ return sprintf(buf, "%s\n", result);
+}
+
+static ssize_t show_port_power_control(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_hub_port *hub_port = container_of(attr,
+ struct usb_hub_port, port_control_attr);
+ const char *result;
+
+ switch (hub_port->port_power_policy) {
+ case USB_PORT_POWER_ON:
+ result = on_string;
+ break;
+ case USB_PORT_POWER_OFF:
+ result = off_string;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return sprintf(buf, "%s\n", result);
+}
+
+static ssize_t
+store_port_power_control(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_device *udev = to_usb_device(dev->parent);
+ struct usb_hub_port *hub_port = container_of(attr,
+ struct usb_hub_port, port_control_attr);
+ char *cp;
+ struct usb_hub *hub = dev_get_drvdata(dev);
+ int port1 = hub_port - hub->port_data + 1;
+ int len = count;
+
+ cp = memchr(buf, '\n', count);
+ if (cp)
+ len = cp - buf;
+
+ if (len == sizeof on_string - 1
+ && strncmp(buf, on_string, len) == 0) {
+ hub_port->port_power_policy = USB_PORT_POWER_ON;
+ set_port_feature(udev, port1, USB_PORT_FEAT_POWER);
+ } else if (len == sizeof off_string - 1
+ && strncmp(buf, off_string, len) == 0) {
+ hub_port->port_power_policy = USB_PORT_POWER_OFF;
+ clear_port_feature(udev, port1, USB_PORT_FEAT_POWER);
+ } else
+ return -EINVAL;
+
+ return count;
+}
+
+static void usb_hub_port_sysfs_add(struct usb_hub *hub)
+{
+ int i, rc;
+ struct attribute *port_attrs[3];
+ struct attribute_group port_attr_group;
+
+ for (i = 0; i < hub->hdev->maxchild; i++) {
+ hub->port_data[i].port_control_attr = (struct device_attribute)
+ __ATTR(control, S_IRUGO | S_IWUSR,
+ show_port_power_control,
+ store_port_power_control);
+ hub->port_data[i].port_state_attr = (struct device_attribute)
+ __ATTR(state, S_IRUGO, show_port_power_state, NULL);
+
+ port_attrs[0] = &hub->port_data[i].port_control_attr.attr;
+ port_attrs[1] = &hub->port_data[i].port_state_attr.attr;
+ port_attrs[2] = NULL;
+ port_attr_group = (struct attribute_group) {
+ .name = port_name[i],
+ .attrs = port_attrs
+ };
+
+ rc = sysfs_create_group(&hub->intfdev->kobj,
+ &port_attr_group);
+ if (rc < 0)
+ dev_err(hub->intfdev, "can't create sys " \
+ "files for port%d\n", i + 1);
+ }
+}
+
+static void usb_hub_port_sysfs_remove(struct usb_hub *hub)
+{
+ int i;
+ struct attribute *port_attrs[3];
+ struct attribute_group port_attr_group;
+
+ for (i = 0; i < hub->hdev->maxchild; i++) {
+ port_attrs[0] = &hub->port_data[i].port_control_attr.attr;
+ port_attrs[1] = &hub->port_data[i].port_state_attr.attr;
+ port_attrs[2] = NULL;
+ port_attr_group = (struct attribute_group) {
+ .name = port_name[i],
+ .attrs = port_attrs
+ };
+ sysfs_remove_group(&hub->intfdev->kobj,
+ &port_attr_group);
+ }
+}
+
+static void usb_hub_port_sysfs_init(void)
+{
+ int i;
+
+ for (i = 0; i < USB_MAXCHILDREN; i++)
+ sprintf(port_name[i], "port%d", i + 1);
+}
+
static const struct usb_device_id hub_id_table[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
.bDeviceClass = USB_CLASS_HUB},
@@ -4531,6 +4696,7 @@ int usb_hub_init(void)
return -1;
}
+ usb_hub_port_sysfs_init();
khubd_task = kthread_run(hub_thread, NULL, "khubd");
if (!IS_ERR(khubd_task))
return 0;
--
1.7.6.rc2.8.g28eb
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
[Site Home] [Linux IBM ACPI] [Linux Power Management] [Linux Kernel] [Linux Laptop] [Kernel Newbies] [Share Photos] [Security] [Netfilter] [Bugtraq] [Rubini] [Photo] [Yosemite Photos] [Yosemite News] [MIPS Linux] [ARM Linux] [Linux Security] [Linux RAID] [Samba] [Video 4 Linux] [Device Mapper] [Linux Resources]
![]() |