LINUX字符设备驱动模块分析(三)RTC设备驱动模型分析

      前面的几篇文章中,我们介绍了字符设备驱动模型以及在字符设备驱动模型基础上实现的混杂设备驱动模型、i2c通用字符设备驱动模型、spi通用字符设备驱动模型,这三种设备驱动模型既有相似处。本篇我们介绍rtc设备驱动模型。

 

rtc设备驱动模型的架构

我们首先介绍下rtc设备驱动模型的架构,然后再详细说明各部分的内容。

       rtc设备驱动模型在应用层提供了三种访问方式:通过procfs下的文件实现对rtc设备的操作、通过sysfs下的属性文件实现对rtc设备的操作、通过字符设备文件实现对rtc设备的操作。

rtc设备驱动模型为了支持这三种访问方法,对驱动模型也做了相应的划分,如下图所示为rtc设备驱动模型的

架构。

  1. 在rtc设备驱动模型的最上层,针对procfs 文件、sysfs 属性文件、字符设备文件相关的处理接口,分别抽象出rtc-dev相关接口、rtc-sysfs相关接口、rtc-procfs相关接口,分别存储在rtc-dev.c、rtc-sysfs.c、rtc-proc.c这三个文件中;
  2. 在rtc-dev.c、rtc-sysfs.c、rtc-proc.c中定义了这三类文件的操作接口(open、read、write、close…);
  3. 上述三类文件的操作接口(open、read、write、close…),会借助rtc interface接口,调用各设备的操作接口,rtc interface接口可以理解为调用设备驱动的桥梁;
  4. 针对rtc设备驱动,均需要实现rtc class ops中的方法,以便被rtc上层接口调用,从而完成与rtc设备的通信操作。
  5. 应用程序通过系统调用接口、vfs相关接口、设备文件系统的inode的操作接口、sysfs文件系统的inode的操作接口、procfs文件系统的inode的操作接口,方才进入rtc设备驱动模型的处理接口中。

rtc字符设备驱动模型的特点

         本次我们主要拿rtc设备驱动模型与i2c/spi通用字符设备驱动模型与混杂字符设备驱动模型的特点来进行对比。我们下面分析下rtc字符设备驱动模型相对于其他的字符设备驱动模型,存在哪些相同点以及异同点。

  1. 针对i2c/spi通用字符设备驱动而言,这两个模型,定义了统一的文件操作接口(open/read/write/ioctl/close等),然后在其通用的open接口中根据i2cdev_open查找到具体的i2c_client/spidev,然后将这些参数作为文件描述符的私有变量传递给read/write/ioctl接口,再read/write/ioctl接口中根据i2cdev/spidev类型变量调用i2c/spi的方法实现和具体设备的通信;
  2. 而在混杂设备驱动模型中,仅实现了open接口,在其open接口misc_open中,根据设备号在混杂设备的注册链表misc_list上查找对应的混杂设备,将其ops指针重新赋值给文件描述符的f_op指针,至此以后,即使用具体混杂设备注册的f_op指针进行操作。
  3. 针对i2c、spi的通用字符设备驱动模型以及混杂设备驱动模型而言,其主设备号是固定的(分别为89、153、10),而rtc字符设备驱动模型中,其主设备号是动态申请的(在接口rtc_dev_init中,通过调用alloc_chrdev_region动态申请了一个主设备号(次设备数为16))。
  4. 在i2c、spi、混杂字符设备驱动模型中,在申请了字符设备号的区域后,即针对该字符设备号区域创建了对应cdev,并注册至cdev_map,至此之后所有的添加的次设备均不需创建cdev,只需要通过device_create接口即可实现字符设备文件节点的创建,而无需再创建cdev类型变量。而在rtc字符设备驱动模型中,其只申请了字符设备区间,而每增加一个次设备,则需要创建一个cdev,并注册至cdev_map。这就可能存在了一个问题:在调用rtc_device_register接口进行cdev创建时,并不会检测该次设备号是否超过申请范围(申请的范围为0-15),这就导致调用rtc_device_register接口可能会创建次设备号超过15的cdev而不会报错,当然了一个设备中的rtc设备也不可能超过15个。(这个也是字符设备驱动模型的一个问题,字符设备驱动模型并没有将字符设备区域申请相关的部分与cdev注册相关的部分做强绑定,在之前的分析中,我们知道即使 char_device_struct与cdev没有关联,也可以进行字符设备的注册。换句话说,即使我不申请字符设备区间,直接调用cdev_add进行字符设备的注册也能成功,这是字符设备驱动模型的一个小问题)。
  5. 因rtc模块每一个次设备均创建了对应的cdev,因此在chrdev_open接口中,直接通过设备号可在cdev_map中查找到对应的cdev,即可找到对应的ops(而i2c、sip以及混杂设备的字符设备模型,其所有次设备均对应同一个cdev,因此在其cdev->ops->open中需要再根据次设备号查找对应的操作接口)。

 

rtc字符设备与rtc_device以及与cdev_map关联如下图所示,主要内容如下:

  1. 针对每一个新注册的次设备,均需要创建对应的cdev,并注册至cdev_map中去;
  2. 每一个次设备,其文件操作接口指针均是相同的(指向rtc_dev_fops),而rtc_dev_fops则通过调用rtc_device中的class_ops,实现调用各rtc设备的驱动接口,实现与rtc设备的通信操作。

本文主要介绍rtc设备驱动模型的框架,对rtc设备驱动模型做一个简要的概述,下一节我们做进一步分析。

 

RTC设备驱动模型相关的结构体变量

针对该设备驱动模型架构而言,我们靠什么实现架构内层级间的关联呢?

        那就是数据结构以及数据结构间的关联,针对rtc设备驱动模型而言,主要有两个结构体变量:struct rtc_device、struct rtc_class_ops 。其中struct rtc_class_ops 定义了各rtc设备的操作接口(包括读写时间、alarm的设置与读取等),即rtc设备对应的操作方法,也就是上面介绍的“RTC class ops”,这些接口是直接与rtc设备通信的。

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 (*read_callback)(struct device *, int data);
	int (*alarm_irq_enable)(struct device *, unsigned int enabled);
};

而rtc_device用于表示一个rtc设备,主要包括(定义如下所示):

  1. device类型变量(实现与设备驱动模型、sysfs的关联);
  2. rtc_class_ops类型的成员变量用于操作rtc设备的接口;
  3. cdev类型的变量,用于表示该rtc设备对应的字符设备。
  4. 而剩下的成员变量包含rtc设备名称、aie、uie、pie处理相关的变量(包括工作队列、等待队列等等)
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;

	/*如下变量主要用于aie、uie、pie相关的处理*/
	struct timerqueue_head timerqueue;
	struct rtc_timer aie_timer;
	struct rtc_timer uie_rtctimer;
	struct hrtimer pie_timer; /* sub second exp, so needs hrtimer */
	int pie_enabled;
	/*rtc驱动模型为该工作队列创建的对应的处理接口,而该处理接口即用来
	处理aie、uie timer相关的等待事件
	而rtc驱动模型也提供了该工作队列的调度接口,供各rtc设备驱动的中断处理接口
	来调用,从而实现对aie、uie的更新
	*/
	struct work_struct irqwork;
	/* Some hardware can't support UIE mode */
	int uie_unsupported;

#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
};

在前面的rtc驱动架构图中已经说明:

  1. 应用程序通过rtc相关的字符设备文件,完成对rtc的操作;
  2. 应用程序通过sysfs中 的属性文件,可完成对rtc的操作;
  3. 应用程序通过procfs中的文件,可完成对rtc的操作。

我们下面就来分析下RTC设备驱动模型中字符设备处理相关的结构体关联、sysfs文件处理相关的结构体关联。

 

字符设备处理相关的结构体关联

下面我们首先分析下字符设备文件节点相关的结构体关联图,如下图所示,具体关联说明如下:

  1. rtc_device通过其cdev类型的成员变量,实现了与cdev_map的关联(这样当系统调用open进入至字符设备的open接口chrdev_open,根据设备号,即可在cdev_map中查找到该rtc设备对应的cdev,进而即可找到其文件操作接口指针rtc_dev_ops);
  2. rtc_dev_ops中的操作接口(open、read、write、ioctl,即属于rtc dev if),一般是通过调用rtc interface层的接口,间接调用rtc设备的class_ops接口(当然针对open、ioctl、release接口是直接调用class_ops中对应的接口,而不需经过rtc interface层的接口)。

 

     该结构体之间的关联图,说明上一章介绍的RTC设备驱动模型的架构图中的“应用层<->设备文件系统接口<->rtc dev if<->rtc interface<->rtc class ops“。

 

 

sysfs文件处理相关的结构体关联

针对sysfs中的文件,一般是对应于device类型的一个属性,而针对rtc设备驱动模型,其device类型的属性定义如下:

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),
	{ },
};

下图是rtc_device、rtc_device->dev、kobject、kobj_type、device_attribute之间的关联。主要内容如下:

  1. rtc_device的device成员通过其内部的kobject变量,链接至devices_kset的kset中的list上中;
  2. 针对每一个rtc设备,均会为其在sysfs的/sys/device目录下创建对应的目录,然后根据该device的属性(针对rtc设备而言,其属性为rtc设备对应的类rtc_class->dev_attrs,即rtc_attrs数组,针对该数组中的每一个成员均对应下图中的一个sysfs__dirent(attr类型));
  3. 针对rtc设备的sysfs属性文件的访问,主要借助dev_sysfs_ops的store/show接口进行访问,而在其store/show接口中,针对device_attribute属性,找到对应属性的store/show接口,从而即访问至rtc sysfs if中的接口;
  4. rtc_sysfs_if中的接口,基本上是调用rtc interface中的接口,与上图中的rtc_dev<->rtc_interface<->rtc_class_ops是一样的。

     该结构体之间的关联图,说明上一章介绍的RTC设备驱动模型的架构图中的“应用层<->sysfs文件系统的inode接口<->rtc sysfs if<->rtc interface<->rtc class ops“。

 

     而针对procfs文件处理相关的结构体关联,由于还没有分析procfs相关的内容,此处暂且略过。

    以上便是字符设备处理相关的结构体关联、sysfs文件处理相关的结构体关联。我认为只要熟悉了上述两部分所说的结构体之间的关联图,基本上也就熟悉了rtc设备注册相关的内容。

 

RTC设备驱动模型的初始化接口

针对rtc设备驱动模型的初始化,主要包括rtc模块的class申请、字符设备区域的申请等内容,其初始化接口为rtc_init,其实现流程如下,主要功能说明如下:

  1. 完成rtc模块class的创建,每一个rtc device均属于该类;
  2. 完成rtc模块字符设备区域的申请;
  3. 完成针对rtc模块class的dev_attr的设置,针对rtc_class上的设备,均会为其创建dev_attr上定义的属性值。

RTC设备的注册

      针对rtc设备的创建,其实就是对rtc_device类型变量的设置,以及字符设备注册、device注册】主要包含如下几方面的内容:

  1. 提供针对rtc设备的访问接口(即实现rtc_class_ops中各接口);
  2. 将该rtc设备对应的字符设备注册至系统中(即注册至cdev_map中);
  3. 调用device_register,将rtc_device的dev成员变量注册至devices_kset中,并完成设备属性的创建;
  4. 调用proc_create_data,向系统挂载的procfs中注册rtc文件,并提供相应的操作方法。
  5. 设置该rtc设备的dev成员的class指针指向rtc_class。

       通过以上几步,即完成了rtc设备的注册,rtc设备注册的接口为rtc_device_register,该接口实现的主要功能也就是上面几个功能,就不再对代码进行细分,由读者自行理解吧。

 

 

 

RTC时间到系统时间的更新机制

    在系统启动后,完成RTC设备驱动的注册之后,LINUX系统会调用函数rtc_hctosys根据RTC时间更新系统时间(即墙上时间)。函数rtc_hctosys处理流程如下:

 

  1. 根据CONFIG_RTC_HCTOSYS_DEVICE,获取LINUX系统默认使用的RTC设备名称。针对CONFIG_RTC_HCTOSYS_DEVICE,其可通过make menuconfig进行配置,如下所示;
  2. 调用接口rtc_class_open,在rtc_class类上查找设备名称为CONFIG_RTC_HCTOSYS_DEVICE的rtc设备;
  3. 若上述步骤中找到rtc设备,则调用rtc_read_time接口获取rtc时间,并调用接口do_settimeofday设置系统时间,从而完成系统时间的更新。

如何实现一个RTC设备驱动

  1. 实现一个rtc_class_ops类型的变量,并根据具体的设备实现该类型中具体的接口;
  2. 调用rtc_device_register完成rtc设备的申请以及注册操作。
  3. 而针对一个实际的rtc设备,其一般为i2c设备,若为i2c设备,还要实现i2c设备驱动,然后在i2c设备驱动的probe接口中,实现上述b中rtc_device_register的调用。

 

结语

        至此完成RTC设备驱动模型的分析,本次分析仅仅分析了RTC设备驱动模型的架构以及相关的结构体变量等内容,而针对rtc_dev_fops、rtc_class_open、rtc_read_time、rtc_set_alarm等rtc interface if、rtc class if、rtc dev if、rtc proc if等接口,均没有细讲,还请感兴趣的同学自行分析,此处希望分析RTC设备驱动模型的架构,能够让读者能够对RTC设备驱动模型有一个感性的认知。

发布了140 篇原创文章 · 获赞 30 · 访问量 45万+

猜你喜欢

转载自blog.csdn.net/lickylin/article/details/103841941