summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDylan Van Assche <me@dylanvanassche.be>2022-07-09 21:10:37 +0200
committerAleksander Morgado <aleksander@aleksander.es>2022-07-16 13:33:29 +0000
commitefb497e2e67d7eba213f6c2822e3f947219f6af4 (patch)
tree370cbea036f7a7df4a927e7fd51fbfa30e8ecc07
parentea3f5ccad85d376c08d57843b533d0bd85ade2f5 (diff)
downloadModemManager-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.ac2
-rw-r--r--meson.build2
-rw-r--r--src/mm-call-qmi.c156
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;
}