|
@@ -197,12 +197,12 @@ acpi_status
|
|
|
acpi_setup_gpe_for_wake(acpi_handle wake_device,
|
|
|
acpi_handle gpe_device, u32 gpe_number)
|
|
|
{
|
|
|
- acpi_status status = AE_BAD_PARAMETER;
|
|
|
+ acpi_status status;
|
|
|
struct acpi_gpe_event_info *gpe_event_info;
|
|
|
struct acpi_namespace_node *device_node;
|
|
|
- struct acpi_gpe_notify_object *notify_object;
|
|
|
+ struct acpi_gpe_notify_info *notify;
|
|
|
+ struct acpi_gpe_notify_info *new_notify;
|
|
|
acpi_cpu_flags flags;
|
|
|
- u8 gpe_dispatch_mask;
|
|
|
|
|
|
ACPI_FUNCTION_TRACE(acpi_setup_gpe_for_wake);
|
|
|
|
|
@@ -216,63 +216,95 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device,
|
|
|
return_ACPI_STATUS(AE_BAD_PARAMETER);
|
|
|
}
|
|
|
|
|
|
+ /* Handle root object case */
|
|
|
+
|
|
|
+ if (wake_device == ACPI_ROOT_OBJECT) {
|
|
|
+ device_node = acpi_gbl_root_node;
|
|
|
+ } else {
|
|
|
+ device_node = ACPI_CAST_PTR(struct acpi_namespace_node, wake_device);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Validate WakeDevice is of type Device */
|
|
|
+
|
|
|
+ if (device_node->type != ACPI_TYPE_DEVICE) {
|
|
|
+ return_ACPI_STATUS (AE_BAD_PARAMETER);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Allocate a new notify object up front, in case it is needed.
|
|
|
+ * Memory allocation while holding a spinlock is a big no-no
|
|
|
+ * on some hosts.
|
|
|
+ */
|
|
|
+ new_notify = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_notify_info));
|
|
|
+ if (!new_notify) {
|
|
|
+ return_ACPI_STATUS(AE_NO_MEMORY);
|
|
|
+ }
|
|
|
+
|
|
|
flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
|
|
|
|
|
|
/* Ensure that we have a valid GPE number */
|
|
|
|
|
|
gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
|
|
|
if (!gpe_event_info) {
|
|
|
+ status = AE_BAD_PARAMETER;
|
|
|
goto unlock_and_exit;
|
|
|
}
|
|
|
|
|
|
- if (wake_device == ACPI_ROOT_OBJECT) {
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
/*
|
|
|
* If there is no method or handler for this GPE, then the
|
|
|
- * wake_device will be notified whenever this GPE fires (aka
|
|
|
- * "implicit notify") Note: The GPE is assumed to be
|
|
|
+ * wake_device will be notified whenever this GPE fires. This is
|
|
|
+ * known as an "implicit notify". Note: The GPE is assumed to be
|
|
|
* level-triggered (for windows compatibility).
|
|
|
*/
|
|
|
- gpe_dispatch_mask = gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK;
|
|
|
- if (gpe_dispatch_mask != ACPI_GPE_DISPATCH_NONE
|
|
|
- && gpe_dispatch_mask != ACPI_GPE_DISPATCH_NOTIFY) {
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- /* Validate wake_device is of type Device */
|
|
|
-
|
|
|
- device_node = ACPI_CAST_PTR(struct acpi_namespace_node, wake_device);
|
|
|
- if (device_node->type != ACPI_TYPE_DEVICE) {
|
|
|
- goto unlock_and_exit;
|
|
|
+ if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
|
|
|
+ ACPI_GPE_DISPATCH_NONE) {
|
|
|
+ /*
|
|
|
+ * This is the first device for implicit notify on this GPE.
|
|
|
+ * Just set the flags here, and enter the NOTIFY block below.
|
|
|
+ */
|
|
|
+ gpe_event_info->flags =
|
|
|
+ (ACPI_GPE_DISPATCH_NOTIFY | ACPI_GPE_LEVEL_TRIGGERED);
|
|
|
}
|
|
|
|
|
|
- if (gpe_dispatch_mask == ACPI_GPE_DISPATCH_NONE) {
|
|
|
- gpe_event_info->flags = (ACPI_GPE_DISPATCH_NOTIFY |
|
|
|
- ACPI_GPE_LEVEL_TRIGGERED);
|
|
|
- gpe_event_info->dispatch.device.node = device_node;
|
|
|
- gpe_event_info->dispatch.device.next = NULL;
|
|
|
- } else {
|
|
|
- /* There are multiple devices to notify implicitly. */
|
|
|
-
|
|
|
- notify_object = ACPI_ALLOCATE_ZEROED(sizeof(*notify_object));
|
|
|
- if (!notify_object) {
|
|
|
- status = AE_NO_MEMORY;
|
|
|
- goto unlock_and_exit;
|
|
|
+ /*
|
|
|
+ * If we already have an implicit notify on this GPE, add
|
|
|
+ * this device to the notify list.
|
|
|
+ */
|
|
|
+ if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) ==
|
|
|
+ ACPI_GPE_DISPATCH_NOTIFY) {
|
|
|
+
|
|
|
+ /* Ensure that the device is not already in the list */
|
|
|
+
|
|
|
+ notify = gpe_event_info->dispatch.notify_list;
|
|
|
+ while (notify) {
|
|
|
+ if (notify->device_node == device_node) {
|
|
|
+ status = AE_ALREADY_EXISTS;
|
|
|
+ goto unlock_and_exit;
|
|
|
+ }
|
|
|
+ notify = notify->next;
|
|
|
}
|
|
|
|
|
|
- notify_object->node = device_node;
|
|
|
- notify_object->next = gpe_event_info->dispatch.device.next;
|
|
|
- gpe_event_info->dispatch.device.next = notify_object;
|
|
|
+ /* Add this device to the notify list for this GPE */
|
|
|
+
|
|
|
+ new_notify->device_node = device_node;
|
|
|
+ new_notify->next = gpe_event_info->dispatch.notify_list;
|
|
|
+ gpe_event_info->dispatch.notify_list = new_notify;
|
|
|
+ new_notify = NULL;
|
|
|
}
|
|
|
|
|
|
- out:
|
|
|
+ /* Mark the GPE as a possible wake event */
|
|
|
+
|
|
|
gpe_event_info->flags |= ACPI_GPE_CAN_WAKE;
|
|
|
status = AE_OK;
|
|
|
|
|
|
- unlock_and_exit:
|
|
|
+unlock_and_exit:
|
|
|
acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
|
|
|
+
|
|
|
+ /* Delete the notify object if it was not used above */
|
|
|
+
|
|
|
+ if (new_notify) {
|
|
|
+ ACPI_FREE(new_notify);
|
|
|
+ }
|
|
|
return_ACPI_STATUS(status);
|
|
|
}
|
|
|
ACPI_EXPORT_SYMBOL(acpi_setup_gpe_for_wake)
|