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
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
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.
18 #if defined(REFCLOCK) && defined(CLOCK_GPSVME)
22 #include "ntp_refclock.h"
23 #include "ntp_unixtime.h"
24 #include "ntp_stdlib.h"
31 #include "/etc/conf/h/io.h"
33 /* GLOBAL STUFF BY RES */
37 #define PRIO 120 /* set the realtime priority */
38 #define NREGS 7 /* number of registers we will use */
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 */
46 extern caddr_t map_vme ();
47 extern void unmap_vme(); /* Unmaps the VME space */
49 struct vmedate { /* structure needed by ntp */
50 unsigned short year; /* *tptr is a pointer to this */
56 unsigned short status;
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 */
63 /* END OF STUFF FROM RES */
68 #define MAXUNITS 2 /* max number of VME units */
69 #define BMAX 50 /* timecode buffer length */
72 * VME interface parameters.
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 */
80 /* I'm using clock type 16 until one is assigned */
81 /* This is set also in vme_control, below */
84 #define GMT 0 /* hour offset from Greenwich */
87 * VME unit control structure.
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 */
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 */
121 * Data space for the unit structures. Note that we allocate these on
122 * the fly, but never give them back.
124 static struct vmeunit *vmeunits[MAXUNITS];
125 static u_char unitinuse[MAXUNITS];
128 * Keep the fudge factors separately so they can be set even
129 * when no clock is configured.
131 static l_fp fudgefactor[MAXUNITS];
132 static u_char stratumtouse[MAXUNITS];
133 static u_char sloppyclockflag[MAXUNITS];
136 * Function prototypes
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 *);
150 struct refclock refclock_gpsvme = {
151 vme_start, vme_shutdown, vme_poll,
152 vme_control, vme_init, vme_buginfo, NOFLAGS
155 int fd_vme; /* file descriptor for ioctls */
159 * vme_init - initialize internal vme driver data
166 * Just zero the data arrays
169 bzero((char *)vmeunits, sizeof vmeunits);
170 bzero((char *)unitinuse, sizeof unitinuse);
174 * Initialize fudge factors to default.
176 for (i = 0; i < MAXUNITS; i++) {
177 fudgefactor[i].l_ui = 0;
178 fudgefactor[i].l_uf = 0;
180 sloppyclockflag[i] = 0;
185 * vme_start - open the VME device and initialize data for processing
193 register struct vmeunit *vme;
199 * Check configuration info.
201 if (unit >= MAXUNITS) {
202 msyslog(LOG_ERR, "vme_start: unit %d invalid", unit);
205 if (unitinuse[unit]) {
206 msyslog(LOG_ERR, "vme_start: unit %d in use", unit);
215 printf("Opening VME DEVICE \n");
217 init_vme(); /* This is in the map_vme.c external file */
220 * Allocate unit structure
222 if (vmeunits[unit] != 0) {
223 vme = vmeunits[unit]; /* The one we want is okay */
225 for (i = 0; i < MAXUNITS; i++) {
226 if (!unitinuse[i] && vmeunits[i] != 0)
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 */
242 bzero((char *)vme, sizeof(struct vmeunit));
243 vmeunits[unit] = vme;
246 * Set up the structures
249 vme->unit = (u_short)unit;
250 vme->timestarted = current_time;
252 vme->io.clock_recv = vme_receive;
253 vme->io.srcclock = (caddr_t)vme;
258 * All done. Initialize a few random peer variables, then
261 peer->precision = VMEPRECISION;
262 peer->stratum = stratumtouse[unit];
263 memcpy( (char *)&peer->refid, USNOREFID,4);
265 /* peer->refid = htonl(VMEHSREFID); */
273 * vme_shutdown - shut down a VME clock
280 register struct vmeunit *vme;
282 if (unit >= MAXUNITS) {
283 msyslog(LOG_ERR, "vme_shutdown: unit %d invalid", unit);
286 if (!unitinuse[unit]) {
287 msyslog(LOG_ERR, "vme_shutdown: unit %d not in use", unit);
292 * Tell the I/O module to turn us off. We're history.
295 vme = vmeunits[unit];
296 io_closeclock(&vme->io);
301 * vme_report_event - note the occurance of an event
303 * This routine presently just remembers the report and logs it, but
304 * does nothing heroic for the trap handler.
315 if (vme->status != (u_short)code) {
316 vme->status = (u_short)code;
317 if (code != CEVNT_NOMINAL)
318 vme->lastevent = (u_short)code;
320 "clock %s event %x", ntoa(&peer->srcadr), code);
326 * vme_receive - receive data from the VME device.
328 * Note: This interface would be interrupt-driven. We don't use that
329 * now, but include a dummy routine for possible future adventures.
333 struct recvbuf *rbufp
339 * vme_poll - called by the transmit procedure
347 struct vmedate *tptr;
355 if (unit >= MAXUNITS) {
356 msyslog(LOG_ERR, "vme_poll: unit %d invalid", unit);
359 if (!unitinuse[unit]) {
360 msyslog(LOG_ERR, "vme_poll: unit %d not in use", unit);
363 vme = vmeunits[unit]; /* Here is the structure */
366 tptr = &vme->vmedata;
368 if ((tptr = get_gpsvme_time()) == NULL ) {
369 vme_report_event(vme, CEVNT_BADREPLY);
373 get_systime(&vme->lastrec);
374 vme->lasttime = current_time;
377 * Get VME time and convert to timestamp format.
378 * The year must come from the system clock.
382 tadr = gmtime(&tloc);
383 tptr->year = (unsigned short)(tadr->tm_year + 1900);
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);
391 record_clock_stats(&(vme->peer->srcadr), vme->lastcode);
392 vme->lencode = (u_short) strlen(vme->lastcode);
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;
402 printf("vme: %3d %02d:%02d:%02d.%06ld %1x\n",
403 vme->day, vme->hour, vme->minute, vme->second,
404 vme->usec, tptr->status);
406 if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */
407 vme_report_event(vme, CEVNT_BADREPLY);
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.
419 if (!clocktime(vme->day, vme->hour, vme->minute,
420 vme->second, GMT, vme->lastrec.l_ui,
421 &vme->yearstart, &vme->lastref.l_ui)) {
423 vme_report_event(vme, CEVNT_BADTIME);
424 msyslog(LOG_ERR, "refclock_gpsvme: bad data!!");
427 TVUTOTSF(vme->usec, vme->lastref.l_uf);
428 tstmp = vme->lastref;
430 L_SUB(&tstmp, &vme->lastrec);
433 L_ADD(&tstmp, &(fudgefactor[vme->unit]));
435 refclock_receive(vme->peer);
439 * vme_control - set fudge factors, return statistics2
444 struct refclockstat *in,
445 struct refclockstat *out,
449 register struct vmeunit *vme;
451 if (unit >= MAXUNITS) {
452 msyslog(LOG_ERR, "vme_control: unit %d invalid)", unit);
457 if (in->haveflags & CLK_HAVETIME1)
458 DTOLFP(in->fudgetime1, &fudgefactor[unit]); /* added mjb lmco 12/20/99 */
460 if (in->haveflags & CLK_HAVEVAL1) {
461 stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf);
462 if (unitinuse[unit]) {
466 * Should actually reselect clock, but
467 * will wait for the next timecode
469 vme = vmeunits[unit];
471 peer->stratum = stratumtouse[unit];
472 if (stratumtouse[unit] <= 1)
473 memcpy( (char *)&peer->refid, USNOREFID,4);
475 peer->refid = htonl(VMEHSREFID);
478 if (in->haveflags & CLK_HAVEFLAG1) {
479 sloppyclockflag[unit] = in->flags & CLK_FLAG1;
484 out->type = 16; /*set by RES SHOULD BE CHANGED */
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 */
490 out ->fudgetime2 = 0; /* should do what above was supposed to do mjb lmco 12/20/99 */
492 out->fudgeval1 = (long)stratumtouse[unit]; /*changed from above LONG was not
493 defined mjb lmco 12/15/99 */
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;
510 out->p_lastcode = "";
511 out->polls = out->noresponse = 0;
512 out->badformat = out->baddata = 0;
514 out->currentstatus = out->lastevent = CEVNT_NOMINAL;
520 * vme_buginfo - return clock dependent debugging info
525 register struct refclockbug *bug,
529 register struct vmeunit *vme;
531 if (unit >= MAXUNITS) {
532 msyslog(LOG_ERR, "vme_buginfo: unit %d invalid)", unit);
536 if (!unitinuse[unit])
538 vme = vmeunits[unit];
542 if (vme->lasttime != 0)
543 bug->values[0] = current_time - vme->lasttime;
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;
554 bug->times[0] = vme->lastref;
555 bug->times[1] = vme->lastrec;
557 /* -------------------------------------------------------*/
558 /* get_gpsvme_time() */
559 /* R. Schmidt, USNO, 1995 */
560 /* It's ugly, but hey, it works and its free */
562 #include "gps.h" /* defines for TrueTime GPS-VME */
564 #define PBIAS 193 /* 193 microsecs to read the GPS experimentally found */
567 get_gpsvme_time(void)
569 extern struct vmedate *time_vme;
570 unsigned short set, hr, min, sec, ums, hms, status;
575 time_t mktime(),time();
576 struct tm *gmtime(), *gmt;
578 gpsmicro = (char *) malloc(7);
580 *greg = (unsigned short *)malloc(sizeof(short) * NREGS);
583 /* reference the freeze command address general register 1 */
585 /* read the registers : */
587 time_vme->year = (unsigned short) *greg[6];
589 time_vme->doy = (unsigned short) (*greg[5] & MASKDAY);
591 time_vme->hr = (unsigned short) ((*greg[4] & MASKHI) >>8);
593 time_vme->mn = (unsigned short) (*greg[4] & MASKLO);
595 time_vme->sec = (unsigned short) (*greg[3] & MASKHI) >>8;
596 /* get microseconds in 2 parts and put together */
598 hms = *greg[3] & MASKLO;
600 time_vme->status = (unsigned short) *greg[5] >>13;
602 /* reference the unfreeze command address general register 1 */
605 sprintf(gpsmicro,"%2.2x%4.4x\0", hms, ums);
606 time_vme->frac = (u_long) gpsmicro;
611 return (NULL); /* fixed mjb lmco 12/20/99 */
618 int refclock_gpsvme_bs;
619 #endif /* REFCLOCK */