18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Sysfs interface for the universal power supply monitor class 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright © 2007 David Woodhouse <dwmw2@infradead.org> 68c2ecf20Sopenharmony_ci * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> 78c2ecf20Sopenharmony_ci * Copyright © 2004 Szabolcs Gyurko 88c2ecf20Sopenharmony_ci * Copyright © 2003 Ian Molton <spyro@f2s.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Modified: 2004, Oct Szabolcs Gyurko 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/ctype.h> 148c2ecf20Sopenharmony_ci#include <linux/device.h> 158c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/stat.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "power_supply.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define MAX_PROP_NAME_LEN 30 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct power_supply_attr { 248c2ecf20Sopenharmony_ci const char *prop_name; 258c2ecf20Sopenharmony_ci char attr_name[MAX_PROP_NAME_LEN + 1]; 268c2ecf20Sopenharmony_ci struct device_attribute dev_attr; 278c2ecf20Sopenharmony_ci const char * const *text_values; 288c2ecf20Sopenharmony_ci int text_values_len; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define _POWER_SUPPLY_ATTR(_name, _text, _len) \ 328c2ecf20Sopenharmony_ci[POWER_SUPPLY_PROP_ ## _name] = \ 338c2ecf20Sopenharmony_ci{ \ 348c2ecf20Sopenharmony_ci .prop_name = #_name, \ 358c2ecf20Sopenharmony_ci .attr_name = #_name "\0", \ 368c2ecf20Sopenharmony_ci .text_values = _text, \ 378c2ecf20Sopenharmony_ci .text_values_len = _len, \ 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define POWER_SUPPLY_ATTR(_name) _POWER_SUPPLY_ATTR(_name, NULL, 0) 418c2ecf20Sopenharmony_ci#define _POWER_SUPPLY_ENUM_ATTR(_name, _text) \ 428c2ecf20Sopenharmony_ci _POWER_SUPPLY_ATTR(_name, _text, ARRAY_SIZE(_text)) 438c2ecf20Sopenharmony_ci#define POWER_SUPPLY_ENUM_ATTR(_name) \ 448c2ecf20Sopenharmony_ci _POWER_SUPPLY_ENUM_ATTR(_name, POWER_SUPPLY_ ## _name ## _TEXT) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic const char * const POWER_SUPPLY_TYPE_TEXT[] = { 478c2ecf20Sopenharmony_ci [POWER_SUPPLY_TYPE_UNKNOWN] = "Unknown", 488c2ecf20Sopenharmony_ci [POWER_SUPPLY_TYPE_BATTERY] = "Battery", 498c2ecf20Sopenharmony_ci [POWER_SUPPLY_TYPE_UPS] = "UPS", 508c2ecf20Sopenharmony_ci [POWER_SUPPLY_TYPE_MAINS] = "Mains", 518c2ecf20Sopenharmony_ci [POWER_SUPPLY_TYPE_USB] = "USB", 528c2ecf20Sopenharmony_ci [POWER_SUPPLY_TYPE_USB_DCP] = "USB_DCP", 538c2ecf20Sopenharmony_ci [POWER_SUPPLY_TYPE_USB_CDP] = "USB_CDP", 548c2ecf20Sopenharmony_ci [POWER_SUPPLY_TYPE_USB_ACA] = "USB_ACA", 558c2ecf20Sopenharmony_ci [POWER_SUPPLY_TYPE_USB_TYPE_C] = "USB_C", 568c2ecf20Sopenharmony_ci [POWER_SUPPLY_TYPE_USB_PD] = "USB_PD", 578c2ecf20Sopenharmony_ci [POWER_SUPPLY_TYPE_USB_PD_DRP] = "USB_PD_DRP", 588c2ecf20Sopenharmony_ci [POWER_SUPPLY_TYPE_APPLE_BRICK_ID] = "BrickID", 598c2ecf20Sopenharmony_ci [POWER_SUPPLY_TYPE_WIRELESS] = "Wireless", 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic const char * const POWER_SUPPLY_USB_TYPE_TEXT[] = { 638c2ecf20Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_UNKNOWN] = "Unknown", 648c2ecf20Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_SDP] = "SDP", 658c2ecf20Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_DCP] = "DCP", 668c2ecf20Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_CDP] = "CDP", 678c2ecf20Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_ACA] = "ACA", 688c2ecf20Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_C] = "C", 698c2ecf20Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_PD] = "PD", 708c2ecf20Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_PD_DRP] = "PD_DRP", 718c2ecf20Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_PD_PPS] = "PD_PPS", 728c2ecf20Sopenharmony_ci [POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID] = "BrickID", 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic const char * const POWER_SUPPLY_STATUS_TEXT[] = { 768c2ecf20Sopenharmony_ci [POWER_SUPPLY_STATUS_UNKNOWN] = "Unknown", 778c2ecf20Sopenharmony_ci [POWER_SUPPLY_STATUS_CHARGING] = "Charging", 788c2ecf20Sopenharmony_ci [POWER_SUPPLY_STATUS_DISCHARGING] = "Discharging", 798c2ecf20Sopenharmony_ci [POWER_SUPPLY_STATUS_NOT_CHARGING] = "Not charging", 808c2ecf20Sopenharmony_ci [POWER_SUPPLY_STATUS_FULL] = "Full", 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic const char * const POWER_SUPPLY_CHARGE_TYPE_TEXT[] = { 848c2ecf20Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_UNKNOWN] = "Unknown", 858c2ecf20Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_NONE] = "N/A", 868c2ecf20Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_TRICKLE] = "Trickle", 878c2ecf20Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_FAST] = "Fast", 888c2ecf20Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_STANDARD] = "Standard", 898c2ecf20Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE] = "Adaptive", 908c2ecf20Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_CUSTOM] = "Custom", 918c2ecf20Sopenharmony_ci [POWER_SUPPLY_CHARGE_TYPE_LONGLIFE] = "Long Life", 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic const char * const POWER_SUPPLY_HEALTH_TEXT[] = { 958c2ecf20Sopenharmony_ci [POWER_SUPPLY_HEALTH_UNKNOWN] = "Unknown", 968c2ecf20Sopenharmony_ci [POWER_SUPPLY_HEALTH_GOOD] = "Good", 978c2ecf20Sopenharmony_ci [POWER_SUPPLY_HEALTH_OVERHEAT] = "Overheat", 988c2ecf20Sopenharmony_ci [POWER_SUPPLY_HEALTH_DEAD] = "Dead", 998c2ecf20Sopenharmony_ci [POWER_SUPPLY_HEALTH_OVERVOLTAGE] = "Over voltage", 1008c2ecf20Sopenharmony_ci [POWER_SUPPLY_HEALTH_UNSPEC_FAILURE] = "Unspecified failure", 1018c2ecf20Sopenharmony_ci [POWER_SUPPLY_HEALTH_COLD] = "Cold", 1028c2ecf20Sopenharmony_ci [POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE] = "Watchdog timer expire", 1038c2ecf20Sopenharmony_ci [POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE] = "Safety timer expire", 1048c2ecf20Sopenharmony_ci [POWER_SUPPLY_HEALTH_OVERCURRENT] = "Over current", 1058c2ecf20Sopenharmony_ci [POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED] = "Calibration required", 1068c2ecf20Sopenharmony_ci [POWER_SUPPLY_HEALTH_WARM] = "Warm", 1078c2ecf20Sopenharmony_ci [POWER_SUPPLY_HEALTH_COOL] = "Cool", 1088c2ecf20Sopenharmony_ci [POWER_SUPPLY_HEALTH_HOT] = "Hot", 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic const char * const POWER_SUPPLY_TECHNOLOGY_TEXT[] = { 1128c2ecf20Sopenharmony_ci [POWER_SUPPLY_TECHNOLOGY_UNKNOWN] = "Unknown", 1138c2ecf20Sopenharmony_ci [POWER_SUPPLY_TECHNOLOGY_NiMH] = "NiMH", 1148c2ecf20Sopenharmony_ci [POWER_SUPPLY_TECHNOLOGY_LION] = "Li-ion", 1158c2ecf20Sopenharmony_ci [POWER_SUPPLY_TECHNOLOGY_LIPO] = "Li-poly", 1168c2ecf20Sopenharmony_ci [POWER_SUPPLY_TECHNOLOGY_LiFe] = "LiFe", 1178c2ecf20Sopenharmony_ci [POWER_SUPPLY_TECHNOLOGY_NiCd] = "NiCd", 1188c2ecf20Sopenharmony_ci [POWER_SUPPLY_TECHNOLOGY_LiMn] = "LiMn", 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic const char * const POWER_SUPPLY_CAPACITY_LEVEL_TEXT[] = { 1228c2ecf20Sopenharmony_ci [POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN] = "Unknown", 1238c2ecf20Sopenharmony_ci [POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL] = "Critical", 1248c2ecf20Sopenharmony_ci [POWER_SUPPLY_CAPACITY_LEVEL_LOW] = "Low", 1258c2ecf20Sopenharmony_ci [POWER_SUPPLY_CAPACITY_LEVEL_NORMAL] = "Normal", 1268c2ecf20Sopenharmony_ci [POWER_SUPPLY_CAPACITY_LEVEL_HIGH] = "High", 1278c2ecf20Sopenharmony_ci [POWER_SUPPLY_CAPACITY_LEVEL_FULL] = "Full", 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic const char * const POWER_SUPPLY_SCOPE_TEXT[] = { 1318c2ecf20Sopenharmony_ci [POWER_SUPPLY_SCOPE_UNKNOWN] = "Unknown", 1328c2ecf20Sopenharmony_ci [POWER_SUPPLY_SCOPE_SYSTEM] = "System", 1338c2ecf20Sopenharmony_ci [POWER_SUPPLY_SCOPE_DEVICE] = "Device", 1348c2ecf20Sopenharmony_ci}; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic struct power_supply_attr power_supply_attrs[] = { 1378c2ecf20Sopenharmony_ci /* Properties of type `int' */ 1388c2ecf20Sopenharmony_ci POWER_SUPPLY_ENUM_ATTR(STATUS), 1398c2ecf20Sopenharmony_ci POWER_SUPPLY_ENUM_ATTR(CHARGE_TYPE), 1408c2ecf20Sopenharmony_ci POWER_SUPPLY_ENUM_ATTR(HEALTH), 1418c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(PRESENT), 1428c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(ONLINE), 1438c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(AUTHENTIC), 1448c2ecf20Sopenharmony_ci POWER_SUPPLY_ENUM_ATTR(TECHNOLOGY), 1458c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CYCLE_COUNT), 1468c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(VOLTAGE_MAX), 1478c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(VOLTAGE_MIN), 1488c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(VOLTAGE_MAX_DESIGN), 1498c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(VOLTAGE_MIN_DESIGN), 1508c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(VOLTAGE_NOW), 1518c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(VOLTAGE_AVG), 1528c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(VOLTAGE_OCV), 1538c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(VOLTAGE_BOOT), 1548c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CURRENT_MAX), 1558c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CURRENT_NOW), 1568c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CURRENT_AVG), 1578c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CURRENT_BOOT), 1588c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(POWER_NOW), 1598c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(POWER_AVG), 1608c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_FULL_DESIGN), 1618c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_EMPTY_DESIGN), 1628c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_FULL), 1638c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_EMPTY), 1648c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_NOW), 1658c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_AVG), 1668c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_COUNTER), 1678c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CONSTANT_CHARGE_CURRENT), 1688c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CONSTANT_CHARGE_CURRENT_MAX), 1698c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CONSTANT_CHARGE_VOLTAGE), 1708c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CONSTANT_CHARGE_VOLTAGE_MAX), 1718c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_CONTROL_LIMIT), 1728c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_CONTROL_LIMIT_MAX), 1738c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_CONTROL_START_THRESHOLD), 1748c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_CONTROL_END_THRESHOLD), 1758c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(INPUT_CURRENT_LIMIT), 1768c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(INPUT_VOLTAGE_LIMIT), 1778c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(INPUT_POWER_LIMIT), 1788c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(ENERGY_FULL_DESIGN), 1798c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(ENERGY_EMPTY_DESIGN), 1808c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(ENERGY_FULL), 1818c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(ENERGY_EMPTY), 1828c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(ENERGY_NOW), 1838c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(ENERGY_AVG), 1848c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CAPACITY), 1858c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CAPACITY_ALERT_MIN), 1868c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CAPACITY_ALERT_MAX), 1878c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CAPACITY_ERROR_MARGIN), 1888c2ecf20Sopenharmony_ci POWER_SUPPLY_ENUM_ATTR(CAPACITY_LEVEL), 1898c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(TEMP), 1908c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(TEMP_MAX), 1918c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(TEMP_MIN), 1928c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(TEMP_ALERT_MIN), 1938c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(TEMP_ALERT_MAX), 1948c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(TEMP_AMBIENT), 1958c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(TEMP_AMBIENT_ALERT_MIN), 1968c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(TEMP_AMBIENT_ALERT_MAX), 1978c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(TIME_TO_EMPTY_NOW), 1988c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(TIME_TO_EMPTY_AVG), 1998c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(TIME_TO_FULL_NOW), 2008c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(TIME_TO_FULL_AVG), 2018c2ecf20Sopenharmony_ci POWER_SUPPLY_ENUM_ATTR(TYPE), 2028c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(USB_TYPE), 2038c2ecf20Sopenharmony_ci POWER_SUPPLY_ENUM_ATTR(SCOPE), 2048c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(PRECHARGE_CURRENT), 2058c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CHARGE_TERM_CURRENT), 2068c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(CALIBRATE), 2078c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(MANUFACTURE_YEAR), 2088c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(MANUFACTURE_MONTH), 2098c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(MANUFACTURE_DAY), 2108c2ecf20Sopenharmony_ci /* Properties of type `const char *' */ 2118c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(MODEL_NAME), 2128c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(MANUFACTURER), 2138c2ecf20Sopenharmony_ci POWER_SUPPLY_ATTR(SERIAL_NUMBER), 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic struct attribute * 2178c2ecf20Sopenharmony_ci__power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1]; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic struct power_supply_attr *to_ps_attr(struct device_attribute *attr) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci return container_of(attr, struct power_supply_attr, dev_attr); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic enum power_supply_property dev_attr_psp(struct device_attribute *attr) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci return to_ps_attr(attr) - power_supply_attrs; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic ssize_t power_supply_show_usb_type(struct device *dev, 2308c2ecf20Sopenharmony_ci const struct power_supply_desc *desc, 2318c2ecf20Sopenharmony_ci union power_supply_propval *value, 2328c2ecf20Sopenharmony_ci char *buf) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci enum power_supply_usb_type usb_type; 2358c2ecf20Sopenharmony_ci ssize_t count = 0; 2368c2ecf20Sopenharmony_ci bool match = false; 2378c2ecf20Sopenharmony_ci int i; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci for (i = 0; i < desc->num_usb_types; ++i) { 2408c2ecf20Sopenharmony_ci usb_type = desc->usb_types[i]; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (value->intval == usb_type) { 2438c2ecf20Sopenharmony_ci count += sprintf(buf + count, "[%s] ", 2448c2ecf20Sopenharmony_ci POWER_SUPPLY_USB_TYPE_TEXT[usb_type]); 2458c2ecf20Sopenharmony_ci match = true; 2468c2ecf20Sopenharmony_ci } else { 2478c2ecf20Sopenharmony_ci count += sprintf(buf + count, "%s ", 2488c2ecf20Sopenharmony_ci POWER_SUPPLY_USB_TYPE_TEXT[usb_type]); 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (!match) { 2538c2ecf20Sopenharmony_ci dev_warn(dev, "driver reporting unsupported connected type\n"); 2548c2ecf20Sopenharmony_ci return -EINVAL; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (count) 2588c2ecf20Sopenharmony_ci buf[count - 1] = '\n'; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return count; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic ssize_t power_supply_show_property(struct device *dev, 2648c2ecf20Sopenharmony_ci struct device_attribute *attr, 2658c2ecf20Sopenharmony_ci char *buf) { 2668c2ecf20Sopenharmony_ci ssize_t ret; 2678c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 2688c2ecf20Sopenharmony_ci struct power_supply_attr *ps_attr = to_ps_attr(attr); 2698c2ecf20Sopenharmony_ci enum power_supply_property psp = dev_attr_psp(attr); 2708c2ecf20Sopenharmony_ci union power_supply_propval value; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (psp == POWER_SUPPLY_PROP_TYPE) { 2738c2ecf20Sopenharmony_ci value.intval = psy->desc->type; 2748c2ecf20Sopenharmony_ci } else { 2758c2ecf20Sopenharmony_ci ret = power_supply_get_property(psy, psp, &value); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (ret < 0) { 2788c2ecf20Sopenharmony_ci if (ret == -ENODATA) 2798c2ecf20Sopenharmony_ci dev_dbg_ratelimited(dev, 2808c2ecf20Sopenharmony_ci "driver has no data for `%s' property\n", 2818c2ecf20Sopenharmony_ci attr->attr.name); 2828c2ecf20Sopenharmony_ci else if (ret != -ENODEV && ret != -EAGAIN) 2838c2ecf20Sopenharmony_ci dev_err_ratelimited(dev, 2848c2ecf20Sopenharmony_ci "driver failed to report `%s' property: %zd\n", 2858c2ecf20Sopenharmony_ci attr->attr.name, ret); 2868c2ecf20Sopenharmony_ci return ret; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (ps_attr->text_values_len > 0 && 2918c2ecf20Sopenharmony_ci value.intval < ps_attr->text_values_len && value.intval >= 0) { 2928c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", ps_attr->text_values[value.intval]); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci switch (psp) { 2968c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_USB_TYPE: 2978c2ecf20Sopenharmony_ci ret = power_supply_show_usb_type(dev, psy->desc, 2988c2ecf20Sopenharmony_ci &value, buf); 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER: 3018c2ecf20Sopenharmony_ci ret = sprintf(buf, "%s\n", value.strval); 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci default: 3048c2ecf20Sopenharmony_ci ret = sprintf(buf, "%d\n", value.intval); 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci return ret; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic ssize_t power_supply_store_property(struct device *dev, 3118c2ecf20Sopenharmony_ci struct device_attribute *attr, 3128c2ecf20Sopenharmony_ci const char *buf, size_t count) { 3138c2ecf20Sopenharmony_ci ssize_t ret; 3148c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 3158c2ecf20Sopenharmony_ci struct power_supply_attr *ps_attr = to_ps_attr(attr); 3168c2ecf20Sopenharmony_ci enum power_supply_property psp = dev_attr_psp(attr); 3178c2ecf20Sopenharmony_ci union power_supply_propval value; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci ret = -EINVAL; 3208c2ecf20Sopenharmony_ci if (ps_attr->text_values_len > 0) { 3218c2ecf20Sopenharmony_ci ret = __sysfs_match_string(ps_attr->text_values, 3228c2ecf20Sopenharmony_ci ps_attr->text_values_len, buf); 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* 3268c2ecf20Sopenharmony_ci * If no match was found, then check to see if it is an integer. 3278c2ecf20Sopenharmony_ci * Integer values are valid for enums in addition to the text value. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci if (ret < 0) { 3308c2ecf20Sopenharmony_ci long long_val; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci ret = kstrtol(buf, 10, &long_val); 3338c2ecf20Sopenharmony_ci if (ret < 0) 3348c2ecf20Sopenharmony_ci return ret; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci ret = long_val; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci value.intval = ret; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = power_supply_set_property(psy, psp, &value); 3428c2ecf20Sopenharmony_ci if (ret < 0) 3438c2ecf20Sopenharmony_ci return ret; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return count; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic umode_t power_supply_attr_is_visible(struct kobject *kobj, 3498c2ecf20Sopenharmony_ci struct attribute *attr, 3508c2ecf20Sopenharmony_ci int attrno) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 3538c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 3548c2ecf20Sopenharmony_ci umode_t mode = S_IRUSR | S_IRGRP | S_IROTH; 3558c2ecf20Sopenharmony_ci int i; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (!power_supply_attrs[attrno].prop_name) 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (attrno == POWER_SUPPLY_PROP_TYPE) 3618c2ecf20Sopenharmony_ci return mode; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci for (i = 0; i < psy->desc->num_properties; i++) { 3648c2ecf20Sopenharmony_ci int property = psy->desc->properties[i]; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (property == attrno) { 3678c2ecf20Sopenharmony_ci if (psy->desc->property_is_writeable && 3688c2ecf20Sopenharmony_ci psy->desc->property_is_writeable(psy, property) > 0) 3698c2ecf20Sopenharmony_ci mode |= S_IWUSR; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return mode; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci return 0; 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic struct attribute_group power_supply_attr_group = { 3798c2ecf20Sopenharmony_ci .attrs = __power_supply_attrs, 3808c2ecf20Sopenharmony_ci .is_visible = power_supply_attr_is_visible, 3818c2ecf20Sopenharmony_ci}; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic const struct attribute_group *power_supply_attr_groups[] = { 3848c2ecf20Sopenharmony_ci &power_supply_attr_group, 3858c2ecf20Sopenharmony_ci NULL, 3868c2ecf20Sopenharmony_ci}; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic void str_to_lower(char *str) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci while (*str) { 3918c2ecf20Sopenharmony_ci *str = tolower(*str); 3928c2ecf20Sopenharmony_ci str++; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_civoid power_supply_init_attrs(struct device_type *dev_type) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci int i; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci dev_type->groups = power_supply_attr_groups; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++) { 4038c2ecf20Sopenharmony_ci struct device_attribute *attr; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (!power_supply_attrs[i].prop_name) { 4068c2ecf20Sopenharmony_ci pr_warn("%s: Property %d skipped because is is missing from power_supply_attrs\n", 4078c2ecf20Sopenharmony_ci __func__, i); 4088c2ecf20Sopenharmony_ci sprintf(power_supply_attrs[i].attr_name, "_err_%d", i); 4098c2ecf20Sopenharmony_ci } else { 4108c2ecf20Sopenharmony_ci str_to_lower(power_supply_attrs[i].attr_name); 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci attr = &power_supply_attrs[i].dev_attr; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci attr->attr.name = power_supply_attrs[i].attr_name; 4168c2ecf20Sopenharmony_ci attr->show = power_supply_show_property; 4178c2ecf20Sopenharmony_ci attr->store = power_supply_store_property; 4188c2ecf20Sopenharmony_ci __power_supply_attrs[i] = &attr->attr; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic int add_prop_uevent(struct device *dev, struct kobj_uevent_env *env, 4238c2ecf20Sopenharmony_ci enum power_supply_property prop, char *prop_buf) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci int ret = 0; 4268c2ecf20Sopenharmony_ci struct power_supply_attr *pwr_attr; 4278c2ecf20Sopenharmony_ci struct device_attribute *dev_attr; 4288c2ecf20Sopenharmony_ci char *line; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci pwr_attr = &power_supply_attrs[prop]; 4318c2ecf20Sopenharmony_ci dev_attr = &pwr_attr->dev_attr; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci ret = power_supply_show_property(dev, dev_attr, prop_buf); 4348c2ecf20Sopenharmony_ci if (ret == -ENODEV || ret == -ENODATA) { 4358c2ecf20Sopenharmony_ci /* 4368c2ecf20Sopenharmony_ci * When a battery is absent, we expect -ENODEV. Don't abort; 4378c2ecf20Sopenharmony_ci * send the uevent with at least the the PRESENT=0 property 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_ci return 0; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (ret < 0) 4438c2ecf20Sopenharmony_ci return ret; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci line = strchr(prop_buf, '\n'); 4468c2ecf20Sopenharmony_ci if (line) 4478c2ecf20Sopenharmony_ci *line = 0; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return add_uevent_var(env, "POWER_SUPPLY_%s=%s", 4508c2ecf20Sopenharmony_ci pwr_attr->prop_name, prop_buf); 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ciint power_supply_uevent(struct device *dev, struct kobj_uevent_env *env) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci struct power_supply *psy = dev_get_drvdata(dev); 4568c2ecf20Sopenharmony_ci int ret = 0, j; 4578c2ecf20Sopenharmony_ci char *prop_buf; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (!psy || !psy->desc) { 4608c2ecf20Sopenharmony_ci dev_dbg(dev, "No power supply yet\n"); 4618c2ecf20Sopenharmony_ci return ret; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->desc->name); 4658c2ecf20Sopenharmony_ci if (ret) 4668c2ecf20Sopenharmony_ci return ret; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci prop_buf = (char *)get_zeroed_page(GFP_KERNEL); 4698c2ecf20Sopenharmony_ci if (!prop_buf) 4708c2ecf20Sopenharmony_ci return -ENOMEM; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci ret = add_prop_uevent(dev, env, POWER_SUPPLY_PROP_TYPE, prop_buf); 4738c2ecf20Sopenharmony_ci if (ret) 4748c2ecf20Sopenharmony_ci goto out; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci for (j = 0; j < psy->desc->num_properties; j++) { 4778c2ecf20Sopenharmony_ci ret = add_prop_uevent(dev, env, psy->desc->properties[j], 4788c2ecf20Sopenharmony_ci prop_buf); 4798c2ecf20Sopenharmony_ci if (ret) 4808c2ecf20Sopenharmony_ci goto out; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ciout: 4848c2ecf20Sopenharmony_ci free_page((unsigned long)prop_buf); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci return ret; 4878c2ecf20Sopenharmony_ci} 488