]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/ntp/ntpd/refclock_bancomm.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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  *      04/28/2005 Rob Neal 
30  *              Modified to add support for Symmetricom bc637PCI-U Time & 
31  *              Frequency Processor. 
32  *              Card bus type (VME/VXI or PCI) and environment are specified via the
33  *              "mode" keyword on the server command in ntp.conf.
34  *              server 127.127.16.u prefer mode m (...) 
35  *              Modes currently supported are 
36  *              1               : FreeBSD PCI 635/637.
37  *              2               : Linux or Windows PCI 635/637.
38  *              not specified, or other number: 
39  *                              : Assumed to be VME/VXI legacy Bancomm card on Solaris.
40  *              Linux and Windows platforms require Symmetricoms' proprietary driver
41  *              for the TFP card. 
42  *              Tested on FreeBSD 5.3 with a 637 card. 
43  */
44
45 #ifdef HAVE_CONFIG_H
46 #include <config.h>
47 #endif
48
49 #if defined(REFCLOCK) && defined(CLOCK_BANC) 
50
51 #include "ntpd.h"
52 #include "ntp_io.h"
53 #include "ntp_refclock.h"
54 #include "ntp_unixtime.h"
55 #include "ntp_stdlib.h"
56
57 #include <stdio.h>
58 #include <syslog.h>
59 #include <ctype.h>
60
61 /*  STUFF BY RES */
62 struct btfp_time                /* Structure for reading 5 time words   */
63                                 /* in one ioctl(2) operation.           */
64 {
65         unsigned short btfp_time[5];  /* Time words 0,1,2,3, and 4. (16bit)*/
66 };
67 /* SunOS5 ioctl commands definitions.*/
68 #define BTFPIOC            ( 'b'<< 8 )
69 #define IOCIO( l, n )      ( BTFPIOC | n )
70 #define IOCIOR( l, n, s )  ( BTFPIOC | n )
71 #define IOCIORN( l, n, s ) ( BTFPIOC | n )
72 #define IOCIOWN( l, n, s ) ( BTFPIOC | n )
73
74 /***** Simple ioctl commands *****/
75 #define RUNLOCK         IOCIOR(b, 19, int )  /* Release Capture Lockout */
76 #define RCR0            IOCIOR(b, 22, int )  /* Read control register zero.*/
77 #define WCR0            IOCIOWN(b, 23, int)          /* Write control register zero*/
78 /***** Compound ioctl commands *****/
79
80 /* Read all 5 time words in one call.   */
81 #define READTIME        IOCIORN(b, 32, sizeof( struct btfp_time ))
82
83 #if defined(__FreeBSD__) 
84 #undef  READTIME
85 #define READTIME        _IOR('u', 5, struct btfp_time )
86 #endif 
87
88 #define VMEFD "/dev/btfp0"
89
90 struct vmedate {               /* structure returned by get_vmetime.c */
91         unsigned short year;
92         unsigned short day;
93         unsigned short hr;
94         unsigned short mn;
95         unsigned short sec;
96         long frac;
97         unsigned short status;
98 };
99
100 /* END OF STUFF FROM RES */
101 typedef void *SYMMT_PCI_HANDLE;
102
103 /*
104  * VME interface parameters. 
105  */
106 #define VMEPRECISION    (-21)   /* precision assumed (1 us) */
107 #define USNOREFID       "BTFP"  /* or whatever */
108 #define VMEREFID        "BTFP"  /* reference id */
109 #define VMEDESCRIPTION  "Bancomm bc635 TFP" /* who we are */
110 #define VMEHSREFID      0x7f7f1000 /* 127.127.16.00 refid hi strata */
111 /* clock type 16 is used here  */
112 #define GMT             0       /* hour offset from Greenwich */
113
114 /*
115  * Imported from ntp_timer module
116  */
117 extern u_long current_time;     /* current time(s) */
118
119 /*
120  * Imported from ntpd module
121  */
122 extern volatile int debug;               /* global debug flag */
123
124 /*
125  * VME unit control structure.
126  * Changes made to vmeunit structure. Most members are now available in the 
127  * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan
128  */
129 struct vmeunit {
130         struct vmedate vmedata; /* data returned from vme read */
131         u_long lasttime;        /* last time clock heard from */
132 };
133
134 /*
135  * Function prototypes
136  */
137 static  int     vme_start       (int, struct peer *);
138 static  void    vme_shutdown    (int, struct peer *);
139 static  void    vme_receive     (struct recvbuf *);
140 static  void    vme_poll        (int unit, struct peer *);
141 struct vmedate *get_datumtime(struct vmedate *);        
142 void    tvme_fill(struct vmedate *, uint32_t btm[2]);
143 /*
144  * Define the bc*() functions as weak so we can compile/link without them.
145  * Only clients with the card will have the proprietary vendor device driver
146  * and interface library needed for use on Linux/Windows platforms.
147  */
148 extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, uint32_t *, uint32_t*, uint8_t*);
149 extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void);
150 extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE);
151
152 /*
153  * Transfer vector
154  */
155 struct  refclock refclock_bancomm = {
156         vme_start,              /* start up driver */
157         vme_shutdown,           /* shut down driver */
158         vme_poll,               /* transmit poll message */
159         noentry,                /* not used (old vme_control) */
160         noentry,                /* initialize driver */ 
161         noentry,                /* not used (old vme_buginfo) */ 
162         NOFLAGS                 /* not used */
163 };
164
165 int fd_vme;  /* file descriptor for ioctls */
166 int regvalue;
167 int tfp_type;   /* mode selector, indicate platform and driver interface */
168 SYMMT_PCI_HANDLE stfp_handle;
169
170
171 /*
172  * vme_start - open the VME device and initialize data for processing
173  */
174 static int
175 vme_start(
176         int unit,
177         struct peer *peer
178         )
179 {
180         register struct vmeunit *vme;
181         struct refclockproc *pp;
182         int dummy;
183         char vmedev[20];
184         
185         tfp_type = (int)(peer->ttl);
186         switch (tfp_type) {             
187                 case 1:
188                         break;
189                 case 2:
190                         stfp_handle = bcStartPci();     /* init the card in lin/win */
191                         break;
192                 default:
193                         break;
194         }
195         /*
196          * Open VME device
197          */
198 #ifdef DEBUG
199
200         printf("Opening DATUM VME DEVICE \n");
201 #endif
202         if ( (fd_vme = open(VMEFD, O_RDWR)) < 0) {
203                 msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev);
204                 return (0);
205         }
206         else  { 
207                 switch (tfp_type) {
208                         case 1: break;
209                         case 2: break;
210                         default: 
211                                 /* Release capture lockout in case it was set before. */
212                                 if( ioctl( fd_vme, RUNLOCK, &dummy ) )
213                                 msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m");
214
215                                 regvalue = 0; /* More esoteric stuff to do... */
216                                 if( ioctl( fd_vme, WCR0, &regvalue ) )
217                                 msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
218                                 break;
219                 }
220         }
221
222         /*
223          * Allocate unit structure
224          */
225         vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit));
226         bzero((char *)vme, sizeof(struct vmeunit));
227
228
229         /*
230          * Set up the structures
231          */
232         pp = peer->procptr;
233         pp->unitptr = (caddr_t) vme;
234         pp->timestarted = current_time;
235
236         pp->io.clock_recv = vme_receive;
237         pp->io.srcclock = (caddr_t)peer;
238         pp->io.datalen = 0;
239         pp->io.fd = fd_vme;
240
241         /*
242          * All done.  Initialize a few random peer variables, then
243          * return success. Note that root delay and root dispersion are
244          * always zero for this clock.
245          */
246         peer->precision = VMEPRECISION;
247         memcpy(&pp->refid, USNOREFID,4);
248         return (1);
249 }
250
251
252 /*
253  * vme_shutdown - shut down a VME clock
254  */
255 static void
256 vme_shutdown(
257         int unit, 
258         struct peer *peer
259         )
260 {
261         register struct vmeunit *vme;
262         struct refclockproc *pp;
263
264         /*
265          * Tell the I/O module to turn us off.  We're history.
266          */
267         pp = peer->procptr;
268         vme = (struct vmeunit *)pp->unitptr;
269         io_closeclock(&pp->io);
270         pp->unitptr = NULL;
271         free(vme);
272         if (tfp_type == 2) bcStopPci(stfp_handle); 
273 }
274
275
276 /*
277  * vme_receive - receive data from the VME device.
278  *
279  * Note: This interface would be interrupt-driven. We don't use that
280  * now, but include a dummy routine for possible future adventures.
281  */
282 static void
283 vme_receive(
284         struct recvbuf *rbufp
285         )
286 {
287 }
288
289
290 /*
291  * vme_poll - called by the transmit procedure
292  */
293 static void
294 vme_poll(
295         int unit,
296         struct peer *peer
297         )
298 {
299         struct vmedate *tptr; 
300         struct vmeunit *vme;
301         struct refclockproc *pp;
302         time_t tloc;
303         struct tm *tadr;
304         
305         pp = peer->procptr;      
306         vme = (struct vmeunit *)pp->unitptr;        /* Here is the structure */
307
308         tptr = &vme->vmedata; 
309         if ((tptr = get_datumtime(tptr)) == NULL ) {
310                 refclock_report(peer, CEVNT_BADREPLY);
311                 return;
312         }
313
314         get_systime(&pp->lastrec);
315         pp->polls++;
316         vme->lasttime = current_time;
317
318         /*
319          * Get VME time and convert to timestamp format. 
320          * The year must come from the system clock.
321          */
322         
323           time(&tloc);
324           tadr = gmtime(&tloc);
325           tptr->year = (unsigned short)(tadr->tm_year + 1900);
326
327         sprintf(pp->a_lastcode, 
328                 "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
329                 tptr->day, 
330                 tptr->hr, 
331                 tptr->mn,
332                 tptr->sec, 
333                 tptr->frac, 
334                 tptr->status);
335
336         pp->lencode = (u_short) strlen(pp->a_lastcode);
337
338         pp->day =  tptr->day;
339         pp->hour =   tptr->hr;
340         pp->minute =  tptr->mn;
341         pp->second =  tptr->sec;
342         pp->nsec =   tptr->frac;        
343
344 #ifdef DEBUG
345         if (debug)
346             printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
347                    pp->day, pp->hour, pp->minute, pp->second,
348                    pp->nsec, tptr->status);
349 #endif
350         if (tptr->status ) {       /*  Status 0 is locked to ref., 1 is not */
351                 refclock_report(peer, CEVNT_BADREPLY);
352                 return;
353         }
354
355         /*
356          * Now, compute the reference time value. Use the heavy
357          * machinery for the seconds and the millisecond field for the
358          * fraction when present. If an error in conversion to internal
359          * format is found, the program declares bad data and exits.
360          * Note that this code does not yet know how to do the years and
361          * relies on the clock-calendar chip for sanity.
362          */
363         if (!refclock_process(pp)) {
364                 refclock_report(peer, CEVNT_BADTIME);
365                 return;
366         }
367         pp->lastref = pp->lastrec;
368         refclock_receive(peer);
369         record_clock_stats(&peer->srcadr, pp->a_lastcode);
370 }
371
372 struct vmedate *
373 get_datumtime(struct vmedate *time_vme)
374 {
375         char cbuf[7];
376         struct btfp_time vts;
377         uint32_t btm[2];
378         uint8_t dmy;
379         
380         if ( time_vme == (struct vmedate *)NULL) {
381           time_vme = (struct vmedate *)malloc(sizeof(struct vmedate ));
382         }
383
384         switch (tfp_type) {
385                 case 1:                         /* BSD, PCI, 2 32bit time words */
386                         if (ioctl(fd_vme, READTIME, &btm)) {
387                         msyslog(LOG_ERR, "get_bc63x error: %m");
388                                 return(NULL);
389                         }
390                         tvme_fill(time_vme, btm);
391                         break;
392
393                 case 2:                         /* Linux/Windows, PCI, 2 32bit time words */
394                         if (bcReadBinTime(stfp_handle, &btm[1], &btm[0], &dmy) == 0) {
395                         msyslog(LOG_ERR, "get_datumtime error: %m"); 
396                                 return(NULL);
397                         }
398                         tvme_fill(time_vme, btm);
399                         break;
400
401                 default:                        /* legacy bancomm card */
402
403                         if (ioctl(fd_vme, READTIME, &vts)) {
404                         msyslog(LOG_ERR, "get_datumtime error: %m");
405                                 return(NULL);
406                         }
407                         /* Get day */
408                         sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) +
409                                 ((vts.btfp_time[ 1 ] & 0xff00) >> 8));  
410                         time_vme->day = (unsigned short)atoi(cbuf);
411
412                         /* Get hour */
413                         sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff);
414
415                         time_vme->hr = (unsigned short)atoi(cbuf);
416
417                         /* Get minutes */
418                         sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8);
419                         time_vme->mn = (unsigned short)atoi(cbuf);
420
421                         /* Get seconds */
422                         sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff);
423                         time_vme->sec = (unsigned short)atoi(cbuf);
424
425                         /* Get microseconds.  Yes, we ignore the 0.1 microsecond digit so
426                                  we can use the TVTOTSF function  later on...*/
427
428                         sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ],
429                         vts.btfp_time[ 4 ]>>8);
430
431                         time_vme->frac = (u_long) atoi(cbuf);
432
433                         /* Get status bit */
434                         time_vme->status = (vts.btfp_time[0] & 0x0010) >>4;
435
436                         break;
437         }
438
439         if (time_vme->status) 
440                 return ((void *)NULL);
441         else
442             return (time_vme);
443 }
444 /* Assign values to time_vme struct. Mostly for readability */
445 void
446 tvme_fill(struct vmedate *time_vme, uint32_t btm[2])
447 {
448         struct tm maj;
449         uint32_t dmaj, dmin;
450
451         dmaj = btm[1];                  /* syntax sugar */
452         dmin = btm[0];
453
454         gmtime_r(&dmaj, &maj);
455         time_vme->day  = maj.tm_yday+1;
456         time_vme->hr   = maj.tm_hour;
457         time_vme->mn   = maj.tm_min;
458         time_vme->sec  = maj.tm_sec;
459         time_vme->frac = (dmin & 0x000fffff) * 1000; 
460         time_vme->frac += ((dmin & 0x00f00000) >> 20) * 100;
461         time_vme->status = (dmin & 0x01000000) >> 24;
462         return;
463 }
464
465 #else
466 int refclock_bancomm_bs;
467 #endif /* REFCLOCK */