Blame local.c

Packit 96c956
/*
Packit 96c956
  chronyd/chronyc - Programs for keeping computer clocks accurate.
Packit 96c956
Packit 96c956
 **********************************************************************
Packit 96c956
 * Copyright (C) Richard P. Curnow  1997-2003
Packit 96c956
 * Copyright (C) Miroslav Lichvar  2011, 2014-2015
Packit 96c956
 * 
Packit 96c956
 * This program is free software; you can redistribute it and/or modify
Packit 96c956
 * it under the terms of version 2 of the GNU General Public License as
Packit 96c956
 * published by the Free Software Foundation.
Packit 96c956
 * 
Packit 96c956
 * This program is distributed in the hope that it will be useful, but
Packit 96c956
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 96c956
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 96c956
 * General Public License for more details.
Packit 96c956
 * 
Packit 96c956
 * You should have received a copy of the GNU General Public License along
Packit 96c956
 * with this program; if not, write to the Free Software Foundation, Inc.,
Packit 96c956
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
Packit 96c956
 * 
Packit 96c956
 **********************************************************************
Packit 96c956
Packit 96c956
  =======================================================================
Packit 96c956
Packit 96c956
  The routines in this file present a common local (system) clock
Packit 96c956
  interface to the rest of the software.
Packit 96c956
Packit 96c956
  They interface with the system specific driver files in sys_*.c
Packit 96c956
  */
Packit 96c956
Packit 96c956
#include "config.h"
Packit 96c956
Packit 96c956
#include "sysincl.h"
Packit 96c956
Packit 96c956
#include "conf.h"
Packit 96c956
#include "local.h"
Packit 96c956
#include "localp.h"
Packit 96c956
#include "memory.h"
Packit 96c956
#include "smooth.h"
Packit 96c956
#include "util.h"
Packit 96c956
#include "logging.h"
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
/* Variable to store the current frequency, in ppm */
Packit 96c956
static double current_freq_ppm;
Packit 96c956
Packit 96c956
/* Maximum allowed frequency, in ppm */
Packit 96c956
static double max_freq_ppm;
Packit 96c956
Packit 96c956
/* Temperature compensation, in ppm */
Packit 96c956
static double temp_comp_ppm;
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
/* Store the system dependent drivers */
Packit 96c956
Packit 96c956
static lcl_ReadFrequencyDriver drv_read_freq;
Packit 96c956
static lcl_SetFrequencyDriver drv_set_freq;
Packit 96c956
static lcl_AccrueOffsetDriver drv_accrue_offset;
Packit 96c956
static lcl_ApplyStepOffsetDriver drv_apply_step_offset;
Packit 96c956
static lcl_OffsetCorrectionDriver drv_offset_convert;
Packit 96c956
static lcl_SetLeapDriver drv_set_leap;
Packit 96c956
static lcl_SetSyncStatusDriver drv_set_sync_status;
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
/* Types and variables associated with handling the parameter change
Packit 96c956
   list */
Packit 96c956
Packit 96c956
typedef struct _ChangeListEntry {
Packit 96c956
  struct _ChangeListEntry *next;
Packit 96c956
  struct _ChangeListEntry *prev;
Packit 96c956
  LCL_ParameterChangeHandler handler;
Packit 96c956
  void *anything;
Packit 96c956
} ChangeListEntry;
Packit 96c956
Packit 96c956
static ChangeListEntry change_list;
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
/* Types and variables associated with handling the parameter change
Packit 96c956
   list */
Packit 96c956
Packit 96c956
typedef struct _DispersionNotifyListEntry {
Packit 96c956
  struct _DispersionNotifyListEntry *next;
Packit 96c956
  struct _DispersionNotifyListEntry *prev;
Packit 96c956
  LCL_DispersionNotifyHandler handler;
Packit 96c956
  void *anything;
Packit 96c956
} DispersionNotifyListEntry;
Packit 96c956
Packit 96c956
static DispersionNotifyListEntry dispersion_notify_list;
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
static int precision_log;
Packit 96c956
static double precision_quantum;
Packit 96c956
Packit 96c956
static double max_clock_error;
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
/* Define the number of increments of the system clock that we want
Packit 96c956
   to see to be fairly sure that we've got something approaching
Packit 96c956
   the minimum increment.  Even on a crummy implementation that can't
Packit 96c956
   interpolate between 10ms ticks, we should get this done in
Packit 96c956
   under 1s of busy waiting. */
Packit 96c956
#define NITERS 100
Packit 96c956
Packit 96c956
#define NSEC_PER_SEC 1000000000
Packit 96c956
Packit 96c956
static void
Packit 96c956
calculate_sys_precision(void)
Packit 96c956
{
Packit 96c956
  struct timespec ts, old_ts;
Packit 96c956
  int iters, diff, best;
Packit 96c956
Packit 96c956
  LCL_ReadRawTime(&old_ts);
Packit 96c956
Packit 96c956
  /* Assume we must be better than a second */
Packit 96c956
  best = NSEC_PER_SEC;
Packit 96c956
  iters = 0;
Packit 96c956
Packit 96c956
  do {
Packit 96c956
    LCL_ReadRawTime(&ts);
Packit 96c956
Packit 96c956
    diff = NSEC_PER_SEC * (ts.tv_sec - old_ts.tv_sec) + (ts.tv_nsec - old_ts.tv_nsec);
Packit 96c956
Packit 96c956
    old_ts = ts;
Packit 96c956
    if (diff > 0) {
Packit 96c956
      if (diff < best)
Packit 96c956
        best = diff;
Packit 96c956
      iters++;
Packit 96c956
    }
Packit 96c956
  } while (iters < NITERS);
Packit 96c956
Packit 96c956
  assert(best > 0);
Packit 96c956
Packit 96c956
  precision_quantum = 1.0e-9 * best;
Packit 96c956
Packit 96c956
  /* Get rounded log2 value of the measured precision */
Packit 96c956
  precision_log = 0;
Packit 96c956
  while (best < 707106781) {
Packit 96c956
    precision_log--;
Packit 96c956
    best *= 2;
Packit 96c956
  }
Packit 96c956
Packit 96c956
  assert(precision_log >= -30);
Packit 96c956
Packit 96c956
  DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
LCL_Initialise(void)
Packit 96c956
{
Packit 96c956
  change_list.next = change_list.prev = &change_list;
Packit 96c956
Packit 96c956
  dispersion_notify_list.next = dispersion_notify_list.prev = &dispersion_notify_list;
Packit 96c956
Packit 96c956
  /* Null out the system drivers, so that we die
Packit 96c956
     if they never get defined before use */
Packit 96c956
  
Packit 96c956
  drv_read_freq = NULL;
Packit 96c956
  drv_set_freq = NULL;
Packit 96c956
  drv_accrue_offset = NULL;
Packit 96c956
  drv_offset_convert = NULL;
Packit 96c956
Packit 96c956
  /* This ought to be set from the system driver layer */
Packit 96c956
  current_freq_ppm = 0.0;
Packit 96c956
  temp_comp_ppm = 0.0;
Packit 96c956
Packit 96c956
  calculate_sys_precision();
Packit 96c956
Packit 96c956
  /* This is the maximum allowed frequency offset in ppm, the time must
Packit 96c956
     never stop or run backwards */
Packit 96c956
  max_freq_ppm = CNF_GetMaxDrift();
Packit 96c956
  max_freq_ppm = CLAMP(0.0, max_freq_ppm, 500000.0);
Packit 96c956
Packit 96c956
  max_clock_error = CNF_GetMaxClockError() * 1e-6;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
LCL_Finalise(void)
Packit 96c956
{
Packit 96c956
  while (change_list.next != &change_list)
Packit 96c956
    LCL_RemoveParameterChangeHandler(change_list.next->handler,
Packit 96c956
                                     change_list.next->anything);
Packit 96c956
Packit 96c956
  while (dispersion_notify_list.next != &dispersion_notify_list)
Packit 96c956
    LCL_RemoveDispersionNotifyHandler(dispersion_notify_list.next->handler,
Packit 96c956
                                      dispersion_notify_list.next->anything);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
/* Routine to read the system precision as a log to base 2 value. */
Packit 96c956
int
Packit 96c956
LCL_GetSysPrecisionAsLog(void)
Packit 96c956
{
Packit 96c956
  return precision_log;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
/* Routine to read the system precision in terms of the actual time step */
Packit 96c956
Packit 96c956
double
Packit 96c956
LCL_GetSysPrecisionAsQuantum(void)
Packit 96c956
{
Packit 96c956
  return precision_quantum;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
double
Packit 96c956
LCL_GetMaxClockError(void)
Packit 96c956
{
Packit 96c956
  return max_clock_error;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
LCL_AddParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything)
Packit 96c956
{
Packit 96c956
  ChangeListEntry *ptr, *new_entry;
Packit 96c956
Packit 96c956
  /* Check that the handler is not already registered */
Packit 96c956
  for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
Packit 96c956
    if (!(ptr->handler != handler || ptr->anything != anything)) {
Packit 96c956
      assert(0);
Packit 96c956
    }
Packit 96c956
  }
Packit 96c956
Packit 96c956
  new_entry = MallocNew(ChangeListEntry);
Packit 96c956
Packit 96c956
  new_entry->handler = handler;
Packit 96c956
  new_entry->anything = anything;
Packit 96c956
Packit 96c956
  /* Chain it into the list */
Packit 96c956
  new_entry->next = &change_list;
Packit 96c956
  new_entry->prev = change_list.prev;
Packit 96c956
  change_list.prev->next = new_entry;
Packit 96c956
  change_list.prev = new_entry;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
/* Remove a handler */
Packit 96c956
void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything)
Packit 96c956
{
Packit 96c956
Packit 96c956
  ChangeListEntry *ptr;
Packit 96c956
  int ok;
Packit 96c956
Packit 96c956
  ptr = NULL;
Packit 96c956
  ok = 0;
Packit 96c956
Packit 96c956
  for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
Packit 96c956
    if (ptr->handler == handler && ptr->anything == anything) {
Packit 96c956
      ok = 1;
Packit 96c956
      break;
Packit 96c956
    }
Packit 96c956
  }
Packit 96c956
Packit 96c956
  assert(ok);
Packit 96c956
Packit 96c956
  /* Unlink entry from the list */
Packit 96c956
  ptr->next->prev = ptr->prev;
Packit 96c956
  ptr->prev->next = ptr->next;
Packit 96c956
Packit 96c956
  Free(ptr);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
int
Packit 96c956
LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler)
Packit 96c956
{
Packit 96c956
  return change_list.next->handler == handler;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
static void
Packit 96c956
invoke_parameter_change_handlers(struct timespec *raw, struct timespec *cooked,
Packit 96c956
                                 double dfreq, double doffset,
Packit 96c956
                                 LCL_ChangeType change_type)
Packit 96c956
{
Packit 96c956
  ChangeListEntry *ptr;
Packit 96c956
Packit 96c956
  for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
Packit 96c956
    (ptr->handler)(raw, cooked, dfreq, doffset, change_type, ptr->anything);
Packit 96c956
  }
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
LCL_AddDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything)
Packit 96c956
{
Packit 96c956
  DispersionNotifyListEntry *ptr, *new_entry;
Packit 96c956
Packit 96c956
  /* Check that the handler is not already registered */
Packit 96c956
  for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
Packit 96c956
    if (!(ptr->handler != handler || ptr->anything != anything)) {
Packit 96c956
      assert(0);
Packit 96c956
    }
Packit 96c956
  }
Packit 96c956
Packit 96c956
  new_entry = MallocNew(DispersionNotifyListEntry);
Packit 96c956
Packit 96c956
  new_entry->handler = handler;
Packit 96c956
  new_entry->anything = anything;
Packit 96c956
Packit 96c956
  /* Chain it into the list */
Packit 96c956
  new_entry->next = &dispersion_notify_list;
Packit 96c956
  new_entry->prev = dispersion_notify_list.prev;
Packit 96c956
  dispersion_notify_list.prev->next = new_entry;
Packit 96c956
  dispersion_notify_list.prev = new_entry;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
/* Remove a handler */
Packit 96c956
extern 
Packit 96c956
void LCL_RemoveDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything)
Packit 96c956
{
Packit 96c956
Packit 96c956
  DispersionNotifyListEntry *ptr;
Packit 96c956
  int ok;
Packit 96c956
Packit 96c956
  ptr = NULL;
Packit 96c956
  ok = 0;
Packit 96c956
Packit 96c956
  for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
Packit 96c956
    if (ptr->handler == handler && ptr->anything == anything) {
Packit 96c956
      ok = 1;
Packit 96c956
      break;
Packit 96c956
    }
Packit 96c956
  }
Packit 96c956
Packit 96c956
  assert(ok);
Packit 96c956
Packit 96c956
  /* Unlink entry from the list */
Packit 96c956
  ptr->next->prev = ptr->prev;
Packit 96c956
  ptr->prev->next = ptr->next;
Packit 96c956
Packit 96c956
  Free(ptr);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
LCL_ReadRawTime(struct timespec *ts)
Packit 96c956
{
Packit 96c956
#if HAVE_CLOCK_GETTIME
Packit 96c956
  if (clock_gettime(CLOCK_REALTIME, ts) < 0)
Packit 96c956
    LOG_FATAL("clock_gettime() failed : %s", strerror(errno));
Packit 96c956
#else
Packit 96c956
  struct timeval tv;
Packit 96c956
Packit 96c956
  if (gettimeofday(&tv, NULL) < 0)
Packit 96c956
    LOG_FATAL("gettimeofday() failed : %s", strerror(errno));
Packit 96c956
Packit 96c956
  UTI_TimevalToTimespec(&tv, ts);
Packit 96c956
#endif
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
LCL_ReadCookedTime(struct timespec *result, double *err)
Packit 96c956
{
Packit 96c956
  struct timespec raw;
Packit 96c956
Packit 96c956
  LCL_ReadRawTime(&raw;;
Packit 96c956
  LCL_CookTime(&raw, result, err);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
LCL_CookTime(struct timespec *raw, struct timespec *cooked, double *err)
Packit 96c956
{
Packit 96c956
  double correction;
Packit 96c956
Packit 96c956
  LCL_GetOffsetCorrection(raw, &correction, err);
Packit 96c956
  UTI_AddDoubleToTimespec(raw, correction, cooked);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
LCL_GetOffsetCorrection(struct timespec *raw, double *correction, double *err)
Packit 96c956
{
Packit 96c956
  /* Call system specific driver to get correction */
Packit 96c956
  (*drv_offset_convert)(raw, correction, err);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
/* Return current frequency */
Packit 96c956
Packit 96c956
double
Packit 96c956
LCL_ReadAbsoluteFrequency(void)
Packit 96c956
{
Packit 96c956
  double freq;
Packit 96c956
Packit 96c956
  freq = current_freq_ppm; 
Packit 96c956
Packit 96c956
  /* Undo temperature compensation */
Packit 96c956
  if (temp_comp_ppm != 0.0) {
Packit 96c956
    freq = (freq + temp_comp_ppm) / (1.0 - 1.0e-6 * temp_comp_ppm);
Packit 96c956
  }
Packit 96c956
Packit 96c956
  return freq;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
static double
Packit 96c956
clamp_freq(double freq)
Packit 96c956
{
Packit 96c956
  if (freq <= max_freq_ppm && freq >= -max_freq_ppm)
Packit 96c956
    return freq;
Packit 96c956
Packit 96c956
  LOG(LOGS_WARN, "Frequency %.1f ppm exceeds allowed maximum", freq);
Packit 96c956
Packit 96c956
  return CLAMP(-max_freq_ppm, freq, max_freq_ppm);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
static int
Packit 96c956
check_offset(struct timespec *now, double offset)
Packit 96c956
{
Packit 96c956
  /* Check if the time will be still sane with accumulated offset */
Packit 96c956
  if (UTI_IsTimeOffsetSane(now, -offset))
Packit 96c956
      return 1;
Packit 96c956
Packit 96c956
  LOG(LOGS_WARN, "Adjustment of %.1f seconds is invalid", -offset);
Packit 96c956
  return 0;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
/* This involves both setting the absolute frequency with the
Packit 96c956
   system-specific driver, as well as calling all notify handlers */
Packit 96c956
Packit 96c956
void
Packit 96c956
LCL_SetAbsoluteFrequency(double afreq_ppm)
Packit 96c956
{
Packit 96c956
  struct timespec raw, cooked;
Packit 96c956
  double dfreq;
Packit 96c956
  
Packit 96c956
  afreq_ppm = clamp_freq(afreq_ppm);
Packit 96c956
Packit 96c956
  /* Apply temperature compensation */
Packit 96c956
  if (temp_comp_ppm != 0.0) {
Packit 96c956
    afreq_ppm = afreq_ppm * (1.0 - 1.0e-6 * temp_comp_ppm) - temp_comp_ppm;
Packit 96c956
  }
Packit 96c956
Packit 96c956
  /* Call the system-specific driver for setting the frequency */
Packit 96c956
  
Packit 96c956
  afreq_ppm = (*drv_set_freq)(afreq_ppm);
Packit 96c956
Packit 96c956
  dfreq = (afreq_ppm - current_freq_ppm) / (1.0e6 - current_freq_ppm);
Packit 96c956
Packit 96c956
  LCL_ReadRawTime(&raw;;
Packit 96c956
  LCL_CookTime(&raw, &cooked, NULL);
Packit 96c956
Packit 96c956
  /* Dispatch to all handlers */
Packit 96c956
  invoke_parameter_change_handlers(&raw, &cooked, dfreq, 0.0, LCL_ChangeAdjust);
Packit 96c956
Packit 96c956
  current_freq_ppm = afreq_ppm;
Packit 96c956
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
LCL_AccumulateDeltaFrequency(double dfreq)
Packit 96c956
{
Packit 96c956
  struct timespec raw, cooked;
Packit 96c956
  double old_freq_ppm;
Packit 96c956
Packit 96c956
  old_freq_ppm = current_freq_ppm;
Packit 96c956
Packit 96c956
  /* Work out new absolute frequency.  Note that absolute frequencies
Packit 96c956
   are handled in units of ppm, whereas the 'dfreq' argument is in
Packit 96c956
   terms of the gradient of the (offset) v (local time) function. */
Packit 96c956
Packit 96c956
  current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm);
Packit 96c956
Packit 96c956
  current_freq_ppm = clamp_freq(current_freq_ppm);
Packit 96c956
Packit 96c956
  /* Call the system-specific driver for setting the frequency */
Packit 96c956
  current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
Packit 96c956
  dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 - old_freq_ppm);
Packit 96c956
Packit 96c956
  LCL_ReadRawTime(&raw;;
Packit 96c956
  LCL_CookTime(&raw, &cooked, NULL);
Packit 96c956
Packit 96c956
  /* Dispatch to all handlers */
Packit 96c956
  invoke_parameter_change_handlers(&raw, &cooked, dfreq, 0.0, LCL_ChangeAdjust);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
LCL_AccumulateOffset(double offset, double corr_rate)
Packit 96c956
{
Packit 96c956
  struct timespec raw, cooked;
Packit 96c956
Packit 96c956
  /* In this case, the cooked time to be passed to the notify clients
Packit 96c956
     has to be the cooked time BEFORE the change was made */
Packit 96c956
Packit 96c956
  LCL_ReadRawTime(&raw;;
Packit 96c956
  LCL_CookTime(&raw, &cooked, NULL);
Packit 96c956
Packit 96c956
  if (!check_offset(&cooked, offset))
Packit 96c956
      return;
Packit 96c956
Packit 96c956
  (*drv_accrue_offset)(offset, corr_rate);
Packit 96c956
Packit 96c956
  /* Dispatch to all handlers */
Packit 96c956
  invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeAdjust);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
int
Packit 96c956
LCL_ApplyStepOffset(double offset)
Packit 96c956
{
Packit 96c956
  struct timespec raw, cooked;
Packit 96c956
Packit 96c956
  /* In this case, the cooked time to be passed to the notify clients
Packit 96c956
     has to be the cooked time BEFORE the change was made */
Packit 96c956
Packit 96c956
  LCL_ReadRawTime(&raw;;
Packit 96c956
  LCL_CookTime(&raw, &cooked, NULL);
Packit 96c956
Packit 96c956
  if (!check_offset(&raw, offset))
Packit 96c956
      return 0;
Packit 96c956
Packit 96c956
  if (!(*drv_apply_step_offset)(offset)) {
Packit 96c956
    LOG(LOGS_ERR, "Could not step system clock");
Packit 96c956
    return 0;
Packit 96c956
  }
Packit 96c956
Packit 96c956
  /* Reset smoothing on all clock steps */
Packit 96c956
  SMT_Reset(&cooked);
Packit 96c956
Packit 96c956
  /* Dispatch to all handlers */
Packit 96c956
  invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeStep);
Packit 96c956
Packit 96c956
  return 1;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
Packit 96c956
    double offset, double dispersion)
Packit 96c956
{
Packit 96c956
  /* Dispatch to all handlers */
Packit 96c956
  invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep);
Packit 96c956
Packit 96c956
  lcl_InvokeDispersionNotifyHandlers(dispersion);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
LCL_NotifyLeap(int leap)
Packit 96c956
{
Packit 96c956
  struct timespec raw, cooked;
Packit 96c956
Packit 96c956
  LCL_ReadRawTime(&raw;;
Packit 96c956
  LCL_CookTime(&raw, &cooked, NULL);
Packit 96c956
Packit 96c956
  /* Smooth the leap second out */
Packit 96c956
  SMT_Leap(&cooked, leap);
Packit 96c956
Packit 96c956
  /* Dispatch to all handlers as if the clock was stepped */
Packit 96c956
  invoke_parameter_change_handlers(&raw, &cooked, 0.0, -leap, LCL_ChangeStep);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
Packit 96c956
{
Packit 96c956
  struct timespec raw, cooked;
Packit 96c956
  double old_freq_ppm;
Packit 96c956
Packit 96c956
  LCL_ReadRawTime(&raw;;
Packit 96c956
  /* Due to modifying the offset, this has to be the cooked time prior
Packit 96c956
     to the change we are about to make */
Packit 96c956
  LCL_CookTime(&raw, &cooked, NULL);
Packit 96c956
Packit 96c956
  if (!check_offset(&cooked, doffset))
Packit 96c956
      return;
Packit 96c956
Packit 96c956
  old_freq_ppm = current_freq_ppm;
Packit 96c956
Packit 96c956
  /* Work out new absolute frequency.  Note that absolute frequencies
Packit 96c956
   are handled in units of ppm, whereas the 'dfreq' argument is in
Packit 96c956
   terms of the gradient of the (offset) v (local time) function. */
Packit 96c956
  current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm);
Packit 96c956
Packit 96c956
  current_freq_ppm = clamp_freq(current_freq_ppm);
Packit 96c956
Packit 96c956
  DEBUG_LOG("old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
Packit 96c956
      old_freq_ppm, current_freq_ppm, doffset);
Packit 96c956
Packit 96c956
  /* Call the system-specific driver for setting the frequency */
Packit 96c956
  current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
Packit 96c956
  dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 - old_freq_ppm);
Packit 96c956
Packit 96c956
  (*drv_accrue_offset)(doffset, corr_rate);
Packit 96c956
Packit 96c956
  /* Dispatch to all handlers */
Packit 96c956
  invoke_parameter_change_handlers(&raw, &cooked, dfreq, doffset, LCL_ChangeAdjust);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
lcl_InvokeDispersionNotifyHandlers(double dispersion)
Packit 96c956
{
Packit 96c956
  DispersionNotifyListEntry *ptr;
Packit 96c956
Packit 96c956
  for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
Packit 96c956
    (ptr->handler)(dispersion, ptr->anything);
Packit 96c956
  }
Packit 96c956
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
Packit 96c956
                          lcl_SetFrequencyDriver set_freq,
Packit 96c956
                          lcl_AccrueOffsetDriver accrue_offset,
Packit 96c956
                          lcl_ApplyStepOffsetDriver apply_step_offset,
Packit 96c956
                          lcl_OffsetCorrectionDriver offset_convert,
Packit 96c956
                          lcl_SetLeapDriver set_leap,
Packit 96c956
                          lcl_SetSyncStatusDriver set_sync_status)
Packit 96c956
{
Packit 96c956
  drv_read_freq = read_freq;
Packit 96c956
  drv_set_freq = set_freq;
Packit 96c956
  drv_accrue_offset = accrue_offset;
Packit 96c956
  drv_apply_step_offset = apply_step_offset;
Packit 96c956
  drv_offset_convert = offset_convert;
Packit 96c956
  drv_set_leap = set_leap;
Packit 96c956
  drv_set_sync_status = set_sync_status;
Packit 96c956
Packit 96c956
  current_freq_ppm = (*drv_read_freq)();
Packit 96c956
Packit 96c956
  DEBUG_LOG("Local freq=%.3fppm", current_freq_ppm);
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
/* Look at the current difference between the system time and the NTP
Packit 96c956
   time, and make a step to cancel it. */
Packit 96c956
Packit 96c956
int
Packit 96c956
LCL_MakeStep(void)
Packit 96c956
{
Packit 96c956
  struct timespec raw;
Packit 96c956
  double correction;
Packit 96c956
Packit 96c956
  LCL_ReadRawTime(&raw;;
Packit 96c956
  LCL_GetOffsetCorrection(&raw, &correction, NULL);
Packit 96c956
Packit 96c956
  if (!check_offset(&raw, -correction))
Packit 96c956
      return 0;
Packit 96c956
Packit 96c956
  /* Cancel remaining slew and make the step */
Packit 96c956
  LCL_AccumulateOffset(correction, 0.0);
Packit 96c956
  if (!LCL_ApplyStepOffset(-correction))
Packit 96c956
    return 0;
Packit 96c956
Packit 96c956
  LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", correction);
Packit 96c956
Packit 96c956
  return 1;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
int
Packit 96c956
LCL_CanSystemLeap(void)
Packit 96c956
{
Packit 96c956
  return drv_set_leap ? 1 : 0;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
LCL_SetSystemLeap(int leap, int tai_offset)
Packit 96c956
{
Packit 96c956
  if (drv_set_leap) {
Packit 96c956
    (drv_set_leap)(leap, tai_offset);
Packit 96c956
  }
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
double
Packit 96c956
LCL_SetTempComp(double comp)
Packit 96c956
{
Packit 96c956
  double uncomp_freq_ppm;
Packit 96c956
Packit 96c956
  if (temp_comp_ppm == comp)
Packit 96c956
    return comp;
Packit 96c956
Packit 96c956
  /* Undo previous compensation */
Packit 96c956
  current_freq_ppm = (current_freq_ppm + temp_comp_ppm) /
Packit 96c956
    (1.0 - 1.0e-6 * temp_comp_ppm);
Packit 96c956
Packit 96c956
  uncomp_freq_ppm = current_freq_ppm;
Packit 96c956
Packit 96c956
  /* Apply new compensation */
Packit 96c956
  current_freq_ppm = current_freq_ppm * (1.0 - 1.0e-6 * comp) - comp;
Packit 96c956
Packit 96c956
  /* Call the system-specific driver for setting the frequency */
Packit 96c956
  current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
Packit 96c956
Packit 96c956
  temp_comp_ppm = (uncomp_freq_ppm - current_freq_ppm) /
Packit 96c956
    (1.0e-6 * uncomp_freq_ppm + 1.0);
Packit 96c956
Packit 96c956
  return temp_comp_ppm;
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */
Packit 96c956
Packit 96c956
void
Packit 96c956
LCL_SetSyncStatus(int synchronised, double est_error, double max_error)
Packit 96c956
{
Packit 96c956
  if (drv_set_sync_status) {
Packit 96c956
    (drv_set_sync_status)(synchronised, est_error, max_error);
Packit 96c956
  }
Packit 96c956
}
Packit 96c956
Packit 96c956
/* ================================================== */