diff options
| -rw-r--r-- | drivers/acpi/bus.c | 122 | ||||
| -rw-r--r-- | include/linux/acpi.h | 9 |
2 files changed, 131 insertions, 0 deletions
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 741191524353..12240be58f27 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c | |||
| @@ -344,6 +344,128 @@ bool acpi_bus_can_wakeup(acpi_handle handle) | |||
| 344 | 344 | ||
| 345 | EXPORT_SYMBOL(acpi_bus_can_wakeup); | 345 | EXPORT_SYMBOL(acpi_bus_can_wakeup); |
| 346 | 346 | ||
| 347 | static void acpi_print_osc_error(acpi_handle handle, | ||
| 348 | struct acpi_osc_context *context, char *error) | ||
| 349 | { | ||
| 350 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER}; | ||
| 351 | int i; | ||
| 352 | |||
| 353 | if (ACPI_FAILURE(acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer))) | ||
| 354 | printk(KERN_DEBUG "%s\n", error); | ||
| 355 | else { | ||
| 356 | printk(KERN_DEBUG "%s:%s\n", (char *)buffer.pointer, error); | ||
| 357 | kfree(buffer.pointer); | ||
| 358 | } | ||
| 359 | printk(KERN_DEBUG"_OSC request data:"); | ||
| 360 | for (i = 0; i < context->cap.length; i += sizeof(u32)) | ||
| 361 | printk("%x ", *((u32 *)(context->cap.pointer + i))); | ||
| 362 | printk("\n"); | ||
| 363 | } | ||
| 364 | |||
| 365 | static u8 hex_val(unsigned char c) | ||
| 366 | { | ||
| 367 | return isdigit(c) ? c - '0' : toupper(c) - 'A' + 10; | ||
| 368 | } | ||
| 369 | |||
| 370 | static acpi_status acpi_str_to_uuid(char *str, u8 *uuid) | ||
| 371 | { | ||
| 372 | int i; | ||
| 373 | static int opc_map_to_uuid[16] = {6, 4, 2, 0, 11, 9, 16, 14, 19, 21, | ||
| 374 | 24, 26, 28, 30, 32, 34}; | ||
| 375 | |||
| 376 | if (strlen(str) != 36) | ||
| 377 | return AE_BAD_PARAMETER; | ||
| 378 | for (i = 0; i < 36; i++) { | ||
| 379 | if (i == 8 || i == 13 || i == 18 || i == 23) { | ||
| 380 | if (str[i] != '-') | ||
| 381 | return AE_BAD_PARAMETER; | ||
| 382 | } else if (!isxdigit(str[i])) | ||
| 383 | return AE_BAD_PARAMETER; | ||
| 384 | } | ||
| 385 | for (i = 0; i < 16; i++) { | ||
| 386 | uuid[i] = hex_val(str[opc_map_to_uuid[i]]) << 4; | ||
| 387 | uuid[i] |= hex_val(str[opc_map_to_uuid[i] + 1]); | ||
| 388 | } | ||
| 389 | return AE_OK; | ||
| 390 | } | ||
| 391 | |||
| 392 | acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) | ||
| 393 | { | ||
| 394 | acpi_status status; | ||
| 395 | struct acpi_object_list input; | ||
| 396 | union acpi_object in_params[4]; | ||
| 397 | union acpi_object *out_obj; | ||
| 398 | u8 uuid[16]; | ||
| 399 | u32 errors; | ||
| 400 | |||
| 401 | if (!context) | ||
| 402 | return AE_ERROR; | ||
| 403 | if (ACPI_FAILURE(acpi_str_to_uuid(context->uuid_str, uuid))) | ||
| 404 | return AE_ERROR; | ||
| 405 | context->ret.length = ACPI_ALLOCATE_BUFFER; | ||
| 406 | context->ret.pointer = NULL; | ||
| 407 | |||
| 408 | /* Setting up input parameters */ | ||
| 409 | input.count = 4; | ||
| 410 | input.pointer = in_params; | ||
| 411 | in_params[0].type = ACPI_TYPE_BUFFER; | ||
| 412 | in_params[0].buffer.length = 16; | ||
| 413 | in_params[0].buffer.pointer = uuid; | ||
| 414 | in_params[1].type = ACPI_TYPE_INTEGER; | ||
| 415 | in_params[1].integer.value = context->rev; | ||
| 416 | in_params[2].type = ACPI_TYPE_INTEGER; | ||
| 417 | in_params[2].integer.value = context->cap.length/sizeof(u32); | ||
| 418 | in_params[3].type = ACPI_TYPE_BUFFER; | ||
| 419 | in_params[3].buffer.length = context->cap.length; | ||
| 420 | in_params[3].buffer.pointer = context->cap.pointer; | ||
| 421 | |||
| 422 | status = acpi_evaluate_object(handle, "_OSC", &input, &context->ret); | ||
| 423 | if (ACPI_FAILURE(status)) | ||
| 424 | return status; | ||
| 425 | |||
| 426 | /* return buffer should have the same length as cap buffer */ | ||
| 427 | if (context->ret.length != context->cap.length) | ||
| 428 | return AE_NULL_OBJECT; | ||
| 429 | |||
| 430 | out_obj = context->ret.pointer; | ||
| 431 | if (out_obj->type != ACPI_TYPE_BUFFER) { | ||
| 432 | acpi_print_osc_error(handle, context, | ||
| 433 | "_OSC evaluation returned wrong type"); | ||
| 434 | status = AE_TYPE; | ||
| 435 | goto out_kfree; | ||
| 436 | } | ||
| 437 | /* Need to ignore the bit0 in result code */ | ||
| 438 | errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0); | ||
| 439 | if (errors) { | ||
| 440 | if (errors & OSC_REQUEST_ERROR) | ||
| 441 | acpi_print_osc_error(handle, context, | ||
| 442 | "_OSC request failed"); | ||
| 443 | if (errors & OSC_INVALID_UUID_ERROR) | ||
| 444 | acpi_print_osc_error(handle, context, | ||
| 445 | "_OSC invalid UUID"); | ||
| 446 | if (errors & OSC_INVALID_REVISION_ERROR) | ||
| 447 | acpi_print_osc_error(handle, context, | ||
| 448 | "_OSC invalid revision"); | ||
| 449 | if (errors & OSC_CAPABILITIES_MASK_ERROR) { | ||
| 450 | if (((u32 *)context->cap.pointer)[OSC_QUERY_TYPE] | ||
| 451 | & OSC_QUERY_ENABLE) | ||
| 452 | goto out_success; | ||
| 453 | status = AE_SUPPORT; | ||
| 454 | goto out_kfree; | ||
| 455 | } | ||
| 456 | status = AE_ERROR; | ||
| 457 | goto out_kfree; | ||
| 458 | } | ||
| 459 | out_success: | ||
| 460 | return AE_OK; | ||
| 461 | |||
| 462 | out_kfree: | ||
| 463 | kfree(context->ret.pointer); | ||
| 464 | context->ret.pointer = NULL; | ||
| 465 | return status; | ||
| 466 | } | ||
| 467 | EXPORT_SYMBOL(acpi_run_osc); | ||
| 468 | |||
| 347 | /* -------------------------------------------------------------------------- | 469 | /* -------------------------------------------------------------------------- |
| 348 | Event Management | 470 | Event Management |
| 349 | -------------------------------------------------------------------------- */ | 471 | -------------------------------------------------------------------------- */ |
diff --git a/include/linux/acpi.h b/include/linux/acpi.h index dfcd920c3e54..3247e09db20d 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h | |||
| @@ -253,6 +253,13 @@ void __init acpi_old_suspend_ordering(void); | |||
| 253 | void __init acpi_s4_no_nvs(void); | 253 | void __init acpi_s4_no_nvs(void); |
| 254 | #endif /* CONFIG_PM_SLEEP */ | 254 | #endif /* CONFIG_PM_SLEEP */ |
| 255 | 255 | ||
| 256 | struct acpi_osc_context { | ||
| 257 | char *uuid_str; /* uuid string */ | ||
| 258 | int rev; | ||
| 259 | struct acpi_buffer cap; /* arg2/arg3 */ | ||
| 260 | struct acpi_buffer ret; /* free by caller if success */ | ||
| 261 | }; | ||
| 262 | |||
| 256 | #define OSC_QUERY_TYPE 0 | 263 | #define OSC_QUERY_TYPE 0 |
| 257 | #define OSC_SUPPORT_TYPE 1 | 264 | #define OSC_SUPPORT_TYPE 1 |
| 258 | #define OSC_CONTROL_TYPE 2 | 265 | #define OSC_CONTROL_TYPE 2 |
| @@ -265,6 +272,8 @@ void __init acpi_s4_no_nvs(void); | |||
| 265 | #define OSC_INVALID_REVISION_ERROR 8 | 272 | #define OSC_INVALID_REVISION_ERROR 8 |
| 266 | #define OSC_CAPABILITIES_MASK_ERROR 16 | 273 | #define OSC_CAPABILITIES_MASK_ERROR 16 |
| 267 | 274 | ||
| 275 | acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); | ||
| 276 | |||
| 268 | /* _OSC DW1 Definition (OS Support Fields) */ | 277 | /* _OSC DW1 Definition (OS Support Fields) */ |
| 269 | #define OSC_EXT_PCI_CONFIG_SUPPORT 1 | 278 | #define OSC_EXT_PCI_CONFIG_SUPPORT 1 |
| 270 | #define OSC_ACTIVE_STATE_PWR_SUPPORT 2 | 279 | #define OSC_ACTIVE_STATE_PWR_SUPPORT 2 |
