Linux|Android 电池管理系统-驱动部分



硬件平台:RK3128
Android版本:6.0

Kernel版本:3.10.92
Android电池管理,基于linux Power Supply架构,Battery驱动程序需要通过sys文件系统向用户空间提供接口,Linux标准的Power Supply驱动程序所使用的文件系统路径问/sys/class/power_supply,其中的每个子目录表示一种能源供应设备的名称。
一、Power Supply初始化
先来看看power_supply结构体,定义在include/linux/power_supply.h中。

struct power_supply { const char *name; /*名称*/ enum power_supply_type type; /*类型*/ enum power_supply_property *properties; /*属性*/ size_t num_properties; /*属性数目*/ char **supplied_to; size_t num_supplicants; char **supplied_from; size_t num_supplies; #ifdef CONFIG_OF struct device_node *of_node; #endif int (*get_property)(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val); int (*set_property)(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val); int (*property_is_writeable)(struct power_supply *psy, enum power_supply_property psp); void (*external_power_changed)(struct power_supply *psy); void (*set_charged)(struct power_supply *psy); /* For APM emulation, think legacy userspace. */ int use_for_apm; /* private */ struct device *dev; struct work_struct changed_work; spinlock_t changed_lock; bool changed; #ifdef CONFIG_THERMAL struct thermal_zone_device *tzd; struct thermal_cooling_device *tcd; #endif#ifdef CONFIG_LEDS_TRIGGERS struct led_trigger *charging_full_trig; char *charging_full_trig_name; struct led_trigger *charging_trig; char *charging_trig_name; struct led_trigger *full_trig; char *full_trig_name; struct led_trigger *online_trig; char *online_trig_name; struct led_trigger *charging_blink_full_solid_trig; char *charging_blink_full_solid_trig_name; #endif };

初始化:
drivers/power/power_supply_core.c
static int __init power_supply_class_init(void) { power_supply_class = class_create(THIS_MODULE, "power_supply"); if (IS_ERR(power_supply_class)) return PTR_ERR(power_supply_class); power_supply_class->dev_uevent = power_supply_uevent; power_supply_init_attrs(&power_supply_dev_type); return 0; }

可以看到创建了class power_supply,也就是/sys/class/power_supply,接着绑定了dev_uevent,dev_uevent是结构体class的一个函数指针,定义在include/linux/device.h中,看看源码中的描述:
* @dev_uevent: Called when a device is added, removed from this class, or a *few other things that generate uevents to add the environment *variables.

也就是说每添加一个设备到power_supply中,就会调用一次power_supply_uevent。
注册:
int power_supply_register(struct device *parent, struct power_supply *psy) { struct device *dev; int rc; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; device_initialize(dev); dev->class = power_supply_class; dev->type = &power_supply_dev_type; dev->parent = parent; dev->release = power_supply_dev_release; dev_set_drvdata(dev, psy); psy->dev = dev; INIT_WORK(&psy->changed_work, power_supply_changed_work); rc = power_supply_check_supplies(psy); if (rc) { dev_info(dev, "Not all required supplies found, defer probe\n"); goto check_supplies_failed; } rc = kobject_set_name(&dev->kobj, "%s", psy->name); if (rc) goto kobject_set_name_failed; rc = device_add(dev); if (rc) goto device_add_failed; spin_lock_init(&psy->changed_lock); rc = device_init_wakeup(dev, true); if (rc) goto wakeup_init_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); if (rc) goto create_triggers_failed; power_supply_changed(psy); goto success; create_triggers_failed: psy_unregister_cooler(psy); register_cooler_failed: psy_unregister_thermal(psy); register_thermal_failed: wakeup_init_failed: device_del(dev); kobject_set_name_failed: device_add_failed: check_supplies_failed: put_device(dev); success: return rc; }


再看看drivers/power/power_supply_sysfs.c
/* 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(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), POWER_SUPPLY_ATTR(voltage_min), POWER_SUPPLY_ATTR(voltage_max_design), POWER_SUPPLY_ATTR(voltage_min_design), ... ...

这里是所有的电源设备属性,但并不需要全部创建,需要创建哪些由我们注册时指定,把需要创建的属性放在properties中即可,在power_supply_uevent中很容易可以看到:
for (j = 0; j < psy->num_properties; j++) { struct device_attribute *attr; char *line; attr = &power_supply_attrs[psy->properties[j]]; ret = power_supply_show_property(dev, attr, prop_buf); if (ret == -ENODEV || ret == -ENODATA) { /* When a battery is absent, we expect -ENODEV. Don't abort; send the uevent with at least the the PRESENT=0 property */ ret = 0; continue; }

psy即我们传入的power_supply。

二、Battery driver
Battery driver通过power_supply_register注册到Power Supply core中,来看看源码:

data ->bat = rk30_battery_supply; ret = power_supply_register(&pdev->dev,&data ->bat);

【Linux|Android 电池管理系统-驱动部分】rk30_battery_supply:
static enum power_supply_property rk30_adc_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, }; static struct power_supply rk30_battery_supply = { .name = "battery", .type = POWER_SUPPLY_TYPE_BATTERY, .get_property= rk30_adc_battery_get_property, .properties= rk30_adc_battery_props, .num_properties = ARRAY_SIZE(rk30_adc_battery_props), };

rk30_adc_battery_props即指定了需要创建的节点列表。到这里Battery驱动就创建完成了。


三、电量更新
Android电量更新是通过uevent通知上层,battery driver会不断计算当前电量,当电量发生变化后,会调用power_supply_changed(&bat ->bat);
void power_supply_changed(struct power_supply *psy) { unsigned long flags; dev_dbg(psy->dev, "%s\n", __func__); spin_lock_irqsave(&psy->changed_lock, flags); psy->changed = true; pm_stay_awake(psy->dev); spin_unlock_irqrestore(&psy->changed_lock, flags); schedule_work(&psy->changed_work); }

changed_work:
static void power_supply_changed_work(struct work_struct *work) { unsigned long flags; struct power_supply *psy = container_of(work, struct power_supply, changed_work); dev_dbg(psy->dev, "%s\n", __func__); spin_lock_irqsave(&psy->changed_lock, flags); if (psy->changed) { psy->changed = false; spin_unlock_irqrestore(&psy->changed_lock, flags); class_for_each_device(power_supply_class, NULL, psy, __power_supply_changed_work); power_supply_update_leds(psy); kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); spin_lock_irqsave(&psy->changed_lock, flags); } if (!psy->changed) pm_relax(psy->dev); spin_unlock_irqrestore(&psy->changed_lock, flags); }

可以看到changed_work中会调用kobject_uevent,其后流程如下:
kobject_uevent ->

kobject_uevent_env->

netlink_broadcast_filtered

通过netlink机制发送给framework用以更新电量,关于framework的部分会在下篇介绍。

    推荐阅读