utils.c 12.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
/*
 *  acpi_utils.c - ACPI Utility Functions ($Revision: 10 $)
 *
 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or (at
 *  your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

#include <linux/kernel.h>
#include <linux/module.h>
28
#include <linux/slab.h>
L
Linus Torvalds 已提交
29 30
#include <linux/init.h>
#include <linux/types.h>
31 32
#include <linux/hardirq.h>
#include <linux/acpi.h>
L
Linus Torvalds 已提交
33 34 35
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>

36 37
#include "internal.h"

L
Linus Torvalds 已提交
38
#define _COMPONENT		ACPI_BUS_COMPONENT
39
ACPI_MODULE_NAME("utils");
L
Linus Torvalds 已提交
40 41 42 43

/* --------------------------------------------------------------------------
                            Object Evaluation Helpers
   -------------------------------------------------------------------------- */
44 45 46
static void
acpi_util_eval_error(acpi_handle h, acpi_string p, acpi_status s)
{
L
Linus Torvalds 已提交
47
#ifdef ACPI_DEBUG_OUTPUT
48 49 50 51 52
	char prefix[80] = {'\0'};
	struct acpi_buffer buffer = {sizeof(prefix), prefix};
	acpi_get_name(h, ACPI_FULL_PATHNAME, &buffer);
	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluate [%s.%s]: %s\n",
		(char *) prefix, p, acpi_format_exception(s)));
L
Linus Torvalds 已提交
53
#else
54
	return;
L
Linus Torvalds 已提交
55
#endif
56 57
}

L
Linus Torvalds 已提交
58
acpi_status
L
Len Brown 已提交
59 60
acpi_extract_package(union acpi_object *package,
		     struct acpi_buffer *format, struct acpi_buffer *buffer)
L
Linus Torvalds 已提交
61
{
L
Len Brown 已提交
62 63 64 65 66 67 68
	u32 size_required = 0;
	u32 tail_offset = 0;
	char *format_string = NULL;
	u32 format_count = 0;
	u32 i = 0;
	u8 *head = NULL;
	u8 *tail = NULL;
L
Linus Torvalds 已提交
69 70


L
Len Brown 已提交
71 72
	if (!package || (package->type != ACPI_TYPE_PACKAGE)
	    || (package->package.count < 1)) {
73
		printk(KERN_WARNING PREFIX "Invalid package argument\n");
74
		return AE_BAD_PARAMETER;
L
Linus Torvalds 已提交
75 76 77
	}

	if (!format || !format->pointer || (format->length < 1)) {
78
		printk(KERN_WARNING PREFIX "Invalid format argument\n");
79
		return AE_BAD_PARAMETER;
L
Linus Torvalds 已提交
80 81 82
	}

	if (!buffer) {
83
		printk(KERN_WARNING PREFIX "Invalid buffer argument\n");
84
		return AE_BAD_PARAMETER;
L
Linus Torvalds 已提交
85 86
	}

L
Len Brown 已提交
87
	format_count = (format->length / sizeof(char)) - 1;
L
Linus Torvalds 已提交
88
	if (format_count > package->package.count) {
89 90 91
		printk(KERN_WARNING PREFIX "Format specifies more objects [%d]"
			      " than exist in package [%d].\n",
			      format_count, package->package.count);
92
		return AE_BAD_DATA;
L
Linus Torvalds 已提交
93 94
	}

95
	format_string = format->pointer;
L
Linus Torvalds 已提交
96 97 98 99

	/*
	 * Calculate size_required.
	 */
L
Len Brown 已提交
100
	for (i = 0; i < format_count; i++) {
L
Linus Torvalds 已提交
101 102 103 104

		union acpi_object *element = &(package->package.elements[i]);

		if (!element) {
105
			return AE_BAD_DATA;
L
Linus Torvalds 已提交
106 107 108 109 110 111 112
		}

		switch (element->type) {

		case ACPI_TYPE_INTEGER:
			switch (format_string[i]) {
			case 'N':
L
Lin Ming 已提交
113 114
				size_required += sizeof(u64);
				tail_offset += sizeof(u64);
L
Linus Torvalds 已提交
115 116
				break;
			case 'S':
L
Len Brown 已提交
117
				size_required +=
L
Lin Ming 已提交
118
				    sizeof(char *) + sizeof(u64) +
L
Len Brown 已提交
119 120
				    sizeof(char);
				tail_offset += sizeof(char *);
L
Linus Torvalds 已提交
121 122
				break;
			default:
123
				printk(KERN_WARNING PREFIX "Invalid package element"
124
					      " [%d]: got number, expecing"
125 126
					      " [%c]\n",
					      i, format_string[i]);
127
				return AE_BAD_DATA;
L
Linus Torvalds 已提交
128 129 130 131 132 133 134 135
				break;
			}
			break;

		case ACPI_TYPE_STRING:
		case ACPI_TYPE_BUFFER:
			switch (format_string[i]) {
			case 'S':
L
Len Brown 已提交
136 137 138 139 140
				size_required +=
				    sizeof(char *) +
				    (element->string.length * sizeof(char)) +
				    sizeof(char);
				tail_offset += sizeof(char *);
L
Linus Torvalds 已提交
141 142
				break;
			case 'B':
L
Len Brown 已提交
143 144 145 146
				size_required +=
				    sizeof(u8 *) +
				    (element->buffer.length * sizeof(u8));
				tail_offset += sizeof(u8 *);
L
Linus Torvalds 已提交
147 148
				break;
			default:
149
				printk(KERN_WARNING PREFIX "Invalid package element"
150
					      " [%d] got string/buffer,"
151 152
					      " expecing [%c]\n",
					      i, format_string[i]);
153
				return AE_BAD_DATA;
L
Linus Torvalds 已提交
154 155 156 157 158 159
				break;
			}
			break;

		case ACPI_TYPE_PACKAGE:
		default:
L
Len Brown 已提交
160 161 162
			ACPI_DEBUG_PRINT((ACPI_DB_INFO,
					  "Found unsupported element at index=%d\n",
					  i));
L
Linus Torvalds 已提交
163
			/* TBD: handle nested packages... */
164
			return AE_SUPPORT;
L
Linus Torvalds 已提交
165 166 167 168 169 170 171 172 173
			break;
		}
	}

	/*
	 * Validate output buffer.
	 */
	if (buffer->length < size_required) {
		buffer->length = size_required;
174
		return AE_BUFFER_OVERFLOW;
L
Len Brown 已提交
175
	} else if (buffer->length != size_required || !buffer->pointer) {
176
		return AE_BAD_PARAMETER;
L
Linus Torvalds 已提交
177 178 179 180 181 182 183 184
	}

	head = buffer->pointer;
	tail = buffer->pointer + tail_offset;

	/*
	 * Extract package data.
	 */
L
Len Brown 已提交
185
	for (i = 0; i < format_count; i++) {
L
Linus Torvalds 已提交
186 187 188 189 190

		u8 **pointer = NULL;
		union acpi_object *element = &(package->package.elements[i]);

		if (!element) {
191
			return AE_BAD_DATA;
L
Linus Torvalds 已提交
192 193 194 195 196 197 198
		}

		switch (element->type) {

		case ACPI_TYPE_INTEGER:
			switch (format_string[i]) {
			case 'N':
L
Lin Ming 已提交
199
				*((u64 *) head) =
L
Len Brown 已提交
200
				    element->integer.value;
L
Lin Ming 已提交
201
				head += sizeof(u64);
L
Linus Torvalds 已提交
202 203
				break;
			case 'S':
L
Len Brown 已提交
204
				pointer = (u8 **) head;
L
Linus Torvalds 已提交
205
				*pointer = tail;
L
Lin Ming 已提交
206
				*((u64 *) tail) =
L
Len Brown 已提交
207
				    element->integer.value;
L
Lin Ming 已提交
208 209
				head += sizeof(u64 *);
				tail += sizeof(u64);
L
Linus Torvalds 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222 223
				/* NULL terminate string */
				*tail = (char)0;
				tail += sizeof(char);
				break;
			default:
				/* Should never get here */
				break;
			}
			break;

		case ACPI_TYPE_STRING:
		case ACPI_TYPE_BUFFER:
			switch (format_string[i]) {
			case 'S':
L
Len Brown 已提交
224
				pointer = (u8 **) head;
L
Linus Torvalds 已提交
225
				*pointer = tail;
L
Len Brown 已提交
226 227 228
				memcpy(tail, element->string.pointer,
				       element->string.length);
				head += sizeof(char *);
L
Linus Torvalds 已提交
229 230 231 232 233 234
				tail += element->string.length * sizeof(char);
				/* NULL terminate string */
				*tail = (char)0;
				tail += sizeof(char);
				break;
			case 'B':
L
Len Brown 已提交
235
				pointer = (u8 **) head;
L
Linus Torvalds 已提交
236
				*pointer = tail;
L
Len Brown 已提交
237 238 239
				memcpy(tail, element->buffer.pointer,
				       element->buffer.length);
				head += sizeof(u8 *);
L
Linus Torvalds 已提交
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
				tail += element->buffer.length * sizeof(u8);
				break;
			default:
				/* Should never get here */
				break;
			}
			break;

		case ACPI_TYPE_PACKAGE:
			/* TBD: handle nested packages... */
		default:
			/* Should never get here */
			break;
		}
	}

256
	return AE_OK;
L
Linus Torvalds 已提交
257 258
}

L
Len Brown 已提交
259
EXPORT_SYMBOL(acpi_extract_package);
L
Linus Torvalds 已提交
260 261

acpi_status
L
Len Brown 已提交
262 263
acpi_evaluate_integer(acpi_handle handle,
		      acpi_string pathname,
264
		      struct acpi_object_list *arguments, unsigned long long *data)
L
Linus Torvalds 已提交
265
{
L
Len Brown 已提交
266
	acpi_status status = AE_OK;
267
	union acpi_object element;
L
Len Brown 已提交
268
	struct acpi_buffer buffer = { 0, NULL };
L
Linus Torvalds 已提交
269 270

	if (!data)
271
		return AE_BAD_PARAMETER;
L
Linus Torvalds 已提交
272 273

	buffer.length = sizeof(union acpi_object);
274
	buffer.pointer = &element;
L
Linus Torvalds 已提交
275 276 277
	status = acpi_evaluate_object(handle, pathname, arguments, &buffer);
	if (ACPI_FAILURE(status)) {
		acpi_util_eval_error(handle, pathname, status);
278
		return status;
L
Linus Torvalds 已提交
279 280
	}

281
	if (element.type != ACPI_TYPE_INTEGER) {
L
Linus Torvalds 已提交
282
		acpi_util_eval_error(handle, pathname, AE_BAD_DATA);
283
		return AE_BAD_DATA;
L
Linus Torvalds 已提交
284 285
	}

286
	*data = element.integer.value;
L
Linus Torvalds 已提交
287

288
	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Return value [%llu]\n", *data));
L
Linus Torvalds 已提交
289

290
	return AE_OK;
L
Linus Torvalds 已提交
291 292
}

L
Len Brown 已提交
293
EXPORT_SYMBOL(acpi_evaluate_integer);
L
Linus Torvalds 已提交
294 295

acpi_status
L
Len Brown 已提交
296 297 298 299
acpi_evaluate_reference(acpi_handle handle,
			acpi_string pathname,
			struct acpi_object_list *arguments,
			struct acpi_handle_list *list)
L
Linus Torvalds 已提交
300
{
L
Len Brown 已提交
301 302 303 304 305
	acpi_status status = AE_OK;
	union acpi_object *package = NULL;
	union acpi_object *element = NULL;
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	u32 i = 0;
L
Linus Torvalds 已提交
306 307 308


	if (!list) {
309
		return AE_BAD_PARAMETER;
L
Linus Torvalds 已提交
310 311 312 313 314 315 316 317
	}

	/* Evaluate object. */

	status = acpi_evaluate_object(handle, pathname, arguments, &buffer);
	if (ACPI_FAILURE(status))
		goto end;

318
	package = buffer.pointer;
L
Linus Torvalds 已提交
319 320

	if ((buffer.length == 0) || !package) {
321 322
		printk(KERN_ERR PREFIX "No return object (len %X ptr %p)\n",
			    (unsigned)buffer.length, package);
L
Linus Torvalds 已提交
323 324 325 326 327
		status = AE_BAD_DATA;
		acpi_util_eval_error(handle, pathname, status);
		goto end;
	}
	if (package->type != ACPI_TYPE_PACKAGE) {
328 329
		printk(KERN_ERR PREFIX "Expecting a [Package], found type %X\n",
			    package->type);
L
Linus Torvalds 已提交
330 331 332 333 334
		status = AE_BAD_DATA;
		acpi_util_eval_error(handle, pathname, status);
		goto end;
	}
	if (!package->package.count) {
335 336
		printk(KERN_ERR PREFIX "[Package] has zero elements (%p)\n",
			    package);
L
Linus Torvalds 已提交
337 338 339 340 341 342
		status = AE_BAD_DATA;
		acpi_util_eval_error(handle, pathname, status);
		goto end;
	}

	if (package->package.count > ACPI_MAX_HANDLES) {
343
		return AE_NO_MEMORY;
L
Linus Torvalds 已提交
344 345 346 347 348 349 350 351 352
	}
	list->count = package->package.count;

	/* Extract package data. */

	for (i = 0; i < list->count; i++) {

		element = &(package->package.elements[i]);

353
		if (element->type != ACPI_TYPE_LOCAL_REFERENCE) {
L
Linus Torvalds 已提交
354
			status = AE_BAD_DATA;
355 356 357
			printk(KERN_ERR PREFIX
				    "Expecting a [Reference] package element, found type %X\n",
				    element->type);
L
Linus Torvalds 已提交
358 359 360 361
			acpi_util_eval_error(handle, pathname, status);
			break;
		}

362 363 364 365 366 367
		if (!element->reference.handle) {
			printk(KERN_WARNING PREFIX "Invalid reference in"
			       " package %s\n", pathname);
			status = AE_NULL_ENTRY;
			break;
		}
L
Linus Torvalds 已提交
368 369 370 371
		/* Get the  acpi_handle. */

		list->handles[i] = element->reference.handle;
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found reference [%p]\n",
L
Len Brown 已提交
372
				  list->handles[i]));
L
Linus Torvalds 已提交
373 374
	}

L
Len Brown 已提交
375
      end:
L
Linus Torvalds 已提交
376 377 378 379 380
	if (ACPI_FAILURE(status)) {
		list->count = 0;
		//kfree(list->handles);
	}

381
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
382

383
	return status;
L
Linus Torvalds 已提交
384 385
}

L
Len Brown 已提交
386
EXPORT_SYMBOL(acpi_evaluate_reference);
M
Matthew Garrett 已提交
387 388

acpi_status
389
acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld)
M
Matthew Garrett 已提交
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
{
	acpi_status status;
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *output;

	status = acpi_evaluate_object(handle, "_PLD", NULL, &buffer);

	if (ACPI_FAILURE(status))
		return status;

	output = buffer.pointer;

	if (!output || output->type != ACPI_TYPE_PACKAGE
	    || !output->package.count
	    || output->package.elements[0].type != ACPI_TYPE_BUFFER
405
	    || output->package.elements[0].buffer.length < ACPI_PLD_REV1_BUFFER_SIZE) {
M
Matthew Garrett 已提交
406 407 408 409
		status = AE_TYPE;
		goto out;
	}

410 411 412 413 414
	status = acpi_decode_pld_buffer(
			output->package.elements[0].buffer.pointer,
			output->package.elements[0].buffer.length,
			pld);

M
Matthew Garrett 已提交
415 416 417 418 419
out:
	kfree(buffer.pointer);
	return status;
}
EXPORT_SYMBOL(acpi_get_physical_device_location);
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461

/**
 * acpi_evaluate_hotplug_ost: Evaluate _OST for hotplug operations
 * @handle: ACPI device handle
 * @source_event: source event code
 * @status_code: status code
 * @status_buf: optional detailed information (NULL if none)
 *
 * Evaluate _OST for hotplug operations. All ACPI hotplug handlers
 * must call this function when evaluating _OST for hotplug operations.
 * When the platform does not support _OST, this function has no effect.
 */
acpi_status
acpi_evaluate_hotplug_ost(acpi_handle handle, u32 source_event,
		u32 status_code, struct acpi_buffer *status_buf)
{
#ifdef ACPI_HOTPLUG_OST
	union acpi_object params[3] = {
		{.type = ACPI_TYPE_INTEGER,},
		{.type = ACPI_TYPE_INTEGER,},
		{.type = ACPI_TYPE_BUFFER,}
	};
	struct acpi_object_list arg_list = {3, params};
	acpi_status status;

	params[0].integer.value = source_event;
	params[1].integer.value = status_code;
	if (status_buf != NULL) {
		params[2].buffer.pointer = status_buf->pointer;
		params[2].buffer.length = status_buf->length;
	} else {
		params[2].buffer.pointer = NULL;
		params[2].buffer.length = 0;
	}

	status = acpi_evaluate_object(handle, "_OST", &arg_list, NULL);
	return status;
#else
	return AE_OK;
#endif
}
EXPORT_SYMBOL(acpi_evaluate_hotplug_ost);
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497

/**
 * acpi_handle_printk: Print message with ACPI prefix and object path
 *
 * This function is called through acpi_handle_<level> macros and prints
 * a message with ACPI prefix and object path.  This function acquires
 * the global namespace mutex to obtain an object path.  In interrupt
 * context, it shows the object path as <n/a>.
 */
void
acpi_handle_printk(const char *level, acpi_handle handle, const char *fmt, ...)
{
	struct va_format vaf;
	va_list args;
	struct acpi_buffer buffer = {
		.length = ACPI_ALLOCATE_BUFFER,
		.pointer = NULL
	};
	const char *path;

	va_start(args, fmt);
	vaf.fmt = fmt;
	vaf.va = &args;

	if (in_interrupt() ||
	    acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer) != AE_OK)
		path = "<n/a>";
	else
		path = buffer.pointer;

	printk("%sACPI: %s: %pV", level, path, &vaf);

	va_end(args);
	kfree(buffer.pointer);
}
EXPORT_SYMBOL(acpi_handle_printk);
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512

/**
 * acpi_has_method: Check whether @handle has a method named @name
 * @handle: ACPI device handle
 * @name: name of object or method
 *
 * Check whether @handle has a method named @name.
 */
bool acpi_has_method(acpi_handle handle, char *name)
{
	acpi_handle tmp;

	return ACPI_SUCCESS(acpi_get_handle(handle, name, &tmp));
}
EXPORT_SYMBOL(acpi_has_method);