Linux RTC 驱动模型分析
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 结构
- struct rtc_device
- {
- struct device dev;
- struct module *owner;
- int id;
- char name[RTC_DEVICE_NAME_SIZE];
- const struct rtc_class_ops *ops;
- struct mutex ops_lock;
- struct cdev char_dev;
- unsigned long flags;
- unsigned long irq_data;
- spinlock_t irq_lock;
- wait_queue_head_t irq_queue;
- struct fasync_struct *async_queue;
- struct rtc_task *irq_task;
- spinlock_t irq_task_lock;
- int irq_freq;
- int max_user_freq;
- #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
- struct work_struct uie_task;
- struct timer_list uie_timer;
- /* Those fields are protected by rtc->irq_lock */
- unsigned int oldsecs;
- unsigned int uie_irq_active:1;
- unsigned int stop_uie_polling:1;
- unsigned int uie_task_active:1;
- unsigned int uie_timer_active:1;
- #endif
- };
2. struct rtc_class_ops 结构
- struct rtc_class_ops {
- int (*open)(struct device *);
- void (*release)(struct device *);
- int (*ioctl)(struct device *, unsigned int, unsigned long);
- int (*read_time)(struct device *, struct rtc_time *);
- int (*set_time)(struct device *, struct rtc_time *);
- int (*read_alarm)(struct device *, struct rtc_wkalrm *);
- int (*set_alarm)(struct device *, struct rtc_wkalrm *);
- int (*proc)(struct device *, struct seq_file *);
- int (*set_mmss)(struct device *, unsigned long secs);
- int (*irq_set_state)(struct device *, int enabled);
- int (*irq_set_freq)(struct device *, int freq);
- int (*read_callback)(struct device *, int data);
- int (*alarm_irq_enable)(struct device *, unsigned int enabled);
- int (*update_irq_enable)(struct device *, unsigned int enabled);
- };
3. struct rtc_time 结构
- struct rtc_time {
- int tm_sec;
- int tm_min;
- int tm_hour;
- int tm_mday;
- int tm_mon;
- int tm_year;
- int tm_wday;
- int tm_yday;
- int tm_isdst;
- };
三. class.c
1. 模块初始化函数:rtc_init
- static int __init rtc_init(void)
- {
- rtc_class = class_create(THIS_MODULE, "rtc");
- if (IS_ERR(rtc_class)) {
- printk(KERN_ERR "%s: couldn't create class\n", __FILE__);
- return PTR_ERR(rtc_class);
- }
- rtc_class->suspend = rtc_suspend;
- rtc_class->resume = rtc_resume;
- rtc_dev_init();
- rtc_sysfs_init(rtc_class);
- return 0;
- }
2. 为底层驱动提供接口:rtc_device_register,rtc_device_unregister
- 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;
- int id, err;
- if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
- err = -ENOMEM;
- goto exit;
- }
- mutex_lock(&idr_lock);
- err = idr_get_new(&rtc_idr, NULL, &id);
- mutex_unlock(&idr_lock);
- /*--------------------(1)---------------------*/
- if (err < 0)
- goto exit;
- id = id & MAX_ID_MASK;
- rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
- if (rtc == NULL) {
- err = -ENOMEM;
- goto exit_idr;
- }
- rtc->id = id;
- rtc->ops = ops;
- rtc->owner = owner;
- rtc->max_user_freq = 64;
- rtc->dev.parent = dev;
- rtc->dev.class = rtc_class;
- rtc->dev.release = rtc_device_release;
- mutex_init(&rtc->ops_lock);
- spin_lock_init(&rtc->irq_lock);
- spin_lock_init(&rtc->irq_task_lock);
- init_waitqueue_head(&rtc->irq_queue);
- strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
- dev_set_name(&rtc->dev, "rtc%d", id);
- /*-------------------(2)--------------------*/
- rtc_dev_prepare(rtc);
- err = device_register(&rtc->dev);
- if (err)
- goto exit_kfree;
- /*-------------------(3)--------------------*/
- rtc_dev_add_device(rtc);
- rtc_sysfs_add_device(rtc);
- rtc_proc_add_device(rtc);
- dev_info(dev, "rtc core: registered %s as %s\n",
- rtc->name, dev_name(&rtc->dev));
- /*-------------------(4)--------------------*/
- return rtc;
- exit_kfree:
- kfree(rtc);
- exit_idr:
- mutex_lock(&idr_lock);
- idr_remove(&rtc_idr, id);
- mutex_unlock(&idr_lock);
- exit:
- dev_err(dev, "rtc core: unable to register %s, err = %d\n",
- name, err);
- return ERR_PTR(err);
- }
(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文件系统接口。
|
评论暂时关闭