diff options
author | Aleksander Morgado <aleksander@lanedo.com> | 2012-10-05 10:10:50 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2012-10-05 10:10:50 +0200 |
commit | d4b6b8dd8c7bf654eaebb83d6cf08f676eadf57e (patch) | |
tree | 89d33f56b29fd8c7dba55d2ea0b4ed4400c2db78 /src | |
parent | 3cf4f3cc1d6713dd251978b1ad497fa506a5ba7d (diff) |
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-broadband-modem-qmi.c | 236 |
1 files changed, 236 insertions, 0 deletions
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 @@ -6455,6 +6455,240 @@ firmware_load_current (MMIfaceModemFirmware *_self, } /*****************************************************************************/ +/* 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 */ typedef struct { @@ -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 |