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