/* * Written by Tzafrir Cohen * 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 #include #include #include #include #include #include #include #include #include #include #include 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