diff options
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/mm-gps-serial-port.c | 218 | ||||
-rw-r--r-- | src/mm-gps-serial-port.h | 57 |
3 files changed, 278 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 416f5c76..53b77b76 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -70,7 +70,9 @@ libserial_la_SOURCES = \ mm-at-serial-port.c \ mm-at-serial-port.h \ mm-qcdm-serial-port.c \ - mm-qcdm-serial-port.h + mm-qcdm-serial-port.h \ + mm-gps-serial-port.c \ + mm-gps-serial-port.h # Daemon specific enum types DAEMON_ENUMS = \ diff --git a/src/mm-gps-serial-port.c b/src/mm-gps-serial-port.c new file mode 100644 index 00000000..cfd653c4 --- /dev/null +++ b/src/mm-gps-serial-port.c @@ -0,0 +1,218 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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: + * + * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include "mm-gps-serial-port.h" +#include "mm-log.h" + +G_DEFINE_TYPE (MMGpsSerialPort, mm_gps_serial_port, MM_TYPE_SERIAL_PORT) + +struct _MMGpsSerialPortPrivate { + /* Trace handler data */ + MMGpsSerialTraceFn callback; + gpointer user_data; + GDestroyNotify notify; + + /* Regex for all known traces */ + GRegex *known_traces_regex; +}; + +/*****************************************************************************/ + +void +mm_gps_serial_port_add_trace_handler (MMGpsSerialPort *self, + MMGpsSerialTraceFn callback, + gpointer user_data, + GDestroyNotify notify) +{ + g_return_if_fail (MM_IS_GPS_SERIAL_PORT (self)); + + if (self->priv->notify) + self->priv->notify (self->priv->user_data); + + self->priv->callback = callback; + self->priv->user_data = user_data; + self->priv->notify = notify; +} + +/*****************************************************************************/ + +static gboolean +remove_eval_cb (const GMatchInfo *match_info, + GString *result, + gpointer user_data) +{ + int *result_len = (int *) user_data; + int start; + int end; + + if (g_match_info_fetch_pos (match_info, 0, &start, &end)) + *result_len -= (end - start); + + return FALSE; +} + +static gboolean +parse_response (MMSerialPort *port, + GByteArray *response, + GError **error) +{ + MMGpsSerialPort *self = MM_GPS_SERIAL_PORT (port); + gboolean matches; + GMatchInfo *match_info; + gchar *str; + gint result_len; + guint i; + + for (i = 0; i < response->len; i++) { + /* If there is any content before the first $, + * assume it's garbage, and skip it */ + if (response->data[i] == '$') { + if (i > 0) + g_byte_array_remove_range (response, 0, i); + /* else, good, we're already started with $ */ + break; + } + } + + matches = g_regex_match_full (self->priv->known_traces_regex, + (const gchar *) response->data, + response->len, + 0, 0, &match_info, NULL); + + if (self->priv->callback) { + while (g_match_info_matches (match_info)) { + gchar *trace; + + trace = g_match_info_fetch (match_info, 0); + if (trace) { + self->priv->callback (self, trace, self->priv->user_data); + g_free (trace); + } + g_match_info_next (match_info, NULL); + } + } + + g_match_info_free (match_info); + + if (!matches) + return FALSE; + + /* Remove matches */ + result_len = response->len; + str = g_regex_replace_eval (self->priv->known_traces_regex, + (const char *) response->data, + response->len, + 0, 0, + remove_eval_cb, &result_len, NULL); + + g_byte_array_remove_range (response, 0, response->len); + g_byte_array_append (response, (const guint8 *) str, result_len); + g_free (str); + + return TRUE; +} + +/*****************************************************************************/ + +static void +debug_log (MMSerialPort *port, const char *prefix, const char *buf, gsize len) +{ + static GString *debug = NULL; + const char *s; + + if (!debug) + debug = g_string_sized_new (256); + + g_string_append (debug, prefix); + g_string_append (debug, " '"); + + s = buf; + while (len--) { + if (g_ascii_isprint (*s)) + g_string_append_c (debug, *s); + else if (*s == '\r') + g_string_append (debug, "<CR>"); + else if (*s == '\n') + g_string_append (debug, "<LF>"); + else + g_string_append_printf (debug, "\\%u", (guint8) (*s & 0xFF)); + + s++; + } + + g_string_append_c (debug, '\''); + mm_dbg ("(%s): %s", mm_port_get_device (MM_PORT (port)), debug->str); + g_string_truncate (debug, 0); +} + +/*****************************************************************************/ + +MMGpsSerialPort * +mm_gps_serial_port_new (const char *name) +{ + return MM_GPS_SERIAL_PORT (g_object_new (MM_TYPE_GPS_SERIAL_PORT, + MM_PORT_DEVICE, name, + MM_PORT_SUBSYS, MM_PORT_SUBSYS_TTY, + MM_PORT_TYPE, MM_PORT_TYPE_GPS, + NULL)); +} + +static void +mm_gps_serial_port_init (MMGpsSerialPort *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), + MM_TYPE_GPS_SERIAL_PORT, + MMGpsSerialPortPrivate); + + /* We'll assume that all traces start with the dollar sign and end with \r\n */ + self->priv->known_traces_regex = + g_regex_new ("\\$.*\\r\\n", + G_REGEX_RAW | G_REGEX_OPTIMIZE, + 0, + NULL); +} + +static void +finalize (GObject *object) +{ + MMGpsSerialPort *self = MM_GPS_SERIAL_PORT (object); + + if (self->priv->notify) + self->priv->notify (self->priv->user_data); + + g_regex_unref (self->priv->known_traces_regex); + + G_OBJECT_CLASS (mm_gps_serial_port_parent_class)->finalize (object); +} + +static void +mm_gps_serial_port_class_init (MMGpsSerialPortClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMSerialPortClass *port_class = MM_SERIAL_PORT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMGpsSerialPortPrivate)); + + /* Virtual methods */ + object_class->finalize = finalize; + + port_class->parse_response = parse_response; + port_class->debug_log = debug_log; +} diff --git a/src/mm-gps-serial-port.h b/src/mm-gps-serial-port.h new file mode 100644 index 00000000..5a7c23b1 --- /dev/null +++ b/src/mm-gps-serial-port.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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: + * + * Copyright (C) 2012 - Aleksander Morgado <aleksander@gnu.org> + */ + +#ifndef MM_GPS_SERIAL_PORT_H +#define MM_GPS_SERIAL_PORT_H + +#include <glib.h> +#include <glib-object.h> + +#include "mm-serial-port.h" + +#define MM_TYPE_GPS_SERIAL_PORT (mm_gps_serial_port_get_type ()) +#define MM_GPS_SERIAL_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_GPS_SERIAL_PORT, MMGpsSerialPort)) +#define MM_GPS_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_GPS_SERIAL_PORT, MMGpsSerialPortClass)) +#define MM_IS_GPS_SERIAL_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_GPS_SERIAL_PORT)) +#define MM_IS_GPS_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_GPS_SERIAL_PORT)) +#define MM_GPS_SERIAL_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_GPS_SERIAL_PORT, MMGpsSerialPortClass)) + +typedef struct _MMGpsSerialPort MMGpsSerialPort; +typedef struct _MMGpsSerialPortClass MMGpsSerialPortClass; +typedef struct _MMGpsSerialPortPrivate MMGpsSerialPortPrivate; + +typedef void (*MMGpsSerialTraceFn) (MMGpsSerialPort *port, + const gchar *trace, + gpointer user_data); + +struct _MMGpsSerialPort { + MMSerialPort parent; + MMGpsSerialPortPrivate *priv; +}; + +struct _MMGpsSerialPortClass { + MMSerialPortClass parent; +}; + +GType mm_gps_serial_port_get_type (void); + +MMGpsSerialPort *mm_gps_serial_port_new (const char *name); + +void mm_gps_serial_port_add_trace_handler (MMGpsSerialPort *self, + MMGpsSerialTraceFn callback, + gpointer user_data, + GDestroyNotify notify); + +#endif /* MM_GPS_SERIAL_PORT_H */ |