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