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__) 203 inline const char *DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/stfp%d",n);return s;} 204 #else 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 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 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 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 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 * 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 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 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