All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/7] Support for mtk modems
@ 2015-11-13 12:25 Alfonso Sanchez-Beato
  2015-11-13 12:25 ` [PATCH 1/7] gitignore: Ignore rilmodem-cs test binary Alfonso Sanchez-Beato
                   ` (6 more replies)
  0 siblings, 7 replies; 9+ messages in thread
From: Alfonso Sanchez-Beato @ 2015-11-13 12:25 UTC (permalink / raw)
  To: ofono

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

This patch series add support for MediaTek modems. The mtkmodem driver
is heavily based on rilmodem.

Alfonso Sanchez-Beato (7):
  gitignore: Ignore rilmodem-cs test binary
  include: Add flag for drivers that watch SIM state
  modem: Add flag for drivers that watch SIM state
  rilmodem: Export functions needed by mtkmodem
  mtkmodem: Add mtkmodem driver
  mtk: Plugin for mtkmodems
  build: Add mtkmodem driver and mtk plugin

 .gitignore                        |    1 +
 Makefile.am                       |   19 +
 drivers/mtkmodem/gprs.c           |  188 ++++
 drivers/mtkmodem/mtk_constants.h  |   84 ++
 drivers/mtkmodem/mtkmodem.c       |   58 ++
 drivers/mtkmodem/mtkmodem.h       |   32 +
 drivers/mtkmodem/mtkreply.c       |   98 +++
 drivers/mtkmodem/mtkreply.h       |   43 +
 drivers/mtkmodem/mtkrequest.c     |  163 ++++
 drivers/mtkmodem/mtkrequest.h     |  143 ++++
 drivers/mtkmodem/mtksettings.c    |  261 ++++++
 drivers/mtkmodem/mtksettings.h    |   43 +
 drivers/mtkmodem/mtkunsol.c       |  199 +++++
 drivers/mtkmodem/mtkunsol.h       |   59 ++
 drivers/mtkmodem/mtkutil.c        |  143 ++++
 drivers/mtkmodem/mtkutil.h        |   47 +
 drivers/mtkmodem/radio-settings.c |  193 +++++
 drivers/mtkmodem/voicecall.c      |  155 ++++
 drivers/rilmodem/radio-settings.c |   21 +-
 drivers/rilmodem/radio-settings.h |   46 +
 drivers/rilmodem/voicecall.c      |   18 +-
 drivers/rilmodem/voicecall.h      |    4 +
 include/modem.h                   |    4 +
 plugins/mtk.c                     | 1704 +++++++++++++++++++++++++++++++++++++
 src/modem.c                       |   38 +-
 25 files changed, 3735 insertions(+), 29 deletions(-)
 create mode 100644 drivers/mtkmodem/gprs.c
 create mode 100644 drivers/mtkmodem/mtk_constants.h
 create mode 100644 drivers/mtkmodem/mtkmodem.c
 create mode 100644 drivers/mtkmodem/mtkmodem.h
 create mode 100644 drivers/mtkmodem/mtkreply.c
 create mode 100644 drivers/mtkmodem/mtkreply.h
 create mode 100644 drivers/mtkmodem/mtkrequest.c
 create mode 100644 drivers/mtkmodem/mtkrequest.h
 create mode 100644 drivers/mtkmodem/mtksettings.c
 create mode 100644 drivers/mtkmodem/mtksettings.h
 create mode 100644 drivers/mtkmodem/mtkunsol.c
 create mode 100644 drivers/mtkmodem/mtkunsol.h
 create mode 100644 drivers/mtkmodem/mtkutil.c
 create mode 100644 drivers/mtkmodem/mtkutil.h
 create mode 100644 drivers/mtkmodem/radio-settings.c
 create mode 100644 drivers/mtkmodem/voicecall.c
 create mode 100644 drivers/rilmodem/radio-settings.h
 create mode 100644 plugins/mtk.c

-- 
2.5.0


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

* [PATCH 1/7] gitignore: Ignore rilmodem-cs test binary
  2015-11-13 12:25 [PATCH 0/7] Support for mtk modems Alfonso Sanchez-Beato
@ 2015-11-13 12:25 ` Alfonso Sanchez-Beato
  2015-11-13 16:06   ` Denis Kenzior
  2015-11-13 12:25 ` [PATCH 2/7] include: Add flag for drivers that watch SIM state Alfonso Sanchez-Beato
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 9+ messages in thread
From: Alfonso Sanchez-Beato @ 2015-11-13 12:25 UTC (permalink / raw)
  To: ofono

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

---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index b9c23a0..a433dad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,6 +44,7 @@ unit/test-mux
 unit/test-caif
 unit/test-stkutil
 unit/test-cdmasms
+unit/test-rilmodem-cs
 unit/test-*.log
 unit/test-*.trs
 
-- 
2.5.0


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

* [PATCH 2/7] include: Add flag for drivers that watch SIM state
  2015-11-13 12:25 [PATCH 0/7] Support for mtk modems Alfonso Sanchez-Beato
  2015-11-13 12:25 ` [PATCH 1/7] gitignore: Ignore rilmodem-cs test binary Alfonso Sanchez-Beato
@ 2015-11-13 12:25 ` Alfonso Sanchez-Beato
  2015-11-13 12:25 ` [PATCH 3/7] modem: " Alfonso Sanchez-Beato
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Alfonso Sanchez-Beato @ 2015-11-13 12:25 UTC (permalink / raw)
  To: ofono

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

Add flag for drivers that handle modems that cannot access the SIM when
in offline state. These drivers watch the SIM state to create/destroy
certain atoms.
---
 include/modem.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/include/modem.h b/include/modem.h
index e40b23e..54ce862 100644
--- a/include/modem.h
+++ b/include/modem.h
@@ -115,6 +115,10 @@ int ofono_modem_set_boolean(struct ofono_modem *modem,
 ofono_bool_t ofono_modem_get_boolean(struct ofono_modem *modem,
 					const char *key);
 
+void ofono_modem_set_driver_watches_sim(struct ofono_modem *modem,
+					ofono_bool_t value);
+ofono_bool_t ofono_modem_get_driver_watches_sim(struct ofono_modem *modem);
+
 int ofono_modem_driver_register(const struct ofono_modem_driver *);
 void ofono_modem_driver_unregister(const struct ofono_modem_driver *);
 
-- 
2.5.0


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

* [PATCH 3/7] modem: Add flag for drivers that watch SIM state
  2015-11-13 12:25 [PATCH 0/7] Support for mtk modems Alfonso Sanchez-Beato
  2015-11-13 12:25 ` [PATCH 1/7] gitignore: Ignore rilmodem-cs test binary Alfonso Sanchez-Beato
  2015-11-13 12:25 ` [PATCH 2/7] include: Add flag for drivers that watch SIM state Alfonso Sanchez-Beato
@ 2015-11-13 12:25 ` Alfonso Sanchez-Beato
  2015-11-13 12:25 ` [PATCH 4/7] rilmodem: Export functions needed by mtkmodem Alfonso Sanchez-Beato
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Alfonso Sanchez-Beato @ 2015-11-13 12:25 UTC (permalink / raw)
  To: ofono

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

Add flag for drivers that handle modems that cannot access the SIM when
in offline state. These drivers watch the SIM state to create/destroy
certain atoms.
---
 src/modem.c | 38 +++++++++++++++++++++++++++-----------
 1 file changed, 27 insertions(+), 11 deletions(-)

diff --git a/src/modem.c b/src/modem.c
index 01b0e35..dec398c 100644
--- a/src/modem.c
+++ b/src/modem.c
@@ -87,6 +87,7 @@ struct ofono_modem {
 	void			*driver_data;
 	char			*driver_type;
 	char			*name;
+	ofono_bool_t		driver_watches_sim;
 };
 
 struct ofono_devinfo {
@@ -708,22 +709,26 @@ static void sim_state_watch(enum ofono_sim_state new_state, void *user)
 	case OFONO_SIM_STATE_RESETTING:
 		break;
 	case OFONO_SIM_STATE_LOCKED_OUT:
-		modem_change_state(modem, MODEM_STATE_PRE_SIM);
+		if (modem->driver_watches_sim == FALSE)
+			modem_change_state(modem, MODEM_STATE_PRE_SIM);
 		break;
 	case OFONO_SIM_STATE_READY:
-		modem_change_state(modem, MODEM_STATE_OFFLINE);
+		/* Avoid state regressions */
+		if (modem->modem_state != MODEM_STATE_ONLINE) {
+			modem_change_state(modem, MODEM_STATE_OFFLINE);
 
-		/* Modem is always online, proceed to online state. */
-		if (modem_is_always_online(modem) == TRUE)
-			set_online(modem, TRUE);
+			/* Modem is always online, proceed to online state. */
+			if (modem_is_always_online(modem) == TRUE)
+				set_online(modem, TRUE);
 
-		if (modem->online == TRUE)
-			modem_change_state(modem, MODEM_STATE_ONLINE);
-		else if (modem->get_online)
-			modem->driver->set_online(modem, 1, common_online_cb,
-							modem);
+			if (modem->online == TRUE)
+				modem_change_state(modem, MODEM_STATE_ONLINE);
+			else if (modem->get_online)
+				modem->driver->set_online(modem, 1,
+						common_online_cb, modem);
 
-		modem->get_online = FALSE;
+			modem->get_online = FALSE;
+		}
 
 		break;
 	}
@@ -1809,6 +1814,17 @@ void ofono_modem_set_driver(struct ofono_modem *modem, const char *type)
 	modem->driver_type = g_strdup(type);
 }
 
+void ofono_modem_set_driver_watches_sim(struct ofono_modem *modem,
+					ofono_bool_t value)
+{
+	modem->driver_watches_sim = value;
+}
+
+ofono_bool_t ofono_modem_get_driver_watches_sim(struct ofono_modem *modem)
+{
+	return modem->driver_watches_sim;
+}
+
 struct ofono_modem *ofono_modem_create(const char *name, const char *type)
 {
 	struct ofono_modem *modem;
-- 
2.5.0


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

* [PATCH 4/7] rilmodem: Export functions needed by mtkmodem
  2015-11-13 12:25 [PATCH 0/7] Support for mtk modems Alfonso Sanchez-Beato
                   ` (2 preceding siblings ...)
  2015-11-13 12:25 ` [PATCH 3/7] modem: " Alfonso Sanchez-Beato
@ 2015-11-13 12:25 ` Alfonso Sanchez-Beato
  2015-11-13 12:25 ` [PATCH 5/7] mtkmodem: Add mtkmodem driver Alfonso Sanchez-Beato
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Alfonso Sanchez-Beato @ 2015-11-13 12:25 UTC (permalink / raw)
  To: ofono

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

---
 drivers/rilmodem/radio-settings.c | 21 +++++++-----------
 drivers/rilmodem/radio-settings.h | 46 +++++++++++++++++++++++++++++++++++++++
 drivers/rilmodem/voicecall.c      | 18 ++++++++++-----
 drivers/rilmodem/voicecall.h      |  4 ++++
 4 files changed, 71 insertions(+), 18 deletions(-)
 create mode 100644 drivers/rilmodem/radio-settings.h

diff --git a/drivers/rilmodem/radio-settings.c b/drivers/rilmodem/radio-settings.c
index 90b49c6..5b47089 100644
--- a/drivers/rilmodem/radio-settings.c
+++ b/drivers/rilmodem/radio-settings.c
@@ -40,6 +40,7 @@
 #include "gril.h"
 
 #include "rilmodem.h"
+#include "radio-settings.h"
 
 /* Preferred network types */
 #define PREF_NET_TYPE_GSM_WCDMA 0
@@ -64,12 +65,6 @@
 #define MTK_PREF_NET_TYPE_LTE_GSM_TYPE (MTK_PREF_NET_TYPE_BASE + 5)
 #define MTK_PREF_NET_TYPE_LTE_GSM_MMDC_TYPE (MTK_PREF_NET_TYPE_BASE + 6)
 
-struct radio_data {
-	GRil *ril;
-	gboolean fast_dormancy;
-	gboolean pending_fd;
-};
-
 static void ril_set_rat_cb(struct ril_msg *message, gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
@@ -86,7 +81,7 @@ static void ril_set_rat_cb(struct ril_msg *message, gpointer user_data)
 	}
 }
 
-static void ril_set_rat_mode(struct ofono_radio_settings *rs,
+void ril_set_rat_mode(struct ofono_radio_settings *rs,
 			enum ofono_radio_access_mode mode,
 			ofono_radio_settings_rat_mode_set_cb_t cb,
 			void *data)
@@ -205,7 +200,7 @@ error:
 	CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
 }
 
-static void ril_query_rat_mode(struct ofono_radio_settings *rs,
+void ril_query_rat_mode(struct ofono_radio_settings *rs,
 			ofono_radio_settings_rat_mode_query_cb_t cb,
 			void *data)
 {
@@ -220,7 +215,7 @@ static void ril_query_rat_mode(struct ofono_radio_settings *rs,
 	CALLBACK_WITH_FAILURE(cb, -1, data);
 }
 
-static void ril_query_fast_dormancy(struct ofono_radio_settings *rs,
+void ril_query_fast_dormancy(struct ofono_radio_settings *rs,
 			ofono_radio_settings_fast_dormancy_query_cb_t cb,
 			void *data)
 {
@@ -247,7 +242,7 @@ static void ril_display_state_cb(struct ril_msg *message, gpointer user_data)
 	}
 }
 
-static void ril_set_fast_dormancy(struct ofono_radio_settings *rs,
+void ril_set_fast_dormancy(struct ofono_radio_settings *rs,
 				ofono_bool_t enable,
 				ofono_radio_settings_fast_dormancy_set_cb_t cb,
 				void *data)
@@ -272,7 +267,7 @@ static void ril_set_fast_dormancy(struct ofono_radio_settings *rs,
 	CALLBACK_WITH_FAILURE(cb, data);
 }
 
-static void ril_query_available_rats(struct ofono_radio_settings *rs,
+void ril_query_available_rats(struct ofono_radio_settings *rs,
 			ofono_radio_settings_available_rats_query_cb_t cb,
 			void *data)
 {
@@ -288,7 +283,7 @@ static void ril_query_available_rats(struct ofono_radio_settings *rs,
 	CALLBACK_WITH_SUCCESS(cb, available_rats, data);
 }
 
-static void ril_delayed_register(const struct ofono_error *error,
+void ril_delayed_register(const struct ofono_error *error,
 							void *user_data)
 {
 	struct ofono_radio_settings *rs = user_data;
@@ -314,7 +309,7 @@ static int ril_radio_settings_probe(struct ofono_radio_settings *rs,
 	return 0;
 }
 
-static void ril_radio_settings_remove(struct ofono_radio_settings *rs)
+void ril_radio_settings_remove(struct ofono_radio_settings *rs)
 {
 	struct radio_data *rd = ofono_radio_settings_get_data(rs);
 	ofono_radio_settings_set_data(rs, NULL);
diff --git a/drivers/rilmodem/radio-settings.h b/drivers/rilmodem/radio-settings.h
new file mode 100644
index 0000000..6c61551
--- /dev/null
+++ b/drivers/rilmodem/radio-settings.h
@@ -0,0 +1,46 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2014  Canonical Ltd.
+ *
+ *  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
+ *
+ */
+
+struct radio_data {
+	GRil *ril;
+	gboolean fast_dormancy;
+	gboolean pending_fd;
+};
+
+void ril_delayed_register(const struct ofono_error *error, void *user_data);
+void ril_radio_settings_remove(struct ofono_radio_settings *rs);
+void ril_query_rat_mode(struct ofono_radio_settings *rs,
+			ofono_radio_settings_rat_mode_query_cb_t cb,
+			void *data);
+void ril_set_rat_mode(struct ofono_radio_settings *rs,
+			enum ofono_radio_access_mode mode,
+			ofono_radio_settings_rat_mode_set_cb_t cb,
+			void *data);
+void ril_query_fast_dormancy(struct ofono_radio_settings *rs,
+			ofono_radio_settings_fast_dormancy_query_cb_t cb,
+			void *data);
+void ril_set_fast_dormancy(struct ofono_radio_settings *rs,
+				ofono_bool_t enable,
+				ofono_radio_settings_fast_dormancy_set_cb_t cb,
+				void *data);
+void ril_query_available_rats(struct ofono_radio_settings *rs,
+			ofono_radio_settings_available_rats_query_cb_t cb,
+			void *data);
diff --git a/drivers/rilmodem/voicecall.c b/drivers/rilmodem/voicecall.c
index 45e8ffb..c28274f 100644
--- a/drivers/rilmodem/voicecall.c
+++ b/drivers/rilmodem/voicecall.c
@@ -752,12 +752,11 @@ static gboolean ril_delayed_register(gpointer user_data)
 	return FALSE;
 }
 
-int ril_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
-			void *data)
+void ril_voicecall_start(GRil *ril,
+				struct ofono_voicecall *vc,
+				unsigned int vendor,
+				struct ril_voicecall_data *vd)
 {
-	GRil *ril = data;
-	struct ril_voicecall_data *vd = g_new0(struct ril_voicecall_data, 1);
-
 	vd->ril = g_ril_clone(ril);
 	vd->vendor = vendor;
 	vd->cb = NULL;
@@ -768,6 +767,15 @@ int ril_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
 	ofono_voicecall_set_data(vc, vd);
 
 	g_idle_add(ril_delayed_register, vc);
+}
+
+int ril_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
+			void *data)
+{
+	GRil *ril = data;
+	struct ril_voicecall_data *vd = g_new0(struct ril_voicecall_data, 1);
+
+	ril_voicecall_start(ril, vc, vendor, vd);
 
 	return 0;
 }
diff --git a/drivers/rilmodem/voicecall.h b/drivers/rilmodem/voicecall.h
index 31e120e..8888450 100644
--- a/drivers/rilmodem/voicecall.h
+++ b/drivers/rilmodem/voicecall.h
@@ -62,5 +62,9 @@ void ril_set_udub(struct ofono_voicecall *vc,
 void ril_release_all_active(struct ofono_voicecall *vc,
 				ofono_voicecall_cb_t cb, void *data);
 
+void ril_voicecall_start(GRil *ril,
+				struct ofono_voicecall *vc,
+				unsigned int vendor,
+				struct ril_voicecall_data *vd);
 void ril_call_state_notify(struct ril_msg *message, gpointer user_data);
 gboolean ril_poll_clcc(gpointer user_data);
-- 
2.5.0


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

* [PATCH 5/7] mtkmodem: Add mtkmodem driver
  2015-11-13 12:25 [PATCH 0/7] Support for mtk modems Alfonso Sanchez-Beato
                   ` (3 preceding siblings ...)
  2015-11-13 12:25 ` [PATCH 4/7] rilmodem: Export functions needed by mtkmodem Alfonso Sanchez-Beato
@ 2015-11-13 12:25 ` Alfonso Sanchez-Beato
  2015-11-13 12:25 ` [PATCH 6/7] mtk: Plugin for mtkmodems Alfonso Sanchez-Beato
  2015-11-13 12:25 ` [PATCH 7/7] build: Add mtkmodem driver and mtk plugin Alfonso Sanchez-Beato
  6 siblings, 0 replies; 9+ messages in thread
From: Alfonso Sanchez-Beato @ 2015-11-13 12:25 UTC (permalink / raw)
  To: ofono

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

mtkmodem handles modems MediaTek modems and is based on rilmodem driver.
---
 drivers/mtkmodem/gprs.c           | 188 +++++++++++++++++++++++++++
 drivers/mtkmodem/mtk_constants.h  |  84 ++++++++++++
 drivers/mtkmodem/mtkmodem.c       |  58 +++++++++
 drivers/mtkmodem/mtkmodem.h       |  32 +++++
 drivers/mtkmodem/mtkreply.c       |  98 ++++++++++++++
 drivers/mtkmodem/mtkreply.h       |  43 +++++++
 drivers/mtkmodem/mtkrequest.c     | 163 ++++++++++++++++++++++++
 drivers/mtkmodem/mtkrequest.h     | 143 +++++++++++++++++++++
 drivers/mtkmodem/mtksettings.c    | 261 ++++++++++++++++++++++++++++++++++++++
 drivers/mtkmodem/mtksettings.h    |  43 +++++++
 drivers/mtkmodem/mtkunsol.c       | 199 +++++++++++++++++++++++++++++
 drivers/mtkmodem/mtkunsol.h       |  59 +++++++++
 drivers/mtkmodem/mtkutil.c        | 143 +++++++++++++++++++++
 drivers/mtkmodem/mtkutil.h        |  47 +++++++
 drivers/mtkmodem/radio-settings.c | 193 ++++++++++++++++++++++++++++
 drivers/mtkmodem/voicecall.c      | 155 ++++++++++++++++++++++
 16 files changed, 1909 insertions(+)
 create mode 100644 drivers/mtkmodem/gprs.c
 create mode 100644 drivers/mtkmodem/mtk_constants.h
 create mode 100644 drivers/mtkmodem/mtkmodem.c
 create mode 100644 drivers/mtkmodem/mtkmodem.h
 create mode 100644 drivers/mtkmodem/mtkreply.c
 create mode 100644 drivers/mtkmodem/mtkreply.h
 create mode 100644 drivers/mtkmodem/mtkrequest.c
 create mode 100644 drivers/mtkmodem/mtkrequest.h
 create mode 100644 drivers/mtkmodem/mtksettings.c
 create mode 100644 drivers/mtkmodem/mtksettings.h
 create mode 100644 drivers/mtkmodem/mtkunsol.c
 create mode 100644 drivers/mtkmodem/mtkunsol.h
 create mode 100644 drivers/mtkmodem/mtkutil.c
 create mode 100644 drivers/mtkmodem/mtkutil.h
 create mode 100644 drivers/mtkmodem/radio-settings.c
 create mode 100644 drivers/mtkmodem/voicecall.c

diff --git a/drivers/mtkmodem/gprs.c b/drivers/mtkmodem/gprs.c
new file mode 100644
index 0000000..073c3f1
--- /dev/null
+++ b/drivers/mtkmodem/gprs.c
@@ -0,0 +1,188 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010  ST-Ericsson AB.
+ *  Copyright (C) 2013-2014  Canonical Ltd.
+ *  Copyright (C) 2013 Jolla Ltd.
+ *
+ *  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 <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs.h>
+#include <ofono/types.h>
+
+#include "common.h"
+
+#include "mtkutil.h"
+#include "mtkmodem.h"
+#include "mtk_constants.h"
+#include "mtkrequest.h"
+#include "drivers/rilmodem/rilutil.h"
+#include "drivers/rilmodem/gprs.h"
+
+/* Time between get data status retries */
+#define GET_STATUS_TIMER_MS 5000
+
+/*
+ * This module is the ofono_gprs_driver implementation for mtkmodem. Most of the
+ * implementation can be found in the rilmodem gprs atom. The main reason for
+ * creating a new atom is the need to handle specific MTK requests that are
+ * needed to set-up the data call.
+ *
+ * Notes:
+ *
+ * 1. ofono_gprs_suspend/resume() are not used by this module, as
+ *    the concept of suspended GPRS is not exposed by RILD.
+ */
+
+struct gprs_attach_data {
+	struct ril_gprs_data *gd;
+	gboolean set_attached;
+};
+
+static void mtk_gprs_set_connect_type_cb(struct ril_msg *message,
+						gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	ofono_gprs_cb_t cb = cbd->cb;
+	struct gprs_attach_data *attach_data = cbd->user;
+	struct ril_gprs_data *gd = attach_data->gd;
+
+	if (message->error == RIL_E_SUCCESS) {
+		g_ril_print_response_no_args(gd->ril, message);
+
+		gd->ofono_attached = attach_data->set_attached;
+		mtk_set_attach_state(gd->modem, gd->ofono_attached);
+
+		CALLBACK_WITH_SUCCESS(cb, cbd->data);
+	} else {
+		ofono_error("%s: RIL error %s", __func__,
+				ril_error_to_string(message->error));
+		CALLBACK_WITH_FAILURE(cb, cbd->data);
+	}
+
+	g_free(attach_data);
+}
+
+static void mtk_gprs_set_attached(struct ofono_gprs *gprs, int attached,
+					ofono_gprs_cb_t cb, void *data)
+{
+	struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
+	struct cb_data *cbd;
+	struct parcel rilp;
+	struct gprs_attach_data *attach_data =
+		g_try_new0(struct gprs_attach_data, 1);
+
+	if (attach_data == NULL) {
+		ofono_error("%s: out of memory", __func__);
+		return;
+	}
+
+	DBG("attached: %d", attached);
+
+	attach_data->gd = gd;
+	attach_data->set_attached = attached;
+
+	cbd = cb_data_new(cb, data, attach_data);
+
+	/* MTK controls attachment with this request, as opposed to rilmodem */
+
+	g_mtk_request_set_gprs_connect_type(gd->ril, attached, &rilp);
+
+	if (g_ril_send(gd->ril, MTK_RIL_REQUEST_SET_GPRS_CONNECT_TYPE, &rilp,
+			mtk_gprs_set_connect_type_cb, cbd, g_free) == 0) {
+		ofono_error("%s: send failed", __func__);
+		g_free(cbd);
+		CALLBACK_WITH_FAILURE(cb, data);
+	}
+}
+
+static void detach_event(struct ril_msg *message, gpointer user_data)
+{
+	struct ofono_gprs *gprs = user_data;
+	struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
+
+	g_ril_print_unsol_no_args(gd->ril, message);
+
+	mtk_detach_received(gd->modem);
+}
+
+static int mtk_gprs_probe(struct ofono_gprs *gprs,
+				unsigned int vendor, void *data)
+{
+	struct ril_gprs_driver_data *driver_data = data;
+	struct ril_gprs_data *gd;
+
+	gd = g_try_new0(struct ril_gprs_data, 1);
+	if (gd == NULL)
+		return -ENOMEM;
+
+	ril_gprs_start(driver_data, gprs, gd);
+
+	/*
+	 * In MTK the event emitted when the gprs state changes is different
+	 * from the one in AOSP ril. Overwrite the one set in parent.
+	 */
+	gd->state_changed_unsol =
+				MTK_RIL_UNSOL_RESPONSE_PS_NETWORK_STATE_CHANGED;
+
+	g_ril_register(gd->ril, MTK_RIL_UNSOL_GPRS_DETACH, detach_event, gprs);
+
+	return 0;
+}
+
+static void mtk_gprs_remove(struct ofono_gprs *gprs)
+{
+	struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
+
+	gd->ofono_attached = FALSE;
+	mtk_set_attach_state(gd->modem, gd->ofono_attached);
+
+	ril_gprs_remove(gprs);
+}
+
+static struct ofono_gprs_driver driver = {
+	.name			= MTKMODEM,
+	.probe			= mtk_gprs_probe,
+	.remove			= mtk_gprs_remove,
+	.set_attached		= mtk_gprs_set_attached,
+	.attached_status	= ril_gprs_registration_status,
+};
+
+void mtk_gprs_init(void)
+{
+	ofono_gprs_driver_register(&driver);
+}
+
+void mtk_gprs_exit(void)
+{
+	ofono_gprs_driver_unregister(&driver);
+}
diff --git a/drivers/mtkmodem/mtk_constants.h b/drivers/mtkmodem/mtk_constants.h
new file mode 100644
index 0000000..bd9891b
--- /dev/null
+++ b/drivers/mtkmodem/mtk_constants.h
@@ -0,0 +1,84 @@
+/*
+ *
+ *  RIL constants for MTK modem
+ *
+ *  Copyright (C) 2014 Canonical Ltd.
+ *
+ *  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
+ *
+ */
+
+#ifndef MTK_CONSTANTS_H
+#define MTK_CONSTANTS_H
+
+/* RIL Request Messages */
+#define MTK_RIL_REQUEST_RADIO_POWEROFF 2011
+#define MTK_RIL_REQUEST_DUAL_SIM_MODE_SWITCH 2012
+#define MTK_RIL_REQUEST_SET_GPRS_CONNECT_TYPE 2016
+#define MTK_RIL_REQUEST_SET_GPRS_TRANSFER_TYPE 2017
+#define MTK_RIL_REQUEST_RADIO_POWERON 2033
+#define MTK_RIL_REQUEST_SET_CALL_INDICATION 2036
+#define MTK_RIL_REQUEST_GET_3G_CAPABILITY 2038
+#define MTK_RIL_REQUEST_SET_3G_CAPABILITY 2039
+#define MTK_RIL_REQUEST_SET_TRM 2053
+#define MTK_RIL_REQUEST_SET_FD_MODE 2073
+#define MTK_RIL_REQUEST_RESUME_REGISTRATION 2077
+#define MTK_RIL_REQUEST_STORE_MODEM_TYPE 2078
+#define MTK_RIL_REQUEST_QUERY_MODEM_TYPE 2079
+
+/* RIL Unsolicited Messages */
+#define MTK_RIL_UNSOL_MTK_BASE 3000
+
+#define MTK_RIL_UNSOL_NEIGHBORING_CELL_INFO (MTK_RIL_UNSOL_MTK_BASE + 0)
+#define MTK_RIL_UNSOL_NETWORK_INFO (MTK_RIL_UNSOL_MTK_BASE + 1)
+#define MTK_RIL_UNSOL_CALL_FORWARDING (MTK_RIL_UNSOL_MTK_BASE + 2)
+#define MTK_RIL_UNSOL_CRSS_NOTIFICATION (MTK_RIL_UNSOL_MTK_BASE + 3)
+#define MTK_RIL_UNSOL_CALL_PROGRESS_INFO (MTK_RIL_UNSOL_MTK_BASE + 4)
+#define MTK_RIL_UNSOL_PHB_READY_NOTIFICATION (MTK_RIL_UNSOL_MTK_BASE + 5)
+#define MTK_RIL_UNSOL_SPEECH_INFO (MTK_RIL_UNSOL_MTK_BASE + 6)
+#define MTK_RIL_UNSOL_SIM_INSERTED_STATUS (MTK_RIL_UNSOL_MTK_BASE + 7)
+#define MTK_RIL_UNSOL_RADIO_TEMPORARILY_UNAVAILABLE (MTK_RIL_UNSOL_MTK_BASE + 8)
+#define MTK_RIL_UNSOL_ME_SMS_STORAGE_FULL (MTK_RIL_UNSOL_MTK_BASE + 9)
+#define MTK_RIL_UNSOL_SMS_READY_NOTIFICATION (MTK_RIL_UNSOL_MTK_BASE + 10)
+#define MTK_RIL_UNSOL_SCRI_RESULT (MTK_RIL_UNSOL_MTK_BASE + 11)
+#define MTK_RIL_UNSOL_VT_STATUS_INFO (MTK_RIL_UNSOL_MTK_BASE + 12)
+#define MTK_RIL_UNSOL_VT_RING_INFO (MTK_RIL_UNSOL_MTK_BASE + 13)
+#define MTK_RIL_UNSOL_INCOMING_CALL_INDICATION (MTK_RIL_UNSOL_MTK_BASE + 14)
+#define MTK_RIL_UNSOL_SIM_MISSING (MTK_RIL_UNSOL_MTK_BASE + 15)
+#define MTK_RIL_UNSOL_GPRS_DETACH (MTK_RIL_UNSOL_MTK_BASE + 16)
+#define MTK_RIL_UNSOL_ATCI_RESPONSE (MTK_RIL_UNSOL_MTK_BASE + 17)
+#define MTK_RIL_UNSOL_SIM_RECOVERY (MTK_RIL_UNSOL_MTK_BASE + 18)
+#define MTK_RIL_UNSOL_VIRTUAL_SIM_ON (MTK_RIL_UNSOL_MTK_BASE + 19)
+#define MTK_RIL_UNSOL_VIRTUAL_SIM_OFF (MTK_RIL_UNSOL_MTK_BASE + 20)
+#define MTK_RIL_UNSOL_INVALID_SIM (MTK_RIL_UNSOL_MTK_BASE + 21)
+#define MTK_RIL_UNSOL_RESPONSE_PS_NETWORK_STATE_CHANGED \
+						(MTK_RIL_UNSOL_MTK_BASE + 22)
+#define MTK_RIL_UNSOL_RESPONSE_ACMT (MTK_RIL_UNSOL_MTK_BASE + 23)
+#define MTK_RIL_UNSOL_EF_CSP_PLMN_MODE_BIT (MTK_RIL_UNSOL_MTK_BASE + 24)
+#define MTK_RIL_UNSOL_IMEI_LOCK (MTK_RIL_UNSOL_MTK_BASE + 25)
+#define MTK_RIL_UNSOL_RESPONSE_MMRR_STATUS_CHANGED (MTK_RIL_UNSOL_MTK_BASE + 26)
+#define MTK_RIL_UNSOL_SIM_PLUG_OUT (MTK_RIL_UNSOL_MTK_BASE + 27)
+#define MTK_RIL_UNSOL_SIM_PLUG_IN (MTK_RIL_UNSOL_MTK_BASE + 28)
+#define MTK_RIL_UNSOL_RESPONSE_ETWS_NOTIFICATION (MTK_RIL_UNSOL_MTK_BASE + 29)
+#define MTK_RIL_UNSOL_RESPONSE_PLMN_CHANGED (MTK_RIL_UNSOL_MTK_BASE + 30)
+#define MTK_RIL_UNSOL_RESPONSE_REGISTRATION_SUSPENDED \
+						(MTK_RIL_UNSOL_MTK_BASE + 31)
+#define MTK_RIL_UNSOL_STK_EVDL_CALL (MTK_RIL_UNSOL_MTK_BASE + 32)
+#define MTK_RIL_UNSOL_DATA_PACKETS_FLUSH (MTK_RIL_UNSOL_MTK_BASE + 33)
+#define MTK_RIL_UNSOL_CIPHER_INDICATION (MTK_RIL_UNSOL_MTK_BASE + 34)
+#define MTK_RIL_UNSOL_FEMTOCELL_INFO (MTK_RIL_UNSOL_MTK_BASE + 35)
+#define MTK_RIL_UNSOL_CNAP (MTK_RIL_UNSOL_MTK_BASE + 36)
+#define MTK_RIL_UNSOL_RAC_UPDATE (MTK_RIL_UNSOL_MTK_BASE + 37)
+
+#endif /* MTK_CONSTANTS_H */
diff --git a/drivers/mtkmodem/mtkmodem.c b/drivers/mtkmodem/mtkmodem.c
new file mode 100644
index 0000000..91cd8ab
--- /dev/null
+++ b/drivers/mtkmodem/mtkmodem.c
@@ -0,0 +1,58 @@
+/*
+ *
+ *  oFono - Open Source Telephony - RIL Modem Support
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2014  Canonical, Ltd. 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 <glib.h>
+#include <gril.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/types.h>
+
+#include "mtkmodem.h"
+
+static int mtkmodem_init(void)
+{
+	DBG("");
+
+	mtk_voicecall_init();
+	mtk_gprs_init();
+	mtk_radio_settings_init();
+
+	return 0;
+}
+
+static void mtkmodem_exit(void)
+{
+	DBG("");
+
+	mtk_voicecall_exit();
+	mtk_gprs_exit();
+	mtk_radio_settings_exit();
+}
+
+OFONO_PLUGIN_DEFINE(mtkmodem, "MTK modem driver", VERSION,
+		OFONO_PLUGIN_PRIORITY_DEFAULT, mtkmodem_init, mtkmodem_exit)
diff --git a/drivers/mtkmodem/mtkmodem.h b/drivers/mtkmodem/mtkmodem.h
new file mode 100644
index 0000000..037a57c
--- /dev/null
+++ b/drivers/mtkmodem/mtkmodem.h
@@ -0,0 +1,32 @@
+/*
+ *
+ *  oFono - Open Source Telephony - RIL Modem Support
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2014  Canonical Ltd.
+ *
+ *  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
+ *
+ */
+
+#define MTKMODEM "mtkmodem"
+
+extern void mtk_voicecall_init(void);
+extern void mtk_voicecall_exit(void);
+
+extern void mtk_gprs_init(void);
+extern void mtk_gprs_exit(void);
+
+extern void mtk_radio_settings_init(void);
+extern void mtk_radio_settings_exit(void);
diff --git a/drivers/mtkmodem/mtkreply.c b/drivers/mtkmodem/mtkreply.c
new file mode 100644
index 0000000..9c7625e
--- /dev/null
+++ b/drivers/mtkmodem/mtkreply.c
@@ -0,0 +1,98 @@
+/*
+ *
+ *  MTK driver for ofono/rilmodem
+ *
+ *  Copyright (C) 2014  Canonical Ltd.
+ *
+ *  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 <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+
+#include "mtkreply.h"
+
+int g_mtk_reply_parse_get_3g_capability(GRil *gril,
+					const struct ril_msg *message)
+{
+	struct parcel rilp;
+	int slot_3g, numint;
+
+	g_ril_init_parcel(message, &rilp);
+
+	numint = parcel_r_int32(&rilp);
+	if (numint < 1) {
+		ofono_error("%s: wrong format", __func__);
+		goto error;
+	}
+
+	/*
+	 * Bitmap with 3g capability per slot. Reply is the same regardless of
+	 * the socket used to sent the request.
+	 */
+	slot_3g = parcel_r_int32(&rilp);
+
+	if (rilp.malformed) {
+		ofono_error("%s: malformed parcel", __func__);
+		goto error;
+	}
+
+	g_ril_append_print_buf(gril, "{%d}", slot_3g);
+	g_ril_print_response(gril, message);
+
+	/* Do it zero-based */
+	return slot_3g - 1;
+
+error:
+	return -1;
+}
+
+int g_mtk_reply_parse_query_modem_type(GRil *gril,
+					const struct ril_msg *message)
+{
+	struct parcel rilp;
+	int numint, type;
+
+	g_ril_init_parcel(message, &rilp);
+
+	numint = parcel_r_int32(&rilp);
+	if (numint != 1) {
+		ofono_error("%s Wrong format", __func__);
+		goto error;
+	}
+
+	type = parcel_r_int32(&rilp);
+
+	if (rilp.malformed) {
+		ofono_error("%s: malformed parcel", __func__);
+		goto error;
+	}
+
+	g_ril_append_print_buf(gril, "{%d}", type);
+	g_ril_print_response(gril, message);
+
+	return type;
+
+error:
+	return -1;
+
+}
diff --git a/drivers/mtkmodem/mtkreply.h b/drivers/mtkmodem/mtkreply.h
new file mode 100644
index 0000000..7a616b6
--- /dev/null
+++ b/drivers/mtkmodem/mtkreply.h
@@ -0,0 +1,43 @@
+/*
+ *
+ *  MTK driver for ofono/rilmodem
+ *
+ *  Copyright (C) 2014  Canonical Ltd.
+ *
+ *  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
+ *
+ */
+
+#ifndef MTKREPLY_H
+#define MTKREPLY_H
+
+#include <ofono/types.h>
+
+#include "gril.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int g_mtk_reply_parse_get_3g_capability(GRil *gril,
+					const struct ril_msg *message);
+
+int g_mtk_reply_parse_query_modem_type(GRil *gril,
+					const struct ril_msg *message);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MTKREPLY_H */
diff --git a/drivers/mtkmodem/mtkrequest.c b/drivers/mtkmodem/mtkrequest.c
new file mode 100644
index 0000000..4c63df3
--- /dev/null
+++ b/drivers/mtkmodem/mtkrequest.c
@@ -0,0 +1,163 @@
+/*
+ *
+ *  MTK driver for ofono/rilmodem
+ *
+ *  Copyright (C) 2014  Canonical Ltd.
+ *
+ *  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 <stdio.h>
+#include <ctype.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs-context.h>
+
+#include "mtkrequest.h"
+#include "simutil.h"
+#include "util.h"
+#include "common.h"
+
+void g_mtk_request_dual_sim_mode_switch(GRil *gril,
+					int mode,
+					struct parcel *rilp)
+{
+	parcel_init(rilp);
+	parcel_w_int32(rilp, 1);
+	parcel_w_int32(rilp, mode);
+
+	g_ril_append_print_buf(gril, "(%d)", mode);
+}
+
+void g_mtk_request_set_gprs_connect_type(GRil *gril,
+						int always,
+						struct parcel *rilp)
+{
+	parcel_init(rilp);
+	parcel_w_int32(rilp, 1);
+	parcel_w_int32(rilp, always);
+
+	g_ril_append_print_buf(gril, "(%d)", always);
+};
+
+void g_mtk_request_set_gprs_transfer_type(GRil *gril,
+						int callprefer,
+						struct parcel *rilp)
+{
+	parcel_init(rilp);
+	parcel_w_int32(rilp, 1);
+	parcel_w_int32(rilp, callprefer);
+
+	g_ril_append_print_buf(gril, "(%d)", callprefer);
+}
+
+void g_mtk_request_set_call_indication(GRil *gril, int mode, int call_id,
+					int seq_number, struct parcel *rilp)
+{
+	parcel_init(rilp);
+	parcel_w_int32(rilp, 3);
+	/* What the parameters set is unknown */
+	parcel_w_int32(rilp, mode);
+	parcel_w_int32(rilp, call_id);
+	parcel_w_int32(rilp, seq_number);
+
+	g_ril_append_print_buf(gril, "(%d,%d,%d)", mode, call_id, seq_number);
+}
+
+void g_mtk_request_set_fd_mode(GRil *gril, int mode, int param1,
+					int param2, struct parcel *rilp)
+{
+	int num_args;
+
+	switch (mode) {
+	case MTK_FD_MODE_DISABLE:
+	case MTK_FD_MODE_ENABLE:
+		num_args = 1;
+		break;
+	case MTK_FD_MODE_SET_TIMER:
+		num_args = 3;
+		break;
+	case MTK_FD_MODE_SCREEN_STATUS:
+		num_args = 2;
+		break;
+	default:
+		ofono_error("%s: mode %d is wrong", __func__, mode);
+		return;
+	}
+
+	parcel_init(rilp);
+	parcel_w_int32(rilp, num_args);
+	parcel_w_int32(rilp, mode);
+
+	g_ril_append_print_buf(gril, "(%d,%d", num_args, mode);
+
+	if (mode == MTK_FD_MODE_SCREEN_STATUS) {
+		parcel_w_int32(rilp, param1);
+		g_ril_append_print_buf(gril, "%s,%d)", print_buf, param1);
+	} else if (mode == MTK_FD_MODE_SET_TIMER) {
+		parcel_w_int32(rilp, param1);
+		parcel_w_int32(rilp, param2);
+		g_ril_append_print_buf(gril, "%s,%d,%d)", print_buf,
+					param1, param2);
+	} else {
+		g_ril_append_print_buf(gril, "%s)", print_buf);
+	}
+}
+
+void g_mtk_request_set_3g_capability(GRil *gril, struct parcel *rilp)
+{
+	int mode = g_ril_get_slot(gril) + 1;
+
+	parcel_init(rilp);
+	parcel_w_int32(rilp, 1);
+	parcel_w_int32(rilp, mode);
+
+	g_ril_append_print_buf(gril, "(%d)", mode);
+}
+
+void g_mtk_request_store_modem_type(GRil *gril, int mode, struct parcel *rilp)
+{
+	parcel_init(rilp);
+	parcel_w_int32(rilp, 1);
+	parcel_w_int32(rilp, mode);
+
+	g_ril_append_print_buf(gril, "(%d)", mode);
+}
+
+void g_mtk_request_set_trm(GRil *gril, int trm, struct parcel *rilp)
+{
+	parcel_init(rilp);
+	parcel_w_int32(rilp, 1);
+	parcel_w_int32(rilp, trm);
+
+	g_ril_append_print_buf(gril, "(%d)", trm);
+}
+
+void g_mtk_request_resume_registration(GRil *gril, int session_id,
+					struct parcel *rilp)
+{
+	parcel_init(rilp);
+	parcel_w_int32(rilp, 1);
+	parcel_w_int32(rilp, session_id);
+
+	g_ril_append_print_buf(gril, "(%d)", session_id);
+}
diff --git a/drivers/mtkmodem/mtkrequest.h b/drivers/mtkmodem/mtkrequest.h
new file mode 100644
index 0000000..a393124
--- /dev/null
+++ b/drivers/mtkmodem/mtkrequest.h
@@ -0,0 +1,143 @@
+/*
+ *
+ *  MTK driver for ofono/rilmodem
+ *
+ *  Copyright (C) 2014  Canonical Ltd.
+ *
+ *  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
+ *
+ */
+
+#ifndef MTKREQUEST_H
+#define MTKREQUEST_H
+
+#include <ofono/types.h>
+#include <ofono/sim.h>
+
+#include "gril.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MTK_SWITCH_MODE_SIM_1_ACTIVE 1
+#define MTK_SWITCH_MODE_SIM_2_ACTIVE 2
+#define MTK_SWITCH_MODE_ALL_INACTIVE -1
+
+#define MTK_GPRS_CONNECT_WHEN_NEEDED 0
+#define MTK_GPRS_CONNECT_ALWAYS 1
+
+#define MTK_GPRS_TRANSFER_DATA_PREFER 0
+#define MTK_GPRS_TRANSFER_CALL_PREFER 1
+
+#define MTK_CALL_INDIC_MODE_AVAIL 0
+#define MTK_CALL_INDIC_MODE_NOT_AVAIL 1
+
+#define MTK_FD_MODE_DISABLE 0
+#define MTK_FD_MODE_ENABLE 1
+#define MTK_FD_MODE_SET_TIMER 2
+#define MTK_FD_MODE_SCREEN_STATUS 3
+
+#define MTK_FD_PAR1_SCREEN_OFF 0
+#define MTK_FD_PAR1_SCREEN_ON 1
+
+#define MTK_MD_TYPE_INVALID 0
+#define MTK_MD_TYPE_2G 1
+#define MTK_MD_TYPE_3G 2
+#define MTK_MD_TYPE_WG 3
+#define MTK_MD_TYPE_TG 4
+/* FDD CSFB modem: LTE FDD - WCDMA - GSM */
+#define MTK_MD_TYPE_LWG 5
+/* TDD CSFB modem: LTE TDD - TD-SCDMA - GSM */
+#define MTK_MD_TYPE_LTG 6
+/* SGLTE modem */
+#define MTK_MD_TYPE_LTNG 7
+
+/*
+ * The meaning of mode seems to be:
+ * -1 -> Both SIMs inactive
+ * 1 -> SIM 1 active
+ * 2 -> SIM 2 active
+ * 3 -> Both SIMs active
+ */
+void g_mtk_request_dual_sim_mode_switch(GRil *gril,
+					int mode,
+					struct parcel *rilp);
+
+/* 0:WHEN_NEEDED , 1: ALWAYS */
+void g_mtk_request_set_gprs_connect_type(GRil *gril,
+						int always,
+						struct parcel *rilp);
+
+/* 0:data prefer , 1: call prefer */
+void g_mtk_request_set_gprs_transfer_type(GRil *gril,
+						int callprefer,
+						struct parcel *rilp);
+
+/*
+ * mode. MTK_CALL_INDIC_MODE_AVAIL: indicates that the specified call can be
+ *       answered.
+ *       MTK_CALL_INDIC_MODE_NOT_AVAIL: indicates we are busy, and that the
+ *       specified call cannot be handled
+ * "mode" seems useful for full dual SIM. An example would be that we have an
+ * active call and an incoming call from the other SIM is signalled.
+ *
+ * call_id and seq_number are used to identify a specific call in the modem and
+ * are taken from a previous RIL_UNSOL_INCOMING_CALL_INDICATION.
+ */
+void g_mtk_request_set_call_indication(GRil *gril, int mode, int call_id,
+					int seq_number, struct parcel *rilp);
+
+/*
+ * mode . MTK_FD_MODE_DISABLE: Disable fast dormancy
+ *        MTK_FD_MODE_ENABLE: Enable fast formancy
+ *        MTK_FD_MODE_SET_TIMER: Set fast dormancy timer
+ *        MTK_FD_MODE_SCREEN_STATUS: Inform modem of screen status, to select
+ *                                   timers to use for FD.
+ * param1 Id of timer to set or screen status
+ * param2 Value of timer, in units of 0.1 seconds
+ *
+ * Setting timers and enabling FD is done by rild initialization, so we do not
+ * need to do it again in ofono.
+ */
+void g_mtk_request_set_fd_mode(GRil *gril, int mode, int param1,
+					int param2, struct parcel *rilp);
+
+/*
+ * Sets the slot to which the GRil object is connected as the one with 3G
+ * capabilities. This makes the other slot just 2G.
+ */
+void g_mtk_request_set_3g_capability(GRil *gril, struct parcel *rilp);
+
+/*
+ * Set the MTK modem mode. See MTK_MD_TYPE_*.
+ */
+void g_mtk_request_store_modem_type(GRil *gril, int mode, struct parcel *rilp);
+
+/*
+ * Set TRM
+ */
+void g_mtk_request_set_trm(GRil *gril, int trm, struct parcel *rilp);
+
+/*
+ * Request to resume registration.
+ */
+void g_mtk_request_resume_registration(GRil *gril, int session_id,
+					struct parcel *rilp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MTKREQUEST_H */
diff --git a/drivers/mtkmodem/mtksettings.c b/drivers/mtkmodem/mtksettings.c
new file mode 100644
index 0000000..165c4d9
--- /dev/null
+++ b/drivers/mtkmodem/mtksettings.c
@@ -0,0 +1,261 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2014  Canonical Ltd.
+ *
+ *  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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <glib.h>
+#include <gdbus.h>
+#include <ofono.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/dbus.h>
+
+#include "drivers/mtkmodem/mtk_constants.h"
+#include "drivers/mtkmodem/mtkrequest.h"
+#include "drivers/mtkmodem/mtkutil.h"
+#include "mtksettings.h"
+
+#define MTK_SETTINGS_INTERFACE "org.ofono.MtkSettings"
+
+struct mtk_settings_data {
+	struct ofono_modem *modem;
+	GRil *ril;
+	ofono_bool_t has_3g;
+	ofono_bool_t has_3g_pending;
+	DBusMessage *pending;
+};
+
+static void set_3g(struct mtk_settings_data *msd, ofono_bool_t has_3g)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = ofono_modem_get_path(msd->modem);
+	dbus_bool_t value = has_3g;
+
+	if (msd->has_3g == has_3g)
+		return;
+
+	ofono_dbus_signal_property_changed(conn, path, MTK_SETTINGS_INTERFACE,
+						"Has3G", DBUS_TYPE_BOOLEAN,
+						&value);
+	msd->has_3g = has_3g;
+}
+
+static void set_3g_cb(struct ril_msg *message, gpointer user_data)
+{
+	struct mtk_settings_data *msd = user_data;
+	DBusMessage *reply;
+
+	if (message->error != RIL_E_SUCCESS) {
+		ofono_error("%s: Error setting 3G", __func__);
+
+		msd->has_3g_pending = msd->has_3g;
+
+		reply = __ofono_error_failed(msd->pending);
+		__ofono_dbus_pending_reply(&msd->pending, reply);
+
+		return;
+	}
+
+	g_ril_print_response_no_args(msd->ril, message);
+
+	reply = dbus_message_new_method_return(msd->pending);
+	__ofono_dbus_pending_reply(&msd->pending, reply);
+
+	set_3g(msd, msd->has_3g_pending);
+
+	mtk_reset_all_modems();
+}
+
+static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
+					void *data)
+{
+	struct mtk_settings_data *msd = data;
+	DBusMessageIter iter;
+	DBusMessageIter var;
+	const char *property;
+
+	if (msd->pending)
+		return __ofono_error_busy(msg);
+
+	if (!dbus_message_iter_init(msg, &iter))
+		return __ofono_error_invalid_args(msg);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return __ofono_error_invalid_args(msg);
+
+	dbus_message_iter_get_basic(&iter, &property);
+	dbus_message_iter_next(&iter);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+		return __ofono_error_invalid_args(msg);
+
+	dbus_message_iter_recurse(&iter, &var);
+
+	if (g_strcmp0(property, "Has3G") == 0) {
+		dbus_bool_t value;
+		struct parcel rilp;
+
+		if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+			return __ofono_error_invalid_args(msg);
+
+		dbus_message_iter_get_basic(&var, &value);
+
+		if (msd->has_3g_pending == (ofono_bool_t) value)
+			return dbus_message_new_method_return(msg);
+
+		/*
+		 * We can only set to true, as setting to false could be
+		 * confusing in a multi-sim environment (>2 SIM)
+		 */
+		if (value == FALSE)
+			return __ofono_error_invalid_args(msg);
+
+		g_mtk_request_set_3g_capability(msd->ril, &rilp);
+
+		if (g_ril_send(msd->ril, MTK_RIL_REQUEST_SET_3G_CAPABILITY,
+				&rilp, set_3g_cb, msd, NULL) == 0) {
+			ofono_error("%s: unable to set 3G for slot", __func__);
+			return __ofono_error_failed(msg);
+		}
+
+		msd->pending = dbus_message_ref(msg);
+		msd->has_3g_pending = value;
+
+		return NULL;
+	}
+
+	return __ofono_error_invalid_args(msg);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct mtk_settings_data *msd = data;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+
+	reply = dbus_message_new_method_return(msg);
+	if (reply == NULL)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					OFONO_PROPERTIES_ARRAY_SIGNATURE,
+					&dict);
+
+	ofono_dbus_dict_append(&dict, "Has3G",
+				DBUS_TYPE_BOOLEAN, &msd->has_3g);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static const GDBusMethodTable mtk_settings_methods[] = {
+	{ GDBUS_ASYNC_METHOD("GetProperties",
+			NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+			get_properties) },
+	{ GDBUS_ASYNC_METHOD("SetProperty",
+			GDBUS_ARGS({ "property", "s" }, { "value", "v" }),
+			NULL, set_property) },
+	{ }
+};
+
+static const GDBusSignalTable mtk_settings_signals[] = {
+	{ GDBUS_SIGNAL("PropertyChanged",
+			GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+	{ }
+};
+
+
+static void register_interface(struct mtk_settings_data *msd)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+
+	if (!g_dbus_register_interface(conn, ofono_modem_get_path(msd->modem),
+					MTK_SETTINGS_INTERFACE,
+					mtk_settings_methods,
+					mtk_settings_signals,
+					NULL, msd, NULL)) {
+		ofono_error("Could not create %s interface",
+				MTK_SETTINGS_INTERFACE);
+		return;
+	}
+
+	ofono_modem_add_interface(msd->modem, MTK_SETTINGS_INTERFACE);
+}
+
+static void unregister_interface(struct mtk_settings_data *msd)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+
+	ofono_modem_remove_interface(msd->modem, MTK_SETTINGS_INTERFACE);
+
+	g_dbus_unregister_interface(conn,
+					ofono_modem_get_path(msd->modem),
+					MTK_SETTINGS_INTERFACE);
+}
+
+struct mtk_settings_data *mtk_settings_create(struct ofono_modem *modem,
+						GRil *ril, ofono_bool_t has_3g)
+{
+	struct mtk_settings_data *msd = g_try_malloc0(sizeof(*msd));
+
+	DBG("");
+
+	if (msd == NULL) {
+		ofono_error("%s: Cannot allocate mtk_settings_data", __func__);
+		return NULL;
+	}
+
+	msd->modem = modem;
+	msd->ril = g_ril_clone(ril);
+
+	msd->has_3g = has_3g;
+	msd->has_3g_pending = has_3g;
+
+	register_interface(msd);
+
+	return msd;
+}
+
+void mtk_settings_remove(struct mtk_settings_data *msd)
+{
+	DBG("");
+
+	if (msd == NULL)
+		return;
+
+	unregister_interface(msd);
+
+	g_ril_unref(msd->ril);
+	g_free(msd);
+}
diff --git a/drivers/mtkmodem/mtksettings.h b/drivers/mtkmodem/mtksettings.h
new file mode 100644
index 0000000..978f33d
--- /dev/null
+++ b/drivers/mtkmodem/mtksettings.h
@@ -0,0 +1,43 @@
+/*
+ *
+ *  MTK driver for ofono/rilmodem
+ *
+ *  Copyright (C) 2014  Canonical Ltd.
+ *
+ *  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
+ *
+ */
+
+#ifndef MTKSETTINGS_H
+#define MTKSETTINGS_H
+
+#include <ofono/types.h>
+
+#include "gril.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct mtk_settings_data;
+
+struct mtk_settings_data *mtk_settings_create(struct ofono_modem *modem,
+						GRil *ril, ofono_bool_t has_3g);
+void mtk_settings_remove(struct mtk_settings_data *msd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MTKSETTINGS_H */
diff --git a/drivers/mtkmodem/mtkunsol.c b/drivers/mtkmodem/mtkunsol.c
new file mode 100644
index 0000000..dcd58cc
--- /dev/null
+++ b/drivers/mtkmodem/mtkunsol.c
@@ -0,0 +1,199 @@
+/*
+ *
+ *  MTK driver for ofono/rilmodem
+ *
+ *  Copyright (C) 2014  Canonical Ltd.
+ *
+ *  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 <stdio.h>
+#include <ctype.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+
+#include "mtkunsol.h"
+#include "util.h"
+#include "common.h"
+
+void g_mtk_unsol_free_call_indication(struct unsol_call_indication *unsol)
+{
+	g_free(unsol);
+}
+
+struct unsol_call_indication *g_mtk_unsol_parse_incoming_call_indication(
+					GRil *gril, struct ril_msg *message)
+{
+	struct parcel rilp;
+	int numstr;
+	struct unsol_call_indication *unsol;
+	char *call_id, *phone, *mystery, *call_mode, *seq_num;
+	char *endp;
+
+	unsol = g_try_malloc0(sizeof(*unsol));
+	if (unsol == NULL) {
+		ofono_error("%s: out of memory", __func__);
+		goto error;
+	}
+
+	g_ril_init_parcel(message, &rilp);
+
+	numstr = parcel_r_int32(&rilp);
+	if (numstr != 5) {
+		ofono_error("%s: wrong array size (%d)", __func__, numstr);
+		goto error;
+	}
+
+	call_id = parcel_r_string(&rilp);
+	phone = parcel_r_string(&rilp);
+	mystery = parcel_r_string(&rilp);
+	call_mode = parcel_r_string(&rilp);
+	seq_num = parcel_r_string(&rilp);
+
+	g_ril_append_print_buf(gril, "{%s,%s,%s,%s,%s}", call_id, phone,
+				mystery, call_mode, seq_num);
+
+	g_ril_print_unsol(gril, message);
+
+	if (rilp.malformed) {
+		ofono_error("%s: malformed parcel", __func__);
+		goto err_conv;
+	}
+
+	if (call_id != NULL && *call_id != '\0') {
+		unsol->call_id = (int) strtol(call_id, &endp, 10);
+		if (*endp != '\0') {
+			ofono_error("%s: cannot parse call id", __func__);
+			goto err_conv;
+		}
+	} else {
+		ofono_error("%s: no call id", __func__);
+		goto err_conv;
+	}
+
+	if (call_mode != NULL && *call_mode != '\0') {
+		unsol->call_mode = (int) strtol(call_mode, &endp, 10);
+		if (*endp != '\0') {
+			ofono_error("%s: cannot parse call mode", __func__);
+			goto err_conv;
+		}
+	} else {
+		ofono_error("%s: no call mode", __func__);
+		goto err_conv;
+	}
+
+	if (seq_num != NULL && *seq_num != '\0') {
+		unsol->seq_number = (int) strtol(seq_num, &endp, 10);
+		if (*endp != '\0') {
+			ofono_error("%s: cannot parse seq num", __func__);
+			goto err_conv;
+		}
+	} else {
+		ofono_error("%s: no seq num", __func__);
+		goto err_conv;
+	}
+
+	g_free(call_id);
+	g_free(phone);
+	g_free(mystery);
+	g_free(call_mode);
+	g_free(seq_num);
+
+	return unsol;
+
+err_conv:
+	g_free(call_id);
+	g_free(phone);
+	g_free(mystery);
+	g_free(call_mode);
+	g_free(seq_num);
+
+error:
+	g_free(unsol);
+
+	return NULL;
+}
+
+int g_mtk_unsol_parse_registration_suspended(GRil *gril,
+						const struct ril_msg *message)
+{
+	struct parcel rilp;
+	int numint, session_id;
+
+	g_ril_init_parcel(message, &rilp);
+
+	numint = parcel_r_int32(&rilp);
+	if (numint != 1) {
+		ofono_error("%s Wrong format", __func__);
+		goto error;
+	}
+
+	session_id = parcel_r_int32(&rilp);
+
+	if (rilp.malformed) {
+		ofono_error("%s: malformed parcel", __func__);
+		goto error;
+	}
+
+	g_ril_append_print_buf(gril, "{%d}", session_id);
+	g_ril_print_unsol(gril, message);
+
+	return session_id;
+
+error:
+	return -1;
+
+}
+
+struct parcel_str_array *g_mtk_unsol_parse_plmn_changed(GRil *gril,
+						const struct ril_msg *message)
+{
+	struct parcel rilp;
+	struct parcel_str_array *str_arr;
+	int i;
+
+	g_ril_init_parcel(message, &rilp);
+
+	str_arr = parcel_r_str_array(&rilp);
+	if (str_arr == NULL || str_arr->num_str == 0) {
+		ofono_error("%s: parse error for %s", __func__,
+				ril_request_id_to_string(message->req));
+		parcel_free_str_array(str_arr);
+		str_arr = NULL;
+		goto out;
+	}
+
+	g_ril_append_print_buf(gril, "{");
+
+	for (i = 0; i < str_arr->num_str; ++i) {
+		if (i + 1 == str_arr->num_str)
+			g_ril_append_print_buf(gril, "%s%s}", print_buf,
+						str_arr->str[i]);
+		else
+			g_ril_append_print_buf(gril, "%s%s, ", print_buf,
+						str_arr->str[i]);
+	}
+
+	g_ril_print_unsol(gril, message);
+
+out:
+	return str_arr;
+}
diff --git a/drivers/mtkmodem/mtkunsol.h b/drivers/mtkmodem/mtkunsol.h
new file mode 100644
index 0000000..135e57e
--- /dev/null
+++ b/drivers/mtkmodem/mtkunsol.h
@@ -0,0 +1,59 @@
+/*
+ *
+ *  MTK driver for ofono/rilmodem
+ *
+ *  Copyright (C) 2014  Canonical Ltd.
+ *
+ *  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
+ *
+ */
+
+#ifndef MTKUNSOL_H
+#define MTKUNSOL_H
+
+#include <ofono/types.h>
+
+#include "gril.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct unsol_call_indication {
+	int call_id;
+	int call_mode;
+	int seq_number;
+};
+
+void g_mtk_unsol_free_call_indication(struct unsol_call_indication *unsol);
+
+struct unsol_call_indication *g_mtk_unsol_parse_incoming_call_indication(
+					GRil *gril, struct ril_msg *message);
+
+/* Returns a session id that must be used in resume registration request */
+int g_mtk_unsol_parse_registration_suspended(GRil *gril,
+						const struct ril_msg *message);
+
+/*
+ * Returns a minimum of one PLMN or NULL. parcel_free_str_array must be invoked
+ * to free the returned pointer.
+ */
+struct parcel_str_array *g_mtk_unsol_parse_plmn_changed(GRil *gril,
+						const struct ril_msg *message);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MTKUNSOL_H */
diff --git a/drivers/mtkmodem/mtkutil.c b/drivers/mtkmodem/mtkutil.c
new file mode 100644
index 0000000..9f3f42e
--- /dev/null
+++ b/drivers/mtkmodem/mtkutil.c
@@ -0,0 +1,143 @@
+/*
+ *
+ *  MTK driver for ofono/rilmodem
+ *
+ *  Copyright (C) 2014  Canonical Ltd.
+ *
+ *  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
+ *
+ */
+
+#include <stddef.h>
+
+#include "mtkutil.h"
+#include "mtk_constants.h"
+
+const char *mtk_request_id_to_string(int req)
+{
+	switch (req) {
+	case MTK_RIL_REQUEST_RADIO_POWEROFF:
+		return "MTK_RIL_REQUEST_RADIO_POWEROFF";
+	case MTK_RIL_REQUEST_DUAL_SIM_MODE_SWITCH:
+		return "MTK_RIL_REQUEST_DUAL_SIM_MODE_SWITCH";
+	case MTK_RIL_REQUEST_SET_GPRS_CONNECT_TYPE:
+		return "MTK_RIL_REQUEST_SET_GPRS_CONNECT_TYPE";
+	case MTK_RIL_REQUEST_SET_GPRS_TRANSFER_TYPE:
+		return "MTK_RIL_REQUEST_SET_GPRS_TRANSFER_TYPE";
+	case MTK_RIL_REQUEST_RADIO_POWERON:
+		return "MTK_RIL_REQUEST_RADIO_POWERON";
+	case MTK_RIL_REQUEST_SET_CALL_INDICATION:
+		return "MTK_RIL_REQUEST_SET_CALL_INDICATION";
+	case MTK_RIL_REQUEST_GET_3G_CAPABILITY:
+		return "MTK_RIL_REQUEST_GET_3G_CAPABILITY";
+	case MTK_RIL_REQUEST_SET_3G_CAPABILITY:
+		return "MTK_RIL_REQUEST_SET_3G_CAPABILITY";
+	case MTK_RIL_REQUEST_SET_TRM:
+		return "MTK_RIL_REQUEST_SET_TRM";
+	case MTK_RIL_REQUEST_SET_FD_MODE:
+		return "MTK_RIL_REQUEST_SET_FD_MODE";
+	case MTK_RIL_REQUEST_STORE_MODEM_TYPE:
+		return "MTK_RIL_REQUEST_STORE_MODEM_TYPE";
+	case MTK_RIL_REQUEST_RESUME_REGISTRATION:
+		return "MTK_RIL_REQUEST_RESUME_REGISTRATION";
+	case MTK_RIL_REQUEST_QUERY_MODEM_TYPE:
+		return "MTK_RIL_REQUEST_QUERY_MODEM_TYPE";
+	default:
+		return NULL;
+	}
+}
+
+const char *mtk_unsol_request_to_string(int req)
+{
+	switch (req) {
+	case MTK_RIL_UNSOL_NEIGHBORING_CELL_INFO:
+		return "MTK_RIL_UNSOL_NEIGHBORING_CELL_INFO";
+	case MTK_RIL_UNSOL_NETWORK_INFO:
+		return "MTK_RIL_UNSOL_NETWORK_INFO";
+	case MTK_RIL_UNSOL_CALL_FORWARDING:
+		return "MTK_RIL_UNSOL_CALL_FORWARDING";
+	case MTK_RIL_UNSOL_CRSS_NOTIFICATION:
+		return "MTK_RIL_UNSOL_CRSS_NOTIFICATION";
+	case MTK_RIL_UNSOL_CALL_PROGRESS_INFO:
+		return "MTK_RIL_UNSOL_CALL_PROGRESS_INFO";
+	case MTK_RIL_UNSOL_PHB_READY_NOTIFICATION:
+		return "MTK_RIL_UNSOL_PHB_READY_NOTIFICATION";
+	case MTK_RIL_UNSOL_SPEECH_INFO:
+		return "MTK_RIL_UNSOL_SPEECH_INFO";
+	case MTK_RIL_UNSOL_SIM_INSERTED_STATUS:
+		return "MTK_RIL_UNSOL_SIM_INSERTED_STATUS";
+	case MTK_RIL_UNSOL_RADIO_TEMPORARILY_UNAVAILABLE:
+		return "MTK_RIL_UNSOL_RADIO_TEMPORARILY_UNAVAILABLE";
+	case MTK_RIL_UNSOL_ME_SMS_STORAGE_FULL:
+		return "MTK_RIL_UNSOL_ME_SMS_STORAGE_FULL";
+	case MTK_RIL_UNSOL_SMS_READY_NOTIFICATION:
+		return "MTK_RIL_UNSOL_SMS_READY_NOTIFICATION";
+	case MTK_RIL_UNSOL_SCRI_RESULT:
+		return "MTK_RIL_UNSOL_SCRI_RESULT";
+	case MTK_RIL_UNSOL_VT_STATUS_INFO:
+		return "MTK_RIL_UNSOL_VT_STATUS_INFO";
+	case MTK_RIL_UNSOL_VT_RING_INFO:
+		return "MTK_RIL_UNSOL_VT_RING_INFO";
+	case MTK_RIL_UNSOL_INCOMING_CALL_INDICATION:
+		return "MTK_RIL_UNSOL_INCOMING_CALL_INDICATION";
+	case MTK_RIL_UNSOL_SIM_MISSING:
+		return "MTK_RIL_UNSOL_SIM_MISSING";
+	case MTK_RIL_UNSOL_GPRS_DETACH:
+		return "MTK_RIL_UNSOL_GPRS_DETACH";
+	case MTK_RIL_UNSOL_ATCI_RESPONSE:
+		return "MTK_RIL_UNSOL_ATCI_RESPONSE";
+	case MTK_RIL_UNSOL_SIM_RECOVERY:
+		return "MTK_RIL_UNSOL_SIM_RECOVERY";
+	case MTK_RIL_UNSOL_VIRTUAL_SIM_ON:
+		return "MTK_RIL_UNSOL_VIRTUAL_SIM_ON";
+	case MTK_RIL_UNSOL_VIRTUAL_SIM_OFF:
+		return "MTK_RIL_UNSOL_VIRTUAL_SIM_OFF";
+	case MTK_RIL_UNSOL_INVALID_SIM:
+		return "MTK_RIL_UNSOL_INVALID_SIM";
+	case MTK_RIL_UNSOL_RESPONSE_PS_NETWORK_STATE_CHANGED:
+		return "MTK_RIL_UNSOL_RESPONSE_PS_NETWORK_STATE_CHANGED";
+	case MTK_RIL_UNSOL_RESPONSE_ACMT:
+		return "MTK_RIL_UNSOL_RESPONSE_ACMT";
+	case MTK_RIL_UNSOL_EF_CSP_PLMN_MODE_BIT:
+		return "MTK_RIL_UNSOL_EF_CSP_PLMN_MODE_BIT";
+	case MTK_RIL_UNSOL_IMEI_LOCK:
+		return "MTK_RIL_UNSOL_IMEI_LOCK";
+	case MTK_RIL_UNSOL_RESPONSE_MMRR_STATUS_CHANGED:
+		return "MTK_RIL_UNSOL_RESPONSE_MMRR_STATUS_CHANGED";
+	case MTK_RIL_UNSOL_SIM_PLUG_OUT:
+		return "MTK_RIL_UNSOL_SIM_PLUG_OUT";
+	case MTK_RIL_UNSOL_SIM_PLUG_IN:
+		return "MTK_RIL_UNSOL_SIM_PLUG_IN";
+	case MTK_RIL_UNSOL_RESPONSE_ETWS_NOTIFICATION:
+		return "MTK_RIL_UNSOL_RESPONSE_ETWS_NOTIFICATION";
+	case MTK_RIL_UNSOL_RESPONSE_PLMN_CHANGED:
+		return "MTK_RIL_UNSOL_RESPONSE_PLMN_CHANGED";
+	case MTK_RIL_UNSOL_RESPONSE_REGISTRATION_SUSPENDED:
+		return "MTK_RIL_UNSOL_RESPONSE_REGISTRATION_SUSPENDED";
+	case MTK_RIL_UNSOL_STK_EVDL_CALL:
+		return "MTK_RIL_UNSOL_STK_EVDL_CALL";
+	case MTK_RIL_UNSOL_DATA_PACKETS_FLUSH:
+		return "MTK_RIL_UNSOL_DATA_PACKETS_FLUSH";
+	case MTK_RIL_UNSOL_CIPHER_INDICATION:
+		return "MTK_RIL_UNSOL_CIPHER_INDICATION";
+	case MTK_RIL_UNSOL_FEMTOCELL_INFO:
+		return "MTK_RIL_UNSOL_FEMTOCELL_INFO";
+	case MTK_RIL_UNSOL_CNAP:
+		return "MTK_RIL_UNSOL_CNAP";
+	case MTK_RIL_UNSOL_RAC_UPDATE:
+		return "MTK_RIL_UNSOL_RAC_UPDATE";
+	default:
+		return NULL;
+	}
+}
diff --git a/drivers/mtkmodem/mtkutil.h b/drivers/mtkmodem/mtkutil.h
new file mode 100644
index 0000000..b7e6a2a
--- /dev/null
+++ b/drivers/mtkmodem/mtkutil.h
@@ -0,0 +1,47 @@
+/*
+ *
+ *  MTK driver for ofono/rilmodem
+ *
+ *  Copyright (C) 2014  Canonical Ltd.
+ *
+ *  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
+ *
+ */
+
+#ifndef MTKUTIL_H
+#define MTKUTIL_H
+
+#include <ofono/types.h>
+#include <gril.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ofono_modem;
+
+void mtk_set_attach_state(struct ofono_modem *modem, ofono_bool_t attached);
+void mtk_detach_received(struct ofono_modem *modem);
+
+void mtk_reset_all_modems(void);
+void mtk_reset_modem(struct ofono_modem *modem);
+
+const char *mtk_request_id_to_string(int req);
+const char *mtk_unsol_request_to_string(int req);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MTKUTIL_H */
diff --git a/drivers/mtkmodem/radio-settings.c b/drivers/mtkmodem/radio-settings.c
new file mode 100644
index 0000000..8f46b0d
--- /dev/null
+++ b/drivers/mtkmodem/radio-settings.c
@@ -0,0 +1,193 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2014  Canonical Ltd.
+ *
+ *  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 <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/radio-settings.h>
+
+#include "mtk_constants.h"
+#include "mtkunsol.h"
+#include "mtkreply.h"
+#include "mtkrequest.h"
+
+#include "common.h"
+#include "mtkmodem.h"
+#include "drivers/rilmodem/radio-settings.h"
+#include "drivers/rilmodem/rilutil.h"
+
+/*
+ * This is the radio settings atom implementation for mtkmodem. Most of the
+ * implementation can be found in the rilmodem atom. The main reason for
+ * creating a new atom is the need to handle specific MTK requests.
+ */
+
+struct set_fd_mode {
+	struct ofono_radio_settings *rst;
+	gboolean on;
+};
+
+static void mtk_set_fd_mode_cb(struct ril_msg *message, gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	struct set_fd_mode *user = cbd->user;
+	struct ofono_radio_settings *rs = user->rst;
+	struct radio_data *rsd = ofono_radio_settings_get_data(rs);
+	ofono_radio_settings_fast_dormancy_set_cb_t cb = cbd->cb;
+
+	if (message->error == RIL_E_SUCCESS ||
+			message->error == RIL_E_RADIO_NOT_AVAILABLE) {
+
+		if (message->error == RIL_E_RADIO_NOT_AVAILABLE)
+			ofono_warn("Could not set fast dormancy "
+					"as radio is not available");
+
+		g_ril_print_response_no_args(rsd->ril, message);
+		ril_set_fast_dormancy(rs, user->on, cb, cbd->data);
+	} else {
+		CALLBACK_WITH_FAILURE(cb, cbd->data);
+	}
+
+	g_free(user);
+}
+
+static void mtk_set_fast_dormancy(struct ofono_radio_settings *rs,
+				ofono_bool_t enable,
+				ofono_radio_settings_fast_dormancy_set_cb_t cb,
+				void *data)
+{
+	struct radio_data *rsd = ofono_radio_settings_get_data(rs);
+	struct set_fd_mode *user = g_malloc0(sizeof(*user));
+	struct cb_data *cbd;
+	struct parcel rilp;
+
+	user->rst = rs;
+	user->on = enable;
+
+	cbd = cb_data_new(cb, data, user);
+
+	g_mtk_request_set_fd_mode(rsd->ril, MTK_FD_MODE_SCREEN_STATUS,
+					enable ? FALSE : TRUE, 0, &rilp);
+
+	if (g_ril_send(rsd->ril, MTK_RIL_REQUEST_SET_FD_MODE, &rilp,
+			mtk_set_fd_mode_cb, cbd, g_free) <= 0) {
+		g_free(cbd);
+		g_free(user);
+		CALLBACK_WITH_FAILURE(cb, data);
+	}
+}
+
+static int mtk_radio_settings_probe(struct ofono_radio_settings *rs,
+					unsigned int vendor, void *user)
+{
+	GRil *ril = user;
+	struct radio_data *rsd = g_new0(struct radio_data, 1);
+
+	rsd->ril = g_ril_clone(ril);
+
+	ofono_radio_settings_set_data(rs, rsd);
+
+	mtk_set_fast_dormancy(rs, FALSE, ril_delayed_register, rs);
+
+	return 0;
+}
+
+static void mtk_query_available_rats_cb(struct ril_msg *message,
+					gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	struct ofono_radio_settings *rs = cbd->user;
+	struct radio_data *rd = ofono_radio_settings_get_data(rs);
+	ofono_radio_settings_available_rats_query_cb_t cb = cbd->cb;
+	unsigned int available_rats = OFONO_RADIO_ACCESS_MODE_GSM;
+	int slot_3g, is_3g;
+
+	if (message->error != RIL_E_SUCCESS) {
+		ofono_error("%s: error %s", __func__,
+				ril_error_to_string(message->error));
+		CALLBACK_WITH_FAILURE(cb, 0, cbd->data);
+		return;
+	}
+
+	slot_3g = g_mtk_reply_parse_get_3g_capability(rd->ril, message);
+	if (slot_3g < 0) {
+		ofono_error("%s: parse error", __func__);
+		CALLBACK_WITH_FAILURE(cb, 0, cbd->data);
+		return;
+	}
+
+	is_3g = (g_ril_get_slot(rd->ril) == slot_3g);
+
+	if (is_3g) {
+		struct ofono_modem *modem = ofono_radio_settings_get_modem(rs);
+		available_rats |= OFONO_RADIO_ACCESS_MODE_UMTS;
+
+		if (ofono_modem_get_boolean(modem, MODEM_PROP_LTE_CAPABLE))
+			available_rats |= OFONO_RADIO_ACCESS_MODE_LTE;
+	}
+
+	CALLBACK_WITH_SUCCESS(cb, available_rats, cbd->data);
+}
+
+static void mtk_query_available_rats(struct ofono_radio_settings *rs,
+			ofono_radio_settings_available_rats_query_cb_t cb,
+			void *data)
+{
+	struct radio_data *rd = ofono_radio_settings_get_data(rs);
+	struct cb_data *cbd = cb_data_new(cb, data, rs);
+
+	if (g_ril_send(rd->ril, MTK_RIL_REQUEST_GET_3G_CAPABILITY, NULL,
+			mtk_query_available_rats_cb, cbd, g_free) <= 0) {
+		g_free(cbd);
+		CALLBACK_WITH_FAILURE(cb, 0, data);
+	}
+}
+
+static struct ofono_radio_settings_driver driver = {
+	.name			= MTKMODEM,
+	.probe			= mtk_radio_settings_probe,
+	.remove			= ril_radio_settings_remove,
+	.query_rat_mode		= ril_query_rat_mode,
+	.set_rat_mode		= ril_set_rat_mode,
+	.query_fast_dormancy	= ril_query_fast_dormancy,
+	.set_fast_dormancy	= mtk_set_fast_dormancy,
+	.query_available_rats	= mtk_query_available_rats
+};
+
+void mtk_radio_settings_init(void)
+{
+	ofono_radio_settings_driver_register(&driver);
+}
+
+void mtk_radio_settings_exit(void)
+{
+	ofono_radio_settings_driver_unregister(&driver);
+}
diff --git a/drivers/mtkmodem/voicecall.c b/drivers/mtkmodem/voicecall.c
new file mode 100644
index 0000000..bfb3c9d
--- /dev/null
+++ b/drivers/mtkmodem/voicecall.c
@@ -0,0 +1,155 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012-2014  Canonical Ltd.
+ *  Copyright (C) 2013 Jolla Ltd.
+ *
+ *  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 <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/voicecall.h>
+
+#include "mtk_constants.h"
+#include "mtkunsol.h"
+#include "mtkrequest.h"
+
+#include "common.h"
+#include "mtkmodem.h"
+#include "drivers/rilmodem/rilutil.h"
+#include "drivers/rilmodem/voicecall.h"
+
+/*
+ * This is the voicecall atom implementation for mtkmodem. Most of the
+ * implementation can be found in the rilmodem atom. The main reason for
+ * creating a new atom is the need to handle specific MTK requests and
+ * unsolicited events.
+ */
+
+static void mtk_set_indication_cb(struct ril_msg *message, gpointer user_data)
+{
+	struct ofono_voicecall *vc = user_data;
+	struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+	if (message->error == RIL_E_SUCCESS)
+		g_ril_print_response_no_args(vd->ril, message);
+	else
+		ofono_error("%s: RIL error %s", __func__,
+				ril_error_to_string(message->error));
+}
+
+static void mtk_incoming_notify(struct ril_msg *message, gpointer user_data)
+{
+	struct ofono_voicecall *vc = user_data;
+	struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+	struct parcel rilp;
+	struct unsol_call_indication *call_ind;
+
+	call_ind = g_mtk_unsol_parse_incoming_call_indication(vd->ril, message);
+	if (call_ind == NULL) {
+		ofono_error("%s: error parsing event", __func__);
+		return;
+	}
+
+	g_mtk_request_set_call_indication(vd->ril, MTK_CALL_INDIC_MODE_AVAIL,
+						call_ind->call_id,
+						call_ind->seq_number, &rilp);
+
+	if (g_ril_send(vd->ril, MTK_RIL_REQUEST_SET_CALL_INDICATION,
+			&rilp, mtk_set_indication_cb, vc, NULL) == 0)
+		ofono_error("%s: cannot send indication", __func__);
+
+	g_mtk_unsol_free_call_indication(call_ind);
+}
+
+static gboolean mtk_delayed_register(gpointer user_data)
+{
+	struct ofono_voicecall *vc = user_data;
+	struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+	/* MTK generates this event instead of CALL_STATE_CHANGED */
+	g_ril_register(vd->ril, MTK_RIL_UNSOL_CALL_PROGRESS_INFO,
+			ril_call_state_notify, vc);
+
+	/* Indicates incoming call, before telling the network our state */
+	g_ril_register(vd->ril, MTK_RIL_UNSOL_INCOMING_CALL_INDICATION,
+			mtk_incoming_notify, vc);
+
+	/* This makes the timeout a single-shot */
+	return FALSE;
+}
+
+static int mtk_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
+				void *data)
+{
+	GRil *ril = data;
+	struct ril_voicecall_data *vd;
+
+	vd = g_try_new0(struct ril_voicecall_data, 1);
+	if (vd == NULL)
+		return -ENOMEM;
+
+	ril_voicecall_start(ril, vc, vendor, vd);
+
+	/*
+	 * Register events after ofono_voicecall_register() is called from
+	 * ril_delayed_register().
+	 */
+	g_idle_add(mtk_delayed_register, vc);
+
+	return 0;
+}
+
+static struct ofono_voicecall_driver driver = {
+	.name			= MTKMODEM,
+	.probe			= mtk_voicecall_probe,
+	.remove			= ril_voicecall_remove,
+	.dial			= ril_dial,
+	.answer			= ril_answer,
+	.hangup_all		= ril_hangup_all,
+	.release_specific	= ril_hangup_specific,
+	.send_tones		= ril_send_dtmf,
+	.create_multiparty	= ril_create_multiparty,
+	.private_chat		= ril_private_chat,
+	.swap_without_accept	= ril_swap_without_accept,
+	.hold_all_active	= ril_hold_all_active,
+	.release_all_held	= ril_release_all_held,
+	.set_udub		= ril_set_udub,
+	.release_all_active	= ril_release_all_active,
+};
+
+void mtk_voicecall_init(void)
+{
+	ofono_voicecall_driver_register(&driver);
+}
+
+void mtk_voicecall_exit(void)
+{
+	ofono_voicecall_driver_unregister(&driver);
+}
-- 
2.5.0


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

* [PATCH 6/7] mtk: Plugin for mtkmodems
  2015-11-13 12:25 [PATCH 0/7] Support for mtk modems Alfonso Sanchez-Beato
                   ` (4 preceding siblings ...)
  2015-11-13 12:25 ` [PATCH 5/7] mtkmodem: Add mtkmodem driver Alfonso Sanchez-Beato
@ 2015-11-13 12:25 ` Alfonso Sanchez-Beato
  2015-11-13 12:25 ` [PATCH 7/7] build: Add mtkmodem driver and mtk plugin Alfonso Sanchez-Beato
  6 siblings, 0 replies; 9+ messages in thread
From: Alfonso Sanchez-Beato @ 2015-11-13 12:25 UTC (permalink / raw)
  To: ofono

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

---
 plugins/mtk.c | 1704 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1704 insertions(+)
 create mode 100644 plugins/mtk.c

diff --git a/plugins/mtk.c b/plugins/mtk.c
new file mode 100644
index 0000000..8a9a720
--- /dev/null
+++ b/plugins/mtk.c
@@ -0,0 +1,1704 @@
+/*
+ *
+ *  oFono - Open Source Telephony - RIL-based devices
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012-2014 Canonical Ltd.
+ *  Copyright (C) 2013 Jolla Ltd.
+ *
+ *  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 <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+#include <ofono/phonebook.h>
+#include <ofono/netreg.h>
+#include <ofono/voicecall.h>
+#include <ofono/sms.h>
+#include <ofono/cbs.h>
+#include <ofono/sim.h>
+#include <ofono/ussd.h>
+#include <ofono/call-forwarding.h>
+#include <ofono/call-settings.h>
+#include <ofono/call-barring.h>
+#include <ofono/call-meter.h>
+#include <ofono/call-volume.h>
+#include <ofono/radio-settings.h>
+#include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
+#include <ofono/audio-settings.h>
+#include <ofono/types.h>
+
+#include "ofono.h"
+#include <common.h>
+
+#include <gril.h>
+#include <grilreply.h>
+#include <grilrequest.h>
+#include <grilunsol.h>
+
+#include "drivers/rilmodem/rilmodem.h"
+#include "drivers/rilmodem/vendor.h"
+
+#include "drivers/mtkmodem/mtkmodem.h"
+#include "drivers/mtkmodem/mtk_constants.h"
+#include "drivers/mtkmodem/mtkutil.h"
+#include "drivers/mtkmodem/mtkrequest.h"
+#include "drivers/mtkmodem/mtkreply.h"
+#include "drivers/mtkmodem/mtkunsol.h"
+#include "drivers/mtkmodem/mtksettings.h"
+
+#define MAX_SIM_STATUS_RETRIES 15
+
+#define MULTISIM_SLOT_0 0
+#define MULTISIM_SLOT_1 1
+
+#define SIM_1_ACTIVE 1
+#define SIM_2_ACTIVE 2
+#define NO_SIM_ACTIVE 0
+
+#define SOCKET_NUM_FOR_DBG_0 -1
+#define SOCKET_NUM_FOR_DBG_1 -2
+
+/* this gives 30s for rild to initialize */
+#define RILD_MAX_CONNECT_RETRIES 5
+#define RILD_CONNECT_RETRY_TIME_S 5
+
+#define T_WAIT_DISCONN_MS 1000
+#define T_SIM_SWITCH_FAILSAFE_MS 1000
+
+#define INVALID_SUSPEND_ID -1
+#define T_FW_SWITCH_S 60
+
+enum mtk_plmn_type {
+	MTK_PLMN_TYPE_UNKNOWN = 0,
+	MTK_PLMN_TYPE_1,
+	MTK_PLMN_TYPE_2,
+	MTK_PLMN_TYPE_3
+};
+
+static const char hex_slot_0[] = "Slot 0: ";
+static const char hex_slot_1[] = "Slot 1: ";
+
+typedef void (*pending_cb_t)(struct cb_data *cbd);
+
+struct mtk_data {
+	GRil *ril;
+	int sim_status_retries;
+	ofono_bool_t ofono_online;
+	ofono_bool_t ofono_online_target;
+	int radio_state;
+	struct ofono_sim *sim;
+	/* pending_* are used in case we are disconnected from the socket */
+	pending_cb_t pending_cb;
+	struct cb_data *pending_cbd;
+	int slot;
+	struct ril_sim_data sim_data;
+	struct ofono_devinfo *devinfo;
+	struct ofono_voicecall *voicecall;
+	struct ofono_call_volume *callvolume;
+	struct cb_data *pending_online_cbd;
+	ofono_bool_t pending_online;
+	ofono_bool_t gprs_attach;
+	int rild_connect_retries;
+	struct ofono_sms *sms;
+	struct ofono_netreg *netreg;
+	struct ofono_ussd *ussd;
+	struct ofono_call_settings *call_settings;
+	struct ofono_call_forwarding *call_forwarding;
+	struct ofono_call_barring *call_barring;
+	struct ofono_phonebook *phonebook;
+	struct ofono_gprs *gprs;
+	struct ofono_message_waiting *message_waiting;
+	struct ofono_modem *modem;
+	ofono_bool_t has_3g;
+	struct mtk_settings_data *mtk_settings;
+	int fw_type;
+	ofono_modem_online_cb_t online_cb;
+	void *online_data;
+	enum mtk_plmn_type sim_plmn_type;
+	enum mtk_plmn_type sensed_plmn_type;
+	int suspend_id;
+	ofono_bool_t trm_pending;
+	unsigned netreg_watch;
+	unsigned status_watch;
+	int netreg_status;
+	guint switch_fw_id;
+};
+
+/*
+ * MTK dual SIM sockets are not completely symmetric: some requests (essentially
+ * those related for radio power management and SIM slot enablement) can be sent
+ * only through the socket for slot 0. So we need a pointer to the main socket.
+ * Also, we need to access information of one channel from the other channel.
+ */
+static struct mtk_data *mtk_data_0;
+static struct mtk_data *mtk_data_1;
+
+/* Some variables control global state of the modem and are then static */
+static gboolean disconnect_expected;
+static guint not_disconn_cb_id;
+
+struct socket_data {
+	GRil *ril;
+	const char *path;
+	int radio_state;
+	guint radio_state_ev_id;
+};
+
+static struct socket_data *sock_0, *sock_1;
+
+static int create_gril(struct ofono_modem *modem);
+static gboolean mtk_connected(gpointer user_data);
+static void mtk_set_online(struct ofono_modem *modem, ofono_bool_t online,
+				ofono_modem_online_cb_t callback, void *data);
+static void query_3g_caps(struct socket_data *sock);
+static void socket_disconnected(gpointer user_data);
+static void start_slot(struct mtk_data *md, struct socket_data *sock,
+			const char *hex_prefix);
+static void exec_pending_online(struct mtk_data *md);
+
+static void mtk_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	ofono_info("%s%s", prefix, str);
+}
+
+static struct mtk_data *mtk_data_complement(struct mtk_data *md)
+{
+	if (md->slot == MULTISIM_SLOT_0)
+		return mtk_data_1;
+	else
+		return mtk_data_0;
+}
+
+static struct socket_data *socket_complement(struct socket_data *sock)
+{
+	if (sock == sock_0)
+		return sock_1;
+	else
+		return sock_0;
+}
+
+/*
+ * mtk_set_attach_state and mtk_detach_received are called by mtkmodem's gprs
+ * driver. They are needed to solve an issue with data attachment: in case
+ * org.ofono.ConnectionManager Powered property is set for, say, slot 1 while
+ * slot 0 has that property also set, slot 1 will not change the data
+ * registration state even after slot 0 data connection is finally dropped. To
+ * force slot 1 to try to attach we need to send an additional
+ * MTK_RIL_REQUEST_SET_GPRS_CONNECT_TYPE. The way to know when to do this is to
+ * detect when slot 0 has finally detached. This is done listening for
+ * MTK_RIL_UNSOL_GPRS_DETACH events, but unfortunately these events are received
+ * in the modem that does not need to know about them, so we have to pass them
+ * to the mtk plugin (which has knowledge of both modems) that will take proper
+ * action in the other modem.
+ */
+
+void mtk_set_attach_state(struct ofono_modem *modem, ofono_bool_t attached)
+{
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	md->gprs_attach = attached;
+}
+
+static void detach_received_cb(struct ril_msg *message, gpointer user_data)
+{
+	struct mtk_data *md = user_data;
+
+	if (message->error == RIL_E_SUCCESS)
+		g_ril_print_response_no_args(md->ril, message);
+	else
+		ofono_error("%s: RIL error %s", __func__,
+				ril_error_to_string(message->error));
+}
+
+void mtk_detach_received(struct ofono_modem *modem)
+{
+	struct mtk_data *md = ofono_modem_get_data(modem);
+	struct mtk_data *md_c = mtk_data_complement(md);
+
+	if (md_c != NULL && md_c->gprs_attach) {
+		struct parcel rilp;
+
+		g_mtk_request_set_gprs_connect_type(md_c->ril,
+						md_c->gprs_attach, &rilp);
+
+		if (g_ril_send(md_c->ril,
+				MTK_RIL_REQUEST_SET_GPRS_CONNECT_TYPE,
+				&rilp, detach_received_cb, md_c, NULL) == 0)
+			ofono_error("%s: send failed", __func__);
+	}
+}
+
+static void radio_state_changed(struct ril_msg *message, gpointer user_data)
+{
+	struct socket_data *sock = user_data;
+	int radio_state = g_ril_unsol_parse_radio_state_changed(sock->ril,
+								message);
+
+	if (radio_state != sock->radio_state) {
+		struct socket_data *sock_c = socket_complement(sock);
+
+		ofono_info("%s, %s, state: %s", __func__, sock->path,
+				ril_radio_state_to_string(radio_state));
+
+		/*
+		 * If there is just one slot, just start it. Otherwise, we ask
+		 * who owns the 3G capabilities in case both slots have already
+		 * radio state different from UNAVAILABLE.
+		 */
+		if (mtk_data_1 == NULL) {
+			mtk_data_0->has_3g = TRUE;
+			start_slot(mtk_data_0, sock, hex_slot_0);
+		} else if (sock->radio_state == RADIO_STATE_UNAVAILABLE &&
+				sock_c != NULL && sock_c->radio_state !=
+						RADIO_STATE_UNAVAILABLE) {
+			query_3g_caps(sock);
+		}
+
+		sock->radio_state = radio_state;
+	}
+}
+
+static void exec_online_callback(struct mtk_data *md)
+{
+	if (md->online_cb != NULL) {
+		/*
+		 * We assume success, as the sim switch request was successful
+		 * too. Also, the SIM_* states can be returned independently of
+		 * the radio state, so we cannot reliably use it to know if the
+		 * sim switch command really did what was requested.
+		 */
+		CALLBACK_WITH_SUCCESS(md->online_cb, md->online_data);
+		md->online_cb = NULL;
+		md->online_data = NULL;
+
+		if (md->ofono_online)
+			md->mtk_settings =
+				mtk_settings_create(md->modem, md->ril,
+							md->has_3g);
+		else
+			mtk_settings_remove(md->mtk_settings);
+
+		exec_pending_online(md);
+	}
+}
+
+static void mtk_radio_state_changed(struct ril_msg *message, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+	int radio_state = g_ril_unsol_parse_radio_state_changed(md->ril,
+								message);
+
+	ofono_info("%s, slot %d: state: %s md->ofono_online: %d",
+			__func__, md->slot,
+			ril_radio_state_to_string(radio_state),
+			md->ofono_online);
+
+	md->radio_state = radio_state;
+
+	switch (radio_state) {
+	case RADIO_STATE_ON:
+	/* RADIO_STATE_SIM_* are deprecated in AOSP, but still used by MTK */
+	case RADIO_STATE_SIM_NOT_READY:
+	case RADIO_STATE_SIM_LOCKED_OR_ABSENT:
+	case RADIO_STATE_SIM_READY:
+	case RADIO_STATE_UNAVAILABLE:
+	case RADIO_STATE_OFF:
+		break;
+	default:
+		/* Malformed parcel; no radio state == broken rild */
+		g_assert(FALSE);
+	}
+
+	exec_online_callback(md);
+}
+
+static void resume_reg_cb(struct ril_msg *message, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	if (message->error != RIL_E_SUCCESS) {
+		ofono_error("%s: RIL error %s", __func__,
+				ril_error_to_string(message->error));
+		return;
+	}
+
+	g_ril_print_response_no_args(md->ril, message);
+}
+
+static void resume_reg(struct ofono_modem *modem)
+{
+	struct mtk_data *md = ofono_modem_get_data(modem);
+	struct parcel rilp;
+
+	if (md->suspend_id == INVALID_SUSPEND_ID)
+		return;
+
+	g_mtk_request_resume_registration(md->ril, md->suspend_id, &rilp);
+
+	if (g_ril_send(md->ril, MTK_RIL_REQUEST_RESUME_REGISTRATION, &rilp,
+			resume_reg_cb, modem, NULL) == 0)
+		ofono_error("%s: failure sending request", __func__);
+
+	md->suspend_id = INVALID_SUSPEND_ID;
+}
+
+static void sim_removed(struct ril_msg *message, gpointer user_data)
+{
+	struct ofono_modem *modem = (struct ofono_modem *) user_data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	DBG("");
+
+	g_ril_print_unsol_no_args(md->ril, message);
+
+	md->sim_plmn_type = MTK_PLMN_TYPE_UNKNOWN;
+
+	ofono_modem_set_powered(modem, FALSE);
+	g_idle_add(mtk_connected, modem);
+}
+
+static void sim_inserted(struct ril_msg *message, gpointer user_data)
+{
+	struct ofono_modem *modem = (struct ofono_modem *) user_data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	DBG("");
+
+	g_ril_print_unsol_no_args(md->ril, message);
+
+	if (getenv("OFONO_RIL_HOT_SIM_SWAP")) {
+		ofono_modem_set_powered(modem, FALSE);
+		g_idle_add(mtk_connected, modem);
+	}
+}
+
+static void set_trm_cb(struct ril_msg *message, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	if (message->error != RIL_E_SUCCESS) {
+		ofono_error("%s: RIL error %s", __func__,
+				ril_error_to_string(message->error));
+		return;
+	}
+
+	g_ril_print_response_no_args(md->ril, message);
+}
+
+static void store_type_cb(struct ril_msg *message, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+	struct parcel rilp;
+	int trm = 0;
+
+	if (message->error != RIL_E_SUCCESS) {
+		ofono_error("%s: RIL error %s", __func__,
+				ril_error_to_string(message->error));
+		md->trm_pending = FALSE;
+		return;
+	}
+
+	g_ril_print_response_no_args(md->ril, message);
+
+	/*
+	 * Send SET_TRM, which reloads the FW. We do not know the meaning of the
+	 * magic TRM numbers (no source code for muxreport daemon).
+	 */
+	if (md->fw_type == MTK_MD_TYPE_LWG) {
+		trm = 11;
+	} else if (md->fw_type == MTK_MD_TYPE_LTG) {
+		trm = 12;
+	} else {
+		ofono_error("%s: wrong modem type %d", __func__, md->fw_type);
+		g_assert(FALSE);
+	}
+
+	g_mtk_request_set_trm(md->ril, trm, &rilp);
+
+	if (g_ril_send(md->ril, MTK_RIL_REQUEST_SET_TRM, &rilp,
+			set_trm_cb, modem, NULL) == 0)
+		ofono_error("%s: failure sending request", __func__);
+
+	/*
+	 * rild will close now the socket, and the response to SET_TRM might be
+	 * received before that or not.
+	 * Measurements showed that rild takes around 5 seconds to re-start, but
+	 * we use an 8 seconds timeout as times can vary and that value is also
+	 * compatible with krillin modem.
+	 */
+}
+
+static gboolean find_in_table(const char *str,
+				const char **table, int num_elem)
+{
+	int i;
+
+	for (i = 0; i < num_elem; ++i)
+		if (strncmp(str, table[i], strlen(table[i])) == 0)
+			return TRUE;
+
+	return FALSE;
+}
+
+static enum mtk_plmn_type get_plmn_type(const char *code)
+{
+	/* China Mobile (CMCC) MCC/MNC codes */
+	const char *table_type1[] = { "46000", "46002", "46007" };
+	/* China Unicom (CU) and China Telecom MCC/MNC codes */
+	const char *table_type3[] =
+		{ "46001", "46006", "46009", "45407", "46005", "45502" };
+
+	if (find_in_table(code, table_type1, G_N_ELEMENTS(table_type1)))
+		return MTK_PLMN_TYPE_1;
+
+	if (find_in_table(code, table_type3, G_N_ELEMENTS(table_type3)))
+		return MTK_PLMN_TYPE_3;
+
+	return MTK_PLMN_TYPE_2;
+}
+
+static int select_modem_fw(enum mtk_plmn_type sim_plmn_type,
+				enum mtk_plmn_type sensed_plmn_type)
+{
+	DBG("PLMN (sim, sensed)=(%d, %d)", sim_plmn_type, sensed_plmn_type);
+
+	/*
+	 * Outside China (sensed t2) -> FDD always
+	 * In China (sensed t1, t3) -> sim t1 is TDD, t2 or t3 is FDD,
+	 *                             or wait for imsi to know sim type
+	 * Unknown country -> sim t2 or t3 are FDD, wait network if t1, wait
+	 *                    network/imsi events if both unknown
+	 */
+	switch (sensed_plmn_type) {
+	case MTK_PLMN_TYPE_2:
+		return MTK_MD_TYPE_LWG;
+	case MTK_PLMN_TYPE_1:
+	case MTK_PLMN_TYPE_3:
+		switch (sim_plmn_type) {
+		case MTK_PLMN_TYPE_1:
+			return MTK_MD_TYPE_LTG;
+		case MTK_PLMN_TYPE_2:
+		case MTK_PLMN_TYPE_3:
+			return MTK_MD_TYPE_LWG;
+		case MTK_PLMN_TYPE_UNKNOWN:
+			return MTK_MD_TYPE_INVALID;
+		};
+	case MTK_PLMN_TYPE_UNKNOWN:
+		switch (sim_plmn_type) {
+		case MTK_PLMN_TYPE_2:
+		case MTK_PLMN_TYPE_3:
+			return MTK_MD_TYPE_LWG;
+		case MTK_PLMN_TYPE_1:
+		case MTK_PLMN_TYPE_UNKNOWN:
+			return MTK_MD_TYPE_INVALID;
+		};
+	};
+
+	/* We should never arrive here */
+	ofono_error("%s: UNREACHABLE POINT REACHED, aborting", __func__);
+	g_assert(FALSE);
+
+	return MTK_MD_TYPE_INVALID;
+}
+
+static void set_fw_type(struct ofono_modem *modem, int type)
+{
+	ofono_bool_t lte_cap;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	md->fw_type = type;
+
+	lte_cap = (type >= MTK_MD_TYPE_LWG) ? TRUE : FALSE;
+	ofono_modem_set_boolean(modem, MODEM_PROP_LTE_CAPABLE, lte_cap);
+}
+
+static const char *fw_type_to_str(int fw_type)
+{
+	switch (fw_type) {
+	case MTK_MD_TYPE_2G:
+		return "2G";
+	case MTK_MD_TYPE_3G:
+		return "3G";
+	case MTK_MD_TYPE_WG:
+		return "WG";
+	case MTK_MD_TYPE_TG:
+		return "TG";
+	case MTK_MD_TYPE_LWG:
+		return "LWG";
+	case MTK_MD_TYPE_LTG:
+		return "LTG";
+	case MTK_MD_TYPE_LTNG:
+		return "LTNG";
+	}
+
+	return "<INVALID>";
+}
+
+static void switch_fw(struct ofono_modem *modem, int fw_type)
+{
+	struct mtk_data *md = ofono_modem_get_data(modem);
+	struct parcel rilp;
+
+	ofono_info("Switching modem FW from %s to %s",
+			fw_type_to_str(md->fw_type), fw_type_to_str(fw_type));
+
+	md->trm_pending = TRUE;
+
+	set_fw_type(modem, fw_type);
+
+	g_mtk_request_store_modem_type(md->ril, fw_type, &rilp);
+
+	if (g_ril_send(md->ril, MTK_RIL_REQUEST_STORE_MODEM_TYPE, &rilp,
+			store_type_cb, modem, NULL) == 0) {
+		ofono_error("%s: failure sending request", __func__);
+		md->trm_pending = FALSE;
+	}
+}
+
+static void check_modem_fw(struct ofono_modem *modem)
+{
+	struct mtk_data *md = ofono_modem_get_data(modem);
+	int best_fw;
+
+	/* We handle only LWG <-> LTG modem fw changes (arale case) */
+	if (md->fw_type != MTK_MD_TYPE_LTG && md->fw_type != MTK_MD_TYPE_LWG)
+		return;
+
+	if (md->trm_pending) {
+		DBG("TRM pending, returning");
+		return;
+	}
+
+	/* Right modem fw type for our SIM/sensed PLMN */
+	best_fw = select_modem_fw(md->sim_plmn_type, md->sensed_plmn_type);
+
+	DBG("Modem type selected is %d", best_fw);
+
+	/* Best FW not known yet: wait for imsi/sensed network */
+	if (best_fw == MTK_MD_TYPE_INVALID)
+		return;
+
+	if (md->fw_type == best_fw) {
+		resume_reg(modem);
+		return;
+	}
+
+	/* We need to reload FW and reset transactionally */
+	switch_fw(modem, best_fw);
+}
+
+static void plmn_changed(struct ril_msg *message, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+	struct parcel_str_array *plmns;
+
+	plmns = g_mtk_unsol_parse_plmn_changed(md->ril, message);
+	if (plmns == NULL) {
+		ofono_error("%s: parse error", __func__);
+		return;
+	}
+
+	md->sensed_plmn_type = get_plmn_type(plmns->str[0]);
+
+	DBG("Best PLMN is %s (type %d)", plmns->str[0], md->sensed_plmn_type);
+
+	parcel_free_str_array(plmns);
+
+	check_modem_fw(modem);
+}
+
+static void reg_suspended(struct ril_msg *message, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+	int suspend_id;
+
+	suspend_id = g_mtk_unsol_parse_registration_suspended(md->ril, message);
+	if (suspend_id < 0) {
+		ofono_error("%s: parse error", __func__);
+		return;
+	}
+
+	md->suspend_id = suspend_id;
+
+	check_modem_fw(modem);
+}
+
+static gboolean tout_not_registered(gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	if (md->trm_pending)
+		goto end;
+
+	DBG("type was %d", md->fw_type);
+
+	if (md->fw_type == MTK_MD_TYPE_LTG)
+		switch_fw(modem, MTK_MD_TYPE_LWG);
+	else
+		switch_fw(modem, MTK_MD_TYPE_LTG);
+
+end:
+	md->switch_fw_id = 0;
+
+	return FALSE;
+}
+
+static void cancel_fw_load_timer(struct mtk_data *md)
+{
+	if (md->switch_fw_id) {
+		g_source_remove(md->switch_fw_id);
+		md->switch_fw_id = 0;
+	}
+}
+
+static void netreg_status_update(struct ofono_modem *modem)
+{
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	if (md->sim_plmn_type != MTK_PLMN_TYPE_1)
+		return;
+
+	if (md->fw_type != MTK_MD_TYPE_LTG && md->fw_type != MTK_MD_TYPE_LWG)
+		return;
+
+	DBG("%d", md->netreg_status);
+
+	if (md->switch_fw_id) {
+		if (md->netreg_status == NETWORK_REGISTRATION_STATUS_REGISTERED
+				|| md->netreg_status ==
+					NETWORK_REGISTRATION_STATUS_ROAMING)
+			cancel_fw_load_timer(md);
+		return;
+	}
+
+	if (md->netreg_status == NETWORK_REGISTRATION_STATUS_NOT_REGISTERED)
+		md->switch_fw_id = g_timeout_add_seconds(T_FW_SWITCH_S,
+						tout_not_registered, modem);
+}
+
+static void netreg_status_changed(int status, int lac, int ci, int tech,
+					const char *mcc, const char *mnc,
+					void *data)
+{
+	struct ofono_modem *modem = data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	if (md->netreg_status == status)
+		return;
+
+	md->netreg_status = status;
+
+	netreg_status_update(modem);
+}
+
+static void netreg_watch(struct ofono_atom *atom,
+				enum ofono_atom_watch_condition cond,
+				void *data)
+{
+	struct ofono_modem *modem = data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+	void *netreg;
+
+	DBG("%d", cond);
+
+	if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
+		cancel_fw_load_timer(md);
+		md->status_watch = 0;
+		return;
+	}
+
+	netreg = __ofono_atom_get_data(atom);
+	md->netreg_status = ofono_netreg_get_status(netreg);
+	md->status_watch = __ofono_netreg_add_status_watch(netreg,
+					netreg_status_changed, modem, NULL);
+
+	netreg_status_update(modem);
+}
+
+static int mtk_probe(struct ofono_modem *modem)
+{
+	struct mtk_data *md = g_try_new0(struct mtk_data, 1);
+
+	if (md == NULL) {
+		errno = ENOMEM;
+		goto error;
+	}
+
+	md->ofono_online = FALSE;
+	md->radio_state = RADIO_STATE_UNAVAILABLE;
+	md->suspend_id = INVALID_SUSPEND_ID;
+
+	md->slot = ofono_modem_get_integer(modem, "Slot");
+
+	if (md->slot == MULTISIM_SLOT_0)
+		mtk_data_0 = md;
+	else
+		mtk_data_1 = md;
+
+	DBG("slot %d", md->slot);
+
+	md->modem = modem;
+
+	ofono_modem_set_data(modem, md);
+
+	return 0;
+
+error:
+	g_free(md);
+
+	return -errno;
+}
+
+static void mtk_remove(struct ofono_modem *modem)
+{
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	ofono_modem_set_data(modem, NULL);
+
+	if (!md)
+		return;
+
+	g_ril_unref(md->ril);
+
+	g_free(md);
+}
+
+static void mtk_pre_sim(struct ofono_modem *modem)
+{
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	DBG("slot %d", md->slot);
+}
+
+static void mtk_post_sim(struct ofono_modem *modem)
+{
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	DBG("slot %d", md->slot);
+}
+
+/*
+ * sim_state_watch listens to SIM state changes and creates/removes atoms
+ * accordingly. This is needed because we cannot rely on the modem core code,
+ * which handles modem state transitions, to do this due to the SIM not being
+ * accessible in the offline state for mtk modems. This causes a mismatch
+ * between what the core thinks it can do in some states and what the mtk modem
+ * can really do in those. This is a workaround to solve that.
+ */
+static void sim_state_watch(enum ofono_sim_state new_state, void *data)
+{
+	struct ofono_modem *modem = data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	if (new_state == OFONO_SIM_STATE_READY) {
+		struct ofono_gprs_context *gc;
+		struct ril_gprs_driver_data gprs_data = { md->ril, modem };
+		struct ril_gprs_context_data inet_ctx =
+			{ md->ril, modem, OFONO_GPRS_CONTEXT_TYPE_INTERNET };
+		struct ril_gprs_context_data mms_ctx =
+			{ md->ril, modem, OFONO_GPRS_CONTEXT_TYPE_MMS };
+
+		DBG("SIM ready, creating more atoms");
+
+		/*
+		 * TODO: this function should setup:
+		 *  - stk ( SIM toolkit )
+		 */
+		md->sms = ofono_sms_create(modem, OFONO_RIL_VENDOR_MTK,
+						RILMODEM, md->ril);
+
+		/* netreg needs access to the SIM (SPN, SPDI) */
+		md->netreg = ofono_netreg_create(modem, OFONO_RIL_VENDOR_MTK,
+							RILMODEM, md->ril);
+		md->ussd = ofono_ussd_create(modem, OFONO_RIL_VENDOR_MTK,
+						RILMODEM, md->ril);
+		md->call_settings =
+			ofono_call_settings_create(modem, OFONO_RIL_VENDOR_MTK,
+							RILMODEM, md->ril);
+		md->call_forwarding =
+			ofono_call_forwarding_create(modem,
+							OFONO_RIL_VENDOR_MTK,
+							RILMODEM, md->ril);
+		md->call_barring =
+			ofono_call_barring_create(modem, OFONO_RIL_VENDOR_MTK,
+							RILMODEM, md->ril);
+		md->phonebook =
+			ofono_phonebook_create(modem, OFONO_RIL_VENDOR_MTK,
+						RILMODEM, modem);
+		md->gprs = ofono_gprs_create(modem, OFONO_RIL_VENDOR_MTK,
+						MTKMODEM, &gprs_data);
+
+		gc = ofono_gprs_context_create(modem, OFONO_RIL_VENDOR_MTK,
+						RILMODEM, &inet_ctx);
+		if (gc) {
+			ofono_gprs_context_set_type(gc,
+					OFONO_GPRS_CONTEXT_TYPE_INTERNET);
+			ofono_gprs_add_context(md->gprs, gc);
+		}
+
+		gc = ofono_gprs_context_create(modem, OFONO_RIL_VENDOR_MTK,
+						RILMODEM, &mms_ctx);
+		if (gc) {
+			ofono_gprs_context_set_type(gc,
+					OFONO_GPRS_CONTEXT_TYPE_MMS);
+			ofono_gprs_add_context(md->gprs, gc);
+		}
+
+		md->message_waiting = ofono_message_waiting_create(modem);
+		if (md->message_waiting)
+			ofono_message_waiting_register(md->message_waiting);
+
+		/*
+		 * Now that we can access IMSI, see if a FW change is needed.
+		 */
+
+		md->sim_plmn_type = get_plmn_type(ofono_sim_get_imsi(md->sim));
+
+		check_modem_fw(modem);
+
+	} else if (new_state == OFONO_SIM_STATE_LOCKED_OUT) {
+
+		DBG("SIM locked, removing atoms");
+
+		if (md->message_waiting) {
+			ofono_message_waiting_remove(md->message_waiting);
+			md->message_waiting = NULL;
+		}
+		if (md->gprs) {
+			ofono_gprs_remove(md->gprs);
+			md->gprs = NULL;
+		}
+		if (md->phonebook) {
+			ofono_phonebook_remove(md->phonebook);
+			md->phonebook = NULL;
+		}
+		if (md->call_barring) {
+			ofono_call_barring_remove(md->call_barring);
+			md->call_barring = NULL;
+		}
+		if (md->call_forwarding) {
+			ofono_call_forwarding_remove(md->call_forwarding);
+			md->call_forwarding = NULL;
+		}
+		if (md->call_settings) {
+			ofono_call_settings_remove(md->call_settings);
+			md->call_settings = NULL;
+		}
+		if (md->ussd) {
+			ofono_ussd_remove(md->ussd);
+			md->ussd = NULL;
+		}
+		if (md->netreg) {
+			ofono_netreg_remove(md->netreg);
+			md->netreg = NULL;
+		}
+		if (md->sms) {
+			ofono_sms_remove(md->sms);
+			md->sms = NULL;
+		}
+	}
+}
+
+static void create_online_atoms(struct ofono_modem *modem)
+{
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	md->sim_data.gril = md->ril;
+	md->sim_data.modem = modem;
+	md->sim_data.ril_state_watch = sim_state_watch;
+
+	md->sim = ofono_sim_create(modem, OFONO_RIL_VENDOR_MTK,
+					RILMODEM, &md->sim_data);
+	g_assert(md->sim != NULL);
+
+	/* Radio settings does not depend on the SIM */
+	ofono_radio_settings_create(modem, OFONO_RIL_VENDOR_MTK,
+					MTKMODEM, md->ril);
+
+	if (md->netreg_watch == 0)
+		md->netreg_watch =
+			__ofono_modem_add_atom_watch(modem,
+						OFONO_ATOM_TYPE_NETREG,
+						netreg_watch, modem, NULL);
+}
+
+static void query_type_cb(struct ril_msg *message, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+	int type;
+
+	if (message->error != RIL_E_SUCCESS) {
+		ofono_error("%s: RIL error %s", __func__,
+				ril_error_to_string(message->error));
+		return;
+	}
+
+	type = g_mtk_reply_parse_query_modem_type(md->ril, message);
+	if (type < 0) {
+		ofono_error("%s: parse error", __func__);
+		return;
+	}
+
+	set_fw_type(modem, type);
+
+	create_online_atoms(modem);
+}
+
+static void mtk_post_online(struct ofono_modem *modem)
+{
+	struct mtk_data *md = ofono_modem_get_data(modem);
+	DBG("slot %d", md->slot);
+
+	/* With modem powered we can query the type in krillin */
+	if (g_ril_send(md->ril, MTK_RIL_REQUEST_QUERY_MODEM_TYPE,
+			NULL, query_type_cb, md->modem, NULL) == 0)
+		ofono_error("%s: failure sending QUERY_MODEM_TYPE", __func__);
+
+	/* Register for changes in SIM insertion */
+	g_ril_register(md->ril, MTK_RIL_UNSOL_SIM_PLUG_OUT,
+			sim_removed, modem);
+	g_ril_register(md->ril, MTK_RIL_UNSOL_SIM_PLUG_IN,
+			sim_inserted, modem);
+}
+
+static void exec_pending_online(struct mtk_data *md)
+{
+	struct mtk_data *md_c;
+
+	DBG("");
+
+	mtk_data_0->pending_cb = NULL;
+
+	/* Execute possible pending operation on the other modem */
+
+	md_c = mtk_data_complement(md);
+
+	if (md_c != NULL && md_c->pending_online_cbd) {
+		struct cb_data *pending_cbd = md_c->pending_online_cbd;
+		ofono_modem_online_cb_t pending_cb = pending_cbd->cb;
+
+		mtk_set_online(pending_cbd->user, md_c->pending_online,
+				pending_cb, pending_cbd->data);
+
+		g_free(md_c->pending_online_cbd);
+		md_c->pending_online_cbd = NULL;
+	}
+}
+
+static gboolean sim_switch_failsafe(gpointer user_data)
+{
+	struct mtk_data *md = user_data;
+
+	exec_online_callback(md);
+
+	return FALSE;
+}
+
+static void mtk_sim_mode_cb(struct ril_msg *message, gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	ofono_modem_online_cb_t cb = cbd->cb;
+	struct ofono_modem *modem = cbd->user;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	if (message->error == RIL_E_SUCCESS) {
+		g_ril_print_response_no_args(md->ril, message);
+		/*
+		 * Although the request was successful, radio state might not
+		 * have changed yet. So we wait for the upcoming radio event,
+		 * otherwise RIL requests that depend on radio state will fail.
+		 * If we are switching the 3G slot, we cannot really trust this
+		 * behaviour, so we add a failsafe timer.
+		 */
+		md->online_cb = cb;
+		md->online_data = cbd->data;
+
+		g_timeout_add(T_SIM_SWITCH_FAILSAFE_MS,
+						sim_switch_failsafe, md);
+	} else {
+		ofono_error("%s: RIL error %s", __func__,
+				ril_error_to_string(message->error));
+		CALLBACK_WITH_FAILURE(cb, cbd->data);
+		exec_pending_online(md);
+	}
+}
+
+static int sim_state()
+{
+	int state = mtk_data_0->ofono_online ? SIM_1_ACTIVE : NO_SIM_ACTIVE;
+	if (mtk_data_1 && mtk_data_1->ofono_online)
+		state |= SIM_2_ACTIVE;
+
+	return state;
+}
+
+static void mtk_send_sim_mode(GRilResponseFunc func, gpointer user_data)
+{
+	struct parcel rilp;
+	struct cb_data *cbd = user_data;
+	ofono_modem_online_cb_t cb = NULL;
+	GDestroyNotify notify = NULL;
+	int sim_mode;
+
+	if (cbd != NULL) {
+		notify = g_free;
+		cb = cbd->cb;
+	}
+
+	/* Case of modems with just one slot */
+	if (mtk_data_1 == NULL) {
+		mtk_data_0->pending_cb = NULL;
+
+		if (cbd != NULL) {
+			CALLBACK_WITH_SUCCESS(cb, cbd->data);
+			g_free(cbd);
+		}
+		return;
+	}
+
+	sim_mode = sim_state();
+
+	if (sim_mode == NO_SIM_ACTIVE)
+		sim_mode = MTK_SWITCH_MODE_ALL_INACTIVE;
+
+	g_mtk_request_dual_sim_mode_switch(mtk_data_0->ril, sim_mode, &rilp);
+
+	/* This request is always sent through the main socket */
+	if (g_ril_send(mtk_data_0->ril, MTK_RIL_REQUEST_DUAL_SIM_MODE_SWITCH,
+			&rilp, func, cbd, notify) == 0 && cbd != NULL) {
+		ofono_error("%s: failure sending request", __func__);
+		mtk_data_0->pending_cb = NULL;
+
+		if (cbd != NULL) {
+			CALLBACK_WITH_FAILURE(cb, cbd->data);
+			g_free(cbd);
+		}
+	}
+}
+
+static gboolean no_disconnect_case(gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+
+	ofono_info("%s: Execute pending sim mode switch", __func__);
+	not_disconn_cb_id = 0;
+
+	mtk_send_sim_mode(mtk_sim_mode_cb, cbd);
+
+	return FALSE;
+}
+
+static void poweron_cb(struct ril_msg *message, gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	struct ofono_modem *modem = cbd->user;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+	ofono_modem_online_cb_t cb = cbd->cb;
+
+	DBG("");
+
+	/*
+	 * MTK's rild behavior when a POWERON is sent to it is different
+	 * depending on whether a previous POWEROFF had been sent. When
+	 * the modem is initialized during device startup, POWERON is
+	 * sent without a prior POWEROFF, rild responds with an OK reply,
+	 * and the modem is brought up. Any subsequent POWERON requests
+	 * are sent whenever both modems have been offlined before ( meaning a
+	 * POWEROFF has been sent prior ). rild may respond to the POWERON
+	 * request, but will usually ( always? ) trigger a socket disconnect in
+	 * this case.
+	 *
+	 * This means there's a race condition between the POWERON reply
+	 * callback and the socket disconnect function ( which triggers a
+	 * SIM_MODE_SWITCH request ). In some cases rild is slower than
+	 * usual closing the socket, so we add a timeout to avoid following
+	 * the code path used when there is not a disconnection. Otherwise,
+	 * there would be a race and some requests would return errors due to
+	 * having been sent through the about-to-be-disconnected socket, leaving
+	 * ofono in an inconsistent state. So, we delay sending the
+	 * SIM_MODE_SWITCH for 1s, to allow the disconnect to happen when we
+	 * know that we have sent previously a POWEROFF.
+	 *
+	 * Also, I saw once that sending SIM_MODE while the
+	 * socket was being disconnected provoked a crash due to SIGPIPE being
+	 * issued. The timeout should also fix this.
+	 */
+
+	if (message->error == RIL_E_SUCCESS) {
+		g_ril_print_response_no_args(md->ril, message);
+
+		if (disconnect_expected) {
+			if (not_disconn_cb_id != 0)
+				g_source_remove(not_disconn_cb_id);
+
+			not_disconn_cb_id = g_timeout_add(T_WAIT_DISCONN_MS,
+						no_disconnect_case, cbd);
+		} else {
+			mtk_send_sim_mode(mtk_sim_mode_cb, cbd);
+		}
+	} else {
+		ofono_error("%s RADIO_POWERON error %s", __func__,
+				ril_error_to_string(message->error));
+		CALLBACK_WITH_FAILURE(cb, cbd->data);
+		g_free(cbd);
+	}
+}
+
+static void poweron_disconnect(struct cb_data *cbd)
+{
+	DBG("Execute pending sim mode switch");
+
+	mtk_send_sim_mode(mtk_sim_mode_cb, cbd);
+}
+
+static void poweroff_cb(struct ril_msg *message, gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	ofono_modem_online_cb_t cb = cbd->cb;
+	struct ofono_modem *modem = cbd->user;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	if (message->error == RIL_E_SUCCESS) {
+		g_ril_print_response_no_args(md->ril, message);
+
+		mtk_settings_remove(md->mtk_settings);
+
+		CALLBACK_WITH_SUCCESS(cb, cbd->data);
+	} else {
+		ofono_error("%s: RIL error %s", __func__,
+				ril_error_to_string(message->error));
+		CALLBACK_WITH_FAILURE(cb, cbd->data);
+	}
+}
+
+static int power_on_off(GRil *ril, gboolean on, struct cb_data *cbd)
+{
+	int cancel_id;
+	int req;
+	struct parcel rilp;
+	struct parcel *p_rilp;
+	GRilResponseFunc resp;
+	GDestroyNotify notify;
+	ofono_modem_online_cb_t cb = cbd->cb;
+
+	/* Case of modems with just one slot */
+	if (mtk_data_1 == NULL) {
+		/* Fall back to generic RIL_REQUEST_RADIO_POWER */
+		req = RIL_REQUEST_RADIO_POWER;
+
+		parcel_init(&rilp);
+		parcel_w_int32(&rilp, 1);
+		parcel_w_int32(&rilp, on);
+		g_ril_append_print_buf(ril, "(%d)", on);
+
+		p_rilp = &rilp;
+	} else {
+		req = on ? MTK_RIL_REQUEST_RADIO_POWERON
+			: MTK_RIL_REQUEST_RADIO_POWEROFF;
+		p_rilp = NULL;
+	}
+
+	if (on) {
+		resp = poweron_cb;
+		notify = NULL;
+	} else {
+		resp = poweroff_cb;
+		notify = g_free;
+	}
+
+	cancel_id = g_ril_send(ril, req, p_rilp, resp, cbd, notify);
+	if (cancel_id == 0) {
+		ofono_error("%s: failure sending request", __func__);
+		CALLBACK_WITH_FAILURE(cb, cbd->data);
+		g_free(cbd);
+		return 0;
+	}
+
+	return cancel_id;
+}
+
+static void mtk_set_online(struct ofono_modem *modem, ofono_bool_t online,
+				ofono_modem_online_cb_t callback, void *data)
+{
+	struct mtk_data *md = ofono_modem_get_data(modem);
+	struct cb_data *cbd = cb_data_new(callback, data, modem);
+	ofono_modem_online_cb_t cb = cbd->cb;
+	int current_state, next_state;
+
+	/*
+	 * Serialize online requests to avoid incoherent states. When changing
+	 * the online state of *one* of the modems, we need to send a
+	 * DUAL_SIM_MODE_SWITCH request, which affects *both* modems. Also, when
+	 * we want to online one modem and at that time both modems are
+	 * offline a RADIO_POWERON needs to be sent before DUAL_SIM_MODE_SWITCH,
+	 * with the additional complexity of being disconnected from the rild
+	 * socket while doing the sequence. This can take some time, and we
+	 * cannot change the state of the other modem while the sequence is
+	 * happenig, as DUAL_SIM_MODE_SWITCH affects both states. Therefore, we
+	 * need to do this serialization, which is different from the one done
+	 * per modem by ofono core.
+	 */
+	if (mtk_data_0->pending_cb != NULL) {
+		md->pending_online_cbd = cbd;
+		md->pending_online = online;
+		return;
+	}
+
+	current_state = sim_state();
+
+	md->ofono_online = online;
+
+	/* Changes as md points to either mtk_data_0 or mtk_data_1 variables */
+	next_state = sim_state();
+
+	DBG("setting md_%d->ofono_online to: %d (from %d to %d)",
+		md->slot, online, current_state, next_state);
+
+	if (current_state == next_state) {
+		CALLBACK_WITH_SUCCESS(cb, cbd->data);
+		g_free(cbd);
+		return;
+	}
+
+	/* Reset mtk_data variables */
+	if (online == FALSE)
+		md->sim_status_retries = 0;
+
+	if (current_state == NO_SIM_ACTIVE) {
+		/* Old state was off, need to power on the modem */
+		if (power_on_off(mtk_data_0->ril, TRUE, cbd)) {
+			/* Socket might disconnect... failsafe */
+			mtk_data_0->pending_cb = poweron_disconnect;
+			mtk_data_0->pending_cbd = cbd;
+		}
+	} else if (next_state == NO_SIM_ACTIVE) {
+		/* Disconnection expected for dual SIM only */
+		if (power_on_off(mtk_data_0->ril, FALSE, cbd)
+				&& mtk_data_1 != NULL)
+			disconnect_expected = TRUE;
+	} else {
+		mtk_send_sim_mode(mtk_sim_mode_cb, cbd);
+	}
+}
+
+static void set_online_cb(const struct ofono_error *error, void *data)
+{
+	if (mtk_data_1->ofono_online_target && !mtk_data_1->ofono_online)
+		mtk_set_online(mtk_data_1->modem, TRUE, set_online_cb, NULL);
+}
+
+static void set_offline_cb(const struct ofono_error *error, void *data)
+{
+	if (mtk_data_1->ofono_online)
+		mtk_set_online(mtk_data_1->modem, FALSE, set_offline_cb, NULL);
+	else if (mtk_data_0->ofono_online_target)
+		mtk_set_online(mtk_data_0->modem, TRUE, set_online_cb, NULL);
+	else
+		mtk_set_online(mtk_data_1->modem, TRUE, set_online_cb, NULL);
+}
+
+void mtk_reset_all_modems(void)
+{
+	if (!mtk_data_0->ofono_online && !mtk_data_1->ofono_online)
+		return;
+
+	mtk_data_0->ofono_online_target = mtk_data_0->ofono_online;
+	mtk_data_1->ofono_online_target = mtk_data_1->ofono_online;
+
+	ofono_modem_set_powered(mtk_data_0->modem, FALSE);
+	ofono_modem_set_powered(mtk_data_1->modem, FALSE);
+
+	if (mtk_data_0->ofono_online)
+		mtk_set_online(mtk_data_0->modem, FALSE, set_offline_cb, NULL);
+	else
+		mtk_set_online(mtk_data_1->modem, FALSE, set_offline_cb, NULL);
+}
+
+void mtk_reset_modem(struct ofono_modem *modem)
+{
+
+	if (ofono_modem_get_powered(modem) == FALSE)
+		return;
+
+	ofono_modem_set_powered(modem, FALSE);
+	g_idle_add(mtk_connected, modem);
+}
+
+static void create_atoms_on_connection(struct ofono_modem *modem)
+{
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	md->devinfo = ofono_devinfo_create(modem, OFONO_RIL_VENDOR_MTK,
+						RILMODEM, md->ril);
+
+	/* Create interfaces useful for emergency calls */
+	md->voicecall = ofono_voicecall_create(modem, OFONO_RIL_VENDOR_MTK,
+						MTKMODEM, md->ril);
+	md->callvolume = ofono_call_volume_create(modem, OFONO_RIL_VENDOR_MTK,
+							RILMODEM, md->ril);
+}
+
+static void remove_atoms_on_disconnection(struct ofono_modem *modem)
+{
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	if (__ofono_modem_find_atom(modem, OFONO_ATOM_TYPES_CALL_VOLUME))
+		ofono_call_volume_remove(md->callvolume);
+	md->callvolume = NULL;
+	if (__ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_VOICECALL))
+		ofono_voicecall_remove(md->voicecall);
+	md->voicecall = NULL;
+	if (__ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_DEVINFO))
+		ofono_devinfo_remove(md->devinfo);
+	md->devinfo = NULL;
+}
+
+static void start_slot(struct mtk_data *md, struct socket_data *sock,
+			const char *hex_prefix)
+{
+	ofono_info("Physical slot %d in socket %s", md->slot, sock->path);
+
+	md->ril = sock->ril;
+	md->radio_state = sock->radio_state;
+
+	g_ril_set_slot(md->ril, md->slot);
+
+	if (getenv("OFONO_RIL_TRACE"))
+		g_ril_set_trace(md->ril, TRUE);
+
+	if (getenv("OFONO_RIL_HEX_TRACE"))
+		g_ril_set_debugf(md->ril, mtk_debug, (char *) hex_prefix);
+
+	g_ril_set_disconnect_function(md->ril, socket_disconnected,
+					md->modem);
+
+	g_ril_unregister(sock->ril, sock->radio_state_ev_id);
+
+	g_ril_register(md->ril, RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED,
+			mtk_radio_state_changed, md->modem);
+
+	mtk_connected(md->modem);
+}
+
+static void query_3g_caps_cb(struct ril_msg *message, gpointer user_data)
+{
+	struct socket_data *sock = user_data;
+	struct socket_data *sock_for_md_0, *sock_for_md_1;
+	int slot_3g;
+
+	if (message->error != RIL_E_SUCCESS) {
+		ofono_error("%s: error %s", __func__,
+				ril_error_to_string(message->error));
+		return;
+	}
+
+	slot_3g = g_mtk_reply_parse_get_3g_capability(sock->ril, message);
+
+	/*
+	 * The socket at sock_slot_0 always connects to the slot with 3G
+	 * capabilities, while sock_slot_1 connects to the slot that is just 2G.
+	 * However, the physical slot that owns the 3G capabilities can be
+	 * changed dynamically using a RILd request, so the sockets can connect
+	 * to different physical slots depending on the current configuration.
+	 * We want to keep the relationship between the physical slots and
+	 * the modem names in DBus (so /ril_0 and /ril_1 always refer to the
+	 * same physical slots), so here we assign the sockets needed by
+	 * mtk_data_0 and mtk_data_1 structures to make sure that happens.
+	 */
+	if (slot_3g == MULTISIM_SLOT_0) {
+		sock_for_md_0 = sock_0;
+		sock_for_md_1 = sock_1;
+		mtk_data_0->has_3g = TRUE;
+		mtk_data_1->has_3g = FALSE;
+	} else {
+		sock_for_md_0 = sock_1;
+		sock_for_md_1 = sock_0;
+		mtk_data_0->has_3g = FALSE;
+		mtk_data_1->has_3g = TRUE;
+	}
+
+	start_slot(mtk_data_0, sock_for_md_0, hex_slot_0);
+	start_slot(mtk_data_1, sock_for_md_1, hex_slot_1);
+
+	g_free(sock_0);
+	sock_0 = NULL;
+	g_free(sock_1);
+	sock_1 = NULL;
+}
+
+static void query_3g_caps(struct socket_data *sock)
+{
+	if (g_ril_send(sock->ril, MTK_RIL_REQUEST_GET_3G_CAPABILITY, NULL,
+			query_3g_caps_cb, sock, NULL) <= 0)
+		ofono_error("%s Error querying 3G capabilities", __func__);
+}
+
+static gboolean mtk_connected(gpointer user_data)
+{
+	struct ofono_modem *modem = (struct ofono_modem *) user_data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	ofono_info("[slot %d] CONNECTED", md->slot);
+
+	DBG("calling set_powered(TRUE)");
+
+	if (!ofono_modem_get_powered(modem))
+		ofono_modem_set_powered(modem, TRUE);
+
+	create_atoms_on_connection(modem);
+
+	if (md->pending_cb)
+		md->pending_cb(md->pending_cbd);
+
+	/* Call the function just once */
+	return FALSE;
+}
+
+static gboolean reconnect_rild(gpointer user_data)
+{
+	struct ofono_modem *modem = (struct ofono_modem *) user_data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	ofono_info("[slot %d] trying to reconnect", md->slot);
+
+	if (create_gril(modem) < 0)
+		return TRUE;
+
+	/* Reconnected: do not call this again */
+	return FALSE;
+}
+
+#define WAIT_FOR_RILD_TO_RESTART_MS 8000	/* Milliseconds */
+
+static void socket_disconnected(gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	DBG("slot %d", md->slot);
+
+	/* Atoms use old gril object, remove and recreate later */
+	remove_atoms_on_disconnection(modem);
+
+	g_ril_unref(md->ril);
+	md->ril = NULL;
+
+	md->sensed_plmn_type = MTK_PLMN_TYPE_UNKNOWN;
+	md->suspend_id = INVALID_SUSPEND_ID;
+	if (md->trm_pending) {
+		md->ofono_online = FALSE;
+		ofono_modem_set_powered(md->modem, FALSE);
+		md->trm_pending = FALSE;
+	}
+
+	/* Disconnection happened so we do not call failsafe function */
+	if (not_disconn_cb_id != 0) {
+		g_source_remove(not_disconn_cb_id);
+		not_disconn_cb_id = 0;
+	}
+
+	/* The disconnection happens because rild is re-starting, wait for it */
+	g_timeout_add(WAIT_FOR_RILD_TO_RESTART_MS, reconnect_rild, modem);
+}
+
+static const char sock_slot_0[] = "/dev/socket/rild";
+static const char sock_slot_1[] = "/dev/socket/rild2";
+
+static int create_gril(struct ofono_modem *modem)
+{
+	struct mtk_data *md = ofono_modem_get_data(modem);
+	struct socket_data *sock;
+	int sock_num;
+
+	DBG("slot %d", md->slot);
+
+	if (md->ril != NULL)
+		return 0;
+
+	sock = g_try_malloc0(sizeof(*sock));
+	if (sock == NULL) {
+		ofono_error("%s: Cannot allocate socket_data", __func__);
+		return -ENOMEM;
+	}
+
+	if (md->slot == MULTISIM_SLOT_0) {
+		sock_num = SOCKET_NUM_FOR_DBG_0;
+		sock->path = sock_slot_0;
+	} else {
+		sock_num = SOCKET_NUM_FOR_DBG_1;
+		sock->path = sock_slot_1;
+	}
+
+	/* Opens the socket to RIL */
+	sock->ril = g_ril_new(sock->path, OFONO_RIL_VENDOR_MTK);
+
+	/*
+	 * NOTE: Since AT modems open a tty, and then call
+	 * g_at_chat_new(), they're able to return -EIO if
+	 * the first fails, and -ENOMEM if the second fails.
+	 * in our case, we already return -EIO if the ril_new
+	 * fails.  If this is important, we can create a ril_socket
+	 * abstraction... ( probaby not a bad idea ).
+	 */
+
+	if (sock->ril == NULL) {
+		ofono_error("g_ril_new() failed to connect to %s!", sock->path);
+		g_free(sock);
+		return -EIO;
+	} else if (md->slot == MULTISIM_SLOT_0) {
+		sock_0 = sock;
+	} else {
+		sock_1 = sock;
+	}
+
+	sock->radio_state = RADIO_STATE_UNAVAILABLE;
+	sock->radio_state_ev_id =
+		g_ril_register(sock->ril,
+				RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED,
+				radio_state_changed, sock);
+
+	g_ril_register(sock->ril, MTK_RIL_UNSOL_RESPONSE_PLMN_CHANGED,
+			plmn_changed, modem);
+	g_ril_register(sock->ril, MTK_RIL_UNSOL_RESPONSE_REGISTRATION_SUSPENDED,
+			reg_suspended, modem);
+
+	/* sock_num is negative to avoid confusion with physical slots */
+	g_ril_set_slot(sock->ril, sock_num);
+
+	g_ril_set_vendor_print_msg_id_funcs(sock->ril,
+						mtk_request_id_to_string,
+						mtk_unsol_request_to_string);
+
+	if (getenv("OFONO_RIL_TRACE"))
+		g_ril_set_trace(sock->ril, TRUE);
+
+	if (getenv("OFONO_RIL_HEX_TRACE"))
+		g_ril_set_debugf(sock->ril, mtk_debug, (char *) sock->path);
+
+	return 0;
+}
+
+static gboolean connect_rild(gpointer user_data)
+{
+	struct ofono_modem *modem = (struct ofono_modem *) user_data;
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	ofono_info("Trying to reconnect to slot %d...", md->slot);
+
+	if (md->rild_connect_retries++ < RILD_MAX_CONNECT_RETRIES) {
+		if (create_gril(modem) < 0)
+			return TRUE;
+	} else {
+		ofono_error("Exiting, can't connect to rild.");
+		exit(0);
+	}
+
+	return FALSE;
+}
+
+static int mtk_enable(struct ofono_modem *modem)
+{
+	int ret;
+
+	/* We handle SIM states due to MTK peculiarities */
+	ofono_modem_set_driver_watches_sim(modem, TRUE);
+
+	ret = create_gril(modem);
+	if (ret < 0)
+		g_timeout_add_seconds(RILD_CONNECT_RETRY_TIME_S,
+					connect_rild, modem);
+
+	/*
+	 * We will mark the modem as powered when we receive an event that
+	 * confirms that the radio is in a state different from unavailable
+	 */
+
+	return -EINPROGRESS;
+}
+
+static int mtk_disable(struct ofono_modem *modem)
+{
+	struct mtk_data *md = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	if (md->slot == MULTISIM_SLOT_0 && not_disconn_cb_id != 0) {
+		g_source_remove(not_disconn_cb_id);
+		not_disconn_cb_id = 0;
+	}
+
+	if (md->ofono_online) {
+		md->ofono_online = FALSE;
+		mtk_send_sim_mode(NULL, NULL);
+	}
+
+	return 0;
+}
+
+static struct ofono_modem_driver mtk_driver = {
+	.name = "mtk",
+	.probe = mtk_probe,
+	.remove = mtk_remove,
+	.enable = mtk_enable,
+	.disable = mtk_disable,
+	.pre_sim = mtk_pre_sim,
+	.post_sim = mtk_post_sim,
+	.post_online = mtk_post_online,
+	.set_online = mtk_set_online,
+};
+
+static int mtk_init(void)
+{
+	int retval = ofono_modem_driver_register(&mtk_driver);
+
+	if (retval != 0)
+		DBG("ofono_modem_driver_register returned: %d", retval);
+
+	return retval;
+}
+
+static void mtk_exit(void)
+{
+	DBG("");
+	ofono_modem_driver_unregister(&mtk_driver);
+}
+
+OFONO_PLUGIN_DEFINE(mtk, "MTK modem driver", VERSION,
+			OFONO_PLUGIN_PRIORITY_DEFAULT, mtk_init, mtk_exit)
-- 
2.5.0


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

* [PATCH 7/7] build: Add mtkmodem driver and mtk plugin
  2015-11-13 12:25 [PATCH 0/7] Support for mtk modems Alfonso Sanchez-Beato
                   ` (5 preceding siblings ...)
  2015-11-13 12:25 ` [PATCH 6/7] mtk: Plugin for mtkmodems Alfonso Sanchez-Beato
@ 2015-11-13 12:25 ` Alfonso Sanchez-Beato
  6 siblings, 0 replies; 9+ messages in thread
From: Alfonso Sanchez-Beato @ 2015-11-13 12:25 UTC (permalink / raw)
  To: ofono

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

---
 Makefile.am | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/Makefile.am b/Makefile.am
index fba25ba..6a2acf2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -126,9 +126,28 @@ builtin_sources += plugins/rildev.c
 builtin_modules += ril
 builtin_sources += plugins/ril.c plugins/ril.h
 
+builtin_modules += mtk
+builtin_sources += plugins/mtk.c
+
 builtin_modules += infineon
 builtin_sources += plugins/infineon.c
 
+builtin_modules += mtkmodem
+builtin_sources += drivers/mtkmodem/mtkmodem.h \
+			drivers/mtkmodem/mtkmodem.c \
+			drivers/mtkmodem/mtkutil.h \
+			drivers/mtkmodem/mtkutil.c \
+			drivers/mtkmodem/mtkrequest.h \
+			drivers/mtkmodem/mtkrequest.c \
+			drivers/mtkmodem/mtkunsol.h \
+			drivers/mtkmodem/mtkunsol.c \
+			drivers/mtkmodem/mtkreply.h \
+			drivers/mtkmodem/mtkreply.c \
+			drivers/mtkmodem/voicecall.c \
+			drivers/mtkmodem/gprs.c \
+			drivers/mtkmodem/radio-settings.c \
+			drivers/mtkmodem/mtksettings.c
+
 builtin_modules += rilmodem
 builtin_sources += drivers/rilmodem/rilmodem.h \
 			drivers/rilmodem/vendor.h \
-- 
2.5.0


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

* Re: [PATCH 1/7] gitignore: Ignore rilmodem-cs test binary
  2015-11-13 12:25 ` [PATCH 1/7] gitignore: Ignore rilmodem-cs test binary Alfonso Sanchez-Beato
@ 2015-11-13 16:06   ` Denis Kenzior
  0 siblings, 0 replies; 9+ messages in thread
From: Denis Kenzior @ 2015-11-13 16:06 UTC (permalink / raw)
  To: ofono

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

Hi Alfonso,

On 11/13/2015 06:25 AM, Alfonso Sanchez-Beato wrote:
> ---
>   .gitignore | 1 +
>   1 file changed, 1 insertion(+)
>

Applied, thanks.

Regards,
-Denis


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

end of thread, other threads:[~2015-11-13 16:06 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-11-13 12:25 [PATCH 0/7] Support for mtk modems Alfonso Sanchez-Beato
2015-11-13 12:25 ` [PATCH 1/7] gitignore: Ignore rilmodem-cs test binary Alfonso Sanchez-Beato
2015-11-13 16:06   ` Denis Kenzior
2015-11-13 12:25 ` [PATCH 2/7] include: Add flag for drivers that watch SIM state Alfonso Sanchez-Beato
2015-11-13 12:25 ` [PATCH 3/7] modem: " Alfonso Sanchez-Beato
2015-11-13 12:25 ` [PATCH 4/7] rilmodem: Export functions needed by mtkmodem Alfonso Sanchez-Beato
2015-11-13 12:25 ` [PATCH 5/7] mtkmodem: Add mtkmodem driver Alfonso Sanchez-Beato
2015-11-13 12:25 ` [PATCH 6/7] mtk: Plugin for mtkmodems Alfonso Sanchez-Beato
2015-11-13 12:25 ` [PATCH 7/7] build: Add mtkmodem driver and mtk plugin Alfonso Sanchez-Beato

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.