Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * acpi_utils.c - ACPI Utility Functions ($Revision: 10 $) | |
3 | * | |
4 | * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> | |
5 | * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> | |
6 | * | |
7 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or (at | |
12 | * your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License along | |
20 | * with this program; if not, write to the Free Software Foundation, Inc., | |
21 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | |
22 | * | |
23 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
24 | */ | |
25 | ||
26 | #include <linux/kernel.h> | |
27 | #include <linux/module.h> | |
28 | #include <linux/init.h> | |
29 | #include <linux/types.h> | |
30 | #include <acpi/acpi_bus.h> | |
31 | #include <acpi/acpi_drivers.h> | |
32 | ||
a192a958 LB |
33 | #include "internal.h" |
34 | ||
1da177e4 | 35 | #define _COMPONENT ACPI_BUS_COMPONENT |
f52fd66d | 36 | ACPI_MODULE_NAME("utils"); |
1da177e4 LT |
37 | |
38 | /* -------------------------------------------------------------------------- | |
39 | Object Evaluation Helpers | |
40 | -------------------------------------------------------------------------- */ | |
4fd7f518 HH |
41 | static void |
42 | acpi_util_eval_error(acpi_handle h, acpi_string p, acpi_status s) | |
43 | { | |
1da177e4 | 44 | #ifdef ACPI_DEBUG_OUTPUT |
4fd7f518 HH |
45 | char prefix[80] = {'\0'}; |
46 | struct acpi_buffer buffer = {sizeof(prefix), prefix}; | |
47 | acpi_get_name(h, ACPI_FULL_PATHNAME, &buffer); | |
48 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluate [%s.%s]: %s\n", | |
49 | (char *) prefix, p, acpi_format_exception(s))); | |
1da177e4 | 50 | #else |
4fd7f518 | 51 | return; |
1da177e4 | 52 | #endif |
4fd7f518 HH |
53 | } |
54 | ||
1da177e4 | 55 | acpi_status |
4be44fcd LB |
56 | acpi_extract_package(union acpi_object *package, |
57 | struct acpi_buffer *format, struct acpi_buffer *buffer) | |
1da177e4 | 58 | { |
4be44fcd LB |
59 | u32 size_required = 0; |
60 | u32 tail_offset = 0; | |
61 | char *format_string = NULL; | |
62 | u32 format_count = 0; | |
63 | u32 i = 0; | |
64 | u8 *head = NULL; | |
65 | u8 *tail = NULL; | |
1da177e4 | 66 | |
1da177e4 | 67 | |
4be44fcd LB |
68 | if (!package || (package->type != ACPI_TYPE_PACKAGE) |
69 | || (package->package.count < 1)) { | |
cece9296 | 70 | printk(KERN_WARNING PREFIX "Invalid package argument\n"); |
d550d98d | 71 | return AE_BAD_PARAMETER; |
1da177e4 LT |
72 | } |
73 | ||
74 | if (!format || !format->pointer || (format->length < 1)) { | |
cece9296 | 75 | printk(KERN_WARNING PREFIX "Invalid format argument\n"); |
d550d98d | 76 | return AE_BAD_PARAMETER; |
1da177e4 LT |
77 | } |
78 | ||
79 | if (!buffer) { | |
cece9296 | 80 | printk(KERN_WARNING PREFIX "Invalid buffer argument\n"); |
d550d98d | 81 | return AE_BAD_PARAMETER; |
1da177e4 LT |
82 | } |
83 | ||
4be44fcd | 84 | format_count = (format->length / sizeof(char)) - 1; |
1da177e4 | 85 | if (format_count > package->package.count) { |
cece9296 LB |
86 | printk(KERN_WARNING PREFIX "Format specifies more objects [%d]" |
87 | " than exist in package [%d].\n", | |
88 | format_count, package->package.count); | |
d550d98d | 89 | return AE_BAD_DATA; |
1da177e4 LT |
90 | } |
91 | ||
50dd0969 | 92 | format_string = format->pointer; |
1da177e4 LT |
93 | |
94 | /* | |
95 | * Calculate size_required. | |
96 | */ | |
4be44fcd | 97 | for (i = 0; i < format_count; i++) { |
1da177e4 LT |
98 | |
99 | union acpi_object *element = &(package->package.elements[i]); | |
100 | ||
101 | if (!element) { | |
d550d98d | 102 | return AE_BAD_DATA; |
1da177e4 LT |
103 | } |
104 | ||
105 | switch (element->type) { | |
106 | ||
107 | case ACPI_TYPE_INTEGER: | |
108 | switch (format_string[i]) { | |
109 | case 'N': | |
110 | size_required += sizeof(acpi_integer); | |
111 | tail_offset += sizeof(acpi_integer); | |
112 | break; | |
113 | case 'S': | |
4be44fcd LB |
114 | size_required += |
115 | sizeof(char *) + sizeof(acpi_integer) + | |
116 | sizeof(char); | |
117 | tail_offset += sizeof(char *); | |
1da177e4 LT |
118 | break; |
119 | default: | |
cece9296 | 120 | printk(KERN_WARNING PREFIX "Invalid package element" |
a6fc6720 | 121 | " [%d]: got number, expecing" |
cece9296 LB |
122 | " [%c]\n", |
123 | i, format_string[i]); | |
d550d98d | 124 | return AE_BAD_DATA; |
1da177e4 LT |
125 | break; |
126 | } | |
127 | break; | |
128 | ||
129 | case ACPI_TYPE_STRING: | |
130 | case ACPI_TYPE_BUFFER: | |
131 | switch (format_string[i]) { | |
132 | case 'S': | |
4be44fcd LB |
133 | size_required += |
134 | sizeof(char *) + | |
135 | (element->string.length * sizeof(char)) + | |
136 | sizeof(char); | |
137 | tail_offset += sizeof(char *); | |
1da177e4 LT |
138 | break; |
139 | case 'B': | |
4be44fcd LB |
140 | size_required += |
141 | sizeof(u8 *) + | |
142 | (element->buffer.length * sizeof(u8)); | |
143 | tail_offset += sizeof(u8 *); | |
1da177e4 LT |
144 | break; |
145 | default: | |
cece9296 | 146 | printk(KERN_WARNING PREFIX "Invalid package element" |
a6fc6720 | 147 | " [%d] got string/buffer," |
cece9296 LB |
148 | " expecing [%c]\n", |
149 | i, format_string[i]); | |
d550d98d | 150 | return AE_BAD_DATA; |
1da177e4 LT |
151 | break; |
152 | } | |
153 | break; | |
154 | ||
155 | case ACPI_TYPE_PACKAGE: | |
156 | default: | |
4be44fcd LB |
157 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
158 | "Found unsupported element at index=%d\n", | |
159 | i)); | |
1da177e4 | 160 | /* TBD: handle nested packages... */ |
d550d98d | 161 | return AE_SUPPORT; |
1da177e4 LT |
162 | break; |
163 | } | |
164 | } | |
165 | ||
166 | /* | |
167 | * Validate output buffer. | |
168 | */ | |
169 | if (buffer->length < size_required) { | |
170 | buffer->length = size_required; | |
d550d98d | 171 | return AE_BUFFER_OVERFLOW; |
4be44fcd | 172 | } else if (buffer->length != size_required || !buffer->pointer) { |
d550d98d | 173 | return AE_BAD_PARAMETER; |
1da177e4 LT |
174 | } |
175 | ||
176 | head = buffer->pointer; | |
177 | tail = buffer->pointer + tail_offset; | |
178 | ||
179 | /* | |
180 | * Extract package data. | |
181 | */ | |
4be44fcd | 182 | for (i = 0; i < format_count; i++) { |
1da177e4 LT |
183 | |
184 | u8 **pointer = NULL; | |
185 | union acpi_object *element = &(package->package.elements[i]); | |
186 | ||
187 | if (!element) { | |
d550d98d | 188 | return AE_BAD_DATA; |
1da177e4 LT |
189 | } |
190 | ||
191 | switch (element->type) { | |
192 | ||
193 | case ACPI_TYPE_INTEGER: | |
194 | switch (format_string[i]) { | |
195 | case 'N': | |
4be44fcd LB |
196 | *((acpi_integer *) head) = |
197 | element->integer.value; | |
1da177e4 LT |
198 | head += sizeof(acpi_integer); |
199 | break; | |
200 | case 'S': | |
4be44fcd | 201 | pointer = (u8 **) head; |
1da177e4 | 202 | *pointer = tail; |
4be44fcd LB |
203 | *((acpi_integer *) tail) = |
204 | element->integer.value; | |
205 | head += sizeof(acpi_integer *); | |
1da177e4 LT |
206 | tail += sizeof(acpi_integer); |
207 | /* NULL terminate string */ | |
208 | *tail = (char)0; | |
209 | tail += sizeof(char); | |
210 | break; | |
211 | default: | |
212 | /* Should never get here */ | |
213 | break; | |
214 | } | |
215 | break; | |
216 | ||
217 | case ACPI_TYPE_STRING: | |
218 | case ACPI_TYPE_BUFFER: | |
219 | switch (format_string[i]) { | |
220 | case 'S': | |
4be44fcd | 221 | pointer = (u8 **) head; |
1da177e4 | 222 | *pointer = tail; |
4be44fcd LB |
223 | memcpy(tail, element->string.pointer, |
224 | element->string.length); | |
225 | head += sizeof(char *); | |
1da177e4 LT |
226 | tail += element->string.length * sizeof(char); |
227 | /* NULL terminate string */ | |
228 | *tail = (char)0; | |
229 | tail += sizeof(char); | |
230 | break; | |
231 | case 'B': | |
4be44fcd | 232 | pointer = (u8 **) head; |
1da177e4 | 233 | *pointer = tail; |
4be44fcd LB |
234 | memcpy(tail, element->buffer.pointer, |
235 | element->buffer.length); | |
236 | head += sizeof(u8 *); | |
1da177e4 LT |
237 | tail += element->buffer.length * sizeof(u8); |
238 | break; | |
239 | default: | |
240 | /* Should never get here */ | |
241 | break; | |
242 | } | |
243 | break; | |
244 | ||
245 | case ACPI_TYPE_PACKAGE: | |
246 | /* TBD: handle nested packages... */ | |
247 | default: | |
248 | /* Should never get here */ | |
249 | break; | |
250 | } | |
251 | } | |
252 | ||
d550d98d | 253 | return AE_OK; |
1da177e4 | 254 | } |
1da177e4 | 255 | |
4be44fcd | 256 | EXPORT_SYMBOL(acpi_extract_package); |
1da177e4 LT |
257 | |
258 | acpi_status | |
4be44fcd LB |
259 | acpi_evaluate_integer(acpi_handle handle, |
260 | acpi_string pathname, | |
27663c58 | 261 | struct acpi_object_list *arguments, unsigned long long *data) |
1da177e4 | 262 | { |
4be44fcd | 263 | acpi_status status = AE_OK; |
40599072 | 264 | union acpi_object element; |
4be44fcd | 265 | struct acpi_buffer buffer = { 0, NULL }; |
1da177e4 | 266 | |
1da177e4 | 267 | if (!data) |
d550d98d | 268 | return AE_BAD_PARAMETER; |
1da177e4 | 269 | |
1da177e4 | 270 | buffer.length = sizeof(union acpi_object); |
40599072 | 271 | buffer.pointer = &element; |
1da177e4 LT |
272 | status = acpi_evaluate_object(handle, pathname, arguments, &buffer); |
273 | if (ACPI_FAILURE(status)) { | |
274 | acpi_util_eval_error(handle, pathname, status); | |
d550d98d | 275 | return status; |
1da177e4 LT |
276 | } |
277 | ||
40599072 | 278 | if (element.type != ACPI_TYPE_INTEGER) { |
1da177e4 | 279 | acpi_util_eval_error(handle, pathname, AE_BAD_DATA); |
d550d98d | 280 | return AE_BAD_DATA; |
1da177e4 LT |
281 | } |
282 | ||
40599072 | 283 | *data = element.integer.value; |
1da177e4 | 284 | |
27663c58 | 285 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Return value [%llu]\n", *data)); |
1da177e4 | 286 | |
d550d98d | 287 | return AE_OK; |
1da177e4 | 288 | } |
1da177e4 | 289 | |
4be44fcd | 290 | EXPORT_SYMBOL(acpi_evaluate_integer); |
1da177e4 LT |
291 | |
292 | #if 0 | |
293 | acpi_status | |
4be44fcd LB |
294 | acpi_evaluate_string(acpi_handle handle, |
295 | acpi_string pathname, | |
296 | acpi_object_list * arguments, acpi_string * data) | |
1da177e4 | 297 | { |
4be44fcd LB |
298 | acpi_status status = AE_OK; |
299 | acpi_object *element = NULL; | |
300 | acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | |
1da177e4 | 301 | |
1da177e4 LT |
302 | |
303 | if (!data) | |
d550d98d | 304 | return AE_BAD_PARAMETER; |
1da177e4 LT |
305 | |
306 | status = acpi_evaluate_object(handle, pathname, arguments, &buffer); | |
307 | if (ACPI_FAILURE(status)) { | |
308 | acpi_util_eval_error(handle, pathname, status); | |
d550d98d | 309 | return status; |
1da177e4 LT |
310 | } |
311 | ||
312 | element = (acpi_object *) buffer.pointer; | |
313 | ||
4be44fcd LB |
314 | if ((element->type != ACPI_TYPE_STRING) |
315 | || (element->type != ACPI_TYPE_BUFFER) | |
316 | || !element->string.length) { | |
1da177e4 | 317 | acpi_util_eval_error(handle, pathname, AE_BAD_DATA); |
d550d98d | 318 | return AE_BAD_DATA; |
1da177e4 LT |
319 | } |
320 | ||
36bcbec7 | 321 | *data = kzalloc(element->string.length + 1, GFP_KERNEL); |
1da177e4 | 322 | if (!data) { |
6468463a | 323 | printk(KERN_ERR PREFIX "Memory allocation\n"); |
d550d98d | 324 | return -ENOMEM; |
1da177e4 | 325 | } |
1da177e4 LT |
326 | |
327 | memcpy(*data, element->string.pointer, element->string.length); | |
328 | ||
329 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Return value [%s]\n", *data)); | |
330 | ||
02438d87 | 331 | kfree(buffer.pointer); |
1da177e4 | 332 | |
d550d98d | 333 | return AE_OK; |
1da177e4 LT |
334 | } |
335 | #endif | |
336 | ||
1da177e4 | 337 | acpi_status |
4be44fcd LB |
338 | acpi_evaluate_reference(acpi_handle handle, |
339 | acpi_string pathname, | |
340 | struct acpi_object_list *arguments, | |
341 | struct acpi_handle_list *list) | |
1da177e4 | 342 | { |
4be44fcd LB |
343 | acpi_status status = AE_OK; |
344 | union acpi_object *package = NULL; | |
345 | union acpi_object *element = NULL; | |
346 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | |
347 | u32 i = 0; | |
1da177e4 | 348 | |
1da177e4 LT |
349 | |
350 | if (!list) { | |
d550d98d | 351 | return AE_BAD_PARAMETER; |
1da177e4 LT |
352 | } |
353 | ||
354 | /* Evaluate object. */ | |
355 | ||
356 | status = acpi_evaluate_object(handle, pathname, arguments, &buffer); | |
357 | if (ACPI_FAILURE(status)) | |
358 | goto end; | |
359 | ||
50dd0969 | 360 | package = buffer.pointer; |
1da177e4 LT |
361 | |
362 | if ((buffer.length == 0) || !package) { | |
6468463a LB |
363 | printk(KERN_ERR PREFIX "No return object (len %X ptr %p)\n", |
364 | (unsigned)buffer.length, package); | |
1da177e4 LT |
365 | status = AE_BAD_DATA; |
366 | acpi_util_eval_error(handle, pathname, status); | |
367 | goto end; | |
368 | } | |
369 | if (package->type != ACPI_TYPE_PACKAGE) { | |
6468463a LB |
370 | printk(KERN_ERR PREFIX "Expecting a [Package], found type %X\n", |
371 | package->type); | |
1da177e4 LT |
372 | status = AE_BAD_DATA; |
373 | acpi_util_eval_error(handle, pathname, status); | |
374 | goto end; | |
375 | } | |
376 | if (!package->package.count) { | |
6468463a LB |
377 | printk(KERN_ERR PREFIX "[Package] has zero elements (%p)\n", |
378 | package); | |
1da177e4 LT |
379 | status = AE_BAD_DATA; |
380 | acpi_util_eval_error(handle, pathname, status); | |
381 | goto end; | |
382 | } | |
383 | ||
384 | if (package->package.count > ACPI_MAX_HANDLES) { | |
d550d98d | 385 | return AE_NO_MEMORY; |
1da177e4 LT |
386 | } |
387 | list->count = package->package.count; | |
388 | ||
389 | /* Extract package data. */ | |
390 | ||
391 | for (i = 0; i < list->count; i++) { | |
392 | ||
393 | element = &(package->package.elements[i]); | |
394 | ||
cd0b2248 | 395 | if (element->type != ACPI_TYPE_LOCAL_REFERENCE) { |
1da177e4 | 396 | status = AE_BAD_DATA; |
6468463a LB |
397 | printk(KERN_ERR PREFIX |
398 | "Expecting a [Reference] package element, found type %X\n", | |
399 | element->type); | |
1da177e4 LT |
400 | acpi_util_eval_error(handle, pathname, status); |
401 | break; | |
402 | } | |
403 | ||
b6a16387 TR |
404 | if (!element->reference.handle) { |
405 | printk(KERN_WARNING PREFIX "Invalid reference in" | |
406 | " package %s\n", pathname); | |
407 | status = AE_NULL_ENTRY; | |
408 | break; | |
409 | } | |
1da177e4 LT |
410 | /* Get the acpi_handle. */ |
411 | ||
412 | list->handles[i] = element->reference.handle; | |
413 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found reference [%p]\n", | |
4be44fcd | 414 | list->handles[i])); |
1da177e4 LT |
415 | } |
416 | ||
4be44fcd | 417 | end: |
1da177e4 LT |
418 | if (ACPI_FAILURE(status)) { |
419 | list->count = 0; | |
420 | //kfree(list->handles); | |
421 | } | |
422 | ||
02438d87 | 423 | kfree(buffer.pointer); |
1da177e4 | 424 | |
d550d98d | 425 | return status; |
1da177e4 | 426 | } |
1da177e4 | 427 | |
4be44fcd | 428 | EXPORT_SYMBOL(acpi_evaluate_reference); |