diff options
-rw-r--r-- | src/plugins/quectel/mm-modem-helpers-quectel.c | 49 | ||||
-rw-r--r-- | src/plugins/quectel/mm-modem-helpers-quectel.h | 2 | ||||
-rw-r--r-- | src/plugins/quectel/mm-shared-quectel.c | 134 | ||||
-rw-r--r-- | src/plugins/quectel/tests/test-modem-helpers-quectel.c | 16 |
4 files changed, 176 insertions, 25 deletions
diff --git a/src/plugins/quectel/mm-modem-helpers-quectel.c b/src/plugins/quectel/mm-modem-helpers-quectel.c index 262d9794..4335e506 100644 --- a/src/plugins/quectel/mm-modem-helpers-quectel.c +++ b/src/plugins/quectel/mm-modem-helpers-quectel.c @@ -89,3 +89,52 @@ mm_quectel_parse_ctzu_test_response (const gchar *response, return TRUE; } + +/*****************************************************************************/ +/* standard firmware info + * Format of the string is: + * "[main version]_[modem and app version]" + * e.g. EM05GFAR07A07M1G_01.016.01.016 + */ +#define QUECTEL_STD_FIRMWARE_VERSION_SEG 2 + +/* Format of the string is: + * "modem_main.modem_minor.ap_main.ap_minor" + * e.g. 01.016.01.016 + */ +#define QUECTEL_STD_MODEM_AP_FIRMWARE_VER_SEG 4 +#define QUECTEL_STD_MODEM_AP_FIRMWARE_VER_LEN 13 + +#define QUECTEL_MAIN_VERSION_INVALID_TAG "00" +#define QUECTEL_MINOR_VERSION_INVALID_TAG "000" + +gboolean +mm_quectel_check_standard_firmware_version_valid (const gchar *std_str) +{ + gboolean valid = TRUE; + g_auto(GStrv) split_std_fw = NULL; + g_auto(GStrv) split_modem_ap_fw = NULL; + const gchar *modem_ap_fw; + + if (std_str) { + split_std_fw = g_strsplit (std_str, "_", QUECTEL_STD_FIRMWARE_VERSION_SEG); + /* Quectel standard format of the [main version]_[modem and app version] + * Sometimes we find that the [modem and app version] query is missing by [AT+QMGR] + * for example: we expect EM05GFAR07A07M1G_01.016.01.016,but unexpected EM05GFAR07A07M1G_01.016.00.000 was returned + * Quectel will check for this abnormal [modem and app version] and flag it + */ + if (g_strv_length (split_std_fw) == QUECTEL_STD_FIRMWARE_VERSION_SEG) { + modem_ap_fw = split_std_fw[1]; + if (strlen (modem_ap_fw) == QUECTEL_STD_MODEM_AP_FIRMWARE_VER_LEN) { + split_modem_ap_fw = g_strsplit (modem_ap_fw, ".", QUECTEL_STD_MODEM_AP_FIRMWARE_VER_SEG); + + if (g_strv_length (split_modem_ap_fw) == QUECTEL_STD_MODEM_AP_FIRMWARE_VER_SEG && + !g_strcmp0 (split_modem_ap_fw[2], QUECTEL_MAIN_VERSION_INVALID_TAG) && + !g_strcmp0 (split_modem_ap_fw[3], QUECTEL_MINOR_VERSION_INVALID_TAG)){ + valid = FALSE; + } + } + } + } + return valid; +} diff --git a/src/plugins/quectel/mm-modem-helpers-quectel.h b/src/plugins/quectel/mm-modem-helpers-quectel.h index d4ec0eae..ea9ff5c1 100644 --- a/src/plugins/quectel/mm-modem-helpers-quectel.h +++ b/src/plugins/quectel/mm-modem-helpers-quectel.h @@ -29,4 +29,6 @@ gboolean mm_quectel_parse_ctzu_test_response (const gchar *response, gboolean *supports_enable_update_rtc, GError **error); +gboolean mm_quectel_check_standard_firmware_version_valid (const gchar *std_str); + #endif /* MM_MODEM_HELPERS_QUECTEL_H */ diff --git a/src/plugins/quectel/mm-shared-quectel.c b/src/plugins/quectel/mm-shared-quectel.c index 93153fff..c95ab200 100644 --- a/src/plugins/quectel/mm-shared-quectel.c +++ b/src/plugins/quectel/mm-shared-quectel.c @@ -59,6 +59,11 @@ typedef struct { GRegex *rdy_regex; } Private; +typedef struct { + MMFirmwareUpdateSettings *update_settings; + gint get_firmware_maximum_retry_int; +} LoadUpdateSettingsContext; + static void private_free (Private *priv) { @@ -210,22 +215,89 @@ quectel_get_firmware_update_methods (MMBaseModem *modem, return update_methods; } +static gboolean quectel_at_port_get_firmware_version_retry (GTask *task); + +/* Eg. Sometimes when the module is booted up and sends the command to acquire the version to the modem, + * the modem may not be ready. The standard app version number of the response was not obtained; + * Fwupd(LVFS) requires relatively complete version information to update firmware. If the version information is incorrect, + * the update may not be possible. Therefore, we will conduct another query, up to 16 times. + */ +#define QUECTEL_STD_AP_FIRMWARE_INVALID_MAXIMUM_RETRY 16 + +static void +quectel_at_port_get_firmware_version_retry_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + const gchar *version; + LoadUpdateSettingsContext *ctx; + + ctx = g_task_get_task_data (task); + version = mm_base_modem_at_command_finish (modem, res, NULL); + ctx->get_firmware_maximum_retry_int--; + + if (version) { + if (mm_quectel_check_standard_firmware_version_valid (version)) { + mm_obj_dbg (modem, "Valid firmware version:%s, re-update", version); + mm_firmware_update_settings_set_version (ctx->update_settings, version); + g_task_return_pointer (task, g_object_ref (ctx->update_settings), g_object_unref); + g_object_unref (task); + return; + } + } + /* When the maximum repeat fetch count is greater than or equal to 0, + * attempt to retrieve version information again. */ + if (ctx->get_firmware_maximum_retry_int >= 0) + g_timeout_add_seconds (1, (GSourceFunc) quectel_at_port_get_firmware_version_retry, task); + else { + mm_obj_dbg (modem, "Maximum retries to query firmware version reached: invalid firmware version received"); + g_task_return_pointer (task, g_object_ref (ctx->update_settings), g_object_unref); + g_object_unref (task); + } +} + +static gboolean +quectel_at_port_get_firmware_version_retry (GTask *task) +{ + MMBaseModem *self; + + self = g_task_get_source_object (task); + + /* Fetch full firmware info */ + mm_base_modem_at_command (self, + "+QGMR?", + 3, + FALSE, + (GAsyncReadyCallback) quectel_at_port_get_firmware_version_retry_ready, + task); + + return G_SOURCE_REMOVE; +} + static void quectel_at_port_get_firmware_version_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { - MMFirmwareUpdateSettings *update_settings; - const gchar *version; + LoadUpdateSettingsContext *ctx; + const gchar *version; + gboolean ap_firmware_version_valid = TRUE; - update_settings = g_task_get_task_data (task); + ctx = g_task_get_task_data (task); version = mm_base_modem_at_command_finish (modem, res, NULL); if (version) - mm_firmware_update_settings_set_version (update_settings, version); + ap_firmware_version_valid = mm_quectel_check_standard_firmware_version_valid (version); - g_task_return_pointer (task, g_object_ref (update_settings), g_object_unref); - g_object_unref (task); + if (version && ap_firmware_version_valid) { + mm_firmware_update_settings_set_version (ctx->update_settings, version); + g_task_return_pointer (task, g_object_ref (ctx->update_settings), g_object_unref); + g_object_unref (task); + } else { + if (version) + mm_obj_dbg (modem, "Invalid firmware version %s return, retrying", version); + g_timeout_add_seconds (1, (GSourceFunc) quectel_at_port_get_firmware_version_retry, task); + } } #if defined WITH_MBIM @@ -237,17 +309,17 @@ quectel_mbim_port_get_firmware_version_ready (MbimDevice *device, g_autoptr(MbimMessage) response = NULL; guint32 version_id; g_autofree gchar *version_str = NULL; - MMFirmwareUpdateSettings *update_settings; + LoadUpdateSettingsContext *ctx; - update_settings = g_task_get_task_data (task); + ctx = g_task_get_task_data (task); response = mbim_device_command_finish (device, res, NULL); if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, NULL) && mbim_message_qdu_quectel_read_version_response_parse (response, &version_id, &version_str, NULL)) { - mm_firmware_update_settings_set_version (update_settings, version_str); + mm_firmware_update_settings_set_version (ctx->update_settings, version_str); } - g_task_return_pointer (task, g_object_ref (update_settings), g_object_unref); + g_task_return_pointer (task, g_object_ref (ctx->update_settings), g_object_unref); g_object_unref (task); } #endif @@ -257,16 +329,16 @@ qfastboot_test_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { - MMFirmwareUpdateSettings *update_settings; + LoadUpdateSettingsContext *ctx; - update_settings = g_task_get_task_data (task); + ctx = g_task_get_task_data (task); /* Set update method */ if (mm_base_modem_at_command_finish (self, res, NULL)) { - mm_firmware_update_settings_set_method (update_settings, MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT); - mm_firmware_update_settings_set_fastboot_at (update_settings, "AT+QFASTBOOT"); + mm_firmware_update_settings_set_method (ctx->update_settings, MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT); + mm_firmware_update_settings_set_fastboot_at (ctx->update_settings, "AT+QFASTBOOT"); } else - mm_firmware_update_settings_set_method (update_settings, MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE); + mm_firmware_update_settings_set_method (ctx->update_settings, MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE); /* Fetch full firmware info */ mm_base_modem_at_command (MM_BASE_MODEM (self), @@ -282,7 +354,7 @@ quectel_at_port_get_firmware_revision_ready (MMBaseModem *self, GAsyncResult *res, GTask *task) { - MMFirmwareUpdateSettings *update_settings; + LoadUpdateSettingsContext *ctx; MMModemFirmwareUpdateMethod update_methods; const gchar *revision; const gchar *name; @@ -290,8 +362,8 @@ quectel_at_port_get_firmware_revision_ready (MMBaseModem *self, g_autoptr(GPtrArray) ids = NULL; GError *error = NULL; - update_settings = g_task_get_task_data (task); - update_methods = mm_firmware_update_settings_get_method (update_settings); + ctx = g_task_get_task_data (task); + update_methods = mm_firmware_update_settings_get_method (ctx->update_settings); /* Set device ids */ ids = mm_iface_firmware_build_generic_device_ids (MM_IFACE_MODEM_FIRMWARE (self), &error); @@ -311,7 +383,7 @@ quectel_at_port_get_firmware_revision_ready (MMBaseModem *self, g_ptr_array_insert (ids, 0, g_strdup_printf ("%s&NAME_%s", id, name)); } - mm_firmware_update_settings_set_device_ids (update_settings, (const gchar **)ids->pdata); + mm_firmware_update_settings_set_device_ids (ctx->update_settings, (const gchar **)ids->pdata); /* Set update methods */ if (update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE) { @@ -333,6 +405,15 @@ quectel_at_port_get_firmware_revision_ready (MMBaseModem *self, } } +static void +load_update_settings_context_free (LoadUpdateSettingsContext *ctx) +{ + if (ctx->update_settings) + g_object_unref (ctx->update_settings); + + g_free (ctx); +} + void mm_shared_quectel_firmware_load_update_settings (MMIfaceModemFirmware *self, GAsyncReadyCallback callback, @@ -341,15 +422,17 @@ mm_shared_quectel_firmware_load_update_settings (MMIfaceModemFirmware *self, GTask *task; MMPortSerialAt *at_port; MMModemFirmwareUpdateMethod update_methods; - MMFirmwareUpdateSettings *update_settings; + LoadUpdateSettingsContext *ctx; task = g_task_new (self, NULL, callback, user_data); - + ctx = g_new0 (LoadUpdateSettingsContext, 1); + at_port = mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL); if (at_port) { update_methods = quectel_get_firmware_update_methods (MM_BASE_MODEM (self), MM_PORT (at_port)); - update_settings = mm_firmware_update_settings_new (update_methods); - g_task_set_task_data (task, update_settings, g_object_unref); + ctx->update_settings = mm_firmware_update_settings_new (update_methods); + ctx->get_firmware_maximum_retry_int = QUECTEL_STD_AP_FIRMWARE_INVALID_MAXIMUM_RETRY; + g_task_set_task_data (task, ctx, (GDestroyNotify)load_update_settings_context_free); /* Fetch modem name */ mm_base_modem_at_command (MM_BASE_MODEM (self), @@ -373,10 +456,11 @@ mm_shared_quectel_firmware_load_update_settings (MMIfaceModemFirmware *self, g_autoptr(MbimMessage) message = NULL; update_methods = quectel_get_firmware_update_methods (MM_BASE_MODEM (self), MM_PORT (mbim)); - update_settings = mm_firmware_update_settings_new (update_methods); + ctx->update_settings = mm_firmware_update_settings_new (update_methods); + ctx->get_firmware_maximum_retry_int = QUECTEL_STD_AP_FIRMWARE_INVALID_MAXIMUM_RETRY; /* Fetch firmware info */ - g_task_set_task_data (task, update_settings, g_object_unref); + g_task_set_task_data (task, ctx, (GDestroyNotify)load_update_settings_context_free); message = mbim_message_qdu_quectel_read_version_set_new (MBIM_QDU_QUECTEL_VERSION_TYPE_FW_BUILD_ID, NULL); mbim_device_command (mm_port_mbim_peek_device (mbim), message, diff --git a/src/plugins/quectel/tests/test-modem-helpers-quectel.c b/src/plugins/quectel/tests/test-modem-helpers-quectel.c index 0e2c7420..dee01865 100644 --- a/src/plugins/quectel/tests/test-modem-helpers-quectel.c +++ b/src/plugins/quectel/tests/test-modem-helpers-quectel.c @@ -80,6 +80,20 @@ test_ctzu (void) } /*****************************************************************************/ +/* Test ^FIRMVERSION test responses */ +static void +test_firmversion (void) +{ + gboolean valid = TRUE; + + valid = mm_quectel_check_standard_firmware_version_valid ("EM05GFAR07A07M1G_01.016.01.016"); + g_assert_cmpuint (valid, ==, TRUE); + + valid = mm_quectel_check_standard_firmware_version_valid ("EM05GFAR07A07M1G_01.016.00.000"); + g_assert_cmpuint (valid, ==, FALSE); +} + +/*****************************************************************************/ int main (int argc, char **argv) { @@ -89,5 +103,7 @@ int main (int argc, char **argv) g_test_add_func ("/MM/quectel/ctzu", test_ctzu); + g_test_add_func ("/MM/quectel/firmversion", test_firmversion); + return g_test_run (); } |