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