All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [RFC] GPRS context setup and teardown (doc/dataconnectionmanager-api.txt)
  2009-10-12 21:35 [RFC] GPRS context setup and teardown (doc/dataconnectionmanager-api.txt) Andrzej Zaborowski
@ 2009-10-12 20:01 ` Andrzej Zaborowski
  2009-10-16 18:23   ` Denis Kenzior
  2009-10-23 22:22 ` Denis Kenzior
  1 sibling, 1 reply; 12+ messages in thread
From: Andrzej Zaborowski @ 2009-10-12 20:01 UTC (permalink / raw)
  To: ofono

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

2009/10/12 Andrzej Zaborowski <andrew.zaborowski@intel.com>:
> here's a one-big-patch that adds the DataConnectionManager interface
> according to doc/dataconnectionmanager-api.txt without any glue for
> the multiplexer or PPP setup.  Only AT backend is implemented.

This patch depends on 0001 attached, other patches are small
independent changes that I had in queue.

Regards

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Move-network-registration-status-to-string-functions.patch --]
[-- Type: text/x-patch, Size: 3713 bytes --]

From 6bdfef650f64766f13abaa4b6538d7facdebd6f3 Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <andrew.zaborowski@intel.com>
Date: Mon, 12 Oct 2009 22:37:14 +0200
Subject: [PATCH 1/5] Move network registration status to string functions to common.c

so that they can be used in data connection related network
registration code.
---
 src/common.c  |   44 ++++++++++++++++++++++++++++++++++++++++++++
 src/common.h  |    3 +++
 src/network.c |   44 --------------------------------------------
 3 files changed, 47 insertions(+), 44 deletions(-)

diff --git a/src/common.c b/src/common.c
index b62e34c..1b002bf 100644
--- a/src/common.c
+++ b/src/common.c
@@ -597,3 +597,47 @@ gboolean is_valid_pin(const char *pin)
 
 	return TRUE;
 }
+
+const char *registration_status_to_string(int status)
+{
+	switch (status) {
+	case NETWORK_REGISTRATION_STATUS_NOT_REGISTERED:
+		return "unregistered";
+	case NETWORK_REGISTRATION_STATUS_REGISTERED:
+		return "registered";
+	case NETWORK_REGISTRATION_STATUS_SEARCHING:
+		return "searching";
+	case NETWORK_REGISTRATION_STATUS_DENIED:
+		return "denied";
+	case NETWORK_REGISTRATION_STATUS_UNKNOWN:
+		return "unknown";
+	case NETWORK_REGISTRATION_STATUS_ROAMING:
+		return "roaming";
+	}
+
+	return "";
+}
+
+const char *registration_tech_to_string(int tech)
+{
+	switch (tech) {
+	case ACCESS_TECHNOLOGY_GSM:
+		return "GSM";
+	case ACCESS_TECHNOLOGY_GSM_COMPACT:
+		return "GSMCompact";
+	case ACCESS_TECHNOLOGY_UTRAN:
+		return "UTRAN";
+	case ACCESS_TECHNOLOGY_GSM_EGPRS:
+		return "GSM+EGPRS";
+	case ACCESS_TECHNOLOGY_UTRAN_HSDPA:
+		return "UTRAN+HSDPA";
+	case ACCESS_TECHNOLOGY_UTRAN_HSUPA:
+		return "UTRAN+HSUPA";
+	case ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA:
+		return "UTRAN+HSDPA+HSUPA";
+	case ACCESS_TECHNOLOGY_EUTRAN:
+		return "EUTRAN";
+	default:
+		return "";
+	}
+}
diff --git a/src/common.h b/src/common.h
index 3805e21..1a5c9ed 100644
--- a/src/common.h
+++ b/src/common.h
@@ -135,3 +135,6 @@ const char *ss_control_type_to_string(enum ss_control_type type);
 const char *bearer_class_to_string(enum bearer_class cls);
 
 gboolean is_valid_pin(const char *pin);
+
+const char *registration_status_to_string(int status);
+const char *registration_tech_to_string(int tech);
diff --git a/src/network.c b/src/network.c
index 0e5d55b..efa0bc7 100644
--- a/src/network.c
+++ b/src/network.c
@@ -117,50 +117,6 @@ static inline const char *network_operator_status_to_string(int status)
 	return "unknown";
 }
 
-static inline const char *registration_status_to_string(int status)
-{
-	switch (status) {
-	case NETWORK_REGISTRATION_STATUS_NOT_REGISTERED:
-		return "unregistered";
-	case NETWORK_REGISTRATION_STATUS_REGISTERED:
-		return "registered";
-	case NETWORK_REGISTRATION_STATUS_SEARCHING:
-		return "searching";
-	case NETWORK_REGISTRATION_STATUS_DENIED:
-		return "denied";
-	case NETWORK_REGISTRATION_STATUS_UNKNOWN:
-		return "unknown";
-	case NETWORK_REGISTRATION_STATUS_ROAMING:
-		return "roaming";
-	}
-
-	return "";
-}
-
-static inline const char *registration_tech_to_string(int tech)
-{
-	switch (tech) {
-	case ACCESS_TECHNOLOGY_GSM:
-		return "GSM";
-	case ACCESS_TECHNOLOGY_GSM_COMPACT:
-		return "GSMCompact";
-	case ACCESS_TECHNOLOGY_UTRAN:
-		return "UTRAN";
-	case ACCESS_TECHNOLOGY_GSM_EGPRS:
-		return "GSM+EGPRS";
-	case ACCESS_TECHNOLOGY_UTRAN_HSDPA:
-		return "UTRAN+HSDPA";
-	case ACCESS_TECHNOLOGY_UTRAN_HSUPA:
-		return "UTRAN+HSUPA";
-	case ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA:
-		return "UTRAN+HSDPA+HSUPA";
-	case ACCESS_TECHNOLOGY_EUTRAN:
-		return "EUTRAN";
-	default:
-		return "";
-	}
-}
-
 static void register_callback(const struct ofono_error *error, void *data)
 {
 	struct ofono_netreg *netreg = data;
-- 
1.6.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-Always-return-a-string-from-telephony_error_to_str.patch --]
[-- Type: text/x-patch, Size: 905 bytes --]

From 73938e4e1005672b99f92995c26a5ff41a01d656 Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <andrew.zaborowski@intel.com>
Date: Mon, 12 Oct 2009 22:39:47 +0200
Subject: [PATCH 2/5] Always return a string from telephony_error_to_str.

So that it can be used as a printf argument directly.
---
 src/common.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/common.c b/src/common.c
index 1b002bf..cb79334 100644
--- a/src/common.c
+++ b/src/common.c
@@ -280,14 +280,14 @@ const char *telephony_error_to_str(const struct ofono_error *error)
 		maxentries = sizeof(ceer_errors) / sizeof(struct error_entry);
 		break;
 	default:
-		return 0;
+		return "Unknown error type";
 	}
 
 	for (i = 0; i < maxentries; i++)
 		if (e[i].error == error->error)
 			return e[i].str;
 
-	return 0;
+	return "Unknown error";
 }
 
 int mmi_service_code_to_bearer_class(int code)
-- 
1.6.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: 0003-Emit-debug-message-instead-of-an-error-when-SMS-stor.patch --]
[-- Type: text/x-patch, Size: 785 bytes --]

From 3f771555079fa77045596fa1bc2b516970a43290 Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <andrew.zaborowski@intel.com>
Date: Mon, 12 Oct 2009 22:41:35 +0200
Subject: [PATCH 3/5] Emit debug message instead of an error when SMS storage is empty on init.

---
 drivers/atmodem/sms.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/atmodem/sms.c b/drivers/atmodem/sms.c
index d425818..b3d6ff5 100644
--- a/drivers/atmodem/sms.c
+++ b/drivers/atmodem/sms.c
@@ -548,7 +548,7 @@ static void at_cmgl_cb(gboolean ok, GAtResult *result, gpointer user_data)
 	struct ofono_sms *sms = user_data;
 
 	if (!ok)
-		ofono_error("Initial listing SMS storage failed!");
+		ofono_debug("Initial listing SMS storage failed!");
 
 	at_cmgl_done(sms);
 }
-- 
1.6.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #5: 0004-Don-t-crash-on-sms-messages-containing-only-UDH.patch --]
[-- Type: text/x-patch, Size: 682 bytes --]

From fc0cb192de7cb0f622aa9e6d6879865de2fe169b Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <andrew.zaborowski@intel.com>
Date: Mon, 12 Oct 2009 22:43:04 +0200
Subject: [PATCH 4/5] Don't crash on sms messages containing only UDH.

---
 src/smsutil.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/src/smsutil.c b/src/smsutil.c
index a618d07..d7e81c7 100644
--- a/src/smsutil.c
+++ b/src/smsutil.c
@@ -2133,6 +2133,9 @@ char *sms_decode_text(GSList *sms_list)
 
 		udl_in_bytes = sms_udl_in_bytes(udl, dcs);
 
+		if (udl_in_bytes == taken)
+			continue;
+
 		if (charset == SMS_CHARSET_7BIT) {
 			unsigned char buf[160];
 			long written;
-- 
1.6.1


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

* [RFC] GPRS context setup and teardown (doc/dataconnectionmanager-api.txt)
@ 2009-10-12 21:35 Andrzej Zaborowski
  2009-10-12 20:01 ` Andrzej Zaborowski
  2009-10-23 22:22 ` Denis Kenzior
  0 siblings, 2 replies; 12+ messages in thread
From: Andrzej Zaborowski @ 2009-10-12 21:35 UTC (permalink / raw)
  To: ofono

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

Hi,
here's a one-big-patch that adds the DataConnectionManager interface
according to doc/dataconnectionmanager-api.txt without any glue for
the multiplexer or PPP setup.  Only AT backend is implemented.

One issue with the AT implementation of the api is that "Powered" (a
read-write property) can be set independently of "Attached" (read-only
property) and remain set when "Attached" is clear.  The semantics would
be that the network doesn't have resources to let the modem attach,
but the modem waits for the resources to become available and then
attaches.  On AT the modem is in this state only when executing +CGATT,
so currently the code will rerun +CGATT as soon as the previous one
returns with error, probably starving other commands.  A possible
workaround would be for "Powered" to flip back to False after the modem
fails to attach once, or give up on having separate properties.
Alternatively we could re-try to attach periodically but on one modem
I've tried +CGATT fails after about 1 minute (that's the Calypso) and
on another only about 0.5s (Nokia phones with AT emulation).

When "Powered"  is set and "RoamingAllowed" is clear and we manage to
attach and find that we're roaming, ofono resets "Powered".

We may want to catch the user trying to dial *99***1# which is the
backwards compatibility quirk for old modems (same way ofono parses
USSD strings).
---
 Makefile.am                       |   12 +-
 doc/dataconnectionmanager-api.txt |    2 +-
 drivers/atmodem/atmodem.c         |    2 +
 drivers/atmodem/atmodem.h         |    3 +
 drivers/atmodem/data-connection.c |  649 ++++++++++++++++++++++
 include/data-connection.h         |   89 +++
 include/types.h                   |   10 +
 plugins/phonesim.c                |    3 +
 src/data-connection.c             | 1074 +++++++++++++++++++++++++++++++++++++
 src/ofono.h                       |    2 +
 10 files changed, 1841 insertions(+), 5 deletions(-)
 create mode 100644 drivers/atmodem/data-connection.c
 create mode 100644 include/data-connection.h
 create mode 100644 src/data-connection.c

diff --git a/Makefile.am b/Makefile.am
index cf84bf7..600dd08 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -10,7 +10,8 @@ include_HEADERS = include/log.h include/plugin.h include/history.h \
 			include/phonebook.h include/ssn.h include/ussd.h \
 			include/sms.h include/sim.h include/message-waiting.h \
 			include/netreg.h include/voicecall.h include/devinfo.h \
-			include/cbs.h include/call-volume.h
+			include/cbs.h include/call-volume.h \
+			include/data-connection.h
 
 nodist_include_HEADERS = include/version.h
 
@@ -108,7 +109,8 @@ builtin_sources += $(gatchat_sources) \
 				drivers/atmodem/call-volume.c \
 				drivers/atmodem/vendor.h \
 				drivers/atmodem/atutil.h \
-				drivers/atmodem/atutil.c
+				drivers/atmodem/atutil.c \
+				drivers/atmodem/data-connection.c
 
 builtin_modules += calypsomodem
 builtin_sources += drivers/atmodem/atutil.h \
@@ -165,7 +167,8 @@ src_ofonod_SOURCES = $(gdbus_sources) $(builtin_sources) \
 			src/ssn.c src/call-barring.c src/sim.c \
 			src/phonebook.c src/history.c src/message-waiting.c \
 			src/simutil.h src/simutil.c src/storage.h \
-			src/storage.c src/cbs.c src/watch.c src/call-volume.c
+			src/storage.c src/cbs.c src/watch.c src/call-volume.c \
+			src/data-connection.c
 
 src_ofonod_LDADD = $(builtin_libadd) \
 			@GLIB_LIBS@ @GTHREAD_LIBS@ @DBUS_LIBS@ -ldl
@@ -197,7 +200,8 @@ doc_files = doc/overview.txt doc/ofono-paper.txt \
 		doc/manager-api.txt doc/modem-api.txt doc/network-api.txt \
 			doc/voicecallmanager-api.txt doc/voicecall-api.txt \
 			doc/call-forwarding-api.txt doc/call-settings-api.txt \
-			doc/call-meter-api.txt
+			doc/call-meter-api.txt \
+			doc/dataconnectionmanager-api.txt
 
 test_files = test/test-manager test/test-modem test/test-voicecall \
 		test/test-network-registration test/test-phonebook \
diff --git a/doc/dataconnectionmanager-api.txt b/doc/dataconnectionmanager-api.txt
index e2bcf7c..982005e 100644
--- a/doc/dataconnectionmanager-api.txt
+++ b/doc/dataconnectionmanager-api.txt
@@ -42,7 +42,7 @@ Signals		PropertyChanged(string property, variant value)
 
 Properties	array{object} PrimaryContexts [readonly]
 
-			List of all primary contexts objects.
+			List of all primary context objects.
 
 		boolean Attached [readonly]
 
diff --git a/drivers/atmodem/atmodem.c b/drivers/atmodem/atmodem.c
index 7cfcf6a..026d8e0 100644
--- a/drivers/atmodem/atmodem.c
+++ b/drivers/atmodem/atmodem.c
@@ -48,6 +48,7 @@ static int atmodem_init(void)
 	at_netreg_init();
 	at_cbs_init();
 	at_call_volume_init();
+	at_data_connection_init();
 
 	return 0;
 }
@@ -68,6 +69,7 @@ static void atmodem_exit(void)
 	at_voicecall_exit();
 	at_cbs_exit();
 	at_call_volume_exit();
+	at_data_connection_exit();
 }
 
 OFONO_PLUGIN_DEFINE(atmodem, "AT modem driver", VERSION,
diff --git a/drivers/atmodem/atmodem.h b/drivers/atmodem/atmodem.h
index 8c61073..46c3780 100644
--- a/drivers/atmodem/atmodem.h
+++ b/drivers/atmodem/atmodem.h
@@ -62,3 +62,6 @@ extern void at_cbs_exit();
 
 extern void at_call_volume_init();
 extern void at_call_volume_exit();
+
+extern void at_data_connection_init();
+extern void at_data_connection_exit();
diff --git a/drivers/atmodem/data-connection.c b/drivers/atmodem/data-connection.c
new file mode 100644
index 0000000..77e9fa2
--- /dev/null
+++ b/drivers/atmodem/data-connection.c
@@ -0,0 +1,649 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2009  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/data-connection.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "atmodem.h"
+
+static const char *cgdcont_prefix[] = { "+CGDCONT:", NULL };
+static const char *cgact_prefix[] = { "+CGACT:", NULL };
+static const char *none_prefix[] = { NULL };
+
+struct data_connection_data {
+	GSList *primary_id_range;
+	GSList *contexts;
+	GSList *new_contexts; /* Not yet defined contexts */
+	GAtChat *chat;
+};
+
+struct set_attached_req {
+	struct ofono_data_connection *dc;
+	int attached;
+	ofono_data_connection_cb_t cb;
+	void *data;
+};
+
+struct set_active_req {
+	struct ofono_data_connection *dc;
+	struct ofono_data_context *ctx;
+	int active;
+	ofono_data_connection_cb_t cb;
+	void *data;
+};
+
+static gint context_id_compare(gconstpointer a, gconstpointer b)
+{
+	const struct ofono_data_context *ctxa = a;
+	const gint *id = b;
+
+	return ctxa->id - *id;
+}
+
+static gint context_compare(gconstpointer a, gconstpointer b)
+{
+	const struct ofono_data_context *ctxa = a;
+	const struct ofono_data_context *ctxb = a;
+
+	return ctxa->id - ctxb->id;
+}
+
+static void context_free(struct ofono_data_context *ctx)
+{
+	if (ctx->apn)
+		g_free(ctx->apn);
+
+	if (ctx->username) {
+		memset(ctx->username, 0, strlen(ctx->username));
+		g_free(ctx->username);
+	}
+
+	if (ctx->password) {
+		memset(ctx->password, 0, strlen(ctx->password));
+		g_free(ctx->password);
+	}
+
+	g_free(ctx);
+}
+
+static unsigned int find_next_primary_id(struct data_connection_data *d)
+{
+	GSList *l;
+	gint i, *range;
+
+	for (l = d->primary_id_range; l; l = l->next)
+		for (range = l->data, i = range[0]; i <= range[1]; i++)
+			if (!g_slist_find_custom(d->contexts, &i,
+							context_id_compare))
+				return i;
+
+	return 0;
+}
+
+static void detached(struct ofono_data_connection *dc)
+{
+	struct data_connection_data *dcd = ofono_data_connection_get_data(dc);
+	GSList *l;
+	struct ofono_data_context *ctx;
+
+	for (l = dcd->contexts; l; l = l->next) {
+		ctx = l->data;
+		if (ctx->active) {
+			ctx->active = 0;
+
+			ofono_data_connection_deactivated(dc, ctx->id);
+		}
+	}
+}
+
+static void at_cgatt_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct set_attached_req *req = user_data;
+	struct ofono_error error;
+
+	dump_response("cgatt_cb", ok, result);
+	decode_at_error(&error, g_at_result_final_response(result));
+
+	if (ok && !req->attached)
+		detached(req->dc);
+
+	req->cb(&error, req->data);
+}
+
+static void at_ps_set_attached(struct ofono_data_connection *dc,
+				int attached, ofono_data_connection_cb_t cb,
+				void *data)
+{
+	struct data_connection_data *dcd = ofono_data_connection_get_data(dc);
+	struct set_attached_req *req;
+	char buf[64];
+
+	req = g_new0(struct set_attached_req, 1);
+	if (!req)
+		goto error;
+
+	req->dc = dc;
+	req->attached = attached;
+	req->cb = cb;
+	req->data = data;
+
+	sprintf(buf, "AT+CGATT=%i", attached ? 1 : 0);
+
+	if (g_at_chat_send(dcd->chat, buf, none_prefix,
+				at_cgatt_cb, req, g_free) > 0)
+		return;
+
+error:
+	if (req)
+		g_free(req);
+
+	CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_cgact_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct set_active_req *req = user_data;
+	struct data_connection_data *dcd =
+		ofono_data_connection_get_data(req->dc);
+	struct ofono_error error;
+	GSList *l;
+	struct ofono_data_context *ctx;
+
+	dump_response("cgact_cb", ok, result);
+	decode_at_error(&error, g_at_result_final_response(result));
+
+	if (ok) {
+		if (req->ctx) {
+			req->ctx->active = req->active;
+
+			if (!req->active)
+				ofono_data_connection_deactivated(req->dc,
+								req->ctx->id);
+		} else
+			for (l = dcd->contexts; l; l = l->next) {
+				ctx = l->data;
+
+				if (g_slist_find(dcd->new_contexts, ctx))
+					continue;
+
+				ctx->active = req->active;
+
+				if (!req->active)
+					ofono_data_connection_deactivated(
+							req->dc, ctx->id);
+			}
+	}
+
+	req->cb(&error, req->data);
+}
+
+static void at_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct set_active_req *req = user_data;
+	struct data_connection_data *dcd =
+		ofono_data_connection_get_data(req->dc);
+	struct ofono_error error;
+	char buf[64];
+
+	dump_response("cgdcont_cb", ok, result);
+	decode_at_error(&error, g_at_result_final_response(result));
+
+	if (!ok) {
+		req->cb(&error, req->data);
+
+		g_free(req);
+		return;
+	}
+
+	/* Context is no longer undefined */
+	dcd->new_contexts = g_slist_remove(dcd->new_contexts, req->ctx);
+
+	sprintf(buf, "AT+CGACT=1,%u", req->ctx->id);
+
+	if (g_at_chat_send(dcd->chat, buf, none_prefix,
+				at_cgact_cb, req, g_free) > 0)
+		return;
+
+	CALLBACK_WITH_FAILURE(req->cb, req->data);
+
+	g_free(req);
+}
+
+static void at_pdp_set_active(struct ofono_data_connection *dc, unsigned id,
+				int active, ofono_data_connection_cb_t cb,
+				void *data)
+{
+	struct data_connection_data *dcd = ofono_data_connection_get_data(dc);
+	struct set_active_req *req = NULL;
+	char buf[64];
+	struct ofono_data_context *ctx;
+	gint cid = id;
+	int len;
+	GSList *l;
+
+	l = g_slist_find_custom(dcd->contexts, &cid, context_id_compare);
+	if (!l)
+		goto error;
+
+	ctx = l->data;
+
+	req = g_new0(struct set_active_req, 1);
+	if (!req)
+		goto error;
+
+	req->dc = dc;
+	req->ctx = ctx;
+	req->active = active;
+	req->cb = cb;
+	req->data = data;
+
+	if (active) {
+		len = sprintf(buf, "AT+CGDCONT=%u,\"IP\"", id);
+		if (ctx->apn)
+			snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"",
+					ctx->apn);
+
+		if (g_at_chat_send(dcd->chat, buf, none_prefix,
+					at_cgdcont_cb, req, NULL) > 0)
+			return;
+	} else {
+		sprintf(buf, "AT+CGACT=0,%u", id);
+
+		if (g_at_chat_send(dcd->chat, buf, none_prefix,
+					at_cgact_cb, req, g_free) > 0)
+			return;
+	}
+
+error:
+	if (req)
+		g_free(req);
+
+	CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_pdp_set_active_all(struct ofono_data_connection *dc,
+				int active, ofono_data_connection_cb_t cb,
+				void *data)
+{
+	struct data_connection_data *dcd = ofono_data_connection_get_data(dc);
+	struct set_active_req *req;
+	char buf[64];
+
+	req = g_new0(struct set_active_req, 1);
+	if (!req)
+		goto error;
+
+	req->dc = dc;
+	req->active = active;
+	req->cb = cb;
+	req->data = data;
+
+	sprintf(buf, "AT+CGACT=%i", active ? 1 : 0);
+
+	if (g_at_chat_send(dcd->chat, buf, none_prefix,
+				at_cgact_cb, req, g_free) > 0)
+		return;
+
+error:
+	if (req)
+		g_free(req);
+
+	CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void at_pdp_alloc(struct ofono_data_connection *dc,
+				ofono_data_connection_alloc_cb_t cb,
+				void *data)
+{
+	struct data_connection_data *dcd = ofono_data_connection_get_data(dc);
+	struct ofono_data_context *ctx;
+	struct ofono_error e;
+	unsigned id = find_next_primary_id(dcd);
+
+	if (!id) {
+		CALLBACK_WITH_FAILURE(cb, NULL, data);
+
+		return;
+	}
+
+	ctx = g_try_new0(struct ofono_data_context, 1);
+	if (!ctx) {
+		CALLBACK_WITH_FAILURE(cb, NULL, data);
+
+		return;
+	}
+
+	ctx->id = id;
+	ctx->apn = g_strdup("");
+	ctx->username = g_strdup("");
+	ctx->password = g_strdup("");
+
+	dcd->new_contexts = g_slist_insert_sorted(dcd->new_contexts,
+						ctx, context_compare);
+	dcd->contexts = g_slist_insert_sorted(dcd->contexts,
+						ctx, context_compare);
+
+	/* The context will be defined (+CGDCONT) lazily, once it's needed
+	 * and the parameters are already set in ctx.  Right now just call
+	 * back */
+	e.type = OFONO_ERROR_TYPE_NO_ERROR;
+	e.error = 0;
+	cb(&e, ctx, data);
+
+	ofono_data_connection_notify(dc, ctx);
+}
+
+static void at_pdp_undefine_cb(gboolean ok, GAtResult *result,
+				gpointer user_data)
+{
+	dump_response("undefine_cb", ok, result);
+
+	if (!ok)
+		ofono_error("Undefining primary context failed");
+}
+
+static void at_pdp_free(struct ofono_data_connection *dc, unsigned id,
+			ofono_data_connection_cb_t cb, void *data)
+{
+	struct data_connection_data *dcd = ofono_data_connection_get_data(dc);
+	struct ofono_error e;
+	char buf[64];
+	struct ofono_data_context *ctx;
+	GSList *l;
+	gint cid = id;
+
+	l = g_slist_find_custom(dcd->contexts, &cid, context_id_compare);
+	if (!l) {
+		CALLBACK_WITH_FAILURE(cb, data);
+
+		return;
+	}
+
+	ctx = l->data;
+	if (ctx->active) {
+		CALLBACK_WITH_FAILURE(cb, data);
+
+		return;
+	}
+
+	/* We can call back already -- even if the request to undefine
+	 * the context fails, the ID can be re-used.  */
+	e.type = OFONO_ERROR_TYPE_NO_ERROR;
+	e.error = 0;
+	cb(&e, data);
+
+	context_free(ctx);
+	dcd->contexts = g_slist_remove(dcd->contexts, ctx);
+
+	if (g_slist_find(dcd->new_contexts, ctx)) {
+		dcd->new_contexts = g_slist_remove(dcd->new_contexts, ctx);
+		return;
+	}
+
+	sprintf(buf, "AT+CGDCONT=%u", id);
+
+	g_at_chat_send(dcd->chat, buf, none_prefix,
+			at_pdp_undefine_cb, NULL, NULL);
+}
+
+static void at_cgact_read_cb(gboolean ok, GAtResult *result,
+				gpointer user_data)
+{
+	struct ofono_data_connection *dc = user_data;
+	struct data_connection_data *dcd = ofono_data_connection_get_data(dc);
+	gint cid, state;
+	GAtResultIter iter;
+	struct ofono_data_context *ctx;
+	GSList *l;
+
+	dump_response("cgact_read_cb", ok, result);
+
+	if (!ok)
+		return;
+
+	while (g_at_result_iter_next(&iter, "+CGACT:")) {
+		if (!g_at_result_iter_next_number(&iter, &cid))
+			continue;
+
+		if (!g_at_result_iter_next_number(&iter, &state))
+			continue;
+
+		l = g_slist_find_custom(dcd->contexts, &cid,
+					context_id_compare);
+		if (!l)
+			continue;
+
+		ctx = l->data;
+		if (ctx->active != state) {
+			ctx->active = state;
+
+			if (state)
+				continue;
+
+			ofono_data_connection_deactivated(dc, ctx->id);
+		}
+	}
+}
+
+static void cgev_notify(GAtResult *result, gpointer user_data)
+{
+	struct ofono_data_connection *dc = user_data;
+	struct data_connection_data *dcd = ofono_data_connection_get_data(dc);
+	GAtResultIter iter;
+	const char *event;
+
+	if (!g_at_result_iter_next(&iter, "+CGEV:"))
+		return;
+
+	if (!g_at_result_iter_next_unquoted_string(&iter, &event))
+		return;
+
+	if (g_str_has_prefix(event, "REJECT "))
+		return;
+
+	if (g_str_has_prefix(event, "NW REACT ") ||
+			g_str_has_prefix(event, "NW DEACT ") ||
+			g_str_has_prefix(event, "ME DEACT ")) {
+		/* Ask what primary contexts are active now */
+		g_at_chat_send(dcd->chat, "AT+CGACT?", cgact_prefix,
+				at_cgact_read_cb, dc, NULL);
+
+		return;
+	}
+
+	if (g_str_has_prefix(event, "NW DETACH ") ||
+			g_str_has_prefix(event, "ME DETACH ")) {
+		detached(dc);
+
+		ofono_data_connection_detached(dc);
+
+		return;
+	}
+
+	if (g_str_has_prefix(event, "NW CLASS ") ||
+			g_str_has_prefix(event, "ME CLASS "))
+		return;
+}
+
+static void cgreg_notify(GAtResult *result, gpointer user_data)
+{
+	struct ofono_data_connection *dc = user_data;
+	GAtResultIter iter;
+	gint status, tech = -1;
+	int lac = -1, ci = -1;
+	const char *str;
+
+	dump_response("cgreg_notify", TRUE, result);
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+CGREG:"))
+		return;
+
+	g_at_result_iter_next_number(&iter, &status);
+
+	if (g_at_result_iter_next_string(&iter, &str))
+		lac = strtol(str, NULL, 16);
+	else
+		goto out;
+
+	if (g_at_result_iter_next_string(&iter, &str))
+		ci = strtol(str, NULL, 16);
+	else
+		goto out;
+
+	g_at_result_iter_next_number(&iter, &tech);
+
+out:
+	ofono_debug("cgreg_notify: %d, %d, %d, %d", status, lac, ci, tech);
+
+	if (status != 1 && status != 5)
+		detached(dc);
+
+	ofono_data_netreg_status_notify(dc, status, lac, ci, tech);
+}
+
+static void at_cgdcont_test_cb(gboolean ok, GAtResult *result,
+				gpointer user_data)
+{
+	struct ofono_data_connection *dc = user_data;
+	struct data_connection_data *dcd = ofono_data_connection_get_data(dc);
+	GAtResultIter iter;
+	gint range[2];
+	GSList *ranges = NULL;
+	const char *pdp_type;
+
+	if (!ok)
+		goto error;
+
+	g_at_result_iter_init(&iter, result);
+
+	while (g_at_result_iter_next(&iter, "+CGDCONT:")) {
+		if (!g_at_result_iter_open_list(&iter))
+			goto next;
+
+		while (g_at_result_iter_next_range(&iter, &range[0],
+							&range[1]))
+			ranges = g_slist_prepend(ranges,
+					g_memdup(range, sizeof(range)));
+
+		if (!g_at_result_iter_close_list(&iter))
+			goto next;
+
+		if (!ranges || range[1] < range[0])
+			goto next;
+
+		if (!g_at_result_iter_next_string(&iter, &pdp_type))
+			goto next;
+
+		/* We look for IP PDPs */
+		if (!strcmp(pdp_type, "IP"))
+			break;
+
+next:
+		if (ranges) {
+			g_slist_foreach(ranges, (GFunc) g_free, NULL);
+			g_slist_free(ranges);
+			ranges = NULL;
+		}
+	}
+	if (!ranges)
+		goto error;
+
+	dcd->primary_id_range = g_slist_reverse(ranges);
+
+	ofono_debug("data_connection_init: registering to notifications");
+
+	g_at_chat_register(dcd->chat, "+CGEV:", cgev_notify, FALSE, dc, NULL);
+	g_at_chat_register(dcd->chat, "+CGREG:", cgreg_notify, FALSE, dc, NULL);
+
+	ofono_data_connection_register(dc);
+
+	return;
+
+error:
+	ofono_data_connection_remove(dc);
+}
+
+static int at_data_connection_probe(struct ofono_data_connection *dc,
+					unsigned int vendor, void *data)
+{
+	GAtChat *chat = data;
+	struct data_connection_data *dcd;
+
+	dcd = g_new0(struct data_connection_data, 1);
+	dcd->chat = chat;
+
+	ofono_data_connection_set_data(dc, dcd);
+
+	g_at_chat_send(chat, "AT+CGREG=2", NULL, NULL, NULL, NULL);
+	g_at_chat_send(chat, "AT+CGAUTO=0", NULL, NULL, NULL, NULL);
+	g_at_chat_send(chat, "AT+CGEREP=2,1", NULL, NULL, NULL, NULL);
+	g_at_chat_send(chat, "AT+CGDCONT=?", cgdcont_prefix,
+			at_cgdcont_test_cb, dc, NULL);
+	return 0;
+}
+
+static void at_data_connection_remove(struct ofono_data_connection *dc)
+{
+	struct data_connection_data *dcd = ofono_data_connection_get_data(dc);
+
+	g_slist_foreach(dcd->contexts, (GFunc) context_free, NULL);
+	g_slist_free(dcd->contexts);
+	g_slist_free(dcd->new_contexts);
+	g_free(dcd);
+}
+
+static struct ofono_data_connection_driver driver = {
+	.name			= "atmodem",
+	.probe			= at_data_connection_probe,
+	.remove			= at_data_connection_remove,
+	.set_attached		= at_ps_set_attached,
+	.set_active		= at_pdp_set_active,
+	.set_active_all		= at_pdp_set_active_all,
+	.create_context		= at_pdp_alloc,
+	.remove_context		= at_pdp_free,
+};
+
+void at_data_connection_init()
+{
+	ofono_data_connection_driver_register(&driver);
+}
+
+void at_data_connection_exit()
+{
+	ofono_data_connection_driver_unregister(&driver);
+}
diff --git a/include/data-connection.h b/include/data-connection.h
new file mode 100644
index 0000000..24b68a8
--- /dev/null
+++ b/include/data-connection.h
@@ -0,0 +1,89 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2009  Intel Corporation. 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
+ *
+ */
+
+#ifndef __OFONO_DATA_CONNECTION_H
+#define __OFONO_DATA_CONNECTION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ofono/types.h>
+
+struct ofono_data_connection;
+
+typedef void (*ofono_data_connection_cb_t)(const struct ofono_error *error,
+						void *data);
+
+typedef void (*ofono_data_connection_alloc_cb_t)(
+						const struct ofono_error *error,
+						struct ofono_data_context *ctx,
+						void *data);
+
+struct ofono_data_connection_driver {
+	const char *name;
+	int (*probe)(struct ofono_data_connection *dc, unsigned int vendor,
+			void *data);
+	void (*remove)(struct ofono_data_connection *dc);
+	void (*set_attached)(struct ofono_data_connection *dc,
+				int attached, ofono_data_connection_cb_t cb,
+				void *data);
+	void (*set_active)(struct ofono_data_connection *dc, unsigned id,
+				int active, ofono_data_connection_cb_t cb,
+				void *data);
+	void (*set_active_all)(struct ofono_data_connection *dc,
+				int active, ofono_data_connection_cb_t cb,
+				void *data);
+	void (*create_context)(struct ofono_data_connection *dc,
+				ofono_data_connection_alloc_cb_t cb,
+				void *data);
+	void (*remove_context)(struct ofono_data_connection *dc, unsigned id,
+				ofono_data_connection_cb_t cb, void *data);
+};
+
+void ofono_data_connection_notify(struct ofono_data_connection *dc,
+					struct ofono_data_context *ctx);
+void ofono_data_connection_deactivated(struct ofono_data_connection *dc,
+					unsigned id);
+void ofono_data_connection_detached(struct ofono_data_connection *dc);
+void ofono_data_netreg_status_notify(struct ofono_data_connection *dc,
+					int status, int lac, int ci, int tech);
+
+int ofono_data_connection_driver_register(
+				const struct ofono_data_connection_driver *d);
+void ofono_data_connection_driver_unregister(
+				const struct ofono_data_connection_driver *d);
+
+struct ofono_data_connection *ofono_data_connection_create(
+		struct ofono_modem *modem, unsigned int vendor,
+		const char *driver, void *data);
+void ofono_data_connection_register(struct ofono_data_connection *dc);
+void ofono_data_connection_remove(struct ofono_data_connection *dc);
+
+void ofono_data_connection_set_data(struct ofono_data_connection *dc,
+					void *data);
+void *ofono_data_connection_get_data(struct ofono_data_connection *dc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OFONO_DATA_CONNECTION_H */
diff --git a/include/types.h b/include/types.h
index 6a9681d..44428fb 100644
--- a/include/types.h
+++ b/include/types.h
@@ -91,6 +91,16 @@ struct ofono_call {
 	int clip_validity;
 };
 
+struct ofono_data_context {
+	unsigned id;
+	int type;
+	int direction;
+	int active;
+	char *apn;
+	char *username;
+	char *password;
+};
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/plugins/phonesim.c b/plugins/phonesim.c
index 02ce443..26e170c 100644
--- a/plugins/phonesim.c
+++ b/plugins/phonesim.c
@@ -54,6 +54,7 @@
 #include <ofono/ssn.h>
 #include <ofono/ussd.h>
 #include <ofono/voicecall.h>
+#include <ofono/data-connection.h>
 
 #include <drivers/atmodem/vendor.h>
 
@@ -285,6 +286,8 @@ static void phonesim_post_sim(struct ofono_modem *modem)
 		ofono_cbs_create(modem, 0, "atmodem", data->chat);
 	}
 
+	ofono_data_connection_create(modem, 0, "atmodem", data->chat);
+
 	mw = ofono_message_waiting_create(modem);
 	if (mw)
 		ofono_message_waiting_register(mw);
diff --git a/src/data-connection.c b/src/data-connection.c
new file mode 100644
index 0000000..eab36c7
--- /dev/null
+++ b/src/data-connection.c
@@ -0,0 +1,1074 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2009  Intel Corporation. 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 <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "ofono.h"
+
+#include "common.h"
+
+#define DATA_CONNECTION_MANAGER_INTERFACE "org.ofono.DataConnectionManager"
+#define DATA_CONTEXT_INTERFACE "org.ofono.PrimaryDataContext"
+
+#define DATA_CONNECTION_FLAG_ATTACHING 0x1
+
+static GSList *g_drivers = NULL;
+
+struct ofono_data_connection {
+	GSList *contexts;
+	int attached;
+	int roaming_allowed;
+	int powered;
+	int status;
+	int location;
+	int cellid;
+	int technology;
+
+	int flags;
+	struct context *current_context;
+	DBusMessage *pending;
+	const struct ofono_data_connection_driver *driver;
+	void *driver_data;
+	struct ofono_atom *atom;
+};
+
+struct context {
+	struct ofono_data_context *context;
+	struct ofono_data_connection *dc;
+};
+
+static void dc_netreg_update(struct ofono_data_connection *dc);
+
+static gint context_compare(gconstpointer a, gconstpointer b)
+{
+	const struct context *ctxa = a;
+	const struct context *ctxb = a;
+
+	return ctxa->context->id - ctxb->context->id;
+}
+
+enum {
+	DATA_CONTEXT_TYPE_INTERNET,
+	DATA_CONTEXT_TYPE_MMS,
+	DATA_CONTEXT_TYPE_WAP,
+};
+
+static inline const char *data_context_type_to_string(int type)
+{
+	switch (type) {
+	case DATA_CONTEXT_TYPE_INTERNET:
+		return "internet";
+	case DATA_CONTEXT_TYPE_MMS:
+		return "mms";
+	case DATA_CONTEXT_TYPE_WAP:
+		return "wap";
+	}
+
+	return NULL;
+}
+
+static const char *dc_build_context_path(struct ofono_data_connection *dc,
+					const struct ofono_data_context *ctx)
+{
+	static char path[256];
+
+	snprintf(path, sizeof(path), "%s/primarycontext%02u",
+			__ofono_atom_get_path(dc->atom), ctx->id);
+
+	return path;
+}
+
+static struct context *dc_context_by_path(
+		struct ofono_data_connection *dc, const char *ctx_path)
+{
+	const char *path = __ofono_atom_get_path(dc->atom);
+	GSList *l;
+	unsigned id;
+
+	if (!g_str_has_prefix(ctx_path, path))
+		return NULL;
+
+	if (sscanf(ctx_path + strlen(path), "/primarycontext%2u", &id) != 1)
+		return NULL;
+
+	for (l = dc->contexts; l; l = l->next) {
+		struct context *ctx = l->data;
+
+		if (ctx->context->id == id)
+			return ctx;
+	}
+
+	return NULL;
+}
+
+static DBusMessage *dc_get_context_properties(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct context *ctx = data;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	dbus_bool_t value;
+	const char *type = data_context_type_to_string(ctx->context->type);
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					OFONO_PROPERTIES_ARRAY_SIGNATURE,
+					&dict);
+
+	value = ctx->context->active;
+	ofono_dbus_dict_append(&dict, "Active", DBUS_TYPE_BOOLEAN, &value);
+
+	ofono_dbus_dict_append(&dict, "AccessPointName",
+				DBUS_TYPE_STRING, &ctx->context->apn);
+
+	ofono_dbus_dict_append(&dict, "Type",
+				DBUS_TYPE_STRING, &type);
+
+	ofono_dbus_dict_append(&dict, "Username",
+				DBUS_TYPE_STRING, &ctx->context->username);
+
+	ofono_dbus_dict_append(&dict, "Passwod",
+				DBUS_TYPE_STRING, &ctx->context->password);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static void context_set_active_callback(const struct ofono_error *error,
+					void *data)
+{
+	struct context *ctx = data;
+	DBusConnection *conn = ofono_dbus_get_connection();
+	DBusMessage *reply;
+	const char *path;
+	dbus_bool_t value;
+
+	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+		ofono_debug("Activating context failed with error: %s",
+				telephony_error_to_str(error));
+
+		reply = __ofono_error_failed(ctx->dc->pending);
+		goto reply;
+	}
+
+	reply = dbus_message_new_method_return(ctx->dc->pending);
+
+	if (!ctx->context->active) /* Signal emitted elsewhere */
+		goto reply;
+
+	path = dc_build_context_path(ctx->dc, ctx->context);
+	value = ctx->context->active;
+	ofono_dbus_signal_property_changed(conn, path, DATA_CONTEXT_INTERFACE,
+						"Active", DBUS_TYPE_BOOLEAN,
+						&value);
+
+reply:
+	__ofono_dbus_pending_reply(&ctx->dc->pending, reply);
+}
+
+static DBusMessage *dc_set_context_property(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct context *ctx = data;
+	DBusMessageIter iter;
+	DBusMessageIter var;
+	const char *property;
+	dbus_bool_t value;
+	const char *str;
+	const char *path;
+
+	if (ctx->dc->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 (!strcmp(property, "Active")) {
+		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 ((dbus_bool_t) ctx->context->active == value)
+			return dbus_message_new_method_return(msg);
+		if (ctx->dc->flags & DATA_CONNECTION_FLAG_ATTACHING)
+			return __ofono_error_busy(msg);
+		if (value && !ctx->dc->attached)
+			return __ofono_error_failed(msg);
+		if (!ctx->dc->driver->set_active)
+			return __ofono_error_not_implemented(msg);
+
+		ctx->dc->pending = dbus_message_ref(msg);
+
+		ctx->dc->driver->set_active(ctx->dc, ctx->context->id,
+						value,
+						context_set_active_callback,
+						ctx);
+
+		return NULL;
+	}
+
+	/* All other properties are read-only when context is active */
+	if (ctx->context->active)
+		return __ofono_error_invalid_args(msg);
+
+	if (!strcmp(property, "AccessPointName")) {
+		if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+			return __ofono_error_invalid_args(msg);
+
+		dbus_message_iter_get_basic(&var, &str);
+
+		if (ctx->context->apn)
+			g_free(ctx->context->apn);
+		ctx->context->apn = g_strdup(str);
+	} else if (!strcmp(property, "Type")) {
+		if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+			return __ofono_error_invalid_args(msg);
+
+		dbus_message_iter_get_basic(&var, &str);
+
+		if (!strcmp(str, "internet"))
+			ctx->context->type = DATA_CONTEXT_TYPE_INTERNET;
+		else
+			return __ofono_error_invalid_args(msg);
+	} else if (!strcmp(property, "Username")) {
+		if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+			return __ofono_error_invalid_args(msg);
+
+		dbus_message_iter_get_basic(&var, &str);
+
+		if (ctx->context->username)
+			g_free(ctx->context->username);
+		ctx->context->username = g_strdup(str);
+	} else if (!strcmp(property, "Password")) {
+		if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+			return __ofono_error_invalid_args(msg);
+
+		dbus_message_iter_get_basic(&var, &str);
+
+		if (ctx->context->password)
+			g_free(ctx->context->password);
+		ctx->context->password = g_strdup(str);
+	} else
+		return __ofono_error_invalid_args(msg);
+
+	path = dc_build_context_path(ctx->dc, ctx->context);
+	ofono_dbus_signal_property_changed(conn, path, DATA_CONTEXT_INTERFACE,
+						property, DBUS_TYPE_STRING,
+						&str);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable context_methods[] = {
+	{ "GetProperties",	"",	"a{sv}",	dc_get_context_properties },
+	{ "SetProperty",	"sv",	"",		dc_set_context_property,
+							G_DBUS_METHOD_FLAG_ASYNC },
+	{ }
+};
+
+static GDBusSignalTable context_signals[] = {
+	{ "PropertyChanged",	"sv" },
+	{ }
+};
+
+static struct context *context_create(struct ofono_data_connection *dc,
+					struct ofono_data_context *ctx)
+{
+	struct context *context = g_try_new0(struct context, 1);
+
+	if (!context)
+		return NULL;
+
+	context->context = ctx;
+	context->dc = dc;
+
+	return context;
+}
+
+static void context_destroy(gpointer userdata)
+{
+	struct context *ctx = userdata;
+
+	g_free(ctx);
+}
+
+static gboolean context_dbus_register(struct ofono_data_connection *dc,
+					struct context *ctx)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = dc_build_context_path(dc, ctx->context);
+
+	if (!g_dbus_register_interface(conn, path, DATA_CONTEXT_INTERFACE,
+					context_methods, context_signals,
+					NULL, ctx, context_destroy)) {
+		ofono_error("Could not register PrimaryContext %s", path);
+		context_destroy(ctx);
+
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean context_dbus_unregister(struct ofono_data_connection *dc,
+					struct ofono_data_context *ctx)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = dc_build_context_path(dc, ctx);
+
+	return g_dbus_unregister_interface(conn, path, DATA_CONTEXT_INTERFACE);
+}
+
+static char **dc_contexts_path_list(struct ofono_data_connection *dc,
+					GSList *context_list)
+{
+	GSList *l;
+	char **i;
+	struct context *ctx;
+	char **objlist = g_new0(char *, g_slist_length(context_list) + 1);
+
+	if (!objlist)
+		return NULL;
+
+	for (i = objlist, l = context_list; l; l = l->next) {
+		ctx = l->data;
+		*i++ = g_strdup(dc_build_context_path(dc, ctx->context));
+	}
+
+	return objlist;
+}
+
+static void dc_generic_callback(const struct ofono_error *error, void *data)
+{
+	struct ofono_data_connection *dc = data;
+	DBusMessage *reply;
+
+	if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
+		ofono_debug("command failed with error: %s",
+				telephony_error_to_str(error));
+
+	if (!dc->pending)
+		return;
+
+	if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+		reply = dbus_message_new_method_return(dc->pending);
+	else
+		reply = __ofono_error_failed(dc->pending);
+
+	__ofono_dbus_pending_reply(&dc->pending, reply);
+}
+
+static void dc_attach_callback(const struct ofono_error *error,
+					void *data)
+{
+	struct ofono_data_connection *dc = data;
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path;
+	dbus_bool_t value;
+
+	if (error->type == OFONO_ERROR_TYPE_NO_ERROR &&
+			(dc->flags & DATA_CONNECTION_FLAG_ATTACHING)) {
+		dc->attached = !dc->attached;
+
+		path = __ofono_atom_get_path(dc->atom);
+		value = dc->attached;
+		ofono_dbus_signal_property_changed(conn, path,
+					DATA_CONNECTION_MANAGER_INTERFACE,
+					"Attached", DBUS_TYPE_BOOLEAN, &value);
+	}
+
+	dc->flags &= ~DATA_CONNECTION_FLAG_ATTACHING;
+
+	dc_netreg_update(dc);
+}
+
+static void dc_netreg_update(struct ofono_data_connection *dc)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	int attach;
+	int operator_ok;
+	const char *path;
+	dbus_bool_t value = 0;
+
+	operator_ok = dc->roaming_allowed ||
+		(dc->status != NETWORK_REGISTRATION_STATUS_ROAMING);
+
+	attach = dc->powered && operator_ok;
+
+	if (dc->attached != attach &&
+			!(dc->flags & DATA_CONNECTION_FLAG_ATTACHING)) {
+		dc->flags |= DATA_CONNECTION_FLAG_ATTACHING;
+
+		dc->driver->set_attached(dc, attach, dc_attach_callback, dc);
+
+		/* Prevent further attempts to attach */
+		if (!attach && dc->powered) {
+			dc->powered = 0;
+
+			path = __ofono_atom_get_path(dc->atom);
+			ofono_dbus_signal_property_changed(conn, path,
+					DATA_CONNECTION_MANAGER_INTERFACE,
+					"Powered", DBUS_TYPE_BOOLEAN, &value);
+		}
+	}
+}
+
+static DBusMessage *dc_get_manager_properties(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct ofono_data_connection *dc = data;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	char **objpath_list;
+	dbus_bool_t value;
+	const char *status = registration_status_to_string(dc->status);
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					OFONO_PROPERTIES_ARRAY_SIGNATURE,
+					&dict);
+
+	objpath_list = dc_contexts_path_list(dc, dc->contexts);
+	if (!objpath_list)
+		return NULL;
+
+	ofono_dbus_dict_append_array(&dict, "PrimaryContexts",
+					DBUS_TYPE_OBJECT_PATH, &objpath_list);
+
+	g_strfreev(objpath_list);
+
+	value = dc->attached;
+	ofono_dbus_dict_append(&dict, "Attached", DBUS_TYPE_BOOLEAN, &value);
+
+	value = dc->roaming_allowed;
+	ofono_dbus_dict_append(&dict, "RoamingAllowed",
+				DBUS_TYPE_BOOLEAN, &value);
+
+	value = dc->powered;
+	ofono_dbus_dict_append(&dict, "Powered", DBUS_TYPE_BOOLEAN, &value);
+
+	ofono_dbus_dict_append(&dict, "Status", DBUS_TYPE_STRING, &status);
+
+	if (dc->location != -1) {
+		dbus_uint16_t location = dc->location;
+		ofono_dbus_dict_append(&dict, "LocationAreaCode",
+					DBUS_TYPE_UINT16, &location);
+	}
+
+	if (dc->cellid != -1) {
+		dbus_uint32_t cellid = dc->cellid;
+		ofono_dbus_dict_append(&dict, "CellId",
+					DBUS_TYPE_UINT32, &cellid);
+	}
+
+	if (dc->technology != -1) {
+		const char *technology =
+			registration_tech_to_string(dc->technology);
+
+		ofono_dbus_dict_append(&dict, "Technology", DBUS_TYPE_STRING,
+					&technology);
+	}
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static DBusMessage *dc_set_manager_property(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct ofono_data_connection *dc = data;
+	DBusMessageIter iter;
+	DBusMessageIter var;
+	const char *property;
+	dbus_bool_t value;
+	const char *path;
+
+	if (dc->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 (!strcmp(property, "RoamingAllowed")) {
+		if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+			return __ofono_error_invalid_args(msg);
+
+		dbus_message_iter_get_basic(&var, &value);
+
+		dc->roaming_allowed = value;
+		dc_netreg_update(dc);
+	} else if (!strcmp(property, "Powered")) {
+		if (!dc->driver->set_attached)
+			return __ofono_error_not_implemented(msg);
+
+		if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+			return __ofono_error_invalid_args(msg);
+
+		dbus_message_iter_get_basic(&var, &value);
+
+		dc->powered = value;
+		dc_netreg_update(dc);
+	} else
+		return __ofono_error_invalid_args(msg);
+
+	path = __ofono_atom_get_path(dc->atom);
+	ofono_dbus_signal_property_changed(conn, path,
+					DATA_CONNECTION_MANAGER_INTERFACE,
+					property, DBUS_TYPE_BOOLEAN, &value);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static void dc_create_context_callback(const struct ofono_error *error,
+					struct ofono_data_context *ctx,
+					void *data)
+{
+	struct ofono_data_connection *dc = data;
+	DBusMessage *reply;
+	const char *path;
+
+	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+		ofono_debug("Creating new context failed with error: %s",
+				telephony_error_to_str(error));
+
+		reply = __ofono_error_failed(dc->pending);
+		goto error;
+	}
+
+	reply = dbus_message_new_method_return(dc->pending);
+
+	path = dc_build_context_path(dc, ctx);
+	dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
+					DBUS_TYPE_INVALID);
+
+error:
+	__ofono_dbus_pending_reply(&dc->pending, reply);
+}
+
+static DBusMessage *dc_create_context(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct ofono_data_connection *dc = data;
+
+	if (dc->pending)
+		return __ofono_error_busy(msg);
+
+	if (!dc->driver->create_context)
+		return __ofono_error_not_implemented(msg);
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID))
+		return __ofono_error_invalid_args(msg);
+
+	dc->pending = dbus_message_ref(msg);
+
+	dc->driver->create_context(dc, dc_create_context_callback, dc);
+
+	return NULL;
+}
+
+static void dc_remove_context_callback(const struct ofono_error *error,
+					void *data)
+{
+	struct ofono_data_connection *dc = data;
+	DBusMessage *reply;
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path;
+	char **objpath_list;
+
+	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+		ofono_error("Removing context failed with error: %s",
+				telephony_error_to_str(error));
+
+		reply = __ofono_error_failed(dc->pending);
+		goto error;
+	}
+
+	context_dbus_unregister(dc, dc->current_context->context);
+	dc->contexts = g_slist_remove(dc->contexts, dc->current_context);
+	dc->current_context = NULL;
+
+	objpath_list = dc_contexts_path_list(dc, dc->contexts);
+	if (!objpath_list) {
+		ofono_error("Could not allocate PrimaryContext objects list");
+		return;
+	}
+
+	path = __ofono_atom_get_path(dc->atom);
+	ofono_dbus_signal_array_property_changed(conn, path,
+					DATA_CONNECTION_MANAGER_INTERFACE,
+					"PrimaryContexts",
+					DBUS_TYPE_OBJECT_PATH, &objpath_list);
+
+	g_strfreev(objpath_list);
+
+	reply = dbus_message_new_method_return(dc->pending);
+
+error:
+	__ofono_dbus_pending_reply(&dc->pending, reply);
+}
+
+static void dc_deactivate_context_callback(const struct ofono_error *error,
+						void *data)
+{
+	struct ofono_data_connection *dc = data;
+
+	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+		ofono_debug("Removing context failed with error: %s",
+				telephony_error_to_str(error));
+
+		dc->current_context = NULL;
+		__ofono_dbus_pending_reply(&dc->pending, __ofono_error_failed(
+						dc->pending));
+		return;
+	}
+
+	dc->driver->remove_context(dc, dc->current_context->context->id,
+					dc_remove_context_callback, dc);
+}
+
+static DBusMessage *dc_remove_context(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct ofono_data_connection *dc = data;
+	struct context *ctx;
+	const char *path;
+
+	if (dc->pending)
+		return __ofono_error_busy(msg);
+
+	if (!dc->driver->remove_context)
+		return __ofono_error_not_implemented(msg);
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+					DBUS_TYPE_INVALID))
+		return __ofono_error_invalid_args(msg);
+
+	if (path[0] == '\0')
+		return __ofono_error_invalid_format(msg);
+
+	ctx = dc_context_by_path(dc, path);
+	if (!ctx)
+		return __ofono_error_not_found(msg);
+
+	dc->pending = dbus_message_ref(msg);
+	dc->current_context = ctx;
+
+	if (ctx->context->active && dc->driver->set_active) {
+		dc->driver->set_active(dc, ctx->context->id, 0,
+					dc_deactivate_context_callback, dc);
+
+		return NULL;
+	}
+
+	dc->driver->remove_context(dc, ctx->context->id,
+					dc_remove_context_callback, dc);
+
+	return NULL;
+}
+
+static DBusMessage *dc_deactivate_all(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct ofono_data_connection *dc = data;
+
+	if (dc->pending)
+		return __ofono_error_busy(msg);
+
+	if (!dc->driver->set_active_all)
+		return __ofono_error_not_implemented(msg);
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID))
+		return __ofono_error_invalid_args(msg);
+
+	dc->pending = dbus_message_ref(msg);
+
+	dc->driver->set_active_all(dc, 0, dc_generic_callback, dc);
+
+	return NULL;
+}
+
+static GDBusMethodTable manager_methods[] = {
+	{ "GetProperties",	"",	"a{sv}",	dc_get_manager_properties },
+	{ "SetProperty",	"sv",	"",		dc_set_manager_property },
+	{ "CreateContext",	"",	"o",		dc_create_context,
+							G_DBUS_METHOD_FLAG_ASYNC },
+	{ "RemoveContext",	"o",	"",		dc_remove_context,
+							G_DBUS_METHOD_FLAG_ASYNC },
+	{ "DeactivateAll",	"",	"",		dc_deactivate_all,
+							G_DBUS_METHOD_FLAG_ASYNC },
+	{ }
+};
+
+static GDBusSignalTable manager_signals[] = {
+	{ "PropertyChanged",	"sv" },
+	{ }
+};
+
+void ofono_data_connection_notify(struct ofono_data_connection *dc,
+					struct ofono_data_context *ctx)
+{
+	struct context *context = context_create(dc, ctx);
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path;
+	char **objpath_list;
+
+	if (!context) {
+		ofono_error("Unable to allocate context struct");
+		return;
+	}
+
+	ofono_debug("Registering new context: %i", ctx->id);
+	if (!context_dbus_register(dc, context))
+		return;
+
+	dc->contexts = g_slist_insert_sorted(dc->contexts,
+						context, context_compare);
+
+	objpath_list = dc_contexts_path_list(dc, dc->contexts);
+	if (!objpath_list) {
+		ofono_error("Unable to allocate PrimaryContext objects list");
+		return;
+	}
+
+	path = __ofono_atom_get_path(dc->atom);
+	ofono_dbus_signal_array_property_changed(conn, path,
+					DATA_CONNECTION_MANAGER_INTERFACE,
+					"PrimaryContexts",
+					DBUS_TYPE_OBJECT_PATH, &objpath_list);
+
+	g_strfreev(objpath_list);
+}
+
+void ofono_data_connection_deactivated(struct ofono_data_connection *dc,
+					unsigned id)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = NULL; /* Suppress warning */
+	dbus_bool_t value = 0;
+	GSList *l;
+	struct context *ctx;
+
+	for (l = dc->contexts; l; l = l->next) {
+		ctx = l->data;
+
+		if (ctx->context->id == id) {
+			path = dc_build_context_path(dc, ctx->context);
+			break;
+		}
+	}
+
+	ofono_dbus_signal_property_changed(conn, path, DATA_CONTEXT_INTERFACE,
+						"Active", DBUS_TYPE_BOOLEAN,
+						&value);
+
+}
+
+void ofono_data_connection_detached(struct ofono_data_connection *dc)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path;
+	dbus_bool_t value = 0;
+
+	if (dc->attached && !(dc->flags & DATA_CONNECTION_FLAG_ATTACHING)) {
+		dc->attached = 0;
+
+		path = __ofono_atom_get_path(dc->atom);
+		ofono_dbus_signal_property_changed(conn, path,
+					DATA_CONNECTION_MANAGER_INTERFACE,
+					"Attached", DBUS_TYPE_BOOLEAN, &value);
+
+		dc_netreg_update(dc);
+	}
+}
+
+static void set_registration_status(struct ofono_data_connection *dc,
+					int status)
+{
+	const char *str_status = registration_status_to_string(status);
+	const char *path = __ofono_atom_get_path(dc->atom);
+	DBusConnection *conn = ofono_dbus_get_connection();
+	dbus_bool_t attached;
+
+	dc->status = status;
+
+	ofono_dbus_signal_property_changed(conn, path,
+					DATA_CONNECTION_MANAGER_INTERFACE,
+					"Status", DBUS_TYPE_STRING,
+					&str_status);
+
+	attached = (status != NETWORK_REGISTRATION_STATUS_REGISTERED &&
+			status != NETWORK_REGISTRATION_STATUS_ROAMING);
+	if (dc->attached != (int) attached &&
+			!(dc->flags & DATA_CONNECTION_FLAG_ATTACHING)) {
+		dc->attached = (int) attached;
+
+		ofono_dbus_signal_property_changed(conn, path,
+				DATA_CONNECTION_MANAGER_INTERFACE,
+				"Attached", DBUS_TYPE_BOOLEAN,
+				&attached);
+
+		dc_netreg_update(dc);
+	}
+}
+
+static void set_registration_location(struct ofono_data_connection *dc,
+					int lac)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = __ofono_atom_get_path(dc->atom);
+	dbus_uint16_t dbus_lac = lac;
+
+	if (lac > 0xffff)
+		return;
+
+	dc->location = lac;
+
+	if (dc->location == -1)
+		return;
+
+	ofono_dbus_signal_property_changed(conn, path,
+					DATA_CONNECTION_MANAGER_INTERFACE,
+					"LocationAreaCode",
+					DBUS_TYPE_UINT16, &dbus_lac);
+}
+
+static void set_registration_cellid(struct ofono_data_connection *dc, int ci)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = __ofono_atom_get_path(dc->atom);
+	dbus_uint32_t dbus_ci = ci;
+
+	dc->cellid = ci;
+
+	if (dc->cellid == -1)
+		return;
+
+	ofono_dbus_signal_property_changed(conn, path,
+					DATA_CONNECTION_MANAGER_INTERFACE,
+					"CellId", DBUS_TYPE_UINT32,
+					&dbus_ci);
+}
+
+static void set_registration_technology(struct ofono_data_connection *dc,
+					int tech)
+{
+	const char *tech_str = registration_tech_to_string(tech);
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = __ofono_atom_get_path(dc->atom);
+
+	dc->technology = tech;
+
+	if (dc->technology == -1)
+		return;
+
+	ofono_dbus_signal_property_changed(conn, path,
+					DATA_CONNECTION_MANAGER_INTERFACE,
+					"Technology", DBUS_TYPE_STRING,
+					&tech_str);
+}
+
+void ofono_data_netreg_status_notify(struct ofono_data_connection *dc,
+					int status, int lac, int ci, int tech)
+{
+	if (dc->status != status)
+		set_registration_status(dc, status);
+
+	if (dc->location != lac)
+		set_registration_location(dc, lac);
+
+	if (dc->cellid != ci)
+		set_registration_cellid(dc, ci);
+
+	if (dc->technology != tech)
+		set_registration_technology(dc, tech);
+}
+
+int ofono_data_connection_driver_register(
+		const struct ofono_data_connection_driver *d)
+{
+	DBG("driver: %p, name: %s", d, d->name);
+
+	if (d->probe == NULL)
+		return -EINVAL;
+
+	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+
+	return 0;
+}
+
+void ofono_data_connection_driver_unregister(
+		const struct ofono_data_connection_driver *d)
+{
+	DBG("driver: %p, name: %s", d, d->name);
+
+	g_drivers = g_slist_remove(g_drivers, (void *)d);
+}
+
+static void data_connection_unregister(struct ofono_atom *atom)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	struct ofono_data_connection *dc = __ofono_atom_get_data(atom);
+	struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+	const char *path = __ofono_atom_get_path(atom);
+
+	g_slist_free(dc->contexts);
+
+	ofono_modem_remove_interface(modem, DATA_CONNECTION_MANAGER_INTERFACE);
+	g_dbus_unregister_interface(conn, path,
+					DATA_CONNECTION_MANAGER_INTERFACE);
+}
+
+static void data_connection_remove(struct ofono_atom *atom)
+{
+	struct ofono_data_connection *dc = __ofono_atom_get_data(atom);
+
+	DBG("atom: %p", atom);
+
+	if (dc == NULL)
+		return;
+
+	if (dc->driver && dc->driver->remove)
+		dc->driver->remove(dc);
+
+	g_free(dc);
+}
+
+struct ofono_data_connection *ofono_data_connection_create(
+		struct ofono_modem *modem, unsigned int vendor,
+		const char *driver, void *data)
+{
+	struct ofono_data_connection *dc;
+	GSList *l;
+
+	if (driver == NULL)
+		return NULL;
+
+	dc = g_try_new0(struct ofono_data_connection, 1);
+
+	if (dc == NULL)
+		return NULL;
+
+	dc->atom = __ofono_modem_add_atom(modem,
+			OFONO_ATOM_TYPE_DATA_CONNECTION,
+			data_connection_remove, dc);
+
+	for (l = g_drivers; l; l = l->next) {
+		const struct ofono_data_connection_driver *drv = l->data;
+
+		if (g_strcmp0(drv->name, driver))
+			continue;
+
+		if (drv->probe(dc, vendor, data) < 0)
+			continue;
+
+		dc->driver = drv;
+		break;
+	}
+
+	dc->technology = -1;
+	dc->cellid = -1;
+	dc->location = -1;
+
+	return dc;
+}
+
+void ofono_data_connection_register(struct ofono_data_connection *dc)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	struct ofono_modem *modem = __ofono_atom_get_modem(dc->atom);
+	const char *path = __ofono_atom_get_path(dc->atom);
+
+	if (!g_dbus_register_interface(conn, path,
+					DATA_CONNECTION_MANAGER_INTERFACE,
+					manager_methods, manager_signals, NULL,
+					dc, NULL)) {
+		ofono_error("Could not create %s interface",
+				DATA_CONNECTION_MANAGER_INTERFACE);
+
+		return;
+	}
+
+	ofono_modem_add_interface(modem, DATA_CONNECTION_MANAGER_INTERFACE);
+
+	__ofono_atom_register(dc->atom, data_connection_unregister);
+}
+
+void ofono_data_connection_remove(struct ofono_data_connection *dc)
+{
+	__ofono_atom_free(dc->atom);
+}
+
+void ofono_data_connection_set_data(struct ofono_data_connection *dc,
+					void *data)
+{
+	dc->driver_data = data;
+}
+
+void *ofono_data_connection_get_data(struct ofono_data_connection *dc)
+{
+	return dc->driver_data;
+}
diff --git a/src/ofono.h b/src/ofono.h
index 409a9e2..177e1fd 100644
--- a/src/ofono.h
+++ b/src/ofono.h
@@ -106,6 +106,7 @@ enum ofono_atom_type {
 	OFONO_ATOM_TYPE_MESSAGE_WAITING = 13,
 	OFONO_ATOM_TYPE_CBS = 14,
 	OFONO_ATOM_TYPES_CALL_VOLUME = 15,
+	OFONO_ATOM_TYPE_DATA_CONNECTION = 16,
 };
 
 enum ofono_atom_watch_condition {
@@ -160,6 +161,7 @@ void __ofono_atom_free(struct ofono_atom *atom);
 #include <ofono/sms.h>
 #include <ofono/sim.h>
 #include <ofono/voicecall.h>
+#include <ofono/data-connection.h>
 
 #include <ofono/ssn.h>
 
-- 
1.6.1


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

* Re: [RFC] GPRS context setup and teardown (doc/dataconnectionmanager-api.txt)
  2009-10-12 20:01 ` Andrzej Zaborowski
@ 2009-10-16 18:23   ` Denis Kenzior
  0 siblings, 0 replies; 12+ messages in thread
From: Denis Kenzior @ 2009-10-16 18:23 UTC (permalink / raw)
  To: ofono

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

Hi Andrew,

> 2009/10/12 Andrzej Zaborowski <andrew.zaborowski@intel.com>:
> > here's a one-big-patch that adds the DataConnectionManager interface
> > according to doc/dataconnectionmanager-api.txt without any glue for
> > the multiplexer or PPP setup.  Only AT backend is implemented.
>
> This patch depends on 0001 attached, other patches are small
> independent changes that I had in queue.

I applied all four small patches.  Still reviewing the big patch :)

Regards,
-Denis



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

* Re: [RFC] GPRS context setup and teardown (doc/dataconnectionmanager-api.txt)
  2009-10-12 21:35 [RFC] GPRS context setup and teardown (doc/dataconnectionmanager-api.txt) Andrzej Zaborowski
  2009-10-12 20:01 ` Andrzej Zaborowski
@ 2009-10-23 22:22 ` Denis Kenzior
  2009-10-24  8:04   ` andrzej zaborowski
  1 sibling, 1 reply; 12+ messages in thread
From: Denis Kenzior @ 2009-10-23 22:22 UTC (permalink / raw)
  To: ofono

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

Hi Andrew,

> Hi,
> here's a one-big-patch that adds the DataConnectionManager interface
> according to doc/dataconnectionmanager-api.txt without any glue for
> the multiplexer or PPP setup.  Only AT backend is implemented.

Ok, so I took the patch and hacked on it for a while.  I disagreed with how 
you split up responsibilities, so much of the logic got moved into the core 
and the driver was simplified.  Some parts might have been lost along the way.

I also decided to split up GPRS into two atoms to ease integration of hardware 
that supports dedicated network interfaces.  This way the attach / GPRS 
network registration parameters can be reused from the generic 'atmodem' 
driver, while specific context handling can be customized.

I'm happy to report that this actually sort of works with my MBM card.  I can 
even define a GPRS context and activate it.  Of course the card crashes as soon 
as I do :)

>
> One issue with the AT implementation of the api is that "Powered" (a
> read-write property) can be set independently of "Attached" (read-only
> property) and remain set when "Attached" is clear.  The semantics would
> be that the network doesn't have resources to let the modem attach,
> but the modem waits for the resources to become available and then
> attaches.  On AT the modem is in this state only when executing +CGATT,
> so currently the code will rerun +CGATT as soon as the previous one
> returns with error, probably starving other commands.  A possible
> workaround would be for "Powered" to flip back to False after the modem
> fails to attach once, or give up on having separate properties.
> Alternatively we could re-try to attach periodically but on one modem
> I've tried +CGATT fails after about 1 minute (that's the Calypso) and
> on another only about 0.5s (Nokia phones with AT emulation).

We should probably do both with some intelligence.  Right now we lose GPRS 
registration when we run an operator scan (the modem turns off GPRS 
automagically), so running CGATT afterwards is OK.  But then we should do it 
in increasing timeouts.  E.g. 5 seconds, 10 seconds, etc and turn off entirely 
once we have given up.

I don't think this is necessary, all voice dial strings are suffixed by ';', so 
dialing *99***1#; should result in an error.

Some parts I still don't understand:

in set_registration_status:
        attached = (status != NETWORK_REGISTRATION_STATUS_REGISTERED &&
                        status != NETWORK_REGISTRATION_STATUS_ROAMING);
        if (gprs->attached != (int) attached &&
                        !(gprs->flags & GPRS_FLAG_ATTACHING)) {
                gprs->attached = (int) attached;

                ofono_dbus_signal_property_changed(conn, path,
                                DATA_CONNECTION_MANAGER_INTERFACE,
                                "Attached", DBUS_TYPE_BOOLEAN,
                                &attached);

                gprs_netreg_update(gprs);
        }

I do not understand this logic at all.  Can't we always call 
gprs_netreg_update here?  In my opinion Attached should only be emitted once 
it really has changed (e.g. in the callback)

gprs_netreg_update:
                /* Prevent further attempts to attach */
                if (!attach && gprs->powered) {
                        gprs->powered = 0;

                        path = __ofono_atom_get_path(gprs->atom);
                        ofono_dbus_signal_property_changed(conn, path,
                                        DATA_CONNECTION_MANAGER_INTERFACE,
                                        "Powered", DBUS_TYPE_BOOLEAN, &value);
                }

This is just too evil.  Can't we set another flag that attached conditions have 
changed while we were attaching/detaching and that we should recheck those 
conditions when we return from attach/detach?

In gprs_set_property:

You should really ignore set property requests that set the value to the 
already set value.  Simply return success and don't do anything else.

Regards,
-Denis

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

* Re: [RFC] GPRS context setup and teardown (doc/dataconnectionmanager-api.txt)
  2009-10-23 22:22 ` Denis Kenzior
@ 2009-10-24  8:04   ` andrzej zaborowski
  2009-10-24  8:18     ` Andrzej Zaborowski
  2009-10-24 15:30     ` Denis Kenzior
  0 siblings, 2 replies; 12+ messages in thread
From: andrzej zaborowski @ 2009-10-24  8:04 UTC (permalink / raw)
  To: ofono

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

Hi,

2009/10/24 Denis Kenzior <denkenz@gmail.com>:
> Ok, so I took the patch and hacked on it for a while.  I disagreed with how
> you split up responsibilities, so much of the logic got moved into the core
> and the driver was simplified.

Yes, there were a couple of possible choices, I chose to align with
how the list of voice calls was managed in voicecall.c .

>
> I also decided to split up GPRS into two atoms to ease integration of hardware
> that supports dedicated network interfaces.  This way the attach / GPRS
> network registration parameters can be reused from the generic 'atmodem'
> driver, while specific context handling can be customized.

That's a good idea.

>
> I'm happy to report that this actually sort of works with my MBM card.  I can
> even define a GPRS context and activate it.  Of course the card crashes as soon
> as I do :)

I'm happy to report it still works on my Nokia phone, too, except for
one test: when you have active contexts and either tell ofono to power
gprs down or are detached by the network for whatever reason,
".active" is never reset and no event emitted, meaning that you can't
remove the context, can't re-activate it or deactivate it.

>
> Some parts I still don't understand:
>
> in set_registration_status:
>        attached = (status != NETWORK_REGISTRATION_STATUS_REGISTERED &&
>                        status != NETWORK_REGISTRATION_STATUS_ROAMING);
>        if (gprs->attached != (int) attached &&
>                        !(gprs->flags & GPRS_FLAG_ATTACHING)) {
>                gprs->attached = (int) attached;
>
>                ofono_dbus_signal_property_changed(conn, path,
>                                DATA_CONNECTION_MANAGER_INTERFACE,
>                                "Attached", DBUS_TYPE_BOOLEAN,
>                                &attached);
>
>                gprs_netreg_update(gprs);
>        }
>
> I do not understand this logic at all.  Can't we always call
> gprs_netreg_update here?

Sure, but it won't do anything, if either "attached" state hasn't
changed at all or it's already busy executing +CGATT.

> In my opinion Attached should only be emitted once
> it really has changed (e.g. in the callback)

Often there's no other notification telling us that we were detached,
so we must take any sign of it into account (it might be broken modems
but I've seen such situations on both modems that I've tested it with)

>
> gprs_netreg_update:
>                /* Prevent further attempts to attach */
>                if (!attach && gprs->powered) {
>                        gprs->powered = 0;
>
>                        path = __ofono_atom_get_path(gprs->atom);
>                        ofono_dbus_signal_property_changed(conn, path,
>                                        DATA_CONNECTION_MANAGER_INTERFACE,
>                                        "Powered", DBUS_TYPE_BOOLEAN, &value);
>                }
>
> This is just too evil.  Can't we set another flag that attached conditions have
> changed while we were attaching/detaching and that we should recheck those
> conditions when we return from attach/detach?

Is it evil because we change a property that is writable?

This is only for the case of RoamingAllowed = 0.  In my understanding
"Powered" being set means that we're attached or attempting to attach,
which is not the case after we detached because of roaming, so the
D-Bus client would be misled.

>
> In gprs_set_property:
>
> You should really ignore set property requests that set the value to the
> already set value.  Simply return success and don't do anything else.

Good point, the attached patch checks for those cases.

Best regards

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Just-return-success-when-value-already-set-in-SetPro.patch --]
[-- Type: text/x-patch, Size: 1070 bytes --]

From 6d4439fc701ca49ad88149833ac2f4b6d68ba4f3 Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <andrew.zaborowski@intel.com>
Date: Sat, 24 Oct 2009 11:27:23 +0200
Subject: [PATCH 1/2] Just return success when value already set in SetProperty.

---
 src/gprs.c |    6 ++++++
 1 files changed, 6 insertions(+), 0 deletions(-)

diff --git a/src/gprs.c b/src/gprs.c
index fb08d9a..1827ceb 100644
--- a/src/gprs.c
+++ b/src/gprs.c
@@ -661,6 +661,9 @@ static DBusMessage *gprs_set_property(DBusConnection *conn,
 
 		dbus_message_iter_get_basic(&var, &value);
 
+		if (gprs->roaming_allowed == (ofono_bool_t) value)
+			return dbus_message_new_method_return(msg);
+
 		gprs->roaming_allowed = value;
 		gprs_netreg_update(gprs);
 	} else if (!strcmp(property, "Powered")) {
@@ -672,6 +675,9 @@ static DBusMessage *gprs_set_property(DBusConnection *conn,
 
 		dbus_message_iter_get_basic(&var, &value);
 
+		if (gprs->powered == (ofono_bool_t) value)
+			return dbus_message_new_method_return(msg);
+
 		gprs->powered = value;
 		gprs_netreg_update(gprs);
 	} else
-- 
1.6.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-Register-gprs-context-on-calypso-modem-phonesim.patch --]
[-- Type: text/x-patch, Size: 1336 bytes --]

From e9498d070f3593c1ef5c27b91f59ca8e2be971ad Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <andrew.zaborowski@intel.com>
Date: Sat, 24 Oct 2009 11:28:07 +0200
Subject: [PATCH 2/2] Register gprs-context on calypso modem/phonesim.

---
 plugins/phonesim.c |    9 ++++++++-
 1 files changed, 8 insertions(+), 1 deletions(-)

diff --git a/plugins/phonesim.c b/plugins/phonesim.c
index 1151da4..9463265 100644
--- a/plugins/phonesim.c
+++ b/plugins/phonesim.c
@@ -55,6 +55,7 @@
 #include <ofono/ussd.h>
 #include <ofono/voicecall.h>
 #include <ofono/gprs.h>
+#include <ofono/gprs-context.h>
 
 #include <drivers/atmodem/vendor.h>
 
@@ -289,6 +290,8 @@ static void phonesim_post_sim(struct ofono_modem *modem)
 {
 	struct phonesim_data *data = ofono_modem_get_data(modem);
 	struct ofono_message_waiting *mw;
+	struct ofono_gprs *gprs;
+	struct ofono_gprs_context *gc;
 
 	DBG("%p", modem);
 
@@ -313,7 +316,11 @@ static void phonesim_post_sim(struct ofono_modem *modem)
 		ofono_cbs_create(modem, 0, "atmodem", data->chat);
 	}
 
-	ofono_gprs_create(modem, 0, "atmodem", data->chat);
+	gprs = ofono_gprs_create(modem, 0, "atmodem", data->chat);
+	gc = ofono_gprs_context_create(modem, 0, "atmodem", data->chat);
+
+	if (gprs && gc)
+		ofono_gprs_add_context(gprs, gc);
 
 	mw = ofono_message_waiting_create(modem);
 	if (mw)
-- 
1.6.1


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

* Re: [RFC] GPRS context setup and teardown (doc/dataconnectionmanager-api.txt)
  2009-10-24  8:04   ` andrzej zaborowski
@ 2009-10-24  8:18     ` Andrzej Zaborowski
  2009-10-24 17:37       ` Denis Kenzior
  2009-10-24 15:30     ` Denis Kenzior
  1 sibling, 1 reply; 12+ messages in thread
From: Andrzej Zaborowski @ 2009-10-24  8:18 UTC (permalink / raw)
  To: ofono

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

2009/10/24 andrzej zaborowski <balrogg@gmail.com>:
> 2009/10/24 Denis Kenzior <denkenz@gmail.com>:
>> Ok, so I took the patch and hacked on it for a while.  I disagreed with how
>> you split up responsibilities, so much of the logic got moved into the core
>> and the driver was simplified.

By the way I notice that in most atoms' _create functions, when we
loop over the drivers
and call drv->probe() until we succeed, we still return success even if none
of the drivers succeeded, should we return a NULL?

Regards

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

* Re: [RFC] GPRS context setup and teardown (doc/dataconnectionmanager-api.txt)
  2009-10-24  8:04   ` andrzej zaborowski
  2009-10-24  8:18     ` Andrzej Zaborowski
@ 2009-10-24 15:30     ` Denis Kenzior
  2009-10-25  0:02       ` andrzej zaborowski
  1 sibling, 1 reply; 12+ messages in thread
From: Denis Kenzior @ 2009-10-24 15:30 UTC (permalink / raw)
  To: ofono

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

Hi Andrew,
> > in set_registration_status:
> >        attached = (status != NETWORK_REGISTRATION_STATUS_REGISTERED &&
> >                        status != NETWORK_REGISTRATION_STATUS_ROAMING);
> >        if (gprs->attached != (int) attached &&
> >                        !(gprs->flags & GPRS_FLAG_ATTACHING)) {
> >                gprs->attached = (int) attached;
> >
> >                ofono_dbus_signal_property_changed(conn, path,
> >                                DATA_CONNECTION_MANAGER_INTERFACE,
> >                                "Attached", DBUS_TYPE_BOOLEAN,
> >                                &attached);
> >
> >                gprs_netreg_update(gprs);
> >        }
> >
> > I do not understand this logic at all.  Can't we always call
> > gprs_netreg_update here?
>
> Sure, but it won't do anything, if either "attached" state hasn't
> changed at all or it's already busy executing +CGATT.

So the issue here is that I can actually understand what gprs_netreg_update 
does.  This function I'm having trouble with.

Lets suppose status=registered arrives and we're already attached.  Here 
attached will be set to FALSE, Attached property will be set to false and 
emitted.  gprs_netreg_update will presumably reset Attached back to True.  Why 
do this for a potentially spurious indication?  Remember your status might be 
indicated when just the network access technology has changed.

Another case.  We're detached (Powered=True, RoamingAllowed=False) and are 
currently Roaming.  Then status=searching arrives.  We report Attached as 
True.

Am I properly outlining the logic or am I missing something?  Again, explain 
the intention here.  Either way, the current logic is way too complicated and 
needs to be simplified.

>
> > In my opinion Attached should only be emitted once
> > it really has changed (e.g. in the callback)
>
> Often there's no other notification telling us that we were detached,
> so we must take any sign of it into account (it might be broken modems
> but I've seen such situations on both modems that I've tested it with)

For situations where we are forcefully detached and/or go into a non-
registered state, the core should take care of this.  If it is a modem not 
reporting this properly, then I'd argue this is something the driver needs to 
take care of.  If the core starts trying to guess / outwit every single broken 
hardware out there it will be unmaintainable.  Lets at least try to keep the 
core clean and only responsible for the things mandated by the relevant 
standards.

>
> > gprs_netreg_update:
> >                /* Prevent further attempts to attach */
> >                if (!attach && gprs->powered) {
> >                        gprs->powered = 0;
> >
> >                        path = __ofono_atom_get_path(gprs->atom);
> >                        ofono_dbus_signal_property_changed(conn, path,
> >                                        DATA_CONNECTION_MANAGER_INTERFACE,
> >                                        "Powered", DBUS_TYPE_BOOLEAN,
> > &value); }
> >
> > This is just too evil.  Can't we set another flag that attached
> > conditions have changed while we were attaching/detaching and that we
> > should recheck those conditions when we return from attach/detach?
>
> Is it evil because we change a property that is writable?

Yes, and also because this is a hack :)

>
> This is only for the case of RoamingAllowed = 0.  In my understanding
> "Powered" being set means that we're attached or attempting to attach,

No, Powered means GPRS is on assuming all conditions are fulfilled (e.g. 
registration, roaming, network resources)  This is a user setting that will be 
persisted (in IMSI based storage.)  It should not be spuriously changed by the 
daemon.

> which is not the case after we detached because of roaming, so the
> D-Bus client would be misled.

No, 'Attached' is what is used for this purpose.

Both patches have been applied.  Thanks!

Regards,
-Denis

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

* Re: [RFC] GPRS context setup and teardown (doc/dataconnectionmanager-api.txt)
  2009-10-24  8:18     ` Andrzej Zaborowski
@ 2009-10-24 17:37       ` Denis Kenzior
  0 siblings, 0 replies; 12+ messages in thread
From: Denis Kenzior @ 2009-10-24 17:37 UTC (permalink / raw)
  To: ofono

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

Hi Andrew,

> 2009/10/24 andrzej zaborowski <balrogg@gmail.com>:
> > 2009/10/24 Denis Kenzior <denkenz@gmail.com>:
> >> Ok, so I took the patch and hacked on it for a while.  I disagreed with
> >> how you split up responsibilities, so much of the logic got moved into
> >> the core and the driver was simplified.
>
> By the way I notice that in most atoms' _create functions, when we
> loop over the drivers
> and call drv->probe() until we succeed, we still return success even if
> none of the drivers succeeded, should we return a NULL?

I think my intention was to eventually have foo_driver_register functions also 
go through the probe process.  Without the driver the atoms are never 
registered, so the way it is now is OK for now.

Regards,
-Denis

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

* Re: [RFC] GPRS context setup and teardown (doc/dataconnectionmanager-api.txt)
  2009-10-24 15:30     ` Denis Kenzior
@ 2009-10-25  0:02       ` andrzej zaborowski
  2009-10-25  3:43         ` Denis Kenzior
  0 siblings, 1 reply; 12+ messages in thread
From: andrzej zaborowski @ 2009-10-25  0:02 UTC (permalink / raw)
  To: ofono

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

Hi,

2009/10/24 Denis Kenzior <denkenz@gmail.com>:
>> > in set_registration_status:
>> >        attached = (status != NETWORK_REGISTRATION_STATUS_REGISTERED &&
>> >                        status != NETWORK_REGISTRATION_STATUS_ROAMING);
>> >        if (gprs->attached != (int) attached &&
>> >                        !(gprs->flags & GPRS_FLAG_ATTACHING)) {
>> >                gprs->attached = (int) attached;
>> >
>> >                ofono_dbus_signal_property_changed(conn, path,
>> >                                DATA_CONNECTION_MANAGER_INTERFACE,
>> >                                "Attached", DBUS_TYPE_BOOLEAN,
>> >                                &attached);
>> >
>> >                gprs_netreg_update(gprs);
>> >        }
>
> So the issue here is that I can actually understand what gprs_netreg_update
> does.  This function I'm having trouble with.
>
> Lets suppose status=registered arrives and we're already attached.  Here
> attached will be set to FALSE, Attached property will be set to false and
> emitted.  gprs_netreg_update will presumably reset Attached back to True.

Now I see it and you're absolutely right to be alerted, I set
"attached" to the reverse of what it should be set to.  We're attached
when we're either in REGISTERED or ROAMING state.  If I had done it
right then the logic would look simple (I suppose):

If either we already knew that we were in that state then there's
nothing to do.  Otherwise we set the D-Bus property to reflect current
state and call gprs_netreg_update to figure out if this is the state
we really wanted to be in.

If the notification indicates we were detached we will also need to
reset all current contexts to inactive here, originally I did this in
the driver.

>>
>> > gprs_netreg_update:
>> >                /* Prevent further attempts to attach */
>> >                if (!attach && gprs->powered) {
>> >                        gprs->powered = 0;
>> >
>> >                        path = __ofono_atom_get_path(gprs->atom);
>> >                        ofono_dbus_signal_property_changed(conn, path,
>> >                                        DATA_CONNECTION_MANAGER_INTERFACE,
>> >                                        "Powered", DBUS_TYPE_BOOLEAN,
>> > &value); }
>> >
>> > This is just too evil.  Can't we set another flag that attached
>> > conditions have changed while we were attaching/detaching and that we
>> > should recheck those conditions when we return from attach/detach?
>>
>> Is it evil because we change a property that is writable?
>
> Yes, and also because this is a hack :)
>
>>
>> This is only for the case of RoamingAllowed = 0.  In my understanding
>> "Powered" being set means that we're attached or attempting to attach,
>
> No, Powered means GPRS is on assuming all conditions are fulfilled (e.g.
> registration, roaming, network resources)  This is a user setting that will be
> persisted (in IMSI based storage.)  It should not be spuriously changed by the
> daemon.

This is a policy question, what do we do when the user disallowed
roaming but the modem wants to roam?  If we decide that we will not
retry to attach until the user pokes us again then "Powered" really
should be reset to false because even if the conditions you listed are
met we still won't attach.  Maybe instead we should do the retries
with increasing timeouts like you mentioned in another message, and
then "Powered" should stay set.

An unrelated thing: you keep the allowed cid range in the core and
only remember the min and max cid.  However I think (but don't know
where to check for it) that +CGDCONT=? is free to return (1,2) instead
of (1-2), or even (1-2,5-6,13).  Other AT commands do return ranges
with this syntax.

Regards

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

* Re: [RFC] GPRS context setup and teardown (doc/dataconnectionmanager-api.txt)
  2009-10-25  0:02       ` andrzej zaborowski
@ 2009-10-25  3:43         ` Denis Kenzior
  2009-10-25  6:39           ` andrzej zaborowski
  0 siblings, 1 reply; 12+ messages in thread
From: Denis Kenzior @ 2009-10-25  3:43 UTC (permalink / raw)
  To: ofono

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

Hi,

> > Lets suppose status=registered arrives and we're already attached.  Here
> > attached will be set to FALSE, Attached property will be set to false and
> > emitted.  gprs_netreg_update will presumably reset Attached back to True.
>
> Now I see it and you're absolutely right to be alerted, I set
> "attached" to the reverse of what it should be set to.  We're attached
> when we're either in REGISTERED or ROAMING state.  If I had done it
> right then the logic would look simple (I suppose):

I still don't believe that it works.  Suppose we're attached and registered.  
Now we get an indication that we're in 'searching' state.  set attach to true, 
compare with stored value (true).  Ah, don't do anything -> not good.

You also emit a signal that outright lies as to the current state.  Suppose 
we're registered and just the cellid / lac / technology changes.  This 
function still gets triggered and does a funky dance of emitting signals and 
calling into the driver to attach / detach.

>
> If either we already knew that we were in that state then there's
> nothing to do.  Otherwise we set the D-Bus property to reflect current
> state and call gprs_netreg_update to figure out if this is the state
> we really wanted to be in.

This part is fine, but lets make it easy to understand, e.g. do all of this 
inside gprs_netreg_update.

>
> If the notification indicates we were detached we will also need to
> reset all current contexts to inactive here, originally I did this in
> the driver.

Yes, this is one of the parts I forgot to port over :(

> This is a policy question, what do we do when the user disallowed
> roaming but the modem wants to roam?  If we decide that we will not
> retry to attach until the user pokes us again then "Powered" really
> should be reset to false because even if the conditions you listed are
> met we still won't attach.  Maybe instead we should do the retries
> with increasing timeouts like you mentioned in another message, and
> then "Powered" should stay set.

Resetting Powered in the core leads to strange situations where after coming 
from an overseas trip you'd need to reset Powered back to on.  I would find 
that annoying.

The nice thing is I don't think we need to play the attach/detach dance.  We 
can take advantage of the netreg atom's information.  If we're roaming there, 
we will be roaming on GPRS as well.  If we're registered there, then we should 
try to attach.

>
> An unrelated thing: you keep the allowed cid range in the core and
> only remember the min and max cid.  However I think (but don't know
> where to check for it) that +CGDCONT=? is free to return (1,2) instead
> of (1-2), or even (1-2,5-6,13).  Other AT commands do return ranges
> with this syntax.

You're absolutely correct that the syntax allows this.  My decision here was 
mostly a  matter of prudence.  I doubt any device actually uses a non-
contiguous cid range.  If/once we do find one that does, we can add a new 
set_sparse_range function (or something) in the core to take care of it.

Regards,
-Denis

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

* Re: [RFC] GPRS context setup and teardown (doc/dataconnectionmanager-api.txt)
  2009-10-25  3:43         ` Denis Kenzior
@ 2009-10-25  6:39           ` andrzej zaborowski
  2009-10-25  6:43             ` andrzej zaborowski
  0 siblings, 1 reply; 12+ messages in thread
From: andrzej zaborowski @ 2009-10-25  6:39 UTC (permalink / raw)
  To: ofono

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

2009/10/25 Denis Kenzior <denkenz@gmail.com>:
>> > Lets suppose status=registered arrives and we're already attached.  Here
>> > attached will be set to FALSE, Attached property will be set to false and
>> > emitted.  gprs_netreg_update will presumably reset Attached back to True.
>>
>> Now I see it and you're absolutely right to be alerted, I set
>> "attached" to the reverse of what it should be set to.  We're attached
>> when we're either in REGISTERED or ROAMING state.  If I had done it
>> right then the logic would look simple (I suppose):
>
> I still don't believe that it works.  Suppose we're attached and registered.
> Now we get an indication that we're in 'searching' state.  set attach to true,
> compare with stored value (true).  Ah, don't do anything -> not good.

I tried to say that I agree that current code is wrong, but assuming
the variable "attached" was set correctly, this would not happen (see
attached patch).

Now that I think of it if the current status is 'searching' then
possibly there's no point calling driver->set_attached (see second
patch).

> Resetting Powered in the core leads to strange situations where after coming
> from an overseas trip you'd need to reset Powered back to on.  I would find
> that annoying.
>
> The nice thing is I don't think we need to play the attach/detach dance.  We
> can take advantage of the netreg atom's information.  If we're roaming there,
> we will be roaming on GPRS as well.  If we're registered there, then we should
> try to attach.

That's a great idea, I will send a patch separately.

Regards

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Fix-interpretation-of-new-GPRS-registration-status-s.patch --]
[-- Type: text/x-patch, Size: 943 bytes --]

From 03f9b38d6ae5d98bcfb46c3f636b791e018b6515 Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <andrew.zaborowski@intel.com>
Date: Sun, 25 Oct 2009 09:14:54 +0100
Subject: [PATCH 1/2] Fix interpretation of new GPRS registration status signal.

---
 src/gprs.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/gprs.c b/src/gprs.c
index 1827ceb..bcba99c 100644
--- a/src/gprs.c
+++ b/src/gprs.c
@@ -882,8 +882,8 @@ static void set_registration_status(struct ofono_gprs *gprs, int status)
 					"Status", DBUS_TYPE_STRING,
 					&str_status);
 
-	attached = (status != NETWORK_REGISTRATION_STATUS_REGISTERED &&
-			status != NETWORK_REGISTRATION_STATUS_ROAMING);
+	attached = (status == NETWORK_REGISTRATION_STATUS_REGISTERED ||
+			status == NETWORK_REGISTRATION_STATUS_ROAMING);
 	if (gprs->attached != (int) attached &&
 			!(gprs->flags & GPRS_FLAG_ATTACHING)) {
 		gprs->attached = (int) attached;
-- 
1.6.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-Don-t-ask-driver-to-attach-GPRS-if-modem-is-already.patch --]
[-- Type: text/x-patch, Size: 1204 bytes --]

From d0f0331ea8d3f8c4df75069cfca082bb99ce0dfa Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <andrew.zaborowski@intel.com>
Date: Sun, 25 Oct 2009 09:35:26 +0100
Subject: [PATCH 2/2] Don't ask driver to attach GPRS if modem is already searching for operator.

---
 src/gprs.c |    8 +++++---
 1 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/gprs.c b/src/gprs.c
index bcba99c..2769c27 100644
--- a/src/gprs.c
+++ b/src/gprs.c
@@ -543,7 +543,9 @@ static void gprs_netreg_update(struct ofono_gprs *gprs)
 	attach = gprs->powered && operator_ok;
 
 	if (gprs->attached != attach &&
-			!(gprs->flags & GPRS_FLAG_ATTACHING)) {
+			!(gprs->flags & GPRS_FLAG_ATTACHING) &&
+			!(attach && gprs->status !=
+				NETWORK_REGISTRATION_STATUS_SEARCHING)) {
 		gprs->flags |= GPRS_FLAG_ATTACHING;
 
 		gprs->driver->set_attached(gprs, attach, gprs_attach_callback,
@@ -892,9 +894,9 @@ static void set_registration_status(struct ofono_gprs *gprs, int status)
 				DATA_CONNECTION_MANAGER_INTERFACE,
 				"Attached", DBUS_TYPE_BOOLEAN,
 				&attached);
-
-		gprs_netreg_update(gprs);
 	}
+
+	gprs_netreg_update(gprs);
 }
 
 static void set_registration_location(struct ofono_gprs *gprs,
-- 
1.6.1


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

* Re: [RFC] GPRS context setup and teardown (doc/dataconnectionmanager-api.txt)
  2009-10-25  6:39           ` andrzej zaborowski
@ 2009-10-25  6:43             ` andrzej zaborowski
  0 siblings, 0 replies; 12+ messages in thread
From: andrzej zaborowski @ 2009-10-25  6:43 UTC (permalink / raw)
  To: ofono

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

2009/10/25 andrzej zaborowski <balrogg@gmail.com>:
> Now that I think of it if the current status is 'searching' then
> possibly there's no point calling driver->set_attached (see second
> patch).

I repeated the same typo in this patch, attached is a corrected version, sorry.

Regards

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0002-Don-t-ask-driver-to-attach-GPRS-if-modem-is-already.patch --]
[-- Type: text/x-patch, Size: 1204 bytes --]

From d0f0331ea8d3f8c4df75069cfca082bb99ce0dfa Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <andrew.zaborowski@intel.com>
Date: Sun, 25 Oct 2009 09:35:26 +0100
Subject: [PATCH 2/2] Don't ask driver to attach GPRS if modem is already searching for operator.

---
 src/gprs.c |    8 +++++---
 1 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/gprs.c b/src/gprs.c
index bcba99c..2769c27 100644
--- a/src/gprs.c
+++ b/src/gprs.c
@@ -543,7 +543,9 @@ static void gprs_netreg_update(struct ofono_gprs *gprs)
 	attach = gprs->powered && operator_ok;
 
 	if (gprs->attached != attach &&
-			!(gprs->flags & GPRS_FLAG_ATTACHING)) {
+			!(gprs->flags & GPRS_FLAG_ATTACHING) &&
+			!(attach && gprs->status ==
+				NETWORK_REGISTRATION_STATUS_SEARCHING)) {
 		gprs->flags |= GPRS_FLAG_ATTACHING;
 
 		gprs->driver->set_attached(gprs, attach, gprs_attach_callback,
@@ -892,9 +894,9 @@ static void set_registration_status(struct ofono_gprs *gprs, int status)
 				DATA_CONNECTION_MANAGER_INTERFACE,
 				"Attached", DBUS_TYPE_BOOLEAN,
 				&attached);
-
-		gprs_netreg_update(gprs);
 	}
+
+	gprs_netreg_update(gprs);
 }
 
 static void set_registration_location(struct ofono_gprs *gprs,
-- 
1.6.1


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

end of thread, other threads:[~2009-10-25  6:43 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-10-12 21:35 [RFC] GPRS context setup and teardown (doc/dataconnectionmanager-api.txt) Andrzej Zaborowski
2009-10-12 20:01 ` Andrzej Zaborowski
2009-10-16 18:23   ` Denis Kenzior
2009-10-23 22:22 ` Denis Kenzior
2009-10-24  8:04   ` andrzej zaborowski
2009-10-24  8:18     ` Andrzej Zaborowski
2009-10-24 17:37       ` Denis Kenzior
2009-10-24 15:30     ` Denis Kenzior
2009-10-25  0:02       ` andrzej zaborowski
2009-10-25  3:43         ` Denis Kenzior
2009-10-25  6:39           ` andrzej zaborowski
2009-10-25  6:43             ` andrzej zaborowski

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.