aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorLan Tianyu <tianyu.lan@intel.com>2012-09-05 01:44:38 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-09-10 16:04:01 -0400
commitca9c9d0c922e4f1dac80699ebab1269d6dbaff85 (patch)
treeacd82e9f8275ddbc34a9a69b05105337b32ce8fc /drivers/usb/core
parent3a22b872e923954853cabd5a18f41da1846bccdb (diff)
usb : Add sysfs files to control port power.
This patch adds two sysfs files for each usb hub port to allow userspace to control the port power policy. For an upcoming Intel xHCI roothub, this will translate into ACPI calls to completely power off or power on the port. As a reminder, when these ports are completely powered off, the USB host and device will see a physical disconnect. All future USB device connections will be lost, and the device will not be able to signal a remote wakeup. The control sysfs file can be written to with two options: "on" - port power must be on. "off" - port must be off. The state sysfs file reports usb port's power state: "on" - powered on "off" - powered off "error" - can't get power state For now, let userspace dictate the port power off policy. Future patches may add an in-kernel policy. Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Lan Tianyu <tianyu.lan@intel.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/hub.c133
1 files changed, 132 insertions, 1 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 4ffe8371adac..3def91eecaad 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -39,11 +39,17 @@
39#endif 39#endif
40#endif 40#endif
41 41
42enum port_power_policy {
43 USB_PORT_POWER_ON = 0,
44 USB_PORT_POWER_OFF,
45};
46
42struct usb_port { 47struct usb_port {
43 struct usb_device *child; 48 struct usb_device *child;
44 struct device dev; 49 struct device dev;
45 struct dev_state *port_owner; 50 struct dev_state *port_owner;
46 enum usb_port_connect_type connect_type; 51 enum usb_port_connect_type connect_type;
52 enum port_power_policy port_power_policy;
47}; 53};
48 54
49struct usb_hub { 55struct usb_hub {
@@ -93,6 +99,10 @@ struct usb_hub {
93 struct usb_port **ports; 99 struct usb_port **ports;
94}; 100};
95 101
102static const char on_string[] = "on";
103static const char off_string[] = "off";
104static const struct attribute_group *port_dev_group[];
105
96static inline int hub_is_superspeed(struct usb_device *hdev) 106static inline int hub_is_superspeed(struct usb_device *hdev)
97{ 107{
98 return (hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS); 108 return (hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS);
@@ -845,7 +855,12 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
845 dev_dbg(hub->intfdev, "trying to enable port power on " 855 dev_dbg(hub->intfdev, "trying to enable port power on "
846 "non-switchable hub\n"); 856 "non-switchable hub\n");
847 for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++) 857 for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++)
848 set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER); 858 if (hub->ports[port1 - 1]->port_power_policy
859 == USB_PORT_POWER_ON)
860 set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
861 else
862 clear_port_feature(hub->hdev, port1,
863 USB_PORT_FEAT_POWER);
849 864
850 /* Wait at least 100 msec for power to become stable */ 865 /* Wait at least 100 msec for power to become stable */
851 delay = max(pgood_delay, (unsigned) 100); 866 delay = max(pgood_delay, (unsigned) 100);
@@ -1263,6 +1278,7 @@ static int usb_hub_create_port_device(struct usb_hub *hub,
1263 1278
1264 hub->ports[port1 - 1] = port_dev; 1279 hub->ports[port1 - 1] = port_dev;
1265 port_dev->dev.parent = hub->intfdev; 1280 port_dev->dev.parent = hub->intfdev;
1281 port_dev->dev.groups = port_dev_group;
1266 port_dev->dev.type = &usb_port_device_type; 1282 port_dev->dev.type = &usb_port_device_type;
1267 dev_set_name(&port_dev->dev, "port%d", port1); 1283 dev_set_name(&port_dev->dev, "port%d", port1);
1268 1284
@@ -2627,6 +2643,25 @@ static int port_is_power_on(struct usb_hub *hub, unsigned portstatus)
2627 return ret; 2643 return ret;
2628} 2644}
2629 2645
2646static int usb_get_hub_port_power_state(struct usb_device *hdev, int port1)
2647{
2648 struct usb_hub *hub = hdev_to_hub(hdev);
2649 struct usb_port_status data;
2650 u16 portstatus;
2651 int ret;
2652
2653 ret = get_port_status(hub->hdev, port1, &data);
2654 if (ret < 4) {
2655 dev_err(hub->intfdev,
2656 "%s failed (err = %d)\n", __func__, ret);
2657 if (ret >= 0)
2658 ret = -EIO;
2659 return ret;
2660 } else
2661 portstatus = le16_to_cpu(data.wPortStatus);
2662 return port_is_power_on(hub, portstatus);
2663}
2664
2630#ifdef CONFIG_PM 2665#ifdef CONFIG_PM
2631 2666
2632/* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */ 2667/* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */
@@ -4633,6 +4668,102 @@ static int hub_thread(void *__unused)
4633 return 0; 4668 return 0;
4634} 4669}
4635 4670
4671static ssize_t show_port_power_state(struct device *dev,
4672 struct device_attribute *attr, char *buf)
4673{
4674 struct usb_device *udev = to_usb_device(dev->parent->parent);
4675 struct usb_interface *intf = to_usb_interface(dev->parent);
4676 int port1, power_state;
4677 const char *result;
4678
4679 sscanf(dev_name(dev), "port%d", &port1);
4680 usb_autopm_get_interface(intf);
4681 power_state = usb_get_hub_port_power_state(udev, port1);
4682 usb_autopm_put_interface(intf);
4683 if (power_state == 1)
4684 result = on_string;
4685 else if (!power_state)
4686 result = off_string;
4687 else
4688 result = "error";
4689 return sprintf(buf, "%s\n", result);
4690}
4691static DEVICE_ATTR(state, S_IRUGO, show_port_power_state, NULL);
4692
4693static ssize_t show_port_power_control(struct device *dev,
4694 struct device_attribute *attr, char *buf)
4695{
4696 struct usb_port *hub_port = to_usb_port(dev);
4697 const char *result;
4698
4699 switch (hub_port->port_power_policy) {
4700 case USB_PORT_POWER_ON:
4701 result = on_string;
4702 break;
4703 case USB_PORT_POWER_OFF:
4704 result = off_string;
4705 break;
4706 default:
4707 return -EINVAL;
4708 }
4709 return sprintf(buf, "%s\n", result);
4710}
4711
4712static ssize_t store_port_power_control(struct device *dev,
4713 struct device_attribute *attr, const char *buf, size_t count)
4714{
4715 struct usb_device *hdev = to_usb_device(dev->parent->parent);
4716 struct usb_interface *intf = to_usb_interface(dev->parent);
4717 struct usb_port *hub_port = to_usb_port(dev);
4718 int port1, ret, len = count;
4719 char *cp;
4720
4721 sscanf(dev_name(dev), "port%d", &port1);
4722 cp = memchr(buf, '\n', count);
4723 if (cp)
4724 len = cp - buf;
4725 if (len == sizeof(on_string) - 1
4726 && strncmp(buf, on_string, len) == 0) {
4727 hub_port->port_power_policy = USB_PORT_POWER_ON;
4728 usb_autopm_get_interface(intf);
4729 ret = set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
4730 usb_autopm_put_interface(intf);
4731 if (ret < 0)
4732 return -EIO;
4733 } else if (len == sizeof(off_string) - 1
4734 && strncmp(buf, off_string, len) == 0) {
4735 struct usb_hub *hub = hdev_to_hub(hdev);
4736
4737 hub_port->port_power_policy = USB_PORT_POWER_OFF;
4738 usb_autopm_get_interface(intf);
4739 hub_port_logical_disconnect(hub, port1);
4740 ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
4741 usb_autopm_put_interface(intf);
4742 if (ret < 0)
4743 return -EIO;
4744 } else
4745 return -EINVAL;
4746
4747 return count;
4748}
4749static DEVICE_ATTR(control, S_IWUSR | S_IRUGO, show_port_power_control,
4750 store_port_power_control);
4751
4752static struct attribute *port_dev_attrs[] = {
4753 &dev_attr_control.attr,
4754 &dev_attr_state.attr,
4755 NULL,
4756};
4757
4758static struct attribute_group port_dev_attr_grp = {
4759 .attrs = port_dev_attrs,
4760};
4761
4762static const struct attribute_group *port_dev_group[] = {
4763 &port_dev_attr_grp,
4764 NULL,
4765};
4766
4636static const struct usb_device_id hub_id_table[] = { 4767static const struct usb_device_id hub_id_table[] = {
4637 { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS, 4768 { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
4638 .bDeviceClass = USB_CLASS_HUB}, 4769 .bDeviceClass = USB_CLASS_HUB},