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:

committed by
Aleksander Morgado

parent
ea3f5ccad8
commit
efb497e2e6
@@ -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")
|
||||||
|
@@ -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)
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user