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