Merge branch 'a3f/rtc-refclk' into 'master'
refclock: add new refclock for RTCs See merge request chrony/chrony!9
This commit is contained in:
commit
8b70af5dc2
6 changed files with 365 additions and 143 deletions
3
configure
vendored
3
configure
vendored
|
@ -839,6 +839,9 @@ if [ $feat_rtc = "1" ] && [ $try_rtc = "1" ] && \
|
||||||
then
|
then
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS rtc_linux.o"
|
EXTRA_OBJECTS="$EXTRA_OBJECTS rtc_linux.o"
|
||||||
add_def FEAT_RTC
|
add_def FEAT_RTC
|
||||||
|
if [ $feat_refclock = "1" ]; then
|
||||||
|
EXTRA_OBJECTS="$EXTRA_OBJECTS refclock_rtc.o"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $feat_refclock = "1" ] && [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \
|
if [ $feat_refclock = "1" ] && [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \
|
||||||
|
|
|
@ -478,7 +478,7 @@ the driver-specific parameter using the *:* character.
|
||||||
+
|
+
|
||||||
This directive can be used multiple times to specify multiple reference clocks.
|
This directive can be used multiple times to specify multiple reference clocks.
|
||||||
+
|
+
|
||||||
There are four drivers included in *chronyd*:
|
There are five drivers included in *chronyd*:
|
||||||
+
|
+
|
||||||
*PPS*:::
|
*PPS*:::
|
||||||
Driver for the kernel PPS (pulse per second) API. The parameter is the path to
|
Driver for the kernel PPS (pulse per second) API. The parameter is the path to
|
||||||
|
@ -596,6 +596,25 @@ refclock PHC /dev/ptp1:nocrossts poll 3 pps
|
||||||
refclock PHC /dev/ptp2:extpps:pin=1 width 0.2 poll 2
|
refclock PHC /dev/ptp2:extpps:pin=1 width 0.2 poll 2
|
||||||
----
|
----
|
||||||
+
|
+
|
||||||
|
*RTC*:::
|
||||||
|
Driver for using the Real Time Clock (RTC) as reference clock.
|
||||||
|
The parameter is the path to the RTC character device which should be used
|
||||||
|
as a time source.
|
||||||
|
This cannot be used cannot be used with the <<rtcfile,*rtcfile*>> or
|
||||||
|
<<rtcsync,*rtcsync*>> directive.
|
||||||
|
The driver supports the following options:
|
||||||
|
+
|
||||||
|
*utc*::::
|
||||||
|
Assume that RTC keeps Universal Coordinated Time (UTC) instead of local
|
||||||
|
time.
|
||||||
|
{blank}:::
|
||||||
|
+
|
||||||
|
Examples:
|
||||||
|
+
|
||||||
|
----
|
||||||
|
refclock RTC /dev/rtc0:utc
|
||||||
|
----
|
||||||
|
+
|
||||||
{blank}::
|
{blank}::
|
||||||
The *refclock* directive supports the following options:
|
The *refclock* directive supports the following options:
|
||||||
+
|
+
|
||||||
|
@ -2203,7 +2222,8 @@ The directive takes no arguments. It is equivalent to specifying the *-u*
|
||||||
switch to the Linux *hwclock* program.
|
switch to the Linux *hwclock* program.
|
||||||
+
|
+
|
||||||
Note that this setting is overridden by the <<hwclockfile,*hwclockfile*>> file
|
Note that this setting is overridden by the <<hwclockfile,*hwclockfile*>> file
|
||||||
and is not relevant for the <<rtcsync,*rtcsync*>> directive.
|
and is not relevant for the <<rtcsync,*rtcsync*>> directive or when the RTC
|
||||||
|
is used as clock reference.
|
||||||
|
|
||||||
[[rtcsync]]*rtcsync*::
|
[[rtcsync]]*rtcsync*::
|
||||||
The *rtcsync* directive enables a mode where the system time is periodically
|
The *rtcsync* directive enables a mode where the system time is periodically
|
||||||
|
|
11
refclock.c
11
refclock.c
|
@ -48,6 +48,7 @@ extern RefclockDriver RCL_SHM_driver;
|
||||||
extern RefclockDriver RCL_SOCK_driver;
|
extern RefclockDriver RCL_SOCK_driver;
|
||||||
extern RefclockDriver RCL_PPS_driver;
|
extern RefclockDriver RCL_PPS_driver;
|
||||||
extern RefclockDriver RCL_PHC_driver;
|
extern RefclockDriver RCL_PHC_driver;
|
||||||
|
extern RefclockDriver RCL_RTC_driver;
|
||||||
|
|
||||||
struct FilterSample {
|
struct FilterSample {
|
||||||
double offset;
|
double offset;
|
||||||
|
@ -63,6 +64,7 @@ struct RCL_Instance_Record {
|
||||||
int driver_poll;
|
int driver_poll;
|
||||||
int driver_polled;
|
int driver_polled;
|
||||||
int poll;
|
int poll;
|
||||||
|
int inpoll;
|
||||||
int leap_status;
|
int leap_status;
|
||||||
int local;
|
int local;
|
||||||
int pps_forced;
|
int pps_forced;
|
||||||
|
@ -159,6 +161,8 @@ RCL_AddRefclock(RefclockParameters *params)
|
||||||
inst->driver = &RCL_PPS_driver;
|
inst->driver = &RCL_PPS_driver;
|
||||||
} else if (strcmp(params->driver_name, "PHC") == 0) {
|
} else if (strcmp(params->driver_name, "PHC") == 0) {
|
||||||
inst->driver = &RCL_PHC_driver;
|
inst->driver = &RCL_PHC_driver;
|
||||||
|
} else if (strcmp(params->driver_name, "RTC") == 0) {
|
||||||
|
inst->driver = &RCL_RTC_driver;
|
||||||
} else {
|
} else {
|
||||||
LOG_FATAL("unknown refclock driver %s", params->driver_name);
|
LOG_FATAL("unknown refclock driver %s", params->driver_name);
|
||||||
}
|
}
|
||||||
|
@ -174,6 +178,7 @@ RCL_AddRefclock(RefclockParameters *params)
|
||||||
inst->driver_parameter_length = 0;
|
inst->driver_parameter_length = 0;
|
||||||
inst->driver_poll = params->driver_poll;
|
inst->driver_poll = params->driver_poll;
|
||||||
inst->poll = params->poll;
|
inst->poll = params->poll;
|
||||||
|
inst->inpoll = 0;
|
||||||
inst->driver_polled = 0;
|
inst->driver_polled = 0;
|
||||||
inst->leap_status = LEAP_Normal;
|
inst->leap_status = LEAP_Normal;
|
||||||
inst->local = params->local;
|
inst->local = params->local;
|
||||||
|
@ -502,7 +507,7 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
|
||||||
log_sample(instance, &cooked_time, 0, 0, raw_offset, offset, dispersion);
|
log_sample(instance, &cooked_time, 0, 0, raw_offset, offset, dispersion);
|
||||||
|
|
||||||
/* for logging purposes */
|
/* for logging purposes */
|
||||||
if (!instance->driver->poll)
|
if (!instance->inpoll)
|
||||||
instance->driver_polled++;
|
instance->driver_polled++;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -659,7 +664,7 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
||||||
offset, dispersion);
|
offset, dispersion);
|
||||||
|
|
||||||
/* for logging purposes */
|
/* for logging purposes */
|
||||||
if (!instance->driver->poll)
|
if (!instance->inpoll)
|
||||||
instance->driver_polled++;
|
instance->driver_polled++;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -785,7 +790,9 @@ poll_timeout(void *arg)
|
||||||
|
|
||||||
if (inst->driver->poll) {
|
if (inst->driver->poll) {
|
||||||
poll = inst->driver_poll;
|
poll = inst->driver_poll;
|
||||||
|
inst->inpoll = 1;
|
||||||
inst->driver->poll(inst);
|
inst->driver->poll(inst);
|
||||||
|
inst->inpoll = 0;
|
||||||
inst->driver_polled++;
|
inst->driver_polled++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
162
refclock_rtc.c
Normal file
162
refclock_rtc.c
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) 2021 Uwe Kleine-König, Pengutronix
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
RTC refclock driver.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "conf.h"
|
||||||
|
#include "refclock.h"
|
||||||
|
#include "local.h"
|
||||||
|
|
||||||
|
#include "logging.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "sched.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "rtc_linux.h"
|
||||||
|
|
||||||
|
struct refrtc_instance {
|
||||||
|
int fd;
|
||||||
|
int polling;
|
||||||
|
int utc;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int refrtc_add_sample(RCL_Instance instance, struct timespec *now,
|
||||||
|
time_t rtc_sec, long rtc_nsec)
|
||||||
|
{
|
||||||
|
struct timespec rtc_ts;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
rtc_ts.tv_sec = rtc_sec;
|
||||||
|
rtc_ts.tv_nsec = rtc_nsec;
|
||||||
|
|
||||||
|
status = RCL_AddSample(instance, now, &rtc_ts, LEAP_Normal);
|
||||||
|
if (status == 0)
|
||||||
|
DEBUG_LOG("rtc_sec = %s, now = %s",
|
||||||
|
UTI_TimespecToString(&rtc_ts), UTI_TimespecToString(now));
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void refrtc_read_after_uie(int rtcfd, int event, void *data)
|
||||||
|
{
|
||||||
|
RCL_Instance instance = (RCL_Instance)data;
|
||||||
|
struct refrtc_instance *rtc = RCL_GetDriverData(instance);
|
||||||
|
time_t rtc_sec;
|
||||||
|
struct timespec now;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
status = RTC_Linux_CheckInterrupt(rtcfd);
|
||||||
|
if (status <= -1) {
|
||||||
|
SCH_RemoveFileHandler(rtcfd);
|
||||||
|
RTC_Linux_SwitchInterrupt(rtcfd, 0); /* Likely to raise error too, but just to be sure... */
|
||||||
|
close(rtcfd);
|
||||||
|
rtc->fd = -1;
|
||||||
|
return;
|
||||||
|
} else if (status == 0) {
|
||||||
|
/* Wait for the next interrupt, this one may be bogus */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_sec = RTC_Linux_ReadTime_AfterInterrupt(rtcfd, rtc->utc, NULL, &now);
|
||||||
|
if (rtc_sec == (time_t)-1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
refrtc_add_sample(instance, &now, rtc_sec, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int refrtc_initialise(RCL_Instance instance)
|
||||||
|
{
|
||||||
|
const char *options[] = {"utc", NULL};
|
||||||
|
int rtcfd;
|
||||||
|
const char *path;
|
||||||
|
struct refrtc_instance *rtc;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
RCL_CheckDriverOptions(instance, options);
|
||||||
|
|
||||||
|
if (CNF_GetRtcSync() || CNF_GetRtcFile())
|
||||||
|
LOG_FATAL("refclock RTC cannot be used together with rtcsync or rtcfile");
|
||||||
|
|
||||||
|
path = RCL_GetDriverParameter(instance);
|
||||||
|
|
||||||
|
rtcfd = open(path, O_RDONLY);
|
||||||
|
if (rtcfd < 0)
|
||||||
|
LOG_FATAL("Could not open RTC device %s : %m", path);
|
||||||
|
|
||||||
|
/* Close on exec */
|
||||||
|
UTI_FdSetCloexec(rtcfd);
|
||||||
|
|
||||||
|
rtc = MallocNew(struct refrtc_instance);
|
||||||
|
rtc->fd = rtcfd;
|
||||||
|
rtc->utc = RCL_GetDriverOption(instance, "utc") ? 1 : 0;
|
||||||
|
|
||||||
|
RCL_SetDriverData(instance, rtc);
|
||||||
|
|
||||||
|
/* Try to enable update interrupts */
|
||||||
|
status = RTC_Linux_SwitchInterrupt(rtcfd, 1);
|
||||||
|
if (status == 1) {
|
||||||
|
SCH_AddFileHandler(rtcfd, SCH_FILE_INPUT, refrtc_read_after_uie, instance);
|
||||||
|
rtc->polling = 0;
|
||||||
|
} else {
|
||||||
|
LOG(LOGS_INFO, "Falling back to polling for %s", path);
|
||||||
|
rtc->polling = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void refrtc_finalise(RCL_Instance instance)
|
||||||
|
{
|
||||||
|
struct refrtc_instance *rtc;
|
||||||
|
|
||||||
|
rtc = RCL_GetDriverData(instance);
|
||||||
|
|
||||||
|
if (!rtc->polling) {
|
||||||
|
SCH_RemoveFileHandler(rtc->fd);
|
||||||
|
RTC_Linux_SwitchInterrupt(rtc->fd, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(rtc->fd);
|
||||||
|
Free(rtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int refrtc_poll(RCL_Instance instance)
|
||||||
|
{
|
||||||
|
struct refrtc_instance *rtc;
|
||||||
|
struct timespec now;
|
||||||
|
time_t rtc_sec;
|
||||||
|
|
||||||
|
rtc = RCL_GetDriverData(instance);
|
||||||
|
|
||||||
|
if (!rtc->polling)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
rtc_sec = RTC_Linux_ReadTime_Now(rtc->fd, rtc->utc, NULL, &now);
|
||||||
|
if (rtc_sec == (time_t)-1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* As the rtc has a resolution of 1s, only add half a second */
|
||||||
|
return refrtc_add_sample(instance, &now, rtc_sec, 500000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
RefclockDriver RCL_RTC_driver = {
|
||||||
|
refrtc_initialise,
|
||||||
|
refrtc_finalise,
|
||||||
|
refrtc_poll
|
||||||
|
};
|
299
rtc_linux.c
299
rtc_linux.c
|
@ -296,14 +296,25 @@ slew_samples
|
||||||
corresponding real time clock 'DMY HMS' form, taking account of
|
corresponding real time clock 'DMY HMS' form, taking account of
|
||||||
whether the user runs his RTC on the local time zone or UTC */
|
whether the user runs his RTC on the local time zone or UTC */
|
||||||
|
|
||||||
static struct tm *
|
static void
|
||||||
rtc_from_t(const time_t *t)
|
rtc_from_t(const time_t *t, struct rtc_time *rtc_raw, int utc)
|
||||||
{
|
{
|
||||||
if (rtc_on_utc) {
|
struct tm *rtc_tm;
|
||||||
return gmtime(t);
|
if (utc) {
|
||||||
|
rtc_tm = gmtime(t);
|
||||||
} else {
|
} else {
|
||||||
return localtime(t);
|
rtc_tm = localtime(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rtc_raw->tm_sec = rtc_tm->tm_sec;
|
||||||
|
rtc_raw->tm_min = rtc_tm->tm_min;
|
||||||
|
rtc_raw->tm_hour = rtc_tm->tm_hour;
|
||||||
|
rtc_raw->tm_mday = rtc_tm->tm_mday;
|
||||||
|
rtc_raw->tm_mon = rtc_tm->tm_mon;
|
||||||
|
rtc_raw->tm_year = rtc_tm->tm_year;
|
||||||
|
rtc_raw->tm_wday = rtc_tm->tm_wday;
|
||||||
|
rtc_raw->tm_yday = rtc_tm->tm_yday;
|
||||||
|
rtc_raw->tm_isdst = rtc_tm->tm_isdst;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
@ -341,17 +352,27 @@ rtc_from_t(const time_t *t)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static time_t
|
static time_t
|
||||||
t_from_rtc(struct tm *stm) {
|
t_from_rtc(struct rtc_time *rtc_raw, int utc)
|
||||||
struct tm temp1, temp2, *tm;
|
{
|
||||||
|
struct tm rtc_tm, temp1, temp2, *tm;
|
||||||
long diff;
|
long diff;
|
||||||
time_t t1, t2;
|
time_t t1, t2;
|
||||||
|
|
||||||
temp1 = *stm;
|
/* Convert to seconds since 1970 */
|
||||||
|
rtc_tm.tm_sec = rtc_raw->tm_sec;
|
||||||
|
rtc_tm.tm_min = rtc_raw->tm_min;
|
||||||
|
rtc_tm.tm_hour = rtc_raw->tm_hour;
|
||||||
|
rtc_tm.tm_mday = rtc_raw->tm_mday;
|
||||||
|
rtc_tm.tm_mon = rtc_raw->tm_mon;
|
||||||
|
rtc_tm.tm_year = rtc_raw->tm_year;
|
||||||
|
rtc_tm.tm_wday = 0;
|
||||||
|
|
||||||
|
temp1 = rtc_tm;
|
||||||
temp1.tm_isdst = 0;
|
temp1.tm_isdst = 0;
|
||||||
|
|
||||||
t1 = mktime(&temp1);
|
t1 = mktime(&temp1);
|
||||||
|
|
||||||
tm = rtc_on_utc ? gmtime(&t1) : localtime(&t1);
|
tm = utc ? gmtime(&t1) : localtime(&t1);
|
||||||
if (!tm) {
|
if (!tm) {
|
||||||
DEBUG_LOG("gmtime()/localtime() failed");
|
DEBUG_LOG("gmtime()/localtime() failed");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -476,8 +497,8 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static int
|
int
|
||||||
switch_interrupts(int on_off)
|
RTC_Linux_SwitchInterrupt(int fd, int on_off)
|
||||||
{
|
{
|
||||||
if (ioctl(fd, on_off ? RTC_UIE_ON : RTC_UIE_OFF, 0) < 0) {
|
if (ioctl(fd, on_off ? RTC_UIE_ON : RTC_UIE_OFF, 0) < 0) {
|
||||||
LOG(LOGS_ERR, "Could not %s RTC interrupt : %s",
|
LOG(LOGS_ERR, "Could not %s RTC interrupt : %s",
|
||||||
|
@ -508,7 +529,7 @@ RTC_Linux_Initialise(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure the RTC supports interrupts */
|
/* Make sure the RTC supports interrupts */
|
||||||
if (!switch_interrupts(1) || !switch_interrupts(0)) {
|
if (!RTC_Linux_SwitchInterrupt(fd, 1) || !RTC_Linux_SwitchInterrupt(fd, 0)) {
|
||||||
close(fd);
|
close(fd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -557,7 +578,7 @@ RTC_Linux_Finalise(void)
|
||||||
/* Remove input file handler */
|
/* Remove input file handler */
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
SCH_RemoveFileHandler(fd);
|
SCH_RemoveFileHandler(fd);
|
||||||
switch_interrupts(0);
|
RTC_Linux_SwitchInterrupt(fd, 0);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
/* Save the RTC data */
|
/* Save the RTC data */
|
||||||
|
@ -578,7 +599,7 @@ static void
|
||||||
measurement_timeout(void *any)
|
measurement_timeout(void *any)
|
||||||
{
|
{
|
||||||
timeout_id = 0;
|
timeout_id = 0;
|
||||||
switch_interrupts(1);
|
RTC_Linux_SwitchInterrupt(fd, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
@ -586,21 +607,10 @@ measurement_timeout(void *any)
|
||||||
static void
|
static void
|
||||||
set_rtc(time_t new_rtc_time)
|
set_rtc(time_t new_rtc_time)
|
||||||
{
|
{
|
||||||
struct tm rtc_tm;
|
|
||||||
struct rtc_time rtc_raw;
|
struct rtc_time rtc_raw;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
rtc_tm = *rtc_from_t(&new_rtc_time);
|
rtc_from_t(&new_rtc_time, &rtc_raw, rtc_on_utc);
|
||||||
|
|
||||||
rtc_raw.tm_sec = rtc_tm.tm_sec;
|
|
||||||
rtc_raw.tm_min = rtc_tm.tm_min;
|
|
||||||
rtc_raw.tm_hour = rtc_tm.tm_hour;
|
|
||||||
rtc_raw.tm_mday = rtc_tm.tm_mday;
|
|
||||||
rtc_raw.tm_mon = rtc_tm.tm_mon;
|
|
||||||
rtc_raw.tm_year = rtc_tm.tm_year;
|
|
||||||
rtc_raw.tm_wday = rtc_tm.tm_wday;
|
|
||||||
rtc_raw.tm_yday = rtc_tm.tm_yday;
|
|
||||||
rtc_raw.tm_isdst = rtc_tm.tm_isdst;
|
|
||||||
|
|
||||||
status = ioctl(fd, RTC_SET_TIME, &rtc_raw);
|
status = ioctl(fd, RTC_SET_TIME, &rtc_raw);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
|
@ -750,16 +760,11 @@ process_reading(time_t rtc_time, struct timespec *system_time)
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
int
|
||||||
read_from_device(int fd_, int event, void *any)
|
RTC_Linux_CheckInterrupt(int fd)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
unsigned long data;
|
unsigned long data;
|
||||||
struct timespec sys_time;
|
|
||||||
struct rtc_time rtc_raw;
|
|
||||||
struct tm rtc_tm;
|
|
||||||
time_t rtc_t;
|
|
||||||
int error = 0;
|
|
||||||
|
|
||||||
status = read(fd, &data, sizeof(data));
|
status = read(fd, &data, sizeof(data));
|
||||||
|
|
||||||
|
@ -767,64 +772,80 @@ read_from_device(int fd_, int event, void *any)
|
||||||
/* This looks like a bad error : the file descriptor was indicating it was
|
/* This looks like a bad error : the file descriptor was indicating it was
|
||||||
* ready to read but we couldn't read anything. Give up. */
|
* ready to read but we couldn't read anything. Give up. */
|
||||||
LOG(LOGS_ERR, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
|
LOG(LOGS_ERR, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
|
||||||
SCH_RemoveFileHandler(fd);
|
return -1;
|
||||||
switch_interrupts(0); /* Likely to raise error too, but just to be sure... */
|
}
|
||||||
close(fd);
|
|
||||||
fd = -1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skip_interrupts > 0) {
|
if (skip_interrupts > 0) {
|
||||||
/* Wait for the next interrupt, this one may be bogus */
|
/* Wait for the next interrupt, this one may be bogus */
|
||||||
skip_interrupts--;
|
skip_interrupts--;
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((data & RTC_UF) == RTC_UF) {
|
/* Update interrupt detected? */
|
||||||
/* Update interrupt detected */
|
return (data & RTC_UF) == RTC_UF;
|
||||||
|
}
|
||||||
/* Read RTC time, sandwiched between two polls of the system clock
|
|
||||||
so we can bound any error. */
|
|
||||||
|
|
||||||
SCH_GetLastEventTime(&sys_time, NULL, NULL);
|
time_t
|
||||||
|
RTC_Linux_ReadTime_AfterInterrupt(int fd, int utc,
|
||||||
|
struct timespec *sys_time_cooked,
|
||||||
|
struct timespec *sys_time_raw)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
struct rtc_time rtc_raw;
|
||||||
|
|
||||||
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
|
/* Read RTC time, sandwiched between two polls of the system clock
|
||||||
if (status < 0) {
|
so we can bound any error. */
|
||||||
LOG(LOGS_ERR, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno));
|
|
||||||
error = 1;
|
|
||||||
goto turn_off_interrupt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert RTC time into a struct timespec */
|
SCH_GetLastEventTime(sys_time_cooked, NULL, sys_time_raw);
|
||||||
rtc_tm.tm_sec = rtc_raw.tm_sec;
|
|
||||||
rtc_tm.tm_min = rtc_raw.tm_min;
|
|
||||||
rtc_tm.tm_hour = rtc_raw.tm_hour;
|
|
||||||
rtc_tm.tm_mday = rtc_raw.tm_mday;
|
|
||||||
rtc_tm.tm_mon = rtc_raw.tm_mon;
|
|
||||||
rtc_tm.tm_year = rtc_raw.tm_year;
|
|
||||||
rtc_tm.tm_wday = 0;
|
|
||||||
|
|
||||||
rtc_t = t_from_rtc(&rtc_tm);
|
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
|
||||||
|
if (status < 0) {
|
||||||
|
LOG(LOGS_ERR, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (rtc_t == (time_t)(-1)) {
|
/* Convert RTC time into a struct timespec */
|
||||||
error = 1;
|
return t_from_rtc(&rtc_raw, utc);
|
||||||
goto turn_off_interrupt;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
process_reading(rtc_t, &sys_time);
|
static void
|
||||||
|
read_from_device(int fd_, int event, void *any)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
struct timespec sys_time;
|
||||||
|
time_t rtc_t;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
if (n_samples < 4) {
|
status = RTC_Linux_CheckInterrupt(fd);
|
||||||
measurement_period = LOWEST_MEASUREMENT_PERIOD;
|
if (status == -1) {
|
||||||
} else if (n_samples < 6) {
|
SCH_RemoveFileHandler(fd);
|
||||||
measurement_period = LOWEST_MEASUREMENT_PERIOD << 1;
|
RTC_Linux_SwitchInterrupt(fd, 0); /* Likely to raise error too, but just to be sure... */
|
||||||
} else if (n_samples < 10) {
|
close(fd);
|
||||||
measurement_period = LOWEST_MEASUREMENT_PERIOD << 2;
|
fd = -1;
|
||||||
} else if (n_samples < 14) {
|
return;
|
||||||
measurement_period = LOWEST_MEASUREMENT_PERIOD << 3;
|
} else if (status == 0) {
|
||||||
} else {
|
/* Wait for the next interrupt, this one may be bogus */
|
||||||
measurement_period = LOWEST_MEASUREMENT_PERIOD << 4;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rtc_t = RTC_Linux_ReadTime_AfterInterrupt(fd, rtc_on_utc, &sys_time, NULL);
|
||||||
|
if (rtc_t == (time_t)-1) {
|
||||||
|
error = 1;
|
||||||
|
goto turn_off_interrupt;
|
||||||
|
}
|
||||||
|
|
||||||
|
process_reading(rtc_t, &sys_time);
|
||||||
|
|
||||||
|
if (n_samples < 4) {
|
||||||
|
measurement_period = LOWEST_MEASUREMENT_PERIOD;
|
||||||
|
} else if (n_samples < 6) {
|
||||||
|
measurement_period = LOWEST_MEASUREMENT_PERIOD << 1;
|
||||||
|
} else if (n_samples < 10) {
|
||||||
|
measurement_period = LOWEST_MEASUREMENT_PERIOD << 2;
|
||||||
|
} else if (n_samples < 14) {
|
||||||
|
measurement_period = LOWEST_MEASUREMENT_PERIOD << 3;
|
||||||
|
} else {
|
||||||
|
measurement_period = LOWEST_MEASUREMENT_PERIOD << 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
turn_off_interrupt:
|
turn_off_interrupt:
|
||||||
|
@ -836,7 +857,7 @@ turn_off_interrupt:
|
||||||
operating_mode = OM_NORMAL;
|
operating_mode = OM_NORMAL;
|
||||||
(after_init_hook)(after_init_hook_arg);
|
(after_init_hook)(after_init_hook_arg);
|
||||||
|
|
||||||
switch_interrupts(0);
|
RTC_Linux_SwitchInterrupt(fd, 0);
|
||||||
|
|
||||||
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
|
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
|
||||||
}
|
}
|
||||||
|
@ -848,7 +869,7 @@ turn_off_interrupt:
|
||||||
DEBUG_LOG("Could not complete after trim relock due to errors");
|
DEBUG_LOG("Could not complete after trim relock due to errors");
|
||||||
operating_mode = OM_NORMAL;
|
operating_mode = OM_NORMAL;
|
||||||
|
|
||||||
switch_interrupts(0);
|
RTC_Linux_SwitchInterrupt(fd, 0);
|
||||||
|
|
||||||
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
|
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
|
||||||
}
|
}
|
||||||
|
@ -856,7 +877,7 @@ turn_off_interrupt:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OM_NORMAL:
|
case OM_NORMAL:
|
||||||
switch_interrupts(0);
|
RTC_Linux_SwitchInterrupt(fd, 0);
|
||||||
|
|
||||||
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
|
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
|
||||||
|
|
||||||
|
@ -878,7 +899,7 @@ RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything)
|
||||||
|
|
||||||
operating_mode = OM_INITIAL;
|
operating_mode = OM_INITIAL;
|
||||||
timeout_id = 0;
|
timeout_id = 0;
|
||||||
switch_interrupts(1);
|
RTC_Linux_SwitchInterrupt(fd, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
@ -911,6 +932,33 @@ RTC_Linux_WriteParameters(void)
|
||||||
return(retval);
|
return(retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
time_t
|
||||||
|
RTC_Linux_ReadTime_Now(int fd, int utc,
|
||||||
|
struct timespec *old_sys_cooked,
|
||||||
|
struct timespec *old_sys_raw)
|
||||||
|
{
|
||||||
|
struct rtc_time rtc_raw, rtc_raw_retry;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
/* Retry reading the rtc until both read attempts give the same sec value.
|
||||||
|
This way the race condition is prevented that the RTC has updated itself
|
||||||
|
during the first read operation. */
|
||||||
|
do {
|
||||||
|
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
|
||||||
|
if (status >= 0) {
|
||||||
|
status = ioctl(fd, RTC_RD_TIME, &rtc_raw_retry);
|
||||||
|
}
|
||||||
|
} while (status >= 0 && rtc_raw.tm_sec != rtc_raw_retry.tm_sec);
|
||||||
|
|
||||||
|
/* Read system clock */
|
||||||
|
if (old_sys_raw)
|
||||||
|
LCL_ReadRawTime(old_sys_raw);
|
||||||
|
if (old_sys_cooked)
|
||||||
|
LCL_ReadCookedTime(old_sys_cooked, NULL);
|
||||||
|
|
||||||
|
return status >= 0 ? t_from_rtc(&rtc_raw, utc) : -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
/* Try to set the system clock from the RTC, in the same manner as
|
/* Try to set the system clock from the RTC, in the same manner as
|
||||||
/sbin/hwclock -s would do. We're not as picky about OS version
|
/sbin/hwclock -s would do. We're not as picky about OS version
|
||||||
|
@ -920,9 +968,7 @@ RTC_Linux_WriteParameters(void)
|
||||||
int
|
int
|
||||||
RTC_Linux_TimePreInit(time_t driftfile_time)
|
RTC_Linux_TimePreInit(time_t driftfile_time)
|
||||||
{
|
{
|
||||||
int fd, status;
|
int fd;
|
||||||
struct rtc_time rtc_raw, rtc_raw_retry;
|
|
||||||
struct tm rtc_tm;
|
|
||||||
time_t rtc_t;
|
time_t rtc_t;
|
||||||
double accumulated_error, sys_offset;
|
double accumulated_error, sys_offset;
|
||||||
struct timespec new_sys_time, old_sys_time;
|
struct timespec new_sys_time, old_sys_time;
|
||||||
|
@ -938,66 +984,41 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
|
||||||
return 0; /* Can't open it, and won't be able to later */
|
return 0; /* Can't open it, and won't be able to later */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Retry reading the rtc until both read attempts give the same sec value.
|
rtc_t = RTC_Linux_ReadTime_Now(fd, rtc_on_utc, &old_sys_time, NULL);
|
||||||
This way the race condition is prevented that the RTC has updated itself
|
|
||||||
during the first read operation. */
|
|
||||||
do {
|
|
||||||
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
|
|
||||||
if (status >= 0) {
|
|
||||||
status = ioctl(fd, RTC_RD_TIME, &rtc_raw_retry);
|
|
||||||
}
|
|
||||||
} while (status >= 0 && rtc_raw.tm_sec != rtc_raw_retry.tm_sec);
|
|
||||||
|
|
||||||
/* Read system clock */
|
|
||||||
LCL_ReadCookedTime(&old_sys_time, NULL);
|
|
||||||
|
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
if (status >= 0) {
|
if (rtc_t != (time_t)(-1)) {
|
||||||
/* Convert to seconds since 1970 */
|
|
||||||
rtc_tm.tm_sec = rtc_raw.tm_sec;
|
|
||||||
rtc_tm.tm_min = rtc_raw.tm_min;
|
|
||||||
rtc_tm.tm_hour = rtc_raw.tm_hour;
|
|
||||||
rtc_tm.tm_mday = rtc_raw.tm_mday;
|
|
||||||
rtc_tm.tm_mon = rtc_raw.tm_mon;
|
|
||||||
rtc_tm.tm_year = rtc_raw.tm_year;
|
|
||||||
|
|
||||||
rtc_t = t_from_rtc(&rtc_tm);
|
|
||||||
|
|
||||||
if (rtc_t != (time_t)(-1)) {
|
/* Work out approximatation to correct time (to about the
|
||||||
|
nearest second) */
|
||||||
/* Work out approximatation to correct time (to about the
|
if (valid_coefs_from_file) {
|
||||||
nearest second) */
|
accumulated_error = file_ref_offset +
|
||||||
if (valid_coefs_from_file) {
|
(rtc_t - file_ref_time) * 1.0e-6 * file_rate_ppm;
|
||||||
accumulated_error = file_ref_offset +
|
|
||||||
(rtc_t - file_ref_time) * 1.0e-6 * file_rate_ppm;
|
|
||||||
} else {
|
|
||||||
accumulated_error = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Correct time */
|
|
||||||
|
|
||||||
new_sys_time.tv_sec = rtc_t;
|
|
||||||
/* Average error in the RTC reading */
|
|
||||||
new_sys_time.tv_nsec = 500000000;
|
|
||||||
|
|
||||||
UTI_AddDoubleToTimespec(&new_sys_time, -accumulated_error, &new_sys_time);
|
|
||||||
|
|
||||||
if (new_sys_time.tv_sec < driftfile_time) {
|
|
||||||
LOG(LOGS_WARN, "RTC time before last driftfile modification (ignored)");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
sys_offset = UTI_DiffTimespecsToDouble(&old_sys_time, &new_sys_time);
|
|
||||||
|
|
||||||
/* Set system time only if the step is larger than 1 second */
|
|
||||||
if (fabs(sys_offset) >= 1.0) {
|
|
||||||
if (LCL_ApplyStepOffset(sys_offset))
|
|
||||||
LOG(LOGS_INFO, "System time set from RTC");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
accumulated_error = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Correct time */
|
||||||
|
|
||||||
|
new_sys_time.tv_sec = rtc_t;
|
||||||
|
/* Average error in the RTC reading */
|
||||||
|
new_sys_time.tv_nsec = 500000000;
|
||||||
|
|
||||||
|
UTI_AddDoubleToTimespec(&new_sys_time, -accumulated_error, &new_sys_time);
|
||||||
|
|
||||||
|
if (new_sys_time.tv_sec < driftfile_time) {
|
||||||
|
LOG(LOGS_WARN, "RTC time before last driftfile modification (ignored)");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sys_offset = UTI_DiffTimespecsToDouble(&old_sys_time, &new_sys_time);
|
||||||
|
|
||||||
|
/* Set system time only if the step is larger than 1 second */
|
||||||
|
if (fabs(sys_offset) >= 1.0) {
|
||||||
|
if (LCL_ApplyStepOffset(sys_offset))
|
||||||
|
LOG(LOGS_INFO, "System time set from RTC");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1065,7 +1086,7 @@ RTC_Linux_Trim(void)
|
||||||
/* And start rapid sampling, interrupts on now */
|
/* And start rapid sampling, interrupts on now */
|
||||||
SCH_RemoveTimeout(timeout_id);
|
SCH_RemoveTimeout(timeout_id);
|
||||||
timeout_id = 0;
|
timeout_id = 0;
|
||||||
switch_interrupts(1);
|
RTC_Linux_SwitchInterrupt(fd, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -42,4 +42,13 @@ extern int RTC_Linux_Trim(void);
|
||||||
|
|
||||||
extern void RTC_Linux_CycleLogFile(void);
|
extern void RTC_Linux_CycleLogFile(void);
|
||||||
|
|
||||||
|
extern int RTC_Linux_SwitchInterrupt(int fd, int on_off);
|
||||||
|
extern int RTC_Linux_CheckInterrupt(int fd);
|
||||||
|
extern time_t RTC_Linux_ReadTime_AfterInterrupt(int fd, int utc,
|
||||||
|
struct timespec *sys_time_cooked,
|
||||||
|
struct timespec *sys_time_raw);
|
||||||
|
extern time_t RTC_Linux_ReadTime_Now(int fd, int utc,
|
||||||
|
struct timespec *sys_time_cooked,
|
||||||
|
struct timespec *sys_time_raw);
|
||||||
|
|
||||||
#endif /* _GOT_RTC_LINUX_H */
|
#endif /* _GOT_RTC_LINUX_H */
|
||||||
|
|
Loading…
Reference in a new issue