1 /* 2 * refclock_leitch - clock driver for the Leitch CSD-5300 Master Clock 3 */ 4 5 #ifdef HAVE_CONFIG_H 6 # include <config.h> 7 #endif 8 9 #if defined(REFCLOCK) && defined(CLOCK_LEITCH) 10 11 #include "ntpd.h" 12 #include "ntp_io.h" 13 #include "ntp_refclock.h" 14 #include "ntp_unixtime.h" 15 16 #include <stdio.h> 17 #include <ctype.h> 18 19 #ifdef STREAM 20 #include <stropts.h> 21 #if defined(LEITCHCLK) 22 #include <sys/clkdefs.h> 23 #endif /* LEITCHCLK */ 24 #endif /* STREAM */ 25 26 #include "ntp_stdlib.h" 27 28 29 /* 30 * Driver for Leitch CSD-5300 Master Clock System 31 * 32 * COMMANDS: 33 * DATE: D <CR> 34 * TIME: T <CR> 35 * STATUS: S <CR> 36 * LOOP: L <CR> 37 * 38 * FORMAT: 39 * DATE: YYMMDD<CR> 40 * TIME: <CR>/HHMMSS <CR>/HHMMSS <CR>/HHMMSS <CR>/ 41 * second bondaried on the stop bit of the <CR> 42 * second boundaries at '/' above. 43 * STATUS: G (good), D (diag fail), T (time not provided) or 44 * P (last phone update failed) 45 */ 46 #define MAXUNITS 1 /* max number of LEITCH units */ 47 #define LEITCHREFID "ATOM" /* reference id */ 48 #define LEITCH_DESCRIPTION "Leitch: CSD 5300 Master Clock System Driver" 49 #define LEITCH232 "/dev/leitch%d" /* name of radio device */ 50 #define SPEED232 B300 /* uart speed (300 baud) */ 51 #define leitch_send(A,M) \ 52 if (debug) fprintf(stderr,"write leitch %s\n",M); \ 53 if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\ 54 if (debug) \ 55 fprintf(stderr, "leitch_send: unit %d send failed\n", A->unit); \ 56 else \ 57 msyslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);} 58 59 #define STATE_IDLE 0 60 #define STATE_DATE 1 61 #define STATE_TIME1 2 62 #define STATE_TIME2 3 63 #define STATE_TIME3 4 64 65 /* 66 * LEITCH unit control structure 67 */ 68 struct leitchunit { 69 struct peer *peer; 70 struct refclockio leitchio; 71 u_char unit; 72 short year; 73 short yearday; 74 short month; 75 short day; 76 short hour; 77 short second; 78 short minute; 79 short state; 80 u_short fudge1; 81 l_fp reftime1; 82 l_fp reftime2; 83 l_fp reftime3; 84 l_fp codetime1; 85 l_fp codetime2; 86 l_fp codetime3; 87 u_long yearstart; 88 }; 89 90 /* 91 * Function prototypes 92 */ 93 static void leitch_init P((void)); 94 static int leitch_start P((int, struct peer *)); 95 static void leitch_shutdown P((int, struct peer *)); 96 static void leitch_poll P((int, struct peer *)); 97 static void leitch_control P((int, struct refclockstat *, struct refclockstat *, struct peer *)); 98 #define leitch_buginfo noentry 99 static void leitch_receive P((struct recvbuf *)); 100 static void leitch_process P((struct leitchunit *)); 101 #if 0 102 static void leitch_timeout P((struct peer *)); 103 #endif 104 static int leitch_get_date P((struct recvbuf *, struct leitchunit *)); 105 static int leitch_get_time P((struct recvbuf *, struct leitchunit *, int)); 106 static int days_per_year P((int)); 107 108 static struct leitchunit leitchunits[MAXUNITS]; 109 static u_char unitinuse[MAXUNITS]; 110 static u_char stratumtouse[MAXUNITS]; 111 static u_int32 refid[MAXUNITS]; 112 113 static char days_in_month [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 114 115 /* 116 * Transfer vector 117 */ 118 struct refclock refclock_leitch = { 119 leitch_start, leitch_shutdown, leitch_poll, 120 leitch_control, leitch_init, leitch_buginfo, NOFLAGS 121 }; 122 123 /* 124 * leitch_init - initialize internal leitch driver data 125 */ 126 static void 127 leitch_init(void) 128 { 129 int i; 130 131 memset((char*)leitchunits, 0, sizeof(leitchunits)); 132 memset((char*)unitinuse, 0, sizeof(unitinuse)); 133 for (i = 0; i < MAXUNITS; i++) 134 memcpy((char *)&refid[i], LEITCHREFID, 4); 135 } 136 137 /* 138 * leitch_shutdown - shut down a LEITCH clock 139 */ 140 static void 141 leitch_shutdown( 142 int unit, 143 struct peer *peer 144 ) 145 { 146 #ifdef DEBUG 147 if (debug) 148 fprintf(stderr, "leitch_shutdown()\n"); 149 #endif 150 } 151 152 /* 153 * leitch_poll - called by the transmit procedure 154 */ 155 static void 156 leitch_poll( 157 int unit, 158 struct peer *peer 159 ) 160 { 161 struct leitchunit *leitch; 162 163 /* start the state machine rolling */ 164 165 #ifdef DEBUG 166 if (debug) 167 fprintf(stderr, "leitch_poll()\n"); 168 #endif 169 if (unit > MAXUNITS) { 170 /* XXXX syslog it */ 171 return; 172 } 173 174 leitch = &leitchunits[unit]; 175 176 if (leitch->state != STATE_IDLE) { 177 /* reset and wait for next poll */ 178 /* XXXX syslog it */ 179 leitch->state = STATE_IDLE; 180 } else { 181 leitch_send(leitch,"D\r"); 182 leitch->state = STATE_DATE; 183 } 184 } 185 186 static void 187 leitch_control( 188 int unit, 189 struct refclockstat *in, 190 struct refclockstat *out, 191 struct peer *passed_peer 192 ) 193 { 194 if (unit >= MAXUNITS) { 195 msyslog(LOG_ERR, 196 "leitch_control: unit %d invalid", unit); 197 return; 198 } 199 200 if (in) { 201 if (in->haveflags & CLK_HAVEVAL1) 202 stratumtouse[unit] = (u_char)(in->fudgeval1); 203 if (in->haveflags & CLK_HAVEVAL2) 204 refid[unit] = in->fudgeval2; 205 if (unitinuse[unit]) { 206 struct peer *peer; 207 208 peer = (&leitchunits[unit])->peer; 209 peer->stratum = stratumtouse[unit]; 210 peer->refid = refid[unit]; 211 } 212 } 213 214 if (out) { 215 memset((char *)out, 0, sizeof (struct refclockstat)); 216 out->type = REFCLK_ATOM_LEITCH; 217 out->haveflags = CLK_HAVEVAL1 | CLK_HAVEVAL2; 218 out->fudgeval1 = (int32)stratumtouse[unit]; 219 out->fudgeval2 = refid[unit]; 220 out->p_lastcode = ""; 221 out->clockdesc = LEITCH_DESCRIPTION; 222 } 223 } 224 225 /* 226 * leitch_start - open the LEITCH devices and initialize data for processing 227 */ 228 static int 229 leitch_start( 230 int unit, 231 struct peer *peer 232 ) 233 { 234 struct leitchunit *leitch; 235 int fd232; 236 char leitchdev[20]; 237 238 /* 239 * Check configuration info. 240 */ 241 if (unit >= MAXUNITS) { 242 msyslog(LOG_ERR, "leitch_start: unit %d invalid", unit); 243 return (0); 244 } 245 246 if (unitinuse[unit]) { 247 msyslog(LOG_ERR, "leitch_start: unit %d in use", unit); 248 return (0); 249 } 250 251 /* 252 * Open serial port. 253 */ 254 (void) sprintf(leitchdev, LEITCH232, unit); 255 fd232 = open(leitchdev, O_RDWR, 0777); 256 if (fd232 == -1) { 257 msyslog(LOG_ERR, 258 "leitch_start: open of %s: %m", leitchdev); 259 return (0); 260 } 261 262 leitch = &leitchunits[unit]; 263 memset((char*)leitch, 0, sizeof(*leitch)); 264 265 #if defined(HAVE_SYSV_TTYS) 266 /* 267 * System V serial line parameters (termio interface) 268 * 269 */ 270 { struct termio ttyb; 271 if (ioctl(fd232, TCGETA, &ttyb) < 0) { 272 msyslog(LOG_ERR, 273 "leitch_start: ioctl(%s, TCGETA): %m", leitchdev); 274 goto screwed; 275 } 276 ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL; 277 ttyb.c_oflag = 0; 278 ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD; 279 ttyb.c_lflag = ICANON; 280 ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0'; 281 if (ioctl(fd232, TCSETA, &ttyb) < 0) { 282 msyslog(LOG_ERR, 283 "leitch_start: ioctl(%s, TCSETA): %m", leitchdev); 284 goto screwed; 285 } 286 } 287 #endif /* HAVE_SYSV_TTYS */ 288 #if defined(HAVE_TERMIOS) 289 /* 290 * POSIX serial line parameters (termios interface) 291 * 292 * The LEITCHCLK option provides timestamping at the driver level. 293 * It requires the tty_clk streams module. 294 */ 295 { struct termios ttyb, *ttyp; 296 297 ttyp = &ttyb; 298 if (tcgetattr(fd232, ttyp) < 0) { 299 msyslog(LOG_ERR, 300 "leitch_start: tcgetattr(%s): %m", leitchdev); 301 goto screwed; 302 } 303 ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL; 304 ttyp->c_oflag = 0; 305 ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD; 306 ttyp->c_lflag = ICANON; 307 ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0'; 308 if (tcsetattr(fd232, TCSANOW, ttyp) < 0) { 309 msyslog(LOG_ERR, 310 "leitch_start: tcsetattr(%s): %m", leitchdev); 311 goto screwed; 312 } 313 if (tcflush(fd232, TCIOFLUSH) < 0) { 314 msyslog(LOG_ERR, 315 "leitch_start: tcflush(%s): %m", leitchdev); 316 goto screwed; 317 } 318 } 319 #endif /* HAVE_TERMIOS */ 320 #ifdef STREAM 321 #if defined(LEITCHCLK) 322 if (ioctl(fd232, I_PUSH, "clk") < 0) 323 msyslog(LOG_ERR, 324 "leitch_start: ioctl(%s, I_PUSH, clk): %m", leitchdev); 325 if (ioctl(fd232, CLK_SETSTR, "\n") < 0) 326 msyslog(LOG_ERR, 327 "leitch_start: ioctl(%s, CLK_SETSTR): %m", leitchdev); 328 #endif /* LEITCHCLK */ 329 #endif /* STREAM */ 330 #if defined(HAVE_BSD_TTYS) 331 /* 332 * 4.3bsd serial line parameters (sgttyb interface) 333 * 334 * The LEITCHCLK option provides timestamping at the driver level. 335 * It requires the tty_clk line discipline and 4.3bsd or later. 336 */ 337 { struct sgttyb ttyb; 338 #if defined(LEITCHCLK) 339 int ldisc = CLKLDISC; 340 #endif /* LEITCHCLK */ 341 342 if (ioctl(fd232, TIOCGETP, &ttyb) < 0) { 343 msyslog(LOG_ERR, 344 "leitch_start: ioctl(%s, TIOCGETP): %m", leitchdev); 345 goto screwed; 346 } 347 ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232; 348 #if defined(LEITCHCLK) 349 ttyb.sg_erase = ttyb.sg_kill = '\r'; 350 ttyb.sg_flags = RAW; 351 #else 352 ttyb.sg_erase = ttyb.sg_kill = '\0'; 353 ttyb.sg_flags = EVENP|ODDP|CRMOD; 354 #endif /* LEITCHCLK */ 355 if (ioctl(fd232, TIOCSETP, &ttyb) < 0) { 356 msyslog(LOG_ERR, 357 "leitch_start: ioctl(%s, TIOCSETP): %m", leitchdev); 358 goto screwed; 359 } 360 #if defined(LEITCHCLK) 361 if (ioctl(fd232, TIOCSETD, &ldisc) < 0) { 362 msyslog(LOG_ERR, 363 "leitch_start: ioctl(%s, TIOCSETD): %m",leitchdev); 364 goto screwed; 365 } 366 #endif /* LEITCHCLK */ 367 } 368 #endif /* HAVE_BSD_TTYS */ 369 370 /* 371 * Set up the structures 372 */ 373 leitch->peer = peer; 374 leitch->unit = unit; 375 leitch->state = STATE_IDLE; 376 leitch->fudge1 = 15; /* 15ms */ 377 378 leitch->leitchio.clock_recv = leitch_receive; 379 leitch->leitchio.srcclock = (caddr_t) leitch; 380 leitch->leitchio.datalen = 0; 381 leitch->leitchio.fd = fd232; 382 if (!io_addclock(&leitch->leitchio)) { 383 goto screwed; 384 } 385 386 /* 387 * All done. Initialize a few random peer variables, then 388 * return success. 389 */ 390 peer->precision = 0; 391 peer->stratum = stratumtouse[unit]; 392 peer->refid = refid[unit]; 393 unitinuse[unit] = 1; 394 return(1); 395 396 /* 397 * Something broke; abandon ship. 398 */ 399 screwed: 400 close(fd232); 401 return(0); 402 } 403 404 /* 405 * leitch_receive - receive data from the serial interface on a leitch 406 * clock 407 */ 408 static void 409 leitch_receive( 410 struct recvbuf *rbufp 411 ) 412 { 413 struct leitchunit *leitch = (struct leitchunit *)rbufp->recv_srcclock; 414 415 #ifdef DEBUG 416 if (debug) 417 fprintf(stderr, "leitch_recieve(%*.*s)\n", 418 rbufp->recv_length, rbufp->recv_length, 419 rbufp->recv_buffer); 420 #endif 421 if (rbufp->recv_length != 7) 422 return; /* The date is return with a trailing newline, 423 discard it. */ 424 425 switch (leitch->state) { 426 case STATE_IDLE: /* unexpected, discard and resync */ 427 return; 428 case STATE_DATE: 429 if (!leitch_get_date(rbufp,leitch)) { 430 leitch->state = STATE_IDLE; 431 break; 432 } 433 leitch_send(leitch,"T\r"); 434 #ifdef DEBUG 435 if (debug) 436 fprintf(stderr, "%u\n",leitch->yearday); 437 #endif 438 leitch->state = STATE_TIME1; 439 break; 440 case STATE_TIME1: 441 if (!leitch_get_time(rbufp,leitch,1)) { 442 } 443 if (!clocktime(leitch->yearday,leitch->hour,leitch->minute, 444 leitch->second, 1, rbufp->recv_time.l_ui, 445 &leitch->yearstart, &leitch->reftime1.l_ui)) { 446 leitch->state = STATE_IDLE; 447 break; 448 } 449 #ifdef DEBUG 450 if (debug) 451 fprintf(stderr, "%lu\n", (u_long)leitch->reftime1.l_ui); 452 #endif 453 MSUTOTSF(leitch->fudge1, leitch->reftime1.l_uf); 454 leitch->codetime1 = rbufp->recv_time; 455 leitch->state = STATE_TIME2; 456 break; 457 case STATE_TIME2: 458 if (!leitch_get_time(rbufp,leitch,2)) { 459 } 460 if (!clocktime(leitch->yearday,leitch->hour,leitch->minute, 461 leitch->second, 1, rbufp->recv_time.l_ui, 462 &leitch->yearstart, &leitch->reftime2.l_ui)) { 463 leitch->state = STATE_IDLE; 464 break; 465 } 466 #ifdef DEBUG 467 if (debug) 468 fprintf(stderr, "%lu\n", (u_long)leitch->reftime2.l_ui); 469 #endif 470 MSUTOTSF(leitch->fudge1, leitch->reftime2.l_uf); 471 leitch->codetime2 = rbufp->recv_time; 472 leitch->state = STATE_TIME3; 473 break; 474 case STATE_TIME3: 475 if (!leitch_get_time(rbufp,leitch,3)) { 476 } 477 if (!clocktime(leitch->yearday,leitch->hour,leitch->minute, 478 leitch->second, GMT, rbufp->recv_time.l_ui, 479 &leitch->yearstart, &leitch->reftime3.l_ui)) { 480 leitch->state = STATE_IDLE; 481 break; 482 } 483 #ifdef DEBUG 484 if (debug) 485 fprintf(stderr, "%lu\n", (u_long)leitch->reftime3.l_ui); 486 #endif 487 MSUTOTSF(leitch->fudge1, leitch->reftime3.l_uf); 488 leitch->codetime3 = rbufp->recv_time; 489 leitch_process(leitch); 490 leitch->state = STATE_IDLE; 491 break; 492 default: 493 msyslog(LOG_ERR, 494 "leitech_receive: invalid state %d unit %d", 495 leitch->state, leitch->unit); 496 } 497 } 498 499 /* 500 * leitch_process - process a pile of samples from the clock 501 * 502 * This routine uses a three-stage median filter to calculate offset and 503 * dispersion. reduce jitter. The dispersion is calculated as the span 504 * of the filter (max - min), unless the quality character (format 2) is 505 * non-blank, in which case the dispersion is calculated on the basis of 506 * the inherent tolerance of the internal radio oscillator, which is 507 * +-2e-5 according to the radio specifications. 508 */ 509 static void 510 leitch_process( 511 struct leitchunit *leitch 512 ) 513 { 514 l_fp off; 515 l_fp tmp_fp; 516 /*double doffset;*/ 517 518 off = leitch->reftime1; 519 L_SUB(&off,&leitch->codetime1); 520 tmp_fp = leitch->reftime2; 521 L_SUB(&tmp_fp,&leitch->codetime2); 522 if (L_ISGEQ(&off,&tmp_fp)) 523 off = tmp_fp; 524 tmp_fp = leitch->reftime3; 525 L_SUB(&tmp_fp,&leitch->codetime3); 526 527 if (L_ISGEQ(&off,&tmp_fp)) 528 off = tmp_fp; 529 /*LFPTOD(&off, doffset);*/ 530 refclock_receive(leitch->peer); 531 } 532 533 /* 534 * days_per_year 535 */ 536 static int 537 days_per_year( 538 int year 539 ) 540 { 541 if (year%4) { /* not a potential leap year */ 542 return (365); 543 } else { 544 if (year % 100) { /* is a leap year */ 545 return (366); 546 } else { 547 if (year % 400) { 548 return (365); 549 } else { 550 return (366); 551 } 552 } 553 } 554 } 555 556 static int 557 leitch_get_date( 558 struct recvbuf *rbufp, 559 struct leitchunit *leitch 560 ) 561 { 562 int i; 563 564 if (rbufp->recv_length < 6) 565 return(0); 566 #undef BAD /* confict: defined as (-1) in AIX sys/param.h */ 567 #define BAD(A) (rbufp->recv_buffer[A] < '0') || (rbufp->recv_buffer[A] > '9') 568 if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5)) 569 return(0); 570 #define ATOB(A) ((rbufp->recv_buffer[A])-'0') 571 leitch->year = ATOB(0)*10 + ATOB(1); 572 leitch->month = ATOB(2)*10 + ATOB(3); 573 leitch->day = ATOB(4)*10 + ATOB(5); 574 575 /* sanity checks */ 576 if (leitch->month > 12) 577 return(0); 578 if (leitch->day > days_in_month[leitch->month-1]) 579 return(0); 580 581 /* calculate yearday */ 582 i = 0; 583 leitch->yearday = leitch->day; 584 585 while ( i < (leitch->month-1) ) 586 leitch->yearday += days_in_month[i++]; 587 588 if ((days_per_year((leitch->year>90?1900:2000)+leitch->year)==365) && 589 leitch->month > 2) 590 leitch->yearday--; 591 592 return(1); 593 } 594 595 /* 596 * leitch_get_time 597 */ 598 static int 599 leitch_get_time( 600 struct recvbuf *rbufp, 601 struct leitchunit *leitch, 602 int which 603 ) 604 { 605 if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5)) 606 return(0); 607 leitch->hour = ATOB(0)*10 +ATOB(1); 608 leitch->minute = ATOB(2)*10 +ATOB(3); 609 leitch->second = ATOB(4)*10 +ATOB(5); 610 611 if ((leitch->hour > 23) || (leitch->minute > 60) || 612 (leitch->second > 60)) 613 return(0); 614 return(1); 615 } 616 617 #else 618 int refclock_leitch_bs; 619 #endif /* REFCLOCK */ 620