xref: /openbsd-src/sys/kern/tty_endrun.c (revision 3e6763994aeb80a553b07ba51b1db2c4725c5c88)
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