• linuxptp源码研究


    目录

    1. 检查网卡是否支持相应的时间戳

    2. linuxptp的目录架构

    3. ptp4l的大致流程分析

    4. gptp协议对应的sync, follow-up, delay-request, delay-response消息在代码的位置

    5.slave收到消息如何处理并调整时间:

    6.一个完整的时间同步的例子


    gptp的报文格式: 报文格式地图——重庆网管博客

    1. 检查网卡是否支持相应的时间戳

    • 通过终端命令ethtool -T eth0查看

    否则可能出现以下这种:eth0网卡不支持软时间戳(-S)对应的SOF_TIMESTAMPING_TX_SOFTWARE,SOF_TIMESTAMPING_RX_SOFTWARE,SOF_TIMESTAMPING_SOFTWARE,

    1. /usrdata # ./ptp4l -i eth0 -m -S
    2. ptp4l[5430.909]: interface 'eth0' does not support requested timestamping mode
    • 找到打印出错的地方--
    1. /* Check the time stamping mode on each interface. */
    2. c->timestamping = timestamping;
    3. required_modes = clock_required_modes(c);
    4. STAILQ_FOREACH(iface, &config->interfaces, list) {
    5. memset(ts_label, 0, sizeof(ts_label));
    6. if (!rtnl_get_ts_device(interface_name(iface), ts_label))
    7. interface_set_label(iface, ts_label);
    8. interface_get_tsinfo(iface);
    9. if (interface_tsinfo_valid(iface) &&
    10. !interface_tsmodes_supported(iface, required_modes)) {
    11. pr_err("interface '%s' does not support requested timestamping mode",
    12. interface_name(iface));
    13. return NULL;
    14. }

    config_get_int(config, NULL, "time_stamping");  // 配置的时间戳

    ----->clock_required_modes()  //逻辑上需要支持的时间戳:比如SOF_TIMESTAMPING_SOFTWARE, 

    SOF_TIMESTAMPING_TX_SOFTWARE,//发包的时间戳

    ----->interface_get_tsinfo ----->sk_get_ts_info

    • 创建socket---------------------fd = socket(AF_INET, SOCK_DGRAM, 0)
    • 通过socket去把信息放到ifr--------ioctl(fd, SIOCETHTOOL, &ifr);     //获取网卡支持的时间类型---和ethtool -T eth0 对应支持的时间戳应该一致

    ------>interface_tsinfo_valid()  和 interface_tsmodes_supported() 来确认这个网卡是否支持此时间模式

    2. linuxptp的目录架构

    研究目录下的makefile发现会编译出来几个APP:主要研究ptp4l, phc2sys这两个app

    • ptp4l:主要是用来计算得出两个设备之间的时间误差(时间戳相差的大小),频率误差(时间走的快慢的差异)。
    • phc2sys:主要是把两个时钟进行同步,比如把systime同步到phc时钟(ptp hardware clock)

    3. ptp4l的大致流程分析

    LinuxPTP的ptp4l.c文件有个int main()函数,makefile通过这个main函数会编译出来ptp4l的可执行程序。

    • 简单的说就是通过getopt_long()函数拿到配置的参数,然后通过clock_create创建一个时钟,在通过poll处理这个时钟相关的事件。
    1. int main(int argc, char *argv[])
    2. {
    3. ......拿到配置对应的参数
    4. while (EOF != (c = getopt_long(argc, argv, "MAEP246HSLf:i:p:sl:mqvh",
    5. opts, &index))) { ......}
    6. ......创建时钟
    7. clock = clock_create(type, cfg, req_phc);
    8. ......
    9. while (is_running()) {
    10. 实时的处理poll得到的clock对应的事件
    11. if (clock_poll(clock))
    12. break;
    13. }
    14. ......
    15. }
    • clock_create函数
    1. struct clock *clock_create(enum clock_type type, struct config *config,
    2. const char *phc_device)
    3. {
    4. ....一些参数配置
    5. ....检查 -i [dev] -i参数指定的设备是否支持需要的时间戳类型
    6. /* Check the time stamping mode on each interface. */
    7. c->timestamping = timestamping;
    8. required_modes = clock_required_modes(c);
    9. STAILQ_FOREACH(iface, &config->interfaces, list) {
    10. memset(ts_label, 0, sizeof(ts_label));
    11. if (!rtnl_get_ts_device(interface_name(iface), ts_label))
    12. interface_set_label(iface, ts_label);
    13. interface_get_tsinfo(iface);
    14. if (interface_tsinfo_valid(iface) &&
    15. !interface_tsmodes_supported(iface, required_modes)) {
    16. pr_err("interface '%s' does not support requested timestamping mode",
    17. interface_name(iface));
    18. return NULL;
    19. }
    20. }
    21. ...... 打开ptp hardware clock
    22. c->clkid = phc_open(phc);
    23. ...... 通过拿到的clkid初始化clock
    24. clockadj_init(c->clkid);
    25. ...... 创建时间控制器,通过makefile的
    26. SERVOS = linreg.o ntpshm.o nullf.o pi.o servo.o
    27. 可知,servos是一个接口类,通过servo_create()创建不同的控制器,
    28. 比如比例积分(pi)控制器,线性(linreg)控制器
    29. c->servo = servo_create(c->config, servo, -fadj, max_adj, sw_ts);
    30. ...... port_open通过指定不同的phc_device来创建相应的回调函数:
    31. p->dispatch 和 p->event
    32. c->uds_ro_port = port_open(phc_device, phc_index, timestamping, 0,
    33. c->uds_ro_if, c);
    34. }

    clock_create函数最重要的两点:

    1. 检查指定的网卡设备是否支持需要的时间戳模式

    2. 通过port_open指定p->dispatch(事件处理分发机制)和p->event(事件有限元状态机的变化)

    4. gptp协议对应的sync, follow-up, delay-request, delay-response消息在代码的位置

    参考:以 ptp4l、E2E 为例的 Linuxptp 代码分析_悠扬侠的博客-CSDN博客_linuxptp源码分析

    之前说了clock_create创建了时钟:在port_open函数指定了事件处理的函数,因为我用到的是bc_event和bc_dispatch,所以以这两个为例子.

    参考的文章已经写的很清楚了:自己也大概的写下:

    1. port_dispatch接口函数里面调用了clock_create函数的p->dispatch方法,而                port_dispatch(p, event, 0);在clock_poll()函数中被一直调用,那么正常的运行状态,所有的事件都是从clock_poll()函数调用port_dispatch--->bc_dispatch
    2. 个人感觉:bc_dispatch做了一些预处理的操作,可以暂时忽略.
    3. 然后就在clock_poll函数调用port_event()接口函数,同样的会一路调用到bc_event()函数
    • master发送sync消息:port_tx_sync()函数:这个函数发送了sync和follow-up报文

    --->port_prepare_and_send函数执行发送消息

    --->transport_send或者transport_sendto调用t->send方法发送数据

    {

    以UDP为例 

    t->send(t, fda, event, 0, msg, len, NULL, &msg->hwts); 对应的是 udp->t.send  = udp_send;

    --->udpsend调用sendto发送数据

    --->立刻调用sk_receive接收反馈的消息,这里是最开始的ioctrl函数指定了发送一条报文,需要从网口设备返回一个时间戳给应用层,如果网口对应的driver有问题,可能会收不到时间戳

    • 设置接口,拿到发送的系统时间戳
    1. {
    2. setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,×tamping, sizeof(timestamping));
    3. setsockopt(fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE,  &flags, sizeof(flags));
    4. }

    }

    • master发送follow-up报文:port_tx_sync
    • master发送delay-request报文:port_pdelay_request
    • slave收到sync报文处理:process_sync
    • slave收到follow-up报文处理:process_follow_up

    5.slave收到消息如何处理并调整时间:

    按照gptp协议的规定,master和slave之间交互了sync.follow-up,delay_requst, delay_response消息,会计算得出时间戳误差,频率误差。

    • 可以查代码看出:在process_follow_up和process_sync函数会调用port_syfufsm(p, event, m);函数(这函数用来切换收到master发来的消息之后的状态机),其中调用了port_synchronize()函数来调整状态并通过port_dispatch分发事件
    • 关键就是通过slave端的几个状态的跳变完成对时间的同步:

    其中master offst对应的是clock_synchronize中的状态跳变,可以很容易看出:比较关键的就是S0到S1,调用了clockadj_step函数,完成对时间戳的改变

    • 还有需要注意:clockadj_step()函数调整的是clkid对应的时间(如果调整的时间是phc对应的clkid的时间,那么同步到系统时间需要用phc2sys这个app)
    1. switch (state) {
    2. case SERVO_UNLOCKED: // S0
    3. break;
    4. case SERVO_JUMP: //s1
    5. clockadj_set_freq(c->clkid, -adj);
    6. //调整时间戳的差值
    7. clockadj_step(c->clkid, -tmv_to_nanoseconds(c->master_offset));
    8. c->ingress_ts = tmv_zero();
    9. if (c->sanity_check) {
    10. clockcheck_set_freq(c->sanity_check, -adj);
    11. clockcheck_step(c->sanity_check,
    12. -tmv_to_nanoseconds(c->master_offset));
    13. }
    14. tsproc_reset(c->tsproc, 0);
    15. clock_step_window(c);
    16. break;
    17. case SERVO_LOCKED:
    18. //调整频率
    19. clock_synchronize_locked(c, adj);
    20. break;
    21. case SERVO_LOCKED_STABLE:
    22. if (c->write_phase_mode) {
    23. clockadj_set_phase(c->clkid, -offset);
    24. adj = 0;
    25. } else {
    26. clock_synchronize_locked(c, adj);
    27. }
    28. break;
    29. }
    • 调整的时间的日志

    1. s0,s1,s2 : 表示时钟伺服器的不同状态,s0表示未锁定,s1表示正在同步,s2表示锁定,锁定状态表示不会再发生阶跃行同步,只是缓慢调整

    6.一个完整的时间同步的例子

    • 交叉工具链编译内核的testptp.c文件
    1. /*
    2. * PTP 1588 clock support - User space test program
    3. *
    4. * Copyright (C) 2010 OMICRON electronics GmbH
    5. *
    6. * This program is free software; you can redistribute it and/or modify
    7. * it under the terms of the GNU General Public License as published by
    8. * the Free Software Foundation; either version 2 of the License, or
    9. * (at your option) any later version.
    10. *
    11. * This program is distributed in the hope that it will be useful,
    12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    14. * GNU General Public License for more details.
    15. *
    16. * You should have received a copy of the GNU General Public License
    17. * along with this program; if not, write to the Free Software
    18. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    19. */
    20. #define _GNU_SOURCE
    21. #define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */
    22. #include
    23. #include
    24. #include
    25. #include
    26. #include
    27. #include
    28. #include
    29. #include
    30. #include
    31. #include
    32. #include
    33. #include
    34. #include
    35. #include
    36. #include
    37. #include
    38. #include
    39. #define DEVICE "/dev/ptp0"
    40. #ifndef ADJ_SETOFFSET
    41. #define ADJ_SETOFFSET 0x0100
    42. #endif
    43. #ifndef CLOCK_INVALID
    44. #define CLOCK_INVALID -1
    45. #endif
    46. /* clock_adjtime is not available in GLIBC < 2.14 */
    47. #if !__GLIBC_PREREQ(2, 14)
    48. #include
    49. static int clock_adjtime(clockid_t id, struct timex *tx)
    50. {
    51. return syscall(__NR_clock_adjtime, id, tx);
    52. }
    53. #endif
    54. static clockid_t get_clockid(int fd)
    55. {
    56. #define CLOCKFD 3
    57. #define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD)
    58. return FD_TO_CLOCKID(fd);
    59. }
    60. static void handle_alarm(int s)
    61. {
    62. printf("received signal %d\n", s);
    63. }
    64. static int install_handler(int signum, void (*handler)(int))
    65. {
    66. struct sigaction action;
    67. sigset_t mask;
    68. /* Unblock the signal. */
    69. sigemptyset(&mask);
    70. sigaddset(&mask, signum);
    71. sigprocmask(SIG_UNBLOCK, &mask, NULL);
    72. /* Install the signal handler. */
    73. action.sa_handler = handler;
    74. action.sa_flags = 0;
    75. sigemptyset(&action.sa_mask);
    76. sigaction(signum, &action, NULL);
    77. return 0;
    78. }
    79. static long ppb_to_scaled_ppm(int ppb)
    80. {
    81. /*
    82. * The 'freq' field in the 'struct timex' is in parts per
    83. * million, but with a 16 bit binary fractional field.
    84. * Instead of calculating either one of
    85. *
    86. * scaled_ppm = (ppb / 1000) << 16 [1]
    87. * scaled_ppm = (ppb << 16) / 1000 [2]
    88. *
    89. * we simply use double precision math, in order to avoid the
    90. * truncation in [1] and the possible overflow in [2].
    91. */
    92. return (long) (ppb * 65.536);
    93. }
    94. static int64_t pctns(struct ptp_clock_time *t)
    95. {
    96. return t->sec * 1000000000LL + t->nsec;
    97. }
    98. static void usage(char *progname)
    99. {
    100. fprintf(stderr,
    101. "usage: %s [options]\n"
    102. " -a val request a one-shot alarm after 'val' seconds\n"
    103. " -A val request a periodic alarm every 'val' seconds\n"
    104. " -c query the ptp clock's capabilities\n"
    105. " -d name device to open\n"
    106. " -e val read 'val' external time stamp events\n"
    107. " -f val adjust the ptp clock frequency by 'val' ppb\n"
    108. " -g get the ptp clock time\n"
    109. " -h prints this message\n"
    110. " -i val index for event/trigger\n"
    111. " -k val measure the time offset between system and phc clock\n"
    112. " for 'val' times (Maximum 25)\n"
    113. " -l list the current pin configuration\n"
    114. " -L pin,val configure pin index 'pin' with function 'val'\n"
    115. " the channel index is taken from the '-i' option\n"
    116. " 'val' specifies the auxiliary function:\n"
    117. " 0 - none\n"
    118. " 1 - external time stamp\n"
    119. " 2 - periodic output\n"
    120. " -p val enable output with a period of 'val' nanoseconds\n"
    121. " -P val enable or disable (val=1|0) the system clock PPS\n"
    122. " -s set the ptp clock time from the system time\n"
    123. " -S set the system time from the ptp clock time\n"
    124. " -t val shift the ptp clock time by 'val' seconds\n"
    125. " -T val set the ptp clock time to 'val' seconds\n",
    126. progname);
    127. }
    128. int main(int argc, char *argv[])
    129. {
    130. struct ptp_clock_caps caps;
    131. struct ptp_extts_event event;
    132. struct ptp_extts_request extts_request;
    133. struct ptp_perout_request perout_request;
    134. struct ptp_pin_desc desc;
    135. struct timespec ts;
    136. struct timex tx;
    137. static timer_t timerid;
    138. struct itimerspec timeout;
    139. struct sigevent sigevent;
    140. struct ptp_clock_time *pct;
    141. struct ptp_sys_offset *sysoff;
    142. char *progname;
    143. unsigned int i;
    144. int c, cnt, fd;
    145. char *device = DEVICE;
    146. clockid_t clkid;
    147. int adjfreq = 0x7fffffff;
    148. int adjtime = 0;
    149. int capabilities = 0;
    150. int extts = 0;
    151. int gettime = 0;
    152. int index = 0;
    153. int list_pins = 0;
    154. int oneshot = 0;
    155. int pct_offset = 0;
    156. int n_samples = 0;
    157. int periodic = 0;
    158. int perout = -1;
    159. int pin_index = -1, pin_func;
    160. int pps = -1;
    161. int seconds = 0;
    162. int settime = 0;
    163. int64_t t1, t2, tp;
    164. int64_t interval, offset;
    165. progname = strrchr(argv[0], '/');
    166. progname = progname ? 1+progname : argv[0];
    167. while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghi:k:lL:p:P:sSt:T:v"))) {
    168. switch (c) {
    169. case 'a':
    170. oneshot = atoi(optarg);
    171. break;
    172. case 'A':
    173. periodic = atoi(optarg);
    174. break;
    175. case 'c':
    176. capabilities = 1;
    177. break;
    178. case 'd':
    179. device = optarg;
    180. break;
    181. case 'e':
    182. extts = atoi(optarg);
    183. break;
    184. case 'f':
    185. adjfreq = atoi(optarg);
    186. break;
    187. case 'g':
    188. gettime = 1;
    189. break;
    190. case 'i':
    191. index = atoi(optarg);
    192. break;
    193. case 'k':
    194. pct_offset = 1;
    195. n_samples = atoi(optarg);
    196. break;
    197. case 'l':
    198. list_pins = 1;
    199. break;
    200. case 'L':
    201. cnt = sscanf(optarg, "%d,%d", &pin_index, &pin_func);
    202. if (cnt != 2) {
    203. usage(progname);
    204. return -1;
    205. }
    206. break;
    207. case 'p':
    208. perout = atoi(optarg);
    209. break;
    210. case 'P':
    211. pps = atoi(optarg);
    212. break;
    213. case 's':
    214. settime = 1;
    215. break;
    216. case 'S':
    217. settime = 2;
    218. break;
    219. case 't':
    220. adjtime = atoi(optarg);
    221. break;
    222. case 'T':
    223. settime = 3;
    224. seconds = atoi(optarg);
    225. break;
    226. case 'h':
    227. usage(progname);
    228. return 0;
    229. case '?':
    230. default:
    231. usage(progname);
    232. return -1;
    233. }
    234. }
    235. fd = open(device, O_RDWR);
    236. if (fd < 0) {
    237. fprintf(stderr, "opening %s: %s\n", device, strerror(errno));
    238. return -1;
    239. }
    240. clkid = get_clockid(fd);
    241. if (CLOCK_INVALID == clkid) {
    242. fprintf(stderr, "failed to read clock id\n");
    243. return -1;
    244. }
    245. if (capabilities) {
    246. if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {
    247. perror("PTP_CLOCK_GETCAPS");
    248. } else {
    249. printf("capabilities:\n"
    250. " %d maximum frequency adjustment (ppb)\n"
    251. " %d programmable alarms\n"
    252. " %d external time stamp channels\n"
    253. " %d programmable periodic signals\n"
    254. " %d pulse per second\n"
    255. " %d programmable pins\n"
    256. " %d cross timestamping\n",
    257. caps.max_adj,
    258. caps.n_alarm,
    259. caps.n_ext_ts,
    260. caps.n_per_out,
    261. caps.pps,
    262. caps.n_pins,
    263. caps.cross_timestamping);
    264. }
    265. }
    266. if (0x7fffffff != adjfreq) {
    267. memset(&tx, 0, sizeof(tx));
    268. tx.modes = ADJ_FREQUENCY;
    269. tx.freq = ppb_to_scaled_ppm(adjfreq);
    270. if (clock_adjtime(clkid, &tx)) {
    271. perror("clock_adjtime");
    272. } else {
    273. puts("frequency adjustment okay");
    274. }
    275. }
    276. if (adjtime) {
    277. memset(&tx, 0, sizeof(tx));
    278. tx.modes = ADJ_SETOFFSET;
    279. tx.time.tv_sec = adjtime;
    280. tx.time.tv_usec = 0;
    281. if (clock_adjtime(clkid, &tx) < 0) {
    282. perror("clock_adjtime");
    283. } else {
    284. puts("time shift okay");
    285. }
    286. }
    287. if (gettime) {
    288. if (clock_gettime(clkid, &ts)) {
    289. perror("clock_gettime");
    290. } else {
    291. printf("clock time: %ld.%09ld or %s",
    292. ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec));
    293. }
    294. }
    295. if (settime == 1) {
    296. clock_gettime(CLOCK_REALTIME, &ts);
    297. if (clock_settime(clkid, &ts)) {
    298. perror("clock_settime");
    299. } else {
    300. puts("set time okay");
    301. }
    302. }
    303. if (settime == 2) {
    304. clock_gettime(clkid, &ts);
    305. if (clock_settime(CLOCK_REALTIME, &ts)) {
    306. perror("clock_settime");
    307. } else {
    308. puts("set time okay");
    309. }
    310. }
    311. if (settime == 3) {
    312. ts.tv_sec = seconds;
    313. ts.tv_nsec = 0;
    314. if (clock_settime(clkid, &ts)) {
    315. perror("clock_settime");
    316. } else {
    317. puts("set time okay");
    318. }
    319. }
    320. if (extts) {
    321. memset(&extts_request, 0, sizeof(extts_request));
    322. extts_request.index = index;
    323. extts_request.flags = PTP_ENABLE_FEATURE;
    324. if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
    325. perror("PTP_EXTTS_REQUEST");
    326. extts = 0;
    327. } else {
    328. puts("external time stamp request okay");
    329. }
    330. for (; extts; extts--) {
    331. cnt = read(fd, &event, sizeof(event));
    332. if (cnt != sizeof(event)) {
    333. perror("read");
    334. break;
    335. }
    336. printf("event index %u at %lld.%09u\n", event.index,
    337. event.t.sec, event.t.nsec);
    338. fflush(stdout);
    339. }
    340. /* Disable the feature again. */
    341. extts_request.flags = 0;
    342. if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
    343. perror("PTP_EXTTS_REQUEST");
    344. }
    345. }
    346. if (list_pins) {
    347. int n_pins = 0;
    348. if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {
    349. perror("PTP_CLOCK_GETCAPS");
    350. } else {
    351. n_pins = caps.n_pins;
    352. }
    353. for (i = 0; i < n_pins; i++) {
    354. desc.index = i;
    355. if (ioctl(fd, PTP_PIN_GETFUNC, &desc)) {
    356. perror("PTP_PIN_GETFUNC");
    357. break;
    358. }
    359. printf("name %s index %u func %u chan %u\n",
    360. desc.name, desc.index, desc.func, desc.chan);
    361. }
    362. }
    363. if (oneshot) {
    364. install_handler(SIGALRM, handle_alarm);
    365. /* Create a timer. */
    366. sigevent.sigev_notify = SIGEV_SIGNAL;
    367. sigevent.sigev_signo = SIGALRM;
    368. if (timer_create(clkid, &sigevent, &timerid)) {
    369. perror("timer_create");
    370. return -1;
    371. }
    372. /* Start the timer. */
    373. memset(&timeout, 0, sizeof(timeout));
    374. timeout.it_value.tv_sec = oneshot;
    375. if (timer_settime(timerid, 0, &timeout, NULL)) {
    376. perror("timer_settime");
    377. return -1;
    378. }
    379. pause();
    380. timer_delete(timerid);
    381. }
    382. if (periodic) {
    383. install_handler(SIGALRM, handle_alarm);
    384. /* Create a timer. */
    385. sigevent.sigev_notify = SIGEV_SIGNAL;
    386. sigevent.sigev_signo = SIGALRM;
    387. if (timer_create(clkid, &sigevent, &timerid)) {
    388. perror("timer_create");
    389. return -1;
    390. }
    391. /* Start the timer. */
    392. memset(&timeout, 0, sizeof(timeout));
    393. timeout.it_interval.tv_sec = periodic;
    394. timeout.it_value.tv_sec = periodic;
    395. if (timer_settime(timerid, 0, &timeout, NULL)) {
    396. perror("timer_settime");
    397. return -1;
    398. }
    399. while (1) {
    400. pause();
    401. }
    402. timer_delete(timerid);
    403. }
    404. if (perout >= 0) {
    405. if (clock_gettime(clkid, &ts)) {
    406. perror("clock_gettime");
    407. return -1;
    408. }
    409. memset(&perout_request, 0, sizeof(perout_request));
    410. perout_request.index = index;
    411. perout_request.start.sec = ts.tv_sec + 2;
    412. perout_request.start.nsec = 0;
    413. perout_request.period.sec = 0;
    414. perout_request.period.nsec = perout;
    415. if (ioctl(fd, PTP_PEROUT_REQUEST, &perout_request)) {
    416. perror("PTP_PEROUT_REQUEST");
    417. } else {
    418. puts("periodic output request okay");
    419. }
    420. }
    421. if (pin_index >= 0) {
    422. memset(&desc, 0, sizeof(desc));
    423. desc.index = pin_index;
    424. desc.func = pin_func;
    425. desc.chan = index;
    426. if (ioctl(fd, PTP_PIN_SETFUNC, &desc)) {
    427. perror("PTP_PIN_SETFUNC");
    428. } else {
    429. puts("set pin function okay");
    430. }
    431. }
    432. if (pps != -1) {
    433. int enable = pps ? 1 : 0;
    434. if (ioctl(fd, PTP_ENABLE_PPS, enable)) {
    435. perror("PTP_ENABLE_PPS");
    436. } else {
    437. puts("pps for system time request okay");
    438. }
    439. }
    440. if (pct_offset) {
    441. if (n_samples <= 0 || n_samples > 25) {
    442. puts("n_samples should be between 1 and 25");
    443. usage(progname);
    444. return -1;
    445. }
    446. sysoff = calloc(1, sizeof(*sysoff));
    447. if (!sysoff) {
    448. perror("calloc");
    449. return -1;
    450. }
    451. sysoff->n_samples = n_samples;
    452. if (ioctl(fd, PTP_SYS_OFFSET, sysoff))
    453. perror("PTP_SYS_OFFSET");
    454. else
    455. puts("system and phc clock time offset request okay");
    456. pct = &sysoff->ts[0];
    457. for (i = 0; i < sysoff->n_samples; i++) {
    458. t1 = pctns(pct+2*i);
    459. tp = pctns(pct+2*i+1);
    460. t2 = pctns(pct+2*i+2);
    461. interval = t2 - t1;
    462. offset = (t2 + t1) / 2 - tp;
    463. printf("system time: %lld.%u\n",
    464. (pct+2*i)->sec, (pct+2*i)->nsec);
    465. printf("phc time: %lld.%u\n",
    466. (pct+2*i+1)->sec, (pct+2*i+1)->nsec);
    467. printf("system time: %lld.%u\n",
    468. (pct+2*i+2)->sec, (pct+2*i+2)->nsec);
    469. printf("system/phc clock time offset is %" PRId64 " ns\n"
    470. "system clock time delay is %" PRId64 " ns\n",
    471. offset, interval);
    472. }
    473. free(sysoff);
    474. }
    475. close(fd);
    476. return 0;
    477. }

    master:

    • ./ptp4l -i eth0 -E -m -l 7 -S -4 & 

    把带时间戳的报文通过ptp报文发送给slave端,用的是systime
    slave: [选择/dev/ptp1是发现在clokadj_step的时候对应的clkid对应的设备是/dev/ptp1,需要根据调试过程动态调整]

    • ./testptp -d /dev/ptp1 -s    //把systime设到/dev/ptp1时间
    • ./ptp4l -i pfe2 -m -4 -E -S -s

     拿到master过来的systime,计算得到的offset是master和slave之间的systime的误差,而在调用clockadj_step的时候用的是两边系统时间的offset。所以需要保证clockadj_step对应的clkid的时间和系统时间是一样的!!

    • ./testptp -d /dev/ptp1 -S    //把/dev/ptp1时间设到systime

    或者

    • ./phc2sys -l 7 -m -c CLOCK_REALTIME -s /dev/ptp1 -w &

    把/dev/ptp1对应的phc时间同步到systime, 

    -w是等待ptp4l: 通过pmc_create()来创建进程间通信,-w当收到了来自ptp4l的消息,就会跳出 Waiting for ptp4l...的循环,然后执行do_loop来执行 update_clock()来跟新时间(把/dev/ptp1上的时间更新到CLOCK_REALTIME上!!)

    • ./testptp -d /dev/ptp1 -k 1  //打印系统,/dev/ptp1时间

  • 相关阅读:
    最难的IB课程为什么含金量最高?
    Integer对象的大小比较
    异常 —— throws
    科普:什么是ChatGPT?(文末有彩蛋)
    快收藏!!整理了100个Python小技巧!!
    Python 批量合并图片到word文档
    聊聊常见的IO模型 BIO/NIO/AIO 、DIO、多路复用等IO模型
    欧拉函数——最大公约数(gcd+筛质数+欧拉函数)
    ChatGPT AIGC 制作大屏可视化分析案例
    文件对象的常用方法和属性
  • 原文地址:https://blog.csdn.net/m0_37844072/article/details/126270366