---
 drivers/atmodem/atutil.h                 |  14 +
 drivers/atmodem/cbs.c                    |   1 +
 drivers/atmodem/gprs-context.c           |   2 +
 drivers/atmodem/gprs.c                   | 554 ++++++++++++++++--
 drivers/atmodem/lte.c                    | 283 ++++++++-
 drivers/atmodem/network-registration.c   | 134 +++--
 drivers/atmodem/sim.c                    |  10 +-
 drivers/atmodem/sms.c                    |  21 +-
 drivers/atmodem/vendor.h                 |   1 +
 drivers/gemaltomodem/gemaltomodem.c      |   5 +-
 drivers/gemaltomodem/gemaltomodem.h      |  19 +-
 drivers/gemaltomodem/gprs-context-wwan.c | 445 ++++++++++++++
 drivers/gemaltomodem/voicecall.c         | 965 +++++++++++++++++++++++++++++++
 drivers/mbimmodem/gprs-context.c         |   2 +
 drivers/mbimmodem/mbim.c                 |   3 +
 drivers/mbimmodem/mbimmodem.c            |   2 +-
 drivers/rilmodem/call-forwarding.c       |   2 +-
 drivers/rilmodem/network-registration.c  |   2 +-
 18 files changed, 2343 insertions(+), 122 deletions(-)
 create mode 100644 drivers/gemaltomodem/gprs-context-wwan.c
 create mode 100644 drivers/gemaltomodem/voicecall.c

diff --git a/drivers/atmodem/atutil.h b/drivers/atmodem/atutil.h
index 7113a4c..57573cb 100644
--- a/drivers/atmodem/atutil.h
+++ b/drivers/atmodem/atutil.h
@@ -115,6 +115,20 @@ static inline int at_util_convert_signal_strength(int 
strength)
        return result;
 }
 
+static inline int at_util_convert_signal_strength_cesq(int strength_GSM, int 
strength_UTRAN, int strength_EUTRAN)
+{
+       int result = -1;
+
+       if (strength_GSM != 99)
+               result = (strength_GSM * 100) / 63;
+       else if (strength_UTRAN != 255)
+               result = (strength_UTRAN * 100) / 96;
+       else if (strength_EUTRAN != 255)
+               result = (strength_EUTRAN * 100) / 97;
+
+       return result;
+}
+
 #define CALLBACK_WITH_FAILURE(cb, args...)             \
        do {                                            \
                struct ofono_error cb_e;                \
diff --git a/drivers/atmodem/cbs.c b/drivers/atmodem/cbs.c
index 0a76ba2..1824b47 100644
--- a/drivers/atmodem/cbs.c
+++ b/drivers/atmodem/cbs.c
@@ -166,6 +166,7 @@ static void at_cbs_set_topics(struct ofono_cbs *cbs, const 
char *topics,
        switch (data->vendor) {
        case OFONO_VENDOR_GOBI:
        case OFONO_VENDOR_QUALCOMM_MSM:
+       case OFONO_VENDOR_GEMALTO:
                g_at_chat_send(data->chat, "AT+CSCB=0", none_prefix,
                                NULL, NULL, NULL);
                break;
diff --git a/drivers/atmodem/gprs-context.c b/drivers/atmodem/gprs-context.c
index 79ac4c8..0585850 100644
--- a/drivers/atmodem/gprs-context.c
+++ b/drivers/atmodem/gprs-context.c
@@ -304,6 +304,8 @@ static void at_gprs_activate_primary(struct 
ofono_gprs_context *gc,
                                snprintf(buf + len, sizeof(buf) - len - 3,
                                                ",\"PAP:%s\"", ctx->apn);
                                break;
+                       default:
+                               break;
                        }
                        break;
                default:
diff --git a/drivers/atmodem/gprs.c b/drivers/atmodem/gprs.c
index df37d05..6aa2cf2 100644
--- a/drivers/atmodem/gprs.c
+++ b/drivers/atmodem/gprs.c
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
  *  Copyright (C) 2010  ST-Ericsson AB.
+ *  Copyright (C) 2018 Gemalto M2M
  *
  *  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
@@ -35,6 +36,7 @@
 #include <ofono/log.h>
 #include <ofono/modem.h>
 #include <ofono/gprs.h>
+#include <common.h>
 
 #include "gatchat.h"
 #include "gatresult.h"
@@ -43,6 +45,8 @@
 #include "vendor.h"
 
 static const char *cgreg_prefix[] = { "+CGREG:", NULL };
+static const char *cereg_prefix[] = { "+CEREG:", NULL };
+static const char *c5greg_prefix[] = { "+C5GREG:", NULL };
 static const char *cgdcont_prefix[] = { "+CGDCONT:", NULL };
 static const char *none_prefix[] = { NULL };
 
@@ -51,7 +55,23 @@ struct gprs_data {
        unsigned int vendor;
        unsigned int last_auto_context_id;
        gboolean telit_try_reattach;
+       gboolean has_cgreg;
+       gboolean has_cereg;
+       gboolean has_c5greg;
+       gboolean nb_inds;
+       gboolean auto_attach; /* for LTE modems & co */
        int attached;
+       int cgreg_status;
+       int cereg_status;
+       int c5greg_status;
+};
+
+struct netreg_info {
+       struct ofono_gprs *gprs;
+       struct gprs_data *gd;
+       const char *ind;
+       int status;
+       int bearer;
 };
 
 static void at_cgatt_cb(gboolean ok, GAtResult *result, gpointer user_data)
@@ -69,9 +89,15 @@ static void at_gprs_set_attached(struct ofono_gprs *gprs, 
int attached,
                                        ofono_gprs_cb_t cb, void *data)
 {
        struct gprs_data *gd = ofono_gprs_get_data(gprs);
-       struct cb_data *cbd = cb_data_new(cb, data);
+       struct cb_data *cbd;
        char buf[64];
 
+       if(gd->auto_attach) {
+               CALLBACK_WITH_SUCCESS(cb, data);
+               return;
+       }
+
+       cbd = cb_data_new(cb, data);
        snprintf(buf, sizeof(buf), "AT+CGATT=%i", attached ? 1 : 0);
 
        if (g_at_chat_send(gd->chat, buf, none_prefix,
@@ -92,21 +118,101 @@ static void at_cgreg_cb(gboolean ok, GAtResult *result, 
gpointer user_data)
        struct ofono_error error;
        int status;
        struct gprs_data *gd = cbd->user;
+       gboolean last = !(gd->has_cereg || gd->has_c5greg);
 
        decode_at_error(&error, g_at_result_final_response(result));
 
        if (!ok) {
-               cb(&error, -1, cbd->data);
-               return;
+               status = -1;
+               goto end;
        }
 
        if (at_util_parse_reg(result, "+CGREG:", NULL, &status,
                                NULL, NULL, NULL, gd->vendor) == FALSE) {
-               CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
-               return;
+               error.type = OFONO_ERROR_TYPE_FAILURE;
+               error.error = 0;
+               status = -1;
+               goto end;
+       }
+
+end:
+       gd->cgreg_status = status;
+
+       if(last)
+               cb(&error, status, cbd->data);
+}
+
+static void at_cereg_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_status_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       int status;
+       struct gprs_data *gd = cbd->user;
+       gboolean last = !gd->has_c5greg;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               status = -1;
+               goto end;
+       }
+
+       if (at_util_parse_reg(result, "+CEREG:", NULL, &status,
+                               NULL, NULL, NULL, gd->vendor) == FALSE) {
+               error.type = OFONO_ERROR_TYPE_FAILURE;
+               error.error = 0;
+               status = -1;
+               goto end;
+       }
+
+end:
+       gd->cereg_status = status;
+
+       if(last) {
+
+               if (gd->cgreg_status==NETWORK_REGISTRATION_STATUS_DENIED ||
+                       
gd->cgreg_status==NETWORK_REGISTRATION_STATUS_REGISTERED)
+                       cb(&error, gd->cgreg_status, cbd->data);
+               else
+                       cb(&error, status, cbd->data);
+       }
+}
+
+static void at_c5greg_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_gprs_status_cb_t cb = cbd->cb;
+       struct ofono_error error;
+       int status;
+       struct gprs_data *gd = cbd->user;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok) {
+               status = -1;
+               goto end;
+       }
+
+       if (at_util_parse_reg(result, "+C5GREG:", NULL, &status,
+                               NULL, NULL, NULL, gd->vendor) == FALSE) {
+               error.type = OFONO_ERROR_TYPE_FAILURE;
+               error.error = 0;
+               status = -1;
+               goto end;
        }
 
-       cb(&error, status, cbd->data);
+end:
+       gd->c5greg_status = status;
+
+       if (gd->cgreg_status==NETWORK_REGISTRATION_STATUS_DENIED ||
+               gd->cgreg_status==NETWORK_REGISTRATION_STATUS_REGISTERED)
+               cb(&error, gd->cgreg_status, cbd->data);
+       else if (gd->cereg_status==NETWORK_REGISTRATION_STATUS_DENIED ||
+               gd->cereg_status==NETWORK_REGISTRATION_STATUS_REGISTERED)
+               cb(&error, gd->cereg_status, cbd->data);
+       else
+               cb(&error, status, cbd->data);
 }
 
 static void at_gprs_registration_status(struct ofono_gprs *gprs,
@@ -114,9 +220,6 @@ static void at_gprs_registration_status(struct ofono_gprs 
*gprs,
                                        void *data)
 {
        struct gprs_data *gd = ofono_gprs_get_data(gprs);
-       struct cb_data *cbd = cb_data_new(cb, data);
-
-       cbd->user = gd;
 
        switch (gd->vendor) {
        case OFONO_VENDOR_GOBI:
@@ -137,13 +240,51 @@ static void at_gprs_registration_status(struct ofono_gprs 
*gprs,
                break;
        }
 
-       if (g_at_chat_send(gd->chat, "AT+CGREG?", cgreg_prefix,
-                               at_cgreg_cb, cbd, g_free) > 0)
-               return;
+       /*
+        * this is long: send all indicators, compare at the end if one reports
+        * attached and use it, otherwise report status for last indicator
+        * tested (higher technology).
+        * Note: AT+CGATT? is not good because doesn't tell us if we are roaming
+        */
+       if(gd->has_cgreg) {
+               struct cb_data *cbd = cb_data_new(cb, data);
+               cbd->user = gd;
+               gd->cgreg_status=-1; /* preset in case of fail of the at send */
+
+               /* g_at_chat_send fails only if g_new_try fails, so we stop */
+               if (g_at_chat_send(gd->chat, "AT+CGREG?", cgreg_prefix,
+                                       at_cgreg_cb, cbd, g_free) == 0) {
+                       g_free(cbd);
+                       CALLBACK_WITH_FAILURE(cb, -1, data);
+                       return;
+               }
+       }
 
-       g_free(cbd);
+       if(gd->has_cereg) {
+               struct cb_data *cbd = cb_data_new(cb, data);
+               cbd->user = gd;
+               gd->cereg_status=-1;
+
+               if (g_at_chat_send(gd->chat, "AT+CEREG?", cereg_prefix,
+                                       at_cereg_cb, cbd, g_free) == 0) {
+                       g_free(cbd);
+                       CALLBACK_WITH_FAILURE(cb, -1, data);
+                       return;
+               }
+       }
+
+       if(gd->has_c5greg) {
+               struct cb_data *cbd = cb_data_new(cb, data);
+               cbd->user = gd;
+               gd->c5greg_status=-1;
 
-       CALLBACK_WITH_FAILURE(cb, -1, data);
+               if (g_at_chat_send(gd->chat, "AT+C5GREG?", c5greg_prefix,
+                                       at_c5greg_cb, cbd, g_free) == 0) {
+                       g_free(cbd);
+                       CALLBACK_WITH_FAILURE(cb, -1, data);
+                       return;
+               }
+       }
 }
 
 static void at_cgdcont_read_cb(gboolean ok, GAtResult *result,
@@ -188,14 +329,100 @@ static void at_cgdcont_read_cb(gboolean ok, GAtResult 
*result,
                                activated_cid);
 }
 
+static int cops_cb(gboolean ok, GAtResult *result)
+{
+       GAtResultIter iter;
+       int format, tech=-1;
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+COPS:"))
+               goto error;
+
+       g_at_result_iter_skip_next(&iter); /* mode: automatic, manual, ... */
+
+       if (!g_at_result_iter_next_number(&iter, &format))
+               goto error;
+
+       g_at_result_iter_skip_next(&iter); /* operator name or code */
+
+       if (!g_at_result_iter_next_number(&iter, &tech))
+               tech = -1; /* make sure it has not been set to something */
+error:
+       return tech;
+}
+
+
+static void netreg_notify_cb(gboolean ok, GAtResult *result, gpointer 
user_data)
+{
+       struct netreg_info *nri = user_data;
+       int cops_tech = cops_cb(ok, result);
+
+       if(cops_tech==-1) { /* take the indicator status */
+               ofono_gprs_status_notify(nri->gprs, nri->status);
+               return;
+       }
+
+       /*
+        * values taken from the 3GPP 27.007 rel.15
+        * matching enum access_technology in common.h up to 7.
+        */
+       if (g_str_equal(nri->ind,"CGREG") && (cops_tech<7 || cops_tech==8))
+               ofono_gprs_status_notify(nri->gprs, nri->status);
+       else if (g_str_equal(nri->ind,"CEREG") && (cops_tech==7 ||
+                                               cops_tech==9 || cops_tech==12))
+               ofono_gprs_status_notify(nri->gprs, nri->status);
+       else if (g_str_equal(nri->ind,"C5GREG") && (cops_tech==10 ||
+                                               cops_tech==11 || cops_tech==13))
+               ofono_gprs_status_notify(nri->gprs, nri->status);
+       /* all other cases ignored: indicator not for current AcT */
+}
+
+static void netreg_notify(struct ofono_gprs *gprs, const char* ind, int status,
+                                                               int bearer)
+{
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+       struct netreg_info *nri;
+
+       if (status==NETWORK_REGISTRATION_STATUS_DENIED ||
+                       status==NETWORK_REGISTRATION_STATUS_REGISTERED ||
+                       status==NETWORK_REGISTRATION_STATUS_ROAMING ||
+                       gd->nb_inds==1) {
+               /* accept this status and process */
+               ofono_gprs_status_notify(gprs, status);
+
+               if(bearer != -1)
+                       ofono_gprs_bearer_notify(gprs, bearer);
+
+               return;
+       }
+
+       /*
+        * in this case nb_inds>1 && status not listed above
+        * we check AT+COPS? for a second opinion.
+        */
+
+       nri = g_new0(struct netreg_info, 1);
+       nri->gprs = gprs;
+       nri->gd = gd;
+       nri->ind = ind;
+       nri->status = status;
+       nri->bearer = bearer;
+       g_at_chat_send(gd->chat, "AT+COPS?", none_prefix, netreg_notify_cb,
+               nri, g_free);
+}
+
 static void cgreg_notify(GAtResult *result, gpointer user_data)
 {
        struct ofono_gprs *gprs = user_data;
-       int status;
        struct gprs_data *gd = ofono_gprs_get_data(gprs);
+       int status, bearer;
 
-       if (at_util_parse_reg_unsolicited(result, "+CGREG:", &status,
-                               NULL, NULL, NULL, gd->vendor) == FALSE)
+       if (!at_util_parse_reg_unsolicited(result, "+CGREG:", &status,
+                               NULL, NULL, &bearer, gd->vendor) == FALSE)
                return;
 
        /*
@@ -220,7 +447,33 @@ static void cgreg_notify(GAtResult *result, gpointer 
user_data)
                gd->telit_try_reattach = FALSE;
        }
 
-       ofono_gprs_status_notify(gprs, status);
+       netreg_notify(gprs, "CGREG", status, bearer);
+}
+
+static void cereg_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs *gprs = user_data;
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+       int status, bearer;
+
+       if (!at_util_parse_reg_unsolicited(result, "+CEREG:", &status,
+                               NULL, NULL, &bearer, gd->vendor) == FALSE)
+               return;
+
+       netreg_notify(gprs, "CEREG", status, bearer);
+}
+
+static void c5greg_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs *gprs = user_data;
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+       int status, bearer;
+
+       if (!at_util_parse_reg_unsolicited(result, "+C5GREG:", &status,
+                               NULL, NULL, &bearer, gd->vendor) == FALSE)
+               return;
+
+       netreg_notify(gprs, "C5GREG", status, bearer);
 }
 
 static void cgev_notify(GAtResult *result, gpointer user_data)
@@ -419,6 +672,91 @@ static void ublox_ureg_notify(GAtResult *result, gpointer 
user_data)
        ofono_gprs_bearer_notify(gprs, bearer);
 }
 
+static void gemalto_ciev_ceer_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs *gprs = user_data;
+       const char *report;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CIEV: ceer,"))
+               return;
+       /*
+        * No need to check release cause group
+        * as we only subscribe to no. 5
+        */
+       if (!g_at_result_iter_skip_next(&iter))
+               return;
+       if (!g_at_result_iter_next_string(&iter, &report))
+               return;
+
+       /* TODO: Handle more of these? */
+
+       if (g_str_equal(report, "Regular deactivation")) {
+               ofono_gprs_detached_notify(gprs);
+               return;
+       }
+}
+
+static void gemalto_ciev_bearer_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs *gprs = user_data;
+       int bearer;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CIEV: psinfo,"))
+               return;
+       if (!g_at_result_iter_next_number(&iter, &bearer))
+               return;
+
+       /* Go from Gemalto representation to oFono representation */
+       switch (bearer) {
+       case 0: /* GPRS/EGPRS not available */
+               /* Same as "no bearer"? */
+               bearer = 0;
+               break;
+       case 1: /* GPRS available, ignore this one */
+               return;
+       case 2: /* GPRS attached */
+               bearer = 1;
+               break;
+       case 3: /* EGPRS available, ignore this one */
+               return;
+       case 4: /* EGPRS attached */
+               bearer = 2;
+               break;
+       case 5: /* UMTS available, ignore this one */
+               return;
+       case 6: /* UMTS attached */
+               bearer = 3;
+               break;
+       case 7: /* HSDPA available, ignore this one */
+               return;
+       case 8: /* HSDPA attached */
+               bearer = 5;
+               break;
+       case 9: /* HSDPA/HSUPA available, ignore this one */
+               return;
+       case 10: /* HSDPA/HSUPA attached */
+               bearer = 6;
+               break;
+       /* TODO: Limit these cases to ALS3? */
+       case 16: /* E-UTRA available, ignore this one */
+               return;
+       case 17: /* E-UTRA attached */
+               bearer = 7;
+               break;
+       default: /* Assume that non-parsable values mean "no bearer" */
+               bearer = 0;
+               break;
+       }
+
+       ofono_gprs_bearer_notify(gprs, bearer);
+}
+
 static void cpsb_notify(GAtResult *result, gpointer user_data)
 {
        struct ofono_gprs *gprs = user_data;
@@ -439,14 +777,48 @@ static void cpsb_notify(GAtResult *result, gpointer 
user_data)
        ofono_gprs_bearer_notify(gprs, bearer);
 }
 
-static void gprs_initialized(gboolean ok, GAtResult *result, gpointer 
user_data)
+static void gprs_initialized(struct ofono_gprs *gprs)
 {
-       struct ofono_gprs *gprs = user_data;
        struct gprs_data *gd = ofono_gprs_get_data(gprs);
 
+       switch (gd->vendor) {
+       case OFONO_VENDOR_GEMALTO:
+               break;
+       default:
+               g_at_chat_send(gd->chat, "AT+CGAUTO=0", none_prefix, NULL, NULL,
+                                                                       NULL);
+       }
+
+       switch (gd->vendor) {
+       case OFONO_VENDOR_MBM:
+               /* Ericsson MBM and ST-E modems don't support AT+CGEREP=2,1 */
+               g_at_chat_send(gd->chat, "AT+CGEREP=1,0", none_prefix,
+                       NULL, NULL, NULL);
+               break;
+       case OFONO_VENDOR_NOKIA:
+               /* Nokia data cards don't support AT+CGEREP=1,0 either */
+               g_at_chat_send(gd->chat, "AT+CGEREP=1", none_prefix,
+                       NULL, NULL, NULL);
+               break;
+       case OFONO_VENDOR_GEMALTO:
+               g_at_chat_send(gd->chat, "AT+CGEREP=2", NULL,
+                                       NULL, NULL, NULL);
+               g_at_chat_send(gd->chat, "AT^SIND=\"psinfo\",1", none_prefix,
+                       NULL, NULL, NULL);
+               break;
+       default:
+               g_at_chat_send(gd->chat, "AT+CGEREP=2,1", none_prefix,
+                       NULL, NULL, NULL);
+               break;
+       }
+
        g_at_chat_register(gd->chat, "+CGEV:", cgev_notify, FALSE, gprs, NULL);
-       g_at_chat_register(gd->chat, "+CGREG:", cgreg_notify,
-                                               FALSE, gprs, NULL);
+       g_at_chat_register(gd->chat, "+CGREG:", cgreg_notify, FALSE, gprs,
+                                                                       NULL);
+       g_at_chat_register(gd->chat, "+CEREG:", cereg_notify, FALSE, gprs,
+                                                                       NULL);
+       g_at_chat_register(gd->chat, "+C5GREG:", c5greg_notify, FALSE, gprs,
+                                                                       NULL);
 
        switch (gd->vendor) {
        case OFONO_VENDOR_HUAWEI:
@@ -468,6 +840,12 @@ static void gprs_initialized(gboolean ok, GAtResult 
*result, gpointer user_data)
                g_at_chat_send(gd->chat, "AT#PSNT=1", none_prefix,
                                                NULL, NULL, NULL);
                break;
+       case OFONO_VENDOR_GEMALTO:
+               g_at_chat_register(gd->chat, "+CIEV: psinfo,",
+                       gemalto_ciev_bearer_notify, FALSE, gprs, NULL);
+               g_at_chat_register(gd->chat, "+CIEV: ceer,",
+                       gemalto_ciev_ceer_notify, FALSE, gprs, NULL);
+               break;
        default:
                g_at_chat_register(gd->chat, "+CPSB:", cpsb_notify,
                                                FALSE, gprs, NULL);
@@ -489,16 +867,33 @@ static void gprs_initialized(gboolean ok, GAtResult 
*result, gpointer user_data)
        ofono_gprs_register(gprs);
 }
 
-static void at_cgreg_test_cb(gboolean ok, GAtResult *result,
+static void set_indreg(struct gprs_data *gd, const char *ind, gboolean present)
+{
+       if(g_str_equal(ind,"CGREG"))
+               gd->has_cgreg = present;
+
+       if(g_str_equal(ind,"CEREG"))
+               gd->has_cereg = present;
+
+       if(g_str_equal(ind,"C5GREG"))
+               gd->has_c5greg = present;
+
+}
+
+static void at_indreg_test_cb(gboolean ok, GAtResult *result,
                                gpointer user_data)
 {
-       struct ofono_gprs *gprs = user_data;
+       struct cb_data *cbd = user_data;
+       struct ofono_gprs *gprs = cbd->cb;
+       const char *ind=cbd->data;
+       const char *last=cbd->user;
+
        struct gprs_data *gd = ofono_gprs_get_data(gprs);
        gint range[2];
        GAtResultIter iter;
        int cgreg1 = 0;
        int cgreg2 = 0;
-       const char *cmd;
+       char buf[32];
 
        if (!ok)
                goto error;
@@ -506,7 +901,8 @@ static void at_cgreg_test_cb(gboolean ok, GAtResult *result,
        g_at_result_iter_init(&iter, result);
 
 retry:
-       if (!g_at_result_iter_next(&iter, "+CGREG:"))
+       sprintf(buf,"+%s:",ind);
+       if (!g_at_result_iter_next(&iter, buf))
                goto error;
 
        if (!g_at_result_iter_open_list(&iter))
@@ -521,45 +917,75 @@ retry:
 
        g_at_result_iter_close_list(&iter);
 
-       if (cgreg2)
-               cmd = "AT+CGREG=2";
-       else if (cgreg1)
-               cmd = "AT+CGREG=1";
-       else
+       if(gd->vendor==OFONO_VENDOR_GEMALTO) {
+               /*
+                * Gemalto prefers to print as much information as available
+                * for support purposes
+                */
+               sprintf(buf, "AT+%s=%d",ind, range[1]);
+       } else if (cgreg1) {
+               sprintf(buf,"AT+%s=1", ind);
+       } else if (cgreg2) {
+               sprintf(buf,"AT+%s=2", ind);
+       } else
                goto error;
 
-       g_at_chat_send(gd->chat, cmd, none_prefix, NULL, NULL, NULL);
-       g_at_chat_send(gd->chat, "AT+CGAUTO=0", none_prefix, NULL, NULL, NULL);
-
-       switch (gd->vendor) {
-       case OFONO_VENDOR_MBM:
-               /* Ericsson MBM and ST-E modems don't support AT+CGEREP=2,1 */
-               g_at_chat_send(gd->chat, "AT+CGEREP=1,0", none_prefix,
-                       gprs_initialized, gprs, NULL);
-               break;
-       case OFONO_VENDOR_NOKIA:
-               /* Nokia data cards don't support AT+CGEREP=1,0 either */
-               g_at_chat_send(gd->chat, "AT+CGEREP=1", none_prefix,
-                       gprs_initialized, gprs, NULL);
-               break;
-       default:
-               g_at_chat_send(gd->chat, "AT+CGEREP=2,1", none_prefix,
-                       gprs_initialized, gprs, NULL);
-               break;
-       }
+       set_indreg(gd, ind,TRUE);
+       g_at_chat_send(gd->chat, buf, none_prefix, NULL, NULL, NULL);
 
+       if(last)
+               goto endcheck;
        return;
 
 error:
-       ofono_info("GPRS not supported on this device");
-       ofono_gprs_remove(gprs);
+       set_indreg(gd, ind,FALSE);
+       if(!last)
+               return;
+
+endcheck:
+       if (gd->has_cgreg)
+               gd->nb_inds++;
+       if (gd->has_cereg)
+               gd->nb_inds++;
+       if (gd->has_c5greg)
+               gd->nb_inds++;
+
+       if(gd->nb_inds==0) {
+               ofono_info("GPRS not supported on this device");
+               ofono_gprs_remove(gprs);
+               return;
+       }
+
+       gprs_initialized(gprs);
+}
+
+static void test_and_set_regstatus(struct ofono_gprs *gprs) {
+       struct gprs_data *gd = ofono_gprs_get_data(gprs);
+       struct cb_data *cbd_cg  = cb_data_new(gprs, "CGREG");
+       struct cb_data *cbd_ce  = cb_data_new(gprs, "CEREG");
+       struct cb_data *cbd_c5g = cb_data_new(gprs, "C5GREG");
+
+       cbd_c5g->user="last";
+
+       /*
+        * modules can support one to all of the network registration indicators
+        *
+        * ofono will execute the next commands and related callbacks in order
+        * therefore it is possible to verify all result on the last one.
+        */
+
+       g_at_chat_send(gd->chat, "AT+CGREG=?", cgreg_prefix,
+                                       at_indreg_test_cb, cbd_cg, g_free);
+       g_at_chat_send(gd->chat, "AT+CEREG=?", cereg_prefix,
+                                       at_indreg_test_cb, cbd_ce, g_free);
+       g_at_chat_send(gd->chat, "AT+C5GREG=?", c5greg_prefix,
+                                       at_indreg_test_cb, cbd_c5g, g_free);
 }
 
 static void at_cgdcont_test_cb(gboolean ok, GAtResult *result,
                                gpointer user_data)
 {
        struct ofono_gprs *gprs = user_data;
-       struct gprs_data *gd = ofono_gprs_get_data(gprs);
        GAtResultIter iter;
        int min, max;
        const char *pdp_type;
@@ -592,18 +1018,16 @@ static void at_cgdcont_test_cb(gboolean ok, GAtResult 
*result,
                        continue;
 
                /* We look for IP PDPs */
-               if (g_str_equal(pdp_type, "IP"))
+               if (g_str_equal(pdp_type, "IP")){
                        found = TRUE;
+               }
        }
 
        if (found == FALSE)
                goto error;
 
        ofono_gprs_set_cid_range(gprs, min, max);
-
-       g_at_chat_send(gd->chat, "AT+CGREG=?", cgreg_prefix,
-                       at_cgreg_test_cb, gprs, NULL);
-
+       test_and_set_regstatus(gprs);
        return;
 
 error:
@@ -616,6 +1040,8 @@ static int at_gprs_probe(struct ofono_gprs *gprs,
 {
        GAtChat *chat = data;
        struct gprs_data *gd;
+       int autoattach;
+       struct ofono_modem* modem=ofono_gprs_get_modem(gprs);
 
        gd = g_try_new0(struct gprs_data, 1);
        if (gd == NULL)
@@ -626,8 +1052,16 @@ static int at_gprs_probe(struct ofono_gprs *gprs,
 
        ofono_gprs_set_data(gprs, gd);
 
-       g_at_chat_send(gd->chat, "AT+CGDCONT=?", cgdcont_prefix,
-                       at_cgdcont_test_cb, gprs, NULL);
+       if (gd->vendor==OFONO_VENDOR_GEMALTO) {
+               autoattach=ofono_modem_get_integer(modem, "Gto_Autoattach");
+               /* set autoattach */
+               gd->auto_attach = (autoattach==1);
+               /* skip the cgdcont scanning: set manually */
+               test_and_set_regstatus(gprs);
+       } else {
+               g_at_chat_send(gd->chat, "AT+CGDCONT=?", cgdcont_prefix,
+                                               at_cgdcont_test_cb, gprs, NULL);
+       }
 
        return 0;
 }
diff --git a/drivers/atmodem/lte.c b/drivers/atmodem/lte.c
index efa4e5f..429b847 100644
--- a/drivers/atmodem/lte.c
+++ b/drivers/atmodem/lte.c
@@ -3,6 +3,7 @@
  *  oFono - Open Source Telephony
  *
  *  Copyright (C) 2017  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2018 Gemalto M2M
  *
  *  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
@@ -40,22 +41,273 @@
 #include "gatresult.h"
 
 #include "atmodem.h"
+#include "vendor.h"
 
 struct lte_driver_data {
        GAtChat *chat;
+       unsigned int vendor;
 };
 
+struct lte_cb_data {
+       const struct ofono_lte_default_attach_info *info;
+       ofono_lte_cb_t cb;
+       const struct ofono_lte *lte;
+       void *data;
+};
+
+static gboolean gemalto_get_auth_command(struct ofono_modem *modem, int cid,
+                               enum ofono_gprs_auth_method auth_method,
+                               const char *username, const char *password,
+                               char *buf, guint buflen)
+{
+       int gto_auth = ofono_modem_get_integer(modem, "Gemalto_Auth");
+       int len;
+       /*
+        * 0: use cgauth
+        * 1: use sgauth(pwd, user)
+        * 2: use sgauth(user, pwd)
+        */
+
+       int auth_type;
+
+       switch (auth_method) {
+       case OFONO_GPRS_AUTH_METHOD_PAP:
+               auth_type=1;
+               break;
+       case OFONO_GPRS_AUTH_METHOD_CHAP:
+               auth_type=2;
+               break;
+       case OFONO_GPRS_AUTH_METHOD_NONE:
+       default:
+               auth_type=0;
+               break;
+       }
+
+       if (auth_type != 0 && (!*username || !*password))
+               return FALSE;
+
+       switch (gto_auth) {
+       case 1:
+       case 2:
+               len = snprintf(buf, buflen, "AT^SGAUTH=%d", cid);
+               break;
+       case 0:
+       default:
+               len = snprintf(buf, buflen, "AT+CGAUTH=%d", cid);
+               break;
+       }
+
+       buflen -= len;
+
+       switch(auth_type) {
+       case 0:
+
+               switch (gto_auth) {
+               case 2:
+                       snprintf(buf+len, buflen, ",0,\"\",\"\"");
+                       break;
+               case 0:
+               case 1:
+               default:
+                       snprintf(buf+len, buflen, ",0");
+                       break;
+               }
+               break;
+
+       case 1:
+       case 2:
+
+               switch (gto_auth) {
+               case 1:
+                       snprintf(buf+len, buflen, ",%d,\"%s\",\"%s\"",
+                                       auth_type, password, username);
+                       break;
+               case 0:
+               case 2:
+               default:
+                       snprintf(buf+len, buflen, ",%d,\"%s\",\"%s\"",
+                                       auth_type, username, password);
+               }
+               break;
+
+       default:
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void gemalto_get_cgdcont_command(struct ofono_modem *modem,
+                       guint cid, enum ofono_gprs_proto proto, const char *apn,
+                                                       char *buf, guint buflen)
+{
+       int len = snprintf(buf, buflen, "AT+CGDCONT=%u", cid);
+       buflen-=len;
+
+       if(!apn) /* it will remove the context */
+               return;
+
+       switch (proto) {
+       case OFONO_GPRS_PROTO_IPV6:
+               snprintf(buf+len, buflen, ",\"IPV6\",\"%s\"", apn);
+               break;
+       case OFONO_GPRS_PROTO_IPV4V6:
+               snprintf(buf+len, buflen, ",\"IPV4V6\",\"%s\"", apn);
+               break;
+       case OFONO_GPRS_PROTO_IP:
+       default:
+               snprintf(buf+len, buflen, ",\"IP\",\"%s\"", apn);
+               break;
+       }
+}
+
+static void at_lte_set_default_auth_info_cb(gboolean ok, GAtResult *result,
+                                                       gpointer user_data)
+{
+       struct lte_cb_data *lcbd = user_data;
+       struct ofono_error error;
+       ofono_lte_cb_t cb = lcbd->cb;
+       void *data = lcbd->data;
+
+       DBG("ok %d", ok);
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       cb(&error, data);
+}
+
+static void gemalto_lte_set_default_auth_info(const struct ofono_lte *lte,
+                       const struct ofono_lte_default_attach_info *info,
+                       ofono_lte_cb_t cb, void *data)
+{
+       struct lte_cb_data *lcbd = data;
+       void* ud = lcbd->data;
+       struct lte_driver_data *ldd = ofono_lte_get_data(lte);
+       struct ofono_modem *modem = ofono_lte_get_modem(lte);
+       char buf[32 + OFONO_GPRS_MAX_USERNAME_LENGTH +
+                                       OFONO_GPRS_MAX_PASSWORD_LENGTH +1];
+
+       if(!gemalto_get_auth_command(modem, 1, info->auth_method,
+                       info->username, info->password, buf, sizeof(buf))) {
+               g_free(lcbd);
+               goto set_auth_failure;
+       }
+
+       if(g_at_chat_send(ldd->chat, buf, NULL, at_lte_set_default_auth_info_cb,
+                                                       lcbd, g_free) > 0)
+               return;
+
+set_auth_failure:
+       CALLBACK_WITH_FAILURE(cb, ud);
+}
+
+static void at_lte_set_default_auth_info(const struct ofono_lte *lte,
+                       const struct ofono_lte_default_attach_info *info,
+                       ofono_lte_cb_t cb, void *data)
+{
+       struct lte_cb_data *lcbd = data;
+       void* ud = lcbd->data;
+       struct lte_driver_data *ldd = ofono_lte_get_data(lte);
+       char buf[32 + OFONO_GPRS_MAX_USERNAME_LENGTH +
+                                       OFONO_GPRS_MAX_PASSWORD_LENGTH +1];
+       guint buflen = sizeof(buf);
+
+       snprintf(buf, buflen, "AT+CGAUTH=0,");
+       buflen-=strlen(buf);
+
+       switch(info->auth_method) {
+       case OFONO_GPRS_AUTH_METHOD_NONE:
+               snprintf(buf+strlen(buf), buflen, "0");
+               break;
+       case OFONO_GPRS_AUTH_METHOD_PAP:
+               snprintf(buf+strlen(buf), buflen, "1,\"%s\",\"%s\"",
+                                               info->username, info->password);
+               break;
+       case OFONO_GPRS_AUTH_METHOD_CHAP:
+               snprintf(buf+strlen(buf), buflen, "2,\"%s\",\"%s\"",
+                                               info->username, info->password);
+               break;
+       default:
+               g_free(lcbd);
+               goto set_auth_failure;
+               break;
+       }
+
+       if(g_at_chat_send(ldd->chat, buf, NULL, at_lte_set_default_auth_info_cb,
+                                                       lcbd, g_free) > 0)
+               return;
+
+set_auth_failure:
+       CALLBACK_WITH_FAILURE(cb, ud);
+}
+
 static void at_lte_set_default_attach_info_cb(gboolean ok, GAtResult *result,
                                                        gpointer user_data)
 {
-       struct cb_data *cbd = user_data;
-       ofono_lte_cb_t cb = cbd->cb;
+       struct lte_cb_data *lcbd = user_data;
+       struct lte_driver_data *ldd = ofono_lte_get_data(lcbd->data);
        struct ofono_error error;
 
        DBG("ok %d", ok);
 
+       if (ok) {
+               switch (ldd->vendor) {
+               case OFONO_VENDOR_GEMALTO:
+                       gemalto_lte_set_default_auth_info(lcbd->lte,
+                                       lcbd->info, lcbd->cb, user_data);
+                       return;
+                       break;
+               default:
+                       at_lte_set_default_auth_info(lcbd->lte,
+                                       lcbd->info, lcbd->cb, user_data);
+                       return;
+                       break;
+               }
+       }
+
        decode_at_error(&error, g_at_result_final_response(result));
-       cb(&error, cbd->data);
+       lcbd->cb(&error, lcbd->data);
+}
+
+static void gemalto_lte_set_default_attach_info(const struct ofono_lte *lte,
+                       const struct ofono_lte_default_attach_info *info,
+                       ofono_lte_cb_t cb, void *data)
+{
+       struct lte_driver_data *ldd = ofono_lte_get_data(lte);
+       struct ofono_modem *modem = ofono_lte_get_modem(lte);
+       char buf[32 + OFONO_GPRS_MAX_APN_LENGTH  +1];
+       struct lte_cb_data *lcbd;
+       int gto_autoconf = ofono_modem_get_integer(modem, "Gemalto_Autoconf");
+
+       /*
+        * to be completed. May require additional properties in the driver
+        * current values plan for Gemalto_Autoconf:
+        * 0: no autoconf (or no param set)
+        * 1: autoconf activated but fallback selected
+        * 2: autoconf activated, profile selected,
+        *              but custom attach apn required for this application
+        * 3-9: rfu
+        * 10: autoconf default bearer and ims
+        * 20: autoconf 10 + autoconf private apn
+        */
+       if(gto_autoconf>=10) {
+               CALLBACK_WITH_SUCCESS(cb, data);
+               return;
+       }
+
+       lcbd = g_new0(struct lte_cb_data, 1);
+       lcbd->data = data;
+       lcbd->info = info;
+       lcbd->cb = cb;
+       lcbd->lte = lte;
+
+       gemalto_get_cgdcont_command(modem, 1, info->proto, info->apn, buf,
+                                                               sizeof(buf));
+
+       if (g_at_chat_send(ldd->chat, buf, NULL,
+                       at_lte_set_default_attach_info_cb, lcbd, NULL) > 0)
+               return;
+
+       CALLBACK_WITH_FAILURE(cb, data);
 }
 
 static void at_lte_set_default_attach_info(const struct ofono_lte *lte,
@@ -63,20 +315,28 @@ static void at_lte_set_default_attach_info(const struct 
ofono_lte *lte,
                        ofono_lte_cb_t cb, void *data)
 {
        struct lte_driver_data *ldd = ofono_lte_get_data(lte);
-       char buf[32 + OFONO_GPRS_MAX_APN_LENGTH + 1];
-       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[32 + OFONO_GPRS_MAX_APN_LENGTH  +1];
+       struct lte_cb_data *lcbd;
 
-       DBG("LTE config with APN: %s", info->apn);
+       if(ldd->vendor==OFONO_VENDOR_GEMALTO) {
+               gemalto_lte_set_default_attach_info(lte, info, cb, data);
+               return;
+       }
+
+       lcbd = g_new0(struct lte_cb_data, 1);
+       lcbd->data = data;
+       lcbd->info = info;
+       lcbd->cb = cb;
+       lcbd->lte = lte;
 
        if (strlen(info->apn) > 0)
                snprintf(buf, sizeof(buf), "AT+CGDCONT=0,\"IP\",\"%s\"",
-                               info->apn);
+                                                       info->apn);
        else
                snprintf(buf, sizeof(buf), "AT+CGDCONT=0,\"IP\"");
 
-       /* We can't do much in case of failure so don't check response. */
        if (g_at_chat_send(ldd->chat, buf, NULL,
-                       at_lte_set_default_attach_info_cb, cbd, g_free) > 0)
+                       at_lte_set_default_attach_info_cb, lcbd, NULL) > 0)
                return;
 
        CALLBACK_WITH_FAILURE(cb, data);
@@ -84,9 +344,7 @@ static void at_lte_set_default_attach_info(const struct 
ofono_lte *lte,
 
 static gboolean lte_delayed_register(gpointer user_data)
 {
-       struct ofono_lte *lte = user_data;
-
-       ofono_lte_register(lte);
+       ofono_lte_register(user_data);
 
        return FALSE;
 }
@@ -103,6 +361,7 @@ static int at_lte_probe(struct ofono_lte *lte, unsigned int 
vendor, void *data)
                return -ENOMEM;
 
        ldd->chat = g_at_chat_clone(chat);
+       ldd->vendor = vendor;
 
        ofono_lte_set_data(lte, ldd);
 
diff --git a/drivers/atmodem/network-registration.c 
b/drivers/atmodem/network-registration.c
index 0854cd1..c6d00a9 100644
--- a/drivers/atmodem/network-registration.c
+++ b/drivers/atmodem/network-registration.c
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
  *  Copyright (C) 2010  ST-Ericsson AB.
+ *  Copyright (C) 2018 Gemalto M2M
  *
  *  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
@@ -56,6 +57,7 @@ struct netreg_data {
        GAtChat *chat;
        char mcc[OFONO_MAX_MCC_LENGTH + 1];
        char mnc[OFONO_MAX_MNC_LENGTH + 1];
+       unsigned int csq_source;
        int signal_index; /* If strength is reported via CIND */
        int signal_min; /* min strength reported via CIND */
        int signal_max; /* max strength reported via CIND */
@@ -179,7 +181,7 @@ static int option_parse_tech(GAtResult *result)
        return tech;
 }
 
-static int cinterion_parse_tech(GAtResult *result)
+static int gemalto_parse_tech(GAtResult *result)
 {
        int tech = -1;
        GAtResultIter iter;
@@ -231,13 +233,13 @@ static void at_creg_cb(gboolean ok, GAtResult *result, 
gpointer user_data)
        cb(&error, status, lac, ci, tech, cbd->data);
 }
 
-static void cinterion_query_tech_cb(gboolean ok, GAtResult *result,
+static void gemalto_query_tech_cb(gboolean ok, GAtResult *result,
                                               gpointer user_data)
 {
        struct tech_query *tq = user_data;
        int tech;
 
-       tech = cinterion_parse_tech(result);
+       tech = gemalto_parse_tech(result);
 
        ofono_netreg_status_notify(tq->netreg,
                        tq->status, tq->lac, tq->ci, tech);
@@ -664,6 +666,45 @@ static void csq_notify(GAtResult *result, gpointer 
user_data)
                                at_util_convert_signal_strength(strength));
 }
 
+static void cesq_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       int strength_GSM, strength_UTRAN, strength_EUTRAN;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CESQ:"))
+               return;
+
+       /* rxlevel gsm, use as a strength indicator */
+       if (!g_at_result_iter_next_number(&iter, &strength_GSM))
+               return;
+
+       /* ber gsm, ignore*/
+       if (!g_at_result_iter_skip_next(&iter))
+               return;
+
+       /* rscp utran, use as a strength indicator */
+       if (!g_at_result_iter_next_number(&iter, &strength_UTRAN))
+               return;
+
+       /* ecno utran, ignore */
+       if (!g_at_result_iter_skip_next(&iter))
+               return;
+
+       /* rsrq eutran, ignore */
+       if (!g_at_result_iter_skip_next(&iter))
+               return;
+
+       /* rsrp eutran, use as a strength indicator */
+       if (!g_at_result_iter_next_number(&iter, &strength_EUTRAN))
+               return;
+
+       ofono_netreg_strength_notify(netreg,
+                               
at_util_convert_signal_strength_cesq(strength_GSM, strength_UTRAN, 
strength_EUTRAN));
+}
+
 static void calypso_csq_notify(GAtResult *result, gpointer user_data)
 {
        struct ofono_netreg *netreg = user_data;
@@ -702,7 +743,6 @@ static void option_osigq_notify(GAtResult *result, gpointer 
user_data)
 
 static void ifx_xhomezr_notify(GAtResult *result, gpointer user_data)
 {
-       //struct ofono_netreg *netreg = user_data;
        const char *label;
        GAtResultIter iter;
 
@@ -767,7 +807,6 @@ static void ifx_xreg_notify(GAtResult *result, gpointer 
user_data)
 
 static void ifx_xciev_notify(GAtResult *result, gpointer user_data)
 {
-       //struct ofono_netreg *netreg = user_data;
        int ind;
        GAtResultIter iter;
 
@@ -876,7 +915,7 @@ static void telit_ciev_notify(GAtResult *result, gpointer 
user_data)
        ofono_netreg_strength_notify(netreg, strength);
 }
 
-static void cinterion_ciev_notify(GAtResult *result, gpointer user_data)
+static void gemalto_ciev_notify(GAtResult *result, gpointer user_data)
 {
        struct ofono_netreg *netreg = user_data;
        struct netreg_data *nd = ofono_netreg_get_data(netreg);
@@ -1233,18 +1272,23 @@ static void at_signal_strength(struct ofono_netreg 
*netreg,
 
        cbd->user = nd;
 
-       /*
-        * If we defaulted to using CIND, then keep using it,
-        * otherwise fall back to CSQ
-        */
-       if (nd->signal_index > 0) {
-               if (g_at_chat_send(nd->chat, "AT+CIND?", cind_prefix,
-                                       cind_cb, cbd, g_free) > 0)
-                       return;
-       } else {
-               if (g_at_chat_send(nd->chat, "AT+CSQ", csq_prefix,
-                               csq_cb, cbd, g_free) > 0)
-                       return;
+       switch(nd->vendor) {
+       case OFONO_VENDOR_GEMALTO:
+               break;
+       default:
+               /*
+                * If we defaulted to using CIND, then keep using it,
+                * otherwise fall back to CSQ
+                */
+               if (nd->signal_index > 0) {
+                       if (g_at_chat_send(nd->chat, "AT+CIND?", cind_prefix,
+                                               cind_cb, cbd, g_free) > 0)
+                               return;
+               } else {
+                       if (g_at_chat_send(nd->chat, "AT+CSQ", csq_prefix,
+                                       csq_cb, cbd, g_free) > 0)
+                               return;
+               }
        }
 
        g_free(cbd);
@@ -1534,6 +1578,9 @@ static void creg_notify(GAtResult *result, gpointer 
user_data)
        tq->ci = ci;
        tq->netreg = netreg;
 
+       if ((status == 1 || status == 5) && tech == -1)
+               tech = nd->tech;
+
        switch (nd->vendor) {
        case OFONO_VENDOR_GOBI:
                if (g_at_chat_send(nd->chat, "AT*CNTI=0", none_prefix,
@@ -1556,19 +1603,18 @@ static void creg_notify(GAtResult *result, gpointer 
user_data)
                                        option_query_tech_cb, tq, g_free) > 0)
                        return;
                break;
-    case OFONO_VENDOR_CINTERION:
-              if (g_at_chat_send(nd->chat, "AT^SMONI",
-                                      smoni_prefix,
-                                      cinterion_query_tech_cb, tq, g_free) > 0)
-                      return;
-              break;
+    case OFONO_VENDOR_GEMALTO:
+               if (tech!=-1)
+                       break;  /* technology already returned by +CREG, so run 
the notify label */
+               if (g_at_chat_send(nd->chat, "AT^SMONI",
+                                       smoni_prefix,
+                                       gemalto_query_tech_cb, tq, g_free) > 0)
+                       return;
+               break;
        }
 
        g_free(tq);
 
-       if ((status == 1 || status == 5) && tech == -1)
-               tech = nd->tech;
-
 notify:
        ofono_netreg_status_notify(netreg, status, lac, ci, tech);
 }
@@ -1861,6 +1907,17 @@ error:
        ofono_netreg_remove(netreg);
 }
 
+static gboolean gemalto_csq_query(gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+
+       g_at_chat_send(nd->chat, "AT+CSQ", none_prefix, NULL, NULL, NULL);
+       g_at_chat_send(nd->chat, "AT+CESQ", none_prefix, NULL, NULL, NULL);
+
+       return TRUE;
+}
+
 static void at_creg_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
 {
        struct ofono_netreg *netreg = user_data;
@@ -2028,12 +2085,12 @@ static void at_creg_set_cb(gboolean ok, GAtResult 
*result, gpointer user_data)
                g_at_chat_send(nd->chat, "AT*TLTS=1", none_prefix,
                                                NULL, NULL, NULL);
                break;
-       case OFONO_VENDOR_CINTERION:
+       case OFONO_VENDOR_GEMALTO:
                /*
-                * We can't set rssi bounds from Cinterion responses
+                * We can't set rssi bounds from Gemalto responses
                 * so set them up to specified values here
                 *
-                * Cinterion rssi signal strength specified as:
+                * Gemalto rssi signal strength specified as:
                 * 0      <= -112dBm
                 * 1 - 4  signal strengh in 15 dB steps
                 * 5      >= -51 dBm
@@ -2042,12 +2099,20 @@ static void at_creg_set_cb(gboolean ok, GAtResult 
*result, gpointer user_data)
                nd->signal_min = 0;
                nd->signal_max = 5;
                nd->signal_invalid = 99;
-
                /* Register for specific signal strength reports */
+               g_at_chat_register(nd->chat, "+CIEV:",
+                               gemalto_ciev_notify, FALSE, netreg, NULL);
                g_at_chat_send(nd->chat, "AT^SIND=\"rssi\",1", none_prefix,
                                NULL, NULL, NULL);
-               g_at_chat_register(nd->chat, "+CIEV:",
-                               cinterion_ciev_notify, FALSE, netreg, NULL);
+               /* Register for +CSQ and +CESQ and then poll them periodically 
*/
+               g_at_chat_register(nd->chat, "+CSQ:", csq_notify,
+                                               FALSE, netreg, NULL);
+               g_at_chat_register(nd->chat, "+CESQ:", cesq_notify,
+                                               FALSE, netreg, NULL);
+
+               if (!nd->csq_source) /* in case it has already be called, to 
avoid a memory leak */
+                       nd->csq_source = g_timeout_add_seconds(60, 
gemalto_csq_query, netreg);
+
                break;
        case OFONO_VENDOR_NOKIA:
        case OFONO_VENDOR_SAMSUNG:
@@ -2145,6 +2210,9 @@ static void at_netreg_remove(struct ofono_netreg *netreg)
        if (nd->nitz_timeout)
                g_source_remove(nd->nitz_timeout);
 
+       if (nd->csq_source)
+               g_source_remove(nd->csq_source);
+
        ofono_netreg_set_data(netreg, NULL);
 
        g_at_chat_unref(nd->chat);
diff --git a/drivers/atmodem/sim.c b/drivers/atmodem/sim.c
index 0907635..2ce72c8 100644
--- a/drivers/atmodem/sim.c
+++ b/drivers/atmodem/sim.c
@@ -66,7 +66,7 @@ static const char *oercn_prefix[] = { "_OERCN:", NULL };
 static const char *cpinr_prefixes[] = { "+CPINR:", "+CPINRE:", NULL };
 static const char *epin_prefix[] = { "*EPIN:", NULL };
 static const char *simcom_spic_prefix[] = { "+SPIC:", NULL };
-static const char *cinterion_spic_prefix[] = { "^SPIC:", NULL };
+static const char *gemalto_spic_prefix[] = { "^SPIC:", NULL };
 static const char *pct_prefix[] = { "#PCT:", NULL };
 static const char *pnnm_prefix[] = { "+PNNM:", NULL };
 static const char *qpinc_prefix[] = { "+QPINC:", NULL };
@@ -1110,7 +1110,7 @@ error:
        CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
 }
 
-static void cinterion_spic_cb(gboolean ok, GAtResult *result,
+static void gemalto_spic_cb(gboolean ok, GAtResult *result,
                                                        gpointer user_data)
 {
        struct cb_data *cbd = user_data;
@@ -1227,9 +1227,9 @@ static void at_pin_retries_query(struct ofono_sim *sim,
                                        upincnt_cb, cbd, g_free) > 0)
                        return;
                break;
-       case OFONO_VENDOR_CINTERION:
-               if (g_at_chat_send(sd->chat, "AT^SPIC", cinterion_spic_prefix,
-                                       cinterion_spic_cb, cbd, g_free) > 0)
+       case OFONO_VENDOR_GEMALTO:
+               if (g_at_chat_send(sd->chat, "AT^SPIC", gemalto_spic_prefix,
+                                       gemalto_spic_cb, cbd, g_free) > 0)
                        return;
                break;
        default:
diff --git a/drivers/atmodem/sms.c b/drivers/atmodem/sms.c
index 7e78fcf..64d0f6e 100644
--- a/drivers/atmodem/sms.c
+++ b/drivers/atmodem/sms.c
@@ -219,10 +219,16 @@ static void at_cmgs(struct ofono_sms *sms, const unsigned 
char *pdu,
        char buf[512];
        int len;
 
-       if (mms) {
-               snprintf(buf, sizeof(buf), "AT+CMMS=%d", mms);
-               g_at_chat_send(data->chat, buf, none_prefix,
-                               NULL, NULL, NULL);
+       switch(data->vendor) {
+       case OFONO_VENDOR_GEMALTO:
+               break;
+       default:
+               if (mms) {
+                       snprintf(buf, sizeof(buf), "AT+CMMS=%d", mms);
+                       g_at_chat_send(data->chat, buf, none_prefix,
+                                       NULL, NULL, NULL);
+               }
+               break;
        }
 
        len = snprintf(buf, sizeof(buf), "AT+CMGS=%d\r", tpdu_len);
@@ -329,7 +335,7 @@ static inline void at_ack_delivery(struct ofono_sms *sms)
        /* We must acknowledge the PDU using CNMA */
        if (data->cnma_ack_pdu) {
                switch (data->vendor) {
-               case OFONO_VENDOR_CINTERION:
+               case OFONO_VENDOR_GEMALTO:
                        snprintf(buf, sizeof(buf), "AT+CNMA=1");
                        break;
                default:
@@ -411,10 +417,10 @@ static void at_cmt_notify(GAtResult *result, gpointer 
user_data)
                goto err;
 
        switch (data->vendor) {
-       case OFONO_VENDOR_CINTERION:
+       case OFONO_VENDOR_GEMALTO:
                if (!g_at_result_iter_next_number(&iter, &tpdu_len)) {
                        /*
-                        * Some cinterions modems (ALS3,PLS8...), act in
+                        * Some Gemalto modems (ALS3,PLS8...), act in
                         * accordance with 3GPP 27.005.  So we need to skip
                         * the first (<alpha>) field
                         *  \r\n+CMT: ,23\r\nCAFECAFECAFE... ...\r\n
@@ -455,6 +461,7 @@ static void at_cmt_notify(GAtResult *result, gpointer 
user_data)
 
        if (data->vendor != OFONO_VENDOR_SIMCOM)
                at_ack_delivery(sms);
+       return;
 
 err:
        ofono_error("Unable to parse CMT notification");
diff --git a/drivers/atmodem/vendor.h b/drivers/atmodem/vendor.h
index 721796e..abe2d89 100644
--- a/drivers/atmodem/vendor.h
+++ b/drivers/atmodem/vendor.h
@@ -49,4 +49,5 @@ enum ofono_vendor {
        OFONO_VENDOR_UBLOX_TOBY_L2,
        OFONO_VENDOR_CINTERION,
        OFONO_VENDOR_XMM,
+       OFONO_VENDOR_GEMALTO,
 };
diff --git a/drivers/gemaltomodem/gemaltomodem.c 
b/drivers/gemaltomodem/gemaltomodem.c
index 91cf238..7afd3c1 100644
--- a/drivers/gemaltomodem/gemaltomodem.c
+++ b/drivers/gemaltomodem/gemaltomodem.c
@@ -35,13 +35,16 @@
 static int gemaltomodem_init(void)
 {
        gemalto_location_reporting_init();
-
+       gemaltowwan_gprs_context_init();
+       gemalto_voicecall_init();
        return 0;
 }
 
 static void gemaltomodem_exit(void)
 {
        gemalto_location_reporting_exit();
+       gemaltowwan_gprs_context_exit();
+       gemalto_voicecall_exit();
 }
 
 OFONO_PLUGIN_DEFINE(gemaltomodem, "Gemalto modem driver", VERSION,
diff --git a/drivers/gemaltomodem/gemaltomodem.h 
b/drivers/gemaltomodem/gemaltomodem.h
index 7ea1e8f..183c370 100644
--- a/drivers/gemaltomodem/gemaltomodem.h
+++ b/drivers/gemaltomodem/gemaltomodem.h
@@ -19,7 +19,24 @@
  *
  */
 
-#include <drivers/atmodem/atutil.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gatchat.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/types.h>
+
+#include <drivers/atmodem/atmodem.h>
 
 extern void gemalto_location_reporting_init();
 extern void gemalto_location_reporting_exit();
+
+extern void gemaltowwan_gprs_context_init();
+extern void gemaltowwan_gprs_context_exit();
+
+extern void gemalto_voicecall_init();
+extern void gemalto_voicecall_exit();
diff --git a/drivers/gemaltomodem/gprs-context-wwan.c 
b/drivers/gemaltomodem/gprs-context-wwan.c
new file mode 100644
index 0000000..669e025
--- /dev/null
+++ b/drivers/gemaltomodem/gprs-context-wwan.c
@@ -0,0 +1,445 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2017 Piotr Haber. All rights reserved.
+ *  Copyright (C) 2018 Sebastian Arnd. All rights reserved.
+ *  Copyright (C) 2018 Gemalto M2M
+ *
+ *  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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs-context.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "gemaltomodem.h"
+
+static const char *none_prefix[] = { NULL };
+
+enum state {
+       STATE_IDLE,
+       STATE_ENABLING,
+       STATE_DISABLING,
+       STATE_ACTIVE,
+};
+
+struct gprs_context_data {
+       GAtChat *chat;
+       unsigned int active_context;
+       char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1];
+       char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1];
+       enum ofono_gprs_auth_method auth_method;
+       enum state state;
+       enum ofono_gprs_proto proto;
+       char address[64];
+       char netmask[64];
+       char gateway[64];
+       char dns1[64];
+       char dns2[64];
+       ofono_gprs_context_cb_t cb;
+       void *cb_data;
+       int use_wwan;
+};
+
+static gboolean gemalto_get_auth_command(struct ofono_modem *modem, int cid,
+                               enum ofono_gprs_auth_method auth_method,
+                               const char *username, const char *password,
+                               char *buf, guint buflen)
+{
+       int gto_auth = ofono_modem_get_integer(modem, "Gemalto_Auth");
+       int len;
+       /*
+        * 0: use cgauth
+        * 1: use sgauth(pwd, user)
+        * 2: use sgauth(user, pwd)
+        */
+
+       int auth_type;
+
+       switch (auth_method) {
+       case OFONO_GPRS_AUTH_METHOD_PAP:
+               auth_type=1;
+               break;
+       case OFONO_GPRS_AUTH_METHOD_CHAP:
+               auth_type=2;
+               break;
+       case OFONO_GPRS_AUTH_METHOD_NONE:
+       default:
+               auth_type=0;
+               break;
+       }
+
+       if (auth_type != 0 && (!*username || !*password))
+               return FALSE;
+
+       switch (gto_auth) {
+       case 1:
+       case 2:
+               len = snprintf(buf, buflen, "AT^SGAUTH=%d", cid);
+               break;
+       case 0:
+       default:
+               len = snprintf(buf, buflen, "AT+CGAUTH=%d", cid);
+               break;
+       }
+
+       buflen -= len;
+
+       switch(auth_type) {
+       case 0:
+
+               switch (gto_auth) {
+               case 2:
+                       snprintf(buf+len, buflen, ",0,\"\",\"\"");
+                       break;
+               case 0:
+               case 1:
+               default:
+                       snprintf(buf+len, buflen, ",0");
+                       break;
+               }
+               break;
+
+       case 1:
+       case 2:
+
+               switch (gto_auth) {
+               case 1:
+                       snprintf(buf+len, buflen, ",%d,\"%s\",\"%s\"",
+                                       auth_type, password, username);
+                       break;
+               case 0:
+               case 2:
+               default:
+                       snprintf(buf+len, buflen, ",%d,\"%s\",\"%s\"",
+                                       auth_type, username, password);
+               }
+               break;
+
+       default:
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void gemalto_get_cgdcont_command(struct ofono_modem *modem,
+                       guint cid, enum ofono_gprs_proto proto, const char *apn,
+                                                       char *buf, guint buflen)
+{
+       int len = snprintf(buf, buflen, "AT+CGDCONT=%u", cid);
+       buflen-=len;
+
+       if(!apn) /* it will remove the context */
+               return;
+
+       switch (proto) {
+       case OFONO_GPRS_PROTO_IPV6:
+               snprintf(buf+len, buflen, ",\"IPV6\",\"%s\"", apn);
+               break;
+       case OFONO_GPRS_PROTO_IPV4V6:
+               snprintf(buf+len, buflen, ",\"IPV4V6\",\"%s\"", apn);
+               break;
+       case OFONO_GPRS_PROTO_IP:
+       default:
+               snprintf(buf+len, buflen, ",\"IP\",\"%s\"", apn);
+               break;
+       }
+}
+
+static void failed_setup(struct ofono_gprs_context *gc,
+                               GAtResult *result, gboolean deactivate)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       struct ofono_error error;
+       char buf[64];
+
+       DBG("deactivate %d", deactivate);
+
+       if (deactivate == TRUE) {
+
+               if(gcd->use_wwan)
+                       sprintf(buf, "AT^SWWAN=0,%u", gcd->active_context);
+               else
+                       sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
+
+               g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL);
+       }
+
+       gcd->active_context = 0;
+       gcd->state = STATE_IDLE;
+
+       if (result == NULL) {
+               CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
+               return;
+       }
+
+       decode_at_error(&error, g_at_result_final_response(result));
+       gcd->cb(&error, gcd->cb_data);
+}
+
+static void activate_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       DBG("ok %d", ok);
+
+       if (!ok) {
+               ofono_error("Unable activate context");
+               /*
+                * We've reported sucess already, so can't just call
+                * failed_setup we call ofono_gprs_context_deactivated instead.
+                * Thats not a clean solution at all, but as it seems there is
+                * no clean way to determine whether it is possible to activate
+                * the context before issuing AT^SWWAN. A possible workaround
+                * might be to issue AT+CGACT=1 and AT+CGACT=0 and try if that
+                * works, before calling CALLBACK_WITH_SUCCESS.
+                */
+               ofono_gprs_context_deactivated(gc, gcd->active_context);
+               gcd->active_context = 0;
+               gcd->state = STATE_IDLE;
+               return;
+       }
+       /* We've reported sucess already */
+}
+
+static void setup_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       char buf[32 + OFONO_GPRS_MAX_USERNAME_LENGTH +
+                                       OFONO_GPRS_MAX_PASSWORD_LENGTH +1];
+       struct ofono_modem *modem = ofono_gprs_context_get_modem(gc);
+       const char *interface;
+
+       if (!ok) {
+               ofono_error("Failed to setup context");
+               failed_setup(gc, result, FALSE);
+               return;
+       }
+
+       if(gemalto_get_auth_command(modem, gcd->active_context, 
gcd->auth_method,
+                       gcd->username, gcd->password, buf, sizeof(buf))) {
+               if (!g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL,
+                                                                       NULL))
+               goto error;
+       }
+       /*
+        * note that if the auth command is not ok we skip it and continue
+        * but if the sending fails we do an error
+        */
+
+       if(gcd->use_wwan)
+               sprintf(buf, "AT^SWWAN=1,%u", gcd->active_context);
+       else
+               sprintf(buf, "AT+CGACT=%u,1", gcd->active_context);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                                       activate_cb, gc, NULL) > 0){
+
+               interface = ofono_modem_get_string(modem, "NetworkInterface");
+
+               ofono_gprs_context_set_interface(gc, interface);
+               ofono_gprs_context_set_ipv4_address(gc, NULL, FALSE);
+
+               /*
+                * We report sucess already here because some modules need a
+                * DHCP request to complete the AT^SWWAN command sucessfully
+                */
+               gcd->state = STATE_ACTIVE;
+
+               CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+
+               return;
+       }
+
+error:
+       failed_setup(gc, NULL, FALSE);
+}
+
+static void gemaltowwan_gprs_activate_primary(struct ofono_gprs_context *gc,
+                               const struct ofono_gprs_primary_context *ctx,
+                               ofono_gprs_context_cb_t cb, void *data)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       char buf[OFONO_GPRS_MAX_APN_LENGTH + 128];
+       struct ofono_modem *modem = ofono_gprs_context_get_modem(gc);
+
+       DBG("cid %u", ctx->cid);
+
+       gcd->use_wwan=ofono_modem_get_integer(modem, "Gemalto_WWAN");
+       gcd->active_context = ctx->cid;
+       gcd->cb = cb;
+       gcd->cb_data = data;
+       memcpy(gcd->username, ctx->username, sizeof(ctx->username));
+       memcpy(gcd->password, ctx->password, sizeof(ctx->password));
+       gcd->state = STATE_ENABLING;
+       gcd->proto = ctx->proto;
+       gcd->auth_method = ctx->auth_method;
+
+       gemalto_get_cgdcont_command(modem, ctx->cid, ctx->proto, ctx->apn, buf,
+                                                               sizeof(buf));
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               setup_cb, gc, NULL) > 0)
+               return;
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void deactivate_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       DBG("ok %d", ok);
+
+       gcd->active_context = 0;
+       gcd->state = STATE_IDLE;
+
+       CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+}
+
+static void gemaltowwan_gprs_deactivate_primary(struct ofono_gprs_context *gc,
+                                       unsigned int cid,
+                                       ofono_gprs_context_cb_t cb, void *data)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       char buf[64];
+
+       DBG("cid %u", cid);
+
+       gcd->state = STATE_DISABLING;
+       gcd->cb = cb;
+       gcd->cb_data = data;
+
+       if(gcd->use_wwan)
+               sprintf(buf, "AT^SWWAN=0,%u", gcd->active_context);
+       else
+               sprintf(buf, "AT+CGACT=%u,0", gcd->active_context);
+
+       if (g_at_chat_send(gcd->chat, buf, none_prefix,
+                               deactivate_cb, gc, NULL) > 0)
+               return;
+
+       CALLBACK_WITH_SUCCESS(cb, data);
+}
+
+static void cgev_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_gprs_context *gc = user_data;
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+       const char *event;
+       int cid = 0;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       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, "ME PDN DEACT"))
+               return;
+
+       sscanf(event, "%*s %*s %*s %u", &cid);
+
+       DBG("cid %d", cid);
+
+       if ((unsigned int) cid != gcd->active_context)
+               return;
+
+       ofono_gprs_context_deactivated(gc, gcd->active_context);
+
+       gcd->active_context = 0;
+       gcd->state = STATE_IDLE;
+}
+
+static int gemaltowwan_gprs_context_probe(struct ofono_gprs_context *gc,
+                                       unsigned int model, void *data)
+{
+       GAtChat *chat = data;
+       struct gprs_context_data *gcd;
+       struct ofono_modem *modem = ofono_gprs_context_get_modem(gc);
+
+       DBG("");
+
+       gcd = g_try_new0(struct gprs_context_data, 1);
+       if (gcd == NULL)
+               return -ENOMEM;
+
+       if(modem)
+               gcd->use_wwan=ofono_modem_get_integer(modem, "Gemalto_WWAN");
+       gcd->chat = g_at_chat_clone(chat);
+       ofono_gprs_context_set_data(gc, gcd);
+       g_at_chat_register(chat, "+CGEV:", cgev_notify, FALSE, gc, NULL);
+
+       return 0;
+}
+
+static void gemaltowwan_gprs_context_remove(struct ofono_gprs_context *gc)
+{
+       struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+       DBG("");
+
+       ofono_gprs_context_set_data(gc, NULL);
+
+       g_at_chat_unref(gcd->chat);
+       g_free(gcd);
+}
+
+static void gemaltowwan_gprs_detach_shutdown(struct ofono_gprs_context *gc,
+                                       unsigned int cid)
+{
+       DBG("cid %u", cid);
+
+       ofono_gprs_context_deactivated(gc, cid);
+}
+
+static struct ofono_gprs_context_driver driver = {
+       .name                   = "gemaltowwanmodem",
+       .probe                  = gemaltowwan_gprs_context_probe,
+       .remove                 = gemaltowwan_gprs_context_remove,
+       .activate_primary       = gemaltowwan_gprs_activate_primary,
+       .deactivate_primary     = gemaltowwan_gprs_deactivate_primary,
+       .detach_shutdown        = gemaltowwan_gprs_detach_shutdown,
+};
+
+void gemaltowwan_gprs_context_init(void)
+{
+       ofono_gprs_context_driver_register(&driver);
+}
+
+void gemaltowwan_gprs_context_exit(void)
+{
+       ofono_gprs_context_driver_unregister(&driver);
+}
diff --git a/drivers/gemaltomodem/voicecall.c b/drivers/gemaltomodem/voicecall.c
new file mode 100644
index 0000000..e67da92
--- /dev/null
+++ b/drivers/gemaltomodem/voicecall.c
@@ -0,0 +1,965 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  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 <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/voicecall.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "common.h"
+
+#include "gemaltomodem.h"
+
+static const char *clcc_prefix[] = { "+CLCC:", NULL };
+static const char *none_prefix[] = { NULL };
+
+/* According to 27.007 COLP is an intermediate status for ATD */
+static const char *atd_prefix[] = { "+COLP:", NULL };
+
+#define FLAG_NEED_CLIP 1
+
+struct voicecall_data {
+       GAtChat *chat;
+       GSList *calls;
+       unsigned int local_release;
+       unsigned int vendor;
+       unsigned char flags;
+};
+
+struct release_id_req {
+       struct ofono_voicecall *vc;
+       ofono_voicecall_cb_t cb;
+       void *data;
+       int id;
+};
+
+struct change_state_req {
+       struct ofono_voicecall *vc;
+       ofono_voicecall_cb_t cb;
+       void *data;
+       int affected_types;
+};
+
+static void generic_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct change_state_req *req = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(req->vc);
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (ok && req->affected_types) {
+               GSList *l;
+               struct ofono_call *call;
+
+               for (l = vd->calls; l; l = l->next) {
+                       call = l->data;
+
+                       if (req->affected_types & (1 << call->status))
+                               vd->local_release |= (1 << call->id);
+               }
+       }
+
+       req->cb(&error, req->data);
+}
+
+static void gemalto_template(const char *cmd, struct ofono_voicecall *vc,
+                       GAtResultFunc result_cb, unsigned int affected_types,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct change_state_req *req = g_try_new0(struct change_state_req, 1);
+
+       if (req == NULL)
+               goto error;
+
+       req->vc = vc;
+       req->cb = cb;
+       req->data = data;
+       req->affected_types = affected_types;
+
+       if (g_at_chat_send(vd->chat, cmd, none_prefix,
+                               result_cb, req, g_free) > 0)
+               return;
+
+error:
+       g_free(req);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void gemalto_answer(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       gemalto_template("ATA", vc, generic_cb, 0, cb, data);
+}
+
+static void gemalto_hangup_all(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       unsigned int affected = (1 << CALL_STATUS_INCOMING) |
+                               (1 << CALL_STATUS_DIALING) |
+                               (1 << CALL_STATUS_ALERTING) |
+                               (1 << CALL_STATUS_WAITING) |
+                               (1 << CALL_STATUS_HELD) |
+                               (1 << CALL_STATUS_ACTIVE);
+
+       /* Hangup all calls */
+       gemalto_template("AT+CHUP", vc, generic_cb, affected, cb, data);
+}
+
+static void gemalto_hangup(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       unsigned int affected = (1 << CALL_STATUS_ACTIVE);
+
+       /* Hangup current active call */
+       gemalto_template("AT+CHLD=1", vc, generic_cb, affected, cb, data);
+}
+
+static void gemalto_hold_all_active(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       unsigned int affected = (1 << CALL_STATUS_ACTIVE);
+       gemalto_template("AT+CHLD=2", vc, generic_cb, affected, cb, data);
+}
+
+static void gemalto_release_all_held(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       unsigned int affected = (1 << CALL_STATUS_INCOMING) |
+                               (1 << CALL_STATUS_WAITING);
+
+       gemalto_template("AT+CHLD=0", vc, generic_cb, affected, cb, data);
+}
+
+static void gemalto_set_udub(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       unsigned int affected = (1 << CALL_STATUS_INCOMING) |
+                               (1 << CALL_STATUS_WAITING);
+
+       gemalto_template("AT+CHLD=0", vc, generic_cb, affected, cb, data);
+}
+
+static void gemalto_release_all_active(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       unsigned int affected = (1 << CALL_STATUS_ACTIVE);
+
+       gemalto_template("AT+CHLD=1", vc, generic_cb, affected, cb, data);
+}
+
+static void release_id_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct release_id_req *req = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(req->vc);
+       struct ofono_error error;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (ok)
+               vd->local_release = 1 << req->id;
+
+       req->cb(&error, req->data);
+}
+
+static void gemalto_release_specific(struct ofono_voicecall *vc, int id,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct release_id_req *req = g_try_new0(struct release_id_req, 1);
+       char buf[32];
+
+       if (req == NULL)
+               goto error;
+
+       req->vc = vc;
+       req->cb = cb;
+       req->data = data;
+       req->id = id;
+
+       snprintf(buf, sizeof(buf), "AT+CHLD=1%d", id);
+
+       if (g_at_chat_send(vd->chat, buf, none_prefix,
+                               release_id_cb, req, g_free) > 0)
+               return;
+
+error:
+       g_free(req);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void gemalto_private_chat(struct ofono_voicecall *vc, int id,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[32];
+
+       snprintf(buf, sizeof(buf), "AT+CHLD=2%d", id);
+       gemalto_template(buf, vc, generic_cb, 0, cb, data);
+}
+
+static void gemalto_create_multiparty(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       gemalto_template("AT+CHLD=3", vc, generic_cb, 0, cb, data);
+}
+
+static void gemalto_transfer(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       /* Held & Active */
+       unsigned int affected = (1 << CALL_STATUS_ACTIVE) |
+                               (1 << CALL_STATUS_HELD);
+
+       /* Transfer can puts held & active calls together and disconnects
+        * from both.  However, some networks support transferring of
+        * dialing/ringing calls as well.
+        */
+       affected |= (1 << CALL_STATUS_DIALING) |
+                               (1 << CALL_STATUS_ALERTING);
+
+       gemalto_template("AT+CHLD=4", vc, generic_cb, affected, cb, data);
+}
+
+static void gemalto_send_dtmf(struct ofono_voicecall *vc, const char *dtmf,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       int len = strlen(dtmf);
+       int s;
+       int i;
+       char *buf;
+       struct ofono_modem *modem = ofono_voicecall_get_modem(vc);
+       int use_quotes = ofono_modem_get_integer(modem, "Gemalto_VTS_quotes");
+
+       /* strlen("+VTS=\"T\";") = 9 + initial AT + null */
+       buf = g_try_new(char, len * 9 + 3);
+
+       if (buf == NULL) {
+               CALLBACK_WITH_FAILURE(cb, data);
+               return;
+       }
+
+       if(use_quotes)
+               s = sprintf(buf, "AT+VTS=\"%c\"", dtmf[0]);
+       else
+               s = sprintf(buf, "AT+VTS=%c", dtmf[0]);
+
+       for (i = 1; i < len; i++) {
+
+               if(use_quotes)
+                       s += sprintf(buf + s, ";+VTS=\"%c\"", dtmf[i]);
+               else
+                       s += sprintf(buf + s, ";+VTS=%c", dtmf[i]);
+       }
+
+       g_at_chat_send(vd->chat, buf, NULL, NULL, NULL, NULL);
+       g_free(buf);
+}
+
+static struct ofono_call *create_call(struct ofono_voicecall *vc, int type,
+                                       int direction, int status,
+                                       const char *num, int num_type, int clip)
+{
+       struct voicecall_data *d = ofono_voicecall_get_data(vc);
+       struct ofono_call *call;
+
+       /* Generate a call structure for the waiting call */
+       call = g_try_new(struct ofono_call, 1);
+       if (call == NULL)
+               return NULL;
+
+       ofono_call_init(call);
+
+       call->id = ofono_voicecall_get_next_callid(vc);
+       call->type = type;
+       call->direction = direction;
+       call->status = status;
+
+       if (clip != 2) {
+               strncpy(call->phone_number.number, num,
+                       OFONO_MAX_PHONE_NUMBER_LENGTH);
+               call->phone_number.type = num_type;
+       }
+
+       call->clip_validity = clip;
+       call->cnap_validity = CNAP_VALIDITY_NOT_AVAILABLE;
+
+       d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare);
+
+       return call;
+}
+
+static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       struct ofono_voicecall *vc = cbd->user;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       ofono_voicecall_cb_t cb = cbd->cb;
+       GAtResultIter iter;
+       const char *num;
+       int type = 128;
+       int validity = 2;
+       struct ofono_error error;
+       struct ofono_call *call;
+       GSList *l;
+
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       if (!ok)
+               goto out;
+
+       /* On a success, make sure to put all active calls on hold */
+       for (l = vd->calls; l; l = l->next) {
+               call = l->data;
+
+               if (call->status != CALL_STATUS_ACTIVE)
+                       continue;
+
+               call->status = CALL_STATUS_HELD;
+               ofono_voicecall_notify(vc, call);
+       }
+
+       g_at_result_iter_init(&iter, result);
+
+       if (g_at_result_iter_next(&iter, "+COLP:")) {
+               g_at_result_iter_next_string(&iter, &num);
+               g_at_result_iter_next_number(&iter, &type);
+
+               if (strlen(num) > 0)
+                       validity = 0;
+               else
+                       validity = 2;
+
+               DBG("colp_notify: %s %d %d", num, type, validity);
+       }
+
+       /* Generate a voice call that was just dialed, we guess the ID */
+       call = create_call(vc, 0, 0, CALL_STATUS_DIALING, num, type, validity);
+       if (call == NULL) {
+               ofono_error("Unable to malloc, call tracking will fail!");
+               return;
+       }
+
+       /* oFono core will generate a call with the dialed number
+        * inside its dial callback.  Unless we got COLP information
+        * we do not need to communicate that a call is being
+        * dialed
+        */
+       if (validity != 2)
+               ofono_voicecall_notify(vc, call);
+
+out:
+       cb(&error, cbd->data);
+}
+
+static void gemalto_dial(struct ofono_voicecall *vc,
+                       const struct ofono_phone_number *ph,
+                       enum ofono_clir_option clir, ofono_voicecall_cb_t cb,
+                       void *data)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct cb_data *cbd = cb_data_new(cb, data);
+       char buf[256];
+
+       cbd->user = vc;
+
+       if (ph->type == 145)
+               snprintf(buf, sizeof(buf), "ATD+%s", ph->number);
+       else
+               snprintf(buf, sizeof(buf), "ATD%s", ph->number);
+
+       switch (clir) {
+       case OFONO_CLIR_OPTION_INVOCATION:
+               strcat(buf, "I");
+               break;
+       case OFONO_CLIR_OPTION_SUPPRESSION:
+               strcat(buf, "i");
+               break;
+       default:
+               break;
+       }
+
+       strcat(buf, ";");
+
+       if (g_at_chat_send(vd->chat, buf, atd_prefix,
+                               atd_cb, cbd, g_free) > 0)
+               return;
+
+       g_free(cbd);
+
+       CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static GSList *gemalto_parse_slcc(GAtResult *result, unsigned int 
*ret_mpty_ids)
+{
+       GAtResultIter iter;
+       GSList *l = NULL;
+       int id, dir, status, type;
+       ofono_bool_t mpty;
+       struct ofono_call *call;
+       unsigned int mpty_ids = 0;
+
+       g_at_result_iter_init(&iter, result);
+
+       while (g_at_result_iter_next(&iter, "+CLCC:")) {
+               const char *str = "";
+               int number_type = 129;
+
+               if (!g_at_result_iter_next_number(&iter, &id))
+                       continue;
+
+               if (id == 0)
+                       continue;
+
+               if (!g_at_result_iter_next_number(&iter, &dir))
+                       continue;
+
+               if (!g_at_result_iter_next_number(&iter, &status))
+                       continue;
+
+               if (status > 5)
+                       continue;
+
+               if (!g_at_result_iter_next_number(&iter, &type))
+                       continue;
+
+               if (!g_at_result_iter_next_number(&iter, &mpty))
+                       continue;
+
+               /* skip 'Reserved=0' parameter, only difference from CLCC */
+               if (!g_at_result_iter_skip_next(&iter))
+                       continue;
+
+               if (g_at_result_iter_next_string(&iter, &str))
+                       g_at_result_iter_next_number(&iter, &number_type);
+
+               call = g_try_new(struct ofono_call, 1);
+               if (call == NULL)
+                       break;
+
+               ofono_call_init(call);
+
+               call->id = id;
+               call->direction = dir;
+               call->status = status;
+               call->type = type;
+               strncpy(call->phone_number.number, str,
+                               OFONO_MAX_PHONE_NUMBER_LENGTH);
+               call->phone_number.type = number_type;
+
+               if (strlen(call->phone_number.number) > 0)
+                       call->clip_validity = 0;
+               else
+                       call->clip_validity = 2;
+
+               l = g_slist_insert_sorted(l, call, at_util_call_compare);
+
+               if (mpty)
+                       mpty_ids |= 1 << id;
+       }
+
+       if (ret_mpty_ids)
+               *ret_mpty_ids = mpty_ids;
+
+       return l;
+}
+
+static void clcc_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GSList *l;
+
+       if (!ok)
+               return;
+
+       vd->calls = at_util_parse_clcc(result, NULL);
+
+       for (l = vd->calls; l; l = l->next)
+               ofono_voicecall_notify(vc, l->data);
+}
+
+static void slcc_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GSList *calls;
+       GSList *n, *o;
+       struct ofono_call *nc, *oc;
+
+       calls = gemalto_parse_slcc(result, NULL);
+
+       n = calls;
+       o = vd->calls;
+
+       while (n || o) {
+               nc = n ? n->data : NULL;
+               oc = o ? o->data : NULL;
+
+               if (oc && (nc == NULL || (nc->id > oc->id))) {
+                       enum ofono_disconnect_reason reason;
+
+                       if (vd->local_release & (1 << oc->id))
+                               reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP;
+                       else
+                               reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
+
+                       if (!oc->type)
+                               ofono_voicecall_disconnected(vc, oc->id,
+                                                               reason, NULL);
+
+                       o = o->next;
+               } else if (nc && (oc == NULL || (nc->id < oc->id))) {
+
+                       if (nc->type == 0) /* new call, signal it */
+                               ofono_voicecall_notify(vc, nc);
+
+                       n = n->next;
+               } else {
+                       /*
+                        * Always use the clip_validity from old call
+                        * the only place this is truly told to us is
+                        * in the CLIP notify, the rest are fudged
+                        * anyway.  Useful when RING, CLIP is used,
+                        * and we're forced to use CLCC/SLCC and clip_validity
+                        * is 1
+                        */
+                       if (oc->clip_validity == 1)
+                               nc->clip_validity = oc->clip_validity;
+
+                       /*
+                        * CNAP doesn't arrive as part of CLCC, always
+                        * re-use from the old call
+                        */
+                       strncpy(nc->name, oc->name,
+                                       OFONO_MAX_CALLER_NAME_LENGTH);
+                       nc->name[OFONO_MAX_CALLER_NAME_LENGTH] = '\0';
+                       nc->cnap_validity = oc->cnap_validity;
+
+                       /*
+                        * CDIP doesn't arrive as part of CLCC, always
+                        * re-use from the old call
+                        */
+                       memcpy(&nc->called_number, &oc->called_number,
+                                       sizeof(oc->called_number));
+
+                       /*
+                        * If the CLIP is not provided and the CLIP never
+                        * arrives, or RING is used, then signal the call
+                        * here
+                        */
+                       if (nc->status == CALL_STATUS_INCOMING &&
+                                       (vd->flags & FLAG_NEED_CLIP)) {
+                               if (nc->type == 0)
+                                       ofono_voicecall_notify(vc, nc);
+
+                               vd->flags &= ~FLAG_NEED_CLIP;
+                       } else if (memcmp(nc, oc, sizeof(*nc)) && nc->type == 0)
+                               ofono_voicecall_notify(vc, nc);
+
+                       n = n->next;
+                       o = o->next;
+               }
+       }
+
+       g_slist_free_full(vd->calls, g_free);
+
+       vd->calls = calls;
+
+       vd->local_release = 0;
+}
+
+static void ring_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       struct ofono_call *call;
+
+       /* See comment in CRING */
+       if (g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_WAITING),
+                               at_util_call_compare_by_status))
+               return;
+
+       /* RING can repeat, ignore if we already have an incoming call */
+       if (g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_INCOMING),
+                               at_util_call_compare_by_status))
+               return;
+
+       /* Generate an incoming call of unknown type */
+       call = create_call(vc, 9, 1, CALL_STATUS_INCOMING, NULL, 128, 2);
+       if (call == NULL) {
+               ofono_error("Couldn't create call!");
+               return;
+       }
+
+       /* We don't know the call type, we must wait for the SLCC URC */
+       vd->flags = FLAG_NEED_CLIP;
+}
+
+static void cring_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *line;
+       int type;
+
+       /* Handle the following situation:
+        * Active Call + Waiting Call.  Active Call is Released.  The Waiting
+        * call becomes Incoming and RING/CRING indications are signaled.
+        * Sometimes these arrive before we get the SLCC URC to find about
+        * the stage change.  If this happens, simply ignore the RING/CRING
+        * when a waiting call exists (cannot have waiting + incoming in GSM)
+        */
+       if (g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_WAITING),
+                               at_util_call_compare_by_status))
+               return;
+
+       /* CRING can repeat, ignore if we already have an incoming call */
+       if (g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_INCOMING),
+                               at_util_call_compare_by_status))
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CRING:"))
+               return;
+
+       line = g_at_result_iter_raw_line(&iter);
+       if (line == NULL)
+               return;
+
+       /* Ignore everything that is not voice for now */
+       if (!strcasecmp(line, "VOICE"))
+               type = 0;
+       else
+               type = 9;
+
+       /* Generate an incoming call */
+       create_call(vc, type, 1, CALL_STATUS_INCOMING, NULL, 128, 2);
+
+       /* We have a call, and call type but don't know the number and
+        * must wait for the CLIP to arrive before announcing the call.
+        * And we wait also for SLCC. If the CLIP arrives
+        * earlier, we announce the call there
+        */
+       vd->flags = FLAG_NEED_CLIP;
+
+       DBG("");
+}
+
+static void clip_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *num;
+       int type, validity;
+       GSList *l;
+       struct ofono_call *call;
+
+       l = g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_INCOMING),
+                               at_util_call_compare_by_status);
+       if (l == NULL) {
+               ofono_error("CLIP for unknown call");
+               return;
+       }
+
+       /* We have already saw a CLIP for this call, no need to parse again */
+       if ((vd->flags & FLAG_NEED_CLIP) == 0)
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CLIP:"))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &type))
+               return;
+
+       if (strlen(num) > 0)
+               validity = CLIP_VALIDITY_VALID;
+       else
+               validity = CLIP_VALIDITY_NOT_AVAILABLE;
+
+       /* Skip subaddr, satype and alpha */
+       g_at_result_iter_skip_next(&iter);
+       g_at_result_iter_skip_next(&iter);
+       g_at_result_iter_skip_next(&iter);
+
+       /* If we have CLI validity field, override our guessed value */
+       g_at_result_iter_next_number(&iter, &validity);
+
+       DBG("%s %d %d", num, type, validity);
+
+       call = l->data;
+
+       strncpy(call->phone_number.number, num,
+               OFONO_MAX_PHONE_NUMBER_LENGTH);
+       call->phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+       call->phone_number.type = type;
+       call->clip_validity = validity;
+
+       if (call->type == 0)
+               ofono_voicecall_notify(vc, call);
+
+       vd->flags &= ~FLAG_NEED_CLIP;
+}
+
+static int class_to_call_type(int cls)
+{
+       switch (cls) {
+       case 1:
+               return 0;
+       case 4:
+               return 2;
+       case 8:
+               return 9;
+       default:
+               return 1;
+       }
+}
+
+static void ccwa_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       const char *num;
+       int num_type, validity, cls;
+       struct ofono_call *call;
+
+       /* if CCWA is resent, ignore it the second time around */
+       if (g_slist_find_custom(vd->calls,
+                               GINT_TO_POINTER(CALL_STATUS_WAITING),
+                               at_util_call_compare_by_status))
+               return;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CCWA:"))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &num_type))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &cls))
+               return;
+
+       /* Skip alpha field */
+       g_at_result_iter_skip_next(&iter);
+
+       if (strlen(num) > 0)
+               validity = 0;
+       else
+               validity = 2;
+
+       /* If we have CLI validity field, override our guessed value */
+       g_at_result_iter_next_number(&iter, &validity);
+
+       DBG("%s %d %d %d", num, num_type, cls, validity);
+
+       call = create_call(vc, class_to_call_type(cls), 1, CALL_STATUS_WAITING,
+                               num, num_type, validity);
+       if (call == NULL) {
+               ofono_error("Unable to malloc. Call management is fubar");
+               return;
+       }
+
+       if (call->type == 0) /* Only notify voice calls */
+               ofono_voicecall_notify(vc, call);
+}
+
+static void cssi_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       GAtResultIter iter;
+       int code, index;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CSSI:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &code))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &index))
+               index = 0;
+
+       ofono_voicecall_ssn_mo_notify(vc, 0, code, index);
+}
+
+static void cssu_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       GAtResultIter iter;
+       int code;
+       int index;
+       const char *num;
+       struct ofono_phone_number ph;
+
+       ph.number[0] = '\0';
+       ph.type = 129;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CSSU:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &code))
+               return;
+
+       if (!g_at_result_iter_next_number_default(&iter, -1, &index))
+               goto out;
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               goto out;
+
+       strncpy(ph.number, num, OFONO_MAX_PHONE_NUMBER_LENGTH);
+
+       if (!g_at_result_iter_next_number(&iter, &ph.type))
+               return;
+
+out:
+       ofono_voicecall_ssn_mt_notify(vc, 0, code, index, &ph);
+}
+
+static void gemalto_voicecall_initialized(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       DBG("voicecall_init: registering to notifications");
+
+       /* NO CARRIER, NO ANSWER, BUSY, NO DIALTONE are handled through SLCC */
+       g_at_chat_register(vd->chat, "^SLCC:", slcc_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "RING", ring_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CRING:", cring_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CLIP:", clip_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CCWA:", ccwa_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CSSI:", cssi_notify, FALSE, vc, NULL);
+       g_at_chat_register(vd->chat, "+CSSU:", cssu_notify, FALSE, vc, NULL);
+
+       ofono_voicecall_register(vc);
+
+       /* Populate the call list */
+       g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_cb, vc, NULL);
+}
+
+static int gemalto_voicecall_probe(struct ofono_voicecall *vc,
+                                       unsigned int vendor, void *data)
+{
+       GAtChat *chat = data;
+       struct voicecall_data *vd;
+
+       vd = g_try_new0(struct voicecall_data, 1);
+
+       if (vd == NULL)
+               return -ENOMEM;
+
+       vd->chat = g_at_chat_clone(chat);
+       vd->vendor = vendor;
+       ofono_voicecall_set_data(vc, vd);
+
+       // TODO: move to a config atom
+       g_at_chat_send(vd->chat, "AT^SNFS=5", NULL, NULL, NULL, NULL);
+
+       g_at_chat_send(vd->chat, "AT+CRC=1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT+COLP=1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT+CLIP=1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT+CCWA=1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT+CSSN=1,1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(vd->chat, "AT^SLCC=1", NULL,
+                               gemalto_voicecall_initialized, vc, NULL);
+       return 0;
+}
+
+static void gemalto_voicecall_remove(struct ofono_voicecall *vc)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       ofono_voicecall_set_data(vc, NULL);
+
+       g_at_chat_unref(vd->chat);
+       g_free(vd);
+}
+
+static struct ofono_voicecall_driver driver = {
+       .name                   = "gemaltomodem",
+       .probe                  = gemalto_voicecall_probe,
+       .remove                 = gemalto_voicecall_remove,
+       .dial                   = gemalto_dial,
+       .answer                 = gemalto_answer,
+       .hangup_all             = gemalto_hangup_all,
+       .hangup_active          = gemalto_hangup,
+       .hold_all_active        = gemalto_hold_all_active,
+       .release_all_held       = gemalto_release_all_held,
+       .set_udub               = gemalto_set_udub,
+       .release_all_active     = gemalto_release_all_active,
+       .release_specific       = gemalto_release_specific,
+       .private_chat           = gemalto_private_chat,
+       .create_multiparty      = gemalto_create_multiparty,
+       .transfer               = gemalto_transfer,
+       .deflect                = NULL,
+       .swap_without_accept    = NULL,
+       .send_tones             = gemalto_send_dtmf
+};
+
+void gemalto_voicecall_init(void)
+{
+       ofono_voicecall_driver_register(&driver);
+}
+
+void gemalto_voicecall_exit(void)
+{
+       ofono_voicecall_driver_unregister(&driver);
+}
diff --git a/drivers/mbimmodem/gprs-context.c b/drivers/mbimmodem/gprs-context.c
index 79793c9..ce33b92 100644
--- a/drivers/mbimmodem/gprs-context.c
+++ b/drivers/mbimmodem/gprs-context.c
@@ -75,6 +75,8 @@ static uint32_t auth_method_to_auth_protocol(enum 
ofono_gprs_auth_method method)
                return 2; /* MBIMAuthProtocolChap */
        case OFONO_GPRS_AUTH_METHOD_PAP:
                return 1; /* MBIMAuthProtocolPap */
+       default:
+               return 0;
        }
 
        return 0;
diff --git a/drivers/mbimmodem/mbim.c b/drivers/mbimmodem/mbim.c
index 9fcf44b..f4132d0 100644
--- a/drivers/mbimmodem/mbim.c
+++ b/drivers/mbimmodem/mbim.c
@@ -781,6 +781,9 @@ static bool open_read_handler(struct l_io *io, void 
*user_data)
        /* Grab OPEN_DONE Status field */
        if (l_get_le32(buf) != 0) {
                close(fd);
+               if (device->disconnect_handler)
+                       device->disconnect_handler(device->ready_data);
+               device->is_ready = false;
                return false;
        }
 
diff --git a/drivers/mbimmodem/mbimmodem.c b/drivers/mbimmodem/mbimmodem.c
index a4c9daa..2a01dd6 100644
--- a/drivers/mbimmodem/mbimmodem.c
+++ b/drivers/mbimmodem/mbimmodem.c
@@ -33,7 +33,7 @@ static int mbimmodem_init(void)
        mbim_devinfo_init();
        mbim_sim_init();
        mbim_netreg_init();
-       mbim_sms_exit();
+       mbim_sms_init();
        mbim_gprs_init();
        mbim_gprs_context_init();
        return 0;
diff --git a/drivers/rilmodem/call-forwarding.c 
b/drivers/rilmodem/call-forwarding.c
index 4aff4d3..0bdab3f 100644
--- a/drivers/rilmodem/call-forwarding.c
+++ b/drivers/rilmodem/call-forwarding.c
@@ -38,7 +38,7 @@
 #include <ofono/call-forwarding.h>
 #include "common.h"
 
-#pragma GCC diagnostic ignored "-Wrestrict"
+//#pragma GCC diagnostic ignored "-Wrestrict"
 
 #include "gril.h"
 
diff --git a/drivers/rilmodem/network-registration.c 
b/drivers/rilmodem/network-registration.c
index 809b3bc..9895c6d 100644
--- a/drivers/rilmodem/network-registration.c
+++ b/drivers/rilmodem/network-registration.c
@@ -37,7 +37,7 @@
 #include <ofono/modem.h>
 #include <ofono/netreg.h>
 
-#pragma GCC diagnostic ignored "-Wrestrict"
+//#pragma GCC diagnostic ignored "-Wrestrict"
 
 #include <gril/gril.h>
 
-- 
1.9.1

_______________________________________________
ofono mailing list
ofono@ofono.org
https://lists.ofono.org/mailman/listinfo/ofono

Reply via email to