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 64struct 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 */ 91struct 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 103struct 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 120struct 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 130typedef 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 */ 146extern u_long current_time; /* current time(s) */ 147 148/* 149 * Imported from ntpd module 150 */ 151extern 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 */ 158struct 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 */ 166static int vme_start (int, struct peer *); 167static void vme_shutdown (int, struct peer *); 168static void vme_receive (struct recvbuf *); 169static void vme_poll (int unit, struct peer *); 170struct vmedate *get_datumtime(struct vmedate *); 171void tvme_fill(struct vmedate *, uint32_t btm[2]); 172void stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp); 173inline 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 */ 181extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, uint32_t *, uint32_t*, uint8_t*); 182extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void); 183extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE); 184 185/* 186 * Transfer vector 187 */ 188struct 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 198int fd_vme; /* file descriptor for ioctls */ 199int regvalue; 200int tfp_type; /* mode selector, indicate platform and driver interface */ 201SYMMT_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__) 208inline const char *DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/stfp%d",n);return s;} 209#else 210inline 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 */ 216static int 217vme_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, ®value ) ) 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 */ 299static void 300vme_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 free(vme); 316 if (tfp_type == 2) bcStopPci(stfp_handle); 317} 318 319 320/* 321 * vme_receive - receive data from the VME device. 322 * 323 * Note: This interface would be interrupt-driven. We don't use that 324 * now, but include a dummy routine for possible future adventures. 325 */ 326static void 327vme_receive( 328 struct recvbuf *rbufp 329 ) 330{ 331} 332 333 334/* 335 * vme_poll - called by the transmit procedure 336 */ 337static void 338vme_poll( 339 int unit, 340 struct peer *peer 341 ) 342{ 343 struct vmedate *tptr; 344 struct vmeunit *vme; 345 struct refclockproc *pp; 346 time_t tloc; 347 struct tm *tadr; 348 349 pp = peer->procptr; 350 vme = (struct vmeunit *)pp->unitptr; /* Here is the structure */ 351 352 tptr = &vme->vmedata; 353 if ((tptr = get_datumtime(tptr)) == NULL ) { 354 refclock_report(peer, CEVNT_BADREPLY); 355 return; 356 } 357 358 get_systime(&pp->lastrec); 359 pp->polls++; 360 vme->lasttime = current_time; 361 362 /* 363 * Get VME time and convert to timestamp format. 364 * The year must come from the system clock. 365 */ 366 367 time(&tloc); 368 tadr = gmtime(&tloc); 369 tptr->year = (unsigned short)(tadr->tm_year + 1900); 370 371 sprintf(pp->a_lastcode, 372 "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d", 373 tptr->day, 374 tptr->hr, 375 tptr->mn, 376 tptr->sec, 377 tptr->frac, 378 tptr->status); 379 380 pp->lencode = (u_short) strlen(pp->a_lastcode); 381 382 pp->day = tptr->day; 383 pp->hour = tptr->hr; 384 pp->minute = tptr->mn; 385 pp->second = tptr->sec; 386 pp->nsec = tptr->frac; 387 388#ifdef DEBUG 389 if (debug) 390 printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n", 391 pp->day, pp->hour, pp->minute, pp->second, 392 pp->nsec, tptr->status); 393#endif 394 if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */ 395 refclock_report(peer, CEVNT_BADREPLY); 396 return; 397 } 398 399 /* 400 * Now, compute the reference time value. Use the heavy 401 * machinery for the seconds and the millisecond field for the 402 * fraction when present. If an error in conversion to internal 403 * format is found, the program declares bad data and exits. 404 * Note that this code does not yet know how to do the years and 405 * relies on the clock-calendar chip for sanity. 406 */ 407 if (!refclock_process(pp)) { 408 refclock_report(peer, CEVNT_BADTIME); 409 return; 410 } 411 pp->lastref = pp->lastrec; 412 refclock_receive(peer); 413 record_clock_stats(&peer->srcadr, pp->a_lastcode); 414} 415 416struct vmedate * 417get_datumtime(struct vmedate *time_vme) 418{ 419 char cbuf[7]; 420 struct btfp_time vts; 421 uint32_t btm[2]; 422 uint8_t dmy; 423 struct stfp_time stfpm; 424 425 if (time_vme == NULL) 426 time_vme = emalloc(sizeof(*time_vme)); 427 428 switch (tfp_type) { 429 case 1: /* BSD, PCI, 2 32bit time words */ 430 if (ioctl(fd_vme, READTIME, &btm)) { 431 msyslog(LOG_ERR, "get_bc63x error: %m"); 432 return(NULL); 433 } 434 tvme_fill(time_vme, btm); 435 break; 436 437 case 2: /* Linux/Windows, PCI, 2 32bit time words */ 438 if (bcReadBinTime(stfp_handle, &btm[1], &btm[0], &dmy) == 0) { 439 msyslog(LOG_ERR, "get_datumtime error: %m"); 440 return(NULL); 441 } 442 tvme_fill(time_vme, btm); 443 break; 444 445 case 3: /** solaris **/ 446 memset(&stfpm,0,sizeof(stfpm)); 447 448 /* we need the time in decimal format */ 449 /* Here we rudely assume that we are the only user of the driver. 450 * Other programs will have to set their own time format before reading 451 * the time. 452 */ 453 if(ioctl (fd_vme, SELTIMEFORMAT, TIME_DECIMAL)){ 454 msyslog(LOG_ERR, "Could not set time format\n"); 455 return (NULL); 456 } 457 /* read the time */ 458 if (ioctl(fd_vme, READTIME, &stfpm)) { 459 msyslog(LOG_ERR, "ioctl error: %m"); 460 return(NULL); 461 } 462 stfp_time2tvme(time_vme, &stfpm); 463 break; 464 465 default: /* legacy bancomm card */ 466 467 if (ioctl(fd_vme, READTIME, &vts)) { 468 msyslog(LOG_ERR, "get_datumtime error: %m"); 469 return(NULL); 470 } 471 /* Get day */ 472 sprintf(cbuf,"%3.3x", ((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 sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff); 478 479 time_vme->hr = (unsigned short)atoi(cbuf); 480 481 /* Get minutes */ 482 sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8); 483 time_vme->mn = (unsigned short)atoi(cbuf); 484 485 /* Get seconds */ 486 sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff); 487 time_vme->sec = (unsigned short)atoi(cbuf); 488 489 /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so 490 we can use the TVTOTSF function later on...*/ 491 492 sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ], 493 vts.btfp_time[ 4 ]>>8); 494 495 time_vme->frac = (u_long) atoi(cbuf); 496 497 /* Get status bit */ 498 time_vme->status = (vts.btfp_time[0] & 0x0010) >>4; 499 500 break; 501 } 502 503 if (time_vme->status) 504 return ((void *)NULL); 505 else 506 return (time_vme); 507} 508/* Assign values to time_vme struct. Mostly for readability */ 509void 510tvme_fill(struct vmedate *time_vme, uint32_t btm[2]) 511{ 512 struct tm maj; 513 uint32_t dmaj, dmin; 514 515 dmaj = btm[1]; /* syntax sugar */ 516 dmin = btm[0]; 517 518 gmtime_r(&dmaj, &maj); 519 time_vme->day = maj.tm_yday+1; 520 time_vme->hr = maj.tm_hour; 521 time_vme->mn = maj.tm_min; 522 time_vme->sec = maj.tm_sec; 523 time_vme->frac = (dmin & 0x000fffff) * 1000; 524 time_vme->frac += ((dmin & 0x00f00000) >> 20) * 100; 525 time_vme->status = (dmin & 0x01000000) >> 24; 526 return; 527} 528 529 530/* Assign values to time_vme struct. Mostly for readability */ 531void 532stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp) 533{ 534 535 time_vme->day = stfp->tm.tm_yday+1; 536 time_vme->hr = stfp->tm.tm_hour; 537 time_vme->mn = stfp->tm.tm_min; 538 time_vme->sec = stfp->tm.tm_sec; 539 time_vme->frac = stfp->usec*1000; 540 time_vme->frac += stfp->hnsec * 100; 541 time_vme->status = stfp->status; 542 return; 543} 544#else 545int refclock_bancomm_bs; 546#endif /* REFCLOCK */ 547