xref: /freebsd/contrib/ntp/ntpd/refclock_gpsvme.c (revision 6fd05b64b5b65dd4ba9b86482a0634a5f0b96c29)
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 
353 
354 
355 	if (unit >= MAXUNITS) {
356 		msyslog(LOG_ERR, "vme_poll: unit %d invalid", unit);
357 		return;
358 	}
359 	if (!unitinuse[unit]) {
360 		msyslog(LOG_ERR, "vme_poll: unit %d not in use", unit);
361 		return;
362 	}
363 	vme = vmeunits[unit];        /* Here is the structure */
364 	vme->polls++;
365 
366 	tptr = &vme->vmedata;
367 
368 	if ((tptr = get_gpsvme_time()) == NULL ) {
369 		vme_report_event(vme, CEVNT_BADREPLY);
370 		return;
371 	}
372 
373 	get_systime(&vme->lastrec);
374 	vme->lasttime = current_time;
375 
376 	/*
377 	 * Get VME time and convert to timestamp format.
378 	 * The year must come from the system clock.
379 	 */
380 	/*
381 	  time(&tloc);
382 	  tadr = gmtime(&tloc);
383 	  tptr->year = (unsigned short)(tadr->tm_year + 1900);
384 	*/
385 
386 	sprintf(vme->lastcode,
387 		"%3.3d %2.2d:%2.2d:%2.2d.%.6d %1d\0",
388 		tptr->doy, tptr->hr, tptr->mn,
389 		tptr->sec, tptr->frac, tptr->status);
390 
391 	record_clock_stats(&(vme->peer->srcadr), vme->lastcode);
392 	vme->lencode = (u_short) strlen(vme->lastcode);
393 
394 	vme->day =  tptr->doy;
395 	vme->hour =   tptr->hr;
396 	vme->minute =  tptr->mn;
397 	vme->second =  tptr->sec;
398 	vme->usec =   tptr->frac;
399 
400 #ifdef DEBUG
401 	if (debug)
402 	    printf("vme: %3d %02d:%02d:%02d.%06ld %1x\n",
403 		   vme->day, vme->hour, vme->minute, vme->second,
404 		   vme->usec, tptr->status);
405 #endif
406 	if (tptr->status ) {       /*  Status 0 is locked to ref., 1 is not */
407 		vme_report_event(vme, CEVNT_BADREPLY);
408 		return;
409 	}
410 
411 	/*
412 	 * Now, compute the reference time value. Use the heavy
413 	 * machinery for the seconds and the millisecond field for the
414 	 * fraction when present. If an error in conversion to internal
415 	 * format is found, the program declares bad data and exits.
416 	 * Note that this code does not yet know how to do the years and
417 	 * relies on the clock-calendar chip for sanity.
418 	 */
419 	if (!clocktime(vme->day, vme->hour, vme->minute,
420 		       vme->second, GMT, vme->lastrec.l_ui,
421 		       &vme->yearstart, &vme->lastref.l_ui)) {
422 		vme->baddata++;
423 		vme_report_event(vme, CEVNT_BADTIME);
424 		msyslog(LOG_ERR, "refclock_gpsvme: bad data!!");
425 		return;
426 	}
427 	TVUTOTSF(vme->usec, vme->lastref.l_uf);
428 	tstmp = vme->lastref;
429 
430 	L_SUB(&tstmp, &vme->lastrec);
431 	vme->coderecv++;
432 
433 	L_ADD(&tstmp, &(fudgefactor[vme->unit]));
434 
435 	refclock_receive(vme->peer);
436 }
437 
438 /*
439  * vme_control - set fudge factors, return statistics2
440  */
441 static void
442 vme_control(
443 	u_int unit,
444 	struct refclockstat *in,
445 	struct refclockstat *out,
446    struct peer * peer
447 	)
448 {
449 	register struct vmeunit *vme;
450 
451 	if (unit >= MAXUNITS) {
452 		msyslog(LOG_ERR, "vme_control: unit %d invalid)", unit);
453 		return;
454 	}
455 
456 	if (in != 0) {
457 		if (in->haveflags & CLK_HAVETIME1)
458 		   DTOLFP(in->fudgetime1, &fudgefactor[unit]);  /* added mjb lmco 12/20/99 */
459 
460 		if (in->haveflags & CLK_HAVEVAL1) {
461 			stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf);
462 			if (unitinuse[unit]) {
463 				struct peer *peer;
464 
465                                 /*
466                                  * Should actually reselect clock, but
467                                  * will wait for the next timecode
468                                  */
469 				vme = vmeunits[unit];
470 				peer = vme->peer;
471 				peer->stratum = stratumtouse[unit];
472 				if (stratumtouse[unit] <= 1)
473 				    memcpy( (char *)&peer->refid, USNOREFID,4);
474 				else
475 				    peer->refid = htonl(VMEHSREFID);
476 			}
477 		}
478 		if (in->haveflags & CLK_HAVEFLAG1) {
479 			sloppyclockflag[unit] = in->flags & CLK_FLAG1;
480 		}
481 	}
482 
483 	if (out != 0) {
484 		out->type = 16;  /*set  by RES  SHOULD BE CHANGED */
485 		out->haveflags
486 			= CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1;
487 		out->clockdesc = VMEDESCRIPTION;
488       LFPTOD(&fudgefactor[unit], out->fudgetime1);  /* added mjb lmco 12/20/99 */
489 
490       out ->fudgetime2 = 0;  /* should do what above was supposed to do  mjb lmco 12/20/99 */
491 
492 		out->fudgeval1 = (long)stratumtouse[unit];  /*changed from above LONG was not
493                                                                       defined  mjb lmco 12/15/99 */
494 
495 		out->fudgeval2 = 0;
496 		out->flags = sloppyclockflag[unit];
497 		if (unitinuse[unit]) {
498 			vme = vmeunits[unit];
499 			out->lencode = vme->lencode;
500          out->p_lastcode = vme->lastcode;
501 			out->timereset = current_time - vme->timestarted;
502 			out->polls = vme->polls;
503 			out->noresponse = vme->noreply;
504 			out->badformat = vme->badformat;
505 			out->baddata = vme->baddata;
506 			out->lastevent = vme->lastevent;
507 			out->currentstatus = vme->status;
508 		} else {
509 			out->lencode = 0;
510          out->p_lastcode = "";
511 			out->polls = out->noresponse = 0;
512 			out->badformat = out->baddata = 0;
513 			out->timereset = 0;
514 			out->currentstatus = out->lastevent = CEVNT_NOMINAL;
515 		}
516 	}
517 }
518 
519 /*
520  * vme_buginfo - return clock dependent debugging info
521  */
522 static void
523 vme_buginfo(
524 	int unit,
525  	register struct refclockbug *bug,
526  	struct peer * peer
527 	)
528 {
529 	register struct vmeunit *vme;
530 
531 	if (unit >= MAXUNITS) {
532 		msyslog(LOG_ERR, "vme_buginfo: unit %d invalid)", unit);
533 		return;
534 	}
535 
536 	if (!unitinuse[unit])
537 	    return;
538 	vme = vmeunits[unit];
539 
540 	bug->nvalues = 11;
541 	bug->ntimes = 5;
542 	if (vme->lasttime != 0)
543 	    bug->values[0] = current_time - vme->lasttime;
544 	else
545 	    bug->values[0] = 0;
546 	bug->values[2] = (u_long)vme->year;
547 	bug->values[3] = (u_long)vme->day;
548 	bug->values[4] = (u_long)vme->hour;
549 	bug->values[5] = (u_long)vme->minute;
550 	bug->values[6] = (u_long)vme->second;
551 	bug->values[7] = (u_long)vme->usec;
552 	bug->values[9] = vme->yearstart;
553 	bug->stimes = 0x1c;
554 	bug->times[0] = vme->lastref;
555 	bug->times[1] = vme->lastrec;
556 }
557 /* -------------------------------------------------------*/
558 /* get_gpsvme_time()                                      */
559 /*  R. Schmidt, USNO, 1995                                */
560 /*  It's ugly, but hey, it works and its free             */
561 
562 #include "gps.h"  /* defines for TrueTime GPS-VME */
563 
564 #define PBIAS  193 /* 193 microsecs to read the GPS  experimentally found */
565 
566 struct vmedate *
567 get_gpsvme_time(void)
568 {
569 	extern struct vmedate  *time_vme;
570 	unsigned short set, hr, min, sec, ums, hms, status;
571 	int ret;
572 	char ti[3];
573 
574 	long tloc ;
575 	time_t  mktime(),time();
576 	struct tm *gmtime(), *gmt;
577 	char  *gpsmicro;
578 	gpsmicro = (char *) malloc(7);
579 
580 	*greg = (unsigned short *)malloc(sizeof(short) * NREGS);
581 
582 
583 	/*  reference the freeze command address general register 1 */
584 	set = *greg[0];
585 	/*  read the registers : */
586 	/* get year */
587 	time_vme->year  = (unsigned short)  *greg[6];
588 	/* Get doy */
589 	time_vme->doy =  (unsigned short) (*greg[5] & MASKDAY);
590 	/* Get hour */
591 	time_vme->hr =  (unsigned short) ((*greg[4] & MASKHI) >>8);
592 	/* Get minutes */
593 	time_vme->mn = (unsigned short)  (*greg[4] & MASKLO);
594 	/* Get seconds */
595 	time_vme->sec = (unsigned short)  (*greg[3] & MASKHI) >>8;
596 	/* get microseconds in 2 parts and put together */
597 	ums  =   *greg[2];
598 	hms  =   *greg[3] & MASKLO;
599 
600 	time_vme->status = (unsigned short) *greg[5] >>13;
601 
602 	/*  reference the unfreeze command address general register 1 */
603 	set = *greg[1];
604 
605 	sprintf(gpsmicro,"%2.2x%4.4x\0", hms, ums);
606 	time_vme->frac = (u_long) gpsmicro;
607 
608 	/*      unmap_vme(); */
609 
610 	if (!status) {
611       return (NULL);  /* fixed mjb lmco 12/20/99 */
612 	}
613 	else
614 	    return (time_vme);
615 }
616 
617 #else
618 int refclock_gpsvme_bs;
619 #endif /* REFCLOCK */
620