1 /* 2 * refclock_atom - clock driver for 1-pps signals 3 */ 4 #ifdef HAVE_CONFIG_H 5 #include <config.h> 6 #endif 7 8 #if defined(REFCLOCK) && defined(CLOCK_ATOM) 9 10 #include <stdio.h> 11 #include <ctype.h> 12 13 #include "ntpd.h" 14 #include "ntp_io.h" 15 #include "ntp_unixtime.h" 16 #include "ntp_refclock.h" 17 #include "ntp_stdlib.h" 18 19 #ifdef HAVE_SYS_TIME_H 20 # include <sys/time.h> 21 #endif 22 #ifdef HAVE_SYS_TERMIOS_H 23 # include <sys/termios.h> 24 #endif 25 #ifdef HAVE_SYS_PPSCLOCK_H 26 # include <sys/ppsclock.h> 27 #endif 28 #ifdef HAVE_PPSAPI 29 #include <sys/timepps.h> 30 #endif /* HAVE_PPSAPI */ 31 32 /* 33 * This driver furnishes an interface for pulse-per-second (PPS) signals 34 * produced by a cesium clock, timing receiver or related equipment. It 35 * can be used to remove accumulated jitter and retime a secondary 36 * server when synchronized to a primary server over a congested, wide- 37 * area network and before redistributing the time to local clients. 38 * 39 * In order for this driver to work, the local clock must be set to 40 * within +-500 ms by another means, such as a radio clock or NTP 41 * itself. The 1-pps signal is connected via a serial port and gadget 42 * box consisting of a one-shot flopflop and RS232 level converter. 43 * Conntection is either via the carrier detect (DCD) lead or via the 44 * receive data (RD) lead. The incidental jitter using the DCD lead is 45 * essentially the interrupt latency. The incidental jitter using the RD 46 * lead has an additional component due to the line sampling clock. When 47 * operated at 38.4 kbps, this arrangement has a worst-case jitter less 48 * than 26 us. 49 * 50 * There are four ways in which this driver can be used. They are 51 * described in decreasing order of merit below. The first way uses the 52 * ppsapi STREAMS module and the LDISC_PPS line discipline, while the 53 * second way uses the ppsclock STREAMS module and the LDISC_PPS line 54 * discipline. Either of these works only for the baseboard serial ports 55 * of the Sun SPARC IPC and clones. However, the ppsapi uses the 56 * proposed IETF interface expected to become standard for PPS signals. 57 * The serial port to be used is specified by the pps command in the 58 * configuration file. This driver reads the timestamp directly by a 59 * designated ioctl() system call. 60 * 61 * The third way uses the LDISC_CLKPPS line discipline and works for 62 * any architecture supporting a serial port. If after a few seconds 63 * this driver finds no ppsclock module configured, it attempts to open 64 * a serial port device /dev/pps%d, where %d is the unit number, and 65 * assign the LDISC_CLKPPS line discipline to it. If the line discipline 66 * fails, no harm is done except the accuracy is reduced somewhat. The 67 * pulse generator in the gadget box is adjusted to produce a start bit 68 * of length 26 usec at 38400 bps. Used with the LDISC_CLKPPS line 69 * discipline, this produces an ASCII DEL character ('\377') followed by 70 * a timestamp at each seconds epoch. 71 * 72 * The fourth way involves an auxiliary radio clock driver which calls 73 * the PPS driver with a timestamp captured by that driver. This use is 74 * documented in the source code for the driver(s) involved. Note that 75 * some drivers collect the sample information themselves before calling 76 * pps_sample(), and others call knowing only that they are running 77 * shortly after an on-time tick and they expect to retrieve the PPS 78 * offset, fudge their result, and insert it into the timestream. 79 * 80 * Fudge Factors 81 * 82 * There are no special fudge factors other than the generic. The fudge 83 * time1 parameter can be used to compensate for miscellaneous UART and 84 * OS delays. Allow about 247 us for uart delays at 38400 bps and about 85 * 1 ms for STREAMS nonsense with older workstations. Velocities may 86 * vary with modern workstations. 87 */ 88 /* 89 * Interface definitions 90 */ 91 #ifdef HAVE_PPSAPI 92 extern int pps_assert; 93 #endif /* HAVE_PPSAPI */ 94 #ifdef TTYCLK 95 #define DEVICE "/dev/pps%d" /* device name and unit */ 96 #ifdef B38400 97 #define SPEED232 B38400 /* uart speed (38400 baud) */ 98 #else 99 #define SPEED232 EXTB /* as above */ 100 #endif 101 #endif /* TTYCLK */ 102 103 #define PRECISION (-20) /* precision assumed (about 1 us) */ 104 #define REFID "PPS\0" /* reference ID */ 105 #define DESCRIPTION "PPS Clock Discipline" /* WRU */ 106 107 #define FLAG_TTY 0x01 /* tty_clk heard from */ 108 #define FLAG_PPS 0x02 /* ppsclock heard from */ 109 #define FLAG_AUX 0x04 /* auxiliary PPS source */ 110 111 static struct peer *pps_peer; /* atom driver for auxiliary PPS sources */ 112 113 #ifdef TTYCLK 114 static void atom_receive P((struct recvbuf *)); 115 #endif /* TTYCLK */ 116 117 /* 118 * Unit control structure 119 */ 120 struct atomunit { 121 #ifdef HAVE_PPSAPI 122 pps_info_t pps_info; /* pps_info control */ 123 #endif /* HAVE_PPSAPI */ 124 #ifdef PPS 125 struct ppsclockev ev; /* ppsclock control */ 126 #endif /* PPS */ 127 int flags; /* flags that wave */ 128 }; 129 130 /* 131 * Function prototypes 132 */ 133 static int atom_start P((int, struct peer *)); 134 static void atom_shutdown P((int, struct peer *)); 135 static void atom_poll P((int, struct peer *)); 136 #if defined(PPS) || defined(HAVE_PPSAPI) 137 static int atom_pps P((struct peer *)); 138 #endif /* PPS || HAVE_PPSAPI */ 139 140 /* 141 * Transfer vector 142 */ 143 struct refclock refclock_atom = { 144 atom_start, /* start up driver */ 145 atom_shutdown, /* shut down driver */ 146 atom_poll, /* transmit poll message */ 147 noentry, /* not used (old atom_control) */ 148 noentry, /* initialize driver */ 149 noentry, /* not used (old atom_buginfo) */ 150 NOFLAGS /* not used */ 151 }; 152 153 154 /* 155 * atom_start - initialize data for processing 156 */ 157 static int 158 atom_start( 159 int unit, 160 struct peer *peer 161 ) 162 { 163 register struct atomunit *up; 164 struct refclockproc *pp; 165 int flags; 166 #ifdef TTYCLK 167 int fd = 0; 168 char device[20]; 169 int ldisc = LDISC_CLKPPS; 170 #endif /* TTYCLK */ 171 172 pps_peer = peer; 173 flags = 0; 174 175 #ifdef TTYCLK 176 # if defined(SCO5_CLOCK) 177 ldisc = LDISC_RAW; /* DCD timestamps without any line discipline */ 178 # endif 179 /* 180 * Open serial port. Use LDISC_CLKPPS line discipline only 181 * if the LDISC_PPS line discipline is not availble, 182 */ 183 # if defined(PPS) || defined(HAVE_PPSAPI) 184 if (fdpps <= 0) 185 # endif 186 { 187 (void)sprintf(device, DEVICE, unit); 188 if ((fd = refclock_open(device, SPEED232, ldisc)) != 0) 189 flags |= FLAG_TTY; 190 } 191 #endif /* TTYCLK */ 192 193 /* 194 * Allocate and initialize unit structure 195 */ 196 if (!(up = (struct atomunit *)emalloc(sizeof(struct atomunit)))) { 197 #ifdef TTYCLK 198 if (flags & FLAG_TTY) 199 (void) close(fd); 200 #endif /* TTYCLK */ 201 return (0); 202 } 203 memset((char *)up, 0, sizeof(struct atomunit)); 204 pp = peer->procptr; 205 pp->unitptr = (caddr_t)up; 206 #ifdef TTYCLK 207 if (flags & FLAG_TTY) { 208 pp->io.clock_recv = atom_receive; 209 pp->io.srcclock = (caddr_t)peer; 210 pp->io.datalen = 0; 211 pp->io.fd = fd; 212 if (!io_addclock(&pp->io)) { 213 (void) close(fd); 214 free(up); 215 return (0); 216 } 217 } 218 #endif /* TTYCLK */ 219 220 /* 221 * Initialize miscellaneous variables 222 */ 223 peer->precision = PRECISION; 224 pp->clockdesc = DESCRIPTION; 225 memcpy((char *)&pp->refid, REFID, 4); 226 up->flags = flags; 227 return (1); 228 } 229 230 231 /* 232 * atom_shutdown - shut down the clock 233 */ 234 static void 235 atom_shutdown( 236 int unit, 237 struct peer *peer 238 ) 239 { 240 register struct atomunit *up; 241 struct refclockproc *pp; 242 243 pp = peer->procptr; 244 up = (struct atomunit *)pp->unitptr; 245 #ifdef TTYCLK 246 if (up->flags & FLAG_TTY) 247 io_closeclock(&pp->io); 248 #endif /* TTYCLK */ 249 if (pps_peer == peer) 250 pps_peer = 0; 251 free(up); 252 } 253 254 255 #if defined(PPS) || defined(HAVE_PPSAPI) 256 /* 257 * atom_pps - receive data from the LDISC_PPS discipline 258 */ 259 static int 260 atom_pps( 261 struct peer *peer 262 ) 263 { 264 register struct atomunit *up; 265 struct refclockproc *pp; 266 #ifdef HAVE_PPSAPI 267 struct timespec timeout; 268 # ifdef HAVE_TIMESPEC 269 struct timespec ts; 270 # else 271 struct timeval ts; 272 # endif /* HAVE_TIMESPEC */ 273 #endif /* HAVE_PPSAPI */ 274 l_fp lftmp; 275 double doffset; 276 int i; 277 #if !defined(HAVE_PPSAPI) 278 int request = 279 # ifdef HAVE_CIOGETEV 280 CIOGETEV 281 # endif 282 # ifdef HAVE_TIOCGPPSEV 283 TIOCGPPSEV 284 # endif 285 ; 286 #endif /* HAVE_PPSAPI */ 287 288 /* 289 * This routine is called once per second when the LDISC_PPS 290 * discipline is present. It snatches the pps timestamp from the 291 * kernel and saves the sign-extended fraction in a circular 292 * buffer for processing at the next poll event. 293 */ 294 pp = peer->procptr; 295 up = (struct atomunit *)pp->unitptr; 296 297 /* 298 * Convert the timeval to l_fp and save for billboards. Sign- 299 * extend the fraction and stash in the buffer. No harm is done 300 * if previous data are overwritten. If the discipline comes bum 301 * or the data grow stale, just forget it. Round the nanoseconds 302 * to microseconds with great care. 303 */ 304 if (fdpps <= 0) 305 return (1); 306 #ifdef HAVE_PPSAPI 307 timeout.tv_sec = 0; 308 timeout.tv_nsec = 0; 309 i = up->pps_info.assert_sequence; 310 if (time_pps_fetch(fdpps, PPS_TSFMT_TSPEC, &up->pps_info, &timeout) 311 < 0) 312 return (2); 313 if (i == up->pps_info.assert_sequence) 314 return (3); 315 if (pps_assert) 316 ts = up->pps_info.assert_timestamp; 317 else 318 ts = up->pps_info.clear_timestamp; 319 pp->lastrec.l_ui = ts.tv_sec + JAN_1970; 320 ts.tv_nsec = (ts.tv_nsec + 500) / 1000; 321 if (ts.tv_nsec > 1000000) { 322 ts.tv_nsec -= 1000000; 323 ts.tv_sec++; 324 } 325 TVUTOTSF(ts.tv_nsec, pp->lastrec.l_uf); 326 #else 327 i = up->ev.serial; 328 if (ioctl(fdpps, request, (caddr_t)&up->ev) < 0) 329 return (2); 330 if (i == up->ev.serial) 331 return (3); 332 pp->lastrec.l_ui = up->ev.tv.tv_sec + JAN_1970; 333 TVUTOTSF(up->ev.tv.tv_usec, pp->lastrec.l_uf); 334 #endif /* HAVE_PPSAPI */ 335 up->flags |= FLAG_PPS; 336 L_CLR(&lftmp); 337 L_ADDF(&lftmp, pp->lastrec.l_f); 338 LFPTOD(&lftmp, doffset); 339 SAMPLE(-doffset + pp->fudgetime1); 340 return (0); 341 } 342 #endif /* PPS || HAVE_PPSAPI */ 343 344 #ifdef TTYCLK 345 /* 346 * atom_receive - receive data from the LDISC_CLK discipline 347 */ 348 static void 349 atom_receive( 350 struct recvbuf *rbufp 351 ) 352 { 353 register struct atomunit *up; 354 struct refclockproc *pp; 355 struct peer *peer; 356 l_fp lftmp; 357 double doffset; 358 359 /* 360 * This routine is called once per second when the serial 361 * interface is in use. It snatches the timestamp from the 362 * buffer and saves the sign-extended fraction in a circular 363 * buffer for processing at the next poll event. 364 */ 365 peer = (struct peer *)rbufp->recv_srcclock; 366 pp = peer->procptr; 367 up = (struct atomunit *)pp->unitptr; 368 pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, 369 &pp->lastrec); 370 371 /* 372 * Save the timestamp for billboards. Sign-extend the fraction 373 * and stash in the buffer. No harm is done if previous data are 374 * overwritten. Do this only if the ppsclock gizmo is not 375 * working. 376 */ 377 if (up->flags & FLAG_PPS) 378 return; 379 L_CLR(&lftmp); 380 L_ADDF(&lftmp, pp->lastrec.l_f); 381 LFPTOD(&lftmp, doffset); 382 SAMPLE(-doffset + pp->fudgetime1); 383 } 384 #endif /* TTYCLK */ 385 386 /* 387 * pps_sample - receive PPS data from some other clock driver 388 */ 389 int 390 pps_sample( 391 l_fp *offset 392 ) 393 { 394 register struct peer *peer; 395 register struct atomunit *up; 396 struct refclockproc *pp; 397 l_fp lftmp; 398 double doffset; 399 400 /* 401 * This routine is called once per second when the external 402 * clock driver processes PPS information. It processes the pps 403 * timestamp and saves the sign-extended fraction in a circular 404 * buffer for processing at the next poll event. 405 */ 406 peer = pps_peer; 407 if (peer == 0) /* nobody home */ 408 return 1; 409 pp = peer->procptr; 410 up = (struct atomunit *)pp->unitptr; 411 412 /* 413 * Convert the timeval to l_fp and save for billboards. Sign- 414 * extend the fraction and stash in the buffer. No harm is done 415 * if previous data are overwritten. If the discipline comes bum 416 * or the data grow stale, just forget it. 417 */ 418 up->flags |= FLAG_AUX; 419 pp->lastrec = *offset; 420 L_CLR(&lftmp); 421 L_ADDF(&lftmp, pp->lastrec.l_f); 422 LFPTOD(&lftmp, doffset); 423 SAMPLE(-doffset + pp->fudgetime1); 424 return (0); 425 } 426 427 /* 428 * atom_poll - called by the transmit procedure 429 */ 430 static void 431 atom_poll( 432 int unit, 433 struct peer *peer 434 ) 435 { 436 #if defined(PPS) || defined(HAVE_PPSAPI) 437 register struct atomunit *up; 438 #endif /* PPS || HAVE_PPSAPI */ 439 struct refclockproc *pp; 440 441 /* 442 * Accumulate samples in the median filter. At the end of each 443 * poll interval, do a little bookeeping and process the 444 * samples. 445 */ 446 pp = peer->procptr; 447 #if defined(PPS) || defined(HAVE_PPSAPI) 448 up = (struct atomunit *)pp->unitptr; 449 if (!(up->flags & !(FLAG_AUX | FLAG_TTY))) { 450 int err; 451 452 err = atom_pps(peer); 453 if (err > 0) { 454 refclock_report(peer, CEVNT_FAULT); 455 return; 456 } 457 } 458 #endif /* PPS || HAVE_PPSAPI */ 459 pp->polls++; 460 if (peer->burst > 0) 461 return; 462 if (pp->coderecv == pp->codeproc) { 463 refclock_report(peer, CEVNT_TIMEOUT); 464 return; 465 } 466 467 /* 468 * Valid time (leap bits zero) is returned only if the prefer 469 * peer has survived the intersection algorithm and within 470 * clock_max of local time and not too long ago. This ensures 471 * the pps time is within +-0.5 s of the local time and the 472 * seconds numbering is unambiguous. 473 */ 474 if (pps_update) { 475 pp->leap = LEAP_NOWARNING; 476 } else { 477 pp->leap = LEAP_NOTINSYNC; 478 return; 479 } 480 pp->variance = 0; 481 record_clock_stats(&peer->srcadr, pp->a_lastcode); 482 refclock_receive(peer); 483 peer->burst = MAXSTAGE; 484 } 485 486 #else 487 int refclock_atom_bs; 488 #endif /* REFCLOCK */ 489