Android 内核学习之三-----Power源码分析学习(2)
4. goldfish_battry驱动学习
goldfish_battry驱动也在下图所示的目录中,即goldfish_battry.c。该驱动是模拟器下的电池驱动。
(1) init
static int __init goldfish_battery_init(void)
{
returnplatform_driver_register(&goldfish_battery_device);
}
驱动模块加载入口,但是找不到driver_register这个函数,在网上看到了一个高手给出了,改函数的操作在,arch/arm/mach-goldfish/pdev_bus.c文件中。
(2) arch/arm/mach-goldfish/pdev_bus.c
=========》》static void goldfish_pdev_worker(structwork_struct *work)
=========》》static int __devinitgoldfish_pdev_bus_probe(struct platform_device *pdev)
在第一个方法中调用了platform_device_register(&pos->pdev);,然后执行probe函数。(这个地方看别人这么分析,但是还不是很明白,不知道是不是电源的驱动模块加载也有一个特定的机制,类似power_supply)
(3) probe
probe是探头探针的意思,这里应该代表着最先运行的意思了吧。Probe对应的函数是goldfish_battery.c文件中的static int goldfish_battery_probe(structplatform_device *pdev)方法。该函数的作用就是初始化goldfish_battery_data结构体,改结构体如下:
struct goldfish_battery_data {
void__iomem *reg_base;
int irq;
spinlock_tlock;
structpower_supply battery;
structpower_supply ac;
};
主要是对其中的power_supply的两个结构体进行填充,首先,将battery 的name设置为”battery “,将ac的那么设置为ac,这个那么就是对应的上一节中的ret = add_uevent_var(env,"POWER_SUPPLY_NAME=%s", psy->name);下对应的psy->name。然后将power_supply_property设置为goldfish_battery_props,而goldfish_battery_props是一个enum,其定义如下。这个值就是上一节的循环里面将要往用户空间传递数据的的属性。这里的goldfish_battery_props就是对应的battery一起往上传递的属性值。
1. static enum power_supply_property goldfish_battery_props[] = {
2. POWER_SUPPLY_PROP_STATUS,
3. POWER_SUPPLY_PROP_HEALTH,
4. POWER_SUPPLY_PROP_PRESENT,
5. POWER_SUPPLY_PROP_TECHNOLOGY,
6. POWER_SUPPLY_PROP_CAPACITY,
7. };
通过上述分析,我们可以知道如果用的是battery的时候,上一节降到的power_supply_uevent回调函数将会上传的字段是(其中valueX对应的是具体的值):
POWER_SUPPLY_NAME=battery
POWER_SUPPLY_PROP_STATUS=valueX
POWER_SUPPLY_PROP_HEALTH= valueX
POWER_SUPPLY_PROP_PRESENT= valueX
POWER_SUPPLY_PROP_TECHNOLOGY= valueX
POWER_SUPPLY_PROP_CAPACITY= valueX
初始化完成以后,probe的后面有一个很重要的方法:power_supply_register(&pdev->dev, &data->ac);该方法的定义在power_supply_core.c中。
(4) power_supply_register
int power_supply_register(struct device *parent, structpower_supply *psy)
{
structdevice *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 =kobject_set_name(&dev->kobj, "%s", psy->name);
if (rc)
gotokobject_set_name_failed;
rc =device_add(dev);
if (rc)
gotodevice_add_failed;
spin_lock_init(&psy->changed_lock);
rc =device_init_wakeup(dev, true);
if (rc)
gotowakeup_init_failed;
rc =power_supply_create_triggers(psy);
if (rc)
gotocreate_triggers_failed;
power_supply_changed(psy);
gotosuccess;
create_triggers_failed:
wakeup_init_failed:
device_del(dev);
kobject_set_name_failed:
device_add_failed:
put_device(dev);
success:
return rc;
}
该方法中首先填充dev,将dev的class,type,release等属性填充上去,然后执行INIT_WORK(struct,func);该函数将func的操作放到任务队列中,等待执行。因此上述代码中的func就是power_supply_changed_work()。power_supply_changed_work方法定义在power_supply_core.c中。最后函数会执行power_supply_changed()方法,power_supply_changed方法也定义在power_supply_core.c,该方法就是具体调度执行INIT_WORK中加入到任务队列的func,在这里就是执行power_supply_changed_work函数。
(5) power_supply_changed_work()
上文讲到该方法定义在power_supply_core.c中,现在因为一个方法不是很明白,将其拿出来作为单独的一节,作为记录,以便以后深入学习。
static voidpower_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);
}
其中kobject_uevent便是将上文书说道的add_uevent中的值通过socket传到用户控件,这是uevent驱动的一个核心作用,就是通过socket将内核态的消息传递到用户空间中,这个也是很多其他进程间消息传递机制所不具备的能力。
这里主要看下class_for_each_device(power_supply_class,NULL, psy,__power_supply_changed_work);是对class的设备链表上的每个设备调用指定的函数。这个函数就是__power_supply_changed_work,定义在power_supply_core.c下。
(6) __power_supply_changed_work(此处有不理解的地方了)???
static int __power_supply_changed_work(struct device*dev, void *data)
{
structpower_supply *psy = (struct power_supply *)data;
structpower_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;
}
该方法主要是执行external_power_changed,但是有两个地方没搞懂,一个是循环语句中的psy->num_supplicants,num_supplicants是一个什么值,什么时候初始化的,二是external_power_changed是在那里定义的,在什么时候挂载到pst所代表的power_supply结构体下的。
(7) 小结
到这里算是简单的吧电池驱动的部分讲完了,大体可以了解了电池驱动是怎么讲电池的一些信息发送到用户控件的,即当电池信息发生变化以后就通过uevent机制将消息传送到用户控件的sys/classs/power_supply下,然后由用户控件的程序去获取变化的信息。当然还有不少细节没有涉及到,也没有搞明白,这个还需要很多时间去学习实践。下面将进入到用户控件看用户控件是怎么获取到底层驱动发送上来的数据的。
继续努力,Keep moving!!!