/*********************************************************************** * * * Copyright (c) David L. Mills 1999-2000 * * * * Permission to use, copy, modify, and distribute this software and * * its documentation for any purpose and without fee is hereby * * granted, provided that the above copyright notice appears in all * * copies and that both the copyright notice and this permission * * notice appear in supporting documentation, and that the name * * University of Delaware not be used in advertising or publicity * * pertaining to distribution of the software without specific, * * written prior permission. The University of Delaware makes no * * representations about the suitability this software for any * * purpose. It is provided "as is" without express or implied * * warranty. * * * *********************************************************************** * * * This header file complies with "Pulse-Per-Second API for UNIX-like * * Operating Systems, Version 1.0", rfc2783. Credit is due Jeff Mogul * * and Marc Brett, from whom much of this code was shamelessly stolen. * * * * this modified timepps.h can be used to provide a PPSAPI interface * * to a machine running SCO Unix. * * * *********************************************************************** * * * A full PPSAPI interface to the SCO Unix kernel would be better, but * * this at least removes the necessity for special coding from the NTP * * NTP drivers. * * * *********************************************************************** * * * Some of this include file * * Copyright (c) 1999 by Ulrich Windl, * * based on code by Reg Clemens * * based on code by Poul-Henning Kamp * * * *********************************************************************** * * * "THE BEER-WARE LICENSE" (Revision 42): * * wrote this file. As long as you retain this * * notice you can do whatever you want with this stuff. If we meet some* * day, and you think this stuff is worth it, you can buy me a beer * * in return. Poul-Henning Kamp * * * **********************************************************************/ /*SCO UNIX version, TIOCDCDTIMESTAMP assumed to exist. */ #ifndef _SYS_TIMEPPS_H_ #define _SYS_TIMEPPS_H_ #include /* to get TIOCDCDTIMESTAMP */ /* Implementation note: the logical states ``assert'' and ``clear'' * are implemented in terms of the UART register, i.e. ``assert'' * means the bit is set. */ /* * The following definitions are architecture independent */ #define PPS_API_VERS_1 1 /* API version number */ #define PPS_JAN_1970 2208988800UL /* 1970 - 1900 in seconds */ #define PPS_NANOSECOND 1000000000L /* one nanosecond in decimal */ #define PPS_FRAC 4294967296. /* 2^32 as a double */ #define PPS_NORMALIZE(x) /* normalize timespec */ \ do { \ if ((x).tv_nsec >= PPS_NANOSECOND) { \ (x).tv_nsec -= PPS_NANOSECOND; \ (x).tv_sec++; \ } else if ((x).tv_nsec < 0) { \ (x).tv_nsec += PPS_NANOSECOND; \ (x).tv_sec--; \ } \ } while (0) #define PPS_TSPECTONTP(x) /* convert timespec to l_fp */ \ do { \ double d_temp; \ \ (x).integral += (unsigned int)PPS_JAN_1970; \ d_temp = (x).fractional * PPS_FRAC / PPS_NANOSECOND; \ if (d_temp >= PPS_FRAC) \ (x).integral++; \ (x).fractional = (unsigned int)d_temp; \ } while (0) /* * Device/implementation parameters (mode) */ #define PPS_CAPTUREASSERT 0x01 /* capture assert events */ #define PPS_CAPTURECLEAR 0x02 /* capture clear events */ #define PPS_CAPTUREBOTH 0x03 /* capture assert and clear events */ #define PPS_OFFSETASSERT 0x10 /* apply compensation for assert ev. */ #define PPS_OFFSETCLEAR 0x20 /* apply compensation for clear ev. */ #define PPS_OFFSETBOTH 0x30 /* apply compensation for both */ #define PPS_CANWAIT 0x100 /* Can we wait for an event? */ #define PPS_CANPOLL 0x200 /* "This bit is reserved for */ /* * Kernel actions (mode) */ #define PPS_ECHOASSERT 0x40 /* feed back assert event to output */ #define PPS_ECHOCLEAR 0x80 /* feed back clear event to output */ /* * Timestamp formats (tsformat) */ #define PPS_TSFMT_TSPEC 0x1000 /* select timespec format */ #define PPS_TSFMT_NTPFP 0x2000 /* select NTP format */ /* * Kernel discipline actions (not used in Solaris) */ #define PPS_KC_HARDPPS 0 /* enable kernel consumer */ #define PPS_KC_HARDPPS_PLL 1 /* phase-lock mode */ #define PPS_KC_HARDPPS_FLL 2 /* frequency-lock mode */ /* * Type definitions */ typedef unsigned long pps_seq_t; /* sequence number */ typedef struct ntp_fp { unsigned int integral; unsigned int fractional; } ntp_fp_t; /* NTP-compatible time stamp */ typedef union pps_timeu { /* timestamp format */ struct timespec tspec; ntp_fp_t ntpfp; unsigned long longpad[3]; } pps_timeu_t; /* generic data type to represent time stamps */ /* * Timestamp information structure */ typedef struct pps_info { pps_seq_t assert_sequence; /* seq. num. of assert event */ pps_seq_t clear_sequence; /* seq. num. of clear event */ pps_timeu_t assert_tu; /* time of assert event */ pps_timeu_t clear_tu; /* time of clear event */ int current_mode; /* current mode bits */ } pps_info_t; #define assert_timestamp assert_tu.tspec #define clear_timestamp clear_tu.tspec #define assert_timestamp_ntpfp assert_tu.ntpfp #define clear_timestamp_ntpfp clear_tu.ntpfp /* * Parameter structure */ typedef struct pps_params { int api_version; /* API version # */ int mode; /* mode bits */ pps_timeu_t assert_off_tu; /* offset compensation for assert */ pps_timeu_t clear_off_tu; /* offset compensation for clear */ } pps_params_t; #define assert_offset assert_off_tu.tspec #define clear_offset clear_off_tu.tspec #define assert_offset_ntpfp assert_off_tu.ntpfp #define clear_offset_ntpfp clear_off_tu.ntpfp /* * The following definitions are architecture-dependent */ #define PPS_CAP (PPS_CAPTUREASSERT | PPS_OFFSETASSERT | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP) #define PPS_RO (PPS_CANWAIT | PPS_CANPOLL | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP) typedef struct { int filedes; /* file descriptor */ pps_params_t params; /* PPS parameters set by user */ struct timeval tv_save; pps_seq_t serial; } pps_unit_t; typedef pps_unit_t* pps_handle_t; /* pps handlebars */ /* *------ Here begins the implementation-specific part! ------ */ #include /* * create PPS handle from file descriptor */ static inline int time_pps_create( int filedes, /* file descriptor */ pps_handle_t *handle /* returned handle */ ) { int one = 1; /* * Check for valid arguments and attach PPS signal. */ if (!handle) { errno = EFAULT; return (-1); /* null pointer */ } /* * Allocate and initialize default unit structure. */ *handle = malloc(sizeof(pps_unit_t)); if (!(*handle)) { errno = EBADF; return (-1); /* what, no memory? */ } memset(*handle, 0, sizeof(pps_unit_t)); (*handle)->filedes = filedes; (*handle)->params.api_version = PPS_API_VERS_1; (*handle)->params.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC; return (0); } /* * release PPS handle */ static inline int time_pps_destroy( pps_handle_t handle ) { /* * Check for valid arguments and detach PPS signal. */ if (!handle) { errno = EBADF; return (-1); /* bad handle */ } free(handle); return (0); } /* * set parameters for handle */ static inline int time_pps_setparams( pps_handle_t handle, const pps_params_t *params ) { int mode, mode_in; /* * Check for valid arguments and set parameters. */ if (!handle) { errno = EBADF; return (-1); /* bad handle */ } if (!params) { errno = EFAULT; return (-1); /* bad argument */ } /* * There was no reasonable consensu in the API working group. * I require `api_version' to be set! */ if (params->api_version != PPS_API_VERS_1) { errno = EINVAL; return(-1); } /* * only settable modes are PPS_CAPTUREASSERT and PPS_OFFSETASSERT */ mode_in = params->mode; /* turn off read-only bits */ mode_in &= ~PPS_RO; /* test remaining bits, should only have captureassert and/or offsetassert */ if (mode_in & ~(PPS_CAPTUREASSERT | PPS_OFFSETASSERT)) { errno = EOPNOTSUPP; return(-1); } /* * ok, ready to go. */ mode = handle->params.mode; memcpy(&handle->params, params, sizeof(pps_params_t)); handle->params.api_version = PPS_API_VERS_1; handle->params.mode = mode | mode_in; return (0); } /* * get parameters for handle */ static inline int time_pps_getparams( pps_handle_t handle, pps_params_t *params ) { /* * Check for valid arguments and get parameters. */ if (!handle) { errno = EBADF; return (-1); /* bad handle */ } if (!params) { errno = EFAULT; return (-1); /* bad argument */ } memcpy(params, &handle->params, sizeof(pps_params_t)); return (0); } /* ( * get capabilities for handle */ static inline int time_pps_getcap( pps_handle_t handle, int *mode ) { /* * Check for valid arguments and get capabilities. */ if (!handle) { errno = EBADF; return (-1); /* bad handle */ } if (!mode) { errno = EFAULT; return (-1); /* bad argument */ } *mode = PPS_CAP; return (0); } /* * Fetch timestamps */ static inline int time_pps_fetch( pps_handle_t handle, const int tsformat, pps_info_t *ppsinfo, const struct timespec *timeout ) { struct timeval tv; pps_info_t infobuf; /* * Check for valid arguments and fetch timestamps */ if (!handle) { errno = EBADF; return (-1); /* bad handle */ } if (!ppsinfo) { errno = EFAULT; return (-1); /* bad argument */ } /* * nb. PPS_CANWAIT is NOT set by the implementation, we can totally * ignore the timeout variable. */ memset(&infobuf, 0, sizeof(infobuf)); /* * if not captureassert, nothing to return. */ if (!handle->params.mode & PPS_CAPTUREASSERT) { memcpy(ppsinfo, &infobuf, sizeof(pps_info_t)); return (0); } if (ioctl(instance->filedes, TIOCDCDTIMESTAMP, &tv) < 0) { perror("time_pps_fetch:"); errno = EOPNOTSUPP; return(-1); } /* * fake serial here */ if (tv.tv_sec != handle->tv_save.tv_sec || tv.tv_usec != handle->tv_save.tv_usec) { handle->tv_save = tv; handle->serial++; } /* * Apply offsets as specified. Note that only assert timestamps * are captured by this interface. */ infobuf.assert_sequence = handle->serial; infobuf.assert_timestamp.tv_sec = tv.tv_sec; infobuf.assert_timestamp.tv_nsec = tv.tv_usec * 1000; if (handle->params.mode & PPS_OFFSETASSERT) { infobuf.assert_timestamp.tv_sec += handle->params.assert_offset.tv_sec; infobuf.assert_timestamp.tv_nsec += handle->params.assert_offset.tv_nsec; PPS_NORMALIZE(infobuf.assert_timestamp); } /* * Translate to specified format */ switch (tsformat) { case PPS_TSFMT_TSPEC: break; /* timespec format requires no translation */ case PPS_TSFMT_NTPFP: /* NTP format requires conversion to fraction form */ PPS_TSPECTONTP(infobuf.assert_timestamp_ntpfp); break; default: errno = EINVAL; return (-1); } infobuf.current_mode = handle->params.mode; memcpy(ppsinfo, &infobuf, sizeof(pps_info_t)); return (0); } /* * specify kernel consumer */ static inline int time_pps_kcbind( pps_handle_t handle, const int kernel_consumer, const int edge, const int tsformat ) { /* * Check for valid arguments and bind kernel consumer */ if (!handle) { errno = EBADF; return (-1); /* bad handle */ } if (geteuid() != 0) { errno = EPERM; return (-1); /* must be superuser */ } errno = EOPNOTSUPP; return(-1); } #endif /* _SYS_TIMEPPS_H_ */