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