diff --git a/.gitignore b/.gitignore index b457b25c..fc3eacb6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,6 @@ tags /.vscode /cscope.out +/.cache subprojects/libmbim subprojects/libqmi diff --git a/src/mm-port-serial.c b/src/mm-port-serial.c index f520ff83..89687059 100644 --- a/src/mm-port-serial.c +++ b/src/mm-port-serial.c @@ -1865,6 +1865,49 @@ mm_port_serial_flash (MMPortSerial *self, ctx->flash_id = g_timeout_add (flash_time, (GSourceFunc)flash_do, self); } +/*****************************************************************************/ +/* Flush */ + +gboolean mm_port_serial_flush (MMPortSerial *self, + MMPortSerialFlushType type, + GError **error) +{ + int tcflush_type; + + g_return_val_if_fail (MM_IS_PORT_SERIAL (self), FALSE); + + if (!mm_port_serial_is_open (self)) { + g_set_error_literal (error, + MM_SERIAL_ERROR, + MM_SERIAL_ERROR_NOT_OPEN, + "The serial port is not open."); + return FALSE; + } + + if (type == MM_PORT_SERIAL_FLUSH_RX) { + tcflush_type = TCIFLUSH; + } else if (type == MM_PORT_SERIAL_FLUSH_TX) { + tcflush_type = TCOFLUSH; + } else if (type == MM_PORT_SERIAL_FLUSH_BOTH) { + tcflush_type = TCIOFLUSH; + } else { + g_assert_not_reached (); + return FALSE; + } + + g_assert (self->priv->fd >= 0); + if (tcflush (self->priv->fd, tcflush_type) != 0) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Failed to flush (%d) serial port, errno %d", + tcflush_type, errno); + return FALSE; + } + + return TRUE; +} + /*****************************************************************************/ gboolean diff --git a/src/mm-port-serial.h b/src/mm-port-serial.h index f050bdc3..706d1621 100644 --- a/src/mm-port-serial.h +++ b/src/mm-port-serial.h @@ -48,6 +48,12 @@ typedef enum { MM_PORT_SERIAL_RESPONSE_ERROR, } MMPortSerialResponseType; +typedef enum { + MM_PORT_SERIAL_FLUSH_RX, + MM_PORT_SERIAL_FLUSH_TX, + MM_PORT_SERIAL_FLUSH_BOTH, +} MMPortSerialFlushType; + typedef struct _MMPortSerial MMPortSerial; typedef struct _MMPortSerialClass MMPortSerialClass; typedef struct _MMPortSerialPrivate MMPortSerialPrivate; @@ -141,6 +147,10 @@ gboolean mm_port_serial_flash_finish (MMPortSerial *self, GError **error); void mm_port_serial_flash_cancel (MMPortSerial *self); +gboolean mm_port_serial_flush (MMPortSerial *self, + MMPortSerialFlushType type, + GError **error); + void mm_port_serial_command (MMPortSerial *self, GByteArray *command, guint32 timeout_seconds, diff --git a/src/plugins/intel/mm-bearer-xmm7360.c b/src/plugins/intel/mm-bearer-xmm7360.c index 10dd2c26..7ca3ad0e 100644 --- a/src/plugins/intel/mm-bearer-xmm7360.c +++ b/src/plugins/intel/mm-bearer-xmm7360.c @@ -26,6 +26,7 @@ #include "mm-broadband-modem-xmm7360.h" #include "mm-broadband-modem-xmm7360-rpc.h" #include "mm-port-serial-xmmrpc-xmm7360.h" +#include "mm-bind.h" G_DEFINE_TYPE (MMBearerXmm7360, mm_bearer_xmm7360, MM_TYPE_BASE_BEARER) @@ -673,6 +674,7 @@ static const MMBroadbandModemXmm7360RpcCommand disconnect_sequence[] = { }, 3, FALSE, + FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success /* response will be: L(0x0) L(0x0) L(0x5dffffff) L(0x0) (meaning unknown) */ }, @@ -686,6 +688,7 @@ static const MMBroadbandModemXmm7360RpcCommand disconnect_sequence[] = { }, 3, FALSE, + FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_final /* response will be: L(0x0) (meaning unknown) */ }, @@ -731,6 +734,7 @@ mm_bearer_xmm7360_new (MMBroadbandModemXmm7360 *modem, * g_object_new here */ bearer = g_object_new (MM_TYPE_BEARER_XMM7360, MM_BASE_BEARER_MODEM, modem, + MM_BIND_TO, modem, MM_BASE_BEARER_CONFIG, config, NULL); diff --git a/src/plugins/intel/mm-broadband-modem-xmm7360-rpc.c b/src/plugins/intel/mm-broadband-modem-xmm7360-rpc.c index 85bc009b..9f3f1773 100644 --- a/src/plugins/intel/mm-broadband-modem-xmm7360-rpc.c +++ b/src/plugins/intel/mm-broadband-modem-xmm7360-rpc.c @@ -84,6 +84,7 @@ typedef struct { gulong cancelled_id; GCancellable *parent_cancellable; const MMBroadbandModemXmm7360RpcCommand *current; + gboolean current_retry; const MMBroadbandModemXmm7360RpcCommand *sequence; guint next_command_wait_id; } RpcSequenceContext; @@ -152,6 +153,7 @@ rpc_sequence_parse_response (MMPortSerialXmmrpcXmm7360 *port, RpcSequenceContext *ctx; Xmm7360RpcResponse *response = NULL; g_autoptr(GError) command_error = NULL; + gboolean command_retry = FALSE; response = mm_port_serial_xmmrpc_xmm7360_command_finish (port, res, &command_error); @@ -173,6 +175,20 @@ rpc_sequence_parse_response (MMPortSerialXmmrpcXmm7360 *port, next->callid ? FALSE : TRUE, /* Last command in sequence? */ command_error, &result_error); + + if (processor_result == MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_FAILURE + && ctx->current->allow_retry_once && !ctx->current_retry) { + mm_obj_warn (port, "Command failed (%s), retrying once", + result_error ? result_error->message : "UNKNOWN"); + + if (response) + xmm7360_rpc_response_free (response); + g_clear_error (&result_error); + + command_retry = TRUE; + goto skip_result; + } + switch (processor_result) { case MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_CONTINUE: g_assert (!result_error); @@ -195,8 +211,14 @@ rpc_sequence_parse_response (MMPortSerialXmmrpcXmm7360 *port, } } - if (processor_result == MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_CONTINUE) { - ctx->current++; +skip_result: + if (processor_result == MM_BROADBAND_MODEM_XMM7360_RPC_RESPONSE_PROCESSOR_RESULT_CONTINUE || + command_retry) { + if (!command_retry) { + ctx->current++; + } + ctx->current_retry = command_retry; + if (ctx->current->callid) { g_assert (!ctx->next_command_wait_id); ctx->next_command_wait_id = g_timeout_add_seconds (ctx->current->wait_seconds, @@ -204,6 +226,7 @@ rpc_sequence_parse_response (MMPortSerialXmmrpcXmm7360 *port, task); return; } + g_assert (!command_retry); /* On last command, end. */ } diff --git a/src/plugins/intel/mm-broadband-modem-xmm7360-rpc.h b/src/plugins/intel/mm-broadband-modem-xmm7360-rpc.h index 18d107bc..feb717f5 100644 --- a/src/plugins/intel/mm-broadband-modem-xmm7360-rpc.h +++ b/src/plugins/intel/mm-broadband-modem-xmm7360-rpc.h @@ -61,6 +61,7 @@ typedef struct { guint timeout; /* Flag to allow cached replies */ gboolean allow_cached; + gboolean allow_retry_once; /* The response processor */ MMBroadbandModemXmm7360RpcResponseProcessor response_processor; /* Time to wait before sending this command (in seconds) */ diff --git a/src/plugins/intel/mm-broadband-modem-xmm7360.c b/src/plugins/intel/mm-broadband-modem-xmm7360.c index a4357079..3f9260c9 100644 --- a/src/plugins/intel/mm-broadband-modem-xmm7360.c +++ b/src/plugins/intel/mm-broadband-modem-xmm7360.c @@ -31,29 +31,31 @@ #include "mm-log-object.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp.h" -#include "mm-iface-modem-location.h" #include "mm-broadband-modem-xmm.h" #include "mm-broadband-modem-xmm7360.h" #include "mm-broadband-modem-xmm7360-rpc.h" +#include "mm-port-scheduler-rr.h" #include "mm-port-serial-xmmrpc-xmm7360.h" #include "mm-bearer-xmm7360.h" +#include "mm-shared-xmm.h" #include "mm-sim-xmm7360.h" static void iface_modem_init (MMIfaceModemInterface *iface); static void iface_modem_3gpp_init (MMIfaceModem3gppInterface *iface); -static void iface_modem_location_init (MMIfaceModemLocationInterface *iface); +static void iface_shared_xmm_init (MMSharedXmmInterface *iface); static MMIfaceModemInterface *iface_modem_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemXmm7360, mm_broadband_modem_xmm7360, MM_TYPE_BROADBAND_MODEM_XMM, 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_LOCATION, iface_modem_location_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_XMM, iface_shared_xmm_init) ) struct _MMBroadbandModemXmm7360Private { MMUnlockRetries *unlock_retries; + GRegex *nmea_regex_full, *nmea_regex_trace; }; /*****************************************************************************/ @@ -1088,14 +1090,14 @@ static const Xmm7360RpcMsgArg set_radio_signal_reporting_args[] = { }; static const MMBroadbandModemXmm7360RpcCommand init_sequence[] = { - { XMM7360_RPC_CALL_UTA_MS_SMS_INIT, FALSE, NULL, 3, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, - { XMM7360_RPC_CALL_UTA_MS_CBS_INIT, FALSE, NULL, 3, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, - { XMM7360_RPC_CALL_UTA_MS_NET_OPEN, FALSE, NULL, 3, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, - { XMM7360_RPC_CALL_UTA_MS_NET_SET_RADIO_SIGNAL_REPORTING, FALSE, set_radio_signal_reporting_args, 3, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success}, - { XMM7360_RPC_CALL_UTA_MS_CALL_CS_INIT, FALSE, NULL, 3, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, - { XMM7360_RPC_CALL_UTA_MS_CALL_PS_INITIALIZE, FALSE, NULL, 3, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, - { XMM7360_RPC_CALL_UTA_MS_SS_INIT, FALSE, NULL, 3, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, - { XMM7360_RPC_CALL_UTA_MS_SIM_OPEN_REQ, FALSE, NULL, 3, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_final }, + { XMM7360_RPC_CALL_UTA_MS_SMS_INIT, FALSE, NULL, 3, FALSE, TRUE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, + { XMM7360_RPC_CALL_UTA_MS_CBS_INIT, FALSE, NULL, 3, FALSE, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, + { XMM7360_RPC_CALL_UTA_MS_NET_OPEN, FALSE, NULL, 3, FALSE, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, + { XMM7360_RPC_CALL_UTA_MS_NET_SET_RADIO_SIGNAL_REPORTING, FALSE, set_radio_signal_reporting_args, 3, FALSE, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success}, + { XMM7360_RPC_CALL_UTA_MS_CALL_CS_INIT, FALSE, NULL, 3, FALSE, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, + { XMM7360_RPC_CALL_UTA_MS_CALL_PS_INITIALIZE, FALSE, NULL, 3, FALSE, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, + { XMM7360_RPC_CALL_UTA_MS_SS_INIT, FALSE, NULL, 3, FALSE, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success }, + { XMM7360_RPC_CALL_UTA_MS_SIM_OPEN_REQ, FALSE, NULL, 3, FALSE, FALSE, mm_broadband_modem_xmm7360_rpc_response_processor_final }, { 0 } }; @@ -1108,6 +1110,8 @@ initialization_started (MMBroadbandModem *modem, MMBroadbandModemXmm7360 *self = MM_BROADBAND_MODEM_XMM7360 (modem); InitializationStartedContext *ctx; GTask *task; + MMPortSerialAt *at_port; + g_autoptr(MMPortScheduler) sched = NULL; task = g_task_new (self, NULL, @@ -1120,6 +1124,14 @@ initialization_started (MMBroadbandModem *modem, ctx->timeout_id = 0; g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_started_context_free); + at_port = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); + if (!at_port) { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, + "no primary AT port"); + g_object_unref (task); + return; + } + /* Open XMMRPC port for initialization */ if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error)) { g_prefix_error (&error, "Couldn't open XMMRPC port during initialization: "); @@ -1134,6 +1146,13 @@ initialization_started (MMBroadbandModem *modem, task, NULL); + g_object_get (G_OBJECT (ctx->port), MM_PORT_SERIAL_SCHEDULER, &sched, NULL); + g_object_set (sched, + MM_PORT_SCHEDULER_RR_INTER_PORT_DELAY, + 300, + NULL); + g_object_set (G_OBJECT (at_port), MM_PORT_SERIAL_SCHEDULER, sched, NULL); + mm_obj_dbg (self, "running init sequence..."); mm_broadband_modem_xmm7360_rpc_sequence_full (MM_BROADBAND_MODEM_XMM7360 (self), ctx->port, @@ -1143,6 +1162,56 @@ initialization_started (MMBroadbandModem *modem, task); } +/*****************************************************************************/ + +static void +nmea_received (MMPortSerialAt *port, + GMatchInfo *info_full, + MMBroadbandModemXmm7360 *self) +{ + g_autofree gchar *trace_full = NULL; + g_autoptr(GMatchInfo) info = NULL; + + trace_full = g_match_info_fetch (info_full, 1); + if (!trace_full) { + return; + } + + g_regex_match (self->priv->nmea_regex_trace, trace_full, 0, &info); + while (g_match_info_matches (info)) { + g_autofree gchar *trace = NULL; + + trace = g_match_info_fetch (info, 1); + if (!trace) { + mm_obj_err (self, "fetching NMEA trace failed"); + } else { + mm_iface_modem_location_gps_update (MM_IFACE_MODEM_LOCATION (self), trace); + } + + g_match_info_next (info, NULL); + } +} + +static void +nmea_parser_register (MMSharedXmm *self_shared_xmm, MMPortSerialAt *gps_port, + gboolean is_register) +{ + MMBroadbandModemXmm7360 *self = MM_BROADBAND_MODEM_XMM7360 (self_shared_xmm); + + if (is_register) { + mm_port_serial_at_add_unsolicited_msg_handler (gps_port, + self->priv->nmea_regex_full, + (MMPortSerialAtUnsolicitedMsgFn)nmea_received, + self, + NULL); + } else { + mm_port_serial_at_add_unsolicited_msg_handler (gps_port, + self->priv->nmea_regex_full, + NULL, NULL, NULL); + } +} + + /*****************************************************************************/ MMBroadbandModemXmm7360 * @@ -1172,6 +1241,13 @@ mm_broadband_modem_xmm7360_init (MMBroadbandModemXmm7360 *self) self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_XMM7360, MMBroadbandModemXmm7360Private); + + self->priv->nmea_regex_full = g_regex_new ("(?:\\r\\n){2}((?:\\$G.*?\\r\\n)+)(?:\\r\\n){2}OK(?:\\r\\n)", + G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, + NULL); + self->priv->nmea_regex_trace = g_regex_new ("(\\$G.*?)\\r\\n", + G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, + NULL); } static void @@ -1179,6 +1255,8 @@ dispose (GObject *object) { MMBroadbandModemXmm7360 *self = MM_BROADBAND_MODEM_XMM7360 (object); + g_clear_pointer (&self->priv->nmea_regex_trace, g_regex_unref); + g_clear_pointer (&self->priv->nmea_regex_full, g_regex_unref); g_clear_object (&self->priv->unlock_retries); G_OBJECT_CLASS (mm_broadband_modem_xmm7360_parent_class)->dispose (object); @@ -1205,13 +1283,9 @@ iface_modem_3gpp_init (MMIfaceModem3gppInterface *iface) } static void -iface_modem_location_init (MMIfaceModemLocationInterface *iface) +iface_shared_xmm_init (MMSharedXmmInterface *iface) { - /* asking for location capabilities can destabilize the device */ - iface->load_capabilities = NULL; - iface->load_capabilities_finish = NULL; - iface->enable_location_gathering = NULL; - iface->enable_location_gathering_finish = NULL; + iface->nmea_parser_register = nmea_parser_register; } static void diff --git a/src/plugins/intel/mm-port-serial-xmmrpc-xmm7360.c b/src/plugins/intel/mm-port-serial-xmmrpc-xmm7360.c index 6a89ab00..ad5aed4f 100644 --- a/src/plugins/intel/mm-port-serial-xmmrpc-xmm7360.c +++ b/src/plugins/intel/mm-port-serial-xmmrpc-xmm7360.c @@ -535,6 +535,12 @@ serial_command_ready (MMPortSerial *port, g_byte_array_remove_range (response_buffer, 0, response_buffer->len); g_byte_array_unref (response_buffer); + if (response && + response->type == XMM7360_RPC_RESPONSE_TYPE_RESPONSE) { + xmm7360_byte_array_log (port, "", response->body); + xmm7360_rpc_msg_args_log (port, "", response->content); + } + g_task_return_pointer (task, response, (GDestroyNotify) xmm7360_rpc_response_free); g_object_unref (task); } @@ -569,10 +575,9 @@ mm_port_serial_xmmrpc_xmm7360_command (MMPortSerialXmmrpcXmm7360 *self, task = g_task_new (self, NULL, callback, user_data); + /* Body logged in debug_log() */ mm_obj_dbg (self, "--> %s%s", is_async ? "(async) " : "", xmm_7360_rpc_call_id_get_string (callid)); - xmm7360_byte_array_log (self, "", buf); - mm_port_serial_command (MM_PORT_SERIAL (self), buf, timeout_seconds, @@ -656,8 +661,7 @@ parse_unsolicited (MMPortSerial *port, GByteArray *response_buffer) if (response->type == XMM7360_RPC_RESPONSE_TYPE_RESPONSE) { mm_obj_dbg (port, "<-- (response)"); - xmm7360_byte_array_log (port, "", response->body); - xmm7360_rpc_msg_args_log (port, "", response->content); + /* Body logged in serial_command_ready() */ return; } @@ -692,6 +696,37 @@ parse_unsolicited (MMPortSerial *port, GByteArray *response_buffer) g_byte_array_remove_range (response_buffer, 0, response_buffer->len); } +static void +debug_log (MMPortSerial *self, + const gchar *prefix, + const gchar *buf, + gsize len) +{ + static GString *debug = NULL; + const char *s; + + if (g_strcmp0 (prefix, "<--") == 0) { + /* Incoming data is already logged in parse functions */ + return; + } + + if (!debug) + debug = g_string_sized_new (256); + + g_string_append (debug, prefix); + g_string_append (debug, " raw ["); + + s = buf; + while (len--) { + g_string_append_printf (debug, "%02x", (guint8)*s); + s++; + } + + g_string_append_c (debug, ']'); + mm_obj_dbg (self, "%s", debug->str); + g_string_truncate (debug, 0); +} + /*****************************************************************************/ MMPortSerialXmmrpcXmm7360 * @@ -747,4 +782,5 @@ mm_port_serial_xmmrpc_xmm7360_class_init (MMPortSerialXmmrpcXmm7360Class *klass) serial_class->parse_response = parse_response; serial_class->parse_unsolicited = parse_unsolicited; + serial_class->debug_log = debug_log; } diff --git a/src/plugins/intel/mm-sim-xmm7360.c b/src/plugins/intel/mm-sim-xmm7360.c index 912e66e8..a941e847 100644 --- a/src/plugins/intel/mm-sim-xmm7360.c +++ b/src/plugins/intel/mm-sim-xmm7360.c @@ -17,6 +17,7 @@ #include "mm-broadband-modem-xmm7360-rpc.h" #include "mm-log-object.h" #include "mm-sim-xmm7360.h" +#include "mm-bind.h" G_DEFINE_TYPE (MMSimXmm7360, mm_sim_xmm7360, MM_TYPE_BASE_SIM) @@ -392,6 +393,7 @@ mm_sim_xmm7360_new (MMBaseModem *modem, callback, user_data, MM_BASE_SIM_MODEM, modem, + MM_BIND_TO, modem, "active", TRUE, /* by default always active */ NULL); } @@ -413,6 +415,7 @@ mm_sim_xmm7360_new_initialized (MMBaseModem *modem, sim = MM_BASE_SIM (g_object_new (MM_TYPE_SIM_XMM7360, MM_BASE_SIM_MODEM, modem, + MM_BIND_TO, modem, MM_BASE_SIM_SLOT_NUMBER, slot_number, "active", active, "sim-type", sim_type, diff --git a/src/plugins/xmm/mm-broadband-modem-xmm.c b/src/plugins/xmm/mm-broadband-modem-xmm.c index 3364c639..7cc26dd8 100644 --- a/src/plugins/xmm/mm-broadband-modem-xmm.c +++ b/src/plugins/xmm/mm-broadband-modem-xmm.c @@ -149,4 +149,8 @@ mm_broadband_modem_xmm_class_init (MMBroadbandModemXmmClass *klass) MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass); broadband_modem_class->setup_ports = mm_shared_xmm_setup_ports; + broadband_modem_class->initialization_started = + mm_shared_xmm_initialization_started; + broadband_modem_class->initialization_started_finish = + mm_shared_xmm_initialization_started_finish; } diff --git a/src/plugins/xmm/mm-shared-xmm.c b/src/plugins/xmm/mm-shared-xmm.c index d63cf1b6..54f36a68 100644 --- a/src/plugins/xmm/mm-shared-xmm.c +++ b/src/plugins/xmm/mm-shared-xmm.c @@ -44,6 +44,7 @@ typedef enum { GPS_ENGINE_STATE_STANDALONE, GPS_ENGINE_STATE_AGPS_MSA, GPS_ENGINE_STATE_AGPS_MSB, + GPS_ENGINE_STATE_UNKNOWN, } GpsEngineState; typedef struct { @@ -64,6 +65,7 @@ typedef struct { MMModemLocationSource enabled_sources; GpsEngineState gps_engine_state; MMPortSerialAt *gps_port; + gboolean gps_port_open; GRegex *xlsrstop_regex; GRegex *nmea_regex; @@ -75,6 +77,13 @@ static void private_free (Private *priv) { g_assert (!priv->pending_gps_engine_stop_task); + + if (priv->gps_port_open) { + g_assert (priv->gps_port); + mm_port_serial_close (MM_PORT_SERIAL (priv->gps_port)); + priv->gps_port_open = FALSE; + } + g_clear_object (&priv->gps_port); if (priv->supported_modes) g_array_unref (priv->supported_modes); @@ -97,7 +106,7 @@ get_private (MMSharedXmm *self) priv = g_object_get_qdata (G_OBJECT (self), private_quark); if (!priv) { priv = g_slice_new0 (Private); - priv->gps_engine_state = GPS_ENGINE_STATE_OFF; + priv->gps_engine_state = GPS_ENGINE_STATE_UNKNOWN; /* Setup regex for URCs */ priv->xbipi_regex = g_regex_new ("\\r\\n\\+XBIPI:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); @@ -995,6 +1004,25 @@ nmea_received (MMPortSerialAt *port, g_free (trace); } +static void +nmea_parser_register (MMSharedXmm *self, MMPortSerialAt *gps_port, + gboolean is_register) +{ + Private *priv = get_private (self); + + if (is_register) { + mm_port_serial_at_add_unsolicited_msg_handler (gps_port, + priv->nmea_regex, + (MMPortSerialAtUnsolicitedMsgFn)nmea_received, + self, + NULL); + } else { + mm_port_serial_at_add_unsolicited_msg_handler (gps_port, + priv->nmea_regex, + NULL, NULL, NULL); + } +} + /*****************************************************************************/ /* GPS engine state selection */ @@ -1029,6 +1057,7 @@ xlcslsr_ready (MMBaseModem *self, const gchar *response; GError *error = NULL; Private *priv; + g_autoptr(GError) reopen_error = NULL; priv = get_private (MM_SHARED_XMM (self)); ctx = g_task_get_task_data (task); @@ -1044,13 +1073,24 @@ xlcslsr_ready (MMBaseModem *self, mm_obj_dbg (self, "GPS engine started"); g_assert (priv->gps_port); - mm_port_serial_at_add_unsolicited_msg_handler (priv->gps_port, - priv->nmea_regex, - (MMPortSerialAtUnsolicitedMsgFn)nmea_received, - self, - NULL); + MM_SHARED_XMM_GET_IFACE (self)->nmea_parser_register (MM_SHARED_XMM (self), + priv->gps_port, + TRUE); + priv->gps_engine_state = ctx->state; + /* + * Keep GPS port open if would not be kept open otherwise + * (like with a locked/non-enabled modem). + */ + g_assert (!priv->gps_port_open); + if (!mm_port_serial_open (MM_PORT_SERIAL (priv->gps_port), &reopen_error)) { + mm_obj_warn (self, "unexpected GPS port reopen error: %s", + reopen_error->message); + } else { + priv->gps_port_open = TRUE; + } + g_task_return_boolean (task, TRUE); g_object_unref (task); } @@ -1092,6 +1132,7 @@ gps_engine_start (GTask *task) pos_mode = 2; break; case GPS_ENGINE_STATE_OFF: + case GPS_ENGINE_STATE_UNKNOWN: default: g_assert_not_reached (); break; @@ -1167,10 +1208,14 @@ gps_engine_stopped (GTask *task) ctx = g_task_get_task_data (task); g_assert (priv->gps_port); - mm_port_serial_at_add_unsolicited_msg_handler ( - priv->gps_port, - priv->nmea_regex, - NULL, NULL, NULL); + MM_SHARED_XMM_GET_IFACE (self)->nmea_parser_register (self, priv->gps_port, + FALSE); + + if (priv->gps_port_open) { + mm_port_serial_close (MM_PORT_SERIAL (priv->gps_port)); + priv->gps_port_open = FALSE; + } + g_clear_object (&priv->gps_port); priv->gps_engine_state = GPS_ENGINE_STATE_OFF; @@ -1314,6 +1359,8 @@ gps_engine_state_select (MMSharedXmm *self, GTask *task; Private *priv; + g_assert (state != GPS_ENGINE_STATE_UNKNOWN); + priv = get_private (self); task = g_task_new (self, NULL, callback, user_data); @@ -1663,7 +1710,6 @@ mm_shared_xmm_setup_ports (MMBroadbandModem *self) { Private *priv; MMPortSerialAt *ports[2]; - g_autoptr(MMPortSerialAt) gps_port = NULL; guint i; mm_obj_dbg (self, "setting up ports in XMM modem..."); @@ -1686,23 +1732,136 @@ mm_shared_xmm_setup_ports (MMBroadbandModem *self) priv->xbipi_regex, NULL, NULL, NULL); } +} - /* Setup the GPS port */ - gps_port = shared_xmm_get_gps_control_port (MM_SHARED_XMM (self), NULL); - if (gps_port) { - /* After running AT+XLSRSTOP we may get an unsolicited response - * reporting its status, we just ignore it. */ - mm_port_serial_at_add_unsolicited_msg_handler ( - gps_port, - priv->xlsrstop_regex, - NULL, NULL, NULL); +gpointer +mm_shared_xmm_initialization_started_finish (MMBroadbandModem *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} - /* make sure GPS is stopped in case it was left enabled */ - mm_base_modem_at_command_full (MM_BASE_MODEM (self), - MM_IFACE_PORT_AT (gps_port), - "+XLSRSTOP", - 3, FALSE, FALSE, NULL, NULL, NULL); +static void +initialization_started_done (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + MMBroadbandModem *self = MM_BROADBAND_MODEM (source_object); + g_autoptr(GTask) task = user_data; + Private *priv = get_private (MM_SHARED_XMM (self)); + g_autoptr(GError) error = NULL; + gpointer ret; + + g_assert (priv->broadband_modem_class_parent); + g_assert (priv->broadband_modem_class_parent->initialization_started_finish); + + ret = priv->broadband_modem_class_parent->initialization_started_finish (self, + res, + &error); + if (!ret) { + g_task_return_error (task, g_steal_pointer (&error)); + return; } + + g_task_return_pointer (task, ret, NULL); +} + +static void +gps_init_stop_done (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + MMSharedXmm *self_xmm = MM_SHARED_XMM (source_object); + Private *priv = get_private (self_xmm); + MMBroadbandModem *self = MM_BROADBAND_MODEM (source_object); + g_autoptr(GTask) task = user_data; + g_autoptr(GError) error = NULL; + + if (!gps_engine_state_select_finish (self_xmm, res, &error)) { + mm_obj_dbg (self, "GPS stop at init failed: %s", + error ? error->message : "(unknown"); + + /* assume GPS simply wasn't on in the modem in the first place */ + if (priv->gps_port) { + MM_SHARED_XMM_GET_IFACE (self)->nmea_parser_register (self_xmm, + priv->gps_port, + FALSE); + + if (priv->gps_port_open) { + mm_port_serial_close (MM_PORT_SERIAL (priv->gps_port)); + priv->gps_port_open = FALSE; + } + + g_clear_object (&priv->gps_port); + } + + priv->gps_engine_state = GPS_ENGINE_STATE_OFF; + } + + g_assert (priv->broadband_modem_class_parent); + g_assert (priv->broadband_modem_class_parent->initialization_started); + + priv->broadband_modem_class_parent->initialization_started (self, + initialization_started_done, + g_steal_pointer (&task)); +} + +void +mm_shared_xmm_initialization_started (MMBroadbandModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMSharedXmm *self_xmm = MM_SHARED_XMM (self); + Private *priv = get_private (self_xmm); + g_autoptr(GTask) task = NULL; + g_autoptr(MMPortSerialAt) gps_port = NULL; + + task = g_task_new (self, NULL, callback, user_data); + + /* Setup the GPS port and stop GPS if it is not supposed to be running */ + gps_port = shared_xmm_get_gps_control_port (self_xmm, NULL); + if (gps_port && priv->gps_engine_state == GPS_ENGINE_STATE_UNKNOWN) { + g_autoptr(GError) gps_port_open_error = NULL; + g_autoptr(GError) gps_port_flush_error = NULL; + + g_assert (!priv->gps_port); + priv->gps_port = g_steal_pointer (&gps_port); + + MM_SHARED_XMM_GET_IFACE (self)->nmea_parser_register (self_xmm, + priv->gps_port, + TRUE); + + g_assert (!priv->gps_port_open); + if (!mm_port_serial_open (MM_PORT_SERIAL (priv->gps_port), + &gps_port_open_error)) { + mm_obj_warn (self, "unexpected init GPS port open error: %s", + gps_port_open_error->message); + } else { + priv->gps_port_open = TRUE; + } + + mm_obj_dbg (self, + "flushing pending data in GPS port"); + + if (!mm_port_serial_flush (MM_PORT_SERIAL (priv->gps_port), + MM_PORT_SERIAL_FLUSH_RX, + &gps_port_flush_error)) { + mm_obj_warn (self, "unexpected init GPS port flush error: %s", + gps_port_flush_error->message); + } + + gps_engine_state_select (self_xmm, GPS_ENGINE_STATE_OFF, + gps_init_stop_done, g_steal_pointer (&task)); + return; + } + + g_assert (priv->broadband_modem_class_parent); + g_assert (priv->broadband_modem_class_parent->initialization_started); + + priv->broadband_modem_class_parent->initialization_started (self, + initialization_started_done, + g_steal_pointer (&task)); } /*****************************************************************************/ @@ -1710,4 +1869,5 @@ mm_shared_xmm_setup_ports (MMBroadbandModem *self) static void mm_shared_xmm_default_init (MMSharedXmmInterface *iface) { + iface->nmea_parser_register = nmea_parser_register; } diff --git a/src/plugins/xmm/mm-shared-xmm.h b/src/plugins/xmm/mm-shared-xmm.h index e78467e7..1896b8f5 100644 --- a/src/plugins/xmm/mm-shared-xmm.h +++ b/src/plugins/xmm/mm-shared-xmm.h @@ -38,11 +38,20 @@ struct _MMSharedXmmInterface { /* Peek location interface of the parent class of the object */ MMIfaceModemLocationInterface * (* peek_parent_location_interface) (MMSharedXmm *self); + + void (* nmea_parser_register) (MMSharedXmm *self, MMPortSerialAt *gps_port, + gboolean is_register); }; /* Shared XMM device setup */ void mm_shared_xmm_setup_ports (MMBroadbandModem *self); +void mm_shared_xmm_initialization_started (MMBroadbandModem *self, + GAsyncReadyCallback callback, + gpointer user_data); +gpointer mm_shared_xmm_initialization_started_finish (MMBroadbandModem *self, + GAsyncResult *res, + GError **error); /* Shared XMM device management support */