mm-call-qmi: implement DTMF support

Overwrite the base class with a QMI implementation
to send DTMF characters during a call. Uses the continuous DTMF
QMI messages to support both CDMA and 3GPP networks.
This commit is contained in:
Dylan Van Assche
2022-07-09 21:10:37 +02:00
committed by Aleksander Morgado
parent ea3f5ccad8
commit efb497e2e6
3 changed files with 158 additions and 2 deletions

View File

@@ -422,7 +422,7 @@ dnl-----------------------------------------------------------------------------
dnl QMI support (enabled by default) dnl QMI support (enabled by default)
dnl dnl
LIBQMI_VERSION=1.31.8 LIBQMI_VERSION=1.31.9
AC_ARG_WITH(qmi, AS_HELP_STRING([--without-qmi], [Build without QMI support]), [], [with_qmi=yes]) AC_ARG_WITH(qmi, AS_HELP_STRING([--without-qmi], [Build without QMI support]), [], [with_qmi=yes])
AM_CONDITIONAL(WITH_QMI, test "x$with_qmi" = "xyes") AM_CONDITIONAL(WITH_QMI, test "x$with_qmi" = "xyes")

View File

@@ -247,7 +247,7 @@ config_h.set('WITH_MBIM', enable_mbim)
# QMI support (enabled by default) # QMI support (enabled by default)
enable_qmi = get_option('qmi') enable_qmi = get_option('qmi')
if enable_qmi if enable_qmi
qmi_glib_dep = dependency('qmi-glib', version: '>= 1.31.8') qmi_glib_dep = dependency('qmi-glib', version: '>= 1.31.9')
endif endif
config_h.set('WITH_QMI', enable_qmi) config_h.set('WITH_QMI', enable_qmi)

View File

@@ -329,6 +329,160 @@ call_hangup (MMBaseCall *self,
qmi_message_voice_end_call_input_unref (input); qmi_message_voice_end_call_input_unref (input);
} }
/*****************************************************************************/
/* Send DTMF character */
typedef struct {
QmiClient *client;
guint8 call_id;
} SendDtmfContext;
static void
send_dtmf_context_free (SendDtmfContext *ctx)
{
g_clear_object (&ctx->client);
g_slice_free (SendDtmfContext, ctx);
}
static gboolean
call_send_dtmf_finish (MMBaseCall *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
voice_stop_continuous_dtmf_ready (QmiClientVoice *client,
GAsyncResult *res,
GTask *task)
{
g_autoptr (QmiMessageVoiceStopContinuousDtmfOutput) output = NULL;
GError *error = NULL;
output = qmi_client_voice_stop_continuous_dtmf_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
g_task_return_error (task, error);
} else if (!qmi_message_voice_stop_continuous_dtmf_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't send DTMF character: ");
g_task_return_error (task, error);
} else {
g_task_return_boolean (task, TRUE);
}
g_object_unref (task);
}
static gboolean
voice_stop_continuous_dtmf (GTask *task)
{
SendDtmfContext *ctx;
GError *error = NULL;
g_autoptr (QmiMessageVoiceStopContinuousDtmfInput) input = NULL;
ctx = g_task_get_task_data (task);
input = qmi_message_voice_stop_continuous_dtmf_input_new ();
qmi_message_voice_stop_continuous_dtmf_input_set_data (input, ctx->call_id, &error);
qmi_client_voice_stop_continuous_dtmf (QMI_CLIENT_VOICE (ctx->client),
input,
5,
NULL,
(GAsyncReadyCallback) voice_stop_continuous_dtmf_ready,
task);
return G_SOURCE_REMOVE;
}
static void
voice_start_continuous_dtmf_ready (QmiClientVoice *client,
GAsyncResult *res,
GTask *task)
{
g_autoptr(QmiMessageVoiceStartContinuousDtmfOutput) output = NULL;
GError *error = NULL;
output = qmi_client_voice_start_continuous_dtmf_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
g_task_return_error (task, error);
g_object_unref (task);
return;
}
if (!qmi_message_voice_start_continuous_dtmf_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't send DTMF character: ");
g_task_return_error (task, error);
g_object_unref (task);
return;
}
/* Disable DTMF press after 500 ms */
g_timeout_add (500, (GSourceFunc) voice_stop_continuous_dtmf, task);
}
static void
call_send_dtmf (MMBaseCall *self,
const gchar *dtmf,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
GError *error = NULL;
SendDtmfContext *ctx;
QmiClient *client = NULL;
guint8 call_id;
g_autoptr (QmiMessageVoiceStartContinuousDtmfInput) input = NULL;
/* Ensure Voice client */
if (!ensure_qmi_client (MM_CALL_QMI (self),
QMI_SERVICE_VOICE, &client,
callback, user_data))
return;
task = g_task_new (self, NULL, callback, user_data);
call_id = mm_base_call_get_index (self);
if (call_id == 0) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"Invalid call index");
g_object_unref (task);
return;
}
ctx = g_slice_new0 (SendDtmfContext);
ctx->client = g_object_ref (client);
ctx->call_id = call_id;
g_task_set_task_data (task, ctx, (GDestroyNotify) send_dtmf_context_free);
/* Send DTMF character as ASCII number */
input = qmi_message_voice_start_continuous_dtmf_input_new ();
qmi_message_voice_start_continuous_dtmf_input_set_data (input,
call_id,
(guint8) dtmf[0],
&error);
if (error) {
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"DTMF data build failed");
g_object_unref (task);
return;
}
qmi_client_voice_start_continuous_dtmf (QMI_CLIENT_VOICE (client),
input,
5,
NULL,
(GAsyncReadyCallback) voice_start_continuous_dtmf_ready,
task);
}
/*****************************************************************************/ /*****************************************************************************/
MMBaseCall * MMBaseCall *
@@ -362,4 +516,6 @@ mm_call_qmi_class_init (MMCallQmiClass *klass)
base_call_class->accept_finish = call_accept_finish; base_call_class->accept_finish = call_accept_finish;
base_call_class->hangup = call_hangup; base_call_class->hangup = call_hangup;
base_call_class->hangup_finish = call_hangup_finish; base_call_class->hangup_finish = call_hangup_finish;
base_call_class->send_dtmf = call_send_dtmf;
base_call_class->send_dtmf_finish = call_send_dtmf_finish;
} }