1 /*
2  *  Sysfs interface for the universal power supply monitor class
3  *
4  *  Copyright © 2007  David Woodhouse <dwmw2@infradead.org>
5  *  Copyright © 2007  Anton Vorontsov <cbou@mail.ru>
6  *  Copyright © 2004  Szabolcs Gyurko
7  *  Copyright © 2003  Ian Molton <spyro@f2s.com>
8  *
9  *  Modified: 2004, Oct     Szabolcs Gyurko
10  *
11  *  You may use this code as per GPL version 2
12  */
13 
14 #include <linux/ctype.h>
15 #include <linux/power_supply.h>
16 #include <linux/slab.h>
17 #include <linux/stat.h>
18 
19 #include "power_supply.h"
20 
21 /*
22  * This is because the name "current" breaks the device attr macro.
23  * The "current" word resolves to "(get_current())" so instead of
24  * "current" "(get_current())" appears in the sysfs.
25  *
26  * The source of this definition is the device.h which calls __ATTR
27  * macro in sysfs.h which calls the __stringify macro.
28  *
29  * Only modification that the name is not tried to be resolved
30  * (as a macro let's say).
31  */
32 
33 #define POWER_SUPPLY_ATTR(_name)					\
34 {									\
35 	.attr = { .name = #_name },					\
36 	.show = power_supply_show_property,				\
37 	.store = power_supply_store_property,				\
38 }
39 
40 static struct device_attribute power_supply_attrs[];
41 
power_supply_show_property(struct device * dev,struct device_attribute * attr,char * buf)42 static ssize_t power_supply_show_property(struct device *dev,
43 					  struct device_attribute *attr,
44 					  char *buf) {
45 	static char *type_text[] = {
46 		"Unknown", "Battery", "UPS", "Mains", "USB",
47 		"USB_DCP", "USB_CDP", "USB_ACA"
48 	};
49 	static char *status_text[] = {
50 		"Unknown", "Charging", "Discharging", "Not charging", "Full"
51 	};
52 	static char *charge_type[] = {
53 		"Unknown", "N/A", "Trickle", "Fast"
54 	};
55 	static char *health_text[] = {
56 		"Unknown", "Good", "Overheat", "Dead", "Over voltage",
57 		"Unspecified failure", "Cold",
58 	};
59 	static char *technology_text[] = {
60 		"Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd",
61 		"LiMn"
62 	};
63 	static char *capacity_level_text[] = {
64 		"Unknown", "Critical", "Low", "Normal", "High", "Full"
65 	};
66 	static char *scope_text[] = {
67 		"Unknown", "System", "Device"
68 	};
69 	ssize_t ret = 0;
70 	struct power_supply *psy = dev_get_drvdata(dev);
71 	const ptrdiff_t off = attr - power_supply_attrs;
72 	union power_supply_propval value;
73 
74 	if (off == POWER_SUPPLY_PROP_TYPE)
75 		value.intval = psy->type;
76 	else
77 		ret = psy->get_property(psy, off, &value);
78 
79 	if (ret < 0) {
80 		if (ret == -ENODATA)
81 			dev_dbg(dev, "driver has no data for `%s' property\n",
82 				attr->attr.name);
83 		else if (ret != -ENODEV)
84 			dev_err(dev, "driver failed to report `%s' property: %zd\n",
85 				attr->attr.name, ret);
86 		return ret;
87 	}
88 
89 	if (off == POWER_SUPPLY_PROP_STATUS)
90 		return sprintf(buf, "%s\n", status_text[value.intval]);
91 	else if (off == POWER_SUPPLY_PROP_CHARGE_TYPE)
92 		return sprintf(buf, "%s\n", charge_type[value.intval]);
93 	else if (off == POWER_SUPPLY_PROP_HEALTH)
94 		return sprintf(buf, "%s\n", health_text[value.intval]);
95 	else if (off == POWER_SUPPLY_PROP_TECHNOLOGY)
96 		return sprintf(buf, "%s\n", technology_text[value.intval]);
97 	else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
98 		return sprintf(buf, "%s\n", capacity_level_text[value.intval]);
99 	else if (off == POWER_SUPPLY_PROP_TYPE)
100 		return sprintf(buf, "%s\n", type_text[value.intval]);
101 	else if (off == POWER_SUPPLY_PROP_SCOPE)
102 		return sprintf(buf, "%s\n", scope_text[value.intval]);
103 	else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
104 		return sprintf(buf, "%s\n", value.strval);
105 
106 	return sprintf(buf, "%d\n", value.intval);
107 }
108 
power_supply_store_property(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)109 static ssize_t power_supply_store_property(struct device *dev,
110 					   struct device_attribute *attr,
111 					   const char *buf, size_t count) {
112 	ssize_t ret;
113 	struct power_supply *psy = dev_get_drvdata(dev);
114 	const ptrdiff_t off = attr - power_supply_attrs;
115 	union power_supply_propval value;
116 	long long_val;
117 
118 	/* TODO: support other types than int */
119 	ret = strict_strtol(buf, 10, &long_val);
120 	if (ret < 0)
121 		return ret;
122 
123 	value.intval = long_val;
124 
125 	ret = psy->set_property(psy, off, &value);
126 	if (ret < 0)
127 		return ret;
128 
129 	return count;
130 }
131 
132 /* Must be in the same order as POWER_SUPPLY_PROP_* */
133 static struct device_attribute power_supply_attrs[] = {
134 	/* Properties of type `int' */
135 	POWER_SUPPLY_ATTR(status),
136 	POWER_SUPPLY_ATTR(charge_type),
137 	POWER_SUPPLY_ATTR(health),
138 	POWER_SUPPLY_ATTR(present),
139 	POWER_SUPPLY_ATTR(online),
140 	POWER_SUPPLY_ATTR(technology),
141 	POWER_SUPPLY_ATTR(cycle_count),
142 	POWER_SUPPLY_ATTR(voltage_max),
143 	POWER_SUPPLY_ATTR(voltage_min),
144 	POWER_SUPPLY_ATTR(voltage_max_design),
145 	POWER_SUPPLY_ATTR(voltage_min_design),
146 	POWER_SUPPLY_ATTR(voltage_now),
147 	POWER_SUPPLY_ATTR(voltage_avg),
148 	POWER_SUPPLY_ATTR(current_max),
149 	POWER_SUPPLY_ATTR(current_now),
150 	POWER_SUPPLY_ATTR(current_avg),
151 	POWER_SUPPLY_ATTR(power_now),
152 	POWER_SUPPLY_ATTR(power_avg),
153 	POWER_SUPPLY_ATTR(charge_full_design),
154 	POWER_SUPPLY_ATTR(charge_empty_design),
155 	POWER_SUPPLY_ATTR(charge_full),
156 	POWER_SUPPLY_ATTR(charge_empty),
157 	POWER_SUPPLY_ATTR(charge_now),
158 	POWER_SUPPLY_ATTR(charge_avg),
159 	POWER_SUPPLY_ATTR(charge_counter),
160 	POWER_SUPPLY_ATTR(energy_full_design),
161 	POWER_SUPPLY_ATTR(energy_empty_design),
162 	POWER_SUPPLY_ATTR(energy_full),
163 	POWER_SUPPLY_ATTR(energy_empty),
164 	POWER_SUPPLY_ATTR(energy_now),
165 	POWER_SUPPLY_ATTR(energy_avg),
166 	POWER_SUPPLY_ATTR(capacity),
167 	POWER_SUPPLY_ATTR(capacity_level),
168 	POWER_SUPPLY_ATTR(temp),
169 	POWER_SUPPLY_ATTR(temp_ambient),
170 	POWER_SUPPLY_ATTR(time_to_empty_now),
171 	POWER_SUPPLY_ATTR(time_to_empty_avg),
172 	POWER_SUPPLY_ATTR(time_to_full_now),
173 	POWER_SUPPLY_ATTR(time_to_full_avg),
174 	POWER_SUPPLY_ATTR(type),
175 	POWER_SUPPLY_ATTR(scope),
176 	/* Properties of type `const char *' */
177 	POWER_SUPPLY_ATTR(model_name),
178 	POWER_SUPPLY_ATTR(manufacturer),
179 	POWER_SUPPLY_ATTR(serial_number),
180 };
181 
182 static struct attribute *
183 __power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1];
184 
power_supply_attr_is_visible(struct kobject * kobj,struct attribute * attr,int attrno)185 static umode_t power_supply_attr_is_visible(struct kobject *kobj,
186 					   struct attribute *attr,
187 					   int attrno)
188 {
189 	struct device *dev = container_of(kobj, struct device, kobj);
190 	struct power_supply *psy = dev_get_drvdata(dev);
191 	umode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
192 	int i;
193 
194 	if (attrno == POWER_SUPPLY_PROP_TYPE)
195 		return mode;
196 
197 	for (i = 0; i < psy->num_properties; i++) {
198 		int property = psy->properties[i];
199 
200 		if (property == attrno) {
201 			if (psy->property_is_writeable &&
202 			    psy->property_is_writeable(psy, property) > 0)
203 				mode |= S_IWUSR;
204 
205 			return mode;
206 		}
207 	}
208 
209 	return 0;
210 }
211 
212 static struct attribute_group power_supply_attr_group = {
213 	.attrs = __power_supply_attrs,
214 	.is_visible = power_supply_attr_is_visible,
215 };
216 
217 static const struct attribute_group *power_supply_attr_groups[] = {
218 	&power_supply_attr_group,
219 	NULL,
220 };
221 
power_supply_init_attrs(struct device_type * dev_type)222 void power_supply_init_attrs(struct device_type *dev_type)
223 {
224 	int i;
225 
226 	dev_type->groups = power_supply_attr_groups;
227 
228 	for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)
229 		__power_supply_attrs[i] = &power_supply_attrs[i].attr;
230 }
231 
kstruprdup(const char * str,gfp_t gfp)232 static char *kstruprdup(const char *str, gfp_t gfp)
233 {
234 	char *ret, *ustr;
235 
236 	ustr = ret = kmalloc(strlen(str) + 1, gfp);
237 
238 	if (!ret)
239 		return NULL;
240 
241 	while (*str)
242 		*ustr++ = toupper(*str++);
243 
244 	*ustr = 0;
245 
246 	return ret;
247 }
248 
power_supply_uevent(struct device * dev,struct kobj_uevent_env * env)249 int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
250 {
251 	struct power_supply *psy = dev_get_drvdata(dev);
252 	int ret = 0, j;
253 	char *prop_buf;
254 	char *attrname;
255 
256 	dev_dbg(dev, "uevent\n");
257 
258 	if (!psy || !psy->dev) {
259 		dev_dbg(dev, "No power supply yet\n");
260 		return ret;
261 	}
262 
263 	dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name);
264 
265 	ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->name);
266 	if (ret)
267 		return ret;
268 
269 	prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
270 	if (!prop_buf)
271 		return -ENOMEM;
272 
273 	for (j = 0; j < psy->num_properties; j++) {
274 		struct device_attribute *attr;
275 		char *line;
276 
277 		attr = &power_supply_attrs[psy->properties[j]];
278 
279 		ret = power_supply_show_property(dev, attr, prop_buf);
280 		if (ret == -ENODEV || ret == -ENODATA) {
281 			/* When a battery is absent, we expect -ENODEV. Don't abort;
282 			   send the uevent with at least the the PRESENT=0 property */
283 			ret = 0;
284 			continue;
285 		}
286 
287 		if (ret < 0)
288 			goto out;
289 
290 		line = strchr(prop_buf, '\n');
291 		if (line)
292 			*line = 0;
293 
294 		attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
295 		if (!attrname) {
296 			ret = -ENOMEM;
297 			goto out;
298 		}
299 
300 		dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf);
301 
302 		ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
303 		kfree(attrname);
304 		if (ret)
305 			goto out;
306 	}
307 
308 out:
309 	free_page((unsigned long)prop_buf);
310 
311 	return ret;
312 }
313