Annotation of embedaddon/ntp/ntpd/refclock_bancomm.c, revision 1.1
1.1 ! misho 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: * Imported from ntpd module
! 150: */
! 151: extern volatile int debug; /* global debug flag */
! 152:
! 153: /*
! 154: * VME unit control structure.
! 155: * Changes made to vmeunit structure. Most members are now available in the
! 156: * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan
! 157: */
! 158: struct vmeunit {
! 159: struct vmedate vmedata; /* data returned from vme read */
! 160: u_long lasttime; /* last time clock heard from */
! 161: };
! 162:
! 163: /*
! 164: * Function prototypes
! 165: */
! 166: static int vme_start (int, struct peer *);
! 167: static void vme_shutdown (int, struct peer *);
! 168: static void vme_receive (struct recvbuf *);
! 169: static void vme_poll (int unit, struct peer *);
! 170: struct vmedate *get_datumtime(struct vmedate *);
! 171: void tvme_fill(struct vmedate *, uint32_t btm[2]);
! 172: void stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp);
! 173: inline const char *DEVICE_NAME(int n);
! 174:
! 175:
! 176: /*
! 177: * Define the bc*() functions as weak so we can compile/link without them.
! 178: * Only clients with the card will have the proprietary vendor device driver
! 179: * and interface library needed for use on Linux/Windows platforms.
! 180: */
! 181: extern uint32_t __attribute__ ((weak)) bcReadBinTime(SYMMT_PCI_HANDLE, uint32_t *, uint32_t*, uint8_t*);
! 182: extern SYMMT_PCI_HANDLE __attribute__ ((weak)) bcStartPci(void);
! 183: extern void __attribute__ ((weak)) bcStopPci(SYMMT_PCI_HANDLE);
! 184:
! 185: /*
! 186: * Transfer vector
! 187: */
! 188: struct refclock refclock_bancomm = {
! 189: vme_start, /* start up driver */
! 190: vme_shutdown, /* shut down driver */
! 191: vme_poll, /* transmit poll message */
! 192: noentry, /* not used (old vme_control) */
! 193: noentry, /* initialize driver */
! 194: noentry, /* not used (old vme_buginfo) */
! 195: NOFLAGS /* not used */
! 196: };
! 197:
! 198: int fd_vme; /* file descriptor for ioctls */
! 199: int regvalue;
! 200: int tfp_type; /* mode selector, indicate platform and driver interface */
! 201: SYMMT_PCI_HANDLE stfp_handle;
! 202:
! 203: /**
! 204: * this macro returns the device name based on
! 205: * the platform we are running on and the device number
! 206: */
! 207: #if defined(__sun__)
! 208: inline const char *DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/stfp%d",n);return s;}
! 209: #else
! 210: inline const char* DEVICE_NAME(int n) {static char s[20]={0}; snprintf(s,19,"/dev/btfp%d",n);return s;}
! 211: #endif /**__sun__**/
! 212:
! 213: /*
! 214: * vme_start - open the VME device and initialize data for processing
! 215: */
! 216: static int
! 217: vme_start(
! 218: int unit,
! 219: struct peer *peer
! 220: )
! 221: {
! 222: register struct vmeunit *vme;
! 223: struct refclockproc *pp;
! 224: int dummy;
! 225: char vmedev[20];
! 226:
! 227: tfp_type = (int)(peer->ttl);
! 228: switch (tfp_type) {
! 229: case 1:
! 230: case 3:
! 231: break;
! 232: case 2:
! 233: stfp_handle = bcStartPci(); /* init the card in lin/win */
! 234: break;
! 235: default:
! 236: break;
! 237: }
! 238: /*
! 239: * Open VME device
! 240: */
! 241: #ifdef DEBUG
! 242:
! 243: printf("Opening DATUM DEVICE %s\n",DEVICE_NAME(peer->refclkunit));
! 244: #endif
! 245: if ( (fd_vme = open(DEVICE_NAME(peer->refclkunit), O_RDWR)) < 0) {
! 246: msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev);
! 247: return (0);
! 248: }
! 249: else {
! 250: switch (tfp_type) {
! 251: case 1: break;
! 252: case 2: break;
! 253: case 3:break;
! 254: default:
! 255: /* Release capture lockout in case it was set before. */
! 256: if( ioctl( fd_vme, RUNLOCK, &dummy ) )
! 257: msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m");
! 258:
! 259: regvalue = 0; /* More esoteric stuff to do... */
! 260: if( ioctl( fd_vme, WCR0, ®value ) )
! 261: msyslog(LOG_ERR, "vme_start: WCR0 failed %m");
! 262: break;
! 263: }
! 264: }
! 265:
! 266: /*
! 267: * Allocate unit structure
! 268: */
! 269: vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit));
! 270: bzero((char *)vme, sizeof(struct vmeunit));
! 271:
! 272:
! 273: /*
! 274: * Set up the structures
! 275: */
! 276: pp = peer->procptr;
! 277: pp->unitptr = (caddr_t) vme;
! 278: pp->timestarted = current_time;
! 279:
! 280: pp->io.clock_recv = vme_receive;
! 281: pp->io.srcclock = (caddr_t)peer;
! 282: pp->io.datalen = 0;
! 283: pp->io.fd = fd_vme;
! 284:
! 285: /*
! 286: * All done. Initialize a few random peer variables, then
! 287: * return success. Note that root delay and root dispersion are
! 288: * always zero for this clock.
! 289: */
! 290: peer->precision = VMEPRECISION;
! 291: memcpy(&pp->refid, USNOREFID,4);
! 292: return (1);
! 293: }
! 294:
! 295:
! 296: /*
! 297: * vme_shutdown - shut down a VME clock
! 298: */
! 299: static void
! 300: vme_shutdown(
! 301: int unit,
! 302: struct peer *peer
! 303: )
! 304: {
! 305: register struct vmeunit *vme;
! 306: struct refclockproc *pp;
! 307:
! 308: /*
! 309: * Tell the I/O module to turn us off. We're history.
! 310: */
! 311: pp = peer->procptr;
! 312: vme = (struct vmeunit *)pp->unitptr;
! 313: io_closeclock(&pp->io);
! 314: pp->unitptr = NULL;
! 315: if (NULL != vme)
! 316: free(vme);
! 317: if (tfp_type == 2)
! 318: 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: snprintf(pp->a_lastcode,
! 374: sizeof(pp->a_lastcode),
! 375: "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d",
! 376: tptr->day,
! 377: tptr->hr,
! 378: tptr->mn,
! 379: tptr->sec,
! 380: tptr->frac,
! 381: tptr->status);
! 382:
! 383: pp->lencode = (u_short) strlen(pp->a_lastcode);
! 384:
! 385: pp->day = tptr->day;
! 386: pp->hour = tptr->hr;
! 387: pp->minute = tptr->mn;
! 388: pp->second = tptr->sec;
! 389: pp->nsec = tptr->frac;
! 390:
! 391: #ifdef DEBUG
! 392: if (debug)
! 393: printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n",
! 394: pp->day, pp->hour, pp->minute, pp->second,
! 395: pp->nsec, tptr->status);
! 396: #endif
! 397: if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */
! 398: refclock_report(peer, CEVNT_BADREPLY);
! 399: return;
! 400: }
! 401:
! 402: /*
! 403: * Now, compute the reference time value. Use the heavy
! 404: * machinery for the seconds and the millisecond field for the
! 405: * fraction when present. If an error in conversion to internal
! 406: * format is found, the program declares bad data and exits.
! 407: * Note that this code does not yet know how to do the years and
! 408: * relies on the clock-calendar chip for sanity.
! 409: */
! 410: if (!refclock_process(pp)) {
! 411: refclock_report(peer, CEVNT_BADTIME);
! 412: return;
! 413: }
! 414: pp->lastref = pp->lastrec;
! 415: refclock_receive(peer);
! 416: record_clock_stats(&peer->srcadr, pp->a_lastcode);
! 417: }
! 418:
! 419: struct vmedate *
! 420: get_datumtime(struct vmedate *time_vme)
! 421: {
! 422: char cbuf[7];
! 423: struct btfp_time vts;
! 424: uint32_t btm[2];
! 425: uint8_t dmy;
! 426: struct stfp_time stfpm;
! 427:
! 428: if (time_vme == NULL)
! 429: time_vme = emalloc(sizeof(*time_vme));
! 430:
! 431: switch (tfp_type) {
! 432: case 1: /* BSD, PCI, 2 32bit time words */
! 433: if (ioctl(fd_vme, READTIME, &btm)) {
! 434: msyslog(LOG_ERR, "get_bc63x error: %m");
! 435: return(NULL);
! 436: }
! 437: tvme_fill(time_vme, btm);
! 438: break;
! 439:
! 440: case 2: /* Linux/Windows, PCI, 2 32bit time words */
! 441: if (bcReadBinTime(stfp_handle, &btm[1], &btm[0], &dmy) == 0) {
! 442: msyslog(LOG_ERR, "get_datumtime error: %m");
! 443: return(NULL);
! 444: }
! 445: tvme_fill(time_vme, btm);
! 446: break;
! 447:
! 448: case 3: /** solaris **/
! 449: memset(&stfpm,0,sizeof(stfpm));
! 450:
! 451: /* we need the time in decimal format */
! 452: /* Here we rudely assume that we are the only user of the driver.
! 453: * Other programs will have to set their own time format before reading
! 454: * the time.
! 455: */
! 456: if(ioctl (fd_vme, SELTIMEFORMAT, TIME_DECIMAL)){
! 457: msyslog(LOG_ERR, "Could not set time format\n");
! 458: return (NULL);
! 459: }
! 460: /* read the time */
! 461: if (ioctl(fd_vme, READTIME, &stfpm)) {
! 462: msyslog(LOG_ERR, "ioctl error: %m");
! 463: return(NULL);
! 464: }
! 465: stfp_time2tvme(time_vme, &stfpm);
! 466: break;
! 467:
! 468: default: /* legacy bancomm card */
! 469:
! 470: if (ioctl(fd_vme, READTIME, &vts)) {
! 471: msyslog(LOG_ERR,
! 472: "get_datumtime error: %m");
! 473: return(NULL);
! 474: }
! 475: /* Get day */
! 476: snprintf(cbuf, sizeof(cbuf), "%3.3x",
! 477: ((vts.btfp_time[ 0 ] & 0x000f) << 8) +
! 478: ((vts.btfp_time[ 1 ] & 0xff00) >> 8));
! 479: time_vme->day = (unsigned short)atoi(cbuf);
! 480:
! 481: /* Get hour */
! 482: snprintf(cbuf, sizeof(cbuf), "%2.2x",
! 483: vts.btfp_time[ 1 ] & 0x00ff);
! 484: time_vme->hr = (unsigned short)atoi(cbuf);
! 485:
! 486: /* Get minutes */
! 487: snprintf(cbuf, sizeof(cbuf), "%2.2x",
! 488: (vts.btfp_time[ 2 ] & 0xff00) >> 8);
! 489: time_vme->mn = (unsigned short)atoi(cbuf);
! 490:
! 491: /* Get seconds */
! 492: snprintf(cbuf, sizeof(cbuf), "%2.2x",
! 493: vts.btfp_time[ 2 ] & 0x00ff);
! 494: time_vme->sec = (unsigned short)atoi(cbuf);
! 495:
! 496: /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so
! 497: we can use the TVTOTSF function later on...*/
! 498:
! 499: snprintf(cbuf, sizeof(cbuf), "%4.4x%2.2x",
! 500: vts.btfp_time[ 3 ],
! 501: vts.btfp_time[ 4 ] >> 8);
! 502: time_vme->frac = (u_long) atoi(cbuf);
! 503:
! 504: /* Get status bit */
! 505: time_vme->status = (vts.btfp_time[0] & 0x0010) >> 4;
! 506:
! 507: break;
! 508: }
! 509:
! 510: if (time_vme->status)
! 511: return ((void *)NULL);
! 512: else
! 513: return (time_vme);
! 514: }
! 515: /* Assign values to time_vme struct. Mostly for readability */
! 516: void
! 517: tvme_fill(struct vmedate *time_vme, uint32_t btm[2])
! 518: {
! 519: struct tm maj;
! 520: uint32_t dmaj, dmin;
! 521:
! 522: dmaj = btm[1]; /* syntax sugar */
! 523: dmin = btm[0];
! 524:
! 525: gmtime_r(&dmaj, &maj);
! 526: time_vme->day = maj.tm_yday+1;
! 527: time_vme->hr = maj.tm_hour;
! 528: time_vme->mn = maj.tm_min;
! 529: time_vme->sec = maj.tm_sec;
! 530: time_vme->frac = (dmin & 0x000fffff) * 1000;
! 531: time_vme->frac += ((dmin & 0x00f00000) >> 20) * 100;
! 532: time_vme->status = (dmin & 0x01000000) >> 24;
! 533: return;
! 534: }
! 535:
! 536:
! 537: /* Assign values to time_vme struct. Mostly for readability */
! 538: void
! 539: stfp_time2tvme(struct vmedate *time_vme, struct stfp_time *stfp)
! 540: {
! 541:
! 542: time_vme->day = stfp->tm.tm_yday+1;
! 543: time_vme->hr = stfp->tm.tm_hour;
! 544: time_vme->mn = stfp->tm.tm_min;
! 545: time_vme->sec = stfp->tm.tm_sec;
! 546: time_vme->frac = stfp->usec*1000;
! 547: time_vme->frac += stfp->hnsec * 100;
! 548: time_vme->status = stfp->status;
! 549: return;
! 550: }
! 551: #else
! 552: int refclock_bancomm_bs;
! 553: #endif /* REFCLOCK */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>