Linux RTC 驱动模型分析


RTC(real time clock)实时时钟,主要作用是给Linux系统提供时间。RTC因为是电池供电的,所以掉电后时间不丢失。Linux内核把RTC用作“离线”的时间与日期维护器。当Linux内核启动时,它从RTC中读取时间与日期,作为基准值。在运行期间内核完全抛开RTC,以软件的形式维护系统的当前时间与日期,并在需要时将时间回写RTC芯片。另外如果RTC提供了IRQ中断并且可以定时,那么RTC还可以作为内核睡眠时唤醒内核的闹钟。应用程序可以用RTC提供的周期中断做一些周期的任务。 linux有两种rtc驱动的接口,一个是老的接口,专门用在PC机上的。另外一钟新接口是基于linux设备驱动程序的。这个新的接口创建了一个RTC驱动模型,实现了RTC的大部分基本功能。而底层驱动无须考虑一些功能的实现,只需将自己注册的RTC核心中,其他工作由RTC核心来完成。下面分析RTC新接口的驱动模型。
一. 驱动模型结构
        与RTC核心有关的文件有:
        /drivers/rtc/class.c          这个文件向linux设备模型核心注册了一个类RTC,然后向驱动程序提供了注册/注销接口
        /drivers/rtc/rtc-dev.c       这个文件定义了基本的设备文件操作函数,如:open,read等
        /drivers/rtc/interface.c     顾名思义,这个文件主要提供了用户程序与RTC驱动的接口函数,用户程序一般通过ioctl与RTC驱动交互,这里定义了每个ioctl命令需要调用的函数
        /drivers/rtc/rtc-sysfs.c     与sysfs有关
        /drivers/rtc/rtc-proc.c      与proc文件系统有关
        /include/linux/rtc.h         定义了与RTC有关的数据结构

        RTC驱动模型结构如下图:


二. 基本数据结构
  1. struct rtc_device 结构
 
  1. struct rtc_device  
  2. {  
  3.     struct device dev;  
  4.     struct module *owner;  
  5.   
  6.     int id;  
  7.     char name[RTC_DEVICE_NAME_SIZE];  
  8.   
  9.     const struct rtc_class_ops *ops;  
  10.     struct mutex ops_lock;  
  11.   
  12.     struct cdev char_dev;  
  13.     unsigned long flags;  
  14.   
  15.     unsigned long irq_data;  
  16.     spinlock_t irq_lock;  
  17.     wait_queue_head_t irq_queue;  
  18.     struct fasync_struct *async_queue;  
  19.   
  20.     struct rtc_task *irq_task;  
  21.     spinlock_t irq_task_lock;  
  22.     int irq_freq;  
  23.     int max_user_freq;  
  24. #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL   
  25.     struct work_struct uie_task;  
  26.     struct timer_list uie_timer;  
  27.     /* Those fields are protected by rtc->irq_lock */  
  28.     unsigned int oldsecs;  
  29.     unsigned int uie_irq_active:1;  
  30.     unsigned int stop_uie_polling:1;  
  31.     unsigned int uie_task_active:1;  
  32.     unsigned int uie_timer_active:1;  
  33. #endif   
  34. };  
        这个结构是RTC驱动程序的基本数据结构,但是他不像其他核心的基本结构一样,驱动程序以他为参数调用注册函数注册到核心。这个结构是由注册函数返回给驱动程序的。
  2. struct rtc_class_ops 结构
  1. struct rtc_class_ops {  
  2.     int (*open)(struct device *);  
  3.     void (*release)(struct device *);  
  4.     int (*ioctl)(struct device *, unsigned int, unsigned long);  
  5.     int (*read_time)(struct device *, struct rtc_time *);  
  6.     int (*set_time)(struct device *, struct rtc_time *);  
  7.     int (*read_alarm)(struct device *, struct rtc_wkalrm *);  
  8.     int (*set_alarm)(struct device *, struct rtc_wkalrm *);  
  9.     int (*proc)(struct device *, struct seq_file *);  
  10.     int (*set_mmss)(struct device *, unsigned long secs);  
  11.     int (*irq_set_state)(struct device *, int enabled);  
  12.     int (*irq_set_freq)(struct device *, int freq);  
  13.     int (*read_callback)(struct device *, int data);  
  14.     int (*alarm_irq_enable)(struct device *, unsigned int enabled);  
  15.     int (*update_irq_enable)(struct device *, unsigned int enabled);  
  16. };  
        这个结构是RTC驱动程序要实现的基本操作函数,注意这里的操作不是文件操作。驱动程序通过初始化这样一个结构,将自己实现的函数与RTC核心联系起来。这里面的大部分函数都要驱动程序来实现。而且这些函数都是操作底层硬件的,属于最底层的函数。
  3. struct rtc_time 结构
 
  1. struct rtc_time {  
  2.     int tm_sec;  
  3.     int tm_min;  
  4.     int tm_hour;  
  5.     int tm_mday;  
  6.     int tm_mon;  
  7.     int tm_year;  
  8.     int tm_wday;  
  9.     int tm_yday;  
  10.     int tm_isdst;  
  11. };  
        代表了时间与日期,从RTC设备读回的时间和日期就保存在这个结构体中
三. class.c 
  1. 模块初始化函数:rtc_init  
  1. static int __init rtc_init(void)  
  2. {  
  3.     rtc_class = class_create(THIS_MODULE, "rtc");  
  4.     if (IS_ERR(rtc_class)) {  
  5.         printk(KERN_ERR "%s: couldn't create class\n", __FILE__);  
  6.         return PTR_ERR(rtc_class);  
  7.     }  
  8.     rtc_class->suspend = rtc_suspend;  
  9.     rtc_class->resume = rtc_resume;  
  10.     rtc_dev_init();  
  11.     rtc_sysfs_init(rtc_class);  
  12.     return 0;  
  13. }  
        rtc_init首先调用class_create创建了一个类--rtc。我们知道类是一个设备的高层视图,他抽象出了底层的实现细节。类的作用就是向用户空间提供设备的信息,驱动程序不需要直接处理类。然后初始化类结构的相应成员,rtc_suspend,rtc_resume这两个函数也是在class.c中实现的。接下来调用rtc_dev_init(),这个函数为RTC设备动态分配设备号,保存在rtc_devt中。最后调用rtc_sysfs_init,初始化rtc_class的属性。
  2. 为底层驱动提供接口:rtc_device_register,rtc_device_unregister
 
  1. struct rtc_device *rtc_device_register(const char *name, struct device *dev,  
  2.                     const struct rtc_class_ops *ops,  
  3.                     struct module *owner)  
  4. {  
  5.     struct rtc_device *rtc;  
  6.     int id, err;  
  7.   
  8.     if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {  
  9.         err = -ENOMEM;  
  10.         goto exit;  
  11.     }  
  12.   
  13.     mutex_lock(&idr_lock);  
  14.     err = idr_get_new(&rtc_idr, NULL, &id);  
  15.     mutex_unlock(&idr_lock);  
  16. /*--------------------(1)---------------------*/  
  17.     if (err < 0)  
  18.         goto exit;  
  19.     id = id & MAX_ID_MASK;  
  20.     rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);  
  21.     if (rtc == NULL) {  
  22.         err = -ENOMEM;  
  23.         goto exit_idr;  
  24.     }  
  25.   
  26.     rtc->id = id;  
  27.     rtc->ops = ops;  
  28.     rtc->owner = owner;  
  29.     rtc->max_user_freq = 64;  
  30.     rtc->dev.parent = dev;  
  31.     rtc->dev.class = rtc_class;  
  32.     rtc->dev.release = rtc_device_release;  
  33.   
  34.     mutex_init(&rtc->ops_lock);  
  35.     spin_lock_init(&rtc->irq_lock);  
  36.     spin_lock_init(&rtc->irq_task_lock);  
  37.     init_waitqueue_head(&rtc->irq_queue);  
  38.   
  39.     strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);  
  40.     dev_set_name(&rtc->dev, "rtc%d", id);  
  41. /*-------------------(2)--------------------*/  
  42.     rtc_dev_prepare(rtc);  
  43.   
  44.   
  45.     err = device_register(&rtc->dev);  
  46.     if (err)  
  47.         goto exit_kfree;  
  48. /*-------------------(3)--------------------*/  
  49.     rtc_dev_add_device(rtc);  
  50.     rtc_sysfs_add_device(rtc);  
  51.     rtc_proc_add_device(rtc);  
  52.   
  53.     dev_info(dev, "rtc core: registered %s as %s\n",  
  54.             rtc->name, dev_name(&rtc->dev));  
  55. /*-------------------(4)--------------------*/  
  56.     return rtc;  
  57.   
  58. exit_kfree:  
  59.     kfree(rtc);  
  60. exit_idr:  
  61.     mutex_lock(&idr_lock);  
  62.     idr_remove(&rtc_idr, id);  
  63.     mutex_unlock(&idr_lock);  
  64. exit:  
  65.     dev_err(dev, "rtc core: unable to register %s, err = %d\n",  
  66.             name, err);  
  67.     return ERR_PTR(err);  
  68. }  
    (1):处理一个idr的结构,idr在linux内核中指的就是整数ID管理机制,从本质上来说,idr是一种将整数ID号和特定指针关联在一起的机制。这个机制最早是在2003年2月加入内核的,当时是作为POSIX定时器的一个补丁。现在,在内核的很多地方都可以找到idr的身影。详细实现请参照相关内核代码。这里从内核中获取一个idr结构,并与id相关联。
    (2):分配了一个rtc_device的结构--rtc,并且初始化了相关的成员:id, rtc_class_ops等等。
    (3):首先调用rtc_dev_prepare(在rtc-dev.c中定义)。因为RTC设备本质来讲还是字符设备,所以这里初始化了字符设备相关的结构:设备号以及文件操作。然后调用device_register将设备注册到linux设备模型核心。这样在模块加载的时候,udev daemon就会自动为我们创建设备文件rtc(n)。
    (4):先后调用rtc_dev_add_device,rtc_sysfs_add_device,rtc_proc_add_device三个函数。rtc_dev_add_device注册字符设备,rtc_sysfs_add_device只是为设备添加了一个闹钟属性,rtc_proc_add_device 创建proc文件系统接口。
  • 1
  • 2
  • 下一页

相关内容