aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@lanedo.com>2012-10-05 10:10:50 +0200
committerAleksander Morgado <aleksander@lanedo.com>2012-10-05 10:10:50 +0200
commitd4b6b8dd8c7bf654eaebb83d6cf08f676eadf57e (patch)
tree89d33f56b29fd8c7dba55d2ea0b4ed4400c2db78 /src
parent3cf4f3cc1d6713dd251978b1ad497fa506a5ba7d (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.c236
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