Patch by Jonathon Wilson:
[reactos.git] / reactos / drivers / bus / acpi / events / evxfregn.c
1 /******************************************************************************
2 *
3 * Module Name: evxfregn - External Interfaces, ACPI Operation Regions and
4 * Address Spaces.
5 * $Revision: 1.2 $
6 *
7 *****************************************************************************/
8
9 /*
10 * Copyright (C) 2000, 2001 R. Byron Moore
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27
28 #include "acpi.h"
29 #include "achware.h"
30 #include "acnamesp.h"
31 #include "acevents.h"
32 #include "amlcode.h"
33 #include "acinterp.h"
34
35 #define _COMPONENT ACPI_EVENTS
36 MODULE_NAME ("evxfregn")
37
38
39 /******************************************************************************
40 *
41 * FUNCTION: Acpi_install_address_space_handler
42 *
43 * PARAMETERS: Device - Handle for the device
44 * Space_id - The address space ID
45 * Handler - Address of the handler
46 * Setup - Address of the setup function
47 * Context - Value passed to the handler on each access
48 *
49 * RETURN: Status
50 *
51 * DESCRIPTION: Install a handler for all Op_regions of a given Space_id.
52 *
53 ******************************************************************************/
54
55 ACPI_STATUS
56 acpi_install_address_space_handler (
57 ACPI_HANDLE device,
58 ACPI_ADDRESS_SPACE_TYPE space_id,
59 ADDRESS_SPACE_HANDLER handler,
60 ADDRESS_SPACE_SETUP setup,
61 void *context)
62 {
63 ACPI_OPERAND_OBJECT *obj_desc;
64 ACPI_OPERAND_OBJECT *handler_obj;
65 ACPI_NAMESPACE_NODE *node;
66 ACPI_STATUS status = AE_OK;
67 OBJECT_TYPE_INTERNAL type;
68 u16 flags = 0;
69
70
71 /* Parameter validation */
72
73 if ((!device) ||
74 ((!handler) && (handler != ACPI_DEFAULT_HANDLER))) {
75 return (AE_BAD_PARAMETER);
76 }
77
78 acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE);
79
80 /* Convert and validate the device handle */
81
82 node = acpi_ns_convert_handle_to_entry (device);
83 if (!node) {
84 status = AE_BAD_PARAMETER;
85 goto unlock_and_exit;
86 }
87
88 /*
89 * This registration is valid for only the types below
90 * and the root. This is where the default handlers
91 * get placed.
92 */
93
94 if ((node->type != ACPI_TYPE_DEVICE) &&
95 (node->type != ACPI_TYPE_PROCESSOR) &&
96 (node->type != ACPI_TYPE_THERMAL) &&
97 (node != acpi_gbl_root_node)) {
98 status = AE_BAD_PARAMETER;
99 goto unlock_and_exit;
100 }
101
102 if (handler == ACPI_DEFAULT_HANDLER) {
103 flags = ADDR_HANDLER_DEFAULT_INSTALLED;
104
105 switch (space_id) {
106 case ADDRESS_SPACE_SYSTEM_MEMORY:
107 handler = acpi_aml_system_memory_space_handler;
108 setup = acpi_ev_system_memory_region_setup;
109 break;
110
111 case ADDRESS_SPACE_SYSTEM_IO:
112 handler = acpi_aml_system_io_space_handler;
113 setup = acpi_ev_io_space_region_setup;
114 break;
115
116 case ADDRESS_SPACE_PCI_CONFIG:
117 handler = acpi_aml_pci_config_space_handler;
118 setup = acpi_ev_pci_config_region_setup;
119 break;
120
121 default:
122 status = AE_NOT_EXIST;
123 goto unlock_and_exit;
124 break;
125 }
126 }
127
128 /*
129 * If the caller hasn't specified a setup routine, use the default
130 */
131 if (!setup) {
132 setup = acpi_ev_default_region_setup;
133 }
134
135 /*
136 * Check for an existing internal object
137 */
138
139 obj_desc = acpi_ns_get_attached_object ((ACPI_HANDLE) node);
140 if (obj_desc) {
141 /*
142 * The object exists.
143 * Make sure the handler is not already installed.
144 */
145
146 /* check the address handler the user requested */
147
148 handler_obj = obj_desc->device.addr_handler;
149 while (handler_obj) {
150 /*
151 * We have an Address handler, see if user requested this
152 * address space.
153 */
154 if(handler_obj->addr_handler.space_id == space_id) {
155 status = AE_EXIST;
156 goto unlock_and_exit;
157 }
158
159 /*
160 * Move through the linked list of handlers
161 */
162 handler_obj = handler_obj->addr_handler.next;
163 }
164 }
165
166 else {
167 /* Obj_desc does not exist, create one */
168
169 if (node->type == ACPI_TYPE_ANY) {
170 type = ACPI_TYPE_DEVICE;
171 }
172
173 else {
174 type = node->type;
175 }
176
177 obj_desc = acpi_cm_create_internal_object (type);
178 if (!obj_desc) {
179 status = AE_NO_MEMORY;
180 goto unlock_and_exit;
181 }
182
183 /* Init new descriptor */
184
185 obj_desc->common.type = (u8) type;
186
187 /* Attach the new object to the Node */
188
189 status = acpi_ns_attach_object (node, obj_desc, (u8) type);
190 if (ACPI_FAILURE (status)) {
191 acpi_cm_remove_reference (obj_desc);
192 goto unlock_and_exit;
193 }
194 }
195
196 /*
197 * Now we can install the handler
198 *
199 * At this point we know that there is no existing handler.
200 * So, we just allocate the object for the handler and link it
201 * into the list.
202 */
203 handler_obj = acpi_cm_create_internal_object (INTERNAL_TYPE_ADDRESS_HANDLER);
204 if (!handler_obj) {
205 status = AE_NO_MEMORY;
206 goto unlock_and_exit;
207 }
208
209 handler_obj->addr_handler.space_id = (u8) space_id;
210 handler_obj->addr_handler.hflags = flags;
211 handler_obj->addr_handler.next = obj_desc->device.addr_handler;
212 handler_obj->addr_handler.region_list = NULL;
213 handler_obj->addr_handler.node = node;
214 handler_obj->addr_handler.handler = handler;
215 handler_obj->addr_handler.context = context;
216 handler_obj->addr_handler.setup = setup;
217
218 /*
219 * Now walk the namespace finding all of the regions this
220 * handler will manage.
221 *
222 * We start at the device and search the branch toward
223 * the leaf nodes until either the leaf is encountered or
224 * a device is detected that has an address handler of the
225 * same type.
226 *
227 * In either case we back up and search down the remainder
228 * of the branch
229 */
230 status = acpi_ns_walk_namespace (ACPI_TYPE_ANY, device,
231 ACPI_UINT32_MAX, NS_WALK_UNLOCK,
232 acpi_ev_addr_handler_helper,
233 handler_obj, NULL);
234
235 /*
236 * Place this handler 1st on the list
237 */
238
239 handler_obj->common.reference_count =
240 (u16) (handler_obj->common.reference_count +
241 obj_desc->common.reference_count - 1);
242 obj_desc->device.addr_handler = handler_obj;
243
244
245 unlock_and_exit:
246 acpi_cm_release_mutex (ACPI_MTX_NAMESPACE);
247 return (status);
248 }
249
250
251 /******************************************************************************
252 *
253 * FUNCTION: Acpi_remove_address_space_handler
254 *
255 * PARAMETERS: Space_id - The address space ID
256 * Handler - Address of the handler
257 *
258 * RETURN: Status
259 *
260 * DESCRIPTION: Install a handler for accesses on an Operation Region
261 *
262 ******************************************************************************/
263
264 ACPI_STATUS
265 acpi_remove_address_space_handler (
266 ACPI_HANDLE device,
267 ACPI_ADDRESS_SPACE_TYPE space_id,
268 ADDRESS_SPACE_HANDLER handler)
269 {
270 ACPI_OPERAND_OBJECT *obj_desc;
271 ACPI_OPERAND_OBJECT *handler_obj;
272 ACPI_OPERAND_OBJECT *region_obj;
273 ACPI_OPERAND_OBJECT **last_obj_ptr;
274 ACPI_NAMESPACE_NODE *node;
275 ACPI_STATUS status = AE_OK;
276
277
278 /* Parameter validation */
279
280 if ((!device) ||
281 ((!handler) && (handler != ACPI_DEFAULT_HANDLER))) {
282 return (AE_BAD_PARAMETER);
283 }
284
285 acpi_cm_acquire_mutex (ACPI_MTX_NAMESPACE);
286
287 /* Convert and validate the device handle */
288
289 node = acpi_ns_convert_handle_to_entry (device);
290 if (!node) {
291 status = AE_BAD_PARAMETER;
292 goto unlock_and_exit;
293 }
294
295
296 /* Make sure the internal object exists */
297
298 obj_desc = acpi_ns_get_attached_object ((ACPI_HANDLE) node);
299 if (!obj_desc) {
300 /*
301 * The object DNE.
302 */
303 status = AE_NOT_EXIST;
304 goto unlock_and_exit;
305 }
306
307 /*
308 * find the address handler the user requested
309 */
310
311 handler_obj = obj_desc->device.addr_handler;
312 last_obj_ptr = &obj_desc->device.addr_handler;
313 while (handler_obj) {
314 /*
315 * We have a handler, see if user requested this one
316 */
317
318 if(handler_obj->addr_handler.space_id == space_id) {
319 /*
320 * Got it, first dereference this in the Regions
321 */
322 region_obj = handler_obj->addr_handler.region_list;
323
324 /* Walk the handler's region list */
325
326 while (region_obj) {
327 /*
328 * First disassociate the handler from the region.
329 *
330 * NOTE: this doesn't mean that the region goes away
331 * The region is just inaccessible as indicated to
332 * the _REG method
333 */
334 acpi_ev_disassociate_region_from_handler(region_obj, FALSE);
335
336 /*
337 * Walk the list, since we took the first region and it
338 * was removed from the list by the dissassociate call
339 * we just get the first item on the list again
340 */
341 region_obj = handler_obj->addr_handler.region_list;
342
343 }
344
345 /*
346 * Remove this Handler object from the list
347 */
348 *last_obj_ptr = handler_obj->addr_handler.next;
349
350 /*
351 * Now we can delete the handler object
352 */
353 acpi_cm_remove_reference (handler_obj);
354 acpi_cm_remove_reference (handler_obj);
355
356 goto unlock_and_exit;
357 }
358
359 /*
360 * Move through the linked list of handlers
361 */
362 last_obj_ptr = &handler_obj->addr_handler.next;
363 handler_obj = handler_obj->addr_handler.next;
364 }
365
366
367 /*
368 * The handler does not exist
369 */
370 status = AE_NOT_EXIST;
371
372
373 unlock_and_exit:
374 acpi_cm_release_mutex (ACPI_MTX_NAMESPACE);
375 return (status);
376 }
377
378