diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2019-09-15 15:02:45 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2019-10-11 09:24:22 +0000 |
commit | 81795f3023a7a03dac5da28fd4b5bf48100a457d (patch) | |
tree | a192909338ccbef83b9f5ad3504abe2e5102de9c /plugins/simtech | |
parent | 583d53a7dfe5a05ea201ea09f248e15e852bf3b3 (diff) |
simtech: implement GPS support with AT+CGPS
The new logic is available in a new 'MMSharedSimtech' interface which
is implemented both by the generic AT-based MMBroadbandModemSimtech
modem, and by a new QMI-based new MMBroadbandModemQmiSimtech.
Fixes https://gitlab.freedesktop.org/mobile-broadband/ModemManager/issues/112
Diffstat (limited to 'plugins/simtech')
-rw-r--r-- | plugins/simtech/mm-broadband-modem-qmi-simtech.c | 91 | ||||
-rw-r--r-- | plugins/simtech/mm-broadband-modem-qmi-simtech.h | 47 | ||||
-rw-r--r-- | plugins/simtech/mm-broadband-modem-simtech.c | 62 | ||||
-rw-r--r-- | plugins/simtech/mm-plugin-simtech.c | 12 | ||||
-rw-r--r-- | plugins/simtech/mm-shared-simtech.c | 515 | ||||
-rw-r--r-- | plugins/simtech/mm-shared-simtech.h | 73 |
6 files changed, 779 insertions, 21 deletions
diff --git a/plugins/simtech/mm-broadband-modem-qmi-simtech.c b/plugins/simtech/mm-broadband-modem-qmi-simtech.c new file mode 100644 index 00000000..0b05e66d --- /dev/null +++ b/plugins/simtech/mm-broadband-modem-qmi-simtech.c @@ -0,0 +1,91 @@ +/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es> + */ + +#include <config.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> + +#include "ModemManager.h" +#include "mm-log.h" +#include "mm-errors-types.h" +#include "mm-iface-modem-location.h" +#include "mm-broadband-modem-qmi-simtech.h" +#include "mm-shared-simtech.h" + +static void iface_modem_location_init (MMIfaceModemLocation *iface); +static void shared_simtech_init (MMSharedSimtech *iface); + +static MMIfaceModemLocation *iface_modem_location_parent; + +G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQmiSimtech, mm_broadband_modem_qmi_simtech, MM_TYPE_BROADBAND_MODEM_QMI, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_SIMTECH, shared_simtech_init)) + +/*****************************************************************************/ + +MMBroadbandModemQmiSimtech * +mm_broadband_modem_qmi_simtech_new (const gchar *device, + const gchar **drivers, + const gchar *plugin, + guint16 vendor_id, + guint16 product_id) +{ + return g_object_new (MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH, + MM_BASE_MODEM_DEVICE, device, + MM_BASE_MODEM_DRIVERS, drivers, + MM_BASE_MODEM_PLUGIN, plugin, + MM_BASE_MODEM_VENDOR_ID, vendor_id, + MM_BASE_MODEM_PRODUCT_ID, product_id, + NULL); +} + +static void +mm_broadband_modem_qmi_simtech_init (MMBroadbandModemQmiSimtech *self) +{ +} + +static void +iface_modem_location_init (MMIfaceModemLocation *iface) +{ + iface_modem_location_parent = g_type_interface_peek_parent (iface); + + iface->load_capabilities = mm_shared_simtech_location_load_capabilities; + iface->load_capabilities_finish = mm_shared_simtech_location_load_capabilities_finish; + iface->enable_location_gathering = mm_shared_simtech_enable_location_gathering; + iface->enable_location_gathering_finish = mm_shared_simtech_enable_location_gathering_finish; + iface->disable_location_gathering = mm_shared_simtech_disable_location_gathering; + iface->disable_location_gathering_finish = mm_shared_simtech_disable_location_gathering_finish; +} + +static MMIfaceModemLocation * +peek_parent_location_interface (MMSharedSimtech *self) +{ + return iface_modem_location_parent; +} + +static void +shared_simtech_init (MMSharedSimtech *iface) +{ + iface->peek_parent_location_interface = peek_parent_location_interface; +} + +static void +mm_broadband_modem_qmi_simtech_class_init (MMBroadbandModemQmiSimtechClass *klass) +{ +} diff --git a/plugins/simtech/mm-broadband-modem-qmi-simtech.h b/plugins/simtech/mm-broadband-modem-qmi-simtech.h new file mode 100644 index 00000000..2f5b819b --- /dev/null +++ b/plugins/simtech/mm-broadband-modem-qmi-simtech.h @@ -0,0 +1,47 @@ +/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es> + */ + +#ifndef MM_BROADBAND_MODEM_QMI_SIMTECH_QMI_H +#define MM_BROADBAND_MODEM_QMI_SIMTECH_QMI_H + +#include "mm-broadband-modem-qmi.h" + +#define MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH (mm_broadband_modem_qmi_simtech_get_type ()) +#define MM_BROADBAND_MODEM_QMI_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH, MMBroadbandModemQmiSimtech)) +#define MM_BROADBAND_MODEM_QMI_SIMTECH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH, MMBroadbandModemQmiSimtechClass)) +#define MM_IS_BROADBAND_MODEM_QMI_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH)) +#define MM_IS_BROADBAND_MODEM_QMI_SIMTECH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH)) +#define MM_BROADBAND_MODEM_QMI_SIMTECH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH, MMBroadbandModemQmiSimtechClass)) + +typedef struct _MMBroadbandModemQmiSimtech MMBroadbandModemQmiSimtech; +typedef struct _MMBroadbandModemQmiSimtechClass MMBroadbandModemQmiSimtechClass; + +struct _MMBroadbandModemQmiSimtech { + MMBroadbandModemQmi parent; +}; + +struct _MMBroadbandModemQmiSimtechClass{ + MMBroadbandModemQmiClass parent; +}; + +GType mm_broadband_modem_qmi_simtech_get_type (void); + +MMBroadbandModemQmiSimtech *mm_broadband_modem_qmi_simtech_new (const gchar *device, + const gchar **drivers, + const gchar *plugin, + guint16 vendor_id, + guint16 product_id); + +#endif /* MM_BROADBAND_MODEM_QMI_SIMTECH_H */ diff --git a/plugins/simtech/mm-broadband-modem-simtech.c b/plugins/simtech/mm-broadband-modem-simtech.c index b2001146..4294538d 100644 --- a/plugins/simtech/mm-broadband-modem-simtech.c +++ b/plugins/simtech/mm-broadband-modem-simtech.c @@ -32,17 +32,24 @@ #include "mm-base-modem-at.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" +#include "mm-iface-modem-location.h" +#include "mm-shared-simtech.h" #include "mm-broadband-modem-simtech.h" -static void iface_modem_init (MMIfaceModem *iface); -static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); +static void iface_modem_init (MMIfaceModem *iface); +static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); +static void iface_modem_location_init (MMIfaceModemLocation *iface); +static void shared_simtech_init (MMSharedSimtech *iface); -static MMIfaceModem *iface_modem_parent; -static MMIfaceModem3gpp *iface_modem_3gpp_parent; +static MMIfaceModem *iface_modem_parent; +static MMIfaceModem3gpp *iface_modem_3gpp_parent; +static MMIfaceModemLocation *iface_modem_location_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemSimtech, mm_broadband_modem_simtech, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) - G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)) + G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_SIMTECH, shared_simtech_init)) /*****************************************************************************/ /* Setup/Cleanup unsolicited events (3GPP interface) */ @@ -828,6 +835,21 @@ mm_broadband_modem_simtech_init (MMBroadbandModemSimtech *self) } static void +iface_modem_init (MMIfaceModem *iface) +{ + iface_modem_parent = g_type_interface_peek_parent (iface); + + iface->load_access_technologies = load_access_technologies; + iface->load_access_technologies_finish = load_access_technologies_finish; + iface->load_supported_modes = load_supported_modes; + iface->load_supported_modes_finish = load_supported_modes_finish; + iface->load_current_modes = load_current_modes; + iface->load_current_modes_finish = load_current_modes_finish; + iface->set_current_modes = set_current_modes; + iface->set_current_modes_finish = set_current_modes_finish; +} + +static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface) { iface_modem_3gpp_parent = g_type_interface_peek_parent (iface); @@ -844,18 +866,28 @@ iface_modem_3gpp_init (MMIfaceModem3gpp *iface) } static void -iface_modem_init (MMIfaceModem *iface) +iface_modem_location_init (MMIfaceModemLocation *iface) { - iface_modem_parent = g_type_interface_peek_parent (iface); + iface_modem_location_parent = g_type_interface_peek_parent (iface); - iface->load_access_technologies = load_access_technologies; - iface->load_access_technologies_finish = load_access_technologies_finish; - iface->load_supported_modes = load_supported_modes; - iface->load_supported_modes_finish = load_supported_modes_finish; - iface->load_current_modes = load_current_modes; - iface->load_current_modes_finish = load_current_modes_finish; - iface->set_current_modes = set_current_modes; - iface->set_current_modes_finish = set_current_modes_finish; + iface->load_capabilities = mm_shared_simtech_location_load_capabilities; + iface->load_capabilities_finish = mm_shared_simtech_location_load_capabilities_finish; + iface->enable_location_gathering = mm_shared_simtech_enable_location_gathering; + iface->enable_location_gathering_finish = mm_shared_simtech_enable_location_gathering_finish; + iface->disable_location_gathering = mm_shared_simtech_disable_location_gathering; + iface->disable_location_gathering_finish = mm_shared_simtech_disable_location_gathering_finish; +} + +static MMIfaceModemLocation * +peek_parent_location_interface (MMSharedSimtech *self) +{ + return iface_modem_location_parent; +} + +static void +shared_simtech_init (MMSharedSimtech *iface) +{ + iface->peek_parent_location_interface = peek_parent_location_interface; } static void diff --git a/plugins/simtech/mm-plugin-simtech.c b/plugins/simtech/mm-plugin-simtech.c index fb3a7950..79afeb54 100644 --- a/plugins/simtech/mm-plugin-simtech.c +++ b/plugins/simtech/mm-plugin-simtech.c @@ -26,7 +26,7 @@ #include "mm-broadband-modem-simtech.h" #if defined WITH_QMI -#include "mm-broadband-modem-qmi.h" +#include "mm-broadband-modem-qmi-simtech.h" #endif G_DEFINE_TYPE (MMPluginSimtech, mm_plugin_simtech, MM_TYPE_PLUGIN) @@ -48,11 +48,11 @@ create_modem (MMPlugin *self, #if defined WITH_QMI if (mm_port_probe_list_has_qmi_port (probes)) { mm_dbg ("QMI-powered SimTech modem found..."); - return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid, - drivers, - mm_plugin_get_name (self), - vendor, - product)); + return MM_BASE_MODEM (mm_broadband_modem_qmi_simtech_new (uid, + drivers, + mm_plugin_get_name (self), + vendor, + product)); } #endif diff --git a/plugins/simtech/mm-shared-simtech.c b/plugins/simtech/mm-shared-simtech.c new file mode 100644 index 00000000..756713c5 --- /dev/null +++ b/plugins/simtech/mm-shared-simtech.c @@ -0,0 +1,515 @@ +/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es> + */ + +#include <config.h> + +#include <glib-object.h> +#include <gio/gio.h> + +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-log.h" +#include "mm-iface-modem.h" +#include "mm-iface-modem-location.h" +#include "mm-base-modem.h" +#include "mm-base-modem-at.h" +#include "mm-shared-simtech.h" + +/*****************************************************************************/ +/* Private data context */ + +#define PRIVATE_TAG "shared-simtech-private-tag" +static GQuark private_quark; + +typedef enum { + FEATURE_SUPPORT_UNKNOWN, + FEATURE_NOT_SUPPORTED, + FEATURE_SUPPORTED, +} FeatureSupport; + +typedef struct { + /* location */ + MMIfaceModemLocation *iface_modem_location_parent; + MMModemLocationSource supported_sources; + MMModemLocationSource enabled_sources; + FeatureSupport cgps_support; +} Private; + +static Private * +get_private (MMSharedSimtech *self) +{ + Private *priv; + + if (G_UNLIKELY (!private_quark)) + private_quark = (g_quark_from_static_string (PRIVATE_TAG)); + + priv = g_object_get_qdata (G_OBJECT (self), private_quark); + if (!priv) { + priv = g_new0 (Private, 1); + priv->supported_sources = MM_MODEM_LOCATION_SOURCE_NONE; + priv->enabled_sources = MM_MODEM_LOCATION_SOURCE_NONE; + priv->cgps_support = FEATURE_SUPPORT_UNKNOWN; + + /* Setup parent class' MMIfaceModemLocation */ + g_assert (MM_SHARED_SIMTECH_GET_INTERFACE (self)->peek_parent_location_interface); + priv->iface_modem_location_parent = MM_SHARED_SIMTECH_GET_INTERFACE (self)->peek_parent_location_interface (self); + + g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, g_free); + } + + return priv; +} + +/*****************************************************************************/ +/* GPS trace received */ + +static void +trace_received (MMPortSerialGps *port, + const gchar *trace, + MMIfaceModemLocation *self) +{ + /* Helper to debug GPS location related issues. Don't depend on a real GPS + * fix for debugging, just use some random values to update */ +#if 0 + if (g_str_has_prefix (trace, "$GPGGA")) { + GString *str; + GDateTime *now; + + now = g_date_time_new_now_utc (); + str = g_string_new (""); + g_string_append_printf (str, + "$GPGGA,%02u%02u%02u,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47", + g_date_time_get_hour (now), + g_date_time_get_minute (now), + g_date_time_get_second (now)); + mm_iface_modem_location_gps_update (self, str->str); + g_string_free (str, TRUE); + g_date_time_unref (now); + return; + } +#endif + + mm_iface_modem_location_gps_update (self, trace); +} + +/*****************************************************************************/ +/* Location capabilities loading (Location interface) */ + +MMModemLocationSource +mm_shared_simtech_location_load_capabilities_finish (MMIfaceModemLocation *self, + GAsyncResult *res, + GError **error) +{ + GError *inner_error = NULL; + gssize aux; + + aux = g_task_propagate_int (G_TASK (res), &inner_error); + if (inner_error) { + g_propagate_error (error, inner_error); + return MM_MODEM_LOCATION_SOURCE_NONE; + } + return (MMModemLocationSource) aux; +} + +static void probe_gps_features (GTask *task); + +static void +cgps_test_ready (MMBaseModem *self, + GAsyncResult *res, + GTask *task) +{ + Private *priv; + + priv = get_private (MM_SHARED_SIMTECH (self)); + + if (!mm_base_modem_at_command_finish (self, res, NULL)) + priv->cgps_support = FEATURE_NOT_SUPPORTED; + else + priv->cgps_support = FEATURE_SUPPORTED; + + probe_gps_features (task); +} + +static void +probe_gps_features (GTask *task) +{ + MMSharedSimtech *self; + MMModemLocationSource sources; + Private *priv; + + self = MM_SHARED_SIMTECH (g_task_get_source_object (task)); + priv = get_private (self); + + /* Need to check if CGPS supported... */ + if (priv->cgps_support == FEATURE_SUPPORT_UNKNOWN) { + mm_base_modem_at_command (MM_BASE_MODEM (self), "+CGPS=?", 3, TRUE, (GAsyncReadyCallback) cgps_test_ready, task); + return; + } + + /* All GPS features probed */ + + /* Recover parent sources */ + sources = GPOINTER_TO_UINT (g_task_get_task_data (task)); + + if (priv->cgps_support == FEATURE_SUPPORTED) { + mm_dbg ("GPS commands supported: GPS capabilities enabled"); + + /* We only flag as supported by this implementation those sources not already + * supported by the parent implementation */ + if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) + priv->supported_sources |= MM_MODEM_LOCATION_SOURCE_GPS_NMEA; + if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_RAW)) + priv->supported_sources |= MM_MODEM_LOCATION_SOURCE_GPS_RAW; + if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) + priv->supported_sources |= MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED; + + sources |= priv->supported_sources; + + /* Add handler for the NMEA traces in the GPS data port */ + mm_port_serial_gps_add_trace_handler (mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)), + (MMPortSerialGpsTraceFn)trace_received, + self, + NULL); + } else + mm_dbg ("No GPS command supported: no GPS capabilities"); + + g_task_return_int (task, (gssize) sources); + g_object_unref (task); +} + +static void +parent_load_capabilities_ready (MMIfaceModemLocation *self, + GAsyncResult *res, + GTask *task) +{ + MMModemLocationSource sources; + GError *error = NULL; + Private *priv; + + priv = get_private (MM_SHARED_SIMTECH (self)); + + sources = priv->iface_modem_location_parent->load_capabilities_finish (self, res, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* Now our own check. If we don't have any GPS port, we're done */ + if (!mm_base_modem_peek_port_gps (MM_BASE_MODEM (self))) { + mm_dbg ("No GPS data port found: no GPS capabilities"); + g_task_return_int (task, sources); + g_object_unref (task); + return; + } + + /* Cache sources supported by the parent */ + g_task_set_task_data (task, GUINT_TO_POINTER (sources), NULL); + + /* Probe all GPS features */ + probe_gps_features (task); +} + +void +mm_shared_simtech_location_load_capabilities (MMIfaceModemLocation *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + Private *priv; + GTask *task; + + priv = get_private (MM_SHARED_SIMTECH (self)); + task = g_task_new (self, NULL, callback, user_data); + + g_assert (priv->iface_modem_location_parent); + g_assert (priv->iface_modem_location_parent->load_capabilities); + g_assert (priv->iface_modem_location_parent->load_capabilities_finish); + + priv->iface_modem_location_parent->load_capabilities (self, + (GAsyncReadyCallback)parent_load_capabilities_ready, + task); +} + +/*****************************************************************************/ +/* Disable location gathering (Location interface) */ + +gboolean +mm_shared_simtech_disable_location_gathering_finish (MMIfaceModemLocation *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +disable_cgps_ready (MMBaseModem *self, + GAsyncResult *res, + GTask *task) +{ + MMModemLocationSource source; + Private *priv; + GError *error = NULL; + + priv = get_private (MM_SHARED_SIMTECH (self)); + + mm_base_modem_at_command_finish (self, res, &error); + + /* Only use the GPS port in NMEA/RAW setups */ + source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task)); + if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { + MMPortSerialGps *gps_port; + + /* Even if we get an error here, we try to close the GPS port */ + gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)); + if (gps_port) + mm_port_serial_close (MM_PORT_SERIAL (gps_port)); + } + + if (error) + g_task_return_error (task, error); + else { + priv->enabled_sources &= source; + g_task_return_boolean (task, TRUE); + } + g_object_unref (task); +} + +static void +parent_disable_location_gathering_ready (MMIfaceModemLocation *self, + GAsyncResult *res, + GTask *task) +{ + GError *error; + Private *priv; + + priv = get_private (MM_SHARED_SIMTECH (self)); + + g_assert (priv->iface_modem_location_parent); + if (!priv->iface_modem_location_parent->disable_location_gathering_finish (self, res, &error)) + g_task_return_error (task, error); + else + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +void +mm_shared_simtech_disable_location_gathering (MMIfaceModemLocation *self, + MMModemLocationSource source, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMModemLocationSource enabled_sources; + Private *priv; + GTask *task; + + task = g_task_new (self, NULL, callback, user_data); + g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL); + + priv = get_private (MM_SHARED_SIMTECH (self)); + g_assert (priv->iface_modem_location_parent); + + /* Only consider request if it applies to one of the sources we are + * supporting, otherwise run parent disable */ + if (!(priv->supported_sources & source)) { + /* If disabling implemented by the parent, run it. */ + if (priv->iface_modem_location_parent->disable_location_gathering && + priv->iface_modem_location_parent->disable_location_gathering_finish) { + priv->iface_modem_location_parent->disable_location_gathering (self, + source, + (GAsyncReadyCallback)parent_disable_location_gathering_ready, + task); + return; + } + /* Otherwise, we're done */ + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + } + + /* We only expect GPS sources here */ + g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | + MM_MODEM_LOCATION_SOURCE_GPS_RAW | + MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)); + + /* Flag as disabled to see how many others we would have left enabled */ + enabled_sources = priv->enabled_sources; + enabled_sources &= ~source; + + /* If there are still GPS-related sources enabled, do nothing else */ + if (enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | + MM_MODEM_LOCATION_SOURCE_GPS_RAW | + MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) { + priv->enabled_sources &= ~source; + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + } + + /* Stop GPS engine if all GPS-related sources are disabled */ + g_assert (priv->cgps_support == FEATURE_SUPPORTED); + mm_base_modem_at_command (MM_BASE_MODEM (self), + "+CGPS=0", + 10, + FALSE, + (GAsyncReadyCallback) disable_cgps_ready, + task); +} + +/*****************************************************************************/ +/* Enable location gathering (Location interface) */ + +gboolean +mm_shared_simtech_enable_location_gathering_finish (MMIfaceModemLocation *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +enable_cgps_ready (MMBaseModem *self, + GAsyncResult *res, + GTask *task) +{ + MMModemLocationSource source; + GError *error = NULL; + Private *priv; + + priv = get_private (MM_SHARED_SIMTECH (self)); + + if (!mm_base_modem_at_command_finish (self, res, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* Only use the GPS port in NMEA/RAW setups */ + source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task)); + if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | + MM_MODEM_LOCATION_SOURCE_GPS_RAW)) { + MMPortSerialGps *gps_port; + + gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)); + if (!gps_port || !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) { + if (error) + g_task_return_error (task, error); + else + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't open raw GPS serial port"); + g_object_unref (task); + return; + } + } + + priv->enabled_sources |= source; + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void +parent_enable_location_gathering_ready (MMIfaceModemLocation *self, + GAsyncResult *res, + GTask *task) +{ + GError *error; + Private *priv; + + priv = get_private (MM_SHARED_SIMTECH (self)); + + g_assert (priv->iface_modem_location_parent); + if (!priv->iface_modem_location_parent->enable_location_gathering_finish (self, res, &error)) + g_task_return_error (task, error); + else + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +void +mm_shared_simtech_enable_location_gathering (MMIfaceModemLocation *self, + MMModemLocationSource source, + GAsyncReadyCallback callback, + gpointer user_data) +{ + Private *priv; + GTask *task; + + task = g_task_new (self, NULL, callback, user_data); + g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL); + + priv = get_private (MM_SHARED_SIMTECH (self)); + g_assert (priv->iface_modem_location_parent); + g_assert (priv->iface_modem_location_parent->enable_location_gathering); + g_assert (priv->iface_modem_location_parent->enable_location_gathering_finish); + + /* Only consider request if it applies to one of the sources we are + * supporting, otherwise run parent enable */ + if (!(priv->supported_sources & source)) { + priv->iface_modem_location_parent->enable_location_gathering (self, + source, + (GAsyncReadyCallback)parent_enable_location_gathering_ready, + task); + return; + } + + /* We only expect GPS sources here */ + g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | + MM_MODEM_LOCATION_SOURCE_GPS_RAW | + MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)); + + /* If GPS already started, store new flag and we're done */ + if (priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | + MM_MODEM_LOCATION_SOURCE_GPS_RAW | + MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) { + priv->enabled_sources |= source; + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + } + + g_assert (priv->cgps_support == FEATURE_SUPPORTED); + mm_base_modem_at_command (MM_BASE_MODEM (self), + "+CGPS=1,1", + 10, + FALSE, + (GAsyncReadyCallback) enable_cgps_ready, + task); +} + +/*****************************************************************************/ + +static void +shared_simtech_init (gpointer g_iface) +{ +} + +GType +mm_shared_simtech_get_type (void) +{ + static GType shared_simtech_type = 0; + + if (!G_UNLIKELY (shared_simtech_type)) { + static const GTypeInfo info = { + sizeof (MMSharedSimtech), /* class_size */ + shared_simtech_init, /* base_init */ + NULL, /* base_finalize */ + }; + + shared_simtech_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedSimtech", &info, 0); + g_type_interface_add_prerequisite (shared_simtech_type, MM_TYPE_IFACE_MODEM_LOCATION); + } + + return shared_simtech_type; +} diff --git a/plugins/simtech/mm-shared-simtech.h b/plugins/simtech/mm-shared-simtech.h new file mode 100644 index 00000000..48342f3c --- /dev/null +++ b/plugins/simtech/mm-shared-simtech.h @@ -0,0 +1,73 @@ +/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es> + */ + +#ifndef MM_SHARED_SIMTECH_H +#define MM_SHARED_SIMTECH_H + +#include <glib-object.h> +#include <gio/gio.h> + +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-broadband-modem.h" +#include "mm-iface-modem.h" +#include "mm-iface-modem-location.h" +#include "mm-iface-modem-voice.h" +#include "mm-iface-modem-time.h" + +#define MM_TYPE_SHARED_SIMTECH (mm_shared_simtech_get_type ()) +#define MM_SHARED_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_SIMTECH, MMSharedSimtech)) +#define MM_IS_SHARED_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_SIMTECH)) +#define MM_SHARED_SIMTECH_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_SIMTECH, MMSharedSimtech)) + +typedef struct _MMSharedSimtech MMSharedSimtech; + +struct _MMSharedSimtech { + GTypeInterface g_iface; + + /* Peek location interface of the parent class of the object */ + MMIfaceModemLocation * (* peek_parent_location_interface) (MMSharedSimtech *self); +}; + +GType mm_shared_simtech_get_type (void); + +/*****************************************************************************/ +/* Location interface */ + +void mm_shared_simtech_location_load_capabilities (MMIfaceModemLocation *self, + GAsyncReadyCallback callback, + gpointer user_data); +MMModemLocationSource mm_shared_simtech_location_load_capabilities_finish (MMIfaceModemLocation *self, + GAsyncResult *res, + GError **error); + +void mm_shared_simtech_enable_location_gathering (MMIfaceModemLocation *self, + MMModemLocationSource source, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_shared_simtech_enable_location_gathering_finish (MMIfaceModemLocation *self, + GAsyncResult *res, + GError **error); + +void mm_shared_simtech_disable_location_gathering (MMIfaceModemLocation *self, + MMModemLocationSource source, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_shared_simtech_disable_location_gathering_finish (MMIfaceModemLocation *self, + GAsyncResult *res, + GError **error); + +#endif /* MM_SHARED_SIMTECH_H */ |