162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Sysfs interface for the universal power supply monitor class 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright © 2007 David Woodhouse <dwmw2@infradead.org> 662306a36Sopenharmony_ci * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> 762306a36Sopenharmony_ci * Copyright © 2004 Szabolcs Gyurko 862306a36Sopenharmony_ci * Copyright © 2003 Ian Molton <spyro@f2s.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Modified: 2004, Oct Szabolcs Gyurko 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/ctype.h> 1462306a36Sopenharmony_ci#include <linux/device.h> 1562306a36Sopenharmony_ci#include <linux/power_supply.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/stat.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "power_supply.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define MAX_PROP_NAME_LEN 30 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct power_supply_attr { 2462306a36Sopenharmony_ci const char *prop_name; 2562306a36Sopenharmony_ci char attr_name[MAX_PROP_NAME_LEN + 1]; 2662306a36Sopenharmony_ci struct device_attribute dev_attr; 2762306a36Sopenharmony_ci const char * const *text_values; 2862306a36Sopenharmony_ci int text_values_len; 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define _POWER_SUPPLY_ATTR(_name, _text, _len) \ 3262306a36Sopenharmony_ci[POWER_SUPPLY_PROP_ ## _name] = \ 3362306a36Sopenharmony_ci{ \ 3462306a36Sopenharmony_ci .prop_name = #_name, \ 3562306a36Sopenharmony_ci .attr_name = #_name "\0", \ 3662306a36Sopenharmony_ci .text_values = _text, \ 3762306a36Sopenharmony_ci .text_values_len = _len, \ 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define POWER_SUPPLY_ATTR(_name) _POWER_SUPPLY_ATTR(_name, NULL, 0) 4162306a36Sopenharmony_ci#define _POWER_SUPPLY_ENUM_ATTR(_name, _text) \ 4262306a36Sopenharmony_ci _POWER_SUPPLY_ATTR(_name, _text, ARRAY_SIZE(_text)) 4362306a36Sopenharmony_ci#define POWER_SUPPLY_ENUM_ATTR(_name) \ 4462306a36Sopenharmony_ci _POWER_SUPPLY_ENUM_ATTR(_name, POWER_SUPPLY_ ## _name ## _TEXT) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic const char * const POWER_SUPPLY_TYPE_TEXT[] = { 4762306a36Sopenharmony_ci [POWER_SUPPLY_TYPE_UNKNOWN] = "Unknown", 4862306a36Sopenharmony_ci [POWER_SUPPLY_TYPE_BATTERY] = "Battery", 4962306a36Sopenharmony_ci [POWER_SUPPLY_TYPE_UPS] = "UPS", 5062306a36Sopenharmony_ci [POWER_SUPPLY_TYPE_MAINS] = "Mains", 5162306a36Sopenharmony_ci [POWER_SUPPLY_TYPE_USB] = "USB", 5262306a36Sopenharmony_ci [POWER_SUPPLY_TYPE_USB_DCP] = "USB_DCP", 5362306a36Sopenharmony_ci [POWER_SUPPLY_TYPE_USB_CDP] = "USB_CDP", 5462306a36Sopenharmony_ci [POWER_SUPPLY_TYPE_USB_ACA] = "USB_ACA", 5562306a36Sopenharmony_ci [POWER_SUPPLY_TYPE_USB_TYPE_C] = "USB_C", 5662306a36Sopenharmony_ci [POWER_SUPPLY_TYPE_USB_PD] = "USB_PD", 5762306a36Sopenharmony_ci [POWER_SUPPLY_TYPE_USB_PD_DRP] = "USB_PD_DRP", 5862306a36Sopenharmony_ci [POWER_SUPPLY_TYPE_APPLE_BRICK_ID] = "BrickID", 5962306a36Sopenharmony_ci [POWER_SUPPLY_TYPE_WIRELESS] = "Wireless", 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic const char * const POWER_SUPPLY_USB_TYPE_TEXT[] = { 6362306a36Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_UNKNOWN] = "Unknown", 6462306a36Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_SDP] = "SDP", 6562306a36Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_DCP] = "DCP", 6662306a36Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_CDP] = "CDP", 6762306a36Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_ACA] = "ACA", 6862306a36Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_C] = "C", 6962306a36Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_PD] = "PD", 7062306a36Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_PD_DRP] = "PD_DRP", 7162306a36Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_PD_PPS] = "PD_PPS", 7262306a36Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID] = "BrickID", 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic const char * const POWER_SUPPLY_STATUS_TEXT[] = { 7662306a36Sopenharmony_ci [POWER_SUPPLY_STATUS_UNKNOWN] = "Unknown", 7762306a36Sopenharmony_ci [POWER_SUPPLY_STATUS_CHARGING] = "Charging", 7862306a36Sopenharmony_ci [POWER_SUPPLY_STATUS_DISCHARGING] = "Discharging", 7962306a36Sopenharmony_ci [POWER_SUPPLY_STATUS_NOT_CHARGING] = "Not charging", 8062306a36Sopenharmony_ci [POWER_SUPPLY_STATUS_FULL] = "Full", 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic const char * const POWER_SUPPLY_CHARGE_TYPE_TEXT[] = { 8462306a36Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_UNKNOWN] = "Unknown", 8562306a36Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_NONE] = "N/A", 8662306a36Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_TRICKLE] = "Trickle", 8762306a36Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_FAST] = "Fast", 8862306a36Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_STANDARD] = "Standard", 8962306a36Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE] = "Adaptive", 9062306a36Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_CUSTOM] = "Custom", 9162306a36Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_LONGLIFE] = "Long Life", 9262306a36Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_BYPASS] = "Bypass", 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic const char * const POWER_SUPPLY_HEALTH_TEXT[] = { 9662306a36Sopenharmony_ci [POWER_SUPPLY_HEALTH_UNKNOWN] = "Unknown", 9762306a36Sopenharmony_ci [POWER_SUPPLY_HEALTH_GOOD] = "Good", 9862306a36Sopenharmony_ci [POWER_SUPPLY_HEALTH_OVERHEAT] = "Overheat", 9962306a36Sopenharmony_ci [POWER_SUPPLY_HEALTH_DEAD] = "Dead", 10062306a36Sopenharmony_ci [POWER_SUPPLY_HEALTH_OVERVOLTAGE] = "Over voltage", 10162306a36Sopenharmony_ci [POWER_SUPPLY_HEALTH_UNSPEC_FAILURE] = "Unspecified failure", 10262306a36Sopenharmony_ci [POWER_SUPPLY_HEALTH_COLD] = "Cold", 10362306a36Sopenharmony_ci [POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE] = "Watchdog timer expire", 10462306a36Sopenharmony_ci [POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE] = "Safety timer expire", 10562306a36Sopenharmony_ci [POWER_SUPPLY_HEALTH_OVERCURRENT] = "Over current", 10662306a36Sopenharmony_ci [POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED] = "Calibration required", 10762306a36Sopenharmony_ci [POWER_SUPPLY_HEALTH_WARM] = "Warm", 10862306a36Sopenharmony_ci [POWER_SUPPLY_HEALTH_COOL] = "Cool", 10962306a36Sopenharmony_ci [POWER_SUPPLY_HEALTH_HOT] = "Hot", 11062306a36Sopenharmony_ci [POWER_SUPPLY_HEALTH_NO_BATTERY] = "No battery", 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic const char * const POWER_SUPPLY_TECHNOLOGY_TEXT[] = { 11462306a36Sopenharmony_ci [POWER_SUPPLY_TECHNOLOGY_UNKNOWN] = "Unknown", 11562306a36Sopenharmony_ci [POWER_SUPPLY_TECHNOLOGY_NiMH] = "NiMH", 11662306a36Sopenharmony_ci [POWER_SUPPLY_TECHNOLOGY_LION] = "Li-ion", 11762306a36Sopenharmony_ci [POWER_SUPPLY_TECHNOLOGY_LIPO] = "Li-poly", 11862306a36Sopenharmony_ci [POWER_SUPPLY_TECHNOLOGY_LiFe] = "LiFe", 11962306a36Sopenharmony_ci [POWER_SUPPLY_TECHNOLOGY_NiCd] = "NiCd", 12062306a36Sopenharmony_ci [POWER_SUPPLY_TECHNOLOGY_LiMn] = "LiMn", 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic const char * const POWER_SUPPLY_CAPACITY_LEVEL_TEXT[] = { 12462306a36Sopenharmony_ci [POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN] = "Unknown", 12562306a36Sopenharmony_ci [POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL] = "Critical", 12662306a36Sopenharmony_ci [POWER_SUPPLY_CAPACITY_LEVEL_LOW] = "Low", 12762306a36Sopenharmony_ci [POWER_SUPPLY_CAPACITY_LEVEL_NORMAL] = "Normal", 12862306a36Sopenharmony_ci [POWER_SUPPLY_CAPACITY_LEVEL_HIGH] = "High", 12962306a36Sopenharmony_ci [POWER_SUPPLY_CAPACITY_LEVEL_FULL] = "Full", 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic const char * const POWER_SUPPLY_SCOPE_TEXT[] = { 13362306a36Sopenharmony_ci [POWER_SUPPLY_SCOPE_UNKNOWN] = "Unknown", 13462306a36Sopenharmony_ci [POWER_SUPPLY_SCOPE_SYSTEM] = "System", 13562306a36Sopenharmony_ci [POWER_SUPPLY_SCOPE_DEVICE] = "Device", 13662306a36Sopenharmony_ci}; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic const char * const POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT[] = { 13962306a36Sopenharmony_ci [POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO] = "auto", 14062306a36Sopenharmony_ci [POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE] = "inhibit-charge", 14162306a36Sopenharmony_ci [POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE] = "force-discharge", 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic struct power_supply_attr power_supply_attrs[] = { 14562306a36Sopenharmony_ci /* Properties of type `int' */ 14662306a36Sopenharmony_ci POWER_SUPPLY_ENUM_ATTR(STATUS), 14762306a36Sopenharmony_ci POWER_SUPPLY_ENUM_ATTR(CHARGE_TYPE), 14862306a36Sopenharmony_ci POWER_SUPPLY_ENUM_ATTR(HEALTH), 14962306a36Sopenharmony_ci POWER_SUPPLY_ATTR(PRESENT), 15062306a36Sopenharmony_ci POWER_SUPPLY_ATTR(ONLINE), 15162306a36Sopenharmony_ci POWER_SUPPLY_ATTR(AUTHENTIC), 15262306a36Sopenharmony_ci POWER_SUPPLY_ENUM_ATTR(TECHNOLOGY), 15362306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CYCLE_COUNT), 15462306a36Sopenharmony_ci POWER_SUPPLY_ATTR(VOLTAGE_MAX), 15562306a36Sopenharmony_ci POWER_SUPPLY_ATTR(VOLTAGE_MIN), 15662306a36Sopenharmony_ci POWER_SUPPLY_ATTR(VOLTAGE_MAX_DESIGN), 15762306a36Sopenharmony_ci POWER_SUPPLY_ATTR(VOLTAGE_MIN_DESIGN), 15862306a36Sopenharmony_ci POWER_SUPPLY_ATTR(VOLTAGE_NOW), 15962306a36Sopenharmony_ci POWER_SUPPLY_ATTR(VOLTAGE_AVG), 16062306a36Sopenharmony_ci POWER_SUPPLY_ATTR(VOLTAGE_OCV), 16162306a36Sopenharmony_ci POWER_SUPPLY_ATTR(VOLTAGE_BOOT), 16262306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CURRENT_MAX), 16362306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CURRENT_NOW), 16462306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CURRENT_AVG), 16562306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CURRENT_BOOT), 16662306a36Sopenharmony_ci POWER_SUPPLY_ATTR(POWER_NOW), 16762306a36Sopenharmony_ci POWER_SUPPLY_ATTR(POWER_AVG), 16862306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_FULL_DESIGN), 16962306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_EMPTY_DESIGN), 17062306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_FULL), 17162306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_EMPTY), 17262306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_NOW), 17362306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_AVG), 17462306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_COUNTER), 17562306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CONSTANT_CHARGE_CURRENT), 17662306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CONSTANT_CHARGE_CURRENT_MAX), 17762306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CONSTANT_CHARGE_VOLTAGE), 17862306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CONSTANT_CHARGE_VOLTAGE_MAX), 17962306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_CONTROL_LIMIT), 18062306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_CONTROL_LIMIT_MAX), 18162306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_CONTROL_START_THRESHOLD), 18262306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_CONTROL_END_THRESHOLD), 18362306a36Sopenharmony_ci POWER_SUPPLY_ENUM_ATTR(CHARGE_BEHAVIOUR), 18462306a36Sopenharmony_ci POWER_SUPPLY_ATTR(INPUT_CURRENT_LIMIT), 18562306a36Sopenharmony_ci POWER_SUPPLY_ATTR(INPUT_VOLTAGE_LIMIT), 18662306a36Sopenharmony_ci POWER_SUPPLY_ATTR(INPUT_POWER_LIMIT), 18762306a36Sopenharmony_ci POWER_SUPPLY_ATTR(ENERGY_FULL_DESIGN), 18862306a36Sopenharmony_ci POWER_SUPPLY_ATTR(ENERGY_EMPTY_DESIGN), 18962306a36Sopenharmony_ci POWER_SUPPLY_ATTR(ENERGY_FULL), 19062306a36Sopenharmony_ci POWER_SUPPLY_ATTR(ENERGY_EMPTY), 19162306a36Sopenharmony_ci POWER_SUPPLY_ATTR(ENERGY_NOW), 19262306a36Sopenharmony_ci POWER_SUPPLY_ATTR(ENERGY_AVG), 19362306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CAPACITY), 19462306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CAPACITY_ALERT_MIN), 19562306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CAPACITY_ALERT_MAX), 19662306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CAPACITY_ERROR_MARGIN), 19762306a36Sopenharmony_ci POWER_SUPPLY_ENUM_ATTR(CAPACITY_LEVEL), 19862306a36Sopenharmony_ci POWER_SUPPLY_ATTR(TEMP), 19962306a36Sopenharmony_ci POWER_SUPPLY_ATTR(TEMP_MAX), 20062306a36Sopenharmony_ci POWER_SUPPLY_ATTR(TEMP_MIN), 20162306a36Sopenharmony_ci POWER_SUPPLY_ATTR(TEMP_ALERT_MIN), 20262306a36Sopenharmony_ci POWER_SUPPLY_ATTR(TEMP_ALERT_MAX), 20362306a36Sopenharmony_ci POWER_SUPPLY_ATTR(TEMP_AMBIENT), 20462306a36Sopenharmony_ci POWER_SUPPLY_ATTR(TEMP_AMBIENT_ALERT_MIN), 20562306a36Sopenharmony_ci POWER_SUPPLY_ATTR(TEMP_AMBIENT_ALERT_MAX), 20662306a36Sopenharmony_ci POWER_SUPPLY_ATTR(TIME_TO_EMPTY_NOW), 20762306a36Sopenharmony_ci POWER_SUPPLY_ATTR(TIME_TO_EMPTY_AVG), 20862306a36Sopenharmony_ci POWER_SUPPLY_ATTR(TIME_TO_FULL_NOW), 20962306a36Sopenharmony_ci POWER_SUPPLY_ATTR(TIME_TO_FULL_AVG), 21062306a36Sopenharmony_ci POWER_SUPPLY_ENUM_ATTR(TYPE), 21162306a36Sopenharmony_ci POWER_SUPPLY_ATTR(USB_TYPE), 21262306a36Sopenharmony_ci POWER_SUPPLY_ENUM_ATTR(SCOPE), 21362306a36Sopenharmony_ci POWER_SUPPLY_ATTR(PRECHARGE_CURRENT), 21462306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_TERM_CURRENT), 21562306a36Sopenharmony_ci POWER_SUPPLY_ATTR(CALIBRATE), 21662306a36Sopenharmony_ci POWER_SUPPLY_ATTR(MANUFACTURE_YEAR), 21762306a36Sopenharmony_ci POWER_SUPPLY_ATTR(MANUFACTURE_MONTH), 21862306a36Sopenharmony_ci POWER_SUPPLY_ATTR(MANUFACTURE_DAY), 21962306a36Sopenharmony_ci /* Properties of type `const char *' */ 22062306a36Sopenharmony_ci POWER_SUPPLY_ATTR(MODEL_NAME), 22162306a36Sopenharmony_ci POWER_SUPPLY_ATTR(MANUFACTURER), 22262306a36Sopenharmony_ci POWER_SUPPLY_ATTR(SERIAL_NUMBER), 22362306a36Sopenharmony_ci}; 22462306a36Sopenharmony_ci#define POWER_SUPPLY_ATTR_CNT ARRAY_SIZE(power_supply_attrs) 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic struct attribute * 22762306a36Sopenharmony_ci__power_supply_attrs[POWER_SUPPLY_ATTR_CNT + 1]; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic struct power_supply_attr *to_ps_attr(struct device_attribute *attr) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci return container_of(attr, struct power_supply_attr, dev_attr); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic enum power_supply_property dev_attr_psp(struct device_attribute *attr) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci return to_ps_attr(attr) - power_supply_attrs; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic ssize_t power_supply_show_usb_type(struct device *dev, 24062306a36Sopenharmony_ci const struct power_supply_desc *desc, 24162306a36Sopenharmony_ci union power_supply_propval *value, 24262306a36Sopenharmony_ci char *buf) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci enum power_supply_usb_type usb_type; 24562306a36Sopenharmony_ci ssize_t count = 0; 24662306a36Sopenharmony_ci bool match = false; 24762306a36Sopenharmony_ci int i; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci for (i = 0; i < desc->num_usb_types; ++i) { 25062306a36Sopenharmony_ci usb_type = desc->usb_types[i]; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (value->intval == usb_type) { 25362306a36Sopenharmony_ci count += sysfs_emit_at(buf, count, "[%s] ", 25462306a36Sopenharmony_ci POWER_SUPPLY_USB_TYPE_TEXT[usb_type]); 25562306a36Sopenharmony_ci match = true; 25662306a36Sopenharmony_ci } else { 25762306a36Sopenharmony_ci count += sysfs_emit_at(buf, count, "%s ", 25862306a36Sopenharmony_ci POWER_SUPPLY_USB_TYPE_TEXT[usb_type]); 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (!match) { 26362306a36Sopenharmony_ci dev_warn(dev, "driver reporting unsupported connected type\n"); 26462306a36Sopenharmony_ci return -EINVAL; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (count) 26862306a36Sopenharmony_ci buf[count - 1] = '\n'; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return count; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic ssize_t power_supply_show_property(struct device *dev, 27462306a36Sopenharmony_ci struct device_attribute *attr, 27562306a36Sopenharmony_ci char *buf) { 27662306a36Sopenharmony_ci ssize_t ret; 27762306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 27862306a36Sopenharmony_ci struct power_supply_attr *ps_attr = to_ps_attr(attr); 27962306a36Sopenharmony_ci enum power_supply_property psp = dev_attr_psp(attr); 28062306a36Sopenharmony_ci union power_supply_propval value; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (psp == POWER_SUPPLY_PROP_TYPE) { 28362306a36Sopenharmony_ci value.intval = psy->desc->type; 28462306a36Sopenharmony_ci } else { 28562306a36Sopenharmony_ci ret = power_supply_get_property(psy, psp, &value); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (ret < 0) { 28862306a36Sopenharmony_ci if (ret == -ENODATA) 28962306a36Sopenharmony_ci dev_dbg_ratelimited(dev, 29062306a36Sopenharmony_ci "driver has no data for `%s' property\n", 29162306a36Sopenharmony_ci attr->attr.name); 29262306a36Sopenharmony_ci else if (ret != -ENODEV && ret != -EAGAIN) 29362306a36Sopenharmony_ci dev_err_ratelimited(dev, 29462306a36Sopenharmony_ci "driver failed to report `%s' property: %zd\n", 29562306a36Sopenharmony_ci attr->attr.name, ret); 29662306a36Sopenharmony_ci return ret; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (ps_attr->text_values_len > 0 && 30162306a36Sopenharmony_ci value.intval < ps_attr->text_values_len && value.intval >= 0) { 30262306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", ps_attr->text_values[value.intval]); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci switch (psp) { 30662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_USB_TYPE: 30762306a36Sopenharmony_ci ret = power_supply_show_usb_type(dev, psy->desc, 30862306a36Sopenharmony_ci &value, buf); 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER: 31162306a36Sopenharmony_ci ret = sysfs_emit(buf, "%s\n", value.strval); 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci default: 31462306a36Sopenharmony_ci ret = sysfs_emit(buf, "%d\n", value.intval); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return ret; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic ssize_t power_supply_store_property(struct device *dev, 32162306a36Sopenharmony_ci struct device_attribute *attr, 32262306a36Sopenharmony_ci const char *buf, size_t count) { 32362306a36Sopenharmony_ci ssize_t ret; 32462306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 32562306a36Sopenharmony_ci struct power_supply_attr *ps_attr = to_ps_attr(attr); 32662306a36Sopenharmony_ci enum power_supply_property psp = dev_attr_psp(attr); 32762306a36Sopenharmony_ci union power_supply_propval value; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci ret = -EINVAL; 33062306a36Sopenharmony_ci if (ps_attr->text_values_len > 0) { 33162306a36Sopenharmony_ci ret = __sysfs_match_string(ps_attr->text_values, 33262306a36Sopenharmony_ci ps_attr->text_values_len, buf); 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* 33662306a36Sopenharmony_ci * If no match was found, then check to see if it is an integer. 33762306a36Sopenharmony_ci * Integer values are valid for enums in addition to the text value. 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_ci if (ret < 0) { 34062306a36Sopenharmony_ci long long_val; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci ret = kstrtol(buf, 10, &long_val); 34362306a36Sopenharmony_ci if (ret < 0) 34462306a36Sopenharmony_ci return ret; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci ret = long_val; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci value.intval = ret; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci ret = power_supply_set_property(psy, psp, &value); 35262306a36Sopenharmony_ci if (ret < 0) 35362306a36Sopenharmony_ci return ret; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return count; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic umode_t power_supply_attr_is_visible(struct kobject *kobj, 35962306a36Sopenharmony_ci struct attribute *attr, 36062306a36Sopenharmony_ci int attrno) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 36362306a36Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 36462306a36Sopenharmony_ci umode_t mode = S_IRUSR | S_IRGRP | S_IROTH; 36562306a36Sopenharmony_ci int i; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (!power_supply_attrs[attrno].prop_name) 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (attrno == POWER_SUPPLY_PROP_TYPE) 37162306a36Sopenharmony_ci return mode; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci for (i = 0; i < psy->desc->num_properties; i++) { 37462306a36Sopenharmony_ci int property = psy->desc->properties[i]; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (property == attrno) { 37762306a36Sopenharmony_ci if (psy->desc->property_is_writeable && 37862306a36Sopenharmony_ci psy->desc->property_is_writeable(psy, property) > 0) 37962306a36Sopenharmony_ci mode |= S_IWUSR; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return mode; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (power_supply_battery_info_has_prop(psy->battery_info, attrno)) 38662306a36Sopenharmony_ci return mode; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return 0; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic const struct attribute_group power_supply_attr_group = { 39262306a36Sopenharmony_ci .attrs = __power_supply_attrs, 39362306a36Sopenharmony_ci .is_visible = power_supply_attr_is_visible, 39462306a36Sopenharmony_ci}; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic const struct attribute_group *power_supply_attr_groups[] = { 39762306a36Sopenharmony_ci &power_supply_attr_group, 39862306a36Sopenharmony_ci NULL, 39962306a36Sopenharmony_ci}; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic void str_to_lower(char *str) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci while (*str) { 40462306a36Sopenharmony_ci *str = tolower(*str); 40562306a36Sopenharmony_ci str++; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_civoid power_supply_init_attrs(struct device_type *dev_type) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci int i; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci dev_type->groups = power_supply_attr_groups; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++) { 41662306a36Sopenharmony_ci struct device_attribute *attr; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (!power_supply_attrs[i].prop_name) { 41962306a36Sopenharmony_ci pr_warn("%s: Property %d skipped because it is missing from power_supply_attrs\n", 42062306a36Sopenharmony_ci __func__, i); 42162306a36Sopenharmony_ci sprintf(power_supply_attrs[i].attr_name, "_err_%d", i); 42262306a36Sopenharmony_ci } else { 42362306a36Sopenharmony_ci str_to_lower(power_supply_attrs[i].attr_name); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci attr = &power_supply_attrs[i].dev_attr; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci attr->attr.name = power_supply_attrs[i].attr_name; 42962306a36Sopenharmony_ci attr->show = power_supply_show_property; 43062306a36Sopenharmony_ci attr->store = power_supply_store_property; 43162306a36Sopenharmony_ci __power_supply_attrs[i] = &attr->attr; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int add_prop_uevent(const struct device *dev, struct kobj_uevent_env *env, 43662306a36Sopenharmony_ci enum power_supply_property prop, char *prop_buf) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci int ret = 0; 43962306a36Sopenharmony_ci struct power_supply_attr *pwr_attr; 44062306a36Sopenharmony_ci struct device_attribute *dev_attr; 44162306a36Sopenharmony_ci char *line; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci pwr_attr = &power_supply_attrs[prop]; 44462306a36Sopenharmony_ci dev_attr = &pwr_attr->dev_attr; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci ret = power_supply_show_property((struct device *)dev, dev_attr, prop_buf); 44762306a36Sopenharmony_ci if (ret == -ENODEV || ret == -ENODATA) { 44862306a36Sopenharmony_ci /* 44962306a36Sopenharmony_ci * When a battery is absent, we expect -ENODEV. Don't abort; 45062306a36Sopenharmony_ci * send the uevent with at least the PRESENT=0 property 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (ret < 0) 45662306a36Sopenharmony_ci return ret; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci line = strchr(prop_buf, '\n'); 45962306a36Sopenharmony_ci if (line) 46062306a36Sopenharmony_ci *line = 0; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return add_uevent_var(env, "POWER_SUPPLY_%s=%s", 46362306a36Sopenharmony_ci pwr_attr->prop_name, prop_buf); 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ciint power_supply_uevent(const struct device *dev, struct kobj_uevent_env *env) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci const struct power_supply *psy = dev_get_drvdata(dev); 46962306a36Sopenharmony_ci const enum power_supply_property *battery_props = 47062306a36Sopenharmony_ci power_supply_battery_info_properties; 47162306a36Sopenharmony_ci unsigned long psy_drv_properties[POWER_SUPPLY_ATTR_CNT / 47262306a36Sopenharmony_ci sizeof(unsigned long) + 1] = {0}; 47362306a36Sopenharmony_ci int ret = 0, j; 47462306a36Sopenharmony_ci char *prop_buf; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (!psy || !psy->desc) { 47762306a36Sopenharmony_ci dev_dbg(dev, "No power supply yet\n"); 47862306a36Sopenharmony_ci return ret; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->desc->name); 48262306a36Sopenharmony_ci if (ret) 48362306a36Sopenharmony_ci return ret; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* 48662306a36Sopenharmony_ci * Kernel generates KOBJ_REMOVE uevent in device removal path, after 48762306a36Sopenharmony_ci * resources have been freed. Exit early to avoid use-after-free. 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ci if (psy->removing) 49062306a36Sopenharmony_ci return 0; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci prop_buf = (char *)get_zeroed_page(GFP_KERNEL); 49362306a36Sopenharmony_ci if (!prop_buf) 49462306a36Sopenharmony_ci return -ENOMEM; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ret = add_prop_uevent(dev, env, POWER_SUPPLY_PROP_TYPE, prop_buf); 49762306a36Sopenharmony_ci if (ret) 49862306a36Sopenharmony_ci goto out; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci for (j = 0; j < psy->desc->num_properties; j++) { 50162306a36Sopenharmony_ci set_bit(psy->desc->properties[j], psy_drv_properties); 50262306a36Sopenharmony_ci ret = add_prop_uevent(dev, env, psy->desc->properties[j], 50362306a36Sopenharmony_ci prop_buf); 50462306a36Sopenharmony_ci if (ret) 50562306a36Sopenharmony_ci goto out; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci for (j = 0; j < power_supply_battery_info_properties_size; j++) { 50962306a36Sopenharmony_ci if (test_bit(battery_props[j], psy_drv_properties)) 51062306a36Sopenharmony_ci continue; 51162306a36Sopenharmony_ci if (!power_supply_battery_info_has_prop(psy->battery_info, 51262306a36Sopenharmony_ci battery_props[j])) 51362306a36Sopenharmony_ci continue; 51462306a36Sopenharmony_ci ret = add_prop_uevent(dev, env, battery_props[j], 51562306a36Sopenharmony_ci prop_buf); 51662306a36Sopenharmony_ci if (ret) 51762306a36Sopenharmony_ci goto out; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ciout: 52162306a36Sopenharmony_ci free_page((unsigned long)prop_buf); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci return ret; 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cissize_t power_supply_charge_behaviour_show(struct device *dev, 52762306a36Sopenharmony_ci unsigned int available_behaviours, 52862306a36Sopenharmony_ci enum power_supply_charge_behaviour current_behaviour, 52962306a36Sopenharmony_ci char *buf) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci bool match = false, available, active; 53262306a36Sopenharmony_ci ssize_t count = 0; 53362306a36Sopenharmony_ci int i; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT); i++) { 53662306a36Sopenharmony_ci available = available_behaviours & BIT(i); 53762306a36Sopenharmony_ci active = i == current_behaviour; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (available && active) { 54062306a36Sopenharmony_ci count += sysfs_emit_at(buf, count, "[%s] ", 54162306a36Sopenharmony_ci POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT[i]); 54262306a36Sopenharmony_ci match = true; 54362306a36Sopenharmony_ci } else if (available) { 54462306a36Sopenharmony_ci count += sysfs_emit_at(buf, count, "%s ", 54562306a36Sopenharmony_ci POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT[i]); 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (!match) { 55062306a36Sopenharmony_ci dev_warn(dev, "driver reporting unsupported charge behaviour\n"); 55162306a36Sopenharmony_ci return -EINVAL; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (count) 55562306a36Sopenharmony_ci buf[count - 1] = '\n'; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci return count; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(power_supply_charge_behaviour_show); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ciint power_supply_charge_behaviour_parse(unsigned int available_behaviours, const char *buf) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci int i = sysfs_match_string(POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT, buf); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (i < 0) 56662306a36Sopenharmony_ci return i; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (available_behaviours & BIT(i)) 56962306a36Sopenharmony_ci return i; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return -EINVAL; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(power_supply_charge_behaviour_parse); 574