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 * VME unit control structure.
150 * Changes made to vmeunit structure. Most members are now available in the
151 * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan
152 */
153 struct vmeunit {
154 struct vmedate vmedata; /* data returned from vme read */
155 u_long lasttime; /* last time clock heard from */
156 };
157
158 /*
159 * Function prototypes
160 */
161 static int vme_start (int, struct peer *);
162 static void vme_shutdown (int, struct peer *);
163 static void vme_receive (struct recvbuf *);
164 static void vme_poll (int unit, struct peer *);
165 struct vmedate *get_datumtime(struct vmedate *);
166 void tvme_fill(struct vmedate *, uint32_t btm[2]);
167 void stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp);
168 inline const char *DEVICE_NAME(int n);
169
170
171 /*
172 * Define the bc*() functions as weak so we can compile/link without them.
173 * Only clients with the card will have the proprietary vendor device driver
174 * and interface library needed for use on Linux/Windows platforms.
175 */
176 extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, uint32_t *, uint32_t*, uint8_t*);
177 extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void);
178 extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE);
179
180 /*
181 * Transfer vector
182 */
183 struct refclock refclock_bancomm = {
184 vme_start, /* start up driver */
185 vme_shutdown, /* shut down driver */
186 vme_poll, /* transmit poll message */
187 noentry, /* not used (old vme_control) */
188 noentry, /* initialize driver */
189 noentry, /* not used (old vme_buginfo) */
190 NOFLAGS /* not used */
191 };
192
193 int fd_vme; /* file descriptor for ioctls */
194 int regvalue;
195 int tfp_type; /* mode selector, indicate platform and driver interface */
196 SYMMT_PCI_HANDLE stfp_handle;
197
198 /**
199 * this macro returns the device name based on
200 * the platform we are running on and the device number
201 */
202 #if defined(__sun__)
DEVICE_NAME(int n)203 inline const char *DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/stfp%d",n);return s;}
204 #else
DEVICE_NAME(int n)205 inline const char* DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/btfp%d",n);return s;}
206 #endif /**__sun__**/
207
208 /*
209 * vme_start - open the VME device and initialize data for processing
210 */
211 static int
vme_start(int unit,struct peer * peer)212 vme_start(
213 int unit,
214 struct peer *peer
215 )
216 {
217 register struct vmeunit *vme;
218 struct refclockproc *pp;
219 int dummy;
220 char vmedev[20];
221
222 tfp_type = (int)(peer->ttl);
223 switch (tfp_type) {
224 case 1:
225 case 3:
226 break;
227 case 2:
228 stfp_handle = bcStartPci(); /* init the card in lin/win */
229 break;
230 default:
231 break;
232 }
233 /*
234 * Open VME device
235 */
236 #ifdef DEBUG
237
238 printf("Opening DATUM DEVICE %s\n",DEVICE_NAME(peer->refclkunit));
239 #endif
240 if ( (fd_vme = open(DEVICE_NAME(peer->refclkunit), O_RDWR)) < 0) {
241 msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev);
242 return (0);
243 }
244 else {
245 switch (tfp_type) {
246 case 1: break;
247 case 2: break;
248 case 3:break;
249 default:
250 /* Release capture lockout in case it was set before. */
251 if( ioctl( fd_vme, RUNLOCK, &dummy ) )
252 msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m");
253
254 regvalue = 0; /* More esoteric stuff to do... */
255 if( ioctl( fd_vme, WCR0, ®value ) )
256 msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
257 break;
258 }
259 }
260
261 /*
262 * Allocate unit structure
263 */
264 vme = emalloc_zero(sizeof(struct vmeunit));
265
266
267 /*
268 * Set up the structures
269 */
270 pp = peer->procptr;
271 pp->unitptr = vme;
272 pp->timestarted = current_time;
273
274 pp->io.clock_recv = vme_receive;
275 pp->io.srcclock = peer;
276 pp->io.datalen = 0;
277 pp->io.fd = fd_vme;
278 /* shouldn't there be an io_addclock() call? */
279
280 /*
281 * All done. Initialize a few random peer variables, then
282 * return success. Note that root delay and root dispersion are
283 * always zero for this clock.
284 */
285 peer->precision = VMEPRECISION;
286 memcpy(&pp->refid, USNOREFID,4);
287 return (1);
288 }
289
290
291 /*
292 * vme_shutdown - shut down a VME clock
293 */
294 static void
vme_shutdown(int unit,struct peer * peer)295 vme_shutdown(
296 int unit,
297 struct peer *peer
298 )
299 {
300 register struct vmeunit *vme;
301 struct refclockproc *pp;
302
303 /*
304 * Tell the I/O module to turn us off. We're history.
305 */
306 pp = peer->procptr;
307 vme = pp->unitptr;
308 io_closeclock(&pp->io);
309 pp->unitptr = NULL;
310 if (NULL != vme)
311 free(vme);
312 if (tfp_type == 2)
313 bcStopPci(stfp_handle);
314 }
315
316
317 /*
318 * vme_receive - receive data from the VME device.
319 *
320 * Note: This interface would be interrupt-driven. We don't use that
321 * now, but include a dummy routine for possible future adventures.
322 */
323 static void
vme_receive(struct recvbuf * rbufp)324 vme_receive(
325 struct recvbuf *rbufp
326 )
327 {
328 }
329
330
331 /*
332 * vme_poll - called by the transmit procedure
333 */
334 static void
vme_poll(int unit,struct peer * peer)335 vme_poll(
336 int unit,
337 struct peer *peer
338 )
339 {
340 struct vmedate *tptr;
341 struct vmeunit *vme;
342 struct refclockproc *pp;
343 time_t tloc;
344 struct tm *tadr;
345
346 pp = peer->procptr;
347 vme = pp->unitptr; /* Here is the structure */
348
349 tptr = &vme->vmedata;
350 if ((tptr = get_datumtime(tptr)) == NULL ) {
351 refclock_report(peer, CEVNT_BADREPLY);
352 return;
353 }
354
355 get_systime(&pp->lastrec);
356 pp->polls++;
357 vme->lasttime = current_time;
358
359 /*
360 * Get VME time and convert to timestamp format.
361 * The year must come from the system clock.
362 */
363
364 time(&tloc);
365 tadr = gmtime(&tloc);
366 tptr->year = (unsigned short)(tadr->tm_year + 1900);
367
368 snprintf(pp->a_lastcode,
369 sizeof(pp->a_lastcode),
370 "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
371 tptr->day,
372 tptr->hr,
373 tptr->mn,
374 tptr->sec,
375 tptr->frac,
376 tptr->status);
377
378 pp->lencode = (u_short) strlen(pp->a_lastcode);
379
380 pp->day = tptr->day;
381 pp->hour = tptr->hr;
382 pp->minute = tptr->mn;
383 pp->second = tptr->sec;
384 pp->nsec = tptr->frac;
385
386 #ifdef DEBUG
387 if (debug)
388 printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
389 pp->day, pp->hour, pp->minute, pp->second,
390 pp->nsec, tptr->status);
391 #endif
392 if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */
393 refclock_report(peer, CEVNT_BADREPLY);
394 return;
395 }
396
397 /*
398 * Now, compute the reference time value. Use the heavy
399 * machinery for the seconds and the millisecond field for the
400 * fraction when present. If an error in conversion to internal
401 * format is found, the program declares bad data and exits.
402 * Note that this code does not yet know how to do the years and
403 * relies on the clock-calendar chip for sanity.
404 */
405 if (!refclock_process(pp)) {
406 refclock_report(peer, CEVNT_BADTIME);
407 return;
408 }
409 pp->lastref = pp->lastrec;
410 refclock_receive(peer);
411 record_clock_stats(&peer->srcadr, pp->a_lastcode);
412 }
413
414 struct vmedate *
get_datumtime(struct vmedate * time_vme)415 get_datumtime(struct vmedate *time_vme)
416 {
417 char cbuf[7];
418 struct btfp_time vts;
419 uint32_t btm[2];
420 uint8_t dmy;
421 struct stfp_time stfpm;
422
423 if (time_vme == NULL)
424 time_vme = emalloc(sizeof(*time_vme));
425
426 switch (tfp_type) {
427 case 1: /* BSD, PCI, 2 32bit time words */
428 if (ioctl(fd_vme, READTIME, &btm)) {
429 msyslog(LOG_ERR, "get_bc63x error: %m");
430 return(NULL);
431 }
432 tvme_fill(time_vme, btm);
433 break;
434
435 case 2: /* Linux/Windows, PCI, 2 32bit time words */
436 if (bcReadBinTime(stfp_handle, &btm[1], &btm[0], &dmy) == 0) {
437 msyslog(LOG_ERR, "get_datumtime error: %m");
438 return(NULL);
439 }
440 tvme_fill(time_vme, btm);
441 break;
442
443 case 3: /** solaris **/
444 memset(&stfpm,0,sizeof(stfpm));
445
446 /* we need the time in decimal format */
447 /* Here we rudely assume that we are the only user of the driver.
448 * Other programs will have to set their own time format before reading
449 * the time.
450 */
451 if(ioctl (fd_vme, SELTIMEFORMAT, TIME_DECIMAL)){
452 msyslog(LOG_ERR, "Could not set time format");
453 return (NULL);
454 }
455 /* read the time */
456 if (ioctl(fd_vme, READTIME, &stfpm)) {
457 msyslog(LOG_ERR, "ioctl error: %m");
458 return(NULL);
459 }
460 stfp_time2tvme(time_vme, &stfpm);
461 break;
462
463 default: /* legacy bancomm card */
464
465 if (ioctl(fd_vme, READTIME, &vts)) {
466 msyslog(LOG_ERR,
467 "get_datumtime error: %m");
468 return(NULL);
469 }
470 /* Get day */
471 snprintf(cbuf, sizeof(cbuf), "%3.3x",
472 ((vts.btfp_time[ 0 ] & 0x000f) << 8) +
473 ((vts.btfp_time[ 1 ] & 0xff00) >> 8));
474 time_vme->day = (unsigned short)atoi(cbuf);
475
476 /* Get hour */
477 snprintf(cbuf, sizeof(cbuf), "%2.2x",
478 vts.btfp_time[ 1 ] & 0x00ff);
479 time_vme->hr = (unsigned short)atoi(cbuf);
480
481 /* Get minutes */
482 snprintf(cbuf, sizeof(cbuf), "%2.2x",
483 (vts.btfp_time[ 2 ] & 0xff00) >> 8);
484 time_vme->mn = (unsigned short)atoi(cbuf);
485
486 /* Get seconds */
487 snprintf(cbuf, sizeof(cbuf), "%2.2x",
488 vts.btfp_time[ 2 ] & 0x00ff);
489 time_vme->sec = (unsigned short)atoi(cbuf);
490
491 /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so
492 we can use the TVTOTSF function later on...*/
493
494 snprintf(cbuf, sizeof(cbuf), "%4.4x%2.2x",
495 vts.btfp_time[ 3 ],
496 vts.btfp_time[ 4 ] >> 8);
497 time_vme->frac = (u_long) atoi(cbuf);
498
499 /* Get status bit */
500 time_vme->status = (vts.btfp_time[0] & 0x0010) >> 4;
501
502 break;
503 }
504
505 if (time_vme->status)
506 return ((void *)NULL);
507 else
508 return (time_vme);
509 }
510 /* Assign values to time_vme struct. Mostly for readability */
511 void
tvme_fill(struct vmedate * time_vme,uint32_t btm[2])512 tvme_fill(struct vmedate *time_vme, uint32_t btm[2])
513 {
514 struct tm maj;
515 uint32_t dmaj, dmin;
516
517 dmaj = btm[1]; /* syntax sugar */
518 dmin = btm[0];
519
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;
529 }
530
531
532 /* Assign values to time_vme struct. Mostly for readability */
533 void
stfp_time2tvme(struct vmedate * time_vme,struct stfp_time * stfp)534 stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp)
535 {
536
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;
545 }
546 #else
547 int refclock_bancomm_bs;
548 #endif /* REFCLOCK */
549