/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * qmi-firmware-update -- Command line tool to update firmware in QMI devices * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Copyright (C) 2017 Zodiac Inflight Innovations * Copyright (C) 2017 Aleksander Morgado */ #include #include #include #include #include #include "qfu-device-selection.h" #include "qfu-udev-helpers.h" G_DEFINE_TYPE (QfuDeviceSelection, qfu_device_selection, G_TYPE_OBJECT) struct _QfuDeviceSelectionPrivate { /* inputs */ gchar *preferred_devices[QFU_UDEV_HELPER_DEVICE_TYPE_LAST]; guint16 preferred_vid; guint16 preferred_pid; guint preferred_busnum; guint preferred_devnum; /* sysfs path */ gchar *sysfs_path; /* generic udev monitor */ QfuUdevHelperGenericMonitor *monitor; }; /******************************************************************************/ static GFile * device_selection_get_single (QfuDeviceSelection *self, QfuUdevHelperDeviceType device_type) { GFile *first_selection = NULL; GFile *preferred_selection = NULL; GList *list, *l; gchar *path; g_debug ("[qfu,device-selection] single %s device requested in sysfs path '%s'", qfu_udev_helper_device_type_to_string (device_type), self->priv->sysfs_path); list = qfu_udev_helper_list_devices (device_type, self->priv->sysfs_path); for (l = list; l; l = g_list_next (l)) { path = g_file_get_path (G_FILE (l->data)); g_debug ("[qfu,device-selection] device found: %s", path); if (!first_selection) first_selection = g_object_ref (l->data); if (!preferred_selection && !g_strcmp0 (path, self->priv->preferred_devices[device_type])) preferred_selection = g_object_ref (l->data); g_free (path); } g_list_free_full (list, (GDestroyNotify) g_object_unref); if (preferred_selection) { path = g_file_get_path (preferred_selection); g_debug ("[qfu,device-selection] using preferred device: %s", path); g_free (path); if (first_selection) g_object_unref (first_selection); return preferred_selection; } if (first_selection) { path = g_file_get_path (first_selection); g_debug ("[qfu,device-selection] using automatically selected device: %s", path); g_free (path); return first_selection; } g_debug ("[qfu,device-selection] couldn't find any device to use"); return NULL; } GFile * qfu_device_selection_get_single_cdc_wdm (QfuDeviceSelection *self) { return device_selection_get_single (self, QFU_UDEV_HELPER_DEVICE_TYPE_CDC_WDM); } GFile * qfu_device_selection_get_single_tty (QfuDeviceSelection *self) { return device_selection_get_single (self, QFU_UDEV_HELPER_DEVICE_TYPE_TTY); } /******************************************************************************/ static GList * device_selection_get_multiple (QfuDeviceSelection *self, QfuUdevHelperDeviceType device_type) { GFile *preferred_selection = NULL; GList *list, *l; gchar *path; g_debug ("[qfu,device-selection] multiple %s devices requested in sysfs path '%s'", qfu_udev_helper_device_type_to_string (device_type), self->priv->sysfs_path); list = qfu_udev_helper_list_devices (device_type, self->priv->sysfs_path); for (l = list; l; l = g_list_next (l)) { path = g_file_get_path (G_FILE (l->data)); g_debug ("[qfu,device-selection] device found: %s", path); if (!preferred_selection && !g_strcmp0 (path, self->priv->preferred_devices[device_type])) preferred_selection = g_object_ref (l->data); g_free (path); } /* If we have a preferred device selected, we will only include that one in the output list */ if (preferred_selection) { path = g_file_get_path (preferred_selection); g_debug ("[qfu,device-selection] using only preferred device: %s", path); g_free (path); g_list_free_full (list, (GDestroyNotify) g_object_unref); return g_list_append (NULL, preferred_selection); } if (list) return list; g_debug ("[qfu,device-selection] couldn't find any device to use"); return NULL; } GList * qfu_device_selection_get_multiple_ttys (QfuDeviceSelection *self) { return device_selection_get_multiple (self, QFU_UDEV_HELPER_DEVICE_TYPE_TTY); } /******************************************************************************/ GFile * qfu_device_selection_wait_for_cdc_wdm_finish (QfuDeviceSelection *self, GAsyncResult *res, GError **error) { return G_FILE (g_task_propagate_pointer (G_TASK (res), error)); } GFile * qfu_device_selection_wait_for_tty_finish (QfuDeviceSelection *self, GAsyncResult *res, GError **error) { return G_FILE (g_task_propagate_pointer (G_TASK (res), error)); } static void wait_for_device_ready (gpointer unused, GAsyncResult *res, GTask *task) { GError *error = NULL; GFile *file; file = qfu_udev_helper_wait_for_device_finish (res, &error); if (!file) g_task_return_error (task, error); else g_task_return_pointer (task, file, (GDestroyNotify) g_object_unref); g_object_unref (task); } void qfu_device_selection_wait_for_cdc_wdm (QfuDeviceSelection *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, cancellable, callback, user_data); qfu_udev_helper_wait_for_device (QFU_UDEV_HELPER_DEVICE_TYPE_CDC_WDM, self->priv->sysfs_path, cancellable, (GAsyncReadyCallback) wait_for_device_ready, task); } void qfu_device_selection_wait_for_tty (QfuDeviceSelection *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, cancellable, callback, user_data); qfu_udev_helper_wait_for_device (QFU_UDEV_HELPER_DEVICE_TYPE_TTY, self->priv->sysfs_path, cancellable, (GAsyncReadyCallback) wait_for_device_ready, task); } /******************************************************************************/ QfuDeviceSelection * qfu_device_selection_new (const gchar *preferred_cdc_wdm, const gchar *preferred_tty, guint16 preferred_vid, guint16 preferred_pid, guint preferred_busnum, guint preferred_devnum, GError **error) { QfuDeviceSelection *self; guint n_selections; /* Note: pid and busnum may be zero */ n_selections = (!!preferred_cdc_wdm + !!preferred_tty + !!preferred_vid + !!preferred_devnum); if (!n_selections) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No device selected"); return NULL; } if (n_selections > 1) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Only one device selection option may be provided"); return NULL; } /* Selection valid, create object */ self = g_object_new (QFU_TYPE_DEVICE_SELECTION, NULL); /* Store inputs */ self->priv->preferred_devices[QFU_UDEV_HELPER_DEVICE_TYPE_CDC_WDM] = g_strdup (preferred_cdc_wdm); self->priv->preferred_devices[QFU_UDEV_HELPER_DEVICE_TYPE_TTY] = g_strdup (preferred_tty); self->priv->preferred_vid = preferred_vid; self->priv->preferred_pid = preferred_pid; self->priv->preferred_busnum = preferred_busnum; self->priv->preferred_devnum = preferred_devnum; /* Initialize sysfs path from inputs */ if (preferred_vid || preferred_devnum) self->priv->sysfs_path = qfu_udev_helper_find_by_device_info (preferred_vid, preferred_pid, preferred_busnum, preferred_devnum, error); else if (preferred_cdc_wdm || preferred_tty) self->priv->sysfs_path = qfu_udev_helper_find_by_file_path (preferred_cdc_wdm ? preferred_cdc_wdm : preferred_tty, error); else g_assert_not_reached (); if (!self->priv->sysfs_path) { g_object_unref (self); return NULL; } /* Initialize right away the generic udev monitor for this sysfs path */ self->priv->monitor = qfu_udev_helper_generic_monitor_new (self->priv->sysfs_path); return self; } static void qfu_device_selection_init (QfuDeviceSelection *self) { self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, QFU_TYPE_DEVICE_SELECTION, QfuDeviceSelectionPrivate); } static void finalize (GObject *object) { QfuDeviceSelection *self = QFU_DEVICE_SELECTION (object); guint i; if (self->priv->monitor) qfu_udev_helper_generic_monitor_free (self->priv->monitor); for (i = 0; i < QFU_UDEV_HELPER_DEVICE_TYPE_LAST; i++) g_free (self->priv->preferred_devices[i]); g_free (self->priv->sysfs_path); G_OBJECT_CLASS (qfu_device_selection_parent_class)->finalize (object); } static void qfu_device_selection_class_init (QfuDeviceSelectionClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (QfuDeviceSelectionPrivate)); object_class->finalize = finalize; }