>From 4ffa5693bcaf283bc3c8ea7808226eb6e92aaaf5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= <bjorn@mork.no>
Date: Mon, 4 Apr 2016 14:44:01 +0200
Subject: [PATCH 1/2] libqmi: support MBIM EXT_QMUX service
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Bjørn Mork <bjorn@mork.no>
---
 configure.ac                 |  13 ++++
 src/libqmi-glib/Makefile.am  |   4 +-
 src/libqmi-glib/qmi-device.c | 156 ++++++++++++++++++++++++++++++++++++++++++-
 src/libqmi-glib/qmi-device.h |   4 +-
 4 files changed, 172 insertions(+), 5 deletions(-)

diff --git a/configure.ac b/configure.ac
index 3e05993..fe6225e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -95,6 +95,18 @@ fi
 
 AM_CONDITIONAL([QMI_USERNAME_ENABLED], [test "x$QMI_USERNAME_ENABLED" = "xyes"])
 
+# MBIM QMUX service support
+AC_ARG_ENABLE(mbim-qmux,
+              AS_HELP_STRING([--enable-mbim-qmux], [support QMI over MBIM QMUX service]))
+if test -n "$enable_mbim_qmux"; then
+    PKG_CHECK_MODULES(MBIM, mbim-glib >= 1.13)
+    MBIM_CFLAGS="$MBIM_CFLAGS -DMBIM_QMUX"
+    AC_SUBST(MBIM_CFLAGS)
+    AC_SUBST(MBIM_LIBS)
+fi
+
+AM_CONDITIONAL(MBIM_QMUX, [test -n "$MBIM_CFLAGS"])
+
 # udev base directory
 AC_ARG_WITH(udev-base-dir, AS_HELP_STRING([--with-udev-base-dir=DIR], [where udev base directory is]))
 if test -n "$with_udev_base_dir" ; then
@@ -146,4 +158,5 @@ echo "
     udev base directory:  ${UDEV_BASE_DIR}
     Documentation:        ${enable_gtk_doc}
     QMI username:         ${QMI_USERNAME_ENABLED} (${QMI_USERNAME})
+    QMUX over MBIM:       ${enable_mbim_qmux}
 "
diff --git a/src/libqmi-glib/Makefile.am b/src/libqmi-glib/Makefile.am
index b24e3ae..6ff4e66 100644
--- a/src/libqmi-glib/Makefile.am
+++ b/src/libqmi-glib/Makefile.am
@@ -5,6 +5,7 @@ lib_LTLIBRARIES = libqmi-glib.la
 
 libqmi_glib_la_CPPFLAGS = \
 	$(GLIB_CFLAGS) \
+	$(MBIM_CFLAGS) \
 	-I$(top_srcdir) \
 	-I$(top_builddir) \
 	-I$(top_srcdir)/src/libqmi-glib \
@@ -39,7 +40,8 @@ libqmi_glib_la_SOURCES = \
 
 libqmi_glib_la_LIBADD = \
 	${top_builddir}/src/libqmi-glib/generated/libqmi-glib-generated.la \
-	$(GLIB_LIBS)
+	$(GLIB_LIBS) \
+	$(MBIM_LIBS)
 
 libqmi_glib_la_LDFLAGS = \
 	-version-info $(QMI_GLIB_LT_CURRENT):$(QMI_GLIB_LT_REVISION):$(QMI_GLIB_LT_AGE)
diff --git a/src/libqmi-glib/qmi-device.c b/src/libqmi-glib/qmi-device.c
index c4abbbc..6c46bca 100644
--- a/src/libqmi-glib/qmi-device.c
+++ b/src/libqmi-glib/qmi-device.c
@@ -32,6 +32,10 @@
 #include <gio/gunixoutputstream.h>
 #include <gio/gunixsocketaddress.h>
 
+#ifdef MBIM_QMUX
+#include <libmbim-glib.h>
+#endif
+
 #include "qmi-device.h"
 #include "qmi-message.h"
 #include "qmi-ctl.h"
@@ -92,6 +96,10 @@ struct _QmiDevicePrivate {
     gchar *path_display;
     gboolean no_file_check;
     gchar *proxy_path;
+    gboolean mbim_qmux;
+#ifdef MBIM_QMUX
+    MbimDevice *mbimdev;
+#endif
 
     /* WWAN interface */
     gboolean no_wwan_check;
@@ -1689,7 +1697,6 @@ input_ready_cb (GInputStream *istream,
         self->priv->buffer = g_byte_array_sized_new (r);
     g_byte_array_append (self->priv->buffer, buffer, r);
 
-    /* Try to parse input messages */
     parse_response (self);
 
     return TRUE;
@@ -2134,6 +2141,56 @@ internal_proxy_open_ready (QmiClientCtl *client_ctl,
     device_open_context_step (ctx);
 }
 
+#ifdef MBIM_QMUX
+static void
+mbim_device_open_ready (MbimDevice *dev,
+                        GAsyncResult *res,
+                        DeviceOpenContext *ctx)
+{
+        GError *error = NULL;
+
+        if (!mbim_device_open_finish (dev, res, &error)) {
+            g_simple_async_result_take_error (ctx->result, error);
+            device_open_context_complete_and_free (ctx);
+            return;
+        }
+        g_debug ("[%s] MBIM device Open..",
+                ctx->self->priv->path_display);
+ 
+        /* Go on */
+        ctx->step++;
+        device_open_context_step (ctx);
+        return;
+}
+
+static void
+mbim_device_new_ready (GObject *source,
+                       GAsyncResult *res,
+                       DeviceOpenContext *ctx)
+{
+    MbimDeviceOpenFlags open_flags = MBIM_DEVICE_OPEN_FLAGS_NONE;
+    GError *error = NULL;
+    MbimDevice *device;
+
+    if (ctx->flags & QMI_DEVICE_OPEN_FLAGS_PROXY)
+        open_flags |= MBIM_DEVICE_OPEN_FLAGS_PROXY;
+    device = mbim_device_new_finish (res, &error);
+    if (!device) {
+        g_simple_async_result_take_error (ctx->result, error);
+        device_open_context_complete_and_free (ctx);
+        return;
+    }
+    ctx->self->priv->mbimdev = device;
+
+    mbim_device_open_full(device,
+                        open_flags,
+                        30,
+                        ctx->cancellable,
+                        (GAsyncReadyCallback)mbim_device_open_ready,
+                        ctx);
+}
+#endif
+
 static void
 create_iostream_ready (QmiDevice *self,
                        GAsyncResult *res,
@@ -2166,6 +2223,20 @@ device_open_context_step (DeviceOpenContext *ctx)
         /* Fall down */
 
     case DEVICE_OPEN_CONTEXT_STEP_CREATE_IOSTREAM:
+#ifdef MBIM_QMUX
+        if (ctx->flags & QMI_DEVICE_OPEN_FLAGS_MBIM) {
+            GFile *file;
+            
+            ctx->self->priv->mbim_qmux = TRUE;
+            file = g_file_new_for_path (ctx->self->priv->path);
+            mbim_device_new (file,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)mbim_device_new_ready,
+                             ctx);
+            g_object_unref (file);
+            return;
+        }
+#endif
         create_iostream (ctx->self,
                          !!(ctx->flags & QMI_DEVICE_OPEN_FLAGS_PROXY),
                          (GAsyncReadyCallback)create_iostream_ready,
@@ -2174,7 +2245,8 @@ device_open_context_step (DeviceOpenContext *ctx)
 
     case DEVICE_OPEN_CONTEXT_STEP_FLAGS_PROXY:
         /* Initialize communication with proxy? */
-        if (ctx->flags & QMI_DEVICE_OPEN_FLAGS_PROXY) {
+        if (ctx->flags & QMI_DEVICE_OPEN_FLAGS_PROXY &&
+            !(ctx->flags & QMI_DEVICE_OPEN_FLAGS_MBIM)) {
             QmiMessageCtlInternalProxyOpenInput *input;
 
             input = qmi_message_ctl_internal_proxy_open_input_new ();
@@ -2369,6 +2441,21 @@ destroy_iostream (QmiDevice *self,
     return TRUE;
 }
 
+#ifdef MBIM_QMUX
+static void
+mbim_device_close_ready (MbimDevice   *dev,
+                    GAsyncResult *res)
+{
+    GError *error = NULL;
+
+    if (!mbim_device_close_finish (dev, res, &error)) {
+        g_printerr ("error: couldn't close device: %s", error->message);
+        g_error_free (error);
+    } else
+        g_debug ("Device closed");
+}
+#endif
+
 /**
  * qmi_device_close:
  * @self: a #QmiDevice
@@ -2386,6 +2473,15 @@ qmi_device_close (QmiDevice *self,
 {
     g_return_val_if_fail (QMI_IS_DEVICE (self), FALSE);
 
+#ifdef MBIM_QMUX
+    if (self->priv->mbim_qmux)
+        mbim_device_close (self->priv->mbimdev,
+                        15,
+                        NULL, 
+                        (GAsyncReadyCallback) mbim_device_close_ready,
+                        NULL);
+    else 
+#endif
     if (!destroy_iostream (self, error)) {
         g_prefix_error (error, "Cannot close QMI device: ");
         return FALSE;
@@ -2394,6 +2490,42 @@ qmi_device_close (QmiDevice *self,
     return TRUE;
 }
 
+#ifdef MBIM_QMUX
+static void
+mbim_device_command_ready (MbimDevice   *dev,
+                        GAsyncResult *res,
+    QmiDevice *qmidev)
+{
+        MbimMessage *response;
+        GError *error = NULL;
+        const guint8 *buf;
+        guint32 len;
+ 
+        response = mbim_device_command_finish (dev, res, &error);
+        if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
+            g_prefix_error (&error, "MBIM error: ");
+            // transaction_complete_and_free (tr, NULL, error);
+            g_error_free (error);
+            mbim_message_unref (response);
+            return;
+        }
+
+        g_debug ("[%s] Received MBIM message\n", qmidev->priv->path_display);
+
+        
+        /* get the information buffer */
+        buf = mbim_message_command_done_get_raw_information_buffer (response, &len);
+        if (!G_UNLIKELY (qmidev->priv->buffer))
+            qmidev->priv->buffer = g_byte_array_sized_new (len);
+        g_byte_array_append (qmidev->priv->buffer, buf, len);
+
+        /* and parse it as QMI */
+        parse_response(qmidev);
+        mbim_message_unref (response);
+        return;
+}
+#endif
+
 /*****************************************************************************/
 /* Command */
 
@@ -2462,7 +2594,7 @@ qmi_device_command (QmiDevice *self,
     tr = transaction_new (self, message, cancellable, callback, user_data);
 
     /* Device must be open */
-    if (!self->priv->istream || !self->priv->ostream) {
+    if ((!self->priv->istream || !self->priv->ostream) && !self->priv->mbim_qmux) {
         error = g_error_new (QMI_CORE_ERROR,
                              QMI_CORE_ERROR_WRONG_STATE,
                              "Device must be open to send commands");
@@ -2531,6 +2663,24 @@ qmi_device_command (QmiDevice *self,
         g_free (printable);
     }
 
+#ifdef MBIM_QMUX
+    /* wrap QMUX in MBIM? */
+    if (self->priv->mbim_qmux) {
+        MbimMessage *mbim;
+
+        mbim = (mbim_message_qmi_msg_set_new (raw_message_len, raw_message, &error));
+        mbim_device_command (self->priv->mbimdev,
+                             mbim,
+                             30,
+                             NULL, /* cancellable */
+                             (GAsyncReadyCallback)mbim_device_command_ready,
+                             self);
+        g_debug ("[%s] Message sent as MBIM\n", self->priv->path_display);
+        
+        /* FIXME: check errors, set proper MBIM TID */
+        return;
+     }
+#endif
     if (!g_output_stream_write_all (self->priv->ostream,
                                     raw_message,
                                     raw_message_len,
diff --git a/src/libqmi-glib/qmi-device.h b/src/libqmi-glib/qmi-device.h
index 58651e1..957efac 100644
--- a/src/libqmi-glib/qmi-device.h
+++ b/src/libqmi-glib/qmi-device.h
@@ -97,6 +97,7 @@ gboolean      qmi_device_is_open          (QmiDevice *self);
  * @QMI_DEVICE_OPEN_FLAGS_NET_QOS_HEADER: set network port to transmit/receive QoS headers; mutually exclusive with @QMI_DEVICE_OPEN_FLAGS_NET_NO_QOS_HEADER
  * @QMI_DEVICE_OPEN_FLAGS_NET_NO_QOS_HEADER: set network port to not transmit/receive QoS headers; mutually exclusive with @QMI_DEVICE_OPEN_FLAGS_NET_QOS_HEADER
  * @QMI_DEVICE_OPEN_FLAGS_PROXY: Try to open the port through the 'qmi-proxy'.
+ * @QMI_DEVICE_OPEN_FLAGS_MBIM: open an MBIM port with QMUX tunneling service
  *
  * Flags to specify which actions to be performed when the device is open.
  */
@@ -108,7 +109,8 @@ typedef enum {
     QMI_DEVICE_OPEN_FLAGS_NET_RAW_IP        = 1 << 3,
     QMI_DEVICE_OPEN_FLAGS_NET_QOS_HEADER    = 1 << 4,
     QMI_DEVICE_OPEN_FLAGS_NET_NO_QOS_HEADER = 1 << 5,
-    QMI_DEVICE_OPEN_FLAGS_PROXY             = 1 << 6
+    QMI_DEVICE_OPEN_FLAGS_PROXY             = 1 << 6,
+    QMI_DEVICE_OPEN_FLAGS_MBIM              = 1 << 7
 } QmiDeviceOpenFlags;
 
 void         qmi_device_open        (QmiDevice *self,
-- 
2.1.4