IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> [ Linux RK3568 ] RTC驱动代码解析 -> 正文阅读

[系统运维][ Linux RK3568 ] RTC驱动代码解析

平台:RK3568 内核版本:4.19.194

RTC简介

RTC 也就是实时时钟,用于记录当前系统时间,对于 Linux 系统而言时间是非常重要的,使用 Linux 设备的时候也需要查看时间。RTC是Linux的时间系统。

RTC 设备驱动是一个标准的字符设备驱动,应用程序通过 open、 release、 read、 write 和 ioctl 等函数完成对 RTC 设备的操作。

RTC相关结构体

// Linux内核将 RTC 设备抽象为 rtc_device 结构体
struct rtc_device {
    struct device dev;                 // 设备
    struct module *owner;

    int id;                            // ID

    const struct rtc_class_ops *ops;   // RTC 设备底层操作函数
    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;

    int irq_freq;
    int max_user_freq;

    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;
    struct work_struct irqwork;
    /* Some hardware can't support UIE mode */
    int uie_unsupported;

    /* Number of nsec it takes to set the RTC clock. This influences when
     * the set ops are called. An offset:
     *   - of 0.5 s will call RTC set for wall clock time 10.0 s at 9.5 s
     *   - of 1.5 s will call RTC set for wall clock time 10.0 s at 8.5 s
     *   - of -0.5 s will call RTC set for wall clock time 10.0 s at 10.5 s
     */
    long set_offset_nsec;

    bool registered;

    struct nvmem_device *nvmem;
    /* Old ABI support */
    bool nvram_old_abi;
    struct bin_attribute *nvram;

    time64_t range_min;
    timeu64_t range_max;
    time64_t start_secs;
    time64_t offset_secs;
    bool set_start_time;

#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 设备的最底层操作函数集合,用户编写
// 只是最底层的 RTC 设备操作函数,并不是提供给应用层的file_operations 函数操作集。
struct rtc_class_ops {
    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_mmss64)(struct device *, time64_t secs);
    int (*set_mmss)(struct device *, unsigned long secs);
    int (*read_callback)(struct device *, int data);
    int (*alarm_irq_enable)(struct device *, unsigned int enabled);
    int (*read_offset)(struct device *, long *offset);
    int (*set_offset)(struct device *, long offset);
};

// 提供给应用层的file_operations 函数操作集 drivers/rtc/rtc-dev.c 
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整体调用框架

在这里插入图片描述

RTC代码解析

涉及到的目录功能总结

  • class.c:向linux设备模型核心注册了一个类RTC,提供了RTC子系统的一些公共函数,让各个RTC驱动注册集成到我们的linux内核中,向驱动程序提供了注册/注销接口。
  • rtc-dev.c:定义了基本的设备文件操作函数,用户程序与RTC驱动的接口函数,这里定义了每个ioctl命令需要调用的函数,还有open,read等。
  • interface.c:提供了ioctl各个命令需要调用的函数。
  • rtc-sysfs.c:与sysfs有关,提供通过sys文件系统操作pcf8563。
  • rtc-proc.c:与proc文件系统有关,提供通过proc文件系统操作pcf8563。
  • hctosys.c:系统起来之后会调用到这个文件中的rtc_hctosys()函数,主要功能是系统起来的时候去读RTC硬件中的时间,然后更新我们的系统时间。
  • rtc.h:定义了与RTC有关的数据结构。

RTC驱动注册函数解析

// 驱动路径:drivers/rtc/class.c
struct class *rtc_class;
rtc_init
    -> rtc_class = class_create(THIS_MODULE, "rtc");      // 创建名为rtc的class
    -> rtc_class->pm = RTC_CLASS_DEV_PM_OPS;              // 提供休眠唤醒相关接口suspend/resume
    -> rtc_dev_init();                                    // 动态申请/dev/rtcN的设备号
    	-> alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");    // RTC_DEV_MAX=16
subsys_initcall(rtc_init);                                // rtc_sysfs_init():rtc类具有的device_attribute属性

rtc_device_register
    -> id = rtc_device_get_id(dev);           // Linux支持多个RTC设备,所以需要为每一个设备分配一个ID
    	-> ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);     // 对应与/dev/rtc0  /dev/rtc1  /dev/rtcN
     -> rtc = rtc_allocate_device();
     	-> struct rtc_device *rtc;
     	-> rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);         // 创建rtc_device设备(对象)并初始化
      	-> ...... rtc->dev.class = rtc_class;  ......       // rtc_init创建的rtc_class
    	// rtc设备中相关锁,等待队列的初始化
     	-> mutex_init(&rtc->ops_lock);
    	-> spin_lock_init(&rtc->irq_lock);
    	-> init_waitqueue_head(&rtc->irq_queue);
     	// 初始化工作队列rtc_timer_do_work
     	-> timerqueue_init_head(&rtc->timerqueue);
    	-> INIT_WORK(&rtc->irqwork, rtc_timer_do_work);
     	// 初始化rtc闹钟中断
    	-> rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc);
     	// RTC更新中断
     	-> rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc);
      	// RTC周期性中断
      	-> hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    	-> rtc->pie_timer.function = rtc_pie_update_irq;
    	-> rtc->pie_enabled = 0;
    -> rtc->id = id;
    -> rtc->ops = ops;            // 对应RTC驱动填充的底层操作函数
    -> rtc->owner = owner;
    -> rtc->dev.parent = dev;
    -> dev_set_name(&rtc->dev, "rtc%d", id);   // 设置rtc的dev成员中的name域
    -> __rtc_read_alarm(rtc, &alrm);        // 检查是否设置闹钟
    -> if (!err && !rtc_valid_tm(&alrm.time))    // 如果RTC芯片中设置有效的Alarm,则初始化,加入队列中
        rtc_initialize_alarm(rtc, &alrm);
    -> rtc_dev_prepare(rtc);               // /dev/rtc0的rtc作为字符设备进行初始化
    	-> cdev_init(&rtc->char_dev, &rtc_dev_fops);     // rtc_dev_fops接口操作函数结构体
     		-> cdev->ops = fops;
    -> cdev_device_add(&rtc->char_dev, &rtc->dev);
    	-> cdev_add(cdev, dev->devt, 1);   // rtc设备作为字符设备添加到系统    生成/dev/rtc0
     -> rtc_proc_add_device(rtc);        // /proc/rtc
     	-> proc_create_single_data("driver/rtc", 0, NULL, rtc_proc_show, 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,
};

kernel中__init类型函数都位于.init.text段中,对应的在.initcall.init段中保存相应的函数指针。系统在启动过程中,根据定义在段中的等级值(0~7)从低到高依次执行。定义:

// include/linux/init.h
#define pure_initcall(fn)       __define_initcall(fn, 0)
#define core_initcall(fn)       __define_initcall(fn, 1)
#define core_initcall_sync(fn)      __define_initcall(fn, 1s)
#define postcore_initcall(fn)       __define_initcall(fn, 2)
#define postcore_initcall_sync(fn)  __define_initcall(fn, 2s)
#define arch_initcall(fn)       __define_initcall(fn, 3)
#define arch_initcall_sync(fn)      __define_initcall(fn, 3s)
#define subsys_initcall(fn)     __define_initcall(fn, 4)
#define subsys_initcall_sync(fn)    __define_initcall(fn, 4s)
#define fs_initcall(fn)         __define_initcall(fn, 5)
#define fs_initcall_sync(fn)        __define_initcall(fn, 5s)
#define rootfs_initcall(fn)     __define_initcall(fn, rootfs)
#define device_initcall(fn)     __define_initcall(fn, 6)
#define device_initcall_sync(fn)    __define_initcall(fn, 6s)
#define late_initcall(fn)       __define_initcall(fn, 7)
#define late_initcall_sync(fn)      __define_initcall(fn, 7s)

应用层调用驱动流程解析

rtc_dev_ioctl (struct file *file, unsigned int cmd, unsigned long arg)           // drivers/rtc/rtc-dev.c   
    -> struct rtc_device *rtc = file->private_data;               // 获取到rtc设备
    -> switch (cmd) 
    -> case RTC_RD_TIME:
    -> rtc_read_time     // drivers/rtc/interface.c
        -> __rtc_read_time
            -> rtc->ops->read_time(rtc->dev.parent, tm);
    -> case RTC_SET_TIME:
    -> rtc_set_time      // drivers/rtc/interface.c
    	-> rtc_valid_tm(tm);       // 参数检查
     // 调用rtc_device中ops结构体的函数指针
     // ops结构体的函数指针已经在RTC驱动中被赋值
    -> 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_mmss64) {
            time64_t secs64 = rtc_tm_to_time64(tm);
            err = rtc->ops->set_mmss64(rtc->dev.parent, secs64);
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-01-16 13:31:42  更:2022-01-16 13:32:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 6:42:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码