当前位置: 首页 > news >正文

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.cRTC设备类创建、设备注册/注销、生命周期管理
字符设备接口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_TIMErtc_read_time()读取时间
RTC_SET_TIMErtc_set_time()设置时间
RTC_ALM_READrtc_read_alarm()读取闹钟
RTC_ALM_SETrtc_set_alarm()设置闹钟
RTC_AIE_ON/OFFrtc_alarm_irq_enable()使能/禁用闹钟中断
RTC_UIE_ON/OFFrtc_update_irq_enable()使能/禁用更新中断
RTC_PIE_ON/OFFrtc_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子系统通过以下机制实现文件节点与函数调用的关联:

  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)调用具体驱动的实现,实现统一接口与硬件驱动的解耦,便于维护和扩展。

http://www.dtcms.com/a/606288.html

相关文章:

  • 主流服务器免费 SSL 证书部署手册 + 混合内容排查指南
  • Linux SNMP 团体号配置指定IP地址访问
  • 酒店移动网站建设方案wordpress添加导航栏
  • 大模型知识蒸馏实战:从Qwen-72B到Qwen-7B的压缩艺术
  • CMake Error at fc_base/gflags-src/CMakeLists.txt:73
  • 做一个网站需要多少人发布网站建设需求的经验
  • 网站开发多少工资做网站编辑好还是美工好
  • 上海网站建设公司四叶互联邗江区建设局网站
  • pytorch-张量转换
  • 推广型网站建设机构甘肃业聚质网络科技有限公司
  • 怎么让同一个局域网上的计算机看到我做的网站以公司名称为后缀的邮箱
  • Java接口与抽象类深度指南:从原理到实战
  • 人工智能备考——2.1.4题解
  • 做淘宝网站需要什么邵阳市城市建设网站
  • 告别闭门造车:用竞品ASO分析驱动应用下载转化
  • 【LeetCode】108. 将有序数组转换为二叉搜索树
  • 12.vector—string(下)
  • 具身智能数据采集全方案:动作捕捉技术驱动机器人拟人化进阶
  • 公司网站地图怎么做长沙网站托管优化
  • 网站免费创建雅虎搜索
  • 多通道手腕压力脉搏波信号
  • 眉县网站建设wordpress首页flash
  • 贪心算法实验2
  • C语言在线编译器开发 | 提高编译效率与用户体验的创新技术
  • MD5 校验脚本
  • 重生归来,我要成功 Python 高手--day35 深度学习 Pytorch
  • 马云有没有学过做网站百度收录时间
  • 企业网站的规划与建设ppt建设一个打鱼游戏网站
  • 在 Linux Ubuntu 24.04 安装 IntelliJ IDEA
  • 自适应网站建设方案建设网站 请示 报告