aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Vorontsov <cbouatmailru@gmail.com>2010-05-18 15:49:51 -0400
committerAnton Vorontsov <cbouatmailru@gmail.com>2010-05-19 04:14:28 -0400
commit5f487cd34f4337f9bc27ca19da72a39d1b0a0ab4 (patch)
treef3204e0cf64a7202c42d1f397fd978ccd61f8225
parenta1e50fd4452b2ed57376ece465a17276b59fad9c (diff)
power_supply: Use attribute groups
This fixes a race between power supply device and initial attributes creation, plus makes it possible to implement writable properties. [Daniel Mack - removed superflous return statement and dropped .mode attribute from POWER_SUPPLY_ATTR] Suggested-by: Greg KH <gregkh@suse.de> Suggested-by: Kay Sievers <kay.sievers@vrfy.org> Signed-off-by: Anton Vorontsov <cbouatmailru@gmail.com> Tested-by: Daniel Mack <daniel@caiaq.de>
-rw-r--r--drivers/power/power_supply.h7
-rw-r--r--drivers/power/power_supply_core.c48
-rw-r--r--drivers/power/power_supply_sysfs.c115
-rw-r--r--include/linux/power_supply.h1
4 files changed, 75 insertions, 96 deletions
diff --git a/drivers/power/power_supply.h b/drivers/power/power_supply.h
index f38ba482be75..018de2b26998 100644
--- a/drivers/power/power_supply.h
+++ b/drivers/power/power_supply.h
@@ -12,15 +12,12 @@
12 12
13#ifdef CONFIG_SYSFS 13#ifdef CONFIG_SYSFS
14 14
15extern int power_supply_create_attrs(struct power_supply *psy); 15extern void power_supply_init_attrs(struct device_type *dev_type);
16extern void power_supply_remove_attrs(struct power_supply *psy);
17extern int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env); 16extern int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env);
18 17
19#else 18#else
20 19
21static inline int power_supply_create_attrs(struct power_supply *psy) 20static inline void power_supply_init_attrs(struct device_type *dev_type) {}
22{ return 0; }
23static inline void power_supply_remove_attrs(struct power_supply *psy) {}
24#define power_supply_uevent NULL 21#define power_supply_uevent NULL
25 22
26#endif /* CONFIG_SYSFS */ 23#endif /* CONFIG_SYSFS */
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index cce75b40b435..91606bb55318 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -13,6 +13,7 @@
13#include <linux/module.h> 13#include <linux/module.h>
14#include <linux/types.h> 14#include <linux/types.h>
15#include <linux/init.h> 15#include <linux/init.h>
16#include <linux/slab.h>
16#include <linux/device.h> 17#include <linux/device.h>
17#include <linux/err.h> 18#include <linux/err.h>
18#include <linux/power_supply.h> 19#include <linux/power_supply.h>
@@ -22,6 +23,8 @@
22struct class *power_supply_class; 23struct class *power_supply_class;
23EXPORT_SYMBOL_GPL(power_supply_class); 24EXPORT_SYMBOL_GPL(power_supply_class);
24 25
26static struct device_type power_supply_dev_type;
27
25static int __power_supply_changed_work(struct device *dev, void *data) 28static int __power_supply_changed_work(struct device *dev, void *data)
26{ 29{
27 struct power_supply *psy = (struct power_supply *)data; 30 struct power_supply *psy = (struct power_supply *)data;
@@ -144,22 +147,39 @@ struct power_supply *power_supply_get_by_name(char *name)
144} 147}
145EXPORT_SYMBOL_GPL(power_supply_get_by_name); 148EXPORT_SYMBOL_GPL(power_supply_get_by_name);
146 149
150static void power_supply_dev_release(struct device *dev)
151{
152 pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
153 kfree(dev);
154}
155
147int power_supply_register(struct device *parent, struct power_supply *psy) 156int power_supply_register(struct device *parent, struct power_supply *psy)
148{ 157{
149 int rc = 0; 158 struct device *dev;
159 int rc;
150 160
151 psy->dev = device_create(power_supply_class, parent, 0, psy, 161 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
152 "%s", psy->name); 162 if (!dev)
153 if (IS_ERR(psy->dev)) { 163 return -ENOMEM;
154 rc = PTR_ERR(psy->dev);
155 goto dev_create_failed;
156 }
157 164
158 INIT_WORK(&psy->changed_work, power_supply_changed_work); 165 device_initialize(dev);
159 166
160 rc = power_supply_create_attrs(psy); 167 dev->class = power_supply_class;
168 dev->type = &power_supply_dev_type;
169 dev->parent = parent;
170 dev->release = power_supply_dev_release;
171 dev_set_drvdata(dev, psy);
172 psy->dev = dev;
173
174 rc = kobject_set_name(&dev->kobj, "%s", psy->name);
175 if (rc)
176 goto kobject_set_name_failed;
177
178 rc = device_add(dev);
161 if (rc) 179 if (rc)
162 goto create_attrs_failed; 180 goto device_add_failed;
181
182 INIT_WORK(&psy->changed_work, power_supply_changed_work);
163 183
164 rc = power_supply_create_triggers(psy); 184 rc = power_supply_create_triggers(psy);
165 if (rc) 185 if (rc)
@@ -170,10 +190,10 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
170 goto success; 190 goto success;
171 191
172create_triggers_failed: 192create_triggers_failed:
173 power_supply_remove_attrs(psy);
174create_attrs_failed:
175 device_unregister(psy->dev); 193 device_unregister(psy->dev);
176dev_create_failed: 194kobject_set_name_failed:
195device_add_failed:
196 kfree(dev);
177success: 197success:
178 return rc; 198 return rc;
179} 199}
@@ -183,7 +203,6 @@ void power_supply_unregister(struct power_supply *psy)
183{ 203{
184 flush_scheduled_work(); 204 flush_scheduled_work();
185 power_supply_remove_triggers(psy); 205 power_supply_remove_triggers(psy);
186 power_supply_remove_attrs(psy);
187 device_unregister(psy->dev); 206 device_unregister(psy->dev);
188} 207}
189EXPORT_SYMBOL_GPL(power_supply_unregister); 208EXPORT_SYMBOL_GPL(power_supply_unregister);
@@ -196,6 +215,7 @@ static int __init power_supply_class_init(void)
196 return PTR_ERR(power_supply_class); 215 return PTR_ERR(power_supply_class);
197 216
198 power_supply_class->dev_uevent = power_supply_uevent; 217 power_supply_class->dev_uevent = power_supply_uevent;
218 power_supply_init_attrs(&power_supply_dev_type);
199 219
200 return 0; 220 return 0;
201} 221}
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index 5b6e352ac7c1..7d7593cad7d9 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -31,7 +31,7 @@
31 31
32#define POWER_SUPPLY_ATTR(_name) \ 32#define POWER_SUPPLY_ATTR(_name) \
33{ \ 33{ \
34 .attr = { .name = #_name, .mode = 0444 }, \ 34 .attr = { .name = #_name }, \
35 .show = power_supply_show_property, \ 35 .show = power_supply_show_property, \
36 .store = NULL, \ 36 .store = NULL, \
37} 37}
@@ -41,6 +41,9 @@ static struct device_attribute power_supply_attrs[];
41static ssize_t power_supply_show_property(struct device *dev, 41static ssize_t power_supply_show_property(struct device *dev,
42 struct device_attribute *attr, 42 struct device_attribute *attr,
43 char *buf) { 43 char *buf) {
44 static char *type_text[] = {
45 "Battery", "UPS", "Mains", "USB"
46 };
44 static char *status_text[] = { 47 static char *status_text[] = {
45 "Unknown", "Charging", "Discharging", "Not charging", "Full" 48 "Unknown", "Charging", "Discharging", "Not charging", "Full"
46 }; 49 };
@@ -58,12 +61,15 @@ static ssize_t power_supply_show_property(struct device *dev,
58 static char *capacity_level_text[] = { 61 static char *capacity_level_text[] = {
59 "Unknown", "Critical", "Low", "Normal", "High", "Full" 62 "Unknown", "Critical", "Low", "Normal", "High", "Full"
60 }; 63 };
61 ssize_t ret; 64 ssize_t ret = 0;
62 struct power_supply *psy = dev_get_drvdata(dev); 65 struct power_supply *psy = dev_get_drvdata(dev);
63 const ptrdiff_t off = attr - power_supply_attrs; 66 const ptrdiff_t off = attr - power_supply_attrs;
64 union power_supply_propval value; 67 union power_supply_propval value;
65 68
66 ret = psy->get_property(psy, off, &value); 69 if (off == POWER_SUPPLY_PROP_TYPE)
70 value.intval = psy->type;
71 else
72 ret = psy->get_property(psy, off, &value);
67 73
68 if (ret < 0) { 74 if (ret < 0) {
69 if (ret == -ENODATA) 75 if (ret == -ENODATA)
@@ -85,6 +91,8 @@ static ssize_t power_supply_show_property(struct device *dev,
85 return sprintf(buf, "%s\n", technology_text[value.intval]); 91 return sprintf(buf, "%s\n", technology_text[value.intval]);
86 else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL) 92 else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
87 return sprintf(buf, "%s\n", capacity_level_text[value.intval]); 93 return sprintf(buf, "%s\n", capacity_level_text[value.intval]);
94 else if (off == POWER_SUPPLY_PROP_TYPE)
95 return sprintf(buf, "%s\n", type_text[value.intval]);
88 else if (off >= POWER_SUPPLY_PROP_MODEL_NAME) 96 else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
89 return sprintf(buf, "%s\n", value.strval); 97 return sprintf(buf, "%s\n", value.strval);
90 98
@@ -132,67 +140,50 @@ static struct device_attribute power_supply_attrs[] = {
132 POWER_SUPPLY_ATTR(time_to_empty_avg), 140 POWER_SUPPLY_ATTR(time_to_empty_avg),
133 POWER_SUPPLY_ATTR(time_to_full_now), 141 POWER_SUPPLY_ATTR(time_to_full_now),
134 POWER_SUPPLY_ATTR(time_to_full_avg), 142 POWER_SUPPLY_ATTR(time_to_full_avg),
143 POWER_SUPPLY_ATTR(type),
135 /* Properties of type `const char *' */ 144 /* Properties of type `const char *' */
136 POWER_SUPPLY_ATTR(model_name), 145 POWER_SUPPLY_ATTR(model_name),
137 POWER_SUPPLY_ATTR(manufacturer), 146 POWER_SUPPLY_ATTR(manufacturer),
138 POWER_SUPPLY_ATTR(serial_number), 147 POWER_SUPPLY_ATTR(serial_number),
139}; 148};
140 149
141static ssize_t power_supply_show_static_attrs(struct device *dev, 150static struct attribute *
142 struct device_attribute *attr, 151__power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1];
143 char *buf) {
144 static char *type_text[] = { "Battery", "UPS", "Mains", "USB" };
145 struct power_supply *psy = dev_get_drvdata(dev);
146
147 return sprintf(buf, "%s\n", type_text[psy->type]);
148}
149
150static struct device_attribute power_supply_static_attrs[] = {
151 __ATTR(type, 0444, power_supply_show_static_attrs, NULL),
152};
153 152
154int power_supply_create_attrs(struct power_supply *psy) 153static mode_t power_supply_attr_is_visible(struct kobject *kobj,
154 struct attribute *attr,
155 int attrno)
155{ 156{
156 int rc = 0; 157 struct device *dev = container_of(kobj, struct device, kobj);
157 int i, j; 158 struct power_supply *psy = dev_get_drvdata(dev);
158 159 int i;
159 for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) {
160 rc = device_create_file(psy->dev,
161 &power_supply_static_attrs[i]);
162 if (rc)
163 goto statics_failed;
164 }
165 160
166 for (j = 0; j < psy->num_properties; j++) { 161 for (i = 0; i < psy->num_properties; i++) {
167 rc = device_create_file(psy->dev, 162 if (psy->properties[i] == attrno)
168 &power_supply_attrs[psy->properties[j]]); 163 return 0444;
169 if (rc)
170 goto dynamics_failed;
171 } 164 }
172 165
173 goto succeed; 166 return 0;
174
175dynamics_failed:
176 while (j--)
177 device_remove_file(psy->dev,
178 &power_supply_attrs[psy->properties[j]]);
179statics_failed:
180 while (i--)
181 device_remove_file(psy->dev, &power_supply_static_attrs[i]);
182succeed:
183 return rc;
184} 167}
185 168
186void power_supply_remove_attrs(struct power_supply *psy) 169static struct attribute_group power_supply_attr_group = {
170 .attrs = __power_supply_attrs,
171 .is_visible = power_supply_attr_is_visible,
172};
173
174static const struct attribute_group *power_supply_attr_groups[] = {
175 &power_supply_attr_group,
176 NULL,
177};
178
179void power_supply_init_attrs(struct device_type *dev_type)
187{ 180{
188 int i; 181 int i;
189 182
190 for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) 183 dev_type->groups = power_supply_attr_groups;
191 device_remove_file(psy->dev, &power_supply_static_attrs[i]);
192 184
193 for (i = 0; i < psy->num_properties; i++) 185 for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)
194 device_remove_file(psy->dev, 186 __power_supply_attrs[i] = &power_supply_attrs[i].attr;
195 &power_supply_attrs[psy->properties[i]]);
196} 187}
197 188
198static char *kstruprdup(const char *str, gfp_t gfp) 189static char *kstruprdup(const char *str, gfp_t gfp)
@@ -236,36 +227,6 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
236 if (!prop_buf) 227 if (!prop_buf)
237 return -ENOMEM; 228 return -ENOMEM;
238 229
239 for (j = 0; j < ARRAY_SIZE(power_supply_static_attrs); j++) {
240 struct device_attribute *attr;
241 char *line;
242
243 attr = &power_supply_static_attrs[j];
244
245 ret = power_supply_show_static_attrs(dev, attr, prop_buf);
246 if (ret < 0)
247 goto out;
248
249 line = strchr(prop_buf, '\n');
250 if (line)
251 *line = 0;
252
253 attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
254 if (!attrname) {
255 ret = -ENOMEM;
256 goto out;
257 }
258
259 dev_dbg(dev, "Static prop %s=%s\n", attrname, prop_buf);
260
261 ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
262 kfree(attrname);
263 if (ret)
264 goto out;
265 }
266
267 dev_dbg(dev, "%zd dynamic props\n", psy->num_properties);
268
269 for (j = 0; j < psy->num_properties; j++) { 230 for (j = 0; j < psy->num_properties; j++) {
270 struct device_attribute *attr; 231 struct device_attribute *attr;
271 char *line; 232 char *line;
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index ebd2b8fb00d0..c5f73a3ab3ab 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -114,6 +114,7 @@ enum power_supply_property {
114 POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 114 POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
115 POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 115 POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
116 POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 116 POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
117 POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
117 /* Properties of type `const char *' */ 118 /* Properties of type `const char *' */
118 POWER_SUPPLY_PROP_MODEL_NAME, 119 POWER_SUPPLY_PROP_MODEL_NAME,
119 POWER_SUPPLY_PROP_MANUFACTURER, 120 POWER_SUPPLY_PROP_MANUFACTURER,