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