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