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>
This commit is contained in:
Dan Williams
2024-09-17 11:26:06 -05:00
parent 7937a89a37
commit 0b5f2adb4b
2 changed files with 38 additions and 6 deletions
+22 -6
View File
@@ -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;
}
+16
View File
@@ -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. */