Merge remote-tracking branch 'cros/upstream' into 'cros/master'
diff --git a/.gitignore b/.gitignore
index 8cb5c2a..1c814dc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,12 @@
*.lo
*.la
*.bz2
+*.swp
+tags
+.*DS_Store
+.deps
+.libs
+.__autoconf_trace_data
ChangeLog
INSTALL
@@ -65,6 +71,7 @@
src/libmbim-glib/test/test-fragment
src/libmbim-glib/test/test-message-parser
src/libmbim-glib/test/test-message-builder
+src/libmbim-glib/test/test-proxy-helpers
src/libmbim-glib/test/*.log
src/libmbim-glib/test/*.trs
@@ -78,4 +85,6 @@
src/mbimcli/.libs
src/mbimcli/mbimcli
-utils/mbim-network
\ No newline at end of file
+utils/mbim-network
+
+src/mbim-proxy/mbim-proxy
diff --git a/NEWS b/NEWS
index ce7936e..6b19cc9 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,30 @@
+Overview of changes in libmbim 1.10
+----------------------------------------
+
+ * API break: Flag values in 'MbimRegistrationFlag' were updated to match the
+ ones in the MBIM documentation.
+
+ * Implemented a new 'mbim-proxy', which allows sharing a single MBIM control
+ port among different processes. The usage of the proxy is optional, and can
+ be requested by specifying the MBIM_DEVICE_OPEN_FLAGS_PROXY flag in the new
+ mbim_device_open_full() method. The 'mbimcli' command line tool was also
+ extended with a new '--device-open-proxy,-p' option, to allow requesting the
+ use of the proxy process.
+
+ * New 'removed' signal added to the MbimDevice, to notify when the underlying
+ connection to the device is lost (e.g. lost connection to the mbim-proxy, or
+ lost access to the MBIM control port).
+
+ * Added support for registering and using custom services.
+
+ * Added additional GMM cause codes to MbimNwError.
+
+ * Transactions are now matched not only by ID but also by type.
+
+ * Several other minor improvements and fixes.
+
+
Overview of changes in libmbim 1.8
----------------------------------------
diff --git a/configure.ac b/configure.ac
index 56370aa..27f82c9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,7 +3,7 @@
dnl The libmbim version number
m4_define([mbim_major_version], [1])
-m4_define([mbim_minor_version], [9])
+m4_define([mbim_minor_version], [11])
m4_define([mbim_micro_version], [0])
m4_define([mbim_version],
[mbim_major_version.mbim_minor_version.mbim_micro_version])
@@ -16,9 +16,9 @@
dnl with old code), increment a.
dnl If the interface has changed in an incompatible way (that is,
dnl functions have changed or been removed), then zero a.
-m4_define([mbim_glib_lt_current], [3])
+m4_define([mbim_glib_lt_current], [4])
m4_define([mbim_glib_lt_revision], [0])
-m4_define([mbim_glib_lt_age], [3])
+m4_define([mbim_glib_lt_age], [0])
AC_INIT([libmbim], [mbim_version], [libmbim-devel@lists.freedesktop.org])
@@ -69,6 +69,7 @@
glib-2.0 >= 2.32
gobject-2.0
gio-2.0
+ gio-unix-2.0
gudev-1.0 >= 147)
AC_SUBST(LIBMBIM_GLIB_CFLAGS)
AC_SUBST(LIBMBIM_GLIB_LIBS)
@@ -81,6 +82,14 @@
AC_SUBST(MBIMCLI_CFLAGS)
AC_SUBST(MBIMCLI_LIBS)
+dnl General dependencies for mbim-proxy
+PKG_CHECK_MODULES(MBIMPROXY,
+ glib-2.0 >= 2.32
+ gobject-2.0
+ gio-2.0)
+AC_SUBST(MBIMPROXY_CFLAGS)
+AC_SUBST(MBIMPROXY_LIBS)
+
GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0`
AC_SUBST(GLIB_MKENUMS)
@@ -104,6 +113,7 @@
src/libmbim-glib/generated/Makefile
src/libmbim-glib/test/Makefile
src/mbimcli/Makefile
+ src/mbim-proxy/Makefile
utils/Makefile
docs/Makefile
docs/reference/Makefile
diff --git a/data/Makefile.am b/data/Makefile.am
index f6820cc..111e028 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -9,4 +9,5 @@
mbim-service-stk.json \
mbim-service-dss.json \
mbim-service-ms-firmware-id.json \
- mbim-service-ms-host-shutdown.json
+ mbim-service-ms-host-shutdown.json \
+ mbim-service-proxy-control.json
diff --git a/data/mbim-service-proxy-control.json b/data/mbim-service-proxy-control.json
new file mode 100644
index 0000000..2379e79
--- /dev/null
+++ b/data/mbim-service-proxy-control.json
@@ -0,0 +1,17 @@
+
+[
+ // *********************************************************************************
+ { "type" : "Service",
+ "name" : "Proxy Control" },
+
+ // *********************************************************************************
+ { "name" : "Configuration",
+ "service" : "Proxy Control",
+ "type" : "Command",
+ "set" : [ { "name" : "DevicePath",
+ "format" : "string" },
+ { "name" : "Timeout",
+ "format" : "guint32" } ],
+ "response" : [] }
+
+]
diff --git a/docs/reference/libmbim-glib/libmbim-glib-common.sections b/docs/reference/libmbim-glib/libmbim-glib-common.sections
index a1d1103..5c3af88 100644
--- a/docs/reference/libmbim-glib/libmbim-glib-common.sections
+++ b/docs/reference/libmbim-glib/libmbim-glib-common.sections
@@ -22,6 +22,7 @@
MBIM_UUID_DSS
MBIM_UUID_MS_FIRMWARE_ID
MBIM_UUID_MS_HOST_SHUTDOWN
+MBIM_UUID_PROXY_CONTROL
<SUBSECTION Methods>
mbim_service_get_string
mbim_service_lookup_name
@@ -53,6 +54,7 @@
MbimCidDss
MbimCidMsFirmwareId
MbimCidMsHostShutdown
+MbimCidProxyControl
<SUBSECTION Methods>
mbim_cid_can_set
mbim_cid_can_query
@@ -67,6 +69,7 @@
mbim_cid_dss_get_string
mbim_cid_ms_firmware_id_get_string
mbim_cid_ms_host_shutdown_get_string
+mbim_cid_proxy_control_get_string
<SUBSECTION Private>
mbim_cid_basic_connect_build_string_from_mask
mbim_cid_sms_build_string_from_mask
@@ -77,6 +80,7 @@
mbim_cid_dss_build_string_from_mask
mbim_cid_ms_firmware_id_build_string_from_mask
mbim_cid_ms_host_shutdown_build_string_from_mask
+mbim_cid_proxy_control_build_string_from_mask
<SUBSECTION Standard>
MBIM_TYPE_CID_AUTH
MBIM_TYPE_CID_BASIC_CONNECT
@@ -87,6 +91,7 @@
MBIM_TYPE_CID_USSD
MBIM_TYPE_CID_MS_FIRMWARE_ID
MBIM_TYPE_CID_MS_HOST_SHUTDOWN
+MBIM_TYPE_CID_PROXY_CONTROL
mbim_cid_auth_get_type
mbim_cid_basic_connect_get_type
mbim_cid_dss_get_type
@@ -96,6 +101,7 @@
mbim_cid_ussd_get_type
mbim_cid_ms_firmware_id_get_type
mbim_cid_ms_host_shutdown_get_type
+mbim_cid_proxy_control_get_type
</SECTION>
<SECTION>
@@ -154,6 +160,10 @@
mbim_message_indicate_status_get_cid
mbim_message_indicate_status_get_raw_information_buffer
<SUBSECTION Private>
+mbim_message_open_done_new
+mbim_message_close_done_new
+mbim_message_proxy_control_configuration_response_parse
+mbim_message_proxy_control_configuration_set_new
mbim_message_type_build_string_from_mask
mbim_message_command_type_build_string_from_mask
<SUBSECTION Standard>
@@ -164,6 +174,12 @@
<SECTION>
<FILE>mbim-device</FILE>
<TITLE>MbimDevice</TITLE>
+MBIM_DEVICE_FILE
+MBIM_DEVICE_IN_SESSION
+MBIM_DEVICE_TRANSACTION_ID
+MBIM_DEVICE_SIGNAL_REMOVED
+MBIM_DEVICE_SIGNAL_INDICATE_STATUS
+MBIM_DEVICE_SIGNAL_ERROR
MbimDevice
mbim_device_new
mbim_device_new_finish
@@ -174,6 +190,9 @@
mbim_device_is_open
mbim_device_open
mbim_device_open_finish
+MbimDeviceOpenFlags
+mbim_device_open_full
+mbim_device_open_full_finish
mbim_device_close
mbim_device_close_finish
mbim_device_close_force
@@ -182,11 +201,6 @@
mbim_device_command_finish
<SUBSECTION Private>
MbimDeviceClass
-MBIM_DEVICE_FILE
-MBIM_DEVICE_IN_SESSION
-MBIM_DEVICE_TRANSACTION_ID
-MBIM_DEVICE_SIGNAL_INDICATE_STATUS
-MBIM_DEVICE_SIGNAL_ERROR
<SUBSECTION Standard>
MBIM_DEVICE
MBIM_DEVICE_CLASS
@@ -199,6 +213,28 @@
</SECTION>
<SECTION>
+<FILE>mbim-proxy</FILE>
+<TITLE>MbimProxy</TITLE>
+MBIM_PROXY_SOCKET_PATH
+MBIM_PROXY_N_CLIENTS
+MBIM_PROXY_N_DEVICES
+MbimProxy
+mbim_proxy_new
+mbim_proxy_get_n_clients
+mbim_proxy_get_n_devices
+<SUBSECTION Standard>
+MbimProxyClass
+MBIM_PROXY
+MBIM_PROXY_CLASS
+MBIM_PROXY_GET_CLASS
+MBIM_IS_PROXY
+MBIM_IS_PROXY_CLASS
+MBIM_TYPE_PROXY
+MbimProxyPrivate
+mbim_proxy_get_type
+</SECTION>
+
+<SECTION>
<FILE>mbim-enums</FILE>
MbimDeviceType
MbimCellularClass
diff --git a/docs/reference/libmbim-glib/libmbim-glib-docs.xml b/docs/reference/libmbim-glib/libmbim-glib-docs.xml
index b709ef3..1765137 100644
--- a/docs/reference/libmbim-glib/libmbim-glib-docs.xml
+++ b/docs/reference/libmbim-glib/libmbim-glib-docs.xml
@@ -44,13 +44,14 @@
<xi:include href="xml/mbim-cid.xml"/>
<xi:include href="xml/mbim-message.xml"/>
<xi:include href="xml/mbim-device.xml"/>
+ <xi:include href="xml/mbim-proxy.xml"/>
<xi:include href="xml/mbim-enums.xml"/>
<xi:include href="xml/mbim-errors.xml"/>
<xi:include href="xml/mbim-utils.xml"/>
</chapter>
<chapter>
- <title>Services</title>
+ <title>Generic Services</title>
<xi:include href="xml/mbim-basic-connect.xml"/>
<xi:include href="xml/mbim-sms.xml"/>
<xi:include href="xml/mbim-ussd.xml"/>
@@ -58,6 +59,10 @@
<xi:include href="xml/mbim-phonebook.xml"/>
<xi:include href="xml/mbim-stk.xml"/>
<xi:include href="xml/mbim-dss.xml"/>
+ </chapter>
+
+ <chapter>
+ <title>Other Services</title>
<xi:include href="xml/mbim-ms-firmware-id.xml"/>
<xi:include href="xml/mbim-ms-host-shutdown.xml"/>
</chapter>
diff --git a/src/Makefile.am b/src/Makefile.am
index 7de85b4..6732c9e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,2 +1,2 @@
-SUBDIRS = libmbim-glib mbimcli
+SUBDIRS = libmbim-glib mbimcli mbim-proxy
diff --git a/src/libmbim-glib/Makefile.am b/src/libmbim-glib/Makefile.am
index 0988a28..4f8bbf3 100644
--- a/src/libmbim-glib/Makefile.am
+++ b/src/libmbim-glib/Makefile.am
@@ -12,6 +12,7 @@
-I$(top_builddir)/src/libmbim-glib \
-I$(top_builddir)/src/libmbim-glib/generated \
-DLIBMBIM_GLIB_COMPILATION \
+ -DLIBEXEC_PATH=\""$(libexecdir)"\" \
-DG_LOG_DOMAIN=\"Mbim\"
libmbim_glib_core_la_SOURCES = \
mbim-version.h \
@@ -22,7 +23,9 @@
mbim-cid.h mbim-cid.c \
mbim-message-private.h mbim-message.h mbim-message.c \
mbim-device.h mbim-device.c \
- mbim-compat.h mbim-compat.c
+ mbim-compat.h mbim-compat.c \
+ mbim-proxy.h mbim-proxy.c \
+ mbim-proxy-helpers.h mbim-proxy-helpers.c
# Final installable library
lib_LTLIBRARIES = libmbim-glib.la
@@ -50,7 +53,8 @@
mbim-cid.h \
mbim-message.h \
mbim-device.h \
- mbim-compat.h
+ mbim-compat.h \
+ mbim-proxy.h
EXTRA_DIST = \
mbim-version.h.in
diff --git a/src/libmbim-glib/generated/Makefile.am b/src/libmbim-glib/generated/Makefile.am
index 43a79aa..ce81a71 100644
--- a/src/libmbim-glib/generated/Makefile.am
+++ b/src/libmbim-glib/generated/Makefile.am
@@ -11,7 +11,8 @@
mbim-stk.h \
mbim-dss.h \
mbim-ms-firmware-id.h \
- mbim-ms-host-shutdown.h
+ mbim-ms-host-shutdown.h \
+ mbim-proxy-control.h
GENERATED_C = \
mbim-error-types.c \
@@ -25,7 +26,8 @@
mbim-stk.c \
mbim-dss.c \
mbim-ms-firmware-id.c \
- mbim-ms-host-shutdown.c
+ mbim-ms-host-shutdown.c \
+ mbim-proxy-control.c
GENERATED_SECTIONS = \
mbim-basic-connect.sections \
@@ -36,7 +38,8 @@
mbim-stk.sections \
mbim-dss.sections \
mbim-ms-firmware-id.sections \
- mbim-ms-host-shutdown.sections
+ mbim-ms-host-shutdown.sections \
+ mbim-proxy-control.sections
# Error types
mbim-error-types.h: $(top_srcdir)/src/libmbim-glib/mbim-errors.h $(top_srcdir)/build-aux/templates/mbim-error-types-template.h
@@ -158,6 +161,15 @@
--input $(top_srcdir)/data/mbim-service-ms-host-shutdown.json \
--output mbim-ms-host-shutdown
+# Proxy Control service
+mbim-proxy-control.h mbim-proxy-control.c mbim-proxy-control.sections: $(top_srcdir)/data/mbim-service-proxy-control.json $(top_srcdir)/build-aux/mbim-codegen/*.py $(top_srcdir)/build-aux/mbim-codegen/mbim-codegen
+ $(AM_V_GEN) \
+ rm -f mbim-proxy-control.h && \
+ rm -f mbim-proxy-control.c && \
+ $(top_srcdir)/build-aux/mbim-codegen/mbim-codegen \
+ --input $(top_srcdir)/data/mbim-service-proxy-control.json \
+ --output mbim-proxy-control
+
BUILT_SOURCES = $(GENERATED_H) $(GENERATED_C)
nodist_libmbim_glib_generated_la_SOURCES = \
diff --git a/src/libmbim-glib/libmbim-glib.h b/src/libmbim-glib/libmbim-glib.h
index 0cfe560..b41122b 100644
--- a/src/libmbim-glib/libmbim-glib.h
+++ b/src/libmbim-glib/libmbim-glib.h
@@ -34,6 +34,7 @@
#include "mbim-message.h"
#include "mbim-device.h"
#include "mbim-enums.h"
+#include "mbim-proxy.h"
/* generated */
#include "mbim-enum-types.h"
diff --git a/src/libmbim-glib/mbim-cid.c b/src/libmbim-glib/mbim-cid.c
index 5ce53e2..50dc4f1 100644
--- a/src/libmbim-glib/mbim-cid.c
+++ b/src/libmbim-glib/mbim-cid.c
@@ -126,6 +126,12 @@
{ TRUE, FALSE, FALSE }, /* MBIM_CID_MS_HOST_SHUTDOWN_NOTIFY */
};
+/* Note: index of the array is CID-1 */
+#define MBIM_CID_PROXY_CONTROL_LAST MBIM_CID_PROXY_CONTROL_CONFIGURATION
+static const CidConfig cid_proxy_control_config [MBIM_CID_PROXY_CONTROL_LAST] = {
+ { TRUE, FALSE, FALSE }, /* MBIM_CID_PROXY_CONTROL_CONFIGURATION */
+};
+
/**
* mbim_cid_can_set:
* @service: a #MbimService.
@@ -143,7 +149,7 @@
g_return_val_if_fail (cid > 0, FALSE);
/* Known service required */
g_return_val_if_fail (service > MBIM_SERVICE_INVALID, FALSE);
- g_return_val_if_fail (service <= MBIM_SERVICE_MS_HOST_SHUTDOWN, FALSE);
+ g_return_val_if_fail (service <= MBIM_SERVICE_PROXY_CONTROL, FALSE);
switch (service) {
case MBIM_SERVICE_BASIC_CONNECT:
@@ -164,6 +170,8 @@
return cid_ms_firmware_id_config[cid - 1].set;
case MBIM_SERVICE_MS_HOST_SHUTDOWN:
return cid_ms_host_shutdown_config[cid - 1].set;
+ case MBIM_SERVICE_PROXY_CONTROL:
+ return cid_proxy_control_config[cid - 1].set;
default:
g_assert_not_reached ();
return FALSE;
@@ -187,7 +195,7 @@
g_return_val_if_fail (cid > 0, FALSE);
/* Known service required */
g_return_val_if_fail (service > MBIM_SERVICE_INVALID, FALSE);
- g_return_val_if_fail (service <= MBIM_SERVICE_MS_HOST_SHUTDOWN, FALSE);
+ g_return_val_if_fail (service <= MBIM_SERVICE_PROXY_CONTROL, FALSE);
switch (service) {
case MBIM_SERVICE_BASIC_CONNECT:
@@ -208,6 +216,8 @@
return cid_ms_firmware_id_config[cid - 1].query;
case MBIM_SERVICE_MS_HOST_SHUTDOWN:
return cid_ms_host_shutdown_config[cid - 1].query;
+ case MBIM_SERVICE_PROXY_CONTROL:
+ return cid_proxy_control_config[cid - 1].query;
default:
g_assert_not_reached ();
return FALSE;
@@ -231,7 +241,7 @@
g_return_val_if_fail (cid > 0, FALSE);
/* Known service required */
g_return_val_if_fail (service > MBIM_SERVICE_INVALID, FALSE);
- g_return_val_if_fail (service <= MBIM_SERVICE_MS_HOST_SHUTDOWN, FALSE);
+ g_return_val_if_fail (service <= MBIM_SERVICE_PROXY_CONTROL, FALSE);
switch (service) {
case MBIM_SERVICE_BASIC_CONNECT:
@@ -252,7 +262,8 @@
return cid_ms_firmware_id_config[cid - 1].notify;
case MBIM_SERVICE_MS_HOST_SHUTDOWN:
return cid_ms_host_shutdown_config[cid - 1].notify;
-
+ case MBIM_SERVICE_PROXY_CONTROL:
+ return cid_proxy_control_config[cid - 1].notify;
default:
g_assert_not_reached ();
return FALSE;
@@ -277,7 +288,7 @@
g_return_val_if_fail (cid > 0, NULL);
/* Known service required */
g_return_val_if_fail (service > MBIM_SERVICE_INVALID, NULL);
- g_return_val_if_fail (service <= MBIM_SERVICE_MS_HOST_SHUTDOWN, NULL);
+ g_return_val_if_fail (service <= MBIM_SERVICE_PROXY_CONTROL, NULL);
switch (service) {
case MBIM_SERVICE_BASIC_CONNECT:
@@ -298,6 +309,8 @@
return mbim_cid_ms_firmware_id_get_string (cid);
case MBIM_SERVICE_MS_HOST_SHUTDOWN:
return mbim_cid_ms_host_shutdown_get_string (cid);
+ case MBIM_SERVICE_PROXY_CONTROL:
+ return mbim_cid_proxy_control_get_string (cid);
default:
g_assert_not_reached ();
return FALSE;
diff --git a/src/libmbim-glib/mbim-cid.h b/src/libmbim-glib/mbim-cid.h
index cd50fc5..3315340 100644
--- a/src/libmbim-glib/mbim-cid.h
+++ b/src/libmbim-glib/mbim-cid.h
@@ -207,6 +207,18 @@
MBIM_CID_MS_HOST_SHUTDOWN_NOTIFY = 1
} MbimCidMsHostShutdown;
+/**
+ * MbimCidProxyControl:
+ * @MBIM_CID_PROXY_CONTROL_UNKNOWN: Unknown command.
+ * @MBIM_CID_PROXY_CONTROL_CONFIGURATION: Configuration.
+ *
+ * MBIM commands in the %MBIM_SERVICE_PROXY_CONTROL service.
+ */
+typedef enum {
+ MBIM_CID_PROXY_CONTROL_UNKNOWN = 0,
+ MBIM_CID_PROXY_CONTROL_CONFIGURATION = 1
+} MbimCidProxyControl;
+
/* Command helpers */
gboolean mbim_cid_can_set (MbimService service,
diff --git a/src/libmbim-glib/mbim-device.c b/src/libmbim-glib/mbim-device.c
index 3ce9c09..086ae4e 100644
--- a/src/libmbim-glib/mbim-device.c
+++ b/src/libmbim-glib/mbim-device.c
@@ -18,6 +18,7 @@
* Boston, MA 02110-1301 USA.
*
* Copyright (C) 2013-2014 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2014 Smith Micro Software, Inc.
*
* Implementation based on the 'QmiDevice' GObject from libqmi-glib.
*/
@@ -28,6 +29,7 @@
#include <termios.h>
#include <unistd.h>
#include <gio/gio.h>
+#include <gio/gunixsocketaddress.h>
#include <gudev/gudev.h>
#include <sys/ioctl.h>
#define IOCTL_WDM_MAX_COMMAND _IOR('H', 0xA0, guint16)
@@ -37,6 +39,8 @@
#include "mbim-message.h"
#include "mbim-message-private.h"
#include "mbim-error-types.h"
+#include "mbim-proxy.h"
+#include "mbim-proxy-control.h"
/**
* SECTION:mbim-device
@@ -67,6 +71,7 @@
enum {
SIGNAL_INDICATE_STATUS,
SIGNAL_ERROR,
+ SIGNAL_REMOVED,
SIGNAL_LAST
};
@@ -78,6 +83,12 @@
TRANSACTION_TYPE_LAST = 2
} TransactionType;
+typedef enum {
+ OPEN_STATUS_CLOSED = 0,
+ OPEN_STATUS_OPENING = 1,
+ OPEN_STATUS_OPEN = 2
+} OpenStatus;
+
struct _MbimDevicePrivate {
/* File */
GFile *file;
@@ -88,6 +99,11 @@
GIOChannel *iochannel;
guint watch_id;
GByteArray *response;
+ OpenStatus open_status;
+
+ /* Support for mbim-proxy */
+ GSocketClient *socket_client;
+ GSocketConnection *socket_connection;
/* HT to keep track of ongoing host/function transactions
* Host transactions: created by us
@@ -105,6 +121,7 @@
guint16 max_control_transfer;
};
+#define MAX_SPAWN_RETRIES 10
#define MAX_CONTROL_TRANSFER 4096
#define MAX_TIME_BETWEEN_FRAGMENTS_MS 1250
@@ -215,6 +232,10 @@
ctx->type,
MBIM_MESSAGE_TYPE_INVALID,
ctx->transaction_id);
+ if (!tr)
+ /* transaction already completed */
+ return FALSE;
+
tr->timeout_id = 0;
/* If no fragment was received, complete transaction with a timeout error */
@@ -389,7 +410,7 @@
{
g_return_val_if_fail (MBIM_IS_DEVICE (self), FALSE);
- return !!self->priv->iochannel;
+ return (self->priv->open_status == OPEN_STATUS_OPEN);
}
/*****************************************************************************/
@@ -569,7 +590,7 @@
case MBIM_MESSAGE_TYPE_COMMAND:
case MBIM_MESSAGE_TYPE_HOST_ERROR:
/* Shouldn't expect host-generated messages as replies */
- g_warning ("[%s] Host-generated message received: ignoring",
+ g_message ("[%s] Host-generated message received: ignoring",
self->priv->path_display);
return;
}
@@ -620,6 +641,7 @@
g_byte_array_remove_range (self->priv->response, 0, self->priv->response->len);
mbim_device_close_force (self, NULL);
+ g_signal_emit (self, signals[SIGNAL_REMOVED], 0 );
return FALSE;
}
@@ -795,94 +817,256 @@
return max;
}
+typedef struct {
+ MbimDevice *self;
+ GSimpleAsyncResult *result;
+ guint spawn_retries;
+} CreateIoChannelContext;
+
+static void
+create_iochannel_context_complete_and_free (CreateIoChannelContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->self);
+ g_slice_free (CreateIoChannelContext, ctx);
+}
+
static gboolean
-create_iochannel (MbimDevice *self,
- GError **error)
+create_iochannel_finish (MbimDevice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+setup_iochannel (CreateIoChannelContext *ctx)
{
GError *inner_error = NULL;
+
+ /* We don't want UTF-8 encoding, we're playing with raw binary data */
+ g_io_channel_set_encoding (ctx->self->priv->iochannel, NULL, NULL);
+
+ /* We don't want to get the channel buffered */
+ g_io_channel_set_buffered (ctx->self->priv->iochannel, FALSE);
+
+ /* Let the GIOChannel own the FD */
+ g_io_channel_set_close_on_unref (ctx->self->priv->iochannel, TRUE);
+
+ /* We don't want to get blocked while writing stuff */
+ if (!g_io_channel_set_flags (ctx->self->priv->iochannel,
+ G_IO_FLAG_NONBLOCK,
+ &inner_error)) {
+ g_simple_async_result_take_error (ctx->result, inner_error);
+ g_io_channel_shutdown (ctx->self->priv->iochannel, FALSE, NULL);
+ g_io_channel_unref (ctx->self->priv->iochannel);
+ ctx->self->priv->iochannel = NULL;
+ g_clear_object (&ctx->self->priv->socket_connection);
+ g_clear_object (&ctx->self->priv->socket_client);
+ create_iochannel_context_complete_and_free (ctx);
+ return;
+ }
+
+ ctx->self->priv->watch_id = g_io_add_watch (ctx->self->priv->iochannel,
+ G_IO_IN | G_IO_ERR | G_IO_HUP,
+ (GIOFunc)data_available,
+ ctx->self);
+
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ create_iochannel_context_complete_and_free (ctx);
+}
+
+static void
+create_iochannel_with_fd (CreateIoChannelContext *ctx)
+{
gint fd;
guint16 max;
- if (self->priv->iochannel) {
- g_set_error_literal (error,
- MBIM_CORE_ERROR,
- MBIM_CORE_ERROR_WRONG_STATE,
- "Already open");
- return FALSE;
- }
-
- g_assert (self->priv->file);
- g_assert (self->priv->path);
-
errno = 0;
- fd = open (self->priv->path, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY);
+ fd = open (ctx->self->priv->path, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY);
if (fd < 0) {
- g_set_error (error,
- MBIM_CORE_ERROR,
- MBIM_CORE_ERROR_FAILED,
- "Cannot open device file '%s': %s",
- self->priv->path_display,
- strerror (errno));
- return FALSE;
+ g_simple_async_result_set_error (
+ ctx->result,
+ MBIM_CORE_ERROR,
+ MBIM_CORE_ERROR_FAILED,
+ "Cannot open device file '%s': %s",
+ ctx->self->priv->path_display,
+ strerror (errno));
+ create_iochannel_context_complete_and_free (ctx);
+ return;
}
/* Query message size */
if (ioctl (fd, IOCTL_WDM_MAX_COMMAND, &max) < 0) {
g_debug ("[%s] Couldn't query maximum message size: "
"IOCTL_WDM_MAX_COMMAND failed: %s",
- self->priv->path_display,
+ ctx->self->priv->path_display,
strerror (errno));
/* Fallback, try to read the descriptor file */
- max = read_max_control_transfer (self);
+ max = read_max_control_transfer (ctx->self);
} else {
g_debug ("[%s] Queried max control message size: %" G_GUINT16_FORMAT,
- self->priv->path_display,
+ ctx->self->priv->path_display,
max);
}
- self->priv->max_control_transfer = max;
+ ctx->self->priv->max_control_transfer = max;
/* Create new GIOChannel */
- self->priv->iochannel = g_io_channel_unix_new (fd);
+ ctx->self->priv->iochannel = g_io_channel_unix_new (fd);
- /* We don't want UTF-8 encoding, we're playing with raw binary data */
- g_io_channel_set_encoding (self->priv->iochannel, NULL, NULL);
+ setup_iochannel (ctx);
+}
- /* We don't want to get the channel buffered */
- g_io_channel_set_buffered (self->priv->iochannel, FALSE);
+static void create_iochannel_with_socket (CreateIoChannelContext *ctx);
- /* Let the GIOChannel own the FD */
- g_io_channel_set_close_on_unref (self->priv->iochannel, TRUE);
+static gboolean
+wait_for_proxy_cb (CreateIoChannelContext *ctx)
+{
+ create_iochannel_with_socket (ctx);
+ return FALSE;
+}
- /* We don't want to get blocked while writing stuff */
- if (!g_io_channel_set_flags (self->priv->iochannel,
- G_IO_FLAG_NONBLOCK,
- &inner_error)) {
- g_prefix_error (&inner_error, "Cannot set non-blocking channel: ");
- g_propagate_error (error, inner_error);
- g_io_channel_shutdown (self->priv->iochannel, FALSE, NULL);
- g_io_channel_unref (self->priv->iochannel);
- self->priv->iochannel = NULL;
- return FALSE;
+static void
+create_iochannel_with_socket (CreateIoChannelContext *ctx)
+{
+ GSocketAddress *socket_address;
+ GError *error = NULL;
+
+ /* Create socket client */
+ if (ctx->self->priv->socket_client)
+ g_object_unref (ctx->self->priv->socket_client);
+ ctx->self->priv->socket_client = g_socket_client_new ();
+ g_socket_client_set_family (ctx->self->priv->socket_client, G_SOCKET_FAMILY_UNIX);
+ g_socket_client_set_socket_type (ctx->self->priv->socket_client, G_SOCKET_TYPE_STREAM);
+ g_socket_client_set_protocol (ctx->self->priv->socket_client, G_SOCKET_PROTOCOL_DEFAULT);
+
+ /* Setup socket address */
+ socket_address = (g_unix_socket_address_new_with_type (
+ MBIM_PROXY_SOCKET_PATH,
+ -1,
+ G_UNIX_SOCKET_ADDRESS_ABSTRACT));
+
+ /* Connect to address */
+ if (ctx->self->priv->socket_connection)
+ g_object_unref (ctx->self->priv->socket_connection);
+ ctx->self->priv->socket_connection = (g_socket_client_connect (
+ ctx->self->priv->socket_client,
+ G_SOCKET_CONNECTABLE (socket_address),
+ NULL,
+ &error));
+ g_object_unref (socket_address);
+
+ if (!ctx->self->priv->socket_connection) {
+ gchar **argc;
+
+ g_debug ("cannot connect to proxy: %s", error->message);
+ g_clear_error (&error);
+ g_clear_object (&ctx->self->priv->socket_client);
+
+ /* Don't retry forever */
+ ctx->spawn_retries++;
+ if (ctx->spawn_retries > MAX_SPAWN_RETRIES) {
+ g_simple_async_result_set_error (ctx->result,
+ MBIM_CORE_ERROR,
+ MBIM_CORE_ERROR_FAILED,
+ "Couldn't spawn the mbim-proxy");
+ create_iochannel_context_complete_and_free (ctx);
+ return;
+ }
+
+ g_debug ("spawning new mbim-proxy (try %u)...", ctx->spawn_retries);
+
+ argc = g_new0 (gchar *, 2);
+ argc[0] = g_strdup (LIBEXEC_PATH "/mbim-proxy");
+ if (!g_spawn_async (NULL, /* working directory */
+ argc,
+ NULL, /* envp */
+ G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
+ NULL, /* child_setup */
+ NULL, /* child_setup_user_data */
+ NULL,
+ &error)) {
+ g_debug ("error spawning mbim-proxy: %s", error->message);
+ g_clear_error (&error);
+ }
+ g_strfreev (argc);
+
+ /* Wait some ms and retry */
+ g_timeout_add (100, (GSourceFunc)wait_for_proxy_cb, ctx);
+ return;
}
- self->priv->watch_id = g_io_add_watch (self->priv->iochannel,
- G_IO_IN | G_IO_ERR | G_IO_HUP,
- (GIOFunc)data_available,
- self);
+ ctx->self->priv->iochannel = g_io_channel_unix_new (
+ g_socket_get_fd (
+ g_socket_connection_get_socket (ctx->self->priv->socket_connection)));
- return !!self->priv->iochannel;
+ /* try to read the descriptor file */
+ ctx->self->priv->max_control_transfer = read_max_control_transfer (ctx->self);
+
+ setup_iochannel (ctx);
}
+static void
+create_iochannel (MbimDevice *self,
+ gboolean proxy,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ CreateIoChannelContext *ctx;
+
+ ctx = g_slice_new (CreateIoChannelContext);
+ ctx->self = g_object_ref (self);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ create_iochannel);
+ ctx->spawn_retries = 0;
+
+ if (self->priv->iochannel) {
+ g_simple_async_result_set_error (ctx->result,
+ MBIM_CORE_ERROR,
+ MBIM_CORE_ERROR_WRONG_STATE,
+ "Already open");
+ create_iochannel_context_complete_and_free (ctx);
+ return;
+ }
+
+ g_assert (self->priv->file);
+ g_assert (self->priv->path);
+
+ if (proxy)
+ create_iochannel_with_socket (ctx);
+ else
+ create_iochannel_with_fd (ctx);
+}
+
+typedef enum {
+ DEVICE_OPEN_CONTEXT_STEP_FIRST = 0,
+ DEVICE_OPEN_CONTEXT_STEP_CREATE_IOCHANNEL,
+ DEVICE_OPEN_CONTEXT_STEP_FLAGS_PROXY,
+ DEVICE_OPEN_CONTEXT_STEP_OPEN_MESSAGE,
+ DEVICE_OPEN_CONTEXT_STEP_LAST
+} DeviceOpenContextStep;
+
typedef struct {
MbimDevice *self;
GSimpleAsyncResult *result;
GCancellable *cancellable;
+ DeviceOpenContextStep step;
+ MbimDeviceOpenFlags flags;
guint timeout;
} DeviceOpenContext;
static void
-device_open_context_complete_and_free (DeviceOpenContext *ctx)
+device_open_context_complete_and_free (DeviceOpenContext *ctx,
+ GError *error)
{
+ if (error)
+ g_simple_async_result_take_error (ctx->result, error);
+ else
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+
g_simple_async_result_complete_in_idle (ctx->result);
g_object_unref (ctx->result);
if (ctx->cancellable)
@@ -891,6 +1075,26 @@
g_slice_free (DeviceOpenContext, ctx);
}
+static void device_open_context_step (DeviceOpenContext *ctx);
+
+/**
+ * mbim_device_open_full_finish:
+ * @self: a #MbimDevice.
+ * @res: a #GAsyncResult.
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an asynchronous open operation started with mbim_device_open_full().
+ *
+ * Returns: %TRUE if successful, %FALSE if @error is set.
+ */
+gboolean
+mbim_device_open_full_finish (MbimDevice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
/**
* mbim_device_open_finish:
* @self: a #MbimDevice.
@@ -903,10 +1107,10 @@
*/
gboolean
mbim_device_open_finish (MbimDevice *self,
- GAsyncResult *res,
- GError **error)
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return mbim_device_open_full_finish (self, res, error);
}
static void open_message (DeviceOpenContext *ctx);
@@ -934,15 +1138,25 @@
/* No more seconds left in the timeout... return error */
}
- g_simple_async_result_take_error (ctx->result, error);
- } else if (!mbim_message_open_done_get_result (response, &error))
- g_simple_async_result_take_error (ctx->result, error);
- else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_debug ("open operation timed out: closed");
+ self->priv->open_status = OPEN_STATUS_CLOSED;
+ device_open_context_complete_and_free (ctx, error);
+ return;
+ }
- if (response)
+ if (!mbim_message_open_done_get_result (response, &error)) {
+ g_debug ("getting open done result failed: closed");
+ self->priv->open_status = OPEN_STATUS_CLOSED;
+ device_open_context_complete_and_free (ctx, error);
mbim_message_unref (response);
- device_open_context_complete_and_free (ctx);
+ return;
+ }
+
+ mbim_message_unref (response);
+
+ /* go on */
+ ctx->step++;
+ device_open_context_step (ctx);
}
static void
@@ -962,6 +1176,181 @@
mbim_message_unref (request);
}
+static void
+proxy_cfg_message_ready (MbimDevice *self,
+ GAsyncResult *res,
+ DeviceOpenContext *ctx)
+{
+ MbimMessage *response;
+ GError *error = NULL;
+
+ response = mbim_device_command_finish (self, res, &error);
+ if (!response) {
+ /* Hard error if proxy cfg command fails */
+ g_debug ("proxy configuration failed: closed");
+ self->priv->open_status = OPEN_STATUS_CLOSED;
+ device_open_context_complete_and_free (ctx, error);
+ return;
+ }
+
+ mbim_message_unref (response);
+
+ ctx->step++;
+ device_open_context_step (ctx);
+}
+
+static void
+proxy_cfg_message (DeviceOpenContext *ctx)
+{
+ GError *error = NULL;
+ MbimMessage *request;
+
+ request = mbim_message_proxy_control_configuration_set_new (ctx->self->priv->path, ctx->timeout, &error);
+
+ /* This message is no longer a direct reply; as the proxy will also try to open the device
+ * directly. If it cannot open the device, it will return an error. */
+ mbim_device_command (ctx->self,
+ request,
+ ctx->timeout,
+ ctx->cancellable,
+ (GAsyncReadyCallback)proxy_cfg_message_ready,
+ ctx);
+ mbim_message_unref (request);
+}
+
+static void
+create_iochannel_ready (MbimDevice *self,
+ GAsyncResult *res,
+ DeviceOpenContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!create_iochannel_finish (self, res, &error)) {
+ g_debug ("creating iochannel failed: closed");
+ self->priv->open_status = OPEN_STATUS_CLOSED;
+ device_open_context_complete_and_free (ctx, error);
+ return;
+ }
+
+ /* Go on */
+ ctx->step++;
+ device_open_context_step (ctx);
+}
+
+static void
+device_open_context_step (DeviceOpenContext *ctx)
+{
+ switch (ctx->step) {
+ case DEVICE_OPEN_CONTEXT_STEP_FIRST:
+ if (ctx->self->priv->open_status == OPEN_STATUS_OPEN) {
+ GError *error;
+
+ error = g_error_new (MBIM_CORE_ERROR,
+ MBIM_CORE_ERROR_WRONG_STATE,
+ "Already open");
+ device_open_context_complete_and_free (ctx, error);
+ return;
+ }
+
+ if (ctx->self->priv->open_status == OPEN_STATUS_OPENING) {
+ GError *error;
+
+ error = g_error_new (MBIM_CORE_ERROR,
+ MBIM_CORE_ERROR_WRONG_STATE,
+ "Already opening");
+ device_open_context_complete_and_free (ctx, error);
+ return;
+ }
+
+ g_debug ("opening device...");
+ g_assert (ctx->self->priv->open_status == OPEN_STATUS_CLOSED);
+ ctx->self->priv->open_status = OPEN_STATUS_OPENING;
+
+ ctx->step++;
+ /* Fall down */
+
+ case DEVICE_OPEN_CONTEXT_STEP_CREATE_IOCHANNEL:
+ create_iochannel (ctx->self,
+ !!(ctx->flags & MBIM_DEVICE_OPEN_FLAGS_PROXY),
+ (GAsyncReadyCallback)create_iochannel_ready,
+ ctx);
+ return;
+
+ case DEVICE_OPEN_CONTEXT_STEP_FLAGS_PROXY:
+ if (ctx->flags & MBIM_DEVICE_OPEN_FLAGS_PROXY) {
+ proxy_cfg_message (ctx);
+ return;
+ }
+ ctx->step++;
+ /* Fall down */
+
+ case DEVICE_OPEN_CONTEXT_STEP_OPEN_MESSAGE:
+ /* If the device is already in-session, avoid the open message */
+ if (!ctx->self->priv->in_session) {
+ open_message (ctx);
+ return;
+ }
+ ctx->step++;
+ /* Fall down */
+
+ case DEVICE_OPEN_CONTEXT_STEP_LAST:
+ /* Nothing else to process, complete without error */
+ ctx->self->priv->open_status = OPEN_STATUS_OPEN;
+ device_open_context_complete_and_free (ctx, NULL);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+/**
+ * mbim_device_open_full:
+ * @self: a #MbimDevice.
+ * @flags: a set of #MbimDeviceOpenFlags.
+ * @timeout: maximum time, in seconds, to wait for the device to be opened.
+ * @cancellable: optional #GCancellable object, #NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the operation is finished.
+ * @user_data: the data to pass to callback function.
+ *
+ * Asynchronously opens a #MbimDevice for I/O.
+ *
+ * This method is an extension of the generic mbim_device_open(), which allows
+ * launching the #MbimDevice with proxy support.
+ *
+ * When the operation is finished @callback will be called. You can then call
+ * mbim_device_open_full_finish() to get the result of the operation.
+ */
+void
+mbim_device_open_full (MbimDevice *self,
+ MbimDeviceOpenFlags flags,
+ guint timeout,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ DeviceOpenContext *ctx;
+
+ g_return_if_fail (MBIM_IS_DEVICE (self));
+ g_return_if_fail (timeout > 0);
+
+ ctx = g_slice_new (DeviceOpenContext);
+ ctx->self = g_object_ref (self);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ mbim_device_open_full);
+ ctx->step = DEVICE_OPEN_CONTEXT_STEP_FIRST;
+ ctx->flags = flags;
+ ctx->timeout = timeout;
+ ctx->cancellable = (cancellable ? g_object_ref (cancellable) : NULL);
+
+ /* Start processing */
+ device_open_context_step (ctx);
+}
+
/**
* mbim_device_open:
* @self: a #MbimDevice.
@@ -982,37 +1371,12 @@
GAsyncReadyCallback callback,
gpointer user_data)
{
- DeviceOpenContext *ctx;
- GError *error = NULL;
-
- g_return_if_fail (MBIM_IS_DEVICE (self));
- g_return_if_fail (timeout > 0);
-
- ctx = g_slice_new (DeviceOpenContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mbim_device_open);
- ctx->timeout = timeout;
- ctx->cancellable = (cancellable ? g_object_ref (cancellable) : NULL);
-
- if (!create_iochannel (self, &error)) {
- g_prefix_error (&error,
- "Cannot open MBIM device: ");
- g_simple_async_result_take_error (ctx->result, error);
- device_open_context_complete_and_free (ctx);
- return;
- }
-
- /* If the device is already in-session, avoid the open message */
- if (self->priv->in_session) {
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- device_open_context_complete_and_free (ctx);
- return;
- }
-
- open_message (ctx);
+ mbim_device_open_full (self,
+ MBIM_DEVICE_OPEN_FLAGS_NONE,
+ timeout,
+ cancellable,
+ callback,
+ user_data);
}
/*****************************************************************************/
@@ -1024,11 +1388,21 @@
{
GError *inner_error = NULL;
- g_io_channel_shutdown (self->priv->iochannel, TRUE, &inner_error);
+ self->priv->open_status = OPEN_STATUS_CLOSED;
+
+ /* Already closed? */
+ if (!self->priv->iochannel && !self->priv->socket_connection && !self->priv->socket_client)
+ return TRUE;
+
+ if (self->priv->iochannel) {
+ g_io_channel_shutdown (self->priv->iochannel, TRUE, &inner_error);
+ g_io_channel_unref (self->priv->iochannel);
+ self->priv->iochannel = NULL;
+ }
/* Failures when closing still make the device to get closed */
- g_io_channel_unref (self->priv->iochannel);
- self->priv->iochannel = NULL;
+ g_clear_object (&self->priv->socket_connection);
+ g_clear_object (&self->priv->socket_client);
if (self->priv->watch_id) {
g_source_remove (self->priv->watch_id);
@@ -1063,10 +1437,6 @@
{
g_return_val_if_fail (MBIM_IS_DEVICE (self), FALSE);
- /* Already closed? */
- if (!self->priv->iochannel)
- return TRUE;
-
return destroy_iochannel (self, error);
}
@@ -1740,6 +2110,7 @@
/* Initialize transaction ID */
self->priv->transaction_id = 0x01;
+ self->priv->open_status = OPEN_STATUS_CLOSED;
}
static void
@@ -1749,6 +2120,8 @@
g_clear_object (&self->priv->file);
+ destroy_iochannel (self, NULL);
+
G_OBJECT_CLASS (mbim_device_parent_class)->dispose (object);
}
@@ -1764,17 +2137,12 @@
if (self->priv->transactions[i]) {
g_assert (g_hash_table_size (self->priv->transactions[i]) == 0);
g_hash_table_unref (self->priv->transactions[i]);
+ self->priv->transactions[i] = NULL;
}
}
g_free (self->priv->path);
g_free (self->priv->path_display);
- if (self->priv->watch_id)
- g_source_remove (self->priv->watch_id);
- if (self->priv->response)
- g_byte_array_unref (self->priv->response);
- if (self->priv->iochannel)
- g_io_channel_unref (self->priv->iochannel);
G_OBJECT_CLASS (mbim_device_parent_class)->finalize (object);
}
@@ -1861,4 +2229,22 @@
G_TYPE_NONE,
1,
G_TYPE_ERROR);
+
+ /**
+ * MbimDevice::device-removed:
+ * @self: the #MbimDevice
+ * @message: None
+ *
+ * The ::device-removed signal is emitted when an unexpected port hang-up is received.
+ */
+ signals[SIGNAL_REMOVED] =
+ g_signal_new (MBIM_DEVICE_SIGNAL_REMOVED,
+ G_OBJECT_CLASS_TYPE (G_OBJECT_CLASS (klass)),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_NONE,
+ 0);
}
diff --git a/src/libmbim-glib/mbim-device.h b/src/libmbim-glib/mbim-device.h
index 67b1b1a..c1a2241 100644
--- a/src/libmbim-glib/mbim-device.h
+++ b/src/libmbim-glib/mbim-device.h
@@ -51,6 +51,7 @@
#define MBIM_DEVICE_SIGNAL_INDICATE_STATUS "device-indicate-status"
#define MBIM_DEVICE_SIGNAL_ERROR "device-error"
+#define MBIM_DEVICE_SIGNAL_REMOVED "device-removed"
/**
* MbimDevice:
@@ -84,6 +85,28 @@
const gchar *mbim_device_get_path_display (MbimDevice *self);
gboolean mbim_device_is_open (MbimDevice *self);
+/**
+ * MbimDeviceOpenFlags:
+ * @MBIM_DEVICE_OPEN_FLAGS_NONE: None.
+ * @MBIM_DEVICE_OPEN_FLAGS_PROXY: Try to open the port through the 'mbim-proxy'.
+ *
+ * Flags to specify which actions to be performed when the device is open.
+ */
+typedef enum {
+ MBIM_DEVICE_OPEN_FLAGS_NONE = 0,
+ MBIM_DEVICE_OPEN_FLAGS_PROXY = 1 << 0
+} MbimDeviceOpenFlags;
+
+void mbim_device_open_full (MbimDevice *self,
+ MbimDeviceOpenFlags flags,
+ guint timeout,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mbim_device_open_full_finish (MbimDevice *self,
+ GAsyncResult *res,
+ GError **error);
+
void mbim_device_open (MbimDevice *self,
guint timeout,
GCancellable *cancellable,
diff --git a/src/libmbim-glib/mbim-message-private.h b/src/libmbim-glib/mbim-message-private.h
index 5d7bffe..b5606e0 100644
--- a/src/libmbim-glib/mbim-message-private.h
+++ b/src/libmbim-glib/mbim-message-private.h
@@ -129,6 +129,10 @@
} __attribute__((packed));
/*****************************************************************************/
+/* Message creation */
+GByteArray *_mbim_message_allocate (MbimMessageType message_type, guint32 transaction_id, guint32 additional_size);
+
+/*****************************************************************************/
/* Fragment interface */
gboolean _mbim_message_is_fragment (const MbimMessage *self);
diff --git a/src/libmbim-glib/mbim-message.c b/src/libmbim-glib/mbim-message.c
index 45d0a44..48f6018 100644
--- a/src/libmbim-glib/mbim-message.c
+++ b/src/libmbim-glib/mbim-message.c
@@ -62,7 +62,7 @@
/*****************************************************************************/
-static GByteArray *
+GByteArray *
_mbim_message_allocate (MbimMessageType message_type,
guint32 transaction_id,
guint32 additional_size)
@@ -1007,12 +1007,7 @@
mbim_message_set_transaction_id (MbimMessage *self,
guint32 transaction_id)
{
- /* Only allow setting transaction ID in host-generated messages */
g_return_if_fail (self != NULL);
- g_return_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND ||
- MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_OPEN ||
- MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_CLOSE ||
- MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_HOST_ERROR);
((struct header *)(self->data))->transaction_id = GUINT32_TO_LE (transaction_id);
}
@@ -1518,6 +1513,32 @@
/* 'Open Done' message interface */
/**
+ * mbim_message_open_done_new:
+ * @transaction_id: transaction ID.
+ * @error_status_code: a #MbimStatusError.
+ *
+ * Create a new #MbimMessage of type %MBIM_MESSAGE_TYPE_OPEN_DONE with the specified
+ * parameters.
+ *
+ * Returns: (transfer full): a newly created #MbimMessage, which should be freed with mbim_message_unref().
+ */
+MbimMessage *
+mbim_message_open_done_new (guint32 transaction_id,
+ MbimStatusError error_status_code)
+{
+ GByteArray *self;
+
+ self = _mbim_message_allocate (MBIM_MESSAGE_TYPE_OPEN_DONE,
+ transaction_id,
+ sizeof (struct open_done_message));
+
+ /* Open header */
+ ((struct full_message *)(self->data))->message.open_done.status_code = GUINT32_TO_LE (error_status_code);
+
+ return (MbimMessage *)self;
+}
+
+/**
* mbim_message_open_done_get_status_code:
* @self: a #MbimMessage.
*
@@ -1588,6 +1609,32 @@
/* 'Close Done' message interface */
/**
+ * mbim_message_close_done_new:
+ * @transaction_id: transaction ID.
+ * @error_status_code: a #MbimStatusError.
+ *
+ * Create a new #MbimMessage of type %MBIM_MESSAGE_TYPE_CLOSE_DONE with the specified
+ * parameters.
+ *
+ * Returns: (transfer full): a newly created #MbimMessage, which should be freed with mbim_message_unref().
+ */
+MbimMessage *
+mbim_message_close_done_new (guint32 transaction_id,
+ MbimStatusError error_status_code)
+{
+ GByteArray *self;
+
+ self = _mbim_message_allocate (MBIM_MESSAGE_TYPE_CLOSE_DONE,
+ transaction_id,
+ sizeof (struct close_done_message));
+
+ /* Open header */
+ ((struct full_message *)(self->data))->message.close_done.status_code = GUINT32_TO_LE (error_status_code);
+
+ return (MbimMessage *)self;
+}
+
+/**
* mbim_message_close_done_get_status_code:
* @self: a #MbimMessage.
*
@@ -1738,7 +1785,7 @@
/* Known service required */
g_return_val_if_fail (service > MBIM_SERVICE_INVALID, FALSE);
- g_return_val_if_fail (service <= MBIM_SERVICE_MS_HOST_SHUTDOWN || mbim_service_id_is_custom (service), FALSE);
+ g_return_val_if_fail (service <= MBIM_SERVICE_PROXY_CONTROL, FALSE);
service_id = mbim_uuid_from_service (service);
self = _mbim_message_allocate (MBIM_MESSAGE_TYPE_COMMAND,
diff --git a/src/libmbim-glib/mbim-message.h b/src/libmbim-glib/mbim-message.h
index 1c6892d..f07a807 100644
--- a/src/libmbim-glib/mbim-message.h
+++ b/src/libmbim-glib/mbim-message.h
@@ -131,9 +131,11 @@
/*****************************************************************************/
/* 'Open Done' message interface */
-MbimStatusError mbim_message_open_done_get_status_code (const MbimMessage *self);
-gboolean mbim_message_open_done_get_result (const MbimMessage *self,
- GError **error);
+MbimMessage *mbim_message_open_done_new (guint32 transaction_id,
+ MbimStatusError error_status_code);
+MbimStatusError mbim_message_open_done_get_status_code (const MbimMessage *self);
+gboolean mbim_message_open_done_get_result (const MbimMessage *self,
+ GError **error);
/*****************************************************************************/
/* 'Close' message interface */
@@ -143,9 +145,11 @@
/*****************************************************************************/
/* 'Close Done' message interface */
-MbimStatusError mbim_message_close_done_get_status_code (const MbimMessage *self);
-gboolean mbim_message_close_done_get_result (const MbimMessage *self,
- GError **error);
+MbimMessage *mbim_message_close_done_new (guint32 transaction_id,
+ MbimStatusError error_status_code);
+MbimStatusError mbim_message_close_done_get_status_code (const MbimMessage *self);
+gboolean mbim_message_close_done_get_result (const MbimMessage *self,
+ GError **error);
/*****************************************************************************/
/* 'Error' message interface */
diff --git a/src/libmbim-glib/mbim-proxy-helpers.c b/src/libmbim-glib/mbim-proxy-helpers.c
new file mode 100644
index 0000000..d0cd0d2
--- /dev/null
+++ b/src/libmbim-glib/mbim-proxy-helpers.c
@@ -0,0 +1,283 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2014 Smith Micro Software, Inc.
+ */
+
+#include <string.h>
+#include "mbim-proxy-helpers.h"
+#include "mbim-message-private.h"
+#include "mbim-message.h"
+#include "mbim-cid.h"
+#include "mbim-uuid.h"
+
+/*****************************************************************************/
+
+static gboolean
+cmp_event_entry_contents (const MbimEventEntry *in,
+ const MbimEventEntry *out)
+{
+ guint i, o;
+
+ g_assert (mbim_uuid_cmp (&(in->device_service_id), &(out->device_service_id)));
+
+ /* First, compare number of cids in the array */
+ if (in->cids_count != out->cids_count)
+ return FALSE;
+
+ if (in->cids_count == 0)
+ g_assert (in->cids == NULL);
+ if (out->cids_count == 0)
+ g_assert (out->cids == NULL);
+
+ for (i = 0; i < in->cids_count; i++) {
+ for (o = 0; o < out->cids_count; o++) {
+ if (in->cids[i] == out->cids[o])
+ break;
+ }
+ if (o == out->cids_count)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+_mbim_proxy_helper_service_subscribe_list_cmp (const MbimEventEntry * const *a,
+ gsize a_size,
+ const MbimEventEntry * const *b,
+ gsize b_size)
+{
+ gsize i, o;
+
+ /* First, compare number of entries a the array */
+ if (a_size != b_size)
+ return FALSE;
+
+ /* Now compare each service one by one */
+ for (i = 0; i < a_size; i++) {
+ /* Look for this same service a the other array */
+ for (o = 0; o < b_size; o++) {
+ /* When service found, compare contents */
+ if (mbim_uuid_cmp (&(a[i]->device_service_id), &(b[o]->device_service_id))) {
+ if (!cmp_event_entry_contents (a[i], b[o]))
+ return FALSE;
+ break;
+ }
+ }
+ /* Service not found! */
+ if (!b[o])
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+void
+_mbim_proxy_helper_service_subscribe_list_debug (const MbimEventEntry * const *list,
+ gsize list_size)
+{
+ gsize i;
+
+ for (i = 0; i < list_size; i++) {
+ const MbimEventEntry *entry = list[i];
+ MbimService service;
+ gchar *str;
+
+ service = mbim_uuid_to_service (&entry->device_service_id);
+ str = mbim_uuid_get_printable (&entry->device_service_id);
+ g_debug ("[service %u] %s (%s)",
+ (guint)i, str, mbim_service_lookup_name (service));
+ g_free (str);
+
+ if (entry->cids_count == 0)
+ g_debug ("[service %u] all CIDs enabled", (guint)i);
+ else {
+ guint j;
+
+ g_debug ("[service %u] %u CIDs enabled", (guint)i, entry->cids_count);;
+ for (j = 0; j < entry->cids_count; j++) {
+ const gchar *cid_str;
+
+ cid_str = mbim_cid_get_printable (service, entry->cids[j]);
+ g_debug ("[service %u] [cid %u] %u (%s)",
+ (guint)i, j, entry->cids[j], cid_str ? cid_str : "unknown");
+ }
+ }
+ }
+}
+
+/*****************************************************************************/
+
+MbimEventEntry **
+_mbim_proxy_helper_service_subscribe_standard_list_new (gsize *out_size)
+{
+ gsize i;
+ MbimService service;
+ MbimEventEntry **out;
+
+ g_assert (out_size != NULL);
+
+ out = g_new0 (MbimEventEntry *, 1 + (MBIM_SERVICE_DSS - MBIM_SERVICE_BASIC_CONNECT + 1));
+
+ for (service = MBIM_SERVICE_BASIC_CONNECT, i = 0;
+ service <= MBIM_SERVICE_DSS;
+ service++, i++) {
+ out[i] = g_new0 (MbimEventEntry, 1);
+ memcpy (&out[i]->device_service_id, mbim_uuid_from_service (service), sizeof (MbimUuid));
+ }
+
+ *out_size = i;
+ return out;
+}
+
+/*****************************************************************************/
+
+MbimEventEntry **
+_mbim_proxy_helper_service_subscribe_request_parse (MbimMessage *message,
+ gsize *out_size)
+{
+ MbimEventEntry **array = NULL;
+ guint32 i;
+ guint32 element_count;
+ guint32 offset = 0;
+ guint32 array_offset;
+ MbimEventEntry *event;
+
+ g_assert (message != NULL);
+ g_assert (out_size != NULL);
+
+ element_count = _mbim_message_read_guint32 (message, offset);
+ if (element_count) {
+ array = g_new (MbimEventEntry *, element_count + 1);
+
+ offset += 4;
+ for (i = 0; i < element_count; i++) {
+ array_offset = _mbim_message_read_guint32 (message, offset);
+
+ event = g_new (MbimEventEntry, 1);
+
+ memcpy (&(event->device_service_id), _mbim_message_read_uuid (message, array_offset), 16);
+ array_offset += 16;
+
+ event->cids_count = _mbim_message_read_guint32 (message, array_offset);
+ array_offset += 4;
+
+ if (event->cids_count)
+ event->cids = _mbim_message_read_guint32_array (message, event->cids_count, array_offset);
+ else
+ event->cids = NULL;
+
+ array[i] = event;
+ offset += 8;
+ }
+
+ array[element_count] = NULL;
+ }
+
+ *out_size = element_count;
+ return array;
+}
+
+/*****************************************************************************/
+
+MbimEventEntry **
+_mbim_proxy_helper_service_subscribe_list_merge (MbimEventEntry **in,
+ gsize in_size,
+ MbimEventEntry **merge,
+ gsize merge_size,
+ gsize *out_size)
+{
+ gsize m;
+
+ g_assert (out_size != NULL);
+
+ *out_size = in_size;
+
+ if (!merge || !merge_size)
+ return in;
+
+ for (m = 0; m < merge_size; m++) {
+ MbimEventEntry *entry = NULL;
+
+ /* look for matching uuid */
+ if (in && in_size) {
+ gsize i;
+
+ for (i = 0; i < in_size; i++) {
+ if (mbim_uuid_cmp (&merge[m]->device_service_id, &in[i]->device_service_id)) {
+ entry = in[i];
+ break;
+ }
+ }
+ }
+
+ /* matching uuid not found in merge array, add it */
+ if (!entry) {
+ gsize o;
+
+ /* Index of the new element to add... */
+ o = *out_size;
+
+ /* Increase number of events in the output array */
+ (*out_size)++;
+ in = g_realloc (in, sizeof (MbimEventEntry *) * (*out_size + 1));
+ in[o] = g_memdup (merge[m], sizeof (MbimEventEntry));
+ if (merge[m]->cids_count)
+ in[o]->cids = g_memdup (merge[m]->cids, sizeof (guint32) * merge[m]->cids_count);
+ else
+ in[o]->cids = NULL;
+ in[*out_size] = NULL;
+ } else {
+ gsize cm, co;
+
+ /* matching uuid found, add cids */
+ if (!entry->cids_count)
+ /* all cids already enabled for uuid */
+ continue;
+
+ /* If we're adding all enabled cids, directly apply that */
+ if (merge[m]->cids_count == 0) {
+ g_free (entry->cids);
+ entry->cids = NULL;
+ entry->cids_count = 0;
+ }
+
+ for (cm = 0; cm < merge[m]->cids_count; cm++) {
+ for (co = 0; co < entry->cids_count; co++) {
+ if (merge[m]->cids[cm] == entry->cids[co]) {
+ break;
+ }
+ }
+
+ if (co == entry->cids_count) {
+ /* cid not found in merge array, add it */
+ entry->cids = g_realloc (entry->cids, sizeof (guint32) * (++entry->cids_count));
+ entry->cids[co] = merge[m]->cids[cm];
+ }
+ }
+ }
+ }
+
+ return in;
+}
diff --git a/src/libmbim-glib/mbim-proxy-helpers.h b/src/libmbim-glib/mbim-proxy-helpers.h
new file mode 100644
index 0000000..6b43cde
--- /dev/null
+++ b/src/libmbim-glib/mbim-proxy-helpers.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2014 Smith Micro Software, Inc.
+ *
+ * This is a private non-installed header
+ */
+
+#ifndef _LIBMBIM_GLIB_MBIM_PROXY_HELPERS_H_
+#define _LIBMBIM_GLIB_MBIM_PROXY_HELPERS_H_
+
+#if !defined (LIBMBIM_GLIB_COMPILATION)
+#error "This is a private header!!"
+#endif
+
+#include <glib.h>
+
+#include "mbim-basic-connect.h"
+
+G_BEGIN_DECLS
+
+gboolean _mbim_proxy_helper_service_subscribe_list_cmp (const MbimEventEntry * const *a,
+ gsize a_size,
+ const MbimEventEntry * const *b,
+ gsize b_size);
+void _mbim_proxy_helper_service_subscribe_list_debug (const MbimEventEntry * const *list,
+ gsize list_size);
+MbimEventEntry **_mbim_proxy_helper_service_subscribe_standard_list_new (gsize *out_size);
+MbimEventEntry **_mbim_proxy_helper_service_subscribe_request_parse (MbimMessage *message,
+ gsize *out_size);
+MbimEventEntry **_mbim_proxy_helper_service_subscribe_list_merge (MbimEventEntry **original,
+ gsize original_size,
+ MbimEventEntry **merge,
+ gsize merge_size,
+ gsize *out_size);
+
+G_END_DECLS
+
+#endif /* _LIBMBIM_GLIB_MBIM_PROXY_HELPERS_H_ */
diff --git a/src/libmbim-glib/mbim-proxy.c b/src/libmbim-glib/mbim-proxy.c
new file mode 100644
index 0000000..7677cc6
--- /dev/null
+++ b/src/libmbim-glib/mbim-proxy.c
@@ -0,0 +1,1331 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Aleksander Morgado <aleksander@lanedo.com>
+ * Copyright (C) 2014 Smith Micro Software, Inc.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <sys/file.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gio/gunixsocketaddress.h>
+
+#include "mbim-device.h"
+#include "mbim-utils.h"
+#include "mbim-proxy.h"
+#include "mbim-message-private.h"
+#include "mbim-cid.h"
+#include "mbim-enum-types.h"
+#include "mbim-error-types.h"
+#include "mbim-basic-connect.h"
+#include "mbim-proxy-helpers.h"
+
+#define BUFFER_SIZE 512
+
+G_DEFINE_TYPE (MbimProxy, mbim_proxy, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_N_CLIENTS,
+ PROP_N_DEVICES,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+struct _MbimProxyPrivate {
+ /* Unix socket service */
+ GSocketService *socket_service;
+
+ /* Clients */
+ GList *clients;
+
+ /* Devices */
+ GList *devices;
+ GList *opening_devices;
+
+ /* Global events array */
+ MbimEventEntry **mbim_event_entry_array;
+ gsize mbim_event_entry_array_size;
+};
+
+static void track_device (MbimProxy *self, MbimDevice *device);
+static void untrack_device (MbimProxy *self, MbimDevice *device);
+static MbimDevice *peek_device_for_path (MbimProxy *self, const gchar *path);
+
+/*****************************************************************************/
+
+/**
+ * mbim_proxy_get_n_clients:
+ * @self: a #MbimProxy.
+ *
+ * Get the number of clients currently connected to the proxy.
+ *
+ * Returns: a #guint.
+ */
+guint
+mbim_proxy_get_n_clients (MbimProxy *self)
+{
+ g_return_val_if_fail (MBIM_IS_PROXY (self), 0);
+
+ return g_list_length (self->priv->clients);
+}
+
+/**
+ * mbim_proxy_get_n_devices:
+ * @self: a #MbimProxy.
+ *
+ * Get the number of devices currently connected to the proxy.
+ *
+ * Returns: a #guint.
+ */
+guint
+mbim_proxy_get_n_devices (MbimProxy *self)
+{
+ g_return_val_if_fail (MBIM_IS_PROXY (self), 0);
+
+ return g_list_length (self->priv->devices);
+}
+
+/*****************************************************************************/
+/* Client info */
+
+typedef struct {
+ volatile gint ref_count;
+
+ MbimProxy *self; /* not full ref */
+ GSocketConnection *connection;
+ GSource *connection_readable_source;
+ GByteArray *buffer;
+
+ /* Only one proxy config allowed at a time */
+ gboolean config_ongoing;
+
+ MbimDevice *device;
+ guint indication_id;
+ gboolean service_subscriber_list_enabled;
+ MbimEventEntry **mbim_event_entry_array;
+ gsize mbim_event_entry_array_size;
+} Client;
+
+static gboolean connection_readable_cb (GSocket *socket, GIOCondition condition, Client *client);
+static void track_client (MbimProxy *self, Client *client);
+static void untrack_client (MbimProxy *self, Client *client);
+
+static void
+client_disconnect (Client *client)
+{
+ if (client->connection_readable_source) {
+ g_source_destroy (client->connection_readable_source);
+ g_source_unref (client->connection_readable_source);
+ client->connection_readable_source = 0;
+ }
+
+ if (client->connection) {
+ g_debug ("Client (%d) connection closed...", g_socket_get_fd (g_socket_connection_get_socket (client->connection)));
+ g_output_stream_close (g_io_stream_get_output_stream (G_IO_STREAM (client->connection)), NULL, NULL);
+ g_object_unref (client->connection);
+ client->connection = NULL;
+ }
+}
+
+static void client_indication_cb (MbimDevice *device,
+ MbimMessage *message,
+ Client *client);
+
+static void
+client_set_device (Client *client,
+ MbimDevice *device)
+{
+ if (client->device) {
+ if (g_signal_handler_is_connected (client->device, client->indication_id))
+ g_signal_handler_disconnect (client->device, client->indication_id);
+ g_object_unref (client->device);
+ }
+
+ if (device) {
+ client->device = g_object_ref (device);
+ client->indication_id = g_signal_connect (client->device,
+ MBIM_DEVICE_SIGNAL_INDICATE_STATUS,
+ G_CALLBACK (client_indication_cb),
+ client);
+ } else {
+ client->device = NULL;
+ client->indication_id = 0;
+ }
+}
+
+static void
+client_unref (Client *client)
+{
+ if (g_atomic_int_dec_and_test (&client->ref_count)) {
+ /* Ensure disconnected */
+ client_disconnect (client);
+ /* Reset device */
+ client_set_device (client, NULL);
+
+ if (client->buffer)
+ g_byte_array_unref (client->buffer);
+
+ if (client->mbim_event_entry_array)
+ mbim_event_entry_array_free (client->mbim_event_entry_array);
+
+ g_slice_free (Client, client);
+ }
+}
+
+static Client *
+client_ref (Client *client)
+{
+ g_atomic_int_inc (&client->ref_count);
+ return client;
+}
+
+static gboolean
+client_send_message (Client *client,
+ MbimMessage *message,
+ GError **error)
+{
+ if (!client->connection) {
+ g_set_error (error,
+ MBIM_CORE_ERROR,
+ MBIM_CORE_ERROR_WRONG_STATE,
+ "Cannot send message: not connected");
+ return FALSE;
+ }
+
+ g_debug ("Client (%d) TX: %u bytes", g_socket_get_fd (g_socket_connection_get_socket (client->connection)), message->len);
+ if (!g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (client->connection)),
+ message->data,
+ message->len,
+ NULL, /* bytes_written */
+ NULL, /* cancellable */
+ error)) {
+ g_prefix_error (error, "Cannot send message to client: ");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Track/untrack clients */
+
+static void
+track_client (MbimProxy *self,
+ Client *client)
+{
+ self->priv->clients = g_list_append (self->priv->clients, client_ref (client));
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_N_CLIENTS]);
+}
+
+static void
+untrack_client (MbimProxy *self,
+ Client *client)
+{
+ /* Disconnect the client explicitly when untracking */
+ client_disconnect (client);
+
+ if (g_list_find (self->priv->clients, client)) {
+ self->priv->clients = g_list_remove (self->priv->clients, client);
+ client_unref (client);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_N_CLIENTS]);
+ }
+}
+
+/*****************************************************************************/
+/* Client indications */
+
+static void
+client_indication_cb (MbimDevice *device,
+ MbimMessage *message,
+ Client *client)
+{
+ guint i;
+ GError *error = NULL;
+ gboolean forward_indication = FALSE;
+ MbimEventEntry *event = NULL;
+
+ if (client->service_subscriber_list_enabled) {
+ /* if client sent the device service subscribe list with element count 0 then
+ * ignore all indications */
+ if (client->mbim_event_entry_array) {
+ for (i = 0; i < client->mbim_event_entry_array_size; i++) {
+ if (mbim_uuid_cmp (mbim_message_indicate_status_get_service_id (message),
+ &client->mbim_event_entry_array[i]->device_service_id)) {
+ event = client->mbim_event_entry_array[i];
+ break;
+ }
+ }
+
+ if (event) {
+ /* found matching service, search for cid */
+ if (event->cids_count) {
+ for (i = 0; i < event->cids_count; i++) {
+ if (mbim_message_indicate_status_get_cid (message) == event->cids[i]) {
+ forward_indication = TRUE;
+ break;
+ }
+ }
+ } else
+ /* cids_count of 0 enables all indications for the service */
+ forward_indication = TRUE;
+ }
+ }
+ } else if (mbim_message_indicate_status_get_service (message) != MBIM_SERVICE_INVALID &&
+ !mbim_service_id_is_custom (mbim_message_indicate_status_get_service (message)))
+ /* only forward standard service indications if service subscriber list is not enabled */
+ forward_indication = TRUE;
+
+ if (forward_indication) {
+ if (!client_send_message (client, message, &error)) {
+ g_warning ("couldn't forward indication to client");
+ g_error_free (error);
+ }
+ }
+}
+
+/*****************************************************************************/
+/* Request info */
+
+typedef struct {
+ MbimProxy *self;
+ Client *client;
+ MbimMessage *message;
+ MbimMessage *response;
+ guint32 original_transaction_id;
+ /* Only used in proxy config */
+ guint32 timeout_secs;
+} Request;
+
+static void
+request_complete_and_free (Request *request)
+{
+ if (request->response) {
+ GError *error = NULL;
+
+ /* Try to send response to client; if it fails, always assume we have
+ * to close the connection */
+ if (!client_send_message (request->client, request->response, &error)) {
+ g_debug ("couldn't send response back to client: %s", error->message);
+ g_error_free (error);
+ /* Disconnect and untrack client */
+ untrack_client (request->self, request->client);
+ }
+
+ mbim_message_unref (request->response);
+ }
+
+ if (request->message)
+ mbim_message_unref (request->message);
+ client_unref (request->client);
+ g_object_unref (request->self);
+ g_slice_free (Request, request);
+}
+
+static Request *
+request_new (MbimProxy *self,
+ Client *client,
+ MbimMessage *message)
+{
+ Request *request;
+
+ request = g_slice_new0 (Request);
+ request->self = g_object_ref (self);
+ request->client = client_ref (client);
+ request->message = mbim_message_ref (message);
+ request->original_transaction_id = mbim_message_get_transaction_id (message);
+
+ return request;
+}
+
+/*****************************************************************************/
+/* Internal proxy device opening operation */
+
+static gboolean
+internal_device_open_finish (MbimProxy *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+typedef struct {
+ MbimDevice *device;
+ GList *pending;
+} OpeningDevice;
+
+static void
+opening_device_complete_and_free (OpeningDevice *info,
+ const GError *error)
+{
+ GList *l;
+
+ /* Complete all pending open actions */
+ for (l = info->pending; l; l = g_list_next (l)) {
+ GSimpleAsyncResult *simple = (GSimpleAsyncResult *)(l->data);
+
+ if (error)
+ g_simple_async_result_set_from_error (simple, error);
+ else
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ }
+
+ g_list_free (info->pending);
+ g_object_unref (info->device);
+ g_slice_free (OpeningDevice, info);
+}
+
+static OpeningDevice *
+peek_opening_device_info (MbimProxy *self,
+ MbimDevice *device)
+{
+ GList *l;
+
+ /* If already being opened, queue it up */
+ for (l = self->priv->opening_devices; l; l = g_list_next (l)) {
+ OpeningDevice *info;
+
+ info = (OpeningDevice *)(l->data);
+ if (g_str_equal (mbim_device_get_path (device), mbim_device_get_path (info->device)))
+ return info;
+ }
+
+ return NULL;
+}
+
+static void
+cancel_opening_device (MbimProxy *self,
+ MbimDevice *device)
+{
+ OpeningDevice *info;
+ GError *error;
+
+ info = peek_opening_device_info (self, device);
+ if (!info)
+ return;
+
+ error = g_error_new (MBIM_CORE_ERROR, MBIM_CORE_ERROR_ABORTED, "Device is gone");
+ opening_device_complete_and_free (info, error);
+ g_error_free (error);
+}
+
+static void
+device_open_ready (MbimDevice *device,
+ GAsyncResult *res,
+ MbimProxy *self)
+{
+ GError *error = NULL;
+ OpeningDevice *info;
+
+ mbim_device_open_finish (device, res, &error);
+
+ info = peek_opening_device_info (self, device);
+ g_assert (info != NULL);
+
+ /* Remove opening device info */
+ self->priv->opening_devices = g_list_remove (self->priv->opening_devices, info);
+
+ /* Complete all pending open actions */
+ opening_device_complete_and_free (info, error);
+
+ if (error) {
+ /* Fully untrack the device as it wasn't correctly open */
+ untrack_device (self, device);
+ g_error_free (error);
+ }
+}
+
+static void
+internal_device_open (MbimProxy *self,
+ MbimDevice *device,
+ guint32 timeout_secs,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ OpeningDevice *info;
+ GSimpleAsyncResult *simple;
+
+ simple = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ internal_device_open);
+
+ /* If the device is already open, we are done */
+ if (mbim_device_is_open (device)) {
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ return;
+ }
+
+ /* If already being opened, queue it up */
+ info = peek_opening_device_info (self, device);
+ if (info) {
+ info->pending = g_list_append (info->pending, simple);
+ return;
+ }
+
+ /* First time opening, go on */
+ info = g_slice_new0 (OpeningDevice);
+ info->device = g_object_ref (device);
+ info->pending = g_list_append (info->pending, simple);
+ self->priv->opening_devices = g_list_prepend (self->priv->opening_devices, info);
+
+ /* Note: for now, only the first timeout request is taken into account */
+
+ /* Need to open the device; and we must make sure the proxy only does this once, even
+ * when multiple clients request it */
+ mbim_device_open (device,
+ timeout_secs,
+ NULL,
+ (GAsyncReadyCallback)device_open_ready,
+ g_object_ref (self));
+}
+
+/*****************************************************************************/
+/* Proxy open */
+
+static gboolean
+process_internal_proxy_open (MbimProxy *self,
+ Client *client,
+ MbimMessage *message)
+{
+ Request *request;
+ MbimStatusError status = MBIM_STATUS_ERROR_FAILURE;
+
+ /* create request holder */
+ request = request_new (self, client, message);
+
+ if (!client->device)
+ g_warning ("cannot process Open: device not set");
+ else if (!mbim_device_is_open (client->device))
+ g_warning ("cannot process Open: device not opened by proxy");
+ else {
+ g_debug ("connection to MBIM device '%s' established", mbim_device_get_path (client->device));
+ status = MBIM_STATUS_ERROR_NONE;
+ }
+
+ request->response = mbim_message_open_done_new (mbim_message_get_transaction_id (request->message), status);
+ request_complete_and_free (request);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Proxy close */
+
+static gboolean
+process_internal_proxy_close (MbimProxy *self,
+ Client *client,
+ MbimMessage *message)
+{
+ Request *request;
+
+ request = request_new (self, client, message);
+ request->response = mbim_message_close_done_new (mbim_message_get_transaction_id (message), MBIM_STATUS_ERROR_NONE);
+ request_complete_and_free (request);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Proxy config */
+
+static MbimMessage *
+build_proxy_control_command_done (MbimMessage *message,
+ MbimStatusError status)
+{
+ MbimMessage *response;
+ struct command_done_message *command_done;
+
+ response = (MbimMessage *) _mbim_message_allocate (MBIM_MESSAGE_TYPE_COMMAND_DONE,
+ mbim_message_get_transaction_id (message),
+ sizeof (struct command_done_message));
+ command_done = &(((struct full_message *)(response->data))->message.command_done);
+ command_done->fragment_header.total = GUINT32_TO_LE (1);
+ command_done->fragment_header.current = 0;
+ memcpy (command_done->service_id, MBIM_UUID_PROXY_CONTROL, sizeof (MbimUuid));
+ command_done->command_id = GUINT32_TO_LE (mbim_message_command_get_cid (message));
+ command_done->status_code = GUINT32_TO_LE (status);
+ command_done->buffer_length = 0;
+
+ return response;
+}
+
+static void
+proxy_config_internal_device_open_ready (MbimProxy *self,
+ GAsyncResult *res,
+ Request *request)
+{
+ GError *error = NULL;
+
+ if (!internal_device_open_finish (self, res, &error)) {
+ g_warning ("error opening device: %s", error->message);
+ g_error_free (error);
+ /* Untrack client and complete without response */
+ untrack_client (request->self, request->client);
+ request_complete_and_free (request);
+ return;
+ }
+
+ if (request->client->config_ongoing == TRUE)
+ request->client->config_ongoing = FALSE;
+ request->response = build_proxy_control_command_done (request->message, MBIM_STATUS_ERROR_NONE);
+ request_complete_and_free (request);
+}
+
+static void
+device_new_ready (GObject *source,
+ GAsyncResult *res,
+ Request *request)
+{
+ GError *error = NULL;
+ MbimDevice *existing;
+ MbimDevice *device;
+
+ device = mbim_device_new_finish (res, &error);
+ if (!device) {
+ g_warning ("couldn't create MBIM device: %s", error->message);
+ g_error_free (error);
+ /* Untrack client and complete without response */
+ untrack_client (request->self, request->client);
+ request_complete_and_free (request);
+ return;
+ }
+
+ /* Store device in the proxy independently */
+ existing = peek_device_for_path (request->self, mbim_device_get_path (device));
+ if (existing) {
+ /* Race condition, we created two MbimDevices for the same port, just skip ours, no big deal */
+ client_set_device (request->client, existing);
+ } else {
+ /* Keep the newly added device in the proxy */
+ track_device (request->self, device);
+ /* Also keep track of the device in the client */
+ client_set_device (request->client, device);
+ }
+ g_object_unref (device);
+
+ internal_device_open (request->self,
+ request->client->device,
+ request->timeout_secs,
+ (GAsyncReadyCallback)proxy_config_internal_device_open_ready,
+ request);
+}
+
+static gboolean
+process_internal_proxy_config (MbimProxy *self,
+ Client *client,
+ MbimMessage *message)
+{
+ Request *request;
+ MbimDevice *device;
+ gchar *path;
+ GFile *file;
+
+ /* create request holder */
+ request = request_new (self, client, message);
+
+ /* Error out if there is already a proxy config ongoing */
+ if (client->config_ongoing) {
+ request->response = build_proxy_control_command_done (message, MBIM_STATUS_ERROR_BUSY);
+ request_complete_and_free (request);
+ return TRUE;
+ }
+
+ /* Only allow SET command */
+ if (mbim_message_command_get_command_type (message) != MBIM_MESSAGE_COMMAND_TYPE_SET) {
+ request->response = build_proxy_control_command_done (message, MBIM_STATUS_ERROR_INVALID_PARAMETERS);
+ request_complete_and_free (request);
+ return TRUE;
+ }
+
+ /* Retrieve path from request */
+ path = _mbim_message_read_string (message, 0, 0);
+ if (!path) {
+ request->response = build_proxy_control_command_done (message, MBIM_STATUS_ERROR_INVALID_PARAMETERS);
+ request_complete_and_free (request);
+ return TRUE;
+ }
+
+ /* Only allow subsequent requests with the same path */
+ if (client->device) {
+ if (g_str_equal (path, mbim_device_get_path (client->device)))
+ request->response = build_proxy_control_command_done (message, MBIM_STATUS_ERROR_NONE);
+ else
+ request->response = build_proxy_control_command_done (message, MBIM_STATUS_ERROR_FAILURE);
+ request_complete_and_free (request);
+ g_free (path);
+ return TRUE;
+ }
+
+ /* Read requested timeout value */
+ request->timeout_secs = _mbim_message_read_guint32 (message, 8);
+
+ /* Check if some other client already handled the same device */
+ device = peek_device_for_path (self, path);
+ if (device) {
+ /* Keep reference and continue */
+ client_set_device (client, device);
+
+ internal_device_open (self,
+ device,
+ request->timeout_secs,
+ (GAsyncReadyCallback)proxy_config_internal_device_open_ready,
+ request);
+ g_free (path);
+ return TRUE;
+ }
+
+ /* Flag as ongoing */
+ client->config_ongoing = TRUE;
+
+ /* Create new MBIM device */
+ file = g_file_new_for_path (path);
+ mbim_device_new (file,
+ NULL,
+ (GAsyncReadyCallback)device_new_ready,
+ request);
+ g_object_unref (file);
+ g_free (path);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Subscriber list */
+
+static void
+track_service_subscribe_list (Client *client,
+ MbimMessage *message)
+{
+ client->service_subscriber_list_enabled = TRUE;
+
+ if (client->mbim_event_entry_array)
+ mbim_event_entry_array_free (client->mbim_event_entry_array);
+
+ client->mbim_event_entry_array = _mbim_proxy_helper_service_subscribe_request_parse (message, &client->mbim_event_entry_array_size);
+
+ if (mbim_utils_get_traces_enabled ()) {
+ g_debug ("Client (%d) service subscribe list built", g_socket_get_fd (g_socket_connection_get_socket (client->connection)));
+ _mbim_proxy_helper_service_subscribe_list_debug ((const MbimEventEntry * const *)client->mbim_event_entry_array,
+ client->mbim_event_entry_array_size);
+ }
+}
+
+static MbimEventEntry **
+merge_client_service_subscribe_lists (MbimProxy *self,
+ gsize *out_size)
+{
+ GList *l;
+ MbimEventEntry **updated;
+ gsize updated_size = 0;
+
+ g_assert (out_size != NULL);
+
+ /* Add previous global list */
+ updated = _mbim_proxy_helper_service_subscribe_list_merge (NULL, 0,
+ self->priv->mbim_event_entry_array, self->priv->mbim_event_entry_array_size,
+ &updated_size);
+
+ for (l = self->priv->clients; l; l = g_list_next (l)) {
+ Client *client;
+
+ client = l->data;
+ if (!client->mbim_event_entry_array)
+ continue;
+
+ /* Add per-client list */
+ updated = _mbim_proxy_helper_service_subscribe_list_merge (updated, updated_size,
+ client->mbim_event_entry_array, client->mbim_event_entry_array_size,
+ &updated_size);
+ }
+
+ if (mbim_utils_get_traces_enabled ()) {
+ g_debug ("Merged service subscribe list built");
+ _mbim_proxy_helper_service_subscribe_list_debug ((const MbimEventEntry * const *)updated, updated_size);
+ }
+
+ *out_size = updated_size;
+ return updated;
+}
+
+static void
+device_service_subscribe_list_set_complete (Request *request,
+ MbimStatusError status)
+{
+ struct command_done_message *command_done;
+ guint32 raw_len;
+ const guint8 *raw_data;
+
+ /* The raw message data to send back as response to client */
+ raw_data = mbim_message_command_get_raw_information_buffer (request->message, &raw_len);
+
+ request->response = (MbimMessage *)_mbim_message_allocate (MBIM_MESSAGE_TYPE_COMMAND_DONE,
+ mbim_message_get_transaction_id (request->message),
+ sizeof (struct command_done_message) +
+ raw_len);
+ command_done = &(((struct full_message *)(request->response->data))->message.command_done);
+ command_done->fragment_header.total = GUINT32_TO_LE (1);
+ command_done->fragment_header.current = 0;
+ memcpy (command_done->service_id, MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
+ command_done->command_id = GUINT32_TO_LE (MBIM_CID_BASIC_CONNECT_DEVICE_SERVICE_SUBSCRIBE_LIST);
+ command_done->status_code = GUINT32_TO_LE (status);
+ command_done->buffer_length = GUINT32_TO_LE (raw_len);
+ memcpy (&command_done->buffer[0], raw_data, raw_len);
+
+ request_complete_and_free (request);
+}
+
+static void
+device_service_subscribe_list_set_ready (MbimDevice *device,
+ GAsyncResult *res,
+ Request *request)
+{
+ MbimMessage *tmp_response;
+ MbimStatusError error_status_code;
+ GError *error = NULL;
+
+ tmp_response = mbim_device_command_finish (device, res, &error);
+ if (!tmp_response) {
+ g_debug ("sending request to device failed: %s", error->message);
+ g_error_free (error);
+ /* Don't disconnect client, just let the request timeout in its side */
+ request_complete_and_free (request);
+ return;
+ }
+
+ error_status_code = GUINT32_FROM_LE (((struct full_message *)(tmp_response->data))->message.command_done.status_code);
+ mbim_message_unref (tmp_response);
+
+ device_service_subscribe_list_set_complete (request, error_status_code);
+}
+
+static gboolean
+process_device_service_subscribe_list (MbimProxy *self,
+ Client *client,
+ MbimMessage *message)
+{
+ MbimEventEntry **updated;
+ gsize updated_size = 0;
+ Request *request;
+
+ /* create request holder */
+ request = request_new (self, client, message);
+
+ /* trace the service subscribe list for the client */
+ track_service_subscribe_list (client, message);
+
+ /* merge all service subscribe list for all clients to set on device */
+ updated = merge_client_service_subscribe_lists (self, &updated_size);
+
+ /* If lists are equal, ignore re-setting them up */
+ if (_mbim_proxy_helper_service_subscribe_list_cmp (
+ (const MbimEventEntry *const *)updated, updated_size,
+ (const MbimEventEntry *const *)self->priv->mbim_event_entry_array, self->priv->mbim_event_entry_array_size)) {
+ /* Complete directly without error */
+ mbim_event_entry_array_free (updated);
+ device_service_subscribe_list_set_complete (request, MBIM_STATUS_ERROR_NONE);
+ return TRUE;
+ }
+
+ /* Lists are different, updated stored one */
+ mbim_event_entry_array_free (self->priv->mbim_event_entry_array);
+ self->priv->mbim_event_entry_array = updated;
+ self->priv->mbim_event_entry_array_size = updated_size;
+
+ message = mbim_message_device_service_subscribe_list_set_new (self->priv->mbim_event_entry_array_size,
+ (const MbimEventEntry *const *)self->priv->mbim_event_entry_array,
+ NULL);
+ mbim_message_set_transaction_id (message, mbim_device_get_next_transaction_id (client->device));
+
+ mbim_device_command (client->device,
+ message,
+ 300,
+ NULL,
+ (GAsyncReadyCallback)device_service_subscribe_list_set_ready,
+ request);
+
+ mbim_message_unref (message);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Standard command */
+
+static void
+device_command_ready (MbimDevice *device,
+ GAsyncResult *res,
+ Request *request)
+{
+ GError *error = NULL;
+
+ request->response = mbim_device_command_finish (device, res, &error);
+ if (!request->response) {
+ g_debug ("sending request to device failed: %s", error->message);
+ g_error_free (error);
+ /* Don't disconnect client, just let the request timeout in its side */
+ request_complete_and_free (request);
+ return;
+ }
+
+ /* replace reponse transaction id with the requested transaction id */
+ mbim_message_set_transaction_id (request->response, request->original_transaction_id);
+
+ request_complete_and_free (request);
+}
+
+static gboolean
+process_command (MbimProxy *self,
+ Client *client,
+ MbimMessage *message)
+{
+ Request *request;
+
+ /* create request holder */
+ request = request_new (self, client, message);
+
+ /* replace command transaction id with internal proxy transaction id to avoid collision */
+ mbim_message_set_transaction_id (message, mbim_device_get_next_transaction_id (client->device));
+
+ /* The timeout needs to be big enough for any kind of transaction to
+ * complete, otherwise the remote clients will lose the reply if they
+ * configured a timeout bigger than this internal one. We should likely
+ * make this value configurable per-client, instead of a hardcoded value.
+ */
+ mbim_device_command (client->device,
+ message,
+ 300,
+ NULL,
+ (GAsyncReadyCallback)device_command_ready,
+ request);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static gboolean
+process_message (MbimProxy *self,
+ Client *client,
+ MbimMessage *message)
+{
+ /* Filter by message type */
+ switch (mbim_message_get_message_type (message)) {
+ case MBIM_MESSAGE_TYPE_OPEN:
+ return process_internal_proxy_open (self, client, message);
+ case MBIM_MESSAGE_TYPE_CLOSE:
+ return process_internal_proxy_close (self, client, message);
+ case MBIM_MESSAGE_TYPE_COMMAND:
+ /* Proxy control message? */
+ if (mbim_message_command_get_service (message) == MBIM_SERVICE_PROXY_CONTROL &&
+ mbim_message_command_get_cid (message) == MBIM_CID_PROXY_CONTROL_CONFIGURATION)
+ return process_internal_proxy_config (self, client, message);
+ /* device service subscribe list message? */
+ if (mbim_message_command_get_service (message) == MBIM_SERVICE_BASIC_CONNECT &&
+ mbim_message_command_get_cid (message) == MBIM_CID_BASIC_CONNECT_DEVICE_SERVICE_SUBSCRIBE_LIST)
+ return process_device_service_subscribe_list (self, client, message);
+ /* Otherwise, standard command to forward */
+ return process_command (self, client, message);
+ default:
+ g_debug ("invalid message from client: not a command message");
+ return FALSE;
+ }
+
+ g_assert_not_reached ();
+}
+
+static void
+parse_request (MbimProxy *self,
+ Client *client)
+{
+ do {
+ MbimMessage *message;
+ guint32 len = 0;
+
+ if (client->buffer->len >= sizeof (struct header) &&
+ (len = GUINT32_FROM_LE (((struct header *)client->buffer->data)->length)) > client->buffer->len) {
+ /* have not received complete message */
+ return;
+ }
+
+ if (!len)
+ return;
+
+ message = mbim_message_new (client->buffer->data, len);
+ if (!message)
+ return;
+ g_byte_array_remove_range (client->buffer, 0, len);
+
+ /* Play with the received message */
+ process_message (self, client, message);
+ mbim_message_unref (message);
+ } while (client->buffer->len > 0);
+}
+
+static gboolean
+connection_readable_cb (GSocket *socket,
+ GIOCondition condition,
+ Client *client)
+{
+ MbimProxy *self;
+ guint8 buffer[BUFFER_SIZE];
+ GError *error = NULL;
+ gssize r;
+
+ /* Recover proxy pointer soon */
+ self = client->self;
+
+ if (condition & G_IO_HUP || condition & G_IO_ERR) {
+ untrack_client (self, client);
+ return FALSE;
+ }
+
+ if (!(condition & G_IO_IN || condition & G_IO_PRI))
+ return TRUE;
+
+ r = g_input_stream_read (g_io_stream_get_input_stream (G_IO_STREAM (client->connection)),
+ buffer,
+ BUFFER_SIZE,
+ NULL,
+ &error);
+ if (r < 0) {
+ g_warning ("Error reading from istream: %s", error ? error->message : "unknown");
+ if (error)
+ g_error_free (error);
+ /* Close the device */
+ untrack_client (self, client);
+ return FALSE;
+ }
+
+ if (r == 0)
+ return TRUE;
+
+ /* else, r > 0 */
+ if (!G_UNLIKELY (client->buffer))
+ client->buffer = g_byte_array_sized_new (r);
+ g_byte_array_append (client->buffer, buffer, r);
+
+ /* Try to parse input messages */
+ parse_request (self, client);
+
+ return TRUE;
+}
+
+static void
+incoming_cb (GSocketService *service,
+ GSocketConnection *connection,
+ GObject *unused,
+ MbimProxy *self)
+{
+ Client *client;
+ GCredentials *credentials;
+ GError *error = NULL;
+ uid_t uid;
+
+ g_debug ("Client (%d) connection open...", g_socket_get_fd (g_socket_connection_get_socket (connection)));
+
+ credentials = g_socket_get_credentials (g_socket_connection_get_socket (connection), &error);
+ if (!credentials) {
+ g_warning ("Client not allowed: Error getting socket credentials: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ uid = g_credentials_get_unix_user (credentials, &error);
+ g_object_unref (credentials);
+ if (error) {
+ g_warning ("Client not allowed: Error getting unix user id: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ if (uid != 0) {
+ g_warning ("Client not allowed: Not enough privileges");
+ return;
+ }
+
+ /* Create client */
+ client = g_slice_new0 (Client);
+ client->self = self;
+ client->ref_count = 1;
+ client->connection = g_object_ref (connection);
+ client->connection_readable_source = g_socket_create_source (g_socket_connection_get_socket (client->connection),
+ G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
+ NULL);
+ g_source_set_callback (client->connection_readable_source,
+ (GSourceFunc)connection_readable_cb,
+ client,
+ NULL);
+ g_source_attach (client->connection_readable_source, NULL);
+
+ /* Keep the client info around */
+ track_client (self, client);
+
+ client_unref (client);
+}
+
+static gboolean
+setup_socket_service (MbimProxy *self,
+ GError **error)
+{
+ GSocketAddress *socket_address;
+ GSocket *socket;
+
+ socket = g_socket_new (G_SOCKET_FAMILY_UNIX,
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_DEFAULT,
+ error);
+ if (!socket)
+ return FALSE;
+
+ /* Bind to address */
+ socket_address = (g_unix_socket_address_new_with_type (
+ MBIM_PROXY_SOCKET_PATH,
+ -1,
+ G_UNIX_SOCKET_ADDRESS_ABSTRACT));
+ if (!g_socket_bind (socket, socket_address, TRUE, error)) {
+ g_object_unref (socket_address);
+ g_object_unref (socket);
+ return FALSE;
+ }
+ g_object_unref (socket_address);
+
+ g_debug ("creating UNIX socket service...");
+
+ /* Listen */
+ if (!g_socket_listen (socket, error)) {
+ g_object_unref (socket);
+ return FALSE;
+ }
+
+ /* Create socket service */
+ self->priv->socket_service = g_socket_service_new ();
+ g_signal_connect (self->priv->socket_service, "incoming", G_CALLBACK (incoming_cb), self);
+ if (!g_socket_listener_add_socket (G_SOCKET_LISTENER (self->priv->socket_service),
+ socket,
+ NULL, /* don't pass an object, will take a reference */
+ error)) {
+ g_prefix_error (error, "Error adding socket at '%s' to socket service: ", MBIM_PROXY_SOCKET_PATH);
+ g_object_unref (socket);
+ return FALSE;
+ }
+
+ g_debug ("starting UNIX socket service at '%s'...", MBIM_PROXY_SOCKET_PATH);
+ g_socket_service_start (self->priv->socket_service);
+ g_object_unref (socket);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Device tracking */
+
+static MbimDevice *
+peek_device_for_path (MbimProxy *self,
+ const gchar *path)
+{
+ GList *l;
+
+ for (l = self->priv->devices; l; l = g_list_next (l)) {
+ /* Return if found */
+ if (g_str_equal (mbim_device_get_path ((MbimDevice *)l->data), path))
+ return (MbimDevice *)l->data;
+ }
+
+ return NULL;
+}
+
+static void
+proxy_device_removed_cb (MbimDevice *device,
+ MbimProxy *self)
+{
+ untrack_device (self, device);
+}
+
+static void
+untrack_device (MbimProxy *self,
+ MbimDevice *device)
+{
+ GList *l;
+ GList *to_remove = NULL;
+
+ if (!g_list_find (self->priv->devices, device))
+ return;
+
+ /* Disconnect right away */
+ g_signal_handlers_disconnect_by_func (device, proxy_device_removed_cb, self);
+
+ /* If pending openings ongoing, complete them with error */
+ cancel_opening_device (self, device);
+
+ /* Lookup all clients with this device */
+ for (l = self->priv->clients; l; l = g_list_next (l)) {
+ if (g_str_equal (mbim_device_get_path (((Client *)(l->data))->device), mbim_device_get_path (device)))
+ to_remove = g_list_append (to_remove, l->data);
+ }
+
+ /* Remove all these clients */
+ for (l = to_remove; l; l = g_list_next (l))
+ untrack_client (self, (Client *)(l->data));
+ g_list_free (to_remove);
+
+ /* And finally, remove the device */
+ self->priv->devices = g_list_remove (self->priv->devices, device);
+ g_object_unref (device);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_N_DEVICES]);
+}
+
+static void
+track_device (MbimProxy *self,
+ MbimDevice *device)
+{
+ self->priv->devices = g_list_append (self->priv->devices, g_object_ref (device));
+ g_signal_connect (device,
+ MBIM_DEVICE_SIGNAL_REMOVED,
+ G_CALLBACK (proxy_device_removed_cb),
+ self);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_N_DEVICES]);
+}
+
+/*****************************************************************************/
+
+MbimProxy *
+mbim_proxy_new (GError **error)
+{
+ MbimProxy *self;
+
+ /* Only root can run the mbim-proxy */
+ if (getuid () != 0) {
+ g_set_error (error,
+ MBIM_CORE_ERROR,
+ MBIM_CORE_ERROR_FAILED,
+ "Not enough privileges");
+ return NULL;
+ }
+
+ self = g_object_new (MBIM_TYPE_PROXY, NULL);
+ if (!setup_socket_service (self, error))
+ g_clear_object (&self);
+ return self;
+}
+
+static void
+mbim_proxy_init (MbimProxy *self)
+{
+ /* Setup private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MBIM_TYPE_PROXY,
+ MbimProxyPrivate);
+
+ /* By default, we assume we have all default services enabled */
+ self->priv->mbim_event_entry_array = _mbim_proxy_helper_service_subscribe_standard_list_new (&self->priv->mbim_event_entry_array_size);
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MbimProxy *self = MBIM_PROXY (object);
+
+ switch (prop_id) {
+ case PROP_N_CLIENTS:
+ g_value_set_uint (value, g_list_length (self->priv->clients));
+ break;
+ case PROP_N_DEVICES:
+ g_value_set_uint (value, g_list_length (self->priv->devices));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+dispose (GObject *object)
+{
+ MbimProxyPrivate *priv = MBIM_PROXY (object)->priv;
+
+ /* This list should always be empty when disposing */
+ g_assert (priv->opening_devices == NULL);
+
+ if (priv->clients) {
+ g_list_free_full (priv->clients, (GDestroyNotify) client_unref);
+ priv->clients = NULL;
+ }
+
+ if (priv->devices) {
+ g_list_free_full (priv->devices, (GDestroyNotify) g_object_unref);
+ priv->devices = NULL;
+ }
+
+ if (priv->socket_service) {
+ if (g_socket_service_is_active (priv->socket_service))
+ g_socket_service_stop (priv->socket_service);
+ g_clear_object (&priv->socket_service);
+ g_unlink (MBIM_PROXY_SOCKET_PATH);
+ g_debug ("UNIX socket service at '%s' stopped", MBIM_PROXY_SOCKET_PATH);
+ }
+
+ if (priv->mbim_event_entry_array) {
+ mbim_event_entry_array_free (priv->mbim_event_entry_array);
+ priv->mbim_event_entry_array = NULL;
+ priv->mbim_event_entry_array_size = 0;
+ }
+
+ G_OBJECT_CLASS (mbim_proxy_parent_class)->dispose (object);
+}
+
+static void
+mbim_proxy_class_init (MbimProxyClass *proxy_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (proxy_class);
+
+ g_type_class_add_private (object_class, sizeof (MbimProxyPrivate));
+
+ /* Virtual methods */
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
+
+ /* Properties */
+ properties[PROP_N_CLIENTS] =
+ g_param_spec_uint (MBIM_PROXY_N_CLIENTS,
+ "Number of clients",
+ "Number of clients currently connected to the proxy",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_N_CLIENTS, properties[PROP_N_CLIENTS]);
+
+ properties[PROP_N_DEVICES] =
+ g_param_spec_uint (MBIM_PROXY_N_DEVICES,
+ "Number of devices",
+ "Number of devices currently managed by the proxy",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_N_DEVICES, properties[PROP_N_DEVICES]);
+}
diff --git a/src/libmbim-glib/mbim-proxy.h b/src/libmbim-glib/mbim-proxy.h
new file mode 100644
index 0000000..f874cc4
--- /dev/null
+++ b/src/libmbim-glib/mbim-proxy.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Aleksander Morgado <aleksander@lanedo.com>
+ * Copyright (C) 2014 Smith Micro Software, Inc.
+ */
+
+#ifndef MBIM_PROXY_H
+#define MBIM_PROXY_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define MBIM_TYPE_PROXY (mbim_proxy_get_type ())
+#define MBIM_PROXY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MBIM_TYPE_PROXY, MbimProxy))
+#define MBIM_PROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MBIM_TYPE_PROXY, MbimProxyClass))
+#define MBIM_IS_PROXY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MBIM_TYPE_PROXY))
+#define MBIM_IS_PROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MBIM_TYPE_PROXY))
+#define MBIM_PROXY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MBIM_TYPE_PROXY, MbimProxyClass))
+
+typedef struct _MbimProxy MbimProxy;
+typedef struct _MbimProxyClass MbimProxyClass;
+typedef struct _MbimProxyPrivate MbimProxyPrivate;
+
+#define MBIM_PROXY_SOCKET_PATH "mbim-proxy"
+
+#define MBIM_PROXY_N_CLIENTS "mbim-proxy-n-clients"
+#define MBIM_PROXY_N_DEVICES "mbim-proxy-n-devices"
+
+struct _MbimProxy {
+ GObject parent;
+ MbimProxyPrivate *priv;
+};
+
+struct _MbimProxyClass {
+ GObjectClass parent;
+};
+
+GType mbim_proxy_get_type (void);
+
+MbimProxy *mbim_proxy_new (GError **error);
+guint mbim_proxy_get_n_clients (MbimProxy *self);
+guint mbim_proxy_get_n_devices (MbimProxy *self);
+
+#endif /* MBIM_PROXY_H */
diff --git a/src/libmbim-glib/mbim-uuid.c b/src/libmbim-glib/mbim-uuid.c
index c06c208..81f6f7a 100644
--- a/src/libmbim-glib/mbim-uuid.c
+++ b/src/libmbim-glib/mbim-uuid.c
@@ -20,6 +20,7 @@
*
* Copyright (C) 2013 - 2014 Aleksander Morgado <aleksander@aleksander.es>
* Copyright (C) 2014 NVDIA Corporation
+ * Copyright (C) 2014 Smith Micro Software, Inc.
*/
#include <config.h>
@@ -37,6 +38,8 @@
* This section defines the data type for unique identifiers.
*/
+#define MBIM_SERVICE_LAST MBIM_SERVICE_PROXY_CONTROL
+
/*****************************************************************************/
/**
@@ -218,6 +221,14 @@
.e = { 0x27, 0xd7, 0xfb, 0x80, 0x95, 0x9c }
};
+static const MbimUuid uuid_proxy_control = {
+ .a = { 0x83, 0x8c, 0xf7, 0xfb },
+ .b = { 0x8d, 0x0d },
+ .c = { 0x4d, 0x7f },
+ .d = { 0x87, 0x1e },
+ .e = { 0xd7, 0x1d , 0xbe, 0xfb, 0xb3, 0x9b }
+};
+
static GList *mbim_custom_service_list = NULL;
typedef struct {
@@ -302,7 +313,7 @@
{
GList *l;
- if (id <= MBIM_SERVICE_MS_HOST_SHUTDOWN)
+ if (id <= MBIM_SERVICE_LAST)
return FALSE;
for (l = mbim_custom_service_list; l != NULL; l = l->next) {
@@ -329,7 +340,7 @@
{
GList *l;
- if (service <= MBIM_SERVICE_MS_HOST_SHUTDOWN)
+ if (service <= MBIM_SERVICE_LAST)
return mbim_service_get_string (service);
for (l = mbim_custom_service_list; l != NULL; l = l->next) {
@@ -353,7 +364,7 @@
GList *l;
g_return_val_if_fail (service >= MBIM_SERVICE_INVALID &&
- (service <= MBIM_SERVICE_MS_HOST_SHUTDOWN ||
+ (service <= MBIM_SERVICE_PROXY_CONTROL ||
mbim_service_id_is_custom (service)),
&uuid_invalid);
@@ -378,6 +389,8 @@
return &uuid_ms_firmware_id;
case MBIM_SERVICE_MS_HOST_SHUTDOWN:
return &uuid_ms_host_shutdown;
+ case MBIM_SERVICE_PROXY_CONTROL:
+ return &uuid_proxy_control;
default:
for (l = mbim_custom_service_list; l != NULL; l = l->next) {
if (service == ((MbimCustomService *)l->data)->service_id)
@@ -427,6 +440,9 @@
if (mbim_uuid_cmp (uuid, &uuid_ms_host_shutdown))
return MBIM_SERVICE_MS_HOST_SHUTDOWN;
+ if (mbim_uuid_cmp (uuid, &uuid_proxy_control))
+ return MBIM_SERVICE_PROXY_CONTROL;
+
for (l = mbim_custom_service_list; l != NULL; l = l->next) {
if (mbim_uuid_cmp (&((MbimCustomService *)l->data)->uuid, uuid))
return ((MbimCustomService *)l->data)->service_id;
diff --git a/src/libmbim-glib/mbim-uuid.h b/src/libmbim-glib/mbim-uuid.h
index c3a6ac2..91def98 100644
--- a/src/libmbim-glib/mbim-uuid.h
+++ b/src/libmbim-glib/mbim-uuid.h
@@ -70,6 +70,7 @@
* @MBIM_SERVICE_DSS: Device Service Stream service.
* @MBIM_SERVICE_MS_FIRMWARE_ID: Microsoft Firmware ID service.
* @MBIM_SERVICE_MS_HOST_SHUTDOWN: Microsoft Host Shutdown service.
+ * @MBIM_SERVICE_PROXY_CONTROL: Proxy Control service.
*
* Enumeration of the generic MBIM services.
*/
@@ -84,6 +85,8 @@
MBIM_SERVICE_DSS = 7,
MBIM_SERVICE_MS_FIRMWARE_ID = 8,
MBIM_SERVICE_MS_HOST_SHUTDOWN = 9,
+ MBIM_SERVICE_PROXY_CONTROL = 10,
+ /* Note: update MBIM_SERVICE_LAST when a new value is added */
} MbimService;
/**
@@ -176,6 +179,15 @@
*/
#define MBIM_UUID_MS_HOST_SHUTDOWN mbim_uuid_from_service (MBIM_SERVICE_MS_HOST_SHUTDOWN)
+/**
+ * MBIM_UUID_PROXY_CONTROL:
+ *
+ * Get the UUID of the %MBIM_SERVICE_PROXY_CONTROL service.
+ *
+ * Returns: (transfer none): a #MbimUuid.
+ */
+#define MBIM_UUID_PROXY_CONTROL mbim_uuid_from_service (MBIM_SERVICE_PROXY_CONTROL)
+
const gchar *mbim_service_lookup_name (guint service);
guint mbim_register_custom_service (const MbimUuid *uuid,
diff --git a/src/libmbim-glib/test/Makefile.am b/src/libmbim-glib/test/Makefile.am
index 708dc3b..cddfb26 100644
--- a/src/libmbim-glib/test/Makefile.am
+++ b/src/libmbim-glib/test/Makefile.am
@@ -6,7 +6,8 @@
test-message \
test-fragment \
test-message-parser \
- test-message-builder
+ test-message-builder \
+ test-proxy-helpers
TEST_PROGS += $(noinst_PROGRAMS)
@@ -90,3 +91,19 @@
$(top_builddir)/src/libmbim-glib/libmbim-glib-core.la \
$(top_builddir)/src/libmbim-glib/generated/libmbim-glib-generated.la \
$(LIBMBIM_GLIB_LIBS)
+
+test_proxy_helpers_SOURCES = \
+ test-proxy-helpers.c \
+ $(top_srcdir)/src/libmbim-glib/mbim-proxy-helpers.h \
+ $(top_srcdir)/src/libmbim-glib/mbim-proxy-helpers.c
+test_proxy_helpers_CPPFLAGS = \
+ $(LIBMBIM_GLIB_CFLAGS) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/src/libmbim-glib \
+ -I$(top_builddir)/src/libmbim-glib \
+ -I$(top_builddir)/src/libmbim-glib/generated \
+ -DLIBMBIM_GLIB_COMPILATION
+test_proxy_helpers_LDADD = \
+ $(top_builddir)/src/libmbim-glib/libmbim-glib-core.la \
+ $(top_builddir)/src/libmbim-glib/generated/libmbim-glib-generated.la \
+ $(LIBMBIM_GLIB_LIBS)
diff --git a/src/libmbim-glib/test/test-proxy-helpers.c b/src/libmbim-glib/test/test-proxy-helpers.c
new file mode 100644
index 0000000..84c773e
--- /dev/null
+++ b/src/libmbim-glib/test/test-proxy-helpers.c
@@ -0,0 +1,535 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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:
+ *
+ * Copyright (C) 2014 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "mbim-cid.h"
+#include "mbim-uuid.h"
+#include "mbim-basic-connect.h"
+#include "mbim-proxy-helpers.h"
+
+/*****************************************************************************/
+
+static void
+test_parse_single_service_0_cids (void)
+{
+ MbimMessage *message;
+ MbimEventEntry **in;
+ MbimEventEntry **out;
+ GError *error = NULL;
+ gsize out_size = 0;
+
+ in = g_new0 (MbimEventEntry *, 2);
+ in[0] = g_new0 (MbimEventEntry, 1);
+ memcpy (&in[0]->device_service_id, MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
+ in[0]->cids_count = 0;
+ in[0]->cids = NULL;
+
+ message = mbim_message_device_service_subscribe_list_set_new (1, (const MbimEventEntry *const *)in, &error);
+ g_assert_no_error (error);
+ g_assert (message != NULL);
+
+ out = _mbim_proxy_helper_service_subscribe_request_parse (message, &out_size);
+ g_assert (out != NULL);
+ g_assert (_mbim_proxy_helper_service_subscribe_list_cmp ((const MbimEventEntry * const *)in, 1,
+ (const MbimEventEntry * const *)out, out_size));
+ g_assert_cmpuint (out_size, ==, 1);
+
+ mbim_message_unref (message);
+ mbim_event_entry_array_free (in);
+ mbim_event_entry_array_free (out);
+}
+
+static void
+test_parse_single_service_1_cids (void)
+{
+ MbimMessage *message;
+ MbimEventEntry **in;
+ MbimEventEntry **out;
+ GError *error = NULL;
+ gsize out_size = 0;
+
+ in = g_new0 (MbimEventEntry *, 2);
+ in[0] = g_new0 (MbimEventEntry, 1);
+ memcpy (&in[0]->device_service_id, MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
+ in[0]->cids_count = 1;
+ in[0]->cids = g_new0 (guint32, in[0]->cids_count);
+ in[0]->cids[0] = MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS;
+
+ message = mbim_message_device_service_subscribe_list_set_new (1, (const MbimEventEntry *const *)in, &error);
+ g_assert_no_error (error);
+ g_assert (message != NULL);
+
+ out = _mbim_proxy_helper_service_subscribe_request_parse (message, &out_size);
+ g_assert (out != NULL);
+ g_assert (_mbim_proxy_helper_service_subscribe_list_cmp ((const MbimEventEntry * const *)in, 1,
+ (const MbimEventEntry * const *)out, out_size));
+ g_assert_cmpuint (out_size, ==, 1);
+
+ mbim_message_unref (message);
+ mbim_event_entry_array_free (in);
+ mbim_event_entry_array_free (out);
+}
+
+static void
+test_parse_single_service_5_cids (void)
+{
+ MbimMessage *message;
+ MbimEventEntry **in;
+ MbimEventEntry **out;
+ GError *error = NULL;
+ gsize out_size = 0;
+
+ in = g_new0 (MbimEventEntry *, 2);
+ in[0] = g_new0 (MbimEventEntry, 1);
+ memcpy (&in[0]->device_service_id, MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
+ in[0]->cids_count = 5;
+ in[0]->cids = g_new0 (guint32, in[0]->cids_count);
+ in[0]->cids[0] = MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS;
+ in[0]->cids[1] = MBIM_CID_BASIC_CONNECT_RADIO_STATE;
+ in[0]->cids[2] = MBIM_CID_BASIC_CONNECT_SIGNAL_STATE;
+ in[0]->cids[3] = MBIM_CID_BASIC_CONNECT_IP_CONFIGURATION;
+ in[0]->cids[4] = MBIM_CID_BASIC_CONNECT_NETWORK_IDLE_HINT;
+
+ message = mbim_message_device_service_subscribe_list_set_new (1, (const MbimEventEntry *const *)in, &error);
+ g_assert_no_error (error);
+ g_assert (message != NULL);
+
+ out = _mbim_proxy_helper_service_subscribe_request_parse (message, &out_size);
+ g_assert (out != NULL);
+ g_assert (_mbim_proxy_helper_service_subscribe_list_cmp ((const MbimEventEntry * const *)in, 1,
+ (const MbimEventEntry * const *)out, out_size));
+ g_assert_cmpuint (out_size, ==, 1);
+
+ mbim_message_unref (message);
+ mbim_event_entry_array_free (in);
+ mbim_event_entry_array_free (out);
+}
+
+/*****************************************************************************/
+
+static MbimEventEntry *
+find_service_in_list (MbimEventEntry **list,
+ gsize list_size,
+ MbimService service)
+{
+ gsize i;
+
+ for (i = 0; i < list_size; i++) {
+ if (mbim_uuid_cmp (&(list[i]->device_service_id), mbim_uuid_from_service (service)))
+ return list[i];
+ }
+
+ return NULL;
+}
+
+static void
+check_standard_list (MbimEventEntry **list,
+ gsize list_size)
+{
+ MbimEventEntry *tmp;
+ MbimService s;
+ gsize i;
+
+ for (i = 0; list[i]; i++);
+ g_assert_cmpuint (i, ==, list_size);
+ g_assert_cmpuint (i, ==, (MBIM_SERVICE_DSS - MBIM_SERVICE_BASIC_CONNECT + 1));
+
+ for (s = MBIM_SERVICE_BASIC_CONNECT; s <= MBIM_SERVICE_DSS; s++) {
+ tmp = find_service_in_list (list, list_size, s);
+ g_assert (tmp != NULL);
+ g_assert_cmpuint (tmp->cids_count, ==, 0);
+ g_assert (tmp->cids == NULL);
+ }
+}
+
+static void
+test_standard_list (void)
+{
+ MbimEventEntry **out;
+ gsize out_size = 0;
+
+ out = _mbim_proxy_helper_service_subscribe_standard_list_new (&out_size);
+ check_standard_list (out, out_size);
+ mbim_event_entry_array_free (out);
+}
+
+/*****************************************************************************/
+
+static void
+test_merge_standard_list_full_none (void)
+{
+ MbimEventEntry **list;
+ gsize list_size = 0;
+ gsize out_size = 0;
+
+ /* list with all standard services */
+ list = _mbim_proxy_helper_service_subscribe_standard_list_new (&list_size);
+
+ /* merge */
+ list = _mbim_proxy_helper_service_subscribe_list_merge (list, list_size, NULL, 0, &out_size);
+
+ check_standard_list (list, out_size);
+
+ mbim_event_entry_array_free (list);
+}
+
+static void
+test_merge_standard_list_full_subset (void)
+{
+ MbimEventEntry **list;
+ gsize list_size = 0;
+ MbimEventEntry **addition;
+ gsize addition_size;
+ gsize out_size = 0;
+
+ /* list with all standard services */
+ list = _mbim_proxy_helper_service_subscribe_standard_list_new (&list_size);
+
+ /* setup a new list with a subset of standard services */
+ addition_size = 2;
+ addition = g_new0 (MbimEventEntry *, addition_size + 1);
+ addition[0] = g_new0 (MbimEventEntry, 1);
+ memcpy (&addition[0]->device_service_id, MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
+ addition[0]->cids_count = 5;
+ addition[0]->cids = g_new0 (guint32, addition[0]->cids_count);
+ addition[0]->cids[0] = MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS;
+ addition[0]->cids[1] = MBIM_CID_BASIC_CONNECT_RADIO_STATE;
+ addition[0]->cids[2] = MBIM_CID_BASIC_CONNECT_SIGNAL_STATE;
+ addition[0]->cids[3] = MBIM_CID_BASIC_CONNECT_IP_CONFIGURATION;
+ addition[0]->cids[4] = MBIM_CID_BASIC_CONNECT_NETWORK_IDLE_HINT;
+ addition[1] = g_new0 (MbimEventEntry, 1);
+ memcpy (&addition[1]->device_service_id, MBIM_UUID_SMS, sizeof (MbimUuid));
+ addition[1]->cids_count = 2;
+ addition[1]->cids = g_new0 (guint32, addition[1]->cids_count);
+ addition[1]->cids[0] = MBIM_CID_SMS_READ;
+ addition[1]->cids[1] = MBIM_CID_SMS_SEND;
+
+ /* merge */
+ list = _mbim_proxy_helper_service_subscribe_list_merge (list, list_size, addition, addition_size, &out_size);
+
+ /* Now, as we added a subset of the elements of the standard list to the
+ * full standard list, we should still get as output the full standard list
+ */
+ check_standard_list (list, out_size);
+
+ mbim_event_entry_array_free (list);
+ mbim_event_entry_array_free (addition);
+}
+
+static void
+test_merge_standard_list_full_full (void)
+{
+ MbimEventEntry **list;
+ gsize list_size = 0;
+ MbimEventEntry **addition;
+ gsize addition_size = 0;
+ gsize out_size = 0;
+
+ /* list with all standard services */
+ list = _mbim_proxy_helper_service_subscribe_standard_list_new (&list_size);
+ /* again, list with all standard services */
+ addition = _mbim_proxy_helper_service_subscribe_standard_list_new (&addition_size);
+
+ /* merge */
+ list = _mbim_proxy_helper_service_subscribe_list_merge (list, list_size, addition, addition_size, &out_size);
+
+ /* Now, as we added a subset of the elements of the standard list to the
+ * full standard list, we should still get as output the full standard list
+ */
+ check_standard_list (list, out_size);
+
+ mbim_event_entry_array_free (list);
+ mbim_event_entry_array_free (addition);
+}
+
+static void
+test_merge_standard_list_subset_full (void)
+{
+ MbimEventEntry **list;
+ gsize list_size;
+ MbimEventEntry **addition;
+ gsize addition_size = 0;
+ gsize out_size = 0;
+
+ /* setup a new list with a subset of standard services */
+ list_size = 2;
+ list = g_new0 (MbimEventEntry *, list_size + 1);
+ list[0] = g_new0 (MbimEventEntry, 1);
+ memcpy (&list[0]->device_service_id, MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
+ list[0]->cids_count = 5;
+ list[0]->cids = g_new0 (guint32, list[0]->cids_count);
+ list[0]->cids[0] = MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS;
+ list[0]->cids[1] = MBIM_CID_BASIC_CONNECT_RADIO_STATE;
+ list[0]->cids[2] = MBIM_CID_BASIC_CONNECT_SIGNAL_STATE;
+ list[0]->cids[3] = MBIM_CID_BASIC_CONNECT_IP_CONFIGURATION;
+ list[0]->cids[4] = MBIM_CID_BASIC_CONNECT_NETWORK_IDLE_HINT;
+ list[1] = g_new0 (MbimEventEntry, 1);
+ memcpy (&list[1]->device_service_id, MBIM_UUID_SMS, sizeof (MbimUuid));
+ list[1]->cids_count = 2;
+ list[1]->cids = g_new0 (guint32, list[1]->cids_count);
+ list[1]->cids[0] = MBIM_CID_SMS_READ;
+ list[1]->cids[1] = MBIM_CID_SMS_SEND;
+
+ /* list with all standard services */
+ addition = _mbim_proxy_helper_service_subscribe_standard_list_new (&addition_size);
+
+ /* merge */
+ list = _mbim_proxy_helper_service_subscribe_list_merge (list, list_size, addition, addition_size, &out_size);
+
+ /* Now, as we added the full standard list to a subset, we should still get
+ * as output the full standard list */
+ check_standard_list (list, out_size);
+
+ mbim_event_entry_array_free (list);
+ mbim_event_entry_array_free (addition);
+}
+
+static void
+test_merge_standard_list_none_full (void)
+{
+ MbimEventEntry **list, **merged_list;
+ gsize addition_size = 0;
+ gsize out_size = 0;
+
+ /* list with all standard services */
+ list = _mbim_proxy_helper_service_subscribe_standard_list_new (&addition_size);
+
+ /* merge */
+ merged_list = _mbim_proxy_helper_service_subscribe_list_merge (NULL, 0, list, addition_size, &out_size);
+
+ check_standard_list (merged_list, out_size);
+
+ mbim_event_entry_array_free (list);
+ mbim_event_entry_array_free (merged_list);
+}
+
+static void
+test_merge_list_same_service (void)
+{
+ MbimEventEntry **list;
+ gsize list_size;
+ MbimEventEntry **addition;
+ gsize addition_size;
+ MbimEventEntry **expected;
+ gsize expected_size;
+ gsize out_size = 0;
+
+ /* setup a new list with a subset of standard services */
+ list_size = 1;
+ list = g_new0 (MbimEventEntry *, list_size + 1);
+ list[0] = g_new0 (MbimEventEntry, 1);
+ memcpy (&list[0]->device_service_id, MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
+ list[0]->cids_count = 2;
+ list[0]->cids = g_new0 (guint32, list[0]->cids_count);
+ list[0]->cids[0] = MBIM_CID_BASIC_CONNECT_IP_CONFIGURATION;
+ list[0]->cids[1] = MBIM_CID_BASIC_CONNECT_NETWORK_IDLE_HINT;
+
+ /* setup a new list with a subset of standard services */
+ addition_size = 1;
+ addition = g_new0 (MbimEventEntry *, addition_size + 1);
+ addition[0] = g_new0 (MbimEventEntry, 1);
+ memcpy (&addition[0]->device_service_id, MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
+ addition[0]->cids_count = 3;
+ addition[0]->cids = g_new0 (guint32, addition[0]->cids_count);
+ addition[0]->cids[0] = MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS;
+ addition[0]->cids[1] = MBIM_CID_BASIC_CONNECT_RADIO_STATE;
+ addition[0]->cids[2] = MBIM_CID_BASIC_CONNECT_SIGNAL_STATE;
+
+ /* merge */
+ list = _mbim_proxy_helper_service_subscribe_list_merge (list, list_size, addition, addition_size, &out_size);
+
+ /* setup the expected list */
+ expected_size = 1;
+ expected = g_new0 (MbimEventEntry *, expected_size + 1);
+ expected[0] = g_new0 (MbimEventEntry, 1);
+ memcpy (&expected[0]->device_service_id, MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
+ expected[0]->cids_count = 5;
+ expected[0]->cids = g_new0 (guint32, expected[0]->cids_count);
+ expected[0]->cids[0] = MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS;
+ expected[0]->cids[1] = MBIM_CID_BASIC_CONNECT_RADIO_STATE;
+ expected[0]->cids[2] = MBIM_CID_BASIC_CONNECT_SIGNAL_STATE;
+ expected[0]->cids[3] = MBIM_CID_BASIC_CONNECT_IP_CONFIGURATION;
+ expected[0]->cids[4] = MBIM_CID_BASIC_CONNECT_NETWORK_IDLE_HINT;
+
+ /* Compare */
+ g_assert (_mbim_proxy_helper_service_subscribe_list_cmp ((const MbimEventEntry * const *)list, out_size,
+ (const MbimEventEntry * const *)expected, expected_size));
+
+ mbim_event_entry_array_free (list);
+ mbim_event_entry_array_free (addition);
+ mbim_event_entry_array_free (expected);
+}
+
+static void
+test_merge_list_different_services (void)
+{
+ MbimEventEntry **list;
+ gsize list_size;
+ MbimEventEntry **addition;
+ gsize addition_size;
+ MbimEventEntry **expected;
+ gsize expected_size;
+ gsize out_size = 0;
+
+ /* setup a new list with a subset of standard services */
+ list_size = 1;
+ list = g_new0 (MbimEventEntry *, list_size + 1);
+ list[0] = g_new0 (MbimEventEntry, 1);
+ memcpy (&list[0]->device_service_id, MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
+ list[0]->cids_count = 5;
+ list[0]->cids = g_new0 (guint32, list[0]->cids_count);
+ list[0]->cids[0] = MBIM_CID_BASIC_CONNECT_IP_CONFIGURATION;
+ list[0]->cids[1] = MBIM_CID_BASIC_CONNECT_NETWORK_IDLE_HINT;
+ list[0]->cids[2] = MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS;
+ list[0]->cids[3] = MBIM_CID_BASIC_CONNECT_RADIO_STATE;
+ list[0]->cids[4] = MBIM_CID_BASIC_CONNECT_SIGNAL_STATE;
+
+ /* setup a new list with a subset of standard services */
+ addition_size = 1;
+ addition = g_new0 (MbimEventEntry *, addition_size + 1);
+ addition[0] = g_new0 (MbimEventEntry, 1);
+ memcpy (&addition[0]->device_service_id, MBIM_UUID_SMS, sizeof (MbimUuid));
+ addition[0]->cids_count = 2;
+ addition[0]->cids = g_new0 (guint32, addition[0]->cids_count);
+ addition[0]->cids[0] = MBIM_CID_SMS_READ;
+ addition[0]->cids[1] = MBIM_CID_SMS_SEND;
+
+ /* merge */
+ list = _mbim_proxy_helper_service_subscribe_list_merge (list, list_size, addition, addition_size, &out_size);
+
+ /* setup the expected list */
+ expected_size = 2;
+ expected = g_new0 (MbimEventEntry *, expected_size + 1);
+ expected[0] = g_new0 (MbimEventEntry, 1);
+ memcpy (&expected[0]->device_service_id, MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
+ expected[0]->cids_count = 5;
+ expected[0]->cids = g_new0 (guint32, expected[0]->cids_count);
+ expected[0]->cids[0] = MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS;
+ expected[0]->cids[1] = MBIM_CID_BASIC_CONNECT_RADIO_STATE;
+ expected[0]->cids[2] = MBIM_CID_BASIC_CONNECT_SIGNAL_STATE;
+ expected[0]->cids[3] = MBIM_CID_BASIC_CONNECT_IP_CONFIGURATION;
+ expected[0]->cids[4] = MBIM_CID_BASIC_CONNECT_NETWORK_IDLE_HINT;
+ expected[1] = g_new0 (MbimEventEntry, 1);
+ memcpy (&expected[1]->device_service_id, MBIM_UUID_SMS, sizeof (MbimUuid));
+ expected[1]->cids_count = 2;
+ expected[1]->cids = g_new0 (guint32, expected[1]->cids_count);
+ expected[1]->cids[0] = MBIM_CID_SMS_READ;
+ expected[1]->cids[1] = MBIM_CID_SMS_SEND;
+
+ /* Compare */
+ g_assert (_mbim_proxy_helper_service_subscribe_list_cmp ((const MbimEventEntry * const *)list, out_size,
+ (const MbimEventEntry * const *)expected, expected_size));
+
+ mbim_event_entry_array_free (list);
+ mbim_event_entry_array_free (addition);
+ mbim_event_entry_array_free (expected);
+}
+
+static void
+test_merge_list_merged_services (void)
+{
+ MbimEventEntry **list;
+ gsize list_size;
+ MbimEventEntry **addition;
+ gsize addition_size;
+ MbimEventEntry **expected;
+ gsize expected_size;
+ gsize out_size = 0;
+
+ /* setup a new list with a subset of standard services */
+ list_size = 2;
+ list = g_new0 (MbimEventEntry *, list_size + 1);
+ list[0] = g_new0 (MbimEventEntry, 1);
+ memcpy (&list[0]->device_service_id, MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
+ list[0]->cids_count = 3;
+ list[0]->cids = g_new0 (guint32, list[0]->cids_count);
+ list[0]->cids[0] = MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS;
+ list[0]->cids[1] = MBIM_CID_BASIC_CONNECT_RADIO_STATE;
+ list[0]->cids[2] = MBIM_CID_BASIC_CONNECT_SIGNAL_STATE;
+ list[1] = g_new0 (MbimEventEntry, 1);
+ memcpy (&list[1]->device_service_id, MBIM_UUID_SMS, sizeof (MbimUuid));
+ list[1]->cids_count = 1;
+ list[1]->cids = g_new0 (guint32, list[1]->cids_count);
+ list[1]->cids[0] = MBIM_CID_SMS_READ;
+
+ /* setup a new list with a subset of standard services */
+ addition_size = 2;
+ addition = g_new0 (MbimEventEntry *, addition_size + 1);
+ addition[0] = g_new0 (MbimEventEntry, 1);
+ memcpy (&addition[0]->device_service_id, MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
+ addition[0]->cids_count = 2;
+ addition[0]->cids = g_new0 (guint32, addition[0]->cids_count);
+ addition[0]->cids[0] = MBIM_CID_BASIC_CONNECT_IP_CONFIGURATION;
+ addition[0]->cids[1] = MBIM_CID_BASIC_CONNECT_NETWORK_IDLE_HINT;
+ addition[1] = g_new0 (MbimEventEntry, 1);
+ memcpy (&addition[1]->device_service_id, MBIM_UUID_SMS, sizeof (MbimUuid));
+ addition[1]->cids_count = 1;
+ addition[1]->cids = g_new0 (guint32, addition[1]->cids_count);
+ addition[1]->cids[0] = MBIM_CID_SMS_SEND;
+
+ /* merge */
+ list = _mbim_proxy_helper_service_subscribe_list_merge (list, list_size, addition, addition_size, &out_size);
+
+ /* setup the expected list */
+ expected_size = 2;
+ expected = g_new0 (MbimEventEntry *, expected_size + 1);
+ expected[0] = g_new0 (MbimEventEntry, 1);
+ memcpy (&expected[0]->device_service_id, MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
+ expected[0]->cids_count = 5;
+ expected[0]->cids = g_new0 (guint32, expected[0]->cids_count);
+ expected[0]->cids[0] = MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS;
+ expected[0]->cids[1] = MBIM_CID_BASIC_CONNECT_RADIO_STATE;
+ expected[0]->cids[2] = MBIM_CID_BASIC_CONNECT_SIGNAL_STATE;
+ expected[0]->cids[3] = MBIM_CID_BASIC_CONNECT_IP_CONFIGURATION;
+ expected[0]->cids[4] = MBIM_CID_BASIC_CONNECT_NETWORK_IDLE_HINT;
+ expected[1] = g_new0 (MbimEventEntry, 1);
+ memcpy (&expected[1]->device_service_id, MBIM_UUID_SMS, sizeof (MbimUuid));
+ expected[1]->cids_count = 2;
+ expected[1]->cids = g_new0 (guint32, expected[1]->cids_count);
+ expected[1]->cids[0] = MBIM_CID_SMS_READ;
+ expected[1]->cids[1] = MBIM_CID_SMS_SEND;
+
+ /* Compare */
+ g_assert (_mbim_proxy_helper_service_subscribe_list_cmp ((const MbimEventEntry * const *)list, out_size,
+ (const MbimEventEntry * const *)expected, expected_size));
+
+ mbim_event_entry_array_free (list);
+ mbim_event_entry_array_free (addition);
+ mbim_event_entry_array_free (expected);
+}
+
+/*****************************************************************************/
+
+int main (int argc, char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/libmbim-glib/proxy/standard-list", test_standard_list);
+ g_test_add_func ("/libmbim-glib/proxy/parse/single-service/0", test_parse_single_service_0_cids);
+ g_test_add_func ("/libmbim-glib/proxy/parse/single-service/1", test_parse_single_service_1_cids);
+ g_test_add_func ("/libmbim-glib/proxy/parse/single-service/5", test_parse_single_service_5_cids);
+ g_test_add_func ("/libmbim-glib/proxy/merge/standard/full_none", test_merge_standard_list_full_none);
+ g_test_add_func ("/libmbim-glib/proxy/merge/standard/full_subset", test_merge_standard_list_full_subset);
+ g_test_add_func ("/libmbim-glib/proxy/merge/standard/full_full", test_merge_standard_list_full_full);
+ g_test_add_func ("/libmbim-glib/proxy/merge/standard/subset_full", test_merge_standard_list_subset_full);
+ g_test_add_func ("/libmbim-glib/proxy/merge/standard/none_full", test_merge_standard_list_none_full);
+ g_test_add_func ("/libmbim-glib/proxy/merge/same-service", test_merge_list_same_service);
+ g_test_add_func ("/libmbim-glib/proxy/merge/different-services", test_merge_list_different_services);
+ g_test_add_func ("/libmbim-glib/proxy/merge/merged-services", test_merge_list_merged_services);
+
+ return g_test_run ();
+}
diff --git a/src/mbim-proxy/Makefile.am b/src/mbim-proxy/Makefile.am
new file mode 100644
index 0000000..83a8ce4
--- /dev/null
+++ b/src/mbim-proxy/Makefile.am
@@ -0,0 +1,16 @@
+
+libexec_PROGRAMS = mbim-proxy
+
+mbim_proxy_CPPFLAGS = \
+ $(MBIMPROXY_CFLAGS) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/src/libmbim-glib \
+ -I$(top_srcdir)/src/libmbim-glib/generated \
+ -I$(top_builddir)/src/libmbim-glib \
+ -I$(top_builddir)/src/libmbim-glib/generated
+
+mbim_proxy_SOURCES = mbim-proxy.c
+
+mbim_proxy_LDADD = \
+ $(MBIMPROXY_LIBS) \
+ $(top_builddir)/src/libmbim-glib/libmbim-glib.la
diff --git a/src/mbim-proxy/mbim-proxy.c b/src/mbim-proxy/mbim-proxy.c
new file mode 100644
index 0000000..f29303f
--- /dev/null
+++ b/src/mbim-proxy/mbim-proxy.c
@@ -0,0 +1,252 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * mbim-proxy -- A proxy to communicate with MBIM ports
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2014 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2014 Smith Micro Software, Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <gio/gio.h>
+#include <glib-unix.h>
+
+#include <libmbim-glib.h>
+
+#define PROGRAM_NAME "mbim-proxy"
+#define PROGRAM_VERSION PACKAGE_VERSION
+
+#define EMPTY_PROXY_LIFETIME_SECS 30
+
+/* Globals */
+static GMainLoop *loop;
+static MbimProxy *proxy;
+static guint timeout_id;
+static guint client_connected_once = FALSE;
+
+/* Main options */
+static gboolean verbose_flag;
+static gboolean version_flag;
+
+static GOptionEntry main_entries[] = {
+ { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag,
+ "Run action with verbose logs, including the debug ones",
+ NULL
+ },
+ { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag,
+ "Print version",
+ NULL
+ },
+ { NULL }
+};
+
+static gboolean
+quit_cb (gpointer user_data)
+{
+ if (loop) {
+ g_warning ("Caught signal, stopping the loop...");
+ g_idle_add ((GSourceFunc) g_main_loop_quit, loop);
+ }
+
+ return FALSE;
+}
+
+static void
+log_handler (const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer user_data)
+{
+ const gchar *log_level_str;
+ time_t now;
+ gchar time_str[64];
+ struct tm *local_time;
+ gboolean err;
+
+ now = time ((time_t *) NULL);
+ local_time = localtime (&now);
+ strftime (time_str, 64, "%d %b %Y, %H:%M:%S", local_time);
+ err = FALSE;
+
+ switch (log_level) {
+ case G_LOG_LEVEL_WARNING:
+ log_level_str = "-Warning **";
+ err = TRUE;
+ break;
+
+ case G_LOG_LEVEL_CRITICAL:
+ case G_LOG_FLAG_FATAL:
+ case G_LOG_LEVEL_ERROR:
+ log_level_str = "-Error **";
+ err = TRUE;
+ break;
+
+ case G_LOG_LEVEL_DEBUG:
+ log_level_str = "[Debug]";
+ break;
+
+ default:
+ log_level_str = "";
+ break;
+ }
+
+ if (!verbose_flag && !err)
+ return;
+
+ g_fprintf (err ? stderr : stdout,
+ "[%s] %s %s\n",
+ time_str,
+ log_level_str,
+ message);
+}
+
+static void
+print_version_and_exit (void)
+{
+ g_print ("\n"
+ PROGRAM_NAME " " PROGRAM_VERSION "\n"
+ "Copyright (C) 2013 Aleksander Morgado\n"
+ "Copyright (C) 2014 Greg Suarez\n"
+ "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl-2.0.html>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n"
+ "\n");
+ exit (EXIT_SUCCESS);
+}
+
+/*****************************************************************************/
+
+static gboolean
+stop_loop_cb (void)
+{
+ timeout_id = 0;
+ if (loop)
+ g_main_loop_quit (loop);
+ return FALSE;
+}
+
+static void
+proxy_n_clients_changed (MbimProxy *_proxy)
+{
+ /* once a client has connected only exit if there are no devices */
+ if (client_connected_once)
+ return;
+
+ if (mbim_proxy_get_n_clients (proxy) == 0) {
+ g_assert (timeout_id == 0);
+ timeout_id = g_timeout_add_seconds (EMPTY_PROXY_LIFETIME_SECS,
+ (GSourceFunc)stop_loop_cb,
+ NULL);
+ return;
+ }
+
+ /* At least one client, remove timeout if any */
+ if (timeout_id) {
+ g_source_remove (timeout_id);
+ timeout_id = 0;
+ }
+
+ client_connected_once = TRUE;
+}
+
+static void
+proxy_n_devices_changed (MbimProxy *_proxy)
+{
+ if (mbim_proxy_get_n_devices (proxy) == 0) {
+ g_assert (timeout_id == 0);
+ timeout_id = g_timeout_add_seconds (EMPTY_PROXY_LIFETIME_SECS,
+ (GSourceFunc)stop_loop_cb,
+ NULL);
+ return;
+ }
+
+ /* At least one device, remove timeout if any */
+ if (timeout_id) {
+ g_source_remove (timeout_id);
+ timeout_id = 0;
+ }
+}
+
+/*****************************************************************************/
+
+int main (int argc, char **argv)
+{
+ GError *error = NULL;
+ GOptionContext *context;
+
+ setlocale (LC_ALL, "");
+
+ g_type_init ();
+
+ /* Setup option context, process it and destroy it */
+ context = g_option_context_new ("- Proxy for MBIM devices");
+ g_option_context_add_main_entries (context, main_entries, NULL);
+ if (!g_option_context_parse (context, &argc, &argv, &error)) {
+ g_printerr ("error: %s\n",
+ error->message);
+ exit (EXIT_FAILURE);
+ }
+ g_option_context_free (context);
+
+ if (version_flag)
+ print_version_and_exit ();
+
+ g_log_set_handler (NULL, G_LOG_LEVEL_MASK, log_handler, NULL);
+ g_log_set_handler ("Mbim", G_LOG_LEVEL_MASK, log_handler, NULL);
+ if (verbose_flag)
+ mbim_utils_set_traces_enabled (TRUE);
+
+ /* Setup signals */
+ g_unix_signal_add (SIGINT, quit_cb, NULL);
+ g_unix_signal_add (SIGHUP, quit_cb, NULL);
+ g_unix_signal_add (SIGTERM, quit_cb, NULL);
+
+ /* Setup proxy */
+ proxy = mbim_proxy_new (&error);
+ if (!proxy) {
+ g_printerr ("error: %s\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ proxy_n_clients_changed (proxy);
+ g_signal_connect (proxy,
+ "notify::" MBIM_PROXY_N_CLIENTS,
+ G_CALLBACK (proxy_n_clients_changed),
+ NULL);
+ g_signal_connect (proxy,
+ "notify::" MBIM_PROXY_N_DEVICES,
+ G_CALLBACK (proxy_n_devices_changed),
+ NULL);
+
+ /* Loop */
+ loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+
+ /* Cleanup; releases socket and such */
+ g_object_unref (proxy);
+
+ g_debug ("exiting 'mbim-proxy'...");
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/mbimcli/mbimcli.c b/src/mbimcli/mbimcli.c
index b299a37..be5c853 100644
--- a/src/mbimcli/mbimcli.c
+++ b/src/mbimcli/mbimcli.c
@@ -47,6 +47,7 @@
/* Main options */
static gchar *device_str;
+static gboolean device_open_proxy_flag;
static gchar *no_open_str;
static gboolean no_close_flag;
static gboolean noop_flag;
@@ -59,6 +60,10 @@
"Specify device path",
"[PATH]"
},
+ { "device-open-proxy", 'p', 0, G_OPTION_ARG_NONE, &device_open_proxy_flag,
+ "Request to use the 'mbim-proxy' proxy",
+ NULL
+ },
{ "no-open", 0, 0, G_OPTION_ARG_STRING, &no_open_str,
"Do not explicitly open the MBIM device before running the command",
"[Transaction ID]"
@@ -277,6 +282,7 @@
GAsyncResult *res)
{
GError *error = NULL;
+ MbimDeviceOpenFlags open_flags = MBIM_DEVICE_OPEN_FLAGS_NONE;
device = mbim_device_new_finish (res, &error);
if (!device) {
@@ -301,12 +307,17 @@
NULL);
}
+ /* Setup device open flags */
+ if (device_open_proxy_flag)
+ open_flags |= MBIM_DEVICE_OPEN_FLAGS_PROXY;
+
/* Open the device */
- mbim_device_open (device,
- 30,
- cancellable,
- (GAsyncReadyCallback) device_open_ready,
- NULL);
+ mbim_device_open_full (device,
+ open_flags,
+ 30,
+ cancellable,
+ (GAsyncReadyCallback) device_open_ready,
+ NULL);
}
/*****************************************************************************/