summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrzej Zaborowski <andrew.zaborowski@intel.com>2010-10-21 07:09:05 +0200
committerDenis Kenzior <denkenz@gmail.com>2010-10-22 21:59:11 -0500
commit0dd1e9ed073ae312cba5da3f5275189ffaaa09f3 (patch)
tree276153f0fff8aa0bc6a0f9c65fc79835657afaac /src
parenta1316b2809c1a775fdc21b4f3b2738cd87425c67 (diff)
downloadofono-0dd1e9ed073ae312cba5da3f5275189ffaaa09f3.tar.bz2
voicecall: __ofono_voicecall_tone_send internal api
This provides a way for other atoms to send DTMF tones during a call. It is assumed that vc->driver->send_tone returns only after the tones have finished being emitted. In this version Dbus DTMF requests are in the same queue as STK requests.
Diffstat (limited to 'src')
-rw-r--r--src/ofono.h6
-rw-r--r--src/voicecall.c269
2 files changed, 243 insertions, 32 deletions
diff --git a/src/ofono.h b/src/ofono.h
index 78e6be14..bd7f33c4 100644
--- a/src/ofono.h
+++ b/src/ofono.h
@@ -207,6 +207,7 @@ enum ofono_voicecall_interaction {
};
typedef void (*ofono_voicecall_dial_cb_t)(struct ofono_call *call, void *data);
+typedef void (*ofono_voicecall_tone_cb_t)(int error, void *data);
ofono_bool_t __ofono_voicecall_is_busy(struct ofono_voicecall *vc,
enum ofono_voicecall_interaction type);
@@ -218,6 +219,11 @@ int __ofono_voicecall_dial(struct ofono_voicecall *vc,
ofono_voicecall_dial_cb_t cb, void *user_data);
void __ofono_voicecall_dial_cancel(struct ofono_voicecall *vc);
+int __ofono_voicecall_tone_send(struct ofono_voicecall *vc,
+ const char *tone_str,
+ ofono_voicecall_tone_cb_t cb, void *user_data);
+void __ofono_voicecall_tone_cancel(struct ofono_voicecall *vc, int id);
+
#include <ofono/sms.h>
struct sms;
diff --git a/src/voicecall.c b/src/voicecall.c
index 7b5fe3bf..26cfb9a8 100644
--- a/src/voicecall.c
+++ b/src/voicecall.c
@@ -56,6 +56,8 @@ struct ofono_voicecall {
void *driver_data;
struct ofono_atom *atom;
struct dial_request *dial_req;
+ GQueue *toneq;
+ guint tone_source;
};
struct voicecall {
@@ -79,12 +81,22 @@ struct dial_request {
struct ofono_phone_number ph;
};
+struct tone_queue_entry {
+ char *tone_str;
+ char *left;
+ ofono_voicecall_tone_cb_t cb;
+ void *user_data;
+ ofono_destroy_func destroy;
+ int id;
+};
+
static const char *default_en_list[] = { "911", "112", NULL };
static const char *default_en_list_no_sim[] = { "119", "118", "999", "110",
"08", "000", NULL };
static void generic_callback(const struct ofono_error *error, void *data);
static void multirelease_callback(const struct ofono_error *err, void *data);
+static gboolean tone_request_run(gpointer user_data);
static gint call_compare_by_id(gconstpointer a, gconstpointer b)
{
@@ -226,6 +238,83 @@ static void dial_request_finish(struct ofono_voicecall *vc)
vc->dial_req = NULL;
}
+static gboolean voicecalls_can_dtmf(struct ofono_voicecall *vc)
+{
+ GSList *l;
+ struct voicecall *v;
+
+ for (l = vc->call_list; l; l = l->next) {
+ v = l->data;
+
+ if (v->call->status == CALL_STATUS_ACTIVE)
+ return TRUE;
+
+ /* Connected for 2nd stage dialing */
+ if (v->call->status == CALL_STATUS_ALERTING)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int tone_queue(struct ofono_voicecall *vc, const char *tone_str,
+ ofono_voicecall_tone_cb_t cb, void *data,
+ ofono_destroy_func destroy)
+{
+ struct tone_queue_entry *entry;
+ int id = 1;
+ int n = 0;
+ int i;
+
+ /*
+ * Tones can be 0-9, *, #, A-D according to 27.007 C.2.11,
+ * and p for Pause.
+ */
+ for (i = 0; tone_str[i]; i++)
+ if (!g_ascii_isdigit(tone_str[i]) && tone_str[i] != 'p' &&
+ tone_str[i] != '*' && tone_str[i] != '#' &&
+ (tone_str[i] < 'A' || tone_str[i] > 'D'))
+ return -EINVAL;
+
+ while ((entry = g_queue_peek_nth(vc->toneq, n++)) != NULL)
+ if (entry->id >= id)
+ id = entry->id + 1;
+
+ entry = g_try_new0(struct tone_queue_entry, 1);
+ if (entry == NULL)
+ return -ENOMEM;
+
+ entry->tone_str = g_strdup(tone_str);
+ entry->left = entry->tone_str;
+ entry->cb = cb;
+ entry->user_data = data;
+ entry->destroy = destroy;
+ entry->id = id;
+
+ g_queue_push_tail(vc->toneq, entry);
+
+ if (g_queue_get_length(vc->toneq) == 1)
+ g_timeout_add(0, tone_request_run, vc);
+
+ return id;
+}
+
+static void tone_request_finish(struct ofono_voicecall *vc,
+ struct tone_queue_entry *entry,
+ int error, gboolean callback)
+{
+ g_queue_remove(vc->toneq, entry);
+
+ if (callback)
+ entry->cb(error, entry->user_data);
+
+ if (entry->destroy)
+ entry->destroy(entry->user_data);
+
+ g_free(entry->tone_str);
+ g_free(entry);
+}
+
static void append_voicecall_properties(struct voicecall *v,
DBusMessageIter *dict)
{
@@ -583,6 +672,13 @@ static void voicecall_set_call_status(struct voicecall *call, int status)
if (status == CALL_STATUS_DISCONNECTED && call->vc->dial_req &&
call == call->vc->dial_req->call)
dial_request_finish(call->vc);
+
+ if (!voicecalls_can_dtmf(call->vc)) {
+ struct tone_queue_entry *entry;
+
+ while ((entry = g_queue_peek_head(call->vc->toneq)))
+ tone_request_finish(call->vc, entry, ENOENT, TRUE);
+ }
}
static void voicecall_set_call_lineid(struct voicecall *v,
@@ -699,25 +795,6 @@ static gboolean voicecalls_have_active(struct ofono_voicecall *vc)
return FALSE;
}
-static gboolean voicecalls_can_dtmf(struct ofono_voicecall *vc)
-{
- GSList *l;
- struct voicecall *v;
-
- for (l = vc->call_list; l; l = l->next) {
- v = l->data;
-
- if (v->call->status == CALL_STATUS_ACTIVE)
- return TRUE;
-
- /* Connected for 2nd stage dialing */
- if (v->call->status == CALL_STATUS_ALERTING)
- return TRUE;
- }
-
- return FALSE;
-}
-
static gboolean voicecalls_have_with_status(struct ofono_voicecall *vc, int status)
{
GSList *l;
@@ -1527,13 +1604,26 @@ out:
return NULL;
}
+static void tone_callback(int error, void *data)
+{
+ struct ofono_voicecall *vc = data;
+ DBusMessage *reply;
+
+ if (error)
+ reply = __ofono_error_failed(vc->pending);
+ else
+ reply = dbus_message_new_method_return(vc->pending);
+
+ __ofono_dbus_pending_reply(&vc->pending, reply);
+}
+
static DBusMessage *manager_tone(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ofono_voicecall *vc = data;
const char *in_tones;
char *tones;
- int i, len;
+ int err, len;
if (vc->pending)
return __ofono_error_busy(msg);
@@ -1556,23 +1646,15 @@ static DBusMessage *manager_tone(DBusConnection *conn,
tones = g_ascii_strup(in_tones, len);
- /* Tones can be 0-9, *, #, A-D according to 27.007 C.2.11 */
- for (i = 0; i < len; i++) {
- if (g_ascii_isdigit(tones[i]) ||
- tones[i] == '*' || tones[i] == '#' ||
- (tones[i] >= 'A' && tones[i] <= 'D'))
- continue;
+ err = tone_queue(vc, tones, tone_callback, vc, NULL);
- g_free(tones);
+ g_free(tones);
+
+ if (err < 0)
return __ofono_error_invalid_format(msg);
- }
vc->pending = dbus_message_ref(msg);
- vc->driver->send_tones(vc, tones, generic_callback, vc);
-
- g_free(tones);
-
return NULL;
}
@@ -2012,6 +2094,20 @@ static void voicecall_remove(struct ofono_atom *atom)
vc->sim = NULL;
}
+ if (vc->tone_source) {
+ g_source_remove(vc->tone_source);
+ vc->tone_source = 0;
+ }
+
+ if (vc->toneq) {
+ struct tone_queue_entry *entry;
+
+ while ((entry = g_queue_peek_head(vc->toneq)))
+ tone_request_finish(vc, entry, ESHUTDOWN, TRUE);
+
+ g_queue_free(vc->toneq);
+ }
+
g_free(vc);
}
@@ -2031,6 +2127,8 @@ struct ofono_voicecall *ofono_voicecall_create(struct ofono_modem *modem,
if (vc == NULL)
return NULL;
+ vc->toneq = g_queue_new();
+
vc->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_VOICECALL,
voicecall_remove, vc);
@@ -2326,3 +2424,110 @@ void __ofono_voicecall_dial_cancel(struct ofono_voicecall *vc)
vc->dial_req->cb = NULL;
}
+
+static void tone_request_cb(const struct ofono_error *error, void *data)
+{
+ struct ofono_voicecall *vc = data;
+ struct tone_queue_entry *entry = g_queue_peek_head(vc->toneq);
+ int len = 0;
+
+ if (!entry)
+ return;
+
+ /*
+ * Call back with error only if the error is related to the
+ * current entry. If the error corresponds to a cancelled
+ * request, do nothing.
+ */
+ if (error && error->type != OFONO_ERROR_TYPE_NO_ERROR &&
+ entry->left > entry->tone_str) {
+ DBG("command failed with error: %s",
+ telephony_error_to_str(error));
+
+ tone_request_finish(vc, entry, EIO, TRUE);
+
+ goto done;
+ }
+
+ if (*entry->left == '\0') {
+ tone_request_finish(vc, entry, 0, TRUE);
+
+ goto done;
+ }
+
+ len = strspn(entry->left, "pP");
+ entry->left += len;
+
+done:
+ /*
+ * Wait 3 seconds per PAUSE, same as for DTMF separator characters
+ * passed in a telephone number according to TS 22.101 A.21,
+ * although 27.007 claims this delay can be set using S8 and
+ * defaults to 2 seconds.
+ */
+ vc->tone_source = g_timeout_add_seconds(len * 3, tone_request_run, vc);
+}
+
+static gboolean tone_request_run(gpointer user_data)
+{
+ struct ofono_voicecall *vc = user_data;
+ struct tone_queue_entry *entry = g_queue_peek_head(vc->toneq);
+ char buf[256];
+ unsigned len;
+
+ vc->tone_source = 0;
+
+ if (!entry)
+ return FALSE;
+
+ len = strcspn(entry->left, "pP");
+
+ if (len) {
+ if (len >= sizeof(buf))
+ len = sizeof(buf) - 1;
+
+ memcpy(buf, entry->left, len);
+ buf[len] = '\0';
+ entry->left += len;
+
+ vc->driver->send_tones(vc, buf, tone_request_cb, vc);
+ } else
+ tone_request_cb(NULL, vc);
+
+ return FALSE;
+}
+
+int __ofono_voicecall_tone_send(struct ofono_voicecall *vc,
+ const char *tone_str,
+ ofono_voicecall_tone_cb_t cb, void *user_data)
+{
+ if (!vc->driver->send_tones)
+ return -ENOSYS;
+
+ /* Send DTMFs only if we have at least one connected call */
+ if (!voicecalls_can_dtmf(vc))
+ return -ENOENT;
+
+ return tone_queue(vc, tone_str, cb, user_data, NULL);
+}
+
+void __ofono_voicecall_tone_cancel(struct ofono_voicecall *vc, int id)
+{
+ struct tone_queue_entry *entry;
+ int n = 0;
+
+ while ((entry = g_queue_peek_nth(vc->toneq, n++)) != NULL)
+ if (entry->id == id)
+ break;
+
+ tone_request_finish(vc, entry, 0, FALSE);
+
+ /*
+ * If we were in the middle of a PAUSE, wake queue up
+ * now, else wake up when current tone finishes.
+ */
+ if (n == 1 && vc->tone_source) {
+ g_source_remove(vc->tone_source);
+ tone_request_run(vc);
+ }
+}