aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/scan.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-03-03 17:05:29 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-03-04 08:25:32 -0500
commita33ec399e9fc266ba20f9b71d693aa63658bf2aa (patch)
tree404418949973ac053cfe02df039124d7189db1ed /drivers/acpi/scan.c
parentc56980744ed99994799850903627c4bbb5fed006 (diff)
ACPI / scan: Introduce common code for ACPI-based device hotplug
Multiple drivers handling hotplug-capable ACPI device nodes install notify handlers covering the same types of events in a very similar way. Moreover, those handlers are installed in separate namespace walks, although that really should be done during namespace scans carried out by acpi_bus_scan(). This leads to substantial code duplication, unnecessary overhead and behavior that is hard to follow. For this reason, introduce common code in drivers/acpi/scan.c for handling hotplug-related notification and carrying out device insertion and eject operations in a generic fashion, such that it may be used by all of the relevant drivers in the future. To cover the existing differences between those drivers introduce struct acpi_hotplug_profile for representing collections of hotplug settings associated with different ACPI scan handlers that can be used by the drivers to make the common code reflect their current behavior. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Toshi Kani <toshi.kani@hp.com> Tested-by: Toshi Kani <toshi.kani@hp.com>
Diffstat (limited to 'drivers/acpi/scan.c')
-rw-r--r--drivers/acpi/scan.c269
1 files changed, 216 insertions, 53 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index cc1b0020478b..de73fdf89598 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -107,32 +107,19 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha
107} 107}
108static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); 108static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
109 109
110/** 110static int acpi_scan_hot_remove(struct acpi_device *device)
111 * acpi_bus_hot_remove_device: hot-remove a device and its children
112 * @context: struct acpi_eject_event pointer (freed in this func)
113 *
114 * Hot-remove a device and its children. This function frees up the
115 * memory space passed by arg context, so that the caller may call
116 * this function asynchronously through acpi_os_hotplug_execute().
117 */
118void acpi_bus_hot_remove_device(void *context)
119{ 111{
120 struct acpi_eject_event *ej_event = context;
121 struct acpi_device *device = ej_event->device;
122 acpi_handle handle = device->handle; 112 acpi_handle handle = device->handle;
123 acpi_handle temp; 113 acpi_handle not_used;
124 struct acpi_object_list arg_list; 114 struct acpi_object_list arg_list;
125 union acpi_object arg; 115 union acpi_object arg;
126 acpi_status status = AE_OK; 116 acpi_status status;
127 u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */
128
129 mutex_lock(&acpi_scan_lock);
130 117
131 /* If there is no handle, the device node has been unregistered. */ 118 /* If there is no handle, the device node has been unregistered. */
132 if (!device->handle) { 119 if (!handle) {
133 dev_dbg(&device->dev, "ACPI handle missing\n"); 120 dev_dbg(&device->dev, "ACPI handle missing\n");
134 put_device(&device->dev); 121 put_device(&device->dev);
135 goto out; 122 return -EINVAL;
136 } 123 }
137 124
138 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 125 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
@@ -143,7 +130,7 @@ void acpi_bus_hot_remove_device(void *context)
143 put_device(&device->dev); 130 put_device(&device->dev);
144 device = NULL; 131 device = NULL;
145 132
146 if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", &temp))) { 133 if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", &not_used))) {
147 arg_list.count = 1; 134 arg_list.count = 1;
148 arg_list.pointer = &arg; 135 arg_list.pointer = &arg;
149 arg.type = ACPI_TYPE_INTEGER; 136 arg.type = ACPI_TYPE_INTEGER;
@@ -161,18 +148,158 @@ void acpi_bus_hot_remove_device(void *context)
161 */ 148 */
162 status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); 149 status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL);
163 if (ACPI_FAILURE(status)) { 150 if (ACPI_FAILURE(status)) {
164 if (status != AE_NOT_FOUND) 151 if (status == AE_NOT_FOUND) {
152 return -ENODEV;
153 } else {
165 acpi_handle_warn(handle, "Eject failed\n"); 154 acpi_handle_warn(handle, "Eject failed\n");
155 return -EIO;
156 }
157 }
158 return 0;
159}
166 160
167 /* Tell the firmware the hot-remove operation has failed. */ 161static void acpi_bus_device_eject(void *context)
168 acpi_evaluate_hotplug_ost(handle, ej_event->event, 162{
169 ost_code, NULL); 163 acpi_handle handle = context;
164 struct acpi_device *device = NULL;
165 struct acpi_scan_handler *handler;
166 u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
167
168 mutex_lock(&acpi_scan_lock);
169
170 acpi_bus_get_device(handle, &device);
171 if (!device)
172 goto err_out;
173
174 handler = device->handler;
175 if (!handler || !handler->hotplug.enabled) {
176 ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
177 goto err_out;
178 }
179 acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
180 ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
181 if (handler->hotplug.mode == AHM_CONTAINER) {
182 device->flags.eject_pending = true;
183 kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
184 } else {
185 int error;
186
187 get_device(&device->dev);
188 error = acpi_scan_hot_remove(device);
189 if (error)
190 goto err_out;
170 } 191 }
171 192
172 out: 193 out:
173 mutex_unlock(&acpi_scan_lock); 194 mutex_unlock(&acpi_scan_lock);
174 kfree(context);
175 return; 195 return;
196
197 err_out:
198 acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, ost_code,
199 NULL);
200 goto out;
201}
202
203static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source)
204{
205 struct acpi_device *device = NULL;
206 u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
207 int error;
208
209 mutex_lock(&acpi_scan_lock);
210
211 acpi_bus_get_device(handle, &device);
212 if (device) {
213 dev_warn(&device->dev, "Attempt to re-insert\n");
214 goto out;
215 }
216 acpi_evaluate_hotplug_ost(handle, ost_source,
217 ACPI_OST_SC_INSERT_IN_PROGRESS, NULL);
218 error = acpi_bus_scan(handle);
219 if (error) {
220 acpi_handle_warn(handle, "Namespace scan failure\n");
221 goto out;
222 }
223 error = acpi_bus_get_device(handle, &device);
224 if (error) {
225 acpi_handle_warn(handle, "Missing device node object\n");
226 goto out;
227 }
228 ost_code = ACPI_OST_SC_SUCCESS;
229 if (device->handler && device->handler->hotplug.mode == AHM_CONTAINER)
230 kobject_uevent(&device->dev.kobj, KOBJ_ONLINE);
231
232 out:
233 acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL);
234 mutex_unlock(&acpi_scan_lock);
235}
236
237static void acpi_scan_bus_check(void *context)
238{
239 acpi_scan_bus_device_check((acpi_handle)context,
240 ACPI_NOTIFY_BUS_CHECK);
241}
242
243static void acpi_scan_device_check(void *context)
244{
245 acpi_scan_bus_device_check((acpi_handle)context,
246 ACPI_NOTIFY_DEVICE_CHECK);
247}
248
249static void acpi_hotplug_notify_cb(acpi_handle handle, u32 type, void *not_used)
250{
251 acpi_osd_exec_callback callback;
252 acpi_status status;
253
254 switch (type) {
255 case ACPI_NOTIFY_BUS_CHECK:
256 acpi_handle_debug(handle, "ACPI_NOTIFY_BUS_CHECK event\n");
257 callback = acpi_scan_bus_check;
258 break;
259 case ACPI_NOTIFY_DEVICE_CHECK:
260 acpi_handle_debug(handle, "ACPI_NOTIFY_DEVICE_CHECK event\n");
261 callback = acpi_scan_device_check;
262 break;
263 case ACPI_NOTIFY_EJECT_REQUEST:
264 acpi_handle_debug(handle, "ACPI_NOTIFY_EJECT_REQUEST event\n");
265 callback = acpi_bus_device_eject;
266 break;
267 default:
268 /* non-hotplug event; possibly handled by other handler */
269 return;
270 }
271 status = acpi_os_hotplug_execute(callback, handle);
272 if (ACPI_FAILURE(status))
273 acpi_evaluate_hotplug_ost(handle, type,
274 ACPI_OST_SC_NON_SPECIFIC_FAILURE,
275 NULL);
276}
277
278/**
279 * acpi_bus_hot_remove_device: hot-remove a device and its children
280 * @context: struct acpi_eject_event pointer (freed in this func)
281 *
282 * Hot-remove a device and its children. This function frees up the
283 * memory space passed by arg context, so that the caller may call
284 * this function asynchronously through acpi_os_hotplug_execute().
285 */
286void acpi_bus_hot_remove_device(void *context)
287{
288 struct acpi_eject_event *ej_event = context;
289 struct acpi_device *device = ej_event->device;
290 acpi_handle handle = device->handle;
291 int error;
292
293 mutex_lock(&acpi_scan_lock);
294
295 error = acpi_scan_hot_remove(device);
296 if (error && handle)
297 acpi_evaluate_hotplug_ost(handle, ej_event->event,
298 ACPI_OST_SC_NON_SPECIFIC_FAILURE,
299 NULL);
300
301 mutex_unlock(&acpi_scan_lock);
302 kfree(context);
176} 303}
177EXPORT_SYMBOL(acpi_bus_hot_remove_device); 304EXPORT_SYMBOL(acpi_bus_hot_remove_device);
178 305
@@ -206,51 +333,61 @@ static ssize_t
206acpi_eject_store(struct device *d, struct device_attribute *attr, 333acpi_eject_store(struct device *d, struct device_attribute *attr,
207 const char *buf, size_t count) 334 const char *buf, size_t count)
208{ 335{
209 int ret = count;
210 acpi_status status;
211 acpi_object_type type = 0;
212 struct acpi_device *acpi_device = to_acpi_device(d); 336 struct acpi_device *acpi_device = to_acpi_device(d);
213 struct acpi_eject_event *ej_event; 337 struct acpi_eject_event *ej_event;
338 acpi_object_type not_used;
339 acpi_status status;
340 u32 ost_source;
341 int ret;
214 342
215 if ((!count) || (buf[0] != '1')) { 343 if (!count || buf[0] != '1')
216 return -EINVAL; 344 return -EINVAL;
217 }
218 if (!acpi_device->driver && !acpi_device->handler) {
219 ret = -ENODEV;
220 goto err;
221 }
222 status = acpi_get_type(acpi_device->handle, &type);
223 if (ACPI_FAILURE(status) || (!acpi_device->flags.ejectable)) {
224 ret = -ENODEV;
225 goto err;
226 }
227 345
228 ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); 346 if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled)
229 if (!ej_event) { 347 && !acpi_device->driver)
230 ret = -ENOMEM; 348 return -ENODEV;
231 goto err; 349
232 } 350 status = acpi_get_type(acpi_device->handle, &not_used);
351 if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable)
352 return -ENODEV;
353
354 mutex_lock(&acpi_scan_lock);
233 355
234 get_device(&acpi_device->dev);
235 ej_event->device = acpi_device;
236 if (acpi_device->flags.eject_pending) { 356 if (acpi_device->flags.eject_pending) {
237 /* event originated from ACPI eject notification */ 357 /* ACPI eject notification event. */
238 ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; 358 ost_source = ACPI_NOTIFY_EJECT_REQUEST;
239 acpi_device->flags.eject_pending = 0; 359 acpi_device->flags.eject_pending = 0;
240 } else { 360 } else {
241 /* event originated from user */ 361 /* Eject initiated by user space. */
242 ej_event->event = ACPI_OST_EC_OSPM_EJECT; 362 ost_source = ACPI_OST_EC_OSPM_EJECT;
243 (void) acpi_evaluate_hotplug_ost(acpi_device->handle,
244 ej_event->event, ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
245 } 363 }
246 364 ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL);
365 if (!ej_event) {
366 ret = -ENOMEM;
367 goto err_out;
368 }
369 acpi_evaluate_hotplug_ost(acpi_device->handle, ost_source,
370 ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
371 ej_event->device = acpi_device;
372 ej_event->event = ost_source;
373 get_device(&acpi_device->dev);
247 status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event); 374 status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event);
248 if (ACPI_FAILURE(status)) { 375 if (ACPI_FAILURE(status)) {
249 put_device(&acpi_device->dev); 376 put_device(&acpi_device->dev);
250 kfree(ej_event); 377 kfree(ej_event);
378 ret = status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN;
379 goto err_out;
251 } 380 }
252err: 381 ret = count;
382
383 out:
384 mutex_unlock(&acpi_scan_lock);
253 return ret; 385 return ret;
386
387 err_out:
388 acpi_evaluate_hotplug_ost(acpi_device->handle, ost_source,
389 ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL);
390 goto out;
254} 391}
255 392
256static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store); 393static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store);
@@ -1555,6 +1692,30 @@ static struct acpi_scan_handler *acpi_scan_match_handler(char *idstr,
1555 return NULL; 1692 return NULL;
1556} 1693}
1557 1694
1695static void acpi_scan_init_hotplug(acpi_handle handle)
1696{
1697 struct acpi_device_info *info;
1698 struct acpi_scan_handler *handler;
1699
1700 if (ACPI_FAILURE(acpi_get_object_info(handle, &info)))
1701 return;
1702
1703 if (!(info->valid & ACPI_VALID_HID)) {
1704 kfree(info);
1705 return;
1706 }
1707
1708 /*
1709 * This relies on the fact that acpi_install_notify_handler() will not
1710 * install the same notify handler routine twice for the same handle.
1711 */
1712 handler = acpi_scan_match_handler(info->hardware_id.string, NULL);
1713 kfree(info);
1714 if (handler && handler->hotplug.enabled)
1715 acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
1716 acpi_hotplug_notify_cb, NULL);
1717}
1718
1558static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, 1719static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
1559 void *not_used, void **return_value) 1720 void *not_used, void **return_value)
1560{ 1721{
@@ -1577,6 +1738,8 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
1577 return AE_OK; 1738 return AE_OK;
1578 } 1739 }
1579 1740
1741 acpi_scan_init_hotplug(handle);
1742
1580 if (!(sta & ACPI_STA_DEVICE_PRESENT) && 1743 if (!(sta & ACPI_STA_DEVICE_PRESENT) &&
1581 !(sta & ACPI_STA_DEVICE_FUNCTIONING)) { 1744 !(sta & ACPI_STA_DEVICE_FUNCTIONING)) {
1582 struct acpi_device_wakeup wakeup; 1745 struct acpi_device_wakeup wakeup;