在展锐的系统目录下可以看到如下几个目录
当设备充电时,会显示充电图标和充电动画, 整个充电状态的显示其实是通过读取sys/class/power_supply 目录下的属性文件控制的,当我们插入USB充电时,sys/class/power_supply/usb/online 节点会置为1,表示USB 已经插入,并且sys/class/power_supply/battery/status 节点会置为Charging 状态,此时Status Bar Battery图标上面会显示一个闪电图标和充电动画。
这篇文章将会梳理sys/class/power_supply 目录和其下各个属性节点的创建流程。
bsp/kernel/kernel4.14/drivers/power/supply/charger-manager.c
static int charger_manager_init_psy(struct charger_manager *cm)
{
struct charger_policy *policy = &(cm->policy);
struct power_supply_config batt_cfg = {};
batt_cfg.drv_data = cm;
batt_cfg.of_node = cm->dev->of_node;
// 注册sys/class/power_supply/interface 节点
policy->interface_psy = devm_power_supply_register(cm->dev,
&interface_psy_desc,
&batt_cfg);
if (IS_ERR(policy->interface_psy)) {
vote_debug("Couldn't register interface_psy power supply\n");
return PTR_ERR(policy->interface_psy);
}
// 注册sys/class/power_supply/hook 节点
policy->hook_psy = devm_power_supply_register(cm->dev,
&hook_psy_desc,
&batt_cfg);
if (IS_ERR(policy->hook_psy)) {
vote_debug("Couldn't register hook_psy power supply\n");
return PTR_ERR(policy->hook_psy);
}
// 注册sys/class/power_supply/usb 节点
policy->usb_power_phy = devm_power_supply_register(cm->dev,
&usb_psy_desc,
&batt_cfg);
if (IS_ERR(policy->usb_power_phy)) {
vote_debug("Couldn't register usb_power_phy power supply\n");
return PTR_ERR(policy->usb_power_phy);
}
// 注册sys/class/power_supply/ac 节点
policy->ac_power_phy = devm_power_supply_register(cm->dev,
&ac_psy_desc,
&batt_cfg);
if (IS_ERR(policy->ac_power_phy)) {
vote_debug("Couldn't register ac_power_phy power supply\n");
return PTR_ERR(policy->ac_power_phy);
}
return 0;
}
static int charger_manager_probe(struct platform_device *pdev){
...
memcpy(&cm->charger_psy_desc, &psy_default, sizeof(psy_default));
...
cm->charger_psy = power_supply_register(&pdev->dev, &cm->charger_psy_desc, &psy_cfg);
...
}
这里以battery 目录的创建流程为例,其他目录相似。
cm->charger_psy = power_supply_register(&pdev->dev, &cm->charger_psy_desc, &psy_cfg);
static const struct power_supply_desc psy_default = {
.name = "battery", // 节点目录的名字
.type = POWER_SUPPLY_TYPE_BATTERY, // 节点目录下type 的类型
.properties = default_charger_props, // 节点目录下的psy 属性信息,可以通过get_property 和 set_property 方法获取这些属性信息
.num_properties = ARRAY_SIZE(default_charger_props),
.get_property = charger_get_property, // 获取节点信息回调方法
.set_property = charger_set_property, // 设置节点信息回调方法
.property_is_writeable = charger_property_is_writeable, // 设置节点是否可以写入数据,这个方法将在power_supply_attr_is_visible中被使用
.no_thermal = true,
};
接着查看power_supply_register() 方法
bsp/kernel/kernel4.14/drivers/power/supply/power_supply_core.c
struct power_supply *__must_check power_supply_register(struct device *parent,
const struct power_supply_desc *desc,
const struct power_supply_config *cfg)
{
return __power_supply_register(parent, desc, cfg, true);
}
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;
struct power_supply *psy;
int i, rc;
...
psy = kzalloc(sizeof(*psy), GFP_KERNEL); // 初始化一个power_supply 设备
if (!psy)
return ERR_PTR(-ENOMEM);
dev = &psy->dev;
device_initialize(dev);
dev->class = power_supply_class; // 指定power_supply_class
dev->type = &power_supply_dev_type; // 指定设备类型
dev->parent = parent; // 指定此设备的父设备
dev->release = power_supply_dev_release; // release回调方法
dev_set_drvdata(dev, psy); // 将psy 保存下来,后面可以通过dev_get_drvdata() 方法获取psy
psy->desc = desc;
if (cfg) {
psy->drv_data = cfg->drv_data;
psy->of_node =
cfg->fwnode ? to_of_node(cfg->fwnode) : cfg->of_node;
psy->supplied_to = cfg->supplied_to;
psy->num_supplicants = cfg->num_supplicants;
}
rc = dev_set_name(dev, "%s", desc->name); // 设置dev的名字,这里是"battery"
if (rc)
goto dev_set_name_failed;
INIT_WORK(&psy->changed_work, power_supply_changed_work); // 初始化工作队列
INIT_DELAYED_WORK(&psy->deferred_register_work,
power_supply_deferred_register_work);
rc = power_supply_check_supplies(psy);
if (rc) {
dev_info(dev, "Not all required supplies found, defer probe\n");
goto check_supplies_failed;
}
spin_lock_init(&psy->changed_lock);
rc = device_init_wakeup(dev, ws);
if (rc)
goto wakeup_init_failed;
rc = device_add(dev); // 将这个设备添加到设备链表中去
if (rc)
goto device_add_failed;
rc = psy_register_thermal(psy); // 注册该设备的温度节点
if (rc)
goto register_thermal_failed;
rc = psy_register_cooler(psy); // 注册冷却设备
if (rc)
goto register_cooler_failed;
rc = power_supply_create_triggers(psy); // 与充电LED有关
if (rc)
goto create_triggers_failed;
atomic_inc(&psy->use_cnt);
psy->initialized = true; // 设置标志位,初始化成功
queue_delayed_work(system_power_efficient_wq,
&psy->deferred_register_work,
POWER_SUPPLY_DEFERRED_REGISTER_TIME);
...
}
在这个power_supply_register()方法中,使用device_add() 方法向bus 添加了此设备,便于bus 总线访问此battery 设备
bsp/kernel/kernel4.14/drivers/base/core.c
int device_add(struct device *dev)
{
struct device *parent;
struct kobject *kobj;
struct class_interface *class_intf;
int error = -EINVAL;
struct kobject *glue_dir = NULL;
dev = get_device(dev); // 增加该设备的引用计数
if (!dev)
goto done;
if (!dev->p) {
error = device_private_init(dev);
if (error)
goto done;
}
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name); // 初始化设备内部的kobject的名字
dev->init_name = NULL;
}
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id); // 使用bus以及设备id来初始化设备内部kobject名字
if (!dev_name(dev)) { // 获得设备的名字
error = -EINVAL;
goto name_error;
}
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
parent = get_device(dev->parent); // 增加设备父设备并增加父设备引用计数
kobj = get_device_parent(dev, parent); // 获取父节点的kobj
if (IS_ERR(kobj)) {
error = PTR_ERR(kobj);
goto parent_error;
}
if (kobj)
dev->kobj.parent = kobj; // 在kobject层实现设备父子关系
/* use parent numa_node */
if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
set_dev_node(dev, dev_to_node(parent));
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); //将设备加入到kobject模型中,创建sys相关目录
if (error) {
glue_dir = get_glue_dir(dev);
goto Error;
}
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
error = device_create_file(dev, &dev_attr_uevent); //创建sys目录下设备的uevent属性文件,通过它可以查看设备的uevent事件
if (error)
goto attrError;
error = device_add_class_symlinks(dev); // 创建of_node、subsystem、device 节点并链接到指定的目录下
if (error)
goto SymlinkError;
error = device_add_attrs(dev); // 创建battery目录下设备其他属性文件, 并设置节点文件的store和show 方法
if (error)
goto AttrsError;
error = bus_add_device(dev); // 将设备加入到管理它的bus总线的设备连表上
if (error)
goto BusError;
error = dpm_sysfs_add(dev); // 电源管理相关
if (error)
goto DPMError;
device_pm_add(dev);
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &dev_attr_dev); //创建sys目录下设备的设备号属性,即major和minor
if (error)
goto DevAttrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto SysEntryError;
devtmpfs_create_node(dev);
}
/* Notify clients of device addition. This call must come
* after dpm_sysfs_add() and before kobject_uevent().
*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
// //通知注册监听该总线的设备,有新设备加入
kobject_uevent(&dev->kobj, KOBJ_ADD);
//产生一个内核uevent事件,该事件可以被内核以及应用层捕获,属于linux设备模型中热插拔机制
bus_probe_device(dev); //------------开始寻找设备所对应的驱动------------
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
//建立设备与总线间的父子关系
if (dev->class) { //如果设备的属于某个设备类,比如Mass storage,HID等等
mutex_lock(&dev->class->p->mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices); //将设备挂接在其设备类上面
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf); //通知有新设备加入
mutex_unlock(&dev->class->p->mutex);
}
....
}
在add_device() 方法中,device_add_attrs() 方法会创建battery目录下设备其他属性文件,我们接着查看是如何创建的
bsp/kernel/kernel4.14/drivers/base/core.c
static int device_add_attrs(struct device *dev)
{
struct class *class = dev->class;
const struct device_type *type = dev->type;
int error;
if (class) { // 如果dev->class 存在,则通过device_add_groups() 方法添加属性文件
error = device_add_groups(dev, class->dev_groups);
if (error)
return error;
}
if (type) {
error = device_add_groups(dev, type->groups); // 如果dev->type 存在,则通过device_add_groups() 方法添加属性文件
if (error)
goto err_remove_class_groups;
}
error = device_add_groups(dev, dev->groups); // 添加dev->groups 里的属性文件
if (error)
goto err_remove_type_groups;
if (device_supports_offline(dev) && !dev->offline_disabled) {
error = device_create_file(dev, &dev_attr_online); // 添加online 节点
if (error)
goto err_remove_dev_groups;
}
....
}
int device_add_groups(struct device *dev, const struct attribute_group **groups)
{
return sysfs_create_groups(&dev->kobj, groups);
}
最终会通过device_add_groups() 方法添加的属性节点,接着查看sysfs_create_groups() 是如何添加属性节点的
bsp/kernel/kernel4.14/fs/sysfs/group.c
int sysfs_create_groups(struct kobject *kobj,
const struct attribute_group **groups)
{
...
for (i = 0; groups[i]; i++) {
error = sysfs_create_group(kobj, groups[i]); // 通过for循环读取groups 里所有的属性,通过sysfs_create_group方法创建属性节点
if (error) {
while (--i >= 0)
sysfs_remove_group(kobj, groups[i]);
break;
}
}
return error;
}
int sysfs_create_group(struct kobject *kobj,
const struct attribute_group *grp)
{
return internal_create_group(kobj, 0, grp);
}
static int internal_create_group(struct kobject *kobj, int update,
const struct attribute_group *grp)
{
struct kernfs_node *kn;
....
if (grp->name) { // 当前没有传入name, 传入的grp 为power_supply_attr_group,当前为NULL
kn = kernfs_create_dir(kobj->sd, grp->name,
S_IRWXU | S_IRUGO | S_IXUGO, kobj);
if (IS_ERR(kn)) {
if (PTR_ERR(kn) == -EEXIST)
sysfs_warn_dup(kobj->sd, grp->name);
return PTR_ERR(kn);
}
} else
kn = kobj->sd;
kernfs_get(kn);
error = create_files(kn, kobj, grp, update); // 通过这个方法创建属性节点
if (error) {
if (grp->name)
kernfs_remove(kn);
}
kernfs_put(kn);
return error;
}
static int create_files(struct kernfs_node *parent, struct kobject *kobj,
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 (update)
kernfs_remove_by_name(parent, (*attr)->name);
if (grp->is_visible) {
mode = grp->is_visible(kobj, *attr, i); // 判断当前要创建的属性节点是否可见
if (!mode)
continue;
}
mode &= SYSFS_PREALLOC | 0664;
// 创建属性节点文件
error = sysfs_add_file_mode_ns(parent, *attr, false,
mode, NULL);
if (unlikely(error))
break;
}
if (error) {
remove_files(parent, grp);
goto exit;
}
}
...
}
最终会通过create_files() 方法创建属性节点。
bsp/kernel/kernel4.14/fs/sysfs/file.c
int sysfs_add_file_mode_ns(struct kernfs_node *parent,
const struct attribute *attr, bool is_bin,
umode_t mode, const void *ns)
{
struct lock_class_key *key = NULL;
const struct kernfs_ops *ops;
struct kernfs_node *kn;
loff_t size;
if (!is_bin) {
struct kobject *kobj = parent->priv;
const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops;
/* every kobject with an attribute needs a ktype assigned */
if (WARN(!sysfs_ops, KERN_ERR
"missing sysfs attribute operations for kobject: %s\n",
kobject_name(kobj)))
return -EINVAL;
// 关联每个文件的show 和store 方法
if (sysfs_ops->show && sysfs_ops->store) {
if (mode & SYSFS_PREALLOC)
ops = &sysfs_prealloc_kfops_rw;
else
ops = &sysfs_file_kfops_rw;
} else if (sysfs_ops->show) {
if (mode & SYSFS_PREALLOC)
ops = &sysfs_prealloc_kfops_ro;
else
ops = &sysfs_file_kfops_ro;
} else if (sysfs_ops->store) {
if (mode & SYSFS_PREALLOC)
ops = &sysfs_prealloc_kfops_wo;
else
ops = &sysfs_file_kfops_wo;
} else
ops = &sysfs_file_kfops_empty;
size = PAGE_SIZE;
} else {
...
#ifdef CONFIG_DEBUG_LOCK_ALLOC
if (!attr->ignore_lockdep)
key = attr->key ?: (struct lock_class_key *)&attr->skey;
#endif
// 创建属性节点文件
kn = __kernfs_create_file(parent, attr->name, mode & 0777, size, ops,
(void *)attr, ns, key);
if (IS_ERR(kn)) {
if (PTR_ERR(kn) == -EEXIST)
sysfs_warn_dup(parent, attr->name);
return PTR_ERR(kn);
}
return 0;
}
kobj->ktype->sysfs_ops 的内容初始化在power_supply_core.c::__power_supply_register() 方法中的device_initialize里
bsp/kernel/kernel4.14/drivers/base/core.c
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
INIT_LIST_HEAD(&dev->dma_pools);
mutex_init(&dev->mutex);
lockdep_set_novalidate_class(&dev->mutex);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_pm_init(dev);
set_dev_node(dev, -1);
#ifdef CONFIG_GENERIC_MSI_IRQ
INIT_LIST_HEAD(&dev->msi_list);
#endif
INIT_LIST_HEAD(&dev->links.consumers);
INIT_LIST_HEAD(&dev->links.suppliers);
dev->links.status = DL_DEV_NO_DRIVER;
}
static struct kobj_type device_ktype = {
.release = device_release,
.sysfs_ops = &dev_sysfs_ops,
.namespace = device_namespace,
};
static const struct sysfs_ops dev_sysfs_ops = {
.show = dev_attr_show,
.store = dev_attr_store,
};
static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct device_attribute *dev_attr = to_dev_attr(attr);
struct device *dev = kobj_to_dev(kobj);
ssize_t ret = -EIO;
if (dev_attr->show)
ret = dev_attr->show(dev, dev_attr, buf);
if (ret >= (ssize_t)PAGE_SIZE) {
print_symbol("dev_attr_show: %s returned bad count\n",
(unsigned long)dev_attr->show);
}
return ret;
}
static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct device_attribute *dev_attr = to_dev_attr(attr);
struct device *dev = kobj_to_dev(kobj);
ssize_t ret = -EIO;
if (dev_attr->store)
ret = dev_attr->store(dev, dev_attr, buf, count);
return ret;
}
查看kobject_init() 方法做了什么
bsp/kernel/kernel4.14/lib/kobject.c
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
char *err_str;
if (!kobj) {
err_str = "invalid kobject pointer!";
goto error;
}
if (!ktype) {
err_str = "must have a ktype to be initialized properly!\n";
goto error;
}
if (kobj->state_initialized) {
/* do not error out as sometimes we can recover */
printk(KERN_ERR "kobject (%p): tried to init an initialized "
"object, something is seriously wrong.\n", kobj);
dump_stack();
}
kobject_init_internal(kobj);
kobj->ktype = ktype;
return;
error:
printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
dump_stack();
}
么device_add_groups(dev, type->groups) 方法中的type->groups 是在哪儿初始化的呢,答案是在power_supply_sysfs.c 中做的初始化。
从驱动开始初始化的地方开始梳理:
bsp/kernel/kernel4.14/drivers/power/supply/power_supply_core.c
static struct device_type power_supply_dev_type;
static int __init power_supply_class_init(void)
{
power_supply_class = class_create(THIS_MODULE, "power_supply");
...
power_supply_init_attrs(&power_supply_dev_type);
return 0;
}
subsys_initcall(power_supply_class_init);
module_exit(power_supply_class_exit);
MODULE_DESCRIPTION("Universal power supply monitor class");
MODULE_AUTHOR("Ian Molton , "
"Szabolcs Gyurko, "
"Anton Vorontsov ");
MODULE_LICENSE("GPL");
该驱动入口是power_supply_class_init方法,在此方法中power_supply_init_attrs() 方法初始化了属性节点,查看此方法的实现
bsp/kernel/kernel4.14/drivers/power/supply/power_supply_sysfs.c
static struct device_attribute power_supply_attrs[];
#define POWER_SUPPLY_ATTR(_name) \
{ \
.attr = { .name = #_name }, \
.show = power_supply_show_property, \
.store = power_supply_store_property, \
}
static struct device_attribute power_supply_attrs[] = {
/* Properties of type `int' */
POWER_SUPPLY_ATTR(status),
POWER_SUPPLY_ATTR(charge_type),
POWER_SUPPLY_ATTR(health),
POWER_SUPPLY_ATTR(present),
POWER_SUPPLY_ATTR(online),
POWER_SUPPLY_ATTR(authentic),
POWER_SUPPLY_ATTR(technology),
POWER_SUPPLY_ATTR(cycle_count),
POWER_SUPPLY_ATTR(voltage_max),
...
}
static struct attribute * __power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1];
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,
};
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;
}
从上面的代码可以看出,power_supply_dev_type->groups 里面的内容就是power_supply_attrs里面的每个attr 组成的数组。
bsp/kernel/kernel4.14/drivers/power/supply/power_supply_core.c
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)
{
...
dev = &psy->dev;
...
dev->type = &power_supply_dev_type;
...
rc = device_add(dev);
...
}
在前面的说到的power_supply_register() 方法中可以看到,传入device_add() 方法中的dev 就是power_supply_dev_type。
所以说device_add_groups(dev, type->groups)方法中的type->groups 即是power_supply_attrs 数组中的每个attr组成的数组。
在power_supply_attrs 数组下有很多attr,但是在power_supply/battery 目录下有些并没有生成,这个是在如下方法控制的
bsp/kernel/kernel4.14/drivers/power/supply/power_supply_sysfs.c
static struct attribute_group power_supply_attr_group = {
.attrs = __power_supply_attrs,
.is_visible = power_supply_attr_is_visible,
};
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;
...
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;
}
is_visible() 方法可以控制当前哪些节点是可见的,从power_supply_attr_is_visible() 方法可以分析出,就是将power_supply_attrs 数组中的所有attrs 与 psy->desc->properties 进行比较,如果在psy->desc->properties 中有定义,则表示当前的attr 表示的节点是可见的。
我们查看battery 目录下的properties 的定义
bsp/kernel/kernel4.14/drivers/power/supply/charger-manager.c
static const struct power_supply_desc psy_default = {
.name = "battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = default_charger_props,
.num_properties = ARRAY_SIZE(default_charger_props),
.get_property = charger_get_property,
.set_property = charger_set_property,
.property_is_writeable = charger_property_is_writeable,
.no_thermal = true,
};
static enum power_supply_property default_charger_props[] = {
/* Guaranteed to provide */
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_AVG,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CAPACITY_RAW,
POWER_SUPPLY_PROP_VOLTAGE_OCV,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CURRENT_COUNTER_MBTK,
POWER_SUPPLY_PROP_SET_SHIP_MODE,
POWER_SUPPLY_PROP_TECHNOLOGY,
#ifdef CONFIG_VENDOR_POWER_VOTER
POWER_SUPPLY_PROP_CHARGING_ENABLED,
POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
#endif
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
};
static int charger_manager_probe(struct platform_device *pdev)
{
...
memcpy(&cm->charger_psy_desc, &psy_default, sizeof(psy_default));
...
cm->charger_psy = power_supply_register(&pdev->dev, &cm->charger_psy_desc, &psy_cfg);
...
}
所以,在battery 目录下会显示default_charger_props 数组里定义了的属性节点。