LInux 锂电池驱动分析

锂电池的驱动程序要实现以 下五个功能:

1.可以自动检测到当前给电 池充电的是USB还 是AC

2.组织过大的充电电流

3.坏电池检测

4.死亡温度的检测

5.电池电压的测量

 

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

struct power_supply {

const char *name;

enum power_supply_type  type;

enum power_supply_property  *properties ;//声明了电源的属性

size_t num_propertie s ;

 

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

void (*set_charged )(struct power_supply  *psy);

 



int use_for_apm;

 



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

};

 

内核主要通过get_property 这个函数指 针来获得驱动中的 有关电池的信息,而这个函数在内核中只给出了其声明,我们在写驱动的时候要 自己实现这 个函数,即讲自 己写 函数 值给函数指针,当内核需要 驱动中的电源的信息的时候,回调这 个get_pr oper ty即 可。另外,我 们写驱 动程序又要给用 户提供接口,内核中的提供给用户的接口即sysfs,通过读取其中的属性就可以得到电源的信息。内核主要通过两个文件power_supply_class.c和power_supply_core.c,我们调用其中的函数就可以把电源(电池,USB power supply 或者AC power supply)的信息展现给用户,有关电源的属性写在/sys/class /powersupp ly文件夹下。

 

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

 

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

static int __init stmp3xxx_bat_init(void)

{

return platform_driver_register( &stmp3xxx_batdrv);

}

这个函数执行platform_ driver_r egi ster( &stmp3xxx_batdrv);并将返回值返回。而platform_drive r_register ()是一个内核函数,它在内核中如下定义:

int platform_driver_regist er(struct platform_driver *drv)

{

drv->driver.bus = &platform_bus_ty pe;

if (drv->probe)

drv->driver.probe = platform_drv_probe;//platform_drv_probe仍然是一个内核函数,、

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

if (drv->remove)

drv->driver.remove = platform_drv_remove;

if (drv->shutdown)

drv->driver.shutdown = platform_drv_shutdown;

 

return driver_register(&drv->driver);

}

这个函数所完成的就是:首先将platfo rm_driver的结 构体变量  driver的bus域 初始化,然后将platform_driver的driver的函数指针probe等初始 化为pla tform_driver 的 probe(如何完成的请看上面代码中给出的注释).然后执行driver_register(&drv->driver)(我 们一会再分析driver_register(&drv->driver))。

 

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

static struct platform_driver stmp3xxx_batdrv = {

.probe = stmp3xxx_bat_probe,

.remove = stmp3xxx_bat_remove,

.shutdown             = stmp3xxx_bat_shutdown,

.suspend = stmp3xxx_bat_suspend,

.resume = stmp3xxx_bat_resume,

.driver = {

.name = ”stmp3xxx-battery”,

.owner = THIS_MODULE,

},

};

 

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

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

struct resource *vdd5v_irq;

info->vdd5v_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

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

struct resource {

resource_size_t start;

resource_size_t end;

const char *name;

unsigned long flags;

struct resource *parent, *sibling, *child;

};
下面举s3c2410平台的i2c驱动作为例子来说明:


static struct resource s3c_i2c_resource[] = {
                 [0] = {
                                     .start = S3C24XX_PA_IIC,
                                     .end = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,
                                     .flags = IORESOURCE_MEM,
                 },
                 [1] = {
                                     .start = IRQ_IIC, //S3C2410_IRQ(27)
                                     .end = IRQ_IIC,
                                     .flags = IORESOURCE_IRQ,
                 }
};

这里定义了两组resource,它描述了 一个I2C设备的资源,第1组描述了这个I2C设备所占用的总线地址范围,IORESOURCE_MEM表示第1组描述的是内存类型的资源信 息,第2组描述了这个I2C设备的中断号,IORESOURCE_IRQ表示第2组描述的是中断资源信息。设备驱动会根据f lags来获取相应的资源信息。

 

 

保存指向驱动特有信息的指针:platform_set_drvdata(pdev, info);

 

对电源进行初始化,代码如下:

info->bat.name                     = ”battery”;//名字

info->bat.type                     = POWER_SUPPLY_TYPE_BATTERY;//类型

info->ba t.properties         = stmp3xxx_bat_props;//属性

info->bat.num_prope rties = ARRAY_SIZE(stmp3xxx_bat_props);//属性的个数

info->bat.get_property     = stmp3xxx_bat_get_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);

 

接下来将三 种电源注册,即把他们的 属性写到s ys文件系统里, 以使用户空间可以得到有关 电源的信息,以其中的一个为例:

ret = power_supply_register(&pdev->dev, &info->bat);//将电池注册

power_supply_register调用内核提供 的函数device_create( )和pow er_supply_create_attr s来实现电池的注册。

这里只写出了驱动要完成的基本功 能,至于如何完成的, 即驱动与硬件 之间的交 互,对寄存器的操作,需要参考具体的硬件手册。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值