linux实时时钟驱动三之代码分析

  
*****************************************************************************************************/

  声明:本博内容均由http://blog.csdn.net/lmm670原创,转载请注明出处,谢谢!
/*****************************************************************************************************/

     这里我们针对三星的S3C24XX系列进行RTC驱动框架的分析,关于RTC(实时时钟)的功能和硬件原理我就不在这里啰嗦了,相信大家都十分清楚,不清楚的可以google或者百度一下,我想通过RTC驱动和大家探讨一下linux驱动的一般框架,为大家在linux驱动的学习路上起到一个抛砖引玉的作用。那有哥们就要问了,为什么选择RTC驱动呢?就是因为它简单明了,大家都容易明白的东西才是好东西,好啦,废话不多说,开始进入正题:

      Linux常见的驱动在driver目录下都有一个文件夹,当然我们的RTC也不例外,我们进入kernel主目录下的drivers/rtc,发现下面包含了许多开发平台的RTC驱动,我们这里是以S3C24xx为主,所以我们要寻找的是“rtc-s3c.c”这个美人儿,她是我们要分析的核心;其他几个相关联的文件分别是:alarm.c, alarm-dev.c,class.c, hctosys.c, interface.c, rtc-dev.c.这几个文件就是我们这部戏的主角儿。

    看戏大家都喜欢去研究人物之间的关系,A喜欢B,B喜欢C,C又喜欢D…….这里我们也来先理顺一下这几个主角之间微妙的关系: 

    首先,当然是我们主角中的战斗机rtc-s3c.c,她是最顶层的直接和硬件打交道的驱动文件,每个平台,高通,marvell,三星都有自己的这部分,一般被命名为类似的”rtc-msm.c,rtc-pxa.c,rtc-s3c.c”;就像每个国家每个部落,总有一批底层工作者,在默默的为上层的骄奢淫逸,荣华富贵贡献着自己的血汗;

     rtc-s3c.c上面的是interface.c,顾名思义就知道是接口文件,它主要是对rtc-s3c.c进行封装,给上层提供统一的接口,屏蔽底层差异化的东东。

 Interface.c再往上就到了rtc-dev.c.,rtc-dev.c最终生成了/dev/rtc,上层的应用程序就是通过操作此文件来进行RTC的相关的设置系统时间,设置闹钟,等等。

所有整个人物关系图如下:

上层应用层序

        |

        |

        V

rtc-dev.c

        |

        |

        V

interface.c

       |

       |

       V

rtc-s3c.c

 

    好像三个文件就已经把整个RTC从底层到上层所有的东东都讲完了,那么还剩下几个文件干嘛呢?

     Alarm.c和alarm-dev.c:这两个文件用来干什么呢?这里先留个悬念,后面和大家慢慢说明

 

     class.c:提供了RTC子系统一些的公共函数,让各个RTC驱动注册集成到我们的linux内核中,她实际上是一个粘合剂。

 

    hctosys.c:系统起来之后,会调用到这个文件中的rtc_hctosys()函数,主要功能是系统起来的时候,去读RTC硬件中的时间,然后更新我们的系统时间。

 

 

   好啦,到这里,相信我们已经对整个rtc驱动各个层都比较清楚了,小时候我语文老师叫我们写作文的时候就喜欢玩“总分”结构,先“总”对整个过程有个大概把握,再“分”起来就容易多了,感觉一切都逃出不我们的“五指山”。

 

后面的几个小节,开始我们的“分”,从细节去分析理解每个文件。

第二节:

     第一节已经分析了整个RTC驱动的框架,这一节我们开始分析真正的主角rtc-s3c.c.看一个驱动,一般都是从驱动文件的最底层看起,不知道是不是写linux内核的大师们学习过我们的《易经》,易经64卦,每一卦有爻,我们分析一个卦的时候,也是从底下一个爻开始分析。废话少说,把代码直接拉到最下面:

666-669行:该驱动文件的描述,作者,申明为开源,以及声明一个模块别称,这些和具体的功能无关,了解一下也不是坏事; 

652-664行:这个大家再熟悉不过了,系统起来的时候回去执行s3c_rtc_init,模块卸载的时候回去调用s3c_rtc_exits3c_rtc_init很爽快,就干了一件事,把这个驱动注册到系统中,与对应的设备匹配起来;而他的兄弟s3c_rtc_exit完成了相反的动作;

638-648行:RTC驱动结构体,如果前面的驱动成功注册进系统后,并找到了何止相对应的设备名,则s3c_rtc_probe函数被调用,它才是驱动开始的第一炮;也就是说,系统起来的时候,只要此驱动找到了与之对应的设备名,probe函数就会被调用。

Suspendresume也是一对好哥们,系统睡眠和唤醒的时候,分别被调用。这两个和电源管理有关的,我们就不去细究了。

下面我们进入到prboe函数中:

 

 

 

467-477行:从平台设备中获取中断资源;

490-504行:从平台设备中获取IO内存资源;

这里要提一下,前面驱动注册的时候用platform_driver_register();就是把驱动注册成了一个平台驱动,对应的平台设备为

struct platform_device s3c_device_rtc = {

         .name                  = "s3c2410-rtc",

         .id                = -1,

         .num_resources         = ARRAY_SIZE(s3c_rtc_resource),

         .resource   = s3c_rtc_resource,

};

资源结构体:

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

         }

};

我们在回过头来跟进

int platform_get_irq(struct platform_device *dev, unsigned int num)

{

         struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);

 

         return r ? r->start : -ENXIO;

}

是不是一目了然了。

接着分析:

 

507-519行,获取clock并使能时钟,enable RTC;

 

524行:device_init_wakeup(&pdev->dev, 1);打开唤醒中断开关,RTC能在睡眠情况下唤醒系统,这只是一个开关,真正能不能唤醒系统,需要把中断设置为唤醒中断。

 

528行:把RTC驱动注册进linux系统,从此,这个RTC就在linux内核中有一席之地了,不再是“没房”一族了。

 

531-534行:注册失败,打道回府,后面都是徒劳。

 

到这里probe函数的主要功能算是完成啦。后面几行留给大家分析一下。

 

讲了这么多,好像还是和RTC的功能没占上半毛钱关系,什么设置闹钟,设置时间的词眼还没出现一个,下面一节开始分析这一块。

第三节:

   函数把rtc驱动注册进了linux中,主要就是注册了对RTC的一些列操作,设置时间,设置闹钟等等,而这些操作都被包含在&s3c_rtcops中,我们来仔细观察这个家伙的全貌:

    static const struct rtc_class_ops s3c_rtcops = {

         .open                 = s3c_rtc_open,

         .release   = s3c_rtc_release,

         .read_time       = s3c_rtc_gettime,

         .set_time          = s3c_rtc_settime,

         .read_alarm     = s3c_rtc_getalarm,

         .set_alarm       = s3c_rtc_setalarm,

         .irq_set_freq   = s3c_rtc_setfreq,

         .irq_set_state = s3c_rtc_setpie,

         .proc                 = s3c_rtc_proc,

};

    是不是很清楚了呢?所有的RTC相关的操作全在里面。读出RTC时间,设置rtc时间,设置闹钟,读取闹钟,设置rtc频率参数等等,我就不一一分析了,无非就是一些对硬件寄存器的读写操作,千篇一律,搞驱动的兄弟和他们熟悉的不能在熟悉了。

    我们重点来看一个函数,那就是s3c_rtc_open

 

    在这个函数中我们发现只做了一件事:就是通过request_irq    RTC的两个终端注册进系统,我们来看看这2个中断函数:

    这2个函数都调用了同一个函数rtc_update_irq只是传进去的参数不一样而已,一个是闹钟中断,一个是系统时钟滴答中断。

进入代码分析:

392行:异步通知系统此中断的到来;

到这里,rtc-s3c.c已经分析完。

下面一节我们看看上层是如何和底层打交道的

第四节:

   

这一节我们分析RTC驱动框架的最上层rtc-dev.c

 

 /

系统起来的时候,rtc_dev_init会被调用,此函数生成了文件/dev/rtc.

501行:把操作集rtc_dev_fops分配给/dev/rtc,而函数rtc_dev_prepare就是在rtc-s3c.c中注册RTC驱动时被调用的;

 

现在上层的接口文件/dev/rtc有了,它的操作集也有了rtc_dev_fops

 

static const struct file_operations rtc_dev_fops = {

         .owner               = THIS_MODULE,

         .llseek                = no_llseek,

         .read                  = rtc_dev_read,

         .poll           = rtc_dev_poll,

         .unlocked_ioctl         = rtc_dev_ioctl,

         .open                 = rtc_dev_open,

         .release   = rtc_dev_release,

         .fasync               = rtc_dev_fasync,

};

接下来我们就看看上层是如何最终设置RTC的。

流程大致如下:

首先上层程序会去open文件/dev/rtc,得到一个文件描述符;接下来就可以对她为所欲为了:

比如:设置RTC时间,上层通过ioctl命令RTC_SET_TIME操作文件描述符,会调用到rtc_dev_ioctl中:

rtc_set_time()位于(interface.c,跟进去:

发现调用了rtc->ops->set_time(rtc->dev.parent, tm);即回调到了我们第3节中的结构体s3c_rtcops中的s3c_rtc_settime函数,继续跟进:

 

直接对RTC寄存器进行写操作,把上层传进来的时间写到rtc寄存器中去。

到这里貌似一切该结束了,好像前面还刘了一个悬念:

Alarm.calarm-dev.c:这两个文件用来干什么呢?

因为我拿到的这份代码是 android linux kernel,这2个文件是android的开发者添加上去的,android内核把rtc-dev.c丢弃了,取而代之的就是这2个东东,因而android下对RTC的操作文件为/dev/alarm而不是我们这里的/dev/rtc,感兴趣的哥们可以好好研究一下。

好了,整个RTC驱动框架分析到此结束了。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值