power_supply_sysfs.c 11.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
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;
120
	struct power_supply *psy = dev_get_drvdata(dev);
121
	enum power_supply_property psp = attr - power_supply_attrs;
122 123
	union power_supply_propval value;

124
	if (psp == POWER_SUPPLY_PROP_TYPE) {
125
		value.intval = psy->desc->type;
126
	} else {
127
		ret = power_supply_get_property(psy, psp, &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
				dev_err_ratelimited(dev,
					"driver failed to report `%s' property: %zd\n",
136 137 138
					attr->attr.name, ret);
			return ret;
		}
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
	switch (psp) {
	case POWER_SUPPLY_PROP_STATUS:
		ret = sprintf(buf, "%s\n",
			      power_supply_status_text[value.intval]);
		break;
	case POWER_SUPPLY_PROP_CHARGE_TYPE:
		ret = sprintf(buf, "%s\n",
			      power_supply_charge_type_text[value.intval]);
		break;
	case POWER_SUPPLY_PROP_HEALTH:
		ret = sprintf(buf, "%s\n",
			      power_supply_health_text[value.intval]);
		break;
	case POWER_SUPPLY_PROP_TECHNOLOGY:
		ret = sprintf(buf, "%s\n",
			      power_supply_technology_text[value.intval]);
		break;
	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
		ret = sprintf(buf, "%s\n",
			      power_supply_capacity_level_text[value.intval]);
		break;
	case POWER_SUPPLY_PROP_TYPE:
		ret = sprintf(buf, "%s\n",
			      power_supply_type_text[value.intval]);
		break;
166 167 168 169 170
	case POWER_SUPPLY_PROP_USB_TYPE:
		ret = power_supply_show_usb_type(dev, psy->desc->usb_types,
						 psy->desc->num_usb_types,
						 &value, buf);
		break;
171 172 173 174 175 176 177 178 179 180 181 182
	case POWER_SUPPLY_PROP_SCOPE:
		ret = sprintf(buf, "%s\n",
			      power_supply_scope_text[value.intval]);
		break;
	case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER:
		ret = sprintf(buf, "%s\n", value.strval);
		break;
	default:
		ret = sprintf(buf, "%d\n", value.intval);
	}

	return ret;
183 184
}

185 186 187 188 189
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);
190
	enum power_supply_property psp = attr - power_supply_attrs;
191 192
	union power_supply_propval value;

193
	switch (psp) {
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
	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;
	}
229

230
	value.intval = ret;
231

232
	ret = power_supply_set_property(psy, psp, &value);
233 234 235 236 237 238
	if (ret < 0)
		return ret;

	return count;
}

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

312 313
static struct attribute *
__power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1];
314

315
static umode_t power_supply_attr_is_visible(struct kobject *kobj,
316 317
					   struct attribute *attr,
					   int attrno)
318
{
319 320
	struct device *dev = container_of(kobj, struct device, kobj);
	struct power_supply *psy = dev_get_drvdata(dev);
321
	umode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
322
	int i;
323

324 325 326
	if (attrno == POWER_SUPPLY_PROP_TYPE)
		return mode;

327 328
	for (i = 0; i < psy->desc->num_properties; i++) {
		int property = psy->desc->properties[i];
329 330

		if (property == attrno) {
331
			if (psy->desc->property_is_writeable &&
332
			    psy->desc->property_is_writeable(psy, property) > 0)
333 334 335 336
				mode |= S_IWUSR;

			return mode;
		}
337 338
	}

339
	return 0;
340 341
}

342 343 344 345 346 347 348 349 350 351 352
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)
353 354 355
{
	int i;

356
	dev_type->groups = power_supply_attr_groups;
357

358 359
	for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)
		__power_supply_attrs[i] = &power_supply_attrs[i].attr;
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
}

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

379
int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
380 381
{
	struct power_supply *psy = dev_get_drvdata(dev);
382
	int ret = 0, j;
383 384 385
	char *prop_buf;
	char *attrname;

386
	if (!psy || !psy->desc) {
387 388 389 390
		dev_dbg(dev, "No power supply yet\n");
		return ret;
	}

391
	ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->desc->name);
392 393 394 395 396 397 398
	if (ret)
		return ret;

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

399
	for (j = 0; j < psy->desc->num_properties; j++) {
400 401 402
		struct device_attribute *attr;
		char *line;

403
		attr = &power_supply_attrs[psy->desc->properties[j]];
404 405

		ret = power_supply_show_property(dev, attr, prop_buf);
406
		if (ret == -ENODEV || ret == -ENODATA) {
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
			/* 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;
		}

426
		ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
427 428 429 430 431 432 433 434 435 436
		kfree(attrname);
		if (ret)
			goto out;
	}

out:
	free_page((unsigned long)prop_buf);

	return ret;
}