All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] drivers: add support for Telit LE910 V2 modem
@ 2017-01-24 14:21 Piotr Haber
  2017-01-24 18:55 ` Denis Kenzior
  0 siblings, 1 reply; 14+ messages in thread
From: Piotr Haber @ 2017-01-24 14:21 UTC (permalink / raw)
  To: ofono

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

LE910 V2 is next generation Telit LTE modem.
It supports 3GPP Rel. 9 LTE Cat. 4 over multiple bands.
Default USB composition uses PID 0x36 and
consists of 6 CDC-ACM serial ports and 1 CDC-NCM network adapter.

For network interface configuration after context setup
see doc/telit-modem.txt
---
Changes in v2:
 - Fix copyright
 - De-attach before configuring context

 Makefile.am                           |   6 +-
 doc/telit-modem.txt                   |  28 ++
 drivers/telitmodem/gprs-context-ncm.c | 497
++++++++++++++++++++++++++++++++++
 drivers/telitmodem/telitmodem.c       |   2 +
 drivers/telitmodem/telitmodem.h       |   2 +
 plugins/le910v2.c                     | 400 +++++++++++++++++++++++++++
 plugins/udevng.c                      |  38 +++
 7 files changed, 972 insertions(+), 1 deletion(-)
 create mode 100644 drivers/telitmodem/gprs-context-ncm.c
 create mode 100644 plugins/le910v2.c

diff --git a/Makefile.am b/Makefile.am
index f76971ec..72c4fcfc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -321,7 +321,8 @@ builtin_modules += telitmodem
 builtin_sources += drivers/atmodem/atutil.h \
 			drivers/telitmodem/telitmodem.h \
 			drivers/telitmodem/telitmodem.c \
-			drivers/telitmodem/location-reporting.c
+			drivers/telitmodem/location-reporting.c \
+			drivers/telitmodem/gprs-context-ncm.c

 builtin_modules += hsomodem
 builtin_sources += drivers/atmodem/atutil.h \
@@ -500,6 +501,9 @@ builtin_sources += plugins/quectel.c
 builtin_modules += ublox
 builtin_sources += plugins/ublox.c

+builtin_modules += le910v2
+builtin_sources += plugins/le910v2.c
+
 if BLUETOOTH
 if BLUEZ4
 builtin_modules += telit
diff --git a/doc/telit-modem.txt b/doc/telit-modem.txt
index 1627fb4c..b1b968b7 100644
--- a/doc/telit-modem.txt
+++ b/doc/telit-modem.txt
@@ -17,3 +17,31 @@ GPS:
   After setting the configuration, a power cycle is required.
   Port Configiuration #8 is available since firmware 12.00.004.
Firmware version
   can be checked using 'AT+CGMR'.
+
+LE910 V2
+========
+
+Default USB composition of LE910V2 uses PID 0x36 (AT#PORTCFG=0)
+and consists of 6 serial ports (CDC-ACM standard, /dev/ttyACMx)
+and 1 network adapter using CDC-NCM standard (wwanx or usbx).
+
+NCM interface configuration follows Telit documentation
+(both documents available on Telit Download Zone - registration required)
+"GE/HE/UE910, UL865, LE910 V2 Linux USB Driver - User Guide r0"
+(document 1VV0301255 Rev.0 - 2016-01-22)
+and "Telit LE910-V2 NCM SETUP r3"
+(document 1VV0301246 Rev.3 - 2016-11-29).
+
+After context is setup, NCM mode activated and PDP context activated
+connection configuration can be read using
+AT+CGPADDR=context_id and AT+CGCONTRDP=context_id commands.
+This is done automatically and results available via
+org.ofono.ConnectionContext.GetProperties DBus method.
+
+Then Linux network interface needs to be configured:
+    ifconfig <Interface> <Address> netmask <Netmask> up
+    route add default gw <Gateway>
+    arp -s <Gateway> 11:22:33:44:55:66
+
+Only after these steps network interface is usable.
+
diff --git a/drivers/telitmodem/gprs-context-ncm.c
b/drivers/telitmodem/gprs-context-ncm.c
new file mode 100644
index 00000000..25f93632
--- /dev/null
+++ b/drivers/telitmodem/gprs-context-ncm.c
@@ -0,0 +1,497 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2017 Piotr Haber. All rights reserved.
+ *
+ *  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
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs-context.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+#include "gatrawip.h"
+
+#include "telitmodem.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *cgpaddr_prefix[] = { "+CGPADDR:", NULL };
+static const char *cgcontrdp_prefix[] = { "+CGCONTRDP:", NULL };
+
+enum state {
+	STATE_IDLE,
+	STATE_ENABLING,
+	STATE_DISABLING,
+	STATE_ACTIVE,
+};
+
+enum auth_method {
+    AUTH_METHOD_NONE,
+    AUTH_METHOD_PAP,
+    AUTH_METHOD_CHAP,
+};
+
+struct gprs_context_data {
+	GAtChat *chat;
+	unsigned int active_context;
+	char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1];
+	char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1];
+	enum auth_method auth_method;
+	enum state state;
+	enum ofono_gprs_proto proto;
+	char address[64];
+	char netmask[64];
+	char gateway[64];
+	char dns1[64];
+	char dns2[64];
+	ofono_gprs_context_cb_t cb;
+	void *cb_data;                                  /* Callback data */
+};
+
+static void failed_setup(struct ofono_gprs_context *gc,
+				GAtResult *result, gboolean deactivate)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	struct ofono_error error;
+	char buf[64];
+
+	DBG("deactivate %d", deactivate);
+
+	if (deactivate == TRUE) {
+		sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
+		g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL);
+	}
+
+	gcd->active_context = 0;
+	gcd->state = STATE_IDLE;
+
+	if (result == NULL) {
+		CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
+		return;
+	}
+
+	decode_at_error(&error, g_at_result_final_response(result));
+	gcd->cb(&error, gcd->cb_data);
+}
+
+static void session_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	struct ofono_modem *modem;
+	const char *interface;
+	const char *dns[3];
+
+	DBG("ok %d", ok);
+
+	if (!ok) {
+		ofono_error("Failed to establish session");
+		failed_setup(gc, result, TRUE);
+		return;
+	}
+
+	gcd->state = STATE_ACTIVE;
+
+	dns[0] = gcd->dns1;
+	dns[1] = gcd->dns2;
+	dns[2] = 0;
+
+	modem = ofono_gprs_context_get_modem(gc);
+	interface = ofono_modem_get_string(modem, "NetworkInterface");
+
+	ofono_gprs_context_set_interface(gc, interface);
+	ofono_gprs_context_set_ipv4_address(gc, gcd->address, TRUE);
+	ofono_gprs_context_set_ipv4_netmask(gc, gcd->netmask);
+	ofono_gprs_context_set_ipv4_gateway(gc, gcd->gateway);
+	ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
+
+	CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+	gcd->cb = NULL;
+	gcd->cb_data = NULL;
+}
+
+static void contrdp_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	char buf[64];
+	int cid, bearer_id;
+	const char *apn, *ip_mask, *gw;
+	const char *dns1, *dns2;
+	GAtResultIter iter;
+	gboolean found = FALSE;
+
+	DBG("ok %d", ok);
+
+	if (!ok) {
+		ofono_error("Unable to get context dynamic paramerers");
+		failed_setup(gc, result, TRUE);
+		return;
+	}
+
+	g_at_result_iter_init(&iter, result);
+
+	while (g_at_result_iter_next(&iter, "+CGCONTRDP:")) {
+		if (!g_at_result_iter_next_number(&iter, &cid))
+			goto error;
+		if (!g_at_result_iter_next_number(&iter, &bearer_id))
+			goto error;
+		if (!g_at_result_iter_next_string(&iter, &apn))
+			goto error;
+		if (!g_at_result_iter_next_string(&iter, &ip_mask))
+			goto error;
+		if (!g_at_result_iter_next_string(&iter, &gw))
+			goto error;
+		if (!g_at_result_iter_next_string(&iter, &dns1))
+			goto error;
+		if (!g_at_result_iter_next_string(&iter, &dns2))
+			goto error;
+
+		if ((unsigned int) cid == gcd->active_context) {
+			found = TRUE;
+			if (gcd->address && strcmp(gcd->address, "") != 0) {
+			    strncpy(gcd->netmask,
+				    &ip_mask[strlen(gcd->address)+1],
+				    sizeof(gcd->netmask));
+			}
+			strncpy(gcd->gateway, gw, sizeof(gcd->gateway));
+			strncpy(gcd->dns1, dns1, sizeof(gcd->dns1));
+			strncpy(gcd->dns2, dns2, sizeof(gcd->dns2));
+		}
+	}
+
+	if (found == FALSE)
+		goto error;
+
+	ofono_info("IP: %s", gcd->address);
+	ofono_info("MASK: %s", gcd->netmask);
+	ofono_info("GW: %s", gcd->gateway);
+	ofono_info("DNS: %s, %s", gcd->dns1, gcd->dns2);
+
+	sprintf(buf, "AT+CGDATA=\"M-RAW_IP\",%d", gcd->active_context);
+	if (g_at_chat_send(gcd->chat, buf, none_prefix,
+					session_cb, gc, NULL) > 0)
+		return;
+
+error:
+	failed_setup(gc, NULL, TRUE);
+}
+
+static void address_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	int cid;
+	const char *address;
+	char buf[64];
+	GAtResultIter iter;
+
+	DBG("ok %d", ok);
+
+	if (!ok) {
+		ofono_error("Unable to get context address");
+		failed_setup(gc, result, TRUE);
+		return;
+	}
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+CGPADDR:"))
+		goto error;
+
+	if (!g_at_result_iter_next_number(&iter, &cid))
+		goto error;
+
+	if ((unsigned int) cid != gcd->active_context)
+		goto error;
+
+	if (!g_at_result_iter_next_string(&iter, &address))
+		goto error;
+
+	strncpy(gcd->address, address, sizeof(gcd->address));
+
+	sprintf(buf, "AT+CGCONTRDP=%d", gcd->active_context);
+	if (g_at_chat_send(gcd->chat, buf, cgcontrdp_prefix,
+					contrdp_cb, gc, NULL) > 0)
+		return;
+
+error:
+	failed_setup(gc, NULL, TRUE);
+}
+
+static void activate_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	char buf[64];
+
+	DBG("ok %d", ok);
+
+	if (!ok) {
+		ofono_error("Unable to activate context");
+		failed_setup(gc, result, FALSE);
+		return;
+	}
+
+	sprintf(buf, "AT+CGPADDR=%u", gcd->active_context);
+	if (g_at_chat_send(gcd->chat, buf, cgpaddr_prefix,
+					address_cb, gc, NULL) > 0)
+		return;
+
+	failed_setup(gc, NULL, TRUE);
+}
+
+static void setup_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	char buf[128];
+
+	DBG("ok %d", ok);
+
+	if (!ok) {
+		ofono_error("Failed to setup context");
+		failed_setup(gc, result, FALSE);
+		return;
+	}
+
+	if (gcd->username[0] && gcd->password[0])
+		sprintf(buf, "AT#PDPAUTH=%u,%u,\"%s\",\"%s\"",
+			gcd->active_context, gcd->auth_method,
+			gcd->username, gcd->password);
+	else
+		sprintf(buf, "AT#PDPAUTH=%u,0", gcd->active_context);
+
+	if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
+		goto error;
+
+	sprintf(buf, "AT#NCM=1,%u", gcd->active_context);
+
+	if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
+		goto error;
+
+	sprintf(buf, "AT+CGACT=1,%u", gcd->active_context);
+
+	if (g_at_chat_send(gcd->chat, buf, none_prefix,
+				activate_cb, gc, NULL) > 0)
+		return;
+
+error:
+	failed_setup(gc, NULL, FALSE);
+}
+
+static void telitncm_gprs_activate_primary(struct ofono_gprs_context *gc,
+				const struct ofono_gprs_primary_context *ctx,
+				ofono_gprs_context_cb_t cb, void *data)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	char buf[OFONO_GPRS_MAX_APN_LENGTH + 128];
+	int len = 0;
+
+	DBG("cid %u", ctx->cid);
+
+	gcd->active_context = ctx->cid;
+	gcd->cb = cb;
+	gcd->cb_data = data;
+	memcpy(gcd->username, ctx->username, sizeof(ctx->username));
+	memcpy(gcd->password, ctx->password, sizeof(ctx->password));
+	gcd->state = STATE_ENABLING;
+	gcd->proto = ctx->proto;
+
+	/* We only support CHAP and PAP */
+	switch (ctx->auth_method) {
+	case OFONO_GPRS_AUTH_METHOD_CHAP:
+		gcd->auth_method = AUTH_METHOD_CHAP;
+		break;
+	case OFONO_GPRS_AUTH_METHOD_PAP:
+		gcd->auth_method = AUTH_METHOD_PAP;
+		break;
+	default:
+		gcd->auth_method = AUTH_METHOD_NONE;
+		break;
+	}
+
+	g_at_chat_send(gcd->chat, "AT+CGATT=0", none_prefix, NULL, NULL, NULL);
+
+	switch (ctx->proto) {
+	case OFONO_GPRS_PROTO_IP:
+		len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"",
+								ctx->cid);
+		break;
+	case OFONO_GPRS_PROTO_IPV6:
+		len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV6\"",
+								ctx->cid);
+		break;
+	case OFONO_GPRS_PROTO_IPV4V6:
+		len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV4V6\"",
+								ctx->cid);
+		break;
+	}
+
+	if (ctx->apn)
+		snprintf(buf + len, sizeof(buf) - len - 3,
+					",\"%s\"", ctx->apn);
+
+	if (g_at_chat_send(gcd->chat, buf, none_prefix,
+				setup_cb, gc, NULL) > 0)
+		return;
+
+	CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void deactivate_cb(gboolean ok, GAtResult *result, gpointer
user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+	DBG("ok %d", ok);
+
+	gcd->active_context = 0;
+	gcd->state = STATE_IDLE;
+
+	CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+	gcd->cb = NULL;
+	gcd->cb_data = NULL;
+}
+
+static void telitncm_gprs_deactivate_primary(struct ofono_gprs_context *gc,
+					unsigned int cid,
+					ofono_gprs_context_cb_t cb, void *data)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	char buf[64];
+
+	DBG("cid %u", cid);
+
+	gcd->state = STATE_DISABLING;
+	gcd->cb = cb;
+	gcd->cb_data = data;
+
+	sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
+	if (g_at_chat_send(gcd->chat, buf, none_prefix,
+				deactivate_cb, gc, NULL) > 0)
+		return;
+
+	CALLBACK_WITH_SUCCESS(cb, data);
+	gcd->cb = NULL;
+	gcd->cb_data = NULL;
+}
+
+static void cgev_notify(GAtResult *result, gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	const char *event;
+	int cid;
+	GAtResultIter iter;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+CGEV:"))
+		return;
+
+	if (!g_at_result_iter_next_unquoted_string(&iter, &event))
+		return;
+
+	if (g_str_has_prefix(event, "NW DEACT") == FALSE)
+		return;
+
+	if (!g_at_result_iter_skip_next(&iter))
+		return;
+
+	if (!g_at_result_iter_next_number(&iter, &cid))
+		return;
+
+	DBG("cid %d", cid);
+
+	if ((unsigned int) cid != gcd->active_context)
+		return;
+
+
+	ofono_gprs_context_deactivated(gc, gcd->active_context);
+
+	gcd->active_context = 0;
+	gcd->state = STATE_IDLE;
+
+}
+
+static int telitncm_gprs_context_probe(struct ofono_gprs_context *gc,
+					unsigned int vendor, void *data)
+{
+	GAtChat *chat = data;
+	struct gprs_context_data *gcd;
+
+	DBG("");
+
+	gcd = g_try_new0(struct gprs_context_data, 1);
+	if (gcd == NULL)
+		return -ENOMEM;
+
+	gcd->chat = g_at_chat_clone(chat);
+
+	ofono_gprs_context_set_data(gc, gcd);
+
+	chat = g_at_chat_get_slave(gcd->chat);
+
+	g_at_chat_register(chat, "+CGEV:", cgev_notify, FALSE, gc, NULL);
+
+	return 0;
+}
+
+static void telitncm_gprs_context_remove(struct ofono_gprs_context *gc)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+	DBG("");
+
+	ofono_gprs_context_set_data(gc, NULL);
+
+	g_at_chat_unref(gcd->chat);
+	g_free(gcd);
+}
+
+static struct ofono_gprs_context_driver driver = {
+	.name			= "telitncmmodem",
+	.probe			= telitncm_gprs_context_probe,
+	.remove			= telitncm_gprs_context_remove,
+	.activate_primary	= telitncm_gprs_activate_primary,
+	.deactivate_primary	= telitncm_gprs_deactivate_primary,
+};
+
+void telitncm_gprs_context_init(void)
+{
+	ofono_gprs_context_driver_register(&driver);
+}
+
+void telitncm_gprs_context_exit(void)
+{
+	ofono_gprs_context_driver_unregister(&driver);
+}
diff --git a/drivers/telitmodem/telitmodem.c
b/drivers/telitmodem/telitmodem.c
index ecb84efb..4aa2c444 100644
--- a/drivers/telitmodem/telitmodem.c
+++ b/drivers/telitmodem/telitmodem.c
@@ -35,6 +35,7 @@
 static int telitmodem_init(void)
 {
 	telit_location_reporting_init();
+	telitncm_gprs_context_init();

 	return 0;
 }
@@ -42,6 +43,7 @@ static int telitmodem_init(void)
 static void telitmodem_exit(void)
 {
 	telit_location_reporting_exit();
+	telitncm_gprs_context_exit();
 }

 OFONO_PLUGIN_DEFINE(telitmodem, "Telit modem driver", VERSION,
diff --git a/drivers/telitmodem/telitmodem.h
b/drivers/telitmodem/telitmodem.h
index 2db41787..8a14595a 100644
--- a/drivers/telitmodem/telitmodem.h
+++ b/drivers/telitmodem/telitmodem.h
@@ -23,3 +23,5 @@

 extern void telit_location_reporting_init();
 extern void telit_location_reporting_exit();
+extern void telitncm_gprs_context_init();
+extern void telitncm_gprs_context_exit();
diff --git a/plugins/le910v2.c b/plugins/le910v2.c
new file mode 100644
index 00000000..e758971d
--- /dev/null
+++ b/plugins/le910v2.c
@@ -0,0 +1,400 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2017 Piotr Haber. All rights reserved.
+ *
+ *  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 <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-barring.h>
+#include <ofono/call-forwarding.h>
+#include <ofono/call-meter.h>
+#include <ofono/call-settings.h>
+#include <ofono/devinfo.h>
+#include <ofono/message-waiting.h>
+#include <ofono/location-reporting.h>
+#include <ofono/netreg.h>
+#include <ofono/phonebook.h>
+#include <ofono/sim.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/voicecall.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+
+static const char *none_prefix[] = { NULL };
+static const char *qss_prefix[] = { "#QSS:", NULL };
+
+struct le910v2_data {
+	GAtChat *chat;		/* AT chat */
+	GAtChat *modem;		/* Data port */
+	struct ofono_sim *sim;
+	ofono_bool_t have_sim;
+	ofono_bool_t sms_phonebook_added;
+};
+
+static void le910v2_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	ofono_info("%s%s", prefix, str);
+}
+
+static GAtChat *open_device(struct ofono_modem *modem,
+				const char *key, char *debug)
+{
+	const char *device;
+	GAtSyntax *syntax;
+	GIOChannel *channel;
+	GAtChat *chat;
+	GHashTable *options;
+
+	device = ofono_modem_get_string(modem, key);
+	if (device == NULL)
+		return NULL;
+
+	DBG("%s %s", key, device);
+
+	options = g_hash_table_new(g_str_hash, g_str_equal);
+	if (options == NULL)
+		return NULL;
+
+	g_hash_table_insert(options, "Baud", "115200");
+	channel = g_at_tty_open(device, options);
+	g_hash_table_destroy(options);
+
+	if (channel == NULL)
+		return NULL;
+
+	syntax = g_at_syntax_new_gsm_permissive();
+	chat = g_at_chat_new(channel, syntax);
+	g_at_syntax_unref(syntax);
+	g_io_channel_unref(channel);
+
+	if (chat == NULL)
+		return NULL;
+
+	if (getenv("OFONO_AT_DEBUG"))
+		g_at_chat_set_debug(chat, le910v2_debug, debug);
+
+	return chat;
+}
+
+static void switch_sim_state_status(struct ofono_modem *modem, int status)
+{
+	struct le910v2_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p, SIM status: %d", modem, status);
+
+	switch (status) {
+	case 0:	/* SIM not inserted */
+		if (data->have_sim == TRUE) {
+			ofono_sim_inserted_notify(data->sim, FALSE);
+			data->have_sim = FALSE;
+			data->sms_phonebook_added = FALSE;
+		}
+		break;
+	case 1:	/* SIM inserted */
+	case 2:	/* SIM inserted and PIN unlocked */
+		if (data->have_sim == FALSE) {
+			ofono_sim_inserted_notify(data->sim, TRUE);
+			data->have_sim = TRUE;
+		}
+		break;
+	case 3:	/* SIM inserted, SMS and phonebook ready */
+		if (data->have_sim == FALSE) {
+			ofono_sim_inserted_notify(data->sim, TRUE);
+			data->have_sim = TRUE;
+		}
+		if (data->sms_phonebook_added == FALSE) {
+			ofono_phonebook_create(modem, 0, "atmodem", data->chat);
+			ofono_sms_create(modem, 0, "atmodem", data->chat);
+			data->sms_phonebook_added = TRUE;
+		}
+		break;
+	default:
+		ofono_warn("Unknown SIM state %d received", status);
+		break;
+	}
+}
+
+static void le910v2_qss_notify(GAtResult *result, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	int status;
+	GAtResultIter iter;
+
+	DBG("%p", modem);
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "#QSS:"))
+		return;
+
+	g_at_result_iter_next_number(&iter, &status);
+
+	switch_sim_state_status(modem, status);
+}
+
+static void qss_query_cb(gboolean ok, GAtResult *result, gpointer
user_data)
+{
+	struct ofono_modem *modem = user_data;
+	int status, mode;
+	GAtResultIter iter;
+
+	DBG("%p", modem);
+
+	if (!ok)
+		return;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "#QSS:"))
+		return;
+
+	if (!g_at_result_iter_next_number(&iter, &mode))
+		return;
+
+	if (!g_at_result_iter_next_number(&iter, &status))
+		return;
+
+	switch_sim_state_status(modem, status);
+}
+
+static void cfun_enable_cb(gboolean ok, GAtResult *result, gpointer
user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct le910v2_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	if (!ok) {
+		g_at_chat_unref(data->chat);
+		data->chat = NULL;
+
+		g_at_chat_unref(data->modem);
+		data->modem = NULL;
+
+		ofono_modem_set_powered(modem, FALSE);
+		return;
+	}
+
+	/*
+	 * Switch data carrier detect signal off.
+	 * When the DCD is disabled the modem does not hangup anymore
+	 * after the data connection.
+	 */
+	g_at_chat_send(data->chat, "AT&C0", NULL, NULL, NULL, NULL);
+
+	data->have_sim = FALSE;
+	data->sms_phonebook_added = FALSE;
+
+	ofono_modem_set_powered(modem, TRUE);
+
+	/*
+	 * Tell the modem not to automatically initiate auto-attach
+	 * proceedures on its own.
+	 */
+	g_at_chat_send(data->chat, "AT#AUTOATT=0", none_prefix,
+				NULL, NULL, NULL);
+
+	/* Follow sim state */
+	g_at_chat_register(data->chat, "#QSS:", le910v2_qss_notify,
+				FALSE, modem, NULL);
+
+	/* Enable sim state notification */
+	g_at_chat_send(data->chat, "AT#QSS=2", none_prefix, NULL, NULL, NULL);
+
+	g_at_chat_send(data->chat, "AT#QSS?", qss_prefix,
+			qss_query_cb, modem, NULL);
+}
+
+static int le910v2_enable(struct ofono_modem *modem)
+{
+	struct le910v2_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	data->modem = open_device(modem, "Modem", "Modem: ");
+	if (data->modem == NULL)
+		return -EINVAL;
+
+	data->chat = open_device(modem, "Aux", "Aux: ");
+	if (data->chat == NULL) {
+		g_at_chat_unref(data->modem);
+		data->modem = NULL;
+		return -EIO;
+	}
+
+	/*
+	 * Disable command echo and
+	 * enable the Extended Error Result Codes
+	 */
+	g_at_chat_send(data->chat, "ATE0 +CMEE=1", none_prefix,
+				NULL, NULL, NULL);
+
+	g_at_chat_send(data->modem, "ATE0", none_prefix,
+				NULL, NULL, NULL);
+
+	/* Set phone functionality */
+	g_at_chat_send(data->chat, "AT+CFUN=1", none_prefix,
+				cfun_enable_cb, modem, NULL);
+
+	return -EINPROGRESS;
+}
+
+static void cfun_disable_cb(gboolean ok, GAtResult *result, gpointer
user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct le910v2_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	g_at_chat_unref(data->chat);
+	data->chat = NULL;
+
+	if (ok)
+		ofono_modem_set_powered(modem, FALSE);
+}
+
+static int le910v2_disable(struct ofono_modem *modem)
+{
+	struct le910v2_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	g_at_chat_cancel_all(data->modem);
+	g_at_chat_unregister_all(data->modem);
+	g_at_chat_unref(data->modem);
+	data->modem = NULL;
+
+	g_at_chat_cancel_all(data->chat);
+	g_at_chat_unregister_all(data->chat);
+
+	/* Power down modem */
+	g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix,
+				cfun_disable_cb, modem, NULL);
+
+	return -EINPROGRESS;
+}
+
+static void le910v2_pre_sim(struct ofono_modem *modem)
+{
+	struct le910v2_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	ofono_devinfo_create(modem, 0, "atmodem", data->chat);
+	data->sim = ofono_sim_create(modem, OFONO_VENDOR_TELIT, "atmodem",
+					data->chat);
+}
+
+static void le910v2_post_online(struct ofono_modem *modem)
+{
+	struct le910v2_data *data = ofono_modem_get_data(modem);
+	struct ofono_gprs *gprs;
+	struct ofono_gprs_context *gc;
+
+	DBG("%p", modem);
+
+	ofono_netreg_create(modem, OFONO_VENDOR_TELIT, "atmodem", data->chat);
+	gprs = ofono_gprs_create(modem, OFONO_VENDOR_TELIT, "atmodem",
+					data->chat);
+	gc = ofono_gprs_context_create(modem, OFONO_VENDOR_TELIT, "telitncmmodem",
+					data->modem);
+
+	if (gprs && gc)
+		ofono_gprs_add_context(gprs, gc);
+}
+
+static int le910v2_probe(struct ofono_modem *modem)
+{
+	struct le910v2_data *data;
+
+	DBG("%p", modem);
+
+	data = g_try_new0(struct le910v2_data, 1);
+	if (data == NULL)
+		return -ENOMEM;
+
+	ofono_modem_set_data(modem, data);
+
+	return 0;
+}
+
+static void le910v2_remove(struct ofono_modem *modem)
+{
+	struct le910v2_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	ofono_modem_set_data(modem, NULL);
+
+	/* Cleanup after hot-unplug */
+	g_at_chat_unref(data->chat);
+	g_at_chat_unref(data->modem);
+
+	g_free(data);
+}
+
+static struct ofono_modem_driver le910v2_driver = {
+	.name		= "le910v2",
+	.probe		= le910v2_probe,
+	.remove		= le910v2_remove,
+	.enable		= le910v2_enable,
+	.disable	= le910v2_disable,
+	.pre_sim	= le910v2_pre_sim,
+	.post_online	= le910v2_post_online,
+};
+
+static int le910v2_init(void)
+{
+	DBG("");
+
+	return ofono_modem_driver_register(&le910v2_driver);
+}
+
+static void le910v2_exit(void)
+{
+	ofono_modem_driver_unregister(&le910v2_driver);
+}
+
+OFONO_PLUGIN_DEFINE(le910v2, "Telit LE910 V2 driver", VERSION,
+		OFONO_PLUGIN_PRIORITY_DEFAULT, le910v2_init, le910v2_exit)
diff --git a/plugins/udevng.c b/plugins/udevng.c
index 50089129..90ddeeab 100644
--- a/plugins/udevng.c
+++ b/plugins/udevng.c
@@ -957,6 +957,41 @@ static gboolean setup_gemalto(struct modem_info* modem)
 	return TRUE;
 }

+static gboolean setup_le910v2(struct modem_info *modem)
+{
+	const char *aux = NULL, *mdm = NULL, *network = NULL;
+	GSList *list;
+
+	DBG("%s", modem->syspath);
+
+	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->sysattr);
+
+		if (g_strcmp0(info->interface, "2/2/1") == 0) {
+			if (g_strcmp0(info->number, "00") == 0)
+				aux = info->devnode;
+			else if (g_strcmp0(info->number, "06") == 0)
+				mdm = info->devnode;
+		} else if (info->sysattr && (g_str_has_suffix(info->sysattr,
+						"CDC NCM") == TRUE)) {
+			network = info->devnode;
+		}
+	}
+	DBG("aux=%s modem=%s network=%s", aux, mdm, network);
+
+	if (aux == NULL || mdm == NULL || network == NULL)
+		return FALSE;
+
+	ofono_modem_set_string(modem->modem, "Aux", aux);
+	ofono_modem_set_string(modem->modem, "Modem", mdm);
+	ofono_modem_set_string(modem->modem, "NetworkInterface", network);
+
+	return TRUE;
+}
+
 static struct {
 	const char *name;
 	gboolean (*setup)(struct modem_info *modem);
@@ -984,6 +1019,7 @@ static struct {
 	{ "quectel",	setup_quectel	},
 	{ "ublox",	setup_ublox	},
 	{ "gemalto",	setup_gemalto	},
+	{ "le910v2",	setup_le910v2,	"device/interface"	},
 	{ }
 };

@@ -1226,6 +1262,8 @@ static struct {
 	{ "gemalto",	"option",	"1e2d",	"0053"	},
 	{ "gemalto",	"cdc_wdm",	"1e2d",	"0053"	},
 	{ "gemalto",	"qmi_wwan",	"1e2d",	"0053"	},
+	{ "le910v2",	"cdc_ncm",	"1bc7", "0036"	},
+	{ "le910v2",	"cdc_acm",	"1bc7", "0036"	},
 	{ }
 };

-- 
2.11.0


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

* Re: [PATCH v2] drivers: add support for Telit LE910 V2 modem
  2017-01-24 14:21 [PATCH v2] drivers: add support for Telit LE910 V2 modem Piotr Haber
@ 2017-01-24 18:55 ` Denis Kenzior
  2017-01-25 10:41   ` [PATCH 0/4] add support for Telit LE910V2 Piotr Haber
  0 siblings, 1 reply; 14+ messages in thread
From: Denis Kenzior @ 2017-01-24 18:55 UTC (permalink / raw)
  To: ofono

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

Hi Piotr,

On 01/24/2017 08:21 AM, Piotr Haber wrote:
> LE910 V2 is next generation Telit LTE modem.
> It supports 3GPP Rel. 9 LTE Cat. 4 over multiple bands.
> Default USB composition uses PID 0x36 and
> consists of 6 CDC-ACM serial ports and 1 CDC-NCM network adapter.
>
> For network interface configuration after context setup
> see doc/telit-modem.txt
> ---
> Changes in v2:
>  - Fix copyright
>  - De-attach before configuring context
>
>  Makefile.am                           |   6 +-
>  doc/telit-modem.txt                   |  28 ++
>  drivers/telitmodem/gprs-context-ncm.c | 497
> ++++++++++++++++++++++++++++++++++
>  drivers/telitmodem/telitmodem.c       |   2 +
>  drivers/telitmodem/telitmodem.h       |   2 +
>  plugins/le910v2.c                     | 400 +++++++++++++++++++++++++++
>  plugins/udevng.c                      |  38 +++

This really needs to be split up into multiple commits:
1. drivers/* changes + related Makefile.am additions
2. le910v2 plugin + related Makefile.am additions
3. one for udevng changes
4. doc changes

See our patch submission guidelines in HACKING

>  7 files changed, 972 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/telitmodem/gprs-context-ncm.c
>  create mode 100644 plugins/le910v2.c
>
> diff --git a/Makefile.am b/Makefile.am
> index f76971ec..72c4fcfc 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -321,7 +321,8 @@ builtin_modules += telitmodem
>  builtin_sources += drivers/atmodem/atutil.h \
>  			drivers/telitmodem/telitmodem.h \
>  			drivers/telitmodem/telitmodem.c \
> -			drivers/telitmodem/location-reporting.c
> +			drivers/telitmodem/location-reporting.c \
> +			drivers/telitmodem/gprs-context-ncm.c
>
>  builtin_modules += hsomodem
>  builtin_sources += drivers/atmodem/atutil.h \
> @@ -500,6 +501,9 @@ builtin_sources += plugins/quectel.c
>  builtin_modules += ublox
>  builtin_sources += plugins/ublox.c
>
> +builtin_modules += le910v2
> +builtin_sources += plugins/le910v2.c
> +
>  if BLUETOOTH
>  if BLUEZ4
>  builtin_modules += telit
> diff --git a/doc/telit-modem.txt b/doc/telit-modem.txt
> index 1627fb4c..b1b968b7 100644
> --- a/doc/telit-modem.txt
> +++ b/doc/telit-modem.txt
> @@ -17,3 +17,31 @@ GPS:
>    After setting the configuration, a power cycle is required.
>    Port Configiuration #8 is available since firmware 12.00.004.
> Firmware version
>    can be checked using 'AT+CGMR'.
> +
> +LE910 V2
> +========
> +
> +Default USB composition of LE910V2 uses PID 0x36 (AT#PORTCFG=0)
> +and consists of 6 serial ports (CDC-ACM standard, /dev/ttyACMx)
> +and 1 network adapter using CDC-NCM standard (wwanx or usbx).
> +
> +NCM interface configuration follows Telit documentation
> +(both documents available on Telit Download Zone - registration required)
> +"GE/HE/UE910, UL865, LE910 V2 Linux USB Driver - User Guide r0"
> +(document 1VV0301255 Rev.0 - 2016-01-22)
> +and "Telit LE910-V2 NCM SETUP r3"
> +(document 1VV0301246 Rev.3 - 2016-11-29).
> +
> +After context is setup, NCM mode activated and PDP context activated
> +connection configuration can be read using
> +AT+CGPADDR=context_id and AT+CGCONTRDP=context_id commands.
> +This is done automatically and results available via
> +org.ofono.ConnectionContext.GetProperties DBus method.
> +
> +Then Linux network interface needs to be configured:
> +    ifconfig <Interface> <Address> netmask <Netmask> up
> +    route add default gw <Gateway>
> +    arp -s <Gateway> 11:22:33:44:55:66
> +
> +Only after these steps network interface is usable.
> +
> diff --git a/drivers/telitmodem/gprs-context-ncm.c
> b/drivers/telitmodem/gprs-context-ncm.c
> new file mode 100644
> index 00000000..25f93632
> --- /dev/null
> +++ b/drivers/telitmodem/gprs-context-ncm.c
> @@ -0,0 +1,497 @@
> +/*
> + *
> + *  oFono - Open Source Telephony
> + *
> + *  Copyright (C) 2017 Piotr Haber. All rights reserved.
> + *
> + *  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
> +
> +#define _GNU_SOURCE
> +#include <string.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <errno.h>
> +#include <sys/stat.h>
> +
> +#include <glib.h>
> +
> +#include <ofono/log.h>
> +#include <ofono/modem.h>
> +#include <ofono/gprs-context.h>
> +
> +#include "gatchat.h"
> +#include "gatresult.h"
> +#include "gatrawip.h"
> +
> +#include "telitmodem.h"
> +
> +static const char *none_prefix[] = { NULL };
> +static const char *cgpaddr_prefix[] = { "+CGPADDR:", NULL };
> +static const char *cgcontrdp_prefix[] = { "+CGCONTRDP:", NULL };
> +
> +enum state {
> +	STATE_IDLE,
> +	STATE_ENABLING,
> +	STATE_DISABLING,
> +	STATE_ACTIVE,
> +};
> +
> +enum auth_method {
> +    AUTH_METHOD_NONE,
> +    AUTH_METHOD_PAP,
> +    AUTH_METHOD_CHAP,
> +};
> +
> +struct gprs_context_data {
> +	GAtChat *chat;
> +	unsigned int active_context;
> +	char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1];
> +	char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1];
> +	enum auth_method auth_method;
> +	enum state state;
> +	enum ofono_gprs_proto proto;
> +	char address[64];
> +	char netmask[64];
> +	char gateway[64];
> +	char dns1[64];
> +	char dns2[64];
> +	ofono_gprs_context_cb_t cb;
> +	void *cb_data;                                  /* Callback data */
> +};
> +
> +static void failed_setup(struct ofono_gprs_context *gc,
> +				GAtResult *result, gboolean deactivate)
> +{
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	struct ofono_error error;
> +	char buf[64];
> +
> +	DBG("deactivate %d", deactivate);
> +
> +	if (deactivate == TRUE) {
> +		sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
> +		g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL);
> +	}
> +
> +	gcd->active_context = 0;
> +	gcd->state = STATE_IDLE;
> +
> +	if (result == NULL) {
> +		CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
> +		return;
> +	}
> +
> +	decode_at_error(&error, g_at_result_final_response(result));
> +	gcd->cb(&error, gcd->cb_data);
> +}
> +
> +static void session_cb(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_gprs_context *gc = user_data;
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	struct ofono_modem *modem;
> +	const char *interface;
> +	const char *dns[3];
> +
> +	DBG("ok %d", ok);
> +
> +	if (!ok) {
> +		ofono_error("Failed to establish session");
> +		failed_setup(gc, result, TRUE);
> +		return;
> +	}
> +
> +	gcd->state = STATE_ACTIVE;
> +
> +	dns[0] = gcd->dns1;
> +	dns[1] = gcd->dns2;
> +	dns[2] = 0;
> +
> +	modem = ofono_gprs_context_get_modem(gc);
> +	interface = ofono_modem_get_string(modem, "NetworkInterface");
> +
> +	ofono_gprs_context_set_interface(gc, interface);
> +	ofono_gprs_context_set_ipv4_address(gc, gcd->address, TRUE);
> +	ofono_gprs_context_set_ipv4_netmask(gc, gcd->netmask);
> +	ofono_gprs_context_set_ipv4_gateway(gc, gcd->gateway);
> +	ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
> +
> +	CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
> +	gcd->cb = NULL;
> +	gcd->cb_data = NULL;
> +}
> +
> +static void contrdp_cb(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_gprs_context *gc = user_data;
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	char buf[64];
> +	int cid, bearer_id;
> +	const char *apn, *ip_mask, *gw;
> +	const char *dns1, *dns2;
> +	GAtResultIter iter;
> +	gboolean found = FALSE;
> +
> +	DBG("ok %d", ok);
> +
> +	if (!ok) {
> +		ofono_error("Unable to get context dynamic paramerers");
> +		failed_setup(gc, result, TRUE);
> +		return;
> +	}
> +
> +	g_at_result_iter_init(&iter, result);
> +
> +	while (g_at_result_iter_next(&iter, "+CGCONTRDP:")) {
> +		if (!g_at_result_iter_next_number(&iter, &cid))
> +			goto error;
> +		if (!g_at_result_iter_next_number(&iter, &bearer_id))
> +			goto error;
> +		if (!g_at_result_iter_next_string(&iter, &apn))
> +			goto error;
> +		if (!g_at_result_iter_next_string(&iter, &ip_mask))
> +			goto error;
> +		if (!g_at_result_iter_next_string(&iter, &gw))
> +			goto error;
> +		if (!g_at_result_iter_next_string(&iter, &dns1))
> +			goto error;
> +		if (!g_at_result_iter_next_string(&iter, &dns2))
> +			goto error;
> +
> +		if ((unsigned int) cid == gcd->active_context) {
> +			found = TRUE;
> +			if (gcd->address && strcmp(gcd->address, "") != 0) {
> +			    strncpy(gcd->netmask,
> +				    &ip_mask[strlen(gcd->address)+1],
> +				    sizeof(gcd->netmask));
> +			}
> +			strncpy(gcd->gateway, gw, sizeof(gcd->gateway));
> +			strncpy(gcd->dns1, dns1, sizeof(gcd->dns1));
> +			strncpy(gcd->dns2, dns2, sizeof(gcd->dns2));
> +		}
> +	}
> +
> +	if (found == FALSE)
> +		goto error;
> +
> +	ofono_info("IP: %s", gcd->address);
> +	ofono_info("MASK: %s", gcd->netmask);
> +	ofono_info("GW: %s", gcd->gateway);
> +	ofono_info("DNS: %s, %s", gcd->dns1, gcd->dns2);
> +
> +	sprintf(buf, "AT+CGDATA=\"M-RAW_IP\",%d", gcd->active_context);
> +	if (g_at_chat_send(gcd->chat, buf, none_prefix,
> +					session_cb, gc, NULL) > 0)
> +		return;
> +
> +error:
> +	failed_setup(gc, NULL, TRUE);
> +}
> +
> +static void address_cb(gboolean ok, GAtResult *result, gpointer user_data)

You have contrdp_cb above and a more generic name here.  I'd suggest 
using executed command name as the callback name.  E.g. cgpaddr_cb, 
cgcontrdp_cb, etc.

> +{
> +	struct ofono_gprs_context *gc = user_data;
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	int cid;
> +	const char *address;
> +	char buf[64];
> +	GAtResultIter iter;
> +
> +	DBG("ok %d", ok);
> +
> +	if (!ok) {
> +		ofono_error("Unable to get context address");
> +		failed_setup(gc, result, TRUE);
> +		return;
> +	}
> +
> +	g_at_result_iter_init(&iter, result);
> +
> +	if (!g_at_result_iter_next(&iter, "+CGPADDR:"))
> +		goto error;
> +
> +	if (!g_at_result_iter_next_number(&iter, &cid))
> +		goto error;
> +
> +	if ((unsigned int) cid != gcd->active_context)
> +		goto error;
> +
> +	if (!g_at_result_iter_next_string(&iter, &address))
> +		goto error;
> +
> +	strncpy(gcd->address, address, sizeof(gcd->address));
> +
> +	sprintf(buf, "AT+CGCONTRDP=%d", gcd->active_context);
> +	if (g_at_chat_send(gcd->chat, buf, cgcontrdp_prefix,
> +					contrdp_cb, gc, NULL) > 0)
> +		return;
> +
> +error:
> +	failed_setup(gc, NULL, TRUE);
> +}
> +
> +static void activate_cb(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_gprs_context *gc = user_data;
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	char buf[64];
> +
> +	DBG("ok %d", ok);
> +
> +	if (!ok) {
> +		ofono_error("Unable to activate context");
> +		failed_setup(gc, result, FALSE);
> +		return;
> +	}
> +
> +	sprintf(buf, "AT+CGPADDR=%u", gcd->active_context);
> +	if (g_at_chat_send(gcd->chat, buf, cgpaddr_prefix,
> +					address_cb, gc, NULL) > 0)
> +		return;
> +
> +	failed_setup(gc, NULL, TRUE);
> +}
> +
> +static void setup_cb(gboolean ok, GAtResult *result, gpointer user_data)

I'd like a more descriptive callback name.  cgdcont_cb or setup_apn_cb

> +{
> +	struct ofono_gprs_context *gc = user_data;
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	char buf[128];
> +
> +	DBG("ok %d", ok);
> +
> +	if (!ok) {
> +		ofono_error("Failed to setup context");
> +		failed_setup(gc, result, FALSE);
> +		return;
> +	}
> +
> +	if (gcd->username[0] && gcd->password[0])
> +		sprintf(buf, "AT#PDPAUTH=%u,%u,\"%s\",\"%s\"",
> +			gcd->active_context, gcd->auth_method,
> +			gcd->username, gcd->password);
> +	else
> +		sprintf(buf, "AT#PDPAUTH=%u,0", gcd->active_context);
> +
> +	if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
> +		goto error;
> +
> +	sprintf(buf, "AT#NCM=1,%u", gcd->active_context);
> +
> +	if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
> +		goto error;
> +
> +	sprintf(buf, "AT+CGACT=1,%u", gcd->active_context);
> +
> +	if (g_at_chat_send(gcd->chat, buf, none_prefix,
> +				activate_cb, gc, NULL) > 0)
> +		return;
> +
> +error:
> +	failed_setup(gc, NULL, FALSE);
> +}
> +
> +static void telitncm_gprs_activate_primary(struct ofono_gprs_context *gc,
> +				const struct ofono_gprs_primary_context *ctx,
> +				ofono_gprs_context_cb_t cb, void *data)
> +{
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	char buf[OFONO_GPRS_MAX_APN_LENGTH + 128];
> +	int len = 0;
> +
> +	DBG("cid %u", ctx->cid);
> +
> +	gcd->active_context = ctx->cid;
> +	gcd->cb = cb;
> +	gcd->cb_data = data;
> +	memcpy(gcd->username, ctx->username, sizeof(ctx->username));
> +	memcpy(gcd->password, ctx->password, sizeof(ctx->password));
> +	gcd->state = STATE_ENABLING;
> +	gcd->proto = ctx->proto;
> +
> +	/* We only support CHAP and PAP */
> +	switch (ctx->auth_method) {
> +	case OFONO_GPRS_AUTH_METHOD_CHAP:
> +		gcd->auth_method = AUTH_METHOD_CHAP;
> +		break;
> +	case OFONO_GPRS_AUTH_METHOD_PAP:
> +		gcd->auth_method = AUTH_METHOD_PAP;
> +		break;
> +	default:
> +		gcd->auth_method = AUTH_METHOD_NONE;
> +		break;

This can be either PAP or CHAP.  Shouldn't this be an error?

> +	}
> +
> +	g_at_chat_send(gcd->chat, "AT+CGATT=0", none_prefix, NULL, NULL, NULL);
> +
> +	switch (ctx->proto) {
> +	case OFONO_GPRS_PROTO_IP:
> +		len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"",
> +								ctx->cid);
> +		break;
> +	case OFONO_GPRS_PROTO_IPV6:
> +		len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV6\"",
> +								ctx->cid);
> +		break;
> +	case OFONO_GPRS_PROTO_IPV4V6:
> +		len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV4V6\"",
> +								ctx->cid);
> +		break;
> +	}
> +
> +	if (ctx->apn)
> +		snprintf(buf + len, sizeof(buf) - len - 3,
> +					",\"%s\"", ctx->apn);
> +
> +	if (g_at_chat_send(gcd->chat, buf, none_prefix,
> +				setup_cb, gc, NULL) > 0)
> +		return;
> +
> +	CALLBACK_WITH_FAILURE(cb, data);
> +}
> +
> +static void deactivate_cb(gboolean ok, GAtResult *result, gpointer
> user_data)
> +{
> +	struct ofono_gprs_context *gc = user_data;
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +
> +	DBG("ok %d", ok);
> +
> +	gcd->active_context = 0;
> +	gcd->state = STATE_IDLE;
> +
> +	CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
> +	gcd->cb = NULL;
> +	gcd->cb_data = NULL;
> +}
> +
> +static void telitncm_gprs_deactivate_primary(struct ofono_gprs_context *gc,
> +					unsigned int cid,
> +					ofono_gprs_context_cb_t cb, void *data)
> +{
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	char buf[64];
> +
> +	DBG("cid %u", cid);
> +
> +	gcd->state = STATE_DISABLING;
> +	gcd->cb = cb;
> +	gcd->cb_data = data;
> +
> +	sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
> +	if (g_at_chat_send(gcd->chat, buf, none_prefix,
> +				deactivate_cb, gc, NULL) > 0)
> +		return;
> +
> +	CALLBACK_WITH_SUCCESS(cb, data);
> +	gcd->cb = NULL;
> +	gcd->cb_data = NULL;
> +}
> +
> +static void cgev_notify(GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_gprs_context *gc = user_data;
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	const char *event;
> +	int cid;
> +	GAtResultIter iter;
> +
> +	g_at_result_iter_init(&iter, result);
> +
> +	if (!g_at_result_iter_next(&iter, "+CGEV:"))
> +		return;
> +
> +	if (!g_at_result_iter_next_unquoted_string(&iter, &event))
> +		return;
> +
> +	if (g_str_has_prefix(event, "NW DEACT") == FALSE)
> +		return;
> +
> +	if (!g_at_result_iter_skip_next(&iter))
> +		return;
> +
> +	if (!g_at_result_iter_next_number(&iter, &cid))
> +		return;
> +
> +	DBG("cid %d", cid);
> +
> +	if ((unsigned int) cid != gcd->active_context)
> +		return;
> +
> +

No double empty lines

> +	ofono_gprs_context_deactivated(gc, gcd->active_context);
> +
> +	gcd->active_context = 0;
> +	gcd->state = STATE_IDLE;
> +

No empty lines at end of functions please

> +}
> +
> +static int telitncm_gprs_context_probe(struct ofono_gprs_context *gc,
> +					unsigned int vendor, void *data)
> +{
> +	GAtChat *chat = data;
> +	struct gprs_context_data *gcd;
> +
> +	DBG("");
> +
> +	gcd = g_try_new0(struct gprs_context_data, 1);
> +	if (gcd == NULL)
> +		return -ENOMEM;
> +
> +	gcd->chat = g_at_chat_clone(chat);
> +
> +	ofono_gprs_context_set_data(gc, gcd);
> +
> +	chat = g_at_chat_get_slave(gcd->chat);

This slave business is only needed for PPP.  Strictly speaking you just 
need a single GAtChat / AT command port to drive this in NCM mode.

> +
> +	g_at_chat_register(chat, "+CGEV:", cgev_notify, FALSE, gc, NULL);
> +
> +	return 0;
> +}
> +
> +static void telitncm_gprs_context_remove(struct ofono_gprs_context *gc)
> +{
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +
> +	DBG("");
> +
> +	ofono_gprs_context_set_data(gc, NULL);
> +
> +	g_at_chat_unref(gcd->chat);
> +	g_free(gcd);
> +}
> +
> +static struct ofono_gprs_context_driver driver = {
> +	.name			= "telitncmmodem",
> +	.probe			= telitncm_gprs_context_probe,
> +	.remove			= telitncm_gprs_context_remove,
> +	.activate_primary	= telitncm_gprs_activate_primary,
> +	.deactivate_primary	= telitncm_gprs_deactivate_primary,
> +};
> +
> +void telitncm_gprs_context_init(void)
> +{
> +	ofono_gprs_context_driver_register(&driver);
> +}
> +
> +void telitncm_gprs_context_exit(void)
> +{
> +	ofono_gprs_context_driver_unregister(&driver);
> +}
> diff --git a/drivers/telitmodem/telitmodem.c
> b/drivers/telitmodem/telitmodem.c
> index ecb84efb..4aa2c444 100644
> --- a/drivers/telitmodem/telitmodem.c
> +++ b/drivers/telitmodem/telitmodem.c
> @@ -35,6 +35,7 @@
>  static int telitmodem_init(void)
>  {
>  	telit_location_reporting_init();
> +	telitncm_gprs_context_init();
>
>  	return 0;
>  }
> @@ -42,6 +43,7 @@ static int telitmodem_init(void)
>  static void telitmodem_exit(void)
>  {
>  	telit_location_reporting_exit();
> +	telitncm_gprs_context_exit();
>  }
>
>  OFONO_PLUGIN_DEFINE(telitmodem, "Telit modem driver", VERSION,
> diff --git a/drivers/telitmodem/telitmodem.h
> b/drivers/telitmodem/telitmodem.h
> index 2db41787..8a14595a 100644
> --- a/drivers/telitmodem/telitmodem.h
> +++ b/drivers/telitmodem/telitmodem.h
> @@ -23,3 +23,5 @@
>
>  extern void telit_location_reporting_init();
>  extern void telit_location_reporting_exit();
> +extern void telitncm_gprs_context_init();
> +extern void telitncm_gprs_context_exit();
> diff --git a/plugins/le910v2.c b/plugins/le910v2.c
> new file mode 100644
> index 00000000..e758971d
> --- /dev/null
> +++ b/plugins/le910v2.c
> @@ -0,0 +1,400 @@
> +/*
> + *
> + *  oFono - Open Source Telephony
> + *
> + *  Copyright (C) 2017 Piotr Haber. All rights reserved.
> + *
> + *  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 <stdlib.h>
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <termios.h>
> +#include <string.h>
> +#include <sys/socket.h>
> +
> +#include <glib.h>
> +#include <gatchat.h>
> +#include <gattty.h>
> +
> +#define OFONO_API_SUBJECT_TO_CHANGE
> +#include <ofono/plugin.h>
> +#include <ofono/log.h>
> +#include <ofono/modem.h>
> +#include <ofono/call-barring.h>
> +#include <ofono/call-forwarding.h>
> +#include <ofono/call-meter.h>
> +#include <ofono/call-settings.h>
> +#include <ofono/devinfo.h>
> +#include <ofono/message-waiting.h>
> +#include <ofono/location-reporting.h>
> +#include <ofono/netreg.h>
> +#include <ofono/phonebook.h>
> +#include <ofono/sim.h>
> +#include <ofono/gprs.h>
> +#include <ofono/gprs-context.h>
> +#include <ofono/sms.h>
> +#include <ofono/ussd.h>
> +#include <ofono/voicecall.h>
> +
> +#include <drivers/atmodem/atutil.h>
> +#include <drivers/atmodem/vendor.h>
> +
> +static const char *none_prefix[] = { NULL };
> +static const char *qss_prefix[] = { "#QSS:", NULL };
> +
> +struct le910v2_data {
> +	GAtChat *chat;		/* AT chat */
> +	GAtChat *modem;		/* Data port */
> +	struct ofono_sim *sim;
> +	ofono_bool_t have_sim;
> +	ofono_bool_t sms_phonebook_added;
> +};
> +

This looks like a fairly close copy of the he910 plugin.  Can these two 
be combined?

> +static void le910v2_debug(const char *str, void *user_data)
> +{
> +	const char *prefix = user_data;
> +
> +	ofono_info("%s%s", prefix, str);
> +}
> +
> +static GAtChat *open_device(struct ofono_modem *modem,
> +				const char *key, char *debug)
> +{
> +	const char *device;
> +	GAtSyntax *syntax;
> +	GIOChannel *channel;
> +	GAtChat *chat;
> +	GHashTable *options;
> +
> +	device = ofono_modem_get_string(modem, key);
> +	if (device == NULL)
> +		return NULL;
> +
> +	DBG("%s %s", key, device);
> +
> +	options = g_hash_table_new(g_str_hash, g_str_equal);
> +	if (options == NULL)
> +		return NULL;
> +
> +	g_hash_table_insert(options, "Baud", "115200");
> +	channel = g_at_tty_open(device, options);
> +	g_hash_table_destroy(options);
> +
> +	if (channel == NULL)
> +		return NULL;
> +
> +	syntax = g_at_syntax_new_gsm_permissive();
> +	chat = g_at_chat_new(channel, syntax);
> +	g_at_syntax_unref(syntax);
> +	g_io_channel_unref(channel);
> +
> +	if (chat == NULL)
> +		return NULL;
> +
> +	if (getenv("OFONO_AT_DEBUG"))
> +		g_at_chat_set_debug(chat, le910v2_debug, debug);
> +
> +	return chat;
> +}
> +
> +static void switch_sim_state_status(struct ofono_modem *modem, int status)
> +{
> +	struct le910v2_data *data = ofono_modem_get_data(modem);
> +
> +	DBG("%p, SIM status: %d", modem, status);
> +
> +	switch (status) {
> +	case 0:	/* SIM not inserted */
> +		if (data->have_sim == TRUE) {
> +			ofono_sim_inserted_notify(data->sim, FALSE);
> +			data->have_sim = FALSE;
> +			data->sms_phonebook_added = FALSE;
> +		}
> +		break;
> +	case 1:	/* SIM inserted */
> +	case 2:	/* SIM inserted and PIN unlocked */
> +		if (data->have_sim == FALSE) {
> +			ofono_sim_inserted_notify(data->sim, TRUE);
> +			data->have_sim = TRUE;
> +		}
> +		break;
> +	case 3:	/* SIM inserted, SMS and phonebook ready */
> +		if (data->have_sim == FALSE) {
> +			ofono_sim_inserted_notify(data->sim, TRUE);
> +			data->have_sim = TRUE;
> +		}
> +		if (data->sms_phonebook_added == FALSE) {
> +			ofono_phonebook_create(modem, 0, "atmodem", data->chat);
> +			ofono_sms_create(modem, 0, "atmodem", data->chat);
> +			data->sms_phonebook_added = TRUE;
> +		}
> +		break;
> +	default:
> +		ofono_warn("Unknown SIM state %d received", status);
> +		break;
> +	}
> +}
> +
> +static void le910v2_qss_notify(GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	int status;
> +	GAtResultIter iter;
> +
> +	DBG("%p", modem);
> +
> +	g_at_result_iter_init(&iter, result);
> +
> +	if (!g_at_result_iter_next(&iter, "#QSS:"))
> +		return;
> +
> +	g_at_result_iter_next_number(&iter, &status);
> +
> +	switch_sim_state_status(modem, status);
> +}
> +
> +static void qss_query_cb(gboolean ok, GAtResult *result, gpointer
> user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	int status, mode;
> +	GAtResultIter iter;
> +
> +	DBG("%p", modem);
> +
> +	if (!ok)
> +		return;
> +
> +	g_at_result_iter_init(&iter, result);
> +
> +	if (!g_at_result_iter_next(&iter, "#QSS:"))
> +		return;
> +
> +	if (!g_at_result_iter_next_number(&iter, &mode))
> +		return;
> +
> +	if (!g_at_result_iter_next_number(&iter, &status))
> +		return;
> +
> +	switch_sim_state_status(modem, status);
> +}
> +
> +static void cfun_enable_cb(gboolean ok, GAtResult *result, gpointer
> user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct le910v2_data *data = ofono_modem_get_data(modem);
> +
> +	DBG("%p", modem);
> +
> +	if (!ok) {
> +		g_at_chat_unref(data->chat);
> +		data->chat = NULL;
> +
> +		g_at_chat_unref(data->modem);
> +		data->modem = NULL;
> +
> +		ofono_modem_set_powered(modem, FALSE);
> +		return;
> +	}
> +
> +	/*
> +	 * Switch data carrier detect signal off.
> +	 * When the DCD is disabled the modem does not hangup anymore
> +	 * after the data connection.
> +	 */
> +	g_at_chat_send(data->chat, "AT&C0", NULL, NULL, NULL, NULL);
> +
> +	data->have_sim = FALSE;
> +	data->sms_phonebook_added = FALSE;
> +
> +	ofono_modem_set_powered(modem, TRUE);
> +
> +	/*
> +	 * Tell the modem not to automatically initiate auto-attach
> +	 * proceedures on its own.
> +	 */
> +	g_at_chat_send(data->chat, "AT#AUTOATT=0", none_prefix,
> +				NULL, NULL, NULL);
> +
> +	/* Follow sim state */
> +	g_at_chat_register(data->chat, "#QSS:", le910v2_qss_notify,
> +				FALSE, modem, NULL);
> +
> +	/* Enable sim state notification */
> +	g_at_chat_send(data->chat, "AT#QSS=2", none_prefix, NULL, NULL, NULL);
> +
> +	g_at_chat_send(data->chat, "AT#QSS?", qss_prefix,
> +			qss_query_cb, modem, NULL);
> +}
> +
> +static int le910v2_enable(struct ofono_modem *modem)
> +{
> +	struct le910v2_data *data = ofono_modem_get_data(modem);
> +
> +	DBG("%p", modem);
> +
> +	data->modem = open_device(modem, "Modem", "Modem: ");
> +	if (data->modem == NULL)
> +		return -EINVAL;
> +
> +	data->chat = open_device(modem, "Aux", "Aux: ");
> +	if (data->chat == NULL) {
> +		g_at_chat_unref(data->modem);
> +		data->modem = NULL;
> +		return -EIO;
> +	}
> +
> +	/*
> +	 * Disable command echo and
> +	 * enable the Extended Error Result Codes
> +	 */
> +	g_at_chat_send(data->chat, "ATE0 +CMEE=1", none_prefix,
> +				NULL, NULL, NULL);
> +
> +	g_at_chat_send(data->modem, "ATE0", none_prefix,
> +				NULL, NULL, NULL);
> +
> +	/* Set phone functionality */
> +	g_at_chat_send(data->chat, "AT+CFUN=1", none_prefix,
> +				cfun_enable_cb, modem, NULL);
> +
> +	return -EINPROGRESS;
> +}
> +
> +static void cfun_disable_cb(gboolean ok, GAtResult *result, gpointer
> user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct le910v2_data *data = ofono_modem_get_data(modem);
> +
> +	DBG("%p", modem);
> +
> +	g_at_chat_unref(data->chat);
> +	data->chat = NULL;
> +
> +	if (ok)
> +		ofono_modem_set_powered(modem, FALSE);
> +}
> +
> +static int le910v2_disable(struct ofono_modem *modem)
> +{
> +	struct le910v2_data *data = ofono_modem_get_data(modem);
> +
> +	DBG("%p", modem);
> +
> +	g_at_chat_cancel_all(data->modem);
> +	g_at_chat_unregister_all(data->modem);
> +	g_at_chat_unref(data->modem);
> +	data->modem = NULL;
> +
> +	g_at_chat_cancel_all(data->chat);
> +	g_at_chat_unregister_all(data->chat);
> +
> +	/* Power down modem */
> +	g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix,
> +				cfun_disable_cb, modem, NULL);
> +
> +	return -EINPROGRESS;
> +}
> +
> +static void le910v2_pre_sim(struct ofono_modem *modem)
> +{
> +	struct le910v2_data *data = ofono_modem_get_data(modem);
> +
> +	DBG("%p", modem);
> +
> +	ofono_devinfo_create(modem, 0, "atmodem", data->chat);
> +	data->sim = ofono_sim_create(modem, OFONO_VENDOR_TELIT, "atmodem",
> +					data->chat);
> +}
> +
> +static void le910v2_post_online(struct ofono_modem *modem)
> +{
> +	struct le910v2_data *data = ofono_modem_get_data(modem);
> +	struct ofono_gprs *gprs;
> +	struct ofono_gprs_context *gc;
> +
> +	DBG("%p", modem);
> +
> +	ofono_netreg_create(modem, OFONO_VENDOR_TELIT, "atmodem", data->chat);
> +	gprs = ofono_gprs_create(modem, OFONO_VENDOR_TELIT, "atmodem",
> +					data->chat);
> +	gc = ofono_gprs_context_create(modem, OFONO_VENDOR_TELIT, "telitncmmodem",
> +					data->modem);
> +

Only the GPRS context driver seems to be different from the HE910, 
right?  Can we handle this with an attribute of some sort.

E.g. if (ofono_modem_get_string(modem, "NetworkInterface") != NULL)

> +	if (gprs && gc)
> +		ofono_gprs_add_context(gprs, gc);
> +}
> +
> +static int le910v2_probe(struct ofono_modem *modem)
> +{
> +	struct le910v2_data *data;
> +
> +	DBG("%p", modem);
> +
> +	data = g_try_new0(struct le910v2_data, 1);
> +	if (data == NULL)
> +		return -ENOMEM;
> +
> +	ofono_modem_set_data(modem, data);
> +
> +	return 0;
> +}
> +
> +static void le910v2_remove(struct ofono_modem *modem)
> +{
> +	struct le910v2_data *data = ofono_modem_get_data(modem);
> +
> +	DBG("%p", modem);
> +
> +	ofono_modem_set_data(modem, NULL);
> +
> +	/* Cleanup after hot-unplug */
> +	g_at_chat_unref(data->chat);
> +	g_at_chat_unref(data->modem);
> +
> +	g_free(data);
> +}
> +
> +static struct ofono_modem_driver le910v2_driver = {
> +	.name		= "le910v2",
> +	.probe		= le910v2_probe,
> +	.remove		= le910v2_remove,
> +	.enable		= le910v2_enable,
> +	.disable	= le910v2_disable,
> +	.pre_sim	= le910v2_pre_sim,
> +	.post_online	= le910v2_post_online,
> +};
> +
> +static int le910v2_init(void)
> +{
> +	DBG("");
> +
> +	return ofono_modem_driver_register(&le910v2_driver);
> +}
> +
> +static void le910v2_exit(void)
> +{
> +	ofono_modem_driver_unregister(&le910v2_driver);
> +}
> +
> +OFONO_PLUGIN_DEFINE(le910v2, "Telit LE910 V2 driver", VERSION,
> +		OFONO_PLUGIN_PRIORITY_DEFAULT, le910v2_init, le910v2_exit)
> diff --git a/plugins/udevng.c b/plugins/udevng.c
> index 50089129..90ddeeab 100644
> --- a/plugins/udevng.c
> +++ b/plugins/udevng.c
> @@ -957,6 +957,41 @@ static gboolean setup_gemalto(struct modem_info* modem)
>  	return TRUE;
>  }
>
> +static gboolean setup_le910v2(struct modem_info *modem)
> +{
> +	const char *aux = NULL, *mdm = NULL, *network = NULL;
> +	GSList *list;
> +
> +	DBG("%s", modem->syspath);
> +
> +	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->sysattr);
> +
> +		if (g_strcmp0(info->interface, "2/2/1") == 0) {
> +			if (g_strcmp0(info->number, "00") == 0)
> +				aux = info->devnode;
> +			else if (g_strcmp0(info->number, "06") == 0)
> +				mdm = info->devnode;
> +		} else if (info->sysattr && (g_str_has_suffix(info->sysattr,
> +						"CDC NCM") == TRUE)) {
> +			network = info->devnode;
> +		}
> +	}
> +	DBG("aux=%s modem=%s network=%s", aux, mdm, network);
> +
> +	if (aux == NULL || mdm == NULL || network == NULL)
> +		return FALSE;
> +
> +	ofono_modem_set_string(modem->modem, "Aux", aux);
> +	ofono_modem_set_string(modem->modem, "Modem", mdm);
> +	ofono_modem_set_string(modem->modem, "NetworkInterface", network);
> +
> +	return TRUE;
> +}
> +
>  static struct {
>  	const char *name;
>  	gboolean (*setup)(struct modem_info *modem);
> @@ -984,6 +1019,7 @@ static struct {
>  	{ "quectel",	setup_quectel	},
>  	{ "ublox",	setup_ublox	},
>  	{ "gemalto",	setup_gemalto	},
> +	{ "le910v2",	setup_le910v2,	"device/interface"	},
>  	{ }
>  };
>
> @@ -1226,6 +1262,8 @@ static struct {
>  	{ "gemalto",	"option",	"1e2d",	"0053"	},
>  	{ "gemalto",	"cdc_wdm",	"1e2d",	"0053"	},
>  	{ "gemalto",	"qmi_wwan",	"1e2d",	"0053"	},
> +	{ "le910v2",	"cdc_ncm",	"1bc7", "0036"	},
> +	{ "le910v2",	"cdc_acm",	"1bc7", "0036"	},
>  	{ }
>  };
>

Regards,
-Denis

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

* [PATCH 0/4] add support for Telit LE910V2
  2017-01-24 18:55 ` Denis Kenzior
@ 2017-01-25 10:41   ` Piotr Haber
  2017-01-25 10:41     ` [PATCH 1/4] telitmodem: support for CDC-NCM network adapter Piotr Haber
                       ` (3 more replies)
  0 siblings, 4 replies; 14+ messages in thread
From: Piotr Haber @ 2017-01-25 10:41 UTC (permalink / raw)
  To: ofono

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

Telit L910 V2 is a LTE Cat. 4 device.

Piotr Haber (4):
  telitmodem: support for CDC-NCM network adapter
  plugins: support for Telit LE910 V2 modem
  doc: description of Telit LE910V2 network setup
  udevng: setup of Telit LE910V2

 Makefile.am                           |   6 +-
 doc/telit-modem.txt                   |  27 ++
 drivers/telitmodem/gprs-context-ncm.c | 497 ++++++++++++++++++++++++++++++++++
 drivers/telitmodem/telitmodem.c       |   2 +
 drivers/telitmodem/telitmodem.h       |   2 +
 plugins/le910v2.c                     | 400 +++++++++++++++++++++++++++
 plugins/udevng.c                      |  38 +++
 7 files changed, 971 insertions(+), 1 deletion(-)
 create mode 100644 drivers/telitmodem/gprs-context-ncm.c
 create mode 100644 plugins/le910v2.c

-- 
2.11.0


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

* [PATCH 1/4] telitmodem: support for CDC-NCM network adapter
  2017-01-25 10:41   ` [PATCH 0/4] add support for Telit LE910V2 Piotr Haber
@ 2017-01-25 10:41     ` Piotr Haber
  2017-01-25 16:33       ` Denis Kenzior
  2017-01-25 10:41     ` [PATCH 2/4] plugins: support for Telit LE910 V2 modem Piotr Haber
                       ` (2 subsequent siblings)
  3 siblings, 1 reply; 14+ messages in thread
From: Piotr Haber @ 2017-01-25 10:41 UTC (permalink / raw)
  To: ofono

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

Network Control Model is a new Communication Device Class
protocol for exchanging Ethernet frames over USB.
NCM is intended to be used with high-speed network
attachments such as HSDPA and LTE data services.
---
 Makefile.am                           |   3 +-
 drivers/telitmodem/gprs-context-ncm.c | 497 ++++++++++++++++++++++++++++++++++
 drivers/telitmodem/telitmodem.c       |   2 +
 drivers/telitmodem/telitmodem.h       |   2 +
 4 files changed, 503 insertions(+), 1 deletion(-)
 create mode 100644 drivers/telitmodem/gprs-context-ncm.c

diff --git a/Makefile.am b/Makefile.am
index f76971ec..2f49027c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -321,7 +321,8 @@ builtin_modules += telitmodem
 builtin_sources += drivers/atmodem/atutil.h \
 			drivers/telitmodem/telitmodem.h \
 			drivers/telitmodem/telitmodem.c \
-			drivers/telitmodem/location-reporting.c
+			drivers/telitmodem/location-reporting.c \
+			drivers/telitmodem/gprs-context-ncm.c
 
 builtin_modules += hsomodem
 builtin_sources += drivers/atmodem/atutil.h \
diff --git a/drivers/telitmodem/gprs-context-ncm.c b/drivers/telitmodem/gprs-context-ncm.c
new file mode 100644
index 00000000..25f93632
--- /dev/null
+++ b/drivers/telitmodem/gprs-context-ncm.c
@@ -0,0 +1,497 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2017 Piotr Haber. All rights reserved.
+ *
+ *  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
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs-context.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+#include "gatrawip.h"
+
+#include "telitmodem.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *cgpaddr_prefix[] = { "+CGPADDR:", NULL };
+static const char *cgcontrdp_prefix[] = { "+CGCONTRDP:", NULL };
+
+enum state {
+	STATE_IDLE,
+	STATE_ENABLING,
+	STATE_DISABLING,
+	STATE_ACTIVE,
+};
+
+enum auth_method {
+    AUTH_METHOD_NONE,
+    AUTH_METHOD_PAP,
+    AUTH_METHOD_CHAP,
+};
+
+struct gprs_context_data {
+	GAtChat *chat;
+	unsigned int active_context;
+	char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1];
+	char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1];
+	enum auth_method auth_method;
+	enum state state;
+	enum ofono_gprs_proto proto;
+	char address[64];
+	char netmask[64];
+	char gateway[64];
+	char dns1[64];
+	char dns2[64];
+	ofono_gprs_context_cb_t cb;
+	void *cb_data;                                  /* Callback data */
+};
+
+static void failed_setup(struct ofono_gprs_context *gc,
+				GAtResult *result, gboolean deactivate)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	struct ofono_error error;
+	char buf[64];
+
+	DBG("deactivate %d", deactivate);
+
+	if (deactivate == TRUE) {
+		sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
+		g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL);
+	}
+
+	gcd->active_context = 0;
+	gcd->state = STATE_IDLE;
+
+	if (result == NULL) {
+		CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
+		return;
+	}
+
+	decode_at_error(&error, g_at_result_final_response(result));
+	gcd->cb(&error, gcd->cb_data);
+}
+
+static void session_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	struct ofono_modem *modem;
+	const char *interface;
+	const char *dns[3];
+
+	DBG("ok %d", ok);
+
+	if (!ok) {
+		ofono_error("Failed to establish session");
+		failed_setup(gc, result, TRUE);
+		return;
+	}
+
+	gcd->state = STATE_ACTIVE;
+
+	dns[0] = gcd->dns1;
+	dns[1] = gcd->dns2;
+	dns[2] = 0;
+
+	modem = ofono_gprs_context_get_modem(gc);
+	interface = ofono_modem_get_string(modem, "NetworkInterface");
+
+	ofono_gprs_context_set_interface(gc, interface);
+	ofono_gprs_context_set_ipv4_address(gc, gcd->address, TRUE);
+	ofono_gprs_context_set_ipv4_netmask(gc, gcd->netmask);
+	ofono_gprs_context_set_ipv4_gateway(gc, gcd->gateway);
+	ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
+
+	CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+	gcd->cb = NULL;
+	gcd->cb_data = NULL;
+}
+
+static void contrdp_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	char buf[64];
+	int cid, bearer_id;
+	const char *apn, *ip_mask, *gw;
+	const char *dns1, *dns2;
+	GAtResultIter iter;
+	gboolean found = FALSE;
+
+	DBG("ok %d", ok);
+
+	if (!ok) {
+		ofono_error("Unable to get context dynamic paramerers");
+		failed_setup(gc, result, TRUE);
+		return;
+	}
+
+	g_at_result_iter_init(&iter, result);
+
+	while (g_at_result_iter_next(&iter, "+CGCONTRDP:")) {
+		if (!g_at_result_iter_next_number(&iter, &cid))
+			goto error;
+		if (!g_at_result_iter_next_number(&iter, &bearer_id))
+			goto error;
+		if (!g_at_result_iter_next_string(&iter, &apn))
+			goto error;
+		if (!g_at_result_iter_next_string(&iter, &ip_mask))
+			goto error;
+		if (!g_at_result_iter_next_string(&iter, &gw))
+			goto error;
+		if (!g_at_result_iter_next_string(&iter, &dns1))
+			goto error;
+		if (!g_at_result_iter_next_string(&iter, &dns2))
+			goto error;
+
+		if ((unsigned int) cid == gcd->active_context) {
+			found = TRUE;
+			if (gcd->address && strcmp(gcd->address, "") != 0) {
+			    strncpy(gcd->netmask,
+				    &ip_mask[strlen(gcd->address)+1],
+				    sizeof(gcd->netmask));
+			}
+			strncpy(gcd->gateway, gw, sizeof(gcd->gateway));
+			strncpy(gcd->dns1, dns1, sizeof(gcd->dns1));
+			strncpy(gcd->dns2, dns2, sizeof(gcd->dns2));
+		}
+	}
+
+	if (found == FALSE)
+		goto error;
+
+	ofono_info("IP: %s", gcd->address);
+	ofono_info("MASK: %s", gcd->netmask);
+	ofono_info("GW: %s", gcd->gateway);
+	ofono_info("DNS: %s, %s", gcd->dns1, gcd->dns2);
+
+	sprintf(buf, "AT+CGDATA=\"M-RAW_IP\",%d", gcd->active_context);
+	if (g_at_chat_send(gcd->chat, buf, none_prefix,
+					session_cb, gc, NULL) > 0)
+		return;
+
+error:
+	failed_setup(gc, NULL, TRUE);
+}
+
+static void address_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	int cid;
+	const char *address;
+	char buf[64];
+	GAtResultIter iter;
+
+	DBG("ok %d", ok);
+
+	if (!ok) {
+		ofono_error("Unable to get context address");
+		failed_setup(gc, result, TRUE);
+		return;
+	}
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+CGPADDR:"))
+		goto error;
+
+	if (!g_at_result_iter_next_number(&iter, &cid))
+		goto error;
+
+	if ((unsigned int) cid != gcd->active_context)
+		goto error;
+
+	if (!g_at_result_iter_next_string(&iter, &address))
+		goto error;
+
+	strncpy(gcd->address, address, sizeof(gcd->address));
+
+	sprintf(buf, "AT+CGCONTRDP=%d", gcd->active_context);
+	if (g_at_chat_send(gcd->chat, buf, cgcontrdp_prefix,
+					contrdp_cb, gc, NULL) > 0)
+		return;
+
+error:
+	failed_setup(gc, NULL, TRUE);
+}
+
+static void activate_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	char buf[64];
+
+	DBG("ok %d", ok);
+
+	if (!ok) {
+		ofono_error("Unable to activate context");
+		failed_setup(gc, result, FALSE);
+		return;
+	}
+
+	sprintf(buf, "AT+CGPADDR=%u", gcd->active_context);
+	if (g_at_chat_send(gcd->chat, buf, cgpaddr_prefix,
+					address_cb, gc, NULL) > 0)
+		return;
+
+	failed_setup(gc, NULL, TRUE);
+}
+
+static void setup_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	char buf[128];
+
+	DBG("ok %d", ok);
+
+	if (!ok) {
+		ofono_error("Failed to setup context");
+		failed_setup(gc, result, FALSE);
+		return;
+	}
+
+	if (gcd->username[0] && gcd->password[0])
+		sprintf(buf, "AT#PDPAUTH=%u,%u,\"%s\",\"%s\"",
+			gcd->active_context, gcd->auth_method,
+			gcd->username, gcd->password);
+	else
+		sprintf(buf, "AT#PDPAUTH=%u,0", gcd->active_context);
+
+	if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
+		goto error;
+
+	sprintf(buf, "AT#NCM=1,%u", gcd->active_context);
+
+	if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
+		goto error;
+
+	sprintf(buf, "AT+CGACT=1,%u", gcd->active_context);
+
+	if (g_at_chat_send(gcd->chat, buf, none_prefix,
+				activate_cb, gc, NULL) > 0)
+		return;
+
+error:
+	failed_setup(gc, NULL, FALSE);
+}
+
+static void telitncm_gprs_activate_primary(struct ofono_gprs_context *gc,
+				const struct ofono_gprs_primary_context *ctx,
+				ofono_gprs_context_cb_t cb, void *data)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	char buf[OFONO_GPRS_MAX_APN_LENGTH + 128];
+	int len = 0;
+
+	DBG("cid %u", ctx->cid);
+
+	gcd->active_context = ctx->cid;
+	gcd->cb = cb;
+	gcd->cb_data = data;
+	memcpy(gcd->username, ctx->username, sizeof(ctx->username));
+	memcpy(gcd->password, ctx->password, sizeof(ctx->password));
+	gcd->state = STATE_ENABLING;
+	gcd->proto = ctx->proto;
+
+	/* We only support CHAP and PAP */
+	switch (ctx->auth_method) {
+	case OFONO_GPRS_AUTH_METHOD_CHAP:
+		gcd->auth_method = AUTH_METHOD_CHAP;
+		break;
+	case OFONO_GPRS_AUTH_METHOD_PAP:
+		gcd->auth_method = AUTH_METHOD_PAP;
+		break;
+	default:
+		gcd->auth_method = AUTH_METHOD_NONE;
+		break;
+	}
+
+	g_at_chat_send(gcd->chat, "AT+CGATT=0", none_prefix, NULL, NULL, NULL);
+
+	switch (ctx->proto) {
+	case OFONO_GPRS_PROTO_IP:
+		len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"",
+								ctx->cid);
+		break;
+	case OFONO_GPRS_PROTO_IPV6:
+		len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV6\"",
+								ctx->cid);
+		break;
+	case OFONO_GPRS_PROTO_IPV4V6:
+		len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV4V6\"",
+								ctx->cid);
+		break;
+	}
+
+	if (ctx->apn)
+		snprintf(buf + len, sizeof(buf) - len - 3,
+					",\"%s\"", ctx->apn);
+
+	if (g_at_chat_send(gcd->chat, buf, none_prefix,
+				setup_cb, gc, NULL) > 0)
+		return;
+
+	CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void deactivate_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+	DBG("ok %d", ok);
+
+	gcd->active_context = 0;
+	gcd->state = STATE_IDLE;
+
+	CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+	gcd->cb = NULL;
+	gcd->cb_data = NULL;
+}
+
+static void telitncm_gprs_deactivate_primary(struct ofono_gprs_context *gc,
+					unsigned int cid,
+					ofono_gprs_context_cb_t cb, void *data)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	char buf[64];
+
+	DBG("cid %u", cid);
+
+	gcd->state = STATE_DISABLING;
+	gcd->cb = cb;
+	gcd->cb_data = data;
+
+	sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
+	if (g_at_chat_send(gcd->chat, buf, none_prefix,
+				deactivate_cb, gc, NULL) > 0)
+		return;
+
+	CALLBACK_WITH_SUCCESS(cb, data);
+	gcd->cb = NULL;
+	gcd->cb_data = NULL;
+}
+
+static void cgev_notify(GAtResult *result, gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	const char *event;
+	int cid;
+	GAtResultIter iter;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+CGEV:"))
+		return;
+
+	if (!g_at_result_iter_next_unquoted_string(&iter, &event))
+		return;
+
+	if (g_str_has_prefix(event, "NW DEACT") == FALSE)
+		return;
+
+	if (!g_at_result_iter_skip_next(&iter))
+		return;
+
+	if (!g_at_result_iter_next_number(&iter, &cid))
+		return;
+
+	DBG("cid %d", cid);
+
+	if ((unsigned int) cid != gcd->active_context)
+		return;
+
+
+	ofono_gprs_context_deactivated(gc, gcd->active_context);
+
+	gcd->active_context = 0;
+	gcd->state = STATE_IDLE;
+
+}
+
+static int telitncm_gprs_context_probe(struct ofono_gprs_context *gc,
+					unsigned int vendor, void *data)
+{
+	GAtChat *chat = data;
+	struct gprs_context_data *gcd;
+
+	DBG("");
+
+	gcd = g_try_new0(struct gprs_context_data, 1);
+	if (gcd == NULL)
+		return -ENOMEM;
+
+	gcd->chat = g_at_chat_clone(chat);
+
+	ofono_gprs_context_set_data(gc, gcd);
+
+	chat = g_at_chat_get_slave(gcd->chat);
+
+	g_at_chat_register(chat, "+CGEV:", cgev_notify, FALSE, gc, NULL);
+
+	return 0;
+}
+
+static void telitncm_gprs_context_remove(struct ofono_gprs_context *gc)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+	DBG("");
+
+	ofono_gprs_context_set_data(gc, NULL);
+
+	g_at_chat_unref(gcd->chat);
+	g_free(gcd);
+}
+
+static struct ofono_gprs_context_driver driver = {
+	.name			= "telitncmmodem",
+	.probe			= telitncm_gprs_context_probe,
+	.remove			= telitncm_gprs_context_remove,
+	.activate_primary	= telitncm_gprs_activate_primary,
+	.deactivate_primary	= telitncm_gprs_deactivate_primary,
+};
+
+void telitncm_gprs_context_init(void)
+{
+	ofono_gprs_context_driver_register(&driver);
+}
+
+void telitncm_gprs_context_exit(void)
+{
+	ofono_gprs_context_driver_unregister(&driver);
+}
diff --git a/drivers/telitmodem/telitmodem.c b/drivers/telitmodem/telitmodem.c
index ecb84efb..4aa2c444 100644
--- a/drivers/telitmodem/telitmodem.c
+++ b/drivers/telitmodem/telitmodem.c
@@ -35,6 +35,7 @@
 static int telitmodem_init(void)
 {
 	telit_location_reporting_init();
+	telitncm_gprs_context_init();
 
 	return 0;
 }
@@ -42,6 +43,7 @@ static int telitmodem_init(void)
 static void telitmodem_exit(void)
 {
 	telit_location_reporting_exit();
+	telitncm_gprs_context_exit();
 }
 
 OFONO_PLUGIN_DEFINE(telitmodem, "Telit modem driver", VERSION,
diff --git a/drivers/telitmodem/telitmodem.h b/drivers/telitmodem/telitmodem.h
index 2db41787..8a14595a 100644
--- a/drivers/telitmodem/telitmodem.h
+++ b/drivers/telitmodem/telitmodem.h
@@ -23,3 +23,5 @@
 
 extern void telit_location_reporting_init();
 extern void telit_location_reporting_exit();
+extern void telitncm_gprs_context_init();
+extern void telitncm_gprs_context_exit();
-- 
2.11.0


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

* [PATCH 2/4] plugins: support for Telit LE910 V2 modem
  2017-01-25 10:41   ` [PATCH 0/4] add support for Telit LE910V2 Piotr Haber
  2017-01-25 10:41     ` [PATCH 1/4] telitmodem: support for CDC-NCM network adapter Piotr Haber
@ 2017-01-25 10:41     ` Piotr Haber
  2017-01-25 16:37       ` Denis Kenzior
  2017-01-25 10:41     ` [PATCH 3/4] doc: description of Telit LE910V2 network setup Piotr Haber
  2017-01-25 10:41     ` [PATCH 4/4] udevng: setup of Telit LE910V2 Piotr Haber
  3 siblings, 1 reply; 14+ messages in thread
From: Piotr Haber @ 2017-01-25 10:41 UTC (permalink / raw)
  To: ofono

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

LE910 V2 is next generation Telit LTE modem.
It supports 3GPP Rel. 9 LTE Cat. 4 over multiple bands.
Default USB composition uses PID 0x36 and
consists of 6 CDC-ACM serial ports and 1 CDC-NCM network adapter.
---
 Makefile.am       |   3 +
 plugins/le910v2.c | 400 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 403 insertions(+)
 create mode 100644 plugins/le910v2.c

diff --git a/Makefile.am b/Makefile.am
index 2f49027c..72c4fcfc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -501,6 +501,9 @@ builtin_sources += plugins/quectel.c
 builtin_modules += ublox
 builtin_sources += plugins/ublox.c
 
+builtin_modules += le910v2
+builtin_sources += plugins/le910v2.c
+
 if BLUETOOTH
 if BLUEZ4
 builtin_modules += telit
diff --git a/plugins/le910v2.c b/plugins/le910v2.c
new file mode 100644
index 00000000..e758971d
--- /dev/null
+++ b/plugins/le910v2.c
@@ -0,0 +1,400 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2017 Piotr Haber. All rights reserved.
+ *
+ *  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 <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-barring.h>
+#include <ofono/call-forwarding.h>
+#include <ofono/call-meter.h>
+#include <ofono/call-settings.h>
+#include <ofono/devinfo.h>
+#include <ofono/message-waiting.h>
+#include <ofono/location-reporting.h>
+#include <ofono/netreg.h>
+#include <ofono/phonebook.h>
+#include <ofono/sim.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/voicecall.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+
+static const char *none_prefix[] = { NULL };
+static const char *qss_prefix[] = { "#QSS:", NULL };
+
+struct le910v2_data {
+	GAtChat *chat;		/* AT chat */
+	GAtChat *modem;		/* Data port */
+	struct ofono_sim *sim;
+	ofono_bool_t have_sim;
+	ofono_bool_t sms_phonebook_added;
+};
+
+static void le910v2_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	ofono_info("%s%s", prefix, str);
+}
+
+static GAtChat *open_device(struct ofono_modem *modem,
+				const char *key, char *debug)
+{
+	const char *device;
+	GAtSyntax *syntax;
+	GIOChannel *channel;
+	GAtChat *chat;
+	GHashTable *options;
+
+	device = ofono_modem_get_string(modem, key);
+	if (device == NULL)
+		return NULL;
+
+	DBG("%s %s", key, device);
+
+	options = g_hash_table_new(g_str_hash, g_str_equal);
+	if (options == NULL)
+		return NULL;
+
+	g_hash_table_insert(options, "Baud", "115200");
+	channel = g_at_tty_open(device, options);
+	g_hash_table_destroy(options);
+
+	if (channel == NULL)
+		return NULL;
+
+	syntax = g_at_syntax_new_gsm_permissive();
+	chat = g_at_chat_new(channel, syntax);
+	g_at_syntax_unref(syntax);
+	g_io_channel_unref(channel);
+
+	if (chat == NULL)
+		return NULL;
+
+	if (getenv("OFONO_AT_DEBUG"))
+		g_at_chat_set_debug(chat, le910v2_debug, debug);
+
+	return chat;
+}
+
+static void switch_sim_state_status(struct ofono_modem *modem, int status)
+{
+	struct le910v2_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p, SIM status: %d", modem, status);
+
+	switch (status) {
+	case 0:	/* SIM not inserted */
+		if (data->have_sim == TRUE) {
+			ofono_sim_inserted_notify(data->sim, FALSE);
+			data->have_sim = FALSE;
+			data->sms_phonebook_added = FALSE;
+		}
+		break;
+	case 1:	/* SIM inserted */
+	case 2:	/* SIM inserted and PIN unlocked */
+		if (data->have_sim == FALSE) {
+			ofono_sim_inserted_notify(data->sim, TRUE);
+			data->have_sim = TRUE;
+		}
+		break;
+	case 3:	/* SIM inserted, SMS and phonebook ready */
+		if (data->have_sim == FALSE) {
+			ofono_sim_inserted_notify(data->sim, TRUE);
+			data->have_sim = TRUE;
+		}
+		if (data->sms_phonebook_added == FALSE) {
+			ofono_phonebook_create(modem, 0, "atmodem", data->chat);
+			ofono_sms_create(modem, 0, "atmodem", data->chat);
+			data->sms_phonebook_added = TRUE;
+		}
+		break;
+	default:
+		ofono_warn("Unknown SIM state %d received", status);
+		break;
+	}
+}
+
+static void le910v2_qss_notify(GAtResult *result, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	int status;
+	GAtResultIter iter;
+
+	DBG("%p", modem);
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "#QSS:"))
+		return;
+
+	g_at_result_iter_next_number(&iter, &status);
+
+	switch_sim_state_status(modem, status);
+}
+
+static void qss_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	int status, mode;
+	GAtResultIter iter;
+
+	DBG("%p", modem);
+
+	if (!ok)
+		return;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "#QSS:"))
+		return;
+
+	if (!g_at_result_iter_next_number(&iter, &mode))
+		return;
+
+	if (!g_at_result_iter_next_number(&iter, &status))
+		return;
+
+	switch_sim_state_status(modem, status);
+}
+
+static void cfun_enable_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct le910v2_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	if (!ok) {
+		g_at_chat_unref(data->chat);
+		data->chat = NULL;
+
+		g_at_chat_unref(data->modem);
+		data->modem = NULL;
+
+		ofono_modem_set_powered(modem, FALSE);
+		return;
+	}
+
+	/*
+	 * Switch data carrier detect signal off.
+	 * When the DCD is disabled the modem does not hangup anymore
+	 * after the data connection.
+	 */
+	g_at_chat_send(data->chat, "AT&C0", NULL, NULL, NULL, NULL);
+
+	data->have_sim = FALSE;
+	data->sms_phonebook_added = FALSE;
+
+	ofono_modem_set_powered(modem, TRUE);
+
+	/*
+	 * Tell the modem not to automatically initiate auto-attach
+	 * proceedures on its own.
+	 */
+	g_at_chat_send(data->chat, "AT#AUTOATT=0", none_prefix,
+				NULL, NULL, NULL);
+
+	/* Follow sim state */
+	g_at_chat_register(data->chat, "#QSS:", le910v2_qss_notify,
+				FALSE, modem, NULL);
+
+	/* Enable sim state notification */
+	g_at_chat_send(data->chat, "AT#QSS=2", none_prefix, NULL, NULL, NULL);
+
+	g_at_chat_send(data->chat, "AT#QSS?", qss_prefix,
+			qss_query_cb, modem, NULL);
+}
+
+static int le910v2_enable(struct ofono_modem *modem)
+{
+	struct le910v2_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	data->modem = open_device(modem, "Modem", "Modem: ");
+	if (data->modem == NULL)
+		return -EINVAL;
+
+	data->chat = open_device(modem, "Aux", "Aux: ");
+	if (data->chat == NULL) {
+		g_at_chat_unref(data->modem);
+		data->modem = NULL;
+		return -EIO;
+	}
+
+	/*
+	 * Disable command echo and
+	 * enable the Extended Error Result Codes
+	 */
+	g_at_chat_send(data->chat, "ATE0 +CMEE=1", none_prefix,
+				NULL, NULL, NULL);
+
+	g_at_chat_send(data->modem, "ATE0", none_prefix,
+				NULL, NULL, NULL);
+
+	/* Set phone functionality */
+	g_at_chat_send(data->chat, "AT+CFUN=1", none_prefix,
+				cfun_enable_cb, modem, NULL);
+
+	return -EINPROGRESS;
+}
+
+static void cfun_disable_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct le910v2_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	g_at_chat_unref(data->chat);
+	data->chat = NULL;
+
+	if (ok)
+		ofono_modem_set_powered(modem, FALSE);
+}
+
+static int le910v2_disable(struct ofono_modem *modem)
+{
+	struct le910v2_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	g_at_chat_cancel_all(data->modem);
+	g_at_chat_unregister_all(data->modem);
+	g_at_chat_unref(data->modem);
+	data->modem = NULL;
+
+	g_at_chat_cancel_all(data->chat);
+	g_at_chat_unregister_all(data->chat);
+
+	/* Power down modem */
+	g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix,
+				cfun_disable_cb, modem, NULL);
+
+	return -EINPROGRESS;
+}
+
+static void le910v2_pre_sim(struct ofono_modem *modem)
+{
+	struct le910v2_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	ofono_devinfo_create(modem, 0, "atmodem", data->chat);
+	data->sim = ofono_sim_create(modem, OFONO_VENDOR_TELIT, "atmodem",
+					data->chat);
+}
+
+static void le910v2_post_online(struct ofono_modem *modem)
+{
+	struct le910v2_data *data = ofono_modem_get_data(modem);
+	struct ofono_gprs *gprs;
+	struct ofono_gprs_context *gc;
+
+	DBG("%p", modem);
+
+	ofono_netreg_create(modem, OFONO_VENDOR_TELIT, "atmodem", data->chat);
+	gprs = ofono_gprs_create(modem, OFONO_VENDOR_TELIT, "atmodem",
+					data->chat);
+	gc = ofono_gprs_context_create(modem, OFONO_VENDOR_TELIT, "telitncmmodem",
+					data->modem);
+
+	if (gprs && gc)
+		ofono_gprs_add_context(gprs, gc);
+}
+
+static int le910v2_probe(struct ofono_modem *modem)
+{
+	struct le910v2_data *data;
+
+	DBG("%p", modem);
+
+	data = g_try_new0(struct le910v2_data, 1);
+	if (data == NULL)
+		return -ENOMEM;
+
+	ofono_modem_set_data(modem, data);
+
+	return 0;
+}
+
+static void le910v2_remove(struct ofono_modem *modem)
+{
+	struct le910v2_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	ofono_modem_set_data(modem, NULL);
+
+	/* Cleanup after hot-unplug */
+	g_at_chat_unref(data->chat);
+	g_at_chat_unref(data->modem);
+
+	g_free(data);
+}
+
+static struct ofono_modem_driver le910v2_driver = {
+	.name		= "le910v2",
+	.probe		= le910v2_probe,
+	.remove		= le910v2_remove,
+	.enable		= le910v2_enable,
+	.disable	= le910v2_disable,
+	.pre_sim	= le910v2_pre_sim,
+	.post_online	= le910v2_post_online,
+};
+
+static int le910v2_init(void)
+{
+	DBG("");
+
+	return ofono_modem_driver_register(&le910v2_driver);
+}
+
+static void le910v2_exit(void)
+{
+	ofono_modem_driver_unregister(&le910v2_driver);
+}
+
+OFONO_PLUGIN_DEFINE(le910v2, "Telit LE910 V2 driver", VERSION,
+		OFONO_PLUGIN_PRIORITY_DEFAULT, le910v2_init, le910v2_exit)
-- 
2.11.0


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

* [PATCH 3/4] doc: description of Telit LE910V2 network setup
  2017-01-25 10:41   ` [PATCH 0/4] add support for Telit LE910V2 Piotr Haber
  2017-01-25 10:41     ` [PATCH 1/4] telitmodem: support for CDC-NCM network adapter Piotr Haber
  2017-01-25 10:41     ` [PATCH 2/4] plugins: support for Telit LE910 V2 modem Piotr Haber
@ 2017-01-25 10:41     ` Piotr Haber
  2017-01-25 16:38       ` Denis Kenzior
  2017-01-25 10:41     ` [PATCH 4/4] udevng: setup of Telit LE910V2 Piotr Haber
  3 siblings, 1 reply; 14+ messages in thread
From: Piotr Haber @ 2017-01-25 10:41 UTC (permalink / raw)
  To: ofono

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

NCM network interface on LE910V2 modem needs to be
configured in a specific way after connection is established.
---
 doc/telit-modem.txt | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/doc/telit-modem.txt b/doc/telit-modem.txt
index 1627fb4c..d4d367d7 100644
--- a/doc/telit-modem.txt
+++ b/doc/telit-modem.txt
@@ -17,3 +17,30 @@ GPS:
   After setting the configuration, a power cycle is required.
   Port Configiuration #8 is available since firmware 12.00.004. Firmware version
   can be checked using 'AT+CGMR'.
+
+LE910 V2
+========
+
+Default USB composition of LE910V2 uses PID 0x36 (AT#PORTCFG=0)
+and consists of 6 serial ports (CDC-ACM standard, /dev/ttyACMx)
+and 1 network adapter using CDC-NCM standard (wwanx or usbx).
+
+NCM interface configuration follows Telit documentation
+(both documents available on Telit Download Zone - registration required)
+"GE/HE/UE910, UL865, LE910 V2 Linux USB Driver - User Guide r0"
+(document 1VV0301255 Rev.0 - 2016-01-22)
+and "Telit LE910-V2 NCM SETUP r3"
+(document 1VV0301246 Rev.3 - 2016-11-29).
+
+After context is setup, NCM mode activated and PDP context activated
+connection configuration can be read using
+AT+CGPADDR=context_id and AT+CGCONTRDP=context_id commands.
+This is done automatically and results available via
+org.ofono.ConnectionContext.GetProperties DBus method.
+
+Then Linux network interface needs to be configured:
+    ifconfig <Interface> <Address> netmask <Netmask> up
+    route add default gw <Gateway>
+    arp -s <Gateway> 11:22:33:44:55:66
+
+Only after these steps network interface is usable.
-- 
2.11.0


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

* [PATCH 4/4] udevng: setup of Telit LE910V2
  2017-01-25 10:41   ` [PATCH 0/4] add support for Telit LE910V2 Piotr Haber
                       ` (2 preceding siblings ...)
  2017-01-25 10:41     ` [PATCH 3/4] doc: description of Telit LE910V2 network setup Piotr Haber
@ 2017-01-25 10:41     ` Piotr Haber
  3 siblings, 0 replies; 14+ messages in thread
From: Piotr Haber @ 2017-01-25 10:41 UTC (permalink / raw)
  To: ofono

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

Setup LE910V2 in default USB composition
with 6 CDC-ACM serial ports and 1 CDC-NCM network adapter.
---
 plugins/udevng.c | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/plugins/udevng.c b/plugins/udevng.c
index 50089129..90ddeeab 100644
--- a/plugins/udevng.c
+++ b/plugins/udevng.c
@@ -957,6 +957,41 @@ static gboolean setup_gemalto(struct modem_info* modem)
 	return TRUE;
 }
 
+static gboolean setup_le910v2(struct modem_info *modem)
+{
+	const char *aux = NULL, *mdm = NULL, *network = NULL;
+	GSList *list;
+
+	DBG("%s", modem->syspath);
+
+	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->sysattr);
+
+		if (g_strcmp0(info->interface, "2/2/1") == 0) {
+			if (g_strcmp0(info->number, "00") == 0)
+				aux = info->devnode;
+			else if (g_strcmp0(info->number, "06") == 0)
+				mdm = info->devnode;
+		} else if (info->sysattr && (g_str_has_suffix(info->sysattr,
+						"CDC NCM") == TRUE)) {
+			network = info->devnode;
+		}
+	}
+	DBG("aux=%s modem=%s network=%s", aux, mdm, network);
+
+	if (aux == NULL || mdm == NULL || network == NULL)
+		return FALSE;
+
+	ofono_modem_set_string(modem->modem, "Aux", aux);
+	ofono_modem_set_string(modem->modem, "Modem", mdm);
+	ofono_modem_set_string(modem->modem, "NetworkInterface", network);
+
+	return TRUE;
+}
+
 static struct {
 	const char *name;
 	gboolean (*setup)(struct modem_info *modem);
@@ -984,6 +1019,7 @@ static struct {
 	{ "quectel",	setup_quectel	},
 	{ "ublox",	setup_ublox	},
 	{ "gemalto",	setup_gemalto	},
+	{ "le910v2",	setup_le910v2,	"device/interface"	},
 	{ }
 };
 
@@ -1226,6 +1262,8 @@ static struct {
 	{ "gemalto",	"option",	"1e2d",	"0053"	},
 	{ "gemalto",	"cdc_wdm",	"1e2d",	"0053"	},
 	{ "gemalto",	"qmi_wwan",	"1e2d",	"0053"	},
+	{ "le910v2",	"cdc_ncm",	"1bc7", "0036"	},
+	{ "le910v2",	"cdc_acm",	"1bc7", "0036"	},
 	{ }
 };
 
-- 
2.11.0


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

* Re: [PATCH 1/4] telitmodem: support for CDC-NCM network adapter
  2017-01-25 10:41     ` [PATCH 1/4] telitmodem: support for CDC-NCM network adapter Piotr Haber
@ 2017-01-25 16:33       ` Denis Kenzior
  0 siblings, 0 replies; 14+ messages in thread
From: Denis Kenzior @ 2017-01-25 16:33 UTC (permalink / raw)
  To: ofono

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

On 01/25/2017 04:41 AM, Piotr Haber wrote:
> Network Control Model is a new Communication Device Class
> protocol for exchanging Ethernet frames over USB.
> NCM is intended to be used with high-speed network
> attachments such as HSDPA and LTE data services.
> ---
>  Makefile.am                           |   3 +-
>  drivers/telitmodem/gprs-context-ncm.c | 497 ++++++++++++++++++++++++++++++++++
>  drivers/telitmodem/telitmodem.c       |   2 +
>  drivers/telitmodem/telitmodem.h       |   2 +
>  4 files changed, 503 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/telitmodem/gprs-context-ncm.c
>
> diff --git a/Makefile.am b/Makefile.am
> index f76971ec..2f49027c 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -321,7 +321,8 @@ builtin_modules += telitmodem
>  builtin_sources += drivers/atmodem/atutil.h \
>  			drivers/telitmodem/telitmodem.h \
>  			drivers/telitmodem/telitmodem.c \
> -			drivers/telitmodem/location-reporting.c
> +			drivers/telitmodem/location-reporting.c \
> +			drivers/telitmodem/gprs-context-ncm.c
>
>  builtin_modules += hsomodem
>  builtin_sources += drivers/atmodem/atutil.h \
> diff --git a/drivers/telitmodem/gprs-context-ncm.c b/drivers/telitmodem/gprs-context-ncm.c
> new file mode 100644
> index 00000000..25f93632
> --- /dev/null
> +++ b/drivers/telitmodem/gprs-context-ncm.c
> @@ -0,0 +1,497 @@
> +/*
> + *
> + *  oFono - Open Source Telephony
> + *
> + *  Copyright (C) 2017 Piotr Haber. All rights reserved.
> + *
> + *  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
> +
> +#define _GNU_SOURCE
> +#include <string.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <errno.h>
> +#include <sys/stat.h>
> +
> +#include <glib.h>
> +
> +#include <ofono/log.h>
> +#include <ofono/modem.h>
> +#include <ofono/gprs-context.h>
> +
> +#include "gatchat.h"
> +#include "gatresult.h"
> +#include "gatrawip.h"

This can be removed, right?  Since Telit is not using a multiplexer, we 
don't have raw IP frames coming over the AT channel.  So we are not 
using g_at_rawip at all.

> +
> +#include "telitmodem.h"
> +
> +static const char *none_prefix[] = { NULL };
> +static const char *cgpaddr_prefix[] = { "+CGPADDR:", NULL };
> +static const char *cgcontrdp_prefix[] = { "+CGCONTRDP:", NULL };
> +
> +enum state {
> +	STATE_IDLE,
> +	STATE_ENABLING,
> +	STATE_DISABLING,
> +	STATE_ACTIVE,
> +};
> +
> +enum auth_method {
> +    AUTH_METHOD_NONE,
> +    AUTH_METHOD_PAP,
> +    AUTH_METHOD_CHAP,
> +};
> +
> +struct gprs_context_data {
> +	GAtChat *chat;
> +	unsigned int active_context;
> +	char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1];
> +	char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1];
> +	enum auth_method auth_method;
> +	enum state state;
> +	enum ofono_gprs_proto proto;
> +	char address[64];
> +	char netmask[64];
> +	char gateway[64];
> +	char dns1[64];
> +	char dns2[64];
> +	ofono_gprs_context_cb_t cb;
> +	void *cb_data;                                  /* Callback data */
> +};
> +
> +static void failed_setup(struct ofono_gprs_context *gc,
> +				GAtResult *result, gboolean deactivate)
> +{
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	struct ofono_error error;
> +	char buf[64];
> +
> +	DBG("deactivate %d", deactivate);
> +
> +	if (deactivate == TRUE) {
> +		sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
> +		g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL);
> +	}
> +
> +	gcd->active_context = 0;
> +	gcd->state = STATE_IDLE;
> +
> +	if (result == NULL) {
> +		CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
> +		return;
> +	}
> +
> +	decode_at_error(&error, g_at_result_final_response(result));
> +	gcd->cb(&error, gcd->cb_data);
> +}
> +
> +static void session_cb(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_gprs_context *gc = user_data;
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	struct ofono_modem *modem;
> +	const char *interface;
> +	const char *dns[3];
> +
> +	DBG("ok %d", ok);
> +
> +	if (!ok) {
> +		ofono_error("Failed to establish session");
> +		failed_setup(gc, result, TRUE);
> +		return;
> +	}
> +
> +	gcd->state = STATE_ACTIVE;
> +
> +	dns[0] = gcd->dns1;
> +	dns[1] = gcd->dns2;
> +	dns[2] = 0;
> +
> +	modem = ofono_gprs_context_get_modem(gc);
> +	interface = ofono_modem_get_string(modem, "NetworkInterface");
> +
> +	ofono_gprs_context_set_interface(gc, interface);
> +	ofono_gprs_context_set_ipv4_address(gc, gcd->address, TRUE);
> +	ofono_gprs_context_set_ipv4_netmask(gc, gcd->netmask);
> +	ofono_gprs_context_set_ipv4_gateway(gc, gcd->gateway);
> +	ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
> +
> +	CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
> +	gcd->cb = NULL;
> +	gcd->cb_data = NULL;
> +}
> +
> +static void contrdp_cb(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_gprs_context *gc = user_data;
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	char buf[64];
> +	int cid, bearer_id;
> +	const char *apn, *ip_mask, *gw;
> +	const char *dns1, *dns2;
> +	GAtResultIter iter;
> +	gboolean found = FALSE;
> +
> +	DBG("ok %d", ok);
> +
> +	if (!ok) {
> +		ofono_error("Unable to get context dynamic paramerers");
> +		failed_setup(gc, result, TRUE);
> +		return;
> +	}
> +
> +	g_at_result_iter_init(&iter, result);
> +
> +	while (g_at_result_iter_next(&iter, "+CGCONTRDP:")) {
> +		if (!g_at_result_iter_next_number(&iter, &cid))
> +			goto error;
> +		if (!g_at_result_iter_next_number(&iter, &bearer_id))
> +			goto error;
> +		if (!g_at_result_iter_next_string(&iter, &apn))
> +			goto error;
> +		if (!g_at_result_iter_next_string(&iter, &ip_mask))
> +			goto error;
> +		if (!g_at_result_iter_next_string(&iter, &gw))
> +			goto error;
> +		if (!g_at_result_iter_next_string(&iter, &dns1))
> +			goto error;
> +		if (!g_at_result_iter_next_string(&iter, &dns2))
> +			goto error;
> +
> +		if ((unsigned int) cid == gcd->active_context) {
> +			found = TRUE;
> +			if (gcd->address && strcmp(gcd->address, "") != 0) {
> +			    strncpy(gcd->netmask,
> +				    &ip_mask[strlen(gcd->address)+1],
> +				    sizeof(gcd->netmask));

Only tabs for indentation please

> +			}

doc/coding-style.txt item M1
> +			strncpy(gcd->gateway, gw, sizeof(gcd->gateway));
> +			strncpy(gcd->dns1, dns1, sizeof(gcd->dns1));
> +			strncpy(gcd->dns2, dns2, sizeof(gcd->dns2));
> +		}
> +	}
> +
> +	if (found == FALSE)
> +		goto error;
> +
> +	ofono_info("IP: %s", gcd->address);
> +	ofono_info("MASK: %s", gcd->netmask);
> +	ofono_info("GW: %s", gcd->gateway);
> +	ofono_info("DNS: %s, %s", gcd->dns1, gcd->dns2);
> +
> +	sprintf(buf, "AT+CGDATA=\"M-RAW_IP\",%d", gcd->active_context);
> +	if (g_at_chat_send(gcd->chat, buf, none_prefix,
> +					session_cb, gc, NULL) > 0)
> +		return;
> +
> +error:
> +	failed_setup(gc, NULL, TRUE);
> +}
> +
> +static void address_cb(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_gprs_context *gc = user_data;
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	int cid;
> +	const char *address;
> +	char buf[64];
> +	GAtResultIter iter;
> +
> +	DBG("ok %d", ok);
> +
> +	if (!ok) {
> +		ofono_error("Unable to get context address");
> +		failed_setup(gc, result, TRUE);
> +		return;
> +	}
> +
> +	g_at_result_iter_init(&iter, result);
> +
> +	if (!g_at_result_iter_next(&iter, "+CGPADDR:"))
> +		goto error;
> +
> +	if (!g_at_result_iter_next_number(&iter, &cid))
> +		goto error;
> +
> +	if ((unsigned int) cid != gcd->active_context)
> +		goto error;
> +
> +	if (!g_at_result_iter_next_string(&iter, &address))
> +		goto error;
> +
> +	strncpy(gcd->address, address, sizeof(gcd->address));
> +
> +	sprintf(buf, "AT+CGCONTRDP=%d", gcd->active_context);
> +	if (g_at_chat_send(gcd->chat, buf, cgcontrdp_prefix,
> +					contrdp_cb, gc, NULL) > 0)
> +		return;
> +
> +error:
> +	failed_setup(gc, NULL, TRUE);
> +}
> +
> +static void activate_cb(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_gprs_context *gc = user_data;
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	char buf[64];
> +
> +	DBG("ok %d", ok);
> +
> +	if (!ok) {
> +		ofono_error("Unable to activate context");
> +		failed_setup(gc, result, FALSE);
> +		return;
> +	}
> +
> +	sprintf(buf, "AT+CGPADDR=%u", gcd->active_context);
> +	if (g_at_chat_send(gcd->chat, buf, cgpaddr_prefix,
> +					address_cb, gc, NULL) > 0)
> +		return;
> +
> +	failed_setup(gc, NULL, TRUE);
> +}
> +
> +static void setup_cb(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_gprs_context *gc = user_data;
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	char buf[128];
> +
> +	DBG("ok %d", ok);
> +
> +	if (!ok) {
> +		ofono_error("Failed to setup context");
> +		failed_setup(gc, result, FALSE);
> +		return;
> +	}
> +
> +	if (gcd->username[0] && gcd->password[0])
> +		sprintf(buf, "AT#PDPAUTH=%u,%u,\"%s\",\"%s\"",
> +			gcd->active_context, gcd->auth_method,
> +			gcd->username, gcd->password);
> +	else
> +		sprintf(buf, "AT#PDPAUTH=%u,0", gcd->active_context);
> +
> +	if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
> +		goto error;
> +
> +	sprintf(buf, "AT#NCM=1,%u", gcd->active_context);
> +
> +	if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
> +		goto error;
> +
> +	sprintf(buf, "AT+CGACT=1,%u", gcd->active_context);
> +
> +	if (g_at_chat_send(gcd->chat, buf, none_prefix,
> +				activate_cb, gc, NULL) > 0)
> +		return;
> +
> +error:
> +	failed_setup(gc, NULL, FALSE);
> +}
> +
> +static void telitncm_gprs_activate_primary(struct ofono_gprs_context *gc,
> +				const struct ofono_gprs_primary_context *ctx,
> +				ofono_gprs_context_cb_t cb, void *data)
> +{
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	char buf[OFONO_GPRS_MAX_APN_LENGTH + 128];
> +	int len = 0;
> +
> +	DBG("cid %u", ctx->cid);
> +
> +	gcd->active_context = ctx->cid;
> +	gcd->cb = cb;
> +	gcd->cb_data = data;
> +	memcpy(gcd->username, ctx->username, sizeof(ctx->username));
> +	memcpy(gcd->password, ctx->password, sizeof(ctx->password));
> +	gcd->state = STATE_ENABLING;
> +	gcd->proto = ctx->proto;
> +
> +	/* We only support CHAP and PAP */
> +	switch (ctx->auth_method) {
> +	case OFONO_GPRS_AUTH_METHOD_CHAP:
> +		gcd->auth_method = AUTH_METHOD_CHAP;
> +		break;
> +	case OFONO_GPRS_AUTH_METHOD_PAP:
> +		gcd->auth_method = AUTH_METHOD_PAP;
> +		break;
> +	default:
> +		gcd->auth_method = AUTH_METHOD_NONE;
> +		break;

This really cannot happen and should be treated as an error.

> +	}
> +
> +	g_at_chat_send(gcd->chat, "AT+CGATT=0", none_prefix, NULL, NULL, NULL);
> +
> +	switch (ctx->proto) {
> +	case OFONO_GPRS_PROTO_IP:
> +		len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"",
> +								ctx->cid);
> +		break;
> +	case OFONO_GPRS_PROTO_IPV6:
> +		len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV6\"",
> +								ctx->cid);
> +		break;
> +	case OFONO_GPRS_PROTO_IPV4V6:
> +		len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV4V6\"",
> +								ctx->cid);
> +		break;
> +	}
> +
> +	if (ctx->apn)
> +		snprintf(buf + len, sizeof(buf) - len - 3,
> +					",\"%s\"", ctx->apn);
> +
> +	if (g_at_chat_send(gcd->chat, buf, none_prefix,
> +				setup_cb, gc, NULL) > 0)
> +		return;
> +
> +	CALLBACK_WITH_FAILURE(cb, data);
> +}
> +
> +static void deactivate_cb(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_gprs_context *gc = user_data;
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +
> +	DBG("ok %d", ok);
> +
> +	gcd->active_context = 0;
> +	gcd->state = STATE_IDLE;
> +
> +	CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
> +	gcd->cb = NULL;
> +	gcd->cb_data = NULL;
> +}
> +
> +static void telitncm_gprs_deactivate_primary(struct ofono_gprs_context *gc,
> +					unsigned int cid,
> +					ofono_gprs_context_cb_t cb, void *data)
> +{
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	char buf[64];
> +
> +	DBG("cid %u", cid);
> +
> +	gcd->state = STATE_DISABLING;
> +	gcd->cb = cb;
> +	gcd->cb_data = data;
> +
> +	sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
> +	if (g_at_chat_send(gcd->chat, buf, none_prefix,
> +				deactivate_cb, gc, NULL) > 0)
> +		return;
> +
> +	CALLBACK_WITH_SUCCESS(cb, data);
> +	gcd->cb = NULL;
> +	gcd->cb_data = NULL;
> +}
> +
> +static void cgev_notify(GAtResult *result, gpointer user_data)
> +{
> +	struct ofono_gprs_context *gc = user_data;
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +	const char *event;
> +	int cid;
> +	GAtResultIter iter;
> +
> +	g_at_result_iter_init(&iter, result);
> +
> +	if (!g_at_result_iter_next(&iter, "+CGEV:"))
> +		return;
> +
> +	if (!g_at_result_iter_next_unquoted_string(&iter, &event))
> +		return;
> +
> +	if (g_str_has_prefix(event, "NW DEACT") == FALSE)
> +		return;
> +
> +	if (!g_at_result_iter_skip_next(&iter))
> +		return;
> +
> +	if (!g_at_result_iter_next_number(&iter, &cid))
> +		return;
> +
> +	DBG("cid %d", cid);
> +
> +	if ((unsigned int) cid != gcd->active_context)
> +		return;
> +
> +

No double empty lines please

> +	ofono_gprs_context_deactivated(gc, gcd->active_context);
> +
> +	gcd->active_context = 0;
> +	gcd->state = STATE_IDLE;
> +

No unnecessary whitespace at end of functions please

> +}
> +
> +static int telitncm_gprs_context_probe(struct ofono_gprs_context *gc,
> +					unsigned int vendor, void *data)
> +{
> +	GAtChat *chat = data;
> +	struct gprs_context_data *gcd;
> +
> +	DBG("");
> +
> +	gcd = g_try_new0(struct gprs_context_data, 1);
> +	if (gcd == NULL)
> +		return -ENOMEM;
> +
> +	gcd->chat = g_at_chat_clone(chat);
> +
> +	ofono_gprs_context_set_data(gc, gcd);
> +
> +	chat = g_at_chat_get_slave(gcd->chat);

You're not using g_at_chat_set_slave() anywhere in patch #2.  So I would 
expect this to crash.  I don't even see why this part is needed.

> +
> +	g_at_chat_register(chat, "+CGEV:", cgev_notify, FALSE, gc, NULL);
> +
> +	return 0;
> +}
> +
> +static void telitncm_gprs_context_remove(struct ofono_gprs_context *gc)
> +{
> +	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +
> +	DBG("");
> +
> +	ofono_gprs_context_set_data(gc, NULL);
> +
> +	g_at_chat_unref(gcd->chat);
> +	g_free(gcd);
> +}
> +
> +static struct ofono_gprs_context_driver driver = {
> +	.name			= "telitncmmodem",
> +	.probe			= telitncm_gprs_context_probe,
> +	.remove			= telitncm_gprs_context_remove,
> +	.activate_primary	= telitncm_gprs_activate_primary,
> +	.deactivate_primary	= telitncm_gprs_deactivate_primary,
> +};
> +
> +void telitncm_gprs_context_init(void)
> +{
> +	ofono_gprs_context_driver_register(&driver);
> +}
> +
> +void telitncm_gprs_context_exit(void)
> +{
> +	ofono_gprs_context_driver_unregister(&driver);
> +}
> diff --git a/drivers/telitmodem/telitmodem.c b/drivers/telitmodem/telitmodem.c
> index ecb84efb..4aa2c444 100644
> --- a/drivers/telitmodem/telitmodem.c
> +++ b/drivers/telitmodem/telitmodem.c
> @@ -35,6 +35,7 @@
>  static int telitmodem_init(void)
>  {
>  	telit_location_reporting_init();
> +	telitncm_gprs_context_init();
>
>  	return 0;
>  }
> @@ -42,6 +43,7 @@ static int telitmodem_init(void)
>  static void telitmodem_exit(void)
>  {
>  	telit_location_reporting_exit();
> +	telitncm_gprs_context_exit();
>  }
>
>  OFONO_PLUGIN_DEFINE(telitmodem, "Telit modem driver", VERSION,
> diff --git a/drivers/telitmodem/telitmodem.h b/drivers/telitmodem/telitmodem.h
> index 2db41787..8a14595a 100644
> --- a/drivers/telitmodem/telitmodem.h
> +++ b/drivers/telitmodem/telitmodem.h
> @@ -23,3 +23,5 @@
>
>  extern void telit_location_reporting_init();
>  extern void telit_location_reporting_exit();
> +extern void telitncm_gprs_context_init();
> +extern void telitncm_gprs_context_exit();
>

Regards,
-Denis

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

* Re: [PATCH 2/4] plugins: support for Telit LE910 V2 modem
  2017-01-25 10:41     ` [PATCH 2/4] plugins: support for Telit LE910 V2 modem Piotr Haber
@ 2017-01-25 16:37       ` Denis Kenzior
  2017-01-26  9:09         ` gluedig
  0 siblings, 1 reply; 14+ messages in thread
From: Denis Kenzior @ 2017-01-25 16:37 UTC (permalink / raw)
  To: ofono

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

On 01/25/2017 04:41 AM, Piotr Haber wrote:
> LE910 V2 is next generation Telit LTE modem.
> It supports 3GPP Rel. 9 LTE Cat. 4 over multiple bands.
> Default USB composition uses PID 0x36 and
> consists of 6 CDC-ACM serial ports and 1 CDC-NCM network adapter.
> ---
>  Makefile.am       |   3 +
>  plugins/le910v2.c | 400 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 403 insertions(+)
>  create mode 100644 plugins/le910v2.c
>

Can you add the LE910 changes into the HE910 driver?  The two are 99% 
the same except for HE910 supporting voice calls and LE910 using a 
different gprs_context driver.

There's no sense in maintaining a 400 line driver that is virtually 
identical to what we already have.

Regards,
-Denis


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

* Re: [PATCH 3/4] doc: description of Telit LE910V2 network setup
  2017-01-25 10:41     ` [PATCH 3/4] doc: description of Telit LE910V2 network setup Piotr Haber
@ 2017-01-25 16:38       ` Denis Kenzior
  0 siblings, 0 replies; 14+ messages in thread
From: Denis Kenzior @ 2017-01-25 16:38 UTC (permalink / raw)
  To: ofono

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

Hi Piotr,

On 01/25/2017 04:41 AM, Piotr Haber wrote:
> NCM network interface on LE910V2 modem needs to be
> configured in a specific way after connection is established.
> ---
>  doc/telit-modem.txt | 27 +++++++++++++++++++++++++++
>  1 file changed, 27 insertions(+)
>

Applied, thanks.

Regards,
-Denis


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

* Re: [PATCH 2/4] plugins: support for Telit LE910 V2 modem
  2017-01-25 16:37       ` Denis Kenzior
@ 2017-01-26  9:09         ` gluedig
  2017-01-26  9:40           ` Antoine Aubert
  2017-01-26 13:46           ` Denis Kenzior
  0 siblings, 2 replies; 14+ messages in thread
From: gluedig @ 2017-01-26  9:09 UTC (permalink / raw)
  To: ofono

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

On 01/25/2017 05:37 PM, Denis Kenzior wrote:
> On 01/25/2017 04:41 AM, Piotr Haber wrote:
>> LE910 V2 is next generation Telit LTE modem.
>> It supports 3GPP Rel. 9 LTE Cat. 4 over multiple bands.
>> Default USB composition uses PID 0x36 and
>> consists of 6 CDC-ACM serial ports and 1 CDC-NCM network adapter.
>> ---
>>  Makefile.am       |   3 +
>>  plugins/le910v2.c | 400
>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 403 insertions(+)
>>  create mode 100644 plugins/le910v2.c
>>
> 
> Can you add the LE910 changes into the HE910 driver?  The two are 99%
> the same except for HE910 supporting voice calls and LE910 using a
> different gprs_context driver.
> 
> There's no sense in maintaining a 400 line driver that is virtually
> identical to what we already have.
> 
> Regards,
> -Denis
> 

Hi,
of course you are right.

I would prepare a series of patches that renames he910 plugin to xe910 -
this is how Telit calls the whole family that consists of lots of models
form 2g (GE910) through 3/3.5g (HE910, DE910, UE910, HE910V2) to
4g(LE910, LE910V2)

Then I'll add support for LE910V2 to xe910 plugin.
How does it sound?

Piotr

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

* Re: [PATCH 2/4] plugins: support for Telit LE910 V2 modem
  2017-01-26  9:09         ` gluedig
@ 2017-01-26  9:40           ` Antoine Aubert
  2017-01-26 11:13             ` gluedig
  2017-01-26 13:46           ` Denis Kenzior
  1 sibling, 1 reply; 14+ messages in thread
From: Antoine Aubert @ 2017-01-26  9:40 UTC (permalink / raw)
  To: ofono

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

Hi,

I'v recently add support for ge910 quad,
(https://github.com/p1ng0o/ofono/commits/ovk) and I had some ugly issues.

1/ doesn't support proper atmux
(https://github.com/p1ng0o/ofono/commit/1b7f64b2faaca91ce711774a12ba79ad35cbb8ed)

Quote from support(a)telit:

"In Telit implementation module is NEVER an initiator since it is up to
controller to send the
SABM command to DLCI 0
* Module is ALWAYS a responder
* Module send the V24 frame as F9 01 EF 09 E3 05 0B 8D 9A F9 where C/R
bit is set to 1
* According to Table 1 this is a response frame
* According to above DTE don’t have to copy the status."

thread with denis,
https://lists.ofono.org/pipermail/ofono/2016-October/016590.html

2/ Ugly init process, forced me to create plugin from scratch
(https://github.com/p1ng0o/ofono/commit/6534001cd5801a30b0c237b697226ed2d02fe774)

- I have to wait QSS=2 for initialize all devices, sometimes it take
more than modem powered timeout !

- Sometime I didn't receive QSS notification at all

Quote from support(a)telit:

"just try to increase the waiting time for QSS answer (also 1 minute at
the beginning: it depends also on the number of records in the SIM
phonebook, ecc.)"

Did you face same issues ?

Antoine Aubert
a.aubert(a)overkiz.com

Le 26/01/2017 à 10:09, gluedig(a)gmail.com a écrit :
> On 01/25/2017 05:37 PM, Denis Kenzior wrote:
>> On 01/25/2017 04:41 AM, Piotr Haber wrote:
>>> LE910 V2 is next generation Telit LTE modem.
>>> It supports 3GPP Rel. 9 LTE Cat. 4 over multiple bands.
>>> Default USB composition uses PID 0x36 and
>>> consists of 6 CDC-ACM serial ports and 1 CDC-NCM network adapter.
>>> ---
>>>  Makefile.am       |   3 +
>>>  plugins/le910v2.c | 400
>>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>  2 files changed, 403 insertions(+)
>>>  create mode 100644 plugins/le910v2.c
>>>
>> Can you add the LE910 changes into the HE910 driver?  The two are 99%
>> the same except for HE910 supporting voice calls and LE910 using a
>> different gprs_context driver.
>>
>> There's no sense in maintaining a 400 line driver that is virtually
>> identical to what we already have.
>>
>> Regards,
>> -Denis
>>
> Hi,
> of course you are right.
>
> I would prepare a series of patches that renames he910 plugin to xe910 -
> this is how Telit calls the whole family that consists of lots of models
> form 2g (GE910) through 3/3.5g (HE910, DE910, UE910, HE910V2) to
> 4g(LE910, LE910V2)
>
> Then I'll add support for LE910V2 to xe910 plugin.
> How does it sound?
>
> Piotr
> _______________________________________________
> ofono mailing list
> ofono(a)ofono.org
> https://lists.ofono.org/mailman/listinfo/ofono


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

* Re: [PATCH 2/4] plugins: support for Telit LE910 V2 modem
  2017-01-26  9:40           ` Antoine Aubert
@ 2017-01-26 11:13             ` gluedig
  0 siblings, 0 replies; 14+ messages in thread
From: gluedig @ 2017-01-26 11:13 UTC (permalink / raw)
  To: ofono

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

Hi
On 01/26/2017 10:40 AM, Antoine Aubert wrote:
> Hi,
> 
> I'v recently add support for ge910 quad,
> (https://github.com/p1ng0o/ofono/commits/ovk) and I had some ugly issues.
> 
> 1/ doesn't support proper atmux
> (https://github.com/p1ng0o/ofono/commit/1b7f64b2faaca91ce711774a12ba79ad35cbb8ed)
> 
> Quote from support(a)telit:
> 
> "In Telit implementation module is NEVER an initiator since it is up to
> controller to send the
> SABM command to DLCI 0
> * Module is ALWAYS a responder
> * Module send the V24 frame as F9 01 EF 09 E3 05 0B 8D 9A F9 where C/R
> bit is set to 1
> * According to Table 1 this is a response frame
> * According to above DTE don’t have to copy the status."
> 
> thread with denis,
> https://lists.ofono.org/pipermail/ofono/2016-October/016590.html
> 
> 2/ Ugly init process, forced me to create plugin from scratch
> (https://github.com/p1ng0o/ofono/commit/6534001cd5801a30b0c237b697226ed2d02fe774)
> 
> - I have to wait QSS=2 for initialize all devices, sometimes it take
> more than modem powered timeout !
> 
> - Sometime I didn't receive QSS notification at all
> 
> Quote from support(a)telit:
> 
> "just try to increase the waiting time for QSS answer (also 1 minute at
> the beginning: it depends also on the number of records in the SIM
> phonebook, ecc.)"
> 
> Did you face same issues ?
> 
No really, LE910V2 does not need AT mux as far as i know - it uses
separate network interface as default (CDC-NCM).
I also tried PPP over modem port, worked ok, just switched aux and modem
ports for it to work ok.

And #QSS=2 returns right away.
First #QSS query returns 0 (SIM not inserted) but seconds later #QSS
notification arrives with status 1

Kind regards
Piotr


> Antoine Aubert
> a.aubert(a)overkiz.com
> 
> Le 26/01/2017 à 10:09, gluedig(a)gmail.com a écrit :
>> On 01/25/2017 05:37 PM, Denis Kenzior wrote:
>>> On 01/25/2017 04:41 AM, Piotr Haber wrote:
>>>> LE910 V2 is next generation Telit LTE modem.
>>>> It supports 3GPP Rel. 9 LTE Cat. 4 over multiple bands.
>>>> Default USB composition uses PID 0x36 and
>>>> consists of 6 CDC-ACM serial ports and 1 CDC-NCM network adapter.
>>>> ---
>>>>  Makefile.am       |   3 +
>>>>  plugins/le910v2.c | 400
>>>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>>  2 files changed, 403 insertions(+)
>>>>  create mode 100644 plugins/le910v2.c
>>>>
>>> Can you add the LE910 changes into the HE910 driver?  The two are 99%
>>> the same except for HE910 supporting voice calls and LE910 using a
>>> different gprs_context driver.
>>>
>>> There's no sense in maintaining a 400 line driver that is virtually
>>> identical to what we already have.
>>>
>>> Regards,
>>> -Denis
>>>
>> Hi,
>> of course you are right.
>>
>> I would prepare a series of patches that renames he910 plugin to xe910 -
>> this is how Telit calls the whole family that consists of lots of models
>> form 2g (GE910) through 3/3.5g (HE910, DE910, UE910, HE910V2) to
>> 4g(LE910, LE910V2)
>>
>> Then I'll add support for LE910V2 to xe910 plugin.
>> How does it sound?
>>
>> Piotr
>> _______________________________________________
>> ofono mailing list
>> ofono(a)ofono.org
>> https://lists.ofono.org/mailman/listinfo/ofono
> 


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

* Re: [PATCH 2/4] plugins: support for Telit LE910 V2 modem
  2017-01-26  9:09         ` gluedig
  2017-01-26  9:40           ` Antoine Aubert
@ 2017-01-26 13:46           ` Denis Kenzior
  1 sibling, 0 replies; 14+ messages in thread
From: Denis Kenzior @ 2017-01-26 13:46 UTC (permalink / raw)
  To: ofono

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

Hi Piotr,

> I would prepare a series of patches that renames he910 plugin to xe910 -
> this is how Telit calls the whole family that consists of lots of models
> form 2g (GE910) through 3/3.5g (HE910, DE910, UE910, HE910V2) to
> 4g(LE910, LE910V2)
>

I think you read my mind, that is exactly what I'd like to see.

> Then I'll add support for LE910V2 to xe910 plugin.
> How does it sound?
>

Sounds great.

Regards,
-Denis


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

end of thread, other threads:[~2017-01-26 13:46 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-01-24 14:21 [PATCH v2] drivers: add support for Telit LE910 V2 modem Piotr Haber
2017-01-24 18:55 ` Denis Kenzior
2017-01-25 10:41   ` [PATCH 0/4] add support for Telit LE910V2 Piotr Haber
2017-01-25 10:41     ` [PATCH 1/4] telitmodem: support for CDC-NCM network adapter Piotr Haber
2017-01-25 16:33       ` Denis Kenzior
2017-01-25 10:41     ` [PATCH 2/4] plugins: support for Telit LE910 V2 modem Piotr Haber
2017-01-25 16:37       ` Denis Kenzior
2017-01-26  9:09         ` gluedig
2017-01-26  9:40           ` Antoine Aubert
2017-01-26 11:13             ` gluedig
2017-01-26 13:46           ` Denis Kenzior
2017-01-25 10:41     ` [PATCH 3/4] doc: description of Telit LE910V2 network setup Piotr Haber
2017-01-25 16:38       ` Denis Kenzior
2017-01-25 10:41     ` [PATCH 4/4] udevng: setup of Telit LE910V2 Piotr Haber

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.