aboutsummaryrefslogblamecommitdiff
path: root/contrib/ntp/ntpd/refclock_gpsvme.c
blob: a7c6d23d3a4f1203f62cdea0bf0e389a8208ad49 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613




































































































































































































































































































































































































































































































































































































































                                                                                      
/*
 * refclock_gpsvme.c  NTP clock driver for the TrueTime GPS-VME
 * R. Schmidt, Time Service, US Naval Obs.  res@tuttle.usno.navy.mil
 * 
 * The refclock type has been defined as 16 (until new id assigned). 
 * These DEFS are included in the Makefile:
 *      DEFS= -DHAVE_TERMIOS -DSYS_HPUX=9
 *      DEFS_LOCAL=  -DREFCLOCK
 *      CLOCKDEFS=   -DGPSVME
 *  The file map_vme.c does the VME memory mapping, and includes vme_init().
 *  map_vme.c is HP-UX specific, because HPUX cannot mmap() device files! Boo!
 *  The file gps.h   provides TrueTime register info. 
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#if defined(REFCLOCK) && defined(CLOCK_GPSVME) 
#include <stdio.h>
#include <syslog.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <sys/time.h>

#include "gps.h"
#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_refclock.h"
#include "ntp_unixtime.h"
#include "ntp_stdlib.h"
#include "/etc/conf/h/io.h"

/* GLOBAL STUFF BY RES */

#include <time.h>

#define PRIO    120		/* set the realtime priority */
#define NREGS	7		/* number of registers we will use */

extern int init_vme();		/* This is just a call to map_vme() */
				/* It doesn't have to be extern */
unsigned short  *greg[NREGS];	/* GPS registers defined in gps.h */
void *gps_base;			/* Base address of GPS VME card returned by */
				/* the map_vme() call */
extern caddr_t map_vme ();   
extern void unmap_vme();	/* Unmaps the VME space */

struct vmedate {		/* structure needed by ntp */
	unsigned short year;	/* *tptr is a pointer to this */
	unsigned short doy;
	unsigned short hr;
	unsigned short mn;
	unsigned short sec;
	unsigned long frac;
	unsigned short status;
};

struct vmedate *get_gpsvme_time();

/* END OF STUFF FROM RES */

/*
 * Definitions
 */
#define MAXUNITS 2              /* max number of VME units */
#define BMAX  50        /* timecode buffer length */

/*
 * VME interface parameters. 
 */
#define VMEPRECISION    (-21)      /* precision assumed (1 us) */
#define USNOREFID       "USNO\0"  /* Or whatever? */
#define VMEREFID        "GPS"   /* reference id */
#define VMEDESCRIPTION  "GPS" /* who we are */
#define VMEHSREFID      0x7f7f1001 /* 127.127.16.01 refid hi strata */

/* I'm using clock type 16 until one is assigned */
/* This is set also in vme_control, below        */


#define GMT             0       /* hour offset from Greenwich */

/*
 * VME unit control structure.
 */
struct vmeunit {
	struct peer *peer;      /* associated peer structure */
	struct refclockio io;   /* given to the I/O handler */
	struct vmedate vmedata; /* data returned from vme read */
	l_fp lastrec;           /* last local time */
	l_fp lastref;           /* last timecode time */
	char lastcode[BMAX];    /* last timecode received */
	u_short lencode;        /* length of last timecode */
	u_long lasttime;        /* last time clock heard from */
	u_short unit;           /* unit number for this guy */
	u_short status;         /* clock status */
	u_short lastevent;      /* last clock event */
	u_short year;           /* year of eternity */
	u_short day;            /* day of year */
	u_short hour;           /* hour of day */
	u_short minute;         /* minute of hour */
	u_short second;         /* seconds of minute */
	u_long usec;            /* microsecond of second */
	u_long yearstart;       /* start of current year */
	u_short leap;           /* leap indicators */
	/*
	 * Status tallies
	 */
	u_long polls;           /* polls sent */
	u_long noreply;         /* no replies to polls */
	u_long coderecv;        /* timecodes received */
	u_long badformat;       /* bad format */
	u_long baddata;         /* bad data */
	u_long timestarted;     /* time we started this */
};

/*
 * Data space for the unit structures.  Note that we allocate these on
 * the fly, but never give them back.
 */
static struct vmeunit *vmeunits[MAXUNITS];
static u_char unitinuse[MAXUNITS];

/*
 * Keep the fudge factors separately so they can be set even
 * when no clock is configured.
 */
static l_fp fudgefactor[MAXUNITS];
static u_char stratumtouse[MAXUNITS];
static u_char sloppyclockflag[MAXUNITS];

/*
 * Function prototypes
 */
static  void    vme_init        (void);
static  int     vme_start       (u_int, struct peer *);
static  void    vme_shutdown    (int);
static  void    vme_report_event        (struct vmeunit *, int);
static  void    vme_receive     (struct recvbuf *);
static  void    vme_poll        (int unit, struct peer *);
static  void    vme_control     (u_int, struct refclockstat *, struct refclockstat *);
static  void    vme_buginfo     (int, struct refclockbug *);

/*
 * Transfer vector
 */
struct  refclock refclock_gpsvme = {
	vme_start, vme_shutdown, vme_poll,
	vme_control, vme_init, vme_buginfo, NOFLAGS
};

int fd_vme;  /* file descriptor for ioctls */
int regvalue;

/*
 * vme_init - initialize internal vme driver data
 */
static void
vme_init(void)
{
	register int i;
	/*
	 * Just zero the data arrays
	 */
	/*
	  bzero((char *)vmeunits, sizeof vmeunits);
	  bzero((char *)unitinuse, sizeof unitinuse);
	*/

	/*
	 * Initialize fudge factors to default.
	 */
	for (i = 0; i < MAXUNITS; i++) {
		fudgefactor[i].l_ui = 0;
		fudgefactor[i].l_uf = 0;
		stratumtouse[i] = 0;
		sloppyclockflag[i] = 0;
	}
}

/*
 * vme_start - open the VME device and initialize data for processing
 */
static int
vme_start(
	u_int unit,
	struct peer *peer
	)
{
	register struct vmeunit *vme;
	register int i;
	int dummy;
	char vmedev[20];

	/*
	 * Check configuration info.
	 */
	if (unit >= MAXUNITS) {
		msyslog(LOG_ERR, "vme_start: unit %d invalid", unit);
		return (0);
	}
	if (unitinuse[unit]) {
		msyslog(LOG_ERR, "vme_start: unit %d in use", unit);
		return (0);
	}

	/*
	 * Open VME device
	 */
#ifdef DEBUG

	printf("Opening  VME DEVICE \n");
#endif
	init_vme();   /* This is in the map_vme.c external file */

	/*
	 * Allocate unit structure
	 */
	if (vmeunits[unit] != 0) {
		vme = vmeunits[unit];   /* The one we want is okay */
	} else {
		for (i = 0; i < MAXUNITS; i++) {
			if (!unitinuse[i] && vmeunits[i] != 0)
			    break;
		}
		if (i < MAXUNITS) {
			/*
			 * Reclaim this one
			 */
			vme = vmeunits[i];
			vmeunits[i] = 0;
		} else {
			vme = (struct vmeunit *)
				emalloc(sizeof(struct vmeunit));
		}
	}
	bzero((char *)vme, sizeof(struct vmeunit));
	vmeunits[unit] = vme;

	/*
	 * Set up the structures
	 */
	vme->peer = peer;
	vme->unit = (u_short)unit;
	vme->timestarted = current_time;

	vme->io.clock_recv = vme_receive;
	vme->io.srcclock = (caddr_t)vme;
	vme->io.datalen = 0;
	vme->io.fd = fd_vme;

	/*
	 * All done.  Initialize a few random peer variables, then
	 * return success.
	 */
	peer->precision = VMEPRECISION;
	peer->stratum = stratumtouse[unit];
	memcpy( (char *)&peer->refid, USNOREFID,4);

	/* peer->refid = htonl(VMEHSREFID); */

	unitinuse[unit] = 1;
	return (1);
}


/*
 * vme_shutdown - shut down a VME clock
 */
static void
vme_shutdown(
	int unit
	)
{
	register struct vmeunit *vme;

	if (unit >= MAXUNITS) {
		msyslog(LOG_ERR, "vme_shutdown: unit %d invalid", unit);
		return;
	}
	if (!unitinuse[unit]) {
		msyslog(LOG_ERR, "vme_shutdown: unit %d not in use", unit);
		return;
	}

	/*
	 * Tell the I/O module to turn us off.  We're history.
	 */
	unmap_vme();
	vme = vmeunits[unit];
	io_closeclock(&vme->io);
	unitinuse[unit] = 0;
}

/*
 * vme_report_event - note the occurance of an event
 *
 * This routine presently just remembers the report and logs it, but
 * does nothing heroic for the trap handler.
 */
static void
vme_report_event(
	struct vmeunit *vme,
	int code
	)
{
	struct peer *peer;
        
	peer = vme->peer;
	if (vme->status != (u_short)code) {
		vme->status = (u_short)code;
		if (code != CEVNT_NOMINAL)
		    vme->lastevent = (u_short)code;
		msyslog(LOG_INFO,
			"clock %s event %x", ntoa(&peer->srcadr), code);
	}
}


/*
 * vme_receive - receive data from the VME device.
 *
 * Note: This interface would be interrupt-driven. We don't use that
 * now, but include a dummy routine for possible future adventures.
 */
static void
vme_receive(
	struct recvbuf *rbufp
	)
{
}

/*
 * vme_poll - called by the transmit procedure
 */
static void
vme_poll(
	int unit,
	struct peer *peer
	)
{
	struct vmedate *tptr; 
	struct vmeunit *vme;
	l_fp tstmp;
	time_t tloc;
	struct tm *tadr;

        
	vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit *));
	tptr = (struct vmedate *)emalloc(sizeof(struct vmedate *));

 
	if (unit >= MAXUNITS) {
		msyslog(LOG_ERR, "vme_poll: unit %d invalid", unit);
		return;
	}
	if (!unitinuse[unit]) {
		msyslog(LOG_ERR, "vme_poll: unit %d not in use", unit);
		return;
	}
	vme = vmeunits[unit];        /* Here is the structure */
	vme->polls++;

	tptr = &vme->vmedata; 
        
	if ((tptr = get_gpsvme_time()) == NULL ) {
		vme_report_event(vme, CEVNT_BADREPLY);
		return;
	}

	get_systime(&vme->lastrec);
	vme->lasttime = current_time;

	/*
	 * Get VME time and convert to timestamp format. 
	 * The year must come from the system clock.
	 */
	/*
	  time(&tloc);
	  tadr = gmtime(&tloc);
	  tptr->year = (unsigned short)(tadr->tm_year + 1900);
	*/

	sprintf(vme->lastcode, 
		"%3.3d %2.2d:%2.2d:%2.2d.%.6d %1d\0",
		tptr->doy, tptr->hr, tptr->mn,
		tptr->sec, tptr->frac, tptr->status);

	record_clock_stats(&(vme->peer->srcadr), vme->lastcode);
	vme->lencode = (u_short) strlen(vme->lastcode);

	vme->day =  tptr->doy;
	vme->hour =   tptr->hr;
	vme->minute =  tptr->mn;
	vme->second =  tptr->sec;
	vme->usec =   tptr->frac;

#ifdef DEBUG
	if (debug)
	    printf("vme: %3d %02d:%02d:%02d.%06ld %1x\n",
		   vme->day, vme->hour, vme->minute, vme->second,
		   vme->usec, tptr->status);
#endif
	if (tptr->status ) {       /*  Status 0 is locked to ref., 1 is not */
		vme_report_event(vme, CEVNT_BADREPLY);
		return;
	}

	/*
	 * Now, compute the reference time value. Use the heavy
	 * machinery for the seconds and the millisecond field for the
	 * fraction when present. If an error in conversion to internal
	 * format is found, the program declares bad data and exits.
	 * Note that this code does not yet know how to do the years and
	 * relies on the clock-calendar chip for sanity.
	 */
	if (!clocktime(vme->day, vme->hour, vme->minute,
		       vme->second, GMT, vme->lastrec.l_ui,
		       &vme->yearstart, &vme->lastref.l_ui)) {
		vme->baddata++;
		vme_report_event(vme, CEVNT_BADTIME);
		msyslog(LOG_ERR, "refclock_gpsvme: bad data!!");
		return;
	}
	TVUTOTSF(vme->usec, vme->lastref.l_uf);
	tstmp = vme->lastref;

	L_SUB(&tstmp, &vme->lastrec);
	vme->coderecv++;

	L_ADD(&tstmp, &(fudgefactor[vme->unit]));

	refclock_receive(vme->peer);
}

/*
 * vme_control - set fudge factors, return statistics
 */
static void
vme_control(
	u_int unit,
	struct refclockstat *in,
	struct refclockstat *out
	)
{
	register struct vmeunit *vme;

	if (unit >= MAXUNITS) {
		msyslog(LOG_ERR, "vme_control: unit %d invalid)", unit);
		return;
	}

	if (in != 0) {
		if (in->haveflags & CLK_HAVETIME1)
		    fudgefactor[unit] = in->fudgetime1;
		if (in->haveflags & CLK_HAVEVAL1) {
			stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf);
			if (unitinuse[unit]) {
				struct peer *peer;

                                /*
                                 * Should actually reselect clock, but
                                 * will wait for the next timecode
                                 */
				vme = vmeunits[unit];
				peer = vme->peer;
				peer->stratum = stratumtouse[unit];
				if (stratumtouse[unit] <= 1)
				    memcpy( (char *)&peer->refid, USNOREFID,4);
				else
				    peer->refid = htonl(VMEHSREFID);
			}
		}
		if (in->haveflags & CLK_HAVEFLAG1) {
			sloppyclockflag[unit] = in->flags & CLK_FLAG1;
		}
	}

	if (out != 0) {
		out->type = 16;  /*set  by RES  SHOULD BE CHANGED */
		out->haveflags
			= CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1;
		out->clockdesc = VMEDESCRIPTION;
		out->fudgetime1 = fudgefactor[unit];
		out->fudgetime2.l_ui = 0;
		out->fudgetime2.l_uf = 0;
		out->fudgeval1 = (LONG)stratumtouse[unit];
		out->fudgeval2 = 0;
		out->flags = sloppyclockflag[unit];
		if (unitinuse[unit]) {
			vme = vmeunits[unit];
			out->lencode = vme->lencode;
			out->lastcode = vme->lastcode;
			out->timereset = current_time - vme->timestarted;
			out->polls = vme->polls;
			out->noresponse = vme->noreply;
			out->badformat = vme->badformat;
			out->baddata = vme->baddata;
			out->lastevent = vme->lastevent;
			out->currentstatus = vme->status;
		} else {
			out->lencode = 0;
			out->lastcode = "";
			out->polls = out->noresponse = 0;
			out->badformat = out->baddata = 0;
			out->timereset = 0;
			out->currentstatus = out->lastevent = CEVNT_NOMINAL;
		}
	}
}

/*
 * vme_buginfo - return clock dependent debugging info
 */
static void
vme_buginfo(
	int unit,
	register struct refclockbug *bug
	)
{
	register struct vmeunit *vme;

	if (unit >= MAXUNITS) {
		msyslog(LOG_ERR, "vme_buginfo: unit %d invalid)", unit);
		return;
	}

	if (!unitinuse[unit])
	    return;
	vme = vmeunits[unit];

	bug->nvalues = 11;
	bug->ntimes = 5;
	if (vme->lasttime != 0)
	    bug->values[0] = current_time - vme->lasttime;
	else
	    bug->values[0] = 0;
	bug->values[2] = (u_long)vme->year;
	bug->values[3] = (u_long)vme->day;
	bug->values[4] = (u_long)vme->hour;
	bug->values[5] = (u_long)vme->minute;
	bug->values[6] = (u_long)vme->second;
	bug->values[7] = (u_long)vme->usec;
	bug->values[9] = vme->yearstart;
	bug->stimes = 0x1c;
	bug->times[0] = vme->lastref;
	bug->times[1] = vme->lastrec;
}
/* -------------------------------------------------------*/
/* get_gpsvme_time()                                      */
/*  R. Schmidt, USNO, 1995                                */
/*  It's ugly, but hey, it works and its free             */

#include "gps.h"  /* defines for TrueTime GPS-VME */

#define PBIAS  193 /* 193 microsecs to read the GPS  experimentally found */

struct vmedate *
get_gpsvme_time(void)
{
	struct vmedate  *time_vme;
	unsigned short set, hr, min, sec, ums, hms, status;
	int ret;
	char ti[3];

	long tloc ;
	time_t  mktime(),time();
	struct tm *gmtime(), *gmt;
	char  *gpsmicro;
	gpsmicro = (char *) malloc(7);  

	time_vme = (struct vmedate *)malloc(sizeof(struct vmedate ));
	*greg = (unsigned short *)malloc(sizeof(short) * NREGS);


	/*  reference the freeze command address general register 1 */
	set = *greg[0];
	/*  read the registers : */
	/* get year */
	time_vme->year  = (unsigned short)  *greg[6];  
	/* Get doy */
	time_vme->doy =  (unsigned short) (*greg[5] & MASKDAY);  
	/* Get hour */
	time_vme->hr =  (unsigned short) ((*greg[4] & MASKHI) >>8);
	/* Get minutes */
	time_vme->mn = (unsigned short)  (*greg[4] & MASKLO);
	/* Get seconds */
	time_vme->sec = (unsigned short)  (*greg[3] & MASKHI) >>8;
	/* get microseconds in 2 parts and put together */
	ums  =   *greg[2];
	hms  =   *greg[3] & MASKLO;

	time_vme->status = (unsigned short) *greg[5] >>13;

	/*  reference the unfreeze command address general register 1 */
	set = *greg[1];

	sprintf(gpsmicro,"%2.2x%4.4x\0", hms, ums);
	time_vme->frac = (u_long) gpsmicro;

	/*      unmap_vme(); */

	if (!status) { 
		return ((void *)NULL);
	}
	else
	    return (time_vme);
}

#else
int refclock_gpsvme_bs;
#endif /* REFCLOCK */