/* -*- 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 */ #include #include #include #include #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, ""); else if (*s == '\n') g_string_append (debug, ""); 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; }