|
|
|
Re: [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] |
|
On Mon, Jun 11, 2012 at 10:24:34AM +0800, Lan Tianyu wrote:
> 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;
Why do you need an attribute per port here? Shouldn't they just be
static variables? Why duplicate them for every port?
> 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();
Did you just race userspace with these attributes? Are you sure?
thanks,
greg k-h
--
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

[Linux Media] [Video for Linux] [Linux Input] [Linux Audio Users] [Photo] [Yosemite News] [Yosemite Photos] [Free Online Dating] [Linux Kernel] [Linux SCSI] [Old Linux USB Devel Archive] [More Archives]
![]() |
![]() |