mirror of
https://github.com/sailfishos/ofono
synced 2025-11-29 14:11:05 +08:00
[ril] New RIL plugin supporting multiple modems. Fixes MER#1110
Enabled by --enable-jolla-rilmodem configure option.
This commit is contained in:
@@ -119,6 +119,32 @@ builtin_sources += plugins/udevng.c
|
||||
endif
|
||||
|
||||
if RILMODEM
|
||||
if JOLLA_RILMODEM
|
||||
builtin_modules += ril
|
||||
builtin_sources += drivers/ril/ril_call_barring.c \
|
||||
drivers/ril/ril_call_forward.c \
|
||||
drivers/ril/ril_call_settings.c \
|
||||
drivers/ril/ril_call_volume.c \
|
||||
drivers/ril/ril_cbs.c \
|
||||
drivers/ril/ril_devinfo.c \
|
||||
drivers/ril/ril_gprs.c \
|
||||
drivers/ril/ril_gprs_context.c \
|
||||
drivers/ril/ril_mce.c \
|
||||
drivers/ril/ril_modem.c \
|
||||
drivers/ril/ril_netreg.c \
|
||||
drivers/ril/ril_oem_raw.c \
|
||||
drivers/ril/ril_phonebook.c \
|
||||
drivers/ril/ril_plugin.c \
|
||||
drivers/ril/ril_plugin_dbus.c \
|
||||
drivers/ril/ril_radio_settings.c \
|
||||
drivers/ril/ril_sim.c \
|
||||
drivers/ril/ril_sim_dbus.c \
|
||||
drivers/ril/ril_sms.c \
|
||||
drivers/ril/ril_stk.c \
|
||||
drivers/ril/ril_ussd.c \
|
||||
drivers/ril/ril_util.c \
|
||||
drivers/ril/ril_voicecall.c
|
||||
else
|
||||
builtin_sources += $(gril_sources)
|
||||
|
||||
builtin_modules += rildev
|
||||
@@ -149,7 +175,7 @@ builtin_sources += drivers/rilmodem/rilmodem.h \
|
||||
drivers/rilmodem/oemraw-messages.c \
|
||||
drivers/rilmodem/call-barring.c \
|
||||
drivers/rilmodem/stk.c
|
||||
|
||||
endif
|
||||
endif
|
||||
|
||||
if ISIMODEM
|
||||
|
||||
@@ -42,6 +42,7 @@ AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],
|
||||
if (test "${enableval}" = "yes" &&
|
||||
test "${ac_cv_prog_cc_g}" = "yes"); then
|
||||
CFLAGS="$CFLAGS -g"
|
||||
CPPFLAGS="$CPPFLAGS -DDEBUG"
|
||||
fi
|
||||
])
|
||||
|
||||
@@ -166,6 +167,20 @@ AC_ARG_ENABLE(rilmodem, AC_HELP_STRING([--disable-rilmodem],
|
||||
[enable_rilmodem=${enableval}])
|
||||
AM_CONDITIONAL(RILMODEM, test "${enable_rilmodem}" != "no")
|
||||
|
||||
AC_ARG_ENABLE(jolla-rilmodem,
|
||||
AC_HELP_STRING([--enable-jolla-rilmodem], [enable Jolla RIL modem]),
|
||||
[enable_jolla_rilmodem=${enableval}], [enable_jolla_rilmodem="no"])
|
||||
AM_CONDITIONAL(JOLLA_RILMODEM, test "${enable_jolla_rilmodem}" != "no")
|
||||
|
||||
if (test "${enable_jolla_rilmodem}" = "yes"); then
|
||||
PKG_CHECK_MODULES(GRILIO, libgrilio, dummy=yes,
|
||||
AC_MSG_ERROR(libgrilio is required))
|
||||
PKG_CHECK_MODULES(GLIBUTIL, libglibutil, dummy=yes,
|
||||
AC_MSG_ERROR(libglibutil is required))
|
||||
CFLAGS="$CFLAGS $GRILIO_CFLAGS $GLIBUTIL_CFLAGS"
|
||||
LIBS="$LIBS $GRILIO_LIBS $GLIBUTIL_LIBS"
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(qmimodem, AC_HELP_STRING([--disable-qmimodem],
|
||||
[disable Qualcomm QMI modem support]),
|
||||
[enable_qmimodem=${enableval}])
|
||||
|
||||
284
ofono/drivers/ril/ril_call_barring.c
Normal file
284
ofono/drivers/ril/ril_call_barring.c
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
#include "ril_constants.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/* See 3GPP 27.007 7.4 for possible values */
|
||||
#define RIL_MAX_SERVICE_LENGTH 3
|
||||
|
||||
/*
|
||||
* ril.h does not state that string count must be given, but that is
|
||||
* still expected by the modem
|
||||
*/
|
||||
#define RIL_QUERY_STRING_COUNT 4
|
||||
#define RIL_SET_STRING_COUNT 5
|
||||
#define RIL_SET_PW_STRING_COUNT 3
|
||||
|
||||
struct ril_call_barring {
|
||||
GRilIoQueue *q;
|
||||
guint timer_id;
|
||||
};
|
||||
|
||||
struct ril_call_barring_cbd {
|
||||
struct ril_call_barring *bd;
|
||||
union _ofono_call_barring_cb {
|
||||
ofono_call_barring_query_cb_t query;
|
||||
ofono_call_barring_set_cb_t set;
|
||||
gpointer ptr;
|
||||
} cb;
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
#define ril_call_barring_cbd_free g_free
|
||||
|
||||
static inline struct ril_call_barring *ril_call_barring_get_data(
|
||||
struct ofono_call_barring *b)
|
||||
{
|
||||
return ofono_call_barring_get_data(b);
|
||||
}
|
||||
|
||||
static struct ril_call_barring_cbd *ril_call_barring_cbd_new(
|
||||
struct ril_call_barring *bd, void *cb, void *data)
|
||||
{
|
||||
struct ril_call_barring_cbd *cbd;
|
||||
|
||||
cbd = g_new0(struct ril_call_barring_cbd, 1);
|
||||
cbd->bd = bd;
|
||||
cbd->cb.ptr = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static inline void ril_call_barring_submit_request(struct ril_call_barring *bd,
|
||||
GRilIoRequest* req, guint code, GRilIoChannelResponseFunc response,
|
||||
void *cb, void *data)
|
||||
{
|
||||
grilio_queue_send_request_full(bd->q, req, code, response,
|
||||
ril_call_barring_cbd_free,
|
||||
ril_call_barring_cbd_new(bd, cb, data));
|
||||
}
|
||||
|
||||
static void ril_call_barring_query_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_call_barring_cbd *cbd = user_data;
|
||||
ofono_call_barring_query_cb_t cb = cbd->cb.query;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
int bearer_class = 0;
|
||||
GRilIoParser rilp;
|
||||
|
||||
/*
|
||||
* Services for which the specified barring facility is active.
|
||||
* "0" means "disabled for all, -1 if unknown"
|
||||
*/
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
grilio_parser_get_int32(&rilp, NULL); /* count */
|
||||
grilio_parser_get_int32(&rilp, &bearer_class);
|
||||
DBG("Active services: %d", bearer_class);
|
||||
cb(ril_error_ok(&error), bearer_class, cbd->data);
|
||||
} else {
|
||||
ofono_error("Call Barring query error %d", status);
|
||||
cb(ril_error_failure(&error), 0, cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_call_barring_query(struct ofono_call_barring *b,
|
||||
const char *lock, int cls,
|
||||
ofono_call_barring_query_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_call_barring *bd = ofono_call_barring_get_data(b);
|
||||
char cls_textual[RIL_MAX_SERVICE_LENGTH];
|
||||
GRilIoRequest *req = grilio_request_new();
|
||||
|
||||
DBG("lock: %s, services to query: %d", lock, cls);
|
||||
|
||||
/*
|
||||
* RIL modems do 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;
|
||||
}
|
||||
|
||||
sprintf(cls_textual, "%d", cls);
|
||||
|
||||
/*
|
||||
* See 3GPP 27.007 7.4 for parameter descriptions.
|
||||
* According to ril.h password should be empty string "" when not
|
||||
* needed, but in reality we only need to give string length as 0
|
||||
*/
|
||||
grilio_request_append_int32(req, RIL_QUERY_STRING_COUNT);
|
||||
grilio_request_append_utf8(req, lock); /* Facility code */
|
||||
grilio_request_append_int32(req, 0); /* Password length */
|
||||
grilio_request_append_utf8(req, cls_textual);
|
||||
grilio_request_append_utf8(req, NULL); /* AID (not yet supported) */
|
||||
|
||||
ril_call_barring_submit_request(bd, req,
|
||||
RIL_REQUEST_QUERY_FACILITY_LOCK,
|
||||
ril_call_barring_query_cb, cb, data);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_call_barring_set_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_call_barring_cbd *cbd = user_data;
|
||||
ofono_call_barring_set_cb_t cb = cbd->cb.set;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
cb(ril_error_ok(&error), cbd->data);
|
||||
} else {
|
||||
ofono_error("Call Barring Set error %d", status);
|
||||
cb(ril_error_failure(&error), cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_call_barring_set(struct ofono_call_barring *b,
|
||||
const char *lock, int enable, const char *passwd, int cls,
|
||||
ofono_call_barring_set_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_call_barring *bd = ofono_call_barring_get_data(b);
|
||||
char cls_textual[RIL_MAX_SERVICE_LENGTH];
|
||||
GRilIoRequest *req = grilio_request_new();
|
||||
|
||||
DBG("lock: %s, enable: %i, bearer class: %i", 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;
|
||||
}
|
||||
|
||||
sprintf(cls_textual, "%d", cls);
|
||||
|
||||
/* See 3GPP 27.007 7.4 for parameter descriptions */
|
||||
grilio_request_append_int32(req, RIL_SET_STRING_COUNT);
|
||||
grilio_request_append_utf8(req, lock); /* Facility code */
|
||||
grilio_request_append_utf8(req, enable ?
|
||||
RIL_FACILITY_LOCK :
|
||||
RIL_FACILITY_UNLOCK);
|
||||
grilio_request_append_utf8(req, passwd);
|
||||
grilio_request_append_utf8(req, cls_textual);
|
||||
grilio_request_append_utf8(req, NULL); /* AID (not yet supported) */
|
||||
|
||||
ril_call_barring_submit_request(bd, req,
|
||||
RIL_REQUEST_SET_FACILITY_LOCK,
|
||||
ril_call_barring_set_cb, cb, data);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_call_barring_set_passwd_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_call_barring_cbd *cbd = user_data;
|
||||
ofono_call_barring_set_cb_t cb = cbd->cb.set;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
cb(ril_error_ok(&error), cbd->data);
|
||||
} else {
|
||||
ofono_error("Call Barring Set PW error %d", status);
|
||||
cb(ril_error_failure(&error), cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_call_barring_set_passwd(struct ofono_call_barring *b,
|
||||
const char *lock, const char *old_passwd,
|
||||
const char *new_passwd, ofono_call_barring_set_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
struct ril_call_barring *bd = ofono_call_barring_get_data(b);
|
||||
GRilIoRequest *req = grilio_request_new();
|
||||
|
||||
DBG("");
|
||||
grilio_request_append_int32(req, RIL_SET_PW_STRING_COUNT);
|
||||
grilio_request_append_utf8(req, lock); /* Facility code */
|
||||
grilio_request_append_utf8(req, old_passwd);
|
||||
grilio_request_append_utf8(req, new_passwd);
|
||||
|
||||
ril_call_barring_submit_request(bd, req,
|
||||
RIL_REQUEST_CHANGE_BARRING_PASSWORD,
|
||||
ril_call_barring_set_passwd_cb, cb, data);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static gboolean ril_call_barring_register(gpointer user_data)
|
||||
{
|
||||
struct ofono_call_barring *b = user_data;
|
||||
struct ril_call_barring *bd = ril_call_barring_get_data(b);
|
||||
|
||||
GASSERT(bd->timer_id);
|
||||
bd->timer_id = 0;
|
||||
ofono_call_barring_register(b);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int ril_call_barring_probe(struct ofono_call_barring *b,
|
||||
unsigned int vendor, void *data)
|
||||
{
|
||||
struct ril_modem *modem = data;
|
||||
struct ril_call_barring *bd = g_new0(struct ril_call_barring, 1);
|
||||
|
||||
DBG("");
|
||||
bd->q = grilio_queue_new(ril_modem_io(modem));
|
||||
bd->timer_id = g_idle_add(ril_call_barring_register, b);
|
||||
ofono_call_barring_set_data(b, bd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ril_call_barring_remove(struct ofono_call_barring *b)
|
||||
{
|
||||
struct ril_call_barring *bd = ril_call_barring_get_data(b);
|
||||
|
||||
DBG("");
|
||||
ofono_call_barring_set_data(b, NULL);
|
||||
|
||||
if (bd->timer_id > 0) {
|
||||
g_source_remove(bd->timer_id);
|
||||
}
|
||||
|
||||
grilio_queue_cancel_all(bd->q, FALSE);
|
||||
grilio_queue_unref(bd->q);
|
||||
g_free(bd);
|
||||
}
|
||||
|
||||
const struct ofono_call_barring_driver ril_call_barring_driver = {
|
||||
.name = RILMODEM_DRIVER,
|
||||
.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
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
315
ofono/drivers/ril/ril_call_forward.c
Normal file
315
ofono/drivers/ril/ril_call_forward.c
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
#include "ril_constants.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
struct ril_call_forward {
|
||||
GRilIoQueue *q;
|
||||
guint timer_id;
|
||||
};
|
||||
|
||||
enum ril_call_forward_cmd {
|
||||
CF_ACTION_DISABLE,
|
||||
CF_ACTION_ENABLE,
|
||||
CF_ACTION_UNUSED,
|
||||
CF_ACTION_REGISTRATION,
|
||||
CF_ACTION_ERASURE
|
||||
};
|
||||
|
||||
struct ril_call_forward_cbd {
|
||||
struct ril_call_forward *fd;
|
||||
union _ofono_call_forward_cb {
|
||||
ofono_call_forwarding_query_cb_t query;
|
||||
ofono_call_forwarding_set_cb_t set;
|
||||
gpointer ptr;
|
||||
} cb;
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
#define ril_call_forward_cbd_free g_free
|
||||
|
||||
static inline struct ril_call_forward *ril_call_forward_get_data(
|
||||
struct ofono_call_forwarding *cf)
|
||||
{
|
||||
return ofono_call_forwarding_get_data(cf);
|
||||
}
|
||||
|
||||
static struct ril_call_forward_cbd *ril_call_forward_cbd_new(void *cb,
|
||||
void *data)
|
||||
{
|
||||
struct ril_call_forward_cbd *cbd;
|
||||
|
||||
cbd = g_new0(struct ril_call_forward_cbd, 1);
|
||||
cbd->cb.ptr = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static inline void ril_call_forward_submit_request(struct ril_call_forward *fd,
|
||||
GRilIoRequest* req, guint code, GRilIoChannelResponseFunc response,
|
||||
void *cb, void *data)
|
||||
{
|
||||
grilio_queue_send_request_full(fd->q, req, code, response,
|
||||
ril_call_forward_cbd_free,
|
||||
ril_call_forward_cbd_new(cb, data));
|
||||
}
|
||||
|
||||
static void ril_forward_set_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_call_forward_cbd *cbd = user_data;
|
||||
ofono_call_forwarding_set_cb_t cb = cbd->cb.set;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
cb(ril_error_ok(&error), cbd->data);
|
||||
} else {
|
||||
ofono_error("CF setting failed");
|
||||
cb(ril_error_failure(&error), cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_call_forward_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 ril_call_forward *fd = ril_call_forward_get_data(cf);
|
||||
GRilIoRequest *req = grilio_request_new();
|
||||
|
||||
ofono_info("cf registration");
|
||||
grilio_request_append_int32(req, CF_ACTION_REGISTRATION);
|
||||
grilio_request_append_int32(req, type);
|
||||
|
||||
/*
|
||||
* 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
|
||||
* BEARER_CLASS_VOICE as per RIL design.
|
||||
*/
|
||||
if (cls == BEARER_CLASS_DEFAULT) {
|
||||
cls = BEARER_CLASS_VOICE;
|
||||
}
|
||||
|
||||
grilio_request_append_int32(req, cls);
|
||||
grilio_request_append_int32(req, number->type);
|
||||
grilio_request_append_utf8(req, number->number);
|
||||
grilio_request_append_int32(req, time);
|
||||
|
||||
ril_call_forward_submit_request(fd, req, RIL_REQUEST_SET_CALL_FORWARD,
|
||||
ril_forward_set_cb, cb, data);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_call_forward_send_cmd(struct ofono_call_forwarding *cf,
|
||||
int type, int cls, ofono_call_forwarding_set_cb_t cb,
|
||||
void *data, int action)
|
||||
{
|
||||
struct ril_call_forward *fd = ril_call_forward_get_data(cf);
|
||||
GRilIoRequest *req = grilio_request_new();
|
||||
|
||||
grilio_request_append_int32(req, action);
|
||||
grilio_request_append_int32(req, type);
|
||||
|
||||
/*
|
||||
* 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
|
||||
* BEARER_CLASS_VOICE as per RIL design.
|
||||
*/
|
||||
if (cls == BEARER_CLASS_DEFAULT) {
|
||||
cls = BEARER_CLASS_VOICE;
|
||||
}
|
||||
|
||||
grilio_request_append_int32(req, cls); /* Service class */
|
||||
|
||||
/* Following 3 values have no real meaning in erasure
|
||||
* but apparently RIL expects them so fields need to
|
||||
* be filled. Otherwise there is no response
|
||||
*/
|
||||
grilio_request_append_int32(req, 0x81); /* TOA unknown */
|
||||
grilio_request_append_utf8(req, "1234567890");
|
||||
grilio_request_append_int32(req, 60);
|
||||
|
||||
ril_call_forward_submit_request(fd, req, RIL_REQUEST_SET_CALL_FORWARD,
|
||||
ril_forward_set_cb, cb, data);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_call_forward_erasure(struct ofono_call_forwarding *cf,
|
||||
int type, int cls, ofono_call_forwarding_set_cb_t cb, void *data)
|
||||
{
|
||||
ofono_info("CF_ACTION_ERASURE");
|
||||
ril_call_forward_send_cmd(cf, type, cls, cb, data, CF_ACTION_ERASURE);
|
||||
}
|
||||
|
||||
static void ril_call_forward_deactivate(struct ofono_call_forwarding *cf,
|
||||
int type, int cls, ofono_call_forwarding_set_cb_t cb, void *data)
|
||||
{
|
||||
ofono_info("CF_ACTION_DISABLE");
|
||||
ril_call_forward_send_cmd(cf, type, cls, cb, data, CF_ACTION_DISABLE);
|
||||
}
|
||||
|
||||
static void ril_call_forward_activate(struct ofono_call_forwarding *cf,
|
||||
int type, int cls, ofono_call_forwarding_set_cb_t cb, void *data)
|
||||
{
|
||||
ofono_info("CF_ACTION_ENABLE");
|
||||
ril_call_forward_send_cmd(cf, type, cls, cb, data, CF_ACTION_ENABLE);
|
||||
}
|
||||
|
||||
static void ril_call_forward_query_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_call_forward_cbd *cbd = user_data;
|
||||
ofono_call_forwarding_query_cb_t cb = cbd->cb.query;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
struct ofono_call_forwarding_condition *list = NULL;
|
||||
GRilIoParser rilp;
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
grilio_parser_get_int32(&rilp, &count);
|
||||
|
||||
list = g_new0(struct ofono_call_forwarding_condition, count);
|
||||
for (i = 0; i < count; i++) {
|
||||
struct ofono_call_forwarding_condition *fw = list + i;
|
||||
char *str;
|
||||
|
||||
grilio_parser_get_int32(&rilp, &fw->status);
|
||||
grilio_parser_get_int32(&rilp, NULL);
|
||||
grilio_parser_get_int32(&rilp, &fw->cls);
|
||||
grilio_parser_get_int32(&rilp, &fw->phone_number.type);
|
||||
str = grilio_parser_get_utf8(&rilp);
|
||||
if (str) {
|
||||
strncpy(fw->phone_number.number, str,
|
||||
OFONO_MAX_PHONE_NUMBER_LENGTH);
|
||||
fw->phone_number.number[
|
||||
OFONO_MAX_PHONE_NUMBER_LENGTH] = 0;
|
||||
g_free(str);
|
||||
}
|
||||
grilio_parser_get_int32(&rilp, &fw->time);
|
||||
}
|
||||
|
||||
cb(ril_error_ok(&error), count, list, cbd->data);
|
||||
g_free(list);
|
||||
} else {
|
||||
ofono_error("CF query failed");
|
||||
cb(ril_error_failure(&error), 0, NULL, cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_call_forward_query(struct ofono_call_forwarding *cf, int type,
|
||||
int cls, ofono_call_forwarding_query_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_call_forward *fd = ril_call_forward_get_data(cf);
|
||||
GRilIoRequest *req = grilio_request_new();
|
||||
|
||||
ofono_info("cf query");
|
||||
grilio_request_append_int32(req, 2);
|
||||
grilio_request_append_int32(req, type);
|
||||
|
||||
/*
|
||||
* 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 per RIL design.
|
||||
*/
|
||||
if (cls == BEARER_CLASS_DEFAULT) {
|
||||
cls = SERVICE_CLASS_NONE;
|
||||
}
|
||||
|
||||
grilio_request_append_int32(req, cls);
|
||||
|
||||
/* Following 3 values have no real meaning in query
|
||||
* but apparently RIL expects them so fields need to
|
||||
* be filled. Otherwise there is no response
|
||||
*/
|
||||
grilio_request_append_int32(req, 0x81); /* TOA unknown */
|
||||
grilio_request_append_utf8(req, "1234567890");
|
||||
grilio_request_append_int32(req, 0);
|
||||
|
||||
ril_call_forward_submit_request(fd, req,
|
||||
RIL_REQUEST_QUERY_CALL_FORWARD_STATUS,
|
||||
ril_call_forward_query_cb, cb, data);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static gboolean ril_call_forward_register(gpointer user_data)
|
||||
{
|
||||
struct ofono_call_forwarding *cf = user_data;
|
||||
struct ril_call_forward *fd = ril_call_forward_get_data(cf);
|
||||
|
||||
fd->timer_id = 0;
|
||||
ofono_call_forwarding_register(cf);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int ril_call_forward_probe(struct ofono_call_forwarding *cf,
|
||||
unsigned int vendor, void *data)
|
||||
{
|
||||
struct ril_modem *modem = data;
|
||||
struct ril_call_forward *fd = g_try_new0(struct ril_call_forward, 1);
|
||||
|
||||
DBG("");
|
||||
fd->q = grilio_queue_new(ril_modem_io(modem));
|
||||
fd->timer_id = g_idle_add(ril_call_forward_register, cf);
|
||||
ofono_call_forwarding_set_data(cf, fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ril_call_forward_remove(struct ofono_call_forwarding *cf)
|
||||
{
|
||||
struct ril_call_forward *fd = ril_call_forward_get_data(cf);
|
||||
|
||||
DBG("");
|
||||
ofono_call_forwarding_set_data(cf, NULL);
|
||||
|
||||
if (fd->timer_id > 0) {
|
||||
g_source_remove(fd->timer_id);
|
||||
}
|
||||
|
||||
grilio_queue_cancel_all(fd->q, FALSE);
|
||||
grilio_queue_unref(fd->q);
|
||||
g_free(fd);
|
||||
}
|
||||
|
||||
const struct ofono_call_forwarding_driver ril_call_forwarding_driver = {
|
||||
.name = RILMODEM_DRIVER,
|
||||
.probe = ril_call_forward_probe,
|
||||
.remove = ril_call_forward_remove,
|
||||
.erasure = ril_call_forward_erasure,
|
||||
.deactivation = ril_call_forward_deactivate,
|
||||
.query = ril_call_forward_query,
|
||||
.registration = ril_call_forward_registration,
|
||||
.activation = ril_call_forward_activate
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
309
ofono/drivers/ril/ril_call_settings.c
Normal file
309
ofono/drivers/ril/ril_call_settings.c
Normal file
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
#include "ril_constants.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
struct ril_call_settings {
|
||||
GRilIoQueue *q;
|
||||
guint timer_id;
|
||||
};
|
||||
|
||||
struct ril_call_settings_cbd {
|
||||
union _ofono_call_settings_cb {
|
||||
ofono_call_settings_status_cb_t status;
|
||||
ofono_call_settings_set_cb_t set;
|
||||
ofono_call_settings_clir_cb_t clir;
|
||||
gpointer ptr;
|
||||
} cb;
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
#define ril_call_settings_cbd_free g_free
|
||||
|
||||
static inline struct ril_call_settings *ril_call_settings_get_data(
|
||||
struct ofono_call_settings *b)
|
||||
{
|
||||
return ofono_call_settings_get_data(b);
|
||||
}
|
||||
|
||||
static struct ril_call_settings_cbd *ril_call_settings_cbd_new(void *cb,
|
||||
void *data)
|
||||
{
|
||||
struct ril_call_settings_cbd *cbd;
|
||||
|
||||
cbd = g_new0(struct ril_call_settings_cbd, 1);
|
||||
cbd->cb.ptr = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static inline void ril_call_settings_submit_req(struct ril_call_settings *sd,
|
||||
GRilIoRequest* req, guint code, GRilIoChannelResponseFunc response,
|
||||
void *cb, void *data)
|
||||
{
|
||||
grilio_queue_send_request_full(sd->q, req, code, response,
|
||||
ril_call_settings_cbd_free,
|
||||
ril_call_settings_cbd_new(cb, data));
|
||||
}
|
||||
|
||||
static void ril_call_settings_clip_query_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_call_settings_cbd *cbd = user_data;
|
||||
ofono_call_settings_status_cb_t cb = cbd->cb.status;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
GRilIoParser rilp;
|
||||
int res = 0;
|
||||
|
||||
/* data length of the response */
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
if (grilio_parser_get_int32(&rilp, &res) && res > 0) {
|
||||
grilio_parser_get_int32(&rilp, &res);
|
||||
}
|
||||
|
||||
cb(ril_error_ok(&error), res, cbd->data);
|
||||
} else {
|
||||
cb(ril_error_failure(&error), -1, cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_call_settings_set_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_call_settings_cbd *cbd = user_data;
|
||||
ofono_call_settings_set_cb_t cb = cbd->cb.set;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
cb(ril_error_ok(&error), cbd->data);
|
||||
} else {
|
||||
cb(ril_error_failure(&error), cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_call_settings_cw_set(struct ofono_call_settings *cs, int mode,
|
||||
int cls, ofono_call_settings_set_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_call_settings *sd = ril_call_settings_get_data(cs);
|
||||
GRilIoRequest *req = grilio_request_sized_new(12);
|
||||
|
||||
grilio_request_append_int32(req, 2); /* Number of params */
|
||||
grilio_request_append_int32(req, mode); /* on/off */
|
||||
|
||||
/* 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_VOICE effectively making it the
|
||||
* default bearer. This in line with API which is
|
||||
* contains only voice anyways.
|
||||
*/
|
||||
if (cls == BEARER_CLASS_DEFAULT) {
|
||||
cls = BEARER_CLASS_VOICE;
|
||||
}
|
||||
|
||||
grilio_request_append_int32(req, cls); /* Service class */
|
||||
|
||||
ril_call_settings_submit_req(sd, req, RIL_REQUEST_SET_CALL_WAITING,
|
||||
ril_call_settings_set_cb, cb, data);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_call_settings_cw_query_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_call_settings_cbd *cbd = user_data;
|
||||
ofono_call_settings_status_cb_t cb = cbd->cb.status;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
GRilIoParser rilp;
|
||||
int res = 0;
|
||||
int sv = 0;
|
||||
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
|
||||
/* first value in int[] is len so let's skip that */
|
||||
grilio_parser_get_int32(&rilp, NULL);
|
||||
|
||||
/* status of call waiting service, disabled is returned only if
|
||||
* service is not active for any service class */
|
||||
grilio_parser_get_int32(&rilp, &res);
|
||||
DBG("CW enabled/disabled: %d", res);
|
||||
|
||||
if (res > 0) {
|
||||
/* services for which call waiting is enabled,
|
||||
27.007 7.12 */
|
||||
grilio_parser_get_int32(&rilp, &sv);
|
||||
DBG("CW enabled for: %d", sv);
|
||||
}
|
||||
|
||||
cb(ril_error_ok(&error), sv, cbd->data);
|
||||
} else {
|
||||
cb(ril_error_failure(&error), -1, cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_call_settings_cw_query(struct ofono_call_settings *cs, int cls,
|
||||
ofono_call_settings_status_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_call_settings *sd = ril_call_settings_get_data(cs);
|
||||
GRilIoRequest *req = grilio_request_sized_new(8);
|
||||
|
||||
grilio_request_append_int32(req, 1); /* Number of params */
|
||||
|
||||
/*
|
||||
* RILD expects service class to be 0 as certain carriers can reject
|
||||
* the query with specific service class
|
||||
*/
|
||||
grilio_request_append_int32(req, 0);
|
||||
|
||||
ril_call_settings_submit_req(sd, req, RIL_REQUEST_QUERY_CALL_WAITING,
|
||||
ril_call_settings_cw_query_cb, cb, data);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_call_settings_clip_query(struct ofono_call_settings *cs,
|
||||
ofono_call_settings_status_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_call_settings *sd = ril_call_settings_get_data(cs);
|
||||
|
||||
ril_call_settings_submit_req(sd, NULL, RIL_REQUEST_QUERY_CLIP,
|
||||
ril_call_settings_clip_query_cb, cb, data);
|
||||
}
|
||||
|
||||
static void ril_call_settings_clir_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_call_settings_cbd *cbd = user_data;
|
||||
ofono_call_settings_clir_cb_t cb = cbd->cb.clir;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
GRilIoParser rilp;
|
||||
int override = -1, network = -1;
|
||||
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
/*first value in int[] is len so let's skip that*/
|
||||
grilio_parser_get_int32(&rilp, NULL);
|
||||
/* Set HideCallerId property from network */
|
||||
grilio_parser_get_int32(&rilp, &override);
|
||||
/* CallingLineRestriction indicates the state of
|
||||
the CLIR supplementary service in the network */
|
||||
grilio_parser_get_int32(&rilp, &network);
|
||||
|
||||
cb(ril_error_ok(&error), override, network, cbd->data);
|
||||
} else {
|
||||
cb(ril_error_failure(&error), -1, -1, cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_call_settings_clir_query(struct ofono_call_settings *cs,
|
||||
ofono_call_settings_clir_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_call_settings *sd = ril_call_settings_get_data(cs);
|
||||
|
||||
ril_call_settings_submit_req(sd, NULL, RIL_REQUEST_GET_CLIR,
|
||||
ril_call_settings_clir_cb, cb, data);
|
||||
}
|
||||
|
||||
static void ril_call_settings_clir_set(struct ofono_call_settings *cs,
|
||||
int mode, ofono_call_settings_set_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_call_settings *sd = ril_call_settings_get_data(cs);
|
||||
GRilIoRequest *req = grilio_request_sized_new(8);
|
||||
|
||||
grilio_request_append_int32(req, 1); /* Number of params */
|
||||
grilio_request_append_int32(req, mode); /* for outgoing calls */
|
||||
|
||||
ril_call_settings_submit_req(sd, req, RIL_REQUEST_SET_CLIR,
|
||||
ril_call_settings_set_cb, cb, data);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static gboolean ril_call_settings_register(gpointer user_data)
|
||||
{
|
||||
struct ofono_call_settings *cs = user_data;
|
||||
struct ril_call_settings *sd = ril_call_settings_get_data(cs);
|
||||
|
||||
DBG("");
|
||||
GASSERT(sd->timer_id);
|
||||
sd->timer_id = 0;
|
||||
ofono_call_settings_register(cs);
|
||||
|
||||
/* Single-shot */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int ril_call_settings_probe(struct ofono_call_settings *cs,
|
||||
unsigned int vendor, void *data)
|
||||
{
|
||||
struct ril_modem *modem = data;
|
||||
struct ril_call_settings *sd = g_try_new0(struct ril_call_settings, 1);
|
||||
|
||||
DBG("");
|
||||
sd->q = grilio_queue_new(ril_modem_io(modem));
|
||||
sd->timer_id = g_idle_add(ril_call_settings_register, cs);
|
||||
ofono_call_settings_set_data(cs, sd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ril_call_settings_remove(struct ofono_call_settings *cs)
|
||||
{
|
||||
struct ril_call_settings *sd = ril_call_settings_get_data(cs);
|
||||
|
||||
DBG("");
|
||||
ofono_call_settings_set_data(cs, NULL);
|
||||
|
||||
if (sd->timer_id > 0) {
|
||||
g_source_remove(sd->timer_id);
|
||||
}
|
||||
|
||||
grilio_queue_cancel_all(sd->q, FALSE);
|
||||
grilio_queue_unref(sd->q);
|
||||
g_free(sd);
|
||||
}
|
||||
|
||||
const struct ofono_call_settings_driver ril_call_settings_driver = {
|
||||
.name = RILMODEM_DRIVER,
|
||||
.probe = ril_call_settings_probe,
|
||||
.remove = ril_call_settings_remove,
|
||||
.clip_query = ril_call_settings_clip_query,
|
||||
.cw_query = ril_call_settings_cw_query,
|
||||
.cw_set = ril_call_settings_cw_set,
|
||||
.clir_query = ril_call_settings_clir_query,
|
||||
.clir_set = ril_call_settings_clir_set
|
||||
|
||||
/*
|
||||
* Not supported in RIL API
|
||||
* .colp_query = ril_call_settings_colp_query,
|
||||
* .colr_query = ril_call_settings_colr_query
|
||||
*/
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
151
ofono/drivers/ril/ril_call_volume.c
Normal file
151
ofono/drivers/ril/ril_call_volume.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
#include "ril_constants.h"
|
||||
|
||||
struct ril_call_volume {
|
||||
struct ofono_call_volume *v;
|
||||
GRilIoQueue *q;
|
||||
guint timer_id;
|
||||
};
|
||||
|
||||
struct ril_call_volume_req {
|
||||
ofono_call_volume_cb_t cb;
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
static inline struct ril_call_volume *ril_call_volume_get_data(
|
||||
struct ofono_call_volume *v)
|
||||
{
|
||||
return ofono_call_volume_get_data(v);
|
||||
}
|
||||
|
||||
static void ril_call_volume_mute_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_call_volume_req *cbd = user_data;
|
||||
ofono_call_volume_cb_t cb = cbd->cb;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
cb(ril_error_ok(&error), cbd->data);
|
||||
} else {
|
||||
ofono_error("Could not set the ril mute state");
|
||||
cb(ril_error_failure(&error), cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_call_volume_mute(struct ofono_call_volume *v, int muted,
|
||||
ofono_call_volume_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_call_volume *vd = ril_call_volume_get_data(v);
|
||||
struct ril_call_volume_req *cbd;
|
||||
GRilIoRequest *req = grilio_request_sized_new(8);
|
||||
|
||||
cbd = g_new(struct ril_call_volume_req, 1);
|
||||
cbd->cb = cb;
|
||||
cbd->data = data;
|
||||
|
||||
DBG("%d", muted);
|
||||
grilio_request_append_int32(req, 1);
|
||||
grilio_request_append_int32(req, muted);
|
||||
grilio_queue_send_request_full(vd->q, req, RIL_REQUEST_SET_MUTE,
|
||||
ril_call_volume_mute_cb, g_free, cbd);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_call_volume_query_mute_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_call_volume *vd = user_data;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
int muted = 0;
|
||||
GRilIoParser rilp;
|
||||
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
grilio_parser_get_int32(&rilp, NULL); /* Array length */
|
||||
grilio_parser_get_int32(&rilp, &muted);
|
||||
DBG("{%d}", muted);
|
||||
ofono_call_volume_set_muted(vd->v, muted);
|
||||
} else {
|
||||
ofono_error("Could not retrive the ril mute state");
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean ril_call_volume_register(gpointer user_data)
|
||||
{
|
||||
struct ril_call_volume *vd = user_data;
|
||||
|
||||
DBG("");
|
||||
GASSERT(vd->timer_id);
|
||||
vd->timer_id = 0;
|
||||
ofono_call_volume_register(vd->v);
|
||||
|
||||
/* Probe the mute state */
|
||||
grilio_queue_send_request_full(vd->q, NULL,
|
||||
RIL_REQUEST_GET_MUTE, ril_call_volume_query_mute_cb, NULL, vd);
|
||||
|
||||
/* This makes the timeout a single-shot */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int ril_call_volume_probe(struct ofono_call_volume *v,
|
||||
unsigned int vendor, void *data)
|
||||
{
|
||||
struct ril_modem *modem = data;
|
||||
struct ril_call_volume *vd = g_new0(struct ril_call_volume, 1);
|
||||
|
||||
DBG("");
|
||||
vd->v = v;
|
||||
vd->q = grilio_queue_new(ril_modem_io(modem));
|
||||
vd->timer_id = g_idle_add(ril_call_volume_register, vd);
|
||||
ofono_call_volume_set_data(v, vd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ril_call_volume_remove(struct ofono_call_volume *v)
|
||||
{
|
||||
struct ril_call_volume *vd = ril_call_volume_get_data(v);
|
||||
|
||||
DBG("");
|
||||
ofono_call_volume_set_data(v, NULL);
|
||||
|
||||
if (vd->timer_id) {
|
||||
g_source_remove(vd->timer_id);
|
||||
}
|
||||
|
||||
grilio_queue_cancel_all(vd->q, FALSE);
|
||||
grilio_queue_unref(vd->q);
|
||||
g_free(vd);
|
||||
}
|
||||
|
||||
const struct ofono_call_volume_driver ril_call_volume_driver = {
|
||||
.name = RILMODEM_DRIVER,
|
||||
.probe = ril_call_volume_probe,
|
||||
.remove = ril_call_volume_remove,
|
||||
.mute = ril_call_volume_mute,
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
119
ofono/drivers/ril/ril_cbs.c
Normal file
119
ofono/drivers/ril/ril_cbs.c
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
#include "ril_constants.h"
|
||||
|
||||
struct ril_cbs {
|
||||
struct ofono_cbs *cbs;
|
||||
GRilIoChannel *io;
|
||||
guint timer_id;
|
||||
gulong event_id;
|
||||
};
|
||||
|
||||
static void ril_set_topics(struct ofono_cbs *cbs, const char *topics,
|
||||
ofono_cbs_set_cb_t cb, void *data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
cb(ril_error_ok(&error), data);
|
||||
}
|
||||
|
||||
static void ril_clear_topics(struct ofono_cbs *cbs,
|
||||
ofono_cbs_set_cb_t cb, void *data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
cb(ril_error_ok(&error), data);
|
||||
}
|
||||
|
||||
static void ril_cbs_notify(GRilIoChannel *io, guint code,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_cbs *cd = user_data;
|
||||
GRilIoParser rilp;
|
||||
char* pdu;
|
||||
|
||||
GASSERT(code == RIL_UNSOL_ON_USSD);
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
pdu = grilio_parser_get_utf8(&rilp);
|
||||
DBG("%s", pdu);
|
||||
if (pdu) {
|
||||
ofono_cbs_notify(cd->cbs, (const guchar *)pdu, strlen(pdu));
|
||||
g_free(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean ril_cbs_register(gpointer user_data)
|
||||
{
|
||||
struct ril_cbs *cd = user_data;
|
||||
|
||||
DBG("");
|
||||
GASSERT(cd->timer_id);
|
||||
cd->timer_id = 0;
|
||||
ofono_cbs_register(cd->cbs);
|
||||
|
||||
cd->event_id = grilio_channel_add_unsol_event_handler(cd->io,
|
||||
ril_cbs_notify, RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS, cd);
|
||||
|
||||
/* Single-shot */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int ril_cbs_probe(struct ofono_cbs *cbs, unsigned int vendor,
|
||||
void *data)
|
||||
{
|
||||
struct ril_modem *modem = data;
|
||||
struct ril_cbs *cd = g_try_new0(struct ril_cbs, 1);
|
||||
|
||||
DBG("");
|
||||
cd->cbs = cbs;
|
||||
cd->io = grilio_channel_ref(ril_modem_io(modem));
|
||||
cd->timer_id = g_idle_add(ril_cbs_register, cd);
|
||||
ofono_cbs_set_data(cbs, cd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ril_cbs_remove(struct ofono_cbs *cbs)
|
||||
{
|
||||
struct ril_cbs *cd = ofono_cbs_get_data(cbs);
|
||||
|
||||
DBG("");
|
||||
ofono_cbs_set_data(cbs, NULL);
|
||||
|
||||
if (cd->timer_id > 0) {
|
||||
g_source_remove(cd->timer_id);
|
||||
}
|
||||
|
||||
grilio_channel_remove_handler(cd->io, cd->event_id);
|
||||
grilio_channel_unref(cd->io);
|
||||
g_free(cd);
|
||||
}
|
||||
|
||||
const struct ofono_cbs_driver ril_cbs_driver = {
|
||||
.name = RILMODEM_DRIVER,
|
||||
.probe = ril_cbs_probe,
|
||||
.remove = ril_cbs_remove,
|
||||
.set_topics = ril_set_topics,
|
||||
.clear_topics = ril_clear_topics
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
408
ofono/drivers/ril/ril_constants.h
Normal file
408
ofono/drivers/ril/ril_constants.h
Normal file
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
*
|
||||
* RIL constants adopted from AOSP's header:
|
||||
*
|
||||
* /hardware/ril/reference_ril/ril.h
|
||||
*
|
||||
* Copyright (C) 2013 Canonical Ltd.
|
||||
* Copyright (C) 2013-2015 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.
|
||||
*/
|
||||
|
||||
#ifndef __RIL_CONSTANTS_H
|
||||
#define __RIL_CONSTANTS_H 1
|
||||
|
||||
/* Error Codes */
|
||||
#define RIL_E_SUCCESS 0
|
||||
#define RIL_E_RADIO_NOT_AVAILABLE 1
|
||||
#define RIL_E_GENERIC_FAILURE 2
|
||||
#define RIL_E_PASSWORD_INCORRECT 3
|
||||
#define RIL_E_SIM_PIN2 4
|
||||
#define RIL_E_SIM_PUK2 5
|
||||
#define RIL_E_REQUEST_NOT_SUPPORTED 6
|
||||
#define RIL_E_CANCELLED 7
|
||||
#define RIL_E_OP_NOT_ALLOWED_DURING_VOICE_CALL 8
|
||||
#define RIL_E_OP_NOT_ALLOWED_BEFORE_REG_TO_NW 9
|
||||
#define RIL_E_SMS_SEND_FAIL_RETRY 10
|
||||
#define RIL_E_SIM_ABSENT 11
|
||||
#define RIL_E_SUBSCRIPTION_NOT_AVAILABLE 12
|
||||
#define RIL_E_MODE_NOT_SUPPORTED 13
|
||||
#define RIL_E_FDN_CHECK_FAILURE 14
|
||||
#define RIL_E_ILLEGAL_SIM_OR_ME 15
|
||||
#define RIL_E_UNUSED 16
|
||||
#define RIL_E_DIAL_MODIFIED_TO_USSD 17
|
||||
#define RIL_E_DIAL_MODIFIED_TO_SS 18
|
||||
#define RIL_E_DIAL_MODIFIED_TO_DIAL 19
|
||||
#define RIL_E_USSD_MODIFIED_TO_DIAL 20
|
||||
#define RIL_E_USSD_MODIFIED_TO_SS 21
|
||||
#define RIL_E_USSD_MODIFIED_TO_USSD 22
|
||||
#define RIL_E_SS_MODIFIED_TO_DIAL 23
|
||||
#define RIL_E_SS_MODIFIED_TO_USSD 24
|
||||
#define RIL_E_SS_MODIFIED_TO_SS 25
|
||||
#define RIL_E_SUBSCRIPTION_NOT_SUPPORTED 26
|
||||
#define RIL_E_MISSING_RESOURCE 27
|
||||
#define RIL_E_NO_SUCH_ELEMENT 28
|
||||
#define RIL_E_INVALID_PARAMETER 29
|
||||
|
||||
/* call states */
|
||||
enum ril_call_state {
|
||||
RIL_CALL_ACTIVE,
|
||||
RIL_CALL_HOLDING,
|
||||
RIL_CALL_DIALING,
|
||||
RIL_CALL_ALERTING,
|
||||
RIL_CALL_INCOMING,
|
||||
RIL_CALL_WAITING
|
||||
};
|
||||
|
||||
/* Radio state */
|
||||
enum ril_radio_state {
|
||||
RADIO_STATE_OFF,
|
||||
RADIO_STATE_UNAVAILABLE,
|
||||
RADIO_STATE_SIM_NOT_READY,
|
||||
RADIO_STATE_SIM_LOCKED_OR_ABSENT,
|
||||
RADIO_STATE_SIM_READY,
|
||||
RADIO_STATE_RUIM_NOT_READY,
|
||||
RADIO_STATE_RUIM_READY,
|
||||
RADIO_STATE_RUIM_LOCKED_OR_ABSENT,
|
||||
RADIO_STATE_NV_NOT_READY,
|
||||
RADIO_STATE_NV_READY,
|
||||
RADIO_STATE_ON
|
||||
};
|
||||
|
||||
/* Preferred network types */
|
||||
enum ril_pref_net_type {
|
||||
PREF_NET_TYPE_GSM_WCDMA,
|
||||
PREF_NET_TYPE_GSM_ONLY,
|
||||
PREF_NET_TYPE_WCDMA,
|
||||
PREF_NET_TYPE_GSM_WCDMA_AUTO,
|
||||
PREF_NET_TYPE_CDMA_EVDO_AUTO,
|
||||
PREF_NET_TYPE_CDMA_ONLY,
|
||||
PREF_NET_TYPE_EVDO_ONLY,
|
||||
PREF_NET_TYPE_GSM_WCDMA_CDMA_EVDO_AUTO,
|
||||
PREF_NET_TYPE_LTE_CDMA_EVDO,
|
||||
PREF_NET_TYPE_LTE_GSM_WCDMA,
|
||||
PREF_NET_TYPE_LTE_CMDA_EVDO_GSM_WCDMA,
|
||||
PREF_NET_TYPE_LTE_ONLY,
|
||||
PREF_NET_TYPE_LTE_WCDMA
|
||||
};
|
||||
|
||||
/* Radio technologies */
|
||||
enum ril_radio_tech {
|
||||
RADIO_TECH_UNKNOWN,
|
||||
RADIO_TECH_GPRS,
|
||||
RADIO_TECH_EDGE,
|
||||
RADIO_TECH_UMTS,
|
||||
RADIO_TECH_IS95A,
|
||||
RADIO_TECH_IS95B,
|
||||
RADIO_TECH_1xRTT ,
|
||||
RADIO_TECH_EVDO_0,
|
||||
RADIO_TECH_EVDO_A,
|
||||
RADIO_TECH_HSDPA,
|
||||
RADIO_TECH_HSUPA ,
|
||||
RADIO_TECH_HSPA,
|
||||
RADIO_TECH_EVDO_B,
|
||||
RADIO_TECH_EHRPD,
|
||||
RADIO_TECH_LTE,
|
||||
RADIO_TECH_HSPAP,
|
||||
RADIO_TECH_GSM,
|
||||
RADIO_TECH_TD_SCDMA,
|
||||
RADIO_TECH_DC_HSDPA
|
||||
};
|
||||
|
||||
/* See RIL_REQUEST_LAST_CALL_FAIL_CAUSE */
|
||||
#define CALL_FAIL_UNOBTAINABLE_NUMBER 1
|
||||
#define CALL_FAIL_NORMAL 16
|
||||
#define CALL_FAIL_BUSY 17
|
||||
#define CALL_FAIL_CONGESTION 34
|
||||
#define CALL_FAIL_ACM_LIMIT_EXCEEDED 68
|
||||
#define CALL_FAIL_CALL_BARRED 240
|
||||
#define CALL_FAIL_FDN_BLOCKED 241
|
||||
#define CALL_FAIL_IMSI_UNKNOWN_IN_VLR 242
|
||||
#define CALL_FAIL_IMEI_NOT_ACCEPTED 243
|
||||
#define CALL_FAIL_DIAL_MODIFIED_TO_USSD 244
|
||||
#define CALL_FAIL_DIAL_MODIFIED_TO_SS 245
|
||||
#define CALL_FAIL_DIAL_MODIFIED_TO_DIAL 246
|
||||
#define CALL_FAIL_CDMA_LOCKED_UNTIL_POWER_CYCLE 1000
|
||||
#define CALL_FAIL_CDMA_DROP 1001
|
||||
#define CALL_FAIL_CDMA_INTERCEPT 1002
|
||||
#define CALL_FAIL_CDMA_REORDER 1003
|
||||
#define CALL_FAIL_CDMA_SO_REJECT 1004
|
||||
#define CALL_FAIL_CDMA_RETRY_ORDER 1005
|
||||
#define CALL_FAIL_CDMA_ACCESS_FAILURE 1006
|
||||
#define CALL_FAIL_CDMA_PREEMPTED 1007
|
||||
#define CALL_FAIL_CDMA_NOT_EMERGENCY 1008
|
||||
#define CALL_FAIL_CDMA_ACCESS_BLOCKED 1009
|
||||
#define CALL_FAIL_ERROR_UNSPECIFIED 0xffff
|
||||
|
||||
/* see RIL_REQUEST_DEACTIVATE_DATA_CALL parameter*/
|
||||
#define RIL_DEACTIVATE_DATA_CALL_NO_REASON 0
|
||||
#define RIL_DEACTIVATE_DATA_CALL_RADIO_SHUTDOWN 1
|
||||
|
||||
/* See RIL_REQUEST_SETUP_DATA_CALL */
|
||||
|
||||
#define RIL_DATA_PROFILE_DEFAULT 0
|
||||
#define RIL_DATA_PROFILE_TETHERED 1
|
||||
#define RIL_DATA_PROFILE_IMS 2
|
||||
#define RIL_DATA_PROFILE_FOTA 3 /* FOTA = Firmware Over the Air */
|
||||
#define RIL_DATA_PROFILE_CBS 4
|
||||
#define RIL_DATA_PROFILE_OEM_BASE 1000 /* Start of OEM-specific profiles */
|
||||
|
||||
#define RIL_AUTH_NONE 0
|
||||
#define RIL_AUTH_PAP 1
|
||||
#define RIL_AUTH_CHAP 2
|
||||
#define RIL_AUTH_BOTH 3
|
||||
|
||||
/* SIM card states */
|
||||
#define RIL_CARDSTATE_ABSENT 0
|
||||
#define RIL_CARDSTATE_PRESENT 1
|
||||
#define RIL_CARDSTATE_ERROR 2
|
||||
|
||||
/* SIM personalization substates */
|
||||
#define RIL_PERSOSUBSTATE_UNKNOWN 0
|
||||
#define RIL_PERSOSUBSTATE_IN_PROGRESS 1
|
||||
#define RIL_PERSOSUBSTATE_READY 2
|
||||
#define RIL_PERSOSUBSTATE_SIM_NETWORK 3
|
||||
#define RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET 4
|
||||
#define RIL_PERSOSUBSTATE_SIM_CORPORATE 5
|
||||
#define RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER 6
|
||||
#define RIL_PERSOSUBSTATE_SIM_SIM 7
|
||||
#define RIL_PERSOSUBSTATE_SIM_NETWORK_PUK 8
|
||||
#define RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK 9
|
||||
#define RIL_PERSOSUBSTATE_SIM_CORPORATE_PUK 10
|
||||
#define RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK 11
|
||||
#define RIL_PERSOSUBSTATE_SIM_SIM_PUK 12
|
||||
#define RIL_PERSOSUBSTATE_RUIM_NETWORK1 13
|
||||
#define RIL_PERSOSUBSTATE_RUIM_NETWORK2 14
|
||||
#define RIL_PERSOSUBSTATE_RUIM_HRPD 15
|
||||
#define RIL_PERSOSUBSTATE_RUIM_CORPORATE 16
|
||||
#define RIL_PERSOSUBSTATE_RUIM_SERVICE_PROVIDER 17
|
||||
#define RIL_PERSOSUBSTATE_RUIM_RUIM 18
|
||||
#define RIL_PERSOSUBSTATE_RUIM_NETWORK1_PUK 19
|
||||
#define RIL_PERSOSUBSTATE_RUIM_NETWORK2_PUK 20
|
||||
#define RIL_PERSOSUBSTATE_RUIM_HRPD_PUK 21
|
||||
#define RIL_PERSOSUBSTATE_RUIM_CORPORATE_PUK 22
|
||||
#define RIL_PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK 23
|
||||
#define RIL_PERSOSUBSTATE_RUIM_RUIM_PUK 24
|
||||
|
||||
/* SIM - App states */
|
||||
#define RIL_APPSTATE_ILLEGAL -1
|
||||
#define RIL_APPSTATE_UNKNOWN 0
|
||||
#define RIL_APPSTATE_DETECTED 1
|
||||
#define RIL_APPSTATE_PIN 2
|
||||
#define RIL_APPSTATE_PUK 3
|
||||
#define RIL_APPSTATE_SUBSCRIPTION_PERSO 4
|
||||
#define RIL_APPSTATE_READY 5
|
||||
|
||||
/* SIM - PIN states */
|
||||
#define RIL_PINSTATE_UNKNOWN 0
|
||||
#define RIL_PINSTATE_ENABLED_NOT_VERIFIED 1
|
||||
#define RIL_PINSTATE_ENABLED_VERIFIED 2
|
||||
#define RIL_PINSTATE_DISABLED 3
|
||||
#define RIL_PINSTATE_ENABLED_BLOCKED 4
|
||||
#define RIL_PINSTATE_ENABLED_PERM_BLOCKED 5
|
||||
|
||||
/* SIM - App types */
|
||||
#define RIL_APPTYPE_UNKNOWN 0
|
||||
#define RIL_APPTYPE_SIM 1
|
||||
#define RIL_APPTYPE_USIM 2
|
||||
#define RIL_APPTYPE_RUIM 3
|
||||
#define RIL_APPTYPE_CSIM 4
|
||||
#define RIL_APPTYPE_ISIM 5
|
||||
|
||||
/* RIL Request Messages */
|
||||
#define RIL_REQUEST_GET_SIM_STATUS 1
|
||||
#define RIL_REQUEST_ENTER_SIM_PIN 2
|
||||
#define RIL_REQUEST_ENTER_SIM_PUK 3
|
||||
#define RIL_REQUEST_ENTER_SIM_PIN2 4
|
||||
#define RIL_REQUEST_ENTER_SIM_PUK2 5
|
||||
#define RIL_REQUEST_CHANGE_SIM_PIN 6
|
||||
#define RIL_REQUEST_CHANGE_SIM_PIN2 7
|
||||
#define RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION 8
|
||||
#define RIL_REQUEST_GET_CURRENT_CALLS 9
|
||||
#define RIL_REQUEST_DIAL 10
|
||||
#define RIL_REQUEST_GET_IMSI 11
|
||||
#define RIL_REQUEST_HANGUP 12
|
||||
#define RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND 13
|
||||
#define RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND 14
|
||||
#define RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE 15
|
||||
#define RIL_REQUEST_CONFERENCE 16
|
||||
#define RIL_REQUEST_UDUB 17
|
||||
#define RIL_REQUEST_LAST_CALL_FAIL_CAUSE 18
|
||||
#define RIL_REQUEST_SIGNAL_STRENGTH 19
|
||||
#define RIL_REQUEST_VOICE_REGISTRATION_STATE 20
|
||||
#define RIL_REQUEST_DATA_REGISTRATION_STATE 21
|
||||
#define RIL_REQUEST_OPERATOR 22
|
||||
#define RIL_REQUEST_RADIO_POWER 23
|
||||
#define RIL_REQUEST_DTMF 24
|
||||
#define RIL_REQUEST_SEND_SMS 25
|
||||
#define RIL_REQUEST_SEND_SMS_EXPECT_MORE 26
|
||||
#define RIL_REQUEST_SETUP_DATA_CALL 27
|
||||
#define RIL_REQUEST_SIM_IO 28
|
||||
#define RIL_REQUEST_SEND_USSD 29
|
||||
#define RIL_REQUEST_CANCEL_USSD 30
|
||||
#define RIL_REQUEST_GET_CLIR 31
|
||||
#define RIL_REQUEST_SET_CLIR 32
|
||||
#define RIL_REQUEST_QUERY_CALL_FORWARD_STATUS 33
|
||||
#define RIL_REQUEST_SET_CALL_FORWARD 34
|
||||
#define RIL_REQUEST_QUERY_CALL_WAITING 35
|
||||
#define RIL_REQUEST_SET_CALL_WAITING 36
|
||||
#define RIL_REQUEST_SMS_ACKNOWLEDGE 37
|
||||
#define RIL_REQUEST_GET_IMEI 38
|
||||
#define RIL_REQUEST_GET_IMEISV 39
|
||||
#define RIL_REQUEST_ANSWER 40
|
||||
#define RIL_REQUEST_DEACTIVATE_DATA_CALL 41
|
||||
#define RIL_REQUEST_QUERY_FACILITY_LOCK 42
|
||||
#define RIL_REQUEST_SET_FACILITY_LOCK 43
|
||||
#define RIL_REQUEST_CHANGE_BARRING_PASSWORD 44
|
||||
#define RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE 45
|
||||
#define RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC 46
|
||||
#define RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL 47
|
||||
#define RIL_REQUEST_QUERY_AVAILABLE_NETWORKS 48
|
||||
#define RIL_REQUEST_DTMF_START 49
|
||||
#define RIL_REQUEST_DTMF_STOP 50
|
||||
#define RIL_REQUEST_BASEBAND_VERSION 51
|
||||
#define RIL_REQUEST_SEPARATE_CONNECTION 52
|
||||
#define RIL_REQUEST_SET_MUTE 53
|
||||
#define RIL_REQUEST_GET_MUTE 54
|
||||
#define RIL_REQUEST_QUERY_CLIP 55
|
||||
#define RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE 56
|
||||
#define RIL_REQUEST_DATA_CALL_LIST 57
|
||||
#define RIL_REQUEST_RESET_RADIO 58
|
||||
#define RIL_REQUEST_OEM_HOOK_RAW 59
|
||||
#define RIL_REQUEST_OEM_HOOK_STRINGS 60
|
||||
#define RIL_REQUEST_SCREEN_STATE 61
|
||||
#define RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION 62
|
||||
#define RIL_REQUEST_WRITE_SMS_TO_SIM 63
|
||||
#define RIL_REQUEST_DELETE_SMS_ON_SIM 64
|
||||
#define RIL_REQUEST_SET_BAND_MODE 65
|
||||
#define RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE 66
|
||||
#define RIL_REQUEST_STK_GET_PROFILE 67
|
||||
#define RIL_REQUEST_STK_SET_PROFILE 68
|
||||
#define RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND 69
|
||||
#define RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE 70
|
||||
#define RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM 71
|
||||
#define RIL_REQUEST_EXPLICIT_CALL_TRANSFER 72
|
||||
#define RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE 73
|
||||
#define RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE 74
|
||||
#define RIL_REQUEST_GET_NEIGHBORING_CELL_IDS 75
|
||||
#define RIL_REQUEST_SET_LOCATION_UPDATES 76
|
||||
#define RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE 77
|
||||
#define RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE 78
|
||||
#define RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE 79
|
||||
#define RIL_REQUEST_SET_TTY_MODE 80
|
||||
#define RIL_REQUEST_QUERY_TTY_MODE 81
|
||||
#define RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE 82
|
||||
#define RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE 83
|
||||
#define RIL_REQUEST_CDMA_FLASH 84
|
||||
#define RIL_REQUEST_CDMA_BURST_DTMF 85
|
||||
#define RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY 86
|
||||
#define RIL_REQUEST_CDMA_SEND_SMS 87
|
||||
#define RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE 88
|
||||
#define RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG 89
|
||||
#define RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG 90
|
||||
#define RIL_REQUEST_GSM_SMS_BROADCAST_ACTIVATION 91
|
||||
#define RIL_REQUEST_CDMA_GET_BROADCAST_SMS_CONFIG 92
|
||||
#define RIL_REQUEST_CDMA_SET_BROADCAST_SMS_CONFIG 93
|
||||
#define RIL_REQUEST_CDMA_SMS_BROADCAST_ACTIVATION 94
|
||||
#define RIL_REQUEST_CDMA_SUBSCRIPTION 95
|
||||
#define RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM 96
|
||||
#define RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM 97
|
||||
#define RIL_REQUEST_DEVICE_IDENTITY 98
|
||||
#define RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE 99
|
||||
#define RIL_REQUEST_GET_SMSC_ADDRESS 100
|
||||
#define RIL_REQUEST_SET_SMSC_ADDRESS 101
|
||||
#define RIL_REQUEST_REPORT_SMS_MEMORY_STATUS 102
|
||||
#define RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING 103
|
||||
#define RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE 104
|
||||
#define RIL_REQUEST_ISIM_AUTHENTICATION 105
|
||||
#define RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU 106
|
||||
#define RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS 107
|
||||
#define RIL_REQUEST_VOICE_RADIO_TECH 108
|
||||
#define RIL_REQUEST_GET_CELL_INFO_LIST 109
|
||||
#define RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE 110
|
||||
#define RIL_REQUEST_SET_INITIAL_ATTACH_APN 111
|
||||
#define RIL_REQUEST_IMS_REGISTRATION_STATE 112
|
||||
#define RIL_REQUEST_IMS_SEND_SMS 113
|
||||
#define RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC 114
|
||||
#define RIL_REQUEST_SIM_OPEN_CHANNEL 115
|
||||
#define RIL_REQUEST_SIM_CLOSE_CHANNEL 116
|
||||
#define RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL 117
|
||||
#define RIL_REQUEST_NV_READ_ITEM 118
|
||||
#define RIL_REQUEST_NV_WRITE_ITEM 119
|
||||
#define RIL_REQUEST_NV_WRITE_CDMA_PRL 120
|
||||
#define RIL_REQUEST_NV_RESET_CONFIG 121
|
||||
#define RIL_REQUEST_SET_UICC_SUBSCRIPTION 122
|
||||
#define RIL_REQUEST_ALLOW_DATA 123
|
||||
#define RIL_REQUEST_GET_HARDWARE_CONFIG 124
|
||||
#define RIL_REQUEST_SIM_AUTHENTICATION 125
|
||||
#define RIL_REQUEST_GET_DC_RT_INFO 126
|
||||
#define RIL_REQUEST_SET_DC_RT_INFO_RATE 127
|
||||
#define RIL_REQUEST_SET_DATA_PROFILE 128
|
||||
#define RIL_REQUEST_SHUTDOWN 129
|
||||
#define RIL_REQUEST_GET_RADIO_CAPABILITY 130
|
||||
#define RIL_REQUEST_SET_RADIO_CAPABILITY 131
|
||||
|
||||
/* RIL Unsolicited Messages */
|
||||
#define RIL_UNSOL_RESPONSE_BASE 1000
|
||||
#define RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED 1000
|
||||
#define RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED 1001
|
||||
#define RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED 1002
|
||||
#define RIL_UNSOL_RESPONSE_NEW_SMS 1003
|
||||
#define RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT 1004
|
||||
#define RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM 1005
|
||||
#define RIL_UNSOL_ON_USSD 1006
|
||||
#define RIL_UNSOL_ON_USSD_REQUEST 1007
|
||||
#define RIL_UNSOL_NITZ_TIME_RECEIVED 1008
|
||||
#define RIL_UNSOL_SIGNAL_STRENGTH 1009
|
||||
#define RIL_UNSOL_DATA_CALL_LIST_CHANGED 1010
|
||||
#define RIL_UNSOL_SUPP_SVC_NOTIFICATION 1011
|
||||
#define RIL_UNSOL_STK_SESSION_END 1012
|
||||
#define RIL_UNSOL_STK_PROACTIVE_COMMAND 1013
|
||||
#define RIL_UNSOL_STK_EVENT_NOTIFY 1014
|
||||
#define RIL_UNSOL_STK_CALL_SETUP 1015
|
||||
#define RIL_UNSOL_SIM_SMS_STORAGE_FULL 1016
|
||||
#define RIL_UNSOL_SIM_REFRESH 1017
|
||||
#define RIL_UNSOL_CALL_RING 1018
|
||||
#define RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED 1019
|
||||
#define RIL_UNSOL_RESPONSE_CDMA_NEW_SMS 1020
|
||||
#define RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS 1021
|
||||
#define RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL 1022
|
||||
#define RIL_UNSOL_RESTRICTED_STATE_CHANGED 1023
|
||||
#define RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE 1024
|
||||
#define RIL_UNSOL_CDMA_CALL_WAITING 1025
|
||||
#define RIL_UNSOL_CDMA_OTA_PROVISION_STATUS 1026
|
||||
#define RIL_UNSOL_CDMA_INFO_REC 1027
|
||||
#define RIL_UNSOL_OEM_HOOK_RAW 1028
|
||||
#define RIL_UNSOL_RINGBACK_TONE 1029
|
||||
#define RIL_UNSOL_RESEND_INCALL_MUTE 1030
|
||||
#define RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED 1031
|
||||
#define RIL_UNSOL_CDMA_PRL_CHANGED 1032
|
||||
#define RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE 1033
|
||||
#define RIL_UNSOL_RIL_CONNECTED 1034
|
||||
#define RIL_UNSOL_VOICE_RADIO_TECH_CHANGED 1035
|
||||
#define RIL_UNSOL_CELL_INFO_LIST 1036
|
||||
#define RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED 1037
|
||||
#define RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED 1038
|
||||
#define RIL_UNSOL_SRVCC_STATE_NOTIFY 1039
|
||||
#define RIL_UNSOL_HARDWARE_CONFIG_CHANGED 1040
|
||||
#define RIL_UNSOL_DC_RT_INFO_CHANGED 1041
|
||||
#define RIL_UNSOL_RADIO_CAPABILITY 1042
|
||||
#define RIL_UNSOL_ON_SS 1043
|
||||
#define RIL_UNSOL_STK_CC_ALPHA_NOTIFY 1044
|
||||
|
||||
/* Suplementary services Service class*/
|
||||
#define SERVICE_CLASS_NONE 0
|
||||
|
||||
/* RIL_FACILITY_LOCK parameters */
|
||||
#define RIL_FACILITY_UNLOCK "0"
|
||||
#define RIL_FACILITY_LOCK "1"
|
||||
|
||||
#endif /*__RIL_CONSTANTS_H */
|
||||
173
ofono/drivers/ril/ril_devinfo.c
Normal file
173
ofono/drivers/ril/ril_devinfo.c
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_constants.h"
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
|
||||
/*
|
||||
* TODO: No public RIL api to query manufacturer or model.
|
||||
* Check where to get, could /system/build.prop be updated to have good values?
|
||||
*/
|
||||
|
||||
struct ril_devinfo {
|
||||
struct ofono_devinfo *info;
|
||||
GRilIoQueue *q;
|
||||
guint timer_id;
|
||||
};
|
||||
|
||||
struct ril_devinfo_req {
|
||||
ofono_devinfo_query_cb_t cb;
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
#define ril_devinfo_req_free g_free
|
||||
|
||||
static inline struct ril_devinfo *ril_devinfo_get_data(
|
||||
struct ofono_devinfo *info)
|
||||
{
|
||||
return ofono_devinfo_get_data(info);
|
||||
}
|
||||
|
||||
struct ril_devinfo_req *ril_devinfo_req_new(ofono_devinfo_query_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
struct ril_devinfo_req *cbd = g_new0(struct ril_devinfo_req, 1);
|
||||
|
||||
cbd->cb = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static void ril_devinfo_query_unsupported(struct ofono_devinfo *info,
|
||||
ofono_devinfo_query_cb_t cb, void *data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
cb(ril_error_failure(&error), "", data);
|
||||
}
|
||||
|
||||
static void ril_devinfo_query_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_devinfo_req *cbd = user_data;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
gchar *res;
|
||||
GRilIoParser rilp;
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
res = grilio_parser_get_utf8(&rilp);
|
||||
DBG("%s", res);
|
||||
cbd->cb(ril_error_ok(&error), res ? res : "", cbd->data);
|
||||
g_free(res);
|
||||
} else {
|
||||
cbd->cb(ril_error_failure(&error), NULL, cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_devinfo_query(struct ofono_devinfo *info, guint cmd,
|
||||
ofono_devinfo_query_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_devinfo *di = ril_devinfo_get_data(info);
|
||||
|
||||
/* See comment in ril_devinfo_remove */
|
||||
if (di->q) {
|
||||
grilio_queue_send_request_full(di->q, NULL, cmd,
|
||||
ril_devinfo_query_cb, ril_devinfo_req_free,
|
||||
ril_devinfo_req_new(cb, data));
|
||||
} else {
|
||||
struct ofono_error error;
|
||||
cb(ril_error_failure(&error), NULL, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_devinfo_query_revision(struct ofono_devinfo *info,
|
||||
ofono_devinfo_query_cb_t cb, void *data)
|
||||
{
|
||||
DBG("");
|
||||
ril_devinfo_query(info, RIL_REQUEST_BASEBAND_VERSION, cb, data);
|
||||
}
|
||||
|
||||
static void ril_devinfo_query_serial(struct ofono_devinfo *info,
|
||||
ofono_devinfo_query_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
/* TODO: make it support both RIL_REQUEST_GET_IMEI (deprecated) and
|
||||
* RIL_REQUEST_DEVICE_IDENTITY depending on the rild version used */
|
||||
DBG("");
|
||||
ril_devinfo_query(info, RIL_REQUEST_GET_IMEI, cb, data);
|
||||
}
|
||||
|
||||
static gboolean ril_devinfo_register(gpointer user_data)
|
||||
{
|
||||
struct ril_devinfo *di = user_data;
|
||||
|
||||
DBG("");
|
||||
di->timer_id = 0;
|
||||
ofono_devinfo_register(di->info);
|
||||
|
||||
/* This makes the timeout a single-shot */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int ril_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor,
|
||||
void *data)
|
||||
{
|
||||
struct ril_modem *modem = data;
|
||||
struct ril_devinfo *di = g_new0(struct ril_devinfo, 1);
|
||||
|
||||
DBG("");
|
||||
di->q = grilio_queue_new(ril_modem_io(modem));
|
||||
di->info = info;
|
||||
|
||||
di->timer_id = g_idle_add(ril_devinfo_register, di);
|
||||
ofono_devinfo_set_data(info, di);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ril_devinfo_remove(struct ofono_devinfo *info)
|
||||
{
|
||||
struct ril_devinfo *di = ril_devinfo_get_data(info);
|
||||
|
||||
DBG("");
|
||||
ofono_devinfo_set_data(info, NULL);
|
||||
|
||||
if (di->timer_id > 0) {
|
||||
g_source_remove(di->timer_id);
|
||||
}
|
||||
|
||||
grilio_queue_cancel_all(di->q, FALSE);
|
||||
grilio_queue_unref(di->q);
|
||||
g_free(di);
|
||||
}
|
||||
|
||||
const struct ofono_devinfo_driver ril_devinfo_driver = {
|
||||
.name = RILMODEM_DRIVER,
|
||||
.probe = ril_devinfo_probe,
|
||||
.remove = ril_devinfo_remove,
|
||||
.query_manufacturer = ril_devinfo_query_unsupported,
|
||||
.query_model = ril_devinfo_query_unsupported,
|
||||
.query_revision = ril_devinfo_query_revision,
|
||||
.query_serial = ril_devinfo_query_serial
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
389
ofono/drivers/ril/ril_gprs.c
Normal file
389
ofono/drivers/ril/ril_gprs.c
Normal file
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_constants.h"
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 2. ofono_gprs_bearer_notify() is never called as RILD does not
|
||||
* expose an unsolicited event equivalent to +CPSB ( see 27.007
|
||||
* 7.29 ), and the tech values returned by REQUEST_DATA/VOICE
|
||||
* _REGISTRATION requests do not match the values defined for
|
||||
* <AcT> in the +CPSB definition. Note, the values returned by
|
||||
* the *REGISTRATION commands are aligned with those defined by
|
||||
* +CREG ( see 27.003 7.2 ).
|
||||
*/
|
||||
|
||||
struct ril_gprs {
|
||||
struct ofono_gprs *gprs;
|
||||
struct ril_modem *md;
|
||||
GRilIoChannel *io;
|
||||
GRilIoQueue *q;
|
||||
gboolean ofono_attached;
|
||||
gboolean ofono_registered;
|
||||
int max_cids;
|
||||
int last_status;
|
||||
int ril_data_tech;
|
||||
gulong event_id;
|
||||
guint poll_id;
|
||||
guint timer_id;
|
||||
};
|
||||
|
||||
struct ril_gprs_cbd {
|
||||
struct ril_gprs *gd;
|
||||
union _ofono_gprs_cb {
|
||||
ofono_gprs_status_cb_t status;
|
||||
ofono_gprs_cb_t cb;
|
||||
gpointer ptr;
|
||||
} cb;
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
#define ril_gprs_cbd_free g_free
|
||||
|
||||
static void ril_gprs_poll_data_reg_state_cb(GRilIoChannel *io, int ril_status,
|
||||
const void *data, guint len, void *user_data);
|
||||
|
||||
static inline struct ril_gprs *ril_gprs_get_data(struct ofono_gprs *b)
|
||||
{
|
||||
return ofono_gprs_get_data(b);
|
||||
}
|
||||
|
||||
static struct ril_gprs_cbd *ril_gprs_cbd_new(struct ril_gprs *gd, void *cb,
|
||||
void *data)
|
||||
{
|
||||
struct ril_gprs_cbd *cbd = g_new0(struct ril_gprs_cbd, 1);
|
||||
|
||||
cbd->gd = gd;
|
||||
cbd->cb.ptr = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
int ril_gprs_ril_data_tech(struct ofono_gprs *gprs)
|
||||
{
|
||||
struct ril_gprs *gd = ril_gprs_get_data(gprs);
|
||||
return gd ? gd->ril_data_tech : -1;
|
||||
}
|
||||
|
||||
static void ril_gprs_poll_data_reg_state(struct ril_gprs *gd)
|
||||
{
|
||||
if (!gd->poll_id) {
|
||||
DBG("");
|
||||
gd->poll_id = grilio_queue_send_request_full(gd->q, NULL,
|
||||
RIL_REQUEST_DATA_REGISTRATION_STATE,
|
||||
ril_gprs_poll_data_reg_state_cb, NULL, gd);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_gprs_state_changed(GRilIoChannel *io, guint code,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_gprs *gd = user_data;
|
||||
|
||||
DBG("%s", ril_modem_get_path(gd->md));
|
||||
ril_gprs_poll_data_reg_state(gd);
|
||||
}
|
||||
|
||||
static gboolean ril_gprs_set_attached_callback(gpointer user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_gprs_cbd *cbd = user_data;
|
||||
|
||||
DBG("%s", ril_modem_get_path(cbd->gd->md));
|
||||
cbd->gd->timer_id = 0;
|
||||
cbd->cb.cb(ril_error_ok(&error), cbd->data);
|
||||
ril_gprs_cbd_free(cbd);
|
||||
|
||||
/* Single shot */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void ril_gprs_set_attached(struct ofono_gprs *gprs, int attached,
|
||||
ofono_gprs_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_gprs *gd = ril_gprs_get_data(gprs);
|
||||
|
||||
DBG("%s attached: %d", ril_modem_get_path(gd->md), 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
|
||||
* notification is received and any configured roaming conditions
|
||||
* are met.
|
||||
*/
|
||||
|
||||
gd->ofono_attached = attached;
|
||||
|
||||
/*
|
||||
* However we cannot respond immediately, since core sets the
|
||||
* value of driver_attached after calling set_attached and that
|
||||
* leads to comparison failure in gprs_attached_update in
|
||||
* connection drop phase
|
||||
*/
|
||||
gd->timer_id = g_idle_add(ril_gprs_set_attached_callback,
|
||||
ril_gprs_cbd_new(gd, cb, data));
|
||||
}
|
||||
|
||||
static int ril_gprs_parse_data_reg_state(struct ril_gprs *gd,
|
||||
const void *data, guint len)
|
||||
{
|
||||
struct ofono_gprs *gprs = gd->gprs;
|
||||
struct ril_reg_data reg;
|
||||
|
||||
if (!ril_util_parse_reg(data, len, ®)) {
|
||||
ofono_error("Failure parsing data registration response.");
|
||||
gd->ril_data_tech = -1;
|
||||
return NETWORK_REGISTRATION_STATUS_UNKNOWN;
|
||||
} else {
|
||||
const int rawstatus = reg.status;
|
||||
|
||||
if (gd->ril_data_tech != reg.ril_tech) {
|
||||
gd->ril_data_tech = reg.ril_tech;
|
||||
DBG("ril data tech %d", reg.ril_tech);
|
||||
}
|
||||
|
||||
if (!gd->ofono_registered) {
|
||||
ofono_gprs_register(gprs);
|
||||
gd->ofono_registered = TRUE;
|
||||
}
|
||||
|
||||
if (reg.max_calls > gd->max_cids) {
|
||||
DBG("Setting max cids to %d", reg.max_calls);
|
||||
gd->max_cids = reg.max_calls;
|
||||
ofono_gprs_set_cid_range(gprs, 1, reg.max_calls);
|
||||
}
|
||||
|
||||
if (reg.status == NETWORK_REGISTRATION_STATUS_ROAMING) {
|
||||
reg.status = ril_netreg_check_if_really_roaming(
|
||||
ril_modem_ofono_netreg(gd->md), reg.status);
|
||||
}
|
||||
|
||||
if (rawstatus != reg.status) {
|
||||
ofono_info("data registration modified %d => %d",
|
||||
rawstatus, reg.status);
|
||||
}
|
||||
|
||||
return reg.status;
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_gprs_registration_status_cb(GRilIoChannel *io, int ril_status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_gprs_cbd *cbd = user_data;
|
||||
ofono_gprs_status_cb_t cb = cbd->cb.status;
|
||||
struct ril_gprs *gd = cbd->gd;
|
||||
struct ofono_gprs *gprs = gd->gprs;
|
||||
struct ofono_error error;
|
||||
int status = -1;
|
||||
|
||||
DBG("%s", ril_modem_get_path(gd->md));
|
||||
if (gd && ril_status == RIL_E_SUCCESS) {
|
||||
ril_error_init_ok(&error);
|
||||
} else {
|
||||
ofono_error("ril_gprs_data_reg_cb: reply failure: %s",
|
||||
ril_error_to_string(ril_status));
|
||||
ril_error_init_failure(&error);
|
||||
goto cb_out;
|
||||
}
|
||||
|
||||
status = ril_gprs_parse_data_reg_state(gd, data, len);
|
||||
if (status == NETWORK_REGISTRATION_STATUS_UNKNOWN) {
|
||||
ril_error_init_failure(&error);
|
||||
goto cb_out;
|
||||
}
|
||||
|
||||
/* Let's minimize logging */
|
||||
if (status != gd->last_status) {
|
||||
ofono_info("data reg changes %d (%d), attached %d",
|
||||
status, gd->last_status, gd->ofono_attached);
|
||||
}
|
||||
|
||||
/* Must be attached if registered or roaming */
|
||||
if (gd->last_status != NETWORK_REGISTRATION_STATUS_REGISTERED &&
|
||||
gd->last_status != NETWORK_REGISTRATION_STATUS_ROAMING) {
|
||||
if (status == NETWORK_REGISTRATION_STATUS_REGISTERED) {
|
||||
gd->ofono_attached = TRUE;
|
||||
} else if ((status == NETWORK_REGISTRATION_STATUS_ROAMING) &&
|
||||
ofono_gprs_get_roaming_allowed(gd->gprs)) {
|
||||
gd->ofono_attached = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ofono_modem_get_online(ofono_gprs_get_modem(gprs)))
|
||||
gd->ofono_attached = FALSE;
|
||||
|
||||
/* if unsolicitated and no state change let's not notify core */
|
||||
if ((status == gd->last_status) && gd->ofono_attached) {
|
||||
goto cb_out;
|
||||
}
|
||||
|
||||
if (!gd->ofono_attached) {
|
||||
if (!cb) {
|
||||
if (status == NETWORK_REGISTRATION_STATUS_ROAMING) {
|
||||
if (!ofono_gprs_get_roaming_allowed(gd->gprs)) {
|
||||
ofono_gprs_detached_notify(gprs);
|
||||
}
|
||||
|
||||
/*
|
||||
* This prevents core ending
|
||||
* into eternal loop with driver
|
||||
*/
|
||||
ril_error_init_failure(&error);
|
||||
}
|
||||
|
||||
ofono_gprs_status_notify(gprs, status);
|
||||
|
||||
} else {
|
||||
/*
|
||||
* This prevents core ending
|
||||
* into eternal loop with driver
|
||||
*/
|
||||
ril_error_init_failure(&error);
|
||||
}
|
||||
|
||||
gd->last_status = status;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!cb) {
|
||||
ofono_gprs_status_notify(gprs, status);
|
||||
}
|
||||
|
||||
gd->last_status = status;
|
||||
|
||||
exit:
|
||||
DBG("data reg status %d, last status %d, attached %d",
|
||||
status, gd->last_status, gd->ofono_attached);
|
||||
cb_out:
|
||||
if (cb) {
|
||||
cb(&error, status, cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_gprs_poll_data_reg_state_cb(GRilIoChannel *io, int ril_status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_gprs *gd = user_data;
|
||||
int status;
|
||||
|
||||
DBG("%s", ril_modem_get_path(gd->md));
|
||||
GASSERT(gd->poll_id);
|
||||
gd->poll_id = 0;
|
||||
|
||||
if (ril_status != RIL_E_SUCCESS) {
|
||||
ofono_error("ril_gprs_data_probe_reg_cb: reply failure: %s",
|
||||
ril_error_to_string(ril_status));
|
||||
status = NETWORK_REGISTRATION_STATUS_UNKNOWN;
|
||||
} else {
|
||||
status = ril_gprs_parse_data_reg_state(gd, data, len);
|
||||
ofono_info("data reg status probed %d", status);
|
||||
}
|
||||
|
||||
if (status != gd->last_status) {
|
||||
ofono_info("data reg changes %d (%d), attached %d",
|
||||
status, gd->last_status, gd->ofono_attached);
|
||||
gd->last_status = status;
|
||||
if (gd->ofono_attached) {
|
||||
ofono_gprs_status_notify(gd->gprs, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_gprs_registration_status(struct ofono_gprs *gprs,
|
||||
ofono_gprs_status_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_gprs *gd = ril_gprs_get_data(gprs);
|
||||
|
||||
DBG("");
|
||||
if (gd) {
|
||||
grilio_queue_send_request_full(gd->q, NULL,
|
||||
RIL_REQUEST_DATA_REGISTRATION_STATE,
|
||||
ril_gprs_registration_status_cb, ril_gprs_cbd_free,
|
||||
ril_gprs_cbd_new(gd, cb, data));
|
||||
}
|
||||
}
|
||||
|
||||
static int ril_gprs_probe(struct ofono_gprs *gprs, unsigned int vendor,
|
||||
void *data)
|
||||
{
|
||||
struct ril_modem *modem = data;
|
||||
struct ril_gprs *gd = g_new0(struct ril_gprs, 1);
|
||||
|
||||
DBG("%s", ril_modem_get_path(modem));
|
||||
gd->md = modem;
|
||||
gd->io = grilio_channel_ref(ril_modem_io(modem));
|
||||
gd->q = grilio_queue_new(gd->io);
|
||||
gd->last_status = -1;
|
||||
gd->ril_data_tech = -1;
|
||||
gd->gprs = gprs;
|
||||
|
||||
ofono_gprs_set_data(gprs, gd);
|
||||
ril_gprs_poll_data_reg_state(gd);
|
||||
gd->event_id = grilio_channel_add_unsol_event_handler(gd->io,
|
||||
ril_gprs_state_changed,
|
||||
RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED, gd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ril_gprs_remove(struct ofono_gprs *gprs)
|
||||
{
|
||||
struct ril_gprs *gd = ril_gprs_get_data(gprs);
|
||||
|
||||
DBG("%s", ril_modem_get_path(gd->md));
|
||||
ofono_gprs_set_data(gprs, NULL);
|
||||
|
||||
if (gd->timer_id > 0) {
|
||||
g_source_remove(gd->timer_id);
|
||||
}
|
||||
|
||||
grilio_channel_remove_handler(gd->io, gd->event_id);
|
||||
grilio_channel_unref(gd->io);
|
||||
grilio_queue_cancel_all(gd->q, FALSE);
|
||||
grilio_queue_unref(gd->q);
|
||||
g_free(gd);
|
||||
}
|
||||
|
||||
const struct ofono_gprs_driver ril_gprs_driver = {
|
||||
.name = RILMODEM_DRIVER,
|
||||
.probe = ril_gprs_probe,
|
||||
.remove = ril_gprs_remove,
|
||||
.set_attached = ril_gprs_set_attached,
|
||||
.attached_status = ril_gprs_registration_status,
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
900
ofono/drivers/ril/ril_gprs_context.c
Normal file
900
ofono/drivers/ril/ril_gprs_context.c
Normal file
@@ -0,0 +1,900 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_constants.h"
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
|
||||
#include <gutil_strv.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define PROTO_IP_STR "IP"
|
||||
#define PROTO_IPV6_STR "IPV6"
|
||||
#define PROTO_IPV4V6_STR "IPV4V6"
|
||||
|
||||
#define MIN_DATA_CALL_LIST_SIZE 8
|
||||
#define MIN_DATA_CALL_REPLY_SIZE 36
|
||||
|
||||
#define SETUP_DATA_CALL_PARAMS 7
|
||||
#define DATA_PROFILE_DEFAULT_STR "0"
|
||||
#define DEACTIVATE_DATA_CALL_PARAMS 2
|
||||
|
||||
#define CTX_ID_NONE ((unsigned int)(-1))
|
||||
|
||||
enum data_call_state {
|
||||
DATA_CALL_INACTIVE,
|
||||
DATA_CALL_LINK_DOWN,
|
||||
DATA_CALL_ACTIVE,
|
||||
};
|
||||
|
||||
enum ril_gprs_context_state {
|
||||
STATE_IDLE,
|
||||
STATE_ACTIVATING,
|
||||
STATE_DEACTIVATING,
|
||||
STATE_ACTIVE,
|
||||
};
|
||||
|
||||
struct ril_gprs_context {
|
||||
struct ofono_gprs_context *gc;
|
||||
struct ril_modem *modem;
|
||||
GRilIoChannel *io;
|
||||
GRilIoQueue *q;
|
||||
guint active_ctx_cid;
|
||||
enum ril_gprs_context_state state;
|
||||
gulong regid;
|
||||
struct ril_gprs_context_data_call *active_call;
|
||||
};
|
||||
|
||||
struct ril_gprs_context_data_call {
|
||||
guint status;
|
||||
gint cid;
|
||||
guint active;
|
||||
int retry_time;
|
||||
int prot;
|
||||
gint mtu;
|
||||
gchar *ifname;
|
||||
gchar **dnses;
|
||||
gchar **gateways;
|
||||
gchar **addresses;
|
||||
};
|
||||
|
||||
struct ril_gprs_context_data_call_list {
|
||||
guint version;
|
||||
guint num;
|
||||
GSList *calls;
|
||||
};
|
||||
|
||||
struct ril_gprs_context_cbd {
|
||||
struct ril_gprs_context *gcd;
|
||||
ofono_gprs_context_cb_t cb;
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
#define ril_gprs_context_cbd_free g_free
|
||||
|
||||
static inline struct ril_gprs_context *ril_gprs_context_get_data(
|
||||
struct ofono_gprs_context *gprs)
|
||||
{
|
||||
return ofono_gprs_context_get_data(gprs);
|
||||
}
|
||||
|
||||
static struct ril_gprs_context_cbd *ril_gprs_context_cbd_new(
|
||||
struct ril_gprs_context *gcd, ofono_gprs_context_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_gprs_context_cbd *cbd =
|
||||
g_new0(struct ril_gprs_context_cbd, 1);
|
||||
|
||||
cbd->gcd = gcd;
|
||||
cbd->cb = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static char *ril_gprs_context_netmask(const char *address)
|
||||
{
|
||||
if (address) {
|
||||
const char *suffix = strchr(address, '/');
|
||||
if (suffix) {
|
||||
int nbits = atoi(suffix + 1);
|
||||
if (nbits > 0 && nbits < 33) {
|
||||
const char* str;
|
||||
struct in_addr in;
|
||||
in.s_addr = htonl((nbits == 32) ? 0xffffffff :
|
||||
((1 << nbits)-1) << (32-nbits));
|
||||
str = inet_ntoa(in);
|
||||
if (str) {
|
||||
return g_strdup(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return g_strdup("255.255.255.0");
|
||||
}
|
||||
|
||||
static const char *ril_gprs_ofono_protocol_to_ril(guint protocol)
|
||||
{
|
||||
switch (protocol) {
|
||||
case OFONO_GPRS_PROTO_IPV6:
|
||||
return PROTO_IPV6_STR;
|
||||
case OFONO_GPRS_PROTO_IPV4V6:
|
||||
return PROTO_IPV4V6_STR;
|
||||
case OFONO_GPRS_PROTO_IP:
|
||||
return PROTO_IP_STR;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ril_gprs_protocol_to_ofono(gchar *protocol_str)
|
||||
{
|
||||
if (protocol_str) {
|
||||
if (!strcmp(protocol_str, PROTO_IPV6_STR)) {
|
||||
return OFONO_GPRS_PROTO_IPV6;
|
||||
} else if (!strcmp(protocol_str, PROTO_IPV4V6_STR)) {
|
||||
return OFONO_GPRS_PROTO_IPV4V6;
|
||||
} else if (!strcmp(protocol_str, PROTO_IP_STR)) {
|
||||
return OFONO_GPRS_PROTO_IP;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ril_gprs_context_data_call_free(
|
||||
struct ril_gprs_context_data_call *call)
|
||||
{
|
||||
if (call) {
|
||||
g_free(call->ifname);
|
||||
g_strfreev(call->dnses);
|
||||
g_strfreev(call->addresses);
|
||||
g_strfreev(call->gateways);
|
||||
g_free(call);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_gprs_context_set_disconnected(struct ril_gprs_context *gcd)
|
||||
{
|
||||
gcd->state = STATE_IDLE;
|
||||
if (gcd->active_call) {
|
||||
ril_gprs_context_data_call_free(gcd->active_call);
|
||||
gcd->active_call = NULL;
|
||||
}
|
||||
if (gcd->active_ctx_cid != CTX_ID_NONE) {
|
||||
guint id = gcd->active_ctx_cid;
|
||||
gcd->active_ctx_cid = CTX_ID_NONE;
|
||||
ofono_gprs_context_deactivated(gcd->gc, id);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_gprs_split_ip_by_protocol(char **ip_array,
|
||||
char ***split_ip_addr,
|
||||
char ***split_ipv6_addr)
|
||||
{
|
||||
const int n = gutil_strv_length(ip_array);
|
||||
int i;
|
||||
|
||||
*split_ipv6_addr = *split_ip_addr = NULL;
|
||||
for (i = 0; i < n && (!*split_ipv6_addr || !*split_ip_addr); i++) {
|
||||
const char *addr = ip_array[i];
|
||||
switch (ril_address_family(addr)) {
|
||||
case AF_INET:
|
||||
if (!*split_ip_addr) {
|
||||
char *mask = ril_gprs_context_netmask(addr);
|
||||
*split_ip_addr = g_strsplit(addr, "/", 2);
|
||||
if (gutil_strv_length(*split_ip_addr) == 2) {
|
||||
g_free((*split_ip_addr)[1]);
|
||||
(*split_ip_addr)[1] = mask;
|
||||
} else {
|
||||
/* This is rather unlikely to happen */
|
||||
*split_ip_addr =
|
||||
gutil_strv_add(*split_ip_addr,
|
||||
mask);
|
||||
g_free(mask);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (!*split_ipv6_addr) {
|
||||
*split_ipv6_addr = g_strsplit(addr, "/", 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_gprs_split_gw_by_protocol(char **gw_array, char **ip_gw,
|
||||
char **ipv6_gw)
|
||||
{
|
||||
const int n = gutil_strv_length(gw_array);
|
||||
int i;
|
||||
|
||||
*ip_gw = *ipv6_gw = NULL;
|
||||
for (i = 0; i < n && (!*ipv6_gw || !*ip_gw); i++) {
|
||||
const char *gw_addr = gw_array[i];
|
||||
switch (ril_address_family(gw_addr)) {
|
||||
case AF_INET:
|
||||
if (!*ip_gw) *ip_gw = g_strdup(gw_addr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (!*ipv6_gw) *ipv6_gw = g_strdup(gw_addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_gprs_split_dns_by_protocol(char **dns_array, char ***dns_addr,
|
||||
char ***dns_ipv6_addr)
|
||||
{
|
||||
const int n = gutil_strv_length(dns_array);
|
||||
int i;
|
||||
|
||||
*dns_ipv6_addr = *dns_addr = 0;
|
||||
for (i = 0; i < n; i++) {
|
||||
const char *addr = dns_array[i];
|
||||
switch (ril_address_family(addr)) {
|
||||
case AF_INET:
|
||||
*dns_addr = gutil_strv_add(*dns_addr, addr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
*dns_ipv6_addr = gutil_strv_add(*dns_ipv6_addr, addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gint ril_gprs_context_parse_data_call_compare(gconstpointer a,
|
||||
gconstpointer b)
|
||||
{
|
||||
const struct ril_gprs_context_data_call *ca = a;
|
||||
const struct ril_gprs_context_data_call *cb = b;
|
||||
|
||||
if (ca->cid < cb->cid) {
|
||||
return -1;
|
||||
} else if (ca->cid > cb->cid) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_gprs_context_data_call_free1(gpointer data)
|
||||
{
|
||||
ril_gprs_context_data_call_free(data);
|
||||
}
|
||||
|
||||
static void ril_gprs_context_data_call_list_free(
|
||||
struct ril_gprs_context_data_call_list *list)
|
||||
{
|
||||
if (list) {
|
||||
g_slist_free_full(list->calls, ril_gprs_context_data_call_free1);
|
||||
g_free(list);
|
||||
}
|
||||
}
|
||||
|
||||
static struct ril_gprs_context_data_call *ril_gprs_context_data_call_find(
|
||||
struct ril_gprs_context_data_call_list *list, gint cid)
|
||||
{
|
||||
if (list) {
|
||||
GSList *entry;
|
||||
|
||||
for (entry = list->calls; entry; entry = entry->next) {
|
||||
struct ril_gprs_context_data_call *call = entry->data;
|
||||
|
||||
if (call->cid == cid) {
|
||||
return call;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Only compares the stuff that's important to us */
|
||||
static gboolean ril_gprs_context_data_call_equal(
|
||||
const struct ril_gprs_context_data_call *c1,
|
||||
const struct ril_gprs_context_data_call *c2)
|
||||
{
|
||||
if (!c1 && !c2) {
|
||||
return TRUE;
|
||||
} else if (c1 && c2) {
|
||||
return c1->cid == c2->cid &&
|
||||
c1->active == c2->active && c1->prot == c2->prot &&
|
||||
!g_strcmp0(c1->ifname, c2->ifname) &&
|
||||
gutil_strv_equal(c1->dnses, c2->dnses) &&
|
||||
gutil_strv_equal(c1->gateways, c2->gateways) &&
|
||||
gutil_strv_equal(c1->addresses, c2->addresses);
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static struct ril_gprs_context_data_call *
|
||||
ril_gprs_context_parse_data_call(int version, GRilIoParser *rilp)
|
||||
{
|
||||
char *prot;
|
||||
struct ril_gprs_context_data_call *call =
|
||||
g_new0(struct ril_gprs_context_data_call, 1);
|
||||
|
||||
grilio_parser_get_uint32(rilp, &call->status);
|
||||
grilio_parser_get_int32(rilp, &call->retry_time);
|
||||
grilio_parser_get_int32(rilp, &call->cid);
|
||||
grilio_parser_get_uint32(rilp, &call->active);
|
||||
prot = grilio_parser_get_utf8(rilp);
|
||||
call->ifname = grilio_parser_get_utf8(rilp);
|
||||
call->addresses = grilio_parser_split_utf8(rilp, " ");
|
||||
call->dnses = grilio_parser_split_utf8(rilp, " ");
|
||||
call->gateways = grilio_parser_split_utf8(rilp, " ");
|
||||
|
||||
call->prot = ril_gprs_protocol_to_ofono(prot);
|
||||
if (call->prot < 0) {
|
||||
ofono_error("Invalid type(protocol) specified: %s", prot);
|
||||
}
|
||||
|
||||
g_free(prot);
|
||||
|
||||
if (version >= 9) {
|
||||
/* PCSCF */
|
||||
grilio_parser_skip_string(rilp);
|
||||
if (version >= 11) {
|
||||
/* MTU */
|
||||
grilio_parser_get_int32(rilp, &call->mtu);
|
||||
}
|
||||
}
|
||||
|
||||
return call;
|
||||
}
|
||||
|
||||
static struct ril_gprs_context_data_call_list *
|
||||
ril_gprs_context_parse_data_call_list(const void *data, guint len)
|
||||
{
|
||||
struct ril_gprs_context_data_call_list *reply =
|
||||
g_new0(struct ril_gprs_context_data_call_list, 1);
|
||||
GRilIoParser rilp;
|
||||
unsigned int i, n;
|
||||
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
grilio_parser_get_uint32(&rilp, &reply->version);
|
||||
grilio_parser_get_uint32(&rilp, &n);
|
||||
DBG("version=%d,num=%d", reply->version, n);
|
||||
|
||||
for (i = 0; i < n && !grilio_parser_at_end(&rilp); i++) {
|
||||
struct ril_gprs_context_data_call *call =
|
||||
ril_gprs_context_parse_data_call(reply->version, &rilp);
|
||||
|
||||
DBG("%d [status=%d,retry=%d,cid=%d,"
|
||||
"active=%d,type=%s,ifname=%s,mtu=%d,"
|
||||
"address=%s, dns=%s %s,gateways=%s]",
|
||||
i, call->status, call->retry_time,
|
||||
call->cid, call->active,
|
||||
ril_gprs_ofono_protocol_to_ril(call->prot),
|
||||
call->ifname, call->mtu, call->addresses[0],
|
||||
call->dnses[0],
|
||||
(call->dnses[0] && call->dnses[1]) ?
|
||||
call->dnses[1] : "",
|
||||
call->gateways[0]);
|
||||
|
||||
reply->num++;
|
||||
reply->calls = g_slist_insert_sorted(reply->calls, call,
|
||||
ril_gprs_context_parse_data_call_compare);
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
static void ril_gprs_context_call_list_changed(GRilIoChannel *io, guint event,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_gprs_context *gcd = user_data;
|
||||
struct ofono_gprs_context *gc = gcd->gc;
|
||||
struct ril_gprs_context_data_call *call = NULL;
|
||||
struct ril_gprs_context_data_call *prev_call;
|
||||
struct ril_gprs_context_data_call_list *unsol =
|
||||
ril_gprs_context_parse_data_call_list(data, len);
|
||||
|
||||
if (gcd->active_call) {
|
||||
/* Find our call */
|
||||
call = ril_gprs_context_data_call_find(unsol,
|
||||
gcd->active_call->cid);
|
||||
if (call) {
|
||||
/* Check if the call have been disconnected */
|
||||
if (call->active == DATA_CALL_INACTIVE) {
|
||||
ofono_error("Clearing active context");
|
||||
ril_gprs_context_set_disconnected(gcd);
|
||||
|
||||
/* Compare it agains the last known state */
|
||||
} else if (ril_gprs_context_data_call_equal(call,
|
||||
gcd->active_call)) {
|
||||
DBG("call %u didn't change", call->cid);
|
||||
call = NULL;
|
||||
} else {
|
||||
/* Steal it from the list */
|
||||
DBG("call %u changed", call->cid);
|
||||
unsol->calls = g_slist_remove(unsol->calls,
|
||||
call);
|
||||
}
|
||||
} else {
|
||||
ofono_error("Clearing active context");
|
||||
ril_gprs_context_set_disconnected(gcd);
|
||||
}
|
||||
}
|
||||
|
||||
/* We don't need the rest of the list anymore */
|
||||
ril_gprs_context_data_call_list_free(unsol);
|
||||
|
||||
if (!call) {
|
||||
/* We are not interested */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Store the updated call data */
|
||||
prev_call = gcd->active_call;
|
||||
gcd->active_call = call;
|
||||
|
||||
if (call->status != 0) {
|
||||
ofono_info("data call status: %d", call->status);
|
||||
}
|
||||
|
||||
if (call->active == DATA_CALL_ACTIVE) {
|
||||
gboolean signal = FALSE;
|
||||
|
||||
if (call->ifname && g_strcmp0(call->ifname, prev_call->ifname)) {
|
||||
DBG("interface changed");
|
||||
signal = TRUE;
|
||||
ofono_gprs_context_set_interface(gc, call->ifname);
|
||||
}
|
||||
|
||||
if (!gutil_strv_equal(call->addresses, prev_call->addresses)) {
|
||||
char **split_ip_addr = NULL;
|
||||
char **split_ipv6_addr = NULL;
|
||||
|
||||
DBG("address changed");
|
||||
signal = TRUE;
|
||||
|
||||
/* Pick 1 address of each protocol */
|
||||
ril_gprs_split_ip_by_protocol(call->addresses,
|
||||
&split_ip_addr, &split_ipv6_addr);
|
||||
|
||||
if ((call->prot == OFONO_GPRS_PROTO_IPV4V6 ||
|
||||
call->prot == OFONO_GPRS_PROTO_IPV6) &&
|
||||
split_ipv6_addr) {
|
||||
ofono_gprs_context_set_ipv6_address(gc,
|
||||
split_ipv6_addr[0]);
|
||||
}
|
||||
|
||||
if ((call->prot == OFONO_GPRS_PROTO_IPV4V6 ||
|
||||
call->prot == OFONO_GPRS_PROTO_IP) &&
|
||||
split_ip_addr) {
|
||||
ofono_gprs_context_set_ipv4_netmask(gc,
|
||||
split_ip_addr[1]);
|
||||
ofono_gprs_context_set_ipv4_address(gc,
|
||||
split_ip_addr[0], TRUE);
|
||||
}
|
||||
|
||||
g_strfreev(split_ip_addr);
|
||||
g_strfreev(split_ipv6_addr);
|
||||
}
|
||||
|
||||
if (!gutil_strv_equal(call->gateways, prev_call->gateways)){
|
||||
char *ip_gw = NULL;
|
||||
char *ipv6_gw = NULL;
|
||||
|
||||
DBG("gateway changed");
|
||||
signal = TRUE;
|
||||
|
||||
/* Pick 1 gw for each protocol*/
|
||||
ril_gprs_split_gw_by_protocol(call->gateways,
|
||||
&ip_gw, &ipv6_gw);
|
||||
|
||||
if ((call->prot == OFONO_GPRS_PROTO_IPV4V6 ||
|
||||
call->prot == OFONO_GPRS_PROTO_IPV6) &&
|
||||
ipv6_gw) {
|
||||
ofono_gprs_context_set_ipv6_gateway(gc, ipv6_gw);
|
||||
}
|
||||
|
||||
if ((call->prot == OFONO_GPRS_PROTO_IPV4V6 ||
|
||||
call->prot == OFONO_GPRS_PROTO_IP) &&
|
||||
ip_gw) {
|
||||
ofono_gprs_context_set_ipv4_gateway(gc, ip_gw);
|
||||
}
|
||||
|
||||
g_free(ip_gw);
|
||||
g_free(ipv6_gw);
|
||||
}
|
||||
|
||||
if (!gutil_strv_equal(call->dnses, prev_call->dnses)){
|
||||
char **dns_ip = NULL;
|
||||
char **dns_ipv6 = NULL;
|
||||
|
||||
DBG("name server(s) changed");
|
||||
signal = TRUE;
|
||||
|
||||
/* split based on protocol*/
|
||||
ril_gprs_split_dns_by_protocol(call->dnses,
|
||||
&dns_ip, &dns_ipv6);
|
||||
|
||||
if ((call->prot == OFONO_GPRS_PROTO_IPV4V6 ||
|
||||
call->prot == OFONO_GPRS_PROTO_IPV6) &&
|
||||
dns_ipv6) {
|
||||
ofono_gprs_context_set_ipv6_dns_servers(gc,
|
||||
(const char **) dns_ipv6);
|
||||
}
|
||||
|
||||
if ((call->prot == OFONO_GPRS_PROTO_IPV4V6 ||
|
||||
call->prot == OFONO_GPRS_PROTO_IP) && dns_ip) {
|
||||
ofono_gprs_context_set_ipv4_dns_servers(gc,
|
||||
(const char**)dns_ip);
|
||||
}
|
||||
|
||||
g_strfreev(dns_ip);
|
||||
g_strfreev(dns_ipv6);
|
||||
}
|
||||
|
||||
if (signal) {
|
||||
ofono_gprs_context_signal_change(gc, call->cid);
|
||||
}
|
||||
}
|
||||
|
||||
ril_gprs_context_data_call_free(prev_call);
|
||||
}
|
||||
|
||||
static void ril_gprs_context_activate_primary_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_gprs_context_cbd *cbd = user_data;
|
||||
ofono_gprs_context_cb_t cb = cbd->cb;
|
||||
struct ril_gprs_context *gcd = cbd->gcd;
|
||||
struct ofono_gprs_context *gc = gcd->gc;
|
||||
struct ofono_error error;
|
||||
struct ril_gprs_context_data_call_list *reply = NULL;
|
||||
struct ril_gprs_context_data_call *call;
|
||||
char **split_ip_addr = NULL;
|
||||
char **split_ipv6_addr = NULL;
|
||||
char* ip_gw = NULL;
|
||||
char* ipv6_gw = NULL;
|
||||
char** dns_addr = NULL;
|
||||
char** dns_ipv6_addr = NULL;
|
||||
|
||||
ofono_info("setting up data call");
|
||||
|
||||
ril_error_init_ok(&error);
|
||||
if (status != RIL_E_SUCCESS) {
|
||||
ofono_error("GPRS context: Reply failure: %s",
|
||||
ril_error_to_string(status));
|
||||
error.type = OFONO_ERROR_TYPE_FAILURE;
|
||||
error.error = status;
|
||||
ril_gprs_context_set_disconnected(gcd);
|
||||
goto done;
|
||||
}
|
||||
|
||||
reply = ril_gprs_context_parse_data_call_list(data, len);
|
||||
if (reply->num != 1) {
|
||||
ofono_error("Number of data calls: %u", reply->num);
|
||||
ril_error_init_failure(&error);
|
||||
ril_gprs_context_set_disconnected(gcd);
|
||||
goto done;
|
||||
}
|
||||
|
||||
call = reply->calls->data;
|
||||
|
||||
if (call->status != 0) {
|
||||
ofono_error("Unexpected data call status %d", call->status);
|
||||
error.type = OFONO_ERROR_TYPE_FAILURE;
|
||||
error.error = call->status;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Must have interface */
|
||||
if (!call->ifname) {
|
||||
ofono_error("GPRS context: No interface");
|
||||
error.type = OFONO_ERROR_TYPE_FAILURE;
|
||||
error.error = EINVAL;
|
||||
ril_gprs_context_set_disconnected(gcd);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check the ip address */
|
||||
ril_gprs_split_ip_by_protocol(call->addresses, &split_ip_addr,
|
||||
&split_ipv6_addr);
|
||||
if (!split_ip_addr && !split_ipv6_addr) {
|
||||
ofono_error("GPRS context: No IP address");
|
||||
error.type = OFONO_ERROR_TYPE_FAILURE;
|
||||
error.error = EINVAL;
|
||||
ril_gprs_context_set_disconnected(gcd);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Steal the call data from the list */
|
||||
g_slist_free(reply->calls);
|
||||
reply->calls = NULL;
|
||||
ril_gprs_context_data_call_free(gcd->active_call);
|
||||
gcd->active_call = call;
|
||||
gcd->state = STATE_ACTIVE;
|
||||
|
||||
ofono_gprs_context_set_interface(gc, call->ifname);
|
||||
ril_gprs_split_gw_by_protocol(call->gateways, &ip_gw, &ipv6_gw);
|
||||
ril_gprs_split_dns_by_protocol(call->dnses, &dns_addr, &dns_ipv6_addr);
|
||||
|
||||
/* TODO:
|
||||
* RILD can return multiple addresses; oFono only supports setting
|
||||
* a single IPv4 and single IPV6 address. At this time, we only use
|
||||
* the first address. It's possible that a RIL may just specify
|
||||
* the end-points of the point-to-point connection, in which case this
|
||||
* code will need to changed to handle such a device.
|
||||
*/
|
||||
|
||||
if (split_ipv6_addr &&
|
||||
(call->prot == OFONO_GPRS_PROTO_IPV6 ||
|
||||
call->prot == OFONO_GPRS_PROTO_IPV4V6)) {
|
||||
|
||||
ofono_gprs_context_set_ipv6_address(gc, split_ipv6_addr[0]);
|
||||
ofono_gprs_context_set_ipv6_gateway(gc, ipv6_gw);
|
||||
ofono_gprs_context_set_ipv6_dns_servers(gc,
|
||||
(const char **) dns_ipv6_addr);
|
||||
}
|
||||
|
||||
if (split_ip_addr &&
|
||||
(call->prot == OFONO_GPRS_PROTO_IP ||
|
||||
call->prot == OFONO_GPRS_PROTO_IPV4V6)) {
|
||||
ofono_gprs_context_set_ipv4_netmask(gc, split_ip_addr[1]);
|
||||
ofono_gprs_context_set_ipv4_address(gc, split_ip_addr[0], TRUE);
|
||||
ofono_gprs_context_set_ipv4_gateway(gc, ip_gw);
|
||||
ofono_gprs_context_set_ipv4_dns_servers(gc,
|
||||
(const char **) dns_addr);
|
||||
}
|
||||
|
||||
done:
|
||||
ril_gprs_context_data_call_list_free(reply);
|
||||
g_strfreev(split_ip_addr);
|
||||
g_strfreev(split_ipv6_addr);
|
||||
g_strfreev(dns_addr);
|
||||
g_strfreev(dns_ipv6_addr);
|
||||
g_free(ip_gw);
|
||||
g_free(ipv6_gw);
|
||||
|
||||
cb(&error, 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 ril_gprs_context *gcd = ril_gprs_context_get_data(gc);
|
||||
struct ofono_netreg *netreg = ril_modem_ofono_netreg(gcd->modem);
|
||||
struct ofono_gprs *gprs = ril_modem_ofono_gprs(gcd->modem);
|
||||
const int rs = ofono_netreg_get_status(netreg);
|
||||
const gchar *protocol_str;
|
||||
GRilIoRequest* req;
|
||||
int tech, auth;
|
||||
|
||||
/* Let's make sure that we aren't connecting when roaming not allowed */
|
||||
if (rs == NETWORK_REGISTRATION_STATUS_ROAMING) {
|
||||
if (!ofono_gprs_get_roaming_allowed(gprs) &&
|
||||
ril_netreg_check_if_really_roaming(netreg, rs) ==
|
||||
NETWORK_REGISTRATION_STATUS_ROAMING) {
|
||||
struct ofono_error error;
|
||||
ofono_info("Can't activate context %d (roaming)",
|
||||
ctx->cid);
|
||||
cb(ril_error_failure(&error), data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ofono_info("Activating context: %d", ctx->cid);
|
||||
protocol_str = ril_gprs_ofono_protocol_to_ril(ctx->proto);
|
||||
GASSERT(protocol_str);
|
||||
|
||||
/* ril.h has this to say about the radio tech parameter:
|
||||
*
|
||||
* ((const char **)data)[0] Radio technology to use: 0-CDMA,
|
||||
* 1-GSM/UMTS, 2... for values above 2
|
||||
* this is RIL_RadioTechnology + 2.
|
||||
*
|
||||
* Makes little sense but it is what it is.
|
||||
*/
|
||||
tech = ril_gprs_ril_data_tech(gprs);
|
||||
if (tech > 2) {
|
||||
tech += 2;
|
||||
} else {
|
||||
/*
|
||||
* This value used to be hardcoded, let's keep using it
|
||||
* as the default.
|
||||
*/
|
||||
tech = RADIO_TECH_HSPA;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
auth = (ctx->username && ctx->username[0]) ?
|
||||
RIL_AUTH_BOTH : RIL_AUTH_NONE;
|
||||
|
||||
/*
|
||||
* TODO: add comments about tethering, other non-public
|
||||
* profiles...
|
||||
*/
|
||||
req = grilio_request_new();
|
||||
grilio_request_append_int32(req, SETUP_DATA_CALL_PARAMS);
|
||||
grilio_request_append_format(req, "%d", tech);
|
||||
grilio_request_append_utf8(req, DATA_PROFILE_DEFAULT_STR);
|
||||
grilio_request_append_utf8(req, ctx->apn);
|
||||
grilio_request_append_utf8(req, ctx->username);
|
||||
grilio_request_append_utf8(req, ctx->password);
|
||||
grilio_request_append_format(req, "%d", auth);
|
||||
grilio_request_append_utf8(req, protocol_str);
|
||||
|
||||
GASSERT(ctx->cid != CTX_ID_NONE);
|
||||
gcd->active_ctx_cid = ctx->cid;
|
||||
gcd->state = STATE_ACTIVATING;
|
||||
|
||||
grilio_queue_send_request_full(gcd->q, req, RIL_REQUEST_SETUP_DATA_CALL,
|
||||
ril_gprs_context_activate_primary_cb, ril_gprs_context_cbd_free,
|
||||
ril_gprs_context_cbd_new(gcd, cb, data));
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_gprs_context_deactivate_primary_cb(GRilIoChannel *io, int err,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_gprs_context_cbd *cbd = user_data;
|
||||
struct ril_gprs_context *gcd = cbd->gcd;
|
||||
ofono_gprs_context_cb_t cb = cbd->cb;
|
||||
|
||||
/* Reply has no data... */
|
||||
if (err == RIL_E_SUCCESS) {
|
||||
ofono_info("Deactivated data call");
|
||||
if (gcd->state == STATE_DEACTIVATING) {
|
||||
gcd->state = STATE_IDLE;
|
||||
}
|
||||
|
||||
cb(ril_error_ok(&error), cbd->data);
|
||||
} else {
|
||||
ofono_error("Deactivate failure: %s", ril_error_to_string(err));
|
||||
cb(ril_error_failure(&error), cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static GRilIoRequest *ril_gprs_context_deactivate_req(unsigned int cid)
|
||||
{
|
||||
/*
|
||||
* TODO: airplane-mode; change reason to '1',
|
||||
* which means "radio power off".
|
||||
*/
|
||||
GRilIoRequest *req = grilio_request_new();
|
||||
grilio_request_append_int32(req, DEACTIVATE_DATA_CALL_PARAMS);
|
||||
grilio_request_append_format(req, "%d", cid);
|
||||
grilio_request_append_format(req, "%d",
|
||||
RIL_DEACTIVATE_DATA_CALL_NO_REASON);
|
||||
return req;
|
||||
}
|
||||
|
||||
static void ril_gprs_context_deactivate_primary(struct ofono_gprs_context *gc,
|
||||
unsigned int id, ofono_gprs_context_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_gprs_context *gcd = ril_gprs_context_get_data(gc);
|
||||
|
||||
ofono_info("Deactivate primary");
|
||||
|
||||
GASSERT(cb);
|
||||
if (gcd->active_call >= 0) {
|
||||
GRilIoRequest* req;
|
||||
|
||||
GASSERT(gcd->active_ctx_cid == id);
|
||||
req = ril_gprs_context_deactivate_req(gcd->active_call->cid);
|
||||
grilio_queue_send_request_full(gcd->q, req,
|
||||
RIL_REQUEST_DEACTIVATE_DATA_CALL,
|
||||
ril_gprs_context_deactivate_primary_cb,
|
||||
ril_gprs_context_cbd_free,
|
||||
ril_gprs_context_cbd_new(gcd, cb, data));
|
||||
grilio_request_unref(req);
|
||||
gcd->state = STATE_DEACTIVATING;
|
||||
} else {
|
||||
struct ofono_error error;
|
||||
cb(ril_error_ok(&error), data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_gprs_context_shutdown(struct ril_gprs_context *gcd)
|
||||
{
|
||||
if (gcd->active_call && gcd->state == STATE_ACTIVE) {
|
||||
GRilIoRequest* req;
|
||||
|
||||
DBG("cid: %d", gcd->active_call->cid);
|
||||
|
||||
/*
|
||||
* Send it to GRilIoChannel so that it doesn't get cancelled
|
||||
* by ril_gprs_context_remove()
|
||||
*/
|
||||
req = ril_gprs_context_deactivate_req(gcd->active_call->cid);
|
||||
grilio_channel_send_request(gcd->io, req,
|
||||
RIL_REQUEST_DEACTIVATE_DATA_CALL);
|
||||
grilio_request_unref(req);
|
||||
|
||||
/*
|
||||
* When disconnect actually completes, we will receive
|
||||
* RIL_UNSOL_DATA_CALL_LIST_CHANGED event
|
||||
*/
|
||||
gcd->state = STATE_DEACTIVATING;
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_gprs_context_detach_shutdown(struct ofono_gprs_context *gc,
|
||||
unsigned int id)
|
||||
{
|
||||
struct ril_gprs_context *gcd = ril_gprs_context_get_data(gc);
|
||||
|
||||
DBG("%d", id);
|
||||
GASSERT(gcd->active_ctx_cid == id);
|
||||
ril_gprs_context_shutdown(gcd);
|
||||
}
|
||||
|
||||
static int ril_gprs_context_probe(struct ofono_gprs_context *gc,
|
||||
unsigned int vendor, void *data)
|
||||
{
|
||||
struct ril_modem *modem = data;
|
||||
struct ril_gprs_context *gcd = g_new0(struct ril_gprs_context, 1);
|
||||
|
||||
DBG("");
|
||||
gcd->gc = gc;
|
||||
gcd->modem = modem;
|
||||
gcd->io = grilio_channel_ref(ril_modem_io(modem));
|
||||
gcd->q = grilio_queue_new(gcd->io);
|
||||
gcd->regid = grilio_channel_add_unsol_event_handler(gcd->io,
|
||||
ril_gprs_context_call_list_changed,
|
||||
RIL_UNSOL_DATA_CALL_LIST_CHANGED, gcd);
|
||||
ril_gprs_context_set_disconnected(gcd);
|
||||
ofono_gprs_context_set_data(gc, gcd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ril_gprs_context_remove(struct ofono_gprs_context *gc)
|
||||
{
|
||||
struct ril_gprs_context *gcd = ril_gprs_context_get_data(gc);
|
||||
|
||||
DBG("");
|
||||
ril_gprs_context_shutdown(gcd);
|
||||
ofono_gprs_context_set_data(gc, NULL);
|
||||
|
||||
grilio_channel_remove_handler(gcd->io, gcd->regid);
|
||||
grilio_channel_unref(gcd->io);
|
||||
grilio_queue_cancel_all(gcd->q, FALSE);
|
||||
grilio_queue_unref(gcd->q);
|
||||
ril_gprs_context_data_call_free(gcd->active_call);
|
||||
g_free(gcd);
|
||||
}
|
||||
|
||||
const struct ofono_gprs_context_driver ril_gprs_context_driver = {
|
||||
.name = RILMODEM_DRIVER,
|
||||
.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,
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
31
ofono/drivers/ril/ril_log.h
Normal file
31
ofono/drivers/ril/ril_log.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#ifndef RIL_LOG_H
|
||||
#define RIL_LOG_H
|
||||
|
||||
#define GLOG_MODULE_NAME ril_log
|
||||
#include <gutil_log.h>
|
||||
#include <ofono/log.h>
|
||||
|
||||
#endif /* RIL_LOG_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
133
ofono/drivers/ril/ril_mce.c
Normal file
133
ofono/drivers/ril/ril_mce.c
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_mce.h"
|
||||
#include "ril_constants.h"
|
||||
|
||||
#include <grilio_channel.h>
|
||||
#include <grilio_request.h>
|
||||
|
||||
#include <mce/dbus-names.h>
|
||||
#include <mce/mode-names.h>
|
||||
|
||||
#include <ofono/log.h>
|
||||
#include <ofono/dbus.h>
|
||||
|
||||
#include <gdbus.h>
|
||||
|
||||
struct ril_mce {
|
||||
GRilIoChannel *io;
|
||||
DBusConnection *conn;
|
||||
int screen_state;
|
||||
guint daemon_watch;
|
||||
guint signal_watch;
|
||||
};
|
||||
|
||||
static void ril_mce_send_screen_state(struct ril_mce *mce, gboolean on)
|
||||
{
|
||||
GRilIoRequest *req = grilio_request_sized_new(8);
|
||||
|
||||
grilio_request_append_int32(req, 1); /* Number of params */
|
||||
grilio_request_append_int32(req, on); /* screen on/off */
|
||||
|
||||
grilio_channel_send_request(mce->io, req, RIL_REQUEST_SCREEN_STATE);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static gboolean ril_mce_display_changed(DBusConnection *conn,
|
||||
DBusMessage *message, void *user_data)
|
||||
{
|
||||
DBusMessageIter iter;
|
||||
|
||||
if (dbus_message_iter_init(message, &iter) &&
|
||||
dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
|
||||
struct ril_mce *mce = user_data;
|
||||
const char *value = NULL;
|
||||
int state;
|
||||
|
||||
dbus_message_iter_get_basic(&iter, &value);
|
||||
DBG(" %s", value);
|
||||
|
||||
/* It is on if it's not off */
|
||||
state = (g_strcmp0(value, MCE_DISPLAY_OFF_STRING) != 0);
|
||||
if (mce->screen_state != state) {
|
||||
mce->screen_state = state;
|
||||
ril_mce_send_screen_state(mce, state);
|
||||
}
|
||||
} else {
|
||||
DBG("");
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void ril_mce_connect(DBusConnection *conn, void *user_data)
|
||||
{
|
||||
struct ril_mce *mce = user_data;
|
||||
|
||||
DBG("");
|
||||
if (!mce->signal_watch) {
|
||||
mce->signal_watch = g_dbus_add_signal_watch(conn,
|
||||
MCE_SERVICE, NULL, MCE_SIGNAL_IF, MCE_DISPLAY_SIG,
|
||||
ril_mce_display_changed, mce, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_mce_disconnect(DBusConnection *conn, void *user_data)
|
||||
{
|
||||
struct ril_mce *mce = user_data;
|
||||
|
||||
DBG("");
|
||||
if (mce->signal_watch) {
|
||||
g_dbus_remove_watch(conn, mce->signal_watch);
|
||||
mce->signal_watch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct ril_mce *ril_mce_new(GRilIoChannel *io)
|
||||
{
|
||||
struct ril_mce *mce = g_new0(struct ril_mce, 1);
|
||||
|
||||
mce->conn = dbus_connection_ref(ofono_dbus_get_connection());
|
||||
mce->io = grilio_channel_ref(io);
|
||||
mce->screen_state = -1;
|
||||
mce->daemon_watch = g_dbus_add_service_watch(mce->conn, MCE_SERVICE,
|
||||
ril_mce_connect, ril_mce_disconnect, mce, NULL);
|
||||
|
||||
return mce;
|
||||
}
|
||||
|
||||
void ril_mce_free(struct ril_mce *mce)
|
||||
{
|
||||
if (mce) {
|
||||
if (mce->signal_watch) {
|
||||
g_dbus_remove_watch(mce->conn, mce->signal_watch);
|
||||
}
|
||||
if (mce->daemon_watch) {
|
||||
g_dbus_remove_watch(mce->conn, mce->daemon_watch);
|
||||
}
|
||||
dbus_connection_unref(mce->conn);
|
||||
grilio_channel_unref(mce->io);
|
||||
g_free(mce);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
32
ofono/drivers/ril/ril_mce.h
Normal file
32
ofono/drivers/ril/ril_mce.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#ifndef RIL_MCE_H
|
||||
#define RIL_MCE_H
|
||||
|
||||
#include "ril_types.h"
|
||||
|
||||
struct ril_mce *ril_mce_new(GRilIoChannel *io);
|
||||
void ril_mce_free(struct ril_mce *mce);
|
||||
|
||||
#endif /* RIL_MCE_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
560
ofono/drivers/ril/ril_modem.c
Normal file
560
ofono/drivers/ril/ril_modem.c
Normal file
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_constants.h"
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
|
||||
#include "ofono.h"
|
||||
|
||||
#define MAX_PDP_CONTEXTS (2)
|
||||
|
||||
enum ril_modem_power_state {
|
||||
POWERED_OFF,
|
||||
POWERING_ON,
|
||||
POWERED_ON,
|
||||
POWERING_OFF
|
||||
};
|
||||
|
||||
enum ril_modem_online_state {
|
||||
OFFLINE,
|
||||
GOING_ONLINE,
|
||||
ONLINE,
|
||||
GOING_OFFLINE
|
||||
};
|
||||
|
||||
enum ril_modem_events {
|
||||
MODEM_EVENT_CONNECTED,
|
||||
MODEM_EVENT_RADIO_STATE_CHANGED,
|
||||
MODEM_EVENT_ERROR,
|
||||
MODEM_EVENT_COUNT
|
||||
};
|
||||
|
||||
struct ril_modem_online_request {
|
||||
ofono_modem_online_cb_t cb;
|
||||
void *data;
|
||||
guint id;
|
||||
};
|
||||
|
||||
struct ril_modem {
|
||||
GRilIoChannel *io;
|
||||
GRilIoQueue *q;
|
||||
struct ofono_modem *modem;
|
||||
struct ofono_radio_settings *radio_settings;
|
||||
struct ril_modem_config config;
|
||||
char *default_name;
|
||||
|
||||
enum ril_radio_state radio_state;
|
||||
enum ril_modem_power_state power_state;
|
||||
gulong event_id[MODEM_EVENT_COUNT];
|
||||
|
||||
ril_modem_cb_t error_cb;
|
||||
void *error_cb_data;
|
||||
|
||||
ril_modem_cb_t removed_cb;
|
||||
void *removed_cb_data;
|
||||
|
||||
struct ril_modem_online_request set_online;
|
||||
struct ril_modem_online_request set_offline;
|
||||
};
|
||||
|
||||
static guint ril_modem_request_power(struct ril_modem *md, gboolean on,
|
||||
GRilIoChannelResponseFunc cb);
|
||||
|
||||
static inline struct ril_modem *ril_modem_from_ofono(struct ofono_modem *modem)
|
||||
{
|
||||
return ofono_modem_get_data(modem);
|
||||
}
|
||||
|
||||
GRilIoChannel *ril_modem_io(struct ril_modem *md)
|
||||
{
|
||||
return md ? md->io : NULL;
|
||||
}
|
||||
|
||||
const struct ril_modem_config *ril_modem_config(struct ril_modem *md)
|
||||
{
|
||||
return md ? &md->config : NULL;
|
||||
}
|
||||
|
||||
struct ofono_modem *ril_modem_ofono_modem(struct ril_modem *md)
|
||||
{
|
||||
return md ? md->modem : NULL;
|
||||
}
|
||||
|
||||
struct ofono_sim *ril_modem_ofono_sim(struct ril_modem *md)
|
||||
{
|
||||
return (md && md->modem) ?
|
||||
__ofono_atom_find(OFONO_ATOM_TYPE_SIM, md->modem) :
|
||||
NULL;
|
||||
}
|
||||
|
||||
struct ofono_gprs *ril_modem_ofono_gprs(struct ril_modem *md)
|
||||
{
|
||||
return (md && md->modem) ?
|
||||
__ofono_atom_find(OFONO_ATOM_TYPE_GPRS, md->modem) :
|
||||
NULL;
|
||||
}
|
||||
|
||||
struct ofono_netreg *ril_modem_ofono_netreg(struct ril_modem *md)
|
||||
{
|
||||
return (md && md->modem) ?
|
||||
__ofono_atom_find(OFONO_ATOM_TYPE_NETREG, md->modem) :
|
||||
NULL;
|
||||
}
|
||||
|
||||
void ril_modem_delete(struct ril_modem *md)
|
||||
{
|
||||
if (md && md->modem) {
|
||||
ofono_modem_remove(md->modem);
|
||||
}
|
||||
}
|
||||
|
||||
void ril_modem_set_error_cb(struct ril_modem *md, ril_modem_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
md->error_cb = cb;
|
||||
md->error_cb_data = data;
|
||||
}
|
||||
|
||||
void ril_modem_set_removed_cb(struct ril_modem *md, ril_modem_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
md->removed_cb = cb;
|
||||
md->removed_cb_data = data;
|
||||
}
|
||||
|
||||
void ril_modem_allow_data(struct ril_modem *md)
|
||||
{
|
||||
if (md && md->modem) {
|
||||
GRilIoRequest *req = grilio_request_sized_new(8);
|
||||
|
||||
DBG("%s", ofono_modem_get_path(md->modem));
|
||||
grilio_request_append_int32(req, 1);
|
||||
grilio_request_append_int32(req, TRUE);
|
||||
grilio_queue_send_request(md->q, req, RIL_REQUEST_ALLOW_DATA);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_modem_signal_error(struct ril_modem *md)
|
||||
{
|
||||
if (md->modem && md->error_cb) {
|
||||
ril_modem_cb_t cb = md->error_cb;
|
||||
void *data = md->error_cb_data;
|
||||
|
||||
md->error_cb = NULL;
|
||||
md->error_cb_data = NULL;
|
||||
cb(md, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_modem_online_request_ok(GRilIoChannel* io,
|
||||
struct ril_modem_online_request *req)
|
||||
{
|
||||
if (req->id) {
|
||||
grilio_channel_cancel_request(io, req->id, FALSE);
|
||||
req->id = 0;
|
||||
}
|
||||
|
||||
if (req->cb) {
|
||||
struct ofono_error error;
|
||||
ofono_modem_online_cb_t cb = req->cb;
|
||||
void *data = req->data;
|
||||
|
||||
req->cb = NULL;
|
||||
req->data = NULL;
|
||||
cb(ril_error_ok(&error), data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_modem_update_online_state(struct ril_modem *md)
|
||||
{
|
||||
switch (md->radio_state) {
|
||||
case RADIO_STATE_ON:
|
||||
ril_modem_online_request_ok(md->io, &md->set_online);
|
||||
break;
|
||||
|
||||
case RADIO_STATE_OFF:
|
||||
case RADIO_STATE_UNAVAILABLE:
|
||||
ril_modem_online_request_ok(md->io, &md->set_offline);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!md->set_offline.id && !md->set_online.id &&
|
||||
md->power_state == POWERING_OFF) {
|
||||
md->power_state = POWERED_OFF;
|
||||
ofono_modem_set_powered(md->modem, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_modem_online_request_done(struct ril_modem *md,
|
||||
struct ril_modem_online_request *req, int ril_status)
|
||||
{
|
||||
GASSERT(req->id);
|
||||
GASSERT(req->cb);
|
||||
GASSERT(req->data);
|
||||
req->id = 0;
|
||||
|
||||
/* If this request has completed successfully, we will
|
||||
* invoke the callback and notify ofono core when we get
|
||||
* RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, i.e. the power
|
||||
* state has actually changed */
|
||||
if (ril_status != RIL_E_SUCCESS) {
|
||||
struct ofono_error error;
|
||||
ofono_modem_online_cb_t cb = req->cb;
|
||||
void *data = req->data;
|
||||
|
||||
req->cb = NULL;
|
||||
req->data = NULL;
|
||||
cb(ril_error_failure(&error), data);
|
||||
}
|
||||
|
||||
ril_modem_update_online_state(md);
|
||||
}
|
||||
|
||||
static void ril_modem_set_online_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_modem *md = user_data;
|
||||
|
||||
DBG("Power on status %s", ril_error_to_string(status));
|
||||
ril_modem_online_request_done(md, &md->set_online, status);
|
||||
}
|
||||
|
||||
static void ril_modem_set_offline_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_modem *md = user_data;
|
||||
|
||||
DBG("Power on status %s", ril_error_to_string(status));
|
||||
ril_modem_online_request_done(md, &md->set_offline, status);
|
||||
}
|
||||
|
||||
static GRilIoRequest *ril_modem_request_radio_power(gboolean on)
|
||||
{
|
||||
GRilIoRequest *req = grilio_request_sized_new(8);
|
||||
|
||||
grilio_request_append_int32(req, 1);
|
||||
grilio_request_append_int32(req, on); /* Radio ON=1, OFF=0 */
|
||||
return req;
|
||||
}
|
||||
|
||||
static guint ril_modem_request_power(struct ril_modem *md, gboolean on,
|
||||
GRilIoChannelResponseFunc cb)
|
||||
{
|
||||
guint id = 0;
|
||||
|
||||
if (md->q) {
|
||||
GRilIoRequest *req = ril_modem_request_radio_power(on);
|
||||
|
||||
DBG("[%u] %s", md->config.slot, on ? "ON" : "OFF");
|
||||
id = grilio_queue_send_request_full(md->q, req,
|
||||
RIL_REQUEST_RADIO_POWER, cb, NULL, md);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static void ril_modem_connected(struct ril_modem *md)
|
||||
{
|
||||
ofono_debug("RIL version %u", md->io->ril_version);
|
||||
ril_modem_request_power(md, FALSE, NULL);
|
||||
if (md->power_state == POWERING_ON) {
|
||||
md->power_state = POWERED_ON;
|
||||
ofono_modem_set_powered(md->modem, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_modem_connected_cb(GRilIoChannel *io, void *user_data)
|
||||
{
|
||||
ril_modem_connected((struct ril_modem *)user_data);
|
||||
}
|
||||
|
||||
static void ril_modem_pre_sim(struct ofono_modem *modem)
|
||||
{
|
||||
struct ril_modem *md = ril_modem_from_ofono(modem);
|
||||
|
||||
DBG("");
|
||||
ofono_devinfo_create(modem, 0, RILMODEM_DRIVER, md);
|
||||
ofono_sim_create(modem, 0, RILMODEM_DRIVER, md);
|
||||
ofono_voicecall_create(modem, 0, RILMODEM_DRIVER, md);
|
||||
}
|
||||
|
||||
static void ril_modem_post_sim(struct ofono_modem *modem)
|
||||
{
|
||||
struct ril_modem *md = ril_modem_from_ofono(modem);
|
||||
struct ofono_gprs *gprs;
|
||||
struct ofono_gprs_context *gc;
|
||||
int i;
|
||||
|
||||
DBG("");
|
||||
ofono_sms_create(modem, 0, RILMODEM_DRIVER, md);
|
||||
gprs = ofono_gprs_create(modem, 0, RILMODEM_DRIVER, md);
|
||||
if (gprs) {
|
||||
for (i = 0; i < MAX_PDP_CONTEXTS; i++) {
|
||||
gc = ofono_gprs_context_create(modem, 0,
|
||||
RILMODEM_DRIVER, md);
|
||||
if (gc == NULL)
|
||||
break;
|
||||
|
||||
ofono_gprs_add_context(gprs, gc);
|
||||
}
|
||||
}
|
||||
|
||||
ofono_phonebook_create(modem, 0, RILMODEM_DRIVER, md);
|
||||
ofono_call_forwarding_create(modem, 0, RILMODEM_DRIVER, md);
|
||||
ofono_call_barring_create(modem, 0, RILMODEM_DRIVER, md);
|
||||
ofono_stk_create(modem, 0, RILMODEM_DRIVER, md);
|
||||
ofono_message_waiting_register(ofono_message_waiting_create(modem));
|
||||
}
|
||||
|
||||
static void ril_modem_post_online(struct ofono_modem *modem)
|
||||
{
|
||||
struct ril_modem *md = ril_modem_from_ofono(modem);
|
||||
|
||||
DBG("");
|
||||
ofono_call_volume_create(modem, 0, RILMODEM_DRIVER, md);
|
||||
ofono_netreg_create(modem, 0, RILMODEM_DRIVER, md);
|
||||
ofono_ussd_create(modem, 0, RILMODEM_DRIVER, md);
|
||||
ofono_call_settings_create(modem, 0, RILMODEM_DRIVER, md);
|
||||
ofono_oem_raw_create(modem, 0, RILMODEM_DRIVER, md);
|
||||
}
|
||||
|
||||
static void ril_modem_set_online(struct ofono_modem *modem, ofono_bool_t online,
|
||||
ofono_modem_online_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_modem *md = ril_modem_from_ofono(modem);
|
||||
struct ril_modem_online_request *req;
|
||||
|
||||
DBG("%s going %sline", ofono_modem_get_path(modem),
|
||||
online ? "on" : "off");
|
||||
|
||||
GASSERT(md->power_state == POWERED_ON);
|
||||
if (online) {
|
||||
req = &md->set_online;
|
||||
GASSERT(!req->id);
|
||||
req->id = ril_modem_request_power(md, TRUE,
|
||||
ril_modem_set_online_cb);
|
||||
} else {
|
||||
req = &md->set_offline;
|
||||
GASSERT(!req->id);
|
||||
req->id = ril_modem_request_power(md, FALSE,
|
||||
ril_modem_set_offline_cb);
|
||||
}
|
||||
|
||||
if (req->id) {
|
||||
req->cb = cb;
|
||||
req->data = data;
|
||||
} else {
|
||||
struct ofono_error error;
|
||||
cb(ril_error_failure(&error), data);
|
||||
}
|
||||
}
|
||||
|
||||
static int ril_modem_enable(struct ofono_modem *modem)
|
||||
{
|
||||
struct ril_modem *md = ril_modem_from_ofono(modem);
|
||||
|
||||
DBG("%s", ofono_modem_get_path(modem));
|
||||
if (md->io->connected) {
|
||||
md->power_state = POWERED_ON;
|
||||
return 0;
|
||||
} else {
|
||||
DBG("Waiting for RIL_UNSOL_RIL_CONNECTED");
|
||||
md->power_state = POWERING_ON;
|
||||
return -EINPROGRESS;
|
||||
}
|
||||
}
|
||||
|
||||
static int ril_modem_disable(struct ofono_modem *modem)
|
||||
{
|
||||
struct ril_modem *md = ril_modem_from_ofono(modem);
|
||||
|
||||
DBG("%s", ofono_modem_get_path(modem));
|
||||
if (md->set_online.id || md->set_offline.id) {
|
||||
md->power_state = POWERING_OFF;
|
||||
return -EINPROGRESS;
|
||||
} else {
|
||||
md->power_state = POWERED_OFF;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_modem_error(GRilIoChannel *io, const GError *error,
|
||||
void *user_data)
|
||||
{
|
||||
struct ril_modem *md = user_data;
|
||||
|
||||
ofono_error("%s", error->message);
|
||||
grilio_channel_remove_handler(io, md->event_id[MODEM_EVENT_ERROR]);
|
||||
md->event_id[MODEM_EVENT_ERROR] = 0;
|
||||
ril_modem_signal_error(md);
|
||||
}
|
||||
|
||||
static int ril_modem_probe(struct ofono_modem *modem)
|
||||
{
|
||||
DBG("%s", ofono_modem_get_path(modem));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ril_modem_remove(struct ofono_modem *modem)
|
||||
{
|
||||
struct ril_modem *md = ril_modem_from_ofono(modem);
|
||||
int i;
|
||||
|
||||
DBG("%s", ofono_modem_get_path(modem));
|
||||
GASSERT(md->modem);
|
||||
|
||||
if (md->radio_state > RADIO_STATE_UNAVAILABLE) {
|
||||
GRilIoRequest *req = ril_modem_request_radio_power(FALSE);
|
||||
grilio_channel_send_request(md->io, req,
|
||||
RIL_REQUEST_RADIO_POWER);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
if (md->removed_cb) {
|
||||
ril_modem_cb_t cb = md->removed_cb;
|
||||
void *data = md->removed_cb_data;
|
||||
|
||||
md->removed_cb = NULL;
|
||||
md->removed_cb_data = NULL;
|
||||
cb(md, data);
|
||||
}
|
||||
|
||||
ofono_modem_set_data(modem, NULL);
|
||||
|
||||
for (i=0; i<G_N_ELEMENTS(md->event_id); i++) {
|
||||
grilio_channel_remove_handler(md->io, md->event_id[i]);
|
||||
}
|
||||
|
||||
grilio_channel_unref(md->io);
|
||||
grilio_queue_cancel_all(md->q, FALSE);
|
||||
grilio_queue_unref(md->q);
|
||||
g_free(md->default_name);
|
||||
g_free(md);
|
||||
}
|
||||
|
||||
static void ril_modem_radio_state_changed(GRilIoChannel *io, guint ril_event,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_modem *md = user_data;
|
||||
GRilIoParser rilp;
|
||||
int radio_state;
|
||||
|
||||
GASSERT(ril_event == RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED);
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
if (grilio_parser_get_int32(&rilp, &radio_state) &&
|
||||
grilio_parser_at_end(&rilp)) {
|
||||
DBG("%s %s", ofono_modem_get_path(md->modem),
|
||||
ril_radio_state_to_string(radio_state));
|
||||
md->radio_state = radio_state;
|
||||
if (radio_state == RADIO_STATE_ON && !md->radio_settings) {
|
||||
DBG("Initializing radio settings interface");
|
||||
md->radio_settings =
|
||||
ofono_radio_settings_create(md->modem, 0,
|
||||
RILMODEM_DRIVER, md);
|
||||
}
|
||||
|
||||
ril_modem_update_online_state(md);
|
||||
} else {
|
||||
ofono_error("Error parsing RADIO_STATE_CHANGED");
|
||||
}
|
||||
}
|
||||
|
||||
struct ril_modem *ril_modem_create(GRilIoChannel *io, const char *dev,
|
||||
const struct ril_modem_config *config)
|
||||
{
|
||||
struct ofono_modem *modem = ofono_modem_create(dev, RILMODEM_DRIVER);
|
||||
|
||||
if (modem) {
|
||||
int err;
|
||||
struct ril_modem *md = g_new0(struct ril_modem, 1);
|
||||
|
||||
/* Copy config */
|
||||
md->config = *config;
|
||||
if (config->default_name && config->default_name[0]) {
|
||||
md->default_name = g_strdup(config->default_name);
|
||||
} else {
|
||||
md->default_name = g_strdup_printf("SIM%u",
|
||||
config->slot + 1);
|
||||
}
|
||||
md->config.default_name = md->default_name;
|
||||
|
||||
md->modem = modem;
|
||||
md->io = grilio_channel_ref(io);
|
||||
md->q = grilio_queue_new(io);
|
||||
ofono_modem_set_data(modem, md);
|
||||
err = ofono_modem_register(modem);
|
||||
if (!err) {
|
||||
md->event_id[MODEM_EVENT_ERROR] =
|
||||
grilio_channel_add_error_handler(io,
|
||||
ril_modem_error, md);
|
||||
md->event_id[MODEM_EVENT_RADIO_STATE_CHANGED] =
|
||||
grilio_channel_add_unsol_event_handler(io,
|
||||
ril_modem_radio_state_changed,
|
||||
RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED,
|
||||
md);
|
||||
if (io->connected) {
|
||||
ril_modem_connected(md);
|
||||
} else {
|
||||
DBG("[%u] waiting for RIL_UNSOL_RIL_CONNECTED",
|
||||
config->slot);
|
||||
md->event_id[MODEM_EVENT_CONNECTED] =
|
||||
grilio_channel_add_connected_handler(
|
||||
io, ril_modem_connected_cb, md);
|
||||
}
|
||||
|
||||
/*
|
||||
* ofono_modem_reset sets Powered to TRUE without
|
||||
* issuing PropertyChange signal.
|
||||
*/
|
||||
ofono_modem_set_powered(md->modem, FALSE);
|
||||
ofono_modem_set_powered(md->modem, TRUE);
|
||||
return md;
|
||||
} else {
|
||||
ofono_error("Error %d registering %s",
|
||||
err, RILMODEM_DRIVER);
|
||||
}
|
||||
|
||||
ofono_modem_remove(modem);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct ofono_modem_driver ril_modem_driver = {
|
||||
.name = RILMODEM_DRIVER,
|
||||
.probe = ril_modem_probe,
|
||||
.remove = ril_modem_remove,
|
||||
.enable = ril_modem_enable,
|
||||
.disable = ril_modem_disable,
|
||||
.pre_sim = ril_modem_pre_sim,
|
||||
.post_sim = ril_modem_post_sim,
|
||||
.post_online = ril_modem_post_online,
|
||||
.set_online = ril_modem_set_online
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
652
ofono/drivers/ril/ril_netreg.c
Normal file
652
ofono/drivers/ril/ril_netreg.c
Normal file
@@ -0,0 +1,652 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
#include "ril_constants.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "simutil.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
enum ril_netreg_events {
|
||||
NETREG_EVENT_VOICE_NETWORK_STATE_CHANGED,
|
||||
NETREG_EVENT_NITZ_TIME_RECEIVED,
|
||||
NETREG_EVENT_SIGNAL_STRENGTH,
|
||||
NETREG_EVENT_COUNT
|
||||
};
|
||||
|
||||
struct ril_netreg {
|
||||
GRilIoChannel *io;
|
||||
GRilIoQueue *q;
|
||||
struct ofono_netreg *netreg;
|
||||
char mcc[OFONO_MAX_MCC_LENGTH + 1];
|
||||
char mnc[OFONO_MAX_MNC_LENGTH + 1];
|
||||
int tech;
|
||||
struct ofono_network_time time;
|
||||
guint timer_id;
|
||||
int corestatus; /* Registration status previously reported to core */
|
||||
gulong event_id[NETREG_EVENT_COUNT];
|
||||
};
|
||||
|
||||
/* 27.007 Section 7.3 <stat> */
|
||||
enum operator_status {
|
||||
OPERATOR_STATUS_UNKNOWN = 0,
|
||||
OPERATOR_STATUS_AVAILABLE = 1,
|
||||
OPERATOR_STATUS_CURRENT = 2,
|
||||
OPERATOR_STATUS_FORBIDDEN = 3,
|
||||
};
|
||||
|
||||
struct ril_netreg_cbd {
|
||||
struct ril_netreg *nd;
|
||||
union {
|
||||
ofono_netreg_status_cb_t status;
|
||||
ofono_netreg_operator_cb_t operator;
|
||||
ofono_netreg_operator_list_cb_t operator_list;
|
||||
ofono_netreg_register_cb_t reg;
|
||||
ofono_netreg_strength_cb_t strength;
|
||||
gpointer ptr;
|
||||
} cb;
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
#define ril_netreg_cbd_free g_free
|
||||
|
||||
static inline struct ril_netreg *ril_netreg_get_data(struct ofono_netreg *nr)
|
||||
{
|
||||
return ofono_netreg_get_data(nr);
|
||||
}
|
||||
|
||||
static struct ril_netreg_cbd *ril_netreg_cbd_new(struct ril_netreg *nd,
|
||||
void *cb, void *data)
|
||||
{
|
||||
struct ril_netreg_cbd *cbd = g_new0(struct ril_netreg_cbd, 1);
|
||||
|
||||
cbd->nd = nd;
|
||||
cbd->cb.ptr = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static gboolean ril_netreg_extract_mcc_mnc(const char *str,
|
||||
struct ofono_network_operator *op)
|
||||
{
|
||||
if (str) {
|
||||
int i;
|
||||
const char *ptr = str;
|
||||
|
||||
/* Three digit country code */
|
||||
for (i = 0;
|
||||
i < OFONO_MAX_MCC_LENGTH && *ptr && isdigit(*ptr);
|
||||
i++) {
|
||||
op->mcc[i] = *ptr++;
|
||||
}
|
||||
op->mcc[i] = 0;
|
||||
|
||||
if (i == OFONO_MAX_MCC_LENGTH) {
|
||||
/* Usually a 2 but sometimes 3 digit network code */
|
||||
for (i=0;
|
||||
i<OFONO_MAX_MNC_LENGTH && *ptr && isdigit(*ptr);
|
||||
i++) {
|
||||
op->mnc[i] = *ptr++;
|
||||
}
|
||||
op->mnc[i] = 0;
|
||||
|
||||
if (i > 0) {
|
||||
|
||||
/*
|
||||
* Sometimes MCC/MNC are followed by + and
|
||||
* what looks like the technology code. This
|
||||
* is of course completely undocumented.
|
||||
*/
|
||||
if (*ptr == '+') {
|
||||
int tech = ril_parse_tech(ptr+1, NULL);
|
||||
if (tech >= 0) {
|
||||
op->tech = tech;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void ril_netreg_state_cb(GRilIoChannel *io, int call_status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_netreg_cbd *cbd = user_data;
|
||||
ofono_netreg_status_cb_t cb = cbd->cb.status;
|
||||
struct ril_netreg *nd = cbd->nd;
|
||||
struct ril_reg_data reg;
|
||||
int rawstatus;
|
||||
|
||||
DBG("");
|
||||
if (call_status != RIL_E_SUCCESS || !nd->netreg) {
|
||||
ofono_error("voice registration status query fail");
|
||||
nd->corestatus = -1;
|
||||
cb(ril_error_failure(&error), -1, -1, -1, -1, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ril_util_parse_reg(data, len, ®)) {
|
||||
DBG("voice registration status parsing fail");
|
||||
nd->corestatus = -1;
|
||||
cb(ril_error_failure(&error), -1, -1, -1, -1, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
rawstatus = reg.status;
|
||||
if (reg.status == NETWORK_REGISTRATION_STATUS_ROAMING) {
|
||||
reg.status = ril_netreg_check_if_really_roaming(nd->netreg,
|
||||
reg.status);
|
||||
}
|
||||
|
||||
if (rawstatus != reg.status) {
|
||||
ofono_info("voice registration modified %d => %d",
|
||||
rawstatus, reg.status);
|
||||
}
|
||||
|
||||
DBG("status:%d corestatus:%d", reg.status, nd->corestatus);
|
||||
|
||||
if (nd->corestatus != reg.status) {
|
||||
ofono_info("voice registration changes %d (%d)",
|
||||
reg.status, nd->corestatus);
|
||||
}
|
||||
|
||||
nd->corestatus = reg.status;
|
||||
nd->tech = reg.access_tech;
|
||||
cb(ril_error_ok(&error), reg.status, reg.lac, reg.ci, reg.access_tech,
|
||||
cbd->data);
|
||||
}
|
||||
|
||||
static void ril_netreg_status_notify(struct ofono_error *error, int status,
|
||||
int lac, int ci, int tech, gpointer user_data)
|
||||
{
|
||||
struct ril_netreg *nd = user_data;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
DBG("Error during status notification");
|
||||
} else if (nd->netreg) {
|
||||
ofono_netreg_status_notify(nd->netreg, status, lac, ci, tech);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_netreg_network_state_change(GRilIoChannel *io,
|
||||
guint ril_event, const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_netreg *nd = user_data;
|
||||
|
||||
GASSERT(ril_event == RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED);
|
||||
grilio_queue_send_request_full(nd->q, NULL,
|
||||
RIL_REQUEST_VOICE_REGISTRATION_STATE,
|
||||
ril_netreg_state_cb, ril_netreg_cbd_free,
|
||||
ril_netreg_cbd_new(nd, ril_netreg_status_notify, nd));
|
||||
}
|
||||
|
||||
static void ril_netreg_registration_status(struct ofono_netreg *netreg,
|
||||
ofono_netreg_status_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_netreg *nd = ril_netreg_get_data(netreg);
|
||||
|
||||
grilio_queue_send_request_full(nd->q, NULL,
|
||||
RIL_REQUEST_VOICE_REGISTRATION_STATE, ril_netreg_state_cb,
|
||||
ril_netreg_cbd_free, ril_netreg_cbd_new(nd, cb, data));
|
||||
}
|
||||
|
||||
static void ril_netreg_current_operator_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_netreg_cbd *cbd = user_data;
|
||||
struct ril_netreg *nd = cbd->nd;
|
||||
struct ofono_error error;
|
||||
struct ofono_network_operator op;
|
||||
struct ofono_network_operator *result = NULL;
|
||||
gchar *lalpha = NULL, *salpha = NULL, *numeric = NULL;
|
||||
int tmp;
|
||||
GRilIoParser rilp;
|
||||
|
||||
ril_error_init_failure(&error);
|
||||
if (status != RIL_E_SUCCESS) {
|
||||
ofono_error("Failed to retrive the current operator: %s",
|
||||
ril_error_to_string(status));
|
||||
goto done;
|
||||
}
|
||||
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
if (!grilio_parser_get_int32(&rilp, &tmp) || !tmp) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
lalpha = grilio_parser_get_utf8(&rilp);
|
||||
salpha = grilio_parser_get_utf8(&rilp);
|
||||
numeric = grilio_parser_get_utf8(&rilp);
|
||||
|
||||
/* Try to use long by default */
|
||||
if (lalpha) {
|
||||
strncpy(op.name, lalpha, OFONO_MAX_OPERATOR_NAME_LENGTH);
|
||||
} else if (salpha) {
|
||||
strncpy(op.name, salpha, OFONO_MAX_OPERATOR_NAME_LENGTH);
|
||||
} else {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!ril_netreg_extract_mcc_mnc(numeric, &op)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Set to current */
|
||||
op.status = OPERATOR_STATUS_CURRENT;
|
||||
op.tech = nd->tech;
|
||||
result = &op;
|
||||
ril_error_init_ok(&error);
|
||||
|
||||
DBG("lalpha=%s, salpha=%s, numeric=%s, %s, mcc=%s, mnc=%s, %s",
|
||||
lalpha, salpha, numeric, op.name, op.mcc, op.mnc,
|
||||
registration_tech_to_string(op.tech));
|
||||
|
||||
done:
|
||||
cbd->cb.operator(&error, result, cbd->data);
|
||||
g_free(lalpha);
|
||||
g_free(salpha);
|
||||
g_free(numeric);
|
||||
}
|
||||
|
||||
static void ril_netreg_current_operator(struct ofono_netreg *netreg,
|
||||
ofono_netreg_operator_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_netreg *nd = ril_netreg_get_data(netreg);
|
||||
|
||||
grilio_queue_send_request_full(nd->q, NULL, RIL_REQUEST_OPERATOR,
|
||||
ril_netreg_current_operator_cb, ril_netreg_cbd_free,
|
||||
ril_netreg_cbd_new(nd, cb, data));
|
||||
}
|
||||
|
||||
static void ril_netreg_list_operators_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_netreg_cbd *cbd = user_data;
|
||||
ofono_netreg_operator_list_cb_t cb = cbd->cb.operator_list;
|
||||
struct ofono_network_operator *list;
|
||||
struct ofono_error error;
|
||||
int noperators = 0, i;
|
||||
GRilIoParser rilp;
|
||||
gboolean ok = TRUE;
|
||||
|
||||
if (status != RIL_E_SUCCESS) {
|
||||
ofono_error("Failed to retrive the list of operators: %s",
|
||||
ril_error_to_string(status));
|
||||
cb(ril_error_failure(&error), 0, NULL, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
|
||||
/* Number of operators at the list (4 strings for every operator) */
|
||||
grilio_parser_get_int32(&rilp, &noperators);
|
||||
GASSERT(!(noperators % 4));
|
||||
noperators /= 4;
|
||||
ofono_info("noperators = %d", noperators);
|
||||
|
||||
list = g_new0(struct ofono_network_operator, noperators);
|
||||
for (i = 0; i < noperators && ok; i++) {
|
||||
struct ofono_network_operator *op = list + i;
|
||||
char *lalpha = grilio_parser_get_utf8(&rilp);
|
||||
char *salpha = grilio_parser_get_utf8(&rilp);
|
||||
char *numeric = grilio_parser_get_utf8(&rilp);
|
||||
char *status = grilio_parser_get_utf8(&rilp);
|
||||
|
||||
/* Try to use long by default */
|
||||
if (lalpha) {
|
||||
strncpy(op->name, lalpha,
|
||||
OFONO_MAX_OPERATOR_NAME_LENGTH);
|
||||
} else if (salpha) {
|
||||
strncpy(op->name, salpha,
|
||||
OFONO_MAX_OPERATOR_NAME_LENGTH);
|
||||
} else {
|
||||
op->name[0] = 0;
|
||||
}
|
||||
|
||||
/* Set the proper status */
|
||||
if (!strcmp(status, "available")) {
|
||||
list[i].status = OPERATOR_STATUS_AVAILABLE;
|
||||
} else if (!strcmp(status, "current")) {
|
||||
list[i].status = OPERATOR_STATUS_CURRENT;
|
||||
} else if (!strcmp(status, "forbidden")) {
|
||||
list[i].status = OPERATOR_STATUS_FORBIDDEN;
|
||||
} else {
|
||||
list[i].status = OPERATOR_STATUS_UNKNOWN;
|
||||
}
|
||||
|
||||
op->tech = ACCESS_TECHNOLOGY_GSM;
|
||||
ok = ril_netreg_extract_mcc_mnc(numeric, op);
|
||||
if (ok) {
|
||||
DBG("[operator=%s, %s, %s, status: %s]", op->name,
|
||||
op->mcc, op->mnc, status);
|
||||
}
|
||||
|
||||
g_free(lalpha);
|
||||
g_free(salpha);
|
||||
g_free(numeric);
|
||||
g_free(status);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
cb(ril_error_ok(&error), noperators, list, cbd->data);
|
||||
} else {
|
||||
cb(ril_error_failure(&error), 0, NULL, cbd->data);
|
||||
}
|
||||
|
||||
g_free(list);
|
||||
}
|
||||
|
||||
static void ril_netreg_list_operators(struct ofono_netreg *netreg,
|
||||
ofono_netreg_operator_list_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_netreg *nd = ril_netreg_get_data(netreg);
|
||||
|
||||
grilio_queue_send_request_full(nd->q, NULL,
|
||||
RIL_REQUEST_QUERY_AVAILABLE_NETWORKS,
|
||||
ril_netreg_list_operators_cb, ril_netreg_cbd_free,
|
||||
ril_netreg_cbd_new(nd, cb, data));
|
||||
}
|
||||
|
||||
static void ril_netreg_register_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_netreg_cbd *cbd = user_data;
|
||||
ofono_netreg_register_cb_t cb = cbd->cb.reg;
|
||||
struct ofono_error error;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
cb(ril_error_ok(&error), cbd->data);
|
||||
} else {
|
||||
ofono_error("registration failed, ril result %d", status);
|
||||
cb(ril_error_failure(&error), cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_netreg_register_auto(struct ofono_netreg *netreg,
|
||||
ofono_netreg_register_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_netreg *nd = ril_netreg_get_data(netreg);
|
||||
|
||||
ofono_info("nw select automatic");
|
||||
grilio_queue_send_request_full(nd->q, NULL,
|
||||
RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC,
|
||||
ril_netreg_register_cb, ril_netreg_cbd_free,
|
||||
ril_netreg_cbd_new(nd, cb, data));
|
||||
}
|
||||
|
||||
static void ril_netreg_register_manual(struct ofono_netreg *netreg,
|
||||
const char *mcc, const char *mnc,
|
||||
ofono_netreg_register_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_netreg *nd = ril_netreg_get_data(netreg);
|
||||
char buf[OFONO_MAX_MCC_LENGTH + OFONO_MAX_MNC_LENGTH + 1];
|
||||
int len = snprintf(buf, sizeof(buf), "%s%s", mcc, mnc);
|
||||
GRilIoRequest *req = grilio_request_new();
|
||||
|
||||
ofono_info("nw select manual: %s%s", mcc, mnc);
|
||||
grilio_request_append_utf8_chars(req, buf, len);
|
||||
grilio_queue_send_request_full(nd->q, req,
|
||||
RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL,
|
||||
ril_netreg_register_cb, ril_netreg_cbd_free,
|
||||
ril_netreg_cbd_new(nd, cb, data));
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static int ril_netreg_get_signal_strength(const void *data, guint len)
|
||||
{
|
||||
GRilIoParser rilp;
|
||||
int gw_signal = 0, cdma_dbm = 0, evdo_dbm = 0, lte_signal = 0;
|
||||
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
|
||||
/* RIL_SignalStrength_v6 */
|
||||
/* GW_SignalStrength */
|
||||
grilio_parser_get_int32(&rilp, &gw_signal);
|
||||
grilio_parser_get_int32(&rilp, NULL); /* bitErrorRate */
|
||||
|
||||
/* CDMA_SignalStrength */
|
||||
grilio_parser_get_int32(&rilp, &cdma_dbm);
|
||||
grilio_parser_get_int32(&rilp, NULL); /* ecio */
|
||||
|
||||
/* EVDO_SignalStrength */
|
||||
grilio_parser_get_int32(&rilp, &evdo_dbm);
|
||||
grilio_parser_get_int32(&rilp, NULL); /* ecio */
|
||||
grilio_parser_get_int32(&rilp, NULL); /* signalNoiseRatio */
|
||||
|
||||
/* LTE_SignalStrength */
|
||||
grilio_parser_get_int32(&rilp, <e_signal);
|
||||
grilio_parser_get_int32(&rilp, NULL); /* rsrp */
|
||||
grilio_parser_get_int32(&rilp, NULL); /* rsrq */
|
||||
grilio_parser_get_int32(&rilp, NULL); /* rssnr */
|
||||
grilio_parser_get_int32(&rilp, NULL); /* cqi */
|
||||
|
||||
DBG("gw: %d, cdma: %d, evdo: %d, lte: %d", gw_signal, cdma_dbm,
|
||||
evdo_dbm, lte_signal);
|
||||
|
||||
/* Return the first valid one */
|
||||
if (gw_signal != 99 && gw_signal != -1) {
|
||||
return (gw_signal * 100) / 31;
|
||||
}
|
||||
|
||||
if (lte_signal != 99 && lte_signal != -1) {
|
||||
return (lte_signal * 100) / 31;
|
||||
}
|
||||
|
||||
/* In case of dbm, return the value directly */
|
||||
if (cdma_dbm != -1) {
|
||||
return MIN(cdma_dbm, 100);
|
||||
}
|
||||
|
||||
if (evdo_dbm != -1) {
|
||||
return MIN(evdo_dbm, 100);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ril_netreg_strength_notify(GRilIoChannel *io, guint ril_event,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_netreg *nd = user_data;
|
||||
int strength;
|
||||
|
||||
GASSERT(ril_event == RIL_UNSOL_SIGNAL_STRENGTH);
|
||||
strength = ril_netreg_get_signal_strength(data, len);
|
||||
DBG("%d", strength);
|
||||
ofono_netreg_strength_notify(nd->netreg, strength);
|
||||
}
|
||||
|
||||
static void ril_netreg_strength_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_netreg_cbd *cbd = user_data;
|
||||
ofono_netreg_strength_cb_t cb = cbd->cb.strength;
|
||||
struct ofono_error error;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
int strength = ril_netreg_get_signal_strength(data, len);
|
||||
cb(ril_error_ok(&error), strength, cbd->data);
|
||||
} else {
|
||||
ofono_error("Failed to retrive the signal strength: %s",
|
||||
ril_error_to_string(status));
|
||||
cb(ril_error_failure(&error), -1, cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_netreg_strength(struct ofono_netreg *netreg,
|
||||
ofono_netreg_strength_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_netreg *nd = ril_netreg_get_data(netreg);
|
||||
|
||||
grilio_queue_send_request_full(nd->q, NULL,
|
||||
RIL_REQUEST_SIGNAL_STRENGTH, ril_netreg_strength_cb,
|
||||
ril_netreg_cbd_free, ril_netreg_cbd_new(nd, cb, data));
|
||||
}
|
||||
|
||||
static void ril_netreg_nitz_notify(GRilIoChannel *io, guint ril_event,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_netreg *nd = user_data;
|
||||
GRilIoParser rilp;
|
||||
int year, mon, mday, hour, min, sec, dst, tzi;
|
||||
char tzs, tz[4];
|
||||
gchar *nitz;
|
||||
|
||||
GASSERT(ril_event == RIL_UNSOL_NITZ_TIME_RECEIVED);
|
||||
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
nitz = grilio_parser_get_utf8(&rilp);
|
||||
|
||||
DBG("%s", nitz);
|
||||
sscanf(nitz, "%u/%u/%u,%u:%u:%u%c%u,%u", &year, &mon, &mday,
|
||||
&hour, &min, &sec, &tzs, &tzi, &dst);
|
||||
snprintf(tz, sizeof(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(nd->netreg, &nd->time);
|
||||
g_free(nitz);
|
||||
}
|
||||
|
||||
int ril_netreg_check_if_really_roaming(struct ofono_netreg *netreg,
|
||||
gint status)
|
||||
{
|
||||
/* These functions tolerate NULL argument */
|
||||
const char *net_mcc = ofono_netreg_get_mcc(netreg);
|
||||
const char *net_mnc = ofono_netreg_get_mnc(netreg);
|
||||
struct sim_spdi *spdi = ofono_netreg_get_spdi(netreg);
|
||||
|
||||
if (spdi && net_mcc && net_mnc) {
|
||||
if (sim_spdi_lookup(spdi, net_mcc, net_mnc)) {
|
||||
ofono_info("voice reg: not roaming based on spdi");
|
||||
return NETWORK_REGISTRATION_STATUS_REGISTERED;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static gboolean ril_netreg_register(gpointer user_data)
|
||||
{
|
||||
struct ril_netreg *nd = user_data;
|
||||
|
||||
GASSERT(nd->timer_id);
|
||||
nd->timer_id = 0;
|
||||
ofono_netreg_register(nd->netreg);
|
||||
|
||||
/* Register for network state changes */
|
||||
nd->event_id[NETREG_EVENT_VOICE_NETWORK_STATE_CHANGED] =
|
||||
grilio_channel_add_unsol_event_handler(nd->io,
|
||||
ril_netreg_network_state_change,
|
||||
RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED, nd);
|
||||
|
||||
/* Register for network time update reports */
|
||||
nd->event_id[NETREG_EVENT_NITZ_TIME_RECEIVED] =
|
||||
grilio_channel_add_unsol_event_handler(nd->io,
|
||||
ril_netreg_nitz_notify,
|
||||
RIL_UNSOL_NITZ_TIME_RECEIVED, nd);
|
||||
|
||||
/* Register for signal strength changes */
|
||||
nd->event_id[NETREG_EVENT_SIGNAL_STRENGTH] =
|
||||
grilio_channel_add_unsol_event_handler(nd->io,
|
||||
ril_netreg_strength_notify,
|
||||
RIL_UNSOL_SIGNAL_STRENGTH, nd);
|
||||
|
||||
/* This makes the timeout a single-shot */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int ril_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor,
|
||||
void *data)
|
||||
{
|
||||
struct ril_modem *modem = data;
|
||||
struct ril_netreg *nd = g_new0(struct ril_netreg, 1);
|
||||
|
||||
DBG("[%u] %p", ril_modem_slot(modem), netreg);
|
||||
nd->io = grilio_channel_ref(ril_modem_io(modem));
|
||||
nd->q = grilio_queue_new(nd->io);
|
||||
nd->netreg = netreg;
|
||||
nd->tech = -1;
|
||||
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;
|
||||
nd->corestatus = -1;
|
||||
|
||||
ofono_netreg_set_data(netreg, nd);
|
||||
nd->timer_id = g_idle_add(ril_netreg_register, nd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ril_netreg_remove(struct ofono_netreg *netreg)
|
||||
{
|
||||
int i;
|
||||
struct ril_netreg *nd = ril_netreg_get_data(netreg);
|
||||
|
||||
DBG("%p", netreg);
|
||||
grilio_queue_cancel_all(nd->q, FALSE);
|
||||
ofono_netreg_set_data(netreg, NULL);
|
||||
|
||||
for (i=0; i<G_N_ELEMENTS(nd->event_id); i++) {
|
||||
grilio_channel_remove_handler(nd->io, nd->event_id[i]);
|
||||
}
|
||||
|
||||
if (nd->timer_id > 0) {
|
||||
g_source_remove(nd->timer_id);
|
||||
}
|
||||
|
||||
grilio_channel_unref(nd->io);
|
||||
grilio_queue_unref(nd->q);
|
||||
g_free(nd);
|
||||
}
|
||||
|
||||
const struct ofono_netreg_driver ril_netreg_driver = {
|
||||
.name = RILMODEM_DRIVER,
|
||||
.probe = ril_netreg_probe,
|
||||
.remove = ril_netreg_remove,
|
||||
.registration_status = ril_netreg_registration_status,
|
||||
.current_operator = ril_netreg_current_operator,
|
||||
.list_operators = ril_netreg_list_operators,
|
||||
.register_auto = ril_netreg_register_auto,
|
||||
.register_manual = ril_netreg_register_manual,
|
||||
.strength = ril_netreg_strength
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
137
ofono/drivers/ril/ril_oem_raw.c
Normal file
137
ofono/drivers/ril/ril_oem_raw.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
#include "ril_constants.h"
|
||||
|
||||
struct ril_oem_raw {
|
||||
GRilIoQueue *q;
|
||||
guint timer_id;
|
||||
};
|
||||
|
||||
struct ril_oem_raw_cbd {
|
||||
ofono_oem_raw_query_cb_t cb;
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
#define ril_oem_raw_cbd_free g_free
|
||||
|
||||
static inline struct ril_oem_raw *ril_oem_raw_get_data(
|
||||
struct ofono_oem_raw *raw)
|
||||
{
|
||||
return ofono_oem_raw_get_data(raw);
|
||||
}
|
||||
|
||||
static struct ril_oem_raw_cbd *ril_oem_raw_cbd_new(ofono_oem_raw_query_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
struct ril_oem_raw_cbd *cbd = g_new0(struct ril_oem_raw_cbd, 1);
|
||||
|
||||
cbd->cb = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static void ril_oem_raw_request_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_oem_raw_cbd *cbd = user_data;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
struct ofono_oem_raw_results result;
|
||||
|
||||
result.data = (void *)data;
|
||||
result.length = len;
|
||||
cbd->cb(ril_error_ok(&error), &result, cbd->data);
|
||||
} else {
|
||||
DBG("error:%d len:%d ", status, len);
|
||||
cbd->cb(ril_error_failure(&error), NULL, cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_oem_raw_request(struct ofono_oem_raw *raw,
|
||||
const struct ofono_oem_raw_request *request,
|
||||
ofono_oem_raw_query_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_oem_raw *od = ril_oem_raw_get_data(raw);
|
||||
GRilIoRequest *req = grilio_request_sized_new(request->length);
|
||||
|
||||
grilio_request_append_bytes(req, request->data, request->length);
|
||||
grilio_queue_send_request_full(od->q, req, RIL_REQUEST_OEM_HOOK_RAW,
|
||||
ril_oem_raw_request_cb, ril_oem_raw_cbd_free,
|
||||
ril_oem_raw_cbd_new(cb, data));
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static gboolean ril_oem_raw_register(gpointer user_data)
|
||||
{
|
||||
struct ofono_oem_raw *raw = user_data;
|
||||
struct ril_oem_raw *od = ril_oem_raw_get_data(raw);
|
||||
|
||||
DBG("");
|
||||
GASSERT(od->timer_id);
|
||||
od->timer_id = 0;
|
||||
ofono_oem_raw_dbus_register(raw);
|
||||
|
||||
/* Single-shot */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int ril_oem_raw_probe(struct ofono_oem_raw *raw, unsigned int vendor,
|
||||
void *data)
|
||||
{
|
||||
struct ril_modem *modem = data;
|
||||
struct ril_oem_raw *od = g_new0(struct ril_oem_raw, 1);
|
||||
|
||||
DBG("");
|
||||
od->q = grilio_queue_new(ril_modem_io(modem));
|
||||
od->timer_id = g_idle_add(ril_oem_raw_register, raw);
|
||||
ofono_oem_raw_set_data(raw, od);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ril_oem_raw_remove(struct ofono_oem_raw *raw)
|
||||
{
|
||||
struct ril_oem_raw *od = ril_oem_raw_get_data(raw);
|
||||
|
||||
DBG("");
|
||||
grilio_queue_cancel_all(od->q, TRUE);
|
||||
ofono_oem_raw_set_data(raw, NULL);
|
||||
|
||||
if (od->timer_id) {
|
||||
g_source_remove(od->timer_id);
|
||||
}
|
||||
|
||||
grilio_queue_unref(od->q);
|
||||
g_free(od);
|
||||
}
|
||||
|
||||
/* const */ struct ofono_oem_raw_driver ril_oem_raw_driver = {
|
||||
.name = RILMODEM_DRIVER,
|
||||
.probe = ril_oem_raw_probe,
|
||||
.remove = ril_oem_raw_remove,
|
||||
.request = ril_oem_raw_request,
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
1026
ofono/drivers/ril/ril_phonebook.c
Normal file
1026
ofono/drivers/ril/ril_phonebook.c
Normal file
File diff suppressed because it is too large
Load Diff
1063
ofono/drivers/ril/ril_plugin.c
Normal file
1063
ofono/drivers/ril/ril_plugin.c
Normal file
File diff suppressed because it is too large
Load Diff
151
ofono/drivers/ril/ril_plugin.h
Normal file
151
ofono/drivers/ril/ril_plugin.h
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#ifndef RIL_PLUGIN_H
|
||||
#define RIL_PLUGIN_H
|
||||
|
||||
#include "ril_types.h"
|
||||
|
||||
#include <ofono/modem.h>
|
||||
#include <ofono/call-barring.h>
|
||||
#include <ofono/call-forwarding.h>
|
||||
#include <ofono/call-settings.h>
|
||||
#include <ofono/call-volume.h>
|
||||
#include <ofono/cbs.h>
|
||||
#include <ofono/devinfo.h>
|
||||
#include <ofono/gprs-context.h>
|
||||
#include <ofono/gprs.h>
|
||||
#include <ofono/netreg.h>
|
||||
#include <ofono/oemraw.h>
|
||||
#include <ofono/phonebook.h>
|
||||
#include <ofono/radio-settings.h>
|
||||
#include <ofono/sim.h>
|
||||
#include <ofono/sms.h>
|
||||
#include <ofono/stk.h>
|
||||
#include <ofono/ussd.h>
|
||||
#include <ofono/voicecall.h>
|
||||
|
||||
#include <grilio_queue.h>
|
||||
#include <grilio_request.h>
|
||||
#include <grilio_parser.h>
|
||||
|
||||
#define RILMODEM_DRIVER "ril"
|
||||
#define RIL_RETRY_SECS (2)
|
||||
#define MAX_SIM_STATUS_RETRIES (15)
|
||||
|
||||
struct ril_plugin {
|
||||
const char *default_voice_imsi;
|
||||
const char *default_data_imsi;
|
||||
const char *default_voice_path;
|
||||
const char *default_data_path;
|
||||
char **available_slots;
|
||||
char **enabled_slots;
|
||||
};
|
||||
|
||||
struct ril_modem_config {
|
||||
guint slot;
|
||||
gboolean enable_4g;
|
||||
const char *default_name;
|
||||
};
|
||||
|
||||
#define RIL_PLUGIN_SIGNAL_VOICE_IMSI (0x01)
|
||||
#define RIL_PLUGIN_SIGNAL_DATA_IMSI (0x02)
|
||||
#define RIL_PLUGIN_SIGNAL_VOICE_PATH (0x04)
|
||||
#define RIL_PLUGIN_SIGNAL_DATA_PATH (0x10)
|
||||
#define RIL_PLUGIN_SIGNAL_ENABLED_SLOTS (0x20)
|
||||
|
||||
struct ril_modem;
|
||||
struct ril_plugin_dbus;
|
||||
typedef void (*ril_modem_cb_t)(struct ril_modem *modem, void *data);
|
||||
|
||||
void ril_plugin_set_enabled_slots(struct ril_plugin *plugin, char **slots);
|
||||
void ril_plugin_set_default_voice_imsi(struct ril_plugin *plugin,
|
||||
const char *imsi);
|
||||
void ril_plugin_set_default_data_imsi(struct ril_plugin *plugin,
|
||||
const char *imsi);
|
||||
|
||||
struct ril_sim_dbus *ril_sim_dbus_new(struct ril_modem *modem);
|
||||
const char *ril_sim_dbus_imsi(struct ril_sim_dbus *dbus);
|
||||
void ril_sim_dbus_free(struct ril_sim_dbus *dbus);
|
||||
|
||||
struct ril_plugin_dbus *ril_plugin_dbus_new(struct ril_plugin *plugin);
|
||||
void ril_plugin_dbus_free(struct ril_plugin_dbus *dbus);
|
||||
void ril_plugin_dbus_signal(struct ril_plugin_dbus *dbus, int mask);
|
||||
|
||||
struct ril_modem *ril_modem_create(GRilIoChannel *io, const char *dev,
|
||||
const struct ril_modem_config *config);
|
||||
void ril_modem_delete(struct ril_modem *modem);
|
||||
void ril_modem_allow_data(struct ril_modem *modem);
|
||||
GRilIoChannel *ril_modem_io(struct ril_modem *modem);
|
||||
const struct ril_modem_config *ril_modem_config(struct ril_modem *modem);
|
||||
struct ofono_sim *ril_modem_ofono_sim(struct ril_modem *modem);
|
||||
struct ofono_gprs *ril_modem_ofono_gprs(struct ril_modem *modem);
|
||||
struct ofono_netreg *ril_modem_ofono_netreg(struct ril_modem *modem);
|
||||
struct ofono_modem *ril_modem_ofono_modem(struct ril_modem *modem);
|
||||
void ril_modem_set_error_cb(struct ril_modem *modem, ril_modem_cb_t cb,
|
||||
void *data);
|
||||
void ril_modem_set_removed_cb(struct ril_modem *modem, ril_modem_cb_t cb,
|
||||
void *data);
|
||||
|
||||
#define ril_modem_slot(md) (ril_modem_config(modem)->slot)
|
||||
#define ril_modem_4g_enabled(md) (ril_modem_config(modem)->enable_4g)
|
||||
#define ril_modem_get_path(md) ofono_modem_get_path(ril_modem_ofono_modem(md))
|
||||
|
||||
void ril_sim_read_file_linear(struct ofono_sim *sim, int fileid,
|
||||
int record, int length, const unsigned char *path,
|
||||
unsigned int path_len, ofono_sim_read_cb_t cb, void *data);
|
||||
void ril_sim_read_file_cyclic(struct ofono_sim *sim, int fileid,
|
||||
int record, int length, const unsigned char *path,
|
||||
unsigned int path_len, ofono_sim_read_cb_t cb, void *data);
|
||||
void ril_sim_read_file_transparent(struct ofono_sim *sim, int fileid,
|
||||
int start, int length, const unsigned char *path,
|
||||
unsigned int path_len, ofono_sim_read_cb_t cb, void *data);
|
||||
void ril_sim_read_file_info(struct ofono_sim *sim, int fileid,
|
||||
const unsigned char *path, unsigned int path_len,
|
||||
ofono_sim_file_info_cb_t cb, void *data);
|
||||
|
||||
int ril_sim_app_type(struct ofono_sim *sim);
|
||||
int ril_gprs_ril_data_tech(struct ofono_gprs *gprs);
|
||||
int ril_netreg_check_if_really_roaming(struct ofono_netreg *netreg,
|
||||
gint status);
|
||||
|
||||
extern const struct ofono_call_barring_driver ril_call_barring_driver;
|
||||
extern const struct ofono_call_forwarding_driver ril_call_forwarding_driver;
|
||||
extern const struct ofono_call_settings_driver ril_call_settings_driver;
|
||||
extern const struct ofono_call_volume_driver ril_call_volume_driver;
|
||||
extern const struct ofono_cbs_driver ril_cbs_driver;
|
||||
extern const struct ofono_devinfo_driver ril_devinfo_driver;
|
||||
extern const struct ofono_gprs_context_driver ril_gprs_context_driver;
|
||||
extern const struct ofono_gprs_driver ril_gprs_driver;
|
||||
extern const struct ofono_modem_driver ril_modem_driver;
|
||||
extern const struct ofono_netreg_driver ril_netreg_driver;
|
||||
extern /* const */ struct ofono_oem_raw_driver ril_oem_raw_driver;
|
||||
extern const struct ofono_phonebook_driver ril_phonebook_driver;
|
||||
extern const struct ofono_radio_settings_driver ril_radio_settings_driver;
|
||||
extern const struct ofono_sim_driver ril_sim_driver;
|
||||
extern const struct ofono_sms_driver ril_sms_driver;
|
||||
extern const struct ofono_stk_driver ril_stk_driver;
|
||||
extern const struct ofono_ussd_driver ril_ussd_driver;
|
||||
extern const struct ofono_voicecall_driver ril_voicecall_driver;
|
||||
|
||||
#endif /* RIL_PLUGIN_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
416
ofono/drivers/ril/ril_plugin_dbus.c
Normal file
416
ofono/drivers/ril/ril_plugin_dbus.c
Normal file
@@ -0,0 +1,416 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
|
||||
#include <ofono/log.h>
|
||||
#include <ofono/dbus.h>
|
||||
|
||||
#include <gutil_strv.h>
|
||||
#include <gutil_log.h>
|
||||
#include <gdbus.h>
|
||||
|
||||
#include "ofono.h"
|
||||
|
||||
struct ril_plugin_dbus {
|
||||
struct ril_plugin *plugin;
|
||||
DBusConnection *conn;
|
||||
};
|
||||
|
||||
#define RIL_DBUS_PATH "/"
|
||||
#define RIL_DBUS_INTERFACE "org.nemomobile.ofono.ModemManager"
|
||||
#define RIL_DBUS_INTERFACE_VERSION (1)
|
||||
|
||||
#define RIL_DBUS_ENABLED_MODEM_CHANGED_SIGNAL "EnabledModemsChanged"
|
||||
#define RIL_DBUS_DEFAULT_VOICE_SIM_CHANGED_SIGNAL "DefaultVoiceSimChanged"
|
||||
#define RIL_DBUS_DEFAULT_DATA_SIM_CHANGED_SIGNAL "DefaultDataSimChanged"
|
||||
#define RIL_DBUS_DEFAULT_VOICE_MODEM_CHANGED_SIGNAL "DefaultVoiceModemChanged"
|
||||
#define RIL_DBUS_DEFAULT_DATA_MODEM_CHANGED_SIGNAL "DefaultDataModemChanged"
|
||||
#define RIL_DBUS_IMSI_AUTO "auto"
|
||||
|
||||
static void ril_plugin_dbus_append_path_array(DBusMessageIter *it, char **paths)
|
||||
{
|
||||
DBusMessageIter array;
|
||||
|
||||
dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY,
|
||||
DBUS_TYPE_OBJECT_PATH_AS_STRING, &array);
|
||||
|
||||
if (paths) {
|
||||
while (*paths) {
|
||||
const char *path = *paths++;
|
||||
dbus_message_iter_append_basic(&array,
|
||||
DBUS_TYPE_OBJECT_PATH, &path);
|
||||
}
|
||||
}
|
||||
|
||||
dbus_message_iter_close_container(it, &array);
|
||||
}
|
||||
|
||||
static void ril_plugin_dbus_append_imsi(DBusMessageIter *it, const char *imsi)
|
||||
{
|
||||
if (!imsi) imsi = RIL_DBUS_IMSI_AUTO;
|
||||
dbus_message_iter_append_basic(it, DBUS_TYPE_STRING, &imsi);
|
||||
}
|
||||
|
||||
static void ril_plugin_dbus_append_path(DBusMessageIter *it, const char *path)
|
||||
{
|
||||
if (!path) path = "";
|
||||
/* It's DBUS_TYPE_STRING because DBUS_TYPE_OBJECT_PATH can't be empty */
|
||||
dbus_message_iter_append_basic(it, DBUS_TYPE_STRING, &path);
|
||||
}
|
||||
|
||||
static void ril_plugin_dbus_message_append_path_array(DBusMessage *msg,
|
||||
char **paths)
|
||||
{
|
||||
DBusMessageIter iter;
|
||||
|
||||
dbus_message_iter_init_append(msg, &iter);
|
||||
ril_plugin_dbus_append_path_array(&iter, paths);
|
||||
}
|
||||
|
||||
static void ril_plugin_dbus_signal_path_array(struct ril_plugin_dbus *dbus,
|
||||
const char *name, char **paths)
|
||||
{
|
||||
DBusMessage *signal = dbus_message_new_signal(RIL_DBUS_PATH,
|
||||
RIL_DBUS_INTERFACE, name);
|
||||
|
||||
ril_plugin_dbus_message_append_path_array(signal, paths);
|
||||
g_dbus_send_message(dbus->conn, signal);
|
||||
}
|
||||
|
||||
static inline void ril_plugin_dbus_signal_imsi(struct ril_plugin_dbus *dbus,
|
||||
const char *name, const char *imsi)
|
||||
{
|
||||
if (!imsi) imsi = RIL_DBUS_IMSI_AUTO;
|
||||
g_dbus_emit_signal(dbus->conn, RIL_DBUS_PATH, RIL_DBUS_INTERFACE,
|
||||
name, DBUS_TYPE_STRING, &imsi, DBUS_TYPE_INVALID);
|
||||
}
|
||||
|
||||
static inline void ril_plugin_dbus_signal_path(struct ril_plugin_dbus *dbus,
|
||||
const char *name, const char *path)
|
||||
{
|
||||
if (!path) path = "";
|
||||
g_dbus_emit_signal(dbus->conn, RIL_DBUS_PATH, RIL_DBUS_INTERFACE,
|
||||
name, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID);
|
||||
}
|
||||
|
||||
void ril_plugin_dbus_signal(struct ril_plugin_dbus *dbus, int mask)
|
||||
{
|
||||
if (dbus) {
|
||||
if (mask & RIL_PLUGIN_SIGNAL_VOICE_IMSI) {
|
||||
ril_plugin_dbus_signal_imsi(dbus,
|
||||
RIL_DBUS_DEFAULT_VOICE_SIM_CHANGED_SIGNAL,
|
||||
dbus->plugin->default_voice_imsi);
|
||||
}
|
||||
if (mask & RIL_PLUGIN_SIGNAL_DATA_IMSI) {
|
||||
ril_plugin_dbus_signal_imsi(dbus,
|
||||
RIL_DBUS_DEFAULT_DATA_SIM_CHANGED_SIGNAL,
|
||||
dbus->plugin->default_data_imsi);
|
||||
}
|
||||
if (mask & RIL_PLUGIN_SIGNAL_ENABLED_SLOTS) {
|
||||
ril_plugin_dbus_signal_path_array(dbus,
|
||||
RIL_DBUS_ENABLED_MODEM_CHANGED_SIGNAL,
|
||||
dbus->plugin->enabled_slots);
|
||||
}
|
||||
if (mask & RIL_PLUGIN_SIGNAL_VOICE_PATH) {
|
||||
ril_plugin_dbus_signal_path(dbus,
|
||||
RIL_DBUS_DEFAULT_VOICE_MODEM_CHANGED_SIGNAL,
|
||||
dbus->plugin->default_voice_path);
|
||||
}
|
||||
if (mask & RIL_PLUGIN_SIGNAL_DATA_PATH) {
|
||||
ril_plugin_dbus_signal_path(dbus,
|
||||
RIL_DBUS_DEFAULT_DATA_MODEM_CHANGED_SIGNAL,
|
||||
dbus->plugin->default_data_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static DBusMessage *ril_plugin_dbus_reply_with_path_array(DBusConnection *conn,
|
||||
DBusMessage *msg, char **paths)
|
||||
{
|
||||
DBusMessage *reply = dbus_message_new_method_return(msg);
|
||||
|
||||
ril_plugin_dbus_message_append_path_array(reply, paths);
|
||||
return reply;
|
||||
}
|
||||
|
||||
static DBusMessage *ril_plugin_dbus_get_all(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
struct ril_plugin_dbus *dbus = data;
|
||||
DBusMessage *reply = dbus_message_new_method_return(msg);
|
||||
dbus_int32_t version = RIL_DBUS_INTERFACE_VERSION;
|
||||
DBusMessageIter iter;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &version);
|
||||
ril_plugin_dbus_append_path_array(&iter, dbus->plugin->available_slots);
|
||||
ril_plugin_dbus_append_path_array(&iter, dbus->plugin->enabled_slots);
|
||||
ril_plugin_dbus_append_imsi(&iter, dbus->plugin->default_data_imsi);
|
||||
ril_plugin_dbus_append_imsi(&iter, dbus->plugin->default_voice_imsi);
|
||||
ril_plugin_dbus_append_path(&iter, dbus->plugin->default_data_path);
|
||||
ril_plugin_dbus_append_path(&iter, dbus->plugin->default_voice_path);
|
||||
return reply;
|
||||
}
|
||||
|
||||
static DBusMessage *ril_plugin_dbus_get_interface_version(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
DBusMessage *reply = dbus_message_new_method_return(msg);
|
||||
dbus_int32_t version = RIL_DBUS_INTERFACE_VERSION;
|
||||
DBusMessageIter iter;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &version);
|
||||
return reply;
|
||||
}
|
||||
|
||||
static DBusMessage *ril_plugin_dbus_get_available_modems(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
struct ril_plugin_dbus *dbus = data;
|
||||
|
||||
return ril_plugin_dbus_reply_with_path_array(conn, msg,
|
||||
dbus->plugin->available_slots);
|
||||
}
|
||||
|
||||
static DBusMessage *ril_plugin_dbus_get_enabled_modems(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
struct ril_plugin_dbus *dbus = data;
|
||||
|
||||
return ril_plugin_dbus_reply_with_path_array(conn, msg,
|
||||
dbus->plugin->enabled_slots);
|
||||
}
|
||||
|
||||
static DBusMessage *ril_plugin_dbus_reply_with_imsi(DBusConnection *conn,
|
||||
DBusMessage *msg, const char *imsi)
|
||||
{
|
||||
DBusMessage *reply = dbus_message_new_method_return(msg);
|
||||
DBusMessageIter iter;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
ril_plugin_dbus_append_imsi(&iter, imsi);
|
||||
return reply;
|
||||
}
|
||||
|
||||
static DBusMessage *ril_plugin_dbus_get_default_data_sim(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
struct ril_plugin_dbus *dbus = data;
|
||||
|
||||
return ril_plugin_dbus_reply_with_imsi(conn, msg,
|
||||
dbus->plugin->default_data_imsi);
|
||||
}
|
||||
|
||||
static DBusMessage *ril_plugin_dbus_get_default_voice_sim(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
struct ril_plugin_dbus *dbus = data;
|
||||
|
||||
return ril_plugin_dbus_reply_with_imsi(conn, msg,
|
||||
dbus->plugin->default_voice_imsi);
|
||||
}
|
||||
|
||||
static DBusMessage *ril_plugin_dbus_reply_with_path(DBusConnection *conn,
|
||||
DBusMessage *msg, const char *path)
|
||||
{
|
||||
DBusMessage *reply = dbus_message_new_method_return(msg);
|
||||
DBusMessageIter iter;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
ril_plugin_dbus_append_path(&iter, path);
|
||||
return reply;
|
||||
}
|
||||
|
||||
static DBusMessage *ril_plugin_dbus_get_default_data_modem(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
struct ril_plugin_dbus *dbus = data;
|
||||
|
||||
return ril_plugin_dbus_reply_with_path(conn, msg,
|
||||
dbus->plugin->default_data_path);
|
||||
}
|
||||
|
||||
static DBusMessage *ril_plugin_dbus_get_default_voice_modem(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
struct ril_plugin_dbus *dbus = data;
|
||||
|
||||
return ril_plugin_dbus_reply_with_path(conn, msg,
|
||||
dbus->plugin->default_voice_path);
|
||||
}
|
||||
|
||||
static DBusMessage *ril_plugin_dbus_set_enabled_modems(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
struct ril_plugin_dbus *dbus = data;
|
||||
DBusMessageIter iter;
|
||||
|
||||
dbus_message_iter_init(msg, &iter);
|
||||
if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
|
||||
char **paths = NULL;
|
||||
DBusMessageIter array;
|
||||
|
||||
dbus_message_iter_recurse(&iter, &array);
|
||||
while (dbus_message_iter_get_arg_type(&array) ==
|
||||
DBUS_TYPE_OBJECT_PATH) {
|
||||
DBusBasicValue value;
|
||||
|
||||
dbus_message_iter_get_basic(&array, &value);
|
||||
paths = gutil_strv_add(paths, value.str);
|
||||
dbus_message_iter_next(&array);
|
||||
}
|
||||
|
||||
ril_plugin_set_enabled_slots(dbus->plugin, paths);
|
||||
g_strfreev(paths);
|
||||
return dbus_message_new_method_return(msg);
|
||||
} else {
|
||||
return __ofono_error_invalid_args(msg);
|
||||
}
|
||||
}
|
||||
|
||||
static DBusMessage *ril_plugin_dbus_set_imsi(struct ril_plugin_dbus *dbus,
|
||||
DBusMessage *msg, void (*apply)(struct ril_plugin *plugin,
|
||||
const char *imsi))
|
||||
{
|
||||
DBusMessageIter iter;
|
||||
|
||||
dbus_message_iter_init(msg, &iter);
|
||||
if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
|
||||
DBusBasicValue value;
|
||||
const char *imsi;
|
||||
|
||||
dbus_message_iter_get_basic(&iter, &value);
|
||||
imsi = value.str;
|
||||
if (!g_strcmp0(imsi, RIL_DBUS_IMSI_AUTO)) imsi = NULL;
|
||||
apply(dbus->plugin, imsi);
|
||||
return dbus_message_new_method_return(msg);
|
||||
} else {
|
||||
return __ofono_error_invalid_args(msg);
|
||||
}
|
||||
}
|
||||
|
||||
static DBusMessage *ril_plugin_dbus_set_default_voice_sim(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
struct ril_plugin_dbus *dbus = data;
|
||||
|
||||
GASSERT(conn == dbus->conn);
|
||||
return ril_plugin_dbus_set_imsi(dbus, msg,
|
||||
ril_plugin_set_default_voice_imsi);
|
||||
}
|
||||
|
||||
static DBusMessage *ril_plugin_dbus_set_default_data_sim(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
struct ril_plugin_dbus *dbus = data;
|
||||
|
||||
GASSERT(conn == dbus->conn);
|
||||
return ril_plugin_dbus_set_imsi(dbus, msg,
|
||||
ril_plugin_set_default_data_imsi);
|
||||
}
|
||||
|
||||
static const GDBusMethodTable ril_plugin_dbus_methods[] = {
|
||||
{ GDBUS_METHOD("GetAll", NULL,
|
||||
GDBUS_ARGS({"version", "i" },
|
||||
{"availableModems", "ao" },
|
||||
{"enabledModems", "ao" },
|
||||
{"defaultDataSim", "s" },
|
||||
{"defaultVoiceSim", "s" },
|
||||
{"defaultDataModem", "s" },
|
||||
{"defaultVoiceModem" , "s"}),
|
||||
ril_plugin_dbus_get_all) },
|
||||
{ GDBUS_METHOD("GetInterfaceVersion",
|
||||
NULL, GDBUS_ARGS({ "version", "i" }),
|
||||
ril_plugin_dbus_get_interface_version) },
|
||||
{ GDBUS_METHOD("GetAvailableModems",
|
||||
NULL, GDBUS_ARGS({ "modems", "ao" }),
|
||||
ril_plugin_dbus_get_available_modems) },
|
||||
{ GDBUS_METHOD("GetEnabledModems",
|
||||
NULL, GDBUS_ARGS({ "modems", "ao" }),
|
||||
ril_plugin_dbus_get_enabled_modems) },
|
||||
{ GDBUS_METHOD("GetDefaultDataSim",
|
||||
NULL, GDBUS_ARGS({ "imsi", "s" }),
|
||||
ril_plugin_dbus_get_default_data_sim) },
|
||||
{ GDBUS_METHOD("GetDefaultVoiceSim",
|
||||
NULL, GDBUS_ARGS({ "imsi", "s" }),
|
||||
ril_plugin_dbus_get_default_voice_sim) },
|
||||
{ GDBUS_METHOD("GetDefaultDataModem",
|
||||
NULL, GDBUS_ARGS({ "path", "s" }),
|
||||
ril_plugin_dbus_get_default_data_modem) },
|
||||
{ GDBUS_METHOD("GetDefaultVoiceModem",
|
||||
NULL, GDBUS_ARGS({ "path", "s" }),
|
||||
ril_plugin_dbus_get_default_voice_modem) },
|
||||
{ GDBUS_METHOD("SetEnabledModems",
|
||||
GDBUS_ARGS({ "modems", "ao" }), NULL,
|
||||
ril_plugin_dbus_set_enabled_modems) },
|
||||
{ GDBUS_METHOD("SetDefaultDataSim",
|
||||
GDBUS_ARGS({ "imsi", "s" }), NULL,
|
||||
ril_plugin_dbus_set_default_data_sim) },
|
||||
{ GDBUS_METHOD("SetDefaultVoiceSim",
|
||||
GDBUS_ARGS({ "imsi", "s" }), NULL,
|
||||
ril_plugin_dbus_set_default_voice_sim) },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const GDBusSignalTable ril_plugin_dbus_signals[] = {
|
||||
{ GDBUS_SIGNAL(RIL_DBUS_ENABLED_MODEM_CHANGED_SIGNAL,
|
||||
GDBUS_ARGS({ "modems", "ao" })) },
|
||||
{ GDBUS_SIGNAL(RIL_DBUS_DEFAULT_DATA_SIM_CHANGED_SIGNAL,
|
||||
GDBUS_ARGS({ "imsi", "s" })) },
|
||||
{ GDBUS_SIGNAL(RIL_DBUS_DEFAULT_VOICE_SIM_CHANGED_SIGNAL,
|
||||
GDBUS_ARGS({ "imsi", "s" })) },
|
||||
{ GDBUS_SIGNAL(RIL_DBUS_DEFAULT_DATA_MODEM_CHANGED_SIGNAL,
|
||||
GDBUS_ARGS({ "path", "s" })) },
|
||||
{ GDBUS_SIGNAL(RIL_DBUS_DEFAULT_VOICE_MODEM_CHANGED_SIGNAL,
|
||||
GDBUS_ARGS({ "path", "s" })) },
|
||||
{ }
|
||||
};
|
||||
|
||||
struct ril_plugin_dbus *ril_plugin_dbus_new(struct ril_plugin *plugin)
|
||||
{
|
||||
struct ril_plugin_dbus *dbus = g_new0(struct ril_plugin_dbus, 1);
|
||||
|
||||
dbus->conn = dbus_connection_ref(ofono_dbus_get_connection());
|
||||
dbus->plugin = plugin;
|
||||
if (g_dbus_register_interface(dbus->conn, RIL_DBUS_PATH,
|
||||
RIL_DBUS_INTERFACE, ril_plugin_dbus_methods,
|
||||
ril_plugin_dbus_signals, NULL, dbus, NULL)) {
|
||||
return dbus;
|
||||
} else {
|
||||
ofono_error("RIL D-Bus register failed");
|
||||
ril_plugin_dbus_free(dbus);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void ril_plugin_dbus_free(struct ril_plugin_dbus *dbus)
|
||||
{
|
||||
if (dbus) {
|
||||
g_dbus_unregister_interface(dbus->conn, RIL_DBUS_PATH,
|
||||
RIL_DBUS_INTERFACE);
|
||||
dbus_connection_unref(dbus->conn);
|
||||
g_free(dbus);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
297
ofono/drivers/ril/ril_radio_settings.c
Normal file
297
ofono/drivers/ril/ril_radio_settings.c
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
#include "ril_constants.h"
|
||||
|
||||
#include "storage.h"
|
||||
|
||||
struct ril_radio_settings {
|
||||
GRilIoQueue *q;
|
||||
int ratmode;
|
||||
guint timer_id;
|
||||
};
|
||||
|
||||
struct ril_radio_settings_cbd {
|
||||
struct ril_radio_settings *rsd;
|
||||
union _ofono_radio_settings_cb {
|
||||
ofono_radio_settings_rat_mode_set_cb_t rat_mode_set;
|
||||
ofono_radio_settings_rat_mode_query_cb_t rat_mode_query;
|
||||
gpointer ptr;
|
||||
} cb;
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
#define RIL_STORE "rilmodem"
|
||||
#define LTE_FLAG "4gOn"
|
||||
|
||||
#define ril_radio_settings_cbd_free g_free
|
||||
|
||||
static inline struct ril_radio_settings *ril_radio_settings_get_data(
|
||||
struct ofono_radio_settings *rs)
|
||||
{
|
||||
return ofono_radio_settings_get_data(rs);
|
||||
}
|
||||
|
||||
static struct ril_radio_settings_cbd *ril_radio_settings_cbd_new(
|
||||
struct ril_radio_settings *rsd, void *cb, void *data)
|
||||
{
|
||||
struct ril_radio_settings_cbd *cbd;
|
||||
|
||||
cbd = g_new0(struct ril_radio_settings_cbd, 1);
|
||||
cbd->rsd = rsd;
|
||||
cbd->cb.ptr = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static void ril_radio_settings_submit_request(struct ril_radio_settings *rsd,
|
||||
GRilIoRequest* req, guint code, GRilIoChannelResponseFunc response,
|
||||
void *cb, void *data)
|
||||
{
|
||||
grilio_queue_send_request_full(rsd->q, req, code, response,
|
||||
ril_radio_settings_cbd_free,
|
||||
ril_radio_settings_cbd_new(rsd, cb, data));
|
||||
}
|
||||
|
||||
static void ril_radio_settings_set_rat_mode_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_radio_settings_cbd *cbd = user_data;
|
||||
ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb.rat_mode_set;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
cb(ril_error_ok(&error), cbd->data);
|
||||
} else {
|
||||
ofono_error("rat mode setting failed");
|
||||
cb(ril_error_failure(&error), cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_radio_settings_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 ril_radio_settings *rsd = ril_radio_settings_get_data(rs);
|
||||
GRilIoRequest *req = grilio_request_sized_new(8);
|
||||
int pref = rsd->ratmode;
|
||||
|
||||
ofono_info("rat mode set %d", mode);
|
||||
switch (mode) {
|
||||
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_AUTO; /* per UI design */
|
||||
break;
|
||||
case OFONO_RADIO_ACCESS_MODE_LTE:
|
||||
pref = PREF_NET_TYPE_LTE_ONLY;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
grilio_request_append_int32(req, 1); /* Number of params */
|
||||
grilio_request_append_int32(req, pref);
|
||||
ril_radio_settings_submit_request(rsd, req,
|
||||
RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE,
|
||||
ril_radio_settings_set_rat_mode_cb, cb, data);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_radio_settings_force_rat(struct ril_radio_settings *rsd,
|
||||
int pref)
|
||||
{
|
||||
if (pref != rsd->ratmode) {
|
||||
GRilIoRequest *req = grilio_request_sized_new(8);
|
||||
DBG("pref ril rat mode %d, ril current %d", pref, rsd->ratmode);
|
||||
|
||||
grilio_request_append_int32(req, 1);
|
||||
grilio_request_append_int32(req, rsd->ratmode);
|
||||
grilio_queue_send_request(rsd->q, req,
|
||||
RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_radio_settings_query_rat_mode_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_radio_settings_cbd *cbd = user_data;
|
||||
ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb.rat_mode_query;
|
||||
|
||||
DBG("");
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
GRilIoParser rilp;
|
||||
int mode = OFONO_RADIO_ACCESS_MODE_ANY;
|
||||
int pref = -1;
|
||||
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
grilio_parser_get_int32(&rilp, NULL);
|
||||
grilio_parser_get_int32(&rilp, &pref);
|
||||
|
||||
switch (pref) {
|
||||
case PREF_NET_TYPE_LTE_ONLY:
|
||||
mode = OFONO_RADIO_ACCESS_MODE_LTE;
|
||||
case PREF_NET_TYPE_GSM_ONLY:
|
||||
mode = OFONO_RADIO_ACCESS_MODE_GSM;
|
||||
break;
|
||||
case PREF_NET_TYPE_GSM_WCDMA_AUTO:/* according to UI design */
|
||||
if (!cb) {
|
||||
ril_radio_settings_force_rat(cbd->rsd, pref);
|
||||
}
|
||||
case PREF_NET_TYPE_WCDMA:
|
||||
case PREF_NET_TYPE_GSM_WCDMA: /* according to UI design */
|
||||
mode = OFONO_RADIO_ACCESS_MODE_UMTS;
|
||||
break;
|
||||
case PREF_NET_TYPE_LTE_CDMA_EVDO:
|
||||
case PREF_NET_TYPE_LTE_GSM_WCDMA:
|
||||
case PREF_NET_TYPE_LTE_CMDA_EVDO_GSM_WCDMA:
|
||||
if (!cb) {
|
||||
ril_radio_settings_force_rat(cbd->rsd, pref);
|
||||
}
|
||||
break;
|
||||
case PREF_NET_TYPE_CDMA_EVDO_AUTO:
|
||||
case PREF_NET_TYPE_CDMA_ONLY:
|
||||
case PREF_NET_TYPE_EVDO_ONLY:
|
||||
case PREF_NET_TYPE_GSM_WCDMA_CDMA_EVDO_AUTO:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ofono_info("rat mode %d (ril %d)", mode, pref);
|
||||
if (cb) {
|
||||
cb(ril_error_ok(&error), mode, cbd->data);
|
||||
}
|
||||
} else {
|
||||
ofono_error("rat mode query failed");
|
||||
if (cb) {
|
||||
cb(ril_error_failure(&error), -1, cbd->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_radio_settings_query_rat_mode(struct ofono_radio_settings *rs,
|
||||
ofono_radio_settings_rat_mode_query_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_radio_settings *rsd = ril_radio_settings_get_data(rs);
|
||||
|
||||
ofono_info("rat mode query");
|
||||
ril_radio_settings_submit_request(rsd, NULL,
|
||||
RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE,
|
||||
ril_radio_settings_query_rat_mode_cb, cb, data);
|
||||
}
|
||||
|
||||
static gboolean ril_radio_settings_get_config(struct ril_radio_settings *rsd)
|
||||
{
|
||||
gboolean needsconfig = FALSE;
|
||||
gboolean value = FALSE;
|
||||
|
||||
/* Hmm... One file shared by all modems... Why?? */
|
||||
|
||||
GKeyFile *keyfile = storage_open(NULL, RIL_STORE);
|
||||
char **alreadyset = g_key_file_get_groups(keyfile, NULL);
|
||||
|
||||
if (alreadyset[0])
|
||||
value = g_key_file_get_boolean(
|
||||
keyfile, alreadyset[0], LTE_FLAG, NULL);
|
||||
else if (rsd->ratmode == PREF_NET_TYPE_GSM_WCDMA_AUTO)
|
||||
value = TRUE;
|
||||
|
||||
if (!value && rsd->ratmode == PREF_NET_TYPE_LTE_GSM_WCDMA) {
|
||||
g_key_file_set_boolean(keyfile,
|
||||
LTE_FLAG, LTE_FLAG, TRUE);
|
||||
needsconfig = TRUE;
|
||||
} else if (value && rsd->ratmode == PREF_NET_TYPE_GSM_WCDMA_AUTO) {
|
||||
g_key_file_set_boolean(keyfile,
|
||||
LTE_FLAG, LTE_FLAG, FALSE);
|
||||
needsconfig = TRUE;
|
||||
}
|
||||
|
||||
g_strfreev(alreadyset);
|
||||
storage_close(NULL, RIL_STORE, keyfile, TRUE);
|
||||
|
||||
DBG("needsconfig %d, rat mode %d", needsconfig, rsd->ratmode);
|
||||
return needsconfig;
|
||||
}
|
||||
|
||||
static gboolean ril_radio_settings_register(gpointer user_data)
|
||||
{
|
||||
struct ofono_radio_settings *rs = user_data;
|
||||
struct ril_radio_settings *rsd = ril_radio_settings_get_data(rs);
|
||||
|
||||
rsd->timer_id = 0;
|
||||
ofono_radio_settings_register(rs);
|
||||
|
||||
if (ril_radio_settings_get_config(rsd)) {
|
||||
ril_radio_settings_submit_request(rsd, NULL,
|
||||
RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE,
|
||||
ril_radio_settings_query_rat_mode_cb, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Single shot */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int ril_radio_settings_probe(struct ofono_radio_settings *rs,
|
||||
unsigned int vendor, void *data)
|
||||
{
|
||||
struct ril_modem *modem = data;
|
||||
struct ril_radio_settings *rsd = g_new0(struct ril_radio_settings, 1);
|
||||
|
||||
DBG("");
|
||||
rsd->q = grilio_queue_new(ril_modem_io(modem));
|
||||
rsd->ratmode = ril_modem_4g_enabled(modem) ?
|
||||
PREF_NET_TYPE_LTE_GSM_WCDMA :
|
||||
PREF_NET_TYPE_GSM_WCDMA_AUTO;
|
||||
|
||||
rsd->timer_id = g_idle_add(ril_radio_settings_register, rs);
|
||||
ofono_radio_settings_set_data(rs, rsd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ril_radio_settings_remove(struct ofono_radio_settings *rs)
|
||||
{
|
||||
struct ril_radio_settings *rd = ril_radio_settings_get_data(rs);
|
||||
|
||||
DBG("");
|
||||
ofono_radio_settings_set_data(rs, NULL);
|
||||
if (rd->timer_id > 0) {
|
||||
g_source_remove(rd->timer_id);
|
||||
}
|
||||
|
||||
grilio_queue_cancel_all(rd->q, FALSE);
|
||||
grilio_queue_unref(rd->q);
|
||||
g_free(rd);
|
||||
}
|
||||
|
||||
const struct ofono_radio_settings_driver ril_radio_settings_driver = {
|
||||
.name = RILMODEM_DRIVER,
|
||||
.probe = ril_radio_settings_probe,
|
||||
.remove = ril_radio_settings_remove,
|
||||
.query_rat_mode = ril_radio_settings_query_rat_mode,
|
||||
.set_rat_mode = ril_radio_settings_set_rat_mode,
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
1230
ofono/drivers/ril/ril_sim.c
Normal file
1230
ofono/drivers/ril/ril_sim.c
Normal file
File diff suppressed because it is too large
Load Diff
243
ofono/drivers/ril/ril_sim_dbus.c
Normal file
243
ofono/drivers/ril/ril_sim_dbus.c
Normal file
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_log.h"
|
||||
|
||||
#include <ofono/log.h>
|
||||
#include <ofono/dbus.h>
|
||||
|
||||
#include <gdbus.h>
|
||||
|
||||
#include "ofono.h"
|
||||
#include "storage.h"
|
||||
|
||||
struct ril_sim_dbus {
|
||||
char *path;
|
||||
char *imsi;
|
||||
char *name;
|
||||
char *default_name;
|
||||
gboolean enable_4g;
|
||||
GKeyFile *storage;
|
||||
DBusConnection *conn;
|
||||
struct ril_modem *md;
|
||||
};
|
||||
|
||||
#define RIL_SIM_STORE "ril"
|
||||
#define RIL_SIM_STORE_GROUP "Settings"
|
||||
#define RIL_SIM_STORE_ENABLE_4G "Enable4G"
|
||||
#define RIL_SIM_STORE_DISPLAY_NAME "DisplayName"
|
||||
|
||||
#define RIL_SIM_DBUS_INTERFACE "org.nemomobile.ofono.SimSettings"
|
||||
#define RIL_SIM_DBUS_INTERFACE_VERSION (1)
|
||||
|
||||
#define RIL_SIM_DBUS_DISPLAY_NAME_CHANGED_SIGNAL "DisplayNameChanged"
|
||||
#define RIL_SIM_DBUS_ENABLE_4G_CHANGED_SIGNAL "Enable4GChanged"
|
||||
|
||||
static DBusMessage *ril_sim_dbus_get_all(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
struct ril_sim_dbus *dbus = data;
|
||||
DBusMessage *reply = dbus_message_new_method_return(msg);
|
||||
dbus_int32_t version = RIL_SIM_DBUS_INTERFACE_VERSION;
|
||||
dbus_bool_t enable_4g = dbus->enable_4g;
|
||||
DBusMessageIter iter;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &version);
|
||||
dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &enable_4g);
|
||||
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &dbus->name);
|
||||
return reply;
|
||||
}
|
||||
|
||||
static DBusMessage *ril_sim_dbus_get_interface_version(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
DBusMessage *reply = dbus_message_new_method_return(msg);
|
||||
dbus_int32_t version = RIL_SIM_DBUS_INTERFACE_VERSION;
|
||||
DBusMessageIter iter;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &version);
|
||||
return reply;
|
||||
}
|
||||
|
||||
static DBusMessage *ril_sim_dbus_get_enable_4g(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
struct ril_sim_dbus *dbus = data;
|
||||
DBusMessage *reply = dbus_message_new_method_return(msg);
|
||||
dbus_bool_t enable_4g = dbus->enable_4g;
|
||||
DBusMessageIter iter;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &enable_4g);
|
||||
return reply;
|
||||
}
|
||||
|
||||
static DBusMessage *ril_sim_dbus_get_display_name(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
struct ril_sim_dbus *dbus = data;
|
||||
DBusMessage *reply = dbus_message_new_method_return(msg);
|
||||
DBusMessageIter iter;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &dbus->name);
|
||||
return reply;
|
||||
}
|
||||
|
||||
static void ril_sim_dbus_update_display_name(struct ril_sim_dbus *dbus,
|
||||
const char *name)
|
||||
{
|
||||
if (g_strcmp0(dbus->name, name)) {
|
||||
g_free(dbus->name);
|
||||
dbus->name = g_strdup(name);
|
||||
g_key_file_set_string(dbus->storage, RIL_SIM_STORE_GROUP,
|
||||
RIL_SIM_STORE_DISPLAY_NAME, name);
|
||||
storage_sync(dbus->imsi, RIL_SIM_STORE, dbus->storage);
|
||||
g_dbus_emit_signal(dbus->conn, dbus->path,
|
||||
RIL_SIM_DBUS_INTERFACE,
|
||||
RIL_SIM_DBUS_DISPLAY_NAME_CHANGED_SIGNAL,
|
||||
DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
static DBusMessage *ril_sim_dbus_set_display_name(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
DBusMessageIter iter;
|
||||
|
||||
dbus_message_iter_init(msg, &iter);
|
||||
if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
|
||||
struct ril_sim_dbus *dbus = data;
|
||||
DBusBasicValue value;
|
||||
const char *name;
|
||||
|
||||
dbus_message_iter_get_basic(&iter, &value);
|
||||
name = value.str;
|
||||
if (!name || !name[0]) name = dbus->default_name;
|
||||
ril_sim_dbus_update_display_name(dbus, name);
|
||||
return dbus_message_new_method_return(msg);
|
||||
} else {
|
||||
return __ofono_error_invalid_args(msg);
|
||||
}
|
||||
}
|
||||
|
||||
static const GDBusMethodTable ril_sim_dbus_methods[] = {
|
||||
{ GDBUS_METHOD("GetAll",
|
||||
NULL, GDBUS_ARGS({ "settings", "ibs" }),
|
||||
ril_sim_dbus_get_all) },
|
||||
{ GDBUS_METHOD("GetInterfaceVersion",
|
||||
NULL, GDBUS_ARGS({ "version", "i" }),
|
||||
ril_sim_dbus_get_interface_version) },
|
||||
{ GDBUS_METHOD("GetEnable4G",
|
||||
NULL, GDBUS_ARGS({ "enable", "b" }),
|
||||
ril_sim_dbus_get_enable_4g) },
|
||||
{ GDBUS_METHOD("GetDisplayName",
|
||||
NULL, GDBUS_ARGS({ "name", "s" }),
|
||||
ril_sim_dbus_get_display_name) },
|
||||
{ GDBUS_METHOD("SetDisplayName",
|
||||
GDBUS_ARGS({ "name", "s" }), NULL,
|
||||
ril_sim_dbus_set_display_name) },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const GDBusSignalTable ril_sim_dbus_signals[] = {
|
||||
{ GDBUS_SIGNAL(RIL_SIM_DBUS_DISPLAY_NAME_CHANGED_SIGNAL,
|
||||
GDBUS_ARGS({ "name", "s" })) },
|
||||
{ GDBUS_SIGNAL(RIL_SIM_DBUS_ENABLE_4G_CHANGED_SIGNAL,
|
||||
GDBUS_ARGS({ "enabled", "b" })) },
|
||||
{ }
|
||||
};
|
||||
|
||||
const char *ril_sim_dbus_imsi(struct ril_sim_dbus *dbus)
|
||||
{
|
||||
return dbus ? dbus->imsi : NULL;
|
||||
}
|
||||
|
||||
struct ril_sim_dbus *ril_sim_dbus_new(struct ril_modem *md)
|
||||
{
|
||||
const char *imsi = ofono_sim_get_imsi(ril_modem_ofono_sim(md));
|
||||
|
||||
if (imsi) {
|
||||
GError *error = NULL;
|
||||
const struct ril_modem_config *config= ril_modem_config(md);
|
||||
struct ril_sim_dbus *dbus = g_new0(struct ril_sim_dbus, 1);
|
||||
|
||||
DBG("%s", ril_modem_get_path(md));
|
||||
dbus->md = md;
|
||||
dbus->conn = dbus_connection_ref(ofono_dbus_get_connection());
|
||||
dbus->path = g_strdup(ril_modem_get_path(md));
|
||||
dbus->imsi = g_strdup(imsi);
|
||||
dbus->default_name = g_strdup(config->default_name);
|
||||
|
||||
/* Load settings */
|
||||
dbus->storage = storage_open(imsi, RIL_SIM_STORE);
|
||||
dbus->enable_4g = g_key_file_get_boolean(dbus->storage,
|
||||
RIL_SIM_STORE_GROUP, RIL_SIM_STORE_ENABLE_4G, &error);
|
||||
if (error) {
|
||||
dbus->enable_4g = config->enable_4g;
|
||||
g_error_free(error);
|
||||
error = NULL;
|
||||
}
|
||||
dbus->name = g_key_file_get_string(dbus->storage,
|
||||
RIL_SIM_STORE_GROUP, RIL_SIM_STORE_DISPLAY_NAME, NULL);
|
||||
if (!dbus->name) {
|
||||
dbus->name = g_strdup(config->default_name);
|
||||
GASSERT(dbus->name);
|
||||
}
|
||||
|
||||
/* Register D-Bus interface */
|
||||
if (g_dbus_register_interface(dbus->conn, dbus->path,
|
||||
RIL_SIM_DBUS_INTERFACE, ril_sim_dbus_methods,
|
||||
ril_sim_dbus_signals, NULL, dbus, NULL)) {
|
||||
ofono_modem_add_interface(ril_modem_ofono_modem(md),
|
||||
RIL_SIM_DBUS_INTERFACE);
|
||||
return dbus;
|
||||
} else {
|
||||
ofono_error("RIL D-Bus register failed");
|
||||
ril_sim_dbus_free(dbus);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ril_sim_dbus_free(struct ril_sim_dbus *dbus)
|
||||
{
|
||||
if (dbus) {
|
||||
DBG("%s", dbus->path);
|
||||
g_dbus_unregister_interface(dbus->conn, dbus->path,
|
||||
RIL_SIM_DBUS_INTERFACE);
|
||||
ofono_modem_remove_interface(ril_modem_ofono_modem(dbus->md),
|
||||
RIL_SIM_DBUS_INTERFACE);
|
||||
dbus_connection_unref(dbus->conn);
|
||||
g_key_file_free(dbus->storage);
|
||||
g_free(dbus->path);
|
||||
g_free(dbus->imsi);
|
||||
g_free(dbus->name);
|
||||
g_free(dbus->default_name);
|
||||
g_free(dbus);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
498
ofono/drivers/ril/ril_sms.c
Normal file
498
ofono/drivers/ril/ril_sms.c
Normal file
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_constants.h"
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
|
||||
#include "smsutil.h"
|
||||
#include "util.h"
|
||||
#include "simutil.h"
|
||||
|
||||
#define SIM_EFSMS_FILEID 0x6F3C
|
||||
#define EFSMS_LENGTH 176
|
||||
|
||||
#define TYPE_LOCAL 129
|
||||
#define TYPE_INTERNATIONAL 145
|
||||
|
||||
static unsigned char path[4] = {0x3F, 0x00, 0x7F, 0x10};
|
||||
|
||||
enum ril_sms_events {
|
||||
SMS_EVENT_NEW_SMS,
|
||||
SMS_EVENT_NEW_STATUS_REPORT,
|
||||
SMS_EVENT_NEW_SMS_ON_SIM,
|
||||
SMS_EVENT_COUNT
|
||||
};
|
||||
|
||||
struct ril_sms {
|
||||
GRilIoChannel *io;
|
||||
GRilIoQueue *q;
|
||||
struct ril_modem *modem;
|
||||
struct ofono_sms *sms;
|
||||
gulong event_id[SMS_EVENT_COUNT];
|
||||
guint timer_id;
|
||||
};
|
||||
|
||||
struct ril_sms_cbd {
|
||||
union _ofono_sms_cb {
|
||||
ofono_sms_sca_set_cb_t sca_set;
|
||||
ofono_sms_sca_query_cb_t sca_query;
|
||||
ofono_sms_submit_cb_t submit;
|
||||
gpointer ptr;
|
||||
} cb;
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
struct ril_sms_on_sim_req {
|
||||
struct ril_sms *sd;
|
||||
int record;
|
||||
};
|
||||
|
||||
#define ril_sms_cbd_free g_free
|
||||
#define ril_sms_on_sim_req_free g_free
|
||||
|
||||
static inline struct ril_sms *ril_sms_get_data(struct ofono_sms *sms)
|
||||
{
|
||||
return ofono_sms_get_data(sms);
|
||||
}
|
||||
|
||||
struct ril_sms_cbd *ril_sms_cbd_new(struct ril_sms *sd, void *cb, void *data)
|
||||
{
|
||||
struct ril_sms_cbd *cbd = g_new0(struct ril_sms_cbd, 1);
|
||||
|
||||
cbd->cb.ptr = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
struct ril_sms_on_sim_req *ril_sms_on_sim_req_new(struct ril_sms *sd, int rec)
|
||||
{
|
||||
struct ril_sms_on_sim_req *cbd = g_new0(struct ril_sms_on_sim_req, 1);
|
||||
|
||||
cbd->sd = sd;
|
||||
cbd->record = rec;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static void ril_sms_sca_set_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_sms_cbd *cbd = user_data;
|
||||
ofono_sms_sca_set_cb_t cb = cbd->cb.sca_set;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
cb(ril_error_ok(&error), cbd->data);
|
||||
} else {
|
||||
ofono_error("csca setting failed");
|
||||
cb(ril_error_failure(&error), cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_sms_sca_set(struct ofono_sms *sms,
|
||||
const struct ofono_phone_number *sca,
|
||||
ofono_sms_sca_set_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_sms *sd = ril_sms_get_data(sms);
|
||||
GRilIoRequest *req = grilio_request_new();
|
||||
char number[OFONO_MAX_PHONE_NUMBER_LENGTH + 4];
|
||||
|
||||
if (sca->type == TYPE_LOCAL) {
|
||||
snprintf(number, sizeof(number), "\"%s\"", sca->number);
|
||||
} else {
|
||||
snprintf(number, sizeof(number), "\"+%s\"", sca->number);
|
||||
}
|
||||
|
||||
DBG("Setting sca: %s", number);
|
||||
grilio_request_append_utf8(req, number);
|
||||
grilio_queue_send_request_full(sd->q, req,
|
||||
RIL_REQUEST_SET_SMSC_ADDRESS, ril_sms_sca_set_cb,
|
||||
ril_sms_cbd_free, ril_sms_cbd_new(sd, cb, data));
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_sms_sca_query_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_sms_cbd *cbd = user_data;
|
||||
ofono_sms_sca_query_cb_t cb = cbd->cb.sca_query;
|
||||
struct ofono_error error;
|
||||
GRilIoParser rilp;
|
||||
gchar *temp_buf;
|
||||
|
||||
if (status != RIL_E_SUCCESS) {
|
||||
ofono_error("csca query failed");
|
||||
cb(ril_error_failure(&error), NULL, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
temp_buf = grilio_parser_get_utf8(&rilp);
|
||||
|
||||
if (temp_buf) {
|
||||
/* RIL gives address in quotes */
|
||||
gchar *number = strtok(temp_buf, "\"");
|
||||
struct ofono_phone_number sca;
|
||||
|
||||
strncpy(sca.number, number, OFONO_MAX_PHONE_NUMBER_LENGTH);
|
||||
sca.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
|
||||
if (sca.number[0] == '+') {
|
||||
number = number + 1;
|
||||
sca.type = TYPE_INTERNATIONAL;
|
||||
} else {
|
||||
sca.type = TYPE_LOCAL;
|
||||
}
|
||||
|
||||
DBG("csca_query_cb: %s, %d", sca.number, sca.type);
|
||||
cb(ril_error_ok(&error), &sca, cbd->data);
|
||||
g_free(temp_buf);
|
||||
} else {
|
||||
ofono_error("return value invalid");
|
||||
cb(ril_error_failure(&error), NULL, cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_sms_sca_query(struct ofono_sms *sms,
|
||||
ofono_sms_sca_query_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_sms *sd = ril_sms_get_data(sms);
|
||||
|
||||
DBG("Sending csca_query");
|
||||
grilio_queue_send_request_full(sd->q, NULL,
|
||||
RIL_REQUEST_GET_SMSC_ADDRESS, ril_sms_sca_query_cb,
|
||||
ril_sms_cbd_free, ril_sms_cbd_new(sd, cb, data));
|
||||
}
|
||||
|
||||
static void ril_sms_submit_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_sms_cbd *cbd = user_data;
|
||||
ofono_sms_submit_cb_t cb = cbd->cb.submit;
|
||||
struct ofono_error error;
|
||||
int mr = 0;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
GRilIoParser rilp;
|
||||
int err = -1;
|
||||
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
|
||||
/* TP-Message-Reference for GSM/
|
||||
* BearerData MessageId for CDMA
|
||||
*/
|
||||
grilio_parser_get_int32(&rilp, &mr);
|
||||
grilio_parser_skip_string(&rilp);
|
||||
|
||||
/* error: 3GPP 27.005, 3.2.5, -1 if unknown or not applicable */
|
||||
grilio_parser_get_int32(&rilp, &err);
|
||||
DBG("sms msg ref: %d, error: %d", mr, err);
|
||||
ril_error_init_ok(&error);
|
||||
} else if (status == RIL_E_GENERIC_FAILURE) {
|
||||
ofono_info("not allowed by MO SMS control, do not retry");
|
||||
error.type = OFONO_ERROR_TYPE_CMS;
|
||||
error.error = 500;
|
||||
} else {
|
||||
ofono_error("sms sending failed, retry");
|
||||
ril_error_init_failure(&error);
|
||||
}
|
||||
|
||||
cb(&error, mr, cbd->data);
|
||||
}
|
||||
|
||||
static void ril_sms_submit(struct ofono_sms *sms, const unsigned char *pdu,
|
||||
int pdu_len, int tpdu_len, int mms,
|
||||
ofono_sms_submit_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_sms *sd = ril_sms_get_data(sms);
|
||||
GRilIoRequest *req = grilio_request_new();
|
||||
int smsc_len;
|
||||
char *tpdu;
|
||||
|
||||
DBG("pdu_len: %d, tpdu_len: %d mms: %d", pdu_len, tpdu_len, mms);
|
||||
|
||||
grilio_request_append_int32(req, 2); /* Number of strings */
|
||||
|
||||
/* SMSC address:
|
||||
*
|
||||
* smsc_len == 1, then zero-length SMSC was spec'd
|
||||
* RILD expects a NULL string in this case instead
|
||||
* of a zero-length string.
|
||||
*/
|
||||
smsc_len = pdu_len - tpdu_len;
|
||||
if (smsc_len > 1) {
|
||||
/* TODO: encode SMSC & write to parcel */
|
||||
DBG("SMSC address specified (smsc_len %d); NOT-IMPLEMENTED",
|
||||
smsc_len);
|
||||
}
|
||||
|
||||
grilio_request_append_utf8(req, NULL); /* default SMSC address */
|
||||
|
||||
/* TPDU:
|
||||
*
|
||||
* 'pdu' is a raw hexadecimal string
|
||||
* encode_hex() turns it into an ASCII/hex UTF8 buffer
|
||||
* grilio_request_append_utf8() encodes utf8 -> utf16
|
||||
*/
|
||||
tpdu = encode_hex(pdu + smsc_len, tpdu_len, 0);
|
||||
grilio_request_append_utf8(req, tpdu);
|
||||
|
||||
DBG("%s", tpdu);
|
||||
grilio_queue_send_request_full(sd->q, req,
|
||||
RIL_REQUEST_SEND_SMS, ril_sms_submit_cb,
|
||||
ril_sms_cbd_free, ril_sms_cbd_new(sd, cb, data));
|
||||
grilio_request_unref(req);
|
||||
g_free(tpdu);
|
||||
}
|
||||
|
||||
static void ril_ack_delivery_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
if (status != RIL_E_SUCCESS) {
|
||||
ofono_error("SMS acknowledgement failed: "
|
||||
"Further SMS reception is not guaranteed");
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_ack_delivery(struct ril_sms *sd, gboolean error)
|
||||
{
|
||||
GRilIoRequest *req = grilio_request_sized_new(12);
|
||||
const int code = (error ? 0 : 0xff);
|
||||
|
||||
DBG("(%d,%d)", error, code);
|
||||
grilio_request_append_int32(req, 2); /* Array size*/
|
||||
grilio_request_append_int32(req, error); /* Success (1)/Failure (0) */
|
||||
grilio_request_append_int32(req, code); /* error code */
|
||||
|
||||
/* ACK the incoming NEW_SMS */
|
||||
grilio_queue_send_request_full(sd->q, req,
|
||||
RIL_REQUEST_SMS_ACKNOWLEDGE, ril_ack_delivery_cb, NULL, NULL);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_sms_notify(GRilIoChannel *io, guint ril_event,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_sms *sd = user_data;
|
||||
GRilIoParser rilp;
|
||||
char *ril_pdu;
|
||||
int ril_pdu_len;
|
||||
unsigned int smsc_len;
|
||||
long ril_buf_len;
|
||||
guchar *ril_data;
|
||||
|
||||
ril_pdu = NULL;
|
||||
ril_data = NULL;
|
||||
|
||||
DBG("event: %d; data_len: %d", ril_event, len);
|
||||
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
ril_pdu = grilio_parser_get_utf8(&rilp);
|
||||
if (ril_pdu == NULL)
|
||||
goto error;
|
||||
|
||||
ril_pdu_len = strlen(ril_pdu);
|
||||
|
||||
DBG("ril_pdu_len is %d", ril_pdu_len);
|
||||
ril_data = decode_hex(ril_pdu, ril_pdu_len, &ril_buf_len, -1);
|
||||
if (ril_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 = ril_data[0] + 1;
|
||||
ofono_info("sms received, smsc_len is %d", smsc_len);
|
||||
DBG("(%s)", ril_pdu);
|
||||
|
||||
if (ril_event == RIL_UNSOL_RESPONSE_NEW_SMS) {
|
||||
/* Last parameter is 'tpdu_len' ( substract SMSC length ) */
|
||||
ofono_sms_deliver_notify(sd->sms, ril_data, ril_buf_len,
|
||||
ril_buf_len - smsc_len);
|
||||
} else {
|
||||
GASSERT(ril_event == RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT);
|
||||
ofono_sms_status_notify(sd->sms, ril_data, ril_buf_len,
|
||||
ril_buf_len - smsc_len);
|
||||
}
|
||||
|
||||
g_free(ril_pdu);
|
||||
g_free(ril_data);
|
||||
ril_ack_delivery(sd, TRUE);
|
||||
return;
|
||||
|
||||
error:
|
||||
g_free(ril_pdu);
|
||||
g_free(ril_data);
|
||||
ril_ack_delivery(sd, FALSE);
|
||||
ofono_error("Unable to parse NEW_SMS notification");
|
||||
}
|
||||
|
||||
static void ril_new_sms_on_sim_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
DBG("%d", status);
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
ofono_info("sms deleted from sim");
|
||||
} else {
|
||||
ofono_error("deleting sms from sim failed");
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_request_delete_sms_om_sim(struct ril_sms *sd, int record)
|
||||
{
|
||||
GRilIoRequest *req = grilio_request_sized_new(8);
|
||||
|
||||
DBG("Deleting record: %d", record);
|
||||
|
||||
grilio_request_append_int32(req, 1); /* Array length */
|
||||
grilio_request_append_int32(req, record);
|
||||
grilio_queue_send_request_full(sd->q, req,
|
||||
RIL_REQUEST_DELETE_SMS_ON_SIM,
|
||||
ril_new_sms_on_sim_cb, NULL, NULL);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_sms_on_sim_cb(const struct ofono_error *error,
|
||||
const unsigned char *sdata,
|
||||
int length, void *data)
|
||||
{
|
||||
struct ril_sms_on_sim_req *cbd = data;
|
||||
struct ril_sms *sd = cbd->sd;
|
||||
|
||||
/*
|
||||
* It seems when reading EFsms RIL returns the whole record including
|
||||
* the first status byte therefore we ignore that as we are only
|
||||
* interested of the following pdu
|
||||
*/
|
||||
/* 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.
|
||||
*/
|
||||
if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
unsigned int smsc_len = sdata[1] + 1;
|
||||
ofono_sms_deliver_notify(sd->sms, sdata + 1, length - 1,
|
||||
length - smsc_len - 1);
|
||||
ril_request_delete_sms_om_sim(sd, cbd->record);
|
||||
} else {
|
||||
ofono_error("cannot read sms from sim");
|
||||
}
|
||||
|
||||
ril_sms_on_sim_req_free(cbd);
|
||||
}
|
||||
|
||||
static void ril_sms_on_sim(GRilIoChannel *io, guint ril_event,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_sms *sd = user_data;
|
||||
struct ofono_sim *sim = ril_modem_ofono_sim(sd->modem);
|
||||
int data_len = 0, rec = 0;
|
||||
GRilIoParser rilp;
|
||||
|
||||
ofono_info("new sms on sim");
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
if (sim &&
|
||||
grilio_parser_get_int32(&rilp, &data_len) && data_len > 0 &&
|
||||
grilio_parser_get_int32(&rilp, &rec)) {
|
||||
DBG("rec %d", rec);
|
||||
ril_sim_read_file_linear(sim, SIM_EFSMS_FILEID, rec,
|
||||
EFSMS_LENGTH, path, sizeof(path),
|
||||
ril_sms_on_sim_cb,
|
||||
ril_sms_on_sim_req_new(sd,rec));
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean ril_sms_register(gpointer user_data)
|
||||
{
|
||||
struct ril_sms *sd = user_data;
|
||||
|
||||
DBG("");
|
||||
GASSERT(sd->timer_id);
|
||||
sd->timer_id = 0;
|
||||
ofono_sms_register(sd->sms);
|
||||
|
||||
/* Register event handlers */
|
||||
sd->event_id[SMS_EVENT_NEW_SMS] =
|
||||
grilio_channel_add_unsol_event_handler(sd->io, ril_sms_notify,
|
||||
RIL_UNSOL_RESPONSE_NEW_SMS, sd);
|
||||
sd->event_id[SMS_EVENT_NEW_STATUS_REPORT] =
|
||||
grilio_channel_add_unsol_event_handler(sd->io, ril_sms_notify,
|
||||
RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT, sd);
|
||||
sd->event_id[SMS_EVENT_NEW_SMS_ON_SIM] =
|
||||
grilio_channel_add_unsol_event_handler(sd->io, ril_sms_on_sim,
|
||||
RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM, sd);
|
||||
|
||||
/* Single-shot */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int ril_sms_probe(struct ofono_sms *sms, unsigned int vendor,
|
||||
void *data)
|
||||
{
|
||||
struct ril_modem *modem = data;
|
||||
struct ril_sms *sd = g_new0(struct ril_sms, 1);
|
||||
|
||||
sd->modem = modem;
|
||||
sd->sms = sms;
|
||||
sd->io = grilio_channel_ref(ril_modem_io(modem));
|
||||
sd->q = grilio_queue_new(sd->io);
|
||||
sd->timer_id = g_idle_add(ril_sms_register, sd);
|
||||
ofono_sms_set_data(sms, sd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ril_sms_remove(struct ofono_sms *sms)
|
||||
{
|
||||
int i;
|
||||
struct ril_sms *sd = ril_sms_get_data(sms);
|
||||
|
||||
DBG("");
|
||||
ofono_sms_set_data(sms, NULL);
|
||||
|
||||
for (i=0; i<G_N_ELEMENTS(sd->event_id); i++) {
|
||||
grilio_channel_remove_handler(sd->io, sd->event_id[i]);
|
||||
|
||||
}
|
||||
|
||||
if (sd->timer_id > 0) {
|
||||
g_source_remove(sd->timer_id);
|
||||
}
|
||||
|
||||
grilio_channel_unref(sd->io);
|
||||
grilio_queue_cancel_all(sd->q, FALSE);
|
||||
grilio_queue_unref(sd->q);
|
||||
g_free(sd);
|
||||
}
|
||||
|
||||
const struct ofono_sms_driver ril_sms_driver = {
|
||||
.name = RILMODEM_DRIVER,
|
||||
.probe = ril_sms_probe,
|
||||
.remove = ril_sms_remove,
|
||||
.sca_query = ril_sms_sca_query,
|
||||
.sca_set = ril_sms_sca_set,
|
||||
.submit = ril_sms_submit,
|
||||
.bearer_query = NULL, /* FIXME: needs investigation. */
|
||||
.bearer_set = NULL
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
303
ofono/drivers/ril/ril_stk.c
Normal file
303
ofono/drivers/ril/ril_stk.c
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
#include "ril_constants.h"
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#ifndef UI_LANG
|
||||
# define UI_LANG "/var/lib/environment/nemo/locale.conf"
|
||||
#endif
|
||||
|
||||
enum ril_stk_events {
|
||||
STK_EVENT_PROACTIVE_COMMAND,
|
||||
STK_EVENT_SESSION_END,
|
||||
STK_EVENT_NOTIFY,
|
||||
STK_EVENT_COUNT
|
||||
};
|
||||
|
||||
struct ril_stk {
|
||||
struct ofono_stk *stk;
|
||||
GRilIoChannel *io;
|
||||
GRilIoQueue *q;
|
||||
gulong event_id[STK_EVENT_COUNT];
|
||||
};
|
||||
|
||||
struct ril_stk_cbd {
|
||||
union _ofono_stk_cb {
|
||||
ofono_stk_envelope_cb_t envelope;
|
||||
ofono_stk_generic_cb_t generic;
|
||||
gpointer ptr;
|
||||
} cb;
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
#define ril_stk_cbd_free g_free
|
||||
|
||||
static inline struct ril_stk *ril_stk_get_data(struct ofono_stk *stk)
|
||||
{
|
||||
return ofono_stk_get_data(stk);
|
||||
}
|
||||
|
||||
struct ril_stk_cbd *ril_stk_cbd_new(void *cb, void *data)
|
||||
{
|
||||
struct ril_stk_cbd *cbd = g_new0(struct ril_stk_cbd, 1);
|
||||
|
||||
cbd->cb.ptr = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static void ril_stk_envelope_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_stk_cbd *cbd = user_data;
|
||||
ofono_stk_envelope_cb_t cb = cbd->cb.envelope;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
DBG("%u bytes(s)", len);
|
||||
cb(ril_error_ok(&error), NULL, 0, cbd->data);
|
||||
} else {
|
||||
DBG("Envelope reply failure: %s", ril_error_to_string(status));
|
||||
cb(ril_error_failure(&error), NULL, 0, cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_stk_envelope(struct ofono_stk *stk, int length,
|
||||
const unsigned char *cmd, ofono_stk_envelope_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_stk *sd = ril_stk_get_data(stk);
|
||||
GRilIoRequest *req = grilio_request_new();
|
||||
char *hex_envelope = encode_hex(cmd, length, 0);
|
||||
|
||||
DBG("%s", hex_envelope);
|
||||
grilio_request_append_utf8(req, hex_envelope);
|
||||
g_free(hex_envelope);
|
||||
grilio_queue_send_request_full(sd->q, req,
|
||||
RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND,
|
||||
ril_stk_envelope_cb, ril_stk_cbd_free,
|
||||
ril_stk_cbd_new(cb, data));
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_stk_terminal_response_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_stk_cbd *cbd = user_data;
|
||||
ofono_stk_generic_cb_t cb = cbd->cb.generic;
|
||||
|
||||
DBG("");
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
cb(ril_error_ok(&error), cbd->data);
|
||||
} else {
|
||||
ofono_error("Error in sending terminal response");
|
||||
cb(ril_error_failure(&error), cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_stk_terminal_response(struct ofono_stk *stk, int length,
|
||||
const unsigned char *resp,
|
||||
ofono_stk_generic_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_stk *sd = ril_stk_get_data(stk);
|
||||
GRilIoRequest *req = grilio_request_new();
|
||||
char *hex_tr = encode_hex(resp, length, 0);
|
||||
|
||||
DBG("rilmodem terminal response: %s", hex_tr);
|
||||
grilio_request_append_utf8(req, hex_tr);
|
||||
g_free(hex_tr);
|
||||
grilio_queue_send_request_full(sd->q, req,
|
||||
RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE,
|
||||
ril_stk_terminal_response_cb,
|
||||
ril_stk_cbd_free, ril_stk_cbd_new(cb, data));
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_stk_user_confirmation(struct ofono_stk *stk,
|
||||
ofono_bool_t confirm)
|
||||
{
|
||||
struct ril_stk *sd = ril_stk_get_data(stk);
|
||||
GRilIoRequest *req = grilio_request_sized_new(8);
|
||||
|
||||
DBG("%d", confirm);
|
||||
grilio_request_append_int32(req, 1); /* size of array */
|
||||
grilio_request_append_int32(req, confirm); /* yes/no */
|
||||
|
||||
grilio_queue_send_request(sd->q, req,
|
||||
RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_stk_pcmd_notify(GRilIoChannel *io, guint code,
|
||||
const void *data, guint data_len, void *user_data)
|
||||
{
|
||||
struct ril_stk *sd = user_data;
|
||||
GRilIoParser rilp;
|
||||
char *pcmd;
|
||||
guchar *pdu;
|
||||
long len = 0;
|
||||
|
||||
GASSERT(code == RIL_UNSOL_STK_PROACTIVE_COMMAND);
|
||||
grilio_parser_init(&rilp, data, data_len);
|
||||
pcmd = grilio_parser_get_utf8(&rilp);
|
||||
DBG("pcmd: %s", pcmd);
|
||||
|
||||
pdu = decode_hex(pcmd, strlen(pcmd), &len, -1);
|
||||
g_free(pcmd);
|
||||
|
||||
ofono_stk_proactive_command_notify(sd->stk, len, pdu);
|
||||
g_free(pdu);
|
||||
}
|
||||
|
||||
static void ril_stk_event_notify(GRilIoChannel *io, guint code,
|
||||
const void *data, guint data_len, void *user_data)
|
||||
{
|
||||
struct ril_stk *sd = user_data;
|
||||
GRilIoParser rilp;
|
||||
char *pcmd = NULL;
|
||||
guchar *pdu = NULL;
|
||||
long len;
|
||||
|
||||
/* Proactive command has been handled by the modem. */
|
||||
GASSERT(code == RIL_UNSOL_STK_EVENT_NOTIFY);
|
||||
grilio_parser_init(&rilp, data, data_len);
|
||||
pcmd = grilio_parser_get_utf8(&rilp);
|
||||
DBG("pcmd: %s", pcmd);
|
||||
pdu = decode_hex(pcmd, strlen(pcmd), &len, -1);
|
||||
g_free(pcmd);
|
||||
|
||||
ofono_stk_proactive_command_handled_notify(sd->stk, len, pdu);
|
||||
g_free(pdu);
|
||||
}
|
||||
|
||||
static void ril_stk_session_end_notify(GRilIoChannel *io, guint code,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_stk *sd = user_data;
|
||||
|
||||
DBG("");
|
||||
GASSERT(code == RIL_UNSOL_STK_SESSION_END);
|
||||
ofono_stk_proactive_session_end_notify(sd->stk);
|
||||
}
|
||||
|
||||
static void ril_stk_agent_ready(struct ofono_stk *stk)
|
||||
{
|
||||
struct ril_stk *sd = ril_stk_get_data(stk);
|
||||
|
||||
DBG("");
|
||||
if (!sd->event_id[STK_EVENT_PROACTIVE_COMMAND]) {
|
||||
DBG("Subscribing notifications");
|
||||
sd->event_id[STK_EVENT_PROACTIVE_COMMAND] =
|
||||
grilio_channel_add_unsol_event_handler(sd->io,
|
||||
ril_stk_pcmd_notify,
|
||||
RIL_UNSOL_STK_PROACTIVE_COMMAND, sd);
|
||||
|
||||
GASSERT(!sd->event_id[STK_EVENT_SESSION_END]);
|
||||
sd->event_id[STK_EVENT_SESSION_END] =
|
||||
grilio_channel_add_unsol_event_handler(sd->io,
|
||||
ril_stk_session_end_notify,
|
||||
RIL_UNSOL_STK_SESSION_END, sd);
|
||||
|
||||
GASSERT(!sd->event_id[STK_EVENT_NOTIFY]);
|
||||
sd->event_id[STK_EVENT_NOTIFY] =
|
||||
grilio_channel_add_unsol_event_handler(sd->io,
|
||||
ril_stk_event_notify,
|
||||
RIL_UNSOL_STK_EVENT_NOTIFY, sd);
|
||||
|
||||
grilio_queue_send_request(sd->q, NULL,
|
||||
RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_stk_set_lang()
|
||||
{
|
||||
GError *error = NULL;
|
||||
GIOChannel* chan = g_io_channel_new_file(UI_LANG, "r", &error);
|
||||
if (chan) {
|
||||
GString* buf = g_string_new(NULL);
|
||||
gsize term;
|
||||
while (g_io_channel_read_line_string(chan, buf, &term, NULL) ==
|
||||
G_IO_STATUS_NORMAL) {
|
||||
char* lang;
|
||||
g_string_set_size(buf, term);
|
||||
lang = strstr(buf->str, "LANG=");
|
||||
if (lang) {
|
||||
setenv("LANG", lang + 5, TRUE);
|
||||
}
|
||||
}
|
||||
g_string_free(buf, TRUE);
|
||||
g_io_channel_unref(chan);
|
||||
} else {
|
||||
DBG("%s: %s", UI_LANG, error->message);
|
||||
g_error_free(error);
|
||||
}
|
||||
}
|
||||
|
||||
static int ril_stk_probe(struct ofono_stk *stk, unsigned int vendor, void *data)
|
||||
{
|
||||
struct ril_modem *modem = data;
|
||||
struct ril_stk *sd = g_new0(struct ril_stk, 1);
|
||||
|
||||
DBG("");
|
||||
sd->stk = stk;
|
||||
sd->io = grilio_channel_ref(ril_modem_io(modem));
|
||||
sd->q = grilio_queue_new(sd->io);
|
||||
|
||||
ofono_stk_set_data(stk, sd);
|
||||
ofono_stk_register(stk);
|
||||
ril_stk_set_lang();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ril_stk_remove(struct ofono_stk *stk)
|
||||
{
|
||||
struct ril_stk *sd = ril_stk_get_data(stk);
|
||||
int i;
|
||||
|
||||
DBG("");
|
||||
ofono_stk_set_data(stk, NULL);
|
||||
|
||||
for (i=0; i<G_N_ELEMENTS(sd->event_id); i++) {
|
||||
grilio_channel_remove_handler(sd->io, sd->event_id[i]);
|
||||
}
|
||||
|
||||
grilio_channel_unref(sd->io);
|
||||
grilio_queue_cancel_all(sd->q, FALSE);
|
||||
grilio_queue_unref(sd->q);
|
||||
g_free(sd);
|
||||
}
|
||||
|
||||
const struct ofono_stk_driver ril_stk_driver = {
|
||||
.name = RILMODEM_DRIVER,
|
||||
.probe = ril_stk_probe,
|
||||
.remove = ril_stk_remove,
|
||||
.envelope = ril_stk_envelope,
|
||||
.terminal_response = ril_stk_terminal_response,
|
||||
.user_confirmation = ril_stk_user_confirmation,
|
||||
.ready = ril_stk_agent_ready
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
41
ofono/drivers/ril/ril_types.h
Normal file
41
ofono/drivers/ril/ril_types.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#ifndef RIL_TYPES_H
|
||||
#define RIL_TYPES_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <grilio_types.h>
|
||||
#include <gutil_macros.h>
|
||||
#include <ofono/types.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#endif /* RIL_TYPES_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
224
ofono/drivers/ril/ril_ussd.c
Normal file
224
ofono/drivers/ril/ril_ussd.c
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
#include "ril_constants.h"
|
||||
|
||||
#include "smsutil.h"
|
||||
#include "util.h"
|
||||
|
||||
struct ril_ussd {
|
||||
struct ofono_ussd *ussd;
|
||||
GRilIoChannel *io;
|
||||
GRilIoQueue *q;
|
||||
guint timer_id;
|
||||
gulong event_id;
|
||||
};
|
||||
|
||||
struct ril_ussd_cbd {
|
||||
ofono_ussd_cb_t cb;
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
#define ril_ussd_cbd_free g_free
|
||||
|
||||
static inline struct ril_ussd *ril_ussd_get_data(struct ofono_ussd *ussd)
|
||||
{
|
||||
return ofono_ussd_get_data(ussd);
|
||||
}
|
||||
|
||||
static struct ril_ussd_cbd *ril_ussd_cbd_new(ofono_ussd_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_ussd_cbd *cbd = g_new0(struct ril_ussd_cbd, 1);
|
||||
|
||||
cbd->cb = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static void ril_ussd_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct ril_ussd_cbd *cbd = user_data;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
cbd->cb(ril_error_ok(&error), cbd->data);
|
||||
} else {
|
||||
cbd->cb(ril_error_failure(&error), cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
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 ofono_error error;
|
||||
enum sms_charset charset;
|
||||
struct ril_ussd *ud = ril_ussd_get_data(ussd);
|
||||
|
||||
ofono_info("send ussd, len:%d", len);
|
||||
if (cbs_dcs_decode(dcs, NULL, NULL, &charset, NULL, NULL, NULL)) {
|
||||
if (charset == SMS_CHARSET_7BIT) {
|
||||
unsigned char unpacked_buf[182];
|
||||
long written = 0;
|
||||
|
||||
unpack_7bit_own_buf(pdu, len, 0, TRUE,
|
||||
sizeof(unpacked_buf)-1, &written, 0,
|
||||
unpacked_buf);
|
||||
|
||||
unpacked_buf[written] = 0;
|
||||
if (written >= 1) {
|
||||
/*
|
||||
* When USSD was packed, additional CR
|
||||
* might have been added (according to
|
||||
* 23.038 6.1.2.3.1). So if the last
|
||||
* character is CR, it should be removed
|
||||
* here.
|
||||
*
|
||||
* Over 2 characters long USSD string must
|
||||
* end with # (checked in valid_ussd_string),
|
||||
* so it should be safe to remove extra CR.
|
||||
*/
|
||||
GRilIoRequest *req = grilio_request_new();
|
||||
int length = strlen((char *)unpacked_buf);
|
||||
while (length > 2 &&
|
||||
unpacked_buf[length-1] == '\r') {
|
||||
unpacked_buf[--length] = 0;
|
||||
}
|
||||
grilio_request_append_utf8_chars(req, (char*)
|
||||
unpacked_buf, length);
|
||||
grilio_queue_send_request_full(ud->q, req,
|
||||
RIL_REQUEST_SEND_USSD, ril_ussd_cb,
|
||||
ril_ussd_cbd_free,
|
||||
ril_ussd_cbd_new(cb, data));
|
||||
grilio_request_unref(req);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cb(ril_error_failure(&error), data);
|
||||
}
|
||||
|
||||
static void ril_ussd_cancel(struct ofono_ussd *ussd,
|
||||
ofono_ussd_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_ussd *ud = ril_ussd_get_data(ussd);
|
||||
|
||||
ofono_info("send ussd cancel");
|
||||
grilio_queue_send_request_full(ud->q, NULL, RIL_REQUEST_CANCEL_USSD,
|
||||
ril_ussd_cb, ril_ussd_cbd_free, ril_ussd_cbd_new(cb, data));
|
||||
}
|
||||
|
||||
static void ril_ussd_notify(GRilIoChannel *io, guint code,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_ussd *ud = user_data;
|
||||
GRilIoParser rilp;
|
||||
char *ussd_from_network = NULL;
|
||||
char *type = NULL;
|
||||
int ussdtype = 0;
|
||||
|
||||
ofono_info("ussd_received");
|
||||
|
||||
GASSERT(code == RIL_UNSOL_ON_USSD);
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
grilio_parser_get_uint32(&rilp, NULL);
|
||||
type = grilio_parser_get_utf8(&rilp);
|
||||
ussd_from_network = grilio_parser_get_utf8(&rilp);
|
||||
|
||||
ussdtype = g_ascii_xdigit_value(*type);
|
||||
|
||||
if (ussd_from_network) {
|
||||
const int data_len = strlen(ussd_from_network);
|
||||
DBG("ussd_received, length %d", data_len);
|
||||
ofono_ussd_notify(ud->ussd, ussdtype, 0xFF,
|
||||
(const unsigned char *) ussd_from_network, data_len);
|
||||
} else {
|
||||
ofono_ussd_notify(ud->ussd, ussdtype, 0, NULL, 0);
|
||||
}
|
||||
|
||||
/* ussd_from_network not freed because core does that if dcs is 0xFF */
|
||||
g_free(type);
|
||||
return;
|
||||
}
|
||||
|
||||
static gboolean ril_ussd_register(gpointer user_data)
|
||||
{
|
||||
struct ril_ussd *ud = user_data;
|
||||
|
||||
DBG("");
|
||||
GASSERT(ud->timer_id);
|
||||
ud->timer_id = 0;
|
||||
ofono_ussd_register(ud->ussd);
|
||||
|
||||
/* Register for USSD events */
|
||||
ud->event_id = grilio_channel_add_unsol_event_handler(ud->io,
|
||||
ril_ussd_notify, RIL_UNSOL_ON_USSD, ud);
|
||||
|
||||
/* Single-shot */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int ril_ussd_probe(struct ofono_ussd *ussd, unsigned int vendor,
|
||||
void *data)
|
||||
{
|
||||
struct ril_modem *modem = data;
|
||||
struct ril_ussd *ud = g_try_new0(struct ril_ussd, 1);
|
||||
|
||||
DBG("");
|
||||
ud->ussd = ussd;
|
||||
ud->io = grilio_channel_ref(ril_modem_io(modem));
|
||||
ud->q = grilio_queue_new(ud->io);
|
||||
ud->timer_id = g_idle_add(ril_ussd_register, ud);
|
||||
ofono_ussd_set_data(ussd, ud);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ril_ussd_remove(struct ofono_ussd *ussd)
|
||||
{
|
||||
struct ril_ussd *ud = ril_ussd_get_data(ussd);
|
||||
|
||||
DBG("");
|
||||
ofono_ussd_set_data(ussd, NULL);
|
||||
|
||||
if (ud->timer_id > 0) {
|
||||
g_source_remove(ud->timer_id);
|
||||
}
|
||||
|
||||
grilio_channel_remove_handler(ud->io, ud->event_id);
|
||||
grilio_channel_unref(ud->io);
|
||||
grilio_queue_cancel_all(ud->q, FALSE);
|
||||
grilio_queue_unref(ud->q);
|
||||
g_free(ud);
|
||||
}
|
||||
|
||||
const struct ofono_ussd_driver ril_ussd_driver = {
|
||||
.name = RILMODEM_DRIVER,
|
||||
.probe = ril_ussd_probe,
|
||||
.remove = ril_ussd_remove,
|
||||
.request = ril_ussd_request,
|
||||
.cancel = ril_ussd_cancel
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
434
ofono/drivers/ril/ril_util.c
Normal file
434
ofono/drivers/ril/ril_util.c
Normal file
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
#include "ril_constants.h"
|
||||
|
||||
#include <grilio_channel.h>
|
||||
#include <grilio_parser.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "util.h"
|
||||
|
||||
const char *ril_error_to_string(int error)
|
||||
{
|
||||
#define RIL_E_(name) case RIL_E_##name: return #name
|
||||
#define GRILIO_E_(name) case GRILIO_STATUS_##name: return "GRILIO_" #name
|
||||
static char unknown[12];
|
||||
switch (error) {
|
||||
case RIL_E_SUCCESS: return "OK";
|
||||
GRILIO_E_(TIMEOUT);
|
||||
GRILIO_E_(CANCELLED);
|
||||
RIL_E_(RADIO_NOT_AVAILABLE);
|
||||
RIL_E_(GENERIC_FAILURE);
|
||||
RIL_E_(PASSWORD_INCORRECT);
|
||||
RIL_E_(SIM_PIN2);
|
||||
RIL_E_(SIM_PUK2);
|
||||
RIL_E_(REQUEST_NOT_SUPPORTED);
|
||||
RIL_E_(CANCELLED);
|
||||
RIL_E_(OP_NOT_ALLOWED_DURING_VOICE_CALL);
|
||||
RIL_E_(OP_NOT_ALLOWED_BEFORE_REG_TO_NW);
|
||||
RIL_E_(SMS_SEND_FAIL_RETRY);
|
||||
RIL_E_(SIM_ABSENT);
|
||||
RIL_E_(SUBSCRIPTION_NOT_AVAILABLE);
|
||||
RIL_E_(MODE_NOT_SUPPORTED);
|
||||
RIL_E_(FDN_CHECK_FAILURE);
|
||||
RIL_E_(ILLEGAL_SIM_OR_ME);
|
||||
RIL_E_(UNUSED);
|
||||
RIL_E_(DIAL_MODIFIED_TO_USSD);
|
||||
RIL_E_(DIAL_MODIFIED_TO_SS);
|
||||
RIL_E_(DIAL_MODIFIED_TO_DIAL);
|
||||
RIL_E_(USSD_MODIFIED_TO_DIAL);
|
||||
RIL_E_(USSD_MODIFIED_TO_SS);
|
||||
RIL_E_(USSD_MODIFIED_TO_USSD);
|
||||
RIL_E_(SS_MODIFIED_TO_DIAL);
|
||||
RIL_E_(SS_MODIFIED_TO_USSD);
|
||||
RIL_E_(SS_MODIFIED_TO_SS);
|
||||
RIL_E_(SUBSCRIPTION_NOT_SUPPORTED);
|
||||
RIL_E_(MISSING_RESOURCE);
|
||||
RIL_E_(NO_SUCH_ELEMENT);
|
||||
RIL_E_(INVALID_PARAMETER);
|
||||
default:
|
||||
snprintf(unknown, sizeof(unknown), "%d", error);
|
||||
return unknown;
|
||||
}
|
||||
}
|
||||
|
||||
const char *ril_request_to_string(guint request)
|
||||
{
|
||||
#define RIL_REQUEST_(name) case RIL_REQUEST_##name: return #name
|
||||
static char unknown[24];
|
||||
switch (request) {
|
||||
RIL_REQUEST_(GET_SIM_STATUS);
|
||||
RIL_REQUEST_(ENTER_SIM_PIN);
|
||||
RIL_REQUEST_(ENTER_SIM_PUK);
|
||||
RIL_REQUEST_(ENTER_SIM_PIN2);
|
||||
RIL_REQUEST_(ENTER_SIM_PUK2);
|
||||
RIL_REQUEST_(CHANGE_SIM_PIN);
|
||||
RIL_REQUEST_(CHANGE_SIM_PIN2);
|
||||
RIL_REQUEST_(ENTER_NETWORK_DEPERSONALIZATION);
|
||||
RIL_REQUEST_(GET_CURRENT_CALLS);
|
||||
RIL_REQUEST_(DIAL);
|
||||
RIL_REQUEST_(GET_IMSI);
|
||||
RIL_REQUEST_(HANGUP);
|
||||
RIL_REQUEST_(HANGUP_WAITING_OR_BACKGROUND);
|
||||
RIL_REQUEST_(HANGUP_FOREGROUND_RESUME_BACKGROUND);
|
||||
RIL_REQUEST_(SWITCH_HOLDING_AND_ACTIVE);
|
||||
RIL_REQUEST_(CONFERENCE);
|
||||
RIL_REQUEST_(UDUB);
|
||||
RIL_REQUEST_(LAST_CALL_FAIL_CAUSE);
|
||||
RIL_REQUEST_(SIGNAL_STRENGTH);
|
||||
RIL_REQUEST_(VOICE_REGISTRATION_STATE);
|
||||
RIL_REQUEST_(DATA_REGISTRATION_STATE);
|
||||
RIL_REQUEST_(OPERATOR);
|
||||
RIL_REQUEST_(RADIO_POWER);
|
||||
RIL_REQUEST_(DTMF);
|
||||
RIL_REQUEST_(SEND_SMS);
|
||||
RIL_REQUEST_(SEND_SMS_EXPECT_MORE);
|
||||
RIL_REQUEST_(SETUP_DATA_CALL);
|
||||
RIL_REQUEST_(SIM_IO);
|
||||
RIL_REQUEST_(SEND_USSD);
|
||||
RIL_REQUEST_(CANCEL_USSD);
|
||||
RIL_REQUEST_(GET_CLIR);
|
||||
RIL_REQUEST_(SET_CLIR);
|
||||
RIL_REQUEST_(QUERY_CALL_FORWARD_STATUS);
|
||||
RIL_REQUEST_(SET_CALL_FORWARD);
|
||||
RIL_REQUEST_(QUERY_CALL_WAITING);
|
||||
RIL_REQUEST_(SET_CALL_WAITING);
|
||||
RIL_REQUEST_(SMS_ACKNOWLEDGE);
|
||||
RIL_REQUEST_(GET_IMEI);
|
||||
RIL_REQUEST_(GET_IMEISV);
|
||||
RIL_REQUEST_(ANSWER);
|
||||
RIL_REQUEST_(DEACTIVATE_DATA_CALL);
|
||||
RIL_REQUEST_(QUERY_FACILITY_LOCK);
|
||||
RIL_REQUEST_(SET_FACILITY_LOCK);
|
||||
RIL_REQUEST_(CHANGE_BARRING_PASSWORD);
|
||||
RIL_REQUEST_(QUERY_NETWORK_SELECTION_MODE);
|
||||
RIL_REQUEST_(SET_NETWORK_SELECTION_AUTOMATIC);
|
||||
RIL_REQUEST_(SET_NETWORK_SELECTION_MANUAL);
|
||||
RIL_REQUEST_(QUERY_AVAILABLE_NETWORKS);
|
||||
RIL_REQUEST_(DTMF_START);
|
||||
RIL_REQUEST_(DTMF_STOP);
|
||||
RIL_REQUEST_(BASEBAND_VERSION);
|
||||
RIL_REQUEST_(SEPARATE_CONNECTION);
|
||||
RIL_REQUEST_(SET_MUTE);
|
||||
RIL_REQUEST_(GET_MUTE);
|
||||
RIL_REQUEST_(QUERY_CLIP);
|
||||
RIL_REQUEST_(LAST_DATA_CALL_FAIL_CAUSE);
|
||||
RIL_REQUEST_(DATA_CALL_LIST);
|
||||
RIL_REQUEST_(RESET_RADIO);
|
||||
RIL_REQUEST_(OEM_HOOK_RAW);
|
||||
RIL_REQUEST_(OEM_HOOK_STRINGS);
|
||||
RIL_REQUEST_(SCREEN_STATE);
|
||||
RIL_REQUEST_(SET_SUPP_SVC_NOTIFICATION);
|
||||
RIL_REQUEST_(WRITE_SMS_TO_SIM);
|
||||
RIL_REQUEST_(DELETE_SMS_ON_SIM);
|
||||
RIL_REQUEST_(SET_BAND_MODE);
|
||||
RIL_REQUEST_(QUERY_AVAILABLE_BAND_MODE);
|
||||
RIL_REQUEST_(STK_GET_PROFILE);
|
||||
RIL_REQUEST_(STK_SET_PROFILE);
|
||||
RIL_REQUEST_(STK_SEND_ENVELOPE_COMMAND);
|
||||
RIL_REQUEST_(STK_SEND_TERMINAL_RESPONSE);
|
||||
RIL_REQUEST_(STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM);
|
||||
RIL_REQUEST_(EXPLICIT_CALL_TRANSFER);
|
||||
RIL_REQUEST_(SET_PREFERRED_NETWORK_TYPE);
|
||||
RIL_REQUEST_(GET_PREFERRED_NETWORK_TYPE);
|
||||
RIL_REQUEST_(GET_NEIGHBORING_CELL_IDS);
|
||||
RIL_REQUEST_(SET_LOCATION_UPDATES);
|
||||
RIL_REQUEST_(CDMA_SET_SUBSCRIPTION_SOURCE);
|
||||
RIL_REQUEST_(CDMA_SET_ROAMING_PREFERENCE);
|
||||
RIL_REQUEST_(CDMA_QUERY_ROAMING_PREFERENCE);
|
||||
RIL_REQUEST_(SET_TTY_MODE);
|
||||
RIL_REQUEST_(QUERY_TTY_MODE);
|
||||
RIL_REQUEST_(CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE);
|
||||
RIL_REQUEST_(CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE);
|
||||
RIL_REQUEST_(CDMA_FLASH);
|
||||
RIL_REQUEST_(CDMA_BURST_DTMF);
|
||||
RIL_REQUEST_(CDMA_VALIDATE_AND_WRITE_AKEY);
|
||||
RIL_REQUEST_(CDMA_SEND_SMS);
|
||||
RIL_REQUEST_(CDMA_SMS_ACKNOWLEDGE);
|
||||
RIL_REQUEST_(GSM_GET_BROADCAST_SMS_CONFIG);
|
||||
RIL_REQUEST_(GSM_SET_BROADCAST_SMS_CONFIG);
|
||||
RIL_REQUEST_(GSM_SMS_BROADCAST_ACTIVATION);
|
||||
RIL_REQUEST_(CDMA_GET_BROADCAST_SMS_CONFIG);
|
||||
RIL_REQUEST_(CDMA_SET_BROADCAST_SMS_CONFIG);
|
||||
RIL_REQUEST_(CDMA_SMS_BROADCAST_ACTIVATION);
|
||||
RIL_REQUEST_(CDMA_SUBSCRIPTION);
|
||||
RIL_REQUEST_(CDMA_WRITE_SMS_TO_RUIM);
|
||||
RIL_REQUEST_(CDMA_DELETE_SMS_ON_RUIM);
|
||||
RIL_REQUEST_(DEVICE_IDENTITY);
|
||||
RIL_REQUEST_(EXIT_EMERGENCY_CALLBACK_MODE);
|
||||
RIL_REQUEST_(GET_SMSC_ADDRESS);
|
||||
RIL_REQUEST_(SET_SMSC_ADDRESS);
|
||||
RIL_REQUEST_(REPORT_SMS_MEMORY_STATUS);
|
||||
RIL_REQUEST_(REPORT_STK_SERVICE_IS_RUNNING);
|
||||
RIL_REQUEST_(CDMA_GET_SUBSCRIPTION_SOURCE);
|
||||
RIL_REQUEST_(ISIM_AUTHENTICATION);
|
||||
RIL_REQUEST_(ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU);
|
||||
RIL_REQUEST_(STK_SEND_ENVELOPE_WITH_STATUS);
|
||||
RIL_REQUEST_(VOICE_RADIO_TECH);
|
||||
RIL_REQUEST_(GET_CELL_INFO_LIST);
|
||||
RIL_REQUEST_(SET_UNSOL_CELL_INFO_LIST_RATE);
|
||||
RIL_REQUEST_(SET_INITIAL_ATTACH_APN);
|
||||
RIL_REQUEST_(IMS_REGISTRATION_STATE);
|
||||
RIL_REQUEST_(IMS_SEND_SMS);
|
||||
RIL_REQUEST_(SIM_TRANSMIT_APDU_BASIC);
|
||||
RIL_REQUEST_(SIM_OPEN_CHANNEL);
|
||||
RIL_REQUEST_(SIM_CLOSE_CHANNEL);
|
||||
RIL_REQUEST_(SIM_TRANSMIT_APDU_CHANNEL);
|
||||
RIL_REQUEST_(NV_READ_ITEM);
|
||||
RIL_REQUEST_(NV_WRITE_ITEM);
|
||||
RIL_REQUEST_(NV_WRITE_CDMA_PRL);
|
||||
RIL_REQUEST_(NV_RESET_CONFIG);
|
||||
RIL_REQUEST_(SET_UICC_SUBSCRIPTION);
|
||||
RIL_REQUEST_(ALLOW_DATA);
|
||||
RIL_REQUEST_(GET_HARDWARE_CONFIG);
|
||||
RIL_REQUEST_(SIM_AUTHENTICATION);
|
||||
RIL_REQUEST_(GET_DC_RT_INFO);
|
||||
RIL_REQUEST_(SET_DC_RT_INFO_RATE);
|
||||
RIL_REQUEST_(SET_DATA_PROFILE);
|
||||
RIL_REQUEST_(SHUTDOWN);
|
||||
RIL_REQUEST_(GET_RADIO_CAPABILITY);
|
||||
RIL_REQUEST_(SET_RADIO_CAPABILITY);
|
||||
default:
|
||||
snprintf(unknown, sizeof(unknown), "RIL_REQUEST_%d", request);
|
||||
return unknown;
|
||||
}
|
||||
}
|
||||
|
||||
const char *ril_unsol_event_to_string(guint event)
|
||||
{
|
||||
#define RIL_UNSOL_(name) case RIL_UNSOL_##name: return #name
|
||||
static char unknown[24];
|
||||
switch (event) {
|
||||
RIL_UNSOL_(RESPONSE_RADIO_STATE_CHANGED);
|
||||
RIL_UNSOL_(RESPONSE_CALL_STATE_CHANGED);
|
||||
RIL_UNSOL_(RESPONSE_VOICE_NETWORK_STATE_CHANGED);
|
||||
RIL_UNSOL_(RESPONSE_NEW_SMS);
|
||||
RIL_UNSOL_(RESPONSE_NEW_SMS_STATUS_REPORT);
|
||||
RIL_UNSOL_(RESPONSE_NEW_SMS_ON_SIM);
|
||||
RIL_UNSOL_(ON_USSD);
|
||||
RIL_UNSOL_(ON_USSD_REQUEST);
|
||||
RIL_UNSOL_(NITZ_TIME_RECEIVED);
|
||||
RIL_UNSOL_(SIGNAL_STRENGTH);
|
||||
RIL_UNSOL_(DATA_CALL_LIST_CHANGED);
|
||||
RIL_UNSOL_(SUPP_SVC_NOTIFICATION);
|
||||
RIL_UNSOL_(STK_SESSION_END);
|
||||
RIL_UNSOL_(STK_PROACTIVE_COMMAND);
|
||||
RIL_UNSOL_(STK_EVENT_NOTIFY);
|
||||
RIL_UNSOL_(STK_CALL_SETUP);
|
||||
RIL_UNSOL_(SIM_SMS_STORAGE_FULL);
|
||||
RIL_UNSOL_(SIM_REFRESH);
|
||||
RIL_UNSOL_(CALL_RING);
|
||||
RIL_UNSOL_(RESPONSE_SIM_STATUS_CHANGED);
|
||||
RIL_UNSOL_(RESPONSE_CDMA_NEW_SMS);
|
||||
RIL_UNSOL_(RESPONSE_NEW_BROADCAST_SMS);
|
||||
RIL_UNSOL_(CDMA_RUIM_SMS_STORAGE_FULL);
|
||||
RIL_UNSOL_(RESTRICTED_STATE_CHANGED);
|
||||
RIL_UNSOL_(ENTER_EMERGENCY_CALLBACK_MODE);
|
||||
RIL_UNSOL_(CDMA_CALL_WAITING);
|
||||
RIL_UNSOL_(CDMA_OTA_PROVISION_STATUS);
|
||||
RIL_UNSOL_(CDMA_INFO_REC);
|
||||
RIL_UNSOL_(OEM_HOOK_RAW);
|
||||
RIL_UNSOL_(RINGBACK_TONE);
|
||||
RIL_UNSOL_(RESEND_INCALL_MUTE);
|
||||
RIL_UNSOL_(CDMA_SUBSCRIPTION_SOURCE_CHANGED);
|
||||
RIL_UNSOL_(CDMA_PRL_CHANGED);
|
||||
RIL_UNSOL_(EXIT_EMERGENCY_CALLBACK_MODE);
|
||||
RIL_UNSOL_(RIL_CONNECTED);
|
||||
RIL_UNSOL_(VOICE_RADIO_TECH_CHANGED);
|
||||
RIL_UNSOL_(CELL_INFO_LIST);
|
||||
RIL_UNSOL_(RESPONSE_IMS_NETWORK_STATE_CHANGED);
|
||||
RIL_UNSOL_(UICC_SUBSCRIPTION_STATUS_CHANGED);
|
||||
RIL_UNSOL_(SRVCC_STATE_NOTIFY);
|
||||
RIL_UNSOL_(HARDWARE_CONFIG_CHANGED);
|
||||
RIL_UNSOL_(DC_RT_INFO_CHANGED);
|
||||
RIL_UNSOL_(RADIO_CAPABILITY);
|
||||
RIL_UNSOL_(ON_SS);
|
||||
RIL_UNSOL_(STK_CC_ALPHA_NOTIFY);
|
||||
default:
|
||||
snprintf(unknown, sizeof(unknown), "RIL_UNSOL_%d", event);
|
||||
return unknown;
|
||||
}
|
||||
}
|
||||
|
||||
const char *ril_radio_state_to_string(int radio_state)
|
||||
{
|
||||
#define RADIO_STATE_(name) case RADIO_STATE_##name: return #name
|
||||
static char unknown[16];
|
||||
switch (radio_state) {
|
||||
RADIO_STATE_(OFF);
|
||||
RADIO_STATE_(UNAVAILABLE);
|
||||
RADIO_STATE_(SIM_NOT_READY);
|
||||
RADIO_STATE_(SIM_LOCKED_OR_ABSENT);
|
||||
RADIO_STATE_(SIM_READY);
|
||||
RADIO_STATE_(RUIM_NOT_READY);
|
||||
RADIO_STATE_(RUIM_READY);
|
||||
RADIO_STATE_(RUIM_LOCKED_OR_ABSENT);
|
||||
RADIO_STATE_(NV_NOT_READY);
|
||||
RADIO_STATE_(NV_READY);
|
||||
RADIO_STATE_(ON);
|
||||
default:
|
||||
snprintf(unknown, sizeof(unknown), "%d (?)", radio_state);
|
||||
return unknown;
|
||||
}
|
||||
}
|
||||
|
||||
int ril_address_family(const char *addr)
|
||||
{
|
||||
if (strchr(addr, ':')) {
|
||||
return AF_INET6;
|
||||
} else if (strchr(addr, '.')) {
|
||||
return AF_INET;
|
||||
} else {
|
||||
return AF_UNSPEC;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean ril_util_parse_reg(const void *data, guint len,
|
||||
struct ril_reg_data *reg)
|
||||
{
|
||||
GRilIoParser rilp;
|
||||
int nparams;
|
||||
gchar *sstatus = NULL, *slac = NULL, *sci = NULL;
|
||||
gchar *stech = NULL, *sreason = NULL, *smax = NULL;
|
||||
|
||||
memset(reg, 0, sizeof(*reg));
|
||||
|
||||
/* Size of response string array
|
||||
*
|
||||
* Should be:
|
||||
* >= 4 for VOICE_REG reply
|
||||
* >= 5 for DATA_REG reply
|
||||
*/
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
if (!grilio_parser_get_int32(&rilp, &nparams) || nparams < 4) {
|
||||
DBG("broken response");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
sstatus = grilio_parser_get_utf8(&rilp);
|
||||
if (!sstatus) {
|
||||
DBG("No sstatus value returned!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
slac = grilio_parser_get_utf8(&rilp);
|
||||
sci = grilio_parser_get_utf8(&rilp);
|
||||
stech = grilio_parser_get_utf8(&rilp);
|
||||
nparams -= 4;
|
||||
|
||||
reg->ril_status = atoi(sstatus);
|
||||
if (reg->ril_status > 10) {
|
||||
reg->status = reg->status - 10;
|
||||
} else {
|
||||
reg->status = reg->ril_status;
|
||||
}
|
||||
|
||||
/* FIXME: need to review VOICE_REGISTRATION response
|
||||
* as it returns ~15 parameters ( vs. 6 for DATA ).
|
||||
*
|
||||
* The first four parameters are the same for both
|
||||
* responses ( although status includes values for
|
||||
* emergency calls for VOICE response ).
|
||||
*
|
||||
* Parameters 5 & 6 have different meanings for
|
||||
* voice & data response.
|
||||
*/
|
||||
if (nparams--) {
|
||||
/* TODO: different use for CDMA */
|
||||
sreason = grilio_parser_get_utf8(&rilp);
|
||||
if (nparams--) {
|
||||
/* TODO: different use for CDMA */
|
||||
smax = grilio_parser_get_utf8(&rilp);
|
||||
if (smax) {
|
||||
reg->max_calls = atoi(smax);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reg->lac = slac ? strtol(slac, NULL, 16) : -1;
|
||||
reg->ci = sci ? strtol(sci, NULL, 16) : -1;
|
||||
reg->access_tech = ril_parse_tech(stech, ®->ril_tech);
|
||||
|
||||
DBG("%s,%s,%s,%d,%s,%s,%s", registration_status_to_string(reg->status),
|
||||
slac, sci, reg->ril_tech,
|
||||
registration_tech_to_string(reg->access_tech),
|
||||
sreason, smax);
|
||||
|
||||
g_free(sstatus);
|
||||
g_free(slac);
|
||||
g_free(sci);
|
||||
g_free(stech);
|
||||
g_free(sreason);
|
||||
g_free(smax);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Returns enum access_technology or -1 on failure. */
|
||||
int ril_parse_tech(const char *stech, int *ril_tech)
|
||||
{
|
||||
int access_tech = -1;
|
||||
int tech = -1;
|
||||
if (stech && stech[0]) {
|
||||
tech = atoi(stech);
|
||||
switch (tech) {
|
||||
case RADIO_TECH_GPRS:
|
||||
case RADIO_TECH_GSM:
|
||||
access_tech = ACCESS_TECHNOLOGY_GSM;
|
||||
break;
|
||||
case RADIO_TECH_EDGE:
|
||||
access_tech = ACCESS_TECHNOLOGY_GSM_EGPRS;
|
||||
break;
|
||||
case RADIO_TECH_UMTS:
|
||||
access_tech = ACCESS_TECHNOLOGY_UTRAN;
|
||||
break;
|
||||
case RADIO_TECH_HSDPA:
|
||||
access_tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA;
|
||||
break;
|
||||
case RADIO_TECH_HSUPA:
|
||||
access_tech = ACCESS_TECHNOLOGY_UTRAN_HSUPA;
|
||||
break;
|
||||
case RADIO_TECH_HSPA:
|
||||
case RADIO_TECH_HSPAP:
|
||||
case RADIO_TECH_DC_HSDPA:
|
||||
access_tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA;
|
||||
break;
|
||||
case RADIO_TECH_LTE:
|
||||
access_tech = ACCESS_TECHNOLOGY_EUTRAN;
|
||||
break;
|
||||
default:
|
||||
DBG("Unknown RIL tech %s", stech);
|
||||
tech = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (ril_tech) {
|
||||
*ril_tech = tech;
|
||||
}
|
||||
return access_tech;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
58
ofono/drivers/ril/ril_util.h
Normal file
58
ofono/drivers/ril/ril_util.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#ifndef RIL_UTIL_H
|
||||
#define RIL_UTIL_H
|
||||
|
||||
#include "ril_types.h"
|
||||
|
||||
#include <ofono/log.h>
|
||||
|
||||
struct ril_reg_data {
|
||||
int ril_status;
|
||||
int ril_tech;
|
||||
int status; /* enum network_registration_status or -1 if none */
|
||||
int access_tech; /* enum access_technology or -1 if none */
|
||||
int lac;
|
||||
int ci;
|
||||
int max_calls;
|
||||
};
|
||||
|
||||
const char *ril_error_to_string(int error);
|
||||
const char *ril_request_to_string(guint request);
|
||||
const char *ril_unsol_event_to_string(guint event);
|
||||
const char *ril_radio_state_to_string(int radio_state);
|
||||
int ril_parse_tech(const char *stech, int *ril_tech);
|
||||
int ril_address_family(const char *addr);
|
||||
gboolean ril_util_parse_reg(const void *data, guint len,
|
||||
struct ril_reg_data *parsed);
|
||||
|
||||
#define ril_error_init_ok(err) \
|
||||
((err)->error = 0, (err)->type = OFONO_ERROR_TYPE_NO_ERROR)
|
||||
#define ril_error_init_failure(err) \
|
||||
((err)->error = 0, (err)->type = OFONO_ERROR_TYPE_FAILURE)
|
||||
|
||||
#define ril_error_ok(err) (ril_error_init_ok(err), err)
|
||||
#define ril_error_failure(err) (ril_error_init_failure(err), err)
|
||||
|
||||
#endif /* RIL_UTIL_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
824
ofono/drivers/ril/ril_voicecall.c
Normal file
824
ofono/drivers/ril/ril_voicecall.c
Normal file
@@ -0,0 +1,824 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - RIL-based devices
|
||||
*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#include "ril_plugin.h"
|
||||
#include "ril_constants.h"
|
||||
#include "ril_util.h"
|
||||
#include "ril_log.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/* Amount of ms we wait between CLCC calls */
|
||||
#define FLAG_NEED_CLIP 1
|
||||
#define MAX_DTMF_BUFFER 32
|
||||
|
||||
enum ril_voicecall_events {
|
||||
VOICECALL_EVENT_CALL_STATE_CHANGED,
|
||||
VOICECALL_EVENT_SUPP_SVC_NOTIFICATION,
|
||||
VOICECALL_EVENT_RINGBACK_TONE,
|
||||
VOICECALL_EVENT_COUNT,
|
||||
};
|
||||
|
||||
struct ril_voicecall {
|
||||
GSList *calls;
|
||||
GRilIoChannel *io;
|
||||
GRilIoQueue *q;
|
||||
struct ofono_voicecall *vc;
|
||||
unsigned int local_release;
|
||||
unsigned char flags;
|
||||
ofono_voicecall_cb_t cb;
|
||||
void *data;
|
||||
guint timer_id;
|
||||
gchar *tone_queue;
|
||||
guint send_dtmf_id;
|
||||
guint clcc_poll_id;
|
||||
gulong event_id[VOICECALL_EVENT_COUNT];
|
||||
gulong supp_svc_notification_id;
|
||||
gulong ringback_tone_event_id;
|
||||
};
|
||||
|
||||
struct release_id_req {
|
||||
struct ofono_voicecall *vc;
|
||||
ofono_voicecall_cb_t cb;
|
||||
gpointer data;
|
||||
int id;
|
||||
};
|
||||
|
||||
struct ril_voicecall_change_state_req {
|
||||
struct ofono_voicecall *vc;
|
||||
ofono_voicecall_cb_t cb;
|
||||
gpointer data;
|
||||
int affected_types;
|
||||
};
|
||||
|
||||
struct lastcause_req {
|
||||
struct ofono_voicecall *vc;
|
||||
int id;
|
||||
};
|
||||
|
||||
struct ril_voicecall_req {
|
||||
struct ofono_voicecall *vc;
|
||||
ofono_voicecall_cb_t cb;
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
static void ril_voicecall_send_one_dtmf(struct ril_voicecall *vd);
|
||||
static void ril_voicecall_clear_dtmf_queue(struct ril_voicecall *vd);
|
||||
|
||||
/*
|
||||
* structs ofono_voicecall and voicecall are fully defined
|
||||
* in src/voicecall.c; we need (read) access to the
|
||||
* call objects, so partially redefine them here.
|
||||
*/
|
||||
struct ofono_voicecall {
|
||||
GSList *call_list;
|
||||
/* ... */
|
||||
};
|
||||
|
||||
struct voicecall {
|
||||
struct ofono_call *call;
|
||||
/* ... */
|
||||
};
|
||||
|
||||
static inline struct ril_voicecall *ril_voicecall_get_data(
|
||||
struct ofono_voicecall *vc)
|
||||
{
|
||||
return ofono_voicecall_get_data(vc);
|
||||
}
|
||||
|
||||
static gint ril_voicecall_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 GSList *ril_voicecall_parse_clcc(const void *data, guint len)
|
||||
{
|
||||
GRilIoParser rilp;
|
||||
GSList *l = NULL;
|
||||
int num = 0, i;
|
||||
gchar *number, *name;
|
||||
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
|
||||
/* Number of RIL_Call structs */
|
||||
|
||||
grilio_parser_get_int32(&rilp, &num);
|
||||
for (i = 0; i < num; i++) {
|
||||
struct ofono_call *call = g_new(struct ofono_call, 1);
|
||||
gint tmp;
|
||||
|
||||
ofono_call_init(call);
|
||||
grilio_parser_get_int32(&rilp, &call->status);
|
||||
grilio_parser_get_uint32(&rilp, &call->id);
|
||||
grilio_parser_get_int32(&rilp, &call->phone_number.type);
|
||||
grilio_parser_get_int32(&rilp, NULL); /* isMpty */
|
||||
|
||||
tmp = 0;
|
||||
grilio_parser_get_int32(&rilp, &tmp);
|
||||
call->direction = (tmp ? /* isMT */
|
||||
CALL_DIRECTION_MOBILE_TERMINATED :
|
||||
CALL_DIRECTION_MOBILE_ORIGINATED);
|
||||
|
||||
grilio_parser_get_int32(&rilp, NULL); /* als */
|
||||
grilio_parser_get_int32(&rilp, &call->type); /* isVoice */
|
||||
grilio_parser_get_int32(&rilp, NULL); /* isVoicePrivacy */
|
||||
number = grilio_parser_get_utf8(&rilp);
|
||||
if (number) {
|
||||
strncpy(call->phone_number.number, number,
|
||||
OFONO_MAX_PHONE_NUMBER_LENGTH);
|
||||
g_free(number);
|
||||
}
|
||||
grilio_parser_get_int32(&rilp, NULL); /* numberPresentation */
|
||||
name = grilio_parser_get_utf8(&rilp);
|
||||
if (name) {
|
||||
strncpy(call->name, name, OFONO_MAX_CALLER_NAME_LENGTH);
|
||||
g_free(name);
|
||||
}
|
||||
grilio_parser_get_int32(&rilp, NULL); /* namePresentation */
|
||||
grilio_parser_get_int32(&rilp, &tmp); /* uusInfo */
|
||||
GASSERT(!tmp);
|
||||
|
||||
if (strlen(call->phone_number.number) > 0) {
|
||||
call->clip_validity = 0;
|
||||
} else {
|
||||
call->clip_validity = 2;
|
||||
}
|
||||
|
||||
DBG("[id=%d,status=%d,type=%d,number=%s,name=%s]",
|
||||
call->id, call->status, call->type,
|
||||
call->phone_number.number, call->name);
|
||||
|
||||
l = g_slist_insert_sorted(l, call, ril_voicecall_compare);
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
static void ril_voicecall_lastcause_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct lastcause_req *reqdata = user_data;
|
||||
struct ofono_voicecall *vc = reqdata->vc;
|
||||
int tmp;
|
||||
int id = reqdata->id;
|
||||
|
||||
enum ofono_disconnect_reason reason = OFONO_DISCONNECT_REASON_ERROR;
|
||||
int last_cause = CALL_FAIL_ERROR_UNSPECIFIED;
|
||||
GRilIoParser rilp;
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
if (grilio_parser_get_int32(&rilp, &tmp) && tmp > 0) {
|
||||
grilio_parser_get_int32(&rilp, &last_cause);
|
||||
}
|
||||
|
||||
/*
|
||||
* Not all call control cause values specified in 3GPP TS 24.008
|
||||
* "Mobile radio interface Layer 3 specification; Core network
|
||||
* protocols", Annex H, are properly reflected in the RIL API.
|
||||
* For example, cause #21 "call rejected" is mapped to
|
||||
* CALL_FAIL_ERROR_UNSPECIFIED, and thus indistinguishable
|
||||
* from a network failure. We signal disconnect reason "remote"
|
||||
* for cause values
|
||||
* - #16 "normal call clearing"
|
||||
* - #17 "user busy"
|
||||
* - UNSPECIFIED for MO calls that are not yet connected
|
||||
* , and disconnect reason "network" otherwise.
|
||||
*/
|
||||
ofono_info("Call %d ended with RIL cause %d", id, last_cause);
|
||||
if (last_cause == CALL_FAIL_NORMAL || last_cause == CALL_FAIL_BUSY) {
|
||||
reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
|
||||
}
|
||||
|
||||
if (last_cause == CALL_FAIL_ERROR_UNSPECIFIED) {
|
||||
GSList *l;
|
||||
struct voicecall *v;
|
||||
for (l = vc->call_list; l; l = l->next) {
|
||||
v = l->data;
|
||||
if (v->call->id == id) {
|
||||
if (v->call->status == CALL_STATUS_DIALING ||
|
||||
v->call->status == CALL_STATUS_ALERTING) {
|
||||
reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ofono_voicecall_disconnected(vc, id, reason, NULL);
|
||||
}
|
||||
|
||||
static void ril_voicecall_clcc_poll_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_voicecall *vd = user_data;
|
||||
GSList *calls;
|
||||
GSList *n, *o;
|
||||
struct ofono_error error;
|
||||
|
||||
GASSERT(vd->clcc_poll_id);
|
||||
vd->clcc_poll_id = 0;
|
||||
|
||||
if (status != RIL_E_SUCCESS) {
|
||||
ofono_error("We are polling CLCC and received an error");
|
||||
ofono_error("All bets are off for call management");
|
||||
return;
|
||||
}
|
||||
|
||||
calls = ril_voicecall_parse_clcc(data, len);
|
||||
|
||||
n = calls;
|
||||
o = vd->calls;
|
||||
|
||||
while (n || o) {
|
||||
struct ofono_call *nc = n ? n->data : NULL;
|
||||
struct ofono_call *oc = o ? o->data : NULL;
|
||||
|
||||
if (oc && (nc == NULL || (nc->id > oc->id))) {
|
||||
if (vd->local_release & (1 << oc->id)) {
|
||||
ofono_voicecall_disconnected(vd->vc, oc->id,
|
||||
OFONO_DISCONNECT_REASON_LOCAL_HANGUP,
|
||||
NULL);
|
||||
} else {
|
||||
/* Get disconnect cause before informing
|
||||
* oFono core */
|
||||
struct lastcause_req *reqdata =
|
||||
g_new0(struct lastcause_req, 1);
|
||||
|
||||
reqdata->vc = vd->vc;
|
||||
reqdata->id = oc->id;
|
||||
grilio_queue_send_request_full(vd->q, NULL,
|
||||
RIL_REQUEST_LAST_CALL_FAIL_CAUSE,
|
||||
ril_voicecall_lastcause_cb,
|
||||
g_free, reqdata);
|
||||
}
|
||||
|
||||
ril_voicecall_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(vd->vc, nc);
|
||||
if (vd->cb) {
|
||||
ofono_voicecall_cb_t cb = vd->cb;
|
||||
cb(ril_error_ok(&error), vd->data);
|
||||
vd->cb = NULL;
|
||||
vd->data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
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(vd->vc, nc);
|
||||
}
|
||||
|
||||
vd->flags &= ~FLAG_NEED_CLIP;
|
||||
} else if (memcmp(nc, oc, sizeof(*nc)) && nc->type) {
|
||||
ofono_voicecall_notify(vd->vc, nc);
|
||||
}
|
||||
|
||||
n = n->next;
|
||||
o = o->next;
|
||||
}
|
||||
}
|
||||
|
||||
g_slist_free_full(vd->calls, g_free);
|
||||
|
||||
vd->calls = calls;
|
||||
vd->local_release = 0;
|
||||
}
|
||||
|
||||
static void ril_voicecall_clcc_poll(struct ril_voicecall *vd)
|
||||
{
|
||||
GASSERT(vd);
|
||||
if (!vd->clcc_poll_id) {
|
||||
vd->clcc_poll_id = grilio_queue_send_request_full(vd->q,
|
||||
NULL, RIL_REQUEST_GET_CURRENT_CALLS,
|
||||
ril_voicecall_clcc_poll_cb, NULL, vd);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_voicecall_request_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_voicecall_change_state_req *req = user_data;
|
||||
struct ril_voicecall *vd = ril_voicecall_get_data(req->vc);
|
||||
struct ofono_error error;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
GSList *l;
|
||||
|
||||
if (req->affected_types) {
|
||||
for (l = vd->calls; l; l = l->next) {
|
||||
struct ofono_call *call = l->data;
|
||||
|
||||
if (req->affected_types & (1 << call->status)) {
|
||||
vd->local_release |= (1 << call->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ril_error_init_ok(&error);
|
||||
} else {
|
||||
ofono_error("generic fail");
|
||||
ril_error_init_failure(&error);
|
||||
}
|
||||
|
||||
ril_voicecall_clcc_poll(vd);
|
||||
|
||||
/* We have to callback after we schedule a poll if required */
|
||||
if (req->cb) {
|
||||
req->cb(&error, req->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_voicecall_request(const guint rreq, struct ofono_voicecall *vc,
|
||||
unsigned int affected_types, GRilIoRequest *ioreq,
|
||||
ofono_voicecall_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_voicecall *vd = ril_voicecall_get_data(vc);
|
||||
struct ril_voicecall_change_state_req *req;
|
||||
|
||||
req = g_new0(struct ril_voicecall_change_state_req, 1);
|
||||
req->vc = vc;
|
||||
req->cb = cb;
|
||||
req->data = data;
|
||||
req->affected_types = affected_types;
|
||||
|
||||
grilio_queue_send_request_full(vd->q, ioreq, rreq,
|
||||
ril_voicecall_request_cb, g_free, req);
|
||||
}
|
||||
|
||||
static void ril_voicecall_dial_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_voicecall_req *cbd = user_data;
|
||||
struct ofono_voicecall *vc = cbd->vc;
|
||||
struct ril_voicecall *vd = ril_voicecall_get_data(vc);
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
if (vd->cb) {
|
||||
/* CLCC will update the oFono call list with
|
||||
* proper ids if it's not done yet */
|
||||
ril_voicecall_clcc_poll(vd);
|
||||
}
|
||||
} else {
|
||||
struct ofono_error error;
|
||||
ofono_error("call failed.");
|
||||
vd->cb = cbd->cb;
|
||||
vd->data = cbd->data;
|
||||
cbd->cb(ril_error_failure(&error), cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_voicecall_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 *vd = ril_voicecall_get_data(vc);
|
||||
struct ril_voicecall_req *cbd = g_new(struct ril_voicecall_req, 1);
|
||||
const char *phstr = phone_number_to_string(ph);
|
||||
GRilIoRequest *req = grilio_request_new();
|
||||
|
||||
ofono_info("dialing \"%s\"", phstr);
|
||||
|
||||
DBG("%s,%d,0", phstr, clir);
|
||||
cbd->vc = vc;
|
||||
cbd->cb = cb;
|
||||
cbd->data = data;
|
||||
|
||||
GASSERT(!vd->cb);
|
||||
vd->cb = cbd->cb;
|
||||
vd->data = cbd->data;
|
||||
|
||||
grilio_request_append_utf8(req, phstr); /* Number to dial */
|
||||
grilio_request_append_int32(req, clir); /* CLIR mode */
|
||||
grilio_request_append_int32(req, 0); /* UUS information (absent) */
|
||||
|
||||
grilio_queue_send_request_full(vd->q, req, RIL_REQUEST_DIAL,
|
||||
ril_voicecall_dial_cb, g_free, cbd);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_voicecall_hangup_all(struct ofono_voicecall *vc,
|
||||
ofono_voicecall_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_voicecall *vd = ril_voicecall_get_data(vc);
|
||||
struct ofono_error error;
|
||||
GSList *l;
|
||||
|
||||
for (l = vd->calls; l; l = l->next) {
|
||||
struct ofono_call *call = l->data;
|
||||
GRilIoRequest *req = grilio_request_sized_new(8);
|
||||
|
||||
/* TODO: Hangup just the active ones once we have call
|
||||
* state tracking (otherwise it can't handle ringing) */
|
||||
DBG("Hanging up call with id %d", call->id);
|
||||
grilio_request_append_int32(req, 1); /* Always 1 - AT+CHLD=1x */
|
||||
grilio_request_append_int32(req, call->id);
|
||||
|
||||
/* Send request to RIL */
|
||||
ril_voicecall_request(RIL_REQUEST_HANGUP, vc, 0x3f, req,
|
||||
NULL, NULL);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
/* TODO: Deal in case of an error at hungup */
|
||||
cb(ril_error_ok(&error), data);
|
||||
}
|
||||
|
||||
static void ril_voicecall_hangup_specific(struct ofono_voicecall *vc,
|
||||
int id, ofono_voicecall_cb_t cb, void *data)
|
||||
{
|
||||
GRilIoRequest *req = grilio_request_sized_new(8);
|
||||
struct ofono_error error;
|
||||
|
||||
DBG("Hanging up call with id %d", id);
|
||||
grilio_request_append_int32(req, 1); /* Always 1 - AT+CHLD=1x */
|
||||
grilio_request_append_int32(req, id);
|
||||
|
||||
/* Send request to RIL */
|
||||
ril_voicecall_request(RIL_REQUEST_HANGUP, vc, 0x3f, req, NULL, NULL);
|
||||
grilio_request_unref(req);
|
||||
cb(ril_error_ok(&error), data);
|
||||
}
|
||||
|
||||
static void ril_voicecall_call_state_changed_event(GRilIoChannel *io,
|
||||
guint ril_event, const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_voicecall *vd = user_data;
|
||||
|
||||
GASSERT(ril_event == RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED);
|
||||
|
||||
/* Just need to request the call list again */
|
||||
ril_voicecall_clcc_poll(vd);
|
||||
}
|
||||
|
||||
static void ril_voicecall_supp_svc_notification_event(GRilIoChannel *io,
|
||||
guint ril_event, const void *data, guint len, void *user_data)
|
||||
{
|
||||
GRilIoParser rilp;
|
||||
struct ril_voicecall *vd = user_data;
|
||||
struct ofono_phone_number phone;
|
||||
int type = 0, code = 0, index = 0;
|
||||
char *tmp = NULL;
|
||||
|
||||
GASSERT(ril_event == RIL_UNSOL_SUPP_SVC_NOTIFICATION);
|
||||
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
grilio_parser_get_int32(&rilp, &type);
|
||||
grilio_parser_get_int32(&rilp, &code);
|
||||
grilio_parser_get_int32(&rilp, &index);
|
||||
grilio_parser_get_int32(&rilp, NULL);
|
||||
tmp = grilio_parser_get_utf8(&rilp);
|
||||
|
||||
if (tmp) {
|
||||
strncpy(phone.number, tmp, OFONO_MAX_PHONE_NUMBER_LENGTH);
|
||||
phone.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = 0;
|
||||
g_free(tmp);
|
||||
} else {
|
||||
phone.number[0] = 0;
|
||||
}
|
||||
|
||||
DBG("RIL data: MT/MO: %i, code: %i, index: %i", type, code, index);
|
||||
|
||||
/* 0 stands for MO intermediate (support TBD), 1 for MT unsolicited */
|
||||
if (type == 1) {
|
||||
ofono_voicecall_ssn_mt_notify(vd->vc, 0, code, index, &phone);
|
||||
} else {
|
||||
ofono_error("Unknown SS notification");
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_voicecall_answer(struct ofono_voicecall *vc,
|
||||
ofono_voicecall_cb_t cb, void *data)
|
||||
{
|
||||
/* Send request to RIL */
|
||||
DBG("Answering current call");
|
||||
ril_voicecall_request(RIL_REQUEST_ANSWER, vc, 0, NULL, cb, data);
|
||||
}
|
||||
|
||||
static void ril_voicecall_send_dtmf_cb(GRilIoChannel *io, int status,
|
||||
const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_voicecall *vd = user_data;
|
||||
|
||||
GASSERT(vd->send_dtmf_id);
|
||||
vd->send_dtmf_id = 0;
|
||||
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
/* Remove sent DTMF character from queue */
|
||||
gchar *tmp = g_strdup(vd->tone_queue + 1);
|
||||
g_free(vd->tone_queue);
|
||||
vd->tone_queue = tmp;
|
||||
|
||||
/* Send the next one */
|
||||
ril_voicecall_send_one_dtmf(vd);
|
||||
} else {
|
||||
DBG("error=%d", status);
|
||||
ril_voicecall_clear_dtmf_queue(vd);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_voicecall_send_one_dtmf(struct ril_voicecall *vd)
|
||||
{
|
||||
if (!vd->send_dtmf_id && vd->tone_queue && vd->tone_queue[0]) {
|
||||
GRilIoRequest *req = grilio_request_sized_new(4);
|
||||
|
||||
/* RIL wants just one character */
|
||||
DBG("%c", vd->tone_queue[0]);
|
||||
grilio_request_append_utf8_chars(req, vd->tone_queue, 1);
|
||||
vd->send_dtmf_id = grilio_queue_send_request_full(vd->q, req,
|
||||
RIL_REQUEST_DTMF, ril_voicecall_send_dtmf_cb, NULL, vd);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_voicecall_send_dtmf(struct ofono_voicecall *vc,
|
||||
const char *dtmf, ofono_voicecall_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_voicecall *vd = ril_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);
|
||||
ril_voicecall_send_one_dtmf(vd);
|
||||
|
||||
cb(ril_error_ok(&error), data);
|
||||
}
|
||||
|
||||
static void ril_voicecall_clear_dtmf_queue(struct ril_voicecall *vd)
|
||||
{
|
||||
g_free(vd->tone_queue);
|
||||
vd->tone_queue = g_strnfill(MAX_DTMF_BUFFER + 1, '\0');
|
||||
if (vd->send_dtmf_id) {
|
||||
grilio_channel_cancel_request(vd->io, vd->send_dtmf_id, FALSE);
|
||||
vd->send_dtmf_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_voicecall_clcc_poll_on_success(GRilIoChannel *io,
|
||||
int status, const void *data, guint len, void *user_data)
|
||||
{
|
||||
if (status == RIL_E_SUCCESS) {
|
||||
ril_voicecall_clcc_poll((struct ril_voicecall *)user_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ril_voicecall_create_multiparty(struct ofono_voicecall *vc,
|
||||
ofono_voicecall_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_voicecall *vd = ril_voicecall_get_data(vc);
|
||||
grilio_queue_send_request_full(vd->q, NULL, RIL_REQUEST_CONFERENCE,
|
||||
ril_voicecall_clcc_poll_on_success, NULL, vd);
|
||||
}
|
||||
|
||||
static void ril_voicecall_transfer(struct ofono_voicecall *vc,
|
||||
ofono_voicecall_cb_t cb, void *data)
|
||||
{
|
||||
ril_voicecall_request(RIL_REQUEST_EXPLICIT_CALL_TRANSFER,
|
||||
vc, 0, NULL, cb, data);
|
||||
}
|
||||
|
||||
static void ril_voicecall_private_chat(struct ofono_voicecall *vc, int id,
|
||||
ofono_voicecall_cb_t cb, void *data)
|
||||
{
|
||||
struct ril_voicecall *vd = ril_voicecall_get_data(vc);
|
||||
GRilIoRequest *req = grilio_request_sized_new(8);
|
||||
grilio_request_append_int32(req, 1);
|
||||
grilio_request_append_int32(req, id);
|
||||
grilio_queue_send_request_full(vd->q, req,
|
||||
RIL_REQUEST_SEPARATE_CONNECTION,
|
||||
ril_voicecall_clcc_poll_on_success, NULL, vd);
|
||||
grilio_request_unref(req);
|
||||
}
|
||||
|
||||
static void ril_voicecall_swap_without_accept(struct ofono_voicecall *vc,
|
||||
ofono_voicecall_cb_t cb, void *data)
|
||||
{
|
||||
ril_voicecall_request(RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE,
|
||||
vc, 0, NULL, cb, data);
|
||||
}
|
||||
|
||||
static void ril_voicecall_hold_all_active(struct ofono_voicecall *vc,
|
||||
ofono_voicecall_cb_t cb, void *data)
|
||||
{
|
||||
ril_voicecall_request(RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE,
|
||||
vc, 0, NULL, cb, data);
|
||||
}
|
||||
|
||||
static void ril_voicecall_release_all_held(struct ofono_voicecall *vc,
|
||||
ofono_voicecall_cb_t cb, void *data)
|
||||
{
|
||||
ril_voicecall_request(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND,
|
||||
vc, 0, NULL, cb, data);
|
||||
}
|
||||
|
||||
static void ril_voicecall_release_all_active(struct ofono_voicecall *vc,
|
||||
ofono_voicecall_cb_t cb, void *data)
|
||||
{
|
||||
ril_voicecall_request(RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND,
|
||||
vc, 0, NULL, cb, data);
|
||||
}
|
||||
|
||||
static void ril_voicecall_set_udub(struct ofono_voicecall *vc,
|
||||
ofono_voicecall_cb_t cb, void *data)
|
||||
{
|
||||
ril_voicecall_request(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND,
|
||||
vc, 0, NULL, cb, data);
|
||||
}
|
||||
|
||||
static gboolean ril_voicecall_enable_supp_svc(struct ril_voicecall *vd)
|
||||
{
|
||||
GRilIoRequest *req = grilio_request_sized_new(8);
|
||||
|
||||
grilio_request_append_int32(req, 1); /* size of array */
|
||||
grilio_request_append_int32(req, 1); /* notifications enabled */
|
||||
|
||||
grilio_queue_send_request(vd->q, req,
|
||||
RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION);
|
||||
grilio_request_unref(req);
|
||||
|
||||
/* Makes this a single shot */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void ril_voicecall_ringback_tone_event(GRilIoChannel *io,
|
||||
guint code, const void *data, guint len, void *user_data)
|
||||
{
|
||||
struct ril_voicecall *vd = user_data;
|
||||
GRilIoParser rilp;
|
||||
guint32 playTone = FALSE;
|
||||
int tmp;
|
||||
|
||||
GASSERT(code == RIL_UNSOL_RINGBACK_TONE);
|
||||
grilio_parser_init(&rilp, data, len);
|
||||
if (grilio_parser_get_int32(&rilp, &tmp) && tmp > 0) {
|
||||
grilio_parser_get_uint32(&rilp, &playTone);
|
||||
}
|
||||
|
||||
DBG("play ringback tone: %d", playTone);
|
||||
ofono_voicecall_ringback_tone_notify(vd->vc, playTone);
|
||||
}
|
||||
|
||||
static gboolean ril_delayed_register(gpointer user_data)
|
||||
{
|
||||
struct ril_voicecall *vd = user_data;
|
||||
|
||||
GASSERT(vd->timer_id);
|
||||
vd->timer_id = 0;
|
||||
ofono_voicecall_register(vd->vc);
|
||||
|
||||
/* Initialize call list */
|
||||
ril_voicecall_clcc_poll(vd);
|
||||
|
||||
/* request supplementary service notifications*/
|
||||
ril_voicecall_enable_supp_svc(vd);
|
||||
|
||||
/* Unsol when call state changes */
|
||||
vd->event_id[VOICECALL_EVENT_CALL_STATE_CHANGED] =
|
||||
grilio_channel_add_unsol_event_handler(vd->io,
|
||||
ril_voicecall_call_state_changed_event,
|
||||
RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED, vd);
|
||||
|
||||
/* Unsol when call set in hold */
|
||||
vd->event_id[VOICECALL_EVENT_SUPP_SVC_NOTIFICATION] =
|
||||
grilio_channel_add_unsol_event_handler(vd->io,
|
||||
ril_voicecall_supp_svc_notification_event,
|
||||
RIL_UNSOL_SUPP_SVC_NOTIFICATION, vd);
|
||||
|
||||
/* Register for ringback tone notifications */
|
||||
vd->event_id[VOICECALL_EVENT_RINGBACK_TONE] =
|
||||
grilio_channel_add_unsol_event_handler(vd->io,
|
||||
ril_voicecall_ringback_tone_event,
|
||||
RIL_UNSOL_RINGBACK_TONE, vd);
|
||||
|
||||
/* This makes the timeout a single-shot */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int ril_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
|
||||
void *data)
|
||||
{
|
||||
struct ril_modem *modem = data;
|
||||
struct ril_voicecall *vd;
|
||||
|
||||
DBG("");
|
||||
vd = g_new0(struct ril_voicecall, 1);
|
||||
vd->io = grilio_channel_ref(ril_modem_io(modem));
|
||||
vd->q = grilio_queue_new(vd->io);
|
||||
vd->vc = vc;
|
||||
vd->timer_id = g_idle_add(ril_delayed_register, vd);
|
||||
ril_voicecall_clear_dtmf_queue(vd);
|
||||
ofono_voicecall_set_data(vc, vd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ril_voicecall_remove(struct ofono_voicecall *vc)
|
||||
{
|
||||
int i;
|
||||
struct ril_voicecall *vd = ril_voicecall_get_data(vc);
|
||||
|
||||
DBG("");
|
||||
ofono_voicecall_set_data(vc, NULL);
|
||||
g_slist_foreach(vd->calls, (GFunc) g_free, NULL);
|
||||
g_slist_free(vd->calls);
|
||||
|
||||
for (i=0; i<G_N_ELEMENTS(vd->event_id); i++) {
|
||||
grilio_channel_remove_handler(vd->io, vd->event_id[i]);
|
||||
}
|
||||
|
||||
if (vd->timer_id > 0) {
|
||||
g_source_remove(vd->timer_id);
|
||||
}
|
||||
|
||||
grilio_channel_unref(vd->io);
|
||||
grilio_queue_cancel_all(vd->q, FALSE);
|
||||
grilio_queue_unref(vd->q);
|
||||
g_free(vd->tone_queue);
|
||||
g_free(vd);
|
||||
}
|
||||
|
||||
const struct ofono_voicecall_driver ril_voicecall_driver = {
|
||||
.name = RILMODEM_DRIVER,
|
||||
.probe = ril_voicecall_probe,
|
||||
.remove = ril_voicecall_remove,
|
||||
.dial = ril_voicecall_dial,
|
||||
.answer = ril_voicecall_answer,
|
||||
.hangup_all = ril_voicecall_hangup_all,
|
||||
.release_specific = ril_voicecall_hangup_specific,
|
||||
.send_tones = ril_voicecall_send_dtmf,
|
||||
.create_multiparty = ril_voicecall_create_multiparty,
|
||||
.transfer = ril_voicecall_transfer,
|
||||
.private_chat = ril_voicecall_private_chat,
|
||||
.swap_without_accept = ril_voicecall_swap_without_accept,
|
||||
.hold_all_active = ril_voicecall_hold_all_active,
|
||||
.release_all_held = ril_voicecall_release_all_held,
|
||||
.set_udub = ril_voicecall_set_udub,
|
||||
.release_all_active = ril_voicecall_release_all_active
|
||||
};
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 8
|
||||
* indent-tabs-mode: t
|
||||
* End:
|
||||
*/
|
||||
@@ -19,6 +19,9 @@ BuildRequires: pkgconfig(libudev) >= 145
|
||||
BuildRequires: pkgconfig(bluez) >= 4.85
|
||||
BuildRequires: pkgconfig(mobile-broadband-provider-info)
|
||||
BuildRequires: pkgconfig(libwspcodec) >= 2.0
|
||||
BuildRequires: pkgconfig(libglibutil)
|
||||
BuildRequires: pkgconfig(libgrilio)
|
||||
BuildRequires: mce-headers
|
||||
BuildRequires: libtool
|
||||
BuildRequires: automake
|
||||
BuildRequires: autoconf
|
||||
@@ -66,6 +69,7 @@ autoreconf --force --install
|
||||
%configure --disable-static \
|
||||
--enable-test \
|
||||
--enable-logcontrol \
|
||||
--enable-jolla-rilmodem \
|
||||
--with-systemdunitdir="/%{_lib}/systemd/system"
|
||||
|
||||
make %{?jobs:-j%jobs}
|
||||
|
||||
Reference in New Issue
Block a user