Blame phc_ctl.c

Packit Service d8d8ac
/*
Packit Service d8d8ac
 * @file phc_ctl.c
Packit Service d8d8ac
 * @brief Utility program to directly control and debug a PHC device.
Packit Service d8d8ac
 * @note Copyright (C) 2014 Jacob Keller <jacob.keller@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
#include <errno.h>
Packit Service d8d8ac
#include <fcntl.h>
Packit Service d8d8ac
#include <float.h>
Packit Service d8d8ac
#include <signal.h>
Packit Service d8d8ac
#include <inttypes.h>
Packit Service d8d8ac
#include <limits.h>
Packit Service d8d8ac
#include <net/if.h>
Packit Service d8d8ac
#include <poll.h>
Packit Service d8d8ac
#include <stdint.h>
Packit Service d8d8ac
#include <stdio.h>
Packit Service d8d8ac
#include <stdlib.h>
Packit Service d8d8ac
#include <string.h>
Packit Service d8d8ac
#include <sys/ioctl.h>
Packit Service d8d8ac
#include <sys/queue.h>
Packit Service d8d8ac
#include <sys/stat.h>
Packit Service d8d8ac
#include <sys/types.h>
Packit Service d8d8ac
#include <unistd.h>
Packit Service d8d8ac
#include <math.h>
Packit Service d8d8ac
Packit Service d8d8ac
#include <linux/pps.h>
Packit Service d8d8ac
#include <linux/ptp_clock.h>
Packit Service d8d8ac
Packit Service d8d8ac
#include "clockadj.h"
Packit Service d8d8ac
#include "missing.h"
Packit Service d8d8ac
#include "phc.h"
Packit Service d8d8ac
#include "print.h"
Packit Service d8d8ac
#include "sk.h"
Packit Service d8d8ac
#include "sysoff.h"
Packit Service d8d8ac
#include "util.h"
Packit Service d8d8ac
#include "version.h"
Packit Service d8d8ac
Packit Service d8d8ac
#define NSEC2SEC 1000000000.0
Packit Service d8d8ac
Packit Service d8d8ac
/* trap the alarm signal so that pause() will wake up on receipt */
Packit Service d8d8ac
static void handle_alarm(int s)
Packit Service d8d8ac
{
Packit Service d8d8ac
	return;
Packit Service d8d8ac
}
Packit Service d8d8ac
Packit Service d8d8ac
static void double_to_timespec(double d, struct timespec *ts)
Packit Service d8d8ac
{
Packit Service d8d8ac
	double fraction, whole;
Packit Service d8d8ac
Packit Service d8d8ac
	fraction = modf(d, &whole);
Packit Service d8d8ac
Packit Service d8d8ac
	/* cast the whole value to a time_t to store as seconds */
Packit Service d8d8ac
	ts->tv_sec = (time_t)whole;
Packit Service d8d8ac
	/* tv_nsec is a long, so we multiply the nanoseconds per second double
Packit Service d8d8ac
	 * value by our fractional component. This results in a correct
Packit Service d8d8ac
	 * timespec from the double representing seconds.
Packit Service d8d8ac
	 */
Packit Service d8d8ac
	ts->tv_nsec = (long)(NSEC2SEC * fraction);
Packit Service d8d8ac
}
Packit Service d8d8ac
Packit Service d8d8ac
static int install_handler(int signum, void(*handler)(int))
Packit Service d8d8ac
{
Packit Service d8d8ac
	struct sigaction action;
Packit Service d8d8ac
	sigset_t mask;
Packit Service d8d8ac
Packit Service d8d8ac
	/* Unblock the signal */
Packit Service d8d8ac
	sigemptyset(&mask);
Packit Service d8d8ac
	sigaddset(&mask, signum);
Packit Service d8d8ac
	sigprocmask(SIG_UNBLOCK, &mask, NULL);
Packit Service d8d8ac
Packit Service d8d8ac
	/* Install the signal handler */
Packit Service d8d8ac
	action.sa_handler = handler;
Packit Service d8d8ac
	action.sa_flags = 0;
Packit Service d8d8ac
	sigemptyset(&action.sa_mask);
Packit Service d8d8ac
	sigaction(signum, &action, NULL);
Packit Service d8d8ac
Packit Service d8d8ac
	return 0;
Packit Service d8d8ac
}
Packit Service d8d8ac
Packit Service d8d8ac
static int64_t calculate_offset(struct timespec *ts1,
Packit Service d8d8ac
				      struct timespec *rt,
Packit Service d8d8ac
				      struct timespec *ts2)
Packit Service d8d8ac
{
Packit Service d8d8ac
	int64_t interval;
Packit Service d8d8ac
	int64_t offset;
Packit Service d8d8ac
Packit Service d8d8ac
#define NSEC_PER_SEC 1000000000ULL
Packit Service d8d8ac
	/* calculate interval between clock realtime */
Packit Service d8d8ac
	interval = (ts2->tv_sec - ts1->tv_sec) * NSEC_PER_SEC;
Packit Service d8d8ac
	interval += ts2->tv_nsec - ts1->tv_nsec;
Packit Service d8d8ac
Packit Service d8d8ac
	/* assume PHC read occured half way between CLOCK_REALTIME reads */
Packit Service d8d8ac
Packit Service d8d8ac
	offset = (rt->tv_sec - ts1->tv_sec) * NSEC_PER_SEC;
Packit Service d8d8ac
	offset += (rt->tv_nsec - ts1->tv_nsec) - (interval / 2);
Packit Service d8d8ac
Packit Service d8d8ac
	return offset;
Packit Service d8d8ac
}
Packit Service d8d8ac
Packit Service d8d8ac
static clockid_t clock_open(char *device)
Packit Service d8d8ac
{
Packit Service d8d8ac
	struct sk_ts_info ts_info;
Packit Service d8d8ac
	char phc_device[19];
Packit Service d8d8ac
	int clkid;
Packit Service d8d8ac
Packit Service d8d8ac
	/* check if device is CLOCK_REALTIME */
Packit Service d8d8ac
	if (!strcasecmp(device, "CLOCK_REALTIME"))
Packit Service d8d8ac
		return CLOCK_REALTIME;
Packit Service d8d8ac
Packit Service d8d8ac
	/* check if device is valid phc device */
Packit Service d8d8ac
	clkid = phc_open(device);
Packit Service d8d8ac
	if (clkid != CLOCK_INVALID)
Packit Service d8d8ac
		return clkid;
Packit Service d8d8ac
Packit Service d8d8ac
	/* check if device is a valid ethernet device */
Packit Service d8d8ac
	if (sk_get_ts_info(device, &ts_info) || !ts_info.valid) {
Packit Service d8d8ac
		pr_err("unknown clock %s: %m", device);
Packit Service d8d8ac
		return CLOCK_INVALID;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	if (ts_info.phc_index < 0) {
Packit Service d8d8ac
		pr_err("interface %s does not have a PHC", device);
Packit Service d8d8ac
		return CLOCK_INVALID;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	sprintf(phc_device, "/dev/ptp%d", ts_info.phc_index);
Packit Service d8d8ac
	clkid = phc_open(phc_device);
Packit Service d8d8ac
	if (clkid == CLOCK_INVALID)
Packit Service d8d8ac
		pr_err("cannot open %s for %s: %m", phc_device, device);
Packit Service d8d8ac
	return clkid;
Packit Service d8d8ac
}
Packit Service d8d8ac
Packit Service d8d8ac
static void usage(const char *progname)
Packit Service d8d8ac
{
Packit Service d8d8ac
	fprintf(stderr,
Packit Service d8d8ac
		"\n"
Packit Service d8d8ac
		"usage: %s [options] <device> -- [command]\n\n"
Packit Service d8d8ac
		" device         ethernet or ptp clock device"
Packit Service d8d8ac
		"\n"
Packit Service d8d8ac
		" options\n"
Packit Service d8d8ac
		" -l [num]       set the logging level to 'num'\n"
Packit Service d8d8ac
		" -q             do not print messages to the syslog\n"
Packit Service d8d8ac
		" -Q             do not print messages to stdout\n"
Packit Service d8d8ac
		" -v             prints the software version and exits\n"
Packit Service d8d8ac
		" -h             prints this message and exits\n"
Packit Service d8d8ac
		"\n"
Packit Service d8d8ac
		" commands\n"
Packit Service d8d8ac
		" specify commands with arguments. Can specify multiple\n"
Packit Service d8d8ac
		" commands to be executed in order. Seconds are read as\n"
Packit Service d8d8ac
		" double precision floating point values.\n"
Packit Service d8d8ac
		"  set  [seconds]  set PHC time (defaults to time on CLOCK_REALTIME)\n"
Packit Service d8d8ac
		"  get             get PHC time\n"
Packit Service d8d8ac
		"  adj  <seconds>  adjust PHC time by offset\n"
Packit Service d8d8ac
		"  freq [ppb]      adjust PHC frequency (default returns current offset)\n"
Packit Service d8d8ac
		"  cmp             compare PHC offset to CLOCK_REALTIME\n"
Packit Service d8d8ac
		"  caps            display device capabilities (default if no command given)\n"
Packit Service d8d8ac
		"  wait <seconds>  pause between commands\n"
Packit Service d8d8ac
		"\n",
Packit Service d8d8ac
		progname);
Packit Service d8d8ac
}
Packit Service d8d8ac
Packit Service d8d8ac
typedef int (*cmd_func_t)(clockid_t, int, char *[]);
Packit Service d8d8ac
struct cmd_t {
Packit Service d8d8ac
	const char *name;
Packit Service d8d8ac
	const cmd_func_t function;
Packit Service d8d8ac
};
Packit Service d8d8ac
Packit Service d8d8ac
static cmd_func_t get_command_function(const char *name);
Packit Service d8d8ac
static inline int name_is_a_command(const char *name);
Packit Service d8d8ac
Packit Service d8d8ac
static int do_set(clockid_t clkid, int cmdc, char *cmdv[])
Packit Service d8d8ac
{
Packit Service d8d8ac
	struct timespec ts;
Packit Service d8d8ac
	double time_arg = 0;
Packit Service d8d8ac
	int args_to_eat = 0;
Packit Service d8d8ac
Packit Service d8d8ac
	enum parser_result r;
Packit Service d8d8ac
Packit Service d8d8ac
	memset(&ts, 0, sizeof(ts));
Packit Service d8d8ac
Packit Service d8d8ac
	/* if we have no more arguments, or the next argument is the ";"
Packit Service d8d8ac
	 * separator, then we run set as default parameter mode */
Packit Service d8d8ac
	if (cmdc < 1 || name_is_a_command(cmdv[0])) {
Packit Service d8d8ac
		clock_gettime(CLOCK_REALTIME, &ts);
Packit Service d8d8ac
Packit Service d8d8ac
		/* since we aren't using the options, we can simply ensure
Packit Service d8d8ac
		 * that we don't eat any arguments
Packit Service d8d8ac
		 */
Packit Service d8d8ac
		args_to_eat = 0;
Packit Service d8d8ac
	} else {
Packit Service d8d8ac
		/* parse the double */
Packit Service d8d8ac
		r = get_ranged_double(cmdv[0], &time_arg, 0.0, DBL_MAX);
Packit Service d8d8ac
		switch (r) {
Packit Service d8d8ac
		case PARSED_OK:
Packit Service d8d8ac
			break;
Packit Service d8d8ac
		case MALFORMED:
Packit Service d8d8ac
			pr_err("set: '%s' is not a valid double", cmdv[0]);
Packit Service d8d8ac
			return -2;
Packit Service d8d8ac
		case OUT_OF_RANGE:
Packit Service d8d8ac
			pr_err("set: '%s' is out of range", cmdv[0]);
Packit Service d8d8ac
			return -2;
Packit Service d8d8ac
		default:
Packit Service d8d8ac
			pr_err("set: couldn't process '%s'", cmdv[0]);
Packit Service d8d8ac
			return -2;
Packit Service d8d8ac
		}
Packit Service d8d8ac
Packit Service d8d8ac
		double_to_timespec(time_arg, &ts);
Packit Service d8d8ac
Packit Service d8d8ac
		/* in order for processing to work, we need to ensure the
Packit Service d8d8ac
		 * run_cmds loop eats the optional set argument
Packit Service d8d8ac
		 */
Packit Service d8d8ac
		args_to_eat = 1;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	if (clock_settime(clkid, &ts)) {
Packit Service d8d8ac
		pr_err("set: failed to set clock time: %s",
Packit Service d8d8ac
			strerror(errno));
Packit Service d8d8ac
		return -1;
Packit Service d8d8ac
	} else {
Packit Service d8d8ac
		pr_notice("set clock time to %ld.%09ld or %s",
Packit Service d8d8ac
			ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec));
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	return args_to_eat;
Packit Service d8d8ac
}
Packit Service d8d8ac
Packit Service d8d8ac
static int do_get(clockid_t clkid, int cmdc, char *cmdv[])
Packit Service d8d8ac
{
Packit Service d8d8ac
	struct timespec ts;
Packit Service d8d8ac
Packit Service d8d8ac
	memset(&ts, 0, sizeof(ts));
Packit Service d8d8ac
	if (clock_gettime(clkid, &ts)) {
Packit Service d8d8ac
		pr_err("get: failed to get clock time: %s",
Packit Service d8d8ac
			strerror(errno));
Packit Service d8d8ac
Packit Service d8d8ac
		return -1;
Packit Service d8d8ac
	} else {
Packit Service d8d8ac
		pr_notice("clock time is %ld.%09lu or %s",
Packit Service d8d8ac
			ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec));
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	/* get operation does not require any arguments */
Packit Service d8d8ac
	return 0;
Packit Service d8d8ac
}
Packit Service d8d8ac
Packit Service d8d8ac
static int do_adj(clockid_t clkid, int cmdc, char *cmdv[])
Packit Service d8d8ac
{
Packit Service d8d8ac
	double time_arg;
Packit Service d8d8ac
	int64_t nsecs;
Packit Service d8d8ac
	enum parser_result r;
Packit Service d8d8ac
Packit Service d8d8ac
	if (cmdc < 1 || name_is_a_command(cmdv[0])) {
Packit Service d8d8ac
		pr_err("adj: missing required time argument");
Packit Service d8d8ac
		return -2;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	/* parse the double time offset argument */
Packit Service d8d8ac
	r = get_ranged_double(cmdv[0], &time_arg, -DBL_MAX, DBL_MAX);
Packit Service d8d8ac
	switch (r) {
Packit Service d8d8ac
	case PARSED_OK:
Packit Service d8d8ac
		break;
Packit Service d8d8ac
	case MALFORMED:
Packit Service d8d8ac
		pr_err("adj: '%s' is not a valid double", cmdv[0]);
Packit Service d8d8ac
		return -2;
Packit Service d8d8ac
	case OUT_OF_RANGE:
Packit Service d8d8ac
		pr_err("adj: '%s' is out of range.", cmdv[0]);
Packit Service d8d8ac
		return -2;
Packit Service d8d8ac
	default:
Packit Service d8d8ac
		pr_err("adj: couldn't process '%s'", cmdv[0]);
Packit Service d8d8ac
		return -2;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	nsecs = (int64_t)(NSEC2SEC * time_arg);
Packit Service d8d8ac
Packit Service d8d8ac
	clockadj_init(clkid);
Packit Service d8d8ac
	clockadj_step(clkid, nsecs);
Packit Service d8d8ac
Packit Service d8d8ac
	pr_notice("adjusted clock by %lf seconds", time_arg);
Packit Service d8d8ac
Packit Service d8d8ac
	/* adjustment always consumes one argument */
Packit Service d8d8ac
	return 1;
Packit Service d8d8ac
}
Packit Service d8d8ac
Packit Service d8d8ac
static int do_freq(clockid_t clkid, int cmdc, char *cmdv[])
Packit Service d8d8ac
{
Packit Service d8d8ac
	double ppb;
Packit Service d8d8ac
	enum parser_result r;
Packit Service d8d8ac
Packit Service d8d8ac
	clockadj_init(clkid);
Packit Service d8d8ac
Packit Service d8d8ac
	if (cmdc < 1 || name_is_a_command(cmdv[0])) {
Packit Service d8d8ac
		ppb = clockadj_get_freq(clkid);
Packit Service d8d8ac
		pr_err("clock frequency offset is %lfppb", ppb);
Packit Service d8d8ac
Packit Service d8d8ac
		/* no argument was used */
Packit Service d8d8ac
		return 0;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	/* parse the double ppb argument */
Packit Service d8d8ac
	r = get_ranged_double(cmdv[0], &ppb, -NSEC2SEC, NSEC2SEC);
Packit Service d8d8ac
	switch (r) {
Packit Service d8d8ac
	case PARSED_OK:
Packit Service d8d8ac
		break;
Packit Service d8d8ac
	case MALFORMED:
Packit Service d8d8ac
		pr_err("freq: '%s' is not a valid double", cmdv[0]);
Packit Service d8d8ac
		return -2;
Packit Service d8d8ac
	case OUT_OF_RANGE:
Packit Service d8d8ac
		pr_err("freq: '%s' is out of range.", cmdv[0]);
Packit Service d8d8ac
		return -2;
Packit Service d8d8ac
	default:
Packit Service d8d8ac
		pr_err("freq: couldn't process '%s'", cmdv[0]);
Packit Service d8d8ac
		return -2;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	clockadj_set_freq(clkid, ppb);
Packit Service d8d8ac
	pr_err("adjusted clock frequency offset to %lfppb", ppb);
Packit Service d8d8ac
Packit Service d8d8ac
	/* consumed one argument to determine the frequency adjustment value */
Packit Service d8d8ac
	return 1;
Packit Service d8d8ac
}
Packit Service d8d8ac
Packit Service d8d8ac
static int do_caps(clockid_t clkid, int cmdc, char *cmdv[])
Packit Service d8d8ac
{
Packit Service d8d8ac
	struct ptp_clock_caps caps;
Packit Service d8d8ac
Packit Service d8d8ac
	if (clkid == CLOCK_REALTIME) {
Packit Service d8d8ac
		pr_warning("CLOCK_REALTIME is not a PHC device.");
Packit Service d8d8ac
		return 0;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	if (ioctl(CLOCKID_TO_FD(clkid), PTP_CLOCK_GETCAPS, &caps)) {
Packit Service d8d8ac
		pr_err("get capabilities failed: %s",
Packit Service d8d8ac
			strerror(errno));
Packit Service d8d8ac
		return -1;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	pr_notice("\n"
Packit Service d8d8ac
		"capabilities:\n"
Packit Service d8d8ac
		"  %d maximum frequency adjustment (ppb)\n"
Packit Service d8d8ac
		"  %d programable alarms\n"
Packit Service d8d8ac
		"  %d external time stamp channels\n"
Packit Service d8d8ac
		"  %d programmable periodic signals\n"
Packit Service d8d8ac
		"  %s pulse per second support",
Packit Service d8d8ac
		caps.max_adj,
Packit Service d8d8ac
		caps.n_alarm,
Packit Service d8d8ac
		caps.n_ext_ts,
Packit Service d8d8ac
		caps.n_per_out,
Packit Service d8d8ac
		caps.pps ? "has" : "doesn't have");
Packit Service d8d8ac
	return 0;
Packit Service d8d8ac
}
Packit Service d8d8ac
Packit Service d8d8ac
static int do_cmp(clockid_t clkid, int cmdc, char *cmdv[])
Packit Service d8d8ac
{
Packit Service d8d8ac
	struct timespec ts, rta, rtb;
Packit Service d8d8ac
	int64_t sys_offset, delay = 0, offset;
Packit Service d8d8ac
	uint64_t sys_ts;
Packit Service d8d8ac
Packit Service d8d8ac
	if (SYSOFF_SUPPORTED ==
Packit Service d8d8ac
	    sysoff_measure(CLOCKID_TO_FD(clkid),
Packit Service d8d8ac
			   9, &sys_offset, &sys_ts, &delay)) {
Packit Service d8d8ac
		pr_notice( "offset from CLOCK_REALTIME is %"PRId64"ns\n",
Packit Service d8d8ac
			sys_offset);
Packit Service d8d8ac
		return 0;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	memset(&ts, 0, sizeof(ts));
Packit Service d8d8ac
	memset(&ts, 0, sizeof(rta));
Packit Service d8d8ac
	memset(&ts, 0, sizeof(rtb));
Packit Service d8d8ac
	if (clock_gettime(CLOCK_REALTIME, &rta) ||
Packit Service d8d8ac
	    clock_gettime(clkid, &ts) ||
Packit Service d8d8ac
	    clock_gettime(CLOCK_REALTIME, &rtb)) {
Packit Service d8d8ac
		pr_err("cmp: failed clock reads: %s\n",
Packit Service d8d8ac
			strerror(errno));
Packit Service d8d8ac
		return -1;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	offset = calculate_offset(&rta, &ts, &rtb;;
Packit Service d8d8ac
	pr_notice( "offset from CLOCK_REALTIME is approximately %"PRId64"ns\n",
Packit Service d8d8ac
		offset);
Packit Service d8d8ac
Packit Service d8d8ac
	return 0;
Packit Service d8d8ac
}
Packit Service d8d8ac
Packit Service d8d8ac
static int do_wait(clockid_t clkid, int cmdc, char *cmdv[])
Packit Service d8d8ac
{
Packit Service d8d8ac
	double time_arg;
Packit Service d8d8ac
	struct timespec ts;
Packit Service d8d8ac
	struct itimerval timer;
Packit Service d8d8ac
	enum parser_result r;
Packit Service d8d8ac
Packit Service d8d8ac
	if (cmdc < 1 || name_is_a_command(cmdv[0])) {
Packit Service d8d8ac
		pr_err("wait: requires sleep duration argument\n");
Packit Service d8d8ac
		return -2;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	memset(&timer, 0, sizeof(timer));
Packit Service d8d8ac
Packit Service d8d8ac
	/* parse the double time offset argument */
Packit Service d8d8ac
	r = get_ranged_double(cmdv[0], &time_arg, 0.0, DBL_MAX);
Packit Service d8d8ac
	switch (r) {
Packit Service d8d8ac
	case PARSED_OK:
Packit Service d8d8ac
		break;
Packit Service d8d8ac
	case MALFORMED:
Packit Service d8d8ac
		pr_err("wait: '%s' is not a valid double", cmdv[0]);
Packit Service d8d8ac
		return -2;
Packit Service d8d8ac
	case OUT_OF_RANGE:
Packit Service d8d8ac
		pr_err("wait: '%s' is out of range.", cmdv[0]);
Packit Service d8d8ac
		return -2;
Packit Service d8d8ac
	default:
Packit Service d8d8ac
		pr_err("wait: couldn't process '%s'", cmdv[0]);
Packit Service d8d8ac
		return -2;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	double_to_timespec(time_arg, &ts);
Packit Service d8d8ac
	timer.it_value.tv_sec = ts.tv_sec;
Packit Service d8d8ac
	timer.it_value.tv_usec = ts.tv_nsec / 1000;
Packit Service d8d8ac
	setitimer(ITIMER_REAL, &timer, NULL);
Packit Service d8d8ac
	pause();
Packit Service d8d8ac
Packit Service d8d8ac
	/* the SIGALRM is already trapped during initialization, so we will
Packit Service d8d8ac
	 * wake up here once the alarm is handled.
Packit Service d8d8ac
	 */
Packit Service d8d8ac
	pr_notice( "process slept for %lf seconds\n", time_arg);
Packit Service d8d8ac
Packit Service d8d8ac
	return 1;
Packit Service d8d8ac
}
Packit Service d8d8ac
Packit Service d8d8ac
static const struct cmd_t all_commands[] = {
Packit Service d8d8ac
	{ "set", &do_set },
Packit Service d8d8ac
	{ "get", &do_get },
Packit Service d8d8ac
	{ "adj", &do_adj },
Packit Service d8d8ac
	{ "freq", &do_freq },
Packit Service d8d8ac
	{ "cmp", &do_cmp },
Packit Service d8d8ac
	{ "caps", &do_caps },
Packit Service d8d8ac
	{ "wait", &do_wait },
Packit Service d8d8ac
	{ 0, 0 }
Packit Service d8d8ac
};
Packit Service d8d8ac
Packit Service d8d8ac
static cmd_func_t get_command_function(const char *name)
Packit Service d8d8ac
{
Packit Service d8d8ac
	int i;
Packit Service d8d8ac
	cmd_func_t cmd = NULL;
Packit Service d8d8ac
Packit Service d8d8ac
	for (i = 0; all_commands[i].name != NULL; i++) {
Packit Service d8d8ac
		if (!strncmp(name,
Packit Service d8d8ac
			     all_commands[i].name,
Packit Service d8d8ac
			     strlen(all_commands[i].name)))
Packit Service d8d8ac
			cmd = all_commands[i].function;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	return cmd;
Packit Service d8d8ac
}
Packit Service d8d8ac
Packit Service d8d8ac
static inline int name_is_a_command(const char *name)
Packit Service d8d8ac
{
Packit Service d8d8ac
	return get_command_function(name) != NULL;
Packit Service d8d8ac
}
Packit Service d8d8ac
Packit Service d8d8ac
static int run_cmds(clockid_t clkid, int cmdc, char *cmdv[])
Packit Service d8d8ac
{
Packit Service d8d8ac
	int i = 0, result = 0;
Packit Service d8d8ac
	cmd_func_t action = NULL;
Packit Service d8d8ac
Packit Service d8d8ac
	while (i < cmdc) {
Packit Service d8d8ac
		char *arg = cmdv[i];
Packit Service d8d8ac
Packit Service d8d8ac
		/* increment now to remove the command argument */
Packit Service d8d8ac
		i++;
Packit Service d8d8ac
Packit Service d8d8ac
		action = get_command_function(arg);
Packit Service d8d8ac
		if (action)
Packit Service d8d8ac
			result = action(clkid, cmdc - i, &cmdv[i]);
Packit Service d8d8ac
		else
Packit Service d8d8ac
			pr_err("unknown command %s.", arg);
Packit Service d8d8ac
Packit Service d8d8ac
		/* result is how many arguments were used up by the command,
Packit Service d8d8ac
		 * not including the ";". We will increment the loop counter
Packit Service d8d8ac
		 * to avoid processing the arguments as commands.
Packit Service d8d8ac
		 */
Packit Service d8d8ac
		if (result < 0)
Packit Service d8d8ac
			return result;
Packit Service d8d8ac
		else
Packit Service d8d8ac
			i += result;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	return 0;
Packit Service d8d8ac
}
Packit Service d8d8ac
Packit Service d8d8ac
int main(int argc, char *argv[])
Packit Service d8d8ac
{
Packit Service d8d8ac
	const char *progname;
Packit Service d8d8ac
	char **cmdv, *default_cmdv[] = { "caps" };
Packit Service d8d8ac
	int c, result, cmdc;
Packit Service d8d8ac
	int print_level = LOG_INFO, verbose = 1, use_syslog = 1;
Packit Service d8d8ac
	clockid_t clkid;
Packit Service d8d8ac
Packit Service d8d8ac
	install_handler(SIGALRM, handle_alarm);
Packit Service d8d8ac
Packit Service d8d8ac
	/* Process the command line arguments. */
Packit Service d8d8ac
	progname = strrchr(argv[0], '/');
Packit Service d8d8ac
	progname = progname ? 1+progname : argv[0];
Packit Service d8d8ac
	while (EOF != (c = getopt(argc, argv,
Packit Service d8d8ac
				  "l:qQvh"))) {
Packit Service d8d8ac
		switch (c) {
Packit Service d8d8ac
		case 'l':
Packit Service d8d8ac
			if (get_arg_val_i(c, optarg, &print_level,
Packit Service d8d8ac
					  PRINT_LEVEL_MIN, PRINT_LEVEL_MAX))
Packit Service d8d8ac
				return -1;
Packit Service d8d8ac
			break;
Packit Service d8d8ac
		case 'q':
Packit Service d8d8ac
			use_syslog = 0;
Packit Service d8d8ac
			break;
Packit Service d8d8ac
		case 'Q':
Packit Service d8d8ac
			verbose = 0;
Packit Service d8d8ac
			break;
Packit Service d8d8ac
		case 'v':
Packit Service d8d8ac
			version_show(stdout);
Packit Service d8d8ac
			return 0;
Packit Service d8d8ac
		case 'h':
Packit Service d8d8ac
			usage(progname);
Packit Service d8d8ac
			return 0;
Packit Service d8d8ac
		default:
Packit Service d8d8ac
			usage(progname);
Packit Service d8d8ac
			return -1;
Packit Service d8d8ac
		}
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	print_set_progname(progname);
Packit Service d8d8ac
	print_set_verbose(verbose);
Packit Service d8d8ac
	print_set_syslog(use_syslog);
Packit Service d8d8ac
	print_set_level(print_level);
Packit Service d8d8ac
Packit Service d8d8ac
	if ((argc - optind) < 1) {
Packit Service d8d8ac
		usage(progname);
Packit Service d8d8ac
		return -1;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	if ((argc - optind) == 1) {
Packit Service d8d8ac
		cmdv = default_cmdv;
Packit Service d8d8ac
		cmdc = 1;
Packit Service d8d8ac
	} else {
Packit Service d8d8ac
		cmdv = &argv[optind+1];
Packit Service d8d8ac
		cmdc = argc - optind - 1;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	clkid = clock_open(argv[optind]);
Packit Service d8d8ac
	if (clkid == CLOCK_INVALID)
Packit Service d8d8ac
		return -1;
Packit Service d8d8ac
Packit Service d8d8ac
	/* pass the remaining arguments to the run_cmds loop */
Packit Service d8d8ac
	result = run_cmds(clkid, cmdc, cmdv);
Packit Service d8d8ac
	if (result < -1) {
Packit Service d8d8ac
		/* show usage when command fails */
Packit Service d8d8ac
		usage(progname);
Packit Service d8d8ac
		return result;
Packit Service d8d8ac
	}
Packit Service d8d8ac
Packit Service d8d8ac
	return 0;
Packit Service d8d8ac
}