mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-05 19:48:47 +00:00
modem-manager: implement qmi pdc active config selection as attach()
When we install the MCFG carrier config files with QMI PDC, we were not explicitly selecting one, and that would end up reporting the "DF" (default) config is in use. Instead, we should explicitly select the carrier configuration that we were using before the firmware upgrade operation. For example, if the device originally was running with the Vodafone specific carrier configuration (e.g. T77W968.F1.0.0.3.7.VF.009) and we trigger the upgrade to the next available firmware associated to the Vodafone carrier (e.g. T77W968.F1.0.0.3.8.VF.009), we would want the device to boot with the Vodafone carrier config selected, instead of booting without any config selected (T77W968.F1.0.0.3.8.DF.009). This also fixes several upgrade problems detected by fwupd, because it may end up complaining that the target firmware that was selected to be installed (e.g. VF variant) is not the one actually reported by the device after the upgrade (e.g. DF variant). The selection of which is the config to activate is based on mapping the mcfg file name with the firmware version reported by the module before the upgrade. E.g. if the VF variant is reported by the module (T77W968.F1.0.0.3.7.VF.009), fwupd will look for a MCFG file named with the "mcfg.VF." prefix.
This commit is contained in:
parent
6c0e9ce6b4
commit
ffb8fd5edc
@ -44,8 +44,17 @@ struct _FuMmDevice {
|
||||
/* qmi-pdc update logic */
|
||||
gchar *port_qmi;
|
||||
FuQmiPdcUpdater *qmi_pdc_updater;
|
||||
GArray *qmi_pdc_active_id;
|
||||
guint attach_idle;
|
||||
};
|
||||
|
||||
enum {
|
||||
SIGNAL_ATTACH_FINISHED,
|
||||
SIGNAL_LAST
|
||||
};
|
||||
|
||||
static guint signals [SIGNAL_LAST] = { 0 };
|
||||
|
||||
G_DEFINE_TYPE (FuMmDevice, fu_mm_device, FU_TYPE_DEVICE)
|
||||
|
||||
static void
|
||||
@ -422,13 +431,16 @@ fu_mm_device_detach (FuDevice *device, GError **error)
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
gchar *filename;
|
||||
GBytes *bytes;
|
||||
gchar *filename;
|
||||
GBytes *bytes;
|
||||
GArray *digest;
|
||||
gboolean active;
|
||||
} FuMmFileInfo;
|
||||
|
||||
static void
|
||||
fu_mm_file_info_free (FuMmFileInfo *file_info)
|
||||
{
|
||||
g_clear_pointer (&file_info->digest, g_array_unref);
|
||||
g_free (file_info->filename);
|
||||
g_bytes_unref (file_info->bytes);
|
||||
g_free (file_info);
|
||||
@ -442,6 +454,34 @@ typedef struct {
|
||||
gsize total_bytes;
|
||||
} FuMmArchiveIterateCtx;
|
||||
|
||||
static gboolean
|
||||
fu_mm_should_be_active (const gchar *version,
|
||||
const gchar *filename)
|
||||
{
|
||||
g_auto(GStrv) split = NULL;
|
||||
g_autofree gchar *carrier_id = NULL;
|
||||
|
||||
/* The filename of the mcfg file is composed of a "mcfg." prefix, then the
|
||||
* carrier code, followed by the carrier version, and finally a ".mbn"
|
||||
* prefix. Here we try to guess, based on the carrier code, whether the
|
||||
* specific mcfg file should be activated after the firmware upgrade
|
||||
* operation.
|
||||
*
|
||||
* This logic requires that the previous device version includes the carrier
|
||||
* code also embedded in the version string. E.g. "xxxx.VF.xxxx". If we find
|
||||
* this match, we assume this is the active config to use.
|
||||
*/
|
||||
|
||||
split = g_strsplit (filename, ".", -1);
|
||||
if (g_strv_length (split) < 4)
|
||||
return FALSE;
|
||||
if (g_strcmp0 (split[0], "mcfg") != 0)
|
||||
return FALSE;
|
||||
|
||||
carrier_id = g_strdup_printf (".%s.", split[1]);
|
||||
return (g_strstr_len (version, -1, carrier_id) != NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_mm_qmi_pdc_archive_iterate_mcfg (FuArchive *archive,
|
||||
const gchar *filename,
|
||||
@ -458,6 +498,7 @@ fu_mm_qmi_pdc_archive_iterate_mcfg (FuArchive *archive,
|
||||
file_info = g_new0 (FuMmFileInfo, 1);
|
||||
file_info->filename = g_strdup (filename);
|
||||
file_info->bytes = g_bytes_ref (bytes);
|
||||
file_info->active = fu_mm_should_be_active (fu_device_get_version (FU_DEVICE (ctx->device)), filename);
|
||||
g_ptr_array_add (ctx->file_infos, file_info);
|
||||
ctx->total_bytes += g_bytes_get_size (file_info->bytes);
|
||||
}
|
||||
@ -479,11 +520,22 @@ fu_mm_device_qmi_close (FuMmDevice *self, GError **error)
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_mm_device_write_firmware_qmi_pdc (FuDevice *device, GBytes *fw, GError **error)
|
||||
fu_mm_device_qmi_close_no_error (FuMmDevice *self, GError **error)
|
||||
{
|
||||
g_autoptr(FuQmiPdcUpdater) updater = NULL;
|
||||
|
||||
updater = g_steal_pointer (&self->qmi_pdc_updater);
|
||||
fu_qmi_pdc_updater_close (updater, NULL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_mm_device_write_firmware_qmi_pdc (FuDevice *device, GBytes *fw, GArray **active_id, GError **error)
|
||||
{
|
||||
g_autoptr(FuArchive) archive = NULL;
|
||||
g_autoptr(FuDeviceLocker) locker = NULL;
|
||||
g_autoptr(GPtrArray) file_infos = g_ptr_array_new_with_free_func ((GDestroyNotify)fu_mm_file_info_free);
|
||||
gint active_i = -1;
|
||||
FuMmArchiveIterateCtx archive_context = {
|
||||
.device = FU_MM_DEVICE (device),
|
||||
.error = NULL,
|
||||
@ -510,14 +562,26 @@ fu_mm_device_write_firmware_qmi_pdc (FuDevice *device, GBytes *fw, GError **erro
|
||||
|
||||
for (guint i = 0; i < file_infos->len; i++) {
|
||||
FuMmFileInfo *file_info = g_ptr_array_index (file_infos, i);
|
||||
if (!fu_qmi_pdc_updater_write (archive_context.device->qmi_pdc_updater,
|
||||
file_info->filename,
|
||||
file_info->bytes,
|
||||
&archive_context.error)) {
|
||||
file_info->digest = fu_qmi_pdc_updater_write (archive_context.device->qmi_pdc_updater,
|
||||
file_info->filename,
|
||||
file_info->bytes,
|
||||
&archive_context.error);
|
||||
if (file_info->digest == NULL) {
|
||||
g_prefix_error (&archive_context.error,
|
||||
"Failed to write file '%s':", file_info->filename);
|
||||
break;
|
||||
}
|
||||
/* if we wrongly detect more than one, just assume the latest one; this
|
||||
* is not critical, it may just take a bit more time to perform the
|
||||
* automatic carrier config switching in ModemManager */
|
||||
if (file_info->active)
|
||||
active_i = i;
|
||||
}
|
||||
|
||||
/* set expected active configuration */
|
||||
if (active_i >= 0 && active_id != NULL) {
|
||||
FuMmFileInfo *file_info = g_ptr_array_index (file_infos, active_i);
|
||||
*active_id = g_array_ref (file_info->digest);
|
||||
}
|
||||
|
||||
if (archive_context.error != NULL) {
|
||||
@ -543,16 +607,65 @@ fu_mm_device_write_firmware (FuDevice *device, GBytes *fw, GError **error)
|
||||
|
||||
/* qmi pdc write operation */
|
||||
if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC)
|
||||
return fu_mm_device_write_firmware_qmi_pdc (device, fw, error);
|
||||
return fu_mm_device_write_firmware_qmi_pdc (device, fw, &self->qmi_pdc_active_id, error);
|
||||
|
||||
g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED,
|
||||
"unsupported update method");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_mm_device_attach_qmi_pdc (FuMmDevice *self, GError **error)
|
||||
{
|
||||
g_autoptr(FuDeviceLocker) locker = NULL;
|
||||
|
||||
/* ignore action if there is no active id specified */
|
||||
if (self->qmi_pdc_active_id == NULL)
|
||||
return TRUE;
|
||||
|
||||
/* errors closing may be expected if the device really reboots itself */
|
||||
locker = fu_device_locker_new_full (self,
|
||||
(FuDeviceLockerFunc) fu_mm_device_qmi_open,
|
||||
(FuDeviceLockerFunc) fu_mm_device_qmi_close_no_error,
|
||||
error);
|
||||
if (locker == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (!fu_qmi_pdc_updater_activate (self->qmi_pdc_updater, self->qmi_pdc_active_id, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_mm_device_attach_noop_idle (gpointer user_data)
|
||||
{
|
||||
FuMmDevice *self = FU_MM_DEVICE (user_data);
|
||||
self->attach_idle = 0;
|
||||
g_signal_emit (self, signals [SIGNAL_ATTACH_FINISHED], 0);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_mm_device_attach_qmi_pdc_idle (gpointer user_data)
|
||||
{
|
||||
FuMmDevice *self = FU_MM_DEVICE (user_data);
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
if (!fu_mm_device_attach_qmi_pdc (self, &error))
|
||||
g_warning ("qmi-pdc attach operation failed: %s", error->message);
|
||||
else
|
||||
g_debug ("qmi-pdc attach operation successful");
|
||||
|
||||
self->attach_idle = 0;
|
||||
g_signal_emit (self, signals [SIGNAL_ATTACH_FINISHED], 0);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_mm_device_attach (FuDevice *device, GError **error)
|
||||
{
|
||||
FuMmDevice *self = FU_MM_DEVICE (device);
|
||||
g_autoptr(FuDeviceLocker) locker = NULL;
|
||||
|
||||
/* lock device */
|
||||
@ -560,6 +673,13 @@ fu_mm_device_attach (FuDevice *device, GError **error)
|
||||
if (locker == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* we want this attach operation to be triggered asynchronously, because the engine
|
||||
* must learn that it has to wait for replug before we actually trigger the reset. */
|
||||
if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC)
|
||||
self->attach_idle = g_idle_add ((GSourceFunc) fu_mm_device_attach_qmi_pdc_idle, self);
|
||||
else
|
||||
self->attach_idle = g_idle_add ((GSourceFunc) fu_mm_device_attach_noop_idle, self);
|
||||
|
||||
/* wait for re-probing after uninhibiting */
|
||||
fu_device_set_remove_delay (device, FU_MM_DEVICE_REMOVE_DELAY_REPROBE);
|
||||
fu_device_add_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
|
||||
@ -579,6 +699,10 @@ static void
|
||||
fu_mm_device_finalize (GObject *object)
|
||||
{
|
||||
FuMmDevice *self = FU_MM_DEVICE (object);
|
||||
if (self->attach_idle)
|
||||
g_source_remove (self->attach_idle);
|
||||
if (self->qmi_pdc_active_id)
|
||||
g_array_unref (self->qmi_pdc_active_id);
|
||||
g_object_unref (self->manager);
|
||||
if (self->omodem != NULL)
|
||||
g_object_unref (self->omodem);
|
||||
@ -600,6 +724,12 @@ fu_mm_device_class_init (FuMmDeviceClass *klass)
|
||||
klass_device->detach = fu_mm_device_detach;
|
||||
klass_device->write_firmware = fu_mm_device_write_firmware;
|
||||
klass_device->attach = fu_mm_device_attach;
|
||||
|
||||
signals [SIGNAL_ATTACH_FINISHED] =
|
||||
g_signal_new ("attach-finished",
|
||||
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
|
||||
0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
FuMmDevice *
|
||||
|
@ -396,12 +396,11 @@ fu_plugin_update (FuPlugin *plugin,
|
||||
return fu_device_write_firmware (device, blob_fw, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_plugin_mm_uninhibit_device_idle (gpointer user_data)
|
||||
static void
|
||||
fu_plugin_mm_device_attach_finished (gpointer user_data)
|
||||
{
|
||||
FuPlugin *plugin = FU_PLUGIN (user_data);
|
||||
fu_plugin_mm_uninhibit_device (plugin);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
@ -414,10 +413,16 @@ fu_plugin_update_attach (FuPlugin *plugin, FuDevice *device, GError **error)
|
||||
if (locker == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* must be done in idle so that the engine explicitly
|
||||
* waits for the device to be redetected */
|
||||
g_idle_add (fu_plugin_mm_uninhibit_device_idle, plugin);
|
||||
/* schedule device attach asynchronously, which is extremely important
|
||||
* so that engine can setup the device "waiting" logic before the actual
|
||||
* attach procedure happens (which will reset the module if it worked
|
||||
* properly) */
|
||||
if (!fu_device_attach (device, error))
|
||||
return FALSE;
|
||||
|
||||
/* reset */
|
||||
return fu_device_attach (FU_DEVICE (device), error);
|
||||
/* this signal will always be emitted asynchronously */
|
||||
g_signal_connect_swapped (device, "attach-finished",
|
||||
G_CALLBACK (fu_plugin_mm_device_attach_finished), plugin);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -390,21 +390,264 @@ fu_qmi_pdc_updater_get_checksum (GBytes *blob)
|
||||
return digest;
|
||||
}
|
||||
|
||||
gboolean
|
||||
GArray *
|
||||
fu_qmi_pdc_updater_write (FuQmiPdcUpdater *self, const gchar *filename, GBytes *blob, GError **error)
|
||||
{
|
||||
g_autoptr(GMainLoop) mainloop = g_main_loop_new (NULL, FALSE);
|
||||
g_autoptr(GArray) digest = fu_qmi_pdc_updater_get_checksum (blob);
|
||||
WriteContext ctx = {
|
||||
.mainloop = mainloop,
|
||||
.qmi_client = self->qmi_client,
|
||||
.blob = blob,
|
||||
.digest = fu_qmi_pdc_updater_get_checksum (blob),
|
||||
.error = NULL,
|
||||
.indication_id = 0,
|
||||
.timeout_id = 0,
|
||||
.blob = blob,
|
||||
.digest = digest,
|
||||
.offset = 0,
|
||||
.token = 0,
|
||||
};
|
||||
|
||||
fu_qmi_pdc_updater_load_config (&ctx);
|
||||
g_main_loop_run (mainloop);
|
||||
|
||||
if (ctx.error != NULL) {
|
||||
g_propagate_error (error, ctx.error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer (&digest);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GMainLoop *mainloop;
|
||||
QmiClientPdc *qmi_client;
|
||||
GError *error;
|
||||
gulong indication_id;
|
||||
guint timeout_id;
|
||||
GArray *digest;
|
||||
guint token;
|
||||
} ActivateContext;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(QmiMessagePdcActivateConfigInput, qmi_message_pdc_activate_config_input_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(QmiMessagePdcActivateConfigOutput, qmi_message_pdc_activate_config_output_unref)
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
static gboolean
|
||||
fu_qmi_pdc_updater_activate_config_timeout (gpointer user_data)
|
||||
{
|
||||
ActivateContext *ctx = user_data;
|
||||
|
||||
ctx->timeout_id = 0;
|
||||
g_signal_handler_disconnect (ctx->qmi_client, ctx->indication_id);
|
||||
ctx->indication_id = 0;
|
||||
|
||||
/* not an error, the device may go away without sending the indication */
|
||||
g_main_loop_quit (ctx->mainloop);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
fu_qmi_pdc_updater_activate_config_indication (QmiClientPdc *client,
|
||||
QmiIndicationPdcActivateConfigOutput *output,
|
||||
ActivateContext *ctx)
|
||||
{
|
||||
guint16 error_code = 0;
|
||||
|
||||
g_source_remove (ctx->timeout_id);
|
||||
ctx->timeout_id = 0;
|
||||
g_signal_handler_disconnect (ctx->qmi_client, ctx->indication_id);
|
||||
ctx->indication_id = 0;
|
||||
|
||||
if (!qmi_indication_pdc_activate_config_output_get_indication_result (output, &error_code, &ctx->error)) {
|
||||
g_main_loop_quit (ctx->mainloop);
|
||||
return;
|
||||
}
|
||||
|
||||
if (error_code != 0) {
|
||||
g_set_error (&ctx->error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"couldn't activate config: %s", qmi_protocol_error_get_string ((QmiProtocolError) error_code));
|
||||
g_main_loop_quit (ctx->mainloop);
|
||||
return;
|
||||
}
|
||||
|
||||
/* assume ok */
|
||||
g_debug ("successful activate configuration indication: assuming device reset is ongoing");
|
||||
g_main_loop_quit (ctx->mainloop);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_qmi_pdc_updater_activate_config_ready (GObject *qmi_client, GAsyncResult *res, gpointer user_data)
|
||||
{
|
||||
ActivateContext *ctx = (ActivateContext *) user_data;
|
||||
g_autoptr(QmiMessagePdcActivateConfigOutput) output = NULL;
|
||||
|
||||
output = qmi_client_pdc_activate_config_finish (QMI_CLIENT_PDC (qmi_client), res, &ctx->error);
|
||||
if (output == NULL) {
|
||||
/* If we didn't receive a response, this is a good indication that the device
|
||||
* reseted itself, we can consider this a successful operation.
|
||||
* Note: not using g_error_matches() to avoid matching the domain, because the
|
||||
* error may be either QMI_CORE_ERROR_TIMEOUT or MBIM_CORE_ERROR_TIMEOUT (same
|
||||
* numeric value), and we don't want to build-depend on libmbim just for this.
|
||||
*/
|
||||
if (ctx->error->code == QMI_CORE_ERROR_TIMEOUT) {
|
||||
g_debug ("request to activate configuration timed out: assuming device reset is ongoing");
|
||||
g_clear_error (&ctx->error);
|
||||
}
|
||||
g_main_loop_quit (ctx->mainloop);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!qmi_message_pdc_activate_config_output_get_result (output, &ctx->error)) {
|
||||
g_main_loop_quit (ctx->mainloop);
|
||||
return;
|
||||
}
|
||||
|
||||
/* When we activate the config, if the operation is successful, we'll just
|
||||
* see the modem going away completely. So, do not consider an error the timeout
|
||||
* waiting for the Activate Config indication, as that is actually a good
|
||||
* thing.
|
||||
*/
|
||||
g_warn_if_fail (ctx->indication_id == 0);
|
||||
ctx->indication_id = g_signal_connect (ctx->qmi_client, "activate-config",
|
||||
G_CALLBACK (fu_qmi_pdc_updater_activate_config_indication), ctx);
|
||||
|
||||
/* don't wait forever */
|
||||
g_warn_if_fail (ctx->timeout_id == 0);
|
||||
ctx->timeout_id = g_timeout_add_seconds (5, fu_qmi_pdc_updater_activate_config_timeout, ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_qmi_pdc_updater_activate_config (ActivateContext *ctx)
|
||||
{
|
||||
g_autoptr(QmiMessagePdcActivateConfigInput) input = NULL;
|
||||
|
||||
input = qmi_message_pdc_activate_config_input_new ();
|
||||
qmi_message_pdc_activate_config_input_set_config_type (input, QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, NULL);
|
||||
qmi_message_pdc_activate_config_input_set_token (input, ctx->token++, NULL);
|
||||
|
||||
g_debug ("activating selected configuration...");
|
||||
qmi_client_pdc_activate_config (ctx->qmi_client, input, 5, NULL,
|
||||
fu_qmi_pdc_updater_activate_config_ready, ctx);
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(QmiMessagePdcSetSelectedConfigInput, qmi_message_pdc_set_selected_config_input_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(QmiMessagePdcSetSelectedConfigOutput, qmi_message_pdc_set_selected_config_output_unref)
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
static gboolean
|
||||
fu_qmi_pdc_updater_set_selected_config_timeout (gpointer user_data)
|
||||
{
|
||||
ActivateContext *ctx = user_data;
|
||||
|
||||
ctx->timeout_id = 0;
|
||||
g_signal_handler_disconnect (ctx->qmi_client, ctx->indication_id);
|
||||
ctx->indication_id = 0;
|
||||
|
||||
g_set_error_literal (&ctx->error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"couldn't set selected config: timed out");
|
||||
g_main_loop_quit (ctx->mainloop);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
fu_qmi_pdc_updater_set_selected_config_indication (QmiClientPdc *client,
|
||||
QmiIndicationPdcSetSelectedConfigOutput *output,
|
||||
ActivateContext *ctx)
|
||||
{
|
||||
guint16 error_code = 0;
|
||||
|
||||
g_source_remove (ctx->timeout_id);
|
||||
ctx->timeout_id = 0;
|
||||
g_signal_handler_disconnect (ctx->qmi_client, ctx->indication_id);
|
||||
ctx->indication_id = 0;
|
||||
|
||||
if (!qmi_indication_pdc_set_selected_config_output_get_indication_result (output, &error_code, &ctx->error)) {
|
||||
g_main_loop_quit (ctx->mainloop);
|
||||
return;
|
||||
}
|
||||
|
||||
if (error_code != 0) {
|
||||
g_set_error (&ctx->error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"couldn't set selected config: %s", qmi_protocol_error_get_string ((QmiProtocolError) error_code));
|
||||
g_main_loop_quit (ctx->mainloop);
|
||||
return;
|
||||
}
|
||||
|
||||
g_debug ("current configuration successfully selected...");
|
||||
|
||||
/* now activate config */
|
||||
fu_qmi_pdc_updater_activate_config (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_qmi_pdc_updater_set_selected_config_ready (GObject *qmi_client, GAsyncResult *res, gpointer user_data)
|
||||
{
|
||||
ActivateContext *ctx = (ActivateContext *) user_data;
|
||||
g_autoptr(QmiMessagePdcSetSelectedConfigOutput) output = NULL;
|
||||
|
||||
output = qmi_client_pdc_set_selected_config_finish (QMI_CLIENT_PDC (qmi_client), res, &ctx->error);
|
||||
if (output == NULL) {
|
||||
g_main_loop_quit (ctx->mainloop);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!qmi_message_pdc_set_selected_config_output_get_result (output, &ctx->error)) {
|
||||
g_main_loop_quit (ctx->mainloop);
|
||||
return;
|
||||
}
|
||||
|
||||
/* after receiving the response to our request, we now expect an indication
|
||||
* with the actual result of the operation */
|
||||
g_warn_if_fail (ctx->indication_id == 0);
|
||||
ctx->indication_id = g_signal_connect (ctx->qmi_client, "set-selected-config",
|
||||
G_CALLBACK (fu_qmi_pdc_updater_set_selected_config_indication), ctx);
|
||||
|
||||
/* don't wait forever */
|
||||
g_warn_if_fail (ctx->timeout_id == 0);
|
||||
ctx->timeout_id = g_timeout_add_seconds (5, fu_qmi_pdc_updater_set_selected_config_timeout, ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_qmi_pdc_updater_set_selected_config (ActivateContext *ctx)
|
||||
{
|
||||
g_autoptr(QmiMessagePdcSetSelectedConfigInput) input = NULL;
|
||||
QmiConfigTypeAndId type_and_id;
|
||||
|
||||
type_and_id.config_type = QMI_PDC_CONFIGURATION_TYPE_SOFTWARE;
|
||||
type_and_id.id = ctx->digest;
|
||||
|
||||
input = qmi_message_pdc_set_selected_config_input_new ();
|
||||
qmi_message_pdc_set_selected_config_input_set_type_with_id (input, &type_and_id, NULL);
|
||||
qmi_message_pdc_set_selected_config_input_set_token (input, ctx->token++, NULL);
|
||||
|
||||
g_debug ("selecting current configuration...");
|
||||
qmi_client_pdc_set_selected_config (ctx->qmi_client, input, 10, NULL,
|
||||
fu_qmi_pdc_updater_set_selected_config_ready, ctx);
|
||||
}
|
||||
|
||||
gboolean
|
||||
fu_qmi_pdc_updater_activate (FuQmiPdcUpdater *self, GArray *digest, GError **error)
|
||||
{
|
||||
g_autoptr(GMainLoop) mainloop = g_main_loop_new (NULL, FALSE);
|
||||
ActivateContext ctx = {
|
||||
.mainloop = mainloop,
|
||||
.qmi_client = self->qmi_client,
|
||||
.error = NULL,
|
||||
.indication_id = 0,
|
||||
.timeout_id = 0,
|
||||
.digest = digest,
|
||||
.token = 0,
|
||||
};
|
||||
|
||||
fu_qmi_pdc_updater_set_selected_config (&ctx);
|
||||
g_main_loop_run (mainloop);
|
||||
|
||||
if (ctx.error != NULL) {
|
||||
g_propagate_error (error, ctx.error);
|
||||
return FALSE;
|
||||
|
@ -15,13 +15,16 @@ G_BEGIN_DECLS
|
||||
G_DECLARE_FINAL_TYPE (FuQmiPdcUpdater, fu_qmi_pdc_updater, FU, QMI_PDC_UPDATER, GObject)
|
||||
|
||||
FuQmiPdcUpdater *fu_qmi_pdc_updater_new (const gchar *qmi_port);
|
||||
gboolean fu_qmi_pdc_updater_open (FuQmiPdcUpdater *self,
|
||||
gboolean fu_qmi_pdc_updater_open (FuQmiPdcUpdater *self,
|
||||
GError **error);
|
||||
gboolean fu_qmi_pdc_updater_write (FuQmiPdcUpdater *self,
|
||||
GArray *fu_qmi_pdc_updater_write (FuQmiPdcUpdater *self,
|
||||
const gchar *filename,
|
||||
GBytes *blob,
|
||||
GError **error);
|
||||
gboolean fu_qmi_pdc_updater_close (FuQmiPdcUpdater *self,
|
||||
gboolean fu_qmi_pdc_updater_activate (FuQmiPdcUpdater *self,
|
||||
GArray *digest,
|
||||
GError **error);
|
||||
gboolean fu_qmi_pdc_updater_close (FuQmiPdcUpdater *self,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
Loading…
Reference in New Issue
Block a user