3 /* refclock_bancomm.c - clock driver for the Datum/Bancomm bc635VME
4 * Time and Frequency Processor. It requires the BANCOMM bc635VME/
5 * bc350VXI Time and Frequency Processor Module Driver for SunOS4.x
6 * and SunOS5.x UNIX Systems. It has been tested on a UltraSparc
7 * IIi-cEngine running Solaris 2.6.
9 * Author(s): Ganesh Ramasivan & Gary Cliff, Computing Devices Canada,
14 * Note(s): The refclock type has been defined as 16.
16 * This program has been modelled after the Bancomm driver
17 * originally written by R. Schmidt of Time Service, U.S.
18 * Naval Observatory for a HP-UX machine. Since the original
19 * authors no longer plan to maintain this code, all
20 * references to the HP-UX vme2 driver subsystem bave been
21 * removed. Functions vme_report_event(), vme_receive(),
22 * vme_control() and vme_buginfo() have been deleted because
23 * they are no longer being used.
26 * Modified to add support for Symmetricom bc637PCI-U Time &
27 * Frequency Processor.
28 * 2/21/2007 Ali Ghorashi
29 * Modified to add support for Symmetricom bc637PCI-U Time &
30 * Frequency Processor on Solaris.
31 * Tested on Solaris 10 with a bc635 card.
33 * Card bus type (VME/VXI or PCI) and environment are specified via the
34 * "mode" keyword on the server command in ntp.conf.
35 * server 127.127.16.u prefer mode M
36 * where u is the id (usually 0) of the entry in /dev (/dev/stfp0)
38 * and M is one of the following modes:
39 * 1 : FreeBSD PCI 635/637.
40 * 2 : Linux or Windows PCI 635/637.
41 * 3 : Solaris PCI 635/637
42 * not specified, or other number:
43 * : Assumed to be VME/VXI legacy Bancomm card on Solaris.
44 * Linux and Windows platforms require Symmetricoms' proprietary driver
46 * Solaris requires Symmetricom's driver and its header file (freely distributed) to
47 * be installed and running.
54 #if defined(REFCLOCK) && defined(CLOCK_BANC)
58 #include "ntp_refclock.h"
59 #include "ntp_unixtime.h"
60 #include "ntp_stdlib.h"
66 struct btfp_time
/* Structure for reading 5 time words */
67 /* in one ioctl(2) operation. */
69 unsigned short btfp_time
[5]; /* Time words 0,1,2,3, and 4. (16bit)*/
71 /* SunOS5 ioctl commands definitions.*/
72 #define BTFPIOC ( 'b'<< 8 )
73 #define IOCIO( l, n ) ( BTFPIOC | n )
74 #define IOCIOR( l, n, s ) ( BTFPIOC | n )
75 #define IOCIORN( l, n, s ) ( BTFPIOC | n )
76 #define IOCIOWN( l, n, s ) ( BTFPIOC | n )
78 /***** Simple ioctl commands *****/
79 #define RUNLOCK IOCIOR(b, 19, int ) /* Release Capture Lockout */
80 #define RCR0 IOCIOR(b, 22, int ) /* Read control register zero.*/
81 #define WCR0 IOCIOWN(b, 23, int) /* Write control register zero*/
82 /***** Compound ioctl commands *****/
84 /* Read all 5 time words in one call. */
85 #define READTIME IOCIORN(b, 32, sizeof( struct btfp_time ))
87 #if defined(__FreeBSD__)
89 #define READTIME _IOR('u', 5, struct btfp_time )
92 /* Solaris specific section */
107 int32_t usec
; /* usec 0 - 999999 */
108 int32_t hnsec
; /* hnsec 0 - 9 (hundreds of nsecs) */
112 #define SELTIMEFORMAT 2
113 # define TIME_DECIMAL 0
114 # define TIME_BINARY 1
119 #endif /** __sun___ **/
120 /* end solaris specific section */
122 struct vmedate
{ /* structure returned by get_vmetime.c */
129 unsigned short status
;
132 typedef void *SYMMT_PCI_HANDLE
;
135 * VME interface parameters.
137 #define VMEPRECISION (-21) /* precision assumed (1 us) */
138 #define USNOREFID "BTFP" /* or whatever */
139 #define VMEREFID "BTFP" /* reference id */
140 #define VMEDESCRIPTION "Bancomm bc635 TFP" /* who we are */
141 #define VMEHSREFID 0x7f7f1000 /* 127.127.16.00 refid hi strata */
142 /* clock type 16 is used here */
143 #define GMT 0 /* hour offset from Greenwich */
146 * Imported from ntp_timer module
148 extern u_long current_time
; /* current time(s) */
151 * Imported from ntpd module
153 extern volatile int debug
; /* global debug flag */
156 * VME unit control structure.
157 * Changes made to vmeunit structure. Most members are now available in the
158 * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan
161 struct vmedate vmedata
; /* data returned from vme read */
162 u_long lasttime
; /* last time clock heard from */
166 * Function prototypes
168 static int vme_start (int, struct peer
*);
169 static void vme_shutdown (int, struct peer
*);
170 static void vme_receive (struct recvbuf
*);
171 static void vme_poll (int unit
, struct peer
*);
172 struct vmedate
*get_datumtime(struct vmedate
*);
173 void tvme_fill(struct vmedate
*, uint32_t btm
[2]);
174 void stfp_time2tvme(struct vmedate
*time_vme
, struct stfp_time
*stfp
);
175 inline const char *DEVICE_NAME(int n
);
179 * Define the bc*() functions as weak so we can compile/link without them.
180 * Only clients with the card will have the proprietary vendor device driver
181 * and interface library needed for use on Linux/Windows platforms.
183 extern uint32_t __attribute__ ((weak
)) bcReadBinTime(SYMMT_PCI_HANDLE
, uint32_t *, uint32_t*, uint8_t*);
184 extern SYMMT_PCI_HANDLE
__attribute__ ((weak
)) bcStartPci(void);
185 extern void __attribute__ ((weak
)) bcStopPci(SYMMT_PCI_HANDLE
);
190 struct refclock refclock_bancomm
= {
191 vme_start
, /* start up driver */
192 vme_shutdown
, /* shut down driver */
193 vme_poll
, /* transmit poll message */
194 noentry
, /* not used (old vme_control) */
195 noentry
, /* initialize driver */
196 noentry
, /* not used (old vme_buginfo) */
197 NOFLAGS
/* not used */
200 int fd_vme
; /* file descriptor for ioctls */
202 int tfp_type
; /* mode selector, indicate platform and driver interface */
203 SYMMT_PCI_HANDLE stfp_handle
;
206 * this macro returns the device name based on
207 * the platform we are running on and the device number
210 inline const char *DEVICE_NAME(int n
) {static char s
[20]={0}; snprintf(s
,19,"/dev/stfp%d",n
);return s
;}
212 inline const char* DEVICE_NAME(int n
) {static char s
[20]={0}; snprintf(s
,19,"/dev/btfp%d",n
);return s
;}
216 * vme_start - open the VME device and initialize data for processing
224 register struct vmeunit
*vme
;
225 struct refclockproc
*pp
;
229 tfp_type
= (int)(peer
->ttl
);
235 stfp_handle
= bcStartPci(); /* init the card in lin/win */
245 printf("Opening DATUM DEVICE %s\n",DEVICE_NAME(peer
->refclkunit
));
247 if ( (fd_vme
= open(DEVICE_NAME(peer
->refclkunit
), O_RDWR
)) < 0) {
248 msyslog(LOG_ERR
, "vme_start: failed open of %s: %m", vmedev
);
257 /* Release capture lockout in case it was set before. */
258 if( ioctl( fd_vme
, RUNLOCK
, &dummy
) )
259 msyslog(LOG_ERR
, "vme_start: RUNLOCK failed %m");
261 regvalue
= 0; /* More esoteric stuff to do... */
262 if( ioctl( fd_vme
, WCR0
, ®value
) )
263 msyslog(LOG_ERR
, "vme_start: WCR0 failed %m");
269 * Allocate unit structure
271 vme
= (struct vmeunit
*)emalloc(sizeof(struct vmeunit
));
272 bzero((char *)vme
, sizeof(struct vmeunit
));
276 * Set up the structures
279 pp
->unitptr
= (caddr_t
) vme
;
280 pp
->timestarted
= current_time
;
282 pp
->io
.clock_recv
= vme_receive
;
283 pp
->io
.srcclock
= (caddr_t
)peer
;
288 * All done. Initialize a few random peer variables, then
289 * return success. Note that root delay and root dispersion are
290 * always zero for this clock.
292 peer
->precision
= VMEPRECISION
;
293 memcpy(&pp
->refid
, USNOREFID
,4);
299 * vme_shutdown - shut down a VME clock
307 register struct vmeunit
*vme
;
308 struct refclockproc
*pp
;
311 * Tell the I/O module to turn us off. We're history.
314 vme
= (struct vmeunit
*)pp
->unitptr
;
315 io_closeclock(&pp
->io
);
318 if (tfp_type
== 2) bcStopPci(stfp_handle
);
323 * vme_receive - receive data from the VME device.
325 * Note: This interface would be interrupt-driven. We don't use that
326 * now, but include a dummy routine for possible future adventures.
330 struct recvbuf
*rbufp
337 * vme_poll - called by the transmit procedure
345 struct vmedate
*tptr
;
347 struct refclockproc
*pp
;
352 vme
= (struct vmeunit
*)pp
->unitptr
; /* Here is the structure */
354 tptr
= &vme
->vmedata
;
355 if ((tptr
= get_datumtime(tptr
)) == NULL
) {
356 refclock_report(peer
, CEVNT_BADREPLY
);
360 get_systime(&pp
->lastrec
);
362 vme
->lasttime
= current_time
;
365 * Get VME time and convert to timestamp format.
366 * The year must come from the system clock.
370 tadr
= gmtime(&tloc
);
371 tptr
->year
= (unsigned short)(tadr
->tm_year
+ 1900);
373 sprintf(pp
->a_lastcode
,
374 "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
382 pp
->lencode
= (u_short
) strlen(pp
->a_lastcode
);
386 pp
->minute
= tptr
->mn
;
387 pp
->second
= tptr
->sec
;
388 pp
->nsec
= tptr
->frac
;
392 printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
393 pp
->day
, pp
->hour
, pp
->minute
, pp
->second
,
394 pp
->nsec
, tptr
->status
);
396 if (tptr
->status
) { /* Status 0 is locked to ref., 1 is not */
397 refclock_report(peer
, CEVNT_BADREPLY
);
402 * Now, compute the reference time value. Use the heavy
403 * machinery for the seconds and the millisecond field for the
404 * fraction when present. If an error in conversion to internal
405 * format is found, the program declares bad data and exits.
406 * Note that this code does not yet know how to do the years and
407 * relies on the clock-calendar chip for sanity.
409 if (!refclock_process(pp
)) {
410 refclock_report(peer
, CEVNT_BADTIME
);
413 pp
->lastref
= pp
->lastrec
;
414 refclock_receive(peer
);
415 record_clock_stats(&peer
->srcadr
, pp
->a_lastcode
);
419 get_datumtime(struct vmedate
*time_vme
)
422 struct btfp_time vts
;
425 struct stfp_time stfpm
;
427 if (time_vme
== NULL
)
428 time_vme
= emalloc(sizeof(*time_vme
));
431 case 1: /* BSD, PCI, 2 32bit time words */
432 if (ioctl(fd_vme
, READTIME
, &btm
)) {
433 msyslog(LOG_ERR
, "get_bc63x error: %m");
436 tvme_fill(time_vme
, btm
);
439 case 2: /* Linux/Windows, PCI, 2 32bit time words */
440 if (bcReadBinTime(stfp_handle
, &btm
[1], &btm
[0], &dmy
) == 0) {
441 msyslog(LOG_ERR
, "get_datumtime error: %m");
444 tvme_fill(time_vme
, btm
);
447 case 3: /** solaris **/
448 memset(&stfpm
,0,sizeof(stfpm
));
450 /* we need the time in decimal format */
451 /* Here we rudely assume that we are the only user of the driver.
452 * Other programs will have to set their own time format before reading
455 if(ioctl (fd_vme
, SELTIMEFORMAT
, TIME_DECIMAL
)){
456 msyslog(LOG_ERR
, "Could not set time format\n");
460 if (ioctl(fd_vme
, READTIME
, &stfpm
)) {
461 msyslog(LOG_ERR
, "ioctl error: %m");
464 stfp_time2tvme(time_vme
, &stfpm
);
467 default: /* legacy bancomm card */
469 if (ioctl(fd_vme
, READTIME
, &vts
)) {
470 msyslog(LOG_ERR
, "get_datumtime error: %m");
474 sprintf(cbuf
,"%3.3x", ((vts
.btfp_time
[ 0 ] & 0x000f) <<8) +
475 ((vts
.btfp_time
[ 1 ] & 0xff00) >> 8));
476 time_vme
->day
= (unsigned short)atoi(cbuf
);
479 sprintf(cbuf
,"%2.2x", vts
.btfp_time
[ 1 ] & 0x00ff);
481 time_vme
->hr
= (unsigned short)atoi(cbuf
);
484 sprintf(cbuf
,"%2.2x", (vts
.btfp_time
[ 2 ] & 0xff00) >>8);
485 time_vme
->mn
= (unsigned short)atoi(cbuf
);
488 sprintf(cbuf
,"%2.2x", vts
.btfp_time
[ 2 ] & 0x00ff);
489 time_vme
->sec
= (unsigned short)atoi(cbuf
);
491 /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so
492 we can use the TVTOTSF function later on...*/
494 sprintf(cbuf
,"%4.4x%2.2x", vts
.btfp_time
[ 3 ],
495 vts
.btfp_time
[ 4 ]>>8);
497 time_vme
->frac
= (u_long
) atoi(cbuf
);
500 time_vme
->status
= (vts
.btfp_time
[0] & 0x0010) >>4;
505 if (time_vme
->status
)
506 return ((void *)NULL
);
510 /* Assign values to time_vme struct. Mostly for readability */
512 tvme_fill(struct vmedate
*time_vme
, uint32_t btm
[2])
517 dmaj
= btm
[1]; /* syntax sugar */
520 gmtime_r(&dmaj
, &maj
);
521 time_vme
->day
= maj
.tm_yday
+1;
522 time_vme
->hr
= maj
.tm_hour
;
523 time_vme
->mn
= maj
.tm_min
;
524 time_vme
->sec
= maj
.tm_sec
;
525 time_vme
->frac
= (dmin
& 0x000fffff) * 1000;
526 time_vme
->frac
+= ((dmin
& 0x00f00000) >> 20) * 100;
527 time_vme
->status
= (dmin
& 0x01000000) >> 24;
532 /* Assign values to time_vme struct. Mostly for readability */
534 stfp_time2tvme(struct vmedate
*time_vme
, struct stfp_time
*stfp
)
537 time_vme
->day
= stfp
->tm
.tm_yday
+1;
538 time_vme
->hr
= stfp
->tm
.tm_hour
;
539 time_vme
->mn
= stfp
->tm
.tm_min
;
540 time_vme
->sec
= stfp
->tm
.tm_sec
;
541 time_vme
->frac
= stfp
->usec
*1000;
542 time_vme
->frac
+= stfp
->hnsec
* 100;
543 time_vme
->status
= stfp
->status
;
547 int refclock_bancomm_bs
;
548 #endif /* REFCLOCK */