Open Source Telephony
 help / color / mirror / Atom feed
* [PATCH 1/4] voicecall: __ofono_voicecall_tone_send internal api.
@ 2010-10-21  5:09 Andrzej Zaborowski
  2010-10-21  5:09 ` [PATCH 2/4] stk: Handle the Send DTMF proactive command Andrzej Zaborowski
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Andrzej Zaborowski @ 2010-10-21  5:09 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 10307 bytes --]

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.
---
 src/ofono.h     |    6 ++
 src/voicecall.c |  269 ++++++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 243 insertions(+), 32 deletions(-)

diff --git a/src/ofono.h b/src/ofono.h
index 78e6be1..bd7f33c 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 7b5fe3b..26cfb9a 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);
+	}
+}
-- 
1.7.1.86.g0e460.dirty


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/4] stk: Handle the Send DTMF proactive command.
  2010-10-21  5:09 [PATCH 1/4] voicecall: __ofono_voicecall_tone_send internal api Andrzej Zaborowski
@ 2010-10-21  5:09 ` Andrzej Zaborowski
  2010-10-23  3:55   ` Denis Kenzior
  2010-10-21  5:09 ` [PATCH 3/4] atmodem: Delay return from send_dtmf Andrzej Zaborowski
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 8+ messages in thread
From: Andrzej Zaborowski @ 2010-10-21  5:09 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 5651 bytes --]

The default_agent_notify and session_agent_notify changes below are
needed for respond_on_exit commands to be able to free up their
resources, reset alpha id, etc.  Commands other than Send DTMF that
use respond_on_exit are all associated with a agent method call and
get notified about agent exit in the callback.
---
 src/stk.c |  143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 142 insertions(+), 1 deletions(-)

diff --git a/src/stk.c b/src/stk.c
index 60b308b..d42a05f 100644
--- a/src/stk.c
+++ b/src/stk.c
@@ -74,6 +74,7 @@ struct ofono_stk {
 	char *idle_mode_text;
 	struct stk_icon_id idle_mode_icon;
 	struct timeval get_inkey_start_ts;
+	int dtmf_id;
 };
 
 struct envelope_op {
@@ -460,8 +461,12 @@ static void default_agent_notify(gpointer user_data)
 {
 	struct ofono_stk *stk = user_data;
 
-	if (stk->current_agent == stk->default_agent && stk->respond_on_exit)
+	if (stk->current_agent == stk->default_agent && stk->respond_on_exit) {
+		if (stk->pending_cmd)
+			stk->cancel_cmd(stk);
+
 		send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED);
+	}
 
 	stk->default_agent = NULL;
 	stk->current_agent = stk->session_agent;
@@ -475,6 +480,9 @@ static void session_agent_notify(gpointer user_data)
 	DBG("Session Agent removed");
 
 	if (stk->current_agent == stk->session_agent && stk->respond_on_exit) {
+		if (stk->pending_cmd)
+			stk->cancel_cmd(stk);
+
 		DBG("Sending Terminate response for session agent");
 		send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED);
 	}
@@ -1852,6 +1860,134 @@ static gboolean handle_command_refresh(const struct stk_command *cmd,
 	return TRUE;
 }
 
+static void send_dtmf_cancel(struct ofono_stk *stk)
+{
+	struct ofono_voicecall *vc = NULL;
+	struct ofono_atom *vc_atom;
+
+	stk->respond_on_exit = FALSE;
+
+	vc_atom = __ofono_modem_find_atom(__ofono_atom_get_modem(stk->atom),
+						OFONO_ATOM_TYPE_VOICECALL);
+	if (vc_atom)
+		vc = __ofono_atom_get_data(vc_atom);
+
+	if (vc) /* Should be always true here */
+		__ofono_voicecall_tone_cancel(vc, stk->dtmf_id);
+
+	stk_alpha_id_unset(stk);
+}
+
+static void dtmf_sent_cb(int error, void *user_data)
+{
+	struct ofono_stk *stk = user_data;
+
+	stk->respond_on_exit = FALSE;
+
+	stk_alpha_id_unset(stk);
+
+	if (error == ENOENT) {
+		struct stk_response rsp;
+		static unsigned char not_in_speech_call_result[] = { 0x07 };
+		static struct ofono_error failure =
+			{ .type = OFONO_ERROR_TYPE_FAILURE };
+
+		memset(&rsp, 0, sizeof(rsp));
+
+		rsp.result.type = STK_RESULT_TYPE_TERMINAL_BUSY;
+		rsp.result.additional_len = sizeof(not_in_speech_call_result);
+		rsp.result.additional = not_in_speech_call_result;
+
+		if (stk_respond(stk, &rsp, stk_command_cb))
+			stk_command_cb(&failure, stk);
+
+		return;
+	}
+
+	if (error == EINVAL)
+		send_simple_response(stk, STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD);
+	else if (error)
+		send_simple_response(stk, STK_RESULT_TYPE_NOT_CAPABLE);
+	else
+		send_simple_response(stk, STK_RESULT_TYPE_SUCCESS);
+}
+
+static gboolean handle_command_send_dtmf(const struct stk_command *cmd,
+						struct stk_response *rsp,
+						struct ofono_stk *stk)
+{
+	static unsigned char not_in_speech_call_result[] = { 0x07 };
+	struct ofono_voicecall *vc = NULL;
+	struct ofono_atom *vc_atom;
+	char dtmf[256], *digit;
+	char *dtmf_from = "01234567890abcABC";
+	char *dtmf_to = "01234567890*#p*#p";
+	int err, pos;
+
+	vc_atom = __ofono_modem_find_atom(__ofono_atom_get_modem(stk->atom),
+						OFONO_ATOM_TYPE_VOICECALL);
+	if (vc_atom)
+		vc = __ofono_atom_get_data(vc_atom);
+
+	if (!vc) {
+		rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE;
+		return TRUE;
+	}
+
+	/* Convert the DTMF string to phone number format */
+	for (pos = 0; cmd->send_dtmf.dtmf[pos] != 0; pos++) {
+		digit = strchr(dtmf_from, cmd->send_dtmf.dtmf[pos]);
+		if (!digit) {
+			rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+			return TRUE;
+		}
+
+		dtmf[pos] = dtmf_to[digit - dtmf_from];
+	}
+	dtmf[pos] = 0;
+
+	err = __ofono_voicecall_tone_send(vc, dtmf, dtmf_sent_cb, stk);
+
+	if (err == -EBUSY) {
+		rsp->result.type = STK_RESULT_TYPE_TERMINAL_BUSY;
+		return TRUE;
+	}
+
+	if (err == -ENOSYS) {
+		rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE;
+		return TRUE;
+	}
+
+	if (err == -ENOENT) {
+		rsp->result.type = STK_RESULT_TYPE_TERMINAL_BUSY;
+		rsp->result.additional_len = sizeof(not_in_speech_call_result);
+		rsp->result.additional = not_in_speech_call_result;
+		return TRUE;
+	}
+
+	if (err < 0) {
+		/*
+		 * We most likely got an out of memory error, tell SIM
+		 * to retry
+		 */
+		rsp->result.type = STK_RESULT_TYPE_TERMINAL_BUSY;
+		return TRUE;
+	}
+
+	stk_alpha_id_set(stk, cmd->send_dtmf.alpha_id, &cmd->send_dtmf.icon_id);
+
+	/*
+	 * Note that we don't strictly require an agent to be connected,
+	 * but to comply with 6.4.24 we need to send a End Session when
+	 * the user decides so.
+	 */
+	stk->respond_on_exit = TRUE;
+	stk->cancel_cmd = send_dtmf_cancel;
+	stk->dtmf_id = err;
+
+	return FALSE;
+}
+
 static void stk_proactive_command_cancel(struct ofono_stk *stk)
 {
 	if (stk->immediate_response)
@@ -2024,6 +2160,11 @@ void ofono_stk_proactive_command_notify(struct ofono_stk *stk,
 							&rsp, stk);
 		break;
 
+	case STK_COMMAND_TYPE_SEND_DTMF:
+		respond = handle_command_send_dtmf(stk->pending_cmd,
+							&rsp, stk);
+		break;
+
 	default:
 		rsp.result.type = STK_RESULT_TYPE_COMMAND_NOT_UNDERSTOOD;
 		break;
-- 
1.7.1.86.g0e460.dirty


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/4] atmodem: Delay return from send_dtmf
  2010-10-21  5:09 [PATCH 1/4] voicecall: __ofono_voicecall_tone_send internal api Andrzej Zaborowski
  2010-10-21  5:09 ` [PATCH 2/4] stk: Handle the Send DTMF proactive command Andrzej Zaborowski
@ 2010-10-21  5:09 ` Andrzej Zaborowski
  2010-10-23  3:56   ` Denis Kenzior
  2010-10-21  5:09 ` [PATCH 4/4] stk: Apply STK text attributes as html Andrzej Zaborowski
  2010-10-23  3:54 ` [PATCH 1/4] voicecall: __ofono_voicecall_tone_send internal api Denis Kenzior
  3 siblings, 1 reply; 8+ messages in thread
From: Andrzej Zaborowski @ 2010-10-21  5:09 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 4405 bytes --]

AT+VTS responds with an OK immediately, so add a wait in the driver
before returning so that core knows when the tone has finished.  Note
that some modems actually do wait against 27.007, and other modems
use a manufacturer specific AT command and the drivers will need to
handle accordingly.
---
 drivers/atmodem/voicecall.c |   63 +++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 61 insertions(+), 2 deletions(-)

diff --git a/drivers/atmodem/voicecall.c b/drivers/atmodem/voicecall.c
index b3c658f..10b8f4b 100644
--- a/drivers/atmodem/voicecall.c
+++ b/drivers/atmodem/voicecall.c
@@ -47,6 +47,9 @@
  /* Amount of time we give for CLIP to arrive before we commence CLCC poll */
 #define CLIP_INTERVAL 200
 
+ /* When +VTD returns 0, an unspecified manufacturer-specific delay is used */
+#define TONE_DURATION 1000
+
 static const char *clcc_prefix[] = { "+CLCC:", NULL };
 static const char *none_prefix[] = { NULL };
 
@@ -59,6 +62,9 @@ struct voicecall_data {
 	unsigned int clcc_source;
 	GAtChat *chat;
 	unsigned int vendor;
+	unsigned int tone_duration;
+	guint vts_source;
+	unsigned int vts_delay;
 };
 
 struct release_id_req {
@@ -522,14 +528,37 @@ static void at_deflect(struct ofono_voicecall *vc,
 	at_template(buf, vc, generic_cb, incoming_or_waiting, cb, data);
 }
 
+static gboolean vts_timeout_cb(gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	struct voicecall_data *vd = cbd->user;
+	ofono_voicecall_cb_t cb = cbd->cb;
+
+	vd->vts_source = 0;
+
+	CALLBACK_WITH_SUCCESS(cb, cbd->data);
+	g_free(cbd);
+
+	return FALSE;
+}
+
 static void vts_cb(gboolean ok, GAtResult *result, gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
+	struct voicecall_data *vd = cbd->user;
 	ofono_voicecall_cb_t cb = cbd->cb;
 	struct ofono_error error;
 
 	decode_at_error(&error, g_at_result_final_response(result));
-	cb(&error, cbd->data);
+
+	if (!ok) {
+		cb(&error, cbd->data);
+
+		g_free(cbd);
+		return;
+	}
+
+	vd->vts_source = g_timeout_add(vd->vts_delay, vts_timeout_cb, cbd);
 }
 
 static void at_send_dtmf(struct ofono_voicecall *vc, const char *dtmf,
@@ -545,6 +574,8 @@ static void at_send_dtmf(struct ofono_voicecall *vc, const char *dtmf,
 	if (!cbd)
 		goto error;
 
+	cbd->user = vd;
+
 	/* strlen("+VTS=T;") = 7 + initial AT + null */
 	buf = g_try_new(char, len * 9 + 3);
 	if (!buf)
@@ -555,8 +586,10 @@ static void at_send_dtmf(struct ofono_voicecall *vc, const char *dtmf,
 	for (i = 1; i < len; i++)
 		s += sprintf(buf + s, ";+VTS=%c", dtmf[i]);
 
+	vd->vts_delay = vd->tone_duration * len;
+
 	s = g_at_chat_send(vd->chat, buf, none_prefix,
-				vts_cb, cbd, g_free);
+				vts_cb, cbd, NULL);
 
 	g_free(buf);
 
@@ -799,6 +832,26 @@ static void busy_notify(GAtResult *result, gpointer user_data)
 			clcc_poll_cb, vc, NULL);
 }
 
+static void vtd_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_voicecall *vc = user_data;
+	struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+	GAtResultIter iter;
+	int duration;
+
+	if (!ok)
+		return;
+
+	g_at_result_iter_init(&iter, result);
+	g_at_result_iter_next(&iter, "+VTD:");
+
+	if (!g_at_result_iter_next_number(&iter, &duration))
+		return;
+
+	if (duration)
+		vd->tone_duration = duration * 100;
+}
+
 static void at_voicecall_initialized(gboolean ok, GAtResult *result,
 					gpointer user_data)
 {
@@ -839,12 +892,15 @@ static int at_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
 
 	vd->chat = g_at_chat_clone(chat);
 	vd->vendor = vendor;
+	vd->tone_duration = TONE_DURATION;
 
 	ofono_voicecall_set_data(vc, vd);
 
 	g_at_chat_send(vd->chat, "AT+CRC=1", NULL, NULL, NULL, NULL);
 	g_at_chat_send(vd->chat, "AT+CLIP=1", NULL, NULL, NULL, NULL);
 	g_at_chat_send(vd->chat, "AT+COLP=1", NULL, NULL, NULL, NULL);
+	g_at_chat_send(vd->chat, "AT+VTD?", NULL,
+				vtd_query_cb, vc, NULL);
 	g_at_chat_send(vd->chat, "AT+CCWA=1", NULL,
 				at_voicecall_initialized, vc, NULL);
 
@@ -858,6 +914,9 @@ static void at_voicecall_remove(struct ofono_voicecall *vc)
 	if (vd->clcc_source)
 		g_source_remove(vd->clcc_source);
 
+	if (vd->vts_source)
+		g_source_remove(vd->vts_source);
+
 	g_slist_foreach(vd->calls, (GFunc) g_free, NULL);
 	g_slist_free(vd->calls);
 
-- 
1.7.1.86.g0e460.dirty


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 4/4] stk: Apply STK text attributes as html.
  2010-10-21  5:09 [PATCH 1/4] voicecall: __ofono_voicecall_tone_send internal api Andrzej Zaborowski
  2010-10-21  5:09 ` [PATCH 2/4] stk: Handle the Send DTMF proactive command Andrzej Zaborowski
  2010-10-21  5:09 ` [PATCH 3/4] atmodem: Delay return from send_dtmf Andrzej Zaborowski
@ 2010-10-21  5:09 ` Andrzej Zaborowski
  2010-10-23  3:56   ` Denis Kenzior
  2010-10-23  3:54 ` [PATCH 1/4] voicecall: __ofono_voicecall_tone_send internal api Denis Kenzior
  3 siblings, 1 reply; 8+ messages in thread
From: Andrzej Zaborowski @ 2010-10-21  5:09 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 11117 bytes --]

---
 src/stk.c |  148 ++++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 123 insertions(+), 25 deletions(-)

diff --git a/src/stk.c b/src/stk.c
index d42a05f..7a43264 100644
--- a/src/stk.c
+++ b/src/stk.c
@@ -257,6 +257,22 @@ void __ofono_cbs_sim_download(struct ofono_stk *stk, const struct cbs *msg)
 		stk_cbs_download_cb(stk, FALSE, NULL, -1);
 }
 
+static char *dbus_apply_text_attributes(const char *text,
+					const struct stk_text_attribute *attr)
+{
+	uint16_t buf[256], *i = buf;
+	const uint8_t *j = attr->attributes;
+	const uint8_t *end = j + attr->len;
+
+	if (attr->len & 3)
+		return NULL;
+
+	while (j < end)
+		*i++ = *j++;
+
+	return stk_text_to_html(text, buf, attr->len / 4);
+}
+
 static struct stk_menu *stk_menu_create(const char *title,
 		const struct stk_text_attribute *title_attr,
 		const struct stk_icon_id *icon, GSList *items,
@@ -268,6 +284,7 @@ static struct stk_menu *stk_menu_create(const char *title,
 	struct stk_menu *ret;
 	GSList *l;
 	int i;
+	struct stk_text_attribute attr;
 
 	DBG("");
 
@@ -281,7 +298,11 @@ static struct stk_menu *stk_menu_create(const char *title,
 	if (ret == NULL)
 		return NULL;
 
-	ret->title = g_strdup(title ? title : "");
+	ret->title = dbus_apply_text_attributes(title ? title : "",
+						title_attr);
+	if (!ret->title)
+		ret->title = g_strdup(title ? title : "");
+
 	memcpy(&ret->icon, icon, sizeof(ret->icon));
 	ret->items = g_new0(struct stk_menu_item, len + 1);
 	ret->default_item = -1;
@@ -290,10 +311,21 @@ static struct stk_menu *stk_menu_create(const char *title,
 
 	for (l = items, i = 0; l; l = l->next, i++) {
 		struct stk_item *item = l->data;
+		char *text;
 
-		ret->items[i].text = g_strdup(item->text);
 		ret->items[i].item_id = item->id;
 
+		text = NULL;
+		if (item_attrs && item_attrs->len) {
+			memcpy(attr.attributes, &item_attrs->list[i * 4], 4);
+			attr.len = 4;
+
+			text = dbus_apply_text_attributes(item->text, &attr);
+		}
+		if (!text)
+			text = strdup(item->text);
+		ret->items[i].text = text;
+
 		if (item_icon_ids && item_icon_ids->len)
 			ret->items[i].icon_id = item_icon_ids->list[i];
 
@@ -390,7 +422,8 @@ static void emit_menu_changed(struct ofono_stk *stk)
 	g_dbus_send_message(conn, signal);
 }
 
-static void stk_alpha_id_set(struct ofono_stk *stk, const char *text,
+static void stk_alpha_id_set(struct ofono_stk *stk,
+		const char *text, const struct stk_text_attribute *attr,
 		const struct stk_icon_id *icon)
 {
 	/* TODO */
@@ -762,7 +795,8 @@ static gboolean handle_command_send_sms(const struct stk_command *cmd,
 
 	stk->cancel_cmd = send_sms_cancel;
 
-	stk_alpha_id_set(stk, cmd->send_sms.alpha_id, &cmd->send_sms.icon_id);
+	stk_alpha_id_set(stk, cmd->send_sms.alpha_id, &cmd->send_sms.text_attr,
+				&cmd->send_sms.icon_id);
 
 	return FALSE;
 }
@@ -773,17 +807,26 @@ static gboolean handle_command_set_idle_text(const struct stk_command *cmd,
 {
 	DBusConnection *conn = ofono_dbus_get_connection();
 	const char *path = __ofono_atom_get_path(stk->atom);
-	const char *idle_mode_text;
+	char *idle_mode_text = NULL;
 
-	if (stk->idle_mode_text) {
-		g_free(stk->idle_mode_text);
-		stk->idle_mode_text = NULL;
+	if (cmd->setup_idle_mode_text.text) {
+		idle_mode_text = dbus_apply_text_attributes(
+					cmd->setup_idle_mode_text.text,
+					&cmd->setup_idle_mode_text.text_attr);
+
+		if (!idle_mode_text) {
+			rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+
+			return TRUE;
+		}
 	}
 
-	if (cmd->setup_idle_mode_text.text)
-		stk->idle_mode_text = g_strdup(cmd->setup_idle_mode_text.text);
+	if (stk->idle_mode_text)
+		g_free(stk->idle_mode_text);
 
-	idle_mode_text = stk->idle_mode_text ? stk->idle_mode_text : "";
+	stk->idle_mode_text = idle_mode_text;
+
+	idle_mode_text = idle_mode_text ? idle_mode_text : "";
 	ofono_dbus_signal_property_changed(conn, path, OFONO_STK_INTERFACE,
 						"IdleModeText",
 						DBUS_TYPE_STRING,
@@ -1166,6 +1209,14 @@ static gboolean handle_command_display_text(const struct stk_command *cmd,
 	struct stk_command_display_text *dt = &stk->pending_cmd->display_text;
 	uint8_t qualifier = stk->pending_cmd->qualifier;
 	ofono_bool_t priority = (qualifier & (1 << 0)) != 0;
+	char *text = dbus_apply_text_attributes(dt->text, &dt->text_attr);
+	int err;
+
+	if (!text) {
+		rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+
+		return TRUE;
+	}
 
 	if (dt->duration.interval) {
 		timeout = dt->duration.interval;
@@ -1179,10 +1230,13 @@ static gboolean handle_command_display_text(const struct stk_command *cmd,
 		}
 	}
 
-	/* We most likely got an out of memory error, tell SIM to retry */
-	if (stk_agent_display_text(stk->current_agent, dt->text, &dt->icon_id,
+	err = stk_agent_display_text(stk->current_agent, text, &dt->icon_id,
 					priority, display_text_cb, stk,
-					display_text_destroy, timeout) < 0) {
+					display_text_destroy, timeout);
+	g_free(text);
+
+	/* We most likely got an out of memory error, tell SIM to retry */
+	if (err < 0) {
 		rsp->result.type = STK_RESULT_TYPE_TERMINAL_BUSY;
 		return TRUE;
 	}
@@ -1316,6 +1370,7 @@ static gboolean handle_command_get_inkey(const struct stk_command *cmd,
 {
 	int timeout = stk->timeout * 1000;
 	const struct stk_command_get_inkey *gi = &cmd->get_inkey;
+	char *text = dbus_apply_text_attributes(gi->text, &gi->text_attr);
 	uint8_t qualifier = stk->pending_cmd->qualifier;
 	gboolean alphabet = (qualifier & (1 << 0)) != 0;
 	gboolean ucs2 = (qualifier & (1 << 1)) != 0;
@@ -1326,6 +1381,12 @@ static gboolean handle_command_get_inkey(const struct stk_command *cmd,
 	 */
 	int err;
 
+	if (!text) {
+		rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+
+		return TRUE;
+	}
+
 	if (gi->duration.interval) {
 		timeout = gi->duration.interval;
 		switch (gi->duration.unit) {
@@ -1342,18 +1403,19 @@ static gboolean handle_command_get_inkey(const struct stk_command *cmd,
 
 	if (yesno)
 		err = stk_agent_request_confirmation(stk->current_agent,
-							gi->text, &gi->icon_id,
+							text, &gi->icon_id,
 							request_confirmation_cb,
 							stk, NULL, timeout);
 	else if (alphabet)
-		err = stk_agent_request_key(stk->current_agent, gi->text,
+		err = stk_agent_request_key(stk->current_agent, text,
 						&gi->icon_id, ucs2,
 						request_key_cb, stk, NULL,
 						timeout);
 	else
-		err = stk_agent_request_digit(stk->current_agent, gi->text,
+		err = stk_agent_request_digit(stk->current_agent, text,
 						&gi->icon_id, request_key_cb,
 						stk, NULL, timeout);
+	g_free(text);
 
 	if (err < 0) {
 		/*
@@ -1414,26 +1476,34 @@ static gboolean handle_command_get_input(const struct stk_command *cmd,
 {
 	int timeout = stk->timeout * 1000;
 	const struct stk_command_get_input *gi = &cmd->get_input;
+	char *text = dbus_apply_text_attributes(gi->text, &gi->text_attr);
 	uint8_t qualifier = stk->pending_cmd->qualifier;
 	gboolean alphabet = (qualifier & (1 << 0)) != 0;
 	gboolean ucs2 = (qualifier & (1 << 1)) != 0;
 	gboolean hidden = (qualifier & (1 << 2)) != 0;
 	int err;
 
+	if (!text) {
+		rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+
+		return TRUE;
+	}
+
 	if (alphabet)
-		err = stk_agent_request_input(stk->current_agent, gi->text,
+		err = stk_agent_request_input(stk->current_agent, text,
 						&gi->icon_id, gi->default_text,
 						ucs2, gi->resp_len.min,
 						gi->resp_len.max, hidden,
 						request_string_cb,
 						stk, NULL, timeout);
 	else
-		err = stk_agent_request_digits(stk->current_agent, gi->text,
+		err = stk_agent_request_digits(stk->current_agent, text,
 						&gi->icon_id, gi->default_text,
 						gi->resp_len.min,
 						gi->resp_len.max, hidden,
 						request_string_cb,
 						stk, NULL, timeout);
+	g_free(text);
 
 	if (err < 0) {
 		/*
@@ -1500,6 +1570,7 @@ static void confirm_call_cb(enum stk_agent_result result, gboolean confirm,
 	uint8_t qualifier = stk->pending_cmd->qualifier;
 	static unsigned char busy_on_call_result[] = { 0x02 };
 	static unsigned char no_cause_result[] = { 0x00 };
+	char *alpha_id = NULL;
 	struct ofono_voicecall *vc = NULL;
 	struct ofono_atom *vc_atom;
 	struct stk_response rsp;
@@ -1535,11 +1606,22 @@ static void confirm_call_cb(enum stk_agent_result result, gboolean confirm,
 		return;
 	}
 
+	if (sc->alpha_id_call_setup) {
+		alpha_id = dbus_apply_text_attributes(sc->alpha_id_call_setup,
+						&sc->text_attr_call_setup);
+		if (!alpha_id) {
+			send_simple_response(stk,
+					STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD);
+			return;
+		}
+	}
+
 	err = __ofono_voicecall_dial(vc, sc->addr.number, sc->addr.ton_npi,
-					sc->alpha_id_call_setup,
-					sc->icon_id_call_setup.id,
+					alpha_id, sc->icon_id_call_setup.id,
 					qualifier >> 1, call_setup_connected,
 					stk);
+	g_free(alpha_id);
+
 	if (err >= 0) {
 		stk->cancel_cmd = call_setup_cancel;
 
@@ -1582,6 +1664,7 @@ static gboolean handle_command_set_up_call(const struct stk_command *cmd,
 	const struct stk_command_setup_call *sc = &cmd->setup_call;
 	uint8_t qualifier = cmd->qualifier;
 	static unsigned char busy_on_call_result[] = { 0x02 };
+	char *alpha_id = NULL;
 	struct ofono_voicecall *vc = NULL;
 	struct ofono_atom *vc_atom;
 	int err;
@@ -1617,9 +1700,19 @@ static gboolean handle_command_set_up_call(const struct stk_command *cmd,
 		return TRUE;
 	}
 
-	err = stk_agent_confirm_call(stk->current_agent, sc->alpha_id_usr_cfm,
+	if (sc->alpha_id_usr_cfm) {
+		alpha_id = dbus_apply_text_attributes(sc->alpha_id_usr_cfm,
+							&sc->text_attr_usr_cfm);
+		if (!alpha_id) {
+			rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+			return TRUE;
+		}
+	}
+
+	err = stk_agent_confirm_call(stk->current_agent, alpha_id,
 					&sc->icon_id_usr_cfm, confirm_call_cb,
 					stk, NULL, stk->timeout * 1000);
+	g_free(alpha_id);
 
 	if (err < 0) {
 		/*
@@ -1794,7 +1887,9 @@ static gboolean handle_command_send_ussd(const struct stk_command *cmd,
 		return TRUE;
 	}
 
-	stk_alpha_id_set(stk, cmd->send_ussd.alpha_id, &cmd->send_ussd.icon_id);
+	stk_alpha_id_set(stk, cmd->send_ussd.alpha_id,
+				&cmd->send_ussd.text_attr,
+				&cmd->send_ussd.icon_id);
 
 	return FALSE;
 }
@@ -1974,7 +2069,9 @@ static gboolean handle_command_send_dtmf(const struct stk_command *cmd,
 		return TRUE;
 	}
 
-	stk_alpha_id_set(stk, cmd->send_dtmf.alpha_id, &cmd->send_dtmf.icon_id);
+	stk_alpha_id_set(stk, cmd->send_dtmf.alpha_id,
+				&cmd->send_dtmf.text_attr,
+				&cmd->send_dtmf.icon_id);
 
 	/*
 	 * Note that we don't strictly require an agent to be connected,
@@ -2204,7 +2301,8 @@ void ofono_stk_proactive_command_handled_notify(struct ofono_stk *stk,
 
 	case STK_COMMAND_TYPE_SEND_SMS:
 		stk_alpha_id_set(stk, cmd->send_sms.alpha_id,
-					&cmd->send_sms.icon_id);
+				&cmd->send_sms.text_attr,
+				&cmd->send_sms.icon_id);
 		break;
 	}
 
-- 
1.7.1.86.g0e460.dirty


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/4] voicecall: __ofono_voicecall_tone_send internal api.
  2010-10-21  5:09 [PATCH 1/4] voicecall: __ofono_voicecall_tone_send internal api Andrzej Zaborowski
                   ` (2 preceding siblings ...)
  2010-10-21  5:09 ` [PATCH 4/4] stk: Apply STK text attributes as html Andrzej Zaborowski
@ 2010-10-23  3:54 ` Denis Kenzior
  3 siblings, 0 replies; 8+ messages in thread
From: Denis Kenzior @ 2010-10-23  3:54 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 562 bytes --]

Hi Andrew,

On 10/21/2010 12:09 AM, Andrzej Zaborowski wrote:
> 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.
> ---
>  src/ofono.h     |    6 ++
>  src/voicecall.c |  269 ++++++++++++++++++++++++++++++++++++++++++++++++-------
>  2 files changed, 243 insertions(+), 32 deletions(-)
> 

Patch has been applied, thanks.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/4] stk: Handle the Send DTMF proactive command.
  2010-10-21  5:09 ` [PATCH 2/4] stk: Handle the Send DTMF proactive command Andrzej Zaborowski
@ 2010-10-23  3:55   ` Denis Kenzior
  0 siblings, 0 replies; 8+ messages in thread
From: Denis Kenzior @ 2010-10-23  3:55 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 819 bytes --]

Hi Andrew,

On 10/21/2010 12:09 AM, Andrzej Zaborowski wrote:
> The default_agent_notify and session_agent_notify changes below are
> needed for respond_on_exit commands to be able to free up their
> resources, reset alpha id, etc.  Commands other than Send DTMF that
> use respond_on_exit are all associated with a agent method call and
> get notified about agent exit in the callback.
> ---
>  src/stk.c |  143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 files changed, 142 insertions(+), 1 deletions(-)
> 

Patch has been applied, thanks.

I did find one minor error here:

> +	if (error == EINVAL)
> +		send_simple_response(stk, STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD);

Seems positive EINVAL can never be returned.  If I missed something, let
me know.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 3/4] atmodem: Delay return from send_dtmf
  2010-10-21  5:09 ` [PATCH 3/4] atmodem: Delay return from send_dtmf Andrzej Zaborowski
@ 2010-10-23  3:56   ` Denis Kenzior
  0 siblings, 0 replies; 8+ messages in thread
From: Denis Kenzior @ 2010-10-23  3:56 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 575 bytes --]

Hi Andrew,

On 10/21/2010 12:09 AM, Andrzej Zaborowski wrote:
> AT+VTS responds with an OK immediately, so add a wait in the driver
> before returning so that core knows when the tone has finished.  Note
> that some modems actually do wait against 27.007, and other modems
> use a manufacturer specific AT command and the drivers will need to
> handle accordingly.
> ---
>  drivers/atmodem/voicecall.c |   63 +++++++++++++++++++++++++++++++++++++++++-
>  1 files changed, 61 insertions(+), 2 deletions(-)
> 

Patch has been applied, thanks.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 4/4] stk: Apply STK text attributes as html.
  2010-10-21  5:09 ` [PATCH 4/4] stk: Apply STK text attributes as html Andrzej Zaborowski
@ 2010-10-23  3:56   ` Denis Kenzior
  0 siblings, 0 replies; 8+ messages in thread
From: Denis Kenzior @ 2010-10-23  3:56 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 344 bytes --]

Hi Andrew,

On 10/21/2010 12:09 AM, Andrzej Zaborowski wrote:
> ---
>  src/stk.c |  148 ++++++++++++++++++++++++++++++++++++++++++++++++++----------
>  1 files changed, 123 insertions(+), 25 deletions(-)
> 

Patch has been applied, thanks.  I also went ahead and marked the
relevant task on the TODO list as done.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2010-10-23  3:56 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-10-21  5:09 [PATCH 1/4] voicecall: __ofono_voicecall_tone_send internal api Andrzej Zaborowski
2010-10-21  5:09 ` [PATCH 2/4] stk: Handle the Send DTMF proactive command Andrzej Zaborowski
2010-10-23  3:55   ` Denis Kenzior
2010-10-21  5:09 ` [PATCH 3/4] atmodem: Delay return from send_dtmf Andrzej Zaborowski
2010-10-23  3:56   ` Denis Kenzior
2010-10-21  5:09 ` [PATCH 4/4] stk: Apply STK text attributes as html Andrzej Zaborowski
2010-10-23  3:56   ` Denis Kenzior
2010-10-23  3:54 ` [PATCH 1/4] voicecall: __ofono_voicecall_tone_send internal api Denis Kenzior

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox