Blame clockadj.c

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