Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+77 -98
+2 -5
drivers/power/power_supply.h
··· 12 12 13 13 #ifdef CONFIG_SYSFS 14 14 15 - extern int power_supply_create_attrs(struct power_supply *psy); 16 - extern void power_supply_remove_attrs(struct power_supply *psy); 15 + extern void power_supply_init_attrs(struct device_type *dev_type); 17 16 extern int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env); 18 17 19 18 #else 20 19 21 - static inline int power_supply_create_attrs(struct power_supply *psy) 22 - { return 0; } 23 - static inline void power_supply_remove_attrs(struct power_supply *psy) {} 20 + static inline void power_supply_init_attrs(struct device_type *dev_type) {} 24 21 #define power_supply_uevent NULL 25 22 26 23 #endif /* CONFIG_SYSFS */
+35 -15
drivers/power/power_supply_core.c
··· 13 13 #include <linux/module.h> 14 14 #include <linux/types.h> 15 15 #include <linux/init.h> 16 + #include <linux/slab.h> 16 17 #include <linux/device.h> 17 18 #include <linux/err.h> 18 19 #include <linux/power_supply.h> ··· 22 21 /* exported for the APM Power driver, APM emulation */ 23 22 struct class *power_supply_class; 24 23 EXPORT_SYMBOL_GPL(power_supply_class); 24 + 25 + static struct device_type power_supply_dev_type; 25 26 26 27 static int __power_supply_changed_work(struct device *dev, void *data) 27 28 { ··· 147 144 } 148 145 EXPORT_SYMBOL_GPL(power_supply_get_by_name); 149 146 147 + static void power_supply_dev_release(struct device *dev) 148 + { 149 + pr_debug("device: '%s': %s\n", dev_name(dev), __func__); 150 + kfree(dev); 151 + } 152 + 150 153 int power_supply_register(struct device *parent, struct power_supply *psy) 151 154 { 152 - int rc = 0; 155 + struct device *dev; 156 + int rc; 153 157 154 - psy->dev = device_create(power_supply_class, parent, 0, psy, 155 - "%s", psy->name); 156 - if (IS_ERR(psy->dev)) { 157 - rc = PTR_ERR(psy->dev); 158 - goto dev_create_failed; 159 - } 158 + dev = kzalloc(sizeof(*dev), GFP_KERNEL); 159 + if (!dev) 160 + return -ENOMEM; 161 + 162 + device_initialize(dev); 163 + 164 + dev->class = power_supply_class; 165 + dev->type = &power_supply_dev_type; 166 + dev->parent = parent; 167 + dev->release = power_supply_dev_release; 168 + dev_set_drvdata(dev, psy); 169 + psy->dev = dev; 170 + 171 + rc = kobject_set_name(&dev->kobj, "%s", psy->name); 172 + if (rc) 173 + goto kobject_set_name_failed; 174 + 175 + rc = device_add(dev); 176 + if (rc) 177 + goto device_add_failed; 160 178 161 179 INIT_WORK(&psy->changed_work, power_supply_changed_work); 162 - 163 - rc = power_supply_create_attrs(psy); 164 - if (rc) 165 - goto create_attrs_failed; 166 180 167 181 rc = power_supply_create_triggers(psy); 168 182 if (rc) ··· 190 170 goto success; 191 171 192 172 create_triggers_failed: 193 - power_supply_remove_attrs(psy); 194 - create_attrs_failed: 195 173 device_unregister(psy->dev); 196 - dev_create_failed: 174 + kobject_set_name_failed: 175 + device_add_failed: 176 + kfree(dev); 197 177 success: 198 178 return rc; 199 179 } ··· 203 183 { 204 184 flush_scheduled_work(); 205 185 power_supply_remove_triggers(psy); 206 - power_supply_remove_attrs(psy); 207 186 device_unregister(psy->dev); 208 187 } 209 188 EXPORT_SYMBOL_GPL(power_supply_unregister); ··· 215 196 return PTR_ERR(power_supply_class); 216 197 217 198 power_supply_class->dev_uevent = power_supply_uevent; 199 + power_supply_init_attrs(&power_supply_dev_type); 218 200 219 201 return 0; 220 202 }
+39 -78
drivers/power/power_supply_sysfs.c
··· 31 31 32 32 #define POWER_SUPPLY_ATTR(_name) \ 33 33 { \ 34 - .attr = { .name = #_name, .mode = 0444 }, \ 34 + .attr = { .name = #_name }, \ 35 35 .show = power_supply_show_property, \ 36 36 .store = NULL, \ 37 37 } ··· 41 41 static ssize_t power_supply_show_property(struct device *dev, 42 42 struct device_attribute *attr, 43 43 char *buf) { 44 + static char *type_text[] = { 45 + "Battery", "UPS", "Mains", "USB" 46 + }; 44 47 static char *status_text[] = { 45 48 "Unknown", "Charging", "Discharging", "Not charging", "Full" 46 49 }; ··· 61 58 static char *capacity_level_text[] = { 62 59 "Unknown", "Critical", "Low", "Normal", "High", "Full" 63 60 }; 64 - ssize_t ret; 61 + ssize_t ret = 0; 65 62 struct power_supply *psy = dev_get_drvdata(dev); 66 63 const ptrdiff_t off = attr - power_supply_attrs; 67 64 union power_supply_propval value; 68 65 69 - ret = psy->get_property(psy, off, &value); 66 + if (off == POWER_SUPPLY_PROP_TYPE) 67 + value.intval = psy->type; 68 + else 69 + ret = psy->get_property(psy, off, &value); 70 70 71 71 if (ret < 0) { 72 72 if (ret == -ENODATA) ··· 91 85 return sprintf(buf, "%s\n", technology_text[value.intval]); 92 86 else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL) 93 87 return sprintf(buf, "%s\n", capacity_level_text[value.intval]); 88 + else if (off == POWER_SUPPLY_PROP_TYPE) 89 + return sprintf(buf, "%s\n", type_text[value.intval]); 94 90 else if (off >= POWER_SUPPLY_PROP_MODEL_NAME) 95 91 return sprintf(buf, "%s\n", value.strval); 96 92 ··· 140 132 POWER_SUPPLY_ATTR(time_to_empty_avg), 141 133 POWER_SUPPLY_ATTR(time_to_full_now), 142 134 POWER_SUPPLY_ATTR(time_to_full_avg), 135 + POWER_SUPPLY_ATTR(type), 143 136 /* Properties of type `const char *' */ 144 137 POWER_SUPPLY_ATTR(model_name), 145 138 POWER_SUPPLY_ATTR(manufacturer), 146 139 POWER_SUPPLY_ATTR(serial_number), 147 140 }; 148 141 149 - static ssize_t power_supply_show_static_attrs(struct device *dev, 150 - struct device_attribute *attr, 151 - char *buf) { 152 - static char *type_text[] = { "Battery", "UPS", "Mains", "USB" }; 153 - struct power_supply *psy = dev_get_drvdata(dev); 142 + static struct attribute * 143 + __power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1]; 154 144 155 - return sprintf(buf, "%s\n", type_text[psy->type]); 145 + static mode_t power_supply_attr_is_visible(struct kobject *kobj, 146 + struct attribute *attr, 147 + int attrno) 148 + { 149 + struct device *dev = container_of(kobj, struct device, kobj); 150 + struct power_supply *psy = dev_get_drvdata(dev); 151 + int i; 152 + 153 + for (i = 0; i < psy->num_properties; i++) { 154 + if (psy->properties[i] == attrno) 155 + return 0444; 156 + } 157 + 158 + return 0; 156 159 } 157 160 158 - static struct device_attribute power_supply_static_attrs[] = { 159 - __ATTR(type, 0444, power_supply_show_static_attrs, NULL), 161 + static struct attribute_group power_supply_attr_group = { 162 + .attrs = __power_supply_attrs, 163 + .is_visible = power_supply_attr_is_visible, 160 164 }; 161 165 162 - int power_supply_create_attrs(struct power_supply *psy) 163 - { 164 - int rc = 0; 165 - int i, j; 166 + static const struct attribute_group *power_supply_attr_groups[] = { 167 + &power_supply_attr_group, 168 + NULL, 169 + }; 166 170 167 - for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) { 168 - rc = device_create_file(psy->dev, 169 - &power_supply_static_attrs[i]); 170 - if (rc) 171 - goto statics_failed; 172 - } 173 - 174 - for (j = 0; j < psy->num_properties; j++) { 175 - rc = device_create_file(psy->dev, 176 - &power_supply_attrs[psy->properties[j]]); 177 - if (rc) 178 - goto dynamics_failed; 179 - } 180 - 181 - goto succeed; 182 - 183 - dynamics_failed: 184 - while (j--) 185 - device_remove_file(psy->dev, 186 - &power_supply_attrs[psy->properties[j]]); 187 - statics_failed: 188 - while (i--) 189 - device_remove_file(psy->dev, &power_supply_static_attrs[i]); 190 - succeed: 191 - return rc; 192 - } 193 - 194 - void power_supply_remove_attrs(struct power_supply *psy) 171 + void power_supply_init_attrs(struct device_type *dev_type) 195 172 { 196 173 int i; 197 174 198 - for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) 199 - device_remove_file(psy->dev, &power_supply_static_attrs[i]); 175 + dev_type->groups = power_supply_attr_groups; 200 176 201 - for (i = 0; i < psy->num_properties; i++) 202 - device_remove_file(psy->dev, 203 - &power_supply_attrs[psy->properties[i]]); 177 + for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++) 178 + __power_supply_attrs[i] = &power_supply_attrs[i].attr; 204 179 } 205 180 206 181 static char *kstruprdup(const char *str, gfp_t gfp) ··· 226 235 prop_buf = (char *)get_zeroed_page(GFP_KERNEL); 227 236 if (!prop_buf) 228 237 return -ENOMEM; 229 - 230 - for (j = 0; j < ARRAY_SIZE(power_supply_static_attrs); j++) { 231 - struct device_attribute *attr; 232 - char *line; 233 - 234 - attr = &power_supply_static_attrs[j]; 235 - 236 - ret = power_supply_show_static_attrs(dev, attr, prop_buf); 237 - if (ret < 0) 238 - goto out; 239 - 240 - line = strchr(prop_buf, '\n'); 241 - if (line) 242 - *line = 0; 243 - 244 - attrname = kstruprdup(attr->attr.name, GFP_KERNEL); 245 - if (!attrname) { 246 - ret = -ENOMEM; 247 - goto out; 248 - } 249 - 250 - dev_dbg(dev, "Static prop %s=%s\n", attrname, prop_buf); 251 - 252 - ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf); 253 - kfree(attrname); 254 - if (ret) 255 - goto out; 256 - } 257 - 258 - dev_dbg(dev, "%zd dynamic props\n", psy->num_properties); 259 238 260 239 for (j = 0; j < psy->num_properties; j++) { 261 240 struct device_attribute *attr;
+1
include/linux/power_supply.h
··· 114 114 POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 115 115 POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 116 116 POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 117 + POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */ 117 118 /* Properties of type `const char *' */ 118 119 POWER_SUPPLY_PROP_MODEL_NAME, 119 120 POWER_SUPPLY_PROP_MANUFACTURER,