diff options
author | Matthew Garrett <mjg@redhat.com> | 2012-05-11 04:08:28 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-11 20:07:02 -0400 |
commit | 54d3f8c63d6940966217b807972778fb17c3fa82 (patch) | |
tree | fc42b3ac4b41ed1cebfd50e494c87a986a021e39 /drivers/usb/core | |
parent | da0af6e78ef311d97754aa03e10eade82cc99e16 (diff) |
usb: Set device removable state based on ACPI USB data
ACPI offers two methods that allow us to infer whether or not a USB port
is removable. The _PLD method gives us information on whether the port is
"user visible" or not. If that's not present then we can fall back to the
_UPC method which tells us whether or not a port is connectable.
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Lan Tianyu <tianyu.lan@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/usb-acpi.c | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index cab5cb7c256c..8947b203d5a4 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c | |||
@@ -19,6 +19,54 @@ | |||
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) | ||
23 | { | ||
24 | acpi_status status; | ||
25 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
26 | union acpi_object *upc; | ||
27 | int ret = 0; | ||
28 | |||
29 | status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer); | ||
30 | |||
31 | if (ACPI_FAILURE(status)) | ||
32 | return -ENODEV; | ||
33 | |||
34 | upc = buffer.pointer; | ||
35 | |||
36 | if (!upc || (upc->type != ACPI_TYPE_PACKAGE) | ||
37 | || upc->package.count != 4) { | ||
38 | ret = -EINVAL; | ||
39 | goto out; | ||
40 | } | ||
41 | |||
42 | if (upc->package.elements[0].integer.value) | ||
43 | udev->removable = USB_DEVICE_REMOVABLE; | ||
44 | else | ||
45 | udev->removable = USB_DEVICE_FIXED; | ||
46 | |||
47 | out: | ||
48 | kfree(upc); | ||
49 | return ret; | ||
50 | } | ||
51 | |||
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 | |||
22 | static int usb_acpi_find_device(struct device *dev, acpi_handle *handle) | 70 | static int usb_acpi_find_device(struct device *dev, acpi_handle *handle) |
23 | { | 71 | { |
24 | struct usb_device *udev; | 72 | struct usb_device *udev; |
@@ -40,6 +88,15 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle) | |||
40 | if (!*handle) | 88 | if (!*handle) |
41 | return -ENODEV; | 89 | return -ENODEV; |
42 | 90 | ||
91 | /* | ||
92 | * PLD will tell us whether a port is removable to the user or | ||
93 | * not. If we don't get an answer from PLD (it's not present | ||
94 | * or it's malformed) then try to infer it from UPC. If a | ||
95 | * device isn't connectable then it's probably not removable. | ||
96 | */ | ||
97 | if (usb_acpi_check_pld(udev, *handle) != 0) | ||
98 | usb_acpi_check_upc(udev, *handle); | ||
99 | |||
43 | return 0; | 100 | return 0; |
44 | } | 101 | } |
45 | 102 | ||