Commit 46ec8598fde74ba59703575c22a6fb0b6b151bb6
Committed by
Len Brown
1 parent
478c6a43fc
Exists in
master
and in
20 other branches
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>
Showing 3 changed files with 83 additions and 0 deletions Side-by-side Diff
drivers/acpi/scan.c
... | ... | @@ -359,6 +359,61 @@ |
359 | 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 | 417 | static int acpi_bus_driver_init(struct acpi_device *, struct acpi_driver *); |
363 | 418 | static int acpi_start_single_object(struct acpi_device *); |
364 | 419 | static int acpi_device_probe(struct device * dev) |
... | ... | @@ -371,6 +426,20 @@ |
371 | 426 | if (!ret) { |
372 | 427 | if (acpi_dev->bus_ops.acpi_op_start) |
373 | 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 | 443 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
375 | 444 | "Found driver [%s] for device [%s]\n", |
376 | 445 | acpi_drv->name, acpi_dev->pnp.bus_id)); |
... | ... | @@ -385,6 +454,8 @@ |
385 | 454 | struct acpi_driver *acpi_drv = acpi_dev->driver; |
386 | 455 | |
387 | 456 | if (acpi_drv) { |
457 | + if (acpi_drv->ops.notify) | |
458 | + acpi_device_remove_notify_handler(acpi_dev); | |
388 | 459 | if (acpi_drv->ops.stop) |
389 | 460 | acpi_drv->ops.stop(acpi_dev, acpi_dev->removal_type); |
390 | 461 | if (acpi_drv->ops.remove) |
include/acpi/acpi_bus.h
... | ... | @@ -95,6 +95,7 @@ |
95 | 95 | typedef int (*acpi_op_resume) (struct acpi_device * device); |
96 | 96 | typedef int (*acpi_op_bind) (struct acpi_device * device); |
97 | 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 | 100 | struct acpi_bus_ops { |
100 | 101 | u32 acpi_op_add:1; |
... | ... | @@ -110,6 +111,7 @@ |
110 | 111 | acpi_op_resume resume; |
111 | 112 | acpi_op_bind bind; |
112 | 113 | acpi_op_unbind unbind; |
114 | + acpi_op_notify notify; | |
113 | 115 | }; |
114 | 116 | |
115 | 117 | struct acpi_driver { |
include/acpi/acpi_drivers.h
... | ... | @@ -67,6 +67,16 @@ |
67 | 67 | #define ACPI_BAY_HID "LNXIOBAY" |
68 | 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 | 81 | PCI |
72 | 82 | -------------------------------------------------------------------------- */ |