aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@aleksander.es>2017-01-13 20:35:49 +0100
committerAleksander Morgado <aleksander@aleksander.es>2017-01-16 11:24:16 +0100
commit86293bebe23d4e43b8ea87a5235b57fd5ea3eaee (patch)
tree67fea1a6c393a4068afbab33d1f0a8a1c99124e1
parent3038951fe085514f6fa4872d0b578756fe22d492 (diff)
downloadexternal_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.c11
-rw-r--r--src/qmi-firmware-update/qfu-operation-reset.c6
-rw-r--r--src/qmi-firmware-update/qfu-operation.h4
-rw-r--r--src/qmi-firmware-update/qfu-reseter.c208
-rw-r--r--src/qmi-firmware-update/qfu-reseter.h6
-rw-r--r--src/qmi-firmware-update/qfu-updater.c2
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,