Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / ntp / ntpd / refclock_gpsvme.c
1 /*
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
4  * 
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
9  *      CLOCKDEFS=   -DGPSVME
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. 
13  */
14 #ifdef HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17
18 #if defined(REFCLOCK) && defined(CLOCK_GPSVME) 
19
20 #include "ntpd.h"
21 #include "ntp_io.h"
22 #include "ntp_refclock.h"
23 #include "ntp_unixtime.h"
24 #include "ntp_stdlib.h"
25
26 #include <stdio.h>
27 #include <syslog.h>
28 #include <ctype.h>
29
30 #include "gps.h"
31 #include "/etc/conf/h/io.h"
32
33 /* GLOBAL STUFF BY RES */
34
35 #include <time.h>
36
37 #define PRIO    120             /* set the realtime priority */
38 #define NREGS   7               /* number of registers we will use */
39
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 */
45
46 extern caddr_t map_vme ();   
47 extern void unmap_vme();        /* Unmaps the VME space */
48
49 struct vmedate {                /* structure needed by ntp */
50         unsigned short year;    /* *tptr is a pointer to this */
51         unsigned short doy;
52         unsigned short hr;
53         unsigned short mn;
54         unsigned short sec;
55         unsigned long frac;
56         unsigned short status;
57 };
58
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 */
62
63 /* END OF STUFF FROM RES */
64
65 /*
66  * Definitions
67  */
68 #define MAXUNITS 2              /* max number of VME units */
69 #define BMAX  50        /* timecode buffer length */
70
71 /*
72  * VME interface parameters. 
73  */
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 */
79
80 /* I'm using clock type 16 until one is assigned */
81 /* This is set also in vme_control, below        */
82
83
84 #define GMT             0       /* hour offset from Greenwich */
85
86 /*
87  * VME unit control structure.
88  */
89 struct vmeunit {
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 */
109         /*
110          * Status tallies
111          */
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 */
118 };
119
120 /*
121  * Data space for the unit structures.  Note that we allocate these on
122  * the fly, but never give them back.
123  */
124 static struct vmeunit *vmeunits[MAXUNITS];
125 static u_char unitinuse[MAXUNITS];
126
127 /*
128  * Keep the fudge factors separately so they can be set even
129  * when no clock is configured.
130  */
131 static l_fp fudgefactor[MAXUNITS];
132 static u_char stratumtouse[MAXUNITS];
133 static u_char sloppyclockflag[MAXUNITS];
134
135 /*
136  * Function prototypes
137  */
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 *);
146
147 /*
148  * Transfer vector
149  */
150 struct  refclock refclock_gpsvme = {
151         vme_start, vme_shutdown, vme_poll,
152         vme_control, vme_init, vme_buginfo, NOFLAGS
153 };
154
155 int fd_vme;  /* file descriptor for ioctls */
156 int regvalue;
157
158 /*
159  * vme_init - initialize internal vme driver data
160  */
161 static void
162 vme_init(void)
163 {
164         register int i;
165         /*
166          * Just zero the data arrays
167          */
168         /*
169           bzero((char *)vmeunits, sizeof vmeunits);
170           bzero((char *)unitinuse, sizeof unitinuse);
171         */
172
173         /*
174          * Initialize fudge factors to default.
175          */
176         for (i = 0; i < MAXUNITS; i++) {
177                 fudgefactor[i].l_ui = 0;
178                 fudgefactor[i].l_uf = 0;
179                 stratumtouse[i] = 0;
180                 sloppyclockflag[i] = 0;
181         }
182 }
183
184 /*
185  * vme_start - open the VME device and initialize data for processing
186  */
187 static int
188 vme_start(
189         u_int unit,
190         struct peer *peer
191         )
192 {
193         register struct vmeunit *vme;
194         register int i;
195         int dummy;
196         char vmedev[20];
197
198         /*
199          * Check configuration info.
200          */
201         if (unit >= MAXUNITS) {
202                 msyslog(LOG_ERR, "vme_start: unit %d invalid", unit);
203                 return (0);
204         }
205         if (unitinuse[unit]) {
206                 msyslog(LOG_ERR, "vme_start: unit %d in use", unit);
207                 return (0);
208         }
209
210         /*
211          * Open VME device
212          */
213 #ifdef DEBUG
214
215         printf("Opening  VME DEVICE \n");
216 #endif
217         init_vme();   /* This is in the map_vme.c external file */
218
219         /*
220          * Allocate unit structure
221          */
222         if (vmeunits[unit] != 0) {
223                 vme = vmeunits[unit];   /* The one we want is okay */
224         } else {
225                 for (i = 0; i < MAXUNITS; i++) {
226                         if (!unitinuse[i] && vmeunits[i] != 0)
227                             break;
228                 }
229                 if (i < MAXUNITS) {
230                         /*
231                          * Reclaim this one
232                          */
233                         vme = vmeunits[i];
234                         vmeunits[i] = 0;
235                 } else {
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 */
240                 }
241         }
242         bzero((char *)vme, sizeof(struct vmeunit));
243         vmeunits[unit] = vme;
244
245         /*
246          * Set up the structures
247          */
248         vme->peer = peer;
249         vme->unit = (u_short)unit;
250         vme->timestarted = current_time;
251
252         vme->io.clock_recv = vme_receive;
253         vme->io.srcclock = (caddr_t)vme;
254         vme->io.datalen = 0;
255         vme->io.fd = fd_vme;
256
257         /*
258          * All done.  Initialize a few random peer variables, then
259          * return success.
260          */
261         peer->precision = VMEPRECISION;
262         peer->stratum = stratumtouse[unit];
263         memcpy( (char *)&peer->refid, USNOREFID,4);
264
265         /* peer->refid = htonl(VMEHSREFID); */
266
267         unitinuse[unit] = 1;
268         return (1);
269 }
270
271
272 /*
273  * vme_shutdown - shut down a VME clock
274  */
275 static void
276 vme_shutdown(
277         int unit
278         )
279 {
280         register struct vmeunit *vme;
281
282         if (unit >= MAXUNITS) {
283                 msyslog(LOG_ERR, "vme_shutdown: unit %d invalid", unit);
284                 return;
285         }
286         if (!unitinuse[unit]) {
287                 msyslog(LOG_ERR, "vme_shutdown: unit %d not in use", unit);
288                 return;
289         }
290
291         /*
292          * Tell the I/O module to turn us off.  We're history.
293          */
294         unmap_vme();
295         vme = vmeunits[unit];
296         io_closeclock(&vme->io);
297         unitinuse[unit] = 0;
298 }
299
300 /*
301  * vme_report_event - note the occurance of an event
302  *
303  * This routine presently just remembers the report and logs it, but
304  * does nothing heroic for the trap handler.
305  */
306 static void
307 vme_report_event(
308         struct vmeunit *vme,
309         int code
310         )
311 {
312         struct peer *peer;
313         
314         peer = vme->peer;
315         if (vme->status != (u_short)code) {
316                 vme->status = (u_short)code;
317                 if (code != CEVNT_NOMINAL)
318                     vme->lastevent = (u_short)code;
319                 msyslog(LOG_INFO,
320                         "clock %s event %x", ntoa(&peer->srcadr), code);
321         }
322 }
323
324
325 /*
326  * vme_receive - receive data from the VME device.
327  *
328  * Note: This interface would be interrupt-driven. We don't use that
329  * now, but include a dummy routine for possible future adventures.
330  */
331 static void
332 vme_receive(
333         struct recvbuf *rbufp
334         )
335 {
336 }
337
338 /*
339  * vme_poll - called by the transmit procedure
340  */
341 static void
342 vme_poll(
343         int unit,
344         struct peer *peer
345         )
346 {
347         struct vmedate *tptr; 
348         struct vmeunit *vme;
349         l_fp tstmp;
350         time_t tloc;
351         struct tm *tadr;
352
353         
354  
355         if (unit >= MAXUNITS) {
356                 msyslog(LOG_ERR, "vme_poll: unit %d invalid", unit);
357                 return;
358         }
359         if (!unitinuse[unit]) {
360                 msyslog(LOG_ERR, "vme_poll: unit %d not in use", unit);
361                 return;
362         }
363         vme = vmeunits[unit];        /* Here is the structure */
364         vme->polls++;
365
366         tptr = &vme->vmedata; 
367         
368         if ((tptr = get_gpsvme_time()) == NULL ) {
369                 vme_report_event(vme, CEVNT_BADREPLY);
370                 return;
371         }
372
373         get_systime(&vme->lastrec);
374         vme->lasttime = current_time;
375
376         /*
377          * Get VME time and convert to timestamp format. 
378          * The year must come from the system clock.
379          */
380         /*
381           time(&tloc);
382           tadr = gmtime(&tloc);
383           tptr->year = (unsigned short)(tadr->tm_year + 1900);
384         */
385
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);
390
391         record_clock_stats(&(vme->peer->srcadr), vme->lastcode);
392         vme->lencode = (u_short) strlen(vme->lastcode);
393
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;
399
400 #ifdef DEBUG
401         if (debug)
402             printf("vme: %3d %02d:%02d:%02d.%06ld %1x\n",
403                    vme->day, vme->hour, vme->minute, vme->second,
404                    vme->usec, tptr->status);
405 #endif
406         if (tptr->status ) {       /*  Status 0 is locked to ref., 1 is not */
407                 vme_report_event(vme, CEVNT_BADREPLY);
408                 return;
409         }
410
411         /*
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.
418          */
419         if (!clocktime(vme->day, vme->hour, vme->minute,
420                        vme->second, GMT, vme->lastrec.l_ui,
421                        &vme->yearstart, &vme->lastref.l_ui)) {
422                 vme->baddata++;
423                 vme_report_event(vme, CEVNT_BADTIME);
424                 msyslog(LOG_ERR, "refclock_gpsvme: bad data!!");
425                 return;
426         }
427         TVUTOTSF(vme->usec, vme->lastref.l_uf);
428         tstmp = vme->lastref;
429
430         L_SUB(&tstmp, &vme->lastrec);
431         vme->coderecv++;
432
433         L_ADD(&tstmp, &(fudgefactor[vme->unit]));
434
435         refclock_receive(vme->peer);
436 }
437
438 /*
439  * vme_control - set fudge factors, return statistics2
440  */
441 static void
442 vme_control(
443         u_int unit,
444         struct refclockstat *in,
445         struct refclockstat *out,
446    struct peer * peer
447         )
448 {
449         register struct vmeunit *vme;
450
451         if (unit >= MAXUNITS) {
452                 msyslog(LOG_ERR, "vme_control: unit %d invalid)", unit);
453                 return;
454         }
455
456         if (in != 0) {
457                 if (in->haveflags & CLK_HAVETIME1)
458                    DTOLFP(in->fudgetime1, &fudgefactor[unit]);  /* added mjb lmco 12/20/99 */
459
460                 if (in->haveflags & CLK_HAVEVAL1) {
461                         stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf);
462                         if (unitinuse[unit]) {
463                                 struct peer *peer;
464
465                                 /*
466                                  * Should actually reselect clock, but
467                                  * will wait for the next timecode
468                                  */
469                                 vme = vmeunits[unit];
470                                 peer = vme->peer;
471                                 peer->stratum = stratumtouse[unit];
472                                 if (stratumtouse[unit] <= 1)
473                                     memcpy( (char *)&peer->refid, USNOREFID,4);
474                                 else
475                                     peer->refid = htonl(VMEHSREFID);
476                         }
477                 }
478                 if (in->haveflags & CLK_HAVEFLAG1) {
479                         sloppyclockflag[unit] = in->flags & CLK_FLAG1;
480                 }
481         }
482
483         if (out != 0) {
484                 out->type = 16;  /*set  by RES  SHOULD BE CHANGED */
485                 out->haveflags
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 */
489
490       out ->fudgetime2 = 0;  /* should do what above was supposed to do  mjb lmco 12/20/99 */
491
492                 out->fudgeval1 = (long)stratumtouse[unit];  /*changed from above LONG was not
493                                                                       defined  mjb lmco 12/15/99 */
494
495                 out->fudgeval2 = 0;
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;
508                 } else {
509                         out->lencode = 0;
510          out->p_lastcode = "";
511                         out->polls = out->noresponse = 0;
512                         out->badformat = out->baddata = 0;
513                         out->timereset = 0;
514                         out->currentstatus = out->lastevent = CEVNT_NOMINAL;
515                 }
516         }
517 }
518
519 /*
520  * vme_buginfo - return clock dependent debugging info
521  */
522 static void
523 vme_buginfo(
524         int unit,
525         register struct refclockbug *bug,
526         struct peer * peer
527         )
528 {
529         register struct vmeunit *vme;
530
531         if (unit >= MAXUNITS) {
532                 msyslog(LOG_ERR, "vme_buginfo: unit %d invalid)", unit);
533                 return;
534         }
535
536         if (!unitinuse[unit])
537             return;
538         vme = vmeunits[unit];
539
540         bug->nvalues = 11;
541         bug->ntimes = 5;
542         if (vme->lasttime != 0)
543             bug->values[0] = current_time - vme->lasttime;
544         else
545             bug->values[0] = 0;
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;
553         bug->stimes = 0x1c;
554         bug->times[0] = vme->lastref;
555         bug->times[1] = vme->lastrec;
556 }
557 /* -------------------------------------------------------*/
558 /* get_gpsvme_time()                                      */
559 /*  R. Schmidt, USNO, 1995                                */
560 /*  It's ugly, but hey, it works and its free             */
561
562 #include "gps.h"  /* defines for TrueTime GPS-VME */
563
564 #define PBIAS  193 /* 193 microsecs to read the GPS  experimentally found */
565
566 struct vmedate *
567 get_gpsvme_time(void)
568 {
569         extern struct vmedate  *time_vme;
570         unsigned short set, hr, min, sec, ums, hms, status;
571         int ret;
572         char ti[3];
573
574         long tloc ;
575         time_t  mktime(),time();
576         struct tm *gmtime(), *gmt;
577         char  *gpsmicro;
578         gpsmicro = (char *) malloc(7);  
579
580         *greg = (unsigned short *)malloc(sizeof(short) * NREGS);
581
582
583         /*  reference the freeze command address general register 1 */
584         set = *greg[0];
585         /*  read the registers : */
586         /* get year */
587         time_vme->year  = (unsigned short)  *greg[6];  
588         /* Get doy */
589         time_vme->doy =  (unsigned short) (*greg[5] & MASKDAY);  
590         /* Get hour */
591         time_vme->hr =  (unsigned short) ((*greg[4] & MASKHI) >>8);
592         /* Get minutes */
593         time_vme->mn = (unsigned short)  (*greg[4] & MASKLO);
594         /* Get seconds */
595         time_vme->sec = (unsigned short)  (*greg[3] & MASKHI) >>8;
596         /* get microseconds in 2 parts and put together */
597         ums  =   *greg[2];
598         hms  =   *greg[3] & MASKLO;
599
600         time_vme->status = (unsigned short) *greg[5] >>13;
601
602         /*  reference the unfreeze command address general register 1 */
603         set = *greg[1];
604
605         sprintf(gpsmicro,"%2.2x%4.4x\0", hms, ums);
606         time_vme->frac = (u_long) gpsmicro;
607
608         /*      unmap_vme(); */
609
610         if (!status) { 
611       return (NULL);  /* fixed mjb lmco 12/20/99 */
612         }
613         else
614             return (time_vme);
615 }
616
617 #else
618 int refclock_gpsvme_bs;
619 #endif /* REFCLOCK */