| @@ -1,938 +1,938 @@ | | | @@ -1,938 +1,938 @@ |
1 | /* $NetBSD: refclock_acts.c,v 1.1.1.1 2009/12/13 16:55:43 kardel Exp $ */ | | 1 | /* $NetBSD: refclock_acts.c,v 1.2 2009/12/14 00:42:21 christos Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time | | 4 | * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time |
5 | * Services | | 5 | * Services |
6 | */ | | 6 | */ |
7 | #ifdef HAVE_CONFIG_H | | 7 | #ifdef HAVE_CONFIG_H |
8 | #include <config.h> | | 8 | #include <config.h> |
9 | #endif | | 9 | #endif |
10 | | | 10 | |
11 | #if defined(REFCLOCK) && (defined(CLOCK_ACTS) || defined(CLOCK_PTBACTS)) | | 11 | #if defined(REFCLOCK) && (defined(CLOCK_ACTS) || defined(CLOCK_PTBACTS)) |
12 | | | 12 | |
13 | #include "ntpd.h" | | 13 | #include "ntpd.h" |
14 | #include "ntp_io.h" | | 14 | #include "ntp_io.h" |
15 | #include "ntp_unixtime.h" | | 15 | #include "ntp_unixtime.h" |
16 | #include "ntp_refclock.h" | | 16 | #include "ntp_refclock.h" |
17 | #include "ntp_stdlib.h" | | 17 | #include "ntp_stdlib.h" |
18 | #include "ntp_control.h" | | 18 | #include "ntp_control.h" |
19 | | | 19 | |
20 | #include <stdio.h> | | 20 | #include <stdio.h> |
21 | #include <ctype.h> | | 21 | #include <ctype.h> |
22 | #ifdef HAVE_SYS_IOCTL_H | | 22 | #ifdef HAVE_SYS_IOCTL_H |
23 | # include <sys/ioctl.h> | | 23 | # include <sys/ioctl.h> |
24 | #endif /* HAVE_SYS_IOCTL_H */ | | 24 | #endif /* HAVE_SYS_IOCTL_H */ |
25 | | | 25 | |
26 | /* | | 26 | /* |
27 | * This driver supports the US (NIST, USNO) and European (PTB, NPL, | | 27 | * This driver supports the US (NIST, USNO) and European (PTB, NPL, |
28 | * etc.) modem time services, as well as Spectracom GPS and WWVB | | 28 | * etc.) modem time services, as well as Spectracom GPS and WWVB |
29 | * receivers connected via a modem. The driver periodically dials a | | 29 | * receivers connected via a modem. The driver periodically dials a |
30 | * number from a telephone list, receives the timecode data and | | 30 | * number from a telephone list, receives the timecode data and |
31 | * calculates the local clock correction. It is designed primarily for | | 31 | * calculates the local clock correction. It is designed primarily for |
32 | * use as backup when neither a radio clock nor connectivity to Internet | | 32 | * use as backup when neither a radio clock nor connectivity to Internet |
33 | * time servers is available. | | 33 | * time servers is available. |
34 | * | | 34 | * |
35 | * This driver requires a modem with a Hayes-compatible command set and | | 35 | * This driver requires a modem with a Hayes-compatible command set and |
36 | * control over the modem data terminal ready (DTR) control line. The | | 36 | * control over the modem data terminal ready (DTR) control line. The |
37 | * modem setup string is hard-coded in the driver and may require | | 37 | * modem setup string is hard-coded in the driver and may require |
38 | * changes for nonstandard modems or special circumstances. For reasons | | 38 | * changes for nonstandard modems or special circumstances. For reasons |
39 | * unrelated to this driver, the data set ready (DSR) control line | | 39 | * unrelated to this driver, the data set ready (DSR) control line |
40 | * should not be set when this driver is first started. | | 40 | * should not be set when this driver is first started. |
41 | * | | 41 | * |
42 | * The calling program is initiated by setting fudge flag1, either | | 42 | * The calling program is initiated by setting fudge flag1, either |
43 | * manually or automatically. When flag1 is set, the calling program | | 43 | * manually or automatically. When flag1 is set, the calling program |
44 | * dials the first number in the phone command of the configuration | | 44 | * dials the first number in the phone command of the configuration |
45 | * file. If that call fails, the calling program dials the second number | | 45 | * file. If that call fails, the calling program dials the second number |
46 | * and so on. The number is specified by the Hayes ATDT prefix followed | | 46 | * and so on. The number is specified by the Hayes ATDT prefix followed |
47 | * by the number itself, including the prefix and long-distance digits | | 47 | * by the number itself, including the prefix and long-distance digits |
48 | * and delay code, if necessary. The flag1 is reset and the calling | | 48 | * and delay code, if necessary. The flag1 is reset and the calling |
49 | * program terminated if (a) a valid clock update has been determined, | | 49 | * program terminated if (a) a valid clock update has been determined, |
50 | * (b) no more numbers remain in the list, (c) a device fault or timeout | | 50 | * (b) no more numbers remain in the list, (c) a device fault or timeout |
51 | * occurs or (d) fudge flag1 is reset manually. | | 51 | * occurs or (d) fudge flag1 is reset manually. |
52 | * | | 52 | * |
53 | * The driver is transparent to each of the modem time services and | | 53 | * The driver is transparent to each of the modem time services and |
54 | * Spectracom radios. It selects the parsing algorithm depending on the | | 54 | * Spectracom radios. It selects the parsing algorithm depending on the |
55 | * message length. There is some hazard should the message be corrupted. | | 55 | * message length. There is some hazard should the message be corrupted. |
56 | * However, the data format is checked carefully and only if all checks | | 56 | * However, the data format is checked carefully and only if all checks |
57 | * succeed is the message accepted. Corrupted lines are discarded | | 57 | * succeed is the message accepted. Corrupted lines are discarded |
58 | * without complaint. | | 58 | * without complaint. |
59 | * | | 59 | * |
60 | * Fudge controls | | 60 | * Fudge controls |
61 | * | | 61 | * |
62 | * flag1 force a call in manual mode | | 62 | * flag1 force a call in manual mode |
63 | * flag2 enable port locking (not verified) | | 63 | * flag2 enable port locking (not verified) |
64 | * flag3 no modem; port is directly connected to device | | 64 | * flag3 no modem; port is directly connected to device |
65 | * flag4 not used | | 65 | * flag4 not used |
66 | * | | 66 | * |
67 | * time1 offset adjustment (s) | | 67 | * time1 offset adjustment (s) |
68 | * | | 68 | * |
69 | * Ordinarily, the serial port is connected to a modem; however, it can | | 69 | * Ordinarily, the serial port is connected to a modem; however, it can |
70 | * be connected directly to a device or another computer for testing and | | 70 | * be connected directly to a device or another computer for testing and |
71 | * calibration. In this case set fudge flag3 and the driver will send a | | 71 | * calibration. In this case set fudge flag3 and the driver will send a |
72 | * single character 'T' at each poll event. In principle, fudge flag2 | | 72 | * single character 'T' at each poll event. In principle, fudge flag2 |
73 | * enables port locking, allowing the modem to be shared when not in use | | 73 | * enables port locking, allowing the modem to be shared when not in use |
74 | * by this driver. At least on Solaris with the current NTP I/O | | 74 | * by this driver. At least on Solaris with the current NTP I/O |
75 | * routines, this results only in lots of ugly error messages. | | 75 | * routines, this results only in lots of ugly error messages. |
76 | */ | | 76 | */ |
77 | /* | | 77 | /* |
78 | * National Institute of Science and Technology (NIST) | | 78 | * National Institute of Science and Technology (NIST) |
79 | * | | 79 | * |
80 | * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii) | | 80 | * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii) |
81 | * | | 81 | * |
82 | * Data Format | | 82 | * Data Format |
83 | * | | 83 | * |
84 | * National Institute of Standards and Technology | | 84 | * National Institute of Standards and Technology |
85 | * Telephone Time Service, Generator 3B | | 85 | * Telephone Time Service, Generator 3B |
86 | * Enter question mark "?" for HELP | | 86 | * Enter question mark "?" for HELP |
87 | * D L D | | 87 | * D L D |
88 | * MJD YR MO DA H M S ST S UT1 msADV <OTM> | | 88 | * MJD YR MO DA H M S ST S UT1 msADV <OTM> |
89 | * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF> | | 89 | * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF> |
90 | * ... | | 90 | * ... |
91 | * | | 91 | * |
92 | * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is | | 92 | * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is |
93 | * the on-time markers echoed by the driver and used by NIST to measure | | 93 | * the on-time markers echoed by the driver and used by NIST to measure |
94 | * and correct for the propagation delay. | | 94 | * and correct for the propagation delay. |
95 | * | | 95 | * |
96 | * US Naval Observatory (USNO) | | 96 | * US Naval Observatory (USNO) |
97 | * | | 97 | * |
98 | * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO) | | 98 | * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO) |
99 | * | | 99 | * |
100 | * Data Format (two lines, repeating at one-second intervals) | | 100 | * Data Format (two lines, repeating at one-second intervals) |
101 | * | | 101 | * |
102 | * jjjjj nnn hhmmss UTC<CR><LF> | | 102 | * jjjjj nnn hhmmss UTC<CR><LF> |
103 | * *<CR><LF> | | 103 | * *<CR><LF> |
104 | * | | 104 | * |
105 | * jjjjj modified Julian day number (not used) | | 105 | * jjjjj modified Julian day number (not used) |
106 | * nnn day of year | | 106 | * nnn day of year |
107 | * hhmmss second of day | | 107 | * hhmmss second of day |
108 | * * on-time marker for previous timecode | | 108 | * * on-time marker for previous timecode |
109 | * ... | | 109 | * ... |
110 | * | | 110 | * |
111 | * USNO does not correct for the propagation delay. A fudge time1 of | | 111 | * USNO does not correct for the propagation delay. A fudge time1 of |
112 | * about .06 s is advisable. | | 112 | * about .06 s is advisable. |
113 | * | | 113 | * |
114 | * European Services (PTB, NPL, etc.) | | 114 | * European Services (PTB, NPL, etc.) |
115 | * | | 115 | * |
116 | * PTB: +49 531 512038 (Germany) | | 116 | * PTB: +49 531 512038 (Germany) |
117 | * NPL: 0906 851 6333 (UK only) | | 117 | * NPL: 0906 851 6333 (UK only) |
118 | * | | 118 | * |
119 | * Data format (see the documentation for phone numbers and formats.) | | 119 | * Data format (see the documentation for phone numbers and formats.) |
120 | * | | 120 | * |
121 | * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500<CR><LF> | | 121 | * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500<CR><LF> |
122 | * | | 122 | * |
123 | * Spectracom GPS and WWVB Receivers | | 123 | * Spectracom GPS and WWVB Receivers |
124 | * | | 124 | * |
125 | * If a modem is connected to a Spectracom receiver, this driver will | | 125 | * If a modem is connected to a Spectracom receiver, this driver will |
126 | * call it up and retrieve the time in one of two formats. As this | | 126 | * call it up and retrieve the time in one of two formats. As this |
127 | * driver does not send anything, the radio will have to either be | | 127 | * driver does not send anything, the radio will have to either be |
128 | * configured in continuous mode or be polled by another local driver. | | 128 | * configured in continuous mode or be polled by another local driver. |
129 | */ | | 129 | */ |
130 | /* | | 130 | /* |
131 | * Interface definitions | | 131 | * Interface definitions |
132 | */ | | 132 | */ |
133 | #define DEVICE "/dev/acts%d" /* device name and unit */ | | 133 | #define DEVICE "/dev/acts%d" /* device name and unit */ |
134 | #define SPEED232 B9600 /* uart speed (9600 baud) */ | | 134 | #define SPEED232 B9600 /* uart speed (9600 baud) */ |
135 | #define PRECISION (-10) /* precision assumed (about 1 ms) */ | | 135 | #define PRECISION (-10) /* precision assumed (about 1 ms) */ |
136 | #define LOCKFILE "/var/spool/locks/LCK..cua%d" | | 136 | #define LOCKFILE "/var/spool/locks/LCK..cua%d" |
137 | #define DESCRIPTION "Automated Computer Time Service" /* WRU */ | | 137 | #define DESCRIPTION "Automated Computer Time Service" /* WRU */ |
138 | #define REFID "NONE" /* default reference ID */ | | 138 | #define REFID "NONE" /* default reference ID */ |
139 | #define MSGCNT 20 /* max message count */ | | 139 | #define MSGCNT 20 /* max message count */ |
140 | #define SMAX 256 /* max clockstats line length */ | | 140 | #define SMAX 256 /* max clockstats line length */ |
141 | #define MAXPHONE 10 /* max number of phone numbers */ | | 141 | #define MAXPHONE 10 /* max number of phone numbers */ |
142 | | | 142 | |
143 | /* | | 143 | /* |
144 | * Calling program modes | | 144 | * Calling program modes |
145 | */ | | 145 | */ |
146 | #define MODE_AUTO 0 /* automatic mode */ | | 146 | #define MODE_AUTO 0 /* automatic mode */ |
147 | #define MODE_BACKUP 1 /* backup mode */ | | 147 | #define MODE_BACKUP 1 /* backup mode */ |
148 | #define MODE_MANUAL 2 /* manual mode */ | | 148 | #define MODE_MANUAL 2 /* manual mode */ |
149 | | | 149 | |
150 | /* | | 150 | /* |
151 | * Service identifiers. | | 151 | * Service identifiers. |
152 | */ | | 152 | */ |
153 | #define REFACTS "NIST" /* NIST reference ID */ | | 153 | #define REFACTS "NIST" /* NIST reference ID */ |
154 | #define LENACTS 50 /* NIST format */ | | 154 | #define LENACTS 50 /* NIST format */ |
155 | #define REFUSNO "USNO" /* USNO reference ID */ | | 155 | #define REFUSNO "USNO" /* USNO reference ID */ |
156 | #define LENUSNO 20 /* USNO */ | | 156 | #define LENUSNO 20 /* USNO */ |
157 | #define REFPTB "PTB\0" /* PTB/NPL reference ID */ | | 157 | #define REFPTB "PTB\0" /* PTB/NPL reference ID */ |
158 | #define LENPTB 78 /* PTB/NPL format */ | | 158 | #define LENPTB 78 /* PTB/NPL format */ |
159 | #define REFWWVB "WWVB" /* WWVB reference ID */ | | 159 | #define REFWWVB "WWVB" /* WWVB reference ID */ |
160 | #define LENWWVB0 22 /* WWVB format 0 */ | | 160 | #define LENWWVB0 22 /* WWVB format 0 */ |
161 | #define LENWWVB2 24 /* WWVB format 2 */ | | 161 | #define LENWWVB2 24 /* WWVB format 2 */ |
162 | #define LF 0x0a /* ASCII LF */ | | 162 | #define LF 0x0a /* ASCII LF */ |
163 | | | 163 | |
164 | /* | | 164 | /* |
165 | * Modem setup strings. These may have to be changed for some modems. | | 165 | * Modem setup strings. These may have to be changed for some modems. |
166 | * | | 166 | * |
167 | * AT command prefix | | 167 | * AT command prefix |
168 | * B1 US answer tone | | 168 | * B1 US answer tone |
169 | * &C0 disable carrier detect | | 169 | * &C0 disable carrier detect |
170 | * &D2 hang up and return to command mode on DTR transition | | 170 | * &D2 hang up and return to command mode on DTR transition |
171 | * E0 modem command echo disabled | | 171 | * E0 modem command echo disabled |
172 | * l1 set modem speaker volume to low level | | 172 | * l1 set modem speaker volume to low level |
173 | * M1 speaker enabled until carrier detect | | 173 | * M1 speaker enabled until carrier detect |
174 | * Q0 return result codes | | 174 | * Q0 return result codes |
175 | * V1 return result codes as English words | | 175 | * V1 return result codes as English words |
176 | */ | | 176 | */ |
177 | #define MODEM_SETUP "ATB1&C0&D2E0L1M1Q0V1\r" /* modem setup */ | | 177 | #define MODEM_SETUP "ATB1&C0&D2E0L1M1Q0V1\r" /* modem setup */ |
178 | #define MODEM_HANGUP "ATH\r" /* modem disconnect */ | | 178 | #define MODEM_HANGUP "ATH\r" /* modem disconnect */ |
179 | | | 179 | |
180 | /* | | 180 | /* |
181 | * Timeouts (all in seconds) | | 181 | * Timeouts (all in seconds) |
182 | */ | | 182 | */ |
183 | #define SETUP 3 /* setup timeout */ | | 183 | #define SETUP 3 /* setup timeout */ |
184 | #define DTR 1 /* DTR timeout */ | | 184 | #define DTR 1 /* DTR timeout */ |
185 | #define ANSWER 60 /* answer timeout */ | | 185 | #define ANSWER 60 /* answer timeout */ |
186 | #define CONNECT 20 /* first valid message timeout */ | | 186 | #define CONNECT 20 /* first valid message timeout */ |
187 | #define TIMECODE 30 /* all valid messages timeout */ | | 187 | #define TIMECODE 30 /* all valid messages timeout */ |
188 | | | 188 | |
189 | /* | | 189 | /* |
190 | * State machine codes | | 190 | * State machine codes |
191 | */ | | 191 | */ |
192 | #define S_IDLE 0 /* wait for poll */ | | 192 | #define S_IDLE 0 /* wait for poll */ |
193 | #define S_OK 1 /* wait for modem setup */ | | 193 | #define S_OK 1 /* wait for modem setup */ |
194 | #define S_DTR 2 /* wait for modem DTR */ | | 194 | #define S_DTR 2 /* wait for modem DTR */ |
195 | #define S_CONNECT 3 /* wait for answer*/ | | 195 | #define S_CONNECT 3 /* wait for answer*/ |
196 | #define S_FIRST 4 /* wait for first valid message */ | | 196 | #define S_FIRST 4 /* wait for first valid message */ |
197 | #define S_MSG 5 /* wait for all messages */ | | 197 | #define S_MSG 5 /* wait for all messages */ |
198 | #define S_CLOSE 6 /* wait after sending disconnect */ | | 198 | #define S_CLOSE 6 /* wait after sending disconnect */ |
199 | | | 199 | |
200 | /* | | 200 | /* |
201 | * Unit control structure | | 201 | * Unit control structure |
202 | */ | | 202 | */ |
203 | struct actsunit { | | 203 | struct actsunit { |
204 | int unit; /* unit number */ | | 204 | int unit; /* unit number */ |
205 | int state; /* the first one was Delaware */ | | 205 | int state; /* the first one was Delaware */ |
206 | int timer; /* timeout counter */ | | 206 | int timer; /* timeout counter */ |
207 | int retry; /* retry index */ | | 207 | int retry; /* retry index */ |
208 | int msgcnt; /* count of messages received */ | | 208 | int msgcnt; /* count of messages received */ |
209 | l_fp tstamp; /* on-time timestamp */ | | 209 | l_fp tstamp; /* on-time timestamp */ |
210 | char *bufptr; /* buffer pointer */ | | 210 | char *bufptr; /* buffer pointer */ |
211 | }; | | 211 | }; |
212 | | | 212 | |
213 | /* | | 213 | /* |
214 | * Function prototypes | | 214 | * Function prototypes |
215 | */ | | 215 | */ |
216 | static int acts_start (int, struct peer *); | | 216 | static int acts_start (int, struct peer *); |
217 | static void acts_shutdown (int, struct peer *); | | 217 | static void acts_shutdown (int, struct peer *); |
218 | static void acts_receive (struct recvbuf *); | | 218 | static void acts_receive (struct recvbuf *); |
219 | static void acts_message (struct peer *); | | 219 | static void acts_message (struct peer *); |
220 | static void acts_timecode (struct peer *, char *); | | 220 | static void acts_timecode (struct peer *, char *); |
221 | static void acts_poll (int, struct peer *); | | 221 | static void acts_poll (int, struct peer *); |
222 | static void acts_timeout (struct peer *); | | 222 | static void acts_timeout (struct peer *); |
223 | static void acts_disc (struct peer *); | | 223 | static void acts_disc (struct peer *); |
224 | static void acts_timer (int, struct peer *); | | 224 | static void acts_timer (int, struct peer *); |
225 | | | 225 | |
226 | /* | | 226 | /* |
227 | * Transfer vector (conditional structure name) | | 227 | * Transfer vector (conditional structure name) |
228 | */ | | 228 | */ |
229 | struct refclock refclock_acts = { | | 229 | struct refclock refclock_acts = { |
230 | acts_start, /* start up driver */ | | 230 | acts_start, /* start up driver */ |
231 | acts_shutdown, /* shut down driver */ | | 231 | acts_shutdown, /* shut down driver */ |
232 | acts_poll, /* transmit poll message */ | | 232 | acts_poll, /* transmit poll message */ |
233 | noentry, /* not used */ | | 233 | noentry, /* not used */ |
234 | noentry, /* not used */ | | 234 | noentry, /* not used */ |
235 | noentry, /* not used */ | | 235 | noentry, /* not used */ |
236 | acts_timer /* housekeeping timer */ | | 236 | acts_timer /* housekeeping timer */ |
237 | }; | | 237 | }; |
238 | | | 238 | |
239 | /* | | 239 | /* |
240 | * Initialize data for processing | | 240 | * Initialize data for processing |
241 | */ | | 241 | */ |
242 | static int | | 242 | static int |
243 | acts_start ( | | 243 | acts_start ( |
244 | int unit, | | 244 | int unit, |
245 | struct peer *peer | | 245 | struct peer *peer |
246 | ) | | 246 | ) |
247 | { | | 247 | { |
248 | struct actsunit *up; | | 248 | struct actsunit *up; |
249 | struct refclockproc *pp; | | 249 | struct refclockproc *pp; |
250 | | | 250 | |
251 | /* | | 251 | /* |
252 | * Allocate and initialize unit structure | | 252 | * Allocate and initialize unit structure |
253 | */ | | 253 | */ |
254 | up = emalloc(sizeof(struct actsunit)); | | 254 | up = emalloc(sizeof(struct actsunit)); |
255 | memset(up, 0, sizeof(struct actsunit)); | | 255 | memset(up, 0, sizeof(struct actsunit)); |
256 | up->unit = unit; | | 256 | up->unit = unit; |
257 | pp = peer->procptr; | | 257 | pp = peer->procptr; |
258 | pp->unitptr = (caddr_t)up; | | 258 | pp->unitptr = (caddr_t)up; |
259 | pp->io.clock_recv = acts_receive; | | 259 | pp->io.clock_recv = acts_receive; |
260 | pp->io.srcclock = (caddr_t)peer; | | 260 | pp->io.srcclock = (caddr_t)peer; |
261 | pp->io.datalen = 0; | | 261 | pp->io.datalen = 0; |
262 | | | 262 | |
263 | /* | | 263 | /* |
264 | * Initialize miscellaneous variables | | 264 | * Initialize miscellaneous variables |
265 | */ | | 265 | */ |
266 | peer->precision = PRECISION; | | 266 | peer->precision = PRECISION; |
267 | pp->clockdesc = DESCRIPTION; | | 267 | pp->clockdesc = DESCRIPTION; |
268 | memcpy((char *)&pp->refid, REFID, 4); | | 268 | memcpy((char *)&pp->refid, REFID, 4); |
269 | peer->sstclktype = CTL_SST_TS_TELEPHONE; | | 269 | peer->sstclktype = CTL_SST_TS_TELEPHONE; |
270 | up->bufptr = pp->a_lastcode; | | 270 | up->bufptr = pp->a_lastcode; |
271 | return (1); | | 271 | return (1); |
272 | } | | 272 | } |
273 | | | 273 | |
274 | | | 274 | |
275 | /* | | 275 | /* |
276 | * acts_shutdown - shut down the clock | | 276 | * acts_shutdown - shut down the clock |
277 | */ | | 277 | */ |
278 | static void | | 278 | static void |
279 | acts_shutdown ( | | 279 | acts_shutdown ( |
280 | int unit, | | 280 | int unit, |
281 | struct peer *peer | | 281 | struct peer *peer |
282 | ) | | 282 | ) |
283 | { | | 283 | { |
284 | struct actsunit *up; | | 284 | struct actsunit *up; |
285 | struct refclockproc *pp; | | 285 | struct refclockproc *pp; |
286 | | | 286 | |
287 | /* | | 287 | /* |
288 | * Warning: do this only when a call is not in progress. | | 288 | * Warning: do this only when a call is not in progress. |
289 | */ | | 289 | */ |
290 | pp = peer->procptr; | | 290 | pp = peer->procptr; |
291 | up = (struct actsunit *)pp->unitptr; | | 291 | up = (struct actsunit *)pp->unitptr; |
292 | free(up); | | 292 | free(up); |
293 | } | | 293 | } |
294 | | | 294 | |
295 | | | 295 | |
296 | /* | | 296 | /* |
297 | * acts_receive - receive data from the serial interface | | 297 | * acts_receive - receive data from the serial interface |
298 | */ | | 298 | */ |
299 | static void | | 299 | static void |
300 | acts_receive ( | | 300 | acts_receive ( |
301 | struct recvbuf *rbufp | | 301 | struct recvbuf *rbufp |
302 | ) | | 302 | ) |
303 | { | | 303 | { |
304 | struct actsunit *up; | | 304 | struct actsunit *up; |
305 | struct refclockproc *pp; | | 305 | struct refclockproc *pp; |
306 | struct peer *peer; | | 306 | struct peer *peer; |
307 | char tbuf[BMAX]; | | 307 | char tbuf[BMAX]; |
308 | char *tptr; | | 308 | char *tptr; |
309 | | | 309 | |
310 | /* | | 310 | /* |
311 | * Initialize pointers and read the timecode and timestamp. Note | | 311 | * Initialize pointers and read the timecode and timestamp. Note |
312 | * we are in raw mode and victim of whatever the terminal | | 312 | * we are in raw mode and victim of whatever the terminal |
313 | * interface kicks up; so, we have to reassemble messages from | | 313 | * interface kicks up; so, we have to reassemble messages from |
314 | * arbitrary fragments. Capture the timecode at the beginning of | | 314 | * arbitrary fragments. Capture the timecode at the beginning of |
315 | * the message and at the '*' and '#' on-time characters. | | 315 | * the message and at the '*' and '#' on-time characters. |
316 | */ | | 316 | */ |
317 | peer = (struct peer *)rbufp->recv_srcclock; | | 317 | peer = (struct peer *)rbufp->recv_srcclock; |
318 | pp = peer->procptr; | | 318 | pp = peer->procptr; |
319 | up = (struct actsunit *)pp->unitptr; | | 319 | up = (struct actsunit *)pp->unitptr; |
320 | pp->lencode = refclock_gtraw(rbufp, tbuf, BMAX - (up->bufptr - | | 320 | pp->lencode = refclock_gtraw(rbufp, tbuf, BMAX - (up->bufptr - |
321 | pp->a_lastcode), &pp->lastrec); | | 321 | pp->a_lastcode), &pp->lastrec); |
322 | for (tptr = tbuf; *tptr != '\0'; tptr++) { | | 322 | for (tptr = tbuf; *tptr != '\0'; tptr++) { |
323 | if (*tptr == LF) { | | 323 | if (*tptr == LF) { |
324 | if (up->bufptr == pp->a_lastcode) { | | 324 | if (up->bufptr == pp->a_lastcode) { |
325 | up->tstamp = pp->lastrec; | | 325 | up->tstamp = pp->lastrec; |
326 | continue; | | 326 | continue; |
327 | | | 327 | |
328 | } else { | | 328 | } else { |
329 | *up->bufptr = '\0'; | | 329 | *up->bufptr = '\0'; |
330 | acts_message(peer); | | 330 | acts_message(peer); |
331 | up->bufptr = pp->a_lastcode; | | 331 | up->bufptr = pp->a_lastcode; |
332 | } | | 332 | } |
333 | } else if (!iscntrl(*tptr)) { | | 333 | } else if (!iscntrl((unsigned char)*tptr)) { |
334 | *up->bufptr++ = *tptr; | | 334 | *up->bufptr++ = *tptr; |
335 | if (*tptr == '*' || *tptr == '#') { | | 335 | if (*tptr == '*' || *tptr == '#') { |
336 | up->tstamp = pp->lastrec; | | 336 | up->tstamp = pp->lastrec; |
337 | write(pp->io.fd, tptr, 1); | | 337 | write(pp->io.fd, tptr, 1); |
338 | } | | 338 | } |
339 | } | | 339 | } |
340 | } | | 340 | } |
341 | } | | 341 | } |
342 | | | 342 | |
343 | | | 343 | |
344 | /* | | 344 | /* |
345 | * acts_message - process message | | 345 | * acts_message - process message |
346 | */ | | 346 | */ |
347 | void | | 347 | void |
348 | acts_message( | | 348 | acts_message( |
349 | struct peer *peer | | 349 | struct peer *peer |
350 | ) | | 350 | ) |
351 | { | | 351 | { |
352 | struct actsunit *up; | | 352 | struct actsunit *up; |
353 | struct refclockproc *pp; | | 353 | struct refclockproc *pp; |
354 | int dtr = TIOCM_DTR; | | 354 | int dtr = TIOCM_DTR; |
355 | char tbuf[SMAX]; | | 355 | char tbuf[SMAX]; |
356 | #ifdef DEBUG | | 356 | #ifdef DEBUG |
357 | u_int modem; | | 357 | u_int modem; |
358 | #endif | | 358 | #endif |
359 | | | 359 | |
360 | /* | | 360 | /* |
361 | * What to do depends on the state and the first token in the | | 361 | * What to do depends on the state and the first token in the |
362 | * message. */ | | 362 | * message. */ |
363 | pp = peer->procptr; | | 363 | pp = peer->procptr; |
364 | up = (struct actsunit *)pp->unitptr; | | 364 | up = (struct actsunit *)pp->unitptr; |
365 | #ifdef DEBUG | | 365 | #ifdef DEBUG |
366 | ioctl(pp->io.fd, TIOCMGET, (char *)&modem); | | 366 | ioctl(pp->io.fd, TIOCMGET, (char *)&modem); |
367 | snprintf(tbuf, sizeof(tbuf), "acts: %04x (%d %d) %d %s", modem, | | 367 | snprintf(tbuf, sizeof(tbuf), "acts: %04x (%d %d) %zu %s", modem, |
368 | up->state, up->timer, strlen(pp->a_lastcode), | | 368 | up->state, up->timer, strlen(pp->a_lastcode), |
369 | pp->a_lastcode); | | 369 | pp->a_lastcode); |
370 | if (debug) | | 370 | if (debug) |
371 | printf("%s\n", tbuf); | | 371 | printf("%s\n", tbuf); |
372 | #endif | | 372 | #endif |
373 | | | 373 | |
374 | /* | | 374 | /* |
375 | * Extract the first token in the line. A NO token sends the | | 375 | * Extract the first token in the line. A NO token sends the |
376 | * message to the clockstats. | | 376 | * message to the clockstats. |
377 | */ | | 377 | */ |
378 | strncpy(tbuf, pp->a_lastcode, SMAX); | | 378 | strncpy(tbuf, pp->a_lastcode, SMAX); |
379 | strtok(tbuf, " "); | | 379 | strtok(tbuf, " "); |
380 | if (strcmp(tbuf, "NO") == 0) { | | 380 | if (strcmp(tbuf, "NO") == 0) { |
381 | report_event(PEVNT_CLOCK, peer, pp->a_lastcode); | | 381 | report_event(PEVNT_CLOCK, peer, pp->a_lastcode); |
382 | return; | | 382 | return; |
383 | } | | 383 | } |
384 | switch(up->state) { | | 384 | switch(up->state) { |
385 | | | 385 | |
386 | /* | | 386 | /* |
387 | * We are waiting for the OK response to the modem setup | | 387 | * We are waiting for the OK response to the modem setup |
388 | * command. When this happens, raise DTR and dial the number | | 388 | * command. When this happens, raise DTR and dial the number |
389 | * followed by \r. | | 389 | * followed by \r. |
390 | */ | | 390 | */ |
391 | case S_OK: | | 391 | case S_OK: |
392 | if (strcmp(tbuf, "OK") != 0) { | | 392 | if (strcmp(tbuf, "OK") != 0) { |
393 | msyslog(LOG_ERR, "acts: setup error %s", | | 393 | msyslog(LOG_ERR, "acts: setup error %s", |
394 | pp->a_lastcode); | | 394 | pp->a_lastcode); |
395 | acts_disc(peer); | | 395 | acts_disc(peer); |
396 | return; | | 396 | return; |
397 | } | | 397 | } |
398 | ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr); | | 398 | ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr); |
399 | up->state = S_DTR; | | 399 | up->state = S_DTR; |
400 | up->timer = DTR; | | 400 | up->timer = DTR; |
401 | return; | | 401 | return; |
402 | | | 402 | |
403 | /* | | 403 | /* |
404 | * We are waiting for the call to be answered. All we care about | | 404 | * We are waiting for the call to be answered. All we care about |
405 | * here is token CONNECT. Send the message to the clockstats. | | 405 | * here is token CONNECT. Send the message to the clockstats. |
406 | */ | | 406 | */ |
407 | case S_CONNECT: | | 407 | case S_CONNECT: |
408 | report_event(PEVNT_CLOCK, peer, pp->a_lastcode); | | 408 | report_event(PEVNT_CLOCK, peer, pp->a_lastcode); |
409 | if (strcmp(tbuf, "CONNECT") != 0) { | | 409 | if (strcmp(tbuf, "CONNECT") != 0) { |
410 | acts_disc(peer); | | 410 | acts_disc(peer); |
411 | return; | | 411 | return; |
412 | } | | 412 | } |
413 | up->state = S_FIRST; | | 413 | up->state = S_FIRST; |
414 | up->timer = CONNECT; | | 414 | up->timer = CONNECT; |
415 | return; | | 415 | return; |
416 | | | 416 | |
417 | /* | | 417 | /* |
418 | * We are waiting for a timecode. Pass it to the parser. | | 418 | * We are waiting for a timecode. Pass it to the parser. |
419 | */ | | 419 | */ |
420 | case S_FIRST: | | 420 | case S_FIRST: |
421 | case S_MSG: | | 421 | case S_MSG: |
422 | acts_timecode(peer, pp->a_lastcode); | | 422 | acts_timecode(peer, pp->a_lastcode); |
423 | break; | | 423 | break; |
424 | } | | 424 | } |
425 | } | | 425 | } |
426 | | | 426 | |
427 | /* | | 427 | /* |
428 | * acts_timecode - identify the service and parse the timecode message | | 428 | * acts_timecode - identify the service and parse the timecode message |
429 | */ | | 429 | */ |
430 | void | | 430 | void |
431 | acts_timecode( | | 431 | acts_timecode( |
432 | struct peer *peer, /* peer structure pointer */ | | 432 | struct peer *peer, /* peer structure pointer */ |
433 | char *str /* timecode string */ | | 433 | char *str /* timecode string */ |
434 | ) | | 434 | ) |
435 | { | | 435 | { |
436 | struct actsunit *up; | | 436 | struct actsunit *up; |
437 | struct refclockproc *pp; | | 437 | struct refclockproc *pp; |
438 | int day; /* day of the month */ | | 438 | int day; /* day of the month */ |
439 | int month; /* month of the year */ | | 439 | int month; /* month of the year */ |
440 | u_long mjd; /* Modified Julian Day */ | | 440 | u_long mjd; /* Modified Julian Day */ |
441 | double dut1; /* DUT adjustment */ | | 441 | double dut1; /* DUT adjustment */ |
442 | | | 442 | |
443 | u_int dst; /* ACTS daylight/standard time */ | | 443 | u_int dst; /* ACTS daylight/standard time */ |
444 | u_int leap; /* ACTS leap indicator */ | | 444 | u_int leap; /* ACTS leap indicator */ |
445 | double msADV; /* ACTS transmit advance (ms) */ | | 445 | double msADV; /* ACTS transmit advance (ms) */ |
446 | char utc[10]; /* ACTS timescale */ | | 446 | char utc[10]; /* ACTS timescale */ |
447 | char flag; /* ACTS on-time character (* or #) */ | | 447 | char flag; /* ACTS on-time character (* or #) */ |
448 | | | 448 | |
449 | char synchar; /* WWVB synchronized indicator */ | | 449 | char synchar; /* WWVB synchronized indicator */ |
450 | char qualchar; /* WWVB quality indicator */ | | 450 | char qualchar; /* WWVB quality indicator */ |
451 | char leapchar; /* WWVB leap indicator */ | | 451 | char leapchar; /* WWVB leap indicator */ |
452 | char dstchar; /* WWVB daylight/savings indicator */ | | 452 | char dstchar; /* WWVB daylight/savings indicator */ |
453 | int tz; /* WWVB timezone */ | | 453 | int tz; /* WWVB timezone */ |
454 | | | 454 | |
455 | u_int leapmonth; /* PTB/NPL month of leap */ | | 455 | u_int leapmonth; /* PTB/NPL month of leap */ |
456 | char leapdir; /* PTB/NPL leap direction */ | | 456 | char leapdir; /* PTB/NPL leap direction */ |
457 | | | 457 | |
458 | /* | | 458 | /* |
459 | * The parser selects the modem format based on the message | | 459 | * The parser selects the modem format based on the message |
460 | * length. Since the data are checked carefully, occasional | | 460 | * length. Since the data are checked carefully, occasional |
461 | * errors due noise are forgivable. | | 461 | * errors due noise are forgivable. |
462 | */ | | 462 | */ |
463 | pp = peer->procptr; | | 463 | pp = peer->procptr; |
464 | up = (struct actsunit *)pp->unitptr; | | 464 | up = (struct actsunit *)pp->unitptr; |
465 | pp->nsec = 0; | | 465 | pp->nsec = 0; |
466 | switch(strlen(str)) { | | 466 | switch(strlen(str)) { |
467 | | | 467 | |
468 | /* | | 468 | /* |
469 | * For USNO format on-time character '*', which is on a line by | | 469 | * For USNO format on-time character '*', which is on a line by |
470 | * itself. Be sure a timecode has been received. | | 470 | * itself. Be sure a timecode has been received. |
471 | */ | | 471 | */ |
472 | case 1: | | 472 | case 1: |
473 | if (*str == '*' && up->msgcnt > 0) | | 473 | if (*str == '*' && up->msgcnt > 0) |
474 | break; | | 474 | break; |
475 | | | 475 | |
476 | return; | | 476 | return; |
477 | | | 477 | |
478 | /* | | 478 | /* |
479 | * ACTS format: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa | | 479 | * ACTS format: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa |
480 | * UTC(NIST) *" | | 480 | * UTC(NIST) *" |
481 | */ | | 481 | */ |
482 | case LENACTS: | | 482 | case LENACTS: |
483 | if (sscanf(str, | | 483 | if (sscanf(str, |
484 | "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c", | | 484 | "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c", |
485 | &mjd, &pp->year, &month, &day, &pp->hour, | | 485 | &mjd, &pp->year, &month, &day, &pp->hour, |
486 | &pp->minute, &pp->second, &dst, &leap, &dut1, | | 486 | &pp->minute, &pp->second, &dst, &leap, &dut1, |
487 | &msADV, utc, &flag) != 13) { | | 487 | &msADV, utc, &flag) != 13) { |
488 | refclock_report(peer, CEVNT_BADREPLY); | | 488 | refclock_report(peer, CEVNT_BADREPLY); |
489 | return; | | 489 | return; |
490 | } | | 490 | } |
491 | | | 491 | |
492 | /* | | 492 | /* |
493 | * Wait until ACTS has calculated the roundtrip delay. | | 493 | * Wait until ACTS has calculated the roundtrip delay. |
494 | * We don't need to do anything, as ACTS adjusts the | | 494 | * We don't need to do anything, as ACTS adjusts the |
495 | * on-time epoch. | | 495 | * on-time epoch. |
496 | */ | | 496 | */ |
497 | if (flag != '#') | | 497 | if (flag != '#') |
498 | return; | | 498 | return; |
499 | | | 499 | |
500 | pp->day = ymd2yd(pp->year, month, day); | | 500 | pp->day = ymd2yd(pp->year, month, day); |
501 | pp->leap = LEAP_NOWARNING; | | 501 | pp->leap = LEAP_NOWARNING; |
502 | if (leap == 1) | | 502 | if (leap == 1) |
503 | pp->leap = LEAP_ADDSECOND; | | 503 | pp->leap = LEAP_ADDSECOND; |
504 | else if (pp->leap == 2) | | 504 | else if (pp->leap == 2) |
505 | pp->leap = LEAP_DELSECOND; | | 505 | pp->leap = LEAP_DELSECOND; |
506 | memcpy(&pp->refid, REFACTS, 4); | | 506 | memcpy(&pp->refid, REFACTS, 4); |
507 | if (up->msgcnt == 0) | | 507 | if (up->msgcnt == 0) |
508 | record_clock_stats(&peer->srcadr, str); | | 508 | record_clock_stats(&peer->srcadr, str); |
509 | up->msgcnt++; | | 509 | up->msgcnt++; |
510 | break; | | 510 | break; |
511 | | | 511 | |
512 | /* | | 512 | /* |
513 | * USNO format: "jjjjj nnn hhmmss UTC" | | 513 | * USNO format: "jjjjj nnn hhmmss UTC" |
514 | */ | | 514 | */ |
515 | case LENUSNO: | | 515 | case LENUSNO: |
516 | if (sscanf(str, "%5ld %3d %2d%2d%2d %3s", | | 516 | if (sscanf(str, "%5ld %3d %2d%2d%2d %3s", |
517 | &mjd, &pp->day, &pp->hour, &pp->minute, | | 517 | &mjd, &pp->day, &pp->hour, &pp->minute, |
518 | &pp->second, utc) != 6) { | | 518 | &pp->second, utc) != 6) { |
519 | refclock_report(peer, CEVNT_BADREPLY); | | 519 | refclock_report(peer, CEVNT_BADREPLY); |
520 | return; | | 520 | return; |
521 | } | | 521 | } |
522 | | | 522 | |
523 | /* | | 523 | /* |
524 | * Wait for the on-time character, which follows in a | | 524 | * Wait for the on-time character, which follows in a |
525 | * separate message. There is no provision for leap | | 525 | * separate message. There is no provision for leap |
526 | * warning. | | 526 | * warning. |
527 | */ | | 527 | */ |
528 | pp->leap = LEAP_NOWARNING; | | 528 | pp->leap = LEAP_NOWARNING; |
529 | memcpy(&pp->refid, REFUSNO, 4); | | 529 | memcpy(&pp->refid, REFUSNO, 4); |
530 | if (up->msgcnt == 0) | | 530 | if (up->msgcnt == 0) |
531 | record_clock_stats(&peer->srcadr, str); | | 531 | record_clock_stats(&peer->srcadr, str); |
532 | up->msgcnt++; | | 532 | up->msgcnt++; |
533 | return; | | 533 | return; |
534 | | | 534 | |
535 | /* | | 535 | /* |
536 | * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ" | | 536 | * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ" |
537 | */ | | 537 | */ |
538 | case LENPTB: | | 538 | case LENPTB: |
539 | if (sscanf(str, | | 539 | if (sscanf(str, |
540 | "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c", | | 540 | "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c", |
541 | &pp->second, &pp->year, &month, &day, &pp->hour, | | 541 | &pp->second, &pp->year, &month, &day, &pp->hour, |
542 | &pp->minute, &mjd, &dut1, &leapdir, &leapmonth, | | 542 | &pp->minute, &mjd, &dut1, &leapdir, &leapmonth, |
543 | &msADV, &flag) != 12) { | | 543 | &msADV, &flag) != 12) { |
544 | refclock_report(peer, CEVNT_BADREPLY); | | 544 | refclock_report(peer, CEVNT_BADREPLY); |
545 | return; | | 545 | return; |
546 | } | | 546 | } |
547 | pp->leap = LEAP_NOWARNING; | | 547 | pp->leap = LEAP_NOWARNING; |
548 | if (leapmonth == month) { | | 548 | if (leapmonth == month) { |
549 | if (leapdir == '+') | | 549 | if (leapdir == '+') |
550 | pp->leap = LEAP_ADDSECOND; | | 550 | pp->leap = LEAP_ADDSECOND; |
551 | else if (leapdir == '-') | | 551 | else if (leapdir == '-') |
552 | pp->leap = LEAP_DELSECOND; | | 552 | pp->leap = LEAP_DELSECOND; |
553 | } | | 553 | } |
554 | pp->day = ymd2yd(pp->year, month, day); | | 554 | pp->day = ymd2yd(pp->year, month, day); |
555 | memcpy(&pp->refid, REFPTB, 4); | | 555 | memcpy(&pp->refid, REFPTB, 4); |
556 | if (up->msgcnt == 0) | | 556 | if (up->msgcnt == 0) |
557 | record_clock_stats(&peer->srcadr, str); | | 557 | record_clock_stats(&peer->srcadr, str); |
558 | up->msgcnt++; | | 558 | up->msgcnt++; |
559 | break; | | 559 | break; |
560 | | | 560 | |
561 | | | 561 | |
562 | /* | | 562 | /* |
563 | * WWVB format 0: "I ddd hh:mm:ss DTZ=nn" | | 563 | * WWVB format 0: "I ddd hh:mm:ss DTZ=nn" |
564 | */ | | 564 | */ |
565 | case LENWWVB0: | | 565 | case LENWWVB0: |
566 | if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d", | | 566 | if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d", |
567 | &synchar, &pp->day, &pp->hour, &pp->minute, | | 567 | &synchar, &pp->day, &pp->hour, &pp->minute, |
568 | &pp->second, &dstchar, &tz) != 7) { | | 568 | &pp->second, &dstchar, &tz) != 7) { |
569 | refclock_report(peer, CEVNT_BADREPLY); | | 569 | refclock_report(peer, CEVNT_BADREPLY); |
570 | return; | | 570 | return; |
571 | } | | 571 | } |
572 | pp->leap = LEAP_NOWARNING; | | 572 | pp->leap = LEAP_NOWARNING; |
573 | if (synchar != ' ') | | 573 | if (synchar != ' ') |
574 | pp->leap = LEAP_NOTINSYNC; | | 574 | pp->leap = LEAP_NOTINSYNC; |
575 | memcpy(&pp->refid, REFWWVB, 4); | | 575 | memcpy(&pp->refid, REFWWVB, 4); |
576 | if (up->msgcnt == 0) | | 576 | if (up->msgcnt == 0) |
577 | record_clock_stats(&peer->srcadr, str); | | 577 | record_clock_stats(&peer->srcadr, str); |
578 | up->msgcnt++; | | 578 | up->msgcnt++; |
579 | break; | | 579 | break; |
580 | | | 580 | |
581 | /* | | 581 | /* |
582 | * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD" | | 582 | * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD" |
583 | */ | | 583 | */ |
584 | case LENWWVB2: | | 584 | case LENWWVB2: |
585 | if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c", | | 585 | if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c", |
586 | &synchar, &qualchar, &pp->year, &pp->day, | | 586 | &synchar, &qualchar, &pp->year, &pp->day, |
587 | &pp->hour, &pp->minute, &pp->second, &pp->nsec, | | 587 | &pp->hour, &pp->minute, &pp->second, &pp->nsec, |
588 | &dstchar, &leapchar, &dstchar) != 11) { | | 588 | &dstchar, &leapchar, &dstchar) != 11) { |
589 | refclock_report(peer, CEVNT_BADREPLY); | | 589 | refclock_report(peer, CEVNT_BADREPLY); |
590 | return; | | 590 | return; |
591 | } | | 591 | } |
592 | pp->nsec *= 1000000; | | 592 | pp->nsec *= 1000000; |
593 | pp->leap = LEAP_NOWARNING; | | 593 | pp->leap = LEAP_NOWARNING; |
594 | if (synchar != ' ') | | 594 | if (synchar != ' ') |
595 | pp->leap = LEAP_NOTINSYNC; | | 595 | pp->leap = LEAP_NOTINSYNC; |
596 | else if (leapchar == 'L') | | 596 | else if (leapchar == 'L') |
597 | pp->leap = LEAP_ADDSECOND; | | 597 | pp->leap = LEAP_ADDSECOND; |
598 | memcpy(&pp->refid, REFWWVB, 4); | | 598 | memcpy(&pp->refid, REFWWVB, 4); |
599 | if (up->msgcnt == 0) | | 599 | if (up->msgcnt == 0) |
600 | record_clock_stats(&peer->srcadr, str); | | 600 | record_clock_stats(&peer->srcadr, str); |
601 | up->msgcnt++; | | 601 | up->msgcnt++; |
602 | break; | | 602 | break; |
603 | | | 603 | |
604 | /* | | 604 | /* |
605 | * None of the above. Just forget about it and wait for the next | | 605 | * None of the above. Just forget about it and wait for the next |
606 | * message or timeout. | | 606 | * message or timeout. |
607 | */ | | 607 | */ |
608 | default: | | 608 | default: |
609 | return; | | 609 | return; |
610 | } | | 610 | } |
611 | | | 611 | |
612 | /* | | 612 | /* |
613 | * We have a valid timecode. The fudge time1 value is added to | | 613 | * We have a valid timecode. The fudge time1 value is added to |
614 | * each sample by the main line routines. Note that in current | | 614 | * each sample by the main line routines. Note that in current |
615 | * telephone networks the propatation time can be different for | | 615 | * telephone networks the propatation time can be different for |
616 | * each call and can reach 200 ms for some calls. | | 616 | * each call and can reach 200 ms for some calls. |
617 | */ | | 617 | */ |
618 | peer->refid = pp->refid; | | 618 | peer->refid = pp->refid; |
619 | pp->lastrec = up->tstamp; | | 619 | pp->lastrec = up->tstamp; |
620 | if (!refclock_process(pp)) { | | 620 | if (!refclock_process(pp)) { |
621 | refclock_report(peer, CEVNT_BADTIME); | | 621 | refclock_report(peer, CEVNT_BADTIME); |
622 | return; | | 622 | return; |
623 | } | | 623 | } |
624 | pp->lastref = pp->lastrec; | | 624 | pp->lastref = pp->lastrec; |
625 | if (up->state != S_MSG) { | | 625 | if (up->state != S_MSG) { |
626 | up->state = S_MSG; | | 626 | up->state = S_MSG; |
627 | up->timer = TIMECODE; | | 627 | up->timer = TIMECODE; |
628 | } | | 628 | } |
629 | } | | 629 | } |
630 | | | 630 | |
631 | | | 631 | |
632 | /* | | 632 | /* |
633 | * acts_poll - called by the transmit routine | | 633 | * acts_poll - called by the transmit routine |
634 | */ | | 634 | */ |
635 | static void | | 635 | static void |
636 | acts_poll ( | | 636 | acts_poll ( |
637 | int unit, | | 637 | int unit, |
638 | struct peer *peer | | 638 | struct peer *peer |
639 | ) | | 639 | ) |
640 | { | | 640 | { |
641 | struct actsunit *up; | | 641 | struct actsunit *up; |
642 | struct refclockproc *pp; | | 642 | struct refclockproc *pp; |
643 | | | 643 | |
644 | /* | | 644 | /* |
645 | * This routine is called at every system poll. All it does is | | 645 | * This routine is called at every system poll. All it does is |
646 | * set flag1 under certain conditions. The real work is done by | | 646 | * set flag1 under certain conditions. The real work is done by |
647 | * the timeout routine and state machine. | | 647 | * the timeout routine and state machine. |
648 | */ | | 648 | */ |
649 | pp = peer->procptr; | | 649 | pp = peer->procptr; |
650 | up = (struct actsunit *)pp->unitptr; | | 650 | up = (struct actsunit *)pp->unitptr; |
651 | switch (peer->ttl) { | | 651 | switch (peer->ttl) { |
652 | | | 652 | |
653 | /* | | 653 | /* |
654 | * In manual mode the calling program is activated by the ntpdc | | 654 | * In manual mode the calling program is activated by the ntpdc |
655 | * program using the enable flag (fudge flag1), either manually | | 655 | * program using the enable flag (fudge flag1), either manually |
656 | * or by a cron job. | | 656 | * or by a cron job. |
657 | */ | | 657 | */ |
658 | case MODE_MANUAL: | | 658 | case MODE_MANUAL: |
659 | /* fall through */ | | 659 | /* fall through */ |
660 | break; | | 660 | break; |
661 | | | 661 | |
662 | /* | | 662 | /* |
663 | * In automatic mode the calling program runs continuously at | | 663 | * In automatic mode the calling program runs continuously at |
664 | * intervals determined by the poll event or specified timeout. | | 664 | * intervals determined by the poll event or specified timeout. |
665 | */ | | 665 | */ |
666 | case MODE_AUTO: | | 666 | case MODE_AUTO: |
667 | pp->sloppyclockflag |= CLK_FLAG1; | | 667 | pp->sloppyclockflag |= CLK_FLAG1; |
668 | break; | | 668 | break; |
669 | | | 669 | |
670 | /* | | 670 | /* |
671 | * In backup mode the calling program runs continuously as long | | 671 | * In backup mode the calling program runs continuously as long |
672 | * as either no peers are available or this peer is selected. | | 672 | * as either no peers are available or this peer is selected. |
673 | */ | | 673 | */ |
674 | case MODE_BACKUP: | | 674 | case MODE_BACKUP: |
675 | if (sys_peer == NULL || sys_peer == peer) | | 675 | if (sys_peer == NULL || sys_peer == peer) |
676 | pp->sloppyclockflag |= CLK_FLAG1; | | 676 | pp->sloppyclockflag |= CLK_FLAG1; |
677 | break; | | 677 | break; |
678 | } | | 678 | } |
679 | } | | 679 | } |
680 | | | 680 | |
681 | | | 681 | |
682 | /* | | 682 | /* |
683 | * acts_timer - called at one-second intervals | | 683 | * acts_timer - called at one-second intervals |
684 | */ | | 684 | */ |
685 | static void | | 685 | static void |
686 | acts_timer( | | 686 | acts_timer( |
687 | int unit, | | 687 | int unit, |
688 | struct peer *peer | | 688 | struct peer *peer |
689 | ) | | 689 | ) |
690 | { | | 690 | { |
691 | struct actsunit *up; | | 691 | struct actsunit *up; |
692 | struct refclockproc *pp; | | 692 | struct refclockproc *pp; |
693 | | | 693 | |
694 | /* | | 694 | /* |
695 | * This routine implments a timeout which runs for a programmed | | 695 | * This routine implments a timeout which runs for a programmed |
696 | * interval. The counter is initialized by the state machine and | | 696 | * interval. The counter is initialized by the state machine and |
697 | * counts down to zero. Upon reaching zero, the state machine is | | 697 | * counts down to zero. Upon reaching zero, the state machine is |
698 | * called. If flag1 is set while in S_IDLE state, force a | | 698 | * called. If flag1 is set while in S_IDLE state, force a |
699 | * timeout. | | 699 | * timeout. |
700 | */ | | 700 | */ |
701 | pp = peer->procptr; | | 701 | pp = peer->procptr; |
702 | up = (struct actsunit *)pp->unitptr; | | 702 | up = (struct actsunit *)pp->unitptr; |
703 | if (pp->sloppyclockflag & CLK_FLAG1 && up->state == S_IDLE) { | | 703 | if (pp->sloppyclockflag & CLK_FLAG1 && up->state == S_IDLE) { |
704 | acts_timeout(peer); | | 704 | acts_timeout(peer); |
705 | return; | | 705 | return; |
706 | } | | 706 | } |
707 | if (up->timer == 0) | | 707 | if (up->timer == 0) |
708 | return; | | 708 | return; |
709 | | | 709 | |
710 | up->timer--; | | 710 | up->timer--; |
711 | if (up->timer == 0) | | 711 | if (up->timer == 0) |
712 | acts_timeout(peer); | | 712 | acts_timeout(peer); |
713 | } | | 713 | } |
714 | | | 714 | |
715 | | | 715 | |
716 | /* | | 716 | /* |
717 | * acts_timeout - called on timeout | | 717 | * acts_timeout - called on timeout |
718 | */ | | 718 | */ |
719 | static void | | 719 | static void |
720 | acts_timeout( | | 720 | acts_timeout( |
721 | struct peer *peer | | 721 | struct peer *peer |
722 | ) | | 722 | ) |
723 | { | | 723 | { |
724 | struct actsunit *up; | | 724 | struct actsunit *up; |
725 | struct refclockproc *pp; | | 725 | struct refclockproc *pp; |
726 | int fd; | | 726 | int fd; |
727 | char device[20]; | | 727 | char device[20]; |
728 | char lockfile[128], pidbuf[8]; | | 728 | char lockfile[128], pidbuf[8]; |
729 | char tbuf[SMAX]; | | 729 | char tbuf[SMAX]; |
730 | | | 730 | |
731 | /* | | 731 | /* |
732 | * The state machine is driven by messages from the modem, when | | 732 | * The state machine is driven by messages from the modem, when |
733 | * first stated and at timeout. | | 733 | * first stated and at timeout. |
734 | */ | | 734 | */ |
735 | pp = peer->procptr; | | 735 | pp = peer->procptr; |
736 | up = (struct actsunit *)pp->unitptr; | | 736 | up = (struct actsunit *)pp->unitptr; |
737 | pp->sloppyclockflag &= ~CLK_FLAG1; | | 737 | pp->sloppyclockflag &= ~CLK_FLAG1; |
738 | if (sys_phone[up->retry] == NULL && !(pp->sloppyclockflag & | | 738 | if (sys_phone[up->retry] == NULL && !(pp->sloppyclockflag & |
739 | CLK_FLAG3)) { | | 739 | CLK_FLAG3)) { |
740 | msyslog(LOG_ERR, "acts: no phones"); | | 740 | msyslog(LOG_ERR, "acts: no phones"); |
741 | return; | | 741 | return; |
742 | } | | 742 | } |
743 | switch(up->state) { | | 743 | switch(up->state) { |
744 | | | 744 | |
745 | /* | | 745 | /* |
746 | * System poll event. Lock the modem port and open the device. | | 746 | * System poll event. Lock the modem port and open the device. |
747 | */ | | 747 | */ |
748 | case S_IDLE: | | 748 | case S_IDLE: |
749 | | | 749 | |
750 | /* | | 750 | /* |
751 | * Lock the modem port. If busy, retry later. Note: if | | 751 | * Lock the modem port. If busy, retry later. Note: if |
752 | * something fails between here and the close, the lock | | 752 | * something fails between here and the close, the lock |
753 | * file may not be removed. | | 753 | * file may not be removed. |
754 | */ | | 754 | */ |
755 | if (pp->sloppyclockflag & CLK_FLAG2) { | | 755 | if (pp->sloppyclockflag & CLK_FLAG2) { |
756 | snprintf(lockfile, sizeof(lockfile), LOCKFILE, | | 756 | snprintf(lockfile, sizeof(lockfile), LOCKFILE, |
757 | up->unit); | | 757 | up->unit); |
758 | fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL, | | 758 | fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL, |
759 | 0644); | | 759 | 0644); |
760 | if (fd < 0) { | | 760 | if (fd < 0) { |
761 | msyslog(LOG_ERR, "acts: port busy"); | | 761 | msyslog(LOG_ERR, "acts: port busy"); |
762 | return; | | 762 | return; |
763 | } | | 763 | } |
764 | snprintf(pidbuf, sizeof(pidbuf), "%d\n", | | 764 | snprintf(pidbuf, sizeof(pidbuf), "%d\n", |
765 | (u_int)getpid()); | | 765 | (u_int)getpid()); |
766 | write(fd, pidbuf, strlen(pidbuf)); | | 766 | write(fd, pidbuf, strlen(pidbuf)); |
767 | close(fd); | | 767 | close(fd); |
768 | } | | 768 | } |
769 | | | 769 | |
770 | /* | | 770 | /* |
771 | * Open the device in raw mode and link the I/O. | | 771 | * Open the device in raw mode and link the I/O. |
772 | */ | | 772 | */ |
773 | if (!pp->io.fd) { | | 773 | if (!pp->io.fd) { |
774 | snprintf(device, sizeof(device), DEVICE, | | 774 | snprintf(device, sizeof(device), DEVICE, |
775 | up->unit); | | 775 | up->unit); |
776 | fd = refclock_open(device, SPEED232, | | 776 | fd = refclock_open(device, SPEED232, |
777 | LDISC_ACTS | LDISC_RAW | LDISC_REMOTE); | | 777 | LDISC_ACTS | LDISC_RAW | LDISC_REMOTE); |
778 | if (fd == 0) { | | 778 | if (fd == 0) { |
779 | msyslog(LOG_ERR, | | 779 | msyslog(LOG_ERR, |
780 | "acts: open fails"); | | 780 | "acts: open fails"); |
781 | return; | | 781 | return; |
782 | } | | 782 | } |
783 | pp->io.fd = fd; | | 783 | pp->io.fd = fd; |
784 | if (!io_addclock(&pp->io)) { | | 784 | if (!io_addclock(&pp->io)) { |
785 | msyslog(LOG_ERR, | | 785 | msyslog(LOG_ERR, |
786 | "acts: addclock fails"); | | 786 | "acts: addclock fails"); |
787 | close(fd); | | 787 | close(fd); |
788 | pp->io.fd = 0; | | 788 | pp->io.fd = 0; |
789 | return; | | 789 | return; |
790 | } | | 790 | } |
791 | } | | 791 | } |
792 | | | 792 | |
793 | /* | | 793 | /* |
794 | * If the port is directly connected to the device, skip | | 794 | * If the port is directly connected to the device, skip |
795 | * the modem business and send 'T' for Spectrabum. | | 795 | * the modem business and send 'T' for Spectrabum. |
796 | */ | | 796 | */ |
797 | if (pp->sloppyclockflag & CLK_FLAG3) { | | 797 | if (pp->sloppyclockflag & CLK_FLAG3) { |
798 | if (write(pp->io.fd, "T", 1) < 0) { | | 798 | if (write(pp->io.fd, "T", 1) < 0) { |
799 | msyslog(LOG_ERR, "acts: write %m"); | | 799 | msyslog(LOG_ERR, "acts: write %m"); |
800 | return; | | 800 | return; |
801 | } | | 801 | } |
802 | up->state = S_FIRST; | | 802 | up->state = S_FIRST; |
803 | up->timer = CONNECT; | | 803 | up->timer = CONNECT; |
804 | return; | | 804 | return; |
805 | } | | 805 | } |
806 | | | 806 | |
807 | /* | | 807 | /* |
808 | * Initialize the modem. This works with Hayes commands. | | 808 | * Initialize the modem. This works with Hayes commands. |
809 | */ | | 809 | */ |
810 | #ifdef DEBUG | | 810 | #ifdef DEBUG |
811 | if (debug) | | 811 | if (debug) |
812 | printf("acts: setup %s\n", MODEM_SETUP); | | 812 | printf("acts: setup %s\n", MODEM_SETUP); |
813 | #endif | | 813 | #endif |
814 | if (write(pp->io.fd, MODEM_SETUP, strlen(MODEM_SETUP)) < | | 814 | if (write(pp->io.fd, MODEM_SETUP, strlen(MODEM_SETUP)) < |
815 | 0) { | | 815 | 0) { |
816 | msyslog(LOG_ERR, "acts: write %m"); | | 816 | msyslog(LOG_ERR, "acts: write %m"); |
817 | return; | | 817 | return; |
818 | } | | 818 | } |
819 | up->state = S_OK; | | 819 | up->state = S_OK; |
820 | up->timer = SETUP; | | 820 | up->timer = SETUP; |
821 | return; | | 821 | return; |
822 | | | 822 | |
823 | /* | | 823 | /* |
824 | * In OK state the modem did not respond to setup. | | 824 | * In OK state the modem did not respond to setup. |
825 | */ | | 825 | */ |
826 | case S_OK: | | 826 | case S_OK: |
827 | msyslog(LOG_ERR, "acts: no modem"); | | 827 | msyslog(LOG_ERR, "acts: no modem"); |
828 | break; | | 828 | break; |
829 | | | 829 | |
830 | /* | | 830 | /* |
831 | * In DTR state we are waiting for the modem to settle down | | 831 | * In DTR state we are waiting for the modem to settle down |
832 | * before hammering it with a dial command. | | 832 | * before hammering it with a dial command. |
833 | */ | | 833 | */ |
834 | case S_DTR: | | 834 | case S_DTR: |
835 | snprintf(tbuf, sizeof(tbuf), "DIAL #%d %s", up->retry, | | 835 | snprintf(tbuf, sizeof(tbuf), "DIAL #%d %s", up->retry, |
836 | sys_phone[up->retry]); | | 836 | sys_phone[up->retry]); |
837 | report_event(PEVNT_CLOCK, peer, tbuf); | | 837 | report_event(PEVNT_CLOCK, peer, tbuf); |
838 | #ifdef DEBUG | | 838 | #ifdef DEBUG |
839 | if (debug) | | 839 | if (debug) |
840 | printf("%s\n", tbuf); | | 840 | printf("%s\n", tbuf); |
841 | #endif | | 841 | #endif |
842 | write(pp->io.fd, sys_phone[up->retry], | | 842 | write(pp->io.fd, sys_phone[up->retry], |
843 | strlen(sys_phone[up->retry])); | | 843 | strlen(sys_phone[up->retry])); |
844 | write(pp->io.fd, "\r", 1); | | 844 | write(pp->io.fd, "\r", 1); |
845 | up->state = S_CONNECT; | | 845 | up->state = S_CONNECT; |
846 | up->timer = ANSWER; | | 846 | up->timer = ANSWER; |
847 | return; | | 847 | return; |
848 | | | 848 | |
849 | /* | | 849 | /* |
850 | * In CONNECT state the call did not complete. | | 850 | * In CONNECT state the call did not complete. |
851 | */ | | 851 | */ |
852 | case S_CONNECT: | | 852 | case S_CONNECT: |
853 | msyslog(LOG_ERR, "acts: no answer"); | | 853 | msyslog(LOG_ERR, "acts: no answer"); |
854 | break; | | 854 | break; |
855 | | | 855 | |
856 | /* | | 856 | /* |
857 | * In FIRST state no messages were received. | | 857 | * In FIRST state no messages were received. |
858 | */ | | 858 | */ |
859 | case S_FIRST: | | 859 | case S_FIRST: |
860 | msyslog(LOG_ERR, "acts: no messages"); | | 860 | msyslog(LOG_ERR, "acts: no messages"); |
861 | break; | | 861 | break; |
862 | | | 862 | |
863 | /* | | 863 | /* |
864 | * In CLOSE state hangup is complete. Close the doors and | | 864 | * In CLOSE state hangup is complete. Close the doors and |
865 | * windows and get some air. | | 865 | * windows and get some air. |
866 | */ | | 866 | */ |
867 | case S_CLOSE: | | 867 | case S_CLOSE: |
868 | | | 868 | |
869 | /* | | 869 | /* |
870 | * Close the device and unlock a shared modem. | | 870 | * Close the device and unlock a shared modem. |
871 | */ | | 871 | */ |
872 | if (pp->io.fd) { | | 872 | if (pp->io.fd) { |
873 | io_closeclock(&pp->io); | | 873 | io_closeclock(&pp->io); |
874 | close(pp->io.fd); | | 874 | close(pp->io.fd); |
875 | if (pp->sloppyclockflag & CLK_FLAG2) { | | 875 | if (pp->sloppyclockflag & CLK_FLAG2) { |
876 | snprintf(lockfile, sizeof(lockfile), | | 876 | snprintf(lockfile, sizeof(lockfile), |
877 | LOCKFILE, up->unit); | | 877 | LOCKFILE, up->unit); |
878 | unlink(lockfile); | | 878 | unlink(lockfile); |
879 | } | | 879 | } |
880 | pp->io.fd = 0; | | 880 | pp->io.fd = 0; |
881 | } | | 881 | } |
882 | | | 882 | |
883 | /* | | 883 | /* |
884 | * If messages were received, fold the tent and wait for | | 884 | * If messages were received, fold the tent and wait for |
885 | * the next poll. If no messages and there are more | | 885 | * the next poll. If no messages and there are more |
886 | * numbers to dial, retry after a short wait. | | 886 | * numbers to dial, retry after a short wait. |
887 | */ | | 887 | */ |
888 | up->bufptr = pp->a_lastcode; | | 888 | up->bufptr = pp->a_lastcode; |
889 | up->timer = 0; | | 889 | up->timer = 0; |
890 | up->state = S_IDLE; | | 890 | up->state = S_IDLE; |
891 | if ( up->msgcnt == 0) { | | 891 | if ( up->msgcnt == 0) { |
892 | up->retry++; | | 892 | up->retry++; |
893 | if (sys_phone[up->retry] == NULL) | | 893 | if (sys_phone[up->retry] == NULL) |
894 | up->retry = 0; | | 894 | up->retry = 0; |
895 | else | | 895 | else |
896 | up->timer = SETUP; | | 896 | up->timer = SETUP; |
897 | } else { | | 897 | } else { |
898 | up->retry = 0; | | 898 | up->retry = 0; |
899 | } | | 899 | } |
900 | up->msgcnt = 0; | | 900 | up->msgcnt = 0; |
901 | return; | | 901 | return; |
902 | } | | 902 | } |
903 | acts_disc(peer); | | 903 | acts_disc(peer); |
904 | } | | 904 | } |
905 | | | 905 | |
906 | | | 906 | |
907 | /* | | 907 | /* |
908 | * acts_disc - disconnect the call and clean the place up. | | 908 | * acts_disc - disconnect the call and clean the place up. |
909 | */ | | 909 | */ |
910 | static void | | 910 | static void |
911 | acts_disc ( | | 911 | acts_disc ( |
912 | struct peer *peer | | 912 | struct peer *peer |
913 | ) | | 913 | ) |
914 | { | | 914 | { |
915 | struct actsunit *up; | | 915 | struct actsunit *up; |
916 | struct refclockproc *pp; | | 916 | struct refclockproc *pp; |
917 | int dtr = TIOCM_DTR; | | 917 | int dtr = TIOCM_DTR; |
918 | | | 918 | |
919 | /* | | 919 | /* |
920 | * We get here if the call terminated successfully or if an | | 920 | * We get here if the call terminated successfully or if an |
921 | * error occured. If the median filter has something in it, | | 921 | * error occured. If the median filter has something in it, |
922 | * feed the data to the clock filter. If a modem port, drop DTR | | 922 | * feed the data to the clock filter. If a modem port, drop DTR |
923 | * to force command mode and send modem hangup. | | 923 | * to force command mode and send modem hangup. |
924 | */ | | 924 | */ |
925 | pp = peer->procptr; | | 925 | pp = peer->procptr; |
926 | up = (struct actsunit *)pp->unitptr; | | 926 | up = (struct actsunit *)pp->unitptr; |
927 | if (up->msgcnt > 0) | | 927 | if (up->msgcnt > 0) |
928 | refclock_receive(peer); | | 928 | refclock_receive(peer); |
929 | if (!(pp->sloppyclockflag & CLK_FLAG3)) { | | 929 | if (!(pp->sloppyclockflag & CLK_FLAG3)) { |
930 | ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr); | | 930 | ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr); |
931 | write(pp->io.fd, MODEM_HANGUP, strlen(MODEM_HANGUP)); | | 931 | write(pp->io.fd, MODEM_HANGUP, strlen(MODEM_HANGUP)); |
932 | } | | 932 | } |
933 | up->timer = SETUP; | | 933 | up->timer = SETUP; |
934 | up->state = S_CLOSE; | | 934 | up->state = S_CLOSE; |
935 | } | | 935 | } |
936 | #else | | 936 | #else |
937 | int refclock_acts_bs; | | 937 | int refclock_acts_bs; |
938 | #endif /* REFCLOCK */ | | 938 | #endif /* REFCLOCK */ |