* Copyright 2022, JΓ©rΓ΄me Duval. All rights reserved.
*
* Distributed under the terms of the MIT License.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <Drivers.h>
#include <KernelExport.h>
#include <OS.h>
#include "ACPIPrivate.h"
#define TRACE(x...)
#define TRACE_ALWAYS(x...) dprintf("acpi_call: " x)
#define CALLED() TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
typedef struct {
device_node *node;
acpi_root_info *acpi;
void *acpi_cookie;
} acpi_call_device_info;
struct acpi_call_desc
{
char* path;
acpi_objects args;
acpi_status retval;
acpi_data result;
acpi_size reslen;
};
static status_t
acpi_call_init_device(void* _node, void** _cookie)
{
CALLED();
device_node *node = (device_node *)_node;
acpi_call_device_info* device = (acpi_call_device_info*)calloc(1, sizeof(acpi_call_device_info));
if (device == NULL)
return B_NO_MEMORY;
device->node = node;
status_t err = gDeviceManager->get_driver(node, (driver_module_info **)&device->acpi,
(void **)&device->acpi_cookie);
if (err != B_OK) {
free(device);
return err;
}
*_cookie = device;
return err;
}
static void
acpi_call_uninit_device(void* _cookie)
{
CALLED();
acpi_call_device_info* device = (acpi_call_device_info*)_cookie;
free(device);
}
static status_t
acpi_call_open(void* _device, const char* path, int openMode, void** _cookie)
{
CALLED();
acpi_call_device_info* device = (acpi_call_device_info*)_device;
*_cookie = device;
return B_OK;
}
static status_t
acpi_call_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
{
TRACE("read(%p, %" B_PRIdOFF", %p, %lu)\n", cookie, position, buffer, *numBytes);
return B_ERROR;
}
static status_t
acpi_call_write(void *cookie, off_t position, const void *buffer,
size_t *numBytes)
{
TRACE("write(%p, %" B_PRIdOFF", %p, %lu)\n", cookie, position, buffer, *numBytes);
return B_ERROR;
}
void
acpi_call_fixup_pointers(acpi_object_type *p, void *target)
{
CALLED();
switch (p->object_type)
{
case ACPI_TYPE_STRING:
p->string.string = (char*)((uint8*)(p->string.string) - (uint8*)p + (uint8*)target);
break;
case ACPI_TYPE_BUFFER:
p->buffer.buffer = (void*)((uint8*)(p->buffer.buffer) - (uint8*)p + (uint8*)target);
break;
}
}
static status_t
acpi_call_control(void *_device, uint32 op, void *buffer, size_t length)
{
TRACE("control(%p, %" B_PRIu32 ", %p, %lu)\n", _device, op, buffer, length);
acpi_call_device_info* device = (acpi_call_device_info*)_device;
if (op == 'ACCA') {
struct acpi_call_desc params;
char path[1024];
if (user_memcpy(¶ms, buffer, sizeof(params)) != B_OK
|| user_memcpy(path, params.path, sizeof(path)) != B_OK) {
return B_BAD_ADDRESS;
}
acpi_data result;
result.length = ACPI_ALLOCATE_BUFFER;
result.pointer = NULL;
acpi_status retval = device->acpi->evaluate_method(NULL, path, ¶ms.args, &result);
if (retval == 0) {
if (result.pointer != NULL) {
if (params.result.pointer != NULL) {
params.result.length = min_c(params.result.length, result.length);
if (result.length >= sizeof(acpi_object_type))
acpi_call_fixup_pointers((acpi_object_type*)(result.pointer), params.result.pointer);
if (user_memcpy(params.result.pointer, result.pointer, params.result.length) != B_OK
|| user_memcpy(buffer, ¶ms, sizeof(params)) != B_OK) {
return B_BAD_ADDRESS;
}
}
free(result.pointer);
}
}
return B_OK;
}
return B_ERROR;
}
static status_t
acpi_call_close(void *cookie)
{
TRACE("close(%p)\n", cookie);
return B_OK;
}
static status_t
acpi_call_free(void *cookie)
{
TRACE("free(%p)\n", cookie);
return B_OK;
}
struct device_module_info gAcpiCallDeviceModule = {
{
ACPI_CALL_DEVICE_MODULE_NAME,
0,
NULL
},
acpi_call_init_device,
acpi_call_uninit_device,
NULL,
acpi_call_open,
acpi_call_close,
acpi_call_free,
acpi_call_read,
acpi_call_write,
NULL,
acpi_call_control,
NULL,
NULL,
};