diff options
author | Dan Williams <dan@ioncontrol.co> | 2024-09-10 08:09:56 -0500 |
---|---|---|
committer | Dan Williams <dan@ioncontrol.co> | 2024-09-17 11:26:06 -0500 |
commit | 0b5f2adb4b078b305604b1f9b785289e279c5164 (patch) | |
tree | 81b48786ced84861b902f70f064230c80e823a09 /src | |
parent | 7937a89a37e941e9a77cddcce8226c316fe70821 (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.c | 28 | ||||
-rw-r--r-- | src/mm-port-serial-qcdm.c | 16 |
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. */ |