git: fd08253cdcec - stable/13 - acpica: add ACPI_GET_PROPERTY to access Device Specific Data (DSD)

From: Marcin Wojtas <mw_at_FreeBSD.org>
Date: Tue, 29 Mar 2022 22:59:53 UTC
The branch stable/13 has been updated by mw:

URL: https://cgit.FreeBSD.org/src/commit/?id=fd08253cdcec12ed4fd6cd47be69d978efc2f214

commit fd08253cdcec12ed4fd6cd47be69d978efc2f214
Author:     Bartlomiej Grzesik <bag@semihalf.com>
AuthorDate: 2021-07-27 12:39:31 +0000
Commit:     Marcin Wojtas <mw@FreeBSD.org>
CommitDate: 2022-03-29 22:24:25 +0000

    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
    
    (cherry picked from commit b91fc6c43a81d3b760fb570c8439a922e536d7e6)
---
 sys/dev/acpica/acpi.c    | 99 ++++++++++++++++++++++++++++++++++++++++++++++++
 sys/dev/acpica/acpi_if.m | 21 ++++++++++
 sys/dev/acpica/acpivar.h | 12 ++++++
 3 files changed, 132 insertions(+)

diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c
index c1b79fb15727..8cbd916634b0 100644
--- a/sys/dev/acpica/acpi.c
+++ b/sys/dev/acpica/acpi.c
@@ -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.
@@ -1733,6 +1747,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)
 {
@@ -2405,6 +2495,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.
  */
diff --git a/sys/dev/acpica/acpi_if.m b/sys/dev/acpica/acpi_if.m
index 6e36f398411e..e0ed4d72e899 100644
--- a/sys/dev/acpica/acpi_if.m
+++ b/sys/dev/acpica/acpi_if.m
@@ -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
diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h
index fb568169cf28..f53adaa1d834 100644
--- a/sys/dev/acpica/acpivar.h
+++ b/sys/dev/acpica/acpivar.h
@@ -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);