diff options
| -rw-r--r-- | drivers/power/power_supply.h | 7 | ||||
| -rw-r--r-- | drivers/power/power_supply_core.c | 48 | ||||
| -rw-r--r-- | drivers/power/power_supply_sysfs.c | 115 | ||||
| -rw-r--r-- | include/linux/power_supply.h | 1 |
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 | ||
| 15 | extern int power_supply_create_attrs(struct power_supply *psy); | 15 | extern void power_supply_init_attrs(struct device_type *dev_type); |
| 16 | extern void power_supply_remove_attrs(struct power_supply *psy); | ||
| 17 | extern int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env); | 16 | extern int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env); |
| 18 | 17 | ||
| 19 | #else | 18 | #else |
| 20 | 19 | ||
| 21 | static inline int power_supply_create_attrs(struct power_supply *psy) | 20 | static inline void power_supply_init_attrs(struct device_type *dev_type) {} |
| 22 | { return 0; } | ||
| 23 | static 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 @@ | |||
| 22 | struct class *power_supply_class; | 23 | struct class *power_supply_class; |
| 23 | EXPORT_SYMBOL_GPL(power_supply_class); | 24 | EXPORT_SYMBOL_GPL(power_supply_class); |
| 24 | 25 | ||
| 26 | static struct device_type power_supply_dev_type; | ||
| 27 | |||
| 25 | static int __power_supply_changed_work(struct device *dev, void *data) | 28 | static 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 | } |
| 145 | EXPORT_SYMBOL_GPL(power_supply_get_by_name); | 148 | EXPORT_SYMBOL_GPL(power_supply_get_by_name); |
| 146 | 149 | ||
| 150 | static 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 | |||
| 147 | int power_supply_register(struct device *parent, struct power_supply *psy) | 156 | int 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 | ||
| 172 | create_triggers_failed: | 192 | create_triggers_failed: |
| 173 | power_supply_remove_attrs(psy); | ||
| 174 | create_attrs_failed: | ||
| 175 | device_unregister(psy->dev); | 193 | device_unregister(psy->dev); |
| 176 | dev_create_failed: | 194 | kobject_set_name_failed: |
| 195 | device_add_failed: | ||
| 196 | kfree(dev); | ||
| 177 | success: | 197 | success: |
| 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 | } |
| 189 | EXPORT_SYMBOL_GPL(power_supply_unregister); | 208 | EXPORT_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[]; | |||
| 41 | static ssize_t power_supply_show_property(struct device *dev, | 41 | static 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 | ||
| 141 | static ssize_t power_supply_show_static_attrs(struct device *dev, | 150 | static 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 | |||
| 150 | static struct device_attribute power_supply_static_attrs[] = { | ||
| 151 | __ATTR(type, 0444, power_supply_show_static_attrs, NULL), | ||
| 152 | }; | ||
| 153 | 152 | ||
| 154 | int power_supply_create_attrs(struct power_supply *psy) | 153 | static 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 | |||
| 175 | dynamics_failed: | ||
| 176 | while (j--) | ||
| 177 | device_remove_file(psy->dev, | ||
| 178 | &power_supply_attrs[psy->properties[j]]); | ||
| 179 | statics_failed: | ||
| 180 | while (i--) | ||
| 181 | device_remove_file(psy->dev, &power_supply_static_attrs[i]); | ||
| 182 | succeed: | ||
| 183 | return rc; | ||
| 184 | } | 167 | } |
| 185 | 168 | ||
| 186 | void power_supply_remove_attrs(struct power_supply *psy) | 169 | static struct attribute_group power_supply_attr_group = { |
| 170 | .attrs = __power_supply_attrs, | ||
| 171 | .is_visible = power_supply_attr_is_visible, | ||
| 172 | }; | ||
| 173 | |||
| 174 | static const struct attribute_group *power_supply_attr_groups[] = { | ||
| 175 | &power_supply_attr_group, | ||
| 176 | NULL, | ||
| 177 | }; | ||
| 178 | |||
| 179 | void 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 | ||
| 198 | static char *kstruprdup(const char *str, gfp_t gfp) | 189 | static 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, |
