All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH] new gemalto plugin
@ 2018-10-26  6:10 Giacinto Cifelli
  2018-10-29 19:58 ` Denis Kenzior
  0 siblings, 1 reply; 16+ messages in thread
From: Giacinto Cifelli @ 2018-10-26  6:10 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 91864 bytes --]

I would like to submit to your attention the new version of the Gemalto
plugin. It is not ready, but would already benefit from some feedback.
The purpose of this new plugin is to address most of the Gemalto modules
(excluding perhaps some LTE CatM and CatNB), interfaced either via USB
or RS232 interface.

I have included the totality of file plugins/gemalto.c because it is
quite different from the current one.

I would appreciate a generic comment on how to split it in commits
this file.

I have added an include/gemalto.h file, as suggested by Jonas Bonn.
There isn't much in it yet, but some additional code should be moved into
it.

The gprs and gprs-context's are perhaps not finished.

In udevng there are 3 additional commits, please just comment if you
like and find them useful and I will submit separately.

There are some review comments, in // format, not part of the code.

---
 include/gemalto.h |   27 +
 plugins/gemalto.c | 2715 +++++++++++++++++++++++++++++++++++++++++++++
 plugins/udevng.c  |  208 ++--
 3 files changed, 2885 insertions(+), 65 deletions(-)
 create mode 100644 include/gemalto.h
 create mode 100644 plugins/gemalto.c

diff --git a/include/gemalto.h b/include/gemalto.h
new file mode 100644
index 00000000..26165eb1
--- /dev/null
+++ b/include/gemalto.h
@@ -0,0 +1,27 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2018 Gemalto M2M
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+enum auth_option {
+	GEMALTO_AUTH_DEFAULTS		= 0,
+	GEMALTO_AUTH_USE_SGAUTH		= 1<<0,
+	GEMALTO_AUTH_ORDER_PWD_USR	= 1<<1,
+	GEMALTO_AUTH_ALWAYS_ALL_PARAMS	= 1<<2,
+};
diff --git a/plugins/gemalto.c b/plugins/gemalto.c
new file mode 100644
index 00000000..6b208572
--- /dev/null
+++ b/plugins/gemalto.c
@@ -0,0 +1,2715 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2017 Vincent Cesson. All rights reserved.
+ *  Copyright (C) 2018 Gemalto M2M
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <linux/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+#include <gdbus.h>
+#include "ofono.h"
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/dbus.h>
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/netreg.h>
+#include <ofono/phonebook.h>
+#include <ofono/sim.h>
+#include <ofono/sms.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/location-reporting.h>
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+#include <string.h>
+
+#ifdef HAVE_ELL
+#include <ell/ell.h>
+#include <drivers/mbimmodem/mbim.h>
+#include <drivers/mbimmodem/mbim-message.h>
+#include <drivers/mbimmodem/mbim-desc.h>
+#endif
// some models can use MBIM, but if the option is not included,
// they fall back to PPP
+
+#include <drivers/qmimodem/qmi.h>
+#include <src/storage.h>
+#include "gemalto.h"
+
// the next part will not be in the official commit
+/* debug utilities - begin */
+
+#define REDCOLOR "\x1b\x5b\x30\x31\x3b\x33\x31\x6d"
+#define NOCOLOR "\x1b\x5b\x30\x30\x6d"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+void print_trace();
+
+void print_trace() {
+    char pid_buf[30];
+    char name_buf[512];
+    int child_pid;
+    sprintf(pid_buf, "%d", getpid());
+    name_buf[readlink("/proc/self/exe", name_buf, 511)]=0;
+    child_pid = fork();
+    if (!child_pid) {
+        dup2(2,1); // redirect output to stderr
+        fprintf(stdout,"stack trace for %s pid=%s\n",name_buf,pid_buf);
+        execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", "bt", name_buf, pid_buf, NULL);
+        abort(); /* If gdb failed to start */
+    } else {
+        waitpid(child_pid,NULL,0);
+    }
+}
+
+/* debug utilities - end */
+
+enum gemalto_connection_type {
+	GEMALTO_CONNECTION_SERIAL = 1,
+	GEMALTO_CONNECTION_USB = 2,
+};
+
+enum gemalto_device_state {
+	STATE_ABSENT = 0,
+	STATE_PROBE = 1,
+	STATE_PRESENT = 2,
+};
+
+enum gprs_option {
+	NO_GPRS = 0,
+	USE_SWWAN = 1,
+	USE_CTX17 = 2,
+	USE_CTX3 = 3,
+	USE_PPP = 4,
+	USE_SWWAN_INV = 5,	/* inverted syntax idx,act */
+	USE_CTX_INV = 6,	/* inverted syntax idx,act */
+};
+
+static const char *none_prefix[] = { NULL };
+static const char *cfun_prefix[] = { "+CFUN:", NULL };
+static const char *sctm_prefix[] = { "^SCTM:", NULL };
+static const char *sbv_prefix[] = { "^SBV:", NULL };
+static const char *sqport_prefix[] = { "^SQPORT:", NULL };
+static const char *sgpsc_prefix[] = { "^SGPSC:", NULL };
+
+typedef void (*OpenResultFunc)(gboolean success, struct ofono_modem *modem);
+
+struct gemalto_data {
+	gboolean init_done;
+	GIOChannel *channel;
+	GAtChat *tmp_chat;
+	OpenResultFunc open_cb;
+	guint read_src;
+	GAtChat *app;
+	GAtChat *mdm;
+	int cfun;
+
+	struct ofono_sim *sim;
+	gboolean have_sim;
+	struct at_util_sim_state_query *sim_state_query;
+	guint modem_ready_id;
+
+	char modelstr[32];
+	char sqport[32];
+
+	guint model;
+	guint probing_timer;
+	guint init_waiting_time;
+	guint waiting_time;
+
+	enum gemalto_connection_type conn;
+	enum gemalto_device_state mbim;
+	enum gemalto_device_state qmi;
+	enum gemalto_device_state ecmncm;
+	enum gemalto_device_state gina;
+	gboolean inverse_enum;
+	gboolean use_mdm_for_app;
+	gboolean voice_avail;
+	enum auth_option auth_syntax;
+	enum gprs_option gprs_opt;
+	gboolean has_lte;
+	gboolean autoattach;
+	gboolean autoconfig;
+	gboolean autoactivation;
+	gboolean vts_with_quotes;
+
+	void *device; /* struct mbim_device* or struct qmi_device* */
+
+	/* mbim data */
+	uint16_t max_segment;
+	uint8_t max_outstanding;
+	uint8_t max_sessions;
+
+	/* hardware monitor variables */
+	DBusMessage *hm_msg;
+	int32_t temperature;
+	int32_t voltage;
+	/* gnss variables */
+	DBusMessage *gnss_msg;
+	/* hardware control variables */
+	DBusMessage *hc_msg;
+	gboolean powersave;
+};
+
+/*******************************************************************************
+ * Generic functions
+ ******************************************************************************/
+
+static void gemalto_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	ofono_info("%s%s", prefix, str);
+}
+
+static const char *gemalto_get_string(struct ofono_modem *modem, const char *k)
+{
+	const char *v;
+
+	if (!modem || !k || !*k)
+		return NULL;
+
+	v = ofono_modem_get_string(modem, k);
+
+	if (!v || !*v)
+		return NULL;
+
+	return v;
+}
+
+static void gemalto_signal(const char *iface, const char *name,
+	const char *value, struct ofono_modem *modem)
+{
+	DBusMessageIter sub_iter,iter;
+	const char *path = ofono_modem_get_path(modem);
+	DBusConnection *conn = ofono_dbus_get_connection();
+
+	DBusMessage *signal = dbus_message_new_signal(path,
+					iface,
+					name);
+
+	DBG("");
+
+	if (signal == NULL) {
+		DBG("Cannot create new signal message");
+		return;
+	}
+
+	dbus_message_iter_init_append(signal, &iter);
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+							"s", &sub_iter);
+	if (!dbus_message_iter_append_basic(&sub_iter,
+				DBUS_TYPE_STRING, &value)) {
+		DBG("Out of memory!");
+		return;
+	}
+
+	dbus_message_iter_close_container(&iter, &sub_iter);
+	g_dbus_send_message(conn, signal);
+}
+
+static void executeWithPrompt(GAtChat *port, const char *command,
+			const char *prompt, const char *argument, void *cb,
+			void *cbd, void *freecall)
+{
+	char *buf;
+	const char *expected_array[2] = {0,0};
+
+	buf = g_strdup_printf("%s\r%s", command, argument);
+
+	if (strlen(argument)>=2 && g_str_equal(argument+strlen(argument)-2,
+									"^Z"))
+		sprintf(buf+strlen(buf)-2,"\x1a");
+
+	if (strlen(argument)>=2 && g_str_equal(argument+strlen(argument)-2,
+									"\\r"))
+		sprintf(buf+strlen(buf)-2,"\r");
+
+	expected_array[0]=prompt;
+	g_at_chat_send_and_expect_short_prompt(port, buf, expected_array,
+							cb, cbd, freecall);
+	free(buf);
+}
+
+static void gemalto_exec_stored_cmd(struct ofono_modem *modem,
+							const char *filename)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	const char *vid = gemalto_get_string(modem, "Vendor");
+	const char *pid = gemalto_get_string(modem, "Model");
+	char store[64];
+	int index;
+	char *command, *prompt, *argument;
+	char key[32];
+	GKeyFile *f;
+
+	sprintf(store,"%s-%s/%s", vid, pid, filename);
+	f = storage_open(NULL, store);
+
+	if (!f)
+		return;
+
+	for (index = 0; ; index++) {
+		sprintf(key, "command_%d", index);
+		command = g_key_file_get_string(f, "Simple", key, NULL);
+
+		if (!command)
+			break;
+
+		DBG(REDCOLOR"executing stored command simple: %s"NOCOLOR, command);
+		g_at_chat_send(data->app, command, NULL, NULL, NULL, NULL);
+	}
+
+	for (index = 0; ; index++) {
+		sprintf(key, "command_%d", index);
+		command = g_key_file_get_string(f, "WithPrompt", key, NULL);
+		sprintf(key, "prompt_%d", index);
+		prompt = g_key_file_get_string(f, "WithPrompt", key, NULL);
+		sprintf(key, "argument_%d", index);
+		argument = g_key_file_get_string(f, "WithPrompt", key, NULL);
+
+		if (!command || !prompt || !argument)
+			break;
+
+		DBG("executing stored command with prompt: %s", command);
+		executeWithPrompt(data->app, command, prompt, argument,
+			NULL, NULL, NULL);
+	}
+
+	storage_close(NULL, store, f, FALSE);
+}
+
+/*******************************************************************************
+ * Hardware monitor interface
+ ******************************************************************************/
+
+#define HARDWARE_MONITOR_INTERFACE OFONO_SERVICE ".gemalto.HardwareMonitor"
+#define CINTERION_LEGACY_HWMON_INTERFACE OFONO_SERVICE ".cinterion.HardwareMonitor"
+
+static void gemalto_sctmb_notify(GAtResult *result, gpointer user_data)
+{
+	GAtResultIter iter;
+	gint value;
+	char *val;
+
+	g_at_result_iter_init(&iter, result);
+	g_at_result_iter_next(&iter, "^SCTM_B:");
+	g_at_result_iter_next_number(&iter, &value);
+
+	switch(value) {
+	case -1:
+		val="Below low temperature alert limit";
+		break;
+	case 0:
+		val="Normal operating temperature";
+		break;
+	case 1:
+		val="Above upper temperature alert limit";
+		break;
+	case 2:
+		val="Above uppermost temperature limit";
+		break;
+	default: /* unvalid value, do not output signal*/
+		return;
+	}
+
+	gemalto_signal(HARDWARE_MONITOR_INTERFACE, "CriticalTemperature", val,
+								user_data);
+}
+
+static void gemalto_sbc_notify(GAtResult *result, gpointer user_data)
+{
+	GAtResultIter iter;
+	const char *value;
+
+	g_at_result_iter_init(&iter, result);
+	g_at_result_iter_next(&iter, "^SBC:");
+	g_at_result_iter_next_unquoted_string(&iter, &value);
+	gemalto_signal(HARDWARE_MONITOR_INTERFACE, "CriticalVoltage", value,
+								user_data);
+}
+
+static void gemalto_sctm_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct gemalto_data *data = user_data;
+	DBusMessage *reply;
+	GAtResultIter iter;
+	DBusMessageIter dbus_iter;
+	DBusMessageIter dbus_dict;
+
+	if (data->hm_msg == NULL)
+		return;
+
+	if (!ok)
+		goto error;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "^SCTM:"))
+		goto error;
+
+	if (!g_at_result_iter_skip_next(&iter))
+		goto error;
+
+	if (!g_at_result_iter_skip_next(&iter))
+		goto error;
+
+	if (!g_at_result_iter_next_number(&iter, &data->temperature))
+		goto error;
+
+	reply = dbus_message_new_method_return(data->hm_msg);
+
+	dbus_message_iter_init_append(reply, &dbus_iter);
+
+	dbus_message_iter_open_container(&dbus_iter, DBUS_TYPE_ARRAY,
+			OFONO_PROPERTIES_ARRAY_SIGNATURE,
+			&dbus_dict);
+
+	ofono_dbus_dict_append(&dbus_dict, "Temperature",
+			DBUS_TYPE_INT32, &data->temperature);
+
+	ofono_dbus_dict_append(&dbus_dict, "Voltage",
+			DBUS_TYPE_UINT32, &data->voltage);
+
+	dbus_message_iter_close_container(&dbus_iter, &dbus_dict);
+
+	__ofono_dbus_pending_reply(&data->hm_msg, reply);
+
+	return;
+
+error:
+	__ofono_dbus_pending_reply(&data->hm_msg,
+			__ofono_error_failed(data->hm_msg));
+}
+
+static void gemalto_sbv_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct gemalto_data *data = user_data;
+	GAtResultIter iter;
+
+	if (!ok)
+		goto error;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "^SBV:"))
+		goto error;
+
+	if (!g_at_result_iter_next_number(&iter, &data->voltage))
+		goto error;
+
+	if (g_at_chat_send(data->app, "AT^SCTM?", sctm_prefix, gemalto_sctm_cb,
+				data, NULL) > 0)
+		return;
+
+error:
+	__ofono_dbus_pending_reply(&data->hm_msg,
+			__ofono_error_failed(data->hm_msg));
+}
+
+static DBusMessage *hardware_monitor_get_statistics(DBusConnection *conn,
+							DBusMessage *msg,
+							void *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+
+	DBG("");
+
+	if (data->hm_msg != NULL)
+		return __ofono_error_busy(msg);
+
+	if (!g_at_chat_send(data->app, "AT^SBV", sbv_prefix, gemalto_sbv_cb,
+			data, NULL))
+		return __ofono_error_failed(msg);
+
+	data->hm_msg = dbus_message_ref(msg);
+
+	return NULL;
+}
+
+static const GDBusMethodTable hardware_monitor_methods[] = {
+	{ GDBUS_ASYNC_METHOD("GetStatistics",
+			NULL, GDBUS_ARGS({ "Statistics", "a{sv}" }),
+			hardware_monitor_get_statistics) },
+	{}
+};
+
+static const GDBusSignalTable hardware_monitor_signals[] = {
+	{ GDBUS_SIGNAL("CriticalTemperature",
+			GDBUS_ARGS({ "temperature", "a{sv}" }) )},
+	{ GDBUS_SIGNAL("CriticalVoltage",
+			GDBUS_ARGS({ "voltage", "a{sv}" }) )},
+	{}
+};
+
+static void gemalto_hardware_monitor_enable(struct ofono_modem *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = ofono_modem_get_path(modem);
+
+	/* Listen to over/undertemperature URCs (activated with AT^SCTM) */
+	g_at_chat_register(data->app, "^SCTM_B:",
+		gemalto_sctmb_notify, FALSE, NULL, NULL);
+	/* Listen to over/under voltage URCs (automatic URC) */
+	g_at_chat_register(data->app, "^SBC:",
+		gemalto_sbc_notify, FALSE, NULL, NULL);
+	/* Enable temperature URC and value output */
+	g_at_chat_send(data->app, "AT^SCTM=1,1", none_prefix, NULL, NULL, NULL);
+
+	if (!g_dbus_register_interface(conn, path, HARDWARE_MONITOR_INTERFACE,
+					hardware_monitor_methods,
+					hardware_monitor_signals,
+					NULL,
+					modem,
+					NULL)) {
+		ofono_error("Could not register %s interface under %s",
+					HARDWARE_MONITOR_INTERFACE, path);
+		return;
+	}
+
+	ofono_modem_add_interface(modem, HARDWARE_MONITOR_INTERFACE);
+
+	if (!g_dbus_register_interface(conn, path,
+					CINTERION_LEGACY_HWMON_INTERFACE,
+					hardware_monitor_methods,
+					NULL,
+					NULL,
+					modem,
+					NULL)) {
+		ofono_error("Could not register %s interface under %s",
+					CINTERION_LEGACY_HWMON_INTERFACE, path);
+		return;
+	}
+
+	ofono_modem_add_interface(modem, CINTERION_LEGACY_HWMON_INTERFACE);
+}
+
+/*******************************************************************************
+ * Time services interface
+ ******************************************************************************/
+
+#define GEMALTO_NITZ_TIME_INTERFACE OFONO_SERVICE ".gemalto.TimeServices"
+
+static DBusMessage *set_modem_datetime(DBusConnection *conn,
+							DBusMessage *msg,
+							void *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	time_t t = time(NULL);
+	struct tm tm;
+	gchar cclk_cmd[32];
+
+	/* Set date and time */
+	tm = *localtime(&t);
+	strftime(cclk_cmd, 32, "AT+CCLK=\"%y/%m/%d,%T\"", &tm);
+	g_at_chat_send(data->app, cclk_cmd, none_prefix, NULL, NULL, NULL);
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable gsmTime_methods[] = {
+	{ GDBUS_ASYNC_METHOD("SetModemDatetime",
+			NULL, NULL, set_modem_datetime) },
+	{}
+};
+
+static const GDBusSignalTable gsmTime_signals[] = {
+	{ GDBUS_SIGNAL("NitzUpdated",
+			GDBUS_ARGS({ "time", "a{sv}" }) )},
+	{}
+};
+
+static void gemalto_time_enable(struct ofono_modem *modem)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = ofono_modem_get_path(modem);
+
+	if (!g_dbus_register_interface(conn, path,
+					GEMALTO_NITZ_TIME_INTERFACE,
+					gsmTime_methods,
+					gsmTime_signals,
+					NULL,
+					modem,
+					NULL)) {
+		ofono_error("Could not register %s interface under %s",
+					GEMALTO_NITZ_TIME_INTERFACE, path);
+		return;
+	}
+
+	ofono_modem_add_interface(modem, GEMALTO_NITZ_TIME_INTERFACE);
+}
+
+/*******************************************************************************
+ * Command passtrhough interface
+ ******************************************************************************/
+
+#define COMMAND_PASSTHROUGH_INTERFACE OFONO_SERVICE ".gemalto.CommandPassthrough"
+
+static int command_passthrough_signal_answer(const char *answer,
+							gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = ofono_modem_get_path(modem);
+	DBusMessage *signal;
+	DBusMessageIter iter;
+
+	if (!conn || !path)
+		return -1;
+
+	signal = dbus_message_new_signal(path, COMMAND_PASSTHROUGH_INTERFACE,
+								"Answer");
+	if (!signal) {
+		ofono_error("Unable to allocate new %s.PropertyChanged signal",
+						COMMAND_PASSTHROUGH_INTERFACE);
+		return -1;
+	}
+
+	dbus_message_iter_init_append(signal, &iter);
+
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &answer);
+
+	DBG("");
+
+	return g_dbus_send_message(conn, signal);
+}
+
+static void command_passthrough_cb(gboolean ok, GAtResult *result,
+							gpointer user_data)
+{
+	GAtResultIter iter;
+	guint len = 0;
+	char *answer;
+
+	g_at_result_iter_init(&iter, result);
+
+	while (g_at_result_iter_next(&iter, NULL)) {
+		len += strlen(g_at_result_iter_raw_line(&iter))+2;
+	}
+
+	len += strlen(g_at_result_final_response(result))+3;
+	answer = g_new0(char, len);
+	g_at_result_iter_init(&iter, result);
+
+	while (g_at_result_iter_next(&iter, NULL)) {
+		sprintf(answer+strlen(answer),"%s\r\n",
+					g_at_result_iter_raw_line(&iter));
+	}
+
+	sprintf(answer+strlen(answer),"%s\r\n",
+					g_at_result_final_response(result));
+
+	DBG("answer_len: %u, answer_string: %s", len, answer);
+	command_passthrough_signal_answer(answer, user_data);
+
+	g_free(answer);
+}
+
+static DBusMessage *command_passthrough_simple(DBusConnection *conn,
+							DBusMessage *msg,
+							void *user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	DBusMessageIter iter;
+	const char *command;
+
+	if (!dbus_message_iter_init(msg, &iter))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"No arguments given");
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+					"Invalid argument type: '%c'",
+					dbus_message_iter_get_arg_type(&iter));
+
+	dbus_message_iter_get_basic(&iter, &command);
+
+	g_at_chat_send(data->app, command, NULL, command_passthrough_cb,
+								modem, NULL);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *command_passthrough_with_prompt(DBusConnection *conn,
+							DBusMessage *msg,
+							void *user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	DBusMessageIter iter;
+	const char *command, *prompt, *argument;
+
+	if (!dbus_message_iter_init(msg, &iter))
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+							"No arguments given");
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+					"Invalid argument type: '%c'",
+					dbus_message_iter_get_arg_type(&iter));
+
+	dbus_message_iter_get_basic(&iter, &command);
+	dbus_message_iter_next(&iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+					"Invalid argument type: '%c'",
+					dbus_message_iter_get_arg_type(&iter));
+
+	dbus_message_iter_get_basic(&iter, &prompt);
+	dbus_message_iter_next(&iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
+					"Invalid argument type: '%c'",
+					dbus_message_iter_get_arg_type(&iter));
+
+	dbus_message_iter_get_basic(&iter, &argument);
+
+	executeWithPrompt(data->app, command, prompt, argument,
+					command_passthrough_cb, modem, NULL);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *command_passthrough_send_break(DBusConnection *conn,
+							DBusMessage *msg,
+							void *user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	GIOChannel *channel = g_at_chat_get_channel(data->app);
+
+	g_io_channel_write_chars(channel, "\r", 1, NULL, NULL);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable command_passthrough_methods[] = {
+	{ GDBUS_ASYNC_METHOD("Simple",
+		GDBUS_ARGS({ "command", "s" }),
+		NULL,
+		command_passthrough_simple) },
+	{ GDBUS_ASYNC_METHOD("WithPrompt",
+		GDBUS_ARGS({ "command", "s" }, { "prompt", "s" },
+							{ "argument", "s" }),
+		NULL,
+		command_passthrough_with_prompt) },
+	{ GDBUS_ASYNC_METHOD("SendBreak",
+		NULL,
+		NULL,
+		command_passthrough_send_break) },
+	{}
+};
+
+static const GDBusSignalTable command_passthrough_signals[] = {
+	{ GDBUS_SIGNAL("Answer",
+		GDBUS_ARGS({ "answer", "s" })) },
+	{ }
+};
+
+static void gemalto_command_passthrough_enable(struct ofono_modem *modem)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = ofono_modem_get_path(modem);
+
+	/* Create Command Passthrough DBus interface */
+	if (!g_dbus_register_interface(conn, path, COMMAND_PASSTHROUGH_INTERFACE,
+					command_passthrough_methods,
+					command_passthrough_signals,
+					NULL,
+					modem,
+					NULL)) {
+		ofono_error("Could not register %s interface under %s",
+					COMMAND_PASSTHROUGH_INTERFACE, path);
+		return;
+	}
+
+	ofono_modem_add_interface(modem, COMMAND_PASSTHROUGH_INTERFACE);
+}
+
+/*******************************************************************************
+ * GNSS interface
+ ******************************************************************************/
+
+#define GNSS_INTERFACE OFONO_SERVICE ".gemalto.GNSS"
+
+static void gnss_get_properties_cb(gboolean ok, GAtResult *result,
+							gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	const char *port = ofono_modem_get_string(modem, "GNSS");
+	GAtResultIter iter;
+	DBusMessage *reply;
+	DBusMessageIter dbusiter;
+	DBusMessageIter dict;
+
+	if (data->gnss_msg == NULL)
+		return;
+
+	if (!ok)
+		goto error;
+
+	reply = dbus_message_new_method_return(data->gnss_msg);
+	dbus_message_iter_init_append(reply, &dbusiter);
+	dbus_message_iter_open_container(&dbusiter, DBUS_TYPE_ARRAY,
+					OFONO_PROPERTIES_ARRAY_SIGNATURE,
+					&dict);
+	g_at_result_iter_init(&iter, result);
+
+	/* supported format: ^SGPSC: "Nmea/Output","off" */
+	while (g_at_result_iter_next(&iter, "^SGPSC:")) {
+		const char *name = "";
+		const char *val = "";
+
+		if (!g_at_result_iter_next_string(&iter, &name))
+			continue;
+
+		/*
+		 * skip the "Info" property:
+		 * different line format and different usage
+		 */
+		if (g_str_equal(name,"Info"))
+			continue;
+
+		if (!g_at_result_iter_next_string(&iter, &val))
+			continue;
+
+		ofono_dbus_dict_append(&dict, name, DBUS_TYPE_STRING, &val);
+	}
+
+	ofono_dbus_dict_append(&dict, "Port", DBUS_TYPE_STRING, &port);
+	dbus_message_iter_close_container(&dbusiter, &dict);
+	__ofono_dbus_pending_reply(&data->gnss_msg, reply);
+	return;
+
+error:
+	__ofono_dbus_pending_reply(&data->gnss_msg,
+			__ofono_error_failed(data->gnss_msg));
+}
+
+static DBusMessage *gnss_get_properties(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+
+	if (data->gnss_msg != NULL)
+		return __ofono_error_busy(msg);
+
+	if (!g_at_chat_send(data->app, "AT^SGPSC?", sgpsc_prefix,
+					gnss_get_properties_cb, modem, NULL))
+		return __ofono_error_failed(msg);
+
+	data->gnss_msg = dbus_message_ref(msg);
+
+	return NULL;
+}
+
+static void gnss_set_properties_cb(gboolean ok, GAtResult *result,
+							gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	DBusMessage *reply;
+
+	if (data->gnss_msg == NULL)
+		return;
+
+	if (!ok) {
+		__ofono_dbus_pending_reply(&data->gnss_msg,
+					__ofono_error_failed(data->gnss_msg));
+		return;
+	}
+
+	reply = dbus_message_new_method_return(data->gnss_msg);
+	__ofono_dbus_pending_reply(&data->gnss_msg, reply);
+}
+
+static DBusMessage *gnss_set_property(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	DBusMessageIter iter, var;
+	const char *name;
+	char *value;
+	char buf[256];
+
+	if (data->gnss_msg != NULL)
+		return __ofono_error_busy(msg);
+
+	if (dbus_message_iter_init(msg, &iter) == FALSE)
+		return __ofono_error_invalid_args(msg);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return __ofono_error_invalid_args(msg);
+
+	dbus_message_iter_get_basic(&iter, &name);
+	dbus_message_iter_next(&iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+		return __ofono_error_invalid_args(msg);
+
+	dbus_message_iter_recurse(&iter, &var);
+
+	if (dbus_message_iter_get_arg_type(&var) !=
+					DBUS_TYPE_STRING)
+		return __ofono_error_invalid_args(msg);
+
+	dbus_message_iter_get_basic(&var, &value);
+
+	snprintf(buf, sizeof(buf), "AT^SGPSC=\"%s\",\"%s\"", name, value);
+
+	if (!g_at_chat_send(data->app, buf, sgpsc_prefix,
+					gnss_set_properties_cb, modem, NULL))
+		return __ofono_error_failed(msg);
+
+	data->gnss_msg = dbus_message_ref(msg);
+	return NULL;
+}
+
+static const GDBusMethodTable gnss_methods[] = {
+	{ GDBUS_ASYNC_METHOD("GetProperties",
+			NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+			gnss_get_properties) },
+	{ GDBUS_ASYNC_METHOD("SetProperty",
+			GDBUS_ARGS({ "property", "s" }, { "value", "v" }),
+			NULL, gnss_set_property) },
+	{ }
+};
+
+static void gnss_exec_stored_param(struct ofono_modem *modem,
+							const char *filename) {
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	const char *vid = ofono_modem_get_string(modem, "Vendor");
+	const char *pid = ofono_modem_get_string(modem, "Model");
+	char store[64];
+	int index;
+	char *property, *value;
+	char key[32];
+	GKeyFile *f;
+	char *command;
+
+	sprintf(store,"%s-%s/%s", vid, pid, filename);
+	f = storage_open(NULL, store);
+
+	if (!f)
+		return;
+
+	for (index=0;;index++) {
+		sprintf(key, "property_%d", index);
+		property = g_key_file_get_string(f, "Properties", key, NULL);
+
+		sprintf(key, "value_%d", index);
+		value = g_key_file_get_string(f, "Properties", key, NULL);
+
+		if(!property || !value)
+			break;
+
+		command = g_strdup_printf("AT^SGPSC=%s,%s", property, value);
+		DBG(REDCOLOR"setting GNSS property: %sNOCOLOR", command);
+		g_at_chat_send(data->app, command, NULL, NULL, NULL, NULL);
+		free(command);
+	}
+
+	storage_close(NULL, store, f, FALSE);
+}
+
+static void gemalto_gnss_enable_cb(gboolean ok, GAtResult *result,
+							gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = ofono_modem_get_path(modem);
+
+	if (!ok)
+		return; /* the module does not support GNSS */
+
+	gnss_exec_stored_param(modem, "gnss_startup");
+
+	/* Create GNSS DBus interface */
+	if (!g_dbus_register_interface(conn, path, GNSS_INTERFACE,
+					gnss_methods,
+					NULL,
+					NULL,
+					modem,
+					NULL)) {
+		ofono_error("Could not register %s interface under %s",
+					GNSS_INTERFACE, path);
+		return;
+	}
+
+	ofono_modem_add_interface(modem, GNSS_INTERFACE);
+}
+
+static void gemalto_gnss_enable(struct ofono_modem *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+
+	g_at_chat_send(data->app, "AT^SGPSC?", sgpsc_prefix,
+					gemalto_gnss_enable_cb, modem, NULL);
+}
+
+/*******************************************************************************
+ * Hardware control interface
+ ******************************************************************************/
+
+#define HARDWARE_CONTROL_INTERFACE OFONO_SERVICE ".gemalto.HardwareControl"
+
+static DBusMessage *hc_get_properties(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	DBusMessage *reply;
+	DBusMessageIter dbusiter;
+	DBusMessageIter dict;
+
+	reply = dbus_message_new_method_return(msg);
+	dbus_message_iter_init_append(reply, &dbusiter);
+	dbus_message_iter_open_container(&dbusiter, DBUS_TYPE_ARRAY,
+					OFONO_PROPERTIES_ARRAY_SIGNATURE,
+					&dict);
+
+	ofono_dbus_dict_append(&dict, "Powersave", DBUS_TYPE_BOOLEAN,
+							&data->powersave);
+	dbus_message_iter_close_container(&dbusiter, &dict);
+
+	return reply;
+}
+
+/*
+ * powersave for older modules:
+ *	command_0=AT+CFUN=7
+ * return:
+ *	command_0=AT+CFUN=1
+ *
+ * powersave example for modules with GNSS (could also only stop the output):
+ *	command_0=AT+CREG=0
+ *	command_1=AT+CGREG=0
+ *	command_2=AT+CEREG=0
+ *	command_3=AT^SGPSC="Engine","0"
+ *	command_4=AT^SGPSC="Power/Antenna","off"
+ * return:
+ *	command_0=AT+CREG=2
+ *	command_1=AT+CGREG=2
+ *	command_2=AT+CEREG=2
+ *	command_4=AT^SGPSC="Power/Antenna","on"
+ *	command_3=AT^SGPSC="Engine","1"
+ */
+
+static void gemalto_powersave_cb(gboolean ok, GAtResult *result,
+				gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	DBusMessage *reply;
+
+	/* flip the state in any case */
+	data->powersave = !data->powersave;
+
+	if (data->hc_msg == NULL)
+		return;
+
+	reply = dbus_message_new_method_return(data->hc_msg);
+	__ofono_dbus_pending_reply(&data->hc_msg, reply);
+}
+
+static DBusMessage *hc_set_property(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	DBusMessageIter iter, var;
+	const char *name;
+	gboolean enable;
+
+	if (data->hc_msg != NULL)
+		return __ofono_error_busy(msg);
+
+	if (dbus_message_iter_init(msg, &iter) == FALSE)
+		return __ofono_error_invalid_args(msg);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return __ofono_error_invalid_args(msg);
+
+	dbus_message_iter_get_basic(&iter, &name);
+
+	if (!g_str_equal(name, "Powersave"))
+		return __ofono_error_invalid_args(msg);
+
+	dbus_message_iter_next(&iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+		return __ofono_error_invalid_args(msg);
+
+	dbus_message_iter_recurse(&iter, &var);
+
+	if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+		return __ofono_error_invalid_args(msg);
+
+	dbus_message_iter_get_basic(&var, &enable);
+
+	if (data->powersave == enable)
+		return dbus_message_new_method_return(msg);
+
+	gemalto_exec_stored_cmd(modem, enable ? "power_mode_powersave" :
+							"power_mode_normal");
+
+	gnss_exec_stored_param(modem, enable ? "gnss_powersave" :
+								"gnss_normal");
+
+	if (!g_at_chat_send(data->app, "AT", none_prefix,
+				gemalto_powersave_cb, modem, NULL))
+		return __ofono_error_failed(msg);
+
+	data->hc_msg = dbus_message_ref(msg);
+	return NULL;
+}
+
+static void gemalto_smso_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	DBusMessage *reply;
+
+	if (data->hc_msg == NULL)
+		return;
+
+	if (data->conn != GEMALTO_CONNECTION_SERIAL)
+		goto finished;
+
+	if (data->mdm)
+		g_at_chat_unref(data->mdm);
+	data->mdm = NULL;
+
+	if (data->app)
+		g_at_chat_unref(data->app);
+	data->app = NULL;
+
+	if (ok)
+		ofono_modem_set_powered(modem, FALSE);
+
+finished:
+	reply = dbus_message_new_method_return(data->hc_msg);
+	__ofono_dbus_pending_reply(&data->hc_msg, reply);
+}
+
+static DBusMessage *hc_shutdown(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+
+	if (data->hc_msg != NULL)
+		return __ofono_error_busy(msg);
+
+	if (!g_at_chat_send(data->app, "AT^SMSO", none_prefix,
+						gemalto_smso_cb, modem, NULL))
+		return __ofono_error_failed(msg);
+
+	data->hc_msg = dbus_message_ref(msg);
+	return NULL;
+}
+
+static void gemalto_detect_sysstart(GAtResult *result, gpointer user_data);
+
+static void gemalto_reset_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	DBusMessage *reply;
+
+	if (data->hc_msg == NULL)
+		return;
+
+	if (data->conn != GEMALTO_CONNECTION_SERIAL)
+		goto finished;
+
+	data->modem_ready_id = g_at_chat_register(data->app,
+		"^SYSSTART", gemalto_detect_sysstart, FALSE,
+		modem, NULL);
+
+finished:
+	reply = dbus_message_new_method_return(data->hc_msg);
+	__ofono_dbus_pending_reply(&data->hc_msg, reply);
+}
+
+static DBusMessage *hc_reset(DBusConnection *conn,
+					DBusMessage *msg, void *user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+
+	if (data->hc_msg != NULL)
+		return __ofono_error_busy(msg);
+
+	if (!g_at_chat_send(data->app, "AT+CFUN=1,1", none_prefix,
+						gemalto_reset_cb, modem, NULL))
+		return __ofono_error_failed(msg);
+
+	data->hc_msg = dbus_message_ref(msg);
+	return NULL;
+}
+
+static const GDBusMethodTable hardware_control_methods[] = {
+	{ GDBUS_ASYNC_METHOD("GetProperties",
+			NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+			hc_get_properties) },
+	{ GDBUS_ASYNC_METHOD("SetProperty",
+			GDBUS_ARGS({ "property", "s" }, { "value", "v" }),
+			NULL, hc_set_property) },
+	{ GDBUS_ASYNC_METHOD("Shutdown",
+			NULL, NULL, hc_shutdown) },
+	{ GDBUS_ASYNC_METHOD("Reset",
+			NULL, NULL, hc_reset) },
+	{ }
+};
+
+static void gemalto_hardware_control_enable(struct ofono_modem *modem)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = ofono_modem_get_path(modem);
+
+	/* Create Hardware Control DBus interface */
+	if (!g_dbus_register_interface(conn, path, HARDWARE_CONTROL_INTERFACE,
+					hardware_control_methods,
+					NULL,
+					NULL,
+					modem,
+					NULL)) {
+		ofono_error("Could not register %s interface under %s",
+					HARDWARE_CONTROL_INTERFACE, path);
+		return;
+	}
+
+	ofono_modem_add_interface(modem, HARDWARE_CONTROL_INTERFACE);
+}
+
+/*******************************************************************************
+ * modem plugin
+ ******************************************************************************/
+
+#ifdef HAVE_ELL
+static int mbim_parse_descriptors(struct gemalto_data *md, const char *file)
+{
+	void *data;
+	size_t len;
+	const struct mbim_desc *desc = NULL;
+	const struct mbim_extended_desc *ext_desc = NULL;
+
+	data = l_file_get_contents(file, &len);
+	if (!data)
+		return -EIO;
+
+	if (!mbim_find_descriptors(data, len, &desc, &ext_desc)) {
+		l_free(data);
+		return -ENOENT;
+	}
+
+	if (desc)
+		md->max_segment = L_LE16_TO_CPU(desc->wMaxControlMessage);
+
+	if (ext_desc)
+		md->max_outstanding = ext_desc->bMaxOutstandingCommandMessages;
+
+	l_free(data);
+	return 0;
+}
+
+static int mbim_probe(struct ofono_modem *modem, struct gemalto_data *data)
+{
+	const char *descriptors;
+	int err;
+
+	descriptors = gemalto_get_string(modem, "DescriptorFile");
+
+	if (!descriptors)
+		return -EINVAL;
+
+	data->max_outstanding = 1;
+
+	err = mbim_parse_descriptors(data, descriptors);
+	if (err < 0) {
+		DBG("Warning, unable to load descriptors, setting defaults");
+		data->max_segment = 512;
+	}
+
+	DBG("MaxSegment: %d, MaxOutstanding: %d",
+		data->max_segment, data->max_outstanding);
+
+	return 0;
+}
+#endif
+
+static int gemalto_probe(struct ofono_modem *modem)
+{
+	struct gemalto_data *data;
+
+	data = g_try_new0(struct gemalto_data, 1);
+	if (data == NULL)
+		return -ENOMEM;
+
+#ifdef HAVE_ELL
+	mbim_probe(modem, data);
+#endif
+
+	ofono_modem_set_data(modem, data);
+
+	return 0;
+}
+
+static void gemalto_remove(struct ofono_modem *modem)
+{
+	struct gemalto_data *data;
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path;
+
+	if (!modem)
+		return;
+
+	data = ofono_modem_get_data(modem);
+	path = ofono_modem_get_path(modem);
+
+	if (!data)
+		return;
+
+#ifdef HAVE_ELL
+	if (data->mbim == STATE_PRESENT) {
+		mbim_device_shutdown(data->device);
+	}
+#endif
+
+	if (data->qmi == STATE_PRESENT) {
+		qmi_device_unref(data->device);
+	}
+
+	if (data->app) {
+		/* Cleanup potential SIM state polling */
+		at_util_sim_state_query_free(data->sim_state_query);
+		data->sim_state_query = NULL;
+
+		g_at_chat_cancel_all(data->app);
+		g_at_chat_unregister_all(data->app);
+		g_at_chat_unref(data->app);
+		data->app = NULL;
+	}
+
+	if (data->mdm) {
+		g_at_chat_cancel_all(data->app);
+		g_at_chat_unregister_all(data->mdm);
+		g_at_chat_unref(data->mdm);
+		data->mdm = NULL;
+	}
+
+	if (conn && path) {
+		if (g_dbus_unregister_interface(conn, path,
+					HARDWARE_MONITOR_INTERFACE))
+			ofono_modem_remove_interface(modem,
+					HARDWARE_MONITOR_INTERFACE);
+
+		if (g_dbus_unregister_interface(conn, path,
+					CINTERION_LEGACY_HWMON_INTERFACE))
+			ofono_modem_remove_interface(modem,
+					CINTERION_LEGACY_HWMON_INTERFACE);
+
+		if (g_dbus_unregister_interface(conn, path,
+					GEMALTO_NITZ_TIME_INTERFACE))
+			ofono_modem_remove_interface(modem,
+					GEMALTO_NITZ_TIME_INTERFACE);
+
+		if (g_dbus_unregister_interface(conn, path,
+					COMMAND_PASSTHROUGH_INTERFACE))
+			ofono_modem_remove_interface(modem,
+					COMMAND_PASSTHROUGH_INTERFACE);
+
+		if (g_dbus_unregister_interface(conn, path,
+					GNSS_INTERFACE))
+			ofono_modem_remove_interface(modem,
+					GNSS_INTERFACE);
+
+		if (g_dbus_unregister_interface(conn, path,
+					HARDWARE_CONTROL_INTERFACE))
+			ofono_modem_remove_interface(modem,
+					HARDWARE_CONTROL_INTERFACE);
+	}
+
+	//if (data->conn == GEMALTO_CONNECTION_SERIAL)
+	//	return;
+
+	ofono_modem_set_data(modem, NULL);
+	g_free(data);
+}
+
+static void sim_ready_cb(gboolean present, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	struct ofono_sim *sim = data->sim;
+
+	at_util_sim_state_query_free(data->sim_state_query);
+	data->sim_state_query = NULL;
+
+	DBG("sim present: %d", present);
+
+	ofono_sim_inserted_notify(sim, present);
+}
+
+static void gemalto_ciev_simstatus_notify(GAtResultIter *iter,
+					struct ofono_modem *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	struct ofono_sim *sim = data->sim;
+	int status;
+
+	DBG("sim status %d", status);
+
+	if (!g_at_result_iter_next_number(iter, &status))
+		return;
+
+	switch (status) {
+	/* SIM is removed from the holder */
+	case 0:
+		ofono_sim_inserted_notify(sim, FALSE);
+		break;
+
+	/* SIM is inserted inside the holder */
+	case 1:
+		/* The SIM won't be ready yet */
+		data->sim_state_query = at_util_sim_state_query_new(data->app,
+					1, 20, sim_ready_cb, modem,
+					NULL);
+		break;
+
+	/* USIM initialization completed. UE has finished reading USIM data. */
+	case 5:
+		ofono_sim_initialized_notify(sim);
+		break;
+
+	default:
+		break;
+	}
+}
+
+static void gemalto_ciev_nitz_notify(GAtResultIter *iter,
+					struct ofono_modem *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	const char *nitz_data;
+	char buf[32];
+
+	/* Example: +CIEV: nitz,<time>,<timezone>,<daylight> */
+	if (!g_at_result_iter_next_string(iter, &nitz_data))
+		return;
+
+	DBG("nitz_data  %s", nitz_data);
+
+	sprintf(buf, "AT+CCLK=\"%s\"", nitz_data);
+	g_at_chat_send(data->app, buf, none_prefix, NULL, NULL, NULL);
+
+	gemalto_signal(GEMALTO_NITZ_TIME_INTERFACE, "NitzUpdated", nitz_data,
+									modem);
+}
+
+static void gemalto_ciev_notify(GAtResult *result, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+
+	const char *sim_status = "simstatus";
+	const char *nitz_status = "nitz";
+	const char *ind_str;
+	GAtResultIter iter;
+
+	g_at_result_iter_init(&iter, result);
+
+	/* Example: +CIEV: simstatus,<status> */
+	if (!g_at_result_iter_next(&iter, "+CIEV:"))
+		return;
+
+	if (!g_at_result_iter_next_unquoted_string(&iter, &ind_str))
+		return;
+
+	if (g_str_equal(sim_status, ind_str)) {
+		gemalto_ciev_simstatus_notify(&iter, modem);
+	} else if (g_str_equal(nitz_status, ind_str)) {
+		gemalto_ciev_nitz_notify(&iter, modem);
+	}
+}
+
+static void sim_state_cb(gboolean present, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+
+	at_util_sim_state_query_free(data->sim_state_query);
+	data->sim_state_query = NULL;
+
+	data->have_sim = present;
+	ofono_modem_set_powered(modem, TRUE);
+
+	/* Register for specific sim status reports */
+	g_at_chat_register(data->app, "+CIEV:",
+			gemalto_ciev_notify, FALSE, modem, NULL);
+
+	g_at_chat_send(data->app, "AT^SIND=\"simstatus\",1", none_prefix,
+			NULL, NULL, NULL);
+	g_at_chat_send(data->app, "AT^SIND=\"nitz\",1", none_prefix,
+			NULL, NULL, NULL);
+}
+
+static void gemalto_exit_urc_notify(GAtResult *result, gpointer user_data)
+{
+	GAtResultIter iter;
+	const char *error_message;
+
+	g_at_result_iter_init(&iter, result);
+	g_at_result_iter_next(&iter, "^EXIT:");
+	g_at_result_iter_next_unquoted_string(&iter, &error_message);
+	ofono_error("Modem exited! Cause: %s", error_message);
+}
+
+static void saic_probe(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct gemalto_data *data = ofono_modem_get_data(user_data);
+
+	if (ok)
+		data->voice_avail = TRUE;
+	else
+		data->voice_avail = FALSE;
+}
+
+static void sgauth_probe(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct gemalto_data *data = ofono_modem_get_data(user_data);
+
+	if (ok)
+		data->auth_syntax = GEMALTO_AUTH_USE_SGAUTH |
+						GEMALTO_AUTH_ORDER_PWD_USR;
+	else
+		data->auth_syntax = GEMALTO_AUTH_DEFAULTS;
+}
+
+static void gemalto_set_cfun_cb(gboolean ok, GAtResult *result,
+					gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+
+	if (!ok || data->cfun == 41) {
+		g_at_chat_cancel_all(data->app);
+		ofono_modem_set_powered(modem, FALSE);
+	} else {
+		data->sim_state_query = at_util_sim_state_query_new(data->app,
+					2, 20, sim_state_cb, modem, NULL);
+	}
+}
+
+static void gemalto_cfun_query(gboolean ok, GAtResult *result,
+							gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(user_data);
+	char buf[256];
+	GAtResultIter iter;
+	int mode;
+
+	sprintf(buf, "AT+CFUN=%d", data->cfun==41?4:data->cfun);
+
+	if (!ok)
+		goto error;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+CFUN:"))
+		goto error;
+
+	if (!g_at_result_iter_next_number(&iter, &mode))
+		goto error;
+
+	if (mode == data->cfun)
+		sprintf(buf, "AT");
+
+error:
+	if (g_at_chat_send(data->app, buf, none_prefix, gemalto_set_cfun_cb,
+				modem, NULL) > 0)
+		return;
+
+	if (data->cfun == 41)
+		ofono_modem_set_powered(modem, FALSE);
+}
+
+static void gemalto_set_cfun(GAtChat *app, int mode, struct ofono_modem *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+
+	data->cfun=mode;
+	g_at_chat_send(app, "AT+CFUN?", cfun_prefix, gemalto_cfun_query, modem, NULL);
+}
+
+static void gemalto_initialize(struct ofono_modem *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	char *urcdest;
+	guint m = data->model;
+
+	DBG("app:%d, mdm:%d, mbim:%d, qmi:%d",
+		data->app!=NULL,
+		data->mdm!=NULL,
+		data->mbim == STATE_PRESENT,
+		data->qmi == STATE_PRESENT);
+
+	if (!data->app  && !data->mdm) {
+		DBG("no AT interface available. Removing this device.");
+		ofono_modem_set_powered(modem, FALSE);
+		return;
+	}
+
+	urcdest = "AT^SCFG=\"URC/DstIfc\",\"app\"";
+
+	if (!data->app) {
+		data->use_mdm_for_app = TRUE;
+		data->app = data->mdm;
+		urcdest = "AT^SCFG=\"URC/DstIfc\",\"mdm\"";
+	}
+
+	if (!data->mdm && (data->gina == STATE_PRESENT)) {
+		/*these modems can start PPP from any port*/
+		data->mdm = data->app;
+	}
+
+	if (data->mdm && data->gprs_opt == NO_GPRS)
+		data->gprs_opt = USE_PPP;
+
+	g_at_chat_set_wakeup_command(data->app, "AT\r", 1000, 5000);
+
+	g_at_chat_send(data->app, "ATE0", none_prefix, NULL, NULL, NULL);
+
+	if (data->gina != STATE_PRESENT)
+		g_at_chat_send(data->app, urcdest, none_prefix, NULL, NULL,
+									NULL);
+
+	/* numeric error codes are interpreted by atmodem/atutil.c functions */
+	g_at_chat_send(data->app, "AT+CMEE=1", none_prefix, NULL, NULL, NULL);
+
+	if (data->mdm)
+		g_at_chat_send(data->mdm, "AT&C0", none_prefix, NULL, NULL,
+									NULL);
+
+	g_at_chat_send(data->app, "AT&C0", none_prefix, NULL, NULL, NULL);
+
+	/* watchdog */
+	g_at_chat_register(data->app, "^EXIT", gemalto_exit_urc_notify, FALSE,
+								modem, NULL);
+	ofono_devinfo_create(modem, OFONO_VENDOR_GEMALTO, "atmodem", data->app);
+	g_at_chat_send(data->app,
+		"AT^SCFG=\"MEopMode/PwrSave\",\"enabled\",52,50", none_prefix,
+							NULL, NULL, NULL);
// this command was in some gemalto.c versions, but it would better be
// placed in one of the configuration files
+
+	if (m != 0x5b && m != 0x5c && m != 0x5d && m != 0xa0) {
+		g_at_chat_send(data->app, "AT^SGAUTH?", NULL, sgauth_probe,
+								modem, NULL);
+	}
+
+	g_at_chat_send(data->app, "AT^SAIC?", NULL, saic_probe, modem, NULL);
+
+	gemalto_exec_stored_cmd(modem, "enable");
+
+	gemalto_command_passthrough_enable(modem);
+	gemalto_hardware_monitor_enable(modem);
+	gemalto_time_enable(modem);
+	gemalto_gnss_enable(modem);
+	gemalto_hardware_control_enable(modem);
+
+	gemalto_set_cfun(data->app, 4, modem);
+	data->init_done = TRUE;
+}
+
+static gboolean gemalto_open_cb(GIOChannel *source, GIOCondition condition,
+							gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	GAtSyntax *syntax;
+
+	if (data->channel == NULL)
+		return TRUE;
+
+	if ((condition & G_IO_IN) == 0)
+		return TRUE;
+
+	g_source_remove(data->probing_timer);
+	data->probing_timer = 0;
+	g_source_remove(data->read_src);
+	g_io_channel_flush(data->channel, NULL);
+	/* reset channel defaults*/
+	g_io_channel_set_buffered(data->channel, TRUE);
+	g_io_channel_set_encoding(data->channel, "UTF-8", NULL);
+
+	syntax = g_at_syntax_new_gsm_permissive();
+	data->tmp_chat = g_at_chat_new(data->channel, syntax);
+	g_at_syntax_unref(syntax);
+
+	if (data->tmp_chat == NULL)
+		goto failed;
+
+	g_io_channel_unref(data->channel);
+	data->channel = NULL;
+	g_at_chat_set_debug(data->tmp_chat, gemalto_debug, "App: ");
+	data->open_cb(TRUE, modem);
+	return TRUE; // finished
+failed:
+	DBG("chat creation failed. aborting.");
+	g_io_channel_unref(data->channel);
+	data->channel = NULL;
+	DBG("aborted.");
+	data->open_cb(FALSE, modem);
+	return FALSE; // abort
+}
+
+static int gemalto_probe_device(void *user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	GIOStatus status;
+
+	if (data->channel==NULL)
+		return FALSE;
+
+	data->waiting_time++;
+	DBG("%d/%d", data->waiting_time, data->init_waiting_time);
+
+	if (data->waiting_time > data->init_waiting_time) {
+		data->waiting_time = 0;
+		goto failed;
+	}
+
+	status = g_io_channel_write_chars(data->channel, "AT\r", 3, NULL, NULL);
+
+	if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN)
+		goto failed;
+
+	return TRUE; // to be called again
+
+failed:
+	g_source_remove(data->probing_timer);
+	data->probing_timer = 0; /* remove the timer reference */
+	DBG("timeout: abort");
+	g_io_channel_unref(data->channel);
+	data->channel = NULL;
+	data->tmp_chat = NULL;
+	data->open_cb(FALSE, modem);
+	return FALSE; // abort
+}
+
+#include <asm/ioctls.h>
+#include <linux/serial.h>
+
+int ioctl(int, int, void *);
// these are temporarily in the middle. Discussed with Denis,
// maybe these and their use below is better placed in g_at_tty_open
+
+static void gemalto_open_device(const char *device,
+				OpenResultFunc func, struct ofono_modem *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	GHashTable *options;
+	int fd;
+	struct serial_struct old, new;
+
+	if (!device  || !*device) {
+		func(FALSE, modem);
+		return;
+	}
+
+	options = g_hash_table_new(g_str_hash, g_str_equal);
+	if (options == NULL) {
+		func(FALSE, modem);
+		return;
+	}
+
+	g_hash_table_insert(options, "Baud", "115200");
+	g_hash_table_insert(options, "StopBits", "1");
+	g_hash_table_insert(options, "DataBits", "8");
+	g_hash_table_insert(options, "Parity", "none");
+	g_hash_table_insert(options, "XonXoff", "off");
+	g_hash_table_insert(options, "RtsCts", "on");
+	g_hash_table_insert(options, "Local", "on");
+	g_hash_table_insert(options, "Read", "on");
+
+	DBG("Opening device %s", device);
+
+	data->channel = g_at_tty_open(device, options);
+	g_hash_table_destroy(options);
+
+	if (!data->channel) {
+		func(FALSE, modem);
+		return;
+	}
+
+	fd = g_io_channel_unix_get_fd(data->channel);
+	ioctl(fd, TIOCGSERIAL, &old);
+	new = old;
+	new.closing_wait = ASYNC_CLOSING_WAIT_NONE;
+	ioctl(fd, TIOCSSERIAL, &new);
// without this, even if the g_at_tty_open sets the port non-blocking,
// it waits anyway 30 seconds if the port is not responding.
+
+	g_io_channel_flush(data->channel, NULL);
+	/* the channel is set by default to "UTF-8" and buffered */
+	g_io_channel_set_encoding(data->channel, NULL, NULL);
+	g_io_channel_set_buffered(data->channel, FALSE);
+	data->open_cb = func;
+	data->read_src = g_io_add_watch(data->channel, G_IO_IN, gemalto_open_cb,
+									modem);
+	data->probing_timer = g_timeout_add_seconds(1, gemalto_probe_device,
+									modem);
+}
// the 3 functions above:
// - open the channel as usual
// - test the port with AT once per second until timeout. Maybe this can be an
//   additional wakeup function in GAtChat
// - attach a GAtChat to it or close it
+
+static void gemalto_enable_mdm_cb(gboolean success, struct ofono_modem *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+
+	data->mdm = data->tmp_chat;
+	data->tmp_chat = NULL;
+	gemalto_initialize(modem);
+}
+
+static void gemalto_enable_app_cb(gboolean success, struct ofono_modem *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	const char *mdm = gemalto_get_string(modem, "Modem");
+
+	data->app = data->tmp_chat;
+	data->tmp_chat = NULL;
+	gemalto_open_device(mdm, gemalto_enable_mdm_cb, modem);
+}
+
+static int gemalto_enable_app(struct ofono_modem *modem)
+{
+	const char *app = gemalto_get_string(modem, "Application");
+
+	gemalto_open_device(app, gemalto_enable_app_cb, modem);
+	return -EINPROGRESS;
+}
+
+#ifdef HAVE_ELL
+static void mbim_device_caps_info_cb(struct mbim_message *message, void *user)
+{
+	struct ofono_modem *modem = user;
+	struct gemalto_data *md = ofono_modem_get_data(modem);
+	uint32_t device_type;
+	uint32_t cellular_class;
+	uint32_t voice_class;
+	uint32_t sim_class;
+	uint32_t data_class;
+	uint32_t sms_caps;
+	uint32_t control_caps;
+	uint32_t max_sessions;
+	char *custom_data_class;
+	char *device_id;
+	char *firmware_info;
+	char *hardware_info;
+	bool r;
+
+	if (mbim_message_get_error(message) != 0)
+		goto error;
+
+	r = mbim_message_get_arguments(message, "uuuuuuuussss",
+					&device_type, &cellular_class,
+					&voice_class, &sim_class, &data_class,
+					&sms_caps, &control_caps, &max_sessions,
+					&custom_data_class, &device_id,
+					&firmware_info, &hardware_info);
+	if (!r)
+		goto error;
+
+	md->max_sessions = max_sessions;
+
+	DBG("DeviceId: %s", device_id);
+	DBG("FirmwareInfo: %s", firmware_info);
+	DBG("HardwareInfo: %s", hardware_info);
+
+	ofono_modem_set_string(modem, "DeviceId", device_id);
+	ofono_modem_set_string(modem, "FirmwareInfo", firmware_info);
+
+	l_free(custom_data_class);
+	l_free(device_id);
+	l_free(firmware_info);
+	l_free(hardware_info);
+
+	message = mbim_message_new(mbim_uuid_basic_connect,
+					MBIM_CID_DEVICE_SERVICE_SUBSCRIBE_LIST,
+					MBIM_COMMAND_TYPE_SET);
+
+	mbim_message_set_arguments(message, "av", 2,
+					"16yuuuuuuu",
+					mbim_uuid_basic_connect, 6,
+					MBIM_CID_SUBSCRIBER_READY_STATUS,
+					MBIM_CID_RADIO_STATE,
+					MBIM_CID_REGISTER_STATE,
+					MBIM_CID_PACKET_SERVICE,
+					MBIM_CID_SIGNAL_STATE,
+					MBIM_CID_CONNECT,
+					"16yuuuu", mbim_uuid_sms, 3,
+					MBIM_CID_SMS_CONFIGURATION,
+					MBIM_CID_SMS_READ,
+					MBIM_CID_SMS_MESSAGE_STORE_STATUS);
+
+	if (mbim_device_send(md->device, 0, message,
+				NULL, NULL, NULL)) {
+		md->mbim = STATE_PRESENT;
+		goto other_devices;
+	}
+
+
+error:
+	mbim_device_shutdown(md->device);
+
+other_devices:
+
+	if (md->init_done)
+		return;
+
+	gemalto_enable_app(modem);  /* continue with mdm interface */
+}
+
+static void mbim_device_ready(void *user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *md = ofono_modem_get_data(modem);
+	struct mbim_message *message =
+		mbim_message_new(mbim_uuid_basic_connect,
+					1, MBIM_COMMAND_TYPE_QUERY);
+
+	mbim_message_set_arguments(message, "");
+	mbim_device_send(md->device, 0, message, mbim_device_caps_info_cb,
+		modem, NULL);
+}
+
+static void mbim_device_closed(void *user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *md = ofono_modem_get_data(modem);
+
+	if (!md)
+		return;
+
+	/*
+	 * if state=probe, it  means that we are in the init phase
+	 * and that we have failed the MBIM_OPEN
+	 */
+	if (md->mbim == STATE_PROBE) {
+		DBG(REDCOLOR"MBIM OPEN failed!"NOCOLOR);
+		gemalto_enable_app(modem); /* continue with other interfaces */
+	}
+
+	/* reset the state for future attempts */
+	md->mbim = STATE_PROBE;
+
+ 	if(md->device)
+		mbim_device_unref(md->device);
+
+	md->device = NULL;
+}
+
+static int mbim_enable(struct ofono_modem *modem)
+{
+	const char *device;
+	int fd;
+	struct gemalto_data *md = ofono_modem_get_data(modem);
+
+	DBG("modem struct: %p", modem);
+
+	device = gemalto_get_string(modem, "NetworkControl");
+	if (!device)
+		goto other_devices;
+
+	DBG("modem device: %s", device);
+	fd = open(device, O_EXCL | O_NONBLOCK | O_RDWR);
+
+	if (fd < 0)
+		goto other_devices;
+
+	DBG("device: %s opened successfully", device);
+	md->device = mbim_device_new(fd, md->max_segment);
+	DBG("created new device %p", md->device);
+
+	mbim_device_set_close_on_unref(md->device, true);
+	mbim_device_set_max_outstanding(md->device, md->max_outstanding);
+	mbim_device_set_ready_handler(md->device,
+					mbim_device_ready, modem, NULL);
+	mbim_device_set_disconnect_handler(md->device,
+				mbim_device_closed, modem, NULL);
+	mbim_device_set_debug(md->device, gemalto_debug, "MBIM:", NULL);
+
+	return -EINPROGRESS;
+
+other_devices:
+
+	if (md->init_done)
+		return 0;
+
+	return gemalto_enable_app(modem);
+}
+#endif
+
+static void qmi_enable_cb(void *user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *md = ofono_modem_get_data(modem);
+	md->qmi = STATE_PRESENT;
+	gemalto_enable_app(modem); /* qmi done, continue with app interface */
+}
+
+static int qmi_enable(struct ofono_modem *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	const char *device;
+	int fd;
+
+	DBG("modem struct: %p", modem);
+
+	device = gemalto_get_string(modem, "NetworkControl");
+	if (!device)
+		return gemalto_enable_app(modem);
+
+	fd = open(device, O_RDWR | O_NONBLOCK | O_CLOEXEC);
+	if (fd < 0)
+		return gemalto_enable_app(modem);
+
+	data->device = qmi_device_new(fd);
+	if (!data->device) {
+		close(fd);
+		return gemalto_enable_app(modem);
+	}
+
+	qmi_device_set_close_on_unref(data->device, true);
+
+	qmi_device_set_debug(data->device, gemalto_debug, "QMI: ");
+
+	qmi_device_discover(data->device, qmi_enable_cb, modem, NULL);
+
+	return -EINPROGRESS;
+}
+
+static void set_from_model(struct gemalto_data *data) {
+	guint m = data->model;
+
+	data->has_lte = TRUE; /* default */
+
+	/* pre-configure non-MBIM network interfaces */
+	if (m != 0x62 && m != 0x5d && m != 0x65) {
+		/*
+		 * note: we probe for ECM/NCM even if the port is not present
+		 * (for serial connection type or serial-like)
+		 */
+		if (m == 0x53 || m == 0x60 || m == 0x63)
+			data->qmi = STATE_PROBE;
+		/*these families have PPP only*/
+		else if (m != 0x58 && m != 0x47 && m != 0x54)
+			data->ecmncm = STATE_PROBE;
+	}
+
+	/* pre-configure SW features */
+	if (m == 0xa0) {
+		data->gprs_opt = USE_CTX3;
+		data->ecmncm = STATE_ABSENT;
+	}
+	if (m == 0x63 || m == 0x65 || m == 0x5b || m == 0x5c || m == 0x5d)
+		data->gina = STATE_PRESENT;
+
+	data->init_waiting_time = 30;
+
+	if (m == 0x55 || m == 0x47) {
+		data->has_lte = FALSE;
+		data->init_waiting_time = 5;
+	}
+
+	if (m == 0x58) {
+		data->has_lte = FALSE;
+		data->init_waiting_time = 15;
+	}
+
+	data->vts_with_quotes = TRUE;
+
+	if (m == 0x5b || m == 0x5c || m == 0x5d || m == 0xa0) {
+		data->vts_with_quotes = FALSE;
+		data->auth_syntax = GEMALTO_AUTH_USE_SGAUTH |
+						GEMALTO_AUTH_ALWAYS_ALL_PARAMS;
+	}
+}
+
+static void store_cgmm(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	GAtResultIter iter;
+	char const *model;
+	char buf[16];
+
+	/* if no model, fallback to a basic 2G one */
+	data->model = 0x47;
+	strncpy(data->modelstr, "", sizeof(data->modelstr));
+
+	if (!ok)
+		return;
+
+	g_at_result_iter_init(&iter, result);
+
+	while (g_at_result_iter_next(&iter, NULL)) {
+		if (!g_at_result_iter_next_unquoted_string(&iter, &model))
+			continue;
+
+		if (model && *model) {
+			strncpy(data->modelstr, model, sizeof(data->modelstr));
+
+			if (g_ascii_strncasecmp(model, "TC", 2) == 0)
+				data->model = 0x47;
+			else if (g_ascii_strncasecmp(model, "MC", 2) == 0)
+				data->model = 0x47;
+			else if (g_ascii_strncasecmp(model, "AC", 2) == 0)
+				data->model = 0x47;
+			else if (g_ascii_strncasecmp(model, "HC", 2) == 0)
+				data->model = 0x47;
+			else if (g_ascii_strncasecmp(model, "HM", 2) == 0)
+				data->model = 0x47;
+			else if (g_ascii_strncasecmp(model, "XT", 2) == 0)
+				data->model = 0x47;
+			else if (g_ascii_strncasecmp(model, "AGS", 3) == 0)
+				data->model = 0x47;
+			else if (g_ascii_strncasecmp(model, "BGS", 3) == 0)
+				data->model = 0x47;
+			else if (g_ascii_strncasecmp(model, "AH3", 3) == 0)
+				data->model = 0x55;
+			else if (g_ascii_strncasecmp(model, "AHS", 3) == 0)
+				data->model = 0x55;
+			else if (g_ascii_strncasecmp(model, "PHS", 3) == 0)
+				data->model = 0x55;
+			else if (g_ascii_strncasecmp(model, "PH8", 3) == 0)
+				data->model = 0x55;
+			else if (g_ascii_strncasecmp(model, "AHS", 3) == 0)
+				data->model = 0x55;
+			else if (g_ascii_strncasecmp(model, "EHS", 3) == 0)
+				data->model = 0x58;
+			else if (g_ascii_strncasecmp(model, "ELS31-", 6) == 0)
+				data->model = 0xa0;
+			else if (g_ascii_strncasecmp(model, "ELS61-", 6) == 0)
+				data->model = 0x5b;
+			else if (g_ascii_strncasecmp(model, "PLS62-", 6) == 0)
+				data->model = 0x5b;
+			else if (g_ascii_strncasecmp(model, "PLS8-", 5) == 0)
+				data->model = 0x61;
+			else if (g_ascii_strncasecmp(model, "ALS3-", 5) == 0)
+				data->model = 0x61;
+			else if (g_ascii_strncasecmp(model, "ALAS5-", 6) == 0)
+				data->model = 0x65;
+			return;
+		}
+	}
+
+	sprintf(buf, "%04x", data->model);
+	ofono_modem_set_string(modem, "Model", buf);
+}
+
+static void store_sqport(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	GAtResultIter iter;
+	char const *sqport;
+
+	/* in case of error, the port is of Modem type */
+	strncpy(data->sqport, "Modem", sizeof(data->sqport));
+
+	if (!ok)
+		goto done;
+
+	g_at_result_iter_init(&iter, result);
+
+	/* answer format: "^SQPORT: Application" */
+	if (!g_at_result_iter_next(&iter, "^SQPORT:"))
+		goto done;
+
+	if (!g_at_result_iter_next_unquoted_string(&iter, &sqport))
+		goto done;
+
+	if (!sqport || !*sqport)
+		goto done;
+
+	strncpy(data->sqport, sqport, sizeof(data->sqport));
+
+done:
+	/* select mdm, app or gina port type */
+	data->ecmncm = STATE_PROBE;
+
+	if (g_str_equal(sqport, "Modem")) {
+		data->mdm = data->app;
+		data->app = NULL;
+		data->ecmncm = STATE_ABSENT;
+	}
+
+	if ((*sqport >= '0' && *sqport <= '9'))
+		data->gina = STATE_PRESENT;
+
+	set_from_model(data);
+	gemalto_initialize(modem);
+}
+
+static void gemalto_detect_serial(gboolean success, struct ofono_modem *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+
+	data->app = data->tmp_chat;
+	data->tmp_chat = NULL;
+
+	g_at_chat_send(data->app, "AT+CGMM", none_prefix, store_cgmm,
+								modem, NULL);
+	g_at_chat_send(data->app, "AT^SQPORT", sqport_prefix, store_sqport,
+								modem, NULL);
+}
+
+static void gemalto_detect_sysstart(GAtResult *result, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+
+	if (data->modem_ready_id) {
+		g_at_chat_unregister(data->app, data->modem_ready_id);
+		data->modem_ready_id = 0;
+	}
+
+	data->tmp_chat = data->app;
+	gemalto_detect_serial(TRUE, modem);
+}
+
+static int gemalto_enable_serial(struct ofono_modem *modem)
+{
+	const char *device = ofono_modem_get_string(modem, "ATport");
+
+	if (!device)
+		return -EINVAL;
+
+	gemalto_open_device(device, gemalto_detect_serial, modem);
+	return -EINPROGRESS;
+}
+
+static int gemalto_enable(struct ofono_modem *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	const char *model = gemalto_get_string(modem, "Model"),
+		   *conn_type = gemalto_get_string(modem, "ConnType");
+	const char
+		*ctl = gemalto_get_string(modem, "NetworkControl"),
+		*net = gemalto_get_string(modem, "NetworkInterface");
+	guint m = 0;
+
+	if (!modem || !data)
+		return -EINVAL;
+
+	data->conn = g_str_equal(conn_type,"Serial") ? GEMALTO_CONNECTION_SERIAL
+						: GEMALTO_CONNECTION_USB;
+
+	if (data->conn == GEMALTO_CONNECTION_SERIAL)
+		return gemalto_enable_serial(modem);
+
+	DBG("modem struct: %p, gemalto_data: %p", modem, data);
+
+	if (data->init_done) {
+		gemalto_set_cfun(data->app, 4, modem);
+
+#ifdef HAVE_ELL
+		if (data->mbim != STATE_ABSENT)
+			mbim_enable(modem);
+#endif
+
+		return -EINPROGRESS;
+	}
+
+	if (model) {
+		data->model = strtoul(model, NULL, 16);
+		m = data->model;
+	}
+
+	/* single ACM interface 02: assign application to modem */
+	if (m == 0xa0) {
+		const char *app = gemalto_get_string(modem, "Application");
+		ofono_modem_set_string(modem, "Modem", app);
+	}
+
+	if (m == 0x60) {
+		const char *app = gemalto_get_string(modem, "Diag");
+		ofono_modem_set_string(modem, "Modem", app);
+	}
+
+	/* if single ACM interface, remove possible extra devices */
+	if (m == 0x58 || m == 0x47 || m == 0x54 || m == 0xa0 || m == 0x60) {
+		ofono_modem_set_string(modem, "Application", NULL);
+		ofono_modem_set_string(modem, "GNSS", NULL);
+		ofono_modem_set_string(modem, "RSA", NULL);
+		ofono_modem_set_string(modem, "Diag", NULL);
+	}
+
+#ifdef HAVE_ELL
+	/* pre-configure MBIM network interface */
+	if (m == 0x62 || m == 0x5d || m == 0x65) {
+		data->mbim = STATE_PROBE;
+	}
+#endif
+
+	set_from_model(data);
+
+#ifdef HAVE_ELL
+	if ((data->mbim == STATE_PROBE) && ctl && net) {
+		data->init_waiting_time = 3;
+		return mbim_enable(modem);
+	}
+#endif
+
+	if ((data->qmi == STATE_PROBE) && ctl && net) {
+		data->init_waiting_time = 10;
+		return qmi_enable(modem);
+	}
+
+	return gemalto_enable_app(modem);
+}
+
+#ifdef HAVE_ELL
+static int mbim_sim_probe(void *device)
+{
+	struct mbim_message *message;
+	/* SIM_GROUP is defined in mbimmodem.h that cannot be included */
+	uint32_t SIM_GROUP = 1;
+
+	message = mbim_message_new(mbim_uuid_basic_connect,
+					MBIM_CID_SUBSCRIBER_READY_STATUS,
+					MBIM_COMMAND_TYPE_QUERY);
+	if (!message)
+		return -ENOMEM;
+
+	mbim_message_set_arguments(message, "");
+
+	if (!mbim_device_send(device, SIM_GROUP, message,
+				NULL, NULL, NULL)) {
+		mbim_message_unref(message);
+		return -EIO;
+	}
+	return 0;
+}
+#endif
+
+static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	ofono_modem_online_cb_t cb = cbd->cb;
+	struct ofono_error error;
+
+	decode_at_error(&error, g_at_result_final_response(result));
+
+	cb(&error, cbd->data);
+}
+
+static void gemalto_set_online_serial(struct ofono_modem *modem,
+			ofono_bool_t online, ofono_modem_online_cb_t cb,
+			void *user_data)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	struct cb_data *cbd = cb_data_new(cb, user_data);
+	char const *command;
+
+	gemalto_exec_stored_cmd(modem, "set_online");
+
+	if (data->model == 0x47) {
+		command = online ? "AT^SCFG=\"MEopMode/Airplane\",\"off\"" :
+					"AT^SCFG=\"MEopMode/Airplane\",\"on\"";
+	} else {
+		command = online ? "AT+CFUN=1" : "AT+CFUN=4";
+	}
+
+	DBG("modem %p %s", modem, online ? "online" : "offline");
+
+	if (g_at_chat_send(data->app, command, NULL, set_online_cb, cbd,
+									g_free))
+		return;
+
+	CALLBACK_WITH_FAILURE(cb, cbd->data);
+	g_free(cbd);
+}
+
+static void gemalto_set_online(struct ofono_modem *modem, ofono_bool_t online,
+				ofono_modem_online_cb_t cb, void *user_data)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	struct cb_data *cbd = cb_data_new(cb, user_data);
+	char const *cmd = online ? "AT+CFUN=1" : "AT+CFUN=4";
+
+	if (data->conn == GEMALTO_CONNECTION_SERIAL) {
+		gemalto_set_online_serial(modem, online, cb, user_data);
+		return;
+	}
+
+	DBG("modem %p %s", modem, online ? "online" : "offline");
+
+	if (g_at_chat_send(data->app, cmd, NULL, set_online_cb, cbd, g_free))
+		return;
+
+	CALLBACK_WITH_FAILURE(cb, cbd->data);
+
+	g_free(cbd);
+}
+
+static void gemalto_pre_sim(struct ofono_modem *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	gemalto_exec_stored_cmd(modem, "pre_sim");
+
+	ofono_location_reporting_create(modem, 0, "gemaltomodem", data->app);
+
+	data->sim = ofono_sim_create(modem, OFONO_VENDOR_GEMALTO,
+		"atmodem", data->app);
+
+	if (data->sim && data->have_sim == TRUE)
+		ofono_sim_inserted_notify(data->sim, TRUE);
+}
+
+static void gemalto_post_sim(struct ofono_modem *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+
+	gemalto_exec_stored_cmd(modem, "post_sim");
+
+#ifdef HAVE_ELL
+	if (data->mbim == STATE_PRESENT) {
+		/* very important to set the interface ready */
+		mbim_sim_probe(data->device);
+	}
+#endif
+
+	ofono_phonebook_create(modem, 0, "atmodem", data->app);
+	ofono_modem_set_integer(modem, "GemaltoAuthType", data->auth_syntax);
+
+	if (data->has_lte)
+		ofono_lte_create(modem, 0, "gemaltomodem", data->app);
+}
+
+static void cgdcont17_probe(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct gemalto_data *data = ofono_modem_get_data(user_data);
+
+	if (ok)
+		data->gprs_opt = USE_CTX17;
+}
+
+static void swwan_probe(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct gemalto_data *data = ofono_modem_get_data(user_data);
+
+	if (ok)
+		data->gprs_opt = USE_SWWAN;
+}
+
+static void autoattach_probe_and_continue(gboolean ok, GAtResult *result,
+							gpointer user_data)
+{
+	struct ofono_modem* modem = user_data;
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+	GAtResultIter iter;
+	struct ofono_message_waiting *mw;
+	struct ofono_gprs *gprs = NULL;
+	struct ofono_gprs_context *gc = NULL;
+
+	data->autoattach = FALSE;
+	ofono_modem_set_integer(modem, "GemaltoAutoAttach", 0);
+
+	if (ok) {
+		g_at_result_iter_init(&iter, result);
+		while (g_at_result_iter_next(&iter, NULL)) {
+			if (strstr(g_at_result_iter_raw_line(&iter),
+					"\"enabled\"")) {
+				data->autoattach = TRUE;
+				ofono_modem_set_integer(modem,
+					"GemaltoAutoAttach", 1);
+
+			}
+		}
+	}
+
+	// TODO: the ofono_gprs_create may require gemaltomodem instead of atmodem
// for now this code works with a modified version of atmodem/gprs.c,
// where the ofono_gprs_set_cid_range is not called, so it can be called below
+
+#ifdef HAVE_ELL
+	if (data->mbim == STATE_PRESENT) {
+		gprs = ofono_gprs_create(modem, OFONO_VENDOR_GEMALTO, "atmodem",
+								data->app);
+		ofono_gprs_set_cid_range(gprs, 0, data->max_sessions);
+		if (data->model == 0x62 || data->model == 0x65) {
+			struct gemalto_mbim_composite comp;
+			comp.device = data->device;
+			comp.chat = data->app;
+			comp.at_cid = 4;
+			gc = ofono_gprs_context_create(modem, 0, "gemaltomodemmbim", &comp);
+		} else /* model == 0x5d */
+			gc = ofono_gprs_context_create(modem, 0, "mbim", data->device);
+	} else
+#endif
+		if (data->qmi == STATE_PRESENT) {
+		gprs = ofono_gprs_create(modem, OFONO_VENDOR_GEMALTO, "atmodem",
+								data->app);
+		// TODO: verify if need be to create the contexts and auth params beforehand
+		// if so, may need a gprs-context gemaltomodem-at-qmi
+
+		/* on QMI devices, only a single context is supported */
+		// TODO: Is 1 ok for attach_APN != context_APN?
+		ofono_gprs_set_cid_range(gprs, 1, 1);
+		gc = ofono_gprs_context_create(modem, 0, "qmimodem",
+								data->device);
+	} else if (data->gprs_opt == USE_SWWAN || data->gprs_opt == USE_CTX17 ||
+						data->gprs_opt == USE_CTX3) {
+		ofono_modem_set_integer(modem, "GemaltoWwan",
+						data->gprs_opt == USE_SWWAN);
+		gprs = ofono_gprs_create(modem, OFONO_VENDOR_GEMALTO, "atmodem",
+								data->app);
+		if (data->gprs_opt == USE_CTX3)
+			ofono_gprs_set_cid_range(gprs, 3, 3);
+		else if (data->model == 0x5b)
+			/*
+			 * limitation: same APN as for attach
+			 * in this case create more contexts
+			 */
+			ofono_gprs_set_cid_range(gprs, 1, 11);
+		else
+			ofono_gprs_set_cid_range(gprs, 4, 16);
+		// maybe rename the next to gemaltomodem-wwan
+		gc = ofono_gprs_context_create(modem, 0, "gemaltomodemswwan",
+								data->app);
+	} else if (data->gprs_opt == USE_PPP) {
+		/* plain PPP only works from mdm ports */
+		gprs = ofono_gprs_create(modem, OFONO_VENDOR_GEMALTO, "atmodem",
+								data->app);
+		if (data->model == 0x47)
+			ofono_gprs_set_cid_range(gprs, 1, 2);
+		else if (data->has_lte)
+			ofono_gprs_set_cid_range(gprs, 4, 16);
+		else
+			ofono_gprs_set_cid_range(gprs, 1, 16);
+
+		gc = ofono_gprs_context_create(modem, 0, "atmodem", data->mdm);
+
+	} /*
+	   * in case of no match above, we have no gprs possibilities
+	   * this is common when using the module through serial interfaces
+	   * nevertheless other services (voice, gpio, gnss) could be available
+	   */
+
+	if (gc)
+		ofono_gprs_context_set_type(gc,
+					OFONO_GPRS_CONTEXT_TYPE_INTERNET);
+
+	if (gprs && gc)
+		ofono_gprs_add_context(gprs, gc);
+
+	/* might have also without voicecall support  */
+	ofono_ussd_create(modem, 0, "atmodem", data->app);
+
+	/*
+	 * Call support is technically possible only after sim insertion
+	 * with the module online. However the EMERGENCY_SETUP procedure of
+	 * the 3GPP TS_24.008 is triggered by the same AT command,
+	 * and namely 'ATD112;', 'ATD911;', etc.
+	 * On the other hand, in airplane-mode it is not possible to do it, nor
+	 * to create all relevant URCs for the atom.
+	 *
+	 * Ofono does not make a distinction between no-sim and
+	 * airplane-mode scenarios, so we create the voicecall in post-online.
+	 * This is half-compatible with the European directives that require
+	 * a SIM inserted also for emergency setup.
+	 */
// this needs further thinking
+
+	if (data->voice_avail) {
+		ofono_modem_set_integer(modem, "GemaltoVtsQuotes",
+						data->vts_with_quotes);
+		ofono_voicecall_create(modem, 0, "gemaltomodem", data->app);
+
+		ofono_call_forwarding_create(modem, 0, "atmodem", data->app);
+		ofono_call_settings_create(modem, 0, "atmodem", data->app);
+		ofono_call_meter_create(modem, 0, "atmodem", data->app);
+		ofono_call_barring_create(modem, 0, "atmodem", data->app);
+	}
+
+	/* modules require to be online to accept at+cnmi */
+	ofono_sms_create(modem, OFONO_VENDOR_GEMALTO, "atmodem", data->app);
+	mw = ofono_message_waiting_create(modem);
+
+	if (mw)
+		ofono_message_waiting_register(mw);
+
+	ofono_netreg_create(modem, OFONO_VENDOR_GEMALTO, "atmodem", data->app);
+}
+
+static int gemalto_post_online_delayed(void *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+
+	/*
+	 * check module capabilities once online and SIM really ready.
+	 *
+	 * Note: the g_at_chat_send calls only insert the commands in a list:
+	 * they are not executed synchronously
+	 *
+	 * Note: ofono executes each AT commands and the related callback before
+	 * proceeding with the next. So continuing on the last AT command is all
+	 * it takes
+	 */
+
+	gemalto_exec_stored_cmd(modem, "post_online");
+
+	if (data->ecmncm == STATE_PROBE) {
+		data->gprs_opt = USE_PPP; /* fallback */
+		g_at_chat_send(data->app, "AT+CGDCONT=17", NULL,
+						cgdcont17_probe, modem, NULL);
+		g_at_chat_send(data->app, "AT^SWWAN?", NULL, swwan_probe, modem,
+									NULL);
+	}
+
+	g_at_chat_send(data->app, "AT^SCFG=\"GPRS/AutoAttach\"", NULL,
+				autoattach_probe_and_continue, modem, NULL);
+
+	return FALSE; /* to kill the timer */
+}
+
+static void gemalto_post_online(struct ofono_modem *modem)
+{
+	/*
+	 * in this version of ofono we must wait for SIM 'really-ready'
+	 * can be avoided when capturing the right URCs
// waiting for the URC (like '+PBREADY') is anyway not risk-free
// because if ofono is started when the module is already initialized
// the URC is missing, and on some models cannot be queried.
// This needs further thinking.
+	 */
+	g_timeout_add_seconds(5, gemalto_post_online_delayed, modem);
+}
+
+#ifdef HAVE_ELL
+static void mbim_radio_off_for_disable(struct mbim_message *message, void *user)
+{
+	struct ofono_modem *modem = user;
+	struct gemalto_data *md = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	mbim_device_shutdown(md->device);
+}
+#endif
+
+static int gemalto_disable_serial(struct ofono_modem *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+
+	if (data->app != NULL) {
+		if (data->model == 0x47) {
+			g_at_chat_send(data->app,
+				"AT^SCFG=\"MEopMode/Airplane\",\"on\"",
+				NULL, NULL, NULL, NULL);
+		} else {
+			gemalto_set_cfun(data->app, 41, modem);
+			return -EINPROGRESS;
+		}
+		g_at_chat_cancel_all(data->app);
+	}
+
+	ofono_modem_set_powered(modem, FALSE);
+	return 0;
+}
+
+static int gemalto_disable(struct ofono_modem *modem)
+{
+	struct gemalto_data *data = ofono_modem_get_data(modem);
+#ifdef HAVE_ELL
+	struct mbim_message *message;
+#endif
+
+	DBG("%p", modem);
+
+	if (data->conn == GEMALTO_CONNECTION_SERIAL)
+		return gemalto_disable_serial(modem);
+
+#ifdef HAVE_ELL
+	if (data->mbim == STATE_PRESENT) {
+		message = mbim_message_new(mbim_uuid_basic_connect,
+						MBIM_CID_RADIO_STATE,
+						MBIM_COMMAND_TYPE_SET);
+		mbim_message_set_arguments(message, "u", 0);
+
+		if (mbim_device_send(data->device, 0, message,
+				mbim_radio_off_for_disable, modem, NULL)==0)
+			mbim_device_closed(modem);
+	}
+#endif
+
+	if (data->app == NULL)
+		return 0;
+
+	gemalto_exec_stored_cmd(modem, "disable");
+
+	gemalto_set_cfun(data->app, 41, modem);
+
+	return -EINPROGRESS;
+}
+
+static struct ofono_modem_driver gemalto_driver = {
+	.name		= "gemalto",
+	.probe		= gemalto_probe,
+	.remove		= gemalto_remove,
+	.enable		= gemalto_enable,
+	.disable	= gemalto_disable,
+	.set_online	= gemalto_set_online,
+	.pre_sim	= gemalto_pre_sim,
+	.post_sim	= gemalto_post_sim,
+	.post_online	= gemalto_post_online,
+};
+
+static int gemalto_init(void)
+{
+	return ofono_modem_driver_register(&gemalto_driver);
+}
+
+static void gemalto_exit(void)
+{
+	ofono_modem_driver_unregister(&gemalto_driver);
+}
+
+OFONO_PLUGIN_DEFINE(gemalto, "Gemalto modem plugin", VERSION,
+		OFONO_PLUGIN_PRIORITY_DEFAULT, gemalto_init, gemalto_exit)
diff --git a/plugins/udevng.c b/plugins/udevng.c
index 3c39e681..6c82eb81 100644
--- a/plugins/udevng.c
+++ b/plugins/udevng.c
@@ -3,6 +3,7 @@
  *  oFono - Open Source Telephony
  *
  *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2018 Gemalto M2M
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -979,20 +980,6 @@ static gboolean setup_serial_modem(struct modem_info* modem)
 	return TRUE;
 }
 
-static gboolean setup_tc65(struct modem_info* modem)
-{
-	ofono_modem_set_driver(modem->modem, "cinterion");
-
-	return setup_serial_modem(modem);
-}
-
-static gboolean setup_ehs6(struct modem_info* modem)
-{
-	ofono_modem_set_driver(modem->modem, "cinterion");
-
-	return setup_serial_modem(modem);
-}
-
 static gboolean setup_ifx(struct modem_info* modem)
 {
 	struct serial_device_info* info;
@@ -1121,60 +1108,133 @@ static gboolean setup_ublox(struct modem_info *modem)
 
 static gboolean setup_gemalto(struct modem_info* modem)
 {
-	const char *app = NULL, *gps = NULL, *mdm = NULL,
-		*net = NULL, *qmi = NULL;
+	const char *mdm = NULL, *app = NULL, *gnss = NULL, *rsa = NULL,
+		*diag = NULL,
+		*ctl = NULL, *net = NULL, *net2 = NULL;
+	char descriptors[PATH_MAX];
+	guint m;
+	const char *prevNet = NULL;
 
 	GSList *list;
 
-	DBG("%s", modem->syspath);
+	DBG("type:%d, syspath:%s [%s:%s]", modem->type, modem->syspath,
+						modem->vendor, modem->model);
+
+	if (modem->type==MODEM_TYPE_SERIAL) {
+		struct serial_device_info* info = modem->serial;
+		ofono_modem_set_string(modem->modem, "ConnType", "Serial");
+		ofono_modem_set_string(modem->modem, "ATport", info->devnode);
+		ofono_modem_set_string(modem->modem, "Vendor", "gemalto");
+		ofono_modem_set_string(modem->modem, "Model", "0000");
+		return TRUE;
+
+	}
+
+	m = strtoul(modem->model, NULL, 16);
 
+	/* map devices */
 	for (list = modem->devices; list; list = list->next) {
 		struct device_info *info = list->data;
 
-		DBG("%s %s %s %s %s", info->devnode, info->interface,
-				info->number, info->label, info->subsystem);
-
-		/* PHS8-P */
-		if (g_strcmp0(info->interface, "255/255/255") == 0) {
-			if (g_strcmp0(info->number, "01") == 0)
-				gps = info->devnode;
-			else if (g_strcmp0(info->number, "02") == 0)
-				app = info->devnode;
-			else if (g_strcmp0(info->number, "03") == 0)
-				mdm = info->devnode;
-			else if (g_strcmp0(info->subsystem, "net") == 0)
-				net = info->devnode;
-			else if (g_strcmp0(info->subsystem, "usbmisc") == 0)
-				qmi = info->devnode;
+		DBG("node:%s, sub:%s, interface:%s, number:%s, sysattr:%s",
+			info->devnode, info->subsystem, info->interface,
+			info->number, info->sysattr);
+
+		if (g_str_equal(info->subsystem,"tty")) {
+			/* option devices (ttyUSBx) */
+			/* note: option devices order in VMs under windows may vary */
+			if (g_str_equal(info->interface, "255/255/255") ||
+					g_str_equal(info->interface, "255/66/1") ||
+					g_str_equal(info->interface, "255/0/0")) {
+				if (g_str_equal(info->number, "00"))	{
+					app = info->devnode;
+				} else if (g_str_equal(info->number, "01")) {
+					gnss = rsa = info->devnode;
+				} else if (g_str_equal(info->number, "02")) {
+					diag = info->devnode;
+				} else if (g_str_equal(info->number, "03")) {
+					mdm = info->devnode;
+				}
+			/* cdc-acm devices (ttyACMx) */
+			} else if (g_str_equal(info->interface,"2/2/0") ||
+					g_str_equal(info->interface,"2/2/1")) {
+				if (g_str_equal(info->number, "00")) {
+					mdm = info->devnode;
+				} else if (g_str_equal(info->number, "02")) {
+					app = info->devnode;
+				} else if (g_str_equal(info->number, "04")) {
+					gnss = info->devnode;
+				} else if (g_str_equal(info->number, "06")) {
+					rsa = info->devnode;
+				} else if (g_str_equal(info->number, "08")) {
+					diag = info->devnode;
+				}
+			}
 		}
 
-		/* Cinterion ALS3, PLS8-E, PLS8-X */
-		if (g_strcmp0(info->interface, "2/2/1") == 0) {
-			if (g_strcmp0(info->number, "00") == 0)
-				mdm = info->devnode;
-			else if (g_strcmp0(info->number, "02") == 0)
-				app = info->devnode;
-			else if (g_strcmp0(info->number, "04") == 0)
-				gps = info->devnode;
-		}
-		if (g_strcmp0(info->interface, "2/6/0") == 0) {
-			if (g_strcmp0(info->subsystem, "net") == 0)
-				net = info->devnode;
+		if (g_str_equal(info->subsystem,"usbmisc"))
+			/* control device for qmi/mbim:  /dev/cdc-wdmX */
+			ctl = info->devnode;
+
+		/* network interfaces */
+		if (g_str_equal(info->subsystem,"net")) {
+			/* cdc-mbim */
+			if (g_str_equal(info->interface,"2/14/0") ||
+				  /* qmi-wwan */
+				  g_str_equal(info->interface,"255/255/255")) {
+				net = info->devnode; /* wwanX */
+			/* cdc-ecm */
+			} else if (g_str_equal(info->interface, "2/6/0") ||
+				  /* cdc-ncm */
+				  g_str_equal(info->interface, "2/13/0")) {
+				 /*
+				  * there could be 2 interfaces in this case,
+				  * and need to have them sorted to be sure to
+				  * report the right interface to activate
+				  */
+				if (!prevNet ||
+					  g_strcmp0(info->number,prevNet)<0) {
+					prevNet = info->number;
+					net2 = net; /* can be NULL, ok */
+					net = info->devnode; /* usbX */
+				} else { // invert
+					net2 = info->devnode; /* usbX */
// not sure this logic of sorting the network interfaces is needed
// but I don't see another way of being sure of having them in order
+				}
+			}
 		}
 	}
 
-	DBG("application=%s gps=%s modem=%s network=%s qmi=%s",
-			app, gps, mdm, net, qmi);
+	sprintf(descriptors, "%s/descriptors", modem->syspath);
+	ofono_modem_set_string(modem->modem, "ConnType", "Usb");
+	ofono_modem_set_string(modem->modem, "DescriptorFile", descriptors);
+	ofono_modem_set_string(modem->modem, "Vendor", modem->vendor);
+	ofono_modem_set_string(modem->modem, "Model", modem->model);
 
-	if (app == NULL || mdm == NULL)
-		return FALSE;
+	/*
+	 * special cases.
+	 * TODO: selected by external parameters:
+	 *   GEMALTO_CONFIG_MBIM_ONLY -> _set_driver(modem->modem, "mbim");
+	 *   GEMALTO_CONFIG_QMI_ONLY -> _set_driver(modem->modem, "gobi");
+	 *   ...
+	 */
+	if (m==0x64) {
+		ofono_modem_set_string(modem->modem, "Device", ctl);
+		ofono_modem_set_string(modem->modem, "NetworkInterface", net);
+		ofono_modem_set_driver(modem->modem, "mbim");
+		return TRUE;
+	}
 
-	ofono_modem_set_string(modem->modem, "Application", app);
-	ofono_modem_set_string(modem->modem, "GPS", gps);
 	ofono_modem_set_string(modem->modem, "Modem", mdm);
-	ofono_modem_set_string(modem->modem, "Device", qmi);
-	ofono_modem_set_string(modem->modem, "Model", modem->model);
+	ofono_modem_set_string(modem->modem, "Application", app);
+	ofono_modem_set_string(modem->modem, "GNSS", gnss);
+	ofono_modem_set_string(modem->modem, "RSA", rsa);
+	ofono_modem_set_string(modem->modem, "Diag", diag);
+
+	ofono_modem_set_string(modem->modem, "NetworkControl", ctl);
 	ofono_modem_set_string(modem->modem, "NetworkInterface", net);
+	ofono_modem_set_string(modem->modem, "NetworkInterface2", net2);
+
+	ofono_modem_set_powered_timeout_hint(modem->modem, 60);
 
 	return TRUE;
 }
@@ -1290,7 +1350,7 @@ static struct {
 	{ "quectel",	setup_quectel	},
 	{ "quectelqmi",	setup_quectelqmi},
 	{ "ublox",	setup_ublox	},
-	{ "gemalto",	setup_gemalto	},
+	{ "gemalto",	setup_gemalto,	"device/interface"	},
 	{ "xmm7xxx",	setup_xmm7xxx	},
 	{ "mbim",	setup_mbim	},
 	/* Following are non-USB modems */
@@ -1298,12 +1358,12 @@ static struct {
 	{ "u8500",	setup_isi_serial	},
 	{ "n900",	setup_isi_serial	},
 	{ "calypso",	setup_serial_modem	},
-	{ "cinterion",	setup_serial_modem	},
+	{ "cinterion",	setup_gemalto },
 	{ "nokiacdma",	setup_serial_modem	},
 	{ "sim900",	setup_serial_modem	},
 	{ "wavecom",	setup_wavecom		},
-	{ "tc65",	setup_tc65		},
-	{ "ehs6",	setup_ehs6		},
+	{ "tc65",	setup_gemalto },
+	{ "ehs6",	setup_gemalto },
 	{ }
 };
 
@@ -1464,11 +1524,10 @@ static void add_serial_device(struct udev_device *dev)
 	struct udev_device* mdev;
 	const char* driver;
 
+	/* discard if the device doesn't have the property OFONO_DRIVER */
 	mdev = get_serial_modem_device(dev);
-	if (!mdev) {
-		DBG("Device is missing required OFONO_DRIVER property");
+	if (!mdev)
// separate commit to remove this polluting message. On some systems
// it can emit the line 256 times.
// By default a typical linux distro presents 64 ttySx ports, so even if a
// few are used, there are too many unneeded notifications.
 		return;
-	}
 
 	driver = udev_device_get_property_value(mdev, "OFONO_DRIVER");
 
@@ -1674,11 +1733,13 @@ static struct {
 	{ "ublox",	"cdc_acm",	"1546", "1102"	},
 	{ "ublox",	"rndis_host",	"1546", "1146"	},
 	{ "ublox",	"cdc_acm",	"1546", "1146"	},
-	{ "gemalto",	"option",	"1e2d",	"0053"	},
-	{ "gemalto",	"cdc_wdm",	"1e2d",	"0053"	},
-	{ "gemalto",	"qmi_wwan",	"1e2d",	"0053"	},
-	{ "gemalto",	"cdc_acm",	"1e2d",	"0061"	},
-	{ "gemalto",	"cdc_ether",	"1e2d",	"0061"	},
+	{ "gemalto",	"cdc_acm",	"1e2d"		},
+	{ "gemalto",	"option",	"1e2d"		},
+	{ "gemalto",	"cdc_wdm",	"1e2d"		},
+	{ "gemalto",	"qmi_wwan",	"1e2d"		},
+	{ "gemalto",	"cdc_ether",	"1e2d",		},
+	{ "gemalto",	"cdc_mbim",	"1e2d",		},
+	{ "gemalto",	"cdc_ncm",	"1e2d",		},
 	{ "telit",	"cdc_ncm",	"1bc7", "0036"	},
 	{ "telit",	"cdc_acm",	"1bc7", "0036"	},
 	{ "xmm7xxx",	"cdc_acm",	"8087"		},
@@ -1708,6 +1769,7 @@ static void check_usb_device(struct udev_device *device)
 	vendor = udev_device_get_property_value(usb_device, "ID_VENDOR_ID");
 	model = udev_device_get_property_value(usb_device, "ID_MODEL_ID");
 
+	/* for serial2usb-like devices, enum as usb/usb_device */
// separate commit or discard
 	driver = udev_device_get_property_value(usb_device, "OFONO_DRIVER");
 	if (!driver) {
 		struct udev_device *usb_interface =
@@ -1719,7 +1781,17 @@ static void check_usb_device(struct udev_device *device)
 					usb_interface, "OFONO_DRIVER");
 	}
 
-	if (driver == NULL) {
+	if (!driver)
+	{
+		/* for serial2usb devices (enum as tty/generic) */
+		const char *serialdriver =
+			udev_device_get_property_value(device, "OFONO_DRIVER");
+		DBG("vId=%s, pId=%s, driver=%s", vendor, model, serialdriver);
+		if (serialdriver)
+			add_serial_device(device);
+	}
// separate commit to match the devices connected through an FDMI chip
+
+	if (!driver) { /* use linux driver */
 		const char *drv;
 		unsigned int i;
 
@@ -1772,6 +1844,7 @@ static void check_usb_device(struct udev_device *device)
 static void check_device(struct udev_device *device)
 {
 	const char *bus;
+	const char *ofono_ignore_device;
 
 	bus = udev_device_get_property_value(device, "ID_BUS");
 	if (bus == NULL) {
@@ -1780,6 +1853,11 @@ static void check_device(struct udev_device *device)
 			return;
 	}
 
+	ofono_ignore_device = udev_device_get_property_value(device,
+							"OFONO_IGNORE_DEVICE");
+		if (ofono_ignore_device)
+			return; /* skip this device */
+
// separate commit to ignore some devices
 	if ((g_str_equal(bus, "usb") == TRUE) ||
 			(g_str_equal(bus, "usbmisc") == TRUE))
 		check_usb_device(device);
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] new gemalto plugin
  2018-10-26  6:10 [RFC PATCH] new gemalto plugin Giacinto Cifelli
@ 2018-10-29 19:58 ` Denis Kenzior
  2018-10-30  6:10   ` Giacinto Cifelli
  0 siblings, 1 reply; 16+ messages in thread
From: Denis Kenzior @ 2018-10-29 19:58 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 100228 bytes --]

Hi Giacinto,

On 10/26/2018 01:10 AM, Giacinto Cifelli wrote:
> I would like to submit to your attention the new version of the Gemalto
> plugin. It is not ready, but would already benefit from some feedback.
> The purpose of this new plugin is to address most of the Gemalto modules
> (excluding perhaps some LTE CatM and CatNB), interfaced either via USB
> or RS232 interface.

So just a cursory look through this, but overall my impression is that 
this code would be utterly unmaintainable.  You need to split this up 
into something without a bazillion if conditions and #ifdefs in it. 
Notice how none of our existing driver code uses #ifdefs.

That means MBIM/QMI/AT logic needs to be separated into separate 
drivers.  If you have a weird QMI/AT or MBIM/AT or QMI over MBIM 
combination stuff going on, then these all need to be separate drivers.

You may want to basically start with the basics.  Get the initial driver 
separation figured out, then you can add fancy stuff like vendor 
specific APIs, etc.  Right now they just clutter the code and are not 
really useful to a wide audience anyway.

> 
> I have included the totality of file plugins/gemalto.c because it is
> quite different from the current one.
> 
> I would appreciate a generic comment on how to split it in commits
> this file.
> 
> I have added an include/gemalto.h file, as suggested by Jonas Bonn.
> There isn't much in it yet, but some additional code should be moved into
> it.
> 
> The gprs and gprs-context's are perhaps not finished.
> 
> In udevng there are 3 additional commits, please just comment if you
> like and find them useful and I will submit separately.
> 
> There are some review comments, in // format, not part of the code.
> 
> ---
>   include/gemalto.h |   27 +
>   plugins/gemalto.c | 2715 +++++++++++++++++++++++++++++++++++++++++++++
>   plugins/udevng.c  |  208 ++--
>   3 files changed, 2885 insertions(+), 65 deletions(-)
>   create mode 100644 include/gemalto.h
>   create mode 100644 plugins/gemalto.c
> 
> diff --git a/include/gemalto.h b/include/gemalto.h
> new file mode 100644
> index 00000000..26165eb1
> --- /dev/null
> +++ b/include/gemalto.h
> @@ -0,0 +1,27 @@
> +/*
> + *
> + *  oFono - Open Source Telephony
> + *
> + *  Copyright (C) 2018 Gemalto M2M
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License version 2 as
> + *  published by the Free Software Foundation.
> + *
> + *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + *
> + */
> +
> +enum auth_option {
> +	GEMALTO_AUTH_DEFAULTS		= 0,
> +	GEMALTO_AUTH_USE_SGAUTH		= 1<<0,
> +	GEMALTO_AUTH_ORDER_PWD_USR	= 1<<1,
> +	GEMALTO_AUTH_ALWAYS_ALL_PARAMS	= 1<<2,
> +};
> diff --git a/plugins/gemalto.c b/plugins/gemalto.c
> new file mode 100644
> index 00000000..6b208572
> --- /dev/null
> +++ b/plugins/gemalto.c
> @@ -0,0 +1,2715 @@
> +/*
> + *
> + *  oFono - Open Source Telephony
> + *
> + *  Copyright (C) 2017 Vincent Cesson. All rights reserved.
> + *  Copyright (C) 2018 Gemalto M2M
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License version 2 as
> + *  published by the Free Software Foundation.
> + *
> + *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> + *
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <sys/mman.h>
> +#include <linux/types.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <glib.h>
> +#include <gatchat.h>
> +#include <gattty.h>
> +#include <gdbus.h>
> +#include "ofono.h"
> +#define OFONO_API_SUBJECT_TO_CHANGE
> +#include <ofono/dbus.h>
> +#include <ofono/plugin.h>
> +#include <ofono/log.h>
> +#include <ofono/modem.h>
> +#include <ofono/devinfo.h>
> +#include <ofono/netreg.h>
> +#include <ofono/phonebook.h>
> +#include <ofono/sim.h>
> +#include <ofono/sms.h>
> +#include <ofono/gprs.h>
> +#include <ofono/gprs-context.h>
> +#include <ofono/location-reporting.h>
> +#include <drivers/atmodem/atutil.h>
> +#include <drivers/atmodem/vendor.h>
> +#include <string.h>
> +
> +#ifdef HAVE_ELL
> +#include <ell/ell.h>
> +#include <drivers/mbimmodem/mbim.h>
> +#include <drivers/mbimmodem/mbim-message.h>
> +#include <drivers/mbimmodem/mbim-desc.h>
> +#endif
> // some models can use MBIM, but if the option is not included,
> // they fall back to PPP
> +
> +#include <drivers/qmimodem/qmi.h>
> +#include <src/storage.h>
> +#include "gemalto.h"
> +
> // the next part will not be in the official commit
> +/* debug utilities - begin */
> +
> +#define REDCOLOR "\x1b\x5b\x30\x31\x3b\x33\x31\x6d"
> +#define NOCOLOR "\x1b\x5b\x30\x30\x6d"
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <sys/wait.h>
> +#include <unistd.h>
> +
> +void print_trace();
> +
> +void print_trace() {
> +    char pid_buf[30];
> +    char name_buf[512];
> +    int child_pid;
> +    sprintf(pid_buf, "%d", getpid());
> +    name_buf[readlink("/proc/self/exe", name_buf, 511)]=0;
> +    child_pid = fork();
> +    if (!child_pid) {
> +        dup2(2,1); // redirect output to stderr
> +        fprintf(stdout,"stack trace for %s pid=%s\n",name_buf,pid_buf);
> +        execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", "bt", name_buf, pid_buf, NULL);
> +        abort(); /* If gdb failed to start */
> +    } else {
> +        waitpid(child_pid,NULL,0);
> +    }
> +}
> +
> +/* debug utilities - end */
> +
> +enum gemalto_connection_type {
> +	GEMALTO_CONNECTION_SERIAL = 1,
> +	GEMALTO_CONNECTION_USB = 2,
> +};
> +
> +enum gemalto_device_state {
> +	STATE_ABSENT = 0,
> +	STATE_PROBE = 1,
> +	STATE_PRESENT = 2,
> +};
> +
> +enum gprs_option {
> +	NO_GPRS = 0,
> +	USE_SWWAN = 1,
> +	USE_CTX17 = 2,
> +	USE_CTX3 = 3,
> +	USE_PPP = 4,
> +	USE_SWWAN_INV = 5,	/* inverted syntax idx,act */
> +	USE_CTX_INV = 6,	/* inverted syntax idx,act */
> +};
> +
> +static const char *none_prefix[] = { NULL };
> +static const char *cfun_prefix[] = { "+CFUN:", NULL };
> +static const char *sctm_prefix[] = { "^SCTM:", NULL };
> +static const char *sbv_prefix[] = { "^SBV:", NULL };
> +static const char *sqport_prefix[] = { "^SQPORT:", NULL };
> +static const char *sgpsc_prefix[] = { "^SGPSC:", NULL };
> +
> +typedef void (*OpenResultFunc)(gboolean success, struct ofono_modem *modem);
> +
> +struct gemalto_data {
> +	gboolean init_done;
> +	GIOChannel *channel;
> +	GAtChat *tmp_chat;
> +	OpenResultFunc open_cb;
> +	guint read_src;
> +	GAtChat *app;
> +	GAtChat *mdm;
> +	int cfun;
> +
> +	struct ofono_sim *sim;
> +	gboolean have_sim;
> +	struct at_util_sim_state_query *sim_state_query;
> +	guint modem_ready_id;
> +
> +	char modelstr[32];
> +	char sqport[32];
> +
> +	guint model;
> +	guint probing_timer;
> +	guint init_waiting_time;
> +	guint waiting_time;
> +
> +	enum gemalto_connection_type conn;
> +	enum gemalto_device_state mbim;
> +	enum gemalto_device_state qmi;
> +	enum gemalto_device_state ecmncm;
> +	enum gemalto_device_state gina;
> +	gboolean inverse_enum;
> +	gboolean use_mdm_for_app;
> +	gboolean voice_avail;
> +	enum auth_option auth_syntax;
> +	enum gprs_option gprs_opt;
> +	gboolean has_lte;
> +	gboolean autoattach;
> +	gboolean autoconfig;
> +	gboolean autoactivation;
> +	gboolean vts_with_quotes;
> +
> +	void *device; /* struct mbim_device* or struct qmi_device* */
> +
> +	/* mbim data */
> +	uint16_t max_segment;
> +	uint8_t max_outstanding;
> +	uint8_t max_sessions;
> +

The mbim plugin is not sufficient for this?

> +	/* hardware monitor variables */
> +	DBusMessage *hm_msg;
> +	int32_t temperature;
> +	int32_t voltage;
> +	/* gnss variables */
> +	DBusMessage *gnss_msg;
> +	/* hardware control variables */
> +	DBusMessage *hc_msg;
> +	gboolean powersave;
> +};
> +
> +/*******************************************************************************
> + * Generic functions
> + ******************************************************************************/
> +
> +static void gemalto_debug(const char *str, void *user_data)
> +{
> +	const char *prefix = user_data;
> +
> +	ofono_info("%s%s", prefix, str);
> +}
> +
> +static const char *gemalto_get_string(struct ofono_modem *modem, const char *k)
> +{
> +	const char *v;
> +
> +	if (!modem || !k || !*k)
> +		return NULL;
> +
> +	v = ofono_modem_get_string(modem, k);
> +
> +	if (!v || !*v)
> +		return NULL;
> +
> +	return v;
> +}

Uhh, why?

> +
> +static void gemalto_signal(const char *iface, const char *name,
> +	const char *value, struct ofono_modem *modem)
> +{
> +	DBusMessageIter sub_iter,iter;
> +	const char *path = ofono_modem_get_path(modem);
> +	DBusConnection *conn = ofono_dbus_get_connection();
> +
> +	DBusMessage *signal = dbus_message_new_signal(path,
> +					iface,
> +					name);
> +
> +	DBG("");
> +
> +	if (signal == NULL) {
> +		DBG("Cannot create new signal message");
> +		return;
> +	}
> +
> +	dbus_message_iter_init_append(signal, &iter);
> +	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
> +							"s", &sub_iter);
> +	if (!dbus_message_iter_append_basic(&sub_iter,
> +				DBUS_TYPE_STRING, &value)) {
> +		DBG("Out of memory!");
> +		return;
> +	}
> +
> +	dbus_message_iter_close_container(&iter, &sub_iter);
> +	g_dbus_send_message(conn, signal);
> +}
> +
> +static void executeWithPrompt(GAtChat *port, const char *command,
> +			const char *prompt, const char *argument, void *cb,
> +			void *cbd, void *freecall)
> +{
> +	char *buf;
> +	const char *expected_array[2] = {0,0};
> +
> +	buf = g_strdup_printf("%s\r%s", command, argument);
> +
> +	if (strlen(argument)>=2 && g_str_equal(argument+strlen(argument)-2,
> +									"^Z"))
> +		sprintf(buf+strlen(buf)-2,"\x1a");
> +
> +	if (strlen(argument)>=2 && g_str_equal(argument+strlen(argument)-2,
> +									"\\r"))
> +		sprintf(buf+strlen(buf)-2,"\r");
> +
> +	expected_array[0]=prompt;
> +	g_at_chat_send_and_expect_short_prompt(port, buf, expected_array,
> +							cb, cbd, freecall);
> +	free(buf);
> +}
> +
> +static void gemalto_exec_stored_cmd(struct ofono_modem *modem,
> +							const char *filename)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	const char *vid = gemalto_get_string(modem, "Vendor");
> +	const char *pid = gemalto_get_string(modem, "Model");
> +	char store[64];
> +	int index;
> +	char *command, *prompt, *argument;
> +	char key[32];
> +	GKeyFile *f;
> +
> +	sprintf(store,"%s-%s/%s", vid, pid, filename);
> +	f = storage_open(NULL, store);
> +
> +	if (!f)
> +		return;
> +
> +	for (index = 0; ; index++) {
> +		sprintf(key, "command_%d", index);
> +		command = g_key_file_get_string(f, "Simple", key, NULL);
> +
> +		if (!command)
> +			break;
> +
> +		DBG(REDCOLOR"executing stored command simple: %s"NOCOLOR, command);
> +		g_at_chat_send(data->app, command, NULL, NULL, NULL, NULL);
> +	}
> +
> +	for (index = 0; ; index++) {
> +		sprintf(key, "command_%d", index);
> +		command = g_key_file_get_string(f, "WithPrompt", key, NULL);
> +		sprintf(key, "prompt_%d", index);
> +		prompt = g_key_file_get_string(f, "WithPrompt", key, NULL);
> +		sprintf(key, "argument_%d", index);
> +		argument = g_key_file_get_string(f, "WithPrompt", key, NULL);
> +
> +		if (!command || !prompt || !argument)
> +			break;
> +
> +		DBG("executing stored command with prompt: %s", command);
> +		executeWithPrompt(data->app, command, prompt, argument,
> +			NULL, NULL, NULL);
> +	}
> +
> +	storage_close(NULL, store, f, FALSE);
> +}

What is this doing actually?

> +
> +/*******************************************************************************
> + * Hardware monitor interface
> + ******************************************************************************/
> +
> +#define HARDWARE_MONITOR_INTERFACE OFONO_SERVICE ".gemalto.HardwareMonitor"
> +#define CINTERION_LEGACY_HWMON_INTERFACE OFONO_SERVICE ".cinterion.HardwareMonitor"
> +
> +static void gemalto_sctmb_notify(GAtResult *result, gpointer user_data)
> +{
> +	GAtResultIter iter;
> +	gint value;
> +	char *val;
> +
> +	g_at_result_iter_init(&iter, result);
> +	g_at_result_iter_next(&iter, "^SCTM_B:");
> +	g_at_result_iter_next_number(&iter, &value);
> +
> +	switch(value) {
> +	case -1:
> +		val="Below low temperature alert limit";
> +		break;
> +	case 0:
> +		val="Normal operating temperature";
> +		break;
> +	case 1:
> +		val="Above upper temperature alert limit";
> +		break;
> +	case 2:
> +		val="Above uppermost temperature limit";
> +		break;
> +	default: /* unvalid value, do not output signal*/
> +		return;
> +	}
> +
> +	gemalto_signal(HARDWARE_MONITOR_INTERFACE, "CriticalTemperature", val,
> +								user_data);
> +}
> +
> +static void gemalto_sbc_notify(GAtResult *result, gpointer user_data)
> +{
> +	GAtResultIter iter;
> +	const char *value;
> +
> +	g_at_result_iter_init(&iter, result);
> +	g_at_result_iter_next(&iter, "^SBC:");
> +	g_at_result_iter_next_unquoted_string(&iter, &value);
> +	gemalto_signal(HARDWARE_MONITOR_INTERFACE, "CriticalVoltage", value,
> +								user_data);
> +}
> +
> +static void gemalto_sctm_cb(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct gemalto_data *data = user_data;
> +	DBusMessage *reply;
> +	GAtResultIter iter;
> +	DBusMessageIter dbus_iter;
> +	DBusMessageIter dbus_dict;
> +
> +	if (data->hm_msg == NULL)
> +		return;
> +
> +	if (!ok)
> +		goto error;
> +
> +	g_at_result_iter_init(&iter, result);
> +
> +	if (!g_at_result_iter_next(&iter, "^SCTM:"))
> +		goto error;
> +
> +	if (!g_at_result_iter_skip_next(&iter))
> +		goto error;
> +
> +	if (!g_at_result_iter_skip_next(&iter))
> +		goto error;
> +
> +	if (!g_at_result_iter_next_number(&iter, &data->temperature))
> +		goto error;
> +
> +	reply = dbus_message_new_method_return(data->hm_msg);
> +
> +	dbus_message_iter_init_append(reply, &dbus_iter);
> +
> +	dbus_message_iter_open_container(&dbus_iter, DBUS_TYPE_ARRAY,
> +			OFONO_PROPERTIES_ARRAY_SIGNATURE,
> +			&dbus_dict);
> +
> +	ofono_dbus_dict_append(&dbus_dict, "Temperature",
> +			DBUS_TYPE_INT32, &data->temperature);
> +
> +	ofono_dbus_dict_append(&dbus_dict, "Voltage",
> +			DBUS_TYPE_UINT32, &data->voltage);
> +
> +	dbus_message_iter_close_container(&dbus_iter, &dbus_dict);
> +
> +	__ofono_dbus_pending_reply(&data->hm_msg, reply);
> +
> +	return;
> +
> +error:
> +	__ofono_dbus_pending_reply(&data->hm_msg,
> +			__ofono_error_failed(data->hm_msg));
> +}
> +
> +static void gemalto_sbv_cb(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct gemalto_data *data = user_data;
> +	GAtResultIter iter;
> +
> +	if (!ok)
> +		goto error;
> +
> +	g_at_result_iter_init(&iter, result);
> +
> +	if (!g_at_result_iter_next(&iter, "^SBV:"))
> +		goto error;
> +
> +	if (!g_at_result_iter_next_number(&iter, &data->voltage))
> +		goto error;
> +
> +	if (g_at_chat_send(data->app, "AT^SCTM?", sctm_prefix, gemalto_sctm_cb,
> +				data, NULL) > 0)
> +		return;
> +
> +error:
> +	__ofono_dbus_pending_reply(&data->hm_msg,
> +			__ofono_error_failed(data->hm_msg));
> +}
> +
> +static DBusMessage *hardware_monitor_get_statistics(DBusConnection *conn,
> +							DBusMessage *msg,
> +							void *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +
> +	DBG("");
> +
> +	if (data->hm_msg != NULL)
> +		return __ofono_error_busy(msg);
> +
> +	if (!g_at_chat_send(data->app, "AT^SBV", sbv_prefix, gemalto_sbv_cb,
> +			data, NULL))
> +		return __ofono_error_failed(msg);
> +
> +	data->hm_msg = dbus_message_ref(msg);
> +
> +	return NULL;
> +}
> +
> +static const GDBusMethodTable hardware_monitor_methods[] = {
> +	{ GDBUS_ASYNC_METHOD("GetStatistics",
> +			NULL, GDBUS_ARGS({ "Statistics", "a{sv}" }),
> +			hardware_monitor_get_statistics) },
> +	{}
> +};
> +
> +static const GDBusSignalTable hardware_monitor_signals[] = {
> +	{ GDBUS_SIGNAL("CriticalTemperature",
> +			GDBUS_ARGS({ "temperature", "a{sv}" }) )},
> +	{ GDBUS_SIGNAL("CriticalVoltage",
> +			GDBUS_ARGS({ "voltage", "a{sv}" }) )},
> +	{}
> +};
> +
> +static void gemalto_hardware_monitor_enable(struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	DBusConnection *conn = ofono_dbus_get_connection();
> +	const char *path = ofono_modem_get_path(modem);
> +
> +	/* Listen to over/undertemperature URCs (activated with AT^SCTM) */
> +	g_at_chat_register(data->app, "^SCTM_B:",
> +		gemalto_sctmb_notify, FALSE, NULL, NULL);
> +	/* Listen to over/under voltage URCs (automatic URC) */
> +	g_at_chat_register(data->app, "^SBC:",
> +		gemalto_sbc_notify, FALSE, NULL, NULL);
> +	/* Enable temperature URC and value output */
> +	g_at_chat_send(data->app, "AT^SCTM=1,1", none_prefix, NULL, NULL, NULL);
> +
> +	if (!g_dbus_register_interface(conn, path, HARDWARE_MONITOR_INTERFACE,
> +					hardware_monitor_methods,
> +					hardware_monitor_signals,
> +					NULL,
> +					modem,
> +					NULL)) {
> +		ofono_error("Could not register %s interface under %s",
> +					HARDWARE_MONITOR_INTERFACE, path);
> +		return;
> +	}
> +
> +	ofono_modem_add_interface(modem, HARDWARE_MONITOR_INTERFACE);
> +
> +	if (!g_dbus_register_interface(conn, path,
> +					CINTERION_LEGACY_HWMON_INTERFACE,
> +					hardware_monitor_methods,
> +					NULL,
> +					NULL,
> +					modem,
> +					NULL)) {
> +		ofono_error("Could not register %s interface under %s",
> +					CINTERION_LEGACY_HWMON_INTERFACE, path);
> +		return;
> +	}
> +
> +	ofono_modem_add_interface(modem, CINTERION_LEGACY_HWMON_INTERFACE);
> +}
> +
> +/*******************************************************************************
> + * Time services interface
> + ******************************************************************************/
> +
> +#define GEMALTO_NITZ_TIME_INTERFACE OFONO_SERVICE ".gemalto.TimeServices"
> +
> +static DBusMessage *set_modem_datetime(DBusConnection *conn,
> +							DBusMessage *msg,
> +							void *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	time_t t = time(NULL);
> +	struct tm tm;
> +	gchar cclk_cmd[32];
> +
> +	/* Set date and time */
> +	tm = *localtime(&t);
> +	strftime(cclk_cmd, 32, "AT+CCLK=\"%y/%m/%d,%T\"", &tm);
> +	g_at_chat_send(data->app, cclk_cmd, none_prefix, NULL, NULL, NULL);
> +	return dbus_message_new_method_return(msg);
> +}
> +
> +static const GDBusMethodTable gsmTime_methods[] = {
> +	{ GDBUS_ASYNC_METHOD("SetModemDatetime",
> +			NULL, NULL, set_modem_datetime) },
> +	{}
> +};
> +

So first question is, why would you want to do this?  These days most 
systems use the time on the Application processor.  Who cares what the 
modem thinks the time is.

Secondly, why not do this as an actual core atom?

> +static const GDBusSignalTable gsmTime_signals[] = {
> +	{ GDBUS_SIGNAL("NitzUpdated",
> +			GDBUS_ARGS({ "time", "a{sv}" }) )},
> +	{}
> +};

You don't like ofono_netreg_time_notify?

> +
> +static void gemalto_time_enable(struct ofono_modem *modem)
> +{
> +	DBusConnection *conn = ofono_dbus_get_connection();
> +	const char *path = ofono_modem_get_path(modem);
> +
> +	if (!g_dbus_register_interface(conn, path,
> +					GEMALTO_NITZ_TIME_INTERFACE,
> +					gsmTime_methods,
> +					gsmTime_signals,
> +					NULL,
> +					modem,
> +					NULL)) {
> +		ofono_error("Could not register %s interface under %s",
> +					GEMALTO_NITZ_TIME_INTERFACE, path);
> +		return;
> +	}
> +
> +	ofono_modem_add_interface(modem, GEMALTO_NITZ_TIME_INTERFACE);
> +}
> +
> +/*******************************************************************************
> + * Command passtrhough interface
> + ******************************************************************************/
> +
> +#define COMMAND_PASSTHROUGH_INTERFACE OFONO_SERVICE ".gemalto.CommandPassthrough"
> +

No AT command pass through in oFono upstream ;)  We've had this 
conversation many times, if there are useful AT commands that can be 
sent via this interface, then they should be abstracted behind a proper 
API instead.

> +static int command_passthrough_signal_answer(const char *answer,
> +							gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	DBusConnection *conn = ofono_dbus_get_connection();
> +	const char *path = ofono_modem_get_path(modem);
> +	DBusMessage *signal;
> +	DBusMessageIter iter;
> +
> +	if (!conn || !path)
> +		return -1;
> +
> +	signal = dbus_message_new_signal(path, COMMAND_PASSTHROUGH_INTERFACE,
> +								"Answer");
> +	if (!signal) {
> +		ofono_error("Unable to allocate new %s.PropertyChanged signal",
> +						COMMAND_PASSTHROUGH_INTERFACE);
> +		return -1;
> +	}
> +
> +	dbus_message_iter_init_append(signal, &iter);
> +
> +	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &answer);
> +
> +	DBG("");
> +
> +	return g_dbus_send_message(conn, signal);
> +}
> +
> +static void command_passthrough_cb(gboolean ok, GAtResult *result,
> +							gpointer user_data)
> +{
> +	GAtResultIter iter;
> +	guint len = 0;
> +	char *answer;
> +
> +	g_at_result_iter_init(&iter, result);
> +
> +	while (g_at_result_iter_next(&iter, NULL)) {
> +		len += strlen(g_at_result_iter_raw_line(&iter))+2;
> +	}
> +
> +	len += strlen(g_at_result_final_response(result))+3;
> +	answer = g_new0(char, len);
> +	g_at_result_iter_init(&iter, result);
> +
> +	while (g_at_result_iter_next(&iter, NULL)) {
> +		sprintf(answer+strlen(answer),"%s\r\n",
> +					g_at_result_iter_raw_line(&iter));
> +	}
> +
> +	sprintf(answer+strlen(answer),"%s\r\n",
> +					g_at_result_final_response(result));
> +
> +	DBG("answer_len: %u, answer_string: %s", len, answer);
> +	command_passthrough_signal_answer(answer, user_data);
> +
> +	g_free(answer);
> +}
> +
> +static DBusMessage *command_passthrough_simple(DBusConnection *conn,
> +							DBusMessage *msg,
> +							void *user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	DBusMessageIter iter;
> +	const char *command;
> +
> +	if (!dbus_message_iter_init(msg, &iter))
> +		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
> +							"No arguments given");
> +
> +	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
> +		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
> +					"Invalid argument type: '%c'",
> +					dbus_message_iter_get_arg_type(&iter));
> +
> +	dbus_message_iter_get_basic(&iter, &command);
> +
> +	g_at_chat_send(data->app, command, NULL, command_passthrough_cb,
> +								modem, NULL);
> +
> +	return dbus_message_new_method_return(msg);
> +}
> +
> +static DBusMessage *command_passthrough_with_prompt(DBusConnection *conn,
> +							DBusMessage *msg,
> +							void *user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	DBusMessageIter iter;
> +	const char *command, *prompt, *argument;
> +
> +	if (!dbus_message_iter_init(msg, &iter))
> +		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
> +							"No arguments given");
> +
> +	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
> +		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
> +					"Invalid argument type: '%c'",
> +					dbus_message_iter_get_arg_type(&iter));
> +
> +	dbus_message_iter_get_basic(&iter, &command);
> +	dbus_message_iter_next(&iter);
> +
> +	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
> +		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
> +					"Invalid argument type: '%c'",
> +					dbus_message_iter_get_arg_type(&iter));
> +
> +	dbus_message_iter_get_basic(&iter, &prompt);
> +	dbus_message_iter_next(&iter);
> +
> +	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
> +		return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS,
> +					"Invalid argument type: '%c'",
> +					dbus_message_iter_get_arg_type(&iter));
> +
> +	dbus_message_iter_get_basic(&iter, &argument);
> +
> +	executeWithPrompt(data->app, command, prompt, argument,
> +					command_passthrough_cb, modem, NULL);
> +
> +	return dbus_message_new_method_return(msg);
> +}
> +
> +static DBusMessage *command_passthrough_send_break(DBusConnection *conn,
> +							DBusMessage *msg,
> +							void *user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	GIOChannel *channel = g_at_chat_get_channel(data->app);
> +
> +	g_io_channel_write_chars(channel, "\r", 1, NULL, NULL);
> +
> +	return dbus_message_new_method_return(msg);
> +}
> +
> +static const GDBusMethodTable command_passthrough_methods[] = {
> +	{ GDBUS_ASYNC_METHOD("Simple",
> +		GDBUS_ARGS({ "command", "s" }),
> +		NULL,
> +		command_passthrough_simple) },
> +	{ GDBUS_ASYNC_METHOD("WithPrompt",
> +		GDBUS_ARGS({ "command", "s" }, { "prompt", "s" },
> +							{ "argument", "s" }),
> +		NULL,
> +		command_passthrough_with_prompt) },
> +	{ GDBUS_ASYNC_METHOD("SendBreak",
> +		NULL,
> +		NULL,
> +		command_passthrough_send_break) },
> +	{}
> +};
> +
> +static const GDBusSignalTable command_passthrough_signals[] = {
> +	{ GDBUS_SIGNAL("Answer",
> +		GDBUS_ARGS({ "answer", "s" })) },
> +	{ }
> +};
> +
> +static void gemalto_command_passthrough_enable(struct ofono_modem *modem)
> +{
> +	DBusConnection *conn = ofono_dbus_get_connection();
> +	const char *path = ofono_modem_get_path(modem);
> +
> +	/* Create Command Passthrough DBus interface */
> +	if (!g_dbus_register_interface(conn, path, COMMAND_PASSTHROUGH_INTERFACE,
> +					command_passthrough_methods,
> +					command_passthrough_signals,
> +					NULL,
> +					modem,
> +					NULL)) {
> +		ofono_error("Could not register %s interface under %s",
> +					COMMAND_PASSTHROUGH_INTERFACE, path);
> +		return;
> +	}
> +
> +	ofono_modem_add_interface(modem, COMMAND_PASSTHROUGH_INTERFACE);
> +}
> +
> +/*******************************************************************************
> + * GNSS interface
> + ******************************************************************************/
> +
> +#define GNSS_INTERFACE OFONO_SERVICE ".gemalto.GNSS"
> +
> +static void gnss_get_properties_cb(gboolean ok, GAtResult *result,
> +							gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	const char *port = ofono_modem_get_string(modem, "GNSS");
> +	GAtResultIter iter;
> +	DBusMessage *reply;
> +	DBusMessageIter dbusiter;
> +	DBusMessageIter dict;
> +
> +	if (data->gnss_msg == NULL)
> +		return;
> +
> +	if (!ok)
> +		goto error;
> +
> +	reply = dbus_message_new_method_return(data->gnss_msg);
> +	dbus_message_iter_init_append(reply, &dbusiter);
> +	dbus_message_iter_open_container(&dbusiter, DBUS_TYPE_ARRAY,
> +					OFONO_PROPERTIES_ARRAY_SIGNATURE,
> +					&dict);
> +	g_at_result_iter_init(&iter, result);
> +
> +	/* supported format: ^SGPSC: "Nmea/Output","off" */
> +	while (g_at_result_iter_next(&iter, "^SGPSC:")) {
> +		const char *name = "";
> +		const char *val = "";
> +
> +		if (!g_at_result_iter_next_string(&iter, &name))
> +			continue;
> +
> +		/*
> +		 * skip the "Info" property:
> +		 * different line format and different usage
> +		 */
> +		if (g_str_equal(name,"Info"))
> +			continue;
> +
> +		if (!g_at_result_iter_next_string(&iter, &val))
> +			continue;
> +
> +		ofono_dbus_dict_append(&dict, name, DBUS_TYPE_STRING, &val);
> +	}
> +
> +	ofono_dbus_dict_append(&dict, "Port", DBUS_TYPE_STRING, &port);
> +	dbus_message_iter_close_container(&dbusiter, &dict);
> +	__ofono_dbus_pending_reply(&data->gnss_msg, reply);
> +	return;
> +
> +error:
> +	__ofono_dbus_pending_reply(&data->gnss_msg,
> +			__ofono_error_failed(data->gnss_msg));
> +}
> +
> +static DBusMessage *gnss_get_properties(DBusConnection *conn,
> +					DBusMessage *msg, void *user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +
> +	if (data->gnss_msg != NULL)
> +		return __ofono_error_busy(msg);
> +
> +	if (!g_at_chat_send(data->app, "AT^SGPSC?", sgpsc_prefix,
> +					gnss_get_properties_cb, modem, NULL))
> +		return __ofono_error_failed(msg);
> +
> +	data->gnss_msg = dbus_message_ref(msg);
> +
> +	return NULL;
> +}
> +
> +static void gnss_set_properties_cb(gboolean ok, GAtResult *result,
> +							gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	DBusMessage *reply;
> +
> +	if (data->gnss_msg == NULL)
> +		return;
> +
> +	if (!ok) {
> +		__ofono_dbus_pending_reply(&data->gnss_msg,
> +					__ofono_error_failed(data->gnss_msg));
> +		return;
> +	}
> +
> +	reply = dbus_message_new_method_return(data->gnss_msg);
> +	__ofono_dbus_pending_reply(&data->gnss_msg, reply);
> +}
> +
> +static DBusMessage *gnss_set_property(DBusConnection *conn,
> +					DBusMessage *msg, void *user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	DBusMessageIter iter, var;
> +	const char *name;
> +	char *value;
> +	char buf[256];
> +
> +	if (data->gnss_msg != NULL)
> +		return __ofono_error_busy(msg);
> +
> +	if (dbus_message_iter_init(msg, &iter) == FALSE)
> +		return __ofono_error_invalid_args(msg);
> +
> +	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
> +		return __ofono_error_invalid_args(msg);
> +
> +	dbus_message_iter_get_basic(&iter, &name);
> +	dbus_message_iter_next(&iter);
> +
> +	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
> +		return __ofono_error_invalid_args(msg);
> +
> +	dbus_message_iter_recurse(&iter, &var);
> +
> +	if (dbus_message_iter_get_arg_type(&var) !=
> +					DBUS_TYPE_STRING)
> +		return __ofono_error_invalid_args(msg);
> +
> +	dbus_message_iter_get_basic(&var, &value);
> +
> +	snprintf(buf, sizeof(buf), "AT^SGPSC=\"%s\",\"%s\"", name, value);
> +

So the modem is doing the property validation?  Yeah, not happening ;) 
Why don't you just configure the device into NMEA mode and use 
location_reporting atom ?

> +	if (!g_at_chat_send(data->app, buf, sgpsc_prefix,
> +					gnss_set_properties_cb, modem, NULL))
> +		return __ofono_error_failed(msg);
> +
> +	data->gnss_msg = dbus_message_ref(msg);
> +	return NULL;
> +}
> +
> +static const GDBusMethodTable gnss_methods[] = {
> +	{ GDBUS_ASYNC_METHOD("GetProperties",
> +			NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
> +			gnss_get_properties) },
> +	{ GDBUS_ASYNC_METHOD("SetProperty",
> +			GDBUS_ARGS({ "property", "s" }, { "value", "v" }),
> +			NULL, gnss_set_property) },
> +	{ }
> +};
> +
> +static void gnss_exec_stored_param(struct ofono_modem *modem,
> +							const char *filename) {
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	const char *vid = ofono_modem_get_string(modem, "Vendor");
> +	const char *pid = ofono_modem_get_string(modem, "Model");
> +	char store[64];
> +	int index;
> +	char *property, *value;
> +	char key[32];
> +	GKeyFile *f;
> +	char *command;
> +
> +	sprintf(store,"%s-%s/%s", vid, pid, filename);
> +	f = storage_open(NULL, store);
> +
> +	if (!f)
> +		return;
> +
> +	for (index=0;;index++) {
> +		sprintf(key, "property_%d", index);
> +		property = g_key_file_get_string(f, "Properties", key, NULL);
> +
> +		sprintf(key, "value_%d", index);
> +		value = g_key_file_get_string(f, "Properties", key, NULL);
> +
> +		if(!property || !value)
> +			break;
> +
> +		command = g_strdup_printf("AT^SGPSC=%s,%s", property, value);
> +		DBG(REDCOLOR"setting GNSS property: %sNOCOLOR", command);
> +		g_at_chat_send(data->app, command, NULL, NULL, NULL, NULL);
> +		free(command);
> +	}
> +
> +	storage_close(NULL, store, f, FALSE);
> +}
> +
> +static void gemalto_gnss_enable_cb(gboolean ok, GAtResult *result,
> +							gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	DBusConnection *conn = ofono_dbus_get_connection();
> +	const char *path = ofono_modem_get_path(modem);
> +
> +	if (!ok)
> +		return; /* the module does not support GNSS */
> +
> +	gnss_exec_stored_param(modem, "gnss_startup");
> +
> +	/* Create GNSS DBus interface */
> +	if (!g_dbus_register_interface(conn, path, GNSS_INTERFACE,
> +					gnss_methods,
> +					NULL,
> +					NULL,
> +					modem,
> +					NULL)) {
> +		ofono_error("Could not register %s interface under %s",
> +					GNSS_INTERFACE, path);
> +		return;
> +	}
> +
> +	ofono_modem_add_interface(modem, GNSS_INTERFACE);
> +}
> +
> +static void gemalto_gnss_enable(struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +
> +	g_at_chat_send(data->app, "AT^SGPSC?", sgpsc_prefix,
> +					gemalto_gnss_enable_cb, modem, NULL);
> +}
> +
> +/*******************************************************************************
> + * Hardware control interface
> + ******************************************************************************/
> +
> +#define HARDWARE_CONTROL_INTERFACE OFONO_SERVICE ".gemalto.HardwareControl"
> +
> +static DBusMessage *hc_get_properties(DBusConnection *conn,
> +					DBusMessage *msg, void *user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	DBusMessage *reply;
> +	DBusMessageIter dbusiter;
> +	DBusMessageIter dict;
> +
> +	reply = dbus_message_new_method_return(msg);
> +	dbus_message_iter_init_append(reply, &dbusiter);
> +	dbus_message_iter_open_container(&dbusiter, DBUS_TYPE_ARRAY,
> +					OFONO_PROPERTIES_ARRAY_SIGNATURE,
> +					&dict);
> +
> +	ofono_dbus_dict_append(&dict, "Powersave", DBUS_TYPE_BOOLEAN,
> +							&data->powersave);
> +	dbus_message_iter_close_container(&dbusiter, &dict);
> +
> +	return reply;
> +}
> +
> +/*
> + * powersave for older modules:
> + *	command_0=AT+CFUN=7
> + * return:
> + *	command_0=AT+CFUN=1
> + *
> + * powersave example for modules with GNSS (could also only stop the output):
> + *	command_0=AT+CREG=0
> + *	command_1=AT+CGREG=0
> + *	command_2=AT+CEREG=0
> + *	command_3=AT^SGPSC="Engine","0"
> + *	command_4=AT^SGPSC="Power/Antenna","off"
> + * return:
> + *	command_0=AT+CREG=2
> + *	command_1=AT+CGREG=2
> + *	command_2=AT+CEREG=2
> + *	command_4=AT^SGPSC="Power/Antenna","on"
> + *	command_3=AT^SGPSC="Engine","1"
> + */

How is 'powersave' useful?

> +
> +static void gemalto_powersave_cb(gboolean ok, GAtResult *result,
> +				gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	DBusMessage *reply;
> +
> +	/* flip the state in any case */
> +	data->powersave = !data->powersave;
> +
> +	if (data->hc_msg == NULL)
> +		return;
> +
> +	reply = dbus_message_new_method_return(data->hc_msg);
> +	__ofono_dbus_pending_reply(&data->hc_msg, reply);
> +}
> +
> +static DBusMessage *hc_set_property(DBusConnection *conn,
> +					DBusMessage *msg, void *user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	DBusMessageIter iter, var;
> +	const char *name;
> +	gboolean enable;
> +
> +	if (data->hc_msg != NULL)
> +		return __ofono_error_busy(msg);
> +
> +	if (dbus_message_iter_init(msg, &iter) == FALSE)
> +		return __ofono_error_invalid_args(msg);
> +
> +	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
> +		return __ofono_error_invalid_args(msg);
> +
> +	dbus_message_iter_get_basic(&iter, &name);
> +
> +	if (!g_str_equal(name, "Powersave"))
> +		return __ofono_error_invalid_args(msg);
> +
> +	dbus_message_iter_next(&iter);
> +
> +	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
> +		return __ofono_error_invalid_args(msg);
> +
> +	dbus_message_iter_recurse(&iter, &var);
> +
> +	if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
> +		return __ofono_error_invalid_args(msg);
> +
> +	dbus_message_iter_get_basic(&var, &enable);
> +
> +	if (data->powersave == enable)
> +		return dbus_message_new_method_return(msg);
> +
> +	gemalto_exec_stored_cmd(modem, enable ? "power_mode_powersave" :
> +							"power_mode_normal");
> +
> +	gnss_exec_stored_param(modem, enable ? "gnss_powersave" :
> +								"gnss_normal");
> +
> +	if (!g_at_chat_send(data->app, "AT", none_prefix,
> +				gemalto_powersave_cb, modem, NULL))
> +		return __ofono_error_failed(msg);
> +
> +	data->hc_msg = dbus_message_ref(msg);
> +	return NULL;
> +}
> +
> +static void gemalto_smso_cb(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	DBusMessage *reply;
> +
> +	if (data->hc_msg == NULL)
> +		return;
> +
> +	if (data->conn != GEMALTO_CONNECTION_SERIAL)
> +		goto finished;
> +
> +	if (data->mdm)
> +		g_at_chat_unref(data->mdm);
> +	data->mdm = NULL;
> +
> +	if (data->app)
> +		g_at_chat_unref(data->app);
> +	data->app = NULL;
> +
> +	if (ok)
> +		ofono_modem_set_powered(modem, FALSE);
> +
> +finished:
> +	reply = dbus_message_new_method_return(data->hc_msg);
> +	__ofono_dbus_pending_reply(&data->hc_msg, reply);
> +}
> +
> +static DBusMessage *hc_shutdown(DBusConnection *conn,
> +					DBusMessage *msg, void *user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +
> +	if (data->hc_msg != NULL)
> +		return __ofono_error_busy(msg);
> +
> +	if (!g_at_chat_send(data->app, "AT^SMSO", none_prefix,
> +						gemalto_smso_cb, modem, NULL))
> +		return __ofono_error_failed(msg);
> +
> +	data->hc_msg = dbus_message_ref(msg);
> +	return NULL;
> +}
> +
> +static void gemalto_detect_sysstart(GAtResult *result, gpointer user_data);
> +
> +static void gemalto_reset_cb(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	DBusMessage *reply;
> +
> +	if (data->hc_msg == NULL)
> +		return;
> +
> +	if (data->conn != GEMALTO_CONNECTION_SERIAL)
> +		goto finished;
> +
> +	data->modem_ready_id = g_at_chat_register(data->app,
> +		"^SYSSTART", gemalto_detect_sysstart, FALSE,
> +		modem, NULL);
> +
> +finished:
> +	reply = dbus_message_new_method_return(data->hc_msg);
> +	__ofono_dbus_pending_reply(&data->hc_msg, reply);
> +}
> +
> +static DBusMessage *hc_reset(DBusConnection *conn,
> +					DBusMessage *msg, void *user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +
> +	if (data->hc_msg != NULL)
> +		return __ofono_error_busy(msg);
> +
> +	if (!g_at_chat_send(data->app, "AT+CFUN=1,1", none_prefix,
> +						gemalto_reset_cb, modem, NULL))
> +		return __ofono_error_failed(msg);
> +
> +	data->hc_msg = dbus_message_ref(msg);
> +	return NULL;
> +}
> +
> +static const GDBusMethodTable hardware_control_methods[] = {
> +	{ GDBUS_ASYNC_METHOD("GetProperties",
> +			NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
> +			hc_get_properties) },
> +	{ GDBUS_ASYNC_METHOD("SetProperty",
> +			GDBUS_ARGS({ "property", "s" }, { "value", "v" }),
> +			NULL, hc_set_property) },
> +	{ GDBUS_ASYNC_METHOD("Shutdown",
> +			NULL, NULL, hc_shutdown) },
> +	{ GDBUS_ASYNC_METHOD("Reset",
> +			NULL, NULL, hc_reset) },
> +	{ }
> +};
> +
> +static void gemalto_hardware_control_enable(struct ofono_modem *modem)
> +{
> +	DBusConnection *conn = ofono_dbus_get_connection();
> +	const char *path = ofono_modem_get_path(modem);
> +
> +	/* Create Hardware Control DBus interface */
> +	if (!g_dbus_register_interface(conn, path, HARDWARE_CONTROL_INTERFACE,
> +					hardware_control_methods,
> +					NULL,
> +					NULL,
> +					modem,
> +					NULL)) {
> +		ofono_error("Could not register %s interface under %s",
> +					HARDWARE_CONTROL_INTERFACE, path);
> +		return;
> +	}
> +
> +	ofono_modem_add_interface(modem, HARDWARE_CONTROL_INTERFACE);
> +}
> +
> +/*******************************************************************************
> + * modem plugin
> + ******************************************************************************/
> +
> +#ifdef HAVE_ELL
> +static int mbim_parse_descriptors(struct gemalto_data *md, const char *file)

Why is MBIM driver not enough?

> +{
> +	void *data;
> +	size_t len;
> +	const struct mbim_desc *desc = NULL;
> +	const struct mbim_extended_desc *ext_desc = NULL;
> +
> +	data = l_file_get_contents(file, &len);
> +	if (!data)
> +		return -EIO;
> +
> +	if (!mbim_find_descriptors(data, len, &desc, &ext_desc)) {
> +		l_free(data);
> +		return -ENOENT;
> +	}
> +
> +	if (desc)
> +		md->max_segment = L_LE16_TO_CPU(desc->wMaxControlMessage);
> +
> +	if (ext_desc)
> +		md->max_outstanding = ext_desc->bMaxOutstandingCommandMessages;
> +
> +	l_free(data);
> +	return 0;
> +}
> +
> +static int mbim_probe(struct ofono_modem *modem, struct gemalto_data *data)
> +{
> +	const char *descriptors;
> +	int err;
> +
> +	descriptors = gemalto_get_string(modem, "DescriptorFile");
> +
> +	if (!descriptors)
> +		return -EINVAL;
> +
> +	data->max_outstanding = 1;
> +
> +	err = mbim_parse_descriptors(data, descriptors);
> +	if (err < 0) {
> +		DBG("Warning, unable to load descriptors, setting defaults");
> +		data->max_segment = 512;
> +	}
> +
> +	DBG("MaxSegment: %d, MaxOutstanding: %d",
> +		data->max_segment, data->max_outstanding);
> +
> +	return 0;
> +}
> +#endif
> +
> +static int gemalto_probe(struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data;
> +
> +	data = g_try_new0(struct gemalto_data, 1);
> +	if (data == NULL)
> +		return -ENOMEM;
> +
> +#ifdef HAVE_ELL
> +	mbim_probe(modem, data);
> +#endif
> +
> +	ofono_modem_set_data(modem, data);
> +
> +	return 0;
> +}
> +
> +static void gemalto_remove(struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data;
> +	DBusConnection *conn = ofono_dbus_get_connection();
> +	const char *path;
> +
> +	if (!modem)
> +		return;
> +
> +	data = ofono_modem_get_data(modem);
> +	path = ofono_modem_get_path(modem);
> +
> +	if (!data)
> +		return;
> +
> +#ifdef HAVE_ELL
> +	if (data->mbim == STATE_PRESENT) {
> +		mbim_device_shutdown(data->device);
> +	}
> +#endif
> +
> +	if (data->qmi == STATE_PRESENT) {
> +		qmi_device_unref(data->device);
> +	}
> +

No, we're not doing MBIM/QMI/AT all in one driver.  These should 
basically be 3 different drivers or merged into plugins/mbim 
plugins/gobi or whatever.

> +	if (data->app) {
> +		/* Cleanup potential SIM state polling */
> +		at_util_sim_state_query_free(data->sim_state_query);
> +		data->sim_state_query = NULL;
> +
> +		g_at_chat_cancel_all(data->app);
> +		g_at_chat_unregister_all(data->app);
> +		g_at_chat_unref(data->app);
> +		data->app = NULL;
> +	}
> +
> +	if (data->mdm) {
> +		g_at_chat_cancel_all(data->app);
> +		g_at_chat_unregister_all(data->mdm);
> +		g_at_chat_unref(data->mdm);
> +		data->mdm = NULL;
> +	}
> +
> +	if (conn && path) {
> +		if (g_dbus_unregister_interface(conn, path,
> +					HARDWARE_MONITOR_INTERFACE))
> +			ofono_modem_remove_interface(modem,
> +					HARDWARE_MONITOR_INTERFACE);
> +
> +		if (g_dbus_unregister_interface(conn, path,
> +					CINTERION_LEGACY_HWMON_INTERFACE))
> +			ofono_modem_remove_interface(modem,
> +					CINTERION_LEGACY_HWMON_INTERFACE);
> +
> +		if (g_dbus_unregister_interface(conn, path,
> +					GEMALTO_NITZ_TIME_INTERFACE))
> +			ofono_modem_remove_interface(modem,
> +					GEMALTO_NITZ_TIME_INTERFACE);
> +
> +		if (g_dbus_unregister_interface(conn, path,
> +					COMMAND_PASSTHROUGH_INTERFACE))
> +			ofono_modem_remove_interface(modem,
> +					COMMAND_PASSTHROUGH_INTERFACE);
> +
> +		if (g_dbus_unregister_interface(conn, path,
> +					GNSS_INTERFACE))
> +			ofono_modem_remove_interface(modem,
> +					GNSS_INTERFACE);
> +
> +		if (g_dbus_unregister_interface(conn, path,
> +					HARDWARE_CONTROL_INTERFACE))
> +			ofono_modem_remove_interface(modem,
> +					HARDWARE_CONTROL_INTERFACE);
> +	}
> +
> +	//if (data->conn == GEMALTO_CONNECTION_SERIAL)
> +	//	return;
> +
> +	ofono_modem_set_data(modem, NULL);
> +	g_free(data);
> +}
> +
> +static void sim_ready_cb(gboolean present, gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	struct ofono_sim *sim = data->sim;
> +
> +	at_util_sim_state_query_free(data->sim_state_query);
> +	data->sim_state_query = NULL;
> +
> +	DBG("sim present: %d", present);
> +
> +	ofono_sim_inserted_notify(sim, present);
> +}
> +
> +static void gemalto_ciev_simstatus_notify(GAtResultIter *iter,
> +					struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	struct ofono_sim *sim = data->sim;
> +	int status;
> +
> +	DBG("sim status %d", status);
> +
> +	if (!g_at_result_iter_next_number(iter, &status))
> +		return;
> +
> +	switch (status) {
> +	/* SIM is removed from the holder */
> +	case 0:
> +		ofono_sim_inserted_notify(sim, FALSE);
> +		break;
> +
> +	/* SIM is inserted inside the holder */
> +	case 1:
> +		/* The SIM won't be ready yet */
> +		data->sim_state_query = at_util_sim_state_query_new(data->app,
> +					1, 20, sim_ready_cb, modem,
> +					NULL);
> +		break;
> +
> +	/* USIM initialization completed. UE has finished reading USIM data. */
> +	case 5:
> +		ofono_sim_initialized_notify(sim);
> +		break;
> +
> +	default:
> +		break;
> +	}
> +}
> +
> +static void gemalto_ciev_nitz_notify(GAtResultIter *iter,
> +					struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	const char *nitz_data;
> +	char buf[32];
> +
> +	/* Example: +CIEV: nitz,<time>,<timezone>,<daylight> */
> +	if (!g_at_result_iter_next_string(iter, &nitz_data))
> +		return;
> +
> +	DBG("nitz_data  %s", nitz_data);
> +
> +	sprintf(buf, "AT+CCLK=\"%s\"", nitz_data);
> +	g_at_chat_send(data->app, buf, none_prefix, NULL, NULL, NULL);
> +
> +	gemalto_signal(GEMALTO_NITZ_TIME_INTERFACE, "NitzUpdated", nitz_data,
> +									modem);
> +}
> +
> +static void gemalto_ciev_notify(GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +
> +	const char *sim_status = "simstatus";
> +	const char *nitz_status = "nitz";
> +	const char *ind_str;
> +	GAtResultIter iter;
> +
> +	g_at_result_iter_init(&iter, result);
> +
> +	/* Example: +CIEV: simstatus,<status> */
> +	if (!g_at_result_iter_next(&iter, "+CIEV:"))
> +		return;
> +
> +	if (!g_at_result_iter_next_unquoted_string(&iter, &ind_str))
> +		return;
> +
> +	if (g_str_equal(sim_status, ind_str)) {
> +		gemalto_ciev_simstatus_notify(&iter, modem);
> +	} else if (g_str_equal(nitz_status, ind_str)) {
> +		gemalto_ciev_nitz_notify(&iter, modem);
> +	}
> +}
> +
> +static void sim_state_cb(gboolean present, gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +
> +	at_util_sim_state_query_free(data->sim_state_query);
> +	data->sim_state_query = NULL;
> +
> +	data->have_sim = present;
> +	ofono_modem_set_powered(modem, TRUE);
> +
> +	/* Register for specific sim status reports */
> +	g_at_chat_register(data->app, "+CIEV:",
> +			gemalto_ciev_notify, FALSE, modem, NULL);
> +
> +	g_at_chat_send(data->app, "AT^SIND=\"simstatus\",1", none_prefix,
> +			NULL, NULL, NULL);
> +	g_at_chat_send(data->app, "AT^SIND=\"nitz\",1", none_prefix,
> +			NULL, NULL, NULL);
> +}
> +
> +static void gemalto_exit_urc_notify(GAtResult *result, gpointer user_data)
> +{
> +	GAtResultIter iter;
> +	const char *error_message;
> +
> +	g_at_result_iter_init(&iter, result);
> +	g_at_result_iter_next(&iter, "^EXIT:");
> +	g_at_result_iter_next_unquoted_string(&iter, &error_message);
> +	ofono_error("Modem exited! Cause: %s", error_message);
> +}
> +
> +static void saic_probe(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(user_data);
> +
> +	if (ok)
> +		data->voice_avail = TRUE;
> +	else
> +		data->voice_avail = FALSE;
> +}
> +
> +static void sgauth_probe(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(user_data);
> +
> +	if (ok)
> +		data->auth_syntax = GEMALTO_AUTH_USE_SGAUTH |
> +						GEMALTO_AUTH_ORDER_PWD_USR;
> +	else
> +		data->auth_syntax = GEMALTO_AUTH_DEFAULTS;
> +}
> +
> +static void gemalto_set_cfun_cb(gboolean ok, GAtResult *result,
> +					gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +
> +	if (!ok || data->cfun == 41) {
> +		g_at_chat_cancel_all(data->app);
> +		ofono_modem_set_powered(modem, FALSE);
> +	} else {
> +		data->sim_state_query = at_util_sim_state_query_new(data->app,
> +					2, 20, sim_state_cb, modem, NULL);
> +	}
> +}
> +
> +static void gemalto_cfun_query(gboolean ok, GAtResult *result,
> +							gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(user_data);
> +	char buf[256];
> +	GAtResultIter iter;
> +	int mode;
> +
> +	sprintf(buf, "AT+CFUN=%d", data->cfun==41?4:data->cfun);
> +
> +	if (!ok)
> +		goto error;
> +
> +	g_at_result_iter_init(&iter, result);
> +
> +	if (!g_at_result_iter_next(&iter, "+CFUN:"))
> +		goto error;
> +
> +	if (!g_at_result_iter_next_number(&iter, &mode))
> +		goto error;
> +
> +	if (mode == data->cfun)
> +		sprintf(buf, "AT");
> +
> +error:
> +	if (g_at_chat_send(data->app, buf, none_prefix, gemalto_set_cfun_cb,
> +				modem, NULL) > 0)
> +		return;
> +
> +	if (data->cfun == 41)
> +		ofono_modem_set_powered(modem, FALSE);
> +}
> +
> +static void gemalto_set_cfun(GAtChat *app, int mode, struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +
> +	data->cfun=mode;
> +	g_at_chat_send(app, "AT+CFUN?", cfun_prefix, gemalto_cfun_query, modem, NULL);
> +}
> +
> +static void gemalto_initialize(struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	char *urcdest;
> +	guint m = data->model;
> +
> +	DBG("app:%d, mdm:%d, mbim:%d, qmi:%d",
> +		data->app!=NULL,
> +		data->mdm!=NULL,
> +		data->mbim == STATE_PRESENT,
> +		data->qmi == STATE_PRESENT);
> +
> +	if (!data->app  && !data->mdm) {
> +		DBG("no AT interface available. Removing this device.");
> +		ofono_modem_set_powered(modem, FALSE);
> +		return;
> +	}
> +
> +	urcdest = "AT^SCFG=\"URC/DstIfc\",\"app\"";
> +
> +	if (!data->app) {
> +		data->use_mdm_for_app = TRUE;
> +		data->app = data->mdm;
> +		urcdest = "AT^SCFG=\"URC/DstIfc\",\"mdm\"";
> +	}
> +
> +	if (!data->mdm && (data->gina == STATE_PRESENT)) {
> +		/*these modems can start PPP from any port*/
> +		data->mdm = data->app;
> +	}
> +
> +	if (data->mdm && data->gprs_opt == NO_GPRS)
> +		data->gprs_opt = USE_PPP;
> +
> +	g_at_chat_set_wakeup_command(data->app, "AT\r", 1000, 5000);
> +
> +	g_at_chat_send(data->app, "ATE0", none_prefix, NULL, NULL, NULL);
> +
> +	if (data->gina != STATE_PRESENT)
> +		g_at_chat_send(data->app, urcdest, none_prefix, NULL, NULL,
> +									NULL);
> +
> +	/* numeric error codes are interpreted by atmodem/atutil.c functions */
> +	g_at_chat_send(data->app, "AT+CMEE=1", none_prefix, NULL, NULL, NULL);
> +
> +	if (data->mdm)
> +		g_at_chat_send(data->mdm, "AT&C0", none_prefix, NULL, NULL,
> +									NULL);
> +
> +	g_at_chat_send(data->app, "AT&C0", none_prefix, NULL, NULL, NULL);
> +
> +	/* watchdog */
> +	g_at_chat_register(data->app, "^EXIT", gemalto_exit_urc_notify, FALSE,
> +								modem, NULL);
> +	ofono_devinfo_create(modem, OFONO_VENDOR_GEMALTO, "atmodem", data->app);
> +	g_at_chat_send(data->app,
> +		"AT^SCFG=\"MEopMode/PwrSave\",\"enabled\",52,50", none_prefix,
> +							NULL, NULL, NULL);
> // this command was in some gemalto.c versions, but it would better be
> // placed in one of the configuration files
> +
> +	if (m != 0x5b && m != 0x5c && m != 0x5d && m != 0xa0) {
> +		g_at_chat_send(data->app, "AT^SGAUTH?", NULL, sgauth_probe,
> +								modem, NULL);
> +	}
> +
> +	g_at_chat_send(data->app, "AT^SAIC?", NULL, saic_probe, modem, NULL);
> +
> +	gemalto_exec_stored_cmd(modem, "enable");
> +
> +	gemalto_command_passthrough_enable(modem);
> +	gemalto_hardware_monitor_enable(modem);
> +	gemalto_time_enable(modem);
> +	gemalto_gnss_enable(modem);
> +	gemalto_hardware_control_enable(modem);
> +
> +	gemalto_set_cfun(data->app, 4, modem);
> +	data->init_done = TRUE;
> +}
> +
> +static gboolean gemalto_open_cb(GIOChannel *source, GIOCondition condition,
> +							gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	GAtSyntax *syntax;
> +
> +	if (data->channel == NULL)
> +		return TRUE;
> +
> +	if ((condition & G_IO_IN) == 0)
> +		return TRUE;
> +
> +	g_source_remove(data->probing_timer);
> +	data->probing_timer = 0;
> +	g_source_remove(data->read_src);
> +	g_io_channel_flush(data->channel, NULL);
> +	/* reset channel defaults*/
> +	g_io_channel_set_buffered(data->channel, TRUE);
> +	g_io_channel_set_encoding(data->channel, "UTF-8", NULL);
> +
> +	syntax = g_at_syntax_new_gsm_permissive();
> +	data->tmp_chat = g_at_chat_new(data->channel, syntax);
> +	g_at_syntax_unref(syntax);
> +
> +	if (data->tmp_chat == NULL)
> +		goto failed;
> +
> +	g_io_channel_unref(data->channel);
> +	data->channel = NULL;
> +	g_at_chat_set_debug(data->tmp_chat, gemalto_debug, "App: ");
> +	data->open_cb(TRUE, modem);
> +	return TRUE; // finished
> +failed:
> +	DBG("chat creation failed. aborting.");
> +	g_io_channel_unref(data->channel);
> +	data->channel = NULL;
> +	DBG("aborted.");
> +	data->open_cb(FALSE, modem);
> +	return FALSE; // abort
> +}
> +
> +static int gemalto_probe_device(void *user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	GIOStatus status;
> +
> +	if (data->channel==NULL)
> +		return FALSE;
> +
> +	data->waiting_time++;
> +	DBG("%d/%d", data->waiting_time, data->init_waiting_time);
> +
> +	if (data->waiting_time > data->init_waiting_time) {
> +		data->waiting_time = 0;
> +		goto failed;
> +	}
> +
> +	status = g_io_channel_write_chars(data->channel, "AT\r", 3, NULL, NULL);
> +
> +	if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN)
> +		goto failed;
> +
> +	return TRUE; // to be called again
> +
> +failed:
> +	g_source_remove(data->probing_timer);
> +	data->probing_timer = 0; /* remove the timer reference */
> +	DBG("timeout: abort");
> +	g_io_channel_unref(data->channel);
> +	data->channel = NULL;
> +	data->tmp_chat = NULL;
> +	data->open_cb(FALSE, modem);
> +	return FALSE; // abort
> +}
> +
> +#include <asm/ioctls.h>
> +#include <linux/serial.h>
> +
> +int ioctl(int, int, void *);
> // these are temporarily in the middle. Discussed with Denis,
> // maybe these and their use below is better placed in g_at_tty_open
> +
> +static void gemalto_open_device(const char *device,
> +				OpenResultFunc func, struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	GHashTable *options;
> +	int fd;
> +	struct serial_struct old, new;
> +
> +	if (!device  || !*device) {
> +		func(FALSE, modem);
> +		return;
> +	}
> +
> +	options = g_hash_table_new(g_str_hash, g_str_equal);
> +	if (options == NULL) {
> +		func(FALSE, modem);
> +		return;
> +	}
> +
> +	g_hash_table_insert(options, "Baud", "115200");
> +	g_hash_table_insert(options, "StopBits", "1");
> +	g_hash_table_insert(options, "DataBits", "8");
> +	g_hash_table_insert(options, "Parity", "none");
> +	g_hash_table_insert(options, "XonXoff", "off");
> +	g_hash_table_insert(options, "RtsCts", "on");
> +	g_hash_table_insert(options, "Local", "on");
> +	g_hash_table_insert(options, "Read", "on");
> +
> +	DBG("Opening device %s", device);
> +
> +	data->channel = g_at_tty_open(device, options);
> +	g_hash_table_destroy(options);
> +
> +	if (!data->channel) {
> +		func(FALSE, modem);
> +		return;
> +	}
> +
> +	fd = g_io_channel_unix_get_fd(data->channel);
> +	ioctl(fd, TIOCGSERIAL, &old);
> +	new = old;
> +	new.closing_wait = ASYNC_CLOSING_WAIT_NONE;
> +	ioctl(fd, TIOCSSERIAL, &new);
> // without this, even if the g_at_tty_open sets the port non-blocking,
> // it waits anyway 30 seconds if the port is not responding.
> +
> +	g_io_channel_flush(data->channel, NULL);
> +	/* the channel is set by default to "UTF-8" and buffered */
> +	g_io_channel_set_encoding(data->channel, NULL, NULL);
> +	g_io_channel_set_buffered(data->channel, FALSE);
> +	data->open_cb = func;
> +	data->read_src = g_io_add_watch(data->channel, G_IO_IN, gemalto_open_cb,
> +									modem);
> +	data->probing_timer = g_timeout_add_seconds(1, gemalto_probe_device,
> +									modem);
> +}
> // the 3 functions above:
> // - open the channel as usual
> // - test the port with AT once per second until timeout. Maybe this can be an
> //   additional wakeup function in GAtChat
> // - attach a GAtChat to it or close it
> +
> +static void gemalto_enable_mdm_cb(gboolean success, struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +
> +	data->mdm = data->tmp_chat;
> +	data->tmp_chat = NULL;
> +	gemalto_initialize(modem);
> +}
> +
> +static void gemalto_enable_app_cb(gboolean success, struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	const char *mdm = gemalto_get_string(modem, "Modem");
> +
> +	data->app = data->tmp_chat;
> +	data->tmp_chat = NULL;
> +	gemalto_open_device(mdm, gemalto_enable_mdm_cb, modem);
> +}
> +
> +static int gemalto_enable_app(struct ofono_modem *modem)
> +{
> +	const char *app = gemalto_get_string(modem, "Application");
> +
> +	gemalto_open_device(app, gemalto_enable_app_cb, modem);
> +	return -EINPROGRESS;
> +}
> +
> +#ifdef HAVE_ELL
> +static void mbim_device_caps_info_cb(struct mbim_message *message, void *user)
> +{
> +	struct ofono_modem *modem = user;
> +	struct gemalto_data *md = ofono_modem_get_data(modem);
> +	uint32_t device_type;
> +	uint32_t cellular_class;
> +	uint32_t voice_class;
> +	uint32_t sim_class;
> +	uint32_t data_class;
> +	uint32_t sms_caps;
> +	uint32_t control_caps;
> +	uint32_t max_sessions;
> +	char *custom_data_class;
> +	char *device_id;
> +	char *firmware_info;
> +	char *hardware_info;
> +	bool r;
> +
> +	if (mbim_message_get_error(message) != 0)
> +		goto error;
> +
> +	r = mbim_message_get_arguments(message, "uuuuuuuussss",
> +					&device_type, &cellular_class,
> +					&voice_class, &sim_class, &data_class,
> +					&sms_caps, &control_caps, &max_sessions,
> +					&custom_data_class, &device_id,
> +					&firmware_info, &hardware_info);
> +	if (!r)
> +		goto error;
> +
> +	md->max_sessions = max_sessions;
> +
> +	DBG("DeviceId: %s", device_id);
> +	DBG("FirmwareInfo: %s", firmware_info);
> +	DBG("HardwareInfo: %s", hardware_info);
> +
> +	ofono_modem_set_string(modem, "DeviceId", device_id);
> +	ofono_modem_set_string(modem, "FirmwareInfo", firmware_info);
> +
> +	l_free(custom_data_class);
> +	l_free(device_id);
> +	l_free(firmware_info);
> +	l_free(hardware_info);
> +
> +	message = mbim_message_new(mbim_uuid_basic_connect,
> +					MBIM_CID_DEVICE_SERVICE_SUBSCRIBE_LIST,
> +					MBIM_COMMAND_TYPE_SET);
> +
> +	mbim_message_set_arguments(message, "av", 2,
> +					"16yuuuuuuu",
> +					mbim_uuid_basic_connect, 6,
> +					MBIM_CID_SUBSCRIBER_READY_STATUS,
> +					MBIM_CID_RADIO_STATE,
> +					MBIM_CID_REGISTER_STATE,
> +					MBIM_CID_PACKET_SERVICE,
> +					MBIM_CID_SIGNAL_STATE,
> +					MBIM_CID_CONNECT,
> +					"16yuuuu", mbim_uuid_sms, 3,
> +					MBIM_CID_SMS_CONFIGURATION,
> +					MBIM_CID_SMS_READ,
> +					MBIM_CID_SMS_MESSAGE_STORE_STATUS);
> +
> +	if (mbim_device_send(md->device, 0, message,
> +				NULL, NULL, NULL)) {
> +		md->mbim = STATE_PRESENT;
> +		goto other_devices;
> +	}
> +
> +
> +error:
> +	mbim_device_shutdown(md->device);
> +
> +other_devices:
> +
> +	if (md->init_done)
> +		return;
> +
> +	gemalto_enable_app(modem);  /* continue with mdm interface */
> +}
> +
> +static void mbim_device_ready(void *user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *md = ofono_modem_get_data(modem);
> +	struct mbim_message *message =
> +		mbim_message_new(mbim_uuid_basic_connect,
> +					1, MBIM_COMMAND_TYPE_QUERY);
> +
> +	mbim_message_set_arguments(message, "");
> +	mbim_device_send(md->device, 0, message, mbim_device_caps_info_cb,
> +		modem, NULL);
> +}
> +
> +static void mbim_device_closed(void *user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *md = ofono_modem_get_data(modem);
> +
> +	if (!md)
> +		return;
> +
> +	/*
> +	 * if state=probe, it  means that we are in the init phase
> +	 * and that we have failed the MBIM_OPEN
> +	 */
> +	if (md->mbim == STATE_PROBE) {
> +		DBG(REDCOLOR"MBIM OPEN failed!"NOCOLOR);
> +		gemalto_enable_app(modem); /* continue with other interfaces */
> +	}
> +
> +	/* reset the state for future attempts */
> +	md->mbim = STATE_PROBE;
> +
> + 	if(md->device)
> +		mbim_device_unref(md->device);
> +
> +	md->device = NULL;
> +}
> +
> +static int mbim_enable(struct ofono_modem *modem)
> +{
> +	const char *device;
> +	int fd;
> +	struct gemalto_data *md = ofono_modem_get_data(modem);
> +
> +	DBG("modem struct: %p", modem);
> +
> +	device = gemalto_get_string(modem, "NetworkControl");
> +	if (!device)
> +		goto other_devices;
> +
> +	DBG("modem device: %s", device);
> +	fd = open(device, O_EXCL | O_NONBLOCK | O_RDWR);
> +
> +	if (fd < 0)
> +		goto other_devices;
> +
> +	DBG("device: %s opened successfully", device);
> +	md->device = mbim_device_new(fd, md->max_segment);
> +	DBG("created new device %p", md->device);
> +
> +	mbim_device_set_close_on_unref(md->device, true);
> +	mbim_device_set_max_outstanding(md->device, md->max_outstanding);
> +	mbim_device_set_ready_handler(md->device,
> +					mbim_device_ready, modem, NULL);
> +	mbim_device_set_disconnect_handler(md->device,
> +				mbim_device_closed, modem, NULL);
> +	mbim_device_set_debug(md->device, gemalto_debug, "MBIM:", NULL);
> +
> +	return -EINPROGRESS;
> +
> +other_devices:
> +
> +	if (md->init_done)
> +		return 0;
> +
> +	return gemalto_enable_app(modem);
> +}
> +#endif
> +
> +static void qmi_enable_cb(void *user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *md = ofono_modem_get_data(modem);
> +	md->qmi = STATE_PRESENT;
> +	gemalto_enable_app(modem); /* qmi done, continue with app interface */
> +}
> +
> +static int qmi_enable(struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	const char *device;
> +	int fd;
> +
> +	DBG("modem struct: %p", modem);
> +
> +	device = gemalto_get_string(modem, "NetworkControl");
> +	if (!device)
> +		return gemalto_enable_app(modem);
> +
> +	fd = open(device, O_RDWR | O_NONBLOCK | O_CLOEXEC);
> +	if (fd < 0)
> +		return gemalto_enable_app(modem);
> +
> +	data->device = qmi_device_new(fd);
> +	if (!data->device) {
> +		close(fd);
> +		return gemalto_enable_app(modem);
> +	}
> +
> +	qmi_device_set_close_on_unref(data->device, true);
> +
> +	qmi_device_set_debug(data->device, gemalto_debug, "QMI: ");
> +
> +	qmi_device_discover(data->device, qmi_enable_cb, modem, NULL);
> +
> +	return -EINPROGRESS;
> +}
> +
> +static void set_from_model(struct gemalto_data *data) {
> +	guint m = data->model;
> +
> +	data->has_lte = TRUE; /* default */
> +
> +	/* pre-configure non-MBIM network interfaces */
> +	if (m != 0x62 && m != 0x5d && m != 0x65) {
> +		/*
> +		 * note: we probe for ECM/NCM even if the port is not present
> +		 * (for serial connection type or serial-like)
> +		 */
> +		if (m == 0x53 || m == 0x60 || m == 0x63)
> +			data->qmi = STATE_PROBE;
> +		/*these families have PPP only*/
> +		else if (m != 0x58 && m != 0x47 && m != 0x54)
> +			data->ecmncm = STATE_PROBE;
> +	}
> +
> +	/* pre-configure SW features */
> +	if (m == 0xa0) {
> +		data->gprs_opt = USE_CTX3;
> +		data->ecmncm = STATE_ABSENT;
> +	}
> +	if (m == 0x63 || m == 0x65 || m == 0x5b || m == 0x5c || m == 0x5d)
> +		data->gina = STATE_PRESENT;
> +
> +	data->init_waiting_time = 30;
> +
> +	if (m == 0x55 || m == 0x47) {
> +		data->has_lte = FALSE;
> +		data->init_waiting_time = 5;
> +	}
> +
> +	if (m == 0x58) {
> +		data->has_lte = FALSE;
> +		data->init_waiting_time = 15;
> +	}
> +
> +	data->vts_with_quotes = TRUE;
> +
> +	if (m == 0x5b || m == 0x5c || m == 0x5d || m == 0xa0) {
> +		data->vts_with_quotes = FALSE;
> +		data->auth_syntax = GEMALTO_AUTH_USE_SGAUTH |
> +						GEMALTO_AUTH_ALWAYS_ALL_PARAMS;
> +	}
> +}
> +
> +static void store_cgmm(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	GAtResultIter iter;
> +	char const *model;
> +	char buf[16];
> +
> +	/* if no model, fallback to a basic 2G one */
> +	data->model = 0x47;
> +	strncpy(data->modelstr, "", sizeof(data->modelstr));
> +
> +	if (!ok)
> +		return;
> +
> +	g_at_result_iter_init(&iter, result);
> +
> +	while (g_at_result_iter_next(&iter, NULL)) {
> +		if (!g_at_result_iter_next_unquoted_string(&iter, &model))
> +			continue;
> +
> +		if (model && *model) {
> +			strncpy(data->modelstr, model, sizeof(data->modelstr));
> +
> +			if (g_ascii_strncasecmp(model, "TC", 2) == 0)
> +				data->model = 0x47;
> +			else if (g_ascii_strncasecmp(model, "MC", 2) == 0)
> +				data->model = 0x47;
> +			else if (g_ascii_strncasecmp(model, "AC", 2) == 0)
> +				data->model = 0x47;
> +			else if (g_ascii_strncasecmp(model, "HC", 2) == 0)
> +				data->model = 0x47;
> +			else if (g_ascii_strncasecmp(model, "HM", 2) == 0)
> +				data->model = 0x47;
> +			else if (g_ascii_strncasecmp(model, "XT", 2) == 0)
> +				data->model = 0x47;
> +			else if (g_ascii_strncasecmp(model, "AGS", 3) == 0)
> +				data->model = 0x47;
> +			else if (g_ascii_strncasecmp(model, "BGS", 3) == 0)
> +				data->model = 0x47;
> +			else if (g_ascii_strncasecmp(model, "AH3", 3) == 0)
> +				data->model = 0x55;
> +			else if (g_ascii_strncasecmp(model, "AHS", 3) == 0)
> +				data->model = 0x55;
> +			else if (g_ascii_strncasecmp(model, "PHS", 3) == 0)
> +				data->model = 0x55;
> +			else if (g_ascii_strncasecmp(model, "PH8", 3) == 0)
> +				data->model = 0x55;
> +			else if (g_ascii_strncasecmp(model, "AHS", 3) == 0)
> +				data->model = 0x55;
> +			else if (g_ascii_strncasecmp(model, "EHS", 3) == 0)
> +				data->model = 0x58;
> +			else if (g_ascii_strncasecmp(model, "ELS31-", 6) == 0)
> +				data->model = 0xa0;
> +			else if (g_ascii_strncasecmp(model, "ELS61-", 6) == 0)
> +				data->model = 0x5b;
> +			else if (g_ascii_strncasecmp(model, "PLS62-", 6) == 0)
> +				data->model = 0x5b;
> +			else if (g_ascii_strncasecmp(model, "PLS8-", 5) == 0)
> +				data->model = 0x61;
> +			else if (g_ascii_strncasecmp(model, "ALS3-", 5) == 0)
> +				data->model = 0x61;
> +			else if (g_ascii_strncasecmp(model, "ALAS5-", 6) == 0)
> +				data->model = 0x65;
> +			return;
> +		}
> +	}
> +
> +	sprintf(buf, "%04x", data->model);
> +	ofono_modem_set_string(modem, "Model", buf);
> +}
> +
> +static void store_sqport(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	GAtResultIter iter;
> +	char const *sqport;
> +
> +	/* in case of error, the port is of Modem type */
> +	strncpy(data->sqport, "Modem", sizeof(data->sqport));
> +
> +	if (!ok)
> +		goto done;
> +
> +	g_at_result_iter_init(&iter, result);
> +
> +	/* answer format: "^SQPORT: Application" */
> +	if (!g_at_result_iter_next(&iter, "^SQPORT:"))
> +		goto done;
> +
> +	if (!g_at_result_iter_next_unquoted_string(&iter, &sqport))
> +		goto done;
> +
> +	if (!sqport || !*sqport)
> +		goto done;
> +
> +	strncpy(data->sqport, sqport, sizeof(data->sqport));
> +
> +done:
> +	/* select mdm, app or gina port type */
> +	data->ecmncm = STATE_PROBE;
> +
> +	if (g_str_equal(sqport, "Modem")) {
> +		data->mdm = data->app;
> +		data->app = NULL;
> +		data->ecmncm = STATE_ABSENT;
> +	}
> +
> +	if ((*sqport >= '0' && *sqport <= '9'))
> +		data->gina = STATE_PRESENT;
> +
> +	set_from_model(data);
> +	gemalto_initialize(modem);
> +}
> +
> +static void gemalto_detect_serial(gboolean success, struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +
> +	data->app = data->tmp_chat;
> +	data->tmp_chat = NULL;
> +
> +	g_at_chat_send(data->app, "AT+CGMM", none_prefix, store_cgmm,
> +								modem, NULL);
> +	g_at_chat_send(data->app, "AT^SQPORT", sqport_prefix, store_sqport,
> +								modem, NULL);
> +}
> +
> +static void gemalto_detect_sysstart(GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +
> +	if (data->modem_ready_id) {
> +		g_at_chat_unregister(data->app, data->modem_ready_id);
> +		data->modem_ready_id = 0;
> +	}
> +
> +	data->tmp_chat = data->app;
> +	gemalto_detect_serial(TRUE, modem);
> +}
> +
> +static int gemalto_enable_serial(struct ofono_modem *modem)
> +{
> +	const char *device = ofono_modem_get_string(modem, "ATport");
> +
> +	if (!device)
> +		return -EINVAL;
> +
> +	gemalto_open_device(device, gemalto_detect_serial, modem);
> +	return -EINPROGRESS;
> +}
> +
> +static int gemalto_enable(struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	const char *model = gemalto_get_string(modem, "Model"),
> +		   *conn_type = gemalto_get_string(modem, "ConnType");
> +	const char
> +		*ctl = gemalto_get_string(modem, "NetworkControl"),
> +		*net = gemalto_get_string(modem, "NetworkInterface");
> +	guint m = 0;
> +
> +	if (!modem || !data)
> +		return -EINVAL;
> +
> +	data->conn = g_str_equal(conn_type,"Serial") ? GEMALTO_CONNECTION_SERIAL
> +						: GEMALTO_CONNECTION_USB;
> +
> +	if (data->conn == GEMALTO_CONNECTION_SERIAL)
> +		return gemalto_enable_serial(modem);
> +
> +	DBG("modem struct: %p, gemalto_data: %p", modem, data);
> +
> +	if (data->init_done) {
> +		gemalto_set_cfun(data->app, 4, modem);
> +
> +#ifdef HAVE_ELL
> +		if (data->mbim != STATE_ABSENT)
> +			mbim_enable(modem);
> +#endif
> +
> +		return -EINPROGRESS;
> +	}
> +
> +	if (model) {
> +		data->model = strtoul(model, NULL, 16);
> +		m = data->model;
> +	}
> +
> +	/* single ACM interface 02: assign application to modem */
> +	if (m == 0xa0) {
> +		const char *app = gemalto_get_string(modem, "Application");
> +		ofono_modem_set_string(modem, "Modem", app);
> +	}
> +
> +	if (m == 0x60) {
> +		const char *app = gemalto_get_string(modem, "Diag");
> +		ofono_modem_set_string(modem, "Modem", app);
> +	}
> +
> +	/* if single ACM interface, remove possible extra devices */
> +	if (m == 0x58 || m == 0x47 || m == 0x54 || m == 0xa0 || m == 0x60) {
> +		ofono_modem_set_string(modem, "Application", NULL);
> +		ofono_modem_set_string(modem, "GNSS", NULL);
> +		ofono_modem_set_string(modem, "RSA", NULL);
> +		ofono_modem_set_string(modem, "Diag", NULL);
> +	}

This really belongs in udevng...

> +
> +#ifdef HAVE_ELL
> +	/* pre-configure MBIM network interface */
> +	if (m == 0x62 || m == 0x5d || m == 0x65) {
> +		data->mbim = STATE_PROBE;
> +	}
> +#endif
> +
> +	set_from_model(data);
> +
> +#ifdef HAVE_ELL
> +	if ((data->mbim == STATE_PROBE) && ctl && net) {
> +		data->init_waiting_time = 3;
> +		return mbim_enable(modem);
> +	}
> +#endif
> +
> +	if ((data->qmi == STATE_PROBE) && ctl && net) {
> +		data->init_waiting_time = 10;
> +		return qmi_enable(modem);
> +	}
> +
> +	return gemalto_enable_app(modem);
> +}
> +
> +#ifdef HAVE_ELL
> +static int mbim_sim_probe(void *device)
> +{
> +	struct mbim_message *message;
> +	/* SIM_GROUP is defined in mbimmodem.h that cannot be included */
> +	uint32_t SIM_GROUP = 1;
> +
> +	message = mbim_message_new(mbim_uuid_basic_connect,
> +					MBIM_CID_SUBSCRIBER_READY_STATUS,
> +					MBIM_COMMAND_TYPE_QUERY);
> +	if (!message)
> +		return -ENOMEM;
> +
> +	mbim_message_set_arguments(message, "");
> +
> +	if (!mbim_device_send(device, SIM_GROUP, message,
> +				NULL, NULL, NULL)) {
> +		mbim_message_unref(message);
> +		return -EIO;
> +	}
> +	return 0;
> +}
> +#endif
> +
> +static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct cb_data *cbd = user_data;
> +	ofono_modem_online_cb_t cb = cbd->cb;
> +	struct ofono_error error;
> +
> +	decode_at_error(&error, g_at_result_final_response(result));
> +
> +	cb(&error, cbd->data);
> +}
> +
> +static void gemalto_set_online_serial(struct ofono_modem *modem,
> +			ofono_bool_t online, ofono_modem_online_cb_t cb,
> +			void *user_data)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	struct cb_data *cbd = cb_data_new(cb, user_data);
> +	char const *command;
> +
> +	gemalto_exec_stored_cmd(modem, "set_online");
> +
> +	if (data->model == 0x47) {
> +		command = online ? "AT^SCFG=\"MEopMode/Airplane\",\"off\"" :
> +					"AT^SCFG=\"MEopMode/Airplane\",\"on\"";
> +	} else {
> +		command = online ? "AT+CFUN=1" : "AT+CFUN=4";
> +	}
> +
> +	DBG("modem %p %s", modem, online ? "online" : "offline");
> +
> +	if (g_at_chat_send(data->app, command, NULL, set_online_cb, cbd,
> +									g_free))
> +		return;
> +
> +	CALLBACK_WITH_FAILURE(cb, cbd->data);
> +	g_free(cbd);
> +}
> +
> +static void gemalto_set_online(struct ofono_modem *modem, ofono_bool_t online,
> +				ofono_modem_online_cb_t cb, void *user_data)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	struct cb_data *cbd = cb_data_new(cb, user_data);
> +	char const *cmd = online ? "AT+CFUN=1" : "AT+CFUN=4";
> +
> +	if (data->conn == GEMALTO_CONNECTION_SERIAL) {
> +		gemalto_set_online_serial(modem, online, cb, user_data);
> +		return;
> +	}
> +
> +	DBG("modem %p %s", modem, online ? "online" : "offline");
> +
> +	if (g_at_chat_send(data->app, cmd, NULL, set_online_cb, cbd, g_free))
> +		return;
> +
> +	CALLBACK_WITH_FAILURE(cb, cbd->data);
> +
> +	g_free(cbd);
> +}
> +
> +static void gemalto_pre_sim(struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +
> +	DBG("%p", modem);
> +
> +	gemalto_exec_stored_cmd(modem, "pre_sim");
> +
> +	ofono_location_reporting_create(modem, 0, "gemaltomodem", data->app);
> +
> +	data->sim = ofono_sim_create(modem, OFONO_VENDOR_GEMALTO,
> +		"atmodem", data->app);
> +
> +	if (data->sim && data->have_sim == TRUE)
> +		ofono_sim_inserted_notify(data->sim, TRUE);
> +}
> +
> +static void gemalto_post_sim(struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +
> +	gemalto_exec_stored_cmd(modem, "post_sim");
> +
> +#ifdef HAVE_ELL
> +	if (data->mbim == STATE_PRESENT) {
> +		/* very important to set the interface ready */
> +		mbim_sim_probe(data->device);
> +	}
> +#endif
> +
> +	ofono_phonebook_create(modem, 0, "atmodem", data->app);
> +	ofono_modem_set_integer(modem, "GemaltoAuthType", data->auth_syntax);
> +
> +	if (data->has_lte)
> +		ofono_lte_create(modem, 0, "gemaltomodem", data->app);
> +}
> +
> +static void cgdcont17_probe(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(user_data);
> +
> +	if (ok)
> +		data->gprs_opt = USE_CTX17;
> +}
> +
> +static void swwan_probe(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(user_data);
> +
> +	if (ok)
> +		data->gprs_opt = USE_SWWAN;
> +}
> +
> +static void autoattach_probe_and_continue(gboolean ok, GAtResult *result,
> +							gpointer user_data)
> +{
> +	struct ofono_modem* modem = user_data;
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +	GAtResultIter iter;
> +	struct ofono_message_waiting *mw;
> +	struct ofono_gprs *gprs = NULL;
> +	struct ofono_gprs_context *gc = NULL;
> +
> +	data->autoattach = FALSE;
> +	ofono_modem_set_integer(modem, "GemaltoAutoAttach", 0);
> +
> +	if (ok) {
> +		g_at_result_iter_init(&iter, result);
> +		while (g_at_result_iter_next(&iter, NULL)) {
> +			if (strstr(g_at_result_iter_raw_line(&iter),
> +					"\"enabled\"")) {
> +				data->autoattach = TRUE;
> +				ofono_modem_set_integer(modem,
> +					"GemaltoAutoAttach", 1);
> +
> +			}
> +		}
> +	}
> +
> +	// TODO: the ofono_gprs_create may require gemaltomodem instead of atmodem
> // for now this code works with a modified version of atmodem/gprs.c,
> // where the ofono_gprs_set_cid_range is not called, so it can be called below
> +
> +#ifdef HAVE_ELL
> +	if (data->mbim == STATE_PRESENT) {
> +		gprs = ofono_gprs_create(modem, OFONO_VENDOR_GEMALTO, "atmodem",
> +								data->app);
> +		ofono_gprs_set_cid_range(gprs, 0, data->max_sessions);
> +		if (data->model == 0x62 || data->model == 0x65) {
> +			struct gemalto_mbim_composite comp;
> +			comp.device = data->device;
> +			comp.chat = data->app;
> +			comp.at_cid = 4;
> +			gc = ofono_gprs_context_create(modem, 0, "gemaltomodemmbim", &comp);
> +		} else /* model == 0x5d */
> +			gc = ofono_gprs_context_create(modem, 0, "mbim", data->device);
> +	} else
> +#endif
> +		if (data->qmi == STATE_PRESENT) {
> +		gprs = ofono_gprs_create(modem, OFONO_VENDOR_GEMALTO, "atmodem",
> +								data->app);
> +		// TODO: verify if need be to create the contexts and auth params beforehand
> +		// if so, may need a gprs-context gemaltomodem-at-qmi
> +
> +		/* on QMI devices, only a single context is supported */
> +		// TODO: Is 1 ok for attach_APN != context_APN?
> +		ofono_gprs_set_cid_range(gprs, 1, 1);
> +		gc = ofono_gprs_context_create(modem, 0, "qmimodem",
> +								data->device);
> +	} else if (data->gprs_opt == USE_SWWAN || data->gprs_opt == USE_CTX17 ||
> +						data->gprs_opt == USE_CTX3) {
> +		ofono_modem_set_integer(modem, "GemaltoWwan",
> +						data->gprs_opt == USE_SWWAN);
> +		gprs = ofono_gprs_create(modem, OFONO_VENDOR_GEMALTO, "atmodem",
> +								data->app);
> +		if (data->gprs_opt == USE_CTX3)
> +			ofono_gprs_set_cid_range(gprs, 3, 3);
> +		else if (data->model == 0x5b)
> +			/*
> +			 * limitation: same APN as for attach
> +			 * in this case create more contexts
> +			 */
> +			ofono_gprs_set_cid_range(gprs, 1, 11);
> +		else
> +			ofono_gprs_set_cid_range(gprs, 4, 16);
> +		// maybe rename the next to gemaltomodem-wwan
> +		gc = ofono_gprs_context_create(modem, 0, "gemaltomodemswwan",
> +								data->app);
> +	} else if (data->gprs_opt == USE_PPP) {
> +		/* plain PPP only works from mdm ports */
> +		gprs = ofono_gprs_create(modem, OFONO_VENDOR_GEMALTO, "atmodem",
> +								data->app);
> +		if (data->model == 0x47)
> +			ofono_gprs_set_cid_range(gprs, 1, 2);
> +		else if (data->has_lte)
> +			ofono_gprs_set_cid_range(gprs, 4, 16);
> +		else
> +			ofono_gprs_set_cid_range(gprs, 1, 16);
> +
> +		gc = ofono_gprs_context_create(modem, 0, "atmodem", data->mdm);
> +
> +	} /*
> +	   * in case of no match above, we have no gprs possibilities
> +	   * this is common when using the module through serial interfaces
> +	   * nevertheless other services (voice, gpio, gnss) could be available
> +	   */
> +
> +	if (gc)
> +		ofono_gprs_context_set_type(gc,
> +					OFONO_GPRS_CONTEXT_TYPE_INTERNET);
> +
> +	if (gprs && gc)
> +		ofono_gprs_add_context(gprs, gc);
> +
> +	/* might have also without voicecall support  */
> +	ofono_ussd_create(modem, 0, "atmodem", data->app);
> +
> +	/*
> +	 * Call support is technically possible only after sim insertion
> +	 * with the module online. However the EMERGENCY_SETUP procedure of
> +	 * the 3GPP TS_24.008 is triggered by the same AT command,
> +	 * and namely 'ATD112;', 'ATD911;', etc.
> +	 * On the other hand, in airplane-mode it is not possible to do it, nor
> +	 * to create all relevant URCs for the atom.
> +	 *
> +	 * Ofono does not make a distinction between no-sim and
> +	 * airplane-mode scenarios, so we create the voicecall in post-online.
> +	 * This is half-compatible with the European directives that require
> +	 * a SIM inserted also for emergency setup.
> +	 */
> // this needs further thinking
> +
> +	if (data->voice_avail) {
> +		ofono_modem_set_integer(modem, "GemaltoVtsQuotes",
> +						data->vts_with_quotes);
> +		ofono_voicecall_create(modem, 0, "gemaltomodem", data->app);
> +
> +		ofono_call_forwarding_create(modem, 0, "atmodem", data->app);
> +		ofono_call_settings_create(modem, 0, "atmodem", data->app);
> +		ofono_call_meter_create(modem, 0, "atmodem", data->app);
> +		ofono_call_barring_create(modem, 0, "atmodem", data->app);
> +	}
> +
> +	/* modules require to be online to accept at+cnmi */
> +	ofono_sms_create(modem, OFONO_VENDOR_GEMALTO, "atmodem", data->app);
> +	mw = ofono_message_waiting_create(modem);
> +
> +	if (mw)
> +		ofono_message_waiting_register(mw);
> +
> +	ofono_netreg_create(modem, OFONO_VENDOR_GEMALTO, "atmodem", data->app);
> +}
> +
> +static int gemalto_post_online_delayed(void *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +
> +	/*
> +	 * check module capabilities once online and SIM really ready.
> +	 *
> +	 * Note: the g_at_chat_send calls only insert the commands in a list:
> +	 * they are not executed synchronously
> +	 *
> +	 * Note: ofono executes each AT commands and the related callback before
> +	 * proceeding with the next. So continuing on the last AT command is all
> +	 * it takes
> +	 */
> +
> +	gemalto_exec_stored_cmd(modem, "post_online");
> +
> +	if (data->ecmncm == STATE_PROBE) {
> +		data->gprs_opt = USE_PPP; /* fallback */
> +		g_at_chat_send(data->app, "AT+CGDCONT=17", NULL,
> +						cgdcont17_probe, modem, NULL);
> +		g_at_chat_send(data->app, "AT^SWWAN?", NULL, swwan_probe, modem,
> +									NULL);
> +	}
> +
> +	g_at_chat_send(data->app, "AT^SCFG=\"GPRS/AutoAttach\"", NULL,
> +				autoattach_probe_and_continue, modem, NULL);
> +
> +	return FALSE; /* to kill the timer */
> +}
> +
> +static void gemalto_post_online(struct ofono_modem *modem)
> +{
> +	/*
> +	 * in this version of ofono we must wait for SIM 'really-ready'
> +	 * can be avoided when capturing the right URCs
> // waiting for the URC (like '+PBREADY') is anyway not risk-free
> // because if ofono is started when the module is already initialized
> // the URC is missing, and on some models cannot be queried.
> // This needs further thinking.
> +	 */
> +	g_timeout_add_seconds(5, gemalto_post_online_delayed, modem);
> +}
> +
> +#ifdef HAVE_ELL
> +static void mbim_radio_off_for_disable(struct mbim_message *message, void *user)
> +{
> +	struct ofono_modem *modem = user;
> +	struct gemalto_data *md = ofono_modem_get_data(modem);
> +
> +	DBG("%p", modem);
> +
> +	mbim_device_shutdown(md->device);
> +}
> +#endif
> +
> +static int gemalto_disable_serial(struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +
> +	if (data->app != NULL) {
> +		if (data->model == 0x47) {
> +			g_at_chat_send(data->app,
> +				"AT^SCFG=\"MEopMode/Airplane\",\"on\"",
> +				NULL, NULL, NULL, NULL);
> +		} else {
> +			gemalto_set_cfun(data->app, 41, modem);
> +			return -EINPROGRESS;
> +		}
> +		g_at_chat_cancel_all(data->app);
> +	}
> +
> +	ofono_modem_set_powered(modem, FALSE);
> +	return 0;
> +}
> +
> +static int gemalto_disable(struct ofono_modem *modem)
> +{
> +	struct gemalto_data *data = ofono_modem_get_data(modem);
> +#ifdef HAVE_ELL
> +	struct mbim_message *message;
> +#endif
> +
> +	DBG("%p", modem);
> +
> +	if (data->conn == GEMALTO_CONNECTION_SERIAL)
> +		return gemalto_disable_serial(modem);
> +
> +#ifdef HAVE_ELL
> +	if (data->mbim == STATE_PRESENT) {
> +		message = mbim_message_new(mbim_uuid_basic_connect,
> +						MBIM_CID_RADIO_STATE,
> +						MBIM_COMMAND_TYPE_SET);
> +		mbim_message_set_arguments(message, "u", 0);
> +
> +		if (mbim_device_send(data->device, 0, message,
> +				mbim_radio_off_for_disable, modem, NULL)==0)
> +			mbim_device_closed(modem);
> +	}
> +#endif

All these #ifdefs need to go away.  The only place that should be using 
HAVE_ELL is main.c.  All the other mbim stuff needs to be merged into 
the mbim driver.

> +
> +	if (data->app == NULL)
> +		return 0;
> +
> +	gemalto_exec_stored_cmd(modem, "disable");
> +
> +	gemalto_set_cfun(data->app, 41, modem);
> +
> +	return -EINPROGRESS;
> +}
> +
> +static struct ofono_modem_driver gemalto_driver = {
> +	.name		= "gemalto",
> +	.probe		= gemalto_probe,
> +	.remove		= gemalto_remove,
> +	.enable		= gemalto_enable,
> +	.disable	= gemalto_disable,
> +	.set_online	= gemalto_set_online,
> +	.pre_sim	= gemalto_pre_sim,
> +	.post_sim	= gemalto_post_sim,
> +	.post_online	= gemalto_post_online,
> +};
> +
> +static int gemalto_init(void)
> +{
> +	return ofono_modem_driver_register(&gemalto_driver);
> +}
> +
> +static void gemalto_exit(void)
> +{
> +	ofono_modem_driver_unregister(&gemalto_driver);
> +}
> +
> +OFONO_PLUGIN_DEFINE(gemalto, "Gemalto modem plugin", VERSION,
> +		OFONO_PLUGIN_PRIORITY_DEFAULT, gemalto_init, gemalto_exit)
> diff --git a/plugins/udevng.c b/plugins/udevng.c
> index 3c39e681..6c82eb81 100644
> --- a/plugins/udevng.c
> +++ b/plugins/udevng.c
> @@ -3,6 +3,7 @@
>    *  oFono - Open Source Telephony
>    *
>    *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
> + *  Copyright (C) 2018 Gemalto M2M
>    *
>    *  This program is free software; you can redistribute it and/or modify
>    *  it under the terms of the GNU General Public License version 2 as
> @@ -979,20 +980,6 @@ static gboolean setup_serial_modem(struct modem_info* modem)
>   	return TRUE;
>   }
>   
> -static gboolean setup_tc65(struct modem_info* modem)
> -{
> -	ofono_modem_set_driver(modem->modem, "cinterion");
> -
> -	return setup_serial_modem(modem);
> -}
> -
> -static gboolean setup_ehs6(struct modem_info* modem)
> -{
> -	ofono_modem_set_driver(modem->modem, "cinterion");
> -
> -	return setup_serial_modem(modem);
> -}
> -
>   static gboolean setup_ifx(struct modem_info* modem)
>   {
>   	struct serial_device_info* info;
> @@ -1121,60 +1108,133 @@ static gboolean setup_ublox(struct modem_info *modem)
>   
>   static gboolean setup_gemalto(struct modem_info* modem)
>   {
> -	const char *app = NULL, *gps = NULL, *mdm = NULL,
> -		*net = NULL, *qmi = NULL;
> +	const char *mdm = NULL, *app = NULL, *gnss = NULL, *rsa = NULL,
> +		*diag = NULL,
> +		*ctl = NULL, *net = NULL, *net2 = NULL;
> +	char descriptors[PATH_MAX];
> +	guint m;
> +	const char *prevNet = NULL;
>   
>   	GSList *list;
>   
> -	DBG("%s", modem->syspath);
> +	DBG("type:%d, syspath:%s [%s:%s]", modem->type, modem->syspath,
> +						modem->vendor, modem->model);
> +
> +	if (modem->type==MODEM_TYPE_SERIAL) {
> +		struct serial_device_info* info = modem->serial;
> +		ofono_modem_set_string(modem->modem, "ConnType", "Serial");
> +		ofono_modem_set_string(modem->modem, "ATport", info->devnode);
> +		ofono_modem_set_string(modem->modem, "Vendor", "gemalto");
> +		ofono_modem_set_string(modem->modem, "Model", "0000");
> +		return TRUE;
> +
> +	}
> +
> +	m = strtoul(modem->model, NULL, 16);
>   
> +	/* map devices */
>   	for (list = modem->devices; list; list = list->next) {
>   		struct device_info *info = list->data;
>   
> -		DBG("%s %s %s %s %s", info->devnode, info->interface,
> -				info->number, info->label, info->subsystem);
> -
> -		/* PHS8-P */
> -		if (g_strcmp0(info->interface, "255/255/255") == 0) {
> -			if (g_strcmp0(info->number, "01") == 0)
> -				gps = info->devnode;
> -			else if (g_strcmp0(info->number, "02") == 0)
> -				app = info->devnode;
> -			else if (g_strcmp0(info->number, "03") == 0)
> -				mdm = info->devnode;
> -			else if (g_strcmp0(info->subsystem, "net") == 0)
> -				net = info->devnode;
> -			else if (g_strcmp0(info->subsystem, "usbmisc") == 0)
> -				qmi = info->devnode;
> +		DBG("node:%s, sub:%s, interface:%s, number:%s, sysattr:%s",
> +			info->devnode, info->subsystem, info->interface,
> +			info->number, info->sysattr);
> +
> +		if (g_str_equal(info->subsystem,"tty")) {
> +			/* option devices (ttyUSBx) */
> +			/* note: option devices order in VMs under windows may vary */
> +			if (g_str_equal(info->interface, "255/255/255") ||
> +					g_str_equal(info->interface, "255/66/1") ||
> +					g_str_equal(info->interface, "255/0/0")) {
> +				if (g_str_equal(info->number, "00"))	{
> +					app = info->devnode;
> +				} else if (g_str_equal(info->number, "01")) {
> +					gnss = rsa = info->devnode;
> +				} else if (g_str_equal(info->number, "02")) {
> +					diag = info->devnode;
> +				} else if (g_str_equal(info->number, "03")) {
> +					mdm = info->devnode;
> +				}
> +			/* cdc-acm devices (ttyACMx) */
> +			} else if (g_str_equal(info->interface,"2/2/0") ||
> +					g_str_equal(info->interface,"2/2/1")) {
> +				if (g_str_equal(info->number, "00")) {
> +					mdm = info->devnode;
> +				} else if (g_str_equal(info->number, "02")) {
> +					app = info->devnode;
> +				} else if (g_str_equal(info->number, "04")) {
> +					gnss = info->devnode;
> +				} else if (g_str_equal(info->number, "06")) {
> +					rsa = info->devnode;
> +				} else if (g_str_equal(info->number, "08")) {
> +					diag = info->devnode;
> +				}
> +			}
>   		}
>   
> -		/* Cinterion ALS3, PLS8-E, PLS8-X */
> -		if (g_strcmp0(info->interface, "2/2/1") == 0) {
> -			if (g_strcmp0(info->number, "00") == 0)
> -				mdm = info->devnode;
> -			else if (g_strcmp0(info->number, "02") == 0)
> -				app = info->devnode;
> -			else if (g_strcmp0(info->number, "04") == 0)
> -				gps = info->devnode;
> -		}
> -		if (g_strcmp0(info->interface, "2/6/0") == 0) {
> -			if (g_strcmp0(info->subsystem, "net") == 0)
> -				net = info->devnode;
> +		if (g_str_equal(info->subsystem,"usbmisc"))
> +			/* control device for qmi/mbim:  /dev/cdc-wdmX */
> +			ctl = info->devnode;
> +
> +		/* network interfaces */
> +		if (g_str_equal(info->subsystem,"net")) {
> +			/* cdc-mbim */
> +			if (g_str_equal(info->interface,"2/14/0") ||
> +				  /* qmi-wwan */
> +				  g_str_equal(info->interface,"255/255/255")) {
> +				net = info->devnode; /* wwanX */
> +			/* cdc-ecm */
> +			} else if (g_str_equal(info->interface, "2/6/0") ||
> +				  /* cdc-ncm */
> +				  g_str_equal(info->interface, "2/13/0")) {
> +				 /*
> +				  * there could be 2 interfaces in this case,
> +				  * and need to have them sorted to be sure to
> +				  * report the right interface to activate
> +				  */
> +				if (!prevNet ||
> +					  g_strcmp0(info->number,prevNet)<0) {
> +					prevNet = info->number;
> +					net2 = net; /* can be NULL, ok */
> +					net = info->devnode; /* usbX */
> +				} else { // invert
> +					net2 = info->devnode; /* usbX */
> // not sure this logic of sorting the network interfaces is needed
> // but I don't see another way of being sure of having them in order
> +				}
> +			}
>   		}
>   	}
>   
> -	DBG("application=%s gps=%s modem=%s network=%s qmi=%s",
> -			app, gps, mdm, net, qmi);
> +	sprintf(descriptors, "%s/descriptors", modem->syspath);
> +	ofono_modem_set_string(modem->modem, "ConnType", "Usb");
> +	ofono_modem_set_string(modem->modem, "DescriptorFile", descriptors);
> +	ofono_modem_set_string(modem->modem, "Vendor", modem->vendor);
> +	ofono_modem_set_string(modem->modem, "Model", modem->model);
>   
> -	if (app == NULL || mdm == NULL)
> -		return FALSE;
> +	/*
> +	 * special cases.
> +	 * TODO: selected by external parameters:
> +	 *   GEMALTO_CONFIG_MBIM_ONLY -> _set_driver(modem->modem, "mbim");
> +	 *   GEMALTO_CONFIG_QMI_ONLY -> _set_driver(modem->modem, "gobi");
> +	 *   ...
> +	 */
> +	if (m==0x64) {
> +		ofono_modem_set_string(modem->modem, "Device", ctl);
> +		ofono_modem_set_string(modem->modem, "NetworkInterface", net);
> +		ofono_modem_set_driver(modem->modem, "mbim");
> +		return TRUE;
> +	}
>   
> -	ofono_modem_set_string(modem->modem, "Application", app);
> -	ofono_modem_set_string(modem->modem, "GPS", gps);
>   	ofono_modem_set_string(modem->modem, "Modem", mdm);
> -	ofono_modem_set_string(modem->modem, "Device", qmi);
> -	ofono_modem_set_string(modem->modem, "Model", modem->model);
> +	ofono_modem_set_string(modem->modem, "Application", app);
> +	ofono_modem_set_string(modem->modem, "GNSS", gnss);
> +	ofono_modem_set_string(modem->modem, "RSA", rsa);
> +	ofono_modem_set_string(modem->modem, "Diag", diag);
> +
> +	ofono_modem_set_string(modem->modem, "NetworkControl", ctl);
>   	ofono_modem_set_string(modem->modem, "NetworkInterface", net);
> +	ofono_modem_set_string(modem->modem, "NetworkInterface2", net2);
> +
> +	ofono_modem_set_powered_timeout_hint(modem->modem, 60);
>   
>   	return TRUE;
>   }
> @@ -1290,7 +1350,7 @@ static struct {
>   	{ "quectel",	setup_quectel	},
>   	{ "quectelqmi",	setup_quectelqmi},
>   	{ "ublox",	setup_ublox	},
> -	{ "gemalto",	setup_gemalto	},
> +	{ "gemalto",	setup_gemalto,	"device/interface"	},
>   	{ "xmm7xxx",	setup_xmm7xxx	},
>   	{ "mbim",	setup_mbim	},
>   	/* Following are non-USB modems */
> @@ -1298,12 +1358,12 @@ static struct {
>   	{ "u8500",	setup_isi_serial	},
>   	{ "n900",	setup_isi_serial	},
>   	{ "calypso",	setup_serial_modem	},
> -	{ "cinterion",	setup_serial_modem	},
> +	{ "cinterion",	setup_gemalto },
>   	{ "nokiacdma",	setup_serial_modem	},
>   	{ "sim900",	setup_serial_modem	},
>   	{ "wavecom",	setup_wavecom		},
> -	{ "tc65",	setup_tc65		},
> -	{ "ehs6",	setup_ehs6		},
> +	{ "tc65",	setup_gemalto },
> +	{ "ehs6",	setup_gemalto },
>   	{ }
>   };
>   
> @@ -1464,11 +1524,10 @@ static void add_serial_device(struct udev_device *dev)
>   	struct udev_device* mdev;
>   	const char* driver;
>   
> +	/* discard if the device doesn't have the property OFONO_DRIVER */
>   	mdev = get_serial_modem_device(dev);
> -	if (!mdev) {
> -		DBG("Device is missing required OFONO_DRIVER property");
> +	if (!mdev)
> // separate commit to remove this polluting message. On some systems
> // it can emit the line 256 times.
> // By default a typical linux distro presents 64 ttySx ports, so even if a
> // few are used, there are too many unneeded notifications.
>   		return;
> -	}
>   
>   	driver = udev_device_get_property_value(mdev, "OFONO_DRIVER");
>   
> @@ -1674,11 +1733,13 @@ static struct {
>   	{ "ublox",	"cdc_acm",	"1546", "1102"	},
>   	{ "ublox",	"rndis_host",	"1546", "1146"	},
>   	{ "ublox",	"cdc_acm",	"1546", "1146"	},
> -	{ "gemalto",	"option",	"1e2d",	"0053"	},
> -	{ "gemalto",	"cdc_wdm",	"1e2d",	"0053"	},
> -	{ "gemalto",	"qmi_wwan",	"1e2d",	"0053"	},
> -	{ "gemalto",	"cdc_acm",	"1e2d",	"0061"	},
> -	{ "gemalto",	"cdc_ether",	"1e2d",	"0061"	},
> +	{ "gemalto",	"cdc_acm",	"1e2d"		},
> +	{ "gemalto",	"option",	"1e2d"		},
> +	{ "gemalto",	"cdc_wdm",	"1e2d"		},
> +	{ "gemalto",	"qmi_wwan",	"1e2d"		},
> +	{ "gemalto",	"cdc_ether",	"1e2d",		},
> +	{ "gemalto",	"cdc_mbim",	"1e2d",		},
> +	{ "gemalto",	"cdc_ncm",	"1e2d",		},
>   	{ "telit",	"cdc_ncm",	"1bc7", "0036"	},
>   	{ "telit",	"cdc_acm",	"1bc7", "0036"	},
>   	{ "xmm7xxx",	"cdc_acm",	"8087"		},
> @@ -1708,6 +1769,7 @@ static void check_usb_device(struct udev_device *device)
>   	vendor = udev_device_get_property_value(usb_device, "ID_VENDOR_ID");
>   	model = udev_device_get_property_value(usb_device, "ID_MODEL_ID");
>   
> +	/* for serial2usb-like devices, enum as usb/usb_device */
> // separate commit or discard
>   	driver = udev_device_get_property_value(usb_device, "OFONO_DRIVER");
>   	if (!driver) {
>   		struct udev_device *usb_interface =
> @@ -1719,7 +1781,17 @@ static void check_usb_device(struct udev_device *device)
>   					usb_interface, "OFONO_DRIVER");
>   	}
>   
> -	if (driver == NULL) {
> +	if (!driver)
> +	{
> +		/* for serial2usb devices (enum as tty/generic) */
> +		const char *serialdriver =
> +			udev_device_get_property_value(device, "OFONO_DRIVER");
> +		DBG("vId=%s, pId=%s, driver=%s", vendor, model, serialdriver);
> +		if (serialdriver)
> +			add_serial_device(device);
> +	}
> // separate commit to match the devices connected through an FDMI chip
> +
> +	if (!driver) { /* use linux driver */
>   		const char *drv;
>   		unsigned int i;
>   
> @@ -1772,6 +1844,7 @@ static void check_usb_device(struct udev_device *device)
>   static void check_device(struct udev_device *device)
>   {
>   	const char *bus;
> +	const char *ofono_ignore_device;
>   
>   	bus = udev_device_get_property_value(device, "ID_BUS");
>   	if (bus == NULL) {
> @@ -1780,6 +1853,11 @@ static void check_device(struct udev_device *device)
>   			return;
>   	}
>   
> +	ofono_ignore_device = udev_device_get_property_value(device,
> +							"OFONO_IGNORE_DEVICE");
> +		if (ofono_ignore_device)
> +			return; /* skip this device */
> +
> // separate commit to ignore some devices
>   	if ((g_str_equal(bus, "usb") == TRUE) ||
>   			(g_str_equal(bus, "usbmisc") == TRUE))
>   		check_usb_device(device);
> 

Regards,
-Denis

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] new gemalto plugin
  2018-10-29 19:58 ` Denis Kenzior
@ 2018-10-30  6:10   ` Giacinto Cifelli
  2018-10-30 15:23     ` Denis Kenzior
  0 siblings, 1 reply; 16+ messages in thread
From: Giacinto Cifelli @ 2018-10-30  6:10 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 17510 bytes --]

Hi Denis,

Thank you for taking the time to look into this.

On Mon, Oct 29, 2018 at 8:58 PM Denis Kenzior <denkenz@gmail.com> wrote:
>
> Hi Giacinto,
>
> On 10/26/2018 01:10 AM, Giacinto Cifelli wrote:
> > I would like to submit to your attention the new version of the Gemalto
> > plugin. It is not ready, but would already benefit from some feedback.
> > The purpose of this new plugin is to address most of the Gemalto modules
> > (excluding perhaps some LTE CatM and CatNB), interfaced either via USB
> > or RS232 interface.
>
> So just a cursory look through this, but overall my impression is that
> this code would be utterly unmaintainable.  You need to split this up
> into something without a bazillion if conditions and #ifdefs in it.
> Notice how none of our existing driver code uses #ifdefs.

In your existing code, there are a few models from each vendor,
partially supported, with hardcoded choices for several options.

>
> That means MBIM/QMI/AT logic needs to be separated into separate
> drivers.  If you have a weird QMI/AT or MBIM/AT or QMI over MBIM
> combination stuff going on, then these all need to be separate drivers.
>

Several modems are either qmi+at or mbim+at, while others are simply
at (for example with ppp, ecm, ncm networking).

The qmi and mbim part is limited to initialization and gprs-context,
the rest is common.
Shall I duplicate everything for this?

The #ifdefs are a choice of the project for the ELL, otherwise it
would be simply, well-integrated ifs (like for qmi).
I understand that the use of ELL will be extended in the future, why
not go all the way, remove the #ifdefs, and declare a full dependency?

> You may want to basically start with the basics.  Get the initial driver
> separation figured out, then you can add fancy stuff like vendor
> specific APIs, etc.  Right now they just clutter the code and are not
> really useful to a wide audience anyway.

Yes

> > +
> > +     /* mbim data */
> > +     uint16_t max_segment;
> > +     uint8_t max_outstanding;
> > +     uint8_t max_sessions;
> > +
>
> The mbim plugin is not sufficient for this?
>

there are no mbim-only gemalto modems, only mbim+at.
And so, it is not suffient, I had to duplicate all this.

> > +
> > +static const char *gemalto_get_string(struct ofono_modem *modem, const char *k)
> > +{
> > +     const char *v;
> > +
> > +     if (!modem || !k || !*k)
> > +             return NULL;
> > +
> > +     v = ofono_modem_get_string(modem, k);
> > +
> > +     if (!v || !*v)
> > +             return NULL;
> > +
> > +     return v;
> > +}
>
> Uhh, why?

to have a simple compare for !var in the code. Because it is not
possible to remove a variable once set, only to set it to an empty
string.
Maybe I should add instead an ofono_modem_unset_string ?

> > +static void gemalto_exec_stored_cmd(struct ofono_modem *modem,
> > +                                                     const char *filename)
> > +{
> > +     struct gemalto_data *data = ofono_modem_get_data(modem);
> > +     const char *vid = gemalto_get_string(modem, "Vendor");
> > +     const char *pid = gemalto_get_string(modem, "Model");
> > +     char store[64];
> > +     int index;
> > +     char *command, *prompt, *argument;
> > +     char key[32];
> > +     GKeyFile *f;
> > +
> > +     sprintf(store,"%s-%s/%s", vid, pid, filename);
> > +     f = storage_open(NULL, store);
> > +
> > +     if (!f)
> > +             return;
> > +
> > +     for (index = 0; ; index++) {
> > +             sprintf(key, "command_%d", index);
> > +             command = g_key_file_get_string(f, "Simple", key, NULL);
> > +
> > +             if (!command)
> > +                     break;
> > +
> > +             DBG(REDCOLOR"executing stored command simple: %s"NOCOLOR, command);
> > +             g_at_chat_send(data->app, command, NULL, NULL, NULL, NULL);
> > +     }
> > +
> > +     for (index = 0; ; index++) {
> > +             sprintf(key, "command_%d", index);
> > +             command = g_key_file_get_string(f, "WithPrompt", key, NULL);
> > +             sprintf(key, "prompt_%d", index);
> > +             prompt = g_key_file_get_string(f, "WithPrompt", key, NULL);
> > +             sprintf(key, "argument_%d", index);
> > +             argument = g_key_file_get_string(f, "WithPrompt", key, NULL);
> > +
> > +             if (!command || !prompt || !argument)
> > +                     break;
> > +
> > +             DBG("executing stored command with prompt: %s", command);
> > +             executeWithPrompt(data->app, command, prompt, argument,
> > +                     NULL, NULL, NULL);
> > +     }
> > +
> > +     storage_close(NULL, store, f, FALSE);
> > +}
>
> What is this doing actually?

Reading stored commands and executing them.
It is intended to configure the modem for a specific application.
There are hooks for each state of the modem state machine.

There isn't much interest in passing these commands through an
interface, because each application has its own configuration and
sends a different set of commands.
And it is modem-specific, so  it is stored by USB VID-PID.

> > +/*******************************************************************************
> > + * Time services interface
> > + ******************************************************************************/
> > +
> > +#define GEMALTO_NITZ_TIME_INTERFACE OFONO_SERVICE ".gemalto.TimeServices"
> > +
> > +static DBusMessage *set_modem_datetime(DBusConnection *conn,
> > +                                                     DBusMessage *msg,
> > +                                                     void *modem)
> > +{
> > +     struct gemalto_data *data = ofono_modem_get_data(modem);
> > +     time_t t = time(NULL);
> > +     struct tm tm;
> > +     gchar cclk_cmd[32];
> > +
> > +     /* Set date and time */
> > +     tm = *localtime(&t);
> > +     strftime(cclk_cmd, 32, "AT+CCLK=\"%y/%m/%d,%T\"", &tm);
> > +     g_at_chat_send(data->app, cclk_cmd, none_prefix, NULL, NULL, NULL);
> > +     return dbus_message_new_method_return(msg);
> > +}
> > +
> > +static const GDBusMethodTable gsmTime_methods[] = {
> > +     { GDBUS_ASYNC_METHOD("SetModemDatetime",
> > +                     NULL, NULL, set_modem_datetime) },
> > +     {}
> > +};
> > +
>
> So first question is, why would you want to do this?  These days most
> systems use the time on the Application processor.  Who cares what the
> modem thinks the time is.

Some systems care: this comes in fact from an application (and will be
committed mentioning a co-author).
And it helps for timestamps in case of stack logs.

>
> Secondly, why not do this as an actual core atom?

Not sure of the general interest, but yes can do.

>
> > +static const GDBusSignalTable gsmTime_signals[] = {
> > +     { GDBUS_SIGNAL("NitzUpdated",
> > +                     GDBUS_ARGS({ "time", "a{sv}" }) )},
> > +     {}
> > +};
>
> You don't like ofono_netreg_time_notify?

I will look into this.

>
> > +
> > +static void gemalto_time_enable(struct ofono_modem *modem)
> > +{
> > +     DBusConnection *conn = ofono_dbus_get_connection();
> > +     const char *path = ofono_modem_get_path(modem);
> > +
> > +     if (!g_dbus_register_interface(conn, path,
> > +                                     GEMALTO_NITZ_TIME_INTERFACE,
> > +                                     gsmTime_methods,
> > +                                     gsmTime_signals,
> > +                                     NULL,
> > +                                     modem,
> > +                                     NULL)) {
> > +             ofono_error("Could not register %s interface under %s",
> > +                                     GEMALTO_NITZ_TIME_INTERFACE, path);
> > +             return;
> > +     }
> > +
> > +     ofono_modem_add_interface(modem, GEMALTO_NITZ_TIME_INTERFACE);
> > +}
> > +
> > +/*******************************************************************************
> > + * Command passtrhough interface
> > + ******************************************************************************/
> > +
> > +#define COMMAND_PASSTHROUGH_INTERFACE OFONO_SERVICE ".gemalto.CommandPassthrough"
> > +
>
> No AT command pass through in oFono upstream ;)  We've had this
> conversation many times, if there are useful AT commands that can be
> sent via this interface, then they should be abstracted behind a proper
> API instead.

I may have misunderstood your comments the one time we had the conversation.
I don't understand your blind refusal: cluttering the interface with
every single command that an application may need for some special
events doesn't seem that brilliant.

> > +/*******************************************************************************
> > + * GNSS interface
> > + ******************************************************************************/
> > +
> > +#define GNSS_INTERFACE OFONO_SERVICE ".gemalto.GNSS"
> > +
> > +static void gnss_get_properties_cb(gboolean ok, GAtResult *result,
> > +                                                     gpointer user_data)
> > +{
> > +     struct ofono_modem *modem = user_data;
> > +     struct gemalto_data *data = ofono_modem_get_data(modem);
> > +     const char *port = ofono_modem_get_string(modem, "GNSS");
> > +     GAtResultIter iter;
> > +     DBusMessage *reply;
> > +     DBusMessageIter dbusiter;
> > +     DBusMessageIter dict;
> > +
> > +     if (data->gnss_msg == NULL)
> > +             return;
> > +
> > +     if (!ok)
> > +             goto error;
> > +
> > +     reply = dbus_message_new_method_return(data->gnss_msg);
> > +     dbus_message_iter_init_append(reply, &dbusiter);
> > +     dbus_message_iter_open_container(&dbusiter, DBUS_TYPE_ARRAY,
> > +                                     OFONO_PROPERTIES_ARRAY_SIGNATURE,
> > +                                     &dict);
> > +     g_at_result_iter_init(&iter, result);
> > +
> > +     /* supported format: ^SGPSC: "Nmea/Output","off" */
> > +     while (g_at_result_iter_next(&iter, "^SGPSC:")) {
> > +             const char *name = "";
> > +             const char *val = "";
> > +
> > +             if (!g_at_result_iter_next_string(&iter, &name))
> > +                     continue;
> > +
> > +             /*
> > +              * skip the "Info" property:
> > +              * different line format and different usage
> > +              */
> > +             if (g_str_equal(name,"Info"))
> > +                     continue;
> > +
> > +             if (!g_at_result_iter_next_string(&iter, &val))
> > +                     continue;
> > +
> > +             ofono_dbus_dict_append(&dict, name, DBUS_TYPE_STRING, &val);
> > +     }
> > +
> > +     ofono_dbus_dict_append(&dict, "Port", DBUS_TYPE_STRING, &port);
> > +     dbus_message_iter_close_container(&dbusiter, &dict);
> > +     __ofono_dbus_pending_reply(&data->gnss_msg, reply);
> > +     return;
> > +
> > +error:
> > +     __ofono_dbus_pending_reply(&data->gnss_msg,
> > +                     __ofono_error_failed(data->gnss_msg));
> > +}
> > +
> > +static DBusMessage *gnss_get_properties(DBusConnection *conn,
> > +                                     DBusMessage *msg, void *user_data)
> > +{
> > +     struct ofono_modem *modem = user_data;
> > +     struct gemalto_data *data = ofono_modem_get_data(modem);
> > +
> > +     if (data->gnss_msg != NULL)
> > +             return __ofono_error_busy(msg);
> > +
> > +     if (!g_at_chat_send(data->app, "AT^SGPSC?", sgpsc_prefix,
> > +                                     gnss_get_properties_cb, modem, NULL))
> > +             return __ofono_error_failed(msg);
> > +
> > +     data->gnss_msg = dbus_message_ref(msg);
> > +
> > +     return NULL;
> > +}
> > +
> > +static void gnss_set_properties_cb(gboolean ok, GAtResult *result,
> > +                                                     gpointer user_data)
> > +{
> > +     struct ofono_modem *modem = user_data;
> > +     struct gemalto_data *data = ofono_modem_get_data(modem);
> > +     DBusMessage *reply;
> > +
> > +     if (data->gnss_msg == NULL)
> > +             return;
> > +
> > +     if (!ok) {
> > +             __ofono_dbus_pending_reply(&data->gnss_msg,
> > +                                     __ofono_error_failed(data->gnss_msg));
> > +             return;
> > +     }
> > +
> > +     reply = dbus_message_new_method_return(data->gnss_msg);
> > +     __ofono_dbus_pending_reply(&data->gnss_msg, reply);
> > +}
> > +
> > +static DBusMessage *gnss_set_property(DBusConnection *conn,
> > +                                     DBusMessage *msg, void *user_data)
> > +{
> > +     struct ofono_modem *modem = user_data;
> > +     struct gemalto_data *data = ofono_modem_get_data(modem);
> > +     DBusMessageIter iter, var;
> > +     const char *name;
> > +     char *value;
> > +     char buf[256];
> > +
> > +     if (data->gnss_msg != NULL)
> > +             return __ofono_error_busy(msg);
> > +
> > +     if (dbus_message_iter_init(msg, &iter) == FALSE)
> > +             return __ofono_error_invalid_args(msg);
> > +
> > +     if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
> > +             return __ofono_error_invalid_args(msg);
> > +
> > +     dbus_message_iter_get_basic(&iter, &name);
> > +     dbus_message_iter_next(&iter);
> > +
> > +     if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
> > +             return __ofono_error_invalid_args(msg);
> > +
> > +     dbus_message_iter_recurse(&iter, &var);
> > +
> > +     if (dbus_message_iter_get_arg_type(&var) !=
> > +                                     DBUS_TYPE_STRING)
> > +             return __ofono_error_invalid_args(msg);
> > +
> > +     dbus_message_iter_get_basic(&var, &value);
> > +
> > +     snprintf(buf, sizeof(buf), "AT^SGPSC=\"%s\",\"%s\"", name, value);
> > +
>
> So the modem is doing the property validation?  Yeah, not happening ;)

The name and number of properties vary by the models.
There isn't much to validate in a general way. Null perhaps, or an
arbitrary maximum length.
This interface is supposed to use GetProperties and then can change
some of them.
That the set property is in the list returned by GetProperties can be verified.

> Why don't you just configure the device into NMEA mode and use
> location_reporting atom ?

that atom is not compatible with gpsd and takes arbitrary choices on
how to handle the receiver, it is incomplete and adds constrains on
the dbus version.
For example, for the gemalto atom, it starts the engine in
assisted-gps mode, without loading the corresponding data files.


> > +/*
> > + * powersave for older modules:
> > + *   command_0=AT+CFUN=7
> > + * return:
> > + *   command_0=AT+CFUN=1
> > + *
> > + * powersave example for modules with GNSS (could also only stop the output):
> > + *   command_0=AT+CREG=0
> > + *   command_1=AT+CGREG=0
> > + *   command_2=AT+CEREG=0
> > + *   command_3=AT^SGPSC="Engine","0"
> > + *   command_4=AT^SGPSC="Power/Antenna","off"
> > + * return:
> > + *   command_0=AT+CREG=2
> > + *   command_1=AT+CGREG=2
> > + *   command_2=AT+CEREG=2
> > + *   command_4=AT^SGPSC="Power/Antenna","on"
> > + *   command_3=AT^SGPSC="Engine","1"
> > + */
>
> How is 'powersave' useful?

If you go on holiday for 1 week, you may like that your car still
starts when you come back.
The modem in this kind of applications is always on, for anti-theft of
simply so that you can blink the lights when coming out of a
supermarket.
But when parked, the modem remains listening for network events, and
only ping the application (and ofono) in case of sms, for example (up
to the specific application how to come back from sleep).
Above, a common setting: no reg events (if we lose coverage we don't
react instead of re-powering) and stop the gnss engine.

> > +
> > +#ifdef HAVE_ELL
> > +static int mbim_parse_descriptors(struct gemalto_data *md, const char *file)
>
> Why is MBIM driver not enough?

for this yes, pure duplication. It would be good to have this code in
an atom or in common.c or similar.
Otherwise, all Gemalto modems are MBIM+AT, so the pure mbim is not enough.

> > +
> > +static void gemalto_remove(struct ofono_modem *modem)
> > +{
> > +     struct gemalto_data *data;
> > +     DBusConnection *conn = ofono_dbus_get_connection();
> > +     const char *path;
> > +
> > +     if (!modem)
> > +             return;
> > +
> > +     data = ofono_modem_get_data(modem);
> > +     path = ofono_modem_get_path(modem);
> > +
> > +     if (!data)
> > +             return;
> > +
> > +#ifdef HAVE_ELL
> > +     if (data->mbim == STATE_PRESENT) {
> > +             mbim_device_shutdown(data->device);
> > +     }
> > +#endif
> > +
> > +     if (data->qmi == STATE_PRESENT) {
> > +             qmi_device_unref(data->device);
> > +     }
> > +
>
> No, we're not doing MBIM/QMI/AT all in one driver.  These should
> basically be 3 different drivers or merged into plugins/mbim
> plugins/gobi or whatever.

again, the modems are all mixed-mode with AT.
To support them properly, I need both views.
But there is no mbim+qmi combination.

Is there a way to support mbim+at today?
What do you suggest?

Regards,
Giacinto

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] new gemalto plugin
  2018-10-30  6:10   ` Giacinto Cifelli
@ 2018-10-30 15:23     ` Denis Kenzior
  2018-10-31  6:55       ` Giacinto Cifelli
  0 siblings, 1 reply; 16+ messages in thread
From: Denis Kenzior @ 2018-10-30 15:23 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 8212 bytes --]

Hi Giacinto,

>> So just a cursory look through this, but overall my impression is that
>> this code would be utterly unmaintainable.  You need to split this up
>> into something without a bazillion if conditions and #ifdefs in it.
>> Notice how none of our existing driver code uses #ifdefs.
> 
> In your existing code, there are a few models from each vendor,
> partially supported, with hardcoded choices for several options.
> 

We can only do so much without docs.  Remember, much of these drivers 
were community contributed based on reverse engineering efforts or if 
lucky, leaked docs which were frequently incomplete.  So let me flip 
this around and ask where were you all this time? ;)

>>
>> That means MBIM/QMI/AT logic needs to be separated into separate
>> drivers.  If you have a weird QMI/AT or MBIM/AT or QMI over MBIM
>> combination stuff going on, then these all need to be separate drivers.
>>
> 
> Several modems are either qmi+at or mbim+at, while others are simply
> at (for example with ppp, ecm, ncm networking).

So you might need to expand on this some more.  What is QMI+AT or 
MBIM+AT actually doing?  Is there a single AT port? Multiple? What is 
the AT port being used for, just vendor specific APIs or something more?

> 
> The qmi and mbim part is limited to initialization and gprs-context,
> the rest is common.
> Shall I duplicate everything for this?

I don't really have a recommendation yet as I don't have enough info. 
But, in general, we prefer duplication over convoluted code.  This is 
because you can turn features off via configure / plugin blacklisting. 
If you have one giant unified driver, then your hands are tied.

> 
> The #ifdefs are a choice of the project for the ELL, otherwise it
> would be simply, well-integrated ifs (like for qmi).
> I understand that the use of ELL will be extended in the future, why
> not go all the way, remove the #ifdefs, and declare a full dependency?

We can do that.  But that won't really help you in the grand scheme of 
things.  My point still stands though, we cannot have a bazillion if 
conditions in the driver with dozens/hundreds of different forks.  That 
just does not lead to maintainable code.  Lets work smarter, not harder 
here.

> 
> Reading stored commands and executing them.
> It is intended to configure the modem for a specific application.
> There are hooks for each state of the modem state machine.
> 
> There isn't much interest in passing these commands through an
> interface, because each application has its own configuration and
> sends a different set of commands.
> And it is modem-specific, so  it is stored by USB VID-PID.
> 

Since it sounds like this is a very esoteric use case, you might want to 
schedule this last.  Right now it just distracts from the core discussion.

>>
>> So first question is, why would you want to do this?  These days most
>> systems use the time on the Application processor.  Who cares what the
>> modem thinks the time is.
> 
> Some systems care: this comes in fact from an application (and will be
> committed mentioning a co-author).
> And it helps for timestamps in case of stack logs.
> 

So who do you think is going to be responsible for setting the time 
appropriately in the modem?  Remember, oFono is a user-centric API.  We 
don't expose stuff that is not somehow visible to the user.

Have you considered just having your modem driver auto-magically setting 
the time into the modem and forgetting all this API business?

>>
>> No AT command pass through in oFono upstream ;)  We've had this
>> conversation many times, if there are useful AT commands that can be
>> sent via this interface, then they should be abstracted behind a proper
>> API instead.
> 
> I may have misunderstood your comments the one time we had the conversation.
> I don't understand your blind refusal: cluttering the interface with
> every single command that an application may need for some special
> events doesn't seem that brilliant.

As I said, we're a user-centric and use-case centric API.  If a typical 
user doesn't see this or doesn't know what to do with something, then 
don't expose it.  If you can't explain a use case for something, don't 
expose it.  No typical user is going to send arbitrary AT commands and 
'application may need special events' is also not a valid use case.

Also, there are security and interference aspects to consider.  One can 
send some AT command that interferes with the functioning of an atom 
driver for example and then your entire system breaks.  Trust me, it is 
just not a good idea.  If you want to shoot yourself in the foot, be my 
guest.  But it isn't going upstream ;)

>> So the modem is doing the property validation?  Yeah, not happening ;)
> 
> The name and number of properties vary by the models.
> There isn't much to validate in a general way. Null perhaps, or an
> arbitrary maximum length.
> This interface is supposed to use GetProperties and then can change
> some of them.
> That the set property is in the list returned by GetProperties can be verified.
> 

Yeah, again.  Not a valid usecase.  We're not here to half-ass expose 
every Gemalto feature via D-Bus.  This is not what this project is about.

>> Why don't you just configure the device into NMEA mode and use
>> location_reporting atom ?
> 
> that atom is not compatible with gpsd and takes arbitrary choices on

So fix gpsd.  We're not taking upstream a vendor specific NMEA API when 
location-reporting already exists.

> how to handle the receiver, it is incomplete and adds constrains on

So fix the driver and set appropriately sane defaults?

> the dbus version.
What now?  The file-descriptor passing has been in DBus for like 8 years 
now.  I do not buy this at all.

> For example, for the gemalto atom, it starts the engine in
> assisted-gps mode, without loading the corresponding data files.
> 

So again, fix the driver?

>> How is 'powersave' useful?
> 
> If you go on holiday for 1 week, you may like that your car still
> starts when you come back.
> The modem in this kind of applications is always on, for anti-theft of
> simply so that you can blink the lights when coming out of a
> supermarket.
> But when parked, the modem remains listening for network events, and
> only ping the application (and ofono) in case of sms, for example (up
> to the specific application how to come back from sleep).
> Above, a common setting: no reg events (if we lose coverage we don't
> react instead of re-powering) and stop the gnss engine.
> 

Okay, lets deal with this later though.

>>
>> No, we're not doing MBIM/QMI/AT all in one driver.  These should
>> basically be 3 different drivers or merged into plugins/mbim
>> plugins/gobi or whatever.
> 
> again, the modems are all mixed-mode with AT.
> To support them properly, I need both views.
> But there is no mbim+qmi combination.

Yes, but others do support MBIM + QMI.

> 
> Is there a way to support mbim+at today?
> What do you suggest?

So I think we have two options we can pursue:

1. Instead of jumbling all the MBIM/QMI logic into the gemalto plugin, 
let the mbim/gobi driver take care of it.  It should be 100% the same 
anyway.  Have udevng set some attributes specific for Gemalto modems. 
Then add another plugin that registers a modem watch and gets notified 
of any modems added to the system.  If the modem has a set of magic 
attributes (e.g. ofono_modem_get_string, etc) then you know it is a 
Gemalto modem and you can bolt on additional capabilities to it.  So for 
example if all you're doing is running vendor AT command extensions over 
the AT port, you can simply have the plugin open the AT port and add any 
additional atoms / vendor APIs to the modem.  Look at 
plugins/smart-messaging.c or hfp_ag_bluez5.c for some ideas.

2. Split the single monolithic gemalto driver into 3+ drivers.  One for 
MBIM+AT, one for QMI+AT, one for Serial/USB AT.  Or alternatively extend 
plugins/gobi or plugins/mbim with Gemalto specific model quirks.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] new gemalto plugin
  2018-10-30 15:23     ` Denis Kenzior
@ 2018-10-31  6:55       ` Giacinto Cifelli
  2018-10-31 18:03         ` Pavel Machek
  2018-10-31 18:42         ` Denis Kenzior
  0 siblings, 2 replies; 16+ messages in thread
From: Giacinto Cifelli @ 2018-10-31  6:55 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 15029 bytes --]

Hi Denis,

On Tue, Oct 30, 2018 at 4:23 PM Denis Kenzior <denkenz@gmail.com> wrote:
>
> Hi Giacinto,
>
> >> So just a cursory look through this, but overall my impression is that
> >> this code would be utterly unmaintainable.  You need to split this up
> >> into something without a bazillion if conditions and #ifdefs in it.
> >> Notice how none of our existing driver code uses #ifdefs.
> >
> > In your existing code, there are a few models from each vendor,
> > partially supported, with hardcoded choices for several options.
> >
>
> We can only do so much without docs.  Remember, much of these drivers
> were community contributed based on reverse engineering efforts or if
> lucky, leaked docs which were frequently incomplete.  So let me flip
> this around and ask where were you all this time? ;)

The point here is that it is the first time a large scale operation is
attempted.
I find it normal to have all kind of options for all the models.
It won't be easy to maintain, I agree.

Below I see that your main concerns are about mixed-mode drivers,
which make only a part of the if's, all the branching is at the moment
limited to the initialization.

>
> >>
> >> That means MBIM/QMI/AT logic needs to be separated into separate
> >> drivers.  If you have a weird QMI/AT or MBIM/AT or QMI over MBIM
> >> combination stuff going on, then these all need to be separate drivers.
> >>
> >
> > Several modems are either qmi+at or mbim+at, while others are simply
> > at (for example with ppp, ecm, ncm networking).
>
> So you might need to expand on this some more.  What is QMI+AT or
> MBIM+AT actually doing?  Is there a single AT port? Multiple? What is
> the AT port being used for, just vendor specific APIs or something more?

MBIM and QMI are actually used only for the gprs-context atom.
The modems can work with the mbim or gobi plugin if they support the
respective protocols (I have tested them with changes in udevng), but
then we only get the bare minimum: no configuration options, hardware
monitor, low-power mode, gnss and its options, and the rest.
So, given that we need the AT interface anyway, everything is done
through it. It is also easier to debug than the big binary blog
(admittedly, wireshark helps a lot for mbim, but not sure for qmi).

>
> >
> > The qmi and mbim part is limited to initialization and gprs-context,
> > the rest is common.
> > Shall I duplicate everything for this?
>
> I don't really have a recommendation yet as I don't have enough info.
> But, in general, we prefer duplication over convoluted code.  This is
> because you can turn features off via configure / plugin blacklisting.
> If you have one giant unified driver, then your hands are tied.

The plugin works this way: if mbim is detected, it is initialized, and
then up to 2 AT interfaces are probed.
The same for qmi: if qmi is detected, it is initialized, and then up
to 2 AT interfaces are probed.
If ELL is not used, mbim is skipped and the modem will use PPP from
atmodem/gprs-context.
The same would be true for qmi, but there is no ifdef, so there is no
need for fallback options.
Apart from its length, the code is quite simple. When the mbim or qmi
initialization is finished, successfully or not, the plugin moves to
the AT interfaces.

QMI support is quite straightforward: only a quick initialization,
basically to make sure it is there. It doesn't need all the gobi
plugin init sequence.
MBIM is more elaborate, it needs to exchange a few frames for a proper
initialization, but then again, it finishes here. This initialization
follows the mbim plugin up to a point, but does not need the entire
mbim plugin enable sequence.
MBIM support works a bit differently depending on the chipset
manufacturer. The drivers/mbim/gprs-context works out of the box for
some models, while for others it is necessary to create the PDP
context with AT+CGDCONT. But this is dealt with a dedicated atom, only
the atom selection is visible in the plugin.

enough for the detour. Back to the plug-in initialization: the
different models have zillion of differences. Whenever possible, I
prefer to ask the modem, otherwise it is if/switch depending on the
model.
Example of the first case: all Gemalto modems support the AT^SAIC
command if there is voice support (I checked down to the MC55 that
were like rel.98 or earlier).
All switches are independent from each other, working on a simple
principle, like for voice: ascertain if the feature is present and
with which options, then select the right atom and atom options.
There are quite a few, but maintainable thanks to that. Unfortunately
not all features can be probed at startup because some require to be
online, so the checks are split in 3 parts: pre-defined with PID,
tested during the enable phase (could be shifted to pre-sim to have a
faster enable), and in post-online.
And this is the part to maintain. If I split the plugins in 3
independent ones, I need to duplicate and maintain this.



>
> >
> > The #ifdefs are a choice of the project for the ELL, otherwise it
> > would be simply, well-integrated ifs (like for qmi).
> > I understand that the use of ELL will be extended in the future, why
> > not go all the way, remove the #ifdefs, and declare a full dependency?
>
> We can do that.  But that won't really help you in the grand scheme of
> things.  My point still stands though, we cannot have a bazillion if
> conditions in the driver with dozens/hundreds of different forks.  That
> just does not lead to maintainable code.  Lets work smarter, not harder
> here.
>

as explained, the bazillion conditions will remain. maybe only
mbim/qmi/plainAt will disappear. And all the vendor-specific atoms
will have to be duplicated too.

> >
> > Reading stored commands and executing them.
> > It is intended to configure the modem for a specific application.
> > There are hooks for each state of the modem state machine.
> >
> > There isn't much interest in passing these commands through an
> > interface, because each application has its own configuration and
> > sends a different set of commands.
> > And it is modem-specific, so  it is stored by USB VID-PID.
> >
>
> Since it sounds like this is a very esoteric use case, you might want to
> schedule this last.  Right now it just distracts from the core discussion.

ok for having it last, but it is not so esoteric.
Take for example the audio settings (which most likely exist for all
other vendors): you can select the type of interface
(analog/digital/usb), the port to use in case there is more than one,
suboptions like I2C or PCM, master/slave, and quite some other.
They could be set in a quite large vendor-specific interface (I saw
some tried it in an atom), but the configuration is fixed for the end
application, and - as you explained - will raise more problems than it
solves exposing these settings through dbus.
So the best is to run a couple of commands to set the interface
properly for the product and that's it.

>
> >>
> >> So first question is, why would you want to do this?  These days most
> >> systems use the time on the Application processor.  Who cares what the
> >> modem thinks the time is.
> >
> > Some systems care: this comes in fact from an application (and will be
> > committed mentioning a co-author).
> > And it helps for timestamps in case of stack logs.
> >
>
> So who do you think is going to be responsible for setting the time
> appropriately in the modem?  Remember, oFono is a user-centric API.  We
> don't expose stuff that is not somehow visible to the user.
>
> Have you considered just having your modem driver auto-magically setting
> the time into the modem and forgetting all this API business?
>

I'll look into this with the customer who proposed it, most likely
reading this conversation already.

> >>
> >> No AT command pass through in oFono upstream ;)  We've had this
> >> conversation many times, if there are useful AT commands that can be
> >> sent via this interface, then they should be abstracted behind a proper
> >> API instead.
> >
> > I may have misunderstood your comments the one time we had the conversation.
> > I don't understand your blind refusal: cluttering the interface with
> > every single command that an application may need for some special
> > events doesn't seem that brilliant.
>
> As I said, we're a user-centric and use-case centric API.  If a typical
> user doesn't see this or doesn't know what to do with something, then
> don't expose it.  If you can't explain a use case for something, don't
> expose it.  No typical user is going to send arbitrary AT commands and
> 'application may need special events' is also not a valid use case.
>
> Also, there are security and interference aspects to consider.  One can
> send some AT command that interferes with the functioning of an atom
> driver for example and then your entire system breaks.  Trust me, it is
> just not a good idea.  If you want to shoot yourself in the foot, be my
> guest.  But it isn't going upstream ;)

it's ok, GPLv2 just requires me to publish the changes I have done to
the code, not to fight to get them upstream.
I have posted this special case, it is public, so I won't discuss it
further here.
If Gemalto users want it, we will give it to them.

Nor I will go into all the use cases of ofono today. There is some
truth in what you say as the initial vision for the project, but
apparently it is growing out of that.

>
> >> So the modem is doing the property validation?  Yeah, not happening ;)
> >
> > The name and number of properties vary by the models.
> > There isn't much to validate in a general way. Null perhaps, or an
> > arbitrary maximum length.
> > This interface is supposed to use GetProperties and then can change
> > some of them.
> > That the set property is in the list returned by GetProperties can be verified.
> >
>
> Yeah, again.  Not a valid usecase.  We're not here to half-ass expose
> every Gemalto feature via D-Bus.  This is not what this project is about.
>

ditto for this atom: I have fulfilled the GPLv2 requirements.

Most of the options would make sense in a generic atom (like: which
satellites constellations to use for the fix (GPS, GLONASS, GALILEO,
Beidou, QZSS, ...), which to output, how to output them, NMEA version
to use, start/stop the receiver, start/stop the output, etc.
But some others would still require a vendor-specific plug-in, like on
which port to output the NMEA sentences, whether to exclude satellites
below a given angle, etc.


> >> Why don't you just configure the device into NMEA mode and use
> >> location_reporting atom ?
> >
> > that atom is not compatible with gpsd and takes arbitrary choices on
>
> So fix gpsd.  We're not taking upstream a vendor specific NMEA API when
> location-reporting already exists.

Changing it would make the dbus interface incompatible.
gpsd takes a device, not an fd, but the atom initialize the receiver
and opens the port.
This kind of linked mechanisms make policies, and are quite impossible
to break once done.
In my opinion, it is a bad design for linux, because the policies
should be a step higher, but it wasn't detected in time and now it has
to be like that.
You may have noticed that I didn't remove this atom from the plugin,
because there are now users for it.
And by the way: passing the fd most likely means that each client is
implementing its own  NMEA parser, with risks of bugs, duplication of
effort and lack of standardization. The atom encourages bad practices.

>
> > how to handle the receiver, it is incomplete and adds constrains on
>
> So fix the driver and set appropriately sane defaults?
>
> > the dbus version.
> What now?  The file-descriptor passing has been in DBus for like 8 years
> now.  I do not buy this at all.
>
> > For example, for the gemalto atom, it starts the engine in
> > assisted-gps mode, without loading the corresponding data files.
> >
>
> So again, fix the driver?
>
> >> How is 'powersave' useful?
> >
> > If you go on holiday for 1 week, you may like that your car still
> > starts when you come back.
> > The modem in this kind of applications is always on, for anti-theft of
> > simply so that you can blink the lights when coming out of a
> > supermarket.
> > But when parked, the modem remains listening for network events, and
> > only ping the application (and ofono) in case of sms, for example (up
> > to the specific application how to come back from sleep).
> > Above, a common setting: no reg events (if we lose coverage we don't
> > react instead of re-powering) and stop the gnss engine.
> >
>
> Okay, lets deal with this later though.

ok

>
> >>
> >> No, we're not doing MBIM/QMI/AT all in one driver.  These should
> >> basically be 3 different drivers or merged into plugins/mbim
> >> plugins/gobi or whatever.
> >
> > again, the modems are all mixed-mode with AT.
> > To support them properly, I need both views.
> > But there is no mbim+qmi combination.
>
> Yes, but others do support MBIM + QMI.

this might be a different use case, like all communication is qmi and
only carried over mbim frames. This would require a different plugin,
but I am not in this case.

>
> >
> > Is there a way to support mbim+at today?
> > What do you suggest?
>
> So I think we have two options we can pursue:
>
> 1. Instead of jumbling all the MBIM/QMI logic into the gemalto plugin,
> let the mbim/gobi driver take care of it.  It should be 100% the same
> anyway.  Have udevng set some attributes specific for Gemalto modems.
> Then add another plugin that registers a modem watch and gets notified
> of any modems added to the system.  If the modem has a set of magic
> attributes (e.g. ofono_modem_get_string, etc) then you know it is a
> Gemalto modem and you can bolt on additional capabilities to it.  So for
> example if all you're doing is running vendor AT command extensions over
> the AT port, you can simply have the plugin open the AT port and add any
> additional atoms / vendor APIs to the modem.  Look at
> plugins/smart-messaging.c or hfp_ag_bluez5.c for some ideas.
>
> 2. Split the single monolithic gemalto driver into 3+ drivers.  One for
> MBIM+AT, one for QMI+AT, one for Serial/USB AT.  Or alternatively extend
> plugins/gobi or plugins/mbim with Gemalto specific model quirks.

the second option is more interesting, but would make the behavior
switches in the plugin duplicated.
What about extracting the qmi and mbim initialization in a separate
file, so not to distract the reader?
All modems are, after all, AT modems, with special initialization for
dedicated interfaces. It initializes mbim just like it does for gnss,
only this code takes more space.

>
> Regards,
> -Denis

Regards,
Giacinto

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] new gemalto plugin
  2018-10-31  6:55       ` Giacinto Cifelli
@ 2018-10-31 18:03         ` Pavel Machek
  2018-10-31 19:45           ` Giacinto Cifelli
  2018-10-31 18:42         ` Denis Kenzior
  1 sibling, 1 reply; 16+ messages in thread
From: Pavel Machek @ 2018-10-31 18:03 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 822 bytes --]

Hi!

> > Also, there are security and interference aspects to consider.  One can
> > send some AT command that interferes with the functioning of an atom
> > driver for example and then your entire system breaks.  Trust me, it is
> > just not a good idea.  If you want to shoot yourself in the foot, be my
> > guest.  But it isn't going upstream ;)
> 
> it's ok, GPLv2 just requires me to publish the changes I have done to
> the code, not to fight to get them upstream.

Actually, GPLv2 does not you require to publish your changes.

It just (approximately) means that if you give binaries to someone, you give them
sources, too...

Best regards,
									Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] new gemalto plugin
  2018-10-31  6:55       ` Giacinto Cifelli
  2018-10-31 18:03         ` Pavel Machek
@ 2018-10-31 18:42         ` Denis Kenzior
  2018-10-31 19:07           ` Marcel Holtmann
  2018-10-31 19:56           ` Giacinto Cifelli
  1 sibling, 2 replies; 16+ messages in thread
From: Denis Kenzior @ 2018-10-31 18:42 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 9900 bytes --]

Hi Giacinto,

>> So you might need to expand on this some more.  What is QMI+AT or
>> MBIM+AT actually doing?  Is there a single AT port? Multiple? What is
>> the AT port being used for, just vendor specific APIs or something more?
> 
> MBIM and QMI are actually used only for the gprs-context atom.
> The modems can work with the mbim or gobi plugin if they support the
> respective protocols (I have tested them with changes in udevng), but
> then we only get the bare minimum: no configuration options, hardware
> monitor, low-power mode, gnss and its options, and the rest.
> So, given that we need the AT interface anyway, everything is done
> through it. It is also easier to debug than the big binary blog
> (admittedly, wireshark helps a lot for mbim, but not sure for qmi).
> 

You really need to decide what you want to do here.  If your modem is 
MBIM, then MBIM driver needs to be used.  I really don't want to get 
into situations where we build some Frankenstein driver with 
gprs-context being driven via MBIM and netreg being driven via AT commands.

I can understand if you have MBIM + a GPS/GNSS port and want to expose 
location-reporting on that.  Ok, fine, I get that.  I can also 
understand if you want to use the AT command port for some vendor 
specific extensions not available via MBIM.  But I would need serious 
convincing of anything beyond this.

> MBIM support works a bit differently depending on the chipset
> manufacturer. The drivers/mbim/gprs-context works out of the box for
> some models, while for others it is necessary to create the PDP
> context with AT+CGDCONT. But this is dealt with a dedicated atom, only
> the atom selection is visible in the plugin.

So explain that one to me?  You send all context details in 
MBIM_CID_CONNECT.  So why would a CGDCONT be needed?

> 
> enough for the detour. Back to the plug-in initialization: the
> different models have zillion of differences. Whenever possible, I
> prefer to ask the modem, otherwise it is if/switch depending on the
> model.
> Example of the first case: all Gemalto modems support the AT^SAIC
> command if there is voice support (I checked down to the MC55 that
> were like rel.98 or earlier).

You do realize that all this probing can be done in the individual atom 
driver, right?  Even as far as the driver being able to call 
ofono_foo_remove if support is lacking.

> All switches are independent from each other, working on a simple
> principle, like for voice: ascertain if the feature is present and
> with which options, then select the right atom and atom options.
> There are quite a few, but maintainable thanks to that. Unfortunately
> not all features can be probed at startup because some require to be
> online, so the checks are split in 3 parts: pre-defined with PID,
> tested during the enable phase (could be shifted to pre-sim to have a
> faster enable), and in post-online.

We've been through this, no AT commands should be executed in pre_sim, 
post_sim, post_online hooks...

> And this is the part to maintain. If I split the plugins in 3
> independent ones, I need to duplicate and maintain this.

No, you really don't.  It might take a while for you to be convinced though.

> mbim/qmi/plainAt will disappear. And all the vendor-specific atoms
> will have to be duplicated too.

Again, they really don't need to be duplicated...

>> Since it sounds like this is a very esoteric use case, you might want to
>> schedule this last.  Right now it just distracts from the core discussion.
> 
> ok for having it last, but it is not so esoteric.
> Take for example the audio settings (which most likely exist for all
> other vendors): you can select the type of interface
> (analog/digital/usb), the port to use in case there is more than one,
> suboptions like I2C or PCM, master/slave, and quite some other.
> They could be set in a quite large vendor-specific interface (I saw
> some tried it in an atom), but the configuration is fixed for the end
> application, and - as you explained - will raise more problems than it
> solves exposing these settings through dbus.
> So the best is to run a couple of commands to set the interface
> properly for the product and that's it.

I understand.  But this is also why we have audio-settings atom.  So all 
such details can be easily put in there and its relevant driver.  If you 
want to have some simple configuration file that is read by your driver 
where the user can select between some common configurations that is 
fine as well.

One can also add additional drivers / plugins out-of-tree that would be 
dynamically loaded and do whatever they desire.  But reading a file and 
blindly executing AT commands in a daemon that is running as root is 
just asking for trouble.  I don't really care what you do in your 
products, but I'm not taking security holes upstream.

>>
>> Have you considered just having your modem driver auto-magically setting
>> the time into the modem and forgetting all this API business?
>>
> 
> I'll look into this with the customer who proposed it, most likely
> reading this conversation already.
> 

So as a hint, your customer might want to start engaging with us 
directly and skip the middle men.  We do have some experience with 
integration given that between oFono and its sister projects we control 
the Bluetooth stack, WiFi stack, cellular stack, NFC stack and the 
overall network management ;)

> it's ok, GPLv2 just requires me to publish the changes I have done to
> the code, not to fight to get them upstream.
> I have posted this special case, it is public, so I won't discuss it
> further here.
> If Gemalto users want it, we will give it to them.
> 

I can only advise you what is acceptable to the oFono upstream project. 
What you do in the end is really up to you and your customers.  But we 
do have certain standards with respect to security and code quality 
which we are not going to compromise on.

> 
> Most of the options would make sense in a generic atom (like: which
> satellites constellations to use for the fix (GPS, GLONASS, GALILEO,
> Beidou, QZSS, ...), which to output, how to output them, NMEA version
> to use, start/stop the receiver, start/stop the output, etc.

So maybe you want to start there and propose some improvements to the 
location reporting APIs?

> But some others would still require a vendor-specific plug-in, like on
> which port to output the NMEA sentences, whether to exclude satellites
> below a given angle, etc.

It seems like this is a platform specific configuration and really 
doesn't belong being exported over D-Bus anyway.

>> So fix gpsd.  We're not taking upstream a vendor specific NMEA API when
>> location-reporting already exists.
> 
> Changing it would make the dbus interface incompatible.
> gpsd takes a device, not an fd, but the atom initialize the receiver
> and opens the port.

So? You can always come up with a simple pseudo-tty proxy?  That's like 
20 minutes of work.  Or maybe 5 in Python.

> This kind of linked mechanisms make policies, and are quite impossible
> to break once done.
> In my opinion, it is a bad design for linux, because the policies
> should be a step higher, but it wasn't detected in time and now it has
> to be like that.
> You may have noticed that I didn't remove this atom from the plugin,
> because there are now users for it.
> And by the way: passing the fd most likely means that each client is
> implementing its own  NMEA parser, with risks of bugs, duplication of
> effort and lack of standardization. The atom encourages bad practices.
> 

Ok, thanks for the rant, but I will have to disagree completely here. 
LocationReporting was done the way it was for very specific reasons.  We 
can't assume that the GPS port will be on one of the USB TTYs.  It may 
be this way on your products, but it definitely is not this way on 
others.  For example, it is exported through phonet or QMI on some devices.

Many modems also need magic commands / setup prior to being able to use 
the GPS receiver.  So the LocationReporting API takes care of that. 
This also allows us to keep track of client lifetime.  So that if/when 
the client dies we can automatically turn off the GPS unit and save some 
battery.

Honestly I'd really like to see GPS integrated properly into the kernel, 
with the ability for userspace daemons like oFono to provide 
drivers/devices to the kernel.  Maybe Pavel has some additional ideas on 
how we could accomplish that.

>> Yes, but others do support MBIM + QMI.
> 
> this might be a different use case, like all communication is qmi and
> only carried over mbim frames. This would require a different plugin,

Not all, but some, yes.

> but I am not in this case.

<snip>

>>
>> 2. Split the single monolithic gemalto driver into 3+ drivers.  One for
>> MBIM+AT, one for QMI+AT, one for Serial/USB AT.  Or alternatively extend
>> plugins/gobi or plugins/mbim with Gemalto specific model quirks.
> 
> the second option is more interesting, but would make the behavior
> switches in the plugin duplicated.
> What about extracting the qmi and mbim initialization in a separate
> file, so not to distract the reader?

As mentioned earlier, we're not doing any Frankenstein drivers.

> All modems are, after all, AT modems, with special initialization for
> dedicated interfaces. It initializes mbim just like it does for gnss,
> only this code takes more space.

Are you talking about your modems here?  Because certainly not 'all' 
modems are AT modems.  Our modems for example are binary protocol based 
with AT commands bolted on top.  Also, many vendors do not provide AT 
ports for MBIM/QMI, etc based devices.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] new gemalto plugin
  2018-10-31 18:42         ` Denis Kenzior
@ 2018-10-31 19:07           ` Marcel Holtmann
  2018-10-31 19:23             ` Denis Kenzior
  2018-10-31 19:56           ` Giacinto Cifelli
  1 sibling, 1 reply; 16+ messages in thread
From: Marcel Holtmann @ 2018-10-31 19:07 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 1633 bytes --]

Hi Denis,

>>> So you might need to expand on this some more.  What is QMI+AT or
>>> MBIM+AT actually doing?  Is there a single AT port? Multiple? What is
>>> the AT port being used for, just vendor specific APIs or something more?
>> MBIM and QMI are actually used only for the gprs-context atom.
>> The modems can work with the mbim or gobi plugin if they support the
>> respective protocols (I have tested them with changes in udevng), but
>> then we only get the bare minimum: no configuration options, hardware
>> monitor, low-power mode, gnss and its options, and the rest.
>> So, given that we need the AT interface anyway, everything is done
>> through it. It is also easier to debug than the big binary blog
>> (admittedly, wireshark helps a lot for mbim, but not sure for qmi).
> 
> You really need to decide what you want to do here.  If your modem is MBIM, then MBIM driver needs to be used.  I really don't want to get into situations where we build some Frankenstein driver with gprs-context being driven via MBIM and netreg being driven via AT commands.
> 
> I can understand if you have MBIM + a GPS/GNSS port and want to expose location-reporting on that.  Ok, fine, I get that.  I can also understand if you want to use the AT command port for some vendor specific extensions not available via MBIM.  But I would need serious convincing of anything beyond this.

I think that MBIM (and even QMI) have AT passthrough options. So by all means, the main transport suppose to be MBIM here. I always prefer if only one hardware port is opened and there is no need to open more.

Regards

Marcel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] new gemalto plugin
  2018-10-31 19:07           ` Marcel Holtmann
@ 2018-10-31 19:23             ` Denis Kenzior
  2018-10-31 19:44               ` Giacinto Cifelli
  0 siblings, 1 reply; 16+ messages in thread
From: Denis Kenzior @ 2018-10-31 19:23 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 473 bytes --]

Hi Marcel,

> 
> I think that MBIM (and even QMI) have AT passthrough options. So by all means, the main transport suppose to be MBIM here. I always prefer if only one hardware port is opened and there is no need to open more.

I've seen QMI over MBIM some vendor extended service UIDs.  Not sure 
about AT over MBIM.  If someone knows more, feel free to educate me.

Isn't AT over QMI also possible?  And in theory AT over QMI over MBIM, lol.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] new gemalto plugin
  2018-10-31 19:23             ` Denis Kenzior
@ 2018-10-31 19:44               ` Giacinto Cifelli
  0 siblings, 0 replies; 16+ messages in thread
From: Giacinto Cifelli @ 2018-10-31 19:44 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 901 bytes --]

Hi,

On Wed, Oct 31, 2018 at 8:23 PM Denis Kenzior <denkenz@gmail.com> wrote:
>
> Hi Marcel,
>
> >
> > I think that MBIM (and even QMI) have AT passthrough options. So by all means, the main transport suppose to be MBIM here. I always prefer if only one hardware port is opened and there is no need to open more.
>
> I've seen QMI over MBIM some vendor extended service UIDs.  Not sure
> about AT over MBIM.  If someone knows more, feel free to educate me.
>
> Isn't AT over QMI also possible?  And in theory AT over QMI over MBIM, lol.
>

AT over MBIM is possible, but we decided not to implement it.
The big advantage of MBIM is a ready-made driver on Windows and on
MM/ofono. Adding AT over it would mean reworking the driver, so no
more advantages.
Over QMI I don't know.
QMI over MBIM is also possible, but with the same disadvantage as AT over MBIM.

> Regards,
> -Denis

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] new gemalto plugin
  2018-10-31 18:03         ` Pavel Machek
@ 2018-10-31 19:45           ` Giacinto Cifelli
  0 siblings, 0 replies; 16+ messages in thread
From: Giacinto Cifelli @ 2018-10-31 19:45 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 1045 bytes --]

H Pavel,

On Wed, Oct 31, 2018 at 7:03 PM Pavel Machek <pavel@ucw.cz> wrote:
>
> Hi!
>
> > > Also, there are security and interference aspects to consider.  One can
> > > send some AT command that interferes with the functioning of an atom
> > > driver for example and then your entire system breaks.  Trust me, it is
> > > just not a good idea.  If you want to shoot yourself in the foot, be my
> > > guest.  But it isn't going upstream ;)
> >
> > it's ok, GPLv2 just requires me to publish the changes I have done to
> > the code, not to fight to get them upstream.
>
> Actually, GPLv2 does not you require to publish your changes.
>
> It just (approximately) means that if you give binaries to someone, you give them
> sources, too...
>

even better!

> Best regards,
>                                                                         Pavel
>
> --
> (english) http://www.livejournal.com/~pavelmachek
> (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

Regards,
Giacinto

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] new gemalto plugin
  2018-10-31 18:42         ` Denis Kenzior
  2018-10-31 19:07           ` Marcel Holtmann
@ 2018-10-31 19:56           ` Giacinto Cifelli
  2018-10-31 20:15             ` Denis Kenzior
  2018-10-31 20:19             ` Marcel Holtmann
  1 sibling, 2 replies; 16+ messages in thread
From: Giacinto Cifelli @ 2018-10-31 19:56 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 11338 bytes --]

Hi Denis,

On Wed, Oct 31, 2018 at 7:42 PM Denis Kenzior <denkenz@gmail.com> wrote:
>
> Hi Giacinto,
>
> >> So you might need to expand on this some more.  What is QMI+AT or
> >> MBIM+AT actually doing?  Is there a single AT port? Multiple? What is
> >> the AT port being used for, just vendor specific APIs or something more?
> >
> > MBIM and QMI are actually used only for the gprs-context atom.
> > The modems can work with the mbim or gobi plugin if they support the
> > respective protocols (I have tested them with changes in udevng), but
> > then we only get the bare minimum: no configuration options, hardware
> > monitor, low-power mode, gnss and its options, and the rest.
> > So, given that we need the AT interface anyway, everything is done
> > through it. It is also easier to debug than the big binary blog
> > (admittedly, wireshark helps a lot for mbim, but not sure for qmi).
> >
>
> You really need to decide what you want to do here.  If your modem is
> MBIM, then MBIM driver needs to be used.  I really don't want to get
> into situations where we build some Frankenstein driver with
> gprs-context being driven via MBIM and netreg being driven via AT commands.
>
> I can understand if you have MBIM + a GPS/GNSS port and want to expose
> location-reporting on that.  Ok, fine, I get that.  I can also
> understand if you want to use the AT command port for some vendor
> specific extensions not available via MBIM.  But I would need serious
> convincing of anything beyond this.
>
> > MBIM support works a bit differently depending on the chipset
> > manufacturer. The drivers/mbim/gprs-context works out of the box for
> > some models, while for others it is necessary to create the PDP
> > context with AT+CGDCONT. But this is dealt with a dedicated atom, only
> > the atom selection is visible in the plugin.
>
> So explain that one to me?  You send all context details in
> MBIM_CID_CONNECT.  So why would a CGDCONT be needed?
>
> >
> > enough for the detour. Back to the plug-in initialization: the
> > different models have zillion of differences. Whenever possible, I
> > prefer to ask the modem, otherwise it is if/switch depending on the
> > model.
> > Example of the first case: all Gemalto modems support the AT^SAIC
> > command if there is voice support (I checked down to the MC55 that
> > were like rel.98 or earlier).
>
> You do realize that all this probing can be done in the individual atom
> driver, right?  Even as far as the driver being able to call
> ofono_foo_remove if support is lacking.
>
> > All switches are independent from each other, working on a simple
> > principle, like for voice: ascertain if the feature is present and
> > with which options, then select the right atom and atom options.
> > There are quite a few, but maintainable thanks to that. Unfortunately
> > not all features can be probed at startup because some require to be
> > online, so the checks are split in 3 parts: pre-defined with PID,
> > tested during the enable phase (could be shifted to pre-sim to have a
> > faster enable), and in post-online.
>
> We've been through this, no AT commands should be executed in pre_sim,
> post_sim, post_online hooks...
>
> > And this is the part to maintain. If I split the plugins in 3
> > independent ones, I need to duplicate and maintain this.
>
> No, you really don't.  It might take a while for you to be convinced though.
>
> > mbim/qmi/plainAt will disappear. And all the vendor-specific atoms
> > will have to be duplicated too.
>
> Again, they really don't need to be duplicated...
>
> >> Since it sounds like this is a very esoteric use case, you might want to
> >> schedule this last.  Right now it just distracts from the core discussion.
> >
> > ok for having it last, but it is not so esoteric.
> > Take for example the audio settings (which most likely exist for all
> > other vendors): you can select the type of interface
> > (analog/digital/usb), the port to use in case there is more than one,
> > suboptions like I2C or PCM, master/slave, and quite some other.
> > They could be set in a quite large vendor-specific interface (I saw
> > some tried it in an atom), but the configuration is fixed for the end
> > application, and - as you explained - will raise more problems than it
> > solves exposing these settings through dbus.
> > So the best is to run a couple of commands to set the interface
> > properly for the product and that's it.
>
> I understand.  But this is also why we have audio-settings atom.  So all
> such details can be easily put in there and its relevant driver.  If you
> want to have some simple configuration file that is read by your driver
> where the user can select between some common configurations that is
> fine as well.
>
> One can also add additional drivers / plugins out-of-tree that would be
> dynamically loaded and do whatever they desire.  But reading a file and
> blindly executing AT commands in a daemon that is running as root is
> just asking for trouble.  I don't really care what you do in your
> products, but I'm not taking security holes upstream.
>
> >>
> >> Have you considered just having your modem driver auto-magically setting
> >> the time into the modem and forgetting all this API business?
> >>
> >
> > I'll look into this with the customer who proposed it, most likely
> > reading this conversation already.
> >
>
> So as a hint, your customer might want to start engaging with us
> directly and skip the middle men.  We do have some experience with
> integration given that between oFono and its sister projects we control
> the Bluetooth stack, WiFi stack, cellular stack, NFC stack and the
> overall network management ;)
>
> > it's ok, GPLv2 just requires me to publish the changes I have done to
> > the code, not to fight to get them upstream.
> > I have posted this special case, it is public, so I won't discuss it
> > further here.
> > If Gemalto users want it, we will give it to them.
> >
>
> I can only advise you what is acceptable to the oFono upstream project.
> What you do in the end is really up to you and your customers.  But we
> do have certain standards with respect to security and code quality
> which we are not going to compromise on.
>
> >
> > Most of the options would make sense in a generic atom (like: which
> > satellites constellations to use for the fix (GPS, GLONASS, GALILEO,
> > Beidou, QZSS, ...), which to output, how to output them, NMEA version
> > to use, start/stop the receiver, start/stop the output, etc.
>
> So maybe you want to start there and propose some improvements to the
> location reporting APIs?
>
> > But some others would still require a vendor-specific plug-in, like on
> > which port to output the NMEA sentences, whether to exclude satellites
> > below a given angle, etc.
>
> It seems like this is a platform specific configuration and really
> doesn't belong being exported over D-Bus anyway.
>
> >> So fix gpsd.  We're not taking upstream a vendor specific NMEA API when
> >> location-reporting already exists.
> >
> > Changing it would make the dbus interface incompatible.
> > gpsd takes a device, not an fd, but the atom initialize the receiver
> > and opens the port.
>
> So? You can always come up with a simple pseudo-tty proxy?  That's like
> 20 minutes of work.  Or maybe 5 in Python.
>
> > This kind of linked mechanisms make policies, and are quite impossible
> > to break once done.
> > In my opinion, it is a bad design for linux, because the policies
> > should be a step higher, but it wasn't detected in time and now it has
> > to be like that.
> > You may have noticed that I didn't remove this atom from the plugin,
> > because there are now users for it.
> > And by the way: passing the fd most likely means that each client is
> > implementing its own  NMEA parser, with risks of bugs, duplication of
> > effort and lack of standardization. The atom encourages bad practices.
> >
>
> Ok, thanks for the rant, but I will have to disagree completely here.
> LocationReporting was done the way it was for very specific reasons.  We
> can't assume that the GPS port will be on one of the USB TTYs.  It may
> be this way on your products, but it definitely is not this way on
> others.  For example, it is exported through phonet or QMI on some devices.
>
> Many modems also need magic commands / setup prior to being able to use
> the GPS receiver.  So the LocationReporting API takes care of that.
> This also allows us to keep track of client lifetime.  So that if/when
> the client dies we can automatically turn off the GPS unit and save some
> battery.
>
> Honestly I'd really like to see GPS integrated properly into the kernel,
> with the ability for userspace daemons like oFono to provide
> drivers/devices to the kernel.  Maybe Pavel has some additional ideas on
> how we could accomplish that.
>
> >> Yes, but others do support MBIM + QMI.
> >
> > this might be a different use case, like all communication is qmi and
> > only carried over mbim frames. This would require a different plugin,
>
> Not all, but some, yes.
>
> > but I am not in this case.
>
> <snip>
>
> >>
> >> 2. Split the single monolithic gemalto driver into 3+ drivers.  One for
> >> MBIM+AT, one for QMI+AT, one for Serial/USB AT.  Or alternatively extend
> >> plugins/gobi or plugins/mbim with Gemalto specific model quirks.
> >
> > the second option is more interesting, but would make the behavior
> > switches in the plugin duplicated.
> > What about extracting the qmi and mbim initialization in a separate
> > file, so not to distract the reader?
>
> As mentioned earlier, we're not doing any Frankenstein drivers.
>
> > All modems are, after all, AT modems, with special initialization for
> > dedicated interfaces. It initializes mbim just like it does for gnss,
> > only this code takes more space.
>
> Are you talking about your modems here?  Because certainly not 'all'
> modems are AT modems.  Our modems for example are binary protocol based
> with AT commands bolted on top.  Also, many vendors do not provide AT
> ports for MBIM/QMI, etc based devices.
>

I am talking about the Gemalto modems in this plugin. They are all AT
modems, with extensions for the gprs-context.

Reading the ofono documentation, I kind of understood that the very
structure of ofono is so done that a plug-in can call the atoms that
it needs.
Was that all just advertisement?

And the Frankenstein idea was good, it is called also cherry-picking.
The drama that it got out of control is just... drama.

Gemalto prefers to use the AT interface because it is well understood,
easy to format, and has an excellent test coverage.
So it will remain the preferred interface when there is a choice
between it and something else.

MBIM is not even supported through the glib, but through the ell
library that is itself quite fresh (and for which there is little
advantage instead of the glib. Maybe because using glib you have to
listen to someone else's opinion for pushing changes).

I will think about the entire conversation and see how to proceed.

> Regards,
> -Denis

Regards,
Giacinto

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] new gemalto plugin
  2018-10-31 19:56           ` Giacinto Cifelli
@ 2018-10-31 20:15             ` Denis Kenzior
  2018-10-31 20:19             ` Marcel Holtmann
  1 sibling, 0 replies; 16+ messages in thread
From: Denis Kenzior @ 2018-10-31 20:15 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 700 bytes --]

Hi Giacinto,

> 
> MBIM is not even supported through the glib, but through the ell
> library that is itself quite fresh (and for which there is little
> advantage instead of the glib. Maybe because using glib you have to
> listen to someone else's opinion for pushing changes).

Look, I understand you're unhappy, but please don't make stuff up.  We 
are unhappy with glib due to its size (including the GObject stuff) and 
not for any other reason you speculate.

And ell is now almost 7 years old and used by multiple serious projects. 
  It is now also a dependency for oFono starting with the next release.
So please, stop the negativity.  It is uncalled for.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] new gemalto plugin
  2018-10-31 19:56           ` Giacinto Cifelli
  2018-10-31 20:15             ` Denis Kenzior
@ 2018-10-31 20:19             ` Marcel Holtmann
  2018-10-31 20:32               ` Giacinto Cifelli
  1 sibling, 1 reply; 16+ messages in thread
From: Marcel Holtmann @ 2018-10-31 20:19 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 12138 bytes --]

Hi Giacinto,

>>>> So you might need to expand on this some more.  What is QMI+AT or
>>>> MBIM+AT actually doing?  Is there a single AT port? Multiple? What is
>>>> the AT port being used for, just vendor specific APIs or something more?
>>> 
>>> MBIM and QMI are actually used only for the gprs-context atom.
>>> The modems can work with the mbim or gobi plugin if they support the
>>> respective protocols (I have tested them with changes in udevng), but
>>> then we only get the bare minimum: no configuration options, hardware
>>> monitor, low-power mode, gnss and its options, and the rest.
>>> So, given that we need the AT interface anyway, everything is done
>>> through it. It is also easier to debug than the big binary blog
>>> (admittedly, wireshark helps a lot for mbim, but not sure for qmi).
>>> 
>> 
>> You really need to decide what you want to do here.  If your modem is
>> MBIM, then MBIM driver needs to be used.  I really don't want to get
>> into situations where we build some Frankenstein driver with
>> gprs-context being driven via MBIM and netreg being driven via AT commands.
>> 
>> I can understand if you have MBIM + a GPS/GNSS port and want to expose
>> location-reporting on that.  Ok, fine, I get that.  I can also
>> understand if you want to use the AT command port for some vendor
>> specific extensions not available via MBIM.  But I would need serious
>> convincing of anything beyond this.
>> 
>>> MBIM support works a bit differently depending on the chipset
>>> manufacturer. The drivers/mbim/gprs-context works out of the box for
>>> some models, while for others it is necessary to create the PDP
>>> context with AT+CGDCONT. But this is dealt with a dedicated atom, only
>>> the atom selection is visible in the plugin.
>> 
>> So explain that one to me?  You send all context details in
>> MBIM_CID_CONNECT.  So why would a CGDCONT be needed?
>> 
>>> 
>>> enough for the detour. Back to the plug-in initialization: the
>>> different models have zillion of differences. Whenever possible, I
>>> prefer to ask the modem, otherwise it is if/switch depending on the
>>> model.
>>> Example of the first case: all Gemalto modems support the AT^SAIC
>>> command if there is voice support (I checked down to the MC55 that
>>> were like rel.98 or earlier).
>> 
>> You do realize that all this probing can be done in the individual atom
>> driver, right?  Even as far as the driver being able to call
>> ofono_foo_remove if support is lacking.
>> 
>>> All switches are independent from each other, working on a simple
>>> principle, like for voice: ascertain if the feature is present and
>>> with which options, then select the right atom and atom options.
>>> There are quite a few, but maintainable thanks to that. Unfortunately
>>> not all features can be probed at startup because some require to be
>>> online, so the checks are split in 3 parts: pre-defined with PID,
>>> tested during the enable phase (could be shifted to pre-sim to have a
>>> faster enable), and in post-online.
>> 
>> We've been through this, no AT commands should be executed in pre_sim,
>> post_sim, post_online hooks...
>> 
>>> And this is the part to maintain. If I split the plugins in 3
>>> independent ones, I need to duplicate and maintain this.
>> 
>> No, you really don't.  It might take a while for you to be convinced though.
>> 
>>> mbim/qmi/plainAt will disappear. And all the vendor-specific atoms
>>> will have to be duplicated too.
>> 
>> Again, they really don't need to be duplicated...
>> 
>>>> Since it sounds like this is a very esoteric use case, you might want to
>>>> schedule this last.  Right now it just distracts from the core discussion.
>>> 
>>> ok for having it last, but it is not so esoteric.
>>> Take for example the audio settings (which most likely exist for all
>>> other vendors): you can select the type of interface
>>> (analog/digital/usb), the port to use in case there is more than one,
>>> suboptions like I2C or PCM, master/slave, and quite some other.
>>> They could be set in a quite large vendor-specific interface (I saw
>>> some tried it in an atom), but the configuration is fixed for the end
>>> application, and - as you explained - will raise more problems than it
>>> solves exposing these settings through dbus.
>>> So the best is to run a couple of commands to set the interface
>>> properly for the product and that's it.
>> 
>> I understand.  But this is also why we have audio-settings atom.  So all
>> such details can be easily put in there and its relevant driver.  If you
>> want to have some simple configuration file that is read by your driver
>> where the user can select between some common configurations that is
>> fine as well.
>> 
>> One can also add additional drivers / plugins out-of-tree that would be
>> dynamically loaded and do whatever they desire.  But reading a file and
>> blindly executing AT commands in a daemon that is running as root is
>> just asking for trouble.  I don't really care what you do in your
>> products, but I'm not taking security holes upstream.
>> 
>>>> 
>>>> Have you considered just having your modem driver auto-magically setting
>>>> the time into the modem and forgetting all this API business?
>>>> 
>>> 
>>> I'll look into this with the customer who proposed it, most likely
>>> reading this conversation already.
>>> 
>> 
>> So as a hint, your customer might want to start engaging with us
>> directly and skip the middle men.  We do have some experience with
>> integration given that between oFono and its sister projects we control
>> the Bluetooth stack, WiFi stack, cellular stack, NFC stack and the
>> overall network management ;)
>> 
>>> it's ok, GPLv2 just requires me to publish the changes I have done to
>>> the code, not to fight to get them upstream.
>>> I have posted this special case, it is public, so I won't discuss it
>>> further here.
>>> If Gemalto users want it, we will give it to them.
>>> 
>> 
>> I can only advise you what is acceptable to the oFono upstream project.
>> What you do in the end is really up to you and your customers.  But we
>> do have certain standards with respect to security and code quality
>> which we are not going to compromise on.
>> 
>>> 
>>> Most of the options would make sense in a generic atom (like: which
>>> satellites constellations to use for the fix (GPS, GLONASS, GALILEO,
>>> Beidou, QZSS, ...), which to output, how to output them, NMEA version
>>> to use, start/stop the receiver, start/stop the output, etc.
>> 
>> So maybe you want to start there and propose some improvements to the
>> location reporting APIs?
>> 
>>> But some others would still require a vendor-specific plug-in, like on
>>> which port to output the NMEA sentences, whether to exclude satellites
>>> below a given angle, etc.
>> 
>> It seems like this is a platform specific configuration and really
>> doesn't belong being exported over D-Bus anyway.
>> 
>>>> So fix gpsd.  We're not taking upstream a vendor specific NMEA API when
>>>> location-reporting already exists.
>>> 
>>> Changing it would make the dbus interface incompatible.
>>> gpsd takes a device, not an fd, but the atom initialize the receiver
>>> and opens the port.
>> 
>> So? You can always come up with a simple pseudo-tty proxy?  That's like
>> 20 minutes of work.  Or maybe 5 in Python.
>> 
>>> This kind of linked mechanisms make policies, and are quite impossible
>>> to break once done.
>>> In my opinion, it is a bad design for linux, because the policies
>>> should be a step higher, but it wasn't detected in time and now it has
>>> to be like that.
>>> You may have noticed that I didn't remove this atom from the plugin,
>>> because there are now users for it.
>>> And by the way: passing the fd most likely means that each client is
>>> implementing its own  NMEA parser, with risks of bugs, duplication of
>>> effort and lack of standardization. The atom encourages bad practices.
>>> 
>> 
>> Ok, thanks for the rant, but I will have to disagree completely here.
>> LocationReporting was done the way it was for very specific reasons.  We
>> can't assume that the GPS port will be on one of the USB TTYs.  It may
>> be this way on your products, but it definitely is not this way on
>> others.  For example, it is exported through phonet or QMI on some devices.
>> 
>> Many modems also need magic commands / setup prior to being able to use
>> the GPS receiver.  So the LocationReporting API takes care of that.
>> This also allows us to keep track of client lifetime.  So that if/when
>> the client dies we can automatically turn off the GPS unit and save some
>> battery.
>> 
>> Honestly I'd really like to see GPS integrated properly into the kernel,
>> with the ability for userspace daemons like oFono to provide
>> drivers/devices to the kernel.  Maybe Pavel has some additional ideas on
>> how we could accomplish that.
>> 
>>>> Yes, but others do support MBIM + QMI.
>>> 
>>> this might be a different use case, like all communication is qmi and
>>> only carried over mbim frames. This would require a different plugin,
>> 
>> Not all, but some, yes.
>> 
>>> but I am not in this case.
>> 
>> <snip>
>> 
>>>> 
>>>> 2. Split the single monolithic gemalto driver into 3+ drivers.  One for
>>>> MBIM+AT, one for QMI+AT, one for Serial/USB AT.  Or alternatively extend
>>>> plugins/gobi or plugins/mbim with Gemalto specific model quirks.
>>> 
>>> the second option is more interesting, but would make the behavior
>>> switches in the plugin duplicated.
>>> What about extracting the qmi and mbim initialization in a separate
>>> file, so not to distract the reader?
>> 
>> As mentioned earlier, we're not doing any Frankenstein drivers.
>> 
>>> All modems are, after all, AT modems, with special initialization for
>>> dedicated interfaces. It initializes mbim just like it does for gnss,
>>> only this code takes more space.
>> 
>> Are you talking about your modems here?  Because certainly not 'all'
>> modems are AT modems.  Our modems for example are binary protocol based
>> with AT commands bolted on top.  Also, many vendors do not provide AT
>> ports for MBIM/QMI, etc based devices.
>> 
> 
> I am talking about the Gemalto modems in this plugin. They are all AT
> modems, with extensions for the gprs-context.
> 
> Reading the ofono documentation, I kind of understood that the very
> structure of ofono is so done that a plug-in can call the atoms that
> it needs.
> Was that all just advertisement?
> 
> And the Frankenstein idea was good, it is called also cherry-picking.
> The drama that it got out of control is just... drama.
> 
> Gemalto prefers to use the AT interface because it is well understood,
> easy to format, and has an excellent test coverage.
> So it will remain the preferred interface when there is a choice
> between it and something else.
> 
> MBIM is not even supported through the glib, but through the ell
> library that is itself quite fresh (and for which there is little
> advantage instead of the glib. Maybe because using glib you have to
> listen to someone else's opinion for pushing changes).

ELL is actually pretty old since most of its original code is from 2011 and most of the parts that oFono uses have been in ELL since a long time now. We now have an option in oFono to use internal ELL or external ELL. So that will make things even easier since you can just default to the internal version if you want to.

We started to dislike GLib since it started to grow out of control from a memory usage point of view. And we really only used a tiny portion of it. When we started ELL, we looked at BlueZ, oFono, ConnMan etc and checked what parts of GLib they actually used and that was the design factor behind ELL.

Keep in mind that we only require GLib >= 2.32 and I think the latest version is 2.58 or something like that. So pushing changes into GLib was never needed.

Regards

Marcel


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] new gemalto plugin
  2018-10-31 20:19             ` Marcel Holtmann
@ 2018-10-31 20:32               ` Giacinto Cifelli
  2018-10-31 21:16                 ` Denis Kenzior
  0 siblings, 1 reply; 16+ messages in thread
From: Giacinto Cifelli @ 2018-10-31 20:32 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 13406 bytes --]

Hi Marcel, Denis,


On Wed, Oct 31, 2018 at 9:19 PM Marcel Holtmann <marcel@holtmann.org> wrote:
>
> Hi Giacinto,
>
> >>>> So you might need to expand on this some more.  What is QMI+AT or
> >>>> MBIM+AT actually doing?  Is there a single AT port? Multiple? What is
> >>>> the AT port being used for, just vendor specific APIs or something more?
> >>>
> >>> MBIM and QMI are actually used only for the gprs-context atom.
> >>> The modems can work with the mbim or gobi plugin if they support the
> >>> respective protocols (I have tested them with changes in udevng), but
> >>> then we only get the bare minimum: no configuration options, hardware
> >>> monitor, low-power mode, gnss and its options, and the rest.
> >>> So, given that we need the AT interface anyway, everything is done
> >>> through it. It is also easier to debug than the big binary blog
> >>> (admittedly, wireshark helps a lot for mbim, but not sure for qmi).
> >>>
> >>
> >> You really need to decide what you want to do here.  If your modem is
> >> MBIM, then MBIM driver needs to be used.  I really don't want to get
> >> into situations where we build some Frankenstein driver with
> >> gprs-context being driven via MBIM and netreg being driven via AT commands.
> >>
> >> I can understand if you have MBIM + a GPS/GNSS port and want to expose
> >> location-reporting on that.  Ok, fine, I get that.  I can also
> >> understand if you want to use the AT command port for some vendor
> >> specific extensions not available via MBIM.  But I would need serious
> >> convincing of anything beyond this.
> >>
> >>> MBIM support works a bit differently depending on the chipset
> >>> manufacturer. The drivers/mbim/gprs-context works out of the box for
> >>> some models, while for others it is necessary to create the PDP
> >>> context with AT+CGDCONT. But this is dealt with a dedicated atom, only
> >>> the atom selection is visible in the plugin.
> >>
> >> So explain that one to me?  You send all context details in
> >> MBIM_CID_CONNECT.  So why would a CGDCONT be needed?
> >>
> >>>
> >>> enough for the detour. Back to the plug-in initialization: the
> >>> different models have zillion of differences. Whenever possible, I
> >>> prefer to ask the modem, otherwise it is if/switch depending on the
> >>> model.
> >>> Example of the first case: all Gemalto modems support the AT^SAIC
> >>> command if there is voice support (I checked down to the MC55 that
> >>> were like rel.98 or earlier).
> >>
> >> You do realize that all this probing can be done in the individual atom
> >> driver, right?  Even as far as the driver being able to call
> >> ofono_foo_remove if support is lacking.
> >>
> >>> All switches are independent from each other, working on a simple
> >>> principle, like for voice: ascertain if the feature is present and
> >>> with which options, then select the right atom and atom options.
> >>> There are quite a few, but maintainable thanks to that. Unfortunately
> >>> not all features can be probed at startup because some require to be
> >>> online, so the checks are split in 3 parts: pre-defined with PID,
> >>> tested during the enable phase (could be shifted to pre-sim to have a
> >>> faster enable), and in post-online.
> >>
> >> We've been through this, no AT commands should be executed in pre_sim,
> >> post_sim, post_online hooks...
> >>
> >>> And this is the part to maintain. If I split the plugins in 3
> >>> independent ones, I need to duplicate and maintain this.
> >>
> >> No, you really don't.  It might take a while for you to be convinced though.
> >>
> >>> mbim/qmi/plainAt will disappear. And all the vendor-specific atoms
> >>> will have to be duplicated too.
> >>
> >> Again, they really don't need to be duplicated...
> >>
> >>>> Since it sounds like this is a very esoteric use case, you might want to
> >>>> schedule this last.  Right now it just distracts from the core discussion.
> >>>
> >>> ok for having it last, but it is not so esoteric.
> >>> Take for example the audio settings (which most likely exist for all
> >>> other vendors): you can select the type of interface
> >>> (analog/digital/usb), the port to use in case there is more than one,
> >>> suboptions like I2C or PCM, master/slave, and quite some other.
> >>> They could be set in a quite large vendor-specific interface (I saw
> >>> some tried it in an atom), but the configuration is fixed for the end
> >>> application, and - as you explained - will raise more problems than it
> >>> solves exposing these settings through dbus.
> >>> So the best is to run a couple of commands to set the interface
> >>> properly for the product and that's it.
> >>
> >> I understand.  But this is also why we have audio-settings atom.  So all
> >> such details can be easily put in there and its relevant driver.  If you
> >> want to have some simple configuration file that is read by your driver
> >> where the user can select between some common configurations that is
> >> fine as well.
> >>
> >> One can also add additional drivers / plugins out-of-tree that would be
> >> dynamically loaded and do whatever they desire.  But reading a file and
> >> blindly executing AT commands in a daemon that is running as root is
> >> just asking for trouble.  I don't really care what you do in your
> >> products, but I'm not taking security holes upstream.
> >>
> >>>>
> >>>> Have you considered just having your modem driver auto-magically setting
> >>>> the time into the modem and forgetting all this API business?
> >>>>
> >>>
> >>> I'll look into this with the customer who proposed it, most likely
> >>> reading this conversation already.
> >>>
> >>
> >> So as a hint, your customer might want to start engaging with us
> >> directly and skip the middle men.  We do have some experience with
> >> integration given that between oFono and its sister projects we control
> >> the Bluetooth stack, WiFi stack, cellular stack, NFC stack and the
> >> overall network management ;)
> >>
> >>> it's ok, GPLv2 just requires me to publish the changes I have done to
> >>> the code, not to fight to get them upstream.
> >>> I have posted this special case, it is public, so I won't discuss it
> >>> further here.
> >>> If Gemalto users want it, we will give it to them.
> >>>
> >>
> >> I can only advise you what is acceptable to the oFono upstream project.
> >> What you do in the end is really up to you and your customers.  But we
> >> do have certain standards with respect to security and code quality
> >> which we are not going to compromise on.
> >>
> >>>
> >>> Most of the options would make sense in a generic atom (like: which
> >>> satellites constellations to use for the fix (GPS, GLONASS, GALILEO,
> >>> Beidou, QZSS, ...), which to output, how to output them, NMEA version
> >>> to use, start/stop the receiver, start/stop the output, etc.
> >>
> >> So maybe you want to start there and propose some improvements to the
> >> location reporting APIs?
> >>
> >>> But some others would still require a vendor-specific plug-in, like on
> >>> which port to output the NMEA sentences, whether to exclude satellites
> >>> below a given angle, etc.
> >>
> >> It seems like this is a platform specific configuration and really
> >> doesn't belong being exported over D-Bus anyway.
> >>
> >>>> So fix gpsd.  We're not taking upstream a vendor specific NMEA API when
> >>>> location-reporting already exists.
> >>>
> >>> Changing it would make the dbus interface incompatible.
> >>> gpsd takes a device, not an fd, but the atom initialize the receiver
> >>> and opens the port.
> >>
> >> So? You can always come up with a simple pseudo-tty proxy?  That's like
> >> 20 minutes of work.  Or maybe 5 in Python.
> >>
> >>> This kind of linked mechanisms make policies, and are quite impossible
> >>> to break once done.
> >>> In my opinion, it is a bad design for linux, because the policies
> >>> should be a step higher, but it wasn't detected in time and now it has
> >>> to be like that.
> >>> You may have noticed that I didn't remove this atom from the plugin,
> >>> because there are now users for it.
> >>> And by the way: passing the fd most likely means that each client is
> >>> implementing its own  NMEA parser, with risks of bugs, duplication of
> >>> effort and lack of standardization. The atom encourages bad practices.
> >>>
> >>
> >> Ok, thanks for the rant, but I will have to disagree completely here.
> >> LocationReporting was done the way it was for very specific reasons.  We
> >> can't assume that the GPS port will be on one of the USB TTYs.  It may
> >> be this way on your products, but it definitely is not this way on
> >> others.  For example, it is exported through phonet or QMI on some devices.
> >>
> >> Many modems also need magic commands / setup prior to being able to use
> >> the GPS receiver.  So the LocationReporting API takes care of that.
> >> This also allows us to keep track of client lifetime.  So that if/when
> >> the client dies we can automatically turn off the GPS unit and save some
> >> battery.
> >>
> >> Honestly I'd really like to see GPS integrated properly into the kernel,
> >> with the ability for userspace daemons like oFono to provide
> >> drivers/devices to the kernel.  Maybe Pavel has some additional ideas on
> >> how we could accomplish that.
> >>
> >>>> Yes, but others do support MBIM + QMI.
> >>>
> >>> this might be a different use case, like all communication is qmi and
> >>> only carried over mbim frames. This would require a different plugin,
> >>
> >> Not all, but some, yes.
> >>
> >>> but I am not in this case.
> >>
> >> <snip>
> >>
> >>>>
> >>>> 2. Split the single monolithic gemalto driver into 3+ drivers.  One for
> >>>> MBIM+AT, one for QMI+AT, one for Serial/USB AT.  Or alternatively extend
> >>>> plugins/gobi or plugins/mbim with Gemalto specific model quirks.
> >>>
> >>> the second option is more interesting, but would make the behavior
> >>> switches in the plugin duplicated.
> >>> What about extracting the qmi and mbim initialization in a separate
> >>> file, so not to distract the reader?
> >>
> >> As mentioned earlier, we're not doing any Frankenstein drivers.
> >>
> >>> All modems are, after all, AT modems, with special initialization for
> >>> dedicated interfaces. It initializes mbim just like it does for gnss,
> >>> only this code takes more space.
> >>
> >> Are you talking about your modems here?  Because certainly not 'all'
> >> modems are AT modems.  Our modems for example are binary protocol based
> >> with AT commands bolted on top.  Also, many vendors do not provide AT
> >> ports for MBIM/QMI, etc based devices.
> >>
> >
> > I am talking about the Gemalto modems in this plugin. They are all AT
> > modems, with extensions for the gprs-context.
> >
> > Reading the ofono documentation, I kind of understood that the very
> > structure of ofono is so done that a plug-in can call the atoms that
> > it needs.
> > Was that all just advertisement?
> >
> > And the Frankenstein idea was good, it is called also cherry-picking.
> > The drama that it got out of control is just... drama.
> >
> > Gemalto prefers to use the AT interface because it is well understood,
> > easy to format, and has an excellent test coverage.
> > So it will remain the preferred interface when there is a choice
> > between it and something else.
> >
> > MBIM is not even supported through the glib, but through the ell
> > library that is itself quite fresh (and for which there is little
> > advantage instead of the glib. Maybe because using glib you have to
> > listen to someone else's opinion for pushing changes).
>
> ELL is actually pretty old since most of its original code is from 2011 and most of the parts that oFono uses have been in ELL since a long time now. We now have an option in oFono to use internal ELL or external ELL. So that will make things even easier since you can just default to the internal version if you want to.
>
> We started to dislike GLib since it started to grow out of control from a memory usage point of view. And we really only used a tiny portion of it. When we started ELL, we looked at BlueZ, oFono, ConnMan etc and checked what parts of GLib they actually used and that was the design factor behind ELL.
>
> Keep in mind that we only require GLib >= 2.32 and I think the latest version is 2.58 or something like that. So pushing changes into GLib was never needed.
>

Thank you for the explanation. Is the glib going to be removed
completely then, or both will coexist?

However I have not seen a real reason not to use AT+MBIM together, nor
a convenient way to do it.

Denis pointed that the atom-configuration tests can be done in the
atoms themselves, except that then I am asked to maintain my own atom
when I want to add more than a couple lines (see the lte atom story).
Nevertheless this is an excellent suggestion to reduce the complexity
in the plugin.

Often MBIM can be turned on or off or changed for some other interface
for the same (Gemalto) modem, depending on the application wishes, so
if the plugin is split, all its settings are just duplicated.


> Regards
>
> Marcel
>

Regards,
Giacinto

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC PATCH] new gemalto plugin
  2018-10-31 20:32               ` Giacinto Cifelli
@ 2018-10-31 21:16                 ` Denis Kenzior
  0 siblings, 0 replies; 16+ messages in thread
From: Denis Kenzior @ 2018-10-31 21:16 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 1472 bytes --]

Hi Giacinto,

> Thank you for the explanation. Is the glib going to be removed
> completely then, or both will coexist?
> 

oFono is a large project, so eradicating use of glib cannot happen 
overnight.  For the foreseeable future we will have both coexist.  I 
will start 'encouraging' people to start using ell instead of glib. 
Especially for new code that does not depend on GAtChat, etc.

> Denis pointed that the atom-configuration tests can be done in the
> atoms themselves, except that then I am asked to maintain my own atom
> when I want to add more than a couple lines (see the lte atom story).
> Nevertheless this is an excellent suggestion to reduce the complexity
> in the plugin.

You will end up writing many of your own atom drivers anyway.  voicecall 
for example.  For others we will figure this out, I've some ideas I need 
to play with.

> 
> Often MBIM can be turned on or off or changed for some other interface
> for the same (Gemalto) modem, depending on the application wishes, so
> if the plugin is split, all its settings are just duplicated.
> 

This also implies having the modem reconfigure itself on the USB 
interface, no?  E.g. a hot-unplug and hot-plug.  I doubt many users will 
be doing this anyway.

If you're worried about duplicating vendor specific API code between 
multiple modem drivers, then don't.  This code can be shared easily and 
I've already given you a few ideas.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2018-10-31 21:16 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-10-26  6:10 [RFC PATCH] new gemalto plugin Giacinto Cifelli
2018-10-29 19:58 ` Denis Kenzior
2018-10-30  6:10   ` Giacinto Cifelli
2018-10-30 15:23     ` Denis Kenzior
2018-10-31  6:55       ` Giacinto Cifelli
2018-10-31 18:03         ` Pavel Machek
2018-10-31 19:45           ` Giacinto Cifelli
2018-10-31 18:42         ` Denis Kenzior
2018-10-31 19:07           ` Marcel Holtmann
2018-10-31 19:23             ` Denis Kenzior
2018-10-31 19:44               ` Giacinto Cifelli
2018-10-31 19:56           ` Giacinto Cifelli
2018-10-31 20:15             ` Denis Kenzior
2018-10-31 20:19             ` Marcel Holtmann
2018-10-31 20:32               ` Giacinto Cifelli
2018-10-31 21:16                 ` Denis Kenzior

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.