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