LInux 锂电池驱动分析
2014-01-22 14:12
141 查看
锂电池的驱动程序要实现以下五个功能: 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_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); 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_property即可。另外,我们写驱动程序又要给用户提供接口,内核中的提供给用户的接口即sysfs,通过读取其中的属性就可以得到电源的信息。内核主要通过两个文件power_supply_class.c和power_supply_core.c,我们调用其中的函数就可以把电源(电池,USB power supply或者AC power supply)的信息展现给用户,有关电源的属性写在/sys/class /powersupply文件夹下。 这样,按照内核提供的接口,驱动程序的书写就很清晰了,结合锂电池的驱动程序的源代码,我们来看看驱动程序的执行过程。 当一个驱动被编译好并被挂到内核上之后,会首先执行一个模块的初始化函数,每个驱动都是统一的,在这里是module_init(stmp3xxx_bat_init);它代表首先执行stmp3xxx_bat_init,在驱动里它是这么定义的: static int __init stmp3xxx_bat_init(void) { return platform_driver_register(&stmp3xxx_batdrv); } 这个函数执行platform_driver_register(&stmp3xxx_batdrv);并将返回值返回。而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仍然是一个内核函数,、 //上面的函数的作用就是将device的driver转变成platform_driver。 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的driver的函数指针probe等初始化为platform_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上的所有设备,通过比较设备的名字和驱动的名字来进行匹配,如果名字相同才能注册成功,当注册成功后接下来会调用platform_driver结构中probe函数指针,在这里就是stmp3xxx_bat_probe,其函数原型 static int stmp3xxx_bat_probe(struct platform_device *pdev),而此时 stmp3xxx_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组描述的是中断资源信息。设备驱动会根据flags来获取相应的资源信息。 保存指向驱动特有信息的指针:platform_set_drvdata(pdev, info); 对电源进行初始化,代码如下: info->bat.name = ”battery”;//名字 info->bat.type = POWER_SUPPLY_TYPE_BATTERY;//类型 info->bat.properties = stmp3xxx_bat_props;//属性 info->bat.num_properties = 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); 接下来将三种电源注册,即把他们的属性写到sys文件系统里,以使用户空间可以得到有关电源的信息,以其中的一个为例: ret = power_supply_register(&pdev->dev, &info->bat);//将电池注册 power_supply_register调用内核提供的函数device_create()和power_supply_create_attrs来实现电池的注册。 这里只写出了驱动要完成的基本功能,至于如何完成的,即驱动与硬件之间的交互,对寄存器的操作,需要参考具体的硬件手册。
相关文章推荐
- LInux 锂电池驱动分析
- Linux下读写FLASH驱动——MTD设备分析
- OMAP3630 Linux I2C总线驱动分析
- ARM-Linux驱动-触摸屏驱动分析 .
- Linux USB驱动框架分析(一)
- linux下LCD驱动的分析
- Linux下USB驱动框架分析
- Linux 网卡驱动学习(一)(分析一个虚拟硬件的网络驱动样例)
- ZYNQ7000基于linux3.0操作系统驱动分析——GPIO驱动第二天
- Linux驱动修炼之道-SPI驱动框架源码分析(中)
- Linux I2C驱动完全分析(二)
- linux驱动那些事儿之驱动分析之前的磨刀工
- linux驱动由浅入深系列:显示子系统之二(基于android的分析)
- S5pc100 触摸屏驱动分析(linux 2.6.35.13内核)
- 千兆网口 Freescale ETSEC + Marvell 88E1111 uboot Linux 驱动分析 一
- Linux spi驱动分析(二)----SPI核心(bus、device_driver和device)
- linux视频驱动开发二之数据结构分析及操作流程(V4L2)
- linux下pl330 DMA控制器驱动分析
- s3c2440触摸屏驱动分析(LINUX2.6)(1)
- linux内核部件分析(九)——设备驱动模型之device-driver