intel: XMM7360: Better NMEA packets parsing

XMM7360 modem NMEA trace packets are wrapped to look like AT command
responses (with "<CR><LF><CR><LF>OK<CR><LF>" 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 <mail@maciej.szmigiero.name>
This commit is contained in:
Maciej S. Szmigiero
2025-06-14 16:02:23 -05:00
committed by Dan Williams
parent 6c4b90de79
commit 0787d04a39
3 changed files with 98 additions and 9 deletions
@@ -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)
{
+26 -9
View File
@@ -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;
}
+3
View File
@@ -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 */