ppscheck.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. /*
  2. * Watch a specified serial port for transitions that might be 1PPS.
  3. *
  4. * Each output line is the second and nanosecond parts of a timestamp
  5. * followed by the names of handshake signals then asserted. Off
  6. * transitions may generate lines with no signals asserted.
  7. *
  8. * If you don't see output within a second, use cgps, xgps, or some other
  9. * equivalent tool to check that your device has satellite lock and is
  10. * getting fixes before giving up on the possibility of 1PPS.
  11. *
  12. * Also, check your cable. Cheap DB9 to DB9 cables such as those
  13. * issued with UPSes often carry TXD/RXD/GND only, omitting handshake
  14. * lines such as DCD. Suspect this especially if the cable jacket
  15. * looks too skinny to hold more than three leads!
  16. *
  17. * This code requires only ANSI/POSIX. If it doesn't compile and run
  18. * on your Unix there is something very wrong with your Unix.
  19. *
  20. * This code by ESR, Copyright 2013, under BSD terms.
  21. * This file is Copyright 2013 by the GPSD project
  22. * SPDX-License-Identifier: BSD-2-clause
  23. */
  24. #include "../include/gpsd_config.h" // must be before all includes
  25. #include <dirent.h> // for opendir()
  26. #include <errno.h>
  27. #include <fcntl.h> // needed for open() and friends
  28. #ifdef HAVE_GETOPT_LONG
  29. #include <getopt.h>
  30. #endif
  31. #include <limits.h> // for PATH_MAX
  32. #ifdef __linux__
  33. #include <linux/tty.h> // for N_PPS
  34. #endif
  35. #include <stdio.h>
  36. #include <stdlib.h> // for atexit()
  37. #include <string.h> // strlcpy(), etc.
  38. #include <sys/ioctl.h>
  39. #include <sys/stat.h>
  40. #if defined(HAVE_SYS_TIMEPPS_H)
  41. #include <sys/timepps.h>
  42. #endif
  43. #include <sys/types.h>
  44. #include <time.h>
  45. // include unistd.h here as it is missing on older pps-tools releases.
  46. // 'close' is not defined otherwise.
  47. #include <unistd.h>
  48. #include "../include/compiler.h" // for FALLTHROUGH
  49. #include "../include/os_compat.h" // backup for strlcpy()
  50. #include "../include/timespec.h"
  51. time_t exit_timer = 0; // for -x option
  52. int device_fd = -1; // fd open device
  53. int pps_fd = -1; // fd open pps device
  54. #if defined(HAVE_SYS_TIMEPPS_H)
  55. // aka RFC2783
  56. pps_handle_t kpps_handle = -1; // from time_pps_create()
  57. #endif // HAVE_SYS_TIMEPPS_H
  58. int path_fd = -1; // fd for open("sys/X")
  59. DIR *sys_dir = NULL; // fd for opendir("sys")
  60. const char *sys_path = "/sys/devices/virtual/pps";
  61. // atexit function
  62. static void myexit(void)
  63. {
  64. if (0 <= device_fd) {
  65. close(device_fd);
  66. }
  67. if (0 <= pps_fd) {
  68. close(pps_fd);
  69. }
  70. if (0 <= path_fd) {
  71. close(path_fd);
  72. path_fd = -1;
  73. }
  74. if (NULL != sys_dir) {
  75. closedir(sys_dir);
  76. }
  77. #if defined(HAVE_SYS_TIMEPPS_H)
  78. if (0 <= kpps_handle) {
  79. time_pps_destroy(kpps_handle);
  80. }
  81. #endif // HAVE_SYS_TIMEPPS_H
  82. }
  83. struct assoc {
  84. int mask;
  85. char *string;
  86. };
  87. /*
  88. * Possible pins for PPS: DCD, CTS, RI, DSR. Pinouts:
  89. *
  90. * DB9 DB25 Name Full name
  91. * --- ---- ---- --------------------
  92. * 3 2 TXD --> Transmit Data
  93. * 2 3 RXD <-- Receive Data
  94. * 7 4 RTS --> Request To Send
  95. * 8 5 CTS <-- Clear To Send
  96. * 6 6 DSR <-- Data Set Ready
  97. * 4 20 DTR --> Data Terminal Ready
  98. * 1 8 DCD <-- Data Carrier Detect
  99. * 9 22 RI <-- Ring Indicator
  100. * 5 7 GND Signal ground
  101. *
  102. * Note that it only makes sense to wait on handshake lines
  103. * activated from the receive side (DCE->DTE) here; in this
  104. * context "DCE" is the GPS. {CD,RI,CTS,DSR} is the
  105. * entire set of these.
  106. */
  107. static const struct assoc hlines[] = {
  108. {TIOCM_CD, "TIOCM_CD"},
  109. {TIOCM_RI, "TIOCM_RI"},
  110. {TIOCM_DSR, "TIOCM_DSR"},
  111. {TIOCM_CTS, "TIOCM_CTS"},
  112. };
  113. #if defined(HAVE_SYS_TIMEPPS_H)
  114. // aka RFC2783
  115. static const struct assoc caps[] = {
  116. {PPS_CAPTUREASSERT, "PPS_CAPTUREASSERT"},
  117. {PPS_CAPTURECLEAR, "PPS_CAPTURECLEAR"},
  118. {PPS_CAPTUREBOTH, "PPS_CAPTUREBOTH"},
  119. {PPS_OFFSETASSERT, "PPS_OFFSETASSERT"},
  120. {PPS_OFFSETCLEAR, "PPS_OFFSETCLEAR"},
  121. {PPS_CANWAIT, "PPS_CANWAIT"},
  122. {PPS_CANPOLL, "PPS_CANPOLL"},
  123. {PPS_ECHOASSERT, "PPS_ECHOASSERT"},
  124. {PPS_ECHOCLEAR, "PPS_ECHOCLEAR"},
  125. {PPS_TSFMT_TSPEC, "PPS_TSFMT_TSPEC"},
  126. {PPS_TSFMT_NTPFP, "PPS_TSFMT_NTPFP"},
  127. };
  128. /* cfg_pps()
  129. * shows KPPS caps
  130. * enable CAPTURES
  131. *
  132. * Return: void
  133. * exits on error
  134. */
  135. static void cfg_kpps(void)
  136. {
  137. int kpps_caps = 0;
  138. pps_params_t pp;
  139. const struct assoc *sp;
  140. // have kernel PPS handle. get RFC2783 features supported
  141. if (0 > time_pps_getcap(kpps_handle, &kpps_caps)) {
  142. (void)printf("ERROR: time_pps_getcap() failed: %s(%d)\n",
  143. strerror(errno), errno);
  144. exit(EXIT_FAILURE);
  145. }
  146. (void)printf("INFO: kpps_caps 0x%02X\n", kpps_caps);
  147. for (sp = caps; sp < caps + sizeof(caps) / sizeof(caps[0]); sp++) {
  148. if (0 != (kpps_caps & sp->mask)) {
  149. (void)printf(" %s\n", sp->string);
  150. }
  151. }
  152. puts("");
  153. if (0 == (PPS_CANWAIT & kpps_caps)) {
  154. (void)puts("ERROR: PPS_CANWAIT is missing.");
  155. }
  156. // construct the setparms structure
  157. memset((void *)&pp, 0, sizeof(pps_params_t));
  158. pp.api_version = PPS_API_VERS_1; // version 1 protocol
  159. switch ((PPS_CAPTUREASSERT | PPS_CAPTURECLEAR) & kpps_caps) {
  160. case PPS_CAPTUREASSERT:
  161. (void)puts("WARNING: missing PPS_CAPTURECLEAR, pulse may be offset");
  162. pp.mode |= PPS_CAPTUREASSERT;
  163. break;
  164. case PPS_CAPTURECLEAR:
  165. (void)puts("WARNING: missing PPS_CAPTUREASSERT, pulse may be offset");
  166. pp.mode |= PPS_CAPTURECLEAR;
  167. break;
  168. case PPS_CAPTUREASSERT | PPS_CAPTURECLEAR:
  169. pp.mode |= PPS_CAPTUREASSERT | PPS_CAPTURECLEAR;
  170. break;
  171. default:
  172. (void)puts("WARNING: missing PPS_CAPTUREASSERT and PPS_CAPTURECLEAR");
  173. exit(EXIT_FAILURE);
  174. }
  175. if (0 > time_pps_setparams(kpps_handle, &pp)) {
  176. (void)printf("ERROR: time_pps_setparams(mode=0x%02X) failed: %s(%d)",
  177. pp.mode, strerror(errno), errno);
  178. exit(EXIT_FAILURE);
  179. }
  180. }
  181. static void do_kpps(void)
  182. {
  183. time_t last = {0};
  184. pps_seq_t clear_seq = -1; // KPPS clear sequence
  185. pps_seq_t assert_seq = -1; // KPPS assert sequence
  186. cfg_kpps(); // get caps, configure KPPS
  187. (void)puts("\n# Src Seconds Signal Sequence");
  188. for (;;) {
  189. pps_info_t pi;
  190. struct timespec kpps_tv;
  191. time_t now;
  192. char ts_str[TIMESPEC_LEN];
  193. kpps_tv.tv_sec = 3; // 3 second timeout
  194. kpps_tv.tv_nsec = 0;
  195. memset((void *)&pi, 0, sizeof(pi)); // paranoia
  196. // wait for an event
  197. if (0 > time_pps_fetch(kpps_handle, PPS_TSFMT_TSPEC, &pi, &kpps_tv)) {
  198. if (ETIMEDOUT == errno ||
  199. EINTR == errno) {
  200. // just a timeout
  201. (void)puts("WARNING: time_pps_fetch() timeout\n");
  202. continue;
  203. }
  204. (void)printf("ERROR: time_pps_fetch() failed: %s(%d)\n",
  205. strerror(errno), errno);
  206. exit(EXIT_FAILURE);
  207. }
  208. now = time(NULL);
  209. if (0 < exit_timer &&
  210. now >= exit_timer) {
  211. break;
  212. }
  213. // new second, output a newline.
  214. if (last != now) {
  215. (void)putchar('\n');
  216. last = now;
  217. }
  218. if (pi.assert_sequence != assert_seq) {
  219. (void)printf(" KPPS %s assert %lu\n",
  220. timespec_str(&pi.assert_timestamp,
  221. ts_str, sizeof(ts_str)),
  222. (unsigned long)pi.assert_sequence);
  223. assert_seq = pi.assert_sequence;
  224. }
  225. if (pi.clear_sequence != clear_seq) {
  226. (void)printf(" KPPS %s clear %lu\n",
  227. timespec_str(&pi.clear_timestamp,
  228. ts_str, sizeof(ts_str)),
  229. (unsigned long)pi.clear_sequence);
  230. clear_seq = pi.clear_sequence;
  231. }
  232. }
  233. exit(EXIT_SUCCESS);
  234. }
  235. #endif
  236. /* list_pps() - list pps devices
  237. * linux specific, OK to just let it fail on other OS
  238. * /sys/devices/virtual/pps/pps?/
  239. */
  240. static void list_pps(void)
  241. {
  242. struct dirent *dp;
  243. sys_dir = opendir(sys_path);
  244. if (NULL == sys_dir) {
  245. (void)printf("ERROR: opendir(%s) failed: %.80s(%d)\n",
  246. sys_path, strerror(errno), errno);
  247. return;
  248. }
  249. while (NULL != (dp = readdir(sys_dir))) {
  250. char name_path[PATH_MAX];
  251. char tty_path[PATH_MAX];
  252. ssize_t len;
  253. if ('.' == dp->d_name[0]) {
  254. continue;
  255. }
  256. (void)printf("INFO: %s ", dp->d_name);
  257. (void)snprintf(name_path, sizeof(name_path), "%s/%s/path",
  258. sys_path, dp->d_name);
  259. if (0 <= path_fd) {
  260. close(path_fd);
  261. path_fd = -1;
  262. }
  263. path_fd = open(name_path, O_RDONLY);
  264. if (-1 == path_fd) {
  265. (void)printf("\nERROR: open(%s) failed: %.80s(%d)\n",
  266. name_path, strerror(errno), errno);
  267. continue;
  268. }
  269. len = read(path_fd, tty_path, sizeof(tty_path));
  270. if (-1 == len) {
  271. (void)printf("\nERROR: read(%s) failed: %.80s(%d)\n",
  272. name_path, strerror(errno), errno);
  273. continue;
  274. }
  275. // tty_path ends with \n
  276. if (0 < len) {
  277. tty_path[len - 1] = '\0';
  278. }
  279. puts(tty_path);
  280. }
  281. (void)close(path_fd);
  282. }
  283. /* find_pps() - find pps device that matches a tty device.
  284. * very similar to list_pps()
  285. * linux specific, OK to just let it fail on other OS
  286. * /sys/devices/virtual/pps/pps?/
  287. *
  288. * return: pointer to static buffer of answer
  289. * NULL on no match
  290. */
  291. static const char *find_pps(const char *device)
  292. {
  293. struct dirent *dp;
  294. static char match[PATH_MAX];
  295. sys_dir = opendir(sys_path);
  296. if (NULL == sys_dir) {
  297. (void)printf("ERROR: opendir(%s) failed: %.80s(%d)\n",
  298. sys_path, strerror(errno), errno);
  299. return NULL;
  300. }
  301. while (NULL != (dp = readdir(sys_dir))) {
  302. char name_path[PATH_MAX];
  303. char tty_path[PATH_MAX];
  304. ssize_t len;
  305. if ('.' == dp->d_name[0]) {
  306. continue;
  307. }
  308. (void)snprintf(name_path, sizeof(name_path), "%s/%s/path",
  309. sys_path, dp->d_name);
  310. if (0 <= path_fd) {
  311. close(path_fd);
  312. path_fd = -1;
  313. }
  314. path_fd = open(name_path, O_RDONLY);
  315. if (-1 == path_fd) {
  316. (void)printf("ERROR: open(%s) failed: %.80s(%d)",
  317. name_path, strerror(errno), errno);
  318. continue;
  319. }
  320. len = read(path_fd, tty_path, sizeof(tty_path));
  321. if (-1 == len) {
  322. (void)printf("ERROR: read(%s) failed: %.80s(%d)",
  323. name_path, strerror(errno), errno);
  324. continue;
  325. }
  326. // tty_path ends with \n
  327. if (0 < len) {
  328. tty_path[len - 1] = '\0';
  329. }
  330. if (0 == strncmp(device, tty_path, sizeof(tty_path))) {
  331. (void)strlcpy(match, dp->d_name, sizeof(match));
  332. return match;
  333. }
  334. }
  335. (void)close(path_fd);
  336. return NULL;
  337. }
  338. /* do_tty()
  339. * the main loop for wathing a tty, and optional companion kpps
  340. *
  341. * return: Never
  342. */
  343. static void do_tty(void)
  344. {
  345. #if defined(HAVE_SYS_TIMEPPS_H)
  346. pps_seq_t clear_seq = -1; // KPPS clear sequence
  347. pps_seq_t assert_seq = -1; // KPPS assert sequence
  348. #endif // HAVE_SYS_TIMEPPS_H
  349. int handshakes;
  350. struct timespec ts;
  351. time_t last_sec = -1;
  352. (void)puts("\n# Src Seconds Signals");
  353. for (;;) {
  354. const struct assoc *sp;
  355. #if defined(HAVE_SYS_TIMEPPS_H)
  356. pps_info_t pi;
  357. struct timespec kpps_tv ;
  358. #endif // HAVE_SYS_TIMEPPS_H
  359. char ts_str[TIMESPEC_LEN];
  360. if (0 < exit_timer &&
  361. time(NULL) >= exit_timer) {
  362. break;
  363. }
  364. // use TIOCMIWAIT to wait for change
  365. // no way to set a timeout on this ioctl()
  366. if (0 != ioctl(device_fd, TIOCMIWAIT,
  367. TIOCM_CD | TIOCM_DSR | TIOCM_RI | TIOCM_CTS)) {
  368. (void)printf("ERROR: ioctl(TIOCMIWAIT) failed: %.80s(%d)\n",
  369. strerror(errno), errno);
  370. exit(EXIT_FAILURE);
  371. }
  372. (void)clock_gettime(CLOCK_REALTIME, &ts); // quick, grab current time
  373. // figure out what changed
  374. // look into TIOCGICOUNT instead?
  375. if (0 != ioctl(device_fd, TIOCMGET, &handshakes)) {
  376. (void)printf("ERROR: ioctl(TIOCMGET) failed: %.80s(%d)\n",
  377. strerror(errno), errno);
  378. exit(EXIT_FAILURE);
  379. }
  380. if (last_sec != ts.tv_sec) {
  381. // new second, new line
  382. (void)putchar('\n');
  383. last_sec = ts.tv_sec;
  384. }
  385. #if defined(HAVE_SYS_TIMEPPS_H)
  386. if (0 <= kpps_handle) {
  387. kpps_tv.tv_sec = 0; // non-blocking
  388. kpps_tv.tv_nsec = 0;
  389. bool good_pi;
  390. good_pi = true;
  391. memset((void *)&pi, 0, sizeof(pi)); // paranoia
  392. if (0 > time_pps_fetch(kpps_handle, PPS_TSFMT_TSPEC,
  393. &pi, &kpps_tv)) {
  394. (void)printf("ERROR: time_pps_fetch() failed: %s(%d)\n",
  395. strerror(errno), errno);
  396. good_pi = false;
  397. }
  398. // print KPPS first, as its timestamp will be before
  399. // TIOCMIWAIT time
  400. if (good_pi) {
  401. if (pi.assert_sequence != assert_seq) {
  402. (void)printf(" KPPS %s assert %lu\n",
  403. timespec_str(&pi.assert_timestamp,
  404. ts_str, sizeof(ts_str)),
  405. (unsigned long)pi.assert_sequence);
  406. assert_seq = pi.assert_sequence;
  407. }
  408. if (pi.clear_sequence != clear_seq) {
  409. (void)printf(" KPPS %s clear %lu\n",
  410. timespec_str(&pi.clear_timestamp,
  411. ts_str, sizeof(ts_str)),
  412. (unsigned long)pi.clear_sequence);
  413. clear_seq = pi.clear_sequence;
  414. }
  415. }
  416. }
  417. #endif // HAVE_SYS_TIMEPPS_H
  418. (void)printf(" TTY %s ",
  419. timespec_str(&ts, ts_str, sizeof(ts_str)));
  420. for (sp = hlines;
  421. sp < hlines + sizeof(hlines) / sizeof(hlines[0]);
  422. sp++) {
  423. if (0 != (handshakes & sp->mask)) {
  424. (void)printf(" %s", sp->string);
  425. }
  426. }
  427. (void)putchar('\n');
  428. }
  429. exit(EXIT_SUCCESS);
  430. }
  431. static void usage(void)
  432. {
  433. (void)printf(
  434. "usage: ppscheck [OPTIONS] <device>\n\n"
  435. #ifdef HAVE_GETOPT_LONG
  436. " --help Show this help, then exit.\n"
  437. " --pps List pps devices active.\n"
  438. " --seconds SEC Exit after SEC seconds delay.\n"
  439. " --version Show version, then exit.\n"
  440. #endif
  441. " -? Show this help, then exit.\n"
  442. " -h Show this help, then exit.\n"
  443. " -m Find pps device that matches <device>\n"
  444. " -p List pps devices active.\n"
  445. " -V Show version, then exit.\n"
  446. " -x SEC Exit after SEC seconds delay.\n"
  447. "\n"
  448. " <device> Device to check (/dev/ttyS0, /dev/pps0, etc.).\n");
  449. }
  450. int main(int argc, char *argv[])
  451. {
  452. #ifdef __linux__
  453. int ldisc = 0; // tty line discipline
  454. #endif
  455. int handshakes;
  456. bool is_tty = false;
  457. bool has_kpps = false;
  458. bool find_kpps = false;
  459. const char *kpps_name = NULL; // logical name of pps device (pps0, etc.).
  460. char kpps_path[PATH_MAX] = ""; // full path to devined kpps device
  461. char *device = NULL; // pointer to <device> name
  462. char device_real[PATH_MAX]; // realname() of <device>
  463. const char *optstring = "?hmpVx:";
  464. #ifdef HAVE_GETOPT_LONG
  465. int option_index = 0;
  466. static struct option long_options[] = {
  467. {"help", no_argument, NULL, 'h'},
  468. {"match", no_argument, NULL, 'm'},
  469. {"pps", no_argument, NULL, 'p'},
  470. {"seconds", required_argument, NULL, 'x'},
  471. {"version", no_argument, NULL, 'V' },
  472. {NULL, 0, NULL, 0},
  473. };
  474. #endif
  475. while (1) {
  476. int ch;
  477. #ifdef HAVE_GETOPT_LONG
  478. ch = getopt_long(argc, argv, optstring, long_options, &option_index);
  479. #else
  480. ch = getopt(argc, argv, optstring);
  481. #endif
  482. if (-1 == ch) {
  483. break;
  484. }
  485. switch(ch){
  486. case '?':
  487. FALLTHROUGH
  488. case 'h':
  489. usage();
  490. exit(EXIT_SUCCESS);
  491. default:
  492. usage();
  493. exit(EXIT_FAILURE);
  494. case 'm':
  495. find_kpps = true;
  496. break;
  497. case 'p':
  498. list_pps();
  499. exit(EXIT_SUCCESS);
  500. case 'V':
  501. (void)printf("%s: %s\n", argv[0], REVISION);
  502. exit(EXIT_SUCCESS);
  503. case 'x':
  504. exit_timer = time(NULL) + strtol(optarg, 0, 0);
  505. break;
  506. }
  507. }
  508. if ((optind + 1) != argc ||
  509. '\0' == argv[optind][0]) {
  510. (void)puts("ERROR: can't run with no device specified");
  511. usage();
  512. exit(EXIT_FAILURE);
  513. }
  514. atexit(myexit);
  515. device = realpath(argv[optind], device_real);;
  516. if (NULL == device) {
  517. (void)printf("ERROR: realpath(%s) failed: %.80s(%d)\n",
  518. argv[optind], strerror(errno), errno);
  519. exit(EXIT_FAILURE);
  520. }
  521. if (0 != strncmp(device_real, argv[optind], sizeof(device_real))) {
  522. (void)printf("INFO: %s is a symlink to %s\n",
  523. argv[optind], device);
  524. }
  525. // handle -p option
  526. if (find_kpps) {
  527. kpps_name = find_pps(device);
  528. if (NULL == kpps_name) {
  529. (void)printf("INFO: pps for %s not found\n", device);
  530. } else {
  531. (void)printf("INFO: %s uses %s\n", device, kpps_name);
  532. }
  533. exit(EXIT_SUCCESS);
  534. }
  535. // TIOCM* only needs RD, KPPS needs WR
  536. device_fd = open(device, O_RDWR);
  537. if (-1 == device_fd) {
  538. (void)printf("ERROR: open(%s) failed: %.80s(%d)\n",
  539. argv[1], strerror(errno), errno);
  540. exit(EXIT_FAILURE);
  541. }
  542. // check that it is a tty
  543. if (0 == ioctl(device_fd, TIOCMGET, &handshakes)) {
  544. is_tty = true;
  545. #ifdef __linux__
  546. /* check current line discipline
  547. * if (0 == ioctl(device_fd, TIOCGETD, &ldisc)) {
  548. * always returns ldisc == 0 */
  549. // set PPS line discipline
  550. ldisc = N_PPS; // 18 - the PPS line discipline
  551. if (0 > ioctl(device_fd, TIOCSETD, &ldisc)) {
  552. (void)printf("ERROR: ioctl(%s, TIOCSETD, 18) failed: %.80s(%d)\n",
  553. argv[1], strerror(errno), errno);
  554. }
  555. // try to find matching kpps device
  556. kpps_name = find_pps(device);
  557. if (NULL != kpps_name) {
  558. (void)snprintf(kpps_path, sizeof(kpps_path), "/dev/%s", kpps_name);
  559. }
  560. #endif // __linux__
  561. } else {
  562. (void)printf("INFO: ioctl(%s, TIOCMGET) failed: %.80s(%d)\n"
  563. "INFO: %s does not appear to be a tty\n",
  564. argv[1], strerror(errno), errno, argv[1]);
  565. is_tty = false;
  566. }
  567. #if defined(HAVE_SYS_TIMEPPS_H)
  568. // aka RFC2783
  569. if (0 == time_pps_create(device_fd, &kpps_handle)) {
  570. has_kpps = true;
  571. } else {
  572. (void)printf("WARNING: time_pps_create(%s)) failed: %.80s(%d)\n"
  573. "WARRING: %s does not appear to be a KPPS device\n",
  574. device, strerror(errno), errno, device);
  575. if ('\0' != kpps_path[0]) {
  576. // try kpps_path
  577. pps_fd = open(kpps_path, O_RDWR);
  578. if (-1 == pps_fd) {
  579. (void)printf("WARNING: open(%s) failed: %.80s(%d)\n",
  580. kpps_path, strerror(errno), errno);
  581. } else {
  582. (void)printf("INFO: matching %s opened\n", kpps_path);
  583. if (0 == time_pps_create(pps_fd, &kpps_handle)) {
  584. has_kpps = true;
  585. } else {
  586. (void)printf(
  587. "WARNING: time_pps_create(%s)) failed: %.80s(%d)\n"
  588. "WARRING: %s does not appear to be a KPPS device\n",
  589. kpps_path, strerror(errno), errno, device);
  590. }
  591. }
  592. }
  593. }
  594. if (!is_tty &&
  595. has_kpps) {
  596. do_kpps();
  597. // never returns
  598. }
  599. #else
  600. (void)puts("WARNING: KPPS not compiled in.");
  601. #endif
  602. if (!is_tty &&
  603. !has_kpps) {
  604. (void)printf("ERROR: %s is not a tty and does not support KPPS.\n",
  605. argv[1]);
  606. exit(EXIT_FAILURE);
  607. }
  608. // else is_tty && !has_kpps
  609. do_tty();
  610. // never returns
  611. // how could this happen?
  612. exit(EXIT_FAILURE);
  613. }
  614. // vim: set expandtab shiftwidth=4