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