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