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子系统通过以下机制实现文件节点与函数调用的关联:
- 字符设备:
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)调用具体驱动的实现,实现统一接口与硬件驱动的解耦,便于维护和扩展。