Blame view

drivers/power/power_supply_sysfs.c 7.86 KB
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   *  Sysfs interface for the universal power supply monitor class
   *
   *  Copyright © 2007  David Woodhouse <dwmw2@infradead.org>
   *  Copyright © 2007  Anton Vorontsov <cbou@mail.ru>
   *  Copyright © 2004  Szabolcs Gyurko
   *  Copyright © 2003  Ian Molton <spyro@f2s.com>
   *
   *  Modified: 2004, Oct     Szabolcs Gyurko
   *
   *  You may use this code as per GPL version 2
   */
  
  #include <linux/ctype.h>
  #include <linux/power_supply.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
16
  #include <linux/slab.h>
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
17

25f12141e   Adrian Bunk   [BATTERY] Every f...
18
  #include "power_supply.h"
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  /*
   * This is because the name "current" breaks the device attr macro.
   * The "current" word resolves to "(get_current())" so instead of
   * "current" "(get_current())" appears in the sysfs.
   *
   * The source of this definition is the device.h which calls __ATTR
   * macro in sysfs.h which calls the __stringify macro.
   *
   * Only modification that the name is not tried to be resolved
   * (as a macro let's say).
   */
  
  #define POWER_SUPPLY_ATTR(_name)					\
  {									\
5f487cd34   Anton Vorontsov   power_supply: Use...
33
  	.attr = { .name = #_name },					\
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
34
  	.show = power_supply_show_property,				\
0011d2d4a   Daniel Mack   power_supply: Add...
35
  	.store = power_supply_store_property,				\
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
36
37
38
39
40
41
42
  }
  
  static struct device_attribute power_supply_attrs[];
  
  static ssize_t power_supply_show_property(struct device *dev,
  					  struct device_attribute *attr,
  					  char *buf) {
5f487cd34   Anton Vorontsov   power_supply: Use...
43
  	static char *type_text[] = {
85efc8a18   Heikki Krogerus   power_supply: Add...
44
45
  		"Battery", "UPS", "Mains", "USB",
  		"USB_DCP", "USB_CDP", "USB_ACA"
5f487cd34   Anton Vorontsov   power_supply: Use...
46
  	};
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
47
48
49
  	static char *status_text[] = {
  		"Unknown", "Charging", "Discharging", "Not charging", "Full"
  	};
ee8076ed3   Andres Salomon   power_supply: Add...
50
51
52
  	static char *charge_type[] = {
  		"Unknown", "N/A", "Trickle", "Fast"
  	};
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
53
54
  	static char *health_text[] = {
  		"Unknown", "Good", "Overheat", "Dead", "Over voltage",
7e386e6e0   Mark Brown   power_supply: Add...
55
  		"Unspecified failure", "Cold",
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
56
57
  	};
  	static char *technology_text[] = {
c7cc930f9   Dmitry Eremin-Solenikov   power_supply: add...
58
59
  		"Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd",
  		"LiMn"
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
60
  	};
b294a290d   Andres Salomon   Revert "power: re...
61
62
63
  	static char *capacity_level_text[] = {
  		"Unknown", "Critical", "Low", "Normal", "High", "Full"
  	};
5f487cd34   Anton Vorontsov   power_supply: Use...
64
  	ssize_t ret = 0;
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
65
66
67
  	struct power_supply *psy = dev_get_drvdata(dev);
  	const ptrdiff_t off = attr - power_supply_attrs;
  	union power_supply_propval value;
5f487cd34   Anton Vorontsov   power_supply: Use...
68
69
70
71
  	if (off == POWER_SUPPLY_PROP_TYPE)
  		value.intval = psy->type;
  	else
  		ret = psy->get_property(psy, off, &value);
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
72
73
  
  	if (ret < 0) {
9d233e8bb   Anton Vorontsov   power_supply_sysf...
74
75
76
77
78
  		if (ret == -ENODATA)
  			dev_dbg(dev, "driver has no data for `%s' property
  ",
  				attr->attr.name);
  		else if (ret != -ENODEV)
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
79
80
81
82
83
84
85
86
87
  			dev_err(dev, "driver failed to report `%s' property
  ",
  				attr->attr.name);
  		return ret;
  	}
  
  	if (off == POWER_SUPPLY_PROP_STATUS)
  		return sprintf(buf, "%s
  ", status_text[value.intval]);
ee8076ed3   Andres Salomon   power_supply: Add...
88
89
90
  	else if (off == POWER_SUPPLY_PROP_CHARGE_TYPE)
  		return sprintf(buf, "%s
  ", charge_type[value.intval]);
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
91
92
93
94
95
96
  	else if (off == POWER_SUPPLY_PROP_HEALTH)
  		return sprintf(buf, "%s
  ", health_text[value.intval]);
  	else if (off == POWER_SUPPLY_PROP_TECHNOLOGY)
  		return sprintf(buf, "%s
  ", technology_text[value.intval]);
b294a290d   Andres Salomon   Revert "power: re...
97
98
99
  	else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
  		return sprintf(buf, "%s
  ", capacity_level_text[value.intval]);
5f487cd34   Anton Vorontsov   power_supply: Use...
100
101
102
  	else if (off == POWER_SUPPLY_PROP_TYPE)
  		return sprintf(buf, "%s
  ", type_text[value.intval]);
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
103
104
105
106
107
108
109
  	else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
  		return sprintf(buf, "%s
  ", value.strval);
  
  	return sprintf(buf, "%d
  ", value.intval);
  }
0011d2d4a   Daniel Mack   power_supply: Add...
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
  static ssize_t power_supply_store_property(struct device *dev,
  					   struct device_attribute *attr,
  					   const char *buf, size_t count) {
  	ssize_t ret;
  	struct power_supply *psy = dev_get_drvdata(dev);
  	const ptrdiff_t off = attr - power_supply_attrs;
  	union power_supply_propval value;
  	long long_val;
  
  	/* TODO: support other types than int */
  	ret = strict_strtol(buf, 10, &long_val);
  	if (ret < 0)
  		return ret;
  
  	value.intval = long_val;
  
  	ret = psy->set_property(psy, off, &value);
  	if (ret < 0)
  		return ret;
  
  	return count;
  }
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
132
133
134
135
  /* Must be in the same order as POWER_SUPPLY_PROP_* */
  static struct device_attribute power_supply_attrs[] = {
  	/* Properties of type `int' */
  	POWER_SUPPLY_ATTR(status),
ee8076ed3   Andres Salomon   power_supply: Add...
136
  	POWER_SUPPLY_ATTR(charge_type),
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
137
138
139
140
  	POWER_SUPPLY_ATTR(health),
  	POWER_SUPPLY_ATTR(present),
  	POWER_SUPPLY_ATTR(online),
  	POWER_SUPPLY_ATTR(technology),
c955fe8e0   Alexey Starikovskiy   POWER: Add suppor...
141
  	POWER_SUPPLY_ATTR(cycle_count),
c7cc930f9   Dmitry Eremin-Solenikov   power_supply: add...
142
143
  	POWER_SUPPLY_ATTR(voltage_max),
  	POWER_SUPPLY_ATTR(voltage_min),
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
144
145
146
147
  	POWER_SUPPLY_ATTR(voltage_max_design),
  	POWER_SUPPLY_ATTR(voltage_min_design),
  	POWER_SUPPLY_ATTR(voltage_now),
  	POWER_SUPPLY_ATTR(voltage_avg),
fe3f6d097   Heikki Krogerus   power_supply: Int...
148
  	POWER_SUPPLY_ATTR(current_max),
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
149
150
  	POWER_SUPPLY_ATTR(current_now),
  	POWER_SUPPLY_ATTR(current_avg),
7faa144a5   Alexey Starikovskiy   ACPI: battery: ad...
151
152
  	POWER_SUPPLY_ATTR(power_now),
  	POWER_SUPPLY_ATTR(power_avg),
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
153
154
155
156
157
158
  	POWER_SUPPLY_ATTR(charge_full_design),
  	POWER_SUPPLY_ATTR(charge_empty_design),
  	POWER_SUPPLY_ATTR(charge_full),
  	POWER_SUPPLY_ATTR(charge_empty),
  	POWER_SUPPLY_ATTR(charge_now),
  	POWER_SUPPLY_ATTR(charge_avg),
8e552c36d   Andres Salomon   power_supply: add...
159
  	POWER_SUPPLY_ATTR(charge_counter),
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
160
161
162
163
164
165
166
  	POWER_SUPPLY_ATTR(energy_full_design),
  	POWER_SUPPLY_ATTR(energy_empty_design),
  	POWER_SUPPLY_ATTR(energy_full),
  	POWER_SUPPLY_ATTR(energy_empty),
  	POWER_SUPPLY_ATTR(energy_now),
  	POWER_SUPPLY_ATTR(energy_avg),
  	POWER_SUPPLY_ATTR(capacity),
b294a290d   Andres Salomon   Revert "power: re...
167
  	POWER_SUPPLY_ATTR(capacity_level),
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
168
169
170
171
172
173
  	POWER_SUPPLY_ATTR(temp),
  	POWER_SUPPLY_ATTR(temp_ambient),
  	POWER_SUPPLY_ATTR(time_to_empty_now),
  	POWER_SUPPLY_ATTR(time_to_empty_avg),
  	POWER_SUPPLY_ATTR(time_to_full_now),
  	POWER_SUPPLY_ATTR(time_to_full_avg),
5f487cd34   Anton Vorontsov   power_supply: Use...
174
  	POWER_SUPPLY_ATTR(type),
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
175
176
177
  	/* Properties of type `const char *' */
  	POWER_SUPPLY_ATTR(model_name),
  	POWER_SUPPLY_ATTR(manufacturer),
7c2670bbb   maximilian attems   ACPI: battery: ad...
178
  	POWER_SUPPLY_ATTR(serial_number),
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
179
  };
5f487cd34   Anton Vorontsov   power_supply: Use...
180
181
  static struct attribute *
  __power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1];
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
182

5f487cd34   Anton Vorontsov   power_supply: Use...
183
184
185
  static mode_t power_supply_attr_is_visible(struct kobject *kobj,
  					   struct attribute *attr,
  					   int attrno)
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
186
  {
5f487cd34   Anton Vorontsov   power_supply: Use...
187
188
  	struct device *dev = container_of(kobj, struct device, kobj);
  	struct power_supply *psy = dev_get_drvdata(dev);
bbabb158f   Daniel Mack   power_supply: Fix...
189
  	mode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
5f487cd34   Anton Vorontsov   power_supply: Use...
190
  	int i;
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
191

bbabb158f   Daniel Mack   power_supply: Fix...
192
193
  	if (attrno == POWER_SUPPLY_PROP_TYPE)
  		return mode;
5f487cd34   Anton Vorontsov   power_supply: Use...
194
  	for (i = 0; i < psy->num_properties; i++) {
0011d2d4a   Daniel Mack   power_supply: Add...
195
196
197
  		int property = psy->properties[i];
  
  		if (property == attrno) {
0011d2d4a   Daniel Mack   power_supply: Add...
198
199
200
201
202
203
  			if (psy->property_is_writeable &&
  			    psy->property_is_writeable(psy, property) > 0)
  				mode |= S_IWUSR;
  
  			return mode;
  		}
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
204
  	}
5f487cd34   Anton Vorontsov   power_supply: Use...
205
  	return 0;
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
206
  }
5f487cd34   Anton Vorontsov   power_supply: Use...
207
208
209
210
211
212
213
214
215
216
217
  static struct attribute_group power_supply_attr_group = {
  	.attrs = __power_supply_attrs,
  	.is_visible = power_supply_attr_is_visible,
  };
  
  static const struct attribute_group *power_supply_attr_groups[] = {
  	&power_supply_attr_group,
  	NULL,
  };
  
  void power_supply_init_attrs(struct device_type *dev_type)
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
218
219
  {
  	int i;
5f487cd34   Anton Vorontsov   power_supply: Use...
220
  	dev_type->groups = power_supply_attr_groups;
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
221

5f487cd34   Anton Vorontsov   power_supply: Use...
222
223
  	for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)
  		__power_supply_attrs[i] = &power_supply_attrs[i].attr;
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
  }
  
  static char *kstruprdup(const char *str, gfp_t gfp)
  {
  	char *ret, *ustr;
  
  	ustr = ret = kmalloc(strlen(str) + 1, gfp);
  
  	if (!ret)
  		return NULL;
  
  	while (*str)
  		*ustr++ = toupper(*str++);
  
  	*ustr = 0;
  
  	return ret;
  }
7eff2e7a8   Kay Sievers   Driver core: chan...
242
  int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
243
244
  {
  	struct power_supply *psy = dev_get_drvdata(dev);
7eff2e7a8   Kay Sievers   Driver core: chan...
245
  	int ret = 0, j;
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
246
247
248
249
250
  	char *prop_buf;
  	char *attrname;
  
  	dev_dbg(dev, "uevent
  ");
56fa18e8f   Dmitry Eremin-Solenikov   power_supply: Fix...
251
  	if (!psy || !psy->dev) {
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
252
253
254
255
256
257
258
  		dev_dbg(dev, "No power supply yet
  ");
  		return ret;
  	}
  
  	dev_dbg(dev, "POWER_SUPPLY_NAME=%s
  ", psy->name);
7eff2e7a8   Kay Sievers   Driver core: chan...
259
  	ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->name);
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
260
261
262
263
264
265
  	if (ret)
  		return ret;
  
  	prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
  	if (!prop_buf)
  		return -ENOMEM;
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
266
267
268
269
270
271
272
  	for (j = 0; j < psy->num_properties; j++) {
  		struct device_attribute *attr;
  		char *line;
  
  		attr = &power_supply_attrs[psy->properties[j]];
  
  		ret = power_supply_show_property(dev, attr, prop_buf);
f722e17fd   Lars-Peter Clausen   power_supply: Ign...
273
  		if (ret == -ENODEV || ret == -ENODATA) {
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
  			/* When a battery is absent, we expect -ENODEV. Don't abort;
  			   send the uevent with at least the the PRESENT=0 property */
  			ret = 0;
  			continue;
  		}
  
  		if (ret < 0)
  			goto out;
  
  		line = strchr(prop_buf, '
  ');
  		if (line)
  			*line = 0;
  
  		attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
  		if (!attrname) {
  			ret = -ENOMEM;
  			goto out;
  		}
  
  		dev_dbg(dev, "prop %s=%s
  ", attrname, prop_buf);
7eff2e7a8   Kay Sievers   Driver core: chan...
296
  		ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
4a11b59d8   Anton Vorontsov   [BATTERY] Univers...
297
298
299
300
301
302
303
304
305
306
  		kfree(attrname);
  		if (ret)
  			goto out;
  	}
  
  out:
  	free_page((unsigned long)prop_buf);
  
  	return ret;
  }