tty_msts.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. /* $OpenBSD: tty_msts.c,v 1.19 2014/11/03 03:08:00 deraadt Exp $ */
  2. /*
  3. * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. /*
  18. * A tty line discipline to decode the Meinberg Standard Time String
  19. * to get the time (http://www.meinberg.de/english/specs/timestr.htm).
  20. */
  21. #include <sys/param.h>
  22. #include <sys/systm.h>
  23. #include <sys/malloc.h>
  24. #include <sys/sensors.h>
  25. #include <sys/tty.h>
  26. #include <sys/conf.h>
  27. #include <sys/time.h>
  28. #ifdef MSTS_DEBUG
  29. #define DPRINTFN(n, x) do { if (mstsdebug > (n)) printf x; } while (0)
  30. int mstsdebug = 0;
  31. #else
  32. #define DPRINTFN(n, x)
  33. #endif
  34. #define DPRINTF(x) DPRINTFN(0, x)
  35. int mstsopen(dev_t, struct tty *, struct proc *);
  36. int mstsclose(struct tty *, int, struct proc *);
  37. int mstsinput(int, struct tty *);
  38. void mstsattach(int);
  39. #define MSTSMAX 32
  40. #define MAXFLDS 4
  41. #ifdef MSTS_DEBUG
  42. #define TRUSTTIME 30
  43. #else
  44. #define TRUSTTIME (10 * 60) /* 10 minutes */
  45. #endif
  46. int msts_count, msts_nxid;
  47. struct msts {
  48. char cbuf[MSTSMAX]; /* receive buffer */
  49. struct ksensor time; /* the timedelta sensor */
  50. struct ksensor signal; /* signal status */
  51. struct ksensordev timedev;
  52. struct timespec ts; /* current timestamp */
  53. struct timespec lts; /* timestamp of last <STX> */
  54. struct timeout msts_tout; /* invalidate sensor */
  55. int64_t gap; /* gap between two sentences */
  56. int64_t last; /* last time rcvd */
  57. int sync; /* if 1, waiting for <STX> */
  58. int pos; /* position in rcv buffer */
  59. int no_pps; /* no PPS although requested */
  60. };
  61. /* MSTS decoding */
  62. void msts_scan(struct msts *, struct tty *);
  63. void msts_decode(struct msts *, struct tty *, char *fld[], int fldcnt);
  64. /* date and time conversion */
  65. int msts_date_to_nano(char *s, int64_t *nano);
  66. int msts_time_to_nano(char *s, int64_t *nano);
  67. /* degrade the timedelta sensor */
  68. void msts_timeout(void *);
  69. void
  70. mstsattach(int dummy)
  71. {
  72. }
  73. int
  74. mstsopen(dev_t dev, struct tty *tp, struct proc *p)
  75. {
  76. struct msts *np;
  77. int error;
  78. DPRINTF(("mstsopen\n"));
  79. if (tp->t_line == MSTSDISC)
  80. return ENODEV;
  81. if ((error = suser(p, 0)) != 0)
  82. return error;
  83. np = malloc(sizeof(struct msts), M_DEVBUF, M_WAITOK|M_ZERO);
  84. snprintf(np->timedev.xname, sizeof(np->timedev.xname), "msts%d",
  85. msts_nxid++);
  86. msts_count++;
  87. np->time.status = SENSOR_S_UNKNOWN;
  88. np->time.type = SENSOR_TIMEDELTA;
  89. #ifndef MSTS_DEBUG
  90. np->time.flags = SENSOR_FINVALID;
  91. #endif
  92. sensor_attach(&np->timedev, &np->time);
  93. np->signal.type = SENSOR_PERCENT;
  94. np->signal.status = SENSOR_S_UNKNOWN;
  95. np->signal.value = 100000LL;
  96. strlcpy(np->signal.desc, "Signal", sizeof(np->signal.desc));
  97. sensor_attach(&np->timedev, &np->signal);
  98. np->sync = 1;
  99. tp->t_sc = (caddr_t)np;
  100. error = linesw[TTYDISC].l_open(dev, tp, p);
  101. if (error) {
  102. free(np, M_DEVBUF, sizeof(*np));
  103. tp->t_sc = NULL;
  104. } else {
  105. sensordev_install(&np->timedev);
  106. timeout_set(&np->msts_tout, msts_timeout, np);
  107. }
  108. return error;
  109. }
  110. int
  111. mstsclose(struct tty *tp, int flags, struct proc *p)
  112. {
  113. struct msts *np = (struct msts *)tp->t_sc;
  114. tp->t_line = TTYDISC; /* switch back to termios */
  115. timeout_del(&np->msts_tout);
  116. sensordev_deinstall(&np->timedev);
  117. free(np, M_DEVBUF, sizeof(*np));
  118. tp->t_sc = NULL;
  119. msts_count--;
  120. if (msts_count == 0)
  121. msts_nxid = 0;
  122. return linesw[TTYDISC].l_close(tp, flags, p);
  123. }
  124. /* collect MSTS sentence from tty */
  125. int
  126. mstsinput(int c, struct tty *tp)
  127. {
  128. struct msts *np = (struct msts *)tp->t_sc;
  129. struct timespec ts;
  130. int64_t gap;
  131. long tmin, tmax;
  132. switch (c) {
  133. case 2: /* ASCII <STX> */
  134. nanotime(&ts);
  135. np->pos = np->sync = 0;
  136. gap = (ts.tv_sec * 1000000000LL + ts.tv_nsec) -
  137. (np->lts.tv_sec * 1000000000LL + np->lts.tv_nsec);
  138. np->lts.tv_sec = ts.tv_sec;
  139. np->lts.tv_nsec = ts.tv_nsec;
  140. if (gap <= np->gap)
  141. break;
  142. np->ts.tv_sec = ts.tv_sec;
  143. np->ts.tv_nsec = ts.tv_nsec;
  144. np->gap = gap;
  145. /*
  146. * If a tty timestamp is available, make sure its value is
  147. * reasonable by comparing against the timestamp just taken.
  148. * If they differ by more than 2 seconds, assume no PPS signal
  149. * is present, note the fact, and keep using the timestamp
  150. * value. When this happens, the sensor state is set to
  151. * CRITICAL later when the MSTS sentence is decoded.
  152. */
  153. if (tp->t_flags & (TS_TSTAMPDCDSET | TS_TSTAMPDCDCLR |
  154. TS_TSTAMPCTSSET | TS_TSTAMPCTSCLR)) {
  155. tmax = lmax(np->ts.tv_sec, tp->t_tv.tv_sec);
  156. tmin = lmin(np->ts.tv_sec, tp->t_tv.tv_sec);
  157. if (tmax - tmin > 1)
  158. np->no_pps = 1;
  159. else {
  160. np->ts.tv_sec = tp->t_tv.tv_sec;
  161. np->ts.tv_nsec = tp->t_tv.tv_usec *
  162. 1000L;
  163. np->no_pps = 0;
  164. }
  165. }
  166. break;
  167. case 3: /* ASCII <ETX> */
  168. if (!np->sync) {
  169. np->cbuf[np->pos] = '\0';
  170. msts_scan(np, tp);
  171. np->sync = 1;
  172. }
  173. break;
  174. default:
  175. if (!np->sync && np->pos < (MSTSMAX - 1))
  176. np->cbuf[np->pos++] = c;
  177. break;
  178. }
  179. /* pass data to termios */
  180. return linesw[TTYDISC].l_rint(c, tp);
  181. }
  182. /* Scan the MSTS sentence just received */
  183. void
  184. msts_scan(struct msts *np, struct tty *tp)
  185. {
  186. int fldcnt = 0, n;
  187. char *fld[MAXFLDS], *cs;
  188. /* split into fields */
  189. fld[fldcnt++] = &np->cbuf[0];
  190. for (cs = NULL, n = 0; n < np->pos && cs == NULL; n++) {
  191. switch (np->cbuf[n]) {
  192. case 3: /* ASCII <ETX> */
  193. np->cbuf[n] = '\0';
  194. cs = &np->cbuf[n + 1];
  195. break;
  196. case ';':
  197. if (fldcnt < MAXFLDS) {
  198. np->cbuf[n] = '\0';
  199. fld[fldcnt++] = &np->cbuf[n + 1];
  200. } else {
  201. DPRINTF(("nr of fields in sentence exceeds "
  202. "maximum of %d\n", MAXFLDS));
  203. return;
  204. }
  205. break;
  206. }
  207. }
  208. msts_decode(np, tp, fld, fldcnt);
  209. }
  210. /* Decode the time string */
  211. void
  212. msts_decode(struct msts *np, struct tty *tp, char *fld[], int fldcnt)
  213. {
  214. int64_t date_nano, time_nano, msts_now;
  215. int jumped = 0;
  216. if (fldcnt != MAXFLDS) {
  217. DPRINTF(("msts: field count mismatch, %d\n", fldcnt));
  218. return;
  219. }
  220. if (msts_time_to_nano(fld[2], &time_nano)) {
  221. DPRINTF(("msts: illegal time, %s\n", fld[2]));
  222. return;
  223. }
  224. if (msts_date_to_nano(fld[0], &date_nano)) {
  225. DPRINTF(("msts: illegal date, %s\n", fld[0]));
  226. return;
  227. }
  228. msts_now = date_nano + time_nano;
  229. if ( fld[3][2] == ' ' ) /* received time in CET */
  230. msts_now = msts_now - 3600 * 1000000000LL;
  231. if ( fld[3][2] == 'S' ) /* received time in CEST */
  232. msts_now = msts_now - 2 * 3600 * 1000000000LL;
  233. if (msts_now <= np->last) {
  234. DPRINTF(("msts: time not monotonically increasing\n"));
  235. jumped = 1;
  236. }
  237. np->last = msts_now;
  238. np->gap = 0LL;
  239. #ifdef MSTS_DEBUG
  240. if (np->time.status == SENSOR_S_UNKNOWN) {
  241. np->time.status = SENSOR_S_OK;
  242. timeout_add_sec(&np->msts_tout, TRUSTTIME);
  243. }
  244. #endif
  245. np->time.value = np->ts.tv_sec * 1000000000LL +
  246. np->ts.tv_nsec - msts_now;
  247. np->time.tv.tv_sec = np->ts.tv_sec;
  248. np->time.tv.tv_usec = np->ts.tv_nsec / 1000L;
  249. if (np->time.status == SENSOR_S_UNKNOWN) {
  250. np->time.status = SENSOR_S_OK;
  251. np->time.flags &= ~SENSOR_FINVALID;
  252. strlcpy(np->time.desc, "MSTS", sizeof(np->time.desc));
  253. }
  254. /*
  255. * only update the timeout if the clock reports the time a valid,
  256. * the status is reported in fld[3][0] and fld[3][1] as follows:
  257. * fld[3][0] == '#' critical
  258. * fld[3][0] == ' ' && fld[3][1] == '*' warning
  259. * fld[3][0] == ' ' && fld[3][1] == ' ' ok
  260. */
  261. if (fld[3][0] == ' ' && fld[3][1] == ' ') {
  262. np->time.status = SENSOR_S_OK;
  263. np->signal.status = SENSOR_S_OK;
  264. } else
  265. np->signal.status = SENSOR_S_WARN;
  266. if (jumped)
  267. np->time.status = SENSOR_S_WARN;
  268. if (np->time.status == SENSOR_S_OK)
  269. timeout_add_sec(&np->msts_tout, TRUSTTIME);
  270. /*
  271. * If tty timestamping is requested, but no PPS signal is present, set
  272. * the sensor state to CRITICAL.
  273. */
  274. if (np->no_pps)
  275. np->time.status = SENSOR_S_CRIT;
  276. }
  277. /*
  278. * Convert date field from MSTS to nanoseconds since the epoch.
  279. * The string must be of the form D:DD.MM.YY .
  280. * Return 0 on success, -1 if illegal characters are encountered.
  281. */
  282. int
  283. msts_date_to_nano(char *s, int64_t *nano)
  284. {
  285. struct clock_ymdhms ymd;
  286. time_t secs;
  287. char *p;
  288. int n;
  289. if (s[0] != 'D' || s[1] != ':' || s[4] != '.' || s[7] != '.')
  290. return -1;
  291. /* shift numbers to DDMMYY */
  292. s[0]=s[2];
  293. s[1]=s[3];
  294. s[2]=s[5];
  295. s[3]=s[6];
  296. s[4]=s[8];
  297. s[5]=s[9];
  298. s[6]='\0';
  299. /* make sure the input contains only numbers and is six digits long */
  300. for (n = 0, p = s; n < 6 && *p && *p >= '0' && *p <= '9'; n++, p++)
  301. ;
  302. if (n != 6 || (*p != '\0'))
  303. return -1;
  304. ymd.dt_year = 2000 + (s[4] - '0') * 10 + (s[5] - '0');
  305. ymd.dt_mon = (s[2] - '0') * 10 + (s[3] - '0');
  306. ymd.dt_day = (s[0] - '0') * 10 + (s[1] - '0');
  307. ymd.dt_hour = ymd.dt_min = ymd.dt_sec = 0;
  308. secs = clock_ymdhms_to_secs(&ymd);
  309. *nano = secs * 1000000000LL;
  310. return 0;
  311. }
  312. /*
  313. * Convert time field from MSTS to nanoseconds since midnight.
  314. * The string must be of the form U:HH.MM.SS .
  315. * Return 0 on success, -1 if illegal characters are encountered.
  316. */
  317. int
  318. msts_time_to_nano(char *s, int64_t *nano)
  319. {
  320. long fac = 36000L, div = 6L, secs = 0L;
  321. char ul = '2';
  322. int n;
  323. if (s[0] != 'U' || s[1] != ':' || s[4] != '.' || s[7] != '.')
  324. return -1;
  325. /* shift numbers to HHMMSS */
  326. s[0]=s[2];
  327. s[1]=s[3];
  328. s[2]=s[5];
  329. s[3]=s[6];
  330. s[4]=s[8];
  331. s[5]=s[9];
  332. s[6]='\0';
  333. for (n = 0, secs = 0; fac && *s && *s >= '0' && *s <= ul; s++, n++) {
  334. secs += (*s - '0') * fac;
  335. div = 16 - div;
  336. fac /= div;
  337. switch (n) {
  338. case 0:
  339. if (*s <= '1')
  340. ul = '9';
  341. else
  342. ul = '3';
  343. break;
  344. case 1:
  345. case 3:
  346. ul = '5';
  347. break;
  348. case 2:
  349. case 4:
  350. ul = '9';
  351. break;
  352. }
  353. }
  354. if (fac)
  355. return -1;
  356. if (*s != '\0')
  357. return -1;
  358. *nano = secs * 1000000000LL;
  359. return 0;
  360. }
  361. /*
  362. * Degrade the sensor state if we received no MSTS string for more than
  363. * TRUSTTIME seconds.
  364. */
  365. void
  366. msts_timeout(void *xnp)
  367. {
  368. struct msts *np = xnp;
  369. if (np->time.status == SENSOR_S_OK) {
  370. np->time.status = SENSOR_S_WARN;
  371. /*
  372. * further degrade in TRUSTTIME seconds if no new valid MSTS
  373. * strings are received.
  374. */
  375. timeout_add_sec(&np->msts_tout, TRUSTTIME);
  376. } else
  377. np->time.status = SENSOR_S_CRIT;
  378. }