diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2016-11-27 17:40:41 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2017-01-16 11:24:12 +0100 |
commit | e43d876f439aa6db36972a9f79a03219db449bf2 (patch) | |
tree | bdb9602a128ed8f4fd2e88babbfaf698d8b56b4a /src/qmi-firmware-update | |
parent | 3cc507ad8cc7a6387d2ea21c9f2419780d1340ea (diff) | |
download | external_libqmi-e43d876f439aa6db36972a9f79a03219db449bf2.zip external_libqmi-e43d876f439aa6db36972a9f79a03219db449bf2.tar.gz external_libqmi-e43d876f439aa6db36972a9f79a03219db449bf2.tar.bz2 |
qmi-firmware-update: wait for cdc-wdm device after image download
Diffstat (limited to 'src/qmi-firmware-update')
-rw-r--r-- | src/qmi-firmware-update/qfu-udev-helpers.c | 179 | ||||
-rw-r--r-- | src/qmi-firmware-update/qfu-udev-helpers.h | 18 | ||||
-rw-r--r-- | src/qmi-firmware-update/qfu-updater.c | 55 |
3 files changed, 182 insertions, 70 deletions
diff --git a/src/qmi-firmware-update/qfu-udev-helpers.c b/src/qmi-firmware-update/qfu-udev-helpers.c index 90016bf..d0d4801 100644 --- a/src/qmi-firmware-update/qfu-udev-helpers.c +++ b/src/qmi-firmware-update/qfu-udev-helpers.c @@ -111,18 +111,19 @@ qfu_udev_helper_get_sysfs_path (GFile *file, /******************************************************************************/ -#define WAIT_FOR_TTY_TIMEOUT_SECS 15 +#define WAIT_FOR_DEVICE_TIMEOUT_SECS 15 typedef struct { - GUdevClient *udev; - gchar *sysfs_path; - guint timeout_id; - gulong uevent_id; - gulong cancellable_id; -} WaitForTtyContext; + QfuUdevHelperWaitForDeviceType device_type; + GUdevClient *udev; + gchar *sysfs_path; + guint timeout_id; + gulong uevent_id; + gulong cancellable_id; +} WaitForDeviceContext; static void -wait_for_tty_context_free (WaitForTtyContext *ctx) +wait_for_device_context_free (WaitForDeviceContext *ctx) { g_assert (!ctx->timeout_id); g_assert (!ctx->uevent_id); @@ -130,64 +131,115 @@ wait_for_tty_context_free (WaitForTtyContext *ctx) g_object_unref (ctx->udev); g_free (ctx->sysfs_path); - g_slice_free (WaitForTtyContext, ctx); + g_slice_free (WaitForDeviceContext, ctx); } GFile * -qfu_udev_helper_wait_for_tty_finish (GAsyncResult *res, - GError **error) +qfu_udev_helper_wait_for_device_finish (GAsyncResult *res, + GError **error) { return G_FILE (g_task_propagate_pointer (G_TASK (res), error)); } +static gchar * +udev_helper_get_udev_device_driver (GUdevDevice *device, + GError **error) +{ + GUdevDevice *parent; + gchar *driver; + + /* We need to look for the parent GUdevDevice which has a "usb_interface" + * devtype. */ + + parent = g_udev_device_get_parent (device); + while (parent) { + GUdevDevice *next; + + if (g_strcmp0 (g_udev_device_get_devtype (parent), "usb_interface") == 0) + break; + + /* Check next parent */ + next = g_udev_device_get_parent (parent); + g_object_unref (parent); + parent = next; + } + + if (!parent) { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "couldn't find parent interface USB device"); + return NULL; + } + + driver = g_strdup (g_udev_device_get_driver (parent)); + g_object_unref (parent); + return driver; +} + static void handle_uevent (GUdevClient *client, const char *action, GUdevDevice *device, GTask *task) { - gchar *sysfs_path; - WaitForTtyContext *ctx; + WaitForDeviceContext *ctx; + gchar *sysfs_path = NULL; + gchar *driver = NULL; + gchar *path; - ctx = (WaitForTtyContext *) g_task_get_task_data (task); + ctx = (WaitForDeviceContext *) g_task_get_task_data (task); if (!g_str_equal (action, "add") && !g_str_equal (action, "move") && !g_str_equal (action, "change")) - return; + goto out; 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); + goto out; + + driver = udev_helper_get_udev_device_driver (device, NULL); + if (!driver) + goto out; + + if (g_strcmp0 (sysfs_path, ctx->sysfs_path) != 0) + goto out; + + switch (ctx->device_type) { + case QFU_UDEV_HELPER_WAIT_FOR_DEVICE_TYPE_TTY: + if (g_strcmp0 (driver, "qcserial") != 0) + goto out; + break; + case QFU_UDEV_HELPER_WAIT_FOR_DEVICE_TYPE_CDC_WDM: + if (g_strcmp0 (driver, "cdc_wdm") != 0) + goto out; + break; + default: + g_assert_not_reached (); } + /* 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); + +out: g_free (sysfs_path); + g_free (driver); } static gboolean -wait_for_tty_timed_out (GTask *task) +wait_for_device_timed_out (GTask *task) { - WaitForTtyContext *ctx; + WaitForDeviceContext *ctx; - ctx = (WaitForTtyContext *) g_task_get_task_data (task); + ctx = (WaitForDeviceContext *) g_task_get_task_data (task); /* Disconnect this handler */ ctx->timeout_id = 0; @@ -199,19 +251,19 @@ wait_for_tty_timed_out (GTask *task) 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", + "waiting for device at '%s' timed out", ctx->sysfs_path); g_object_unref (task); return FALSE; } static void -wait_for_tty_cancelled (GCancellable *cancellable, - GTask *task) +wait_for_device_cancelled (GCancellable *cancellable, + GTask *task) { - WaitForTtyContext *ctx; + WaitForDeviceContext *ctx; - ctx = (WaitForTtyContext *) g_task_get_task_data (task); + ctx = (WaitForDeviceContext *) g_task_get_task_data (task); /* Disconnect this handler */ g_cancellable_disconnect (g_task_get_cancellable (task), ctx->cancellable_id); @@ -224,29 +276,38 @@ wait_for_tty_cancelled (GCancellable *cancellable, ctx->uevent_id = 0; g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, - "waiting for TTY at '%s' cancelled", + "waiting for device 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) +qfu_udev_helper_wait_for_device (QfuUdevHelperWaitForDeviceType device_type, + const gchar *sysfs_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - static const gchar *subsys_monitor[] = { "tty", NULL }; - GTask *task; - WaitForTtyContext *ctx; + static const gchar *tty_subsys_monitor[] = { "tty", NULL }; + static const gchar *cdc_wdm_subsys_monitor[] = { "usbmisc", "usb", NULL }; + GTask *task; + WaitForDeviceContext *ctx; - ctx = g_slice_new0 (WaitForTtyContext); + ctx = g_slice_new0 (WaitForDeviceContext); + ctx->device_type = device_type; ctx->sysfs_path = g_strdup (sysfs_path); - ctx->udev = g_udev_client_new (subsys_monitor); + + if (ctx->device_type == QFU_UDEV_HELPER_WAIT_FOR_DEVICE_TYPE_TTY) + ctx->udev = g_udev_client_new (tty_subsys_monitor); + else if (ctx->device_type == QFU_UDEV_HELPER_WAIT_FOR_DEVICE_TYPE_CDC_WDM) + ctx->udev = g_udev_client_new (cdc_wdm_subsys_monitor); + else + g_assert_not_reached (); task = g_task_new (NULL, cancellable, callback, user_data); - g_task_set_task_data (task, ctx, (GDestroyNotify) wait_for_tty_context_free); + g_task_set_task_data (task, ctx, (GDestroyNotify) wait_for_device_context_free); - /* Monitor for tty additions. */ + /* Monitor for device additions. */ ctx->uevent_id = g_signal_connect (ctx->udev, "uevent", G_CALLBACK (handle_uevent), @@ -254,13 +315,13 @@ qfu_udev_helper_wait_for_tty (const gchar *sysfs_path, /* Allow cancellation */ ctx->cancellable_id = g_cancellable_connect (cancellable, - (GCallback) wait_for_tty_cancelled, + (GCallback) wait_for_device_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, + ctx->timeout_id = g_timeout_add_seconds (WAIT_FOR_DEVICE_TIMEOUT_SECS, + (GSourceFunc) wait_for_device_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 8eedb7b..759c5cf 100644 --- a/src/qmi-firmware-update/qfu-udev-helpers.h +++ b/src/qmi-firmware-update/qfu-udev-helpers.h @@ -33,12 +33,18 @@ 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); +typedef enum { + QFU_UDEV_HELPER_WAIT_FOR_DEVICE_TYPE_TTY, + QFU_UDEV_HELPER_WAIT_FOR_DEVICE_TYPE_CDC_WDM, +} QfuUdevHelperWaitForDeviceType; + +void qfu_udev_helper_wait_for_device (QfuUdevHelperWaitForDeviceType device_type, + const gchar *sysfs_path, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GFile *qfu_udev_helper_wait_for_device_finish (GAsyncResult *res, + GError **error); G_END_DECLS diff --git a/src/qmi-firmware-update/qfu-updater.c b/src/qmi-firmware-update/qfu-updater.c index 590707a..b777a4e 100644 --- a/src/qmi-firmware-update/qfu-updater.c +++ b/src/qmi-firmware-update/qfu-updater.c @@ -60,6 +60,7 @@ typedef enum { RUN_CONTEXT_STEP_CLEANUP_QMI_DEVICE, RUN_CONTEXT_STEP_WAIT_FOR_TTY, RUN_CONTEXT_STEP_DOWNLOAD_IMAGE, + RUN_CONTEXT_STEP_WAIT_FOR_CDC_WDM, RUN_CONTEXT_STEP_CLEANUP_IMAGE, RUN_CONTEXT_STEP_LAST } RunContextStep; @@ -154,6 +155,48 @@ run_context_step_cleanup_image (GTask *task) } static void +wait_for_cdc_wdm_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_device_finish (res, &error); + if (!ctx->tty) { + 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); + + /* Go on */ + run_context_step_next (task, ctx->step + 1); +} + +static void +run_context_step_wait_for_cdc_wdm (GTask *task) +{ + RunContext *ctx; + + ctx = (RunContext *) g_task_get_task_data (task); + + 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, + g_task_get_cancellable (task), + (GAsyncReadyCallback) wait_for_cdc_wdm_ready, + task); +} + +static void run_context_step_download_image (GTask *task) { RunContext *ctx; @@ -177,7 +220,7 @@ wait_for_tty_ready (gpointer unused, ctx = (RunContext *) g_task_get_task_data (task); - ctx->tty = qfu_udev_helper_wait_for_tty_finish (res, &error); + ctx->tty = qfu_udev_helper_wait_for_device_finish (res, &error); if (!ctx->tty) { g_prefix_error (&error, "error waiting for TTY: "); g_task_return_error (task, error); @@ -201,10 +244,11 @@ run_context_step_wait_for_tty (GTask *task) 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); + qfu_udev_helper_wait_for_device (QFU_UDEV_HELPER_WAIT_FOR_DEVICE_TYPE_TTY, + ctx->sysfs_path, + g_task_get_cancellable (task), + (GAsyncReadyCallback) wait_for_tty_ready, + task); } static void @@ -648,6 +692,7 @@ static const RunContextStepFunc run_context_step_func[] = { [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_DOWNLOAD_IMAGE] = run_context_step_download_image, + [RUN_CONTEXT_STEP_WAIT_FOR_CDC_WDM] = run_context_step_wait_for_cdc_wdm, [RUN_CONTEXT_STEP_CLEANUP_IMAGE] = run_context_step_cleanup_image, }; |