④、RTC平台驱动的设备移除、挂起和恢复接口函数的实现,代码如下:
*注意:这是使用了一个__devexit,还记得在第①步中的__devexit_p和第②步中的__devinit吗? 我们还是先来讲讲这个: 在Linux内核中,使用了大量不同的宏来标记具有不同作用的函数和数据结构, 这些宏在include/linux/init.h 头文件中定义,编译器通过这些宏可以把代码优化放到合适的内存位置, 以减少内存占用和提高内核效率。__devinit、__devexit就是这些宏之一,在probe()和remove()函数中 应该使用__devinit和__devexit宏。又当remove()函数使用了__devexit宏时,则在驱动结构体中一定要 使用__devexit_p宏来引用remove(),所以在第①步中就用__devexit_p来引用rtc_remove*/ static int __devexit rtc_remove(struct platform_device *dev) { /*从系统平台设备中获取RTC设备类的数据*/ struct rtc_device *rtc = platform_get_drvdata(dev);
platform_set_drvdata(dev, NULL); /*清空平台设备中RTC驱动数据*/ rtc_device_unregister(rtc); /*注销RTC设备类*/
rtc_setpie(&dev->dev, 0); /*禁止RTC节拍时间计数寄存器TICNT的使能功能*/ rtc_setaie(0); /*禁止RTC报警控制寄存器RTCALM的全局报警使能功能*/
iounmap(rtc_base); /*释放RTC虚拟地址映射空间*/ release_resource(rtc_mem); /*释放获取的RTC平台设备的资源*/ kfree(rtc_mem); /*销毁保存RTC平台设备的资源内存空间*/
return 0; }
/*对RTC平台设备驱动电源管理的支持。CONFIG_PM这个宏定义在内核中, 当配置内核时选上电源管理,则RTC平台驱动的设备挂起和恢复功能均有效, 这时候你应该明白了在第②步中为什么要有device_init_wakeup(&pdev->dev, 1)这句吧!!*/ #ifdef CONFIG_PM
static int ticnt_save; /*定义一个变量来保存挂起时的TICNT值*/
/*RTC平台驱动的设备挂起接口函数的实现*/ static int rtc_suspend(struct platform_device *pdev, pm_message_t state) {
ticnt_save = readb(rtc_base + S3C2410_TICNT); /*以节拍时间计数寄存器TICNT的值为挂起点*/
rtc_enable(pdev, 0); /*挂起了之后就禁止RTC控制使能*/
return 0; }
/*RTC平台驱动的设备恢复接口函数的实现*/ static int rtc_resume(struct platform_device *pdev) { rtc_enable(pdev, 1); /*恢复之前先使能RTC控制*/
writeb(ticnt_save, rtc_base + S3C2410_TICNT); /*恢复挂起时的TICNT值,RTC节拍时间继续计数*/
return 0; }
#else /*配置内核时没选上电源管理,RTC平台驱动的设备挂起和恢复功能均无效,这两个函数也就无需实现了*/ #define rtc_suspend NULL #define rtc_resume NULL #endif |
好了,到此RTC驱动程序编写完成了。在这里不知大家有没有留意,在前面的概念部分中我们讲到过,如果把一个字符设备注册成为一个平台设备,除了要实现平台设备驱动中platform_driver的接口函数外,还要实现字符设备驱动中file_operations的接口函数,但是从上面的驱动代码中看,这里并没有对RTC进行file_operations的操作,这是怎么回事啊?原来对RTC进行file_operations的操作在RTC的核心部分已经由系统提供了。在第②步的探测函数rtc_probe中,首先用rtc_device_register注册为RTC设备类,我们看rtc_device_register的实现(在class.c中),又调用了rtc_dev_prepare(rtc),其实现在rtc-dev.c中,那么在这里面才对RTC进行了file_operations操作,对RTC驱动的设备号也在rtc-dev.c中处理的。
四、回过头再来分析理解具体RTC驱动程序代码的结构
在上面的各步骤中,我已对RTC驱动程序的每行代码的作用都做了详细的讲述,但到结尾部分后,也许你会有点找不着北的感觉。的确,整个代码太长,而且用文字的方式也确实很难把整个驱动的结构描述清晰。下面,我就用图形的方式来概括上面各步骤之间的关系,使整个驱动程序的结构更加清晰明了。
五、结束语
通过对RTC驱动的实现,我们对平台设备有了进一步的了解,这对我们以后编写I2C、IIS、看门狗等设备的驱动有了很大的帮助。另外,可以看出在对具体硬件操作的时候实际是对该硬件的各种寄存器进行访问读写,所以这就要求我们在编写一个设备驱动之前必须先了解该设备的数据手册,列出所有的寄存器及功能,这样才能在编写驱动时正确的对设备进行访问。