总说:
RTC设备驱动是一个标准的字符设备驱动,应用程序通过open、release、read、write和ioctl等函数完成对RTC设备的操作。
Linux内核将RTC设备抽象为rtc_device结构体,因此RTC设备驱动就是申请并初始化rtc_device,最后将rtc_device注册到Linux内核里面,这样Linux内核就有一个RTC设备的。
至于RTC设备的操作肯定是用一个操作集合(结构体)来表示的,rtc_device结构体里有一个rtc_class_ops结构体,(编写RTC内核驱动的主要步骤就是填写rtc_class_ops),rtc_class_ops为RTC设备的最底层操作函数集合,包括从RTC设备中读取时间、向RTC设备写入新的时间值等。因此,rtc_class_ops是需要用户根据所使用的RTC设备编写的。
(这个结构体中使用的struct device参数就是rtc_device_register()使用的那个dev,它代表总线上的物理设备。这个struct device的driver_data数据一般保存struct device的状态,包括指向rtc_device的指针。驱动开发者至少应该提供read_time/set_time这两个接口,其他函数都是可选的。)
用户操作rtc驱动结构图:
第一部分:框架
从上面的图中我们可以看出RTC框架大致可以分为三部分:rtc-s3c.c ,class.c 以及 rtc-dev.c 。他们依次向上抽象注册。同时又对下面底层的函数进行回调,从而构成了整个完整的RTC框架。我们现在先分开介绍。
rtc-s3c.c 层:
该层是RTC框架的底层,与硬件打交道。用时又由于这是RTC框架的底层,所以各个芯片对应的这一层可能会不一样,所以该层是与具体芯片相关的。同时又由于内核面向对象的思想,所以在该层一定要有一个结构体表示RTC设备——rtc_device(见下面)。
class.c层:
该层作为rtc-s3c.c 和 rtc-dev.c 的中间层,对这两层有一个连接作用。同时由于RTC设备最终要注册进内核,所以该层也有对RTC实行注册中转的作用。总的来说该层的作用就是连接中转。同时由于该层已经与具体硬件没有关系了,所以该层的代码不用修改。
rtc-dev.c 层:
该层作为RTC对上层的抽象层。在该层中将RTC设备抽象为字符设备,并用字符设备的框架对其进行注册。同时由于该层是纯软件的概念了,所以不能对相关的硬件进行操作。而要操作相关硬件时要通过回调函数找到对相关硬件的操作函数,并执行该函数。这用我们就又实现了从抽象到具体的过程。
通过上面的分析,你也许就明白了,其实上面这三层是相互关联,相互调用的关系。从rtc-s3c.c ,class.c 到 rtc-dev.c经过层层抽象,将RTC设备抽象到字符设备实现对RTC设备向内核的注册,而又通过rtc-dev.c对rtc-s3c.c 的回调实现从抽象到具体的操作。上面就是这三层的关系。而我们现在分析各个层中的函数来详细了解他们内部的关系。
第二部分:结合代码分析RTC框架
我们先分析rtc-s3c.c 进而分析class.c 最后rtc-dev.c,从中我们了解各个层中函数调用的过程(在代码分析这部分我会删除部分不重要的判断或是其他的语句,来使代码看起来更加的清晰明了)。
rtc-s3c.c :
我们从入口函数开始分析:
static int __init s3c_rtc_init(void)
{
printk(banner);
return platform_driver_register(&s3c2410_rtcdrv);
}
从入口函数中我们可以看出他最主要的是注册了一个平台驱动结构体:s3c2410_rtcdrv。那我们就要看看这个平台驱动结构体做了什么:
static struct platform_driver s3c2410_rtcdrv = {
.probe = s3c_rtc_probe,
.remove = s3c_rtc_remove,
.suspend = s3c_rtc_suspend,
.resume = s3c_rtc_resume,
.driver = {
.name = "s3c2410-rtc",
.owner = THIS_MODULE,
},
};
从上面我们可以看出他是一个名为s3c2410-rtc的平台驱动,而在平台——设备——驱动模型中我们知道,既然有这个驱动那就一定有一个与其同名的平台设备,通过搜索我们在arch\arm\plat-s3c24xx\devs.c文件中找到了与其同名的设备:
static struct resource s3c_rtc_resource[] = {
[0] = {
.start = S3C24XX_PA_RTC,//寄存器地址
.end = S3C24XX_PA_RTC + 0xff,//同上
.flags = IORESOURCE_MEM, //中断
},
[1] = {
.start = IRQ_RTC,
.end = IRQ_RTC,
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = IRQ_TICK,
.end = IRQ_TICK,
.flags = IORESOURCE_IRQ
}
};
struct platform_device s3c_device_rtc = {
.name = "s3c2410-rtc",
.id = -1,
.num_resources &