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 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 */
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();
61 /* END OF STUFF FROM RES */
66 #define MAXUNITS 2 /* max number of VME units */
67 #define BMAX 50 /* timecode buffer length */
70 * VME interface parameters.
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 */
78 /* I'm using clock type 16 until one is assigned */
79 /* This is set also in vme_control, below */
82 #define GMT 0 /* hour offset from Greenwich */
85 * VME unit control structure.
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 */
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 */
119 * Data space for the unit structures. Note that we allocate these on
120 * the fly, but never give them back.
122 static struct vmeunit *vmeunits[MAXUNITS];
123 static u_char unitinuse[MAXUNITS];
126 * Keep the fudge factors separately so they can be set even
127 * when no clock is configured.
129 static l_fp fudgefactor[MAXUNITS];
130 static u_char stratumtouse[MAXUNITS];
131 static u_char sloppyclockflag[MAXUNITS];
134 * Function prototypes
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 *);
148 struct refclock refclock_gpsvme = {
149 vme_start, vme_shutdown, vme_poll,
150 vme_control, vme_init, vme_buginfo, NOFLAGS
153 int fd_vme; /* file descriptor for ioctls */
157 * vme_init - initialize internal vme driver data
164 * Just zero the data arrays
167 bzero((char *)vmeunits, sizeof vmeunits);
168 bzero((char *)unitinuse, sizeof unitinuse);
172 * Initialize fudge factors to default.
174 for (i = 0; i < MAXUNITS; i++) {
175 fudgefactor[i].l_ui = 0;
176 fudgefactor[i].l_uf = 0;
178 sloppyclockflag[i] = 0;
183 * vme_start - open the VME device and initialize data for processing
191 register struct vmeunit *vme;
197 * Check configuration info.
199 if (unit >= MAXUNITS) {
200 msyslog(LOG_ERR, "vme_start: unit %d invalid", unit);
203 if (unitinuse[unit]) {
204 msyslog(LOG_ERR, "vme_start: unit %d in use", unit);
213 printf("Opening VME DEVICE \n");
215 init_vme(); /* This is in the map_vme.c external file */
218 * Allocate unit structure
220 if (vmeunits[unit] != 0) {
221 vme = vmeunits[unit]; /* The one we want is okay */
223 for (i = 0; i < MAXUNITS; i++) {
224 if (!unitinuse[i] && vmeunits[i] != 0)
234 vme = (struct vmeunit *)
235 emalloc(sizeof(struct vmeunit));
238 bzero((char *)vme, sizeof(struct vmeunit));
239 vmeunits[unit] = vme;
242 * Set up the structures
245 vme->unit = (u_short)unit;
246 vme->timestarted = current_time;
248 vme->io.clock_recv = vme_receive;
249 vme->io.srcclock = (caddr_t)vme;
254 * All done. Initialize a few random peer variables, then
257 peer->precision = VMEPRECISION;
258 peer->stratum = stratumtouse[unit];
259 memcpy( (char *)&peer->refid, USNOREFID,4);
261 /* peer->refid = htonl(VMEHSREFID); */
269 * vme_shutdown - shut down a VME clock
276 register struct vmeunit *vme;
278 if (unit >= MAXUNITS) {
279 msyslog(LOG_ERR, "vme_shutdown: unit %d invalid", unit);
282 if (!unitinuse[unit]) {
283 msyslog(LOG_ERR, "vme_shutdown: unit %d not in use", unit);
288 * Tell the I/O module to turn us off. We're history.
291 vme = vmeunits[unit];
292 io_closeclock(&vme->io);
297 * vme_report_event - note the occurance of an event
299 * This routine presently just remembers the report and logs it, but
300 * does nothing heroic for the trap handler.
311 if (vme->status != (u_short)code) {
312 vme->status = (u_short)code;
313 if (code != CEVNT_NOMINAL)
314 vme->lastevent = (u_short)code;
316 "clock %s event %x", ntoa(&peer->srcadr), code);
322 * vme_receive - receive data from the VME device.
324 * Note: This interface would be interrupt-driven. We don't use that
325 * now, but include a dummy routine for possible future adventures.
329 struct recvbuf *rbufp
335 * vme_poll - called by the transmit procedure
343 struct vmedate *tptr;
350 vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit *));
351 tptr = (struct vmedate *)emalloc(sizeof(struct vmedate *));
354 if (unit >= MAXUNITS) {
355 msyslog(LOG_ERR, "vme_poll: unit %d invalid", unit);
358 if (!unitinuse[unit]) {
359 msyslog(LOG_ERR, "vme_poll: unit %d not in use", unit);
362 vme = vmeunits[unit]; /* Here is the structure */
365 tptr = &vme->vmedata;
367 if ((tptr = get_gpsvme_time()) == NULL ) {
368 vme_report_event(vme, CEVNT_BADREPLY);
372 get_systime(&vme->lastrec);
373 vme->lasttime = current_time;
376 * Get VME time and convert to timestamp format.
377 * The year must come from the system clock.
381 tadr = gmtime(&tloc);
382 tptr->year = (unsigned short)(tadr->tm_year + 1900);
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);
390 record_clock_stats(&(vme->peer->srcadr), vme->lastcode);
391 vme->lencode = (u_short) strlen(vme->lastcode);
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;
401 printf("vme: %3d %02d:%02d:%02d.%06ld %1x\n",
402 vme->day, vme->hour, vme->minute, vme->second,
403 vme->usec, tptr->status);
405 if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */
406 vme_report_event(vme, CEVNT_BADREPLY);
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.
418 if (!clocktime(vme->day, vme->hour, vme->minute,
419 vme->second, GMT, vme->lastrec.l_ui,
420 &vme->yearstart, &vme->lastref.l_ui)) {
422 vme_report_event(vme, CEVNT_BADTIME);
423 msyslog(LOG_ERR, "refclock_gpsvme: bad data!!");
426 TVUTOTSF(vme->usec, vme->lastref.l_uf);
427 tstmp = vme->lastref;
429 L_SUB(&tstmp, &vme->lastrec);
432 L_ADD(&tstmp, &(fudgefactor[vme->unit]));
434 refclock_receive(vme->peer);
438 * vme_control - set fudge factors, return statistics
443 struct refclockstat *in,
444 struct refclockstat *out
447 register struct vmeunit *vme;
449 if (unit >= MAXUNITS) {
450 msyslog(LOG_ERR, "vme_control: unit %d invalid)", unit);
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]) {
463 * Should actually reselect clock, but
464 * will wait for the next timecode
466 vme = vmeunits[unit];
468 peer->stratum = stratumtouse[unit];
469 if (stratumtouse[unit] <= 1)
470 memcpy( (char *)&peer->refid, USNOREFID,4);
472 peer->refid = htonl(VMEHSREFID);
475 if (in->haveflags & CLK_HAVEFLAG1) {
476 sloppyclockflag[unit] = in->flags & CLK_FLAG1;
481 out->type = 16; /*set by RES SHOULD BE CHANGED */
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];
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;
505 out->polls = out->noresponse = 0;
506 out->badformat = out->baddata = 0;
508 out->currentstatus = out->lastevent = CEVNT_NOMINAL;
514 * vme_buginfo - return clock dependent debugging info
519 register struct refclockbug *bug
522 register struct vmeunit *vme;
524 if (unit >= MAXUNITS) {
525 msyslog(LOG_ERR, "vme_buginfo: unit %d invalid)", unit);
529 if (!unitinuse[unit])
531 vme = vmeunits[unit];
535 if (vme->lasttime != 0)
536 bug->values[0] = current_time - vme->lasttime;
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;
547 bug->times[0] = vme->lastref;
548 bug->times[1] = vme->lastrec;
550 /* -------------------------------------------------------*/
551 /* get_gpsvme_time() */
552 /* R. Schmidt, USNO, 1995 */
553 /* It's ugly, but hey, it works and its free */
555 #include "gps.h" /* defines for TrueTime GPS-VME */
557 #define PBIAS 193 /* 193 microsecs to read the GPS experimentally found */
560 get_gpsvme_time(void)
562 struct vmedate *time_vme;
563 unsigned short set, hr, min, sec, ums, hms, status;
568 time_t mktime(),time();
569 struct tm *gmtime(), *gmt;
571 gpsmicro = (char *) malloc(7);
573 time_vme = (struct vmedate *)malloc(sizeof(struct vmedate ));
574 *greg = (unsigned short *)malloc(sizeof(short) * NREGS);
577 /* reference the freeze command address general register 1 */
579 /* read the registers : */
581 time_vme->year = (unsigned short) *greg[6];
583 time_vme->doy = (unsigned short) (*greg[5] & MASKDAY);
585 time_vme->hr = (unsigned short) ((*greg[4] & MASKHI) >>8);
587 time_vme->mn = (unsigned short) (*greg[4] & MASKLO);
589 time_vme->sec = (unsigned short) (*greg[3] & MASKHI) >>8;
590 /* get microseconds in 2 parts and put together */
592 hms = *greg[3] & MASKLO;
594 time_vme->status = (unsigned short) *greg[5] >>13;
596 /* reference the unfreeze command address general register 1 */
599 sprintf(gpsmicro,"%2.2x%4.4x\0", hms, ums);
600 time_vme->frac = (u_long) gpsmicro;
605 return ((void *)NULL);
612 int refclock_gpsvme_bs;
613 #endif /* REFCLOCK */