* Copyright 2009, Vincent Duvert, vincent.duvert@free.fr
* Copyright 2009, Clemens Zeidler, haiku@clemens-zeidler.de
* Copyright 2008-2015, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2006, Bryan Varner. All rights reserved.
* Copyright 2005, Nathan Whitehorn. All rights reserved.
*
* Distributed under the terms of the MIT License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ACPI.h>
#include <dpc.h>
#include <KernelExport.h>
#include <PCI.h>
#include <safemode.h>
extern "C" {
#include "acpi.h"
#include "accommon.h"
#include "acdisasm.h"
#include "acnamesp.h"
}
#include "ACPIPrivate.h"
#include "arch_init.h"
#ifdef TRACE_ACPI_BUS
#define TRACE(x...) dprintf("acpi: " x)
#else
#define TRACE(x...)
#endif
#define ERROR(x...) dprintf("acpi: " x)
#define ACPI_DEVICE_ID_LENGTH 0x08
extern dpc_module_info* gDPC;
void* gDPCHandle = NULL;
static bool
checkAndLogFailure(const ACPI_STATUS status, const char* msg)
{
bool failure = ACPI_FAILURE(status);
if (failure)
dprintf("acpi: %s %s\n", msg, AcpiFormatException(status));
return failure;
}
static ACPI_STATUS
get_device_by_hid_callback(ACPI_HANDLE object, UINT32 depth, void* context,
void** _returnValue)
{
uint32* counter = (uint32*)context;
ACPI_BUFFER buffer;
TRACE("get_device_by_hid_callback %p, %d, %p\n", object, depth, context);
*_returnValue = NULL;
if (counter[0] == counter[1]) {
buffer.Length = 254;
buffer.Pointer = malloc(255);
if (checkAndLogFailure(AcpiGetName(object, ACPI_FULL_PATHNAME, &buffer),
"Failed to find device")) {
free(buffer.Pointer);
return AE_CTRL_TERMINATE;
}
((char*)buffer.Pointer)[buffer.Length] = '\0';
*_returnValue = buffer.Pointer;
return AE_CTRL_TERMINATE;
}
counter[1]++;
return AE_OK;
}
#ifdef ACPI_DEBUG_OUTPUT
static void
globalGPEHandler(UINT32 eventType, ACPI_HANDLE device, UINT32 eventNumber,
void* context)
{
ACPI_BUFFER path;
char deviceName[256];
path.Length = sizeof(deviceName);
path.Pointer = deviceName;
ACPI_STATUS status = AcpiNsHandleToPathname(device, &path);
if (ACPI_FAILURE(status))
strcpy(deviceName, "(missing)");
switch (eventType) {
case ACPI_EVENT_TYPE_GPE:
dprintf("acpi: GPE Event %d for %s\n", eventNumber, deviceName);
break;
case ACPI_EVENT_TYPE_FIXED:
{
switch (eventNumber) {
case ACPI_EVENT_PMTIMER:
dprintf("acpi: PMTIMER(%d) event for %s\n", eventNumber,
deviceName);
break;
case ACPI_EVENT_GLOBAL:
dprintf("acpi: Global(%d) event for %s\n", eventNumber,
deviceName);
break;
case ACPI_EVENT_POWER_BUTTON:
dprintf("acpi: Powerbutton(%d) event for %s\n", eventNumber,
deviceName);
break;
case ACPI_EVENT_SLEEP_BUTTON:
dprintf("acpi: sleepbutton(%d) event for %s\n", eventNumber,
deviceName);
break;
case ACPI_EVENT_RTC:
dprintf("acpi: RTC(%d) event for %s\n", eventNumber,
deviceName);
break;
default:
dprintf("acpi: unknown fixed(%d) event for %s\n",
eventNumber, deviceName);
}
break;
}
default:
dprintf("acpi: unknown event type (%d:%d) event for %s\n",
eventType, eventNumber, deviceName);
}
}
static void globalNotifyHandler(ACPI_HANDLE device, UINT32 value, void* context)
{
ACPI_BUFFER path;
char deviceName[256];
path.Length = sizeof(deviceName);
path.Pointer = deviceName;
ACPI_STATUS status = AcpiNsHandleToPathname(device, &path);
if (ACPI_FAILURE(status))
strcpy(deviceName, "(missing)");
dprintf("acpi: Notify event %d for %s\n", value, deviceName);
}
#endif
static status_t
acpi_std_ops(int32 op,...)
{
switch (op) {
case B_MODULE_INIT:
{
void *settings;
bool acpiDisabled = false;
AcpiGbl_CopyDsdtLocally = true;
settings = load_driver_settings("kernel");
if (settings != NULL) {
acpiDisabled = !get_driver_boolean_parameter(settings, "acpi",
true, true);
unload_driver_settings(settings);
}
if (!acpiDisabled) {
settings = load_driver_settings(B_SAFEMODE_DRIVER_SETTINGS);
if (settings != NULL) {
acpiDisabled = get_driver_boolean_parameter(settings,
B_SAFEMODE_DISABLE_ACPI, false, false);
unload_driver_settings(settings);
}
}
if (acpiDisabled) {
ERROR("ACPI disabled\n");
return ENOSYS;
}
if (gDPC->new_dpc_queue(&gDPCHandle, "acpi_task",
B_URGENT_DISPLAY_PRIORITY + 1) != B_OK) {
ERROR("failed to create os execution queue\n");
return B_ERROR;
}
#ifdef ACPI_DEBUG_OUTPUT
AcpiDbgLevel = ACPI_DEBUG_ALL | ACPI_LV_VERBOSE;
AcpiDbgLayer = ACPI_ALL_COMPONENTS;
#endif
if (checkAndLogFailure(AcpiInitializeSubsystem(),
"AcpiInitializeSubsystem failed"))
goto err_dpc;
if (checkAndLogFailure(AcpiInitializeTables(NULL, 0, TRUE),
"AcpiInitializeTables failed"))
goto err_acpi;
if (checkAndLogFailure(AcpiLoadTables(),
"AcpiLoadTables failed"))
goto err_acpi;
arch_init_interrupt_controller();
if (checkAndLogFailure(AcpiEnableSubsystem(
ACPI_FULL_INITIALIZATION),
"AcpiEnableSubsystem failed"))
goto err_acpi;
if (checkAndLogFailure(AcpiInitializeObjects(
ACPI_FULL_INITIALIZATION),
"AcpiInitializeObjects failed"))
goto err_acpi;
#ifdef ACPI_DEBUG_OUTPUT
checkAndLogFailure(
AcpiInstallGlobalEventHandler(globalGPEHandler, NULL),
"Failed to install global GPE-handler.");
checkAndLogFailure(AcpiInstallNotifyHandler(ACPI_ROOT_OBJECT,
ACPI_ALL_NOTIFY, globalNotifyHandler, NULL),
"Failed to install global Notify-handler.");
#endif
checkAndLogFailure(AcpiEnableAllRuntimeGpes(),
"Failed to enable all runtime Gpes");
checkAndLogFailure(AcpiUpdateAllGpes(),
"Failed to update all Gpes");
TRACE("ACPI initialized\n");
return B_OK;
err_acpi:
checkAndLogFailure(AcpiTerminate(), "AcpiTerminate failed");
err_dpc:
gDPC->delete_dpc_queue(gDPCHandle);
gDPCHandle = NULL;
return B_ERROR;
}
case B_MODULE_UNINIT:
{
checkAndLogFailure(AcpiTerminate(),
"Could not bring system out of ACPI mode. Oh well.");
gDPC->delete_dpc_queue(gDPCHandle);
gDPCHandle = NULL;
break;
}
default:
return B_ERROR;
}
return B_OK;
}
status_t
get_handle(acpi_handle parent, const char *pathname, acpi_handle *retHandle)
{
return AcpiGetHandle(parent, (ACPI_STRING)pathname, retHandle) == AE_OK
? B_OK : B_ERROR;
}
status_t
get_name(acpi_handle handle, uint32 nameType, char* returnedName,
size_t bufferLength)
{
ACPI_BUFFER buffer = {bufferLength, (void*)returnedName};
return AcpiGetName(handle, nameType, &buffer) == AE_OK ? B_OK : B_ERROR;
}
status_t
acquire_global_lock(uint16 timeout, uint32 *handle)
{
return AcpiAcquireGlobalLock(timeout, (UINT32*)handle) == AE_OK
? B_OK : B_ERROR;
}
status_t
release_global_lock(uint32 handle)
{
return AcpiReleaseGlobalLock(handle) == AE_OK ? B_OK : B_ERROR;
}
status_t
install_notify_handler(acpi_handle device, uint32 handlerType,
acpi_notify_handler handler, void *context)
{
return AcpiInstallNotifyHandler(device, handlerType,
(ACPI_NOTIFY_HANDLER)handler, context) == AE_OK ? B_OK : B_ERROR;
}
status_t
remove_notify_handler(acpi_handle device, uint32 handlerType,
acpi_notify_handler handler)
{
return AcpiRemoveNotifyHandler(device, handlerType,
(ACPI_NOTIFY_HANDLER)handler) == AE_OK ? B_OK : B_ERROR;
}
status_t
update_all_gpes()
{
return AcpiUpdateAllGpes() == AE_OK ? B_OK : B_ERROR;
}
status_t
enable_gpe(acpi_handle handle, uint32 gpeNumber)
{
return AcpiEnableGpe(handle, gpeNumber) == AE_OK ? B_OK : B_ERROR;
}
status_t
disable_gpe(acpi_handle handle, uint32 gpeNumber)
{
return AcpiDisableGpe(handle, gpeNumber) == AE_OK ? B_OK : B_ERROR;
}
status_t
clear_gpe(acpi_handle handle, uint32 gpeNumber)
{
return AcpiClearGpe(handle, gpeNumber) == AE_OK ? B_OK : B_ERROR;
}
status_t
set_gpe(acpi_handle handle, uint32 gpeNumber, uint8 action)
{
return AcpiSetGpe(handle, gpeNumber, action) == AE_OK ? B_OK : B_ERROR;
}
status_t
finish_gpe(acpi_handle handle, uint32 gpeNumber)
{
return AcpiFinishGpe(handle, gpeNumber) == AE_OK ? B_OK : B_ERROR;
}
status_t
install_gpe_handler(acpi_handle handle, uint32 gpeNumber, uint32 type,
acpi_gpe_handler handler, void *data)
{
return AcpiInstallGpeHandler(handle, gpeNumber, type,
(ACPI_GPE_HANDLER)handler, data) == AE_OK ? B_OK : B_ERROR;
}
status_t
remove_gpe_handler(acpi_handle handle, uint32 gpeNumber,
acpi_gpe_handler address)
{
return AcpiRemoveGpeHandler(handle, gpeNumber, (ACPI_GPE_HANDLER)address)
== AE_OK ? B_OK : B_ERROR;
}
status_t
install_address_space_handler(acpi_handle handle, uint32 spaceId,
acpi_adr_space_handler handler, acpi_adr_space_setup setup, void *data)
{
return AcpiInstallAddressSpaceHandler(handle, spaceId,
(ACPI_ADR_SPACE_HANDLER)handler, (ACPI_ADR_SPACE_SETUP)setup, data)
== AE_OK ? B_OK : B_ERROR;
}
status_t
remove_address_space_handler(acpi_handle handle, uint32 spaceId,
acpi_adr_space_handler handler)
{
return AcpiRemoveAddressSpaceHandler(handle, spaceId,
(ACPI_ADR_SPACE_HANDLER)handler) == AE_OK ? B_OK : B_ERROR;
}
void
enable_fixed_event(uint32 event)
{
AcpiEnableEvent(event, 0);
}
void
disable_fixed_event(uint32 event)
{
AcpiDisableEvent(event, 0);
}
uint32
fixed_event_status(uint32 event)
{
ACPI_EVENT_STATUS status = 0;
AcpiGetEventStatus(event, &status);
return status;
}
void
reset_fixed_event(uint32 event)
{
AcpiClearEvent(event);
}
status_t
install_fixed_event_handler(uint32 event, acpi_event_handler handler,
void *data)
{
return AcpiInstallFixedEventHandler(event, (ACPI_EVENT_HANDLER)handler, data) == AE_OK
? B_OK : B_ERROR;
}
status_t
remove_fixed_event_handler(uint32 event, acpi_event_handler handler)
{
return AcpiRemoveFixedEventHandler(event, (ACPI_EVENT_HANDLER)handler) == AE_OK
? B_OK : B_ERROR;
}
status_t
get_next_entry(uint32 objectType, const char *base, char *result,
size_t length, void **counter)
{
ACPI_HANDLE parent, child, newChild;
ACPI_BUFFER buffer;
ACPI_STATUS status;
TRACE("get_next_entry %ld, %s\n", objectType, base);
if (base == NULL || !strcmp(base, "\\")) {
parent = ACPI_ROOT_OBJECT;
} else {
status = AcpiGetHandle(NULL, (ACPI_STRING)base, &parent);
if (status != AE_OK)
return B_ENTRY_NOT_FOUND;
}
child = *counter;
status = AcpiGetNextObject(objectType, parent, child, &newChild);
if (status != AE_OK)
return B_ENTRY_NOT_FOUND;
*counter = newChild;
buffer.Length = length;
buffer.Pointer = result;
status = AcpiGetName(newChild, ACPI_FULL_PATHNAME, &buffer);
if (status != AE_OK)
return B_NO_MEMORY;
return B_OK;
}
status_t
get_next_object(uint32 objectType, acpi_handle parent,
acpi_handle* currentChild)
{
acpi_handle child = *currentChild;
return AcpiGetNextObject(objectType, parent, child, currentChild) == AE_OK
? B_OK : B_ERROR;
}
status_t
get_device(const char* hid, uint32 index, char* result, size_t resultLength)
{
ACPI_STATUS status;
uint32 counter[2] = {index, 0};
char *buffer = NULL;
TRACE("get_device %s, index %ld\n", hid, index);
status = AcpiGetDevices((ACPI_STRING)hid, (ACPI_WALK_CALLBACK)&get_device_by_hid_callback,
counter, (void**)&buffer);
if (status != AE_OK || buffer == NULL)
return B_ENTRY_NOT_FOUND;
strlcpy(result, buffer, resultLength);
free(buffer);
return B_OK;
}
status_t
get_device_info(const char *path, char** hid, char** cidList,
size_t cidListCount, char** uid, char** cls)
{
ACPI_HANDLE handle;
ACPI_DEVICE_INFO *info;
TRACE("get_device_info: path %s\n", path);
if (AcpiGetHandle(NULL, (ACPI_STRING)path, &handle) != AE_OK)
return B_ENTRY_NOT_FOUND;
if (AcpiGetObjectInfo(handle, &info) != AE_OK)
return B_BAD_TYPE;
if ((info->Valid & ACPI_VALID_HID) != 0 && hid != NULL)
*hid = strndup(info->HardwareId.String, info->HardwareId.Length);
if ((info->Valid & ACPI_VALID_CID) != 0 && cidList != NULL) {
if (cidListCount > info->CompatibleIdList.Count)
cidListCount = info->CompatibleIdList.Count;
for (size_t i = 0; i < cidListCount; i++) {
cidList[i] = strndup(info->CompatibleIdList.Ids[i].String,
info->CompatibleIdList.Ids[i].Length);
}
}
if ((info->Valid & ACPI_VALID_UID) != 0 && uid != NULL)
*uid = strndup(info->UniqueId.String, info->UniqueId.Length);
if ((info->Valid & ACPI_VALID_CLS) != 0 && cls != NULL
&& info->ClassCode.Length >= ACPI_PCICLS_STRING_SIZE) {
*cls = strndup(info->ClassCode.String, info->ClassCode.Length);
}
AcpiOsFree(info);
return B_OK;
}
status_t
get_device_addr(const char *path, uint32 *addr)
{
ACPI_HANDLE handle;
TRACE("get_device_adr: path %s, hid %s\n", path, hid);
if (AcpiGetHandle(NULL, (ACPI_STRING)path, &handle) != AE_OK)
return B_ENTRY_NOT_FOUND;
status_t status = B_BAD_VALUE;
acpi_data buf;
acpi_object_type object;
buf.pointer = &object;
buf.length = sizeof(acpi_object_type);
if (addr != NULL
&& evaluate_method(handle, "_ADR", NULL, &buf) == B_OK
&& object.object_type == ACPI_TYPE_INTEGER) {
status = B_OK;
*addr = object.integer.integer;
}
return status;
}
uint32
get_object_type(const char* path)
{
ACPI_HANDLE handle;
ACPI_OBJECT_TYPE type;
if (AcpiGetHandle(NULL, (ACPI_STRING)path, &handle) != AE_OK)
return B_ENTRY_NOT_FOUND;
AcpiGetType(handle, &type);
return type;
}
status_t
get_object(const char* path, acpi_object_type** _returnValue)
{
ACPI_HANDLE handle;
ACPI_BUFFER buffer;
ACPI_STATUS status;
status = AcpiGetHandle(NULL, (ACPI_STRING)path, &handle);
if (status != AE_OK)
return B_ENTRY_NOT_FOUND;
buffer.Pointer = NULL;
buffer.Length = ACPI_ALLOCATE_BUFFER;
status = AcpiEvaluateObject(handle, NULL, NULL, &buffer);
*_returnValue = (acpi_object_type*)buffer.Pointer;
return status == AE_OK ? B_OK : B_ERROR;
}
status_t
get_object_typed(const char* path, acpi_object_type** _returnValue,
uint32 objectType)
{
ACPI_HANDLE handle;
ACPI_BUFFER buffer;
ACPI_STATUS status;
status = AcpiGetHandle(NULL, (ACPI_STRING)path, &handle);
if (status != AE_OK)
return B_ENTRY_NOT_FOUND;
buffer.Pointer = NULL;
buffer.Length = ACPI_ALLOCATE_BUFFER;
status = AcpiEvaluateObjectTyped(handle, NULL, NULL, &buffer, objectType);
*_returnValue = (acpi_object_type*)buffer.Pointer;
return status == AE_OK ? B_OK : B_ERROR;
}
status_t
ns_handle_to_pathname(acpi_handle targetHandle, acpi_data *buffer)
{
status_t status = AcpiNsHandleToPathname(targetHandle,
(ACPI_BUFFER*)buffer, false);
return status == AE_OK ? B_OK : B_ERROR;
}
status_t
evaluate_object(acpi_handle handle, const char* object, acpi_objects *args,
acpi_object_type* returnValue, size_t bufferLength)
{
ACPI_BUFFER buffer;
ACPI_STATUS status;
buffer.Pointer = returnValue;
buffer.Length = bufferLength;
status = AcpiEvaluateObject(handle, (ACPI_STRING)object,
(ACPI_OBJECT_LIST*)args, returnValue != NULL ? &buffer : NULL);
if (status == AE_BUFFER_OVERFLOW)
dprintf("evaluate_object: the passed buffer is too small!\n");
return status == AE_OK ? B_OK : B_ERROR;
}
status_t
evaluate_method(acpi_handle handle, const char* method,
acpi_objects *args, acpi_data *returnValue)
{
ACPI_STATUS status;
status = AcpiEvaluateObject(handle, (ACPI_STRING)method,
(ACPI_OBJECT_LIST*)args, (ACPI_BUFFER*)returnValue);
if (status == AE_BUFFER_OVERFLOW)
dprintf("evaluate_method: the passed buffer is too small!\n");
return status == AE_OK ? B_OK : B_ERROR;
}
status_t
get_irq_routing_table(acpi_handle busDeviceHandle, acpi_data *retBuffer)
{
ACPI_STATUS status;
status = AcpiGetIrqRoutingTable(busDeviceHandle, (ACPI_BUFFER*)retBuffer);
if (status == AE_BUFFER_OVERFLOW)
dprintf("get_irq_routing_table: the passed buffer is too small!\n");
return status == AE_OK ? B_OK : B_ERROR;
}
status_t
get_current_resources(acpi_handle busDeviceHandle, acpi_data *retBuffer)
{
return AcpiGetCurrentResources(busDeviceHandle, (ACPI_BUFFER*)retBuffer)
== AE_OK ? B_OK : B_ERROR;
}
status_t
get_possible_resources(acpi_handle busDeviceHandle, acpi_data *retBuffer)
{
return AcpiGetPossibleResources(busDeviceHandle, (ACPI_BUFFER*)retBuffer)
== AE_OK ? B_OK : B_ERROR;
}
status_t
set_current_resources(acpi_handle busDeviceHandle, acpi_data *buffer)
{
return AcpiSetCurrentResources(busDeviceHandle, (ACPI_BUFFER*)buffer)
== AE_OK ? B_OK : B_ERROR;
}
status_t
walk_resources(acpi_handle busDeviceHandle, char* method,
acpi_walk_resources_callback callback, void* context)
{
return AcpiWalkResources(busDeviceHandle, method,
(ACPI_WALK_RESOURCE_CALLBACK)callback, context);
}
status_t
walk_namespace(acpi_handle busDeviceHandle, uint32 objectType,
uint32 maxDepth, acpi_walk_callback descendingCallback,
acpi_walk_callback ascendingCallback, void* context, void** returnValue)
{
return AcpiWalkNamespace(objectType, busDeviceHandle, maxDepth,
(ACPI_WALK_CALLBACK)descendingCallback,
(ACPI_WALK_CALLBACK)ascendingCallback, context, returnValue);
}
status_t
prepare_sleep_state(uint8 state, void (*wakeFunc)(void), size_t size)
{
ACPI_STATUS acpiStatus;
TRACE("prepare_sleep_state %d, %p, %ld\n", state, wakeFunc, size);
if (state != ACPI_POWER_STATE_OFF) {
physical_entry wakeVector;
status_t status;
status = get_memory_map((const void*)wakeFunc, size, &wakeVector, 1);
if (status != B_OK)
return status;
# if B_HAIKU_PHYSICAL_BITS > 32
if (wakeVector.address >= 0x100000000LL) {
ERROR("prepare_sleep_state(): ACPI 2.0c says use 32 bit "
"vector, but we have a physical address >= 4 GB\n");
}
# endif
acpiStatus = AcpiSetFirmwareWakingVector(wakeVector.address,
wakeVector.address);
if (acpiStatus != AE_OK)
return B_ERROR;
}
acpiStatus = AcpiEnterSleepStatePrep(state);
if (acpiStatus != AE_OK)
return B_ERROR;
return B_OK;
}
status_t
enter_sleep_state(uint8 state)
{
ACPI_STATUS status;
TRACE("enter_sleep_state %d\n", state);
cpu_status cpu = disable_interrupts();
status = AcpiEnterSleepState(state);
restore_interrupts(cpu);
panic("AcpiEnterSleepState should not return.");
if (status != AE_OK)
return B_ERROR;
if (status != AE_OK)
return B_ERROR;*/
return B_OK;
}
status_t
reboot(void)
{
ACPI_STATUS status;
TRACE("reboot\n");
status = AcpiReset();
if (status == AE_NOT_EXIST)
return B_UNSUPPORTED;
if (status != AE_OK) {
ERROR("Reset failed, status = %d\n", status);
return B_ERROR;
}
snooze(1000000);
ERROR("Reset failed, timeout\n");
return B_ERROR;
}
status_t
get_table(const char* signature, uint32 instance, void** tableHeader)
{
return AcpiGetTable((char*)signature, instance,
(ACPI_TABLE_HEADER**)tableHeader) == AE_OK ? B_OK : B_ERROR;
}
status_t
read_bit_register(uint32 regid, uint32 *val)
{
return AcpiReadBitRegister(regid, (UINT32 *)val);
}
status_t
write_bit_register(uint32 regid, uint32 val)
{
return AcpiWriteBitRegister(regid, val);
}
struct acpi_module_info gACPIModule = {
{
B_ACPI_MODULE_NAME,
B_KEEP_LOADED,
acpi_std_ops
},
get_handle,
get_name,
acquire_global_lock,
release_global_lock,
install_notify_handler,
remove_notify_handler,
update_all_gpes,
enable_gpe,
disable_gpe,
clear_gpe,
set_gpe,
finish_gpe,
install_gpe_handler,
remove_gpe_handler,
install_address_space_handler,
remove_address_space_handler,
enable_fixed_event,
disable_fixed_event,
fixed_event_status,
reset_fixed_event,
install_fixed_event_handler,
remove_fixed_event_handler,
get_next_entry,
get_next_object,
walk_namespace,
get_device,
get_device_info,
get_object_type,
get_object,
get_object_typed,
ns_handle_to_pathname,
evaluate_object,
evaluate_method,
get_irq_routing_table,
get_current_resources,
get_possible_resources,
set_current_resources,
walk_resources,
prepare_sleep_state,
enter_sleep_state,
reboot,
get_table,
read_bit_register,
write_bit_register
};