From d4b6b8dd8c7bf654eaebb83d6cf08f676eadf57e Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Fri, 5 Oct 2012 10:10:50 +0200 Subject: broadband-modem-qmi: implement Firmware switching using QMI Soo... we can now switch firmware images directly from ModemManager, taking care of completely rebooting the modem directly. The new image to select needs to be specified by the 'unique-id' currently, which maps to the 'Build ID' reported in QMI for PRI and MODEM images. --- src/mm-broadband-modem-qmi.c | 236 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) (limited to 'src') diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c index d487cde7..ea428db9 100644 --- a/src/mm-broadband-modem-qmi.c +++ b/src/mm-broadband-modem-qmi.c @@ -6454,6 +6454,240 @@ firmware_load_current (MMIfaceModemFirmware *_self, g_object_unref (result); } +/*****************************************************************************/ +/* Change current firmware (Firmware interface) */ + +typedef struct { + MMBroadbandModemQmi *self; + QmiClientDms *client; + GSimpleAsyncResult *result; + MMFirmwareProperties *firmware; +} FirmwareChangeCurrentContext; + +static void +firmware_change_current_context_complete_and_free (FirmwareChangeCurrentContext *ctx) +{ + g_simple_async_result_complete_in_idle (ctx->result); + g_object_unref (ctx->result); + g_object_unref (ctx->self); + g_object_unref (ctx->client); + if (ctx->firmware) + g_object_unref (ctx->firmware); + g_slice_free (FirmwareChangeCurrentContext, ctx); +} + +static gboolean +firmware_change_current_finish (MMIfaceModemFirmware *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void +firmware_set_operating_mode_reset_ready (QmiClientDms *client, + GAsyncResult *res, + FirmwareChangeCurrentContext *ctx) +{ + QmiMessageDmsSetOperatingModeOutput *output; + GError *error = NULL; + + output = qmi_client_dms_set_operating_mode_finish (client, res, &error); + if (!output || + !qmi_message_dms_set_operating_mode_output_get_result (output, &error)) { + g_simple_async_result_take_error (ctx->result, error); + } else { + mm_info ("Modem is being rebooted now"); + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + } + + if (output) + qmi_message_dms_set_operating_mode_output_unref (output); + + firmware_change_current_context_complete_and_free (ctx); +} + +static void +firmware_set_operating_mode_offline_ready (QmiClientDms *client, + GAsyncResult *res, + FirmwareChangeCurrentContext *ctx) +{ + QmiMessageDmsSetOperatingModeInput *input; + QmiMessageDmsSetOperatingModeOutput *output; + GError *error = NULL; + + output = qmi_client_dms_set_operating_mode_finish (client, res, &error); + if (!output) { + g_simple_async_result_take_error (ctx->result, error); + firmware_change_current_context_complete_and_free (ctx); + return; + } + + if (!qmi_message_dms_set_operating_mode_output_get_result (output, &error)) { + g_simple_async_result_take_error (ctx->result, error); + firmware_change_current_context_complete_and_free (ctx); + qmi_message_dms_set_operating_mode_output_unref (output); + return; + } + + qmi_message_dms_set_operating_mode_output_unref (output); + + /* Now, go into reset mode. This will fully reboot the modem, and the current + * modem object should get disposed. */ + input = qmi_message_dms_set_operating_mode_input_new (); + qmi_message_dms_set_operating_mode_input_set_mode (input, QMI_DMS_OPERATING_MODE_RESET, NULL); + qmi_client_dms_set_operating_mode (ctx->client, + input, + 20, + NULL, + (GAsyncReadyCallback)firmware_set_operating_mode_reset_ready, + ctx); + qmi_message_dms_set_operating_mode_input_unref (input); +} + +static void +firmware_select_stored_image_ready (QmiClientDms *client, + GAsyncResult *res, + FirmwareChangeCurrentContext *ctx) +{ + QmiMessageDmsSetOperatingModeInput *input; + QmiMessageDmsSetFirmwarePreferenceOutput *output; + GError *error = NULL; + + output = qmi_client_dms_set_firmware_preference_finish (client, res, &error); + if (!output) { + g_simple_async_result_take_error (ctx->result, error); + firmware_change_current_context_complete_and_free (ctx); + return; + } + + if (!qmi_message_dms_set_firmware_preference_output_get_result (output, &error)) { + g_simple_async_result_take_error (ctx->result, error); + firmware_change_current_context_complete_and_free (ctx); + qmi_message_dms_set_firmware_preference_output_unref (output); + return; + } + + qmi_message_dms_set_firmware_preference_output_unref (output); + + /* Now, go into offline mode */ + input = qmi_message_dms_set_operating_mode_input_new (); + qmi_message_dms_set_operating_mode_input_set_mode (input, QMI_DMS_OPERATING_MODE_OFFLINE, NULL); + qmi_client_dms_set_operating_mode (ctx->client, + input, + 20, + NULL, + (GAsyncReadyCallback)firmware_set_operating_mode_offline_ready, + ctx); + qmi_message_dms_set_operating_mode_input_unref (input); +} + +static MMFirmwareProperties * +find_firmware_properties_by_unique_id (MMBroadbandModemQmi *self, + const gchar *unique_id) +{ + GList *l; + + for (l = self->priv->firmware_list; l; l = g_list_next (l)) { + if (g_str_equal (mm_firmware_properties_get_unique_id (MM_FIRMWARE_PROPERTIES (l->data)), + unique_id)) + return g_object_ref (l->data); + } + + return NULL; +} + +static void +firmware_change_current (MMIfaceModemFirmware *self, + const gchar *unique_id, + GAsyncReadyCallback callback, + gpointer user_data) +{ + QmiMessageDmsSetFirmwarePreferenceInput *input; + FirmwareChangeCurrentContext *ctx; + QmiClient *client = NULL; + GArray *array; + QmiMessageDmsSetFirmwarePreferenceInputListImage modem_image_id; + QmiMessageDmsSetFirmwarePreferenceInputListImage pri_image_id; + guint8 *tmp; + gsize tmp_len; + + if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self), + QMI_SERVICE_DMS, &client, + callback, user_data)) + return; + + ctx = g_slice_new0 (FirmwareChangeCurrentContext); + ctx->self = g_object_ref (self); + ctx->client = g_object_ref (client); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + firmware_change_current); + + /* If we're already in the requested firmware, we're done */ + if (ctx->self->priv->current_firmware && + g_str_equal (mm_firmware_properties_get_unique_id (ctx->self->priv->current_firmware), + unique_id)) { + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + firmware_change_current_context_complete_and_free (ctx); + return; + } + + /* Look for the firmware image with the requested unique ID */ + ctx->firmware = find_firmware_properties_by_unique_id (ctx->self, unique_id); + if (!ctx->firmware) { + g_simple_async_result_set_error (ctx->result, + MM_CORE_ERROR, + MM_CORE_ERROR_NOT_FOUND, + "Firmware with unique ID '%s' wasn't found", + unique_id); + firmware_change_current_context_complete_and_free (ctx); + return; + } + + /* Modem image ID */ + tmp_len = 0; + tmp = (guint8 *)mm_utils_hexstr2bin (mm_firmware_properties_get_gobi_modem_unique_id (ctx->firmware), &tmp_len); + modem_image_id.type = QMI_DMS_FIRMWARE_IMAGE_TYPE_MODEM; + modem_image_id.build_id = (gchar *)mm_firmware_properties_get_unique_id (ctx->firmware); + modem_image_id.unique_id = g_array_sized_new (FALSE, FALSE, sizeof (guint8), tmp_len); + g_array_insert_vals (modem_image_id.unique_id, 0, tmp, tmp_len); + g_free (tmp); + + /* PRI image ID */ + tmp_len = 0; + tmp = (guint8 *)mm_utils_hexstr2bin (mm_firmware_properties_get_gobi_pri_unique_id (ctx->firmware), &tmp_len); + pri_image_id.type = QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI; + pri_image_id.build_id = (gchar *)mm_firmware_properties_get_unique_id (ctx->firmware); + pri_image_id.unique_id = g_array_sized_new (FALSE, FALSE, sizeof (guint8), tmp_len); + g_array_insert_vals (pri_image_id.unique_id, 0, tmp, tmp_len); + g_free (tmp); + + mm_dbg ("Changing Gobi firmware to MODEM '%s' and PRI '%s' with Build ID '%s'...", + mm_firmware_properties_get_gobi_modem_unique_id (ctx->firmware), + mm_firmware_properties_get_gobi_pri_unique_id (ctx->firmware), + unique_id); + + /* Build array of image IDs */ + array = g_array_sized_new (FALSE, FALSE, sizeof (QmiMessageDmsSetFirmwarePreferenceInputListImage), 2); + g_array_append_val (array, modem_image_id); + g_array_append_val (array, pri_image_id); + + input = qmi_message_dms_set_firmware_preference_input_new (); + qmi_message_dms_set_firmware_preference_input_set_list (input, array, NULL); + qmi_client_dms_set_firmware_preference ( + ctx->client, + input, + 10, + NULL, + (GAsyncReadyCallback)firmware_select_stored_image_ready, + ctx); + g_array_unref (modem_image_id.unique_id); + g_array_unref (pri_image_id.unique_id); + qmi_message_dms_set_firmware_preference_input_unref (input); +} + /*****************************************************************************/ /* First initialization step */ @@ -6854,6 +7088,8 @@ iface_modem_firmware_init (MMIfaceModemFirmware *iface) iface->load_list_finish = firmware_load_list_finish; iface->load_current = firmware_load_current; iface->load_current_finish = firmware_load_current_finish; + iface->change_current = firmware_change_current; + iface->change_current_finish = firmware_change_current_finish; } static void -- cgit v1.2.3-70-g09d2