Android电池驱动

学习要点:1、电池驱动的架构;

2、电池电压的获取,百分比的转换(包括不同用电情况下的分析);

3、充电管理;

当我们要写一个驱动的时候,首先要知道内核提供给驱动的接口,就是当驱动挂载到内核上的时候,内核怎么知道驱动中的信息的,如何来控制驱动。而内核提供给电池驱动的接口就是结构体power_supply。

Battery驱动程序需要通过sys文件系统向用户空间提供接口,sys文件系统的路径是由上层的程序指定的。

Linux标准的Power Supply驱动程序所使用的文件系统路径问/sys/class/power_supply,其中的每个子目录表示一种能源供应设备的名称。

Power Supply驱动程序的头文件在include/linux/power_supply.h中定义,注册和注销驱动程序的函数如下所示:

int power_supply_register(struct device *parent,struct power_supply *psy);

void power_supply_unregister(struct power_supply *psy);

其中power_supply结构体为驱动程序需要实现的部分,

struct power_supply{

   const chat *name;/*设备名字*/

   enum power_supply_type type;/*类型*/

   enum power_supply_property *properties;/*属性指针*/

  size_t num_properties;/*属性数目*/

  int (*get_property)(struct power_supply *psy,/*获得属性*/

                                enum power_supply_property psp,

                                union power_supply_propval *val);

  .........../*省略部分,其他属性可以查看include/linux/power_supply.h中struct power_supply*/

}

内核主要通过get_property这个函数指针来获得驱动中的有关电池的信息,而这个函数在内核中只给出了声明,我们在写驱动的时候要自己实现这个函数,即将自己写的函数赋值给这个函数指针,当内核需要驱动中电源信息的时候就回调这个get_property函数。另外,我们写驱动程序的时候又要给用户提供接口,内核中提供给用户的接口就是sysfs,通过读取sysfs文件系统中文件内容,就可以得到电源的信息。内核主要通过两个文件power_supply_class.c和power_supply_core.c,我们调用其中的函数就可以把电源(电池,USB或AC)的信息展现给用户,有关电源的属性写在/sys/class/powersupply文件夹下(此文件夹为程序运行后所生成的)。

这样,按照内核提供的接口,驱动程序的书写就很清晰了,结合电池的驱动程序代码,我们来看看驱动程序的执行过程。

当一个驱动被编译好并挂到内核上之后,会首先执行一个模块的初始化函数,每个驱动都是统一的,在这里是module_init(cvt_mid_power_init);它代表首先执行cvt_mid_power_init这个函数,在驱动里它是这么定义的:

static int __init cvt_mid_power_init(void)
{
 int i;
 int ret;

 for (i = 0; i < ARRAY_SIZE(cvt_mid_power_supplies); i++) {
  ret = power_supply_register(NULL, &cvt_mid_power_supplies[i]);
  if (ret) {
   pr_err("%s: failed to register %s\n", __func__,
    cvt_mid_power_supplies[i].name);
   goto failed;
  }
 }
 platform_device_register(&cvt_mid_power_dev);
 return platform_driver_register(&cvt_mid_power_driver);

failed:
 while (--i >= 0)
  power_supply_unregister(&cvt_mid_power_supplies[i]);
 return ret;
}

首先是通过power_supply_register将cvt_mid_power_supplis[]数组中所提供的电源进行注册,即把他们的属性写到sys文件系统里,以使用户空间可以得到有关电源的信息,

power_supply_register调用内核提供的函数device_create()和power_supply_create_attrs来实现电池的注册.

这个函数执行platform_driver_register(&cvt_mid_driver);并将返回值返回。而platform_driver_register()是一个内核函数,它在内核中如下定义:

int platform_driver_register(struct platform_driver *drv)

{

    drv->driver.bus = &platform_bus_type;

    if(drv->probe)

        drv->driver.probe = platform_drv_probe;//platform_drv_probe仍然是一个内核函数,他在内核里定义如下:

  /*

      static int platform_drv_probe(struct device *_dev)

     {

            struct paltform_driver *drv = to_platform_driver(_dev->driver);

           struct  platform_device *dev = to_platform_device(_dev); 

           return drv->probe(dev);

     }

  */

//上面的函数作用就是将device的driver转变成platform_drive。

    if(drv->remove)

          drv->driver.remove = platform_drv_remove;

    if(drv->shutdown)

           drv->driver.shutdown = platform_drv_shutdown;

    return driver_register(&drv->driver);

}

这个函数的功能就是:首先将platform_driver的结构体变量driver的bus域初始化,然后将platform_driver的函数指针probe等初始化为platform_driver的probe。然后执行driver_register(&drv->driver).

platform_driver_register()在我们驱动中,他的参数是一个结构体指针&cvt_mid_power_driver,在我们的驱动里它是如下定义的:

static struct platform_driver cvt_mid_power_driver = {
 .probe = cvt_mid_power_probe,
 .remove = __devexit_p(cvt_mid_power_remove),
 #ifdef CONFIG_PM
 .suspend = NULL,
 .resume = NULL,
 #endif
 .driver = {
  .name = "cvt_mid_power",
  .pm = &cvt_mid_battery_pm_ops,
 },
};

下面讲下driver_register(&drv->driver),在这里就不贴出其中的代码了,比较复杂,可以用Source Insighe跟踪其中的调用过程,在这里我就大致的介绍下它的主要过程,一些不重要的东西就省略掉,首先它会遍历在BUS上的所有设备,通过比较设备的名字和驱动的名字来进行匹配,如果名字相同才能注册成功,当注册成功后接下来就会调用platform_driver 结构中的probe函数指针,在这里就是cvt_mid_power_probe,其函数原型static int cvr_mid_power_probe(struct platform_device *pdev),而此时cvt_mid_power_probe的参数就是我们在总线上找到的和驱动相匹配的设备,它是在驱动注册的时候,找到和驱动匹配的设备后给pdev初始化的。

下面我们说下cvt_mid_power_probe所完成的主要功能:获取电源设备的中断资源,代码实现如下:

static __devinit int cvt_mid_power_probe(struct platform_device *pdev)
{
 int irq;
 int irq_flag;
 int i;
 int ret;

 g_adc_client = adc_register(0, adc_battery_callback, NULL);
 dbg_printk("%s,%d,g_adc_client=%d\n", __func__, __LINE__, (int)g_adc_client);
 if(g_adc_client == 0)
 {
  printk("cvt_mid_power_probe error: can not registe adc!\n");
  return -1;
 }
 gpio_request(CHG_OK_PIN, NULL);
 gpio_pull_updown(CHG_OK_PIN, GPIOPullUp);
 gpio_direction_input(CHG_OK_PIN);

 gpio_request(DC_DET_PIN, NULL);
 gpio_pull_updown(DC_DET_PIN, GPIOPullUp);
 gpio_direction_input(DC_DET_PIN);

 g_workqueue = create_singlethread_workqueue("cvt_mid_power_adcwq");
 INIT_WORK(&g_work, adc_work_func);
 alarm_init(&g_alarm, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP, cvt_mid_battery_alarm);
 wake_lock_init(&g_work_wake_lock, WAKE_LOCK_SUSPEND, "cvt-mid-charger");
 initFilter(batteryCapcityFilterBuffer, FILTER_DEPTH);
 for(i = 0; i < TABLE_SIZE; i ++)
 {
  //For 3.7V battery, but use 7.4V battery table...
  battery_step_table[i] /= 2;
  battery_charging_step_table[i] /= 2;
 }
 for(i = 0; i < FILTER_DEPTH; i ++)
 {
  va7882_cal_battery_capacity();
 }
 wake_lock(&g_work_wake_lock);
 queue_work(g_workqueue, &g_work);
 cvt_mid_program_alarm(1);

 irq = gpio_to_irq(DC_DET_PIN);
 irq_flag = gpio_get_value (DC_DET_PIN) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
// irq_flag = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
 ret = request_irq(irq, battery_dc_wakeup, irq_flag, "ac_charge_irq", NULL);
 if (ret) {
  printk("failed to request dc det irq\n");
 }
 enable_irq_wake(irq);
 return 0;
}

下面说下resource,该元素存入了最为重要的设备资源信息,例如设备的地址,中断号等,其定义如下:struct resource{

      resource_size_t start;

     resource_size_t end;

     const char *name;

    unsigned long flags;

    struct resource *parent,*sibling,*child;

};

保存有电池驱动特有信息的指针:cvt_mid_power_supplies;

对电源进行初始化:

static struct power_supply cvt_mid_power_supplies[] = {
 {
  .name = "ac",//设备名称
  .type = POWER_SUPPLY_TYPE_MAINS,//类型
  .supplied_to = cvt_mid_power_ac_supplied_to,//供电电池
  .num_supplicants = ARRAY_SIZE(cvt_mid_power_ac_supplied_to),//供电电池个数
  .properties = cvt_mid_power_ac_props,//属性
  .num_properties = ARRAY_SIZE(cvt_mid_power_ac_props),//属性数目
  .get_property = cvt_mid_power_get_ac_property,//得到属性的函数
 }, {
  .name = "battery",
  .type = POWER_SUPPLY_TYPE_BATTERY,
  .properties = cvt_mid_power_battery_props,
  .num_properties = ARRAY_SIZE(cvt_mid_power_battery_props),
  .get_property = cvt_mid_power_get_battery_property,
 },
};

这里主要是实现给电源名字类型等赋初值,最主要是将get_property函数指向我们写好的可以得到电源的属性的函数的起始地址,以便当内核需要用到驱动的信息的时候进行回调。

接下来初始化timer,mutex,代码如下:

init_timer(&info->sm_timer);

info->sm_timer.data = (unsigned long)info;

info->sm_timer.function = state_machine_timer;

mutex_init(&info->sm_lock);

 

你可能感兴趣的:(android,linux,struct,能源)