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