summaryrefslogtreecommitdiff
path: root/src/venus_gps.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/venus_gps.c')
-rw-r--r--src/venus_gps.c631
1 files changed, 631 insertions, 0 deletions
diff --git a/src/venus_gps.c b/src/venus_gps.c
new file mode 100644
index 0000000..4a02096
--- /dev/null
+++ b/src/venus_gps.c
@@ -0,0 +1,631 @@
+/*
+ * Venus GPS Controller Example
+ *
+ * Copyright (C) 2010 by Multi-Tech Systems
+ *
+ * Author: James Maki <jmaki@multitech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <poll.h>
+#include <getopt.h>
+#include <time.h>
+#include <signal.h>
+
+#include "log.h"
+#include "cbuffer.h"
+#include "utils.h"
+#include "venus_gps.h"
+#include "venus_api.h"
+
+static sig_atomic_t timer_expired;
+static void sigalarm_handler(int arg) {
+ timer_expired = 1;
+}
+
+static sig_atomic_t sigpipe = 0;
+static void sigpipe_handler(int arg) {
+ sigpipe = 1;
+}
+
+enum {
+ MODE_SERVER = 0,
+ MODE_CLIENT = 1,
+};
+
+static char *device = "/dev/ttyS3";
+static char remote_host[256];
+static int port = 5445;
+static speed_t baud_rate = B9600;
+
+static uint8_t gga = 1;
+static uint8_t gsa = 1;
+static uint8_t gsv = 1;
+static uint8_t gll = 1;
+static uint8_t rmc = 1;
+static uint8_t vtg = 1;
+static uint8_t zda = 1;
+
+/*
+ * XXX: I don't know if these work, yet. I haven't tried them out.
+ *
+ */
+static uint8_t start_mode = (uint8_t) -1;
+static int16_t latitude = 45;
+static int16_t longitude = -93;
+static int16_t altitude = 256;
+
+
+static int tcp_exchange_data(int sd, int tty) {
+ int i;
+ int err;
+ struct pollfd fds[2];
+ nfds_t nfds = 2;
+ struct cbuffer bufs[2];
+
+ log_debug("starting");
+
+ memset(fds, 0, sizeof(fds));
+
+ for(i = 0; i < nfds; i++) {
+ memset(&fds[i], 0, sizeof(fds[i]));
+ bufs[i].read_index = bufs[i].write_index = 0;
+ }
+
+ fds[0].fd = tty;
+ fds[1].fd = sd;
+
+ while(1) {
+ for(i = 0; i < nfds; i++) {
+ fds[i].events = 0;
+ }
+
+ for(i = 0; i < nfds; i++) {
+ if(buffer_free(&bufs[i])) {
+ fds[i].events |= POLLIN;
+ }
+ if(buffer_used(&bufs[i])) {
+ fds[DIR_REV(i)].events |= POLLOUT;
+ }
+ }
+
+ err = poll(fds, nfds, -1);
+ if(err <= 0) {
+ log_error("poll returned %d errno: %d", err, errno);
+ break;
+ }
+
+ log_debug("poll returned %d", err);
+
+ for(i = 0; i < nfds; i++) {
+ if(fds[i].revents) {
+ log_debug("fds[%d]: %d revents: %04X POLLIN: %04X POLLOUT: %04X POLLHUP: %04X", i, fds[i].fd, fds[i].revents, POLLIN, POLLOUT, POLLHUP);
+
+ if(fds[i].revents & POLLHUP) {
+ log_error("fds[%d]: %d POLLHUP", i, fds[i].fd);
+ return 0;
+ } else if(fds[i].revents & POLLOUT) {
+ log_debug("fds[%d]: %d POLLOUT buffer_used: %lu", i, fds[i].fd, (unsigned long) buffer_used(&bufs[DIR_REV(i)]));
+
+ err = buffered_write(fds[i].fd, &bufs[DIR_REV(i)]);
+ if(err < 0) {
+ log_error("fds[%d]: %d safe_write failed: errno: %d", i, fds[i].fd, errno);
+ return -1;
+ }
+ } else if(fds[i].revents & POLLIN) {
+ log_debug("fds[%d]: %d POLLIN buffer_free: %lu", i, fds[i].fd, (unsigned long) buffer_free(&bufs[i]));
+
+ err = buffered_read(fds[i].fd, &bufs[i]);
+ if(err <= 0) {
+ log_debug("fds[%d]: %d safe_read failed: errno: %d", i, fds[i].fd, errno);
+ return -1;
+ }
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
+static int udp_send_msgs(int sd, int tty) {
+ char buf[NMEA_SENTENCE_MAX];
+ int count;
+ int err;
+
+ while(1) {
+ count = read_nmea_sentence(tty, buf, sizeof(buf));
+ if(count <= 0) {
+ log_error("read_nmea_sentence failed: quiting");
+ return -1;
+ }
+ err = send(sd, buf, count, 0);
+ if(err < 0) {
+ log_debug("send failed: %d", errno);
+ return -1;
+ }
+ }
+
+ return -1;
+}
+
+static int udp_server() {
+ log_error("UDP Server mode is not implemented");
+ exit(1);
+ return -1;
+}
+
+static int udp_client() {
+ int tty;
+ int sd;
+
+ log_notice("Venus GPS UDP Client connecting to %s:%d", remote_host, port);
+
+ while(1) {
+ sd = inet_conn_str(remote_host, port, SOCK_DGRAM);
+ if(sd < 0) {
+ log_error("inet_conn_str failed: %d", errno);
+ sleep(60);
+ } else {
+ log_debug("connection opened");
+
+ tty = venus_open(device, baud_rate);
+
+ set_nonblocking(sd);
+
+ udp_send_msgs(sd, tty);
+
+ close(sd);
+ close(tty);
+ }
+ }
+
+ return -1;
+}
+
+static int tcp_client() {
+ int tty;
+ int sd;
+
+ log_notice("Venus GPS TCP Client connecting to %s:%d", remote_host, port);
+
+ while(1) {
+ sd = inet_conn_str(remote_host, port, SOCK_STREAM);
+ if(sd < 0) {
+ log_error("inet_conn_str failed: %d", errno);
+ sleep(60);
+ } else {
+ log_debug("connection opened");
+
+ tty = venus_open(device, baud_rate);
+
+ set_nonblocking(tty);
+ set_nonblocking(sd);
+
+ tcp_exchange_data(sd, tty);
+
+ close(sd);
+ close(tty);
+ }
+ }
+
+ return -1;
+}
+
+static int tcp_server() {
+ int err;
+ int tty;
+ int lsd;
+ int sd;
+ int cli_addr_len;
+ int serv_addr_len;
+ int reuse = 1;
+
+ struct sockaddr_in serv_addr;
+ struct sockaddr_in cli_addr;
+
+ lsd = socket(PF_INET, SOCK_STREAM, 0);
+ if(lsd == -1) {
+ log_error("socket failed: %d", errno);
+ exit(1);
+ }
+
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ serv_addr.sin_port = htons(port);
+
+ err = setsockopt(lsd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
+ if(err == -1) {
+ log_error("setsockopt failed: %d", errno);
+ exit(1);
+ }
+
+ serv_addr_len = sizeof(serv_addr);
+
+ err = bind(lsd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
+ if(err == -1) {
+ log_error("bind failed: %d", errno);
+ exit(1);
+ }
+
+ err = listen(lsd, 1);
+ if(err == -1) {
+ log_error("listen failed: %d", errno);
+ exit(1);
+ }
+
+ log_notice("Venus GPS TCP Server listening on port %d", port);
+
+ while(1) {
+ cli_addr_len = sizeof(cli_addr);
+
+ sd = accept(lsd, (struct sockaddr *) &cli_addr, (socklen_t *) &cli_addr_len);
+ if(sd == -1) {
+ log_error("accept failed: %d", errno);
+ } else {
+ log_debug("accepted");
+
+ tty = venus_open(device, baud_rate);
+
+ set_nonblocking(tty);
+ set_nonblocking(sd);
+
+ tcp_exchange_data(sd, tty);
+
+ close(sd);
+ close(tty);
+ }
+ }
+
+ close(lsd);
+
+ return -1;
+}
+
+static void print_version(const char *name) {
+ printf("%s (" PACKAGE ") " VERSION " (" __DATE__ " " __TIME__ ")\n", name);
+ printf("Copyright (C) 2010 by Multi-Tech Systems\n");
+ printf(
+"This program is free software; you may redistribute it under the terms of\n"
+"the GNU General Public License version 2 or (at your option) any later version.\n"
+"This program has absolutely no warranty.\n");
+}
+
+static void usage(FILE *out) {
+ fprintf(out, "usage: venus-gps [ OPTIONS ... ]\n");
+ fprintf(out, "where OPTIONS := { \n");
+ fprintf(out, " --daemonize |\n");
+ fprintf(out, " -d --device <device> (default: /dev/ttyS3) |\n");
+ fprintf(out, " -p --port <port> (default: 5445) |\n");
+ fprintf(out, " --protocol { tcp (default) | udp } |\n");
+ fprintf(out, " --mode { client | server (default) } |\n");
+ fprintf(out, " --remote-host <host> |\n");
+#if CONFIG_CAN_DEFAULT
+ fprintf(out, " -f --factory-defaults |\n");
+#endif
+#if CONFIG_CAN_RESET
+ fprintf(out, "\n");
+ fprintf(out, " --start-mode 0-5 (default: 0) |\n");
+ fprintf(out, " --latitude VAL (default: ) |\n");
+ fprintf(out, " --longitude VAL (default: ) |\n");
+ fprintf(out, " --altitude VAL (default: ) |\n");
+#endif
+ fprintf(out, "\n");
+ fprintf(out, " --gga 0-255 (default: 1) |\n");
+ fprintf(out, " --gsa 0-255 (default: 1) |\n");
+ fprintf(out, " --gsv 0-255 (default: 1) |\n");
+ fprintf(out, " --gll 0-255 (default: 1) |\n");
+ fprintf(out, " --rmc 0-255 (default: 1) |\n");
+#if CONFIG_HAVE_ZDA
+ fprintf(out, " --zda 0-255 (default: 1) |\n");
+#endif
+ fprintf(out, " --vtg 0-255 (default: 1)\n");
+ fprintf(out, " }\n");
+ fprintf(out, "\n");
+}
+
+#define OPT_DEVICE 'd'
+#define OPT_PORT 'p'
+#define OPT_FACTORY_DEFAULTS 'f'
+#define OPT_BAUD_RATE 'b'
+
+enum {
+ OPT_VERSION = 128,
+ OPT_HELP,
+ OPT_GGA,
+ OPT_GSA,
+ OPT_GSV,
+ OPT_GLL,
+ OPT_RMC,
+ OPT_VTG,
+ OPT_ZDA,
+ OPT_START_MODE,
+ OPT_LATITUDE,
+ OPT_LONGITUDE,
+ OPT_ALTITUDE,
+ OPT_REMOTE_HOST,
+ OPT_DAEMONIZE,
+ OPT_PROTOCOL,
+ OPT_MODE,
+};
+
+static char *short_options = "b:d:p:f";
+static struct option long_options[] = {
+ {"protocol", 1, 0, OPT_PROTOCOL},
+ {"daemonize", 0, 0, OPT_DAEMONIZE},
+ {"device", 1, 0, OPT_DEVICE},
+ {"mode", 1, 0, OPT_MODE},
+ {"remote-host", 1, 0, OPT_REMOTE_HOST},
+ {"port", 1, 0, OPT_PORT},
+ {"baud-rate", 1, 0, OPT_BAUD_RATE},
+#if CONFIG_CAN_DEFAULT
+ {"factory-defaults", 0, 0, OPT_FACTORY_DEFAULTS},
+#endif
+ {"gga", 1, 0, OPT_GGA},
+ {"gsa", 1, 0, OPT_GSA},
+ {"gsv", 1, 0, OPT_GSV},
+ {"gll", 1, 0, OPT_GLL},
+ {"rmc", 1, 0, OPT_RMC},
+ {"vtg", 1, 0, OPT_VTG},
+#if CONFIG_HAVE_ZDA
+ {"zda", 1, 0, OPT_ZDA},
+#endif
+#if CONFIG_CAN_RESET
+ {"start-mode", 1, 0, OPT_START_MODE},
+ {"latitude", 1, 0, OPT_LATITUDE},
+ {"longitude", 1, 0, OPT_LONGITUDE},
+ {"altitude", 1, 0, OPT_ALTITUDE},
+#endif
+ {"version", 0, NULL, OPT_VERSION},
+ {"help", 0, NULL, OPT_HELP},
+ {0, 0, 0, 0},
+};
+
+int main(int argc, char *argv[]) {
+ int i;
+ int option_index;
+ int tty;
+ int factory_defaults = 0;
+ int daemonize = 0;
+ int proto = IPPROTO_TCP;
+ int mode = MODE_SERVER;
+
+ struct sigaction sa;
+
+ sa.sa_handler = sigalarm_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGALRM, &sa, NULL);
+
+ sa.sa_handler = sigpipe_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGPIPE, &sa, NULL);
+
+ while((i = getopt_long(argc, argv, short_options, long_options, &option_index)) >= 0) {
+ switch(i) {
+ case 0:
+ break;
+
+ case OPT_PROTOCOL:
+ if(!strcasecmp("tcp", optarg)) {
+ proto = IPPROTO_TCP;
+ } else if(!strcasecmp("udp", optarg)) {
+ proto = IPPROTO_UDP;
+ } else {
+ log_error("invalid transport protocol");
+ usage(stderr);
+ exit(1);
+ }
+ break;
+
+ case OPT_DAEMONIZE:
+ daemonize = 1;
+ break;
+
+ case OPT_MODE:
+ if(!strcasecmp("server", optarg)) {
+ mode = MODE_SERVER;
+ } else if(!strcasecmp("client", optarg)) {
+ mode = MODE_CLIENT;
+ } else {
+ log_error("invalid mode");
+ usage(stderr);
+ exit(1);
+ }
+ break;
+
+ case OPT_DEVICE:
+ device = optarg;
+ break;
+
+ case OPT_PORT:
+ port = atoi(optarg);
+ if(port <= 0 || port >= (1 << 16)) {
+ log_error("invalid port");
+ usage(stderr);
+ exit(1);
+ }
+ break;
+
+ case OPT_BAUD_RATE:
+ baud_rate = atoi(optarg);
+ baud_rate = value_to_baud(baud_rate);
+ if(baud_rate == (speed_t) -1) {
+ usage(stderr);
+ exit(1);
+ }
+ break;
+
+ case OPT_GGA:
+ gga = atoi(optarg);
+ break;
+
+ case OPT_GSA:
+ gsa = atoi(optarg);
+ break;
+
+ case OPT_GSV:
+ gsv = atoi(optarg);
+ break;
+
+ case OPT_GLL:
+ gll = atoi(optarg);
+ break;
+
+ case OPT_RMC:
+ rmc = atoi(optarg);
+ break;
+
+ case OPT_VTG:
+ vtg = atoi(optarg);
+ break;
+
+#if CONFIG_HAVE_ZDA
+ case OPT_ZDA:
+ zda = atoi(optarg);
+ break;
+#endif
+
+#if CONFIG_CAN_RESET
+ case OPT_START_MODE:
+ start_mode = (uint8_t) atoi(optarg);
+ break;
+
+ case OPT_LATITUDE:
+ latitude = (int16_t) atoi(optarg);
+ break;
+
+ case OPT_LONGITUDE:
+ longitude = (int16_t) atoi(optarg);
+ break;
+
+ case OPT_ALTITUDE:
+ altitude = (int16_t) atoi(optarg);
+ break;
+#endif
+
+#if CONFIG_CAN_DEFAULT
+ case OPT_FACTORY_DEFAULTS:
+ factory_defaults = 1;
+ break;
+#endif
+
+ case OPT_REMOTE_HOST:
+ snprintf(remote_host, sizeof(remote_host), "%s", optarg);
+ break;
+
+ case OPT_VERSION:
+ print_version("venus-gps");
+ exit(0);
+ break;
+
+ case OPT_HELP:
+ usage(stdout);
+ exit(0);
+ break;
+
+ default:
+ usage(stderr);
+ exit(1);
+ }
+ }
+
+ if(optind < argc) {
+ usage(stderr);
+ exit(1);
+ }
+
+ if(daemonize) {
+#if CONFIG_USE_SYSLOG
+ daemon(0, 0);
+#else
+ daemon(0, 1);
+#endif
+ }
+
+#if CONFIG_USE_SYSLOG
+ openlog("VenusGPS", LOG_NDELAY, LOG_FACILITY);
+# if DEBUG
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+# else
+ setlogmask(LOG_UPTO(LOG_INFO));
+# endif
+#endif
+
+ tty = venus_open(device, baud_rate);
+ if(tty < 0) {
+ log_error("failed to open tty: errno: %d", errno);
+ exit(1);
+ }
+
+ venus_conf_format(tty, MSG_TYPE_BINARY, UPDATE_ATTR_SRAM);
+ venus_query_sw_version(tty, SW_TYPE_RESERVED);
+
+ if(factory_defaults) {
+ venus_factory_defaults(tty, FACTORY_DEFAULTS_REBOOT);
+ log_notice("setting factory defaults");
+ exit(0);
+ }
+ if(start_mode != (typeof(start_mode)) -1) {
+ venus_system_restart(tty, start_mode, time(NULL), latitude, longitude, altitude);
+ log_notice("issuing system restart");
+ exit(0);
+ }
+
+ venus_conf_nmea(tty, gga, gsa, gsv, gll, rmc, vtg, zda, UPDATE_ATTR_SRAM);
+
+ venus_conf_format(tty, MSG_TYPE_NMEA, UPDATE_ATTR_SRAM);
+
+ close(tty);
+
+ if(proto == IPPROTO_TCP) {
+ if(mode == MODE_SERVER) {
+ tcp_server();
+ } else {
+ if(!*remote_host) {
+ log_notice("remote-host must be specified");
+ exit(1);
+ }
+ tcp_client();
+ }
+ } else if(proto == IPPROTO_UDP) {
+ if(mode == MODE_SERVER) {
+ udp_server();
+ } else {
+ if(!*remote_host) {
+ log_notice("remote-host must be specified");
+ exit(1);
+ }
+ udp_client();
+ }
+ }
+
+ return 0;
+}