diff options
author | Bjorn Helgaas <bjorn.helgaas@hp.com> | 2009-03-30 13:48:13 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2009-04-05 02:25:07 -0400 |
commit | 46ec8598fde74ba59703575c22a6fb0b6b151bb6 (patch) | |
tree | 21936a8b188c58e69e2bd5d951df28e6e05930fa /drivers | |
parent | 478c6a43fcbc6c11609f8cee7c7b57223907754f (diff) |
ACPI: support acpi_device_ops .notify methods
This patch adds support for ACPI device driver .notify() methods. If
such a method is present, Linux/ACPI installs a handler for device
notifications (but not for system notifications such as Bus Check,
Device Check, etc). When a device notification occurs, Linux/ACPI
passes it on to the driver's .notify() method.
In most cases, this removes the need for drivers to install their own
handlers for device-specific notifications.
For fixed hardware devices like some power and sleep buttons, there's
no notification value because there's no control method to execute a
Notify opcode. When a fixed hardware device generates an event, we
handle it the same as a regular device notification, except we send
a ACPI_FIXED_HARDWARE_EVENT value. This is outside the normal 0x0-0xff
range used by Notify opcodes.
Several drivers install their own handlers for system Bus Check and
Device Check notifications so they can support hot-plug. This patch
doesn't affect that usage.
Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
Reviewed-by: Alex Chiang <achiang@hp.com>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/scan.c | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index b7308efce458..20c23c049207 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
@@ -359,6 +359,61 @@ static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env) | |||
359 | return 0; | 359 | return 0; |
360 | } | 360 | } |
361 | 361 | ||
362 | static void acpi_device_notify(acpi_handle handle, u32 event, void *data) | ||
363 | { | ||
364 | struct acpi_device *device = data; | ||
365 | |||
366 | device->driver->ops.notify(device, event); | ||
367 | } | ||
368 | |||
369 | static acpi_status acpi_device_notify_fixed(void *data) | ||
370 | { | ||
371 | struct acpi_device *device = data; | ||
372 | |||
373 | acpi_device_notify(device->handle, ACPI_FIXED_HARDWARE_EVENT, device); | ||
374 | return AE_OK; | ||
375 | } | ||
376 | |||
377 | static int acpi_device_install_notify_handler(struct acpi_device *device) | ||
378 | { | ||
379 | acpi_status status; | ||
380 | char *hid; | ||
381 | |||
382 | hid = acpi_device_hid(device); | ||
383 | if (!strcmp(hid, ACPI_BUTTON_HID_POWERF)) | ||
384 | status = | ||
385 | acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, | ||
386 | acpi_device_notify_fixed, | ||
387 | device); | ||
388 | else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) | ||
389 | status = | ||
390 | acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, | ||
391 | acpi_device_notify_fixed, | ||
392 | device); | ||
393 | else | ||
394 | status = acpi_install_notify_handler(device->handle, | ||
395 | ACPI_DEVICE_NOTIFY, | ||
396 | acpi_device_notify, | ||
397 | device); | ||
398 | |||
399 | if (ACPI_FAILURE(status)) | ||
400 | return -EINVAL; | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static void acpi_device_remove_notify_handler(struct acpi_device *device) | ||
405 | { | ||
406 | if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) | ||
407 | acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, | ||
408 | acpi_device_notify_fixed); | ||
409 | else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) | ||
410 | acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, | ||
411 | acpi_device_notify_fixed); | ||
412 | else | ||
413 | acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, | ||
414 | acpi_device_notify); | ||
415 | } | ||
416 | |||
362 | static int acpi_bus_driver_init(struct acpi_device *, struct acpi_driver *); | 417 | static int acpi_bus_driver_init(struct acpi_device *, struct acpi_driver *); |
363 | static int acpi_start_single_object(struct acpi_device *); | 418 | static int acpi_start_single_object(struct acpi_device *); |
364 | static int acpi_device_probe(struct device * dev) | 419 | static int acpi_device_probe(struct device * dev) |
@@ -371,6 +426,20 @@ static int acpi_device_probe(struct device * dev) | |||
371 | if (!ret) { | 426 | if (!ret) { |
372 | if (acpi_dev->bus_ops.acpi_op_start) | 427 | if (acpi_dev->bus_ops.acpi_op_start) |
373 | acpi_start_single_object(acpi_dev); | 428 | acpi_start_single_object(acpi_dev); |
429 | |||
430 | if (acpi_drv->ops.notify) { | ||
431 | ret = acpi_device_install_notify_handler(acpi_dev); | ||
432 | if (ret) { | ||
433 | if (acpi_drv->ops.stop) | ||
434 | acpi_drv->ops.stop(acpi_dev, | ||
435 | acpi_dev->removal_type); | ||
436 | if (acpi_drv->ops.remove) | ||
437 | acpi_drv->ops.remove(acpi_dev, | ||
438 | acpi_dev->removal_type); | ||
439 | return ret; | ||
440 | } | ||
441 | } | ||
442 | |||
374 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 443 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
375 | "Found driver [%s] for device [%s]\n", | 444 | "Found driver [%s] for device [%s]\n", |
376 | acpi_drv->name, acpi_dev->pnp.bus_id)); | 445 | acpi_drv->name, acpi_dev->pnp.bus_id)); |
@@ -385,6 +454,8 @@ static int acpi_device_remove(struct device * dev) | |||
385 | struct acpi_driver *acpi_drv = acpi_dev->driver; | 454 | struct acpi_driver *acpi_drv = acpi_dev->driver; |
386 | 455 | ||
387 | if (acpi_drv) { | 456 | if (acpi_drv) { |
457 | if (acpi_drv->ops.notify) | ||
458 | acpi_device_remove_notify_handler(acpi_dev); | ||
388 | if (acpi_drv->ops.stop) | 459 | if (acpi_drv->ops.stop) |
389 | acpi_drv->ops.stop(acpi_dev, acpi_dev->removal_type); | 460 | acpi_drv->ops.stop(acpi_dev, acpi_dev->removal_type); |
390 | if (acpi_drv->ops.remove) | 461 | if (acpi_drv->ops.remove) |