1 /* $NetBSD: refclock_bancomm.c,v 1.5 2020/05/25 20:47:25 christos Exp $ */ 2 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 11 * 12 * Date: July 1999 13 * 14 * Note(s): The refclock type has been defined as 16. 15 * 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. 24 * 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. 32 * 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) 37 * 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. 48 */ 49 50 #ifdef HAVE_CONFIG_H 51 #include <config.h> 52 #endif 53 54 #if defined(REFCLOCK) && defined(CLOCK_BANC) 55 56 #include "ntpd.h" 57 #include "ntp_io.h" 58 #include "ntp_refclock.h" 59 #include "ntp_unixtime.h" 60 #include "ntp_stdlib.h" 61 62 #include <stdio.h> 63 #include <syslog.h> 64 #include <ctype.h> 65 #ifdef HAVE_SYS_IOCTL_H 66 # include <sys/ioctl.h> 67 #endif 68 69 struct btfp_time /* Structure for reading 5 time words */ 70 /* in one ioctl(2) operation. */ 71 { 72 unsigned short btfp_time[5]; /* Time words 0,1,2,3, and 4. (16bit)*/ 73 }; 74 /* SunOS5 ioctl commands definitions.*/ 75 #define BTFPIOC ( 'b'<< 8 ) 76 #define IOCIO( l, n ) ( BTFPIOC | n ) 77 #define IOCIOR( l, n, s ) ( BTFPIOC | n ) 78 #define IOCIORN( l, n, s ) ( BTFPIOC | n ) 79 #define IOCIOWN( l, n, s ) ( BTFPIOC | n ) 80 81 /***** Simple ioctl commands *****/ 82 #define RUNLOCK IOCIOR(b, 19, int ) /* Release Capture Lockout */ 83 #define RCR0 IOCIOR(b, 22, int ) /* Read control register zero.*/ 84 #define WCR0 IOCIOWN(b, 23, int) /* Write control register zero*/ 85 /***** Compound ioctl commands *****/ 86 87 /* Read all 5 time words in one call. */ 88 #if defined(__FreeBSD__) 89 # define READTIME _IOR('u', 5, struct btfp_time ) 90 #else 91 # define READTIME IOCIORN(b, 32, sizeof( struct btfp_time )) 92 #endif 93 94 /* Solaris specific section */ 95 struct stfp_tm { 96 int32_t tm_sec; 97 int32_t tm_min; 98 int32_t tm_hour; 99 int32_t tm_mday; 100 int32_t tm_mon; 101 int32_t tm_year; 102 int32_t tm_wday; 103 int32_t tm_yday; 104 int32_t tm_isdst; 105 }; 106 107 struct stfp_time { 108 struct stfp_tm tm; 109 int32_t usec; /* usec 0 - 999999 */ 110 int32_t hnsec; /* hnsec 0 - 9 (hundreds of nsecs) */ 111 int32_t status; 112 }; 113 114 #define SELTIMEFORMAT 2 115 # define TIME_DECIMAL 0 116 # define TIME_BINARY 1 117 118 #if defined(__sun__) 119 #undef READTIME 120 #define READTIME 9 121 #endif /** __sun___ **/ 122 /* end solaris specific section */ 123 124 struct vmedate { /* structure returned by get_vmetime.c */ 125 unsigned short year; 126 unsigned short day; 127 unsigned short hr; 128 unsigned short mn; 129 unsigned short sec; 130 long frac; 131 unsigned short status; 132 }; 133 134 typedef void *SYMMT_PCI_HANDLE; 135 136 /* 137 * VME interface parameters. 138 */ 139 #define VMEPRECISION (-21) /* precision assumed (1 us) */ 140 #define USNOREFID "BTFP" /* or whatever */ 141 #define VMEREFID "BTFP" /* reference id */ 142 #define VMEDESCRIPTION "Bancomm bc635 TFP" /* who we are */ 143 #define VMEHSREFID 0x7f7f1000 /* 127.127.16.00 refid hi strata */ 144 /* clock type 16 is used here */ 145 #define GMT 0 /* hour offset from Greenwich */ 146 147 /* 148 * Imported from ntp_timer module 149 */ 150 extern u_long current_time; /* current time(s) */ 151 152 /* 153 * VME unit control structure. 154 * Changes made to vmeunit structure. Most members are now available in the 155 * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan 156 */ 157 struct vmeunit { 158 struct vmedate vmedata; /* data returned from vme read */ 159 u_long lasttime; /* last time clock heard from */ 160 }; 161 162 /* 163 * Function prototypes 164 */ 165 static int vme_start (int, struct peer *); 166 static void vme_shutdown (int, struct peer *); 167 static void vme_receive (struct recvbuf *); 168 static void vme_poll (int unit, struct peer *); 169 struct vmedate *get_datumtime(struct vmedate *); 170 void tvme_fill(struct vmedate *, uint32_t btm[2]); 171 void stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp); 172 static const char *get_devicename(int n); 173 174 /* [Bug 3558] and [Bug 1674] perlinger@ntp.org says: 175 * 176 * bcReadBinTime() is defined to use two DWORD pointers on Windows and 177 * Linux in the BANCOMM SDK. DWORD is of course Windows-specific 178 * (*shudder*), and it is defined as 'unsigned long' under 179 * Linux/Unix. (*sigh*) 180 * 181 * This creates quite some headache. The size of 'unsigned long' is 182 * platform/compiler/memory-model dependent (LP32 vs LP64 vs LLP64), 183 * while the card itself always creates 32bit time stamps. What a 184 * bummer. And DWORD has tendency to contain 64bit on Win64 (which is 185 * why we have a DWORD32 defined on Win64) so it can be used as 186 * substitute for 'UINT_PTR' in Windows API headers. I won't even try 187 * to comment on that, because anything I have to say will not be civil. 188 * 189 * We work around this by possibly using a wrapper function that makes 190 * the necessary conversions/casts. It might be a bit tricky to 191 * maintain the conditional logic below, but any lingering disease needs 192 * constant care to avoid a breakout. 193 */ 194 #if defined(__linux__) 195 typedef unsigned long bcBinTimeT; 196 # if SIZEOF_LONG == 4 197 # define safeReadBinTime bcReadBinTime 198 # endif 199 #elif defined(SYS_WINNT) 200 typedef DWORD bcBinTimeT; 201 # if !defined(_WIN64) || _WIN64 == 0 202 # define safeReadBinTime bcReadBinTime 203 # endif 204 #else 205 typedef uint32_t bcBinTimeT; 206 # define safeReadBinTime bcReadBinTime 207 #endif 208 209 /* 210 * Define the bc*() functions as weak so we can compile/link without them. 211 * Only clients with the card will have the proprietary vendor device driver 212 * and interface library needed for use on Linux/Windows platforms. 213 */ 214 extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, bcBinTimeT*, bcBinTimeT*, uint8_t*); 215 extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void); 216 extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE); 217 218 /* This is the conversion wrapper for the long/DWORD/uint32_t clash in 219 * reading binary times. 220 */ 221 #ifndef safeReadBinTime 222 static uint32_t 223 safeReadBinTime( 224 SYMMT_PCI_HANDLE hnd, 225 uint32_t *pt1, 226 uint32_t *pt2, 227 uint8_t *p3 228 ) 229 { 230 bcBinTimeT t1, t2; 231 uint32_t rc; 232 233 rc = bcReadBinTime(hnd, &t1, &t2, p3); 234 if (rc != 0) { 235 *pt1 = (uint32_t)t1; 236 *pt2 = (uint32_t)t2; 237 } 238 return rc; 239 } 240 #endif /* !defined(safeReadBinTime) */ 241 242 /* 243 * Transfer vector 244 */ 245 struct refclock refclock_bancomm = { 246 vme_start, /* start up driver */ 247 vme_shutdown, /* shut down driver */ 248 vme_poll, /* transmit poll message */ 249 noentry, /* not used (old vme_control) */ 250 noentry, /* initialize driver */ 251 noentry, /* not used (old vme_buginfo) */ 252 NOFLAGS /* not used */ 253 }; 254 255 int fd_vme; /* file descriptor for ioctls */ 256 int regvalue; 257 int tfp_type; /* mode selector, indicate platform and driver interface */ 258 SYMMT_PCI_HANDLE stfp_handle; 259 260 /* This helper function returns the device name based on the platform we 261 * are running on and the device number. 262 * 263 * Uses a static buffer, so the result is valid only to the next call of 264 * this function! 265 */ 266 static const char* 267 get_devicename(int n) 268 { 269 270 # if defined(__sun__) 271 static const char * const template ="/dev/stfp%d"; 272 # else 273 static const char * const template ="/dev/btfp%d"; 274 # endif 275 static char namebuf[20]; 276 277 snprintf(namebuf, sizeof(namebuf), template, n); 278 namebuf[sizeof(namebuf)-1] = '\0'; /* paranoia rulez! */ 279 return namebuf; 280 } 281 282 /* 283 * vme_start - open the VME device and initialize data for processing 284 */ 285 static int 286 vme_start( 287 int unit, 288 struct peer *peer 289 ) 290 { 291 register struct vmeunit *vme; 292 struct refclockproc *pp; 293 int dummy; 294 char vmedev[20]; 295 296 tfp_type = (int)(peer->ttl); 297 switch (tfp_type) { 298 case 1: 299 case 3: 300 break; 301 case 2: 302 stfp_handle = bcStartPci(); /* init the card in lin/win */ 303 break; 304 default: 305 break; 306 } 307 /* 308 * Open VME device 309 */ 310 #ifdef DEBUG 311 312 printf("Opening DATUM DEVICE %s\n",get_devicename(peer->refclkunit)); 313 #endif 314 if ( (fd_vme = open(get_devicename(peer->refclkunit), O_RDWR)) < 0) { 315 msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev); 316 return (0); 317 } 318 else { 319 switch (tfp_type) { 320 case 1: break; 321 case 2: break; 322 case 3:break; 323 default: 324 /* Release capture lockout in case it was set before. */ 325 if( ioctl( fd_vme, RUNLOCK, &dummy ) ) 326 msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m"); 327 328 regvalue = 0; /* More esoteric stuff to do... */ 329 if( ioctl( fd_vme, WCR0, ®value ) ) 330 msyslog(LOG_ERR, "vme_start: WCR0 failed %m"); 331 break; 332 } 333 } 334 335 /* 336 * Allocate unit structure 337 */ 338 vme = emalloc_zero(sizeof(struct vmeunit)); 339 340 341 /* 342 * Set up the structures 343 */ 344 pp = peer->procptr; 345 pp->unitptr = vme; 346 pp->timestarted = current_time; 347 348 pp->io.clock_recv = vme_receive; 349 pp->io.srcclock = peer; 350 pp->io.datalen = 0; 351 pp->io.fd = fd_vme; 352 /* shouldn't there be an io_addclock() call? */ 353 354 /* 355 * All done. Initialize a few random peer variables, then 356 * return success. Note that root delay and root dispersion are 357 * always zero for this clock. 358 */ 359 peer->precision = VMEPRECISION; 360 memcpy(&pp->refid, USNOREFID,4); 361 return (1); 362 } 363 364 365 /* 366 * vme_shutdown - shut down a VME clock 367 */ 368 static void 369 vme_shutdown( 370 int unit, 371 struct peer *peer 372 ) 373 { 374 register struct vmeunit *vme; 375 struct refclockproc *pp; 376 377 /* 378 * Tell the I/O module to turn us off. We're history. 379 */ 380 pp = peer->procptr; 381 vme = pp->unitptr; 382 io_closeclock(&pp->io); 383 pp->unitptr = NULL; 384 if (NULL != vme) 385 free(vme); 386 if (tfp_type == 2) 387 bcStopPci(stfp_handle); 388 } 389 390 391 /* 392 * vme_receive - receive data from the VME device. 393 * 394 * Note: This interface would be interrupt-driven. We don't use that 395 * now, but include a dummy routine for possible future adventures. 396 */ 397 static void 398 vme_receive( 399 struct recvbuf *rbufp 400 ) 401 { 402 } 403 404 405 /* 406 * vme_poll - called by the transmit procedure 407 */ 408 static void 409 vme_poll( 410 int unit, 411 struct peer *peer 412 ) 413 { 414 struct vmedate *tptr; 415 struct vmeunit *vme; 416 struct refclockproc *pp; 417 time_t tloc; 418 struct tm *tadr; 419 420 pp = peer->procptr; 421 vme = pp->unitptr; /* Here is the structure */ 422 423 tptr = &vme->vmedata; 424 if ((tptr = get_datumtime(tptr)) == NULL ) { 425 refclock_report(peer, CEVNT_BADREPLY); 426 return; 427 } 428 429 get_systime(&pp->lastrec); 430 pp->polls++; 431 vme->lasttime = current_time; 432 433 /* 434 * Get VME time and convert to timestamp format. 435 * The year must come from the system clock. 436 */ 437 438 time(&tloc); 439 tadr = gmtime(&tloc); 440 tptr->year = (unsigned short)(tadr->tm_year + 1900); 441 442 snprintf(pp->a_lastcode, 443 sizeof(pp->a_lastcode), 444 "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d", 445 tptr->day, 446 tptr->hr, 447 tptr->mn, 448 tptr->sec, 449 tptr->frac, 450 tptr->status); 451 452 pp->lencode = (u_short) strlen(pp->a_lastcode); 453 454 pp->day = tptr->day; 455 pp->hour = tptr->hr; 456 pp->minute = tptr->mn; 457 pp->second = tptr->sec; 458 pp->nsec = tptr->frac; 459 460 #ifdef DEBUG 461 if (debug) 462 printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n", 463 pp->day, pp->hour, pp->minute, pp->second, 464 pp->nsec, tptr->status); 465 #endif 466 if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */ 467 refclock_report(peer, CEVNT_BADREPLY); 468 return; 469 } 470 471 /* 472 * Now, compute the reference time value. Use the heavy 473 * machinery for the seconds and the millisecond field for the 474 * fraction when present. If an error in conversion to internal 475 * format is found, the program declares bad data and exits. 476 * Note that this code does not yet know how to do the years and 477 * relies on the clock-calendar chip for sanity. 478 */ 479 if (!refclock_process(pp)) { 480 refclock_report(peer, CEVNT_BADTIME); 481 return; 482 } 483 pp->lastref = pp->lastrec; 484 refclock_receive(peer); 485 record_clock_stats(&peer->srcadr, pp->a_lastcode); 486 } 487 488 struct vmedate * 489 get_datumtime(struct vmedate *time_vme) 490 { 491 char cbuf[7]; 492 struct btfp_time vts; 493 uint32_t btm[2]; 494 uint8_t dmy; 495 struct stfp_time stfpm; 496 497 if (time_vme == NULL) 498 time_vme = emalloc(sizeof(*time_vme)); 499 500 switch (tfp_type) { 501 case 1: /* BSD, PCI, 2 32bit time words */ 502 if (ioctl(fd_vme, READTIME, &btm)) { 503 msyslog(LOG_ERR, "get_bc63x error: %m"); 504 return(NULL); 505 } 506 tvme_fill(time_vme, btm); 507 break; 508 509 case 2: /* Linux/Windows, PCI, 2 32bit time words */ 510 if (safeReadBinTime(stfp_handle, &btm[1], &btm[0], &dmy) == 0) { 511 msyslog(LOG_ERR, "get_datumtime error: %m"); 512 return(NULL); 513 } 514 tvme_fill(time_vme, btm); 515 break; 516 517 case 3: /** solaris **/ 518 memset(&stfpm,0,sizeof(stfpm)); 519 520 /* we need the time in decimal format */ 521 /* Here we rudely assume that we are the only user of the driver. 522 * Other programs will have to set their own time format before reading 523 * the time. 524 */ 525 if(ioctl (fd_vme, SELTIMEFORMAT, TIME_DECIMAL)){ 526 msyslog(LOG_ERR, "Could not set time format"); 527 return (NULL); 528 } 529 /* read the time */ 530 if (ioctl(fd_vme, READTIME, &stfpm)) { 531 msyslog(LOG_ERR, "ioctl error: %m"); 532 return(NULL); 533 } 534 stfp_time2tvme(time_vme, &stfpm); 535 break; 536 537 default: /* legacy bancomm card */ 538 539 if (ioctl(fd_vme, READTIME, &vts)) { 540 msyslog(LOG_ERR, 541 "get_datumtime error: %m"); 542 return(NULL); 543 } 544 /* Get day */ 545 snprintf(cbuf, sizeof(cbuf), "%3.3x", 546 ((vts.btfp_time[ 0 ] & 0x000f) << 8) + 547 ((vts.btfp_time[ 1 ] & 0xff00) >> 8)); 548 time_vme->day = (unsigned short)atoi(cbuf); 549 550 /* Get hour */ 551 snprintf(cbuf, sizeof(cbuf), "%2.2x", 552 vts.btfp_time[ 1 ] & 0x00ff); 553 time_vme->hr = (unsigned short)atoi(cbuf); 554 555 /* Get minutes */ 556 snprintf(cbuf, sizeof(cbuf), "%2.2x", 557 (vts.btfp_time[ 2 ] & 0xff00) >> 8); 558 time_vme->mn = (unsigned short)atoi(cbuf); 559 560 /* Get seconds */ 561 snprintf(cbuf, sizeof(cbuf), "%2.2x", 562 vts.btfp_time[ 2 ] & 0x00ff); 563 time_vme->sec = (unsigned short)atoi(cbuf); 564 565 /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so 566 we can use the TVTOTSF function later on...*/ 567 568 snprintf(cbuf, sizeof(cbuf), "%4.4x%2.2x", 569 vts.btfp_time[ 3 ], 570 vts.btfp_time[ 4 ] >> 8); 571 time_vme->frac = (u_long) atoi(cbuf); 572 573 /* Get status bit */ 574 time_vme->status = (vts.btfp_time[0] & 0x0010) >> 4; 575 576 break; 577 } 578 579 if (time_vme->status) 580 return ((void *)NULL); 581 else 582 return (time_vme); 583 } 584 /* Assign values to time_vme struct. Mostly for readability */ 585 void 586 tvme_fill(struct vmedate *time_vme, uint32_t btm[2]) 587 { 588 struct tm maj; 589 time_t dmaj; 590 uint32_t dmin; 591 592 dmaj = btm[1]; /* syntax sugar & expansion */ 593 dmin = btm[0]; /* just syntax sugar */ 594 595 gmtime_r(&dmaj, &maj); 596 time_vme->day = maj.tm_yday+1; 597 time_vme->hr = maj.tm_hour; 598 time_vme->mn = maj.tm_min; 599 time_vme->sec = maj.tm_sec; 600 time_vme->frac = (dmin & 0x000fffff) * 1000; 601 time_vme->frac += ((dmin & 0x00f00000) >> 20) * 100; 602 time_vme->status = (dmin & 0x01000000) >> 24; 603 return; 604 } 605 606 607 /* Assign values to time_vme struct. Mostly for readability */ 608 void 609 stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp) 610 { 611 612 time_vme->day = stfp->tm.tm_yday+1; 613 time_vme->hr = stfp->tm.tm_hour; 614 time_vme->mn = stfp->tm.tm_min; 615 time_vme->sec = stfp->tm.tm_sec; 616 time_vme->frac = stfp->usec*1000; 617 time_vme->frac += stfp->hnsec * 100; 618 time_vme->status = stfp->status; 619 return; 620 } 621 #else 622 int refclock_bancomm_bs; 623 #endif /* REFCLOCK */ 624