---
 src/gprs.c |  916 ++++++++++++++++++++++++++++++++++++++++++++----------------
 1 files changed, 679 insertions(+), 237 deletions(-)

diff --git a/src/gprs.c b/src/gprs.c
index 37e7aee..6edeef6 100644
--- a/src/gprs.c
+++ b/src/gprs.c
@@ -54,6 +54,7 @@
 #define MAX_MESSAGE_CENTER_LENGTH 255
 #define MAX_CONTEXTS 256
 #define SUSPEND_TIMEOUT 8
+#define ACTIVATE_FAIL_DELAY 2
 
 static GSList *g_drivers = NULL;
 static GSList *g_context_drivers = NULL;
@@ -97,19 +98,11 @@ struct ofono_gprs {
        struct ofono_atom *atom;
 };
 
-struct ofono_gprs_context {
-       struct ofono_gprs *gprs;
-       enum ofono_gprs_context_type type;
-       ofono_bool_t inuse;
-       const struct ofono_gprs_context_driver *driver;
-       void *driver_data;
-       struct ofono_atom *atom;
-};
+struct pri_context;
 
-struct context_settings {
-       enum ofono_gprs_context_type type;
+struct context_settings_ip {
        char *interface;
-       gboolean static_ip;
+       enum ofono_gprs_addrconf_method method;
        char *ip;
        char *netmask;
        char *gateway;
@@ -117,21 +110,43 @@ struct context_settings {
        char *proxy;
 };
 
+struct context_settings_ipv6 {
+       char *interface;
+       char *address;
+       char **dns;
+};
+
+struct ofono_gprs_context {
+       struct ofono_gprs *gprs;
+       struct pri_context *pri;
+       enum ofono_gprs_context_type type;
+       enum ofono_gprs_proto proto;
+       struct context_settings_ip *settings_ip;
+       struct context_settings_ipv6 *settings_ipv6;
+       guint timer;
+       unsigned int cid;
+       const struct ofono_gprs_context_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+};
+
 struct pri_context {
        ofono_bool_t active;
        enum ofono_gprs_context_type type;
        char name[MAX_CONTEXT_NAME_LENGTH + 1];
        char message_proxy[MAX_MESSAGE_PROXY_LENGTH + 1];
        char message_center[MAX_MESSAGE_CENTER_LENGTH + 1];
+       char apn[OFONO_GPRS_MAX_APN_LENGTH + 1];
+       char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1];
+       char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1];
+       enum ofono_gprs_proto proto;
        unsigned int id;
        char *path;
        char *key;
-       struct context_settings *settings;
        char *proxy_host;
        uint16_t proxy_port;
        DBusMessage *pending;
-       struct ofono_gprs_primary_context context;
-       struct ofono_gprs_context *context_driver;
+       GSList *bearers;
        struct ofono_gprs *gprs;
 };
 
@@ -240,11 +255,26 @@ static gboolean gprs_proto_from_string(const char *str,
        } else if (g_str_equal(str, "ipv6")) {
                *proto = OFONO_GPRS_PROTO_IPV6;
                return TRUE;
+       } else if (g_str_equal(str, "ipv4v6")) {
+               *proto = OFONO_GPRS_PROTO_IPV4V6;
+               return TRUE;
        }
 
        return FALSE;
 }
 
+static const char *gprs_addrconf_to_string(enum ofono_gprs_proto method)
+{
+       switch (method) {
+       case OFONO_GPRS_ADDRCONF_STATIC:
+               return "static";
+       case OFONO_GPRS_ADDRCONF_DHCP:
+               return "dhcp";
+       }
+
+       return NULL;
+}
+
 static unsigned int gprs_cid_alloc(struct ofono_gprs *gprs)
 {
        return idmap_alloc(gprs->cid_map);
@@ -270,21 +300,37 @@ static struct pri_context *gprs_context_by_path(struct 
ofono_gprs *gprs,
        return NULL;
 }
 
-static void context_settings_free(struct context_settings *settings)
+static void context_settings_free(struct ofono_gprs_context *gc)
+{
+       if (gc->settings_ip == NULL)
+               return;
+
+       g_free(gc->settings_ip->interface);
+       g_free(gc->settings_ip->ip);
+       g_free(gc->settings_ip->netmask);
+       g_free(gc->settings_ip->gateway);
+       g_strfreev(gc->settings_ip->dns);
+       g_free(gc->settings_ip->proxy);
+       g_free(gc->settings_ip);
+       gc->settings_ip = NULL;
+}
+
+static void context_ipv6settings_free(struct ofono_gprs_context *gc)
 {
-       g_free(settings->interface);
-       g_free(settings->ip);
-       g_free(settings->netmask);
-       g_free(settings->gateway);
-       g_strfreev(settings->dns);
-       g_free(settings->proxy);
+       if (gc->settings_ipv6 == NULL)
+               return;
 
-       g_free(settings);
+       g_free(gc->settings_ipv6->interface);
+       g_free(gc->settings_ipv6->address);
+       g_strfreev(gc->settings_ipv6->dns);
+       g_free(gc->settings_ipv6);
+       gc->settings_ipv6 = NULL;
 }
 
-static void context_settings_append_variant(struct context_settings *settings,
+static void context_settings_append_variant(struct ofono_gprs_context *gc,
                                                DBusMessageIter *iter)
 {
+       struct context_settings_ip *settings = gc->settings_ip;
        DBusMessageIter variant;
        DBusMessageIter array;
        char typesig[5];
@@ -306,22 +352,21 @@ static void context_settings_append_variant(struct 
context_settings *settings,
        if (settings == NULL)
                goto done;
 
-       ofono_dbus_dict_append(&array, "Interface",
-                               DBUS_TYPE_STRING, &settings->interface);
+       if (settings->interface != NULL)
+               ofono_dbus_dict_append(&array, "Interface",
+                                       DBUS_TYPE_STRING, &settings->interface);
 
-       if (settings->type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
+       if (gc->pri->type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
                if (settings->proxy)
                        ofono_dbus_dict_append(&array, "Proxy",
                                        DBUS_TYPE_STRING, &settings->proxy);
                goto done;
        }
 
-       if (settings->static_ip == TRUE)
-               method = "static";
-       else
-               method = "dhcp";
-
-       ofono_dbus_dict_append(&array, "Method", DBUS_TYPE_STRING, &method);
+       method = gprs_addrconf_to_string(settings->method);
+       if (method != NULL)
+               ofono_dbus_dict_append(&array, "Method", DBUS_TYPE_STRING,
+                                       &method);
 
        if (settings->ip)
                ofono_dbus_dict_append(&array, "Address", DBUS_TYPE_STRING,
@@ -346,7 +391,7 @@ done:
        dbus_message_iter_close_container(iter, &variant);
 }
 
-static void context_settings_append_dict(struct context_settings *settings,
+static void context_settings_append_dict(struct ofono_gprs_context *gc,
                                                DBusMessageIter *dict)
 {
        DBusMessageIter entry;
@@ -357,14 +402,15 @@ static void context_settings_append_dict(struct 
context_settings *settings,
 
        dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
 
-       context_settings_append_variant(settings, &entry);
+       context_settings_append_variant(gc, &entry);
 
        dbus_message_iter_close_container(dict, &entry);
 }
 
-static void pri_context_signal_settings(struct pri_context *ctx)
+static void context_signal_settings(struct ofono_gprs_context *gc)
 {
        DBusConnection *conn = ofono_dbus_get_connection();
+       struct pri_context *ctx = gc->pri;
        const char *path = ctx->path;
        DBusMessage *signal;
        DBusMessageIter iter;
@@ -374,6 +420,98 @@ static void pri_context_signal_settings(struct pri_context 
*ctx)
                                        OFONO_CONNECTION_CONTEXT_INTERFACE,
                                        "PropertyChanged");
 
+       DBG("signal = %p", signal);
+
+       if (signal == NULL)
+               return;
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &prop);
+
+       context_settings_append_variant(gc, &iter);
+
+       g_dbus_send_message(conn, signal);
+}
+
+static void context_ipv6settings_append_variant(struct ofono_gprs_context *gc,
+                                               DBusMessageIter *iter)
+{
+       struct context_settings_ipv6 *settings = gc->settings_ipv6;
+       DBusMessageIter variant;
+       DBusMessageIter array;
+       char typesig[5];
+       char arraysig[6];
+
+       arraysig[0] = DBUS_TYPE_ARRAY;
+       arraysig[1] = typesig[0] = DBUS_DICT_ENTRY_BEGIN_CHAR;
+       arraysig[2] = typesig[1] = DBUS_TYPE_STRING;
+       arraysig[3] = typesig[2] = DBUS_TYPE_VARIANT;
+       arraysig[4] = typesig[3] = DBUS_DICT_ENTRY_END_CHAR;
+       arraysig[5] = typesig[4] = '\0';
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                               arraysig, &variant);
+       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+                                               typesig, &array);
+
+       if (settings == NULL)
+               goto done;
+
+       if (settings->interface != NULL)
+               ofono_dbus_dict_append(&array, "Interface",
+                                       DBUS_TYPE_STRING, &settings->interface);
+
+       if (settings->address != NULL) {
+         uint8_t addr[16];
+         char *strll = alloca(INET6_ADDRSTRLEN);
+
+         inet_pton(AF_INET6, settings->address, addr);
+         memset(addr, 0, 8);
+         addr[0] = 0xf0;
+         addr[1] = 0x80;
+         inet_ntop(AF_INET6, addr, strll, INET6_ADDRSTRLEN);
+         ofono_dbus_dict_append(&array, "LinkLocal", DBUS_TYPE_STRING, &strll);
+       }
+
+       if (settings->dns != NULL)
+               ofono_dbus_dict_append_array(&array, "DomainNameServers",
+                                       DBUS_TYPE_STRING, &settings->dns);
+done:
+       dbus_message_iter_close_container(&variant, &array);
+
+       dbus_message_iter_close_container(iter, &variant);
+}
+
+static void context_ipv6settings_append_dict(struct ofono_gprs_context *gc,
+                                               DBusMessageIter *dict)
+{
+       DBusMessageIter entry;
+       const char *key = "IPv6Settings";
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                               NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       context_ipv6settings_append_variant(gc, &entry);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static void context_signal_ipv6settings(struct ofono_gprs_context *gc)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       struct pri_context *ctx = gc->pri;
+       const char *path = ctx->path;
+       DBusMessage *signal;
+       DBusMessageIter iter;
+       const char *prop = "IPv6Settings";
+
+       signal = dbus_message_new_signal(path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "PropertyChanged");
+
        if (signal == NULL)
                return;
 
@@ -381,11 +519,28 @@ static void pri_context_signal_settings(struct 
pri_context *ctx)
 
        dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &prop);
 
-       context_settings_append_variant(ctx->settings, &iter);
+       context_ipv6settings_append_variant(gc, &iter);
 
        g_dbus_send_message(conn, signal);
 }
 
+static void pri_context_settings_append_dict(struct pri_context *ctx,
+                                               DBusMessageIter *dict)
+{
+       struct ofono_gprs_context *ip = NULL, *ipv6 = NULL;
+       GSList *l;
+
+       for (l = ctx->bearers; l; l = l->next) {
+               struct ofono_gprs_context *gc = l->data;
+
+               if (gc->settings_ip)
+                       context_settings_append_dict(gc, dict);
+
+               if (gc->settings_ipv6)
+                       context_ipv6settings_append_dict(gc, dict);
+       }
+}
+
 static void pri_parse_proxy(struct pri_context *ctx, const char *proxy)
 {
        char *scheme, *host, *port, *path;
@@ -552,78 +707,62 @@ static void pri_setproxy(const char *interface, const 
char *proxy)
 
 static void pri_reset_context_settings(struct pri_context *ctx)
 {
-       char *interface;
-
-       if (ctx->settings == NULL)
-               return;
-
-       interface = ctx->settings->interface;
-       ctx->settings->interface = NULL;
-
-       context_settings_free(ctx->settings);
-       ctx->settings = NULL;
-
-       pri_context_signal_settings(ctx);
-
        if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
-               pri_setaddr(interface, NULL);
-
                g_free(ctx->proxy_host);
                ctx->proxy_host = NULL;
                ctx->proxy_port = 0;
        }
-
-       pri_ifupdown(interface, FALSE);
-
-       g_free(interface);
 }
 
-static void pri_update_context_settings(struct pri_context *ctx,
-                                       const char *interface,
-                                       ofono_bool_t static_ip,
-                                       const char *ip, const char *netmask,
-                                       const char *gateway, const char **dns)
+static void pri_update_context_settings(struct pri_context *ctx)
 {
-       if (ctx->settings)
-               context_settings_free(ctx->settings);
+       struct ofono_gprs_context *ip = NULL, *ipv6 = NULL;
+       GSList *l;
 
-       ctx->settings = g_try_new0(struct context_settings, 1);
-       if (ctx->settings == NULL)
-               return;
+       for (l = ctx->bearers; l; l = l->next) {
+               struct ofono_gprs_context *gc = l->data;
 
-       ctx->settings->type = ctx->type;
+               if (gc->settings_ip != NULL)
+                       ip = gc;
 
-       ctx->settings->interface = g_strdup(interface);
-       ctx->settings->static_ip = static_ip;
-       ctx->settings->ip = g_strdup(ip);
-       ctx->settings->netmask = g_strdup(netmask);
-       ctx->settings->gateway = g_strdup(gateway);
-       ctx->settings->dns = g_strdupv((char **)dns);
+               if (gc->settings_ipv6 != NULL)
+                       ipv6 = gc;
+       }
 
-       if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS && ctx->message_proxy)
-               ctx->settings->proxy = g_strdup(ctx->message_proxy);
+       if (ip != NULL) {
+               pri_ifupdown(ip->settings_ip->interface, TRUE);
 
-       pri_ifupdown(interface, TRUE);
+               if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS &&
+                               ctx->message_proxy != NULL) {
+                       ip->settings_ip->proxy = g_strdup(ctx->message_proxy);
+                       pri_parse_proxy(ctx, ctx->message_proxy);
 
-       if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
-               pri_parse_proxy(ctx, ctx->message_proxy);
+                       DBG("proxy %s port %u", ctx->proxy_host,
+                               ctx->proxy_port);
+
+                       pri_setaddr(ip->settings_ip->interface,
+                                       ip->settings_ip->ip);
 
-               DBG("proxy %s port %u", ctx->proxy_host, ctx->proxy_port);
+                       if (ctx->proxy_host)
+                               pri_setproxy(ip->settings_ip->interface,
+                                               ctx->proxy_host);
 
-               pri_setaddr(interface, ip);
+               }
 
-               if (ctx->proxy_host)
-                       pri_setproxy(interface, ctx->proxy_host);
+               context_signal_settings(ip);
        }
 
-       pri_context_signal_settings(ctx);
+       if (ipv6 != NULL) {
+               pri_ifupdown(ipv6->settings_ipv6->interface, TRUE);
+               context_signal_ipv6settings(ipv6);
+       }
 }
 
 static void append_context_properties(struct pri_context *ctx,
                                        DBusMessageIter *dict)
 {
        const char *type = gprs_context_type_to_string(ctx->type);
-       const char *proto = gprs_proto_to_string(ctx->context.proto);
+       const char *proto = gprs_proto_to_string(ctx->proto);
        const char *name = ctx->name;
        dbus_bool_t value;
        const char *strvalue;
@@ -637,15 +776,15 @@ static void append_context_properties(struct pri_context 
*ctx,
 
        ofono_dbus_dict_append(dict, "Protocol", DBUS_TYPE_STRING, &proto);
 
-       strvalue = ctx->context.apn;
+       strvalue = ctx->apn;
        ofono_dbus_dict_append(dict, "AccessPointName", DBUS_TYPE_STRING,
                                &strvalue);
 
-       strvalue = ctx->context.username;
+       strvalue = ctx->username;
        ofono_dbus_dict_append(dict, "Username", DBUS_TYPE_STRING,
                                &strvalue);
 
-       strvalue = ctx->context.password;
+       strvalue = ctx->password;
        ofono_dbus_dict_append(dict, "Password", DBUS_TYPE_STRING,
                                &strvalue);
 
@@ -659,7 +798,7 @@ static void append_context_properties(struct pri_context 
*ctx,
                                        DBUS_TYPE_STRING, &strvalue);
        }
 
-       context_settings_append_dict(ctx->settings, dict);
+       pri_context_settings_append_dict(ctx, dict);
 }
 
 static DBusMessage *pri_get_properties(DBusConnection *conn,
@@ -685,44 +824,243 @@ static DBusMessage *pri_get_properties(DBusConnection 
*conn,
        return reply;
 }
 
-static void pri_activate_callback(const struct ofono_error *error,
-                                       const char *interface,
-                                       ofono_bool_t static_ip,
-                                       const char *ip, const char *netmask,
-                                       const char *gateway, const char **dns,
-                                       void *data)
+static void pri_context_activated(struct pri_context *ctx)
 {
-       struct pri_context *ctx = data;
        DBusConnection *conn = ofono_dbus_get_connection();
        dbus_bool_t value;
 
-       DBG("%p %s", ctx, interface);
+       ctx->active = TRUE;
+       __ofono_dbus_pending_reply(&ctx->pending,
+                               dbus_message_new_method_return(ctx->pending));
+
+       pri_update_context_settings(ctx);
+
+       value = ctx->active;
+       ofono_dbus_signal_property_changed(conn, ctx->path,
+                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
+                                       "Active", DBUS_TYPE_BOOLEAN, &value);
+}
+
+static struct ofono_gprs_context *assign_packet_bearer(struct pri_context *ctx)
+{
+       struct ofono_gprs *gprs = ctx->gprs;
+       struct idmap *cidmap = gprs->cid_map;
+       unsigned int cid_min, cid;
+       GSList *l;
+
+       if (cidmap == NULL)
+               return NULL;
+
+       cid_min = idmap_get_min(cidmap);
+
+       cid = gprs_cid_alloc(gprs);
+       if (cid == 0)
+               return NULL;
+
+       for (l = gprs->context_drivers; l; l = l->next) {
+               struct ofono_gprs_context *gc = l->data;
+
+               if (gc->pri != NULL)
+                       continue;
+
+               if (gc->type == OFONO_GPRS_CONTEXT_TYPE_ANY ||
+                               gc->type == ctx->type) {
+                       gc->cid = cid;
+                       gc->pri = ctx;
+                       ctx->bearers = g_slist_prepend(ctx->bearers, gc);
+                       return gc;
+               }
+       }
+
+       return NULL;
+}
+
+static void release_packet_bearer(struct ofono_gprs_context *gc)
+{
+       struct pri_context *ctx;
+       ofono_bool_t ip, ipv6;
+
+       if (gc == NULL)
+               return;
+
+       ctx = gc->pri;
+
+       if (gc->settings_ip != NULL) {
+               if (ctx->type == OFONO_GPRS_CONTEXT_TYPE_MMS)
+                       pri_setaddr(gc->settings_ip->interface, NULL);
+
+               pri_ifupdown(gc->settings_ip->interface, FALSE);
+               context_settings_free(gc);
+
+               if (ctx->active == TRUE)
+                       context_signal_settings(gc);
+       }
+
+       if (gc->settings_ipv6 != NULL) {
+               pri_ifupdown(gc->settings_ipv6->interface, FALSE);
+               context_ipv6settings_free(gc);
+
+               if (ctx->active == TRUE)
+                       context_signal_ipv6settings(gc);
+       }
+
+       if (gc->timer) {
+               g_source_remove(gc->timer);
+               gc->timer = 0;
+       }
+
+       gprs_cid_release(ctx->gprs, gc->cid);
+       gc->cid = 0;
+       ctx->bearers = g_slist_remove(ctx->bearers, gc);
+       gc->pri = NULL;
+}
+
+static void pri_activate_error(struct pri_context *ctx)
+{
+       __ofono_dbus_pending_reply(&ctx->pending,
+                               __ofono_error_failed(ctx->pending));
+
+       while (ctx->bearers != NULL)
+               release_packet_bearer(ctx->bearers->data);
+}
+
+static gboolean pri_activate_next(gpointer data);
+
+static void pri_activate_context_callback(const struct ofono_error *error,
+                                       ofono_bool_t single_bearers,
+                                       void *data)
+{
+       struct ofono_gprs_context *gc = data;
+       struct pri_context *ctx = gc->pri;
+       enum ofono_gprs_proto proto = gc->proto;
+       int bearers = g_slist_length(ctx->bearers);
+
+       if (ctx == NULL)
+               return;
 
        if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
-               DBG("Activating context failed with error: %s",
+
+               if (bearers > 1) {
+                       DBG("Activating second bearer failed with error: %s",
                                telephony_error_to_str(error));
-               __ofono_dbus_pending_reply(&ctx->pending,
-                                       __ofono_error_failed(ctx->pending));
+                       release_packet_bearer(gc);
+                       pri_context_activated(ctx);
+                       return;
+               }
 
-               gprs_cid_release(ctx->gprs, ctx->context.cid);
-               ctx->context.cid = 0;
-               ctx->context_driver->inuse = FALSE;
-               ctx->context_driver = NULL;
+               DBG("Activating bearer failed with error: %s",
+                               telephony_error_to_str(error));
+
+               if (ctx->proto != OFONO_GPRS_PROTO_IPV4V6 ||
+                               proto == OFONO_GPRS_PROTO_IPV6) {
+                       pri_activate_error(ctx);
+                       return;
+               }
+
+               /* Retry first bearer with different protocol */
+               if (proto == OFONO_GPRS_PROTO_IPV4V6)
+                       gc->proto = OFONO_GPRS_PROTO_IP;
+               else
+                       gc->proto = OFONO_GPRS_PROTO_IPV6;
 
+               gc->timer = g_timeout_add_seconds(ACTIVATE_FAIL_DELAY,
+                                                       pri_activate_next, gc);
                return;
        }
 
-       ctx->active = TRUE;
+       if (ctx->proto != OFONO_GPRS_PROTO_IPV4V6 || single_bearers == FALSE ||
+                       bearers > 1 || gc->proto == OFONO_GPRS_PROTO_IPV6) {
+               pri_context_activated(ctx);
+               return;
+       }
+
+       /* Activate second bearer */
+       if (gc->settings_ip == NULL)
+               proto = OFONO_GPRS_PROTO_IP;
+       else
+               proto = OFONO_GPRS_PROTO_IPV6;
+
+       gc = assign_packet_bearer(ctx);
+       if (gc == NULL) {
+               /* No suitable driver. Go with a single bearer. */
+               pri_context_activated(ctx);
+               return;
+       }
+
+       gc->proto = proto;
+       gc->timer = g_timeout_add_seconds(0, pri_activate_next, gc);
+}
+
+static gboolean pri_activate_next(gpointer data)
+{
+       struct ofono_gprs_context *gc = data;
+       struct pri_context *ctx = gc->pri;
+
+       gc->timer = 0;
+       gc->driver->activate_primary_new(gc, gc->cid, ctx->apn, gc->proto,
+                                               ctx->username, ctx->password,
+                                               pri_activate_context_callback,
+                                               gc);
+       return FALSE;
+}
+
+/* Backward compatibility hack (to be removed) ==> */
+
+static void pri_activate_callback_shim(const struct ofono_error *error,
+                               const char *interface, ofono_bool_t static_ip,
+                               const char *address, const char *netmask,
+                               const char *gw, const char **dns, void *data)
+{
+       struct ofono_gprs_context *gc = data;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
+               ofono_gprs_context_set_ip_interface(gc, interface);
+               ofono_gprs_context_set_ip_addrconf(gc, static_ip ?
+                                               OFONO_GPRS_ADDRCONF_STATIC :
+                                               OFONO_GPRS_ADDRCONF_DHCP);
+               ofono_gprs_context_set_ip_address(gc, address);
+               ofono_gprs_context_set_ip_netmask(gc, netmask);
+               ofono_gprs_context_set_ip_gateway(gc, gw);
+               ofono_gprs_context_set_ip_dns_servers(gc, dns);
+       }
+
+       pri_activate_context_callback(error, FALSE, data);
+}
+
+static void activate_primary_shim(struct ofono_gprs_context *gc,
+                                       unsigned int id,
+                                       const char *apn,
+                                       enum ofono_gprs_proto proto,
+                                       const char *username,
+                                       const char *password,
+                                       void *data)
+{
+       struct ofono_gprs_primary_context context;
+
+       memset(&context, 0, sizeof(context));
+       context.cid = gc->cid;
+       strncpy(context.apn, apn, OFONO_GPRS_MAX_APN_LENGTH);
+       strncpy(context.username, username, OFONO_GPRS_MAX_USERNAME_LENGTH);
+       strncpy(context.password, password, OFONO_GPRS_MAX_PASSWORD_LENGTH);
+       context.proto = proto;
+
+       gc->driver->activate_primary(gc, &context, pri_activate_callback_shim,
+                                       data);
+}
+
+/* <== end of backward compatiblity hack */
+
+static void pri_context_deactivated(struct pri_context *ctx)
+{
+       DBusConnection *conn = ofono_dbus_get_connection();
+       dbus_bool_t value;
+
+       ctx->active = FALSE;
+
        __ofono_dbus_pending_reply(&ctx->pending,
                                dbus_message_new_method_return(ctx->pending));
 
-       /*
-        * 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, static_ip,
-                                               ip, netmask, gateway, dns);
+       pri_reset_context_settings(ctx);
 
        value = ctx->active;
        ofono_dbus_signal_property_changed(conn, ctx->path,
@@ -730,11 +1068,11 @@ static void pri_activate_callback(const struct 
ofono_error *error,
                                        "Active", DBUS_TYPE_BOOLEAN, &value);
 }
 
-static void pri_deactivate_callback(const struct ofono_error *error, void 
*data)
+static void pri_deactivate_callback(const struct ofono_error *error,
+                                               void *data)
 {
-       struct pri_context *ctx = data;
-       DBusConnection *conn = ofono_dbus_get_connection();
-       dbus_bool_t value;
+       struct ofono_gprs_context *gc = data;
+       struct pri_context *ctx = gc->pri;
 
        if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
                DBG("Deactivating context failed with error: %s",
@@ -744,21 +1082,16 @@ static void pri_deactivate_callback(const struct 
ofono_error *error, void *data)
                return;
        }
 
-       gprs_cid_release(ctx->gprs, ctx->context.cid);
-       ctx->context.cid = 0;
-       ctx->active = FALSE;
-       ctx->context_driver->inuse = FALSE;
-       ctx->context_driver = NULL;
-
-       __ofono_dbus_pending_reply(&ctx->pending,
-                               dbus_message_new_method_return(ctx->pending));
+       release_packet_bearer(gc);
 
-       pri_reset_context_settings(ctx);
+       if (ctx->bearers != NULL) {
+               gc = ctx->bearers->data;
+               gc->driver->deactivate_primary(gc, gc->cid,
+                                       pri_deactivate_callback, gc);
+               return;
+       }
 
-       value = ctx->active;
-       ofono_dbus_signal_property_changed(conn, ctx->path,
-                                       OFONO_CONNECTION_CONTEXT_INTERFACE,
-                                       "Active", DBUS_TYPE_BOOLEAN, &value);
+       pri_context_deactivated(ctx);
 }
 
 static DBusMessage *pri_set_apn(struct pri_context *ctx, DBusConnection *conn,
@@ -769,13 +1102,13 @@ static DBusMessage *pri_set_apn(struct pri_context *ctx, 
DBusConnection *conn,
        if (strlen(apn) > OFONO_GPRS_MAX_APN_LENGTH)
                return __ofono_error_invalid_format(msg);
 
-       if (g_str_equal(apn, ctx->context.apn))
+       if (g_str_equal(apn, ctx->apn))
                return dbus_message_new_method_return(msg);
 
        if (is_valid_apn(apn) == FALSE)
                return __ofono_error_invalid_format(msg);
 
-       strcpy(ctx->context.apn, apn);
+       strcpy(ctx->apn, apn);
 
        if (settings) {
                g_key_file_set_string(settings, ctx->key,
@@ -802,10 +1135,10 @@ static DBusMessage *pri_set_username(struct pri_context 
*ctx,
        if (strlen(username) > OFONO_GPRS_MAX_USERNAME_LENGTH)
                return __ofono_error_invalid_format(msg);
 
-       if (g_str_equal(username, ctx->context.username))
+       if (g_str_equal(username, ctx->username))
                return dbus_message_new_method_return(msg);
 
-       strcpy(ctx->context.username, username);
+       strcpy(ctx->username, username);
 
        if (settings) {
                g_key_file_set_string(settings, ctx->key,
@@ -832,10 +1165,10 @@ static DBusMessage *pri_set_password(struct pri_context 
*ctx,
        if (strlen(password) > OFONO_GPRS_MAX_PASSWORD_LENGTH)
                return __ofono_error_invalid_format(msg);
 
-       if (g_str_equal(password, ctx->context.password))
+       if (g_str_equal(password, ctx->password))
                return dbus_message_new_method_return(msg);
 
-       strcpy(ctx->context.password, password);
+       strcpy(ctx->password, password);
 
        if (settings) {
                g_key_file_set_string(settings, ctx->key,
@@ -891,10 +1224,10 @@ static DBusMessage *pri_set_proto(struct pri_context 
*ctx,
        if (gprs_proto_from_string(str, &proto) == FALSE)
                return __ofono_error_invalid_format(msg);
 
-       if (ctx->context.proto == proto)
+       if (ctx->proto == proto)
                return dbus_message_new_method_return(msg);
 
-       ctx->context.proto = proto;
+       ctx->proto = proto;
 
        if (settings) {
                g_key_file_set_string(settings, ctx->key, "Protocol", str);
@@ -995,42 +1328,11 @@ static DBusMessage *pri_set_message_center(struct 
pri_context *ctx,
        return NULL;
 }
 
-static gboolean assign_context(struct pri_context *ctx)
-{
-       struct idmap *cidmap = ctx->gprs->cid_map;
-       unsigned int cid_min;
-       GSList *l;
-
-       if (cidmap == NULL)
-               return FALSE;
-
-       cid_min = idmap_get_min(cidmap);
-
-       ctx->context.cid = gprs_cid_alloc(ctx->gprs);
-       if (ctx->context.cid == 0)
-               return FALSE;
-
-       for (l = ctx->gprs->context_drivers; l; l = l->next) {
-               struct ofono_gprs_context *gc = l->data;
-
-               if (gc->inuse == TRUE)
-                       continue;
-
-               if (gc->type == OFONO_GPRS_CONTEXT_TYPE_ANY ||
-                                               gc->type == ctx->type) {
-                       ctx->context_driver = gc;
-                       ctx->context_driver->inuse = TRUE;
-                       return TRUE;
-               }
-       }
-
-       return FALSE;
-}
-
 static DBusMessage *pri_set_property(DBusConnection *conn,
                                        DBusMessage *msg, void *data)
 {
        struct pri_context *ctx = data;
+       struct ofono_gprs *gprs = ctx->gprs;
        DBusMessageIter iter;
        DBusMessageIter var;
        const char *property;
@@ -1052,9 +1354,9 @@ static DBusMessage *pri_set_property(DBusConnection *conn,
        dbus_message_iter_recurse(&iter, &var);
 
        if (g_str_equal(property, "Active")) {
-               struct ofono_gprs_context *gc;
+               struct ofono_gprs_context *gc = NULL;
 
-               if (ctx->gprs->pending)
+               if (gprs->pending)
                        return __ofono_error_busy(msg);
 
                if (ctx->pending)
@@ -1068,31 +1370,49 @@ static DBusMessage *pri_set_property(DBusConnection 
*conn,
                if (ctx->active == (ofono_bool_t) value)
                        return dbus_message_new_method_return(msg);
 
-               if (value && !ctx->gprs->attached)
+               if (value && !gprs->attached)
                        return __ofono_error_not_attached(msg);
 
-               if (ctx->gprs->flags & GPRS_FLAG_ATTACHING)
+               if (gprs->flags & GPRS_FLAG_ATTACHING)
                        return __ofono_error_attach_in_progress(msg);
 
-               if (value) {
-                       if (assign_context(ctx) == FALSE)
-                               return __ofono_error_not_implemented(msg);
-               }
+               if (value)
+                       gc = assign_packet_bearer(ctx);
+               else if (ctx->bearers != NULL)
+                       gc = ctx->bearers->data;
 
-               gc = ctx->context_driver;
                if (gc == NULL || gc->driver == NULL ||
-                               gc->driver->activate_primary == NULL ||
+                               (gc->driver->activate_primary == NULL &&
+                               gc->driver->activate_primary_new == NULL) ||
                                gc->driver->deactivate_primary == NULL)
                        return __ofono_error_not_implemented(msg);
 
                ctx->pending = dbus_message_ref(msg);
 
-               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);
+               if (value) {
+                       gc->proto = ctx->proto;
+                       /* Backward compatiblity hack (to be removed) ==> */
+                       if (gc->driver->activate_primary_new == NULL)
+                               activate_primary_shim(gc,
+                                               gc->cid,
+                                               ctx->apn,
+                                               gc->proto,
+                                               ctx->username,
+                                               ctx->password,
+                                               gc);
+                       else
+                       /* <== end of backward compatiblity hack */
+                               gc->driver->activate_primary_new(gc,
+                                               gc->cid,
+                                               ctx->apn,
+                                               gc->proto,
+                                               ctx->username,
+                                               ctx->password,
+                                               pri_activate_context_callback,
+                                               gc);
+               } else
+                       gc->driver->deactivate_primary(gc, gc->cid,
+                                               pri_deactivate_callback, gc);
 
                return NULL;
        }
@@ -1207,10 +1527,7 @@ static void pri_context_destroy(gpointer userdata)
 {
        struct pri_context *ctx = userdata;
 
-       if (ctx->settings) {
-               context_settings_free(ctx->settings);
-               ctx->settings = NULL;
-       }
+       pri_reset_context_settings(ctx);
 
        g_free(ctx->proxy_host);
 
@@ -1344,13 +1661,10 @@ static void gprs_attached_update(struct ofono_gprs 
*gprs)
                        if (ctx->active == FALSE)
                                continue;
 
-                       gprs_cid_release(gprs, ctx->context.cid);
-                       ctx->context.cid = 0;
                        ctx->active = FALSE;
-                       ctx->context_driver->inuse = FALSE;
-                       ctx->context_driver = NULL;
 
-                       pri_reset_context_settings(ctx);
+                       while (ctx->bearers != NULL)
+                               release_packet_bearer(ctx->bearers->data);
 
                        value = FALSE;
                        ofono_dbus_signal_property_changed(conn, ctx->path,
@@ -1597,15 +1911,15 @@ static void write_context_settings(struct ofono_gprs 
*gprs,
        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);
+                               "AccessPointName", context->apn);
        g_key_file_set_string(gprs->settings, context->key,
-                               "Username", context->context.username);
+                               "Username", context->username);
        g_key_file_set_string(gprs->settings, context->key,
-                               "Password", context->context.password);
+                               "Password", context->password);
        g_key_file_set_string(gprs->settings, context->key, "Type",
                                gprs_context_type_to_string(context->type));
        g_key_file_set_string(gprs->settings, context->key, "Protocol",
-                               gprs_proto_to_string(context->context.proto));
+                               gprs_proto_to_string(context->proto));
 }
 
 static struct pri_context *add_context(struct ofono_gprs *gprs,
@@ -1711,7 +2025,8 @@ static DBusMessage *gprs_add_context(DBusConnection *conn,
 static void gprs_deactivate_for_remove(const struct ofono_error *error,
                                                void *data)
 {
-       struct pri_context *ctx = data;
+       struct ofono_gprs_context *gc = data;
+       struct pri_context *ctx = gc->pri;
        struct ofono_gprs *gprs = ctx->gprs;
        DBusConnection *conn;
        char *path;
@@ -1726,10 +2041,14 @@ static void gprs_deactivate_for_remove(const struct 
ofono_error *error,
                return;
        }
 
-       gprs_cid_release(gprs, ctx->context.cid);
-       ctx->context.cid = 0;
-       ctx->context_driver->inuse = FALSE;
-       ctx->context_driver = NULL;
+       release_packet_bearer(gc);
+
+       if (ctx->bearers != NULL) {
+               gc = ctx->bearers->data;
+               gc->driver->deactivate_primary(gc, gc->cid,
+                                       gprs_deactivate_for_remove, gc);
+               return;
+       }
 
        if (gprs->settings) {
                g_key_file_remove_group(gprs->settings, ctx->key, NULL);
@@ -1776,15 +2095,16 @@ static DBusMessage *gprs_remove_context(DBusConnection 
*conn,
                return __ofono_error_not_found(msg);
 
        if (ctx->active) {
-               struct ofono_gprs_context *gc = ctx->context_driver;
+               struct ofono_gprs_context *gc;
 
                /* This context is already being messed with */
                if (ctx->pending)
                        return __ofono_error_busy(msg);
 
                gprs->pending = dbus_message_ref(msg);
-               gc->driver->deactivate_primary(gc, ctx->context.cid,
-                                       gprs_deactivate_for_remove, ctx);
+               gc = ctx->bearers->data;
+               gc->driver->deactivate_primary(gc, gc->cid,
+                                       gprs_deactivate_for_remove, gc);
                return NULL;
        }
 
@@ -1810,7 +2130,8 @@ static DBusMessage *gprs_remove_context(DBusConnection 
*conn,
 static void gprs_deactivate_for_all(const struct ofono_error *error,
                                        void *data)
 {
-       struct pri_context *ctx = data;
+       struct ofono_gprs_context *gc = data;
+       struct pri_context *ctx = gc->pri;
        struct ofono_gprs *gprs = ctx->gprs;
        DBusConnection *conn;
        dbus_bool_t value;
@@ -1821,12 +2142,16 @@ static void gprs_deactivate_for_all(const struct 
ofono_error *error,
                return;
        }
 
-       gprs_cid_release(gprs, ctx->context.cid);
-       ctx->active = FALSE;
-       ctx->context.cid = 0;
-       ctx->context_driver->inuse = FALSE;
-       ctx->context_driver = NULL;
+       release_packet_bearer(gc);
+
+       if (ctx->bearers != NULL) {
+               gc = ctx->bearers->data;
+               gc->driver->deactivate_primary(gc, gc->cid,
+                                               gprs_deactivate_for_all, gc);
+               return;
+       }
 
+       ctx->active = FALSE;
        pri_reset_context_settings(ctx);
 
        value = ctx->active;
@@ -1850,9 +2175,9 @@ static void gprs_deactivate_next(struct ofono_gprs *gprs)
                if (ctx->active == FALSE)
                        continue;
 
-               gc = ctx->context_driver;
-               gc->driver->deactivate_primary(gc, ctx->context.cid,
-                                       gprs_deactivate_for_all, ctx);
+               gc = ctx->bearers->data;
+               gc->driver->deactivate_primary(gc, gc->cid,
+                                               gprs_deactivate_for_all, gc);
 
                return;
        }
@@ -2062,34 +2387,23 @@ void ofono_gprs_context_deactivated(struct 
ofono_gprs_context *gc,
 {
        DBusConnection *conn = ofono_dbus_get_connection();
        GSList *l;
-       struct pri_context *ctx;
+       struct pri_context *ctx = gc->pri;
        dbus_bool_t value;
 
-       if (gc->gprs == NULL)
+       if (gc->gprs == NULL || ctx == NULL || ctx->active == FALSE)
                return;
 
-       for (l = gc->gprs->contexts; l; l = l->next) {
-               ctx = l->data;
-
-               if (ctx->context.cid != cid)
-                       continue;
-
-               if (ctx->active == FALSE)
-                       break;
-
-               gprs_cid_release(ctx->gprs, ctx->context.cid);
-               ctx->context.cid = 0;
-               ctx->active = FALSE;
-               ctx->context_driver->inuse = FALSE;
-               ctx->context_driver = NULL;
+       release_packet_bearer(gc);
 
-               pri_reset_context_settings(ctx);
+       if (ctx->bearers != NULL)
+               return;
 
-               value = FALSE;
-               ofono_dbus_signal_property_changed(conn, ctx->path,
+       ctx->active = FALSE;
+       pri_reset_context_settings(ctx);
+       value = FALSE;
+       ofono_dbus_signal_property_changed(conn, ctx->path,
                                        OFONO_CONNECTION_CONTEXT_INTERFACE,
                                        "Active", DBUS_TYPE_BOOLEAN, &value);
-       }
 }
 
 int ofono_gprs_context_driver_register(const struct ofono_gprs_context_driver 
*d)
@@ -2120,6 +2434,9 @@ static void gprs_context_remove(struct ofono_atom *atom)
        if (gc == NULL)
                return;
 
+       if (gc->pri != NULL)
+               release_packet_bearer(gc);
+
        if (gc->driver && gc->driver->remove)
                gc->driver->remove(gc);
 
@@ -2192,6 +2509,131 @@ void ofono_gprs_context_set_type(struct 
ofono_gprs_context *gc,
        gc->type = type;
 }
 
+static struct context_settings_ip *get_ip_settings(struct ofono_gprs_context 
*gc)
+{
+       if (gc == NULL)
+               return NULL;
+
+       if (gc->settings_ip == NULL)
+               gc->settings_ip = g_try_new0(struct context_settings_ip, 1);
+
+       return gc->settings_ip;
+}
+
+void ofono_gprs_context_set_ip_interface(struct ofono_gprs_context *gc,
+                                       const char *interface)
+{
+       struct context_settings_ip *settings = get_ip_settings(gc);
+
+       if (settings == NULL || settings->interface != NULL)
+               return;
+
+       settings->interface = g_strdup(interface);
+}
+
+void ofono_gprs_context_set_ip_addrconf(struct ofono_gprs_context *gc,
+                                       enum ofono_gprs_addrconf_method method)
+{
+       struct context_settings_ip *settings = get_ip_settings(gc);
+
+       if (settings == NULL || settings->method != OFONO_GPRS_ADDRCONF_NONE)
+               return;
+
+       settings->method = method;
+}
+
+void ofono_gprs_context_set_ip_address(struct ofono_gprs_context *gc,
+                                       const char *address)
+{
+       struct context_settings_ip *settings = get_ip_settings(gc);
+
+       if (settings == NULL || settings->ip != NULL)
+               return;
+
+       settings->ip = g_strdup(address);
+}
+
+void ofono_gprs_context_set_ip_netmask(struct ofono_gprs_context *gc,
+                                       const char *netmask)
+{
+       struct context_settings_ip *settings = get_ip_settings(gc);
+
+       if (settings == NULL || settings->netmask != NULL)
+               return;
+
+       settings->netmask = g_strdup(netmask);
+}
+
+void ofono_gprs_context_set_ip_gateway(struct ofono_gprs_context *gc,
+                                       const char *gateway)
+{
+       struct context_settings_ip *settings = get_ip_settings(gc);
+
+       if (settings == NULL || settings->gateway != NULL)
+               return;
+
+       settings->gateway = g_strdup(gateway);
+}
+
+void ofono_gprs_context_set_ip_dns_servers(struct ofono_gprs_context *gc,
+                                               const char **dns)
+{
+       struct context_settings_ip *settings = get_ip_settings(gc);
+
+       if (settings == NULL || settings->dns != NULL)
+               return;
+
+       settings->dns = g_strdupv((char **)dns);
+}
+
+static struct context_settings_ipv6 *get_ipv6_settings(struct 
ofono_gprs_context *gc)
+{
+       if (gc == NULL)
+               return NULL;
+
+       if (gc->settings_ipv6 == NULL) {
+               gc->settings_ipv6 = g_try_new0(struct context_settings_ipv6, 1);
+
+               if (gc->settings_ipv6 == NULL)
+                       return NULL;
+       }
+
+       return gc->settings_ipv6;
+}
+
+void ofono_gprs_context_set_ipv6_interface(struct ofono_gprs_context *gc,
+                                       const char *interface)
+{
+       struct context_settings_ipv6 *settings = get_ipv6_settings(gc);
+
+       if (settings == NULL || settings->interface != NULL)
+               return;
+
+       settings->interface = g_strdup(interface);
+}
+
+void ofono_gprs_context_set_ipv6_address(struct ofono_gprs_context *gc,
+                                               const char *address)
+{
+       struct context_settings_ipv6 *settings = get_ipv6_settings(gc);
+
+       if (settings == NULL || settings->address != NULL)
+               return;
+
+       settings->address = g_strdup(address);
+}
+
+void ofono_gprs_context_set_ipv6_dns_servers(struct ofono_gprs_context *gc,
+                                               const char **dns)
+{
+       struct context_settings_ipv6 *settings = get_ipv6_settings(gc);
+
+       if (settings == NULL || settings->dns != NULL)
+               return;
+
+       settings->dns = g_strdupv((char **)dns);
+}
+
 int ofono_gprs_driver_register(const struct ofono_gprs_driver *d)
 {
        DBG("driver: %p, name: %s", d, d->name);
@@ -2431,10 +2873,10 @@ static gboolean load_context(struct ofono_gprs *gprs, 
const char *group)
 
        idmap_take(gprs->pid_map, id);
        context->id = id;
-       strcpy(context->context.username, username);
-       strcpy(context->context.password, password);
-       strcpy(context->context.apn, apn);
-       context->context.proto = proto;
+       strcpy(context->username, username);
+       strcpy(context->password, password);
+       strcpy(context->apn, apn);
+       context->proto = proto;
 
        if (msgproxy != NULL)
                strcpy(context->message_proxy, msgproxy);
-- 
1.7.1

_______________________________________________
ofono mailing list
ofono@ofono.org
http://lists.ofono.org/listinfo/ofono

Reply via email to