summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTony Espy <espy@canonical.com>2015-10-13 18:07:53 +0200
committerDenis Kenzior <denkenz@gmail.com>2015-10-13 17:38:45 -0500
commite918a6b222ace2ae034b6b2a2474bda457dfc619 (patch)
tree3e41881b1cb212e7f3102276027c9e5a5320eae2
parent9c2af753c0ca7e344019e33911bc590f35f81b12 (diff)
downloadofono-e918a6b222ace2ae034b6b2a2474bda457dfc619.tar.bz2
rilmodem: driver for Android modems
Driver for modems that are accessed through the Android Radio Interface Layer (RIL) for telephony, using the gril library. The driver is almost feature complete with some exceptions, being CBS and SAT the most prominent. Co-authored-by: Tony Espy <espy@canonical.com> Co-authored-by: Ricardo Salveti de Araujo <ricardo.salveti@canonical.com> Co-authored-by: Alfonso Sanchez-Beato <alfonso.sanchez-beato@canonical.com> Co-authored-by: Islam Amer <islam.amer@jollamobile.com> Co-authored-by: Jussi Kangas <jussi.kangas@tieto.com> Co-authored-by: Juho Hämäläinen <juho.hamalainen@tieto.com> Co-authored-by: Petri Takalokastari <petri.takalokastari@oss.tieto.com> Co-authored-by: Jarko Poutiainen <Jarko.Poutiainen@oss.tieto.com> Co-authored-by: Tommi Kenakkala <tommi.kenakkala@oss.tieto.com> Co-authored-by: Miia Leinonen <miia.leinonen@oss.tieto.com> Co-authored-by: Martti Piirainen <martti.piirainen@canonical.com> Co-authored-by: You-Sheng Yang <vicamo.yang@canonical.com>
-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);