]>
Commit | Line | Data |
---|---|---|
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 */ | |
23 | static 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 | ||
28 | static 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 | ||
59 | static 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 | ||
79 | void 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 | ||
130 | void 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 | */ | |
155 | int 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 | } | |
188 | EXPORT_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 | */ | |
209 | int 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 | } | |
231 | EXPORT_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 | */ | |
254 | int 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 | } | |
364 | EXPORT_SYMBOL_GPL(acpi_dev_get_property_reference); |