1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * drivers/acpi/device_sysfs.c - ACPI device sysfs attributes and modalias. |
4 | * |
5 | * Copyright (C) 2015, Intel Corp. |
6 | * Author: Mika Westerberg <mika.westerberg@linux.intel.com> |
7 | * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
8 | * |
9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
10 | * |
11 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
12 | */ |
13 | |
14 | #include <linux/acpi.h> |
15 | #include <linux/device.h> |
16 | #include <linux/export.h> |
17 | #include <linux/nls.h> |
18 | |
19 | #include "internal.h" |
20 | |
21 | static ssize_t acpi_object_path(acpi_handle handle, char *buf) |
22 | { |
23 | struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; |
24 | int result; |
25 | |
26 | result = acpi_get_name(object: handle, ACPI_FULL_PATHNAME, ret_path_ptr: &path); |
27 | if (result) |
28 | return result; |
29 | |
30 | result = sprintf(buf, fmt: "%s\n" , (char *)path.pointer); |
31 | kfree(objp: path.pointer); |
32 | return result; |
33 | } |
34 | |
35 | struct acpi_data_node_attr { |
36 | struct attribute attr; |
37 | ssize_t (*show)(struct acpi_data_node *, char *); |
38 | ssize_t (*store)(struct acpi_data_node *, const char *, size_t count); |
39 | }; |
40 | |
41 | #define DATA_NODE_ATTR(_name) \ |
42 | static struct acpi_data_node_attr data_node_##_name = \ |
43 | __ATTR(_name, 0444, data_node_show_##_name, NULL) |
44 | |
45 | static ssize_t data_node_show_path(struct acpi_data_node *dn, char *buf) |
46 | { |
47 | return dn->handle ? acpi_object_path(handle: dn->handle, buf) : 0; |
48 | } |
49 | |
50 | DATA_NODE_ATTR(path); |
51 | |
52 | static struct attribute *acpi_data_node_default_attrs[] = { |
53 | &data_node_path.attr, |
54 | NULL |
55 | }; |
56 | ATTRIBUTE_GROUPS(acpi_data_node_default); |
57 | |
58 | #define to_data_node(k) container_of(k, struct acpi_data_node, kobj) |
59 | #define to_attr(a) container_of(a, struct acpi_data_node_attr, attr) |
60 | |
61 | static ssize_t acpi_data_node_attr_show(struct kobject *kobj, |
62 | struct attribute *attr, char *buf) |
63 | { |
64 | struct acpi_data_node *dn = to_data_node(kobj); |
65 | struct acpi_data_node_attr *dn_attr = to_attr(attr); |
66 | |
67 | return dn_attr->show ? dn_attr->show(dn, buf) : -ENXIO; |
68 | } |
69 | |
70 | static const struct sysfs_ops acpi_data_node_sysfs_ops = { |
71 | .show = acpi_data_node_attr_show, |
72 | }; |
73 | |
74 | static void acpi_data_node_release(struct kobject *kobj) |
75 | { |
76 | struct acpi_data_node *dn = to_data_node(kobj); |
77 | |
78 | complete(&dn->kobj_done); |
79 | } |
80 | |
81 | static const struct kobj_type acpi_data_node_ktype = { |
82 | .sysfs_ops = &acpi_data_node_sysfs_ops, |
83 | .default_groups = acpi_data_node_default_groups, |
84 | .release = acpi_data_node_release, |
85 | }; |
86 | |
87 | static void acpi_expose_nondev_subnodes(struct kobject *kobj, |
88 | struct acpi_device_data *data) |
89 | { |
90 | struct list_head *list = &data->subnodes; |
91 | struct acpi_data_node *dn; |
92 | |
93 | if (list_empty(head: list)) |
94 | return; |
95 | |
96 | list_for_each_entry(dn, list, sibling) { |
97 | int ret; |
98 | |
99 | init_completion(x: &dn->kobj_done); |
100 | ret = kobject_init_and_add(kobj: &dn->kobj, ktype: &acpi_data_node_ktype, |
101 | parent: kobj, fmt: "%s" , dn->name); |
102 | if (!ret) |
103 | acpi_expose_nondev_subnodes(kobj: &dn->kobj, data: &dn->data); |
104 | else if (dn->handle) |
105 | acpi_handle_err(dn->handle, "Failed to expose (%d)\n" , ret); |
106 | } |
107 | } |
108 | |
109 | static void acpi_hide_nondev_subnodes(struct acpi_device_data *data) |
110 | { |
111 | struct list_head *list = &data->subnodes; |
112 | struct acpi_data_node *dn; |
113 | |
114 | if (list_empty(head: list)) |
115 | return; |
116 | |
117 | list_for_each_entry_reverse(dn, list, sibling) { |
118 | acpi_hide_nondev_subnodes(data: &dn->data); |
119 | kobject_put(kobj: &dn->kobj); |
120 | } |
121 | } |
122 | |
123 | /** |
124 | * create_pnp_modalias - Create hid/cid(s) string for modalias and uevent |
125 | * @acpi_dev: ACPI device object. |
126 | * @modalias: Buffer to print into. |
127 | * @size: Size of the buffer. |
128 | * |
129 | * Creates hid/cid(s) string needed for modalias and uevent |
130 | * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: |
131 | * char *modalias: "acpi:IBM0001:ACPI0001" |
132 | * Return: 0: no _HID and no _CID |
133 | * -EINVAL: output error |
134 | * -ENOMEM: output is truncated |
135 | */ |
136 | static int create_pnp_modalias(const struct acpi_device *acpi_dev, char *modalias, |
137 | int size) |
138 | { |
139 | int len; |
140 | int count; |
141 | struct acpi_hardware_id *id; |
142 | |
143 | /* Avoid unnecessarily loading modules for non present devices. */ |
144 | if (!acpi_device_is_present(adev: acpi_dev)) |
145 | return 0; |
146 | |
147 | /* |
148 | * Since we skip ACPI_DT_NAMESPACE_HID from the modalias below, 0 should |
149 | * be returned if ACPI_DT_NAMESPACE_HID is the only ACPI/PNP ID in the |
150 | * device's list. |
151 | */ |
152 | count = 0; |
153 | list_for_each_entry(id, &acpi_dev->pnp.ids, list) |
154 | if (strcmp(id->id, ACPI_DT_NAMESPACE_HID)) |
155 | count++; |
156 | |
157 | if (!count) |
158 | return 0; |
159 | |
160 | len = snprintf(buf: modalias, size, fmt: "acpi:" ); |
161 | if (len >= size) |
162 | return -ENOMEM; |
163 | |
164 | size -= len; |
165 | |
166 | list_for_each_entry(id, &acpi_dev->pnp.ids, list) { |
167 | if (!strcmp(id->id, ACPI_DT_NAMESPACE_HID)) |
168 | continue; |
169 | |
170 | count = snprintf(buf: &modalias[len], size, fmt: "%s:" , id->id); |
171 | |
172 | if (count >= size) |
173 | return -ENOMEM; |
174 | |
175 | len += count; |
176 | size -= count; |
177 | } |
178 | |
179 | return len; |
180 | } |
181 | |
182 | /** |
183 | * create_of_modalias - Creates DT compatible string for modalias and uevent |
184 | * @acpi_dev: ACPI device object. |
185 | * @modalias: Buffer to print into. |
186 | * @size: Size of the buffer. |
187 | * |
188 | * Expose DT compatible modalias as of:NnameTCcompatible. This function should |
189 | * only be called for devices having ACPI_DT_NAMESPACE_HID in their list of |
190 | * ACPI/PNP IDs. |
191 | */ |
192 | static int create_of_modalias(const struct acpi_device *acpi_dev, char *modalias, |
193 | int size) |
194 | { |
195 | struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; |
196 | const union acpi_object *of_compatible, *obj; |
197 | acpi_status status; |
198 | int len, count; |
199 | int i, nval; |
200 | char *c; |
201 | |
202 | status = acpi_get_name(object: acpi_dev->handle, ACPI_SINGLE_NAME, ret_path_ptr: &buf); |
203 | if (ACPI_FAILURE(status)) |
204 | return -ENODEV; |
205 | |
206 | /* DT strings are all in lower case */ |
207 | for (c = buf.pointer; *c != '\0'; c++) |
208 | *c = tolower(*c); |
209 | |
210 | len = snprintf(buf: modalias, size, fmt: "of:N%sT" , (char *)buf.pointer); |
211 | ACPI_FREE(buf.pointer); |
212 | |
213 | if (len >= size) |
214 | return -ENOMEM; |
215 | |
216 | size -= len; |
217 | |
218 | of_compatible = acpi_dev->data.of_compatible; |
219 | if (of_compatible->type == ACPI_TYPE_PACKAGE) { |
220 | nval = of_compatible->package.count; |
221 | obj = of_compatible->package.elements; |
222 | } else { /* Must be ACPI_TYPE_STRING. */ |
223 | nval = 1; |
224 | obj = of_compatible; |
225 | } |
226 | for (i = 0; i < nval; i++, obj++) { |
227 | count = snprintf(buf: &modalias[len], size, fmt: "C%s" , |
228 | obj->string.pointer); |
229 | |
230 | if (count >= size) |
231 | return -ENOMEM; |
232 | |
233 | len += count; |
234 | size -= count; |
235 | } |
236 | |
237 | return len; |
238 | } |
239 | |
240 | int __acpi_device_uevent_modalias(const struct acpi_device *adev, |
241 | struct kobj_uevent_env *env) |
242 | { |
243 | int len; |
244 | |
245 | if (!adev) |
246 | return -ENODEV; |
247 | |
248 | if (list_empty(head: &adev->pnp.ids)) |
249 | return 0; |
250 | |
251 | if (add_uevent_var(env, format: "MODALIAS=" )) |
252 | return -ENOMEM; |
253 | |
254 | if (adev->data.of_compatible) |
255 | len = create_of_modalias(acpi_dev: adev, modalias: &env->buf[env->buflen - 1], |
256 | size: sizeof(env->buf) - env->buflen); |
257 | else |
258 | len = create_pnp_modalias(acpi_dev: adev, modalias: &env->buf[env->buflen - 1], |
259 | size: sizeof(env->buf) - env->buflen); |
260 | if (len < 0) |
261 | return len; |
262 | |
263 | env->buflen += len; |
264 | |
265 | return 0; |
266 | } |
267 | |
268 | /** |
269 | * acpi_device_uevent_modalias - uevent modalias for ACPI-enumerated devices. |
270 | * @dev: Struct device to get ACPI device node. |
271 | * @env: Environment variables of the kobject uevent. |
272 | * |
273 | * Create the uevent modalias field for ACPI-enumerated devices. |
274 | * |
275 | * Because other buses do not support ACPI HIDs & CIDs, e.g. for a device with |
276 | * hid:IBM0001 and cid:ACPI0001 you get: "acpi:IBM0001:ACPI0001". |
277 | */ |
278 | int acpi_device_uevent_modalias(const struct device *dev, struct kobj_uevent_env *env) |
279 | { |
280 | return __acpi_device_uevent_modalias(adev: acpi_companion_match(dev), env); |
281 | } |
282 | EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); |
283 | |
284 | static int __acpi_device_modalias(const struct acpi_device *adev, char *buf, int size) |
285 | { |
286 | int len, count; |
287 | |
288 | if (!adev) |
289 | return -ENODEV; |
290 | |
291 | if (list_empty(head: &adev->pnp.ids)) |
292 | return 0; |
293 | |
294 | len = create_pnp_modalias(acpi_dev: adev, modalias: buf, size: size - 1); |
295 | if (len < 0) { |
296 | return len; |
297 | } else if (len > 0) { |
298 | buf[len++] = '\n'; |
299 | size -= len; |
300 | } |
301 | if (!adev->data.of_compatible) |
302 | return len; |
303 | |
304 | count = create_of_modalias(acpi_dev: adev, modalias: buf + len, size: size - 1); |
305 | if (count < 0) { |
306 | return count; |
307 | } else if (count > 0) { |
308 | len += count; |
309 | buf[len++] = '\n'; |
310 | } |
311 | |
312 | return len; |
313 | } |
314 | |
315 | /** |
316 | * acpi_device_modalias - modalias sysfs attribute for ACPI-enumerated devices. |
317 | * @dev: Struct device to get ACPI device node. |
318 | * @buf: The buffer to save pnp_modalias and of_modalias. |
319 | * @size: Size of buffer. |
320 | * |
321 | * Create the modalias sysfs attribute for ACPI-enumerated devices. |
322 | * |
323 | * Because other buses do not support ACPI HIDs & CIDs, e.g. for a device with |
324 | * hid:IBM0001 and cid:ACPI0001 you get: "acpi:IBM0001:ACPI0001". |
325 | */ |
326 | int acpi_device_modalias(struct device *dev, char *buf, int size) |
327 | { |
328 | return __acpi_device_modalias(adev: acpi_companion_match(dev), buf, size); |
329 | } |
330 | EXPORT_SYMBOL_GPL(acpi_device_modalias); |
331 | |
332 | static ssize_t |
333 | modalias_show(struct device *dev, struct device_attribute *attr, char *buf) |
334 | { |
335 | return __acpi_device_modalias(to_acpi_device(dev), buf, size: 1024); |
336 | } |
337 | static DEVICE_ATTR_RO(modalias); |
338 | |
339 | static ssize_t real_power_state_show(struct device *dev, |
340 | struct device_attribute *attr, char *buf) |
341 | { |
342 | struct acpi_device *adev = to_acpi_device(dev); |
343 | int state; |
344 | int ret; |
345 | |
346 | ret = acpi_device_get_power(device: adev, state: &state); |
347 | if (ret) |
348 | return ret; |
349 | |
350 | return sprintf(buf, fmt: "%s\n" , acpi_power_state_string(state)); |
351 | } |
352 | |
353 | static DEVICE_ATTR_RO(real_power_state); |
354 | |
355 | static ssize_t power_state_show(struct device *dev, |
356 | struct device_attribute *attr, char *buf) |
357 | { |
358 | struct acpi_device *adev = to_acpi_device(dev); |
359 | |
360 | return sprintf(buf, fmt: "%s\n" , acpi_power_state_string(state: adev->power.state)); |
361 | } |
362 | |
363 | static DEVICE_ATTR_RO(power_state); |
364 | |
365 | static ssize_t |
366 | eject_store(struct device *d, struct device_attribute *attr, |
367 | const char *buf, size_t count) |
368 | { |
369 | struct acpi_device *acpi_device = to_acpi_device(d); |
370 | acpi_object_type not_used; |
371 | acpi_status status; |
372 | |
373 | if (!count || buf[0] != '1') |
374 | return -EINVAL; |
375 | |
376 | if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled) |
377 | && !d->driver) |
378 | return -ENODEV; |
379 | |
380 | status = acpi_get_type(object: acpi_device->handle, out_type: ¬_used); |
381 | if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable) |
382 | return -ENODEV; |
383 | |
384 | acpi_dev_get(adev: acpi_device); |
385 | status = acpi_hotplug_schedule(adev: acpi_device, ACPI_OST_EC_OSPM_EJECT); |
386 | if (ACPI_SUCCESS(status)) |
387 | return count; |
388 | |
389 | acpi_dev_put(adev: acpi_device); |
390 | acpi_evaluate_ost(handle: acpi_device->handle, ACPI_OST_EC_OSPM_EJECT, |
391 | ACPI_OST_SC_NON_SPECIFIC_FAILURE, NULL); |
392 | return status == AE_NO_MEMORY ? -ENOMEM : -EAGAIN; |
393 | } |
394 | |
395 | static DEVICE_ATTR_WO(eject); |
396 | |
397 | static ssize_t |
398 | hid_show(struct device *dev, struct device_attribute *attr, char *buf) |
399 | { |
400 | struct acpi_device *acpi_dev = to_acpi_device(dev); |
401 | |
402 | return sprintf(buf, fmt: "%s\n" , acpi_device_hid(device: acpi_dev)); |
403 | } |
404 | static DEVICE_ATTR_RO(hid); |
405 | |
406 | static ssize_t uid_show(struct device *dev, |
407 | struct device_attribute *attr, char *buf) |
408 | { |
409 | struct acpi_device *acpi_dev = to_acpi_device(dev); |
410 | |
411 | return sprintf(buf, fmt: "%s\n" , acpi_device_uid(acpi_dev)); |
412 | } |
413 | static DEVICE_ATTR_RO(uid); |
414 | |
415 | static ssize_t adr_show(struct device *dev, |
416 | struct device_attribute *attr, char *buf) |
417 | { |
418 | struct acpi_device *acpi_dev = to_acpi_device(dev); |
419 | |
420 | if (acpi_dev->pnp.bus_address > U32_MAX) |
421 | return sprintf(buf, fmt: "0x%016llx\n" , acpi_dev->pnp.bus_address); |
422 | else |
423 | return sprintf(buf, fmt: "0x%08llx\n" , acpi_dev->pnp.bus_address); |
424 | } |
425 | static DEVICE_ATTR_RO(adr); |
426 | |
427 | static ssize_t path_show(struct device *dev, |
428 | struct device_attribute *attr, char *buf) |
429 | { |
430 | struct acpi_device *acpi_dev = to_acpi_device(dev); |
431 | |
432 | return acpi_object_path(handle: acpi_dev->handle, buf); |
433 | } |
434 | static DEVICE_ATTR_RO(path); |
435 | |
436 | /* sysfs file that shows description text from the ACPI _STR method */ |
437 | static ssize_t description_show(struct device *dev, |
438 | struct device_attribute *attr, |
439 | char *buf) |
440 | { |
441 | struct acpi_device *acpi_dev = to_acpi_device(dev); |
442 | int result; |
443 | |
444 | if (acpi_dev->pnp.str_obj == NULL) |
445 | return 0; |
446 | |
447 | /* |
448 | * The _STR object contains a Unicode identifier for a device. |
449 | * We need to convert to utf-8 so it can be displayed. |
450 | */ |
451 | result = utf16s_to_utf8s( |
452 | pwcs: (wchar_t *)acpi_dev->pnp.str_obj->buffer.pointer, |
453 | len: acpi_dev->pnp.str_obj->buffer.length, |
454 | endian: UTF16_LITTLE_ENDIAN, s: buf, |
455 | PAGE_SIZE - 1); |
456 | |
457 | buf[result++] = '\n'; |
458 | |
459 | return result; |
460 | } |
461 | static DEVICE_ATTR_RO(description); |
462 | |
463 | static ssize_t |
464 | sun_show(struct device *dev, struct device_attribute *attr, |
465 | char *buf) |
466 | { |
467 | struct acpi_device *acpi_dev = to_acpi_device(dev); |
468 | acpi_status status; |
469 | unsigned long long sun; |
470 | |
471 | status = acpi_evaluate_integer(handle: acpi_dev->handle, pathname: "_SUN" , NULL, data: &sun); |
472 | if (ACPI_FAILURE(status)) |
473 | return -EIO; |
474 | |
475 | return sprintf(buf, fmt: "%llu\n" , sun); |
476 | } |
477 | static DEVICE_ATTR_RO(sun); |
478 | |
479 | static ssize_t |
480 | hrv_show(struct device *dev, struct device_attribute *attr, |
481 | char *buf) |
482 | { |
483 | struct acpi_device *acpi_dev = to_acpi_device(dev); |
484 | acpi_status status; |
485 | unsigned long long hrv; |
486 | |
487 | status = acpi_evaluate_integer(handle: acpi_dev->handle, pathname: "_HRV" , NULL, data: &hrv); |
488 | if (ACPI_FAILURE(status)) |
489 | return -EIO; |
490 | |
491 | return sprintf(buf, fmt: "%llu\n" , hrv); |
492 | } |
493 | static DEVICE_ATTR_RO(hrv); |
494 | |
495 | static ssize_t status_show(struct device *dev, struct device_attribute *attr, |
496 | char *buf) |
497 | { |
498 | struct acpi_device *acpi_dev = to_acpi_device(dev); |
499 | acpi_status status; |
500 | unsigned long long sta; |
501 | |
502 | status = acpi_evaluate_integer(handle: acpi_dev->handle, pathname: "_STA" , NULL, data: &sta); |
503 | if (ACPI_FAILURE(status)) |
504 | return -EIO; |
505 | |
506 | return sprintf(buf, fmt: "%llu\n" , sta); |
507 | } |
508 | static DEVICE_ATTR_RO(status); |
509 | |
510 | /** |
511 | * acpi_device_setup_files - Create sysfs attributes of an ACPI device. |
512 | * @dev: ACPI device object. |
513 | */ |
514 | int acpi_device_setup_files(struct acpi_device *dev) |
515 | { |
516 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; |
517 | acpi_status status; |
518 | int result = 0; |
519 | |
520 | /* |
521 | * Devices gotten from FADT don't have a "path" attribute |
522 | */ |
523 | if (dev->handle) { |
524 | result = device_create_file(device: &dev->dev, entry: &dev_attr_path); |
525 | if (result) |
526 | goto end; |
527 | } |
528 | |
529 | if (!list_empty(head: &dev->pnp.ids)) { |
530 | result = device_create_file(device: &dev->dev, entry: &dev_attr_hid); |
531 | if (result) |
532 | goto end; |
533 | |
534 | result = device_create_file(device: &dev->dev, entry: &dev_attr_modalias); |
535 | if (result) |
536 | goto end; |
537 | } |
538 | |
539 | /* |
540 | * If device has _STR, 'description' file is created |
541 | */ |
542 | if (acpi_has_method(handle: dev->handle, name: "_STR" )) { |
543 | status = acpi_evaluate_object(object: dev->handle, pathname: "_STR" , |
544 | NULL, return_object_buffer: &buffer); |
545 | if (ACPI_FAILURE(status)) |
546 | buffer.pointer = NULL; |
547 | dev->pnp.str_obj = buffer.pointer; |
548 | result = device_create_file(device: &dev->dev, entry: &dev_attr_description); |
549 | if (result) |
550 | goto end; |
551 | } |
552 | |
553 | if (dev->pnp.type.bus_address) |
554 | result = device_create_file(device: &dev->dev, entry: &dev_attr_adr); |
555 | if (acpi_device_uid(dev)) |
556 | result = device_create_file(device: &dev->dev, entry: &dev_attr_uid); |
557 | |
558 | if (acpi_has_method(handle: dev->handle, name: "_SUN" )) { |
559 | result = device_create_file(device: &dev->dev, entry: &dev_attr_sun); |
560 | if (result) |
561 | goto end; |
562 | } |
563 | |
564 | if (acpi_has_method(handle: dev->handle, name: "_HRV" )) { |
565 | result = device_create_file(device: &dev->dev, entry: &dev_attr_hrv); |
566 | if (result) |
567 | goto end; |
568 | } |
569 | |
570 | if (acpi_has_method(handle: dev->handle, name: "_STA" )) { |
571 | result = device_create_file(device: &dev->dev, entry: &dev_attr_status); |
572 | if (result) |
573 | goto end; |
574 | } |
575 | |
576 | /* |
577 | * If device has _EJ0, 'eject' file is created that is used to trigger |
578 | * hot-removal function from userland. |
579 | */ |
580 | if (acpi_has_method(handle: dev->handle, name: "_EJ0" )) { |
581 | result = device_create_file(device: &dev->dev, entry: &dev_attr_eject); |
582 | if (result) |
583 | return result; |
584 | } |
585 | |
586 | if (dev->flags.power_manageable) { |
587 | result = device_create_file(device: &dev->dev, entry: &dev_attr_power_state); |
588 | if (result) |
589 | return result; |
590 | |
591 | if (dev->power.flags.power_resources) |
592 | result = device_create_file(device: &dev->dev, |
593 | entry: &dev_attr_real_power_state); |
594 | } |
595 | |
596 | acpi_expose_nondev_subnodes(kobj: &dev->dev.kobj, data: &dev->data); |
597 | |
598 | end: |
599 | return result; |
600 | } |
601 | |
602 | /** |
603 | * acpi_device_remove_files - Remove sysfs attributes of an ACPI device. |
604 | * @dev: ACPI device object. |
605 | */ |
606 | void acpi_device_remove_files(struct acpi_device *dev) |
607 | { |
608 | acpi_hide_nondev_subnodes(data: &dev->data); |
609 | |
610 | if (dev->flags.power_manageable) { |
611 | device_remove_file(dev: &dev->dev, attr: &dev_attr_power_state); |
612 | if (dev->power.flags.power_resources) |
613 | device_remove_file(dev: &dev->dev, |
614 | attr: &dev_attr_real_power_state); |
615 | } |
616 | |
617 | /* |
618 | * If device has _STR, remove 'description' file |
619 | */ |
620 | if (acpi_has_method(handle: dev->handle, name: "_STR" )) { |
621 | kfree(objp: dev->pnp.str_obj); |
622 | device_remove_file(dev: &dev->dev, attr: &dev_attr_description); |
623 | } |
624 | /* |
625 | * If device has _EJ0, remove 'eject' file. |
626 | */ |
627 | if (acpi_has_method(handle: dev->handle, name: "_EJ0" )) |
628 | device_remove_file(dev: &dev->dev, attr: &dev_attr_eject); |
629 | |
630 | if (acpi_has_method(handle: dev->handle, name: "_SUN" )) |
631 | device_remove_file(dev: &dev->dev, attr: &dev_attr_sun); |
632 | |
633 | if (acpi_has_method(handle: dev->handle, name: "_HRV" )) |
634 | device_remove_file(dev: &dev->dev, attr: &dev_attr_hrv); |
635 | |
636 | if (acpi_device_uid(dev)) |
637 | device_remove_file(dev: &dev->dev, attr: &dev_attr_uid); |
638 | if (dev->pnp.type.bus_address) |
639 | device_remove_file(dev: &dev->dev, attr: &dev_attr_adr); |
640 | device_remove_file(dev: &dev->dev, attr: &dev_attr_modalias); |
641 | device_remove_file(dev: &dev->dev, attr: &dev_attr_hid); |
642 | if (acpi_has_method(handle: dev->handle, name: "_STA" )) |
643 | device_remove_file(dev: &dev->dev, attr: &dev_attr_status); |
644 | if (dev->handle) |
645 | device_remove_file(dev: &dev->dev, attr: &dev_attr_path); |
646 | } |
647 | |