aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qmi-firmware-update/qfu-udev-helpers.c157
-rw-r--r--src/qmi-firmware-update/qfu-udev-helpers.h8
-rw-r--r--src/qmi-firmware-update/qfu-updater.c47
3 files changed, 212 insertions, 0 deletions
diff --git a/src/qmi-firmware-update/qfu-udev-helpers.c b/src/qmi-firmware-update/qfu-udev-helpers.c
index 32d8e48..90016bf 100644
--- a/src/qmi-firmware-update/qfu-udev-helpers.c
+++ b/src/qmi-firmware-update/qfu-udev-helpers.c
@@ -108,3 +108,160 @@ qfu_udev_helper_get_sysfs_path (GFile *file,
g_object_unref (udev);
return sysfs_path;
}
+
+/******************************************************************************/
+
+#define WAIT_FOR_TTY_TIMEOUT_SECS 15
+
+typedef struct {
+ GUdevClient *udev;
+ gchar *sysfs_path;
+ guint timeout_id;
+ gulong uevent_id;
+ gulong cancellable_id;
+} WaitForTtyContext;
+
+static void
+wait_for_tty_context_free (WaitForTtyContext *ctx)
+{
+ g_assert (!ctx->timeout_id);
+ g_assert (!ctx->uevent_id);
+ g_assert (!ctx->cancellable_id);
+
+ g_object_unref (ctx->udev);
+ g_free (ctx->sysfs_path);
+ g_slice_free (WaitForTtyContext, ctx);
+}
+
+GFile *
+qfu_udev_helper_wait_for_tty_finish (GAsyncResult *res,
+ GError **error)
+{
+ return G_FILE (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static void
+handle_uevent (GUdevClient *client,
+ const char *action,
+ GUdevDevice *device,
+ GTask *task)
+{
+ gchar *sysfs_path;
+ WaitForTtyContext *ctx;
+
+ ctx = (WaitForTtyContext *) g_task_get_task_data (task);
+
+ if (!g_str_equal (action, "add") && !g_str_equal (action, "move") && !g_str_equal (action, "change"))
+ return;
+
+ sysfs_path = qfu_udev_helper_get_udev_device_sysfs_path (device, NULL);
+ if (!sysfs_path)
+ return;
+
+ /* If sysfs_path matches and driver is 'qcserial', we're done */
+ if ((g_strcmp0 (sysfs_path, ctx->sysfs_path) == 0) &&
+ (g_strcmp0 (g_udev_device_get_property (device, "ID_USB_DRIVER"), "qcserial") == 0)) {
+ gchar *path;
+
+ /* Disconnect this handler */
+ g_signal_handler_disconnect (ctx->udev, ctx->uevent_id);
+ ctx->uevent_id = 0;
+
+ /* Disconnect the other handlers */
+ g_cancellable_disconnect (g_task_get_cancellable (task), ctx->cancellable_id);
+ ctx->cancellable_id = 0;
+ g_source_remove (ctx->timeout_id);
+ ctx->timeout_id = 0;
+
+ path = g_strdup_printf ("/dev/%s", g_udev_device_get_name (device));
+ g_task_return_pointer (task, g_file_new_for_path (path), g_object_unref);
+ g_object_unref (task);
+ g_free (path);
+ }
+
+ g_free (sysfs_path);
+}
+
+static gboolean
+wait_for_tty_timed_out (GTask *task)
+{
+ WaitForTtyContext *ctx;
+
+ ctx = (WaitForTtyContext *) g_task_get_task_data (task);
+
+ /* Disconnect this handler */
+ ctx->timeout_id = 0;
+
+ /* Disconnect the other handlers */
+ g_cancellable_disconnect (g_task_get_cancellable (task), ctx->cancellable_id);
+ ctx->cancellable_id = 0;
+ g_signal_handler_disconnect (ctx->udev, ctx->uevent_id);
+ ctx->uevent_id = 0;
+
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+ "waiting for TTY at '%s' timed out",
+ ctx->sysfs_path);
+ g_object_unref (task);
+ return FALSE;
+}
+
+static void
+wait_for_tty_cancelled (GCancellable *cancellable,
+ GTask *task)
+{
+ WaitForTtyContext *ctx;
+
+ ctx = (WaitForTtyContext *) g_task_get_task_data (task);
+
+ /* Disconnect this handler */
+ g_cancellable_disconnect (g_task_get_cancellable (task), ctx->cancellable_id);
+ ctx->cancellable_id = 0;
+
+ /* Disconnect the other handlers */
+ g_source_remove (ctx->timeout_id);
+ ctx->timeout_id = 0;
+ g_signal_handler_disconnect (ctx->udev, ctx->uevent_id);
+ ctx->uevent_id = 0;
+
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED,
+ "waiting for TTY at '%s' cancelled",
+ ctx->sysfs_path);
+ g_object_unref (task);
+}
+
+void
+qfu_udev_helper_wait_for_tty (const gchar *sysfs_path,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ static const gchar *subsys_monitor[] = { "tty", NULL };
+ GTask *task;
+ WaitForTtyContext *ctx;
+
+ ctx = g_slice_new0 (WaitForTtyContext);
+ ctx->sysfs_path = g_strdup (sysfs_path);
+ ctx->udev = g_udev_client_new (subsys_monitor);
+
+ task = g_task_new (NULL, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) wait_for_tty_context_free);
+
+ /* Monitor for tty additions. */
+ ctx->uevent_id = g_signal_connect (ctx->udev,
+ "uevent",
+ G_CALLBACK (handle_uevent),
+ task);
+
+ /* Allow cancellation */
+ ctx->cancellable_id = g_cancellable_connect (cancellable,
+ (GCallback) wait_for_tty_cancelled,
+ task,
+ NULL);
+
+ /* And also, setup a timeout to avoid waiting forever. */
+ ctx->timeout_id = g_timeout_add_seconds (WAIT_FOR_TTY_TIMEOUT_SECS,
+ (GSourceFunc) wait_for_tty_timed_out,
+ task);
+
+ /* Note: task ownership is shared between the signals and the timeout */
+}
diff --git a/src/qmi-firmware-update/qfu-udev-helpers.h b/src/qmi-firmware-update/qfu-udev-helpers.h
index e12bd80..8eedb7b 100644
--- a/src/qmi-firmware-update/qfu-udev-helpers.h
+++ b/src/qmi-firmware-update/qfu-udev-helpers.h
@@ -23,6 +23,7 @@
#define QFU_UDEV_HELPERS_H
#include <gio/gio.h>
+#include <gudev/gudev.h>
G_BEGIN_DECLS
@@ -32,6 +33,13 @@ gchar *qfu_udev_helper_get_sysfs_path (GFile *file,
const gchar *const *subsys,
GError **error);
+void qfu_udev_helper_wait_for_tty (const gchar *sysfs_path,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GFile *qfu_udev_helper_wait_for_tty_finish (GAsyncResult *res,
+ GError **error);
+
G_END_DECLS
#endif /* QFU_UDEV_HELPERS_H */
diff --git a/src/qmi-firmware-update/qfu-updater.c b/src/qmi-firmware-update/qfu-updater.c
index f507f18..64b6c67 100644
--- a/src/qmi-firmware-update/qfu-updater.c
+++ b/src/qmi-firmware-update/qfu-updater.c
@@ -58,6 +58,7 @@ typedef enum {
RUN_CONTEXT_STEP_OFFLINE,
RUN_CONTEXT_STEP_RESET,
RUN_CONTEXT_STEP_CLEANUP_QMI_DEVICE,
+ RUN_CONTEXT_STEP_WAIT_FOR_TTY,
RUN_CONTEXT_STEP_CLEANUP_IMAGE,
RUN_CONTEXT_STEP_LAST
} RunContextStep;
@@ -75,11 +76,15 @@ typedef struct {
/* QMI device and client */
QmiDevice *qmi_device;
QmiClientDms *qmi_client;
+ /* TTY file */
+ GFile *tty;
} RunContext;
static void
run_context_free (RunContext *ctx)
{
+ if (ctx->tty)
+ g_object_unref (ctx->tty);
if (ctx->qmi_client) {
g_assert (ctx->qmi_device);
qmi_device_release_client (ctx->qmi_device,
@@ -148,6 +153,47 @@ run_context_step_cleanup_image (GTask *task)
}
static void
+wait_for_tty_ready (gpointer unused,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ RunContext *ctx;
+ gchar *tty_path;
+
+ ctx = (RunContext *) g_task_get_task_data (task);
+
+ ctx->tty = qfu_udev_helper_wait_for_tty_finish (res, &error);
+ if (!ctx->tty) {
+ 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);
+
+ /* Go on */
+ run_context_step_next (task, ctx->step + 1);
+}
+
+static void
+run_context_step_wait_for_tty (GTask *task)
+{
+ RunContext *ctx;
+
+ ctx = (RunContext *) g_task_get_task_data (task);
+
+ g_debug ("[qfu-updater] reset requested, now waiting for TTY device...");
+ qfu_udev_helper_wait_for_tty (ctx->sysfs_path,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) wait_for_tty_ready,
+ task);
+}
+
+static void
qmi_client_release_ready (QmiDevice *device,
GAsyncResult *res,
GTask *task)
@@ -586,6 +632,7 @@ static const RunContextStepFunc run_context_step_func[] = {
[RUN_CONTEXT_STEP_OFFLINE] = run_context_step_offline,
[RUN_CONTEXT_STEP_RESET] = run_context_step_reset,
[RUN_CONTEXT_STEP_CLEANUP_QMI_DEVICE] = run_context_step_cleanup_qmi_device,
+ [RUN_CONTEXT_STEP_WAIT_FOR_TTY] = run_context_step_wait_for_tty,
[RUN_CONTEXT_STEP_CLEANUP_IMAGE] = run_context_step_cleanup_image,
};