/contrib/ntp/ntpd/refclock_leitch.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 626 lines · 469 code · 55 blank · 102 comment · 68 complexity · 61579f995b06198f02d11840ce050bd0 MD5 · raw file

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