mirror of
https://github.com/torvalds/linux.git
synced 2025-11-06 11:39:24 +02:00
1/ Device DAX for persistent memory:
Device DAX is the device-centric analogue of Filesystem DAX
(CONFIG_FS_DAX). It allows memory ranges to be allocated and mapped
without need of an intervening file system. Device DAX is strict,
precise and predictable. Specifically this interface:
a) Guarantees fault granularity with respect to a given page size
(pte, pmd, or pud) set at configuration time.
b) Enforces deterministic behavior by being strict about what fault
scenarios are supported.
Persistent memory is the first target, but the mechanism is also
targeted for exclusive allocations of performance/feature differentiated
memory ranges.
2/ Support for the HPE DSM (device specific method) command formats.
This enables management of these first generation devices until a
unified DSM specification materializes.
3/ Further ACPI 6.1 compliance with support for the common dimm
identifier format.
4/ Various fixes and cleanups across the subsystem.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQIcBAABAgAGBQJXQhdeAAoJEB7SkWpmfYgCYP8P/RAgHkroL5lUKKU45TQUBKcY
diC9POeNSccme4tIRIQCGQUZ7+7mKM5ECv2ulF4xYOHvFBCcd/8OF6xKAXs48r3v
oguYhvX1YvIkBc9FUfBQbR1IsCOJ7uWp/UYiYCIQEXS5tS9Jv545j3ASqDt9xWoV
TWlceZn3yWSbASiV9qZ2eXhEkk75pg4yara++rsm2/7rs/TTXn5EIjBs+57BtAo+
6utI4fTy0CQvBYwVzam3m7y9dt2Z2jWXL4hgmT7pkvJ7HDoctVly0P9+bknJPUAo
g+NugKgTGeiqH5GYp5CTZ9KvL91sDF4q00pfinITVdFl0E3VE293cIHlAzSQBm5/
w58xxaRV958ZvpH7EaBmYQG82QDi/eFNqeHqVGn0xAM6MlaqO7avUMQp2lRPYMCJ
u1z/NloR5yo+sffHxsn5Luiq9KqOf6zk33PuxEkKbN74OayCSPn/SeVCO7rQR0B6
yPMJTTcTiCLnId1kOWAPaEmuK2U3BW/+ogg7hKgeCQSysuy5n6Ok5a2vEx/gJRAm
v9yF68RmIWumpHr+QB0TmB8mVbD5SY+xWTm3CqJb9MipuFIOF7AVsPyTgucBvE7s
v+i5F6MDO6tcVfiDT4AiZEt6D2TM5RbtckkUEX3ZTD6j7CGuR5D8bH0HNRrghrYk
KT1lAk6tjWBOGAHc5Ji7
=Y3Xv
-----END PGP SIGNATURE-----
Merge tag 'libnvdimm-for-4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm
Pull libnvdimm updates from Dan Williams:
"The bulk of this update was stabilized before the merge window and
appeared in -next. The "device dax" implementation was revised this
week in response to review feedback, and to address failures detected
by the recently expanded ndctl unit test suite.
Not included in this pull request are two dax topic branches (dax
error handling, and dax radix-tree locking). These topics were
deferred to get a few more days of -next integration testing, and to
coordinate a branch baseline with Ted and the ext4 tree. Vishal and
Ross will send the error handling and locking topics respectively in
the next few days.
This branch has received a positive build result from the kbuild robot
across 226 configs.
Summary:
- Device DAX for persistent memory: Device DAX is the device-centric
analogue of Filesystem DAX (CONFIG_FS_DAX). It allows memory
ranges to be allocated and mapped without need of an intervening
file system. Device DAX is strict, precise and predictable.
Specifically this interface:
a) Guarantees fault granularity with respect to a given page size
(pte, pmd, or pud) set at configuration time.
b) Enforces deterministic behavior by being strict about what
fault scenarios are supported.
Persistent memory is the first target, but the mechanism is also
targeted for exclusive allocations of performance/feature
differentiated memory ranges.
- Support for the HPE DSM (device specific method) command formats.
This enables management of these first generation devices until a
unified DSM specification materializes.
- Further ACPI 6.1 compliance with support for the common dimm
identifier format.
- Various fixes and cleanups across the subsystem"
* tag 'libnvdimm-for-4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm: (40 commits)
libnvdimm, dax: fix deletion
libnvdimm, dax: fix alignment validation
libnvdimm, dax: autodetect support
libnvdimm: release ida resources
Revert "block: enable dax for raw block devices"
/dev/dax, core: file operations and dax-mmap
/dev/dax, pmem: direct access to persistent memory
libnvdimm: stop requiring a driver ->remove() method
libnvdimm, dax: record the specified alignment of a dax-device instance
libnvdimm, dax: reserve space to store labels for device-dax
libnvdimm, dax: introduce device-dax infrastructure
nfit: add sysfs dimm 'family' and 'dsm_mask' attributes
tools/testing/nvdimm: ND_CMD_CALL support
nfit: disable vendor specific commands
nfit: export subsystem ids as attributes
nfit: fix format interface code byte order per ACPI6.1
nfit, libnvdimm: limited/whitelisted dimm command marshaling mechanism
nfit, libnvdimm: clarify "commands" vs "_DSMs"
libnvdimm: increase max envelope size for ioctl
acpi/nfit: Add sysfs "id" for NVDIMM ID
...
752 lines
19 KiB
C
752 lines
19 KiB
C
/*
|
|
* 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.
|
|
*
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/init.h>
|
|
#include <linux/types.h>
|
|
#include <linux/hardirq.h>
|
|
#include <linux/acpi.h>
|
|
#include <linux/dynamic_debug.h>
|
|
|
|
#include "internal.h"
|
|
#include "sleep.h"
|
|
|
|
#define _COMPONENT ACPI_BUS_COMPONENT
|
|
ACPI_MODULE_NAME("utils");
|
|
|
|
/* --------------------------------------------------------------------------
|
|
Object Evaluation Helpers
|
|
-------------------------------------------------------------------------- */
|
|
static void
|
|
acpi_util_eval_error(acpi_handle h, acpi_string p, acpi_status s)
|
|
{
|
|
#ifdef ACPI_DEBUG_OUTPUT
|
|
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)));
|
|
#else
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
acpi_status
|
|
acpi_extract_package(union acpi_object *package,
|
|
struct acpi_buffer *format, struct acpi_buffer *buffer)
|
|
{
|
|
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;
|
|
|
|
|
|
if (!package || (package->type != ACPI_TYPE_PACKAGE)
|
|
|| (package->package.count < 1)) {
|
|
printk(KERN_WARNING PREFIX "Invalid package argument\n");
|
|
return AE_BAD_PARAMETER;
|
|
}
|
|
|
|
if (!format || !format->pointer || (format->length < 1)) {
|
|
printk(KERN_WARNING PREFIX "Invalid format argument\n");
|
|
return AE_BAD_PARAMETER;
|
|
}
|
|
|
|
if (!buffer) {
|
|
printk(KERN_WARNING PREFIX "Invalid buffer argument\n");
|
|
return AE_BAD_PARAMETER;
|
|
}
|
|
|
|
format_count = (format->length / sizeof(char)) - 1;
|
|
if (format_count > package->package.count) {
|
|
printk(KERN_WARNING PREFIX "Format specifies more objects [%d]"
|
|
" than exist in package [%d].\n",
|
|
format_count, package->package.count);
|
|
return AE_BAD_DATA;
|
|
}
|
|
|
|
format_string = format->pointer;
|
|
|
|
/*
|
|
* Calculate size_required.
|
|
*/
|
|
for (i = 0; i < format_count; i++) {
|
|
|
|
union acpi_object *element = &(package->package.elements[i]);
|
|
|
|
switch (element->type) {
|
|
|
|
case ACPI_TYPE_INTEGER:
|
|
switch (format_string[i]) {
|
|
case 'N':
|
|
size_required += sizeof(u64);
|
|
tail_offset += sizeof(u64);
|
|
break;
|
|
case 'S':
|
|
size_required +=
|
|
sizeof(char *) + sizeof(u64) +
|
|
sizeof(char);
|
|
tail_offset += sizeof(char *);
|
|
break;
|
|
default:
|
|
printk(KERN_WARNING PREFIX "Invalid package element"
|
|
" [%d]: got number, expecting"
|
|
" [%c]\n",
|
|
i, format_string[i]);
|
|
return AE_BAD_DATA;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case ACPI_TYPE_STRING:
|
|
case ACPI_TYPE_BUFFER:
|
|
switch (format_string[i]) {
|
|
case 'S':
|
|
size_required +=
|
|
sizeof(char *) +
|
|
(element->string.length * sizeof(char)) +
|
|
sizeof(char);
|
|
tail_offset += sizeof(char *);
|
|
break;
|
|
case 'B':
|
|
size_required +=
|
|
sizeof(u8 *) + element->buffer.length;
|
|
tail_offset += sizeof(u8 *);
|
|
break;
|
|
default:
|
|
printk(KERN_WARNING PREFIX "Invalid package element"
|
|
" [%d] got string/buffer,"
|
|
" expecting [%c]\n",
|
|
i, format_string[i]);
|
|
return AE_BAD_DATA;
|
|
break;
|
|
}
|
|
break;
|
|
case ACPI_TYPE_LOCAL_REFERENCE:
|
|
switch (format_string[i]) {
|
|
case 'R':
|
|
size_required += sizeof(void *);
|
|
tail_offset += sizeof(void *);
|
|
break;
|
|
default:
|
|
printk(KERN_WARNING PREFIX "Invalid package element"
|
|
" [%d] got reference,"
|
|
" expecting [%c]\n",
|
|
i, format_string[i]);
|
|
return AE_BAD_DATA;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case ACPI_TYPE_PACKAGE:
|
|
default:
|
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
"Found unsupported element at index=%d\n",
|
|
i));
|
|
/* TBD: handle nested packages... */
|
|
return AE_SUPPORT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Validate output buffer.
|
|
*/
|
|
if (buffer->length == ACPI_ALLOCATE_BUFFER) {
|
|
buffer->pointer = ACPI_ALLOCATE_ZEROED(size_required);
|
|
if (!buffer->pointer)
|
|
return AE_NO_MEMORY;
|
|
buffer->length = size_required;
|
|
} else {
|
|
if (buffer->length < size_required) {
|
|
buffer->length = size_required;
|
|
return AE_BUFFER_OVERFLOW;
|
|
} else if (buffer->length != size_required ||
|
|
!buffer->pointer) {
|
|
return AE_BAD_PARAMETER;
|
|
}
|
|
}
|
|
|
|
head = buffer->pointer;
|
|
tail = buffer->pointer + tail_offset;
|
|
|
|
/*
|
|
* Extract package data.
|
|
*/
|
|
for (i = 0; i < format_count; i++) {
|
|
|
|
u8 **pointer = NULL;
|
|
union acpi_object *element = &(package->package.elements[i]);
|
|
|
|
switch (element->type) {
|
|
|
|
case ACPI_TYPE_INTEGER:
|
|
switch (format_string[i]) {
|
|
case 'N':
|
|
*((u64 *) head) =
|
|
element->integer.value;
|
|
head += sizeof(u64);
|
|
break;
|
|
case 'S':
|
|
pointer = (u8 **) head;
|
|
*pointer = tail;
|
|
*((u64 *) tail) =
|
|
element->integer.value;
|
|
head += sizeof(u64 *);
|
|
tail += sizeof(u64);
|
|
/* 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':
|
|
pointer = (u8 **) head;
|
|
*pointer = tail;
|
|
memcpy(tail, element->string.pointer,
|
|
element->string.length);
|
|
head += sizeof(char *);
|
|
tail += element->string.length * sizeof(char);
|
|
/* NULL terminate string */
|
|
*tail = (char)0;
|
|
tail += sizeof(char);
|
|
break;
|
|
case 'B':
|
|
pointer = (u8 **) head;
|
|
*pointer = tail;
|
|
memcpy(tail, element->buffer.pointer,
|
|
element->buffer.length);
|
|
head += sizeof(u8 *);
|
|
tail += element->buffer.length;
|
|
break;
|
|
default:
|
|
/* Should never get here */
|
|
break;
|
|
}
|
|
break;
|
|
case ACPI_TYPE_LOCAL_REFERENCE:
|
|
switch (format_string[i]) {
|
|
case 'R':
|
|
*(void **)head =
|
|
(void *)element->reference.handle;
|
|
head += sizeof(void *);
|
|
break;
|
|
default:
|
|
/* Should never get here */
|
|
break;
|
|
}
|
|
break;
|
|
case ACPI_TYPE_PACKAGE:
|
|
/* TBD: handle nested packages... */
|
|
default:
|
|
/* Should never get here */
|
|
break;
|
|
}
|
|
}
|
|
|
|
return AE_OK;
|
|
}
|
|
|
|
EXPORT_SYMBOL(acpi_extract_package);
|
|
|
|
acpi_status
|
|
acpi_evaluate_integer(acpi_handle handle,
|
|
acpi_string pathname,
|
|
struct acpi_object_list *arguments, unsigned long long *data)
|
|
{
|
|
acpi_status status = AE_OK;
|
|
union acpi_object element;
|
|
struct acpi_buffer buffer = { 0, NULL };
|
|
|
|
if (!data)
|
|
return AE_BAD_PARAMETER;
|
|
|
|
buffer.length = sizeof(union acpi_object);
|
|
buffer.pointer = &element;
|
|
status = acpi_evaluate_object(handle, pathname, arguments, &buffer);
|
|
if (ACPI_FAILURE(status)) {
|
|
acpi_util_eval_error(handle, pathname, status);
|
|
return status;
|
|
}
|
|
|
|
if (element.type != ACPI_TYPE_INTEGER) {
|
|
acpi_util_eval_error(handle, pathname, AE_BAD_DATA);
|
|
return AE_BAD_DATA;
|
|
}
|
|
|
|
*data = element.integer.value;
|
|
|
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Return value [%llu]\n", *data));
|
|
|
|
return AE_OK;
|
|
}
|
|
|
|
EXPORT_SYMBOL(acpi_evaluate_integer);
|
|
|
|
acpi_status
|
|
acpi_evaluate_reference(acpi_handle handle,
|
|
acpi_string pathname,
|
|
struct acpi_object_list *arguments,
|
|
struct acpi_handle_list *list)
|
|
{
|
|
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;
|
|
|
|
|
|
if (!list) {
|
|
return AE_BAD_PARAMETER;
|
|
}
|
|
|
|
/* Evaluate object. */
|
|
|
|
status = acpi_evaluate_object(handle, pathname, arguments, &buffer);
|
|
if (ACPI_FAILURE(status))
|
|
goto end;
|
|
|
|
package = buffer.pointer;
|
|
|
|
if ((buffer.length == 0) || !package) {
|
|
status = AE_BAD_DATA;
|
|
acpi_util_eval_error(handle, pathname, status);
|
|
goto end;
|
|
}
|
|
if (package->type != ACPI_TYPE_PACKAGE) {
|
|
status = AE_BAD_DATA;
|
|
acpi_util_eval_error(handle, pathname, status);
|
|
goto end;
|
|
}
|
|
if (!package->package.count) {
|
|
status = AE_BAD_DATA;
|
|
acpi_util_eval_error(handle, pathname, status);
|
|
goto end;
|
|
}
|
|
|
|
if (package->package.count > ACPI_MAX_HANDLES) {
|
|
return AE_NO_MEMORY;
|
|
}
|
|
list->count = package->package.count;
|
|
|
|
/* Extract package data. */
|
|
|
|
for (i = 0; i < list->count; i++) {
|
|
|
|
element = &(package->package.elements[i]);
|
|
|
|
if (element->type != ACPI_TYPE_LOCAL_REFERENCE) {
|
|
status = AE_BAD_DATA;
|
|
acpi_util_eval_error(handle, pathname, status);
|
|
break;
|
|
}
|
|
|
|
if (!element->reference.handle) {
|
|
status = AE_NULL_ENTRY;
|
|
acpi_util_eval_error(handle, pathname, status);
|
|
break;
|
|
}
|
|
/* Get the acpi_handle. */
|
|
|
|
list->handles[i] = element->reference.handle;
|
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found reference [%p]\n",
|
|
list->handles[i]));
|
|
}
|
|
|
|
end:
|
|
if (ACPI_FAILURE(status)) {
|
|
list->count = 0;
|
|
//kfree(list->handles);
|
|
}
|
|
|
|
kfree(buffer.pointer);
|
|
|
|
return status;
|
|
}
|
|
|
|
EXPORT_SYMBOL(acpi_evaluate_reference);
|
|
|
|
acpi_status
|
|
acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld)
|
|
{
|
|
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
|
|
|| output->package.elements[0].buffer.length < ACPI_PLD_REV1_BUFFER_SIZE) {
|
|
status = AE_TYPE;
|
|
goto out;
|
|
}
|
|
|
|
status = acpi_decode_pld_buffer(
|
|
output->package.elements[0].buffer.pointer,
|
|
output->package.elements[0].buffer.length,
|
|
pld);
|
|
|
|
out:
|
|
kfree(buffer.pointer);
|
|
return status;
|
|
}
|
|
EXPORT_SYMBOL(acpi_get_physical_device_location);
|
|
|
|
/**
|
|
* acpi_evaluate_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_ost(acpi_handle handle, u32 source_event, u32 status_code,
|
|
struct acpi_buffer *status_buf)
|
|
{
|
|
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};
|
|
|
|
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;
|
|
}
|
|
|
|
return acpi_evaluate_object(handle, "_OST", &arg_list, NULL);
|
|
}
|
|
EXPORT_SYMBOL(acpi_evaluate_ost);
|
|
|
|
/**
|
|
* acpi_handle_path: Return the object path of handle
|
|
*
|
|
* Caller must free the returned buffer
|
|
*/
|
|
static char *acpi_handle_path(acpi_handle handle)
|
|
{
|
|
struct acpi_buffer buffer = {
|
|
.length = ACPI_ALLOCATE_BUFFER,
|
|
.pointer = NULL
|
|
};
|
|
|
|
if (in_interrupt() ||
|
|
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer) != AE_OK)
|
|
return NULL;
|
|
return buffer.pointer;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
const char *path;
|
|
|
|
va_start(args, fmt);
|
|
vaf.fmt = fmt;
|
|
vaf.va = &args;
|
|
|
|
path = acpi_handle_path(handle);
|
|
printk("%sACPI: %s: %pV", level, path ? path : "<n/a>" , &vaf);
|
|
|
|
va_end(args);
|
|
kfree(path);
|
|
}
|
|
EXPORT_SYMBOL(acpi_handle_printk);
|
|
|
|
#if defined(CONFIG_DYNAMIC_DEBUG)
|
|
/**
|
|
* __acpi_handle_debug: pr_debug with ACPI prefix and object path
|
|
*
|
|
* This function is called through acpi_handle_debug macro and debug
|
|
* 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_debug(struct _ddebug *descriptor, acpi_handle handle,
|
|
const char *fmt, ...)
|
|
{
|
|
struct va_format vaf;
|
|
va_list args;
|
|
const char *path;
|
|
|
|
va_start(args, fmt);
|
|
vaf.fmt = fmt;
|
|
vaf.va = &args;
|
|
|
|
path = acpi_handle_path(handle);
|
|
__dynamic_pr_debug(descriptor, "ACPI: %s: %pV", path ? path : "<n/a>", &vaf);
|
|
|
|
va_end(args);
|
|
kfree(path);
|
|
}
|
|
EXPORT_SYMBOL(__acpi_handle_debug);
|
|
#endif
|
|
|
|
/**
|
|
* 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);
|
|
|
|
acpi_status acpi_execute_simple_method(acpi_handle handle, char *method,
|
|
u64 arg)
|
|
{
|
|
union acpi_object obj = { .type = ACPI_TYPE_INTEGER };
|
|
struct acpi_object_list arg_list = { .count = 1, .pointer = &obj, };
|
|
|
|
obj.integer.value = arg;
|
|
|
|
return acpi_evaluate_object(handle, method, &arg_list, NULL);
|
|
}
|
|
EXPORT_SYMBOL(acpi_execute_simple_method);
|
|
|
|
/**
|
|
* acpi_evaluate_ej0: Evaluate _EJ0 method for hotplug operations
|
|
* @handle: ACPI device handle
|
|
*
|
|
* Evaluate device's _EJ0 method for hotplug operations.
|
|
*/
|
|
acpi_status acpi_evaluate_ej0(acpi_handle handle)
|
|
{
|
|
acpi_status status;
|
|
|
|
status = acpi_execute_simple_method(handle, "_EJ0", 1);
|
|
if (status == AE_NOT_FOUND)
|
|
acpi_handle_warn(handle, "No _EJ0 support for device\n");
|
|
else if (ACPI_FAILURE(status))
|
|
acpi_handle_warn(handle, "Eject failed (0x%x)\n", status);
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* acpi_evaluate_lck: Evaluate _LCK method to lock/unlock device
|
|
* @handle: ACPI device handle
|
|
* @lock: lock device if non-zero, otherwise unlock device
|
|
*
|
|
* Evaluate device's _LCK method if present to lock/unlock device
|
|
*/
|
|
acpi_status acpi_evaluate_lck(acpi_handle handle, int lock)
|
|
{
|
|
acpi_status status;
|
|
|
|
status = acpi_execute_simple_method(handle, "_LCK", !!lock);
|
|
if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
|
|
if (lock)
|
|
acpi_handle_warn(handle,
|
|
"Locking device failed (0x%x)\n", status);
|
|
else
|
|
acpi_handle_warn(handle,
|
|
"Unlocking device failed (0x%x)\n", status);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* acpi_evaluate_dsm - evaluate device's _DSM method
|
|
* @handle: ACPI device handle
|
|
* @uuid: UUID of requested functions, should be 16 bytes
|
|
* @rev: revision number of requested function
|
|
* @func: requested function number
|
|
* @argv4: the function specific parameter
|
|
*
|
|
* Evaluate device's _DSM method with specified UUID, revision id and
|
|
* function number. Caller needs to free the returned object.
|
|
*
|
|
* Though ACPI defines the fourth parameter for _DSM should be a package,
|
|
* some old BIOSes do expect a buffer or an integer etc.
|
|
*/
|
|
union acpi_object *
|
|
acpi_evaluate_dsm(acpi_handle handle, const u8 *uuid, u64 rev, u64 func,
|
|
union acpi_object *argv4)
|
|
{
|
|
acpi_status ret;
|
|
struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
|
|
union acpi_object params[4];
|
|
struct acpi_object_list input = {
|
|
.count = 4,
|
|
.pointer = params,
|
|
};
|
|
|
|
params[0].type = ACPI_TYPE_BUFFER;
|
|
params[0].buffer.length = 16;
|
|
params[0].buffer.pointer = (char *)uuid;
|
|
params[1].type = ACPI_TYPE_INTEGER;
|
|
params[1].integer.value = rev;
|
|
params[2].type = ACPI_TYPE_INTEGER;
|
|
params[2].integer.value = func;
|
|
if (argv4) {
|
|
params[3] = *argv4;
|
|
} else {
|
|
params[3].type = ACPI_TYPE_PACKAGE;
|
|
params[3].package.count = 0;
|
|
params[3].package.elements = NULL;
|
|
}
|
|
|
|
ret = acpi_evaluate_object(handle, "_DSM", &input, &buf);
|
|
if (ACPI_SUCCESS(ret))
|
|
return (union acpi_object *)buf.pointer;
|
|
|
|
if (ret != AE_NOT_FOUND)
|
|
acpi_handle_warn(handle,
|
|
"failed to evaluate _DSM (0x%x)\n", ret);
|
|
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL(acpi_evaluate_dsm);
|
|
|
|
/**
|
|
* acpi_check_dsm - check if _DSM method supports requested functions.
|
|
* @handle: ACPI device handle
|
|
* @uuid: UUID of requested functions, should be 16 bytes at least
|
|
* @rev: revision number of requested functions
|
|
* @funcs: bitmap of requested functions
|
|
*
|
|
* Evaluate device's _DSM method to check whether it supports requested
|
|
* functions. Currently only support 64 functions at maximum, should be
|
|
* enough for now.
|
|
*/
|
|
bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, u64 rev, u64 funcs)
|
|
{
|
|
int i;
|
|
u64 mask = 0;
|
|
union acpi_object *obj;
|
|
|
|
if (funcs == 0)
|
|
return false;
|
|
|
|
obj = acpi_evaluate_dsm(handle, uuid, rev, 0, NULL);
|
|
if (!obj)
|
|
return false;
|
|
|
|
/* For compatibility, old BIOSes may return an integer */
|
|
if (obj->type == ACPI_TYPE_INTEGER)
|
|
mask = obj->integer.value;
|
|
else if (obj->type == ACPI_TYPE_BUFFER)
|
|
for (i = 0; i < obj->buffer.length && i < 8; i++)
|
|
mask |= (((u64)obj->buffer.pointer[i]) << (i * 8));
|
|
ACPI_FREE(obj);
|
|
|
|
/*
|
|
* Bit 0 indicates whether there's support for any functions other than
|
|
* function 0 for the specified UUID and revision.
|
|
*/
|
|
if ((mask & 0x1) && (mask & funcs) == funcs)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
EXPORT_SYMBOL(acpi_check_dsm);
|
|
|
|
/**
|
|
* acpi_dev_found - Detect presence of a given ACPI device in the namespace.
|
|
* @hid: Hardware ID of the device.
|
|
*
|
|
* Return %true if the device was present at the moment of invocation.
|
|
* Note that if the device is pluggable, it may since have disappeared.
|
|
*
|
|
* For this function to work, acpi_bus_scan() must have been executed
|
|
* which happens in the subsys_initcall() subsection. Hence, do not
|
|
* call from a subsys_initcall() or earlier (use acpi_get_devices()
|
|
* instead). Calling from module_init() is fine (which is synonymous
|
|
* with device_initcall()).
|
|
*/
|
|
bool acpi_dev_found(const char *hid)
|
|
{
|
|
struct acpi_device_bus_id *acpi_device_bus_id;
|
|
bool found = false;
|
|
|
|
mutex_lock(&acpi_device_lock);
|
|
list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node)
|
|
if (!strcmp(acpi_device_bus_id->bus_id, hid)) {
|
|
found = true;
|
|
break;
|
|
}
|
|
mutex_unlock(&acpi_device_lock);
|
|
|
|
return found;
|
|
}
|
|
EXPORT_SYMBOL(acpi_dev_found);
|
|
|
|
/*
|
|
* acpi_backlight= handling, this is done here rather then in video_detect.c
|
|
* because __setup cannot be used in modules.
|
|
*/
|
|
char acpi_video_backlight_string[16];
|
|
EXPORT_SYMBOL(acpi_video_backlight_string);
|
|
|
|
static int __init acpi_backlight(char *str)
|
|
{
|
|
strlcpy(acpi_video_backlight_string, str,
|
|
sizeof(acpi_video_backlight_string));
|
|
return 1;
|
|
}
|
|
__setup("acpi_backlight=", acpi_backlight);
|