diff options
Diffstat (limited to 'plugins/quectel/mm-shared-quectel.c')
-rw-r--r-- | plugins/quectel/mm-shared-quectel.c | 1039 |
1 files changed, 0 insertions, 1039 deletions
diff --git a/plugins/quectel/mm-shared-quectel.c b/plugins/quectel/mm-shared-quectel.c deleted file mode 100644 index 47d7cd33..00000000 --- a/plugins/quectel/mm-shared-quectel.c +++ /dev/null @@ -1,1039 +0,0 @@ -/* -*- 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) 2018-2020 Aleksander Morgado <aleksander@aleksander.es> - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. - */ - -#include <config.h> - -#include <glib-object.h> -#include <gio/gio.h> - -#define _LIBMM_INSIDE_MM -#include <libmm-glib.h> - -#include "mm-log-object.h" -#include "mm-iface-modem.h" -#include "mm-iface-modem-firmware.h" -#include "mm-iface-modem-location.h" -#include "mm-base-modem.h" -#include "mm-base-modem-at.h" -#include "mm-shared-quectel.h" -#include "mm-modem-helpers-quectel.h" - -#if defined WITH_MBIM -#include "mm-broadband-modem-mbim.h" -#endif - -/*****************************************************************************/ -/* Private context */ - -#define PRIVATE_TAG "shared-quectel-private-tag" -static GQuark private_quark; - -typedef enum { - FEATURE_SUPPORT_UNKNOWN, - FEATURE_NOT_SUPPORTED, - FEATURE_SUPPORTED, -} FeatureSupport; - -typedef struct { - MMBroadbandModemClass *broadband_modem_class_parent; - MMIfaceModem *iface_modem_parent; - MMIfaceModemLocation *iface_modem_location_parent; - MMModemLocationSource provided_sources; - MMModemLocationSource enabled_sources; - FeatureSupport qgps_supported; - GRegex *qgpsurc_regex; - GRegex *qlwurc_regex; - GRegex *rdy_regex; -} Private; - -static void -private_free (Private *priv) -{ - g_regex_unref (priv->qgpsurc_regex); - g_regex_unref (priv->qlwurc_regex); - g_regex_unref (priv->rdy_regex); - g_slice_free (Private, priv); -} - -static Private * -get_private (MMSharedQuectel *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_slice_new0 (Private); - - priv->provided_sources = MM_MODEM_LOCATION_SOURCE_NONE; - priv->enabled_sources = MM_MODEM_LOCATION_SOURCE_NONE; - priv->qgps_supported = FEATURE_SUPPORT_UNKNOWN; - priv->qgpsurc_regex = g_regex_new ("\\r\\n\\+QGPSURC:.*", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - priv->qlwurc_regex = g_regex_new ("\\r\\n\\+QLWURC:.*", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - priv->rdy_regex = g_regex_new ("\\r\\nRDY", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - - g_assert (priv->qgpsurc_regex); - g_assert (priv->qlwurc_regex); - g_assert (priv->rdy_regex); - - g_assert (MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_broadband_modem_class); - priv->broadband_modem_class_parent = MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_broadband_modem_class (self); - - g_assert (MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_modem_location_interface); - priv->iface_modem_location_parent = MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_modem_location_interface (self); - - g_assert (MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_modem_interface); - priv->iface_modem_parent = MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_modem_interface (self); - - g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); - } - return priv; -} - -/*****************************************************************************/ -/* RDY unsolicited event handler */ - -static void -rdy_handler (MMPortSerialAt *port, - GMatchInfo *match_info, - MMBroadbandModem *self) -{ - /* The RDY URC indicates a modem reset that may or may not go hand-in-hand - * with USB re-enumeration. For the latter case, we must make sure to - * re-synchronize modem and ModemManager states by re-probing. - */ - mm_obj_warn (self, "modem reset detected, triggering reprobe"); - mm_base_modem_set_reprobe (MM_BASE_MODEM (self), TRUE); - mm_base_modem_set_valid (MM_BASE_MODEM (self), FALSE); -} - -/*****************************************************************************/ -/* Setup ports (Broadband modem class) */ - -void -mm_shared_quectel_setup_ports (MMBroadbandModem *self) -{ - Private *priv; - MMPortSerialAt *ports[2]; - guint i; - - priv = get_private (MM_SHARED_QUECTEL (self)); - g_assert (priv->broadband_modem_class_parent); - g_assert (priv->broadband_modem_class_parent->setup_ports); - - /* Parent setup always first */ - priv->broadband_modem_class_parent->setup_ports (self); - - ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); - ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); - - /* Enable/disable unsolicited events in given port */ - for (i = 0; i < G_N_ELEMENTS (ports); i++) { - if (!ports[i]) - continue; - - /* Ignore +QGPSURC */ - mm_port_serial_at_add_unsolicited_msg_handler ( - ports[i], - priv->qgpsurc_regex, - NULL, NULL, NULL); - - /* Ignore +QLWURC */ - mm_port_serial_at_add_unsolicited_msg_handler ( - ports[i], - priv->qlwurc_regex, - NULL, NULL, NULL); - - /* Handle RDY */ - mm_port_serial_at_add_unsolicited_msg_handler ( - ports[i], - priv->rdy_regex, - (MMPortSerialAtUnsolicitedMsgFn)rdy_handler, - self, - NULL); - } -} - -/*****************************************************************************/ -/* Firmware update settings loading (Firmware interface) */ - -MMFirmwareUpdateSettings * -mm_shared_quectel_firmware_load_update_settings_finish (MMIfaceModemFirmware *self, - GAsyncResult *res, - GError **error) -{ - return g_task_propagate_pointer (G_TASK (res), error); -} - -static gboolean -quectel_is_sahara_supported (MMBaseModem *modem, - MMPort *port) -{ - return mm_kernel_device_get_global_property_as_boolean (mm_port_peek_kernel_device (port), "ID_MM_QUECTEL_SAHARA"); -} - -static gboolean -quectel_is_firehose_supported (MMBaseModem *modem, - MMPort *port) -{ - return mm_kernel_device_get_global_property_as_boolean (mm_port_peek_kernel_device (port), "ID_MM_QUECTEL_FIREHOSE"); -} - -static MMModemFirmwareUpdateMethod -quectel_get_firmware_update_methods (MMBaseModem *modem, - MMPort *port) -{ - MMModemFirmwareUpdateMethod update_methods; - - update_methods = MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE; - - if (quectel_is_firehose_supported (modem, port)) - update_methods |= MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE; - if (quectel_is_sahara_supported (modem, port)) - update_methods |= MM_MODEM_FIRMWARE_UPDATE_METHOD_SAHARA; - - return update_methods; -} - -static void -quectel_at_port_get_firmware_version_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) -{ - MMFirmwareUpdateSettings *update_settings; - const gchar *version; - - update_settings = g_task_get_task_data (task); - - version = mm_base_modem_at_command_finish (modem, res, NULL); - if (version) - mm_firmware_update_settings_set_version (update_settings, version); - - g_task_return_pointer (task, g_object_ref (update_settings), g_object_unref); - g_object_unref (task); -} - -#if defined WITH_MBIM -static void -quectel_mbim_port_get_firmware_version_ready (MbimDevice *device, - GAsyncResult *res, - GTask *task) -{ - g_autoptr(MbimMessage) response = NULL; - guint32 version_id; - g_autofree gchar *version_str = NULL; - MMFirmwareUpdateSettings *update_settings; - - update_settings = g_task_get_task_data (task); - - response = mbim_device_command_finish (device, res, NULL); - if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, NULL) && - mbim_message_qdu_quectel_read_version_response_parse (response, &version_id, &version_str, NULL)) { - mm_firmware_update_settings_set_version (update_settings, version_str); - } - - g_task_return_pointer (task, g_object_ref (update_settings), g_object_unref); - g_object_unref (task); -} -#endif - -static void -qfastboot_test_ready (MMBaseModem *self, - GAsyncResult *res, - GTask *task) -{ - MMFirmwareUpdateSettings *update_settings; - - update_settings = g_task_get_task_data (task); - - /* Set update method */ - if (mm_base_modem_at_command_finish (self, res, NULL)) { - mm_firmware_update_settings_set_method (update_settings, MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT); - mm_firmware_update_settings_set_fastboot_at (update_settings, "AT+QFASTBOOT"); - } else - mm_firmware_update_settings_set_method (update_settings, MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE); - - /* Fetch full firmware info */ - mm_base_modem_at_command (MM_BASE_MODEM (self), - "+QGMR?", - 3, - FALSE, - (GAsyncReadyCallback) quectel_at_port_get_firmware_version_ready, - task); -} - -static void -quectel_at_port_get_firmware_revision_ready (MMBaseModem *self, - GAsyncResult *res, - GTask *task) -{ - MMFirmwareUpdateSettings *update_settings; - MMModemFirmwareUpdateMethod update_methods; - const gchar *revision; - const gchar *name; - const gchar *id; - g_autoptr(GPtrArray) ids = NULL; - GError *error = NULL; - - update_settings = g_task_get_task_data (task); - update_methods = mm_firmware_update_settings_get_method (update_settings); - - /* Set device ids */ - ids = mm_iface_firmware_build_generic_device_ids (MM_IFACE_MODEM_FIRMWARE (self), &error); - if (error) { - mm_obj_warn (self, "failed to build generic device ids: %s", error->message); - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - /* Add device id based on modem name */ - revision = mm_base_modem_at_command_finish (self, res, NULL); - if (revision && g_utf8_validate (revision, -1, NULL)) { - name = g_strndup (revision, 7); - mm_obj_dbg (self, "revision %s converted to modem name %s", revision, name); - id = (const gchar *) g_ptr_array_index (ids, 0); - g_ptr_array_insert (ids, 0, g_strdup_printf ("%s&NAME_%s", id, name)); - } - - mm_firmware_update_settings_set_device_ids (update_settings, (const gchar **)ids->pdata); - - /* Set update methods */ - if (update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE) { - /* Fetch full firmware info */ - mm_base_modem_at_command (self, - "+QGMR?", - 3, - TRUE, - (GAsyncReadyCallback) quectel_at_port_get_firmware_version_ready, - task); - } else { - /* Check fastboot support */ - mm_base_modem_at_command (self, - "AT+QFASTBOOT=?", - 3, - TRUE, - (GAsyncReadyCallback) qfastboot_test_ready, - task); - } -} - -void -mm_shared_quectel_firmware_load_update_settings (MMIfaceModemFirmware *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GTask *task; - MMPortSerialAt *at_port; - MMModemFirmwareUpdateMethod update_methods; - MMFirmwareUpdateSettings *update_settings; -#if defined WITH_MBIM - MMPortMbim *mbim; -#endif - - task = g_task_new (self, NULL, callback, user_data); - - at_port = mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL); - if (at_port) { - update_methods = quectel_get_firmware_update_methods (MM_BASE_MODEM (self), MM_PORT (at_port)); - update_settings = mm_firmware_update_settings_new (update_methods); - g_task_set_task_data (task, update_settings, g_object_unref); - - /* Fetch modem name */ - mm_base_modem_at_command (MM_BASE_MODEM (self), - "+CGMR", - 3, - TRUE, - (GAsyncReadyCallback) quectel_at_port_get_firmware_revision_ready, - task); - - return; - } - -#if defined WITH_MBIM - mbim = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self)); - if (mbim) { - g_autoptr(MbimMessage) message = NULL; - - update_methods = quectel_get_firmware_update_methods (MM_BASE_MODEM (self), MM_PORT (mbim)); - update_settings = mm_firmware_update_settings_new (update_methods); - - /* Fetch firmware info */ - g_task_set_task_data (task, update_settings, g_object_unref); - message = mbim_message_qdu_quectel_read_version_set_new (MBIM_QDU_QUECTEL_VERSION_TYPE_FW_BUILD_ID, NULL); - mbim_device_command (mm_port_mbim_peek_device (mbim), - message, - 5, - NULL, - (GAsyncReadyCallback) quectel_mbim_port_get_firmware_version_ready, - task); - return; - } -#endif - - g_task_return_new_error (task, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't find a port to fetch firmware info"); - g_object_unref (task); -} - -/*****************************************************************************/ -/* "+QUSIM: 1" URC is emitted by Quectel modems after the USIM has been - * (re)initialized. We register a handler for this URC and perform a check - * for SIM swap when it is encountered. The motivation for this is to detect - * M2M eUICC profile switches. According to SGP.02 chapter 3.2.1, the eUICC - * shall trigger a REFRESH operation with eUICC reset when a new profile is - * enabled. The +QUSIM URC appears after the eUICC has restarted and can act - * as a trigger for profile switch check. This should basically be handled - * the same as a physical SIM swap, so the existing SIM hot swap mechanism - * is used. - */ - -static void -quectel_qusim_check_for_sim_swap_ready (MMIfaceModem *self, - GAsyncResult *res) -{ - g_autoptr(GError) error = NULL; - - if (!MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish (self, res, &error)) - mm_obj_warn (self, "couldn't check SIM swap: %s", error->message); - else - mm_obj_dbg (self, "check SIM swap completed"); -} - -static void -quectel_qusim_unsolicited_handler (MMPortSerialAt *port, - GMatchInfo *match_info, - MMIfaceModem *self) -{ - if (!MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap || - !MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish) - return; - - mm_obj_dbg (self, "checking SIM swap"); - MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap ( - self, - NULL, - NULL, - (GAsyncReadyCallback)quectel_qusim_check_for_sim_swap_ready, - NULL); -} - -/*****************************************************************************/ -/* Setup SIM hot swap context (Modem interface) */ - -gboolean -mm_shared_quectel_setup_sim_hot_swap_finish (MMIfaceModem *self, - GAsyncResult *res, - GError **error) -{ - return g_task_propagate_boolean (G_TASK (res), error); -} - -static void -parent_setup_sim_hot_swap_ready (MMIfaceModem *self, - GAsyncResult *res, - GTask *task) -{ - Private *priv; - g_autoptr(GError) error = NULL; - - priv = get_private (MM_SHARED_QUECTEL (self)); - - if (!priv->iface_modem_parent->setup_sim_hot_swap_finish (self, res, &error)) - mm_obj_dbg (self, "additional SIM hot swap detection setup failed: %s", error->message); - - /* The +QUSIM based setup never fails, so we can safely return success here */ - g_task_return_boolean (task, TRUE); - g_object_unref (task); -} - -void -mm_shared_quectel_setup_sim_hot_swap (MMIfaceModem *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - Private *priv; - MMPortSerialAt *ports[2]; - GTask *task; - guint i; - g_autoptr(GRegex) pattern = NULL; - g_autoptr(GError) error = NULL; - - priv = get_private (MM_SHARED_QUECTEL (self)); - - task = g_task_new (self, NULL, callback, user_data); - - ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); - ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); - - pattern = g_regex_new ("\\+QUSIM:\\s*1\\r\\n", G_REGEX_RAW, 0, NULL); - g_assert (pattern); - - for (i = 0; i < G_N_ELEMENTS (ports); i++) { - if (ports[i]) - mm_port_serial_at_add_unsolicited_msg_handler ( - ports[i], - pattern, - (MMPortSerialAtUnsolicitedMsgFn)quectel_qusim_unsolicited_handler, - self, - NULL); - } - - mm_obj_dbg (self, "+QUSIM detection set up"); - - if (!mm_broadband_modem_sim_hot_swap_ports_context_init (MM_BROADBAND_MODEM (self), &error)) - mm_obj_warn (self, "failed to initialize SIM hot swap ports context: %s", error->message); - - /* Now, if available, setup parent logic */ - if (priv->iface_modem_parent->setup_sim_hot_swap && - priv->iface_modem_parent->setup_sim_hot_swap_finish) { - priv->iface_modem_parent->setup_sim_hot_swap (self, - (GAsyncReadyCallback) parent_setup_sim_hot_swap_ready, - task); - return; - } - - /* Otherwise, we're done */ - g_task_return_boolean (task, TRUE); - g_object_unref (task); -} - -/*****************************************************************************/ -/* SIM hot swap cleanup (Modem interface) */ - -void -mm_shared_quectel_cleanup_sim_hot_swap (MMIfaceModem *self) -{ - mm_broadband_modem_sim_hot_swap_ports_context_reset (MM_BROADBAND_MODEM (self)); -} - -/*****************************************************************************/ -/* GPS trace received */ - -static void -trace_received (MMPortSerialGps *port, - const gchar *trace, - MMIfaceModemLocation *self) -{ - mm_iface_modem_location_gps_update (self, trace); -} - -/*****************************************************************************/ -/* Location capabilities loading (Location interface) */ - -MMModemLocationSource -mm_shared_quectel_location_load_capabilities_finish (MMIfaceModemLocation *self, - GAsyncResult *res, - GError **error) -{ - GError *inner_error = NULL; - gssize value; - - value = 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)value; -} - -static void -probe_qgps_ready (MMBaseModem *_self, - GAsyncResult *res, - GTask *task) -{ - MMSharedQuectel *self; - Private *priv; - MMModemLocationSource sources; - - self = MM_SHARED_QUECTEL (g_task_get_source_object (task)); - priv = get_private (self); - - priv->qgps_supported = (!!mm_base_modem_at_command_finish (_self, res, NULL) ? - FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED); - - mm_obj_dbg (self, "GPS management with +QGPS is %ssupported", - priv->qgps_supported ? "" : "not "); - - /* Recover parent sources */ - sources = GPOINTER_TO_UINT (g_task_get_task_data (task)); - - /* Only flag as provided those sources not already provided by the parent */ - if (priv->qgps_supported == FEATURE_SUPPORTED) { - if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) - priv->provided_sources |= MM_MODEM_LOCATION_SOURCE_GPS_NMEA; - if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_RAW)) - priv->provided_sources |= MM_MODEM_LOCATION_SOURCE_GPS_RAW; - if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) - priv->provided_sources |= MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED; - - sources |= priv->provided_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); - } - - /* So we're done, complete */ - g_task_return_int (task, sources); - g_object_unref (task); -} - -static void -parent_load_capabilities_ready (MMIfaceModemLocation *self, - GAsyncResult *res, - GTask *task) -{ - Private *priv; - MMModemLocationSource sources; - GError *error = NULL; - - priv = get_private (MM_SHARED_QUECTEL (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_obj_dbg (self, "no GPS data port found: no GPS capabilities"); - g_task_return_int (task, sources); - g_object_unref (task); - return; - } - - /* Store parent supported sources in task data */ - g_task_set_task_data (task, GUINT_TO_POINTER (sources), NULL); - - /* Probe QGPS support */ - g_assert (priv->qgps_supported == FEATURE_SUPPORT_UNKNOWN); - mm_base_modem_at_command (MM_BASE_MODEM (self), - "+QGPS=?", - 3, - TRUE, /* cached */ - (GAsyncReadyCallback)probe_qgps_ready, - task); -} - -void -mm_shared_quectel_location_load_capabilities (MMIfaceModemLocation *_self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GTask *task; - Private *priv; - - task = g_task_new (_self, NULL, callback, user_data); - priv = get_private (MM_SHARED_QUECTEL (_self)); - - /* Chain up parent's setup */ - priv->iface_modem_location_parent->load_capabilities (_self, - (GAsyncReadyCallback)parent_load_capabilities_ready, - task); -} - -/*****************************************************************************/ -/* Enable location gathering (Location interface) */ - -/* NOTES: - * 1) "+QGPSCFG=\"nmeasrc\",1" will be necessary for getting location data - * without the nmea port. - * 2) may be necessary to set "+QGPSCFG=\"gpsnmeatype\". - * 3) QGPSXTRA=1 is necessary to support XTRA assistance data for - * faster GNSS location locks. - */ -static const MMBaseModemAtCommand gps_startup[] = { - { "+QGPSCFG=\"outport\",\"usbnmea\"", 3, FALSE, mm_base_modem_response_processor_no_result_continue }, - { "+QGPS=1", 3, FALSE, mm_base_modem_response_processor_no_result_continue }, - { "+QGPSXTRA=1", 3, FALSE, mm_base_modem_response_processor_no_result_continue }, - { NULL } -}; - -gboolean -mm_shared_quectel_enable_location_gathering_finish (MMIfaceModemLocation *self, - GAsyncResult *res, - GError **error) -{ - return g_task_propagate_boolean (G_TASK (res), error); -} - -static void -gps_startup_ready (MMBaseModem *self, - GAsyncResult *res, - GTask *task) -{ - MMModemLocationSource source; - GError *error = NULL; - Private *priv; - - priv = get_private (MM_SHARED_QUECTEL (self)); - - mm_base_modem_at_sequence_finish (self, res, NULL, &error); - if (error) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - source = GPOINTER_TO_UINT (g_task_get_task_data (task)); - - /* Check if the nmea/raw gps port exists and is available */ - 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"); - } else { - /* GPS port was successfully opened */ - priv->enabled_sources |= source; - g_task_return_boolean (task, TRUE); - } - } else { - /* No need to open GPS port */ - 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 = NULL; - Private *priv; - - priv = get_private (MM_SHARED_QUECTEL (self)); - 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_quectel_enable_location_gathering (MMIfaceModemLocation *self, - MMModemLocationSource source, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GTask *task; - Private *priv; - gboolean start_gps = FALSE; - - priv = get_private (MM_SHARED_QUECTEL (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); - - task = g_task_new (self, NULL, callback, user_data); - g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL); - - /* Check if the source is provided by the parent */ - if (!(priv->provided_sources & source)) { - priv->iface_modem_location_parent->enable_location_gathering ( - self, - source, - (GAsyncReadyCallback)parent_enable_location_gathering_ready, - task); - return; - } - - /* Only start GPS engine if not done already */ - start_gps = ((source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | - MM_MODEM_LOCATION_SOURCE_GPS_RAW | - MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) && - !(priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | - MM_MODEM_LOCATION_SOURCE_GPS_RAW | - MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))); - - if (start_gps) { - mm_base_modem_at_sequence ( - MM_BASE_MODEM (self), - gps_startup, - NULL, /* response_processor_context */ - NULL, /* response_processor_context_free */ - (GAsyncReadyCallback)gps_startup_ready, - task); - return; - } - - /* If the GPS is already running just return */ - priv->enabled_sources |= source; - g_task_return_boolean (task, TRUE); - g_object_unref (task); -} - -/*****************************************************************************/ -/* Disable location gathering (Location interface) */ - -gboolean -mm_shared_quectel_disable_location_gathering_finish (MMIfaceModemLocation *self, - GAsyncResult *res, - GError **error) -{ - return g_task_propagate_boolean (G_TASK (res), error); -} - -static void -qgps_end_ready (MMBaseModem *self, - GAsyncResult *res, - GTask *task) -{ - GError *error = NULL; - - if (!mm_base_modem_at_command_finish (self, res, &error)) - g_task_return_error (task, error); - else - g_task_return_boolean (task, TRUE); - g_object_unref (task); -} - -static void -disable_location_gathering_parent_ready (MMIfaceModemLocation *self, - GAsyncResult *res, - GTask *task) -{ - GError *error = NULL; - Private *priv; - - priv = get_private (MM_SHARED_QUECTEL (self)); - 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_quectel_disable_location_gathering (MMIfaceModemLocation *self, - MMModemLocationSource source, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GTask *task; - Private *priv; - - priv = get_private (MM_SHARED_QUECTEL (self)); - g_assert (priv->iface_modem_location_parent); - - task = g_task_new (self, NULL, callback, user_data); - priv->enabled_sources &= ~source; - - /* Pass handling to parent if we don't handle it */ - if (!(source & priv->provided_sources)) { - /* The step to disable location gathering may not exist */ - 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)disable_location_gathering_parent_ready, - task); - return; - } - - g_task_return_boolean (task, TRUE); - g_object_unref (task); - return; - } - - /* Turn off gps on the modem if the source uses gps, - * and there are no other gps sources enabled */ - if ((source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | - MM_MODEM_LOCATION_SOURCE_GPS_RAW | - MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) && - !(priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | - MM_MODEM_LOCATION_SOURCE_GPS_RAW | - MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))) { - /* Close the data port if we don't need it anymore */ - if (source & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) { - MMPortSerialGps *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)); - } - - mm_base_modem_at_command (MM_BASE_MODEM (self), - "+QGPSEND", - 3, - FALSE, - (GAsyncReadyCallback)qgps_end_ready, - task); - return; - } - - g_task_return_boolean (task, TRUE); - g_object_unref (task); -} - -/*****************************************************************************/ -/* Check support (Time interface) */ - -gboolean -mm_shared_quectel_time_check_support_finish (MMIfaceModemTime *self, - GAsyncResult *res, - GError **error) -{ - return g_task_propagate_boolean (G_TASK (res), error); -} - -static void -support_cclk_query_ready (MMBaseModem *self, - GAsyncResult *res, - GTask *task) -{ - /* error never returned */ - g_task_return_boolean (task, !!mm_base_modem_at_command_finish (self, res, NULL)); - g_object_unref (task); -} - -static void -support_cclk_query (GTask *task) -{ - MMBaseModem *self; - - self = g_task_get_source_object (task); - mm_base_modem_at_command (MM_BASE_MODEM (self), - "+CCLK?", - 3, - FALSE, - (GAsyncReadyCallback)support_cclk_query_ready, - task); -} - -static void -ctzu_set_ready (MMBaseModem *self, - GAsyncResult *res, - GTask *task) -{ - g_autoptr(GError) error = NULL; - - if (!mm_base_modem_at_command_finish (self, res, &error)) - mm_obj_warn (self, "couldn't enable automatic time zone update: %s", error->message); - - support_cclk_query (task); -} - -static void -ctzu_test_ready (MMBaseModem *self, - GAsyncResult *res, - GTask *task) -{ - g_autoptr(GError) error = NULL; - const gchar *response; - gboolean supports_disable; - gboolean supports_enable; - gboolean supports_enable_update_rtc; - const gchar *cmd = NULL; - - /* If CTZU isn't supported, run CCLK right away */ - response = mm_base_modem_at_command_finish (self, res, NULL); - if (!response) { - support_cclk_query (task); - return; - } - - if (!mm_quectel_parse_ctzu_test_response (response, - self, - &supports_disable, - &supports_enable, - &supports_enable_update_rtc, - &error)) { - mm_obj_warn (self, "couldn't parse +CTZU test response: %s", error->message); - support_cclk_query (task); - return; - } - - /* Custom time support check because some Quectel modems (e.g. EC25) require - * +CTZU=3 in order to have the CCLK? time reported in localtime, instead of - * UTC time. */ - if (supports_enable_update_rtc) - cmd = "+CTZU=3"; - else if (supports_enable) - cmd = "+CTZU=1"; - - if (!cmd) { - mm_obj_warn (self, "unknown +CTZU support"); - support_cclk_query (task); - return; - } - - mm_base_modem_at_command (MM_BASE_MODEM (self), - cmd, - 3, - FALSE, - (GAsyncReadyCallback)ctzu_set_ready, - task); -} - -void -mm_shared_quectel_time_check_support (MMIfaceModemTime *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GTask *task; - - task = g_task_new (self, NULL, callback, user_data); - mm_base_modem_at_command (MM_BASE_MODEM (self), - "+CTZU=?", - 3, - TRUE, /* cached! */ - (GAsyncReadyCallback)ctzu_test_ready, - task); -} - -/*****************************************************************************/ - -static void -shared_quectel_init (gpointer g_iface) -{ -} - -GType -mm_shared_quectel_get_type (void) -{ - static GType shared_quectel_type = 0; - - if (!G_UNLIKELY (shared_quectel_type)) { - static const GTypeInfo info = { - sizeof (MMSharedQuectel), /* class_size */ - shared_quectel_init, /* base_init */ - NULL, /* base_finalize */ - }; - - shared_quectel_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedQuectel", &info, 0); - g_type_interface_add_prerequisite (shared_quectel_type, MM_TYPE_IFACE_MODEM_FIRMWARE); - } - - return shared_quectel_type; -} |