]> git.ipfire.org Git - thirdparty/chrony.git/blob - rtc_linux.c
test: use env shebang in all bash scripts
[thirdparty/chrony.git] / rtc_linux.c
1 /*
2 chronyd/chronyc - Programs for keeping computer clocks accurate.
3
4 **********************************************************************
5 * Copyright (C) Richard P. Curnow 1997-2003
6 * Copyright (C) Miroslav Lichvar 2012-2014
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of version 2 of the GNU General Public License as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 **********************************************************************
22
23 =======================================================================
24
25 Real-time clock driver for linux. This interfaces the program with
26 the clock that keeps time when the machine is turned off.
27
28 */
29
30 #include "config.h"
31
32 #include "sysincl.h"
33
34 #include <linux/rtc.h>
35
36 #include "logging.h"
37 #include "sched.h"
38 #include "local.h"
39 #include "util.h"
40 #include "sys_linux.h"
41 #include "reference.h"
42 #include "regress.h"
43 #include "rtc.h"
44 #include "rtc_linux.h"
45 #include "conf.h"
46 #include "memory.h"
47
48 /* ================================================== */
49 /* Forward prototypes */
50
51 static void measurement_timeout(void *any);
52
53 static void read_from_device(int fd_, int event, void *any);
54
55 /* ================================================== */
56
57 typedef enum {
58 OM_NORMAL,
59 OM_INITIAL,
60 OM_AFTERTRIM
61 } OperatingMode;
62
63 static OperatingMode operating_mode = OM_NORMAL;
64
65 /* ================================================== */
66
67 static int fd = -1;
68
69 #define LOWEST_MEASUREMENT_PERIOD 15
70 #define HIGHEST_MEASUREMENT_PERIOD 480
71 #define N_SAMPLES_PER_REGRESSION 1
72
73 static int measurement_period = LOWEST_MEASUREMENT_PERIOD;
74
75 static SCH_TimeoutID timeout_id = 0;
76
77 static int skip_interrupts;
78
79 /* ================================================== */
80
81 /* Maximum number of samples held */
82 #define MAX_SAMPLES 64
83
84 /* Real time clock samples. We store the seconds count as originally
85 measured, together with a 'trim' that compensates these values for
86 any steps made to the RTC to bring it back into line
87 occasionally. The trim is in seconds. */
88 static time_t *rtc_sec = NULL;
89 static double *rtc_trim = NULL;
90
91 /* Reference time, against which delta times on the RTC scale are measured */
92 static time_t rtc_ref;
93
94
95 /* System clock samples associated with the above samples. */
96 static struct timespec *system_times = NULL;
97
98 /* Number of samples currently stored. */
99 static int n_samples;
100
101 /* Number of new samples since last regression */
102 static int n_samples_since_regression;
103
104 /* Number of runs of residuals in last regression (for logging) */
105 static int n_runs;
106
107 /* Coefficients */
108 /* Whether they are valid */
109 static int coefs_valid;
110
111 /* Reference time */
112 static time_t coef_ref_time;
113 /* Number of seconds by which RTC was fast of the system time at coef_ref_time */
114 static double coef_seconds_fast;
115
116 /* Estimated number of seconds that RTC gains relative to system time
117 for each second of ITS OWN time */
118 static double coef_gain_rate;
119
120 /* Gain rate saved just before we step the RTC to correct it to the
121 nearest second, so that we can write a useful set of coefs to the
122 RTC data file once we have reacquired its offset after the step */
123 static double saved_coef_gain_rate;
124
125 /* Threshold for automatic RTC trimming in seconds, zero when disabled */
126 static double autotrim_threshold;
127
128 /* Filename supplied by config file where RTC coefficients are
129 stored. */
130 static char *coefs_file_name;
131
132 /* ================================================== */
133 /* Coefficients read from file at start of run. */
134
135 /* Whether we have tried to load the coefficients */
136 static int tried_to_load_coefs = 0;
137
138 /* Whether valid coefficients were read */
139 static int valid_coefs_from_file = 0;
140
141 /* Coefs read in */
142 static time_t file_ref_time;
143 static double file_ref_offset, file_rate_ppm;
144
145 /* ================================================== */
146
147 /* Flag to remember whether to assume the RTC is running on UTC */
148 static int rtc_on_utc = 1;
149
150 /* ================================================== */
151
152 static LOG_FileID logfileid;
153
154 /* ================================================== */
155
156 static void (*after_init_hook)(void *) = NULL;
157 static void *after_init_hook_arg = NULL;
158
159 /* ================================================== */
160
161 static void
162 discard_samples(int new_first)
163 {
164 int n_to_save;
165
166 assert(new_first >= 0 && new_first < n_samples);
167
168 n_to_save = n_samples - new_first;
169
170 memmove(rtc_sec, rtc_sec + new_first, n_to_save * sizeof(time_t));
171 memmove(rtc_trim, rtc_trim + new_first, n_to_save * sizeof(double));
172 memmove(system_times, system_times + new_first, n_to_save * sizeof(struct timespec));
173
174 n_samples = n_to_save;
175 }
176
177 /* ================================================== */
178
179 #define NEW_FIRST_WHEN_FULL 4
180
181 static void
182 accumulate_sample(time_t rtc, struct timespec *sys)
183 {
184
185 if (n_samples == MAX_SAMPLES) {
186 /* Discard oldest samples */
187 discard_samples(NEW_FIRST_WHEN_FULL);
188 }
189
190 /* Discard all samples if the RTC was stepped back (not our trim) */
191 if (n_samples > 0 && rtc_sec[n_samples - 1] - rtc >= rtc_trim[n_samples - 1]) {
192 DEBUG_LOG("RTC samples discarded");
193 n_samples = 0;
194 }
195
196 /* Always use most recent sample as reference */
197 /* use sample only if n_sample is not negative*/
198 if(n_samples >=0)
199 {
200 rtc_ref = rtc;
201 rtc_sec[n_samples] = rtc;
202 rtc_trim[n_samples] = 0.0;
203 system_times[n_samples] = *sys;
204 ++n_samples_since_regression;
205 }
206 ++n_samples;
207 }
208
209 /* ================================================== */
210 /* The new_sample flag is to indicate whether to adjust the
211 measurement period depending on the behaviour of the standard
212 deviation. */
213
214 static void
215 run_regression(int new_sample,
216 int *valid,
217 time_t *ref,
218 double *fast,
219 double *slope)
220 {
221 double rtc_rel[MAX_SAMPLES]; /* Relative times on RTC axis */
222 double offsets[MAX_SAMPLES]; /* How much the RTC is fast of the system clock */
223 int i;
224 double est_intercept, est_slope;
225 int best_new_start;
226
227 if (n_samples > 0) {
228
229 for (i=0; i<n_samples; i++) {
230 rtc_rel[i] = rtc_trim[i] + (double)(rtc_sec[i] - rtc_ref);
231 offsets[i] = ((double) (rtc_ref - system_times[i].tv_sec) -
232 (1.0e-9 * system_times[i].tv_nsec) +
233 rtc_rel[i]);
234
235 }
236
237 if (RGR_FindBestRobustRegression
238 (rtc_rel, offsets,
239 n_samples, 1.0e-9,
240 &est_intercept, &est_slope,
241 &n_runs,
242 &best_new_start)) {
243
244 /* Calculate and store coefficients. We don't do any error
245 bounds processing on any of these. */
246 *valid = 1;
247 *ref = rtc_ref;
248 *fast = est_intercept;
249 *slope = est_slope;
250
251 if (best_new_start > 0) {
252 discard_samples(best_new_start);
253 }
254
255
256 } else {
257 /* Keep existing coefficients. */
258 }
259 } else {
260 /* Keep existing coefficients. */
261 }
262
263 }
264
265 /* ================================================== */
266
267 static void
268 slew_samples
269 (struct timespec *raw, struct timespec *cooked,
270 double dfreq,
271 double doffset,
272 LCL_ChangeType change_type,
273 void *anything)
274 {
275 int i;
276 double delta_time;
277 double old_seconds_fast, old_gain_rate;
278
279 if (change_type == LCL_ChangeUnknownStep) {
280 /* Drop all samples. */
281 n_samples = 0;
282 }
283
284 for (i=0; i<n_samples; i++) {
285 UTI_AdjustTimespec(system_times + i, cooked, system_times + i, &delta_time,
286 dfreq, doffset);
287 }
288
289 old_seconds_fast = coef_seconds_fast;
290 old_gain_rate = coef_gain_rate;
291
292 if (coefs_valid) {
293 coef_seconds_fast += doffset;
294 coef_gain_rate += dfreq * (1.0 - coef_gain_rate);
295 }
296
297 DEBUG_LOG("dfreq=%.8f doffset=%.6f old_fast=%.6f old_rate=%.3f new_fast=%.6f new_rate=%.3f",
298 dfreq, doffset,
299 old_seconds_fast, 1.0e6 * old_gain_rate,
300 coef_seconds_fast, 1.0e6 * coef_gain_rate);
301 }
302
303 /* ================================================== */
304
305 /* Function to convert from a time_t value represenging UTC to the
306 corresponding real time clock 'DMY HMS' form, taking account of
307 whether the user runs his RTC on the local time zone or UTC */
308
309 static struct tm *
310 rtc_from_t(const time_t *t)
311 {
312 if (rtc_on_utc) {
313 return gmtime(t);
314 } else {
315 return localtime(t);
316 }
317 }
318
319 /* ================================================== */
320
321 /* Inverse function to get back from RTC 'DMY HMS' form to time_t UTC
322 form. This essentially uses mktime(), but involves some awful
323 complexity to cope with timezones. The problem is that mktime's
324 behaviour with regard to the daylight saving flag in the 'struct
325 tm' does not seem to be reliable across all systems, unless that
326 flag is set to zero.
327
328 tm_isdst = -1 does not seem to work with all libc's - it is treated
329 as meaning there is DST, or fails completely. (It is supposed to
330 use the timezone info to work out whether summer time is active at
331 the specified epoch).
332
333 tm_isdst = 1 fails if the local timezone has no summer time defined.
334
335 The approach taken is as follows. Suppose the RTC is on localtime.
336 We perform all mktime calls with the tm_isdst field set to zero.
337
338 Let y be the RTC reading in 'DMY HMS' form. Let M be the mktime
339 function with tm_isdst=0 and L be the localtime function.
340
341 We seek x such that y = L(x). Now there will exist a value Z(t)
342 such that M(L(t)) = t + Z(t) for all t, where Z(t) depends on
343 whether daylight saving is active at time t.
344
345 We want L(x) = y. Therefore M(L(x)) = x + Z = M(y). But
346 M(L(M(y))) = M(y) + Z. Therefore x = M(y) - Z = M(y) - (M(L(M(y)))
347 - M(y)).
348
349 The case for the RTC running on UTC is identical but without the
350 potential complication that Z depends on t.
351 */
352
353 static time_t
354 t_from_rtc(struct tm *stm) {
355 struct tm temp1, temp2, *tm;
356 long diff;
357 time_t t1, t2;
358
359 temp1 = *stm;
360 temp1.tm_isdst = 0;
361
362 t1 = mktime(&temp1);
363
364 tm = rtc_on_utc ? gmtime(&t1) : localtime(&t1);
365 if (!tm) {
366 DEBUG_LOG("gmtime()/localtime() failed");
367 return -1;
368 }
369
370 temp2 = *tm;
371 temp2.tm_isdst = 0;
372 t2 = mktime(&temp2);
373 diff = t2 - t1;
374
375 if (t1 - diff == -1)
376 DEBUG_LOG("Could not convert RTC time");
377
378 return t1 - diff;
379 }
380
381 /* ================================================== */
382
383 static void
384 read_hwclock_file(const char *hwclock_file)
385 {
386 FILE *in;
387 char line[256];
388 int i;
389
390 if (!hwclock_file || !hwclock_file[0])
391 return;
392
393 in = UTI_OpenFile(NULL, hwclock_file, NULL, 'r', 0);
394 if (!in)
395 return;
396
397 /* Read third line from the file. */
398 for (i = 0; i < 3; i++) {
399 if (!fgets(line, sizeof(line), in))
400 break;
401 }
402
403 fclose(in);
404
405 if (i == 3 && !strncmp(line, "LOCAL", 5)) {
406 rtc_on_utc = 0;
407 } else if (i == 3 && !strncmp(line, "UTC", 3)) {
408 rtc_on_utc = 1;
409 } else {
410 LOG(LOGS_WARN, "Could not read RTC LOCAL/UTC setting from %s", hwclock_file);
411 }
412 }
413
414 /* ================================================== */
415
416 static void
417 setup_config(void)
418 {
419 if (CNF_GetRtcOnUtc()) {
420 rtc_on_utc = 1;
421 } else {
422 rtc_on_utc = 0;
423 }
424
425 read_hwclock_file(CNF_GetHwclockFile());
426
427 autotrim_threshold = CNF_GetRtcAutotrim();
428 }
429
430 /* ================================================== */
431 /* Read the coefficients from the file where they were saved
432 the last time the program was run. */
433
434 static void
435 read_coefs_from_file(void)
436 {
437 FILE *in;
438
439 if (!tried_to_load_coefs) {
440
441 valid_coefs_from_file = 0; /* only gets set true if we succeed */
442
443 tried_to_load_coefs = 1;
444
445 if (coefs_file_name &&
446 (in = UTI_OpenFile(NULL, coefs_file_name, NULL, 'r', 0))) {
447 if (fscanf(in, "%d%ld%lf%lf",
448 &valid_coefs_from_file,
449 &file_ref_time,
450 &file_ref_offset,
451 &file_rate_ppm) == 4) {
452 } else {
453 LOG(LOGS_WARN, "Could not read coefficients from %s", coefs_file_name);
454 }
455 fclose(in);
456 }
457 }
458 }
459
460 /* ================================================== */
461 /* Write the coefficients to the file where they will be read
462 the next time the program is run. */
463
464 static int
465 write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
466 {
467 FILE *out;
468
469 /* Create a temporary file with a '.tmp' extension. */
470 out = UTI_OpenFile(NULL, coefs_file_name, ".tmp", 'w', 0644);
471 if (!out)
472 return RTC_ST_BADFILE;
473
474 /* Gain rate is written out in ppm */
475 fprintf(out, "%1d %ld %.6f %.3f\n", valid, ref_time, offset, 1.0e6 * rate);
476 fclose(out);
477
478 /* Rename the temporary file to the correct location */
479 if (!UTI_RenameTempFile(NULL, coefs_file_name, ".tmp", NULL))
480 return RTC_ST_BADFILE;
481
482 return RTC_ST_OK;
483 }
484
485 /* ================================================== */
486
487 static int
488 switch_interrupts(int on_off)
489 {
490 if (ioctl(fd, on_off ? RTC_UIE_ON : RTC_UIE_OFF, 0) < 0) {
491 LOG(LOGS_ERR, "Could not %s RTC interrupt : %s",
492 on_off ? "enable" : "disable", strerror(errno));
493 return 0;
494 }
495
496 if (on_off)
497 skip_interrupts = 1;
498
499 return 1;
500 }
501
502 /* ================================================== */
503 /* file_name is the name of the file where we save the RTC params
504 between executions. Return status is whether we could initialise
505 on this version of the system. */
506
507 int
508 RTC_Linux_Initialise(void)
509 {
510 /* Try to open the device */
511 fd = open(CNF_GetRtcDevice(), O_RDWR);
512 if (fd < 0) {
513 LOG(LOGS_ERR, "Could not open RTC device %s : %s",
514 CNF_GetRtcDevice(), strerror(errno));
515 return 0;
516 }
517
518 /* Make sure the RTC supports interrupts */
519 if (!switch_interrupts(1) || !switch_interrupts(0)) {
520 close(fd);
521 return 0;
522 }
523
524 /* Close on exec */
525 UTI_FdSetCloexec(fd);
526
527 rtc_sec = MallocArray(time_t, MAX_SAMPLES);
528 rtc_trim = MallocArray(double, MAX_SAMPLES);
529 system_times = MallocArray(struct timespec, MAX_SAMPLES);
530
531 /* Setup details depending on configuration options */
532 setup_config();
533
534 /* In case it didn't get done by pre-init */
535 coefs_file_name = CNF_GetRtcFile();
536
537 n_samples = 0;
538 n_samples_since_regression = 0;
539 n_runs = 0;
540 coefs_valid = 0;
541
542 measurement_period = LOWEST_MEASUREMENT_PERIOD;
543
544 operating_mode = OM_NORMAL;
545
546 /* Register file handler */
547 SCH_AddFileHandler(fd, SCH_FILE_INPUT, read_from_device, NULL);
548
549 /* Register slew handler */
550 LCL_AddParameterChangeHandler(slew_samples, NULL);
551
552 logfileid = CNF_GetLogRtc() ? LOG_FileOpen("rtc",
553 " Date (UTC) Time RTC fast (s) Val Est fast (s) Slope (ppm) Ns Nr Meas")
554 : -1;
555 return 1;
556 }
557
558 /* ================================================== */
559
560 void
561 RTC_Linux_Finalise(void)
562 {
563 SCH_RemoveTimeout(timeout_id);
564 timeout_id = 0;
565
566 /* Remove input file handler */
567 if (fd >= 0) {
568 SCH_RemoveFileHandler(fd);
569 switch_interrupts(0);
570 close(fd);
571
572 /* Save the RTC data */
573 (void) RTC_Linux_WriteParameters();
574
575 }
576
577 if (rtc_sec)
578 LCL_RemoveParameterChangeHandler(slew_samples, NULL);
579
580 Free(rtc_sec);
581 Free(rtc_trim);
582 Free(system_times);
583 }
584
585 /* ================================================== */
586
587 static void
588 measurement_timeout(void *any)
589 {
590 timeout_id = 0;
591 switch_interrupts(1);
592 }
593
594 /* ================================================== */
595
596 static void
597 set_rtc(time_t new_rtc_time)
598 {
599 struct tm rtc_tm;
600 struct rtc_time rtc_raw;
601 int status;
602
603 rtc_tm = *rtc_from_t(&new_rtc_time);
604
605 rtc_raw.tm_sec = rtc_tm.tm_sec;
606 rtc_raw.tm_min = rtc_tm.tm_min;
607 rtc_raw.tm_hour = rtc_tm.tm_hour;
608 rtc_raw.tm_mday = rtc_tm.tm_mday;
609 rtc_raw.tm_mon = rtc_tm.tm_mon;
610 rtc_raw.tm_year = rtc_tm.tm_year;
611 rtc_raw.tm_wday = rtc_tm.tm_wday;
612 rtc_raw.tm_yday = rtc_tm.tm_yday;
613 rtc_raw.tm_isdst = rtc_tm.tm_isdst;
614
615 status = ioctl(fd, RTC_SET_TIME, &rtc_raw);
616 if (status < 0) {
617 LOG(LOGS_ERR, "Could not set RTC time");
618 }
619
620 }
621
622 /* ================================================== */
623
624 static void
625 handle_initial_trim(void)
626 {
627 double rate;
628 long delta_time;
629 double rtc_error_now, sys_error_now;
630
631 /* The idea is to accumulate some number of samples at 1 second
632 intervals, then do a robust regression fit to this. This
633 should give a good fix on the intercept (=system clock error
634 rel to RTC) at a particular time, removing risk of any
635 particular sample being an outlier. We can then look at the
636 elapsed interval since the epoch recorded in the RTC file,
637 and correct the system time accordingly. */
638
639 run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate);
640
641 n_samples_since_regression = 0;
642
643 /* Set sample number to -1 so the next sample is not used, as it will not yet be corrected for System Trim*/
644
645 n_samples = -1;
646
647
648 read_coefs_from_file();
649
650 if (valid_coefs_from_file) {
651 /* Can process data */
652 delta_time = coef_ref_time - file_ref_time;
653 rate = 1.0e-6 * file_rate_ppm;
654 rtc_error_now = file_ref_offset + rate * (double) delta_time;
655
656 /* sys_error_now is positive if the system clock is fast */
657 sys_error_now = rtc_error_now - coef_seconds_fast;
658
659 LCL_AccumulateOffset(sys_error_now, 0.0);
660 LOG(LOGS_INFO, "System clock off from RTC by %f seconds (slew)",
661 sys_error_now);
662 } else {
663 LOG(LOGS_WARN, "No valid rtcfile coefficients");
664 }
665
666 coefs_valid = 0;
667
668 (after_init_hook)(after_init_hook_arg);
669
670 operating_mode = OM_NORMAL;
671 }
672
673 /* ================================================== */
674
675 static void
676 handle_relock_after_trim(void)
677 {
678 int valid;
679 time_t ref;
680 double fast, slope;
681
682 valid = 0;
683 run_regression(1, &valid, &ref, &fast, &slope);
684
685 if (valid) {
686 write_coefs_to_file(1,ref,fast,saved_coef_gain_rate);
687 } else {
688 DEBUG_LOG("Could not do regression after trim");
689 }
690
691 coefs_valid = 0;
692 n_samples = 0;
693 n_samples_since_regression = 0;
694 operating_mode = OM_NORMAL;
695 measurement_period = LOWEST_MEASUREMENT_PERIOD;
696 }
697
698 /* ================================================== */
699
700 static void
701 maybe_autotrim(void)
702 {
703 /* Trim only when in normal mode, the coefficients are fresh, the current
704 offset is above the threshold and the system clock is synchronized */
705
706 if (operating_mode != OM_NORMAL || !coefs_valid || n_samples_since_regression)
707 return;
708
709 if (autotrim_threshold <= 0.0 || fabs(coef_seconds_fast) < autotrim_threshold)
710 return;
711
712 if (REF_GetOurStratum() >= 16)
713 return;
714
715 RTC_Linux_Trim();
716 }
717
718 /* ================================================== */
719
720 static void
721 process_reading(time_t rtc_time, struct timespec *system_time)
722 {
723 double rtc_fast;
724
725 accumulate_sample(rtc_time, system_time);
726
727 switch (operating_mode) {
728 case OM_NORMAL:
729
730 if (n_samples_since_regression >= N_SAMPLES_PER_REGRESSION) {
731 run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate);
732 n_samples_since_regression = 0;
733 maybe_autotrim();
734 }
735
736 break;
737 case OM_INITIAL:
738 if (n_samples_since_regression >= 8) {
739 handle_initial_trim();
740 }
741 break;
742 case OM_AFTERTRIM:
743 if (n_samples_since_regression >= 8) {
744 handle_relock_after_trim();
745 }
746 break;
747 default:
748 assert(0);
749 break;
750 }
751
752
753 if (logfileid != -1) {
754 rtc_fast = (rtc_time - system_time->tv_sec) - 1.0e-9 * system_time->tv_nsec;
755
756 LOG_FileWrite(logfileid, "%s %14.6f %1d %14.6f %12.3f %2d %2d %4d",
757 UTI_TimeToLogForm(system_time->tv_sec),
758 rtc_fast,
759 coefs_valid,
760 coef_seconds_fast, coef_gain_rate * 1.0e6, n_samples, n_runs, measurement_period);
761 }
762
763 }
764
765 /* ================================================== */
766
767 static void
768 read_from_device(int fd_, int event, void *any)
769 {
770 int status;
771 unsigned long data;
772 struct timespec sys_time;
773 struct rtc_time rtc_raw;
774 struct tm rtc_tm;
775 time_t rtc_t;
776 int error = 0;
777
778 status = read(fd, &data, sizeof(data));
779
780 if (status < 0) {
781 /* This looks like a bad error : the file descriptor was indicating it was
782 * ready to read but we couldn't read anything. Give up. */
783 LOG(LOGS_ERR, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
784 SCH_RemoveFileHandler(fd);
785 switch_interrupts(0); /* Likely to raise error too, but just to be sure... */
786 close(fd);
787 fd = -1;
788 return;
789 }
790
791 if (skip_interrupts > 0) {
792 /* Wait for the next interrupt, this one may be bogus */
793 skip_interrupts--;
794 return;
795 }
796
797 if ((data & RTC_UF) == RTC_UF) {
798 /* Update interrupt detected */
799
800 /* Read RTC time, sandwiched between two polls of the system clock
801 so we can bound any error. */
802
803 SCH_GetLastEventTime(&sys_time, NULL, NULL);
804
805 status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
806 if (status < 0) {
807 LOG(LOGS_ERR, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno));
808 error = 1;
809 goto turn_off_interrupt;
810 }
811
812 /* Convert RTC time into a struct timespec */
813 rtc_tm.tm_sec = rtc_raw.tm_sec;
814 rtc_tm.tm_min = rtc_raw.tm_min;
815 rtc_tm.tm_hour = rtc_raw.tm_hour;
816 rtc_tm.tm_mday = rtc_raw.tm_mday;
817 rtc_tm.tm_mon = rtc_raw.tm_mon;
818 rtc_tm.tm_year = rtc_raw.tm_year;
819
820 rtc_t = t_from_rtc(&rtc_tm);
821
822 if (rtc_t == (time_t)(-1)) {
823 error = 1;
824 goto turn_off_interrupt;
825 }
826
827 process_reading(rtc_t, &sys_time);
828
829 if (n_samples < 4) {
830 measurement_period = LOWEST_MEASUREMENT_PERIOD;
831 } else if (n_samples < 6) {
832 measurement_period = LOWEST_MEASUREMENT_PERIOD << 1;
833 } else if (n_samples < 10) {
834 measurement_period = LOWEST_MEASUREMENT_PERIOD << 2;
835 } else if (n_samples < 14) {
836 measurement_period = LOWEST_MEASUREMENT_PERIOD << 3;
837 } else {
838 measurement_period = LOWEST_MEASUREMENT_PERIOD << 4;
839 }
840
841 }
842
843 turn_off_interrupt:
844
845 switch (operating_mode) {
846 case OM_INITIAL:
847 if (error) {
848 DEBUG_LOG("Could not complete initial step due to errors");
849 operating_mode = OM_NORMAL;
850 (after_init_hook)(after_init_hook_arg);
851
852 switch_interrupts(0);
853
854 timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
855 }
856
857 break;
858
859 case OM_AFTERTRIM:
860 if (error) {
861 DEBUG_LOG("Could not complete after trim relock due to errors");
862 operating_mode = OM_NORMAL;
863
864 switch_interrupts(0);
865
866 timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
867 }
868
869 break;
870
871 case OM_NORMAL:
872 switch_interrupts(0);
873
874 timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
875
876 break;
877 default:
878 assert(0);
879 break;
880 }
881
882 }
883
884 /* ================================================== */
885
886 void
887 RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything)
888 {
889 after_init_hook = after_hook;
890 after_init_hook_arg = anything;
891
892 operating_mode = OM_INITIAL;
893 timeout_id = 0;
894 switch_interrupts(1);
895 }
896
897 /* ================================================== */
898
899 void
900 RTC_Linux_StartMeasurements(void)
901 {
902 measurement_timeout(NULL);
903 }
904
905 /* ================================================== */
906
907 int
908 RTC_Linux_WriteParameters(void)
909 {
910 int retval;
911
912 if (fd < 0) {
913 return RTC_ST_NODRV;
914 }
915
916 if (coefs_valid) {
917 retval = write_coefs_to_file(1,coef_ref_time, coef_seconds_fast, coef_gain_rate);
918 } else {
919 /* Don't change the existing file, it may not be 100% valid but is our
920 current best guess. */
921 retval = RTC_ST_OK; /*write_coefs_to_file(0,0,0.0,0.0); */
922 }
923
924 return(retval);
925 }
926
927 /* ================================================== */
928 /* Try to set the system clock from the RTC, in the same manner as
929 /sbin/hwclock -s would do. We're not as picky about OS version
930 etc in this case, since we have fewer requirements regarding the
931 RTC behaviour than we do for the rest of the module. */
932
933 int
934 RTC_Linux_TimePreInit(time_t driftfile_time)
935 {
936 int fd, status;
937 struct rtc_time rtc_raw, rtc_raw_retry;
938 struct tm rtc_tm;
939 time_t rtc_t;
940 double accumulated_error, sys_offset;
941 struct timespec new_sys_time, old_sys_time;
942
943 coefs_file_name = CNF_GetRtcFile();
944
945 setup_config();
946 read_coefs_from_file();
947
948 fd = open(CNF_GetRtcDevice(), O_RDONLY);
949
950 if (fd < 0) {
951 return 0; /* Can't open it, and won't be able to later */
952 }
953
954 /* Retry reading the rtc until both read attempts give the same sec value.
955 This way the race condition is prevented that the RTC has updated itself
956 during the first read operation. */
957 do {
958 status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
959 if (status >= 0) {
960 status = ioctl(fd, RTC_RD_TIME, &rtc_raw_retry);
961 }
962 } while (status >= 0 && rtc_raw.tm_sec != rtc_raw_retry.tm_sec);
963
964 /* Read system clock */
965 LCL_ReadCookedTime(&old_sys_time, NULL);
966
967 close(fd);
968
969 if (status >= 0) {
970 /* Convert to seconds since 1970 */
971 rtc_tm.tm_sec = rtc_raw.tm_sec;
972 rtc_tm.tm_min = rtc_raw.tm_min;
973 rtc_tm.tm_hour = rtc_raw.tm_hour;
974 rtc_tm.tm_mday = rtc_raw.tm_mday;
975 rtc_tm.tm_mon = rtc_raw.tm_mon;
976 rtc_tm.tm_year = rtc_raw.tm_year;
977
978 rtc_t = t_from_rtc(&rtc_tm);
979
980 if (rtc_t != (time_t)(-1)) {
981
982 /* Work out approximatation to correct time (to about the
983 nearest second) */
984 if (valid_coefs_from_file) {
985 accumulated_error = file_ref_offset +
986 (rtc_t - file_ref_time) * 1.0e-6 * file_rate_ppm;
987 } else {
988 accumulated_error = 0.0;
989 }
990
991 /* Correct time */
992
993 new_sys_time.tv_sec = rtc_t;
994 /* Average error in the RTC reading */
995 new_sys_time.tv_nsec = 500000000;
996
997 UTI_AddDoubleToTimespec(&new_sys_time, -accumulated_error, &new_sys_time);
998
999 if (new_sys_time.tv_sec < driftfile_time) {
1000 LOG(LOGS_WARN, "RTC time before last driftfile modification (ignored)");
1001 return 0;
1002 }
1003
1004 sys_offset = UTI_DiffTimespecsToDouble(&old_sys_time, &new_sys_time);
1005
1006 /* Set system time only if the step is larger than 1 second */
1007 if (fabs(sys_offset) >= 1.0) {
1008 if (LCL_ApplyStepOffset(sys_offset))
1009 LOG(LOGS_INFO, "System time set from RTC");
1010 }
1011 } else {
1012 return 0;
1013 }
1014 } else {
1015 return 0;
1016 }
1017
1018 return 1;
1019 }
1020
1021 /* ================================================== */
1022
1023 int
1024 RTC_Linux_GetReport(RPT_RTC_Report *report)
1025 {
1026 report->ref_time.tv_sec = coef_ref_time;
1027 report->ref_time.tv_nsec = 0;
1028 report->n_samples = n_samples;
1029 report->n_runs = n_runs;
1030 if (n_samples > 1) {
1031 report->span_seconds = ((rtc_sec[n_samples-1] - rtc_sec[0]) +
1032 (long)(rtc_trim[n_samples-1] - rtc_trim[0]));
1033 } else {
1034 report->span_seconds = 0;
1035 }
1036 report->rtc_seconds_fast = coef_seconds_fast;
1037 report->rtc_gain_rate_ppm = 1.0e6 * coef_gain_rate;
1038 return 1;
1039 }
1040
1041 /* ================================================== */
1042
1043 int
1044 RTC_Linux_Trim(void)
1045 {
1046 struct timespec now;
1047
1048 /* Remember the slope coefficient - we won't be able to determine a
1049 good one in a few seconds when we determine the new offset! */
1050 saved_coef_gain_rate = coef_gain_rate;
1051
1052 if (fabs(coef_seconds_fast) > 1.0) {
1053
1054 LOG(LOGS_INFO, "RTC wrong by %.3f seconds (step)",
1055 coef_seconds_fast);
1056
1057 /* Do processing to set clock. Let R be the value we set the
1058 RTC to, then in 500ms the RTC ticks (R+1) (see comments in
1059 arch/i386/kernel/time.c about the behaviour of the real time
1060 clock chip). If S is the system time now, the error at the
1061 next RTC tick is given by E = (R+1) - (S+0.5). Ideally we
1062 want |E| <= 0.5, which implies R <= S <= R+1, i.e. R is just
1063 the rounded down part of S, i.e. the seconds part. */
1064
1065 LCL_ReadCookedTime(&now, NULL);
1066
1067 set_rtc(now.tv_sec);
1068
1069 /* All old samples will now look bogus under the new
1070 regime. */
1071 n_samples = 0;
1072 operating_mode = OM_AFTERTRIM;
1073
1074 /* Estimate the offset in case writertc is called or chronyd
1075 is terminated during rapid sampling */
1076 coef_seconds_fast = -now.tv_nsec / 1.0e9 + 0.5;
1077 coef_ref_time = now.tv_sec;
1078
1079 /* And start rapid sampling, interrupts on now */
1080 SCH_RemoveTimeout(timeout_id);
1081 timeout_id = 0;
1082 switch_interrupts(1);
1083 }
1084
1085 return 1;
1086
1087 }