diff options
-rw-r--r-- | share/man/man4/Makefile | 6 | ||||
-rw-r--r-- | share/man/man4/nmea.4 | 50 | ||||
-rw-r--r-- | share/man/man4/tty.4 | 8 | ||||
-rw-r--r-- | sys/conf/files | 6 | ||||
-rw-r--r-- | sys/kern/tty_conf.c | 17 | ||||
-rw-r--r-- | sys/kern/tty_nmea.c | 405 | ||||
-rw-r--r-- | sys/sys/ttycom.h | 3 |
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_ */ |