rtc-dev.c 初始化了一个file_operations结构--rtc_dev_fops,并定义了这些操作函数。
1. rtc_dev_fops rtc基本的文件操作
- static const struct file_operations rtc_dev_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = rtc_dev_read,
- .poll = rtc_dev_poll,
- .unlocked_ioctl = rtc_dev_ioctl,
- .open = rtc_dev_open,
- .release = rtc_dev_release,
- .fasync = rtc_dev_fasync,
- ;
- rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
- {
- struct rtc_device *rtc = file->private_data;
- DECLARE_WAITQUEUE(wait, current);
- unsigned long data;
- ssize_t ret;
- if (count != sizeof(unsigned int) && count < sizeof(unsigned long))
- return -EINVAL;
- add_wait_queue(&rtc->irq_queue, &wait);
- do {
- __set_current_state(TASK_INTERRUPTIBLE);
- spin_lock_irq(&rtc->irq_lock);
- data = rtc->irq_data;
- rtc->irq_data = 0;
- spin_unlock_irq(&rtc->irq_lock);
- if (data != 0) {
- ret = 0;
- break;
- }
- if (file->f_flags & O_NONBLOCK) {
- ret = -EAGAIN;
- break;
- }
- if (signal_pending(current)) {
- ret = -ERESTARTSYS;
- break;
- }
- schedule();
- } while (1);
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&rtc->irq_queue, &wait);
- if (ret == 0) {
- /* Check for any data updates */
- if (rtc->ops->read_callback)
- data = rtc->ops->read_callback(rtc->dev.parent,
- data);
- if (sizeof(int) != sizeof(long) &&
- count == sizeof(unsigned int))
- ret = put_user(data, (unsigned int __user *)buf) ?:
- sizeof(unsigned int);
- else
- ret = put_user(data, (unsigned long __user *)buf) ?:
- sizeof(unsigned long);
- }
- return ret;
- }
五. 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 函数
- int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
- {
- int err;
- err = mutex_lock_interruptible(&rtc->ops_lock);
- if (err)
- return err;
- if (!rtc->ops)
- err = -ENODEV;
- else if (!rtc->ops->read_time)
- err = -EINVAL;
- else {
- memset(tm, 0, sizeof(struct rtc_time));
- err = rtc->ops->read_time(rtc->dev.parent, tm);
- }
- mutex_unlock(&rtc->ops_lock);
- return err;
- }
2. rtc_set_time 函数
- int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
- {
- int err;
- err = rtc_valid_tm(tm);
- if (err != 0)
- return err;
- err = mutex_lock_interruptible(&rtc->ops_lock);
- if (err)
- return err;
- if (!rtc->ops)
- err = -ENODEV;
- else if (rtc->ops->set_time)
- err = rtc->ops->set_time(rtc->dev.parent, tm);
- else if (rtc->ops->set_mmss) {
- unsigned long secs;
- err = rtc_tm_to_time(tm, &secs);
- if (err == 0)
- err = rtc->ops->set_mmss(rtc->dev.parent, secs);
- } else
- err = -EINVAL;
- mutex_unlock(&rtc->ops_lock);
- return err;
- }
六. rtc-sysfs.c 部分
这个部分主要是有关sysfs的操作。rtc-sysfs.c中定义了这样一个设备属性组,如下:
- static struct device_attribute rtc_attrs[] = {
- __ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL),
- __ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL),
- __ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL),
- __ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL),
- __ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq,
- rtc_sysfs_set_max_user_freq),
- __ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys, NULL),
- { },
- };
这个属性组是在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中初始化了一个文件操作:
- static const struct file_operations rtc_proc_fops = {
- .open = rtc_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = rtc_proc_release,
- };
- void rtc_proc_add_device(struct rtc_device *rtc)
- {
- if (rtc->id == 0)
- proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc);
- }
- static int rtc_proc_open(struct inode *inode, struct file *file)
- {
- struct rtc_device *rtc = PDE(inode)->data;
- if (!try_module_get(THIS_MODULE))
- return -ENODEV;
- return single_open(file, rtc_proc_show, rtc);
- }
- static int rtc_proc_show(struct seq_file *seq, void *offset)
- {
- int err;
- struct rtc_device *rtc = seq->private;
- const struct rtc_class_ops *ops = rtc->ops;
- struct rtc_wkalrm alrm;
- struct rtc_time tm;
- err = rtc_read_time(rtc, &tm);
- if (err == 0) {
- seq_printf(seq,
- "rtc_time\t: %02d:%02d:%02d\n"
- "rtc_date\t: %04d-%02d-%02d\n",
- tm.tm_hour, tm.tm_min, tm.tm_sec,
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
- }
- err = rtc_read_alarm(rtc, &alrm);
- if (err == 0) {
- seq_printf(seq, "alrm_time\t: ");
- if ((unsigned int)alrm.time.tm_hour <= 24)
- seq_printf(seq, "%02d:", alrm.time.tm_hour);
- else
- seq_printf(seq, "**:");
- if ((unsigned int)alrm.time.tm_min <= 59)
- seq_printf(seq, "%02d:", alrm.time.tm_min);
- else
- seq_printf(seq, "**:");
- if ((unsigned int)alrm.time.tm_sec <= 59)
- seq_printf(seq, "%02d\n", alrm.time.tm_sec);
- else
- seq_printf(seq, "**\n");
- seq_printf(seq, "alrm_date\t: ");
- if ((unsigned int)alrm.time.tm_year <= 200)
- seq_printf(seq, "%04d-", alrm.time.tm_year + 1900);
- else
- seq_printf(seq, "****-");
- if ((unsigned int)alrm.time.tm_mon <= 11)
- seq_printf(seq, "%02d-", alrm.time.tm_mon + 1);
- else
- seq_printf(seq, "**-");
- if (alrm.time.tm_mday && (unsigned int)alrm.time.tm_mday <= 31)
- seq_printf(seq, "%02d\n", alrm.time.tm_mday);
- else
- seq_printf(seq, "**\n");
- seq_printf(seq, "alarm_IRQ\t: %s\n",
- alrm.enabled ? "yes" : "no");
- seq_printf(seq, "alrm_pending\t: %s\n",
- alrm.pending ? "yes" : "no");
- }
- seq_printf(seq, "24hr\t\t: yes\n");
- if (ops->proc)
- ops->proc(rtc->dev.parent, seq);
- return 0;
- }
六. 总结
RTC核心使底层硬件对用户来说是透明的,并且减少了编写驱动程序的工作量。RTC新的驱动接口提供了更多的功能,使系统可以同时存在多个RTC。/dev,sysfs,proc这三种机制的实现使得应用程序能灵活的使用RTC,RTC核心虽然表面上看上去很简单,但是还是涉及到很多知识,有些东西书上讲的还是不够详细,还需要通过分析代码加深理解。 另外RTC核心代码的组织方式也值得学习,不同功能的代码放在不同的文件中,简单明了。