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:
+22
-6
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
Reference in New Issue
Block a user