---
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
[email protected]
https://lists.ofono.org/mailman/listinfo/ofono