Linux RTC 驱动模型分析(2)

来源:互联网 发布:linux 内存使用 编辑:程序博客网 时间:2024/06/05 07:30
四. rtc-dev.c 
        rtc-dev.c 初始化了一个file_operations结构--rtc_dev_fops,并定义了这些操作函数。
  1. rtc_dev_fops rtc基本的文件操作
 
  1. static const struct file_operations rtc_dev_fops = {  
  2. .owner      = THIS_MODULE,  
  3. .llseek     = no_llseek,  
  4. .read       = rtc_dev_read,  
  5. .poll       = rtc_dev_poll,  
  6. .unlocked_ioctl = rtc_dev_ioctl,  
  7. .open       = rtc_dev_open,  
  8. .release    = rtc_dev_release,  
  9. .fasync     = rtc_dev_fasync,  
  10. ;   
  2. 函数的实现(以rtc_dev_read为例) 
 
  1. rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)  
  2. {  
  3.     struct rtc_device *rtc = file->private_data;  
  4.   
  5.     DECLARE_WAITQUEUE(wait, current);  
  6.     unsigned long data;  
  7.     ssize_t ret;  
  8.     if (count != sizeof(unsigned int) && count < sizeof(unsigned long))  
  9.         return -EINVAL;  
  10.     add_wait_queue(&rtc->irq_queue, &wait);  
  11.     do {  
  12.         __set_current_state(TASK_INTERRUPTIBLE);  
  13.   
  14.         spin_lock_irq(&rtc->irq_lock);  
  15.         data = rtc->irq_data;  
  16.         rtc->irq_data = 0;  
  17.         spin_unlock_irq(&rtc->irq_lock);  
  18.   
  19.         if (data != 0) {  
  20.             ret = 0;  
  21.             break;  
  22.         }  
  23.         if (file->f_flags & O_NONBLOCK) {  
  24.             ret = -EAGAIN;  
  25.             break;  
  26.         }  
  27.         if (signal_pending(current)) {  
  28.             ret = -ERESTARTSYS;  
  29.             break;  
  30.         }  
  31.         schedule();  
  32.     } while (1);  
  33.     set_current_state(TASK_RUNNING);  
  34.     remove_wait_queue(&rtc->irq_queue, &wait);  
  35.   
  36.     if (ret == 0) {  
  37.         /* Check for any data updates */  
  38.         if (rtc->ops->read_callback)  
  39.             data = rtc->ops->read_callback(rtc->dev.parent,  
  40.                                data);  
  41.   
  42.         if (sizeof(int) != sizeof(long) &&  
  43.             count == sizeof(unsigned int))  
  44.             ret = put_user(data, (unsigned int __user *)buf) ?:  
  45.                 sizeof(unsigned int);  
  46.         else  
  47.             ret = put_user(data, (unsigned long __user *)buf) ?:  
  48.                 sizeof(unsigned long);  
  49.     }  
  50.     return ret;  
  51. }  
        这里的read不是应用程序用来获取时间的,而是有其他的作用,他帮助应用程序周期性的完成一些工作。如果要使用这个功能,应用程序首先保证RTC驱动程序提供这样的功能。这个功能是这样实现的:进程读取/dev/rtc(n),进程睡眠直到RTC中断将他唤醒。我们可以发现,这里的睡眠是ldd3中提到的手工睡眠。这个函数的手工休眠过程如下:首先调用DECLARE_WAITQUEUE(wait, current),声明一个等待队列入口,然后调用add_wait_queue将这个入口加入到RTC的irq等待队列里,然后进入循环。在循环里首先把进程的状态改成TASK_INTERRUPTIBLE,这样进程就不能再被调度运行。但是现在进程还在运行,没有进入睡眠状态。程序然后读取RTC里面的irq_data,如果不是零,那么程序跳出这个循环,进程不会睡眠。因为这个irq_data在rtc的中断处理程序会被赋值,而读过之后就会清零,所以如果数据不是零的话说明发生过一次中断。如果是零那么没有发生中断,调用schedule,进程会被调度出可运行队列,从而让出处理器,真正进入睡眠。跳出循环代表被唤醒,然后将进程状态改变为可运行,移除等待队列入口。最后将读回的数据传给用户空间。
五. interface.c 
        interface.c里的所有函数的实现都对应于rtc-dev.c 中ioctl相应的命令。对应关系如下:
RTC_ALM_READ                     rtc_read_alarm           读取闹钟时间
RTC_ALM_SET                      rtc_set_alarm            设置闹钟时间
RTC_RD_TIME                      rtc_read_time            读取时间与日期
RTC_SET_TIME                     rtc_set_time             设置时间与日期
RTC_PIE_ON RTC_PIE_OFF           rtc_irq_set_state              开关RTC全局中断的函数
RTC_AIE_ON RTC_AIE_OFF           rtc_alarm_irq_enable     使能禁止RTC闹钟中断
RTC_UIE_OFF RTC_UIE_ON           rtc_update_irq_enable    使能禁止RTC更新中断
RTC_IRQP_SET                     rtc_irq_set_freq         设置中断的频率
       以上就是所有ioctl的命令与实现的对应关系。其中如果不涉及中断的话,有两个命令需要我们特别关心一下,就是RTC_RD_TIME与RTC_SET_TIME。因为RTC最基本的功能就是提供时间与日期。这两个命令恰恰是获取时间和设置时间。下面分析一下这两个命令的实现,也就是rtc_set_alarm与rtc_read_time函数的实现:
  1. rtc_read_time 函数
 
  1. int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)  
  2. {  
  3.     int err;  
  4.   
  5.     err = mutex_lock_interruptible(&rtc->ops_lock);  
  6.     if (err)  
  7.         return err;  
  8.   
  9.     if (!rtc->ops)  
  10.         err = -ENODEV;  
  11.     else if (!rtc->ops->read_time)  
  12.         err = -EINVAL;  
  13.     else {  
  14.         memset(tm, 0, sizeof(struct rtc_time));  
  15.         err = rtc->ops->read_time(rtc->dev.parent, tm);  
  16.     }  
  17.   
  18.     mutex_unlock(&rtc->ops_lock);  
  19.     return err;  
  20. }  
        这个函数用了一个信号来保证在同一时刻只有一个进程可以获取时间。锁定了这个信号量后,调用rtc->ops里面read函数,这个函数是由具体的驱动程序实现的,操作底层硬件。读回的时间存放在rtc_time结构里面的。
  2. rtc_set_time 函数
 
  1. int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)  
  2. {  
  3.     int err;  
  4.   
  5.     err = rtc_valid_tm(tm);  
  6.     if (err != 0)  
  7.         return err;  
  8.   
  9.     err = mutex_lock_interruptible(&rtc->ops_lock);  
  10.     if (err)  
  11.         return err;  
  12.   
  13.     if (!rtc->ops)  
  14.         err = -ENODEV;  
  15.     else if (rtc->ops->set_time)  
  16.         err = rtc->ops->set_time(rtc->dev.parent, tm);  
  17.     else if (rtc->ops->set_mmss) {  
  18.         unsigned long secs;  
  19.         err = rtc_tm_to_time(tm, &secs);  
  20.         if (err == 0)  
  21.             err = rtc->ops->set_mmss(rtc->dev.parent, secs);  
  22.     } else  
  23.         err = -EINVAL;  
  24.   
  25.     mutex_unlock(&rtc->ops_lock);  
  26.     return err;  
  27. }  
        这个函数其实和rtc_read_time函数差不多,同样是锁定信号量,同样是调用底层驱动函数。但是这里的设置时间提供了两个调用:一个是set_time,一个是set_mmss。因为有的RTC硬件只计算秒数,不关心墙钟时间,所以如果是这样的RTC,必须实现set_mmss来设置时间。
六. rtc-sysfs.c 部分
        这个部分主要是有关sysfs的操作。rtc-sysfs.c中定义了这样一个设备属性组,如下:
 
  1. static struct device_attribute rtc_attrs[] = {  
  2.     __ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL),  
  3.     __ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL),  
  4.     __ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL),  
  5.     __ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL),  
  6.     __ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq,  
  7.           rtc_sysfs_set_max_user_freq),  
  8.     __ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys, NULL),  
  9.      { },  
  10. };  

       这个属性组是在class.c的模块初始化函数中,由rtc_sysfs_init函数赋值给rtc_class->dev_attrs的,以后属于这个类的设备都会有这些属性。但是我们知道要想一个设备结构拥有一种属性,必须调用device_create_file,这样才会使这个属性出现在sysfs相关设备目录里。但是在这里的代码中只是给这个类的dev_attrs域赋值了这个属性组指针,而没有调用device_create_file。我原来以为是在rtc_device_resgister函数中,由rtc_sysfs_add_device完成这个工作,但是这个函数只是给设备添加了闹钟属性,并没有处理这个属性组。最后发现这个工作是由device_register来完成的。这里的调用关系有点复杂:

device_register调用device_add

device_add调用 device_add_attrs

 device_add_attrs调用device_add_attributes

device_add_attributes调用device_create_file来完成设备的属性设置的。

        设置完属性后,在/sys/class/rtc/rtc(n)的目录下就会出现name,date,time等文件,用户读这些文件的时候就会调用相应的函数。如读取name文件,就会调用rtc_sysfs_show_name函数,这个函数也是在rtc-sysfs.c中实现的,作用是读取并显示时间。

七. rtc-proc.c 
       这个文件提供RTC的proc文件系统接口。proc文件系统是软件创建的文件系统,内核通过他向外界导出信息,下面的每一个文件都绑定一个函数,当用户读取这个文件的时候,这个函数会向文件写入信息。rtc-proc.c中初始化了一个文件操作:
 
  1. static const struct file_operations rtc_proc_fops = {  
  2.     .open       = rtc_proc_open,  
  3.     .read       = seq_read,  
  4.     .llseek     = seq_lseek,  
  5.     .release    = rtc_proc_release,  
  6. };  
RTC驱动在向RTC核心注册自己的时候,由注册函数rtc_device_resgister调用rtc_proc_add_device来实现proc接口的初始化,这个函数如下定义:
  1. void rtc_proc_add_device(struct rtc_device *rtc)  
  2. {  
  3.     if (rtc->id == 0)  
  4.         proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc);  
  5. }  
他主要调用了proc_create_data。proc_create_data完成创建文件节点的作用,并将文件的操作函数与节点联系起来。调用这个函数后,在/proc/driver目录下就会有一个文件rtc,应用程序打开这个文件就会调用rtc_proc_open函数,这个函数如下定义:
  1. static int rtc_proc_open(struct inode *inode, struct file *file)  
  2. {  
  3.         struct rtc_device *rtc = PDE(inode)->data;  
  4.         if (!try_module_get(THIS_MODULE))  
  5.                 return -ENODEV;  
  6.        return single_open(file, rtc_proc_show, rtc);  
  7. }  
我们知道一个proc的文件必须与一个操作函数组成一个proc入口项,这个文件才能正常工作。这个函数最主要作用就是调用single_open,创建一个proc文件入口项,使其操作函数是rtc_proc_show,并初始化seq_file接口。rtc_proc_show函数如下定义:
  1. static int rtc_proc_show(struct seq_file *seq, void *offset)  
  2. {  
  3.     int err;  
  4.     struct rtc_device *rtc = seq->private;  
  5.     const struct rtc_class_ops *ops = rtc->ops;  
  6.     struct rtc_wkalrm alrm;  
  7.     struct rtc_time tm;  
  8.   
  9.     err = rtc_read_time(rtc, &tm);  
  10.     if (err == 0) {  
  11.         seq_printf(seq,  
  12.             "rtc_time\t: %02d:%02d:%02d\n"  
  13.             "rtc_date\t: %04d-%02d-%02d\n",  
  14.             tm.tm_hour, tm.tm_min, tm.tm_sec,  
  15.             tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);  
  16.     }  
  17.   
  18.     err = rtc_read_alarm(rtc, &alrm);  
  19.     if (err == 0) {  
  20.         seq_printf(seq, "alrm_time\t: ");  
  21.         if ((unsigned int)alrm.time.tm_hour <= 24)  
  22.             seq_printf(seq, "%02d:", alrm.time.tm_hour);  
  23.         else  
  24.             seq_printf(seq, "**:");  
  25.         if ((unsigned int)alrm.time.tm_min <= 59)  
  26.             seq_printf(seq, "%02d:", alrm.time.tm_min);  
  27.         else  
  28.             seq_printf(seq, "**:");  
  29.         if ((unsigned int)alrm.time.tm_sec <= 59)  
  30.             seq_printf(seq, "%02d\n", alrm.time.tm_sec);  
  31.         else  
  32.             seq_printf(seq, "**\n");  
  33.   
  34.         seq_printf(seq, "alrm_date\t: ");  
  35.         if ((unsigned int)alrm.time.tm_year <= 200)  
  36.             seq_printf(seq, "%04d-", alrm.time.tm_year + 1900);  
  37.         else  
  38.             seq_printf(seq, "****-");  
  39.         if ((unsigned int)alrm.time.tm_mon <= 11)  
  40.             seq_printf(seq, "%02d-", alrm.time.tm_mon + 1);  
  41.         else  
  42.             seq_printf(seq, "**-");  
  43.         if (alrm.time.tm_mday && (unsigned int)alrm.time.tm_mday <= 31)  
  44.             seq_printf(seq, "%02d\n", alrm.time.tm_mday);  
  45.         else  
  46.             seq_printf(seq, "**\n");  
  47.         seq_printf(seq, "alarm_IRQ\t: %s\n",  
  48.                 alrm.enabled ? "yes" : "no");  
  49.         seq_printf(seq, "alrm_pending\t: %s\n",  
  50.                 alrm.pending ? "yes" : "no");  
  51.     }  
  52.   
  53.     seq_printf(seq, "24hr\t\t: yes\n");  
  54.   
  55.     if (ops->proc)  
  56.         ops->proc(rtc->dev.parent, seq);  
  57.   
  58.     return 0;  
  59. }  
这个函数就是最后给用户显示信息的函数了,可以看出他通过调用rtc_deivce中的操作函数,读取时间,日期和一些其他的信息显示给用户。 
六. 总结

RTC核心使底层硬件对用户来说是透明的,并且减少了编写驱动程序的工作量。RTC新的驱动接口提供了更多的功能,使系统可以同时存在多个RTC。/dev,sysfs,proc这三种机制的实现使得应用程序能灵活的使用RTC,RTC核心虽然表面上看上去很简单,但是还是涉及到很多知识,有些东西书上讲的还是不够详细,还需要通过分析代码加深理解。 另外RTC核心代码的组织方式也值得学习,不同功能的代码放在不同的文件中,简单明了。  

原创粉丝点击