acpica: add ACPI_GET_PROPERTY to access Device Specific Data (DSD)

Add lazy acquiring of DSD package, which allows accessing Device
Specific Data.

Reviewed by: manu, mw
Sponsored by: Semihalf
Differential revision: https://reviews.freebsd.org/D31596
This commit is contained in:
Bartlomiej Grzesik 2021-07-27 14:39:31 +02:00 committed by Marcin Wojtas
parent 34b1efcea1
commit b91fc6c43a
3 changed files with 132 additions and 0 deletions

View File

@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sched.h>
#include <sys/smp.h>
#include <sys/timetc.h>
#include <sys/uuid.h>
#if defined(__i386__) || defined(__amd64__)
#include <machine/clock.h>
@ -147,10 +148,13 @@ static int acpi_device_id_probe(device_t bus, device_t dev, char **ids, char **m
static ACPI_STATUS acpi_device_eval_obj(device_t bus, device_t dev,
ACPI_STRING pathname, ACPI_OBJECT_LIST *parameters,
ACPI_BUFFER *ret);
static ACPI_STATUS acpi_device_get_prop(device_t bus, device_t dev,
ACPI_STRING propname, const ACPI_OBJECT **value);
static ACPI_STATUS acpi_device_scan_cb(ACPI_HANDLE h, UINT32 level,
void *context, void **retval);
static ACPI_STATUS acpi_device_scan_children(device_t bus, device_t dev,
int max_depth, acpi_scan_cb_t user_fn, void *arg);
static ACPI_STATUS acpi_find_dsd(device_t bus, device_t dev);
static int acpi_isa_pnp_probe(device_t bus, device_t child,
struct isa_pnp_id *ids);
static void acpi_platform_osc(device_t dev);
@ -223,6 +227,7 @@ static device_method_t acpi_methods[] = {
/* ACPI bus */
DEVMETHOD(acpi_id_probe, acpi_device_id_probe),
DEVMETHOD(acpi_evaluate_object, acpi_device_eval_obj),
DEVMETHOD(acpi_get_property, acpi_device_get_prop),
DEVMETHOD(acpi_pwr_for_sleep, acpi_device_pwr_for_sleep),
DEVMETHOD(acpi_scan_children, acpi_device_scan_children),
@ -296,6 +301,15 @@ int acpi_susp_bounce;
SYSCTL_INT(_debug_acpi, OID_AUTO, suspend_bounce, CTLFLAG_RW,
&acpi_susp_bounce, 0, "Don't actually suspend, just test devices.");
/*
* ACPI standard UUID for Device Specific Data Package
* "Device Properties UUID for _DSD" Rev. 2.0
*/
static const struct uuid acpi_dsd_uuid = {
0xdaffd814, 0x6eba, 0x4d8c, 0x8a, 0x91,
{ 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01 }
};
/*
* ACPI can only be loaded as a module by the loader; activating it after
* system bootstrap time is not useful, and can be fatal to the system.
@ -1727,6 +1741,82 @@ acpi_device_eval_obj(device_t bus, device_t dev, ACPI_STRING pathname,
return (AcpiEvaluateObject(h, pathname, parameters, ret));
}
static ACPI_STATUS
acpi_device_get_prop(device_t bus, device_t dev, ACPI_STRING propname,
const ACPI_OBJECT **value)
{
const ACPI_OBJECT *pkg, *name, *val;
struct acpi_device *ad;
ACPI_STATUS status;
int i;
ad = device_get_ivars(dev);
if (ad == NULL || propname == NULL)
return (AE_BAD_PARAMETER);
if (ad->dsd_pkg == NULL) {
if (ad->dsd.Pointer == NULL) {
status = acpi_find_dsd(bus, dev);
if (ACPI_FAILURE(status))
return (status);
} else {
return (AE_NOT_FOUND);
}
}
for (i = 0; i < ad->dsd_pkg->Package.Count; i ++) {
pkg = &ad->dsd_pkg->Package.Elements[i];
if (pkg->Type != ACPI_TYPE_PACKAGE || pkg->Package.Count != 2)
continue;
name = &pkg->Package.Elements[0];
val = &pkg->Package.Elements[1];
if (name->Type != ACPI_TYPE_STRING)
continue;
if (strncmp(propname, name->String.Pointer, name->String.Length) == 0) {
if (value != NULL)
*value = val;
return (AE_OK);
}
}
return (AE_NOT_FOUND);
}
static ACPI_STATUS
acpi_find_dsd(device_t bus, device_t dev)
{
const ACPI_OBJECT *dsd, *guid, *pkg;
struct acpi_device *ad;
ACPI_STATUS status;
ad = device_get_ivars(dev);
ad->dsd.Length = ACPI_ALLOCATE_BUFFER;
ad->dsd.Pointer = NULL;
ad->dsd_pkg = NULL;
status = ACPI_EVALUATE_OBJECT(bus, dev, "_DSD", NULL, &ad->dsd);
if (ACPI_FAILURE(status))
return (status);
dsd = ad->dsd.Pointer;
guid = &dsd->Package.Elements[0];
pkg = &dsd->Package.Elements[1];
if (guid->Type != ACPI_TYPE_BUFFER || pkg->Type != ACPI_TYPE_PACKAGE ||
guid->Buffer.Length != sizeof(acpi_dsd_uuid))
return (AE_NOT_FOUND);
if (memcmp(guid->Buffer.Pointer, &acpi_dsd_uuid,
sizeof(acpi_dsd_uuid)) == 0) {
ad->dsd_pkg = pkg;
return (AE_OK);
}
return (AE_NOT_FOUND);
}
int
acpi_device_pwr_for_sleep(device_t bus, device_t dev, int *dstate)
{
@ -2399,6 +2489,15 @@ acpi_GetHandleInScope(ACPI_HANDLE parent, char *path, ACPI_HANDLE *result)
}
}
ACPI_STATUS
acpi_GetProperty(device_t dev, ACPI_STRING propname,
const ACPI_OBJECT **value)
{
device_t bus = device_get_parent(dev);
return (ACPI_GET_PROPERTY(bus, dev, propname, value));
}
/*
* Allocate a buffer with a preset data size.
*/

View File

@ -121,6 +121,27 @@ METHOD ACPI_STATUS evaluate_object {
ACPI_BUFFER *ret;
};
#
# Get property value from Device Specific Data
#
# device_t bus: parent bus for the device
#
# device_t dev: find property for this device's handle.
#
# const ACPI_STRING propname: name of the property
#
# const ACPI_OBJECT **value: property value output
# Specify NULL if ignored
#
# Returns: AE_OK or an error value
#
METHOD ACPI_STATUS get_property {
device_t bus;
device_t dev;
ACPI_STRING propname;
const ACPI_OBJECT **value;
};
#
# Get the highest power state (D0-D3) that is usable for a device when
# suspending/resuming. If a bus calls this when suspending a device, it

View File

@ -90,6 +90,9 @@ struct acpi_device {
int ad_flags;
int ad_cls_class;
ACPI_BUFFER dsd; /* Device Specific Data */
const ACPI_OBJECT *dsd_pkg;
/* Resources */
struct resource_list ad_rl;
};
@ -350,6 +353,8 @@ BOOLEAN acpi_DeviceIsPresent(device_t dev);
BOOLEAN acpi_BatteryIsPresent(device_t dev);
ACPI_STATUS acpi_GetHandleInScope(ACPI_HANDLE parent, char *path,
ACPI_HANDLE *result);
ACPI_STATUS acpi_GetProperty(device_t dev, ACPI_STRING propname,
const ACPI_OBJECT **value);
ACPI_BUFFER *acpi_AllocBuffer(int size);
ACPI_STATUS acpi_ConvertBufferToInteger(ACPI_BUFFER *bufp,
UINT32 *number);
@ -396,6 +401,13 @@ int acpi_MatchHid(ACPI_HANDLE h, const char *hid);
#define ACPI_MATCHHID_HID 1
#define ACPI_MATCHHID_CID 2
static __inline bool
acpi_HasProperty(device_t dev, ACPI_STRING propname)
{
return ACPI_SUCCESS(acpi_GetProperty(dev, propname, NULL));
}
struct acpi_parse_resource_set {
void (*set_init)(device_t dev, void *arg, void **context);
void (*set_done)(device_t dev, void *context);