broadband-modem,voice: implement call waiting status setup/query with +CCWA

This commit is contained in:
Aleksander Morgado
2019-07-15 17:13:21 +02:00
parent 9263e79dc3
commit f39dbe72e5
4 changed files with 215 additions and 0 deletions

View File

@@ -7956,6 +7956,74 @@ modem_voice_transfer (MMIfaceModemVoice *self,
user_data);
}
/*****************************************************************************/
/* 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) */
@@ -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

View File

@@ -2723,6 +2723,85 @@ mm_3gpp_parse_cemode_query_response (const gchar *response,
return FALSE;
}
/*************************************************************************/
/* 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

View File

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

View File

@@ -4018,6 +4018,63 @@ test_ccwa_indication (void)
g_regex_unref (r);
}
/*****************************************************************************/
/* +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 */
@@ -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));