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