power_supply_sysfs.c 11.4 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
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"
};

49 50 51 52 53
static const char * const power_supply_usb_type_text[] = {
	"Unknown", "SDP", "DCP", "CDP", "ACA", "C",
	"PD", "PD_DRP", "PD_PPS", "BrickID"
};

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
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"
};

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
static ssize_t power_supply_show_usb_type(struct device *dev,
					  enum power_supply_usb_type *usb_types,
					  ssize_t num_usb_types,
					  union power_supply_propval *value,
					  char *buf)
{
	enum power_supply_usb_type usb_type;
	ssize_t count = 0;
	bool match = false;
	int i;

	for (i = 0; i < num_usb_types; ++i) {
		usb_type = usb_types[i];

		if (value->intval == usb_type) {
			count += sprintf(buf + count, "[%s] ",
					 power_supply_usb_type_text[usb_type]);
			match = true;
		} else {
			count += sprintf(buf + count, "%s ",
					 power_supply_usb_type_text[usb_type]);
		}
	}

	if (!match) {
		dev_warn(dev, "driver reporting unsupported connected type\n");
		return -EINVAL;
	}

	if (count)
		buf[count - 1] = '\n';

	return count;
}

116 117 118
static ssize_t power_supply_show_property(struct device *dev,
					  struct device_attribute *attr,
					  char *buf) {
119
	ssize_t ret = 0;
120 121 122 123
	struct power_supply *psy = dev_get_drvdata(dev);
	const ptrdiff_t off = attr - power_supply_attrs;
	union power_supply_propval value;

124
	if (off == POWER_SUPPLY_PROP_TYPE) {
125
		value.intval = psy->desc->type;
126
	} else {
127
		ret = power_supply_get_property(psy, off, &value);
128

129 130 131 132
		if (ret < 0) {
			if (ret == -ENODATA)
				dev_dbg(dev, "driver has no data for `%s' property\n",
					attr->attr.name);
133
			else if (ret != -ENODEV && ret != -EAGAIN)
134 135 136 137
				dev_err(dev, "driver failed to report `%s' property: %zd\n",
					attr->attr.name, ret);
			return ret;
		}
138 139 140
	}

	if (off == POWER_SUPPLY_PROP_STATUS)
141 142
		return sprintf(buf, "%s\n",
			       power_supply_status_text[value.intval]);
143
	else if (off == POWER_SUPPLY_PROP_CHARGE_TYPE)
144 145
		return sprintf(buf, "%s\n",
			       power_supply_charge_type_text[value.intval]);
146
	else if (off == POWER_SUPPLY_PROP_HEALTH)
147 148
		return sprintf(buf, "%s\n",
			       power_supply_health_text[value.intval]);
149
	else if (off == POWER_SUPPLY_PROP_TECHNOLOGY)
150 151
		return sprintf(buf, "%s\n",
			       power_supply_technology_text[value.intval]);
152
	else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
153 154
		return sprintf(buf, "%s\n",
			       power_supply_capacity_level_text[value.intval]);
155
	else if (off == POWER_SUPPLY_PROP_TYPE)
156 157
		return sprintf(buf, "%s\n",
			       power_supply_type_text[value.intval]);
158 159 160 161
	else if (off == POWER_SUPPLY_PROP_USB_TYPE)
		return power_supply_show_usb_type(dev, psy->desc->usb_types,
						  psy->desc->num_usb_types,
						  &value, buf);
162
	else if (off == POWER_SUPPLY_PROP_SCOPE)
163 164
		return sprintf(buf, "%s\n",
			       power_supply_scope_text[value.intval]);
165 166 167 168 169 170
	else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
		return sprintf(buf, "%s\n", value.strval);

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

171 172 173 174 175 176 177 178
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;

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
	/* 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;
	}
216

217
	value.intval = ret;
218

219
	ret = power_supply_set_property(psy, off, &value);
220 221 222 223 224 225
	if (ret < 0)
		return ret;

	return count;
}

226 227 228 229
/* 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),
230
	POWER_SUPPLY_ATTR(charge_type),
231 232 233
	POWER_SUPPLY_ATTR(health),
	POWER_SUPPLY_ATTR(present),
	POWER_SUPPLY_ATTR(online),
234
	POWER_SUPPLY_ATTR(authentic),
235
	POWER_SUPPLY_ATTR(technology),
236
	POWER_SUPPLY_ATTR(cycle_count),
237 238
	POWER_SUPPLY_ATTR(voltage_max),
	POWER_SUPPLY_ATTR(voltage_min),
239 240 241 242
	POWER_SUPPLY_ATTR(voltage_max_design),
	POWER_SUPPLY_ATTR(voltage_min_design),
	POWER_SUPPLY_ATTR(voltage_now),
	POWER_SUPPLY_ATTR(voltage_avg),
243
	POWER_SUPPLY_ATTR(voltage_ocv),
244
	POWER_SUPPLY_ATTR(voltage_boot),
245
	POWER_SUPPLY_ATTR(current_max),
246 247
	POWER_SUPPLY_ATTR(current_now),
	POWER_SUPPLY_ATTR(current_avg),
248
	POWER_SUPPLY_ATTR(current_boot),
249 250
	POWER_SUPPLY_ATTR(power_now),
	POWER_SUPPLY_ATTR(power_avg),
251 252 253 254 255 256
	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),
257
	POWER_SUPPLY_ATTR(charge_counter),
258
	POWER_SUPPLY_ATTR(constant_charge_current),
259
	POWER_SUPPLY_ATTR(constant_charge_current_max),
260
	POWER_SUPPLY_ATTR(constant_charge_voltage),
261
	POWER_SUPPLY_ATTR(constant_charge_voltage_max),
262 263
	POWER_SUPPLY_ATTR(charge_control_limit),
	POWER_SUPPLY_ATTR(charge_control_limit_max),
264
	POWER_SUPPLY_ATTR(input_current_limit),
265 266 267 268 269 270 271
	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),
272 273
	POWER_SUPPLY_ATTR(capacity_alert_min),
	POWER_SUPPLY_ATTR(capacity_alert_max),
274
	POWER_SUPPLY_ATTR(capacity_level),
275
	POWER_SUPPLY_ATTR(temp),
276 277
	POWER_SUPPLY_ATTR(temp_max),
	POWER_SUPPLY_ATTR(temp_min),
278 279
	POWER_SUPPLY_ATTR(temp_alert_min),
	POWER_SUPPLY_ATTR(temp_alert_max),
280
	POWER_SUPPLY_ATTR(temp_ambient),
281 282
	POWER_SUPPLY_ATTR(temp_ambient_alert_min),
	POWER_SUPPLY_ATTR(temp_ambient_alert_max),
283 284 285 286
	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),
287
	POWER_SUPPLY_ATTR(type),
288
	POWER_SUPPLY_ATTR(usb_type),
289
	POWER_SUPPLY_ATTR(scope),
290
	POWER_SUPPLY_ATTR(precharge_current),
291
	POWER_SUPPLY_ATTR(charge_term_current),
292
	POWER_SUPPLY_ATTR(calibrate),
293 294 295
	/* Properties of type `const char *' */
	POWER_SUPPLY_ATTR(model_name),
	POWER_SUPPLY_ATTR(manufacturer),
296
	POWER_SUPPLY_ATTR(serial_number),
297 298
};

299 300
static struct attribute *
__power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1];
301

302
static umode_t power_supply_attr_is_visible(struct kobject *kobj,
303 304
					   struct attribute *attr,
					   int attrno)
305
{
306 307
	struct device *dev = container_of(kobj, struct device, kobj);
	struct power_supply *psy = dev_get_drvdata(dev);
308
	umode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
309
	int i;
310

311 312 313
	if (attrno == POWER_SUPPLY_PROP_TYPE)
		return mode;

314 315
	for (i = 0; i < psy->desc->num_properties; i++) {
		int property = psy->desc->properties[i];
316 317

		if (property == attrno) {
318
			if (psy->desc->property_is_writeable &&
319
			    psy->desc->property_is_writeable(psy, property) > 0)
320 321 322 323
				mode |= S_IWUSR;

			return mode;
		}
324 325
	}

326
	return 0;
327 328
}

329 330 331 332 333 334 335 336 337 338 339
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)
340 341 342
{
	int i;

343
	dev_type->groups = power_supply_attr_groups;
344

345 346
	for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)
		__power_supply_attrs[i] = &power_supply_attrs[i].attr;
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
}

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

366
int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
367 368
{
	struct power_supply *psy = dev_get_drvdata(dev);
369
	int ret = 0, j;
370 371 372 373 374
	char *prop_buf;
	char *attrname;

	dev_dbg(dev, "uevent\n");

375
	if (!psy || !psy->desc) {
376 377 378 379
		dev_dbg(dev, "No power supply yet\n");
		return ret;
	}

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

382
	ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->desc->name);
383 384 385 386 387 388 389
	if (ret)
		return ret;

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

390
	for (j = 0; j < psy->desc->num_properties; j++) {
391 392 393
		struct device_attribute *attr;
		char *line;

394
		attr = &power_supply_attrs[psy->desc->properties[j]];
395 396

		ret = power_supply_show_property(dev, attr, prop_buf);
397
		if (ret == -ENODEV || ret == -ENODATA) {
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
			/* 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);

419
		ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
420 421 422 423 424 425 426 427 428 429
		kfree(attrname);
		if (ret)
			goto out;
	}

out:
	free_page((unsigned long)prop_buf);

	return ret;
}