diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-08-26 22:37:19 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-09-14 19:47:34 -0400 |
commit | 263b4c1a64bc12470684aeaf7c44f03d31716819 (patch) | |
tree | 80442913eae7918339e5f2ee2629317795b5e4b7 | |
parent | 445b0eb058f5f31c844a731cb82e7441d0d9e578 (diff) |
ACPI / property: Expose data-only subnodes via sysfs
Add infrastructure needed to expose data-only subnodes of ACPI
device objects introduced previously via sysfs.
Each data-only subnode is represented as a sysfs directory under
the directory corresponding to its parent object (a device or a
data-only subnode). Each of them has a "path" attribute (containing
the full ACPI namespace path to the object the subnode data come from)
at this time.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-rw-r--r-- | drivers/acpi/device_sysfs.c | 120 | ||||
-rw-r--r-- | drivers/acpi/property.c | 8 | ||||
-rw-r--r-- | include/acpi/acpi_bus.h | 3 |
3 files changed, 116 insertions, 15 deletions
diff --git a/drivers/acpi/device_sysfs.c b/drivers/acpi/device_sysfs.c index 4ab4582e586b..707cf6213bc2 100644 --- a/drivers/acpi/device_sysfs.c +++ b/drivers/acpi/device_sysfs.c | |||
@@ -26,6 +26,106 @@ | |||
26 | 26 | ||
27 | #include "internal.h" | 27 | #include "internal.h" |
28 | 28 | ||
29 | static ssize_t acpi_object_path(acpi_handle handle, char *buf) | ||
30 | { | ||
31 | struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
32 | int result; | ||
33 | |||
34 | result = acpi_get_name(handle, ACPI_FULL_PATHNAME, &path); | ||
35 | if (result) | ||
36 | return result; | ||
37 | |||
38 | result = sprintf(buf, "%s\n", (char*)path.pointer); | ||
39 | kfree(path.pointer); | ||
40 | return result; | ||
41 | } | ||
42 | |||
43 | struct acpi_data_node_attr { | ||
44 | struct attribute attr; | ||
45 | ssize_t (*show)(struct acpi_data_node *, char *); | ||
46 | ssize_t (*store)(struct acpi_data_node *, const char *, size_t count); | ||
47 | }; | ||
48 | |||
49 | #define DATA_NODE_ATTR(_name) \ | ||
50 | static struct acpi_data_node_attr data_node_##_name = \ | ||
51 | __ATTR(_name, 0444, data_node_show_##_name, NULL) | ||
52 | |||
53 | static ssize_t data_node_show_path(struct acpi_data_node *dn, char *buf) | ||
54 | { | ||
55 | return acpi_object_path(dn->handle, buf); | ||
56 | } | ||
57 | |||
58 | DATA_NODE_ATTR(path); | ||
59 | |||
60 | static struct attribute *acpi_data_node_default_attrs[] = { | ||
61 | &data_node_path.attr, | ||
62 | NULL | ||
63 | }; | ||
64 | |||
65 | #define to_data_node(k) container_of(k, struct acpi_data_node, kobj) | ||
66 | #define to_attr(a) container_of(a, struct acpi_data_node_attr, attr) | ||
67 | |||
68 | static ssize_t acpi_data_node_attr_show(struct kobject *kobj, | ||
69 | struct attribute *attr, char *buf) | ||
70 | { | ||
71 | struct acpi_data_node *dn = to_data_node(kobj); | ||
72 | struct acpi_data_node_attr *dn_attr = to_attr(attr); | ||
73 | |||
74 | return dn_attr->show ? dn_attr->show(dn, buf) : -ENXIO; | ||
75 | } | ||
76 | |||
77 | static const struct sysfs_ops acpi_data_node_sysfs_ops = { | ||
78 | .show = acpi_data_node_attr_show, | ||
79 | }; | ||
80 | |||
81 | static void acpi_data_node_release(struct kobject *kobj) | ||
82 | { | ||
83 | struct acpi_data_node *dn = to_data_node(kobj); | ||
84 | complete(&dn->kobj_done); | ||
85 | } | ||
86 | |||
87 | static struct kobj_type acpi_data_node_ktype = { | ||
88 | .sysfs_ops = &acpi_data_node_sysfs_ops, | ||
89 | .default_attrs = acpi_data_node_default_attrs, | ||
90 | .release = acpi_data_node_release, | ||
91 | }; | ||
92 | |||
93 | static void acpi_expose_nondev_subnodes(struct kobject *kobj, | ||
94 | struct acpi_device_data *data) | ||
95 | { | ||
96 | struct list_head *list = &data->subnodes; | ||
97 | struct acpi_data_node *dn; | ||
98 | |||
99 | if (list_empty(list)) | ||
100 | return; | ||
101 | |||
102 | list_for_each_entry(dn, list, sibling) { | ||
103 | int ret; | ||
104 | |||
105 | init_completion(&dn->kobj_done); | ||
106 | ret = kobject_init_and_add(&dn->kobj, &acpi_data_node_ktype, | ||
107 | kobj, dn->name); | ||
108 | if (ret) | ||
109 | acpi_handle_err(dn->handle, "Failed to expose (%d)\n", ret); | ||
110 | else | ||
111 | acpi_expose_nondev_subnodes(&dn->kobj, &dn->data); | ||
112 | } | ||
113 | } | ||
114 | |||
115 | static void acpi_hide_nondev_subnodes(struct acpi_device_data *data) | ||
116 | { | ||
117 | struct list_head *list = &data->subnodes; | ||
118 | struct acpi_data_node *dn; | ||
119 | |||
120 | if (list_empty(list)) | ||
121 | return; | ||
122 | |||
123 | list_for_each_entry_reverse(dn, list, sibling) { | ||
124 | acpi_hide_nondev_subnodes(&dn->data); | ||
125 | kobject_put(&dn->kobj); | ||
126 | } | ||
127 | } | ||
128 | |||
29 | /** | 129 | /** |
30 | * create_pnp_modalias - Create hid/cid(s) string for modalias and uevent | 130 | * create_pnp_modalias - Create hid/cid(s) string for modalias and uevent |
31 | * @acpi_dev: ACPI device object. | 131 | * @acpi_dev: ACPI device object. |
@@ -323,20 +423,12 @@ static ssize_t acpi_device_adr_show(struct device *dev, | |||
323 | } | 423 | } |
324 | static DEVICE_ATTR(adr, 0444, acpi_device_adr_show, NULL); | 424 | static DEVICE_ATTR(adr, 0444, acpi_device_adr_show, NULL); |
325 | 425 | ||
326 | static ssize_t | 426 | static ssize_t acpi_device_path_show(struct device *dev, |
327 | acpi_device_path_show(struct device *dev, struct device_attribute *attr, char *buf) { | 427 | struct device_attribute *attr, char *buf) |
428 | { | ||
328 | struct acpi_device *acpi_dev = to_acpi_device(dev); | 429 | struct acpi_device *acpi_dev = to_acpi_device(dev); |
329 | struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
330 | int result; | ||
331 | |||
332 | result = acpi_get_name(acpi_dev->handle, ACPI_FULL_PATHNAME, &path); | ||
333 | if (result) | ||
334 | goto end; | ||
335 | 430 | ||
336 | result = sprintf(buf, "%s\n", (char*)path.pointer); | 431 | return acpi_object_path(acpi_dev->handle, buf); |
337 | kfree(path.pointer); | ||
338 | end: | ||
339 | return result; | ||
340 | } | 432 | } |
341 | static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL); | 433 | static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL); |
342 | 434 | ||
@@ -475,6 +567,8 @@ int acpi_device_setup_files(struct acpi_device *dev) | |||
475 | &dev_attr_real_power_state); | 567 | &dev_attr_real_power_state); |
476 | } | 568 | } |
477 | 569 | ||
570 | acpi_expose_nondev_subnodes(&dev->dev.kobj, &dev->data); | ||
571 | |||
478 | end: | 572 | end: |
479 | return result; | 573 | return result; |
480 | } | 574 | } |
@@ -485,6 +579,8 @@ end: | |||
485 | */ | 579 | */ |
486 | void acpi_device_remove_files(struct acpi_device *dev) | 580 | void acpi_device_remove_files(struct acpi_device *dev) |
487 | { | 581 | { |
582 | acpi_hide_nondev_subnodes(&dev->data); | ||
583 | |||
488 | if (dev->flags.power_manageable) { | 584 | if (dev->flags.power_manageable) { |
489 | device_remove_file(&dev->dev, &dev_attr_power_state); | 585 | device_remove_file(&dev->dev, &dev_attr_power_state); |
490 | if (dev->power.flags.power_resources) | 586 | if (dev->power.flags.power_resources) |
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 17c436de376b..333f9146d19e 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c | |||
@@ -64,12 +64,13 @@ static bool acpi_nondev_subnode_ok(acpi_handle scope, | |||
64 | goto fail; | 64 | goto fail; |
65 | 65 | ||
66 | if (acpi_extract_properties(buf.pointer, &dn->data)) | 66 | if (acpi_extract_properties(buf.pointer, &dn->data)) |
67 | dn->data.pointer = buf.pointer; | 67 | dn->handle = handle; |
68 | 68 | ||
69 | if (acpi_enumerate_nondev_subnodes(scope, buf.pointer, &dn->data)) | 69 | if (acpi_enumerate_nondev_subnodes(scope, buf.pointer, &dn->data)) |
70 | dn->data.pointer = buf.pointer; | 70 | dn->handle = handle; |
71 | 71 | ||
72 | if (dn->data.pointer) { | 72 | if (dn->handle) { |
73 | dn->data.pointer = buf.pointer; | ||
73 | list_add_tail(&dn->sibling, list); | 74 | list_add_tail(&dn->sibling, list); |
74 | return true; | 75 | return true; |
75 | } | 76 | } |
@@ -302,6 +303,7 @@ static void acpi_destroy_nondev_subnodes(struct list_head *list) | |||
302 | 303 | ||
303 | list_for_each_entry_safe_reverse(dn, next, list, sibling) { | 304 | list_for_each_entry_safe_reverse(dn, next, list, sibling) { |
304 | acpi_destroy_nondev_subnodes(&dn->data.subnodes); | 305 | acpi_destroy_nondev_subnodes(&dn->data.subnodes); |
306 | wait_for_completion(&dn->kobj_done); | ||
305 | list_del(&dn->sibling); | 307 | list_del(&dn->sibling); |
306 | ACPI_FREE((void *)dn->data.pointer); | 308 | ACPI_FREE((void *)dn->data.pointer); |
307 | kfree(dn); | 309 | kfree(dn); |
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 79cfee646d6b..e0d7c193d6e0 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h | |||
@@ -382,9 +382,12 @@ struct acpi_device { | |||
382 | /* Non-device subnode */ | 382 | /* Non-device subnode */ |
383 | struct acpi_data_node { | 383 | struct acpi_data_node { |
384 | const char *name; | 384 | const char *name; |
385 | acpi_handle handle; | ||
385 | struct fwnode_handle fwnode; | 386 | struct fwnode_handle fwnode; |
386 | struct acpi_device_data data; | 387 | struct acpi_device_data data; |
387 | struct list_head sibling; | 388 | struct list_head sibling; |
389 | struct kobject kobj; | ||
390 | struct completion kobj_done; | ||
388 | }; | 391 | }; |
389 | 392 | ||
390 | static inline bool acpi_check_dma(struct acpi_device *adev, bool *coherent) | 393 | static inline bool acpi_check_dma(struct acpi_device *adev, bool *coherent) |