545 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Sysfs interface for the universal power supply monitor class4 *5 * Copyright © 2007 David Woodhouse <dwmw2@infradead.org>6 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>7 * Copyright © 2004 Szabolcs Gyurko8 * Copyright © 2003 Ian Molton <spyro@f2s.com>9 *10 * Modified: 2004, Oct Szabolcs Gyurko11 */12 13#include <linux/ctype.h>14#include <linux/device.h>15#include <linux/power_supply.h>16#include <linux/slab.h>17#include <linux/stat.h>18#include <linux/string_helpers.h>19 20#include "power_supply.h"21 22#define MAX_PROP_NAME_LEN 3023 24struct power_supply_attr {25 const char *prop_name;26 char attr_name[MAX_PROP_NAME_LEN + 1];27 struct device_attribute dev_attr;28 const char * const *text_values;29 int text_values_len;30};31 32#define _POWER_SUPPLY_ATTR(_name, _text, _len) \33[POWER_SUPPLY_PROP_ ## _name] = \34{ \35 .prop_name = #_name, \36 .attr_name = #_name "\0", \37 .text_values = _text, \38 .text_values_len = _len, \39}40 41#define POWER_SUPPLY_ATTR(_name) _POWER_SUPPLY_ATTR(_name, NULL, 0)42#define _POWER_SUPPLY_ENUM_ATTR(_name, _text) \43 _POWER_SUPPLY_ATTR(_name, _text, ARRAY_SIZE(_text))44#define POWER_SUPPLY_ENUM_ATTR(_name) \45 _POWER_SUPPLY_ENUM_ATTR(_name, POWER_SUPPLY_ ## _name ## _TEXT)46 47static const char * const POWER_SUPPLY_TYPE_TEXT[] = {48 [POWER_SUPPLY_TYPE_UNKNOWN] = "Unknown",49 [POWER_SUPPLY_TYPE_BATTERY] = "Battery",50 [POWER_SUPPLY_TYPE_UPS] = "UPS",51 [POWER_SUPPLY_TYPE_MAINS] = "Mains",52 [POWER_SUPPLY_TYPE_USB] = "USB",53 [POWER_SUPPLY_TYPE_USB_DCP] = "USB_DCP",54 [POWER_SUPPLY_TYPE_USB_CDP] = "USB_CDP",55 [POWER_SUPPLY_TYPE_USB_ACA] = "USB_ACA",56 [POWER_SUPPLY_TYPE_USB_TYPE_C] = "USB_C",57 [POWER_SUPPLY_TYPE_USB_PD] = "USB_PD",58 [POWER_SUPPLY_TYPE_USB_PD_DRP] = "USB_PD_DRP",59 [POWER_SUPPLY_TYPE_APPLE_BRICK_ID] = "BrickID",60 [POWER_SUPPLY_TYPE_WIRELESS] = "Wireless",61};62 63static const char * const POWER_SUPPLY_USB_TYPE_TEXT[] = {64 [POWER_SUPPLY_USB_TYPE_UNKNOWN] = "Unknown",65 [POWER_SUPPLY_USB_TYPE_SDP] = "SDP",66 [POWER_SUPPLY_USB_TYPE_DCP] = "DCP",67 [POWER_SUPPLY_USB_TYPE_CDP] = "CDP",68 [POWER_SUPPLY_USB_TYPE_ACA] = "ACA",69 [POWER_SUPPLY_USB_TYPE_C] = "C",70 [POWER_SUPPLY_USB_TYPE_PD] = "PD",71 [POWER_SUPPLY_USB_TYPE_PD_DRP] = "PD_DRP",72 [POWER_SUPPLY_USB_TYPE_PD_PPS] = "PD_PPS",73 [POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID] = "BrickID",74};75 76static const char * const POWER_SUPPLY_STATUS_TEXT[] = {77 [POWER_SUPPLY_STATUS_UNKNOWN] = "Unknown",78 [POWER_SUPPLY_STATUS_CHARGING] = "Charging",79 [POWER_SUPPLY_STATUS_DISCHARGING] = "Discharging",80 [POWER_SUPPLY_STATUS_NOT_CHARGING] = "Not charging",81 [POWER_SUPPLY_STATUS_FULL] = "Full",82};83 84static const char * const POWER_SUPPLY_CHARGE_TYPE_TEXT[] = {85 [POWER_SUPPLY_CHARGE_TYPE_UNKNOWN] = "Unknown",86 [POWER_SUPPLY_CHARGE_TYPE_NONE] = "N/A",87 [POWER_SUPPLY_CHARGE_TYPE_TRICKLE] = "Trickle",88 [POWER_SUPPLY_CHARGE_TYPE_FAST] = "Fast",89 [POWER_SUPPLY_CHARGE_TYPE_STANDARD] = "Standard",90 [POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE] = "Adaptive",91 [POWER_SUPPLY_CHARGE_TYPE_CUSTOM] = "Custom",92 [POWER_SUPPLY_CHARGE_TYPE_LONGLIFE] = "Long Life",93 [POWER_SUPPLY_CHARGE_TYPE_BYPASS] = "Bypass",94};95 96static const char * const POWER_SUPPLY_HEALTH_TEXT[] = {97 [POWER_SUPPLY_HEALTH_UNKNOWN] = "Unknown",98 [POWER_SUPPLY_HEALTH_GOOD] = "Good",99 [POWER_SUPPLY_HEALTH_OVERHEAT] = "Overheat",100 [POWER_SUPPLY_HEALTH_DEAD] = "Dead",101 [POWER_SUPPLY_HEALTH_OVERVOLTAGE] = "Over voltage",102 [POWER_SUPPLY_HEALTH_UNSPEC_FAILURE] = "Unspecified failure",103 [POWER_SUPPLY_HEALTH_COLD] = "Cold",104 [POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE] = "Watchdog timer expire",105 [POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE] = "Safety timer expire",106 [POWER_SUPPLY_HEALTH_OVERCURRENT] = "Over current",107 [POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED] = "Calibration required",108 [POWER_SUPPLY_HEALTH_WARM] = "Warm",109 [POWER_SUPPLY_HEALTH_COOL] = "Cool",110 [POWER_SUPPLY_HEALTH_HOT] = "Hot",111 [POWER_SUPPLY_HEALTH_NO_BATTERY] = "No battery",112};113 114static const char * const POWER_SUPPLY_TECHNOLOGY_TEXT[] = {115 [POWER_SUPPLY_TECHNOLOGY_UNKNOWN] = "Unknown",116 [POWER_SUPPLY_TECHNOLOGY_NiMH] = "NiMH",117 [POWER_SUPPLY_TECHNOLOGY_LION] = "Li-ion",118 [POWER_SUPPLY_TECHNOLOGY_LIPO] = "Li-poly",119 [POWER_SUPPLY_TECHNOLOGY_LiFe] = "LiFe",120 [POWER_SUPPLY_TECHNOLOGY_NiCd] = "NiCd",121 [POWER_SUPPLY_TECHNOLOGY_LiMn] = "LiMn",122};123 124static const char * const POWER_SUPPLY_CAPACITY_LEVEL_TEXT[] = {125 [POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN] = "Unknown",126 [POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL] = "Critical",127 [POWER_SUPPLY_CAPACITY_LEVEL_LOW] = "Low",128 [POWER_SUPPLY_CAPACITY_LEVEL_NORMAL] = "Normal",129 [POWER_SUPPLY_CAPACITY_LEVEL_HIGH] = "High",130 [POWER_SUPPLY_CAPACITY_LEVEL_FULL] = "Full",131};132 133static const char * const POWER_SUPPLY_SCOPE_TEXT[] = {134 [POWER_SUPPLY_SCOPE_UNKNOWN] = "Unknown",135 [POWER_SUPPLY_SCOPE_SYSTEM] = "System",136 [POWER_SUPPLY_SCOPE_DEVICE] = "Device",137};138 139static const char * const POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT[] = {140 [POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO] = "auto",141 [POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE] = "inhibit-charge",142 [POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE] = "force-discharge",143};144 145static struct power_supply_attr power_supply_attrs[] = {146 /* Properties of type `int' */147 POWER_SUPPLY_ENUM_ATTR(STATUS),148 POWER_SUPPLY_ENUM_ATTR(CHARGE_TYPE),149 POWER_SUPPLY_ENUM_ATTR(HEALTH),150 POWER_SUPPLY_ATTR(PRESENT),151 POWER_SUPPLY_ATTR(ONLINE),152 POWER_SUPPLY_ATTR(AUTHENTIC),153 POWER_SUPPLY_ENUM_ATTR(TECHNOLOGY),154 POWER_SUPPLY_ATTR(CYCLE_COUNT),155 POWER_SUPPLY_ATTR(VOLTAGE_MAX),156 POWER_SUPPLY_ATTR(VOLTAGE_MIN),157 POWER_SUPPLY_ATTR(VOLTAGE_MAX_DESIGN),158 POWER_SUPPLY_ATTR(VOLTAGE_MIN_DESIGN),159 POWER_SUPPLY_ATTR(VOLTAGE_NOW),160 POWER_SUPPLY_ATTR(VOLTAGE_AVG),161 POWER_SUPPLY_ATTR(VOLTAGE_OCV),162 POWER_SUPPLY_ATTR(VOLTAGE_BOOT),163 POWER_SUPPLY_ATTR(CURRENT_MAX),164 POWER_SUPPLY_ATTR(CURRENT_NOW),165 POWER_SUPPLY_ATTR(CURRENT_AVG),166 POWER_SUPPLY_ATTR(CURRENT_BOOT),167 POWER_SUPPLY_ATTR(POWER_NOW),168 POWER_SUPPLY_ATTR(POWER_AVG),169 POWER_SUPPLY_ATTR(CHARGE_FULL_DESIGN),170 POWER_SUPPLY_ATTR(CHARGE_EMPTY_DESIGN),171 POWER_SUPPLY_ATTR(CHARGE_FULL),172 POWER_SUPPLY_ATTR(CHARGE_EMPTY),173 POWER_SUPPLY_ATTR(CHARGE_NOW),174 POWER_SUPPLY_ATTR(CHARGE_AVG),175 POWER_SUPPLY_ATTR(CHARGE_COUNTER),176 POWER_SUPPLY_ATTR(CONSTANT_CHARGE_CURRENT),177 POWER_SUPPLY_ATTR(CONSTANT_CHARGE_CURRENT_MAX),178 POWER_SUPPLY_ATTR(CONSTANT_CHARGE_VOLTAGE),179 POWER_SUPPLY_ATTR(CONSTANT_CHARGE_VOLTAGE_MAX),180 POWER_SUPPLY_ATTR(CHARGE_CONTROL_LIMIT),181 POWER_SUPPLY_ATTR(CHARGE_CONTROL_LIMIT_MAX),182 POWER_SUPPLY_ATTR(CHARGE_CONTROL_START_THRESHOLD),183 POWER_SUPPLY_ATTR(CHARGE_CONTROL_END_THRESHOLD),184 POWER_SUPPLY_ENUM_ATTR(CHARGE_BEHAVIOUR),185 POWER_SUPPLY_ATTR(INPUT_CURRENT_LIMIT),186 POWER_SUPPLY_ATTR(INPUT_VOLTAGE_LIMIT),187 POWER_SUPPLY_ATTR(INPUT_POWER_LIMIT),188 POWER_SUPPLY_ATTR(ENERGY_FULL_DESIGN),189 POWER_SUPPLY_ATTR(ENERGY_EMPTY_DESIGN),190 POWER_SUPPLY_ATTR(ENERGY_FULL),191 POWER_SUPPLY_ATTR(ENERGY_EMPTY),192 POWER_SUPPLY_ATTR(ENERGY_NOW),193 POWER_SUPPLY_ATTR(ENERGY_AVG),194 POWER_SUPPLY_ATTR(CAPACITY),195 POWER_SUPPLY_ATTR(CAPACITY_ALERT_MIN),196 POWER_SUPPLY_ATTR(CAPACITY_ALERT_MAX),197 POWER_SUPPLY_ATTR(CAPACITY_ERROR_MARGIN),198 POWER_SUPPLY_ENUM_ATTR(CAPACITY_LEVEL),199 POWER_SUPPLY_ATTR(TEMP),200 POWER_SUPPLY_ATTR(TEMP_MAX),201 POWER_SUPPLY_ATTR(TEMP_MIN),202 POWER_SUPPLY_ATTR(TEMP_ALERT_MIN),203 POWER_SUPPLY_ATTR(TEMP_ALERT_MAX),204 POWER_SUPPLY_ATTR(TEMP_AMBIENT),205 POWER_SUPPLY_ATTR(TEMP_AMBIENT_ALERT_MIN),206 POWER_SUPPLY_ATTR(TEMP_AMBIENT_ALERT_MAX),207 POWER_SUPPLY_ATTR(TIME_TO_EMPTY_NOW),208 POWER_SUPPLY_ATTR(TIME_TO_EMPTY_AVG),209 POWER_SUPPLY_ATTR(TIME_TO_FULL_NOW),210 POWER_SUPPLY_ATTR(TIME_TO_FULL_AVG),211 POWER_SUPPLY_ENUM_ATTR(TYPE),212 POWER_SUPPLY_ENUM_ATTR(USB_TYPE),213 POWER_SUPPLY_ENUM_ATTR(SCOPE),214 POWER_SUPPLY_ATTR(PRECHARGE_CURRENT),215 POWER_SUPPLY_ATTR(CHARGE_TERM_CURRENT),216 POWER_SUPPLY_ATTR(CALIBRATE),217 POWER_SUPPLY_ATTR(MANUFACTURE_YEAR),218 POWER_SUPPLY_ATTR(MANUFACTURE_MONTH),219 POWER_SUPPLY_ATTR(MANUFACTURE_DAY),220 /* Properties of type `const char *' */221 POWER_SUPPLY_ATTR(MODEL_NAME),222 POWER_SUPPLY_ATTR(MANUFACTURER),223 POWER_SUPPLY_ATTR(SERIAL_NUMBER),224};225#define POWER_SUPPLY_ATTR_CNT ARRAY_SIZE(power_supply_attrs)226 227static struct attribute *228__power_supply_attrs[POWER_SUPPLY_ATTR_CNT + 1];229 230static struct power_supply_attr *to_ps_attr(struct device_attribute *attr)231{232 return container_of(attr, struct power_supply_attr, dev_attr);233}234 235static enum power_supply_property dev_attr_psp(struct device_attribute *attr)236{237 return to_ps_attr(attr) - power_supply_attrs;238}239 240static ssize_t power_supply_show_enum_with_available(241 struct device *dev, const char * const labels[], int label_count,242 unsigned int available_values, int value, char *buf)243{244 bool match = false, available, active;245 ssize_t count = 0;246 int i;247 248 for (i = 0; i < label_count; i++) {249 available = available_values & BIT(i);250 active = i == value;251 252 if (available && active) {253 count += sysfs_emit_at(buf, count, "[%s] ", labels[i]);254 match = true;255 } else if (available) {256 count += sysfs_emit_at(buf, count, "%s ", labels[i]);257 }258 }259 260 if (!match) {261 dev_warn(dev, "driver reporting unavailable enum value %d\n", value);262 return -EINVAL;263 }264 265 if (count)266 buf[count - 1] = '\n';267 268 return count;269}270 271static ssize_t power_supply_show_property(struct device *dev,272 struct device_attribute *attr,273 char *buf) {274 ssize_t ret;275 struct power_supply *psy = dev_get_drvdata(dev);276 struct power_supply_attr *ps_attr = to_ps_attr(attr);277 enum power_supply_property psp = dev_attr_psp(attr);278 union power_supply_propval value;279 280 if (psp == POWER_SUPPLY_PROP_TYPE) {281 value.intval = psy->desc->type;282 } else {283 ret = power_supply_get_property(psy, psp, &value);284 285 if (ret < 0) {286 if (ret == -ENODATA)287 dev_dbg_ratelimited(dev,288 "driver has no data for `%s' property\n",289 attr->attr.name);290 else if (ret != -ENODEV && ret != -EAGAIN)291 dev_err_ratelimited(dev,292 "driver failed to report `%s' property: %zd\n",293 attr->attr.name, ret);294 return ret;295 }296 }297 298 switch (psp) {299 case POWER_SUPPLY_PROP_USB_TYPE:300 ret = power_supply_show_enum_with_available(301 dev, POWER_SUPPLY_USB_TYPE_TEXT,302 ARRAY_SIZE(POWER_SUPPLY_USB_TYPE_TEXT),303 psy->desc->usb_types, value.intval, buf);304 break;305 case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:306 ret = power_supply_charge_behaviour_show(dev, psy->desc->charge_behaviours,307 value.intval, buf);308 break;309 case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER:310 ret = sysfs_emit(buf, "%s\n", value.strval);311 break;312 default:313 if (ps_attr->text_values_len > 0 &&314 value.intval < ps_attr->text_values_len && value.intval >= 0) {315 ret = sysfs_emit(buf, "%s\n", ps_attr->text_values[value.intval]);316 } else {317 ret = sysfs_emit(buf, "%d\n", value.intval);318 }319 }320 321 return ret;322}323 324static ssize_t power_supply_store_property(struct device *dev,325 struct device_attribute *attr,326 const char *buf, size_t count) {327 ssize_t ret;328 struct power_supply *psy = dev_get_drvdata(dev);329 struct power_supply_attr *ps_attr = to_ps_attr(attr);330 enum power_supply_property psp = dev_attr_psp(attr);331 union power_supply_propval value;332 333 ret = -EINVAL;334 if (ps_attr->text_values_len > 0) {335 ret = __sysfs_match_string(ps_attr->text_values,336 ps_attr->text_values_len, buf);337 }338 339 /*340 * If no match was found, then check to see if it is an integer.341 * Integer values are valid for enums in addition to the text value.342 */343 if (ret < 0) {344 long long_val;345 346 ret = kstrtol(buf, 10, &long_val);347 if (ret < 0)348 return ret;349 350 ret = long_val;351 }352 353 value.intval = ret;354 355 ret = power_supply_set_property(psy, psp, &value);356 if (ret < 0)357 return ret;358 359 return count;360}361 362static umode_t power_supply_attr_is_visible(struct kobject *kobj,363 struct attribute *attr,364 int attrno)365{366 struct device *dev = kobj_to_dev(kobj);367 struct power_supply *psy = dev_get_drvdata(dev);368 umode_t mode = S_IRUSR | S_IRGRP | S_IROTH;369 int i;370 371 if (!power_supply_attrs[attrno].prop_name)372 return 0;373 374 if (attrno == POWER_SUPPLY_PROP_TYPE)375 return mode;376 377 for (i = 0; i < psy->desc->num_properties; i++) {378 int property = psy->desc->properties[i];379 380 if (property == attrno) {381 if (power_supply_property_is_writeable(psy, property) > 0)382 mode |= S_IWUSR;383 384 return mode;385 }386 }387 388 if (power_supply_battery_info_has_prop(psy->battery_info, attrno))389 return mode;390 391 return 0;392}393 394static const struct attribute_group power_supply_attr_group = {395 .attrs = __power_supply_attrs,396 .is_visible = power_supply_attr_is_visible,397};398 399const struct attribute_group *power_supply_attr_groups[] = {400 &power_supply_attr_group,401 NULL402};403 404void power_supply_init_attrs(void)405{406 int i;407 408 for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++) {409 struct device_attribute *attr;410 411 if (!power_supply_attrs[i].prop_name) {412 pr_warn("%s: Property %d skipped because it is missing from power_supply_attrs\n",413 __func__, i);414 sprintf(power_supply_attrs[i].attr_name, "_err_%d", i);415 } else {416 string_lower(power_supply_attrs[i].attr_name,417 power_supply_attrs[i].attr_name);418 }419 420 attr = &power_supply_attrs[i].dev_attr;421 422 attr->attr.name = power_supply_attrs[i].attr_name;423 attr->show = power_supply_show_property;424 attr->store = power_supply_store_property;425 __power_supply_attrs[i] = &attr->attr;426 }427}428 429static int add_prop_uevent(const struct device *dev, struct kobj_uevent_env *env,430 enum power_supply_property prop, char *prop_buf)431{432 int ret = 0;433 struct power_supply_attr *pwr_attr;434 struct device_attribute *dev_attr;435 char *line;436 437 pwr_attr = &power_supply_attrs[prop];438 dev_attr = &pwr_attr->dev_attr;439 440 ret = power_supply_show_property((struct device *)dev, dev_attr, prop_buf);441 if (ret == -ENODEV || ret == -ENODATA) {442 /*443 * When a battery is absent, we expect -ENODEV. Don't abort;444 * send the uevent with at least the PRESENT=0 property445 */446 return 0;447 }448 449 if (ret < 0)450 return ret;451 452 line = strchr(prop_buf, '\n');453 if (line)454 *line = 0;455 456 return add_uevent_var(env, "POWER_SUPPLY_%s=%s",457 pwr_attr->prop_name, prop_buf);458}459 460int power_supply_uevent(const struct device *dev, struct kobj_uevent_env *env)461{462 const struct power_supply *psy = dev_get_drvdata(dev);463 const enum power_supply_property *battery_props =464 power_supply_battery_info_properties;465 unsigned long psy_drv_properties[POWER_SUPPLY_ATTR_CNT /466 sizeof(unsigned long) + 1] = {0};467 int ret = 0, j;468 char *prop_buf;469 470 if (!psy || !psy->desc) {471 dev_dbg(dev, "No power supply yet\n");472 return ret;473 }474 475 ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->desc->name);476 if (ret)477 return ret;478 479 /*480 * Kernel generates KOBJ_REMOVE uevent in device removal path, after481 * resources have been freed. Exit early to avoid use-after-free.482 */483 if (psy->removing)484 return 0;485 486 prop_buf = (char *)get_zeroed_page(GFP_KERNEL);487 if (!prop_buf)488 return -ENOMEM;489 490 ret = add_prop_uevent(dev, env, POWER_SUPPLY_PROP_TYPE, prop_buf);491 if (ret)492 goto out;493 494 for (j = 0; j < psy->desc->num_properties; j++) {495 set_bit(psy->desc->properties[j], psy_drv_properties);496 ret = add_prop_uevent(dev, env, psy->desc->properties[j],497 prop_buf);498 if (ret)499 goto out;500 }501 502 for (j = 0; j < power_supply_battery_info_properties_size; j++) {503 if (test_bit(battery_props[j], psy_drv_properties))504 continue;505 if (!power_supply_battery_info_has_prop(psy->battery_info,506 battery_props[j]))507 continue;508 ret = add_prop_uevent(dev, env, battery_props[j],509 prop_buf);510 if (ret)511 goto out;512 }513 514out:515 free_page((unsigned long)prop_buf);516 517 return ret;518}519 520ssize_t power_supply_charge_behaviour_show(struct device *dev,521 unsigned int available_behaviours,522 enum power_supply_charge_behaviour current_behaviour,523 char *buf)524{525 return power_supply_show_enum_with_available(526 dev, POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT,527 ARRAY_SIZE(POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT),528 available_behaviours, current_behaviour, buf);529}530EXPORT_SYMBOL_GPL(power_supply_charge_behaviour_show);531 532int power_supply_charge_behaviour_parse(unsigned int available_behaviours, const char *buf)533{534 int i = sysfs_match_string(POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT, buf);535 536 if (i < 0)537 return i;538 539 if (available_behaviours & BIT(i))540 return i;541 542 return -EINVAL;543}544EXPORT_SYMBOL_GPL(power_supply_charge_behaviour_parse);545