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>
This commit is contained in:
Tony Espy 2015-10-13 18:07:53 +02:00 committed by Denis Kenzior
parent 9c2af753c0
commit e918a6b222
22 changed files with 7558 additions and 0 deletions

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

218
drivers/rilmodem/devinfo.c Normal file
View File

@ -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);
}

View File

@ -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);
}

487
drivers/rilmodem/gprs.c Normal file
View File

@ -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);
}

46
drivers/rilmodem/gprs.h Normal file
View File

@ -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);

View File

@ -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);
}

1055
drivers/rilmodem/phonebook.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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);

View File

@ -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)

View File

@ -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);

194
drivers/rilmodem/rilutil.c Normal file
View File

@ -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);
}

165
drivers/rilmodem/rilutil.h Normal file
View File

@ -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 */

1200
drivers/rilmodem/sim.c Normal file

File diff suppressed because it is too large Load Diff

315
drivers/rilmodem/sms.c Normal file
View File

@ -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);
}

264
drivers/rilmodem/ussd.c Normal file
View File

@ -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);
}

32
drivers/rilmodem/vendor.h Normal file
View File

@ -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 */

View File

@ -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);
}

View File

@ -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);