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