/* $NetBSD:$ */ /* * Copyright (c) 2000 Jonathan Stone (hereinafter referred to as the author) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jonathan Stone for * the NetBSD project. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include void ppstest __P((const char *PPSfilename)); void usage __P((void)); int main(int argc, char **argv) { const char *devname; struct stat st; if (argc != 2) { usage(); } devname = argv[1]; if (stat(devname, &st) < 0) { perror("ppstest: stat'ing device special file"); exit(EX_NOINPUT); } if ((st.st_mode & S_IFMT) != S_IFCHR) { fprintf(stderr, "ppstest: %s is not a device special file\n", devname); usage(); } ppstest(argv[1]); exit(0); } void usage() { fprintf(stderr, "usage: testpps \n"); exit(EX_USAGE); } void ppstest(PPSfilename) const char *PPSfilename; { int fd; pps_handle_t handle; /* handle for PPS sources */ pps_params_t params; /* selection of events, options */ pps_info_t infobuf; /* status of events */ struct timespec timeout; /* waiting time */ int mode; /* supported mode bits */ int status; /* status bits */ int capturemode; u_long assert_seq, clear_seq; int first; /* Open a file descriptor and enable PPS on rising edges */ fd = open(PPSfilename, O_RDWR, 0); if (fd < 0) { fprintf(stderr, "cannot open %s\n", PPSfilename); exit(1); } status = time_pps_create(fd, &handle); if (status < 0) { perror("ppstest: time_pps_create()"); exit(2); } status = time_pps_getcap(handle, &mode); if (status < 0) { perror("ppstest setup: time_pps_getcap() failed"); exit(2); } /* Complain if the implementation cannot capture ASSERT events. */ if ((mode & PPS_CAPTUREASSERT) == 0) { fprintf(stderr, "ppstest: %s cannot CAPTUREASSERT; mode=%#04x\n", PPSfilename, mode); exit(1); } /* * Fetch parameters from kernel, set the desired capture mode, * then write those parameters backto the kernel. * Always capture assert. Capture clear too, if the * implementation supports it. * (simple parallel-port setups only support one edge.) */ capturemode = mode & (PPS_CAPTUREASSERT|PPS_CAPTURECLEAR); status = time_pps_getparams(handle, ¶ms); if (status < 0) { perror("ppstest setup: time_pps_getparams() failed"); exit(2); } params.mode |= capturemode; params.mode |= PPS_TSFMT_TSPEC; /* use ``struct timespec'' format */ status = time_pps_setparams(handle, ¶ms); if (status < 0) { perror("ppstest setup: time_pps_setparams() failed"); exit(2); } /* create a zero-valued timeout */ timeout.tv_sec = 0; timeout.tv_nsec = 0; status = time_pps_fetch(handle, PPS_TSFMT_TSPEC, &infobuf, &timeout); if (status < 0) { perror("ppstest: initial: time_pps_fetch() failed"); } first = 1; /* loop, printing the most recent timestamp every second or so */ for (first = 1; ;first = 0) { /* * Poll at 1 sec - epsilon intervals, to allow for * scheduling vagaries and clock drift. * (NB: Nyquist's theorem does not apply to discrete events.) */ usleep(900000); status = time_pps_fetch(handle, PPS_TSFMT_TSPEC, &infobuf, &timeout); if (status < 0) { perror("ppstest loop: time_pps_fetch() failed"); } /* * If first time round the loop, print sequencenumbers. * thereafter, print the timestamp(s) whose sequence-number * changed since last printed. If we see both assert * and clear events in one iteration, print the timestamps * in sequence-number order. (The loop may be out of phase. * Or, since this is a diagnostic utility, the kernel may * have dropped a tick.) */ /* first clear timestamp, or new and earlier than assert? */ if ((mode & PPS_CAPTURECLEAR) && (first || clear_seq != infobuf.clear_sequence) && infobuf.clear_sequence < infobuf.assert_sequence) { printf("Clear timestamp: %d.%09d, sequence: %ld", (int)infobuf.clear_timestamp.tv_sec, (int)infobuf.clear_timestamp.tv_nsec, (u_long) infobuf.clear_sequence); if (!first && clear_seq+1 != infobuf.clear_sequence) printf(" (dropped %d)", infobuf.assert_sequence - clear_seq); putchar('\n'); clear_seq = infobuf.clear_sequence; } /* first or new assert timestamp? */ if ((mode & PPS_CAPTURECLEAR) && (first || assert_seq != infobuf.assert_sequence)) { printf("Assert timestamp: %d.%09d, sequence: %ld", (int)infobuf.assert_timestamp.tv_sec, (int)infobuf.assert_timestamp.tv_nsec, (u_long) infobuf.assert_sequence); if (!first && assert_seq+1 != infobuf.assert_sequence) printf(" (dropped %d)", infobuf.assert_sequence - assert_seq); putchar('\n'); assert_seq = infobuf.assert_sequence; } /* first or new clear timestamp, and later than assert? */ if ((mode & PPS_CAPTURECLEAR) && (first || clear_seq != infobuf.clear_sequence) && infobuf.clear_sequence >= infobuf.assert_sequence) { printf("Clear timestamp: %d.%09d, sequence: %ld", (int)infobuf.clear_timestamp.tv_sec, (int)infobuf.clear_timestamp.tv_nsec, (u_long) infobuf.clear_sequence); if (!first && clear_seq + 1 != infobuf.clear_sequence) printf(" (dropped %d)", infobuf.clear_sequence - clear_seq); putchar('\n'); clear_seq = infobuf.clear_sequence; } first = 0; } }