diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/Makefile | 1 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 6 | ||||
-rw-r--r-- | drivers/acpi/property.c | 364 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 2 |
4 files changed, 373 insertions, 0 deletions
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index c3b2fcb729f3..6d11522f0e48 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile | |||
@@ -47,6 +47,7 @@ acpi-y += int340x_thermal.o | |||
47 | acpi-y += power.o | 47 | acpi-y += power.o |
48 | acpi-y += event.o | 48 | acpi-y += event.o |
49 | acpi-y += sysfs.o | 49 | acpi-y += sysfs.o |
50 | acpi-y += property.o | ||
50 | acpi-$(CONFIG_X86) += acpi_cmos_rtc.o | 51 | acpi-$(CONFIG_X86) += acpi_cmos_rtc.o |
51 | acpi-$(CONFIG_DEBUG_FS) += debugfs.o | 52 | acpi-$(CONFIG_DEBUG_FS) += debugfs.o |
52 | acpi-$(CONFIG_ACPI_NUMA) += numa.o | 53 | acpi-$(CONFIG_ACPI_NUMA) += numa.o |
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 447f6d679b29..163e82f536fa 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h | |||
@@ -173,4 +173,10 @@ static inline void suspend_nvs_restore(void) {} | |||
173 | bool acpi_osi_is_win8(void); | 173 | bool acpi_osi_is_win8(void); |
174 | #endif | 174 | #endif |
175 | 175 | ||
176 | /*-------------------------------------------------------------------------- | ||
177 | Device properties | ||
178 | -------------------------------------------------------------------------- */ | ||
179 | void acpi_init_properties(struct acpi_device *adev); | ||
180 | void acpi_free_properties(struct acpi_device *adev); | ||
181 | |||
176 | #endif /* _ACPI_INTERNAL_H_ */ | 182 | #endif /* _ACPI_INTERNAL_H_ */ |
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c new file mode 100644 index 000000000000..c4a3e800e82c --- /dev/null +++ b/drivers/acpi/property.c | |||
@@ -0,0 +1,364 @@ | |||
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); | ||
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 0476e90b2091..40d80ac0552f 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
@@ -922,6 +922,7 @@ static void acpi_device_release(struct device *dev) | |||
922 | { | 922 | { |
923 | struct acpi_device *acpi_dev = to_acpi_device(dev); | 923 | struct acpi_device *acpi_dev = to_acpi_device(dev); |
924 | 924 | ||
925 | acpi_free_properties(acpi_dev); | ||
925 | acpi_free_pnp_ids(&acpi_dev->pnp); | 926 | acpi_free_pnp_ids(&acpi_dev->pnp); |
926 | acpi_free_power_resources_lists(acpi_dev); | 927 | acpi_free_power_resources_lists(acpi_dev); |
927 | kfree(acpi_dev); | 928 | kfree(acpi_dev); |
@@ -1926,6 +1927,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, | |||
1926 | acpi_set_device_status(device, sta); | 1927 | acpi_set_device_status(device, sta); |
1927 | acpi_device_get_busid(device); | 1928 | acpi_device_get_busid(device); |
1928 | acpi_set_pnp_ids(handle, &device->pnp, type); | 1929 | acpi_set_pnp_ids(handle, &device->pnp, type); |
1930 | acpi_init_properties(device); | ||
1929 | acpi_bus_get_flags(device); | 1931 | acpi_bus_get_flags(device); |
1930 | device->flags.match_driver = false; | 1932 | device->flags.match_driver = false; |
1931 | device->flags.initialized = true; | 1933 | device->flags.initialized = true; |