展锐power_supply 目录下属性节点生成流程

相关文章

介绍

在展锐的系统目录下可以看到如下几个目录

  • sys/class/power_supply/ac   外部充电器充电相关
  • sys/class/power_supply/usb  usb充电相关
  • sys/class/power_supply/battery  电池状态相关

当设备充电时,会显示充电图标和充电动画, 整个充电状态的显示其实是通过读取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 目录和其下各个属性节点的创建流程。

1. 创建ac、usb、battery 等目录

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);
    ...
}

2. sys/class/power_supply/battery目录创建流程

这里以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();
}

3. type->groups 初始化流程

么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 <spyro@f2s.com>, "
          "Szabolcs Gyurko, "
          "Anton Vorontsov <cbou@mail.ru>");
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组成的数组。

4. 控制节点是否显示流程

在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 数组里定义了的属性节点。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`power_supply_get_property`、`power_supply_set_property`、`power_supply_property_is_writeable` 是 Linux 内核中与电源管理相关的函数,主要用于获取和设置电源供应设备的属性值。下面分别对这三个函数进行详细说明: 1. `power_supply_get_property` 函数 `power_supply_get_property` 函数用于获取电源供应设备的属性值。其函数原型为: ``` int power_supply_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val); ``` 其中,`psy` 是指向 `struct power_supply` 结构体的指针,用于指定要获取属性的电源供应设备;`psp` 是一个枚举类型的值,用于指定要获取的属性名称;`val` 是一个 `union` 类型的变量,用于存储获取到的属性值。 使用 `power_supply_get_property` 函数时,需要先定义一个 `struct power_supply` 类型的变量,并将其指针传递给函数。同时需要指定要获取的属性名称,可以通过 `enum power_supply_property` 枚举类型来指定。获取到的属性值会被存储在 `union power_supply_propval` 变量中。这个变量包含了多种类型的属性值,可以根据属性的类型来获取相应的值。 2. `power_supply_set_property` 函数 `power_supply_set_property` 函数用于设置电源供应设备的属性值。其函数原型为: ``` int power_supply_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val); ``` 其中,`psy` 是指向 `struct power_supply` 结构体的指针,用于指定要设置属性的电源供应设备;`psp` 是一个枚举类型的值,用于指定要设置的属性名称;`val` 是一个 `const union` 类型的指针,用于指定要设置的属性值。 使用 `power_supply_set_property` 函数时,需要先定义一个 `struct power_supply` 类型的变量,并将其指针传递给函数。同时需要指定要设置的属性名称和属性值,可以通过 `enum power_supply_property` 枚举类型和 `union power_supply_propval` 变量来指定。需要注意的是,某些属性可能是只读的,不能通过这个函数来设置属性值。 3. `power_supply_property_is_writeable` 函数 `power_supply_property_is_writeable` 函数用于判断电源供应设备的某个属性是否可写。其函数原型为: ``` int power_supply_property_is_writeable(struct power_supply *psy, enum power_supply_property psp); ``` 其中,`psy` 是指向 `struct power_supply` 结构体的指针,用于指定要查询的电源供应设备;`psp` 是一个枚举类型的值,用于指定要查询的属性名称。 使用 `power_supply_property_is_writeable` 函数时,需要先定义一个 `struct power_supply` 类型的变量,并将其指针传递给函数。同时需要指定要查询的属性名称,可以通过 `enum power_supply_property` 枚举类型来指定。函数会返回一个整型值,表示该属性是否可写。如果可写,则返回 1,否则返回 0。 这三个函数是 Linux 内核中电源管理的重要函数,可以用于获取和设置电源供应设备的属性值,帮助开发人员进行电源管理的相关工作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值