diff options
Diffstat (limited to 'drivers/acpi/scan.c')
-rw-r--r-- | drivers/acpi/scan.c | 104 |
1 files changed, 43 insertions, 61 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 6d85289f1c12..f3132aa47a69 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
@@ -6,6 +6,8 @@ | |||
6 | #include <linux/init.h> | 6 | #include <linux/init.h> |
7 | #include <linux/kernel.h> | 7 | #include <linux/kernel.h> |
8 | #include <linux/acpi.h> | 8 | #include <linux/acpi.h> |
9 | #include <linux/signal.h> | ||
10 | #include <linux/kthread.h> | ||
9 | 11 | ||
10 | #include <acpi/acpi_drivers.h> | 12 | #include <acpi/acpi_drivers.h> |
11 | #include <acpi/acinterp.h> /* for acpi_ex_eisa_id_to_string() */ | 13 | #include <acpi/acinterp.h> /* for acpi_ex_eisa_id_to_string() */ |
@@ -92,17 +94,37 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha | |||
92 | } | 94 | } |
93 | static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); | 95 | static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); |
94 | 96 | ||
95 | static int acpi_eject_operation(acpi_handle handle, int lockable) | 97 | static int acpi_bus_hot_remove_device(void *context) |
96 | { | 98 | { |
99 | struct acpi_device *device; | ||
100 | acpi_handle handle = context; | ||
97 | struct acpi_object_list arg_list; | 101 | struct acpi_object_list arg_list; |
98 | union acpi_object arg; | 102 | union acpi_object arg; |
99 | acpi_status status = AE_OK; | 103 | acpi_status status = AE_OK; |
100 | 104 | ||
101 | /* | 105 | if (acpi_bus_get_device(handle, &device)) |
102 | * TBD: evaluate _PS3? | 106 | return 0; |
103 | */ | 107 | |
108 | if (!device) | ||
109 | return 0; | ||
110 | |||
111 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
112 | "Hot-removing device %s...\n", device->dev.bus_id)); | ||
113 | |||
104 | 114 | ||
105 | if (lockable) { | 115 | if (acpi_bus_trim(device, 1)) { |
116 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
117 | "Removing device failed\n")); | ||
118 | return -1; | ||
119 | } | ||
120 | |||
121 | /* power off device */ | ||
122 | status = acpi_evaluate_object(handle, "_PS3", NULL, NULL); | ||
123 | if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) | ||
124 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, | ||
125 | "Power-off device failed\n")); | ||
126 | |||
127 | if (device->flags.lockable) { | ||
106 | arg_list.count = 1; | 128 | arg_list.count = 1; |
107 | arg_list.pointer = &arg; | 129 | arg_list.pointer = &arg; |
108 | arg.type = ACPI_TYPE_INTEGER; | 130 | arg.type = ACPI_TYPE_INTEGER; |
@@ -118,26 +140,22 @@ static int acpi_eject_operation(acpi_handle handle, int lockable) | |||
118 | /* | 140 | /* |
119 | * TBD: _EJD support. | 141 | * TBD: _EJD support. |
120 | */ | 142 | */ |
121 | |||
122 | status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); | 143 | status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); |
123 | if (ACPI_FAILURE(status)) { | 144 | if (ACPI_FAILURE(status)) |
124 | return (-ENODEV); | 145 | return -ENODEV; |
125 | } | ||
126 | 146 | ||
127 | return (0); | 147 | return 0; |
128 | } | 148 | } |
129 | 149 | ||
130 | static ssize_t | 150 | static ssize_t |
131 | acpi_eject_store(struct device *d, struct device_attribute *attr, | 151 | acpi_eject_store(struct device *d, struct device_attribute *attr, |
132 | const char *buf, size_t count) | 152 | const char *buf, size_t count) |
133 | { | 153 | { |
134 | int result; | ||
135 | int ret = count; | 154 | int ret = count; |
136 | int islockable; | ||
137 | acpi_status status; | 155 | acpi_status status; |
138 | acpi_handle handle; | ||
139 | acpi_object_type type = 0; | 156 | acpi_object_type type = 0; |
140 | struct acpi_device *acpi_device = to_acpi_device(d); | 157 | struct acpi_device *acpi_device = to_acpi_device(d); |
158 | struct task_struct *task; | ||
141 | 159 | ||
142 | if ((!count) || (buf[0] != '1')) { | 160 | if ((!count) || (buf[0] != '1')) { |
143 | return -EINVAL; | 161 | return -EINVAL; |
@@ -154,18 +172,12 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, | |||
154 | goto err; | 172 | goto err; |
155 | } | 173 | } |
156 | 174 | ||
157 | islockable = acpi_device->flags.lockable; | 175 | /* remove the device in another thread to fix the deadlock issue */ |
158 | handle = acpi_device->handle; | 176 | task = kthread_run(acpi_bus_hot_remove_device, |
159 | 177 | acpi_device->handle, "acpi_hot_remove_device"); | |
160 | result = acpi_bus_trim(acpi_device, 1); | 178 | if (IS_ERR(task)) |
161 | 179 | ret = PTR_ERR(task); | |
162 | if (!result) | 180 | err: |
163 | result = acpi_eject_operation(handle, islockable); | ||
164 | |||
165 | if (result) { | ||
166 | ret = -EBUSY; | ||
167 | } | ||
168 | err: | ||
169 | return ret; | 181 | return ret; |
170 | } | 182 | } |
171 | 183 | ||
@@ -691,9 +703,7 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device) | |||
691 | acpi_status status = 0; | 703 | acpi_status status = 0; |
692 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | 704 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |
693 | union acpi_object *package = NULL; | 705 | union acpi_object *package = NULL; |
694 | union acpi_object in_arg[3]; | 706 | int psw_error; |
695 | struct acpi_object_list arg_list = { 3, in_arg }; | ||
696 | acpi_status psw_status = AE_OK; | ||
697 | 707 | ||
698 | struct acpi_device_id button_device_ids[] = { | 708 | struct acpi_device_id button_device_ids[] = { |
699 | {"PNP0C0D", 0}, | 709 | {"PNP0C0D", 0}, |
@@ -725,39 +735,11 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device) | |||
725 | * So it is necessary to call _DSW object first. Only when it is not | 735 | * So it is necessary to call _DSW object first. Only when it is not |
726 | * present will the _PSW object used. | 736 | * present will the _PSW object used. |
727 | */ | 737 | */ |
728 | /* | 738 | psw_error = acpi_device_sleep_wake(device, 0, 0, 0); |
729 | * Three agruments are needed for the _DSW object. | 739 | if (psw_error) |
730 | * Argument 0: enable/disable the wake capabilities | 740 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
731 | * When _DSW object is called to disable the wake capabilities, maybe | 741 | "error in _DSW or _PSW evaluation\n")); |
732 | * the first argument is filled. The value of the other two agruments | 742 | |
733 | * is meaningless. | ||
734 | */ | ||
735 | in_arg[0].type = ACPI_TYPE_INTEGER; | ||
736 | in_arg[0].integer.value = 0; | ||
737 | in_arg[1].type = ACPI_TYPE_INTEGER; | ||
738 | in_arg[1].integer.value = 0; | ||
739 | in_arg[2].type = ACPI_TYPE_INTEGER; | ||
740 | in_arg[2].integer.value = 0; | ||
741 | psw_status = acpi_evaluate_object(device->handle, "_DSW", | ||
742 | &arg_list, NULL); | ||
743 | if (ACPI_FAILURE(psw_status) && (psw_status != AE_NOT_FOUND)) | ||
744 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "error in evaluate _DSW\n")); | ||
745 | /* | ||
746 | * When the _DSW object is not present, OSPM will call _PSW object. | ||
747 | */ | ||
748 | if (psw_status == AE_NOT_FOUND) { | ||
749 | /* | ||
750 | * Only one agruments is required for the _PSW object. | ||
751 | * agrument 0: enable/disable the wake capabilities | ||
752 | */ | ||
753 | arg_list.count = 1; | ||
754 | in_arg[0].integer.value = 0; | ||
755 | psw_status = acpi_evaluate_object(device->handle, "_PSW", | ||
756 | &arg_list, NULL); | ||
757 | if (ACPI_FAILURE(psw_status) && (psw_status != AE_NOT_FOUND)) | ||
758 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "error in " | ||
759 | "evaluate _PSW\n")); | ||
760 | } | ||
761 | /* Power button, Lid switch always enable wakeup */ | 743 | /* Power button, Lid switch always enable wakeup */ |
762 | if (!acpi_match_device_ids(device, button_device_ids)) | 744 | if (!acpi_match_device_ids(device, button_device_ids)) |
763 | device->wakeup.flags.run_wake = 1; | 745 | device->wakeup.flags.run_wake = 1; |