linux内核驱动:ptp内核phc框架
目录
- 一、介绍
- 二、PHC驱动文件
- 三、主要数据结构
- 四、初始化和调用流程
- 五、总结
一、介绍
本文基于linux内核5.10.xxx总结ptp1588精确时间协议实现过程中,内核部分的8A34002实现的phc(PTP hardware clock)驱动支持;
ptp的系统框架
.红圈部分为本笔记总结的phc驱动的部分;
.紫圈为用户层的ptp协议栈,如开源的ptp4l;
.蓝圈为集成在用户层ptp协议栈中时钟伺服控制部分,会根据ptp主时钟和本地时间的差值采用pid算法,调用linux的中的通用接口去控制phc驱动进行本地时间和频率的调整,进而和主时钟进行同步;
.红圈中的phc主要作用为通过用户空间的时钟伺服逻辑控制phc驱动进行微调时间递增的频率,实现phc功能的芯片如瑞萨的8A34002;
.MAC部分内置支持TSU时间标签单元寄存器时,时间信息可以在ptp数据包接收和发送的时机从TSU中获取或者调整TSU的时间;
.图中phc和mac中都有TSU时间标签单元,但是mac中的时间标签和通过网络传输过来的数据在驱动上更好集成时间精度更高,图中的方案是phc部分产生时钟频率,mac中的TSU以外部的时钟频率进行时间计时;
二、PHC驱动文件
1、ptp_chardev.c
实现结构struct posix_clock_operations *ops中的部分函数如下标注接口;
struct posix_clock_operations
{
struct module *owner;
int (*clock_adjtime)(struct posix_clock *pc, struct __kernel_timex *tx);
int (*clock_gettime)(struct posix_clock *pc, struct timespec64 *ts);
int (*clock_getres) (struct posix_clock *pc, struct timespec64 *ts);
int (*clock_settime)(struct posix_clock *pc,
const struct timespec64 ts);
/
* Optional character device methods:
*/
long (*ioctl) (struct posix_clock *pc,unsigned int cmd, unsigned long arg);
int (*open) (struct posix_clock *pc, fmode_t f_mode);
__poll_t (*poll) (struct posix_clock *pc,struct file *file, poll_table *wait);
int (*release) (struct posix_clock *pc);
ssize_t (*read) (struct posix_clock *pc,uint flags, char __user *buf, size_t cnt);
}
2、ptp_clock.c
ptp子系统的创建函数;
struct ptp_clock 的注册函数。创建/dev/ptp0、/dev/ptp1等设备节点;
static struct posix_clock_operations ptp_clock_ops 变量的定义;
实现static struct posix_clock_operations ptp_clock_ops 中的部分函数:
struct posix_clock_operations结构
{
struct module owner;
int (*clock_adjtime)(struct posix_clock *pc, struct __kernel_timex *tx);
int (*clock_gettime)(struct posix_clock *pc, struct timespec64 *ts);
int (*clock_getres) (struct posix_clock *pc, struct timespec64 *ts);
int (*clock_settime)(struct posix_clock *pc,const struct timespec64 *ts);
/
* Optional character device methods:
*/
long (*ioctl) (struct posix_clock *pc,unsigned int cmd, unsigned long arg);
int (*open) (struct posix_clock *pc, fmode_t f_mode);
__poll_t (*poll) (struct posix_clock *pc,struct file *file, poll_table *wait);
int (*release) (struct posix_clock *pc);
ssize_t (*read) (struct posix_clock *pc,uint flags, char __user *buf, size_t cnt);
}
3、ptp_sysfs.c
ptp设备的属性组定义;
定义如下属性:
static struct attribute *ptp_attrs[] = {
&dev_attr_clock_name.attr,
&dev_attr_max_adjustment.attr,
&dev_attr_n_alarms.attr,
&dev_attr_n_external_timestamps.attr,
&dev_attr_n_periodic_outputs.attr,
&dev_attr_n_programmable_pins.attr,
&dev_attr_pps_available.attr,
&dev_attr_extts_enable.attr,
&dev_attr_fifo.attr,
&dev_attr_period.attr,
&dev_attr_pps_enable.attr,
NULL
};
4、posix-timers.c
封装了关于获取时间、设置时间、调节频率的对应用户空间的系统调用函数,以 SYSCALL_DEFINE4 宏进行了注册,详见文件中定义;
定义了一个以时钟ID为数组下标的struct k_clock * const posix_clocks[] 数组;
5、posix-clock.c
字符设备节点/dev/ptp0、/dev/ptp1等的标准字符驱动函数实现;
static const struct file_operations posix_clock_file_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = posix_clock_read,
.poll = posix_clock_poll,
.unlocked_ioctl = posix_clock_ioctl,
.open = posix_clock_open,
.release = posix_clock_release,
#ifdef CONFIG_COMPAT
.compat_ioctl = posix_clock_compat_ioctl,
#endif
};
注册动态时钟:
const struct k_clock clock_posix_dynamic = {
.clock_getres = pc_clock_getres,
.clock_set = pc_clock_settime,
.clock_get_timespec = pc_clock_gettime,
.clock_adj = pc_clock_adjtime,
};
6、ptp_clockmatrix.c
8A34002芯片实现phc驱动的驱动文件;
具体实现struct ptp_clock_info 中的底层钩子函数;
三、主要数据结构
1、struct ptp_clock
定义于ptp_private.h;
是对phc设备的整体封装表示,包括设备信息的描述和操作集;
2、struct posix_clock
定义于posix-clock.h ;
可以理解成是struct ptp_clock的抽象父类,struct ptp_clock为具体实现,应用调用时逐渐从抽象结构 struct ptp_clock 向具体实现 struct ptp_clock 调用;
3、struct posix_clock_operations ops;
定义于posix-clock.h ;
struct posix_clock 中的操作函数集成员;
4、struct ptp_clock_info
定义于ptp_clock_kernel.h;
struct ptp_cloc中操作集成员info的表示;
驱动需要具体实现的是此结构;
5、关系
四、初始化和调用流程
五、总结
1、实现phc驱动方案或者芯片可以有多种选择,支持程序控制调节时钟频率且能达到调节精度要求即可;
2、ptp本地时间的TSU时间标签也可能在网络的phy芯片中,实际应用中需要实现网络底层驱动和tsu时间标签驱动接口的结合;