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>):
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
驱动需要实现的操作函数集:
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)
创建流程:
// 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节点
操作函数映射:
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/)
属性定义:

// 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,
};
属性组注册:
// 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/下创建属性文件
属性读写函数示例:
// 读时间属性
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)
创建流程:
// 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);
}
显示函数:
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 子系统初始化
// 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 设备分配
// class.c: devm_rtc_allocate_device()
struct rtc_device *devm_rtc_allocate_device(struct device *dev)
{// 1. 获取设备IDid = 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 设备注册
// 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_DEVICEif (is_hctosys_device(rtc))rtc_hctosys(rtc);#endifreturn 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 中断处理流程
// 硬件中断发生
硬件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 定时器队列管理
// 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 最小驱动实现
// 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 中断处理驱动示例
// 中断处理函数
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子系统通过以下机制实现文件节点与函数调用的关联:
- 字符设备:
file_operations→cdev_init→/dev/rtcN - SysFS:
device_attribute→attribute_group→/sys/class/rtc/rtcN/ - Proc:
proc_create_single_data→/proc/driver/rtc
所有接口最终通过rtc->ops(rtc_class_ops)调用具体驱动的实现,实现统一接口与硬件驱动的解耦,便于维护和扩展。
