1181834Sroberto/*********************************************************************** 2181834Sroberto * * 3181834Sroberto * Copyright (c) David L. Mills 1999-2000 * 4181834Sroberto * * 5181834Sroberto * Permission to use, copy, modify, and distribute this software and * 6280849Scy * its documentation for any purpose and with or without fee is hereby * 7181834Sroberto * granted, provided that the above copyright notice appears in all * 8181834Sroberto * copies and that both the copyright notice and this permission * 9181834Sroberto * notice appear in supporting documentation, and that the name * 10181834Sroberto * University of Delaware not be used in advertising or publicity * 11181834Sroberto * pertaining to distribution of the software without specific, * 12181834Sroberto * written prior permission. The University of Delaware makes no * 13181834Sroberto * representations about the suitability this software for any * 14181834Sroberto * purpose. It is provided "as is" without express or implied * 15181834Sroberto * warranty. * 16181834Sroberto * * 17181834Sroberto *********************************************************************** 18181834Sroberto * * 19181834Sroberto * This header file complies with "Pulse-Per-Second API for UNIX-like * 20181834Sroberto * Operating Systems, Version 1.0", rfc2783. Credit is due Jeff Mogul * 21181834Sroberto * and Marc Brett, from whom much of this code was shamelessly stolen. * 22181834Sroberto * * 23181834Sroberto * this modified timepps.h can be used to provide a PPSAPI interface * 24181834Sroberto * to a machine running SCO Unix. * 25181834Sroberto * * 26181834Sroberto *********************************************************************** 27181834Sroberto * * 28181834Sroberto * A full PPSAPI interface to the SCO Unix kernel would be better, but * 29181834Sroberto * this at least removes the necessity for special coding from the NTP * 30181834Sroberto * NTP drivers. * 31181834Sroberto * * 32181834Sroberto *********************************************************************** 33181834Sroberto * * 34181834Sroberto * Some of this include file * 35181834Sroberto * Copyright (c) 1999 by Ulrich Windl, * 36181834Sroberto * based on code by Reg Clemens <reg@dwf.com> * 37181834Sroberto * based on code by Poul-Henning Kamp <phk@FreeBSD.org> * 38181834Sroberto * * 39181834Sroberto *********************************************************************** 40181834Sroberto * * 41181834Sroberto * "THE BEER-WARE LICENSE" (Revision 42): * 42181834Sroberto * <phk@FreeBSD.org> wrote this file. As long as you retain this * 43181834Sroberto * notice you can do whatever you want with this stuff. If we meet some* 44181834Sroberto * day, and you think this stuff is worth it, you can buy me a beer * 45181834Sroberto * in return. Poul-Henning Kamp * 46181834Sroberto * * 47181834Sroberto **********************************************************************/ 48181834Sroberto 49181834Sroberto/*SCO UNIX version, TIOCDCDTIMESTAMP assumed to exist. */ 50181834Sroberto 51181834Sroberto#ifndef _SYS_TIMEPPS_H_ 52181834Sroberto#define _SYS_TIMEPPS_H_ 53181834Sroberto 54181834Sroberto#include <termios.h> /* to get TIOCDCDTIMESTAMP */ 55181834Sroberto 56181834Sroberto/* Implementation note: the logical states ``assert'' and ``clear'' 57181834Sroberto * are implemented in terms of the UART register, i.e. ``assert'' 58181834Sroberto * means the bit is set. 59181834Sroberto */ 60181834Sroberto 61181834Sroberto/* 62181834Sroberto * The following definitions are architecture independent 63181834Sroberto */ 64181834Sroberto 65181834Sroberto#define PPS_API_VERS_1 1 /* API version number */ 66181834Sroberto#define PPS_JAN_1970 2208988800UL /* 1970 - 1900 in seconds */ 67181834Sroberto#define PPS_NANOSECOND 1000000000L /* one nanosecond in decimal */ 68181834Sroberto#define PPS_FRAC 4294967296. /* 2^32 as a double */ 69181834Sroberto 70181834Sroberto#define PPS_NORMALIZE(x) /* normalize timespec */ \ 71181834Sroberto do { \ 72181834Sroberto if ((x).tv_nsec >= PPS_NANOSECOND) { \ 73181834Sroberto (x).tv_nsec -= PPS_NANOSECOND; \ 74181834Sroberto (x).tv_sec++; \ 75181834Sroberto } else if ((x).tv_nsec < 0) { \ 76181834Sroberto (x).tv_nsec += PPS_NANOSECOND; \ 77181834Sroberto (x).tv_sec--; \ 78181834Sroberto } \ 79181834Sroberto } while (0) 80181834Sroberto 81181834Sroberto#define PPS_TSPECTONTP(x) /* convert timespec to l_fp */ \ 82181834Sroberto do { \ 83181834Sroberto double d_temp; \ 84181834Sroberto \ 85181834Sroberto (x).integral += (unsigned int)PPS_JAN_1970; \ 86181834Sroberto d_temp = (x).fractional * PPS_FRAC / PPS_NANOSECOND; \ 87181834Sroberto if (d_temp >= PPS_FRAC) \ 88181834Sroberto (x).integral++; \ 89181834Sroberto (x).fractional = (unsigned int)d_temp; \ 90181834Sroberto } while (0) 91181834Sroberto 92181834Sroberto/* 93181834Sroberto * Device/implementation parameters (mode) 94181834Sroberto */ 95181834Sroberto 96181834Sroberto#define PPS_CAPTUREASSERT 0x01 /* capture assert events */ 97181834Sroberto#define PPS_CAPTURECLEAR 0x02 /* capture clear events */ 98181834Sroberto#define PPS_CAPTUREBOTH 0x03 /* capture assert and clear events */ 99181834Sroberto 100181834Sroberto#define PPS_OFFSETASSERT 0x10 /* apply compensation for assert ev. */ 101181834Sroberto#define PPS_OFFSETCLEAR 0x20 /* apply compensation for clear ev. */ 102181834Sroberto#define PPS_OFFSETBOTH 0x30 /* apply compensation for both */ 103181834Sroberto 104181834Sroberto#define PPS_CANWAIT 0x100 /* Can we wait for an event? */ 105181834Sroberto#define PPS_CANPOLL 0x200 /* "This bit is reserved for */ 106181834Sroberto 107181834Sroberto/* 108181834Sroberto * Kernel actions (mode) 109181834Sroberto */ 110181834Sroberto 111181834Sroberto#define PPS_ECHOASSERT 0x40 /* feed back assert event to output */ 112181834Sroberto#define PPS_ECHOCLEAR 0x80 /* feed back clear event to output */ 113181834Sroberto 114181834Sroberto/* 115181834Sroberto * Timestamp formats (tsformat) 116181834Sroberto */ 117181834Sroberto 118181834Sroberto#define PPS_TSFMT_TSPEC 0x1000 /* select timespec format */ 119181834Sroberto#define PPS_TSFMT_NTPFP 0x2000 /* select NTP format */ 120181834Sroberto 121181834Sroberto/* 122181834Sroberto * Kernel discipline actions (not used in Solaris) 123181834Sroberto */ 124181834Sroberto 125181834Sroberto#define PPS_KC_HARDPPS 0 /* enable kernel consumer */ 126181834Sroberto#define PPS_KC_HARDPPS_PLL 1 /* phase-lock mode */ 127181834Sroberto#define PPS_KC_HARDPPS_FLL 2 /* frequency-lock mode */ 128181834Sroberto 129181834Sroberto/* 130181834Sroberto * Type definitions 131181834Sroberto */ 132181834Sroberto 133181834Srobertotypedef unsigned long pps_seq_t; /* sequence number */ 134181834Sroberto 135181834Srobertotypedef struct ntp_fp { 136181834Sroberto unsigned int integral; 137181834Sroberto unsigned int fractional; 138181834Sroberto} ntp_fp_t; /* NTP-compatible time stamp */ 139181834Sroberto 140181834Srobertotypedef union pps_timeu { /* timestamp format */ 141181834Sroberto struct timespec tspec; 142181834Sroberto ntp_fp_t ntpfp; 143181834Sroberto unsigned long longpad[3]; 144181834Sroberto} pps_timeu_t; /* generic data type to represent time stamps */ 145181834Sroberto 146181834Sroberto/* 147181834Sroberto * Timestamp information structure 148181834Sroberto */ 149181834Sroberto 150181834Srobertotypedef struct pps_info { 151181834Sroberto pps_seq_t assert_sequence; /* seq. num. of assert event */ 152181834Sroberto pps_seq_t clear_sequence; /* seq. num. of clear event */ 153181834Sroberto pps_timeu_t assert_tu; /* time of assert event */ 154181834Sroberto pps_timeu_t clear_tu; /* time of clear event */ 155181834Sroberto int current_mode; /* current mode bits */ 156181834Sroberto} pps_info_t; 157181834Sroberto 158181834Sroberto#define assert_timestamp assert_tu.tspec 159181834Sroberto#define clear_timestamp clear_tu.tspec 160181834Sroberto 161181834Sroberto#define assert_timestamp_ntpfp assert_tu.ntpfp 162181834Sroberto#define clear_timestamp_ntpfp clear_tu.ntpfp 163181834Sroberto 164181834Sroberto/* 165181834Sroberto * Parameter structure 166181834Sroberto */ 167181834Sroberto 168181834Srobertotypedef struct pps_params { 169181834Sroberto int api_version; /* API version # */ 170181834Sroberto int mode; /* mode bits */ 171181834Sroberto pps_timeu_t assert_off_tu; /* offset compensation for assert */ 172181834Sroberto pps_timeu_t clear_off_tu; /* offset compensation for clear */ 173181834Sroberto} pps_params_t; 174181834Sroberto 175181834Sroberto#define assert_offset assert_off_tu.tspec 176181834Sroberto#define clear_offset clear_off_tu.tspec 177181834Sroberto 178181834Sroberto#define assert_offset_ntpfp assert_off_tu.ntpfp 179181834Sroberto#define clear_offset_ntpfp clear_off_tu.ntpfp 180181834Sroberto 181181834Sroberto/* 182181834Sroberto * The following definitions are architecture-dependent 183181834Sroberto */ 184181834Sroberto 185181834Sroberto#define PPS_CAP (PPS_CAPTUREASSERT | PPS_OFFSETASSERT | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP) 186181834Sroberto#define PPS_RO (PPS_CANWAIT | PPS_CANPOLL | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP) 187181834Sroberto 188181834Srobertotypedef struct { 189181834Sroberto int filedes; /* file descriptor */ 190181834Sroberto pps_params_t params; /* PPS parameters set by user */ 191181834Sroberto struct timeval tv_save; 192181834Sroberto pps_seq_t serial; 193181834Sroberto} pps_unit_t; 194181834Sroberto 195181834Srobertotypedef pps_unit_t* pps_handle_t; /* pps handlebars */ 196181834Sroberto 197181834Sroberto/* 198181834Sroberto *------ Here begins the implementation-specific part! ------ 199181834Sroberto */ 200181834Sroberto 201181834Sroberto#include <errno.h> 202181834Sroberto 203181834Sroberto/* 204181834Sroberto * create PPS handle from file descriptor 205181834Sroberto */ 206181834Sroberto 207181834Srobertostatic inline int 208181834Srobertotime_pps_create( 209181834Sroberto int filedes, /* file descriptor */ 210181834Sroberto pps_handle_t *handle /* returned handle */ 211181834Sroberto ) 212181834Sroberto{ 213181834Sroberto int one = 1; 214181834Sroberto 215181834Sroberto /* 216181834Sroberto * Check for valid arguments and attach PPS signal. 217181834Sroberto */ 218181834Sroberto 219181834Sroberto if (!handle) { 220181834Sroberto errno = EFAULT; 221181834Sroberto return (-1); /* null pointer */ 222181834Sroberto } 223181834Sroberto 224181834Sroberto /* 225181834Sroberto * Allocate and initialize default unit structure. 226181834Sroberto */ 227181834Sroberto 228181834Sroberto *handle = malloc(sizeof(pps_unit_t)); 229181834Sroberto if (!(*handle)) { 230181834Sroberto errno = EBADF; 231181834Sroberto return (-1); /* what, no memory? */ 232181834Sroberto } 233181834Sroberto 234181834Sroberto memset(*handle, 0, sizeof(pps_unit_t)); 235181834Sroberto (*handle)->filedes = filedes; 236181834Sroberto (*handle)->params.api_version = PPS_API_VERS_1; 237181834Sroberto (*handle)->params.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC; 238181834Sroberto return (0); 239181834Sroberto} 240181834Sroberto 241181834Sroberto/* 242181834Sroberto * release PPS handle 243181834Sroberto */ 244181834Sroberto 245181834Srobertostatic inline int 246181834Srobertotime_pps_destroy( 247181834Sroberto pps_handle_t handle 248181834Sroberto ) 249181834Sroberto{ 250181834Sroberto /* 251181834Sroberto * Check for valid arguments and detach PPS signal. 252181834Sroberto */ 253181834Sroberto 254181834Sroberto if (!handle) { 255181834Sroberto errno = EBADF; 256181834Sroberto return (-1); /* bad handle */ 257181834Sroberto } 258181834Sroberto free(handle); 259181834Sroberto return (0); 260181834Sroberto} 261181834Sroberto 262181834Sroberto/* 263181834Sroberto * set parameters for handle 264181834Sroberto */ 265181834Sroberto 266181834Srobertostatic inline int 267181834Srobertotime_pps_setparams( 268181834Sroberto pps_handle_t handle, 269181834Sroberto const pps_params_t *params 270181834Sroberto ) 271181834Sroberto{ 272181834Sroberto int mode, mode_in; 273181834Sroberto /* 274181834Sroberto * Check for valid arguments and set parameters. 275181834Sroberto */ 276181834Sroberto 277181834Sroberto if (!handle) { 278181834Sroberto errno = EBADF; 279181834Sroberto return (-1); /* bad handle */ 280181834Sroberto } 281181834Sroberto 282181834Sroberto if (!params) { 283181834Sroberto errno = EFAULT; 284181834Sroberto return (-1); /* bad argument */ 285181834Sroberto } 286181834Sroberto 287181834Sroberto /* 288181834Sroberto * There was no reasonable consensu in the API working group. 289181834Sroberto * I require `api_version' to be set! 290181834Sroberto */ 291181834Sroberto 292181834Sroberto if (params->api_version != PPS_API_VERS_1) { 293181834Sroberto errno = EINVAL; 294181834Sroberto return(-1); 295181834Sroberto } 296181834Sroberto 297181834Sroberto /* 298181834Sroberto * only settable modes are PPS_CAPTUREASSERT and PPS_OFFSETASSERT 299181834Sroberto */ 300181834Sroberto 301181834Sroberto mode_in = params->mode; 302181834Sroberto 303181834Sroberto /* turn off read-only bits */ 304181834Sroberto 305181834Sroberto mode_in &= ~PPS_RO; 306181834Sroberto 307181834Sroberto /* test remaining bits, should only have captureassert and/or offsetassert */ 308181834Sroberto 309181834Sroberto if (mode_in & ~(PPS_CAPTUREASSERT | PPS_OFFSETASSERT)) { 310181834Sroberto errno = EOPNOTSUPP; 311181834Sroberto return(-1); 312181834Sroberto } 313181834Sroberto 314181834Sroberto /* 315181834Sroberto * ok, ready to go. 316181834Sroberto */ 317181834Sroberto 318181834Sroberto mode = handle->params.mode; 319181834Sroberto memcpy(&handle->params, params, sizeof(pps_params_t)); 320181834Sroberto handle->params.api_version = PPS_API_VERS_1; 321181834Sroberto handle->params.mode = mode | mode_in; 322181834Sroberto return (0); 323181834Sroberto} 324181834Sroberto 325181834Sroberto/* 326181834Sroberto * get parameters for handle 327181834Sroberto */ 328181834Sroberto 329181834Srobertostatic inline int 330181834Srobertotime_pps_getparams( 331181834Sroberto pps_handle_t handle, 332181834Sroberto pps_params_t *params 333181834Sroberto ) 334181834Sroberto{ 335181834Sroberto /* 336181834Sroberto * Check for valid arguments and get parameters. 337181834Sroberto */ 338181834Sroberto 339181834Sroberto if (!handle) { 340181834Sroberto errno = EBADF; 341181834Sroberto return (-1); /* bad handle */ 342181834Sroberto } 343181834Sroberto 344181834Sroberto if (!params) { 345181834Sroberto errno = EFAULT; 346181834Sroberto return (-1); /* bad argument */ 347181834Sroberto } 348181834Sroberto 349181834Sroberto memcpy(params, &handle->params, sizeof(pps_params_t)); 350181834Sroberto return (0); 351181834Sroberto} 352181834Sroberto 353181834Sroberto/* ( 354181834Sroberto * get capabilities for handle 355181834Sroberto */ 356181834Sroberto 357181834Srobertostatic inline int 358181834Srobertotime_pps_getcap( 359181834Sroberto pps_handle_t handle, 360181834Sroberto int *mode 361181834Sroberto ) 362181834Sroberto{ 363181834Sroberto /* 364181834Sroberto * Check for valid arguments and get capabilities. 365181834Sroberto */ 366181834Sroberto 367181834Sroberto if (!handle) { 368181834Sroberto errno = EBADF; 369181834Sroberto return (-1); /* bad handle */ 370181834Sroberto } 371181834Sroberto 372181834Sroberto if (!mode) { 373181834Sroberto errno = EFAULT; 374181834Sroberto return (-1); /* bad argument */ 375181834Sroberto } 376181834Sroberto *mode = PPS_CAP; 377181834Sroberto return (0); 378181834Sroberto} 379181834Sroberto 380181834Sroberto/* 381181834Sroberto * Fetch timestamps 382181834Sroberto */ 383181834Sroberto 384181834Srobertostatic inline int 385181834Srobertotime_pps_fetch( 386181834Sroberto pps_handle_t handle, 387181834Sroberto const int tsformat, 388181834Sroberto pps_info_t *ppsinfo, 389181834Sroberto const struct timespec *timeout 390181834Sroberto ) 391181834Sroberto{ 392181834Sroberto struct timeval tv; 393181834Sroberto pps_info_t infobuf; 394181834Sroberto 395181834Sroberto /* 396181834Sroberto * Check for valid arguments and fetch timestamps 397181834Sroberto */ 398181834Sroberto 399181834Sroberto if (!handle) { 400181834Sroberto errno = EBADF; 401181834Sroberto return (-1); /* bad handle */ 402181834Sroberto } 403181834Sroberto 404181834Sroberto if (!ppsinfo) { 405181834Sroberto errno = EFAULT; 406181834Sroberto return (-1); /* bad argument */ 407181834Sroberto } 408181834Sroberto 409181834Sroberto /* 410181834Sroberto * nb. PPS_CANWAIT is NOT set by the implementation, we can totally 411181834Sroberto * ignore the timeout variable. 412181834Sroberto */ 413181834Sroberto 414181834Sroberto memset(&infobuf, 0, sizeof(infobuf)); 415181834Sroberto 416181834Sroberto /* 417181834Sroberto * if not captureassert, nothing to return. 418181834Sroberto */ 419181834Sroberto 420181834Sroberto if (!handle->params.mode & PPS_CAPTUREASSERT) { 421181834Sroberto memcpy(ppsinfo, &infobuf, sizeof(pps_info_t)); 422181834Sroberto return (0); 423181834Sroberto } 424181834Sroberto 425181834Sroberto if (ioctl(instance->filedes, TIOCDCDTIMESTAMP, &tv) < 0) { 426181834Sroberto perror("time_pps_fetch:"); 427181834Sroberto errno = EOPNOTSUPP; 428181834Sroberto return(-1); 429181834Sroberto } 430181834Sroberto 431181834Sroberto /* 432181834Sroberto * fake serial here 433181834Sroberto */ 434181834Sroberto 435181834Sroberto if (tv.tv_sec != handle->tv_save.tv_sec || tv.tv_usec != handle->tv_save.tv_usec) { 436181834Sroberto handle->tv_save = tv; 437181834Sroberto handle->serial++; 438181834Sroberto } 439181834Sroberto 440181834Sroberto /* 441181834Sroberto * Apply offsets as specified. Note that only assert timestamps 442181834Sroberto * are captured by this interface. 443181834Sroberto */ 444181834Sroberto 445181834Sroberto infobuf.assert_sequence = handle->serial; 446181834Sroberto infobuf.assert_timestamp.tv_sec = tv.tv_sec; 447181834Sroberto infobuf.assert_timestamp.tv_nsec = tv.tv_usec * 1000; 448181834Sroberto 449181834Sroberto if (handle->params.mode & PPS_OFFSETASSERT) { 450181834Sroberto infobuf.assert_timestamp.tv_sec += handle->params.assert_offset.tv_sec; 451181834Sroberto infobuf.assert_timestamp.tv_nsec += handle->params.assert_offset.tv_nsec; 452181834Sroberto PPS_NORMALIZE(infobuf.assert_timestamp); 453181834Sroberto } 454181834Sroberto 455181834Sroberto /* 456181834Sroberto * Translate to specified format 457181834Sroberto */ 458181834Sroberto 459181834Sroberto switch (tsformat) { 460181834Sroberto case PPS_TSFMT_TSPEC: 461181834Sroberto break; /* timespec format requires no translation */ 462181834Sroberto 463181834Sroberto case PPS_TSFMT_NTPFP: /* NTP format requires conversion to fraction form */ 464181834Sroberto PPS_TSPECTONTP(infobuf.assert_timestamp_ntpfp); 465181834Sroberto break; 466181834Sroberto 467181834Sroberto default: 468181834Sroberto errno = EINVAL; 469181834Sroberto return (-1); 470181834Sroberto } 471181834Sroberto 472181834Sroberto infobuf.current_mode = handle->params.mode; 473181834Sroberto memcpy(ppsinfo, &infobuf, sizeof(pps_info_t)); 474181834Sroberto return (0); 475181834Sroberto} 476181834Sroberto 477181834Sroberto/* 478181834Sroberto * specify kernel consumer 479181834Sroberto */ 480181834Sroberto 481181834Srobertostatic inline int 482181834Srobertotime_pps_kcbind( 483181834Sroberto pps_handle_t handle, 484181834Sroberto const int kernel_consumer, 485181834Sroberto const int edge, const int tsformat 486181834Sroberto ) 487181834Sroberto{ 488181834Sroberto /* 489181834Sroberto * Check for valid arguments and bind kernel consumer 490181834Sroberto */ 491181834Sroberto if (!handle) { 492181834Sroberto errno = EBADF; 493181834Sroberto return (-1); /* bad handle */ 494181834Sroberto } 495181834Sroberto if (geteuid() != 0) { 496181834Sroberto errno = EPERM; 497181834Sroberto return (-1); /* must be superuser */ 498181834Sroberto } 499181834Sroberto errno = EOPNOTSUPP; 500181834Sroberto return(-1); 501181834Sroberto} 502181834Sroberto 503181834Sroberto#endif /* _SYS_TIMEPPS_H_ */ 504