power_supply_sysfs.c 10.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 *  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>
15
#include <linux/device.h>
16
#include <linux/power_supply.h>
17
#include <linux/slab.h>
18
#include <linux/stat.h>
19

20 21
#include "power_supply.h"

22 23 24 25 26 27 28 29 30 31 32 33 34 35
/*
 * 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)					\
{									\
36
	.attr = { .name = #_name },					\
37
	.show = power_supply_show_property,				\
38
	.store = power_supply_store_property,				\
39 40 41 42
}

static struct device_attribute power_supply_attrs[];

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
static const char * const power_supply_type_text[] = {
	"Unknown", "Battery", "UPS", "Mains", "USB",
	"USB_DCP", "USB_CDP", "USB_ACA", "USB_C",
	"USB_PD", "USB_PD_DRP", "BrickID"
};

static const char * const power_supply_status_text[] = {
	"Unknown", "Charging", "Discharging", "Not charging", "Full"
};

static const char * const power_supply_charge_type_text[] = {
	"Unknown", "N/A", "Trickle", "Fast"
};

static const char * const power_supply_health_text[] = {
	"Unknown", "Good", "Overheat", "Dead", "Over voltage",
	"Unspecified failure", "Cold", "Watchdog timer expire",
	"Safety timer expire"
};

static const char * const power_supply_technology_text[] = {
	"Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd",
	"LiMn"
};

static const char * const power_supply_capacity_level_text[] = {
	"Unknown", "Critical", "Low", "Normal", "High", "Full"
};

static const char * const power_supply_scope_text[] = {
	"Unknown", "System", "Device"
};

76 77 78
static ssize_t power_supply_show_property(struct device *dev,
					  struct device_attribute *attr,
					  char *buf) {
79
	ssize_t ret = 0;
80 81 82 83
	struct power_supply *psy = dev_get_drvdata(dev);
	const ptrdiff_t off = attr - power_supply_attrs;
	union power_supply_propval value;

84
	if (off == POWER_SUPPLY_PROP_TYPE) {
85
		value.intval = psy->desc->type;
86
	} else {
87
		ret = power_supply_get_property(psy, off, &value);
88

89 90 91 92
		if (ret < 0) {
			if (ret == -ENODATA)
				dev_dbg(dev, "driver has no data for `%s' property\n",
					attr->attr.name);
93
			else if (ret != -ENODEV && ret != -EAGAIN)
94 95 96 97
				dev_err(dev, "driver failed to report `%s' property: %zd\n",
					attr->attr.name, ret);
			return ret;
		}
98 99 100
	}

	if (off == POWER_SUPPLY_PROP_STATUS)
101 102
		return sprintf(buf, "%s\n",
			       power_supply_status_text[value.intval]);
103
	else if (off == POWER_SUPPLY_PROP_CHARGE_TYPE)
104 105
		return sprintf(buf, "%s\n",
			       power_supply_charge_type_text[value.intval]);
106
	else if (off == POWER_SUPPLY_PROP_HEALTH)
107 108
		return sprintf(buf, "%s\n",
			       power_supply_health_text[value.intval]);
109
	else if (off == POWER_SUPPLY_PROP_TECHNOLOGY)
110 111
		return sprintf(buf, "%s\n",
			       power_supply_technology_text[value.intval]);
112
	else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
113 114
		return sprintf(buf, "%s\n",
			       power_supply_capacity_level_text[value.intval]);
115
	else if (off == POWER_SUPPLY_PROP_TYPE)
116 117
		return sprintf(buf, "%s\n",
			       power_supply_type_text[value.intval]);
118
	else if (off == POWER_SUPPLY_PROP_SCOPE)
119 120
		return sprintf(buf, "%s\n",
			       power_supply_scope_text[value.intval]);
121 122 123 124 125 126
	else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
		return sprintf(buf, "%s\n", value.strval);

	return sprintf(buf, "%d\n", value.intval);
}

127 128 129 130 131 132 133 134
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;

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
	/* maybe it is a enum property? */
	switch (off) {
	case POWER_SUPPLY_PROP_STATUS:
		ret = sysfs_match_string(power_supply_status_text, buf);
		break;
	case POWER_SUPPLY_PROP_CHARGE_TYPE:
		ret = sysfs_match_string(power_supply_charge_type_text, buf);
		break;
	case POWER_SUPPLY_PROP_HEALTH:
		ret = sysfs_match_string(power_supply_health_text, buf);
		break;
	case POWER_SUPPLY_PROP_TECHNOLOGY:
		ret = sysfs_match_string(power_supply_technology_text, buf);
		break;
	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
		ret = sysfs_match_string(power_supply_capacity_level_text, buf);
		break;
	case POWER_SUPPLY_PROP_SCOPE:
		ret = sysfs_match_string(power_supply_scope_text, buf);
		break;
	default:
		ret = -EINVAL;
	}

	/*
	 * If no match was found, then check to see if it is an integer.
	 * Integer values are valid for enums in addition to the text value.
	 */
	if (ret < 0) {
		long long_val;

		ret = kstrtol(buf, 10, &long_val);
		if (ret < 0)
			return ret;

		ret = long_val;
	}
172

173
	value.intval = ret;
174

175
	ret = power_supply_set_property(psy, off, &value);
176 177 178 179 180 181
	if (ret < 0)
		return ret;

	return count;
}

182 183 184 185
/* 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),
186
	POWER_SUPPLY_ATTR(charge_type),
187 188 189
	POWER_SUPPLY_ATTR(health),
	POWER_SUPPLY_ATTR(present),
	POWER_SUPPLY_ATTR(online),
190
	POWER_SUPPLY_ATTR(authentic),
191
	POWER_SUPPLY_ATTR(technology),
192
	POWER_SUPPLY_ATTR(cycle_count),
193 194
	POWER_SUPPLY_ATTR(voltage_max),
	POWER_SUPPLY_ATTR(voltage_min),
195 196 197 198
	POWER_SUPPLY_ATTR(voltage_max_design),
	POWER_SUPPLY_ATTR(voltage_min_design),
	POWER_SUPPLY_ATTR(voltage_now),
	POWER_SUPPLY_ATTR(voltage_avg),
199
	POWER_SUPPLY_ATTR(voltage_ocv),
200
	POWER_SUPPLY_ATTR(voltage_boot),
201
	POWER_SUPPLY_ATTR(current_max),
202 203
	POWER_SUPPLY_ATTR(current_now),
	POWER_SUPPLY_ATTR(current_avg),
204
	POWER_SUPPLY_ATTR(current_boot),
205 206
	POWER_SUPPLY_ATTR(power_now),
	POWER_SUPPLY_ATTR(power_avg),
207 208 209 210 211 212
	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),
213
	POWER_SUPPLY_ATTR(charge_counter),
214
	POWER_SUPPLY_ATTR(constant_charge_current),
215
	POWER_SUPPLY_ATTR(constant_charge_current_max),
216
	POWER_SUPPLY_ATTR(constant_charge_voltage),
217
	POWER_SUPPLY_ATTR(constant_charge_voltage_max),
218 219
	POWER_SUPPLY_ATTR(charge_control_limit),
	POWER_SUPPLY_ATTR(charge_control_limit_max),
220
	POWER_SUPPLY_ATTR(input_current_limit),
221 222 223 224 225 226 227
	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),
228 229
	POWER_SUPPLY_ATTR(capacity_alert_min),
	POWER_SUPPLY_ATTR(capacity_alert_max),
230
	POWER_SUPPLY_ATTR(capacity_level),
231
	POWER_SUPPLY_ATTR(temp),
232 233
	POWER_SUPPLY_ATTR(temp_max),
	POWER_SUPPLY_ATTR(temp_min),
234 235
	POWER_SUPPLY_ATTR(temp_alert_min),
	POWER_SUPPLY_ATTR(temp_alert_max),
236
	POWER_SUPPLY_ATTR(temp_ambient),
237 238
	POWER_SUPPLY_ATTR(temp_ambient_alert_min),
	POWER_SUPPLY_ATTR(temp_ambient_alert_max),
239 240 241 242
	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),
243
	POWER_SUPPLY_ATTR(type),
244
	POWER_SUPPLY_ATTR(scope),
245
	POWER_SUPPLY_ATTR(precharge_current),
246
	POWER_SUPPLY_ATTR(charge_term_current),
247
	POWER_SUPPLY_ATTR(calibrate),
248 249 250
	/* Properties of type `const char *' */
	POWER_SUPPLY_ATTR(model_name),
	POWER_SUPPLY_ATTR(manufacturer),
251
	POWER_SUPPLY_ATTR(serial_number),
252 253
};

254 255
static struct attribute *
__power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1];
256

257
static umode_t power_supply_attr_is_visible(struct kobject *kobj,
258 259
					   struct attribute *attr,
					   int attrno)
260
{
261 262
	struct device *dev = container_of(kobj, struct device, kobj);
	struct power_supply *psy = dev_get_drvdata(dev);
263
	umode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
264
	int i;
265

266 267 268
	if (attrno == POWER_SUPPLY_PROP_TYPE)
		return mode;

269 270
	for (i = 0; i < psy->desc->num_properties; i++) {
		int property = psy->desc->properties[i];
271 272

		if (property == attrno) {
273
			if (psy->desc->property_is_writeable &&
274
			    psy->desc->property_is_writeable(psy, property) > 0)
275 276 277 278
				mode |= S_IWUSR;

			return mode;
		}
279 280
	}

281
	return 0;
282 283
}

284 285 286 287 288 289 290 291 292 293 294
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)
295 296 297
{
	int i;

298
	dev_type->groups = power_supply_attr_groups;
299

300 301
	for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)
		__power_supply_attrs[i] = &power_supply_attrs[i].attr;
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
}

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;
}

321
int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
322 323
{
	struct power_supply *psy = dev_get_drvdata(dev);
324
	int ret = 0, j;
325 326 327 328 329
	char *prop_buf;
	char *attrname;

	dev_dbg(dev, "uevent\n");

330
	if (!psy || !psy->desc) {
331 332 333 334
		dev_dbg(dev, "No power supply yet\n");
		return ret;
	}

335
	dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->desc->name);
336

337
	ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->desc->name);
338 339 340 341 342 343 344
	if (ret)
		return ret;

	prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
	if (!prop_buf)
		return -ENOMEM;

345
	for (j = 0; j < psy->desc->num_properties; j++) {
346 347 348
		struct device_attribute *attr;
		char *line;

349
		attr = &power_supply_attrs[psy->desc->properties[j]];
350 351

		ret = power_supply_show_property(dev, attr, prop_buf);
352
		if (ret == -ENODEV || ret == -ENODATA) {
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
			/* 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, '\n');
		if (line)
			*line = 0;

		attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
		if (!attrname) {
			ret = -ENOMEM;
			goto out;
		}

		dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf);

374
		ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
375 376 377 378 379 380 381 382 383 384
		kfree(attrname);
		if (ret)
			goto out;
	}

out:
	free_page((unsigned long)prop_buf);

	return ret;
}