Android手机过power_supply的节点获取充电和电池信息,今天来分析下power_supply。先不讲那么多,用个demo来熟悉下
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
struct mtk_charger_type {
struct power_supply_desc usb_desc;
struct power_supply_config usb_cfg;
enum power_supply_usb_type type;
struct power_supply *usb_psy;
};
static enum power_supply_property mt_usb_properties[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
};
static int mt_usb_get_property(struct power_supply *psy,enum power_supply_property psp, union power_supply_propval *val)
{
struct mtk_charger_type *info;
info = (struct mtk_charger_type *)power_supply_get_drvdata(psy);
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
if ((info->type == POWER_SUPPLY_USB_TYPE_SDP) ||
(info->type == POWER_SUPPLY_USB_TYPE_CDP))
val->intval = 1;
else
val->intval = 0;
break;
case POWER_SUPPLY_PROP_CURRENT_MAX:
val->intval = 500000;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
val->intval = 5000000;
break;
default:
return -EINVAL;
}
return 0;
}
static int power_probe(struct platform_device *pdev)
{
struct mtk_charger_type *info;
info = devm_kzalloc(&pdev->dev, sizeof(*info),GFP_KERNEL);
if (!info)
return -ENOMEM;
info->usb_desc.name = "usb";
info->usb_desc.type = POWER_SUPPLY_TYPE_USB;
info->usb_desc.properties = mt_usb_properties;
info->usb_desc.num_properties = ARRAY_SIZE(mt_usb_properties);
info->usb_desc.get_property = mt_usb_get_property;
info->usb_cfg.drv_data = info;
info->usb_psy = power_supply_register(&pdev->dev,&info->usb_desc, &info->usb_cfg);
if (IS_ERR(info->usb_psy)) {
pr_notice("%s Failed to register power supply: %ld\n",__func__, PTR_ERR(info->usb_psy));
return PTR_ERR(info->usb_psy);
}
return 0;
}
static int power_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_device power_devices =
{
.name = "power",
.id = 0,
};
static struct platform_driver power_driver =
{
.probe = power_probe,
.remove = power_remove,
.driver = {
.name = "power",
.owner = THIS_MODULE,
},
};
static int power_init(void)
{
platform_device_register(&power_devices);
platform_driver_register(&power_driver);
return 0;
}
static void __exit power_exit(void)
{
platform_device_unregister(&power_devices);
platform_driver_unregister(&power_driver);
}
module_init(power_init);
module_exit(power_exit);
MODULE_LICENSE("GPL");
编译成ko,在linux的环境运行下
看下power_supply_register的实现
static struct power_supply *__must_check__power_supply_register(struct device *parent,const struct power_supply_desc *desc,const struct power_supply_config *cfg,bool ws)
{
struct device *dev;
dev->class = power_supply_class;
INIT_WORK(&psy->changed_work, power_supply_changed_work);
device_add(dev);
}
device_add最终会跑到create_files,到sys对应的目录下创建文件
dev->class = power_supply_class;device属于power_supply_class下的设备,遍历grp里的所有属性,如果device也实现了该属性,便创建相应的文件
static int create_files(struct kernfs_node *parent, struct kobject *kobj,kuid_t uid, kgid_t gid,const struct attribute_group *grp, int update)
{
struct attribute *const *attr;
struct bin_attribute *const *bin_attr;
int error = 0, i;
if (grp->attrs) {
for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {
umode_t mode = (*attr)->mode;
if (grp->is_visible) {
mode = grp->is_visible(kobj, *attr, i);
if (!mode)
continue;
}
error = sysfs_add_file_mode_ns(parent, *attr, false,mode, uid, gid, NULL);
}
}
}
power_supply_core.c
static int __init power_supply_class_init(void)
{
power_supply_class = class_create(THIS_MODULE, "power_supply");
power_supply_class->dev_uevent = power_supply_uevent;
power_supply_init_attrs(&power_supply_dev_type);
}
void power_supply_init_attrs(struct device_type *dev_type)
{
int i;
dev_type->groups = power_supply_attr_groups;
for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)
__power_supply_attrs[i] = &power_supply_attrs[i].attr;
}
/* Must be in the same order as POWER_SUPPLY_PROP_* */
static struct device_attribute power_supply_attrs[] = {
/* Properties of type `int' */
POWER_SUPPLY_ATTR(status),
...
POWER_SUPPLY_ATTR(serial_number),
};
static struct attribute *
__power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1];
static umode_t power_supply_attr_is_visible(struct kobject *kobj,
struct attribute *attr,
int attrno)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct power_supply *psy = dev_get_drvdata(dev);
umode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
int i;
if (attrno == POWER_SUPPLY_PROP_TYPE)
return mode;
for (i = 0; i < psy->desc->num_properties; i++) {
int property = psy->desc->properties[i];
if (property == attrno) {
if (psy->desc->property_is_writeable &&
psy->desc->property_is_writeable(psy, property) > 0)
mode |= S_IWUSR;
return mode;
}
}
return 0;
}
static struct attribute_group power_supply_attr_group = {
.attrs = __power_supply_attrs,
.is_visible = power_supply_attr_is_visible,
};
static const struct attribute_group *power_supply_attr_groups[] = {
&power_supply_attr_group,
NULL,
};
#define POWER_SUPPLY_ATTR(_name) \
{ \
.attr = { .name = #_name }, \
.show = power_supply_show_property, \
.store = power_supply_store_property, \
}
static ssize_t power_supply_show_property(struct device *dev,struct device_attribute *attr,char *buf) {
ssize_t ret;
struct power_supply *psy = dev_get_drvdata(dev);
enum power_supply_property psp = attr - power_supply_attrs;
union power_supply_propval value;
if (psp == POWER_SUPPLY_PROP_TYPE) {
value.intval = psy->desc->type;
} else {
ret = power_supply_get_property(psy, psp, &value);
}
switch (psp) {
...
default:
ret = sprintf(buf, "%d\n", value.intval);
}
return ret;
}
再来看下这张图
int device_add(struct device *dev)
{
...
error = device_create_file(dev, &dev_attr_uevent);
...
}
current_max online voltage_max是驱动里带的属性,所以有该节点
uevent是device_add函数带来的节点,其实现是power_supply_uevent,该函数把所有的属性都汇报了一遍
type默认也是可见的,看power_supply_attr_is_visible函数
其他的几个文件夹可以不关注(在Android手机里面没有生成)
enum power_supply_property {
/* Properties of type `int' */
POWER_SUPPLY_PROP_STATUS = 0,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_AUTHENTIC,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MIN,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_AVG,
POWER_SUPPLY_PROP_VOLTAGE_OCV,
POWER_SUPPLY_PROP_VOLTAGE_BOOT,
POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_CURRENT_BOOT,
POWER_SUPPLY_PROP_POWER_NOW,
POWER_SUPPLY_PROP_POWER_AVG,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_EMPTY,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CHARGE_AVG,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
POWER_SUPPLY_PROP_ENERGY_FULL,
POWER_SUPPLY_PROP_ENERGY_EMPTY,
POWER_SUPPLY_PROP_ENERGY_NOW,
POWER_SUPPLY_PROP_ENERGY_AVG,
POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, /* in percents! */
POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TEMP_MAX,
POWER_SUPPLY_PROP_TEMP_MIN,
POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
POWER_SUPPLY_PROP_TEMP_AMBIENT,
POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN,
POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
POWER_SUPPLY_PROP_USB_TYPE,
POWER_SUPPLY_PROP_SCOPE,
POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
POWER_SUPPLY_PROP_CALIBRATE,
/* Local extensions */
POWER_SUPPLY_PROP_USB_HC,
POWER_SUPPLY_PROP_USB_OTG,
POWER_SUPPLY_PROP_CHARGE_ENABLED,
/* Local extensions of type int64_t */
POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
/* Properties of type `const char *' */
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_SERIAL_NUMBER,
POWER_SUPPLY_PROP_FEED_WATCHDOG,
};