1 /*
2  * Watch a specified serial port for transitions that might be 1PPS.
3  *
4  * Each output line is the second and nanosecond parts of a timestamp
5  * followed by the names of handshake signals then asserted.  Off
6  * transitions may generate lines with no signals aserted.
7  *
8  * If you don't see output within a second, use gpsmon or some other
9  * equivalent tool to check that your device has satellite lock and is
10  * getting fixes before giving up on the possibility of 1PPS.
11  *
12  * Also, check your cable. Cheap DB9 to DB9 cables such as those
13  * issued with UPSes often carry TXD/RXD/GND only, omitting handshake
14  * lines such as DCD.  Suspect this especially if the cable jacket
15  * looks too skinny to hold more than three leads!
16  *
17  * This code requires only ANSI/POSIX. If it doesn't compile and run
18  * on your Unix there is something very wrong with your Unix.
19  *
20  * This code by ESR, Copyright (C) 2013, under BSD terms.
21  * This file is Copyright (c)2013-2018 by the GPSD project
22  * SPDX-License-Identifier: BSD-2-clause
23  */
24 
25 #include "gpsd_config.h"  /* must be before all includes */
26 
27 #include <errno.h>
28 #include <fcntl.h>	/* needed for open() and friends */
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/ioctl.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <time.h>
36 #include <unistd.h>
37 
38 #include "revision.h"
39 #include "timespec.h"
40 
41 struct assoc {
42     int mask;
43     char *string;
44 };
45 
46 /*
47  * Possible pins for PPS: DCD, CTS, RI, DSR. Pinouts:
48  *
49  * DB9  DB25  Name      Full name
50  * ---  ----  ----      --------------------
51  *  3     2    TXD  --> Transmit Data
52  *  2     3    RXD  <-- Receive Data
53  *  7     4    RTS  --> Request To Send
54  *  8     5    CTS  <-- Clear To Send
55  *  6     6    DSR  <-- Data Set Ready
56  *  4    20    DTR  --> Data Terminal Ready
57  *  1     8    DCD  <-- Data Carrier Detect
58  *  9    22    RI   <-- Ring Indicator
59  *  5     7    GND      Signal ground
60  *
61  * Note that it only makes sense to wait on handshake lines
62  * activated from the receive side (DCE->DTE) here; in this
63  * context "DCE" is the GPS. {CD,RI,CTS,DSR} is the
64  * entire set of these.
65  */
66 static const struct assoc hlines[] = {
67     {TIOCM_CD, "TIOCM_CD"},
68     {TIOCM_RI, "TIOCM_RI"},
69     {TIOCM_DSR, "TIOCM_DSR"},
70     {TIOCM_CTS, "TIOCM_CTS"},
71 };
72 
usage(void)73 static void usage(void)
74 {
75 	(void)fprintf(stderr, "usage: ppscheck [-h] [ -V] <device>\n");
76 	(void)fprintf(stderr, "                 -h   print usage\n");
77 	(void)fprintf(stderr, "                 -V   print cwVersion\n");
78 	exit(1);
79 }
80 
main(int argc,char * argv[])81 int main(int argc, char *argv[])
82 {
83     struct timespec ts;
84     int fd;
85     int c;
86     char ts_buf[TIMESPEC_LEN];
87 
88     while((c = getopt(argc, argv, "hV")) != -1) {
89 	    switch(c){
90 	    case 'h':
91 	    default:
92 		usage();
93 		exit(0);
94 	    case 'V':
95 		(void)printf("%s: %s\n", argv[0], REVISION);
96 		exit(EXIT_SUCCESS);
97 	    }
98     }
99     argc -= optind;
100     argv += optind;
101 
102     if (argc != 1)
103 	    usage();
104 
105     fd = open(argv[0], O_RDONLY);
106 
107     if (fd == -1) {
108 	(void)fprintf(stderr,
109 		      "open(%s) failed: %d %.40s\n",
110 		      argv[0], errno, strerror(errno));
111 	exit(1);
112     }
113 
114     (void)fprintf(stdout, "# Seconds  nanoSecs   Signals\n");
115     for (;;) {
116 	if (ioctl(fd, TIOCMIWAIT, TIOCM_CD|TIOCM_DSR|TIOCM_RI|TIOCM_CTS) != 0) {
117 	    (void)fprintf(stderr,
118 			  "PPS ioctl(TIOCMIWAIT) failed: %d %.40s\n",
119 			  errno, strerror(errno));
120 	    break;
121 	} else {
122 	    const struct assoc *sp;
123 	    int handshakes;
124 
125 	    (void)clock_gettime(CLOCK_REALTIME, &ts);
126 	    (void)ioctl(fd, TIOCMGET, &handshakes);
127 	    (void)fprintf(stdout, "%s",
128                           timespec_str(&ts, ts_buf, sizeof(ts_buf)));
129 	    for (sp = hlines;
130 		 sp < hlines + sizeof(hlines)/sizeof(hlines[0]);
131 		 sp++)
132 		if ((handshakes & sp->mask) != 0) {
133 		    (void)fputc(' ', stdout);
134 		    (void)fputs(sp->string, stdout);
135 		}
136 	    (void)fputc('\n', stdout);
137 	}
138     }
139 
140     exit(EXIT_SUCCESS);
141 }
142 
143 /* end */
144