Implement dial up support in GPRS atom.
---
src/gprs.c | 439 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 437 insertions(+), 2 deletions(-)
diff --git a/src/gprs.c b/src/gprs.c
index dd58c51..cb83e23 100644
--- a/src/gprs.c
+++ b/src/gprs.c
@@ -39,6 +39,7 @@
#include "common.h"
#include "storage.h"
#include "idmap.h"
+#include "gatserver.h"
#define GPRS_FLAG_ATTACHING 0x1
#define GPRS_FLAG_RECHECK 0x2
@@ -48,6 +49,10 @@
#define MAX_CONTEXT_NAME_LENGTH 127
#define MAX_CONTEXTS 256
+#define DUN_LOCAL "192.168.1.1"
+#define DUN_PEER "192.168.1.2"
+#define EMULATOR_GPRS_TIMEOUT 20
+
static GSList *g_drivers = NULL;
static GSList *g_context_drivers = NULL;
@@ -55,6 +60,7 @@ enum gprs_context_type {
GPRS_CONTEXT_TYPE_INTERNET = 0,
GPRS_CONTEXT_TYPE_MMS,
GPRS_CONTEXT_TYPE_WAP,
+ GPRS_CONTEXT_TYPE_DUN,
GPRS_CONTEXT_TYPE_INVALID,
};
@@ -81,6 +87,9 @@ struct ofono_gprs {
struct ofono_emulator *dun;
unsigned int dun_watch;
unsigned int dun_status_watch;
+ int attach_source;
+ int context_source;
+ int timeout_source;
const struct ofono_gprs_driver *driver;
void *driver_data;
struct ofono_atom *atom;
@@ -115,6 +124,13 @@ struct pri_context {
struct ofono_gprs *gprs;
};
+struct gprs_dun_data {
+ struct ofono_gprs *gprs;
+ ofono_emulator_gprs_connect_cb cb;
+ void *cb_data;
+ struct pri_context *context;
+};
+
static void gprs_netreg_update(struct ofono_gprs *gprs);
static const char *gprs_context_type_to_string(int type)
@@ -168,6 +184,21 @@ static struct pri_context *gprs_context_by_path(struct ofono_gprs
*gprs,
return NULL;
}
+static struct pri_context *gprs_context_by_type(struct ofono_gprs *gprs,
+ enum gprs_context_type type)
+{
+ GSList *l;
+
+ for (l = gprs->contexts; l; l = l->next) {
+ struct pri_context *ctx = l->data;
+
+ if (type == ctx->type)
+ return ctx;
+ }
+
+ return NULL;
+}
+
static void context_settings_free(struct context_settings *settings)
{
g_free(settings->interface);
@@ -401,6 +432,53 @@ static DBusMessage *pri_get_properties(DBusConnection *conn,
return reply;
}
+static void dun_activate(const char *interface, const char *local,
+ const char *peer,
+ const char **dns,
+ void *data)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+ struct pri_context *ctx = data;
+ const char *netmask = "255.255.255.255";
+ dbus_bool_t value;
+
+ DBG("%p %s", ctx, interface);
+
+ ctx->active = TRUE;
+
+ /* If we don't have the interface, don't bother emitting any settings,
+ * as nobody can make use of them
+ */
+ if (interface != NULL)
+ pri_update_context_settings(ctx, interface, TRUE,
+ local, netmask, peer, dns);
+
+ value = ctx->active;
+ ofono_dbus_signal_property_changed(conn, ctx->path,
+ OFONO_DATA_CONTEXT_INTERFACE,
+ "Active", DBUS_TYPE_BOOLEAN,
+ &value);
+}
+
+static void dun_deactivate(struct pri_context *ctx)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+ dbus_bool_t value;
+
+ gprs_cid_release(ctx->gprs, ctx->context.cid);
+ ctx->context.cid = 0;
+
+ ctx->active = FALSE;
+
+ pri_reset_context_settings(ctx);
+
+ value = ctx->active;
+ ofono_dbus_signal_property_changed(conn, ctx->path,
+ OFONO_DATA_CONTEXT_INTERFACE,
+ "Active", DBUS_TYPE_BOOLEAN,
+ &value);
+}
+
static void pri_activate_callback(const struct ofono_error *error,
const char *interface, ofono_bool_t static_ip,
const char *ip, const char *netmask,
@@ -417,7 +495,8 @@ static void pri_activate_callback(const struct ofono_error *error,
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
DBG("Activating context failed with error: %s",
telephony_error_to_str(error));
- __ofono_dbus_pending_reply(&gc->pending,
+ if (gc && gc->pending)
+ __ofono_dbus_pending_reply(&gc->pending,
__ofono_error_failed(gc->pending));
gprs_cid_release(ctx->gprs, ctx->context.cid);
@@ -427,7 +506,9 @@ static void pri_activate_callback(const struct ofono_error *error,
}
ctx->active = TRUE;
- __ofono_dbus_pending_reply(&gc->pending,
+
+ if (gc && gc->pending)
+ __ofono_dbus_pending_reply(&gc->pending,
dbus_message_new_method_return(gc->pending));
/* If we don't have the interface, don't bother emitting any settings,
@@ -693,6 +774,12 @@ static DBusMessage *pri_set_property(DBusConnection *conn,
gc->pending = dbus_message_ref(msg);
+ if (ctx->type == GPRS_CONTEXT_TYPE_DUN) {
+ /* Not support yet */
+ ofono_error("Active DUN context are not yet supported");
+ return __ofono_error_failed(msg);
+ }
+
if (value)
gc->driver->activate_primary(gc, &ctx->context,
pri_activate_callback, ctx);
@@ -1095,6 +1182,82 @@ static DBusMessage *gprs_set_property(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}
+static struct pri_context *ofono_gprs_create_context(
+ struct ofono_gprs *gprs,
+ const char *name, const char *typestr)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+ struct pri_context *context;
+ const char *path;
+ enum gprs_context_type type;
+ char **objpath_list;
+ unsigned int id;
+
+ if (strlen(name) == 0 || strlen(name) > MAX_CONTEXT_NAME_LENGTH)
+ return NULL;
+
+ type = gprs_context_string_to_type(typestr);
+
+ if (type == GPRS_CONTEXT_TYPE_INVALID)
+ return NULL;
+
+ if (gprs->last_context_id)
+ id = idmap_alloc_next(gprs->pid_map, gprs->last_context_id);
+ else
+ id = idmap_alloc(gprs->pid_map);
+
+ if (id > idmap_get_max(gprs->pid_map))
+ return NULL;
+
+ context = pri_context_create(gprs, name, type);
+ context->id = id;
+
+ if (!context) {
+ ofono_error("Unable to allocate context struct");
+ return NULL;
+ }
+
+ DBG("Registering new context");
+
+ if (!context_dbus_register(context)) {
+ ofono_error("Unable to register primary context");
+ return NULL;
+ }
+
+ gprs->last_context_id = id;
+
+ if (gprs->settings) {
+ g_key_file_set_string(gprs->settings, context->key,
+ "Name", context->name);
+ g_key_file_set_string(gprs->settings, context->key,
+ "AccessPointName",
+ context->context.apn);
+ g_key_file_set_string(gprs->settings, context->key,
+ "Username", context->context.username);
+ g_key_file_set_string(gprs->settings, context->key,
+ "Password", context->context.password);
+ g_key_file_set_string(gprs->settings, context->key, "Type",
+ gprs_context_type_to_string(context->type));
+ storage_sync(gprs->imsi, SETTINGS_STORE, gprs->settings);
+ }
+
+ gprs->contexts = g_slist_append(gprs->contexts, context);
+
+ objpath_list = gprs_contexts_path_list(gprs->contexts);
+
+ if (objpath_list) {
+ path = __ofono_atom_get_path(gprs->atom);
+ ofono_dbus_signal_array_property_changed(conn, path,
+ OFONO_DATA_CONNECTION_MANAGER_INTERFACE,
+ "PrimaryContexts",
+ DBUS_TYPE_OBJECT_PATH, &objpath_list);
+
+ g_strfreev(objpath_list);
+ }
+
+ return context;
+}
+
static DBusMessage *gprs_create_context(DBusConnection *conn,
DBusMessage *msg, void *data)
{
@@ -1390,14 +1553,284 @@ void ofono_gprs_context_deactivated(struct ofono_gprs_context
*gc,
}
}
+static ofono_bool_t set_primary_context_powered(struct pri_context *ctx,
+ ofono_bool_t value)
+{
+ struct ofono_gprs_context *gc = ctx->gprs->context_driver;
+
+ if (gc == NULL || gc->driver->activate_primary == NULL ||
+ gc->driver->deactivate_primary == NULL ||
+ ctx->gprs->cid_map == NULL)
+ return FALSE;
+
+ if (gc->pending)
+ return FALSE;
+
+ if (ctx->active == value)
+ return FALSE;
+
+ if (value && !ctx->gprs->attached)
+ return FALSE;
+
+ if (ctx->gprs->flags & GPRS_FLAG_ATTACHING)
+ return FALSE;
+
+ if (value) {
+ ctx->context.cid = gprs_cid_alloc(ctx->gprs);
+
+ if (ctx->context.cid == 0)
+ return FALSE;
+
+ if (ctx->context.cid !=
+ idmap_get_min(ctx->gprs->cid_map)) {
+ ofono_error("Multiple active contexts are"
+ " not yet supported");
+
+ gprs_cid_release(ctx->gprs, ctx->context.cid);
+ ctx->context.cid = 0;
+
+ return FALSE;
+ }
+ }
+
+ if (value)
+ gc->driver->activate_primary(gc, &ctx->context,
+ pri_activate_callback, ctx);
+ else
+ gc->driver->deactivate_primary(gc, ctx->context.cid,
+ pri_deactivate_callback, ctx);
+
+ return TRUE;
+}
+
+static void ofono_emulator_dun_disconnect(struct ofono_gprs *gprs,
+ struct ofono_emulator_req *req)
+{
+ struct pri_context *dun;
+
+ dun = gprs_context_by_type(gprs, GPRS_CONTEXT_TYPE_DUN);
+ if (!dun) {
+ req->cb(G_AT_SERVER_RESULT_NO_CARRIER, req->cb_data);
+ return;
+ }
+
+ dun_deactivate(dun);
+
+ context_dbus_unregister(dun);
+ gprs->contexts = g_slist_remove(gprs->contexts, dun);
+
+ req->cb(G_AT_SERVER_RESULT_NO_CARRIER, req->cb_data);
+}
+
+static void ofono_emulator_dun_connect(struct ofono_gprs *gprs,
+ struct ofono_emulator_dun_connect_req *req)
+{
+ struct pri_context *dun;
+ const char *dns[3];
+
+ dun = gprs_context_by_type(gprs, GPRS_CONTEXT_TYPE_DUN);
+ if (!dun) {
+ req->cb(G_AT_SERVER_RESULT_ERROR, req->cb_data);
+ return;
+ }
+
+ dns[0] = req->dns1;
+ dns[1] = req->dns2;
+ dns[2] = 0;
+
+ dun_activate(req->iface, req->local, req->peer, dns, dun);
+
+ req->cb(G_AT_SERVER_RESULT_OK, req->cb_data);
+}
+
+static void ofono_emulator_remove_sources(struct ofono_gprs *gprs)
+{
+ if (gprs == NULL)
+ return;
+
+ if (gprs->timeout_source)
+ g_source_remove(gprs->timeout_source);
+
+ if (gprs->attach_source)
+ g_source_remove(gprs->attach_source);
+
+ if (gprs->context_source)
+ g_source_remove(gprs->context_source);
+}
+
+static gboolean emulator_gprs_timeout(gpointer user_data)
+{
+ struct gprs_dun_data *data = user_data;
+ struct ofono_gprs *gprs = data->gprs;
+ struct pri_context *context = data->context;
+ struct ofono_error error;
+
+ ofono_emulator_remove_sources(gprs);
+
+ if (context->active == TRUE)
+ goto done;
+
+ error.type = OFONO_ERROR_TYPE_FAILURE;
+ data->cb(&error, NULL, NULL, NULL, NULL, data->cb_data);
+
+done:
+ g_free(data);
+
+ return FALSE;
+}
+
+static gboolean pri_context_update(gpointer user_data)
+{
+ struct gprs_dun_data *data = user_data;
+ struct ofono_gprs *gprs = data->gprs;
+ struct pri_context *context = data->context;
+ struct context_settings *settings = context->settings;
+ struct ofono_error error;
+
+ DBG("active %d\n", context->active);
+
+ if (context->active != TRUE)
+ return TRUE;
+
+ gprs->context_source = 0;
+ g_source_remove(gprs->timeout_source);
+
+ error.type = OFONO_ERROR_TYPE_NO_ERROR;
+
+ data->cb(&error, settings->interface, DUN_LOCAL, DUN_PEER,
+ (const char **)settings->dns,
+ data->cb_data);
+
+ g_free(data);
+
+ return FALSE;
+}
+
+static gboolean gprs_attach_update(gpointer user_data)
+{
+ struct gprs_dun_data *data = user_data;
+ struct ofono_gprs *gprs = data->gprs;
+ struct pri_context *context = data->context;
+
+ DBG("attached %d driver attached %d\n", gprs->attached,
+ gprs->driver_attached);
+
+ if (gprs->attached != TRUE || gprs->driver_attached != TRUE)
+ return TRUE;
+
+ gprs->attach_source = 0;
+
+ if (context->active != TRUE) {
+ set_primary_context_powered(context, TRUE);
+
+ /* wait until the primary context is actived */
+ gprs->context_source = g_timeout_add_seconds(1,
+ pri_context_update, data);
+ return FALSE;
+ }
+
+ pri_context_update(data);
+
+ return FALSE;
+}
+
+static ofono_bool_t gprs_dun_connect(struct pri_context *dun,
+ ofono_emulator_gprs_connect_cb cb,
+ void *cb_data)
+{
+ struct ofono_gprs *gprs = dun->gprs;
+ struct pri_context *context;
+ struct gprs_dun_data *data = g_try_new0(struct gprs_dun_data, 1);
+
+ if (!data)
+ return FALSE;
+
+ /* Create context for real modem if not connected yet */
+ if (gprs->contexts)
+ context = gprs_context_by_type(gprs,
+ GPRS_CONTEXT_TYPE_INTERNET);
+ else
+ context = ofono_gprs_create_context(gprs,
+ "default", "internet");
+
+ if (!context)
+ return FALSE;
+
+ data->gprs = gprs;
+ data->cb = cb;
+ data->cb_data = cb_data;
+ data->context = context;
+
+ gprs->timeout_source = g_timeout_add_seconds(EMULATOR_GPRS_TIMEOUT,
+ emulator_gprs_timeout, data);
+
+ /* check gprs attaching status */
+ if (!gprs->powered || !gprs->attached || !gprs->driver_attached) {
+ gprs->powered = TRUE;
+
+ gprs_netreg_update(gprs);
+
+ /* wait until GPRS is attached */
+ gprs->attach_source = g_timeout_add_seconds(1,
+ gprs_attach_update, data);
+ return TRUE;
+ }
+
+ gprs_attach_update(data);
+
+ return TRUE;
+}
+
+static void ofono_emulator_gprs_connect(struct ofono_gprs *gprs,
+ struct ofono_emulator_gprs_connect_req *req)
+{
+ const char *dial_str = req->dial_str;
+ struct pri_context *dun;
+ struct ofono_error error;
+
+ DBG("dial call %s", dial_str);
+
+ /* Create DUN context if not exists */
+ dun = gprs_context_by_type(gprs, GPRS_CONTEXT_TYPE_DUN);
+ if (!dun) {
+ dun = ofono_gprs_create_context(gprs, "dun", "dun");
+ if (!dun)
+ goto error;
+ }
+
+ if (!gprs_dun_connect(dun, req->cb, req->cb_data))
+ goto error;
+
+ return;
+error:
+ error.type = OFONO_ERROR_TYPE_FAILURE;
+ req->cb(&error, NULL, NULL, NULL, NULL, req->cb_data);
+}
+
static void dun_status_watch(struct ofono_emulator *e,
enum ofono_emulator_status status,
void *data, void *user_data)
{
+ struct ofono_gprs *gprs = user_data;
+
if (e == NULL)
return;
switch (status) {
+ case OFONO_EMULATOR_STATUS_GPRS_CONNECT:
+ ofono_emulator_gprs_connect(gprs, data);
+ break;
+ case OFONO_EMULATOR_STATUS_DUN_CONNECTING:
+ ofono_emulator_dun_connect(gprs, data);
+ break;
+ case OFONO_EMULATOR_STATUS_DUN_CONNECTED:
+ break;
+ case OFONO_EMULATOR_STATUS_DUN_DISCONNECTED:
+ ofono_emulator_dun_disconnect(gprs, data);
+ break;
+ case OFONO_EMULATOR_STATUS_DESTROY:
+ ofono_emulator_remove_sources(gprs);
+ break;
default:
break;
}
@@ -1584,6 +2017,8 @@ static void gprs_unregister(struct ofono_atom *atom)
gprs->dun = NULL;
}
+ ofono_emulator_remove_sources(gprs);
+
ofono_modem_remove_interface(modem,
OFONO_DATA_CONNECTION_MANAGER_INTERFACE);
g_dbus_unregister_interface(conn, path,
--
1.6.3.3