Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / ntp / dist / ntpd / refclock_bancomm.c
blob16e1813bcb6bd886eb81833c0d6aabfeca472f35
1 /* $NetBSD$ */
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.
8 *
9 * Author(s): Ganesh Ramasivan & Gary Cliff, Computing Devices Canada,
10 * Ottawa, Canada
12 * Date: July 1999
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.
25 * 04/28/2005 Rob Neal
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
45 * for the TFP card.
46 * Solaris requires Symmetricom's driver and its header file (freely distributed) to
47 * be installed and running.
50 #ifdef HAVE_CONFIG_H
51 #include <config.h>
52 #endif
54 #if defined(REFCLOCK) && defined(CLOCK_BANC)
56 #include "ntpd.h"
57 #include "ntp_io.h"
58 #include "ntp_refclock.h"
59 #include "ntp_unixtime.h"
60 #include "ntp_stdlib.h"
62 #include <stdio.h>
63 #include <syslog.h>
64 #include <ctype.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__)
88 #undef READTIME
89 #define READTIME _IOR('u', 5, struct btfp_time )
90 #endif
92 /* Solaris specific section */
93 struct stfp_tm {
94 int32_t tm_sec;
95 int32_t tm_min;
96 int32_t tm_hour;
97 int32_t tm_mday;
98 int32_t tm_mon;
99 int32_t tm_year;
100 int32_t tm_wday;
101 int32_t tm_yday;
102 int32_t tm_isdst;
105 struct stfp_time {
106 struct stfp_tm tm;
107 int32_t usec; /* usec 0 - 999999 */
108 int32_t hnsec; /* hnsec 0 - 9 (hundreds of nsecs) */
109 int32_t status;
112 #define SELTIMEFORMAT 2
113 # define TIME_DECIMAL 0
114 # define TIME_BINARY 1
116 #if defined(__sun__)
117 #undef READTIME
118 #define READTIME 9
119 #endif /** __sun___ **/
120 /* end solaris specific section */
122 struct vmedate { /* structure returned by get_vmetime.c */
123 unsigned short year;
124 unsigned short day;
125 unsigned short hr;
126 unsigned short mn;
127 unsigned short sec;
128 long frac;
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
160 struct vmeunit {
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);
188 * Transfer vector
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 */
201 int regvalue;
202 int tfp_type; /* mode selector, indicate platform and driver interface */
203 SYMMT_PCI_HANDLE stfp_handle;
205 /**
206 * this macro returns the device name based on
207 * the platform we are running on and the device number
209 #if defined(__sun__)
210 inline const char *DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/stfp%d",n);return s;}
211 #else
212 inline const char* DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/btfp%d",n);return s;}
213 #endif /**__sun__**/
216 * vme_start - open the VME device and initialize data for processing
218 static int
219 vme_start(
220 int unit,
221 struct peer *peer
224 register struct vmeunit *vme;
225 struct refclockproc *pp;
226 int dummy;
227 char vmedev[20];
229 tfp_type = (int)(peer->ttl);
230 switch (tfp_type) {
231 case 1:
232 case 3:
233 break;
234 case 2:
235 stfp_handle = bcStartPci(); /* init the card in lin/win */
236 break;
237 default:
238 break;
241 * Open VME device
243 #ifdef DEBUG
245 printf("Opening DATUM DEVICE %s\n",DEVICE_NAME(peer->refclkunit));
246 #endif
247 if ( (fd_vme = open(DEVICE_NAME(peer->refclkunit), O_RDWR)) < 0) {
248 msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev);
249 return (0);
251 else {
252 switch (tfp_type) {
253 case 1: break;
254 case 2: break;
255 case 3:break;
256 default:
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, &regvalue ) )
263 msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
264 break;
269 * Allocate unit structure
271 vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit));
272 bzero((char *)vme, sizeof(struct vmeunit));
276 * Set up the structures
278 pp = peer->procptr;
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;
284 pp->io.datalen = 0;
285 pp->io.fd = fd_vme;
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);
294 return (1);
299 * vme_shutdown - shut down a VME clock
301 static void
302 vme_shutdown(
303 int unit,
304 struct peer *peer
307 register struct vmeunit *vme;
308 struct refclockproc *pp;
311 * Tell the I/O module to turn us off. We're history.
313 pp = peer->procptr;
314 vme = (struct vmeunit *)pp->unitptr;
315 io_closeclock(&pp->io);
316 pp->unitptr = NULL;
317 free(vme);
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.
328 static void
329 vme_receive(
330 struct recvbuf *rbufp
337 * vme_poll - called by the transmit procedure
339 static void
340 vme_poll(
341 int unit,
342 struct peer *peer
345 struct vmedate *tptr;
346 struct vmeunit *vme;
347 struct refclockproc *pp;
348 time_t tloc;
349 struct tm *tadr;
351 pp = peer->procptr;
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);
357 return;
360 get_systime(&pp->lastrec);
361 pp->polls++;
362 vme->lasttime = current_time;
365 * Get VME time and convert to timestamp format.
366 * The year must come from the system clock.
369 time(&tloc);
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",
375 tptr->day,
376 tptr->hr,
377 tptr->mn,
378 tptr->sec,
379 tptr->frac,
380 tptr->status);
382 pp->lencode = (u_short) strlen(pp->a_lastcode);
384 pp->day = tptr->day;
385 pp->hour = tptr->hr;
386 pp->minute = tptr->mn;
387 pp->second = tptr->sec;
388 pp->nsec = tptr->frac;
390 #ifdef DEBUG
391 if (debug)
392 printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
393 pp->day, pp->hour, pp->minute, pp->second,
394 pp->nsec, tptr->status);
395 #endif
396 if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */
397 refclock_report(peer, CEVNT_BADREPLY);
398 return;
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);
411 return;
413 pp->lastref = pp->lastrec;
414 refclock_receive(peer);
415 record_clock_stats(&peer->srcadr, pp->a_lastcode);
418 struct vmedate *
419 get_datumtime(struct vmedate *time_vme)
421 char cbuf[7];
422 struct btfp_time vts;
423 uint32_t btm[2];
424 uint8_t dmy;
425 struct stfp_time stfpm;
427 if (time_vme == NULL)
428 time_vme = emalloc(sizeof(*time_vme));
430 switch (tfp_type) {
431 case 1: /* BSD, PCI, 2 32bit time words */
432 if (ioctl(fd_vme, READTIME, &btm)) {
433 msyslog(LOG_ERR, "get_bc63x error: %m");
434 return(NULL);
436 tvme_fill(time_vme, btm);
437 break;
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");
442 return(NULL);
444 tvme_fill(time_vme, btm);
445 break;
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
453 * the time.
455 if(ioctl (fd_vme, SELTIMEFORMAT, TIME_DECIMAL)){
456 msyslog(LOG_ERR, "Could not set time format\n");
457 return (NULL);
459 /* read the time */
460 if (ioctl(fd_vme, READTIME, &stfpm)) {
461 msyslog(LOG_ERR, "ioctl error: %m");
462 return(NULL);
464 stfp_time2tvme(time_vme, &stfpm);
465 break;
467 default: /* legacy bancomm card */
469 if (ioctl(fd_vme, READTIME, &vts)) {
470 msyslog(LOG_ERR, "get_datumtime error: %m");
471 return(NULL);
473 /* Get day */
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);
478 /* Get hour */
479 sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff);
481 time_vme->hr = (unsigned short)atoi(cbuf);
483 /* Get minutes */
484 sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8);
485 time_vme->mn = (unsigned short)atoi(cbuf);
487 /* Get seconds */
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);
499 /* Get status bit */
500 time_vme->status = (vts.btfp_time[0] & 0x0010) >>4;
502 break;
505 if (time_vme->status)
506 return ((void *)NULL);
507 else
508 return (time_vme);
510 /* Assign values to time_vme struct. Mostly for readability */
511 void
512 tvme_fill(struct vmedate *time_vme, uint32_t btm[2])
514 struct tm maj;
515 uint32_t dmaj, dmin;
517 dmaj = btm[1]; /* syntax sugar */
518 dmin = btm[0];
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;
528 return;
532 /* Assign values to time_vme struct. Mostly for readability */
533 void
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;
544 return;
546 #else
547 int refclock_bancomm_bs;
548 #endif /* REFCLOCK */