在Linux内核中,RTC设备驱动也是一个标准的字符设备驱动,在应用程序中通过open、close、read、write和ioctl等函数来操作RTC设备。
在Linux内核中,RTC设备也有一个自己的设备结构体rtc_device,所以在RTC设备驱动的流程就是rtc_device结构体的申请、初始化,然后将rtc_device结构体注册到内核中。可以看出大多数设备都是这样一个流程,只不过对用的设备结构体和函数不同罢了。
rtc_device 结构体定义在 include/linux/rtc.h 文件中,内容如下:
104 struct rtc_device
105 {
106 struct device dev; /* 设备 */
107 struct module *owner;
108
109 int id; /* ID */
110 char name[RTC_DEVICE_NAME_SIZE]; /* 名字 */
111
112 const struct rtc_class_ops *ops; /* RTC 设备底层操作函数 */
113 struct mutex ops_lock;
114
115 struct cdev char_dev; /* 字符设备 */
116 unsigned long flags;
117
118 unsigned long irq_data;
119 spinlock_t irq_lock;
120 wait_queue_head_t irq_queue;
121 struct fasync_struct *async_queue;
122
123 struct rtc_task *irq_task;
124 spinlock_t irq_task_lock;
125 int irq_freq;
126 int max_user_freq;
127
128 struct timerqueue_head timerqueue;
129 struct rtc_timer aie_timer;
130 struct rtc_timer uie_rtctimer;
131 struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */
132 int pie_enabled;
133 struct work_struct irqwork;
134 /* Some hardware can't support UIE mode */
135 int uie_unsupported;
......
147 };
rtc_device 结构体中虽然有很多成员变量,但是大多数都不用我们操心,我们需要重点关心的是ops成员变量,这是一个rtc_class_ops类型的指针变量。rtc_class_ops结构体是RTC设备最底层的操作函数的集合,包括读取、写入时间等功能。因此rtc_class_ops结构体中实现的功能函数在不同的RTC设备中有所不同,rtc_class_ops此结构体定义在include/linux/rtc.h 文件中,内容如下:
71 struct rtc_class_ops {
72 int (*open)(struct device *);
73 void (*release)(struct device *);
74 int (*ioctl)(struct device *, unsigned int, unsigned long);
75 int (*read_time)(struct device *, struct rtc_time *);
76 int (*set_time)(struct device *, struct rtc_time *);
77 int (*read_alarm)(struct device *, struct rtc_wkalrm *);
78 int (*set_alarm)(struct device *, struct rtc_wkalrm *);
79 int (*proc)(struct device *, struct seq_file *);
80 int (*set_mmss64)(struct device *, time64_t secs);
81 int (*set_mmss)(struct device *, unsigned long secs);
82 int (*read_callback)(struct device *, int data);
83 int (*alarm_irq_enable)(struct device *, unsigned int enabled);
84 };
从tc_class_ops结构体的成员变量中就可以看出,里面集成了所有的功能。但是需要注意的是,rtc_class_ops 中的这些函数只是最底层的 RTC 设备操作函数,并不是提供给应用层的file_operations 函数操作集。RTC 是个字符设备,那么肯定有字符设备的file_operations 函数操作集,Linux 内核提供了一个 RTC 通用字符设备驱动文件,文件名为drivers/rtc/rtc-dev.c,rtc-dev.c 文件提供了所有 RTC 设备共用的 file_operations 函数操作集,如下所示:
448 static const struct file_operations rtc_dev_fops = {
449 .owner = THIS_MODULE,
450 .llseek = no_llseek,
451 .read = rtc_dev_read,
452 .poll = rtc_dev_poll,
453 .unlocked_ioctl = rtc_dev_ioctl,
454 .open = rtc_dev_open,
455 .release = rtc_dev_release,
456 .fasync = rtc_dev_fasync,
457 };
标准的字符设备的操作集合,应用程序就可以通过这个结构体中的函数来实现自己需要的功能。比如要读取、设置时间,那么会通过rtc_dev_ioctl函数来实现,rtc_dev_ioctl函数会在去调用rtc_class_ops 结构体中的 read_time、set_time 等函数来对具体 RTC 设备的读写操作。我们简单来看一下 rtc_dev_ioctl 函数,函数部分内容如下:
218 static long rtc_dev_ioctl(struct file *file,
219 unsigned int cmd, unsigned long arg)
220 {
221 int err = 0;
222 struct rtc_device *rtc = file->private_data;
223 const struct rtc_class_ops *ops = rtc->ops;
224 struct rtc_time tm;
225 struct rtc_wkalrm alarm;
226 void __user *uarg = (void __user *) arg;
227
228 err = mutex_lock_interruptible(&rtc->ops_lock);
229 if (err)
230 return err;
......
269 switch (cmd) {
......
333 case RTC_RD_TIME: /* 读取时间 */
334 mutex_unlock(&rtc->ops_lock);
335
336 err = rtc_read_time(rtc, &tm);
337 if (err < 0)
338 return err;
339
340 if (copy_to_user(uarg, &tm, sizeof(tm)))
341 err = -EFAULT;
342 return err;
343
344 case RTC_SET_TIME: /* 设置时间 */
345 mutex_unlock(&rtc->ops_lock);
346
347 if (copy_from_user(&tm, uarg, sizeof(tm)))
348 return -EFAULT;
349
350 return rtc_set_time(rtc, &tm);
......
401 default:
402 /* Finally try the driver's ioctl interface */
403 if (ops->ioctl) {
404 err = ops->ioctl(rtc->dev.parent, cmd, arg);
405 if (err == -ENOIOCTLCMD)
406 err = -ENOTTY;
407 } else
408 err = -ENOTTY;
409 break;
410 }
411
412 done:
413 mutex_unlock(&rtc->ops_lock);
414 return err;
415 }
第 333 行,RTC_RD_TIME 为时间读取命令。
第 336 行,如果是读取时间的话就调用 rtc_read_time 函数获取当前 RTC 时钟,
在rtc_read_time 函数中,rtc_read_time 会调用__rtc_read_time 函数,__rtc_read_time 函数内容如下:
23 static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
24 {
25 int err;
26 if (!rtc->ops)
27 err = -ENODEV;
28 else if (!rtc->ops->read_time)
29 err = -EINVAL;
30 else {
31 memset(tm, 0, sizeof(struct rtc_time));
32 err = rtc->ops->read_time(rtc->dev.parent, tm);
33 if (err < 0) {
34 dev_dbg(&rtc->dev, "read_time: fail to read: %d\n",
35 err);
36 return err;
37 }
38
39 err = rtc_valid_tm(tm);
40 if (err < 0)
41 dev_dbg(&rtc->dev, "read_time: rtc_time isn't valid\n");
42 }
43 return err;
44 }
第32行,__rtc_read_time函数在调用rtc_class_ops 结构体中的read_time 来从 RTC 设备中获取当前时间。
在rtc_dev_ioctl 函数中对其他命令都是类似的,最终都会调用到rtc_class_ops 结构体中对用的函数。所以在Linux内核中RTC驱动的调用流程就是:
应用程序(ioctl) —>内核层(rtc_dev_ioctl) -->设备驱动层(rtc_class_ops)
rtc_device_register函数:将rtc_device 结构体注册到内核中去。此函数会申请一个rtc_device并且初始化这个rtc_device,最后向调用者返回这个 rtc_device,函数原型如下:
struct rtc_device *rtc_device_register(const char *name,
struct device *dev,
const struct rtc_class_ops *ops,
struct module *owner)
name:设备名字。
dev:设备。
ops:RTC 底层驱动函数集。
owner:驱动模块拥有者。
返回值:注册成功的话就返回 rtc_device,错误的话会返回一个负值。
rtc_device_unregister 函数:用了注销注册的rtc_device结构体。函数原型如下:
void rtc_device_unregister(struct rtc_device *rtc)