#endif
#endif
-#include "sys_generic.h"
#include "sys_linux.h"
+#include "sys_timex.h"
#include "conf.h"
#include "logging.h"
-#include "wrap_adjtimex.h"
-/* The threshold for adjtimex maxerror when the kernel sets the UNSYNC flag */
-#define UNSYNC_MAXERROR 16.0
+/* Frequency scale to convert from ppm to the timex freq */
+#define FREQ_SCALE (double)(1 << 16)
+
+/* Definitions used if missed in the system headers */
+#ifndef ADJ_SETOFFSET
+#define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */
+#endif
+#ifndef ADJ_NANO
+#define ADJ_NANO 0x2000 /* select nanosecond resolution */
+#endif
/* This is the uncompensated system tick value */
static int nominal_tick;
static int
apply_step_offset(double offset)
{
- if (TMX_ApplyStepOffset(-offset) < 0) {
- DEBUG_LOG(LOGF_SysLinux, "adjtimex() failed");
- return 0;
+ struct timex txc;
+
+ txc.modes = ADJ_SETOFFSET | ADJ_NANO;
+ txc.time.tv_sec = -offset;
+ txc.time.tv_usec = 1.0e9 * (-offset - txc.time.tv_sec);
+ if (txc.time.tv_usec < 0) {
+ txc.time.tv_sec--;
+ txc.time.tv_usec += 1000000000;
}
+ if (SYS_Timex_Adjust(&txc, 1) < 0)
+ return 0;
+
return 1;
}
static double
set_frequency(double freq_ppm)
{
+ struct timex txc;
long required_tick;
double required_freq;
int required_delta_tick;
required_freq = -(freq_ppm - dhz * required_delta_tick);
required_tick = nominal_tick - required_delta_tick;
- if (TMX_SetFrequency(&required_freq, required_tick) < 0) {
- LOG_FATAL(LOGF_SysLinux, "adjtimex failed for set_frequency, freq_ppm=%10.4e required_freq=%10.4e required_tick=%ld",
- freq_ppm, required_freq, required_tick);
- }
+ txc.modes = ADJ_TICK | ADJ_FREQUENCY;
+ txc.freq = required_freq * FREQ_SCALE;
+ txc.tick = required_tick;
+
+ SYS_Timex_Adjust(&txc, 0);
current_delta_tick = required_delta_tick;
- return dhz * current_delta_tick - required_freq;
+ return dhz * current_delta_tick - txc.freq / FREQ_SCALE;
}
/* ================================================== */
static double
read_frequency(void)
{
- long tick;
- double freq;
-
- if (TMX_GetFrequency(&freq, &tick) < 0) {
- LOG_FATAL(LOGF_SysLinux, "adjtimex() failed");
- }
-
- current_delta_tick = nominal_tick - tick;
-
- return dhz * current_delta_tick - freq;
-}
-
-/* ================================================== */
-
-static void
-set_leap(int leap)
-{
- int applied;
-
- applied = 0;
- if (!leap && TMX_GetLeapApplied(&applied) < 0) {
- LOG_FATAL(LOGF_SysLinux, "adjtimex() failed in set_leap");
- }
-
- if (TMX_SetLeap(leap) < 0) {
- LOG_FATAL(LOGF_SysLinux, "adjtimex() failed in set_leap");
- }
+ struct timex txc;
- LOG(LOGS_INFO, LOGF_SysLinux, "System clock status %s leap second",
- leap ? (leap > 0 ? "set to insert" : "set to delete") :
- (applied ? "reset after" : "set to not insert/delete"));
-}
+ txc.modes = 0;
-/* ================================================== */
-
-static void
-set_sync_status(int synchronised, double est_error, double max_error)
-{
- if (synchronised) {
- if (est_error > UNSYNC_MAXERROR)
- est_error = UNSYNC_MAXERROR;
- if (max_error >= UNSYNC_MAXERROR) {
- max_error = UNSYNC_MAXERROR;
- synchronised = 0;
- }
- } else {
- est_error = max_error = UNSYNC_MAXERROR;
- }
+ SYS_Timex_Adjust(&txc, 0);
- /* Clear the UNSYNC flag only if rtcsync is enabled */
- if (!CNF_GetRtcSync())
- synchronised = 0;
+ current_delta_tick = nominal_tick - txc.tick;
- TMX_SetSync(synchronised, est_error, max_error);
+ return dhz * current_delta_tick - txc.freq / FREQ_SCALE;
}
/* ================================================== */
* a +/- 10% movement of tick away from the nominal value 1e6/USER_HZ. */
static int
-guess_hz(int tick)
+guess_hz(void)
{
- int i, tick_lo, tick_hi, ihz;
+ struct timex txc;
+ int i, tick, tick_lo, tick_hi, ihz;
double tick_nominal;
+
+ txc.modes = 0;
+ SYS_Timex_Adjust(&txc, 0);
+ tick = txc.tick;
+
/* Pick off the hz=100 case first */
if (tick >= 9000 && tick <= 11000) {
return 100;
}
/* oh dear. doomed. */
+ LOG_FATAL(LOGF_SysLinux, "Can't determine hz from tick %d", tick);
+
return 0;
}
get_version_specific_details(void)
{
int major, minor, patch;
- long tick;
- double freq;
struct utsname uts;
hz = get_hz();
- if (!hz) {
- if (TMX_GetFrequency(&freq, &tick) < 0)
- LOG_FATAL(LOGF_SysLinux, "adjtimex() failed");
-
- hz = guess_hz(tick);
-
- if (!hz)
- LOG_FATAL(LOGF_SysLinux, "Can't determine hz from tick %ld", tick);
- }
+ if (!hz)
+ hz = guess_hz();
dhz = (double) hz;
nominal_tick = (1000000L + (hz/2))/hz; /* Mirror declaration in kernel */
hz, nominal_tick, max_tick_bias);
}
+/* ================================================== */
+
+static void
+reset_adjtime_offset(void)
+{
+ struct timex txc;
+
+ /* Reset adjtime() offset */
+ txc.modes = ADJ_OFFSET_SINGLESHOT;
+ txc.offset = 0;
+
+ SYS_Timex_Adjust(&txc, 0);
+}
+
+/* ================================================== */
+
+static int
+test_step_offset(void)
+{
+ struct timex txc;
+
+ /* Zero maxerror and check it's reset to a maximum after ADJ_SETOFFSET.
+ This seems to be the only way how to verify that the kernel really
+ supports the ADJ_SETOFFSET mode as it doesn't return an error on unknown
+ mode. */
+
+ txc.modes = MOD_MAXERROR;
+ txc.maxerror = 0;
+
+ if (SYS_Timex_Adjust(&txc, 1) < 0 || txc.maxerror != 0)
+ return 0;
+
+ txc.modes = ADJ_SETOFFSET | ADJ_NANO;
+ txc.time.tv_sec = 0;
+ txc.time.tv_usec = 0;
+
+ if (SYS_Timex_Adjust(&txc, 1) < 0 || txc.maxerror < 100000)
+ return 0;
+
+ return 1;
+}
+
/* ================================================== */
/* Initialisation code for this module */
{
get_version_specific_details();
- if (TMX_ResetOffset() < 0) {
- LOG_FATAL(LOGF_SysLinux, "adjtimex() failed");
- }
+ reset_adjtime_offset();
- if (have_setoffset && TMX_TestStepOffset() < 0) {
+ if (have_setoffset && !test_step_offset()) {
LOG(LOGS_INFO, LOGF_SysLinux, "adjtimex() doesn't support ADJ_SETOFFSET");
have_setoffset = 0;
}
- SYS_Generic_CompleteFreqDriver(1.0e6 * max_tick_bias / nominal_tick,
- 1.0 / tick_update_hz,
- read_frequency, set_frequency,
- have_setoffset ? apply_step_offset : NULL,
- set_leap, set_sync_status);
+ SYS_Timex_InitialiseWithFunctions(1.0e6 * max_tick_bias / nominal_tick,
+ 1.0 / tick_update_hz,
+ read_frequency, set_frequency,
+ have_setoffset ? apply_step_offset : NULL);
}
/* ================================================== */
void
SYS_Linux_Finalise(void)
{
- SYS_Generic_Finalise();
+ SYS_Timex_Finalise();
}
/* ================================================== */
+++ /dev/null
-/*
- chronyd/chronyc - Programs for keeping computer clocks accurate.
-
- **********************************************************************
- * Copyright (C) Richard P. Curnow 1997-2002
- * Copyright (C) Miroslav Lichvar 2011-2012, 2014
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- **********************************************************************
-
- =======================================================================
-
- This is a wrapper around the Linux adjtimex system call.
-
- */
-
-#include "config.h"
-
-#include "wrap_adjtimex.h"
-
-#include <sys/timex.h>
-
-/* Definitions used if missing in the system headers */
-#ifndef ADJ_TAI
-#define ADJ_TAI 0x0080 /* set TAI offset */
-#endif
-#ifndef ADJ_SETOFFSET
-#define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */
-#endif
-#ifndef ADJ_NANO
-#define ADJ_NANO 0x2000 /* select nanosecond resolution */
-#endif
-#ifndef ADJ_OFFSET_SS_READ
-#define ADJ_OFFSET_SS_READ 0xa001 /* read-only adjtime */
-#endif
-
-/* Frequency offset scale (shift) */
-#define SHIFT_USEC 16
-
-static int status = 0;
-
-int
-TMX_ResetOffset(void)
-{
- struct timex txc;
-
- /* Reset adjtime() offset */
- txc.modes = ADJ_OFFSET_SINGLESHOT;
- txc.offset = 0;
- if (adjtimex(&txc) < 0)
- return -1;
-
- /* Reset PLL offset */
- txc.modes = ADJ_OFFSET | ADJ_STATUS;
- txc.status = STA_PLL;
- txc.offset = 0;
- if (adjtimex(&txc) < 0)
- return -1;
-
- /* Set status back */
- txc.modes = ADJ_STATUS;
- txc.status = status;
- if (adjtimex(&txc) < 0)
- return -1;
-
- return 0;
-}
-
-int
-TMX_SetFrequency(double *freq, long tick)
-{
- struct timex txc;
-
- txc.modes = ADJ_TICK | ADJ_FREQUENCY;
-
- txc.freq = (long)(*freq * (double)(1 << SHIFT_USEC));
- *freq = txc.freq / (double)(1 << SHIFT_USEC);
- txc.tick = tick;
-
- return adjtimex(&txc);
-}
-
-int
-TMX_GetFrequency(double *freq, long *tick)
-{
- struct timex txc;
- int result;
- txc.modes = 0; /* pure read */
- result = adjtimex(&txc);
- *freq = txc.freq / (double)(1 << SHIFT_USEC);
- *tick = txc.tick;
- return result;
-}
-
-int
-TMX_SetLeap(int leap)
-{
- struct timex txc;
-
- status &= ~(STA_INS | STA_DEL);
-
- if (leap > 0) {
- status |= STA_INS;
- } else if (leap < 0) {
- status |= STA_DEL;
- }
-
- txc.modes = ADJ_STATUS;
- txc.status = status;
-
- return adjtimex(&txc);
-}
-
-int
-TMX_GetLeapApplied(int *applied)
-{
- struct timex txc;
- int state;
-
- txc.modes = 0;
- state = adjtimex(&txc);
- if (state < 0)
- return -1;
-
- *applied = state == TIME_WAIT;
-
- return 0;
-}
-
-int TMX_SetSync(int sync, double est_error, double max_error)
-{
- struct timex txc;
-
- if (sync) {
- status &= ~STA_UNSYNC;
- } else {
- status |= STA_UNSYNC;
- }
-
- txc.modes = ADJ_STATUS | ADJ_ESTERROR | ADJ_MAXERROR;
- txc.status = status;
- txc.esterror = est_error * 1.0e6;
- txc.maxerror = max_error * 1.0e6;
-
- return adjtimex(&txc);
-}
-
-int
-TMX_TestStepOffset(void)
-{
- struct timex txc;
-
- /* Zero maxerror and check it's reset to a maximum after ADJ_SETOFFSET.
- This seems to be the only way how to verify that the kernel really
- supports the ADJ_SETOFFSET mode as it doesn't return an error on unknown
- mode. */
-
- txc.modes = ADJ_MAXERROR;
- txc.maxerror = 0;
- if (adjtimex(&txc) < 0 || txc.maxerror != 0)
- return -1;
-
- txc.modes = ADJ_SETOFFSET | ADJ_NANO;
- txc.time.tv_sec = 0;
- txc.time.tv_usec = 0;
- if (adjtimex(&txc) < 0 || txc.maxerror < 100000)
- return -1;
-
- return 0;
-}
-
-int
-TMX_ApplyStepOffset(double offset)
-{
- struct timex txc;
-
- txc.modes = ADJ_SETOFFSET | ADJ_NANO;
- txc.time.tv_sec = offset;
- txc.time.tv_usec = 1.0e9 * (offset - txc.time.tv_sec);
- if (txc.time.tv_usec < 0) {
- txc.time.tv_sec--;
- txc.time.tv_usec += 1000000000;
- }
-
- return adjtimex(&txc);
-}
+++ /dev/null
-/*
- chronyd/chronyc - Programs for keeping computer clocks accurate.
-
- **********************************************************************
- * Copyright (C) Richard P. Curnow 1997-2002
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- **********************************************************************
-
- =======================================================================
-
- The header file for the adjtimex wrapper
- */
-
-#ifndef GOT_WRAP_ADJTIMEX_H
-#define GOT_WRAP_ADJTIMEX_H
-
-int TMX_ResetOffset(void);
-int TMX_SetFrequency(double *freq, long tick);
-int TMX_GetFrequency(double *freq, long *tick);
-int TMX_SetLeap(int leap);
-int TMX_GetLeapApplied(int *applied);
-int TMX_SetSync(int sync, double est_error, double max_error);
-int TMX_TestStepOffset(void);
-int TMX_ApplyStepOffset(double offset);
-
-#endif /* GOT_WRAP_ADJTIMEX_H */
-