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 <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 adb6dc9b44
commit 4c319181e2
3 changed files with 141 additions and 15 deletions
+4
View File
@@ -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;
}
+131 -15
View File
@@ -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));
}
/*****************************************************************************/
+6
View File
@@ -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 */