1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /****************************************************************************** |
3 | * |
4 | * Module Name: evmisc - Miscellaneous event manager support functions |
5 | * |
6 | * Copyright (C) 2000 - 2023, Intel Corp. |
7 | * |
8 | *****************************************************************************/ |
9 | |
10 | #include <acpi/acpi.h> |
11 | #include "accommon.h" |
12 | #include "acevents.h" |
13 | #include "acnamesp.h" |
14 | |
15 | #define _COMPONENT ACPI_EVENTS |
16 | ACPI_MODULE_NAME("evmisc" ) |
17 | |
18 | /* Local prototypes */ |
19 | static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context); |
20 | |
21 | /******************************************************************************* |
22 | * |
23 | * FUNCTION: acpi_ev_is_notify_object |
24 | * |
25 | * PARAMETERS: node - Node to check |
26 | * |
27 | * RETURN: TRUE if notifies allowed on this object |
28 | * |
29 | * DESCRIPTION: Check type of node for a object that supports notifies. |
30 | * |
31 | * TBD: This could be replaced by a flag bit in the node. |
32 | * |
33 | ******************************************************************************/ |
34 | |
35 | u8 acpi_ev_is_notify_object(struct acpi_namespace_node *node) |
36 | { |
37 | |
38 | switch (node->type) { |
39 | case ACPI_TYPE_DEVICE: |
40 | case ACPI_TYPE_PROCESSOR: |
41 | case ACPI_TYPE_THERMAL: |
42 | /* |
43 | * These are the ONLY objects that can receive ACPI notifications |
44 | */ |
45 | return (TRUE); |
46 | |
47 | default: |
48 | |
49 | return (FALSE); |
50 | } |
51 | } |
52 | |
53 | /******************************************************************************* |
54 | * |
55 | * FUNCTION: acpi_ev_queue_notify_request |
56 | * |
57 | * PARAMETERS: node - NS node for the notified object |
58 | * notify_value - Value from the Notify() request |
59 | * |
60 | * RETURN: Status |
61 | * |
62 | * DESCRIPTION: Dispatch a device notification event to a previously |
63 | * installed handler. |
64 | * |
65 | ******************************************************************************/ |
66 | |
67 | acpi_status |
68 | acpi_ev_queue_notify_request(struct acpi_namespace_node *node, u32 notify_value) |
69 | { |
70 | union acpi_operand_object *obj_desc; |
71 | union acpi_operand_object *handler_list_head = NULL; |
72 | union acpi_generic_state *info; |
73 | u8 handler_list_id = 0; |
74 | acpi_status status = AE_OK; |
75 | |
76 | ACPI_FUNCTION_NAME(ev_queue_notify_request); |
77 | |
78 | /* Are Notifies allowed on this object? */ |
79 | |
80 | if (!acpi_ev_is_notify_object(node)) { |
81 | return (AE_TYPE); |
82 | } |
83 | |
84 | /* Get the correct notify list type (System or Device) */ |
85 | |
86 | if (notify_value <= ACPI_MAX_SYS_NOTIFY) { |
87 | handler_list_id = ACPI_SYSTEM_HANDLER_LIST; |
88 | } else { |
89 | handler_list_id = ACPI_DEVICE_HANDLER_LIST; |
90 | } |
91 | |
92 | /* Get the notify object attached to the namespace Node */ |
93 | |
94 | obj_desc = acpi_ns_get_attached_object(node); |
95 | if (obj_desc) { |
96 | |
97 | /* We have an attached object, Get the correct handler list */ |
98 | |
99 | handler_list_head = |
100 | obj_desc->common_notify.notify_list[handler_list_id]; |
101 | } |
102 | |
103 | /* |
104 | * If there is no notify handler (Global or Local) |
105 | * for this object, just ignore the notify |
106 | */ |
107 | if (!acpi_gbl_global_notify[handler_list_id].handler |
108 | && !handler_list_head) { |
109 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
110 | "No notify handler for Notify, ignoring (%4.4s, %X) node %p\n" , |
111 | acpi_ut_get_node_name(node), notify_value, |
112 | node)); |
113 | |
114 | return (AE_OK); |
115 | } |
116 | |
117 | /* Setup notify info and schedule the notify dispatcher */ |
118 | |
119 | info = acpi_ut_create_generic_state(); |
120 | if (!info) { |
121 | return (AE_NO_MEMORY); |
122 | } |
123 | |
124 | info->common.descriptor_type = ACPI_DESC_TYPE_STATE_NOTIFY; |
125 | |
126 | info->notify.node = node; |
127 | info->notify.value = (u16)notify_value; |
128 | info->notify.handler_list_id = handler_list_id; |
129 | info->notify.handler_list_head = handler_list_head; |
130 | info->notify.global = &acpi_gbl_global_notify[handler_list_id]; |
131 | |
132 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
133 | "Dispatching Notify on [%4.4s] (%s) Value 0x%2.2X (%s) Node %p\n" , |
134 | acpi_ut_get_node_name(node), |
135 | acpi_ut_get_type_name(node->type), notify_value, |
136 | acpi_ut_get_notify_name(notify_value, ACPI_TYPE_ANY), |
137 | node)); |
138 | |
139 | status = acpi_os_execute(type: OSL_NOTIFY_HANDLER, |
140 | function: acpi_ev_notify_dispatch, context: info); |
141 | if (ACPI_FAILURE(status)) { |
142 | acpi_ut_delete_generic_state(state: info); |
143 | } |
144 | |
145 | return (status); |
146 | } |
147 | |
148 | /******************************************************************************* |
149 | * |
150 | * FUNCTION: acpi_ev_notify_dispatch |
151 | * |
152 | * PARAMETERS: context - To be passed to the notify handler |
153 | * |
154 | * RETURN: None. |
155 | * |
156 | * DESCRIPTION: Dispatch a device notification event to a previously |
157 | * installed handler. |
158 | * |
159 | ******************************************************************************/ |
160 | |
161 | static void ACPI_SYSTEM_XFACE acpi_ev_notify_dispatch(void *context) |
162 | { |
163 | union acpi_generic_state *info = (union acpi_generic_state *)context; |
164 | union acpi_operand_object *handler_obj; |
165 | |
166 | ACPI_FUNCTION_ENTRY(); |
167 | |
168 | /* Invoke a global notify handler if installed */ |
169 | |
170 | if (info->notify.global->handler) { |
171 | info->notify.global->handler(info->notify.node, |
172 | info->notify.value, |
173 | info->notify.global->context); |
174 | } |
175 | |
176 | /* Now invoke the local notify handler(s) if any are installed */ |
177 | |
178 | handler_obj = info->notify.handler_list_head; |
179 | while (handler_obj) { |
180 | handler_obj->notify.handler(info->notify.node, |
181 | info->notify.value, |
182 | handler_obj->notify.context); |
183 | |
184 | handler_obj = |
185 | handler_obj->notify.next[info->notify.handler_list_id]; |
186 | } |
187 | |
188 | /* All done with the info object */ |
189 | |
190 | acpi_ut_delete_generic_state(state: info); |
191 | } |
192 | |
193 | #if (!ACPI_REDUCED_HARDWARE) |
194 | /****************************************************************************** |
195 | * |
196 | * FUNCTION: acpi_ev_terminate |
197 | * |
198 | * PARAMETERS: none |
199 | * |
200 | * RETURN: none |
201 | * |
202 | * DESCRIPTION: Disable events and free memory allocated for table storage. |
203 | * |
204 | ******************************************************************************/ |
205 | |
206 | void acpi_ev_terminate(void) |
207 | { |
208 | u32 i; |
209 | acpi_status status; |
210 | |
211 | ACPI_FUNCTION_TRACE(ev_terminate); |
212 | |
213 | if (acpi_gbl_events_initialized) { |
214 | /* |
215 | * Disable all event-related functionality. In all cases, on error, |
216 | * print a message but obviously we don't abort. |
217 | */ |
218 | |
219 | /* Disable all fixed events */ |
220 | |
221 | for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) { |
222 | status = acpi_disable_event(i, 0); |
223 | if (ACPI_FAILURE(status)) { |
224 | ACPI_ERROR((AE_INFO, |
225 | "Could not disable fixed event %u" , |
226 | (u32) i)); |
227 | } |
228 | } |
229 | |
230 | /* Disable all GPEs in all GPE blocks */ |
231 | |
232 | status = acpi_ev_walk_gpe_list(acpi_hw_disable_gpe_block, NULL); |
233 | if (ACPI_FAILURE(status)) { |
234 | ACPI_EXCEPTION((AE_INFO, status, |
235 | "Could not disable GPEs in GPE block" )); |
236 | } |
237 | |
238 | status = acpi_ev_remove_global_lock_handler(); |
239 | if (ACPI_FAILURE(status)) { |
240 | ACPI_EXCEPTION((AE_INFO, status, |
241 | "Could not remove Global Lock handler" )); |
242 | } |
243 | |
244 | acpi_gbl_events_initialized = FALSE; |
245 | } |
246 | |
247 | /* Remove SCI handlers */ |
248 | |
249 | status = acpi_ev_remove_all_sci_handlers(); |
250 | if (ACPI_FAILURE(status)) { |
251 | ACPI_ERROR((AE_INFO, "Could not remove SCI handler" )); |
252 | } |
253 | |
254 | /* Deallocate all handler objects installed within GPE info structs */ |
255 | |
256 | status = acpi_ev_walk_gpe_list(acpi_ev_delete_gpe_handlers, NULL); |
257 | if (ACPI_FAILURE(status)) { |
258 | ACPI_EXCEPTION((AE_INFO, status, |
259 | "Could not delete GPE handlers" )); |
260 | } |
261 | |
262 | /* Return to original mode if necessary */ |
263 | |
264 | if (acpi_gbl_original_mode == ACPI_SYS_MODE_LEGACY) { |
265 | status = acpi_disable(); |
266 | if (ACPI_FAILURE(status)) { |
267 | ACPI_WARNING((AE_INFO, "AcpiDisable failed" )); |
268 | } |
269 | } |
270 | return_VOID; |
271 | } |
272 | |
273 | #endif /* !ACPI_REDUCED_HARDWARE */ |
274 | |