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