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
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS rtc_linux.o"
|
||||
add_def FEAT_RTC
|
||||
if [ $feat_refclock = "1" ]; then
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS refclock_rtc.o"
|
||||
fi
|
||||
fi
|
||||
|
||||
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.
|
||||
+
|
||||
There are four drivers included in *chronyd*:
|
||||
There are five drivers included in *chronyd*:
|
||||
+
|
||||
*PPS*:::
|
||||
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
|
||||
----
|
||||
+
|
||||
*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}::
|
||||
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.
|
||||
+
|
||||
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*::
|
||||
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_PPS_driver;
|
||||
extern RefclockDriver RCL_PHC_driver;
|
||||
extern RefclockDriver RCL_RTC_driver;
|
||||
|
||||
struct FilterSample {
|
||||
double offset;
|
||||
|
@ -63,6 +64,7 @@ struct RCL_Instance_Record {
|
|||
int driver_poll;
|
||||
int driver_polled;
|
||||
int poll;
|
||||
int inpoll;
|
||||
int leap_status;
|
||||
int local;
|
||||
int pps_forced;
|
||||
|
@ -159,6 +161,8 @@ RCL_AddRefclock(RefclockParameters *params)
|
|||
inst->driver = &RCL_PPS_driver;
|
||||
} else if (strcmp(params->driver_name, "PHC") == 0) {
|
||||
inst->driver = &RCL_PHC_driver;
|
||||
} else if (strcmp(params->driver_name, "RTC") == 0) {
|
||||
inst->driver = &RCL_RTC_driver;
|
||||
} else {
|
||||
LOG_FATAL("unknown refclock driver %s", params->driver_name);
|
||||
}
|
||||
|
@ -174,6 +178,7 @@ RCL_AddRefclock(RefclockParameters *params)
|
|||
inst->driver_parameter_length = 0;
|
||||
inst->driver_poll = params->driver_poll;
|
||||
inst->poll = params->poll;
|
||||
inst->inpoll = 0;
|
||||
inst->driver_polled = 0;
|
||||
inst->leap_status = LEAP_Normal;
|
||||
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);
|
||||
|
||||
/* for logging purposes */
|
||||
if (!instance->driver->poll)
|
||||
if (!instance->inpoll)
|
||||
instance->driver_polled++;
|
||||
|
||||
return 1;
|
||||
|
@ -659,7 +664,7 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
|||
offset, dispersion);
|
||||
|
||||
/* for logging purposes */
|
||||
if (!instance->driver->poll)
|
||||
if (!instance->inpoll)
|
||||
instance->driver_polled++;
|
||||
|
||||
return 1;
|
||||
|
@ -785,7 +790,9 @@ poll_timeout(void *arg)
|
|||
|
||||
if (inst->driver->poll) {
|
||||
poll = inst->driver_poll;
|
||||
inst->inpoll = 1;
|
||||
inst->driver->poll(inst);
|
||||
inst->inpoll = 0;
|
||||
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
|
||||
};
|
199
rtc_linux.c
199
rtc_linux.c
|
@ -296,14 +296,25 @@ slew_samples
|
|||
corresponding real time clock 'DMY HMS' form, taking account of
|
||||
whether the user runs his RTC on the local time zone or UTC */
|
||||
|
||||
static struct tm *
|
||||
rtc_from_t(const time_t *t)
|
||||
static void
|
||||
rtc_from_t(const time_t *t, struct rtc_time *rtc_raw, int utc)
|
||||
{
|
||||
if (rtc_on_utc) {
|
||||
return gmtime(t);
|
||||
struct tm *rtc_tm;
|
||||
if (utc) {
|
||||
rtc_tm = gmtime(t);
|
||||
} 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
|
||||
t_from_rtc(struct tm *stm) {
|
||||
struct tm temp1, temp2, *tm;
|
||||
t_from_rtc(struct rtc_time *rtc_raw, int utc)
|
||||
{
|
||||
struct tm rtc_tm, temp1, temp2, *tm;
|
||||
long diff;
|
||||
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;
|
||||
|
||||
t1 = mktime(&temp1);
|
||||
|
||||
tm = rtc_on_utc ? gmtime(&t1) : localtime(&t1);
|
||||
tm = utc ? gmtime(&t1) : localtime(&t1);
|
||||
if (!tm) {
|
||||
DEBUG_LOG("gmtime()/localtime() failed");
|
||||
return -1;
|
||||
|
@ -476,8 +497,8 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
|
|||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
switch_interrupts(int on_off)
|
||||
int
|
||||
RTC_Linux_SwitchInterrupt(int fd, int on_off)
|
||||
{
|
||||
if (ioctl(fd, on_off ? RTC_UIE_ON : RTC_UIE_OFF, 0) < 0) {
|
||||
LOG(LOGS_ERR, "Could not %s RTC interrupt : %s",
|
||||
|
@ -508,7 +529,7 @@ RTC_Linux_Initialise(void)
|
|||
}
|
||||
|
||||
/* 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);
|
||||
return 0;
|
||||
}
|
||||
|
@ -557,7 +578,7 @@ RTC_Linux_Finalise(void)
|
|||
/* Remove input file handler */
|
||||
if (fd >= 0) {
|
||||
SCH_RemoveFileHandler(fd);
|
||||
switch_interrupts(0);
|
||||
RTC_Linux_SwitchInterrupt(fd, 0);
|
||||
close(fd);
|
||||
|
||||
/* Save the RTC data */
|
||||
|
@ -578,7 +599,7 @@ static void
|
|||
measurement_timeout(void *any)
|
||||
{
|
||||
timeout_id = 0;
|
||||
switch_interrupts(1);
|
||||
RTC_Linux_SwitchInterrupt(fd, 1);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
@ -586,21 +607,10 @@ measurement_timeout(void *any)
|
|||
static void
|
||||
set_rtc(time_t new_rtc_time)
|
||||
{
|
||||
struct tm rtc_tm;
|
||||
struct rtc_time rtc_raw;
|
||||
int status;
|
||||
|
||||
rtc_tm = *rtc_from_t(&new_rtc_time);
|
||||
|
||||
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;
|
||||
rtc_from_t(&new_rtc_time, &rtc_raw, rtc_on_utc);
|
||||
|
||||
status = ioctl(fd, RTC_SET_TIME, &rtc_raw);
|
||||
if (status < 0) {
|
||||
|
@ -750,16 +760,11 @@ process_reading(time_t rtc_time, struct timespec *system_time)
|
|||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
read_from_device(int fd_, int event, void *any)
|
||||
int
|
||||
RTC_Linux_CheckInterrupt(int fd)
|
||||
{
|
||||
int status;
|
||||
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));
|
||||
|
||||
|
@ -767,46 +772,64 @@ read_from_device(int fd_, int event, void *any)
|
|||
/* This looks like a bad error : the file descriptor was indicating it was
|
||||
* ready to read but we couldn't read anything. Give up. */
|
||||
LOG(LOGS_ERR, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
|
||||
SCH_RemoveFileHandler(fd);
|
||||
switch_interrupts(0); /* Likely to raise error too, but just to be sure... */
|
||||
close(fd);
|
||||
fd = -1;
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (skip_interrupts > 0) {
|
||||
/* Wait for the next interrupt, this one may be bogus */
|
||||
skip_interrupts--;
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((data & RTC_UF) == RTC_UF) {
|
||||
/* Update interrupt detected */
|
||||
/* Update interrupt detected? */
|
||||
return (data & RTC_UF) == RTC_UF;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* Read RTC time, sandwiched between two polls of the system clock
|
||||
so we can bound any error. */
|
||||
|
||||
SCH_GetLastEventTime(&sys_time, NULL, NULL);
|
||||
SCH_GetLastEventTime(sys_time_cooked, NULL, sys_time_raw);
|
||||
|
||||
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));
|
||||
error = 1;
|
||||
goto turn_off_interrupt;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Convert RTC time into a struct timespec */
|
||||
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;
|
||||
return t_from_rtc(&rtc_raw, utc);
|
||||
}
|
||||
|
||||
rtc_t = t_from_rtc(&rtc_tm);
|
||||
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 (rtc_t == (time_t)(-1)) {
|
||||
status = RTC_Linux_CheckInterrupt(fd);
|
||||
if (status == -1) {
|
||||
SCH_RemoveFileHandler(fd);
|
||||
RTC_Linux_SwitchInterrupt(fd, 0); /* Likely to raise error too, but just to be sure... */
|
||||
close(fd);
|
||||
fd = -1;
|
||||
return;
|
||||
} else if (status == 0) {
|
||||
/* Wait for the next interrupt, this one may be bogus */
|
||||
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;
|
||||
}
|
||||
|
@ -825,8 +848,6 @@ read_from_device(int fd_, int event, void *any)
|
|||
measurement_period = LOWEST_MEASUREMENT_PERIOD << 4;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
turn_off_interrupt:
|
||||
|
||||
switch (operating_mode) {
|
||||
|
@ -836,7 +857,7 @@ turn_off_interrupt:
|
|||
operating_mode = OM_NORMAL;
|
||||
(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);
|
||||
}
|
||||
|
@ -848,7 +869,7 @@ turn_off_interrupt:
|
|||
DEBUG_LOG("Could not complete after trim relock due to errors");
|
||||
operating_mode = OM_NORMAL;
|
||||
|
||||
switch_interrupts(0);
|
||||
RTC_Linux_SwitchInterrupt(fd, 0);
|
||||
|
||||
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
|
||||
}
|
||||
|
@ -856,7 +877,7 @@ turn_off_interrupt:
|
|||
break;
|
||||
|
||||
case OM_NORMAL:
|
||||
switch_interrupts(0);
|
||||
RTC_Linux_SwitchInterrupt(fd, 0);
|
||||
|
||||
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;
|
||||
timeout_id = 0;
|
||||
switch_interrupts(1);
|
||||
RTC_Linux_SwitchInterrupt(fd, 1);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
@ -911,6 +932,33 @@ RTC_Linux_WriteParameters(void)
|
|||
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
|
||||
/sbin/hwclock -s would do. We're not as picky about OS version
|
||||
|
@ -920,9 +968,7 @@ RTC_Linux_WriteParameters(void)
|
|||
int
|
||||
RTC_Linux_TimePreInit(time_t driftfile_time)
|
||||
{
|
||||
int fd, status;
|
||||
struct rtc_time rtc_raw, rtc_raw_retry;
|
||||
struct tm rtc_tm;
|
||||
int fd;
|
||||
time_t rtc_t;
|
||||
double accumulated_error, sys_offset;
|
||||
struct timespec new_sys_time, old_sys_time;
|
||||
|
@ -938,32 +984,10 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
|
|||
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.
|
||||
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);
|
||||
rtc_t = RTC_Linux_ReadTime_Now(fd, rtc_on_utc, &old_sys_time, NULL);
|
||||
|
||||
close(fd);
|
||||
|
||||
if (status >= 0) {
|
||||
/* 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
|
||||
|
@ -998,9 +1022,6 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
|
|||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -1065,7 +1086,7 @@ RTC_Linux_Trim(void)
|
|||
/* And start rapid sampling, interrupts on now */
|
||||
SCH_RemoveTimeout(timeout_id);
|
||||
timeout_id = 0;
|
||||
switch_interrupts(1);
|
||||
RTC_Linux_SwitchInterrupt(fd, 1);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
|
|
@ -42,4 +42,13 @@ extern int RTC_Linux_Trim(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 */
|
||||
|
|
Loading…
Reference in a new issue