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