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 * The time on the bc635 TFP must be set to GMT due to the 24 * fact that NTP makes use of GMT for all its calculations. 25 * 26 * Installation of the Datum/Bancomm driver creates the 27 * device file /dev/btfp0 28 */ 29 30 #ifdef HAVE_CONFIG_H 31 #include <config.h> 32 #endif 33 34 #if defined(REFCLOCK) && defined(CLOCK_BANC) 35 #include <stdio.h> 36 #include <syslog.h> 37 #include <ctype.h> 38 #include <string.h> 39 #include <strings.h> 40 #include <sys/time.h> 41 42 #include "ntpd.h" 43 #include "ntp_io.h" 44 #include "ntp_refclock.h" 45 #include "ntp_unixtime.h" 46 #include "ntp_stdlib.h" 47 48 /* STUFF BY RES */ 49 struct btfp_time /* Structure for reading 5 time words */ 50 /* in one ioctl(2) operation. */ 51 { 52 unsigned short btfp_time[5]; /* Time words 0,1,2,3, and 4. (16bit)*/ 53 }; 54 55 /* SunOS5 ioctl commands definitions.*/ 56 #define BTFPIOC ( 'b'<< 8 ) 57 #define IOCIO( l, n ) ( BTFPIOC | n ) 58 #define IOCIOR( l, n, s ) ( BTFPIOC | n ) 59 #define IOCIORN( l, n, s ) ( BTFPIOC | n ) 60 #define IOCIOWN( l, n, s ) ( BTFPIOC | n ) 61 62 /***** Simple ioctl commands *****/ 63 #define RUNLOCK IOCIOR(b, 19, int ) /* Release Capture Lockout */ 64 #define RCR0 IOCIOR(b, 22, int ) /* Read control register zero.*/ 65 #define WCR0 IOCIOWN(b, 23, int) /* Write control register zero*/ 66 67 /***** Compound ioctl commands *****/ 68 69 /* Read all 5 time words in one call. */ 70 #define READTIME IOCIORN(b, 32, sizeof( struct btfp_time )) 71 #define VMEFD "/dev/btfp0" 72 73 struct vmedate { /* structure returned by get_vmetime.c */ 74 unsigned short year; 75 unsigned short day; 76 unsigned short hr; 77 unsigned short mn; 78 unsigned short sec; 79 unsigned long frac; 80 unsigned short status; 81 }; 82 83 /* END OF STUFF FROM RES */ 84 85 /* 86 * Definitions 87 */ 88 #define MAXUNITS 2 /* max number of VME units */ 89 90 /* 91 * VME interface parameters. 92 */ 93 #define VMEPRECISION (-21) /* precision assumed (1 us) */ 94 #define USNOREFID "BTFP" /* or whatever */ 95 #define VMEREFID "BTFP" /* reference id */ 96 #define VMEDESCRIPTION "Bancomm bc635 TFP" /* who we are */ 97 #define VMEHSREFID 0x7f7f1000 /* 127.127.16.00 refid hi strata */ 98 /* clock type 16 is used here */ 99 #define GMT 0 /* hour offset from Greenwich */ 100 101 /* 102 * Imported from ntp_timer module 103 */ 104 extern u_long current_time; /* current time(s) */ 105 106 /* 107 * Imported from ntpd module 108 */ 109 extern int debug; /* global debug flag */ 110 111 /* 112 * VME unit control structure. 113 * Changes made to vmeunit structure. Most members are now available in the 114 * new refclockproc structure in ntp_refclock.h - 07/99 - Ganesh Ramasivan 115 */ 116 struct vmeunit { 117 struct vmedate vmedata; /* data returned from vme read */ 118 u_long lasttime; /* last time clock heard from */ 119 }; 120 121 /* 122 * Keep the fudge factors separately so they can be set even 123 * when no clock is configured. 124 */ 125 static double fudgefactor[MAXUNITS]; 126 static u_char stratumtouse[MAXUNITS]; 127 static u_char sloppyclockflag[MAXUNITS]; 128 129 /* 130 * Function prototypes 131 */ 132 static void vme_init (void); 133 static int vme_start (int, struct peer *); 134 static void vme_shutdown (int, struct peer *); 135 static void vme_receive (struct recvbuf *); 136 static void vme_poll (int unit, struct peer *); 137 struct vmedate *get_datumtime(struct vmedate *); 138 139 /* 140 * Transfer vector 141 */ 142 struct refclock refclock_bancomm = { 143 vme_start, 144 vme_shutdown, 145 vme_poll, 146 noentry, /* not used (old vme_control) */ 147 vme_init, 148 noentry, /* not used (old vme_buginfo) */ 149 NOFLAGS 150 }; 151 152 int fd_vme; /* file descriptor for ioctls */ 153 int regvalue; 154 155 /* 156 * vme_init - initialize internal vme driver data 157 */ 158 static void 159 vme_init(void) 160 { 161 register int i; 162 163 /* 164 * Initialize fudge factors to default. 165 */ 166 for (i = 0; i < MAXUNITS; i++) { 167 fudgefactor[i] = 0.0; 168 stratumtouse[i] = 0; 169 sloppyclockflag[i] = 0; 170 } 171 } 172 173 /* 174 * vme_start - open the VME device and initialize data for processing 175 */ 176 static int 177 vme_start( 178 int unit, 179 struct peer *peer 180 ) 181 { 182 register struct vmeunit *vme; 183 struct refclockproc *pp; 184 int dummy; 185 char vmedev[20]; 186 187 /* 188 * Check configuration info. 189 */ 190 if (unit >= MAXUNITS) { 191 msyslog(LOG_ERR, "vme_start: unit %d invalid", unit); 192 return (0); 193 } 194 195 /* 196 * Open VME device 197 */ 198 #ifdef DEBUG 199 200 printf("Opening DATUM VME DEVICE \n"); 201 #endif 202 if ( (fd_vme = open(VMEFD, O_RDWR)) < 0) { 203 msyslog(LOG_ERR, "vme_start: failed open of %s: %m", vmedev); 204 return (0); 205 } 206 else { /* Release capture lockout in case it was set from before. */ 207 if( ioctl( fd_vme, RUNLOCK, &dummy ) ) 208 msyslog(LOG_ERR, "vme_start: RUNLOCK failed %m"); 209 210 regvalue = 0; /* More esoteric stuff to do... */ 211 if( ioctl( fd_vme, WCR0, ®value ) ) 212 msyslog(LOG_ERR, "vme_start: WCR0 failed %m"); 213 } 214 215 /* 216 * Allocate unit structure 217 */ 218 vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit)); 219 bzero((char *)vme, sizeof(struct vmeunit)); 220 221 222 /* 223 * Set up the structures 224 */ 225 pp = peer->procptr; 226 pp->unitptr = (caddr_t) vme; 227 pp->timestarted = current_time; 228 229 pp->io.clock_recv = vme_receive; 230 pp->io.srcclock = (caddr_t)peer; 231 pp->io.datalen = 0; 232 pp->io.fd = fd_vme; 233 234 /* 235 * All done. Initialize a few random peer variables, then 236 * return success. Note that root delay and root dispersion are 237 * always zero for this clock. 238 */ 239 pp->leap = LEAP_NOWARNING; 240 peer->precision = VMEPRECISION; 241 peer->stratum = stratumtouse[unit]; 242 memcpy( (char *)&peer->refid, USNOREFID,4); 243 244 peer->refid = htonl(VMEHSREFID); 245 246 return (1); 247 } 248 249 250 /* 251 * vme_shutdown - shut down a VME clock 252 */ 253 static void 254 vme_shutdown( 255 int unit, 256 struct peer *peer 257 ) 258 { 259 register struct vmeunit *vme; 260 struct refclockproc *pp; 261 262 pp = peer->procptr; 263 264 if (unit >= MAXUNITS) { 265 msyslog(LOG_ERR, "vme_shutdown: unit %d invalid", unit); 266 return; 267 } 268 269 /* 270 * Tell the I/O module to turn us off. We're history. 271 */ 272 vme = (struct vmeunit *)pp->unitptr; 273 io_closeclock(&pp->io); 274 pp->unitptr = NULL; 275 free(vme); 276 } 277 278 279 /* 280 * vme_receive - receive data from the VME device. 281 * 282 * Note: This interface would be interrupt-driven. We don't use that 283 * now, but include a dummy routine for possible future adventures. 284 */ 285 static void 286 vme_receive( 287 struct recvbuf *rbufp 288 ) 289 { 290 } 291 292 293 /* 294 * vme_poll - called by the transmit procedure 295 */ 296 static void 297 vme_poll( 298 int unit, 299 struct peer *peer 300 ) 301 { 302 struct vmedate *tptr; 303 struct vmeunit *vme; 304 struct refclockproc *pp; 305 time_t tloc; 306 struct tm *tadr; 307 308 pp = peer->procptr; 309 vme = (struct vmeunit *)pp->unitptr; /* Here is the structure */ 310 311 tptr = &vme->vmedata; 312 if ((tptr = get_datumtime(tptr)) == NULL ) { 313 refclock_report(peer, CEVNT_BADREPLY); 314 return; 315 } 316 317 get_systime(&pp->lastrec); 318 pp->polls++; 319 vme->lasttime = current_time; 320 321 /* 322 * Get VME time and convert to timestamp format. 323 * The year must come from the system clock. 324 */ 325 326 time(&tloc); 327 tadr = gmtime(&tloc); 328 tptr->year = (unsigned short)(tadr->tm_year + 1900); 329 330 331 sprintf(pp->a_lastcode, 332 "%3.3d %2.2d:%2.2d:%2.2d.%.6ld %1d", 333 tptr->day, 334 tptr->hr, 335 tptr->mn, 336 tptr->sec, 337 tptr->frac, 338 tptr->status); 339 340 pp->lencode = (u_short) strlen(pp->a_lastcode); 341 342 pp->day = tptr->day; 343 pp->hour = tptr->hr; 344 pp->minute = tptr->mn; 345 pp->second = tptr->sec; 346 pp->usec = tptr->frac; 347 348 #ifdef DEBUG 349 if (debug) 350 printf("pp: %3d %02d:%02d:%02d.%06ld %1x\n", 351 pp->day, pp->hour, pp->minute, pp->second, 352 pp->usec, tptr->status); 353 #endif 354 if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */ 355 refclock_report(peer, CEVNT_BADREPLY); 356 return; 357 } 358 359 /* 360 * Now, compute the reference time value. Use the heavy 361 * machinery for the seconds and the millisecond field for the 362 * fraction when present. If an error in conversion to internal 363 * format is found, the program declares bad data and exits. 364 * Note that this code does not yet know how to do the years and 365 * relies on the clock-calendar chip for sanity. 366 */ 367 if (!refclock_process(pp)) { 368 refclock_report(peer, CEVNT_BADTIME); 369 return; 370 } 371 record_clock_stats(&peer->srcadr, pp->a_lastcode); 372 refclock_receive(peer); 373 } 374 375 struct vmedate * 376 get_datumtime(struct vmedate *time_vme) 377 { 378 unsigned short status; 379 char cbuf[7]; 380 struct btfp_time vts; 381 382 if ( time_vme == (struct vmedate *)NULL) { 383 time_vme = (struct vmedate *)malloc(sizeof(struct vmedate )); 384 } 385 386 if( ioctl(fd_vme, READTIME, &vts)) 387 msyslog(LOG_ERR, "get_datumtime error: %m"); 388 389 /* if you want to actually check the validity of these registers, do a 390 define of CHECK above this. I didn't find it necessary. - RES 391 */ 392 393 #ifdef CHECK 394 395 /* Get day */ 396 sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) + 397 ((vts.btfp_time[ 1 ] & 0xff00) >> 8)); 398 399 if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2]) ) 400 time_vme->day = (unsigned short)atoi(cbuf); 401 else 402 time_vme->day = (unsigned short) 0; 403 404 /* Get hour */ 405 sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff); 406 407 if (isdigit(cbuf[0]) && isdigit(cbuf[1])) 408 time_vme->hr = (unsigned short)atoi(cbuf); 409 else 410 time_vme->hr = (unsigned short) 0; 411 412 /* Get minutes */ 413 sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8); 414 if (isdigit(cbuf[0]) && isdigit(cbuf[1])) 415 time_vme->mn = (unsigned short)atoi(cbuf); 416 else 417 time_vme->mn = (unsigned short) 0; 418 419 /* Get seconds */ 420 sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff); 421 422 if (isdigit(cbuf[0]) && isdigit(cbuf[1])) 423 time_vme->sec = (unsigned short)atoi(cbuf); 424 else 425 time_vme->sec = (unsigned short) 0; 426 427 /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so we can 428 use the TVTOTSF function later on...*/ 429 430 sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ], 431 vts.btfp_time[ 4 ]>>8); 432 433 if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2]) 434 && isdigit(cbuf[3]) && isdigit(cbuf[4]) && isdigit(cbuf[5])) 435 time_vme->frac = (u_long) atoi(cbuf); 436 else 437 time_vme->frac = (u_long) 0; 438 #else 439 440 /* DONT CHECK just trust the card */ 441 442 /* Get day */ 443 sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) + 444 ((vts.btfp_time[ 1 ] & 0xff00) >> 8)); 445 time_vme->day = (unsigned short)atoi(cbuf); 446 447 /* Get hour */ 448 sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff); 449 450 time_vme->hr = (unsigned short)atoi(cbuf); 451 452 /* Get minutes */ 453 sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8); 454 time_vme->mn = (unsigned short)atoi(cbuf); 455 456 /* Get seconds */ 457 sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff); 458 time_vme->sec = (unsigned short)atoi(cbuf); 459 460 /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so we can 461 use the TVTOTSF function later on...*/ 462 463 sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ], 464 vts.btfp_time[ 4 ]>>8); 465 466 time_vme->frac = (u_long) atoi(cbuf); 467 468 #endif /* CHECK */ 469 470 /* Get status bit */ 471 status = (vts.btfp_time[0] & 0x0010) >>4; 472 time_vme->status = status; /* Status=0 if locked to ref. */ 473 /* Status=1 if flywheeling */ 474 if (status) { /* lost lock ? */ 475 return ((void *)NULL); 476 } 477 else 478 return (time_vme); 479 } 480 481 #else 482 int refclock_bancomm_bs; 483 #endif /* REFCLOCK */ 484