Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  Sysfs interface for the universal power supply monitor class
0004  *
0005  *  Copyright © 2007  David Woodhouse <dwmw2@infradead.org>
0006  *  Copyright © 2007  Anton Vorontsov <cbou@mail.ru>
0007  *  Copyright © 2004  Szabolcs Gyurko
0008  *  Copyright © 2003  Ian Molton <spyro@f2s.com>
0009  *
0010  *  Modified: 2004, Oct     Szabolcs Gyurko
0011  */
0012 
0013 #include <linux/ctype.h>
0014 #include <linux/device.h>
0015 #include <linux/power_supply.h>
0016 #include <linux/slab.h>
0017 #include <linux/stat.h>
0018 
0019 #include "power_supply.h"
0020 
0021 #define MAX_PROP_NAME_LEN 30
0022 
0023 struct power_supply_attr {
0024     const char *prop_name;
0025     char attr_name[MAX_PROP_NAME_LEN + 1];
0026     struct device_attribute dev_attr;
0027     const char * const *text_values;
0028     int text_values_len;
0029 };
0030 
0031 #define _POWER_SUPPLY_ATTR(_name, _text, _len)  \
0032 [POWER_SUPPLY_PROP_ ## _name] =         \
0033 {                       \
0034     .prop_name = #_name,            \
0035     .attr_name = #_name "\0",       \
0036     .text_values = _text,           \
0037     .text_values_len = _len,        \
0038 }
0039 
0040 #define POWER_SUPPLY_ATTR(_name) _POWER_SUPPLY_ATTR(_name, NULL, 0)
0041 #define _POWER_SUPPLY_ENUM_ATTR(_name, _text)   \
0042     _POWER_SUPPLY_ATTR(_name, _text, ARRAY_SIZE(_text))
0043 #define POWER_SUPPLY_ENUM_ATTR(_name)   \
0044     _POWER_SUPPLY_ENUM_ATTR(_name, POWER_SUPPLY_ ## _name ## _TEXT)
0045 
0046 static const char * const POWER_SUPPLY_TYPE_TEXT[] = {
0047     [POWER_SUPPLY_TYPE_UNKNOWN]     = "Unknown",
0048     [POWER_SUPPLY_TYPE_BATTERY]     = "Battery",
0049     [POWER_SUPPLY_TYPE_UPS]         = "UPS",
0050     [POWER_SUPPLY_TYPE_MAINS]       = "Mains",
0051     [POWER_SUPPLY_TYPE_USB]         = "USB",
0052     [POWER_SUPPLY_TYPE_USB_DCP]     = "USB_DCP",
0053     [POWER_SUPPLY_TYPE_USB_CDP]     = "USB_CDP",
0054     [POWER_SUPPLY_TYPE_USB_ACA]     = "USB_ACA",
0055     [POWER_SUPPLY_TYPE_USB_TYPE_C]      = "USB_C",
0056     [POWER_SUPPLY_TYPE_USB_PD]      = "USB_PD",
0057     [POWER_SUPPLY_TYPE_USB_PD_DRP]      = "USB_PD_DRP",
0058     [POWER_SUPPLY_TYPE_APPLE_BRICK_ID]  = "BrickID",
0059     [POWER_SUPPLY_TYPE_WIRELESS]        = "Wireless",
0060 };
0061 
0062 static const char * const POWER_SUPPLY_USB_TYPE_TEXT[] = {
0063     [POWER_SUPPLY_USB_TYPE_UNKNOWN]     = "Unknown",
0064     [POWER_SUPPLY_USB_TYPE_SDP]     = "SDP",
0065     [POWER_SUPPLY_USB_TYPE_DCP]     = "DCP",
0066     [POWER_SUPPLY_USB_TYPE_CDP]     = "CDP",
0067     [POWER_SUPPLY_USB_TYPE_ACA]     = "ACA",
0068     [POWER_SUPPLY_USB_TYPE_C]       = "C",
0069     [POWER_SUPPLY_USB_TYPE_PD]      = "PD",
0070     [POWER_SUPPLY_USB_TYPE_PD_DRP]      = "PD_DRP",
0071     [POWER_SUPPLY_USB_TYPE_PD_PPS]      = "PD_PPS",
0072     [POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID]  = "BrickID",
0073 };
0074 
0075 static const char * const POWER_SUPPLY_STATUS_TEXT[] = {
0076     [POWER_SUPPLY_STATUS_UNKNOWN]       = "Unknown",
0077     [POWER_SUPPLY_STATUS_CHARGING]      = "Charging",
0078     [POWER_SUPPLY_STATUS_DISCHARGING]   = "Discharging",
0079     [POWER_SUPPLY_STATUS_NOT_CHARGING]  = "Not charging",
0080     [POWER_SUPPLY_STATUS_FULL]      = "Full",
0081 };
0082 
0083 static const char * const POWER_SUPPLY_CHARGE_TYPE_TEXT[] = {
0084     [POWER_SUPPLY_CHARGE_TYPE_UNKNOWN]  = "Unknown",
0085     [POWER_SUPPLY_CHARGE_TYPE_NONE]     = "N/A",
0086     [POWER_SUPPLY_CHARGE_TYPE_TRICKLE]  = "Trickle",
0087     [POWER_SUPPLY_CHARGE_TYPE_FAST]     = "Fast",
0088     [POWER_SUPPLY_CHARGE_TYPE_STANDARD] = "Standard",
0089     [POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE] = "Adaptive",
0090     [POWER_SUPPLY_CHARGE_TYPE_CUSTOM]   = "Custom",
0091     [POWER_SUPPLY_CHARGE_TYPE_LONGLIFE] = "Long Life",
0092     [POWER_SUPPLY_CHARGE_TYPE_BYPASS]   = "Bypass",
0093 };
0094 
0095 static const char * const POWER_SUPPLY_HEALTH_TEXT[] = {
0096     [POWER_SUPPLY_HEALTH_UNKNOWN]           = "Unknown",
0097     [POWER_SUPPLY_HEALTH_GOOD]          = "Good",
0098     [POWER_SUPPLY_HEALTH_OVERHEAT]          = "Overheat",
0099     [POWER_SUPPLY_HEALTH_DEAD]          = "Dead",
0100     [POWER_SUPPLY_HEALTH_OVERVOLTAGE]       = "Over voltage",
0101     [POWER_SUPPLY_HEALTH_UNSPEC_FAILURE]        = "Unspecified failure",
0102     [POWER_SUPPLY_HEALTH_COLD]          = "Cold",
0103     [POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE] = "Watchdog timer expire",
0104     [POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE]   = "Safety timer expire",
0105     [POWER_SUPPLY_HEALTH_OVERCURRENT]       = "Over current",
0106     [POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED]  = "Calibration required",
0107     [POWER_SUPPLY_HEALTH_WARM]          = "Warm",
0108     [POWER_SUPPLY_HEALTH_COOL]          = "Cool",
0109     [POWER_SUPPLY_HEALTH_HOT]           = "Hot",
0110     [POWER_SUPPLY_HEALTH_NO_BATTERY]        = "No battery",
0111 };
0112 
0113 static const char * const POWER_SUPPLY_TECHNOLOGY_TEXT[] = {
0114     [POWER_SUPPLY_TECHNOLOGY_UNKNOWN]   = "Unknown",
0115     [POWER_SUPPLY_TECHNOLOGY_NiMH]      = "NiMH",
0116     [POWER_SUPPLY_TECHNOLOGY_LION]      = "Li-ion",
0117     [POWER_SUPPLY_TECHNOLOGY_LIPO]      = "Li-poly",
0118     [POWER_SUPPLY_TECHNOLOGY_LiFe]      = "LiFe",
0119     [POWER_SUPPLY_TECHNOLOGY_NiCd]      = "NiCd",
0120     [POWER_SUPPLY_TECHNOLOGY_LiMn]      = "LiMn",
0121 };
0122 
0123 static const char * const POWER_SUPPLY_CAPACITY_LEVEL_TEXT[] = {
0124     [POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN]   = "Unknown",
0125     [POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL]  = "Critical",
0126     [POWER_SUPPLY_CAPACITY_LEVEL_LOW]   = "Low",
0127     [POWER_SUPPLY_CAPACITY_LEVEL_NORMAL]    = "Normal",
0128     [POWER_SUPPLY_CAPACITY_LEVEL_HIGH]  = "High",
0129     [POWER_SUPPLY_CAPACITY_LEVEL_FULL]  = "Full",
0130 };
0131 
0132 static const char * const POWER_SUPPLY_SCOPE_TEXT[] = {
0133     [POWER_SUPPLY_SCOPE_UNKNOWN]    = "Unknown",
0134     [POWER_SUPPLY_SCOPE_SYSTEM] = "System",
0135     [POWER_SUPPLY_SCOPE_DEVICE] = "Device",
0136 };
0137 
0138 static const char * const POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT[] = {
0139     [POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO]        = "auto",
0140     [POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE]  = "inhibit-charge",
0141     [POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE] = "force-discharge",
0142 };
0143 
0144 static struct power_supply_attr power_supply_attrs[] = {
0145     /* Properties of type `int' */
0146     POWER_SUPPLY_ENUM_ATTR(STATUS),
0147     POWER_SUPPLY_ENUM_ATTR(CHARGE_TYPE),
0148     POWER_SUPPLY_ENUM_ATTR(HEALTH),
0149     POWER_SUPPLY_ATTR(PRESENT),
0150     POWER_SUPPLY_ATTR(ONLINE),
0151     POWER_SUPPLY_ATTR(AUTHENTIC),
0152     POWER_SUPPLY_ENUM_ATTR(TECHNOLOGY),
0153     POWER_SUPPLY_ATTR(CYCLE_COUNT),
0154     POWER_SUPPLY_ATTR(VOLTAGE_MAX),
0155     POWER_SUPPLY_ATTR(VOLTAGE_MIN),
0156     POWER_SUPPLY_ATTR(VOLTAGE_MAX_DESIGN),
0157     POWER_SUPPLY_ATTR(VOLTAGE_MIN_DESIGN),
0158     POWER_SUPPLY_ATTR(VOLTAGE_NOW),
0159     POWER_SUPPLY_ATTR(VOLTAGE_AVG),
0160     POWER_SUPPLY_ATTR(VOLTAGE_OCV),
0161     POWER_SUPPLY_ATTR(VOLTAGE_BOOT),
0162     POWER_SUPPLY_ATTR(CURRENT_MAX),
0163     POWER_SUPPLY_ATTR(CURRENT_NOW),
0164     POWER_SUPPLY_ATTR(CURRENT_AVG),
0165     POWER_SUPPLY_ATTR(CURRENT_BOOT),
0166     POWER_SUPPLY_ATTR(POWER_NOW),
0167     POWER_SUPPLY_ATTR(POWER_AVG),
0168     POWER_SUPPLY_ATTR(CHARGE_FULL_DESIGN),
0169     POWER_SUPPLY_ATTR(CHARGE_EMPTY_DESIGN),
0170     POWER_SUPPLY_ATTR(CHARGE_FULL),
0171     POWER_SUPPLY_ATTR(CHARGE_EMPTY),
0172     POWER_SUPPLY_ATTR(CHARGE_NOW),
0173     POWER_SUPPLY_ATTR(CHARGE_AVG),
0174     POWER_SUPPLY_ATTR(CHARGE_COUNTER),
0175     POWER_SUPPLY_ATTR(CONSTANT_CHARGE_CURRENT),
0176     POWER_SUPPLY_ATTR(CONSTANT_CHARGE_CURRENT_MAX),
0177     POWER_SUPPLY_ATTR(CONSTANT_CHARGE_VOLTAGE),
0178     POWER_SUPPLY_ATTR(CONSTANT_CHARGE_VOLTAGE_MAX),
0179     POWER_SUPPLY_ATTR(CHARGE_CONTROL_LIMIT),
0180     POWER_SUPPLY_ATTR(CHARGE_CONTROL_LIMIT_MAX),
0181     POWER_SUPPLY_ATTR(CHARGE_CONTROL_START_THRESHOLD),
0182     POWER_SUPPLY_ATTR(CHARGE_CONTROL_END_THRESHOLD),
0183     POWER_SUPPLY_ENUM_ATTR(CHARGE_BEHAVIOUR),
0184     POWER_SUPPLY_ATTR(INPUT_CURRENT_LIMIT),
0185     POWER_SUPPLY_ATTR(INPUT_VOLTAGE_LIMIT),
0186     POWER_SUPPLY_ATTR(INPUT_POWER_LIMIT),
0187     POWER_SUPPLY_ATTR(ENERGY_FULL_DESIGN),
0188     POWER_SUPPLY_ATTR(ENERGY_EMPTY_DESIGN),
0189     POWER_SUPPLY_ATTR(ENERGY_FULL),
0190     POWER_SUPPLY_ATTR(ENERGY_EMPTY),
0191     POWER_SUPPLY_ATTR(ENERGY_NOW),
0192     POWER_SUPPLY_ATTR(ENERGY_AVG),
0193     POWER_SUPPLY_ATTR(CAPACITY),
0194     POWER_SUPPLY_ATTR(CAPACITY_ALERT_MIN),
0195     POWER_SUPPLY_ATTR(CAPACITY_ALERT_MAX),
0196     POWER_SUPPLY_ATTR(CAPACITY_ERROR_MARGIN),
0197     POWER_SUPPLY_ENUM_ATTR(CAPACITY_LEVEL),
0198     POWER_SUPPLY_ATTR(TEMP),
0199     POWER_SUPPLY_ATTR(TEMP_MAX),
0200     POWER_SUPPLY_ATTR(TEMP_MIN),
0201     POWER_SUPPLY_ATTR(TEMP_ALERT_MIN),
0202     POWER_SUPPLY_ATTR(TEMP_ALERT_MAX),
0203     POWER_SUPPLY_ATTR(TEMP_AMBIENT),
0204     POWER_SUPPLY_ATTR(TEMP_AMBIENT_ALERT_MIN),
0205     POWER_SUPPLY_ATTR(TEMP_AMBIENT_ALERT_MAX),
0206     POWER_SUPPLY_ATTR(TIME_TO_EMPTY_NOW),
0207     POWER_SUPPLY_ATTR(TIME_TO_EMPTY_AVG),
0208     POWER_SUPPLY_ATTR(TIME_TO_FULL_NOW),
0209     POWER_SUPPLY_ATTR(TIME_TO_FULL_AVG),
0210     POWER_SUPPLY_ENUM_ATTR(TYPE),
0211     POWER_SUPPLY_ATTR(USB_TYPE),
0212     POWER_SUPPLY_ENUM_ATTR(SCOPE),
0213     POWER_SUPPLY_ATTR(PRECHARGE_CURRENT),
0214     POWER_SUPPLY_ATTR(CHARGE_TERM_CURRENT),
0215     POWER_SUPPLY_ATTR(CALIBRATE),
0216     POWER_SUPPLY_ATTR(MANUFACTURE_YEAR),
0217     POWER_SUPPLY_ATTR(MANUFACTURE_MONTH),
0218     POWER_SUPPLY_ATTR(MANUFACTURE_DAY),
0219     /* Properties of type `const char *' */
0220     POWER_SUPPLY_ATTR(MODEL_NAME),
0221     POWER_SUPPLY_ATTR(MANUFACTURER),
0222     POWER_SUPPLY_ATTR(SERIAL_NUMBER),
0223 };
0224 
0225 static struct attribute *
0226 __power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1];
0227 
0228 static struct power_supply_attr *to_ps_attr(struct device_attribute *attr)
0229 {
0230     return container_of(attr, struct power_supply_attr, dev_attr);
0231 }
0232 
0233 static enum power_supply_property dev_attr_psp(struct device_attribute *attr)
0234 {
0235     return  to_ps_attr(attr) - power_supply_attrs;
0236 }
0237 
0238 static ssize_t power_supply_show_usb_type(struct device *dev,
0239                       const struct power_supply_desc *desc,
0240                       union power_supply_propval *value,
0241                       char *buf)
0242 {
0243     enum power_supply_usb_type usb_type;
0244     ssize_t count = 0;
0245     bool match = false;
0246     int i;
0247 
0248     for (i = 0; i < desc->num_usb_types; ++i) {
0249         usb_type = desc->usb_types[i];
0250 
0251         if (value->intval == usb_type) {
0252             count += sprintf(buf + count, "[%s] ",
0253                      POWER_SUPPLY_USB_TYPE_TEXT[usb_type]);
0254             match = true;
0255         } else {
0256             count += sprintf(buf + count, "%s ",
0257                      POWER_SUPPLY_USB_TYPE_TEXT[usb_type]);
0258         }
0259     }
0260 
0261     if (!match) {
0262         dev_warn(dev, "driver reporting unsupported connected type\n");
0263         return -EINVAL;
0264     }
0265 
0266     if (count)
0267         buf[count - 1] = '\n';
0268 
0269     return count;
0270 }
0271 
0272 static ssize_t power_supply_show_property(struct device *dev,
0273                       struct device_attribute *attr,
0274                       char *buf) {
0275     ssize_t ret;
0276     struct power_supply *psy = dev_get_drvdata(dev);
0277     struct power_supply_attr *ps_attr = to_ps_attr(attr);
0278     enum power_supply_property psp = dev_attr_psp(attr);
0279     union power_supply_propval value;
0280 
0281     if (psp == POWER_SUPPLY_PROP_TYPE) {
0282         value.intval = psy->desc->type;
0283     } else {
0284         ret = power_supply_get_property(psy, psp, &value);
0285 
0286         if (ret < 0) {
0287             if (ret == -ENODATA)
0288                 dev_dbg(dev, "driver has no data for `%s' property\n",
0289                     attr->attr.name);
0290             else if (ret != -ENODEV && ret != -EAGAIN)
0291                 dev_err_ratelimited(dev,
0292                     "driver failed to report `%s' property: %zd\n",
0293                     attr->attr.name, ret);
0294             return ret;
0295         }
0296     }
0297 
0298     if (ps_attr->text_values_len > 0 &&
0299         value.intval < ps_attr->text_values_len && value.intval >= 0) {
0300         return sprintf(buf, "%s\n", ps_attr->text_values[value.intval]);
0301     }
0302 
0303     switch (psp) {
0304     case POWER_SUPPLY_PROP_USB_TYPE:
0305         ret = power_supply_show_usb_type(dev, psy->desc,
0306                         &value, buf);
0307         break;
0308     case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER:
0309         ret = sprintf(buf, "%s\n", value.strval);
0310         break;
0311     default:
0312         ret = sprintf(buf, "%d\n", value.intval);
0313     }
0314 
0315     return ret;
0316 }
0317 
0318 static ssize_t power_supply_store_property(struct device *dev,
0319                        struct device_attribute *attr,
0320                        const char *buf, size_t count) {
0321     ssize_t ret;
0322     struct power_supply *psy = dev_get_drvdata(dev);
0323     struct power_supply_attr *ps_attr = to_ps_attr(attr);
0324     enum power_supply_property psp = dev_attr_psp(attr);
0325     union power_supply_propval value;
0326 
0327     ret = -EINVAL;
0328     if (ps_attr->text_values_len > 0) {
0329         ret = __sysfs_match_string(ps_attr->text_values,
0330                        ps_attr->text_values_len, buf);
0331     }
0332 
0333     /*
0334      * If no match was found, then check to see if it is an integer.
0335      * Integer values are valid for enums in addition to the text value.
0336      */
0337     if (ret < 0) {
0338         long long_val;
0339 
0340         ret = kstrtol(buf, 10, &long_val);
0341         if (ret < 0)
0342             return ret;
0343 
0344         ret = long_val;
0345     }
0346 
0347     value.intval = ret;
0348 
0349     ret = power_supply_set_property(psy, psp, &value);
0350     if (ret < 0)
0351         return ret;
0352 
0353     return count;
0354 }
0355 
0356 static umode_t power_supply_attr_is_visible(struct kobject *kobj,
0357                        struct attribute *attr,
0358                        int attrno)
0359 {
0360     struct device *dev = kobj_to_dev(kobj);
0361     struct power_supply *psy = dev_get_drvdata(dev);
0362     umode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
0363     int i;
0364 
0365     if (!power_supply_attrs[attrno].prop_name)
0366         return 0;
0367 
0368     if (attrno == POWER_SUPPLY_PROP_TYPE)
0369         return mode;
0370 
0371     for (i = 0; i < psy->desc->num_properties; i++) {
0372         int property = psy->desc->properties[i];
0373 
0374         if (property == attrno) {
0375             if (psy->desc->property_is_writeable &&
0376                 psy->desc->property_is_writeable(psy, property) > 0)
0377                 mode |= S_IWUSR;
0378 
0379             return mode;
0380         }
0381     }
0382 
0383     return 0;
0384 }
0385 
0386 static const struct attribute_group power_supply_attr_group = {
0387     .attrs = __power_supply_attrs,
0388     .is_visible = power_supply_attr_is_visible,
0389 };
0390 
0391 static const struct attribute_group *power_supply_attr_groups[] = {
0392     &power_supply_attr_group,
0393     NULL,
0394 };
0395 
0396 static void str_to_lower(char *str)
0397 {
0398     while (*str) {
0399         *str = tolower(*str);
0400         str++;
0401     }
0402 }
0403 
0404 void power_supply_init_attrs(struct device_type *dev_type)
0405 {
0406     int i;
0407 
0408     dev_type->groups = power_supply_attr_groups;
0409 
0410     for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++) {
0411         struct device_attribute *attr;
0412 
0413         if (!power_supply_attrs[i].prop_name) {
0414             pr_warn("%s: Property %d skipped because it is missing from power_supply_attrs\n",
0415                 __func__, i);
0416             sprintf(power_supply_attrs[i].attr_name, "_err_%d", i);
0417         } else {
0418             str_to_lower(power_supply_attrs[i].attr_name);
0419         }
0420 
0421         attr = &power_supply_attrs[i].dev_attr;
0422 
0423         attr->attr.name = power_supply_attrs[i].attr_name;
0424         attr->show = power_supply_show_property;
0425         attr->store = power_supply_store_property;
0426         __power_supply_attrs[i] = &attr->attr;
0427     }
0428 }
0429 
0430 static int add_prop_uevent(struct device *dev, struct kobj_uevent_env *env,
0431                enum power_supply_property prop, char *prop_buf)
0432 {
0433     int ret = 0;
0434     struct power_supply_attr *pwr_attr;
0435     struct device_attribute *dev_attr;
0436     char *line;
0437 
0438     pwr_attr = &power_supply_attrs[prop];
0439     dev_attr = &pwr_attr->dev_attr;
0440 
0441     ret = power_supply_show_property(dev, dev_attr, prop_buf);
0442     if (ret == -ENODEV || ret == -ENODATA) {
0443         /*
0444          * When a battery is absent, we expect -ENODEV. Don't abort;
0445          * send the uevent with at least the the PRESENT=0 property
0446          */
0447         return 0;
0448     }
0449 
0450     if (ret < 0)
0451         return ret;
0452 
0453     line = strchr(prop_buf, '\n');
0454     if (line)
0455         *line = 0;
0456 
0457     return add_uevent_var(env, "POWER_SUPPLY_%s=%s",
0458                   pwr_attr->prop_name, prop_buf);
0459 }
0460 
0461 int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
0462 {
0463     struct power_supply *psy = dev_get_drvdata(dev);
0464     int ret = 0, j;
0465     char *prop_buf;
0466 
0467     if (!psy || !psy->desc) {
0468         dev_dbg(dev, "No power supply yet\n");
0469         return ret;
0470     }
0471 
0472     ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->desc->name);
0473     if (ret)
0474         return ret;
0475 
0476     prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
0477     if (!prop_buf)
0478         return -ENOMEM;
0479 
0480     ret = add_prop_uevent(dev, env, POWER_SUPPLY_PROP_TYPE, prop_buf);
0481     if (ret)
0482         goto out;
0483 
0484     for (j = 0; j < psy->desc->num_properties; j++) {
0485         ret = add_prop_uevent(dev, env, psy->desc->properties[j],
0486                       prop_buf);
0487         if (ret)
0488             goto out;
0489     }
0490 
0491 out:
0492     free_page((unsigned long)prop_buf);
0493 
0494     return ret;
0495 }
0496 
0497 ssize_t power_supply_charge_behaviour_show(struct device *dev,
0498                        unsigned int available_behaviours,
0499                        enum power_supply_charge_behaviour current_behaviour,
0500                        char *buf)
0501 {
0502     bool match = false, available, active;
0503     ssize_t count = 0;
0504     int i;
0505 
0506     for (i = 0; i < ARRAY_SIZE(POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT); i++) {
0507         available = available_behaviours & BIT(i);
0508         active = i == current_behaviour;
0509 
0510         if (available && active) {
0511             count += sysfs_emit_at(buf, count, "[%s] ",
0512                            POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT[i]);
0513             match = true;
0514         } else if (available) {
0515             count += sysfs_emit_at(buf, count, "%s ",
0516                            POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT[i]);
0517         }
0518     }
0519 
0520     if (!match) {
0521         dev_warn(dev, "driver reporting unsupported charge behaviour\n");
0522         return -EINVAL;
0523     }
0524 
0525     if (count)
0526         buf[count - 1] = '\n';
0527 
0528     return count;
0529 }
0530 EXPORT_SYMBOL_GPL(power_supply_charge_behaviour_show);
0531 
0532 int power_supply_charge_behaviour_parse(unsigned int available_behaviours, const char *buf)
0533 {
0534     int i = sysfs_match_string(POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT, buf);
0535 
0536     if (i < 0)
0537         return i;
0538 
0539     if (available_behaviours & BIT(i))
0540         return i;
0541 
0542     return -EINVAL;
0543 }
0544 EXPORT_SYMBOL_GPL(power_supply_charge_behaviour_parse);