1 /*********************************************************************** 2 * * 3 * Copyright (c) David L. Mills 1999-2000 * 4 * * 5 * Permission to use, copy, modify, and distribute this software and * 6 * its documentation for any purpose and with or without fee is hereby * 7 * granted, provided that the above copyright notice appears in all * 8 * copies and that both the copyright notice and this permission * 9 * notice appear in supporting documentation, and that the name * 10 * University of Delaware not be used in advertising or publicity * 11 * pertaining to distribution of the software without specific, * 12 * written prior permission. The University of Delaware makes no * 13 * representations about the suitability this software for any * 14 * purpose. It is provided "as is" without express or implied * 15 * warranty. * 16 * * 17 *********************************************************************** 18 * * 19 * This header file complies with "Pulse-Per-Second API for UNIX-like * 20 * Operating Systems, Version 1.0", rfc2783. Credit is due Jeff Mogul * 21 * and Marc Brett, from whom much of this code was shamelessly stolen. * 22 * * 23 * this modified timepps.h can be used to provide a PPSAPI interface * 24 * to a machine running SunOS. * 25 * * 26 *********************************************************************** 27 * * 28 * A full PPSAPI interface to the SunOS kernel would be better, but * 29 * this at least removes the necessity for special coding from the NTP * 30 * NTP drivers. * 31 * * 32 *********************************************************************** 33 * * 34 * Some of this include file * 35 * Copyright (c) 1999 by Ulrich Windl, * 36 * based on code by Reg Clemens <reg@dwf.com> * 37 * based on code by Poul-Henning Kamp <phk@FreeBSD.org> * 38 * * 39 *********************************************************************** 40 * * 41 * "THE BEER-WARE LICENSE" (Revision 42): * 42 * <phk@FreeBSD.org> wrote this file. As long as you retain this * 43 * notice you can do whatever you want with this stuff. If we meet some* 44 * day, and you think this stuff is worth it, you can buy me a beer * 45 * in return. Poul-Henning Kamp * 46 * * 47 **********************************************************************/ 48 49 /* SunOS version, CIOGETEV assumed to exist for SunOS */ 50 51 #ifndef _SYS_TIMEPPS_H_ 52 #define _SYS_TIMEPPS_H_ 53 54 #include <termios.h> /* to get CIOGETEV */ 55 56 /* Implementation note: the logical states ``assert'' and ``clear'' 57 * are implemented in terms of the UART register, i.e. ``assert'' 58 * means the bit is set. 59 */ 60 61 /* 62 * The following definitions are architecture independent 63 */ 64 65 #define PPS_API_VERS_1 1 /* API version number */ 66 #define PPS_JAN_1970 2208988800UL /* 1970 - 1900 in seconds */ 67 #define PPS_NANOSECOND 1000000000L /* one nanosecond in decimal */ 68 #define PPS_FRAC 4294967296. /* 2^32 as a double */ 69 70 #define PPS_NORMALIZE(x) /* normalize timespec */ \ 71 do { \ 72 if ((x).tv_nsec >= PPS_NANOSECOND) { \ 73 (x).tv_nsec -= PPS_NANOSECOND; \ 74 (x).tv_sec++; \ 75 } else if ((x).tv_nsec < 0) { \ 76 (x).tv_nsec += PPS_NANOSECOND; \ 77 (x).tv_sec--; \ 78 } \ 79 } while (0) 80 81 #define PPS_TSPECTONTP(x) /* convert timespec to l_fp */ \ 82 do { \ 83 double d_temp; \ 84 \ 85 (x).integral += (unsigned int)PPS_JAN_1970; \ 86 d_temp = (x).fractional * PPS_FRAC / PPS_NANOSECOND; \ 87 if (d_temp >= PPS_FRAC) \ 88 (x).integral++; \ 89 (x).fractional = (unsigned int)d_temp; \ 90 } while (0) 91 92 /* 93 * Device/implementation parameters (mode) 94 */ 95 96 #define PPS_CAPTUREASSERT 0x01 /* capture assert events */ 97 #define PPS_CAPTURECLEAR 0x02 /* capture clear events */ 98 #define PPS_CAPTUREBOTH 0x03 /* capture assert and clear events */ 99 100 #define PPS_OFFSETASSERT 0x10 /* apply compensation for assert ev. */ 101 #define PPS_OFFSETCLEAR 0x20 /* apply compensation for clear ev. */ 102 #define PPS_OFFSETBOTH 0x30 /* apply compensation for both */ 103 104 #define PPS_CANWAIT 0x100 /* Can we wait for an event? */ 105 #define PPS_CANPOLL 0x200 /* "This bit is reserved for */ 106 107 /* 108 * Kernel actions (mode) 109 */ 110 111 #define PPS_ECHOASSERT 0x40 /* feed back assert event to output */ 112 #define PPS_ECHOCLEAR 0x80 /* feed back clear event to output */ 113 114 /* 115 * Timestamp formats (tsformat) 116 */ 117 118 #define PPS_TSFMT_TSPEC 0x1000 /* select timespec format */ 119 #define PPS_TSFMT_NTPFP 0x2000 /* select NTP format */ 120 121 /* 122 * Kernel discipline actions (not used in SunOS) 123 */ 124 125 #define PPS_KC_HARDPPS 0 /* enable kernel consumer */ 126 #define PPS_KC_HARDPPS_PLL 1 /* phase-lock mode */ 127 #define PPS_KC_HARDPPS_FLL 2 /* frequency-lock mode */ 128 129 /* 130 * Type definitions 131 */ 132 133 typedef unsigned long pps_seq_t; /* sequence number */ 134 135 typedef struct ntp_fp { 136 unsigned int integral; 137 unsigned int fractional; 138 } ntp_fp_t; /* NTP-compatible time stamp */ 139 140 typedef union pps_timeu { /* timestamp format */ 141 struct timespec tspec; 142 ntp_fp_t ntpfp; 143 unsigned long longpad[3]; 144 } pps_timeu_t; /* generic data type to represent time stamps */ 145 146 /* 147 * Timestamp information structure 148 */ 149 150 typedef struct pps_info { 151 pps_seq_t assert_sequence; /* seq. num. of assert event */ 152 pps_seq_t clear_sequence; /* seq. num. of clear event */ 153 pps_timeu_t assert_tu; /* time of assert event */ 154 pps_timeu_t clear_tu; /* time of clear event */ 155 int current_mode; /* current mode bits */ 156 } pps_info_t; 157 158 #define assert_timestamp assert_tu.tspec 159 #define clear_timestamp clear_tu.tspec 160 161 #define assert_timestamp_ntpfp assert_tu.ntpfp 162 #define clear_timestamp_ntpfp clear_tu.ntpfp 163 164 /* 165 * Parameter structure 166 */ 167 168 typedef struct pps_params { 169 int api_version; /* API version # */ 170 int mode; /* mode bits */ 171 pps_timeu_t assert_off_tu; /* offset compensation for assert */ 172 pps_timeu_t clear_off_tu; /* offset compensation for clear */ 173 } pps_params_t; 174 175 #define assert_offset assert_off_tu.tspec 176 #define clear_offset clear_off_tu.tspec 177 178 #define assert_offset_ntpfp assert_off_tu.ntpfp 179 #define clear_offset_ntpfp clear_off_tu.ntpfp 180 181 /* 182 * The following definitions are architecture-dependent 183 */ 184 185 #define PPS_CAP (PPS_CAPTUREASSERT | PPS_OFFSETASSERT | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP) 186 #define PPS_RO (PPS_CANWAIT | PPS_CANPOLL | PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP) 187 188 typedef struct { 189 int filedes; /* file descriptor */ 190 pps_params_t params; /* PPS parameters set by user */ 191 } pps_unit_t; 192 193 typedef pps_unit_t* pps_handle_t; /* pps handlebars */ 194 195 /* 196 *------ Here begins the implementation-specific part! ------ 197 */ 198 199 #include <errno.h> 200 201 /* 202 * create PPS handle from file descriptor 203 */ 204 205 static inline int 206 time_pps_create( 207 int filedes, /* file descriptor */ 208 pps_handle_t *handle /* returned handle */ 209 ) 210 { 211 /* 212 * Check for valid arguments and attach PPS signal. 213 */ 214 215 if (!handle) { 216 errno = EFAULT; 217 return (-1); /* null pointer */ 218 } 219 220 if (ioctl(filedes, I_PUSH, "ppsclock") < 0) { 221 perror("time_pps_create: I_PUSH ppsclock failed"); 222 return (-1); 223 } 224 225 /* 226 * Allocate and initialize default unit structure. 227 */ 228 229 *handle = malloc(sizeof(pps_unit_t)); 230 if (!(*handle)) { 231 errno = EBADF; 232 return (-1); /* what, no memory? */ 233 } 234 235 memset(*handle, 0, sizeof(pps_unit_t)); 236 (*handle)->filedes = filedes; 237 (*handle)->params.api_version = PPS_API_VERS_1; 238 (*handle)->params.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC; 239 return (0); 240 } 241 242 /* 243 * release PPS handle 244 */ 245 246 static inline int 247 time_pps_destroy( 248 pps_handle_t handle 249 ) 250 { 251 /* 252 * Check for valid arguments and detach PPS signal. 253 */ 254 255 if (!handle) { 256 errno = EBADF; 257 return (-1); /* bad handle */ 258 } 259 free(handle); 260 return (0); 261 } 262 263 /* 264 * set parameters for handle 265 */ 266 267 static inline int 268 time_pps_setparams( 269 pps_handle_t handle, 270 const pps_params_t *params 271 ) 272 { 273 int mode, mode_in; 274 /* 275 * Check for valid arguments and set parameters. 276 */ 277 278 if (!handle) { 279 errno = EBADF; 280 return (-1); /* bad handle */ 281 } 282 283 if (!params) { 284 errno = EFAULT; 285 return (-1); /* bad argument */ 286 } 287 288 /* 289 * There was no reasonable consensu in the API working group. 290 * I require `api_version' to be set! 291 */ 292 293 if (params->api_version != PPS_API_VERS_1) { 294 errno = EINVAL; 295 return(-1); 296 } 297 298 /* 299 * only settable modes are PPS_CAPTUREASSERT and PPS_OFFSETASSERT 300 */ 301 302 mode_in = params->mode; 303 304 /* turn off read-only bits */ 305 306 mode_in &= ~PPS_RO; 307 308 /* test remaining bits, should only have captureassert and/or offsetassert */ 309 310 if (mode_in & ~(PPS_CAPTUREASSERT | PPS_OFFSETASSERT)) { 311 errno = EOPNOTSUPP; 312 return(-1); 313 } 314 315 /* 316 * ok, ready to go. 317 */ 318 319 mode = handle->params.mode; 320 memcpy(&handle->params, params, sizeof(pps_params_t)); 321 handle->params.api_version = PPS_API_VERS_1; 322 handle->params.mode = mode | mode_in; 323 return (0); 324 } 325 326 /* 327 * get parameters for handle 328 */ 329 330 static inline int 331 time_pps_getparams( 332 pps_handle_t handle, 333 pps_params_t *params 334 ) 335 { 336 /* 337 * Check for valid arguments and get parameters. 338 */ 339 340 if (!handle) { 341 errno = EBADF; 342 return (-1); /* bad handle */ 343 } 344 345 if (!params) { 346 errno = EFAULT; 347 return (-1); /* bad argument */ 348 } 349 350 memcpy(params, &handle->params, sizeof(pps_params_t)); 351 return (0); 352 } 353 354 /* ( 355 * get capabilities for handle 356 */ 357 358 static inline int 359 time_pps_getcap( 360 pps_handle_t handle, 361 int *mode 362 ) 363 { 364 /* 365 * Check for valid arguments and get capabilities. 366 */ 367 368 if (!handle) { 369 errno = EBADF; 370 return (-1); /* bad handle */ 371 } 372 373 if (!mode) { 374 errno = EFAULT; 375 return (-1); /* bad argument */ 376 } 377 *mode = PPS_CAP; 378 return (0); 379 } 380 381 /* 382 * Fetch timestamps 383 */ 384 385 static inline int 386 time_pps_fetch( 387 pps_handle_t handle, 388 const int tsformat, 389 pps_info_t *ppsinfo, 390 const struct timespec *timeout 391 ) 392 { 393 struct ppsclockev { 394 struct timeval tv; 395 u_int serial; 396 } ev; 397 pps_info_t infobuf; 398 399 /* 400 * Check for valid arguments and fetch timestamps 401 */ 402 403 if (!handle) { 404 errno = EBADF; 405 return (-1); /* bad handle */ 406 } 407 408 if (!ppsinfo) { 409 errno = EFAULT; 410 return (-1); /* bad argument */ 411 } 412 413 /* 414 * nb. PPS_CANWAIT is NOT set by the implementation, we can totally 415 * ignore the timeout variable. 416 */ 417 418 memset(&infobuf, 0, sizeof(infobuf)); 419 420 /* 421 * if not captureassert, nothing to return. 422 */ 423 424 if (!handle->params.mode & PPS_CAPTUREASSERT) { 425 memcpy(ppsinfo, &infobuf, sizeof(pps_info_t)); 426 return (0); 427 } 428 429 #if defined(__STDC__) 430 #define CIOGETEV _IOR('C', 0, struct ppsclockev) /* get last pps event */ 431 #else 432 #define CIOGETEV _IOR(C, 0, struct ppsclockev) /* get last pps event */ 433 #endif 434 435 if (ioctl(handle->filedes, CIOGETEV, (caddr_t) &ev) < 0) { 436 perror("time_pps_fetch:"); 437 errno = EOPNOTSUPP; 438 return(-1); 439 } 440 441 /* 442 * Apply offsets as specified. Note that only assert timestamps 443 * are captured by this interface. 444 */ 445 446 infobuf.assert_sequence = ev.serial; 447 infobuf.assert_timestamp.tv_sec = ev.tv.tv_sec; 448 infobuf.assert_timestamp.tv_nsec = ev.tv.tv_usec * 1000; 449 450 if (handle->params.mode & PPS_OFFSETASSERT) { 451 infobuf.assert_timestamp.tv_sec += handle->params.assert_offset.tv_sec; 452 infobuf.assert_timestamp.tv_nsec += handle->params.assert_offset.tv_nsec; 453 PPS_NORMALIZE(infobuf.assert_timestamp); 454 } 455 456 /* 457 * Translate to specified format 458 */ 459 460 switch (tsformat) { 461 case PPS_TSFMT_TSPEC: 462 break; /* timespec format requires no translation */ 463 464 case PPS_TSFMT_NTPFP: /* NTP format requires conversion to fraction form */ 465 PPS_TSPECTONTP(infobuf.assert_timestamp_ntpfp); 466 break; 467 468 default: 469 errno = EINVAL; 470 return (-1); 471 } 472 473 infobuf.current_mode = handle->params.mode; 474 memcpy(ppsinfo, &infobuf, sizeof(pps_info_t)); 475 return (0); 476 } 477 478 /* 479 * specify kernel consumer 480 */ 481 482 static inline int 483 time_pps_kcbind( 484 pps_handle_t handle, 485 const int kernel_consumer, 486 const int edge, const int tsformat 487 ) 488 { 489 /* 490 * Check for valid arguments and bind kernel consumer 491 */ 492 if (!handle) { 493 errno = EBADF; 494 return (-1); /* bad handle */ 495 } 496 if (geteuid() != 0) { 497 errno = EPERM; 498 return (-1); /* must be superuser */ 499 } 500 errno = EOPNOTSUPP; 501 return(-1); 502 } 503 504 #endif /* _SYS_TIMEPPS_H_ */ 505