[PATCH] qmi-firmware-update: support USB3->USB2 mode changes during upgrade

Bjørn Mork bjorn at mork.no
Wed Jan 17 14:02:53 UTC 2018


Modems operating in USB3 SuperSpeed mode may change to
USB2 HighSpeed mode while in boot-and-hold. This changes
the USB port name, causing device matching failure.

Fix by accepting matches on both the original USB(3) port and
the "peer" USB(2) port.

With this, a Sierra Wireless EM7455 can be successfully upgraded
while connected to a USB3 port:

  [qfu,utils] operating mode set successfully...
  [qfu-updater] reset requested successfully...
  [qfu-updater] cleaning up QMI device...
  [/dev/cdc-wdm0] Releasing 'dms' client with flags 'none'...
  [/dev/cdc-wdm0] Unregistered 'dms' client with ID '2'
  [qfu-updater] reset requested, now waiting for TTY device...
  [/dev/cdc-wdm0] Sent message...
  [/dev/cdc-wdm0] Sent message (translated)...
  [/dev/cdc-wdm0] Received message...
  [qfu-udev] event: remove ttyUSB0
  [qfu-udev] event: remove ttyUSB1
  [qfu-udev] event: remove 2-6:1.1
  [qfu-udev] event: remove ttyUSB2
  [qfu-udev] event: remove wwan0
  [qfu-udev] event: remove 2-6:1.0
  [qfu-udev] event: remove 2-6:1.3
  [qfu-udev] event: remove 2-6:1.2
  [qfu-udev] event: remove cdc-wdm0
  [qfu-udev] event: remove 2-6:1.12
  [qfu-udev] event: remove 2-6:1.13
  [qfu-udev] event: remove 2-6
  [qfu-udev] event: add 1-6
  [qfu-udev] event: add 1-6:1.0
  [qfu-udev] event: add ttyUSB0
  [qfu-udev] peer lookup for ttyUSB0: /sys/devices/pci0000:00/0000:00:14.0/usb1/1-0:1.0/usb1-port6 => /sys/devices/pci0000:00/0000:00:14.0/usb1/1-6
  [qfu-udev] waiting device (tty) matched: ttyUSB0
  [qfu-updater] TTY device found: /dev/ttyUSB0
  [qfu-qdl-device] opening TTY: /dev/ttyUSB0
  [qfu-qdl-device] setting terminal in raw mode...
  [qfu,dload-message] sent sdp:
  [qfu-qdl-device] >> 70:00:00 [3, unframed]
  [qfu-qdl-device] >> 7E:70:00:00:14:46:7E [7]
  [qfu-qdl-device] << 7E:0D:16:00:00:00:00:88:4D:7E [10]
  [qfu-qdl-device] << 0D:16:00:00:00:00 [6, unframed]
  ..
  [qfu-updater] no more files to download
  [qfu-updater] QDL reset
  [qfu,qdl-message] sent reset-req:
  [qfu-qdl-device] >> 2D [1, unframed]
  [qfu-qdl-device] >> 7E:2D:9F:0A:7E [5]
  [qfu-updater] now waiting for cdc-wdm device...
  [qfu-udev] event: remove ttyUSB0
  [qfu-udev] event: remove 1-6:1.0
  [qfu-udev] event: remove 1-6
  [qfu-udev] event: add 2-6
  [qfu-udev] event: add 2-6:1.1
  [qfu-udev] event: add 2-6:1.2
  [qfu-udev] event: add 2-6:1.13
  [qfu-udev] event: add 2-6:1.3
  [qfu-udev] event: add 2-6:1.12
  [qfu-udev] event: add cdc-wdm0
  [qfu-udev] waiting device (cdc-wdm) matched: cdc-wdm0
  [qfu-updater] cdc-wdm device found: /dev/cdc-wdm0
  [qfu-updater] waiting some time (5s) before accessing the cdc-wdm device...
  [qfu-udev] event: add 2-6:1.0
  [qfu-udev] event: add ttyUSB0
  [qfu-udev] event: add ttyUSB2
  [qfu-udev] event: add ttyUSB1
  [qfu-udev] event: add wwan0
  [qfu-updater] creating QMI DMS client after upgrade...

Signed-off-by: Bjørn Mork <bjorn at mork.no>
---
 src/qmi-firmware-update/qfu-device-selection.c |  8 +++++++
 src/qmi-firmware-update/qfu-udev-helpers.c     | 33 ++++++++++++++++++++++++++
 src/qmi-firmware-update/qfu-udev-helpers.h     |  3 +++
 3 files changed, 44 insertions(+)

diff --git a/src/qmi-firmware-update/qfu-device-selection.c b/src/qmi-firmware-update/qfu-device-selection.c
index 22c2063f444b..f3576956a346 100644
--- a/src/qmi-firmware-update/qfu-device-selection.c
+++ b/src/qmi-firmware-update/qfu-device-selection.c
@@ -42,6 +42,8 @@ struct _QfuDeviceSelectionPrivate {
 #if defined WITH_UDEV
     /* sysfs path */
     gchar   *sysfs_path;
+    /* peer port sysfs path */
+    gchar   *peer_port;
     /* generic udev monitor */
     QfuUdevHelperGenericMonitor *monitor;
 #endif
@@ -227,6 +229,7 @@ qfu_device_selection_wait_for_cdc_wdm (QfuDeviceSelection  *self,
     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,
+                                     self->priv->peer_port,
                                      cancellable,
                                      (GAsyncReadyCallback) wait_for_device_ready,
                                      task);
@@ -243,6 +246,7 @@ qfu_device_selection_wait_for_tty (QfuDeviceSelection  *self,
     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,
+                                     self->priv->peer_port,
                                      cancellable,
                                      (GAsyncReadyCallback) wait_for_device_ready,
                                      task);
@@ -309,6 +313,9 @@ qfu_device_selection_new (const gchar  *preferred_cdc_wdm,
             return NULL;
         }
 
+        /* look for a peer port */
+        self->priv->peer_port = qfu_udev_helper_find_peer_port (self->priv->sysfs_path, error);
+
         /* Initialize right away the generic udev monitor for this sysfs path */
         self->priv->monitor = qfu_udev_helper_generic_monitor_new (self->priv->sysfs_path);
     }
@@ -333,6 +340,7 @@ finalize (GObject *object)
     if (self->priv->monitor)
         qfu_udev_helper_generic_monitor_free (self->priv->monitor);
     g_free (self->priv->sysfs_path);
+    g_free (self->priv->peer_port);
 #endif
 
     for (i = 0; i < QFU_UDEV_HELPER_DEVICE_TYPE_LAST; i++)
diff --git a/src/qmi-firmware-update/qfu-udev-helpers.c b/src/qmi-firmware-update/qfu-udev-helpers.c
index 7524dc5fce92..27cb89fb30e2 100644
--- a/src/qmi-firmware-update/qfu-udev-helpers.c
+++ b/src/qmi-firmware-update/qfu-udev-helpers.c
@@ -228,6 +228,22 @@ qfu_udev_helper_find_by_file_path (const gchar  *path,
     return sysfs_path;
 }
 
+gchar *
+qfu_udev_helper_find_peer_port (const gchar  *sysfs_path,
+                                GError      **error)
+{
+    gchar *tmp, *path;
+
+    tmp = g_build_filename (sysfs_path, "port", "peer", NULL);
+    path = realpath (tmp, NULL);
+    g_free (tmp);
+    if (!path)
+        return NULL;
+
+    g_debug ("[qfu-udev] peer port for '%s' found: %s", sysfs_path, path);
+    return path;
+}
+
 /******************************************************************************/
 
 static gboolean
@@ -457,6 +473,7 @@ typedef struct {
     QfuUdevHelperDeviceType  device_type;
     GUdevClient             *udev;
     gchar                   *sysfs_path;
+    gchar                   *peer_port;
     guint                    timeout_id;
     gulong                   uevent_id;
     gulong                   cancellable_id;
@@ -471,6 +488,7 @@ wait_for_device_context_free (WaitForDeviceContext *ctx)
 
     g_object_unref (ctx->udev);
     g_free (ctx->sysfs_path);
+    g_free (ctx->peer_port);
     g_slice_free (WaitForDeviceContext, ctx);
 }
 
@@ -496,6 +514,19 @@ handle_uevent (GUdevClient *client,
         return;
 
     file = device_matches_sysfs_and_type (device, ctx->sysfs_path, ctx->device_type);
+    if (!file && ctx->peer_port) {
+        gchar *tmp, *path;
+
+        tmp = g_build_filename (ctx->peer_port, "device", NULL);
+        path = realpath (tmp, NULL);
+        g_free (tmp);
+        if (!path)
+            return;
+
+        file = device_matches_sysfs_and_type (device, path, ctx->device_type);
+        g_debug ("[qfu-udev] peer lookup for %s: %s => %s",  g_udev_device_get_name (device), ctx->peer_port, path);
+        g_free (path);
+    }
     if (!file)
         return;
 
@@ -567,6 +598,7 @@ wait_for_device_cancelled (GCancellable *cancellable,
 void
 qfu_udev_helper_wait_for_device (QfuUdevHelperDeviceType  device_type,
                                  const gchar             *sysfs_path,
+                                 const gchar             *peer_port,
                                  GCancellable            *cancellable,
                                  GAsyncReadyCallback      callback,
                                  gpointer                 user_data)
@@ -577,6 +609,7 @@ qfu_udev_helper_wait_for_device (QfuUdevHelperDeviceType  device_type,
     ctx = g_slice_new0 (WaitForDeviceContext);
     ctx->device_type = device_type;
     ctx->sysfs_path = g_strdup (sysfs_path);
+    ctx->peer_port = g_strdup (peer_port);
 
     if (ctx->device_type == QFU_UDEV_HELPER_DEVICE_TYPE_TTY)
         ctx->udev = g_udev_client_new (tty_subsys_list);
diff --git a/src/qmi-firmware-update/qfu-udev-helpers.h b/src/qmi-firmware-update/qfu-udev-helpers.h
index 6f6eac0d1dca..552e96bd0d5c 100644
--- a/src/qmi-firmware-update/qfu-udev-helpers.h
+++ b/src/qmi-firmware-update/qfu-udev-helpers.h
@@ -42,6 +42,8 @@ gchar *qfu_udev_helper_find_by_file          (GFile        *file,
                                               GError      **error);
 gchar *qfu_udev_helper_find_by_file_path     (const gchar  *path,
                                               GError      **error);
+gchar *qfu_udev_helper_find_peer_port        (const gchar  *sysfs_path,
+                                              GError      **error);
 gchar *qfu_udev_helper_find_by_device_info   (guint16       vid,
                                               guint16       pid,
                                               guint         busnum,
@@ -53,6 +55,7 @@ GList *qfu_udev_helper_list_devices           (QfuUdevHelperDeviceType   device_
 
 void   qfu_udev_helper_wait_for_device        (QfuUdevHelperDeviceType   device_type,
                                                const gchar              *sysfs_path,
+                                               const gchar              *peer_port,
                                                GCancellable             *cancellable,
                                                GAsyncReadyCallback       callback,
                                                gpointer                  user_data);
-- 
2.11.0



More information about the libqmi-devel mailing list