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 _______________________________________________ ofono mailing list ofono@ofono.org http://lists.ofono.org/listinfo/ofono