diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2017-01-13 20:35:49 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2017-01-16 11:24:16 +0100 |
commit | 86293bebe23d4e43b8ea87a5235b57fd5ea3eaee (patch) | |
tree | 67fea1a6c393a4068afbab33d1f0a8a1c99124e1 | |
parent | 3038951fe085514f6fa4872d0b578756fe22d492 (diff) | |
download | external_libqmi-86293bebe23d4e43b8ea87a5235b57fd5ea3eaee.zip external_libqmi-86293bebe23d4e43b8ea87a5235b57fd5ea3eaee.tar.gz external_libqmi-86293bebe23d4e43b8ea87a5235b57fd5ea3eaee.tar.bz2 |
qmi-firmware-update: prefer DMS 0x003e for boot & hold reset
-rw-r--r-- | src/qmi-firmware-update/qfu-main.c | 11 | ||||
-rw-r--r-- | src/qmi-firmware-update/qfu-operation-reset.c | 6 | ||||
-rw-r--r-- | src/qmi-firmware-update/qfu-operation.h | 4 | ||||
-rw-r--r-- | src/qmi-firmware-update/qfu-reseter.c | 208 | ||||
-rw-r--r-- | src/qmi-firmware-update/qfu-reseter.h | 6 | ||||
-rw-r--r-- | src/qmi-firmware-update/qfu-updater.c | 2 |
6 files changed, 188 insertions, 49 deletions
diff --git a/src/qmi-firmware-update/qfu-main.c b/src/qmi-firmware-update/qfu-main.c index df6733d..efbf2d0 100644 --- a/src/qmi-firmware-update/qfu-main.c +++ b/src/qmi-firmware-update/qfu-main.c @@ -379,11 +379,8 @@ print_help_examples (void) " The MC7700 is a 9200 device which doesn't require the explicit firmware, config\n" " and carrier strings. Unlike the MC7354, which would reboot itself into QDL\n" " download mode once these previous strings were configured, the MC7700 requires\n" - " an AT command to be sent in a TTY port to request the reset in QDL download\n" - " mode.\n" - "\n" - " The user doesn't need to explicitly specify the path to the TTY, though, it will\n" - " be automatically detected and processed during the firmware update process.\n" + " a specific \"boot and hold\" command to be sent (either via QMI or AT) to request\n" + " the reset in QDL download mode.\n" "\n" " 2a) An update operation specifying the vid:pid of the device (fails if multiple\n" " devices with the same vid:pid are found):\n" @@ -562,7 +559,9 @@ int main (int argc, char **argv) if (action_reset_flag) { g_assert (QFU_IS_DEVICE_SELECTION (device_selection)); - result = qfu_operation_reset_run (device_selection); + result = qfu_operation_reset_run (device_selection, + device_open_proxy_flag, + device_open_mbim_flag); goto out; } diff --git a/src/qmi-firmware-update/qfu-operation-reset.c b/src/qmi-firmware-update/qfu-operation-reset.c index 402eb1d..65b5e21 100644 --- a/src/qmi-firmware-update/qfu-operation-reset.c +++ b/src/qmi-firmware-update/qfu-operation-reset.c @@ -105,12 +105,14 @@ operation_reseter_run (QfuReseter *reseter) } gboolean -qfu_operation_reset_run (QfuDeviceSelection *device_selection) +qfu_operation_reset_run (QfuDeviceSelection *device_selection, + gboolean device_open_proxy, + gboolean device_open_mbim) { QfuReseter *reseter = NULL; gboolean result; - reseter = qfu_reseter_new (device_selection); + reseter = qfu_reseter_new (device_selection, NULL, device_open_proxy, device_open_mbim); result = operation_reseter_run (reseter); g_object_unref (reseter); return result; diff --git a/src/qmi-firmware-update/qfu-operation.h b/src/qmi-firmware-update/qfu-operation.h index b9d40ba..683e6d5 100644 --- a/src/qmi-firmware-update/qfu-operation.h +++ b/src/qmi-firmware-update/qfu-operation.h @@ -38,7 +38,9 @@ gboolean qfu_operation_update_run (const gchar **images, gboolean qfu_operation_update_qdl_run (const gchar **images, QfuDeviceSelection *device_selection); gboolean qfu_operation_verify_run (const gchar **images); -gboolean qfu_operation_reset_run (QfuDeviceSelection *device_selection); +gboolean qfu_operation_reset_run (QfuDeviceSelection *device_selection, + gboolean device_open_proxy, + gboolean device_open_mbim); G_END_DECLS diff --git a/src/qmi-firmware-update/qfu-reseter.c b/src/qmi-firmware-update/qfu-reseter.c index 55d6e2a..b37ff2d 100644 --- a/src/qmi-firmware-update/qfu-reseter.c +++ b/src/qmi-firmware-update/qfu-reseter.c @@ -29,11 +29,15 @@ #include "qfu-reseter.h" #include "qfu-at-device.h" +#include "qfu-utils.h" G_DEFINE_TYPE (QfuReseter, qfu_reseter, G_TYPE_OBJECT) struct _QfuReseterPrivate { QfuDeviceSelection *device_selection; + QmiClientDms *qmi_client; + gboolean device_open_proxy; + gboolean device_open_mbim; }; /******************************************************************************/ @@ -42,6 +46,12 @@ struct _QfuReseterPrivate { #define MAX_RETRIES 2 typedef struct { + /* Files to use */ + GList *ttys; + GFile *cdc_wdm; + /* QMI client amd device */ + QmiDevice *qmi_device; + QmiClientDms *qmi_client; /* List of AT devices */ GList *at_devices; GList *current; @@ -52,6 +62,21 @@ typedef struct { static void run_context_free (RunContext *ctx) { + if (ctx->cdc_wdm) + g_object_unref (ctx->cdc_wdm); + if (ctx->qmi_client) { + g_assert (ctx->qmi_device); + qmi_device_release_client (ctx->qmi_device, + QMI_CLIENT (ctx->qmi_client), + QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID, + 10, NULL, NULL, NULL); + g_object_unref (ctx->qmi_client); + } + if (ctx->qmi_device) { + qmi_device_close (ctx->qmi_device, NULL); + g_object_unref (ctx->qmi_device); + } + g_list_free_full (ctx->ttys, (GDestroyNotify) g_object_unref); g_list_free_full (ctx->at_devices, (GDestroyNotify) g_object_unref); g_slice_free (RunContext, ctx); } @@ -64,17 +89,17 @@ qfu_reseter_run_finish (QfuReseter *self, return g_task_propagate_boolean (G_TASK (res), error); } -static void run_context_step (GTask *task); +static void run_context_step_at (GTask *task); static gboolean -run_context_step_cb (GTask *task) +run_context_step_at_cb (GTask *task) { - run_context_step (task); + run_context_step_at (task); return FALSE; } static void -run_context_step_next (GTask *task) +run_context_step_at_next (GTask *task) { RunContext *ctx; @@ -84,7 +109,7 @@ run_context_step_next (GTask *task) if (!ctx->current) { if (!ctx->retries) { g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, - "couldn't run reseter operation"); + "couldn't run reset operation"); g_object_unref (task); return; } @@ -95,36 +120,136 @@ run_context_step_next (GTask *task) } /* Schedule next step in an idle */ - g_idle_add ((GSourceFunc) run_context_step_cb, task); + g_idle_add ((GSourceFunc) run_context_step_at_cb, task); +} + +static gint +device_sort_by_name_reversed (QfuAtDevice *a, QfuAtDevice *b) +{ + return strcmp (qfu_at_device_get_name (b), qfu_at_device_get_name (a)); } static void -run_context_step (GTask *task) +run_context_step_at (GTask *task) { RunContext *ctx; QfuAtDevice *at_device; GError *error = NULL; ctx = (RunContext *) g_task_get_task_data (task); - g_assert (ctx->current); + + /* If we get to AT reset after trying QMI, and we didn't find any port to + * use, return error */ + if (!ctx->ttys) { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, + "No devices found to run reset operation"); + g_object_unref (task); + return; + } + + /* Initialize AT devices the first time we get here */ + if (!ctx->at_devices) { + GList *l; + + for (l = ctx->ttys; l; l = g_list_next (l)) { + at_device = qfu_at_device_new (G_FILE (l->data), g_task_get_cancellable (task), &error); + if (!at_device) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + ctx->at_devices = g_list_append (ctx->at_devices, at_device); + } + + /* Sort by filename reversed; usually the TTY with biggest number is a + * good AT port */ + ctx->at_devices = g_list_sort (ctx->at_devices, (GCompareFunc) device_sort_by_name_reversed); + /* Select first TTY to start */ + ctx->current = ctx->at_devices; + } else + g_assert (ctx->current); at_device = QFU_AT_DEVICE (ctx->current->data); if (!qfu_at_device_boothold (at_device, g_task_get_cancellable (task), &error)) { - g_debug ("error: %s", error->message); + g_debug ("[qfu-reseter] error: %s", error->message); g_error_free (error); - run_context_step_next (task); + run_context_step_at_next (task); return; } /* Success! */ + g_debug ("[qfu-reseter] successfully run 'at boothold' operation"); g_task_return_boolean (task, TRUE); g_object_unref (task); } -static gint -device_sort_by_name_reversed (QfuAtDevice *a, QfuAtDevice *b) +static void +set_firmware_id_ready (QmiClientDms *client, + GAsyncResult *res, + GTask *task) { - return strcmp (qfu_at_device_get_name (b), qfu_at_device_get_name (a)); + QmiMessageDmsSetFirmwareIdOutput *output; + GError *error = NULL; + + output = qmi_client_dms_set_firmware_id_finish (client, res, &error); + if (!output || !qmi_message_dms_set_firmware_id_output_get_result (output, &error)) { + g_debug ("[qfu-reseter] error: couldn't run 'set firmware id' operation: %s", error->message); + g_error_free (error); + if (output) + qmi_message_dms_set_firmware_id_output_unref (output); + g_debug ("[qfu-reseter] skipping QMI-based boothold"); + run_context_step_at (task); + return; + } + + g_debug ("[qfu-reseter] successfully run 'set firmware id' operation"); + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void +run_context_step_qmi (GTask *task) +{ + RunContext *ctx; + QfuReseter *self; + + ctx = (RunContext *) g_task_get_task_data (task); + self = g_task_get_source_object (task); + + g_assert (ctx->qmi_client || self->priv->qmi_client); + + /* Run DMS 0x003e to power cycle in boot & hold mode */ + qmi_client_dms_set_firmware_id (self->priv->qmi_client ? self->priv->qmi_client : ctx->qmi_client, + NULL, + 10, + g_task_get_cancellable (task), + (GAsyncReadyCallback) set_firmware_id_ready, + task); +} + +static void +new_client_dms_ready (gpointer unused, + GAsyncResult *res, + GTask *task) +{ + RunContext *ctx; + GError *error = NULL; + + ctx = (RunContext *) g_task_get_task_data (task); + + if (!qfu_utils_new_client_dms_finish (res, + &ctx->qmi_device, + &ctx->qmi_client, + &error)) { + /* Jump to AT-based boothold */ + g_debug ("[qfu-reseter] error: couldn't allocate QMI client: %s", error->message); + g_error_free (error); + g_debug ("[qfu-reseter] skipping QMI-based boothold"); + run_context_step_at (task); + return; + } + + run_context_step_qmi (task); } void @@ -135,7 +260,6 @@ qfu_reseter_run (QfuReseter *self, { RunContext *ctx; GTask *task; - GList *l, *ttys; ctx = g_slice_new0 (RunContext); ctx->retries = MAX_RETRIES; @@ -143,48 +267,55 @@ qfu_reseter_run (QfuReseter *self, task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify) run_context_free); - /* List TTYs we may use for the reset operation */ - ttys = qfu_device_selection_get_multiple_ttys (self->priv->device_selection); - if (!ttys) { + /* List devices to use */ + if (!self->priv->qmi_client) + ctx->cdc_wdm = qfu_device_selection_get_single_cdc_wdm (self->priv->device_selection); + ctx->ttys = qfu_device_selection_get_multiple_ttys (self->priv->device_selection); + + if (!ctx->ttys && !ctx->cdc_wdm && !self->priv->qmi_client) { g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - "No TTYs found to run reset operation"); + "No devices found to run reset operation"); g_object_unref (task); return; } - /* Build QfuAtDevice objects for each TTY given */ - for (l = ttys; l; l = g_list_next (l)) { - GError *error = NULL; - QfuAtDevice *at_device; - - at_device = qfu_at_device_new (G_FILE (l->data), cancellable, &error); - if (!at_device) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - ctx->at_devices = g_list_append (ctx->at_devices, at_device); + /* If no cdc-wdm file available, try AT directly */ + if (!ctx->cdc_wdm && !self->priv->qmi_client) { + run_context_step_at (task); + return; } - g_list_free_full (ttys, (GDestroyNotify) g_object_unref); - /* Sort by filename reversed; usually the TTY with biggest number is a - * good AT port */ - ctx->at_devices = g_list_sort (ctx->at_devices, (GCompareFunc) device_sort_by_name_reversed); + /* If we already got a QMI client as input, try QMI directly */ + if (self->priv->qmi_client) { + run_context_step_qmi (task); + return; + } - /* Select first TTY and start */ - ctx->current = ctx->at_devices; - run_context_step (task); + /* Otherwise, try to allocate a QMI client */ + g_assert (ctx->cdc_wdm); + qfu_utils_new_client_dms (ctx->cdc_wdm, + self->priv->device_open_proxy, + self->priv->device_open_mbim, + cancellable, + (GAsyncReadyCallback) new_client_dms_ready, + task); } /******************************************************************************/ QfuReseter * -qfu_reseter_new (QfuDeviceSelection *device_selection) +qfu_reseter_new (QfuDeviceSelection *device_selection, + QmiClientDms *qmi_client, + gboolean device_open_proxy, + gboolean device_open_mbim) { QfuReseter *self; self = g_object_new (QFU_TYPE_RESETER, NULL); self->priv->device_selection = g_object_ref (device_selection); + self->priv->qmi_client = qmi_client ? g_object_ref (qmi_client) : NULL; + self->priv->device_open_proxy = device_open_proxy; + self->priv->device_open_mbim = device_open_mbim; return self; } @@ -201,6 +332,7 @@ dispose (GObject *object) QfuReseter *self = QFU_RESETER (object); g_clear_object (&self->priv->device_selection); + g_clear_object (&self->priv->qmi_client); G_OBJECT_CLASS (qfu_reseter_parent_class)->dispose (object); } diff --git a/src/qmi-firmware-update/qfu-reseter.h b/src/qmi-firmware-update/qfu-reseter.h index 46dbabd..e80f77a 100644 --- a/src/qmi-firmware-update/qfu-reseter.h +++ b/src/qmi-firmware-update/qfu-reseter.h @@ -24,6 +24,7 @@ #include <glib-object.h> #include <gio/gio.h> +#include <libqmi-glib.h> #include "qfu-device-selection.h" @@ -50,7 +51,10 @@ struct _QfuReseterClass { }; GType qfu_reseter_get_type (void); -QfuReseter *qfu_reseter_new (QfuDeviceSelection *device_selection); +QfuReseter *qfu_reseter_new (QfuDeviceSelection *device_selection, + QmiClientDms *qmi_client, + gboolean device_open_proxy, + gboolean device_open_mbim); void qfu_reseter_run (QfuReseter *self, GCancellable *cancellable, GAsyncReadyCallback callback, diff --git a/src/qmi-firmware-update/qfu-updater.c b/src/qmi-firmware-update/qfu-updater.c index 55cd52b..328b583 100644 --- a/src/qmi-firmware-update/qfu-updater.c +++ b/src/qmi-firmware-update/qfu-updater.c @@ -571,7 +571,7 @@ run_context_step_reset (GTask *task) } /* Boothold reset */ - reseter = qfu_reseter_new (self->priv->device_selection, ctx->qmi_client); + reseter = qfu_reseter_new (self->priv->device_selection, ctx->qmi_client, FALSE, FALSE); qfu_reseter_run (reseter, g_task_get_cancellable (task), (GAsyncReadyCallback) reseter_run_ready, |