3.2.0内核rtc框架分析

比起2.6内核,3.2内核在rtc框架上加了一层class,变为了三层。


VFS 虚拟文件系统


rtc-dev.c 核心层
向总线层提供 rtc_dev_add_device、rtc_dev_del_device等接口


kernel/driver/rtc/class.c 总线层
向具体设备驱动提供 rtc_device_register 等接口


rtc-ds1307.c rtc-ds1742.c等具体驱动


从class.c源码中rtc_device_register->rtc_sysfs_add_device和rtc_proc_add_device
可知,rtc不仅在/dev下创建设备节点,还在虚拟文件系统中有节点

ls /sys/class/rtc -l
lrwxrwxrwx    1 root     root            0 Jul 27  2012 rtc0 -> ../../devices/platform/omap/omap_i2c.1/i2c-1/1-0068/rtc/rtc0

/sys/下节点实际上是I2C下软链接,RTC一般是挂载I2C总线上的。

从内核文档可知,新的框架移除了所谓的“一系统一RTC”的理念,比如说 AM335X 上有两个RTC芯片,其中一个是外挂在I2C总线上的(低功耗),另一个是集成在SOC上(高功耗,高性能)的,系统刚启动时候会从I2C上的RTC读取时间,但是一旦上电运行后,就以SOC上的芯片来提供时钟脉冲来工作,为的是利用它的高性能。

IOCTL INTERFACE
The ioctl() calls supported by /dev/rtc are also supported by the RTC class framework.

RTC_RD_TIME, RTC_SET_TIME--提供读写RTC的接口
RTC_ALM_SET, RTC_ALM_READ--当RTC和中断IRQ做了连接,那么会每隔一定时间产生中断。
...还有其他一些不常用接口。

举个例子,RTC alarm可以用作系统唤醒源,可以把系统从低功耗的睡眠或者挂起状态唤醒到正常运行状态。这就给我们思路来做电源管理这块了。

内核提供了一个很好的示例 rtc-test.c 来说明这块,可以参考。

下面就从 rtc-test.c 来分析整个框架(PS:这个驱动也是学习platform平台的好例子)。

test_probe
    struct rtc_device *rtc = rtc_device_register("test", &plat_dev->dev, &test_rtc_ops, THIS_MODULE);   
        rtc_device_register  //class.c
            struct rtc_device *rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
            rtc->id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);  //get a new id,下面会用到,这个函数没有具体分析
            rtc->ops = ops;  //就是上面的 test_rtc_ops
            rtc->dev.class = rtc_class;  //rtc总线,在rtc_init函数中通过rtc_class = class_create(THIS_MODULE, "rtc")注册
            rtc_dev_prepare(rtc);  //这个函数很重要,下面我们会单独分析
            //分别在 /dev、/sys、/proc下注册节点
            rtc_dev_add_device(rtc);
            rtc_sysfs_add_device(rtc);
            rtc_proc_add_device(rtc);

我们来看下核心层都做了什么

    rtc_dev_init //rtc-dev.c 核心层
        alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");  //RTC_DEV_MAX = 16  动态申请设备号,并存入rtc_devt中        

回到上面的 rtc_dev_add_device

rtc_dev_add_device(struct rtc_device *rtc)  
    cdev_add(&rtc->char_dev, rtc->dev.devt, 1); //到这里才真正注册设备节点

那么问题就来了,上面申请的rtc_devt到哪里用了?rtc->dev.devt是多少,而字符设备的ops在哪?这就需要到上面我们说的rtc_dev_prepare中看了

rtc_dev_prepare  //rtc-dev.c
    rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);  //也就说用的是上面动态申请的主设备号,但是次设备号用的是class.c中分配的
    cdev_init(&rtc->char_dev, &rtc_dev_fops);  //ops出现了

注意区分 rtc_dev_fops 和上面的 test_rtc_ops 不是一个概念

rtc_dev_fops :是file_operations结构,供设备节点的read/write使用
test_rtc_ops :为rtc设备抽象出的rtc_class_ops结构,用于rtc设备具体读写实现   

到这里,我们就能就能梳理清楚 /dev/rtc*是如何注册使用的了。

当应用层调用ioctl(“/dev/rtc*”, RTC_RD_TIME, &rtc_tm),实际上调用的是 rtc_dev_fops 中的相关接口,我们来跟下代码

--------------------------------------------------------
ioctl("/dev/rtc*", RTC_RD_TIME, &rtc_tm)   应用层
--------------------------------------------------------
rtc_dev_fops                               rtc-dev.c 核心层            
rtc_dev_ioctl   //就是上面的rtc_dev_fops->unlocked_ioctl
    const struct rtc_class_ops *ops = rtc->ops; //取到具体驱动中的ops,这里就是rtc-test.c中的 test_rtc_ops
    mutex_lock_interruptible(&rtc->ops_lock);  //加个锁,防止多个进程同时控制
    case RTC_RD_TIME:
        rtc_read_time(rtc, &tm);
        copy_to_user(uarg, &tm, sizeof(tm))
---------------------------------------------------------
接下来具体分析 rtc_read_alarm 的实现  rtc/interface.c
rtc_read_time   
    __rtc_read_time(rtc, tm);
        memset(tm, 0, sizeof(struct rtc_time));
        rtc->ops->read_time(rtc->dev.parent, tm);  //最终调用的是驱动里边的ops->read_time
----------------------------------------------------------
rtc->ops->read_time = test_rtc_read_time    具体驱动层rtc-test.c     
    rtc_time_to_tm(get_seconds(), tm);  //这里具体怎么读,怎么写就取决于具体的硬件设备了

总结下,从上面分析可知,如果我们要编写一个具体的RTC设备驱动程序,只需要做两步即可:

1.调用 rtc_device_register 接口注册设备
2.实现 rtc_class_ops 需要的接口  //这个需要根据具体硬件设备来编程

我们具体来分析一个真正的设备实现,以3.2.0中rtc-ds1307.c来分析,这个文件实际上是以I2C体系来实现rtc驱动(大多数RTC芯片都是挂在I2C上的)

ds1307_init
    i2c_add_driver(&ds1307_driver); 
        ds1307_probe  //当BSP文件出现设备端时候调用这个函数
            ds1307->read_block_data = i2c_smbus_read_i2c_block_data;  //最后读数据还是通过I2C总线来读的
            ds1307->rtc = rtc_device_register(client->name, &client->dev, &ds13xx_rtc_ops, THIS_MODULE);  //出现了,注册RTC设备

//接下来主要任务就是实现这些ops接口啦,走一个 ds1307_get_time

static const struct rtc_class_ops ds13xx_rtc_ops = {
    .read_time  = ds1307_get_time,
    .set_time   = ds1307_set_time,
    .read_alarm = ds1337_read_alarm,
    .set_alarm  = ds1337_set_alarm,
    .alarm_irq_enable = ds1307_alarm_irq_enable,
};          
ds1307_get_time
    ds1307->read_block_data(ds1307->client, ds1307->offset, 7, ds1307->regs); //实际上调用就是上面的 i2c_smbus_read_i2c_block_data

剩下的我们就不再继续分析了,直接借助I2C总线接口就很容易实现编程了

至于 /sys 和 /proc相关的代码,不再分析,这又牵扯到两个很大的体系,后边我们会说道

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浓咖啡jy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值