]> git.ipfire.org Git - thirdparty/chrony.git/blame - refclock_phc.c
ntp: fix loop test for special reference modes
[thirdparty/chrony.git] / refclock_phc.c
CommitLineData
1d289787
ML
1/*
2 chronyd/chronyc - Programs for keeping computer clocks accurate.
3
4 **********************************************************************
a4e3f836 5 * Copyright (C) Miroslav Lichvar 2013, 2017
1d289787
ML
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of version 2 of the GNU General Public License as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 **********************************************************************
21
22 =======================================================================
23
24 PTP hardware clock (PHC) refclock driver.
25
26 */
27
28#include "config.h"
29
30#include "refclock.h"
31
32#ifdef FEAT_PHC
33
34#include "sysincl.h"
35
1d289787 36#include "refclock.h"
eceb8d99
ML
37#include "hwclock.h"
38#include "local.h"
1d289787 39#include "logging.h"
9df4d361 40#include "memory.h"
1d289787 41#include "util.h"
eceb8d99 42#include "sched.h"
9df4d361 43#include "sys_linux.h"
1d289787 44
9df4d361
ML
45struct phc_instance {
46 int fd;
47 int mode;
a60fc73e 48 int nocrossts;
eceb8d99
ML
49 int extpps;
50 int pin;
51 int channel;
52 HCL_Instance clock;
1d289787
ML
53};
54
eceb8d99
ML
55static void read_ext_pulse(int sockfd, int event, void *anything);
56
1d289787
ML
57static int phc_initialise(RCL_Instance instance)
58{
a78031ce 59 const char *options[] = {"nocrossts", "extpps", "pin", "channel", "clear", NULL};
9df4d361 60 struct phc_instance *phc;
eceb8d99
ML
61 int phc_fd, rising_edge;
62 char *path, *s;
1d289787 63
a78031ce
ML
64 RCL_CheckDriverOptions(instance, options);
65
1d289787
ML
66 path = RCL_GetDriverParameter(instance);
67
9df4d361 68 phc_fd = SYS_Linux_OpenPHC(path, 0);
1d289787 69 if (phc_fd < 0) {
f282856c 70 LOG_FATAL("Could not open PHC");
1d289787
ML
71 return 0;
72 }
73
9df4d361
ML
74 phc = MallocNew(struct phc_instance);
75 phc->fd = phc_fd;
76 phc->mode = 0;
a60fc73e 77 phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
eceb8d99
ML
78 phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
79
80 if (phc->extpps) {
81 s = RCL_GetDriverOption(instance, "pin");
82 phc->pin = s ? atoi(s) : 0;
83 s = RCL_GetDriverOption(instance, "channel");
84 phc->channel = s ? atoi(s) : 0;
85 rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
c0717a27 86 phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
eceb8d99
ML
87
88 if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
89 rising_edge, !rising_edge, 1))
90 LOG_FATAL("Could not enable external PHC timestamping");
91
92 SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
93 } else {
94 phc->pin = phc->channel = 0;
95 phc->clock = NULL;
96 }
1d289787 97
9df4d361 98 RCL_SetDriverData(instance, phc);
1d289787
ML
99 return 1;
100}
101
102static void phc_finalise(RCL_Instance instance)
103{
9df4d361
ML
104 struct phc_instance *phc;
105
106 phc = (struct phc_instance *)RCL_GetDriverData(instance);
eceb8d99
ML
107
108 if (phc->extpps) {
109 SCH_RemoveFileHandler(phc->fd);
110 SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
111 HCL_DestroyInstance(phc->clock);
112 }
113
9df4d361
ML
114 close(phc->fd);
115 Free(phc);
1d289787
ML
116}
117
eceb8d99
ML
118static void read_ext_pulse(int fd, int event, void *anything)
119{
120 RCL_Instance instance;
121 struct phc_instance *phc;
122 struct timespec phc_ts, local_ts;
123 double local_err;
124 int channel;
125
126 instance = anything;
127 phc = RCL_GetDriverData(instance);
128
129 if (!SYS_Linux_ReadPHCExtTimestamp(phc->fd, &phc_ts, &channel))
130 return;
131
132 if (channel != phc->channel) {
133 DEBUG_LOG("Unexpected extts channel %d\n", channel);
134 return;
135 }
136
137 if (!HCL_CookTime(phc->clock, &phc_ts, &local_ts, &local_err))
138 return;
139
140 RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
141 UTI_DiffTimespecsToDouble(&phc_ts, &local_ts));
142}
143
1d289787
ML
144static int phc_poll(RCL_Instance instance)
145{
9df4d361 146 struct phc_instance *phc;
eceb8d99
ML
147 struct timespec phc_ts, sys_ts, local_ts;
148 double offset, phc_err, local_err;
1d289787 149
9df4d361 150 phc = (struct phc_instance *)RCL_GetDriverData(instance);
1d289787 151
a60fc73e 152 if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance),
eceb8d99
ML
153 &phc->mode, &phc_ts, &sys_ts, &phc_err))
154 return 0;
155
156 if (phc->extpps) {
157 LCL_CookTime(&sys_ts, &local_ts, &local_err);
158 HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
9df4d361 159 return 0;
eceb8d99 160 }
1d289787 161
9df4d361 162 offset = UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts);
1d289787 163
eceb8d99 164 DEBUG_LOG("PHC offset: %+.9f err: %.9f", offset, phc_err);
1d289787 165
9df4d361 166 return RCL_AddSample(instance, &sys_ts, offset, LEAP_Normal);
1d289787
ML
167}
168
169RefclockDriver RCL_PHC_driver = {
170 phc_initialise,
171 phc_finalise,
172 phc_poll
173};
174
175#else
176
177RefclockDriver RCL_PHC_driver = { NULL, NULL, NULL };
178
179#endif