tty_endrun.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. /* $OpenBSD: tty_endrun.c,v 1.6 2014/11/03 03:08:00 deraadt Exp $ */
  2. /*
  3. * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
  4. * Copyright (c) 2009 Kevin Steves <stevesk@openbsd.org>
  5. *
  6. * Permission to use, copy, modify, and distribute this software for any
  7. * purpose with or without fee is hereby granted, provided that the above
  8. * copyright notice and this permission notice appear in all copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. */
  18. /*
  19. * A tty line discipline to decode the EndRun Technologies native
  20. * time-of-day message.
  21. * http://www.endruntechnologies.com/
  22. */
  23. /*
  24. * EndRun Format:
  25. *
  26. * T YYYY DDD HH:MM:SS zZZ m<CR><LF>
  27. *
  28. * T is the Time Figure of Merit (TFOM) character (described below).
  29. * This is the on-time character, transmitted during the first
  30. * millisecond of each second.
  31. *
  32. * YYYY is the year
  33. * DDD is the day-of-year
  34. * : is the colon character (0x3A)
  35. * HH is the hour of the day
  36. * MM is the minute of the hour
  37. * SS is the second of the minute
  38. * z is the sign of the offset to UTC, + implies time is ahead of UTC.
  39. * ZZ is the magnitude of the offset to UTC in units of half-hours.
  40. * Non-zero only when the Timemode is Local.
  41. * m is the Timemode character and is one of:
  42. * G = GPS
  43. * L = Local
  44. * U = UTC
  45. * <CR> is the ASCII carriage return character (0x0D)
  46. * <LF> is the ASCII line feed character (0x0A)
  47. */
  48. #include <sys/param.h>
  49. #include <sys/systm.h>
  50. #include <sys/malloc.h>
  51. #include <sys/sensors.h>
  52. #include <sys/tty.h>
  53. #include <sys/conf.h>
  54. #include <sys/time.h>
  55. #ifdef ENDRUN_DEBUG
  56. #define DPRINTFN(n, x) do { if (endrundebug > (n)) printf x; } while (0)
  57. int endrundebug = 0;
  58. #else
  59. #define DPRINTFN(n, x)
  60. #endif
  61. #define DPRINTF(x) DPRINTFN(0, x)
  62. int endrunopen(dev_t, struct tty *, struct proc *p);
  63. int endrunclose(struct tty *, int, struct proc *p);
  64. int endruninput(int, struct tty *);
  65. void endrunattach(int);
  66. #define ENDRUNLEN 27 /* strlen("6 2009 018 20:41:17 +00 U\r\n") */
  67. #define NUMFLDS 6
  68. #ifdef ENDRUN_DEBUG
  69. #define TRUSTTIME 30
  70. #else
  71. #define TRUSTTIME (10 * 60) /* 10 minutes */
  72. #endif
  73. int endrun_count, endrun_nxid;
  74. struct endrun {
  75. char cbuf[ENDRUNLEN]; /* receive buffer */
  76. struct ksensor time; /* the timedelta sensor */
  77. struct ksensor signal; /* signal status */
  78. struct ksensordev timedev;
  79. struct timespec ts; /* current timestamp */
  80. struct timespec lts; /* timestamp of last TFOM */
  81. struct timeout endrun_tout; /* invalidate sensor */
  82. int64_t gap; /* gap between two sentences */
  83. int64_t last; /* last time rcvd */
  84. #define SYNC_SCAN 1 /* scanning for '\n' */
  85. #define SYNC_EOL 2 /* '\n' seen, next char TFOM */
  86. int sync;
  87. int pos; /* position in rcv buffer */
  88. int no_pps; /* no PPS although requested */
  89. #ifdef ENDRUN_DEBUG
  90. char tfom;
  91. #endif
  92. };
  93. /* EndRun decoding */
  94. void endrun_scan(struct endrun *, struct tty *);
  95. void endrun_decode(struct endrun *, struct tty *, char *fld[], int fldcnt);
  96. /* date and time conversion */
  97. int endrun_atoi(char *s, int len);
  98. int endrun_date_to_nano(char *s1, char *s2, int64_t *nano);
  99. int endrun_time_to_nano(char *s, int64_t *nano);
  100. int endrun_offset_to_nano(char *s, int64_t *nano);
  101. /* degrade the timedelta sensor */
  102. void endrun_timeout(void *);
  103. void
  104. endrunattach(int dummy)
  105. {
  106. }
  107. int
  108. endrunopen(dev_t dev, struct tty *tp, struct proc *p)
  109. {
  110. struct endrun *np;
  111. int error;
  112. DPRINTF(("endrunopen\n"));
  113. if (tp->t_line == ENDRUNDISC)
  114. return ENODEV;
  115. if ((error = suser(p, 0)) != 0)
  116. return error;
  117. np = malloc(sizeof(struct endrun), M_DEVBUF, M_WAITOK|M_ZERO);
  118. snprintf(np->timedev.xname, sizeof(np->timedev.xname), "endrun%d",
  119. endrun_nxid++);
  120. endrun_count++;
  121. np->time.status = SENSOR_S_UNKNOWN;
  122. np->time.type = SENSOR_TIMEDELTA;
  123. #ifndef ENDRUN_DEBUG
  124. np->time.flags = SENSOR_FINVALID;
  125. #endif
  126. sensor_attach(&np->timedev, &np->time);
  127. np->signal.type = SENSOR_PERCENT;
  128. np->signal.status = SENSOR_S_UNKNOWN;
  129. np->signal.value = 100000LL;
  130. strlcpy(np->signal.desc, "Signal", sizeof(np->signal.desc));
  131. sensor_attach(&np->timedev, &np->signal);
  132. np->sync = SYNC_SCAN;
  133. #ifdef ENDRUN_DEBUG
  134. np->tfom = '0';
  135. #endif
  136. tp->t_sc = (caddr_t)np;
  137. error = linesw[TTYDISC].l_open(dev, tp, p);
  138. if (error) {
  139. free(np, M_DEVBUF, sizeof(*np));
  140. tp->t_sc = NULL;
  141. } else {
  142. sensordev_install(&np->timedev);
  143. timeout_set(&np->endrun_tout, endrun_timeout, np);
  144. }
  145. return error;
  146. }
  147. int
  148. endrunclose(struct tty *tp, int flags, struct proc *p)
  149. {
  150. struct endrun *np = (struct endrun *)tp->t_sc;
  151. DPRINTF(("endrunclose\n"));
  152. tp->t_line = TTYDISC; /* switch back to termios */
  153. timeout_del(&np->endrun_tout);
  154. sensordev_deinstall(&np->timedev);
  155. free(np, M_DEVBUF, sizeof(*np));
  156. tp->t_sc = NULL;
  157. endrun_count--;
  158. if (endrun_count == 0)
  159. endrun_nxid = 0;
  160. return linesw[TTYDISC].l_close(tp, flags, p);
  161. }
  162. /* collect EndRun sentence from tty */
  163. int
  164. endruninput(int c, struct tty *tp)
  165. {
  166. struct endrun *np = (struct endrun *)tp->t_sc;
  167. struct timespec ts;
  168. int64_t gap;
  169. long tmin, tmax;
  170. if (np->sync == SYNC_EOL) {
  171. nanotime(&ts);
  172. np->pos = 0;
  173. np->sync = SYNC_SCAN;
  174. np->cbuf[np->pos++] = c; /* TFOM char */
  175. gap = (ts.tv_sec * 1000000000LL + ts.tv_nsec) -
  176. (np->lts.tv_sec * 1000000000LL + np->lts.tv_nsec);
  177. np->lts.tv_sec = ts.tv_sec;
  178. np->lts.tv_nsec = ts.tv_nsec;
  179. if (gap <= np->gap)
  180. goto nogap;
  181. np->ts.tv_sec = ts.tv_sec;
  182. np->ts.tv_nsec = ts.tv_nsec;
  183. np->gap = gap;
  184. /*
  185. * If a tty timestamp is available, make sure its value is
  186. * reasonable by comparing against the timestamp just taken.
  187. * If they differ by more than 2 seconds, assume no PPS signal
  188. * is present, note the fact, and keep using the timestamp
  189. * value. When this happens, the sensor state is set to
  190. * CRITICAL later when the EndRun sentence is decoded.
  191. */
  192. if (tp->t_flags & (TS_TSTAMPDCDSET | TS_TSTAMPDCDCLR |
  193. TS_TSTAMPCTSSET | TS_TSTAMPCTSCLR)) {
  194. tmax = lmax(np->ts.tv_sec, tp->t_tv.tv_sec);
  195. tmin = lmin(np->ts.tv_sec, tp->t_tv.tv_sec);
  196. if (tmax - tmin > 1)
  197. np->no_pps = 1;
  198. else {
  199. np->ts.tv_sec = tp->t_tv.tv_sec;
  200. np->ts.tv_nsec = tp->t_tv.tv_usec *
  201. 1000L;
  202. np->no_pps = 0;
  203. }
  204. }
  205. } else if (c == '\n') {
  206. if (np->pos == ENDRUNLEN - 1) {
  207. /* don't copy '\n' into cbuf */
  208. np->cbuf[np->pos] = '\0';
  209. endrun_scan(np, tp);
  210. }
  211. np->sync = SYNC_EOL;
  212. } else {
  213. if (np->pos < ENDRUNLEN - 1)
  214. np->cbuf[np->pos++] = c;
  215. }
  216. nogap:
  217. /* pass data to termios */
  218. return linesw[TTYDISC].l_rint(c, tp);
  219. }
  220. /* Scan the EndRun sentence just received */
  221. void
  222. endrun_scan(struct endrun *np, struct tty *tp)
  223. {
  224. int fldcnt = 0, n;
  225. char *fld[NUMFLDS], *cs;
  226. DPRINTFN(1, ("%s\n", np->cbuf));
  227. /* split into fields */
  228. fld[fldcnt++] = &np->cbuf[0];
  229. for (cs = NULL, n = 0; n < np->pos && cs == NULL; n++) {
  230. switch (np->cbuf[n]) {
  231. case '\r':
  232. np->cbuf[n] = '\0';
  233. cs = &np->cbuf[n + 1];
  234. break;
  235. case ' ':
  236. if (fldcnt < NUMFLDS) {
  237. np->cbuf[n] = '\0';
  238. fld[fldcnt++] = &np->cbuf[n + 1];
  239. } else {
  240. DPRINTF(("endrun: nr of fields in sentence "
  241. "exceeds expected: %d\n", NUMFLDS));
  242. return;
  243. }
  244. break;
  245. }
  246. }
  247. endrun_decode(np, tp, fld, fldcnt);
  248. }
  249. /* Decode the time string */
  250. void
  251. endrun_decode(struct endrun *np, struct tty *tp, char *fld[], int fldcnt)
  252. {
  253. int64_t date_nano, time_nano, offset_nano, endrun_now;
  254. char tfom;
  255. int jumped = 0;
  256. if (fldcnt != NUMFLDS) {
  257. DPRINTF(("endrun: field count mismatch, %d\n", fldcnt));
  258. return;
  259. }
  260. if (endrun_time_to_nano(fld[3], &time_nano) == -1) {
  261. DPRINTF(("endrun: illegal time, %s\n", fld[3]));
  262. return;
  263. }
  264. if (endrun_date_to_nano(fld[1], fld[2], &date_nano) == -1) {
  265. DPRINTF(("endrun: illegal date, %s %s\n", fld[1], fld[2]));
  266. return;
  267. }
  268. offset_nano = 0;
  269. /* only parse offset when timemode is local */
  270. if (fld[5][0] == 'L' &&
  271. endrun_offset_to_nano(fld[4], &offset_nano) == -1) {
  272. DPRINTF(("endrun: illegal offset, %s\n", fld[4]));
  273. return;
  274. }
  275. endrun_now = date_nano + time_nano + offset_nano;
  276. if (endrun_now <= np->last) {
  277. DPRINTF(("endrun: time not monotonically increasing "
  278. "last %lld now %lld\n",
  279. (long long)np->last, (long long)endrun_now));
  280. jumped = 1;
  281. }
  282. np->last = endrun_now;
  283. np->gap = 0LL;
  284. #ifdef ENDRUN_DEBUG
  285. if (np->time.status == SENSOR_S_UNKNOWN) {
  286. np->time.status = SENSOR_S_OK;
  287. timeout_add_sec(&np->endrun_tout, TRUSTTIME);
  288. }
  289. #endif
  290. np->time.value = np->ts.tv_sec * 1000000000LL +
  291. np->ts.tv_nsec - endrun_now;
  292. np->time.tv.tv_sec = np->ts.tv_sec;
  293. np->time.tv.tv_usec = np->ts.tv_nsec / 1000L;
  294. if (np->time.status == SENSOR_S_UNKNOWN) {
  295. np->time.status = SENSOR_S_OK;
  296. np->time.flags &= ~SENSOR_FINVALID;
  297. strlcpy(np->time.desc, "EndRun", sizeof(np->time.desc));
  298. }
  299. /*
  300. * Only update the timeout if the clock reports the time as valid.
  301. *
  302. * Time Figure Of Merit (TFOM) values:
  303. *
  304. * 6 - time error is < 100 us
  305. * 7 - time error is < 1 ms
  306. * 8 - time error is < 10 ms
  307. * 9 - time error is > 10 ms,
  308. * unsynchronized state if never locked to CDMA
  309. */
  310. switch (tfom = fld[0][0]) {
  311. case '6':
  312. case '7':
  313. case '8':
  314. np->time.status = SENSOR_S_OK;
  315. np->signal.status = SENSOR_S_OK;
  316. break;
  317. case '9':
  318. np->signal.status = SENSOR_S_WARN;
  319. break;
  320. default:
  321. DPRINTF(("endrun: invalid TFOM: '%c'\n", tfom));
  322. np->signal.status = SENSOR_S_CRIT;
  323. break;
  324. }
  325. #ifdef ENDRUN_DEBUG
  326. if (np->tfom != tfom) {
  327. DPRINTF(("endrun: TFOM changed from %c to %c\n",
  328. np->tfom, tfom));
  329. np->tfom = tfom;
  330. }
  331. #endif
  332. if (jumped)
  333. np->time.status = SENSOR_S_WARN;
  334. if (np->time.status == SENSOR_S_OK)
  335. timeout_add_sec(&np->endrun_tout, TRUSTTIME);
  336. /*
  337. * If tty timestamping is requested, but no PPS signal is present, set
  338. * the sensor state to CRITICAL.
  339. */
  340. if (np->no_pps)
  341. np->time.status = SENSOR_S_CRIT;
  342. }
  343. int
  344. endrun_atoi(char *s, int len)
  345. {
  346. int n;
  347. char *p;
  348. /* make sure the input contains only numbers */
  349. for (n = 0, p = s; n < len && *p && *p >= '0' && *p <= '9'; n++, p++)
  350. ;
  351. if (n != len || *p != '\0')
  352. return -1;
  353. for (n = 0; *s; s++)
  354. n = n * 10 + *s - '0';
  355. return n;
  356. }
  357. /*
  358. * Convert date fields from EndRun to nanoseconds since the epoch.
  359. * The year string must be of the form YYYY .
  360. * The day of year string must be of the form DDD .
  361. * Return 0 on success, -1 if illegal characters are encountered.
  362. */
  363. int
  364. endrun_date_to_nano(char *y, char *doy, int64_t *nano)
  365. {
  366. struct clock_ymdhms clock;
  367. time_t secs;
  368. int n, i;
  369. int year_days = 365;
  370. int month_days[] = {
  371. 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  372. };
  373. #define FEBRUARY 2
  374. #define LEAPYEAR(x) \
  375. ((x) % 4 == 0 && \
  376. (x) % 100 != 0) || \
  377. (x) % 400 == 0
  378. if ((n = endrun_atoi(y, 4)) == -1)
  379. return -1;
  380. clock.dt_year = n;
  381. if (LEAPYEAR(n)) {
  382. month_days[FEBRUARY]++;
  383. year_days++;
  384. }
  385. if ((n = endrun_atoi(doy, 3)) == -1 || n == 0 || n > year_days)
  386. return -1;
  387. /* convert day of year to month, day */
  388. for (i = 1; n > month_days[i]; i++) {
  389. n -= month_days[i];
  390. }
  391. clock.dt_mon = i;
  392. clock.dt_day = n;
  393. DPRINTFN(1, ("mm/dd %d/%d\n", i, n));
  394. clock.dt_hour = clock.dt_min = clock.dt_sec = 0;
  395. secs = clock_ymdhms_to_secs(&clock);
  396. *nano = secs * 1000000000LL;
  397. return 0;
  398. }
  399. /*
  400. * Convert time field from EndRun to nanoseconds since midnight.
  401. * The string must be of the form HH:MM:SS .
  402. * Return 0 on success, -1 if illegal characters are encountered.
  403. */
  404. int
  405. endrun_time_to_nano(char *s, int64_t *nano)
  406. {
  407. struct clock_ymdhms clock;
  408. time_t secs;
  409. int n;
  410. if (s[2] != ':' || s[5] != ':')
  411. return -1;
  412. s[2] = '\0';
  413. s[5] = '\0';
  414. if ((n = endrun_atoi(&s[0], 2)) == -1 || n > 23)
  415. return -1;
  416. clock.dt_hour = n;
  417. if ((n = endrun_atoi(&s[3], 2)) == -1 || n > 59)
  418. return -1;
  419. clock.dt_min = n;
  420. if ((n = endrun_atoi(&s[6], 2)) == -1 || n > 60)
  421. return -1;
  422. clock.dt_sec = n;
  423. DPRINTFN(1, ("hh:mm:ss %d:%d:%d\n", (int)clock.dt_hour,
  424. (int)clock.dt_min,
  425. (int)clock.dt_sec));
  426. secs = clock.dt_hour * 3600
  427. + clock.dt_min * 60
  428. + clock.dt_sec;
  429. DPRINTFN(1, ("secs %lu\n", (unsigned long)secs));
  430. *nano = secs * 1000000000LL;
  431. return 0;
  432. }
  433. int
  434. endrun_offset_to_nano(char *s, int64_t *nano)
  435. {
  436. time_t secs;
  437. int n;
  438. if (!(s[0] == '+' || s[0] == '-'))
  439. return -1;
  440. if ((n = endrun_atoi(&s[1], 2)) == -1)
  441. return -1;
  442. secs = n * 30 * 60;
  443. *nano = secs * 1000000000LL;
  444. if (s[0] == '+')
  445. *nano = -*nano;
  446. DPRINTFN(1, ("offset secs %lu nanosecs %lld\n",
  447. (unsigned long)secs, (long long)*nano));
  448. return 0;
  449. }
  450. /*
  451. * Degrade the sensor state if we received no EndRun string for more than
  452. * TRUSTTIME seconds.
  453. */
  454. void
  455. endrun_timeout(void *xnp)
  456. {
  457. struct endrun *np = xnp;
  458. if (np->time.status == SENSOR_S_OK) {
  459. np->time.status = SENSOR_S_WARN;
  460. /*
  461. * further degrade in TRUSTTIME seconds if no new valid EndRun
  462. * strings are received.
  463. */
  464. timeout_add_sec(&np->endrun_tout, TRUSTTIME);
  465. } else
  466. np->time.status = SENSOR_S_CRIT;
  467. }