kern_tc.c revision 58377
1117610Sdes/* 2117610Sdes * ---------------------------------------------------------------------------- 3141098Sdes * "THE BEER-WARE LICENSE" (Revision 42): 4255376Sdes * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you 5141098Sdes * can do whatever you want with this stuff. If we meet some day, and you think 6141098Sdes * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7141098Sdes * ---------------------------------------------------------------------------- 8141098Sdes * 9117610Sdes * $FreeBSD: head/sys/kern/kern_tc.c 58377 2000-03-20 14:09:06Z phk $ 10141098Sdes */ 11117610Sdes 12141098Sdes#include "opt_ntp.h" 13141098Sdes 14141098Sdes#include <sys/param.h> 15141098Sdes#include <sys/timetc.h> 16141098Sdes#include <sys/malloc.h> 17141098Sdes#include <sys/kernel.h> 18117610Sdes#include <sys/sysctl.h> 19141098Sdes#include <sys/systm.h> 20141098Sdes#include <sys/timex.h> 21141098Sdes#include <sys/timepps.h> 22141098Sdes 23141098Sdes/* 24141098Sdes * Number of timecounters used to implement stable storage 25141098Sdes */ 26141098Sdes#ifndef NTIMECOUNTER 27141098Sdes#define NTIMECOUNTER 5 28141098Sdes#endif 29141098Sdes 30141098Sdesstatic MALLOC_DEFINE(M_TIMECOUNTER, "timecounter", 31141098Sdes "Timecounter stable storage"); 32141098Sdes 33141098Sdesstatic void tco_setscales __P((struct timecounter *tc)); 34141098Sdesstatic __inline unsigned tco_delta __P((struct timecounter *tc)); 35141098Sdes 36141098Sdestime_t time_second; 37117610Sdes 38255376Sdesstruct timeval boottime; 39117610SdesSYSCTL_STRUCT(_kern, KERN_BOOTTIME, boottime, CTLFLAG_RD, 40117610Sdes &boottime, timeval, "System boottime"); 41117610Sdes 42228692SdesSYSCTL_NODE(_kern, OID_AUTO, timecounter, CTLFLAG_RW, 0, ""); 43117610Sdes 44228692Sdesstatic unsigned nmicrotime; 45228692Sdesstatic unsigned nnanotime; 46228692Sdesstatic unsigned ngetmicrotime; 47228692Sdesstatic unsigned ngetnanotime; 48117610Sdesstatic unsigned nmicrouptime; 49117610Sdesstatic unsigned nnanouptime; 50117610Sdesstatic unsigned ngetmicrouptime; 51228692Sdesstatic unsigned ngetnanouptime; 52228692SdesSYSCTL_INT(_kern_timecounter, OID_AUTO, nmicrotime, CTLFLAG_RD, &nmicrotime, 0, ""); 53228692SdesSYSCTL_INT(_kern_timecounter, OID_AUTO, nnanotime, CTLFLAG_RD, &nnanotime, 0, ""); 54228692SdesSYSCTL_INT(_kern_timecounter, OID_AUTO, nmicrouptime, CTLFLAG_RD, &nmicrouptime, 0, ""); 55228692SdesSYSCTL_INT(_kern_timecounter, OID_AUTO, nnanouptime, CTLFLAG_RD, &nnanouptime, 0, ""); 56228692SdesSYSCTL_INT(_kern_timecounter, OID_AUTO, ngetmicrotime, CTLFLAG_RD, &ngetmicrotime, 0, ""); 57117610SdesSYSCTL_INT(_kern_timecounter, OID_AUTO, ngetnanotime, CTLFLAG_RD, &ngetnanotime, 0, ""); 58228692SdesSYSCTL_INT(_kern_timecounter, OID_AUTO, ngetmicrouptime, CTLFLAG_RD, &ngetmicrouptime, 0, ""); 59228692SdesSYSCTL_INT(_kern_timecounter, OID_AUTO, ngetnanouptime, CTLFLAG_RD, &ngetnanouptime, 0, ""); 60117610Sdes 61228692Sdes/* 62228692Sdes * Implement a dummy timecounter which we can use until we get a real one 63228692Sdes * in the air. This allows the console and other early stuff to use 64228692Sdes * timeservices. 65228692Sdes */ 66228692Sdes 67228692Sdesstatic unsigned 68228692Sdesdummy_get_timecount(struct timecounter *tc) 69228692Sdes{ 70117610Sdes static unsigned now; 71228692Sdes 72228692Sdes return (++now); 73228692Sdes} 74228692Sdes 75228692Sdesstatic struct timecounter dummy_timecounter = { 76228692Sdes dummy_get_timecount, 77228692Sdes 0, 78228692Sdes ~0u, 79228692Sdes 1000000, 80228692Sdes "dummy" 81228692Sdes}; 82228692Sdes 83228692Sdesstruct timecounter *timecounter = &dummy_timecounter; 84228692Sdes 85228692Sdesstatic __inline unsigned 86228692Sdestco_delta(struct timecounter *tc) 87228692Sdes{ 88228692Sdes 89141098Sdes return ((tc->tc_get_timecount(tc) - tc->tc_offset_count) & 90228692Sdes tc->tc_counter_mask); 91228692Sdes} 92141098Sdes 93228692Sdes/* 94141098Sdes * We have eight functions for looking at the clock, four for 95141098Sdes * microseconds and four for nanoseconds. For each there is fast 96141098Sdes * but less precise version "get{nano|micro}[up]time" which will 97228692Sdes * return a time which is up to 1/HZ previous to the call, whereas 98228692Sdes * the raw version "{nano|micro}[up]time" will return a timestamp 99228692Sdes * which is as precise as possible. The "up" variants return the 100174832Sdes * time relative to system boot, these are well suited for time 101117610Sdes * interval measurements. 102228692Sdes */ 103228692Sdes 104141098Sdesvoid 105174832Sdesgetmicrotime(struct timeval *tvp) 106174832Sdes{ 107117610Sdes struct timecounter *tc; 108174832Sdes 109174832Sdes ngetmicrotime++; 110174832Sdes tc = timecounter; 111117610Sdes *tvp = tc->tc_microtime; 112141098Sdes} 113228692Sdes 114228692Sdesvoid 115117610Sdesgetnanotime(struct timespec *tsp) 116228692Sdes{ 117228692Sdes struct timecounter *tc; 118228692Sdes 119228692Sdes ngetnanotime++; 120228692Sdes tc = timecounter; 121228692Sdes *tsp = tc->tc_nanotime; 122228692Sdes} 123228692Sdes 124228692Sdesvoid 125228692Sdesmicrotime(struct timeval *tv) 126141098Sdes{ 127228692Sdes struct timecounter *tc; 128228692Sdes 129141098Sdes nmicrotime++; 130117610Sdes tc = timecounter; 131228692Sdes tv->tv_sec = tc->tc_offset_sec; 132141098Sdes tv->tv_usec = tc->tc_offset_micro; 133228692Sdes tv->tv_usec += ((u_int64_t)tco_delta(tc) * tc->tc_scale_micro) >> 32; 134117610Sdes tv->tv_usec += boottime.tv_usec; 135228692Sdes tv->tv_sec += boottime.tv_sec; 136117610Sdes while (tv->tv_usec >= 1000000) { 137228692Sdes tv->tv_usec -= 1000000; 138228692Sdes tv->tv_sec++; 139141098Sdes } 140228692Sdes} 141117610Sdes 142174832Sdesvoid 143117610Sdesnanotime(struct timespec *ts) 144228692Sdes{ 145228692Sdes unsigned count; 146228692Sdes u_int64_t delta; 147228692Sdes struct timecounter *tc; 148228692Sdes 149228692Sdes nnanotime++; 150228692Sdes tc = timecounter; 151228692Sdes ts->tv_sec = tc->tc_offset_sec; 152117610Sdes count = tco_delta(tc); 153141098Sdes delta = tc->tc_offset_nano; 154228692Sdes delta += ((u_int64_t)count * tc->tc_scale_nano_f); 155117610Sdes delta >>= 32; 156228692Sdes delta += ((u_int64_t)count * tc->tc_scale_nano_i); 157117610Sdes delta += boottime.tv_usec * 1000; 158228692Sdes ts->tv_sec += boottime.tv_sec; 159255376Sdes while (delta >= 1000000000) { 160255376Sdes delta -= 1000000000; 161255376Sdes ts->tv_sec++; 162255376Sdes } 163228692Sdes ts->tv_nsec = delta; 164117610Sdes} 165228692Sdes 166117610Sdesvoid 167174832Sdesgetmicrouptime(struct timeval *tvp) 168174832Sdes{ 169228692Sdes struct timecounter *tc; 170141098Sdes 171228692Sdes ngetmicrouptime++; 172228692Sdes tc = timecounter; 173228692Sdes tvp->tv_sec = tc->tc_offset_sec; 174228692Sdes tvp->tv_usec = tc->tc_offset_micro; 175228692Sdes} 176141098Sdes 177228692Sdesvoid 178141098Sdesgetnanouptime(struct timespec *tsp) 179141098Sdes{ 180228692Sdes struct timecounter *tc; 181228692Sdes 182228692Sdes ngetnanouptime++; 183228692Sdes tc = timecounter; 184228692Sdes tsp->tv_sec = tc->tc_offset_sec; 185228692Sdes tsp->tv_nsec = tc->tc_offset_nano >> 32; 186228692Sdes} 187228692Sdes 188228692Sdesvoid 189228692Sdesmicrouptime(struct timeval *tv) 190228692Sdes{ 191228692Sdes struct timecounter *tc; 192228692Sdes 193255376Sdes nmicrouptime++; 194255376Sdes tc = timecounter; 195255376Sdes tv->tv_sec = tc->tc_offset_sec; 196255376Sdes tv->tv_usec = tc->tc_offset_micro; 197228692Sdes tv->tv_usec += ((u_int64_t)tco_delta(tc) * tc->tc_scale_micro) >> 32; 198228692Sdes if (tv->tv_usec >= 1000000) { 199228692Sdes tv->tv_usec -= 1000000; 200228692Sdes tv->tv_sec++; 201141098Sdes } 202141098Sdes} 203141098Sdes 204141098Sdesvoid 205255376Sdesnanouptime(struct timespec *ts) 206141098Sdes{ 207141098Sdes unsigned count; 208117610Sdes u_int64_t delta; 209117610Sdes struct timecounter *tc; 210228692Sdes 211255376Sdes nnanouptime++; 212255376Sdes tc = timecounter; 213255376Sdes ts->tv_sec = tc->tc_offset_sec; 214255376Sdes count = tco_delta(tc); 215255376Sdes delta = tc->tc_offset_nano; 216228692Sdes delta += ((u_int64_t)count * tc->tc_scale_nano_f); 217228692Sdes delta >>= 32; 218228692Sdes delta += ((u_int64_t)count * tc->tc_scale_nano_i); 219228692Sdes if (delta >= 1000000000) { 220228692Sdes delta -= 1000000000; 221228692Sdes ts->tv_sec++; 222228692Sdes } 223228692Sdes ts->tv_nsec = delta; 224228692Sdes} 225228692Sdes 226228692Sdesstatic void 227228692Sdestco_setscales(struct timecounter *tc) 228228692Sdes{ 229228692Sdes u_int64_t scale; 230228692Sdes 231228692Sdes scale = 1000000000LL << 32; 232228692Sdes scale += tc->tc_adjustment; 233228692Sdes scale /= tc->tc_tweak->tc_frequency; 234228692Sdes tc->tc_scale_micro = scale / 1000; 235228692Sdes tc->tc_scale_nano_f = scale & 0xffffffff; 236228692Sdes tc->tc_scale_nano_i = scale >> 32; 237228692Sdes} 238228692Sdes 239228692Sdesvoid 240228692Sdestc_update(struct timecounter *tc) 241141098Sdes{ 242141098Sdes tco_setscales(tc); 243255376Sdes} 244141098Sdes 245255376Sdesvoid 246141098Sdestc_init(struct timecounter *tc) 247117610Sdes{ 248141098Sdes struct timespec ts1; 249141098Sdes struct timecounter *t1, *t2, *t3; 250228692Sdes int i; 251228692Sdes 252228692Sdes tc->tc_adjustment = 0; 253228692Sdes tc->tc_tweak = tc; 254117610Sdes tco_setscales(tc); 255174832Sdes tc->tc_offset_count = tc->tc_get_timecount(tc); 256141098Sdes if (timecounter == &dummy_timecounter) 257141098Sdes tc->tc_avail = tc; 258141098Sdes else { 259141098Sdes tc->tc_avail = timecounter->tc_tweak->tc_avail; 260141098Sdes timecounter->tc_tweak->tc_avail = tc; 261141098Sdes } 262117610Sdes MALLOC(t1, struct timecounter *, sizeof *t1, M_TIMECOUNTER, M_WAITOK); 263228692Sdes tc->tc_other = t1; 264141098Sdes *t1 = *tc; 265141098Sdes t2 = t1; 266141098Sdes for (i = 1; i < NTIMECOUNTER; i++) { 267228692Sdes MALLOC(t3, struct timecounter *, sizeof *t3, 268117610Sdes M_TIMECOUNTER, M_WAITOK); 269141098Sdes *t3 = *tc; 270141098Sdes t3->tc_other = t2; 271141098Sdes t2 = t3; 272174832Sdes } 273228692Sdes t1->tc_other = t3; 274174832Sdes tc = t1; 275174832Sdes 276228692Sdes printf("Timecounter \"%s\" frequency %lu Hz\n", 277228692Sdes tc->tc_name, (u_long)tc->tc_frequency); 278228692Sdes 279228692Sdes /* XXX: For now always start using the counter. */ 280228692Sdes tc->tc_offset_count = tc->tc_get_timecount(tc); 281228692Sdes nanouptime(&ts1); 282228692Sdes tc->tc_offset_nano = (u_int64_t)ts1.tv_nsec << 32; 283228692Sdes tc->tc_offset_micro = ts1.tv_nsec / 1000; 284228692Sdes tc->tc_offset_sec = ts1.tv_sec; 285228692Sdes timecounter = tc; 286228692Sdes} 287228692Sdes 288228692Sdesvoid 289228692Sdestc_setclock(struct timespec *ts) 290228692Sdes{ 291228692Sdes struct timespec ts2; 292228692Sdes 293228692Sdes nanouptime(&ts2); 294228692Sdes boottime.tv_sec = ts->tv_sec - ts2.tv_sec; 295228692Sdes boottime.tv_usec = (ts->tv_nsec - ts2.tv_nsec) / 1000; 296228692Sdes if (boottime.tv_usec < 0) { 297228692Sdes boottime.tv_usec += 1000000; 298228692Sdes boottime.tv_sec--; 299228692Sdes } 300228692Sdes /* fiddle all the little crinkly bits around the fiords... */ 301228692Sdes tc_windup(); 302228692Sdes} 303228692Sdes 304228692Sdesstatic void 305228692Sdesswitch_timecounter(struct timecounter *newtc) 306228692Sdes{ 307228692Sdes int s; 308228692Sdes struct timecounter *tc; 309141098Sdes struct timespec ts; 310141098Sdes 311117610Sdes s = splclock(); 312228692Sdes tc = timecounter; 313117610Sdes if (newtc->tc_tweak == tc->tc_tweak) { 314228692Sdes splx(s); 315228692Sdes return; 316228692Sdes } 317228692Sdes newtc = newtc->tc_tweak->tc_other; 318228692Sdes nanouptime(&ts); 319228692Sdes newtc->tc_offset_sec = ts.tv_sec; 320228692Sdes newtc->tc_offset_nano = (u_int64_t)ts.tv_nsec << 32; 321228692Sdes newtc->tc_offset_micro = ts.tv_nsec / 1000; 322228692Sdes newtc->tc_offset_count = newtc->tc_get_timecount(newtc); 323228692Sdes tco_setscales(newtc); 324117610Sdes timecounter = newtc; 325228692Sdes splx(s); 326228692Sdes} 327228692Sdes 328228692Sdesstatic struct timecounter * 329228692Sdessync_other_counter(void) 330228692Sdes{ 331228692Sdes struct timecounter *tc, *tcn, *tco; 332117610Sdes unsigned delta; 333228692Sdes 334228692Sdes tco = timecounter; 335228692Sdes tc = tco->tc_other; 336228692Sdes tcn = tc->tc_other; 337228692Sdes *tc = *tco; 338228692Sdes tc->tc_other = tcn; 339228692Sdes delta = tco_delta(tc); 340117610Sdes tc->tc_offset_count += delta; 341228692Sdes tc->tc_offset_count &= tc->tc_counter_mask; 342228692Sdes tc->tc_offset_nano += (u_int64_t)delta * tc->tc_scale_nano_f; 343228692Sdes tc->tc_offset_nano += (u_int64_t)delta * tc->tc_scale_nano_i << 32; 344228692Sdes return (tc); 345228692Sdes} 346228692Sdes 347228692Sdesvoid 348228692Sdestc_windup(void) 349228692Sdes{ 350117610Sdes struct timecounter *tc, *tco; 351228692Sdes struct timeval tvt; 352228692Sdes 353228692Sdes tco = timecounter; 354228692Sdes tc = sync_other_counter(); 355228692Sdes /* 356228692Sdes * We may be inducing a tiny error here, the tc_poll_pps() may 357255376Sdes * process a latched count which happens after the tco_delta() 358228692Sdes * in sync_other_counter(), which would extend the previous 359228692Sdes * counters parameters into the domain of this new one. 360228692Sdes * Since the timewindow is very small for this, the error is 361228692Sdes * going to be only a few weenieseconds (as Dave Mills would 362228692Sdes * say), so lets just not talk more about it, OK ? 363228692Sdes */ 364228692Sdes if (tco->tc_poll_pps) 365228692Sdes tco->tc_poll_pps(tco); 366228692Sdes if (timedelta != 0) { 367228692Sdes tvt = boottime; 368228692Sdes tvt.tv_usec += tickdelta; 369228692Sdes if (tvt.tv_usec >= 1000000) { 370228692Sdes tvt.tv_sec++; 371228692Sdes tvt.tv_usec -= 1000000; 372228692Sdes } else if (tvt.tv_usec < 0) { 373228692Sdes tvt.tv_sec--; 374228692Sdes tvt.tv_usec += 1000000; 375228692Sdes } 376228692Sdes boottime = tvt; 377228692Sdes timedelta -= tickdelta; 378228692Sdes } 379228692Sdes 380228692Sdes while (tc->tc_offset_nano >= 1000000000ULL << 32) { 381228692Sdes tc->tc_offset_nano -= 1000000000ULL << 32; 382228692Sdes tc->tc_offset_sec++; 383228692Sdes ntp_update_second(tc); /* XXX only needed if xntpd runs */ 384228692Sdes tco_setscales(tc); 385228692Sdes } 386228692Sdes 387228692Sdes tc->tc_offset_micro = (tc->tc_offset_nano / 1000) >> 32; 388228692Sdes 389228692Sdes /* Figure out the wall-clock time */ 390228692Sdes tc->tc_nanotime.tv_sec = tc->tc_offset_sec + boottime.tv_sec; 391228692Sdes tc->tc_nanotime.tv_nsec = 392228692Sdes (tc->tc_offset_nano >> 32) + boottime.tv_usec * 1000; 393228692Sdes tc->tc_microtime.tv_usec = tc->tc_offset_micro + boottime.tv_usec; 394228692Sdes if (tc->tc_nanotime.tv_nsec >= 1000000000) { 395255376Sdes tc->tc_nanotime.tv_nsec -= 1000000000; 396228692Sdes tc->tc_microtime.tv_usec -= 1000000; 397228692Sdes tc->tc_nanotime.tv_sec++; 398228692Sdes } 399228692Sdes time_second = tc->tc_microtime.tv_sec = tc->tc_nanotime.tv_sec; 400228692Sdes 401228692Sdes timecounter = tc; 402228692Sdes} 403228692Sdes 404228692Sdesstatic int 405141098Sdessysctl_kern_timecounter_hardware SYSCTL_HANDLER_ARGS 406228692Sdes{ 407228692Sdes char newname[32]; 408228692Sdes struct timecounter *newtc, *tc; 409228692Sdes int error; 410228692Sdes 411228692Sdes tc = timecounter->tc_tweak; 412228692Sdes strncpy(newname, tc->tc_name, sizeof(newname)); 413255376Sdes error = sysctl_handle_string(oidp, &newname[0], sizeof(newname), req); 414228692Sdes if (error == 0 && req->newptr != NULL && 415228692Sdes strcmp(newname, tc->tc_name) != 0) { 416228692Sdes for (newtc = tc->tc_avail; newtc != tc; 417228692Sdes newtc = newtc->tc_avail) { 418228692Sdes if (strcmp(newname, newtc->tc_name) == 0) { 419228692Sdes /* Warm up new timecounter. */ 420228692Sdes (void)newtc->tc_get_timecount(newtc); 421228692Sdes 422228692Sdes switch_timecounter(newtc); 423228692Sdes return (0); 424228692Sdes } 425228692Sdes } 426228692Sdes return (EINVAL); 427228692Sdes } 428228692Sdes return (error); 429228692Sdes} 430228692Sdes 431228692SdesSYSCTL_PROC(_kern_timecounter, OID_AUTO, hardware, CTLTYPE_STRING | CTLFLAG_RW, 432228692Sdes 0, 0, sysctl_kern_timecounter_hardware, "A", ""); 433228692Sdes 434228692Sdes 435228692Sdesint 436228692Sdespps_ioctl(u_long cmd, caddr_t data, struct pps_state *pps) 437228692Sdes{ 438228692Sdes pps_params_t *app; 439228692Sdes struct pps_fetch_args *fapi; 440228692Sdes#ifdef PPS_SYNC 441141098Sdes struct pps_kcbind_args *kapi; 442228692Sdes#endif 443141098Sdes 444117610Sdes switch (cmd) { 445141098Sdes case PPS_IOC_CREATE: 446228692Sdes return (0); 447228692Sdes case PPS_IOC_DESTROY: 448228692Sdes return (0); 449228692Sdes case PPS_IOC_SETPARAMS: 450141098Sdes app = (pps_params_t *)data; 451117610Sdes if (app->mode & ~pps->ppscap) 452141098Sdes return (EINVAL); 453141098Sdes pps->ppsparam = *app; 454141098Sdes return (0); 455117610Sdes case PPS_IOC_GETPARAMS: 456141098Sdes app = (pps_params_t *)data; 457174832Sdes *app = pps->ppsparam; 458117610Sdes app->api_version = PPS_API_VERS_1; 459174832Sdes return (0); 460228692Sdes case PPS_IOC_GETCAP: 461117610Sdes *(int*)data = pps->ppscap; 462141098Sdes return (0); 463141098Sdes case PPS_IOC_FETCH: 464141098Sdes fapi = (struct pps_fetch_args *)data; 465141098Sdes if (fapi->tsformat && fapi->tsformat != PPS_TSFMT_TSPEC) 466174832Sdes return (EINVAL); 467141098Sdes if (fapi->timeout.tv_sec || fapi->timeout.tv_nsec) 468228692Sdes return (EOPNOTSUPP); 469228692Sdes pps->ppsinfo.current_mode = pps->ppsparam.mode; 470228692Sdes fapi->pps_info_buf = pps->ppsinfo; 471228692Sdes return (0); 472117610Sdes case PPS_IOC_KCBIND: 473228692Sdes#ifdef PPS_SYNC 474228692Sdes kapi = (struct pps_kcbind_args *)data; 475228692Sdes /* XXX Only root should be able to do this */ 476228692Sdes if (kapi->tsformat && kapi->tsformat != PPS_TSFMT_TSPEC) 477117610Sdes return (EINVAL); 478228692Sdes if (kapi->kernel_consumer != PPS_KC_HARDPPS) 479228692Sdes return (EINVAL); 480228692Sdes if (kapi->edge & ~pps->ppscap) 481228692Sdes return (EINVAL); 482228692Sdes pps->kcmode = kapi->edge; 483117610Sdes return (0); 484228692Sdes#else 485228692Sdes return (EOPNOTSUPP); 486228692Sdes#endif 487228692Sdes default: 488228692Sdes return (ENOTTY); 489228692Sdes } 490228692Sdes} 491228692Sdes 492228692Sdesvoid 493228692Sdespps_init(struct pps_state *pps) 494228692Sdes{ 495228692Sdes pps->ppscap |= PPS_TSFMT_TSPEC; 496228692Sdes if (pps->ppscap & PPS_CAPTUREASSERT) 497228692Sdes pps->ppscap |= PPS_OFFSETASSERT; 498228692Sdes if (pps->ppscap & PPS_CAPTURECLEAR) 499228692Sdes pps->ppscap |= PPS_OFFSETCLEAR; 500228692Sdes} 501228692Sdes 502228692Sdesvoid 503228692Sdespps_event(struct pps_state *pps, struct timecounter *tc, unsigned count, int event) 504228692Sdes{ 505228692Sdes struct timespec ts, *tsp, *osp; 506228692Sdes u_int64_t delta; 507228692Sdes unsigned tcount, *pcount; 508228692Sdes int foff, fhard; 509228692Sdes pps_seq_t *pseq; 510228692Sdes 511228692Sdes /* Things would be easier with arrays... */ 512228692Sdes if (event == PPS_CAPTUREASSERT) { 513228692Sdes tsp = &pps->ppsinfo.assert_timestamp; 514228692Sdes osp = &pps->ppsparam.assert_offset; 515228692Sdes foff = pps->ppsparam.mode & PPS_OFFSETASSERT; 516228692Sdes fhard = pps->kcmode & PPS_CAPTUREASSERT; 517228692Sdes pcount = &pps->ppscount[0]; 518228692Sdes pseq = &pps->ppsinfo.assert_sequence; 519141098Sdes } else { 520117610Sdes tsp = &pps->ppsinfo.clear_timestamp; 521141098Sdes osp = &pps->ppsparam.clear_offset; 522141098Sdes foff = pps->ppsparam.mode & PPS_OFFSETCLEAR; 523141098Sdes fhard = pps->kcmode & PPS_CAPTURECLEAR; 524141098Sdes pcount = &pps->ppscount[1]; 525228692Sdes pseq = &pps->ppsinfo.clear_sequence; 526228692Sdes } 527141098Sdes 528 /* The timecounter changed: bail */ 529 if (!pps->ppstc || 530 pps->ppstc->tc_name != tc->tc_name || 531 tc->tc_name != timecounter->tc_name) { 532 pps->ppstc = tc; 533 *pcount = count; 534 return; 535 } 536 537 /* Nothing really happened */ 538 if (*pcount == count) 539 return; 540 541 *pcount = count; 542 543 /* Convert the count to timespec */ 544 ts.tv_sec = tc->tc_offset_sec; 545 tcount = count - tc->tc_offset_count; 546 tcount &= tc->tc_counter_mask; 547 delta = tc->tc_offset_nano; 548 delta += ((u_int64_t)tcount * tc->tc_scale_nano_f); 549 delta >>= 32; 550 delta += ((u_int64_t)tcount * tc->tc_scale_nano_i); 551 delta += boottime.tv_usec * 1000; 552 ts.tv_sec += boottime.tv_sec; 553 while (delta >= 1000000000) { 554 delta -= 1000000000; 555 ts.tv_sec++; 556 } 557 ts.tv_nsec = delta; 558 559 (*pseq)++; 560 *tsp = ts; 561 562 if (foff) { 563 timespecadd(tsp, osp); 564 if (tsp->tv_nsec < 0) { 565 tsp->tv_nsec += 1000000000; 566 tsp->tv_sec -= 1; 567 } 568 } 569#ifdef PPS_SYNC 570 if (fhard) { 571 /* magic, at its best... */ 572 tcount = count - pps->ppscount[2]; 573 pps->ppscount[2] = count; 574 tcount &= tc->tc_counter_mask; 575 delta = ((u_int64_t)tcount * tc->tc_tweak->tc_scale_nano_f); 576 delta >>= 32; 577 delta += ((u_int64_t)tcount * tc->tc_tweak->tc_scale_nano_i); 578 hardpps(tsp, delta); 579 } 580#endif 581} 582