#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: acpi_util.c,v 1.37 2025/10/04 01:12:15 thorpej Exp $");
#include <sys/param.h>
#include <sys/kmem.h>
#include <sys/cpu.h>
#include <dev/acpi/acpireg.h>
#include <dev/acpi/acpivar.h>
#include <dev/acpi/acpi_intr.h>
#include <sys/device_calls.h>
#include <machine/acpi_machdep.h>
#define _COMPONENT ACPI_BUS_COMPONENT
ACPI_MODULE_NAME ("acpi_util")
static void acpi_clean_node(ACPI_HANDLE, void *);
static ACPI_STATUS acpi_dsd_property(ACPI_HANDLE, const char *,
ACPI_BUFFER *, ACPI_OBJECT_TYPE, ACPI_OBJECT **);
static const char * const acpicpu_ids[] = {
"ACPI0007",
NULL
};
static const struct device_compatible_entry dtlink_compat_data[] = {
{ .compat = "PRP0001" },
DEVICE_COMPAT_EOL
};
static device_call_t
acpi_devhandle_lookup_device_call(devhandle_t handle, const char *name,
devhandle_t *call_handlep)
{
__link_set_decl(acpi_device_calls, struct device_call_descriptor);
struct device_call_descriptor * const *desc;
__link_set_foreach(desc, acpi_device_calls) {
if (strcmp((*desc)->name, name) == 0) {
return (*desc)->call;
}
}
return NULL;
}
static const struct devhandle_impl acpi_devhandle_impl = {
.type = DEVHANDLE_TYPE_ACPI,
.lookup_device_call = acpi_devhandle_lookup_device_call,
};
devhandle_t
devhandle_from_acpi(devhandle_t super_handle, ACPI_HANDLE const hdl)
{
devhandle_type_t super_type = devhandle_type(super_handle);
devhandle_t handle = { 0 };
if (super_type == DEVHANDLE_TYPE_ACPI) {
handle.impl = super_handle.impl;
} else {
KASSERT(super_type == DEVHANDLE_TYPE_INVALID);
handle.impl = &acpi_devhandle_impl;
}
handle.pointer = hdl;
return handle;
}
ACPI_HANDLE
devhandle_to_acpi(devhandle_t const handle)
{
KASSERT(devhandle_type(handle) == DEVHANDLE_TYPE_ACPI);
return handle.pointer;
}
static int
acpi_device_enumerate_children(device_t dev, devhandle_t call_handle, void *v)
{
struct device_enumerate_children_args *args = v;
ACPI_HANDLE hdl = devhandle_to_acpi(call_handle);
struct acpi_devnode *devnode, *ad;
devnode = acpi_match_node(hdl);
KASSERT(devnode != NULL);
SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) {
if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE ||
!acpi_device_present(ad->ad_handle)) {
continue;
}
if (!args->callback(dev, devhandle_from_acpi(call_handle,
ad->ad_handle),
args->callback_arg)) {
break;
}
}
return 0;
}
ACPI_DEVICE_CALL_REGISTER(DEVICE_ENUMERATE_CHILDREN_STR,
acpi_device_enumerate_children)
static int
acpi_device_register(device_t dev, devhandle_t call_handle, void *v)
{
ACPI_HANDLE handle = devhandle_to_acpi(call_handle);
ACPI_BUFFER buf;
ACPI_STATUS rv;
buf.Pointer = NULL;
buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
rv = AcpiGetName(handle, ACPI_FULL_PATHNAME, &buf);
if (! ACPI_FAILURE(rv)) {
device_setprop_string(dev, "device-path", buf.Pointer);
}
ACPI_FREE(buf.Pointer);
return 0;
}
ACPI_DEVICE_CALL_REGISTER(DEVICE_REGISTER_STR,
acpi_device_register)
static bool
prop_type_to_acpi(prop_type_t type, ACPI_OBJECT_TYPE *out)
{
switch (type) {
case PROP_TYPE_NUMBER:
case PROP_TYPE_BOOL:
*out = ACPI_TYPE_INTEGER;
break;
case PROP_TYPE_DATA:
*out = ACPI_TYPE_ANY;
break;
case PROP_TYPE_STRING:
*out = ACPI_TYPE_STRING;
break;
case PROP_TYPE_UNKNOWN:
*out = ACPI_TYPE_ANY;
break;
default:
return false;
}
return true;
}
static bool
acpi_type_to_prop(ACPI_OBJECT_TYPE acpitype, prop_type_t *out)
{
switch (acpitype) {
case ACPI_TYPE_INTEGER:
*out = PROP_TYPE_NUMBER;
break;
case ACPI_TYPE_BUFFER:
*out = PROP_TYPE_DATA;
break;
case ACPI_TYPE_STRING:
*out = PROP_TYPE_STRING;
break;
default:
return false;
}
return true;
}
static int
acpi_device_get_property(device_t dev, devhandle_t call_handle, void *v)
{
struct device_get_property_args *args = v;
ACPI_HANDLE hdl = devhandle_to_acpi(call_handle);
ACPI_OBJECT *propval;
ACPI_OBJECT_TYPE acpitype;
ACPI_STATUS rv;
ACPI_BUFFER buf;
int error = 0;
if (! prop_type_to_acpi(args->reqtype, &acpitype)) {
return EFTYPE;
}
buf.Pointer = NULL;
buf.Length = ACPI_ALLOCATE_BUFFER;
rv = acpi_dsd_property(hdl, args->prop, &buf, acpitype, &propval);
if (!ACPI_SUCCESS(rv)) {
if (rv == AE_TYPE) {
error = EFTYPE;
goto out;
}
error = ENOENT;
goto out;
}
args->encoding = _LITTLE_ENDIAN;
if (! acpi_type_to_prop(propval->Type, &args->type)) {
error = EFTYPE;
goto out;
}
switch (propval->Type) {
case ACPI_TYPE_INTEGER:
args->propsize = sizeof(propval->Integer.Value);
if (args->buf != NULL) {
if (args->reqtype == PROP_TYPE_BOOL) {
KASSERT(args->buflen == sizeof(bool));
*(bool *)args->buf =
propval->Integer.Value != 0;
goto out;
}
if (args->reqtype != PROP_TYPE_NUMBER) {
error = EFTYPE;
goto out;
}
KASSERT(args->buflen == sizeof(uint64_t));
*(uint64_t *)args->buf =
le64toh(propval->Integer.Value);
}
break;
case ACPI_TYPE_STRING:
args->propsize = propval->String.Length + 1;
if (args->buf != NULL) {
if (args->buflen < args->propsize) {
error = EFBIG;
goto out;
}
strlcpy(args->buf, propval->String.Pointer,
args->buflen);
}
break;
case ACPI_TYPE_BUFFER:
args->propsize = propval->Buffer.Length;
if (args->buf != NULL) {
if (args->buflen < args->propsize) {
error = EFBIG;
goto out;
}
memcpy(args->buf, propval->Buffer.Pointer,
args->propsize);
}
break;
default:
error = EFTYPE;
goto out;
}
out:
if (buf.Pointer != NULL) {
ACPI_FREE(buf.Pointer);
}
return error;
}
ACPI_DEVICE_CALL_REGISTER(DEVICE_GET_PROPERTY_STR,
acpi_device_get_property)
ACPI_STATUS
acpi_eval_integer(ACPI_HANDLE handle, const char *path, ACPI_INTEGER *valp)
{
ACPI_OBJECT obj;
ACPI_BUFFER buf;
ACPI_STATUS rv;
if (handle == NULL)
handle = ACPI_ROOT_OBJECT;
(void)memset(&obj, 0, sizeof(obj));
buf.Pointer = &obj;
buf.Length = sizeof(obj);
rv = AcpiEvaluateObject(handle, path, NULL, &buf);
if (ACPI_FAILURE(rv))
return rv;
if (buf.Length == 0)
return AE_NULL_OBJECT;
if (obj.Type != ACPI_TYPE_INTEGER)
return AE_TYPE;
if (valp != NULL)
*valp = obj.Integer.Value;
return AE_OK;
}
ACPI_STATUS
acpi_eval_set_integer(ACPI_HANDLE handle, const char *path, ACPI_INTEGER val)
{
ACPI_OBJECT_LIST arg;
ACPI_OBJECT obj;
if (handle == NULL)
handle = ACPI_ROOT_OBJECT;
obj.Type = ACPI_TYPE_INTEGER;
obj.Integer.Value = val;
arg.Count = 1;
arg.Pointer = &obj;
return AcpiEvaluateObject(handle, path, &arg, NULL);
}
ACPI_STATUS
acpi_eval_string(ACPI_HANDLE handle, const char *path, char **stringp)
{
ACPI_OBJECT *obj;
ACPI_BUFFER buf;
ACPI_STATUS rv;
rv = acpi_eval_struct(handle, path, &buf);
if (ACPI_FAILURE(rv))
return rv;
obj = buf.Pointer;
if (obj->Type != ACPI_TYPE_STRING) {
rv = AE_TYPE;
goto out;
}
if (obj->String.Length == 0) {
rv = AE_BAD_DATA;
goto out;
}
*stringp = ACPI_ALLOCATE(obj->String.Length + 1);
if (*stringp == NULL) {
rv = AE_NO_MEMORY;
goto out;
}
(void)memcpy(*stringp, obj->String.Pointer, obj->String.Length);
(*stringp)[obj->String.Length] = '\0';
out:
ACPI_FREE(buf.Pointer);
return rv;
}
ACPI_STATUS
acpi_eval_struct(ACPI_HANDLE handle, const char *path, ACPI_BUFFER *buf)
{
if (handle == NULL)
handle = ACPI_ROOT_OBJECT;
buf->Pointer = NULL;
buf->Length = ACPI_ALLOCATE_LOCAL_BUFFER;
return AcpiEvaluateObject(handle, path, NULL, buf);
}
ACPI_STATUS
acpi_eval_reference_handle(ACPI_OBJECT *elm, ACPI_HANDLE *handle)
{
if (elm == NULL || handle == NULL)
return AE_BAD_PARAMETER;
switch (elm->Type) {
case ACPI_TYPE_ANY:
case ACPI_TYPE_LOCAL_REFERENCE:
if (elm->Reference.Handle == NULL)
return AE_NULL_ENTRY;
*handle = elm->Reference.Handle;
return AE_OK;
case ACPI_TYPE_STRING:
return AcpiGetHandle(NULL, elm->String.Pointer, handle);
default:
return AE_TYPE;
}
}
ACPI_STATUS
acpi_foreach_package_object(ACPI_OBJECT *pkg,
ACPI_STATUS (*func)(ACPI_OBJECT *, void *), void *arg)
{
ACPI_STATUS rv = AE_OK;
uint32_t i;
if (pkg == NULL)
return AE_BAD_PARAMETER;
if (pkg->Type != ACPI_TYPE_PACKAGE)
return AE_TYPE;
for (i = 0; i < pkg->Package.Count; i++) {
rv = (*func)(&pkg->Package.Elements[i], arg);
if (ACPI_FAILURE(rv))
break;
}
return rv;
}
ACPI_STATUS
acpi_get(ACPI_HANDLE handle, ACPI_BUFFER *buf,
ACPI_STATUS (*getit)(ACPI_HANDLE, ACPI_BUFFER *))
{
buf->Pointer = NULL;
buf->Length = ACPI_ALLOCATE_LOCAL_BUFFER;
return (*getit)(handle, buf);
}
const char *
acpi_name(ACPI_HANDLE handle)
{
static char name[80];
ACPI_BUFFER buf;
ACPI_STATUS rv;
if (handle == NULL)
handle = ACPI_ROOT_OBJECT;
buf.Pointer = name;
buf.Length = sizeof(name);
rv = AcpiGetName(handle, ACPI_FULL_PATHNAME, &buf);
if (ACPI_FAILURE(rv))
return "UNKNOWN";
return name;
}
char *
acpi_pack_compat_list(struct acpi_devnode *ad, size_t *sizep)
{
ACPI_DEVICE_INFO *devinfo = ad->ad_devinfo;
KASSERT(sizep != NULL);
char *sl = NULL;
size_t slsize = 0;
uint32_t i;
bool dtlink = false;
ACPI_BUFFER buf;
ACPI_STATUS ret;
ACPI_OBJECT *obj;
char *compatible;
int n;
buf.Pointer = NULL;
buf.Length = ACPI_ALLOCATE_BUFFER;
if ((devinfo->Valid & ACPI_VALID_HID) != 0) {
const char *cp = devinfo->HardwareId.String;
if (device_compatible_pmatch_strlist(cp, strlen(cp) + 1,
dtlink_compat_data)) {
dtlink = true;
} else {
strlist_append(&sl, &slsize, cp);
}
}
if ((devinfo->Valid & ACPI_VALID_CID) != 0) {
for (i = 0; i < devinfo->CompatibleIdList.Count; i++) {
const char *cp =
devinfo->CompatibleIdList.Ids[i].String;
if (device_compatible_pmatch_strlist(cp, strlen(cp) + 1,
dtlink_compat_data)) {
dtlink = true;
} else {
strlist_append(&sl, &slsize, cp);
}
}
}
if (dtlink) {
ret = acpi_dsd_string(ad->ad_handle, "compatible",
&compatible);
if (ACPI_SUCCESS(ret)) {
strlist_append(&sl, &slsize, compatible);
kmem_strfree(compatible);
goto done;
}
ret = acpi_dsd_property(ad->ad_handle, "compatible", &buf,
ACPI_TYPE_PACKAGE, &obj);
if (ACPI_FAILURE(ret)) {
goto done;
}
if (obj->Package.Count == 0) {
goto done;
}
for (n = 0; n < obj->Package.Count; n++) {
if (obj->Package.Elements[n].Type != ACPI_TYPE_STRING) {
continue;
}
strlist_append(&sl, &slsize,
obj->Package.Elements[n].String.Pointer);
}
}
done:
if (buf.Pointer != NULL) {
ACPI_FREE(buf.Pointer);
}
*sizep = slsize;
return sl;
}
#define ACPI_COMPATSTR_MAX 8
static const char **
acpi_compatible_alloc_strarray(ACPI_PNP_DEVICE_ID *ids,
unsigned int count, const char **buf)
{
unsigned int i;
buf = kmem_tmpbuf_alloc(count * sizeof(const char *),
buf, ACPI_COMPATSTR_MAX * sizeof(const char *), KM_SLEEP);
for (i = 0; i < count; i++) {
buf[i] = ids[i].String;
}
return buf;
}
static void
acpi_compatible_free_strarray(const char **cpp, unsigned int count,
const char **buf)
{
kmem_tmpbuf_free(cpp, count * sizeof(const char *), buf);
}
static int
acpi_compatible_match_dtlink(const struct acpi_attach_args * const aa,
const struct device_compatible_entry * const dce)
{
const char *strings[ACPI_COMPATSTR_MAX * sizeof(const char *)];
ACPI_HANDLE handle = aa->aa_node->ad_handle;
ACPI_BUFFER buf;
char *compatible;
ACPI_STATUS ret;
ACPI_OBJECT *obj;
int rv = 0, n;
buf.Pointer = NULL;
buf.Length = ACPI_ALLOCATE_BUFFER;
ret = acpi_dsd_string(handle, "compatible", &compatible);
if (ACPI_SUCCESS(ret)) {
strings[0] = compatible;
rv = device_compatible_pmatch(strings, 1, dce);
kmem_strfree(compatible);
goto done;
}
ret = acpi_dsd_property(handle, "compatible", &buf,
ACPI_TYPE_PACKAGE, &obj);
if (ACPI_FAILURE(ret)) {
goto done;
}
if (obj->Package.Count == 0) {
goto done;
}
for (n = 0; n < imin(obj->Package.Count, ACPI_COMPATSTR_MAX); n++) {
if (obj->Package.Elements[n].Type != ACPI_TYPE_STRING) {
goto done;
}
strings[n] = obj->Package.Elements[n].String.Pointer;
}
rv = device_compatible_pmatch(strings, n, dce);
done:
if (buf.Pointer != NULL) {
ACPI_FREE(buf.Pointer);
}
if (rv) {
rv = (rv - 1) + ACPI_MATCHSCORE_CID;
return imin(rv, ACPI_MATCHSCORE_CID_MAX);
}
return 0;
}
int
acpi_compatible_match(const struct acpi_attach_args * const aa,
const struct device_compatible_entry * const dce)
{
const char *strings[ACPI_COMPATSTR_MAX * sizeof(const char *)];
const char **cpp;
bool dtlink = false;
int rv;
if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) {
return 0;
}
ACPI_DEVICE_INFO *ad = aa->aa_node->ad_devinfo;
if ((ad->Valid & ACPI_VALID_HID) != 0) {
strings[0] = ad->HardwareId.String;
if (device_compatible_pmatch(strings, 1, dce) != 0) {
return ACPI_MATCHSCORE_HID;
}
if (device_compatible_pmatch(strings, 1,
dtlink_compat_data) != 0) {
dtlink = true;
}
}
if ((ad->Valid & ACPI_VALID_CID) != 0) {
cpp = acpi_compatible_alloc_strarray(ad->CompatibleIdList.Ids,
ad->CompatibleIdList.Count, strings);
rv = device_compatible_pmatch(cpp,
ad->CompatibleIdList.Count, dce);
if (!dtlink &&
device_compatible_pmatch(cpp, ad->CompatibleIdList.Count,
dtlink_compat_data) != 0) {
dtlink = true;
}
acpi_compatible_free_strarray(cpp, ad->CompatibleIdList.Count,
strings);
if (rv) {
rv = (rv - 1) + ACPI_MATCHSCORE_CID;
return imin(rv, ACPI_MATCHSCORE_CID_MAX);
}
}
if (dtlink) {
return acpi_compatible_match_dtlink(aa, dce);
}
return 0;
}
const struct device_compatible_entry *
acpi_compatible_lookup(const struct acpi_attach_args * const aa,
const struct device_compatible_entry * const dce)
{
const struct device_compatible_entry *rv = NULL;
const char *strings[ACPI_COMPATSTR_MAX];
const char **cpp;
if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) {
return NULL;
}
ACPI_DEVICE_INFO *ad = aa->aa_node->ad_devinfo;
if ((ad->Valid & ACPI_VALID_HID) != 0) {
strings[0] = ad->HardwareId.String;
rv = device_compatible_plookup(strings, 1, dce);
if (rv != NULL)
return rv;
}
if ((ad->Valid & ACPI_VALID_CID) != 0) {
cpp = acpi_compatible_alloc_strarray(ad->CompatibleIdList.Ids,
ad->CompatibleIdList.Count, strings);
rv = device_compatible_plookup(cpp,
ad->CompatibleIdList.Count, dce);
acpi_compatible_free_strarray(cpp, ad->CompatibleIdList.Count,
strings);
}
return rv;
}
int
acpi_match_hid(ACPI_DEVICE_INFO *ad, const char * const *ids)
{
uint32_t i, n;
char *id;
while (*ids) {
if ((ad->Valid & ACPI_VALID_HID) != 0) {
if (pmatch(ad->HardwareId.String, *ids, NULL) == 2)
return 1;
}
if ((ad->Valid & ACPI_VALID_CID) != 0) {
n = ad->CompatibleIdList.Count;
for (i = 0; i < n; i++) {
id = ad->CompatibleIdList.Ids[i].String;
if (pmatch(id, *ids, NULL) == 2)
return 1;
}
}
ids++;
}
return 0;
}
int
acpi_match_class(ACPI_HANDLE handle, uint8_t pci_class, uint8_t pci_subclass,
uint8_t pci_interface)
{
ACPI_BUFFER buf;
ACPI_OBJECT *obj;
ACPI_STATUS rv;
int match = 0;
rv = acpi_eval_struct(handle, "_CLS", &buf);
if (ACPI_FAILURE(rv))
goto done;
obj = buf.Pointer;
if (obj->Type != ACPI_TYPE_PACKAGE)
goto done;
if (obj->Package.Count != 3)
goto done;
if (obj->Package.Elements[0].Type != ACPI_TYPE_INTEGER ||
obj->Package.Elements[1].Type != ACPI_TYPE_INTEGER ||
obj->Package.Elements[2].Type != ACPI_TYPE_INTEGER)
goto done;
match = obj->Package.Elements[0].Integer.Value == pci_class &&
obj->Package.Elements[1].Integer.Value == pci_subclass &&
obj->Package.Elements[2].Integer.Value == pci_interface;
done:
if (buf.Pointer)
ACPI_FREE(buf.Pointer);
return match ? ACPI_MATCHSCORE_CLS : 0;
}
struct acpi_devnode *
acpi_match_node(ACPI_HANDLE handle)
{
struct acpi_devnode *ad;
ACPI_STATUS rv;
if (handle == NULL)
return NULL;
rv = AcpiGetData(handle, acpi_clean_node, (void **)&ad);
if (ACPI_FAILURE(rv))
return NULL;
return ad;
}
void
acpi_match_node_init(struct acpi_devnode *ad)
{
(void)AcpiAttachData(ad->ad_handle, acpi_clean_node, ad);
}
static void
acpi_clean_node(ACPI_HANDLE handle, void *aux)
{
}
ACPI_HANDLE
acpi_match_cpu_info(struct cpu_info *ci)
{
struct acpi_softc *sc = acpi_softc;
struct acpi_devnode *ad;
ACPI_INTEGER val;
ACPI_OBJECT *obj;
ACPI_BUFFER buf;
ACPI_HANDLE hdl;
ACPI_STATUS rv;
if (sc == NULL)
return NULL;
SIMPLEQ_FOREACH(ad, &sc->sc_head, ad_list) {
hdl = ad->ad_handle;
switch (ad->ad_type) {
case ACPI_TYPE_DEVICE:
if (acpi_match_hid(ad->ad_devinfo, acpicpu_ids) == 0)
break;
rv = acpi_eval_integer(hdl, "_UID", &val);
if (ACPI_SUCCESS(rv) && val == ci->ci_acpiid)
return hdl;
break;
case ACPI_TYPE_PROCESSOR:
rv = acpi_eval_struct(hdl, NULL, &buf);
if (ACPI_FAILURE(rv))
break;
obj = buf.Pointer;
if (obj->Processor.ProcId == ci->ci_acpiid) {
ACPI_FREE(buf.Pointer);
return hdl;
}
ACPI_FREE(buf.Pointer);
break;
}
}
return NULL;
}
struct cpu_info *
acpi_match_cpu_handle(ACPI_HANDLE hdl)
{
struct cpu_info *ci;
ACPI_DEVICE_INFO *di;
CPU_INFO_ITERATOR cii;
ACPI_INTEGER val;
ACPI_OBJECT *obj;
ACPI_BUFFER buf;
ACPI_STATUS rv;
ci = NULL;
di = NULL;
buf.Pointer = NULL;
rv = AcpiGetObjectInfo(hdl, &di);
if (ACPI_FAILURE(rv))
return NULL;
switch (di->Type) {
case ACPI_TYPE_DEVICE:
if (acpi_match_hid(di, acpicpu_ids) == 0)
goto out;
rv = acpi_eval_integer(hdl, "_UID", &val);
if (ACPI_FAILURE(rv))
goto out;
break;
case ACPI_TYPE_PROCESSOR:
rv = acpi_eval_struct(hdl, NULL, &buf);
if (ACPI_FAILURE(rv))
goto out;
obj = buf.Pointer;
val = obj->Processor.ProcId;
break;
default:
goto out;
}
for (CPU_INFO_FOREACH(cii, ci)) {
if (ci->ci_acpiid == val)
goto out;
}
ci = NULL;
out:
if (di != NULL)
ACPI_FREE(di);
if (buf.Pointer != NULL)
ACPI_FREE(buf.Pointer);
return ci;
}
struct acpi_irq_handler {
uint32_t aih_irq;
void *aih_ih;
};
void *
acpi_intr_establish(device_t dev, uint64_t c, int ipl, bool mpsafe,
int (*intr)(void *), void *iarg, const char *xname)
{
ACPI_STATUS rv;
ACPI_HANDLE hdl = (void *)(uintptr_t)c;
struct acpi_resources res;
struct acpi_irq *irq;
void *aih = NULL;
rv = acpi_resource_parse(dev, hdl, "_CRS", &res,
&acpi_resource_parse_ops_quiet);
if (ACPI_FAILURE(rv))
return NULL;
irq = acpi_res_irq(&res, 0);
if (irq == NULL)
goto end;
aih = acpi_intr_establish_irq(dev, irq, ipl, mpsafe,
intr, iarg, xname);
end:
acpi_resource_cleanup(&res);
return aih;
}
void *
acpi_intr_establish_irq(device_t dev, struct acpi_irq *irq, int ipl,
bool mpsafe, int (*intr)(void *), void *iarg, const char *xname)
{
struct acpi_irq_handler *aih;
void *ih;
const int type = (irq->ar_type == ACPI_EDGE_SENSITIVE) ? IST_EDGE : IST_LEVEL;
ih = acpi_md_intr_establish(irq->ar_irq, ipl, type, intr, iarg, mpsafe, xname);
if (ih == NULL)
return NULL;
aih = kmem_alloc(sizeof(struct acpi_irq_handler), KM_SLEEP);
aih->aih_irq = irq->ar_irq;
aih->aih_ih = ih;
return aih;
}
void
acpi_intr_mask(void *c)
{
struct acpi_irq_handler * const aih = c;
acpi_md_intr_mask(aih->aih_ih);
}
void
acpi_intr_unmask(void *c)
{
struct acpi_irq_handler * const aih = c;
acpi_md_intr_unmask(aih->aih_ih);
}
void
acpi_intr_disestablish(void *c)
{
struct acpi_irq_handler *aih = c;
acpi_md_intr_disestablish(aih->aih_ih);
kmem_free(aih, sizeof(struct acpi_irq_handler));
}
const char *
acpi_intr_string(void *c, char *buf, size_t size)
{
struct acpi_irq_handler *aih = c;
intr_handle_t ih = aih->aih_irq;
return intr_string(ih, buf, size);
}
static UINT8 acpi_dsd_uuid[ACPI_UUID_LENGTH] = {
0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, 0x4d,
0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01
};
static ACPI_STATUS
acpi_dsd_property(ACPI_HANDLE handle, const char *prop, ACPI_BUFFER *pbuf, ACPI_OBJECT_TYPE type, ACPI_OBJECT **ret)
{
ACPI_OBJECT *obj, *uuid, *props, *pobj, *propkey, *propval;
ACPI_STATUS rv;
int n;
rv = AcpiEvaluateObjectTyped(handle, "_DSD", NULL, pbuf, ACPI_TYPE_PACKAGE);
if (ACPI_FAILURE(rv))
return rv;
props = NULL;
obj = (ACPI_OBJECT *)pbuf->Pointer;
for (n = 0; (n + 1) < obj->Package.Count; n += 2) {
uuid = &obj->Package.Elements[n];
if (uuid->Buffer.Length == ACPI_UUID_LENGTH &&
memcmp(uuid->Buffer.Pointer, acpi_dsd_uuid, ACPI_UUID_LENGTH) == 0) {
props = &obj->Package.Elements[n + 1];
break;
}
}
if (props == NULL)
return AE_NOT_FOUND;
for (n = 0; n < props->Package.Count; n++) {
pobj = &props->Package.Elements[n];
if (pobj->Type != ACPI_TYPE_PACKAGE || pobj->Package.Count != 2)
continue;
propkey = (ACPI_OBJECT *)&pobj->Package.Elements[0];
propval = (ACPI_OBJECT *)&pobj->Package.Elements[1];
if (propkey->Type != ACPI_TYPE_STRING)
continue;
if (strcmp(propkey->String.Pointer, prop) != 0)
continue;
if (type != ACPI_TYPE_ANY && propval->Type != type) {
return AE_TYPE;
} else {
*ret = propval;
return AE_OK;
}
break;
}
return AE_NOT_FOUND;
}
ACPI_STATUS
acpi_dsd_integer(ACPI_HANDLE handle, const char *prop, ACPI_INTEGER *val)
{
ACPI_OBJECT *propval;
ACPI_STATUS rv;
ACPI_BUFFER buf;
buf.Pointer = NULL;
buf.Length = ACPI_ALLOCATE_BUFFER;
rv = acpi_dsd_property(handle, prop, &buf, ACPI_TYPE_INTEGER, &propval);
if (ACPI_SUCCESS(rv))
*val = propval->Integer.Value;
if (buf.Pointer != NULL)
ACPI_FREE(buf.Pointer);
return rv;
}
ACPI_STATUS
acpi_dsd_string(ACPI_HANDLE handle, const char *prop, char **val)
{
ACPI_OBJECT *propval;
ACPI_STATUS rv;
ACPI_BUFFER buf;
buf.Pointer = NULL;
buf.Length = ACPI_ALLOCATE_BUFFER;
rv = acpi_dsd_property(handle, prop, &buf, ACPI_TYPE_STRING, &propval);
if (ACPI_SUCCESS(rv))
*val = kmem_strdup(propval->String.Pointer, KM_SLEEP);
if (buf.Pointer != NULL)
ACPI_FREE(buf.Pointer);
return rv;
}
ACPI_STATUS
acpi_dsd_bool(ACPI_HANDLE handle, const char *prop, bool *val)
{
ACPI_STATUS rv;
ACPI_INTEGER ival;
rv = acpi_dsd_integer(handle, prop, &ival);
if (ACPI_SUCCESS(rv)) {
*val = ival != 0;
}
return rv;
}
ACPI_STATUS
acpi_dsm_typed(ACPI_HANDLE handle, uint8_t *uuid, ACPI_INTEGER rev,
ACPI_INTEGER func, const ACPI_OBJECT *arg3, ACPI_OBJECT_TYPE return_type,
ACPI_OBJECT **return_obj)
{
ACPI_OBJECT_LIST arg;
ACPI_OBJECT obj[4];
ACPI_BUFFER buf;
ACPI_STATUS status;
arg.Count = 4;
arg.Pointer = obj;
obj[0].Type = ACPI_TYPE_BUFFER;
obj[0].Buffer.Length = ACPI_UUID_LENGTH;
obj[0].Buffer.Pointer = uuid;
obj[1].Type = ACPI_TYPE_INTEGER;
obj[1].Integer.Value = rev;
obj[2].Type = ACPI_TYPE_INTEGER;
obj[2].Integer.Value = func;
if (arg3 != NULL) {
obj[3] = *arg3;
} else {
obj[3].Type = ACPI_TYPE_PACKAGE;
obj[3].Package.Count = 0;
obj[3].Package.Elements = NULL;
}
buf.Pointer = NULL;
buf.Length = ACPI_ALLOCATE_BUFFER;
if (return_obj == NULL && return_type == ACPI_TYPE_ANY) {
status = AcpiEvaluateObject(handle, "_DSM", &arg, NULL);
} else {
*return_obj = NULL;
status = AcpiEvaluateObjectTyped(handle, "_DSM", &arg, &buf,
return_type);
}
if (ACPI_FAILURE(status)) {
return status;
}
if (return_obj != NULL) {
*return_obj = buf.Pointer;
} else if (buf.Pointer != NULL) {
ACPI_FREE(buf.Pointer);
}
return AE_OK;
}
ACPI_STATUS
acpi_dsm_integer(ACPI_HANDLE handle, uint8_t *uuid, ACPI_INTEGER rev,
ACPI_INTEGER func, const ACPI_OBJECT *arg3, ACPI_INTEGER *ret)
{
ACPI_OBJECT *obj;
ACPI_STATUS status;
status = acpi_dsm_typed(handle, uuid, rev, func, arg3,
ACPI_TYPE_INTEGER, &obj);
if (ACPI_FAILURE(status)) {
return status;
}
*ret = obj->Integer.Value;
ACPI_FREE(obj);
return AE_OK;
}
ACPI_STATUS
acpi_dsm(ACPI_HANDLE handle, uint8_t *uuid, ACPI_INTEGER rev,
ACPI_INTEGER func, const ACPI_OBJECT *arg3, ACPI_OBJECT **return_obj)
{
return acpi_dsm_typed(handle, uuid, rev, func, arg3, ACPI_TYPE_ANY,
return_obj);
}
ACPI_STATUS
acpi_dsm_query(ACPI_HANDLE handle, uint8_t *uuid, ACPI_INTEGER rev,
ACPI_INTEGER *ret)
{
ACPI_OBJECT *obj;
ACPI_STATUS status;
uint8_t *data;
u_int n;
status = acpi_dsm(handle, uuid, rev, 0, NULL, &obj);
if (ACPI_FAILURE(status)) {
return status;
}
if (obj->Type == ACPI_TYPE_INTEGER) {
*ret = obj->Integer.Value;
} else if (obj->Type == ACPI_TYPE_BUFFER &&
obj->Buffer.Length <= 8) {
*ret = 0;
data = (uint8_t *)obj->Buffer.Pointer;
for (n = 0; n < obj->Buffer.Length; n++) {
*ret |= (uint64_t)data[n] << (n * 8);
}
} else {
status = AE_TYPE;
}
ACPI_FREE(obj);
return status;
}
ACPI_STATUS
acpi_claim_childdevs(device_t dev, struct acpi_devnode *devnode,
const char *method)
{
struct acpi_devnode *ad;
SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) {
if (ad->ad_device != NULL)
continue;
if (method != NULL) {
ACPI_HANDLE h;
ACPI_STATUS rv;
rv = AcpiGetHandle(ad->ad_handle, method, &h);
if (ACPI_FAILURE(rv)) {
continue;
}
}
aprint_debug_dev(dev, "claiming %s\n",
acpi_name(ad->ad_handle));
ad->ad_device = dev;
acpi_claim_childdevs(dev, ad, method);
}
return AE_OK;
}