【北京迅为】i.MX6ULL终结者Linux RTC驱动实验Linux内核RTC驱动简介

在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)

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/BeiJingXunWei/article/details/112358429