1226586Sdim/*********************************************************************** 2226586Sdim * * 3226586Sdim * Copyright (c) David L. Mills 1999-2000 * 4226586Sdim * * 5226586Sdim * Permission to use, copy, modify, and distribute this software and * 6226586Sdim * its documentation for any purpose and without fee is hereby * 7226586Sdim * granted, provided that the above copyright notice appears in all * 8226586Sdim * copies and that both the copyright notice and this permission * 9226586Sdim * notice appear in supporting documentation, and that the name * 10226586Sdim * University of Delaware not be used in advertising or publicity * 11226586Sdim * pertaining to distribution of the software without specific, * 12226586Sdim * written prior permission. The University of Delaware makes no * 13226586Sdim * representations about the suitability this software for any * 14249423Sdim * purpose. It is provided "as is" without express or implied * 15226586Sdim * warranty. * 16226586Sdim * * 17226586Sdim *********************************************************************** 18226586Sdim * * 19226586Sdim * This header file complies with "Pulse-Per-Second API for UNIX-like * 20226586Sdim * Operating Systems, Version 1.0", rfc2783. Credit is due Jeff Mogul * 21226586Sdim * and Marc Brett, from whom much of this code was shamelessly stolen. * 22226586Sdim * * 23226586Sdim * this modified timepps.h can be used to provide a PPSAPI interface * 24226586Sdim * to a machine running Solaris (2.6 and above). * 25226586Sdim * * 26226586Sdim *********************************************************************** 27226586Sdim * * 28226586Sdim * A full PPSAPI interface to the Solaris kernel would be better, but * 29226586Sdim * this at least removes the necessity for special coding from the NTP * 30226586Sdim * NTP drivers. * 31226586Sdim * * 32226586Sdim *********************************************************************** 33226586Sdim * * 34288943Sdim * Some of this include file * 35226586Sdim * Copyright (c) 1999 by Ulrich Windl, * 36226586Sdim * based on code by Reg Clemens <reg@dwf.com> * 37226586Sdim * based on code by Poul-Henning Kamp <phk@FreeBSD.org> * 38234353Sdim * * 39234353Sdim *********************************************************************** 40234353Sdim * * 41234353Sdim * "THE BEER-WARE LICENSE" (Revision 42): * 42226586Sdim * <phk@FreeBSD.org> wrote this file. As long as you retain this * 43226586Sdim * notice you can do whatever you want with this stuff. If we meet some* 44226586Sdim * day, and you think this stuff is worth it, you can buy me a beer * 45226586Sdim * in return. Poul-Henning Kamp * 46226586Sdim * * 47226586Sdim **********************************************************************/ 48234353Sdim 49243830Sdim/* Solaris version, TIOCGPPSEV and TIOCSPPS assumed to exist. */ 50276479Sdim 51276479Sdim#ifndef _SYS_TIMEPPS_H_ 52226586Sdim#define _SYS_TIMEPPS_H_ 53226586Sdim 54226586Sdim#include <termios.h> /* to get TOCGPPSEV and TIOCSPPS */ 55239462Sdim 56234353Sdim/* Implementation note: the logical states ``assert'' and ``clear'' 57234353Sdim * are implemented in terms of the UART register, i.e. ``assert'' 58226586Sdim * means the bit is set. 59226586Sdim */ 60226586Sdim 61226586Sdim/* 62243830Sdim * The following definitions are architecture independent 63239462Sdim */ 64239462Sdim 65239462Sdim#define PPS_API_VERS_1 1 /* API version number */ 66239462Sdim#define PPS_JAN_1970 2208988800UL /* 1970 - 1900 in seconds */ 67239462Sdim#define PPS_NANOSECOND 1000000000L /* one nanosecond in decimal */ 68239462Sdim#define PPS_FRAC 4294967296. /* 2^32 as a double */ 69239462Sdim 70243830Sdim#define PPS_NORMALIZE(x) /* normalize timespec */ \ 71249423Sdim do { \ 72251662Sdim if ((x).tv_nsec >= PPS_NANOSECOND) { \ 73251662Sdim (x).tv_nsec -= PPS_NANOSECOND; \ 74243830Sdim (x).tv_sec++; \ 75243830Sdim } else if ((x).tv_nsec < 0) { \ 76239462Sdim (x).tv_nsec += PPS_NANOSECOND; \ 77249423Sdim (x).tv_sec--; \ 78251662Sdim } \ 79251662Sdim } while (0) 80243830Sdim 81243830Sdim#define PPS_TSPECTONTP(x) /* convert timespec to l_fp */ \ 82239462Sdim do { \ 83239462Sdim double d_temp; \ 84239462Sdim \ 85276479Sdim (x).integral += (unsigned int)PPS_JAN_1970; \ 86276479Sdim d_temp = (x).fractional * PPS_FRAC / PPS_NANOSECOND; \ 87276479Sdim if (d_temp >= PPS_FRAC) \ 88276479Sdim (x).integral++; \ 89276479Sdim (x).fractional = (unsigned int)d_temp; \ 90276479Sdim } while (0) 91226586Sdim 92226586Sdim/* 93226586Sdim * Device/implementation parameters (mode) 94226586Sdim */ 95234353Sdim 96226586Sdim#define PPS_CAPTUREASSERT 0x01 /* capture assert events */ 97226586Sdim#define PPS_CAPTURECLEAR 0x02 /* capture clear events */ 98226586Sdim#define PPS_CAPTUREBOTH 0x03 /* capture assert and clear events */ 99234353Sdim 100234353Sdim#define PPS_OFFSETASSERT 0x10 /* apply compensation for assert ev. */ 101226586Sdim#define PPS_OFFSETCLEAR 0x20 /* apply compensation for clear ev. */ 102226586Sdim#define PPS_OFFSETBOTH 0x30 /* apply compensation for both */ 103226586Sdim 104226586Sdim#define PPS_CANWAIT 0x100 /* Can we wait for an event? */ 105226586Sdim#define PPS_CANPOLL 0x200 /* "This bit is reserved for */ 106226586Sdim 107226586Sdim/* 108226586Sdim * Kernel actions (mode) 109226586Sdim */ 110226586Sdim 111226586Sdim#define PPS_ECHOASSERT 0x40 /* feed back assert event to output */ 112226586Sdim#define PPS_ECHOCLEAR 0x80 /* feed back clear event to output */ 113226586Sdim 114226586Sdim/* 115226586Sdim * Timestamp formats (tsformat) 116226586Sdim */ 117226586Sdim 118226586Sdim#define PPS_TSFMT_TSPEC 0x1000 /* select timespec format */ 119226586Sdim#define PPS_TSFMT_NTPFP 0x2000 /* select NTP format */ 120226586Sdim 121226586Sdim/* 122226586Sdim * Kernel discipline actions (not used in Solaris) 123226586Sdim */ 124226586Sdim 125234353Sdim#define PPS_KC_HARDPPS 0 /* enable kernel consumer */ 126226586Sdim#define PPS_KC_HARDPPS_PLL 1 /* phase-lock mode */ 127226586Sdim#define PPS_KC_HARDPPS_FLL 2 /* frequency-lock mode */ 128226586Sdim 129226586Sdim/* 130226586Sdim * Type definitions 131234353Sdim */ 132234353Sdim 133226586Sdimtypedef unsigned long pps_seq_t; /* sequence number */ 134226586Sdim 135226586Sdimtypedef struct ntp_fp { 136226586Sdim unsigned int integral; 137226586Sdim unsigned int fractional; 138226586Sdim} ntp_fp_t; /* NTP-compatible time stamp */ 139226586Sdim 140226586Sdimtypedef union pps_timeu { /* timestamp format */ 141226586Sdim struct timespec tspec; 142226586Sdim ntp_fp_t ntpfp; 143226586Sdim unsigned long longpad[3]; 144226586Sdim} pps_timeu_t; /* generic data type to represent time stamps */ 145226586Sdim 146226586Sdim/* 147226586Sdim * Timestamp information structure 148226586Sdim */ 149226586Sdim 150226586Sdimtypedef struct pps_info { 151226586Sdim pps_seq_t assert_sequence; /* seq. num. of assert event */ 152226586Sdim pps_seq_t clear_sequence; /* seq. num. of clear event */ 153226586Sdim pps_timeu_t assert_tu; /* time of assert event */ 154226586Sdim pps_timeu_t clear_tu; /* time of clear event */ 155226586Sdim int current_mode; /* current mode bits */ 156226586Sdim} pps_info_t; 157234353Sdim 158226586Sdim#define assert_timestamp assert_tu.tspec 159226586Sdim#define clear_timestamp clear_tu.tspec 160226586Sdim 161276479Sdim#define assert_timestamp_ntpfp assert_tu.ntpfp 162243830Sdim#define clear_timestamp_ntpfp clear_tu.ntpfp 163226586Sdim 164226586Sdim/* 165226586Sdim * Parameter structure 166226586Sdim */ 167226586Sdim 168226586Sdimtypedef struct pps_params { 169226586Sdim int api_version; /* API version # */ 170226586Sdim int mode; /* mode bits */ 171226586Sdim pps_timeu_t assert_off_tu; /* offset compensation for assert */ 172226586Sdim pps_timeu_t clear_off_tu; /* offset compensation for clear */ 173226586Sdim} pps_params_t; 174239462Sdim 175234353Sdim#define assert_offset assert_off_tu.tspec 176226586Sdim#define clear_offset clear_off_tu.tspec 177234353Sdim 178226586Sdim#define assert_offset_ntpfp assert_off_tu.ntpfp 179226586Sdim#define clear_offset_ntpfp clear_off_tu.ntpfp 180226586Sdim 181226586Sdim/* 182226586Sdim * The following definitions are architecture-dependent 183226586Sdim */ 184226586Sdim 185226586Sdim#define PPS_CAP (PPS_CAPTUREASSERT | PPS_OFFSETASSERT | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP) 186226586Sdim#define PPS_RO (PPS_CANWAIT | PPS_CANPOLL | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP) 187226586Sdim 188226586Sdimtypedef struct { 189226586Sdim int filedes; /* file descriptor */ 190226586Sdim pps_params_t params; /* PPS parameters set by user */ 191239462Sdim} pps_unit_t; 192239462Sdim 193226586Sdimtypedef pps_unit_t* pps_handle_t; /* pps handlebars */ 194261991Sdim 195261991Sdim/* 196226586Sdim *------ Here begins the implementation-specific part! ------ 197239462Sdim */ 198239462Sdim 199239462Sdim#include <errno.h> 200239462Sdim 201239462Sdim/* 202239462Sdim * create PPS handle from file descriptor 203239462Sdim */ 204239462Sdim 205239462Sdimstatic inline int 206239462Sdimtime_pps_create( 207239462Sdim int filedes, /* file descriptor */ 208239462Sdim pps_handle_t *handle /* returned handle */ 209239462Sdim ) 210239462Sdim{ 211239462Sdim int one = 1; 212239462Sdim 213239462Sdim /* 214239462Sdim * Check for valid arguments and attach PPS signal. 215239462Sdim */ 216239462Sdim 217226586Sdim if (!handle) { 218243830Sdim errno = EFAULT; 219234353Sdim return (-1); /* null pointer */ 220239462Sdim } 221276479Sdim 222276479Sdim if (ioctl(filedes, TIOCSPPS, &one) < 0) { 223226586Sdim perror("refclock_ioctl: TIOCSPPS failed:"); 224226586Sdim return (-1); 225226586Sdim } 226226586Sdim 227226586Sdim /* 228226586Sdim * Allocate and initialize default unit structure. 229226586Sdim */ 230226586Sdim 231226586Sdim *handle = malloc(sizeof(pps_unit_t)); 232226586Sdim if (!(*handle)) { 233234353Sdim errno = EBADF; 234226586Sdim return (-1); /* what, no memory? */ 235226586Sdim } 236226586Sdim 237234353Sdim memset(*handle, 0, sizeof(pps_unit_t)); 238234353Sdim (*handle)->filedes = filedes; 239234353Sdim (*handle)->params.api_version = PPS_API_VERS_1; 240226586Sdim (*handle)->params.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC; 241226586Sdim return (0); 242226586Sdim} 243226586Sdim 244226586Sdim/* 245226586Sdim * release PPS handle 246226586Sdim */ 247226586Sdim 248226586Sdimstatic inline int 249226586Sdimtime_pps_destroy( 250226586Sdim pps_handle_t handle 251243830Sdim ) 252226586Sdim{ 253226586Sdim /* 254226586Sdim * Check for valid arguments and detach PPS signal. 255226586Sdim */ 256243830Sdim 257243830Sdim if (!handle) { 258243830Sdim errno = EBADF; 259226586Sdim return (-1); /* bad handle */ 260226586Sdim } 261226586Sdim free(handle); 262226586Sdim return (0); 263226586Sdim} 264226586Sdim 265226586Sdim/* 266226586Sdim * set parameters for handle 267226586Sdim */ 268226586Sdim 269226586Sdimstatic inline int 270234353Sdimtime_pps_setparams( 271234353Sdim pps_handle_t handle, 272234353Sdim const pps_params_t *params 273234353Sdim ) 274234353Sdim{ 275234353Sdim int mode, mode_in; 276226586Sdim /* 277226586Sdim * Check for valid arguments and set parameters. 278243830Sdim */ 279243830Sdim 280243830Sdim if (!handle) { 281243830Sdim errno = EBADF; 282226586Sdim return (-1); /* bad handle */ 283234353Sdim } 284234353Sdim 285234353Sdim if (!params) { 286234353Sdim errno = EFAULT; 287234353Sdim return (-1); /* bad argument */ 288226586Sdim } 289226586Sdim 290243830Sdim /* 291243830Sdim * There was no reasonable consensu in the API working group. 292243830Sdim * I require `api_version' to be set! 293226586Sdim */ 294226586Sdim 295226586Sdim if (params->api_version != PPS_API_VERS_1) { 296276479Sdim errno = EINVAL; 297226586Sdim return(-1); 298226586Sdim } 299226586Sdim 300226586Sdim /* 301226586Sdim * only settable modes are PPS_CAPTUREASSERT and PPS_OFFSETASSERT 302226586Sdim */ 303226586Sdim 304226586Sdim mode_in = params->mode; 305226586Sdim 306226586Sdim /* turn off read-only bits */ 307226586Sdim 308226586Sdim mode_in &= ~PPS_RO; 309226586Sdim 310226586Sdim /* test remaining bits, should only have captureassert and/or offsetassert */ 311226586Sdim 312226586Sdim if (mode_in & ~(PPS_CAPTUREASSERT | PPS_OFFSETASSERT)) { 313226586Sdim errno = EOPNOTSUPP; 314226586Sdim return(-1); 315226586Sdim } 316226586Sdim 317226586Sdim /* 318226586Sdim * ok, ready to go. 319226586Sdim */ 320249423Sdim 321261991Sdim mode = handle->params.mode; 322261991Sdim memcpy(&handle->params, params, sizeof(pps_params_t)); 323226586Sdim handle->params.api_version = PPS_API_VERS_1; 324234353Sdim handle->params.mode = mode | mode_in; 325226586Sdim return (0); 326234353Sdim} 327234353Sdim 328226586Sdim/* 329226586Sdim * get parameters for handle 330226586Sdim */ 331226586Sdim 332226586Sdimstatic inline int 333234353Sdimtime_pps_getparams( 334239462Sdim pps_handle_t handle, 335234353Sdim pps_params_t *params 336234353Sdim ) 337226586Sdim{ 338226586Sdim /* 339234353Sdim * Check for valid arguments and get parameters. 340234353Sdim */ 341234353Sdim 342234353Sdim if (!handle) { 343234353Sdim errno = EBADF; 344234353Sdim return (-1); /* bad handle */ 345239462Sdim } 346234353Sdim 347234353Sdim if (!params) { 348234353Sdim errno = EFAULT; 349234353Sdim return (-1); /* bad argument */ 350234353Sdim } 351234353Sdim 352234353Sdim memcpy(params, &handle->params, sizeof(pps_params_t)); 353234353Sdim return (0); 354234353Sdim} 355234353Sdim 356234353Sdim/* ( 357234353Sdim * get capabilities for handle 358234353Sdim */ 359234353Sdim 360234353Sdimstatic inline int 361243830Sdimtime_pps_getcap( 362234353Sdim pps_handle_t handle, 363234353Sdim int *mode 364234353Sdim ) 365234353Sdim{ 366234353Sdim /* 367234353Sdim * Check for valid arguments and get capabilities. 368234353Sdim */ 369234353Sdim 370243830Sdim if (!handle) { 371276479Sdim errno = EBADF; 372243830Sdim return (-1); /* bad handle */ 373234353Sdim } 374234353Sdim 375234353Sdim if (!mode) { 376234353Sdim errno = EFAULT; 377234353Sdim return (-1); /* bad argument */ 378234353Sdim } 379234353Sdim *mode = PPS_CAP; 380234353Sdim return (0); 381243830Sdim} 382243830Sdim 383261991Sdim/* 384243830Sdim * Fetch timestamps 385243830Sdim */ 386243830Sdim 387243830Sdimstatic inline int 388234353Sdimtime_pps_fetch( 389226586Sdim pps_handle_t handle, 390226586Sdim const int tsformat, 391226586Sdim pps_info_t *ppsinfo, 392226586Sdim const struct timespec *timeout 393234353Sdim ) 394261991Sdim{ 395226586Sdim struct ppsclockev { 396226586Sdim struct timeval tv; 397239462Sdim u_int serial; 398226586Sdim } ev; 399276479Sdim 400243830Sdim pps_info_t infobuf; 401243830Sdim 402243830Sdim /* 403234353Sdim * Check for valid arguments and fetch timestamps 404226586Sdim */ 405226586Sdim 406226586Sdim if (!handle) { 407226586Sdim errno = EBADF; 408226586Sdim return (-1); /* bad handle */ 409226586Sdim } 410226586Sdim 411226586Sdim if (!ppsinfo) { 412226586Sdim errno = EFAULT; 413243830Sdim return (-1); /* bad argument */ 414234353Sdim } 415251662Sdim 416251662Sdim /* 417251662Sdim * nb. PPS_CANWAIT is NOT set by the implementation, we can totally 418251662Sdim * ignore the timeout variable. 419251662Sdim */ 420226586Sdim 421251662Sdim memset(&infobuf, 0, sizeof(infobuf)); 422251662Sdim 423251662Sdim /* 424251662Sdim * if not captureassert, nothing to return. 425251662Sdim */ 426251662Sdim 427239462Sdim if (!handle->params.mode & PPS_CAPTUREASSERT) { 428251662Sdim memcpy(ppsinfo, &infobuf, sizeof(pps_info_t)); 429251662Sdim return (0); 430251662Sdim } 431251662Sdim 432251662Sdim if (ioctl(handle->filedes, TIOCGPPSEV, (caddr_t) &ev) < 0) { 433251662Sdim perror("time_pps_fetch:"); 434251662Sdim errno = EOPNOTSUPP; 435251662Sdim return(-1); 436251662Sdim } 437251662Sdim 438251662Sdim /* 439251662Sdim * Apply offsets as specified. Note that only assert timestamps 440251662Sdim * are captured by this interface. 441226586Sdim */ 442226586Sdim 443226586Sdim infobuf.assert_sequence = ev.serial; 444226586Sdim infobuf.assert_timestamp.tv_sec = ev.tv.tv_sec; 445226586Sdim infobuf.assert_timestamp.tv_nsec = ev.tv.tv_usec * 1000; 446249423Sdim 447249423Sdim if (handle->params.mode & PPS_OFFSETASSERT) { 448249423Sdim infobuf.assert_timestamp.tv_sec += handle->params.assert_offset.tv_sec; 449234353Sdim infobuf.assert_timestamp.tv_nsec += handle->params.assert_offset.tv_nsec; 450234353Sdim PPS_NORMALIZE(infobuf.assert_timestamp); 451226586Sdim } 452234353Sdim 453226586Sdim /* 454226586Sdim * Translate to specified format 455226586Sdim */ 456226586Sdim 457226586Sdim switch (tsformat) { 458261991Sdim case PPS_TSFMT_TSPEC: 459261991Sdim break; /* timespec format requires no translation */ 460226586Sdim 461226586Sdim case PPS_TSFMT_NTPFP: /* NTP format requires conversion to fraction form */ 462226586Sdim PPS_TSPECTONTP(infobuf.assert_timestamp_ntpfp); 463234353Sdim break; 464249423Sdim 465249423Sdim default: 466226586Sdim errno = EINVAL; 467226586Sdim return (-1); 468249423Sdim } 469249423Sdim 470249423Sdim infobuf.current_mode = handle->params.mode; 471239462Sdim memcpy(ppsinfo, &infobuf, sizeof(pps_info_t)); 472234353Sdim return (0); 473249423Sdim} 474239462Sdim 475239462Sdim/* 476249423Sdim * specify kernel consumer 477239462Sdim */ 478239462Sdim 479239462Sdimstatic inline int 480249423Sdimtime_pps_kcbind( 481249423Sdim pps_handle_t handle, 482249423Sdim const int kernel_consumer, 483249423Sdim const int edge, const int tsformat 484249423Sdim ) 485249423Sdim{ 486249423Sdim /* 487239462Sdim * Check for valid arguments and bind kernel consumer 488239462Sdim */ 489239462Sdim if (!handle) { 490239462Sdim errno = EBADF; 491239462Sdim return (-1); /* bad handle */ 492239462Sdim } 493239462Sdim if (geteuid() != 0) { 494239462Sdim errno = EPERM; 495239462Sdim return (-1); /* must be superuser */ 496239462Sdim } 497276479Sdim errno = EOPNOTSUPP; 498243830Sdim return(-1); 499239462Sdim} 500249423Sdim 501249423Sdim#endif /* _SYS_TIMEPPS_H_ */ 502249423Sdim