diff options
author | Dylan Van Assche <me@dylanvanassche.be> | 2022-07-09 21:10:37 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2022-07-16 13:33:29 +0000 |
commit | efb497e2e67d7eba213f6c2822e3f947219f6af4 (patch) | |
tree | 370cbea036f7a7df4a927e7fd51fbfa30e8ecc07 | |
parent | ea3f5ccad85d376c08d57843b533d0bd85ade2f5 (diff) | |
download | ModemManager-efb497e2e67d7eba213f6c2822e3f947219f6af4.tar.gz |
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.
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | meson.build | 2 | ||||
-rw-r--r-- | src/mm-call-qmi.c | 156 |
3 files changed, 158 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac index 4027696a7..d4580a31e 100644 --- a/configure.ac +++ b/configure.ac @@ -422,7 +422,7 @@ dnl----------------------------------------------------------------------------- dnl QMI support (enabled by default) 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]) AM_CONDITIONAL(WITH_QMI, test "x$with_qmi" = "xyes") diff --git a/meson.build b/meson.build index c71b18c02..f656d357a 100644 --- a/meson.build +++ b/meson.build @@ -247,7 +247,7 @@ config_h.set('WITH_MBIM', enable_mbim) # QMI support (enabled by default) enable_qmi = get_option('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 config_h.set('WITH_QMI', enable_qmi) diff --git a/src/mm-call-qmi.c b/src/mm-call-qmi.c index 38a2aa9e7..8fad9584a 100644 --- a/src/mm-call-qmi.c +++ b/src/mm-call-qmi.c @@ -330,6 +330,160 @@ call_hangup (MMBaseCall *self, } /*****************************************************************************/ +/* 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 * mm_call_qmi_new (MMBaseModem *modem, @@ -362,4 +516,6 @@ mm_call_qmi_class_init (MMCallQmiClass *klass) base_call_class->accept_finish = call_accept_finish; base_call_class->hangup = call_hangup; 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; } |