RTC实时时钟驱动

linux中的RTC驱动位于drivers/rtc下,里面包含了许多开发平台的RTC驱动,我们这里是以S3C24xx为主,所以它的RTC驱动为rtc-s3c.c


30.Linux-RTC驱动分析及使用 - 诺谦 - 博客园

1.进入./drivers/rtc/rtc-s3c.c

还是首先进入入口函数,如下图所示:

这里注册了一个“s3c2410-rtc”名称的平台设备驱动

而“s3c2410-rtc”的平台设备,在./arch/arm/plat-s3c24xx/dev.c里定义了,如下图所示:

当内核匹配到有与它名称同名的平台设备,就会调用.probe函数,

接下来我们便进入s3c2410_rtcdrv->probe函数中看看,做了什么:

static int s3c_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;           //rtc设备结构体
struct resource *res;
int ret;

s3c_rtc_tickno = platform_get_irq(pdev, 1);          //获取IRQ_TICK节拍中断资源
s3c_rtc_alarmno = platform_get_irq(pdev, 0);        //获取IRQ_RTC闹钟中断资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);   //获取内存资源

s3c_rtc_mem = request_mem_region(res->start,res->end-res->start+1,pdev->name);//申请内存资源

s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);     //对内存进行重映射

s3c_rtc_enable(pdev, 1);          //设置硬件相关设置,使能RTC寄存器

s3c_rtc_setfreq(s3c_rtc_freq);      //设置TICONT寄存器,使能节拍中断,设置节拍计数值

/*1.注册RTC设备*/
rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,THIS_MODULE);  
rtc->max_user_freq = 128;
platform_set_drvdata(pdev, rtc);
      return 0;
}

显然最终会调用rtc_device_register()函数来向内核注册rtc_device设备驱动,注册成功会返回一个已注册好的rtc_device,而s3c_rtcops是一个rtc_class_ops类型的结构体,里面有如何操作这个rtc设备的最底层函数,比如读写RTC时间,读写闹钟时间等,会保存在rtc_device->ops里,注意是保存起来,而不是当作操作集注册给驱动。该函数在drivers/rtc/Class.c文件内被定义。Class.c文件主要定义了RTC子系统,而内核初始化,便会进入Class.c,进入rtc_init()->rtc_dev_init(),来注册字符设备:

err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");   
// RTC_DEV_MAX=16,表示只注册0~15个次设备号,设备编号保存在rtc_devt中 

2.它与rtc_device_register()函数注册RTC设备,会有什么关系?

接下来便来看rtc_device_register(),代码如下:

struct rtc_device *rtc_device_register(const char *name, struct device *dev,const struct rtc_class_ops *ops,struct module *owner)
{
       struct rtc_device *rtc;    //定义一个rtc_device结构体
       ... ...
       rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);  //分配rtc_device结构体为全局变量
       /*设置rtc_device*/
     rtc->id = id;
       rtc->ops = ops;                        //将s3c_rtcops保存在rtc_device->ops里
       rtc->owner = owner;
       ... ...
       rtc_dev_prepare(rtc);                  //1.做提前准备,初始化cdev结构体
       ... ...
       rtc_dev_add_device(rtc);               //2.在/dev下创建rtc相关文件,将cdev添加到系统中
       ... ...
    return rtc;
}

上面的rtc_dev_prepare(rtc)和rtc_dev_add_device(rtc)

主要做了以下两个工作(位于./drivers/rtc/rtc-dev.c):

cdev_init(&rtc->char_dev, &rtc_dev_fops);          //绑定file_operations  

cdev_add(&rtc->char_dev, rtc->dev.devt, 1);    //注册rtc->char_dev字符设备,添加一个从设备到系统中

显然这一连串操作就是注册新字符驱动设备的流程。

值得注意的是这里注册进驱动的file_operations是rtc_dev_fops,并不是上面最底层的RTC操作函数(rtc_class_ops结构体类型的s3c_rtcops),但是最终rtc_dev_fops结构体会调用到最底层的s3c_rtcops操作集。

所以“s3c2410-rtc”平台设备驱动的.probe主要做了以下几件事:

  • 1.设置RTC相关寄存器
  • 2.分配rtc_device结构体
  • 3.设置rtc_device结构体 
  •     -> 3.1 将struct  rtc_class_ops  s3c_rtcops放入rtc_device->ops保存
  • 4. 注册rtc->char_dev字符设备,且该字符设备操作集为:struct file_operations  rtc_dev_fops 

3.上面的file_operations操作结构体rtc_dev_fops 的成员,如下图所示: 

 当我们应用层open(”/dev/rtcXX”)时,就会调用rtc_dev_fops-> rtc_dev_open()。

static int rtc_dev_open(struct inode *inode, struct file *file)
{
   struct rtc_device *rtc = container_of(inode->i_cdev,struct rtc_device, char_dev);//获取对应的rtc_device
   const struct rtc_class_ops *ops = rtc->ops;                            //最终等于s3c_rtcops

   file->private_data = rtc;                     //设置file结构体的私有成员等于rtc_device,再次执行ioctl等函数时,直接就可以提取file->private_data即可

   err = ops->open ? ops->open(rtc->dev.parent) : 0;  //调用s3c_rtcops->open

   mutex_unlock(&rtc->char_lock);
   return err;
}

显然最终还是调用rtc_device下的s3c_rtcops->open,注册给设备驱动的rtc_dev_fops操作集只是作为一个中介,最终还是调用保存在rtc_device->ops里的最底层的s3c_rtcops操作集。

框架如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

内核分析笔记

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

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

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

打赏作者

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

抵扣说明:

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

余额充值