]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/acpi/property.c
ACPI: Add support for device specific properties
[mirror_ubuntu-jammy-kernel.git] / drivers / acpi / property.c
CommitLineData
ffdcd955
MW
1/*
2 * ACPI device specific properties support.
3 *
4 * Copyright (C) 2014, Intel Corporation
5 * All rights reserved.
6 *
7 * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
8 * Darren Hart <dvhart@linux.intel.com>
9 * Rafael J. Wysocki <rafael.j.wysocki@intel.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 */
15
16#include <linux/acpi.h>
17#include <linux/device.h>
18#include <linux/export.h>
19
20#include "internal.h"
21
22/* ACPI _DSD device properties UUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
23static const u8 prp_uuid[16] = {
24 0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, 0x4d,
25 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01
26};
27
28static bool acpi_property_value_ok(const union acpi_object *value)
29{
30 int j;
31
32 /*
33 * The value must be an integer, a string, a reference, or a package
34 * whose every element must be an integer, a string, or a reference.
35 */
36 switch (value->type) {
37 case ACPI_TYPE_INTEGER:
38 case ACPI_TYPE_STRING:
39 case ACPI_TYPE_LOCAL_REFERENCE:
40 return true;
41
42 case ACPI_TYPE_PACKAGE:
43 for (j = 0; j < value->package.count; j++)
44 switch (value->package.elements[j].type) {
45 case ACPI_TYPE_INTEGER:
46 case ACPI_TYPE_STRING:
47 case ACPI_TYPE_LOCAL_REFERENCE:
48 continue;
49
50 default:
51 return false;
52 }
53
54 return true;
55 }
56 return false;
57}
58
59static bool acpi_properties_format_valid(const union acpi_object *properties)
60{
61 int i;
62
63 for (i = 0; i < properties->package.count; i++) {
64 const union acpi_object *property;
65
66 property = &properties->package.elements[i];
67 /*
68 * Only two elements allowed, the first one must be a string and
69 * the second one has to satisfy certain conditions.
70 */
71 if (property->package.count != 2
72 || property->package.elements[0].type != ACPI_TYPE_STRING
73 || !acpi_property_value_ok(&property->package.elements[1]))
74 return false;
75 }
76 return true;
77}
78
79void acpi_init_properties(struct acpi_device *adev)
80{
81 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
82 const union acpi_object *desc;
83 acpi_status status;
84 int i;
85
86 status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, &buf,
87 ACPI_TYPE_PACKAGE);
88 if (ACPI_FAILURE(status))
89 return;
90
91 desc = buf.pointer;
92 if (desc->package.count % 2)
93 goto fail;
94
95 /* Look for the device properties UUID. */
96 for (i = 0; i < desc->package.count; i += 2) {
97 const union acpi_object *uuid, *properties;
98
99 uuid = &desc->package.elements[i];
100 properties = &desc->package.elements[i + 1];
101
102 /*
103 * The first element must be a UUID and the second one must be
104 * a package.
105 */
106 if (uuid->type != ACPI_TYPE_BUFFER || uuid->buffer.length != 16
107 || properties->type != ACPI_TYPE_PACKAGE)
108 break;
109
110 if (memcmp(uuid->buffer.pointer, prp_uuid, sizeof(prp_uuid)))
111 continue;
112
113 /*
114 * We found the matching UUID. Now validate the format of the
115 * package immediately following it.
116 */
117 if (!acpi_properties_format_valid(properties))
118 break;
119
120 adev->data.pointer = buf.pointer;
121 adev->data.properties = properties;
122 return;
123 }
124
125 fail:
126 dev_warn(&adev->dev, "Returned _DSD data is not valid, skipping\n");
127 ACPI_FREE(buf.pointer);
128}
129
130void acpi_free_properties(struct acpi_device *adev)
131{
132 ACPI_FREE((void *)adev->data.pointer);
133 adev->data.pointer = NULL;
134 adev->data.properties = NULL;
135}
136
137/**
138 * acpi_dev_get_property - return an ACPI property with given name
139 * @adev: ACPI device to get property
140 * @name: Name of the property
141 * @type: Expected property type
142 * @obj: Location to store the property value (if not %NULL)
143 *
144 * Look up a property with @name and store a pointer to the resulting ACPI
145 * object at the location pointed to by @obj if found.
146 *
147 * Callers must not attempt to free the returned objects. These objects will be
148 * freed by the ACPI core automatically during the removal of @adev.
149 *
150 * Return: %0 if property with @name has been found (success),
151 * %-EINVAL if the arguments are invalid,
152 * %-ENODATA if the property doesn't exist,
153 * %-EPROTO if the property value type doesn't match @type.
154 */
155int acpi_dev_get_property(struct acpi_device *adev, const char *name,
156 acpi_object_type type, const union acpi_object **obj)
157{
158 const union acpi_object *properties;
159 int i;
160
161 if (!adev || !name)
162 return -EINVAL;
163
164 if (!adev->data.pointer || !adev->data.properties)
165 return -ENODATA;
166
167 properties = adev->data.properties;
168 for (i = 0; i < properties->package.count; i++) {
169 const union acpi_object *propname, *propvalue;
170 const union acpi_object *property;
171
172 property = &properties->package.elements[i];
173
174 propname = &property->package.elements[0];
175 propvalue = &property->package.elements[1];
176
177 if (!strcmp(name, propname->string.pointer)) {
178 if (type != ACPI_TYPE_ANY && propvalue->type != type)
179 return -EPROTO;
180 else if (obj)
181 *obj = propvalue;
182
183 return 0;
184 }
185 }
186 return -ENODATA;
187}
188EXPORT_SYMBOL_GPL(acpi_dev_get_property);
189
190/**
191 * acpi_dev_get_property_array - return an ACPI array property with given name
192 * @adev: ACPI device to get property
193 * @name: Name of the property
194 * @type: Expected type of array elements
195 * @obj: Location to store a pointer to the property value (if not NULL)
196 *
197 * Look up an array property with @name and store a pointer to the resulting
198 * ACPI object at the location pointed to by @obj if found.
199 *
200 * Callers must not attempt to free the returned objects. Those objects will be
201 * freed by the ACPI core automatically during the removal of @adev.
202 *
203 * Return: %0 if array property (package) with @name has been found (success),
204 * %-EINVAL if the arguments are invalid,
205 * %-ENODATA if the property doesn't exist,
206 * %-EPROTO if the property is not a package or the type of its elements
207 * doesn't match @type.
208 */
209int acpi_dev_get_property_array(struct acpi_device *adev, const char *name,
210 acpi_object_type type,
211 const union acpi_object **obj)
212{
213 const union acpi_object *prop;
214 int ret, i;
215
216 ret = acpi_dev_get_property(adev, name, ACPI_TYPE_PACKAGE, &prop);
217 if (ret)
218 return ret;
219
220 if (type != ACPI_TYPE_ANY) {
221 /* Check that all elements are of correct type. */
222 for (i = 0; i < prop->package.count; i++)
223 if (prop->package.elements[i].type != type)
224 return -EPROTO;
225 }
226 if (obj)
227 *obj = prop;
228
229 return 0;
230}
231EXPORT_SYMBOL_GPL(acpi_dev_get_property_array);
232
233/**
234 * acpi_dev_get_property_reference - returns handle to the referenced object
235 * @adev: ACPI device to get property
236 * @name: Name of the property
237 * @size_prop: Name of the "size" property in referenced object
238 * @index: Index of the reference to return
239 * @args: Location to store the returned reference with optional arguments
240 *
241 * Find property with @name, verifify that it is a package containing at least
242 * one object reference and if so, store the ACPI device object pointer to the
243 * target object in @args->adev.
244 *
245 * If the reference includes arguments (@size_prop is not %NULL) follow the
246 * reference and check whether or not there is an integer property @size_prop
247 * under the target object and if so, whether or not its value matches the
248 * number of arguments that follow the reference. If there's more than one
249 * reference in the property value package, @index is used to select the one to
250 * return.
251 *
252 * Return: %0 on success, negative error code on failure.
253 */
254int acpi_dev_get_property_reference(struct acpi_device *adev, const char *name,
255 const char *size_prop, size_t index,
256 struct acpi_reference_args *args)
257{
258 const union acpi_object *element, *end;
259 const union acpi_object *obj;
260 struct acpi_device *device;
261 int ret, idx = 0;
262
263 ret = acpi_dev_get_property(adev, name, ACPI_TYPE_ANY, &obj);
264 if (ret)
265 return ret;
266
267 /*
268 * The simplest case is when the value is a single reference. Just
269 * return that reference then.
270 */
271 if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) {
272 if (size_prop || index)
273 return -EINVAL;
274
275 ret = acpi_bus_get_device(obj->reference.handle, &device);
276 if (ret)
277 return ret;
278
279 args->adev = device;
280 args->nargs = 0;
281 return 0;
282 }
283
284 /*
285 * If it is not a single reference, then it is a package of
286 * references followed by number of ints as follows:
287 *
288 * Package () { REF, INT, REF, INT, INT }
289 *
290 * The index argument is then used to determine which reference
291 * the caller wants (along with the arguments).
292 */
293 if (obj->type != ACPI_TYPE_PACKAGE || index >= obj->package.count)
294 return -EPROTO;
295
296 element = obj->package.elements;
297 end = element + obj->package.count;
298
299 while (element < end) {
300 u32 nargs, i;
301
302 if (element->type != ACPI_TYPE_LOCAL_REFERENCE)
303 return -EPROTO;
304
305 ret = acpi_bus_get_device(element->reference.handle, &device);
306 if (ret)
307 return -ENODEV;
308
309 element++;
310 nargs = 0;
311
312 if (size_prop) {
313 const union acpi_object *prop;
314
315 /*
316 * Find out how many arguments the refenced object
317 * expects by reading its size_prop property.
318 */
319 ret = acpi_dev_get_property(device, size_prop,
320 ACPI_TYPE_INTEGER, &prop);
321 if (ret)
322 return ret;
323
324 nargs = prop->integer.value;
325 if (nargs > MAX_ACPI_REFERENCE_ARGS
326 || element + nargs > end)
327 return -EPROTO;
328
329 /*
330 * Skip to the start of the arguments and verify
331 * that they all are in fact integers.
332 */
333 for (i = 0; i < nargs; i++)
334 if (element[i].type != ACPI_TYPE_INTEGER)
335 return -EPROTO;
336 } else {
337 /* assume following integer elements are all args */
338 for (i = 0; element + i < end; i++) {
339 int type = element[i].type;
340
341 if (type == ACPI_TYPE_INTEGER)
342 nargs++;
343 else if (type == ACPI_TYPE_LOCAL_REFERENCE)
344 break;
345 else
346 return -EPROTO;
347 }
348 }
349
350 if (idx++ == index) {
351 args->adev = device;
352 args->nargs = nargs;
353 for (i = 0; i < nargs; i++)
354 args->args[i] = element[i].integer.value;
355
356 return 0;
357 }
358
359 element += nargs;
360 }
361
362 return -EPROTO;
363}
364EXPORT_SYMBOL_GPL(acpi_dev_get_property_reference);