Blame clockadj.c

Packit 9c3e7e
/**
Packit 9c3e7e
 * @file clockadj.c
Packit 9c3e7e
 * @note Copyright (C) 2013 Richard Cochran <richardcochran@gmail.com>
Packit 9c3e7e
 *
Packit 9c3e7e
 * This program is free software; you can redistribute it and/or modify
Packit 9c3e7e
 * it under the terms of the GNU General Public License as published by
Packit 9c3e7e
 * the Free Software Foundation; either version 2 of the License, or
Packit 9c3e7e
 * (at your option) any later version.
Packit 9c3e7e
 *
Packit 9c3e7e
 * This program is distributed in the hope that it will be useful,
Packit 9c3e7e
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 9c3e7e
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 9c3e7e
 * GNU General Public License for more details.
Packit 9c3e7e
 *
Packit 9c3e7e
 * You should have received a copy of the GNU General Public License along
Packit 9c3e7e
 * with this program; if not, write to the Free Software Foundation, Inc.,
Packit 9c3e7e
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit 9c3e7e
 */
Packit 9c3e7e
Packit 9c3e7e
#include <math.h>
Packit 9c3e7e
#include <string.h>
Packit 9c3e7e
#include <unistd.h>
Packit 9c3e7e
Packit 9c3e7e
#include "clockadj.h"
Packit 9c3e7e
#include "missing.h"
Packit 9c3e7e
#include "print.h"
Packit 9c3e7e
Packit 9c3e7e
#define NS_PER_SEC 1000000000LL
Packit 9c3e7e
Packit 9c3e7e
static int realtime_leap_bit;
Packit 9c3e7e
static long realtime_hz;
Packit 9c3e7e
static long realtime_nominal_tick;
Packit 9c3e7e
Packit 9c3e7e
void clockadj_init(clockid_t clkid)
Packit 9c3e7e
{
Packit 9c3e7e
#ifdef _SC_CLK_TCK
Packit 9c3e7e
	if (clkid == CLOCK_REALTIME) {
Packit 9c3e7e
		/* This is USER_HZ in the kernel. */
Packit 9c3e7e
		realtime_hz = sysconf(_SC_CLK_TCK);
Packit 9c3e7e
		if (realtime_hz > 0) {
Packit 9c3e7e
			/* This is TICK_USEC in the kernel. */
Packit 9c3e7e
			realtime_nominal_tick =
Packit 9c3e7e
				(1000000 + realtime_hz / 2) / realtime_hz;
Packit 9c3e7e
		}
Packit 9c3e7e
	}
Packit 9c3e7e
#endif
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
void clockadj_set_freq(clockid_t clkid, double freq)
Packit 9c3e7e
{
Packit 9c3e7e
	struct timex tx;
Packit 9c3e7e
	memset(&tx, 0, sizeof(tx));
Packit 9c3e7e
Packit 9c3e7e
	/* With system clock set also the tick length. */
Packit 9c3e7e
	if (clkid == CLOCK_REALTIME && realtime_nominal_tick) {
Packit 9c3e7e
		tx.modes |= ADJ_TICK;
Packit 9c3e7e
		tx.tick = round(freq / 1e3 / realtime_hz) + realtime_nominal_tick;
Packit 9c3e7e
		freq -= 1e3 * realtime_hz * (tx.tick - realtime_nominal_tick);
Packit 9c3e7e
	}
Packit 9c3e7e
Packit 9c3e7e
	tx.modes |= ADJ_FREQUENCY;
Packit 9c3e7e
	tx.freq = (long) (freq * 65.536);
Packit 9c3e7e
	if (clock_adjtime(clkid, &tx) < 0)
Packit 9c3e7e
		pr_err("failed to adjust the clock: %m");
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
double clockadj_get_freq(clockid_t clkid)
Packit 9c3e7e
{
Packit 9c3e7e
	double f = 0.0;
Packit 9c3e7e
	struct timex tx;
Packit 9c3e7e
	memset(&tx, 0, sizeof(tx));
Packit 9c3e7e
	if (clock_adjtime(clkid, &tx) < 0) {
Packit 9c3e7e
		pr_err("failed to read out the clock frequency adjustment: %m");
Packit 9c3e7e
	} else {
Packit 9c3e7e
		f = tx.freq / 65.536;
Packit 9c3e7e
		if (clkid == CLOCK_REALTIME && realtime_nominal_tick && tx.tick)
Packit 9c3e7e
			f += 1e3 * realtime_hz * (tx.tick - realtime_nominal_tick);
Packit 9c3e7e
	}
Packit 9c3e7e
	return f;
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
void clockadj_step(clockid_t clkid, int64_t step)
Packit 9c3e7e
{
Packit 9c3e7e
	struct timex tx;
Packit 9c3e7e
	int sign = 1;
Packit 9c3e7e
	if (step < 0) {
Packit 9c3e7e
		sign = -1;
Packit 9c3e7e
		step *= -1;
Packit 9c3e7e
	}
Packit 9c3e7e
	memset(&tx, 0, sizeof(tx));
Packit 9c3e7e
	tx.modes = ADJ_SETOFFSET | ADJ_NANO;
Packit 9c3e7e
	tx.time.tv_sec  = sign * (step / NS_PER_SEC);
Packit 9c3e7e
	tx.time.tv_usec = sign * (step % NS_PER_SEC);
Packit 9c3e7e
	/*
Packit 9c3e7e
	 * The value of a timeval is the sum of its fields, but the
Packit 9c3e7e
	 * field tv_usec must always be non-negative.
Packit 9c3e7e
	 */
Packit 9c3e7e
	if (tx.time.tv_usec < 0) {
Packit 9c3e7e
		tx.time.tv_sec  -= 1;
Packit 9c3e7e
		tx.time.tv_usec += 1000000000;
Packit 9c3e7e
	}
Packit 9c3e7e
	if (clock_adjtime(clkid, &tx) < 0)
Packit 9c3e7e
		pr_err("failed to step clock: %m");
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
void sysclk_set_leap(int leap)
Packit 9c3e7e
{
Packit 9c3e7e
	clockid_t clkid = CLOCK_REALTIME;
Packit 9c3e7e
	struct timex tx;
Packit 9c3e7e
	const char *m = NULL;
Packit 9c3e7e
	memset(&tx, 0, sizeof(tx));
Packit 9c3e7e
	tx.modes = ADJ_STATUS;
Packit 9c3e7e
	switch (leap) {
Packit 9c3e7e
	case -1:
Packit 9c3e7e
		tx.status = STA_DEL;
Packit 9c3e7e
		m = "clock set to delete leap second at midnight (UTC)";
Packit 9c3e7e
		break;
Packit 9c3e7e
	case 1:
Packit 9c3e7e
		tx.status = STA_INS;
Packit 9c3e7e
		m = "clock set to insert leap second at midnight (UTC)";
Packit 9c3e7e
		break;
Packit 9c3e7e
	default:
Packit 9c3e7e
		tx.status = 0;
Packit 9c3e7e
	}
Packit 9c3e7e
	if (clock_adjtime(clkid, &tx) < 0)
Packit 9c3e7e
		pr_err("failed to set the clock status: %m");
Packit 9c3e7e
	else if (m)
Packit 9c3e7e
		pr_notice("%s", m);
Packit 9c3e7e
	realtime_leap_bit = tx.status;
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
void sysclk_set_tai_offset(int offset)
Packit 9c3e7e
{
Packit 9c3e7e
	clockid_t clkid = CLOCK_REALTIME;
Packit 9c3e7e
	struct timex tx;
Packit 9c3e7e
	memset(&tx, 0, sizeof(tx));
Packit 9c3e7e
	tx.modes = ADJ_TAI;
Packit 9c3e7e
	tx.constant = offset;
Packit 9c3e7e
	if (clock_adjtime(clkid, &tx) < 0)
Packit 9c3e7e
		pr_err("failed to set TAI offset: %m");
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
int sysclk_max_freq(void)
Packit 9c3e7e
{
Packit 9c3e7e
	clockid_t clkid = CLOCK_REALTIME;
Packit 9c3e7e
	int f = 0;
Packit 9c3e7e
	struct timex tx;
Packit 9c3e7e
	memset(&tx, 0, sizeof(tx));
Packit 9c3e7e
	if (clock_adjtime(clkid, &tx) < 0)
Packit 9c3e7e
		pr_err("failed to read out the clock maximum adjustment: %m");
Packit 9c3e7e
	else
Packit 9c3e7e
		f = tx.tolerance / 65.536;
Packit 9c3e7e
	if (!f)
Packit 9c3e7e
		f = 500000;
Packit 9c3e7e
Packit 9c3e7e
	/* The kernel allows the tick length to be adjusted up to 10%. But use
Packit 9c3e7e
	   it only if the overall frequency of the clock can be adjusted
Packit 9c3e7e
	   continuously with the tick and freq fields (i.e. hz <= 1000). */
Packit 9c3e7e
	if (realtime_nominal_tick && 2 * f >= 1000 * realtime_hz)
Packit 9c3e7e
		f = realtime_nominal_tick / 10 * 1000 * realtime_hz;
Packit 9c3e7e
Packit 9c3e7e
	return f;
Packit 9c3e7e
}
Packit 9c3e7e
Packit 9c3e7e
void sysclk_set_sync(void)
Packit 9c3e7e
{
Packit 9c3e7e
	clockid_t clkid = CLOCK_REALTIME;
Packit 9c3e7e
	struct timex tx;
Packit 9c3e7e
	memset(&tx, 0, sizeof(tx));
Packit 9c3e7e
	/* Clear the STA_UNSYNC flag from the status and keep the maxerror
Packit 9c3e7e
	   value (which is increased automatically by 500 ppm) below 16 seconds
Packit 9c3e7e
	   to avoid getting the STA_UNSYNC flag back. */
Packit 9c3e7e
	tx.modes = ADJ_STATUS | ADJ_MAXERROR;
Packit 9c3e7e
	tx.status = realtime_leap_bit;
Packit 9c3e7e
	if (clock_adjtime(clkid, &tx) < 0)
Packit 9c3e7e
		pr_err("failed to set clock status and maximum error: %m");
Packit 9c3e7e
}