diff --git a/drivers/Makefile.am b/drivers/Makefile.am index 5ade3e04..709b5816 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -6,7 +6,7 @@ builtin_cflags = builtin_modules += atmodem builtin_sources += atmodem/atmodem.c atmodem/at.h \ atmodem/session.h atmodem/session.c \ - atmodem/call-settings.c atmodem/call-waiting.c \ + atmodem/call-settings.c \ atmodem/call-forwarding.c atmodem/call-meter.c \ atmodem/network-registration.c atmodem/sim.c \ atmodem/ussd.c atmodem/voicecall.c \ diff --git a/drivers/atmodem/at.h b/drivers/atmodem/at.h index 4907be7a..de38a893 100644 --- a/drivers/atmodem/at.h +++ b/drivers/atmodem/at.h @@ -73,9 +73,6 @@ extern void at_network_registration_exit(struct ofono_modem *modem); extern void at_call_forwarding_init(struct ofono_modem *modem); extern void at_call_forwarding_exit(struct ofono_modem *modem); -extern void at_call_waiting_init(struct ofono_modem *modem); -extern void at_call_waiting_exit(struct ofono_modem *modem); - extern void at_call_settings_init(struct ofono_modem *modem); extern void at_call_settings_exit(struct ofono_modem *modem); diff --git a/drivers/atmodem/atmodem.c b/drivers/atmodem/atmodem.c index 8f14bc01..2d87a8ad 100644 --- a/drivers/atmodem/atmodem.c +++ b/drivers/atmodem/atmodem.c @@ -103,7 +103,6 @@ static void manager_free(gpointer user) struct at_data *at = l->data; at_call_forwarding_exit(at->modem); - at_call_waiting_exit(at->modem); at_call_settings_exit(at->modem); at_network_registration_exit(at->modem); at_voicecall_exit(at->modem); @@ -341,7 +340,6 @@ static void create_cb(GIOChannel *io, gboolean success, gpointer user) at_sim_init(at->modem); at_call_forwarding_init(at->modem); at_call_settings_init(at->modem); - at_call_waiting_init(at->modem); at_network_registration_init(at->modem); at_voicecall_init(at->modem); at_call_meter_init(at->modem); diff --git a/drivers/atmodem/call-settings.c b/drivers/atmodem/call-settings.c index aaed4441..30420cec 100644 --- a/drivers/atmodem/call-settings.c +++ b/drivers/atmodem/call-settings.c @@ -42,6 +42,109 @@ static const char *none_prefix[] = { NULL }; static const char *clir_prefix[] = { "+CLIR:", NULL }; static const char *colp_prefix[] = { "+COLP:", NULL }; static const char *clip_prefix[] = { "+CLIP:", NULL }; +static const char *ccwa_prefix[] = { "+CCWA:", NULL }; + +static void ccwa_query_cb(gboolean ok, GAtResult *result, gpointer user_data) +{ + struct cb_data *cbd = user_data; + ofono_call_waiting_status_cb_t cb = cbd->cb; + int conditions = 0; + int status; + int cls; + struct ofono_error error; + GAtResultIter iter; + + dump_response("ccwa_query_cb", ok, result); + decode_at_error(&error, g_at_result_final_response(result)); + + if (!ok) + goto out; + + g_at_result_iter_init(&iter, result); + + while (g_at_result_iter_next(&iter, "+CCWA:")) { + g_at_result_iter_next_number(&iter, &status); + g_at_result_iter_next_number(&iter, &cls); + + if (status == 1) + conditions |= cls; + } + + ofono_debug("CW enabled for: %d", conditions); + +out: + cb(&error, conditions, cbd->data); +} + +static void at_ccwa_query(struct ofono_modem *modem, int cls, + ofono_call_waiting_status_cb_t cb, void *data) +{ + struct at_data *at = ofono_modem_userdata(modem); + struct cb_data *cbd = cb_data_new(modem, cb, data); + char buf[64]; + + if (!cbd) + goto error; + + cbd->user = GINT_TO_POINTER(cls); + + if (cls == 7) + sprintf(buf, "AT+CCWA=1,2"); + else + sprintf(buf, "AT+CCWA=1,2,%d", cls); + + if (g_at_chat_send(at->parser, buf, ccwa_prefix, + ccwa_query_cb, cbd, g_free) > 0) + return; + +error: + if (cbd) + g_free(cbd); + + { + DECLARE_FAILURE(error); + cb(&error, 0, data); + } +} + +static void ccwa_set_cb(gboolean ok, GAtResult *result, gpointer user_data) +{ + struct cb_data *cbd = user_data; + ofono_generic_cb_t cb = cbd->cb; + struct ofono_error error; + + dump_response("ccwa_set_cb", ok, result); + decode_at_error(&error, g_at_result_final_response(result)); + + cb(&error, cbd->data); +} + +static void at_ccwa_set(struct ofono_modem *modem, int mode, int cls, + ofono_generic_cb_t cb, void *data) +{ + struct at_data *at = ofono_modem_userdata(modem); + struct cb_data *cbd = cb_data_new(modem, cb, data); + char buf[64]; + + if (!cbd) + goto error; + + sprintf(buf, "AT+CCWA=1,%d,%d", mode, cls); + + if (g_at_chat_send(at->parser, buf, none_prefix, + ccwa_set_cb, cbd, g_free) > 0) + return; + +error: + if (cbd) + g_free(cbd); + + { + DECLARE_FAILURE(error); + cb(&error, data); + } +} + static void clip_query_cb(gboolean ok, GAtResult *result, gpointer user_data) { @@ -257,7 +360,9 @@ static struct ofono_call_settings_ops ops = { .colp_query = at_colp_query, .clir_query = at_clir_query, .clir_set = at_clir_set, - .colr_query = NULL + .colr_query = NULL, + .cw_query = at_ccwa_query, + .cw_set = at_ccwa_set, }; void at_call_settings_init(struct ofono_modem *modem) diff --git a/drivers/atmodem/call-waiting.c b/drivers/atmodem/call-waiting.c deleted file mode 100644 index 2e978d59..00000000 --- a/drivers/atmodem/call-waiting.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * - * 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 -#endif - -#define _GNU_SOURCE -#include -#include -#include - -#include - -#include -#include "driver.h" - -#include "gatchat.h" -#include "gatresult.h" - -#include "at.h" - -static const char *none_prefix[] = { NULL }; -static const char *ccwa_prefix[] = { "+CCWA:", NULL }; - -static void ccwa_query_cb(gboolean ok, GAtResult *result, gpointer user_data) -{ - struct cb_data *cbd = user_data; - ofono_call_waiting_status_cb_t cb = cbd->cb; - int conditions = 0; - int status; - int cls; - struct ofono_error error; - GAtResultIter iter; - - dump_response("ccwa_query_cb", ok, result); - decode_at_error(&error, g_at_result_final_response(result)); - - if (!ok) - goto out; - - g_at_result_iter_init(&iter, result); - - while (g_at_result_iter_next(&iter, "+CCWA:")) { - g_at_result_iter_next_number(&iter, &status); - g_at_result_iter_next_number(&iter, &cls); - - if (status == 1) - conditions |= cls; - } - - ofono_debug("CW enabled for: %d", conditions); - -out: - cb(&error, conditions, cbd->data); -} - -static void at_ccwa_query(struct ofono_modem *modem, int cls, - ofono_call_waiting_status_cb_t cb, void *data) -{ - struct at_data *at = ofono_modem_userdata(modem); - struct cb_data *cbd = cb_data_new(modem, cb, data); - char buf[64]; - - if (!cbd) - goto error; - - cbd->user = GINT_TO_POINTER(cls); - - if (cls == 7) - sprintf(buf, "AT+CCWA=1,2"); - else - sprintf(buf, "AT+CCWA=1,2,%d", cls); - - if (g_at_chat_send(at->parser, buf, ccwa_prefix, - ccwa_query_cb, cbd, g_free) > 0) - return; - -error: - if (cbd) - g_free(cbd); - - { - DECLARE_FAILURE(error); - cb(&error, 0, data); - } -} - -static void ccwa_set_cb(gboolean ok, GAtResult *result, gpointer user_data) -{ - struct cb_data *cbd = user_data; - ofono_generic_cb_t cb = cbd->cb; - struct ofono_error error; - - dump_response("ccwa_set_cb", ok, result); - decode_at_error(&error, g_at_result_final_response(result)); - - cb(&error, cbd->data); -} - -static void at_ccwa_set(struct ofono_modem *modem, int mode, int cls, - ofono_generic_cb_t cb, void *data) -{ - struct at_data *at = ofono_modem_userdata(modem); - struct cb_data *cbd = cb_data_new(modem, cb, data); - char buf[64]; - - if (!cbd) - goto error; - - sprintf(buf, "AT+CCWA=1,%d,%d", mode, cls); - - if (g_at_chat_send(at->parser, buf, none_prefix, - ccwa_set_cb, cbd, g_free) > 0) - return; - -error: - if (cbd) - g_free(cbd); - - { - DECLARE_FAILURE(error); - cb(&error, data); - } -} - -static struct ofono_call_waiting_ops ops = { - .query = at_ccwa_query, - .set = at_ccwa_set -}; - -void at_call_waiting_init(struct ofono_modem *modem) -{ - ofono_call_waiting_register(modem, &ops); -} - -void at_call_waiting_exit(struct ofono_modem *modem) -{ - ofono_call_waiting_unregister(modem); -} diff --git a/src/Makefile.am b/src/Makefile.am index 28dcc774..2f3ad7f9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,7 +11,7 @@ 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 \ + call-settings.c call-forwarding.c call-meter.c \ smsutil.h smsutil.c cssn.h cssn.c call-barring.c sim.h sim.c ofonod_LDADD = $(top_builddir)/plugins/libbuiltin.la \ diff --git a/src/call-settings.c b/src/call-settings.c index c540baa9..97f4124a 100644 --- a/src/call-settings.c +++ b/src/call-settings.c @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -42,6 +43,14 @@ #define CALL_SETTINGS_FLAG_CACHED 0x1 +enum call_setting_type { + CALL_SETTING_TYPE_CLIP = 0, + CALL_SETTING_TYPE_COLP, + CALL_SETTING_TYPE_COLR, + CALL_SETTING_TYPE_CLIR, + CALL_SETTING_TYPE_CW +}; + struct call_settings_data { struct ofono_call_settings_ops *ops; int clir; @@ -49,17 +58,12 @@ struct call_settings_data { int clip; int colp; int clir_setting; + int cw; 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 + int ss_req_cls; + enum call_setting_type ss_setting; }; static void cs_register_ss_controls(struct ofono_modem *modem); @@ -215,6 +219,36 @@ static void set_colr(struct ofono_modem *modem, int colr) } } +static void set_cw(struct ofono_modem *modem, int new_cw, int mask) +{ + struct call_settings_data *cs = modem->call_settings; + DBusConnection *conn = dbus_gsm_connection(); + char buf[64]; + int j; + const char *value; + + for (j = 1; j <= BEARER_CLASS_PAD; j = j << 1) { + if ((j & mask) == 0) + continue; + + if ((cs->cw & j) == (new_cw & j)) + continue; + + if (new_cw & j) + value = "enabled"; + else + value = "disabled"; + + sprintf(buf, "%sCallWaiting", bearer_class_to_string(j)); + dbus_gsm_signal_property_changed(conn, modem->path, + CALL_SETTINGS_INTERFACE, + buf, DBUS_TYPE_STRING, + &value); + } + + cs->cw = new_cw; +} + static struct call_settings_data *call_settings_create() { struct call_settings_data *r; @@ -243,6 +277,190 @@ static void call_settings_destroy(gpointer data) g_free(cs); } +static void property_append_cw_conditions(DBusMessageIter *dict, + int conditions, int mask) +{ + int i; + char prop[128]; + const char *value; + + for (i = 1; i <= BEARER_CLASS_PAD; i = i << 1) { + if (!(mask & i)) + continue; + + sprintf(prop, "%sCallWaiting", bearer_class_to_string(i)); + + if (conditions & i) + value = "enabled"; + else + value = "disabled"; + + dbus_gsm_dict_append(dict, prop, DBUS_TYPE_STRING, &value); + } +} + +static void generate_cw_ss_query_reply(struct ofono_modem *modem) +{ + struct call_settings_data *cs = modem->call_settings; + const char *sig = "(sa{sv})"; + const char *ss_type = ss_control_type_to_string(cs->ss_req_type); + const char *context = "CallWaiting"; + DBusMessageIter iter; + DBusMessageIter var; + DBusMessageIter vstruct; + DBusMessageIter dict; + 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_open_container(&vstruct, DBUS_TYPE_ARRAY, + PROPERTIES_ARRAY_SIGNATURE, &dict); + + property_append_cw_conditions(&dict, cs->cw, cs->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(&cs->pending, reply); +} + +static void cw_ss_query_callback(const struct ofono_error *error, int status, + 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 CW via SS failed"); + + cs->flags &= ~CALL_SETTINGS_FLAG_CACHED; + dbus_gsm_pending_reply(&cs->pending, + dbus_gsm_failed(cs->pending)); + + return; + } + + set_cw(modem, status, BEARER_CLASS_VOICE); + + generate_cw_ss_query_reply(modem); +} + +static void cw_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 CW via SS failed"); + dbus_gsm_pending_reply(&cs->pending, + dbus_gsm_failed(cs->pending)); + + return; + } + + cs->ops->cw_query(modem, BEARER_CLASS_DEFAULT, + 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_settings_data *cs = modem->call_settings; + DBusConnection *conn = dbus_gsm_connection(); + int cls = BEARER_CLASS_SS_DEFAULT; + DBusMessage *reply; + + if (!cs) + return FALSE; + + if (strcmp(sc, "43")) + return FALSE; + + if (cs->pending) { + reply = dbus_gsm_busy(msg); + goto error; + } + + if (strlen(sib) || strlen(sib) || strlen(dn)) + goto bad_format; + + if ((type == SS_CONTROL_TYPE_QUERY && !cs->ops->cw_query) || + (type != SS_CONTROL_TYPE_QUERY && !cs->ops->cw_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; + } + + cs->ss_req_cls = cls; + cs->pending = dbus_message_ref(msg); + + /* For the default case use the more readily accepted value */ + if (cls == BEARER_CLASS_SS_DEFAULT) + cls = BEARER_CLASS_DEFAULT; + + switch (type) { + case SS_CONTROL_TYPE_REGISTRATION: + case SS_CONTROL_TYPE_ACTIVATION: + cs->ss_req_type = SS_CONTROL_TYPE_ACTIVATION; + cs->ops->cw_set(modem, 1, cls, cw_ss_set_callback, modem); + break; + + case SS_CONTROL_TYPE_QUERY: + cs->ss_req_type = SS_CONTROL_TYPE_QUERY; + /* Always query the entire set, SMS not applicable + * according to 22.004 Appendix A, so CLASS_DEFAULT + * is safe to use here + */ + cs->ops->cw_query(modem, BEARER_CLASS_DEFAULT, + cw_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->cw_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 generate_ss_query_reply(struct ofono_modem *modem, const char *context, const char *value) { @@ -293,7 +511,7 @@ static void clip_colp_colr_ss_query_cb(const struct ofono_error *error, return; } - switch (cs->call_setting_type) { + switch (cs->ss_setting) { case CALL_SETTING_TYPE_CLIP: set_clip(modem, status); value = clip_status_to_string(status); @@ -343,13 +561,13 @@ static gboolean clip_colp_colr_ss(struct ofono_modem *modem, int type, } if (!strcmp(sc, "30")) { - cs->call_setting_type = CALL_SETTING_TYPE_CLIP; + cs->ss_setting = CALL_SETTING_TYPE_CLIP; query_op = cs->ops->clip_query; } else if (!strcmp(sc, "76")) { - cs->call_setting_type = CALL_SETTING_TYPE_COLP; + cs->ss_setting = CALL_SETTING_TYPE_COLP; query_op = cs->ops->colp_query; } else if (!strcmp(sc, "77")) { - cs->call_setting_type = CALL_SETTING_TYPE_COLR; + cs->ss_setting = CALL_SETTING_TYPE_COLR; query_op = cs->ops->colr_query; } else return FALSE; @@ -484,7 +702,7 @@ static gboolean clir_ss_control(struct ofono_modem *modem, int type, return TRUE; } - cs->call_setting_type = CALL_SETTING_TYPE_CLIR; + cs->ss_setting = CALL_SETTING_TYPE_CLIR; cs->pending = dbus_message_ref(msg); switch (type) { @@ -520,6 +738,8 @@ static void cs_register_ss_controls(struct ofono_modem *modem) ss_control_register(modem, "31", clir_ss_control); ss_control_register(modem, "76", clip_colp_colr_ss); + ss_control_register(modem, "43", cw_ss_control); + if (cs->ops->colr_query) ss_control_register(modem, "77", clip_colp_colr_ss); } @@ -532,6 +752,8 @@ static void cs_unregister_ss_controls(struct ofono_modem *modem) ss_control_unregister(modem, "31", clir_ss_control); ss_control_unregister(modem, "76", clip_colp_colr_ss); + ss_control_unregister(modem, "43", cw_ss_control); + if (cs->ops->colr_query) ss_control_unregister(modem, "77", clip_colp_colr_ss); } @@ -575,6 +797,8 @@ static DBusMessage *generate_get_properties_reply(struct ofono_modem *modem, str = hide_callerid_to_string(cs->clir_setting); dbus_gsm_dict_append(&dict, "HideCallerId", DBUS_TYPE_STRING, &str); + property_append_cw_conditions(&dict, cs->cw, BEARER_CLASS_VOICE); + dbus_message_iter_close_container(&iter, &dict); return reply; @@ -702,6 +926,33 @@ static gboolean query_colr(gpointer user) return FALSE; } +static void cs_cw_callback(const struct ofono_error *error, int status, + void *data) +{ + struct ofono_modem *modem = data; + + if (error->type == OFONO_ERROR_TYPE_NO_ERROR) + set_cw(modem, status, BEARER_CLASS_VOICE); + + g_timeout_add(0, query_colr, modem); +} + +static gboolean query_cw(gpointer user) +{ + struct ofono_modem *modem = user; + struct call_settings_data *cs = modem->call_settings; + + if (!cs->ops->cw_query) { + query_colr(modem); + return FALSE; + } + + cs->ops->cw_query(modem, BEARER_CLASS_DEFAULT, + cs_cw_callback, modem); + + return FALSE; +} + static DBusMessage *cs_get_properties(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -717,7 +968,7 @@ static DBusMessage *cs_get_properties(DBusConnection *conn, DBusMessage *msg, /* Query the settings and report back */ cs->pending = dbus_message_ref(msg); - query_colr(modem); + query_cw(modem); return NULL; } @@ -734,7 +985,7 @@ static void clir_set_query_callback(const struct ofono_error *error, return; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { - ofono_error("setting clir was successful, but the query was not"); + ofono_error("set clir successful, but the query was not"); cs->flags &= ~CALL_SETTINGS_FLAG_CACHED; @@ -793,6 +1044,95 @@ static DBusMessage *set_clir(DBusMessage *msg, struct ofono_modem *modem, return NULL; } +static void cw_set_query_callback(const struct ofono_error *error, int status, + void *data) +{ + struct ofono_modem *modem = data; + struct call_settings_data *cs = modem->call_settings; + + if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { + ofono_error("CW set succeeded, but query failed!"); + + cs->flags &= ~CALL_SETTINGS_FLAG_CACHED; + + dbus_gsm_pending_reply(&cs->pending, + dbus_gsm_failed(cs->pending)); + return; + } + + dbus_gsm_pending_reply(&cs->pending, + dbus_message_new_method_return(cs->pending)); + + set_cw(modem, status, BEARER_CLASS_VOICE); +} + +static void cw_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("Error occurred during CW set"); + + dbus_gsm_pending_reply(&cs->pending, + dbus_gsm_failed(cs->pending)); + + return; + } + + cs->ops->cw_query(modem, BEARER_CLASS_DEFAULT, + cw_set_query_callback, modem); +} + +static DBusMessage *set_cw_req(DBusMessage *msg, struct ofono_modem *modem, + const char *setting, int cls) +{ + struct call_settings_data *cs = modem->call_settings; + int cw; + + if (cs->ops->cw_set == NULL) + return dbus_gsm_not_implemented(msg); + + if (!strcmp(setting, "enabled")) + cw = 1; + else if (!strcmp(setting, "disabled")) + cw = 0; + else + return dbus_gsm_invalid_format(msg); + + cs->pending = dbus_message_ref(msg); + + cs->ops->cw_set(modem, cw, cls, cw_set_callback, modem); + + return NULL; +} + +static gboolean is_cw_property(const char *property, int mask, int *out_cls) +{ + int i; + int len; + const char *prefix; + + for (i = 1; i <= BEARER_CLASS_PAD; i = i << 1) { + if ((i & mask) == 0) + continue; + + prefix = bearer_class_to_string(i); + + len = strlen(prefix); + + if (strncmp(property, prefix, len)) + continue; + + if (!strcmp(property+len, "CallWaiting")) { + *out_cls = i; + return TRUE; + } + } + + return FALSE; +} + static DBusMessage *cs_set_property(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -801,6 +1141,7 @@ static DBusMessage *cs_set_property(DBusConnection *conn, DBusMessage *msg, DBusMessageIter iter; DBusMessageIter var; const char *property; + int cls; if (cs->pending) return dbus_gsm_busy(msg); @@ -828,6 +1169,15 @@ static DBusMessage *cs_set_property(DBusConnection *conn, DBusMessage *msg, dbus_message_iter_get_basic(&var, &setting); return set_clir(msg, modem, setting); + } else if (is_cw_property(property, BEARER_CLASS_VOICE, &cls)) { + 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_cw_req(msg, modem, setting, cls); } return dbus_gsm_invalid_args(msg); diff --git a/src/call-waiting.c b/src/call-waiting.c deleted file mode 100644 index 29773b8b..00000000 --- a/src/call-waiting.c +++ /dev/null @@ -1,528 +0,0 @@ -/* - * - * 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 -#endif - -#include -#include -#include - -#include -#include -#include - -#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; - int conditions; - int ss_req_type; - int ss_req_cls; -}; - -static void cw_register_ss_controls(struct ofono_modem *modem); -static void cw_unregister_ss_controls(struct ofono_modem *modem); - -static struct call_waiting_data *call_waiting_create() -{ - struct call_waiting_data *r; - - r = g_new0(struct call_waiting_data, 1); - - 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_free(cw); -} - -static void update_conditions(struct ofono_modem *modem, int new_conditions, - int mask) -{ - struct call_waiting_data *cw = modem->call_waiting; - DBusConnection *conn = dbus_gsm_connection(); - char buf[64]; - int j; - const char *value; - - for (j = 1; j <= BEARER_CLASS_PAD; j = j << 1) { - if ((j & mask) == 0) - continue; - - if ((cw->conditions & j) == (new_conditions & j)) - continue; - - if (new_conditions & j) - value = "enabled"; - else - value = "disabled"; - - sprintf(buf, "%s", bearer_class_to_string(j)); - dbus_gsm_signal_property_changed(conn, modem->path, - CALL_WAITING_INTERFACE, - buf, DBUS_TYPE_STRING, - &value); - } - - cw->conditions = new_conditions; -} - -static void property_append_cw_conditions(DBusMessageIter *dict, - int conditions, int mask) -{ - int i; - const char *prop; - const char *value; - - for (i = 1; i <= BEARER_CLASS_PAD; i = i << 1) { - if (!(mask & i)) - continue; - - prop = bearer_class_to_string(i); - - if (conditions & i) - value = "enabled"; - else - value = "disabled"; - - dbus_gsm_dict_append(dict, prop, DBUS_TYPE_STRING, &value); - } -} - -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->conditions, 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 status, - 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"); - - cw->flags &= ~CALL_WAITING_FLAG_CACHED; - dbus_gsm_pending_reply(&cw->pending, - dbus_gsm_failed(cw->pending)); - - return; - } - - update_conditions(modem, status, BEARER_CLASS_VOICE); - 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; - int cls; - - 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; - } - - cls = cw->ss_req_cls | BEARER_CLASS_DEFAULT; - - 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_SS_DEFAULT; - DBusMessage *reply; - - 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); - - cls = BEARER_CLASS_DEFAULT; - - 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; - - 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->conditions, - BEARER_CLASS_VOICE); - - dbus_message_iter_close_container(&iter, &dict); - - return reply; -} - -static void cw_query_callback(const struct ofono_error *error, int status, - 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 during cw query"); - goto out; - } - - update_conditions(modem, status, BEARER_CLASS_VOICE); - 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 status, - void *data) -{ - struct ofono_modem *modem = data; - struct call_waiting_data *cw = modem->call_waiting; - - 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)); - - update_conditions(modem, status, BEARER_CLASS_VOICE); -} - -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; -} diff --git a/src/driver.h b/src/driver.h index 4a5dac38..b1883a29 100644 --- a/src/driver.h +++ b/src/driver.h @@ -312,23 +312,16 @@ struct ofono_call_settings_ops { 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); + void (*cw_query)(struct ofono_modem *modem, int cls, + ofono_call_waiting_status_cb_t cb, void *data); + void (*cw_set)(struct ofono_modem *modem, int mode, int cls, + 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);