diff options
-rw-r--r-- | drivers/acpi/scan.c | 249 |
1 files changed, 155 insertions, 94 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8f3adf924e83..d2e3c3e3f9c9 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
@@ -114,7 +114,12 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, | |||
114 | return 0; | 114 | return 0; |
115 | } | 115 | } |
116 | 116 | ||
117 | /* | 117 | /** |
118 | * create_pnp_modalias - Create hid/cid(s) string for modalias and uevent | ||
119 | * @acpi_dev: ACPI device object. | ||
120 | * @modalias: Buffer to print into. | ||
121 | * @size: Size of the buffer. | ||
122 | * | ||
118 | * Creates hid/cid(s) string needed for modalias and uevent | 123 | * Creates hid/cid(s) string needed for modalias and uevent |
119 | * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: | 124 | * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: |
120 | * char *modalias: "acpi:IBM0001:ACPI0001" | 125 | * char *modalias: "acpi:IBM0001:ACPI0001" |
@@ -122,68 +127,98 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, | |||
122 | * -EINVAL: output error | 127 | * -EINVAL: output error |
123 | * -ENOMEM: output is truncated | 128 | * -ENOMEM: output is truncated |
124 | */ | 129 | */ |
125 | static int create_modalias(struct acpi_device *acpi_dev, char *modalias, | 130 | static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias, |
126 | int size) | 131 | int size) |
127 | { | 132 | { |
128 | int len; | 133 | int len; |
129 | int count; | 134 | int count; |
130 | struct acpi_hardware_id *id; | 135 | struct acpi_hardware_id *id; |
131 | 136 | ||
132 | if (list_empty(&acpi_dev->pnp.ids)) | ||
133 | return 0; | ||
134 | |||
135 | /* | 137 | /* |
136 | * If the device has PRP0001 we expose DT compatible modalias | 138 | * Since we skip PRP0001 from the modalias below, 0 should be returned |
137 | * instead in form of of:NnameTCcompatible. | 139 | * if PRP0001 is the only ACPI/PNP ID in the device's list. |
138 | */ | 140 | */ |
139 | if (acpi_dev->data.of_compatible) { | 141 | count = 0; |
140 | struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; | 142 | list_for_each_entry(id, &acpi_dev->pnp.ids, list) |
141 | const union acpi_object *of_compatible, *obj; | 143 | if (strcmp(id->id, "PRP0001")) |
142 | int i, nval; | 144 | count++; |
143 | char *c; | ||
144 | |||
145 | acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf); | ||
146 | /* DT strings are all in lower case */ | ||
147 | for (c = buf.pointer; *c != '\0'; c++) | ||
148 | *c = tolower(*c); | ||
149 | |||
150 | len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer); | ||
151 | ACPI_FREE(buf.pointer); | ||
152 | |||
153 | of_compatible = acpi_dev->data.of_compatible; | ||
154 | if (of_compatible->type == ACPI_TYPE_PACKAGE) { | ||
155 | nval = of_compatible->package.count; | ||
156 | obj = of_compatible->package.elements; | ||
157 | } else { /* Must be ACPI_TYPE_STRING. */ | ||
158 | nval = 1; | ||
159 | obj = of_compatible; | ||
160 | } | ||
161 | for (i = 0; i < nval; i++, obj++) { | ||
162 | count = snprintf(&modalias[len], size, "C%s", | ||
163 | obj->string.pointer); | ||
164 | if (count < 0) | ||
165 | return -EINVAL; | ||
166 | if (count >= size) | ||
167 | return -ENOMEM; | ||
168 | |||
169 | len += count; | ||
170 | size -= count; | ||
171 | } | ||
172 | } else { | ||
173 | len = snprintf(modalias, size, "acpi:"); | ||
174 | size -= len; | ||
175 | 145 | ||
176 | list_for_each_entry(id, &acpi_dev->pnp.ids, list) { | 146 | if (!count) |
177 | count = snprintf(&modalias[len], size, "%s:", id->id); | 147 | return 0; |
178 | if (count < 0) | 148 | |
179 | return -EINVAL; | 149 | len = snprintf(modalias, size, "acpi:"); |
180 | if (count >= size) | 150 | if (len <= 0) |
181 | return -ENOMEM; | 151 | return len; |
182 | len += count; | 152 | |
183 | size -= count; | 153 | size -= len; |
184 | } | 154 | |
155 | list_for_each_entry(id, &acpi_dev->pnp.ids, list) { | ||
156 | if (!strcmp(id->id, "PRP0001")) | ||
157 | continue; | ||
158 | |||
159 | count = snprintf(&modalias[len], size, "%s:", id->id); | ||
160 | if (count < 0) | ||
161 | return -EINVAL; | ||
162 | |||
163 | if (count >= size) | ||
164 | return -ENOMEM; | ||
165 | |||
166 | len += count; | ||
167 | size -= count; | ||
185 | } | 168 | } |
169 | modalias[len] = '\0'; | ||
170 | return len; | ||
171 | } | ||
172 | |||
173 | /** | ||
174 | * create_of_modalias - Creates DT compatible string for modalias and uevent | ||
175 | * @acpi_dev: ACPI device object. | ||
176 | * @modalias: Buffer to print into. | ||
177 | * @size: Size of the buffer. | ||
178 | * | ||
179 | * Expose DT compatible modalias as of:NnameTCcompatible. This function should | ||
180 | * only be called for devices having PRP0001 in their list of ACPI/PNP IDs. | ||
181 | */ | ||
182 | static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias, | ||
183 | int size) | ||
184 | { | ||
185 | struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; | ||
186 | const union acpi_object *of_compatible, *obj; | ||
187 | int len, count; | ||
188 | int i, nval; | ||
189 | char *c; | ||
186 | 190 | ||
191 | acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf); | ||
192 | /* DT strings are all in lower case */ | ||
193 | for (c = buf.pointer; *c != '\0'; c++) | ||
194 | *c = tolower(*c); | ||
195 | |||
196 | len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer); | ||
197 | ACPI_FREE(buf.pointer); | ||
198 | |||
199 | if (len <= 0) | ||
200 | return len; | ||
201 | |||
202 | of_compatible = acpi_dev->data.of_compatible; | ||
203 | if (of_compatible->type == ACPI_TYPE_PACKAGE) { | ||
204 | nval = of_compatible->package.count; | ||
205 | obj = of_compatible->package.elements; | ||
206 | } else { /* Must be ACPI_TYPE_STRING. */ | ||
207 | nval = 1; | ||
208 | obj = of_compatible; | ||
209 | } | ||
210 | for (i = 0; i < nval; i++, obj++) { | ||
211 | count = snprintf(&modalias[len], size, "C%s", | ||
212 | obj->string.pointer); | ||
213 | if (count < 0) | ||
214 | return -EINVAL; | ||
215 | |||
216 | if (count >= size) | ||
217 | return -ENOMEM; | ||
218 | |||
219 | len += count; | ||
220 | size -= count; | ||
221 | } | ||
187 | modalias[len] = '\0'; | 222 | modalias[len] = '\0'; |
188 | return len; | 223 | return len; |
189 | } | 224 | } |
@@ -236,61 +271,100 @@ static struct acpi_device *acpi_companion_match(const struct device *dev) | |||
236 | return adev; | 271 | return adev; |
237 | } | 272 | } |
238 | 273 | ||
239 | /* | 274 | static int __acpi_device_uevent_modalias(struct acpi_device *adev, |
240 | * Creates uevent modalias field for ACPI enumerated devices. | 275 | struct kobj_uevent_env *env) |
241 | * Because the other buses does not support ACPI HIDs & CIDs. | ||
242 | * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: | ||
243 | * "acpi:IBM0001:ACPI0001" | ||
244 | */ | ||
245 | int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) | ||
246 | { | 276 | { |
247 | int len; | 277 | int len; |
248 | 278 | ||
249 | if (!acpi_companion_match(dev)) | 279 | if (!adev) |
250 | return -ENODEV; | 280 | return -ENODEV; |
251 | 281 | ||
282 | if (list_empty(&adev->pnp.ids)) | ||
283 | return 0; | ||
284 | |||
252 | if (add_uevent_var(env, "MODALIAS=")) | 285 | if (add_uevent_var(env, "MODALIAS=")) |
253 | return -ENOMEM; | 286 | return -ENOMEM; |
254 | len = create_modalias(ACPI_COMPANION(dev), &env->buf[env->buflen - 1], | 287 | |
255 | sizeof(env->buf) - env->buflen); | 288 | len = create_pnp_modalias(adev, &env->buf[env->buflen - 1], |
256 | if (len <= 0) | 289 | sizeof(env->buf) - env->buflen); |
290 | if (len < 0) | ||
257 | return len; | 291 | return len; |
292 | |||
293 | env->buflen += len; | ||
294 | if (!adev->data.of_compatible) | ||
295 | return 0; | ||
296 | |||
297 | if (len > 0 && add_uevent_var(env, "MODALIAS=")) | ||
298 | return -ENOMEM; | ||
299 | |||
300 | len = create_of_modalias(adev, &env->buf[env->buflen - 1], | ||
301 | sizeof(env->buf) - env->buflen); | ||
302 | if (len < 0) | ||
303 | return len; | ||
304 | |||
258 | env->buflen += len; | 305 | env->buflen += len; |
306 | |||
259 | return 0; | 307 | return 0; |
260 | } | 308 | } |
261 | EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); | ||
262 | 309 | ||
263 | /* | 310 | /* |
264 | * Creates modalias sysfs attribute for ACPI enumerated devices. | 311 | * Creates uevent modalias field for ACPI enumerated devices. |
265 | * Because the other buses does not support ACPI HIDs & CIDs. | 312 | * Because the other buses does not support ACPI HIDs & CIDs. |
266 | * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: | 313 | * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: |
267 | * "acpi:IBM0001:ACPI0001" | 314 | * "acpi:IBM0001:ACPI0001" |
268 | */ | 315 | */ |
269 | int acpi_device_modalias(struct device *dev, char *buf, int size) | 316 | int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) |
270 | { | 317 | { |
271 | int len; | 318 | return __acpi_device_uevent_modalias(acpi_companion_match(dev), env); |
319 | } | ||
320 | EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); | ||
321 | |||
322 | static int __acpi_device_modalias(struct acpi_device *adev, char *buf, int size) | ||
323 | { | ||
324 | int len, count; | ||
272 | 325 | ||
273 | if (!acpi_companion_match(dev)) | 326 | if (!adev) |
274 | return -ENODEV; | 327 | return -ENODEV; |
275 | 328 | ||
276 | len = create_modalias(ACPI_COMPANION(dev), buf, size -1); | 329 | if (list_empty(&adev->pnp.ids)) |
277 | if (len <= 0) | 330 | return 0; |
331 | |||
332 | len = create_pnp_modalias(adev, buf, size - 1); | ||
333 | if (len < 0) { | ||
278 | return len; | 334 | return len; |
279 | buf[len++] = '\n'; | 335 | } else if (len > 0) { |
336 | buf[len++] = '\n'; | ||
337 | size -= len; | ||
338 | } | ||
339 | if (!adev->data.of_compatible) | ||
340 | return len; | ||
341 | |||
342 | count = create_of_modalias(adev, buf + len, size - 1); | ||
343 | if (count < 0) { | ||
344 | return count; | ||
345 | } else if (count > 0) { | ||
346 | len += count; | ||
347 | buf[len++] = '\n'; | ||
348 | } | ||
349 | |||
280 | return len; | 350 | return len; |
281 | } | 351 | } |
352 | |||
353 | /* | ||
354 | * Creates modalias sysfs attribute for ACPI enumerated devices. | ||
355 | * Because the other buses does not support ACPI HIDs & CIDs. | ||
356 | * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: | ||
357 | * "acpi:IBM0001:ACPI0001" | ||
358 | */ | ||
359 | int acpi_device_modalias(struct device *dev, char *buf, int size) | ||
360 | { | ||
361 | return __acpi_device_modalias(acpi_companion_match(dev), buf, size); | ||
362 | } | ||
282 | EXPORT_SYMBOL_GPL(acpi_device_modalias); | 363 | EXPORT_SYMBOL_GPL(acpi_device_modalias); |
283 | 364 | ||
284 | static ssize_t | 365 | static ssize_t |
285 | acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { | 366 | acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { |
286 | struct acpi_device *acpi_dev = to_acpi_device(dev); | 367 | return __acpi_device_modalias(to_acpi_device(dev), buf, 1024); |
287 | int len; | ||
288 | |||
289 | len = create_modalias(acpi_dev, buf, 1024); | ||
290 | if (len <= 0) | ||
291 | return len; | ||
292 | buf[len++] = '\n'; | ||
293 | return len; | ||
294 | } | 368 | } |
295 | static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); | 369 | static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); |
296 | 370 | ||
@@ -1046,20 +1120,7 @@ static int acpi_bus_match(struct device *dev, struct device_driver *drv) | |||
1046 | 1120 | ||
1047 | static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env) | 1121 | static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env) |
1048 | { | 1122 | { |
1049 | struct acpi_device *acpi_dev = to_acpi_device(dev); | 1123 | return __acpi_device_uevent_modalias(to_acpi_device(dev), env); |
1050 | int len; | ||
1051 | |||
1052 | if (list_empty(&acpi_dev->pnp.ids)) | ||
1053 | return 0; | ||
1054 | |||
1055 | if (add_uevent_var(env, "MODALIAS=")) | ||
1056 | return -ENOMEM; | ||
1057 | len = create_modalias(acpi_dev, &env->buf[env->buflen - 1], | ||
1058 | sizeof(env->buf) - env->buflen); | ||
1059 | if (len <= 0) | ||
1060 | return len; | ||
1061 | env->buflen += len; | ||
1062 | return 0; | ||
1063 | } | 1124 | } |
1064 | 1125 | ||
1065 | static void acpi_device_notify(acpi_handle handle, u32 event, void *data) | 1126 | static void acpi_device_notify(acpi_handle handle, u32 event, void *data) |