Commit | Line | Data |
---|---|---|
899596e0 LW |
1 | /* |
2 | * apple.c - Apple ACPI quirks | |
3 | * Copyright (C) 2017 Lukas Wunner <lukas@wunner.de> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License (version 2) as | |
7 | * published by the Free Software Foundation. | |
8 | */ | |
9 | ||
10 | #include <linux/acpi.h> | |
11 | #include <linux/bitmap.h> | |
12 | #include <linux/platform_data/x86/apple.h> | |
13 | #include <linux/uuid.h> | |
14 | ||
15 | /* Apple _DSM device properties GUID */ | |
16 | static const guid_t apple_prp_guid = | |
17 | GUID_INIT(0xa0b5b7c6, 0x1318, 0x441c, | |
18 | 0xb0, 0xc9, 0xfe, 0x69, 0x5e, 0xaf, 0x94, 0x9b); | |
19 | ||
20 | /** | |
21 | * acpi_extract_apple_properties - retrieve and convert Apple _DSM properties | |
22 | * @adev: ACPI device for which to retrieve the properties | |
23 | * | |
24 | * Invoke Apple's custom _DSM once to check the protocol version and once more | |
25 | * to retrieve the properties. They are marshalled up in a single package as | |
26 | * alternating key/value elements, unlike _DSD which stores them as a package | |
27 | * of 2-element packages. Convert to _DSD format and make them available under | |
28 | * the primary fwnode. | |
29 | */ | |
30 | void acpi_extract_apple_properties(struct acpi_device *adev) | |
31 | { | |
32 | unsigned int i, j = 0, newsize = 0, numprops, numvalid; | |
33 | union acpi_object *props, *newprops; | |
34 | unsigned long *valid = NULL; | |
35 | void *free_space; | |
36 | ||
37 | if (!x86_apple_machine) | |
38 | return; | |
39 | ||
40 | props = acpi_evaluate_dsm_typed(adev->handle, &apple_prp_guid, 1, 0, | |
41 | NULL, ACPI_TYPE_BUFFER); | |
42 | if (!props) | |
43 | return; | |
44 | ||
45 | if (!props->buffer.length) | |
46 | goto out_free; | |
47 | ||
48 | if (props->buffer.pointer[0] != 3) { | |
49 | acpi_handle_info(adev->handle, FW_INFO | |
50 | "unsupported properties version %*ph\n", | |
51 | props->buffer.length, props->buffer.pointer); | |
52 | goto out_free; | |
53 | } | |
54 | ||
55 | ACPI_FREE(props); | |
56 | props = acpi_evaluate_dsm_typed(adev->handle, &apple_prp_guid, 1, 1, | |
57 | NULL, ACPI_TYPE_PACKAGE); | |
58 | if (!props) | |
59 | return; | |
60 | ||
61 | numprops = props->package.count / 2; | |
62 | if (!numprops) | |
63 | goto out_free; | |
64 | ||
d030fd0e | 65 | valid = bitmap_zalloc(numprops, GFP_KERNEL); |
899596e0 LW |
66 | if (!valid) |
67 | goto out_free; | |
68 | ||
69 | /* newsize = key length + value length of each tuple */ | |
70 | for (i = 0; i < numprops; i++) { | |
71 | union acpi_object *key = &props->package.elements[i * 2]; | |
72 | union acpi_object *val = &props->package.elements[i * 2 + 1]; | |
73 | ||
74 | if ( key->type != ACPI_TYPE_STRING || | |
75 | (val->type != ACPI_TYPE_INTEGER && | |
76 | val->type != ACPI_TYPE_BUFFER)) | |
77 | continue; /* skip invalid properties */ | |
78 | ||
79 | __set_bit(i, valid); | |
80 | newsize += key->string.length + 1; | |
81 | if ( val->type == ACPI_TYPE_BUFFER) | |
82 | newsize += val->buffer.length; | |
83 | } | |
84 | ||
85 | numvalid = bitmap_weight(valid, numprops); | |
86 | if (numprops > numvalid) | |
87 | acpi_handle_info(adev->handle, FW_INFO | |
88 | "skipped %u properties: wrong type\n", | |
89 | numprops - numvalid); | |
90 | if (numvalid == 0) | |
91 | goto out_free; | |
92 | ||
93 | /* newsize += top-level package + 3 objects for each key/value tuple */ | |
94 | newsize += (1 + 3 * numvalid) * sizeof(union acpi_object); | |
95 | newprops = ACPI_ALLOCATE_ZEROED(newsize); | |
96 | if (!newprops) | |
97 | goto out_free; | |
98 | ||
99 | /* layout: top-level package | packages | key/value tuples | strings */ | |
100 | newprops->type = ACPI_TYPE_PACKAGE; | |
101 | newprops->package.count = numvalid; | |
102 | newprops->package.elements = &newprops[1]; | |
103 | free_space = &newprops[1 + 3 * numvalid]; | |
104 | ||
105 | for_each_set_bit(i, valid, numprops) { | |
106 | union acpi_object *key = &props->package.elements[i * 2]; | |
107 | union acpi_object *val = &props->package.elements[i * 2 + 1]; | |
108 | unsigned int k = 1 + numvalid + j * 2; /* index into newprops */ | |
109 | unsigned int v = k + 1; | |
110 | ||
111 | newprops[1 + j].type = ACPI_TYPE_PACKAGE; | |
112 | newprops[1 + j].package.count = 2; | |
113 | newprops[1 + j].package.elements = &newprops[k]; | |
114 | ||
115 | newprops[k].type = ACPI_TYPE_STRING; | |
116 | newprops[k].string.length = key->string.length; | |
117 | newprops[k].string.pointer = free_space; | |
118 | memcpy(free_space, key->string.pointer, key->string.length); | |
119 | free_space += key->string.length + 1; | |
120 | ||
121 | newprops[v].type = val->type; | |
122 | if (val->type == ACPI_TYPE_INTEGER) { | |
123 | newprops[v].integer.value = val->integer.value; | |
124 | } else { | |
125 | newprops[v].buffer.length = val->buffer.length; | |
126 | newprops[v].buffer.pointer = free_space; | |
127 | memcpy(free_space, val->buffer.pointer, | |
128 | val->buffer.length); | |
129 | free_space += val->buffer.length; | |
130 | } | |
131 | j++; /* count valid properties */ | |
132 | } | |
133 | WARN_ON(free_space != (void *)newprops + newsize); | |
134 | ||
135 | adev->data.properties = newprops; | |
136 | adev->data.pointer = newprops; | |
137 | ||
138 | out_free: | |
139 | ACPI_FREE(props); | |
d030fd0e | 140 | bitmap_free(valid); |
899596e0 | 141 | } |