在linux中battery驱动主要是去处理供电方面的东西,大家看下driver在bsp中的路径就可以知道,android模拟器使用的goldfish内核中battery驱动的位置是:
android/common/drivers/power/goldfish_battery.c
目前手机,平板电脑日益普及,在嵌入式领域battery的续航能力也一直制约着手机等嵌入式设备的发展,iphone比android手机做的好多了,希望android可以再处理上下功夫,赶超apple,废话不多说,这里battery主要是处理,电池供电、插上充电器充电、USB供电等事情的发生,还有就是一些电池的信息管理,比如说电量、温度等状态可以使用户知道。
OK,这边我们主要是使用goldfish中的battery驱动来分析一下linux中的power模块是如何工作的。
在这之前我们首先要来看一下power_supply这个device driver 子系统是如何建立的,这边我们涉及到的代码都在/common/drivers/power/下:
power_supply_core.c
power_supply_sysfs.c
goldfish_battery.c
power_supply_core.c是power_supple subsystem的核心函数,在power_supply子系统在linux启动的时候会先调用到里面的饿init函数:
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; return 0; } subsys_initcall(power_supply_class_init);
power_supply_class->dev_uevent = power_supply_uevent;这句话把power_supply_uevent挂到power_supply_class的dev_uevent上,这里说明下,就是说power_supply子系统都是使用uevent机制把信息传到user space的,当battery的状态发生改变的时候会向用户空间上报一个uevent,这样的话用户空间就可以知道什么时候去抓信息。
这个power_supply_uevent是个回调函数,被定义在power_supply_sysfs.c中:
int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env) { struct power_supply *psy = dev_get_drvdata(dev); int ret = 0, j; char *prop_buf; char *attrname; dev_dbg(dev, "uevent\n"); if (!psy || !psy->dev) { dev_dbg(dev, "No power supply yet\n"); return ret; } dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name); ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->name); if (ret) return ret; prop_buf = (char *)get_zeroed_page(GFP_KERNEL); if (!prop_buf) return -ENOMEM; for (j = 0; j < ARRAY_SIZE(power_supply_static_attrs); j++) { struct device_attribute *attr; char *line; attr = &power_supply_static_attrs[j]; ret = power_supply_show_static_attrs(dev, attr, prop_buf); if (ret < 0) goto out; line = strchr(prop_buf, '\n'); if (line) *line = 0; attrname = kstruprdup(attr->attr.name, GFP_KERNEL); if (!attrname) { ret = -ENOMEM; goto out; } dev_dbg(dev, "Static prop %s=%s\n", attrname, prop_buf); ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf); kfree(attrname); if (ret) goto out; } dev_dbg(dev, "%zd dynamic props\n", psy->num_properties); 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) { /* 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; } if (ret < 0) goto out; line = strchr(prop_buf, '\n'); if (line) *line = 0; attrname = kstruprdup(attr->attr.name, GFP_KERNEL); if (!attrname) { ret = -ENOMEM; goto out; } dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf); ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf); kfree(attrname); if (ret) goto out; } out: free_page((unsigned long)prop_buf); return ret; }
我们抓重点,首先是ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->name);这句话添加了uevent的一些环境变量,这里添加了POWER_SUPPLY_NAME,接着都是些分配内存的代码,还有就是把power_supply_properties中注册的所有属性都添加成环境变量,这样的话当battery的某个属性发生变化的时候都会通报到上层让用户知道。
这里uevent我不做详细介绍,网上资料也一大堆,后面讲到jni层的时候我也会做补充用户层是如何去开启一个socket来抓kernel中的uevent的。
回到我们的power_supply_core.c中,在linux起来的时候只有调用到core中的class_init函数,其他的函数都是为我们的driver服务的,
现在我们来看下我们的goldfish_battery驱动,打开android/common/drivers/power/goldfish_battery.c,一般我喜欢先看init函数,
static int __init goldfish_battery_init(void) { return platform_driver_register(&goldfish_battery_device); }
arch/arm/mach-goldfish/pdev_bus.c
static void goldfish_pdev_worker(struct work_struct *work);
static uint32_t pdev_bus_base;
static uint32_t pdev_bus_irq;
static LIST_HEAD(pdev_bus_new_devices);
static LIST_HEAD(pdev_bus_registered_devices);
static LIST_HEAD(pdev_bus_removed_devices);
static DECLARE_WORK(pdev_bus_worker, goldfish_pdev_worker);
static void goldfish_pdev_worker(struct work_struct *work)
{
int ret;
struct pdev_bus_dev *pos, *n;
list_for_each_entry_safe(pos, n, &pdev_bus_removed_devices, list) {
list_del(&pos->list);
platform_device_unregister(&pos->pdev);
kfree(pos);
}
list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) {
list_del(&pos->list);
ret = platform_device_register(&pos->pdev);
if(ret) {
printk("goldfish_pdev_worker failed to register device, %s\n", pos->pdev.name);
}
else {
printk("goldfish_pdev_worker registered %s\n", pos->pdev.name);
}
list_add_tail(&pos->list, &pdev_bus_registered_devices);
}
}
---------------------------------------------------------------------------------
probe函数主要还是做一些初始化,probe的意思就是探测的意思,就是试着去初始化(我是这么理解的),
static int goldfish_battery_probe(struct platform_device *pdev) { int ret; struct resource *r; struct goldfish_battery_data *data; data = kzalloc(sizeof(*data), GFP_KERNEL); if (data == NULL) { ret = -ENOMEM; goto err_data_alloc_failed; } spin_lock_init(&data->lock); data->battery.properties = goldfish_battery_props; data->battery.num_properties = ARRAY_SIZE(goldfish_battery_props); data->battery.get_property = goldfish_battery_get_property; data->battery.name = "battery"; data->battery.type = POWER_SUPPLY_TYPE_BATTERY; data->ac.properties = goldfish_ac_props; data->ac.num_properties = ARRAY_SIZE(goldfish_ac_props); data->ac.get_property = goldfish_ac_get_property; data->ac.name = "ac"; data->ac.type = POWER_SUPPLY_TYPE_MAINS; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { printk(KERN_ERR "%s: platform_get_resource failed\n", pdev->name); ret = -ENODEV; goto err_no_io_base; } data->reg_base = IO_ADDRESS(r->start - IO_START); data->irq = platform_get_irq(pdev, 0); if (data->irq < 0) { printk(KERN_ERR "%s: platform_get_irq failed\n", pdev->name); ret = -ENODEV; goto err_no_irq; } ret = request_irq(data->irq, goldfish_battery_interrupt, IRQF_SHARED, pdev->name, data); if (ret) goto err_request_irq_failed; ret = power_supply_register(&pdev->dev, &data->ac); if (ret) goto err_ac_failed; ret = power_supply_register(&pdev->dev, &data->battery); if (ret) goto err_battery_failed; platform_set_drvdata(pdev, data); battery_data = data; GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK); return 0; err_battery_failed: power_supply_unregister(&data->ac); err_ac_failed: free_irq(data->irq, data); err_request_irq_failed: err_no_irq: err_no_io_base: kfree(data); err_data_alloc_failed: return ret; }
首先是goldfish_battery_data 这个结构体:
struct goldfish_battery_data { uint32_t reg_base; int irq; spinlock_t lock; struct power_supply battery; struct power_supply ac; };
这个结构体中我们关注的是power_supply这个结构体,别的没什么好说的,中断啊,寄存器地址啊,自旋锁什么的。
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; int (*get_property)(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val); void (*external_power_changed)(struct power_supply *psy); /* For APM emulation, think legacy userspace. */ int use_for_apm; /* private */ struct device *dev; struct work_struct changed_work; #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; #endif };
这部分我们对照着probe中的代码一起分析,主要还是填充这个结构体:
data->battery.properties = goldfish_battery_props; data->battery.num_properties = ARRAY_SIZE(goldfish_battery_props); data->battery.get_property = goldfish_battery_get_property; data->battery.name = "battery"; data->battery.type = POWER_SUPPLY_TYPE_BATTERY;
static enum power_supply_property goldfish_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CAPACITY, };
然后是属性的数目,不多说了,下面第三个参数比较重要 data->battery.get_property = goldfish_battery_get_property;
static int goldfish_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct goldfish_battery_data *data = container_of(psy, struct goldfish_battery_data, battery); int ret = 0; switch (psp) { case POWER_SUPPLY_PROP_STATUS: //modify charge to discharge //val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS); val->intval = POWER_SUPPLY_STATUS_DISCHARGING; //-----modify end break; case POWER_SUPPLY_PROP_HEALTH: val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH); break; case POWER_SUPPLY_PROP_PRESENT: val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT); break; case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = POWER_SUPPLY_TECHNOLOGY_LION; break; case POWER_SUPPLY_PROP_CAPACITY: //modify capacity from 50 per to 20 per by Jay //val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY); val->intval = global_brightness; //----modify end break; default: ret = -EINVAL; break; } return ret; }
大家注意到这里这个函数的参数,struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val
第一个参数是我们最重要的data struct,第二个参数是属性的类型,选择我们要得到的属性,第三个参数就是这个属性的值是多少,比如说电池电量是百分之多少。
这个函数是被挂在power_supply结构体中的get_property上的,它是在哪被回调呢?我们先在这里打个问号,我们知道这里只是在一些结构体中填充好了我们battery的信息,至于在什么时候回调,什么时候使用,什么时候上传uevent,我们后面会讲到,留意下!!
继续回到probe函数,下面是
data->battery.name = "battery"; data->battery.type = POWER_SUPPLY_TYPE_BATTERY;
下面就是跟硬件相关的东西了,一般的device是会用中断的方法去触发,这里goldfish的内核使用的是PC的中断资源。
data->irq = platform_get_irq(pdev, 0); if (data->irq < 0) { printk(KERN_ERR "%s: platform_get_irq failed\n", pdev->name); ret = -ENODEV; goto err_no_irq; } ret = request_irq(data->irq, goldfish_battery_interrupt, IRQF_SHARED, pdev->name, data);
ret = power_supply_register(&pdev->dev, &data->ac); if (ret) goto err_ac_failed; ret = power_supply_register(&pdev->dev, &data->battery); if (ret) goto err_battery_failed; platform_set_drvdata(pdev, data); battery_data = data;
int power_supply_register(struct device *parent, struct power_supply *psy) { int rc = 0; psy->dev = device_create(power_supply_class, parent, 0, psy, "%s", psy->name); if (IS_ERR(psy->dev)) { rc = PTR_ERR(psy->dev); goto dev_create_failed; } INIT_WORK(&psy->changed_work, power_supply_changed_work); rc = power_supply_create_attrs(psy); if (rc) goto create_attrs_failed; rc = power_supply_create_triggers(psy); if (rc) goto create_triggers_failed; power_supply_changed(psy); goto success; create_triggers_failed: power_supply_remove_attrs(psy); create_attrs_failed: device_unregister(psy->dev); dev_create_failed: success: return rc; }
然后是初始化了工作队列INIT_WORK(&psy->changed_work, power_supply_changed_work);
oK,我们来看下这个工作队列的回调函数,power_supply_changed_work
static void power_supply_changed_work(struct work_struct *work) { struct power_supply *psy = container_of(work, struct power_supply, changed_work); dev_dbg(psy->dev, "%s\n", __func__); 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); }
它遍历了我们power_supply_class上的所有节点,用psy作为参数调用了__power_supply_changed_work函数,__power_supply_changed_work函数的作用是匹配我们的驱动
static int __power_supply_changed_work(struct device *dev, void *data) { struct power_supply *psy = (struct power_supply *)data; struct power_supply *pst = dev_get_drvdata(dev); int i; for (i = 0; i < psy->num_supplicants; i++) if (!strcmp(psy->supplied_to[i], pst->name)) { if (pst->external_power_changed) pst->external_power_changed(pst); } return 0; }
所以说,调用到我们的工作队列的时候就会向用户空间上报event,这个动作被封装在
void power_supply_changed(struct power_supply *psy) { dev_dbg(psy->dev, "%s\n", __func__); schedule_work(&psy->changed_work); }
回到我们的power_supply_register函数中
rc = power_supply_create_attrs(psy);被定义在power_supply_sysfs.c中
int power_supply_create_attrs(struct power_supply *psy) { int rc = 0; int i, j; for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) { rc = device_create_file(psy->dev, &power_supply_static_attrs[i]); if (rc) goto statics_failed; } for (j = 0; j < psy->num_properties; j++) { rc = device_create_file(psy->dev, &power_supply_attrs[psy->properties[j]]); if (rc) goto dynamics_failed; } goto succeed; dynamics_failed: while (j--) device_remove_file(psy->dev, &power_supply_attrs[psy->properties[j]]); statics_failed: while (i--) device_remove_file(psy->dev, &power_supply_static_attrs[i]); succeed: return rc; }
这里介绍完毕,回到我们的goldfish驱动文件,最后就是我们的中断函数了。
static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id) { unsigned long irq_flags; struct goldfish_battery_data *data = dev_id; uint32_t status; spin_lock_irqsave(&data->lock, irq_flags); /* read status flags, which will clear the interrupt */ status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS); status &= BATTERY_INT_MASK; if (status & BATTERY_STATUS_CHANGED) power_supply_changed(&data->battery); if (status & AC_STATUS_CHANGED) power_supply_changed(&data->ac); spin_unlock_irqrestore(&data->lock, irq_flags); return status ? IRQ_HANDLED : IRQ_NONE; }
这里我什么也不看只想看一句话
if (status & BATTERY_STATUS_CHANGED) power_supply_changed(&data->battery); if (status & AC_STATUS_CHANGED) power_supply_changed(&data->ac);
之前讲过,就是power_supply_changed函数触发了向用户空间上报event事件的代码。OK ,android goldfish battery驱动就讲到这边,我看android模拟器的电池老是动啊动的很是不爽所以我在get_property函数中充电状态改成了不是充电状态,然后把电池电量设置为恒定的,嘿嘿,这样的话就不会动了,哈哈。下面贴张图,有图有真相
下面一篇我们会讲到android framework中jni对驱动的封装,好累啊,一口气写完battery driver分析。