diff options
-rw-r--r-- | src/mm-broadband-modem.c | 72 | ||||
-rw-r--r-- | src/mm-modem-helpers.c | 79 | ||||
-rw-r--r-- | src/mm-modem-helpers.h | 6 | ||||
-rw-r--r-- | src/tests/test-modem-helpers.c | 58 |
4 files changed, 215 insertions, 0 deletions
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c index f54ddc56..b0ccd191 100644 --- a/src/mm-broadband-modem.c +++ b/src/mm-broadband-modem.c @@ -7957,6 +7957,74 @@ modem_voice_transfer (MMIfaceModemVoice *self, } /*****************************************************************************/ +/* Call waiting setup (Voice interface) */ + +static gboolean +modem_voice_call_waiting_setup_finish (MMIfaceModemVoice *self, + GAsyncResult *res, + GError **error) +{ + return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); +} + +static void +modem_voice_call_waiting_setup (MMIfaceModemVoice *self, + gboolean enable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + gchar *cmd; + + /* Enabling or disabling the call waiting service will only be allowed when + * the modem is registered in the network, and so, CCWA URC handling will + * always be setup at this point (as it's part of the modem enabling phase). + * So, just enable or disable the service (second field) but leaving URCs + * (first field) always enabled. */ + cmd = g_strdup_printf ("+CCWA=1,%u", enable); + mm_base_modem_at_command (MM_BASE_MODEM (self), + cmd, + 60, + FALSE, + callback, + user_data); + g_free (cmd); +} + +/*****************************************************************************/ +/* Call waiting query (Voice interface) */ + +static gboolean +modem_voice_call_waiting_query_finish (MMIfaceModemVoice *self, + GAsyncResult *res, + gboolean *status, + GError **error) +{ + const gchar *response; + + response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); + if (!response) + return FALSE; + + return mm_3gpp_parse_ccwa_service_query_response (response, status, error); +} + +static void +modem_voice_call_waiting_query (MMIfaceModemVoice *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + /* This operation will only be allowed while enabled, and so, CCWA URC + * handling would always be enabled at this point. So, just perform the + * query, but leaving URCs enabled either way. */ + mm_base_modem_at_command (MM_BASE_MODEM (self), + "+CCWA=1,2", + 60, + FALSE, + callback, + user_data); +} + +/*****************************************************************************/ /* ESN loading (CDMA interface) */ static gchar * @@ -12196,6 +12264,10 @@ iface_modem_voice_init (MMIfaceModemVoice *iface) iface->leave_multiparty_finish = modem_voice_leave_multiparty_finish; iface->transfer = modem_voice_transfer; iface->transfer_finish = modem_voice_transfer_finish; + iface->call_waiting_setup = modem_voice_call_waiting_setup; + iface->call_waiting_setup_finish = modem_voice_call_waiting_setup_finish; + iface->call_waiting_query = modem_voice_call_waiting_query; + iface->call_waiting_query_finish = modem_voice_call_waiting_query_finish; } static void diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c index b9d364d2..c6644c1c 100644 --- a/src/mm-modem-helpers.c +++ b/src/mm-modem-helpers.c @@ -2724,6 +2724,85 @@ mm_3gpp_parse_cemode_query_response (const gchar *response, } /*************************************************************************/ +/* CCWA service query response parser */ + +gboolean +mm_3gpp_parse_ccwa_service_query_response (const gchar *response, + gboolean *status, + GError **error) +{ + GRegex *r; + GError *inner_error = NULL; + GMatchInfo *match_info = NULL; + gint class_1_status = -1; + + /* + * AT+CCWA=<n>[,<mode>] + * +CCWA: <status>,<class1> + * [+CCWA: <status>,<class2> + * [...]] + * OK + * + * If <classX> is 255 it applies to ALL classes. + * + * We're only interested in class 1 (voice) + */ + r = g_regex_new ("\\+CCWA:\\s*(\\d+),\\s*(\\d+)$", + G_REGEX_RAW | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF, + G_REGEX_MATCH_NEWLINE_CRLF, + NULL); + g_assert (r != NULL); + + g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error); + if (inner_error) + goto out; + + /* Parse the results */ + while (g_match_info_matches (match_info)) { + guint st; + guint class; + + if (!mm_get_uint_from_match_info (match_info, 2, &class)) + mm_warn ("couldn't parse class from +CCWA line"); + else if (class == 1 || class == 255) { + if (!mm_get_uint_from_match_info (match_info, 1, &st)) + mm_warn ("couldn't parse status from +CCWA line"); + else { + class_1_status = st; + break; + } + } + g_match_info_next (match_info, NULL); + } + +out: + g_clear_pointer (&match_info, g_match_info_free); + g_regex_unref (r); + + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + + if (class_1_status < 0) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, + "call waiting status for voice class missing"); + return FALSE; + } + + if (class_1_status != 0 && class_1_status != 1) { + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, + "call waiting status for voice class invalid: %d", class_1_status); + return FALSE; + } + + if (status) + *status = (gboolean) class_1_status; + + return TRUE; +} + +/*************************************************************************/ static MMSmsStorage storage_from_str (const gchar *str) diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h index cce4bd20..8a73daab 100644 --- a/src/mm-modem-helpers.h +++ b/src/mm-modem-helpers.h @@ -397,6 +397,12 @@ gboolean mm_3gpp_parse_cemode_query_response (const gchar *r MMModem3gppEpsUeModeOperation *out_mode, GError **error); +/* CCWA service query response parser */ +gboolean mm_3gpp_parse_ccwa_service_query_response (const gchar *response, + gboolean *status, + GError **error); + + /* Additional 3GPP-specific helpers */ MMModem3gppFacility mm_3gpp_acronym_to_facility (const gchar *str); diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c index 8470e3ac..0bca7f09 100644 --- a/src/tests/test-modem-helpers.c +++ b/src/tests/test-modem-helpers.c @@ -4019,6 +4019,63 @@ test_ccwa_indication (void) } /*****************************************************************************/ +/* +CCWA service query response testing */ + +static void +common_test_ccwa_response (const gchar *response, + gboolean expected_status, + gboolean expected_error) +{ + gboolean status = FALSE; + GError *error = NULL; + gboolean result; + + result = mm_3gpp_parse_ccwa_service_query_response (response, &status, &error); + + if (expected_error) { + g_assert (!result); + g_assert (error); + g_error_free (error); + } else { + g_assert (result); + g_assert_no_error (error); + g_assert_cmpuint (status, ==, expected_status); + } +} + +typedef struct { + const gchar *response; + gboolean expected_status; + gboolean expected_error; +} TestCcwa; + +static TestCcwa test_ccwa[] = { + { "+CCWA: 0,255", FALSE, FALSE }, /* all disabled */ + { "+CCWA: 1,255", TRUE, FALSE }, /* all enabled */ + { "+CCWA: 0,1\r\n" + "+CCWA: 0,4\r\n", FALSE, FALSE }, /* voice and fax disabled */ + { "+CCWA: 1,1\r\n" + "+CCWA: 1,4\r\n", TRUE, FALSE }, /* voice and fax enabled */ + { "+CCWA: 0,2\r\n" + "+CCWA: 0,4\r\n" + "+CCWA: 0,8\r\n", FALSE, TRUE }, /* data, fax, sms disabled, voice not given */ + { "+CCWA: 1,2\r\n" + "+CCWA: 1,4\r\n" + "+CCWA: 1,8\r\n", FALSE, TRUE }, /* data, fax, sms enabled, voice not given */ + { "+CCWA: 2,1\r\n" + "+CCWA: 2,4\r\n", FALSE, TRUE }, /* voice and fax enabled but unexpected state */ +}; + +static void +test_ccwa_response (void) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (test_ccwa); i++) + common_test_ccwa_response (test_ccwa[i].response, test_ccwa[i].expected_status, test_ccwa[i].expected_error); +} + +/*****************************************************************************/ /* Test +CLCC URCs */ static void @@ -4435,6 +4492,7 @@ int main (int argc, char **argv) g_test_suite_add (suite, TESTCASE (test_clip_indication, NULL)); g_test_suite_add (suite, TESTCASE (test_ccwa_indication, NULL)); + g_test_suite_add (suite, TESTCASE (test_ccwa_response, NULL)); g_test_suite_add (suite, TESTCASE (test_clcc_response_empty, NULL)); g_test_suite_add (suite, TESTCASE (test_clcc_response_single, NULL)); |