summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/rilmodem/call-barring.c245
-rw-r--r--drivers/rilmodem/call-forwarding.c327
-rw-r--r--drivers/rilmodem/call-settings.c286
-rw-r--r--drivers/rilmodem/call-volume.c182
-rw-r--r--drivers/rilmodem/devinfo.c218
-rw-r--r--drivers/rilmodem/gprs-context.c585
-rw-r--r--drivers/rilmodem/gprs.c487
-rw-r--r--drivers/rilmodem/gprs.h46
-rw-r--r--drivers/rilmodem/network-registration.c566
-rw-r--r--drivers/rilmodem/phonebook.c1055
-rw-r--r--drivers/rilmodem/radio-settings.c300
-rw-r--r--drivers/rilmodem/radio-settings.h47
-rw-r--r--drivers/rilmodem/rilmodem.c78
-rw-r--r--drivers/rilmodem/rilmodem.h71
-rw-r--r--drivers/rilmodem/rilutil.c194
-rw-r--r--drivers/rilmodem/rilutil.h165
-rw-r--r--drivers/rilmodem/sim.c1200
-rw-r--r--drivers/rilmodem/sms.c315
-rw-r--r--drivers/rilmodem/ussd.c264
-rw-r--r--drivers/rilmodem/vendor.h32
-rw-r--r--drivers/rilmodem/voicecall.c824
-rw-r--r--drivers/rilmodem/voicecall.h71
22 files changed, 7558 insertions, 0 deletions
diff --git a/drivers/rilmodem/call-barring.c b/drivers/rilmodem/call-barring.c
new file mode 100644
index 00000000..6a19556e
--- /dev/null
+++ b/drivers/rilmodem/call-barring.c
@@ -0,0 +1,245 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2014 Jolla Ltd
+ * Contact: Miia Leinonen
+ * Copyright (C) 2014 Canonical Ltd
+ *
+ * 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
+ *
+ */
+
+#include <errno.h>
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+
+#include "common.h"
+#include "grilrequest.h"
+#include "grilreply.h"
+#include "call-barring.h"
+#include "rilmodem.h"
+#include "ril_constants.h"
+
+struct barring_data {
+ GRil *ril;
+};
+
+static void ril_call_barring_query_cb(struct ril_msg *message,
+ gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_call_barring_query_cb_t cb = cbd->cb;
+ struct barring_data *bd = cbd->user;
+ int bearer_class;
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("%s: query failed, err: %s", __func__,
+ ril_error_to_string(message->error));
+ goto error;
+ }
+
+ bearer_class = g_ril_reply_parse_query_facility_lock(bd->ril, message);
+ if (bearer_class < 0)
+ goto error;
+
+ CALLBACK_WITH_SUCCESS(cb, bearer_class, cbd->data);
+ return;
+
+error:
+ CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void ril_call_barring_query(struct ofono_call_barring *cb,
+ const char *lock, int cls,
+ ofono_call_barring_query_cb_t callback,
+ void *data)
+{
+ struct barring_data *bd = ofono_call_barring_get_data(cb);
+ struct cb_data *cbd = cb_data_new(callback, data, bd);
+ struct parcel rilp;
+
+ DBG("lock: %s, services to query: %d", lock, cls);
+
+ /*
+ * RIL modems do not support 7 as default bearer class. According to
+ * TS 22.030 Annex C: When service code is not given it corresponds to
+ * "All tele and bearer services"
+ */
+ if (cls == BEARER_CLASS_DEFAULT)
+ cls = SERVICE_CLASS_NONE;
+
+ /* ril.h: password should be empty string "" when not needed */
+ g_ril_request_query_facility_lock(bd->ril, lock, "", cls, &rilp);
+
+ if (g_ril_send(bd->ril, RIL_REQUEST_QUERY_FACILITY_LOCK, &rilp,
+ ril_call_barring_query_cb, cbd, g_free) <= 0) {
+ ofono_error("%s: sending failed", __func__);
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(callback, -1, data);
+ }
+}
+
+static void ril_call_barring_set_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_call_barring_set_cb_t cb = cbd->cb;
+ struct barring_data *bd = cbd->user;
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("%s: set failed, err: %s", __func__,
+ ril_error_to_string(message->error));
+ goto error;
+ }
+
+ /* Just for printing return value */
+ g_ril_reply_parse_set_facility_lock(bd->ril, message);
+
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ return;
+
+error:
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void ril_call_barring_set(struct ofono_call_barring *cb,
+ const char *lock, int enable,
+ const char *passwd, int cls,
+ ofono_call_barring_set_cb_t callback,
+ void *data)
+{
+ struct barring_data *bd = ofono_call_barring_get_data(cb);
+ struct cb_data *cbd = cb_data_new(callback, data, bd);
+ struct parcel rilp;
+
+ DBG("lock: %s, enable: %d, bearer class: %d", lock, enable, cls);
+
+ /*
+ * RIL modem does not support 7 as default bearer class. According to
+ * the 22.030 Annex C: When service code is not given it corresponds to
+ * "All tele and bearer services"
+ */
+ if (cls == BEARER_CLASS_DEFAULT)
+ cls = SERVICE_CLASS_NONE;
+
+ g_ril_request_set_facility_lock(bd->ril, lock, enable,
+ passwd, cls, &rilp);
+
+ if (g_ril_send(bd->ril, RIL_REQUEST_SET_FACILITY_LOCK, &rilp,
+ ril_call_barring_set_cb, cbd, g_free) <= 0) {
+ ofono_error("%s: sending failed", __func__);
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(callback, data);
+ }
+}
+
+static void ril_call_barring_set_passwd_cb(struct ril_msg *message,
+ gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_call_barring_set_cb_t cb = cbd->cb;
+ struct barring_data *bd = cbd->user;
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("%s: set password failed, err: %s", __func__,
+ ril_error_to_string(message->error));
+ goto error;
+ }
+
+ g_ril_print_response_no_args(bd->ril, message);
+
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ return;
+
+error:
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void ril_call_barring_set_passwd(struct ofono_call_barring *barr,
+ const char *lock,
+ const char *old_passwd,
+ const char *new_passwd,
+ ofono_call_barring_set_cb_t cb,
+ void *data)
+{
+ struct barring_data *bd = ofono_call_barring_get_data(barr);
+ struct cb_data *cbd = cb_data_new(cb, data, bd);
+ struct parcel rilp;
+
+ DBG("lock %s old %s new %s", lock, old_passwd, new_passwd);
+
+ g_ril_request_change_barring_password(bd->ril, lock, old_passwd,
+ new_passwd, &rilp);
+
+ if (g_ril_send(bd->ril, RIL_REQUEST_CHANGE_BARRING_PASSWORD, &rilp,
+ ril_call_barring_set_passwd_cb, cbd, g_free) <= 0) {
+ ofono_error("%s: sending failed", __func__);
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static gboolean ril_delayed_register(gpointer user_data)
+{
+ struct ofono_call_barring *cb = user_data;
+
+ ofono_call_barring_register(cb);
+ return FALSE;
+}
+
+static int ril_call_barring_probe(struct ofono_call_barring *cb,
+ unsigned int vendor, void *user)
+{
+ GRil *ril = user;
+ struct barring_data *bd = g_try_new0(struct barring_data, 1);
+ if (bd == NULL)
+ return -ENOMEM;
+
+ bd->ril = g_ril_clone(ril);
+ ofono_call_barring_set_data(cb, bd);
+
+ g_idle_add(ril_delayed_register, cb);
+
+ return 0;
+}
+
+static void ril_call_barring_remove(struct ofono_call_barring *cb)
+{
+ struct barring_data *data = ofono_call_barring_get_data(cb);
+ ofono_call_barring_set_data(cb, NULL);
+
+ g_ril_unref(data->ril);
+ g_free(data);
+}
+
+static struct ofono_call_barring_driver driver = {
+ .name = "rilmodem",
+ .probe = ril_call_barring_probe,
+ .remove = ril_call_barring_remove,
+ .query = ril_call_barring_query,
+ .set = ril_call_barring_set,
+ .set_passwd = ril_call_barring_set_passwd
+};
+
+void ril_call_barring_init(void)
+{
+ ofono_call_barring_driver_register(&driver);
+}
+
+void ril_call_barring_exit(void)
+{
+ ofono_call_barring_driver_unregister(&driver);
+}
diff --git a/drivers/rilmodem/call-forwarding.c b/drivers/rilmodem/call-forwarding.c
new file mode 100644
index 00000000..7965e7d5
--- /dev/null
+++ b/drivers/rilmodem/call-forwarding.c
@@ -0,0 +1,327 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2013 Jolla Ltd
+ * Contact: Jussi Kangas <jussi.kangas@tieto.com>
+ * Copyright (C) 2014 Canonical Ltd.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-forwarding.h>
+
+#include "gril.h"
+#include "grilrequest.h"
+#include "grilreply.h"
+#include "grilunsol.h"
+
+#include "rilmodem.h"
+
+#include "common.h"
+
+enum cf_action {
+ CF_ACTION_DISABLE,
+ CF_ACTION_ENABLE,
+ CF_ACTION_INTERROGATE,
+ CF_ACTION_REGISTRATION,
+ CF_ACTION_ERASURE,
+};
+
+struct forw_data {
+ GRil *ril;
+ enum cf_action last_action;
+ int last_cls;
+};
+
+static const char *cf_action_to_string(enum cf_action action)
+{
+ switch (action) {
+ case CF_ACTION_DISABLE:
+ return "DISABLE";
+ case CF_ACTION_ENABLE:
+ return "ENABLE";
+ case CF_ACTION_INTERROGATE:
+ return "INTERROGATE";
+ case CF_ACTION_REGISTRATION:
+ return "REGISTRATION";
+ case CF_ACTION_ERASURE:
+ return "ERASURE";
+ }
+
+ return NULL;
+}
+
+static void ril_query_call_fwd_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct forw_data *fd = ofono_call_forwarding_get_data(cbd->user);
+ ofono_call_forwarding_query_cb_t cb = cbd->cb;
+ struct ofono_call_forwarding_condition *list;
+ unsigned int list_size;
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("%s: rild error: %s", __func__,
+ ril_error_to_string(message->error));
+ goto error;
+ }
+
+ list = g_ril_reply_parse_query_call_fwd(fd->ril, message, &list_size);
+ /*
+ * From atmodem:
+ *
+ * Specification is really unclear about this
+ * generate status=0 for all classes just in case
+ */
+ if (list_size == 0) {
+ list = g_new0(struct ofono_call_forwarding_condition, 1);
+ list_size = 1;
+
+ list->status = 0;
+ list->cls = fd->last_cls;
+ } else if (list == NULL) {
+ goto error;
+ }
+
+ CALLBACK_WITH_SUCCESS(cb, (int) list_size, list, cbd->data);
+ g_free(list);
+ return;
+
+error:
+ CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data);
+}
+
+static void ril_set_forward_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_call_forwarding_set_cb_t cb = cbd->cb;
+ struct forw_data *fd = ofono_call_forwarding_get_data(cbd->user);
+
+ if (message->error == RIL_E_SUCCESS) {
+ g_ril_print_response_no_args(fd->ril, message);
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ } else {
+ ofono_error("%s: CF %s failed; rild error: %s", __func__,
+ cf_action_to_string(fd->last_action),
+ ril_error_to_string(message->error));
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ }
+}
+
+static int ril_send_forward_cmd(int type, int cls,
+ const struct ofono_phone_number *number,
+ int time,
+ struct cb_data *cbd,
+ enum cf_action action)
+{
+ struct ofono_call_forwarding *cf = cbd->user;
+ struct forw_data *fd = ofono_call_forwarding_get_data(cf);
+ struct parcel rilp;
+ struct req_call_fwd fwd_req;
+ int ret = 0, request;
+ GRilResponseFunc response_func;
+
+ if (action == CF_ACTION_INTERROGATE) {
+ request = RIL_REQUEST_QUERY_CALL_FORWARD_STATUS;
+ response_func = ril_query_call_fwd_cb;
+ } else {
+ request = RIL_REQUEST_SET_CALL_FORWARD;
+ response_func = ril_set_forward_cb;
+ }
+
+ DBG("%s - %s", ril_request_id_to_string(request),
+ cf_action_to_string(action));
+
+ /*
+ * Modem seems to respond with error to all queries
+ * or settings made with bearer class
+ * BEARER_CLASS_DEFAULT. Design decision: If given
+ * class is BEARER_CLASS_DEFAULT let's map it to
+ * SERVICE_CLASS_NONE as with it e.g. ./send-ussd '*21*<phone_number>#'
+ * returns cls:53 i.e. 1+4+16+32 as service class.
+ */
+ if (cls == BEARER_CLASS_DEFAULT)
+ cls = SERVICE_CLASS_NONE;
+
+ fd->last_action = action;
+ fd->last_cls = cls;
+
+ fwd_req.action = (int) action;
+ fwd_req.type = type;
+ fwd_req.cls = cls;
+ fwd_req.number = number;
+
+ /*
+ * time has no real meaing for action commands other
+ * then registration, so if not needed, set arbitrary
+ * 60s time so rild doesn't return an error.
+ */
+ if (time == -1)
+ fwd_req.time = 60;
+ else
+ fwd_req.time = time;
+
+ g_ril_request_call_fwd(fd->ril, &fwd_req, &rilp);
+
+ ret = g_ril_send(fd->ril, request, &rilp, response_func, cbd, g_free);
+ if (ret == 0)
+ ofono_error("%s: CF action %s failed", __func__,
+ cf_action_to_string(action));
+ return ret;
+}
+
+static void ril_activate(struct ofono_call_forwarding *cf,
+ int type, int cls,
+ ofono_call_forwarding_set_cb_t cb, void *data)
+{
+ struct cb_data *cbd = cb_data_new(cb, data, cf);
+
+ if (ril_send_forward_cmd(type, cls, NULL, -1, cbd,
+ CF_ACTION_ENABLE) == 0) {
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ g_free(cbd);
+ }
+}
+
+static void ril_erasure(struct ofono_call_forwarding *cf,
+ int type, int cls,
+ ofono_call_forwarding_set_cb_t cb, void *data)
+{
+ struct cb_data *cbd = cb_data_new(cb, data, cf);
+
+ if (ril_send_forward_cmd(type, cls, NULL, -1, cbd,
+ CF_ACTION_ERASURE) == 0) {
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ g_free(cbd);
+ }
+}
+
+static void ril_deactivate(struct ofono_call_forwarding *cf,
+ int type, int cls,
+ ofono_call_forwarding_set_cb_t cb, void *data)
+{
+ struct cb_data *cbd = cb_data_new(cb, data, cf);
+
+ if (ril_send_forward_cmd(type, cls, NULL, -1, cbd,
+ CF_ACTION_DISABLE) == 0) {
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ g_free(cbd);
+ }
+}
+
+static void ril_registration(struct ofono_call_forwarding *cf, int type,
+ int cls,
+ const struct ofono_phone_number *number,
+ int time, ofono_call_forwarding_set_cb_t cb,
+ void *data)
+{
+ struct cb_data *cbd = cb_data_new(cb, data, cf);
+
+ if (ril_send_forward_cmd(type, cls, number, time, cbd,
+ CF_ACTION_REGISTRATION) == 0) {
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ g_free(cbd);
+ }
+}
+
+static void ril_query(struct ofono_call_forwarding *cf, int type, int cls,
+ ofono_call_forwarding_query_cb_t cb,
+ void *data)
+{
+ struct cb_data *cbd = cb_data_new(cb, data, cf);
+
+ if (ril_send_forward_cmd(type, cls, NULL, -1, cbd,
+ CF_ACTION_INTERROGATE) == 0) {
+ CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data);
+ g_free(cbd);
+ }
+}
+
+static gboolean ril_delayed_register(gpointer user_data)
+{
+ struct ofono_call_forwarding *cf = user_data;
+
+ ofono_call_forwarding_register(cf);
+ return FALSE;
+}
+
+static int ril_call_forwarding_probe(struct ofono_call_forwarding *cf,
+ unsigned int vendor, void *user)
+{
+ GRil *ril = user;
+ struct forw_data *fd;
+
+ fd = g_try_new0(struct forw_data, 1);
+ if (fd == NULL)
+ return -ENOMEM;
+
+ fd->ril = g_ril_clone(ril);
+ ofono_call_forwarding_set_data(cf, fd);
+
+ /*
+ * ofono_call_forwarding_register() needs to be called after
+ * the driver has been set in ofono_call_forwarding_create(),
+ * which calls this function. Most other drivers make
+ * some kind of capabilities query to the modem, and then
+ * call register in the callback; we use an idle event instead.
+ */
+ g_idle_add(ril_delayed_register, cf);
+
+ return 0;
+}
+
+static void ril_call_forwarding_remove(struct ofono_call_forwarding *cf)
+{
+ struct forw_data *data = ofono_call_forwarding_get_data(cf);
+ ofono_call_forwarding_set_data(cf, NULL);
+
+ g_ril_unref(data->ril);
+ g_free(data);
+}
+
+static struct ofono_call_forwarding_driver driver = {
+ .name = RILMODEM,
+ .probe = ril_call_forwarding_probe,
+ .remove = ril_call_forwarding_remove,
+ .erasure = ril_erasure,
+ .deactivation = ril_deactivate,
+ .query = ril_query,
+ .registration = ril_registration,
+ .activation = ril_activate
+};
+
+void ril_call_forwarding_init(void)
+{
+ ofono_call_forwarding_driver_register(&driver);
+}
+
+void ril_call_forwarding_exit(void)
+{
+ ofono_call_forwarding_driver_unregister(&driver);
+}
diff --git a/drivers/rilmodem/call-settings.c b/drivers/rilmodem/call-settings.c
new file mode 100644
index 00000000..5603fbd6
--- /dev/null
+++ b/drivers/rilmodem/call-settings.c
@@ -0,0 +1,286 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2013 Jolla Ltd
+ * Copyright (C) 2013 Canonical Ltd
+ * Contact: Jussi Kangas <jussi.kangas@tieto.com>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-settings.h>
+
+#include "gril.h"
+#include "grilutil.h"
+#include "grilrequest.h"
+#include "grilreply.h"
+
+#include "rilmodem.h"
+#include "ril_constants.h"
+#include "common.h"
+
+struct settings_data {
+ GRil *ril;
+};
+
+static void ril_set_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct ofono_call_settings *cs = cbd->user;
+ struct settings_data *sd = ofono_call_settings_get_data(cs);
+ ofono_call_settings_set_cb_t cb = cbd->cb;
+
+ if (message->error == RIL_E_SUCCESS) {
+ g_ril_print_response_no_args(sd->ril, message);
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ } else {
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ }
+}
+
+static void ril_cw_set(struct ofono_call_settings *cs, int mode, int cls,
+ ofono_call_settings_set_cb_t cb, void *data)
+{
+ struct settings_data *sd = ofono_call_settings_get_data(cs);
+ struct cb_data *cbd = cb_data_new(cb, data, cs);
+ int ret;
+ struct parcel rilp;
+
+ g_ril_request_set_call_waiting(sd->ril, mode, cls, &rilp);
+
+ ret = g_ril_send(sd->ril, RIL_REQUEST_SET_CALL_WAITING, &rilp,
+ ril_set_cb, cbd, g_free);
+
+ /* In case of error free cbd and return the cb with failure */
+ if (ret <= 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static void ril_cw_query_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct ofono_call_settings *cs = cbd->user;
+ struct settings_data *sd = ofono_call_settings_get_data(cs);
+ ofono_call_settings_status_cb_t cb = cbd->cb;
+
+ if (message->error == RIL_E_SUCCESS) {
+ int res;
+
+ res = g_ril_reply_parse_query_call_waiting(sd->ril, message);
+
+ CALLBACK_WITH_SUCCESS(cb, res, cbd->data);
+ } else {
+ CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+ }
+}
+
+static void ril_cw_query(struct ofono_call_settings *cs, int cls,
+ ofono_call_settings_status_cb_t cb, void *data)
+{
+ struct settings_data *sd = ofono_call_settings_get_data(cs);
+ struct cb_data *cbd = cb_data_new(cb, data, cs);
+ int ret;
+ struct parcel rilp;
+
+ g_ril_request_query_call_waiting(sd->ril, cls, &rilp);
+
+ ret = g_ril_send(sd->ril, RIL_REQUEST_QUERY_CALL_WAITING, &rilp,
+ ril_cw_query_cb, cbd, g_free);
+
+ /* In case of error free cbd and return the cb with failure */
+ if (ret <= 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, -1, data);
+ }
+}
+
+static void ril_clip_query_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct ofono_call_settings *cs = cbd->user;
+ struct settings_data *sd = ofono_call_settings_get_data(cs);
+ ofono_call_settings_status_cb_t cb = cbd->cb;
+
+ if (message->error == RIL_E_SUCCESS) {
+ int res;
+
+ res = g_ril_reply_parse_query_clip(sd->ril, message);
+
+ CALLBACK_WITH_SUCCESS(cb, res, cbd->data);
+ } else {
+ CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+ }
+}
+
+static void ril_clip_query(struct ofono_call_settings *cs,
+ ofono_call_settings_status_cb_t cb, void *data)
+{
+ struct settings_data *sd = ofono_call_settings_get_data(cs);
+ struct cb_data *cbd = cb_data_new(cb, data, cs);
+ int ret;
+
+ ret = g_ril_send(sd->ril, RIL_REQUEST_QUERY_CLIP, NULL,
+ ril_clip_query_cb, cbd, g_free);
+
+ /* In case of error free cbd and return the cb with failure */
+ if (ret <= 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, -1, data);
+ }
+}
+
+static void ril_clir_query_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct ofono_call_settings *cs = cbd->user;
+ struct settings_data *sd = ofono_call_settings_get_data(cs);
+ ofono_call_settings_clir_cb_t cb = cbd->cb;
+ struct reply_clir *rclir;
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("%s: Reply failure: %s", __func__,
+ ril_error_to_string(message->error));
+ goto error;
+ }
+
+ rclir = g_ril_reply_parse_get_clir(sd->ril, message);
+ if (rclir == NULL) {
+ ofono_error("%s: parse error", __func__);
+ goto error;
+ }
+
+ CALLBACK_WITH_SUCCESS(cb, rclir->status, rclir->provisioned, cbd->data);
+
+ g_ril_reply_free_get_clir(rclir);
+
+ return;
+
+error:
+ CALLBACK_WITH_FAILURE(cb, -1, -1, cbd->data);
+}
+
+static void ril_clir_query(struct ofono_call_settings *cs,
+ ofono_call_settings_clir_cb_t cb, void *data)
+{
+ struct settings_data *sd = ofono_call_settings_get_data(cs);
+ struct cb_data *cbd = cb_data_new(cb, data, cs);
+ int ret;
+
+ ret = g_ril_send(sd->ril, RIL_REQUEST_GET_CLIR, NULL,
+ ril_clir_query_cb, cbd, g_free);
+
+ if (ret <= 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, -1, -1, data);
+ }
+}
+
+
+static void ril_clir_set(struct ofono_call_settings *cs, int mode,
+ ofono_call_settings_set_cb_t cb, void *data)
+{
+ struct settings_data *sd = ofono_call_settings_get_data(cs);
+ struct cb_data *cbd = cb_data_new(cb, data, cs);
+ struct parcel rilp;
+ int ret;
+
+ g_ril_request_set_clir(sd->ril, mode, &rilp);
+
+ ret = g_ril_send(sd->ril, RIL_REQUEST_SET_CLIR, &rilp,
+ ril_set_cb, cbd, g_free);
+
+ if (ret <= 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static gboolean ril_delayed_register(gpointer user_data)
+{
+ struct ofono_call_settings *cs = user_data;
+
+ ofono_call_settings_register(cs);
+
+ return FALSE;
+}
+
+static int ril_call_settings_probe(struct ofono_call_settings *cs,
+ unsigned int vendor, void *user)
+{
+ GRil *ril = user;
+ struct settings_data *sd = g_new0(struct settings_data, 1);
+
+ sd->ril = g_ril_clone(ril);
+
+ ofono_call_settings_set_data(cs, sd);
+
+ g_idle_add(ril_delayed_register, cs);
+
+ return 0;
+}
+
+static void ril_call_settings_remove(struct ofono_call_settings *cs)
+{
+ struct settings_data *sd = ofono_call_settings_get_data(cs);
+ ofono_call_settings_set_data(cs, NULL);
+
+ g_ril_unref(sd->ril);
+ g_free(sd);
+}
+
+static struct ofono_call_settings_driver driver = {
+ .name = RILMODEM,
+ .probe = ril_call_settings_probe,
+ .remove = ril_call_settings_remove,
+ .clip_query = ril_clip_query,
+ .cw_query = ril_cw_query,
+ .cw_set = ril_cw_set,
+ .clir_query = ril_clir_query,
+ .clir_set = ril_clir_set
+
+ /*
+ * Not supported in RIL API
+ * .colp_query = ril_colp_query,
+ * .colr_query = ril_colr_query
+ */
+};
+
+void ril_call_settings_init(void)
+{
+ ofono_call_settings_driver_register(&driver);
+}
+
+void ril_call_settings_exit(void)
+{
+ ofono_call_settings_driver_unregister(&driver);
+}
diff --git a/drivers/rilmodem/call-volume.c b/drivers/rilmodem/call-volume.c
new file mode 100644
index 00000000..4e88772e
--- /dev/null
+++ b/drivers/rilmodem/call-volume.c
@@ -0,0 +1,182 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2012-2013 Canonical Ltd.
+ *
+ * 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 <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-volume.h>
+
+#include "gril.h"
+#include "grilutil.h"
+
+#include "common.h"
+
+#include "rilmodem.h"
+#include "parcel.h"
+#include "grilrequest.h"
+#include "grilreply.h"
+
+struct cv_data {
+ GRil *ril;
+ unsigned int vendor;
+};
+
+static void volume_mute_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_call_volume_cb_t cb = cbd->cb;
+ struct cv_data *cvd = cbd->user;
+ struct ofono_error error;
+
+ if (message->error == RIL_E_SUCCESS) {
+ decode_ril_error(&error, "OK");
+
+ g_ril_print_response_no_args(cvd->ril, message);
+
+ } else {
+ ofono_error("Could not set the ril mute state");
+ decode_ril_error(&error, "FAIL");
+ }
+
+ cb(&error, cbd->data);
+}
+
+static void ril_call_volume_mute(struct ofono_call_volume *cv, int muted,
+ ofono_call_volume_cb_t cb, void *data)
+{
+ struct cv_data *cvd = ofono_call_volume_get_data(cv);
+ struct cb_data *cbd = cb_data_new(cb, data, cvd);
+ struct parcel rilp;
+
+ DBG("Initial ril muted state: %d", muted);
+
+ g_ril_request_set_mute(cvd->ril, muted, &rilp);
+
+ if (g_ril_send(cvd->ril, RIL_REQUEST_SET_MUTE, &rilp,
+ volume_mute_cb, cbd, g_free) == 0) {
+ ofono_error("Send RIL_REQUEST_SET_MUTE failed.");
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static void probe_mute_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct ofono_call_volume *cv = user_data;
+ struct cv_data *cvd = ofono_call_volume_get_data(cv);
+ int muted;
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("Could not retrieve the ril mute state");
+ return;
+ }
+
+ muted = g_ril_reply_parse_get_mute(cvd->ril, message);
+
+ ofono_call_volume_set_muted(cv, muted);
+}
+
+static void call_probe_mute(gpointer user_data)
+{
+ struct ofono_call_volume *cv = user_data;
+ struct cv_data *cvd = ofono_call_volume_get_data(cv);
+
+ g_ril_send(cvd->ril, RIL_REQUEST_GET_MUTE, NULL,
+ probe_mute_cb, cv, NULL);
+}
+
+static gboolean ril_delayed_register(gpointer user_data)
+{
+ struct ofono_call_volume *cv = user_data;
+ DBG("");
+ ofono_call_volume_register(cv);
+
+ /* Probe the mute state */
+ call_probe_mute(user_data);
+
+ /* This makes the timeout a single-shot */
+ return FALSE;
+}
+
+static int ril_call_volume_probe(struct ofono_call_volume *cv,
+ unsigned int vendor, void *data)
+{
+ GRil *ril = data;
+ struct cv_data *cvd;
+
+ cvd = g_new0(struct cv_data, 1);
+ if (cvd == NULL)
+ return -ENOMEM;
+
+ cvd->ril = g_ril_clone(ril);
+ cvd->vendor = vendor;
+
+ ofono_call_volume_set_data(cv, cvd);
+
+ /*
+ * ofono_call_volume_register() needs to be called after
+ * the driver has been set in ofono_call_volume_create(),
+ * which calls this function. Most other drivers make
+ * some kind of capabilities query to the modem, and then
+ * call register in the callback; we use an idle event instead.
+ */
+ g_idle_add(ril_delayed_register, cv);
+
+ return 0;
+}
+
+static void ril_call_volume_remove(struct ofono_call_volume *cv)
+{
+ struct cv_data *cvd = ofono_call_volume_get_data(cv);
+
+ ofono_call_volume_set_data(cv, NULL);
+
+ g_ril_unref(cvd->ril);
+ g_free(cvd);
+}
+
+static struct ofono_call_volume_driver driver = {
+ .name = RILMODEM,
+ .probe = ril_call_volume_probe,
+ .remove = ril_call_volume_remove,
+ .mute = ril_call_volume_mute,
+};
+
+void ril_call_volume_init(void)
+{
+ ofono_call_volume_driver_register(&driver);
+}
+
+void ril_call_volume_exit(void)
+{
+ ofono_call_volume_driver_unregister(&driver);
+}
diff --git a/drivers/rilmodem/devinfo.c b/drivers/rilmodem/devinfo.c
new file mode 100644
index 00000000..2811837b
--- /dev/null
+++ b/drivers/rilmodem/devinfo.c
@@ -0,0 +1,218 @@
+/*
+ *
+ * oFono - Open Source Telephony - RIL Modem Support
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2012-2013 Canonical Ltd.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/devinfo.h>
+
+#include "gril.h"
+
+#include "rilmodem.h"
+#include "grilreply.h"
+
+/*
+ * TODO: The functions in this file are stubbed out, and
+ * will need to be re-worked to talk to the /gril layer
+ * in order to get real values from RILD.
+ */
+
+static void ril_query_manufacturer(struct ofono_devinfo *info,
+ ofono_devinfo_query_cb_t cb,
+ void *data)
+{
+ const char *attr = "Fake Manufacturer";
+ struct cb_data *cbd = cb_data_new(cb, data, NULL);
+ struct ofono_error error;
+ decode_ril_error(&error, "OK");
+
+ cb(&error, attr, cbd->data);
+
+ /* Note: this will need to change if cbd passed to gril layer */
+ g_free(cbd);
+}
+
+static void ril_query_model(struct ofono_devinfo *info,
+ ofono_devinfo_query_cb_t cb,
+ void *data)
+{
+ const char *attr = "Fake Modem Model";
+ struct cb_data *cbd = cb_data_new(cb, data, NULL);
+ struct ofono_error error;
+ decode_ril_error(&error, "OK");
+
+ cb(&error, attr, cbd->data);
+
+ /* Note: this will need to change if cbd passed to gril layer */
+ g_free(cbd);
+}
+
+static void query_revision_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_devinfo_query_cb_t cb = cbd->cb;
+ GRil *ril = cbd->user;
+ struct ofono_error error;
+ char *revision;
+
+ if (message->error == RIL_E_SUCCESS) {
+ decode_ril_error(&error, "OK");
+ } else {
+ decode_ril_error(&error, "FAIL");
+ cb(&error, NULL, cbd->data);
+ return;
+ }
+
+ revision = g_ril_reply_parse_baseband_version(ril, message);
+
+ cb(&error, revision, cbd->data);
+
+ g_free(revision);
+}
+
+static void ril_query_revision(struct ofono_devinfo *info,
+ ofono_devinfo_query_cb_t cb,
+ void *data)
+{
+ GRil *ril = ofono_devinfo_get_data(info);
+ struct cb_data *cbd = cb_data_new(cb, data, ril);
+
+ if (g_ril_send(ril, RIL_REQUEST_BASEBAND_VERSION, NULL,
+ query_revision_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, NULL, data);
+ }
+}
+
+static void query_serial_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_devinfo_query_cb_t cb = cbd->cb;
+ GRil *ril = cbd->user;
+ struct ofono_error error;
+ gchar *imei;
+
+ if (message->error == RIL_E_SUCCESS) {
+ decode_ril_error(&error, "OK");
+ } else {
+ decode_ril_error(&error, "FAIL");
+ cb(&error, NULL, cbd->data);
+ return;
+ }
+
+ imei = g_ril_reply_parse_baseband_version(ril, message);
+
+ cb(&error, imei, cbd->data);
+
+ g_free(imei);
+}
+
+static void ril_query_serial(struct ofono_devinfo *info,
+ ofono_devinfo_query_cb_t cb,
+ void *data)
+{
+ GRil *ril = ofono_devinfo_get_data(info);
+ struct cb_data *cbd = cb_data_new(cb, data, ril);
+
+ /*
+ * TODO: make it support both RIL_REQUEST_GET_IMEI (deprecated) and
+ * RIL_REQUEST_DEVICE_IDENTITY depending on the rild version used
+ */
+
+ if (g_ril_send(ril, RIL_REQUEST_GET_IMEI, NULL,
+ query_serial_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, NULL, data);
+ }
+}
+
+static gboolean ril_delayed_register(gpointer user_data)
+{
+ struct ofono_devinfo *info = user_data;
+ DBG("");
+ ofono_devinfo_register(info);
+
+ /* This makes the timeout a single-shot */
+ return FALSE;
+}
+
+static int ril_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor,
+ void *data)
+{
+ GRil *ril = NULL;
+
+ if (data != NULL)
+ ril = g_ril_clone(data);
+
+ ofono_devinfo_set_data(info, ril);
+
+ /*
+ * ofono_devinfo_register() needs to be called after
+ * the driver has been set in ofono_devinfo_create(),
+ * which calls this function. Most other drivers make
+ * some kind of capabilities query to the modem, and then
+ * call register in the callback; we use an idle event instead.
+ */
+ g_idle_add(ril_delayed_register, info);
+
+ return 0;
+}
+
+static void ril_devinfo_remove(struct ofono_devinfo *info)
+{
+ GRil *ril = ofono_devinfo_get_data(info);
+
+ ofono_devinfo_set_data(info, NULL);
+
+ g_ril_unref(ril);
+}
+
+static struct ofono_devinfo_driver driver = {
+ .name = RILMODEM,
+ .probe = ril_devinfo_probe,
+ .remove = ril_devinfo_remove,
+ .query_manufacturer = ril_query_manufacturer,
+ .query_model = ril_query_model,
+ .query_revision = ril_query_revision,
+ .query_serial = ril_query_serial
+};
+
+void ril_devinfo_init(void)
+{
+ ofono_devinfo_driver_register(&driver);
+}
+
+void ril_devinfo_exit(void)
+{
+ ofono_devinfo_driver_unregister(&driver);
+}
diff --git a/drivers/rilmodem/gprs-context.c b/drivers/rilmodem/gprs-context.c
new file mode 100644
index 00000000..20215335
--- /dev/null
+++ b/drivers/rilmodem/gprs-context.c
@@ -0,0 +1,585 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2013 Canonical Ltd.
+ *
+ * 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 <sys/stat.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs-context.h>
+#include <ofono/types.h>
+
+#include "ofono.h"
+
+#include "grilreply.h"
+#include "grilrequest.h"
+#include "grilunsol.h"
+
+#include "gprs.h"
+#include "rilmodem.h"
+
+#define NUM_DEACTIVATION_RETRIES 4
+#define TIME_BETWEEN_DEACT_RETRIES_S 2
+
+enum state {
+ STATE_IDLE,
+ STATE_ENABLING,
+ STATE_DISABLING,
+ STATE_ACTIVE,
+};
+
+struct gprs_context_data {
+ GRil *ril;
+ struct ofono_modem *modem;
+ unsigned vendor;
+ gint active_ctx_cid;
+ gint active_rild_cid;
+ enum state state;
+ guint call_list_id;
+ char *apn;
+ enum ofono_gprs_context_type type;
+ int deact_retries;
+ guint retry_ev_id;
+ struct cb_data *retry_cbd;
+ guint reset_ev_id;
+};
+
+static void ril_gprs_context_deactivate_primary(struct ofono_gprs_context *gc,
+ unsigned int id,
+ ofono_gprs_context_cb_t cb,
+ void *data);
+static void ril_deactivate_data_call_cb(struct ril_msg *message,
+ gpointer user_data);
+
+static void set_context_disconnected(struct gprs_context_data *gcd)
+{
+ DBG("");
+
+ gcd->active_ctx_cid = -1;
+ gcd->active_rild_cid = -1;
+ gcd->state = STATE_IDLE;
+ g_free(gcd->apn);
+ gcd->apn = NULL;
+}
+
+static void disconnect_context(struct ofono_gprs_context *gc)
+{
+ ril_gprs_context_deactivate_primary(gc, 0, NULL, NULL);
+}
+
+static void ril_gprs_context_call_list_changed(struct ril_msg *message,
+ gpointer user_data)
+{
+ struct ofono_gprs_context *gc = user_data;
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+ struct ril_data_call *call = NULL;
+ struct ril_data_call_list *call_list;
+ gboolean active_cid_found = FALSE;
+ gboolean disconnect = FALSE;
+ GSList *iterator = NULL;
+
+ call_list = g_ril_unsol_parse_data_call_list(gcd->ril, message);
+ if (call_list == NULL)
+ return;
+
+ DBG("*gc: %p num calls: %d", gc, g_slist_length(call_list->calls));
+
+ for (iterator = call_list->calls; iterator; iterator = iterator->next) {
+ call = (struct ril_data_call *) iterator->data;
+
+ if (call->cid == gcd->active_rild_cid) {
+ active_cid_found = TRUE;
+ DBG("found call - cid: %d", call->cid);
+
+ if (call->active == 0) {
+ DBG("call !active; notify disconnect: %d",
+ call->cid);
+ disconnect = TRUE;
+ }
+
+ break;
+ }
+ }
+
+ if ((disconnect == TRUE || active_cid_found == FALSE)
+ && gcd->state != STATE_IDLE) {
+ ofono_info("Clearing active context; disconnect: %d"
+ " active_cid_found: %d active_ctx_cid: %d",
+ disconnect, active_cid_found, gcd->active_ctx_cid);
+
+ ofono_gprs_context_deactivated(gc, gcd->active_ctx_cid);
+ set_context_disconnected(gcd);
+ }
+
+ g_ril_unsol_free_data_call_list(call_list);
+}
+
+static void ril_setup_data_call_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_gprs_context_cb_t cb = cbd->cb;
+ struct ofono_gprs_context *gc = cbd->user;
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+ struct ril_data_call *call = NULL;
+ struct ril_data_call_list *call_list = NULL;
+
+ DBG("*gc: %p", gc);
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("%s: setup data call failed for apn: %s - %s",
+ __func__, gcd->apn,
+ ril_error_to_string(message->error));
+
+ set_context_disconnected(gcd);
+ goto error;
+ }
+
+ call_list = g_ril_unsol_parse_data_call_list(gcd->ril, message);
+ if (call_list == NULL) {
+ /* parsing failed, need to actually disconnect */
+ disconnect_context(gc);
+ goto error;
+ }
+
+ if (g_slist_length(call_list->calls) != 1) {
+ ofono_error("%s: setup_data_call reply for apn: %s,"
+ " includes %d calls",
+ __func__, gcd->apn,
+ g_slist_length(call_list->calls));
+
+ disconnect_context(gc);
+ goto error;
+ }
+
+ call = (struct ril_data_call *) call_list->calls->data;
+
+ /* Check for valid DNS settings, except for MMS contexts */
+ if (gcd->type != OFONO_GPRS_CONTEXT_TYPE_MMS
+ && (call->dns_addrs == NULL
+ || g_strv_length(call->dns_addrs) == 0)) {
+ ofono_error("%s: no DNS in context of type %d",
+ __func__, gcd->type);
+ disconnect_context(gc);
+ goto error;
+ }
+
+ if (call->status != PDP_FAIL_NONE) {
+ ofono_error("%s: reply->status for apn: %s, is non-zero: %s",
+ __func__, gcd->apn,
+ ril_pdp_fail_to_string(call->status));
+
+ set_context_disconnected(gcd);
+ goto error;
+ }
+
+ gcd->active_rild_cid = call->cid;
+ gcd->state = STATE_ACTIVE;
+
+ ofono_gprs_context_set_interface(gc, call->ifname);
+ ofono_gprs_context_set_ipv4_netmask(gc,
+ ril_util_get_netmask(call->ip_addr));
+
+ ofono_gprs_context_set_ipv4_address(gc, call->ip_addr, TRUE);
+ ofono_gprs_context_set_ipv4_gateway(gc, call->gateways[0]);
+
+ ofono_gprs_context_set_ipv4_dns_servers(gc,
+ (const char **) call->dns_addrs);
+
+ g_ril_unsol_free_data_call_list(call_list);
+
+ /* activate listener for data call changed events.... */
+ gcd->call_list_id =
+ g_ril_register(gcd->ril,
+ RIL_UNSOL_DATA_CALL_LIST_CHANGED,
+ ril_gprs_context_call_list_changed, gc);
+
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ return;
+
+error:
+ g_ril_unsol_free_data_call_list(call_list);
+
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void ril_gprs_context_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);
+ struct ofono_modem *modem = ofono_gprs_context_get_modem(gc);
+ struct ofono_atom *gprs_atom =
+ __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_GPRS);
+ struct ofono_gprs *gprs = NULL;
+ struct ril_gprs_data *gd = NULL;
+ struct cb_data *cbd = cb_data_new(cb, data, gc);
+ struct req_setup_data_call request;
+ struct parcel rilp;
+ struct ofono_error error;
+ int ret = 0;
+
+ g_assert(gprs_atom != NULL);
+ gprs = __ofono_atom_get_data(gprs_atom);
+ g_assert(gprs != NULL);
+ gd = ofono_gprs_get_data(gprs);
+ g_assert(gd != NULL);
+
+ /*
+ * 0: CDMA 1: GSM/UMTS, 2...
+ * anything 2+ is a RadioTechnology value +2
+ */
+ DBG("*gc: %p activating cid: %d; curr_tech: %d", gc, ctx->cid,
+ gd->tech);
+
+ if (gd->tech == RADIO_TECH_UNKNOWN) {
+ ofono_error("%s: radio tech for apn: %s UNKNOWN!", __func__,
+ gcd->apn);
+ request.tech = 1;
+ } else {
+ request.tech = gd->tech + 2;
+ }
+
+ /*
+ * TODO: add comments about tethering, other non-public
+ * profiles...
+ */
+ if (g_ril_vendor(gcd->ril) == OFONO_RIL_VENDOR_MTK &&
+ gcd->type == OFONO_GPRS_CONTEXT_TYPE_MMS)
+ request.data_profile = RIL_DATA_PROFILE_MTK_MMS;
+ else
+ request.data_profile = RIL_DATA_PROFILE_DEFAULT;
+
+ request.apn = g_strdup(ctx->apn);
+ request.username = g_strdup(ctx->username);
+ request.password = g_strdup(ctx->password);
+
+ /*
+ * We do the same as in $AOSP/frameworks/opt/telephony/src/java/com/
+ * android/internal/telephony/dataconnection/DataConnection.java,
+ * onConnect(), and use authentication or not depending on whether
+ * the user field is empty or not.
+ */
+ if (request.username != NULL && request.username[0] != '\0')
+ request.auth_type = RIL_AUTH_BOTH;
+ else
+ request.auth_type = RIL_AUTH_NONE;
+
+ request.protocol = ctx->proto;
+ request.req_cid = ctx->cid;
+
+ if (g_ril_request_setup_data_call(gcd->ril,
+ &request,
+ &rilp,
+ &error) == FALSE) {
+ ofono_error("%s: couldn't build SETUP_DATA_CALL"
+ " request for apn: %s.",
+ __func__, request.apn);
+ goto error;
+ }
+
+ gcd->active_ctx_cid = ctx->cid;
+ gcd->state = STATE_ENABLING;
+ gcd->apn = g_strdup(ctx->apn);
+
+ ret = g_ril_send(gcd->ril, RIL_REQUEST_SETUP_DATA_CALL, &rilp,
+ ril_setup_data_call_cb, cbd, g_free);
+
+error:
+ g_free(request.apn);
+ g_free(request.username);
+ g_free(request.password);
+
+ if (ret == 0) {
+ ofono_error("%s: send SETUP_DATA_CALL failed for apn: %s.",
+ __func__, gcd->apn);
+
+ set_context_disconnected(gcd);
+
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static gboolean reset_modem(gpointer data)
+{
+ /* TODO call mtk_reset_modem when driver is upstreamed */
+ return FALSE;
+}
+
+static gboolean retry_deactivate(gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_gprs_context_cb_t cb = cbd->cb;
+ struct ofono_gprs_context *gc = cbd->user;
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+ struct req_deactivate_data_call request;
+ struct parcel rilp;
+ struct ofono_error error;
+
+ gcd->retry_ev_id = 0;
+
+ /* We might have received a call list update while waiting */
+ if (gcd->state == STATE_IDLE) {
+ if (cb)
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+
+ g_free(cbd);
+
+ return FALSE;
+ }
+
+ request.cid = gcd->active_rild_cid;
+ request.reason = RIL_DEACTIVATE_DATA_CALL_NO_REASON;
+
+ g_ril_request_deactivate_data_call(gcd->ril, &request, &rilp, &error);
+
+ if (g_ril_send(gcd->ril, RIL_REQUEST_DEACTIVATE_DATA_CALL, &rilp,
+ ril_deactivate_data_call_cb, cbd, g_free) == 0) {
+
+ ofono_error("%s: send DEACTIVATE_DATA_CALL failed for apn: %s",
+ __func__, gcd->apn);
+ if (cb)
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+
+ g_free(cbd);
+ }
+
+ return FALSE;
+}
+
+static void ril_deactivate_data_call_cb(struct ril_msg *message,
+ gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_gprs_context_cb_t cb = cbd->cb;
+ struct ofono_gprs_context *gc = cbd->user;
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+ gint active_ctx_cid;
+
+ DBG("*gc: %p", gc);
+
+ if (message->error == RIL_E_SUCCESS) {
+
+ g_ril_print_response_no_args(gcd->ril, message);
+
+ active_ctx_cid = gcd->active_ctx_cid;
+ set_context_disconnected(gcd);
+
+ /*
+ * If the deactivate was a result of a data network detach or of
+ * an error in data call establishment, there won't be call
+ * back, so _deactivated() needs to be called directly.
+ */
+ if (cb)
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ else
+ ofono_gprs_context_deactivated(gc, active_ctx_cid);
+
+ } else {
+ ofono_error("%s: reply failure for apn: %s - %s",
+ __func__, gcd->apn,
+ ril_error_to_string(message->error));
+
+ /*
+ * It has been detected that some modems fail the deactivation
+ * temporarily. We do retries to handle that case.
+ */
+ if (--(gcd->deact_retries) > 0) {
+ gcd->retry_cbd = cb_data_new(cb, cbd->data, gc);
+ gcd->retry_ev_id =
+ g_timeout_add_seconds(
+ TIME_BETWEEN_DEACT_RETRIES_S,
+ retry_deactivate, gcd->retry_cbd);
+ } else {
+ ofono_error("%s: retry limit hit", __func__);
+
+ if (cb)
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+
+ /*
+ * Reset modem if MTK. TODO Failures deactivating a
+ * context have not been reported for other modems, but
+ * it would be good to have a generic method to force an
+ * internal reset nonetheless.
+ */
+ if (gcd->vendor == OFONO_RIL_VENDOR_MTK)
+ gcd->reset_ev_id = g_idle_add(reset_modem, gcd);
+ }
+ }
+}
+
+static void ril_gprs_context_deactivate_primary(struct ofono_gprs_context *gc,
+ unsigned int id,
+ ofono_gprs_context_cb_t cb, void *data)
+{
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+ struct cb_data *cbd = NULL;
+ struct parcel rilp;
+ struct req_deactivate_data_call request;
+ struct ofono_error error;
+ int ret = 0;
+
+ DBG("*gc: %p cid: %d active_rild_cid: %d", gc, id,
+ gcd->active_rild_cid);
+
+ if (gcd->state == STATE_IDLE || gcd->state == STATE_DISABLING) {
+ /* nothing to do */
+
+ if (cb) {
+ CALLBACK_WITH_SUCCESS(cb, data);
+ g_free(cbd);
+ }
+
+ return;
+ }
+
+ cbd = cb_data_new(cb, data, gc);
+
+ gcd->state = STATE_DISABLING;
+ if (g_ril_unregister(gcd->ril, gcd->call_list_id) == FALSE) {
+ ofono_warn("%s: couldn't remove call_list listener"
+ " for apn: %s.",
+ __func__, gcd->apn);
+ }
+
+ request.cid = gcd->active_rild_cid;
+ request.reason = RIL_DEACTIVATE_DATA_CALL_NO_REASON;
+
+ if (g_ril_request_deactivate_data_call(gcd->ril, &request,
+ &rilp, &error) == FALSE) {
+
+ ofono_error("%s: couldn't build DEACTIVATE_DATA_CALL"
+ " request for apn: %s.",
+ __func__, gcd->apn);
+ goto error;
+ }
+
+ gcd->deact_retries = NUM_DEACTIVATION_RETRIES;
+ ret = g_ril_send(gcd->ril, RIL_REQUEST_DEACTIVATE_DATA_CALL, &rilp,
+ ril_deactivate_data_call_cb, cbd, g_free);
+
+error:
+ if (ret == 0) {
+ /* TODO: should we force state to disconnected here? */
+
+ ofono_error("%s: send DEACTIVATE_DATA_CALL failed for apn: %s",
+ __func__, gcd->apn);
+ g_free(cbd);
+ if (cb)
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static void ril_gprs_context_detach_shutdown(struct ofono_gprs_context *gc,
+ unsigned int id)
+{
+ DBG("*gc: %p cid: %d", gc, id);
+
+ ril_gprs_context_deactivate_primary(gc, 0, NULL, NULL);
+}
+
+static int ril_gprs_context_probe(struct ofono_gprs_context *gc,
+ unsigned int vendor, void *data)
+{
+ struct ril_gprs_context_data *ril_data = data;
+ struct gprs_context_data *gcd;
+
+ DBG("*gc: %p", gc);
+
+ gcd = g_try_new0(struct gprs_context_data, 1);
+ if (gcd == NULL)
+ return -ENOMEM;
+
+ gcd->ril = g_ril_clone(ril_data->gril);
+ gcd->modem = ril_data->modem;
+ gcd->vendor = vendor;
+ set_context_disconnected(gcd);
+ gcd->call_list_id = -1;
+ gcd->type = ril_data->type;
+
+ ofono_gprs_context_set_data(gc, gcd);
+
+ return 0;
+}
+
+static void ril_gprs_context_remove(struct ofono_gprs_context *gc)
+{
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+ DBG("*gc: %p", gc);
+
+ if (gcd->state != STATE_IDLE && gcd->state != STATE_DISABLING) {
+ struct req_deactivate_data_call request;
+ struct parcel rilp;
+ struct ofono_error error;
+
+ request.cid = gcd->active_rild_cid;
+ request.reason = RIL_DEACTIVATE_DATA_CALL_NO_REASON;
+ g_ril_request_deactivate_data_call(gcd->ril, &request,
+ &rilp, &error);
+
+ g_ril_send(gcd->ril, RIL_REQUEST_DEACTIVATE_DATA_CALL,
+ &rilp, NULL, NULL, NULL);
+ }
+
+ if (gcd->retry_ev_id > 0) {
+ g_source_remove(gcd->retry_ev_id);
+ g_free(gcd->retry_cbd);
+ }
+
+ if (gcd->reset_ev_id > 0)
+ g_source_remove(gcd->reset_ev_id);
+
+ ofono_gprs_context_set_data(gc, NULL);
+
+ g_ril_unref(gcd->ril);
+ g_free(gcd);
+}
+
+static struct ofono_gprs_context_driver driver = {
+ .name = RILMODEM,
+ .probe = ril_gprs_context_probe,
+ .remove = ril_gprs_context_remove,
+ .activate_primary = ril_gprs_context_activate_primary,
+ .deactivate_primary = ril_gprs_context_deactivate_primary,
+ .detach_shutdown = ril_gprs_context_detach_shutdown,
+};
+
+void ril_gprs_context_init(void)
+{
+ ofono_gprs_context_driver_register(&driver);
+}
+
+void ril_gprs_context_exit(void)
+{
+ ofono_gprs_context_driver_unregister(&driver);
+}
diff --git a/drivers/rilmodem/gprs.c b/drivers/rilmodem/gprs.c
new file mode 100644
index 00000000..75dcfcc0
--- /dev/null
+++ b/drivers/rilmodem/gprs.c
@@ -0,0 +1,487 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2010 ST-Ericsson AB.
+ * Copyright (C) 2013 Canonical Ltd.
+ * Copyright (C) 2013 Jolla Ltd.
+ *
+ * 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/gprs.h>
+#include <ofono/types.h>
+
+#include "gril.h"
+#include "grilutil.h"
+#include "common.h"
+#include "rilmodem.h"
+
+#include "grilreply.h"
+#include "grilrequest.h"
+#include "grilunsol.h"
+#include "gprs.h"
+
+/* Time between get data status retries */
+#define GET_STATUS_TIMER_MS 5000
+
+/*
+ * This module is the ofono_gprs_driver implementation for rilmodem.
+ *
+ * Notes:
+ *
+ * 1. ofono_gprs_suspend/resume() are not used by this module, as
+ * the concept of suspended GPRS is not exposed by RILD.
+ */
+
+static int ril_tech_to_bearer_tech(int ril_tech)
+{
+ /*
+ * This code handles the mapping between the RIL_RadioTechnology
+ * and packet bearer values ( see <curr_bearer> values - 27.007
+ * Section 7.29 ).
+ */
+
+ switch (ril_tech) {
+ case RADIO_TECH_GSM:
+ case RADIO_TECH_UNKNOWN:
+ return PACKET_BEARER_NONE;
+ case RADIO_TECH_GPRS:
+ return PACKET_BEARER_GPRS;
+ case RADIO_TECH_EDGE:
+ return PACKET_BEARER_EGPRS;
+ case RADIO_TECH_UMTS:
+ return PACKET_BEARER_UMTS;
+ case RADIO_TECH_HSDPA:
+ return PACKET_BEARER_HSDPA;
+ case RADIO_TECH_HSUPA:
+ return PACKET_BEARER_HSUPA;
+ case RADIO_TECH_HSPAP:
+ case RADIO_TECH_HSPA:
+ /*
+ * HSPAP is HSPA+; which ofono doesn't define;
+ * so, if differentiating HSPA and HSPA+ is
+ * important, then ofono needs to be patched,
+ * and we probably also need to introduce a
+ * new indicator icon.
+ */
+ return PACKET_BEARER_HSUPA_HSDPA;
+ case RADIO_TECH_LTE:
+ return PACKET_BEARER_EPS;
+ default:
+ return PACKET_BEARER_NONE;
+ }
+}
+
+static void ril_gprs_state_change(struct ril_msg *message, gpointer user_data)
+{
+ struct ofono_gprs *gprs = user_data;
+ struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
+
+ g_ril_print_unsol_no_args(gd->ril, message);
+
+ /*
+ * We just want to track network data status if ofono
+ * itself is attached, so we avoid unnecessary data state requests.
+ */
+ if (gd->ofono_attached == TRUE)
+ ril_gprs_registration_status(gprs, NULL, NULL);
+}
+
+gboolean ril_gprs_set_attached_cb(gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_gprs_cb_t cb = cbd->cb;
+
+ DBG("");
+
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ g_free(cbd);
+
+ /* Run once per g_idle_add() call */
+ return FALSE;
+}
+
+static void ril_gprs_set_attached(struct ofono_gprs *gprs, int attached,
+ ofono_gprs_cb_t cb, void *data)
+{
+ struct cb_data *cbd = cb_data_new(cb, data, NULL);
+ struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
+
+ DBG("attached: %d", attached);
+
+ /*
+ * As RIL offers no actual control over the GPRS 'attached'
+ * state, we save the desired state, and use it to override
+ * the actual modem's state in the 'attached_status' function.
+ * This is similar to the way the core ofono gprs code handles
+ * data roaming ( see src/gprs.c gprs_netreg_update().
+ *
+ * The core gprs code calls driver->set_attached() when a netreg
+ * notificaiton is received and any configured roaming conditions
+ * are met.
+ */
+ gd->ofono_attached = attached;
+
+ /*
+ * Call from idle loop, so core can set driver_attached before
+ * the callback is invoked.
+ */
+ g_idle_add(ril_gprs_set_attached_cb, cbd);
+}
+
+static gboolean ril_get_status_retry(gpointer user_data)
+{
+ struct ofono_gprs *gprs = user_data;
+ struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
+
+ gd->status_retry_cb_id = 0;
+
+ ril_gprs_registration_status(gprs, NULL, NULL);
+
+ return FALSE;
+}
+
+static void ril_data_reg_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_gprs_status_cb_t cb = cbd->cb;
+ struct ofono_gprs *gprs = cbd->user;
+ struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
+ struct reply_data_reg_state *reply;
+ gboolean attached = FALSE;
+ gboolean notify_status = FALSE;
+ int old_status;
+
+ old_status = gd->rild_status;
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("%s: DATA_REGISTRATION_STATE reply failure: %s",
+ __func__,
+ ril_error_to_string(message->error));
+ goto error;
+ }
+
+ reply = g_ril_reply_parse_data_reg_state(gd->ril, message);
+ if (reply == NULL)
+ goto error;
+
+ /*
+ * There are three cases that can result in this callback
+ * running:
+ *
+ * 1) The driver's probe() method was called, and thus an
+ * internal call to ril_gprs_registration_status() is
+ * generated. No ofono cb exists.
+ *
+ * 2) ril_gprs_state_change() is called due to an unsolicited
+ * event from RILD. No ofono cb exists.
+ *
+ * 3) The ofono code code calls the driver's attached_status()
+ * function. A valid ofono cb exists.
+ */
+
+ if (gd->rild_status != reply->reg_state.status) {
+ gd->rild_status = reply->reg_state.status;
+
+ if (cb == NULL)
+ notify_status = TRUE;
+ }
+
+ /*
+ * Override the actual status based upon the desired
+ * attached status set by the core GPRS code ( controlled
+ * by the ConnnectionManager's 'Powered' property ).
+ */
+ attached = (reply->reg_state.status ==
+ NETWORK_REGISTRATION_STATUS_REGISTERED ||
+ reply->reg_state.status ==
+ NETWORK_REGISTRATION_STATUS_ROAMING);
+
+ if (attached && gd->ofono_attached == FALSE) {
+ DBG("attached=true; ofono_attached=false; return !REGISTERED");
+ reply->reg_state.status =
+ NETWORK_REGISTRATION_STATUS_NOT_REGISTERED;
+
+ /*
+ * Further optimization so that if ril_status ==
+ * NOT_REGISTERED, ofono_attached == false, and status ==
+ * ROAMING | REGISTERED, then notify gets cleared...
+ *
+ * As is, this results in unecessary status notify calls
+ * when nothing has changed.
+ */
+ if (notify_status && reply->reg_state.status == old_status)
+ notify_status = FALSE;
+ }
+
+ if (old_status == -1) {
+ ofono_gprs_register(gprs);
+
+ /* Different rild implementations use different events here */
+ g_ril_register(gd->ril,
+ gd->state_changed_unsol,
+ ril_gprs_state_change, gprs);
+
+ if (reply->max_cids == 0)
+ gd->max_cids = RIL_MAX_NUM_ACTIVE_DATA_CALLS;
+ else if (reply->max_cids < RIL_MAX_NUM_ACTIVE_DATA_CALLS)
+ gd->max_cids = reply->max_cids;
+ else
+ gd->max_cids = RIL_MAX_NUM_ACTIVE_DATA_CALLS;
+
+ DBG("Setting max cids to %d", gd->max_cids);
+ ofono_gprs_set_cid_range(gprs, 1, gd->max_cids);
+
+ /*
+ * This callback is a result of the inital call
+ * to probe(), so should return after registration.
+ */
+ g_free(reply);
+
+ return;
+ }
+
+ /* Just need to notify ofono if it's already attached */
+ if (notify_status) {
+
+ /*
+ * If network disconnect has occurred, call detached_notify()
+ * instead of status_notify().
+ */
+ if (!attached &&
+ (old_status == NETWORK_REGISTRATION_STATUS_REGISTERED ||
+ old_status ==
+ NETWORK_REGISTRATION_STATUS_ROAMING)) {
+ DBG("calling ofono_gprs_detached_notify()");
+ ofono_gprs_detached_notify(gprs);
+ reply->reg_state.tech = RADIO_TECH_UNKNOWN;
+ } else {
+ DBG("calling ofono_gprs_status_notify()");
+ ofono_gprs_status_notify(gprs, reply->reg_state.status);
+ }
+ }
+
+ if (gd->tech != reply->reg_state.tech) {
+ gd->tech = reply->reg_state.tech;
+
+ ofono_gprs_bearer_notify(gprs,
+ ril_tech_to_bearer_tech(reply->reg_state.tech));
+ }
+
+ if (cb)
+ CALLBACK_WITH_SUCCESS(cb, reply->reg_state.status, cbd->data);
+
+ g_free(reply);
+
+ return;
+error:
+
+ /*
+ * For some modems DATA_REGISTRATION_STATE will return an error until we
+ * are registered in the voice network.
+ */
+ if (old_status == -1 && message->error == RIL_E_GENERIC_FAILURE)
+ gd->status_retry_cb_id =
+ g_timeout_add(GET_STATUS_TIMER_MS,
+ ril_get_status_retry, gprs);
+
+ if (cb)
+ CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+void ril_gprs_registration_status(struct ofono_gprs *gprs,
+ ofono_gprs_status_cb_t cb, void *data)
+{
+ struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
+ struct cb_data *cbd = cb_data_new(cb, data, gprs);
+
+ DBG("");
+
+ if (g_ril_send(gd->ril, RIL_REQUEST_DATA_REGISTRATION_STATE, NULL,
+ ril_data_reg_cb, cbd, g_free) == 0) {
+ ofono_error("%s: send "
+ "RIL_REQUEST_DATA_REGISTRATION_STATE failed",
+ __func__);
+ g_free(cbd);
+
+ if (cb != NULL)
+ CALLBACK_WITH_FAILURE(cb, -1, data);
+ }
+}
+
+static void drop_data_call_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct ofono_gprs *gprs = user_data;
+ struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
+
+ if (message->error == RIL_E_SUCCESS)
+ g_ril_print_response_no_args(gd->ril, message);
+ else
+ ofono_error("%s: RIL error %s", __func__,
+ ril_error_to_string(message->error));
+
+ if (--(gd->pending_deact_req) == 0)
+ ril_gprs_registration_status(gprs, NULL, NULL);
+}
+
+static int drop_data_call(struct ofono_gprs *gprs, int cid)
+{
+ struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
+ struct req_deactivate_data_call request;
+ struct parcel rilp;
+ struct ofono_error error;
+
+ request.cid = cid;
+ request.reason = RIL_DEACTIVATE_DATA_CALL_NO_REASON;
+
+ g_ril_request_deactivate_data_call(gd->ril, &request, &rilp, &error);
+
+ if (g_ril_send(gd->ril, RIL_REQUEST_DEACTIVATE_DATA_CALL,
+ &rilp, drop_data_call_cb, gprs, NULL) == 0) {
+ ofono_error("%s: send failed", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void get_active_data_calls_cb(struct ril_msg *message,
+ gpointer user_data)
+{
+ struct ofono_gprs *gprs = user_data;
+ struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
+ struct ril_data_call_list *call_list = NULL;
+ GSList *iterator;
+ struct ril_data_call *call;
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("%s: RIL error %s", __func__,
+ ril_error_to_string(message->error));
+ goto end;
+ }
+
+ /* reply can be NULL when there are no existing data calls */
+ call_list = g_ril_unsol_parse_data_call_list(gd->ril, message);
+ if (call_list == NULL)
+ goto end;
+
+ /*
+ * We disconnect from previous calls here, which might be needed
+ * because of a previous ofono abort, as some rild implementations do
+ * not disconnect the calls even after the ril socket is closed.
+ */
+ for (iterator = call_list->calls; iterator; iterator = iterator->next) {
+ call = iterator->data;
+ DBG("Standing data call with cid %d", call->cid);
+ if (drop_data_call(gprs, call->cid) == 0)
+ ++(gd->pending_deact_req);
+ }
+
+ g_ril_unsol_free_data_call_list(call_list);
+
+end:
+ if (gd->pending_deact_req == 0)
+ ril_gprs_registration_status(gprs, NULL, NULL);
+}
+
+static void get_active_data_calls(struct ofono_gprs *gprs)
+{
+ struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
+
+ if (g_ril_send(gd->ril, RIL_REQUEST_DATA_CALL_LIST, NULL,
+ get_active_data_calls_cb, gprs, NULL) == 0)
+ ofono_error("%s: send failed", __func__);
+}
+
+void ril_gprs_start(struct ril_gprs_driver_data *driver_data,
+ struct ofono_gprs *gprs, struct ril_gprs_data *gd)
+{
+ gd->ril = g_ril_clone(driver_data->gril);
+ gd->modem = driver_data->modem;
+ gd->ofono_attached = FALSE;
+ gd->max_cids = 0;
+ gd->rild_status = -1;
+ gd->tech = RADIO_TECH_UNKNOWN;
+ /* AOSP RILD tracks data network state together with voice */
+ gd->state_changed_unsol =
+ RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED;
+
+ ofono_gprs_set_data(gprs, gd);
+
+ get_active_data_calls(gprs);
+}
+
+int ril_gprs_probe(struct ofono_gprs *gprs, unsigned int vendor, void *data)
+{
+ struct ril_gprs_driver_data *driver_data = data;
+ struct ril_gprs_data *gd;
+
+ gd = g_try_new0(struct ril_gprs_data, 1);
+ if (gd == NULL)
+ return -ENOMEM;
+
+ ril_gprs_start(driver_data, gprs, gd);
+
+ return 0;
+}
+
+void ril_gprs_remove(struct ofono_gprs *gprs)
+{
+ struct ril_gprs_data *gd = ofono_gprs_get_data(gprs);
+
+ DBG("");
+
+ if (gd->status_retry_cb_id != 0)
+ g_source_remove(gd->status_retry_cb_id);
+
+ ofono_gprs_set_data(gprs, NULL);
+
+ g_ril_unref(gd->ril);
+ g_free(gd);
+}
+
+static struct ofono_gprs_driver driver = {
+ .name = RILMODEM,
+ .probe = ril_gprs_probe,
+ .remove = ril_gprs_remove,
+ .set_attached = ril_gprs_set_attached,
+ .attached_status = ril_gprs_registration_status,
+};
+
+void ril_gprs_init(void)
+{
+ ofono_gprs_driver_register(&driver);
+}
+
+void ril_gprs_exit(void)
+{
+ ofono_gprs_driver_unregister(&driver);
+}
diff --git a/drivers/rilmodem/gprs.h b/drivers/rilmodem/gprs.h
new file mode 100644
index 00000000..78bb14cd
--- /dev/null
+++ b/drivers/rilmodem/gprs.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2014 Canonical Ltd.
+ *
+ * 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
+ *
+ */
+
+#include "drivers/rilmodem/rilutil.h"
+
+struct ril_gprs_data {
+ GRil *ril;
+ struct ofono_modem *modem;
+ gboolean ofono_attached;
+ unsigned int max_cids;
+ int rild_status;
+ int tech;
+ int state_changed_unsol;
+ int pending_deact_req;
+ guint status_retry_cb_id;
+};
+
+int ril_gprs_probe(struct ofono_gprs *gprs, unsigned int vendor, void *data);
+void ril_gprs_remove(struct ofono_gprs *gprs);
+void ril_gprs_start(struct ril_gprs_driver_data *driver_data,
+ struct ofono_gprs *gprs, struct ril_gprs_data *gd);
+gboolean ril_gprs_set_attached_cb(gpointer user_data);
+void ril_gprs_registration_status(struct ofono_gprs *gprs,
+ ofono_gprs_status_cb_t cb, void *data);
+void ril_gprs_set_ia_apn(struct ofono_gprs *gprs, const char *apn,
+ enum ofono_gprs_proto proto, const char *user,
+ const char *passwd, const char *mccmnc,
+ ofono_gprs_cb_t cb, void *data);
diff --git a/drivers/rilmodem/network-registration.c b/drivers/rilmodem/network-registration.c
new file mode 100644
index 00000000..6808a293
--- /dev/null
+++ b/drivers/rilmodem/network-registration.c
@@ -0,0 +1,566 @@
+/*
+ *
+ * oFono - Open Source Telephony - RIL Modem Support
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2010 ST-Ericsson AB.
+ * Copyright (C) 2012-2013 Canonical Ltd.
+ * Copyright (C) 2013 Jolla Ltd.
+ *
+ * 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 <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/netreg.h>
+
+#include "common.h"
+#include "gril.h"
+#include "rilmodem.h"
+
+#include "grilreply.h"
+#include "grilrequest.h"
+#include "grilunsol.h"
+
+struct netreg_data {
+ GRil *ril;
+ char mcc[OFONO_MAX_MCC_LENGTH + 1];
+ char mnc[OFONO_MAX_MNC_LENGTH + 1];
+ 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 */
+ int signal_invalid; /* invalid strength reported via CIND */
+ int tech;
+ struct ofono_network_time time;
+ guint nitz_timeout;
+ unsigned int vendor;
+};
+
+static void ril_registration_status(struct ofono_netreg *netreg,
+ ofono_netreg_status_cb_t cb,
+ void *data);
+
+static int ril_tech_to_access_tech(int ril_tech)
+{
+ /*
+ * This code handles the mapping between the RIL_RadioTechnology
+ * and ofono's access technology values ( see <Act> values - 27.007
+ * Section 7.3 ).
+ */
+
+ switch (ril_tech) {
+ case RADIO_TECH_UNKNOWN:
+ return -1;
+ case RADIO_TECH_GSM:
+ case RADIO_TECH_GPRS:
+ return ACCESS_TECHNOLOGY_GSM;
+ case RADIO_TECH_EDGE:
+ return ACCESS_TECHNOLOGY_GSM_EGPRS;
+ case RADIO_TECH_UMTS:
+ return ACCESS_TECHNOLOGY_UTRAN;
+ case RADIO_TECH_HSDPA:
+ return ACCESS_TECHNOLOGY_UTRAN_HSDPA;
+ case RADIO_TECH_HSUPA:
+ return ACCESS_TECHNOLOGY_UTRAN_HSUPA;
+ case RADIO_TECH_HSPAP:
+ case RADIO_TECH_HSPA:
+ /* HSPAP is HSPA+; which ofono doesn't define;
+ * so, if differentiating HSPA and HSPA+ is
+ * important, then ofono needs to be patched,
+ * and we probably also need to introduce a
+ * new indicator icon.
+ */
+
+ return ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA;
+ case RADIO_TECH_LTE:
+ return ACCESS_TECHNOLOGY_EUTRAN;
+ default:
+ return -1;
+ }
+}
+
+static void extract_mcc_mnc(const char *str, char *mcc, char *mnc)
+{
+ /* Three digit country code */
+ strncpy(mcc, str, OFONO_MAX_MCC_LENGTH);
+ mcc[OFONO_MAX_MCC_LENGTH] = '\0';
+
+ /* Usually a 2 but sometimes 3 digit network code */
+ strncpy(mnc, str + OFONO_MAX_MCC_LENGTH, OFONO_MAX_MNC_LENGTH);
+ mnc[OFONO_MAX_MNC_LENGTH] = '\0';
+}
+
+static void ril_creg_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_netreg_status_cb_t cb = cbd->cb;
+ struct netreg_data *nd = cbd->user;
+ struct reply_reg_state *reply;
+
+ DBG("");
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("%s: failed to pull registration state",
+ __func__);
+ goto error;
+ }
+
+ reply = g_ril_reply_parse_voice_reg_state(nd->ril, message);
+ if (reply == NULL)
+ goto error;
+
+ nd->tech = reply->tech;
+
+ CALLBACK_WITH_SUCCESS(cb,
+ reply->status,
+ reply->lac,
+ reply->ci,
+ ril_tech_to_access_tech(reply->tech),
+ cbd->data);
+
+ g_free(reply);
+ return;
+
+error:
+ CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, cbd->data);
+}
+
+static void ril_creg_notify(struct ofono_error *error, int status, int lac,
+ int ci, int tech, gpointer user_data)
+{
+ struct ofono_netreg *netreg = user_data;
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ DBG("Error during status notification");
+ return;
+ }
+
+ ofono_netreg_status_notify(netreg, status, lac, ci, tech);
+}
+
+static void ril_network_state_change(struct ril_msg *message,
+ gpointer user_data)
+{
+ struct ofono_netreg *netreg = user_data;
+ struct netreg_data *nd = ofono_netreg_get_data(netreg);
+
+ g_ril_print_unsol_no_args(nd->ril, message);
+
+ ril_registration_status(netreg, NULL, NULL);
+}
+
+static void ril_registration_status(struct ofono_netreg *netreg,
+ ofono_netreg_status_cb_t cb,
+ void *data)
+{
+ struct netreg_data *nd = ofono_netreg_get_data(netreg);
+ struct cb_data *cbd;
+
+ /*
+ * If no cb specified, setup internal callback to
+ * handle unsolicited VOICE_NET_STATE_CHANGE events.
+ */
+ if (cb == NULL)
+ cbd = cb_data_new(ril_creg_notify, netreg, nd);
+ else
+ cbd = cb_data_new(cb, data, nd);
+
+ if (g_ril_send(nd->ril, RIL_REQUEST_VOICE_REGISTRATION_STATE, NULL,
+ ril_creg_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, -1, -1, -1, -1, data);
+ }
+}
+
+static void set_oper_name(const struct reply_operator *reply,
+ struct ofono_network_operator *op)
+{
+ /* Try to use long by default */
+ if (reply->lalpha)
+ strncpy(op->name, reply->lalpha,
+ OFONO_MAX_OPERATOR_NAME_LENGTH);
+ else if (reply->salpha)
+ strncpy(op->name, reply->salpha,
+ OFONO_MAX_OPERATOR_NAME_LENGTH);
+}
+
+static void ril_cops_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_netreg_operator_cb_t cb = cbd->cb;
+ struct netreg_data *nd = cbd->user;
+ struct reply_operator *reply;
+ struct ofono_network_operator op;
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("%s: failed to retrive the current operator",
+ __func__);
+ goto error;
+ }
+
+ reply = g_ril_reply_parse_operator(nd->ril, message);
+ if (reply == NULL)
+ goto error;
+
+ set_oper_name(reply, &op);
+
+ extract_mcc_mnc(reply->numeric, op.mcc, op.mnc);
+
+ /* Set to current */
+ op.status = OPERATOR_STATUS_CURRENT;
+ op.tech = ril_tech_to_access_tech(nd->tech);
+
+ CALLBACK_WITH_SUCCESS(cb, &op, cbd->data);
+
+ g_ril_reply_free_operator(reply);
+
+ return;
+
+error:
+ CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static void ril_current_operator(struct ofono_netreg *netreg,
+ ofono_netreg_operator_cb_t cb, void *data)
+{
+ struct netreg_data *nd = ofono_netreg_get_data(netreg);
+ struct cb_data *cbd = cb_data_new(cb, data, nd);
+
+ if (g_ril_send(nd->ril, RIL_REQUEST_OPERATOR, NULL,
+ ril_cops_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, NULL, data);
+ }
+}
+
+static void ril_cops_list_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_netreg_operator_list_cb_t cb = cbd->cb;
+ struct netreg_data *nd = cbd->user;
+ struct reply_avail_ops *reply = NULL;
+ struct ofono_network_operator *ops;
+ struct reply_operator *operator;
+ GSList *l;
+ unsigned int i = 0;
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("%s: failed to retrive the list of operators",
+ __func__);
+ goto error;
+ }
+
+ reply = g_ril_reply_parse_avail_ops(nd->ril, message);
+ if (reply == NULL)
+ goto error;
+
+ ops = g_try_new0(struct ofono_network_operator, reply->num_ops);
+ if (ops == NULL) {
+ ofono_error("%s: can't allocate ofono_network_operator",
+ __func__);
+
+ goto error;
+ }
+
+ for (l = reply->list; l; l = l->next) {
+ operator = l->data;
+
+ set_oper_name(operator, &ops[i]);
+
+ extract_mcc_mnc(operator->numeric, ops[i].mcc, ops[i].mnc);
+
+ ops[i].tech = ril_tech_to_access_tech(operator->tech);
+
+ /* Set the proper status */
+ if (!strcmp(operator->status, "unknown"))
+ ops[i].status = OPERATOR_STATUS_UNKNOWN;
+ else if (!strcmp(operator->status, "available"))
+ ops[i].status = OPERATOR_STATUS_AVAILABLE;
+ else if (!strcmp(operator->status, "current"))
+ ops[i].status = OPERATOR_STATUS_CURRENT;
+ else if (!strcmp(operator->status, "forbidden"))
+ ops[i].status = OPERATOR_STATUS_FORBIDDEN;
+
+ i++;
+ }
+
+ CALLBACK_WITH_SUCCESS(cb, reply->num_ops, ops, cbd->data);
+ g_ril_reply_free_avail_ops(reply);
+
+ return;
+
+error:
+ CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data);
+ g_ril_reply_free_avail_ops(reply);
+}
+
+static void ril_list_operators(struct ofono_netreg *netreg,
+ ofono_netreg_operator_list_cb_t cb, void *data)
+{
+ struct netreg_data *nd = ofono_netreg_get_data(netreg);
+ struct cb_data *cbd = cb_data_new(cb, data, nd);
+
+ if (g_ril_send(nd->ril, RIL_REQUEST_QUERY_AVAILABLE_NETWORKS, NULL,
+ ril_cops_list_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, 0, NULL, data);
+ }
+}
+
+static void ril_register_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_netreg_register_cb_t cb = cbd->cb;
+ struct netreg_data *nd = cbd->user;
+ struct ofono_error error;
+
+ if (message->error == RIL_E_SUCCESS) {
+ decode_ril_error(&error, "OK");
+
+ g_ril_print_response_no_args(nd->ril, message);
+
+ } else {
+ decode_ril_error(&error, "FAIL");
+ }
+
+ cb(&error, cbd->data);
+}
+
+static void ril_register_auto(struct ofono_netreg *netreg,
+ ofono_netreg_register_cb_t cb, void *data)
+{
+ struct netreg_data *nd = ofono_netreg_get_data(netreg);
+ struct cb_data *cbd = cb_data_new(cb, data, nd);
+
+ if (g_ril_send(nd->ril, RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC,
+ NULL, ril_register_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static void ril_register_manual(struct ofono_netreg *netreg,
+ const char *mcc, const char *mnc,
+ ofono_netreg_register_cb_t cb, void *data)
+{
+ struct netreg_data *nd = ofono_netreg_get_data(netreg);
+ struct cb_data *cbd = cb_data_new(cb, data, nd);
+ char buf[OFONO_MAX_MCC_LENGTH + OFONO_MAX_MNC_LENGTH + 1];
+ struct parcel rilp;
+
+ /* RIL expects a char * specifying MCCMNC of network to select */
+ snprintf(buf, sizeof(buf), "%s%s", mcc, mnc);
+
+ g_ril_request_set_net_select_manual(nd->ril, buf, &rilp);
+
+ /* In case of error free cbd and return the cb with failure */
+ if (g_ril_send(nd->ril, RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL, &rilp,
+ ril_register_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static void ril_strength_notify(struct ril_msg *message, gpointer user_data)
+{
+ struct ofono_netreg *netreg = user_data;
+ struct netreg_data *nd = ofono_netreg_get_data(netreg);
+ int strength = g_ril_unsol_parse_signal_strength(nd->ril, message,
+ nd->tech);
+
+ ofono_netreg_strength_notify(netreg, strength);
+}
+
+static void ril_strength_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_netreg_strength_cb_t cb = cbd->cb;
+ struct netreg_data *nd = cbd->user;
+ struct ofono_error error;
+ int strength;
+
+ if (message->error == RIL_E_SUCCESS) {
+ decode_ril_error(&error, "OK");
+ } else {
+ ofono_error("Failed to retrive the signal strength");
+ goto error;
+ }
+
+ /* The g_ril_unsol* function handles both reply & unsolicited */
+ strength = g_ril_unsol_parse_signal_strength(nd->ril, message,
+ nd->tech);
+ cb(&error, strength, cbd->data);
+
+ return;
+
+error:
+ CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void ril_signal_strength(struct ofono_netreg *netreg,
+ ofono_netreg_strength_cb_t cb, void *data)
+{
+ struct netreg_data *nd = ofono_netreg_get_data(netreg);
+ struct cb_data *cbd = cb_data_new(cb, data, nd);
+
+ if (g_ril_send(nd->ril, RIL_REQUEST_SIGNAL_STRENGTH, NULL,
+ ril_strength_cb, cbd, g_free) == 0) {
+ ofono_error("Send RIL_REQUEST_SIGNAL_STRENGTH failed.");
+
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, -1, data);
+ }
+}
+
+static void ril_nitz_notify(struct ril_msg *message, gpointer user_data)
+{
+ struct ofono_netreg *netreg = user_data;
+ struct netreg_data *nd = ofono_netreg_get_data(netreg);
+ int year, mon, mday, hour, min, sec, dst, tzi, n_match;
+ char tzs, tz[4];
+ gchar *nitz;
+
+ nitz = g_ril_unsol_parse_nitz(nd->ril, message);
+ if (nitz == NULL)
+ goto error;
+
+ n_match = sscanf(nitz, "%u/%u/%u,%u:%u:%u%c%u,%u", &year, &mon,
+ &mday, &hour, &min, &sec, &tzs, &tzi, &dst);
+ if (n_match != 9)
+ goto error;
+
+ sprintf(tz, "%c%d", tzs, tzi);
+
+ nd->time.utcoff = atoi(tz) * 15 * 60;
+ nd->time.dst = dst;
+ nd->time.sec = sec;
+ nd->time.min = min;
+ nd->time.hour = hour;
+ nd->time.mday = mday;
+ nd->time.mon = mon;
+ nd->time.year = 2000 + year;
+
+ ofono_netreg_time_notify(netreg, &nd->time);
+
+ g_free(nitz);
+
+ return;
+
+error:
+ ofono_error("%s: unable to notify ofono about NITZ (%s)",
+ __func__, nitz ? nitz : "null");
+ g_free(nitz);
+}
+
+static gboolean ril_delayed_register(gpointer user_data)
+{
+ struct ofono_netreg *netreg = user_data;
+ struct netreg_data *nd = ofono_netreg_get_data(netreg);
+ ofono_netreg_register(netreg);
+
+ /* Register for network state changes */
+ g_ril_register(nd->ril, RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED,
+ ril_network_state_change, netreg);
+
+ /* Register for network time update reports */
+ g_ril_register(nd->ril, RIL_UNSOL_NITZ_TIME_RECEIVED,
+ ril_nitz_notify, netreg);
+
+ /* Register for signal strength changes */
+ g_ril_register(nd->ril, RIL_UNSOL_SIGNAL_STRENGTH,
+ ril_strength_notify, netreg);
+
+ /* This makes the timeout a single-shot */
+ return FALSE;
+}
+
+static int ril_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor,
+ void *data)
+{
+ GRil *ril = data;
+ struct netreg_data *nd;
+
+ nd = g_new0(struct netreg_data, 1);
+
+ nd->ril = g_ril_clone(ril);
+ nd->vendor = vendor;
+ nd->tech = RADIO_TECH_UNKNOWN;
+ nd->time.sec = -1;
+ nd->time.min = -1;
+ nd->time.hour = -1;
+ nd->time.mday = -1;
+ nd->time.mon = -1;
+ nd->time.year = -1;
+ nd->time.dst = 0;
+ nd->time.utcoff = 0;
+ ofono_netreg_set_data(netreg, nd);
+
+ /*
+ * ofono_netreg_register() needs to be called after
+ * the driver has been set in ofono_netreg_create(),
+ * which calls this function. Most other drivers make
+ * some kind of capabilities query to the modem, and then
+ * call register in the callback; we use the idle loop here.
+ */
+ g_idle_add(ril_delayed_register, netreg);
+
+ return 0;
+}
+
+static void ril_netreg_remove(struct ofono_netreg *netreg)
+{
+ struct netreg_data *nd = ofono_netreg_get_data(netreg);
+
+ if (nd->nitz_timeout)
+ g_source_remove(nd->nitz_timeout);
+
+ ofono_netreg_set_data(netreg, NULL);
+
+ g_ril_unref(nd->ril);
+ g_free(nd);
+}
+
+static struct ofono_netreg_driver driver = {
+ .name = RILMODEM,
+ .probe = ril_netreg_probe,
+ .remove = ril_netreg_remove,
+ .registration_status = ril_registration_status,
+ .current_operator = ril_current_operator,
+ .list_operators = ril_list_operators,
+ .register_auto = ril_register_auto,
+ .register_manual = ril_register_manual,
+ .strength = ril_signal_strength,
+};
+
+void ril_netreg_init(void)
+{
+ ofono_netreg_driver_register(&driver);
+}
+
+void ril_netreg_exit(void)
+{
+ ofono_netreg_driver_unregister(&driver);
+}
diff --git a/drivers/rilmodem/phonebook.c b/drivers/rilmodem/phonebook.c
new file mode 100644
index 00000000..c3f1c0b7
--- /dev/null
+++ b/drivers/rilmodem/phonebook.c
@@ -0,0 +1,1055 @@
+/*
+ *
+ * oFono - Open Source Telephony - RIL Modem Support
+ *
+ * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) ST-Ericsson SA 2010.
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2013 Jolla Ltd
+ * Contact: Jussi Kangas <jussi.kangas@tieto.com>
+ * Copyright (C) 2014 Canonical Ltd
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include <glib.h>
+
+#include <ofono.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/phonebook.h>
+#include <sim.h>
+#include <simfs.h>
+#include <util.h>
+
+#include "gril.h"
+#include "simutil.h"
+#include "common.h"
+
+#include "rilmodem.h"
+
+#define UNUSED 0xFF
+
+#define EXT1_CP_SUBADDRESS 1
+#define EXT1_ADDITIONAL_DATA 2
+
+/* TON (Type Of Number) See TS 24.008 */
+#define TON_MASK 0x70
+#define TON_INTERNATIONAL 0x10
+
+enum constructed_tag {
+ TYPE_1_TAG = 0xA8,
+ TYPE_2_TAG = 0xA9,
+ TYPE_3_TAG = 0xAA
+};
+
+enum file_type_tag {
+ TYPE_ADN = 0xC0,
+ TYPE_IAP = 0xC1,
+ TYPE_EXT1 = 0xC2,
+ TYPE_SNE = 0xC3,
+ TYPE_ANR = 0xC4,
+ TYPE_PBC = 0xC5,
+ TYPE_GPR = 0xC6,
+ TYPE_AAS = 0xC7,
+ TYPE_GAS = 0xC8,
+ TYPE_UID = 0xC9,
+ TYPE_EMAIL = 0xCA,
+ TYPE_CCP1 = 0xCB
+};
+
+struct pb_file_info {
+ enum constructed_tag pbr_type;
+ int file_id;
+ enum file_type_tag file_type;
+ int file_length;
+ int record_length;
+};
+
+struct record_to_read {
+ int file_id;
+ enum file_type_tag type_tag;
+ int record_length;
+ int record;
+ int adn_idx;
+ gboolean anr_ext; /* Is it an EXT1 record for ANR? */
+ gboolean set_by_iap; /* Type 2 file? */
+};
+
+struct phonebook_entry {
+ int entry;
+ char *name;
+ char *number;
+ char *email;
+ char *anr;
+ char *sne;
+};
+
+unsigned char sim_path[] = { 0x3F, 0x00, 0x7F, 0x10 };
+unsigned char usim_path[] = { 0x3F, 0x00, 0x7F, 0x10, 0x5F, 0x3A };
+
+/*
+ * Table for BCD to utf8 conversion. See table 4.4 in TS 31.102.
+ * BCD 0x0C indicates pause before sending following digits as DTMF tones.
+ * BCD 0x0D is a wildcard that means "any digit". These values are mapped to
+ * ',' and '?', following the Android/iPhone convention for the first and Nokia
+ * convention for the second (only OEM that I have seen that supports this
+ * feature). BCD 0x0E is reserved, we convert it to 'r'.
+ */
+static const char digit_to_utf8[] = "0123456789*#,?r\0";
+
+/* One of these for each record in EF_PBR */
+struct pb_ref_rec {
+ GSList *pb_files; /* File ids to read (pb_file_info nodes) */
+ GSList *pb_next; /* Next file info to read */
+ GSList *pending_records; /* List of record_to_read */
+ GSList *next_record; /* Next record_to_read to process */
+ GTree *phonebook; /* Container of phonebook_entry structures */
+};
+
+struct pb_data {
+ GSList *pb_refs;
+ GSList *pb_ref_next;
+ struct ofono_sim *sim;
+ struct ofono_sim_context *sim_context;
+ const unsigned char *df_path;
+ size_t df_size;
+};
+
+static void read_info_cb(int ok, unsigned char file_status,
+ int total_length, int record_length,
+ void *userdata);
+
+static gint comp_int(gconstpointer a, gconstpointer b)
+{
+ int a_val = GPOINTER_TO_INT(a);
+ int b_val = GPOINTER_TO_INT(b);
+
+ return a_val - b_val;
+}
+
+static const struct pb_file_info *
+ext1_info(const GSList *pb_files)
+{
+ const GSList *l;
+ for (l = pb_files; l; l = l->next) {
+ const struct pb_file_info *f_info = l->data;
+ if (f_info->file_type == TYPE_EXT1)
+ return f_info;
+ }
+
+ return NULL;
+}
+
+static struct phonebook_entry *handle_adn(size_t len, const unsigned char *msg,
+ struct pb_ref_rec *ref, int adn_idx)
+{
+ unsigned name_length = len - 14;
+ unsigned number_start = name_length;
+ unsigned number_length;
+ unsigned extension_record = UNUSED;
+ unsigned i, prefix;
+ char *number = NULL;
+ char *name = sim_string_to_utf8(msg, name_length);
+ struct phonebook_entry *new_entry;
+
+ /* Length contains also TON & NPI */
+ number_length = msg[number_start];
+
+ if (number_length != UNUSED && number_length != 0) {
+ number_length--;
+ /* '+' + number + terminator */
+ number = g_try_malloc0(2 * number_length + 2);
+
+ if (number) {
+ prefix = 0;
+
+ if ((msg[number_start + 1] & TON_MASK)
+ == TON_INTERNATIONAL) {
+ number[0] = '+';
+ prefix = 1;
+ }
+
+ for (i = 0; i < number_length; i++) {
+
+ number[2 * i + prefix] =
+ digit_to_utf8[msg[number_start + 2 + i]
+ & 0x0f];
+ number[2 * i + 1 + prefix] =
+ digit_to_utf8[msg[number_start + 2 + i]
+ >> 4];
+ }
+
+ extension_record = msg[len - 1];
+ }
+ }
+
+ DBG("ADN name %s, number %s ", name, number);
+ DBG("number length %d extension_record %d",
+ 2 * number_length, extension_record);
+
+ if ((name == NULL || *name == '\0') && number == NULL)
+ goto end;
+
+ new_entry = g_try_malloc0(sizeof(*new_entry));
+ if (new_entry == NULL) {
+ ofono_error("%s: out of memory", __func__);
+ goto end;
+ }
+
+ new_entry->name = name;
+ new_entry->number = number;
+
+ DBG("Creating PB entry %d with", adn_idx);
+ DBG("name %s and number %s", new_entry->name, new_entry->number);
+
+ g_tree_insert(ref->phonebook, GINT_TO_POINTER(adn_idx), new_entry);
+
+ if (extension_record != UNUSED) {
+ struct record_to_read *ext_rec =
+ g_try_malloc0(sizeof(*ext_rec));
+ const struct pb_file_info *f_info = ext1_info(ref->pb_files);
+
+ if (ext_rec && f_info) {
+ ext_rec->file_id = f_info->file_id;
+ ext_rec->type_tag = TYPE_EXT1;
+ ext_rec->record_length = f_info->record_length;
+ ext_rec->record = extension_record;
+ ext_rec->adn_idx = adn_idx;
+
+ ref->pending_records =
+ g_slist_prepend(ref->pending_records, ext_rec);
+ }
+ }
+
+ return new_entry;
+
+end:
+ g_free(name);
+ g_free(number);
+
+ return NULL;
+}
+
+static void handle_iap(size_t len, const unsigned char *msg,
+ struct pb_ref_rec *ref,
+ const struct record_to_read *rec_data)
+{
+ GSList *l;
+ size_t i = 0;
+
+ for (l = ref->pb_files; l; l = l->next) {
+ struct pb_file_info *f_info = l->data;
+ if (f_info->pbr_type == TYPE_2_TAG) {
+ if (i >= len) {
+ ofono_error("%s: EF_IAP record too small",
+ __func__);
+ return;
+ }
+ if (msg[i] != UNUSED) {
+ struct record_to_read *new_rec =
+ g_try_malloc0(sizeof(*new_rec));
+ if (new_rec == NULL) {
+ ofono_error("%s: OOM", __func__);
+ return;
+ }
+ DBG("type 0x%X record %d",
+ f_info->file_type, msg[i]);
+
+ new_rec->file_id = f_info->file_id;
+ new_rec->type_tag = f_info->file_type;
+ new_rec->record_length = f_info->record_length;
+ new_rec->record = msg[i];
+ new_rec->adn_idx = rec_data->adn_idx;
+ new_rec->anr_ext = FALSE;
+ new_rec->set_by_iap = TRUE;
+
+ ref->pending_records =
+ g_slist_prepend(ref->pending_records,
+ new_rec);
+ }
+ ++i;
+ }
+ }
+}
+
+static void handle_sne(size_t len, const unsigned char *msg,
+ struct pb_ref_rec *ref,
+ const struct record_to_read *rec_data)
+{
+ char *sne;
+
+ /* There are additional fields for type 2 files */
+ if (rec_data->set_by_iap)
+ len -= 2;
+
+ sne = sim_string_to_utf8(msg, len);
+
+ if (sne && *sne != '\0') {
+ struct phonebook_entry *entry;
+
+ entry = g_tree_lookup(ref->phonebook,
+ GINT_TO_POINTER(rec_data->adn_idx));
+ if (entry) {
+ /* If one already exists, delete it */
+ if (entry->sne)
+ g_free(entry->sne);
+
+ DBG("Adding SNE %s to %d", sne, rec_data->adn_idx);
+ DBG("name %s", entry->name);
+
+ entry->sne = sne;
+ } else {
+ g_free(sne);
+ }
+ } else {
+ g_free(sne);
+ }
+}
+
+static void handle_anr(size_t len,
+ const unsigned char *msg,
+ struct pb_ref_rec *ref,
+ const struct record_to_read *rec_data)
+{
+ unsigned number_length;
+ unsigned extension_record;
+ unsigned aas_record;
+ unsigned i, prefix;
+ char *anr;
+ struct phonebook_entry *entry;
+
+ if (len < 15) {
+ ofono_error("%s: bad EF_ANR record size", __func__);
+ return;
+ }
+
+ aas_record = msg[0];
+ if (aas_record == UNUSED)
+ return;
+
+ DBG("ANR %d", aas_record);
+
+ /* Length contains also TON & NPI */
+ number_length = msg[1];
+ if (number_length < 2)
+ return;
+
+ number_length--;
+ /* '+' + number + terminator */
+ anr = g_try_malloc0(2 * number_length + 2);
+ if (anr == NULL)
+ return;
+
+ prefix = 0;
+ if ((msg[2] & TON_MASK) == TON_INTERNATIONAL) {
+ anr[0] = '+';
+ prefix = 1;
+ }
+
+ for (i = 0; i < number_length; i++) {
+ anr[2 * i + prefix] = digit_to_utf8[msg[3 + i] & 0x0f];
+ anr[2 * i + 1 + prefix] = digit_to_utf8[msg[3 + i] >> 4];
+ }
+
+ entry = g_tree_lookup(ref->phonebook,
+ GINT_TO_POINTER(rec_data->adn_idx));
+ if (entry == NULL) {
+ g_free(anr);
+ return;
+ }
+
+ /* If one already exists, delete it */
+ if (entry->anr)
+ g_free(entry->anr);
+
+ DBG("Adding ANR %s to %d", anr, rec_data->adn_idx);
+ DBG("name %s", entry->name);
+
+ entry->anr = anr;
+
+ extension_record = msg[14];
+
+ DBG("ANR to entry %d number %s number length %d",
+ rec_data->adn_idx, anr, number_length);
+ DBG("extension_record %d aas %d", extension_record, aas_record);
+
+ if (extension_record != UNUSED) {
+ struct record_to_read *ext_rec =
+ g_try_malloc0(sizeof(*ext_rec));
+ const struct pb_file_info *f_info = ext1_info(ref->pb_files);
+
+ if (ext_rec && f_info) {
+ ext_rec->file_id = f_info->file_id;
+ ext_rec->type_tag = TYPE_EXT1;
+ ext_rec->record_length = f_info->record_length;
+ ext_rec->record = extension_record;
+ ext_rec->adn_idx = rec_data->adn_idx;
+ ext_rec->anr_ext = TRUE;
+
+ ref->pending_records =
+ g_slist_prepend(ref->pending_records, ext_rec);
+ }
+ }
+}
+
+static void handle_email(size_t len, const unsigned char *msg,
+ struct pb_ref_rec *ref,
+ const struct record_to_read *rec_data)
+{
+ char *email;
+ struct phonebook_entry *entry;
+
+ /* There are additional fields for type 2 files */
+ if (rec_data->set_by_iap)
+ len -= 2;
+
+ email = sim_string_to_utf8(msg, len);
+ if (email == NULL || *email == '\0') {
+ g_free(email);
+ return;
+ }
+
+ entry = g_tree_lookup(ref->phonebook,
+ GINT_TO_POINTER(rec_data->adn_idx));
+ if (entry == NULL) {
+ g_free(email);
+ return;
+ }
+
+ /* if one already exists, delete it */
+ if (entry->email)
+ g_free(entry->email);
+
+ DBG("Adding email to entry %d", rec_data->adn_idx);
+ DBG("name %s", entry->name);
+
+ entry->email = email;
+}
+
+static void handle_ext1(size_t len, const unsigned char *msg,
+ struct pb_ref_rec *ref,
+ const struct record_to_read *rec_data)
+{
+ unsigned number_length, i, next_extension_record;
+ struct phonebook_entry *entry;
+ char *ext_number;
+
+ if (len < 13) {
+ ofono_error("%s: bad EF_EXT1 record size", __func__);
+ return;
+ }
+
+ /* Check if there is more extension data */
+ next_extension_record = msg[12];
+ if (next_extension_record != UNUSED) {
+ struct record_to_read *ext_rec =
+ g_try_malloc0(sizeof(*ext_rec));
+ const struct pb_file_info *f_info = ext1_info(ref->pb_files);
+
+ if (ext_rec && f_info) {
+ DBG("next_extension_record %d", next_extension_record);
+
+ ext_rec->file_id = f_info->file_id;
+ ext_rec->record_length = f_info->record_length;
+ ext_rec->type_tag = TYPE_EXT1;
+ ext_rec->record = next_extension_record;
+ ext_rec->adn_idx = rec_data->adn_idx;
+ ext_rec->anr_ext = rec_data->anr_ext;
+
+ ref->pending_records =
+ g_slist_prepend(ref->pending_records, ext_rec);
+ }
+ }
+
+ if (msg[0] != EXT1_ADDITIONAL_DATA) {
+ DBG("EXT1 record with subaddress ignored");
+ return;
+ }
+
+ number_length = msg[1];
+ ext_number = g_try_malloc0(2 * number_length + 1);
+ if (ext_number == NULL)
+ return;
+
+ for (i = 0; i < number_length; i++) {
+ ext_number[2 * i] = digit_to_utf8[msg[2 + i] & 0x0f];
+ ext_number[2 * i + 1] = digit_to_utf8[msg[2 + i] >> 4];
+ }
+
+ DBG("Number extension %s", ext_number);
+ DBG("number length %d", number_length);
+
+ DBG("Looking for ADN entry %d", rec_data->adn_idx);
+ entry = g_tree_lookup(ref->phonebook,
+ GINT_TO_POINTER(rec_data->adn_idx));
+ if (entry == NULL) {
+ g_free(ext_number);
+ return;
+ }
+
+ if (rec_data->anr_ext) {
+ char *anr = entry->anr;
+ entry->anr = g_strconcat(anr, ext_number, NULL);
+ g_free(anr);
+ } else {
+ char *number = entry->number;
+ entry->number = g_strconcat(number, ext_number, NULL);
+ g_free(number);
+ }
+
+ g_free(ext_number);
+}
+
+static const char *file_tag_to_string(enum file_type_tag tag)
+{
+ switch (tag) {
+ case TYPE_ADN: return "ADN";
+ case TYPE_IAP: return "IAP";
+ case TYPE_EXT1: return "EXT1";
+ case TYPE_SNE: return "SNE";
+ case TYPE_ANR: return "ANR";
+ case TYPE_PBC: return "PBC";
+ case TYPE_GPR: return "GPR";
+ case TYPE_AAS: return "AAS";
+ case TYPE_GAS: return "GAS";
+ case TYPE_UID: return "UID";
+ case TYPE_EMAIL: return "EMAIL";
+ case TYPE_CCP1: return "CCP1";
+ default: return "<UNKNOWN>";
+ }
+}
+
+static void decode_read_response(const struct record_to_read *rec_data,
+ const unsigned char *msg, size_t len,
+ struct pb_ref_rec *ref)
+{
+ DBG("Decoding %s type record", file_tag_to_string(rec_data->type_tag));
+ switch (rec_data->type_tag) {
+ case TYPE_IAP:
+ handle_iap(len, msg, ref, rec_data);
+ break;
+ case TYPE_SNE:
+ handle_sne(len, msg, ref, rec_data);
+ break;
+ case TYPE_ANR:
+ handle_anr(len, msg, ref, rec_data);
+ break;
+ case TYPE_EMAIL:
+ handle_email(len, msg, ref, rec_data);
+ break;
+ case TYPE_EXT1:
+ handle_ext1(len, msg, ref, rec_data);
+ break;
+ default:
+ DBG("Skipping type");
+ break;
+ }
+}
+
+static gboolean export_entry(gpointer key, gpointer value, gpointer data)
+{
+ struct ofono_phonebook *pb = data;
+ struct phonebook_entry *entry = value;
+
+ ofono_phonebook_entry(pb, -1,
+ entry->number, -1,
+ entry->name, -1,
+ NULL,
+ entry->anr, -1,
+ entry->sne,
+ entry->email,
+ NULL, NULL);
+
+ g_free(entry->name);
+ g_free(entry->number);
+ g_free(entry->email);
+ g_free(entry->anr);
+ g_free(entry->sne);
+ g_free(entry);
+
+ return FALSE;
+}
+
+static void export_and_return(gboolean ok, struct cb_data *cbd)
+{
+ struct ofono_phonebook *pb = cbd->user;
+ ofono_phonebook_cb_t cb = cbd->cb;
+ struct pb_data *pbd = ofono_phonebook_get_data(pb);
+ GSList *l;
+
+ DBG("phonebook fully read");
+
+ for (l = pbd->pb_refs; l != NULL; l = l->next) {
+ struct pb_ref_rec *ref = l->data;
+
+ g_tree_foreach(ref->phonebook, export_entry, pb);
+ g_tree_destroy(ref->phonebook);
+ g_slist_free_full(ref->pending_records, g_free);
+ g_slist_free_full(ref->pb_files, g_free);
+ }
+
+ g_slist_free_full(pbd->pb_refs, g_free);
+ pbd->pb_refs = NULL;
+
+ if (ok)
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ else
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+
+ g_free(cbd);
+}
+
+static void read_record_cb(int ok, int total_length, int record,
+ const unsigned char *data,
+ int record_length, void *userdata)
+{
+ struct cb_data *cbd = userdata;
+ struct ofono_phonebook *pb = cbd->user;
+ struct pb_data *pbd = ofono_phonebook_get_data(pb);
+ struct pb_ref_rec *ref = pbd->pb_ref_next->data;
+ struct record_to_read *rec;
+
+ if (!ok) {
+ ofono_error("%s: error %d", __func__, ok);
+ export_and_return(FALSE, cbd);
+ return;
+ }
+
+ DBG("ok %d; total_length %d; record %d; record_length %d",
+ ok, total_length, record, record_length);
+
+ rec = ref->next_record->data;
+
+ /* This call might add elements to pending_records */
+ decode_read_response(rec, data, record_length, ref);
+
+ ref->pending_records = g_slist_remove(ref->pending_records, rec);
+ g_free(rec);
+
+ if (ref->pending_records) {
+ struct record_to_read *rec;
+
+ ref->next_record = ref->pending_records;
+ rec = ref->next_record->data;
+
+ ofono_sim_read_record(pbd->sim_context, rec->file_id,
+ OFONO_SIM_FILE_STRUCTURE_FIXED,
+ rec->record,
+ rec->record_length,
+ pbd->df_path, pbd->df_size,
+ read_record_cb, cbd);
+ } else {
+ /* Read files from next EF_PBR record, if any */
+
+ pbd->pb_ref_next = pbd->pb_ref_next->next;
+ if (pbd->pb_ref_next == NULL) {
+ export_and_return(TRUE, cbd);
+ } else {
+ struct pb_ref_rec *ref;
+
+ DBG("Next EFpbr record");
+
+ ref = pbd->pb_ref_next->data;
+
+ if (!ref->pb_files) {
+ export_and_return(TRUE, cbd);
+ } else {
+ struct pb_file_info *file_info;
+
+ ref->pb_next = ref->pb_files;
+ file_info = ref->pb_files->data;
+
+ ofono_sim_read_info(pbd->sim_context,
+ file_info->file_id,
+ OFONO_SIM_FILE_STRUCTURE_FIXED,
+ pbd->df_path, pbd->df_size,
+ read_info_cb, cbd);
+ }
+ }
+ }
+}
+
+static void pb_adn_cb(int ok, int total_length, int record,
+ const unsigned char *data,
+ int record_length, void *userdata)
+{
+ struct cb_data *cbd = userdata;
+ struct ofono_phonebook *pb = cbd->user;
+ struct pb_data *pbd = ofono_phonebook_get_data(pb);
+ struct pb_ref_rec *ref = pbd->pb_ref_next->data;
+ GSList *l;
+
+ if (!ok) {
+ ofono_error("%s: error %d", __func__, ok);
+ export_and_return(FALSE, cbd);
+ return;
+ }
+
+ DBG("ok %d; total_length %d; record %d; record_length %d",
+ ok, total_length, record, record_length);
+
+ if (handle_adn(record_length, data, ref, record) != NULL) {
+ /* Add type 1 records */
+ for (l = ref->pb_files; l; l = l->next) {
+ const struct pb_file_info *f_info = l->data;
+ struct record_to_read *ext_rec;
+
+ if (f_info->pbr_type == TYPE_1_TAG &&
+ f_info->file_type != TYPE_ADN) {
+ ext_rec = g_try_malloc0(sizeof(*ext_rec));
+ if (ext_rec == NULL)
+ break;
+
+ ext_rec->file_id = f_info->file_id;
+ ext_rec->type_tag = f_info->file_type;
+ ext_rec->record_length = f_info->record_length;
+ ext_rec->record = record;
+ ext_rec->adn_idx = record;
+
+ ref->pending_records =
+ g_slist_prepend(ref->pending_records,
+ ext_rec);
+ }
+ }
+ }
+
+ if (record*record_length >= total_length) {
+ DBG("All ADN records read: reading additional files");
+
+ if (ref->pending_records) {
+ struct record_to_read *rec;
+
+ ref->next_record = ref->pending_records;
+ rec = ref->next_record->data;
+
+ ofono_sim_read_record(pbd->sim_context, rec->file_id,
+ OFONO_SIM_FILE_STRUCTURE_FIXED,
+ rec->record,
+ rec->record_length,
+ pbd->df_path, pbd->df_size,
+ read_record_cb, cbd);
+ } else {
+ export_and_return(TRUE, cbd);
+ }
+ }
+}
+
+static void read_info_cb(int ok, unsigned char file_status,
+ int total_length, int record_length,
+ void *userdata)
+{
+ struct cb_data *cbd = userdata;
+ struct ofono_phonebook *pb = cbd->user;
+ struct pb_data *pbd = ofono_phonebook_get_data(pb);
+ struct pb_file_info *file_info;
+ struct pb_ref_rec *ref = pbd->pb_ref_next->data;
+
+ file_info = ref->pb_next->data;
+ ref->pb_next = ref->pb_next->next;
+
+ if (ok) {
+ file_info->record_length = record_length;
+ file_info->file_length = total_length;
+
+ DBG("file id %x record length %d total_length %d",
+ file_info->file_id, record_length, total_length);
+ } else {
+ ofono_warn("%s: %x not found", __func__, file_info->file_id);
+ ref->pb_files = g_slist_remove(ref->pb_files, file_info);
+ g_free(file_info);
+ }
+
+ if (ref->pb_next == NULL) {
+ if (ref->pb_files == NULL) {
+ ofono_warn("%s: no phonebook on SIM", __func__);
+ export_and_return(FALSE, cbd);
+ return;
+ }
+
+ /* Read full contents of the master file */
+ file_info = ref->pb_files->data;
+
+ ofono_sim_read_path(pbd->sim_context, file_info->file_id,
+ OFONO_SIM_FILE_STRUCTURE_FIXED,
+ pbd->df_path, pbd->df_size,
+ pb_adn_cb, cbd);
+ } else {
+ file_info = ref->pb_next->data;
+
+ ofono_sim_read_info(pbd->sim_context, file_info->file_id,
+ OFONO_SIM_FILE_STRUCTURE_FIXED,
+ pbd->df_path, pbd->df_size,
+ read_info_cb, cbd);
+ }
+}
+
+static void start_sim_app_read(struct cb_data *cbd)
+{
+ struct ofono_phonebook *pb = cbd->user;
+ struct pb_data *pbd = ofono_phonebook_get_data(pb);
+ struct pb_ref_rec *ref_rec;
+ struct pb_file_info *f_info;
+ struct pb_file_info *f_ext1;
+
+ pbd->df_path = sim_path;
+ pbd->df_size = sizeof(sim_path);
+
+ ref_rec = g_try_malloc0(sizeof(*ref_rec));
+ if (ref_rec == NULL) {
+ ofono_error("%s: OOM", __func__);
+ export_and_return(FALSE, cbd);
+ return;
+ }
+
+ ref_rec->phonebook = g_tree_new(comp_int);
+
+ /* Only EF_ADN and EF_EXT1 read for SIM */
+
+ f_info = g_try_malloc0(sizeof(*f_info));
+ if (f_info == NULL) {
+ ofono_error("%s: OOM", __func__);
+ export_and_return(FALSE, cbd);
+ return;
+ }
+
+ f_info->file_id = SIM_EFADN_FILEID;
+ f_info->pbr_type = TYPE_1_TAG;
+ f_info->file_type = TYPE_ADN;
+ ref_rec->pb_files = g_slist_append(ref_rec->pb_files, f_info);
+
+ f_ext1 = g_try_malloc0(sizeof(*f_ext1));
+ if (f_ext1 == NULL) {
+ ofono_error("%s: OOM", __func__);
+ export_and_return(FALSE, cbd);
+ return;
+ }
+
+ f_ext1->file_id = SIM_EFEXT1_FILEID;
+ f_ext1->pbr_type = TYPE_3_TAG;
+ f_ext1->file_type = TYPE_EXT1;
+ ref_rec->pb_files = g_slist_append(ref_rec->pb_files, f_ext1);
+
+ pbd->pb_refs = g_slist_append(pbd->pb_refs, ref_rec);
+ pbd->pb_ref_next = pbd->pb_refs;
+
+ ref_rec->pb_next = ref_rec->pb_files;
+
+ /* Start reading process for MF */
+ ofono_sim_read_info(pbd->sim_context, f_info->file_id,
+ OFONO_SIM_FILE_STRUCTURE_FIXED,
+ pbd->df_path, pbd->df_size,
+ read_info_cb, cbd);
+}
+
+static void pb_reference_data_cb(int ok, int total_length, int record,
+ const unsigned char *sdata,
+ int record_length, void *userdata)
+{
+ struct cb_data *cbd = userdata;
+ struct ofono_phonebook *pb = cbd->user;
+ struct pb_data *pbd = ofono_phonebook_get_data(pb);
+ const unsigned char *ptr = sdata;
+ gboolean finished = FALSE;
+ struct pb_ref_rec *ref_rec;
+
+ DBG("total_length %d record %d record_length %d",
+ total_length, record, record_length);
+
+ if (!ok) {
+ /* We migh have a SIM instead of USIM application: try that */
+ DBG("%s: error %d, trying SIM files", __func__, ok);
+ start_sim_app_read(cbd);
+ return;
+ }
+
+ ref_rec = g_try_malloc0(sizeof(*ref_rec));
+ if (ref_rec == NULL) {
+ ofono_error("%s: OOM", __func__);
+ export_and_return(FALSE, cbd);
+ return;
+ }
+
+ ref_rec->phonebook = g_tree_new(comp_int);
+
+ while (ptr < sdata + record_length && finished == FALSE) {
+ int typelen, file_id, i;
+ enum constructed_tag pbr_type = *ptr;
+
+ switch (pbr_type) {
+ case TYPE_1_TAG:
+ case TYPE_2_TAG:
+ case TYPE_3_TAG:
+ typelen = *(ptr + 1);
+ DBG("File type=%02X, len=%d", *ptr, typelen);
+ ptr += 2;
+ i = 0;
+
+ while (i < typelen) {
+ struct pb_file_info *file_info =
+ g_try_new0(struct pb_file_info, 1);
+ if (!file_info) {
+ ofono_error("%s: OOM", __func__);
+ export_and_return(FALSE, cbd);
+ return;
+ }
+
+ file_id = (ptr[i + 2] << 8) + ptr[i + 3];
+
+ DBG("creating file info for File type=%02X",
+ ptr[i]);
+ DBG("File ID=%04X", file_id);
+
+ file_info->pbr_type = pbr_type;
+ file_info->file_type = ptr[i];
+ file_info->file_id = file_id;
+ /* Keep order, important for type 2 files */
+ ref_rec->pb_files =
+ g_slist_append(ref_rec->pb_files,
+ file_info);
+ i += ptr[i + 1] + 2;
+ }
+
+ ptr += typelen;
+ break;
+ default:
+ DBG("All handled %02x", *ptr);
+ finished = TRUE;
+ break;
+ }
+ }
+
+ pbd->pb_refs = g_slist_append(pbd->pb_refs, ref_rec);
+
+ if (record*record_length >= total_length) {
+ struct pb_ref_rec *ref;
+ struct pb_file_info *file_info;
+
+ DBG("All EFpbr records read");
+
+ pbd->pb_ref_next = pbd->pb_refs;
+ ref = pbd->pb_ref_next->data;
+
+ if (ref->pb_files == NULL) {
+ ofono_error("%s: no files to read", __func__);
+ export_and_return(FALSE, cbd);
+ return;
+ }
+
+ ref->pb_next = ref->pb_files;
+ file_info = ref->pb_files->data;
+
+ /* Start reading process for first EF_PBR entry */
+
+ ofono_sim_read_info(pbd->sim_context, file_info->file_id,
+ OFONO_SIM_FILE_STRUCTURE_FIXED,
+ pbd->df_path, pbd->df_size,
+ read_info_cb, cbd);
+ }
+}
+
+static void ril_export_entries(struct ofono_phonebook *pb,
+ const char *storage,
+ ofono_phonebook_cb_t cb, void *data)
+{
+ struct pb_data *pbd = ofono_phonebook_get_data(pb);
+ struct cb_data *cbd;
+
+ DBG("Storage %s", storage);
+
+ /* Only for SIM memory */
+ if (strcmp(storage, "SM") != 0) {
+ CALLBACK_WITH_FAILURE(cb, data);
+ return;
+ }
+
+ cbd = cb_data_new(cb, data, pb);
+
+ /* Assume USIM, change in case EF_PBR is not present */
+ pbd->df_path = usim_path;
+ pbd->df_size = sizeof(usim_path);
+
+ ofono_sim_read(pbd->sim_context, SIM_EFPBR_FILEID,
+ OFONO_SIM_FILE_STRUCTURE_FIXED,
+ pb_reference_data_cb, cbd);
+}
+
+static gboolean ril_delayed_register(gpointer user_data)
+{
+ struct ofono_phonebook *pb = user_data;
+
+ ofono_phonebook_register(pb);
+ return FALSE;
+}
+
+static int ril_phonebook_probe(struct ofono_phonebook *pb,
+ unsigned int vendor, void *user)
+{
+ struct ofono_modem *modem = user;
+ struct pb_data *pd = g_try_new0(struct pb_data, 1);
+ if (pd == NULL)
+ return -ENOMEM;
+
+ pd->sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem);
+ if (pd->sim == NULL)
+ return -ENOENT;
+
+ pd->sim_context = ofono_sim_context_create(pd->sim);
+ if (pd->sim_context == NULL)
+ return -ENOENT;
+
+ ofono_phonebook_set_data(pb, pd);
+
+ g_idle_add(ril_delayed_register, pb);
+
+ return 0;
+}
+
+static void ril_phonebook_remove(struct ofono_phonebook *pb)
+{
+ struct pb_data *pbd = ofono_phonebook_get_data(pb);
+
+ ofono_phonebook_set_data(pb, NULL);
+ ofono_sim_context_free(pbd->sim_context);
+
+ g_free(pbd);
+}
+
+static struct ofono_phonebook_driver driver = {
+ .name = RILMODEM,
+ .probe = ril_phonebook_probe,
+ .remove = ril_phonebook_remove,
+ .export_entries = ril_export_entries
+};
+
+void ril_phonebook_init(void)
+{
+ ofono_phonebook_driver_register(&driver);
+}
+
+void ril_phonebook_exit(void)
+{
+ ofono_phonebook_driver_unregister(&driver);
+}
diff --git a/drivers/rilmodem/radio-settings.c b/drivers/rilmodem/radio-settings.c
new file mode 100644
index 00000000..9153e5bb
--- /dev/null
+++ b/drivers/rilmodem/radio-settings.c
@@ -0,0 +1,300 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2013 Jolla Ltd
+ * Contact: Jussi Kangas <jussi.kangas@tieto.com>
+ * Copyright (C) 2014 Canonical Ltd
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/radio-settings.h>
+
+#include "gril.h"
+
+#include "rilmodem.h"
+
+#include "grilrequest.h"
+#include "grilreply.h"
+#include "radio-settings.h"
+
+static void ril_set_rat_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct ofono_radio_settings *rs = cbd->user;
+ struct radio_data *rd = ofono_radio_settings_get_data(rs);
+ ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb;
+
+ if (message->error == RIL_E_SUCCESS) {
+ g_ril_print_response_no_args(rd->ril, message);
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ } else {
+ ofono_error("%s: rat mode setting failed", __func__);
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ }
+}
+
+void ril_set_rat_mode(struct ofono_radio_settings *rs,
+ enum ofono_radio_access_mode mode,
+ ofono_radio_settings_rat_mode_set_cb_t cb,
+ void *data)
+{
+ struct radio_data *rd = ofono_radio_settings_get_data(rs);
+ struct cb_data *cbd = cb_data_new(cb, data, rs);
+ struct parcel rilp;
+ int pref = PREF_NET_TYPE_GSM_WCDMA;
+
+ switch (mode) {
+ case OFONO_RADIO_ACCESS_MODE_ANY:
+ pref = PREF_NET_TYPE_LTE_GSM_WCDMA;
+ break;
+ case OFONO_RADIO_ACCESS_MODE_GSM:
+ pref = PREF_NET_TYPE_GSM_ONLY;
+ break;
+ case OFONO_RADIO_ACCESS_MODE_UMTS:
+ pref = PREF_NET_TYPE_GSM_WCDMA;
+ break;
+ case OFONO_RADIO_ACCESS_MODE_LTE:
+ pref = PREF_NET_TYPE_LTE_GSM_WCDMA;
+ break;
+ }
+
+ g_ril_request_set_preferred_network_type(rd->ril, pref, &rilp);
+
+ if (g_ril_send(rd->ril, RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE,
+ &rilp, ril_set_rat_cb, cbd, g_free) == 0) {
+ ofono_error("%s: unable to set rat mode", __func__);
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static void ril_rat_mode_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
+ struct ofono_radio_settings *rs = cbd->user;
+ struct radio_data *rd = ofono_radio_settings_get_data(rs);
+ int mode, pref;
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("%s: error %s", __func__,
+ ril_error_to_string(message->error));
+ CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+ return;
+ }
+
+ pref = g_ril_reply_parse_get_preferred_network_type(rd->ril, message);
+ if (pref < 0) {
+ ofono_error("%s: parse error", __func__);
+ CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+ return;
+ }
+
+ /*
+ * GSM_WCDMA_AUTO -> ril.h: GSM/WCDMA (auto mode, according to PRL)
+ * PRL: preferred roaming list.
+ * This value is returned when selecting the slot as having 3G
+ * capabilities, so it is sort of the default for MTK modems.
+ */
+
+ switch (pref) {
+ case PREF_NET_TYPE_GSM_WCDMA:
+ case PREF_NET_TYPE_GSM_WCDMA_AUTO:
+ mode = OFONO_RADIO_ACCESS_MODE_UMTS;
+ break;
+ case PREF_NET_TYPE_GSM_ONLY:
+ mode = OFONO_RADIO_ACCESS_MODE_GSM;
+ break;
+ case PREF_NET_TYPE_LTE_GSM_WCDMA:
+ mode = OFONO_RADIO_ACCESS_MODE_LTE;
+ break;
+ default:
+ ofono_error("%s: Unexpected preferred network type (%d)",
+ __func__, pref);
+ mode = OFONO_RADIO_ACCESS_MODE_ANY;
+ break;
+ }
+
+ CALLBACK_WITH_SUCCESS(cb, mode, cbd->data);
+}
+
+void ril_query_rat_mode(struct ofono_radio_settings *rs,
+ ofono_radio_settings_rat_mode_query_cb_t cb,
+ void *data)
+{
+ struct radio_data *rd = ofono_radio_settings_get_data(rs);
+ struct cb_data *cbd = cb_data_new(cb, data, rs);
+
+ if (g_ril_send(rd->ril, RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE,
+ NULL, ril_rat_mode_cb, cbd, g_free) == 0) {
+ ofono_error("%s: unable to send rat mode query", __func__);
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, -1, data);
+ }
+}
+
+void ril_query_fast_dormancy(struct ofono_radio_settings *rs,
+ ofono_radio_settings_fast_dormancy_query_cb_t cb,
+ void *data)
+{
+ struct radio_data *rd = ofono_radio_settings_get_data(rs);
+
+ CALLBACK_WITH_SUCCESS(cb, rd->fast_dormancy, data);
+}
+
+static void ril_display_state_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct ofono_radio_settings *rs = cbd->user;
+ struct radio_data *rd = ofono_radio_settings_get_data(rs);
+ ofono_radio_settings_fast_dormancy_set_cb_t cb = cbd->cb;
+
+ if (message->error == RIL_E_SUCCESS) {
+ g_ril_print_response_no_args(rd->ril, message);
+
+ rd->fast_dormancy = rd->pending_fd;
+
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ } else {
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ }
+}
+
+void ril_set_fast_dormancy(struct ofono_radio_settings *rs,
+ ofono_bool_t enable,
+ ofono_radio_settings_fast_dormancy_set_cb_t cb,
+ void *data)
+{
+ struct radio_data *rd = ofono_radio_settings_get_data(rs);
+ struct cb_data *cbd = cb_data_new(cb, data, rs);
+ struct parcel rilp;
+
+ g_ril_request_screen_state(rd->ril, enable ? 0 : 1, &rilp);
+
+ rd->pending_fd = enable;
+
+ if (g_ril_send(rd->ril, RIL_REQUEST_SCREEN_STATE, &rilp,
+ ril_display_state_cb, cbd, g_free) <= 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static ofono_bool_t query_available_rats_cb(gpointer user_data)
+{
+ unsigned int available_rats;
+ struct cb_data *cbd = user_data;
+ ofono_radio_settings_available_rats_query_cb_t cb = cbd->cb;
+ struct ofono_radio_settings *rs = cbd->user;
+ struct radio_data *rd = ofono_radio_settings_get_data(rs);
+
+ available_rats = OFONO_RADIO_ACCESS_MODE_GSM
+ | OFONO_RADIO_ACCESS_MODE_UMTS;
+
+ if (ofono_modem_get_boolean(rd->modem, MODEM_PROP_LTE_CAPABLE))
+ available_rats |= OFONO_RADIO_ACCESS_MODE_LTE;
+
+ CALLBACK_WITH_SUCCESS(cb, available_rats, cbd->data);
+
+ g_free(cbd);
+
+ return FALSE;
+}
+
+void ril_query_available_rats(struct ofono_radio_settings *rs,
+ ofono_radio_settings_available_rats_query_cb_t cb,
+ void *data)
+{
+ struct cb_data *cbd = cb_data_new(cb, data, rs);
+
+ g_idle_add(query_available_rats_cb, cbd);
+}
+
+void ril_delayed_register(const struct ofono_error *error, void *user_data)
+{
+ struct ofono_radio_settings *rs = user_data;
+
+ if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+ ofono_radio_settings_register(rs);
+ else
+ ofono_error("%s: cannot set default fast dormancy", __func__);
+}
+
+static int ril_radio_settings_probe(struct ofono_radio_settings *rs,
+ unsigned int vendor, void *user)
+{
+ struct ril_radio_settings_driver_data *rs_init_data = user;
+ struct radio_data *rsd = g_try_new0(struct radio_data, 1);
+
+ if (rsd == NULL) {
+ ofono_error("%s: cannot allocate memory", __func__);
+ return -ENOMEM;
+ }
+
+ rsd->ril = g_ril_clone(rs_init_data->gril);
+ rsd->modem = rs_init_data->modem;
+
+ ofono_radio_settings_set_data(rs, rsd);
+
+ ril_set_fast_dormancy(rs, FALSE, ril_delayed_register, rs);
+
+ return 0;
+}
+
+void ril_radio_settings_remove(struct ofono_radio_settings *rs)
+{
+ struct radio_data *rd = ofono_radio_settings_get_data(rs);
+ ofono_radio_settings_set_data(rs, NULL);
+
+ g_ril_unref(rd->ril);
+ g_free(rd);
+}
+
+static struct ofono_radio_settings_driver driver = {
+ .name = RILMODEM,
+ .probe = ril_radio_settings_probe,
+ .remove = ril_radio_settings_remove,
+ .query_rat_mode = ril_query_rat_mode,
+ .set_rat_mode = ril_set_rat_mode,
+ .query_fast_dormancy = ril_query_fast_dormancy,
+ .set_fast_dormancy = ril_set_fast_dormancy,
+ .query_available_rats = ril_query_available_rats
+};
+
+void ril_radio_settings_init(void)
+{
+ ofono_radio_settings_driver_register(&driver);
+}
+
+void ril_radio_settings_exit(void)
+{
+ ofono_radio_settings_driver_unregister(&driver);
+}
diff --git a/drivers/rilmodem/radio-settings.h b/drivers/rilmodem/radio-settings.h
new file mode 100644
index 00000000..727d5381
--- /dev/null
+++ b/drivers/rilmodem/radio-settings.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2014 Canonical Ltd.
+ *
+ * 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
+ *
+ */
+
+struct radio_data {
+ GRil *ril;
+ struct ofono_modem *modem;
+ gboolean fast_dormancy;
+ gboolean pending_fd;
+};
+
+void ril_delayed_register(const struct ofono_error *error, void *user_data);
+void ril_radio_settings_remove(struct ofono_radio_settings *rs);
+void ril_query_rat_mode(struct ofono_radio_settings *rs,
+ ofono_radio_settings_rat_mode_query_cb_t cb,
+ void *data);
+void ril_set_rat_mode(struct ofono_radio_settings *rs,
+ enum ofono_radio_access_mode mode,
+ ofono_radio_settings_rat_mode_set_cb_t cb,
+ void *data);
+void ril_query_fast_dormancy(struct ofono_radio_settings *rs,
+ ofono_radio_settings_fast_dormancy_query_cb_t cb,
+ void *data);
+void ril_set_fast_dormancy(struct ofono_radio_settings *rs,
+ ofono_bool_t enable,
+ ofono_radio_settings_fast_dormancy_set_cb_t cb,
+ void *data);
+void ril_query_available_rats(struct ofono_radio_settings *rs,
+ ofono_radio_settings_available_rats_query_cb_t cb,
+ void *data);
diff --git a/drivers/rilmodem/rilmodem.c b/drivers/rilmodem/rilmodem.c
new file mode 100644
index 00000000..e693563d
--- /dev/null
+++ b/drivers/rilmodem/rilmodem.c
@@ -0,0 +1,78 @@
+/*
+ *
+ * oFono - Open Source Telephony - RIL Modem Support
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2012 Canonical, Ltd. 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
+
+#include <glib.h>
+#include <gril.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/types.h>
+
+#include "rilmodem.h"
+
+static int rilmodem_init(void)
+{
+ DBG("");
+
+ ril_devinfo_init();
+ ril_sim_init();
+ ril_voicecall_init();
+ ril_sms_init();
+ ril_netreg_init();
+ ril_call_volume_init();
+ ril_gprs_init();
+ ril_gprs_context_init();
+ ril_ussd_init();
+ ril_call_settings_init();
+ ril_call_forwarding_init();
+ ril_radio_settings_init();
+ ril_call_barring_init();
+
+ return 0;
+}
+
+static void rilmodem_exit(void)
+{
+ DBG("");
+
+ ril_devinfo_exit();
+ ril_sim_exit();
+ ril_voicecall_exit();
+ ril_sms_exit();
+ ril_netreg_exit();
+ ril_call_volume_exit();
+ ril_gprs_exit();
+ ril_gprs_context_exit();
+ ril_ussd_exit();
+ ril_call_settings_exit();
+ ril_call_forwarding_exit();
+ ril_radio_settings_exit();
+ ril_call_barring_exit();
+}
+
+OFONO_PLUGIN_DEFINE(rilmodem, "RIL modem driver", VERSION,
+ OFONO_PLUGIN_PRIORITY_DEFAULT, rilmodem_init, rilmodem_exit)
diff --git a/drivers/rilmodem/rilmodem.h b/drivers/rilmodem/rilmodem.h
new file mode 100644
index 00000000..987ce3c0
--- /dev/null
+++ b/drivers/rilmodem/rilmodem.h
@@ -0,0 +1,71 @@
+/*
+ *
+ * oFono - Open Source Telephony - RIL Modem Support
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2012 Canonical Ltd.
+ *
+ * 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
+ *
+ */
+
+#include "rilutil.h"
+
+#define RILMODEM "rilmodem"
+
+/* Shared constants */
+#define EF_STATUS_INVALIDATED 0
+#define EF_STATUS_VALID 1
+
+extern void ril_devinfo_init(void);
+extern void ril_devinfo_exit(void);
+
+extern void ril_call_volume_init(void);
+extern void ril_call_volume_exit(void);
+
+extern void ril_voicecall_init(void);
+extern void ril_voicecall_exit(void);
+
+extern void ril_sim_init(void);
+extern void ril_sim_exit(void);
+
+extern void ril_sms_init(void);
+extern void ril_sms_exit(void);
+
+extern void ril_netreg_init(void);
+extern void ril_netreg_exit(void);
+
+extern void ril_gprs_init(void);
+extern void ril_gprs_exit(void);
+
+extern void ril_gprs_context_init(void);
+extern void ril_gprs_context_exit(void);
+
+extern void ril_ussd_init(void);
+extern void ril_ussd_exit(void);
+
+extern void ril_call_settings_init(void);
+extern void ril_call_settings_exit(void);
+
+extern void ril_call_forwarding_init(void);
+extern void ril_call_forwarding_exit(void);
+
+extern void ril_radio_settings_init(void);
+extern void ril_radio_settings_exit(void);
+
+extern void ril_call_barring_init(void);
+extern void ril_call_barring_exit(void);
+
+extern void ril_phonebook_init(void);
+extern void ril_phonebook_exit(void);
diff --git a/drivers/rilmodem/rilutil.c b/drivers/rilmodem/rilutil.c
new file mode 100644
index 00000000..c173940e
--- /dev/null
+++ b/drivers/rilmodem/rilutil.c
@@ -0,0 +1,194 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2012 Canonical Ltd.
+ *
+ * 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
+
+#include <glib.h>
+#include <gril.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/log.h>
+#include <ofono/types.h>
+
+#include "common.h"
+#include "rilutil.h"
+#include "simutil.h"
+#include "util.h"
+#include "ril_constants.h"
+
+struct ril_util_sim_state_query {
+ GRil *ril;
+ guint cpin_poll_source;
+ guint cpin_poll_count;
+ guint interval;
+ guint num_times;
+ ril_util_sim_inserted_cb_t cb;
+ void *userdata;
+ GDestroyNotify destroy;
+};
+
+static gboolean cpin_check(gpointer userdata);
+
+void decode_ril_error(struct ofono_error *error, const char *final)
+{
+ if (!strcmp(final, "OK")) {
+ error->type = OFONO_ERROR_TYPE_NO_ERROR;
+ error->error = 0;
+ } else {
+ error->type = OFONO_ERROR_TYPE_FAILURE;
+ error->error = 0;
+ }
+}
+
+gint ril_util_call_compare_by_status(gconstpointer a, gconstpointer b)
+{
+ const struct ofono_call *call = a;
+ int status = GPOINTER_TO_INT(b);
+
+ if (status != call->status)
+ return 1;
+
+ return 0;
+}
+
+gint ril_util_call_compare_by_phone_number(gconstpointer a, gconstpointer b)
+{
+ const struct ofono_call *call = a;
+ const struct ofono_phone_number *pb = b;
+
+ return memcmp(&call->phone_number, pb,
+ sizeof(struct ofono_phone_number));
+}
+
+gint ril_util_call_compare_by_id(gconstpointer a, gconstpointer b)
+{
+ const struct ofono_call *call = a;
+ unsigned int id = GPOINTER_TO_UINT(b);
+
+ if (id < call->id)
+ return -1;
+
+ if (id > call->id)
+ return 1;
+
+ return 0;
+}
+
+gint ril_util_call_compare(gconstpointer a, gconstpointer b)
+{
+ const struct ofono_call *ca = a;
+ const struct ofono_call *cb = b;
+
+ if (ca->id < cb->id)
+ return -1;
+
+ if (ca->id > cb->id)
+ return 1;
+
+ return 0;
+}
+
+static gboolean cpin_check(gpointer userdata)
+{
+ struct ril_util_sim_state_query *req = userdata;
+
+ req->cpin_poll_source = 0;
+
+ return FALSE;
+}
+
+gchar *ril_util_get_netmask(const gchar *address)
+{
+ char *result;
+
+ if (g_str_has_suffix(address, "/30")) {
+ result = PREFIX_30_NETMASK;
+ } else if (g_str_has_suffix(address, "/29")) {
+ result = PREFIX_29_NETMASK;
+ } else if (g_str_has_suffix(address, "/28")) {
+ result = PREFIX_28_NETMASK;
+ } else if (g_str_has_suffix(address, "/27")) {
+ result = PREFIX_27_NETMASK;
+ } else if (g_str_has_suffix(address, "/26")) {
+ result = PREFIX_26_NETMASK;
+ } else if (g_str_has_suffix(address, "/25")) {
+ result = PREFIX_25_NETMASK;
+ } else if (g_str_has_suffix(address, "/24")) {
+ result = PREFIX_24_NETMASK;
+ } else {
+ /*
+ * This handles the case where the
+ * Samsung RILD returns an address without
+ * a prefix, however it explicitly sets a
+ * /24 netmask ( which isn't returned as
+ * an attribute of the DATA_CALL.
+ *
+ * TODO/OEM: this might need to be quirked
+ * for specific devices.
+ */
+ result = PREFIX_24_NETMASK;
+ }
+
+ DBG("address: %s netmask: %s", address, result);
+
+ return result;
+}
+
+struct ril_util_sim_state_query *ril_util_sim_state_query_new(GRil *ril,
+ guint interval, guint num_times,
+ ril_util_sim_inserted_cb_t cb,
+ void *userdata,
+ GDestroyNotify destroy)
+{
+ struct ril_util_sim_state_query *req;
+
+ req = g_new0(struct ril_util_sim_state_query, 1);
+
+ req->ril = ril;
+ req->interval = interval;
+ req->num_times = num_times;
+ req->cb = cb;
+ req->userdata = userdata;
+ req->destroy = destroy;
+
+ cpin_check(req);
+
+ return req;
+}
+
+void ril_util_sim_state_query_free(struct ril_util_sim_state_query *req)
+{
+ if (req == NULL)
+ return;
+
+ if (req->cpin_poll_source > 0)
+ g_source_remove(req->cpin_poll_source);
+
+ if (req->destroy)
+ req->destroy(req->userdata);
+
+ g_free(req);
+}
diff --git a/drivers/rilmodem/rilutil.h b/drivers/rilmodem/rilutil.h
new file mode 100644
index 00000000..25aed2eb
--- /dev/null
+++ b/drivers/rilmodem/rilutil.h
@@ -0,0 +1,165 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2012 Canonical Ltd.
+ *
+ * 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
+ *
+ */
+#ifndef RILUTIL_H
+#define RILUTIL_H
+
+#include <stdio.h>
+#include <modem.h>
+#include <sim.h>
+#include <gprs-context.h>
+
+/* TODO: create a table lookup*/
+#define PREFIX_30_NETMASK "255.255.255.252"
+#define PREFIX_29_NETMASK "255.255.255.248"
+#define PREFIX_28_NETMASK "255.255.255.240"
+#define PREFIX_27_NETMASK "255.255.255.224"
+#define PREFIX_26_NETMASK "255.255.255.192"
+#define PREFIX_25_NETMASK "255.255.255.128"
+#define PREFIX_24_NETMASK "255.255.255.0"
+
+#define MODEM_PROP_LTE_CAPABLE "lte-capable"
+
+enum ril_util_sms_store {
+ RIL_UTIL_SMS_STORE_SM = 0,
+ RIL_UTIL_SMS_STORE_ME = 1,
+ RIL_UTIL_SMS_STORE_MT = 2,
+ RIL_UTIL_SMS_STORE_SR = 3,
+ RIL_UTIL_SMS_STORE_BM = 4,
+};
+
+/* 3GPP TS 27.007 Release 8 Section 5.5 */
+enum at_util_charset {
+ RIL_UTIL_CHARSET_GSM = 0x1,
+ RIL_UTIL_CHARSET_HEX = 0x2,
+ RIL_UTIL_CHARSET_IRA = 0x4,
+ RIL_UTIL_CHARSET_PCCP437 = 0x8,
+ RIL_UTIL_CHARSET_PCDN = 0x10,
+ RIL_UTIL_CHARSET_UCS2 = 0x20,
+ RIL_UTIL_CHARSET_UTF8 = 0x40,
+ RIL_UTIL_CHARSET_8859_1 = 0x80,
+ RIL_UTIL_CHARSET_8859_2 = 0x100,
+ RIL_UTIL_CHARSET_8859_3 = 0x200,
+ RIL_UTIL_CHARSET_8859_4 = 0x400,
+ RIL_UTIL_CHARSET_8859_5 = 0x800,
+ RIL_UTIL_CHARSET_8859_6 = 0x1000,
+ RIL_UTIL_CHARSET_8859_C = 0x2000,
+ RIL_UTIL_CHARSET_8859_A = 0x4000,
+ RIL_UTIL_CHARSET_8859_G = 0x8000,
+ RIL_UTIL_CHARSET_8859_H = 0x10000,
+};
+
+struct ril_sim_data {
+ struct ofono_modem *modem;
+ GRil *gril;
+ ofono_sim_state_event_cb_t ril_state_watch;
+};
+
+struct ril_gprs_context_data {
+ GRil *gril;
+ struct ofono_modem *modem;
+ enum ofono_gprs_context_type type;
+};
+
+struct ril_voicecall_driver_data {
+ GRil *gril;
+ struct ofono_modem *modem;
+};
+
+struct ril_gprs_driver_data {
+ GRil *gril;
+ struct ofono_modem *modem;
+};
+
+struct ril_radio_settings_driver_data {
+ GRil *gril;
+ struct ofono_modem *modem;
+};
+
+typedef void (*ril_util_sim_inserted_cb_t)(gboolean present, void *userdata);
+
+void decode_ril_error(struct ofono_error *error, const char *final);
+gint ril_util_call_compare_by_status(gconstpointer a, gconstpointer b);
+gint ril_util_call_compare_by_phone_number(gconstpointer a, gconstpointer b);
+gint ril_util_call_compare_by_id(gconstpointer a, gconstpointer b);
+gint ril_util_call_compare(gconstpointer a, gconstpointer b);
+gchar *ril_util_get_netmask(const char *address);
+
+struct ril_util_sim_state_query *ril_util_sim_state_query_new(GRil *ril,
+ guint interval, guint num_times,
+ ril_util_sim_inserted_cb_t cb,
+ void *userdata,
+ GDestroyNotify destroy);
+void ril_util_sim_state_query_free(struct ril_util_sim_state_query *req);
+
+struct cb_data {
+ void *cb;
+ void *data;
+ void *user;
+};
+
+static inline struct cb_data *cb_data_new(void *cb, void *data, void *user)
+{
+ struct cb_data *ret;
+
+ ret = g_new0(struct cb_data, 1);
+ ret->cb = cb;
+ ret->data = data;
+ ret->user = user;
+
+ return ret;
+}
+
+static inline int ril_util_convert_signal_strength(int strength)
+{
+ int result;
+
+ if (strength == 99)
+ result = -1;
+ else
+ result = (strength * 100) / 31;
+
+ return result;
+}
+
+#define DECLARE_FAILURE(e) \
+ struct ofono_error e; \
+ e.type = OFONO_ERROR_TYPE_FAILURE; \
+ e.error = 0 \
+
+#define CALLBACK_WITH_FAILURE(cb, args...) \
+ do { \
+ struct ofono_error cb_e; \
+ cb_e.type = OFONO_ERROR_TYPE_FAILURE; \
+ cb_e.error = 0; \
+ \
+ cb(&cb_e, ##args); \
+ } while (0) \
+
+#define CALLBACK_WITH_SUCCESS(f, args...) \
+ do { \
+ struct ofono_error e; \
+ e.type = OFONO_ERROR_TYPE_NO_ERROR; \
+ e.error = 0; \
+ f(&e, ##args); \
+ } while (0)
+
+#endif /* RILUTIL_H */
diff --git a/drivers/rilmodem/sim.c b/drivers/rilmodem/sim.c
new file mode 100644
index 00000000..178cb97b
--- /dev/null
+++ b/drivers/rilmodem/sim.c
@@ -0,0 +1,1200 @@
+/*
+ *
+ * oFono - Open Source Telephony - RIL Modem Support
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2013 Canonical, Ltd. All rights reserved.
+ * Copyright (C) 2015 Ratchanan Srirattanamet.
+ *
+ * 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/sim.h>
+
+#include "ofono.h"
+
+#include "simutil.h"
+#include "util.h"
+
+#include "gril.h"
+#include "grilutil.h"
+#include "parcel.h"
+#include "ril_constants.h"
+#include "rilmodem.h"
+
+#include "grilreply.h"
+#include "grilrequest.h"
+#include "grilunsol.h"
+
+#include "drivers/infineonmodem/infineon_constants.h"
+
+/* Number of passwords in EPINC response */
+#define MTK_EPINC_NUM_PASSWD 4
+
+/*
+ * Based on ../drivers/atmodem/sim.c.
+ *
+ * TODO:
+ * 1. Defines constants for hex literals
+ * 2. Document P1-P3 usage (+CSRM)
+ */
+
+/*
+ * TODO: CDMA/IMS
+ *
+ * This code currently only grabs the AID/application ID from
+ * the gsm_umts application on the SIM card. This code will
+ * need to be modified for CDMA support, and possibly IMS-based
+ * applications. In this case, app_id should be changed to an
+ * array or HashTable of app_status structures.
+ *
+ * The same applies to the app_type.
+ */
+
+static void ril_pin_change_state(struct ofono_sim *sim,
+ enum ofono_sim_password_type passwd_type,
+ int enable, const char *passwd,
+ ofono_sim_lock_unlock_cb_t cb, void *data);
+
+struct sim_data {
+ GRil *ril;
+ enum ofono_ril_vendor vendor;
+ gchar *aid_str;
+ guint app_type;
+ gchar *app_str;
+ guint app_index;
+ enum ofono_sim_password_type passwd_type;
+ int retries[OFONO_SIM_PASSWORD_INVALID];
+ enum ofono_sim_password_type passwd_state;
+ struct ofono_modem *modem;
+ ofono_sim_state_event_cb_t ril_state_watch;
+ ofono_bool_t unlock_pending;
+};
+
+struct change_state_cbd {
+ struct ofono_sim *sim;
+ enum ofono_sim_password_type passwd_type;
+ int enable;
+ const char *passwd;
+ ofono_sim_lock_unlock_cb_t cb;
+ void *data;
+};
+
+static void send_get_sim_status(struct ofono_sim *sim);
+
+static void ril_file_info_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_sim_file_info_cb_t cb = cbd->cb;
+ struct sim_data *sd = cbd->user;
+ struct ofono_error error;
+ gboolean ok = FALSE;
+ int sw1, sw2;
+ int flen = 0, rlen = 0, str = 0;
+ guchar access[3] = { 0x00, 0x00, 0x00 };
+ guchar file_status = EF_STATUS_VALID;
+ struct reply_sim_io *reply = NULL;
+
+ /* Error, and no data */
+ if (message->error != RIL_E_SUCCESS && message->buf_len == 0) {
+ ofono_error("%s: Reply failure: %s", __func__,
+ ril_error_to_string(message->error));
+ decode_ril_error(&error, "FAIL");
+ goto error;
+ }
+
+ /*
+ * The reply can have event data even when message->error is not zero
+ * in mako.
+ */
+ reply = g_ril_reply_parse_sim_io(sd->ril, message);
+ if (reply == NULL) {
+ decode_ril_error(&error, "FAIL");
+ goto error;
+ }
+
+ sw1 = reply->sw1;
+ sw2 = reply->sw2;
+
+ /*
+ * SIM app file not found || USIM app file not found
+ * See 3gpp TS 51.011, 9.4.4, and ETSI TS 102 221, 10.2.1.5.3
+ * This can happen with result SUCCESS (maguro) or GENERIC_FAILURE
+ * (mako)
+ */
+ if ((sw1 == 0x94 && sw2 == 0x04) || (sw1 == 0x6A && sw2 == 0x82)) {
+ DBG("File not found. Error %s",
+ ril_error_to_string(message->error));
+ decode_ril_error(&error, "FAIL");
+ goto error;
+ }
+
+ if (message->error == RIL_E_SUCCESS) {
+ decode_ril_error(&error, "OK");
+ } else {
+ ofono_error("%s: Reply failure: %s, %02x, %02x", __func__,
+ ril_error_to_string(message->error), sw1, sw2);
+ decode_ril_error(&error, "FAIL");
+ goto error;
+ }
+
+ if ((sw1 != 0x90 && sw1 != 0x91 && sw1 != 0x92 && sw1 != 0x9f) ||
+ (sw1 == 0x90 && sw2 != 0x00)) {
+ ofono_error("Error reply, invalid values: sw1: %02x sw2: %02x",
+ sw1, sw2);
+
+ /* TODO: fix decode_ril_error to take type & error */
+
+ error.type = OFONO_ERROR_TYPE_SIM;
+ error.error = (sw1 << 8) | sw2;
+
+ goto error;
+ }
+
+ if (reply->hex_len) {
+ if (reply->hex_response[0] == 0x62) {
+ ok = sim_parse_3g_get_response(reply->hex_response,
+ reply->hex_len,
+ &flen, &rlen, &str,
+ access, NULL);
+ } else {
+ ok = sim_parse_2g_get_response(reply->hex_response,
+ reply->hex_len,
+ &flen, &rlen, &str,
+ access, &file_status);
+ }
+ }
+
+ if (!ok) {
+ ofono_error("%s: parse response failed", __func__);
+ decode_ril_error(&error, "FAIL");
+ goto error;
+ }
+
+ cb(&error, flen, str, rlen, access, file_status, cbd->data);
+
+ g_ril_reply_free_sim_io(reply);
+
+ return;
+
+error:
+ g_ril_reply_free_sim_io(reply);
+
+ cb(&error, -1, -1, -1, NULL, EF_STATUS_INVALIDATED, cbd->data);
+}
+
+static void ril_sim_read_info(struct ofono_sim *sim, int fileid,
+ const unsigned char *path,
+ unsigned int path_len,
+ ofono_sim_file_info_cb_t cb, void *data)
+{
+ struct sim_data *sd = ofono_sim_get_data(sim);
+ struct cb_data *cbd = cb_data_new(cb, data, sd);
+ struct parcel rilp;
+ struct req_sim_read_info req;
+ guint ret = 0;
+
+ DBG("file %04x", fileid);
+
+ req.app_type = sd->app_type;
+ req.aid_str = sd->aid_str;
+ req.fileid = fileid;
+ req.path = path;
+ req.path_len = path_len;
+
+ if (!g_ril_request_sim_read_info(sd->ril,
+ &req,
+ &rilp)) {
+ ofono_error("Couldn't build SIM read info request");
+ goto error;
+ }
+
+ g_ril_append_print_buf(sd->ril,
+ "%s0,0,15,(null),pin2=(null),aid=%s)",
+ print_buf,
+ sd->aid_str);
+
+ ret = g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp,
+ ril_file_info_cb, cbd, g_free);
+
+error:
+ if (ret == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL,
+ EF_STATUS_INVALIDATED, data);
+ }
+}
+
+static void ril_file_io_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_sim_read_cb_t cb = cbd->cb;
+ struct sim_data *sd = cbd->user;
+ struct ofono_error error;
+ struct reply_sim_io *reply;
+
+ if (message->error == RIL_E_SUCCESS) {
+ decode_ril_error(&error, "OK");
+ } else {
+ ofono_error("RILD reply failure: %s",
+ ril_error_to_string(message->error));
+ goto error;
+ }
+
+ reply = g_ril_reply_parse_sim_io(sd->ril, message);
+ if (reply == NULL) {
+ ofono_error("Can't parse SIM IO response from RILD");
+ goto error;
+ }
+
+ if (reply->hex_len == 0) {
+ ofono_error("Null SIM IO response from RILD");
+ g_ril_reply_free_sim_io(reply);
+ goto error;
+ }
+
+ cb(&error, reply->hex_response, reply->hex_len, cbd->data);
+
+ g_ril_reply_free_sim_io(reply);
+
+ return;
+
+error:
+ decode_ril_error(&error, "FAIL");
+ cb(&error, NULL, 0, cbd->data);
+}
+
+static void ril_file_write_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_sim_write_cb_t cb = cbd->cb;
+ struct sim_data *sd = cbd->user;
+ struct reply_sim_io *reply;
+ int sw1, sw2;
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("%s: RILD reply failure: %s",
+ __func__, ril_error_to_string(message->error));
+ goto error;
+ }
+
+ reply = g_ril_reply_parse_sim_io(sd->ril, message);
+ if (reply == NULL) {
+ ofono_error("%s: Can't parse SIM IO response", __func__);
+ goto error;
+ }
+
+ sw1 = reply->sw1;
+ sw2 = reply->sw2;
+
+ g_ril_reply_free_sim_io(reply);
+
+ if ((sw1 != 0x90 && sw1 != 0x91 && sw1 != 0x92 && sw1 != 0x9f) ||
+ (sw1 == 0x90 && sw2 != 0x00)) {
+ struct ofono_error error;
+
+ ofono_error("%s: error sw1 %02x sw2 %02x", __func__, sw1, sw2);
+
+ error.type = OFONO_ERROR_TYPE_SIM;
+ error.error = (sw1 << 8) | sw2;
+
+ cb(&error, cbd->data);
+
+ return;
+ }
+
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+
+ return;
+
+error:
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void ril_sim_read_binary(struct ofono_sim *sim, int fileid,
+ int start, int length,
+ const unsigned char *path,
+ unsigned int path_len,
+ ofono_sim_read_cb_t cb, void *data)
+{
+ struct sim_data *sd = ofono_sim_get_data(sim);
+ struct cb_data *cbd = cb_data_new(cb, data, sd);
+ struct parcel rilp;
+ struct req_sim_read_binary req;
+ gint ret = 0;
+
+ DBG("file %04x", fileid);
+
+ req.app_type = sd->app_type;
+ req.aid_str = sd->aid_str;
+ req.fileid = fileid;
+ req.path = path;
+ req.path_len = path_len;
+ req.start = start;
+ req.length = length;
+
+ if (!g_ril_request_sim_read_binary(sd->ril,
+ &req,
+ &rilp)) {
+ ofono_error("Couldn't build SIM read binary request");
+ goto error;
+ }
+
+ g_ril_append_print_buf(sd->ril,
+ "%s%d,%d,%d,(null),pin2=(null),aid=%s)",
+ print_buf,
+ (start >> 8),
+ (start & 0xff),
+ length,
+ sd->aid_str);
+
+ ret = g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp,
+ ril_file_io_cb, cbd, g_free);
+error:
+ if (ret == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
+ }
+}
+
+static void ril_sim_read_record(struct ofono_sim *sim, int fileid,
+ int record, int length,
+ const unsigned char *path,
+ unsigned int path_len,
+ ofono_sim_read_cb_t cb, void *data)
+{
+ struct sim_data *sd = ofono_sim_get_data(sim);
+ struct cb_data *cbd = cb_data_new(cb, data, sd);
+ struct parcel rilp;
+ struct req_sim_read_record req;
+ guint ret = 0;
+
+ DBG("file %04x", fileid);
+
+ req.app_type = sd->app_type;
+ req.aid_str = sd->aid_str;
+ req.fileid = fileid;
+ req.path = path;
+ req.path_len = path_len;
+ req.record = record;
+ req.length = length;
+
+ if (!g_ril_request_sim_read_record(sd->ril,
+ &req,
+ &rilp)) {
+ ofono_error("Couldn't build SIM read record request");
+ goto error;
+ }
+
+ g_ril_append_print_buf(sd->ril,
+ "%s%d,%d,%d,(null),pin2=(null),aid=%s)",
+ print_buf,
+ record,
+ 4,
+ length,
+ sd->aid_str);
+
+ ret = g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp,
+ ril_file_io_cb, cbd, g_free);
+
+error:
+ if (ret == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, NULL, 0, data);
+ }
+}
+
+static void ril_sim_update_binary(struct ofono_sim *sim, int fileid,
+ int start, int length,
+ const unsigned char *value,
+ const unsigned char *path,
+ unsigned int path_len,
+ ofono_sim_write_cb_t cb, void *data)
+{
+ struct sim_data *sd = ofono_sim_get_data(sim);
+ struct cb_data *cbd = cb_data_new(cb, data, sd);
+ struct parcel rilp;
+ struct req_sim_write_binary req;
+ guint ret = 0;
+
+ DBG("file 0x%04x", fileid);
+
+ req.app_type = sd->app_type;
+ req.aid_str = sd->aid_str;
+ req.fileid = fileid;
+ req.path = path;
+ req.path_len = path_len;
+ req.start = start;
+ req.length = length;
+ req.data = value;
+
+ if (!g_ril_request_sim_write_binary(sd->ril, &req, &rilp)) {
+ ofono_error("%s: Couldn't build SIM write request", __func__);
+ goto error;
+ }
+
+ ret = g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp,
+ ril_file_write_cb, cbd, g_free);
+
+error:
+ if (ret == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static void update_record(struct ofono_sim *sim, int fileid,
+ enum req_record_access_mode mode,
+ int record, int length,
+ const unsigned char *value,
+ const unsigned char *path,
+ unsigned int path_len,
+ ofono_sim_write_cb_t cb, void *data)
+{
+ struct sim_data *sd = ofono_sim_get_data(sim);
+ struct cb_data *cbd = cb_data_new(cb, data, sd);
+ struct parcel rilp;
+ struct req_sim_write_record req;
+ guint ret = 0;
+
+ DBG("file 0x%04x", fileid);
+
+ req.app_type = sd->app_type;
+ req.aid_str = sd->aid_str;
+ req.fileid = fileid;
+ req.path = path;
+ req.path_len = path_len;
+ req.mode = mode;
+ req.record = record;
+ req.length = length;
+ req.data = value;
+
+ if (!g_ril_request_sim_write_record(sd->ril, &req, &rilp)) {
+ ofono_error("%s: Couldn't build SIM write request", __func__);
+ goto error;
+ }
+
+ ret = g_ril_send(sd->ril, RIL_REQUEST_SIM_IO, &rilp,
+ ril_file_write_cb, cbd, g_free);
+
+error:
+ if (ret == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static void ril_sim_update_record(struct ofono_sim *sim, int fileid,
+ int record, int length,
+ const unsigned char *value,
+ const unsigned char *path,
+ unsigned int path_len,
+ ofono_sim_write_cb_t cb, void *data)
+{
+ update_record(sim, fileid, GRIL_REC_ACCESS_MODE_ABSOLUTE, record,
+ length, value, path, path_len, cb, data);
+}
+
+static void ril_sim_update_cyclic(struct ofono_sim *sim, int fileid,
+ int length, const unsigned char *value,
+ const unsigned char *path,
+ unsigned int path_len,
+ ofono_sim_write_cb_t cb, void *data)
+{
+ /* Only mode valid for cyclic files is PREVIOUS */
+ update_record(sim, fileid, GRIL_REC_ACCESS_MODE_PREVIOUS, 0,
+ length, value, path, path_len, cb, data);
+}
+
+static void ril_imsi_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_sim_imsi_cb_t cb = cbd->cb;
+ struct sim_data *sd = cbd->user;
+ struct ofono_error error;
+ gchar *imsi;
+
+ if (message->error == RIL_E_SUCCESS) {
+ DBG("GET IMSI reply - OK");
+ decode_ril_error(&error, "OK");
+ } else {
+ ofono_error("Reply failure: %s",
+ ril_error_to_string(message->error));
+ goto error;
+ }
+
+ imsi = g_ril_reply_parse_imsi(sd->ril, message);
+ if (imsi == NULL) {
+ ofono_error("Error empty IMSI");
+ goto error;
+ }
+
+ cb(&error, imsi, cbd->data);
+ g_free(imsi);
+
+ return;
+
+error:
+ decode_ril_error(&error, "FAIL");
+ cb(&error, NULL, cbd->data);
+}
+
+static void ril_read_imsi(struct ofono_sim *sim, ofono_sim_imsi_cb_t cb,
+ void *data)
+{
+ struct sim_data *sd = ofono_sim_get_data(sim);
+ struct cb_data *cbd = cb_data_new(cb, data, sd);
+ struct parcel rilp;
+
+ g_ril_request_read_imsi(sd->ril, sd->aid_str, &rilp);
+
+ if (g_ril_send(sd->ril, RIL_REQUEST_GET_IMSI, &rilp,
+ ril_imsi_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, NULL, data);
+ }
+}
+
+static void configure_active_app(struct sim_data *sd,
+ struct reply_sim_app *app,
+ guint index)
+{
+ g_free(sd->aid_str);
+ g_free(sd->app_str);
+ sd->app_type = app->app_type;
+ sd->aid_str = g_strdup(app->aid_str);
+ sd->app_str = g_strdup(app->app_str);
+ sd->app_index = index;
+
+ DBG("setting aid_str (AID) to: %s", sd->aid_str);
+ switch (app->app_state) {
+ case RIL_APPSTATE_PIN:
+ sd->passwd_state = OFONO_SIM_PASSWORD_SIM_PIN;
+ break;
+ case RIL_APPSTATE_PUK:
+ sd->passwd_state = OFONO_SIM_PASSWORD_SIM_PUK;
+ break;
+ case RIL_APPSTATE_SUBSCRIPTION_PERSO:
+ switch (app->perso_substate) {
+ case RIL_PERSOSUBSTATE_SIM_NETWORK:
+ sd->passwd_state = OFONO_SIM_PASSWORD_PHNET_PIN;
+ break;
+ case RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET:
+ sd->passwd_state = OFONO_SIM_PASSWORD_PHNETSUB_PIN;
+ break;
+ case RIL_PERSOSUBSTATE_SIM_CORPORATE:
+ sd->passwd_state = OFONO_SIM_PASSWORD_PHCORP_PIN;
+ break;
+ case RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER:
+ sd->passwd_state = OFONO_SIM_PASSWORD_PHSP_PIN;
+ break;
+ case RIL_PERSOSUBSTATE_SIM_SIM:
+ sd->passwd_state = OFONO_SIM_PASSWORD_PHSIM_PIN;
+ break;
+ case RIL_PERSOSUBSTATE_SIM_NETWORK_PUK:
+ sd->passwd_state = OFONO_SIM_PASSWORD_PHNET_PUK;
+ break;
+ case RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK:
+ sd->passwd_state = OFONO_SIM_PASSWORD_PHNETSUB_PUK;
+ break;
+ case RIL_PERSOSUBSTATE_SIM_CORPORATE_PUK:
+ sd->passwd_state = OFONO_SIM_PASSWORD_PHCORP_PUK;
+ break;
+ case RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK:
+ sd->passwd_state = OFONO_SIM_PASSWORD_PHSP_PUK;
+ break;
+ case RIL_PERSOSUBSTATE_SIM_SIM_PUK:
+ sd->passwd_state = OFONO_SIM_PASSWORD_PHFSIM_PUK;
+ break;
+ default:
+ sd->passwd_state = OFONO_SIM_PASSWORD_NONE;
+ break;
+ };
+ break;
+ case RIL_APPSTATE_READY:
+ sd->passwd_state = OFONO_SIM_PASSWORD_NONE;
+ break;
+ case RIL_APPSTATE_UNKNOWN:
+ case RIL_APPSTATE_DETECTED:
+ default:
+ sd->passwd_state = OFONO_SIM_PASSWORD_INVALID;
+ break;
+ }
+}
+
+static void sim_status_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct ofono_sim *sim = user_data;
+ struct sim_data *sd = ofono_sim_get_data(sim);
+ struct reply_sim_status *status;
+ guint search_index;
+
+ status = g_ril_reply_parse_sim_status(sd->ril, message);
+ if (status == NULL) {
+ ofono_error("%s: Cannot parse SIM status reply", __func__);
+ return;
+ }
+
+ DBG("SIM status is %u", status->card_state);
+
+ if (status->card_state == RIL_CARDSTATE_PRESENT)
+ ofono_sim_inserted_notify(sim, TRUE);
+ else if (status && status->card_state == RIL_CARDSTATE_ABSENT)
+ ofono_sim_inserted_notify(sim, FALSE);
+ else
+ ofono_error("%s: bad SIM state (%u)",
+ __func__, status->card_state);
+
+ if (status->card_state == RIL_CARDSTATE_PRESENT) {
+ /*
+ * TODO(CDMA): need some kind of logic
+ * to set the correct app_index
+ */
+ search_index = status->gsm_umts_index;
+ if (search_index < status->num_apps) {
+ struct reply_sim_app *app = status->apps[search_index];
+
+ if (app->app_type != RIL_APPTYPE_UNKNOWN) {
+ /*
+ * We cache the current password state. Ideally
+ * this should be done by issuing a
+ * GET_SIM_STATUS request from
+ * ril_query_passwd_state, which is called by
+ * the core after sending a password, but
+ * unfortunately the response to GET_SIM_STATUS
+ * is not reliable in mako when sent just after
+ * sending the password. Some time is needed
+ * before the modem refreshes its internal
+ * state, and when it does it sends a
+ * SIM_STATUS_CHANGED event. In that moment we
+ * retrieve the status and this function is
+ * executed. We call __ofono_sim_recheck_pin as
+ * it is the only way to indicate the core to
+ * call query_passwd_state again. An option
+ * that can be explored in the future is wait
+ * before invoking core callback for send_passwd
+ * until we know the real password state.
+ */
+ configure_active_app(sd, app, search_index);
+ DBG("passwd_state: %d", sd->passwd_state);
+
+ /*
+ * Note: There doesn't seem to be any other way
+ * to force the core SIM code to recheck the
+ * PIN. This call causes the core to call this
+ * atom's query_passwd() function.
+ */
+ __ofono_sim_recheck_pin(sim);
+ }
+ }
+ }
+
+ g_ril_reply_free_sim_status(status);
+}
+
+static void send_get_sim_status(struct ofono_sim *sim)
+{
+ struct sim_data *sd = ofono_sim_get_data(sim);
+
+ g_ril_send(sd->ril, RIL_REQUEST_GET_SIM_STATUS, NULL,
+ sim_status_cb, sim, NULL);
+}
+
+static void ril_sim_status_changed(struct ril_msg *message, gpointer user_data)
+{
+ struct ofono_sim *sim = (struct ofono_sim *) user_data;
+ struct sim_data *sd = ofono_sim_get_data(sim);
+
+ DBG("");
+
+ g_ril_print_unsol_no_args(sd->ril, message);
+
+ send_get_sim_status(sim);
+}
+
+static void inf_pin_retries_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_sim_pin_retries_cb_t cb = cbd->cb;
+ struct sim_data *sd = cbd->user;
+ struct reply_oem_hook *reply = NULL;
+ int32_t *ret_data;
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("Reply failure: %s",
+ ril_error_to_string(message->error));
+ goto error;
+ }
+
+ reply = g_ril_reply_oem_hook_raw(sd->ril, message);
+ if (reply == NULL) {
+ ofono_error("%s: parse error", __func__);
+ goto error;
+ }
+
+ if (reply->length < 5 * (int) sizeof(int32_t)) {
+ ofono_error("%s: reply too small", __func__);
+ goto error;
+ }
+
+ /* First integer is INF_RIL_REQUEST_OEM_GET_REMAIN_SIM_PIN_ATTEMPTS */
+ ret_data = reply->data;
+ sd->retries[OFONO_SIM_PASSWORD_SIM_PIN] = *(++ret_data);
+ sd->retries[OFONO_SIM_PASSWORD_SIM_PIN2] = *(++ret_data);
+ sd->retries[OFONO_SIM_PASSWORD_SIM_PUK] = *(++ret_data);
+ sd->retries[OFONO_SIM_PASSWORD_SIM_PUK2] = *(++ret_data);
+
+ g_ril_reply_free_oem_hook(reply);
+ CALLBACK_WITH_SUCCESS(cb, sd->retries, cbd->data);
+
+ return;
+
+error:
+ g_ril_reply_free_oem_hook(reply);
+ CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static void mtk_pin_retries_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_sim_pin_retries_cb_t cb = cbd->cb;
+ struct sim_data *sd = cbd->user;
+ struct parcel_str_array *str_arr = NULL;
+ int pin[MTK_EPINC_NUM_PASSWD];
+ int num_pin;
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("Reply failure: %s",
+ ril_error_to_string(message->error));
+ goto error;
+ }
+
+ str_arr = g_ril_reply_oem_hook_strings(sd->ril, message);
+ if (str_arr == NULL || str_arr->num_str < 1) {
+ ofono_error("%s: parse error", __func__);
+ goto error;
+ }
+
+ num_pin = sscanf(str_arr->str[0], "+EPINC:%d,%d,%d,%d",
+ &pin[0], &pin[1], &pin[2], &pin[3]);
+
+ if (num_pin != MTK_EPINC_NUM_PASSWD) {
+ ofono_error("%s: failed parsing %s", __func__, str_arr->str[0]);
+ goto error;
+ }
+
+ sd->retries[OFONO_SIM_PASSWORD_SIM_PIN] = pin[0];
+ sd->retries[OFONO_SIM_PASSWORD_SIM_PIN2] = pin[1];
+ sd->retries[OFONO_SIM_PASSWORD_SIM_PUK] = pin[2];
+ sd->retries[OFONO_SIM_PASSWORD_SIM_PUK2] = pin[3];
+
+ parcel_free_str_array(str_arr);
+ CALLBACK_WITH_SUCCESS(cb, sd->retries, cbd->data);
+ return;
+
+error:
+ parcel_free_str_array(str_arr);
+ CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static void ril_query_pin_retries(struct ofono_sim *sim,
+ ofono_sim_pin_retries_cb_t cb,
+ void *data)
+{
+ struct sim_data *sd = ofono_sim_get_data(sim);
+
+ DBG("");
+
+ if (sd->vendor == OFONO_RIL_VENDOR_INFINEON) {
+ struct cb_data *cbd = cb_data_new(cb, data, sd);
+ struct parcel rilp;
+ int32_t oem_req =
+ INF_RIL_REQUEST_OEM_GET_REMAIN_SIM_PIN_ATTEMPTS;
+
+ g_ril_request_oem_hook_raw(sd->ril, &oem_req,
+ sizeof(oem_req), &rilp);
+
+ /* Send request to RIL */
+ if (g_ril_send(sd->ril, RIL_REQUEST_OEM_HOOK_RAW, &rilp,
+ inf_pin_retries_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, NULL, data);
+ }
+ } else if (sd->vendor == OFONO_RIL_VENDOR_MTK) {
+ struct cb_data *cbd = cb_data_new(cb, data, sd);
+ struct parcel rilp;
+ const char *at_epinc[] = { "AT+EPINC", "+EPINC:" };
+
+ g_ril_request_oem_hook_strings(sd->ril, at_epinc,
+ G_N_ELEMENTS(at_epinc), &rilp);
+
+ if (g_ril_send(sd->ril, RIL_REQUEST_OEM_HOOK_STRINGS, &rilp,
+ mtk_pin_retries_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, NULL, data);
+ }
+ } else {
+ CALLBACK_WITH_SUCCESS(cb, sd->retries, data);
+ }
+}
+
+static void ril_query_passwd_state(struct ofono_sim *sim,
+ ofono_sim_passwd_cb_t cb, void *data)
+{
+ struct sim_data *sd = ofono_sim_get_data(sim);
+ DBG("passwd_state %u", sd->passwd_state);
+
+ if (sd->passwd_state == OFONO_SIM_PASSWORD_INVALID)
+ CALLBACK_WITH_FAILURE(cb, -1, data);
+ else
+ CALLBACK_WITH_SUCCESS(cb, sd->passwd_state, data);
+}
+
+static void ril_pin_change_state_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_sim_lock_unlock_cb_t cb = cbd->cb;
+ struct ofono_sim *sim = cbd->user;
+ struct sim_data *sd = ofono_sim_get_data(sim);
+ int *retries;
+ /*
+ * There is no reason to ask SIM status until
+ * unsolicited sim status change indication
+ * Looks like state does not change before that.
+ */
+
+ DBG("Enter password: type %d, result %d",
+ sd->passwd_type, message->error);
+
+ retries = g_ril_reply_parse_retries(sd->ril, message, sd->passwd_type);
+ if (retries != NULL) {
+ memcpy(sd->retries, retries, sizeof(sd->retries));
+ g_free(retries);
+ }
+
+ /* TODO: re-factor to not use macro for FAILURE;
+ doesn't return error! */
+ if (message->error == RIL_E_SUCCESS) {
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ } else {
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ /*
+ * Refresh passwd_state (not needed if the unlock is
+ * successful, as an event will refresh the state in that case)
+ */
+ send_get_sim_status(sim);
+ }
+}
+
+static void ril_pin_send(struct ofono_sim *sim, const char *passwd,
+ ofono_sim_lock_unlock_cb_t cb, void *data)
+{
+ /*
+ * TODO: This function is supposed to enter the pending password, which
+ * might be also PIN2. So we must check the pending PIN in the future.
+ */
+
+ struct sim_data *sd = ofono_sim_get_data(sim);
+ struct cb_data *cbd = cb_data_new(cb, data, sim);
+ struct parcel rilp;
+
+ sd->passwd_type = OFONO_SIM_PASSWORD_SIM_PIN;
+
+ g_ril_request_pin_send(sd->ril,
+ passwd,
+ sd->aid_str,
+ &rilp);
+
+ if (g_ril_send(sd->ril, RIL_REQUEST_ENTER_SIM_PIN, &rilp,
+ ril_pin_change_state_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static void enter_pin_done(const struct ofono_error *error, void *data)
+{
+ struct change_state_cbd *csd = data;
+ struct sim_data *sd = ofono_sim_get_data(csd->sim);
+
+ if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ ofono_error("%s: wrong password", __func__);
+ sd->unlock_pending = FALSE;
+ CALLBACK_WITH_FAILURE(csd->cb, csd->data);
+ } else {
+ ril_pin_change_state(csd->sim, csd->passwd_type, csd->enable,
+ csd->passwd, csd->cb, csd->data);
+ }
+
+ g_free(csd);
+}
+
+static void ril_pin_change_state(struct ofono_sim *sim,
+ enum ofono_sim_password_type passwd_type,
+ int enable, const char *passwd,
+ ofono_sim_lock_unlock_cb_t cb, void *data)
+{
+ struct sim_data *sd = ofono_sim_get_data(sim);
+ struct cb_data *cbd;
+ struct parcel rilp;
+ struct req_pin_change_state req;
+ int ret = 0;
+
+ /*
+ * If we want to unlock a password that has not been entered yet,
+ * we enter it before trying to unlock. We need sd->unlock_pending as
+ * the password still has not yet been refreshed when this function is
+ * called from enter_pin_done().
+ */
+ if (ofono_sim_get_password_type(sim) == passwd_type
+ && enable == FALSE && sd->unlock_pending == FALSE) {
+ struct change_state_cbd *csd = g_malloc0(sizeof(*csd));
+ csd->sim = sim;
+ csd->passwd_type = passwd_type;
+ csd->enable = enable;
+ csd->passwd = passwd;
+ csd->cb = cb;
+ csd->data = data;
+ sd->unlock_pending = TRUE;
+
+ ril_pin_send(sim, passwd, enter_pin_done, csd);
+
+ return;
+ }
+
+ sd->unlock_pending = FALSE;
+
+ cbd = cb_data_new(cb, data, sim);
+
+ sd->passwd_type = passwd_type;
+
+ req.aid_str = sd->aid_str;
+ req.passwd_type = passwd_type;
+ req.enable = enable;
+ req.passwd = passwd;
+
+ if (!g_ril_request_pin_change_state(sd->ril,
+ &req,
+ &rilp)) {
+ ofono_error("Couldn't build pin change state request");
+ goto error;
+ }
+
+ ret = g_ril_send(sd->ril, RIL_REQUEST_SET_FACILITY_LOCK, &rilp,
+ ril_pin_change_state_cb, cbd, g_free);
+
+error:
+ if (ret == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static void ril_pin_send_puk(struct ofono_sim *sim,
+ const char *puk, const char *passwd,
+ ofono_sim_lock_unlock_cb_t cb, void *data)
+{
+ struct sim_data *sd = ofono_sim_get_data(sim);
+ struct cb_data *cbd = cb_data_new(cb, data, sim);
+ struct parcel rilp;
+
+ sd->passwd_type = OFONO_SIM_PASSWORD_SIM_PUK;
+
+ g_ril_request_pin_send_puk(sd->ril,
+ puk,
+ passwd,
+ sd->aid_str,
+ &rilp);
+
+ if (g_ril_send(sd->ril, RIL_REQUEST_ENTER_SIM_PUK, &rilp,
+ ril_pin_change_state_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static void ril_change_passwd(struct ofono_sim *sim,
+ enum ofono_sim_password_type passwd_type,
+ const char *old_passwd, const char *new_passwd,
+ ofono_sim_lock_unlock_cb_t cb, void *data)
+{
+ struct sim_data *sd = ofono_sim_get_data(sim);
+ struct cb_data *cbd = cb_data_new(cb, data, sim);
+ struct parcel rilp;
+ int request = RIL_REQUEST_CHANGE_SIM_PIN;
+
+ sd->passwd_type = passwd_type;
+
+ g_ril_request_change_passwd(sd->ril,
+ old_passwd,
+ new_passwd,
+ sd->aid_str,
+ &rilp);
+
+ if (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2)
+ request = RIL_REQUEST_CHANGE_SIM_PIN2;
+
+ if (g_ril_send(sd->ril, request, &rilp, ril_pin_change_state_cb,
+ cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static gboolean listen_and_get_sim_status(gpointer user)
+{
+ struct ofono_sim *sim = user;
+ struct sim_data *sd = ofono_sim_get_data(sim);
+
+ send_get_sim_status(sim);
+
+ g_ril_register(sd->ril, RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED,
+ (GRilNotifyFunc) ril_sim_status_changed, sim);
+
+ /* TODO: should we also register for RIL_UNSOL_SIM_REFRESH? */
+ return FALSE;
+}
+
+static gboolean ril_sim_register(gpointer user)
+{
+ struct ofono_sim *sim = user;
+ struct sim_data *sd = ofono_sim_get_data(sim);
+
+ DBG("");
+
+ ofono_sim_register(sim);
+
+ if (sd->ril_state_watch != NULL &&
+ !ofono_sim_add_state_watch(sim, sd->ril_state_watch,
+ sd->modem, NULL))
+ ofono_error("Error registering ril sim watch");
+
+ /*
+ * We use g_idle_add here to make sure that the presence of the SIM
+ * interface is signalled before signalling anything else from the said
+ * interface, as ofono_sim_register also uses g_idle_add.
+ */
+ g_idle_add(listen_and_get_sim_status, sim);
+
+ return FALSE;
+}
+
+static int ril_sim_probe(struct ofono_sim *sim, unsigned int vendor,
+ void *data)
+{
+ struct ril_sim_data *ril_data = data;
+ GRil *ril = ril_data->gril;
+ struct sim_data *sd;
+ int i;
+
+ sd = g_new0(struct sim_data, 1);
+ sd->ril = g_ril_clone(ril);
+ sd->vendor = vendor;
+ sd->aid_str = NULL;
+ sd->app_str = NULL;
+ sd->app_type = RIL_APPTYPE_UNKNOWN;
+ sd->passwd_state = OFONO_SIM_PASSWORD_NONE;
+ sd->passwd_type = OFONO_SIM_PASSWORD_NONE;
+ sd->modem = ril_data->modem;
+ sd->ril_state_watch = ril_data->ril_state_watch;
+
+ for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
+ sd->retries[i] = -1;
+
+ ofono_sim_set_data(sim, sd);
+
+ /*
+ * TODO: analyze if capability check is needed
+ * and/or timer should be adjusted.
+ *
+ * ofono_sim_register() needs to be called after the
+ * driver has been set in ofono_sim_create(), which
+ * calls this function. Most other drivers make some
+ * kind of capabilities query to the modem, and then
+ * call register in the callback; we use an idle event
+ * instead.
+ */
+ g_idle_add(ril_sim_register, sim);
+
+ return 0;
+}
+
+static void ril_sim_remove(struct ofono_sim *sim)
+{
+ struct sim_data *sd = ofono_sim_get_data(sim);
+
+ ofono_sim_set_data(sim, NULL);
+
+ g_ril_unref(sd->ril);
+ g_free(sd->aid_str);
+ g_free(sd->app_str);
+ g_free(sd);
+}
+
+static struct ofono_sim_driver driver = {
+ .name = RILMODEM,
+ .probe = ril_sim_probe,
+ .remove = ril_sim_remove,
+ .read_file_info = ril_sim_read_info,
+ .read_file_transparent = ril_sim_read_binary,
+ .read_file_linear = ril_sim_read_record,
+ .read_file_cyclic = ril_sim_read_record,
+ .write_file_transparent = ril_sim_update_binary,
+ .write_file_linear = ril_sim_update_record,
+ .write_file_cyclic = ril_sim_update_cyclic,
+ .read_imsi = ril_read_imsi,
+ .query_passwd_state = ril_query_passwd_state,
+ .send_passwd = ril_pin_send,
+ .query_pin_retries = ril_query_pin_retries,
+ .reset_passwd = ril_pin_send_puk,
+ .change_passwd = ril_change_passwd,
+ .lock = ril_pin_change_state,
+/*
+ * TODO: Implmenting PIN/PUK support requires defining
+ * the following driver methods.
+ *
+ * In the meanwhile, as long as the SIM card is present,
+ * and unlocked, the core SIM code will check for the
+ * presence of query_passwd_state, and if null, then the
+ * function sim_initialize_after_pin() is called.
+ *
+ * .query_locked = ril_pin_query_enabled,
+ */
+};
+
+void ril_sim_init(void)
+{
+ DBG("");
+ ofono_sim_driver_register(&driver);
+}
+
+void ril_sim_exit(void)
+{
+ ofono_sim_driver_unregister(&driver);
+}
diff --git a/drivers/rilmodem/sms.c b/drivers/rilmodem/sms.c
new file mode 100644
index 00000000..14816e5d
--- /dev/null
+++ b/drivers/rilmodem/sms.c
@@ -0,0 +1,315 @@
+/*
+ *
+ * oFono - Open Source Telephony - RIL Modem Support
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2012-2013 Canonical Ltd.
+ * Copyright (C) 2013 Jolla Ltd.
+ *
+ * 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 <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+#include <gril.h>
+#include <parcel.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/sms.h>
+#include "smsutil.h"
+#include "util.h"
+
+#include "rilmodem.h"
+#include "grilrequest.h"
+#include "grilreply.h"
+#include "grilunsol.h"
+
+struct sms_data {
+ GRil *ril;
+ unsigned int vendor;
+};
+
+static void ril_csca_set_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_sms_sca_set_cb_t cb = cbd->cb;
+ struct sms_data *sd = cbd->user;
+
+ if (message->error == RIL_E_SUCCESS) {
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ } else {
+ ofono_error("%s RILD reply failure: %s",
+ g_ril_request_id_to_string(sd->ril, message->req),
+ ril_error_to_string(message->error));
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ }
+}
+
+static void ril_csca_set(struct ofono_sms *sms,
+ const struct ofono_phone_number *sca,
+ ofono_sms_sca_set_cb_t cb, void *user_data)
+{
+ struct sms_data *sd = ofono_sms_get_data(sms);
+ struct cb_data *cbd = cb_data_new(cb, user_data, sd);
+ struct parcel rilp;
+
+ g_ril_request_set_smsc_address(sd->ril, sca, &rilp);
+
+ /* Send request to RIL */
+ if (g_ril_send(sd->ril, RIL_REQUEST_SET_SMSC_ADDRESS, &rilp,
+ ril_csca_set_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, user_data);
+ }
+}
+
+static void ril_csca_query_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_sms_sca_query_cb_t cb = cbd->cb;
+ struct sms_data *sd = cbd->user;
+ struct ofono_phone_number *sca;
+
+ if (message->error != RIL_E_SUCCESS) {
+ ofono_error("%s RILD reply failure: %s",
+ g_ril_request_id_to_string(sd->ril, message->req),
+ ril_error_to_string(message->error));
+ CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+ return;
+ }
+
+ sca = g_ril_reply_parse_get_smsc_address(sd->ril, message);
+ if (sca == NULL) {
+ CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+ } else {
+ CALLBACK_WITH_SUCCESS(cb, sca, cbd->data);
+ g_free(sca);
+ }
+}
+
+static void ril_csca_query(struct ofono_sms *sms, ofono_sms_sca_query_cb_t cb,
+ void *user_data)
+{
+ struct sms_data *sd = ofono_sms_get_data(sms);
+ struct cb_data *cbd = cb_data_new(cb, user_data, sd);
+
+ DBG("Sending csca_query");
+
+ if (g_ril_send(sd->ril, RIL_REQUEST_GET_SMSC_ADDRESS, NULL,
+ ril_csca_query_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, NULL, user_data);
+ }
+}
+
+static void ril_submit_sms_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct ofono_error error;
+ ofono_sms_submit_cb_t cb = cbd->cb;
+ struct sms_data *sd = cbd->user;
+ int mr = 0;
+
+ if (message->error == RIL_E_SUCCESS) {
+ decode_ril_error(&error, "OK");
+ mr = g_ril_reply_parse_sms_response(sd->ril, message);
+ } else {
+ decode_ril_error(&error, "FAIL");
+ }
+
+ cb(&error, mr, cbd->data);
+}
+
+static void ril_cmgs(struct ofono_sms *sms, const unsigned char *pdu,
+ int pdu_len, int tpdu_len, int mms,
+ ofono_sms_submit_cb_t cb, void *user_data)
+{
+ struct sms_data *sd = ofono_sms_get_data(sms);
+ struct cb_data *cbd = cb_data_new(cb, user_data, sd);
+ struct parcel rilp;
+ struct req_sms_cmgs req;
+
+ DBG("pdu_len: %d, tpdu_len: %d mms: %d", pdu_len, tpdu_len, mms);
+
+ /* TODO: if (mms) { ... } */
+
+ req.pdu = pdu;
+ req.pdu_len = pdu_len;
+ req.tpdu_len = tpdu_len;
+
+ g_ril_request_sms_cmgs(sd->ril, &req, &rilp);
+
+ if (g_ril_send(sd->ril, RIL_REQUEST_SEND_SMS, &rilp,
+ ril_submit_sms_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, -1, user_data);
+ }
+}
+
+static void ril_ack_delivery_cb(struct ril_msg *message, gpointer user_data)
+{
+ if (message->error != RIL_E_SUCCESS)
+ ofono_error("SMS acknowledgement failed: "
+ "Further SMS reception is not guaranteed");
+}
+
+static void ril_ack_delivery(struct ofono_sms *sms)
+{
+ struct sms_data *sd = ofono_sms_get_data(sms);
+ struct parcel rilp;
+
+ g_ril_request_sms_acknowledge(sd->ril, &rilp);
+
+ /* TODO: should ACK be sent for either of the error cases? */
+
+ /* ACK the incoming NEW_SMS */
+ g_ril_send(sd->ril, RIL_REQUEST_SMS_ACKNOWLEDGE, &rilp,
+ ril_ack_delivery_cb, NULL, NULL);
+
+}
+
+static void ril_sms_notify(struct ril_msg *message, gpointer user_data)
+{
+ struct ofono_sms *sms = user_data;
+ struct sms_data *sd = ofono_sms_get_data(sms);
+ unsigned int smsc_len;
+ long ril_buf_len;
+ struct unsol_sms_data *pdu_data;
+
+ DBG("req: %d; data_len: %d", message->req, (int) message->buf_len);
+
+ pdu_data = g_ril_unsol_parse_new_sms(sd->ril, message);
+ if (pdu_data == NULL)
+ goto error;
+
+ /*
+ * The first octect in the pdu contains the SMSC address length
+ * which is the X following octects it reads. We add 1 octet to
+ * the read length to take into account this read octet in order
+ * to calculate the proper tpdu length.
+ */
+ smsc_len = pdu_data->data[0] + 1;
+ ril_buf_len = pdu_data->length;
+ DBG("smsc_len is %d", smsc_len);
+
+ if (message->req == RIL_UNSOL_RESPONSE_NEW_SMS)
+ /* Last parameter is 'tpdu_len' ( substract SMSC length ) */
+ ofono_sms_deliver_notify(sms, pdu_data->data,
+ ril_buf_len,
+ ril_buf_len - smsc_len);
+ else if (message->req == RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT)
+ ofono_sms_status_notify(sms, pdu_data->data, ril_buf_len,
+ ril_buf_len - smsc_len);
+
+ /* ACK the incoming NEW_SMS */
+ ril_ack_delivery(sms);
+
+ g_ril_unsol_free_sms_data(pdu_data);
+
+error:
+ ;
+}
+
+static gboolean ril_delayed_register(gpointer user_data)
+{
+ struct ofono_sms *sms = user_data;
+ struct sms_data *data = ofono_sms_get_data(sms);
+
+ DBG("");
+ ofono_sms_register(sms);
+
+ /* register to receive INCOMING_SMS and SMS status reports */
+ g_ril_register(data->ril, RIL_UNSOL_RESPONSE_NEW_SMS,
+ ril_sms_notify, sms);
+ g_ril_register(data->ril, RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT,
+ ril_sms_notify, sms);
+
+ /* This makes the delayed call a single-shot */
+ return FALSE;
+}
+
+static int ril_sms_probe(struct ofono_sms *sms, unsigned int vendor,
+ void *user)
+{
+ GRil *ril = user;
+ struct sms_data *data;
+
+ data = g_new0(struct sms_data, 1);
+ data->ril = g_ril_clone(ril);
+ data->vendor = vendor;
+
+ ofono_sms_set_data(sms, data);
+
+ /*
+ * ofono_sms_register() needs to be called after
+ * the driver has been set in ofono_sms_create(), which
+ * calls this function. Most other drivers make some
+ * kind of capabilities query to the modem, and then
+ * call register in the callback; we use an idle add instead.
+ */
+ g_idle_add(ril_delayed_register, sms);
+
+ return 0;
+}
+
+static void ril_sms_remove(struct ofono_sms *sms)
+{
+ struct sms_data *data = ofono_sms_get_data(sms);
+
+ DBG("");
+
+ g_ril_unref(data->ril);
+ g_free(data);
+
+ ofono_sms_set_data(sms, NULL);
+}
+
+static struct ofono_sms_driver driver = {
+ .name = RILMODEM,
+ .probe = ril_sms_probe,
+ .sca_query = ril_csca_query,
+ .sca_set = ril_csca_set,
+ .remove = ril_sms_remove,
+ .submit = ril_cmgs,
+
+ /*
+ * TODO: investigate/implement:
+ * .bearer_query = NULL,
+ * .bearer_set = NULL,
+ */
+};
+
+void ril_sms_init(void)
+{
+ DBG("");
+ if (ofono_sms_driver_register(&driver))
+ DBG("ofono_sms_driver_register failed!");
+}
+
+void ril_sms_exit(void)
+{
+ DBG("");
+ ofono_sms_driver_unregister(&driver);
+}
diff --git a/drivers/rilmodem/ussd.c b/drivers/rilmodem/ussd.c
new file mode 100644
index 00000000..04985ec7
--- /dev/null
+++ b/drivers/rilmodem/ussd.c
@@ -0,0 +1,264 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2013 Jolla Ltd
+ * Copyright (C) 2013 Canonical Ltd
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/ussd.h>
+#include <smsutil.h>
+#include <util.h>
+
+#include "gril.h"
+#include "grilutil.h"
+#include "grilrequest.h"
+#include "grilunsol.h"
+
+#include "rilmodem.h"
+
+#include "ril_constants.h"
+
+struct ussd_data {
+ GRil *ril;
+};
+
+static gboolean request_success(gpointer data)
+{
+ struct cb_data *cbd = data;
+ ofono_ussd_cb_t cb = cbd->cb;
+
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ g_free(cbd);
+
+ return FALSE;
+}
+
+static void ril_ussd_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct ofono_ussd *ussd = user_data;
+ struct ussd_data *ud = ofono_ussd_get_data(ussd);
+
+ /*
+ * We fake an ON_USSD event if there was an error sending the request,
+ * as core will be waiting for one to respond to the Initiate() call.
+ * Note that we already made the callback (see ril_ussd_request()).
+ */
+ if (message->error == RIL_E_SUCCESS)
+ g_ril_print_response_no_args(ud->ril, message);
+ else
+ ofono_ussd_notify(ussd, OFONO_USSD_STATUS_NOT_SUPPORTED,
+ 0, NULL, 0);
+}
+
+static void ril_ussd_request(struct ofono_ussd *ussd, int dcs,
+ const unsigned char *pdu, int len,
+ ofono_ussd_cb_t cb, void *data)
+{
+ struct ussd_data *ud = ofono_ussd_get_data(ussd);
+ struct cb_data *cbd = cb_data_new(cb, data, ussd);
+ enum sms_charset charset;
+ char *text = NULL;
+ int ret = 0;
+
+ if (cbs_dcs_decode(dcs, NULL, NULL, &charset, NULL, NULL, NULL)) {
+
+ if (charset == SMS_CHARSET_7BIT) {
+ long written;
+
+ text = (char *) unpack_7bit(pdu, len, 0, TRUE,
+ 0, &written, 1);
+ if (text != NULL)
+ *(text + written) = '\0';
+
+ } else if (charset == SMS_CHARSET_UCS2) {
+ text = g_convert((char *) pdu, len,
+ "UTF-8//TRANSLIT", "UCS-2BE",
+ NULL, NULL, NULL);
+ } else {
+ ofono_error("%s: No support for charset %d",
+ __func__, charset);
+ }
+ }
+
+ if (text) {
+ struct parcel rilp;
+
+ g_ril_request_send_ussd(ud->ril, text, &rilp);
+
+ ret = g_ril_send(ud->ril, RIL_REQUEST_SEND_USSD,
+ &rilp, ril_ussd_cb, ussd, NULL);
+ g_free(text);
+ }
+
+ /*
+ * We do not wait for the SEND_USSD reply to do the callback, as some
+ * networks send it after sending one or more ON_USSD events. From the
+ * ofono core perspective, Initiate() does not return until one ON_USSD
+ * event is received: making here a successful callback just makes the
+ * core wait for that event.
+ */
+ if (ret <= 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ } else {
+ g_idle_add(request_success, cbd);
+ }
+}
+
+static void ril_ussd_cancel_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct ofono_ussd *ussd = cbd->user;
+ struct ussd_data *ud = ofono_ussd_get_data(ussd);
+ ofono_ussd_cb_t cb = cbd->cb;
+
+ if (message->error == RIL_E_SUCCESS) {
+ g_ril_print_response_no_args(ud->ril, message);
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ } else {
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ }
+}
+
+static void ril_ussd_cancel(struct ofono_ussd *ussd,
+ ofono_ussd_cb_t cb, void *user_data)
+{
+ struct ussd_data *ud = ofono_ussd_get_data(ussd);
+ struct cb_data *cbd = cb_data_new(cb, user_data, ussd);
+ int ret;
+
+ ret = g_ril_send(ud->ril, RIL_REQUEST_CANCEL_USSD, NULL,
+ ril_ussd_cancel_cb, cbd, g_free);
+
+ if (ret <= 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, user_data);
+ }
+}
+
+static void ril_ussd_notify(struct ril_msg *message, gpointer user_data)
+{
+ struct ofono_ussd *ussd = user_data;
+ struct ussd_data *ud = ofono_ussd_get_data(ussd);
+ struct unsol_ussd *unsol;
+
+ unsol = g_ril_unsol_parse_ussd(ud->ril, message);
+ if (unsol == NULL) {
+ ofono_error("%s: Parsing error", __func__);
+ return;
+ }
+
+ /* To fix bug in MTK: USSD-Notify arrive with type 2 instead of 0 */
+ if (g_ril_vendor(ud->ril) == OFONO_RIL_VENDOR_MTK &&
+ unsol->message != NULL && unsol->type == 2)
+ unsol->type = 0;
+
+ /*
+ * With data coding scheme 0x48, we are saying that the ussd string is a
+ * UCS-2 string, uncompressed, and with unspecified message class. For
+ * the DCS coding, see 3gpp 23.038, sect. 5.
+ */
+ if (unsol->message != NULL) {
+ gsize written;
+ char *ucs2;
+
+ ucs2 = g_convert(unsol->message, -1, "UCS-2BE//TRANSLIT",
+ "UTF-8", NULL, &written, NULL);
+ if (ucs2 != NULL) {
+ ofono_ussd_notify(ussd, unsol->type, 0x48,
+ (unsigned char *) ucs2, written);
+ g_free(ucs2);
+ } else {
+ ofono_error("%s: Error transcoding", __func__);
+ }
+ } else {
+ ofono_ussd_notify(ussd, unsol->type, 0, NULL, 0);
+ }
+
+ g_ril_unsol_free_ussd(unsol);
+}
+
+static gboolean ril_delayed_register(gpointer user_data)
+{
+ struct ofono_ussd *ussd = user_data;
+ struct ussd_data *ud = ofono_ussd_get_data(ussd);
+
+ DBG("");
+
+ ofono_ussd_register(ussd);
+
+ /* Register for USSD responses */
+ g_ril_register(ud->ril, RIL_UNSOL_ON_USSD, ril_ussd_notify, ussd);
+
+ return FALSE;
+}
+
+static int ril_ussd_probe(struct ofono_ussd *ussd,
+ unsigned int vendor,
+ void *user)
+{
+ GRil *ril = user;
+ struct ussd_data *ud = g_new0(struct ussd_data, 1);
+
+ ud->ril = g_ril_clone(ril);
+ ofono_ussd_set_data(ussd, ud);
+ g_idle_add(ril_delayed_register, ussd);
+
+ return 0;
+}
+
+static void ril_ussd_remove(struct ofono_ussd *ussd)
+{
+ struct ussd_data *ud = ofono_ussd_get_data(ussd);
+ ofono_ussd_set_data(ussd, NULL);
+
+ g_ril_unref(ud->ril);
+ g_free(ud);
+}
+
+static struct ofono_ussd_driver driver = {
+ .name = RILMODEM,
+ .probe = ril_ussd_probe,
+ .remove = ril_ussd_remove,
+ .request = ril_ussd_request,
+ .cancel = ril_ussd_cancel
+};
+
+void ril_ussd_init(void)
+{
+ ofono_ussd_driver_register(&driver);
+}
+
+void ril_ussd_exit(void)
+{
+ ofono_ussd_driver_unregister(&driver);
+}
diff --git a/drivers/rilmodem/vendor.h b/drivers/rilmodem/vendor.h
new file mode 100644
index 00000000..83cc939a
--- /dev/null
+++ b/drivers/rilmodem/vendor.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2014 Canonical Ltd. 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
+ *
+ */
+
+#ifndef RILMODEM_VENDOR_H
+#define RILMODEM_VENDOR_H
+
+enum ofono_ril_vendor {
+ OFONO_RIL_VENDOR_AOSP = 0,
+ OFONO_RIL_VENDOR_MTK,
+ OFONO_RIL_VENDOR_INFINEON,
+ OFONO_RIL_VENDOR_QCOM_MSIM
+};
+
+#endif /* RILMODEM_VENDOR_H */
diff --git a/drivers/rilmodem/voicecall.c b/drivers/rilmodem/voicecall.c
new file mode 100644
index 00000000..3fa4b375
--- /dev/null
+++ b/drivers/rilmodem/voicecall.c
@@ -0,0 +1,824 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2012-2013 Canonical Ltd.
+ * Copyright (C) 2013 Jolla Ltd.
+ *
+ * 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.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/voicecall.h>
+
+#include "gril.h"
+#include "grilrequest.h"
+#include "grilreply.h"
+#include "grilunsol.h"
+
+#include "common.h"
+#include "rilmodem.h"
+#include "voicecall.h"
+
+/* Amount of ms we wait between CLCC calls */
+#define POLL_CLCC_INTERVAL 300
+
+#define FLAG_NEED_CLIP 1
+
+#define MAX_DTMF_BUFFER 32
+
+/* To use with change_state_req::affected_types */
+#define AFFECTED_STATES_ALL 0x3F
+
+/* Auto-answer delay in seconds */
+#define AUTO_ANSWER_DELAY_S 3
+
+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;
+ /* Call states affected by a local release (1 << enum call_status) */
+ int affected_types;
+};
+
+struct lastcause_req {
+ struct ofono_voicecall *vc;
+ int id;
+};
+
+/* Data for dial after swap */
+struct hold_before_dial_req {
+ struct ofono_voicecall *vc;
+ struct ofono_phone_number dial_ph;
+ enum ofono_clir_option dial_clir;
+};
+
+static void send_one_dtmf(struct ril_voicecall_data *vd);
+static void clear_dtmf_queue(struct ril_voicecall_data *vd);
+
+static void lastcause_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct lastcause_req *reqdata = user_data;
+ struct ofono_voicecall *vc = reqdata->vc;
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+ enum ofono_disconnect_reason reason;
+
+ reason = g_ril_reply_parse_call_fail_cause(vd->ril, message);
+
+ DBG("Call %d ended with reason %d", reqdata->id, reason);
+
+ ofono_voicecall_disconnected(vc, reqdata->id, reason, NULL);
+}
+
+static gboolean auto_answer_call(gpointer user_data)
+{
+ struct ofono_voicecall *vc = user_data;
+
+ DBG("");
+
+ ril_answer(vc, NULL, NULL);
+
+ return FALSE;
+}
+
+static gboolean is_auto_answer(struct ril_voicecall_data *vd,
+ struct ofono_call *call)
+{
+ static const char test_mcc_mnc_1[] = "00101";
+ static const char test_mcc_mnc_2[] = "001001";
+
+ const char *imsi;
+ struct ofono_sim *sim;
+
+ if (call->status != CALL_STATUS_INCOMING)
+ return FALSE;
+
+ sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, vd->modem);
+ if (sim == NULL)
+ return FALSE;
+
+ imsi = ofono_sim_get_imsi(sim);
+ if (imsi == NULL)
+ return FALSE;
+
+ if (strncmp(imsi, test_mcc_mnc_1, sizeof(test_mcc_mnc_1) - 1) == 0 ||
+ strncmp(imsi, test_mcc_mnc_2, sizeof(test_mcc_mnc_2) - 1)
+ == 0) {
+ ofono_info("Auto answering incoming call, imsi is %s", imsi);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void clcc_poll_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct ofono_voicecall *vc = user_data;
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+ int reqid = RIL_REQUEST_LAST_CALL_FAIL_CAUSE;
+ GSList *calls;
+ GSList *n, *o;
+ struct ofono_call *nc, *oc;
+
+ /*
+ * We consider all calls have been dropped if there is no radio, which
+ * happens, for instance, when flight mode is set whilst in a call.
+ */
+ if (message->error != RIL_E_SUCCESS &&
+ message->error != RIL_E_RADIO_NOT_AVAILABLE) {
+ ofono_error("We are polling CLCC and received an error");
+ ofono_error("All bets are off for call management");
+ return;
+ }
+
+ calls = g_ril_reply_parse_get_calls(vd->ril, message);
+
+ n = calls;
+ o = vd->calls;
+
+ while (n || o) {
+ nc = n ? n->data : NULL;
+ oc = o ? o->data : NULL;
+
+ /* TODO: Add comments explaining call id handling */
+ if (oc && (nc == NULL || (nc->id > oc->id))) {
+ if (vd->local_release & (1 << oc->id)) {
+ ofono_voicecall_disconnected(vc, oc->id,
+ OFONO_DISCONNECT_REASON_LOCAL_HANGUP,
+ NULL);
+ } else if (message->error ==
+ RIL_E_RADIO_NOT_AVAILABLE) {
+ ofono_voicecall_disconnected(vc, oc->id,
+ OFONO_DISCONNECT_REASON_ERROR,
+ NULL);
+ } else {
+ /* Get disconnect cause before calling core */
+ struct lastcause_req *reqdata =
+ g_try_new0(struct lastcause_req, 1);
+ if (reqdata != NULL) {
+ reqdata->vc = user_data;
+ reqdata->id = oc->id;
+
+ g_ril_send(vd->ril, reqid, NULL,
+ lastcause_cb, reqdata,
+ g_free);
+ }
+ }
+
+ clear_dtmf_queue(vd);
+
+ o = o->next;
+ } else if (nc && (oc == NULL || (nc->id < oc->id))) {
+ /* new call, signal it */
+ if (nc->type) {
+ ofono_voicecall_notify(vc, nc);
+
+ if (vd->cb) {
+ struct ofono_error error;
+ ofono_voicecall_cb_t cb = vd->cb;
+ decode_ril_error(&error, "OK");
+ cb(&error, vd->data);
+ vd->cb = NULL;
+ vd->data = NULL;
+ }
+
+ if (is_auto_answer(vd, nc))
+ g_timeout_add_seconds(
+ AUTO_ANSWER_DELAY_S,
+ auto_answer_call, vc);
+ }
+
+ 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 and clip_validity
+ * is 1
+ */
+ if (oc->clip_validity == 1)
+ nc->clip_validity = oc->clip_validity;
+
+ 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)
+ ofono_voicecall_notify(vc, nc);
+
+ vd->flags &= ~FLAG_NEED_CLIP;
+ } else if (memcmp(nc, oc, sizeof(*nc)) && nc->type)
+ ofono_voicecall_notify(vc, nc);
+
+ n = n->next;
+ o = o->next;
+ }
+ }
+
+ g_slist_foreach(vd->calls, (GFunc) g_free, NULL);
+ g_slist_free(vd->calls);
+
+ vd->calls = calls;
+ vd->local_release = 0;
+}
+
+gboolean ril_poll_clcc(gpointer user_data)
+{
+ struct ofono_voicecall *vc = user_data;
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+ g_ril_send(vd->ril, RIL_REQUEST_GET_CURRENT_CALLS, NULL,
+ clcc_poll_cb, vc, NULL);
+
+ vd->clcc_source = 0;
+
+ return FALSE;
+}
+
+static void generic_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct change_state_req *req = user_data;
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(req->vc);
+ struct ofono_error error;
+
+ if (message->error == RIL_E_SUCCESS) {
+ decode_ril_error(&error, "OK");
+ } else {
+ decode_ril_error(&error, "FAIL");
+ goto out;
+ }
+
+ g_ril_print_response_no_args(vd->ril, message);
+
+ if (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);
+ }
+ }
+
+out:
+ g_ril_send(vd->ril, RIL_REQUEST_GET_CURRENT_CALLS, NULL,
+ clcc_poll_cb, req->vc, NULL);
+
+ /* We have to callback after we schedule a poll if required */
+ if (req->cb)
+ req->cb(&error, req->data);
+}
+
+static int ril_template(const guint rreq, struct ofono_voicecall *vc,
+ GRilResponseFunc func, unsigned int affected_types,
+ gpointer pdata, ofono_voicecall_cb_t cb, void *data)
+{
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+ struct change_state_req *req = g_try_new0(struct change_state_req, 1);
+ int ret;
+
+ if (req == NULL)
+ goto error;
+
+ req->vc = vc;
+ req->cb = cb;
+ req->data = data;
+ req->affected_types = affected_types;
+
+ ret = g_ril_send(vd->ril, rreq, pdata, func, req, g_free);
+ if (ret > 0)
+ return ret;
+error:
+ g_free(req);
+
+ if (cb)
+ CALLBACK_WITH_FAILURE(cb, data);
+
+ return 0;
+}
+
+static void rild_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct ofono_voicecall *vc = cbd->user;
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+ ofono_voicecall_cb_t cb = cbd->cb;
+ struct ofono_error error;
+
+ /*
+ * DIAL_MODIFIED_TO_DIAL means redirection. The call we will see when
+ * polling will have a different called number.
+ */
+ if (message->error == RIL_E_SUCCESS ||
+ (g_ril_vendor(vd->ril) == OFONO_RIL_VENDOR_AOSP &&
+ message->error == RIL_E_DIAL_MODIFIED_TO_DIAL)) {
+ decode_ril_error(&error, "OK");
+ } else {
+ decode_ril_error(&error, "FAIL");
+ goto out;
+ }
+
+ g_ril_print_response_no_args(vd->ril, message);
+
+ /* CLCC will update the oFono call list with proper ids */
+ if (!vd->clcc_source)
+ vd->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL,
+ ril_poll_clcc, vc);
+
+ /* we cannot answer just yet since we don't know the call id */
+ vd->cb = cb;
+ vd->data = cbd->data;
+
+ return;
+
+out:
+ cb(&error, cbd->data);
+}
+
+static void dial(struct ofono_voicecall *vc,
+ const struct ofono_phone_number *ph,
+ enum ofono_clir_option clir, ofono_voicecall_cb_t cb,
+ void *data)
+{
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+ struct cb_data *cbd = cb_data_new(cb, data, vc);
+ struct parcel rilp;
+
+ g_ril_request_dial(vd->ril, ph, clir, &rilp);
+
+ /* Send request to RIL */
+ if (g_ril_send(vd->ril, RIL_REQUEST_DIAL, &rilp,
+ rild_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+}
+
+static void hold_before_dial_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct hold_before_dial_req *req = cbd->user;
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(req->vc);
+ ofono_voicecall_cb_t cb = cbd->cb;
+
+ if (message->error != RIL_E_SUCCESS) {
+ g_free(req);
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ return;
+ }
+
+ g_ril_print_response_no_args(vd->ril, message);
+
+ /* Current calls held: we can dial now */
+ dial(req->vc, &req->dial_ph, req->dial_clir, cb, cbd->data);
+
+ g_free(req);
+}
+
+void ril_dial(struct ofono_voicecall *vc, const struct ofono_phone_number *ph,
+ enum ofono_clir_option clir, ofono_voicecall_cb_t cb,
+ void *data)
+{
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+ int current_active = 0;
+ struct ofono_call *call;
+ GSList *l;
+
+ /* Check for current active calls */
+ for (l = vd->calls; l; l = l->next) {
+ call = l->data;
+
+ if (call->status == CALL_STATUS_ACTIVE) {
+ current_active = 1;
+ break;
+ }
+ }
+
+ /*
+ * The network will put current active calls on hold. In some cases
+ * (mako), the modem also updates properly the state. In others
+ * (maguro), we need to explicitly set the state to held. In both cases
+ * we send a request for holding the active call, as it is not harmful
+ * when it is not really needed, and is what Android does.
+ */
+ if (current_active) {
+ struct hold_before_dial_req *req;
+ struct cb_data *cbd;
+
+ req = g_malloc0(sizeof(*req));
+ req->vc = vc;
+ req->dial_ph = *ph;
+ req->dial_clir = clir;
+
+ cbd = cb_data_new(cb, data, req);
+
+ if (g_ril_send(vd->ril, RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE,
+ NULL, hold_before_dial_cb, cbd, g_free) == 0) {
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
+
+ } else {
+ dial(vc, ph, clir, cb, data);
+ }
+}
+
+void ril_hangup_all(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb,
+ void *data)
+{
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+ struct parcel rilp;
+ struct ofono_error error;
+ struct ofono_call *call;
+ GSList *l;
+
+ for (l = vd->calls; l; l = l->next) {
+ call = l->data;
+
+ if (call->status == CALL_STATUS_INCOMING) {
+ /*
+ * Need to use this request so that declined
+ * calls in this state, are properly forwarded
+ * to voicemail. REQUEST_HANGUP doesn't do the
+ * right thing for some operators, causing the
+ * caller to hear a fast busy signal.
+ */
+ ril_template(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND,
+ vc, generic_cb, AFFECTED_STATES_ALL,
+ NULL, NULL, NULL);
+ } else {
+
+ /* TODO: Hangup just the active ones once we have call
+ * state tracking (otherwise it can't handle ringing) */
+ g_ril_request_hangup(vd->ril, call->id, &rilp);
+
+ /* Send request to RIL */
+ ril_template(RIL_REQUEST_HANGUP, vc, generic_cb,
+ AFFECTED_STATES_ALL, &rilp, NULL, NULL);
+ }
+ }
+
+ /* TODO: Deal in case of an error at hungup */
+ decode_ril_error(&error, "OK");
+ cb(&error, data);
+}
+
+void ril_hangup_specific(struct ofono_voicecall *vc,
+ int id, ofono_voicecall_cb_t cb, void *data)
+{
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+ struct parcel rilp;
+
+ DBG("Hanging up call with id %d", id);
+
+ g_ril_request_hangup(vd->ril, id, &rilp);
+
+ /* Send request to RIL */
+ ril_template(RIL_REQUEST_HANGUP, vc, generic_cb,
+ AFFECTED_STATES_ALL, &rilp, cb, data);
+}
+
+void ril_call_state_notify(struct ril_msg *message, gpointer user_data)
+{
+ struct ofono_voicecall *vc = user_data;
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+ g_ril_print_unsol_no_args(vd->ril, message);
+
+ /* Just need to request the call list again */
+ ril_poll_clcc(vc);
+
+ return;
+}
+
+static void ril_ss_notify(struct ril_msg *message, gpointer user_data)
+{
+ struct ofono_voicecall *vc = user_data;
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+ struct unsol_supp_svc_notif *unsol;
+
+ unsol = g_ril_unsol_parse_supp_svc_notif(vd->ril, message);
+ if (unsol == NULL) {
+ ofono_error("%s: Parsing error", __func__);
+ return;
+ }
+
+ DBG("RIL data: MT/MO: %i, code: %i, index: %i",
+ unsol->notif_type, unsol->code, unsol->index);
+
+ /* 0 stands for MO intermediate, 1 for MT unsolicited */
+ /* TODO How do we know the affected call? Refresh call list? */
+ if (unsol->notif_type == 1)
+ ofono_voicecall_ssn_mt_notify(
+ vc, 0, unsol->code, unsol->index, &unsol->number);
+ else
+ ofono_voicecall_ssn_mo_notify(vc, 0, unsol->code, unsol->index);
+
+ g_ril_unsol_free_supp_svc_notif(unsol);
+}
+
+void ril_answer(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data)
+{
+ DBG("Answering current call");
+
+ /* Send request to RIL */
+ ril_template(RIL_REQUEST_ANSWER, vc, generic_cb, 0, NULL, cb, data);
+}
+
+static void ril_send_dtmf_cb(struct ril_msg *message, gpointer user_data)
+{
+ struct ril_voicecall_data *vd = user_data;
+
+ if (message->error == RIL_E_SUCCESS) {
+ /* Remove sent DTMF character from queue */
+ gchar *tmp_tone_queue = g_strdup(vd->tone_queue + 1);
+ int remaining = strlen(tmp_tone_queue);
+
+ memcpy(vd->tone_queue, tmp_tone_queue, remaining);
+ vd->tone_queue[remaining] = '\0';
+ g_free(tmp_tone_queue);
+
+ vd->tone_pending = FALSE;
+
+ if (remaining > 0)
+ send_one_dtmf(vd);
+ } else {
+ DBG("error=%d", message->error);
+ clear_dtmf_queue(vd);
+ }
+}
+
+static void send_one_dtmf(struct ril_voicecall_data *vd)
+{
+ struct parcel rilp;
+
+ if (vd->tone_pending == TRUE)
+ return; /* RIL request pending */
+
+ if (strlen(vd->tone_queue) == 0)
+ return; /* nothing to send */
+
+ g_ril_request_dtmf(vd->ril, vd->tone_queue[0], &rilp);
+
+ g_ril_send(vd->ril, RIL_REQUEST_DTMF, &rilp,
+ ril_send_dtmf_cb, vd, NULL);
+
+ vd->tone_pending = TRUE;
+}
+
+void ril_send_dtmf(struct ofono_voicecall *vc, const char *dtmf,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+ struct ofono_error error;
+
+ DBG("Queue '%s'", dtmf);
+
+ /*
+ * Queue any incoming DTMF (up to MAX_DTMF_BUFFER characters),
+ * send them to RIL one-by-one, immediately call back
+ * core with no error
+ */
+ g_strlcat(vd->tone_queue, dtmf, MAX_DTMF_BUFFER);
+ send_one_dtmf(vd);
+
+ /* We don't really care about errors here */
+ decode_ril_error(&error, "OK");
+ cb(&error, data);
+}
+
+static void clear_dtmf_queue(struct ril_voicecall_data *vd)
+{
+ g_free(vd->tone_queue);
+ vd->tone_queue = g_strnfill(MAX_DTMF_BUFFER + 1, '\0');
+ vd->tone_pending = FALSE;
+}
+
+void ril_create_multiparty(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ ril_template(RIL_REQUEST_CONFERENCE, vc, generic_cb, 0, NULL, cb, data);
+}
+
+void ril_private_chat(struct ofono_voicecall *vc, int id,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+ struct parcel rilp;
+
+ g_ril_request_separate_conn(vd->ril, id, &rilp);
+
+ /* Send request to RIL */
+ ril_template(RIL_REQUEST_SEPARATE_CONNECTION, vc,
+ generic_cb, 0, &rilp, cb, data);
+}
+
+void ril_swap_without_accept(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ ril_template(RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE, vc,
+ generic_cb, 0, NULL, cb, data);
+}
+
+void ril_hold_all_active(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ ril_template(RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE, vc,
+ generic_cb, 0, NULL, cb, data);
+}
+
+void ril_release_all_held(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ ril_template(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, vc,
+ generic_cb, 0, NULL, cb, data);
+}
+
+void ril_release_all_active(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ ril_template(RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND, vc,
+ generic_cb, 0, NULL, cb, data);
+}
+
+void ril_set_udub(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ ril_template(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, vc,
+ generic_cb, 0, NULL, cb, data);
+}
+
+static gboolean enable_supp_svc(gpointer user_data)
+{
+ struct ofono_voicecall *vc = user_data;
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+ struct parcel rilp;
+
+ g_ril_request_set_supp_svc_notif(vd->ril, &rilp);
+
+ g_ril_send(vd->ril, RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION, &rilp,
+ NULL, vc, NULL);
+
+ /* Makes this a single shot */
+ return FALSE;
+}
+
+static gboolean ril_delayed_register(gpointer user_data)
+{
+ struct ofono_voicecall *vc = user_data;
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+ ofono_voicecall_register(vc);
+
+ /* Initialize call list */
+ ril_poll_clcc(vc);
+
+ /* Unsol when call state changes */
+ g_ril_register(vd->ril, RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,
+ ril_call_state_notify, vc);
+
+ /* Unsol when call set on hold */
+ g_ril_register(vd->ril, RIL_UNSOL_SUPP_SVC_NOTIFICATION,
+ ril_ss_notify, vc);
+
+ /* request supplementary service notifications*/
+ enable_supp_svc(vc);
+
+ /* This makes the timeout a single-shot */
+ return FALSE;
+}
+
+void ril_voicecall_start(struct ril_voicecall_driver_data *driver_data,
+ struct ofono_voicecall *vc,
+ unsigned int vendor,
+ struct ril_voicecall_data *vd)
+{
+ vd->ril = g_ril_clone(driver_data->gril);
+ vd->modem = driver_data->modem;
+ vd->vendor = vendor;
+ vd->cb = NULL;
+ vd->data = NULL;
+
+ clear_dtmf_queue(vd);
+
+ ofono_voicecall_set_data(vc, vd);
+
+ /*
+ * ofono_voicecall_register() needs to be called after
+ * the driver has been set in ofono_voicecall_create(),
+ * which calls this function. Most other drivers make
+ * some kind of capabilities query to the modem, and then
+ * call register in the callback; we use an idle event instead.
+ */
+ g_idle_add(ril_delayed_register, vc);
+}
+
+int ril_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
+ void *data)
+{
+ struct ril_voicecall_driver_data *driver_data = data;
+ struct ril_voicecall_data *vd;
+
+ vd = g_try_new0(struct ril_voicecall_data, 1);
+ if (vd == NULL)
+ return -ENOMEM;
+
+ ril_voicecall_start(driver_data, vc, vendor, vd);
+
+ return 0;
+}
+
+void ril_voicecall_remove(struct ofono_voicecall *vc)
+{
+ struct ril_voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+ if (vd->clcc_source)
+ g_source_remove(vd->clcc_source);
+
+ g_slist_foreach(vd->calls, (GFunc) g_free, NULL);
+ g_slist_free(vd->calls);
+
+ ofono_voicecall_set_data(vc, NULL);
+
+ g_ril_unref(vd->ril);
+ g_free(vd->tone_queue);
+ g_free(vd);
+}
+
+static struct ofono_voicecall_driver driver = {
+ .name = RILMODEM,
+ .probe = ril_voicecall_probe,
+ .remove = ril_voicecall_remove,
+ .dial = ril_dial,
+ .answer = ril_answer,
+ .hangup_all = ril_hangup_all,
+ .release_specific = ril_hangup_specific,
+ .send_tones = ril_send_dtmf,
+ .create_multiparty = ril_create_multiparty,
+ .private_chat = ril_private_chat,
+ .swap_without_accept = ril_swap_without_accept,
+ .hold_all_active = ril_hold_all_active,
+ .release_all_held = ril_release_all_held,
+ .set_udub = ril_set_udub,
+ .release_all_active = ril_release_all_active,
+};
+
+void ril_voicecall_init(void)
+{
+ ofono_voicecall_driver_register(&driver);
+}
+
+void ril_voicecall_exit(void)
+{
+ ofono_voicecall_driver_unregister(&driver);
+}
diff --git a/drivers/rilmodem/voicecall.h b/drivers/rilmodem/voicecall.h
new file mode 100644
index 00000000..0407abeb
--- /dev/null
+++ b/drivers/rilmodem/voicecall.h
@@ -0,0 +1,71 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2014 Canonical Ltd.
+ *
+ * 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
+ *
+ */
+
+struct ril_voicecall_data {
+ GSList *calls;
+ /* Call local hangup indicator, one bit per call (1 << call_id) */
+ unsigned int local_release;
+ unsigned int clcc_source;
+ GRil *ril;
+ struct ofono_modem *modem;
+ unsigned int vendor;
+ unsigned char flags;
+ ofono_voicecall_cb_t cb;
+ void *data;
+ gchar *tone_queue;
+ gboolean tone_pending;
+};
+
+int ril_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
+ void *data);
+void ril_voicecall_remove(struct ofono_voicecall *vc);
+void ril_dial(struct ofono_voicecall *vc, const struct ofono_phone_number *ph,
+ enum ofono_clir_option clir, ofono_voicecall_cb_t cb,
+ void *data);
+void ril_answer(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data);
+void ril_hangup_all(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb,
+ void *data);
+void ril_hangup_specific(struct ofono_voicecall *vc,
+ int id, ofono_voicecall_cb_t cb, void *data);
+void ril_send_dtmf(struct ofono_voicecall *vc, const char *dtmf,
+ ofono_voicecall_cb_t cb, void *data);
+void ril_create_multiparty(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data);
+void ril_private_chat(struct ofono_voicecall *vc, int id,
+ ofono_voicecall_cb_t cb, void *data);
+void ril_swap_without_accept(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data);
+void ril_hold_all_active(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data);
+void ril_release_all_held(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data);
+void ril_set_udub(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data);
+void ril_release_all_active(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data);
+
+void ril_voicecall_start(struct ril_voicecall_driver_data *driver_data,
+ struct ofono_voicecall *vc,
+ unsigned int vendor,
+ struct ril_voicecall_data *vd);
+void ril_call_state_notify(struct ril_msg *message, gpointer user_data);
+gboolean ril_poll_clcc(gpointer user_data);