xref: /freebsd/contrib/ntp/ntpd/refclock_bancomm.c (revision f5f40dd63bc7acbb5312b26ac1ea1103c12352a6)
1c0b746e5SOllivier Robert /* refclock_bancomm.c - clock driver for the  Datum/Bancomm bc635VME
2c0b746e5SOllivier Robert  * Time and Frequency Processor. It requires the BANCOMM bc635VME/
3c0b746e5SOllivier Robert  * bc350VXI Time and Frequency Processor Module Driver for SunOS4.x
4c0b746e5SOllivier Robert  * and SunOS5.x UNIX Systems. It has been tested on a UltraSparc
5c0b746e5SOllivier Robert  * IIi-cEngine running Solaris 2.6.
6c0b746e5SOllivier Robert  *
7c0b746e5SOllivier Robert  * Author(s): 	Ganesh Ramasivan & Gary Cliff, Computing Devices Canada,
8c0b746e5SOllivier Robert  *		Ottawa, Canada
9c0b746e5SOllivier Robert  *
10c0b746e5SOllivier Robert  * Date: 	July 1999
11c0b746e5SOllivier Robert  *
12c0b746e5SOllivier Robert  * Note(s):	The refclock type has been defined as 16.
13c0b746e5SOllivier Robert  *
14c0b746e5SOllivier Robert  *		This program has been modelled after the Bancomm driver
15c0b746e5SOllivier Robert  *		originally written by R. Schmidt of Time Service, U.S.
16c0b746e5SOllivier Robert  *		Naval Observatory for a HP-UX machine. Since the original
17c0b746e5SOllivier Robert  *		authors no longer plan to maintain this code, all
18c0b746e5SOllivier Robert  *		references to the HP-UX vme2 driver subsystem bave been
19c0b746e5SOllivier Robert  *		removed. Functions vme_report_event(), vme_receive(),
20c0b746e5SOllivier Robert  *		vme_control() and vme_buginfo() have been deleted because
21c0b746e5SOllivier Robert  *		they are no longer being used.
22c0b746e5SOllivier Robert  *
23ea906c41SOllivier Robert  *	04/28/2005 Rob Neal
24ea906c41SOllivier Robert  *		Modified to add support for Symmetricom bc637PCI-U Time &
25ea906c41SOllivier Robert  *		Frequency Processor.
262b15cb3dSCy Schubert  *	2/21/2007 Ali Ghorashi
272b15cb3dSCy Schubert  *	        Modified to add support for Symmetricom bc637PCI-U Time &
282b15cb3dSCy Schubert  *		Frequency Processor on Solaris.
292b15cb3dSCy Schubert  *		Tested on Solaris 10 with a bc635 card.
302b15cb3dSCy Schubert  *
31ea906c41SOllivier Robert  *		Card bus type (VME/VXI or PCI) and environment are specified via the
32ea906c41SOllivier Robert  *		"mode" keyword on the server command in ntp.conf.
332b15cb3dSCy Schubert  *		server 127.127.16.u prefer mode M
342b15cb3dSCy Schubert  *		where u is the id (usually 0) of the entry in /dev (/dev/stfp0)
352b15cb3dSCy Schubert  *
362b15cb3dSCy Schubert  *		and M is one of the following modes:
37ea906c41SOllivier Robert  *		1		: FreeBSD PCI 635/637.
38ea906c41SOllivier Robert  *		2		: Linux or Windows PCI 635/637.
392b15cb3dSCy Schubert  *		3		: Solaris PCI 635/637
40ea906c41SOllivier Robert  *		not specified, or other number:
41ea906c41SOllivier Robert  *				: Assumed to be VME/VXI legacy Bancomm card on Solaris.
42ea906c41SOllivier Robert  *		Linux and Windows platforms require Symmetricoms' proprietary driver
43ea906c41SOllivier Robert  *		for the TFP card.
442b15cb3dSCy Schubert  *		Solaris requires Symmetricom's driver and its header file (freely distributed) to
452b15cb3dSCy Schubert  *		be installed and running.
46c0b746e5SOllivier Robert  */
47c0b746e5SOllivier Robert 
48c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H
49c0b746e5SOllivier Robert #include <config.h>
50c0b746e5SOllivier Robert #endif
51c0b746e5SOllivier Robert 
52c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_BANC)
53c0b746e5SOllivier Robert 
54c0b746e5SOllivier Robert #include "ntpd.h"
55c0b746e5SOllivier Robert #include "ntp_io.h"
56c0b746e5SOllivier Robert #include "ntp_refclock.h"
57c0b746e5SOllivier Robert #include "ntp_unixtime.h"
58c0b746e5SOllivier Robert #include "ntp_stdlib.h"
59c0b746e5SOllivier Robert 
60224ba2bdSOllivier Robert #include <stdio.h>
61224ba2bdSOllivier Robert #include <syslog.h>
62224ba2bdSOllivier Robert #include <ctype.h>
63052d159aSCy Schubert #ifdef HAVE_SYS_IOCTL_H
64052d159aSCy Schubert # include <sys/ioctl.h>
65052d159aSCy Schubert #endif
66224ba2bdSOllivier Robert 
67c0b746e5SOllivier Robert struct btfp_time                /* Structure for reading 5 time words   */
68c0b746e5SOllivier Robert                                 /* in one ioctl(2) operation.           */
69c0b746e5SOllivier Robert {
70c0b746e5SOllivier Robert 	unsigned short btfp_time[5];  /* Time words 0,1,2,3, and 4. (16bit)*/
71c0b746e5SOllivier Robert };
72c0b746e5SOllivier Robert /* SunOS5 ioctl commands definitions.*/
73c0b746e5SOllivier Robert #define BTFPIOC            ( 'b'<< 8 )
74c0b746e5SOllivier Robert #define IOCIO( l, n )      ( BTFPIOC | n )
75c0b746e5SOllivier Robert #define IOCIOR( l, n, s )  ( BTFPIOC | n )
76c0b746e5SOllivier Robert #define IOCIORN( l, n, s ) ( BTFPIOC | n )
77c0b746e5SOllivier Robert #define IOCIOWN( l, n, s ) ( BTFPIOC | n )
78c0b746e5SOllivier Robert 
79c0b746e5SOllivier Robert /***** Simple ioctl commands *****/
80c0b746e5SOllivier Robert #define RUNLOCK		IOCIOR(b, 19, int )  /* Release Capture Lockout */
81c0b746e5SOllivier Robert #define RCR0		IOCIOR(b, 22, int )  /* Read control register zero.*/
82c0b746e5SOllivier Robert #define	WCR0		IOCIOWN(b, 23, int)  /* Write control register zero*/
83c0b746e5SOllivier Robert /***** Compound ioctl commands *****/
84c0b746e5SOllivier Robert 
85c0b746e5SOllivier Robert /* Read all 5 time words in one call.   */
86ea906c41SOllivier Robert #if defined(__FreeBSD__)
87ea906c41SOllivier Robert # define READTIME	_IOR('u', 5, struct btfp_time )
88052d159aSCy Schubert #else
89052d159aSCy Schubert # define READTIME	IOCIORN(b, 32, sizeof( struct btfp_time ))
90ea906c41SOllivier Robert #endif
91ea906c41SOllivier Robert 
922b15cb3dSCy Schubert /* Solaris specific section */
932b15cb3dSCy Schubert struct	stfp_tm {
942b15cb3dSCy Schubert 	int32_t tm_sec;
952b15cb3dSCy Schubert 	int32_t tm_min;
962b15cb3dSCy Schubert 	int32_t tm_hour;
972b15cb3dSCy Schubert 	int32_t tm_mday;
982b15cb3dSCy Schubert 	int32_t tm_mon;
992b15cb3dSCy Schubert 	int32_t tm_year;
1002b15cb3dSCy Schubert 	int32_t tm_wday;
1012b15cb3dSCy Schubert 	int32_t tm_yday;
1022b15cb3dSCy Schubert 	int32_t tm_isdst;
1032b15cb3dSCy Schubert };
1042b15cb3dSCy Schubert 
1052b15cb3dSCy Schubert struct stfp_time {
1062b15cb3dSCy Schubert 	struct stfp_tm	tm;
1072b15cb3dSCy Schubert 	int32_t 	usec;			/* usec 0 - 999999 */
1082b15cb3dSCy Schubert 	int32_t 	hnsec;			/* hnsec 0 - 9 (hundreds of nsecs) */
1092b15cb3dSCy Schubert 	int32_t 	status;
1102b15cb3dSCy Schubert };
1112b15cb3dSCy Schubert 
1122b15cb3dSCy Schubert #define SELTIMEFORMAT	2
1132b15cb3dSCy Schubert #	define TIME_DECIMAL 0
1142b15cb3dSCy Schubert #	define TIME_BINARY	1
1152b15cb3dSCy Schubert 
1162b15cb3dSCy Schubert #if defined(__sun__)
1172b15cb3dSCy Schubert #undef	READTIME
1182b15cb3dSCy Schubert #define READTIME		9
1192b15cb3dSCy Schubert #endif /** __sun___ **/
1202b15cb3dSCy Schubert /* end solaris specific section */
121c0b746e5SOllivier Robert 
122c0b746e5SOllivier Robert struct vmedate {			   /* structure returned by get_vmetime.c */
123c0b746e5SOllivier Robert 	unsigned short year;
124c0b746e5SOllivier Robert 	unsigned short day;
125c0b746e5SOllivier Robert 	unsigned short hr;
126c0b746e5SOllivier Robert 	unsigned short mn;
127c0b746e5SOllivier Robert 	unsigned short sec;
128ea906c41SOllivier Robert 	long frac;
129c0b746e5SOllivier Robert 	unsigned short status;
130c0b746e5SOllivier Robert };
131c0b746e5SOllivier Robert 
132ea906c41SOllivier Robert typedef void *SYMMT_PCI_HANDLE;
133c0b746e5SOllivier Robert 
134c0b746e5SOllivier Robert /*
135c0b746e5SOllivier Robert  * VME interface parameters.
136c0b746e5SOllivier Robert  */
137c0b746e5SOllivier Robert #define VMEPRECISION    (-21)   /* precision assumed (1 us) */
138c0b746e5SOllivier Robert #define USNOREFID       "BTFP"  /* or whatever */
139c0b746e5SOllivier Robert #define VMEREFID        "BTFP"  /* reference id */
140c0b746e5SOllivier Robert #define VMEDESCRIPTION  "Bancomm bc635 TFP" /* who we are */
141c0b746e5SOllivier Robert #define VMEHSREFID      0x7f7f1000 /* 127.127.16.00 refid hi strata */
142c0b746e5SOllivier Robert /* clock type 16 is used here  */
143c0b746e5SOllivier Robert #define GMT           	0       /* hour offset from Greenwich */
144c0b746e5SOllivier Robert 
145c0b746e5SOllivier Robert /*
146c0b746e5SOllivier Robert  * Imported from ntp_timer module
147c0b746e5SOllivier Robert  */
148c0b746e5SOllivier Robert extern u_long current_time;     /* current time(s) */
149c0b746e5SOllivier Robert 
150c0b746e5SOllivier Robert /*
151c0b746e5SOllivier Robert  * VME unit control structure.
152c0b746e5SOllivier Robert  * Changes made to vmeunit structure. Most members are now available in the
153c0b746e5SOllivier Robert  * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan
154c0b746e5SOllivier Robert  */
155c0b746e5SOllivier Robert struct vmeunit {
156c0b746e5SOllivier Robert 	struct vmedate vmedata; /* data returned from vme read */
157c0b746e5SOllivier Robert 	u_long lasttime;        /* last time clock heard from */
158c0b746e5SOllivier Robert };
159c0b746e5SOllivier Robert 
160c0b746e5SOllivier Robert /*
161c0b746e5SOllivier Robert  * Function prototypes
162c0b746e5SOllivier Robert  */
163c0b746e5SOllivier Robert static  int     vme_start       (int, struct peer *);
164c0b746e5SOllivier Robert static  void    vme_shutdown    (int, struct peer *);
165c0b746e5SOllivier Robert static  void    vme_receive     (struct recvbuf *);
166c0b746e5SOllivier Robert static  void    vme_poll        (int unit, struct peer *);
167c0b746e5SOllivier Robert struct vmedate *get_datumtime(struct vmedate *);
168ea906c41SOllivier Robert void	tvme_fill(struct vmedate *, uint32_t btm[2]);
1692b15cb3dSCy Schubert void	stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp);
170052d159aSCy Schubert static const char *get_devicename(int n);
1712b15cb3dSCy Schubert 
172052d159aSCy Schubert /* [Bug 3558] and [Bug 1674]  perlinger@ntp.org says:
173052d159aSCy Schubert  *
174052d159aSCy Schubert  * bcReadBinTime() is defined to use two DWORD pointers on Windows and
175052d159aSCy Schubert  * Linux in the BANCOMM SDK.  DWORD is of course Windows-specific
176052d159aSCy Schubert  * (*shudder*), and it is defined as 'unsigned long' under
177052d159aSCy Schubert  * Linux/Unix. (*sigh*)
178052d159aSCy Schubert  *
179052d159aSCy Schubert  * This creates quite some headache.  The size of 'unsigned long' is
180052d159aSCy Schubert  * platform/compiler/memory-model dependent (LP32 vs LP64 vs LLP64),
181052d159aSCy Schubert  * while the card itself always creates 32bit time stamps.  What a
182052d159aSCy Schubert  * bummer.  And DWORD has tendency to contain 64bit on Win64 (which is
183052d159aSCy Schubert  * why we have a DWORD32 defined on Win64) so it can be used as
184052d159aSCy Schubert  * substitute for 'UINT_PTR' in Windows API headers.  I won't even try
185052d159aSCy Schubert  * to comment on that, because anything I have to say will not be civil.
186052d159aSCy Schubert  *
187052d159aSCy Schubert  * We work around this by possibly using a wrapper function that makes
188052d159aSCy Schubert  * the necessary conversions/casts.  It might be a bit tricky to
189052d159aSCy Schubert  * maintain the conditional logic below, but any lingering disease needs
190052d159aSCy Schubert  * constant care to avoid a breakout.
191052d159aSCy Schubert  */
192052d159aSCy Schubert #if defined(__linux__)
193052d159aSCy Schubert   typedef unsigned long bcBinTimeT;
194052d159aSCy Schubert # if SIZEOF_LONG == 4
195052d159aSCy Schubert #   define safeReadBinTime bcReadBinTime
196052d159aSCy Schubert # endif
197052d159aSCy Schubert #elif defined(SYS_WINNT)
198052d159aSCy Schubert   typedef DWORD bcBinTimeT;
199052d159aSCy Schubert # if !defined(_WIN64) || _WIN64 == 0
200052d159aSCy Schubert #   define safeReadBinTime bcReadBinTime
201052d159aSCy Schubert # endif
202052d159aSCy Schubert #else
203052d159aSCy Schubert   typedef uint32_t bcBinTimeT;
204052d159aSCy Schubert # define safeReadBinTime bcReadBinTime
205052d159aSCy Schubert #endif
2062b15cb3dSCy Schubert 
207ea906c41SOllivier Robert /*
208ea906c41SOllivier Robert  * Define the bc*() functions as weak so we can compile/link without them.
209ea906c41SOllivier Robert  * Only clients with the card will have the proprietary vendor device driver
210ea906c41SOllivier Robert  * and interface library needed for use on Linux/Windows platforms.
211ea906c41SOllivier Robert  */
212052d159aSCy Schubert extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, bcBinTimeT*, bcBinTimeT*, uint8_t*);
213ea906c41SOllivier Robert extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void);
214ea906c41SOllivier Robert extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE);
215c0b746e5SOllivier Robert 
216052d159aSCy Schubert /* This is the conversion wrapper for the long/DWORD/uint32_t clash in
217052d159aSCy Schubert  * reading binary times.
218052d159aSCy Schubert  */
219052d159aSCy Schubert #ifndef safeReadBinTime
220052d159aSCy Schubert static uint32_t
221052d159aSCy Schubert safeReadBinTime(
222052d159aSCy Schubert 	SYMMT_PCI_HANDLE hnd,
223052d159aSCy Schubert 	uint32_t        *pt1,
224052d159aSCy Schubert 	uint32_t        *pt2,
225052d159aSCy Schubert 	uint8_t         *p3
226052d159aSCy Schubert 	)
227052d159aSCy Schubert {
228052d159aSCy Schubert 	bcBinTimeT t1, t2;
229052d159aSCy Schubert 	uint32_t   rc;
230052d159aSCy Schubert 
231052d159aSCy Schubert 	rc = bcReadBinTime(hnd, &t1, &t2, p3);
232052d159aSCy Schubert 	if (rc != 0) {
233052d159aSCy Schubert 		*pt1 = (uint32_t)t1;
234052d159aSCy Schubert 		*pt2 = (uint32_t)t2;
235052d159aSCy Schubert 	}
236052d159aSCy Schubert 	return rc;
237052d159aSCy Schubert }
238052d159aSCy Schubert #endif /* !defined(safeReadBinTime) */
239052d159aSCy Schubert 
240c0b746e5SOllivier Robert /*
241c0b746e5SOllivier Robert  * Transfer vector
242c0b746e5SOllivier Robert  */
243c0b746e5SOllivier Robert struct  refclock refclock_bancomm = {
244224ba2bdSOllivier Robert 	vme_start, 		/* start up driver */
245224ba2bdSOllivier Robert 	vme_shutdown,		/* shut down driver */
246224ba2bdSOllivier Robert 	vme_poll,		/* transmit poll message */
247c0b746e5SOllivier Robert 	noentry,		/* not used (old vme_control) */
248224ba2bdSOllivier Robert 	noentry,		/* initialize driver */
249c0b746e5SOllivier Robert 	noentry,		/* not used (old vme_buginfo) */
250224ba2bdSOllivier Robert 	NOFLAGS			/* not used */
251c0b746e5SOllivier Robert };
252c0b746e5SOllivier Robert 
253c0b746e5SOllivier Robert int fd_vme;  /* file descriptor for ioctls */
254c0b746e5SOllivier Robert int regvalue;
255ea906c41SOllivier Robert int tfp_type;	/* mode selector, indicate platform and driver interface */
256ea906c41SOllivier Robert SYMMT_PCI_HANDLE stfp_handle;
257c0b746e5SOllivier Robert 
258052d159aSCy Schubert /* This helper function returns the device name based on the platform we
259052d159aSCy Schubert  * are running on and the device number.
260052d159aSCy Schubert  *
261052d159aSCy Schubert  * Uses a static buffer, so the result is valid only to the next call of
262052d159aSCy Schubert  * this function!
2632b15cb3dSCy Schubert  */
264052d159aSCy Schubert static const char*
265052d159aSCy Schubert get_devicename(int n)
266052d159aSCy Schubert {
267052d159aSCy Schubert 
2682b15cb3dSCy Schubert #   if defined(__sun__)
269052d159aSCy Schubert 	static const char * const template ="/dev/stfp%d";
2702b15cb3dSCy Schubert #   else
271052d159aSCy Schubert 	static const char * const template ="/dev/btfp%d";
272052d159aSCy Schubert #   endif
273052d159aSCy Schubert 	static char namebuf[20];
274052d159aSCy Schubert 
275052d159aSCy Schubert 	snprintf(namebuf, sizeof(namebuf), template, n);
276052d159aSCy Schubert 	namebuf[sizeof(namebuf)-1] = '\0'; /* paranoia rulez! */
277052d159aSCy Schubert 	return namebuf;
278052d159aSCy Schubert }
279c0b746e5SOllivier Robert 
280c0b746e5SOllivier Robert /*
281c0b746e5SOllivier Robert  * vme_start - open the VME device and initialize data for processing
282c0b746e5SOllivier Robert  */
283c0b746e5SOllivier Robert static int
284c0b746e5SOllivier Robert vme_start(
285c0b746e5SOllivier Robert 	int unit,
286c0b746e5SOllivier Robert 	struct peer *peer
287c0b746e5SOllivier Robert 	)
288c0b746e5SOllivier Robert {
289c0b746e5SOllivier Robert 	register struct vmeunit *vme;
290c0b746e5SOllivier Robert 	struct refclockproc *pp;
291c0b746e5SOllivier Robert 	int dummy;
292c0b746e5SOllivier Robert 	char vmedev[20];
293c0b746e5SOllivier Robert 
294ea906c41SOllivier Robert 	tfp_type = (int)(peer->ttl);
295ea906c41SOllivier Robert 	switch (tfp_type) {
296ea906c41SOllivier Robert 		case 1:
2972b15cb3dSCy Schubert 		case 3:
298ea906c41SOllivier Robert 			break;
299ea906c41SOllivier Robert 		case 2:
300ea906c41SOllivier Robert 			stfp_handle = bcStartPci(); 	/* init the card in lin/win */
301ea906c41SOllivier Robert 			break;
302ea906c41SOllivier Robert 		default:
303ea906c41SOllivier Robert 			break;
304ea906c41SOllivier Robert 	}
305c0b746e5SOllivier Robert 	/*
306c0b746e5SOllivier Robert 	 * Open VME device
307c0b746e5SOllivier Robert 	 */
308c0b746e5SOllivier Robert #ifdef DEBUG
309c0b746e5SOllivier Robert 
310052d159aSCy Schubert 	printf("Opening DATUM DEVICE %s\n",get_devicename(peer->refclkunit));
311c0b746e5SOllivier Robert #endif
312052d159aSCy Schubert 	if ( (fd_vme = open(get_devicename(peer->refclkunit), O_RDWR)) < 0) {
313c0b746e5SOllivier Robert 		msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev);
314c0b746e5SOllivier Robert 		return (0);
315c0b746e5SOllivier Robert 	}
316ea906c41SOllivier Robert 	else  {
317ea906c41SOllivier Robert 		switch (tfp_type) {
318ea906c41SOllivier Robert 		  	case 1:	break;
319ea906c41SOllivier Robert 			case 2: break;
3202b15cb3dSCy Schubert 			case 3:break;
321ea906c41SOllivier Robert 			default:
322ea906c41SOllivier Robert 				/* Release capture lockout in case it was set before. */
323c0b746e5SOllivier Robert 				if( ioctl( fd_vme, RUNLOCK, &dummy ) )
324c0b746e5SOllivier Robert 		    		msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m");
325c0b746e5SOllivier Robert 
326c0b746e5SOllivier Robert 				regvalue = 0; /* More esoteric stuff to do... */
327c0b746e5SOllivier Robert 				if( ioctl( fd_vme, WCR0, &regvalue ) )
328c0b746e5SOllivier Robert 		    		msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
329ea906c41SOllivier Robert 				break;
330ea906c41SOllivier Robert 		}
331c0b746e5SOllivier Robert 	}
332c0b746e5SOllivier Robert 
333c0b746e5SOllivier Robert 	/*
334c0b746e5SOllivier Robert 	 * Allocate unit structure
335c0b746e5SOllivier Robert 	 */
3362b15cb3dSCy Schubert 	vme = emalloc_zero(sizeof(struct vmeunit));
337c0b746e5SOllivier Robert 
338c0b746e5SOllivier Robert 
339c0b746e5SOllivier Robert 	/*
340c0b746e5SOllivier Robert 	 * Set up the structures
341c0b746e5SOllivier Robert 	 */
342c0b746e5SOllivier Robert 	pp = peer->procptr;
3432b15cb3dSCy Schubert 	pp->unitptr = vme;
344c0b746e5SOllivier Robert 	pp->timestarted = current_time;
345c0b746e5SOllivier Robert 
346c0b746e5SOllivier Robert 	pp->io.clock_recv = vme_receive;
3472b15cb3dSCy Schubert 	pp->io.srcclock = peer;
348c0b746e5SOllivier Robert 	pp->io.datalen = 0;
349c0b746e5SOllivier Robert 	pp->io.fd = fd_vme;
3502b15cb3dSCy Schubert 	/* shouldn't there be an io_addclock() call? */
351c0b746e5SOllivier Robert 
352c0b746e5SOllivier Robert 	/*
353c0b746e5SOllivier Robert 	 * All done.  Initialize a few random peer variables, then
354c0b746e5SOllivier Robert  	 * return success. Note that root delay and root dispersion are
355c0b746e5SOllivier Robert 	 * always zero for this clock.
356c0b746e5SOllivier Robert 	 */
357c0b746e5SOllivier Robert 	peer->precision = VMEPRECISION;
358224ba2bdSOllivier Robert 	memcpy(&pp->refid, USNOREFID,4);
359c0b746e5SOllivier Robert 	return (1);
360c0b746e5SOllivier Robert }
361c0b746e5SOllivier Robert 
362c0b746e5SOllivier Robert 
363c0b746e5SOllivier Robert /*
364c0b746e5SOllivier Robert  * vme_shutdown - shut down a VME clock
365c0b746e5SOllivier Robert  */
366c0b746e5SOllivier Robert static void
367c0b746e5SOllivier Robert vme_shutdown(
368c0b746e5SOllivier Robert 	int unit,
369c0b746e5SOllivier Robert 	struct peer *peer
370c0b746e5SOllivier Robert 	)
371c0b746e5SOllivier Robert {
372c0b746e5SOllivier Robert 	register struct vmeunit *vme;
373c0b746e5SOllivier Robert 	struct refclockproc *pp;
374c0b746e5SOllivier Robert 
375c0b746e5SOllivier Robert 	/*
376c0b746e5SOllivier Robert 	 * Tell the I/O module to turn us off.  We're history.
377c0b746e5SOllivier Robert 	 */
378224ba2bdSOllivier Robert 	pp = peer->procptr;
3792b15cb3dSCy Schubert 	vme = pp->unitptr;
380c0b746e5SOllivier Robert 	io_closeclock(&pp->io);
381c0b746e5SOllivier Robert 	pp->unitptr = NULL;
3822b15cb3dSCy Schubert 	if (NULL != vme)
383c0b746e5SOllivier Robert 		free(vme);
3842b15cb3dSCy Schubert 	if (tfp_type == 2)
3852b15cb3dSCy Schubert 		bcStopPci(stfp_handle);
386c0b746e5SOllivier Robert }
387c0b746e5SOllivier Robert 
388c0b746e5SOllivier Robert 
389c0b746e5SOllivier Robert /*
390c0b746e5SOllivier Robert  * vme_receive - receive data from the VME device.
391c0b746e5SOllivier Robert  *
392c0b746e5SOllivier Robert  * Note: This interface would be interrupt-driven. We don't use that
393c0b746e5SOllivier Robert  * now, but include a dummy routine for possible future adventures.
394c0b746e5SOllivier Robert  */
395c0b746e5SOllivier Robert static void
396c0b746e5SOllivier Robert vme_receive(
397c0b746e5SOllivier Robert 	struct recvbuf *rbufp
398c0b746e5SOllivier Robert 	)
399c0b746e5SOllivier Robert {
400c0b746e5SOllivier Robert }
401c0b746e5SOllivier Robert 
402c0b746e5SOllivier Robert 
403c0b746e5SOllivier Robert /*
404c0b746e5SOllivier Robert  * vme_poll - called by the transmit procedure
405c0b746e5SOllivier Robert  */
406c0b746e5SOllivier Robert static void
407c0b746e5SOllivier Robert vme_poll(
408c0b746e5SOllivier Robert 	int unit,
409c0b746e5SOllivier Robert 	struct peer *peer
410c0b746e5SOllivier Robert 	)
411c0b746e5SOllivier Robert {
412c0b746e5SOllivier Robert 	struct vmedate *tptr;
413c0b746e5SOllivier Robert 	struct vmeunit *vme;
414c0b746e5SOllivier Robert 	struct refclockproc *pp;
415c0b746e5SOllivier Robert 	time_t tloc;
416c0b746e5SOllivier Robert 	struct tm *tadr;
417c0b746e5SOllivier Robert 
418c0b746e5SOllivier Robert 	pp = peer->procptr;
4192b15cb3dSCy Schubert 	vme = pp->unitptr;        /* Here is the structure */
420c0b746e5SOllivier Robert 
421c0b746e5SOllivier Robert 	tptr = &vme->vmedata;
422c0b746e5SOllivier Robert 	if ((tptr = get_datumtime(tptr)) == NULL ) {
423c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
424c0b746e5SOllivier Robert 		return;
425c0b746e5SOllivier Robert 	}
426c0b746e5SOllivier Robert 
427c0b746e5SOllivier Robert 	get_systime(&pp->lastrec);
428c0b746e5SOllivier Robert 	pp->polls++;
429c0b746e5SOllivier Robert 	vme->lasttime = current_time;
430c0b746e5SOllivier Robert 
431c0b746e5SOllivier Robert 	/*
432c0b746e5SOllivier Robert 	 * Get VME time and convert to timestamp format.
433c0b746e5SOllivier Robert 	 * The year must come from the system clock.
434c0b746e5SOllivier Robert 	 */
435c0b746e5SOllivier Robert 
436c0b746e5SOllivier Robert 	  time(&tloc);
437c0b746e5SOllivier Robert 	  tadr = gmtime(&tloc);
438c0b746e5SOllivier Robert 	  tptr->year = (unsigned short)(tadr->tm_year + 1900);
439c0b746e5SOllivier Robert 
4402b15cb3dSCy Schubert 	snprintf(pp->a_lastcode,
4412b15cb3dSCy Schubert 		 sizeof(pp->a_lastcode),
442c0b746e5SOllivier Robert 		 "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
443c0b746e5SOllivier Robert 		 tptr->day,
444c0b746e5SOllivier Robert 		 tptr->hr,
445c0b746e5SOllivier Robert 		 tptr->mn,
446c0b746e5SOllivier Robert 		 tptr->sec,
447c0b746e5SOllivier Robert 		 tptr->frac,
448c0b746e5SOllivier Robert 		 tptr->status);
449c0b746e5SOllivier Robert 
450c0b746e5SOllivier Robert 	pp->lencode = (u_short) strlen(pp->a_lastcode);
451c0b746e5SOllivier Robert 
452c0b746e5SOllivier Robert 	pp->day =  tptr->day;
453c0b746e5SOllivier Robert 	pp->hour =   tptr->hr;
454c0b746e5SOllivier Robert 	pp->minute =  tptr->mn;
455c0b746e5SOllivier Robert 	pp->second =  tptr->sec;
456ea906c41SOllivier Robert 	pp->nsec =   tptr->frac;
457c0b746e5SOllivier Robert 
458c0b746e5SOllivier Robert #ifdef DEBUG
459c0b746e5SOllivier Robert 	if (debug)
460c0b746e5SOllivier Robert 	    printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
461c0b746e5SOllivier Robert 		   pp->day, pp->hour, pp->minute, pp->second,
462ea906c41SOllivier Robert 		   pp->nsec, tptr->status);
463c0b746e5SOllivier Robert #endif
464c0b746e5SOllivier Robert 	if (tptr->status ) {       /*  Status 0 is locked to ref., 1 is not */
465c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
466c0b746e5SOllivier Robert 		return;
467c0b746e5SOllivier Robert 	}
468c0b746e5SOllivier Robert 
469c0b746e5SOllivier Robert 	/*
470c0b746e5SOllivier Robert 	 * Now, compute the reference time value. Use the heavy
471c0b746e5SOllivier Robert 	 * machinery for the seconds and the millisecond field for the
472c0b746e5SOllivier Robert 	 * fraction when present. If an error in conversion to internal
473c0b746e5SOllivier Robert 	 * format is found, the program declares bad data and exits.
474c0b746e5SOllivier Robert 	 * Note that this code does not yet know how to do the years and
475c0b746e5SOllivier Robert 	 * relies on the clock-calendar chip for sanity.
476c0b746e5SOllivier Robert 	 */
477c0b746e5SOllivier Robert 	if (!refclock_process(pp)) {
478c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADTIME);
479c0b746e5SOllivier Robert 		return;
480c0b746e5SOllivier Robert 	}
4819c2daa00SOllivier Robert 	pp->lastref = pp->lastrec;
482c0b746e5SOllivier Robert 	refclock_receive(peer);
4839c2daa00SOllivier Robert 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
484c0b746e5SOllivier Robert }
485c0b746e5SOllivier Robert 
486c0b746e5SOllivier Robert struct vmedate *
487c0b746e5SOllivier Robert get_datumtime(struct vmedate *time_vme)
488c0b746e5SOllivier Robert {
489c0b746e5SOllivier Robert 	char cbuf[7];
490c0b746e5SOllivier Robert 	struct btfp_time vts;
491ea906c41SOllivier Robert 	uint32_t btm[2];
492ea906c41SOllivier Robert 	uint8_t dmy;
4932b15cb3dSCy Schubert 	struct stfp_time stfpm;
494c0b746e5SOllivier Robert 
4952b15cb3dSCy Schubert 	if (time_vme == NULL)
4962b15cb3dSCy Schubert   		time_vme = emalloc(sizeof(*time_vme));
497c0b746e5SOllivier Robert 
498ea906c41SOllivier Robert 	switch (tfp_type) {
499ea906c41SOllivier Robert 		case 1:				/* BSD, PCI, 2 32bit time words */
500ea906c41SOllivier Robert 			if (ioctl(fd_vme, READTIME, &btm)) {
501ea906c41SOllivier Robert 	    		msyslog(LOG_ERR, "get_bc63x error: %m");
502ea906c41SOllivier Robert 				return(NULL);
503ea906c41SOllivier Robert 			}
504ea906c41SOllivier Robert 			tvme_fill(time_vme, btm);
505ea906c41SOllivier Robert 			break;
506ea906c41SOllivier Robert 
507ea906c41SOllivier Robert 		case 2:				/* Linux/Windows, PCI, 2 32bit time words */
508052d159aSCy Schubert 			if (safeReadBinTime(stfp_handle, &btm[1], &btm[0], &dmy) == 0) {
509c0b746e5SOllivier Robert 	    		msyslog(LOG_ERR, "get_datumtime error: %m");
510ea906c41SOllivier Robert 				return(NULL);
511ea906c41SOllivier Robert 			}
512ea906c41SOllivier Robert 			tvme_fill(time_vme, btm);
513ea906c41SOllivier Robert 			break;
514c0b746e5SOllivier Robert 
5152b15cb3dSCy Schubert 		case 3: /** solaris **/
5162b15cb3dSCy Schubert 			memset(&stfpm,0,sizeof(stfpm));
5172b15cb3dSCy Schubert 
5182b15cb3dSCy Schubert 			/* we need the time in decimal format */
5192b15cb3dSCy Schubert 			/* Here we rudely assume that we are the only user of the driver.
5202b15cb3dSCy Schubert 			 * Other programs will have to set their own time format before reading
5212b15cb3dSCy Schubert 			 * the time.
5222b15cb3dSCy Schubert 			 */
5232b15cb3dSCy Schubert 			if(ioctl (fd_vme, SELTIMEFORMAT, TIME_DECIMAL)){
5242b15cb3dSCy Schubert 					msyslog(LOG_ERR, "Could not set time format");
5252b15cb3dSCy Schubert 					return (NULL);
5262b15cb3dSCy Schubert 			}
5272b15cb3dSCy Schubert 			/* read the time */
5282b15cb3dSCy Schubert 			if (ioctl(fd_vme, READTIME, &stfpm)) {
5292b15cb3dSCy Schubert 				msyslog(LOG_ERR, "ioctl error: %m");
5302b15cb3dSCy Schubert 				return(NULL);
5312b15cb3dSCy Schubert 			}
5322b15cb3dSCy Schubert 			stfp_time2tvme(time_vme,  &stfpm);
5332b15cb3dSCy Schubert 			break;
5342b15cb3dSCy Schubert 
535ea906c41SOllivier Robert 		default:			/* legacy bancomm card */
536c0b746e5SOllivier Robert 
537ea906c41SOllivier Robert 			if (ioctl(fd_vme, READTIME, &vts)) {
5382b15cb3dSCy Schubert 				msyslog(LOG_ERR,
5392b15cb3dSCy Schubert 					"get_datumtime error: %m");
540ea906c41SOllivier Robert 				return(NULL);
541ea906c41SOllivier Robert 			}
542c0b746e5SOllivier Robert 			/* Get day */
5432b15cb3dSCy Schubert 			snprintf(cbuf, sizeof(cbuf), "%3.3x",
5442b15cb3dSCy Schubert 				 ((vts.btfp_time[ 0 ] & 0x000f) << 8) +
545c0b746e5SOllivier Robert 				  ((vts.btfp_time[ 1 ] & 0xff00) >> 8));
546c0b746e5SOllivier Robert 			time_vme->day = (unsigned short)atoi(cbuf);
547c0b746e5SOllivier Robert 
548c0b746e5SOllivier Robert 			/* Get hour */
5492b15cb3dSCy Schubert 			snprintf(cbuf, sizeof(cbuf), "%2.2x",
5502b15cb3dSCy Schubert 				 vts.btfp_time[ 1 ] & 0x00ff);
551c0b746e5SOllivier Robert 			time_vme->hr = (unsigned short)atoi(cbuf);
552c0b746e5SOllivier Robert 
553c0b746e5SOllivier Robert 			/* Get minutes */
5542b15cb3dSCy Schubert 			snprintf(cbuf, sizeof(cbuf), "%2.2x",
5552b15cb3dSCy Schubert 				 (vts.btfp_time[ 2 ] & 0xff00) >> 8);
556c0b746e5SOllivier Robert 			time_vme->mn = (unsigned short)atoi(cbuf);
557c0b746e5SOllivier Robert 
558c0b746e5SOllivier Robert 			/* Get seconds */
5592b15cb3dSCy Schubert 			snprintf(cbuf, sizeof(cbuf), "%2.2x",
5602b15cb3dSCy Schubert 				 vts.btfp_time[ 2 ] & 0x00ff);
561c0b746e5SOllivier Robert 			time_vme->sec = (unsigned short)atoi(cbuf);
562c0b746e5SOllivier Robert 
563ea906c41SOllivier Robert 			/* Get microseconds.  Yes, we ignore the 0.1 microsecond digit so
564ea906c41SOllivier Robert 				 we can use the TVTOTSF function  later on...*/
565c0b746e5SOllivier Robert 
5662b15cb3dSCy Schubert 			snprintf(cbuf, sizeof(cbuf), "%4.4x%2.2x",
5672b15cb3dSCy Schubert 				 vts.btfp_time[ 3 ],
568c0b746e5SOllivier Robert 				 vts.btfp_time[ 4 ] >> 8);
569c0b746e5SOllivier Robert 			time_vme->frac = (u_long) atoi(cbuf);
570c0b746e5SOllivier Robert 
571c0b746e5SOllivier Robert 			/* Get status bit */
572ea906c41SOllivier Robert 			time_vme->status = (vts.btfp_time[0] & 0x0010) >> 4;
573ea906c41SOllivier Robert 
574ea906c41SOllivier Robert 			break;
575c0b746e5SOllivier Robert 	}
576ea906c41SOllivier Robert 
577ea906c41SOllivier Robert 	if (time_vme->status)
578ea906c41SOllivier Robert 		return ((void *)NULL);
579c0b746e5SOllivier Robert 	else
580c0b746e5SOllivier Robert 	    return (time_vme);
581c0b746e5SOllivier Robert }
582ea906c41SOllivier Robert /* Assign values to time_vme struct. Mostly for readability */
583ea906c41SOllivier Robert void
584ea906c41SOllivier Robert tvme_fill(struct vmedate *time_vme, uint32_t btm[2])
585ea906c41SOllivier Robert {
586ea906c41SOllivier Robert 	struct tm maj;
587052d159aSCy Schubert 	time_t   dmaj;
588052d159aSCy Schubert 	uint32_t dmin;
589ea906c41SOllivier Robert 
590052d159aSCy Schubert 	dmaj = btm[1];			/* syntax sugar & expansion */
591052d159aSCy Schubert 	dmin = btm[0];			/* just syntax sugar */
592ea906c41SOllivier Robert 
593ea906c41SOllivier Robert 	gmtime_r(&dmaj, &maj);
594ea906c41SOllivier Robert 	time_vme->day  = maj.tm_yday+1;
595ea906c41SOllivier Robert 	time_vme->hr   = maj.tm_hour;
596ea906c41SOllivier Robert 	time_vme->mn   = maj.tm_min;
597ea906c41SOllivier Robert 	time_vme->sec  = maj.tm_sec;
598ea906c41SOllivier Robert 	time_vme->frac = (dmin & 0x000fffff) * 1000;
599ea906c41SOllivier Robert 	time_vme->frac += ((dmin & 0x00f00000) >> 20) * 100;
600ea906c41SOllivier Robert 	time_vme->status = (dmin & 0x01000000) >> 24;
601ea906c41SOllivier Robert 	return;
602ea906c41SOllivier Robert }
603c0b746e5SOllivier Robert 
6042b15cb3dSCy Schubert 
6052b15cb3dSCy Schubert /* Assign values to time_vme struct. Mostly for readability */
6062b15cb3dSCy Schubert void
6072b15cb3dSCy Schubert stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp)
6082b15cb3dSCy Schubert {
6092b15cb3dSCy Schubert 
6102b15cb3dSCy Schubert 	time_vme->day  = stfp->tm.tm_yday+1;
6112b15cb3dSCy Schubert 	time_vme->hr   = stfp->tm.tm_hour;
6122b15cb3dSCy Schubert 	time_vme->mn   = stfp->tm.tm_min;
6132b15cb3dSCy Schubert 	time_vme->sec  = stfp->tm.tm_sec;
6142b15cb3dSCy Schubert 	time_vme->frac = stfp->usec*1000;
6152b15cb3dSCy Schubert 	time_vme->frac += stfp->hnsec * 100;
6162b15cb3dSCy Schubert 	time_vme->status = stfp->status;
6172b15cb3dSCy Schubert 	return;
6182b15cb3dSCy Schubert }
619c0b746e5SOllivier Robert #else
620*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT
621c0b746e5SOllivier Robert #endif /* REFCLOCK */
622