根据linux中总线、设备和驱动这个模型来看,所有的设备驱动都挂在总线上,并且驱动应该是和平台无关的。唯一和平台有关的一类驱动应该是芯片内部的各个控制器的驱动,例如芯片内部I2C控制器,芯片内部LCD控制器,芯片内部看门狗等等。如果是一个外部的LCD控制器的驱动程序,它的实现一定要和平台无关,这样针对不同的平台只要修改板级文件即可,而不需要修改驱动程序本身。从linux内核源代码中的S3c2410_wdt.c(位于drivers/watchdog中)可以看出,从不同的角度看,看门狗分别可以属于:平台设备、字符设备和混杂设备。所以可以看到该驱动程序中不仅实现了平台设备驱动的probe, remove等函数,还实现了字符设备的file-operations中的open,write, ioctl, llseek, release等函数。下面分析这个驱动程序。
第一个问题:platform_driver和platform_device是如何建立关联的?
在驱动程序S3c2410_wdt.c中定义了platform_driver结构体s3c2410wdt_driver,代码清单如下:
其中的.name = "s3c2410-wdt"代表了驱动的名称。
而相应的设备在平台相关的文件中已经定义了(在arch/arm/plat-s3c24xx/Devs.c)代码清单如下:
其中的设备名称也是"s3c2410-wdt".
并且这个设备已经在平台初始化时使用platform_add_devices函数添加到了总线上。 以smdk2410开发板为例,代码清单如下:
可知现在总线已经挂上了一个名为"s3c2410-wdt"的设备了。
在加载驱动模块时,执行了watchdog_init函数,代码清单如下:
其中platform_driver_register函数的作用是将这个驱动注册到platform总线上,同时寻找在platform总线上与之匹配的设备。使用sourceinsight可以清楚看到这其中主要函数的执行顺序为:
platform_driver_register --> driver_register --> bus_add_driver --> driver_attach --> bus_for_each_dev --> __driver_attach --> driver_match_device --> platform的match方法:of_platform_bus_match。仔细查看这个方法会发现实际上就是匹配了设备的名称和驱动的名称,只要这两个名字一样,就匹配成功。如果匹配成功,则在__driver_attach中继续调用driver__probe_device方法,这个方法最终调用的就是platform_driver中的probe方法。至此完成了驱动程序与设备的关联。
第二个问题:既然最正确的驱动程序中不应该包含平台相关的代码,那么驱动是怎样得到平台的信息的呢?
这里使用了platform_driver中的probe方法。 probe,顾名思义,是“探测”,是driver对device的探测。结合S3c2410_wdt.c源代码对探测的过程作进一步理解。
第三个问题: 具体操作硬件的代码在哪?
在S3c2410_wdt.c中直接对硬件进行操作的函数有
s3c2410wdt_start --开始开始看门狗计时器
s3c2410wdt_stop --停止看门狗计时器
s3c2410wdt_keepalive --“喂狗”
s3c2410wdt_set_heartbeat -- 设置“心跳”,就是计数周期
s3c2410wdt_suspend --挂起看门狗,就是在关闭看门狗之前保存状态
s3c2410wdt_resume --恢复看门狗计时器
第四个问题: 这些操作硬件的代码在驱动程序中又是怎么组织的呢?
前面说到,芯片内部的这个看门狗从不同角度看是有不同的身份的:平台设备,字符设备和混杂设备。下图显示了操作硬件的代码是怎样组织的。
------------------------------------------------------以上---------------------------------------------------