aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLan Tianyu <tianyu.lan@intel.com>2012-09-05 01:44:34 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-09-10 16:04:01 -0400
commit05f916894a692f0cc0973aef21521133623b21c0 (patch)
treee6de832d1a7ec2fe92235a2f1cd6e2e6c6d33ae7
parentd557542421da643358201664903e67fd01dfca1a (diff)
usb/acpi: Store info on device removability.
In the upcoming USB port power off patches, we need to know whether a USB port can ever see a disconnect event. Often USB ports are internal to a system, and users can't disconnect USB devices from that port. Sometimes those ports will remain empty, because the OEM chose not to connect an internal USB device to that port. According to ACPI Spec 9.13, PLD indicates whether USB port is user visible and _UPC indicates whether a USB device can be connected to the USB port (we'll call this "connectible"). Here's a matrix of the possible combinations: Visible Connectible Name Example ------------------------------------------------------------------------- Yes No Unknown (Invalid state.) Yes Yes Hot-plug USB ports on the outside of a laptop. A user could freely connect and disconnect USB devices. No Yes Hard-wired A USB modem hard-wired to a port on the inside of a laptop. No No Not used The port is internal to the system and will remain empty. Represent each of these four states with an enum usb_port_connect_type. The four states are USB_PORT_CONNECT_TYPE_UNKNOWN, USB_PORT_CONNECT_TYPE_HOT_PLUG, USB_PORT_CONNECT_TYPE_HARD_WIRED, and USB_PORT_NOT_USED. When we get the USB port's acpi_handle, store the state in connect_type in struct usb_port. 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>
-rw-r--r--drivers/usb/core/hub.c31
-rw-r--r--drivers/usb/core/usb-acpi.c80
-rw-r--r--drivers/usb/core/usb.h4
-rw-r--r--include/linux/usb.h7
4 files changed, 87 insertions, 35 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 3341b55f495c..4ffe8371adac 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -43,6 +43,7 @@ struct usb_port {
43 struct usb_device *child; 43 struct usb_device *child;
44 struct device dev; 44 struct device dev;
45 struct dev_state *port_owner; 45 struct dev_state *port_owner;
46 enum usb_port_connect_type connect_type;
46}; 47};
47 48
48struct usb_hub { 49struct usb_hub {
@@ -5078,6 +5079,36 @@ struct usb_device *usb_hub_find_child(struct usb_device *hdev,
5078} 5079}
5079EXPORT_SYMBOL_GPL(usb_hub_find_child); 5080EXPORT_SYMBOL_GPL(usb_hub_find_child);
5080 5081
5082/**
5083 * usb_set_hub_port_connect_type - set hub port connect type.
5084 * @hdev: USB device belonging to the usb hub
5085 * @port1: port num of the port
5086 * @type: connect type of the port
5087 */
5088void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1,
5089 enum usb_port_connect_type type)
5090{
5091 struct usb_hub *hub = hdev_to_hub(hdev);
5092
5093 hub->ports[port1 - 1]->connect_type = type;
5094}
5095
5096/**
5097 * usb_get_hub_port_connect_type - Get the port's connect type
5098 * @hdev: USB device belonging to the usb hub
5099 * @port1: port num of the port
5100 *
5101 * Return connect type of the port and if input params are
5102 * invalid, return USB_PORT_CONNECT_TYPE_UNKNOWN.
5103 */
5104enum usb_port_connect_type
5105usb_get_hub_port_connect_type(struct usb_device *hdev, int port1)
5106{
5107 struct usb_hub *hub = hdev_to_hub(hdev);
5108
5109 return hub->ports[port1 - 1]->connect_type;
5110}
5111
5081#ifdef CONFIG_ACPI 5112#ifdef CONFIG_ACPI
5082/** 5113/**
5083 * usb_get_hub_port_acpi_handle - Get the usb port's acpi handle 5114 * usb_get_hub_port_acpi_handle - Get the usb port's acpi handle
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index 47197bf0b28d..404d86afb243 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -19,20 +19,29 @@
19 19
20#include "usb.h" 20#include "usb.h"
21 21
22static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle) 22static int usb_acpi_check_port_connect_type(struct usb_device *hdev,
23 acpi_handle handle, int port1)
23{ 24{
24 acpi_status status; 25 acpi_status status;
25 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 26 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
26 union acpi_object *upc; 27 union acpi_object *upc;
28 struct acpi_pld pld;
27 int ret = 0; 29 int ret = 0;
28 30
29 status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer); 31 /*
30 32 * Accoding to ACPI Spec 9.13. PLD indicates whether usb port is
33 * user visible and _UPC indicates whether it is connectable. If
34 * the port was visible and connectable, it could be freely connected
35 * and disconnected with USB devices. If no visible and connectable,
36 * a usb device is directly hard-wired to the port. If no visible and
37 * no connectable, the port would be not used.
38 */
39 status = acpi_get_physical_device_location(handle, &pld);
31 if (ACPI_FAILURE(status)) 40 if (ACPI_FAILURE(status))
32 return -ENODEV; 41 return -ENODEV;
33 42
43 status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
34 upc = buffer.pointer; 44 upc = buffer.pointer;
35
36 if (!upc || (upc->type != ACPI_TYPE_PACKAGE) 45 if (!upc || (upc->type != ACPI_TYPE_PACKAGE)
37 || upc->package.count != 4) { 46 || upc->package.count != 4) {
38 ret = -EINVAL; 47 ret = -EINVAL;
@@ -40,33 +49,20 @@ static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle)
40 } 49 }
41 50
42 if (upc->package.elements[0].integer.value) 51 if (upc->package.elements[0].integer.value)
43 udev->removable = USB_DEVICE_REMOVABLE; 52 if (pld.user_visible)
44 else 53 usb_set_hub_port_connect_type(hdev, port1,
45 udev->removable = USB_DEVICE_FIXED; 54 USB_PORT_CONNECT_TYPE_HOT_PLUG);
55 else
56 usb_set_hub_port_connect_type(hdev, port1,
57 USB_PORT_CONNECT_TYPE_HARD_WIRED);
58 else if (!pld.user_visible)
59 usb_set_hub_port_connect_type(hdev, port1, USB_PORT_NOT_USED);
46 60
47out: 61out:
48 kfree(upc); 62 kfree(upc);
49 return ret; 63 return ret;
50} 64}
51 65
52static int usb_acpi_check_pld(struct usb_device *udev, acpi_handle handle)
53{
54 acpi_status status;
55 struct acpi_pld pld;
56
57 status = acpi_get_physical_device_location(handle, &pld);
58
59 if (ACPI_FAILURE(status))
60 return -ENODEV;
61
62 if (pld.user_visible)
63 udev->removable = USB_DEVICE_REMOVABLE;
64 else
65 udev->removable = USB_DEVICE_FIXED;
66
67 return 0;
68}
69
70static int usb_acpi_find_device(struct device *dev, acpi_handle *handle) 66static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
71{ 67{
72 struct usb_device *udev; 68 struct usb_device *udev;
@@ -88,8 +84,30 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
88 */ 84 */
89 if (is_usb_device(dev)) { 85 if (is_usb_device(dev)) {
90 udev = to_usb_device(dev); 86 udev = to_usb_device(dev);
91 if (udev->parent) 87 if (udev->parent) {
88 enum usb_port_connect_type type;
89
90 /*
91 * According usb port's connect type to set usb device's
92 * removability.
93 */
94 type = usb_get_hub_port_connect_type(udev->parent,
95 udev->portnum);
96 switch (type) {
97 case USB_PORT_CONNECT_TYPE_HOT_PLUG:
98 udev->removable = USB_DEVICE_REMOVABLE;
99 break;
100 case USB_PORT_CONNECT_TYPE_HARD_WIRED:
101 udev->removable = USB_DEVICE_FIXED;
102 break;
103 default:
104 udev->removable = USB_DEVICE_REMOVABLE_UNKNOWN;
105 break;
106 }
107
92 return -ENODEV; 108 return -ENODEV;
109 }
110
93 /* root hub's parent is the usb hcd. */ 111 /* root hub's parent is the usb hcd. */
94 parent_handle = DEVICE_ACPI_HANDLE(dev->parent); 112 parent_handle = DEVICE_ACPI_HANDLE(dev->parent);
95 *handle = acpi_get_child(parent_handle, udev->portnum); 113 *handle = acpi_get_child(parent_handle, udev->portnum);
@@ -122,18 +140,10 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
122 if (!*handle) 140 if (!*handle)
123 return -ENODEV; 141 return -ENODEV;
124 } 142 }
143 usb_acpi_check_port_connect_type(udev, *handle, port_num);
125 } else 144 } else
126 return -ENODEV; 145 return -ENODEV;
127 146
128 /*
129 * PLD will tell us whether a port is removable to the user or
130 * not. If we don't get an answer from PLD (it's not present
131 * or it's malformed) then try to infer it from UPC. If a
132 * device isn't connectable then it's probably not removable.
133 */
134 if (usb_acpi_check_pld(udev, *handle) != 0)
135 usb_acpi_check_upc(udev, *handle);
136
137 return 0; 147 return 0;
138} 148}
139 149
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 1633f6e99e35..1c528c1bf0be 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -169,6 +169,10 @@ extern void usb_notify_add_device(struct usb_device *udev);
169extern void usb_notify_remove_device(struct usb_device *udev); 169extern void usb_notify_remove_device(struct usb_device *udev);
170extern void usb_notify_add_bus(struct usb_bus *ubus); 170extern void usb_notify_add_bus(struct usb_bus *ubus);
171extern void usb_notify_remove_bus(struct usb_bus *ubus); 171extern void usb_notify_remove_bus(struct usb_bus *ubus);
172extern enum usb_port_connect_type
173 usb_get_hub_port_connect_type(struct usb_device *hdev, int port1);
174extern void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1,
175 enum usb_port_connect_type type);
172 176
173#ifdef CONFIG_ACPI 177#ifdef CONFIG_ACPI
174extern int usb_acpi_register(void); 178extern int usb_acpi_register(void);
diff --git a/include/linux/usb.h b/include/linux/usb.h
index ff8ef2d28589..e0084a1fa37f 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -384,6 +384,13 @@ enum usb_device_removable {
384 USB_DEVICE_FIXED, 384 USB_DEVICE_FIXED,
385}; 385};
386 386
387enum usb_port_connect_type {
388 USB_PORT_CONNECT_TYPE_UNKNOWN = 0,
389 USB_PORT_CONNECT_TYPE_HOT_PLUG,
390 USB_PORT_CONNECT_TYPE_HARD_WIRED,
391 USB_PORT_NOT_USED,
392};
393
387/* 394/*
388 * USB 3.0 Link Power Management (LPM) parameters. 395 * USB 3.0 Link Power Management (LPM) parameters.
389 * 396 *