diff options
-rw-r--r-- | drivers/usb/core/hub.c | 31 | ||||
-rw-r--r-- | drivers/usb/core/usb-acpi.c | 80 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 4 | ||||
-rw-r--r-- | include/linux/usb.h | 7 |
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 | ||
48 | struct usb_hub { | 49 | struct usb_hub { |
@@ -5078,6 +5079,36 @@ struct usb_device *usb_hub_find_child(struct usb_device *hdev, | |||
5078 | } | 5079 | } |
5079 | EXPORT_SYMBOL_GPL(usb_hub_find_child); | 5080 | EXPORT_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 | */ | ||
5088 | void 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 | */ | ||
5104 | enum usb_port_connect_type | ||
5105 | usb_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 | ||
22 | static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle) | 22 | static 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 | ||
47 | out: | 61 | out: |
48 | kfree(upc); | 62 | kfree(upc); |
49 | return ret; | 63 | return ret; |
50 | } | 64 | } |
51 | 65 | ||
52 | static 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 | |||
70 | static int usb_acpi_find_device(struct device *dev, acpi_handle *handle) | 66 | static 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); | |||
169 | extern void usb_notify_remove_device(struct usb_device *udev); | 169 | extern void usb_notify_remove_device(struct usb_device *udev); |
170 | extern void usb_notify_add_bus(struct usb_bus *ubus); | 170 | extern void usb_notify_add_bus(struct usb_bus *ubus); |
171 | extern void usb_notify_remove_bus(struct usb_bus *ubus); | 171 | extern void usb_notify_remove_bus(struct usb_bus *ubus); |
172 | extern enum usb_port_connect_type | ||
173 | usb_get_hub_port_connect_type(struct usb_device *hdev, int port1); | ||
174 | extern 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 |
174 | extern int usb_acpi_register(void); | 178 | extern 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 | ||
387 | enum 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 | * |