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 | |
| 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>
| -rw-r--r-- | drivers/acpi/scan.c | 71 | ||||
| -rw-r--r-- | include/acpi/acpi_bus.h | 2 | ||||
| -rw-r--r-- | include/acpi/acpi_drivers.h | 10 |
3 files changed, 83 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) |
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 08ec60c8366a..a2228511d4be 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h | |||
| @@ -95,6 +95,7 @@ typedef int (*acpi_op_suspend) (struct acpi_device * device, | |||
| 95 | typedef int (*acpi_op_resume) (struct acpi_device * device); | 95 | typedef int (*acpi_op_resume) (struct acpi_device * device); |
| 96 | typedef int (*acpi_op_bind) (struct acpi_device * device); | 96 | typedef int (*acpi_op_bind) (struct acpi_device * device); |
| 97 | typedef int (*acpi_op_unbind) (struct acpi_device * device); | 97 | typedef int (*acpi_op_unbind) (struct acpi_device * device); |
| 98 | typedef void (*acpi_op_notify) (struct acpi_device * device, u32 event); | ||
| 98 | 99 | ||
| 99 | struct acpi_bus_ops { | 100 | struct acpi_bus_ops { |
| 100 | u32 acpi_op_add:1; | 101 | u32 acpi_op_add:1; |
| @@ -110,6 +111,7 @@ struct acpi_device_ops { | |||
| 110 | acpi_op_resume resume; | 111 | acpi_op_resume resume; |
| 111 | acpi_op_bind bind; | 112 | acpi_op_bind bind; |
| 112 | acpi_op_unbind unbind; | 113 | acpi_op_unbind unbind; |
| 114 | acpi_op_notify notify; | ||
| 113 | }; | 115 | }; |
| 114 | 116 | ||
| 115 | struct acpi_driver { | 117 | struct acpi_driver { |
diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index 241d227de6c0..0352c8f0b05b 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h | |||
| @@ -67,6 +67,16 @@ | |||
| 67 | #define ACPI_BAY_HID "LNXIOBAY" | 67 | #define ACPI_BAY_HID "LNXIOBAY" |
| 68 | #define ACPI_DOCK_HID "LNXDOCK" | 68 | #define ACPI_DOCK_HID "LNXDOCK" |
| 69 | 69 | ||
| 70 | /* | ||
| 71 | * For fixed hardware buttons, we fabricate acpi_devices with HID | ||
| 72 | * ACPI_BUTTON_HID_POWERF or ACPI_BUTTON_HID_SLEEPF. Fixed hardware | ||
| 73 | * signals only an event; it doesn't supply a notification value. | ||
| 74 | * To allow drivers to treat notifications from fixed hardware the | ||
| 75 | * same as those from real devices, we turn the events into this | ||
| 76 | * notification value. | ||
| 77 | */ | ||
| 78 | #define ACPI_FIXED_HARDWARE_EVENT 0x100 | ||
| 79 | |||
| 70 | /* -------------------------------------------------------------------------- | 80 | /* -------------------------------------------------------------------------- |
| 71 | PCI | 81 | PCI |
| 72 | -------------------------------------------------------------------------- */ | 82 | -------------------------------------------------------------------------- */ |
