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;
356 if (unit >= MAXUNITS) {
357 msyslog(LOG_ERR, "vme_poll: unit %d invalid", unit);
360 if (!unitinuse[unit]) {
361 msyslog(LOG_ERR, "vme_poll: unit %d not in use", unit);
364 vme = vmeunits[unit]; /* Here is the structure */
367 tptr = &vme->vmedata;
369 if ((tptr = get_gpsvme_time()) == NULL ) {
370 vme_report_event(vme, CEVNT_BADREPLY);
374 get_systime(&vme->lastrec);
375 vme->lasttime = current_time;
378 * Get VME time and convert to timestamp format.
379 * The year must come from the system clock.
383 tadr = gmtime(&tloc);
384 tptr->year = (unsigned short)(tadr->tm_year + 1900);
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);
392 record_clock_stats(&(vme->peer->srcadr), vme->lastcode);
393 vme->lencode = (u_short) strlen(vme->lastcode);
395 vme->day = tptr->doy;
396 vme->hour = tptr->hr;
397 vme->minute = tptr->mn;
398 vme->second = tptr->sec;
399 vme->nsec = tptr->frac * 1000;
403 printf("vme: %3d %02d:%02d:%02d.%06ld %1x\n",
404 vme->day, vme->hour, vme->minute, vme->second,
405 vme->nsec, tptr->status);
407 if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */
408 vme_report_event(vme, CEVNT_BADREPLY);
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.
420 if (!clocktime(vme->day, vme->hour, vme->minute,
421 vme->second, GMT, vme->lastrec.l_ui,
422 &vme->yearstart, &vme->lastref.l_ui)) {
424 vme_report_event(vme, CEVNT_BADTIME);
425 msyslog(LOG_ERR, "refclock_gpsvme: bad data!!");
428 vme->lastref.l_uf = 0;
429 DTOLFP(vme->nsec / 1e9, <emp);
430 L_ADD(&vme->lastrec, <emp);
431 tstmp = vme->lastref;
433 L_SUB(&tstmp, &vme->lastrec);
436 L_ADD(&tstmp, &(fudgefactor[vme->unit]));
437 vme->lastref = vme->lastrec;
438 refclock_receive(vme->peer);
442 * vme_control - set fudge factors, return statistics2
447 struct refclockstat *in,
448 struct refclockstat *out,
452 register struct vmeunit *vme;
454 if (unit >= MAXUNITS) {
455 msyslog(LOG_ERR, "vme_control: unit %d invalid)", unit);
460 if (in->haveflags & CLK_HAVETIME1)
461 DTOLFP(in->fudgetime1, &fudgefactor[unit]); /* added mjb lmco 12/20/99 */
463 if (in->haveflags & CLK_HAVEVAL1) {
464 stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf);
465 if (unitinuse[unit]) {
469 * Should actually reselect clock, but
470 * will wait for the next timecode
472 vme = vmeunits[unit];
474 peer->stratum = stratumtouse[unit];
475 if (stratumtouse[unit] <= 1)
476 memcpy( (char *)&peer->refid, USNOREFID,4);
478 peer->refid = htonl(VMEHSREFID);
481 if (in->haveflags & CLK_HAVEFLAG1) {
482 sloppyclockflag[unit] = in->flags & CLK_FLAG1;
487 out->type = 16; /*set by RES SHOULD BE CHANGED */
489 = CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1;
490 out->clockdesc = VMEDESCRIPTION;
491 LFPTOD(&fudgefactor[unit], out->fudgetime1); /* added mjb lmco 12/20/99 */
493 out ->fudgetime2 = 0; /* should do what above was supposed to do mjb lmco 12/20/99 */
495 out->fudgeval1 = (long)stratumtouse[unit]; /*changed from above LONG was not
496 defined mjb lmco 12/15/99 */
499 out->flags = sloppyclockflag[unit];
500 if (unitinuse[unit]) {
501 vme = vmeunits[unit];
502 out->lencode = vme->lencode;
503 out->p_lastcode = vme->lastcode;
504 out->timereset = current_time - vme->timestarted;
505 out->polls = vme->polls;
506 out->noresponse = vme->noreply;
507 out->badformat = vme->badformat;
508 out->baddata = vme->baddata;
509 out->lastevent = vme->lastevent;
510 out->currentstatus = vme->status;
513 out->p_lastcode = "";
514 out->polls = out->noresponse = 0;
515 out->badformat = out->baddata = 0;
517 out->currentstatus = out->lastevent = CEVNT_NOMINAL;
523 * vme_buginfo - return clock dependent debugging info
528 register struct refclockbug *bug,
532 register struct vmeunit *vme;
534 if (unit >= MAXUNITS) {
535 msyslog(LOG_ERR, "vme_buginfo: unit %d invalid)", unit);
539 if (!unitinuse[unit])
541 vme = vmeunits[unit];
545 if (vme->lasttime != 0)
546 bug->values[0] = current_time - vme->lasttime;
549 bug->values[2] = (u_long)vme->year;
550 bug->values[3] = (u_long)vme->day;
551 bug->values[4] = (u_long)vme->hour;
552 bug->values[5] = (u_long)vme->minute;
553 bug->values[6] = (u_long)vme->second;
554 bug->values[7] = (u_long)vme->nsec;
555 bug->values[9] = vme->yearstart;
557 bug->times[0] = vme->lastref;
558 bug->times[1] = vme->lastrec;
560 /* -------------------------------------------------------*/
561 /* get_gpsvme_time() */
562 /* R. Schmidt, USNO, 1995 */
563 /* It's ugly, but hey, it works and its free */
565 #include "gps.h" /* defines for TrueTime GPS-VME */
567 #define PBIAS 193 /* 193 microsecs to read the GPS experimentally found */
570 get_gpsvme_time(void)
572 extern struct vmedate *time_vme;
573 unsigned short set, hr, min, sec, ums, hms, status;
578 time_t mktime(),time();
579 struct tm *gmtime(), *gmt;
581 gpsmicro = (char *) malloc(7);
583 *greg = (unsigned short *)malloc(sizeof(short) * NREGS);
586 /* reference the freeze command address general register 1 */
588 /* read the registers : */
590 time_vme->year = (unsigned short) *greg[6];
592 time_vme->doy = (unsigned short) (*greg[5] & MASKDAY);
594 time_vme->hr = (unsigned short) ((*greg[4] & MASKHI) >>8);
596 time_vme->mn = (unsigned short) (*greg[4] & MASKLO);
598 time_vme->sec = (unsigned short) (*greg[3] & MASKHI) >>8;
599 /* get microseconds in 2 parts and put together */
601 hms = *greg[3] & MASKLO;
603 time_vme->status = (unsigned short) *greg[5] >>13;
605 /* reference the unfreeze command address general register 1 */
608 sprintf(gpsmicro,"%2.2x%4.4x\0", hms, ums);
609 time_vme->frac = (u_long) gpsmicro;
614 return (NULL); /* fixed mjb lmco 12/20/99 */
621 int refclock_gpsvme_bs;
622 #endif /* REFCLOCK */