1 /* 2 * refclock_gpsvme.c NTP clock driver for the TrueTime GPS-VME 3 * R. Schmidt, Time Service, US Naval Obs. res@tuttle.usno.navy.mil 4 * 5 * The refclock type has been defined as 16 (until new id assigned). 6 * These DEFS are included in the Makefile: 7 * DEFS= -DHAVE_TERMIOS -DSYS_HPUX=9 8 * DEFS_LOCAL= -DREFCLOCK 9 * CLOCKDEFS= -DGPSVME 10 * The file map_vme.c does the VME memory mapping, and includes vme_init(). 11 * map_vme.c is HP-UX specific, because HPUX cannot mmap() device files! Boo! 12 * The file gps.h provides TrueTime register info. 13 */ 14 #ifdef HAVE_CONFIG_H 15 #include <config.h> 16 #endif 17 18 #if defined(REFCLOCK) && defined(CLOCK_GPSVME) 19 #include <stdio.h> 20 #include <syslog.h> 21 #include <ctype.h> 22 #include <string.h> 23 #include <strings.h> 24 #include <sys/time.h> 25 26 #include "gps.h" 27 #include "ntpd.h" 28 #include "ntp_io.h" 29 #include "ntp_refclock.h" 30 #include "ntp_unixtime.h" 31 #include "ntp_stdlib.h" 32 #include "/etc/conf/h/io.h" 33 34 /* GLOBAL STUFF BY RES */ 35 36 #include <time.h> 37 38 #define PRIO 120 /* set the realtime priority */ 39 #define NREGS 7 /* number of registers we will use */ 40 41 extern int init_vme(); /* This is just a call to map_vme() */ 42 /* It doesn't have to be extern */ 43 unsigned short *greg[NREGS]; /* GPS registers defined in gps.h */ 44 void *gps_base; /* Base address of GPS VME card returned by */ 45 /* the map_vme() call */ 46 extern caddr_t map_vme (); 47 extern void unmap_vme(); /* Unmaps the VME space */ 48 49 struct vmedate { /* structure needed by ntp */ 50 unsigned short year; /* *tptr is a pointer to this */ 51 unsigned short doy; 52 unsigned short hr; 53 unsigned short mn; 54 unsigned short sec; 55 unsigned long frac; 56 unsigned short status; 57 }; 58 59 struct vmedate *get_gpsvme_time(); 60 61 /* END OF STUFF FROM RES */ 62 63 /* 64 * Definitions 65 */ 66 #define MAXUNITS 2 /* max number of VME units */ 67 #define BMAX 50 /* timecode buffer length */ 68 69 /* 70 * VME interface parameters. 71 */ 72 #define VMEPRECISION (-21) /* precision assumed (1 us) */ 73 #define USNOREFID "USNO\0" /* Or whatever? */ 74 #define VMEREFID "GPS" /* reference id */ 75 #define VMEDESCRIPTION "GPS" /* who we are */ 76 #define VMEHSREFID 0x7f7f1001 /* 127.127.16.01 refid hi strata */ 77 78 /* I'm using clock type 16 until one is assigned */ 79 /* This is set also in vme_control, below */ 80 81 82 #define GMT 0 /* hour offset from Greenwich */ 83 84 /* 85 * VME unit control structure. 86 */ 87 struct vmeunit { 88 struct peer *peer; /* associated peer structure */ 89 struct refclockio io; /* given to the I/O handler */ 90 struct vmedate vmedata; /* data returned from vme read */ 91 l_fp lastrec; /* last local time */ 92 l_fp lastref; /* last timecode time */ 93 char lastcode[BMAX]; /* last timecode received */ 94 u_short lencode; /* length of last timecode */ 95 u_long lasttime; /* last time clock heard from */ 96 u_short unit; /* unit number for this guy */ 97 u_short status; /* clock status */ 98 u_short lastevent; /* last clock event */ 99 u_short year; /* year of eternity */ 100 u_short day; /* day of year */ 101 u_short hour; /* hour of day */ 102 u_short minute; /* minute of hour */ 103 u_short second; /* seconds of minute */ 104 u_long usec; /* microsecond of second */ 105 u_long yearstart; /* start of current year */ 106 u_short leap; /* leap indicators */ 107 /* 108 * Status tallies 109 */ 110 u_long polls; /* polls sent */ 111 u_long noreply; /* no replies to polls */ 112 u_long coderecv; /* timecodes received */ 113 u_long badformat; /* bad format */ 114 u_long baddata; /* bad data */ 115 u_long timestarted; /* time we started this */ 116 }; 117 118 /* 119 * Data space for the unit structures. Note that we allocate these on 120 * the fly, but never give them back. 121 */ 122 static struct vmeunit *vmeunits[MAXUNITS]; 123 static u_char unitinuse[MAXUNITS]; 124 125 /* 126 * Keep the fudge factors separately so they can be set even 127 * when no clock is configured. 128 */ 129 static l_fp fudgefactor[MAXUNITS]; 130 static u_char stratumtouse[MAXUNITS]; 131 static u_char sloppyclockflag[MAXUNITS]; 132 133 /* 134 * Function prototypes 135 */ 136 static void vme_init (void); 137 static int vme_start (u_int, struct peer *); 138 static void vme_shutdown (int); 139 static void vme_report_event (struct vmeunit *, int); 140 static void vme_receive (struct recvbuf *); 141 static void vme_poll (int unit, struct peer *); 142 static void vme_control (u_int, struct refclockstat *, struct refclockstat *); 143 static void vme_buginfo (int, struct refclockbug *); 144 145 /* 146 * Transfer vector 147 */ 148 struct refclock refclock_gpsvme = { 149 vme_start, vme_shutdown, vme_poll, 150 vme_control, vme_init, vme_buginfo, NOFLAGS 151 }; 152 153 int fd_vme; /* file descriptor for ioctls */ 154 int regvalue; 155 156 /* 157 * vme_init - initialize internal vme driver data 158 */ 159 static void 160 vme_init(void) 161 { 162 register int i; 163 /* 164 * Just zero the data arrays 165 */ 166 /* 167 bzero((char *)vmeunits, sizeof vmeunits); 168 bzero((char *)unitinuse, sizeof unitinuse); 169 */ 170 171 /* 172 * Initialize fudge factors to default. 173 */ 174 for (i = 0; i < MAXUNITS; i++) { 175 fudgefactor[i].l_ui = 0; 176 fudgefactor[i].l_uf = 0; 177 stratumtouse[i] = 0; 178 sloppyclockflag[i] = 0; 179 } 180 } 181 182 /* 183 * vme_start - open the VME device and initialize data for processing 184 */ 185 static int 186 vme_start( 187 u_int unit, 188 struct peer *peer 189 ) 190 { 191 register struct vmeunit *vme; 192 register int i; 193 int dummy; 194 char vmedev[20]; 195 196 /* 197 * Check configuration info. 198 */ 199 if (unit >= MAXUNITS) { 200 msyslog(LOG_ERR, "vme_start: unit %d invalid", unit); 201 return (0); 202 } 203 if (unitinuse[unit]) { 204 msyslog(LOG_ERR, "vme_start: unit %d in use", unit); 205 return (0); 206 } 207 208 /* 209 * Open VME device 210 */ 211 #ifdef DEBUG 212 213 printf("Opening VME DEVICE \n"); 214 #endif 215 init_vme(); /* This is in the map_vme.c external file */ 216 217 /* 218 * Allocate unit structure 219 */ 220 if (vmeunits[unit] != 0) { 221 vme = vmeunits[unit]; /* The one we want is okay */ 222 } else { 223 for (i = 0; i < MAXUNITS; i++) { 224 if (!unitinuse[i] && vmeunits[i] != 0) 225 break; 226 } 227 if (i < MAXUNITS) { 228 /* 229 * Reclaim this one 230 */ 231 vme = vmeunits[i]; 232 vmeunits[i] = 0; 233 } else { 234 vme = (struct vmeunit *) 235 emalloc(sizeof(struct vmeunit)); 236 } 237 } 238 bzero((char *)vme, sizeof(struct vmeunit)); 239 vmeunits[unit] = vme; 240 241 /* 242 * Set up the structures 243 */ 244 vme->peer = peer; 245 vme->unit = (u_short)unit; 246 vme->timestarted = current_time; 247 248 vme->io.clock_recv = vme_receive; 249 vme->io.srcclock = (caddr_t)vme; 250 vme->io.datalen = 0; 251 vme->io.fd = fd_vme; 252 253 /* 254 * All done. Initialize a few random peer variables, then 255 * return success. 256 */ 257 peer->precision = VMEPRECISION; 258 peer->stratum = stratumtouse[unit]; 259 memcpy( (char *)&peer->refid, USNOREFID,4); 260 261 /* peer->refid = htonl(VMEHSREFID); */ 262 263 unitinuse[unit] = 1; 264 return (1); 265 } 266 267 268 /* 269 * vme_shutdown - shut down a VME clock 270 */ 271 static void 272 vme_shutdown( 273 int unit 274 ) 275 { 276 register struct vmeunit *vme; 277 278 if (unit >= MAXUNITS) { 279 msyslog(LOG_ERR, "vme_shutdown: unit %d invalid", unit); 280 return; 281 } 282 if (!unitinuse[unit]) { 283 msyslog(LOG_ERR, "vme_shutdown: unit %d not in use", unit); 284 return; 285 } 286 287 /* 288 * Tell the I/O module to turn us off. We're history. 289 */ 290 unmap_vme(); 291 vme = vmeunits[unit]; 292 io_closeclock(&vme->io); 293 unitinuse[unit] = 0; 294 } 295 296 /* 297 * vme_report_event - note the occurance of an event 298 * 299 * This routine presently just remembers the report and logs it, but 300 * does nothing heroic for the trap handler. 301 */ 302 static void 303 vme_report_event( 304 struct vmeunit *vme, 305 int code 306 ) 307 { 308 struct peer *peer; 309 310 peer = vme->peer; 311 if (vme->status != (u_short)code) { 312 vme->status = (u_short)code; 313 if (code != CEVNT_NOMINAL) 314 vme->lastevent = (u_short)code; 315 msyslog(LOG_INFO, 316 "clock %s event %x", ntoa(&peer->srcadr), code); 317 } 318 } 319 320 321 /* 322 * vme_receive - receive data from the VME device. 323 * 324 * Note: This interface would be interrupt-driven. We don't use that 325 * now, but include a dummy routine for possible future adventures. 326 */ 327 static void 328 vme_receive( 329 struct recvbuf *rbufp 330 ) 331 { 332 } 333 334 /* 335 * vme_poll - called by the transmit procedure 336 */ 337 static void 338 vme_poll( 339 int unit, 340 struct peer *peer 341 ) 342 { 343 struct vmedate *tptr; 344 struct vmeunit *vme; 345 l_fp tstmp; 346 time_t tloc; 347 struct tm *tadr; 348 349 350 vme = (struct vmeunit *)emalloc(sizeof(struct vmeunit *)); 351 tptr = (struct vmedate *)emalloc(sizeof(struct vmedate *)); 352 353 354 if (unit >= MAXUNITS) { 355 msyslog(LOG_ERR, "vme_poll: unit %d invalid", unit); 356 return; 357 } 358 if (!unitinuse[unit]) { 359 msyslog(LOG_ERR, "vme_poll: unit %d not in use", unit); 360 return; 361 } 362 vme = vmeunits[unit]; /* Here is the structure */ 363 vme->polls++; 364 365 tptr = &vme->vmedata; 366 367 if ((tptr = get_gpsvme_time()) == NULL ) { 368 vme_report_event(vme, CEVNT_BADREPLY); 369 return; 370 } 371 372 get_systime(&vme->lastrec); 373 vme->lasttime = current_time; 374 375 /* 376 * Get VME time and convert to timestamp format. 377 * The year must come from the system clock. 378 */ 379 /* 380 time(&tloc); 381 tadr = gmtime(&tloc); 382 tptr->year = (unsigned short)(tadr->tm_year + 1900); 383 */ 384 385 sprintf(vme->lastcode, 386 "%3.3d %2.2d:%2.2d:%2.2d.%.6d %1d\0", 387 tptr->doy, tptr->hr, tptr->mn, 388 tptr->sec, tptr->frac, tptr->status); 389 390 record_clock_stats(&(vme->peer->srcadr), vme->lastcode); 391 vme->lencode = (u_short) strlen(vme->lastcode); 392 393 vme->day = tptr->doy; 394 vme->hour = tptr->hr; 395 vme->minute = tptr->mn; 396 vme->second = tptr->sec; 397 vme->usec = tptr->frac; 398 399 #ifdef DEBUG 400 if (debug) 401 printf("vme: %3d %02d:%02d:%02d.%06ld %1x\n", 402 vme->day, vme->hour, vme->minute, vme->second, 403 vme->usec, tptr->status); 404 #endif 405 if (tptr->status ) { /* Status 0 is locked to ref., 1 is not */ 406 vme_report_event(vme, CEVNT_BADREPLY); 407 return; 408 } 409 410 /* 411 * Now, compute the reference time value. Use the heavy 412 * machinery for the seconds and the millisecond field for the 413 * fraction when present. If an error in conversion to internal 414 * format is found, the program declares bad data and exits. 415 * Note that this code does not yet know how to do the years and 416 * relies on the clock-calendar chip for sanity. 417 */ 418 if (!clocktime(vme->day, vme->hour, vme->minute, 419 vme->second, GMT, vme->lastrec.l_ui, 420 &vme->yearstart, &vme->lastref.l_ui)) { 421 vme->baddata++; 422 vme_report_event(vme, CEVNT_BADTIME); 423 msyslog(LOG_ERR, "refclock_gpsvme: bad data!!"); 424 return; 425 } 426 TVUTOTSF(vme->usec, vme->lastref.l_uf); 427 tstmp = vme->lastref; 428 429 L_SUB(&tstmp, &vme->lastrec); 430 vme->coderecv++; 431 432 L_ADD(&tstmp, &(fudgefactor[vme->unit])); 433 434 refclock_receive(vme->peer); 435 } 436 437 /* 438 * vme_control - set fudge factors, return statistics 439 */ 440 static void 441 vme_control( 442 u_int unit, 443 struct refclockstat *in, 444 struct refclockstat *out 445 ) 446 { 447 register struct vmeunit *vme; 448 449 if (unit >= MAXUNITS) { 450 msyslog(LOG_ERR, "vme_control: unit %d invalid)", unit); 451 return; 452 } 453 454 if (in != 0) { 455 if (in->haveflags & CLK_HAVETIME1) 456 fudgefactor[unit] = in->fudgetime1; 457 if (in->haveflags & CLK_HAVEVAL1) { 458 stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf); 459 if (unitinuse[unit]) { 460 struct peer *peer; 461 462 /* 463 * Should actually reselect clock, but 464 * will wait for the next timecode 465 */ 466 vme = vmeunits[unit]; 467 peer = vme->peer; 468 peer->stratum = stratumtouse[unit]; 469 if (stratumtouse[unit] <= 1) 470 memcpy( (char *)&peer->refid, USNOREFID,4); 471 else 472 peer->refid = htonl(VMEHSREFID); 473 } 474 } 475 if (in->haveflags & CLK_HAVEFLAG1) { 476 sloppyclockflag[unit] = in->flags & CLK_FLAG1; 477 } 478 } 479 480 if (out != 0) { 481 out->type = 16; /*set by RES SHOULD BE CHANGED */ 482 out->haveflags 483 = CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1; 484 out->clockdesc = VMEDESCRIPTION; 485 out->fudgetime1 = fudgefactor[unit]; 486 out->fudgetime2.l_ui = 0; 487 out->fudgetime2.l_uf = 0; 488 out->fudgeval1 = (LONG)stratumtouse[unit]; 489 out->fudgeval2 = 0; 490 out->flags = sloppyclockflag[unit]; 491 if (unitinuse[unit]) { 492 vme = vmeunits[unit]; 493 out->lencode = vme->lencode; 494 out->lastcode = vme->lastcode; 495 out->timereset = current_time - vme->timestarted; 496 out->polls = vme->polls; 497 out->noresponse = vme->noreply; 498 out->badformat = vme->badformat; 499 out->baddata = vme->baddata; 500 out->lastevent = vme->lastevent; 501 out->currentstatus = vme->status; 502 } else { 503 out->lencode = 0; 504 out->lastcode = ""; 505 out->polls = out->noresponse = 0; 506 out->badformat = out->baddata = 0; 507 out->timereset = 0; 508 out->currentstatus = out->lastevent = CEVNT_NOMINAL; 509 } 510 } 511 } 512 513 /* 514 * vme_buginfo - return clock dependent debugging info 515 */ 516 static void 517 vme_buginfo( 518 int unit, 519 register struct refclockbug *bug 520 ) 521 { 522 register struct vmeunit *vme; 523 524 if (unit >= MAXUNITS) { 525 msyslog(LOG_ERR, "vme_buginfo: unit %d invalid)", unit); 526 return; 527 } 528 529 if (!unitinuse[unit]) 530 return; 531 vme = vmeunits[unit]; 532 533 bug->nvalues = 11; 534 bug->ntimes = 5; 535 if (vme->lasttime != 0) 536 bug->values[0] = current_time - vme->lasttime; 537 else 538 bug->values[0] = 0; 539 bug->values[2] = (u_long)vme->year; 540 bug->values[3] = (u_long)vme->day; 541 bug->values[4] = (u_long)vme->hour; 542 bug->values[5] = (u_long)vme->minute; 543 bug->values[6] = (u_long)vme->second; 544 bug->values[7] = (u_long)vme->usec; 545 bug->values[9] = vme->yearstart; 546 bug->stimes = 0x1c; 547 bug->times[0] = vme->lastref; 548 bug->times[1] = vme->lastrec; 549 } 550 /* -------------------------------------------------------*/ 551 /* get_gpsvme_time() */ 552 /* R. Schmidt, USNO, 1995 */ 553 /* It's ugly, but hey, it works and its free */ 554 555 #include "gps.h" /* defines for TrueTime GPS-VME */ 556 557 #define PBIAS 193 /* 193 microsecs to read the GPS experimentally found */ 558 559 struct vmedate * 560 get_gpsvme_time(void) 561 { 562 struct vmedate *time_vme; 563 unsigned short set, hr, min, sec, ums, hms, status; 564 int ret; 565 char ti[3]; 566 567 long tloc ; 568 time_t mktime(),time(); 569 struct tm *gmtime(), *gmt; 570 char *gpsmicro; 571 gpsmicro = (char *) malloc(7); 572 573 time_vme = (struct vmedate *)malloc(sizeof(struct vmedate )); 574 *greg = (unsigned short *)malloc(sizeof(short) * NREGS); 575 576 577 /* reference the freeze command address general register 1 */ 578 set = *greg[0]; 579 /* read the registers : */ 580 /* get year */ 581 time_vme->year = (unsigned short) *greg[6]; 582 /* Get doy */ 583 time_vme->doy = (unsigned short) (*greg[5] & MASKDAY); 584 /* Get hour */ 585 time_vme->hr = (unsigned short) ((*greg[4] & MASKHI) >>8); 586 /* Get minutes */ 587 time_vme->mn = (unsigned short) (*greg[4] & MASKLO); 588 /* Get seconds */ 589 time_vme->sec = (unsigned short) (*greg[3] & MASKHI) >>8; 590 /* get microseconds in 2 parts and put together */ 591 ums = *greg[2]; 592 hms = *greg[3] & MASKLO; 593 594 time_vme->status = (unsigned short) *greg[5] >>13; 595 596 /* reference the unfreeze command address general register 1 */ 597 set = *greg[1]; 598 599 sprintf(gpsmicro,"%2.2x%4.4x\0", hms, ums); 600 time_vme->frac = (u_long) gpsmicro; 601 602 /* unmap_vme(); */ 603 604 if (!status) { 605 return ((void *)NULL); 606 } 607 else 608 return (time_vme); 609 } 610 611 #else 612 int refclock_gpsvme_bs; 613 #endif /* REFCLOCK */ 614