aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDan Williams <dan@ioncontrol.co>2024-09-10 08:09:56 -0500
committerDan Williams <dan@ioncontrol.co>2024-09-17 11:26:06 -0500
commit0b5f2adb4b078b305604b1f9b785289e279c5164 (patch)
tree81b48786ced84861b902f70f064230c80e823a09 /src
parent7937a89a37e941e9a77cddcce8226c316fe70821 (diff)
port-probe: restart AT probing if "NO CARRIER" is received during QCDM probing
If the system and modem are independently powered, a system restart may leave one of the modem's AT ports in PPP mode. On restart this may cause AT probing to fail. The modem may terminate PPP after receiving bogus HDLC frames from QCDM probing (since PPP also uses HDLC framing), and return "NO CARRIER". We can use this to restart AT probing now that PPP is down and auto-detect the expected additional AT ports. Feb 08 20:22:53 ModemManager[385]: <debug> [1675887773.887574] [ttyUSB2/at] <-- '\8\158\239~' Feb 08 20:22:54 ModemManager[385]: <debug> [1675887774.215054] [ttyUSB2/at] <-- '\8\214\137~' Feb 08 20:22:54 ModemManager[385]: <debug> [1675887774.851349] [ttyUSB2/at] <-- '\8\250\177~' Feb 08 20:22:55 ModemManager[385]: <debug> [1675887775.576776] [ttyUSB2/at] <-- '\8\168u~' Feb 08 20:22:55 ModemManager[385]: <debug> [1675887775.612694] [ttyUSB2/at] <-- '\8w\25~' Feb 08 20:22:55 ModemManager[385]: <debug> [1675887775.862886] [ttyUSB2/at] <-- '\8!&~' Feb 08 20:22:56 ModemManager[385]: <debug> [1675887776.614747] [ttyUSB2/at] <-- '\8%\254~' Feb 08 20:22:56 ModemManager[385]: <debug> [1675887776.686207] [ttyUSB2/probe] port is not AT-capable Feb 08 20:22:56 ModemManager[385]: <debug> [1675887776.686447] [ttyUSB2/probe] probing QCDM... Feb 08 20:22:56 ModemManager[385]: <debug> [1675887776.725483] [ttyUSB2/qcdm] --> 7e 00 78 f0 7e Feb 08 20:22:56 ModemManager[385]: <debug> [1675887776.880941] [ttyUSB2/qcdm] <-- 0d 0a 4e 4f 20 43 41 52 52 49 45 52 0d 0a Signed-off-by: Dan Williams <dan@ioncontrol.co>
Diffstat (limited to 'src')
-rw-r--r--src/mm-port-probe.c28
-rw-r--r--src/mm-port-serial-qcdm.c16
2 files changed, 38 insertions, 6 deletions
diff --git a/src/mm-port-probe.c b/src/mm-port-probe.c
index b9a7d422..dcedc3a8 100644
--- a/src/mm-port-probe.c
+++ b/src/mm-port-probe.c
@@ -114,10 +114,12 @@ struct _MMPortProbePrivate {
/*****************************************************************************/
-void
-mm_port_probe_reset (MMPortProbe *self)
+static void
+mm_port_probe_clear (MMPortProbe *self)
{
- g_assert (!self->priv->task);
+ /* Clears existing probe results so probing can restart from the beginning.
+ * Should only be used internally as it does not ensure `task` is NULL.
+ */
self->priv->flags = 0;
self->priv->is_at = FALSE;
self->priv->is_qcdm = FALSE;
@@ -129,6 +131,14 @@ mm_port_probe_reset (MMPortProbe *self)
self->priv->is_mbim = FALSE;
}
+void
+mm_port_probe_reset (MMPortProbe *self)
+{
+ /* Clears existing probe results after probing is complete */
+ g_assert (!self->priv->task);
+ mm_port_probe_clear (self);
+}
+
/*****************************************************************************/
/* Probe task completions.
* Always make sure that the stored task is NULL when the task is completed.
@@ -669,7 +679,7 @@ serial_probe_qcdm_parse_response (MMPortSerialQcdm *port,
gint err = QCDM_SUCCESS;
gboolean is_qcdm = FALSE;
gboolean retry = FALSE;
- GError *error = NULL;
+ g_autoptr(GError) error = NULL;
GByteArray *response;
PortProbeRunContext *ctx;
@@ -695,11 +705,17 @@ serial_probe_qcdm_parse_response (MMPortSerialQcdm *port,
} else if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_PARSE_FAILED)) {
/* Failed to unescape QCDM packet: don't retry */
mm_obj_dbg (self, "QCDM parsing error: %s", error->message);
- g_error_free (error);
+ } else if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_CARRIER)) {
+ /* Special-case: the port may have been in PPP mode (if system is restarted
+ * but the modem still had power) and failed AT probing. QCDM probing
+ * sends empty HDLC frames that PPP parses and then terminates the
+ * connection with "NO CARRIER". Match this and go back to AT probing.
+ */
+ mm_obj_dbg (self, "QCDM parsing got NO CARRIER; retrying AT probing");
+ mm_port_probe_clear (self);
} else {
if (!g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT))
mm_obj_dbg (self, "QCDM probe error: (%d) %s", error->code, error->message);
- g_error_free (error);
retry = TRUE;
}
diff --git a/src/mm-port-serial-qcdm.c b/src/mm-port-serial-qcdm.c
index 08f24303..080f639f 100644
--- a/src/mm-port-serial-qcdm.c
+++ b/src/mm-port-serial-qcdm.c
@@ -12,6 +12,7 @@
*
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2010 Red Hat, Inc.
+ * Copyright (C) 2024 JUCR GmbH
*/
#include <stdio.h>
@@ -70,6 +71,9 @@ find_qcdm_start (GByteArray *response, gsize *start)
return FALSE;
}
+/* /r/nNO CARRIER/r/n */
+static const gchar no_carrier[] = { 0x0d, 0x0a, 0x4e, 0x4f, 0x20, 0x43, 0x41, 0x52, 0x52, 0x49, 0x45, 0x52, 0x0d, 0x0a };
+
static MMPortSerialResponseType
parse_qcdm (GByteArray *response,
gboolean want_log,
@@ -84,6 +88,18 @@ parse_qcdm (GByteArray *response,
/* Get the offset into the buffer of where the QCDM frame starts */
if (!find_qcdm_start (response, &start)) {
+ /* As a special case detect \r\nNO CARRIER\r\n which happens when a port
+ * is in PPP mode and QCDM attemps to send QCDM requests. The modem will
+ * often terminate PPP when it receives the bogus frame.
+ */
+ if (response->len >= sizeof (no_carrier) && memcmp (response->data, no_carrier, sizeof (no_carrier)) == 0) {
+ g_set_error (error,
+ MM_CONNECTION_ERROR,
+ MM_CONNECTION_ERROR_NO_CARRIER,
+ "Received NO CARRIER response");
+ return MM_PORT_SERIAL_RESPONSE_ERROR;
+ }
+
/* Discard the unparsable data right away, we do need a QCDM
* start, and anything that comes before it is unknown data
* that we'll never use. */