diff options
Diffstat (limited to 'xpp/utils/adj_clock.c')
-rw-r--r-- | xpp/utils/adj_clock.c | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/xpp/utils/adj_clock.c b/xpp/utils/adj_clock.c new file mode 100644 index 0000000..6dfa483 --- /dev/null +++ b/xpp/utils/adj_clock.c @@ -0,0 +1,228 @@ +/* + * Written by Tzafrir Cohen <tzafrir.cohen@xorcom.com> + * Copyright (C) 2006, Xorcom + * + * Derived from zttest.c + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include <sys/timex.h> +#include <linux/param.h> +#include <syslog.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/time.h> +#include <sys/signal.h> +#include <math.h> + +static const char *rcsid = "$Id$"; + +#define FREQ_PER_TICK (64*1024) +#define USEC_PER_ZAP_TICK 1000 /* Zaptel tick in usec */ +#define USER_HZ 100 +#define SYNC_TOLERANCE 100 +#define DEFAULT_INTERVAL 60 + +static const char *program; +static int synced_time; /* time (seconds) we are so far synced */ +static int verbose = 0; +static int sync_tolerance = 100; + +void usage() { + fprintf(stderr, "%s: synchronize system clock from zaptel clock.\n" + "Version: %s\n" + "\n" + "Usage: %s [ -c COUNT ] [-i INTERVAL] [ -t PERIOD ] [-v]\n" + " %s -h\n" + "\n" + "Options:\n" + " -c only run COUNT cycles of synchronization (default: forever).\n" + " -i set cycle Interval to INTERVAL (in seconds. Default: %d).\n" + " -t sync tolerance to PERIOD (in usec. Default: %d). For debugging.\n" + " -v Be more Verbose\n" + " -h Print this Help text and exit.\n" + "", program, rcsid, program, program, DEFAULT_INTERVAL, + sync_tolerance); +} + +/* based on zttest.c */ + +/* + * Print a message to the log according to the value of synced_time + * + * All parameters are for the "Not Synced" warning message. + * TODO: Find a better way. + */ +void status_message(void) { + if (synced_time == 0) + syslog(LOG_NOTICE, "Not Synced.\n"); + else + syslog(LOG_INFO, "Good Sync for %d seconds\n", synced_time); + + return; +} + +/* + * interval: period, in seconds + * diff_usec: Current offset, in micro-seconds. + */ +int clock_sync(int interval, int diff_usec) +{ + struct timex cur_time = {.modes=0}; /* just query */ + int diff_tick; /* Difference in adjtimex ticks */ + int req_tick; /* Required adjtimex tick value */ + long long diff_freq; + long long req_freq; + int usec_left; /* Usec adjusted by frequency */ + int ret; + + ret = adjtimex(&cur_time); + + diff_tick = diff_usec / (interval * USER_HZ); + usec_left = diff_usec - diff_tick * (interval * USER_HZ); + diff_freq = usec_left * FREQ_PER_TICK / interval; + + req_tick = cur_time.tick - diff_tick; + req_freq = cur_time.freq - diff_freq; + + if(!diff_tick && abs(usec_left) < sync_tolerance) + synced_time += interval; + else + synced_time = 0; + status_message(); + if (verbose || !synced_time) + syslog(LOG_INFO, "interval: %d, diff_tick: %d, diff_usec: %d, usec_left: %d.\n", + interval, diff_tick, diff_usec, usec_left); + + /* set the clock rate */ + cur_time.freq = req_freq; + cur_time.tick = req_tick; + cur_time.modes = ADJ_TICK | ADJ_FREQUENCY; + ret = adjtimex(&cur_time); + if (ret < 0) + syslog(LOG_ERR, "Adjtimex failed to set frequency. New frequency: %lld. Error: %d (%s)\n", + req_freq, errno, strerror(errno)); + return 0; +} + +static int pass = 0; + +void hup_handler(int sig) +{ + if (verbose > 0) { + syslog(LOG_INFO, "--- Results after %d passes ---\n", pass); + status_message(); + } + closelog(); + exit(0); +} + +void sigusr1_handler(int sig) { + verbose++; + syslog(LOG_INFO, "Increased verbosity to %d (increase: SIGUSR1, reset: SIGUSR2).\n", verbose); + status_message(); +} + +void sigusr2_handler(int sig) { + verbose = 0; + syslog(LOG_INFO, "Set verbosity to 0 (increase: SIGUSR1, reset: SIGUSR2).\n"); +} + +void sync_cycle(int fd, int interval) +{ + char buf[1024]; /* TODO: why 1024 and not 1000? */ + struct timeval start; + struct timeval end; + long long usec; + int count = 0; + int i; + int res; + + gettimeofday(&start, NULL); + for (i=0; i < interval*8; i++) { + res = read(fd, buf, sizeof(buf)); + if (res < 0) { + syslog(LOG_ERR, "Failed to read from pseudo interface: %s\n", strerror(errno)); + exit(1); + } + count += res/8; + } + gettimeofday(&end, NULL); + + /* time difference in microseconds: */ + usec = (end.tv_sec - start.tv_sec) * 1000000; + usec += end.tv_usec - start.tv_usec; + + clock_sync(interval, usec - (count * USEC_PER_ZAP_TICK)); + + pass++; +} + +int main(int argc, char *argv[]) +{ + int fd; + int opt; + int interval = DEFAULT_INTERVAL; + int cycles_to_run = 0; + int i; + + program = argv[0]; + while((opt = getopt(argc, argv, "c:hi:t:v")) != -1) { + switch (opt) { + case 'h': usage(); exit(0); break; + case 'c': cycles_to_run = atoi(optarg); break; + case 'i': interval = atoi(optarg); break; /* number of seconds */ + case 't': sync_tolerance = atoi(optarg); break; /* usec */ + case 'v': verbose++; break; + default: + fprintf(stderr, "%s: Unknown option: %c. Aborting\n", argv[0], opt); + usage(); + exit(1); + } + } + + openlog(program, LOG_PERROR | LOG_PID, LOG_DAEMON); + fd = open("/dev/zap/pseudo", O_RDWR); + if (fd < 0) { + fprintf(stderr, "Unable to open zap interface: %s\n", strerror(errno)); + exit(1); + } + if (verbose >=1) + syslog(LOG_INFO, "Opened pseudo zap interface, measuring accuracy...\n"); + + signal(SIGHUP, hup_handler); + signal(SIGINT, hup_handler); + signal(SIGUSR1, sigusr1_handler); + signal(SIGUSR2, sigusr2_handler); + + if (cycles_to_run > 0) { + for(i=0; i<cycles_to_run; i++) { + sync_cycle(fd, interval); + } + hup_handler(0); /* exit */; + } + + /* else (option -c not provided): run forever. */ + for(;;) { + sync_cycle(fd, interval); + } +} |