summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/man/man4/Makefile6
-rw-r--r--share/man/man4/nmea.450
-rw-r--r--share/man/man4/tty.48
-rw-r--r--sys/conf/files6
-rw-r--r--sys/kern/tty_conf.c17
-rw-r--r--sys/kern/tty_nmea.c405
-rw-r--r--sys/sys/ttycom.h3
7 files changed, 488 insertions, 7 deletions
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 06d4563fe15..9f5ba9984cc 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.362 2006/05/28 17:22:48 deraadt Exp $
+# $OpenBSD: Makefile,v 1.363 2006/06/01 20:10:28 mbalmer Exp $
MAN= aac.4 ac97.4 acphy.4 acpi.4 acpihpet.4 acpitimer.4 \
adc.4 addcom.4 admcts.4 admlc.4 admtemp.4 \
@@ -25,8 +25,8 @@ MAN= aac.4 ac97.4 acphy.4 acpi.4 acpihpet.4 acpitimer.4 \
lmc.4 lmenv.4 lmtemp.4 lo.4 lofn.4 lpt.4 lxtphy.4 luphy.4 \
maestro.4 maxds.4 maxtmp.4 midi.4 \
mii.4 mfi.4 mpi.4 mpt.4 mpu.4 mtd.4 mtdphy.4 multicast.4 mtio.4 ne.4 \
- neo.4 netintro.4 nfe.4 nge.4 noct.4 nofn.4 nsclpcsio.4 nsgphy.4 \
- nsphy.4 nsphyter.4 null.4 nviic.4 ohci.4 opl.4 options.4 \
+ neo.4 netintro.4 nfe.4 nge.4 nmea.4 noct.4 nofn.4 nsclpcsio.4 \
+ nsgphy.4 nsphy.4 nsphyter.4 null.4 nviic.4 ohci.4 opl.4 options.4 \
onewire.4 oosiop.4 osiop.4 owid.4 owtemp.4 \
pcagpio.4 pcdisplay.4 pchb.4 pci.4 pcib.4 pcfadc.4 \
pcfiic.4 pciide.4 pckbc.4 pckbd.4 pcmcia.4 pcn.4 pcppi.4 pcscp.4 \
diff --git a/share/man/man4/nmea.4 b/share/man/man4/nmea.4
new file mode 100644
index 00000000000..84bd3471ad3
--- /dev/null
+++ b/share/man/man4/nmea.4
@@ -0,0 +1,50 @@
+.\" $OpenBSD: nmea.4,v 1.1 2006/06/01 20:10:28 mbalmer Exp $
+.\"
+.\" Copyright (c) 2006 Marc Balmer <mbalmer@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd May 42, 2006
+.Dt NMEA 4
+.Os
+.Sh NAME
+.Nm nmea
+.Nd line discipline for NMEA0183 devices
+.Sh SYNOPSIS
+.Cd "pseudo-device nmea" Op Ar count
+.Sh DESCRIPTION
+This line discipline provides an interface to NMEA talking devices
+which are connected to a host through a serial line.
+.Pp
+The line discipline is enabled by a sequence:
+.Bd -literal -offset indent
+#include <sys/ttycom.h>
+int ldisc = NMEADISC, fildes; ...
+ioctl(fildes, TIOCSETD, &ldisc);
+.Ed
+.Pp
+While application program can still use the device as if it had the
+default (termios) line discipline attached, the lince discipline
+maintains a timedelta sensor in the kernel.
+.Sh SEE ALSO
+.Xr tty 4
+.Sh HISTORY
+The
+.Nm
+interface appeared in
+.Ox 4.0 .
+.Sh AUTHORS
+The
+.Nm
+line discpiline was written by
+.An Marc Balmer Aq mbalmer@openbsd.org .
diff --git a/share/man/man4/tty.4 b/share/man/man4/tty.4
index 92ab55c0dd6..072512a48d2 100644
--- a/share/man/man4/tty.4
+++ b/share/man/man4/tty.4
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tty.4,v 1.26 2006/04/27 19:30:27 deraadt Exp $
+.\" $OpenBSD: tty.4,v 1.27 2006/06/01 20:10:28 mbalmer Exp $
.\" $NetBSD: tty.4,v 1.4 1996/03/19 04:26:01 paulus Exp $
.\"
.\" Copyright (c) 1991, 1992, 1993
@@ -213,6 +213,8 @@ Serial IP line discipline.
Point to Point Protocol line discipline.
.It STRIPDISC
Starmode Radio IP line discipline.
+.It NMEADISC
+NMEA0183 line discipline.
.El
.Pp
.It Dv TIOCGETD Fa int *ldisc
@@ -495,3 +497,7 @@ controlling terminal, if any
.Xr getty 8
.Sh HISTORY
The cua support is inspired by similar support in SunOS.
+The NMEA0183 line discipline was added in
+.Ox 4.0
+by
+.An Marc Balmer Aq mbalmer@openbsd.org
diff --git a/sys/conf/files b/sys/conf/files
index 1ce78a06430..07e885ed356 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.374 2006/05/28 17:21:14 uwe Exp $
+# $OpenBSD: files,v 1.375 2006/06/01 20:10:28 mbalmer Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -935,3 +935,7 @@ file net/pfkey.c key | ipsec | tcp_signature
file net/pfkeyv2.c key | ipsec | tcp_signature
file net/pfkeyv2_parsemessage.c key | ipsec | tcp_signature
file net/pfkeyv2_convert.c key | ipsec | tcp_signature
+
+# NMEA0183 support
+pseudo-device nmea: tty
+file kern/tty_nmea.c nmea needs-flag
diff --git a/sys/kern/tty_conf.c b/sys/kern/tty_conf.c
index e5ab5c36215..5ddcc090f39 100644
--- a/sys/kern/tty_conf.c
+++ b/sys/kern/tty_conf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tty_conf.c,v 1.9 2005/12/21 12:43:49 jsg Exp $ */
+/* $OpenBSD: tty_conf.c,v 1.10 2006/06/01 20:10:28 mbalmer Exp $ */
/* $NetBSD: tty_conf.c,v 1.18 1996/05/19 17:17:55 jonathan Exp $ */
/*-
@@ -94,6 +94,13 @@ int stripinput(int c, struct tty *tp);
int stripstart(struct tty *tp);
#endif
+#include "nmea.h"
+#if NNMEA > 0
+int nmeaopen(dev_t, struct tty *);
+int nmeaclose(struct tty *, int);
+int nmeainput(int, struct tty *);
+#endif
+
struct linesw linesw[] =
{
{ ttyopen, ttylclose, ttread, ttwrite, nullioctl,
@@ -141,6 +148,14 @@ struct linesw linesw[] =
{ ttynodisc, ttyerrclose, ttyerrio, ttyerrio, nullioctl,
ttyerrinput, ttyerrstart, nullmodem },
#endif
+
+#if NNMEA > 0
+ { nmeaopen, nmeaclose, ttread, ttwrite, nullioctl,
+ nmeainput, ttstart, ttymodem }, /* 7- NMEADISC */
+#else
+ { ttynodisc, ttyerrclose, ttyerrio, ttyerrio, nullioctl,
+ ttyerrinput, ttyerrstart, nullmodem },
+#endif
};
int nlinesw = sizeof (linesw) / sizeof (linesw[0]);
diff --git a/sys/kern/tty_nmea.c b/sys/kern/tty_nmea.c
new file mode 100644
index 00000000000..a6215bf3b31
--- /dev/null
+++ b/sys/kern/tty_nmea.c
@@ -0,0 +1,405 @@
+/* $OpenBSD $*/
+
+/*
+ * Copyright (c) 2006 Marc Balmer <mbalmer@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* line discipline to decode NMEA0183 data */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/queue.h>
+#include <sys/malloc.h>
+#include <sys/sensors.h>
+#include <sys/tty.h>
+#include <sys/conf.h>
+
+#include <dev/clock_subr.h> /* clock_subr not avail on all arches */
+
+#ifdef NMEA_DEBUG
+#define DPRINTFN(n, x) do { if (nmeadebug > (n)) printf x; } while (0)
+int nmeadebug = 0;
+#else
+#define DPRINTFN(n, x)
+#endif
+#define DPRINTF(x) DPRINTFN(0, x)
+
+int nmeaopen(dev_t, struct tty *);
+int nmeaclose(struct tty *, int);
+int nmeainput(int, struct tty *);
+void nmeaattach(int);
+
+#define NMEAMAX 82
+#define MAXFLDS 12
+
+/* NMEA talker identifiers */
+
+#define TI_UNK 0
+#define TI_GPS 1
+#define TI_LORC 2
+
+/* NMEA message types */
+
+#define MSG_RMC 5393731 /* recommended minimum sentence C */
+#define MSG_GGA 4671297
+#define MSG_GSA 4674369 /* satellites active */
+#define MSG_GSV 4674390 /* satellites in view */
+#define MSG_VTG 5657671 /* velocity, direction, speed */
+
+int nmea_count = 0;
+time_t nmea_last = 0;
+
+struct nmea {
+ char cbuf[NMEAMAX];
+ struct sensor time;
+ struct timeval tv; /* soft timestamp */
+ struct timespec ts;
+ int state; /* state we're in */
+ int pos;
+ int fpos[MAXFLDS];
+ int flds; /* expect nr of fields */
+ int fldcnt; /* actual count of fields */
+ int cksum; /* calculated checksum */
+ int msgcksum; /* received cksum */
+ int ti; /* talker identifier */
+ int msg; /* NMEA msg type */
+};
+
+/* NMEA protocol state machine */
+
+void nmea_hdlr(struct nmea *, int c);
+
+/* NMEA decoding */
+
+void nmea_decode(struct nmea *);
+void nmea_rmc(struct nmea *);
+
+/* helper functions */
+
+int nmea_atoi(char *, int *);
+void nmea_bufadd(struct nmea *, int);
+
+enum states {
+ S_SYNC = 0,
+ S_TI_1,
+ S_TI_2,
+ S_MSG,
+ S_DATA,
+ S_CKSUM
+};
+
+int
+nmeaopen(dev_t dev, struct tty *tp)
+{
+ struct nmea *np;
+
+ if (tp->t_line != NMEADISC) {
+ np = malloc(sizeof(struct nmea), M_WAITOK, M_DEVBUF);
+ tp->t_sc = (caddr_t)np;
+
+ snprintf(np->time.device, sizeof(np->time.device), "nmea%d",
+ nmea_count++);
+ np->time.status = SENSOR_S_UNKNOWN;
+ np->time.type = SENSOR_TIMEDELTA;
+ np->time.value = 0LL;
+ np->time.rfact = 0;
+ np->time.flags = 0;
+ np->state = S_SYNC;
+ }
+
+ return linesw[0].l_open(dev, tp);
+}
+
+int
+nmeaclose(struct tty *tp, int flags)
+{
+ struct nmea *np = (struct nmea *)tp->t_sc;
+
+ tp->t_line = 0; /* switch back to termios */
+ if (np->time.status != SENSOR_S_UNKNOWN)
+ sensor_del(&np->time);
+ free(np, M_DEVBUF);
+ nmea_count--;
+ return linesw[0].l_close(tp, flags);
+}
+
+/* scan input from tty for NMEA telegrams */
+int
+nmeainput(int c, struct tty *tp)
+{
+ struct nmea *np = (struct nmea *)tp->t_sc;
+
+ nmea_hdlr(np, c);
+
+ /* pass data to termios */
+ return linesw[0].l_rint(c, tp);
+}
+
+void
+nmeaattach(int dummy)
+{
+}
+
+/* NMEA state machine */
+void
+nmea_hdlr(struct nmea *np, int c)
+{
+ switch (np->state) {
+ case S_SYNC:
+ switch (c) {
+ case '$':
+ /* timestamp and delta refs now */
+ microtime(&np->tv);
+ nanotime(&np->ts);
+ np->pos = 0;
+ np->fldcnt = 0;
+ np->flds = 0;
+ np->cbuf[np->pos++] = c;
+ np->cksum = 0;
+ np->msgcksum = -1;
+ np->ti = TI_UNK;
+ np->state = S_TI_1;
+ }
+ break;
+ case S_TI_1:
+ nmea_bufadd(np, c);
+ np->state = S_TI_2;
+ switch (c) {
+ case 'G':
+ np->ti = TI_GPS;
+ break;
+ case 'L':
+ np->ti = TI_LORC;
+ break;
+ default:
+ np->state = S_SYNC;
+ }
+ break;
+ case S_TI_2:
+ nmea_bufadd(np, c);
+ np->state = S_SYNC;
+ switch (c) {
+ case 'P':
+ if (np->ti == TI_GPS)
+ np->state = S_MSG;
+ break;
+ case 'C':
+ if (np->ti == TI_LORC)
+ np->state = S_MSG;
+ break;
+ }
+ break;
+ case S_MSG:
+ nmea_bufadd(np, c);
+ if (np->pos == 6) {
+ np->msg = (np->cbuf[3] << 16) + (np->cbuf[4] << 8) +
+ np->cbuf[5];
+ switch (np->msg) {
+ case MSG_RMC:
+ np->flds = 12;
+ np->state = S_DATA;
+ break;
+ default:
+ np->state = S_SYNC;
+ }
+ }
+ break;
+ case S_DATA:
+ switch (c) {
+ case '\n':
+ np->cbuf[np->pos] = '\0';
+ nmea_decode(np);
+ np->state = S_SYNC;
+ break;
+ case '*':
+ np->cbuf[np->pos++] = c;
+ np->msgcksum = 0;
+ np->state = S_CKSUM;
+ break;
+ case ',':
+ np->fpos[np->fldcnt++] = np->pos + 1;
+ default:
+ if (np->pos < NMEAMAX)
+ nmea_bufadd(np, c);
+ else
+ np->state = S_SYNC;
+ }
+ break;
+ case S_CKSUM:
+ switch (c) {
+ case '\r':
+ case '\n':
+ np->cbuf[np->pos] = '\0';
+ nmea_decode(np);
+ np->state = S_SYNC;
+ break;
+ default:
+ if (np->pos < NMEAMAX && ((c >= '0' && c<= '9') ||
+ (c >= 'A' && c <= 'F'))) {
+ np->cbuf[np->pos++] = c;
+ if (np->msgcksum)
+ np->msgcksum <<= 4;
+ if (c >= '0' && c<= '9')
+ np->msgcksum += c - '0';
+ else if (c >= 'A' && c <= 'F')
+ np->msgcksum += 10 + c - 'A';
+ } else
+ np->state = S_SYNC;
+ }
+ break;
+ }
+}
+
+/* add a character to the buffer and update the checksum */
+void
+nmea_bufadd(struct nmea *np, int c)
+{
+ np->cbuf[np->pos++] = c;
+ np->cksum ^= c;
+}
+
+/*
+ * convert a string to a number, stop at the first non-numerical
+ * character or the end of the string. any non-numerical character
+ * following the number other than ',' is considered an error.
+ */
+int
+nmea_atoi(char *s, int *num)
+{
+ int n;
+
+ for (n = 0; *s && *s >= '0' && *s <= '9'; s++) {
+ if (n)
+ n *= 10;
+ n += *s - '0';
+ }
+
+ if (*s && *s != ',') /* no numeric character */
+ return (-1);
+
+ *num = n;
+ return (0);
+}
+
+void
+nmea_decode(struct nmea *np)
+{
+ switch (np->msg) {
+ case MSG_RMC:
+ nmea_rmc(np);
+ break;
+ }
+}
+
+/* Decode the minimum recommended nav info sentence (RMC) */
+void
+nmea_rmc(struct nmea *np)
+{
+ struct clock_ymdhms ymdhms;
+ time_t nmea_now;
+ int n;
+
+ if (np->fldcnt != np->flds) {
+ DPRINTF("field count mismatch\n");
+ return;
+ }
+ if (np->msgcksum >= 0 && np->cksum != np->msgcksum) {
+ DPRINTF("checksum error");
+ return;
+ }
+ np->cbuf[13] = '\0';
+ if (nmea_atoi(&np->cbuf[11], &n)) {
+ DPRINTF("error in sec\n");
+ return;
+ }
+ ymdhms.dt_sec = n;
+ np->cbuf[11] = 0;
+ if (nmea_atoi(&np->cbuf[9], &n)) {
+ DPRINTF("error in min\n");
+ return;
+ }
+ ymdhms.dt_min = n;
+ np->cbuf[9] = 0;
+ if (nmea_atoi(&np->cbuf[7], &n)) {
+ DPRINTF("error in hour\n");
+ return;
+ }
+ ymdhms.dt_hour = n;
+ if (nmea_atoi(&np->cbuf[np->fpos[8] + 4], &n)) {
+ DPRINTF("error in year\n");
+ return;
+ }
+ ymdhms.dt_year = 2000 + n;
+ np->cbuf[np->fpos[8] + 4] = '\0';
+ if (nmea_atoi(&np->cbuf[np->fpos[8] + 2], &n)) {
+ DPRINTF("error in month\n");
+ return;
+ }
+ ymdhms.dt_mon = n;
+ np->cbuf[np->fpos[8] + 2] = '\0';
+ if (nmea_atoi(&np->cbuf[np->fpos[8]], &n)) {
+ DPRINTF("error in day\n");
+ return;
+ }
+ ymdhms.dt_day = n;
+ nmea_now = clock_ymdhms_to_secs(&ymdhms);
+ if (nmea_now <= nmea_last) {
+ DPRINTF("time not monotonically increasing\n");
+ return;
+ }
+ nmea_last = nmea_now;
+ np->time.value = (np->ts.tv_sec - nmea_now)
+ * 1000000000 + np->ts.tv_nsec;
+ np->time.tv.tv_sec = np->tv.tv_sec;
+ np->time.tv.tv_usec = np->tv.tv_usec;
+ if (np->time.status == SENSOR_S_UNKNOWN) {
+ strlcpy(np->time.desc, np->ti == TI_GPS ? "GPS GPS" :
+ "LORC Loran-C", sizeof(np->time.desc));
+ switch (np->cbuf[np->fpos[11]]) {
+ case 'S':
+ strlcat(np->time.desc, " simulated",
+ sizeof(np->time.desc));
+ break;
+ case 'E':
+ strlcat(np->time.desc, " estimated",
+ sizeof(np->time.desc));
+ break;
+ case 'A':
+ strlcat(np->time.desc, " autonomous",
+ sizeof(np->time.desc));
+ break;
+ case 'D':
+ strlcat(np->time.desc, " differential",
+ sizeof(np->time.desc));
+ break;
+ case 'N':
+ strlcat(np->time.desc, " not valid",
+ sizeof(np->time.desc));
+ break;
+ }
+ np->time.status = SENSOR_S_OK;
+ sensor_add(&np->time);
+ }
+ switch (np->cbuf[np->fpos[1]]) {
+ case 'A':
+ np->time.status = SENSOR_S_OK;
+ break;
+ case 'V':
+ np->time.status = SENSOR_S_WARN;
+ break;
+ default:
+ DPRINTF("unknown warning indication\n");
+ }
+}
diff --git a/sys/sys/ttycom.h b/sys/sys/ttycom.h
index ad0f3579d1e..0d8e4ba4d10 100644
--- a/sys/sys/ttycom.h
+++ b/sys/sys/ttycom.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ttycom.h,v 1.7 2006/04/27 19:30:28 deraadt Exp $ */
+/* $OpenBSD: ttycom.h,v 1.8 2006/06/01 20:10:28 mbalmer Exp $ */
/* $NetBSD: ttycom.h,v 1.4 1996/05/19 17:17:53 jonathan Exp $ */
/*-
@@ -142,5 +142,6 @@ struct tstamps {
#define SLIPDISC 4 /* serial IP discipline */
#define PPPDISC 5 /* ppp discipline */
#define STRIPDISC 6 /* metricom wireless IP discipline */
+#define NMEADISC 7 /* NMEA0183 discipline */
#endif /* !_SYS_TTYCOM_H_ */