diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2016-12-05 18:55:31 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2017-01-16 11:24:14 +0100 |
commit | d6fccedd7b67567b1bef9323391364c6721feb19 (patch) | |
tree | 14d73292b2e8f376b969dab148519463227c3345 | |
parent | 99120ad04aa971257ab0dc4508354bb0664ffc86 (diff) | |
download | external_libqmi-d6fccedd7b67567b1bef9323391364c6721feb19.zip external_libqmi-d6fccedd7b67567b1bef9323391364c6721feb19.tar.gz external_libqmi-d6fccedd7b67567b1bef9323391364c6721feb19.tar.bz2 |
qmi-firmware-update: implement support for direct QDL download
i.e. without going through the QMI triggered reboot.
-rw-r--r-- | src/qmi-firmware-update/qfu-main.c | 25 | ||||
-rw-r--r-- | src/qmi-firmware-update/qfu-operation-update.c | 112 | ||||
-rw-r--r-- | src/qmi-firmware-update/qfu-operation.h | 18 | ||||
-rw-r--r-- | src/qmi-firmware-update/qfu-updater.c | 161 | ||||
-rw-r--r-- | src/qmi-firmware-update/qfu-updater.h | 3 |
5 files changed, 219 insertions, 100 deletions
diff --git a/src/qmi-firmware-update/qfu-main.c b/src/qmi-firmware-update/qfu-main.c index 73f02ea..df157fa 100644 --- a/src/qmi-firmware-update/qfu-main.c +++ b/src/qmi-firmware-update/qfu-main.c @@ -49,6 +49,10 @@ static gchar *carrier_str; static gboolean device_open_proxy_flag; static gboolean device_open_mbim_flag; +/* Update (QDL mode) */ +static gboolean action_update_qdl_flag; +static gchar *serial_str; + /* Verify */ static gboolean action_verify_flag;; @@ -91,6 +95,18 @@ static GOptionEntry context_update_entries[] = { { NULL } }; +static GOptionEntry context_update_qdl_entries[] = { + { "update-qdl", 'U', 0, G_OPTION_ARG_NONE, &action_update_qdl_flag, + "Launch firmware update process in QDL mode.", + NULL + }, + { "serial", 's', 0, G_OPTION_ARG_FILENAME, &serial_str, + "Specify QDL serial device path (e.g. /dev/ttyUSB0).", + "[PATH]" + }, + { NULL } +}; + static GOptionEntry context_verify_entries[] = { { "verify", 'z', 0, G_OPTION_ARG_NONE, &action_verify_flag, "Analyze and Verify firmware images.", @@ -242,6 +258,10 @@ int main (int argc, char **argv) g_option_group_add_entries (group, context_update_entries); g_option_context_add_group (context, group); + group = g_option_group_new ("update-qdl", "Update options (QDL mode)", "", NULL, NULL); + g_option_group_add_entries (group, context_update_qdl_entries); + g_option_context_add_group (context, group); + group = g_option_group_new ("verify", "Verify options", "", NULL, NULL); g_option_group_add_entries (group, context_verify_entries); g_option_context_add_group (context, group); @@ -274,7 +294,7 @@ int main (int argc, char **argv) qmi_utils_set_traces_enabled (TRUE); /* We don't allow multiple actions at the same time */ - n_actions = (action_verify_flag + action_update_flag); + n_actions = (action_verify_flag + action_update_flag + action_update_qdl_flag); if (n_actions == 0) { g_printerr ("error: no actions specified\n"); goto out; @@ -299,6 +319,9 @@ int main (int argc, char **argv) carrier_str, device_open_proxy_flag, device_open_mbim_flag); + else if (action_update_qdl_flag) + result = qfu_operation_update_qdl_run ((const gchar **) image_strv, + serial_str); else if (action_verify_flag) result = qfu_operation_verify_run ((const gchar **) image_strv); else diff --git a/src/qmi-firmware-update/qfu-operation-update.c b/src/qmi-firmware-update/qfu-operation-update.c index a989cdc..53396c1 100644 --- a/src/qmi-firmware-update/qfu-operation-update.c +++ b/src/qmi-firmware-update/qfu-operation-update.c @@ -72,6 +72,47 @@ run_ready (QfuUpdater *updater, g_idle_add ((GSourceFunc) g_main_loop_quit, operation->loop); } +static gboolean +operation_update_run (QfuUpdater *updater, + const gchar **images) +{ + UpdateOperation operation = { + .loop = NULL, + .cancellable = NULL, + .result = FALSE, + }; + GList *image_file_list = NULL; + guint i; + + g_assert (images); + g_assert (QFU_IS_UPDATER (updater)); + + /* Create runtime context */ + operation.loop = g_main_loop_new (NULL, FALSE); + operation.cancellable = g_cancellable_new (); + + /* Setup signals */ + g_unix_signal_add (SIGINT, (GSourceFunc) signal_handler, &operation); + g_unix_signal_add (SIGHUP, (GSourceFunc) signal_handler, &operation); + g_unix_signal_add (SIGTERM, (GSourceFunc) signal_handler, &operation); + + /* Create list of image files */ + for (i = 0; images[i]; i++) + image_file_list = g_list_append (image_file_list, g_file_new_for_commandline_arg (images[i])); + + /* Run! */ + qfu_updater_run (updater, image_file_list, operation.cancellable, (GAsyncReadyCallback) run_ready, &operation); + g_list_free_full (image_file_list, (GDestroyNotify) g_object_unref); + g_main_loop_run (operation.loop); + + if (operation.cancellable) + g_object_unref (operation.cancellable); + if (operation.loop) + g_main_loop_unref (operation.loop); + + return operation.result; +} + gboolean qfu_operation_update_run (const gchar **images, const gchar *device, @@ -81,78 +122,77 @@ qfu_operation_update_run (const gchar **images, gboolean device_open_proxy, gboolean device_open_mbim) { - UpdateOperation operation = { - .loop = NULL, - .cancellable = NULL, - .result = FALSE, - }; QfuUpdater *updater = NULL; GFile *device_file = NULL; - GList *image_file_list = NULL; - guint i; + gboolean result; g_assert (images); /* No device path given? */ if (!device) { g_printerr ("error: no device path specified\n"); - goto out; + return FALSE; } /* No firmware version given? */ if (!firmware_version) { g_printerr ("error: no firmware version specified\n"); - goto out; + return FALSE; } /* No config version given? */ if (!config_version) { g_printerr ("error: no config version specified\n"); - goto out; + return FALSE; } /* No carrier given? */ if (!carrier) { g_printerr ("error: no carrier specified\n"); - goto out; + return FALSE; } - /* Create runtime context */ - operation.loop = g_main_loop_new (NULL, FALSE); - operation.cancellable = g_cancellable_new (); - - /* Setup signals */ - g_unix_signal_add (SIGINT, (GSourceFunc) signal_handler, &operation); - g_unix_signal_add (SIGHUP, (GSourceFunc) signal_handler, &operation); - g_unix_signal_add (SIGTERM, (GSourceFunc) signal_handler, &operation); - - /* Create list of image files */ - for (i = 0; images[i]; i++) - image_file_list = g_list_append (image_file_list, g_file_new_for_commandline_arg (images[i])); - /* Create updater */ device_file = g_file_new_for_commandline_arg (device); updater = qfu_updater_new (device_file, firmware_version, config_version, carrier, - image_file_list, device_open_proxy, device_open_mbim); g_object_unref (device_file); - g_list_free_full (image_file_list, (GDestroyNotify) g_object_unref); /* Run! */ - qfu_updater_run (updater, operation.cancellable, (GAsyncReadyCallback) run_ready, &operation); - g_main_loop_run (operation.loop); + result = operation_update_run (updater, images); -out: - if (updater) - g_object_unref (updater); - if (operation.cancellable) - g_object_unref (operation.cancellable); - if (operation.loop) - g_main_loop_unref (operation.loop); + g_object_unref (updater); + return result; +} - return operation.result; +gboolean +qfu_operation_update_qdl_run (const gchar **images, + const gchar *serial) +{ + QfuUpdater *updater = NULL; + GFile *serial_file = NULL; + gboolean result; + + g_assert (images); + + /* No device path given? */ + if (!serial) { + g_printerr ("error: no serial path specified\n"); + return FALSE; + } + + /* Create updater */ + serial_file = g_file_new_for_commandline_arg (serial); + updater = qfu_updater_new_qdl (serial_file); + g_object_unref (serial_file); + + /* Run! */ + result = operation_update_run (updater, images); + + g_object_unref (updater); + return result; } diff --git a/src/qmi-firmware-update/qfu-operation.h b/src/qmi-firmware-update/qfu-operation.h index 296d315..29b7856 100644 --- a/src/qmi-firmware-update/qfu-operation.h +++ b/src/qmi-firmware-update/qfu-operation.h @@ -26,14 +26,16 @@ G_BEGIN_DECLS -gboolean qfu_operation_update_run (const gchar **images, - const gchar *device, - const gchar *firmware_version, - const gchar *config_version, - const gchar *carrier, - gboolean device_open_proxy, - gboolean device_open_mbim); -gboolean qfu_operation_verify_run (const gchar **images); +gboolean qfu_operation_update_run (const gchar **images, + const gchar *device, + const gchar *firmware_version, + const gchar *config_version, + const gchar *carrier, + gboolean device_open_proxy, + gboolean device_open_mbim); +gboolean qfu_operation_update_qdl_run (const gchar **images, + const gchar *serial); +gboolean qfu_operation_verify_run (const gchar **images); G_END_DECLS diff --git a/src/qmi-firmware-update/qfu-updater.c b/src/qmi-firmware-update/qfu-updater.c index 6edfcb1..3b3b7c2 100644 --- a/src/qmi-firmware-update/qfu-updater.c +++ b/src/qmi-firmware-update/qfu-updater.c @@ -35,15 +35,21 @@ G_DEFINE_TYPE (QfuUpdater, qfu_updater, G_TYPE_OBJECT) +typedef enum { + UPDATER_TYPE_UNKNOWN, + UPDATER_TYPE_GENERIC, + UPDATER_TYPE_QDL, +} UpdaterType; + struct _QfuUpdaterPrivate { - /* Inputs */ - GFile *cdc_wdm_file; - gchar *firmware_version; - gchar *config_version; - gchar *carrier; - GList *image_file_list; - gboolean device_open_proxy; - gboolean device_open_mbim; + UpdaterType type; + GFile *cdc_wdm_file; + GFile *serial_file; + gchar *firmware_version; + gchar *config_version; + gchar *carrier; + gboolean device_open_proxy; + gboolean device_open_mbim; }; static const gchar *cdc_wdm_subsys[] = { "usbmisc", "usb", NULL }; @@ -73,31 +79,33 @@ typedef enum { } RunContextStep; typedef struct { + /* Device files and common USB sysfs path*/ + GFile *cdc_wdm_file; + GFile *serial_file; + gchar *sysfs_path; + /* Context step */ RunContextStep step; - /* List of pending QfuImages to download */ - GList *pending_images; - /* Current image being downloaded */ + + /* List of pending QfuImages to download, and the current one being + * processed. */ + GList *pending_images; QfuImage *current_image; - /* USB info */ - gchar *sysfs_path; + /* QMI device and client */ QmiDevice *qmi_device; QmiClientDms *qmi_client; gint qmi_client_retries; - /* TTY file */ - GFile *tty; + /* QDL device */ - QfuQdlDevice *qdl; + QfuQdlDevice *qdl_device; } RunContext; static void run_context_free (RunContext *ctx) { - if (ctx->qdl) - g_object_unref (&ctx->qdl); - if (ctx->tty) - g_object_unref (ctx->tty); + if (ctx->qdl_device) + g_object_unref (&ctx->qdl_device); if (ctx->qmi_client) { g_assert (ctx->qmi_device); qmi_device_release_client (ctx->qmi_device, @@ -114,6 +122,10 @@ run_context_free (RunContext *ctx) g_object_unref (ctx->current_image); g_list_free_full (ctx->pending_images, (GDestroyNotify) g_object_unref); g_free (ctx->sysfs_path); + if (ctx->serial_file) + g_object_unref (ctx->serial_file); + if (ctx->cdc_wdm_file) + g_object_unref (ctx->cdc_wdm_file); g_slice_free (RunContext, ctx); } @@ -153,21 +165,22 @@ wait_for_cdc_wdm_ready (gpointer unused, { GError *error = NULL; RunContext *ctx; - gchar *tty_path; + gchar *path; ctx = (RunContext *) g_task_get_task_data (task); - ctx->tty = qfu_udev_helper_wait_for_device_finish (res, &error); - if (!ctx->tty) { + g_assert (!ctx->cdc_wdm_file); + ctx->cdc_wdm_file = qfu_udev_helper_wait_for_device_finish (res, &error); + if (!ctx->cdc_wdm_file) { g_prefix_error (&error, "error waiting for cdc-wdm: "); g_task_return_error (task, error); g_object_unref (task); return; } - tty_path = g_file_get_path (ctx->tty); - g_debug ("[qfu-updater] cdc-wdm device found: %s", tty_path); - g_free (tty_path); + path = g_file_get_path (ctx->cdc_wdm_file); + g_debug ("[qfu-updater] cdc-wdm device found: %s", path); + g_free (path); /* Go on */ run_context_step_next (task, ctx->step + 1); @@ -180,8 +193,6 @@ run_context_step_wait_for_cdc_wdm (GTask *task) ctx = (RunContext *) g_task_get_task_data (task); - g_print ("rebooting in normal mode...\n"); - g_debug ("[qfu-updater] now waiting for cdc-wdm device..."); qfu_udev_helper_wait_for_device (QFU_UDEV_HELPER_WAIT_FOR_DEVICE_TYPE_CDC_WDM, ctx->sysfs_path, @@ -194,14 +205,26 @@ static void run_context_step_cleanup_qdl_device (GTask *task) { RunContext *ctx; + QfuUpdater *self; ctx = (RunContext *) g_task_get_task_data (task); + self = g_task_get_source_object (task); - g_assert (ctx->qdl); + g_assert (ctx->qdl_device); + g_assert (ctx->serial_file); g_debug ("[qfu-updater] QDL reset"); - qfu_qdl_device_reset (ctx->qdl, g_task_get_cancellable (task), NULL); - g_clear_object (&ctx->qdl); + qfu_qdl_device_reset (ctx->qdl_device, g_task_get_cancellable (task), NULL); + g_clear_object (&ctx->qdl_device); + g_clear_object (&ctx->serial_file); + + g_print ("rebooting in normal mode...\n"); + + /* If we were running in QDL mode, we don't even wait for the reboot to finish */ + if (self->priv->type == UPDATER_TYPE_QDL) { + run_context_step_next (task, RUN_CONTEXT_STEP_LAST); + return; + } run_context_step_next (task, ctx->step + 1); } @@ -251,19 +274,19 @@ run_context_step_download_image (GTask *task) aux); g_free (aux); - if (!qfu_qdl_device_hello (ctx->qdl, cancellable, &error)) { + if (!qfu_qdl_device_hello (ctx->qdl_device, cancellable, &error)) { g_prefix_error (&error, "couldn't send greetings to device: "); goto out; } - if (!qfu_qdl_device_ufopen (ctx->qdl, ctx->current_image, cancellable, &error)) { + if (!qfu_qdl_device_ufopen (ctx->qdl_device, ctx->current_image, cancellable, &error)) { g_prefix_error (&error, "couldn't open session: "); goto out; } n_chunks = qfu_image_get_n_data_chunks (ctx->current_image); for (sequence = 0; sequence < n_chunks; sequence++) { - if (!qfu_qdl_device_ufwrite (ctx->qdl, ctx->current_image, sequence, cancellable, &error)) { + if (!qfu_qdl_device_ufwrite (ctx->qdl_device, ctx->current_image, sequence, cancellable, &error)) { g_prefix_error (&error, "couldn't write in session: "); goto out; } @@ -271,7 +294,7 @@ run_context_step_download_image (GTask *task) g_debug ("[qfu-updater] all chunks ack-ed"); - if (!qfu_qdl_device_ufclose (ctx->qdl, cancellable, &error)) { + if (!qfu_qdl_device_ufclose (ctx->qdl_device, cancellable, &error)) { g_prefix_error (&error, "couldn't close session: "); goto out; } @@ -325,9 +348,10 @@ run_context_step_qdl_device (GTask *task) ctx = (RunContext *) g_task_get_task_data (task); - g_assert (!ctx->qdl); - ctx->qdl = qfu_qdl_device_new (ctx->tty, g_task_get_cancellable (task), &error); - if (!ctx->qdl) { + g_assert (ctx->serial_file); + g_assert (!ctx->qdl_device); + ctx->qdl_device = qfu_qdl_device_new (ctx->serial_file, g_task_get_cancellable (task), &error); + if (!ctx->qdl_device) { g_prefix_error (&error, "error creating device: "); g_task_return_error (task, error); g_object_unref (task); @@ -344,21 +368,22 @@ wait_for_tty_ready (gpointer unused, { GError *error = NULL; RunContext *ctx; - gchar *tty_path; + gchar *path; ctx = (RunContext *) g_task_get_task_data (task); - ctx->tty = qfu_udev_helper_wait_for_device_finish (res, &error); - if (!ctx->tty) { + g_assert (!ctx->serial_file); + ctx->serial_file = qfu_udev_helper_wait_for_device_finish (res, &error); + if (!ctx->serial_file) { g_prefix_error (&error, "error waiting for TTY: "); g_task_return_error (task, error); g_object_unref (task); return; } - tty_path = g_file_get_path (ctx->tty); - g_debug ("[qfu-updater] TTY device found: %s", tty_path); - g_free (tty_path); + path = g_file_get_path (ctx->serial_file); + g_debug ("[qfu-updater] TTY device found: %s", path); + g_free (path); /* Go on */ run_context_step_next (task, ctx->step + 1); @@ -410,6 +435,7 @@ qmi_client_release_ready (QmiDevice *device, g_debug ("[qfu-updater] QMI device closed"); g_clear_object (&ctx->qmi_device); + g_clear_object (&ctx->cdc_wdm_file); /* Go on */ run_context_step_next (task, ctx->step + 1); @@ -754,6 +780,7 @@ run_context_step_qmi_device (GTask *task) self = g_task_get_source_object (task); g_debug ("[qfu-updater] creating QMI device..."); + g_assert (self->priv->cdc_wdm_file); qmi_device_new (self->priv->cdc_wdm_file, g_task_get_cancellable (task), (GAsyncReadyCallback) qmi_device_ready, @@ -770,6 +797,8 @@ run_context_step_usb_info (GTask *task) ctx = (RunContext *) g_task_get_task_data (task); self = g_task_get_source_object (task); + g_assert (self->priv->cdc_wdm_file); + g_debug ("[qfu-updater] looking for device sysfs path..."); ctx->sysfs_path = qfu_udev_helper_get_sysfs_path (self->priv->cdc_wdm_file, cdc_wdm_subsys, &error); if (!ctx->sysfs_path) { @@ -835,6 +864,7 @@ image_sort_by_size (QfuImage *a, QfuImage *b) void qfu_updater_run (QfuUpdater *self, + GList *image_file_list, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) @@ -843,13 +873,15 @@ qfu_updater_run (QfuUpdater *self, GTask *task; GList *l; + g_assert (image_file_list); + ctx = g_slice_new0 (RunContext); task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify) run_context_free); /* Build QfuImage objects for each image file given */ - for (l = self->priv->image_file_list; l; l = g_list_next (l)) { + for (l = image_file_list; l; l = g_list_next (l)) { GError *error = NULL; QfuImage *image; @@ -866,8 +898,19 @@ qfu_updater_run (QfuUpdater *self, * the use case anyway, first flash e.g. the .cwe file, then the .nvu one. */ ctx->pending_images = g_list_sort (ctx->pending_images, (GCompareFunc) image_sort_by_size); - /* Define amount of retries trying to allocate a QMI DMS client */ - ctx->qmi_client_retries = QMI_CLIENT_RETRIES; + switch (self->priv->type) { + case UPDATER_TYPE_GENERIC: + ctx->step = RUN_CONTEXT_STEP_USB_INFO; + ctx->qmi_client_retries = QMI_CLIENT_RETRIES; + ctx->cdc_wdm_file = g_object_ref (self->priv->cdc_wdm_file); + break; + case UPDATER_TYPE_QDL: + ctx->step = RUN_CONTEXT_STEP_QDL_DEVICE; + ctx->serial_file = g_object_ref (self->priv->serial_file); + break; + default: + g_assert_not_reached (); + } run_context_step (task); } @@ -879,23 +922,35 @@ qfu_updater_new (GFile *cdc_wdm_file, const gchar *firmware_version, const gchar *config_version, const gchar *carrier, - GList *image_file_list, gboolean device_open_proxy, gboolean device_open_mbim) { QfuUpdater *self; g_assert (G_IS_FILE (cdc_wdm_file)); - g_assert (image_file_list); self = g_object_new (QFU_TYPE_UPDATER, NULL); + self->priv->type = UPDATER_TYPE_GENERIC; self->priv->cdc_wdm_file = g_object_ref (cdc_wdm_file); self->priv->device_open_proxy = device_open_proxy; self->priv->device_open_mbim = device_open_mbim; self->priv->firmware_version = g_strdup (firmware_version); self->priv->config_version = g_strdup (config_version); self->priv->carrier = g_strdup (carrier); - self->priv->image_file_list = g_list_copy_deep (image_file_list, (GCopyFunc) g_object_ref, NULL); + + return self; +} + +QfuUpdater * +qfu_updater_new_qdl (GFile *serial_file) +{ + QfuUpdater *self; + + g_assert (G_IS_FILE (serial_file)); + + self = g_object_new (QFU_TYPE_UPDATER, NULL); + self->priv->type = UPDATER_TYPE_QDL; + self->priv->serial_file = g_object_ref (serial_file); return self; } @@ -904,6 +959,7 @@ static void qfu_updater_init (QfuUpdater *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, QFU_TYPE_UPDATER, QfuUpdaterPrivate); + self->priv->type = UPDATER_TYPE_UNKNOWN; } static void @@ -911,11 +967,8 @@ dispose (GObject *object) { QfuUpdater *self = QFU_UPDATER (object); + g_clear_object (&self->priv->serial_file); g_clear_object (&self->priv->cdc_wdm_file); - if (self->priv->image_file_list) { - g_list_free_full (self->priv->image_file_list, (GDestroyNotify) g_object_unref); - self->priv->image_file_list = NULL; - } G_OBJECT_CLASS (qfu_updater_parent_class)->dispose (object); } diff --git a/src/qmi-firmware-update/qfu-updater.h b/src/qmi-firmware-update/qfu-updater.h index b978792..56ff6b5 100644 --- a/src/qmi-firmware-update/qfu-updater.h +++ b/src/qmi-firmware-update/qfu-updater.h @@ -52,10 +52,11 @@ QfuUpdater *qfu_updater_new (GFile *cdc_wdm_file, const gchar *firmware_version, const gchar *config_version, const gchar *carrier, - GList *image_file_list, gboolean device_open_proxy, gboolean device_open_mbim); +QfuUpdater *qfu_updater_new_qdl (GFile *serial_file); void qfu_updater_run (QfuUpdater *self, + GList *image_file_list, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); |