aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@aleksander.es>2016-12-05 19:28:36 +0100
committerAleksander Morgado <aleksander@aleksander.es>2017-01-16 11:24:14 +0100
commitd613f8ac4f55ed918ba6bd916f31cfac0d036d9f (patch)
treef271c74433911e39cd5a1583644d01005939c5a0
parentd6fccedd7b67567b1bef9323391364c6721feb19 (diff)
downloadexternal_libqmi-d613f8ac4f55ed918ba6bd916f31cfac0d036d9f.zip
external_libqmi-d613f8ac4f55ed918ba6bd916f31cfac0d036d9f.tar.gz
external_libqmi-d613f8ac4f55ed918ba6bd916f31cfac0d036d9f.tar.bz2
qmi-firmware-update: allow specifying devices by vid:pid or bus:dev
-rw-r--r--src/qmi-firmware-update/qfu-main.c196
-rw-r--r--src/qmi-firmware-update/qfu-udev-helpers.c501
-rw-r--r--src/qmi-firmware-update/qfu-udev-helpers.h37
-rw-r--r--src/qmi-firmware-update/qfu-updater.c9
4 files changed, 589 insertions, 154 deletions
diff --git a/src/qmi-firmware-update/qfu-main.c b/src/qmi-firmware-update/qfu-main.c
index df157fa..7e42170 100644
--- a/src/qmi-firmware-update/qfu-main.c
+++ b/src/qmi-firmware-update/qfu-main.c
@@ -33,6 +33,7 @@
#include <libqmi-glib.h>
#include "qfu-operation.h"
+#include "qfu-udev-helpers.h"
#define PROGRAM_NAME "qmi-firmware-update"
#define PROGRAM_VERSION PACKAGE_VERSION
@@ -40,6 +41,12 @@
/*****************************************************************************/
/* Options */
+/* Generic device selections */
+static guint busnum;
+static guint devnum;
+static guint16 vid;
+static guint16 pid;
+
/* Update */
static gboolean action_update_flag;
static gchar *device_str;
@@ -54,7 +61,7 @@ static gboolean action_update_qdl_flag;
static gchar *serial_str;
/* Verify */
-static gboolean action_verify_flag;;
+static gboolean action_verify_flag;
/* Main */
static gchar **image_strv;
@@ -63,6 +70,113 @@ static gboolean silent_flag;
static gboolean version_flag;
static gboolean help_flag;
+static gboolean
+parse_busnum_devnum (const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error)
+{
+ gchar **strv;
+ gint busnum_idx = -1;
+ gint devnum_idx = 0;
+ gulong aux;
+ gboolean result = FALSE;
+
+ strv = g_strsplit (value, ":", -1);
+ g_assert (strv[0]);
+ if (strv[1]) {
+ busnum_idx = 0;
+ devnum_idx = 1;
+ if (strv[2]) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "invalid busnum-devnum string: too many fields");
+ goto out;
+ }
+ }
+
+ if (busnum_idx != -1) {
+ aux = strtoul (strv[busnum_idx], NULL, 10);
+ if (aux == 0 || aux > G_MAXUINT) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "invalid bus number: %s", strv[busnum_idx]);
+ goto out;
+ }
+ busnum = (guint) aux;
+ }
+
+ aux = strtoul (strv[devnum_idx], NULL, 10);
+ if (aux == 0 || aux > G_MAXUINT) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "invalid dev number: %s", strv[devnum_idx]);
+ goto out;
+ }
+ devnum = (guint) aux;
+ result = TRUE;
+
+out:
+ g_strfreev (strv);
+ return result;
+}
+
+static gboolean
+parse_vid_pid (const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error)
+{
+ gchar **strv;
+ gint vid_idx = 0;
+ gint pid_idx = -1;
+ gulong aux;
+ gboolean result = FALSE;
+
+ strv = g_strsplit (value, ":", -1);
+ g_assert (strv[0]);
+ if (strv[1]) {
+ pid_idx = 1;
+ if (strv[2]) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "invalid vid-pid string: too many fields");
+ goto out;
+ }
+ }
+
+ if (pid_idx != -1) {
+ aux = strtoul (strv[pid_idx], NULL, 16);
+ if (aux == 0 || aux > G_MAXUINT16) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "invalid product id: %s", strv[pid_idx]);
+ goto out;
+ }
+ pid = (guint) aux;
+ }
+
+ aux = strtoul (strv[vid_idx], NULL, 16);
+ if (aux == 0 || aux > G_MAXUINT16) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "invalid vendor id: %s", strv[vid_idx]);
+ goto out;
+ }
+ vid = (guint16) aux;
+ result = TRUE;
+
+out:
+ g_strfreev (strv);
+ return result;
+}
+
+static GOptionEntry context_selection_entries[] = {
+ { "busnum-devnum", 'N', 0, G_OPTION_ARG_CALLBACK, &parse_busnum_devnum,
+ "Select device by bus and device number (in decimal).",
+ "[BUS:]DEV"
+ },
+ { "vid-pid", 'D', 0, G_OPTION_ARG_CALLBACK, &parse_vid_pid,
+ "Select device by device vendor and product id (in hexadecimal).",
+ "VID:[PID]"
+ },
+ { NULL }
+};
+
static GOptionEntry context_update_entries[] = {
{ "update", 'u', 0, G_OPTION_ARG_NONE, &action_update_flag,
"Launch firmware update process.",
@@ -239,6 +353,58 @@ print_help (GOptionContext *context)
g_free (str);
}
+static gchar *
+select_path (const char *manual,
+ QfuUdevHelperDeviceType type)
+{
+ gchar *path = NULL;
+ GError *error = NULL;
+ gchar *sysfs_path = NULL;
+ GList *list = NULL;
+
+ if (manual && (vid != 0 || pid != 0)) {
+ g_printerr ("error: cannot specify device path and vid:pid lookup\n");
+ return NULL;
+ }
+
+ if (manual && (busnum != 0 || devnum != 0)) {
+ g_printerr ("error: cannot specify device path and busnum:devnum lookup\n");
+ return NULL;
+ }
+
+ if ((vid != 0 || pid != 0) && (busnum != 0 || devnum != 0)) {
+ g_printerr ("error: cannot specify busnum:devnum and vid:pid lookups\n");
+ return NULL;
+ }
+
+ if (manual) {
+ path = g_strdup (manual);
+ goto out;
+ }
+
+ /* lookup sysfs path */
+ sysfs_path = qfu_udev_helper_find_by_device_info (vid, pid, busnum, devnum, &error);
+ if (!sysfs_path) {
+ g_printerr ("error: %s\n", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ list = qfu_udev_helper_list_devices (type, sysfs_path);
+ if (!list) {
+ g_printerr ("error: no devices found in sysfs path: %s\n", sysfs_path);
+ goto out;
+ }
+
+ path = g_file_get_path (G_FILE (list->data));
+
+out:
+ if (list)
+ g_list_free_full (list, (GDestroyNotify) g_object_unref);
+
+ return path;
+}
+
int main (int argc, char **argv)
{
GError *error = NULL;
@@ -254,6 +420,10 @@ int main (int argc, char **argv)
/* Setup option context, process it and destroy it */
context = g_option_context_new ("- Update firmware in QMI devices");
+ group = g_option_group_new ("selection", "Generic device selection options", "", NULL, NULL);
+ g_option_group_add_entries (group, context_selection_entries);
+ g_option_context_add_group (context, group);
+
group = g_option_group_new ("update", "Update options", "", NULL, NULL);
g_option_group_add_entries (group, context_update_entries);
g_option_context_add_group (context, group);
@@ -311,18 +481,32 @@ int main (int argc, char **argv)
}
/* Run */
- if (action_update_flag)
+ if (action_update_flag) {
+ gchar *path;
+
+ path = select_path (device_str, QFU_UDEV_HELPER_DEVICE_TYPE_CDC_WDM);
+ if (!path)
+ goto out;
+ g_debug ("using cdc-wdm device: %s", path);
result = qfu_operation_update_run ((const gchar **) image_strv,
- device_str,
+ path,
firmware_version_str,
config_version_str,
carrier_str,
device_open_proxy_flag,
device_open_mbim_flag);
- else if (action_update_qdl_flag)
+ g_free (path);
+ } else if (action_update_qdl_flag) {
+ gchar *path;
+
+ path = select_path (serial_str, QFU_UDEV_HELPER_DEVICE_TYPE_TTY);
+ if (!path)
+ goto out;
+ g_debug ("using tty device: %s", path);
result = qfu_operation_update_qdl_run ((const gchar **) image_strv,
- serial_str);
- else if (action_verify_flag)
+ path);
+ g_free (path);
+ } else if (action_verify_flag)
result = qfu_operation_verify_run ((const gchar **) image_strv);
else
g_assert_not_reached ();
diff --git a/src/qmi-firmware-update/qfu-udev-helpers.c b/src/qmi-firmware-update/qfu-udev-helpers.c
index 9fd271c..47656db 100644
--- a/src/qmi-firmware-update/qfu-udev-helpers.c
+++ b/src/qmi-firmware-update/qfu-udev-helpers.c
@@ -19,17 +19,83 @@
* Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
*/
+#include <stdlib.h>
+
#include <gio/gio.h>
#include <gudev/gudev.h>
#include "qfu-udev-helpers.h"
-gchar *
-qfu_udev_helper_get_udev_device_sysfs_path (GUdevDevice *device,
- GError **error)
+static const gchar *tty_subsys_list[] = { "tty", NULL };
+static const gchar *cdc_wdm_subsys_list[] = { "usbmisc", "usb", NULL };
+
+/******************************************************************************/
+
+static GUdevDevice *
+find_udev_device_for_file (GFile *file,
+ GError **error)
+{
+ GUdevClient *client = NULL;
+ GUdevDevice *device = NULL;
+ gchar *basename = NULL;
+ const gchar **subsys_list = NULL;
+ guint i;
+
+ basename = g_file_get_basename (file);
+ if (!basename) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "couldn't get filename");
+ goto out;
+ }
+
+ client = g_udev_client_new (NULL);
+
+ if (g_str_has_prefix (basename, "tty"))
+ subsys_list = tty_subsys_list;
+ else if (g_str_has_prefix (basename, "cdc-wdm"))
+ subsys_list = cdc_wdm_subsys_list;
+ else {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "unknown device file type");
+ goto out;
+ }
+
+ for (i = 0; !device && subsys_list[i]; i++)
+ device = g_udev_client_query_by_subsystem_and_name (client, subsys_list[i], basename);
+
+ if (!device) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "device not found");
+ goto out;
+ }
+
+out:
+ g_free (basename);
+ if (client)
+ g_object_unref (client);
+
+ return device;
+}
+
+static gboolean
+udev_helper_get_udev_device_details (GUdevDevice *device,
+ gchar **out_sysfs_path,
+ guint16 *out_vid,
+ guint16 *out_pid,
+ guint *out_busnum,
+ guint *out_devnum,
+ GError **error)
{
GUdevDevice *parent;
- gchar *sysfs_path;
+ gulong aux;
+
+ if (out_vid)
+ *out_vid = 0;
+ if (out_pid)
+ *out_pid = 0;
+ if (out_sysfs_path)
+ *out_sysfs_path = NULL;
+ if (out_busnum)
+ *out_busnum = 0;
+ if (out_devnum)
+ *out_devnum = 0;
/* We need to look for the parent GUdevDevice which has a "usb_device"
* devtype. */
@@ -49,77 +115,324 @@ qfu_udev_helper_get_udev_device_sysfs_path (GUdevDevice *device,
if (!parent) {
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "couldn't find parent physical USB device");
- return NULL;
+ return FALSE;
+ }
+
+ if (out_sysfs_path)
+ *out_sysfs_path = g_strdup (g_udev_device_get_sysfs_path (parent));
+
+ if (out_vid) {
+ aux = strtoul (g_udev_device_get_property (parent, "ID_VENDOR_ID"), NULL, 16);
+ if (aux <= G_MAXUINT16)
+ *out_vid = (guint16) aux;
+ }
+
+ if (out_pid) {
+ aux = strtoul (g_udev_device_get_property (parent, "ID_MODEL_ID"), NULL, 16);
+ if (aux <= G_MAXUINT16)
+ *out_pid = (guint16) aux;
+ }
+
+ if (out_busnum) {
+ aux = strtoul (g_udev_device_get_property (parent, "BUSNUM"), NULL, 10);
+ if (aux <= G_MAXUINT)
+ *out_busnum = (guint16) aux;
+ }
+
+ if (out_devnum) {
+ aux = strtoul (g_udev_device_get_property (parent, "DEVNUM"), NULL, 10);
+ if (aux <= G_MAXUINT)
+ *out_devnum = (guint16) aux;
}
- sysfs_path = g_strdup (g_udev_device_get_sysfs_path (parent));
g_object_unref (parent);
- return sysfs_path;
+ return TRUE;
+}
+
+static gboolean
+udev_helper_get_udev_interface_details (GUdevDevice *device,
+ gchar **out_driver,
+ GError **error)
+{
+ GUdevDevice *parent;
+
+ /* 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 FALSE;
+ }
+
+ if (out_driver)
+ *out_driver = g_strdup (g_udev_device_get_driver (parent));
+
+ g_object_unref (parent);
+ return TRUE;
}
+/******************************************************************************/
+
gchar *
-qfu_udev_helper_get_sysfs_path (GFile *file,
- const gchar *const *subsys,
- GError **error)
+qfu_udev_helper_find_by_file (GFile *file,
+ GError **error)
{
- guint i;
- GUdevClient *udev;
+ GUdevDevice *device;
gchar *sysfs_path = NULL;
- gchar *basename;
- gboolean matched = FALSE;
- /* Get the filename */
- basename = g_file_get_basename (file);
- if (!basename) {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "couldn't get filename");
- return NULL;
+ device = find_udev_device_for_file (file, error);
+ if (device) {
+ udev_helper_get_udev_device_details (device,
+ &sysfs_path, NULL, NULL, NULL, NULL,
+ error);
+ g_object_unref (device);
}
+ return sysfs_path;
+}
- udev = g_udev_client_new (NULL);
+/******************************************************************************/
- /* Note: we'll only get devices reported in either one subsystem or the
- * other, never in both */
- for (i = 0; !matched && subsys[i]; i++) {
- GList *devices, *iter;
+static gboolean
+udev_helper_device_already_added (GPtrArray *ptr,
+ const gchar *sysfs_path)
+{
+ guint i;
- devices = g_udev_client_query_by_subsystem (udev, subsys[i]);
- for (iter = devices; !matched && iter; iter = g_list_next (iter)) {
- const gchar *name;
- GUdevDevice *device;
+ for (i = 0; i < ptr->len; i++) {
+ if (g_strcmp0 (g_ptr_array_index (ptr, i), sysfs_path) == 0)
+ return TRUE;
+ }
- device = G_UDEV_DEVICE (iter->data);
- name = g_udev_device_get_name (device);
- if (g_strcmp0 (name, basename) != 0)
- continue;
+ return FALSE;
+}
- /* We'll halt the search once this has been processed */
- matched = TRUE;
- sysfs_path = qfu_udev_helper_get_udev_device_sysfs_path (device, error);
+static GPtrArray *
+udev_helper_find_by_device_info_in_subsystem (GPtrArray *sysfs_paths,
+ GUdevClient *udev,
+ const gchar *subsystem,
+ guint16 vid,
+ guint16 pid,
+ guint busnum,
+ guint devnum)
+{
+ GList *devices;
+ GList *iter;
+
+ devices = g_udev_client_query_by_subsystem (udev, subsystem);
+ for (iter = devices; iter; iter = g_list_next (iter)) {
+ GUdevDevice *device;
+ guint16 device_vid = 0;
+ guint16 device_pid = 0;
+ guint device_busnum = 0;
+ guint device_devnum = 0;
+ gchar *device_sysfs_path = NULL;
+
+ device = G_UDEV_DEVICE (iter->data);
+
+ if (udev_helper_get_udev_device_details (device,
+ &device_sysfs_path,
+ &device_vid,
+ &device_pid,
+ &device_busnum,
+ &device_devnum,
+ NULL)) {
+ if ((vid == 0 || vid == device_vid) &&
+ (pid == 0 || pid == device_pid) &&
+ (busnum == 0 || busnum == device_busnum) &&
+ (devnum == 0 || devnum == device_devnum) &&
+ (!udev_helper_device_already_added (sysfs_paths, device_sysfs_path)))
+ g_ptr_array_add (sysfs_paths, device_sysfs_path);
+ else
+ g_free (device_sysfs_path);
}
- g_list_free_full (devices, (GDestroyNotify) g_object_unref);
+
+ g_object_unref (device);
}
+ g_list_free (devices);
+ return sysfs_paths;
+}
- if (!matched) {
- g_assert (!sysfs_path);
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "couldn't find device");
+gchar *
+qfu_udev_helper_find_by_device_info (guint16 vid,
+ guint16 pid,
+ guint busnum,
+ guint devnum,
+ GError **error)
+{
+ GUdevClient *udev;
+ guint i;
+ GPtrArray *sysfs_paths;
+ GString *match_str;
+ gchar *sysfs_path = NULL;
+
+ sysfs_paths = g_ptr_array_new ();
+ udev = g_udev_client_new (NULL);
+ match_str = g_string_new ("");
+
+ if (vid != 0)
+ g_string_append_printf (match_str, "vid 0x%04x", vid);
+ if (pid != 0)
+ g_string_append_printf (match_str, "%spid 0x%04x", match_str->len > 0 ? ", " : "", pid);
+ if (busnum != 0)
+ g_string_append_printf (match_str, "%sbus %03u", match_str->len > 0 ? ", " : "", busnum);
+ if (devnum != 0)
+ g_string_append_printf (match_str, "%sdev %03u", match_str->len > 0 ? ", " : "", devnum);
+ g_assert (match_str->len > 0);
+
+ for (i = 0; tty_subsys_list[i]; i++)
+ sysfs_paths = udev_helper_find_by_device_info_in_subsystem (sysfs_paths,
+ udev,
+ tty_subsys_list[i],
+ vid, pid, busnum, devnum);
+
+ for (i = 0; cdc_wdm_subsys_list[i]; i++)
+ sysfs_paths = udev_helper_find_by_device_info_in_subsystem (sysfs_paths,
+ udev,
+ cdc_wdm_subsys_list[i],
+ vid, pid, busnum, devnum);
+
+ for (i = 0; i < sysfs_paths->len; i++)
+ g_debug ("[%s] sysfs path: %s", match_str->str, (gchar *) g_ptr_array_index (sysfs_paths, i));
+
+ if (sysfs_paths->len == 0) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "no device found with matching criteria: %s",
+ match_str->str);
+ goto out;
}
- g_free (basename);
+ if (sysfs_paths->len > 1) {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "multiple devices (%u) found with matching criteria: %s",
+ sysfs_paths->len, match_str->str);
+ goto out;
+ }
+
+ sysfs_path = g_strdup (g_ptr_array_index (sysfs_paths, 0));
+
+out:
+
+ g_ptr_array_unref (sysfs_paths);
+ g_string_free (match_str, TRUE);
g_object_unref (udev);
+
return sysfs_path;
}
/******************************************************************************/
+static GFile *
+device_matches_sysfs_and_type (GUdevDevice *device,
+ const gchar *sysfs_path,
+ QfuUdevHelperDeviceType type)
+{
+ GFile *file = NULL;
+ gchar *device_sysfs_path = NULL;
+ gchar *device_driver = NULL;
+ gchar *device_path = NULL;
+
+ if (!udev_helper_get_udev_device_details (device,
+ &device_sysfs_path, NULL, NULL, NULL, NULL,
+ NULL))
+ if (!device_sysfs_path)
+ goto out;
+
+ if (g_strcmp0 (device_sysfs_path, sysfs_path) != 0)
+ goto out;
+
+ if (!udev_helper_get_udev_interface_details (device,
+ &device_driver,
+ NULL))
+ goto out;
+
+ switch (type) {
+ case QFU_UDEV_HELPER_DEVICE_TYPE_TTY:
+ if (g_strcmp0 (device_driver, "qcserial") != 0)
+ goto out;
+ break;
+ case QFU_UDEV_HELPER_DEVICE_TYPE_CDC_WDM:
+ if (g_strcmp0 (device_driver, "qmi_wwan") != 0 && g_strcmp0 (device_driver, "cdc_mbim") != 0)
+ goto out;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ device_path = g_strdup_printf ("/dev/%s", g_udev_device_get_name (device));
+ file = g_file_new_for_path (device_path);
+ g_free (device_path);
+
+out:
+ g_free (device_sysfs_path);
+ g_free (device_driver);
+ return file;
+}
+
+GList *
+qfu_udev_helper_list_devices (QfuUdevHelperDeviceType device_type,
+ const gchar *sysfs_path)
+{
+ GUdevClient *udev;
+ const gchar **subsys_list = NULL;
+ guint i;
+ GList *files = NULL;
+
+ udev = g_udev_client_new (NULL);
+
+ switch (device_type) {
+ case QFU_UDEV_HELPER_DEVICE_TYPE_TTY:
+ subsys_list = tty_subsys_list;
+ break;
+ case QFU_UDEV_HELPER_DEVICE_TYPE_CDC_WDM:
+ subsys_list = cdc_wdm_subsys_list;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ for (i = 0; subsys_list[i]; i++) {
+ GList *devices, *iter;
+
+ devices = g_udev_client_query_by_subsystem (udev, subsys_list[i]);
+ for (iter = devices; iter; iter = g_list_next (iter)) {
+ GFile *file;
+
+ file = device_matches_sysfs_and_type (G_UDEV_DEVICE (iter->data), sysfs_path, device_type);
+ if (file)
+ files = g_list_prepend (files, file);
+ g_object_unref (G_OBJECT (iter->data));
+ }
+ g_list_free (devices);
+ }
+
+ g_object_unref (udev);
+ return files;
+}
+
+/******************************************************************************/
+
#define WAIT_FOR_DEVICE_TIMEOUT_SECS 60
typedef struct {
- QfuUdevHelperWaitForDeviceType device_type;
- GUdevClient *udev;
- gchar *sysfs_path;
- guint timeout_id;
- gulong uevent_id;
- gulong cancellable_id;
+ QfuUdevHelperDeviceType device_type;
+ GUdevClient *udev;
+ gchar *sysfs_path;
+ guint timeout_id;
+ gulong uevent_id;
+ gulong cancellable_id;
} WaitForDeviceContext;
static void
@@ -141,39 +454,6 @@ qfu_udev_helper_wait_for_device_finish (GAsyncResult *res,
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,
@@ -181,42 +461,18 @@ handle_uevent (GUdevClient *client,
GTask *task)
{
WaitForDeviceContext *ctx;
- gchar *sysfs_path = NULL;
- gchar *driver = NULL;
- gchar *path;
+ GFile *file;
ctx = (WaitForDeviceContext *) g_task_get_task_data (task);
g_debug ("[qfu-udev] event: %s %s", action, g_udev_device_get_name (device));
if (!g_str_equal (action, "add") && !g_str_equal (action, "move") && !g_str_equal (action, "change"))
- goto out;
-
- sysfs_path = qfu_udev_helper_get_udev_device_sysfs_path (device, NULL);
- if (!sysfs_path)
- goto out;
- g_debug ("[qfu-udev] sysfs path: %s", sysfs_path);
-
- if (g_strcmp0 (sysfs_path, ctx->sysfs_path) != 0)
- goto out;
-
- driver = udev_helper_get_udev_device_driver (device, NULL);
- if (!driver)
- goto out;
- g_debug ("[qfu-udev] driver: %s", driver);
+ return;
- 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, "qmi_wwan") != 0 && g_strcmp0 (driver, "cdc_mbim") != 0)
- goto out;
- break;
- default:
- g_assert_not_reached ();
- }
+ file = device_matches_sysfs_and_type (device, ctx->sysfs_path, ctx->device_type);
+ if (!file)
+ return;
g_debug ("[qfu-udev] waiting device matched");
@@ -230,14 +486,7 @@ handle_uevent (GUdevClient *client,
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);
+ g_task_return_pointer (task, file, g_object_unref);
}
static gboolean
@@ -288,14 +537,12 @@ wait_for_device_cancelled (GCancellable *cancellable,
}
void
-qfu_udev_helper_wait_for_device (QfuUdevHelperWaitForDeviceType device_type,
- const gchar *sysfs_path,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+qfu_udev_helper_wait_for_device (QfuUdevHelperDeviceType device_type,
+ const gchar *sysfs_path,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- static const gchar *tty_subsys_monitor[] = { "tty", NULL };
- static const gchar *cdc_wdm_subsys_monitor[] = { "usbmisc", "usb", NULL };
GTask *task;
WaitForDeviceContext *ctx;
@@ -303,10 +550,10 @@ qfu_udev_helper_wait_for_device (QfuUdevHelperWaitForDeviceType device_type,
ctx->device_type = device_type;
ctx->sysfs_path = g_strdup (sysfs_path);
- 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);
+ if (ctx->device_type == QFU_UDEV_HELPER_DEVICE_TYPE_TTY)
+ ctx->udev = g_udev_client_new (tty_subsys_list);
+ else if (ctx->device_type == QFU_UDEV_HELPER_DEVICE_TYPE_CDC_WDM)
+ ctx->udev = g_udev_client_new (cdc_wdm_subsys_list);
else
g_assert_not_reached ();
diff --git a/src/qmi-firmware-update/qfu-udev-helpers.h b/src/qmi-firmware-update/qfu-udev-helpers.h
index 759c5cf..9a26120 100644
--- a/src/qmi-firmware-update/qfu-udev-helpers.h
+++ b/src/qmi-firmware-update/qfu-udev-helpers.h
@@ -27,24 +27,29 @@
G_BEGIN_DECLS
-gchar *qfu_udev_helper_get_udev_device_sysfs_path (GUdevDevice *device,
- GError **error);
-gchar *qfu_udev_helper_get_sysfs_path (GFile *file,
- const gchar *const *subsys,
- GError **error);
+gchar *qfu_udev_helper_find_by_file (GFile *file,
+ GError **error);
+gchar *qfu_udev_helper_find_by_device_info (guint16 vid,
+ guint16 pid,
+ guint busnum,
+ guint devnum,
+ 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);
+ QFU_UDEV_HELPER_DEVICE_TYPE_TTY,
+ QFU_UDEV_HELPER_DEVICE_TYPE_CDC_WDM,
+} QfuUdevHelperDeviceType;
+
+GList *qfu_udev_helper_list_devices (QfuUdevHelperDeviceType device_type,
+ const gchar *sysfs_path);
+
+void qfu_udev_helper_wait_for_device (QfuUdevHelperDeviceType 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 3b3b7c2..a50862c 100644
--- a/src/qmi-firmware-update/qfu-updater.c
+++ b/src/qmi-firmware-update/qfu-updater.c
@@ -52,8 +52,6 @@ struct _QfuUpdaterPrivate {
gboolean device_open_mbim;
};
-static const gchar *cdc_wdm_subsys[] = { "usbmisc", "usb", NULL };
-
/******************************************************************************/
/* Run */
@@ -194,7 +192,7 @@ run_context_step_wait_for_cdc_wdm (GTask *task)
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,
+ qfu_udev_helper_wait_for_device (QFU_UDEV_HELPER_DEVICE_TYPE_CDC_WDM,
ctx->sysfs_path,
g_task_get_cancellable (task),
(GAsyncReadyCallback) wait_for_cdc_wdm_ready,
@@ -399,7 +397,7 @@ run_context_step_wait_for_tty (GTask *task)
g_print ("rebooting in download mode...\n");
g_debug ("[qfu-updater] reset requested, now waiting for TTY device...");
- qfu_udev_helper_wait_for_device (QFU_UDEV_HELPER_WAIT_FOR_DEVICE_TYPE_TTY,
+ qfu_udev_helper_wait_for_device (QFU_UDEV_HELPER_DEVICE_TYPE_TTY,
ctx->sysfs_path,
g_task_get_cancellable (task),
(GAsyncReadyCallback) wait_for_tty_ready,
@@ -798,9 +796,10 @@ run_context_step_usb_info (GTask *task)
self = g_task_get_source_object (task);
g_assert (self->priv->cdc_wdm_file);
+ g_assert (!ctx->sysfs_path);
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);
+ ctx->sysfs_path = qfu_udev_helper_find_by_file (self->priv->cdc_wdm_file, &error);
if (!ctx->sysfs_path) {
g_prefix_error (&error, "couldn't get cdc-wdm device sysfs path: ");
g_task_return_error (task, error);