--- /dev/null
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Richard P. Curnow 1997-2001
+ * Copyright (C) J. Hannken-Illjes 2001
+ * Copyright (C) Bryan Christianson 2015
+ *
+ * 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.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Driver file for the MacOS X operating system.
+
+ */
+
+#include "config.h"
+
+#ifdef MACOSX
+
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <nlist.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <signal.h>
+
+#include "sys_macosx.h"
+#include "localp.h"
+#include "logging.h"
+#include "util.h"
+
+/* ================================================== */
+
+/* This register contains the number of seconds by which the local
+ clock was estimated to be fast of reference time at the epoch when
+ gettimeofday() returned T0 */
+
+static double offset_register;
+
+/* This register contains the epoch to which the offset is referenced */
+
+static struct timeval T0;
+
+/* This register contains the current estimate of the system
+ frequency, in absolute (NOT ppm) */
+
+static double current_freq;
+
+/* This register contains the number of seconds of adjustment that
+ were passed to adjtime last time it was called. */
+
+static double adjustment_requested;
+
+/* Kernel parameters to calculate adjtime error. */
+
+static int kern_tickadj;
+static long kern_bigadj;
+
+/* ================================================== */
+
+static void
+clock_initialise(void)
+{
+ struct timeval newadj, oldadj;
+
+ offset_register = 0.0;
+ adjustment_requested = 0.0;
+ current_freq = 0.0;
+
+ if (gettimeofday(&T0, NULL) < 0) {
+ LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
+ }
+
+ newadj.tv_sec = 0;
+ newadj.tv_usec = 0;
+
+ if (adjtime(&newadj, &oldadj) < 0) {
+ LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
+ }
+}
+
+/* ================================================== */
+
+static void
+clock_finalise(void)
+{
+ /* Nothing to do yet */
+}
+
+/* ================================================== */
+
+static void
+start_adjust(void)
+{
+ struct timeval newadj, oldadj;
+ struct timeval T1;
+ double elapsed, accrued_error;
+ double adjust_required;
+ struct timeval exact_newadj;
+ long delta, tickdelta;
+ double rounding_error;
+ double old_adjust_remaining;
+
+ /* Determine the amount of error built up since the last adjustment */
+ if (gettimeofday(&T1, NULL) < 0) {
+ LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
+ }
+
+ UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
+ accrued_error = elapsed * current_freq;
+
+ adjust_required = - (accrued_error + offset_register);
+
+ UTI_DoubleToTimeval(adjust_required, &exact_newadj);
+
+ /* At this point, we need to round the required adjustment the
+ same way the kernel does. */
+
+ delta = exact_newadj.tv_sec * 1000000 + exact_newadj.tv_usec;
+ if (delta > kern_bigadj || delta < -kern_bigadj)
+ tickdelta = 10 * kern_tickadj;
+ else
+ tickdelta = kern_tickadj;
+ if (delta % tickdelta)
+ delta = delta / tickdelta * tickdelta;
+ newadj.tv_sec = 0;
+ newadj.tv_usec = (int)delta;
+ UTI_NormaliseTimeval(&newadj);
+
+ /* Add rounding error back onto offset register. */
+ UTI_DiffTimevalsToDouble(&rounding_error, &newadj, &exact_newadj);
+
+ if (adjtime(&newadj, &oldadj) < 0) {
+ LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
+ }
+
+ UTI_TimevalToDouble(&oldadj, &old_adjust_remaining);
+
+ offset_register = rounding_error - old_adjust_remaining;
+
+ T0 = T1;
+ UTI_TimevalToDouble(&newadj, &adjustment_requested);
+}
+
+/* ================================================== */
+
+static void
+stop_adjust(void)
+{
+ struct timeval T1;
+ struct timeval zeroadj, remadj;
+ double adjustment_remaining, adjustment_achieved;
+ double elapsed, elapsed_plus_adjust;
+
+ zeroadj.tv_sec = 0;
+ zeroadj.tv_usec = 0;
+
+ if (adjtime(&zeroadj, &remadj) < 0) {
+ LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
+ }
+
+ if (gettimeofday(&T1, NULL) < 0) {
+ LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
+ }
+
+ UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
+ UTI_TimevalToDouble(&remadj, &adjustment_remaining);
+
+ adjustment_achieved = adjustment_requested - adjustment_remaining;
+ elapsed_plus_adjust = elapsed - adjustment_achieved;
+
+ offset_register += current_freq * elapsed_plus_adjust - adjustment_remaining;
+
+ adjustment_requested = 0.0;
+ T0 = T1;
+}
+
+/* ================================================== */
+
+/* Positive offset means system clock is fast of true time, therefore
+ slew backwards */
+
+static void
+accrue_offset(double offset, double corr_rate)
+{
+ stop_adjust();
+ offset_register += offset;
+ start_adjust();
+}
+
+/* ================================================== */
+
+/* Positive offset means system clock is fast of true time, therefore
+ step backwards */
+
+static int
+apply_step_offset(double offset)
+{
+ struct timeval old_time, new_time, T1;
+
+ stop_adjust();
+
+ if (gettimeofday(&old_time, NULL) < 0) {
+ LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
+ }
+
+ UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
+
+ if (settimeofday(&new_time, NULL) < 0) {
+ DEBUG_LOG(LOGF_SysMacOSX, "settimeofday() failed");
+ return 0;
+ }
+
+ UTI_AddDoubleToTimeval(&T0, offset, &T1);
+ T0 = T1;
+
+ start_adjust();
+
+ return 1;
+}
+
+/* ================================================== */
+
+static double
+set_frequency(double new_freq_ppm)
+{
+ stop_adjust();
+ current_freq = new_freq_ppm * 1.0e-6;
+ start_adjust();
+
+ return current_freq * 1.0e6;
+}
+
+/* ================================================== */
+
+static double
+read_frequency(void)
+{
+ return current_freq * 1.0e6;
+}
+
+/* ================================================== */
+
+static void
+get_offset_correction(struct timeval *raw,
+ double *corr, double *err)
+{
+ stop_adjust();
+ *corr = -offset_register;
+ start_adjust();
+ if (err)
+ *err = 0.0;
+}
+
+/* ================================================== */
+
+void
+SYS_MacOSX_Initialise(void)
+{
+ int result;
+ size_t len;
+ struct clockinfo clockinfo;
+ int mib[2];
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_CLOCKRATE;
+
+ len = sizeof(clockinfo);
+ result = sysctl(mib, 2, &clockinfo, &len, NULL, 0);
+
+ if(result < 0) {
+ LOG_FATAL(LOGF_SysMacOSX, "Cannot read clockinfo");
+ }
+ kern_tickadj = clockinfo.tickadj;
+ kern_bigadj = clockinfo.tick;
+
+ clock_initialise();
+
+ lcl_RegisterSystemDrivers(read_frequency, set_frequency,
+ accrue_offset, apply_step_offset,
+ get_offset_correction,
+ NULL /* set_leap */,
+ NULL /* set_sync_status */);
+}
+
+/* ================================================== */
+
+void
+SYS_MacOSX_Finalise(void)
+{
+ clock_finalise();
+}
+
+/* ================================================== */
+
+#endif