From f9146378b81335a075ce7ccc6c1a300867220dca Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sat, 22 Feb 2025 10:53:25 +0100 Subject: [PATCH 01/10] .gitignore: ignore clangd cache directory Signed-off-by: Maciej S. Szmigiero --- .gitignore | 1 + 1 file changed, 1 insertion(+) 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 From df9f2d0de53027379a10fe44ed60b2130b232442 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sat, 10 May 2025 18:26:26 +0200 Subject: [PATCH 02/10] intel: XMM7360: Make compatible with recent binding changes Commit ec5104c9a87a ("Move log parent and connection property binding to helper") made it obligatory for MM_TYPE_BASE_BEARER and MM_TYPE_BASE_SIM derived classes to specify MM_BIND_TO property, but recently merged XMM7360 support wasn't converted before being merged. This caused XMM7360 to fail to set-up SIM D-Bus object and mmcli to throw assertions when trying to access that non-existing SIM object. Add the missing XMM7360 MM_BIND_TO properties. Signed-off-by: Maciej S. Szmigiero --- src/plugins/intel/mm-bearer-xmm7360.c | 2 ++ src/plugins/intel/mm-sim-xmm7360.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/plugins/intel/mm-bearer-xmm7360.c b/src/plugins/intel/mm-bearer-xmm7360.c index 10dd2c26..ec108274 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) @@ -731,6 +732,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-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, From 5059b812fc23531136825a5c879758539a4f5962 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sat, 11 Jan 2025 14:40:34 +0100 Subject: [PATCH 03/10] intel: XMM7360: RPC and AT port interlocking We have to avoid sending RPC and AT commands at the same time (even thought they are sent via different serial ports) since apparently this modems does not like it. It also looks like this modem does not like to receive a RPC command soon after an AT one, since such RPC commands sometimes fail to generate any response. Prevent this problem by delaying a RPC command if there was an AT command completed recently. The sufficient time gap was determined experimentally, but 300 ms seems to be enough. RPC commands are relatively rare so this should not be a performance issue. Otherwise we often never get any response to RPC calls if AT command happens to be sent at the same time. Signed-off-by: Maciej S. Szmigiero --- src/plugins/intel/mm-broadband-modem-xmm7360.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/plugins/intel/mm-broadband-modem-xmm7360.c b/src/plugins/intel/mm-broadband-modem-xmm7360.c index a4357079..f50e9408 100644 --- a/src/plugins/intel/mm-broadband-modem-xmm7360.c +++ b/src/plugins/intel/mm-broadband-modem-xmm7360.c @@ -36,6 +36,7 @@ #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-sim-xmm7360.h" @@ -1108,6 +1109,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 +1123,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 +1145,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, From a83a9bf437b80d1afea631aceca7a0488b6c9c9a Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sat, 11 Jan 2025 14:42:36 +0100 Subject: [PATCH 04/10] intel: XMM7360: Re-enable location interface Seems to work just fine once the port interlocking is being done and some other fixes are made in the next commit. Signed-off-by: Maciej S. Szmigiero --- src/plugins/intel/mm-broadband-modem-xmm7360.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/plugins/intel/mm-broadband-modem-xmm7360.c b/src/plugins/intel/mm-broadband-modem-xmm7360.c index f50e9408..f949c253 100644 --- a/src/plugins/intel/mm-broadband-modem-xmm7360.c +++ b/src/plugins/intel/mm-broadband-modem-xmm7360.c @@ -31,7 +31,6 @@ #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" @@ -43,14 +42,12 @@ static void iface_modem_init (MMIfaceModemInterface *iface); static void iface_modem_3gpp_init (MMIfaceModem3gppInterface *iface); -static void iface_modem_location_init (MMIfaceModemLocationInterface *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) ) struct _MMBroadbandModemXmm7360Private { @@ -1222,16 +1219,6 @@ iface_modem_3gpp_init (MMIfaceModem3gppInterface *iface) iface->set_initial_eps_bearer_settings_finish = modem_3gpp_set_initial_eps_bearer_settings_finish; } -static void -iface_modem_location_init (MMIfaceModemLocationInterface *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; -} - static void mm_broadband_modem_xmm7360_class_init (MMBroadbandModemXmm7360Class *klass) { From 912e8578319d125f6f997bc7fe240dda6f5dbd12 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sat, 11 Jan 2025 14:33:21 +0100 Subject: [PATCH 05/10] shared-xmm: keep GPS port open if GPS is enabled We need to keep GPS port open if would not be kept open otherwise (like it is the case with a locked/non-enabled modem). Otherwise: * While GPS can be enabled in that modem state the traces don't reach MM API consumers since nothing is servicing the serial port. * Once modem actually gets enabled the queued GPS output in the modem serial port floods the main port and often confuses the init sequence. We also have to make sure that we don't stop GPS during the init sequence if it is supposed to be running, otherwise there will be the same issue as commit d3a2aebf16ac ("mbm: don't stop GPS interface on reinitialization if it should be running") fixed for mbm. Signed-off-by: Maciej S. Szmigiero --- src/plugins/xmm/mm-shared-xmm.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/plugins/xmm/mm-shared-xmm.c b/src/plugins/xmm/mm-shared-xmm.c index d63cf1b6..0142f404 100644 --- a/src/plugins/xmm/mm-shared-xmm.c +++ b/src/plugins/xmm/mm-shared-xmm.c @@ -64,6 +64,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 +76,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); @@ -1029,6 +1037,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); @@ -1051,6 +1060,18 @@ xlcslsr_ready (MMBaseModem *self, NULL); 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); } @@ -1171,6 +1192,12 @@ gps_engine_stopped (GTask *task) priv->gps_port, priv->nmea_regex, NULL, NULL, NULL); + + 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; @@ -1687,9 +1714,9 @@ mm_shared_xmm_setup_ports (MMBroadbandModem *self) NULL, NULL, NULL); } - /* Setup the GPS port */ + /* Setup the GPS port and stop GPS if it is not supposed to be running */ gps_port = shared_xmm_get_gps_control_port (MM_SHARED_XMM (self), NULL); - if (gps_port) { + if (gps_port && priv->gps_engine_state == GPS_ENGINE_STATE_OFF) { /* 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 ( From 6c4b90de791a64d6d6d46762673215e07fbdb3c4 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sat, 25 Jan 2025 17:48:05 +0100 Subject: [PATCH 06/10] intel: XMM7360: Print raw serial port data when it is actually sent/dispatched With serial port interlocking just queuing serial port data does not mean it will be immediately sent - the other serial port could be still busy. For debugging serial port issues it is often necessary to see the exact moment the data was sent or received. Implement debug_log method for MMPortSerialXmmrpcXmm7360 and log raw serial port data there (just as MMPortSerialAt does) instead of doing this in the command queuing function. The debug_log method is called by MMPortSerial base when the data is about to be actually sent to the serial port. Additionally, log command response body where it is actually returned to higher layers rather than in the unsolicited response handler. Signed-off-by: Maciej S. Szmigiero --- .../intel/mm-port-serial-xmmrpc-xmm7360.c | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) 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; } From 0787d04a3993a4e0af5dc89f6cb4a54ea5a88ba1 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sun, 26 Jan 2025 21:32:22 +0100 Subject: [PATCH 07/10] intel: XMM7360: Better NMEA packets parsing XMM7360 modem NMEA trace packets are wrapped to look like AT command responses (with "OK" at the end). This wrapping causes problems if a genuine AT command was just sent and ModemManager is expecting a similarly formatted response to it. In such case the NMEA trace packet is taken as the response to the AT command, which usually causes the command to fail due to response parsing failure. Fix this by implementing a custom NMEA trace packets parser for these modems. Signed-off-by: Maciej S. Szmigiero --- .../intel/mm-broadband-modem-xmm7360.c | 69 +++++++++++++++++++ src/plugins/xmm/mm-shared-xmm.c | 35 +++++++--- src/plugins/xmm/mm-shared-xmm.h | 3 + 3 files changed, 98 insertions(+), 9 deletions(-) diff --git a/src/plugins/intel/mm-broadband-modem-xmm7360.c b/src/plugins/intel/mm-broadband-modem-xmm7360.c index f949c253..a57d47d3 100644 --- a/src/plugins/intel/mm-broadband-modem-xmm7360.c +++ b/src/plugins/intel/mm-broadband-modem-xmm7360.c @@ -38,20 +38,24 @@ #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_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_SHARED_XMM, iface_shared_xmm_init) ) struct _MMBroadbandModemXmm7360Private { MMUnlockRetries *unlock_retries; + GRegex *nmea_regex_full, *nmea_regex_trace; }; /*****************************************************************************/ @@ -1158,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 * @@ -1187,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 @@ -1194,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); @@ -1219,6 +1282,12 @@ iface_modem_3gpp_init (MMIfaceModem3gppInterface *iface) iface->set_initial_eps_bearer_settings_finish = modem_3gpp_set_initial_eps_bearer_settings_finish; } +static void +iface_shared_xmm_init (MMSharedXmmInterface *iface) +{ + iface->nmea_parser_register = nmea_parser_register; +} + static void mm_broadband_modem_xmm7360_class_init (MMBroadbandModemXmm7360Class *klass) { diff --git a/src/plugins/xmm/mm-shared-xmm.c b/src/plugins/xmm/mm-shared-xmm.c index 0142f404..208a994b 100644 --- a/src/plugins/xmm/mm-shared-xmm.c +++ b/src/plugins/xmm/mm-shared-xmm.c @@ -1003,6 +1003,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 */ @@ -1053,11 +1072,10 @@ 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; /* @@ -1188,10 +1206,8 @@ 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)); @@ -1737,4 +1753,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..c9d09e37 100644 --- a/src/plugins/xmm/mm-shared-xmm.h +++ b/src/plugins/xmm/mm-shared-xmm.h @@ -38,6 +38,9 @@ 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 */ From adb6dc9b44224136ccdd087f29f4796e0bfa54ed Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sat, 31 May 2025 13:08:01 +0200 Subject: [PATCH 08/10] port-serial: add flush operation Add a serial port flush operation which can be used to discard either serial port data received but not read, or serial port written but not transmitted, or both types of data. Signed-off-by: Maciej S. Szmigiero --- src/mm-port-serial.c | 43 +++++++++++++++++++++++++++++++++++++++++++ src/mm-port-serial.h | 10 ++++++++++ 2 files changed, 53 insertions(+) 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, From 4c319181e221a0be40257498f4093dcef62a97f6 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sun, 16 Feb 2025 18:14:41 +0100 Subject: [PATCH 09/10] shared-xmm: Properly stop GPS if it was running on modem init With the recent addition of serial port scheduler it often happens that NMEA traces are mistaken for command responses at init time if GPS was left enabled in the modem. For this reason, the proper NMEA packet parser needs to be enabled early at modem init before the code ensures that GPS is stopped (if it is not supposed to be running of course). Since this modem disables GPS asynchronously with processing of the +XLSRSTOP AT command the full GPS stop machinery has be used to reliably achieve that GPS engine stop. It's also important to flush the existing serial port data after attaching the NMEA packet parser but before issuing that stop AT command, otherwise command processor for the stop command can also be confused by the existing queued data. Signed-off-by: Maciej S. Szmigiero --- src/plugins/xmm/mm-broadband-modem-xmm.c | 4 + src/plugins/xmm/mm-shared-xmm.c | 146 ++++++++++++++++++++--- src/plugins/xmm/mm-shared-xmm.h | 6 + 3 files changed, 141 insertions(+), 15 deletions(-) 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 208a994b..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 { @@ -105,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); @@ -1131,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; @@ -1357,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); @@ -1706,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..."); @@ -1729,23 +1732,136 @@ mm_shared_xmm_setup_ports (MMBroadbandModem *self) priv->xbipi_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); +} + +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 (MM_SHARED_XMM (self), NULL); - if (gps_port && priv->gps_engine_state == GPS_ENGINE_STATE_OFF) { - /* 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); + 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; - /* 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); + 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)); } /*****************************************************************************/ diff --git a/src/plugins/xmm/mm-shared-xmm.h b/src/plugins/xmm/mm-shared-xmm.h index c9d09e37..1896b8f5 100644 --- a/src/plugins/xmm/mm-shared-xmm.h +++ b/src/plugins/xmm/mm-shared-xmm.h @@ -46,6 +46,12 @@ struct _MMSharedXmmInterface { /* 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 */ From 0d29f6dfa51af07837ed5407d170d07aa1e9120c Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sat, 10 May 2025 20:19:57 +0200 Subject: [PATCH 10/10] intel: XMM7360: Allow re-trying the first RPC init command command once It seems that the very first XMM7360 RPC init command often times out after resuming the laptop from s2idle for unknown reasons. This makes the modem unusable in such situation until the next sleep/resume cycle. Re-trying this RPC command allows the modem to initialize normally. It's possible that this is a kernel iosm driver or modem firmware issue but considering that the kernel driver seems orphaned by Intel and one can't really expect much firmware support for this modem either let's do the easiest thing and simply allow retrying (once) the very first RPC init command if it fails or times out. Signed-off-by: Maciej S. Szmigiero --- src/plugins/intel/mm-bearer-xmm7360.c | 2 ++ .../intel/mm-broadband-modem-xmm7360-rpc.c | 27 +++++++++++++++++-- .../intel/mm-broadband-modem-xmm7360-rpc.h | 1 + .../intel/mm-broadband-modem-xmm7360.c | 16 +++++------ 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/plugins/intel/mm-bearer-xmm7360.c b/src/plugins/intel/mm-bearer-xmm7360.c index ec108274..7ca3ad0e 100644 --- a/src/plugins/intel/mm-bearer-xmm7360.c +++ b/src/plugins/intel/mm-bearer-xmm7360.c @@ -674,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) */ }, @@ -687,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) */ }, 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 a57d47d3..3f9260c9 100644 --- a/src/plugins/intel/mm-broadband-modem-xmm7360.c +++ b/src/plugins/intel/mm-broadband-modem-xmm7360.c @@ -1090,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 } };