Merge request !1356 from 'sierra-call-urc-fix'

serial-parsers: don't echo-remove strange call start/end URCs

https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/merge_requests/1356
This commit is contained in:
Dan Williams
2025-05-22 08:09:41 -05:00
12 changed files with 100 additions and 31 deletions
+1
View File
@@ -468,6 +468,7 @@ base_modem_internal_grab_port (MMBaseModem *self,
if (MM_IS_PORT_SERIAL_AT (port)) {
mm_port_serial_at_set_response_parser (MM_PORT_SERIAL_AT (port),
mm_serial_parser_v1_parse,
mm_serial_parser_v1_remove_echo,
mm_serial_parser_v1_new (),
mm_serial_parser_v1_destroy);
/* Prefer plugin-provided flags to the generic ones */
+1 -2
View File
@@ -8473,8 +8473,7 @@ set_voice_in_call_unsolicited_events_handlers (MMBroadbandModem *self,
g_autoptr(GRegex) in_call_event_regex = NULL;
guint i;
in_call_event_regex = g_regex_new ("\\r\\n(NO CARRIER|BUSY|NO ANSWER|NO DIALTONE)(\\r)?\\r\\n$",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
in_call_event_regex = mm_call_end_regex_get ();
ports[0] = MM_PORT_SERIAL_AT (ports_ctx->primary);
ports[1] = MM_PORT_SERIAL_AT (ports_ctx->secondary);
+18
View File
@@ -482,6 +482,24 @@ mm_at_quote_string (const gchar *input)
/*****************************************************************************/
GRegex *
mm_call_end_regex_get (void)
{
/* Example:
* <CR><LF>NO ANSWER<CR><LF>
* <LF>NO CARRIER<CR><LF>
* <CR><LF>BUSY<CR><LF>
*
* Some Sierra devices omit the leading <CR> for in-call responses.
*/
return g_regex_new ("(\\r)?\\n(NO CARRIER)|(BUSY)|(NO ANSWER)|(NO DIALTONE)\\r\\n",
G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW | G_REGEX_OPTIMIZE,
0,
NULL);
}
/*****************************************************************************/
GRegex *
mm_voice_ring_regex_get (void)
{
+6
View File
@@ -85,6 +85,12 @@ gchar *mm_bcd_to_string (const guint8 *bcd,
*/
gchar *mm_at_quote_string (const gchar *input);
/*****************************************************************************/
/* CALL specific helpers and utilities */
/*****************************************************************************/
GRegex *mm_call_end_regex_get (void);
/*****************************************************************************/
/* VOICE specific helpers and utilities */
/*****************************************************************************/
+1
View File
@@ -1434,6 +1434,7 @@ serial_open_at (MMPortProbe *self)
NULL);
mm_port_serial_at_set_response_parser (MM_PORT_SERIAL_AT (ctx->serial),
mm_serial_parser_v1_parse,
mm_serial_parser_v1_remove_echo,
parser,
mm_serial_parser_v1_destroy);
}
+5 -22
View File
@@ -42,6 +42,7 @@ enum {
struct _MMPortSerialAtPrivate {
/* Response parser data */
MMPortSerialAtResponseParserFn response_parser_fn;
MMPortSerialAtRemoveEchoFn remove_echo_fn;
gpointer response_parser_user_data;
GDestroyNotify response_parser_notify;
@@ -61,6 +62,7 @@ struct _MMPortSerialAtPrivate {
void
mm_port_serial_at_set_response_parser (MMPortSerialAt *self,
MMPortSerialAtResponseParserFn fn,
MMPortSerialAtRemoveEchoFn echo_fn,
gpointer user_data,
GDestroyNotify notify)
{
@@ -70,30 +72,11 @@ mm_port_serial_at_set_response_parser (MMPortSerialAt *self,
self->priv->response_parser_notify (self->priv->response_parser_user_data);
self->priv->response_parser_fn = fn;
self->priv->remove_echo_fn = echo_fn;
self->priv->response_parser_user_data = user_data;
self->priv->response_parser_notify = notify;
}
void
mm_port_serial_at_remove_echo (GByteArray *response)
{
guint i;
if (response->len <= 2)
return;
for (i = 0; i < (response->len - 1); i++) {
/* If there is any content before the first
* <CR><LF>, assume it's echo or garbage, and skip it */
if (response->data[i] == '\r' && response->data[i + 1] == '\n') {
if (i > 0)
g_byte_array_remove_range (response, 0, i);
/* else, good, we're already started with <CR><LF> */
break;
}
}
}
static MMPortSerialResponseType
parse_response (MMPortSerial *port,
GByteArray *response,
@@ -109,7 +92,7 @@ parse_response (MMPortSerial *port,
/* Remove echo */
if (self->priv->remove_echo)
mm_port_serial_at_remove_echo (response);
self->priv->remove_echo_fn (self->priv->response_parser_user_data, response);
/* If there's no response to receive, we're done; e.g. if we only got
* unsolicited messages */
@@ -243,7 +226,7 @@ parse_unsolicited (MMPortSerial *port, GByteArray *response)
/* Remove echo */
if (self->priv->remove_echo)
mm_port_serial_at_remove_echo (response);
self->priv->remove_echo_fn (self->priv->response_parser_user_data, response);
for (iter = self->priv->unsolicited_msg_handlers; iter; iter = iter->next) {
MMAtUnsolicitedMsgHandler *handler = (MMAtUnsolicitedMsgHandler *) iter->data;
+4 -2
View File
@@ -65,6 +65,9 @@ typedef void (*MMPortSerialAtUnsolicitedMsgFn) (MMPortSerialAt *port,
GMatchInfo *match_info,
gpointer user_data);
typedef void (*MMPortSerialAtRemoveEchoFn) (gpointer user_data,
GByteArray *response);
#define MM_PORT_SERIAL_AT_REMOVE_ECHO "remove-echo"
#define MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED "init-sequence-enabled"
#define MM_PORT_SERIAL_AT_INIT_SEQUENCE "init-sequence"
@@ -97,6 +100,7 @@ void mm_port_serial_at_enable_unsolicited_msg_handler (MMPortSerialAt *self,
void mm_port_serial_at_set_response_parser (MMPortSerialAt *self,
MMPortSerialAtResponseParserFn fn,
MMPortSerialAtRemoveEchoFn echo_fn,
gpointer user_data,
GDestroyNotify notify);
@@ -113,8 +117,6 @@ gchar *mm_port_serial_at_command_finish (MMPortSerialAt *self,
GError **error);
/* Just for unit tests */
void mm_port_serial_at_remove_echo (GByteArray *response);
void mm_port_serial_at_set_flags (MMPortSerialAt *self,
MMPortSerialAtFlag flags);
+53 -4
View File
@@ -17,8 +17,11 @@
#include <string.h>
#include <stdlib.h>
#define _LIBMM_INSIDE_MM
#include "mm-error-helpers.h"
#include "mm-serial-parsers.h"
#include "mm-modem-helpers.h"
#include "mm-log-object.h"
/* Clean up the response by removing control characters like <CR><LF> etc */
@@ -92,7 +95,8 @@ typedef struct {
GRegex *regex_cms_error_str;
GRegex *regex_ezx_error;
GRegex *regex_unknown_error;
GRegex *regex_connect_failed;
GRegex *regex_call_start;
GRegex *regex_call_end;
GRegex *regex_na;
GRegex *regex_custom_error;
/* User-provided parser filter */
@@ -117,7 +121,8 @@ mm_serial_parser_v1_new (void)
parser->regex_cms_error_str = g_regex_new ("\\r\\n\\+CMS ERROR:\\s*([^\\n\\r]+)\\r\\n", flags, 0, NULL);
parser->regex_ezx_error = g_regex_new ("\\r\\nMODEM ERROR:\\s*(\\d+)\\r\\n", flags, 0, NULL);
parser->regex_unknown_error = g_regex_new ("\\r\\n(ERROR)|(COMMAND NOT SUPPORT)\\r\\n", flags, 0, NULL);
parser->regex_connect_failed = g_regex_new ("\\r\\n(NO CARRIER)|(BUSY)|(NO ANSWER)|(NO DIALTONE)\\r\\n", flags, 0, NULL);
parser->regex_call_start = g_regex_new ("(\\r)?\\n(CONNECT)\\r\\n", flags, 0, NULL);
parser->regex_call_end = mm_call_end_regex_get ();
/* Samsung Z810 may reply "NA" to report a not-available error */
parser->regex_na = g_regex_new ("\\r\\nNA\\r\\n", flags, 0, NULL);
@@ -160,6 +165,49 @@ mm_serial_parser_v1_add_filter (gpointer data,
parser->filter_user_data = user_data;
}
void
mm_serial_parser_v1_remove_echo (gpointer data,
GByteArray *response)
{
MMSerialParserV1 *parser = (MMSerialParserV1 *) data;
guint i;
if (response->len <= 2)
return;
/* Some devices omit the leading <CR> from call end responses which would
* otherwise fail the <CR><LF> checks below and be removed. We want to leave
* them in the response.
*/
if (g_regex_match_full (parser->regex_call_end,
(const gchar *) response->data,
response->len,
0,
0,
NULL,
NULL))
return;
if (g_regex_match_full (parser->regex_call_start,
(const gchar *) response->data,
response->len,
0,
0,
NULL,
NULL))
return;
for (i = 0; i < (response->len - 1); i++) {
/* If there is any content before the first
* <CR><LF>, assume it's echo or garbage, and skip it */
if (response->data[i] == '\r' && response->data[i + 1] == '\n') {
if (i > 0)
g_byte_array_remove_range (response, 0, i);
/* else, good, we're already started with <CR><LF> */
break;
}
}
}
gboolean
mm_serial_parser_v1_parse (gpointer data,
GString *response,
@@ -316,7 +364,7 @@ mm_serial_parser_v1_parse (gpointer data,
g_clear_pointer (&match_info, g_match_info_free);
/* Connection failures */
found = g_regex_match_full (parser->regex_connect_failed,
found = g_regex_match_full (parser->regex_call_end,
response->str, response->len,
0, 0, &match_info, NULL);
if (found) {
@@ -396,7 +444,8 @@ mm_serial_parser_v1_destroy (gpointer data)
g_regex_unref (parser->regex_cms_error_str);
g_regex_unref (parser->regex_ezx_error);
g_regex_unref (parser->regex_unknown_error);
g_regex_unref (parser->regex_connect_failed);
g_regex_unref (parser->regex_call_start);
g_regex_unref (parser->regex_call_end);
g_regex_unref (parser->regex_na);
if (parser->regex_custom_successful)
+2
View File
@@ -26,6 +26,8 @@ gboolean mm_serial_parser_v1_parse (gpointer parser,
GString *response,
gpointer log_object,
GError **error);
void mm_serial_parser_v1_remove_echo (gpointer parser,
GByteArray *response);
void mm_serial_parser_v1_destroy (gpointer parser);
gboolean mm_serial_parser_v1_is_known_error (const GError *error);
@@ -1245,6 +1245,7 @@ setup_ports (MMBroadbandModem *self)
mm_port_serial_at_set_response_parser (MM_PORT_SERIAL_AT (primary),
mm_serial_parser_v1_parse,
mm_serial_parser_v1_remove_echo,
parser,
mm_serial_parser_v1_destroy);
}
+7 -1
View File
@@ -46,6 +46,8 @@ static const EchoRemovalTest echo_removal_tests[] = {
{ "echo echo\r\nthis is valid\r\nand so is this", "\r\nthis is valid\r\nand so is this" },
{ "\r\nthis is valid\r\nand so is this", "\r\nthis is valid\r\nand so is this" },
{ "\r\nthis is valid\r\nand so is this\r\n", "\r\nthis is valid\r\nand so is this\r\n" },
{ "\nNO CARRIER\r\n", "\nNO CARRIER\r\n" },
{ "\nCONNECT\r\n", "\nCONNECT\r\n" },
};
static const ParseResponseTest parse_ok_tests[] = {
@@ -89,6 +91,7 @@ at_serial_echo_removal (void)
guint i;
for (i = 0; i < G_N_ELEMENTS (echo_removal_tests); i++) {
gpointer parser;
GByteArray *ba;
/* Note that we add last NUL also to the byte array, so that we can compare
@@ -98,7 +101,9 @@ at_serial_echo_removal (void)
(guint8 *)echo_removal_tests[i].original,
strlen (echo_removal_tests[i].original) + 1);
mm_port_serial_at_remove_echo (ba);
parser = mm_serial_parser_v1_new ();
mm_serial_parser_v1_remove_echo (parser, ba);
mm_serial_parser_v1_destroy (parser);
g_assert_cmpstr ((gchar *)ba->data, ==, echo_removal_tests[i].without_echo);
@@ -119,6 +124,7 @@ _run_parse_test (const ParseResponseTest tests[], guint number_of_tests)
parser = mm_serial_parser_v1_new ();
response = g_string_new (tests[i].response);
found = mm_serial_parser_v1_parse (parser, response, NULL, &error);
mm_serial_parser_v1_destroy (parser);
/* Verify if we expect a match or not */
g_assert_cmpint (found, ==, tests[i].found);
+1
View File
@@ -237,6 +237,7 @@ start_cb (void)
/* Set common response parser */
mm_port_serial_at_set_response_parser (MM_PORT_SERIAL_AT (port),
mm_serial_parser_v1_parse,
mm_serial_parser_v1_remove_echo,
mm_serial_parser_v1_new (),
mm_serial_parser_v1_destroy);