Annotation of embedaddon/ntp/ntpd/refclock_zyfer.c, revision 1.1.1.1
1.1 misho 1: /*
2: * refclock_zyfer - clock driver for the Zyfer GPSTarplus Clock
3: *
4: * Harlan Stenn, Jan 2002
5: */
6:
7: #ifdef HAVE_CONFIG_H
8: #include <config.h>
9: #endif
10:
11: #if defined(REFCLOCK) && defined(CLOCK_ZYFER)
12:
13: #include "ntpd.h"
14: #include "ntp_io.h"
15: #include "ntp_refclock.h"
16: #include "ntp_stdlib.h"
17: #include "ntp_unixtime.h"
18:
19: #include <stdio.h>
20: #include <ctype.h>
21:
22: #ifdef HAVE_SYS_TERMIOS_H
23: # include <sys/termios.h>
24: #endif
25: #ifdef HAVE_SYS_PPSCLOCK_H
26: # include <sys/ppsclock.h>
27: #endif
28:
29: /*
30: * This driver provides support for the TOD serial port of a Zyfer GPStarplus.
31: * This clock also provides PPS as well as IRIG outputs.
32: * Precision is limited by the serial driver, etc.
33: *
34: * If I was really brave I'd hack/generalize the serial driver to deal
35: * with arbitrary on-time characters. This clock *begins* the stream with
36: * `!`, the on-time character, and the string is *not* EOL-terminated.
37: *
38: * Configure the beast for 9600, 8N1. While I see leap-second stuff
39: * in the documentation, the published specs on the TOD format only show
40: * the seconds going to '59'. I see no leap warning in the TOD format.
41: *
42: * The clock sends the following message once per second:
43: *
44: * !TIME,2002,017,07,59,32,2,4,1
45: * YYYY DDD HH MM SS m T O
46: *
47: * ! On-time character
48: * YYYY Year
49: * DDD 001-366 Day of Year
50: * HH 00-23 Hour
51: * MM 00-59 Minute
52: * SS 00-59 Second (probably 00-60)
53: * m 1-5 Time Mode:
54: * 1 = GPS time
55: * 2 = UTC time
56: * 3 = LGPS time (Local GPS)
57: * 4 = LUTC time (Local UTC)
58: * 5 = Manual time
59: * T 4-9 Time Figure Of Merit:
60: * 4 x <= 1us
61: * 5 1us < x <= 10 us
62: * 6 10us < x <= 100us
63: * 7 100us < x <= 1ms
64: * 8 1ms < x <= 10ms
65: * 9 10ms < x
66: * O 0-4 Operation Mode:
67: * 0 Warm-up
68: * 1 Time Locked
69: * 2 Coasting
70: * 3 Recovering
71: * 4 Manual
72: *
73: */
74:
75: /*
76: * Interface definitions
77: */
78: #define DEVICE "/dev/zyfer%d" /* device name and unit */
79: #define SPEED232 B9600 /* uart speed (9600 baud) */
80: #define PRECISION (-20) /* precision assumed (about 1 us) */
81: #define REFID "GPS\0" /* reference ID */
82: #define DESCRIPTION "Zyfer GPStarplus" /* WRU */
83:
84: #define LENZYFER 29 /* timecode length */
85:
86: /*
87: * Unit control structure
88: */
89: struct zyferunit {
90: u_char Rcvbuf[LENZYFER + 1];
91: u_char polled; /* poll message flag */
92: int pollcnt;
93: l_fp tstamp; /* timestamp of last poll */
94: int Rcvptr;
95: };
96:
97: /*
98: * Function prototypes
99: */
100: static int zyfer_start (int, struct peer *);
101: static void zyfer_shutdown (int, struct peer *);
102: static void zyfer_receive (struct recvbuf *);
103: static void zyfer_poll (int, struct peer *);
104:
105: /*
106: * Transfer vector
107: */
108: struct refclock refclock_zyfer = {
109: zyfer_start, /* start up driver */
110: zyfer_shutdown, /* shut down driver */
111: zyfer_poll, /* transmit poll message */
112: noentry, /* not used (old zyfer_control) */
113: noentry, /* initialize driver (not used) */
114: noentry, /* not used (old zyfer_buginfo) */
115: NOFLAGS /* not used */
116: };
117:
118:
119: /*
120: * zyfer_start - open the devices and initialize data for processing
121: */
122: static int
123: zyfer_start(
124: int unit,
125: struct peer *peer
126: )
127: {
128: register struct zyferunit *up;
129: struct refclockproc *pp;
130: int fd;
131: char device[20];
132:
133: /*
134: * Open serial port.
135: * Something like LDISC_ACTS that looked for ! would be nice...
136: */
137: (void)sprintf(device, DEVICE, unit);
138: if ( !(fd = refclock_open(device, SPEED232, LDISC_RAW)) )
139: return (0);
140:
141: msyslog(LOG_NOTICE, "zyfer(%d) fd: %d dev <%s>", unit, fd, device);
142:
143: /*
144: * Allocate and initialize unit structure
145: */
146: if (!(up = (struct zyferunit *)
147: emalloc(sizeof(struct zyferunit)))) {
148: (void) close(fd);
149: return (0);
150: }
151: memset((char *)up, 0, sizeof(struct zyferunit));
152: pp = peer->procptr;
153: pp->io.clock_recv = zyfer_receive;
154: pp->io.srcclock = (caddr_t)peer;
155: pp->io.datalen = 0;
156: pp->io.fd = fd;
157: if (!io_addclock(&pp->io)) {
158: (void) close(fd);
159: free(up);
160: return (0);
161: }
162: pp->unitptr = (caddr_t)up;
163:
164: /*
165: * Initialize miscellaneous variables
166: */
167: peer->precision = PRECISION;
168: pp->clockdesc = DESCRIPTION;
169: memcpy((char *)&pp->refid, REFID, 4);
170: up->pollcnt = 2;
171: up->polled = 0; /* May not be needed... */
172:
173: return (1);
174: }
175:
176:
177: /*
178: * zyfer_shutdown - shut down the clock
179: */
180: static void
181: zyfer_shutdown(
182: int unit,
183: struct peer *peer
184: )
185: {
186: register struct zyferunit *up;
187: struct refclockproc *pp;
188:
189: pp = peer->procptr;
190: up = (struct zyferunit *)pp->unitptr;
191: io_closeclock(&pp->io);
192: free(up);
193: }
194:
195:
196: /*
197: * zyfer_receive - receive data from the serial interface
198: */
199: static void
200: zyfer_receive(
201: struct recvbuf *rbufp
202: )
203: {
204: register struct zyferunit *up;
205: struct refclockproc *pp;
206: struct peer *peer;
207: int tmode; /* Time mode */
208: int tfom; /* Time Figure Of Merit */
209: int omode; /* Operation mode */
210: u_char *p;
211: #ifdef PPS
212: struct ppsclockev ppsev;
213: int request;
214: #ifdef HAVE_CIOGETEV
215: request = CIOGETEV;
216: #endif
217: #ifdef HAVE_TIOCGPPSEV
218: request = TIOCGPPSEV;
219: #endif
220: #endif /* PPS */
221:
222: peer = (struct peer *)rbufp->recv_srcclock;
223: pp = peer->procptr;
224: up = (struct zyferunit *)pp->unitptr;
225: p = (u_char *) &rbufp->recv_space;
226: /*
227: * If lencode is 0:
228: * - if *rbufp->recv_space is !
229: * - - call refclock_gtlin to get things going
230: * - else flush
231: * else stuff it on the end of lastcode
232: * If we don't have LENZYFER bytes
233: * - wait for more data
234: * Crack the beast, and if it's OK, process it.
235: *
236: * We use refclock_gtlin() because we might use LDISC_CLK.
237: *
238: * Under FreeBSD, we get the ! followed by two 14-byte packets.
239: */
240:
241: if (pp->lencode >= LENZYFER)
242: pp->lencode = 0;
243:
244: if (!pp->lencode) {
245: if (*p == '!')
246: pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode,
247: BMAX, &pp->lastrec);
248: else
249: return;
250: } else {
251: memcpy(pp->a_lastcode + pp->lencode, p, rbufp->recv_length);
252: pp->lencode += rbufp->recv_length;
253: pp->a_lastcode[pp->lencode] = '\0';
254: }
255:
256: if (pp->lencode < LENZYFER)
257: return;
258:
259: record_clock_stats(&peer->srcadr, pp->a_lastcode);
260:
261: /*
262: * We get down to business, check the timecode format and decode
263: * its contents. If the timecode has invalid length or is not in
264: * proper format, we declare bad format and exit.
265: */
266:
267: if (pp->lencode != LENZYFER) {
268: refclock_report(peer, CEVNT_BADTIME);
269: return;
270: }
271:
272: /*
273: * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1"
274: */
275: if (sscanf(pp->a_lastcode, "!TIME,%4d,%3d,%2d,%2d,%2d,%d,%d,%d",
276: &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second,
277: &tmode, &tfom, &omode) != 8) {
278: refclock_report(peer, CEVNT_BADREPLY);
279: return;
280: }
281:
282: if (tmode != 2) {
283: refclock_report(peer, CEVNT_BADTIME);
284: return;
285: }
286:
287: /* Should we make sure tfom is 4? */
288:
289: if (omode != 1) {
290: pp->leap = LEAP_NOTINSYNC;
291: return;
292: }
293: #ifdef PPS
294: if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) {
295: ppsev.tv.tv_sec += (u_int32) JAN_1970;
296: TVTOTS(&ppsev.tv,&up->tstamp);
297: }
298: /* record the last ppsclock event time stamp */
299: pp->lastrec = up->tstamp;
300: #endif /* PPS */
301: if (!refclock_process(pp)) {
302: refclock_report(peer, CEVNT_BADTIME);
303: return;
304: }
305:
306: /*
307: * Good place for record_clock_stats()
308: */
309: up->pollcnt = 2;
310:
311: if (up->polled) {
312: up->polled = 0;
313: refclock_receive(peer);
314: }
315: }
316:
317:
318: /*
319: * zyfer_poll - called by the transmit procedure
320: */
321: static void
322: zyfer_poll(
323: int unit,
324: struct peer *peer
325: )
326: {
327: register struct zyferunit *up;
328: struct refclockproc *pp;
329:
330: /*
331: * We don't really do anything here, except arm the receiving
332: * side to capture a sample and check for timeouts.
333: */
334: pp = peer->procptr;
335: up = (struct zyferunit *)pp->unitptr;
336: if (!up->pollcnt)
337: refclock_report(peer, CEVNT_TIMEOUT);
338: else
339: up->pollcnt--;
340: pp->polls++;
341: up->polled = 1;
342: }
343:
344: #else
345: int refclock_zyfer_bs;
346: #endif /* REFCLOCK */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>