aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 *