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)
29 #include "ntp_refclock.h"
30 #include "ntp_unixtime.h"
31 #include "ntp_stdlib.h"
32 #include "/etc/conf/h/io.h"
34 /* GLOBAL STUFF BY RES */
38 #define PRIO 120 /* set the realtime priority */
39 #define NREGS 7 /* number of registers we will use */
41 extern int init_vme(); /* This is just a call to map_vme() */
42 /* It doesn't have to be extern */
43 /* the map_vme() call */
44 extern unsigned short *greg[NREGS]; /* made extern to avoid being in both map_vme.c and this file */
45 extern void *gps_base; /* mjb lmco 12/20/99 */
47 extern caddr_t map_vme ();
48 extern void unmap_vme(); /* Unmaps the VME space */
50 struct vmedate { /* structure needed by ntp */
51 unsigned short year; /* *tptr is a pointer to this */
57 unsigned short status;
60 struct vmedate *get_gpsvme_time();
61 struct vmedate * time_vme; /* added to emulate LM refclock_gpsvme
62 (Made global per RES suggestion to fix mem leak DW lmco) mjb lmco 12/15/99 */
64 /* END OF STUFF FROM RES */
69 #define MAXUNITS 2 /* max number of VME units */
70 #define BMAX 50 /* timecode buffer length */
73 * VME interface parameters.
75 #define VMEPRECISION (-21) /* precision assumed (1 us) */
76 #define USNOREFID "USNO\0" /* Or whatever? */
77 #define VMEREFID "GPS" /* reference id */
78 #define VMEDESCRIPTION "GPS" /* who we are */
79 #define VMEHSREFID 0x7f7f1001 /* 127.127.16.01 refid hi strata */
81 /* I'm using clock type 16 until one is assigned */
82 /* This is set also in vme_control, below */
85 #define GMT 0 /* hour offset from Greenwich */
88 * VME unit control structure.
91 struct peer *peer; /* associated peer structure */
92 struct refclockio io; /* given to the I/O handler */
93 struct vmedate vmedata; /* data returned from vme read */
94 l_fp lastrec; /* last local time */
95 l_fp lastref; /* last timecode time */
96 char lastcode[BMAX]; /* last timecode received */
97 u_short lencode; /* length of last timecode */
98 u_long lasttime; /* last time clock heard from */
99 u_short unit; /* unit number for this guy */
100 u_short status; /* clock status */
101 u_short lastevent; /* last clock event */
102 u_short year; /* year of eternity */
103 u_short day; /* day of year */
104 u_short hour; /* hour of day */
105 u_short minute; /* minute of hour */
106 u_short second; /* seconds of minute */
107 u_long usec; /* microsecond of second */
108 u_long yearstart; /* start of current year */
109 u_short leap; /* leap indicators */
113 u_long polls; /* polls sent */
114 u_long noreply; /* no replies to polls */
115 u_long coderecv; /* timecodes received */
116 u_long badformat; /* bad format */
117 u_long baddata; /* bad data */
118 u_long timestarted; /* time we started this */
122 * Data space for the unit structures. Note that we allocate these on
123 * the fly, but never give them back.
125 static struct vmeunit *vmeunits[MAXUNITS];
126 static u_char unitinuse[MAXUNITS];
129 * Keep the fudge factors separately so they can be set even
130 * when no clock is configured.
132 static l_fp fudgefactor[MAXUNITS];
133 static u_char stratumtouse[MAXUNITS];
134 static u_char sloppyclockflag[MAXUNITS];
137 * Function prototypes
139 static void vme_init (void);
140 static int vme_start (int, struct peer *);
141 static void vme_shutdown (int, struct peer *);
142 static void vme_report_event (struct vmeunit *, int);
143 static void vme_receive (struct recvbuf *);
144 static void vme_poll (int unit, struct peer *);
145 static void vme_control (int, struct refclockstat *, struct refclockstat *, struct peer *);
146 static void vme_buginfo (int, struct refclockbug *, struct peer *);
151 struct refclock refclock_gpsvme = {
152 vme_start, vme_shutdown, vme_poll,
153 vme_control, vme_init, vme_buginfo, NOFLAGS
156 int fd_vme; /* file descriptor for ioctls */
160 * vme_init - initialize internal vme driver data
167 * Just zero the data arrays
170 bzero((char *)vmeunits, sizeof vmeunits);
171 bzero((char *)unitinuse, sizeof unitinuse);
175 * Initialize fudge factors to default.
177 for (i = 0; i < MAXUNITS; i++) {
178 fudgefactor[i].l_ui = 0;
179 fudgefactor[i].l_uf = 0;
181 sloppyclockflag[i] = 0;
186 * vme_start - open the VME device and initialize data for processing
194 register struct vmeunit *vme;
200 * Check configuration info.
202 if (unit >= MAXUNITS) {
203 msyslog(LOG_ERR, "vme_start: unit %d invalid", unit);
206 if (unitinuse[unit]) {
207 msyslog(LOG_ERR, "vme_start: unit %d in use", unit);
216 printf("Opening VME DEVICE \n");
218 init_vme(); /* This is in the map_vme.c external file */
221 * Allocate unit structure
223 if (vmeunits[unit] != 0) {
224 vme = vmeunits[unit]; /* The one we want is okay */
226 for (i = 0; i < MAXUNITS; i++) {
227 if (!unitinuse[i] && vmeunits[i] != 0)
237 vme = (struct vmeunit *)
238 emalloc(sizeof(struct vmeunit));
239 time_vme = (struct vmedate *)malloc(sizeof(struct vmedate)); /* Added to emulate LM's refclock_gpsvme
240 (added to fix mem lead DW lmco) mjb lmco 12/22/99 */
243 bzero((char *)vme, sizeof(struct vmeunit));
244 vmeunits[unit] = vme;
247 * Set up the structures
250 vme->unit = (u_short)unit;
251 vme->timestarted = current_time;
253 vme->io.clock_recv = vme_receive;
254 vme->io.srcclock = (caddr_t)vme;
259 * All done. Initialize a few random peer variables, then
262 peer->precision = VMEPRECISION;
263 peer->stratum = stratumtouse[unit];
264 memcpy( (char *)&peer->refid, USNOREFID,4);
266 /* peer->refid = htonl(VMEHSREFID); */
274 * vme_shutdown - shut down a VME clock
281 register struct vmeunit *vme;
283 if (unit >= MAXUNITS) {
284 msyslog(LOG_ERR, "vme_shutdown: unit %d invalid", unit);
287 if (!unitinuse[unit]) {
288 msyslog(LOG_ERR, "vme_shutdown: unit %d not in use", unit);
293 * Tell the I/O module to turn us off. We're history.
296 vme = vmeunits[unit];
297 io_closeclock(&vme->io);
302 * vme_report_event - note the occurance of an event
304 * This routine presently just remembers the report and logs it, but
305 * does nothing heroic for the trap handler.
316 if (vme->status != (u_short)code) {
317 vme->status = (u_short)code;
318 if (code != CEVNT_NOMINAL)
319 vme->lastevent = (u_short)code;
321 "clock %s event %x", ntoa(&peer->srcadr), code);
327 * vme_receive - receive data from the VME device.
329 * Note: This interface would be interrupt-driven. We don't use that
330 * now, but include a dummy routine for possible future adventures.
334 struct recvbuf *rbufp
340 * vme_poll - called by the transmit procedure
348 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->usec = tptr->frac;
403 printf("vme: %3d %02d:%02d:%02d.%06ld %1x\n",
404 vme->day, vme->hour, vme->minute, vme->second,
405 vme->usec, 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 TVUTOTSF(vme->usec, vme->lastref.l_uf);
429 tstmp = vme->lastref;
431 L_SUB(&tstmp, &vme->lastrec);
434 L_ADD(&tstmp, &(fudgefactor[vme->unit]));
436 refclock_receive(vme->peer);
440 * vme_control - set fudge factors, return statistics2
445 struct refclockstat *in,
446 struct refclockstat *out,
450 register struct vmeunit *vme;
452 if (unit >= MAXUNITS) {
453 msyslog(LOG_ERR, "vme_control: unit %d invalid)", unit);
458 if (in->haveflags & CLK_HAVETIME1)
459 DTOLFP(in->fudgetime1, &fudgefactor[unit]); /* added mjb lmco 12/20/99 */
461 if (in->haveflags & CLK_HAVEVAL1) {
462 stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf);
463 if (unitinuse[unit]) {
467 * Should actually reselect clock, but
468 * will wait for the next timecode
470 vme = vmeunits[unit];
472 peer->stratum = stratumtouse[unit];
473 if (stratumtouse[unit] <= 1)
474 memcpy( (char *)&peer->refid, USNOREFID,4);
476 peer->refid = htonl(VMEHSREFID);
479 if (in->haveflags & CLK_HAVEFLAG1) {
480 sloppyclockflag[unit] = in->flags & CLK_FLAG1;
485 out->type = 16; /*set by RES SHOULD BE CHANGED */
487 = CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1;
488 out->clockdesc = VMEDESCRIPTION;
489 LFPTOD(&fudgefactor[unit], out->fudgetime1); /* added mjb lmco 12/20/99 */
491 out ->fudgetime2 = 0; /* should do what above was supposed to do mjb lmco 12/20/99 */
493 out->fudgeval1 = (long)stratumtouse[unit]; /*changed from above LONG was not
494 defined mjb lmco 12/15/99 */
497 out->flags = sloppyclockflag[unit];
498 if (unitinuse[unit]) {
499 vme = vmeunits[unit];
500 out->lencode = vme->lencode;
501 out->p_lastcode = vme->lastcode;
502 out->timereset = current_time - vme->timestarted;
503 out->polls = vme->polls;
504 out->noresponse = vme->noreply;
505 out->badformat = vme->badformat;
506 out->baddata = vme->baddata;
507 out->lastevent = vme->lastevent;
508 out->currentstatus = vme->status;
511 out->p_lastcode = "";
512 out->polls = out->noresponse = 0;
513 out->badformat = out->baddata = 0;
515 out->currentstatus = out->lastevent = CEVNT_NOMINAL;
521 * vme_buginfo - return clock dependent debugging info
526 register struct refclockbug *bug,
530 register struct vmeunit *vme;
532 if (unit >= MAXUNITS) {
533 msyslog(LOG_ERR, "vme_buginfo: unit %d invalid)", unit);
537 if (!unitinuse[unit])
539 vme = vmeunits[unit];
543 if (vme->lasttime != 0)
544 bug->values[0] = current_time - vme->lasttime;
547 bug->values[2] = (u_long)vme->year;
548 bug->values[3] = (u_long)vme->day;
549 bug->values[4] = (u_long)vme->hour;
550 bug->values[5] = (u_long)vme->minute;
551 bug->values[6] = (u_long)vme->second;
552 bug->values[7] = (u_long)vme->usec;
553 bug->values[9] = vme->yearstart;
555 bug->times[0] = vme->lastref;
556 bug->times[1] = vme->lastrec;
558 /* -------------------------------------------------------*/
559 /* get_gpsvme_time() */
560 /* R. Schmidt, USNO, 1995 */
561 /* It's ugly, but hey, it works and its free */
563 #include "gps.h" /* defines for TrueTime GPS-VME */
565 #define PBIAS 193 /* 193 microsecs to read the GPS experimentally found */
568 get_gpsvme_time(void)
570 extern struct vmedate *time_vme;
571 unsigned short set, hr, min, sec, ums, hms, status;
576 time_t mktime(),time();
577 struct tm *gmtime(), *gmt;
579 gpsmicro = (char *) malloc(7);
581 *greg = (unsigned short *)malloc(sizeof(short) * NREGS);
584 /* reference the freeze command address general register 1 */
586 /* read the registers : */
588 time_vme->year = (unsigned short) *greg[6];
590 time_vme->doy = (unsigned short) (*greg[5] & MASKDAY);
592 time_vme->hr = (unsigned short) ((*greg[4] & MASKHI) >>8);
594 time_vme->mn = (unsigned short) (*greg[4] & MASKLO);
596 time_vme->sec = (unsigned short) (*greg[3] & MASKHI) >>8;
597 /* get microseconds in 2 parts and put together */
599 hms = *greg[3] & MASKLO;
601 time_vme->status = (unsigned short) *greg[5] >>13;
603 /* reference the unfreeze command address general register 1 */
606 sprintf(gpsmicro,"%2.2x%4.4x\0", hms, ums);
607 time_vme->frac = (u_long) gpsmicro;
612 return (NULL); /* fixed mjb lmco 12/20/99 */
619 int refclock_gpsvme_bs;
620 #endif /* REFCLOCK */