0001-novatel-move-custom-init-method-to-separate-source-f.patch0000664000175100017510000003361213117476072022076 0ustar uuFrom eebc5ac983e15036e39946bbc02140f9e7718ab5 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Fri, 6 Feb 2015 15:30:50 +0100 Subject: [PATCH 1/9] novatel: move custom init method to separate source files --- plugins/Makefile.am | 2 + plugins/novatel/mm-common-novatel.c | 158 ++++++++++++++++++++++++++++++++++++ plugins/novatel/mm-common-novatel.h | 31 +++++++ plugins/novatel/mm-plugin-novatel.c | 146 +-------------------------------- 4 files changed, 194 insertions(+), 143 deletions(-) create mode 100644 plugins/novatel/mm-common-novatel.c create mode 100644 plugins/novatel/mm-common-novatel.h diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 60d0536..7731804 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -459,6 +459,8 @@ libmm_plugin_novatel_lte_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) libmm_plugin_novatel_la_SOURCES = \ novatel/mm-plugin-novatel.c \ novatel/mm-plugin-novatel.h \ + novatel/mm-common-novatel.c \ + novatel/mm-common-novatel.h \ novatel/mm-broadband-modem-novatel.c \ novatel/mm-broadband-modem-novatel.h libmm_plugin_novatel_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) diff --git a/plugins/novatel/mm-common-novatel.c b/plugins/novatel/mm-common-novatel.c new file mode 100644 index 0000000..5305d12 --- /dev/null +++ b/plugins/novatel/mm-common-novatel.c @@ -0,0 +1,158 @@ +/* -*- 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) 2015 Aleksander Morgado + */ + +#include "mm-common-novatel.h" +#include "mm-log.h" + +/*****************************************************************************/ +/* Custom init */ + +typedef struct { + MMPortProbe *probe; + MMPortSerialAt *port; + GCancellable *cancellable; + GSimpleAsyncResult *result; + guint nwdmat_retries; + guint wait_time; +} CustomInitContext; + +static void +custom_init_context_complete_and_free (CustomInitContext *ctx) +{ + g_simple_async_result_complete_in_idle (ctx->result); + + if (ctx->cancellable) + g_object_unref (ctx->cancellable); + g_object_unref (ctx->port); + g_object_unref (ctx->probe); + g_object_unref (ctx->result); + g_slice_free (CustomInitContext, ctx); +} + +gboolean +mm_common_novatel_custom_init_finish (MMPortProbe *probe, + GAsyncResult *result, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); +} + +static void custom_init_step (CustomInitContext *ctx); + +static void +nwdmat_ready (MMPortSerialAt *port, + GAsyncResult *res, + CustomInitContext *ctx) +{ + const gchar *response; + GError *error = NULL; + + response = mm_port_serial_at_command_finish (port, res, &error); + if (error) { + if (g_error_matches (error, + MM_SERIAL_ERROR, + MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { + custom_init_step (ctx); + goto out; + } + + mm_dbg ("(Novatel) Error flipping secondary ports to AT mode: %s", error->message); + } + + /* Finish custom_init */ + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + custom_init_context_complete_and_free (ctx); + +out: + if (error) + g_error_free (error); +} + +static gboolean +custom_init_wait_cb (CustomInitContext *ctx) +{ + custom_init_step (ctx); + return FALSE; +} + +static void +custom_init_step (CustomInitContext *ctx) +{ + /* If cancelled, end */ + if (g_cancellable_is_cancelled (ctx->cancellable)) { + mm_dbg ("(Novatel) no need to keep on running custom init in (%s)", + mm_port_get_device (MM_PORT (ctx->port))); + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + custom_init_context_complete_and_free (ctx); + return; + } + + /* If device has a QMI port, don't run $NWDMAT */ + if (mm_port_probe_list_has_qmi_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (ctx->probe)))) { + mm_dbg ("(Novatel) no need to run custom init in (%s): device has QMI port", + mm_port_get_device (MM_PORT (ctx->port))); + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + custom_init_context_complete_and_free (ctx); + return; + } + + if (ctx->wait_time > 0) { + ctx->wait_time--; + g_timeout_add_seconds (1, (GSourceFunc)custom_init_wait_cb, ctx); + return; + } + + if (ctx->nwdmat_retries > 0) { + ctx->nwdmat_retries--; + mm_port_serial_at_command (ctx->port, + "$NWDMAT=1", + 3, + FALSE, /* raw */ + FALSE, /* allow_cached */ + ctx->cancellable, + (GAsyncReadyCallback)nwdmat_ready, + ctx); + return; + } + + /* Finish custom_init */ + mm_dbg ("(Novatel) couldn't flip secondary port to AT in (%s): all retries consumed", + mm_port_get_device (MM_PORT (ctx->port))); + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + custom_init_context_complete_and_free (ctx); +} + +void +mm_common_novatel_custom_init (MMPortProbe *probe, + MMPortSerialAt *port, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + CustomInitContext *ctx; + + ctx = g_slice_new (CustomInitContext); + ctx->result = g_simple_async_result_new (G_OBJECT (probe), + callback, + user_data, + mm_common_novatel_custom_init); + ctx->probe = g_object_ref (probe); + ctx->port = g_object_ref (port); + ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + ctx->nwdmat_retries = 3; + ctx->wait_time = 2; + + custom_init_step (ctx); +} diff --git a/plugins/novatel/mm-common-novatel.h b/plugins/novatel/mm-common-novatel.h new file mode 100644 index 0000000..70572fd --- /dev/null +++ b/plugins/novatel/mm-common-novatel.h @@ -0,0 +1,31 @@ +/* -*- 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) 2015 Aleksander Morgado + */ + +#ifndef MM_COMMON_NOVATEL_H +#define MM_COMMON_NOVATEL_H + +#include "glib.h" +#include "mm-plugin.h" + +void mm_common_novatel_custom_init (MMPortProbe *probe, + MMPortSerialAt *port, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_common_novatel_custom_init_finish (MMPortProbe *probe, + GAsyncResult *result, + GError **error); + +#endif /* MM_COMMON_NOVATEL_H */ diff --git a/plugins/novatel/mm-plugin-novatel.c b/plugins/novatel/mm-plugin-novatel.c index b57c0e8..398a289 100644 --- a/plugins/novatel/mm-plugin-novatel.c +++ b/plugins/novatel/mm-plugin-novatel.c @@ -28,6 +28,7 @@ #include #include "mm-plugin-novatel.h" +#include "mm-common-novatel.h" #include "mm-private-boxed-types.h" #include "mm-broadband-modem-novatel.h" #include "mm-log.h" @@ -42,147 +43,6 @@ int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; /*****************************************************************************/ -/* Custom init */ - -typedef struct { - MMPortProbe *probe; - MMPortSerialAt *port; - GCancellable *cancellable; - GSimpleAsyncResult *result; - guint nwdmat_retries; - guint wait_time; -} CustomInitContext; - -static void -custom_init_context_complete_and_free (CustomInitContext *ctx) -{ - g_simple_async_result_complete_in_idle (ctx->result); - - if (ctx->cancellable) - g_object_unref (ctx->cancellable); - g_object_unref (ctx->port); - g_object_unref (ctx->probe); - g_object_unref (ctx->result); - g_slice_free (CustomInitContext, ctx); -} - -static gboolean -novatel_custom_init_finish (MMPortProbe *probe, - GAsyncResult *result, - GError **error) -{ - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); -} - -static void custom_init_step (CustomInitContext *ctx); - -static void -nwdmat_ready (MMPortSerialAt *port, - GAsyncResult *res, - CustomInitContext *ctx) -{ - const gchar *response; - GError *error = NULL; - - response = mm_port_serial_at_command_finish (port, res, &error); - if (error) { - if (g_error_matches (error, - MM_SERIAL_ERROR, - MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { - custom_init_step (ctx); - goto out; - } - - mm_dbg ("(Novatel) Error flipping secondary ports to AT mode: %s", error->message); - } - - /* Finish custom_init */ - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - custom_init_context_complete_and_free (ctx); - -out: - if (error) - g_error_free (error); -} - -static gboolean -custom_init_wait_cb (CustomInitContext *ctx) -{ - custom_init_step (ctx); - return FALSE; -} - -static void -custom_init_step (CustomInitContext *ctx) -{ - /* If cancelled, end */ - if (g_cancellable_is_cancelled (ctx->cancellable)) { - mm_dbg ("(Novatel) no need to keep on running custom init in (%s)", - mm_port_get_device (MM_PORT (ctx->port))); - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - custom_init_context_complete_and_free (ctx); - return; - } - - /* If device has a QMI port, don't run $NWDMAT */ - if (mm_port_probe_list_has_qmi_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (ctx->probe)))) { - mm_dbg ("(Novatel) no need to run custom init in (%s): device has QMI port", - mm_port_get_device (MM_PORT (ctx->port))); - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - custom_init_context_complete_and_free (ctx); - return; - } - - if (ctx->wait_time > 0) { - ctx->wait_time--; - g_timeout_add_seconds (1, (GSourceFunc)custom_init_wait_cb, ctx); - return; - } - - if (ctx->nwdmat_retries > 0) { - ctx->nwdmat_retries--; - mm_port_serial_at_command (ctx->port, - "$NWDMAT=1", - 3, - FALSE, /* raw */ - FALSE, /* allow_cached */ - ctx->cancellable, - (GAsyncReadyCallback)nwdmat_ready, - ctx); - return; - } - - /* Finish custom_init */ - mm_dbg ("(Novatel) couldn't flip secondary port to AT in (%s): all retries consumed", - mm_port_get_device (MM_PORT (ctx->port))); - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - custom_init_context_complete_and_free (ctx); -} - -static void -novatel_custom_init (MMPortProbe *probe, - MMPortSerialAt *port, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - CustomInitContext *ctx; - - ctx = g_slice_new (CustomInitContext); - ctx->result = g_simple_async_result_new (G_OBJECT (probe), - callback, - user_data, - novatel_custom_init); - ctx->probe = g_object_ref (probe); - ctx->port = g_object_ref (port); - ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL; - ctx->nwdmat_retries = 3; - ctx->wait_time = 2; - - custom_init_step (ctx); -} - -/*****************************************************************************/ static MMBaseModem * create_modem (MMPlugin *self, @@ -223,8 +83,8 @@ mm_plugin_create (void) static const mm_uint16_pair forbidden_products[] = { { 0x1410, 0x9010 }, /* Novatel E362 */ { 0, 0 } }; static const MMAsyncMethod custom_init = { - .async = G_CALLBACK (novatel_custom_init), - .finish = G_CALLBACK (novatel_custom_init_finish), + .async = G_CALLBACK (mm_common_novatel_custom_init), + .finish = G_CALLBACK (mm_common_novatel_custom_init_finish), }; return MM_PLUGIN ( -- 2.7.4 0002-novatel-build-a-common-non-inst-library-with-the-cor.patch0000664000175100017510000000403713117476072021756 0ustar uuFrom 138d1e8927f5dc20552d672a574825983aee7246 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Fri, 6 Feb 2015 15:55:18 +0100 Subject: [PATCH 2/9] novatel: build a common non-inst library with the core Novatel modem support --- plugins/Makefile.am | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 7731804..67f28e3 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -442,6 +442,19 @@ libmm_plugin_iridium_la_SOURCES = \ libmm_plugin_iridium_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) libmm_plugin_iridium_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) +# Common Novatel modem support library +noinst_LTLIBRARIES += libmm-utils-novatel.la +libmm_utils_novatel_la_SOURCES = \ + novatel/mm-common-novatel.c \ + novatel/mm-common-novatel.h \ + novatel/mm-broadband-modem-novatel.c \ + novatel/mm-broadband-modem-novatel.h +libmm_utils_novatel_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) +libmm_utils_novatel_la_LIBADD = $(GUDEV_LIBS) $(MM_LIBS) + +NOVATEL_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/novatel +NOVATEL_COMMON_LIBADD_FLAGS = $(builddir)/libmm-utils-novatel.la + # Novatel LTE modem libmm_plugin_novatel_lte_la_SOURCES = \ novatel/mm-plugin-novatel-lte.c \ @@ -458,13 +471,10 @@ libmm_plugin_novatel_lte_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) # Novatel non-LTE modem libmm_plugin_novatel_la_SOURCES = \ novatel/mm-plugin-novatel.c \ - novatel/mm-plugin-novatel.h \ - novatel/mm-common-novatel.c \ - novatel/mm-common-novatel.h \ - novatel/mm-broadband-modem-novatel.c \ - novatel/mm-broadband-modem-novatel.h -libmm_plugin_novatel_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) + novatel/mm-plugin-novatel.h +libmm_plugin_novatel_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(NOVATEL_COMMON_COMPILER_FLAGS) libmm_plugin_novatel_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) +libmm_plugin_novatel_la_LIBADD = $(NOVATEL_COMMON_LIBADD_FLAGS) # Altair LTE modem libmm_plugin_altair_lte_la_SOURCES = \ -- 2.7.4 0003-sierra-build-a-common-non-inst-library-with-the-core.patch0000664000175100017510000000425013117476072021736 0ustar uuFrom 16db1c0879ca5204e26a135522090177ff86f4a0 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Fri, 6 Feb 2015 16:51:13 +0100 Subject: [PATCH 3/9] sierra: build a common non-inst library with the core Sierra modem support --- plugins/Makefile.am | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 67f28e3..32c4a31 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -263,10 +263,9 @@ libmm_plugin_hso_la_SOURCES = \ libmm_plugin_hso_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) libmm_plugin_hso_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) -# Sierra -libmm_plugin_sierra_la_SOURCES = \ - sierra/mm-plugin-sierra.c \ - sierra/mm-plugin-sierra.h \ +# Common Sierra modem support library +noinst_LTLIBRARIES += libmm-utils-sierra.la +libmm_utils_sierra_la_SOURCES = \ sierra/mm-common-sierra.c \ sierra/mm-common-sierra.h \ sierra/mm-sim-sierra.c \ @@ -274,12 +273,22 @@ libmm_plugin_sierra_la_SOURCES = \ sierra/mm-broadband-bearer-sierra.c \ sierra/mm-broadband-bearer-sierra.h \ sierra/mm-broadband-modem-sierra.c \ - sierra/mm-broadband-modem-sierra.h \ + sierra/mm-broadband-modem-sierra.h +libmm_utils_sierra_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) +libmm_utils_sierra_la_LIBADD = $(GUDEV_LIBS) $(MM_LIBS) + +SIERRA_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/sierra +SIERRA_COMMON_LIBADD_FLAGS = $(builddir)/libmm-utils-sierra.la + +# Sierra +libmm_plugin_sierra_la_SOURCES = \ + sierra/mm-plugin-sierra.c \ + sierra/mm-plugin-sierra.h \ sierra/mm-broadband-modem-sierra-icera.c \ sierra/mm-broadband-modem-sierra-icera.h -libmm_plugin_sierra_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(ICERA_COMMON_COMPILER_FLAGS) +libmm_plugin_sierra_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(ICERA_COMMON_COMPILER_FLAGS) $(SIERRA_COMMON_COMPILER_FLAGS) libmm_plugin_sierra_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) -libmm_plugin_sierra_la_LIBADD = $(ICERA_COMMON_LIBADD_FLAGS) +libmm_plugin_sierra_la_LIBADD = $(ICERA_COMMON_LIBADD_FLAGS) $(SIERRA_COMMON_LIBADD_FLAGS) # Wavecom (Sierra Airlink) libmm_plugin_wavecom_la_SOURCES = \ -- 2.7.4 0004-mbm-build-a-common-non-inst-library-with-the-core-Er.patch0000664000175100017510000000340413117476072021571 0ustar uuFrom 885c964b80354139fc043e5291c4bf8d76431928 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Fri, 6 Feb 2015 17:11:52 +0100 Subject: [PATCH 4/9] mbm: build a common non-inst library with the core Ericsson modem support --- plugins/Makefile.am | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 32c4a31..1f03236 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -212,10 +212,9 @@ test_modem_helpers_huawei_LDADD = \ $(top_builddir)/src/libmodem-helpers.la test_modem_helpers_huawei_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) -# MBM -libmm_plugin_mbm_la_SOURCES = \ - mbm/mm-plugin-mbm.c \ - mbm/mm-plugin-mbm.h \ +# Common Mbm modem support library +noinst_LTLIBRARIES += libmm-utils-mbm.la +libmm_utils_mbm_la_SOURCES = \ mbm/mm-broadband-modem-mbm.c \ mbm/mm-broadband-modem-mbm.h \ mbm/mm-broadband-bearer-mbm.c \ @@ -224,8 +223,20 @@ libmm_plugin_mbm_la_SOURCES = \ mbm/mm-modem-helpers-mbm.h \ mbm/mm-sim-mbm.c \ mbm/mm-sim-mbm.h -libmm_plugin_mbm_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) +libmm_utils_mbm_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) +libmm_utils_mbm_la_LIBADD = $(GUDEV_LIBS) $(MM_LIBS) + +MBM_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/mbm +MBM_COMMON_LIBADD_FLAGS = $(builddir)/libmm-utils-mbm.la + +# MBM +libmm_plugin_mbm_la_SOURCES = \ + mbm/mm-plugin-mbm.c \ + mbm/mm-plugin-mbm.h +libmm_plugin_mbm_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(MBM_COMMON_COMPILER_FLAGS) libmm_plugin_mbm_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) +libmm_plugin_mbm_la_LIBADD = $(MBM_COMMON_LIBADD_FLAGS) + udevrules_DATA += mbm/77-mm-ericsson-mbm.rules noinst_PROGRAMS += test-modem-helpers-mbm -- 2.7.4 0005-sierra-move-custom-init-and-port-grabbing-methods-to.patch0000664000175100017510000005027113117476072022052 0ustar uuFrom 2c37d68a0614246bcb74b21447bd690ae4d54b37 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Fri, 6 Feb 2015 17:29:56 +0100 Subject: [PATCH 5/9] sierra: move custom init and port grabbing methods to separate source files --- plugins/sierra/mm-common-sierra.c | 217 ++++++++++++++++++++++++++++++++++++ plugins/sierra/mm-common-sierra.h | 17 +++ plugins/sierra/mm-plugin-sierra.c | 224 +------------------------------------- 3 files changed, 239 insertions(+), 219 deletions(-) diff --git a/plugins/sierra/mm-common-sierra.c b/plugins/sierra/mm-common-sierra.c index 82a5d7b..e712093 100644 --- a/plugins/sierra/mm-common-sierra.c +++ b/plugins/sierra/mm-common-sierra.c @@ -15,6 +15,9 @@ * Copyright (C) 2012 Lanedo GmbH */ +#include +#include + #include "mm-common-sierra.h" #include "mm-base-modem-at.h" #include "mm-log.h" @@ -24,6 +27,220 @@ static MMIfaceModem *iface_modem_parent; /*****************************************************************************/ +/* Custom init and port type hints */ + +#define TAG_SIERRA_APP_PORT "sierra-app-port" +#define TAG_SIERRA_APP1_PPP_OK "sierra-app1-ppp-ok" + +gboolean +mm_common_sierra_grab_port (MMPlugin *self, + MMBaseModem *modem, + MMPortProbe *probe, + GError **error) +{ + MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE; + MMPortType ptype; + + ptype = mm_port_probe_get_port_type (probe); + + /* Is it a GSM secondary port? */ + if (g_object_get_data (G_OBJECT (probe), TAG_SIERRA_APP_PORT)) { + if (g_object_get_data (G_OBJECT (probe), TAG_SIERRA_APP1_PPP_OK)) + pflags = MM_PORT_SERIAL_AT_FLAG_PPP; + else + pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY; + } else if (ptype == MM_PORT_TYPE_AT) + pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY; + + return mm_base_modem_grab_port (modem, + mm_port_probe_get_port_subsys (probe), + mm_port_probe_get_port_name (probe), + mm_port_probe_get_parent_path (probe), + ptype, + pflags, + error); +} + +gboolean +mm_common_sierra_port_probe_list_is_icera (GList *probes) +{ + GList *l; + + for (l = probes; l; l = g_list_next (l)) { + /* Only assume the Icera probing check is valid IF the port is not + * secondary. This will skip the stupid ports which reply OK to every + * AT command, even the one we use to check for Icera support */ + if (mm_port_probe_is_icera (MM_PORT_PROBE (l->data)) && + !g_object_get_data (G_OBJECT (l->data), TAG_SIERRA_APP_PORT)) + return TRUE; + } + + return FALSE; +} + +typedef struct { + MMPortProbe *probe; + MMPortSerialAt *port; + GCancellable *cancellable; + GSimpleAsyncResult *result; + guint retries; +} SierraCustomInitContext; + +static void +sierra_custom_init_context_complete_and_free (SierraCustomInitContext *ctx) +{ + g_simple_async_result_complete_in_idle (ctx->result); + + if (ctx->cancellable) + g_object_unref (ctx->cancellable); + g_object_unref (ctx->port); + g_object_unref (ctx->probe); + g_object_unref (ctx->result); + g_slice_free (SierraCustomInitContext, ctx); +} + +gboolean +mm_common_sierra_custom_init_finish (MMPortProbe *probe, + GAsyncResult *result, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); +} + +static void sierra_custom_init_step (SierraCustomInitContext *ctx); + +static void +gcap_ready (MMPortSerialAt *port, + GAsyncResult *res, + SierraCustomInitContext *ctx) +{ + const gchar *response; + GError *error = NULL; + + response = mm_port_serial_at_command_finish (port, res, &error); + if (error) { + /* If consumed all tries and the last error was a timeout, assume the + * port is not AT */ + if (ctx->retries == 0 && + g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { + mm_port_probe_set_result_at (ctx->probe, FALSE); + } + /* If reported a hard parse error, this port is definitely not an AT + * port, skip trying. */ + else if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_PARSE_FAILED)) { + mm_port_probe_set_result_at (ctx->probe, FALSE); + ctx->retries = 0; + } + /* Some Icera-based devices (eg, USB305) have an AT-style port that + * replies to everything with ERROR, so tag as unsupported; sometimes + * the real AT ports do this too, so let a retry tag the port as + * supported if it responds correctly later. */ + else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN)) { + mm_port_probe_set_result_at (ctx->probe, FALSE); + } + + /* Just retry... */ + sierra_custom_init_step (ctx); + goto out; + } + + /* A valid reply to ATI tells us this is an AT port already */ + mm_port_probe_set_result_at (ctx->probe, TRUE); + + /* Sierra APPx ports have limited AT command parsers that just reply with + * "OK" to most commands. These can sometimes be used for PPP while the + * main port is used for status and control, but older modems tend to crash + * or fail PPP. So we whitelist modems that are known to allow PPP on the + * secondary APP ports. + */ + if (strstr (response, "APP1")) { + g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP_PORT, GUINT_TO_POINTER (TRUE)); + + /* PPP-on-APP1-port whitelist */ + if (strstr (response, "C885") || + strstr (response, "USB 306") || + strstr (response, "MC8790")) + g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP1_PPP_OK, GUINT_TO_POINTER (TRUE)); + + /* For debugging: let users figure out if their device supports PPP + * on the APP1 port or not. + */ + if (getenv ("MM_SIERRA_APP1_PPP_OK")) { + mm_dbg ("Sierra: APP1 PPP OK '%s'", response); + g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP1_PPP_OK, GUINT_TO_POINTER (TRUE)); + } + } else if (strstr (response, "APP2") || + strstr (response, "APP3") || + strstr (response, "APP4")) { + /* Additional APP ports don't support most AT commands, so they cannot + * be used as the primary port. + */ + g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP_PORT, GUINT_TO_POINTER (TRUE)); + } + + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + sierra_custom_init_context_complete_and_free (ctx); + +out: + if (error) + g_error_free (error); +} + +static void +sierra_custom_init_step (SierraCustomInitContext *ctx) +{ + /* If cancelled, end */ + if (g_cancellable_is_cancelled (ctx->cancellable)) { + mm_dbg ("(Sierra) no need to keep on running custom init in '%s'", + mm_port_get_device (MM_PORT (ctx->port))); + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + sierra_custom_init_context_complete_and_free (ctx); + return; + } + + if (ctx->retries == 0) { + mm_dbg ("(Sierra) Couldn't get port type hints from '%s'", + mm_port_get_device (MM_PORT (ctx->port))); + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + sierra_custom_init_context_complete_and_free (ctx); + return; + } + + ctx->retries--; + mm_port_serial_at_command ( + ctx->port, + "ATI", + 3, + FALSE, /* raw */ + FALSE, /* allow_cached */ + ctx->cancellable, + (GAsyncReadyCallback)gcap_ready, + ctx); +} + +void +mm_common_sierra_custom_init (MMPortProbe *probe, + MMPortSerialAt *port, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SierraCustomInitContext *ctx; + + ctx = g_slice_new (SierraCustomInitContext); + ctx->result = g_simple_async_result_new (G_OBJECT (probe), + callback, + user_data, + mm_common_sierra_custom_init); + ctx->probe = g_object_ref (probe); + ctx->port = g_object_ref (port); + ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + ctx->retries = 3; + + sierra_custom_init_step (ctx); +} + +/*****************************************************************************/ /* Modem power up (Modem interface) */ gboolean diff --git a/plugins/sierra/mm-common-sierra.h b/plugins/sierra/mm-common-sierra.h index ec206d2..22471c0 100644 --- a/plugins/sierra/mm-common-sierra.h +++ b/plugins/sierra/mm-common-sierra.h @@ -18,10 +18,27 @@ #ifndef MM_COMMON_SIERRA_H #define MM_COMMON_SIERRA_H +#include "mm-plugin.h" #include "mm-broadband-modem.h" #include "mm-iface-modem.h" #include "mm-base-sim.h" +gboolean mm_common_sierra_grab_port (MMPlugin *self, + MMBaseModem *modem, + MMPortProbe *probe, + GError **error); + +gboolean mm_common_sierra_port_probe_list_is_icera (GList *probes); + +void mm_common_sierra_custom_init (MMPortProbe *probe, + MMPortSerialAt *port, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_common_sierra_custom_init_finish (MMPortProbe *probe, + GAsyncResult *result, + GError **error); + void mm_common_sierra_load_power_state (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data); diff --git a/plugins/sierra/mm-plugin-sierra.c b/plugins/sierra/mm-plugin-sierra.c index 96f657a..665b61c 100644 --- a/plugins/sierra/mm-plugin-sierra.c +++ b/plugins/sierra/mm-plugin-sierra.c @@ -15,7 +15,6 @@ * Copyright (C) 2012 Lanedo GmbH */ -#include #include #include @@ -24,6 +23,7 @@ #include "mm-log.h" #include "mm-plugin-sierra.h" +#include "mm-common-sierra.h" #include "mm-broadband-modem-sierra.h" #include "mm-broadband-modem-sierra-icera.h" @@ -41,191 +41,6 @@ int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; /*****************************************************************************/ -/* Custom init */ - -#define TAG_SIERRA_APP_PORT "sierra-app-port" -#define TAG_SIERRA_APP1_PPP_OK "sierra-app1-ppp-ok" - -typedef struct { - MMPortProbe *probe; - MMPortSerialAt *port; - GCancellable *cancellable; - GSimpleAsyncResult *result; - guint retries; -} SierraCustomInitContext; - -static void -sierra_custom_init_context_complete_and_free (SierraCustomInitContext *ctx) -{ - g_simple_async_result_complete_in_idle (ctx->result); - - if (ctx->cancellable) - g_object_unref (ctx->cancellable); - g_object_unref (ctx->port); - g_object_unref (ctx->probe); - g_object_unref (ctx->result); - g_slice_free (SierraCustomInitContext, ctx); -} - -static gboolean -sierra_custom_init_finish (MMPortProbe *probe, - GAsyncResult *result, - GError **error) -{ - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); -} - -static void sierra_custom_init_step (SierraCustomInitContext *ctx); - -static void -gcap_ready (MMPortSerialAt *port, - GAsyncResult *res, - SierraCustomInitContext *ctx) -{ - const gchar *response; - GError *error = NULL; - - response = mm_port_serial_at_command_finish (port, res, &error); - if (error) { - /* If consumed all tries and the last error was a timeout, assume the - * port is not AT */ - if (ctx->retries == 0 && - g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { - mm_port_probe_set_result_at (ctx->probe, FALSE); - } - /* If reported a hard parse error, this port is definitely not an AT - * port, skip trying. */ - else if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_PARSE_FAILED)) { - mm_port_probe_set_result_at (ctx->probe, FALSE); - ctx->retries = 0; - } - /* Some Icera-based devices (eg, USB305) have an AT-style port that - * replies to everything with ERROR, so tag as unsupported; sometimes - * the real AT ports do this too, so let a retry tag the port as - * supported if it responds correctly later. */ - else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN)) { - mm_port_probe_set_result_at (ctx->probe, FALSE); - } - - /* Just retry... */ - sierra_custom_init_step (ctx); - goto out; - } - - /* A valid reply to ATI tells us this is an AT port already */ - mm_port_probe_set_result_at (ctx->probe, TRUE); - - /* Sierra APPx ports have limited AT command parsers that just reply with - * "OK" to most commands. These can sometimes be used for PPP while the - * main port is used for status and control, but older modems tend to crash - * or fail PPP. So we whitelist modems that are known to allow PPP on the - * secondary APP ports. - */ - if (strstr (response, "APP1")) { - g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP_PORT, GUINT_TO_POINTER (TRUE)); - - /* PPP-on-APP1-port whitelist */ - if (strstr (response, "C885") || - strstr (response, "USB 306") || - strstr (response, "MC8790")) - g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP1_PPP_OK, GUINT_TO_POINTER (TRUE)); - - /* For debugging: let users figure out if their device supports PPP - * on the APP1 port or not. - */ - if (getenv ("MM_SIERRA_APP1_PPP_OK")) { - mm_dbg ("Sierra: APP1 PPP OK '%s'", response); - g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP1_PPP_OK, GUINT_TO_POINTER (TRUE)); - } - } else if (strstr (response, "APP2") || - strstr (response, "APP3") || - strstr (response, "APP4")) { - /* Additional APP ports don't support most AT commands, so they cannot - * be used as the primary port. - */ - g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP_PORT, GUINT_TO_POINTER (TRUE)); - } - - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - sierra_custom_init_context_complete_and_free (ctx); - -out: - if (error) - g_error_free (error); -} - -static void -sierra_custom_init_step (SierraCustomInitContext *ctx) -{ - /* If cancelled, end */ - if (g_cancellable_is_cancelled (ctx->cancellable)) { - mm_dbg ("(Sierra) no need to keep on running custom init in '%s'", - mm_port_get_device (MM_PORT (ctx->port))); - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - sierra_custom_init_context_complete_and_free (ctx); - return; - } - - if (ctx->retries == 0) { - mm_dbg ("(Sierra) Couldn't get port type hints from '%s'", - mm_port_get_device (MM_PORT (ctx->port))); - g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); - sierra_custom_init_context_complete_and_free (ctx); - return; - } - - ctx->retries--; - mm_port_serial_at_command ( - ctx->port, - "ATI", - 3, - FALSE, /* raw */ - FALSE, /* allow_cached */ - ctx->cancellable, - (GAsyncReadyCallback)gcap_ready, - ctx); -} - -static void -sierra_custom_init (MMPortProbe *probe, - MMPortSerialAt *port, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SierraCustomInitContext *ctx; - - ctx = g_slice_new (SierraCustomInitContext); - ctx->result = g_simple_async_result_new (G_OBJECT (probe), - callback, - user_data, - sierra_custom_init); - ctx->probe = g_object_ref (probe); - ctx->port = g_object_ref (port); - ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL; - ctx->retries = 3; - - sierra_custom_init_step (ctx); -} - -/*****************************************************************************/ - -static gboolean -sierra_port_probe_list_is_icera (GList *probes) -{ - GList *l; - - for (l = probes; l; l = g_list_next (l)) { - /* Only assume the Icera probing check is valid IF the port is not - * secondary. This will skip the stupid ports which reply OK to every - * AT command, even the one we use to check for Icera support */ - if (mm_port_probe_is_icera (MM_PORT_PROBE (l->data)) && - !g_object_get_data (G_OBJECT (l->data), TAG_SIERRA_APP_PORT)) - return TRUE; - } - - return FALSE; -} static MMBaseModem * create_modem (MMPlugin *self, @@ -258,7 +73,7 @@ create_modem (MMPlugin *self, } #endif - if (sierra_port_probe_list_is_icera (probes)) + if (mm_common_sierra_port_probe_list_is_icera (probes)) return MM_BASE_MODEM (mm_broadband_modem_sierra_icera_new (sysfs_path, drivers, mm_plugin_get_name (self), @@ -272,35 +87,6 @@ create_modem (MMPlugin *self, product)); } -static gboolean -grab_port (MMPlugin *self, - MMBaseModem *modem, - MMPortProbe *probe, - GError **error) -{ - MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE; - MMPortType ptype; - - ptype = mm_port_probe_get_port_type (probe); - - /* Is it a GSM secondary port? */ - if (g_object_get_data (G_OBJECT (probe), TAG_SIERRA_APP_PORT)) { - if (g_object_get_data (G_OBJECT (probe), TAG_SIERRA_APP1_PPP_OK)) - pflags = MM_PORT_SERIAL_AT_FLAG_PPP; - else - pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY; - } else if (ptype == MM_PORT_TYPE_AT) - pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY; - - return mm_base_modem_grab_port (modem, - mm_port_probe_get_port_subsys (probe), - mm_port_probe_get_port_name (probe), - mm_port_probe_get_parent_path (probe), - ptype, - pflags, - error); -} - /*****************************************************************************/ G_MODULE_EXPORT MMPlugin * @@ -309,8 +95,8 @@ mm_plugin_create (void) static const gchar *subsystems[] = { "tty", "net", "usb", NULL }; static const gchar *drivers[] = { "sierra", "sierra_net", NULL }; static const MMAsyncMethod custom_init = { - .async = G_CALLBACK (sierra_custom_init), - .finish = G_CALLBACK (sierra_custom_init_finish), + .async = G_CALLBACK (mm_common_sierra_custom_init), + .finish = G_CALLBACK (mm_common_sierra_custom_init_finish), }; return MM_PLUGIN ( @@ -339,5 +125,5 @@ mm_plugin_sierra_class_init (MMPluginSierraClass *klass) MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; - plugin_class->grab_port = grab_port; + plugin_class->grab_port = mm_common_sierra_grab_port; } -- 2.7.4 0006-sierra-move-all-the-legacy-Sierra-support-to-a-new-s.patch0000664000175100017510000003116113117476072021633 0ustar uuFrom ba2c094b17462ab865ad047427a865ac5bb161bc Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Fri, 6 Feb 2015 20:02:20 +0100 Subject: [PATCH 6/9] sierra: move all the legacy Sierra support to a new 'sierra-legacy' plugin In short: * The 'sierra-legacy' plugin will handle all the old AT based modems, including the DirectIP ones. This plugin is filtered by driver ('sierra' or 'sierra_net') and forbidden-drivers ('qmi_wwan' and 'cdc_mbim'). This plugin should also grab HP and AT&T branded models if they are handled by the proper kernel driver. * The 'sierra' plugin will only handle QMI or MBIM based Sierra modems, which are really all the new ones. This plugin is filtered by VID (0x1199) and driver (qmi_wwan and cdc_mbim). For this separation to work, the 'sierra' and 'sierra_net' plugins need to be complementary to each other. --- plugins/Makefile.am | 18 ++++-- plugins/sierra/mm-plugin-sierra-legacy.c | 99 ++++++++++++++++++++++++++++++++ plugins/sierra/mm-plugin-sierra-legacy.h | 40 +++++++++++++ plugins/sierra/mm-plugin-sierra.c | 34 ++++------- 4 files changed, 162 insertions(+), 29 deletions(-) create mode 100644 plugins/sierra/mm-plugin-sierra-legacy.c create mode 100644 plugins/sierra/mm-plugin-sierra-legacy.h diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 1f03236..b656429 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -142,6 +142,7 @@ pkglib_LTLIBRARIES = \ libmm-plugin-pantech.la \ libmm-plugin-zte.la \ libmm-plugin-sierra.la \ + libmm-plugin-sierra-legacy.la \ libmm-plugin-mbm.la \ libmm-plugin-via.la \ libmm-plugin-telit.la \ @@ -291,15 +292,22 @@ libmm_utils_sierra_la_LIBADD = $(GUDEV_LIBS) $(MM_LIBS) SIERRA_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/sierra SIERRA_COMMON_LIBADD_FLAGS = $(builddir)/libmm-utils-sierra.la -# Sierra +# Sierra (new QMI or MBIM modems) libmm_plugin_sierra_la_SOURCES = \ sierra/mm-plugin-sierra.c \ - sierra/mm-plugin-sierra.h \ + sierra/mm-plugin-sierra.h +libmm_plugin_sierra_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) +libmm_plugin_sierra_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) + +# Sierra (legacy) +libmm_plugin_sierra_legacy_la_SOURCES = \ + sierra/mm-plugin-sierra-legacy.c \ + sierra/mm-plugin-sierra-legacy.h \ sierra/mm-broadband-modem-sierra-icera.c \ sierra/mm-broadband-modem-sierra-icera.h -libmm_plugin_sierra_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(ICERA_COMMON_COMPILER_FLAGS) $(SIERRA_COMMON_COMPILER_FLAGS) -libmm_plugin_sierra_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) -libmm_plugin_sierra_la_LIBADD = $(ICERA_COMMON_LIBADD_FLAGS) $(SIERRA_COMMON_LIBADD_FLAGS) +libmm_plugin_sierra_legacy_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(ICERA_COMMON_COMPILER_FLAGS) $(SIERRA_COMMON_COMPILER_FLAGS) +libmm_plugin_sierra_legacy_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) +libmm_plugin_sierra_legacy_la_LIBADD = $(ICERA_COMMON_LIBADD_FLAGS) $(SIERRA_COMMON_LIBADD_FLAGS) # Wavecom (Sierra Airlink) libmm_plugin_wavecom_la_SOURCES = \ diff --git a/plugins/sierra/mm-plugin-sierra-legacy.c b/plugins/sierra/mm-plugin-sierra-legacy.c new file mode 100644 index 0000000..9064bcf --- /dev/null +++ b/plugins/sierra/mm-plugin-sierra-legacy.c @@ -0,0 +1,99 @@ +/* -*- 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) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 - 2012 Red Hat, Inc. + * Copyright (C) 2012 Lanedo GmbH + * Copyright (C) 2015 Aleksander Morgado + */ + +#include +#include + +#define _LIBMM_INSIDE_MM +#include + +#include "mm-log.h" +#include "mm-plugin-sierra-legacy.h" +#include "mm-common-sierra.h" +#include "mm-broadband-modem-sierra.h" +#include "mm-broadband-modem-sierra-icera.h" + +G_DEFINE_TYPE (MMPluginSierraLegacy, mm_plugin_sierra_legacy, MM_TYPE_PLUGIN) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +/*****************************************************************************/ + +static MMBaseModem * +create_modem (MMPlugin *self, + const gchar *sysfs_path, + const gchar **drivers, + guint16 vendor, + guint16 product, + GList *probes, + GError **error) +{ + if (mm_common_sierra_port_probe_list_is_icera (probes)) + return MM_BASE_MODEM (mm_broadband_modem_sierra_icera_new (sysfs_path, + drivers, + mm_plugin_get_name (self), + vendor, + product)); + + return MM_BASE_MODEM (mm_broadband_modem_sierra_new (sysfs_path, + drivers, + mm_plugin_get_name (self), + vendor, + product)); +} + +/*****************************************************************************/ + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + static const gchar *subsystems[] = { "tty", "net", NULL }; + static const gchar *drivers[] = { "sierra", "sierra_net", NULL }; + static const gchar *forbidden_drivers[] = { "qmi_wwan", "cdc_mbim", NULL }; + static const MMAsyncMethod custom_init = { + .async = G_CALLBACK (mm_common_sierra_custom_init), + .finish = G_CALLBACK (mm_common_sierra_custom_init_finish), + }; + + return MM_PLUGIN ( + g_object_new (MM_TYPE_PLUGIN_SIERRA_LEGACY, + MM_PLUGIN_NAME, "Sierra (legacy)", + MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, + MM_PLUGIN_ALLOWED_DRIVERS, drivers, + MM_PLUGIN_FORBIDDEN_DRIVERS, forbidden_drivers, + MM_PLUGIN_ALLOWED_AT, TRUE, + MM_PLUGIN_CUSTOM_INIT, &custom_init, + MM_PLUGIN_ICERA_PROBE, TRUE, + MM_PLUGIN_REMOVE_ECHO, FALSE, + NULL)); +} + +static void +mm_plugin_sierra_legacy_init (MMPluginSierraLegacy *self) +{ +} + +static void +mm_plugin_sierra_legacy_class_init (MMPluginSierraLegacyClass *klass) +{ + MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); + + plugin_class->create_modem = create_modem; + plugin_class->grab_port = mm_common_sierra_grab_port; +} diff --git a/plugins/sierra/mm-plugin-sierra-legacy.h b/plugins/sierra/mm-plugin-sierra-legacy.h new file mode 100644 index 0000000..787118d --- /dev/null +++ b/plugins/sierra/mm-plugin-sierra-legacy.h @@ -0,0 +1,40 @@ +/* -*- 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) 2015 Aleksander Morgado + */ + +#ifndef MM_PLUGIN_SIERRA_LEGACY_H +#define MM_PLUGIN_SIERRA_LEGACY_H + +#include "mm-plugin.h" + +#define MM_TYPE_PLUGIN_SIERRA_LEGACY (mm_plugin_sierra_legacy_get_type ()) +#define MM_PLUGIN_SIERRA_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_SIERRA_LEGACY, MMPluginSierraLegacy)) +#define MM_PLUGIN_SIERRA_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_SIERRA_LEGACY, MMPluginSierraLegacyClass)) +#define MM_IS_PLUGIN_SIERRA_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_SIERRA_LEGACY)) +#define MM_IS_PLUGIN_SIERRA_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_SIERRA_LEGACY)) +#define MM_PLUGIN_SIERRA_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_SIERRA_LEGACY, MMPluginSierraLegacyClass)) + +typedef struct { + MMPlugin parent; +} MMPluginSierraLegacy; + +typedef struct { + MMPluginClass parent; +} MMPluginSierraLegacyClass; + +GType mm_plugin_sierra_legacy_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_SIERRA_LEGACY_H */ diff --git a/plugins/sierra/mm-plugin-sierra.c b/plugins/sierra/mm-plugin-sierra.c index 665b61c..62262ae 100644 --- a/plugins/sierra/mm-plugin-sierra.c +++ b/plugins/sierra/mm-plugin-sierra.c @@ -13,6 +13,7 @@ * Copyright (C) 2008 - 2009 Novell, Inc. * Copyright (C) 2009 - 2012 Red Hat, Inc. * Copyright (C) 2012 Lanedo GmbH + * Copyright (C) 2015 Aleksander Morgado */ #include @@ -23,9 +24,6 @@ #include "mm-log.h" #include "mm-plugin-sierra.h" -#include "mm-common-sierra.h" -#include "mm-broadband-modem-sierra.h" -#include "mm-broadband-modem-sierra-icera.h" #if defined WITH_QMI #include "mm-broadband-modem-qmi.h" @@ -73,18 +71,12 @@ create_modem (MMPlugin *self, } #endif - if (mm_common_sierra_port_probe_list_is_icera (probes)) - return MM_BASE_MODEM (mm_broadband_modem_sierra_icera_new (sysfs_path, - drivers, - mm_plugin_get_name (self), - vendor, - product)); - - return MM_BASE_MODEM (mm_broadband_modem_sierra_new (sysfs_path, - drivers, - mm_plugin_get_name (self), - vendor, - product)); + /* Fallback to default modem in the worst case */ + return MM_BASE_MODEM (mm_broadband_modem_new (sysfs_path, + drivers, + mm_plugin_get_name (self), + vendor, + product)); } /*****************************************************************************/ @@ -93,24 +85,19 @@ G_MODULE_EXPORT MMPlugin * mm_plugin_create (void) { static const gchar *subsystems[] = { "tty", "net", "usb", NULL }; - static const gchar *drivers[] = { "sierra", "sierra_net", NULL }; - static const MMAsyncMethod custom_init = { - .async = G_CALLBACK (mm_common_sierra_custom_init), - .finish = G_CALLBACK (mm_common_sierra_custom_init_finish), - }; + static const guint16 vendor_ids[] = { 0x1199, 0 }; + static const gchar *drivers[] = { "qmi_wwan", "cdc_mbim", NULL }; return MM_PLUGIN ( g_object_new (MM_TYPE_PLUGIN_SIERRA, MM_PLUGIN_NAME, "Sierra", MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, + MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, MM_PLUGIN_ALLOWED_DRIVERS, drivers, MM_PLUGIN_ALLOWED_AT, TRUE, MM_PLUGIN_ALLOWED_QCDM, TRUE, MM_PLUGIN_ALLOWED_QMI, TRUE, MM_PLUGIN_ALLOWED_MBIM, TRUE, - MM_PLUGIN_CUSTOM_INIT, &custom_init, - MM_PLUGIN_ICERA_PROBE, TRUE, - MM_PLUGIN_REMOVE_ECHO, FALSE, NULL)); } @@ -125,5 +112,4 @@ mm_plugin_sierra_class_init (MMPluginSierraClass *klass) MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); plugin_class->create_modem = create_modem; - plugin_class->grab_port = mm_common_sierra_grab_port; } -- 2.7.4 0007-dell-new-Dell-plugin.patch0000664000175100017510000005342513117476072013663 0ustar uuFrom 25036c83bd8fb4e12f76ba6aeac75f28b675207a Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Fri, 6 Feb 2015 16:35:52 +0100 Subject: [PATCH 7/9] dell: new Dell plugin For Dell-branded Novatel, Sierra and Ericsson modems. The Novatel plugin will no longer accept every Dell-branded modem, which was the current situation. Instead, a new Dell plugin will take care of probing for the correct vendor string, and based on the results create a specific Novatel, Sierra or Ericsson modem. In order to properly support this, the Novatel, Sierra and MBM plugins now export their implementations into non-inst libraries that the Dell plugin will import. Also, for now, the Dell plugin doesn't make any difference between e.g. Sierra or Ericsson MBIM implementations, just a generic MBIM modem is created in both cases, as that is anyway what the Ericsson MBM and Sierra plugins do already. https://bugs.freedesktop.org/show_bug.cgi?id=86713 --- plugins/Makefile.am | 9 + plugins/dell/mm-plugin-dell.c | 448 ++++++++++++++++++++++++++++++++++++ plugins/dell/mm-plugin-dell.h | 46 ++++ plugins/novatel/mm-plugin-novatel.c | 4 +- 4 files changed, 504 insertions(+), 3 deletions(-) create mode 100644 plugins/dell/mm-plugin-dell.c create mode 100644 plugins/dell/mm-plugin-dell.h diff --git a/plugins/Makefile.am b/plugins/Makefile.am index b656429..589d976 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -128,6 +128,7 @@ pkglib_LTLIBRARIES = \ libmm-plugin-motorola.la \ libmm-plugin-novatel.la \ libmm-plugin-novatel-lte.la \ + libmm-plugin-dell.la \ libmm-plugin-altair-lte.la \ libmm-plugin-samsung.la \ libmm-plugin-option.la \ @@ -504,6 +505,14 @@ libmm_plugin_novatel_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(NOVATEL_COM libmm_plugin_novatel_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) libmm_plugin_novatel_la_LIBADD = $(NOVATEL_COMMON_LIBADD_FLAGS) +# Dell (e.g. Novatel or Sierra) modem +libmm_plugin_dell_la_SOURCES = \ + dell/mm-plugin-dell.c \ + dell/mm-plugin-dell.h +libmm_plugin_dell_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(NOVATEL_COMMON_COMPILER_FLAGS) $(SIERRA_COMMON_COMPILER_FLAGS) $(MBM_COMMON_COMPILER_FLAGS) +libmm_plugin_dell_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) +libmm_plugin_dell_la_LIBADD = $(NOVATEL_COMMON_LIBADD_FLAGS) $(SIERRA_COMMON_LIBADD_FLAGS) $(MBM_COMMON_LIBADD_FLAGS) + # Altair LTE modem libmm_plugin_altair_lte_la_SOURCES = \ altair/mm-modem-helpers-altair-lte.c \ diff --git a/plugins/dell/mm-plugin-dell.c b/plugins/dell/mm-plugin-dell.c new file mode 100644 index 0000000..0dff19b --- /dev/null +++ b/plugins/dell/mm-plugin-dell.c @@ -0,0 +1,448 @@ +/* -*- 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. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Copyright (C) 2015 Aleksander Morgado + */ + +#include +#include + +#define _LIBMM_INSIDE_MM +#include + +#include "mm-plugin-dell.h" +#include "mm-common-novatel.h" +#include "mm-private-boxed-types.h" +#include "mm-broadband-modem.h" +#include "mm-broadband-modem-novatel.h" +#include "mm-common-novatel.h" +#include "mm-broadband-modem-sierra.h" +#include "mm-common-sierra.h" +#include "mm-log.h" + +#if defined WITH_QMI +#include "mm-broadband-modem-qmi.h" +#endif + +#if defined WITH_MBIM +#include "mm-broadband-modem-mbim.h" +#endif + +G_DEFINE_TYPE (MMPluginDell, mm_plugin_dell, MM_TYPE_PLUGIN) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +#define TAG_DELL_MANUFACTURER "dell-manufacturer" +typedef enum { + DELL_MANUFACTURER_UNKNOWN = 0, + DELL_MANUFACTURER_NOVATEL = 1, + DELL_MANUFACTURER_SIERRA = 2, + DELL_MANUFACTURER_ERICSSON = 3 +} DellManufacturer; + +/*****************************************************************************/ +/* Custom init */ + +typedef struct { + MMPortProbe *probe; + MMPortSerialAt *port; + GCancellable *cancellable; + GSimpleAsyncResult *result; + guint gmi_retries; + guint cgmi_retries; + guint ati_retries; +} CustomInitContext; + +static void +custom_init_context_complete_and_free (CustomInitContext *ctx) +{ + g_simple_async_result_complete_in_idle (ctx->result); + + if (ctx->cancellable) + g_object_unref (ctx->cancellable); + g_object_unref (ctx->port); + g_object_unref (ctx->probe); + g_object_unref (ctx->result); + g_slice_free (CustomInitContext, ctx); +} + +static gboolean +dell_custom_init_finish (MMPortProbe *probe, + GAsyncResult *result, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); +} + +static void +novatel_custom_init_ready (MMPortProbe *probe, + GAsyncResult *res, + CustomInitContext *ctx) +{ + GError *error = NULL; + + if (!mm_common_novatel_custom_init_finish (probe, res, &error)) + g_simple_async_result_take_error (ctx->result, error); + else + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + custom_init_context_complete_and_free (ctx); +} + +static void +sierra_custom_init_ready (MMPortProbe *probe, + GAsyncResult *res, + CustomInitContext *ctx) +{ + GError *error = NULL; + + if (!mm_common_sierra_custom_init_finish (probe, res, &error)) + g_simple_async_result_take_error (ctx->result, error); + else + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + custom_init_context_complete_and_free (ctx); +} + +static void custom_init_step (CustomInitContext *ctx); + +static void +custom_init_step_next_command (CustomInitContext *ctx) +{ + if (ctx->gmi_retries > 0) + ctx->gmi_retries = 0; + else if (ctx->cgmi_retries > 0) + ctx->cgmi_retries = 0; + else if (ctx->ati_retries > 0) + ctx->ati_retries = 0; + custom_init_step (ctx); +} + +static void +response_ready (MMPortSerialAt *port, + GAsyncResult *res, + CustomInitContext *ctx) +{ + const gchar *response; + GError *error = NULL; + gchar *lower; + DellManufacturer manufacturer; + + response = mm_port_serial_at_command_finish (port, res, &error); + if (error) { + /* Non-timeout error, jump to next command */ + if (!g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { + mm_dbg ("(Dell) Error probing AT port: %s", error->message); + g_error_free (error); + custom_init_step_next_command (ctx); + return; + } + /* Directly retry same command on timeout */ + custom_init_step (ctx); + g_error_free (error); + return; + } + + /* Guess manufacturer from response */ + lower = g_ascii_strdown (response, -1); + if (strstr (lower, "novatel")) + manufacturer = DELL_MANUFACTURER_NOVATEL; + else if (strstr (lower, "sierra")) + manufacturer = DELL_MANUFACTURER_SIERRA; + else if (strstr (lower, "ericsson")) + manufacturer = DELL_MANUFACTURER_ERICSSON; + else + manufacturer = DELL_MANUFACTURER_UNKNOWN; + g_free (lower); + + /* Tag based on manufacturer */ + if (manufacturer != DELL_MANUFACTURER_UNKNOWN) { + g_object_set_data (G_OBJECT (ctx->probe), TAG_DELL_MANUFACTURER, GUINT_TO_POINTER (manufacturer)); + + /* Run additional custom init, if needed */ + + if (manufacturer == DELL_MANUFACTURER_NOVATEL) { + mm_common_novatel_custom_init (ctx->probe, + ctx->port, + ctx->cancellable, + (GAsyncReadyCallback) novatel_custom_init_ready, + ctx); + return; + } + + if (manufacturer == DELL_MANUFACTURER_SIERRA) { + mm_common_sierra_custom_init (ctx->probe, + ctx->port, + ctx->cancellable, + (GAsyncReadyCallback) sierra_custom_init_ready, + ctx); + return; + } + + /* Finish custom_init */ + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + custom_init_context_complete_and_free (ctx); + return; + } + + /* If we got a response, but we didn't get an expected string, try with next command */ + custom_init_step_next_command (ctx); +} + +static void +custom_init_step (CustomInitContext *ctx) +{ + /* If cancelled, end */ + if (g_cancellable_is_cancelled (ctx->cancellable)) { + mm_dbg ("(Dell) no need to keep on running custom init in (%s)", + mm_port_get_device (MM_PORT (ctx->port))); + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + custom_init_context_complete_and_free (ctx); + return; + } + +#if defined WITH_QMI + /* If device has a QMI port, don't run anything else, as we don't care */ + if (mm_port_probe_list_has_qmi_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (ctx->probe)))) { + mm_dbg ("(Dell) no need to run custom init in (%s): device has QMI port", + mm_port_get_device (MM_PORT (ctx->port))); + mm_port_probe_set_result_at (ctx->probe, FALSE); + mm_port_probe_set_result_qcdm (ctx->probe, FALSE); + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + custom_init_context_complete_and_free (ctx); + return; + } +#endif + +#if defined WITH_MBIM + /* If device has a MBIM port, don't run anything else, as we don't care */ + if (mm_port_probe_list_has_mbim_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (ctx->probe)))) { + mm_dbg ("(Dell) no need to run custom init in (%s): device has MBIM port", + mm_port_get_device (MM_PORT (ctx->port))); + mm_port_probe_set_result_at (ctx->probe, FALSE); + mm_port_probe_set_result_qcdm (ctx->probe, FALSE); + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + custom_init_context_complete_and_free (ctx); + return; + } +#endif + + if (ctx->gmi_retries > 0) { + ctx->gmi_retries--; + mm_port_serial_at_command (ctx->port, + "AT+GMI", + 3, + FALSE, /* raw */ + FALSE, /* allow_cached */ + ctx->cancellable, + (GAsyncReadyCallback)response_ready, + ctx); + return; + } + + if (ctx->cgmi_retries > 0) { + ctx->cgmi_retries--; + mm_port_serial_at_command (ctx->port, + "AT+CGMI", + 3, + FALSE, /* raw */ + FALSE, /* allow_cached */ + ctx->cancellable, + (GAsyncReadyCallback)response_ready, + ctx); + return; + } + + if (ctx->ati_retries > 0) { + ctx->ati_retries--; + /* Note: in Ericsson devices, ATI3 seems to reply the vendor string */ + mm_port_serial_at_command (ctx->port, + "ATI1I2I3", + 3, + FALSE, /* raw */ + FALSE, /* allow_cached */ + ctx->cancellable, + (GAsyncReadyCallback)response_ready, + ctx); + return; + } + + /* Finish custom_init */ + mm_dbg ("(Dell) couldn't flip secondary port to AT in (%s): all retries consumed", + mm_port_get_device (MM_PORT (ctx->port))); + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + custom_init_context_complete_and_free (ctx); +} + +static void +dell_custom_init (MMPortProbe *probe, + MMPortSerialAt *port, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + CustomInitContext *ctx; + + ctx = g_slice_new (CustomInitContext); + ctx->result = g_simple_async_result_new (G_OBJECT (probe), + callback, + user_data, + dell_custom_init); + ctx->probe = g_object_ref (probe); + ctx->port = g_object_ref (port); + ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + ctx->gmi_retries = 3; + ctx->ati_retries = 3; + + custom_init_step (ctx); +} + +/*****************************************************************************/ + +static gboolean +port_probe_list_has_manufacturer_port (GList *probes, + DellManufacturer manufacturer) +{ + GList *l; + + for (l = probes; l; l = g_list_next (l)) { + if (GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (l->data), TAG_DELL_MANUFACTURER)) == manufacturer) + return TRUE; + } + return FALSE; +} + +static MMBaseModem * +create_modem (MMPlugin *self, + const gchar *sysfs_path, + const gchar **drivers, + guint16 vendor, + guint16 product, + GList *probes, + GError **error) +{ + /* Note: at this point we don't make any difference between different + * Dell-branded QMI or MBIM modems; they may come from Novatel, Ericsson or + * Sierra. */ + +#if defined WITH_QMI + if (mm_port_probe_list_has_qmi_port (probes)) { + mm_dbg ("QMI-powered Dell-branded modem found..."); + return MM_BASE_MODEM (mm_broadband_modem_qmi_new (sysfs_path, + drivers, + mm_plugin_get_name (self), + vendor, + product)); + } +#endif + +#if defined WITH_MBIM + if (mm_port_probe_list_has_mbim_port (probes)) { + mm_dbg ("MBIM-powered Dell-branded modem found..."); + return MM_BASE_MODEM (mm_broadband_modem_mbim_new (sysfs_path, + drivers, + mm_plugin_get_name (self), + vendor, + product)); + } +#endif + + if (port_probe_list_has_manufacturer_port (probes, DELL_MANUFACTURER_NOVATEL)) { + mm_dbg ("Novatel-powered Dell-branded modem found..."); + return MM_BASE_MODEM (mm_broadband_modem_novatel_new (sysfs_path, + drivers, + mm_plugin_get_name (self), + vendor, + product)); + } + + if (port_probe_list_has_manufacturer_port (probes, DELL_MANUFACTURER_SIERRA)) { + mm_dbg ("Sierra-powered Dell-branded modem found..."); + return MM_BASE_MODEM (mm_broadband_modem_sierra_new (sysfs_path, + drivers, + mm_plugin_get_name (self), + vendor, + product)); + } + + mm_dbg ("Dell-branded generic modem found..."); + return MM_BASE_MODEM (mm_broadband_modem_new (sysfs_path, + drivers, + mm_plugin_get_name (self), + vendor, + product)); +} + +/*****************************************************************************/ + +static gboolean +grab_port (MMPlugin *self, + MMBaseModem *modem, + MMPortProbe *probe, + GError **error) +{ + /* Only Sierra needs custom grab port, due to the port type hints */ + if (MM_IS_BROADBAND_MODEM_SIERRA (modem)) + return mm_common_sierra_grab_port (self, modem, probe, error); + + return mm_base_modem_grab_port (modem, + mm_port_probe_get_port_subsys (probe), + mm_port_probe_get_port_name (probe), + mm_port_probe_get_parent_path (probe), + mm_port_probe_get_port_type (probe), + MM_PORT_SERIAL_AT_FLAG_NONE, + error); +} + +/*****************************************************************************/ + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + static const gchar *subsystems[] = { "tty", "net", "usb", NULL }; + static const guint16 vendors[] = { 0x413c, 0 }; + static const MMAsyncMethod custom_init = { + .async = G_CALLBACK (dell_custom_init), + .finish = G_CALLBACK (dell_custom_init_finish), + }; + + return MM_PLUGIN ( + g_object_new (MM_TYPE_PLUGIN_DELL, + MM_PLUGIN_NAME, "Dell", + MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, + MM_PLUGIN_ALLOWED_VENDOR_IDS, vendors, + MM_PLUGIN_ALLOWED_AT, TRUE, + MM_PLUGIN_CUSTOM_INIT, &custom_init, + MM_PLUGIN_ALLOWED_QCDM, TRUE, + MM_PLUGIN_ALLOWED_QMI, TRUE, + MM_PLUGIN_ALLOWED_MBIM, TRUE, + NULL)); +} + +static void +mm_plugin_dell_init (MMPluginDell *self) +{ +} + +static void +mm_plugin_dell_class_init (MMPluginDellClass *klass) +{ + MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); + + plugin_class->create_modem = create_modem; + plugin_class->grab_port = grab_port; +} diff --git a/plugins/dell/mm-plugin-dell.h b/plugins/dell/mm-plugin-dell.h new file mode 100644 index 0000000..cc1a539 --- /dev/null +++ b/plugins/dell/mm-plugin-dell.h @@ -0,0 +1,46 @@ +/* -*- 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. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Copyright (C) 2015 Aleksander Morgado + */ + +#ifndef MM_PLUGIN_DELL_H +#define MM_PLUGIN_DELL_H + +#include "mm-plugin.h" + +#define MM_TYPE_PLUGIN_DELL (mm_plugin_dell_get_type ()) +#define MM_PLUGIN_DELL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_DELL, MMPluginDell)) +#define MM_PLUGIN_DELL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_DELL, MMPluginDellClass)) +#define MM_IS_PLUGIN_DELL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_DELL)) +#define MM_IS_PLUGIN_DELL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_DELL)) +#define MM_PLUGIN_DELL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_DELL, MMPluginDellClass)) + +typedef struct { + MMPlugin parent; +} MMPluginDell; + +typedef struct { + MMPluginClass parent; +} MMPluginDellClass; + +GType mm_plugin_dell_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_DELL_H */ diff --git a/plugins/novatel/mm-plugin-novatel.c b/plugins/novatel/mm-plugin-novatel.c index 398a289..9476150 100644 --- a/plugins/novatel/mm-plugin-novatel.c +++ b/plugins/novatel/mm-plugin-novatel.c @@ -77,9 +77,7 @@ G_MODULE_EXPORT MMPlugin * mm_plugin_create (void) { static const gchar *subsystems[] = { "tty", "net", "usb", NULL }; - static const guint16 vendors[] = { 0x1410, /* Novatel */ - 0x413c, /* Dell */ - 0 }; + static const guint16 vendors[] = { 0x1410, 0 }; static const mm_uint16_pair forbidden_products[] = { { 0x1410, 0x9010 }, /* Novatel E362 */ { 0, 0 } }; static const MMAsyncMethod custom_init = { -- 2.7.4 0008-broadband-modem-qmi-use-DMS-Set-FCC-Authentication-i.patch0000664000175100017510000000354713117476072021342 0ustar uuFrom 183bf0b36631c9d883bd8097c6692d0d354ac98e Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Sat, 7 Feb 2015 21:39:00 +0100 Subject: [PATCH 8/9] broadband-modem-qmi: use 'DMS Set FCC Authentication' if online mode fails Some new devices, like the Dell DW5770, will return an internal error when trying to bring the power mode to online. We can avoid this by sending the magic "DMS Set FCC Auth" message before retrying. Bumping libqmi version to 1.13.4, which is the one that supports this new message. https://bugzilla.kernel.org/show_bug.cgi?id=92101 Conflicts: configure.ac src/mm-broadband-modem-qmi.c --- configure.ac | 2 +- src/mm-broadband-modem-qmi.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 05c9f51..d03e2f6 100644 --- a/configure.ac +++ b/configure.ac @@ -226,7 +226,7 @@ AC_ARG_WITH(qmi, AS_HELP_STRING([--without-qmi], [Build without QMI support]), [ AM_CONDITIONAL(WITH_QMI, test "x$with_qmi" = "xyes") case $with_qmi in yes) - PKG_CHECK_MODULES(QMI, [qmi-glib >= 1.12.4], [have_qmi=yes],[have_qmi=no]) + PKG_CHECK_MODULES(QMI, [qmi-glib >= 1.13.4], [have_qmi=yes],[have_qmi=no]) if test "x$have_qmi" = "xno"; then AC_MSG_ERROR([Couldn't find libqmi-glib. Install it, or otherwise configure using --without-qmi to disable QMI support.]) else diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c index b2644de..36fd74c 100644 --- a/src/mm-broadband-modem-qmi.c +++ b/src/mm-broadband-modem-qmi.c @@ -2587,8 +2587,8 @@ dms_set_operating_mode_ready (QmiClientDms *client, return; } - /* Good! we're done, go to last step */ - ctx->step = SET_OPERATING_MODE_STEP_LAST; + /* Good! */ + ctx->step++; set_operating_mode_context_step (ctx); } -- 2.7.4 0009-broadband-modem-mbim-try-to-use-FCC-Auth-through-QMI.patch0000664000175100017510000004365213117476072021362 0ustar uuFrom f303b399b707e9bf85bb023c854f9d43d14f9459 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Mon, 6 Jun 2016 22:15:55 +0200 Subject: [PATCH 9/9] broadband-modem-mbim: try to use FCC Auth through QMI-over-MBIM if power up fails Conflicts: configure.ac src/mm-broadband-modem-mbim.c --- configure.ac | 5 + src/mm-broadband-modem-mbim.c | 445 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 385 insertions(+), 65 deletions(-) diff --git a/configure.ac b/configure.ac index d03e2f6..8375c21 100644 --- a/configure.ac +++ b/configure.ac @@ -201,6 +201,9 @@ AM_CONDITIONAL(WITH_POLKIT, [test "x$with_polkit" != "xnone" ]) dnl dnl MBIM support (enabled by default) dnl + +LIBMBIM_VERSION=1.14.0 + AC_ARG_WITH(mbim, AS_HELP_STRING([--without-mbim], [Build without MBIM support]), [], [with_mbim=yes]) AM_CONDITIONAL(WITH_MBIM, test "x$with_mbim" = "xyes") case $with_mbim in @@ -222,6 +225,8 @@ esac dnl dnl QMI support (enabled by default) dnl + + AC_ARG_WITH(qmi, AS_HELP_STRING([--without-qmi], [Build without QMI support]), [], [with_qmi=yes]) AM_CONDITIONAL(WITH_QMI, test "x$with_qmi" = "xyes") case $with_qmi in diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c index 82f3815..f5120df 100644 --- a/src/mm-broadband-modem-mbim.c +++ b/src/mm-broadband-modem-mbim.c @@ -38,6 +38,10 @@ #include "mm-iface-modem-messaging.h" #include "mm-sms-part-3gpp.h" +#if defined WITH_QMI +# include +#endif + static void iface_modem_init (MMIfaceModem *iface); static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); static void iface_modem_messaging_init (MMIfaceModemMessaging *iface); @@ -939,43 +943,246 @@ modem_load_power_state (MMIfaceModem *self, } /*****************************************************************************/ -/* Power up/down (Modem interface) */ +/* Power up (Modem interface) */ + +typedef enum { + POWER_UP_CONTEXT_STEP_FIRST, +#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED + POWER_UP_CONTEXT_STEP_QMI_DEVICE_NEW, + POWER_UP_CONTEXT_STEP_QMI_DEVICE_OPEN, + POWER_UP_CONTEXT_STEP_ALLOCATE_QMI_CLIENT_DMS, + POWER_UP_CONTEXT_STEP_FCC_AUTH, + POWER_UP_CONTEXT_STEP_RELEASE_QMI_CLIENT_DMS, + POWER_UP_CONTEXT_STEP_RETRY, +#endif + POWER_UP_CONTEXT_STEP_LAST, +} PowerUpContextStep; + +typedef struct { + MMBroadbandModemMbim *self; + MbimDevice *device; + GSimpleAsyncResult *result; + PowerUpContextStep step; +#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED + QmiDevice *qmi_device; + QmiClient *qmi_client; + GError *saved_error; +#endif +} PowerUpContext; + +static void +power_up_context_complete_and_free (PowerUpContext *ctx) +{ +#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED + if (ctx->qmi_device) { + if (ctx->qmi_client) { + qmi_device_release_client (ctx->qmi_device, + ctx->qmi_client, + QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID, + 3, NULL, NULL, NULL); + g_object_unref (ctx->qmi_client); + } + g_object_unref (ctx->qmi_device); + } + if (ctx->saved_error) + g_error_free (ctx->saved_error); +#endif + g_simple_async_result_complete (ctx->result); + g_object_unref (ctx->result); + g_object_unref (ctx->device); + g_object_unref (ctx->self); + g_slice_free (PowerUpContext, ctx); +} static gboolean -common_power_up_down_finish (MMIfaceModem *self, - GAsyncResult *res, - GError **error) +power_up_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) { return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); } +static void power_up_context_step (PowerUpContext *ctx); + +#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED + static void -radio_state_set_down_ready (MbimDevice *device, - GAsyncResult *res, - GSimpleAsyncResult *simple) +release_qmi_client_dms_ready (QmiDevice *dev, + GAsyncResult *res, + PowerUpContext *ctx) { - MbimMessage *response; GError *error = NULL; - response = mbim_device_command_finish (device, res, &error); - if (response) - mm_mbim_helper_response_get_result (response, &error); + /* Non-fatal error */ + if (!qmi_device_release_client_finish (dev, res, &error)) { + g_debug ("error: couldn't release client: %s", error->message); + g_error_free (error); + } - if (error) - g_simple_async_result_take_error (simple, error); - else - g_simple_async_result_set_op_res_gboolean (simple, TRUE); + ctx->step++; + power_up_context_step (ctx); +} - if (response) - mbim_message_unref (response); - g_simple_async_result_complete (simple); - g_object_unref (simple); +static void +set_radio_state_release_qmi_client_dms (PowerUpContext *ctx) +{ + qmi_device_release_client (ctx->qmi_device, + ctx->qmi_client, + QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID, + 10, + NULL, + (GAsyncReadyCallback)release_qmi_client_dms_ready, + ctx); } static void -radio_state_set_up_ready (MbimDevice *device, - GAsyncResult *res, - GSimpleAsyncResult *simple) +set_fcc_authentication_ready (QmiClientDms *client, + GAsyncResult *res, + PowerUpContext *ctx) +{ + QmiMessageDmsSetFccAuthenticationOutput *output; + GError *error = NULL; + + output = qmi_client_dms_set_fcc_authentication_finish (client, res, &error); + if (!output || !qmi_message_dms_set_fcc_authentication_output_get_result (output, &error)) { + g_debug ("error: couldn't set FCC auth: %s", error->message); + g_error_free (error); + g_assert (ctx->saved_error); + g_simple_async_result_take_error (ctx->result, ctx->saved_error); + ctx->saved_error = NULL; + power_up_context_complete_and_free (ctx); + goto out; + } + + ctx->step++; + power_up_context_step (ctx); + +out: + if (output) + qmi_message_dms_set_fcc_authentication_output_unref (output); +} + +static void +set_radio_state_fcc_auth (PowerUpContext *ctx) +{ + qmi_client_dms_set_fcc_authentication (QMI_CLIENT_DMS (ctx->qmi_client), + NULL, + 10, + NULL, /* cancellable */ + (GAsyncReadyCallback)set_fcc_authentication_ready, + ctx); +} + +static void +qmi_client_dms_ready (QmiDevice *dev, + GAsyncResult *res, + PowerUpContext *ctx) +{ + GError *error = NULL; + + ctx->qmi_client = qmi_device_allocate_client_finish (dev, res, &error); + if (!ctx->qmi_client) { + g_debug ("error: couldn't create DMS client: %s", error->message); + g_error_free (error); + g_assert (ctx->saved_error); + g_simple_async_result_take_error (ctx->result, ctx->saved_error); + ctx->saved_error = NULL; + power_up_context_complete_and_free (ctx); + return; + } + + ctx->step++; + power_up_context_step (ctx); +} + +static void +set_radio_state_allocate_qmi_client_dms (PowerUpContext *ctx) +{ + g_assert (ctx->qmi_device); + qmi_device_allocate_client (ctx->qmi_device, + QMI_SERVICE_DMS, + QMI_CID_NONE, + 10, + NULL, /* cancellable */ + (GAsyncReadyCallback) qmi_client_dms_ready, + ctx); +} + +static void +device_open_ready (QmiDevice *dev, + GAsyncResult *res, + PowerUpContext *ctx) +{ + GError *error = NULL; + + if (!qmi_device_open_finish (dev, res, &error)) { + g_debug ("error: couldn't open QmiDevice: %s", error->message); + g_error_free (error); + g_assert (ctx->saved_error); + g_simple_async_result_take_error (ctx->result, ctx->saved_error); + ctx->saved_error = NULL; + power_up_context_complete_and_free (ctx); + return; + } + + ctx->step++; + power_up_context_step (ctx); +} + +static void +set_radio_state_qmi_device_open (PowerUpContext *ctx) +{ + /* Open the device */ + g_assert (ctx->qmi_device); + qmi_device_open (ctx->qmi_device, + (QMI_DEVICE_OPEN_FLAGS_PROXY | QMI_DEVICE_OPEN_FLAGS_MBIM), + 15, + NULL, /* cancellable */ + (GAsyncReadyCallback)device_open_ready, + ctx); +} + +static void +qmi_device_new_ready (GObject *unused, + GAsyncResult *res, + PowerUpContext *ctx) +{ + GError *error = NULL; + + ctx->qmi_device = qmi_device_new_finish (res, &error); + if (!ctx->qmi_device) { + g_debug ("error: couldn't create QmiDevice: %s", error->message); + g_error_free (error); + g_assert (ctx->saved_error); + g_simple_async_result_take_error (ctx->result, ctx->saved_error); + ctx->saved_error = NULL; + power_up_context_complete_and_free (ctx); + return; + } + + ctx->step++; + power_up_context_step (ctx); +} + +static void +set_radio_state_qmi_device_new (PowerUpContext *ctx) +{ + GFile *file; + + file = mbim_device_get_file (ctx->device); + qmi_device_new (file, + NULL, /* cancellable */ + (GAsyncReadyCallback) qmi_device_new_ready, + ctx); + g_object_unref (file); +} + +#endif + +static void +radio_state_set_up_ready (MbimDevice *device, + GAsyncResult *res, + PowerUpContext *ctx) { MbimMessage *response; GError *error = NULL; @@ -995,64 +1202,99 @@ radio_state_set_up_ready (MbimDevice *device, MM_CORE_ERROR_FAILED, "Cannot power-up: hardware radio switch is OFF"); else if (software_radio_state == MBIM_RADIO_SWITCH_STATE_OFF) - g_warn_if_reached (); + error = g_error_new (MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Cannot power-up: sotware radio switch is OFF"); } - if (error) - g_simple_async_result_take_error (simple, error); - else - g_simple_async_result_set_op_res_gboolean (simple, TRUE); - if (response) mbim_message_unref (response); - g_simple_async_result_complete (simple); - g_object_unref (simple); -} -static void -common_power_up_down (MMIfaceModem *self, - gboolean up, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *result; - MbimDevice *device; - MbimMessage *message; - MbimRadioSwitchState state; - GAsyncReadyCallback ready_cb; + /* Nice! we're done, quick exit */ + if (!error) { + ctx->step = POWER_UP_CONTEXT_STEP_LAST; + power_up_context_step (ctx); + return; + } - if (!peek_device (self, &device, callback, user_data)) +#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED + /* Only the first attempt isn't fatal */ + if (ctx->step == POWER_UP_CONTEXT_STEP_FIRST) { + /* Warn and keep, will retry */ + g_warning ("%s", error->message); + g_assert (!ctx->saved_error); + ctx->saved_error = error; + ctx->step++; + power_up_context_step (ctx); return; + } +#endif - result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - common_power_up_down); + /* Fatal */ + g_simple_async_result_take_error (ctx->result, error); + power_up_context_complete_and_free (ctx); +} - if (up) { - ready_cb = (GAsyncReadyCallback)radio_state_set_up_ready; - state = MBIM_RADIO_SWITCH_STATE_ON; - } else { - ready_cb = (GAsyncReadyCallback)radio_state_set_down_ready; - state = MBIM_RADIO_SWITCH_STATE_OFF; - } +static void +set_radio_state_up (PowerUpContext *ctx) +{ + MbimMessage *message; - message = mbim_message_radio_state_set_new (state, NULL); - mbim_device_command (device, + message = mbim_message_radio_state_set_new (MBIM_RADIO_SWITCH_STATE_ON, NULL); + mbim_device_command (ctx->device, message, 20, NULL, - ready_cb, - result); + (GAsyncReadyCallback)radio_state_set_up_ready, + ctx); mbim_message_unref (message); } static void -modem_power_down (MMIfaceModem *self, - GAsyncReadyCallback callback, - gpointer user_data) +power_up_context_step (PowerUpContext *ctx) { - common_power_up_down (self, FALSE, callback, user_data); + switch (ctx->step) { + case POWER_UP_CONTEXT_STEP_FIRST: + set_radio_state_up (ctx); + return; + +#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED + + case POWER_UP_CONTEXT_STEP_QMI_DEVICE_NEW: + set_radio_state_qmi_device_new (ctx); + return; + + case POWER_UP_CONTEXT_STEP_QMI_DEVICE_OPEN: + set_radio_state_qmi_device_open (ctx); + return; + + case POWER_UP_CONTEXT_STEP_ALLOCATE_QMI_CLIENT_DMS: + set_radio_state_allocate_qmi_client_dms (ctx); + return; + + case POWER_UP_CONTEXT_STEP_FCC_AUTH: + set_radio_state_fcc_auth (ctx); + return; + + case POWER_UP_CONTEXT_STEP_RELEASE_QMI_CLIENT_DMS: + set_radio_state_release_qmi_client_dms (ctx); + return; + + case POWER_UP_CONTEXT_STEP_RETRY: + set_radio_state_up (ctx); + return; + +#endif + + case POWER_UP_CONTEXT_STEP_LAST: + /* Good! */ + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + power_up_context_complete_and_free (ctx); + return; + + default: + g_assert_not_reached (); + } } static void @@ -1060,7 +1302,80 @@ modem_power_up (MMIfaceModem *self, GAsyncReadyCallback callback, gpointer user_data) { - common_power_up_down (self, TRUE, callback, user_data); + PowerUpContext *ctx; + MbimDevice *device; + + if (!peek_device (self, &device, callback, user_data)) + return; + + ctx = g_slice_new0 (PowerUpContext); + ctx->self = g_object_ref (self); + ctx->device = g_object_ref (device); + ctx->step = POWER_UP_CONTEXT_STEP_FIRST; + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + modem_power_up); + power_up_context_step (ctx); +} + +/*****************************************************************************/ +/* Power down (Modem interface) */ + +static gboolean +power_down_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void +radio_state_set_down_ready (MbimDevice *device, + GAsyncResult *res, + GSimpleAsyncResult *simple) +{ + MbimMessage *response; + GError *error = NULL; + + response = mbim_device_command_finish (device, res, &error); + if (response) { + mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error); + mbim_message_unref (response); + } + + if (error) + g_simple_async_result_take_error (simple, error); + else + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +static void +modem_power_down (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + MbimDevice *device; + MbimMessage *message; + + if (!peek_device (self, &device, callback, user_data)) + return; + + simple = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + modem_power_down); + message = mbim_message_radio_state_set_new (MBIM_RADIO_SWITCH_STATE_OFF, NULL); + mbim_device_command (device, + message, + 20, + NULL, + (GAsyncReadyCallback)radio_state_set_down_ready, + simple); + mbim_message_unref (message); } /*****************************************************************************/ @@ -2903,9 +3218,9 @@ iface_modem_init (MMIfaceModem *iface) iface->load_power_state = modem_load_power_state; iface->load_power_state_finish = modem_load_power_state_finish; iface->modem_power_up = modem_power_up; - iface->modem_power_up_finish = common_power_up_down_finish; + iface->modem_power_up_finish = power_up_finish; iface->modem_power_down = modem_power_down; - iface->modem_power_down_finish = common_power_up_down_finish; + iface->modem_power_down_finish = power_down_finish; iface->load_supported_ip_families = modem_load_supported_ip_families; iface->load_supported_ip_families_finish = modem_load_supported_ip_families_finish; -- 2.7.4