1*3e676399Smpi /* $OpenBSD: tty_endrun.c,v 1.8 2018/02/19 08:59:52 mpi Exp $ */
261406af6Sstevesk
361406af6Sstevesk /*
461406af6Sstevesk * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
561406af6Sstevesk * Copyright (c) 2009 Kevin Steves <stevesk@openbsd.org>
661406af6Sstevesk *
761406af6Sstevesk * Permission to use, copy, modify, and distribute this software for any
861406af6Sstevesk * purpose with or without fee is hereby granted, provided that the above
961406af6Sstevesk * copyright notice and this permission notice appear in all copies.
1061406af6Sstevesk *
1161406af6Sstevesk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1261406af6Sstevesk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1361406af6Sstevesk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1461406af6Sstevesk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1561406af6Sstevesk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1661406af6Sstevesk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1761406af6Sstevesk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1861406af6Sstevesk */
1961406af6Sstevesk
2061406af6Sstevesk /*
2161406af6Sstevesk * A tty line discipline to decode the EndRun Technologies native
2261406af6Sstevesk * time-of-day message.
2361406af6Sstevesk * http://www.endruntechnologies.com/
2461406af6Sstevesk */
2561406af6Sstevesk
2661406af6Sstevesk /*
2761406af6Sstevesk * EndRun Format:
2861406af6Sstevesk *
2961406af6Sstevesk * T YYYY DDD HH:MM:SS zZZ m<CR><LF>
3061406af6Sstevesk *
3161406af6Sstevesk * T is the Time Figure of Merit (TFOM) character (described below).
3261406af6Sstevesk * This is the on-time character, transmitted during the first
3361406af6Sstevesk * millisecond of each second.
3461406af6Sstevesk *
3561406af6Sstevesk * YYYY is the year
3661406af6Sstevesk * DDD is the day-of-year
3761406af6Sstevesk * : is the colon character (0x3A)
3861406af6Sstevesk * HH is the hour of the day
3961406af6Sstevesk * MM is the minute of the hour
4061406af6Sstevesk * SS is the second of the minute
4161406af6Sstevesk * z is the sign of the offset to UTC, + implies time is ahead of UTC.
4261406af6Sstevesk * ZZ is the magnitude of the offset to UTC in units of half-hours.
4361406af6Sstevesk * Non-zero only when the Timemode is Local.
4461406af6Sstevesk * m is the Timemode character and is one of:
4561406af6Sstevesk * G = GPS
4661406af6Sstevesk * L = Local
4761406af6Sstevesk * U = UTC
4861406af6Sstevesk * <CR> is the ASCII carriage return character (0x0D)
4961406af6Sstevesk * <LF> is the ASCII line feed character (0x0A)
5061406af6Sstevesk */
5161406af6Sstevesk
5261406af6Sstevesk #include <sys/param.h>
5361406af6Sstevesk #include <sys/systm.h>
5461406af6Sstevesk #include <sys/malloc.h>
5561406af6Sstevesk #include <sys/sensors.h>
5661406af6Sstevesk #include <sys/tty.h>
5761406af6Sstevesk #include <sys/conf.h>
5861406af6Sstevesk #include <sys/time.h>
5961406af6Sstevesk
6061406af6Sstevesk #ifdef ENDRUN_DEBUG
6161406af6Sstevesk #define DPRINTFN(n, x) do { if (endrundebug > (n)) printf x; } while (0)
6261406af6Sstevesk int endrundebug = 0;
6361406af6Sstevesk #else
6461406af6Sstevesk #define DPRINTFN(n, x)
6561406af6Sstevesk #endif
6661406af6Sstevesk #define DPRINTF(x) DPRINTFN(0, x)
6761406af6Sstevesk
6861406af6Sstevesk void endrunattach(int);
6961406af6Sstevesk
7061406af6Sstevesk #define ENDRUNLEN 27 /* strlen("6 2009 018 20:41:17 +00 U\r\n") */
7161406af6Sstevesk #define NUMFLDS 6
7261406af6Sstevesk #ifdef ENDRUN_DEBUG
7361406af6Sstevesk #define TRUSTTIME 30
7461406af6Sstevesk #else
7561406af6Sstevesk #define TRUSTTIME (10 * 60) /* 10 minutes */
7661406af6Sstevesk #endif
7761406af6Sstevesk
7861406af6Sstevesk int endrun_count, endrun_nxid;
7961406af6Sstevesk
8061406af6Sstevesk struct endrun {
8161406af6Sstevesk char cbuf[ENDRUNLEN]; /* receive buffer */
8261406af6Sstevesk struct ksensor time; /* the timedelta sensor */
8361406af6Sstevesk struct ksensor signal; /* signal status */
8461406af6Sstevesk struct ksensordev timedev;
8561406af6Sstevesk struct timespec ts; /* current timestamp */
8661406af6Sstevesk struct timespec lts; /* timestamp of last TFOM */
8761406af6Sstevesk struct timeout endrun_tout; /* invalidate sensor */
8861406af6Sstevesk int64_t gap; /* gap between two sentences */
8961406af6Sstevesk int64_t last; /* last time rcvd */
9061406af6Sstevesk #define SYNC_SCAN 1 /* scanning for '\n' */
9161406af6Sstevesk #define SYNC_EOL 2 /* '\n' seen, next char TFOM */
9261406af6Sstevesk int sync;
9361406af6Sstevesk int pos; /* position in rcv buffer */
9461406af6Sstevesk int no_pps; /* no PPS although requested */
9561406af6Sstevesk #ifdef ENDRUN_DEBUG
9661406af6Sstevesk char tfom;
9761406af6Sstevesk #endif
9861406af6Sstevesk };
9961406af6Sstevesk
10061406af6Sstevesk /* EndRun decoding */
10161406af6Sstevesk void endrun_scan(struct endrun *, struct tty *);
10261406af6Sstevesk void endrun_decode(struct endrun *, struct tty *, char *fld[], int fldcnt);
10361406af6Sstevesk
10461406af6Sstevesk /* date and time conversion */
10561406af6Sstevesk int endrun_atoi(char *s, int len);
10661406af6Sstevesk int endrun_date_to_nano(char *s1, char *s2, int64_t *nano);
10761406af6Sstevesk int endrun_time_to_nano(char *s, int64_t *nano);
10861406af6Sstevesk int endrun_offset_to_nano(char *s, int64_t *nano);
10961406af6Sstevesk
11061406af6Sstevesk /* degrade the timedelta sensor */
11161406af6Sstevesk void endrun_timeout(void *);
11261406af6Sstevesk
11361406af6Sstevesk void
endrunattach(int dummy)11461406af6Sstevesk endrunattach(int dummy)
11561406af6Sstevesk {
11661406af6Sstevesk }
11761406af6Sstevesk
11861406af6Sstevesk int
endrunopen(dev_t dev,struct tty * tp,struct proc * p)11979f6c33aStedu endrunopen(dev_t dev, struct tty *tp, struct proc *p)
12061406af6Sstevesk {
12161406af6Sstevesk struct endrun *np;
12261406af6Sstevesk int error;
12361406af6Sstevesk
12461406af6Sstevesk DPRINTF(("endrunopen\n"));
12561406af6Sstevesk if (tp->t_line == ENDRUNDISC)
12661406af6Sstevesk return ENODEV;
127*3e676399Smpi if ((error = suser(p)) != 0)
12861406af6Sstevesk return error;
12961406af6Sstevesk np = malloc(sizeof(struct endrun), M_DEVBUF, M_WAITOK|M_ZERO);
13061406af6Sstevesk snprintf(np->timedev.xname, sizeof(np->timedev.xname), "endrun%d",
13161406af6Sstevesk endrun_nxid++);
13261406af6Sstevesk endrun_count++;
13361406af6Sstevesk np->time.status = SENSOR_S_UNKNOWN;
13461406af6Sstevesk np->time.type = SENSOR_TIMEDELTA;
13561406af6Sstevesk #ifndef ENDRUN_DEBUG
13661406af6Sstevesk np->time.flags = SENSOR_FINVALID;
13761406af6Sstevesk #endif
13861406af6Sstevesk sensor_attach(&np->timedev, &np->time);
13961406af6Sstevesk
14061406af6Sstevesk np->signal.type = SENSOR_PERCENT;
14161406af6Sstevesk np->signal.status = SENSOR_S_UNKNOWN;
14261406af6Sstevesk np->signal.value = 100000LL;
14361406af6Sstevesk strlcpy(np->signal.desc, "Signal", sizeof(np->signal.desc));
14461406af6Sstevesk sensor_attach(&np->timedev, &np->signal);
14561406af6Sstevesk
14661406af6Sstevesk np->sync = SYNC_SCAN;
14761406af6Sstevesk #ifdef ENDRUN_DEBUG
14861406af6Sstevesk np->tfom = '0';
14961406af6Sstevesk #endif
15061406af6Sstevesk tp->t_sc = (caddr_t)np;
15161406af6Sstevesk
15279f6c33aStedu error = linesw[TTYDISC].l_open(dev, tp, p);
15361406af6Sstevesk if (error) {
1545ff140d2Sderaadt free(np, M_DEVBUF, sizeof(*np));
15561406af6Sstevesk tp->t_sc = NULL;
15661406af6Sstevesk } else {
15761406af6Sstevesk sensordev_install(&np->timedev);
15861406af6Sstevesk timeout_set(&np->endrun_tout, endrun_timeout, np);
15961406af6Sstevesk }
16061406af6Sstevesk
16161406af6Sstevesk return error;
16261406af6Sstevesk }
16361406af6Sstevesk
16461406af6Sstevesk int
endrunclose(struct tty * tp,int flags,struct proc * p)16579f6c33aStedu endrunclose(struct tty *tp, int flags, struct proc *p)
16661406af6Sstevesk {
16761406af6Sstevesk struct endrun *np = (struct endrun *)tp->t_sc;
16861406af6Sstevesk
16961406af6Sstevesk DPRINTF(("endrunclose\n"));
17061406af6Sstevesk tp->t_line = TTYDISC; /* switch back to termios */
17161406af6Sstevesk timeout_del(&np->endrun_tout);
17261406af6Sstevesk sensordev_deinstall(&np->timedev);
1735ff140d2Sderaadt free(np, M_DEVBUF, sizeof(*np));
17461406af6Sstevesk tp->t_sc = NULL;
17561406af6Sstevesk endrun_count--;
17661406af6Sstevesk if (endrun_count == 0)
17761406af6Sstevesk endrun_nxid = 0;
17879f6c33aStedu return linesw[TTYDISC].l_close(tp, flags, p);
17961406af6Sstevesk }
18061406af6Sstevesk
18161406af6Sstevesk /* collect EndRun sentence from tty */
18261406af6Sstevesk int
endruninput(int c,struct tty * tp)18361406af6Sstevesk endruninput(int c, struct tty *tp)
18461406af6Sstevesk {
18561406af6Sstevesk struct endrun *np = (struct endrun *)tp->t_sc;
18661406af6Sstevesk struct timespec ts;
18761406af6Sstevesk int64_t gap;
18861406af6Sstevesk long tmin, tmax;
18961406af6Sstevesk
19061406af6Sstevesk if (np->sync == SYNC_EOL) {
19161406af6Sstevesk nanotime(&ts);
19261406af6Sstevesk np->pos = 0;
19361406af6Sstevesk np->sync = SYNC_SCAN;
19461406af6Sstevesk np->cbuf[np->pos++] = c; /* TFOM char */
19561406af6Sstevesk
19661406af6Sstevesk gap = (ts.tv_sec * 1000000000LL + ts.tv_nsec) -
19761406af6Sstevesk (np->lts.tv_sec * 1000000000LL + np->lts.tv_nsec);
19861406af6Sstevesk
19961406af6Sstevesk np->lts.tv_sec = ts.tv_sec;
20061406af6Sstevesk np->lts.tv_nsec = ts.tv_nsec;
20161406af6Sstevesk
20261406af6Sstevesk if (gap <= np->gap)
20361406af6Sstevesk goto nogap;
20461406af6Sstevesk
20561406af6Sstevesk np->ts.tv_sec = ts.tv_sec;
20661406af6Sstevesk np->ts.tv_nsec = ts.tv_nsec;
20761406af6Sstevesk np->gap = gap;
20861406af6Sstevesk
20961406af6Sstevesk /*
21061406af6Sstevesk * If a tty timestamp is available, make sure its value is
21161406af6Sstevesk * reasonable by comparing against the timestamp just taken.
21261406af6Sstevesk * If they differ by more than 2 seconds, assume no PPS signal
21361406af6Sstevesk * is present, note the fact, and keep using the timestamp
21461406af6Sstevesk * value. When this happens, the sensor state is set to
21561406af6Sstevesk * CRITICAL later when the EndRun sentence is decoded.
21661406af6Sstevesk */
21761406af6Sstevesk if (tp->t_flags & (TS_TSTAMPDCDSET | TS_TSTAMPDCDCLR |
21861406af6Sstevesk TS_TSTAMPCTSSET | TS_TSTAMPCTSCLR)) {
21961406af6Sstevesk tmax = lmax(np->ts.tv_sec, tp->t_tv.tv_sec);
22061406af6Sstevesk tmin = lmin(np->ts.tv_sec, tp->t_tv.tv_sec);
22161406af6Sstevesk if (tmax - tmin > 1)
22261406af6Sstevesk np->no_pps = 1;
22361406af6Sstevesk else {
22461406af6Sstevesk np->ts.tv_sec = tp->t_tv.tv_sec;
22561406af6Sstevesk np->ts.tv_nsec = tp->t_tv.tv_usec *
22661406af6Sstevesk 1000L;
22761406af6Sstevesk np->no_pps = 0;
22861406af6Sstevesk }
22961406af6Sstevesk }
23061406af6Sstevesk } else if (c == '\n') {
23161406af6Sstevesk if (np->pos == ENDRUNLEN - 1) {
23261406af6Sstevesk /* don't copy '\n' into cbuf */
23361406af6Sstevesk np->cbuf[np->pos] = '\0';
23461406af6Sstevesk endrun_scan(np, tp);
23561406af6Sstevesk }
23661406af6Sstevesk np->sync = SYNC_EOL;
23761406af6Sstevesk } else {
23861406af6Sstevesk if (np->pos < ENDRUNLEN - 1)
23961406af6Sstevesk np->cbuf[np->pos++] = c;
24061406af6Sstevesk }
24161406af6Sstevesk
24261406af6Sstevesk nogap:
24361406af6Sstevesk /* pass data to termios */
24461406af6Sstevesk return linesw[TTYDISC].l_rint(c, tp);
24561406af6Sstevesk }
24661406af6Sstevesk
24761406af6Sstevesk /* Scan the EndRun sentence just received */
24861406af6Sstevesk void
endrun_scan(struct endrun * np,struct tty * tp)24961406af6Sstevesk endrun_scan(struct endrun *np, struct tty *tp)
25061406af6Sstevesk {
25161406af6Sstevesk int fldcnt = 0, n;
25261406af6Sstevesk char *fld[NUMFLDS], *cs;
25361406af6Sstevesk
25461406af6Sstevesk DPRINTFN(1, ("%s\n", np->cbuf));
25561406af6Sstevesk /* split into fields */
25661406af6Sstevesk fld[fldcnt++] = &np->cbuf[0];
25761406af6Sstevesk for (cs = NULL, n = 0; n < np->pos && cs == NULL; n++) {
25861406af6Sstevesk switch (np->cbuf[n]) {
25961406af6Sstevesk case '\r':
26061406af6Sstevesk np->cbuf[n] = '\0';
26161406af6Sstevesk cs = &np->cbuf[n + 1];
26261406af6Sstevesk break;
26361406af6Sstevesk case ' ':
26461406af6Sstevesk if (fldcnt < NUMFLDS) {
26561406af6Sstevesk np->cbuf[n] = '\0';
26661406af6Sstevesk fld[fldcnt++] = &np->cbuf[n + 1];
26761406af6Sstevesk } else {
26861406af6Sstevesk DPRINTF(("endrun: nr of fields in sentence "
26961406af6Sstevesk "exceeds expected: %d\n", NUMFLDS));
27061406af6Sstevesk return;
27161406af6Sstevesk }
27261406af6Sstevesk break;
27361406af6Sstevesk }
27461406af6Sstevesk }
27561406af6Sstevesk endrun_decode(np, tp, fld, fldcnt);
27661406af6Sstevesk }
27761406af6Sstevesk
27861406af6Sstevesk /* Decode the time string */
27961406af6Sstevesk void
endrun_decode(struct endrun * np,struct tty * tp,char * fld[],int fldcnt)28061406af6Sstevesk endrun_decode(struct endrun *np, struct tty *tp, char *fld[], int fldcnt)
28161406af6Sstevesk {
28261406af6Sstevesk int64_t date_nano, time_nano, offset_nano, endrun_now;
28361406af6Sstevesk char tfom;
28474698e67Sckuethe int jumped = 0;
28561406af6Sstevesk
28661406af6Sstevesk if (fldcnt != NUMFLDS) {
28761406af6Sstevesk DPRINTF(("endrun: field count mismatch, %d\n", fldcnt));
28861406af6Sstevesk return;
28961406af6Sstevesk }
29061406af6Sstevesk if (endrun_time_to_nano(fld[3], &time_nano) == -1) {
29161406af6Sstevesk DPRINTF(("endrun: illegal time, %s\n", fld[3]));
29261406af6Sstevesk return;
29361406af6Sstevesk }
29461406af6Sstevesk if (endrun_date_to_nano(fld[1], fld[2], &date_nano) == -1) {
29561406af6Sstevesk DPRINTF(("endrun: illegal date, %s %s\n", fld[1], fld[2]));
29661406af6Sstevesk return;
29761406af6Sstevesk }
29861406af6Sstevesk offset_nano = 0;
29961406af6Sstevesk /* only parse offset when timemode is local */
30061406af6Sstevesk if (fld[5][0] == 'L' &&
30161406af6Sstevesk endrun_offset_to_nano(fld[4], &offset_nano) == -1) {
30261406af6Sstevesk DPRINTF(("endrun: illegal offset, %s\n", fld[4]));
30361406af6Sstevesk return;
30461406af6Sstevesk }
30561406af6Sstevesk
30661406af6Sstevesk endrun_now = date_nano + time_nano + offset_nano;
30761406af6Sstevesk if (endrun_now <= np->last) {
30861406af6Sstevesk DPRINTF(("endrun: time not monotonically increasing "
30961406af6Sstevesk "last %lld now %lld\n",
31061406af6Sstevesk (long long)np->last, (long long)endrun_now));
31174698e67Sckuethe jumped = 1;
31261406af6Sstevesk }
31361406af6Sstevesk np->last = endrun_now;
31461406af6Sstevesk np->gap = 0LL;
31561406af6Sstevesk #ifdef ENDRUN_DEBUG
31661406af6Sstevesk if (np->time.status == SENSOR_S_UNKNOWN) {
31761406af6Sstevesk np->time.status = SENSOR_S_OK;
31861406af6Sstevesk timeout_add_sec(&np->endrun_tout, TRUSTTIME);
31961406af6Sstevesk }
32061406af6Sstevesk #endif
32161406af6Sstevesk
32261406af6Sstevesk np->time.value = np->ts.tv_sec * 1000000000LL +
32361406af6Sstevesk np->ts.tv_nsec - endrun_now;
32461406af6Sstevesk np->time.tv.tv_sec = np->ts.tv_sec;
32561406af6Sstevesk np->time.tv.tv_usec = np->ts.tv_nsec / 1000L;
32661406af6Sstevesk if (np->time.status == SENSOR_S_UNKNOWN) {
32761406af6Sstevesk np->time.status = SENSOR_S_OK;
32861406af6Sstevesk np->time.flags &= ~SENSOR_FINVALID;
32961406af6Sstevesk strlcpy(np->time.desc, "EndRun", sizeof(np->time.desc));
33061406af6Sstevesk }
33161406af6Sstevesk /*
33261406af6Sstevesk * Only update the timeout if the clock reports the time as valid.
33361406af6Sstevesk *
33461406af6Sstevesk * Time Figure Of Merit (TFOM) values:
33561406af6Sstevesk *
33661406af6Sstevesk * 6 - time error is < 100 us
33761406af6Sstevesk * 7 - time error is < 1 ms
33861406af6Sstevesk * 8 - time error is < 10 ms
33961406af6Sstevesk * 9 - time error is > 10 ms,
34061406af6Sstevesk * unsynchronized state if never locked to CDMA
34161406af6Sstevesk */
34261406af6Sstevesk
34361406af6Sstevesk switch (tfom = fld[0][0]) {
34461406af6Sstevesk case '6':
34561406af6Sstevesk case '7':
34661406af6Sstevesk case '8':
34761406af6Sstevesk np->time.status = SENSOR_S_OK;
34861406af6Sstevesk np->signal.status = SENSOR_S_OK;
34961406af6Sstevesk break;
35061406af6Sstevesk case '9':
35161406af6Sstevesk np->signal.status = SENSOR_S_WARN;
35261406af6Sstevesk break;
35361406af6Sstevesk default:
35461406af6Sstevesk DPRINTF(("endrun: invalid TFOM: '%c'\n", tfom));
35561406af6Sstevesk np->signal.status = SENSOR_S_CRIT;
35661406af6Sstevesk break;
35761406af6Sstevesk }
35861406af6Sstevesk
35961406af6Sstevesk #ifdef ENDRUN_DEBUG
36061406af6Sstevesk if (np->tfom != tfom) {
36161406af6Sstevesk DPRINTF(("endrun: TFOM changed from %c to %c\n",
36261406af6Sstevesk np->tfom, tfom));
36361406af6Sstevesk np->tfom = tfom;
36461406af6Sstevesk }
36561406af6Sstevesk #endif
36674698e67Sckuethe if (jumped)
36774698e67Sckuethe np->time.status = SENSOR_S_WARN;
36874698e67Sckuethe if (np->time.status == SENSOR_S_OK)
36974698e67Sckuethe timeout_add_sec(&np->endrun_tout, TRUSTTIME);
37061406af6Sstevesk
37161406af6Sstevesk /*
37261406af6Sstevesk * If tty timestamping is requested, but no PPS signal is present, set
37361406af6Sstevesk * the sensor state to CRITICAL.
37461406af6Sstevesk */
37561406af6Sstevesk if (np->no_pps)
37661406af6Sstevesk np->time.status = SENSOR_S_CRIT;
37761406af6Sstevesk }
37861406af6Sstevesk
37961406af6Sstevesk int
endrun_atoi(char * s,int len)38061406af6Sstevesk endrun_atoi(char *s, int len)
38161406af6Sstevesk {
38261406af6Sstevesk int n;
38361406af6Sstevesk char *p;
38461406af6Sstevesk
38561406af6Sstevesk /* make sure the input contains only numbers */
38661406af6Sstevesk for (n = 0, p = s; n < len && *p && *p >= '0' && *p <= '9'; n++, p++)
38761406af6Sstevesk ;
38861406af6Sstevesk if (n != len || *p != '\0')
38961406af6Sstevesk return -1;
39061406af6Sstevesk
39161406af6Sstevesk for (n = 0; *s; s++)
39261406af6Sstevesk n = n * 10 + *s - '0';
39361406af6Sstevesk
39461406af6Sstevesk return n;
39561406af6Sstevesk }
39661406af6Sstevesk
39761406af6Sstevesk /*
39861406af6Sstevesk * Convert date fields from EndRun to nanoseconds since the epoch.
39961406af6Sstevesk * The year string must be of the form YYYY .
40061406af6Sstevesk * The day of year string must be of the form DDD .
40161406af6Sstevesk * Return 0 on success, -1 if illegal characters are encountered.
40261406af6Sstevesk */
40361406af6Sstevesk int
endrun_date_to_nano(char * y,char * doy,int64_t * nano)40461406af6Sstevesk endrun_date_to_nano(char *y, char *doy, int64_t *nano)
40561406af6Sstevesk {
40661406af6Sstevesk struct clock_ymdhms clock;
40761406af6Sstevesk time_t secs;
40861406af6Sstevesk int n, i;
40961406af6Sstevesk int year_days = 365;
41061406af6Sstevesk int month_days[] = {
41161406af6Sstevesk 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
41261406af6Sstevesk };
41361406af6Sstevesk
41461406af6Sstevesk #define FEBRUARY 2
41561406af6Sstevesk
41661406af6Sstevesk #define LEAPYEAR(x) \
41761406af6Sstevesk ((x) % 4 == 0 && \
41861406af6Sstevesk (x) % 100 != 0) || \
41961406af6Sstevesk (x) % 400 == 0
42061406af6Sstevesk
42161406af6Sstevesk if ((n = endrun_atoi(y, 4)) == -1)
42261406af6Sstevesk return -1;
42361406af6Sstevesk clock.dt_year = n;
42461406af6Sstevesk
42561406af6Sstevesk if (LEAPYEAR(n)) {
42661406af6Sstevesk month_days[FEBRUARY]++;
42761406af6Sstevesk year_days++;
42861406af6Sstevesk }
42961406af6Sstevesk
43061406af6Sstevesk if ((n = endrun_atoi(doy, 3)) == -1 || n == 0 || n > year_days)
43161406af6Sstevesk return -1;
43261406af6Sstevesk
43361406af6Sstevesk /* convert day of year to month, day */
43461406af6Sstevesk for (i = 1; n > month_days[i]; i++) {
43561406af6Sstevesk n -= month_days[i];
43661406af6Sstevesk }
43761406af6Sstevesk clock.dt_mon = i;
43861406af6Sstevesk clock.dt_day = n;
43961406af6Sstevesk
44061406af6Sstevesk DPRINTFN(1, ("mm/dd %d/%d\n", i, n));
44161406af6Sstevesk
44261406af6Sstevesk clock.dt_hour = clock.dt_min = clock.dt_sec = 0;
44361406af6Sstevesk
44461406af6Sstevesk secs = clock_ymdhms_to_secs(&clock);
44561406af6Sstevesk *nano = secs * 1000000000LL;
44661406af6Sstevesk return 0;
44761406af6Sstevesk }
44861406af6Sstevesk
44961406af6Sstevesk /*
45061406af6Sstevesk * Convert time field from EndRun to nanoseconds since midnight.
45161406af6Sstevesk * The string must be of the form HH:MM:SS .
45261406af6Sstevesk * Return 0 on success, -1 if illegal characters are encountered.
45361406af6Sstevesk */
45461406af6Sstevesk int
endrun_time_to_nano(char * s,int64_t * nano)45561406af6Sstevesk endrun_time_to_nano(char *s, int64_t *nano)
45661406af6Sstevesk {
45761406af6Sstevesk struct clock_ymdhms clock;
45861406af6Sstevesk time_t secs;
45961406af6Sstevesk int n;
46061406af6Sstevesk
46161406af6Sstevesk if (s[2] != ':' || s[5] != ':')
46261406af6Sstevesk return -1;
46361406af6Sstevesk
46461406af6Sstevesk s[2] = '\0';
46561406af6Sstevesk s[5] = '\0';
46661406af6Sstevesk
46761406af6Sstevesk if ((n = endrun_atoi(&s[0], 2)) == -1 || n > 23)
46861406af6Sstevesk return -1;
46961406af6Sstevesk clock.dt_hour = n;
47061406af6Sstevesk if ((n = endrun_atoi(&s[3], 2)) == -1 || n > 59)
47161406af6Sstevesk return -1;
47261406af6Sstevesk clock.dt_min = n;
47361406af6Sstevesk if ((n = endrun_atoi(&s[6], 2)) == -1 || n > 60)
47461406af6Sstevesk return -1;
47561406af6Sstevesk clock.dt_sec = n;
47661406af6Sstevesk
47761406af6Sstevesk DPRINTFN(1, ("hh:mm:ss %d:%d:%d\n", (int)clock.dt_hour,
47861406af6Sstevesk (int)clock.dt_min,
47961406af6Sstevesk (int)clock.dt_sec));
48061406af6Sstevesk secs = clock.dt_hour * 3600
48161406af6Sstevesk + clock.dt_min * 60
48261406af6Sstevesk + clock.dt_sec;
48361406af6Sstevesk
48461406af6Sstevesk DPRINTFN(1, ("secs %lu\n", (unsigned long)secs));
48561406af6Sstevesk
48661406af6Sstevesk *nano = secs * 1000000000LL;
48761406af6Sstevesk return 0;
48861406af6Sstevesk }
48961406af6Sstevesk
49061406af6Sstevesk int
endrun_offset_to_nano(char * s,int64_t * nano)49161406af6Sstevesk endrun_offset_to_nano(char *s, int64_t *nano)
49261406af6Sstevesk {
49361406af6Sstevesk time_t secs;
49461406af6Sstevesk int n;
49561406af6Sstevesk
49661406af6Sstevesk if (!(s[0] == '+' || s[0] == '-'))
49761406af6Sstevesk return -1;
49861406af6Sstevesk
49961406af6Sstevesk if ((n = endrun_atoi(&s[1], 2)) == -1)
50061406af6Sstevesk return -1;
50161406af6Sstevesk secs = n * 30 * 60;
50261406af6Sstevesk
50361406af6Sstevesk *nano = secs * 1000000000LL;
50461406af6Sstevesk if (s[0] == '+')
50561406af6Sstevesk *nano = -*nano;
50661406af6Sstevesk
50761406af6Sstevesk DPRINTFN(1, ("offset secs %lu nanosecs %lld\n",
50861406af6Sstevesk (unsigned long)secs, (long long)*nano));
50961406af6Sstevesk
51061406af6Sstevesk return 0;
51161406af6Sstevesk }
51261406af6Sstevesk
51361406af6Sstevesk /*
51461406af6Sstevesk * Degrade the sensor state if we received no EndRun string for more than
51561406af6Sstevesk * TRUSTTIME seconds.
51661406af6Sstevesk */
51761406af6Sstevesk void
endrun_timeout(void * xnp)51861406af6Sstevesk endrun_timeout(void *xnp)
51961406af6Sstevesk {
52061406af6Sstevesk struct endrun *np = xnp;
52161406af6Sstevesk
52261406af6Sstevesk if (np->time.status == SENSOR_S_OK) {
52361406af6Sstevesk np->time.status = SENSOR_S_WARN;
52461406af6Sstevesk /*
52561406af6Sstevesk * further degrade in TRUSTTIME seconds if no new valid EndRun
52661406af6Sstevesk * strings are received.
52761406af6Sstevesk */
52861406af6Sstevesk timeout_add_sec(&np->endrun_tout, TRUSTTIME);
52961406af6Sstevesk } else
53061406af6Sstevesk np->time.status = SENSOR_S_CRIT;
53161406af6Sstevesk }
532