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