Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / ntp / ntpd / refclock_bancomm.c
1 /* refclock_bancomm.c - clock driver for the  Datum/Bancomm bc635VME 
2  * Time and Frequency Processor. It requires the BANCOMM bc635VME/
3  * bc350VXI Time and Frequency Processor Module Driver for SunOS4.x 
4  * and SunOS5.x UNIX Systems. It has been tested on a UltraSparc 
5  * IIi-cEngine running Solaris 2.6.
6  * 
7  * Author(s):   Ganesh Ramasivan & Gary Cliff, Computing Devices Canada,
8  *              Ottawa, Canada
9  *
10  * Date:        July 1999
11  *
12  * Note(s):     The refclock type has been defined as 16.
13  *
14  *              This program has been modelled after the Bancomm driver
15  *              originally written by R. Schmidt of Time Service, U.S. 
16  *              Naval Observatory for a HP-UX machine. Since the original
17  *              authors no longer plan to maintain this code, all 
18  *              references to the HP-UX vme2 driver subsystem bave been
19  *              removed. Functions vme_report_event(), vme_receive(), 
20  *              vme_control() and vme_buginfo() have been deleted because
21  *              they are no longer being used.
22  *
23  *              The time on the bc635 TFP must be set to GMT due to the 
24  *              fact that NTP makes use of GMT for all its calculations.
25  *
26  *              Installation of the Datum/Bancomm driver creates the 
27  *              device file /dev/btfp0 
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #if defined(REFCLOCK) && defined(CLOCK_BANC) 
35
36 #include "ntpd.h"
37 #include "ntp_io.h"
38 #include "ntp_refclock.h"
39 #include "ntp_unixtime.h"
40 #include "ntp_stdlib.h"
41
42 #include <stdio.h>
43 #include <syslog.h>
44 #include <ctype.h>
45
46 /*  STUFF BY RES */
47 struct btfp_time                /* Structure for reading 5 time words   */
48                                 /* in one ioctl(2) operation.           */
49 {
50         unsigned short btfp_time[5];  /* Time words 0,1,2,3, and 4. (16bit)*/
51 };
52
53 /* SunOS5 ioctl commands definitions.*/
54 #define BTFPIOC            ( 'b'<< 8 )
55 #define IOCIO( l, n )      ( BTFPIOC | n )
56 #define IOCIOR( l, n, s )  ( BTFPIOC | n )
57 #define IOCIORN( l, n, s ) ( BTFPIOC | n )
58 #define IOCIOWN( l, n, s ) ( BTFPIOC | n )
59
60 /***** Simple ioctl commands *****/
61 #define RUNLOCK         IOCIOR(b, 19, int )  /* Release Capture Lockout */
62 #define RCR0            IOCIOR(b, 22, int )  /* Read control register zero.*/
63 #define WCR0            IOCIOWN(b, 23, int)          /* Write control register zero*/
64
65 /***** Compound ioctl commands *****/
66
67 /* Read all 5 time words in one call.   */
68 #define READTIME        IOCIORN(b, 32, sizeof( struct btfp_time ))
69 #define VMEFD "/dev/btfp0"
70
71 struct vmedate {               /* structure returned by get_vmetime.c */
72         unsigned short year;
73         unsigned short day;
74         unsigned short hr;
75         unsigned short mn;
76         unsigned short sec;
77         unsigned long frac;
78         unsigned short status;
79 };
80
81 /* END OF STUFF FROM RES */
82
83 /*
84  * VME interface parameters. 
85  */
86 #define VMEPRECISION    (-21)   /* precision assumed (1 us) */
87 #define USNOREFID       "BTFP"  /* or whatever */
88 #define VMEREFID        "BTFP"  /* reference id */
89 #define VMEDESCRIPTION  "Bancomm bc635 TFP" /* who we are */
90 #define VMEHSREFID      0x7f7f1000 /* 127.127.16.00 refid hi strata */
91 /* clock type 16 is used here  */
92 #define GMT             0       /* hour offset from Greenwich */
93
94 /*
95  * Imported from ntp_timer module
96  */
97 extern u_long current_time;     /* current time(s) */
98
99 /*
100  * Imported from ntpd module
101  */
102 extern int debug;               /* global debug flag */
103
104 /*
105  * VME unit control structure.
106  * Changes made to vmeunit structure. Most members are now available in the 
107  * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan
108  */
109 struct vmeunit {
110         struct vmedate vmedata; /* data returned from vme read */
111         u_long lasttime;        /* last time clock heard from */
112 };
113
114 /*
115  * Function prototypes
116  */
117 static  void    vme_init        (void);
118 static  int     vme_start       (int, struct peer *);
119 static  void    vme_shutdown    (int, struct peer *);
120 static  void    vme_receive     (struct recvbuf *);
121 static  void    vme_poll        (int unit, struct peer *);
122 struct vmedate *get_datumtime(struct vmedate *);
123
124 /*
125  * Transfer vector
126  */
127 struct  refclock refclock_bancomm = {
128         vme_start,              /* start up driver */
129         vme_shutdown,           /* shut down driver */
130         vme_poll,               /* transmit poll message */
131         noentry,                /* not used (old vme_control) */
132         noentry,                /* initialize driver */ 
133         noentry,                /* not used (old vme_buginfo) */ 
134         NOFLAGS                 /* not used */
135 };
136
137 int fd_vme;  /* file descriptor for ioctls */
138 int regvalue;
139
140
141 /*
142  * vme_start - open the VME device and initialize data for processing
143  */
144 static int
145 vme_start(
146         int unit,
147         struct peer *peer
148         )
149 {
150         register struct vmeunit *vme;
151         struct refclockproc *pp;
152         int dummy;
153         char vmedev[20];
154
155         /*
156          * Open VME device
157          */
158 #ifdef DEBUG
159
160         printf("Opening DATUM VME DEVICE \n");
161 #endif
162         if ( (fd_vme = open(VMEFD, O_RDWR)) < 0) {
163                 msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev);
164                 return (0);
165         }
166         else  { /* Release capture lockout in case it was set from before. */
167                 if( ioctl( fd_vme, RUNLOCK, &dummy ) )
168                     msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m");
169
170                 regvalue = 0; /* More esoteric stuff to do... */
171                 if( ioctl( fd_vme, WCR0, &regvalue ) )
172                     msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
173         }
174
175         /*
176          * Allocate unit structure
177          */
178         vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit));
179         bzero((char *)vme, sizeof(struct vmeunit));
180
181
182         /*
183          * Set up the structures
184          */
185         pp = peer->procptr;
186         pp->unitptr = (caddr_t) vme;
187         pp->timestarted = current_time;
188
189         pp->io.clock_recv = vme_receive;
190         pp->io.srcclock = (caddr_t)peer;
191         pp->io.datalen = 0;
192         pp->io.fd = fd_vme;
193
194         /*
195          * All done.  Initialize a few random peer variables, then
196          * return success. Note that root delay and root dispersion are
197          * always zero for this clock.
198          */
199         peer->precision = VMEPRECISION;
200         memcpy(&pp->refid, USNOREFID,4);
201         return (1);
202 }
203
204
205 /*
206  * vme_shutdown - shut down a VME clock
207  */
208 static void
209 vme_shutdown(
210         int unit, 
211         struct peer *peer
212         )
213 {
214         register struct vmeunit *vme;
215         struct refclockproc *pp;
216
217         /*
218          * Tell the I/O module to turn us off.  We're history.
219          */
220         pp = peer->procptr;
221         vme = (struct vmeunit *)pp->unitptr;
222         io_closeclock(&pp->io);
223         pp->unitptr = NULL;
224         free(vme);
225 }
226
227
228 /*
229  * vme_receive - receive data from the VME device.
230  *
231  * Note: This interface would be interrupt-driven. We don't use that
232  * now, but include a dummy routine for possible future adventures.
233  */
234 static void
235 vme_receive(
236         struct recvbuf *rbufp
237         )
238 {
239 }
240
241
242 /*
243  * vme_poll - called by the transmit procedure
244  */
245 static void
246 vme_poll(
247         int unit,
248         struct peer *peer
249         )
250 {
251         struct vmedate *tptr; 
252         struct vmeunit *vme;
253         struct refclockproc *pp;
254         time_t tloc;
255         struct tm *tadr;
256         
257         pp = peer->procptr;      
258         vme = (struct vmeunit *)pp->unitptr;        /* Here is the structure */
259
260         tptr = &vme->vmedata; 
261         if ((tptr = get_datumtime(tptr)) == NULL ) {
262                 refclock_report(peer, CEVNT_BADREPLY);
263                 return;
264         }
265
266         get_systime(&pp->lastrec);
267         pp->polls++;
268         vme->lasttime = current_time;
269
270         /*
271          * Get VME time and convert to timestamp format. 
272          * The year must come from the system clock.
273          */
274         
275           time(&tloc);
276           tadr = gmtime(&tloc);
277           tptr->year = (unsigned short)(tadr->tm_year + 1900);
278         
279
280         sprintf(pp->a_lastcode, 
281                 "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
282                 tptr->day, 
283                 tptr->hr, 
284                 tptr->mn,
285                 tptr->sec, 
286                 tptr->frac, 
287                 tptr->status);
288
289         pp->lencode = (u_short) strlen(pp->a_lastcode);
290
291         pp->day =  tptr->day;
292         pp->hour =   tptr->hr;
293         pp->minute =  tptr->mn;
294         pp->second =  tptr->sec;
295         pp->usec =   tptr->frac;        
296
297 #ifdef DEBUG
298         if (debug)
299             printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
300                    pp->day, pp->hour, pp->minute, pp->second,
301                    pp->usec, tptr->status);
302 #endif
303         if (tptr->status ) {       /*  Status 0 is locked to ref., 1 is not */
304                 refclock_report(peer, CEVNT_BADREPLY);
305                 return;
306         }
307
308         /*
309          * Now, compute the reference time value. Use the heavy
310          * machinery for the seconds and the millisecond field for the
311          * fraction when present. If an error in conversion to internal
312          * format is found, the program declares bad data and exits.
313          * Note that this code does not yet know how to do the years and
314          * relies on the clock-calendar chip for sanity.
315          */
316         if (!refclock_process(pp)) {
317                 refclock_report(peer, CEVNT_BADTIME);
318                 return;
319         }
320         record_clock_stats(&peer->srcadr, pp->a_lastcode);
321         refclock_receive(peer);
322 }
323
324 struct vmedate *
325 get_datumtime(struct vmedate *time_vme)
326 {
327         unsigned short  status;
328         char cbuf[7];
329         struct btfp_time vts;
330         
331         if ( time_vme == (struct vmedate *)NULL) {
332           time_vme = (struct vmedate *)malloc(sizeof(struct vmedate ));
333         }
334
335         if( ioctl(fd_vme, READTIME, &vts))
336             msyslog(LOG_ERR, "get_datumtime error: %m");
337
338         /* if you want to actually check the validity of these registers, do a 
339            define of CHECK   above this.  I didn't find it necessary. - RES
340         */
341
342 #ifdef CHECK            
343
344         /* Get day */
345         sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) +
346                 ((vts.btfp_time[ 1 ] & 0xff00) >> 8));  
347
348         if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2]) )
349             time_vme->day = (unsigned short)atoi(cbuf);
350         else
351             time_vme->day = (unsigned short) 0;
352
353         /* Get hour */
354         sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff);
355
356         if (isdigit(cbuf[0]) && isdigit(cbuf[1]))
357             time_vme->hr = (unsigned short)atoi(cbuf);
358         else
359             time_vme->hr = (unsigned short) 0;
360
361         /* Get minutes */
362         sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8);
363         if (isdigit(cbuf[0]) && isdigit(cbuf[1]))
364             time_vme->mn = (unsigned short)atoi(cbuf);
365         else
366             time_vme->mn = (unsigned short) 0;
367
368         /* Get seconds */
369         sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff);
370
371         if (isdigit(cbuf[0]) && isdigit(cbuf[1]))
372             time_vme->sec = (unsigned short)atoi(cbuf);
373         else
374             time_vme->sec = (unsigned short) 0;
375
376         /* Get microseconds.  Yes, we ignore the 0.1 microsecond digit so we can
377            use the TVTOTSF function  later on...*/
378
379         sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ],
380                 vts.btfp_time[ 4 ]>>8);
381
382         if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2])
383             && isdigit(cbuf[3]) && isdigit(cbuf[4]) && isdigit(cbuf[5]))
384             time_vme->frac = (u_long) atoi(cbuf);
385         else
386             time_vme->frac = (u_long) 0;
387 #else
388
389         /* DONT CHECK  just trust the card */
390
391         /* Get day */
392         sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) +
393                 ((vts.btfp_time[ 1 ] & 0xff00) >> 8));  
394         time_vme->day = (unsigned short)atoi(cbuf);
395
396         /* Get hour */
397         sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff);
398
399         time_vme->hr = (unsigned short)atoi(cbuf);
400
401         /* Get minutes */
402         sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8);
403         time_vme->mn = (unsigned short)atoi(cbuf);
404
405         /* Get seconds */
406         sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff);
407         time_vme->sec = (unsigned short)atoi(cbuf);
408
409         /* Get microseconds.  Yes, we ignore the 0.1 microsecond digit so we can
410            use the TVTOTSF function  later on...*/
411
412         sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ],
413                 vts.btfp_time[ 4 ]>>8);
414
415         time_vme->frac = (u_long) atoi(cbuf);
416
417 #endif /* CHECK */
418
419         /* Get status bit */
420         status = (vts.btfp_time[0] & 0x0010) >>4;
421         time_vme->status = status;  /* Status=0 if locked to ref. */
422         /* Status=1 if flywheeling */
423         if (status) {        /* lost lock ? */
424                 return ((void *)NULL);
425         }
426         else
427             return (time_vme);
428 }
429
430 #else
431 int refclock_bancomm_bs;
432 #endif /* REFCLOCK */