aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--introspection/org.freedesktop.ModemManager1.Call.xml11
-rw-r--r--src/mm-base-call.c77
-rw-r--r--src/mm-base-call.h3
-rw-r--r--src/mm-call-at.c7
-rw-r--r--src/mm-call-qmi.c6
5 files changed, 81 insertions, 23 deletions
diff --git a/introspection/org.freedesktop.ModemManager1.Call.xml b/introspection/org.freedesktop.ModemManager1.Call.xml
index 41dc40f3..6f1fdf44 100644
--- a/introspection/org.freedesktop.ModemManager1.Call.xml
+++ b/introspection/org.freedesktop.ModemManager1.Call.xml
@@ -103,10 +103,15 @@
<!--
SendDtmf:
- @dtmf: DTMF tone identifier [0-9A-D*#] or pause character [,].
+ @dtmf: A string of DTMF tone identifiers [0-9A-D*#] and/or pause characters [,].
- Send a DTMF tone (Dual Tone Multi-Frequency) (only on supported modems).
- Since 1.26 the comma [,] is interpreted as a two-second pause.
+ Send one or more DTMF tones (Dual Tone Multi-Frequency) (only on supported modems).
+ Before 1.26 only the first character in @dtmf was sent to the modem;
+ all others were discarded.
+
+ Since 1.26 up to 50 tone identifiers are accepted and each will be
+ sent to the modem in the order given. The comma [,] character pauses
+ DTMF tones for two-seconds then continues with the remaining characters.
Applicable only if state is <link linkend="MM-CALL-STATE-ACTIVE:CAPS"><constant>MM_CALL_STATE_ACTIVE</constant></link>.
diff --git a/src/mm-base-call.c b/src/mm-base-call.c
index 68f77b80..e011c448 100644
--- a/src/mm-base-call.c
+++ b/src/mm-base-call.c
@@ -736,6 +736,7 @@ typedef enum {
DTMF_STEP_START,
DTMF_STEP_TIMEOUT,
DTMF_STEP_STOP,
+ DTMF_STEP_NEXT,
DTMF_STEP_LAST,
} DtmfStep;
@@ -743,7 +744,12 @@ typedef struct {
DtmfStep step;
GError *saved_error;
guint8 call_id;
- gchar *dtmf;
+ /* Array of DTMF runs; split by pauses */
+ GPtrArray *dtmfs;
+ /* index into dtmfs */
+ guint cur_dtmf;
+ /* index into cur dtmf run string */
+ gchar *cur_tone;
guint timeout_id;
} SendDtmfContext;
@@ -760,7 +766,8 @@ static void
send_dtmf_context_free (SendDtmfContext *ctx)
{
send_dtmf_context_clear_timeout (ctx);
- g_free (ctx->dtmf);
+ g_ptr_array_foreach (ctx->dtmfs, (GFunc) g_free, NULL);
+ g_ptr_array_free (ctx->dtmfs, TRUE);
g_assert (!ctx->saved_error);
g_slice_free (SendDtmfContext, ctx);
}
@@ -831,6 +838,10 @@ dtmf_timeout (GTask *task)
ctx = g_task_get_task_data (task);
+ /* If this was a pause character; move past it */
+ if (ctx->cur_tone[0] == MM_CALL_DTMF_PAUSE_CHAR)
+ ctx->cur_tone++;
+
send_dtmf_context_clear_timeout (ctx);
ctx->step++;
send_dtmf_task_step_next (task);
@@ -845,17 +856,21 @@ send_dtmf_ready (MMBaseCall *self,
{
SendDtmfContext *ctx;
GError *error = NULL;
- gboolean success;
+ gssize num_sent;
ctx = g_task_get_task_data (task);
- success = MM_BASE_CALL_GET_CLASS (self)->send_dtmf_finish (self, res, &error);
+ num_sent = MM_BASE_CALL_GET_CLASS (self)->send_dtmf_finish (self, res, &error);
if (ctx->step == DTMF_STEP_START) {
- if (!success) {
+ if (num_sent < 0) {
g_propagate_error (&ctx->saved_error, error);
ctx->step = DTMF_STEP_LAST;
- } else
+ } else {
+ g_assert (num_sent > 0);
+ g_assert ((guint) num_sent <= strlen (ctx->cur_tone));
+ ctx->cur_tone += num_sent;
ctx->step++;
+ }
send_dtmf_task_step_next (task);
}
@@ -875,7 +890,7 @@ send_dtmf_task_step_next (GTask *task)
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
- is_pause = (ctx->dtmf[0] == MM_CALL_DTMF_PAUSE_CHAR);
+ is_pause = (ctx->cur_tone[0] == MM_CALL_DTMF_PAUSE_CHAR);
need_stop = MM_BASE_CALL_GET_CLASS (self)->stop_dtmf &&
MM_BASE_CALL_GET_CLASS (self)->stop_dtmf_finish;
@@ -886,7 +901,7 @@ send_dtmf_task_step_next (GTask *task)
case DTMF_STEP_START:
if (!is_pause) {
MM_BASE_CALL_GET_CLASS (self)->send_dtmf (self,
- ctx->dtmf,
+ ctx->cur_tone,
(GAsyncReadyCallback)send_dtmf_ready,
g_object_ref (task));
return;
@@ -914,6 +929,22 @@ send_dtmf_task_step_next (GTask *task)
return;
}
/* Fall through */
+ case DTMF_STEP_NEXT:
+ /* Advance to next DTMF run? */
+ if (ctx->cur_tone[0] == '\0') {
+ ctx->cur_dtmf++;
+ if (ctx->cur_dtmf < ctx->dtmfs->len)
+ ctx->cur_tone = g_ptr_array_index (ctx->dtmfs, ctx->cur_dtmf);
+ }
+
+ /* More to send? */
+ if (ctx->cur_tone[0]) {
+ ctx->step = DTMF_STEP_START;
+ send_dtmf_task_step_next (task);
+ return;
+ }
+ /* no more DTMF characters to send */
+ /* Fall through */
case DTMF_STEP_LAST:
send_dtmf_context_clear_timeout (ctx);
if (ctx->saved_error)
@@ -948,9 +979,9 @@ send_dtmf_task_new (MMBaseCall *self,
gpointer user_data,
GError **error)
{
- GTask *task;
- SendDtmfContext *ctx;
- guint8 call_id;
+ GTask *task;
+ SendDtmfContext *ctx;
+ guint8 call_id;
call_id = mm_base_call_get_index (self);
if (call_id == 0) {
@@ -962,9 +993,13 @@ send_dtmf_task_new (MMBaseCall *self,
}
task = g_task_new (self, NULL, callback, user_data);
+
ctx = g_slice_new0 (SendDtmfContext);
ctx->call_id = call_id;
- ctx->dtmf = g_strdup (dtmf);
+ /* Split DTMF into runs of DTMF characters interrupted by pauses */
+ ctx->dtmfs = mm_dtmf_split (dtmf);
+ g_assert (ctx->dtmfs->len > 0);
+ ctx->cur_tone = g_ptr_array_index (ctx->dtmfs, ctx->cur_dtmf);
g_task_set_task_data (task, ctx, (GDestroyNotify) send_dtmf_context_free);
return task;
@@ -1021,6 +1056,22 @@ handle_send_dtmf_auth_ready (MMAuthProvider *authp,
state = mm_gdbus_call_get_state (MM_GDBUS_CALL (ctx->self));
+ /* Ensure there are DTMF characters to send */
+ if (!ctx->dtmf || !ctx->dtmf[0]) {
+ mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "No DTMF characters given");
+ handle_send_dtmf_context_free (ctx);
+ return;
+ }
+
+ /* And that there aren't too many */
+ if (strlen (ctx->dtmf) > 50) {
+ mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Too many DTMF characters");
+ handle_send_dtmf_context_free (ctx);
+ return;
+ }
+
/* Check if we do support doing DTMF at all */
if (!MM_BASE_CALL_GET_CLASS (ctx->self)->send_dtmf ||
!MM_BASE_CALL_GET_CLASS (ctx->self)->send_dtmf_finish) {
@@ -1031,7 +1082,7 @@ handle_send_dtmf_auth_ready (MMAuthProvider *authp,
}
/* We can only send_dtmf when call is in ACTIVE state */
- if (state != MM_CALL_STATE_ACTIVE ){
+ if (state != MM_CALL_STATE_ACTIVE) {
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"This call was not active, cannot send dtmf");
handle_send_dtmf_context_free (ctx);
diff --git a/src/mm-base-call.h b/src/mm-base-call.h
index 2e45ca5f..a9a6efc3 100644
--- a/src/mm-base-call.h
+++ b/src/mm-base-call.h
@@ -95,7 +95,8 @@ struct _MMBaseCallClass {
const gchar *dtmf,
GAsyncReadyCallback callback,
gpointer user_data);
- gboolean (* send_dtmf_finish) (MMBaseCall *self,
+ /* Returns the number of DTMF characters sent, or < 1 on error */
+ gssize (* send_dtmf_finish) (MMBaseCall *self,
GAsyncResult *res,
GError **error);
diff --git a/src/mm-call-at.c b/src/mm-call-at.c
index 0a81b929..3a1c3263 100644
--- a/src/mm-call-at.c
+++ b/src/mm-call-at.c
@@ -312,12 +312,12 @@ call_hangup (MMBaseCall *_self,
/*****************************************************************************/
/* Send DTMF tone to call */
-static gboolean
+static gssize
call_send_dtmf_finish (MMBaseCall *self,
GAsyncResult *res,
GError **error)
{
- return g_task_propagate_boolean (G_TASK (res), error);
+ return g_task_propagate_int (G_TASK (res), error);
}
static void
@@ -338,7 +338,8 @@ call_send_dtmf_ready (MMBaseModem *modem,
return;
}
- g_task_return_boolean (task, TRUE);
+ /* We sent one character */
+ g_task_return_int (task, 1);
g_object_unref (task);
}
diff --git a/src/mm-call-qmi.c b/src/mm-call-qmi.c
index b1b6746d..44ab1f37 100644
--- a/src/mm-call-qmi.c
+++ b/src/mm-call-qmi.c
@@ -432,12 +432,12 @@ call_stop_dtmf (MMBaseCall *_self,
task);
}
-static gboolean
+static gssize
call_send_dtmf_finish (MMBaseCall *call,
GAsyncResult *res,
GError **error)
{
- return g_task_propagate_boolean (G_TASK (res), error);
+ return g_task_propagate_int (G_TASK (res), error);
}
static void
@@ -456,7 +456,7 @@ voice_start_continuous_dtmf_ready (QmiClientVoice *client,
g_prefix_error (&error, "Couldn't send DTMF character: ");
g_task_return_error (task, error);
} else {
- g_task_return_boolean (task, TRUE);
+ g_task_return_int (task, 1);
}
g_object_unref (task);
}