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 pp->lastref = pp->lastrec; 321 refclock_receive(peer); 322 record_clock_stats(&peer->srcadr, pp->a_lastcode); 323 } 324 325 struct vmedate * 326 get_datumtime(struct vmedate *time_vme) 327 { 328 unsigned short status; 329 char cbuf[7]; 330 struct btfp_time vts; 331 332 if ( time_vme == (struct vmedate *)NULL) { 333 time_vme = (struct vmedate *)malloc(sizeof(struct vmedate )); 334 } 335 336 if( ioctl(fd_vme, READTIME, &vts)) 337 msyslog(LOG_ERR, "get_datumtime error: %m"); 338 339 /* if you want to actually check the validity of these registers, do a 340 define of CHECK above this. I didn't find it necessary. - RES 341 */ 342 343 #ifdef CHECK 344 345 /* Get day */ 346 sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) + 347 ((vts.btfp_time[ 1 ] & 0xff00) >> 8)); 348 349 if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2]) ) 350 time_vme->day = (unsigned short)atoi(cbuf); 351 else 352 time_vme->day = (unsigned short) 0; 353 354 /* Get hour */ 355 sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff); 356 357 if (isdigit(cbuf[0]) && isdigit(cbuf[1])) 358 time_vme->hr = (unsigned short)atoi(cbuf); 359 else 360 time_vme->hr = (unsigned short) 0; 361 362 /* Get minutes */ 363 sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8); 364 if (isdigit(cbuf[0]) && isdigit(cbuf[1])) 365 time_vme->mn = (unsigned short)atoi(cbuf); 366 else 367 time_vme->mn = (unsigned short) 0; 368 369 /* Get seconds */ 370 sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff); 371 372 if (isdigit(cbuf[0]) && isdigit(cbuf[1])) 373 time_vme->sec = (unsigned short)atoi(cbuf); 374 else 375 time_vme->sec = (unsigned short) 0; 376 377 /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so we can 378 use the TVTOTSF function later on...*/ 379 380 sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ], 381 vts.btfp_time[ 4 ]>>8); 382 383 if (isdigit(cbuf[0]) && isdigit(cbuf[1]) && isdigit(cbuf[2]) 384 && isdigit(cbuf[3]) && isdigit(cbuf[4]) && isdigit(cbuf[5])) 385 time_vme->frac = (u_long) atoi(cbuf); 386 else 387 time_vme->frac = (u_long) 0; 388 #else 389 390 /* DONT CHECK just trust the card */ 391 392 /* Get day */ 393 sprintf(cbuf,"%3.3x", ((vts.btfp_time[ 0 ] & 0x000f) <<8) + 394 ((vts.btfp_time[ 1 ] & 0xff00) >> 8)); 395 time_vme->day = (unsigned short)atoi(cbuf); 396 397 /* Get hour */ 398 sprintf(cbuf,"%2.2x", vts.btfp_time[ 1 ] & 0x00ff); 399 400 time_vme->hr = (unsigned short)atoi(cbuf); 401 402 /* Get minutes */ 403 sprintf(cbuf,"%2.2x", (vts.btfp_time[ 2 ] & 0xff00) >>8); 404 time_vme->mn = (unsigned short)atoi(cbuf); 405 406 /* Get seconds */ 407 sprintf(cbuf,"%2.2x", vts.btfp_time[ 2 ] & 0x00ff); 408 time_vme->sec = (unsigned short)atoi(cbuf); 409 410 /* Get microseconds. Yes, we ignore the 0.1 microsecond digit so we can 411 use the TVTOTSF function later on...*/ 412 413 sprintf(cbuf,"%4.4x%2.2x", vts.btfp_time[ 3 ], 414 vts.btfp_time[ 4 ]>>8); 415 416 time_vme->frac = (u_long) atoi(cbuf); 417 418 #endif /* CHECK */ 419 420 /* Get status bit */ 421 status = (vts.btfp_time[0] & 0x0010) >>4; 422 time_vme->status = status; /* Status=0 if locked to ref. */ 423 /* Status=1 if flywheeling */ 424 if (status) { /* lost lock ? */ 425 return ((void *)NULL); 426 } 427 else 428 return (time_vme); 429 } 430 431 #else 432 int refclock_bancomm_bs; 433 #endif /* REFCLOCK */ 434