Linux RTC 驱动子系统详细实现方案

1. 整体架构设计

1.1 架构层次图

复制代码
┌─────────────────────────────────────────────────┐
│           用户空间接口层                          │
│  /dev/rtcN  |  /sys/class/rtc/rtcN/  |  /proc/  │
└─────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────┐
│           文件节点接口层                          │
│  dev.c (字符设备) | sysfs.c | proc.c            │
└─────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────┐
│           核心接口层 (interface.c)                │
│  rtc_read_time() | rtc_set_time() | rtc_alarm() │
└─────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────┐
│           设备管理层 (class.c)                    │
│  设备注册 | 生命周期管理 | 属性组管理            │
└─────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────┐
│           具体驱动层 (rtc-xxx.c)                 │
│  硬件操作 | 中断处理 | 寄存器访问                │
└─────────────────────────────────────────────────┘

1.2 核心模块划分

模块 文件 功能
设备类管理 class.c RTC设备类创建、设备注册/注销、生命周期管理
字符设备接口 dev.c /dev/rtcN节点、ioctl接口、中断处理
核心接口 interface.c 时间读写、闹钟设置、中断使能等核心API
SysFS接口 sysfs.c /sys/class/rtc/rtcN/属性文件
Proc接口 proc.c /proc/driver/rtc信息展示
工具库 lib.c 时间转换、验证等辅助函数

2. 核心数据结构

2.1 struct rtc_device

RTC设备的核心结构(定义在<linux/rtc.h>):

c 复制代码
struct rtc_device {
    struct device dev;              // 设备结构
    struct module *owner;            // 模块所有者
    int id;                          // 设备ID (rtc0, rtc1...)
    
    // 操作函数指针
    const struct rtc_class_ops *ops;  // 驱动提供的操作函数集
    
    // 锁和同步
    struct mutex ops_lock;           // 操作锁
    spinlock_t irq_lock;             // 中断锁
    
    // 中断相关
    wait_queue_head_t irq_queue;     // 中断等待队列
    unsigned long irq_data;           // 中断数据
    struct fasync_struct *async_queue; // 异步通知队列
    
    // 定时器
    struct timerqueue_head timerqueue; // 定时器队列
    struct rtc_timer aie_timer;       // 闹钟中断定时器
    struct rtc_timer uie_rtctimer;    // 更新中断定时器
    struct hrtimer pie_timer;         // 周期性中断定时器
    
    // 工作队列
    struct work_struct irqwork;       // 中断处理工作
    
    // 特性标志
    unsigned long features[4];        // 功能特性位图
    
    // 时间范围
    time64_t range_min;               // 最小时间
    time64_t range_max;               // 最大时间
    time64_t start_secs;              // 起始秒数
    long offset_secs;                 // 偏移秒数
    
    // 频率设置
    int irq_freq;                     // 中断频率
    int max_user_freq;                // 最大用户频率
    
    // 字符设备
    struct cdev char_dev;             // 字符设备结构
    unsigned long flags;              // 设备标志
};

2.2 struct rtc_class_ops

驱动需要实现的操作函数集:

c 复制代码
struct rtc_class_ops {
    int (*open)(struct device *);              // 打开设备
    void (*release)(struct device *);          // 释放设备
    int (*ioctl)(struct device *, unsigned int, unsigned long); // ioctl
    
    // 时间操作
    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 (*alarm_irq_enable)(struct device *, unsigned int);     // 闹钟中断使能
    
    // 参数操作
    int (*read_offset)(struct device *, long *);                // 读偏移
    int (*set_offset)(struct device *, long);                   // 写偏移
    int (*param_get)(struct device *, struct rtc_param *);     // 读参数
    int (*param_set)(struct device *, struct rtc_param *);     // 写参数
    
    // 其他
    void (*proc)(struct device *, struct seq_file *);           // proc信息
    int (*read_callback)(struct device *, int data);           // 读回调
};

3. 文件节点创建机制

3.1 字符设备节点 (/dev/rtcN)

创建流程:

c 复制代码
// 1. 初始化阶段 (rtc_init)
rtc_dev_init() 
    → alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc")
    // 分配字符设备号

// 2. 设备准备阶段 (rtc_dev_prepare)
rtc_dev_prepare(rtc)
    → rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id)
    → cdev_init(&rtc->char_dev, &rtc_dev_fops)  // 关联操作函数
    → rtc->char_dev.owner = rtc->owner

// 3. 设备注册阶段 (__devm_rtc_register_device)
cdev_device_add(&rtc->char_dev, &rtc->dev)
    // 在/dev/下创建rtcN节点

操作函数映射:

c 复制代码
static const struct file_operations rtc_dev_fops = {
    .open      = rtc_dev_open,        // 打开: 设置BUSY标志
    .release   = rtc_dev_release,     // 关闭: 清理中断
    .read      = rtc_dev_read,        // 读: 等待中断数据
    .poll      = rtc_dev_poll,        // 轮询: 检查中断状态
    .unlocked_ioctl = rtc_dev_ioctl,  // ioctl: 时间/闹钟操作
    .fasync    = rtc_dev_fasync,     // 异步通知
};

ioctl命令映射:

ioctl命令 处理函数 功能
RTC_RD_TIME rtc_read_time() 读取时间
RTC_SET_TIME rtc_set_time() 设置时间
RTC_ALM_READ rtc_read_alarm() 读取闹钟
RTC_ALM_SET rtc_set_alarm() 设置闹钟
RTC_AIE_ON/OFF rtc_alarm_irq_enable() 使能/禁用闹钟中断
RTC_UIE_ON/OFF rtc_update_irq_enable() 使能/禁用更新中断
RTC_PIE_ON/OFF rtc_irq_set_state() 使能/禁用周期性中断

3.2 SysFS属性节点 (/sys/class/rtc/rtcN/)

属性定义:

c 复制代码
// sysfs.c中定义的属性数组
static struct attribute *rtc_attrs[] = {
    &dev_attr_name.attr,          // 设备名称
    &dev_attr_date.attr,          // 日期 (YYYY-MM-DD)
    &dev_attr_time.attr,          // 时间 (HH:MM:SS)
    &dev_attr_since_epoch.attr,   // 自1970年以来的秒数
    &dev_attr_max_user_freq.attr, // 最大用户频率
    &dev_attr_hctosys.attr,       // 是否用于系统时钟
    &dev_attr_wakealarm.attr,      // 唤醒闹钟
    &dev_attr_offset.attr,         // 时钟偏移
    &dev_attr_range.attr,          // 时间范围
    NULL,
};

属性组注册:

c 复制代码
// class.c: rtc_allocate_device()
rtc->dev.groups = rtc_get_dev_attribute_groups();
    ↓
// sysfs.c: rtc_get_dev_attribute_groups()
return rtc_attr_groups;  // 返回属性组数组
    ↓
// 设备注册时,内核自动在/sys/class/rtc/rtcN/下创建属性文件

属性读写函数示例:

c 复制代码
// 读时间属性
static ssize_t time_show(struct device *dev, 
                        struct device_attribute *attr, 
                        char *buf)
{
    struct rtc_time tm;
    int retval = rtc_read_time(to_rtc_device(dev), &tm);
    if (retval)
        return retval;
    return sprintf(buf, "%ptRt\n", &tm);
}
static DEVICE_ATTR_RO(time);

// 写闹钟属性
static ssize_t wakealarm_store(struct device *dev,
                              struct device_attribute *attr,
                              const char *buf, size_t n)
{
    struct rtc_wkalrm alm;
    // 解析buf中的时间戳
    // 调用rtc_set_alarm()
    return rtc_set_alarm(rtc, &alm);
}
static DEVICE_ATTR_RW(wakealarm);

3.3 Proc文件系统节点 (/proc/driver/rtc)

创建流程:

c 复制代码
// proc.c: rtc_proc_add_device()
void rtc_proc_add_device(struct rtc_device *rtc)
{
    if (is_rtc_hctosys(rtc))  // 只为主RTC创建
        proc_create_single_data("driver/rtc", 0, NULL, 
                                rtc_proc_show, rtc);
}

显示函数:

c 复制代码
static int rtc_proc_show(struct seq_file *seq, void *offset)
{
    struct rtc_device *rtc = seq->private;
    
    // 显示当前时间
    rtc_read_time(rtc, &tm);
    seq_printf(seq, "rtc_time\t: %ptRt\n", &tm);
    
    // 显示闹钟信息
    rtc_read_alarm(rtc, &alrm);
    seq_printf(seq, "alrm_time\t: %ptRt\n", &alrm.time);
    seq_printf(seq, "alarm_IRQ\t: %s\n", 
               alrm.enabled ? "yes" : "no");
    
    // 显示中断状态
    seq_printf(seq, "update IRQ enabled\t: %s\n",
               rtc->uie_rtctimer.enabled ? "yes" : "no");
    
    // 调用驱动的proc函数(如果有)
    if (ops->proc)
        ops->proc(rtc->dev.parent, seq);
    
    return 0;
}

4. 设备注册流程

4.1 子系统初始化

c 复制代码
// class.c: rtc_init()
static int __init rtc_init(void)
{
    // 1. 创建RTC设备类
    rtc_class = class_create(THIS_MODULE, "rtc");
    
    // 2. 设置电源管理操作
    rtc_class->pm = RTC_CLASS_DEV_PM_OPS;
    
    // 3. 初始化字符设备
    rtc_dev_init();
        → alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");
    
    return 0;
}
subsys_initcall(rtc_init);  // 子系统初始化调用

4.2 设备分配

c 复制代码
// class.c: devm_rtc_allocate_device()
struct rtc_device *devm_rtc_allocate_device(struct device *dev)
{
    // 1. 获取设备ID
    id = rtc_device_get_id(dev);
    
    // 2. 分配RTC设备结构
    rtc = rtc_allocate_device();
        → kzalloc(sizeof(*rtc), GFP_KERNEL)
        → device_initialize(&rtc->dev)
        → rtc->dev.class = rtc_class
        → rtc->dev.groups = rtc_get_dev_attribute_groups()
        → mutex_init(&rtc->ops_lock)
        → timerqueue_init_head(&rtc->timerqueue)
    
    // 3. 设置设备名称
    dev_set_name(&rtc->dev, "rtc%d", id);
    
    return rtc;
}

4.3 设备注册

c 复制代码
// class.c: __devm_rtc_register_device()
int __devm_rtc_register_device(struct module *owner, 
                                struct rtc_device *rtc)
{
    // 1. 验证操作函数
    if (!rtc->ops) return -EINVAL;
    
    // 2. 设置特性标志
    if (!rtc->ops->set_alarm)
        clear_bit(RTC_FEATURE_ALARM, rtc->features);
    
    // 3. 计算时间偏移
    rtc_device_get_offset(rtc);
    
    // 4. 初始化闹钟
    __rtc_read_alarm(rtc, &alrm);
    if (!rtc_valid_tm(&alrm.time))
        rtc_initialize_alarm(rtc, &alrm);
    
    // 5. 准备字符设备
    rtc_dev_prepare(rtc);
        → cdev_init(&rtc->char_dev, &rtc_dev_fops)
    
    // 6. 添加字符设备
    cdev_device_add(&rtc->char_dev, &rtc->dev);
        // 创建/dev/rtcN节点
    
    // 7. 添加proc节点
    rtc_proc_add_device(rtc);
        // 创建/proc/driver/rtc节点
    
    // 8. 系统时钟同步(如果配置)
    #ifdef CONFIG_RTC_HCTOSYS_DEVICE
    if (is_hctosys_device(rtc))
        rtc_hctosys(rtc);
    #endif
    
    return 0;
}

5. 核心接口函数调用链

5.1 读取时间流程

复制代码
用户空间: cat /sys/class/rtc/rtc0/time
    ↓
内核sysfs: sysfs_read_file()
    ↓
sysfs.c: time_show()
    ↓
interface.c: rtc_read_time(rtc, &tm)
    ↓
interface.c: __rtc_read_time(rtc, tm)
    ↓
interface.c: rtc->ops->read_time(rtc->dev.parent, tm)
    ↓
具体驱动: pcf85053_rtc_read_time()
    ↓
硬件寄存器读取

5.2 设置时间流程

复制代码
用户空间: echo "2024-01-01 12:00:00" > /sys/class/rtc/rtc0/time
    ↓
内核sysfs: sysfs_write_file()
    ↓
sysfs.c: time_store() (如果实现)
    ↓
interface.c: rtc_set_time(rtc, &tm)
    ↓
interface.c: rtc_valid_tm(tm)  // 验证时间
    ↓
interface.c: rtc_valid_range(rtc, tm)  // 验证范围
    ↓
interface.c: rtc_subtract_offset(rtc, tm)  // 减去偏移
    ↓
interface.c: rtc->ops->set_time(rtc->dev.parent, tm)
    ↓
具体驱动: pcf85053_rtc_set_time()
    ↓
硬件寄存器写入

5.3 闹钟设置流程

复制代码
用户空间: echo +3600 > /sys/class/rtc/rtc0/wakealarm
    ↓
sysfs.c: wakealarm_store()
    ↓
interface.c: rtc_set_alarm(rtc, &alarm)
    ↓
interface.c: rtc_timer_enqueue(rtc, &rtc->aie_timer)
    ↓
interface.c: __rtc_set_alarm(rtc, &alarm)
    ↓
interface.c: rtc->ops->set_alarm(rtc->dev.parent, &alarm)
    ↓
具体驱动: 硬件闹钟寄存器设置

6. 中断处理机制

6.1 中断类型

中断类型 说明 定时器
AIE (Alarm Interrupt) 闹钟中断 aie_timer
UIE (Update Interrupt) 秒更新中断 uie_rtctimer
PIE (Periodic Interrupt) 周期性中断 pie_timer (hrtimer)

6.2 中断处理流程

c 复制代码
// 硬件中断发生
硬件RTC → 产生中断 → 驱动中断处理函数
    ↓
// 驱动中断处理函数
static irqreturn_t rtc_irq_handler(int irq, void *dev_id)
{
    struct rtc_device *rtc = dev_id;
    
    // 通知RTC子系统
    rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF);
        ↓
    // interface.c: rtc_update_irq()
    schedule_work(&rtc->irqwork);  // 调度工作队列
        ↓
    // interface.c: rtc_timer_do_work()
    // 处理定时器队列,触发回调函数
    timer->func(timer->rtc);
        ↓
    // 例如: rtc_aie_update_irq()
    rtc_handle_legacy_irq(rtc, 1, RTC_AF);
        ↓
    // 唤醒等待的进程
    wake_up_interruptible(&rtc->irq_queue);
    // 发送异步通知
    kill_fasync(&rtc->async_queue, SIGIO, POLL_IN);
}

6.3 定时器队列管理

c 复制代码
// interface.c: rtc_timer_enqueue()
static int rtc_timer_enqueue(struct rtc_device *rtc, 
                             struct rtc_timer *timer)
{
    // 1. 读取当前时间
    __rtc_read_time(rtc, &tm);
    
    // 2. 跳过已过期的定时器
    while (next && next->expires < now)
        next = timerqueue_iterate_next(next);
    
    // 3. 添加到定时器队列
    timerqueue_add(&rtc->timerqueue, &timer->node);
    
    // 4. 如果是最近的定时器,设置硬件闹钟
    if (timer == next_timer)
        __rtc_set_alarm(rtc, &alarm);
}

7. 具体驱动实现示例

7.1 最小驱动实现

c 复制代码
// rtc-pcf85053.c 示例

// 1. 定义操作函数
static int pcf85053_rtc_read_time(struct device *dev, 
                                   struct rtc_time *tm)
{
    // 从硬件读取时间寄存器
    // 转换为rtc_time结构
    return 0;
}

// 2. 定义操作函数集
static const struct rtc_class_ops pcf85053_rtc_ops = {
    .read_time = pcf85053_rtc_read_time,
    // .set_time = pcf85053_rtc_set_time,  // 可选
    // .read_alarm = pcf85053_rtc_read_alarm,  // 可选
};

// 3. 驱动probe函数
static int pcf85053_rtc_probe(struct i2c_client *client,
                              const struct i2c_device_id *id)
{
    struct pcf85053 *pcf85053;
    struct rtc_device *rtc;
    
    // 分配驱动私有数据
    pcf85053 = devm_kzalloc(&client->dev, sizeof(*pcf85053), GFP_KERNEL);
    
    // 初始化硬件(如regmap)
    pcf85053->regmap = devm_regmap_init_i2c(client, &config);
    
    // 分配RTC设备
    rtc = devm_rtc_allocate_device(&client->dev);
    
    // 设置操作函数
    rtc->ops = &pcf85053_rtc_ops;
    
    // 设置时间范围
    rtc->range_max = U32_MAX;
    
    // 注册设备(自动创建所有文件节点)
    return devm_rtc_register_device(rtc);
}

7.2 中断处理驱动示例

c 复制代码
// 中断处理函数
static irqreturn_t rtc_irq_handler(int irq, void *dev_id)
{
    struct rtc_device *rtc = dev_id;
    
    // 清除中断标志
    clear_interrupt_flag();
    
    // 通知RTC子系统
    rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF);
    
    return IRQ_HANDLED;
}

// 在probe中注册中断
static int rtc_probe(struct platform_device *pdev)
{
    int irq = platform_get_irq(pdev, 0);
    
    ret = devm_request_irq(&pdev->dev, irq, rtc_irq_handler,
                          0, "rtc", rtc);
}

8. 关键设计要点

8.1 分层设计

  • 接口层:统一API,隐藏硬件差异
  • 驱动层:实现硬件操作
  • 通过rtc_class_ops解耦

8.2 多接口支持

  • 字符设备:/dev/rtcN,支持ioctl
  • SysFS:/sys/class/rtc/rtcN/,属性文件
  • Proc:/proc/driver/rtc,信息展示

8.3 时间管理

  • 时间范围:range_min/range_max
  • 时间偏移:offset_secs用于扩展范围
  • 时间验证:rtc_valid_tm()rtc_valid_range()

8.4 中断管理

  • 定时器队列:统一管理所有定时器
  • 工作队列:异步处理中断
  • 中断模拟:支持软件模拟UIE

8.5 电源管理

  • 挂起/恢复:保存和恢复RTC状态
  • 系统时钟同步:rtc_hctosys()

9. 总结

RTC子系统通过以下机制实现文件节点与函数调用的关联:

  1. 字符设备:file_operationscdev_init/dev/rtcN
  2. SysFS:device_attributeattribute_group/sys/class/rtc/rtcN/
  3. Proc:proc_create_single_data/proc/driver/rtc

所有接口最终通过rtc->opsrtc_class_ops)调用具体驱动的实现,实现统一接口与硬件驱动的解耦,便于维护和扩展。

相关推荐
淮北4942 小时前
linux系统学习(10.shell基础)
linux·运维·服务器·学习
视觉震撼2 小时前
RDP登录事件详细溯源分析脚本(兼容Windows PowerShell版本)
运维·网络·windows·网络安全·网络攻击模型·安全威胁分析·安全架构
Lolo_fi2 小时前
记录Fedora43上安装向日葵
linux
noravinsc2 小时前
两台 centos 7.9 部署 pbs version 18.1.4 集群
linux·运维·centos
你的微笑,乱了夏天2 小时前
linux centos常用命令整理
linux·运维·centos
故林丶3 小时前
【Linux】CentOS 7.8 Docker + Docker Compose 安装
linux·docker·centos
刘一说3 小时前
CentOS系统一键安装Memcached教程
linux·centos·memcached
网硕互联的小客服3 小时前
Windows2008 如何禁用FSO?
运维·服务器·网络·windows·安全
不惑_3 小时前
[特殊字符] 在 Linux 上设置 SQLite
linux·jvm·sqlite