All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.