]> git.ipfire.org Git - thirdparty/chrony.git/blame - refclock_phc.c
clientlog: add support for KoD rate limiting
[thirdparty/chrony.git] / refclock_phc.c
CommitLineData
1d289787
ML
1/*
2 chronyd/chronyc - Programs for keeping computer clocks accurate.
3
4 **********************************************************************
5f66722b 5 * Copyright (C) Miroslav Lichvar 2013, 2017, 2023
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
c8649ccb
ML
36#include <sys/sysmacros.h>
37
38#include "array.h"
1d289787 39#include "refclock.h"
eceb8d99
ML
40#include "hwclock.h"
41#include "local.h"
1d289787 42#include "logging.h"
9df4d361 43#include "memory.h"
1d289787 44#include "util.h"
eceb8d99 45#include "sched.h"
9df4d361 46#include "sys_linux.h"
1d289787 47
9df4d361
ML
48struct phc_instance {
49 int fd;
c8649ccb 50 int dev_index;
9df4d361 51 int mode;
a60fc73e 52 int nocrossts;
eceb8d99
ML
53 int extpps;
54 int pin;
55 int channel;
c8649ccb 56 struct timespec last_extts;
eceb8d99 57 HCL_Instance clock;
1d289787
ML
58};
59
c8649ccb
ML
60/* Array of RCL_Instance with enabled extpps */
61static ARR_Instance extts_phcs = NULL;
62
eceb8d99
ML
63static void read_ext_pulse(int sockfd, int event, void *anything);
64
1d289787
ML
65static int phc_initialise(RCL_Instance instance)
66{
a78031ce 67 const char *options[] = {"nocrossts", "extpps", "pin", "channel", "clear", NULL};
9df4d361 68 struct phc_instance *phc;
eceb8d99 69 int phc_fd, rising_edge;
c8649ccb 70 struct stat st;
eceb8d99 71 char *path, *s;
1d289787 72
a78031ce
ML
73 RCL_CheckDriverOptions(instance, options);
74
1d289787
ML
75 path = RCL_GetDriverParameter(instance);
76
9df4d361 77 phc_fd = SYS_Linux_OpenPHC(path, 0);
aa22c515 78 if (phc_fd < 0)
f282856c 79 LOG_FATAL("Could not open PHC");
1d289787 80
9df4d361
ML
81 phc = MallocNew(struct phc_instance);
82 phc->fd = phc_fd;
c8649ccb
ML
83 if (fstat(phc_fd, &st) < 0 || !S_ISCHR(st.st_mode))
84 LOG_FATAL("Could not get PHC index");
85 phc->dev_index = minor(st.st_rdev);
9df4d361 86 phc->mode = 0;
a60fc73e 87 phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
eceb8d99 88 phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
c8649ccb 89 UTI_ZeroTimespec(&phc->last_extts);
09b7f77f
ML
90 phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)),
91 RCL_GetPrecision(instance));
92
eceb8d99
ML
93 if (phc->extpps) {
94 s = RCL_GetDriverOption(instance, "pin");
95 phc->pin = s ? atoi(s) : 0;
96 s = RCL_GetDriverOption(instance, "channel");
97 phc->channel = s ? atoi(s) : 0;
98 rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
eceb8d99
ML
99
100 if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
101 rising_edge, !rising_edge, 1))
102 LOG_FATAL("Could not enable external PHC timestamping");
103
104 SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
c8649ccb
ML
105
106 if (!extts_phcs)
107 extts_phcs = ARR_CreateInstance(sizeof (RCL_Instance));
108 ARR_AppendElement(extts_phcs, &instance);
eceb8d99
ML
109 } else {
110 phc->pin = phc->channel = 0;
eceb8d99 111 }
1d289787 112
9df4d361 113 RCL_SetDriverData(instance, phc);
1d289787
ML
114 return 1;
115}
116
117static void phc_finalise(RCL_Instance instance)
118{
9df4d361 119 struct phc_instance *phc;
c8649ccb 120 unsigned int i;
9df4d361
ML
121
122 phc = (struct phc_instance *)RCL_GetDriverData(instance);
eceb8d99
ML
123
124 if (phc->extpps) {
125 SCH_RemoveFileHandler(phc->fd);
126 SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
c8649ccb
ML
127
128 for (i = 0; i < ARR_GetSize(extts_phcs); i++) {
129 if ((*(RCL_Instance *)ARR_GetElement(extts_phcs, i)) == instance)
130 ARR_RemoveElement(extts_phcs, i--);
131 }
132 if (ARR_GetSize(extts_phcs) == 0) {
133 ARR_DestroyInstance(extts_phcs);
134 extts_phcs = NULL;
135 }
eceb8d99
ML
136 }
137
09b7f77f 138 HCL_DestroyInstance(phc->clock);
9df4d361
ML
139 close(phc->fd);
140 Free(phc);
1d289787
ML
141}
142
c8649ccb 143static void process_ext_pulse(RCL_Instance instance, struct timespec *phc_ts)
eceb8d99 144{
eceb8d99 145 struct phc_instance *phc;
c8649ccb 146 struct timespec local_ts;
eceb8d99 147 double local_err;
eceb8d99 148
eceb8d99
ML
149 phc = RCL_GetDriverData(instance);
150
c8649ccb
ML
151 if (UTI_CompareTimespecs(&phc->last_extts, phc_ts) == 0) {
152 DEBUG_LOG("Ignoring duplicated PHC timestamp");
eceb8d99
ML
153 return;
154 }
c8649ccb 155 phc->last_extts = *phc_ts;
eceb8d99 156
c8649ccb 157 if (!HCL_CookTime(phc->clock, phc_ts, &local_ts, &local_err))
eceb8d99
ML
158 return;
159
160 RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
c8649ccb
ML
161 UTI_DiffTimespecsToDouble(phc_ts, &local_ts));
162}
163
164static void read_ext_pulse(int fd, int event, void *anything)
165{
166 RCL_Instance instance;
167 struct phc_instance *phc1, *phc2;
168 struct timespec phc_ts;
169 unsigned int i;
170 int channel;
171
172 if (!SYS_Linux_ReadPHCExtTimestamp(fd, &phc_ts, &channel))
173 return;
174
175 instance = anything;
176 phc1 = RCL_GetDriverData(instance);
177
178 /* The Linux kernel (as of 6.2) has one shared queue of timestamps for all
179 descriptors of the same PHC. Search for all refclocks that expect
180 the timestamp. */
181
182 for (i = 0; i < ARR_GetSize(extts_phcs); i++) {
183 instance = *(RCL_Instance *)ARR_GetElement(extts_phcs, i);
184 phc2 = RCL_GetDriverData(instance);
185 if (!phc2->extpps || phc2->dev_index != phc1->dev_index || phc2->channel != channel)
186 continue;
187 process_ext_pulse(instance, &phc_ts);
188 }
eceb8d99
ML
189}
190
09b7f77f
ML
191#define PHC_READINGS 25
192
1d289787
ML
193static int phc_poll(RCL_Instance instance)
194{
09b7f77f 195 struct timespec phc_ts, sys_ts, local_ts, readings[PHC_READINGS][3];
9df4d361 196 struct phc_instance *phc;
663dde1a 197 double phc_err, local_err;
09b7f77f 198 int n_readings;
1d289787 199
9df4d361 200 phc = (struct phc_instance *)RCL_GetDriverData(instance);
1d289787 201
09b7f77f
ML
202 n_readings = SYS_Linux_GetPHCReadings(phc->fd, phc->nocrossts, &phc->mode,
203 PHC_READINGS, readings);
204 if (n_readings < 1)
eceb8d99
ML
205 return 0;
206
09b7f77f
ML
207 if (!HCL_ProcessReadings(phc->clock, n_readings, readings, &phc_ts, &sys_ts, &phc_err))
208 return 0;
209
210 LCL_CookTime(&sys_ts, &local_ts, &local_err);
211 HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
212
213 if (phc->extpps)
9df4d361 214 return 0;
1d289787 215
663dde1a
ML
216 DEBUG_LOG("PHC offset: %+.9f err: %.9f",
217 UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts), phc_err);
1d289787 218
663dde1a 219 return RCL_AddSample(instance, &sys_ts, &phc_ts, LEAP_Normal);
1d289787
ML
220}
221
222RefclockDriver RCL_PHC_driver = {
223 phc_initialise,
224 phc_finalise,
225 phc_poll
226};
227
228#else
229
230RefclockDriver RCL_PHC_driver = { NULL, NULL, NULL };
231
232#endif