From 757df26b295a9b9306fe3278b7f3730cc3a9c703 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 15 May 2025 10:13:22 -0500 Subject: broadband-modem,modem-helpers: move +CPIN response parsing to modem helpers Unit test it better, plus remove code duplication in various plugins. Signed-off-by: Dan Williams --- src/mm-broadband-modem.c | 61 +++------------------- src/mm-modem-helpers.c | 64 ++++++++++++++++++++++++ src/mm-modem-helpers.h | 5 ++ src/plugins/simtech/mm-broadband-modem-simtech.c | 39 +-------------- src/tests/test-modem-helpers.c | 33 ++++++++++++ 5 files changed, 110 insertions(+), 92 deletions(-) diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c index e0f277fe..3d02553d 100644 --- a/src/mm-broadband-modem.c +++ b/src/mm-broadband-modem.c @@ -1505,32 +1505,6 @@ modem_load_own_numbers (MMIfaceModem *self, /*****************************************************************************/ /* Check if unlock required (Modem interface) */ -typedef struct { - const gchar *result; - MMModemLock code; -} CPinResult; - -static CPinResult unlock_results[] = { - /* Longer entries first so we catch the correct one with strcmp() */ - { "READY", MM_MODEM_LOCK_NONE }, - { "SIM PIN2", MM_MODEM_LOCK_SIM_PIN2 }, - { "SIM PUK2", MM_MODEM_LOCK_SIM_PUK2 }, - { "SIM PIN", MM_MODEM_LOCK_SIM_PIN }, - { "SIM PUK", MM_MODEM_LOCK_SIM_PUK }, - { "PH-NETSUB PIN", MM_MODEM_LOCK_PH_NETSUB_PIN }, - { "PH-NETSUB PUK", MM_MODEM_LOCK_PH_NETSUB_PUK }, - { "PH-FSIM PIN", MM_MODEM_LOCK_PH_FSIM_PIN }, - { "PH-FSIM PUK", MM_MODEM_LOCK_PH_FSIM_PUK }, - { "PH-CORP PIN", MM_MODEM_LOCK_PH_CORP_PIN }, - { "PH-CORP PUK", MM_MODEM_LOCK_PH_CORP_PUK }, - { "PH-SIM PIN", MM_MODEM_LOCK_PH_SIM_PIN }, - { "PH-NET PIN", MM_MODEM_LOCK_PH_NET_PIN }, - { "PH-NET PUK", MM_MODEM_LOCK_PH_NET_PUK }, - { "PH-SP PIN", MM_MODEM_LOCK_PH_SP_PIN }, - { "PH-SP PUK", MM_MODEM_LOCK_PH_SP_PUK }, - { NULL } -}; - static MMModemLock modem_load_unlock_required_finish (MMIfaceModem *self, GAsyncResult *res, @@ -1553,42 +1527,19 @@ cpin_query_ready (MMIfaceModem *self, GTask *task) { - MMModemLock lock = MM_MODEM_LOCK_UNKNOWN; + MMModemLock lock = MM_MODEM_LOCK_UNKNOWN; const gchar *result; - GError *error = NULL; + GError *error = NULL; result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); if (error) { g_task_return_error (task, error); - g_object_unref (task); - return; - } - - if (result && - strstr (result, "+CPIN:")) { - CPinResult *iter = &unlock_results[0]; - const gchar *str; - - str = strstr (result, "+CPIN:") + 6; - /* Skip possible whitespaces after '+CPIN:' and before the response */ - while (*str == ' ') - str++; - - /* Some phones (Motorola EZX models) seem to quote the response */ - if (str[0] == '"') - str++; - - /* Translate the reply */ - while (iter->result) { - if (g_str_has_prefix (str, iter->result)) { - lock = iter->code; - break; - } - iter++; - } + } else { + if (result) + lock = mm_parse_cpin_response (result, TRUE); + g_task_return_int (task, lock); } - g_task_return_int (task, lock); g_object_unref (task); } diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c index d69951be..a79ca956 100644 --- a/src/mm-modem-helpers.c +++ b/src/mm-modem-helpers.c @@ -5807,3 +5807,67 @@ mm_string_uint_map_lookup (const MMStringUintMap *map, } return default_value; } + +/*****************************************************************************/ + +typedef struct { + const gchar *result; + const MMModemLock code; +} CPinResult; + +static const CPinResult unlock_results[] = { + /* Longer entries first so we catch the correct one with strcmp() */ + { "READY", MM_MODEM_LOCK_NONE }, + { "SIM PIN2", MM_MODEM_LOCK_SIM_PIN2 }, + { "SIM PUK2", MM_MODEM_LOCK_SIM_PUK2 }, + { "SIM PIN", MM_MODEM_LOCK_SIM_PIN }, + { "SIM PUK", MM_MODEM_LOCK_SIM_PUK }, + { "PH-NETSUB PIN", MM_MODEM_LOCK_PH_NETSUB_PIN }, + { "PH-NETSUB PUK", MM_MODEM_LOCK_PH_NETSUB_PUK }, + { "PH-FSIM PIN", MM_MODEM_LOCK_PH_FSIM_PIN }, + { "PH-FSIM PUK", MM_MODEM_LOCK_PH_FSIM_PUK }, + { "PH-CORP PIN", MM_MODEM_LOCK_PH_CORP_PIN }, + { "PH-CORP PUK", MM_MODEM_LOCK_PH_CORP_PUK }, + { "PH-SIM PIN", MM_MODEM_LOCK_PH_SIM_PIN }, + { "PH-NET PIN", MM_MODEM_LOCK_PH_NET_PIN }, + { "PH-NET PUK", MM_MODEM_LOCK_PH_NET_PUK }, + { "PH-SP PIN", MM_MODEM_LOCK_PH_SP_PIN }, + { "PH-SP PUK", MM_MODEM_LOCK_PH_SP_PUK }, + { NULL } +}; + +MMModemLock +mm_parse_cpin_response (const gchar *response, + gboolean expect_cpin_prefix) +{ + const CPinResult *iter = &unlock_results[0]; + + if (expect_cpin_prefix) { + const gchar *p; + + p = strstr (response, "+CPIN:"); + if (!p) + return MM_MODEM_LOCK_UNKNOWN; + + /* Advance past the +CPIN: */ + response = p + 6; + } + + /* Skip possible whitespaces after '+CPIN:' and before the response */ + while (*response == ' ') + response++; + + /* Some phones (Motorola EZX models) seem to quote the response */ + if (response[0] == '"') + response++; + + /* Translate the reply */ + while (iter->result) { + if (g_str_has_prefix (response, iter->result)) { + return iter->code; + } + iter++; + } + + return MM_MODEM_LOCK_UNKNOWN; +} diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h index 73203c8b..eb62a551 100644 --- a/src/mm-modem-helpers.h +++ b/src/mm-modem-helpers.h @@ -638,4 +638,9 @@ guint mm_string_uint_map_lookup (const MMStringUintMap *map, const gchar *str, const guint default_value); +/*****************************************************************************/ + +MMModemLock mm_parse_cpin_response (const gchar *response, + gboolean expect_cpin_prefix); + #endif /* MM_MODEM_HELPERS_H */ diff --git a/src/plugins/simtech/mm-broadband-modem-simtech.c b/src/plugins/simtech/mm-broadband-modem-simtech.c index cdc822d8..a390a88f 100644 --- a/src/plugins/simtech/mm-broadband-modem-simtech.c +++ b/src/plugins/simtech/mm-broadband-modem-simtech.c @@ -72,31 +72,6 @@ struct _MMBroadbandModemSimtechPrivate { MMModemLock sim_lock; }; -typedef struct { - const gchar *result; - MMModemLock code; -} CPinResult; - -static CPinResult unlock_results[] = { - { "READY", MM_MODEM_LOCK_NONE }, - { "SIM PIN2", MM_MODEM_LOCK_SIM_PIN2 }, - { "SIM PUK2", MM_MODEM_LOCK_SIM_PUK2 }, - { "SIM PIN", MM_MODEM_LOCK_SIM_PIN }, - { "SIM PUK", MM_MODEM_LOCK_SIM_PUK }, - { "PH-NETSUB PIN", MM_MODEM_LOCK_PH_NETSUB_PIN }, - { "PH-NETSUB PUK", MM_MODEM_LOCK_PH_NETSUB_PUK }, - { "PH-FSIM PIN", MM_MODEM_LOCK_PH_FSIM_PIN }, - { "PH-FSIM PUK", MM_MODEM_LOCK_PH_FSIM_PUK }, - { "PH-CORP PIN", MM_MODEM_LOCK_PH_CORP_PIN }, - { "PH-CORP PUK", MM_MODEM_LOCK_PH_CORP_PUK }, - { "PH-SIM PIN", MM_MODEM_LOCK_PH_SIM_PIN }, - { "PH-NET PIN", MM_MODEM_LOCK_PH_NET_PIN }, - { "PH-NET PUK", MM_MODEM_LOCK_PH_NET_PUK }, - { "PH-SP PIN", MM_MODEM_LOCK_PH_SP_PIN }, - { "PH-SP PUK", MM_MODEM_LOCK_PH_SP_PUK }, - { NULL } -}; - /*****************************************************************************/ /* Setup/Cleanup unsolicited events (3GPP interface) */ @@ -158,20 +133,10 @@ simtech_cpin_changed (MMPortSerialAt *port, MMBroadbandModemSimtech *self) { g_autofree gchar *str = NULL; - CPinResult *iter; str = mm_get_string_unquoted_from_match_info (match_info, 1); - if (str) { - iter = &unlock_results[0]; - /* Translate the reply */ - while (iter->result) { - if (g_str_has_prefix (str, iter->result)) { - self->priv->sim_lock = iter->code; - return; - } - iter++; - } - } + if (str) + self->priv->sim_lock = mm_parse_cpin_response (str, FALSE); } static void diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c index a3a42ad8..3421921b 100644 --- a/src/tests/test-modem-helpers.c +++ b/src/tests/test-modem-helpers.c @@ -5089,6 +5089,37 @@ test_string_to_access_tech (void) /*****************************************************************************/ +typedef struct { + const gchar *str; + const gboolean expect_prefix; + const MMModemLock expected_lock; +} TestCpinResponse; + +static const TestCpinResponse test_cpin_responses[] = { + { "+CME ERROR: 100", TRUE, MM_MODEM_LOCK_UNKNOWN }, + { "+CPIN: SIM PIN", TRUE, MM_MODEM_LOCK_SIM_PIN }, + { "+CPIN: SIM PUK2", TRUE, MM_MODEM_LOCK_SIM_PUK2 }, + { " SIM PUK", FALSE, MM_MODEM_LOCK_SIM_PUK }, + { " SIM PUK2", FALSE, MM_MODEM_LOCK_SIM_PUK2 }, + { "+CPIN: \"SIM PIN\"", TRUE, MM_MODEM_LOCK_SIM_PIN }, + { "+CPIN: PH-NET PIN", TRUE, MM_MODEM_LOCK_PH_NET_PIN }, +}; + +static void +test_cpin_response (void) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (test_cpin_responses); i++) { + MMModemLock lock; + + lock = mm_parse_cpin_response (test_cpin_responses[i].str, test_cpin_responses[i].expect_prefix); + g_assert_cmpint (lock, ==, test_cpin_responses[i].expected_lock); + } +} + +/*****************************************************************************/ + #define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (GTestFixtureFunc) t, NULL) int main (int argc, char **argv) @@ -5351,6 +5382,8 @@ int main (int argc, char **argv) g_test_suite_add (suite, TESTCASE (test_string_to_access_tech, NULL)); + g_test_suite_add (suite, TESTCASE (test_cpin_response, NULL)); + result = g_test_run (); reg_test_data_free (reg_data); -- cgit v1.2.3-70-g09d2 From 5c1fcbfe4a6ee9e89f8b859e26d7d0f74106178d Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 15 May 2025 10:21:13 -0500 Subject: mtk-legacy: read IMSI if CPIN response is an error Some older MTK-based phones (Tecno T528 NEW) reply to +CPIN with an error but are (obviously) unlocked, otherwise they wouldn't be able to boot up to the point of talking to a computer. Since they are actually unlocked they will happily give out the IMSI even if +CPIN errors. To work around the +CPIN issue treat the device as unlocked if we can successfully read the IMSI. Fixes: https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/980 Signed-off-by: Dan Williams --- src/plugins/mtk/mm-broadband-modem-mtk-legacy.c | 97 +++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/src/plugins/mtk/mm-broadband-modem-mtk-legacy.c b/src/plugins/mtk/mm-broadband-modem-mtk-legacy.c index 36121d0e..fe1f48cb 100644 --- a/src/plugins/mtk/mm-broadband-modem-mtk-legacy.c +++ b/src/plugins/mtk/mm-broadband-modem-mtk-legacy.c @@ -55,6 +55,101 @@ struct _MMBroadbandModemMtkLegacyPrivate { }; /*****************************************************************************/ +/* Check unlock required (Modem interface) */ + +static MMModemLock +load_unlock_required_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + GError *inner_error = NULL; + gssize value; + + value = g_task_propagate_int (G_TASK (res), &inner_error); + if (inner_error) { + g_propagate_error (error, inner_error); + return MM_MODEM_LOCK_UNKNOWN; + } + return (MMModemLock)value; +} + +static void +unlock_required_cimi_query_ready (MMIfaceModem *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + + mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (error) + g_task_return_error (task, error); + else { + /* Assume unlocked if we can successfully read the IMSI */ + g_task_return_int (task, MM_MODEM_LOCK_NONE); + } + g_object_unref (task); +} + +static void +cpin_query_ready (MMIfaceModem *self, + GAsyncResult *res, + GTask *task) +{ + MMModemLock lock = MM_MODEM_LOCK_UNKNOWN; + const gchar *result; + GError *error = NULL; + + result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (error) { + /* Some older MTK-based phones reply to +CPIN with CME ERROR 100, + * but to even boot up they require the SIM PIN and so must be + * unlocked. Double-check by reading the IMSI though. + */ + if (g_error_matches (error, + MM_MOBILE_EQUIPMENT_ERROR, + MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN)) { + mm_base_modem_at_command (MM_BASE_MODEM (self), + "+CIMI", + 10, + FALSE, + (GAsyncReadyCallback)unlock_required_cimi_query_ready, + task); + return; + } + + /* Otherwise just return the error */ + g_task_return_error (task, error); + } else { + if (result) + lock = mm_parse_cpin_response (result, TRUE); + g_task_return_int (task, lock); + } + + g_object_unref (task); +} + +static void +load_unlock_required (MMIfaceModem *self, + gboolean last_attempt, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (self, cancellable, callback, user_data); + + mm_obj_dbg (self, "checking if unlock required..."); + mm_base_modem_at_command (MM_BASE_MODEM (self), + "+CPIN?", + 10, + FALSE, + (GAsyncReadyCallback)cpin_query_ready, + task); +} + +/*****************************************************************************/ + static gboolean modem_after_sim_unlock_finish (MMIfaceModem *self, GAsyncResult *res, @@ -835,6 +930,8 @@ iface_modem_init (MMIfaceModemInterface *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); + iface->load_unlock_required = load_unlock_required; + iface->load_unlock_required_finish = load_unlock_required_finish; iface->modem_after_sim_unlock = modem_after_sim_unlock; iface->modem_after_sim_unlock_finish = modem_after_sim_unlock_finish; iface->load_supported_modes = load_supported_modes; -- cgit v1.2.3-70-g09d2