xref: /freebsd/contrib/ntp/ntpd/refclock_gpsvme.c (revision 3642298923e528d795e3a30ec165d2b469e28b40)
1 /*
2  * refclock_gpsvme.c  NTP clock driver for the TrueTime GPS-VME
3  * R. Schmidt, Time Service, US Naval Obs.  res@tuttle.usno.navy.mil
4  *
5  * The refclock type has been defined as 16 (until new id assigned).
6  * These DEFS are included in the Makefile:
7  *      DEFS= -DHAVE_TERMIOS -DSYS_HPUX=9
8  *      DEFS_LOCAL=  -DREFCLOCK
9  *      CLOCKDEFS=   -DGPSVME
10  *  The file map_vme.c does the VME memory mapping, and includes vme_init().
11  *  map_vme.c is HP-UX specific, because HPUX cannot mmap() device files! Boo!
12  *  The file gps.h   provides TrueTime register info.
13  */
14 #ifdef HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17 
18 #if defined(REFCLOCK) && defined(CLOCK_GPSVME)
19 
20 #include "ntpd.h"
21 #include "ntp_io.h"
22 #include "ntp_refclock.h"
23 #include "ntp_unixtime.h"
24 #include "ntp_stdlib.h"
25 
26 #include <stdio.h>
27 #include <syslog.h>
28 #include <ctype.h>
29 
30 #include "gps.h"
31 #include "/etc/conf/h/io.h"
32 
33 /* GLOBAL STUFF BY RES */
34 
35 #include <time.h>
36 
37 #define PRIO    120		/* set the realtime priority */
38 #define NREGS	7		/* number of registers we will use */
39 
40 extern int init_vme();		/* This is just a call to map_vme() */
41 				/* It doesn't have to be extern */
42 				/* the map_vme() call */
43 extern unsigned short *greg[NREGS];   /* made extern to avoid being in both map_vme.c and this file */
44 extern void *gps_base;                /*  mjb lmco 12/20/99 */
45 
46 extern caddr_t map_vme ();
47 extern void unmap_vme();	/* Unmaps the VME space */
48 
49 struct vmedate {		/* structure needed by ntp */
50 	unsigned short year;	/* *tptr is a pointer to this */
51 	unsigned short doy;
52 	unsigned short hr;
53 	unsigned short mn;
54 	unsigned short sec;
55 	unsigned long frac;
56 	unsigned short status;
57 };
58 
59 struct vmedate *get_gpsvme_time();
60 struct vmedate * time_vme;  /* added to emulate LM refclock_gpsvme
61                   (Made global per RES suggestion to fix mem leak DW lmco)  mjb lmco 12/15/99 */
62 
63 /* END OF STUFF FROM RES */
64 
65 /*
66  * Definitions
67  */
68 #define MAXUNITS 2              /* max number of VME units */
69 #define BMAX  50        /* timecode buffer length */
70 
71 /*
72  * VME interface parameters.
73  */
74 #define VMEPRECISION    (-21)      /* precision assumed (1 us) */
75 #define USNOREFID       "USNO\0"  /* Or whatever? */
76 #define VMEREFID        "GPS"   /* reference id */
77 #define VMEDESCRIPTION  "GPS" /* who we are */
78 #define VMEHSREFID      0x7f7f1001 /* 127.127.16.01 refid hi strata */
79 
80 /* I'm using clock type 16 until one is assigned */
81 /* This is set also in vme_control, below        */
82 
83 
84 #define GMT             0       /* hour offset from Greenwich */
85 
86 /*
87  * VME unit control structure.
88  */
89 struct vmeunit {
90 	struct peer *peer;      /* associated peer structure */
91 	struct refclockio io;   /* given to the I/O handler */
92 	struct vmedate vmedata; /* data returned from vme read */
93 	l_fp lastrec;           /* last local time */
94 	l_fp lastref;           /* last timecode time */
95 	char lastcode[BMAX];    /* last timecode received */
96 	u_short lencode;        /* length of last timecode */
97 	u_long lasttime;        /* last time clock heard from */
98 	u_short unit;           /* unit number for this guy */
99 	u_short status;         /* clock status */
100 	u_short lastevent;      /* last clock event */
101 	u_short year;           /* year of eternity */
102 	u_short day;            /* day of year */
103 	u_short hour;           /* hour of day */
104 	u_short minute;         /* minute of hour */
105 	u_short second;         /* seconds of minute */
106 	u_long usec;            /* microsecond of second */
107 	u_long yearstart;       /* start of current year */
108 	u_short leap;           /* leap indicators */
109 	/*
110 	 * Status tallies
111 	 */
112 	u_long polls;           /* polls sent */
113 	u_long noreply;         /* no replies to polls */
114 	u_long coderecv;        /* timecodes received */
115 	u_long badformat;       /* bad format */
116 	u_long baddata;         /* bad data */
117 	u_long timestarted;     /* time we started this */
118 };
119 
120 /*
121  * Data space for the unit structures.  Note that we allocate these on
122  * the fly, but never give them back.
123  */
124 static struct vmeunit *vmeunits[MAXUNITS];
125 static u_char unitinuse[MAXUNITS];
126 
127 /*
128  * Keep the fudge factors separately so they can be set even
129  * when no clock is configured.
130  */
131 static l_fp fudgefactor[MAXUNITS];
132 static u_char stratumtouse[MAXUNITS];
133 static u_char sloppyclockflag[MAXUNITS];
134 
135 /*
136  * Function prototypes
137  */
138 static  void    vme_init        (void);
139 static  int     vme_start       (int, struct peer *);
140 static  void    vme_shutdown    (int, struct peer *);
141 static  void    vme_report_event        (struct vmeunit *, int);
142 static  void    vme_receive     (struct recvbuf *);
143 static  void    vme_poll        (int unit, struct peer *);
144 static  void    vme_control     (int, struct refclockstat *, struct refclockstat *, struct peer *);
145 static  void    vme_buginfo     (int, struct refclockbug *, struct peer *);
146 
147 /*
148  * Transfer vector
149  */
150 struct  refclock refclock_gpsvme = {
151 	vme_start, vme_shutdown, vme_poll,
152 	vme_control, vme_init, vme_buginfo, NOFLAGS
153 };
154 
155 int fd_vme;  /* file descriptor for ioctls */
156 int regvalue;
157 
158 /*
159  * vme_init - initialize internal vme driver data
160  */
161 static void
162 vme_init(void)
163 {
164 	register int i;
165 	/*
166 	 * Just zero the data arrays
167 	 */
168 	/*
169 	  bzero((char *)vmeunits, sizeof vmeunits);
170 	  bzero((char *)unitinuse, sizeof unitinuse);
171 	*/
172 
173 	/*
174 	 * Initialize fudge factors to default.
175 	 */
176 	for (i = 0; i < MAXUNITS; i++) {
177 		fudgefactor[i].l_ui = 0;
178 		fudgefactor[i].l_uf = 0;
179 		stratumtouse[i] = 0;
180 		sloppyclockflag[i] = 0;
181 	}
182 }
183 
184 /*
185  * vme_start - open the VME device and initialize data for processing
186  */
187 static int
188 vme_start(
189 	u_int unit,
190 	struct peer *peer
191 	)
192 {
193 	register struct vmeunit *vme;
194 	register int i;
195 	int dummy;
196 	char vmedev[20];
197 
198 	/*
199 	 * Check configuration info.
200 	 */
201 	if (unit >= MAXUNITS) {
202 		msyslog(LOG_ERR, "vme_start: unit %d invalid", unit);
203 		return (0);
204 	}
205 	if (unitinuse[unit]) {
206 		msyslog(LOG_ERR, "vme_start: unit %d in use", unit);
207 		return (0);
208 	}
209 
210 	/*
211 	 * Open VME device
212 	 */
213 #ifdef DEBUG
214 
215 	printf("Opening  VME DEVICE \n");
216 #endif
217 	init_vme();   /* This is in the map_vme.c external file */
218 
219 	/*
220 	 * Allocate unit structure
221 	 */
222 	if (vmeunits[unit] != 0) {
223 		vme = vmeunits[unit];   /* The one we want is okay */
224 	} else {
225 		for (i = 0; i < MAXUNITS; i++) {
226 			if (!unitinuse[i] && vmeunits[i] != 0)
227 			    break;
228 		}
229 		if (i < MAXUNITS) {
230 			/*
231 			 * Reclaim this one
232 			 */
233 			vme = vmeunits[i];
234 			vmeunits[i] = 0;
235 		} else {
236 			vme = (struct vmeunit *)
237 				emalloc(sizeof(struct vmeunit));
238          time_vme = (struct vmedate *)malloc(sizeof(struct vmedate)); /* Added to emulate LM's refclock_gpsvme
239                                                         (added to fix mem lead DW lmco)  mjb lmco 12/22/99 */
240 		}
241 	}
242 	bzero((char *)vme, sizeof(struct vmeunit));
243 	vmeunits[unit] = vme;
244 
245 	/*
246 	 * Set up the structures
247 	 */
248 	vme->peer = peer;
249 	vme->unit = (u_short)unit;
250 	vme->timestarted = current_time;
251 
252 	vme->io.clock_recv = vme_receive;
253 	vme->io.srcclock = (caddr_t)vme;
254 	vme->io.datalen = 0;
255 	vme->io.fd = fd_vme;
256 
257 	/*
258 	 * All done.  Initialize a few random peer variables, then
259 	 * return success.
260 	 */
261 	peer->precision = VMEPRECISION;
262 	peer->stratum = stratumtouse[unit];
263 	memcpy( (char *)&peer->refid, USNOREFID,4);
264 
265 	/* peer->refid = htonl(VMEHSREFID); */
266 
267 	unitinuse[unit] = 1;
268 	return (1);
269 }
270 
271 
272 /*
273  * vme_shutdown - shut down a VME clock
274  */
275 static void
276 vme_shutdown(
277 	int unit
278 	)
279 {
280 	register struct vmeunit *vme;
281 
282 	if (unit >= MAXUNITS) {
283 		msyslog(LOG_ERR, "vme_shutdown: unit %d invalid", unit);
284 		return;
285 	}
286 	if (!unitinuse[unit]) {
287 		msyslog(LOG_ERR, "vme_shutdown: unit %d not in use", unit);
288 		return;
289 	}
290 
291 	/*
292 	 * Tell the I/O module to turn us off.  We're history.
293 	 */
294 	unmap_vme();
295 	vme = vmeunits[unit];
296 	io_closeclock(&vme->io);
297 	unitinuse[unit] = 0;
298 }
299 
300 /*
301  * vme_report_event - note the occurance of an event
302  *
303  * This routine presently just remembers the report and logs it, but
304  * does nothing heroic for the trap handler.
305  */
306 static void
307 vme_report_event(
308 	struct vmeunit *vme,
309 	int code
310 	)
311 {
312 	struct peer *peer;
313 
314 	peer = vme->peer;
315 	if (vme->status != (u_short)code) {
316 		vme->status = (u_short)code;
317 		if (code != CEVNT_NOMINAL)
318 		    vme->lastevent = (u_short)code;
319 		msyslog(LOG_INFO,
320 			"clock %s event %x", ntoa(&peer->srcadr), code);
321 	}
322 }
323 
324 
325 /*
326  * vme_receive - receive data from the VME device.
327  *
328  * Note: This interface would be interrupt-driven. We don't use that
329  * now, but include a dummy routine for possible future adventures.
330  */
331 static void
332 vme_receive(
333 	struct recvbuf *rbufp
334 	)
335 {
336 }
337 
338 /*
339  * vme_poll - called by the transmit procedure
340  */
341 static void
342 vme_poll(
343 	int unit,
344 	struct peer *peer
345 	)
346 {
347 	struct vmedate *tptr;
348 	struct vmeunit *vme;
349 	l_fp tstmp;
350 	time_t tloc;
351 	struct tm *tadr;
352 	long ltemp;
353 
354 
355 
356 	if (unit >= MAXUNITS) {
357 		msyslog(LOG_ERR, "vme_poll: unit %d invalid", unit);
358 		return;
359 	}
360 	if (!unitinuse[unit]) {
361 		msyslog(LOG_ERR, "vme_poll: unit %d not in use", unit);
362 		return;
363 	}
364 	vme = vmeunits[unit];        /* Here is the structure */
365 	vme->polls++;
366 
367 	tptr = &vme->vmedata;
368 
369 	if ((tptr = get_gpsvme_time()) == NULL ) {
370 		vme_report_event(vme, CEVNT_BADREPLY);
371 		return;
372 	}
373 
374 	get_systime(&vme->lastrec);
375 	vme->lasttime = current_time;
376 
377 	/*
378 	 * Get VME time and convert to timestamp format.
379 	 * The year must come from the system clock.
380 	 */
381 	/*
382 	  time(&tloc);
383 	  tadr = gmtime(&tloc);
384 	  tptr->year = (unsigned short)(tadr->tm_year + 1900);
385 	*/
386 
387 	sprintf(vme->lastcode,
388 		"%3.3d %2.2d:%2.2d:%2.2d.%.6d %1d\0",
389 		tptr->doy, tptr->hr, tptr->mn,
390 		tptr->sec, tptr->frac, tptr->status);
391 
392 	record_clock_stats(&(vme->peer->srcadr), vme->lastcode);
393 	vme->lencode = (u_short) strlen(vme->lastcode);
394 
395 	vme->day =  tptr->doy;
396 	vme->hour =   tptr->hr;
397 	vme->minute =  tptr->mn;
398 	vme->second =  tptr->sec;
399 	vme->nsec =   tptr->frac * 1000;
400 
401 #ifdef DEBUG
402 	if (debug)
403 	    printf("vme: %3d %02d:%02d:%02d.%06ld %1x\n",
404 		   vme->day, vme->hour, vme->minute, vme->second,
405 		   vme->nsec, tptr->status);
406 #endif
407 	if (tptr->status ) {       /*  Status 0 is locked to ref., 1 is not */
408 		vme_report_event(vme, CEVNT_BADREPLY);
409 		return;
410 	}
411 
412 	/*
413 	 * Now, compute the reference time value. Use the heavy
414 	 * machinery for the seconds and the millisecond field for the
415 	 * fraction when present. If an error in conversion to internal
416 	 * format is found, the program declares bad data and exits.
417 	 * Note that this code does not yet know how to do the years and
418 	 * relies on the clock-calendar chip for sanity.
419 	 */
420 	if (!clocktime(vme->day, vme->hour, vme->minute,
421 		       vme->second, GMT, vme->lastrec.l_ui,
422 		       &vme->yearstart, &vme->lastref.l_ui)) {
423 		vme->baddata++;
424 		vme_report_event(vme, CEVNT_BADTIME);
425 		msyslog(LOG_ERR, "refclock_gpsvme: bad data!!");
426 		return;
427 	}
428 	vme->lastref.l_uf = 0;
429 	DTOLFP(vme->nsec / 1e9, &ltemp);
430 	L_ADD(&vme->lastrec, &ltemp);
431 	tstmp = vme->lastref;
432 
433 	L_SUB(&tstmp, &vme->lastrec);
434 	vme->coderecv++;
435 
436 	L_ADD(&tstmp, &(fudgefactor[vme->unit]));
437 	vme->lastref = vme->lastrec;
438 	refclock_receive(vme->peer);
439 }
440 
441 /*
442  * vme_control - set fudge factors, return statistics2
443  */
444 static void
445 vme_control(
446 	u_int unit,
447 	struct refclockstat *in,
448 	struct refclockstat *out,
449    struct peer * peer
450 	)
451 {
452 	register struct vmeunit *vme;
453 
454 	if (unit >= MAXUNITS) {
455 		msyslog(LOG_ERR, "vme_control: unit %d invalid)", unit);
456 		return;
457 	}
458 
459 	if (in != 0) {
460 		if (in->haveflags & CLK_HAVETIME1)
461 		   DTOLFP(in->fudgetime1, &fudgefactor[unit]);  /* added mjb lmco 12/20/99 */
462 
463 		if (in->haveflags & CLK_HAVEVAL1) {
464 			stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf);
465 			if (unitinuse[unit]) {
466 				struct peer *peer;
467 
468                                 /*
469                                  * Should actually reselect clock, but
470                                  * will wait for the next timecode
471                                  */
472 				vme = vmeunits[unit];
473 				peer = vme->peer;
474 				peer->stratum = stratumtouse[unit];
475 				if (stratumtouse[unit] <= 1)
476 				    memcpy( (char *)&peer->refid, USNOREFID,4);
477 				else
478 				    peer->refid = htonl(VMEHSREFID);
479 			}
480 		}
481 		if (in->haveflags & CLK_HAVEFLAG1) {
482 			sloppyclockflag[unit] = in->flags & CLK_FLAG1;
483 		}
484 	}
485 
486 	if (out != 0) {
487 		out->type = 16;  /*set  by RES  SHOULD BE CHANGED */
488 		out->haveflags
489 			= CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1;
490 		out->clockdesc = VMEDESCRIPTION;
491       LFPTOD(&fudgefactor[unit], out->fudgetime1);  /* added mjb lmco 12/20/99 */
492 
493       out ->fudgetime2 = 0;  /* should do what above was supposed to do  mjb lmco 12/20/99 */
494 
495 		out->fudgeval1 = (long)stratumtouse[unit];  /*changed from above LONG was not
496                                                                       defined  mjb lmco 12/15/99 */
497 
498 		out->fudgeval2 = 0;
499 		out->flags = sloppyclockflag[unit];
500 		if (unitinuse[unit]) {
501 			vme = vmeunits[unit];
502 			out->lencode = vme->lencode;
503          out->p_lastcode = vme->lastcode;
504 			out->timereset = current_time - vme->timestarted;
505 			out->polls = vme->polls;
506 			out->noresponse = vme->noreply;
507 			out->badformat = vme->badformat;
508 			out->baddata = vme->baddata;
509 			out->lastevent = vme->lastevent;
510 			out->currentstatus = vme->status;
511 		} else {
512 			out->lencode = 0;
513          out->p_lastcode = "";
514 			out->polls = out->noresponse = 0;
515 			out->badformat = out->baddata = 0;
516 			out->timereset = 0;
517 			out->currentstatus = out->lastevent = CEVNT_NOMINAL;
518 		}
519 	}
520 }
521 
522 /*
523  * vme_buginfo - return clock dependent debugging info
524  */
525 static void
526 vme_buginfo(
527 	int unit,
528  	register struct refclockbug *bug,
529  	struct peer * peer
530 	)
531 {
532 	register struct vmeunit *vme;
533 
534 	if (unit >= MAXUNITS) {
535 		msyslog(LOG_ERR, "vme_buginfo: unit %d invalid)", unit);
536 		return;
537 	}
538 
539 	if (!unitinuse[unit])
540 	    return;
541 	vme = vmeunits[unit];
542 
543 	bug->nvalues = 11;
544 	bug->ntimes = 5;
545 	if (vme->lasttime != 0)
546 	    bug->values[0] = current_time - vme->lasttime;
547 	else
548 	    bug->values[0] = 0;
549 	bug->values[2] = (u_long)vme->year;
550 	bug->values[3] = (u_long)vme->day;
551 	bug->values[4] = (u_long)vme->hour;
552 	bug->values[5] = (u_long)vme->minute;
553 	bug->values[6] = (u_long)vme->second;
554 	bug->values[7] = (u_long)vme->nsec;
555 	bug->values[9] = vme->yearstart;
556 	bug->stimes = 0x1c;
557 	bug->times[0] = vme->lastref;
558 	bug->times[1] = vme->lastrec;
559 }
560 /* -------------------------------------------------------*/
561 /* get_gpsvme_time()                                      */
562 /*  R. Schmidt, USNO, 1995                                */
563 /*  It's ugly, but hey, it works and its free             */
564 
565 #include "gps.h"  /* defines for TrueTime GPS-VME */
566 
567 #define PBIAS  193 /* 193 microsecs to read the GPS  experimentally found */
568 
569 struct vmedate *
570 get_gpsvme_time(void)
571 {
572 	extern struct vmedate  *time_vme;
573 	unsigned short set, hr, min, sec, ums, hms, status;
574 	int ret;
575 	char ti[3];
576 
577 	long tloc ;
578 	time_t  mktime(),time();
579 	struct tm *gmtime(), *gmt;
580 	char  *gpsmicro;
581 	gpsmicro = (char *) malloc(7);
582 
583 	*greg = (unsigned short *)malloc(sizeof(short) * NREGS);
584 
585 
586 	/*  reference the freeze command address general register 1 */
587 	set = *greg[0];
588 	/*  read the registers : */
589 	/* get year */
590 	time_vme->year  = (unsigned short)  *greg[6];
591 	/* Get doy */
592 	time_vme->doy =  (unsigned short) (*greg[5] & MASKDAY);
593 	/* Get hour */
594 	time_vme->hr =  (unsigned short) ((*greg[4] & MASKHI) >>8);
595 	/* Get minutes */
596 	time_vme->mn = (unsigned short)  (*greg[4] & MASKLO);
597 	/* Get seconds */
598 	time_vme->sec = (unsigned short)  (*greg[3] & MASKHI) >>8;
599 	/* get microseconds in 2 parts and put together */
600 	ums  =   *greg[2];
601 	hms  =   *greg[3] & MASKLO;
602 
603 	time_vme->status = (unsigned short) *greg[5] >>13;
604 
605 	/*  reference the unfreeze command address general register 1 */
606 	set = *greg[1];
607 
608 	sprintf(gpsmicro,"%2.2x%4.4x\0", hms, ums);
609 	time_vme->frac = (u_long) gpsmicro;
610 
611 	/*      unmap_vme(); */
612 
613 	if (!status) {
614       return (NULL);  /* fixed mjb lmco 12/20/99 */
615 	}
616 	else
617 	    return (time_vme);
618 }
619 
620 #else
621 int refclock_gpsvme_bs;
622 #endif /* REFCLOCK */
623