mirror of git://git.sysmocom.de/ofono
Add implementation of telephony daemon
This commit is contained in:
parent
838583f498
commit
a78b36290b
|
@ -7,7 +7,11 @@ endif
|
|||
|
||||
sbin_PROGRAMS = ofonod
|
||||
|
||||
ofonod_SOURCES = main.c ofono.h log.c plugin.c
|
||||
ofonod_SOURCES = main.c ofono.h log.c plugin.c \
|
||||
driver.h modem.h modem.c common.h common.c \
|
||||
manager.c dbus-gsm.h dbus-gsm.c util.h util.c \
|
||||
network.c voicecall.c ussd.h ussd.c \
|
||||
call-settings.c call-waiting.c call-forwarding.c call-meter.c
|
||||
|
||||
ofonod_LDADD = @GDBUS_LIBS@ @GLIB_LIBS@ @GTHREAD_LIBS@ -ldl
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,764 @@
|
|||
/*
|
||||
*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include <glib.h>
|
||||
#include <gdbus.h>
|
||||
|
||||
#include "ofono.h"
|
||||
|
||||
#include "driver.h"
|
||||
#include "common.h"
|
||||
#include "dbus-gsm.h"
|
||||
#include "modem.h"
|
||||
|
||||
#define CALL_METER_INTERFACE "org.ofono.CallMeter"
|
||||
|
||||
#define CALL_METER_FLAG_CACHED 0x1
|
||||
#define CALL_METER_FLAG_HAVE_PUCT 0x2
|
||||
|
||||
struct call_meter_data {
|
||||
struct ofono_call_meter_ops *ops;
|
||||
int flags;
|
||||
DBusMessage *pending;
|
||||
|
||||
int call_meter;
|
||||
int acm;
|
||||
int acm_max;
|
||||
double ppu;
|
||||
char currency[4];
|
||||
char *passwd;
|
||||
};
|
||||
|
||||
static struct call_meter_data *call_meter_create(void)
|
||||
{
|
||||
struct call_meter_data *cm = g_try_new0(struct call_meter_data, 1);
|
||||
|
||||
return cm;
|
||||
}
|
||||
|
||||
static void call_meter_destroy(gpointer userdata)
|
||||
{
|
||||
struct ofono_modem *modem = userdata;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
g_free(cm);
|
||||
|
||||
modem->call_meter = NULL;
|
||||
}
|
||||
|
||||
static void set_call_meter(struct ofono_modem *modem, int value)
|
||||
{
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (cm->call_meter != value) {
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
cm->call_meter = value;
|
||||
|
||||
dbus_gsm_signal_property_changed(conn, modem->path,
|
||||
CALL_METER_INTERFACE,
|
||||
"CallMeter",
|
||||
DBUS_TYPE_UINT32,
|
||||
&cm->call_meter);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_acm(struct ofono_modem *modem, int value)
|
||||
{
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (cm->acm != value) {
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
cm->acm = value;
|
||||
|
||||
dbus_gsm_signal_property_changed(conn, modem->path,
|
||||
CALL_METER_INTERFACE,
|
||||
"AccumulatedCallMeter",
|
||||
DBUS_TYPE_UINT32,
|
||||
&cm->acm);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_acm_max(struct ofono_modem *modem, int value)
|
||||
{
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (cm->acm_max != value) {
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
cm->acm_max = value;
|
||||
|
||||
dbus_gsm_signal_property_changed(conn, modem->path,
|
||||
CALL_METER_INTERFACE,
|
||||
"AccumulatedCallMeterMaximum",
|
||||
DBUS_TYPE_UINT32,
|
||||
&cm->acm_max);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_ppu(struct ofono_modem *modem, double value)
|
||||
{
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (cm->ppu != value) {
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
cm->ppu = value;
|
||||
|
||||
dbus_gsm_signal_property_changed(conn, modem->path,
|
||||
CALL_METER_INTERFACE,
|
||||
"PricePerUnit",
|
||||
DBUS_TYPE_DOUBLE,
|
||||
&cm->ppu);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_currency(struct ofono_modem *modem, const char *value)
|
||||
{
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (strlen(value) > 3) {
|
||||
ofono_error("Currency reported with size > 3: %s", value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(cm->currency, value)) {
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
const char *dbusval = cm->currency;
|
||||
|
||||
strncpy(cm->currency, value, 3);
|
||||
cm->currency[3] = '\0';
|
||||
|
||||
dbus_gsm_signal_property_changed(conn, modem->path,
|
||||
CALL_METER_INTERFACE,
|
||||
"Currency",
|
||||
DBUS_TYPE_STRING,
|
||||
&dbusval);
|
||||
}
|
||||
}
|
||||
|
||||
static void cm_get_properties_reply(struct ofono_modem *modem)
|
||||
{
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
//struct call_meter_property *property;
|
||||
DBusMessage *reply;
|
||||
DBusMessageIter iter, dict;
|
||||
const char *currency = cm->currency;
|
||||
|
||||
reply = dbus_message_new_method_return(cm->pending);
|
||||
if (!reply)
|
||||
return;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
|
||||
PROPERTIES_ARRAY_SIGNATURE, &dict);
|
||||
|
||||
dbus_gsm_dict_append(&dict, "CallMeter", DBUS_TYPE_UINT32,
|
||||
&cm->call_meter);
|
||||
|
||||
dbus_gsm_dict_append(&dict, "AccumulatedCallMeter", DBUS_TYPE_UINT32,
|
||||
&cm->acm);
|
||||
|
||||
dbus_gsm_dict_append(&dict, "AccumulatedCallMeterMaximum",
|
||||
DBUS_TYPE_UINT32, &cm->acm_max);
|
||||
|
||||
dbus_gsm_dict_append(&dict, "PricePerUnit", DBUS_TYPE_DOUBLE, &cm->ppu);
|
||||
|
||||
dbus_gsm_dict_append(&dict, "Currency", DBUS_TYPE_STRING, ¤cy);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &dict);
|
||||
|
||||
dbus_gsm_pending_reply(&cm->pending, reply);
|
||||
}
|
||||
|
||||
static void query_call_meter_callback(const struct ofono_error *error, int value,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
|
||||
set_call_meter(modem, value);
|
||||
|
||||
if (cm->pending)
|
||||
cm_get_properties_reply(modem);
|
||||
}
|
||||
|
||||
static gboolean query_call_meter(gpointer user)
|
||||
{
|
||||
struct ofono_modem *modem = user;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (!cm->ops->call_meter_query) {
|
||||
if (cm->pending)
|
||||
cm_get_properties_reply(modem);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cm->ops->call_meter_query(modem, query_call_meter_callback, modem);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void query_acm_callback(const struct ofono_error *error, int value,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
//struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
|
||||
set_acm(modem, value);
|
||||
|
||||
g_timeout_add(0, query_call_meter, modem);
|
||||
}
|
||||
|
||||
static gboolean query_acm(gpointer user)
|
||||
{
|
||||
struct ofono_modem *modem = user;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (!cm->ops->acm_query) {
|
||||
query_call_meter(modem);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cm->ops->acm_query(modem, query_acm_callback, modem);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void query_acm_max_callback(const struct ofono_error *error, int value,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
|
||||
set_acm_max(modem, value);
|
||||
|
||||
cm->flags |= CALL_METER_FLAG_CACHED;
|
||||
|
||||
g_timeout_add(0, query_acm, modem);
|
||||
}
|
||||
|
||||
static gboolean query_acm_max(gpointer user)
|
||||
{
|
||||
struct ofono_modem *modem = user;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (!cm->ops->acm_max_query) {
|
||||
cm->flags |= CALL_METER_FLAG_CACHED;
|
||||
|
||||
query_acm(modem);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cm->ops->acm_max_query(modem, query_acm_max_callback, modem);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void query_puct_callback(const struct ofono_error *error,
|
||||
const char *currency, double ppu, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
cm->flags |= CALL_METER_FLAG_HAVE_PUCT;
|
||||
set_currency(modem, currency);
|
||||
set_ppu(modem, ppu);
|
||||
}
|
||||
|
||||
g_timeout_add(0, query_acm_max, modem);
|
||||
}
|
||||
|
||||
static gboolean query_puct(gpointer user)
|
||||
{
|
||||
struct ofono_modem *modem = user;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (!cm->ops->puct_query) {
|
||||
query_acm_max(modem);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cm->ops->puct_query(modem, query_puct_callback, modem);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static DBusMessage *cm_get_properties(DBusConnection *conn, DBusMessage *msg,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (cm->pending)
|
||||
return dbus_gsm_busy(msg);
|
||||
|
||||
cm->pending = dbus_message_ref(msg);
|
||||
|
||||
/* We don't need to query ppu, currency & acm_max every time
|
||||
* Not sure if we have to query acm & call_meter every time
|
||||
* so lets play on the safe side and query them. They should be
|
||||
* fast to query anyway
|
||||
*/
|
||||
if (cm->flags & CALL_METER_FLAG_CACHED)
|
||||
query_acm(modem);
|
||||
else
|
||||
query_puct(modem);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void set_acm_max_query_callback(const struct ofono_error *error, int value,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
DBusMessage *reply;
|
||||
|
||||
if (!cm->pending)
|
||||
return;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ofono_error("Setting acm_max successful, but query was not");
|
||||
|
||||
cm->flags &= ~CALL_METER_FLAG_CACHED;
|
||||
|
||||
dbus_gsm_pending_reply(&cm->pending,
|
||||
dbus_gsm_failed(cm->pending));
|
||||
return;
|
||||
}
|
||||
|
||||
reply = dbus_message_new_method_return(cm->pending);
|
||||
dbus_gsm_pending_reply(&cm->pending, reply);
|
||||
|
||||
set_acm_max(modem, value);
|
||||
}
|
||||
|
||||
static void set_acm_max_callback(const struct ofono_error *error, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ofono_debug("Setting acm_max failed");
|
||||
dbus_gsm_pending_reply(&cm->pending,
|
||||
dbus_gsm_failed(cm->pending));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Assume if we have acm_reset, we have acm_query */
|
||||
cm->ops->acm_max_query(modem, set_acm_max_query_callback, modem);
|
||||
}
|
||||
|
||||
static DBusMessage *prop_set_acm_max(DBusMessage *msg, struct ofono_modem *modem,
|
||||
DBusMessageIter *dbus_value,
|
||||
const char *pin2)
|
||||
{
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
dbus_uint32_t value;
|
||||
|
||||
if (!cm->ops->acm_max_set)
|
||||
return dbus_gsm_not_implemented(msg);
|
||||
|
||||
dbus_message_iter_get_basic(dbus_value, &value);
|
||||
|
||||
cm->pending = dbus_message_ref(msg);
|
||||
|
||||
cm->ops->acm_max_set(modem, value, pin2, set_acm_max_callback, modem);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void set_puct_query_callback(const struct ofono_error *error,
|
||||
const char *currency, double ppu,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
DBusMessage *reply;
|
||||
|
||||
if (!cm->pending)
|
||||
return;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ofono_error("Setting PUCT successful, but query was not");
|
||||
|
||||
cm->flags &= ~CALL_METER_FLAG_CACHED;
|
||||
|
||||
dbus_gsm_pending_reply(&cm->pending,
|
||||
dbus_gsm_failed(cm->pending));
|
||||
return;
|
||||
}
|
||||
|
||||
reply = dbus_message_new_method_return(cm->pending);
|
||||
dbus_gsm_pending_reply(&cm->pending, reply);
|
||||
|
||||
set_currency(modem, currency);
|
||||
set_ppu(modem, ppu);
|
||||
}
|
||||
|
||||
static void set_puct_callback(const struct ofono_error *error, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ofono_debug("setting puct failed");
|
||||
dbus_gsm_pending_reply(&cm->pending,
|
||||
dbus_gsm_failed(cm->pending));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Assume if we have puct_set, we have puct_query */
|
||||
cm->ops->puct_query(modem, set_puct_query_callback, modem);
|
||||
}
|
||||
|
||||
/* This function is for the really bizarre case of someone trying to call
|
||||
* SetProperty before GetProperties. But we must handle it...
|
||||
*/
|
||||
static void set_puct_initial_query_callback(const struct ofono_error *error,
|
||||
const char *currency,
|
||||
double ppu, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter var;
|
||||
const char *name;
|
||||
const char *pin2;
|
||||
|
||||
if (!cm->pending)
|
||||
return;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
dbus_gsm_pending_reply(&cm->pending,
|
||||
dbus_gsm_failed(cm->pending));
|
||||
return;
|
||||
}
|
||||
|
||||
set_currency(modem, currency);
|
||||
set_ppu(modem, ppu);
|
||||
|
||||
cm->flags |= CALL_METER_FLAG_HAVE_PUCT;
|
||||
|
||||
dbus_message_iter_init(cm->pending, &iter);
|
||||
dbus_message_iter_get_basic(&iter, &name);
|
||||
dbus_message_iter_next(&iter);
|
||||
dbus_message_iter_recurse(&iter, &var);
|
||||
dbus_message_iter_next(&iter);
|
||||
dbus_message_iter_get_basic(&iter, &pin2);
|
||||
|
||||
if (!strcmp(name, "PricePerUnit"))
|
||||
dbus_message_iter_get_basic(&var, &ppu);
|
||||
else
|
||||
dbus_message_iter_get_basic(&var, ¤cy);
|
||||
|
||||
cm->ops->puct_set(modem, currency, ppu, pin2,
|
||||
set_puct_callback, modem);
|
||||
}
|
||||
|
||||
static DBusMessage *prop_set_ppu(DBusMessage *msg, struct ofono_modem *modem,
|
||||
DBusMessageIter *var, const char *pin2)
|
||||
{
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
double ppu;
|
||||
|
||||
if (!cm->ops->puct_set || !cm->ops->puct_query)
|
||||
return dbus_gsm_not_implemented(msg);
|
||||
|
||||
dbus_message_iter_get_basic(var, &ppu);
|
||||
|
||||
if (ppu < 0.0)
|
||||
return dbus_gsm_invalid_format(msg);
|
||||
|
||||
cm->pending = dbus_message_ref(msg);
|
||||
|
||||
if (cm->flags & CALL_METER_FLAG_HAVE_PUCT)
|
||||
cm->ops->puct_set(modem, cm->currency, ppu, pin2,
|
||||
set_puct_callback, modem);
|
||||
else
|
||||
cm->ops->puct_query(modem, set_puct_initial_query_callback,
|
||||
modem);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static DBusMessage *prop_set_cur(DBusMessage *msg, struct ofono_modem *modem,
|
||||
DBusMessageIter *var, const char *pin2)
|
||||
{
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
const char *value;
|
||||
|
||||
if (!cm->ops->puct_set || !cm->ops->puct_query)
|
||||
return dbus_gsm_not_implemented(msg);
|
||||
|
||||
dbus_message_iter_get_basic(var, &value);
|
||||
|
||||
if (strlen(value) > 3)
|
||||
return dbus_gsm_invalid_format(msg);
|
||||
|
||||
cm->pending = dbus_message_ref(msg);
|
||||
|
||||
if (cm->flags & CALL_METER_FLAG_HAVE_PUCT)
|
||||
cm->ops->puct_set(modem, value, cm->ppu, pin2,
|
||||
set_puct_callback, modem);
|
||||
else
|
||||
cm->ops->puct_query(modem, set_puct_initial_query_callback,
|
||||
modem);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct call_meter_property {
|
||||
const char *name;
|
||||
int type;
|
||||
DBusMessage* (*set)(DBusMessage *msg, struct ofono_modem *modem,
|
||||
DBusMessageIter *var, const char *pin2);
|
||||
};
|
||||
|
||||
static struct call_meter_property cm_properties[] = {
|
||||
{ "AccumulatedCallMeterMaximum",DBUS_TYPE_UINT32, prop_set_acm_max },
|
||||
{ "PricePerUnit", DBUS_TYPE_DOUBLE, prop_set_ppu },
|
||||
{ "Currency", DBUS_TYPE_STRING, prop_set_cur },
|
||||
{ NULL, 0, 0 },
|
||||
};
|
||||
|
||||
static DBusMessage *cm_set_property(DBusConnection *conn, DBusMessage *msg,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter var;
|
||||
const char *name, *passwd = "";
|
||||
struct call_meter_property *property;
|
||||
|
||||
if (cm->pending)
|
||||
return dbus_gsm_busy(msg);
|
||||
|
||||
if (!dbus_message_iter_init(msg, &iter))
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
|
||||
dbus_message_iter_get_basic(&iter, &name);
|
||||
|
||||
dbus_message_iter_next(&iter);
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
|
||||
dbus_message_iter_recurse(&iter, &var);
|
||||
|
||||
if (!dbus_message_iter_next(&iter))
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
|
||||
dbus_message_iter_get_basic(&iter, &passwd);
|
||||
|
||||
if (!is_valid_pin(passwd))
|
||||
return dbus_gsm_invalid_format(msg);
|
||||
|
||||
for (property = cm_properties; property->name; property++) {
|
||||
if (strcmp(name, property->name))
|
||||
continue;
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&var) != property->type)
|
||||
return dbus_gsm_invalid_format(msg);
|
||||
|
||||
return property->set(msg, modem, &var, passwd);
|
||||
}
|
||||
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
}
|
||||
|
||||
static void reset_acm_query_callback(const struct ofono_error *error, int value,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
DBusMessage *reply;
|
||||
|
||||
if (!cm->pending)
|
||||
return;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ofono_error("Reseting ACM successful, but query was not");
|
||||
|
||||
cm->flags &= ~CALL_METER_FLAG_CACHED;
|
||||
|
||||
dbus_gsm_pending_reply(&cm->pending,
|
||||
dbus_gsm_failed(cm->pending));
|
||||
return;
|
||||
}
|
||||
|
||||
reply = dbus_message_new_method_return(cm->pending);
|
||||
dbus_gsm_pending_reply(&cm->pending, reply);
|
||||
|
||||
set_acm(modem, value);
|
||||
}
|
||||
|
||||
static void acm_reset_callback(const struct ofono_error *error, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ofono_debug("reseting acm failed");
|
||||
dbus_gsm_pending_reply(&cm->pending,
|
||||
dbus_gsm_failed(cm->pending));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Assume if we have acm_reset, we have acm_query */
|
||||
cm->ops->acm_query(modem, reset_acm_query_callback, modem);
|
||||
}
|
||||
|
||||
static DBusMessage *cm_acm_reset(DBusConnection *conn, DBusMessage *msg,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_meter_data *cm = modem->call_meter;
|
||||
DBusMessageIter iter;
|
||||
const char *pin2;
|
||||
|
||||
if (cm->pending)
|
||||
return dbus_gsm_busy(msg);
|
||||
|
||||
if (!dbus_message_iter_init(msg, &iter))
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
|
||||
dbus_message_iter_get_basic(&iter, &pin2);
|
||||
|
||||
if (!is_valid_pin(pin2))
|
||||
return dbus_gsm_invalid_format(msg);
|
||||
|
||||
if (!cm->ops->acm_reset)
|
||||
return dbus_gsm_not_implemented(msg);
|
||||
|
||||
cm->pending = dbus_message_ref(msg);
|
||||
|
||||
cm->ops->acm_reset(modem, pin2, acm_reset_callback, modem);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GDBusMethodTable cm_methods[] = {
|
||||
{ "GetProperties", "", "a{sv}", cm_get_properties,
|
||||
G_DBUS_METHOD_FLAG_ASYNC },
|
||||
{ "SetProperty", "svs", "", cm_set_property,
|
||||
G_DBUS_METHOD_FLAG_ASYNC },
|
||||
{ "Reset", "s", "", cm_acm_reset,
|
||||
G_DBUS_METHOD_FLAG_ASYNC },
|
||||
{ }
|
||||
};
|
||||
|
||||
static GDBusSignalTable cm_signals[] = {
|
||||
{ "PropertyChanged", "sv" },
|
||||
{ "NearMaximumWarning", "" },
|
||||
{ }
|
||||
};
|
||||
|
||||
void ofono_call_meter_changed_notify(struct ofono_modem *modem, int new_value)
|
||||
{
|
||||
set_call_meter(modem, new_value);
|
||||
}
|
||||
|
||||
void ofono_call_meter_maximum_notify(struct ofono_modem *modem)
|
||||
{
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
DBusMessage *signal;
|
||||
|
||||
signal = dbus_message_new_signal(modem->path,
|
||||
CALL_METER_INTERFACE, "NearMaximumWarning");
|
||||
if (!signal) {
|
||||
ofono_error("Unable to allocate new %s.NearMaximumWarning "
|
||||
"signal", CALL_METER_INTERFACE);
|
||||
return;
|
||||
}
|
||||
|
||||
g_dbus_send_message(conn, signal);
|
||||
}
|
||||
|
||||
int ofono_call_meter_register(struct ofono_modem *modem,
|
||||
struct ofono_call_meter_ops *ops)
|
||||
{
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
if (!modem || !ops)
|
||||
return -1;
|
||||
|
||||
modem->call_meter = call_meter_create();
|
||||
|
||||
if (!modem->call_meter)
|
||||
return -1;
|
||||
|
||||
modem->call_meter->ops = ops;
|
||||
|
||||
if (!g_dbus_register_interface(conn, modem->path, CALL_METER_INTERFACE,
|
||||
cm_methods, cm_signals, NULL, modem,
|
||||
call_meter_destroy)) {
|
||||
ofono_error("Could not create %s interface",
|
||||
CALL_METER_INTERFACE);
|
||||
call_meter_destroy(modem);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
modem_add_interface(modem, CALL_METER_INTERFACE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ofono_call_meter_unregister(struct ofono_modem *modem)
|
||||
{
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
if (!modem->call_meter)
|
||||
return;
|
||||
|
||||
modem_remove_interface(modem, CALL_METER_INTERFACE);
|
||||
g_dbus_unregister_interface(conn, modem->path, CALL_METER_INTERFACE);
|
||||
|
||||
modem->call_meter = NULL;
|
||||
}
|
|
@ -0,0 +1,900 @@
|
|||
/*
|
||||
*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include <glib.h>
|
||||
#include <gdbus.h>
|
||||
|
||||
#include "ofono.h"
|
||||
|
||||
#include "driver.h"
|
||||
#include "common.h"
|
||||
#include "dbus-gsm.h"
|
||||
#include "modem.h"
|
||||
#include "ussd.h"
|
||||
|
||||
#define CALL_SETTINGS_INTERFACE "org.ofono.CallSettings"
|
||||
|
||||
#define CALL_SETTINGS_FLAG_CACHED 0x1
|
||||
|
||||
struct call_settings_data {
|
||||
struct ofono_call_settings_ops *ops;
|
||||
int clir;
|
||||
int colr;
|
||||
int clip;
|
||||
int colp;
|
||||
int clir_setting;
|
||||
int flags;
|
||||
DBusMessage *pending;
|
||||
int ss_req_type;
|
||||
int call_setting_type;
|
||||
};
|
||||
|
||||
enum call_setting_type {
|
||||
CALL_SETTING_TYPE_CLIP = 0,
|
||||
CALL_SETTING_TYPE_COLP,
|
||||
CALL_SETTING_TYPE_COLR,
|
||||
CALL_SETTING_TYPE_CLIR
|
||||
};
|
||||
|
||||
static void cs_register_ss_controls(struct ofono_modem *modem);
|
||||
static void cs_unregister_ss_controls(struct ofono_modem *modem);
|
||||
|
||||
static const char *clip_status_to_string(int status)
|
||||
{
|
||||
switch (status) {
|
||||
case CLIP_STATUS_NOT_PROVISIONED:
|
||||
return "disabled";
|
||||
case CLIP_STATUS_PROVISIONED:
|
||||
return "enabled";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *colp_status_to_string(int status)
|
||||
{
|
||||
switch (status) {
|
||||
case COLP_STATUS_NOT_PROVISIONED:
|
||||
return "disabled";
|
||||
case COLP_STATUS_PROVISIONED:
|
||||
return "enabled";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *colr_status_to_string(int status)
|
||||
{
|
||||
switch (status) {
|
||||
case COLR_STATUS_NOT_PROVISIONED:
|
||||
return "disabled";
|
||||
case COLR_STATUS_PROVISIONED:
|
||||
return "enabled";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *hide_callerid_to_string(int status)
|
||||
{
|
||||
switch (status) {
|
||||
case OFONO_CLIR_OPTION_DEFAULT:
|
||||
return "default";
|
||||
case OFONO_CLIR_OPTION_INVOCATION:
|
||||
return "enabled";
|
||||
case OFONO_CLIR_OPTION_SUPPRESSION:
|
||||
return "disabled";
|
||||
default:
|
||||
return "default";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *clir_status_to_string(int status)
|
||||
{
|
||||
switch (status) {
|
||||
case CLIR_STATUS_NOT_PROVISIONED:
|
||||
return "disabled";
|
||||
case CLIR_STATUS_PROVISIONED_PERMANENT:
|
||||
return "permanent";
|
||||
case CLIR_STATUS_TEMPORARY_RESTRICTED:
|
||||
return "on";
|
||||
case CLIR_STATUS_TEMPORARY_ALLOWED:
|
||||
return "off";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void set_clir_network(struct ofono_modem *modem, int clir)
|
||||
{
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
|
||||
if (cs->clir != clir) {
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
const char *str = clir_status_to_string(clir);
|
||||
|
||||
cs->clir = clir;
|
||||
|
||||
dbus_gsm_signal_property_changed(conn, modem->path,
|
||||
CALL_SETTINGS_INTERFACE,
|
||||
"CallingLineRestriction",
|
||||
DBUS_TYPE_STRING, &str);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_clir_override(struct ofono_modem *modem, int override)
|
||||
{
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
|
||||
if (cs->clir_setting != override) {
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
const char *str = hide_callerid_to_string(override);
|
||||
|
||||
cs->clir_setting = override;
|
||||
|
||||
dbus_gsm_signal_property_changed(conn, modem->path,
|
||||
CALL_SETTINGS_INTERFACE,
|
||||
"HideCallerId", DBUS_TYPE_STRING, &str);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_clip(struct ofono_modem *modem, int clip)
|
||||
{
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
|
||||
if (cs->clip != clip) {
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
const char *str = clip_status_to_string(clip);
|
||||
|
||||
cs->clip = clip;
|
||||
|
||||
dbus_gsm_signal_property_changed(conn, modem->path,
|
||||
CALL_SETTINGS_INTERFACE,
|
||||
"CallingLinePresentation",
|
||||
DBUS_TYPE_STRING, &str);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_colp(struct ofono_modem *modem, int colp)
|
||||
{
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
|
||||
if (cs->colp != colp) {
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
const char *str = colp_status_to_string(colp);
|
||||
|
||||
cs->colp = colp;
|
||||
|
||||
dbus_gsm_signal_property_changed(conn, modem->path,
|
||||
CALL_SETTINGS_INTERFACE,
|
||||
"CalledLinePresentation",
|
||||
DBUS_TYPE_STRING, &str);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_colr(struct ofono_modem *modem, int colr)
|
||||
{
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
|
||||
if (cs->colr != colr) {
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
const char *str = colr_status_to_string(colr);
|
||||
|
||||
cs->colr = colr;
|
||||
|
||||
dbus_gsm_signal_property_changed(conn, modem->path,
|
||||
CALL_SETTINGS_INTERFACE,
|
||||
"CalledLineRestriction",
|
||||
DBUS_TYPE_STRING, &str);
|
||||
}
|
||||
}
|
||||
|
||||
static struct call_settings_data *call_settings_create()
|
||||
{
|
||||
struct call_settings_data *r;
|
||||
|
||||
r = g_try_new0(struct call_settings_data, 1);
|
||||
|
||||
if (!r)
|
||||
return r;
|
||||
|
||||
/* Set all the settings to unknown state */
|
||||
r->clip = 2;
|
||||
r->clir = 2;
|
||||
r->colp = 2;
|
||||
r->colr = 2;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void call_settings_destroy(gpointer data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
|
||||
cs_unregister_ss_controls(modem);
|
||||
|
||||
g_free(cs);
|
||||
}
|
||||
|
||||
static void generate_ss_query_reply(struct ofono_modem *modem,
|
||||
const char *context, const char *value)
|
||||
{
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
const char *sig = "(ss)";
|
||||
const char *ss_type = ss_control_type_to_string(cs->ss_req_type);
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter var;
|
||||
DBusMessageIter vstruct;
|
||||
DBusMessage *reply;
|
||||
|
||||
reply = dbus_message_new_method_return(cs->pending);
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &context);
|
||||
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig, &var);
|
||||
|
||||
dbus_message_iter_open_container(&var, DBUS_TYPE_STRUCT, NULL,
|
||||
&vstruct);
|
||||
|
||||
dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING,
|
||||
&ss_type);
|
||||
|
||||
dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING, &value);
|
||||
|
||||
dbus_message_iter_close_container(&var, &vstruct);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &var);
|
||||
|
||||
dbus_gsm_pending_reply(&cs->pending, reply);
|
||||
}
|
||||
|
||||
static void clip_colp_colr_ss_query_cb(const struct ofono_error *error,
|
||||
int status, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
const char *context;
|
||||
const char *value;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ofono_debug("Error occurred during ss control query");
|
||||
dbus_gsm_pending_reply(&cs->pending,
|
||||
dbus_gsm_failed(cs->pending));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cs->call_setting_type) {
|
||||
case CALL_SETTING_TYPE_CLIP:
|
||||
set_clip(modem, status);
|
||||
value = clip_status_to_string(status);
|
||||
context = "CallingLinePresentation";
|
||||
break;
|
||||
|
||||
case CALL_SETTING_TYPE_COLP:
|
||||
set_colp(modem, status);
|
||||
value = colp_status_to_string(status);
|
||||
context = "CalledLinePresentation";
|
||||
break;
|
||||
|
||||
case CALL_SETTING_TYPE_COLR:
|
||||
set_colr(modem, status);
|
||||
value = colr_status_to_string(status);
|
||||
context = "CallingLineRestriction";
|
||||
break;
|
||||
|
||||
default:
|
||||
dbus_gsm_pending_reply(&cs->pending,
|
||||
dbus_gsm_failed(cs->pending));
|
||||
ofono_error("Unknown type during COLR/COLP/CLIP ss");
|
||||
return;
|
||||
};
|
||||
|
||||
generate_ss_query_reply(modem, context, value);
|
||||
}
|
||||
|
||||
static gboolean clip_colp_colr_ss(struct ofono_modem *modem, int type,
|
||||
const char *sc, const char *sia,
|
||||
const char *sib, const char *sic,
|
||||
const char *dn, DBusMessage *msg)
|
||||
{
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
void (*query_op)(struct ofono_modem *modem, ofono_call_setting_status_cb_t cb,
|
||||
void *data);
|
||||
|
||||
if (!cs)
|
||||
return FALSE;
|
||||
|
||||
if (cs->pending) {
|
||||
DBusMessage *reply = dbus_gsm_busy(msg);
|
||||
g_dbus_send_message(conn, reply);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!strcmp(sc, "30")) {
|
||||
cs->call_setting_type = CALL_SETTING_TYPE_CLIP;
|
||||
query_op = cs->ops->clip_query;
|
||||
} else if (!strcmp(sc, "76")) {
|
||||
cs->call_setting_type = CALL_SETTING_TYPE_COLP;
|
||||
query_op = cs->ops->colp_query;
|
||||
} else if (!strcmp(sc, "77")) {
|
||||
cs->call_setting_type = CALL_SETTING_TYPE_COLR;
|
||||
query_op = cs->ops->colr_query;
|
||||
} else
|
||||
return FALSE;
|
||||
|
||||
if (type != SS_CONTROL_TYPE_QUERY || strlen(sia) || strlen(sib) ||
|
||||
strlen(sic) || strlen(dn)) {
|
||||
DBusMessage *reply = dbus_gsm_invalid_format(msg);
|
||||
g_dbus_send_message(conn, reply);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!query_op) {
|
||||
DBusMessage *reply = dbus_gsm_not_implemented(msg);
|
||||
g_dbus_send_message(conn, reply);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ofono_debug("Received CLIP/COLR/COLP query ss control");
|
||||
|
||||
cs->pending = dbus_message_ref(msg);
|
||||
|
||||
query_op(modem, clip_colp_colr_ss_query_cb, modem);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void clir_ss_query_callback(const struct ofono_error *error,
|
||||
int override, int network, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
const char *value;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ofono_debug("setting clir via SS failed");
|
||||
dbus_gsm_pending_reply(&cs->pending,
|
||||
dbus_gsm_failed(cs->pending));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (network) {
|
||||
case CLIR_STATUS_UNKNOWN:
|
||||
value = "uknown";
|
||||
break;
|
||||
|
||||
case CLIR_STATUS_PROVISIONED_PERMANENT:
|
||||
value = "enabled";
|
||||
break;
|
||||
|
||||
case CLIR_STATUS_NOT_PROVISIONED:
|
||||
value = "disabled";
|
||||
break;
|
||||
|
||||
case CLIR_STATUS_TEMPORARY_RESTRICTED:
|
||||
if (override == OFONO_CLIR_OPTION_SUPPRESSION)
|
||||
value = "enabled";
|
||||
else
|
||||
value = "disabled";
|
||||
break;
|
||||
|
||||
case CLIR_STATUS_TEMPORARY_ALLOWED:
|
||||
if (override == OFONO_CLIR_OPTION_INVOCATION)
|
||||
value = "enabled";
|
||||
else
|
||||
value = "disabled";
|
||||
break;
|
||||
};
|
||||
|
||||
generate_ss_query_reply(modem, "CallingLineRestriction", value);
|
||||
|
||||
set_clir_network(modem, network);
|
||||
set_clir_override(modem, override);
|
||||
}
|
||||
|
||||
static void clir_ss_set_callback(const struct ofono_error *error, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ofono_debug("setting clir via SS failed");
|
||||
dbus_gsm_pending_reply(&cs->pending,
|
||||
dbus_gsm_failed(cs->pending));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
cs->ops->clir_query(modem, clir_ss_query_callback, modem);
|
||||
}
|
||||
|
||||
static gboolean clir_ss_control(struct ofono_modem *modem, int type,
|
||||
const char *sc, const char *sia,
|
||||
const char *sib, const char *sic,
|
||||
const char *dn, DBusMessage *msg)
|
||||
{
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
//void *op;
|
||||
|
||||
if (!cs)
|
||||
return FALSE;
|
||||
|
||||
if (strcmp(sc, "31"))
|
||||
return FALSE;
|
||||
|
||||
if (cs->pending) {
|
||||
DBusMessage *reply = dbus_gsm_busy(msg);
|
||||
g_dbus_send_message(conn, reply);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* This is the temporary form of CLIR, handled in voicecalls */
|
||||
if (!strlen(sia) && !strlen(sib) & !strlen(sic) &&
|
||||
strlen(dn) && type != SS_CONTROL_TYPE_QUERY)
|
||||
return FALSE;
|
||||
|
||||
if (strlen(sia) || strlen(sib) || strlen(sic) || strlen(dn)) {
|
||||
DBusMessage *reply = dbus_gsm_invalid_format(msg);
|
||||
g_dbus_send_message(conn, reply);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if ((type == SS_CONTROL_TYPE_QUERY && !cs->ops->clir_query) ||
|
||||
(type != SS_CONTROL_TYPE_QUERY && !cs->ops->clir_set)) {
|
||||
DBusMessage *reply = dbus_gsm_not_implemented(msg);
|
||||
g_dbus_send_message(conn, reply);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cs->call_setting_type = CALL_SETTING_TYPE_CLIR;
|
||||
cs->pending = dbus_message_ref(msg);
|
||||
|
||||
switch (type) {
|
||||
case SS_CONTROL_TYPE_REGISTRATION:
|
||||
case SS_CONTROL_TYPE_ACTIVATION:
|
||||
cs->ss_req_type = SS_CONTROL_TYPE_ACTIVATION;
|
||||
cs->ops->clir_set(modem, OFONO_CLIR_OPTION_INVOCATION,
|
||||
clir_ss_set_callback, modem);
|
||||
break;
|
||||
|
||||
case SS_CONTROL_TYPE_QUERY:
|
||||
cs->ss_req_type = SS_CONTROL_TYPE_QUERY;
|
||||
cs->ops->clir_query(modem, clir_ss_query_callback,
|
||||
modem);
|
||||
break;
|
||||
|
||||
case SS_CONTROL_TYPE_DEACTIVATION:
|
||||
case SS_CONTROL_TYPE_ERASURE:
|
||||
cs->ss_req_type = SS_CONTROL_TYPE_DEACTIVATION;
|
||||
cs->ops->clir_set(modem, OFONO_CLIR_OPTION_SUPPRESSION,
|
||||
clir_ss_set_callback, modem);
|
||||
break;
|
||||
};
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void cs_register_ss_controls(struct ofono_modem *modem)
|
||||
{
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
|
||||
ss_control_register(modem, "30", clip_colp_colr_ss);
|
||||
ss_control_register(modem, "31", clir_ss_control);
|
||||
ss_control_register(modem, "76", clip_colp_colr_ss);
|
||||
|
||||
if (cs->ops->colr_query)
|
||||
ss_control_register(modem, "77", clip_colp_colr_ss);
|
||||
}
|
||||
|
||||
static void cs_unregister_ss_controls(struct ofono_modem *modem)
|
||||
{
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
|
||||
ss_control_unregister(modem, "30", clip_colp_colr_ss);
|
||||
ss_control_unregister(modem, "31", clir_ss_control);
|
||||
ss_control_unregister(modem, "76", clip_colp_colr_ss);
|
||||
|
||||
if (cs->ops->colr_query)
|
||||
ss_control_unregister(modem, "77", clip_colp_colr_ss);
|
||||
}
|
||||
|
||||
static DBusMessage *generate_get_properties_reply(struct ofono_modem *modem,
|
||||
DBusMessage *msg)
|
||||
{
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
DBusMessage *reply;
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter dict;
|
||||
const char *str;
|
||||
|
||||
reply = dbus_message_new_method_return(msg);
|
||||
|
||||
if (!reply)
|
||||
return NULL;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
|
||||
PROPERTIES_ARRAY_SIGNATURE,
|
||||
&dict);
|
||||
|
||||
str = clip_status_to_string(cs->clip);
|
||||
dbus_gsm_dict_append(&dict, "CallingLinePresentation",
|
||||
DBUS_TYPE_STRING, &str);
|
||||
|
||||
str = colp_status_to_string(cs->colp);
|
||||
dbus_gsm_dict_append(&dict, "CalledLinePresentation",
|
||||
DBUS_TYPE_STRING, &str);
|
||||
|
||||
str = colr_status_to_string(cs->colr);
|
||||
dbus_gsm_dict_append(&dict, "CalledLineRestriction",
|
||||
DBUS_TYPE_STRING, &str);
|
||||
|
||||
str = clir_status_to_string(cs->clir);
|
||||
dbus_gsm_dict_append(&dict, "CallingLineRestriction",
|
||||
DBUS_TYPE_STRING, &str);
|
||||
|
||||
str = hide_callerid_to_string(cs->clir_setting);
|
||||
dbus_gsm_dict_append(&dict, "HideCallerId", DBUS_TYPE_STRING, &str);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &dict);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
static void cs_clir_callback(const struct ofono_error *error,
|
||||
int override_setting, int network_setting,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
//DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
|
||||
goto out;
|
||||
|
||||
set_clir_network(modem, network_setting);
|
||||
set_clir_override(modem, override_setting);
|
||||
|
||||
cs->flags |= CALL_SETTINGS_FLAG_CACHED;
|
||||
|
||||
out:
|
||||
if (cs->pending) {
|
||||
DBusMessage *reply = generate_get_properties_reply(modem,
|
||||
cs->pending);
|
||||
dbus_gsm_pending_reply(&cs->pending, reply);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean query_clir(gpointer user)
|
||||
{
|
||||
struct ofono_modem *modem = user;
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
|
||||
if (!cs->ops->clir_query) {
|
||||
if (cs->pending) {
|
||||
DBusMessage *reply =
|
||||
generate_get_properties_reply(modem,
|
||||
cs->pending);
|
||||
dbus_gsm_pending_reply(&cs->pending, reply);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cs->ops->clir_query(modem, cs_clir_callback, modem);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void cs_clip_callback(const struct ofono_error *error,
|
||||
int state, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
|
||||
if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
|
||||
set_clip(modem, state);
|
||||
|
||||
g_timeout_add(0, query_clir, modem);
|
||||
}
|
||||
|
||||
static gboolean query_clip(gpointer user)
|
||||
{
|
||||
struct ofono_modem *modem = user;
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
|
||||
if (!cs->ops->clip_query) {
|
||||
query_clir(modem);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cs->ops->clip_query(modem, cs_clip_callback, modem);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void cs_colp_callback(const struct ofono_error *error,
|
||||
int state, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
|
||||
if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
|
||||
set_colp(modem, state);
|
||||
|
||||
g_timeout_add(0, query_clip, modem);
|
||||
}
|
||||
|
||||
static gboolean query_colp(gpointer user)
|
||||
{
|
||||
struct ofono_modem *modem = user;
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
|
||||
if (!cs->ops->colp_query) {
|
||||
query_clip(modem);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cs->ops->colp_query(modem, cs_colp_callback, modem);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void cs_colr_callback(const struct ofono_error *error,
|
||||
int state, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
|
||||
if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
|
||||
set_colr(modem, state);
|
||||
|
||||
g_timeout_add(0, query_colp, modem);
|
||||
}
|
||||
|
||||
static gboolean query_colr(gpointer user)
|
||||
{
|
||||
struct ofono_modem *modem = user;
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
|
||||
if (!cs->ops->colr_query) {
|
||||
query_colp(modem);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cs->ops->colr_query(modem, cs_colr_callback, modem);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static DBusMessage *cs_get_properties(DBusConnection *conn, DBusMessage *msg,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
|
||||
if (cs->pending)
|
||||
return dbus_gsm_busy(msg);
|
||||
|
||||
if (cs->flags & CALL_SETTINGS_FLAG_CACHED)
|
||||
return generate_get_properties_reply(modem, msg);
|
||||
|
||||
/* Query the settings and report back */
|
||||
cs->pending = dbus_message_ref(msg);
|
||||
|
||||
query_colr(modem);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void clir_set_query_callback(const struct ofono_error *error,
|
||||
int override_setting,
|
||||
int network_setting, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
DBusMessage *reply;
|
||||
|
||||
if (!cs->pending)
|
||||
return;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ofono_error("setting clir was successful, but the query was not");
|
||||
|
||||
cs->flags &= ~CALL_SETTINGS_FLAG_CACHED;
|
||||
|
||||
reply = dbus_gsm_failed(cs->pending);
|
||||
dbus_gsm_pending_reply(&cs->pending, reply);
|
||||
return;
|
||||
}
|
||||
|
||||
reply = dbus_message_new_method_return(cs->pending);
|
||||
dbus_gsm_pending_reply(&cs->pending, reply);
|
||||
|
||||
set_clir_override(modem, override_setting);
|
||||
set_clir_network(modem, network_setting);
|
||||
}
|
||||
|
||||
static void clir_set_callback(const struct ofono_error *error, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ofono_debug("setting clir failed");
|
||||
dbus_gsm_pending_reply(&cs->pending,
|
||||
dbus_gsm_failed(cs->pending));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Assume that if we have clir_set, we have clir_query */
|
||||
cs->ops->clir_query(modem, clir_set_query_callback, modem);
|
||||
}
|
||||
|
||||
static DBusMessage *set_clir(DBusMessage *msg, struct ofono_modem *modem,
|
||||
const char *setting)
|
||||
{
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
int clir = -1;
|
||||
|
||||
if (cs->ops->clir_set == NULL)
|
||||
return dbus_gsm_not_implemented(msg);
|
||||
|
||||
if (!strcmp(setting, "default"))
|
||||
clir = 0;
|
||||
else if (!strcmp(setting, "enabled"))
|
||||
clir = 1;
|
||||
else if (!strcmp(setting, "disabled"))
|
||||
clir = 2;
|
||||
|
||||
if (clir == -1)
|
||||
return dbus_gsm_invalid_format(msg);
|
||||
|
||||
cs->pending = dbus_message_ref(msg);
|
||||
|
||||
cs->ops->clir_set(modem, clir, clir_set_callback, modem);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static DBusMessage *cs_set_property(DBusConnection *conn, DBusMessage *msg,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter var;
|
||||
const char *property;
|
||||
|
||||
if (cs->pending)
|
||||
return dbus_gsm_busy(msg);
|
||||
|
||||
if (!dbus_message_iter_init(msg, &iter))
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
|
||||
dbus_message_iter_get_basic(&iter, &property);
|
||||
dbus_message_iter_next(&iter);
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
|
||||
dbus_message_iter_recurse(&iter, &var);
|
||||
|
||||
if (!strcmp(property, "HideCallerId")) {
|
||||
const char *setting;
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
|
||||
return dbus_gsm_invalid_format(msg);
|
||||
|
||||
dbus_message_iter_get_basic(&var, &setting);
|
||||
|
||||
return set_clir(msg, modem, setting);
|
||||
}
|
||||
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
}
|
||||
|
||||
static GDBusMethodTable cs_methods[] = {
|
||||
{ "GetProperties", "", "a{sv}", cs_get_properties,
|
||||
G_DBUS_METHOD_FLAG_ASYNC },
|
||||
{ "SetProperty", "sv", "", cs_set_property,
|
||||
G_DBUS_METHOD_FLAG_ASYNC },
|
||||
{ }
|
||||
};
|
||||
|
||||
static GDBusSignalTable cs_signals[] = {
|
||||
{ "PropertyChanged", "sv" },
|
||||
{ }
|
||||
};
|
||||
|
||||
int ofono_call_settings_register(struct ofono_modem *modem,
|
||||
struct ofono_call_settings_ops *ops)
|
||||
{
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
if (modem == NULL)
|
||||
return -1;
|
||||
|
||||
if (ops == NULL)
|
||||
return -1;
|
||||
|
||||
modem->call_settings = call_settings_create();
|
||||
|
||||
if (!modem->call_settings)
|
||||
return -1;
|
||||
|
||||
modem->call_settings->ops = ops;
|
||||
|
||||
if (!g_dbus_register_interface(conn, modem->path,
|
||||
CALL_SETTINGS_INTERFACE,
|
||||
cs_methods, cs_signals, NULL,
|
||||
modem, call_settings_destroy)) {
|
||||
ofono_error("Could not register CallSettings %s", modem->path);
|
||||
call_settings_destroy(modem);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ofono_debug("Registered call settings interface");
|
||||
|
||||
cs_register_ss_controls(modem);
|
||||
|
||||
modem_add_interface(modem, CALL_SETTINGS_INTERFACE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ofono_call_settings_unregister(struct ofono_modem *modem)
|
||||
{
|
||||
struct call_settings_data *cs = modem->call_settings;
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
if (!cs)
|
||||
return;
|
||||
|
||||
modem_remove_interface(modem, CALL_SETTINGS_INTERFACE);
|
||||
g_dbus_unregister_interface(conn, modem->path,
|
||||
CALL_SETTINGS_INTERFACE);
|
||||
|
||||
modem->call_settings = NULL;
|
||||
}
|
|
@ -0,0 +1,648 @@
|
|||
/*
|
||||
*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include <glib.h>
|
||||
#include <gdbus.h>
|
||||
|
||||
#include "ofono.h"
|
||||
|
||||
#include "driver.h"
|
||||
#include "common.h"
|
||||
#include "dbus-gsm.h"
|
||||
#include "modem.h"
|
||||
#include "ussd.h"
|
||||
|
||||
#define CALL_WAITING_INTERFACE "org.ofono.CallWaiting"
|
||||
|
||||
#define CALL_WAITING_FLAG_CACHED 0x1
|
||||
|
||||
struct call_waiting_data {
|
||||
struct ofono_call_waiting_ops *ops;
|
||||
int flags;
|
||||
DBusMessage *pending;
|
||||
GSList *cw_list;
|
||||
int ss_req_type;
|
||||
int ss_req_cls;
|
||||
};
|
||||
|
||||
static const char *enabled = "enabled";
|
||||
static const char *disabled = "disabled";
|
||||
|
||||
static void cw_register_ss_controls(struct ofono_modem *modem);
|
||||
static void cw_unregister_ss_controls(struct ofono_modem *modem);
|
||||
|
||||
static gint cw_condition_compare(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
const struct ofono_cw_condition *ca = a;
|
||||
const struct ofono_cw_condition *cb = b;
|
||||
|
||||
if (ca->cls < cb->cls)
|
||||
return -1;
|
||||
|
||||
if (ca->cls > cb->cls)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gint cw_condition_find_with_cls(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
const struct ofono_cw_condition *c = a;
|
||||
int cls = GPOINTER_TO_INT(b);
|
||||
|
||||
if (c->cls < cls)
|
||||
return -1;
|
||||
|
||||
if (c->cls > cls)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct call_waiting_data *call_waiting_create()
|
||||
{
|
||||
struct call_waiting_data *r;
|
||||
|
||||
r = g_try_new0(struct call_waiting_data, 1);
|
||||
|
||||
if (!r)
|
||||
return r;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void call_waiting_destroy(gpointer data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_waiting_data *cw = modem->call_waiting;
|
||||
|
||||
cw_unregister_ss_controls(modem);
|
||||
|
||||
g_slist_foreach(cw->cw_list, (GFunc)g_free, NULL);
|
||||
g_slist_free(cw->cw_list);
|
||||
|
||||
g_free(cw);
|
||||
}
|
||||
|
||||
static void cw_cond_list_print(GSList *list)
|
||||
{
|
||||
GSList *l;
|
||||
struct ofono_cw_condition *cond;
|
||||
|
||||
for (l = list; l; l = l->next) {
|
||||
cond = l->data;
|
||||
|
||||
ofono_debug("CW condition status: %d, class: %d",
|
||||
cond->status, cond->cls);
|
||||
}
|
||||
}
|
||||
|
||||
static GSList *cw_cond_list_create(int total,
|
||||
const struct ofono_cw_condition *list)
|
||||
{
|
||||
GSList *l = NULL;
|
||||
int i;
|
||||
int j;
|
||||
struct ofono_cw_condition *cond;
|
||||
|
||||
/* Specification is not really clear on how the results are reported,
|
||||
* most modems report it as multiple list items, one for each class
|
||||
* however, specification does leave room for a single compound value
|
||||
* to be reported
|
||||
*/
|
||||
for (i = 0; i < total; i++) {
|
||||
for (j = 1; j <= BEARER_CLASS_PAD; j = j << 1) {
|
||||
if (!(list[i].cls & j))
|
||||
continue;
|
||||
|
||||
if (list[i].status == 0)
|
||||
continue;
|
||||
|
||||
cond = g_new0(struct ofono_cw_condition, 1);
|
||||
|
||||
memcpy(cond, &list[i], sizeof(struct ofono_cw_condition));
|
||||
cond->cls = j;
|
||||
|
||||
l = g_slist_insert_sorted(l, cond,
|
||||
cw_condition_compare);
|
||||
}
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
static void set_new_cond_list(struct ofono_modem *modem, GSList *new_cw_list)
|
||||
{
|
||||
struct call_waiting_data *cw = modem->call_waiting;
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
GSList *n;
|
||||
GSList *o;
|
||||
struct ofono_cw_condition *nc;
|
||||
struct ofono_cw_condition *oc;
|
||||
char buf[64];
|
||||
|
||||
for (n = new_cw_list; n; n = n->next) {
|
||||
nc = n->data;
|
||||
|
||||
if (nc->cls > BEARER_CLASS_FAX)
|
||||
continue;
|
||||
|
||||
sprintf(buf, "%s", bearer_class_to_string(nc->cls));
|
||||
|
||||
o = g_slist_find_custom(cw->cw_list, GINT_TO_POINTER(nc->cls),
|
||||
cw_condition_find_with_cls);
|
||||
|
||||
if (o) {
|
||||
g_free(o->data);
|
||||
cw->cw_list = g_slist_remove(cw->cw_list, o->data);
|
||||
} else {
|
||||
dbus_gsm_signal_property_changed(conn, modem->path,
|
||||
CALL_WAITING_INTERFACE,
|
||||
buf, DBUS_TYPE_STRING,
|
||||
&enabled);
|
||||
}
|
||||
}
|
||||
|
||||
for (o = cw->cw_list; o; o = o->next) {
|
||||
oc = o->data;
|
||||
|
||||
sprintf(buf, "%s", bearer_class_to_string(oc->cls));
|
||||
|
||||
dbus_gsm_signal_property_changed(conn, modem->path,
|
||||
CALL_WAITING_INTERFACE,
|
||||
buf, DBUS_TYPE_STRING,
|
||||
&disabled);
|
||||
}
|
||||
|
||||
g_slist_foreach(cw->cw_list, (GFunc)g_free, NULL);
|
||||
g_slist_free(cw->cw_list);
|
||||
|
||||
cw->cw_list = new_cw_list;
|
||||
}
|
||||
|
||||
static void property_append_cw_conditions(DBusMessageIter *dict,
|
||||
GSList *cw_list, int mask)
|
||||
{
|
||||
GSList *l;
|
||||
int i;
|
||||
struct ofono_cw_condition *cw;
|
||||
const char *prop;
|
||||
|
||||
for (i = 1, l = cw_list; i <= BEARER_CLASS_PAD; i = i << 1) {
|
||||
if (!(mask & i))
|
||||
continue;
|
||||
|
||||
prop = bearer_class_to_string(i);
|
||||
|
||||
while (l && (cw = l->data) && (cw->cls < i))
|
||||
l = l->next;
|
||||
|
||||
if (!l || cw->cls != i) {
|
||||
dbus_gsm_dict_append(dict, prop, DBUS_TYPE_STRING,
|
||||
&disabled);
|
||||
continue;
|
||||
}
|
||||
|
||||
dbus_gsm_dict_append(dict, prop, DBUS_TYPE_STRING, &enabled);
|
||||
}
|
||||
}
|
||||
|
||||
static void generate_ss_query_reply(struct ofono_modem *modem)
|
||||
{
|
||||
struct call_waiting_data *cw = modem->call_waiting;
|
||||
const char *sig = "(sa{sv})";
|
||||
const char *ss_type = ss_control_type_to_string(cw->ss_req_type);
|
||||
const char *context = "CallWaiting";
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter var;
|
||||
DBusMessageIter vstruct;
|
||||
DBusMessageIter dict;
|
||||
DBusMessage *reply;
|
||||
|
||||
reply = dbus_message_new_method_return(cw->pending);
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &context);
|
||||
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig, &var);
|
||||
|
||||
dbus_message_iter_open_container(&var, DBUS_TYPE_STRUCT, NULL,
|
||||
&vstruct);
|
||||
|
||||
dbus_message_iter_append_basic(&vstruct, DBUS_TYPE_STRING,
|
||||
&ss_type);
|
||||
|
||||
dbus_message_iter_open_container(&vstruct, DBUS_TYPE_ARRAY,
|
||||
PROPERTIES_ARRAY_SIGNATURE, &dict);
|
||||
|
||||
property_append_cw_conditions(&dict, cw->cw_list, cw->ss_req_cls);
|
||||
|
||||
dbus_message_iter_close_container(&vstruct, &dict);
|
||||
|
||||
dbus_message_iter_close_container(&var, &vstruct);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &var);
|
||||
|
||||
dbus_gsm_pending_reply(&cw->pending, reply);
|
||||
}
|
||||
|
||||
static void cw_ss_query_callback(const struct ofono_error *error, int num,
|
||||
struct ofono_cw_condition *cond_list,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_waiting_data *cw = modem->call_waiting;
|
||||
GSList *l;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ofono_debug("setting CW via SS failed");
|
||||
|
||||
cw->flags &= ~CALL_WAITING_FLAG_CACHED;
|
||||
dbus_gsm_pending_reply(&cw->pending,
|
||||
dbus_gsm_failed(cw->pending));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
l = cw_cond_list_create(num, cond_list);
|
||||
|
||||
cw_cond_list_print(l);
|
||||
|
||||
set_new_cond_list(modem, l);
|
||||
cw->flags |= CALL_WAITING_FLAG_CACHED;
|
||||
|
||||
generate_ss_query_reply(modem);
|
||||
}
|
||||
|
||||
static void cw_ss_set_callback(const struct ofono_error *error, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_waiting_data *cw = modem->call_waiting;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ofono_debug("setting CW via SS failed");
|
||||
dbus_gsm_pending_reply(&cw->pending,
|
||||
dbus_gsm_failed(cw->pending));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
cw->ops->query(modem, cw->ss_req_cls, cw_ss_query_callback, modem);
|
||||
}
|
||||
|
||||
static gboolean cw_ss_control(struct ofono_modem *modem, int type,
|
||||
const char *sc, const char *sia,
|
||||
const char *sib, const char *sic,
|
||||
const char *dn, DBusMessage *msg)
|
||||
{
|
||||
struct call_waiting_data *cw = modem->call_waiting;
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
int cls = BEARER_CLASS_DEFAULT;
|
||||
DBusMessage *reply;
|
||||
//void *op;
|
||||
|
||||
if (!cw)
|
||||
return FALSE;
|
||||
|
||||
if (strcmp(sc, "43"))
|
||||
return FALSE;
|
||||
|
||||
if (cw->pending) {
|
||||
reply = dbus_gsm_busy(msg);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (strlen(sib) || strlen(sib) || strlen(dn))
|
||||
goto bad_format;
|
||||
|
||||
if ((type == SS_CONTROL_TYPE_QUERY && !cw->ops->query) ||
|
||||
(type != SS_CONTROL_TYPE_QUERY && !cw->ops->set)) {
|
||||
reply = dbus_gsm_not_implemented(msg);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (strlen(sia) > 0) {
|
||||
long service_code;
|
||||
char *end;
|
||||
|
||||
service_code = strtoul(sia, &end, 10);
|
||||
|
||||
if (end == sia || *end != '\0')
|
||||
goto bad_format;
|
||||
|
||||
cls = mmi_service_code_to_bearer_class(service_code);
|
||||
if (cls == 0)
|
||||
goto bad_format;
|
||||
}
|
||||
|
||||
cw->ss_req_cls = cls;
|
||||
cw->pending = dbus_message_ref(msg);
|
||||
|
||||
switch (type) {
|
||||
case SS_CONTROL_TYPE_REGISTRATION:
|
||||
case SS_CONTROL_TYPE_ACTIVATION:
|
||||
cw->ss_req_type = SS_CONTROL_TYPE_ACTIVATION;
|
||||
cw->ops->set(modem, 1, cls, cw_ss_set_callback, modem);
|
||||
break;
|
||||
|
||||
case SS_CONTROL_TYPE_QUERY:
|
||||
cw->ss_req_type = SS_CONTROL_TYPE_QUERY;
|
||||
cw->ops->query(modem, cls, cw_ss_query_callback, modem);
|
||||
break;
|
||||
|
||||
case SS_CONTROL_TYPE_DEACTIVATION:
|
||||
case SS_CONTROL_TYPE_ERASURE:
|
||||
cw->ss_req_type = SS_CONTROL_TYPE_DEACTIVATION;
|
||||
cw->ops->set(modem, 0, cls, cw_ss_set_callback, modem);
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
bad_format:
|
||||
reply = dbus_gsm_invalid_format(msg);
|
||||
error:
|
||||
g_dbus_send_message(conn, reply);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void cw_register_ss_controls(struct ofono_modem *modem)
|
||||
{
|
||||
ss_control_register(modem, "43", cw_ss_control);
|
||||
}
|
||||
|
||||
static void cw_unregister_ss_controls(struct ofono_modem *modem)
|
||||
{
|
||||
ss_control_unregister(modem, "43", cw_ss_control);
|
||||
}
|
||||
|
||||
static DBusMessage *generate_get_properties_reply(struct ofono_modem *modem,
|
||||
DBusMessage *msg)
|
||||
{
|
||||
struct call_waiting_data *cw = modem->call_waiting;
|
||||
DBusMessage *reply;
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter dict;
|
||||
//int i;
|
||||
//GSList *l;
|
||||
|
||||
reply = dbus_message_new_method_return(msg);
|
||||
|
||||
if (!reply)
|
||||
return NULL;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
|
||||
PROPERTIES_ARRAY_SIGNATURE,
|
||||
&dict);
|
||||
|
||||
property_append_cw_conditions(&dict, cw->cw_list, BEARER_CLASS_DEFAULT);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &dict);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
static void cw_query_callback(const struct ofono_error *error, int num,
|
||||
struct ofono_cw_condition *cond_list, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_waiting_data *cw = modem->call_waiting;
|
||||
GSList *l = NULL;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ofono_debug("Error during cw query");
|
||||
goto out;
|
||||
}
|
||||
|
||||
l = cw_cond_list_create(num, cond_list);
|
||||
|
||||
cw_cond_list_print(l);
|
||||
|
||||
set_new_cond_list(modem, l);
|
||||
cw->flags |= CALL_WAITING_FLAG_CACHED;
|
||||
|
||||
out:
|
||||
if (cw->pending) {
|
||||
DBusMessage *reply;
|
||||
|
||||
reply = generate_get_properties_reply(modem, cw->pending);
|
||||
dbus_gsm_pending_reply(&cw->pending, reply);
|
||||
}
|
||||
}
|
||||
|
||||
static DBusMessage *cw_get_properties(DBusConnection *conn, DBusMessage *msg,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_waiting_data *cw = modem->call_waiting;
|
||||
|
||||
if (cw->pending)
|
||||
return dbus_gsm_busy(msg);
|
||||
|
||||
if (!cw->ops->query)
|
||||
return dbus_gsm_not_implemented(msg);
|
||||
|
||||
if (cw->flags & CALL_WAITING_FLAG_CACHED)
|
||||
return generate_get_properties_reply(modem, msg);
|
||||
|
||||
cw->pending = dbus_message_ref(msg);
|
||||
|
||||
cw->ops->query(modem, BEARER_CLASS_DEFAULT, cw_query_callback, modem);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void set_query_callback(const struct ofono_error *error, int num,
|
||||
struct ofono_cw_condition *cond_list, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_waiting_data *cw = modem->call_waiting;
|
||||
GSList *l = NULL;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ofono_error("CW set succeeded, but query failed!");
|
||||
cw->flags &= ~CALL_WAITING_FLAG_CACHED;
|
||||
|
||||
dbus_gsm_pending_reply(&cw->pending,
|
||||
dbus_gsm_failed(cw->pending));
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_gsm_pending_reply(&cw->pending,
|
||||
dbus_message_new_method_return(cw->pending));
|
||||
|
||||
l = cw_cond_list_create(num, cond_list);
|
||||
|
||||
cw_cond_list_print(l);
|
||||
|
||||
set_new_cond_list(modem, l);
|
||||
}
|
||||
|
||||
static void set_callback(const struct ofono_error *error, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_waiting_data *cw = modem->call_waiting;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ofono_debug("Error occurred during CW set");
|
||||
|
||||
dbus_gsm_pending_reply(&cw->pending,
|
||||
dbus_gsm_failed(cw->pending));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
cw->ops->query(modem, BEARER_CLASS_DEFAULT, set_query_callback, modem);
|
||||
}
|
||||
|
||||
static DBusMessage *cw_set_property(DBusConnection *conn, DBusMessage *msg,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct call_waiting_data *cw = modem->call_waiting;
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter var;
|
||||
const char *property;
|
||||
int i;
|
||||
|
||||
if (cw->pending)
|
||||
return dbus_gsm_busy(msg);
|
||||
|
||||
if (!cw->ops->set)
|
||||
return dbus_gsm_not_implemented(msg);
|
||||
|
||||
if (!dbus_message_iter_init(msg, &iter))
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
|
||||
dbus_message_iter_get_basic(&iter, &property);
|
||||
dbus_message_iter_next(&iter);
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
|
||||
dbus_message_iter_recurse(&iter, &var);
|
||||
|
||||
for (i = 1; i < BEARER_CLASS_SMS; i = i << 1)
|
||||
if (!strcmp(property, bearer_class_to_string(i)))
|
||||
break;
|
||||
|
||||
if (i < BEARER_CLASS_SMS) {
|
||||
const char *value;
|
||||
int status;
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
|
||||
return dbus_gsm_invalid_format(msg);
|
||||
|
||||
dbus_message_iter_get_basic(&var, &value);
|
||||
|
||||
if (!strcmp(value, "enabled"))
|
||||
status = 1;
|
||||
else if (!strcmp(value, "disabled"))
|
||||
status = 0;
|
||||
else
|
||||
return dbus_gsm_invalid_format(msg);
|
||||
|
||||
cw->pending = dbus_message_ref(msg);
|
||||
|
||||
cw->ops->set(modem, status, i, set_callback, modem);
|
||||
}
|
||||
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
}
|
||||
|
||||
static GDBusMethodTable cw_methods[] = {
|
||||
{ "GetProperties", "", "a{sv}", cw_get_properties,
|
||||
G_DBUS_METHOD_FLAG_ASYNC },
|
||||
{ "SetProperty", "sv", "", cw_set_property,
|
||||
G_DBUS_METHOD_FLAG_ASYNC },
|
||||
{ }
|
||||
};
|
||||
|
||||
static GDBusSignalTable cw_signals[] = {
|
||||
{ "PropertyChanged", "sv" },
|
||||
{ }
|
||||
};
|
||||
|
||||
int ofono_call_waiting_register(struct ofono_modem *modem,
|
||||
struct ofono_call_waiting_ops *ops)
|
||||
{
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
if (modem == NULL)
|
||||
return -1;
|
||||
|
||||
if (ops == NULL)
|
||||
return -1;
|
||||
|
||||
modem->call_waiting = call_waiting_create();
|
||||
|
||||
if (!modem->call_waiting)
|
||||
return -1;
|
||||
|
||||
modem->call_waiting->ops = ops;
|
||||
|
||||
if (!g_dbus_register_interface(conn, modem->path,
|
||||
CALL_WAITING_INTERFACE,
|
||||
cw_methods, cw_signals, NULL,
|
||||
modem, call_waiting_destroy)) {
|
||||
ofono_error("Could not register CallWaiting %s", modem->path);
|
||||
call_waiting_destroy(modem);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ofono_debug("Registered call waiting interface");
|
||||
|
||||
cw_register_ss_controls(modem);
|
||||
|
||||
modem_add_interface(modem, CALL_WAITING_INTERFACE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ofono_call_waiting_unregister(struct ofono_modem *modem)
|
||||
{
|
||||
struct call_waiting_data *cw = modem->call_waiting;
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
if (!cw)
|
||||
return;
|
||||
|
||||
modem_remove_interface(modem, CALL_WAITING_INTERFACE);
|
||||
g_dbus_unregister_interface(conn, modem->path,
|
||||
CALL_WAITING_INTERFACE);
|
||||
|
||||
modem->call_waiting = NULL;
|
||||
}
|
|
@ -0,0 +1,576 @@
|
|||
/*
|
||||
*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "driver.h"
|
||||
#include "common.h"
|
||||
|
||||
struct error_entry {
|
||||
int error;
|
||||
const char *str;
|
||||
};
|
||||
|
||||
/* 0-127 from 24.011 Annex E2
|
||||
* 127-255 23.040 Section 9.2.3.22
|
||||
* Rest are from 27.005 Section 3.2.5
|
||||
*/
|
||||
struct error_entry cms_errors[] = {
|
||||
{ 1, "Unassigned number" },
|
||||
{ 8, "Operator determined barring" },
|
||||
{ 10, "Call barred" },
|
||||
{ 21, "Short message transfer rejected" },
|
||||
{ 27, "Destination out of service" },
|
||||
{ 28, "Unindentified subscriber" },
|
||||
{ 29, "Facility rejected" },
|
||||
{ 30, "Unknown subscriber" },
|
||||
{ 38, "Network out of order" },
|
||||
{ 41, "Temporary failure" },
|
||||
{ 42, "Congestion" },
|
||||
{ 47, "Recources unavailable" },
|
||||
{ 50, "Requested facility not subscribed" },
|
||||
{ 69, "Requested facility not implemented" },
|
||||
{ 81, "Invalid short message transfer reference value" },
|
||||
{ 95, "Invalid message, unspecified" },
|
||||
{ 96, "Invalid mandatory information" },
|
||||
{ 97, "Message type non existent or not implemented" },
|
||||
{ 98, "Message not compatible with short message protocol state" },
|
||||
{ 99, "Information element non-existent or not implemented" },
|
||||
{ 111, "Protocol error, unspecified" },
|
||||
{ 127, "Internetworking error, unspecified" },
|
||||
{ 128, "Telematic internetworking not supported" },
|
||||
{ 129, "Short message type 0 not supported" },
|
||||
{ 130, "Cannot replace short message" },
|
||||
{ 143, "Unspecified TP-PID error" },
|
||||
{ 144, "Data code scheme not supported" },
|
||||
{ 145, "Message class not supported" },
|
||||
{ 159, "Unspecified TP-DCS error" },
|
||||
{ 160, "Command cannot be actioned" },
|
||||
{ 161, "Command unsupported" },
|
||||
{ 175, "Unspecified TP-Command error" },
|
||||
{ 176, "TPDU not supported" },
|
||||
{ 192, "SC busy" },
|
||||
{ 193, "No SC subscription" },
|
||||
{ 194, "SC System failure" },
|
||||
{ 195, "Invalid SME address" },
|
||||
{ 196, "Destination SME barred" },
|
||||
{ 197, "SM Rejected-Duplicate SM" },
|
||||
{ 198, "TP-VPF not supported" },
|
||||
{ 199, "TP-VP not supported" },
|
||||
{ 208, "(U)SIM SMS Storage full" },
|
||||
{ 209, "No SMS Storage capability in SIM" },
|
||||
{ 210, "Error in MS" },
|
||||
{ 211, "Memory capacity exceeded" },
|
||||
{ 212, "Sim application toolkit busy" },
|
||||
{ 213, "SIM data download error" },
|
||||
{ 255, "Unspecified error cause" },
|
||||
{ 300, "ME Failure" },
|
||||
{ 301, "SMS service of ME reserved" },
|
||||
{ 302, "Operation not allowed" },
|
||||
{ 303, "Operation not supported" },
|
||||
{ 304, "Invalid PDU mode parameter" },
|
||||
{ 305, "Invalid Text mode parameter" },
|
||||
{ 310, "(U)SIM not inserted" },
|
||||
{ 311, "(U)SIM PIN required" },
|
||||
{ 312, "PH-(U)SIM PIN required" },
|
||||
{ 313, "(U)SIM failure" },
|
||||
{ 314, "(U)SIM busy" },
|
||||
{ 315, "(U)SIM wrong" },
|
||||
{ 316, "(U)SIM PUK required" },
|
||||
{ 317, "(U)SIM PIN2 required" },
|
||||
{ 318, "(U)SIM PUK2 required" },
|
||||
{ 320, "Memory failure" },
|
||||
{ 321, "Invalid memory index" },
|
||||
{ 322, "Memory full" },
|
||||
{ 330, "SMSC address unknown" },
|
||||
{ 331, "No network service" },
|
||||
{ 332, "Network timeout" },
|
||||
{ 340, "No +CNMA expected" },
|
||||
{ 500, "Unknown error" },
|
||||
};
|
||||
|
||||
/* 27.007, Section 9 */
|
||||
struct error_entry cme_errors[] = {
|
||||
{ 0, "Phone failure" },
|
||||
{ 1, "No connection to phone" },
|
||||
{ 2, "Phone adapter link reserved" },
|
||||
{ 3, "Operation not allowed" },
|
||||
{ 4, "Operation not supported" },
|
||||
{ 5, "PH_SIM PIN required" },
|
||||
{ 6, "PH_FSIM PIN required" },
|
||||
{ 7, "PH_FSIM PUK required" },
|
||||
{ 10, "SIM not inserted" },
|
||||
{ 11, "SIM PIN required" },
|
||||
{ 12, "SIM PUK required" },
|
||||
{ 13, "SIM failure" },
|
||||
{ 14, "SIM busy" },
|
||||
{ 15, "SIM wrong" },
|
||||
{ 16, "Incorrect password" },
|
||||
{ 17, "SIM PIN2 required" },
|
||||
{ 18, "SIM PUK2 required" },
|
||||
{ 20, "Memory full" },
|
||||
{ 21, "Invalid index" },
|
||||
{ 22, "Not found" },
|
||||
{ 23, "Memory failure" },
|
||||
{ 24, "Text string too long" },
|
||||
{ 25, "Invalid characters in text string" },
|
||||
{ 26, "Dial string too long" },
|
||||
{ 27, "Invalid characters in dial string" },
|
||||
{ 30, "No network service" },
|
||||
{ 31, "Network timeout" },
|
||||
{ 32, "Network not allowed, emergency calls only" },
|
||||
{ 40, "Network personalization PIN required" },
|
||||
{ 41, "Network personalization PUK required" },
|
||||
{ 42, "Network subset personalization PIN required" },
|
||||
{ 43, "Network subset personalization PUK required" },
|
||||
{ 44, "Service provider personalization PIN required" },
|
||||
{ 45, "Service provider personalization PUK required" },
|
||||
{ 46, "Corporate personalization PIN required" },
|
||||
{ 47, "Corporate personalization PUK required" },
|
||||
{ 48, "PH-SIM PUK required" },
|
||||
{ 100, "Unknown error" },
|
||||
{ 103, "Illegal MS" },
|
||||
{ 106, "Illegal ME" },
|
||||
{ 107, "GPRS services not allowed" },
|
||||
{ 111, "PLMN not allowed" },
|
||||
{ 112, "Location area not allowed" },
|
||||
{ 113, "Roaming not allowed in this location area" },
|
||||
{ 126, "Operation temporary not allowed" },
|
||||
{ 132, "Service operation not supported" },
|
||||
{ 133, "Requested service option not subscribed" },
|
||||
{ 134, "Service option temporary out of order" },
|
||||
{ 148, "Unspecified GPRS error" },
|
||||
{ 149, "PDP authentication failure" },
|
||||
{ 150, "Invalid mobile class" },
|
||||
{ 256, "Operation temporarily not allowed" },
|
||||
{ 257, "Call barred" },
|
||||
{ 258, "Phone is busy" },
|
||||
{ 259, "User abort" },
|
||||
{ 260, "Invalid dial string" },
|
||||
{ 261, "SS not executed" },
|
||||
{ 262, "SIM Blocked" },
|
||||
{ 263, "Invalid block" },
|
||||
{ 772, "SIM powered down" },
|
||||
};
|
||||
|
||||
/* 24.008 Annex H */
|
||||
struct error_entry ceer_errors[] = {
|
||||
{ 1, "Unassigned number" },
|
||||
{ 3, "No route to destination" },
|
||||
{ 6, "Channel unacceptable" },
|
||||
{ 8, "Operator determined barring" },
|
||||
{ 16, "Normal call clearing" },
|
||||
{ 17, "User busy" },
|
||||
{ 18, "No user responding" },
|
||||
{ 19, "User alerting, no answer" },
|
||||
{ 21, "Call rejected" },
|
||||
{ 22, "Number changed" },
|
||||
{ 25, "Pre-emption" },
|
||||
{ 26, "Non-selected user clearing" },
|
||||
{ 27, "Destination out of order" },
|
||||
{ 28, "Invalid number format (incomplete number)" },
|
||||
{ 29, "Facility rejected" },
|
||||
{ 30, "Response to STATUS ENQUIRY" },
|
||||
{ 31, "Normal, unspecified" },
|
||||
{ 34, "No circuit/channel available" },
|
||||
{ 38, "Network out of order" },
|
||||
{ 41, "Temporary failure" },
|
||||
{ 42, "Switching equipment congestion" },
|
||||
{ 43, "Access information discared" },
|
||||
{ 44, "Requested circuit/channel not available" },
|
||||
{ 47, "Resource unavailable (unspecified)" },
|
||||
{ 49, "Quality of service unavailable" },
|
||||
{ 50, "Requested facility not subscribed" },
|
||||
{ 55, "Incoming calls barred within the CUG" },
|
||||
{ 57, "Bearer capability not authorized" },
|
||||
{ 58, "Bearar capability not presently available" },
|
||||
{ 63, "Service or option not available, unspecified" },
|
||||
{ 65, "Bearer service not implemented" },
|
||||
{ 68, "ACM equal to or greater than ACMmax" },
|
||||
{ 69, "Requested facility not implemented" },
|
||||
{ 70, "Only restricted digital information bearer capability is available" },
|
||||
{ 79, "Service or option not implemented, unspecified" },
|
||||
{ 81, "Invalid transaction identifier value" },
|
||||
{ 87, "User not member of CUG" },
|
||||
{ 88, "Incompatible destination" },
|
||||
{ 91, "Invalid transit network selection" },
|
||||
{ 95, "Semantically incorrect message" },
|
||||
{ 96, "Invalid mandatory information"},
|
||||
{ 97, "Message type non-existent or not implemented" },
|
||||
{ 98, "Message type not compatible with protocol state" },
|
||||
{ 99, "Information element non-existent or not implemented" },
|
||||
{ 100, "Conditional IE error" },
|
||||
{ 101, "Message not compatible with protocol state" },
|
||||
{ 102, "Recovery on timer expirty" },
|
||||
{ 111, "Protocol error, unspecified" },
|
||||
{ 127, "Interworking, unspecified" },
|
||||
};
|
||||
|
||||
gboolean valid_phone_number_format(const char *number)
|
||||
{
|
||||
int len = strlen(number);
|
||||
int begin = 0;
|
||||
int i;
|
||||
|
||||
if (!len)
|
||||
return FALSE;
|
||||
|
||||
if (len > OFONO_MAX_PHONE_NUMBER_LENGTH)
|
||||
return FALSE;
|
||||
|
||||
if (number[0] == '+')
|
||||
begin = 1;
|
||||
|
||||
for (i = begin; i < len; i++) {
|
||||
if (number[i] >= '0' && number[i] <= '9')
|
||||
continue;
|
||||
|
||||
if (number[i] == '*' || number[i] == '#')
|
||||
continue;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
const char *telephony_error_to_str(const struct ofono_error *error)
|
||||
{
|
||||
struct error_entry *e;
|
||||
int maxentries;
|
||||
int i;
|
||||
|
||||
switch (error->type) {
|
||||
case OFONO_ERROR_TYPE_CME:
|
||||
e = cme_errors;
|
||||
maxentries = sizeof(cme_errors) / sizeof(struct error_entry);
|
||||
break;
|
||||
case OFONO_ERROR_TYPE_CMS:
|
||||
e = cms_errors;
|
||||
maxentries = sizeof(cme_errors) / sizeof(struct error_entry);
|
||||
break;
|
||||
case OFONO_ERROR_TYPE_CEER:
|
||||
e = ceer_errors;
|
||||
maxentries = sizeof(ceer_errors) / sizeof(struct error_entry);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < maxentries; i++)
|
||||
if (e[i].error == error->error)
|
||||
return e[i].str;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mmi_service_code_to_bearer_class(int code)
|
||||
{
|
||||
int cls = 0;
|
||||
|
||||
switch (code) {
|
||||
case 10:
|
||||
cls = BEARER_CLASS_DEFAULT | BEARER_CLASS_SMS;
|
||||
break;
|
||||
case 11:
|
||||
cls = BEARER_CLASS_VOICE;
|
||||
break;
|
||||
case 12:
|
||||
cls = BEARER_CLASS_DATA;
|
||||
break;
|
||||
case 13:
|
||||
cls = BEARER_CLASS_FAX;
|
||||
break;
|
||||
case 16:
|
||||
cls = BEARER_CLASS_SMS;
|
||||
break;
|
||||
/* TODO: Voice Group Call & Broadcast VGCS & VBS */
|
||||
case 17:
|
||||
case 18:
|
||||
break;
|
||||
case 19:
|
||||
cls = BEARER_CLASS_DEFAULT;
|
||||
break;
|
||||
|
||||
/* Funny, according to 22.030, 20 implies BS 7-11 */
|
||||
/* 22.004 only defines BS 7 (Data Sync) & BS 8 (Data Async) */
|
||||
case 20:
|
||||
cls = BEARER_CLASS_DATA_SYNC | BEARER_CLASS_DATA_ASYNC;
|
||||
break;
|
||||
/* According to 22.030: All Async */
|
||||
case 21:
|
||||
/* According to 22.030: All Data Async */
|
||||
case 25:
|
||||
cls = BEARER_CLASS_DATA_ASYNC;
|
||||
break;
|
||||
/* According to 22.030: All Sync */
|
||||
case 22:
|
||||
/* According to 22.030: All Data Sync */
|
||||
case 24:
|
||||
cls = BEARER_CLASS_DATA_SYNC;
|
||||
break;
|
||||
/* According to 22.030: Telephony & All Sync services */
|
||||
case 26:
|
||||
cls = BEARER_CLASS_VOICE | BEARER_CLASS_DATA_SYNC;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return cls;
|
||||
}
|
||||
|
||||
const char *phone_number_to_string(const char *number, int type)
|
||||
{
|
||||
static char buffer[64];
|
||||
|
||||
if (type == 145 && (strlen(number) > 0) && number[0] != '+') {
|
||||
buffer[0] = '+';
|
||||
strncpy(buffer + 1, number, 62);
|
||||
buffer[63] = '\0';
|
||||
} else {
|
||||
strncpy(buffer, number, 63);
|
||||
buffer[63] = '\0';
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void string_to_phone_number(const char *str, int *type, const char **number)
|
||||
{
|
||||
if (strlen(str) && str[0] == '+') {
|
||||
*number = &str[1];
|
||||
*type = 145; /* International */
|
||||
} else {
|
||||
*number = &str[0];
|
||||
*type = 129; /* Local */
|
||||
}
|
||||
}
|
||||
|
||||
int valid_ussd_string(const char *str)
|
||||
{
|
||||
int len = strlen(str);
|
||||
|
||||
if (!len)
|
||||
return FALSE;
|
||||
|
||||
/* It is hard to understand exactly what constitutes a valid USSD string
|
||||
* According to 22.090:
|
||||
* Case a - 1, 2 or 3 digits from the set (*, #) followed by 1X(Y),
|
||||
* where X=any number 0‑4, Y=any number 0‑9, then, optionally "*
|
||||
* followed by any number of any characters", and concluding with #SEND
|
||||
*
|
||||
* Case b - 1, 2 or 3 digits from the set (*, #) followed by 1X(Y),
|
||||
* where X=any number 5‑9, Y=any number 0‑9, then, optionally "*
|
||||
* followed by any number of any characters", and concluding with #SEND
|
||||
*
|
||||
* Case c - 7(Y) SEND, where Y=any number 0‑9
|
||||
*
|
||||
* Case d - All other formats
|
||||
*
|
||||
* According to 22.030 Figure 3.5.3.2 USSD strings can be:
|
||||
*
|
||||
* Supplementary service control
|
||||
* SIM control
|
||||
* Manufacturer defined
|
||||
* Terminated by '#'
|
||||
* Short String - This can be any 2 digit short string. If the string
|
||||
* starts with a '1' and no calls are in progress then
|
||||
* this string is treated as a call setup request
|
||||
*
|
||||
* Everything else is not a valid USSD string
|
||||
*/
|
||||
|
||||
if (len != 2 && str[len-1] != '#')
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
const char *ss_control_type_to_string(enum ss_control_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case SS_CONTROL_TYPE_ACTIVATION:
|
||||
return "acivation";
|
||||
case SS_CONTROL_TYPE_REGISTRATION:
|
||||
return "registration";
|
||||
case SS_CONTROL_TYPE_QUERY:
|
||||
return "interrogation";
|
||||
case SS_CONTROL_TYPE_DEACTIVATION:
|
||||
return "deactivation";
|
||||
case SS_CONTROL_TYPE_ERASURE:
|
||||
return "erasure";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define NEXT_FIELD(str, dest) \
|
||||
do { \
|
||||
dest = str; \
|
||||
\
|
||||
str = strchrnul(str, '*'); \
|
||||
if (*str) { \
|
||||
*str = '\0'; \
|
||||
str += 1; \
|
||||
} \
|
||||
} while (0) \
|
||||
|
||||
/* Note: The str will be modified, so in case of error you should
|
||||
* throw it away and start over
|
||||
*/
|
||||
gboolean parse_ss_control_string(char *str, int *ss_type,
|
||||
char **sc, char **sia,
|
||||
char **sib, char **sic,
|
||||
char **dn)
|
||||
{
|
||||
int len = strlen(str);
|
||||
int cur = 0;
|
||||
char *c;
|
||||
unsigned int i;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
/* Minimum is {*,#}SC# */
|
||||
if (len < 4)
|
||||
goto out;
|
||||
|
||||
if (str[0] != '*' && str[0] != '#')
|
||||
goto out;
|
||||
|
||||
cur = 1;
|
||||
|
||||
if (str[1] != '*' && str[1] != '#' && str[1] > '9' && str[1] < '0')
|
||||
goto out;
|
||||
|
||||
if (str[0] == '#' && str[1] == '*')
|
||||
goto out;
|
||||
|
||||
if (str[1] == '#' || str[1] == '*')
|
||||
cur = 2;
|
||||
|
||||
if (str[0] == '*' && str[1] == '*')
|
||||
*ss_type = SS_CONTROL_TYPE_REGISTRATION;
|
||||
else if (str[0] == '#' && str[1] == '#')
|
||||
*ss_type = SS_CONTROL_TYPE_ERASURE;
|
||||
else if (str[0] == '*' && str[1] == '#')
|
||||
*ss_type = SS_CONTROL_TYPE_QUERY;
|
||||
else if (str[0] == '*')
|
||||
*ss_type = SS_CONTROL_TYPE_ACTIVATION;
|
||||
else
|
||||
*ss_type = SS_CONTROL_TYPE_DEACTIVATION;
|
||||
|
||||
/* Must have at least one other '#' */
|
||||
c = strrchr(str+cur, '#');
|
||||
|
||||
if (!c)
|
||||
goto out;
|
||||
|
||||
*dn = c+1;
|
||||
*c = '\0';
|
||||
|
||||
if (strlen(*dn) > 0 && !valid_phone_number_format(*dn))
|
||||
goto out;
|
||||
|
||||
c = str+cur;
|
||||
|
||||
NEXT_FIELD(c, *sc);
|
||||
|
||||
/* According to 22.030 SC is 2 or 3 digits, there can be
|
||||
* an optional digit 'n' if this is a call setup string,
|
||||
* however 22.030 does not define any SC of length 3
|
||||
* with an 'n' present
|
||||
*/
|
||||
if (strlen(*sc) < 2 || strlen(*sc) > 3)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < strlen(*sc); i++)
|
||||
if (!isdigit((*sc)[i]))
|
||||
goto out;
|
||||
|
||||
NEXT_FIELD(c, *sia);
|
||||
NEXT_FIELD(c, *sib);
|
||||
NEXT_FIELD(c, *sic);
|
||||
|
||||
if (*c == '\0')
|
||||
ret = TRUE;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *bearer_class_lut[] = {
|
||||
"Voice",
|
||||
"Data",
|
||||
"Fax",
|
||||
"Sms",
|
||||
"DataSync",
|
||||
"DataAsync",
|
||||
"DataPad",
|
||||
"DataPacket"
|
||||
};
|
||||
|
||||
const char *bearer_class_to_string(enum bearer_class cls)
|
||||
{
|
||||
switch (cls) {
|
||||
case BEARER_CLASS_VOICE:
|
||||
return bearer_class_lut[0];
|
||||
case BEARER_CLASS_DATA:
|
||||
return bearer_class_lut[1];
|
||||
case BEARER_CLASS_FAX:
|
||||
return bearer_class_lut[2];
|
||||
case BEARER_CLASS_SMS:
|
||||
return bearer_class_lut[3];
|
||||
case BEARER_CLASS_DATA_SYNC:
|
||||
return bearer_class_lut[4];
|
||||
case BEARER_CLASS_DATA_ASYNC:
|
||||
return bearer_class_lut[5];
|
||||
case BEARER_CLASS_PACKET:
|
||||
return bearer_class_lut[6];
|
||||
case BEARER_CLASS_PAD:
|
||||
return bearer_class_lut[7];
|
||||
case BEARER_CLASS_DEFAULT:
|
||||
break;
|
||||
};
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gboolean is_valid_pin(const char *pin)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < strlen(pin); i++)
|
||||
if (pin[i] < '0' || pin[i] > '9')
|
||||
return FALSE;
|
||||
|
||||
if (i > 8)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
/* 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
|
||||
};
|
||||
|
||||
/* 27.007 Section 7.3 <AcT> */
|
||||
enum access_technology {
|
||||
ACCESS_TECHNOLOGY_GSM = 0,
|
||||
ACCESS_TECHNOLOGY_GSM_COMPACT = 1,
|
||||
ACCESS_TECHNOLOGY_UTRAN = 2,
|
||||
ACCESS_TECHNOLOGY_GSM_EGPRS = 3,
|
||||
ACCESS_TECHNOLOGY_UTRAN_HSDPA = 4,
|
||||
ACCESS_TECHNOLOGY_UTRAN_HSUPA = 5,
|
||||
ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA = 6
|
||||
};
|
||||
|
||||
/* 27.007 Section 7.2 <stat> */
|
||||
enum network_registration_status {
|
||||
NETWORK_REGISTRATION_STATUS_NOT_REGISTERED = 0,
|
||||
NETWORK_REGISTRATION_STATUS_REGISTERED = 1,
|
||||
NETWORK_REGISTRATION_STATUS_SEARCHING = 2,
|
||||
NETWORK_REGISTRATION_STATUS_DENIED = 3,
|
||||
NETWORK_REGISTRATION_STATUS_UNKNOWN = 4,
|
||||
NETWORK_REGISTRATION_STATUS_ROAMING = 5
|
||||
};
|
||||
|
||||
/* 27.007 Section 7.7 */
|
||||
enum clir_status {
|
||||
CLIR_STATUS_NOT_PROVISIONED = 0,
|
||||
CLIR_STATUS_PROVISIONED_PERMANENT,
|
||||
CLIR_STATUS_UNKNOWN,
|
||||
CLIR_STATUS_TEMPORARY_RESTRICTED,
|
||||
CLIR_STATUS_TEMPORARY_ALLOWED
|
||||
};
|
||||
|
||||
/* 27.007 Section 7.6 */
|
||||
enum clip_status {
|
||||
CLIP_STATUS_NOT_PROVISIONED = 0,
|
||||
CLIP_STATUS_PROVISIONED,
|
||||
CLIP_STATUS_UNKNOWN
|
||||
};
|
||||
|
||||
/* 27.007 Section 7.6 */
|
||||
enum clip_validity {
|
||||
CLIP_VALIDITY_VALID = 0,
|
||||
CLIP_VALIDITY_WITHHELD = 1,
|
||||
CLIP_VALIDITY_NOT_AVAILABLE = 2
|
||||
};
|
||||
|
||||
/* 27.007 Section 7.8 */
|
||||
enum colp_status {
|
||||
COLP_STATUS_NOT_PROVISIONED = 0,
|
||||
COLP_STATUS_PROVISIONED = 1,
|
||||
COLP_STATUS_UNKNOWN = 2
|
||||
};
|
||||
|
||||
/* This is not defined in 27.007, but presumably the same as CLIP/COLP */
|
||||
enum colr_status {
|
||||
COLR_STATUS_NOT_PROVISIONED = 0,
|
||||
COLR_STATUS_PROVISIONED = 1,
|
||||
COLR_STATUS_UNKNOWN = 2
|
||||
};
|
||||
|
||||
/* 27.007 Section 7.18 */
|
||||
enum call_status {
|
||||
CALL_STATUS_ACTIVE = 0,
|
||||
CALL_STATUS_HELD = 1,
|
||||
CALL_STATUS_DIALING = 2,
|
||||
CALL_STATUS_ALERTING = 3,
|
||||
CALL_STATUS_INCOMING = 4,
|
||||
CALL_STATUS_WAITING = 5,
|
||||
CALL_STATUS_DISCONNECTED
|
||||
};
|
||||
|
||||
/* 27.007 Section 7.18 */
|
||||
enum call_direction {
|
||||
CALL_DIRECTION_MOBILE_ORIGINATED = 0,
|
||||
CALL_DIRECTION_MOBILE_TERMINATED = 1
|
||||
};
|
||||
|
||||
/* 27.007 Section 7.11 */
|
||||
enum bearer_class {
|
||||
BEARER_CLASS_VOICE = 1,
|
||||
BEARER_CLASS_DATA = 2,
|
||||
BEARER_CLASS_FAX = 4,
|
||||
BEARER_CLASS_DEFAULT = 7,
|
||||
BEARER_CLASS_SMS = 8,
|
||||
BEARER_CLASS_DATA_SYNC = 16,
|
||||
BEARER_CLASS_DATA_ASYNC = 32,
|
||||
BEARER_CLASS_PACKET = 64,
|
||||
BEARER_CLASS_PAD = 128
|
||||
};
|
||||
|
||||
enum call_forwarding_type {
|
||||
CALL_FORWARDING_TYPE_UNCONDITIONAL = 0,
|
||||
CALL_FORWARDING_TYPE_BUSY = 1,
|
||||
CALL_FORWARDING_TYPE_NO_REPLY = 2,
|
||||
CALL_FORWARDING_TYPE_NOT_REACHABLE = 3,
|
||||
CALL_FORWARDING_TYPE_ALL = 4,
|
||||
CALL_FORWARDING_TYPE_ALL_CONDITIONAL = 5
|
||||
};
|
||||
|
||||
enum ussd_status {
|
||||
USSD_STATUS_NOTIFY = 0,
|
||||
USSD_STATUS_ACTION_REQUIRED = 1,
|
||||
USSD_STATUS_TERMINATED = 2,
|
||||
USSD_STATUS_LOCAL_CLIENT_RESPONDED = 3,
|
||||
USSD_STATUS_NOT_SUPPORTED = 4,
|
||||
USSD_STATUS_TIMED_OUT = 5,
|
||||
};
|
||||
|
||||
/* 22.030 Section 6.5.2 */
|
||||
enum ss_control_type {
|
||||
SS_CONTROL_TYPE_ACTIVATION,
|
||||
SS_CONTROL_TYPE_DEACTIVATION,
|
||||
SS_CONTROL_TYPE_QUERY,
|
||||
SS_CONTROL_TYPE_REGISTRATION,
|
||||
SS_CONTROL_TYPE_ERASURE,
|
||||
};
|
||||
|
||||
const char *telephony_error_to_str(const struct ofono_error *error);
|
||||
|
||||
gboolean valid_phone_number_format(const char *number);
|
||||
const char *phone_number_to_string(const char *number, int type);
|
||||
void string_to_phone_number(const char *str, int *type, const char **number);
|
||||
|
||||
int mmi_service_code_to_bearer_class(int code);
|
||||
|
||||
gboolean valid_ussd_string(const char *str);
|
||||
|
||||
gboolean parse_ss_control_string(char *str, int *ss_type,
|
||||
char **sc, char **sia,
|
||||
char **sib, char **sic,
|
||||
char **dn);
|
||||
|
||||
const char *ss_control_type_to_string(enum ss_control_type type);
|
||||
|
||||
const char *bearer_class_to_string(enum bearer_class cls);
|
||||
|
||||
gboolean is_valid_pin(const char *pin);
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
#include <dbus/dbus.h>
|
||||
#include <gdbus.h>
|
||||
|
||||
#include "ofono.h"
|
||||
|
||||
#include "dbus-gsm.h"
|
||||
|
||||
#define SERVICE_NAME "org.ofono"
|
||||
|
||||
#define RECONNECT_RETRY_TIMEOUT 2000
|
||||
|
||||
static DBusConnection *g_connection;
|
||||
|
||||
void dbus_gsm_free_string_array(char **array)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!array)
|
||||
return;
|
||||
|
||||
for (i = 0; array[i]; i++)
|
||||
g_free(array[i]);
|
||||
|
||||
g_free(array);
|
||||
}
|
||||
|
||||
void dbus_gsm_append_variant(DBusMessageIter *iter,
|
||||
int type, void *value)
|
||||
{
|
||||
char sig[2];
|
||||
DBusMessageIter valueiter;
|
||||
|
||||
sig[0] = type;
|
||||
sig[1] = 0;
|
||||
|
||||
dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
|
||||
sig, &valueiter);
|
||||
|
||||
dbus_message_iter_append_basic(&valueiter, type, value);
|
||||
|
||||
dbus_message_iter_close_container(iter, &valueiter);
|
||||
}
|
||||
|
||||
void dbus_gsm_dict_append(DBusMessageIter *dict,
|
||||
const char *key, int type, void *value)
|
||||
{
|
||||
DBusMessageIter keyiter;
|
||||
|
||||
if (type == DBUS_TYPE_STRING) {
|
||||
const char *str = *((const char **) value);
|
||||
if (str == NULL)
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
|
||||
NULL, &keyiter);
|
||||
|
||||
dbus_message_iter_append_basic(&keyiter, DBUS_TYPE_STRING, &key);
|
||||
|
||||
dbus_gsm_append_variant(&keyiter, type, value);
|
||||
|
||||
dbus_message_iter_close_container(dict, &keyiter);
|
||||
}
|
||||
|
||||
void dbus_gsm_append_array_variant(DBusMessageIter *iter, int type, void *val)
|
||||
{
|
||||
DBusMessageIter variant, array;
|
||||
char typesig[2];
|
||||
char arraysig[3];
|
||||
const char **str_array = *(const char ***)val;
|
||||
int i;
|
||||
|
||||
arraysig[0] = DBUS_TYPE_ARRAY;
|
||||
arraysig[1] = typesig[0] = type;
|
||||
arraysig[2] = typesig[1] = '\0';
|
||||
|
||||
dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
|
||||
arraysig, &variant);
|
||||
|
||||
dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
|
||||
typesig, &array);
|
||||
|
||||
for (i = 0; str_array[i]; i++)
|
||||
dbus_message_iter_append_basic(&array, type,
|
||||
&(str_array[i]));
|
||||
|
||||
dbus_message_iter_close_container(&variant, &array);
|
||||
|
||||
dbus_message_iter_close_container(iter, &variant);
|
||||
}
|
||||
|
||||
void dbus_gsm_dict_append_array(DBusMessageIter *dict, const char *key,
|
||||
int type, void *val)
|
||||
{
|
||||
DBusMessageIter entry;
|
||||
|
||||
dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
|
||||
NULL, &entry);
|
||||
|
||||
dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
|
||||
|
||||
dbus_gsm_append_array_variant(&entry, type, val);
|
||||
|
||||
dbus_message_iter_close_container(dict, &entry);
|
||||
}
|
||||
|
||||
int dbus_gsm_signal_property_changed(DBusConnection *conn,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *name,
|
||||
int type, void *value)
|
||||
{
|
||||
DBusMessage *signal;
|
||||
DBusMessageIter iter;
|
||||
|
||||
signal = dbus_message_new_signal(path, interface, "PropertyChanged");
|
||||
|
||||
if (!signal) {
|
||||
ofono_error("Unable to allocate new %s.PropertyChanged signal",
|
||||
interface);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dbus_message_iter_init_append(signal, &iter);
|
||||
|
||||
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
|
||||
|
||||
dbus_gsm_append_variant(&iter, type, value);
|
||||
|
||||
return g_dbus_send_message(conn, signal);
|
||||
}
|
||||
|
||||
int dbus_gsm_signal_array_property_changed(DBusConnection *conn,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *name,
|
||||
int type, void *value)
|
||||
|
||||
{
|
||||
DBusMessage *signal;
|
||||
DBusMessageIter iter;
|
||||
|
||||
signal = dbus_message_new_signal(path, interface, "PropertyChanged");
|
||||
|
||||
if (!signal) {
|
||||
ofono_error("Unable to allocate new %s.PropertyChanged signal",
|
||||
interface);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dbus_message_iter_init_append(signal, &iter);
|
||||
|
||||
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
|
||||
|
||||
dbus_gsm_append_array_variant(&iter, type, value);
|
||||
|
||||
return g_dbus_send_message(conn, signal);
|
||||
}
|
||||
|
||||
DBusConnection *dbus_gsm_connection()
|
||||
{
|
||||
return g_connection;
|
||||
}
|
||||
|
||||
void dbus_gsm_set_connection(DBusConnection *conn)
|
||||
{
|
||||
if (conn && g_connection != NULL)
|
||||
ofono_error("Setting a connection when it is not NULL");
|
||||
|
||||
g_connection = conn;
|
||||
}
|
||||
|
||||
static gboolean system_bus_reconnect(void *user_data)
|
||||
{
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
if (!conn && (dbus_gsm_init() < 0))
|
||||
return TRUE;
|
||||
|
||||
conn = dbus_gsm_connection();
|
||||
|
||||
if (conn && dbus_connection_get_is_connected(conn))
|
||||
return FALSE;
|
||||
|
||||
ofono_error("While attempting to reconnect, conn != NULL,"
|
||||
" but not connected");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void system_bus_disconnected(DBusConnection *conn, void *user_data)
|
||||
{
|
||||
ofono_error("System bus has disconnected!");
|
||||
|
||||
dbus_gsm_set_connection(NULL);
|
||||
|
||||
g_timeout_add(RECONNECT_RETRY_TIMEOUT,
|
||||
system_bus_reconnect, NULL);
|
||||
}
|
||||
|
||||
int dbus_gsm_init()
|
||||
{
|
||||
DBusConnection *conn;
|
||||
DBusError error;
|
||||
|
||||
dbus_error_init(&error);
|
||||
|
||||
conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, SERVICE_NAME, &error);
|
||||
if (!conn) {
|
||||
ofono_error("Unable to hop onto D-Bus: %s", error.message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (g_dbus_set_disconnect_function(conn, system_bus_disconnected,
|
||||
NULL, NULL) == FALSE) {
|
||||
dbus_connection_unref(conn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dbus_gsm_set_connection(conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbus_gsm_exit()
|
||||
{
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
if (!conn || !dbus_connection_get_is_connected(conn))
|
||||
return;
|
||||
|
||||
dbus_gsm_set_connection(NULL);
|
||||
|
||||
dbus_connection_unref(conn);
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include <gdbus.h>
|
||||
|
||||
DBusConnection *dbus_gsm_connection();
|
||||
void dbus_gsm_set_connection(DBusConnection *conn);
|
||||
|
||||
int dbus_gsm_init();
|
||||
void dbus_gsm_exit();
|
||||
|
||||
#define MAX_DBUS_PATH_LEN 64
|
||||
|
||||
void dbus_gsm_free_string_array(char **array);
|
||||
|
||||
/* Essentially a{sv} */
|
||||
#define PROPERTIES_ARRAY_SIGNATURE DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \
|
||||
DBUS_TYPE_STRING_AS_STRING \
|
||||
DBUS_TYPE_VARIANT_AS_STRING \
|
||||
DBUS_DICT_ENTRY_END_CHAR_AS_STRING
|
||||
|
||||
void dbus_gsm_dict_append(DBusMessageIter *dict, const char *key, int type,
|
||||
void *value);
|
||||
|
||||
void dbus_gsm_append_variant(DBusMessageIter *iter, int type, void *value);
|
||||
|
||||
void dbus_gsm_append_array_variant(DBusMessageIter *iter, int type, void *val);
|
||||
|
||||
void dbus_gsm_dict_append_array(DBusMessageIter *dict, const char *key,
|
||||
int type, void *val);
|
||||
|
||||
|
||||
int dbus_gsm_signal_property_changed(DBusConnection *conn, const char *path,
|
||||
const char *interface, const char *name,
|
||||
int type, void *value);
|
||||
|
||||
int dbus_gsm_signal_array_property_changed(DBusConnection *conn,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *name, int type,
|
||||
void *value);
|
||||
|
||||
#define DBUS_GSM_ERROR_INTERFACE "org.ofono.Error"
|
||||
|
||||
static inline DBusMessage *dbus_gsm_invalid_args(DBusMessage *msg)
|
||||
{
|
||||
return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE
|
||||
".InvalidArguments",
|
||||
"Invalid arguments in method call");
|
||||
}
|
||||
|
||||
static inline DBusMessage *dbus_gsm_invalid_format(DBusMessage *msg)
|
||||
{
|
||||
return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE
|
||||
".InvalidFormat",
|
||||
"Argument format is not recognized");
|
||||
}
|
||||
|
||||
static inline DBusMessage *dbus_gsm_not_implemented(DBusMessage *msg)
|
||||
{
|
||||
return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE
|
||||
".NotImplemented",
|
||||
"Implementation not provided");
|
||||
}
|
||||
|
||||
static inline DBusMessage *dbus_gsm_failed(DBusMessage *msg)
|
||||
{
|
||||
return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".Failed",
|
||||
"Operation failed");
|
||||
}
|
||||
|
||||
static inline DBusMessage *dbus_gsm_busy(DBusMessage *msg)
|
||||
{
|
||||
return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".InProgress",
|
||||
"Operation already in progress");
|
||||
}
|
||||
|
||||
static inline DBusMessage *dbus_gsm_not_found(DBusMessage *msg)
|
||||
{
|
||||
return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".NotFound",
|
||||
"Object is not found or not valid for this operation");
|
||||
}
|
||||
|
||||
static inline DBusMessage *dbus_gsm_not_active(DBusMessage *msg)
|
||||
{
|
||||
return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".NotActive",
|
||||
"Operation is not active or in progress");
|
||||
}
|
||||
|
||||
static inline DBusMessage *dbus_gsm_not_supported(DBusMessage *msg)
|
||||
{
|
||||
return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE
|
||||
".NotSupported",
|
||||
"Operation is not supported by the"
|
||||
" network / modem");
|
||||
}
|
||||
|
||||
static inline DBusMessage *dbus_gsm_timed_out(DBusMessage *msg)
|
||||
{
|
||||
return g_dbus_create_error(msg, DBUS_GSM_ERROR_INTERFACE ".Timedout",
|
||||
"Operation failure due to timeout");
|
||||
}
|
||||
|
||||
static inline void dbus_gsm_pending_reply(DBusMessage **msg, DBusMessage *reply)
|
||||
{
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
g_dbus_send_message(conn, reply);
|
||||
|
||||
dbus_message_unref(*msg);
|
||||
*msg = NULL;
|
||||
}
|
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
struct ofono_modem;
|
||||
|
||||
/* 27.007 Section 6.2 */
|
||||
enum ofono_clir_option {
|
||||
OFONO_CLIR_OPTION_DEFAULT = 0,
|
||||
OFONO_CLIR_OPTION_INVOCATION,
|
||||
OFONO_CLIR_OPTION_SUPPRESSION
|
||||
};
|
||||
|
||||
/* 27.007 Section 6.2 */
|
||||
enum ofono_cug_option {
|
||||
OFONO_CUG_OPTION_DEFAULT = 0,
|
||||
OFONO_CUG_OPTION_INVOCATION = 1,
|
||||
};
|
||||
|
||||
enum ofono_error_type {
|
||||
OFONO_ERROR_TYPE_NO_ERROR = 0,
|
||||
OFONO_ERROR_TYPE_CME,
|
||||
OFONO_ERROR_TYPE_CMS,
|
||||
OFONO_ERROR_TYPE_CEER,
|
||||
OFONO_ERROR_TYPE_FAILURE
|
||||
};
|
||||
|
||||
struct ofono_error {
|
||||
enum ofono_error_type type;
|
||||
int error;
|
||||
};
|
||||
|
||||
enum ofono_disconnect_reason {
|
||||
OFONO_DISCONNECT_REASON_UNKNOWN = 0,
|
||||
OFONO_DISCONNECT_REASON_LOCAL_HANGUP,
|
||||
OFONO_DISCONNECT_REASON_REMOTE_HANGUP,
|
||||
OFONO_DISCONNECT_REASON_ERROR,
|
||||
};
|
||||
|
||||
#define OFONO_MAX_PHONE_NUMBER_LENGTH 20
|
||||
|
||||
struct ofono_call {
|
||||
unsigned id;
|
||||
int type;
|
||||
int direction;
|
||||
int status;
|
||||
char phone_number[OFONO_MAX_PHONE_NUMBER_LENGTH + 1];
|
||||
int number_type;
|
||||
int clip_validity;
|
||||
};
|
||||
|
||||
/* Theoretical limit is 16, but each GSM char can be encoded into
|
||||
* * 3 UTF8 characters resulting in 16*3=48 chars
|
||||
* */
|
||||
#define OFONO_MAX_OPERATOR_NAME_LENGTH 63
|
||||
|
||||
struct ofono_network_operator {
|
||||
char name[OFONO_MAX_OPERATOR_NAME_LENGTH + 1];
|
||||
short mcc;
|
||||
short mnc;
|
||||
int status;
|
||||
int tech;
|
||||
};
|
||||
|
||||
/* 27.007 Section 7.11 Call Forwarding */
|
||||
struct ofono_cf_condition {
|
||||
int status;
|
||||
int cls;
|
||||
char phone_number[OFONO_MAX_PHONE_NUMBER_LENGTH + 1];
|
||||
int number_type;
|
||||
int time;
|
||||
};
|
||||
|
||||
/* 27.007 Section 7.12 Call Waiting */
|
||||
struct ofono_cw_condition {
|
||||
int status;
|
||||
int cls;
|
||||
};
|
||||
|
||||
/* Notification functions, the integer values here should map to
|
||||
* values obtained from the modem. The enumerations are the same
|
||||
* as the values for the fields found in 3GPP TS 27.007
|
||||
*
|
||||
* Pass in the integer value -1 if the value is not known
|
||||
* Pass in NULL string value if the value is not known
|
||||
*/
|
||||
typedef void (*ofono_generic_cb_t)(const struct ofono_error *error,
|
||||
void *data);
|
||||
|
||||
typedef void (*ofono_call_list_cb_t)(const struct ofono_error *error,
|
||||
int numcalls,
|
||||
const struct ofono_call *call_list,
|
||||
void *data);
|
||||
|
||||
typedef void (*ofono_current_operator_cb_t)(const struct ofono_error *error,
|
||||
const struct ofono_network_operator *op,
|
||||
void *data);
|
||||
|
||||
typedef void (*ofono_operator_list_cb_t)(const struct ofono_error *error,
|
||||
int total,
|
||||
const struct ofono_network_operator *list,
|
||||
void *data);
|
||||
|
||||
typedef void (*ofono_registration_status_cb_t)(const struct ofono_error *error,
|
||||
int status, int lac, int ci, int tech,
|
||||
void *data);
|
||||
|
||||
typedef void (*ofono_signal_strength_cb_t)(const struct ofono_error *error,
|
||||
int strength, void *data);
|
||||
|
||||
typedef void (*ofono_call_forwarding_query_cb_t)(const struct ofono_error *error,
|
||||
int total,
|
||||
const struct ofono_cf_condition *list,
|
||||
void *data);
|
||||
|
||||
typedef void (*ofono_modem_attribute_query_cb_t)(const struct ofono_error *error,
|
||||
const char *attribute, void *data);
|
||||
|
||||
typedef void (*ofono_call_setting_status_cb_t)(const struct ofono_error *error,
|
||||
int status, void *data);
|
||||
|
||||
typedef void (*ofono_clir_setting_cb_t)(const struct ofono_error *error,
|
||||
int override, int network, void *data);
|
||||
|
||||
typedef void (*ofono_call_waiting_status_cb_t)(const struct ofono_error *error,
|
||||
int num, struct ofono_cw_condition *cond,
|
||||
void *data);
|
||||
|
||||
typedef void (*ofono_call_meter_query_cb_t)(const struct ofono_error *error,
|
||||
int value, void *data);
|
||||
|
||||
typedef void (*ofono_call_meter_puct_query_cb_t)(const struct ofono_error *error,
|
||||
const char *currency, double ppu,
|
||||
void *data);
|
||||
|
||||
struct ofono_modem_attribute_ops {
|
||||
void (*query_manufacturer)(struct ofono_modem *modem,
|
||||
ofono_modem_attribute_query_cb_t cb, void *data);
|
||||
void (*query_serial)(struct ofono_modem *modem,
|
||||
ofono_modem_attribute_query_cb_t cb, void *data);
|
||||
void (*query_model)(struct ofono_modem *modem,
|
||||
ofono_modem_attribute_query_cb_t cb, void *data);
|
||||
void (*query_revision)(struct ofono_modem *modem,
|
||||
ofono_modem_attribute_query_cb_t cb, void *data);
|
||||
};
|
||||
|
||||
struct ofono_modem *ofono_modem_register(struct ofono_modem_attribute_ops *ops);
|
||||
int ofono_modem_unregister(struct ofono_modem *modem);
|
||||
|
||||
void ofono_modem_set_userdata(struct ofono_modem *modem, void *data);
|
||||
void *ofono_modem_userdata(struct ofono_modem *modem);
|
||||
|
||||
/* Network related functions, including registration status, operator selection
|
||||
* and signal strength indicators.
|
||||
*
|
||||
* It is up to the plugin to implement CSQ polling if the modem does not support
|
||||
* vendor extensions for signal strength notification.
|
||||
*/
|
||||
struct ofono_network_registration_ops {
|
||||
void (*registration_status)(struct ofono_modem *modem,
|
||||
ofono_registration_status_cb_t cb, void *data);
|
||||
void (*current_operator)(struct ofono_modem *modem,
|
||||
ofono_current_operator_cb_t cb, void *data);
|
||||
void (*list_operators)(struct ofono_modem *modem,
|
||||
ofono_operator_list_cb_t cb, void *data);
|
||||
void (*register_auto)(struct ofono_modem *modem,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*register_manual)(struct ofono_modem *modem,
|
||||
const struct ofono_network_operator *oper,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*deregister)(struct ofono_modem *modem,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*signal_strength)(struct ofono_modem *modem,
|
||||
ofono_signal_strength_cb_t, void *data);
|
||||
};
|
||||
|
||||
void ofono_signal_strength_notify(struct ofono_modem *modem, int strength);
|
||||
void ofono_network_registration_notify(struct ofono_modem *modem, int status,
|
||||
int lac, int ci, int tech);
|
||||
int ofono_network_registration_register(struct ofono_modem *modem,
|
||||
struct ofono_network_registration_ops *ops);
|
||||
void ofono_network_registration_unregister(struct ofono_modem *modem);
|
||||
|
||||
/* Voice call related functionality, including ATD, ATA, +CHLD, CTFR, CLCC,
|
||||
* SSN notifications (CSSI and CSSU) and VTS.
|
||||
*
|
||||
* It is up to the plugin to implement polling of CLCC if the modem does
|
||||
* not support vendor extensions for call progress indication.
|
||||
*/
|
||||
struct ofono_voicecall_ops {
|
||||
void (*dial)(struct ofono_modem *modem, const char *number,
|
||||
int number_type, enum ofono_clir_option clir,
|
||||
enum ofono_cug_option cug, ofono_generic_cb_t cb,
|
||||
void *data);
|
||||
void (*answer)(struct ofono_modem *modem,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*hangup)(struct ofono_modem *modem,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*list_calls)(struct ofono_modem *modem,
|
||||
ofono_call_list_cb_t cb, void *data);
|
||||
void (*hold_all_active)(struct ofono_modem *modem,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*release_all_held)(struct ofono_modem *modem,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*set_udub)(struct ofono_modem *modem,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*release_all_active)(struct ofono_modem *modem,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*release_specific)(struct ofono_modem *modem, int id,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*private_chat)(struct ofono_modem *modem, int id,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*create_multiparty)(struct ofono_modem *modem,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*transfer)(struct ofono_modem *modem,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*deflect)(struct ofono_modem *modem, const char *number,
|
||||
int number_type, ofono_generic_cb_t cb, void *data);
|
||||
void (*swap_without_accept)(struct ofono_modem *modem,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*send_tones)(struct ofono_modem *modem, const char *tones,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
};
|
||||
|
||||
void ofono_voicecall_notify(struct ofono_modem *modem, const struct ofono_call *call);
|
||||
void ofono_voicecall_disconnected(struct ofono_modem *modem, int id,
|
||||
enum ofono_disconnect_reason reason,
|
||||
const struct ofono_error *error);
|
||||
void ofono_voicecall_cssi(struct ofono_modem *modem, int code, int index);
|
||||
void ofono_voicecall_cssu(struct ofono_modem *modem, int code, int index,
|
||||
const char *number, int number_type);
|
||||
|
||||
int ofono_voicecall_register(struct ofono_modem *modem, struct ofono_voicecall_ops *ops);
|
||||
void ofono_voicecall_unregister(struct ofono_modem *modem);
|
||||
|
||||
struct ofono_call_forwarding_ops {
|
||||
void (*activation)(struct ofono_modem *modem, int type, int cls,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*registration)(struct ofono_modem *modem, int type, int cls,
|
||||
const char *number, int number_type, int time,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*deactivation)(struct ofono_modem *modem, int type, int cls,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*erasure)(struct ofono_modem *modem, int type, int cls,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*query)(struct ofono_modem *modem, int type, int cls,
|
||||
ofono_call_forwarding_query_cb_t cb, void *data);
|
||||
};
|
||||
|
||||
int ofono_call_forwarding_register(struct ofono_modem *modem,
|
||||
struct ofono_call_forwarding_ops *ops);
|
||||
void ofono_call_forwarding_unregister(struct ofono_modem *modem);
|
||||
|
||||
struct ofono_ussd_ops {
|
||||
void (*request)(struct ofono_modem *modem, const char *str,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*cancel)(struct ofono_modem *modem,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
};
|
||||
|
||||
void ofono_ussd_notify(struct ofono_modem *modem, int status, const char *str);
|
||||
int ofono_ussd_register(struct ofono_modem *modem, struct ofono_ussd_ops *ops);
|
||||
void ofono_ussd_unregister(struct ofono_modem *modem);
|
||||
|
||||
struct ofono_call_settings_ops {
|
||||
void (*clip_query)(struct ofono_modem *modem,
|
||||
ofono_call_setting_status_cb_t cb, void *data);
|
||||
void (*colp_query)(struct ofono_modem *modem,
|
||||
ofono_call_setting_status_cb_t cb, void *data);
|
||||
void (*clir_query)(struct ofono_modem *modem, ofono_clir_setting_cb_t cb,
|
||||
void *data);
|
||||
void (*colr_query)(struct ofono_modem *modem,
|
||||
ofono_call_setting_status_cb_t cb, void *data);
|
||||
void (*clir_set)(struct ofono_modem *modem, int mode, ofono_generic_cb_t cb,
|
||||
void *data);
|
||||
};
|
||||
|
||||
int ofono_call_settings_register(struct ofono_modem *modem,
|
||||
struct ofono_call_settings_ops *ops);
|
||||
void ofono_call_settings_unregister(struct ofono_modem *modem);
|
||||
|
||||
struct ofono_call_waiting_ops {
|
||||
void (*query)(struct ofono_modem *modem, int cls,
|
||||
ofono_call_waiting_status_cb_t cb, void *data);
|
||||
void (*set)(struct ofono_modem *modem, int mode, int cls,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
};
|
||||
|
||||
int ofono_call_waiting_register(struct ofono_modem *modem,
|
||||
struct ofono_call_waiting_ops *ops);
|
||||
void ofono_call_waiting_unregister(struct ofono_modem *modem);
|
||||
|
||||
struct ofono_call_meter_ops {
|
||||
void (*call_meter_query)(struct ofono_modem *modem,
|
||||
ofono_call_meter_query_cb_t cb, void *data);
|
||||
void (*acm_query)(struct ofono_modem *modem,
|
||||
ofono_call_meter_query_cb_t cb, void *data);
|
||||
void (*acm_reset)(struct ofono_modem *modem, const char *sim_pin2,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
void (*acm_max_query)(struct ofono_modem *modem,
|
||||
ofono_call_meter_query_cb_t cb, void *data);
|
||||
void (*acm_max_set)(struct ofono_modem *modem, int new_value,
|
||||
const char *sim_pin2, ofono_generic_cb_t cb, void *data);
|
||||
void (*puct_query)(struct ofono_modem *modem,
|
||||
ofono_call_meter_puct_query_cb_t cb, void *data);
|
||||
void (*puct_set)(struct ofono_modem *modem, const char *currency,
|
||||
double ppu, const char *sim_pin2,
|
||||
ofono_generic_cb_t cb, void *data);
|
||||
};
|
||||
|
||||
int ofono_call_meter_register(struct ofono_modem *modem,
|
||||
struct ofono_call_meter_ops *ops);
|
||||
void ofono_call_meter_unregister(struct ofono_modem *modem);
|
||||
void ofono_call_meter_maximum_notify(struct ofono_modem *modem);
|
||||
void ofono_call_meter_changed_notify(struct ofono_modem *modem, int new_value);
|
13
src/main.c
13
src/main.c
|
@ -31,6 +31,8 @@
|
|||
|
||||
#include "ofono.h"
|
||||
|
||||
#include "dbus-gsm.h"
|
||||
|
||||
static GMainLoop *event_loop;
|
||||
|
||||
static void sig_debug(int sig)
|
||||
|
@ -100,6 +102,12 @@ int main(int argc, char **argv)
|
|||
|
||||
__ofono_log_init(option_detach, option_debug);
|
||||
|
||||
if (dbus_gsm_init() != 0)
|
||||
goto cleanup;
|
||||
|
||||
if (__ofono_manager_init() < 0)
|
||||
goto cleanup;
|
||||
|
||||
__ofono_plugin_init(NULL, NULL);
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
|
@ -118,6 +126,11 @@ int main(int argc, char **argv)
|
|||
|
||||
__ofono_plugin_cleanup();
|
||||
|
||||
__ofono_manager_cleanup();
|
||||
|
||||
dbus_gsm_exit();
|
||||
|
||||
cleanup:
|
||||
g_main_loop_unref(event_loop);
|
||||
|
||||
__ofono_log_cleanup();
|
||||
|
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <dbus/dbus.h>
|
||||
#include <glib.h>
|
||||
#include <gdbus.h>
|
||||
|
||||
#include "ofono.h"
|
||||
|
||||
#include "dbus-gsm.h"
|
||||
|
||||
#include "modem.h"
|
||||
#include "driver.h"
|
||||
|
||||
#define MANAGER_INTERFACE "org.ofono.Manager"
|
||||
#define MANAGER_PATH "/"
|
||||
|
||||
static GSList *g_modem_list = NULL;
|
||||
static int g_next_modem_id = 1;
|
||||
|
||||
#if 0
|
||||
struct ofono_modem *manager_find_modem_by_id(int id)
|
||||
{
|
||||
GSList *l;
|
||||
struct ofono_modem *modem;
|
||||
|
||||
for (l = g_modem_list; l; l = l->next) {
|
||||
modem = l->data;
|
||||
|
||||
if (modem->id == id)
|
||||
return modem;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Clients only need to free *modems */
|
||||
static int modem_list(char ***modems)
|
||||
{
|
||||
GSList *l;
|
||||
int i;
|
||||
struct ofono_modem *modem;
|
||||
|
||||
*modems = g_new0(char *, g_slist_length(g_modem_list) + 1);
|
||||
|
||||
if (!*modems)
|
||||
return -1;
|
||||
|
||||
for (l = g_modem_list, i = 0; l; l = l->next, i++) {
|
||||
modem = l->data;
|
||||
|
||||
(*modems)[i] = modem->path;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ofono_modem *ofono_modem_register(struct ofono_modem_attribute_ops *ops)
|
||||
{
|
||||
struct ofono_modem *modem;
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
char **modems;
|
||||
|
||||
modem = modem_create(g_next_modem_id, ops);
|
||||
|
||||
if (modem == NULL)
|
||||
return 0;
|
||||
|
||||
++g_next_modem_id;
|
||||
|
||||
g_modem_list = g_slist_prepend(g_modem_list, modem);
|
||||
|
||||
if (modem_list(&modems) == 0) {
|
||||
dbus_gsm_signal_array_property_changed(conn, MANAGER_PATH,
|
||||
MANAGER_INTERFACE, "Modems",
|
||||
DBUS_TYPE_OBJECT_PATH, &modems);
|
||||
|
||||
g_free(modems);
|
||||
}
|
||||
|
||||
return modem;
|
||||
}
|
||||
|
||||
int ofono_modem_unregister(struct ofono_modem *m)
|
||||
{
|
||||
struct ofono_modem *modem = m;
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
char **modems;
|
||||
|
||||
if (modem == NULL)
|
||||
return -1;
|
||||
|
||||
modem_remove(modem);
|
||||
|
||||
g_modem_list = g_slist_remove(g_modem_list, modem);
|
||||
|
||||
if (modem_list(&modems) == 0) {
|
||||
dbus_gsm_signal_array_property_changed(conn, MANAGER_PATH,
|
||||
MANAGER_INTERFACE, "Modems",
|
||||
DBUS_TYPE_OBJECT_PATH, &modems);
|
||||
|
||||
g_free(modems);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DBusMessage *manager_get_properties(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter dict;
|
||||
DBusMessage *reply;
|
||||
char **modems;
|
||||
|
||||
reply = dbus_message_new_method_return(msg);
|
||||
if (!reply)
|
||||
return NULL;
|
||||
|
||||
if (modem_list(&modems) == -1)
|
||||
return NULL;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
|
||||
PROPERTIES_ARRAY_SIGNATURE,
|
||||
&dict);
|
||||
|
||||
dbus_gsm_dict_append_array(&dict, "Modems", DBUS_TYPE_OBJECT_PATH,
|
||||
&modems);
|
||||
|
||||
g_free(modems);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &dict);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
static GDBusMethodTable manager_methods[] = {
|
||||
{ "GetProperties", "", "a{sv}", manager_get_properties },
|
||||
{ }
|
||||
};
|
||||
|
||||
static GDBusSignalTable manager_signals[] = {
|
||||
{ "PropertyChanged", "sv" },
|
||||
{ }
|
||||
};
|
||||
|
||||
int __ofono_manager_init()
|
||||
{
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
gboolean ret;
|
||||
|
||||
ret = g_dbus_register_interface(conn, "/", MANAGER_INTERFACE,
|
||||
manager_methods, manager_signals,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
if (ret == FALSE)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __ofono_manager_cleanup()
|
||||
{
|
||||
GSList *l;
|
||||
struct ofono_modem *modem;
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
/* Clean up in case plugins didn't unregister the modems */
|
||||
for (l = g_modem_list; l; l = l->next) {
|
||||
modem = l->data;
|
||||
|
||||
if (!modem)
|
||||
continue;
|
||||
|
||||
ofono_debug("plugin owning %s forgot to unregister, cleaning up",
|
||||
modem->path);
|
||||
modem_remove(modem);
|
||||
}
|
||||
|
||||
g_slist_free(g_modem_list);
|
||||
g_modem_list = 0;
|
||||
|
||||
g_dbus_unregister_interface(conn, "/", MANAGER_INTERFACE);
|
||||
}
|
|
@ -0,0 +1,432 @@
|
|||
/*
|
||||
*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include <glib.h>
|
||||
#include <gdbus.h>
|
||||
|
||||
#include "ofono.h"
|
||||
|
||||
#include "dbus-gsm.h"
|
||||
#include "modem.h"
|
||||
#include "driver.h"
|
||||
|
||||
#define MODEM_INTERFACE "org.ofono.Modem"
|
||||
|
||||
#define MODEM_FLAG_INITIALIZING_ATTRS 1
|
||||
|
||||
#define ATTRIBUTE_QUERY_DELAY 0
|
||||
|
||||
struct ofono_modem_data {
|
||||
char *manufacturer;
|
||||
char *model;
|
||||
char *revision;
|
||||
char *serial;
|
||||
GSList *interface_list;
|
||||
int flags;
|
||||
unsigned int idlist;
|
||||
struct ofono_modem_attribute_ops *ops;
|
||||
DBusMessage *pending;
|
||||
guint interface_update;
|
||||
};
|
||||
|
||||
unsigned int modem_alloc_callid(struct ofono_modem *modem)
|
||||
{
|
||||
struct ofono_modem_data *d = modem->modem_info;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 1; i < sizeof(d->idlist) * 8; i++) {
|
||||
if (d->idlist & (0x1 << i))
|
||||
continue;
|
||||
|
||||
d->idlist |= (0x1 << i);
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void modem_release_callid(struct ofono_modem *modem, int id)
|
||||
{
|
||||
struct ofono_modem_data *d = modem->modem_info;
|
||||
|
||||
d->idlist &= ~(0x1 << id);
|
||||
}
|
||||
|
||||
void ofono_modem_set_userdata(struct ofono_modem *modem, void *userdata)
|
||||
{
|
||||
if (modem)
|
||||
modem->userdata = userdata;
|
||||
}
|
||||
|
||||
void *ofono_modem_userdata(struct ofono_modem *modem)
|
||||
{
|
||||
if (modem)
|
||||
return modem->userdata;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void modem_free(gpointer data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
GSList *l;
|
||||
|
||||
if (modem == NULL)
|
||||
return;
|
||||
|
||||
for (l = modem->modem_info->interface_list; l; l = l->next)
|
||||
g_free(l->data);
|
||||
|
||||
g_slist_free(modem->modem_info->interface_list);
|
||||
|
||||
g_free(modem->modem_info->manufacturer);
|
||||
g_free(modem->modem_info->serial);
|
||||
g_free(modem->modem_info->revision);
|
||||
g_free(modem->modem_info->model);
|
||||
|
||||
if (modem->modem_info->pending)
|
||||
dbus_message_unref(modem->modem_info->pending);
|
||||
|
||||
if (modem->modem_info->interface_update)
|
||||
g_source_remove(modem->modem_info->interface_update);
|
||||
|
||||
g_free(modem->modem_info);
|
||||
g_free(modem->path);
|
||||
g_free(modem);
|
||||
}
|
||||
|
||||
static DBusMessage *generate_properties_reply(struct ofono_modem *modem,
|
||||
DBusConnection *conn, DBusMessage *msg)
|
||||
{
|
||||
struct ofono_modem_data *info = modem->modem_info;
|
||||
DBusMessage *reply;
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter dict;
|
||||
|
||||
char **interfaces;
|
||||
int i;
|
||||
GSList *l;
|
||||
|
||||
reply = dbus_message_new_method_return(msg);
|
||||
if (!reply)
|
||||
return NULL;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
|
||||
PROPERTIES_ARRAY_SIGNATURE,
|
||||
&dict);
|
||||
|
||||
if (info->manufacturer)
|
||||
dbus_gsm_dict_append(&dict, "Manufacturer", DBUS_TYPE_STRING,
|
||||
&info->manufacturer);
|
||||
|
||||
if (info->model)
|
||||
dbus_gsm_dict_append(&dict, "Model", DBUS_TYPE_STRING,
|
||||
&info->model);
|
||||
|
||||
if (info->revision)
|
||||
dbus_gsm_dict_append(&dict, "Revision", DBUS_TYPE_STRING,
|
||||
&info->revision);
|
||||
|
||||
if (info->serial)
|
||||
dbus_gsm_dict_append(&dict, "Serial", DBUS_TYPE_STRING,
|
||||
&info->serial);
|
||||
|
||||
interfaces = g_new0(char *, g_slist_length(info->interface_list) + 1);
|
||||
for (i = 0, l = info->interface_list; l; l = l->next, i++)
|
||||
interfaces[i] = l->data;
|
||||
|
||||
dbus_gsm_dict_append_array(&dict, "Interfaces", DBUS_TYPE_STRING,
|
||||
&interfaces);
|
||||
|
||||
g_free(interfaces);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &dict);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
static DBusMessage *modem_get_properties(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
|
||||
if (modem->modem_info->flags & MODEM_FLAG_INITIALIZING_ATTRS) {
|
||||
modem->modem_info->pending = dbus_message_ref(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return generate_properties_reply(modem, conn, msg);
|
||||
}
|
||||
|
||||
static GDBusMethodTable modem_methods[] = {
|
||||
{ "GetProperties", "", "a{sv}", modem_get_properties,
|
||||
G_DBUS_METHOD_FLAG_ASYNC },
|
||||
{ }
|
||||
};
|
||||
|
||||
static GDBusSignalTable modem_signals[] = {
|
||||
{ "PropertyChanged", "sv" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static gboolean trigger_interface_update(void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct ofono_modem_data *info = modem->modem_info;
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
char **interfaces;
|
||||
GSList *l;
|
||||
int i;
|
||||
|
||||
interfaces = g_new0(char *, g_slist_length(info->interface_list) + 1);
|
||||
for (i = 0, l = info->interface_list; l; l = l->next, i++)
|
||||
interfaces[i] = l->data;
|
||||
|
||||
dbus_gsm_signal_array_property_changed(conn, modem->path,
|
||||
MODEM_INTERFACE,
|
||||
"Interfaces", DBUS_TYPE_STRING,
|
||||
&interfaces);
|
||||
|
||||
g_free(interfaces);
|
||||
|
||||
info->interface_update = 0;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void modem_add_interface(struct ofono_modem *modem, const char *interface)
|
||||
{
|
||||
struct ofono_modem_data *info = modem->modem_info;
|
||||
|
||||
info->interface_list =
|
||||
g_slist_prepend(info->interface_list, g_strdup(interface));
|
||||
|
||||
if (info->interface_update == 0)
|
||||
info->interface_update =
|
||||
g_timeout_add(0, trigger_interface_update, modem);
|
||||
}
|
||||
|
||||
void modem_remove_interface(struct ofono_modem *modem, const char *interface)
|
||||
{
|
||||
struct ofono_modem_data *info = modem->modem_info;
|
||||
|
||||
GSList *found = g_slist_find_custom(info->interface_list,
|
||||
interface,
|
||||
(GCompareFunc) strcmp);
|
||||
|
||||
if (!found) {
|
||||
ofono_error("Interface %s not found on the interface_list",
|
||||
interface);
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(found->data);
|
||||
|
||||
info->interface_list =
|
||||
g_slist_remove(info->interface_list, found->data);
|
||||
|
||||
if (info->interface_update == 0)
|
||||
info->interface_update =
|
||||
g_timeout_add(0, trigger_interface_update, modem);
|
||||
}
|
||||
|
||||
static void finish_attr_query(struct ofono_modem *modem)
|
||||
{
|
||||
//struct ofono_modem_data *info = modem->modem_info;
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
DBusMessage *reply;
|
||||
|
||||
modem->modem_info->flags &= ~MODEM_FLAG_INITIALIZING_ATTRS;
|
||||
|
||||
if (!modem->modem_info->pending)
|
||||
return;
|
||||
|
||||
reply = generate_properties_reply(modem, conn,
|
||||
modem->modem_info->pending);
|
||||
|
||||
if (reply)
|
||||
g_dbus_send_message(conn, reply);
|
||||
|
||||
dbus_message_unref(modem->modem_info->pending);
|
||||
modem->modem_info->pending = NULL;
|
||||
}
|
||||
|
||||
static void query_serial_cb(const struct ofono_error *error,
|
||||
const char *serial, void *user)
|
||||
{
|
||||
struct ofono_modem *modem = user;
|
||||
|
||||
if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
|
||||
modem->modem_info->serial = g_strdup(serial);
|
||||
|
||||
finish_attr_query(modem);
|
||||
}
|
||||
|
||||
static gboolean query_serial(gpointer user)
|
||||
{
|
||||
struct ofono_modem *modem = user;
|
||||
|
||||
if (!modem->modem_info->ops->query_serial) {
|
||||
finish_attr_query(modem);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
modem->modem_info->ops->query_serial(modem, query_serial_cb, modem);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void query_revision_cb(const struct ofono_error *error,
|
||||
const char *revision, void *user)
|
||||
{
|
||||
struct ofono_modem *modem = user;
|
||||
|
||||
if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
|
||||
modem->modem_info->revision = g_strdup(revision);
|
||||
|
||||
g_timeout_add(0, query_serial, modem);
|
||||
}
|
||||
|
||||
static gboolean query_revision(gpointer user)
|
||||
{
|
||||
struct ofono_modem *modem = user;
|
||||
|
||||
if (!modem->modem_info->ops->query_revision) {
|
||||
g_timeout_add(0, query_serial, modem);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
modem->modem_info->ops->query_revision(modem, query_revision_cb, modem);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void query_model_cb(const struct ofono_error *error,
|
||||
const char *model, void *user)
|
||||
{
|
||||
struct ofono_modem *modem = user;
|
||||
|
||||
if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
|
||||
modem->modem_info->model = g_strdup(model);
|
||||
|
||||
g_timeout_add(0, query_revision, modem);
|
||||
}
|
||||
|
||||
static gboolean query_model(gpointer user)
|
||||
{
|
||||
struct ofono_modem *modem = user;
|
||||
|
||||
if (!modem->modem_info->ops->query_model) {
|
||||
/* If model is not supported, don't bother querying revision */
|
||||
g_timeout_add(0, query_serial, modem);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
modem->modem_info->ops->query_model(modem, query_model_cb, modem);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void query_manufacturer_cb(const struct ofono_error *error,
|
||||
const char *manufacturer, void *user)
|
||||
{
|
||||
struct ofono_modem *modem = user;
|
||||
|
||||
if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
|
||||
modem->modem_info->manufacturer = g_strdup(manufacturer);
|
||||
|
||||
g_timeout_add(0, query_model, modem);
|
||||
}
|
||||
|
||||
static gboolean query_manufacturer(gpointer user)
|
||||
{
|
||||
struct ofono_modem *modem = user;
|
||||
|
||||
if (!modem->modem_info->ops->query_manufacturer) {
|
||||
g_timeout_add(0, query_model, modem);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
modem->modem_info->ops->query_manufacturer(modem, query_manufacturer_cb,
|
||||
modem);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
struct ofono_modem *modem_create(int id, struct ofono_modem_attribute_ops *ops)
|
||||
{
|
||||
char path[MAX_DBUS_PATH_LEN];
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
struct ofono_modem *modem;
|
||||
|
||||
modem = g_try_new0(struct ofono_modem, 1);
|
||||
if (modem == NULL)
|
||||
return modem;
|
||||
|
||||
modem->modem_info = g_try_new0(struct ofono_modem_data, 1);
|
||||
if (modem->modem_info == NULL) {
|
||||
g_free(modem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
modem->id = id;
|
||||
modem->modem_info->ops = ops;
|
||||
|
||||
snprintf(path, MAX_DBUS_PATH_LEN, "/modem%d", modem->id);
|
||||
modem->path = g_strdup(path);
|
||||
|
||||
if (!g_dbus_register_interface(conn, path, MODEM_INTERFACE,
|
||||
modem_methods, modem_signals, NULL,
|
||||
modem, modem_free)) {
|
||||
ofono_error("Modem interface init failed on path %s", path);
|
||||
modem_free(modem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
modem->modem_info->flags |= MODEM_FLAG_INITIALIZING_ATTRS;
|
||||
g_timeout_add(ATTRIBUTE_QUERY_DELAY, query_manufacturer, modem);
|
||||
|
||||
return modem;
|
||||
}
|
||||
|
||||
void modem_remove(struct ofono_modem *modem)
|
||||
{
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
/* Need to make a copy to keep gdbus happy */
|
||||
char *path = g_strdup(modem->path);
|
||||
|
||||
ofono_debug("Removing modem: %s", modem->path);
|
||||
|
||||
g_dbus_unregister_interface(conn, path, MODEM_INTERFACE);
|
||||
|
||||
g_free(path);
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
struct ofono_modem_attribute_ops;
|
||||
|
||||
struct ofono_modem {
|
||||
int id;
|
||||
char *path;
|
||||
|
||||
void *userdata;
|
||||
|
||||
GSList *ss_control_list;
|
||||
|
||||
struct ofono_modem_data *modem_info;
|
||||
struct network_registration_data *network_registration;
|
||||
struct voicecalls_data *voicecalls;
|
||||
struct call_forwarding_data *call_forwarding;
|
||||
struct ussd_data *ussd;
|
||||
struct call_settings_data *call_settings;
|
||||
struct call_waiting_data *call_waiting;
|
||||
struct call_meter_data *call_meter;
|
||||
};
|
||||
|
||||
struct ofono_modem *modem_create(int id, struct ofono_modem_attribute_ops *ops);
|
||||
void modem_remove(struct ofono_modem *modem);
|
||||
|
||||
void modem_add_interface(struct ofono_modem *modem, const char *interface);
|
||||
void modem_remove_interface(struct ofono_modem *modem, const char *interface);
|
||||
|
||||
unsigned int modem_alloc_callid(struct ofono_modem *modem);
|
||||
void modem_release_callid(struct ofono_modem *modem, int id);
|
File diff suppressed because it is too large
Load Diff
|
@ -23,6 +23,9 @@
|
|||
|
||||
#define OFONO_API_SUBJECT_TO_CHANGE
|
||||
|
||||
int __ofono_manager_init();
|
||||
void __ofono_manager_cleanup();
|
||||
|
||||
#include <ofono/log.h>
|
||||
|
||||
int __ofono_log_init(gboolean detach, gboolean debug);
|
||||
|
|
|
@ -0,0 +1,426 @@
|
|||
/*
|
||||
*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include <glib.h>
|
||||
#include <gdbus.h>
|
||||
|
||||
#include "ofono.h"
|
||||
|
||||
#include "dbus-gsm.h"
|
||||
#include "modem.h"
|
||||
#include "driver.h"
|
||||
#include "common.h"
|
||||
#include "ussd.h"
|
||||
|
||||
#define SUPPLEMENTARY_SERVICES_INTERFACE "org.ofono.SupplementaryServices"
|
||||
|
||||
#define USSD_FLAG_PENDING 0x1
|
||||
|
||||
enum ussd_state {
|
||||
USSD_STATE_IDLE = 0,
|
||||
USSD_STATE_ACTIVE = 1,
|
||||
USSD_STATE_USER_ACTION = 2
|
||||
};
|
||||
|
||||
static struct ussd_data *ussd_create()
|
||||
{
|
||||
struct ussd_data *r;
|
||||
|
||||
r = g_try_new0(struct ussd_data, 1);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void ussd_destroy(gpointer data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct ussd_data *ussd = modem->ussd;
|
||||
|
||||
g_free(ussd);
|
||||
}
|
||||
|
||||
struct ss_control_entry {
|
||||
char *service;
|
||||
ss_control_cb_t cb;
|
||||
};
|
||||
|
||||
static struct ss_control_entry *ss_control_entry_create(const char *service,
|
||||
ss_control_cb_t cb)
|
||||
{
|
||||
struct ss_control_entry *r;
|
||||
|
||||
r = g_try_new0(struct ss_control_entry, 1);
|
||||
|
||||
if (!r)
|
||||
return r;
|
||||
|
||||
r->service = g_strdup(service);
|
||||
r->cb = cb;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void ss_control_entry_destroy(struct ss_control_entry *ca)
|
||||
{
|
||||
g_free(ca->service);
|
||||
g_free(ca);
|
||||
}
|
||||
|
||||
static gint ss_control_entry_compare(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
const struct ss_control_entry *ca = a;
|
||||
const struct ss_control_entry *cb = b;
|
||||
int ret;
|
||||
|
||||
ret = strcmp(ca->service, cb->service);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ca->cb < cb->cb)
|
||||
return -1;
|
||||
|
||||
if (ca->cb > cb->cb)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gint ss_control_entry_find_by_service(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
const struct ss_control_entry *ca = a;
|
||||
//const char *cb = b;
|
||||
|
||||
return strcmp(ca->service, b);
|
||||
}
|
||||
|
||||
gboolean ss_control_register(struct ofono_modem *modem, const char *str,
|
||||
ss_control_cb_t cb)
|
||||
{
|
||||
//struct ussd_data *ussd = modem->ussd;
|
||||
struct ss_control_entry *entry;
|
||||
|
||||
if (!modem)
|
||||
return FALSE;
|
||||
|
||||
entry = ss_control_entry_create(str, cb);
|
||||
|
||||
if (!entry)
|
||||
return FALSE;
|
||||
|
||||
modem->ss_control_list = g_slist_append(modem->ss_control_list, entry);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void ss_control_unregister(struct ofono_modem *modem, const char *str,
|
||||
ss_control_cb_t cb)
|
||||
{
|
||||
//struct ussd_data *ussd = modem->ussd;
|
||||
const struct ss_control_entry entry = { (char *)str, cb };
|
||||
GSList *l;
|
||||
|
||||
if (!modem)
|
||||
return;
|
||||
|
||||
l = g_slist_find_custom(modem->ss_control_list, &entry,
|
||||
ss_control_entry_compare);
|
||||
|
||||
if (!l)
|
||||
return;
|
||||
|
||||
ss_control_entry_destroy(l->data);
|
||||
modem->ss_control_list = g_slist_remove(modem->ss_control_list,
|
||||
l->data);
|
||||
}
|
||||
|
||||
static gboolean recognized_control_string(struct ofono_modem *modem,
|
||||
const char *ss_str,
|
||||
DBusMessage *msg)
|
||||
{
|
||||
//struct ussd_data *ussd = modem->ussd;
|
||||
char *str = g_strdup(ss_str);
|
||||
char *sc, *sia, *sib, *sic, *dn;
|
||||
int type;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
ofono_debug("parsing control string");
|
||||
|
||||
if (parse_ss_control_string(str, &type, &sc, &sia, &sib, &sic, &dn)) {
|
||||
GSList *l = modem->ss_control_list;
|
||||
|
||||
ofono_debug("Got parse result: %d, %s, %s, %s, %s, %s",
|
||||
type, sc, sia, sib, sic, dn);
|
||||
|
||||
while ((l = g_slist_find_custom(l, sc,
|
||||
ss_control_entry_find_by_service)) != NULL) {
|
||||
struct ss_control_entry *entry = l->data;
|
||||
|
||||
if (entry->cb(modem, type, sc, sia, sib, sic, dn, msg)) {
|
||||
ret = TRUE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
l = l->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Handle all strings that control voice calls */
|
||||
|
||||
/* TODO: Handle Multiple subscriber profile DN*59#SEND and *59#SEND
|
||||
*/
|
||||
|
||||
/* Note: SIM PIN/PIN2 change and unblock and IMEI presentation
|
||||
* procedures are not handled by the daemon since they are not followed
|
||||
* by SEND and are not valid USSD requests.
|
||||
*/
|
||||
|
||||
/* TODO: Handle Password registration according to 22.030 Section 6.5.4
|
||||
*/
|
||||
|
||||
out:
|
||||
g_free(str);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ofono_ussd_notify(struct ofono_modem *modem, int status, const char *str)
|
||||
{
|
||||
struct ussd_data *ussd = modem->ussd;
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
const char *ussdstr = "USSD";
|
||||
const char sig[] = { DBUS_TYPE_STRING, 0 };
|
||||
DBusMessage *reply;
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter variant;
|
||||
|
||||
if (status == USSD_STATUS_NOT_SUPPORTED) {
|
||||
ussd->state = USSD_STATE_IDLE;
|
||||
reply = dbus_gsm_not_supported(ussd->pending);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (status == USSD_STATUS_TIMED_OUT) {
|
||||
ussd->state = USSD_STATE_IDLE;
|
||||
reply = dbus_gsm_timed_out(ussd->pending);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* TODO: Rework this in the Agent framework */
|
||||
if (ussd->state == USSD_STATE_ACTIVE) {
|
||||
if (status == USSD_STATUS_ACTION_REQUIRED) {
|
||||
ofono_error("Unable to handle action required ussd");
|
||||
return;
|
||||
}
|
||||
|
||||
reply = dbus_message_new_method_return(ussd->pending);
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
|
||||
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
|
||||
&ussdstr);
|
||||
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, sig,
|
||||
&variant);
|
||||
|
||||
dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING,
|
||||
&str);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &variant);
|
||||
|
||||
ussd->state = USSD_STATE_IDLE;
|
||||
} else {
|
||||
ofono_error("Received an unsolicited USSD, ignoring for now...");
|
||||
ofono_debug("USSD is: status: %d, %s", status, str);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
out:
|
||||
g_dbus_send_message(conn, reply);
|
||||
|
||||
dbus_message_unref(ussd->pending);
|
||||
ussd->pending = NULL;
|
||||
}
|
||||
|
||||
static void ussd_callback(const struct ofono_error *error, void *data)
|
||||
{
|
||||
struct ussd_data *ussd = data;
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
DBusMessage *reply;
|
||||
|
||||
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
|
||||
ofono_debug("ussd request failed with error: %s",
|
||||
telephony_error_to_str(error));
|
||||
|
||||
ussd->flags &= ~USSD_FLAG_PENDING;
|
||||
|
||||
if (!ussd->pending)
|
||||
return;
|
||||
|
||||
if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
|
||||
ussd->state = USSD_STATE_ACTIVE;
|
||||
return;
|
||||
}
|
||||
|
||||
reply = dbus_gsm_failed(ussd->pending);
|
||||
|
||||
g_dbus_send_message(conn, reply);
|
||||
|
||||
dbus_message_unref(ussd->pending);
|
||||
ussd->pending = NULL;
|
||||
}
|
||||
|
||||
static DBusMessage *ussd_initiate(DBusConnection *conn, DBusMessage *msg,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct ussd_data *ussd = modem->ussd;
|
||||
const char *str;
|
||||
|
||||
if (ussd->flags & USSD_FLAG_PENDING)
|
||||
return dbus_gsm_busy(msg);
|
||||
|
||||
if (ussd->state == USSD_STATE_ACTIVE)
|
||||
return dbus_gsm_busy(msg);
|
||||
|
||||
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str,
|
||||
DBUS_TYPE_INVALID) == FALSE)
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
|
||||
if (strlen(str) == 0)
|
||||
return dbus_gsm_invalid_format(msg);
|
||||
|
||||
ofono_debug("checking if this is a recognized control string");
|
||||
if (recognized_control_string(modem, str, msg))
|
||||
return NULL;
|
||||
|
||||
ofono_debug("No.., checking if this is a USSD string");
|
||||
if (!valid_ussd_string(str))
|
||||
return dbus_gsm_invalid_format(msg);
|
||||
|
||||
ofono_debug("OK, running USSD request");
|
||||
|
||||
if (!ussd->ops->request)
|
||||
return dbus_gsm_not_implemented(msg);
|
||||
|
||||
ussd->flags |= USSD_FLAG_PENDING;
|
||||
ussd->pending = dbus_message_ref(msg);
|
||||
|
||||
ussd->ops->request(modem, str, ussd_callback, ussd);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ussd_cancel_callback(const struct ofono_error *err, void *data)
|
||||
{
|
||||
//struct ussd_data *ussd = data;
|
||||
}
|
||||
|
||||
static DBusMessage *ussd_cancel(DBusConnection *conn, DBusMessage *msg,
|
||||
void *data)
|
||||
{
|
||||
struct ofono_modem *modem = data;
|
||||
struct ussd_data *ussd = modem->ussd;
|
||||
|
||||
if (ussd->flags & USSD_FLAG_PENDING)
|
||||
return dbus_gsm_busy(msg);
|
||||
|
||||
if (ussd->state == USSD_STATE_IDLE)
|
||||
return dbus_gsm_not_active(msg);
|
||||
|
||||
if (!ussd->ops->cancel)
|
||||
return dbus_gsm_not_implemented(msg);
|
||||
|
||||
ussd->flags |= USSD_FLAG_PENDING;
|
||||
ussd->pending = dbus_message_ref(msg);
|
||||
|
||||
ussd->ops->cancel(modem, ussd_cancel_callback, ussd);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GDBusMethodTable ussd_methods[] = {
|
||||
{ "Initiate", "s", "sv", ussd_initiate,
|
||||
G_DBUS_METHOD_FLAG_ASYNC },
|
||||
{ "Cancel", "", "", ussd_cancel,
|
||||
G_DBUS_METHOD_FLAG_ASYNC },
|
||||
{ }
|
||||
};
|
||||
|
||||
static GDBusSignalTable ussd_signals[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
int ofono_ussd_register(struct ofono_modem *modem, struct ofono_ussd_ops *ops)
|
||||
{
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
if (modem == NULL)
|
||||
return -1;
|
||||
|
||||
if (ops == NULL)
|
||||
return -1;
|
||||
|
||||
modem->ussd = ussd_create();
|
||||
|
||||
if (modem->ussd == NULL)
|
||||
return -1;
|
||||
|
||||
modem->ussd->ops = ops;
|
||||
|
||||
if (!g_dbus_register_interface(conn, modem->path,
|
||||
SUPPLEMENTARY_SERVICES_INTERFACE,
|
||||
ussd_methods, ussd_signals, NULL,
|
||||
modem, ussd_destroy)) {
|
||||
ofono_error("Could not create %s interface",
|
||||
SUPPLEMENTARY_SERVICES_INTERFACE);
|
||||
|
||||
ussd_destroy(modem->ussd);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
modem_add_interface(modem, SUPPLEMENTARY_SERVICES_INTERFACE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ofono_ussd_unregister(struct ofono_modem *modem)
|
||||
{
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
if (modem->ussd == NULL)
|
||||
return;
|
||||
|
||||
modem_remove_interface(modem, SUPPLEMENTARY_SERVICES_INTERFACE);
|
||||
g_dbus_unregister_interface(conn, modem->path,
|
||||
SUPPLEMENTARY_SERVICES_INTERFACE);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
struct ussd_data {
|
||||
struct ofono_ussd_ops *ops;
|
||||
int state;
|
||||
DBusMessage *pending;
|
||||
int flags;
|
||||
};
|
||||
|
||||
typedef gboolean (*ss_control_cb_t)(struct ofono_modem *modem, int type,
|
||||
const char *sc,
|
||||
const char *sia, const char *sib,
|
||||
const char *sic, const char *dn,
|
||||
DBusMessage *msg);
|
||||
|
||||
gboolean ss_control_register(struct ofono_modem *modem, const char *str,
|
||||
ss_control_cb_t cb);
|
||||
|
||||
void ss_control_unregister(struct ofono_modem *modem, const char *str,
|
||||
ss_control_cb_t cb);
|
|
@ -0,0 +1,693 @@
|
|||
/*
|
||||
*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
Name: GSM 03.38 to Unicode
|
||||
Unicode version: 3.0
|
||||
Table version: 1.1
|
||||
Table format: Format A
|
||||
Date: 2000 May 30
|
||||
Authors: Ken Whistler
|
||||
Kent Karlsson
|
||||
Markus Kuhn
|
||||
|
||||
Copyright (c) 2000 Unicode, Inc. All Rights reserved.
|
||||
|
||||
This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
|
||||
No claims are made as to fitness for any particular purpose. No
|
||||
warranties of any kind are expressed or implied. The recipient
|
||||
agrees to determine applicability of information provided. If this
|
||||
file has been provided on optical media by Unicode, Inc., the sole
|
||||
remedy for any claim will be exchange of defective media within 90
|
||||
days of receipt.
|
||||
|
||||
Unicode, Inc. hereby grants the right to freely use the information
|
||||
supplied in this file in the creation of products supporting the
|
||||
Unicode Standard, and to make copies of this file in any form for
|
||||
internal or external distribution as long as this notice remains
|
||||
attached.
|
||||
*/
|
||||
|
||||
/* GSM to Unicode extension table, for GSM sequences starting with 0x1B */
|
||||
static unsigned short gsm_extension[] =
|
||||
{
|
||||
0x0A, 0x000C, /* See NOTE 3 in 23.038 */
|
||||
0x14, 0x005E,
|
||||
0x1B, 0x0020, /* See NOTE 1 in 23.038 */
|
||||
0x28, 0x007B,
|
||||
0x29, 0x007D,
|
||||
0x2F, 0x005C,
|
||||
0x3C, 0x005B,
|
||||
0x3D, 0x007E,
|
||||
0x3E, 0x005D,
|
||||
0x40, 0x007C,
|
||||
0x65, 0x20AC
|
||||
};
|
||||
|
||||
/* Used for conversion of GSM to Unicode */
|
||||
static unsigned short gsm_table[] =
|
||||
{
|
||||
0x0040, 0x00A3, 0x0024, 0x00A5, 0x00E8, 0x00E9, 0x00F9, 0x00EC, /* 0x07 */
|
||||
0x00F2, 0x00E7, 0x000A, 0x00D8, 0x00F8, 0x000D, 0x00C5, 0x00E5, /* 0x0F */
|
||||
0x0394, 0x005F, 0x03A6, 0x0393, 0x039B, 0x03A9, 0x03A0, 0x03A8, /* 0x17 */
|
||||
0x03A3, 0x0398, 0x039E, 0x00A0, 0x00C6, 0x00E6, 0x00DF, 0x00C9, /* 0x1F */
|
||||
0x0020, 0x0021, 0x0022, 0x0023, 0x00A4, 0x0025, 0x0026, 0x0027, /* 0x27 */
|
||||
0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, /* 0x2F */
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, /* 0x37 */
|
||||
0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, /* 0x3F */
|
||||
0x00A1, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, /* 0x47 */
|
||||
0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, /* 0x4F */
|
||||
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, /* 0x57 */
|
||||
0x0058, 0x0059, 0x005A, 0x00C4, 0x00D6, 0x00D1, 0x00DC, 0x00A7, /* 0x5F */
|
||||
0x00BF, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, /* 0x67 */
|
||||
0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, /* 0x6F */
|
||||
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, /* 0x77 */
|
||||
0x0078, 0x0079, 0x007A, 0x00E4, 0x00F6, 0x00F1, 0x00FC, 0x00E0 /* 0x7F */
|
||||
};
|
||||
|
||||
#define GUND 0xFFFF
|
||||
|
||||
/* 3GPP 27.005 Annex A */
|
||||
static unsigned short unicode_256_table[] =
|
||||
{
|
||||
GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0x07 */
|
||||
GUND, GUND, 0x0A, GUND, 0x1B0A, 0x0D, GUND, GUND, /* 0x0F */
|
||||
GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0x17 */
|
||||
GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0x1F */
|
||||
0x20, 0x21, 0x22, 0x23, 0x02, 0x25, 0x26, 0x27, /* 0x27 */
|
||||
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, /* 0x2F */
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x37 */
|
||||
0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, /* 0x3F */
|
||||
0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x47 */
|
||||
0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* 0x4F */
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x57 */
|
||||
0x58, 0x59, 0x5A, 0x1B3C, 0x1B2F, 0x1B3E, 0x1B14, 0x11, /* 0x5F */
|
||||
GUND, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x67 */
|
||||
0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, /* 0x6F */
|
||||
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x77 */
|
||||
0x78, 0x79, 0x7A, 0x1B28, 0x1B40, 0x1B29, 0x1B3D, GUND, /* 0x7F */
|
||||
GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0x87 */
|
||||
GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0x8F */
|
||||
GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0x97 */
|
||||
GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0x9F */
|
||||
GUND, 0x40, GUND, 0x01, 0x24, 0x03, GUND, 0x5f, /* 0xA7 */
|
||||
GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0xAF */
|
||||
GUND, GUND, GUND, GUND, GUND, GUND, GUND, GUND, /* 0xB7 */
|
||||
GUND, GUND, GUND, GUND, GUND, GUND, GUND, 0x60, /* 0xBF */
|
||||
0x41, 0x41, 0x41, 0x41, 0x5B, 0x0E, 0x1C, 0x09, /* 0xC7 */
|
||||
0x45, 0x1F, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49, /* 0xCF */
|
||||
GUND, 0x5D, 0x4F, 0x4F, 0x4F, 0x4F, 0x5C, GUND, /* 0xD7 */
|
||||
0x0B, 0x55, 0x55, 0x55, 0x5E, 0x59, GUND, 0x1E, /* 0xDF */
|
||||
0x7F, 0x61, 0x61, 0x61, 0x7B, 0x0F, 0x1D, 0x09, /* 0xE7 */
|
||||
0x04, 0x05, 0x65, 0x65, 0x07, 0x69, 0x69, 0x69, /* 0xEF */
|
||||
GUND, 0x7D, 0x08, 0x6F, 0x6F, 0x6F, 0x7C, GUND, /* 0xF7 */
|
||||
0x0C, 0x06, 0x75, 0x75, 0x7E, 0x79, GUND, 0x79 /* 0xFF */
|
||||
};
|
||||
|
||||
/* Starts at 0x0390 */
|
||||
static unsigned short greek_unicode_offset = 0x0390;
|
||||
|
||||
static unsigned short greek_unicode_table[] =
|
||||
{
|
||||
GUND, GUND, GUND, 0x13, 0x10, GUND, GUND, GUND, /* 0x07 */
|
||||
0x19, GUND, GUND, 0x14, GUND, GUND, 0x1A, GUND, /* 0x0F */
|
||||
0x16, GUND, GUND, 0x18, GUND, GUND, 0x12, GUND, /* 0x17 */
|
||||
0x17, 0x15, GUND, GUND, GUND, GUND, GUND, GUND, /* 0x1F */
|
||||
};
|
||||
|
||||
#define UTF8_LENGTH(c) \
|
||||
((c) < 0x80 ? 1 : \
|
||||
((c) < 0x800 ? 2 : 3))
|
||||
|
||||
static unsigned short gsm_extension_table_lookup(unsigned char k)
|
||||
{
|
||||
static unsigned int ext_table_len =
|
||||
(sizeof(gsm_extension) / sizeof(unsigned short)) >> 1;
|
||||
unsigned int i;
|
||||
unsigned short *t;
|
||||
|
||||
for (i = 0, t = gsm_extension; i < ext_table_len; i++) {
|
||||
if (t[0] == k)
|
||||
return t[1];
|
||||
t += 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Converts text coded using GSM codec into UTF8 encoded text. If len
|
||||
* is less than 0, and terminator character is given, the length is
|
||||
* computed automatically.
|
||||
*
|
||||
* Returns newly-allocated UTF8 encoded string or NULL if the conversion
|
||||
* could not be performed. Returns the number of bytes read from the
|
||||
* GSM encoded string in items_read (if not NULL), not including the
|
||||
* terminator character. Returns the number of bytes written into the UTF8
|
||||
* encoded string in items_written (if not NULL) not including the terminal
|
||||
* '\0' character. The caller is reponsible for freeing the returned value.
|
||||
*/
|
||||
char *convert_gsm_to_utf8(const unsigned char *text, long len,
|
||||
long *items_read, long *items_written,
|
||||
unsigned char terminator)
|
||||
{
|
||||
char *res = NULL;
|
||||
char *out;
|
||||
long i = 0;
|
||||
long res_length;
|
||||
|
||||
if (len == 0 || (len < 0 && !terminator))
|
||||
goto err_out;
|
||||
|
||||
if (len < 0) {
|
||||
i = 0;
|
||||
|
||||
while (text[i] != terminator)
|
||||
i++;
|
||||
|
||||
len = i;
|
||||
}
|
||||
|
||||
for (i = 0, res_length = 0; i < len; i++) {
|
||||
unsigned short c;
|
||||
|
||||
if (text[i] > 0x7f)
|
||||
goto err_out;
|
||||
|
||||
if (text[i] == 0x1b) {
|
||||
++i;
|
||||
if (i >= len)
|
||||
goto err_out;
|
||||
|
||||
c = gsm_extension_table_lookup(text[i]);
|
||||
|
||||
if (c == 0)
|
||||
goto err_out;
|
||||
} else {
|
||||
c = gsm_table[text[i]];
|
||||
}
|
||||
|
||||
res_length += UTF8_LENGTH(c);
|
||||
}
|
||||
|
||||
res = g_malloc(res_length + 1);
|
||||
|
||||
if (!res)
|
||||
goto err_out;
|
||||
|
||||
out = res;
|
||||
|
||||
i = 0;
|
||||
while (out < res + res_length) {
|
||||
unsigned short c;
|
||||
|
||||
if (text[i] == 0x1b)
|
||||
c = gsm_extension_table_lookup(text[++i]);
|
||||
else
|
||||
c = gsm_table[text[i]];
|
||||
|
||||
out += g_unichar_to_utf8(c, out);
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
*out = '\0';
|
||||
|
||||
if (items_written)
|
||||
*items_written = out - res;
|
||||
|
||||
err_out:
|
||||
if (items_read)
|
||||
*items_read = i;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static unsigned short unicode_to_gsm(unsigned short c)
|
||||
{
|
||||
static int greek_unicode_size = sizeof(greek_unicode_table) /
|
||||
sizeof(unsigned short);
|
||||
unsigned short converted = GUND;
|
||||
|
||||
if (c == 0x20AC)
|
||||
converted = 0x1B65;
|
||||
else if (c < 256)
|
||||
converted = unicode_256_table[c];
|
||||
else if ((c >= greek_unicode_offset) &&
|
||||
(c < (greek_unicode_offset + greek_unicode_size))) {
|
||||
converted = greek_unicode_table[c-greek_unicode_offset];
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Converts UTF-8 encoded text to GSM alphabet. The result is unpacked,
|
||||
* with the 7th bit always 0. If terminator is not 0, a terminator character
|
||||
* is appended to the result. This should be in the range 0x80-0xf0
|
||||
*
|
||||
* Returns the encoded data or NULL if the data could not be encoded. The
|
||||
* data must be freed by the caller. If items_read is not NULL, it contains
|
||||
* the actual number of bytes read. If items_written is not NULL, contains
|
||||
* the number of bytes written.
|
||||
*/
|
||||
unsigned char *convert_utf8_to_gsm(const char *text, long len,
|
||||
long *items_read, long *items_written,
|
||||
unsigned char terminator)
|
||||
{
|
||||
long nchars = 0;
|
||||
const char *in;
|
||||
unsigned char *out;
|
||||
unsigned char *res = NULL;
|
||||
long res_len;
|
||||
long i;
|
||||
|
||||
in = text;
|
||||
res_len = 0;
|
||||
|
||||
while ((len < 0 || text + len - in > 0) && *in) {
|
||||
long max = len < 0 ? 6 : text + len - in;
|
||||
gunichar c = g_utf8_get_char_validated(in, max);
|
||||
unsigned short converted = GUND;
|
||||
|
||||
if (c & 0x80000000)
|
||||
goto err_out;
|
||||
|
||||
if (c > 0xffff)
|
||||
goto err_out;
|
||||
|
||||
converted = unicode_to_gsm(c);
|
||||
|
||||
if (converted == GUND)
|
||||
goto err_out;
|
||||
|
||||
if (converted & 0x1b00)
|
||||
res_len += 2;
|
||||
else
|
||||
res_len += 1;
|
||||
|
||||
in = g_utf8_next_char(in);
|
||||
nchars += 1;
|
||||
}
|
||||
|
||||
res = g_malloc(res_len + (terminator ? 1 : 0));
|
||||
|
||||
if (!res)
|
||||
goto err_out;
|
||||
|
||||
in = text;
|
||||
out = res;
|
||||
for (i = 0; i < nchars; i++) {
|
||||
unsigned short converted;
|
||||
|
||||
gunichar c = g_utf8_get_char(in);
|
||||
|
||||
converted = unicode_to_gsm(c);
|
||||
if (converted & 0x1b00) {
|
||||
*out = 0x1b;
|
||||
++out;
|
||||
}
|
||||
|
||||
*out = converted;
|
||||
++out;
|
||||
|
||||
in = g_utf8_next_char(in);
|
||||
}
|
||||
|
||||
if (terminator)
|
||||
*out = terminator;
|
||||
|
||||
if (items_written)
|
||||
*items_written = out - res;
|
||||
|
||||
err_out:
|
||||
if (items_read)
|
||||
*items_read = in - text;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Decodes the hex encoded data and converts to a byte array. If terminator
|
||||
* is not 0, the terminator character is appended to the end of the result.
|
||||
* This might be useful for converting GSM encoded data if the CSCS is set
|
||||
* to HEX.
|
||||
*
|
||||
* Please note that this since GSM does allow embedded null characeters, use
|
||||
* of the terminator or the items_writen is encouraged to find the real size
|
||||
* of the result.
|
||||
*/
|
||||
unsigned char *decode_hex_own_buf(const char *in, long len, long *items_written,
|
||||
unsigned char terminator,
|
||||
unsigned char *buf)
|
||||
{
|
||||
long i, j;
|
||||
char c;
|
||||
unsigned char b;
|
||||
|
||||
if (len < 0)
|
||||
len = strlen(in);
|
||||
|
||||
len &= ~0x1;
|
||||
|
||||
for (i = 0, j = 0; i < len; i++, j++) {
|
||||
c = toupper(in[i]);
|
||||
|
||||
if (c >= '0' && c <= '9')
|
||||
b = c - '0';
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
b = 10 + c - 'A';
|
||||
else
|
||||
return NULL;
|
||||
|
||||
i += 1;
|
||||
|
||||
c = toupper(in[i]);
|
||||
|
||||
if (c >= '0' && c <= '9')
|
||||
b = b*16 + c - '0';
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
b = b*16 + 10 + c - 'A';
|
||||
else
|
||||
return NULL;
|
||||
|
||||
buf[j] = b;
|
||||
}
|
||||
|
||||
if (terminator)
|
||||
buf[j] = terminator;
|
||||
|
||||
if (items_written)
|
||||
*items_written = j;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
unsigned char *decode_hex(const char *in, long len, long *items_written,
|
||||
unsigned char terminator)
|
||||
{
|
||||
long i;
|
||||
char c;
|
||||
unsigned char *buf;
|
||||
|
||||
if (len < 0)
|
||||
len = strlen(in);
|
||||
|
||||
len &= ~0x1;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
c = toupper(in[i]);
|
||||
|
||||
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'))
|
||||
continue;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = g_new(unsigned char, (len >> 1) + (terminator ? 1 : 0));
|
||||
|
||||
return decode_hex_own_buf(in, len, items_written, terminator, buf);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Encodes the data using hexadecimal characters. len can be negative,
|
||||
* in that case the terminator is used to find the last character. This is
|
||||
* useful for handling GSM-encoded strings which allow ASCII NULL character
|
||||
* in the stream.
|
||||
*/
|
||||
char *encode_hex_own_buf(const unsigned char *in, long len,
|
||||
unsigned char terminator, char *buf)
|
||||
{
|
||||
long i, j;
|
||||
char c;
|
||||
|
||||
if (len < 0) {
|
||||
i = 0;
|
||||
|
||||
while (in[i] != terminator)
|
||||
i++;
|
||||
|
||||
len = i;
|
||||
}
|
||||
|
||||
for (i = 0, j = 0; i < len; i++, j++) {
|
||||
c = (in[i] >> 4) & 0xf;
|
||||
|
||||
if (c <= 9)
|
||||
buf[j] = '0' + c;
|
||||
else
|
||||
buf[j] = 'A' + c - 10;
|
||||
|
||||
j += 1;
|
||||
|
||||
c = (in[i]) & 0xf;
|
||||
|
||||
if (c <= 9)
|
||||
buf[j] = '0' + c;
|
||||
else
|
||||
buf[j] = 'A' + c - 10;
|
||||
}
|
||||
|
||||
buf[j] = '\0';
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *encode_hex(const unsigned char *in, long len, unsigned char terminator)
|
||||
{
|
||||
char *buf;
|
||||
int i;
|
||||
|
||||
if (len < 0) {
|
||||
i = 0;
|
||||
|
||||
while (in[i] != terminator)
|
||||
i++;
|
||||
|
||||
len = i;
|
||||
}
|
||||
|
||||
buf = g_new(char, len * 2 + 1);
|
||||
|
||||
return encode_hex_own_buf(in, len, terminator, buf);
|
||||
}
|
||||
|
||||
unsigned char *unpack_7bit_own_buf(const unsigned char *in, long len,
|
||||
int byte_offset, gboolean cb,
|
||||
long max_to_unpack, long *items_written,
|
||||
unsigned char terminator,
|
||||
unsigned char *buf)
|
||||
{
|
||||
unsigned char rest = 0;
|
||||
unsigned char *out = buf;
|
||||
int bits = 7 - (byte_offset % 7);
|
||||
long i;
|
||||
|
||||
if (len <= 0)
|
||||
return NULL;
|
||||
|
||||
/* In the case of CB, unpack as much as possible */
|
||||
if (cb == TRUE)
|
||||
max_to_unpack = len * 8 / 7;
|
||||
|
||||
for (i = 0; (i < len) && ((out-buf) < max_to_unpack); i++) {
|
||||
/* Grab what we have in the current octet */
|
||||
*out = (in[i] & ((1 << bits) - 1)) << (7 - bits);
|
||||
|
||||
/* Append what we have from the previous octet, if any */
|
||||
*out |= rest;
|
||||
|
||||
/* Figure out the remainder */
|
||||
rest = (in[i] >> bits) & ((1 << (8-bits)) - 1);
|
||||
|
||||
/* We have the entire character, here we don't increate
|
||||
* out if this is we started at an offset. Instead
|
||||
* we effectively populate variable rest */
|
||||
if (i != 0 || bits == 7)
|
||||
out++;
|
||||
|
||||
if ((out-buf) == max_to_unpack)
|
||||
break;
|
||||
|
||||
/* We expected only 1 bit from this octet, means there's 7
|
||||
* left, take care of them here */
|
||||
if (bits == 1) {
|
||||
*out = rest;
|
||||
out++;
|
||||
bits = 7;
|
||||
rest = 0;
|
||||
} else
|
||||
bits = bits - 1;
|
||||
}
|
||||
|
||||
/* According to 23.038 6.1.2.3.1, last paragraph:
|
||||
* "If the total number of characters to be sent equals (8n-1)
|
||||
* where n=1,2,3 etc. then there are 7 spare bits at the end
|
||||
* of the message. To avoid the situation where the receiving
|
||||
* entity confuses 7 binary zero pad bits as the @ character,
|
||||
* the carriage return or <CR> character shall be used for
|
||||
* padding in this situation, just as for Cell Broadcast."
|
||||
*
|
||||
* "The receiving entity shall remove the final <CR> character where
|
||||
* the message ends on an octet boundary with <CR> as the last
|
||||
* character.
|
||||
*/
|
||||
if (cb && (((out - buf) % 8) == 0) && (*(out-1) == '\r'))
|
||||
out = out - 1;
|
||||
|
||||
if (terminator)
|
||||
*out = terminator;
|
||||
|
||||
if (items_written)
|
||||
*items_written = out - buf;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
unsigned char *unpack_7bit(const unsigned char *in, long len, int byte_offset,
|
||||
gboolean cb, long max_to_unpack,
|
||||
long *items_written, unsigned char terminator)
|
||||
{
|
||||
unsigned char *buf = g_new(unsigned char,
|
||||
len * 8 / 7 + (terminator ? 1 : 0));
|
||||
|
||||
return unpack_7bit_own_buf(in, len, byte_offset, cb, max_to_unpack,
|
||||
items_written, terminator, buf);
|
||||
}
|
||||
|
||||
unsigned char *pack_7bit_own_buf(const unsigned char *in, long len,
|
||||
int byte_offset, gboolean cb,
|
||||
long *items_written,
|
||||
unsigned char terminator,
|
||||
unsigned char *buf)
|
||||
{
|
||||
int bits = 7 - (byte_offset % 7);
|
||||
unsigned char *out = buf;
|
||||
long i;
|
||||
long total_bits;
|
||||
|
||||
if (len == 0 || !items_written)
|
||||
return NULL;
|
||||
|
||||
if (len < 0) {
|
||||
i = 0;
|
||||
|
||||
while (in[i] != terminator)
|
||||
i++;
|
||||
|
||||
len = i;
|
||||
}
|
||||
|
||||
total_bits = len * 7;
|
||||
|
||||
if (bits != 7) {
|
||||
total_bits += bits;
|
||||
bits = bits - 1;
|
||||
*out = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (bits != 7) {
|
||||
*out |= (in[i] & ((1 << (7 - bits)) - 1)) <<
|
||||
(bits + 1);
|
||||
out++;
|
||||
}
|
||||
|
||||
/* This is a no op when bits == 0, lets keep valgrind happy */
|
||||
if (bits != 0)
|
||||
*out = in[i] >> (7 - bits);
|
||||
|
||||
if (bits == 0)
|
||||
bits = 7;
|
||||
else
|
||||
bits = bits - 1;
|
||||
}
|
||||
|
||||
/* If <CR> is intended to be the last character and the message
|
||||
* (including the wanted <CR>) ends on an octet boundary, then
|
||||
* another <CR> must be added together with a padding bit 0. The
|
||||
* receiving entity will perform the carriage return function twice,
|
||||
* but this will not result in misoperation as the definition of
|
||||
* <CR> in clause 6.1.1 is identical to the definition of <CR><CR>.
|
||||
*/
|
||||
if (cb && ((total_bits % 8) == 1))
|
||||
*out |= '\r' << 1;
|
||||
|
||||
if (bits != 7)
|
||||
out++;
|
||||
|
||||
if (cb && ((total_bits % 8) == 0) && (in[len-1] == '\r')) {
|
||||
*out = '\r';
|
||||
out++;
|
||||
}
|
||||
|
||||
*items_written = out - buf;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
unsigned char *pack_7bit(const unsigned char *in, long len, int byte_offset,
|
||||
gboolean cb, long *items_written,
|
||||
unsigned char terminator)
|
||||
{
|
||||
int bits = 7 - (byte_offset % 7);
|
||||
long i;
|
||||
long total_bits;
|
||||
unsigned char *buf;
|
||||
|
||||
if (len == 0 || !items_written)
|
||||
return NULL;
|
||||
|
||||
if (len < 0) {
|
||||
i = 0;
|
||||
|
||||
while (in[i] != terminator)
|
||||
i++;
|
||||
|
||||
len = i;
|
||||
}
|
||||
|
||||
total_bits = len * 7;
|
||||
|
||||
if (bits != 7)
|
||||
total_bits += bits;
|
||||
|
||||
/* Round up number of bytes, must append <cr> if true */
|
||||
if (cb && ((total_bits % 8) == 0) && (in[len-1] == '\r'))
|
||||
buf = g_new(unsigned char, (total_bits + 14) / 8);
|
||||
else
|
||||
buf = g_new(unsigned char, (total_bits + 7) / 8);
|
||||
|
||||
return pack_7bit_own_buf(in, len, byte_offset, cb, items_written,
|
||||
terminator, buf);
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
char *convert_gsm_to_utf8(const unsigned char *text, long len, long *items_read,
|
||||
long *items_written, unsigned char terminator);
|
||||
unsigned char *convert_utf8_to_gsm(const char *text, long len, long *items_read,
|
||||
long *items_written, unsigned char terminator);
|
||||
|
||||
unsigned char *decode_hex_own_buf(const char *in, long len, long *items_written,
|
||||
unsigned char terminator,
|
||||
unsigned char *buf);
|
||||
|
||||
unsigned char *decode_hex(const char *in, long len, long *items_written,
|
||||
unsigned char terminator);
|
||||
|
||||
char *encode_hex_own_buf(const unsigned char *in, long len,
|
||||
unsigned char terminator, char *buf);
|
||||
|
||||
char *encode_hex(const unsigned char *in, long len,
|
||||
unsigned char terminator);
|
||||
|
||||
unsigned char *unpack_7bit_own_buf(const unsigned char *in, long len,
|
||||
int byte_offset, gboolean cb,
|
||||
long max_to_unpack, long *items_written,
|
||||
unsigned char terminator,
|
||||
unsigned char *buf);
|
||||
|
||||
unsigned char *unpack_7bit(const unsigned char *in, long len, int byte_offset,
|
||||
gboolean cb, long max_to_unpack,
|
||||
long *items_written, unsigned char terminator);
|
||||
|
||||
unsigned char *pack_7bit_own_buf(const unsigned char *in, long len,
|
||||
int byte_offset, gboolean cb,
|
||||
long *items_written,
|
||||
unsigned char terminator,
|
||||
unsigned char *buf);
|
||||
|
||||
unsigned char *pack_7bit(const unsigned char *in, long len, int byte_offset,
|
||||
gboolean cb_or_ussd,
|
||||
long *items_written, unsigned char terminator);
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue