1 /* $NetBSD: refclock_bancomm.c,v 1.4 2016/01/08 21:35:39 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 66 struct btfp_time /* Structure for reading 5 time words */ 67 /* in one ioctl(2) operation. */ 68 { 69 unsigned short btfp_time[5]; /* Time words 0,1,2,3, and 4. (16bit)*/ 70 }; 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 ) 77 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 *****/ 83 84 /* Read all 5 time words in one call. */ 85 #define READTIME IOCIORN(b, 32, sizeof( struct btfp_time )) 86 87 #if defined(__FreeBSD__) 88 #undef READTIME 89 #define READTIME _IOR('u', 5, struct btfp_time ) 90 #endif 91 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; 103 }; 104 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; 110 }; 111 112 #define SELTIMEFORMAT 2 113 # define TIME_DECIMAL 0 114 # define TIME_BINARY 1 115 116 #if defined(__sun__) 117 #undef READTIME 118 #define READTIME 9 119 #endif /** __sun___ **/ 120 /* end solaris specific section */ 121 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; 130 }; 131 132 typedef void *SYMMT_PCI_HANDLE; 133 134 /* 135 * VME interface parameters. 136 */ 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 */ 144 145 /* 146 * Imported from ntp_timer module 147 */ 148 extern u_long current_time; /* current time(s) */ 149 150 /* 151 * VME unit control structure. 152 * Changes made to vmeunit structure. Most members are now available in the 153 * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan 154 */ 155 struct vmeunit { 156 struct vmedate vmedata; /* data returned from vme read */ 157 u_long lasttime; /* last time clock heard from */ 158 }; 159 160 /* 161 * Function prototypes 162 */ 163 static int vme_start (int, struct peer *); 164 static void vme_shutdown (int, struct peer *); 165 static void vme_receive (struct recvbuf *); 166 static void vme_poll (int unit, struct peer *); 167 struct vmedate *get_datumtime(struct vmedate *); 168 void tvme_fill(struct vmedate *, uint32_t btm[2]); 169 void stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp); 170 inline const char *DEVICE_NAME(int n); 171 172 173 /* 174 * Define the bc*() functions as weak so we can compile/link without them. 175 * Only clients with the card will have the proprietary vendor device driver 176 * and interface library needed for use on Linux/Windows platforms. 177 */ 178 extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, uint32_t *, uint32_t*, uint8_t*); 179 extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void); 180 extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE); 181 182 /* 183 * Transfer vector 184 */ 185 struct refclock refclock_bancomm = { 186 vme_start, /* start up driver */ 187 vme_shutdown, /* shut down driver */ 188 vme_poll, /* transmit poll message */ 189 noentry, /* not used (old vme_control) */ 190 noentry, /* initialize driver */ 191 noentry, /* not used (old vme_buginfo) */ 192 NOFLAGS /* not used */ 193 }; 194 195 int fd_vme; /* file descriptor for ioctls */ 196 int regvalue; 197 int tfp_type; /* mode selector, indicate platform and driver interface */ 198 SYMMT_PCI_HANDLE stfp_handle; 199 200 /** 201 * this macro returns the device name based on 202 * the platform we are running on and the device number 203 */ 204 #if defined(__sun__) 205 inline const char *DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/stfp%d",n);return s;} 206 #else 207 inline const char* DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/btfp%d",n);return s;} 208 #endif /**__sun__**/ 209 210 /* 211 * vme_start - open the VME device and initialize data for processing 212 */ 213 static int 214 vme_start( 215 int unit, 216 struct peer *peer 217 ) 218 { 219 register struct vmeunit *vme; 220 struct refclockproc *pp; 221 int dummy; 222 char vmedev[20]; 223 224 tfp_type = (int)(peer->ttl); 225 switch (tfp_type) { 226 case 1: 227 case 3: 228 break; 229 case 2: 230 stfp_handle = bcStartPci(); /* init the card in lin/win */ 231 break; 232 default: 233 break; 234 } 235 /* 236 * Open VME device 237 */ 238 #ifdef DEBUG 239 240 printf("Opening DATUM DEVICE %s\n",DEVICE_NAME(peer->refclkunit)); 241 #endif 242 if ( (fd_vme = open(DEVICE_NAME(peer->refclkunit), O_RDWR)) < 0) { 243 msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev); 244 return (0); 245 } 246 else { 247 switch (tfp_type) { 248 case 1: break; 249 case 2: break; 250 case 3:break; 251 default: 252 /* Release capture lockout in case it was set before. */ 253 if( ioctl( fd_vme, RUNLOCK, &dummy ) ) 254 msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m"); 255 256 regvalue = 0; /* More esoteric stuff to do... */ 257 if( ioctl( fd_vme, WCR0, ®value ) ) 258 msyslog(LOG_ERR, "vme_start: WCR0 failed %m"); 259 break; 260 } 261 } 262 263 /* 264 * Allocate unit structure 265 */ 266 vme = emalloc_zero(sizeof(struct vmeunit)); 267 268 269 /* 270 * Set up the structures 271 */ 272 pp = peer->procptr; 273 pp->unitptr = vme; 274 pp->timestarted = current_time; 275 276 pp->io.clock_recv = vme_receive; 277 pp->io.srcclock = peer; 278 pp->io.datalen = 0; 279 pp->io.fd = fd_vme; 280 /* shouldn't there be an io_addclock() call? */ 281 282 /* 283 * All done. Initialize a few random peer variables, then 284 * return success. Note that root delay and root dispersion are 285 * always zero for this clock. 286 */ 287 peer->precision = VMEPRECISION; 288 memcpy(&pp->refid, USNOREFID,4); 289 return (1); 290 } 291 292 293 /* 294 * vme_shutdown - shut down a VME clock 295 */ 296 static void 297 vme_shutdown( 298 int unit, 299 struct peer *peer 300 ) 301 { 302 register struct vmeunit *vme; 303 struct refclockproc *pp; 304 305 /* 306 * Tell the I/O module to turn us off. We're history. 307 */ 308 pp = peer->procptr; 309 vme = pp->unitptr; 310 io_closeclock(&pp->io); 311 pp->unitptr = NULL; 312 if (NULL != vme) 313 free(vme); 314 if (tfp_type == 2) 315 bcStopPci(stfp_handle); 316 } 317 318 319 /* 320 * vme_receive - receive data from the VME device. 321 * 322 * Note: This interface would be interrupt-driven. We don't use that 323 * now, but include a dummy routine for possible future adventures. 324 */ 325 static void 326 vme_receive( 327 struct recvbuf *rbufp 328 ) 329 { 330 } 331 332 333 /* 334 * vme_poll - called by the transmit procedure 335 */ 336 static void 337 vme_poll( 338 int unit, 339 struct peer *peer 340 ) 341 { 342 struct vmedate *tptr; 343 struct vmeunit *vme; 344 struct refclockproc *pp; 345 time_t tloc; 346 struct tm *tadr; 347 348 pp = peer->procptr; 349 vme = pp->unitptr; /* Here is the structure */ 350 351 tptr = &vme->vmedata; 352 if ((tptr = get_datumtime(tptr)) == NULL ) { 353 refclock_report(peer, CEVNT_BADREPLY); 354 return; 355 } 356 357 get_systime(&pp->lastrec); 358 pp->polls++; 359 vme->lasttime = current_time; 360 361 /* 362 * Get VME time and convert to timestamp format. 363 * The year must come from the system clock. 364 */ 365 366 time(&tloc); 367 tadr = gmtime(&tloc); 368 tptr->year = (unsigned short)(tadr->tm_year + 1900); 369 370 snprintf(pp->a_lastcode, 371 sizeof(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 416 struct vmedate * 417 get_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"); 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, 469 "get_datumtime error: %m"); 470 return(NULL); 471 } 472 /* Get day */ 473 snprintf(cbuf, sizeof(cbuf), "%3.3x", 474 ((vts.btfp_time[ 0 ] & 0x000f) << 8) + 475 ((vts.btfp_time[ 1 ] & 0xff00) >> 8)); 476 time_vme->day = (unsigned short)atoi(cbuf); 477 478 /* Get hour */ 479 snprintf(cbuf, sizeof(cbuf), "%2.2x", 480 vts.btfp_time[ 1 ] & 0x00ff); 481 time_vme->hr = (unsigned short)atoi(cbuf); 482 483 /* Get minutes */ 484 snprintf(cbuf, sizeof(cbuf), "%2.2x", 485 (vts.btfp_time[ 2 ] & 0xff00) >> 8); 486 time_vme->mn = (unsigned short)atoi(cbuf); 487 488 /* Get seconds */ 489 snprintf(cbuf, sizeof(cbuf), "%2.2x", 490 vts.btfp_time[ 2 ] & 0x00ff); 491 time_vme->sec = (unsigned short)atoi(cbuf); 492 493 /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so 494 we can use the TVTOTSF function later on...*/ 495 496 snprintf(cbuf, sizeof(cbuf), "%4.4x%2.2x", 497 vts.btfp_time[ 3 ], 498 vts.btfp_time[ 4 ] >> 8); 499 time_vme->frac = (u_long) atoi(cbuf); 500 501 /* Get status bit */ 502 time_vme->status = (vts.btfp_time[0] & 0x0010) >> 4; 503 504 break; 505 } 506 507 if (time_vme->status) 508 return ((void *)NULL); 509 else 510 return (time_vme); 511 } 512 /* Assign values to time_vme struct. Mostly for readability */ 513 void 514 tvme_fill(struct vmedate *time_vme, uint32_t btm[2]) 515 { 516 struct tm maj; 517 uint32_t dmaj, dmin; 518 519 dmaj = btm[1]; /* syntax sugar */ 520 dmin = btm[0]; 521 522 gmtime_r(&dmaj, &maj); 523 time_vme->day = maj.tm_yday+1; 524 time_vme->hr = maj.tm_hour; 525 time_vme->mn = maj.tm_min; 526 time_vme->sec = maj.tm_sec; 527 time_vme->frac = (dmin & 0x000fffff) * 1000; 528 time_vme->frac += ((dmin & 0x00f00000) >> 20) * 100; 529 time_vme->status = (dmin & 0x01000000) >> 24; 530 return; 531 } 532 533 534 /* Assign values to time_vme struct. Mostly for readability */ 535 void 536 stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp) 537 { 538 539 time_vme->day = stfp->tm.tm_yday+1; 540 time_vme->hr = stfp->tm.tm_hour; 541 time_vme->mn = stfp->tm.tm_min; 542 time_vme->sec = stfp->tm.tm_sec; 543 time_vme->frac = stfp->usec*1000; 544 time_vme->frac += stfp->hnsec * 100; 545 time_vme->status = stfp->status; 546 return; 547 } 548 #else 549 int refclock_bancomm_bs; 550 #endif /* REFCLOCK */ 551