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