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