All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] HFP support into oFono and BlueZ
@ 2010-01-11 17:08 ` Gustavo F. Padovan
  0 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-11 17:08 UTC (permalink / raw)
  To: linux-bluetooth, ofono, zhenhua.zhang

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

Hi,

These patches implement the new API for the Audio Gateway in BlueZ. It
follows the last version of the HandsfreeGateway and HandsfreeAgent
Intefaces API.
The first two patches is for BlueZ and the other for oFono. You can
test it with using enable-modem and test-voicecall scripts into the
test dir of oFono.
Feel free to test it and send me your comments. We have some bugs yet.

The audio part is not working yet. We are going to work on pulseaudio
this week to get this done soon.

Regards,

-- 
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi

[-- Attachment #2: 0001-clean-up-audio-gateway.c.patch --]
[-- Type: application/octet-stream, Size: 24620 bytes --]

From 61dcdcd2b51cc60a7e424dbb46f073bf666091bb Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 6 Nov 2009 18:08:58 -0200
Subject: [PATCH 1/2] clean up audio/gateway.c

remove all code related to the AT engine
---
 audio/gateway.c |  760 +------------------------------------------------------
 1 files changed, 1 insertions(+), 759 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index a1c1ea2..3dc09ff 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -53,52 +53,6 @@
 #include "dbus-common.h"
 
 #define RFCOMM_BUF_SIZE 256
-/* not-more-then-16 defined by GSM + 1 for NULL + padding */
-#define AG_INDICATOR_DESCR_SIZE 20
-#define AG_CALLER_NUM_SIZE 64	/* size of number + type */
-
-/* commands */
-#define AG_FEATURES "AT+BRSF=26\r"     /* = 0x7F = All features supported */
-#define AG_INDICATORS_SUPP "AT+CIND=?\r"
-#define AG_INDICATORS_VAL "AT+CIND?\r"
-#define AG_INDICATORS_ENABLE "AT+CMER=3,0,0,1\r"
-#define AG_HOLD_MPTY_SUPP "AT+CHLD=?\r"
-#define AG_CALLER_IDENT_ENABLE "AT+CLIP=1\r"
-#define AG_CARRIER_FORMAT "AT+COPS=3,0\r"
-#define AG_EXTENDED_RESULT_CODE "AT+CMEE=1\r"
-
-#define AG_FEATURE_3WAY 0x1
-#define AG_FEATURE_EXTENDED_RES_CODE 0x100
-/* Hold and multipary AG features.
- * Comments below are copied from hands-free spec for reference */
-/* Releases all held calls or sets User Determined User Busy (UDUB)
- * for a waiting call */
-#define AG_CHLD_0 0x01
-/* Releases all active calls (if any exist) and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_1 0x02
-/* Releases specified active call only <x> */
-#define AG_CHLD_1x 0x04
-/* Places all active calls (if any exist) on hold and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_2 0x08
-/* Request private consultation mode with specified call <x> (Place all
- * calls on hold EXCEPT the call <x>) */
-#define AG_CHLD_2x 0x10
-/* Adds a held call to the conversation */
-#define AG_CHLD_3 0x20
-/* Connects the two calls and disconnects the subscriber from both calls
- * (Explicit Call Transfer). Support for this value and its associated
- * functionality is optional for the HF. */
-#define AG_CHLD_4 0x40
-
-#define OK_RESPONSE "\r\nOK\r\n"
-#define ERROR_RESPONSE "\r\nERROR\r\n"
-
-struct indicator {
-	gchar descr[AG_INDICATOR_DESCR_SIZE];
-	gint value;
-};
 
 struct gateway {
 	gateway_state_t state;
@@ -108,387 +62,10 @@ struct gateway {
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
 	DBusMessage *connect_message;
-	guint ag_features;
-	guint hold_multiparty_features;
-	GSList *indies;
-	gboolean is_dialing;
-	gboolean call_active;
-
-	int sp_gain;
-	int mic_gain;
 };
 
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device);
-
 int gateway_close(struct audio_device *device);
 
-static void rfcomm_start_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	gw->rfcomm_watch_id = g_io_add_watch(gw->rfcomm,
-			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-			(GIOFunc) rfcomm_ag_data_cb, dev);
-}
-
-static void rfcomm_stop_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	g_source_remove(gw->rfcomm_watch_id);
-}
-
-static gboolean io_channel_write_all(GIOChannel *io, gchar *data,
-					gsize count)
-{
-	gsize written = 0;
-	GIOStatus status;
-
-	while (count > 0) {
-		status = g_io_channel_write_chars(io, data, count, &written,
-						NULL);
-		if (status != G_IO_STATUS_NORMAL)
-			return FALSE;
-
-		data += written;
-		count -= written;
-	}
-	return TRUE;
-}
-
-/* it's worth to mention that data and response could be the same pointers */
-static gboolean rfcomm_send_and_read(struct gateway *gw, gchar *data,
-                                    gchar *response, gsize count)
-{
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read = 0;
-	gboolean got_ok = FALSE;
-	gboolean got_error = FALSE;
-	gchar *resp_buf = response;
-	gsize toread = RFCOMM_BUF_SIZE - 1;
-	GIOStatus status;
-
-	if (!io_channel_write_all(rfcomm, data, count))
-		return FALSE;
-
-	while (!(got_ok || got_error)) {
-		status = g_io_channel_read_chars(rfcomm, resp_buf, toread,
-						&read, NULL);
-		if (status == G_IO_STATUS_NORMAL)
-			resp_buf[read] = '\0';
-		else {
-			debug("rfcomm_send_and_read(): %m");
-			return FALSE;
-		}
-		got_ok = NULL != strstr(resp_buf, OK_RESPONSE);
-		got_error = NULL != strstr(resp_buf, ERROR_RESPONSE);
-		resp_buf += read;
-		toread -= read;
-	}
-	return TRUE;
-}
-
-/* get <descr> from the names: (<descr>, (<values>)), (<descr>, (<values>))
- * ... */
-static GSList *parse_indicator_names(gchar *names, GSList *indies)
-{
-	gchar *current = names - 1;
-	GSList *result = indies;
-	gchar *next;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 2;
-		next = strstr(current, ",(");
-		ind = g_slice_new(struct indicator);
-		strncpy(ind->descr, current, 20);
-		ind->descr[(intptr_t) next - (intptr_t) current] = '\0';
-		result = g_slist_append(result, (gpointer) ind);
-		current = strstr(next + 1, ",(");
-	}
-	return result;
-}
-
-/* get values from <val0>,<val1>,... */
-static GSList *parse_indicator_values(gchar *values, GSList *indies)
-{
-	gint val;
-	gchar *current = values - 1;
-	GSList *runner = indies;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 1;
-		sscanf(current, "%d", &val);
-		current = strchr(current, ',');
-		ind = g_slist_nth_data(runner, 0);
-		ind->value = val;
-		runner = g_slist_next(runner);
-	}
-	return indies;
-}
-
-/* get values from <val0>,<val1>,... */
-static guint get_hold_mpty_features(gchar *features)
-{
-	guint result = 0;
-
-	if (strstr(features, "0"))
-		result |= AG_CHLD_0;
-
-	if (strstr(features, "1"))
-		result |= AG_CHLD_1;
-
-	if (strstr(features, "1x"))
-		result |= AG_CHLD_1x;
-
-	if (strstr(features, "2"))
-		result |= AG_CHLD_2;
-
-	if (strstr(features, "2x"))
-		result |= AG_CHLD_2x;
-
-	if (strstr(features, "3"))
-		result |= AG_CHLD_3;
-
-	if (strstr(features, "4"))
-		result |= AG_CHLD_4;
-
-	return result;
-}
-
-static gboolean establish_service_level_conn(struct gateway *gw)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	gboolean res;
-
-	debug("at the begin of establish_service_level_conn()");
-	res = rfcomm_send_and_read(gw, AG_FEATURES, buf,
-				sizeof(AG_FEATURES) - 1);
-	if (!res || sscanf(buf, "\r\n+BRSF:%d", &gw->ag_features) != 1)
-		return FALSE;
-
-	debug("features are 0x%X", gw->ag_features);
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_SUPP, buf,
-				sizeof(AG_INDICATORS_SUPP) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_names(strchr(buf, '('), NULL);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_VAL, buf,
-		sizeof(AG_INDICATORS_VAL) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_values(strchr(buf, ':') + 1, gw->indies);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_ENABLE, buf,
-				sizeof(AG_INDICATORS_ENABLE) - 1);
-	if (!res || !strstr(buf, "OK"))
-		return FALSE;
-
-	if ((gw->ag_features & AG_FEATURE_3WAY) != 0) {
-		res = rfcomm_send_and_read(gw, AG_HOLD_MPTY_SUPP, buf,
-				sizeof(AG_HOLD_MPTY_SUPP) - 1);
-		if (!res || !strstr(buf, "+CHLD:")) {
-			g_slice_free1(RFCOMM_BUF_SIZE, buf);
-			return FALSE;
-		}
-		gw->hold_multiparty_features = get_hold_mpty_features(
-							strchr(buf, '('));
-
-	} else
-		gw->hold_multiparty_features = 0;
-
-	debug("Service layer connection successfully established!");
-	rfcomm_send_and_read(gw, AG_CALLER_IDENT_ENABLE, buf,
-			sizeof(AG_CALLER_IDENT_ENABLE) - 1);
-	rfcomm_send_and_read(gw, AG_CARRIER_FORMAT, buf,
-			sizeof(AG_CARRIER_FORMAT) - 1);
-	if ((gw->ag_features & AG_FEATURE_EXTENDED_RES_CODE) != 0)
-		rfcomm_send_and_read(gw, AG_EXTENDED_RESULT_CODE, buf,
-			sizeof(AG_EXTENDED_RESULT_CODE) - 1);
-
-	return TRUE;
-}
-
-static void process_ind_change(struct audio_device *dev, guint index,
-							gint value)
-{
-	struct gateway *gw = dev->gateway;
-	struct indicator *ind = g_slist_nth_data(gw->indies, index - 1);
-	gchar *name = ind->descr;
-
-	ind->value = value;
-
-	debug("at the begin of process_ind_change, name is %s", name);
-	if (!strcmp(name, "\"call\"")) {
-		if (value > 0) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallStarted", DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-			gw->call_active = TRUE;
-		} else {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallEnded", DBUS_TYPE_INVALID);
-			gw->call_active = FALSE;
-		}
-
-	} else if (!strcmp(name, "\"callsetup\"")) {
-		if (value == 0 && gw->is_dialing) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-		} else if (!gw->is_dialing && value > 0)
-			gw->is_dialing = TRUE;
-
-	} else if (!strcmp(name, "\"callheld\"")) {
-		/* FIXME: The following code is based on assumptions only.
-		 * Has to be tested for interoperability
-		 * I assume that callheld=2 would be sent when dial from HF
-		 * failed in case of 3-way call
-		 * Unfortunately this path is not covered by the HF spec so
-		 * the code has to be tested for interop
-		*/
-		/* '2' means: all calls held, no active calls */
-		if (value == 2) {
-			if (gw->is_dialing) {
-				g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-				gw->is_dialing = FALSE;
-			}
-		}
-	} else if (!strcmp(name, "\"service\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RegistrationStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"signal\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "SignalStrength",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"roam\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RoamingStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"battchg\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "BatteryCharge",
-				DBUS_TYPE_UINT16, &value);
-}
-
-static void process_ring(struct audio_device *device, GIOChannel *chan,
-			gchar *buf)
-{
-	gchar number[AG_CALLER_NUM_SIZE];
-	gchar *cli;
-	gchar *sep;
-	gsize read;
-	GIOStatus status;
-
-	rfcomm_stop_watch(device);
-	status = g_io_channel_read_chars(chan, buf, RFCOMM_BUF_SIZE - 1, &read, NULL);
-	if (status != G_IO_STATUS_NORMAL)
-		return;
-
-	debug("at the begin of process_ring");
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 10)
-		error("process_ring(): buf is too long '%s'", buf);
-	else if ((cli = strstr(buf, "\r\n+CLIP"))) {
-		if (sscanf(cli, "\r\n+CLIP: \"%s", number) == 1) {
-			sep = strchr(number, '"');
-			sep[0] = '\0';
-
-			/* FIXME:signal will be emitted on each RING+CLIP.
-			 * That's bad */
-			cli = number;
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE, "Ring",
-					DBUS_TYPE_STRING, &cli,
-					DBUS_TYPE_INVALID);
-			device->gateway->is_dialing = TRUE;
-		} else
-			error("process_ring(): '%s' in place of +CLIP after RING", buf);
-
-	}
-
-	rfcomm_start_watch(device);
-}
-
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	struct gateway *gw;
-	gsize read;
-	/* some space for value */
-	gchar indicator[AG_INDICATOR_DESCR_SIZE + 4];
-	gint value;
-	guint index;
-	gchar *sep;
-
-	debug("at the begin of rfcomm_ag_data_cb()");
-	if (cond & G_IO_NVAL)
-		return FALSE;
-
-	gw = device->gateway;
-
-	if (cond & (G_IO_ERR | G_IO_HUP)) {
-		debug("connection with remote BT is closed");
-		gateway_close(device);
-		return FALSE;
-	}
-
-	if (g_io_channel_read_chars(chan, buf, sizeof(buf) - 1, &read, NULL)
-			!= G_IO_STATUS_NORMAL)
-		return TRUE;
-	buf[read] = '\0';
-
-	if (strlen(buf) > AG_INDICATOR_DESCR_SIZE + 14)
-		error("rfcomm_ag_data_cb(): buf is too long '%s'", buf);
-	else if (sscanf(buf, "\r\n+CIEV:%s\r\n", indicator) == 1) {
-		sep = strchr(indicator, ',');
-		sep[0] = '\0';
-		sep += 1;
-		index = atoi(indicator);
-		value = atoi(sep);
-		process_ind_change(device, index, value);
-	} else if (strstr(buf, "RING"))
-		process_ring(device, chan, buf);
-	else if (sscanf(buf, "\r\n+BVRA:%d\r\n", &value) == 1) {
-		if (value == 0)
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionActive",
-					DBUS_TYPE_INVALID);
-		else
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionInactive",
-					DBUS_TYPE_INVALID);
-	} else if (sscanf(buf, "\r\n+VGS:%d\r\n", &value) == 1) {
-		gw->sp_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "SpeakerGain",
-				DBUS_TYPE_UINT16, &value);
-	} else if (sscanf(buf, "\r\n+VGM:%d\r\n", &value) == 1) {
-		gw->mic_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "MicrophoneGain",
-				DBUS_TYPE_UINT16, &value);
-	} else
-		error("rfcomm_ag_data_cb(): read wrong data '%s'", buf);
-
-	return TRUE;
-}
-
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
 {
@@ -541,7 +118,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	DBusMessage *conn_mes = gw->connect_message;
 	gchar gw_addr[18];
 	GIOFlags flags;
 
@@ -563,29 +139,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 	if (!gw->rfcomm)
 		gw->rfcomm = g_io_channel_ref(chan);
 
-	if (establish_service_level_conn(dev->gateway)) {
-		gboolean value = TRUE;
-
-		debug("%s: Connected to %s", dev->path, gw_addr);
-		rfcomm_start_watch(dev);
-		if (conn_mes) {
-			DBusMessage *reply =
-				dbus_message_new_method_return(conn_mes);
-			dbus_connection_send(dev->conn, reply, NULL);
-			dbus_message_unref(reply);
-			dbus_message_unref(conn_mes);
-			gw->connect_message = NULL;
-		}
-
-		gw->state = GATEWAY_STATE_CONNECTED;
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN,	&value);
-		return;
-	} else
-		error("%s: Failed to establish service layer connection to %s",
-			dev->path, gw_addr);
-
 	if (NULL != gw->sco_start_cb)
 		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 
@@ -732,321 +285,20 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
-static DBusMessage *process_ag_reponse(DBusMessage *msg, gchar *response)
-{
-	DBusMessage *reply;
-
-
-	debug("in process_ag_reponse, response is %s", response);
-	if (strstr(response, OK_RESPONSE))
-		reply = dbus_message_new_method_return(msg);
-	else {
-		/* FIXME: some code should be here to processes errors
-		 *  in better fasion */
-		debug("AG responded with '%s' to %s method call", response,
-				dbus_message_get_member(msg));
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".OperationFailed",
-					"Operation failed.See log for details");
-	}
-	return reply;
-}
-
-static DBusMessage *process_simple(DBusMessage *msg, struct audio_device *dev,
-					gchar *data)
-{
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-
-	rfcomm_stop_watch(dev);
-	rfcomm_send_and_read(gw, data, buf, strlen(data));
-	rfcomm_start_watch(dev);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_ANSWER "ATA\r"
-
-static DBusMessage *ag_answer(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	if (gw->call_active)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".CallAlreadyAnswered",
-					"Call AlreadyAnswered");
-
-	return process_simple(msg, dev, AG_ANSWER);
-}
-
-#define AG_HANGUP "AT+CHUP\r"
-
-static DBusMessage *ag_terminate_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	return process_simple(msg, dev, AG_HANGUP);
-}
-
-/* according to GSM spec */
-#define ALLOWED_NUMBER_SYMBOLS "1234567890*#ABCD"
-#define AG_PLACE_CALL "ATD%s;\r"
-/* dialing from memory is not supported as headset spec doesn't define a way
- * to retreive phone memory entries.
- */
-static DBusMessage *ag_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint atd_len;
-	DBusMessage *result;
-
-	debug("at the begin of ag_call()");
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	atd_len = sprintf(buf, AG_PLACE_CALL, number);
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, buf, buf, atd_len);
-	rfcomm_start_watch(device);
-
-	result = process_ag_reponse(msg, buf);
-	return result;
-}
-
-#define AG_GET_CARRIER "AT+COPS?\r"
-
-static DBusMessage *ag_get_operator(DBusConnection *conn, DBusMessage *msg,
-					void *data)
-{
-	struct audio_device *dev = (struct audio_device *) data;
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read;
-	gchar *result, *sep;
-	DBusMessage *reply;
-	GIOStatus status;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(dev);
-	io_channel_write_all(rfcomm, AG_GET_CARRIER, strlen(AG_GET_CARRIER));
-
-	status = g_io_channel_read_chars(rfcomm, buf, RFCOMM_BUF_SIZE - 1,
-						&read, NULL);
-	rfcomm_start_watch(dev);
-	if (G_IO_STATUS_NORMAL == status) {
-		buf[read] = '\0';
-		if (strstr(buf, "+COPS")) {
-			if (!strrchr(buf, ','))
-				result = "0";
-			else {
-				result = strchr(buf, '\"') + 1;
-				sep = strchr(result, '\"');
-				sep[0] = '\0';
-			}
-
-			reply = dbus_message_new_method_return(msg);
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&result, DBUS_TYPE_INVALID);
-		} else {
-			info("ag_get_operator(): '+COPS' expected but"
-				" '%s' received", buf);
-			reply = dbus_message_new_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Unexpected response from AG");
-		}
-	} else {
-		error("ag_get_operator(): %m");
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".ConnectionFailed",
-					"Failed to receive response from AG");
-	}
-
-	return reply;
-}
-
-#define AG_SEND_DTMF "AT+VTS=%c\r"
-static DBusMessage *ag_send_dtmf(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint com_len;
-	gboolean got_ok = TRUE;
-	gint num_len;
-	gint i = 0;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	num_len = strlen(number);
-	rfcomm_stop_watch(device);
-	while (i < num_len && got_ok) {
-		com_len = sprintf(buf, AG_SEND_DTMF, number[i]);
-		rfcomm_send_and_read(gw, buf, buf, com_len);
-		got_ok = NULL != strstr(buf, OK_RESPONSE);
-		i += 1;
-	}
-	rfcomm_start_watch(device);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_GET_SUBSCRIBER_NUMS "AT+CNUM\r"
-#define CNUM_LEN 5             /* length of "+CNUM" string */
-#define MAX_NUMBER_CNT 16
-static DBusMessage *ag_get_subscriber_num(DBusConnection *conn,
-					DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number, *end;
-	DBusMessage *reply = dbus_message_new_method_return(msg);
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, AG_GET_SUBSCRIBER_NUMS, buf,
-			strlen(AG_GET_SUBSCRIBER_NUMS));
-	rfcomm_start_watch(device);
-
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 21)
-		error("ag_get_subscriber_num(): buf is too long '%s'", buf);
-	else if (strstr(buf, "+CNUM")) {
-		number = strchr(buf, ',');
-		number++;
-		end = strchr(number, ',');
-		if (end) {
-			*end = '\0';
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&number, DBUS_TYPE_INVALID);
-		}
-	} else
-		error("ag_get_subscriber_num(): read wrong data '%s'", buf);
-
-	return reply;
-}
-
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	DBusMessage *reply;
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	gboolean value;
-	guint index = 0;
-	struct indicator *ind;
-
-	reply = dbus_message_new_method_return(msg);
-	if (!reply)
-		return NULL;
-
-	dbus_message_iter_init_append(reply, &iter);
-
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
-	/* Connected */
-	value = gateway_is_connected(device);
-	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
-
-	if (!value)
-		goto done;
-
-	while ((ind = g_slist_nth_data(gw->indies, index))) {
-		if(!strcmp(ind->descr, "\"service\""))
-			dict_append_entry(&dict, "RegistrationStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"signal\""))
-			dict_append_entry(&dict, "SignalStrength",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"roam\""))
-			dict_append_entry(&dict, "RoamingStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"battchg\""))
-			dict_append_entry(&dict, "BatteryCharge",
-					DBUS_TYPE_UINT16, &ind->value);
-		index++;
-	}
-
-	/* SpeakerGain */
-	dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16,
-				&device->gateway->sp_gain);
-
-	/* MicrophoneGain */
-	dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16,
-				&device->gateway->mic_gain);
-done:
-	dbus_message_iter_close_container(&iter, &dict);
-	return reply;
+	return NULL;
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "Disconnect", "", "", ag_disconnect },
-	{ "AnswerCall", "", "", ag_answer },
-	{ "TerminateCall", "", "", ag_terminate_call },
-	{ "Call", "s", "", ag_call },
-	{ "GetOperatorName", "", "s", ag_get_operator },
-	{ "SendDTMF", "s", "", ag_send_dtmf },
-	{ "GetSubscriberNumber", "", "s", ag_get_subscriber_num },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
 	{ NULL, NULL, NULL, NULL }
 };
 
 static GDBusSignalTable gateway_signals[] = {
-	{ "Ring", "s" },
-	{ "CallTerminated", "" },
-	{ "CallStarted", "" },
-	{ "CallEnded", "" },
 	{ "PropertyChanged", "sv" },
 	{ NULL, NULL }
 };
@@ -1063,9 +315,6 @@ struct gateway *gateway_init(struct audio_device *dev)
 
 	debug("in gateway_init, dev is %p", dev);
 	gw = g_new0(struct gateway, 1);
-	gw->indies = NULL;
-	gw->is_dialing = FALSE;
-	gw->call_active = FALSE;
 	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
 
@@ -1107,11 +356,6 @@ void gateway_start_service(struct audio_device *device)
 	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
 }
 
-static void indicator_slice_free(gpointer mem)
-{
-	g_slice_free(struct indicator, mem);
-}
-
 int gateway_close(struct audio_device *device)
 {
 	struct gateway *gw = device->gateway;
@@ -1119,8 +363,6 @@ int gateway_close(struct audio_device *device)
 	GIOChannel *sco = gw->sco;
 	gboolean value = FALSE;
 
-	g_slist_foreach(gw->indies, (GFunc) indicator_slice_free, NULL);
-	g_slist_free(gw->indies);
 	if (rfcomm) {
 		g_io_channel_shutdown(rfcomm, TRUE, NULL);
 		g_io_channel_unref(rfcomm);
-- 
1.6.4.4


[-- Attachment #3: 0002-Implement-HandsfreeGateway-Interface.patch --]
[-- Type: application/octet-stream, Size: 25574 bytes --]

From ead81eacbb6fcfff6b279f18f7546cf25ca69c11 Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 8 Jan 2010 17:38:21 -0200
Subject: [PATCH 2/2] Implement HandsfreeGateway Interface

Create a interface where a Handsfree agent can register and use BlueZ to
handle the rfcomm and sco links.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for his
prototype on this code.
---
 audio/gateway.c |  592 ++++++++++++++++++++++++++++++++++++++++++++++---------
 audio/gateway.h |   12 +-
 audio/manager.c |   20 +--
 audio/unix.c    |    8 +
 4 files changed, 522 insertions(+), 110 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index 3dc09ff..8cb66ad 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -5,6 +5,8 @@
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
  *  Copyright (C) 2008-2009  Leonid Movshovich <event.riga@gmail.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
+ *  Copyright (C) 2010  Gustavo F. Padovan <padovan@profusion.mobi>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -32,12 +34,15 @@
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
 
 #include <glib.h>
 #include <dbus/dbus.h>
 #include <gdbus.h>
 
 #include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
 #include <bluetooth/sco.h>
@@ -52,19 +57,106 @@
 #include "btio.h"
 #include "dbus-common.h"
 
-#define RFCOMM_BUF_SIZE 256
+#define MAX_OPEN_TRIES 5
+#define OPEN_WAIT 300
+
+struct agent {
+	char *name;	/* Bus id */
+	char *path;	/* D-Bus path */
+	guint watch;	/* Disconnect watch */
+};
 
 struct gateway {
 	gateway_state_t state;
-	GIOChannel *rfcomm;
-	guint rfcomm_watch_id;
+	int channel;
+	char *tty;		/* gw->tty created from rfcomm */
+	GIOChannel *rfcomm;	/* remote AG requested rfcomm connection */
+	int rfcomm_id;		/* present N in /dev/rfcommN */
+	int rfcomm_fd;
 	GIOChannel *sco;
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
-	DBusMessage *connect_message;
+	struct agent *agent;
+	DBusMessage *msg;
 };
 
-int gateway_close(struct audio_device *device);
+static struct audio_device *active;	/* single active device with oFono */
+static DBusConnection *connection;
+
+static gboolean gateway_close(gpointer data);
+static int gateway_release_tty(struct audio_device *device);
+
+static const char *connected2str(int i)
+{
+	switch (i) {
+		case GATEWAY_STATE_DISCONNECTED:
+			return "disconnected";
+		case GATEWAY_STATE_CONNECTING:
+			return "connecting";
+		case GATEWAY_STATE_CONNECTED:
+			return "connected";
+		case GATEWAY_STATE_PLAYING:
+			return "playing";
+		default:
+			return "";
+	}
+}
+
+static void agent_free(struct agent *agent)
+{
+	if (!agent)
+		return;
+
+	g_free(agent->name);
+	g_free(agent->path);
+	g_free(agent);
+}
+
+static void agent_disconnect(struct agent *agent)
+{
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "Release");
+
+	dbus_message_set_no_reply(msg, TRUE);
+	g_dbus_send_message(connection, msg);
+
+	g_dbus_remove_watch(connection, agent->watch);
+}
+
+static gboolean agent_sendfd(struct agent *agent, int fd,
+		DBusPendingCallNotifyFunction notify, void *data)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "NewConnection");
+
+	dbus_message_append_args(msg, DBUS_TYPE_UNIX_FD, &fd,
+					DBUS_TYPE_INVALID);
+
+	if (dbus_connection_send_with_reply(connection, msg, &call, -1) == FALSE)
+		return FALSE;
+
+	dbus_pending_call_set_notify(call, notify, data, NULL);
+	dbus_pending_call_unref(call);
+
+	return TRUE;
+}
+
+static void change_state(struct audio_device *dev, int new_state)
+{
+	struct gateway *gw = dev->gateway;
+	const char *val = connected2str(new_state);
+
+	gw->state = new_state;
+
+	emit_property_changed(connection,dev->path,
+			AUDIO_GATEWAY_INTERFACE, "Connected",
+			DBUS_TYPE_STRING, &val);
+}
 
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
@@ -79,6 +171,7 @@ static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 		g_io_channel_shutdown(gw->sco, TRUE, NULL);
 		g_io_channel_unref(gw->sco);
 		gw->sco = NULL;
+		change_state(dev, GATEWAY_STATE_CONNECTED);
 		return FALSE;
 	}
 
@@ -92,63 +185,205 @@ static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
 
 	debug("at the begin of sco_connect_cb() in gateway.c");
 
+	gw->sco = chan;
+	g_io_channel_ref(chan);
+
 	if (err) {
 		error("sco_connect_cb(): %s", err->message);
-		/* not sure, but from other point of view,
-		 * what is the reason to have headset which
-		 * cannot play audio? */
-		if (gw->sco_start_cb)
-			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 		gateway_close(dev);
 		return;
 	}
 
-	gw->sco = g_io_channel_ref(chan);
 	if (gw->sco_start_cb)
 		gw->sco_start_cb(dev, gw->sco_start_cb_data);
 
-	/* why is this here? */
 	fcntl(g_io_channel_unix_get_fd(chan), F_SETFL, 0);
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
 				(GIOFunc) sco_io_cb, dev);
 }
 
+static void open_notify(int fd, int err, struct audio_device *dev)
+{
+	DBusMessage *reply = NULL;
+	struct gateway *gw = dev->gateway;
+	const char *result = "ok";
+
+	if (err) {
+		/* Max tries exceeded */
+		gateway_close(dev);
+
+		if (gw->msg)
+			reply = g_dbus_create_error(gw->msg,
+						ERROR_INTERFACE ".Failed",
+						strerror(err));
+	} else {
+		if (gw->msg)
+			reply = g_dbus_create_reply(gw->msg,
+						DBUS_TYPE_STRING, &result,
+						DBUS_TYPE_INVALID);
+
+		emit_property_changed(dev->conn, dev->path,
+					AUDIO_GATEWAY_INTERFACE,
+					"Device", DBUS_TYPE_STRING,
+					&gw->tty);
+
+		change_state(dev, GATEWAY_STATE_CONNECTED);
+		active = dev;
+	}
+
+	/* Reply to the requestor */
+	if (gw->msg && reply)
+		g_dbus_send_message(dev->conn, reply);
+}
+
+static void newconnection_reply(DBusPendingCall *call, void *data)
+{
+	struct gateway *gw = data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError derr;
+
+	if (!gw->rfcomm) {
+		debug("RFCOMM disconnected from server before agent reply");
+		return;
+	}
+
+	dbus_error_init(&derr);
+	if (!dbus_set_error_from_message(&derr, reply)) {
+		info("Agent reply: file descriptor passed successfuly");
+		return;
+	}
+
+	debug("Agent reply: %s", derr.message);
+
+	dbus_error_free(&derr);
+	g_idle_add(gateway_close, gw);
+}
+
+static gboolean open_continue(gpointer user_data)
+{
+	struct audio_device *dev = user_data;
+	struct gateway *gw = dev->gateway;
+	int fd;
+	static int ntries = MAX_OPEN_TRIES;
+	DBusMessage *reply;
+
+	fd = open(gw->tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
+	if (fd < 0) {
+		int err = errno;
+		error("Could not open %s: %s (%d)",
+				gw->tty, strerror(err), err);
+		if (!--ntries) {
+			/* Reporting error */
+			open_notify(fd, err, dev);
+			ntries = MAX_OPEN_TRIES;
+			return FALSE;
+		}
+		return TRUE;
+	}
+	/* Connection succeeded */
+	open_notify(fd, 0, dev);
+	gw->rfcomm_fd = fd;
+
+	if (!agent_sendfd(gw->agent, fd, newconnection_reply, gw))
+		reply = g_dbus_create_error(gw->msg, ERROR_INTERFACE ".Failed",
+				"Can not pass file descriptor");
+	else
+		reply = dbus_message_new_method_return(gw->msg);
+
+	return FALSE;
+}
+
+static int port_open(struct audio_device *dev)
+{
+	int fd;
+
+	fd = open(dev->gateway->tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
+	if (fd < 0) {
+		g_timeout_add(OPEN_WAIT, open_continue, dev);
+		return -EINPROGRESS;
+	}
+
+	return fd;
+}
+
 static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 				gpointer user_data)
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	gchar gw_addr[18];
-	GIOFlags flags;
+	struct rfcomm_dev_req req;
+	DBusMessage *reply;
+	int sk, fd, id;
+	const char *err_msg;
+	char src[18], dst[18];
 
 	if (err) {
 		error("connect(): %s", err->message);
 		if (gw->sco_start_cb)
 			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
-		return;
+		goto fail;
 	}
 
-	ba2str(&dev->dst, gw_addr);
-	/* Blocking mode should be default, but just in case: */
-	flags = g_io_channel_get_flags(chan);
-	flags &= ~G_IO_FLAG_NONBLOCK;
-	flags &= G_IO_FLAG_MASK;
-	g_io_channel_set_flags(chan, flags, NULL);
-	g_io_channel_set_encoding(chan, NULL, NULL);
-	g_io_channel_set_buffered(chan, FALSE);
-	if (!gw->rfcomm)
-		gw->rfcomm = g_io_channel_ref(chan);
+	ba2str(&dev->src, src);
+	ba2str(&dev->dst, dst);
+
+	/* make /dev/rfcomm serial port from chan */
+	memset(&req, 0, sizeof(req));
+	req.dev_id = -1;
+	req.flags = (1 << RFCOMM_REUSE_DLC);
+	bacpy(&req.src, &dev->src);
+	bacpy(&req.dst, &dev->dst);
+	req.channel = gw->channel;
+
+	sk = g_io_channel_unix_get_fd(chan);
+	id = ioctl(sk, RFCOMMCREATEDEV, &req);
+	if (id < 0) {
+		err_msg = strerror(errno);
+		error("ioctl(RFCOMMCREATEDEV) failed: %s (%d)",
+				strerror(errno), errno);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		g_io_channel_unref(chan);
+		chan = NULL;
+		goto fail;
+	}
 
-	if (NULL != gw->sco_start_cb)
-		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
+	gw->rfcomm_id = id;
+	gw->tty = g_strdup_printf("/dev/rfcomm%d", id);
 
-	gateway_close(dev);
+	g_io_channel_shutdown(chan, TRUE, NULL);
+
+	/* Addressing connect port */
+	fd = port_open(dev);
+	if (fd < 0)
+		/* Open in progress: Wait the callback */
+		return;
+
+	open_notify(fd, 0, dev);
+	gw->rfcomm_fd = fd;
+
+	if (!agent_sendfd(gw->agent, fd, newconnection_reply, gw)) {
+		reply = g_dbus_create_error(gw->msg, ERROR_INTERFACE ".Failed",
+				"Can not pass file descriptor");
+	} else
+		reply = dbus_message_new_method_return(gw->msg);
+
+	return;
+
+fail:
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".Failed", ".Connection attempt failed");
+		dbus_message_unref(gw->msg);
+		gw->msg = NULL;
+	}
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
+	return;
 }
 
 static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 {
 	struct audio_device *dev = user_data;
-	DBusMessage *msg = dev->gateway->connect_message;
+	struct gateway *gw = dev->gateway;
 	int ch = -1;
 	sdp_list_t *protos, *classes;
 	uuid_t uuid;
@@ -182,8 +417,6 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 
 	if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
 			uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
-		sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free,
-									NULL);
 		sdp_list_free(protos, NULL);
 		error("Invalid service record or not HFP");
 		goto fail;
@@ -197,6 +430,8 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 		goto fail;
 	}
 
+	gw->channel = ch;
+
 	io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -204,25 +439,26 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 				BT_IO_OPT_INVALID);
 	if (!io) {
 		error("Unable to connect: %s", err->message);
-		if (msg) {
-			error_common_reply(dev->conn, msg, ERROR_INTERFACE
-						".ConnectionAttemptFailed",
-						err->message);
-			msg = NULL;
-		}
-		g_error_free(err);
+		if (err)
+			g_error_free(err);
 		gateway_close(dev);
+		goto fail;
 	}
 
 	g_io_channel_unref(io);
+
+	change_state(dev, GATEWAY_STATE_CONNECTING);
 	return;
 
 fail:
-	if (msg)
-		error_common_reply(dev->conn, msg, ERROR_INTERFACE
-					".NotSupported", "Not supported");
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".NotSupported", "Not supported");
+		dbus_message_unref(gw->msg);
+		gw->msg = NULL;
+	}
 
-	dev->gateway->connect_message = NULL;
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
 
 	sco_cb = dev->gateway->sco_start_cb;
 	if (sco_cb)
@@ -245,22 +481,93 @@ static DBusMessage *ag_connect(DBusConnection *conn, DBusMessage *msg,
 	struct gateway *gw = au_dev->gateway;
 
 	debug("at the begin of ag_connect()");
-	if (gw->rfcomm)
+
+	if (gw->tty)
 		return g_dbus_create_error(msg, ERROR_INTERFACE
 					".AlreadyConnected",
 					"Already Connected");
 
-	gw->connect_message = dbus_message_ref(msg);
+	if (!gw->agent)
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+				".Failed", "Agent not assined");
+
+	gw->msg = dbus_message_ref(msg);
 	if (get_records(au_dev) < 0) {
-		dbus_message_unref(gw->connect_message);
+		dbus_message_unref(gw->msg);
 		return g_dbus_create_error(msg, ERROR_INTERFACE
 					".ConnectAttemptFailed",
 					"Connect Attempt Failed");
 	}
 	debug("at the end of ag_connect()");
+
 	return NULL;
 }
 
+static int gateway_release_tty(struct audio_device *device)
+{
+	struct gateway *gw = device->gateway;
+	struct rfcomm_dev_req req;
+	int rfcomm_ctl;
+	int err = 0;
+
+	rfcomm_ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM);
+	if (rfcomm_ctl < 0)
+		return -errno;
+
+	if (gw->rfcomm_fd) {
+		close(gw->rfcomm_fd);
+		gw->rfcomm_fd = -1;
+	}
+
+	memset(&req, 0, sizeof(req));
+	req.dev_id = gw->rfcomm_id;
+
+	req.flags = (1 << RFCOMM_HANGUP_NOW);
+
+	if (ioctl(rfcomm_ctl, RFCOMMRELEASEDEV, &req) < 0) {
+		err = errno;
+		error("Can't release device %s: %s (%d)",
+				gw->tty, strerror(err), err);
+	}
+	close(rfcomm_ctl);
+	gw->rfcomm_id = -1;
+	return 0;
+}
+
+static gboolean gateway_close(gpointer data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	gboolean value = FALSE;
+
+	if (gw->tty) {
+		gateway_release_tty(device);
+		g_free(gw->tty);
+		gw->tty = NULL;
+	}
+
+	if (gw->rfcomm) {
+		g_io_channel_shutdown(gw->rfcomm, TRUE, NULL);
+		g_io_channel_unref(gw->rfcomm);
+		gw->rfcomm = NULL;
+	}
+
+	if (gw->sco) {
+		g_io_channel_shutdown(gw->sco, TRUE, NULL);
+		g_io_channel_unref(gw->sco);
+		gw->sco = NULL;
+		gw->sco_start_cb = NULL;
+		gw->sco_start_cb_data = NULL;
+	}
+
+	gw->state = GATEWAY_STATE_DISCONNECTED;
+
+	emit_property_changed(device->conn, device->path,
+				AUDIO_GATEWAY_INTERFACE,
+				"Connected", DBUS_TYPE_BOOLEAN, &value);
+	return FALSE;
+}
+
 static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
@@ -269,11 +576,14 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	DBusMessage *reply = NULL;
 	char gw_addr[18];
 
+	if (!connection)
+		return NULL;
+
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
 		return NULL;
 
-	if (!gw->rfcomm)
+	if (!gw->tty && !gw->rfcomm)
 		return g_dbus_create_error(msg, ERROR_INTERFACE
 						".NotConnected",
 						"Device not Connected");
@@ -285,16 +595,117 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
+static void agent_exited(DBusConnection *conn, void *data)
+{
+	struct gateway *gateway = data;
+	struct agent *agent = gateway->agent;
+
+	debug("Agent %s exited", agent->name);
+
+	agent_free(agent);
+	gateway->agent = NULL;
+}
+
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	return NULL;
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	const char *value;
+
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	value = connected2str(gw->state);
+	dict_append_entry(&dict, "Connected",
+			DBUS_TYPE_STRING, &value);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct agent *agent;
+	const char *path, *name;
+
+
+	if (gw->agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".AlreadyExists",
+				"Agent already exists");
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+			DBUS_TYPE_INVALID))
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+				".InvalidArguments", "Invalid argument");
+
+	name = dbus_message_get_sender(msg);
+	agent = g_new0(struct agent, 1);
+	if (!agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed",
+				"Failed to create a new agent");
+
+	agent->name = strdup(name);
+	agent->path = strdup(path);
+
+	agent->watch = g_dbus_add_disconnect_watch(conn, name,
+			agent_exited, gw, NULL);
+
+	gw->agent = agent;
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+				DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct agent *agent;
+
+	if (!gw->agent)
+		goto done;
+
+	agent = gw->agent;
+	if (strcmp(agent->name, dbus_message_get_sender(msg)) != 0)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed", "Permission denied");
+
+	agent_disconnect(gw->agent);
+
+	g_dbus_remove_watch(conn, agent->watch);
+
+	agent_free(agent);
+	gw->agent = NULL;
+
+done:
+	return dbus_message_new_method_return(msg);
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
-	{ "Disconnect", "", "", ag_disconnect },
+	{ "Disconnect", "", "", ag_disconnect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
+	{ "RegisterAgent", "o", "", register_agent },
+	{ "UnregisterAgent", "o", "", unregister_agent },
 	{ NULL, NULL, NULL, NULL }
 };
 
@@ -307,17 +718,23 @@ struct gateway *gateway_init(struct audio_device *dev)
 {
 	struct gateway *gw;
 
+	gw = g_new0(struct gateway, 1);
+
+	connection = dbus_connection_ref(dev->conn);
+
 	if (!g_dbus_register_interface(dev->conn, dev->path,
 					AUDIO_GATEWAY_INTERFACE,
 					gateway_methods, gateway_signals,
 					NULL, dev, NULL))
 		return NULL;
 
-	debug("in gateway_init, dev is %p", dev);
-	gw = g_new0(struct gateway, 1);
-	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
+}
 
+void gateway_exit()
+{
+	dbus_connection_unref(connection);
+	connection = NULL;
 }
 
 gboolean gateway_is_connected(struct audio_device *dev)
@@ -326,17 +743,6 @@ gboolean gateway_is_connected(struct audio_device *dev)
 			dev->gateway->state == GATEWAY_STATE_CONNECTED);
 }
 
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
-{
-	if (!io)
-		return -EINVAL;
-
-	g_io_channel_ref(io);
-	dev->gateway->rfcomm = io;
-
-	return 0;
-}
-
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 {
 	struct gateway *gw = dev->gateway;
@@ -347,42 +753,39 @@ int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 	gw->sco = g_io_channel_ref(io);
 
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                                (GIOFunc) sco_io_cb, dev);
+				 (GIOFunc) sco_io_cb, dev);
+
+	change_state(dev, GATEWAY_STATE_PLAYING);
+
 	return 0;
 }
 
-void gateway_start_service(struct audio_device *device)
+int gateway_connect_rfcomm(struct audio_device *dev,
+		GIOChannel *chan, int ch)
 {
-	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
+	if (!chan)
+		return -EINVAL;
+
+	g_io_channel_ref(chan);
+	dev->gateway->rfcomm = chan;
+	dev->gateway->channel = ch;
+
+	return 0;
 }
 
-int gateway_close(struct audio_device *device)
+void gateway_start_service(struct audio_device *dev)
 {
-	struct gateway *gw = device->gateway;
-	GIOChannel *rfcomm = gw->rfcomm;
-	GIOChannel *sco = gw->sco;
-	gboolean value = FALSE;
+	struct gateway *gw = dev->gateway;
+	GError *err = NULL;
 
-	if (rfcomm) {
-		g_io_channel_shutdown(rfcomm, TRUE, NULL);
-		g_io_channel_unref(rfcomm);
-		gw->rfcomm = NULL;
-	}
+	if (gw->rfcomm == NULL)
+		return;
 
-	if (sco) {
-		g_io_channel_shutdown(sco, TRUE, NULL);
-		g_io_channel_unref(sco);
-		gw->sco = NULL;
-		gw->sco_start_cb = NULL;
-		gw->sco_start_cb_data = NULL;
+	if (!bt_io_accept(gw->rfcomm, rfcomm_connect_cb, dev, NULL,
+							&err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
 	}
-
-	gw->state = GATEWAY_STATE_DISCONNECTED;
-
-	emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN, &value);
-	return 0;
 }
 
 /* These are functions to be called from unix.c for audio system
@@ -394,13 +797,17 @@ gboolean gateway_request_stream(struct audio_device *dev,
 	GError *err = NULL;
 	GIOChannel *io;
 
-	if (!gw->rfcomm) {
+	if (!gw->tty && !gw->rfcomm) {
 		gw->sco_start_cb = cb;
 		gw->sco_start_cb_data = user_data;
 		get_records(dev);
 	} else if (!gw->sco) {
+		char source[128], destination[128];
+
 		gw->sco_start_cb = cb;
 		gw->sco_start_cb_data = user_data;
+		ba2str(&dev->src, source);
+		ba2str(&dev->dst, destination);
 		io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -418,12 +825,12 @@ gboolean gateway_request_stream(struct audio_device *dev,
 	return TRUE;
 }
 
-int gateway_config_stream(struct audio_device *dev, gateway_stream_cb_t sco_cb,
-				void *user_data)
+int gateway_config_stream(struct audio_device *dev,
+				gateway_stream_cb_t sco_cb, void *user_data)
 {
 	struct gateway *gw = dev->gateway;
 
-	if (!gw->rfcomm) {
+	if (!gw->rfcomm && !gw->tty) {
 		gw->sco_start_cb = sco_cb;
 		gw->sco_start_cb_data = user_data;
 		return get_records(dev);
@@ -464,4 +871,3 @@ void gateway_suspend_stream(struct audio_device *dev)
 	gw->sco_start_cb = NULL;
 	gw->sco_start_cb_data = NULL;
 }
-
diff --git a/audio/gateway.h b/audio/gateway.h
index 3b0457f..eaf6801 100644
--- a/audio/gateway.h
+++ b/audio/gateway.h
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -22,20 +23,25 @@
  *
  */
 
-#define AUDIO_GATEWAY_INTERFACE "org.bluez.HeadsetGateway"
+#define AUDIO_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
 
 #define DEFAULT_HSP_HS_CHANNEL 6
 #define DEFAULT_HFP_HS_CHANNEL 7
 
 typedef enum {
 	GATEWAY_STATE_DISCONNECTED,
-	GATEWAY_STATE_CONNECTED
+	GATEWAY_STATE_CONNECTED,
+	GATEWAY_STATE_CONNECTING,
+	GATEWAY_STATE_PLAYING,
 } gateway_state_t;
 
 typedef void (*gateway_stream_cb_t) (struct audio_device *dev, void *user_data);
+
 struct gateway *gateway_init(struct audio_device *device);
+void gateway_exit();
 gboolean gateway_is_connected(struct audio_device *dev);
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *chan);
+int gateway_connect_rfcomm(struct audio_device *dev,
+			GIOChannel *chan, int ch);
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *chan);
 void gateway_start_service(struct audio_device *device);
 gboolean gateway_request_stream(struct audio_device *dev,
diff --git a/audio/manager.c b/audio/manager.c
index 413c1f3..e768014 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -198,7 +198,7 @@ static void handle_uuid(const char *uuidstr, struct audio_device *device)
 		break;
 	case HANDSFREE_AGW_SVCLASS_ID:
 		debug("Found Handsfree AG record");
-		if (device->gateway == NULL)
+		if (enabled.gateway && (device->gateway == NULL))
 			device->gateway = gateway_init(device);
 		break;
 	case AUDIO_SINK_SVCLASS_ID:
@@ -586,7 +586,7 @@ static void hf_io_cb(GIOChannel *chan, gpointer data)
 		goto drop;
 	}
 
-	if (gateway_connect_rfcomm(device, chan) < 0) {
+	if (gateway_connect_rfcomm(device, chan, ch) < 0) {
 		error("Allocating new GIOChannel failed!");
 		goto drop;
 	}
@@ -905,22 +905,12 @@ static void headset_server_remove(struct btd_adapter *adapter)
 static int gateway_server_probe(struct btd_adapter *adapter)
 {
 	struct audio_adapter *adp;
-	const gchar *path = adapter_get_path(adapter);
-	int ret;
-
-	DBG("path %s", path);
 
 	adp = audio_adapter_get(adapter);
 	if (!adp)
 		return -EINVAL;
 
-	ret = gateway_server_init(adp);
-	if (ret < 0) {
-		audio_adapter_ref(adp);
-		return ret;
-	}
-
-	return 0;
+	return gateway_server_init(adp);
 }
 
 static void gateway_server_remove(struct btd_adapter *adapter)
@@ -1166,8 +1156,10 @@ void audio_manager_exit(void)
 		telephony_exit();
 	}
 
-	if (enabled.gateway)
+	if (enabled.gateway) {
 		btd_unregister_adapter_driver(&gateway_server_driver);
+		gateway_exit();
+	}
 
 	if (enabled.source || enabled.sink)
 		btd_unregister_adapter_driver(&a2dp_server_driver);
diff --git a/audio/unix.c b/audio/unix.c
index 5cf4f94..bd1a415 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -395,6 +395,9 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	struct bt_start_stream_rsp *rsp = (void *) buf;
 	struct bt_new_stream_ind *ind = (void *) buf;
 
+	if (!dev)
+		goto failed;
+
 	memset(buf, 0, sizeof(buf));
 	rsp->h.type = BT_RESPONSE;
 	rsp->h.name = BT_START_STREAM;
@@ -416,6 +419,11 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	}
 
 	client->req_id = 0;
+	return;
+
+failed:
+	error("gateway_resume_complete: resume failed");
+	unix_ipc_error(client, BT_START_STREAM, EIO);
 }
 
 static void headset_suspend_complete(struct audio_device *dev, void *user_data)
-- 
1.6.4.4


[-- Attachment #4: 0001-Add-HFP-support-in-oFono.patch --]
[-- Type: application/octet-stream, Size: 13342 bytes --]

From fdc16a31547438d8b91f5521185f8ef5f5cf43fc Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 8 Jan 2010 17:25:43 -0200
Subject: [PATCH] Add HFP support in oFono

It uses BlueZ through to get HFP working following the
org.bluez.HandsfreeGateway and org.bluez.HandsfreeAgent from the BlueZ
D-Bus API.

You need the HFP patch into BlueZ too.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for its prototype
on this code.
---
 gatchat/gattty.c    |   23 ++++
 gatchat/gattty.h    |    1 +
 include/modem.h     |    2 +
 plugins/hfp.c       |  316 +++++++++++++++++++++++++++++++++++++++++++++++++--
 plugins/modemconf.c |    3 +-
 src/modem.c         |   15 +++
 6 files changed, 348 insertions(+), 12 deletions(-)

diff --git a/gatchat/gattty.c b/gatchat/gattty.c
index 7d665e1..b25675b 100644
--- a/gatchat/gattty.c
+++ b/gatchat/gattty.c
@@ -260,3 +260,26 @@ GIOChannel *g_at_tty_open(const char *tty, GHashTable *options)
 
 	return channel;
 }
+
+GIOChannel *g_at_get_channel_from_fd(int fd)
+{
+	GIOChannel *channel;
+	struct termios ti;
+
+	/* Switch TTY to raw mode */
+	memset(&ti, 0, sizeof(ti));
+	cfmakeraw(&ti);
+	tcflush(fd, TCIOFLUSH);
+	tcsetattr(fd, TCSANOW, &ti);
+
+	channel = g_io_channel_unix_new(fd);
+
+	if (channel == NULL) {
+		close(fd);
+		return NULL;
+	}
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+
+	return channel;
+}
diff --git a/gatchat/gattty.h b/gatchat/gattty.h
index c8e0c8f..5fff05d 100644
--- a/gatchat/gattty.h
+++ b/gatchat/gattty.h
@@ -43,6 +43,7 @@ extern "C" {
  */
 GIOChannel *g_at_tty_open(const char *tty, GHashTable *options);
 
+GIOChannel *g_at_get_channel_from_fd(int fd);
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/modem.h b/include/modem.h
index f718b9c..a375b12 100644
--- a/include/modem.h
+++ b/include/modem.h
@@ -47,6 +47,8 @@ struct ofono_modem *ofono_modem_create(const char *type);
 int ofono_modem_register(struct ofono_modem *modem);
 void ofono_modem_remove(struct ofono_modem *modem);
 
+struct ofono_modem *ofono_modem_get_by_path(const char *path);
+
 void ofono_modem_set_powered(struct ofono_modem *modem, ofono_bool_t powered);
 ofono_bool_t ofono_modem_get_powered(struct ofono_modem *modem);
 
diff --git a/plugins/hfp.c b/plugins/hfp.c
index b56abdb..dfa84fb 100644
--- a/plugins/hfp.c
+++ b/plugins/hfp.c
@@ -30,6 +30,8 @@
 #include <glib.h>
 #include <gatchat.h>
 #include <gattty.h>
+#include <gdbus.h>
+#include <ofono.h>
 
 #define OFONO_API_SUBJECT_TO_CHANGE
 #include <ofono/plugin.h>
@@ -52,11 +54,28 @@
 
 #include <drivers/hfpmodem/hfpmodem.h>
 
+#include <ofono/dbus.h>
+
+#define BLUEZ_SERVICE "org.bluez"
+#define BLUEZ_PATH "/"
+#define BLUEZ_MANAGER_INTERFACE "org.bluez.Manager"
+#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter"
+#define BLUEZ_DEVICE_INTERFACE "org.bluez.Device"
+#define BLUEZ_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
+
+#define HFP_AGENT_INTERFACE "org.bluez.HandsfreeAgent"
+
 static const char *brsf_prefix[] = { "+BRSF:", NULL };
 static const char *cind_prefix[] = { "+CIND:", NULL };
 static const char *cmer_prefix[] = { "+CMER:", NULL };
 static const char *chld_prefix[] = { "+CHLD:", NULL };
 
+static DBusConnection *connection;
+static char *ofono_handsfree_path;
+
+
+static int timeout;
+
 static int hfp_disable(struct ofono_modem *modem);
 
 static void hfp_debug(const char *str, void *user_data)
@@ -135,6 +154,60 @@ static void cmer_cb(gboolean ok, GAtResult *result, gpointer user_data)
 		sevice_level_conn_established(modem);
 }
 
+static int send_method_call(const char *dest, const char *path,
+				const char *interface, const char *method,
+				DBusPendingCallNotifyFunction cb,
+				void *user_data, int type, ...)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+	va_list args;
+
+	msg = dbus_message_new_method_call(dest, path, interface, method);
+	if (!msg) {
+		ofono_error("Unable to allocate new D-Bus %s message", method);
+		return -ENOMEM;
+	}
+
+	va_start(args, type);
+
+	if (!dbus_message_append_args_valist(msg, type, args)) {
+		dbus_message_unref(msg);
+		va_end(args);
+		return -EIO;
+	}
+
+	va_end(args);
+
+	if (!cb) {
+		g_dbus_send_message(connection, msg);
+		return 0;
+	}
+
+	if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+		ofono_error("Sending %s failed", method);
+		dbus_message_unref(msg);
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(call, cb, user_data, NULL);
+	dbus_pending_call_unref(call);
+	dbus_message_unref(msg);
+
+	return 0;
+}
+
+static gboolean hfp_enable_timeout(gpointer user)
+{
+	struct ofono_modem *modem = user;
+
+	if (ofono_modem_get_powered(modem))
+		return FALSE;
+
+	hfp_disable(modem);
+	return FALSE;
+}
+
 static void cind_status_cb(gboolean ok, GAtResult *result,
 				gpointer user_data)
 {
@@ -260,8 +333,7 @@ error:
 }
 
 /* either oFono or Phone could request SLC connection */
-static int service_level_connection(struct ofono_modem *modem,
-				const char *tty)
+static int service_level_connection(struct ofono_modem *modem, int fd)
 {
 	struct hfp_data *data = ofono_modem_get_data(modem);
 	GIOChannel *io;
@@ -269,7 +341,7 @@ static int service_level_connection(struct ofono_modem *modem,
 	GAtChat *chat;
 	char buf[64];
 
-	io = g_at_tty_open(tty, NULL);
+	io = g_at_get_channel_from_fd(fd);
 	if (!io) {
 		ofono_error("Service level connection failed: %s (%d)",
 			strerror(errno), errno);
@@ -296,9 +368,173 @@ static int service_level_connection(struct ofono_modem *modem,
 	return -EINPROGRESS;
 }
 
+static DBusMessage *hfp_agent_new_connection(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	int fd;
+	const char *path;
+	struct ofono_modem *modem = NULL;
+
+	path = dbus_message_get_path(msg);
+	modem = ofono_modem_get_by_path(path);
+
+	if (!modem)
+		return NULL;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UNIX_FD, &fd,
+				DBUS_TYPE_INVALID))
+		return __ofono_error_invalid_args(msg);
+
+	service_level_connection(modem, fd);
+
+	return NULL;
+}
+
+static DBusMessage *hfp_agent_release(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	return NULL;
+}
+
+static GDBusMethodTable agent_methods[] = {
+        { "NewConnection", "h", "", hfp_agent_new_connection,
+		G_DBUS_METHOD_FLAG_ASYNC },
+	{ "Release", "", "", hfp_agent_release },
+	{NULL, NULL, NULL, NULL}
+};
+
+static void agent_registered_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusMessage *reply;
+
+	reply = dbus_pending_call_steal_reply(call);
+	dbus_message_unref(reply);
+}
+
+static void agent_unregistered_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusMessage *reply;
+
+	reply = dbus_pending_call_steal_reply(call);
+	dbus_message_unref(reply);
+}
+
+static void find_device_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	struct ofono_modem *modem = user_data;
+	const char *device, *obj_path;
+	int ret;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH,
+				&device, DBUS_TYPE_INVALID) == FALSE) {
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("aha! %s", err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	ofono_debug("Using device %s", device);
+	ofono_handsfree_path = g_strdup(device);
+
+	obj_path = ofono_modem_get_path(modem);
+
+	ret = send_method_call(BLUEZ_SERVICE, device,
+				BLUEZ_GATEWAY_INTERFACE, "RegisterAgent",
+				agent_registered_cb, NULL, DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+
+	if (ret < 0)
+		ofono_error("agent_register failed(%d)", ret);
+
+done:
+	dbus_message_unref(reply);;
+}
+
+static void get_adapter_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	struct ofono_modem *modem = user_data;
+	const char *adapter = NULL;
+	const char *address;
+	int ret;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+	if (dbus_message_get_args(reply, &err,
+				DBUS_TYPE_OBJECT_PATH,
+				&adapter, DBUS_TYPE_INVALID) == FALSE) {
+		if (adapter == NULL) {
+			ofono_error("bluetooth adapter is not enabled");
+			dbus_error_free(&err);
+			goto done;
+		}
+
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s %s", adapter, err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	address = ofono_modem_get_string(modem, "Address");
+
+	DBG("address %s", address);
+
+	if (address == NULL)
+		return;
+
+	ret = send_method_call(BLUEZ_SERVICE, adapter,
+				BLUEZ_ADAPTER_INTERFACE, "FindDevice",
+				find_device_cb, modem,
+				DBUS_TYPE_STRING, &address,
+				DBUS_TYPE_INVALID);
+
+	if (ret < 0)
+		ofono_error("find_device failed(%d)", ret);
+
+done:
+	dbus_message_unref(reply);
+
+}
+
+static int hfp_register_ofono_handsfree(struct ofono_modem *modem)
+{
+	ofono_debug("Register oFono Agent to bluetooth daemon");
+
+	return send_method_call(BLUEZ_SERVICE, BLUEZ_PATH,
+				BLUEZ_MANAGER_INTERFACE, "DefaultAdapter",
+				get_adapter_cb, modem,
+				DBUS_TYPE_INVALID);
+}
+
+static int hfp_unregister_ofono_handsfree(struct ofono_modem *modem)
+{
+	const char *obj_path = ofono_modem_get_path(modem);
+
+	ofono_debug("Unregister oFono Agent to bluetooth daemon");
+
+	if (!ofono_handsfree_path)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, ofono_handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "UnregisterAgent",
+				agent_unregistered_cb, NULL,
+				DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+}
+
 static int hfp_probe(struct ofono_modem *modem)
 {
 	struct hfp_data *data;
+	const char *obj_path;
+	DBusConnection *conn = ofono_dbus_get_connection();
 
 	data = g_try_new0(struct hfp_data, 1);
 	if (!data)
@@ -312,6 +548,15 @@ static int hfp_probe(struct ofono_modem *modem)
 
 	ofono_modem_set_data(modem, data);
 
+	connection = dbus_connection_ref(conn);
+
+	obj_path = ofono_modem_get_path(modem);
+	g_dbus_register_interface(conn, obj_path, HFP_AGENT_INTERFACE,
+			agent_methods, NULL, NULL, data, NULL);
+
+	if (hfp_register_ofono_handsfree(modem) != 0)
+		return -EINVAL;
+
 	return 0;
 }
 
@@ -322,24 +567,74 @@ static void hfp_remove(struct ofono_modem *modem)
 	if (data)
 		g_free(data);
 
+	hfp_unregister_ofono_handsfree(modem);
+
+	if (ofono_handsfree_path)
+		g_free(ofono_handsfree_path);
+
+	dbus_connection_unref(connection);
+
 	ofono_modem_set_data(modem, NULL);
 }
 
+static void port_connect_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	const char *msg;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_STRING,
+				&msg, DBUS_TYPE_INVALID) == FALSE) {
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s", err.message);
+			dbus_error_free(&err);
+			goto done;
+		}
+	}
+
+	if (strcmp(msg, "ok"))
+		ofono_error("Connect failed: %s", msg);
+done:
+	dbus_message_unref(reply);
+}
+
+static int hfp_connect_ofono_handsfree()
+{
+	ofono_debug("Connect to bluetooth daemon");
+
+	if (!ofono_handsfree_path || !connection)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, ofono_handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Connect",
+				port_connect_cb, NULL, DBUS_TYPE_INVALID);
+}
+
 /* power up hardware */
 static int hfp_enable(struct ofono_modem *modem)
 {
-	const char *tty;
-	int ret;
-
 	DBG("%p", modem);
 
-	tty = ofono_modem_get_string(modem, "Device");
-	if (tty == NULL)
+	timeout = g_timeout_add_seconds(10, hfp_enable_timeout, modem);
+
+	if (hfp_connect_ofono_handsfree() < 0)
 		return -EINVAL;
 
-	ret = service_level_connection(modem, tty);
+	return -EINPROGRESS;
+}
+
+static int hfp_disconnect_ofono_handsfree()
+{
+	if (!ofono_handsfree_path || !connection)
+		return -EINVAL;
 
-	return ret;
+	return send_method_call(BLUEZ_SERVICE, ofono_handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Disconnect",
+				NULL, NULL, DBUS_TYPE_INVALID);
 }
 
 static int hfp_disable(struct ofono_modem *modem)
@@ -359,6 +654,7 @@ static int hfp_disable(struct ofono_modem *modem)
 
 	ofono_modem_set_powered(modem, FALSE);
 
+	hfp_disconnect_ofono_handsfree();
 	return 0;
 }
 
diff --git a/plugins/modemconf.c b/plugins/modemconf.c
index 882fa5a..8fdeff0 100644
--- a/plugins/modemconf.c
+++ b/plugins/modemconf.c
@@ -124,12 +124,11 @@ static struct ofono_modem *create_modem(GKeyFile *keyfile, const char *group)
 
 	modem = ofono_modem_create(driver);
 
-	if (!g_strcmp0(driver, "phonesim"))
+	if (!g_strcmp0(driver, "phonesim") || !g_strcmp0(driver, "hfp"))
 		set_address(modem, keyfile, group);
 
 	if (!g_strcmp0(driver, "atgen") || !g_strcmp0(driver, "g1") ||
 						!g_strcmp0(driver, "calypso") ||
-						!g_strcmp0(driver, "hfp") ||
 						!g_strcmp0(driver, "palmpre"))
 		set_device(modem, keyfile, group);
 
diff --git a/src/modem.c b/src/modem.c
index 557fe6e..ff413df 100644
--- a/src/modem.c
+++ b/src/modem.c
@@ -1067,6 +1067,21 @@ struct ofono_modem *ofono_modem_create(const char *type)
 	return modem;
 }
 
+struct ofono_modem *ofono_modem_get_by_path(const char *path)
+{
+	GSList *l;
+	struct ofono_modem *modem;
+
+	for (l = g_modem_list; l; l = l->next) {
+		modem = l->data;
+
+		if (!g_strcmp0(modem->path, path))
+			return modem;
+	}
+
+	return NULL;
+}
+
 static void emit_modems()
 {
 	DBusConnection *conn = ofono_dbus_get_connection();
-- 
1.6.4.4


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

* [RFC] HFP support into oFono and BlueZ
@ 2010-01-11 17:08 ` Gustavo F. Padovan
  0 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-11 17:08 UTC (permalink / raw)
  To: ofono

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

Hi,

These patches implement the new API for the Audio Gateway in BlueZ. It
follows the last version of the HandsfreeGateway and HandsfreeAgent
Intefaces API.
The first two patches is for BlueZ and the other for oFono. You can
test it with using enable-modem and test-voicecall scripts into the
test dir of oFono.
Feel free to test it and send me your comments. We have some bugs yet.

The audio part is not working yet. We are going to work on pulseaudio
this week to get this done soon.

Regards,

-- 
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi

[-- Attachment #2: 0001-clean-up-audio-gateway.c.patch --]
[-- Type: application/octet-stream, Size: 24620 bytes --]

From 61dcdcd2b51cc60a7e424dbb46f073bf666091bb Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 6 Nov 2009 18:08:58 -0200
Subject: [PATCH 1/2] clean up audio/gateway.c

remove all code related to the AT engine
---
 audio/gateway.c |  760 +------------------------------------------------------
 1 files changed, 1 insertions(+), 759 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index a1c1ea2..3dc09ff 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -53,52 +53,6 @@
 #include "dbus-common.h"
 
 #define RFCOMM_BUF_SIZE 256
-/* not-more-then-16 defined by GSM + 1 for NULL + padding */
-#define AG_INDICATOR_DESCR_SIZE 20
-#define AG_CALLER_NUM_SIZE 64	/* size of number + type */
-
-/* commands */
-#define AG_FEATURES "AT+BRSF=26\r"     /* = 0x7F = All features supported */
-#define AG_INDICATORS_SUPP "AT+CIND=?\r"
-#define AG_INDICATORS_VAL "AT+CIND?\r"
-#define AG_INDICATORS_ENABLE "AT+CMER=3,0,0,1\r"
-#define AG_HOLD_MPTY_SUPP "AT+CHLD=?\r"
-#define AG_CALLER_IDENT_ENABLE "AT+CLIP=1\r"
-#define AG_CARRIER_FORMAT "AT+COPS=3,0\r"
-#define AG_EXTENDED_RESULT_CODE "AT+CMEE=1\r"
-
-#define AG_FEATURE_3WAY 0x1
-#define AG_FEATURE_EXTENDED_RES_CODE 0x100
-/* Hold and multipary AG features.
- * Comments below are copied from hands-free spec for reference */
-/* Releases all held calls or sets User Determined User Busy (UDUB)
- * for a waiting call */
-#define AG_CHLD_0 0x01
-/* Releases all active calls (if any exist) and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_1 0x02
-/* Releases specified active call only <x> */
-#define AG_CHLD_1x 0x04
-/* Places all active calls (if any exist) on hold and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_2 0x08
-/* Request private consultation mode with specified call <x> (Place all
- * calls on hold EXCEPT the call <x>) */
-#define AG_CHLD_2x 0x10
-/* Adds a held call to the conversation */
-#define AG_CHLD_3 0x20
-/* Connects the two calls and disconnects the subscriber from both calls
- * (Explicit Call Transfer). Support for this value and its associated
- * functionality is optional for the HF. */
-#define AG_CHLD_4 0x40
-
-#define OK_RESPONSE "\r\nOK\r\n"
-#define ERROR_RESPONSE "\r\nERROR\r\n"
-
-struct indicator {
-	gchar descr[AG_INDICATOR_DESCR_SIZE];
-	gint value;
-};
 
 struct gateway {
 	gateway_state_t state;
@@ -108,387 +62,10 @@ struct gateway {
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
 	DBusMessage *connect_message;
-	guint ag_features;
-	guint hold_multiparty_features;
-	GSList *indies;
-	gboolean is_dialing;
-	gboolean call_active;
-
-	int sp_gain;
-	int mic_gain;
 };
 
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device);
-
 int gateway_close(struct audio_device *device);
 
-static void rfcomm_start_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	gw->rfcomm_watch_id = g_io_add_watch(gw->rfcomm,
-			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-			(GIOFunc) rfcomm_ag_data_cb, dev);
-}
-
-static void rfcomm_stop_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	g_source_remove(gw->rfcomm_watch_id);
-}
-
-static gboolean io_channel_write_all(GIOChannel *io, gchar *data,
-					gsize count)
-{
-	gsize written = 0;
-	GIOStatus status;
-
-	while (count > 0) {
-		status = g_io_channel_write_chars(io, data, count, &written,
-						NULL);
-		if (status != G_IO_STATUS_NORMAL)
-			return FALSE;
-
-		data += written;
-		count -= written;
-	}
-	return TRUE;
-}
-
-/* it's worth to mention that data and response could be the same pointers */
-static gboolean rfcomm_send_and_read(struct gateway *gw, gchar *data,
-                                    gchar *response, gsize count)
-{
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read = 0;
-	gboolean got_ok = FALSE;
-	gboolean got_error = FALSE;
-	gchar *resp_buf = response;
-	gsize toread = RFCOMM_BUF_SIZE - 1;
-	GIOStatus status;
-
-	if (!io_channel_write_all(rfcomm, data, count))
-		return FALSE;
-
-	while (!(got_ok || got_error)) {
-		status = g_io_channel_read_chars(rfcomm, resp_buf, toread,
-						&read, NULL);
-		if (status == G_IO_STATUS_NORMAL)
-			resp_buf[read] = '\0';
-		else {
-			debug("rfcomm_send_and_read(): %m");
-			return FALSE;
-		}
-		got_ok = NULL != strstr(resp_buf, OK_RESPONSE);
-		got_error = NULL != strstr(resp_buf, ERROR_RESPONSE);
-		resp_buf += read;
-		toread -= read;
-	}
-	return TRUE;
-}
-
-/* get <descr> from the names: (<descr>, (<values>)), (<descr>, (<values>))
- * ... */
-static GSList *parse_indicator_names(gchar *names, GSList *indies)
-{
-	gchar *current = names - 1;
-	GSList *result = indies;
-	gchar *next;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 2;
-		next = strstr(current, ",(");
-		ind = g_slice_new(struct indicator);
-		strncpy(ind->descr, current, 20);
-		ind->descr[(intptr_t) next - (intptr_t) current] = '\0';
-		result = g_slist_append(result, (gpointer) ind);
-		current = strstr(next + 1, ",(");
-	}
-	return result;
-}
-
-/* get values from <val0>,<val1>,... */
-static GSList *parse_indicator_values(gchar *values, GSList *indies)
-{
-	gint val;
-	gchar *current = values - 1;
-	GSList *runner = indies;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 1;
-		sscanf(current, "%d", &val);
-		current = strchr(current, ',');
-		ind = g_slist_nth_data(runner, 0);
-		ind->value = val;
-		runner = g_slist_next(runner);
-	}
-	return indies;
-}
-
-/* get values from <val0>,<val1>,... */
-static guint get_hold_mpty_features(gchar *features)
-{
-	guint result = 0;
-
-	if (strstr(features, "0"))
-		result |= AG_CHLD_0;
-
-	if (strstr(features, "1"))
-		result |= AG_CHLD_1;
-
-	if (strstr(features, "1x"))
-		result |= AG_CHLD_1x;
-
-	if (strstr(features, "2"))
-		result |= AG_CHLD_2;
-
-	if (strstr(features, "2x"))
-		result |= AG_CHLD_2x;
-
-	if (strstr(features, "3"))
-		result |= AG_CHLD_3;
-
-	if (strstr(features, "4"))
-		result |= AG_CHLD_4;
-
-	return result;
-}
-
-static gboolean establish_service_level_conn(struct gateway *gw)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	gboolean res;
-
-	debug("at the begin of establish_service_level_conn()");
-	res = rfcomm_send_and_read(gw, AG_FEATURES, buf,
-				sizeof(AG_FEATURES) - 1);
-	if (!res || sscanf(buf, "\r\n+BRSF:%d", &gw->ag_features) != 1)
-		return FALSE;
-
-	debug("features are 0x%X", gw->ag_features);
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_SUPP, buf,
-				sizeof(AG_INDICATORS_SUPP) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_names(strchr(buf, '('), NULL);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_VAL, buf,
-		sizeof(AG_INDICATORS_VAL) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_values(strchr(buf, ':') + 1, gw->indies);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_ENABLE, buf,
-				sizeof(AG_INDICATORS_ENABLE) - 1);
-	if (!res || !strstr(buf, "OK"))
-		return FALSE;
-
-	if ((gw->ag_features & AG_FEATURE_3WAY) != 0) {
-		res = rfcomm_send_and_read(gw, AG_HOLD_MPTY_SUPP, buf,
-				sizeof(AG_HOLD_MPTY_SUPP) - 1);
-		if (!res || !strstr(buf, "+CHLD:")) {
-			g_slice_free1(RFCOMM_BUF_SIZE, buf);
-			return FALSE;
-		}
-		gw->hold_multiparty_features = get_hold_mpty_features(
-							strchr(buf, '('));
-
-	} else
-		gw->hold_multiparty_features = 0;
-
-	debug("Service layer connection successfully established!");
-	rfcomm_send_and_read(gw, AG_CALLER_IDENT_ENABLE, buf,
-			sizeof(AG_CALLER_IDENT_ENABLE) - 1);
-	rfcomm_send_and_read(gw, AG_CARRIER_FORMAT, buf,
-			sizeof(AG_CARRIER_FORMAT) - 1);
-	if ((gw->ag_features & AG_FEATURE_EXTENDED_RES_CODE) != 0)
-		rfcomm_send_and_read(gw, AG_EXTENDED_RESULT_CODE, buf,
-			sizeof(AG_EXTENDED_RESULT_CODE) - 1);
-
-	return TRUE;
-}
-
-static void process_ind_change(struct audio_device *dev, guint index,
-							gint value)
-{
-	struct gateway *gw = dev->gateway;
-	struct indicator *ind = g_slist_nth_data(gw->indies, index - 1);
-	gchar *name = ind->descr;
-
-	ind->value = value;
-
-	debug("at the begin of process_ind_change, name is %s", name);
-	if (!strcmp(name, "\"call\"")) {
-		if (value > 0) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallStarted", DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-			gw->call_active = TRUE;
-		} else {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallEnded", DBUS_TYPE_INVALID);
-			gw->call_active = FALSE;
-		}
-
-	} else if (!strcmp(name, "\"callsetup\"")) {
-		if (value == 0 && gw->is_dialing) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-		} else if (!gw->is_dialing && value > 0)
-			gw->is_dialing = TRUE;
-
-	} else if (!strcmp(name, "\"callheld\"")) {
-		/* FIXME: The following code is based on assumptions only.
-		 * Has to be tested for interoperability
-		 * I assume that callheld=2 would be sent when dial from HF
-		 * failed in case of 3-way call
-		 * Unfortunately this path is not covered by the HF spec so
-		 * the code has to be tested for interop
-		*/
-		/* '2' means: all calls held, no active calls */
-		if (value == 2) {
-			if (gw->is_dialing) {
-				g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-				gw->is_dialing = FALSE;
-			}
-		}
-	} else if (!strcmp(name, "\"service\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RegistrationStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"signal\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "SignalStrength",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"roam\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RoamingStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"battchg\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "BatteryCharge",
-				DBUS_TYPE_UINT16, &value);
-}
-
-static void process_ring(struct audio_device *device, GIOChannel *chan,
-			gchar *buf)
-{
-	gchar number[AG_CALLER_NUM_SIZE];
-	gchar *cli;
-	gchar *sep;
-	gsize read;
-	GIOStatus status;
-
-	rfcomm_stop_watch(device);
-	status = g_io_channel_read_chars(chan, buf, RFCOMM_BUF_SIZE - 1, &read, NULL);
-	if (status != G_IO_STATUS_NORMAL)
-		return;
-
-	debug("at the begin of process_ring");
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 10)
-		error("process_ring(): buf is too long '%s'", buf);
-	else if ((cli = strstr(buf, "\r\n+CLIP"))) {
-		if (sscanf(cli, "\r\n+CLIP: \"%s", number) == 1) {
-			sep = strchr(number, '"');
-			sep[0] = '\0';
-
-			/* FIXME:signal will be emitted on each RING+CLIP.
-			 * That's bad */
-			cli = number;
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE, "Ring",
-					DBUS_TYPE_STRING, &cli,
-					DBUS_TYPE_INVALID);
-			device->gateway->is_dialing = TRUE;
-		} else
-			error("process_ring(): '%s' in place of +CLIP after RING", buf);
-
-	}
-
-	rfcomm_start_watch(device);
-}
-
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	struct gateway *gw;
-	gsize read;
-	/* some space for value */
-	gchar indicator[AG_INDICATOR_DESCR_SIZE + 4];
-	gint value;
-	guint index;
-	gchar *sep;
-
-	debug("at the begin of rfcomm_ag_data_cb()");
-	if (cond & G_IO_NVAL)
-		return FALSE;
-
-	gw = device->gateway;
-
-	if (cond & (G_IO_ERR | G_IO_HUP)) {
-		debug("connection with remote BT is closed");
-		gateway_close(device);
-		return FALSE;
-	}
-
-	if (g_io_channel_read_chars(chan, buf, sizeof(buf) - 1, &read, NULL)
-			!= G_IO_STATUS_NORMAL)
-		return TRUE;
-	buf[read] = '\0';
-
-	if (strlen(buf) > AG_INDICATOR_DESCR_SIZE + 14)
-		error("rfcomm_ag_data_cb(): buf is too long '%s'", buf);
-	else if (sscanf(buf, "\r\n+CIEV:%s\r\n", indicator) == 1) {
-		sep = strchr(indicator, ',');
-		sep[0] = '\0';
-		sep += 1;
-		index = atoi(indicator);
-		value = atoi(sep);
-		process_ind_change(device, index, value);
-	} else if (strstr(buf, "RING"))
-		process_ring(device, chan, buf);
-	else if (sscanf(buf, "\r\n+BVRA:%d\r\n", &value) == 1) {
-		if (value == 0)
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionActive",
-					DBUS_TYPE_INVALID);
-		else
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionInactive",
-					DBUS_TYPE_INVALID);
-	} else if (sscanf(buf, "\r\n+VGS:%d\r\n", &value) == 1) {
-		gw->sp_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "SpeakerGain",
-				DBUS_TYPE_UINT16, &value);
-	} else if (sscanf(buf, "\r\n+VGM:%d\r\n", &value) == 1) {
-		gw->mic_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "MicrophoneGain",
-				DBUS_TYPE_UINT16, &value);
-	} else
-		error("rfcomm_ag_data_cb(): read wrong data '%s'", buf);
-
-	return TRUE;
-}
-
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
 {
@@ -541,7 +118,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	DBusMessage *conn_mes = gw->connect_message;
 	gchar gw_addr[18];
 	GIOFlags flags;
 
@@ -563,29 +139,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 	if (!gw->rfcomm)
 		gw->rfcomm = g_io_channel_ref(chan);
 
-	if (establish_service_level_conn(dev->gateway)) {
-		gboolean value = TRUE;
-
-		debug("%s: Connected to %s", dev->path, gw_addr);
-		rfcomm_start_watch(dev);
-		if (conn_mes) {
-			DBusMessage *reply =
-				dbus_message_new_method_return(conn_mes);
-			dbus_connection_send(dev->conn, reply, NULL);
-			dbus_message_unref(reply);
-			dbus_message_unref(conn_mes);
-			gw->connect_message = NULL;
-		}
-
-		gw->state = GATEWAY_STATE_CONNECTED;
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN,	&value);
-		return;
-	} else
-		error("%s: Failed to establish service layer connection to %s",
-			dev->path, gw_addr);
-
 	if (NULL != gw->sco_start_cb)
 		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 
@@ -732,321 +285,20 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
-static DBusMessage *process_ag_reponse(DBusMessage *msg, gchar *response)
-{
-	DBusMessage *reply;
-
-
-	debug("in process_ag_reponse, response is %s", response);
-	if (strstr(response, OK_RESPONSE))
-		reply = dbus_message_new_method_return(msg);
-	else {
-		/* FIXME: some code should be here to processes errors
-		 *  in better fasion */
-		debug("AG responded with '%s' to %s method call", response,
-				dbus_message_get_member(msg));
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".OperationFailed",
-					"Operation failed.See log for details");
-	}
-	return reply;
-}
-
-static DBusMessage *process_simple(DBusMessage *msg, struct audio_device *dev,
-					gchar *data)
-{
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-
-	rfcomm_stop_watch(dev);
-	rfcomm_send_and_read(gw, data, buf, strlen(data));
-	rfcomm_start_watch(dev);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_ANSWER "ATA\r"
-
-static DBusMessage *ag_answer(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	if (gw->call_active)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".CallAlreadyAnswered",
-					"Call AlreadyAnswered");
-
-	return process_simple(msg, dev, AG_ANSWER);
-}
-
-#define AG_HANGUP "AT+CHUP\r"
-
-static DBusMessage *ag_terminate_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	return process_simple(msg, dev, AG_HANGUP);
-}
-
-/* according to GSM spec */
-#define ALLOWED_NUMBER_SYMBOLS "1234567890*#ABCD"
-#define AG_PLACE_CALL "ATD%s;\r"
-/* dialing from memory is not supported as headset spec doesn't define a way
- * to retreive phone memory entries.
- */
-static DBusMessage *ag_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint atd_len;
-	DBusMessage *result;
-
-	debug("at the begin of ag_call()");
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	atd_len = sprintf(buf, AG_PLACE_CALL, number);
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, buf, buf, atd_len);
-	rfcomm_start_watch(device);
-
-	result = process_ag_reponse(msg, buf);
-	return result;
-}
-
-#define AG_GET_CARRIER "AT+COPS?\r"
-
-static DBusMessage *ag_get_operator(DBusConnection *conn, DBusMessage *msg,
-					void *data)
-{
-	struct audio_device *dev = (struct audio_device *) data;
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read;
-	gchar *result, *sep;
-	DBusMessage *reply;
-	GIOStatus status;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(dev);
-	io_channel_write_all(rfcomm, AG_GET_CARRIER, strlen(AG_GET_CARRIER));
-
-	status = g_io_channel_read_chars(rfcomm, buf, RFCOMM_BUF_SIZE - 1,
-						&read, NULL);
-	rfcomm_start_watch(dev);
-	if (G_IO_STATUS_NORMAL == status) {
-		buf[read] = '\0';
-		if (strstr(buf, "+COPS")) {
-			if (!strrchr(buf, ','))
-				result = "0";
-			else {
-				result = strchr(buf, '\"') + 1;
-				sep = strchr(result, '\"');
-				sep[0] = '\0';
-			}
-
-			reply = dbus_message_new_method_return(msg);
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&result, DBUS_TYPE_INVALID);
-		} else {
-			info("ag_get_operator(): '+COPS' expected but"
-				" '%s' received", buf);
-			reply = dbus_message_new_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Unexpected response from AG");
-		}
-	} else {
-		error("ag_get_operator(): %m");
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".ConnectionFailed",
-					"Failed to receive response from AG");
-	}
-
-	return reply;
-}
-
-#define AG_SEND_DTMF "AT+VTS=%c\r"
-static DBusMessage *ag_send_dtmf(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint com_len;
-	gboolean got_ok = TRUE;
-	gint num_len;
-	gint i = 0;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	num_len = strlen(number);
-	rfcomm_stop_watch(device);
-	while (i < num_len && got_ok) {
-		com_len = sprintf(buf, AG_SEND_DTMF, number[i]);
-		rfcomm_send_and_read(gw, buf, buf, com_len);
-		got_ok = NULL != strstr(buf, OK_RESPONSE);
-		i += 1;
-	}
-	rfcomm_start_watch(device);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_GET_SUBSCRIBER_NUMS "AT+CNUM\r"
-#define CNUM_LEN 5             /* length of "+CNUM" string */
-#define MAX_NUMBER_CNT 16
-static DBusMessage *ag_get_subscriber_num(DBusConnection *conn,
-					DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number, *end;
-	DBusMessage *reply = dbus_message_new_method_return(msg);
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, AG_GET_SUBSCRIBER_NUMS, buf,
-			strlen(AG_GET_SUBSCRIBER_NUMS));
-	rfcomm_start_watch(device);
-
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 21)
-		error("ag_get_subscriber_num(): buf is too long '%s'", buf);
-	else if (strstr(buf, "+CNUM")) {
-		number = strchr(buf, ',');
-		number++;
-		end = strchr(number, ',');
-		if (end) {
-			*end = '\0';
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&number, DBUS_TYPE_INVALID);
-		}
-	} else
-		error("ag_get_subscriber_num(): read wrong data '%s'", buf);
-
-	return reply;
-}
-
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	DBusMessage *reply;
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	gboolean value;
-	guint index = 0;
-	struct indicator *ind;
-
-	reply = dbus_message_new_method_return(msg);
-	if (!reply)
-		return NULL;
-
-	dbus_message_iter_init_append(reply, &iter);
-
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
-	/* Connected */
-	value = gateway_is_connected(device);
-	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
-
-	if (!value)
-		goto done;
-
-	while ((ind = g_slist_nth_data(gw->indies, index))) {
-		if(!strcmp(ind->descr, "\"service\""))
-			dict_append_entry(&dict, "RegistrationStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"signal\""))
-			dict_append_entry(&dict, "SignalStrength",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"roam\""))
-			dict_append_entry(&dict, "RoamingStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"battchg\""))
-			dict_append_entry(&dict, "BatteryCharge",
-					DBUS_TYPE_UINT16, &ind->value);
-		index++;
-	}
-
-	/* SpeakerGain */
-	dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16,
-				&device->gateway->sp_gain);
-
-	/* MicrophoneGain */
-	dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16,
-				&device->gateway->mic_gain);
-done:
-	dbus_message_iter_close_container(&iter, &dict);
-	return reply;
+	return NULL;
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "Disconnect", "", "", ag_disconnect },
-	{ "AnswerCall", "", "", ag_answer },
-	{ "TerminateCall", "", "", ag_terminate_call },
-	{ "Call", "s", "", ag_call },
-	{ "GetOperatorName", "", "s", ag_get_operator },
-	{ "SendDTMF", "s", "", ag_send_dtmf },
-	{ "GetSubscriberNumber", "", "s", ag_get_subscriber_num },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
 	{ NULL, NULL, NULL, NULL }
 };
 
 static GDBusSignalTable gateway_signals[] = {
-	{ "Ring", "s" },
-	{ "CallTerminated", "" },
-	{ "CallStarted", "" },
-	{ "CallEnded", "" },
 	{ "PropertyChanged", "sv" },
 	{ NULL, NULL }
 };
@@ -1063,9 +315,6 @@ struct gateway *gateway_init(struct audio_device *dev)
 
 	debug("in gateway_init, dev is %p", dev);
 	gw = g_new0(struct gateway, 1);
-	gw->indies = NULL;
-	gw->is_dialing = FALSE;
-	gw->call_active = FALSE;
 	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
 
@@ -1107,11 +356,6 @@ void gateway_start_service(struct audio_device *device)
 	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
 }
 
-static void indicator_slice_free(gpointer mem)
-{
-	g_slice_free(struct indicator, mem);
-}
-
 int gateway_close(struct audio_device *device)
 {
 	struct gateway *gw = device->gateway;
@@ -1119,8 +363,6 @@ int gateway_close(struct audio_device *device)
 	GIOChannel *sco = gw->sco;
 	gboolean value = FALSE;
 
-	g_slist_foreach(gw->indies, (GFunc) indicator_slice_free, NULL);
-	g_slist_free(gw->indies);
 	if (rfcomm) {
 		g_io_channel_shutdown(rfcomm, TRUE, NULL);
 		g_io_channel_unref(rfcomm);
-- 
1.6.4.4


[-- Attachment #3: 0002-Implement-HandsfreeGateway-Interface.patch --]
[-- Type: application/octet-stream, Size: 25574 bytes --]

From ead81eacbb6fcfff6b279f18f7546cf25ca69c11 Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 8 Jan 2010 17:38:21 -0200
Subject: [PATCH 2/2] Implement HandsfreeGateway Interface

Create a interface where a Handsfree agent can register and use BlueZ to
handle the rfcomm and sco links.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for his
prototype on this code.
---
 audio/gateway.c |  592 ++++++++++++++++++++++++++++++++++++++++++++++---------
 audio/gateway.h |   12 +-
 audio/manager.c |   20 +--
 audio/unix.c    |    8 +
 4 files changed, 522 insertions(+), 110 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index 3dc09ff..8cb66ad 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -5,6 +5,8 @@
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
  *  Copyright (C) 2008-2009  Leonid Movshovich <event.riga@gmail.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
+ *  Copyright (C) 2010  Gustavo F. Padovan <padovan@profusion.mobi>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -32,12 +34,15 @@
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
 
 #include <glib.h>
 #include <dbus/dbus.h>
 #include <gdbus.h>
 
 #include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
 #include <bluetooth/sco.h>
@@ -52,19 +57,106 @@
 #include "btio.h"
 #include "dbus-common.h"
 
-#define RFCOMM_BUF_SIZE 256
+#define MAX_OPEN_TRIES 5
+#define OPEN_WAIT 300
+
+struct agent {
+	char *name;	/* Bus id */
+	char *path;	/* D-Bus path */
+	guint watch;	/* Disconnect watch */
+};
 
 struct gateway {
 	gateway_state_t state;
-	GIOChannel *rfcomm;
-	guint rfcomm_watch_id;
+	int channel;
+	char *tty;		/* gw->tty created from rfcomm */
+	GIOChannel *rfcomm;	/* remote AG requested rfcomm connection */
+	int rfcomm_id;		/* present N in /dev/rfcommN */
+	int rfcomm_fd;
 	GIOChannel *sco;
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
-	DBusMessage *connect_message;
+	struct agent *agent;
+	DBusMessage *msg;
 };
 
-int gateway_close(struct audio_device *device);
+static struct audio_device *active;	/* single active device with oFono */
+static DBusConnection *connection;
+
+static gboolean gateway_close(gpointer data);
+static int gateway_release_tty(struct audio_device *device);
+
+static const char *connected2str(int i)
+{
+	switch (i) {
+		case GATEWAY_STATE_DISCONNECTED:
+			return "disconnected";
+		case GATEWAY_STATE_CONNECTING:
+			return "connecting";
+		case GATEWAY_STATE_CONNECTED:
+			return "connected";
+		case GATEWAY_STATE_PLAYING:
+			return "playing";
+		default:
+			return "";
+	}
+}
+
+static void agent_free(struct agent *agent)
+{
+	if (!agent)
+		return;
+
+	g_free(agent->name);
+	g_free(agent->path);
+	g_free(agent);
+}
+
+static void agent_disconnect(struct agent *agent)
+{
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "Release");
+
+	dbus_message_set_no_reply(msg, TRUE);
+	g_dbus_send_message(connection, msg);
+
+	g_dbus_remove_watch(connection, agent->watch);
+}
+
+static gboolean agent_sendfd(struct agent *agent, int fd,
+		DBusPendingCallNotifyFunction notify, void *data)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "NewConnection");
+
+	dbus_message_append_args(msg, DBUS_TYPE_UNIX_FD, &fd,
+					DBUS_TYPE_INVALID);
+
+	if (dbus_connection_send_with_reply(connection, msg, &call, -1) == FALSE)
+		return FALSE;
+
+	dbus_pending_call_set_notify(call, notify, data, NULL);
+	dbus_pending_call_unref(call);
+
+	return TRUE;
+}
+
+static void change_state(struct audio_device *dev, int new_state)
+{
+	struct gateway *gw = dev->gateway;
+	const char *val = connected2str(new_state);
+
+	gw->state = new_state;
+
+	emit_property_changed(connection,dev->path,
+			AUDIO_GATEWAY_INTERFACE, "Connected",
+			DBUS_TYPE_STRING, &val);
+}
 
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
@@ -79,6 +171,7 @@ static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 		g_io_channel_shutdown(gw->sco, TRUE, NULL);
 		g_io_channel_unref(gw->sco);
 		gw->sco = NULL;
+		change_state(dev, GATEWAY_STATE_CONNECTED);
 		return FALSE;
 	}
 
@@ -92,63 +185,205 @@ static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
 
 	debug("at the begin of sco_connect_cb() in gateway.c");
 
+	gw->sco = chan;
+	g_io_channel_ref(chan);
+
 	if (err) {
 		error("sco_connect_cb(): %s", err->message);
-		/* not sure, but from other point of view,
-		 * what is the reason to have headset which
-		 * cannot play audio? */
-		if (gw->sco_start_cb)
-			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 		gateway_close(dev);
 		return;
 	}
 
-	gw->sco = g_io_channel_ref(chan);
 	if (gw->sco_start_cb)
 		gw->sco_start_cb(dev, gw->sco_start_cb_data);
 
-	/* why is this here? */
 	fcntl(g_io_channel_unix_get_fd(chan), F_SETFL, 0);
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
 				(GIOFunc) sco_io_cb, dev);
 }
 
+static void open_notify(int fd, int err, struct audio_device *dev)
+{
+	DBusMessage *reply = NULL;
+	struct gateway *gw = dev->gateway;
+	const char *result = "ok";
+
+	if (err) {
+		/* Max tries exceeded */
+		gateway_close(dev);
+
+		if (gw->msg)
+			reply = g_dbus_create_error(gw->msg,
+						ERROR_INTERFACE ".Failed",
+						strerror(err));
+	} else {
+		if (gw->msg)
+			reply = g_dbus_create_reply(gw->msg,
+						DBUS_TYPE_STRING, &result,
+						DBUS_TYPE_INVALID);
+
+		emit_property_changed(dev->conn, dev->path,
+					AUDIO_GATEWAY_INTERFACE,
+					"Device", DBUS_TYPE_STRING,
+					&gw->tty);
+
+		change_state(dev, GATEWAY_STATE_CONNECTED);
+		active = dev;
+	}
+
+	/* Reply to the requestor */
+	if (gw->msg && reply)
+		g_dbus_send_message(dev->conn, reply);
+}
+
+static void newconnection_reply(DBusPendingCall *call, void *data)
+{
+	struct gateway *gw = data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError derr;
+
+	if (!gw->rfcomm) {
+		debug("RFCOMM disconnected from server before agent reply");
+		return;
+	}
+
+	dbus_error_init(&derr);
+	if (!dbus_set_error_from_message(&derr, reply)) {
+		info("Agent reply: file descriptor passed successfuly");
+		return;
+	}
+
+	debug("Agent reply: %s", derr.message);
+
+	dbus_error_free(&derr);
+	g_idle_add(gateway_close, gw);
+}
+
+static gboolean open_continue(gpointer user_data)
+{
+	struct audio_device *dev = user_data;
+	struct gateway *gw = dev->gateway;
+	int fd;
+	static int ntries = MAX_OPEN_TRIES;
+	DBusMessage *reply;
+
+	fd = open(gw->tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
+	if (fd < 0) {
+		int err = errno;
+		error("Could not open %s: %s (%d)",
+				gw->tty, strerror(err), err);
+		if (!--ntries) {
+			/* Reporting error */
+			open_notify(fd, err, dev);
+			ntries = MAX_OPEN_TRIES;
+			return FALSE;
+		}
+		return TRUE;
+	}
+	/* Connection succeeded */
+	open_notify(fd, 0, dev);
+	gw->rfcomm_fd = fd;
+
+	if (!agent_sendfd(gw->agent, fd, newconnection_reply, gw))
+		reply = g_dbus_create_error(gw->msg, ERROR_INTERFACE ".Failed",
+				"Can not pass file descriptor");
+	else
+		reply = dbus_message_new_method_return(gw->msg);
+
+	return FALSE;
+}
+
+static int port_open(struct audio_device *dev)
+{
+	int fd;
+
+	fd = open(dev->gateway->tty, O_RDWR | O_NOCTTY | O_NONBLOCK);
+	if (fd < 0) {
+		g_timeout_add(OPEN_WAIT, open_continue, dev);
+		return -EINPROGRESS;
+	}
+
+	return fd;
+}
+
 static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 				gpointer user_data)
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	gchar gw_addr[18];
-	GIOFlags flags;
+	struct rfcomm_dev_req req;
+	DBusMessage *reply;
+	int sk, fd, id;
+	const char *err_msg;
+	char src[18], dst[18];
 
 	if (err) {
 		error("connect(): %s", err->message);
 		if (gw->sco_start_cb)
 			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
-		return;
+		goto fail;
 	}
 
-	ba2str(&dev->dst, gw_addr);
-	/* Blocking mode should be default, but just in case: */
-	flags = g_io_channel_get_flags(chan);
-	flags &= ~G_IO_FLAG_NONBLOCK;
-	flags &= G_IO_FLAG_MASK;
-	g_io_channel_set_flags(chan, flags, NULL);
-	g_io_channel_set_encoding(chan, NULL, NULL);
-	g_io_channel_set_buffered(chan, FALSE);
-	if (!gw->rfcomm)
-		gw->rfcomm = g_io_channel_ref(chan);
+	ba2str(&dev->src, src);
+	ba2str(&dev->dst, dst);
+
+	/* make /dev/rfcomm serial port from chan */
+	memset(&req, 0, sizeof(req));
+	req.dev_id = -1;
+	req.flags = (1 << RFCOMM_REUSE_DLC);
+	bacpy(&req.src, &dev->src);
+	bacpy(&req.dst, &dev->dst);
+	req.channel = gw->channel;
+
+	sk = g_io_channel_unix_get_fd(chan);
+	id = ioctl(sk, RFCOMMCREATEDEV, &req);
+	if (id < 0) {
+		err_msg = strerror(errno);
+		error("ioctl(RFCOMMCREATEDEV) failed: %s (%d)",
+				strerror(errno), errno);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		g_io_channel_unref(chan);
+		chan = NULL;
+		goto fail;
+	}
 
-	if (NULL != gw->sco_start_cb)
-		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
+	gw->rfcomm_id = id;
+	gw->tty = g_strdup_printf("/dev/rfcomm%d", id);
 
-	gateway_close(dev);
+	g_io_channel_shutdown(chan, TRUE, NULL);
+
+	/* Addressing connect port */
+	fd = port_open(dev);
+	if (fd < 0)
+		/* Open in progress: Wait the callback */
+		return;
+
+	open_notify(fd, 0, dev);
+	gw->rfcomm_fd = fd;
+
+	if (!agent_sendfd(gw->agent, fd, newconnection_reply, gw)) {
+		reply = g_dbus_create_error(gw->msg, ERROR_INTERFACE ".Failed",
+				"Can not pass file descriptor");
+	} else
+		reply = dbus_message_new_method_return(gw->msg);
+
+	return;
+
+fail:
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".Failed", ".Connection attempt failed");
+		dbus_message_unref(gw->msg);
+		gw->msg = NULL;
+	}
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
+	return;
 }
 
 static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 {
 	struct audio_device *dev = user_data;
-	DBusMessage *msg = dev->gateway->connect_message;
+	struct gateway *gw = dev->gateway;
 	int ch = -1;
 	sdp_list_t *protos, *classes;
 	uuid_t uuid;
@@ -182,8 +417,6 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 
 	if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
 			uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
-		sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free,
-									NULL);
 		sdp_list_free(protos, NULL);
 		error("Invalid service record or not HFP");
 		goto fail;
@@ -197,6 +430,8 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 		goto fail;
 	}
 
+	gw->channel = ch;
+
 	io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -204,25 +439,26 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 				BT_IO_OPT_INVALID);
 	if (!io) {
 		error("Unable to connect: %s", err->message);
-		if (msg) {
-			error_common_reply(dev->conn, msg, ERROR_INTERFACE
-						".ConnectionAttemptFailed",
-						err->message);
-			msg = NULL;
-		}
-		g_error_free(err);
+		if (err)
+			g_error_free(err);
 		gateway_close(dev);
+		goto fail;
 	}
 
 	g_io_channel_unref(io);
+
+	change_state(dev, GATEWAY_STATE_CONNECTING);
 	return;
 
 fail:
-	if (msg)
-		error_common_reply(dev->conn, msg, ERROR_INTERFACE
-					".NotSupported", "Not supported");
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".NotSupported", "Not supported");
+		dbus_message_unref(gw->msg);
+		gw->msg = NULL;
+	}
 
-	dev->gateway->connect_message = NULL;
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
 
 	sco_cb = dev->gateway->sco_start_cb;
 	if (sco_cb)
@@ -245,22 +481,93 @@ static DBusMessage *ag_connect(DBusConnection *conn, DBusMessage *msg,
 	struct gateway *gw = au_dev->gateway;
 
 	debug("at the begin of ag_connect()");
-	if (gw->rfcomm)
+
+	if (gw->tty)
 		return g_dbus_create_error(msg, ERROR_INTERFACE
 					".AlreadyConnected",
 					"Already Connected");
 
-	gw->connect_message = dbus_message_ref(msg);
+	if (!gw->agent)
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+				".Failed", "Agent not assined");
+
+	gw->msg = dbus_message_ref(msg);
 	if (get_records(au_dev) < 0) {
-		dbus_message_unref(gw->connect_message);
+		dbus_message_unref(gw->msg);
 		return g_dbus_create_error(msg, ERROR_INTERFACE
 					".ConnectAttemptFailed",
 					"Connect Attempt Failed");
 	}
 	debug("at the end of ag_connect()");
+
 	return NULL;
 }
 
+static int gateway_release_tty(struct audio_device *device)
+{
+	struct gateway *gw = device->gateway;
+	struct rfcomm_dev_req req;
+	int rfcomm_ctl;
+	int err = 0;
+
+	rfcomm_ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM);
+	if (rfcomm_ctl < 0)
+		return -errno;
+
+	if (gw->rfcomm_fd) {
+		close(gw->rfcomm_fd);
+		gw->rfcomm_fd = -1;
+	}
+
+	memset(&req, 0, sizeof(req));
+	req.dev_id = gw->rfcomm_id;
+
+	req.flags = (1 << RFCOMM_HANGUP_NOW);
+
+	if (ioctl(rfcomm_ctl, RFCOMMRELEASEDEV, &req) < 0) {
+		err = errno;
+		error("Can't release device %s: %s (%d)",
+				gw->tty, strerror(err), err);
+	}
+	close(rfcomm_ctl);
+	gw->rfcomm_id = -1;
+	return 0;
+}
+
+static gboolean gateway_close(gpointer data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	gboolean value = FALSE;
+
+	if (gw->tty) {
+		gateway_release_tty(device);
+		g_free(gw->tty);
+		gw->tty = NULL;
+	}
+
+	if (gw->rfcomm) {
+		g_io_channel_shutdown(gw->rfcomm, TRUE, NULL);
+		g_io_channel_unref(gw->rfcomm);
+		gw->rfcomm = NULL;
+	}
+
+	if (gw->sco) {
+		g_io_channel_shutdown(gw->sco, TRUE, NULL);
+		g_io_channel_unref(gw->sco);
+		gw->sco = NULL;
+		gw->sco_start_cb = NULL;
+		gw->sco_start_cb_data = NULL;
+	}
+
+	gw->state = GATEWAY_STATE_DISCONNECTED;
+
+	emit_property_changed(device->conn, device->path,
+				AUDIO_GATEWAY_INTERFACE,
+				"Connected", DBUS_TYPE_BOOLEAN, &value);
+	return FALSE;
+}
+
 static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
@@ -269,11 +576,14 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	DBusMessage *reply = NULL;
 	char gw_addr[18];
 
+	if (!connection)
+		return NULL;
+
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
 		return NULL;
 
-	if (!gw->rfcomm)
+	if (!gw->tty && !gw->rfcomm)
 		return g_dbus_create_error(msg, ERROR_INTERFACE
 						".NotConnected",
 						"Device not Connected");
@@ -285,16 +595,117 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
+static void agent_exited(DBusConnection *conn, void *data)
+{
+	struct gateway *gateway = data;
+	struct agent *agent = gateway->agent;
+
+	debug("Agent %s exited", agent->name);
+
+	agent_free(agent);
+	gateway->agent = NULL;
+}
+
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	return NULL;
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	const char *value;
+
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	value = connected2str(gw->state);
+	dict_append_entry(&dict, "Connected",
+			DBUS_TYPE_STRING, &value);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct agent *agent;
+	const char *path, *name;
+
+
+	if (gw->agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".AlreadyExists",
+				"Agent already exists");
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+			DBUS_TYPE_INVALID))
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+				".InvalidArguments", "Invalid argument");
+
+	name = dbus_message_get_sender(msg);
+	agent = g_new0(struct agent, 1);
+	if (!agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed",
+				"Failed to create a new agent");
+
+	agent->name = strdup(name);
+	agent->path = strdup(path);
+
+	agent->watch = g_dbus_add_disconnect_watch(conn, name,
+			agent_exited, gw, NULL);
+
+	gw->agent = agent;
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+				DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct agent *agent;
+
+	if (!gw->agent)
+		goto done;
+
+	agent = gw->agent;
+	if (strcmp(agent->name, dbus_message_get_sender(msg)) != 0)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed", "Permission denied");
+
+	agent_disconnect(gw->agent);
+
+	g_dbus_remove_watch(conn, agent->watch);
+
+	agent_free(agent);
+	gw->agent = NULL;
+
+done:
+	return dbus_message_new_method_return(msg);
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
-	{ "Disconnect", "", "", ag_disconnect },
+	{ "Disconnect", "", "", ag_disconnect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
+	{ "RegisterAgent", "o", "", register_agent },
+	{ "UnregisterAgent", "o", "", unregister_agent },
 	{ NULL, NULL, NULL, NULL }
 };
 
@@ -307,17 +718,23 @@ struct gateway *gateway_init(struct audio_device *dev)
 {
 	struct gateway *gw;
 
+	gw = g_new0(struct gateway, 1);
+
+	connection = dbus_connection_ref(dev->conn);
+
 	if (!g_dbus_register_interface(dev->conn, dev->path,
 					AUDIO_GATEWAY_INTERFACE,
 					gateway_methods, gateway_signals,
 					NULL, dev, NULL))
 		return NULL;
 
-	debug("in gateway_init, dev is %p", dev);
-	gw = g_new0(struct gateway, 1);
-	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
+}
 
+void gateway_exit()
+{
+	dbus_connection_unref(connection);
+	connection = NULL;
 }
 
 gboolean gateway_is_connected(struct audio_device *dev)
@@ -326,17 +743,6 @@ gboolean gateway_is_connected(struct audio_device *dev)
 			dev->gateway->state == GATEWAY_STATE_CONNECTED);
 }
 
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
-{
-	if (!io)
-		return -EINVAL;
-
-	g_io_channel_ref(io);
-	dev->gateway->rfcomm = io;
-
-	return 0;
-}
-
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 {
 	struct gateway *gw = dev->gateway;
@@ -347,42 +753,39 @@ int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 	gw->sco = g_io_channel_ref(io);
 
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                                (GIOFunc) sco_io_cb, dev);
+				 (GIOFunc) sco_io_cb, dev);
+
+	change_state(dev, GATEWAY_STATE_PLAYING);
+
 	return 0;
 }
 
-void gateway_start_service(struct audio_device *device)
+int gateway_connect_rfcomm(struct audio_device *dev,
+		GIOChannel *chan, int ch)
 {
-	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
+	if (!chan)
+		return -EINVAL;
+
+	g_io_channel_ref(chan);
+	dev->gateway->rfcomm = chan;
+	dev->gateway->channel = ch;
+
+	return 0;
 }
 
-int gateway_close(struct audio_device *device)
+void gateway_start_service(struct audio_device *dev)
 {
-	struct gateway *gw = device->gateway;
-	GIOChannel *rfcomm = gw->rfcomm;
-	GIOChannel *sco = gw->sco;
-	gboolean value = FALSE;
+	struct gateway *gw = dev->gateway;
+	GError *err = NULL;
 
-	if (rfcomm) {
-		g_io_channel_shutdown(rfcomm, TRUE, NULL);
-		g_io_channel_unref(rfcomm);
-		gw->rfcomm = NULL;
-	}
+	if (gw->rfcomm == NULL)
+		return;
 
-	if (sco) {
-		g_io_channel_shutdown(sco, TRUE, NULL);
-		g_io_channel_unref(sco);
-		gw->sco = NULL;
-		gw->sco_start_cb = NULL;
-		gw->sco_start_cb_data = NULL;
+	if (!bt_io_accept(gw->rfcomm, rfcomm_connect_cb, dev, NULL,
+							&err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
 	}
-
-	gw->state = GATEWAY_STATE_DISCONNECTED;
-
-	emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN, &value);
-	return 0;
 }
 
 /* These are functions to be called from unix.c for audio system
@@ -394,13 +797,17 @@ gboolean gateway_request_stream(struct audio_device *dev,
 	GError *err = NULL;
 	GIOChannel *io;
 
-	if (!gw->rfcomm) {
+	if (!gw->tty && !gw->rfcomm) {
 		gw->sco_start_cb = cb;
 		gw->sco_start_cb_data = user_data;
 		get_records(dev);
 	} else if (!gw->sco) {
+		char source[128], destination[128];
+
 		gw->sco_start_cb = cb;
 		gw->sco_start_cb_data = user_data;
+		ba2str(&dev->src, source);
+		ba2str(&dev->dst, destination);
 		io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -418,12 +825,12 @@ gboolean gateway_request_stream(struct audio_device *dev,
 	return TRUE;
 }
 
-int gateway_config_stream(struct audio_device *dev, gateway_stream_cb_t sco_cb,
-				void *user_data)
+int gateway_config_stream(struct audio_device *dev,
+				gateway_stream_cb_t sco_cb, void *user_data)
 {
 	struct gateway *gw = dev->gateway;
 
-	if (!gw->rfcomm) {
+	if (!gw->rfcomm && !gw->tty) {
 		gw->sco_start_cb = sco_cb;
 		gw->sco_start_cb_data = user_data;
 		return get_records(dev);
@@ -464,4 +871,3 @@ void gateway_suspend_stream(struct audio_device *dev)
 	gw->sco_start_cb = NULL;
 	gw->sco_start_cb_data = NULL;
 }
-
diff --git a/audio/gateway.h b/audio/gateway.h
index 3b0457f..eaf6801 100644
--- a/audio/gateway.h
+++ b/audio/gateway.h
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -22,20 +23,25 @@
  *
  */
 
-#define AUDIO_GATEWAY_INTERFACE "org.bluez.HeadsetGateway"
+#define AUDIO_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
 
 #define DEFAULT_HSP_HS_CHANNEL 6
 #define DEFAULT_HFP_HS_CHANNEL 7
 
 typedef enum {
 	GATEWAY_STATE_DISCONNECTED,
-	GATEWAY_STATE_CONNECTED
+	GATEWAY_STATE_CONNECTED,
+	GATEWAY_STATE_CONNECTING,
+	GATEWAY_STATE_PLAYING,
 } gateway_state_t;
 
 typedef void (*gateway_stream_cb_t) (struct audio_device *dev, void *user_data);
+
 struct gateway *gateway_init(struct audio_device *device);
+void gateway_exit();
 gboolean gateway_is_connected(struct audio_device *dev);
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *chan);
+int gateway_connect_rfcomm(struct audio_device *dev,
+			GIOChannel *chan, int ch);
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *chan);
 void gateway_start_service(struct audio_device *device);
 gboolean gateway_request_stream(struct audio_device *dev,
diff --git a/audio/manager.c b/audio/manager.c
index 413c1f3..e768014 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -198,7 +198,7 @@ static void handle_uuid(const char *uuidstr, struct audio_device *device)
 		break;
 	case HANDSFREE_AGW_SVCLASS_ID:
 		debug("Found Handsfree AG record");
-		if (device->gateway == NULL)
+		if (enabled.gateway && (device->gateway == NULL))
 			device->gateway = gateway_init(device);
 		break;
 	case AUDIO_SINK_SVCLASS_ID:
@@ -586,7 +586,7 @@ static void hf_io_cb(GIOChannel *chan, gpointer data)
 		goto drop;
 	}
 
-	if (gateway_connect_rfcomm(device, chan) < 0) {
+	if (gateway_connect_rfcomm(device, chan, ch) < 0) {
 		error("Allocating new GIOChannel failed!");
 		goto drop;
 	}
@@ -905,22 +905,12 @@ static void headset_server_remove(struct btd_adapter *adapter)
 static int gateway_server_probe(struct btd_adapter *adapter)
 {
 	struct audio_adapter *adp;
-	const gchar *path = adapter_get_path(adapter);
-	int ret;
-
-	DBG("path %s", path);
 
 	adp = audio_adapter_get(adapter);
 	if (!adp)
 		return -EINVAL;
 
-	ret = gateway_server_init(adp);
-	if (ret < 0) {
-		audio_adapter_ref(adp);
-		return ret;
-	}
-
-	return 0;
+	return gateway_server_init(adp);
 }
 
 static void gateway_server_remove(struct btd_adapter *adapter)
@@ -1166,8 +1156,10 @@ void audio_manager_exit(void)
 		telephony_exit();
 	}
 
-	if (enabled.gateway)
+	if (enabled.gateway) {
 		btd_unregister_adapter_driver(&gateway_server_driver);
+		gateway_exit();
+	}
 
 	if (enabled.source || enabled.sink)
 		btd_unregister_adapter_driver(&a2dp_server_driver);
diff --git a/audio/unix.c b/audio/unix.c
index 5cf4f94..bd1a415 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -395,6 +395,9 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	struct bt_start_stream_rsp *rsp = (void *) buf;
 	struct bt_new_stream_ind *ind = (void *) buf;
 
+	if (!dev)
+		goto failed;
+
 	memset(buf, 0, sizeof(buf));
 	rsp->h.type = BT_RESPONSE;
 	rsp->h.name = BT_START_STREAM;
@@ -416,6 +419,11 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	}
 
 	client->req_id = 0;
+	return;
+
+failed:
+	error("gateway_resume_complete: resume failed");
+	unix_ipc_error(client, BT_START_STREAM, EIO);
 }
 
 static void headset_suspend_complete(struct audio_device *dev, void *user_data)
-- 
1.6.4.4


[-- Attachment #4: 0001-Add-HFP-support-in-oFono.patch --]
[-- Type: application/octet-stream, Size: 13342 bytes --]

From fdc16a31547438d8b91f5521185f8ef5f5cf43fc Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 8 Jan 2010 17:25:43 -0200
Subject: [PATCH] Add HFP support in oFono

It uses BlueZ through to get HFP working following the
org.bluez.HandsfreeGateway and org.bluez.HandsfreeAgent from the BlueZ
D-Bus API.

You need the HFP patch into BlueZ too.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for its prototype
on this code.
---
 gatchat/gattty.c    |   23 ++++
 gatchat/gattty.h    |    1 +
 include/modem.h     |    2 +
 plugins/hfp.c       |  316 +++++++++++++++++++++++++++++++++++++++++++++++++--
 plugins/modemconf.c |    3 +-
 src/modem.c         |   15 +++
 6 files changed, 348 insertions(+), 12 deletions(-)

diff --git a/gatchat/gattty.c b/gatchat/gattty.c
index 7d665e1..b25675b 100644
--- a/gatchat/gattty.c
+++ b/gatchat/gattty.c
@@ -260,3 +260,26 @@ GIOChannel *g_at_tty_open(const char *tty, GHashTable *options)
 
 	return channel;
 }
+
+GIOChannel *g_at_get_channel_from_fd(int fd)
+{
+	GIOChannel *channel;
+	struct termios ti;
+
+	/* Switch TTY to raw mode */
+	memset(&ti, 0, sizeof(ti));
+	cfmakeraw(&ti);
+	tcflush(fd, TCIOFLUSH);
+	tcsetattr(fd, TCSANOW, &ti);
+
+	channel = g_io_channel_unix_new(fd);
+
+	if (channel == NULL) {
+		close(fd);
+		return NULL;
+	}
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+
+	return channel;
+}
diff --git a/gatchat/gattty.h b/gatchat/gattty.h
index c8e0c8f..5fff05d 100644
--- a/gatchat/gattty.h
+++ b/gatchat/gattty.h
@@ -43,6 +43,7 @@ extern "C" {
  */
 GIOChannel *g_at_tty_open(const char *tty, GHashTable *options);
 
+GIOChannel *g_at_get_channel_from_fd(int fd);
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/modem.h b/include/modem.h
index f718b9c..a375b12 100644
--- a/include/modem.h
+++ b/include/modem.h
@@ -47,6 +47,8 @@ struct ofono_modem *ofono_modem_create(const char *type);
 int ofono_modem_register(struct ofono_modem *modem);
 void ofono_modem_remove(struct ofono_modem *modem);
 
+struct ofono_modem *ofono_modem_get_by_path(const char *path);
+
 void ofono_modem_set_powered(struct ofono_modem *modem, ofono_bool_t powered);
 ofono_bool_t ofono_modem_get_powered(struct ofono_modem *modem);
 
diff --git a/plugins/hfp.c b/plugins/hfp.c
index b56abdb..dfa84fb 100644
--- a/plugins/hfp.c
+++ b/plugins/hfp.c
@@ -30,6 +30,8 @@
 #include <glib.h>
 #include <gatchat.h>
 #include <gattty.h>
+#include <gdbus.h>
+#include <ofono.h>
 
 #define OFONO_API_SUBJECT_TO_CHANGE
 #include <ofono/plugin.h>
@@ -52,11 +54,28 @@
 
 #include <drivers/hfpmodem/hfpmodem.h>
 
+#include <ofono/dbus.h>
+
+#define BLUEZ_SERVICE "org.bluez"
+#define BLUEZ_PATH "/"
+#define BLUEZ_MANAGER_INTERFACE "org.bluez.Manager"
+#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter"
+#define BLUEZ_DEVICE_INTERFACE "org.bluez.Device"
+#define BLUEZ_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
+
+#define HFP_AGENT_INTERFACE "org.bluez.HandsfreeAgent"
+
 static const char *brsf_prefix[] = { "+BRSF:", NULL };
 static const char *cind_prefix[] = { "+CIND:", NULL };
 static const char *cmer_prefix[] = { "+CMER:", NULL };
 static const char *chld_prefix[] = { "+CHLD:", NULL };
 
+static DBusConnection *connection;
+static char *ofono_handsfree_path;
+
+
+static int timeout;
+
 static int hfp_disable(struct ofono_modem *modem);
 
 static void hfp_debug(const char *str, void *user_data)
@@ -135,6 +154,60 @@ static void cmer_cb(gboolean ok, GAtResult *result, gpointer user_data)
 		sevice_level_conn_established(modem);
 }
 
+static int send_method_call(const char *dest, const char *path,
+				const char *interface, const char *method,
+				DBusPendingCallNotifyFunction cb,
+				void *user_data, int type, ...)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+	va_list args;
+
+	msg = dbus_message_new_method_call(dest, path, interface, method);
+	if (!msg) {
+		ofono_error("Unable to allocate new D-Bus %s message", method);
+		return -ENOMEM;
+	}
+
+	va_start(args, type);
+
+	if (!dbus_message_append_args_valist(msg, type, args)) {
+		dbus_message_unref(msg);
+		va_end(args);
+		return -EIO;
+	}
+
+	va_end(args);
+
+	if (!cb) {
+		g_dbus_send_message(connection, msg);
+		return 0;
+	}
+
+	if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+		ofono_error("Sending %s failed", method);
+		dbus_message_unref(msg);
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(call, cb, user_data, NULL);
+	dbus_pending_call_unref(call);
+	dbus_message_unref(msg);
+
+	return 0;
+}
+
+static gboolean hfp_enable_timeout(gpointer user)
+{
+	struct ofono_modem *modem = user;
+
+	if (ofono_modem_get_powered(modem))
+		return FALSE;
+
+	hfp_disable(modem);
+	return FALSE;
+}
+
 static void cind_status_cb(gboolean ok, GAtResult *result,
 				gpointer user_data)
 {
@@ -260,8 +333,7 @@ error:
 }
 
 /* either oFono or Phone could request SLC connection */
-static int service_level_connection(struct ofono_modem *modem,
-				const char *tty)
+static int service_level_connection(struct ofono_modem *modem, int fd)
 {
 	struct hfp_data *data = ofono_modem_get_data(modem);
 	GIOChannel *io;
@@ -269,7 +341,7 @@ static int service_level_connection(struct ofono_modem *modem,
 	GAtChat *chat;
 	char buf[64];
 
-	io = g_at_tty_open(tty, NULL);
+	io = g_at_get_channel_from_fd(fd);
 	if (!io) {
 		ofono_error("Service level connection failed: %s (%d)",
 			strerror(errno), errno);
@@ -296,9 +368,173 @@ static int service_level_connection(struct ofono_modem *modem,
 	return -EINPROGRESS;
 }
 
+static DBusMessage *hfp_agent_new_connection(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	int fd;
+	const char *path;
+	struct ofono_modem *modem = NULL;
+
+	path = dbus_message_get_path(msg);
+	modem = ofono_modem_get_by_path(path);
+
+	if (!modem)
+		return NULL;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UNIX_FD, &fd,
+				DBUS_TYPE_INVALID))
+		return __ofono_error_invalid_args(msg);
+
+	service_level_connection(modem, fd);
+
+	return NULL;
+}
+
+static DBusMessage *hfp_agent_release(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	return NULL;
+}
+
+static GDBusMethodTable agent_methods[] = {
+        { "NewConnection", "h", "", hfp_agent_new_connection,
+		G_DBUS_METHOD_FLAG_ASYNC },
+	{ "Release", "", "", hfp_agent_release },
+	{NULL, NULL, NULL, NULL}
+};
+
+static void agent_registered_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusMessage *reply;
+
+	reply = dbus_pending_call_steal_reply(call);
+	dbus_message_unref(reply);
+}
+
+static void agent_unregistered_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusMessage *reply;
+
+	reply = dbus_pending_call_steal_reply(call);
+	dbus_message_unref(reply);
+}
+
+static void find_device_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	struct ofono_modem *modem = user_data;
+	const char *device, *obj_path;
+	int ret;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH,
+				&device, DBUS_TYPE_INVALID) == FALSE) {
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("aha! %s", err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	ofono_debug("Using device %s", device);
+	ofono_handsfree_path = g_strdup(device);
+
+	obj_path = ofono_modem_get_path(modem);
+
+	ret = send_method_call(BLUEZ_SERVICE, device,
+				BLUEZ_GATEWAY_INTERFACE, "RegisterAgent",
+				agent_registered_cb, NULL, DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+
+	if (ret < 0)
+		ofono_error("agent_register failed(%d)", ret);
+
+done:
+	dbus_message_unref(reply);;
+}
+
+static void get_adapter_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	struct ofono_modem *modem = user_data;
+	const char *adapter = NULL;
+	const char *address;
+	int ret;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+	if (dbus_message_get_args(reply, &err,
+				DBUS_TYPE_OBJECT_PATH,
+				&adapter, DBUS_TYPE_INVALID) == FALSE) {
+		if (adapter == NULL) {
+			ofono_error("bluetooth adapter is not enabled");
+			dbus_error_free(&err);
+			goto done;
+		}
+
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s %s", adapter, err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	address = ofono_modem_get_string(modem, "Address");
+
+	DBG("address %s", address);
+
+	if (address == NULL)
+		return;
+
+	ret = send_method_call(BLUEZ_SERVICE, adapter,
+				BLUEZ_ADAPTER_INTERFACE, "FindDevice",
+				find_device_cb, modem,
+				DBUS_TYPE_STRING, &address,
+				DBUS_TYPE_INVALID);
+
+	if (ret < 0)
+		ofono_error("find_device failed(%d)", ret);
+
+done:
+	dbus_message_unref(reply);
+
+}
+
+static int hfp_register_ofono_handsfree(struct ofono_modem *modem)
+{
+	ofono_debug("Register oFono Agent to bluetooth daemon");
+
+	return send_method_call(BLUEZ_SERVICE, BLUEZ_PATH,
+				BLUEZ_MANAGER_INTERFACE, "DefaultAdapter",
+				get_adapter_cb, modem,
+				DBUS_TYPE_INVALID);
+}
+
+static int hfp_unregister_ofono_handsfree(struct ofono_modem *modem)
+{
+	const char *obj_path = ofono_modem_get_path(modem);
+
+	ofono_debug("Unregister oFono Agent to bluetooth daemon");
+
+	if (!ofono_handsfree_path)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, ofono_handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "UnregisterAgent",
+				agent_unregistered_cb, NULL,
+				DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+}
+
 static int hfp_probe(struct ofono_modem *modem)
 {
 	struct hfp_data *data;
+	const char *obj_path;
+	DBusConnection *conn = ofono_dbus_get_connection();
 
 	data = g_try_new0(struct hfp_data, 1);
 	if (!data)
@@ -312,6 +548,15 @@ static int hfp_probe(struct ofono_modem *modem)
 
 	ofono_modem_set_data(modem, data);
 
+	connection = dbus_connection_ref(conn);
+
+	obj_path = ofono_modem_get_path(modem);
+	g_dbus_register_interface(conn, obj_path, HFP_AGENT_INTERFACE,
+			agent_methods, NULL, NULL, data, NULL);
+
+	if (hfp_register_ofono_handsfree(modem) != 0)
+		return -EINVAL;
+
 	return 0;
 }
 
@@ -322,24 +567,74 @@ static void hfp_remove(struct ofono_modem *modem)
 	if (data)
 		g_free(data);
 
+	hfp_unregister_ofono_handsfree(modem);
+
+	if (ofono_handsfree_path)
+		g_free(ofono_handsfree_path);
+
+	dbus_connection_unref(connection);
+
 	ofono_modem_set_data(modem, NULL);
 }
 
+static void port_connect_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	const char *msg;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_STRING,
+				&msg, DBUS_TYPE_INVALID) == FALSE) {
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s", err.message);
+			dbus_error_free(&err);
+			goto done;
+		}
+	}
+
+	if (strcmp(msg, "ok"))
+		ofono_error("Connect failed: %s", msg);
+done:
+	dbus_message_unref(reply);
+}
+
+static int hfp_connect_ofono_handsfree()
+{
+	ofono_debug("Connect to bluetooth daemon");
+
+	if (!ofono_handsfree_path || !connection)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, ofono_handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Connect",
+				port_connect_cb, NULL, DBUS_TYPE_INVALID);
+}
+
 /* power up hardware */
 static int hfp_enable(struct ofono_modem *modem)
 {
-	const char *tty;
-	int ret;
-
 	DBG("%p", modem);
 
-	tty = ofono_modem_get_string(modem, "Device");
-	if (tty == NULL)
+	timeout = g_timeout_add_seconds(10, hfp_enable_timeout, modem);
+
+	if (hfp_connect_ofono_handsfree() < 0)
 		return -EINVAL;
 
-	ret = service_level_connection(modem, tty);
+	return -EINPROGRESS;
+}
+
+static int hfp_disconnect_ofono_handsfree()
+{
+	if (!ofono_handsfree_path || !connection)
+		return -EINVAL;
 
-	return ret;
+	return send_method_call(BLUEZ_SERVICE, ofono_handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Disconnect",
+				NULL, NULL, DBUS_TYPE_INVALID);
 }
 
 static int hfp_disable(struct ofono_modem *modem)
@@ -359,6 +654,7 @@ static int hfp_disable(struct ofono_modem *modem)
 
 	ofono_modem_set_powered(modem, FALSE);
 
+	hfp_disconnect_ofono_handsfree();
 	return 0;
 }
 
diff --git a/plugins/modemconf.c b/plugins/modemconf.c
index 882fa5a..8fdeff0 100644
--- a/plugins/modemconf.c
+++ b/plugins/modemconf.c
@@ -124,12 +124,11 @@ static struct ofono_modem *create_modem(GKeyFile *keyfile, const char *group)
 
 	modem = ofono_modem_create(driver);
 
-	if (!g_strcmp0(driver, "phonesim"))
+	if (!g_strcmp0(driver, "phonesim") || !g_strcmp0(driver, "hfp"))
 		set_address(modem, keyfile, group);
 
 	if (!g_strcmp0(driver, "atgen") || !g_strcmp0(driver, "g1") ||
 						!g_strcmp0(driver, "calypso") ||
-						!g_strcmp0(driver, "hfp") ||
 						!g_strcmp0(driver, "palmpre"))
 		set_device(modem, keyfile, group);
 
diff --git a/src/modem.c b/src/modem.c
index 557fe6e..ff413df 100644
--- a/src/modem.c
+++ b/src/modem.c
@@ -1067,6 +1067,21 @@ struct ofono_modem *ofono_modem_create(const char *type)
 	return modem;
 }
 
+struct ofono_modem *ofono_modem_get_by_path(const char *path)
+{
+	GSList *l;
+	struct ofono_modem *modem;
+
+	for (l = g_modem_list; l; l = l->next) {
+		modem = l->data;
+
+		if (!g_strcmp0(modem->path, path))
+			return modem;
+	}
+
+	return NULL;
+}
+
 static void emit_modems()
 {
 	DBusConnection *conn = ofono_dbus_get_connection();
-- 
1.6.4.4


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

* Re: [RFC] HFP support into oFono and BlueZ
  2010-01-11 17:08 ` Gustavo F. Padovan
@ 2010-01-11 19:05   ` Gustavo F. Padovan
  -1 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-11 19:05 UTC (permalink / raw)
  To: linux-bluetooth, ofono, zhenhua.zhang

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

On Mon, Jan 11, 2010 at 3:08 PM, Gustavo F. Padovan
<padovan@profusion.mobi> wrote:
> Hi,
>
> These patches implement the new API for the Audio Gateway in BlueZ. It
> follows the last version of the HandsfreeGateway and HandsfreeAgent
> Intefaces API.
> The first two patches is for BlueZ and the other for oFono. You can
> test it with using enable-modem and test-voicecall scripts into the
> test dir of oFono.
> Feel free to test it and send me your comments. We have some bugs yet.

New version of the patches: now we do not handle tty stuff on the BlueZ side.

>
> The audio part is not working yet. We are going to work on pulseaudio
> this week to get this done soon.
>
> Regards,
>
> --
> Gustavo F. Padovan
> ProFUSION embedded systems - http://profusion.mobi
>



-- 
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi

[-- Attachment #2: 0001-clean-up-audio-gateway.c.patch --]
[-- Type: application/octet-stream, Size: 24620 bytes --]

From 7eea257a7b4a0cbc026aab6a60df9bb580065831 Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 6 Nov 2009 18:08:58 -0200
Subject: [PATCH 1/2] clean up audio/gateway.c

remove all code related to the AT engine
---
 audio/gateway.c |  760 +------------------------------------------------------
 1 files changed, 1 insertions(+), 759 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index a1c1ea2..3dc09ff 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -53,52 +53,6 @@
 #include "dbus-common.h"
 
 #define RFCOMM_BUF_SIZE 256
-/* not-more-then-16 defined by GSM + 1 for NULL + padding */
-#define AG_INDICATOR_DESCR_SIZE 20
-#define AG_CALLER_NUM_SIZE 64	/* size of number + type */
-
-/* commands */
-#define AG_FEATURES "AT+BRSF=26\r"     /* = 0x7F = All features supported */
-#define AG_INDICATORS_SUPP "AT+CIND=?\r"
-#define AG_INDICATORS_VAL "AT+CIND?\r"
-#define AG_INDICATORS_ENABLE "AT+CMER=3,0,0,1\r"
-#define AG_HOLD_MPTY_SUPP "AT+CHLD=?\r"
-#define AG_CALLER_IDENT_ENABLE "AT+CLIP=1\r"
-#define AG_CARRIER_FORMAT "AT+COPS=3,0\r"
-#define AG_EXTENDED_RESULT_CODE "AT+CMEE=1\r"
-
-#define AG_FEATURE_3WAY 0x1
-#define AG_FEATURE_EXTENDED_RES_CODE 0x100
-/* Hold and multipary AG features.
- * Comments below are copied from hands-free spec for reference */
-/* Releases all held calls or sets User Determined User Busy (UDUB)
- * for a waiting call */
-#define AG_CHLD_0 0x01
-/* Releases all active calls (if any exist) and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_1 0x02
-/* Releases specified active call only <x> */
-#define AG_CHLD_1x 0x04
-/* Places all active calls (if any exist) on hold and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_2 0x08
-/* Request private consultation mode with specified call <x> (Place all
- * calls on hold EXCEPT the call <x>) */
-#define AG_CHLD_2x 0x10
-/* Adds a held call to the conversation */
-#define AG_CHLD_3 0x20
-/* Connects the two calls and disconnects the subscriber from both calls
- * (Explicit Call Transfer). Support for this value and its associated
- * functionality is optional for the HF. */
-#define AG_CHLD_4 0x40
-
-#define OK_RESPONSE "\r\nOK\r\n"
-#define ERROR_RESPONSE "\r\nERROR\r\n"
-
-struct indicator {
-	gchar descr[AG_INDICATOR_DESCR_SIZE];
-	gint value;
-};
 
 struct gateway {
 	gateway_state_t state;
@@ -108,387 +62,10 @@ struct gateway {
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
 	DBusMessage *connect_message;
-	guint ag_features;
-	guint hold_multiparty_features;
-	GSList *indies;
-	gboolean is_dialing;
-	gboolean call_active;
-
-	int sp_gain;
-	int mic_gain;
 };
 
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device);
-
 int gateway_close(struct audio_device *device);
 
-static void rfcomm_start_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	gw->rfcomm_watch_id = g_io_add_watch(gw->rfcomm,
-			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-			(GIOFunc) rfcomm_ag_data_cb, dev);
-}
-
-static void rfcomm_stop_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	g_source_remove(gw->rfcomm_watch_id);
-}
-
-static gboolean io_channel_write_all(GIOChannel *io, gchar *data,
-					gsize count)
-{
-	gsize written = 0;
-	GIOStatus status;
-
-	while (count > 0) {
-		status = g_io_channel_write_chars(io, data, count, &written,
-						NULL);
-		if (status != G_IO_STATUS_NORMAL)
-			return FALSE;
-
-		data += written;
-		count -= written;
-	}
-	return TRUE;
-}
-
-/* it's worth to mention that data and response could be the same pointers */
-static gboolean rfcomm_send_and_read(struct gateway *gw, gchar *data,
-                                    gchar *response, gsize count)
-{
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read = 0;
-	gboolean got_ok = FALSE;
-	gboolean got_error = FALSE;
-	gchar *resp_buf = response;
-	gsize toread = RFCOMM_BUF_SIZE - 1;
-	GIOStatus status;
-
-	if (!io_channel_write_all(rfcomm, data, count))
-		return FALSE;
-
-	while (!(got_ok || got_error)) {
-		status = g_io_channel_read_chars(rfcomm, resp_buf, toread,
-						&read, NULL);
-		if (status == G_IO_STATUS_NORMAL)
-			resp_buf[read] = '\0';
-		else {
-			debug("rfcomm_send_and_read(): %m");
-			return FALSE;
-		}
-		got_ok = NULL != strstr(resp_buf, OK_RESPONSE);
-		got_error = NULL != strstr(resp_buf, ERROR_RESPONSE);
-		resp_buf += read;
-		toread -= read;
-	}
-	return TRUE;
-}
-
-/* get <descr> from the names: (<descr>, (<values>)), (<descr>, (<values>))
- * ... */
-static GSList *parse_indicator_names(gchar *names, GSList *indies)
-{
-	gchar *current = names - 1;
-	GSList *result = indies;
-	gchar *next;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 2;
-		next = strstr(current, ",(");
-		ind = g_slice_new(struct indicator);
-		strncpy(ind->descr, current, 20);
-		ind->descr[(intptr_t) next - (intptr_t) current] = '\0';
-		result = g_slist_append(result, (gpointer) ind);
-		current = strstr(next + 1, ",(");
-	}
-	return result;
-}
-
-/* get values from <val0>,<val1>,... */
-static GSList *parse_indicator_values(gchar *values, GSList *indies)
-{
-	gint val;
-	gchar *current = values - 1;
-	GSList *runner = indies;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 1;
-		sscanf(current, "%d", &val);
-		current = strchr(current, ',');
-		ind = g_slist_nth_data(runner, 0);
-		ind->value = val;
-		runner = g_slist_next(runner);
-	}
-	return indies;
-}
-
-/* get values from <val0>,<val1>,... */
-static guint get_hold_mpty_features(gchar *features)
-{
-	guint result = 0;
-
-	if (strstr(features, "0"))
-		result |= AG_CHLD_0;
-
-	if (strstr(features, "1"))
-		result |= AG_CHLD_1;
-
-	if (strstr(features, "1x"))
-		result |= AG_CHLD_1x;
-
-	if (strstr(features, "2"))
-		result |= AG_CHLD_2;
-
-	if (strstr(features, "2x"))
-		result |= AG_CHLD_2x;
-
-	if (strstr(features, "3"))
-		result |= AG_CHLD_3;
-
-	if (strstr(features, "4"))
-		result |= AG_CHLD_4;
-
-	return result;
-}
-
-static gboolean establish_service_level_conn(struct gateway *gw)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	gboolean res;
-
-	debug("at the begin of establish_service_level_conn()");
-	res = rfcomm_send_and_read(gw, AG_FEATURES, buf,
-				sizeof(AG_FEATURES) - 1);
-	if (!res || sscanf(buf, "\r\n+BRSF:%d", &gw->ag_features) != 1)
-		return FALSE;
-
-	debug("features are 0x%X", gw->ag_features);
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_SUPP, buf,
-				sizeof(AG_INDICATORS_SUPP) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_names(strchr(buf, '('), NULL);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_VAL, buf,
-		sizeof(AG_INDICATORS_VAL) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_values(strchr(buf, ':') + 1, gw->indies);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_ENABLE, buf,
-				sizeof(AG_INDICATORS_ENABLE) - 1);
-	if (!res || !strstr(buf, "OK"))
-		return FALSE;
-
-	if ((gw->ag_features & AG_FEATURE_3WAY) != 0) {
-		res = rfcomm_send_and_read(gw, AG_HOLD_MPTY_SUPP, buf,
-				sizeof(AG_HOLD_MPTY_SUPP) - 1);
-		if (!res || !strstr(buf, "+CHLD:")) {
-			g_slice_free1(RFCOMM_BUF_SIZE, buf);
-			return FALSE;
-		}
-		gw->hold_multiparty_features = get_hold_mpty_features(
-							strchr(buf, '('));
-
-	} else
-		gw->hold_multiparty_features = 0;
-
-	debug("Service layer connection successfully established!");
-	rfcomm_send_and_read(gw, AG_CALLER_IDENT_ENABLE, buf,
-			sizeof(AG_CALLER_IDENT_ENABLE) - 1);
-	rfcomm_send_and_read(gw, AG_CARRIER_FORMAT, buf,
-			sizeof(AG_CARRIER_FORMAT) - 1);
-	if ((gw->ag_features & AG_FEATURE_EXTENDED_RES_CODE) != 0)
-		rfcomm_send_and_read(gw, AG_EXTENDED_RESULT_CODE, buf,
-			sizeof(AG_EXTENDED_RESULT_CODE) - 1);
-
-	return TRUE;
-}
-
-static void process_ind_change(struct audio_device *dev, guint index,
-							gint value)
-{
-	struct gateway *gw = dev->gateway;
-	struct indicator *ind = g_slist_nth_data(gw->indies, index - 1);
-	gchar *name = ind->descr;
-
-	ind->value = value;
-
-	debug("at the begin of process_ind_change, name is %s", name);
-	if (!strcmp(name, "\"call\"")) {
-		if (value > 0) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallStarted", DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-			gw->call_active = TRUE;
-		} else {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallEnded", DBUS_TYPE_INVALID);
-			gw->call_active = FALSE;
-		}
-
-	} else if (!strcmp(name, "\"callsetup\"")) {
-		if (value == 0 && gw->is_dialing) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-		} else if (!gw->is_dialing && value > 0)
-			gw->is_dialing = TRUE;
-
-	} else if (!strcmp(name, "\"callheld\"")) {
-		/* FIXME: The following code is based on assumptions only.
-		 * Has to be tested for interoperability
-		 * I assume that callheld=2 would be sent when dial from HF
-		 * failed in case of 3-way call
-		 * Unfortunately this path is not covered by the HF spec so
-		 * the code has to be tested for interop
-		*/
-		/* '2' means: all calls held, no active calls */
-		if (value == 2) {
-			if (gw->is_dialing) {
-				g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-				gw->is_dialing = FALSE;
-			}
-		}
-	} else if (!strcmp(name, "\"service\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RegistrationStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"signal\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "SignalStrength",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"roam\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RoamingStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"battchg\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "BatteryCharge",
-				DBUS_TYPE_UINT16, &value);
-}
-
-static void process_ring(struct audio_device *device, GIOChannel *chan,
-			gchar *buf)
-{
-	gchar number[AG_CALLER_NUM_SIZE];
-	gchar *cli;
-	gchar *sep;
-	gsize read;
-	GIOStatus status;
-
-	rfcomm_stop_watch(device);
-	status = g_io_channel_read_chars(chan, buf, RFCOMM_BUF_SIZE - 1, &read, NULL);
-	if (status != G_IO_STATUS_NORMAL)
-		return;
-
-	debug("at the begin of process_ring");
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 10)
-		error("process_ring(): buf is too long '%s'", buf);
-	else if ((cli = strstr(buf, "\r\n+CLIP"))) {
-		if (sscanf(cli, "\r\n+CLIP: \"%s", number) == 1) {
-			sep = strchr(number, '"');
-			sep[0] = '\0';
-
-			/* FIXME:signal will be emitted on each RING+CLIP.
-			 * That's bad */
-			cli = number;
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE, "Ring",
-					DBUS_TYPE_STRING, &cli,
-					DBUS_TYPE_INVALID);
-			device->gateway->is_dialing = TRUE;
-		} else
-			error("process_ring(): '%s' in place of +CLIP after RING", buf);
-
-	}
-
-	rfcomm_start_watch(device);
-}
-
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	struct gateway *gw;
-	gsize read;
-	/* some space for value */
-	gchar indicator[AG_INDICATOR_DESCR_SIZE + 4];
-	gint value;
-	guint index;
-	gchar *sep;
-
-	debug("at the begin of rfcomm_ag_data_cb()");
-	if (cond & G_IO_NVAL)
-		return FALSE;
-
-	gw = device->gateway;
-
-	if (cond & (G_IO_ERR | G_IO_HUP)) {
-		debug("connection with remote BT is closed");
-		gateway_close(device);
-		return FALSE;
-	}
-
-	if (g_io_channel_read_chars(chan, buf, sizeof(buf) - 1, &read, NULL)
-			!= G_IO_STATUS_NORMAL)
-		return TRUE;
-	buf[read] = '\0';
-
-	if (strlen(buf) > AG_INDICATOR_DESCR_SIZE + 14)
-		error("rfcomm_ag_data_cb(): buf is too long '%s'", buf);
-	else if (sscanf(buf, "\r\n+CIEV:%s\r\n", indicator) == 1) {
-		sep = strchr(indicator, ',');
-		sep[0] = '\0';
-		sep += 1;
-		index = atoi(indicator);
-		value = atoi(sep);
-		process_ind_change(device, index, value);
-	} else if (strstr(buf, "RING"))
-		process_ring(device, chan, buf);
-	else if (sscanf(buf, "\r\n+BVRA:%d\r\n", &value) == 1) {
-		if (value == 0)
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionActive",
-					DBUS_TYPE_INVALID);
-		else
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionInactive",
-					DBUS_TYPE_INVALID);
-	} else if (sscanf(buf, "\r\n+VGS:%d\r\n", &value) == 1) {
-		gw->sp_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "SpeakerGain",
-				DBUS_TYPE_UINT16, &value);
-	} else if (sscanf(buf, "\r\n+VGM:%d\r\n", &value) == 1) {
-		gw->mic_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "MicrophoneGain",
-				DBUS_TYPE_UINT16, &value);
-	} else
-		error("rfcomm_ag_data_cb(): read wrong data '%s'", buf);
-
-	return TRUE;
-}
-
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
 {
@@ -541,7 +118,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	DBusMessage *conn_mes = gw->connect_message;
 	gchar gw_addr[18];
 	GIOFlags flags;
 
@@ -563,29 +139,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 	if (!gw->rfcomm)
 		gw->rfcomm = g_io_channel_ref(chan);
 
-	if (establish_service_level_conn(dev->gateway)) {
-		gboolean value = TRUE;
-
-		debug("%s: Connected to %s", dev->path, gw_addr);
-		rfcomm_start_watch(dev);
-		if (conn_mes) {
-			DBusMessage *reply =
-				dbus_message_new_method_return(conn_mes);
-			dbus_connection_send(dev->conn, reply, NULL);
-			dbus_message_unref(reply);
-			dbus_message_unref(conn_mes);
-			gw->connect_message = NULL;
-		}
-
-		gw->state = GATEWAY_STATE_CONNECTED;
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN,	&value);
-		return;
-	} else
-		error("%s: Failed to establish service layer connection to %s",
-			dev->path, gw_addr);
-
 	if (NULL != gw->sco_start_cb)
 		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 
@@ -732,321 +285,20 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
-static DBusMessage *process_ag_reponse(DBusMessage *msg, gchar *response)
-{
-	DBusMessage *reply;
-
-
-	debug("in process_ag_reponse, response is %s", response);
-	if (strstr(response, OK_RESPONSE))
-		reply = dbus_message_new_method_return(msg);
-	else {
-		/* FIXME: some code should be here to processes errors
-		 *  in better fasion */
-		debug("AG responded with '%s' to %s method call", response,
-				dbus_message_get_member(msg));
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".OperationFailed",
-					"Operation failed.See log for details");
-	}
-	return reply;
-}
-
-static DBusMessage *process_simple(DBusMessage *msg, struct audio_device *dev,
-					gchar *data)
-{
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-
-	rfcomm_stop_watch(dev);
-	rfcomm_send_and_read(gw, data, buf, strlen(data));
-	rfcomm_start_watch(dev);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_ANSWER "ATA\r"
-
-static DBusMessage *ag_answer(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	if (gw->call_active)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".CallAlreadyAnswered",
-					"Call AlreadyAnswered");
-
-	return process_simple(msg, dev, AG_ANSWER);
-}
-
-#define AG_HANGUP "AT+CHUP\r"
-
-static DBusMessage *ag_terminate_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	return process_simple(msg, dev, AG_HANGUP);
-}
-
-/* according to GSM spec */
-#define ALLOWED_NUMBER_SYMBOLS "1234567890*#ABCD"
-#define AG_PLACE_CALL "ATD%s;\r"
-/* dialing from memory is not supported as headset spec doesn't define a way
- * to retreive phone memory entries.
- */
-static DBusMessage *ag_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint atd_len;
-	DBusMessage *result;
-
-	debug("at the begin of ag_call()");
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	atd_len = sprintf(buf, AG_PLACE_CALL, number);
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, buf, buf, atd_len);
-	rfcomm_start_watch(device);
-
-	result = process_ag_reponse(msg, buf);
-	return result;
-}
-
-#define AG_GET_CARRIER "AT+COPS?\r"
-
-static DBusMessage *ag_get_operator(DBusConnection *conn, DBusMessage *msg,
-					void *data)
-{
-	struct audio_device *dev = (struct audio_device *) data;
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read;
-	gchar *result, *sep;
-	DBusMessage *reply;
-	GIOStatus status;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(dev);
-	io_channel_write_all(rfcomm, AG_GET_CARRIER, strlen(AG_GET_CARRIER));
-
-	status = g_io_channel_read_chars(rfcomm, buf, RFCOMM_BUF_SIZE - 1,
-						&read, NULL);
-	rfcomm_start_watch(dev);
-	if (G_IO_STATUS_NORMAL == status) {
-		buf[read] = '\0';
-		if (strstr(buf, "+COPS")) {
-			if (!strrchr(buf, ','))
-				result = "0";
-			else {
-				result = strchr(buf, '\"') + 1;
-				sep = strchr(result, '\"');
-				sep[0] = '\0';
-			}
-
-			reply = dbus_message_new_method_return(msg);
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&result, DBUS_TYPE_INVALID);
-		} else {
-			info("ag_get_operator(): '+COPS' expected but"
-				" '%s' received", buf);
-			reply = dbus_message_new_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Unexpected response from AG");
-		}
-	} else {
-		error("ag_get_operator(): %m");
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".ConnectionFailed",
-					"Failed to receive response from AG");
-	}
-
-	return reply;
-}
-
-#define AG_SEND_DTMF "AT+VTS=%c\r"
-static DBusMessage *ag_send_dtmf(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint com_len;
-	gboolean got_ok = TRUE;
-	gint num_len;
-	gint i = 0;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	num_len = strlen(number);
-	rfcomm_stop_watch(device);
-	while (i < num_len && got_ok) {
-		com_len = sprintf(buf, AG_SEND_DTMF, number[i]);
-		rfcomm_send_and_read(gw, buf, buf, com_len);
-		got_ok = NULL != strstr(buf, OK_RESPONSE);
-		i += 1;
-	}
-	rfcomm_start_watch(device);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_GET_SUBSCRIBER_NUMS "AT+CNUM\r"
-#define CNUM_LEN 5             /* length of "+CNUM" string */
-#define MAX_NUMBER_CNT 16
-static DBusMessage *ag_get_subscriber_num(DBusConnection *conn,
-					DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number, *end;
-	DBusMessage *reply = dbus_message_new_method_return(msg);
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, AG_GET_SUBSCRIBER_NUMS, buf,
-			strlen(AG_GET_SUBSCRIBER_NUMS));
-	rfcomm_start_watch(device);
-
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 21)
-		error("ag_get_subscriber_num(): buf is too long '%s'", buf);
-	else if (strstr(buf, "+CNUM")) {
-		number = strchr(buf, ',');
-		number++;
-		end = strchr(number, ',');
-		if (end) {
-			*end = '\0';
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&number, DBUS_TYPE_INVALID);
-		}
-	} else
-		error("ag_get_subscriber_num(): read wrong data '%s'", buf);
-
-	return reply;
-}
-
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	DBusMessage *reply;
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	gboolean value;
-	guint index = 0;
-	struct indicator *ind;
-
-	reply = dbus_message_new_method_return(msg);
-	if (!reply)
-		return NULL;
-
-	dbus_message_iter_init_append(reply, &iter);
-
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
-	/* Connected */
-	value = gateway_is_connected(device);
-	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
-
-	if (!value)
-		goto done;
-
-	while ((ind = g_slist_nth_data(gw->indies, index))) {
-		if(!strcmp(ind->descr, "\"service\""))
-			dict_append_entry(&dict, "RegistrationStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"signal\""))
-			dict_append_entry(&dict, "SignalStrength",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"roam\""))
-			dict_append_entry(&dict, "RoamingStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"battchg\""))
-			dict_append_entry(&dict, "BatteryCharge",
-					DBUS_TYPE_UINT16, &ind->value);
-		index++;
-	}
-
-	/* SpeakerGain */
-	dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16,
-				&device->gateway->sp_gain);
-
-	/* MicrophoneGain */
-	dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16,
-				&device->gateway->mic_gain);
-done:
-	dbus_message_iter_close_container(&iter, &dict);
-	return reply;
+	return NULL;
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "Disconnect", "", "", ag_disconnect },
-	{ "AnswerCall", "", "", ag_answer },
-	{ "TerminateCall", "", "", ag_terminate_call },
-	{ "Call", "s", "", ag_call },
-	{ "GetOperatorName", "", "s", ag_get_operator },
-	{ "SendDTMF", "s", "", ag_send_dtmf },
-	{ "GetSubscriberNumber", "", "s", ag_get_subscriber_num },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
 	{ NULL, NULL, NULL, NULL }
 };
 
 static GDBusSignalTable gateway_signals[] = {
-	{ "Ring", "s" },
-	{ "CallTerminated", "" },
-	{ "CallStarted", "" },
-	{ "CallEnded", "" },
 	{ "PropertyChanged", "sv" },
 	{ NULL, NULL }
 };
@@ -1063,9 +315,6 @@ struct gateway *gateway_init(struct audio_device *dev)
 
 	debug("in gateway_init, dev is %p", dev);
 	gw = g_new0(struct gateway, 1);
-	gw->indies = NULL;
-	gw->is_dialing = FALSE;
-	gw->call_active = FALSE;
 	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
 
@@ -1107,11 +356,6 @@ void gateway_start_service(struct audio_device *device)
 	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
 }
 
-static void indicator_slice_free(gpointer mem)
-{
-	g_slice_free(struct indicator, mem);
-}
-
 int gateway_close(struct audio_device *device)
 {
 	struct gateway *gw = device->gateway;
@@ -1119,8 +363,6 @@ int gateway_close(struct audio_device *device)
 	GIOChannel *sco = gw->sco;
 	gboolean value = FALSE;
 
-	g_slist_foreach(gw->indies, (GFunc) indicator_slice_free, NULL);
-	g_slist_free(gw->indies);
 	if (rfcomm) {
 		g_io_channel_shutdown(rfcomm, TRUE, NULL);
 		g_io_channel_unref(rfcomm);
-- 
1.6.4.4


[-- Attachment #3: 0002-Implement-HandsfreeGateway-Interface.patch --]
[-- Type: application/octet-stream, Size: 21795 bytes --]

From 56757bb836d58caecd9fd6366e3bb1b70e778841 Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 8 Jan 2010 17:38:21 -0200
Subject: [PATCH 2/2] Implement HandsfreeGateway Interface

Create a interface where a Handsfree agent can register and use BlueZ to
handle the rfcomm and sco links.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for his
prototype on this code.
---
 audio/gateway.c |  447 +++++++++++++++++++++++++++++++++++++++++++------------
 audio/gateway.h |   12 +-
 audio/manager.c |   20 +--
 audio/unix.c    |    8 +
 4 files changed, 377 insertions(+), 110 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index 3dc09ff..7e517b5 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -5,6 +5,8 @@
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
  *  Copyright (C) 2008-2009  Leonid Movshovich <event.riga@gmail.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
+ *  Copyright (C) 2010  Gustavo F. Padovan <padovan@profusion.mobi>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -32,12 +34,15 @@
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
 
 #include <glib.h>
 #include <dbus/dbus.h>
 #include <gdbus.h>
 
 #include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
 #include <bluetooth/sco.h>
@@ -52,19 +57,101 @@
 #include "btio.h"
 #include "dbus-common.h"
 
-#define RFCOMM_BUF_SIZE 256
+#define MAX_OPEN_TRIES 5
+#define OPEN_WAIT 300
+
+struct agent {
+	char *name;	/* Bus id */
+	char *path;	/* D-Bus path */
+	guint watch;	/* Disconnect watch */
+};
 
 struct gateway {
 	gateway_state_t state;
-	GIOChannel *rfcomm;
-	guint rfcomm_watch_id;
+	int channel;
+	GIOChannel *rfcomm;	/* remote AG requested rfcomm connection */
 	GIOChannel *sco;
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
-	DBusMessage *connect_message;
+	struct agent *agent;
+	DBusMessage *msg;
 };
 
-int gateway_close(struct audio_device *device);
+static DBusConnection *connection;
+
+static gboolean gateway_close(gpointer data);
+
+static const char *connected2str(int i)
+{
+	switch (i) {
+		case GATEWAY_STATE_DISCONNECTED:
+			return "disconnected";
+		case GATEWAY_STATE_CONNECTING:
+			return "connecting";
+		case GATEWAY_STATE_CONNECTED:
+			return "connected";
+		case GATEWAY_STATE_PLAYING:
+			return "playing";
+		default:
+			return "";
+	}
+}
+
+static void agent_free(struct agent *agent)
+{
+	if (!agent)
+		return;
+
+	g_free(agent->name);
+	g_free(agent->path);
+	g_free(agent);
+}
+
+static void agent_disconnect(struct agent *agent)
+{
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "Release");
+
+	dbus_message_set_no_reply(msg, TRUE);
+	g_dbus_send_message(connection, msg);
+
+	g_dbus_remove_watch(connection, agent->watch);
+}
+
+static gboolean agent_sendfd(struct agent *agent, int fd,
+		DBusPendingCallNotifyFunction notify, void *data)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "NewConnection");
+
+	dbus_message_append_args(msg, DBUS_TYPE_UNIX_FD, &fd,
+					DBUS_TYPE_INVALID);
+
+	if (dbus_connection_send_with_reply(connection, msg, &call, -1) == FALSE)
+		return FALSE;
+
+	dbus_pending_call_set_notify(call, notify, data, NULL);
+	dbus_pending_call_unref(call);
+
+	return TRUE;
+}
+
+static void change_state(struct audio_device *dev, int new_state)
+{
+	struct gateway *gw = dev->gateway;
+	const char *val = connected2str(new_state);
+
+	gw->state = new_state;
+
+	emit_property_changed(connection,dev->path,
+			AUDIO_GATEWAY_INTERFACE, "Connected",
+			DBUS_TYPE_STRING, &val);
+}
 
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
@@ -79,6 +166,7 @@ static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 		g_io_channel_shutdown(gw->sco, TRUE, NULL);
 		g_io_channel_unref(gw->sco);
 		gw->sco = NULL;
+		change_state(dev, GATEWAY_STATE_CONNECTED);
 		return FALSE;
 	}
 
@@ -92,63 +180,101 @@ static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
 
 	debug("at the begin of sco_connect_cb() in gateway.c");
 
+	gw->sco = chan;
+	g_io_channel_ref(chan);
+
 	if (err) {
 		error("sco_connect_cb(): %s", err->message);
-		/* not sure, but from other point of view,
-		 * what is the reason to have headset which
-		 * cannot play audio? */
-		if (gw->sco_start_cb)
-			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 		gateway_close(dev);
 		return;
 	}
 
-	gw->sco = g_io_channel_ref(chan);
 	if (gw->sco_start_cb)
 		gw->sco_start_cb(dev, gw->sco_start_cb_data);
 
-	/* why is this here? */
 	fcntl(g_io_channel_unix_get_fd(chan), F_SETFL, 0);
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
 				(GIOFunc) sco_io_cb, dev);
 }
 
+static void newconnection_reply(DBusPendingCall *call, void *data)
+{
+	struct gateway *gw = data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError derr;
+
+	if (!gw->rfcomm) {
+		debug("RFCOMM disconnected from server before agent reply");
+		return;
+	}
+
+	dbus_error_init(&derr);
+	if (!dbus_set_error_from_message(&derr, reply)) {
+		info("Agent reply: file descriptor passed successfuly");
+		return;
+	}
+
+	debug("Agent reply: %s", derr.message);
+
+	dbus_error_free(&derr);
+	g_idle_add(gateway_close, gw);
+}
+
 static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 				gpointer user_data)
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	gchar gw_addr[18];
-	GIOFlags flags;
+	struct rfcomm_dev_req req;
+	DBusMessage *reply;
+	int sk;
+	char src[18], dst[18];
 
 	if (err) {
 		error("connect(): %s", err->message);
 		if (gw->sco_start_cb)
 			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
-		return;
+		goto fail;
 	}
 
-	ba2str(&dev->dst, gw_addr);
-	/* Blocking mode should be default, but just in case: */
-	flags = g_io_channel_get_flags(chan);
-	flags &= ~G_IO_FLAG_NONBLOCK;
-	flags &= G_IO_FLAG_MASK;
-	g_io_channel_set_flags(chan, flags, NULL);
-	g_io_channel_set_encoding(chan, NULL, NULL);
-	g_io_channel_set_buffered(chan, FALSE);
-	if (!gw->rfcomm)
-		gw->rfcomm = g_io_channel_ref(chan);
+	ba2str(&dev->src, src);
+	ba2str(&dev->dst, dst);
 
-	if (NULL != gw->sco_start_cb)
-		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
+	/* make /dev/rfcomm serial port from chan */
+	memset(&req, 0, sizeof(req));
+	req.dev_id = -1;
+	req.flags = (1 << RFCOMM_REUSE_DLC);
+	bacpy(&req.src, &dev->src);
+	bacpy(&req.dst, &dev->dst);
+	req.channel = gw->channel;
 
-	gateway_close(dev);
+	sk = g_io_channel_unix_get_fd(chan);
+
+	change_state(dev, GATEWAY_STATE_CONNECTED);
+
+	if (!agent_sendfd(gw->agent, sk, newconnection_reply, gw)) {
+		reply = g_dbus_create_error(gw->msg, ERROR_INTERFACE ".Failed",
+				"Can not pass file descriptor");
+	} else
+		reply = dbus_message_new_method_return(gw->msg);
+
+	return;
+
+fail:
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".Failed", ".Connection attempt failed");
+		dbus_message_unref(gw->msg);
+		gw->msg = NULL;
+	}
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
+	return;
 }
 
 static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 {
 	struct audio_device *dev = user_data;
-	DBusMessage *msg = dev->gateway->connect_message;
+	struct gateway *gw = dev->gateway;
 	int ch = -1;
 	sdp_list_t *protos, *classes;
 	uuid_t uuid;
@@ -182,8 +308,6 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 
 	if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
 			uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
-		sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free,
-									NULL);
 		sdp_list_free(protos, NULL);
 		error("Invalid service record or not HFP");
 		goto fail;
@@ -197,6 +321,8 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 		goto fail;
 	}
 
+	gw->channel = ch;
+
 	io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -204,25 +330,26 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 				BT_IO_OPT_INVALID);
 	if (!io) {
 		error("Unable to connect: %s", err->message);
-		if (msg) {
-			error_common_reply(dev->conn, msg, ERROR_INTERFACE
-						".ConnectionAttemptFailed",
-						err->message);
-			msg = NULL;
-		}
-		g_error_free(err);
+		if (err)
+			g_error_free(err);
 		gateway_close(dev);
+		goto fail;
 	}
 
 	g_io_channel_unref(io);
+
+	change_state(dev, GATEWAY_STATE_CONNECTING);
 	return;
 
 fail:
-	if (msg)
-		error_common_reply(dev->conn, msg, ERROR_INTERFACE
-					".NotSupported", "Not supported");
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".NotSupported", "Not supported");
+		dbus_message_unref(gw->msg);
+		gw->msg = NULL;
+	}
 
-	dev->gateway->connect_message = NULL;
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
 
 	sco_cb = dev->gateway->sco_start_cb;
 	if (sco_cb)
@@ -243,22 +370,57 @@ static DBusMessage *ag_connect(DBusConnection *conn, DBusMessage *msg,
 {
 	struct audio_device *au_dev = (struct audio_device *) data;
 	struct gateway *gw = au_dev->gateway;
+	DBusMessage *reply;
 
 	debug("at the begin of ag_connect()");
-	if (gw->rfcomm)
+
+	if (!gw->agent)
 		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".AlreadyConnected",
-					"Already Connected");
+				".Failed", "Agent not assined");
 
-	gw->connect_message = dbus_message_ref(msg);
+	gw->msg = dbus_message_ref(msg);
 	if (get_records(au_dev) < 0) {
-		dbus_message_unref(gw->connect_message);
+		dbus_message_unref(gw->msg);
 		return g_dbus_create_error(msg, ERROR_INTERFACE
 					".ConnectAttemptFailed",
 					"Connect Attempt Failed");
 	}
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
 	debug("at the end of ag_connect()");
-	return NULL;
+
+	return reply;
+}
+
+static gboolean gateway_close(gpointer data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	gboolean value = FALSE;
+
+	if (gw->rfcomm) {
+		g_io_channel_shutdown(gw->rfcomm, TRUE, NULL);
+		g_io_channel_unref(gw->rfcomm);
+		gw->rfcomm = NULL;
+	}
+
+	if (gw->sco) {
+		g_io_channel_shutdown(gw->sco, TRUE, NULL);
+		g_io_channel_unref(gw->sco);
+		gw->sco = NULL;
+		gw->sco_start_cb = NULL;
+		gw->sco_start_cb_data = NULL;
+	}
+
+	gw->state = GATEWAY_STATE_DISCONNECTED;
+
+	emit_property_changed(device->conn, device->path,
+				AUDIO_GATEWAY_INTERFACE,
+				"Connected", DBUS_TYPE_BOOLEAN, &value);
+	return FALSE;
 }
 
 static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
@@ -269,6 +431,9 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	DBusMessage *reply = NULL;
 	char gw_addr[18];
 
+	if (!connection)
+		return NULL;
+
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
 		return NULL;
@@ -285,16 +450,117 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
+static void agent_exited(DBusConnection *conn, void *data)
+{
+	struct gateway *gateway = data;
+	struct agent *agent = gateway->agent;
+
+	debug("Agent %s exited", agent->name);
+
+	agent_free(agent);
+	gateway->agent = NULL;
+}
+
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	return NULL;
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	const char *value;
+
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	value = connected2str(gw->state);
+	dict_append_entry(&dict, "Connected",
+			DBUS_TYPE_STRING, &value);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct agent *agent;
+	const char *path, *name;
+
+
+	if (gw->agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".AlreadyExists",
+				"Agent already exists");
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+			DBUS_TYPE_INVALID))
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+				".InvalidArguments", "Invalid argument");
+
+	name = dbus_message_get_sender(msg);
+	agent = g_new0(struct agent, 1);
+	if (!agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed",
+				"Failed to create a new agent");
+
+	agent->name = strdup(name);
+	agent->path = strdup(path);
+
+	agent->watch = g_dbus_add_disconnect_watch(conn, name,
+			agent_exited, gw, NULL);
+
+	gw->agent = agent;
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+				DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct agent *agent;
+
+	if (!gw->agent)
+		goto done;
+
+	agent = gw->agent;
+	if (strcmp(agent->name, dbus_message_get_sender(msg)) != 0)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed", "Permission denied");
+
+	agent_disconnect(gw->agent);
+
+	g_dbus_remove_watch(conn, agent->watch);
+
+	agent_free(agent);
+	gw->agent = NULL;
+
+done:
+	return dbus_message_new_method_return(msg);
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
-	{ "Disconnect", "", "", ag_disconnect },
+	{ "Disconnect", "", "", ag_disconnect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
+	{ "RegisterAgent", "o", "", register_agent },
+	{ "UnregisterAgent", "o", "", unregister_agent },
 	{ NULL, NULL, NULL, NULL }
 };
 
@@ -307,17 +573,23 @@ struct gateway *gateway_init(struct audio_device *dev)
 {
 	struct gateway *gw;
 
+	gw = g_new0(struct gateway, 1);
+
+	connection = dbus_connection_ref(dev->conn);
+
 	if (!g_dbus_register_interface(dev->conn, dev->path,
 					AUDIO_GATEWAY_INTERFACE,
 					gateway_methods, gateway_signals,
 					NULL, dev, NULL))
 		return NULL;
 
-	debug("in gateway_init, dev is %p", dev);
-	gw = g_new0(struct gateway, 1);
-	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
+}
 
+void gateway_exit()
+{
+	dbus_connection_unref(connection);
+	connection = NULL;
 }
 
 gboolean gateway_is_connected(struct audio_device *dev)
@@ -326,17 +598,6 @@ gboolean gateway_is_connected(struct audio_device *dev)
 			dev->gateway->state == GATEWAY_STATE_CONNECTED);
 }
 
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
-{
-	if (!io)
-		return -EINVAL;
-
-	g_io_channel_ref(io);
-	dev->gateway->rfcomm = io;
-
-	return 0;
-}
-
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 {
 	struct gateway *gw = dev->gateway;
@@ -347,42 +608,39 @@ int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 	gw->sco = g_io_channel_ref(io);
 
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                                (GIOFunc) sco_io_cb, dev);
+				 (GIOFunc) sco_io_cb, dev);
+
+	change_state(dev, GATEWAY_STATE_PLAYING);
+
 	return 0;
 }
 
-void gateway_start_service(struct audio_device *device)
+int gateway_connect_rfcomm(struct audio_device *dev,
+		GIOChannel *chan, int ch)
 {
-	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
+	if (!chan)
+		return -EINVAL;
+
+	g_io_channel_ref(chan);
+	dev->gateway->rfcomm = chan;
+	dev->gateway->channel = ch;
+
+	return 0;
 }
 
-int gateway_close(struct audio_device *device)
+void gateway_start_service(struct audio_device *dev)
 {
-	struct gateway *gw = device->gateway;
-	GIOChannel *rfcomm = gw->rfcomm;
-	GIOChannel *sco = gw->sco;
-	gboolean value = FALSE;
+	struct gateway *gw = dev->gateway;
+	GError *err = NULL;
 
-	if (rfcomm) {
-		g_io_channel_shutdown(rfcomm, TRUE, NULL);
-		g_io_channel_unref(rfcomm);
-		gw->rfcomm = NULL;
-	}
+	if (gw->rfcomm == NULL)
+		return;
 
-	if (sco) {
-		g_io_channel_shutdown(sco, TRUE, NULL);
-		g_io_channel_unref(sco);
-		gw->sco = NULL;
-		gw->sco_start_cb = NULL;
-		gw->sco_start_cb_data = NULL;
+	if (!bt_io_accept(gw->rfcomm, rfcomm_connect_cb, dev, NULL,
+							&err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
 	}
-
-	gw->state = GATEWAY_STATE_DISCONNECTED;
-
-	emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN, &value);
-	return 0;
 }
 
 /* These are functions to be called from unix.c for audio system
@@ -399,8 +657,12 @@ gboolean gateway_request_stream(struct audio_device *dev,
 		gw->sco_start_cb_data = user_data;
 		get_records(dev);
 	} else if (!gw->sco) {
+		char source[128], destination[128];
+
 		gw->sco_start_cb = cb;
 		gw->sco_start_cb_data = user_data;
+		ba2str(&dev->src, source);
+		ba2str(&dev->dst, destination);
 		io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -418,8 +680,8 @@ gboolean gateway_request_stream(struct audio_device *dev,
 	return TRUE;
 }
 
-int gateway_config_stream(struct audio_device *dev, gateway_stream_cb_t sco_cb,
-				void *user_data)
+int gateway_config_stream(struct audio_device *dev,
+				gateway_stream_cb_t sco_cb, void *user_data)
 {
 	struct gateway *gw = dev->gateway;
 
@@ -464,4 +726,3 @@ void gateway_suspend_stream(struct audio_device *dev)
 	gw->sco_start_cb = NULL;
 	gw->sco_start_cb_data = NULL;
 }
-
diff --git a/audio/gateway.h b/audio/gateway.h
index 3b0457f..eaf6801 100644
--- a/audio/gateway.h
+++ b/audio/gateway.h
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -22,20 +23,25 @@
  *
  */
 
-#define AUDIO_GATEWAY_INTERFACE "org.bluez.HeadsetGateway"
+#define AUDIO_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
 
 #define DEFAULT_HSP_HS_CHANNEL 6
 #define DEFAULT_HFP_HS_CHANNEL 7
 
 typedef enum {
 	GATEWAY_STATE_DISCONNECTED,
-	GATEWAY_STATE_CONNECTED
+	GATEWAY_STATE_CONNECTED,
+	GATEWAY_STATE_CONNECTING,
+	GATEWAY_STATE_PLAYING,
 } gateway_state_t;
 
 typedef void (*gateway_stream_cb_t) (struct audio_device *dev, void *user_data);
+
 struct gateway *gateway_init(struct audio_device *device);
+void gateway_exit();
 gboolean gateway_is_connected(struct audio_device *dev);
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *chan);
+int gateway_connect_rfcomm(struct audio_device *dev,
+			GIOChannel *chan, int ch);
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *chan);
 void gateway_start_service(struct audio_device *device);
 gboolean gateway_request_stream(struct audio_device *dev,
diff --git a/audio/manager.c b/audio/manager.c
index 413c1f3..e768014 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -198,7 +198,7 @@ static void handle_uuid(const char *uuidstr, struct audio_device *device)
 		break;
 	case HANDSFREE_AGW_SVCLASS_ID:
 		debug("Found Handsfree AG record");
-		if (device->gateway == NULL)
+		if (enabled.gateway && (device->gateway == NULL))
 			device->gateway = gateway_init(device);
 		break;
 	case AUDIO_SINK_SVCLASS_ID:
@@ -586,7 +586,7 @@ static void hf_io_cb(GIOChannel *chan, gpointer data)
 		goto drop;
 	}
 
-	if (gateway_connect_rfcomm(device, chan) < 0) {
+	if (gateway_connect_rfcomm(device, chan, ch) < 0) {
 		error("Allocating new GIOChannel failed!");
 		goto drop;
 	}
@@ -905,22 +905,12 @@ static void headset_server_remove(struct btd_adapter *adapter)
 static int gateway_server_probe(struct btd_adapter *adapter)
 {
 	struct audio_adapter *adp;
-	const gchar *path = adapter_get_path(adapter);
-	int ret;
-
-	DBG("path %s", path);
 
 	adp = audio_adapter_get(adapter);
 	if (!adp)
 		return -EINVAL;
 
-	ret = gateway_server_init(adp);
-	if (ret < 0) {
-		audio_adapter_ref(adp);
-		return ret;
-	}
-
-	return 0;
+	return gateway_server_init(adp);
 }
 
 static void gateway_server_remove(struct btd_adapter *adapter)
@@ -1166,8 +1156,10 @@ void audio_manager_exit(void)
 		telephony_exit();
 	}
 
-	if (enabled.gateway)
+	if (enabled.gateway) {
 		btd_unregister_adapter_driver(&gateway_server_driver);
+		gateway_exit();
+	}
 
 	if (enabled.source || enabled.sink)
 		btd_unregister_adapter_driver(&a2dp_server_driver);
diff --git a/audio/unix.c b/audio/unix.c
index 5cf4f94..bd1a415 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -395,6 +395,9 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	struct bt_start_stream_rsp *rsp = (void *) buf;
 	struct bt_new_stream_ind *ind = (void *) buf;
 
+	if (!dev)
+		goto failed;
+
 	memset(buf, 0, sizeof(buf));
 	rsp->h.type = BT_RESPONSE;
 	rsp->h.name = BT_START_STREAM;
@@ -416,6 +419,11 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	}
 
 	client->req_id = 0;
+	return;
+
+failed:
+	error("gateway_resume_complete: resume failed");
+	unix_ipc_error(client, BT_START_STREAM, EIO);
 }
 
 static void headset_suspend_complete(struct audio_device *dev, void *user_data)
-- 
1.6.4.4


[-- Attachment #4: 0001-Add-HFP-support-in-oFono.patch --]
[-- Type: application/octet-stream, Size: 13008 bytes --]

From 2cf8501a52bc7c2747127d399bab41e63660283a Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 8 Jan 2010 17:25:43 -0200
Subject: [PATCH] Add HFP support in oFono

It uses BlueZ through to get HFP working following the
org.bluez.HandsfreeGateway and org.bluez.HandsfreeAgent from the BlueZ
D-Bus API.

You need the HFP patch into BlueZ too.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for its prototype
on this code.
---
 gatchat/gattty.c    |   23 ++++
 gatchat/gattty.h    |    1 +
 include/modem.h     |    2 +
 plugins/hfp.c       |  299 +++++++++++++++++++++++++++++++++++++++++++++++++--
 plugins/modemconf.c |    3 +-
 src/modem.c         |   15 +++
 6 files changed, 331 insertions(+), 12 deletions(-)

diff --git a/gatchat/gattty.c b/gatchat/gattty.c
index 02ca389..ae12662 100644
--- a/gatchat/gattty.c
+++ b/gatchat/gattty.c
@@ -260,3 +260,26 @@ GIOChannel *g_at_tty_open(const char *tty, GHashTable *options)
 
 	return channel;
 }
+
+GIOChannel *g_at_get_channel_from_fd(int fd)
+{
+	GIOChannel *channel;
+	struct termios ti;
+
+	/* Switch TTY to raw mode */
+	memset(&ti, 0, sizeof(ti));
+	cfmakeraw(&ti);
+	tcflush(fd, TCIOFLUSH);
+	tcsetattr(fd, TCSANOW, &ti);
+
+	channel = g_io_channel_unix_new(fd);
+
+	if (channel == NULL) {
+		close(fd);
+		return NULL;
+	}
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+
+	return channel;
+}
diff --git a/gatchat/gattty.h b/gatchat/gattty.h
index dc3fe16..730f1dc 100644
--- a/gatchat/gattty.h
+++ b/gatchat/gattty.h
@@ -43,6 +43,7 @@ extern "C" {
  */
 GIOChannel *g_at_tty_open(const char *tty, GHashTable *options);
 
+GIOChannel *g_at_get_channel_from_fd(int fd);
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/modem.h b/include/modem.h
index b2aa607..bf28515 100644
--- a/include/modem.h
+++ b/include/modem.h
@@ -47,6 +47,8 @@ struct ofono_modem *ofono_modem_create(const char *name, const char *type);
 int ofono_modem_register(struct ofono_modem *modem);
 void ofono_modem_remove(struct ofono_modem *modem);
 
+struct ofono_modem *ofono_modem_get_by_path(const char *path);
+
 void ofono_modem_set_powered(struct ofono_modem *modem, ofono_bool_t powered);
 ofono_bool_t ofono_modem_get_powered(struct ofono_modem *modem);
 
diff --git a/plugins/hfp.c b/plugins/hfp.c
index 3bbd922..2e5e2a1 100644
--- a/plugins/hfp.c
+++ b/plugins/hfp.c
@@ -30,6 +30,8 @@
 #include <glib.h>
 #include <gatchat.h>
 #include <gattty.h>
+#include <gdbus.h>
+#include <ofono.h>
 
 #define OFONO_API_SUBJECT_TO_CHANGE
 #include <ofono/plugin.h>
@@ -52,11 +54,28 @@
 
 #include <drivers/hfpmodem/hfpmodem.h>
 
+#include <ofono/dbus.h>
+
+#define BLUEZ_SERVICE "org.bluez"
+#define BLUEZ_PATH "/"
+#define BLUEZ_MANAGER_INTERFACE "org.bluez.Manager"
+#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter"
+#define BLUEZ_DEVICE_INTERFACE "org.bluez.Device"
+#define BLUEZ_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
+
+#define HFP_AGENT_INTERFACE "org.bluez.HandsfreeAgent"
+
 static const char *brsf_prefix[] = { "+BRSF:", NULL };
 static const char *cind_prefix[] = { "+CIND:", NULL };
 static const char *cmer_prefix[] = { "+CMER:", NULL };
 static const char *chld_prefix[] = { "+CHLD:", NULL };
 
+static DBusConnection *connection;
+static char *ofono_handsfree_path;
+
+
+static int timeout;
+
 static int hfp_disable(struct ofono_modem *modem);
 
 static void hfp_debug(const char *str, void *user_data)
@@ -135,6 +154,60 @@ static void cmer_cb(gboolean ok, GAtResult *result, gpointer user_data)
 		sevice_level_conn_established(modem);
 }
 
+static int send_method_call(const char *dest, const char *path,
+				const char *interface, const char *method,
+				DBusPendingCallNotifyFunction cb,
+				void *user_data, int type, ...)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+	va_list args;
+
+	msg = dbus_message_new_method_call(dest, path, interface, method);
+	if (!msg) {
+		ofono_error("Unable to allocate new D-Bus %s message", method);
+		return -ENOMEM;
+	}
+
+	va_start(args, type);
+
+	if (!dbus_message_append_args_valist(msg, type, args)) {
+		dbus_message_unref(msg);
+		va_end(args);
+		return -EIO;
+	}
+
+	va_end(args);
+
+	if (!cb) {
+		g_dbus_send_message(connection, msg);
+		return 0;
+	}
+
+	if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+		ofono_error("Sending %s failed", method);
+		dbus_message_unref(msg);
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(call, cb, user_data, NULL);
+	dbus_pending_call_unref(call);
+	dbus_message_unref(msg);
+
+	return 0;
+}
+
+static gboolean hfp_enable_timeout(gpointer user)
+{
+	struct ofono_modem *modem = user;
+
+	if (ofono_modem_get_powered(modem))
+		return FALSE;
+
+	hfp_disable(modem);
+	return FALSE;
+}
+
 static void cind_status_cb(gboolean ok, GAtResult *result,
 				gpointer user_data)
 {
@@ -260,8 +333,7 @@ error:
 }
 
 /* either oFono or Phone could request SLC connection */
-static int service_level_connection(struct ofono_modem *modem,
-				const char *tty)
+static int service_level_connection(struct ofono_modem *modem, int fd)
 {
 	struct hfp_data *data = ofono_modem_get_data(modem);
 	GIOChannel *io;
@@ -269,7 +341,7 @@ static int service_level_connection(struct ofono_modem *modem,
 	GAtChat *chat;
 	char buf[64];
 
-	io = g_at_tty_open(tty, NULL);
+	io = g_at_get_channel_from_fd(fd);
 	if (!io) {
 		ofono_error("Service level connection failed: %s (%d)",
 			strerror(errno), errno);
@@ -296,9 +368,173 @@ static int service_level_connection(struct ofono_modem *modem,
 	return -EINPROGRESS;
 }
 
+static DBusMessage *hfp_agent_new_connection(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	int fd;
+	const char *path;
+	struct ofono_modem *modem = NULL;
+
+	path = dbus_message_get_path(msg);
+	modem = ofono_modem_get_by_path(path);
+
+	if (!modem)
+		return NULL;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UNIX_FD, &fd,
+				DBUS_TYPE_INVALID))
+		return __ofono_error_invalid_args(msg);
+
+	service_level_connection(modem, fd);
+
+	return NULL;
+}
+
+static DBusMessage *hfp_agent_release(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	return NULL;
+}
+
+static GDBusMethodTable agent_methods[] = {
+        { "NewConnection", "h", "", hfp_agent_new_connection,
+		G_DBUS_METHOD_FLAG_ASYNC },
+	{ "Release", "", "", hfp_agent_release },
+	{NULL, NULL, NULL, NULL}
+};
+
+static void agent_registered_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusMessage *reply;
+
+	reply = dbus_pending_call_steal_reply(call);
+	dbus_message_unref(reply);
+}
+
+static void agent_unregistered_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusMessage *reply;
+
+	reply = dbus_pending_call_steal_reply(call);
+	dbus_message_unref(reply);
+}
+
+static void find_device_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	struct ofono_modem *modem = user_data;
+	const char *device, *obj_path;
+	int ret;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH,
+				&device, DBUS_TYPE_INVALID) == FALSE) {
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s", err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	ofono_debug("Using device %s", device);
+	ofono_handsfree_path = g_strdup(device);
+
+	obj_path = ofono_modem_get_path(modem);
+
+	ret = send_method_call(BLUEZ_SERVICE, device,
+				BLUEZ_GATEWAY_INTERFACE, "RegisterAgent",
+				agent_registered_cb, NULL, DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+
+	if (ret < 0)
+		ofono_error("agent_register failed(%d)", ret);
+
+done:
+	dbus_message_unref(reply);;
+}
+
+static void get_adapter_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	struct ofono_modem *modem = user_data;
+	const char *adapter = NULL;
+	const char *address;
+	int ret;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+	if (dbus_message_get_args(reply, &err,
+				DBUS_TYPE_OBJECT_PATH,
+				&adapter, DBUS_TYPE_INVALID) == FALSE) {
+		if (adapter == NULL) {
+			ofono_error("bluetooth adapter is not enabled");
+			dbus_error_free(&err);
+			goto done;
+		}
+
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s %s", adapter, err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	address = ofono_modem_get_string(modem, "Address");
+
+	DBG("address %s", address);
+
+	if (address == NULL)
+		return;
+
+	ret = send_method_call(BLUEZ_SERVICE, adapter,
+				BLUEZ_ADAPTER_INTERFACE, "FindDevice",
+				find_device_cb, modem,
+				DBUS_TYPE_STRING, &address,
+				DBUS_TYPE_INVALID);
+
+	if (ret < 0)
+		ofono_error("find_device failed(%d)", ret);
+
+done:
+	dbus_message_unref(reply);
+
+}
+
+static int hfp_register_ofono_handsfree(struct ofono_modem *modem)
+{
+	ofono_debug("Register oFono Agent to bluetooth daemon");
+
+	return send_method_call(BLUEZ_SERVICE, BLUEZ_PATH,
+				BLUEZ_MANAGER_INTERFACE, "DefaultAdapter",
+				get_adapter_cb, modem,
+				DBUS_TYPE_INVALID);
+}
+
+static int hfp_unregister_ofono_handsfree(struct ofono_modem *modem)
+{
+	const char *obj_path = ofono_modem_get_path(modem);
+
+	ofono_debug("Unregister oFono Agent to bluetooth daemon");
+
+	if (!ofono_handsfree_path)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, ofono_handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "UnregisterAgent",
+				agent_unregistered_cb, NULL,
+				DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+}
+
 static int hfp_probe(struct ofono_modem *modem)
 {
 	struct hfp_data *data;
+	const char *obj_path;
+	DBusConnection *conn = ofono_dbus_get_connection();
 
 	data = g_try_new0(struct hfp_data, 1);
 	if (!data)
@@ -312,6 +548,15 @@ static int hfp_probe(struct ofono_modem *modem)
 
 	ofono_modem_set_data(modem, data);
 
+	connection = dbus_connection_ref(conn);
+
+	obj_path = ofono_modem_get_path(modem);
+	g_dbus_register_interface(conn, obj_path, HFP_AGENT_INTERFACE,
+			agent_methods, NULL, NULL, data, NULL);
+
+	if (hfp_register_ofono_handsfree(modem) != 0)
+		return -EINVAL;
+
 	return 0;
 }
 
@@ -322,24 +567,57 @@ static void hfp_remove(struct ofono_modem *modem)
 	if (data)
 		g_free(data);
 
+	hfp_unregister_ofono_handsfree(modem);
+
+	if (ofono_handsfree_path)
+		g_free(ofono_handsfree_path);
+
+	dbus_connection_unref(connection);
+
 	ofono_modem_set_data(modem, NULL);
 }
 
+static void port_connect_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusMessage *reply;
+
+	reply = dbus_pending_call_steal_reply(call);
+	dbus_message_unref(reply);
+}
+
+static int hfp_connect_ofono_handsfree()
+{
+	ofono_debug("Connect to bluetooth daemon");
+
+	if (!ofono_handsfree_path || !connection)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, ofono_handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Connect",
+				port_connect_cb, NULL, DBUS_TYPE_INVALID);
+}
+
 /* power up hardware */
 static int hfp_enable(struct ofono_modem *modem)
 {
-	const char *tty;
-	int ret;
-
 	DBG("%p", modem);
 
-	tty = ofono_modem_get_string(modem, "Device");
-	if (tty == NULL)
+	timeout = g_timeout_add_seconds(10, hfp_enable_timeout, modem);
+
+	if (hfp_connect_ofono_handsfree() < 0)
 		return -EINVAL;
 
-	ret = service_level_connection(modem, tty);
+	return -EINPROGRESS;
+}
+
+static int hfp_disconnect_ofono_handsfree()
+{
+	if (!ofono_handsfree_path || !connection)
+		return -EINVAL;
 
-	return ret;
+	return send_method_call(BLUEZ_SERVICE, ofono_handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Disconnect",
+				NULL, NULL, DBUS_TYPE_INVALID);
 }
 
 static int hfp_disable(struct ofono_modem *modem)
@@ -359,6 +637,7 @@ static int hfp_disable(struct ofono_modem *modem)
 
 	ofono_modem_set_powered(modem, FALSE);
 
+	hfp_disconnect_ofono_handsfree();
 	return 0;
 }
 
diff --git a/plugins/modemconf.c b/plugins/modemconf.c
index c8c261f..b4c1600 100644
--- a/plugins/modemconf.c
+++ b/plugins/modemconf.c
@@ -124,12 +124,11 @@ static struct ofono_modem *create_modem(GKeyFile *keyfile, const char *group)
 
 	modem = ofono_modem_create(group, driver);
 
-	if (!g_strcmp0(driver, "phonesim"))
+	if (!g_strcmp0(driver, "phonesim") || !g_strcmp0(driver, "hfp"))
 		set_address(modem, keyfile, group);
 
 	if (!g_strcmp0(driver, "atgen") || !g_strcmp0(driver, "g1") ||
 						!g_strcmp0(driver, "calypso") ||
-						!g_strcmp0(driver, "hfp") ||
 						!g_strcmp0(driver, "palmpre"))
 		set_device(modem, keyfile, group);
 
diff --git a/src/modem.c b/src/modem.c
index bbc9905..9fab722 100644
--- a/src/modem.c
+++ b/src/modem.c
@@ -1074,6 +1074,21 @@ struct ofono_modem *ofono_modem_create(const char *name, const char *type)
 	return modem;
 }
 
+struct ofono_modem *ofono_modem_get_by_path(const char *path)
+{
+	GSList *l;
+	struct ofono_modem *modem;
+
+	for (l = g_modem_list; l; l = l->next) {
+		modem = l->data;
+
+		if (!g_strcmp0(modem->path, path))
+			return modem;
+	}
+
+	return NULL;
+}
+
 static void emit_modems()
 {
 	DBusConnection *conn = ofono_dbus_get_connection();
-- 
1.6.4.4


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

* Re: [RFC] HFP support into oFono and BlueZ
@ 2010-01-11 19:05   ` Gustavo F. Padovan
  0 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-11 19:05 UTC (permalink / raw)
  To: ofono

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

On Mon, Jan 11, 2010 at 3:08 PM, Gustavo F. Padovan
<padovan@profusion.mobi> wrote:
> Hi,
>
> These patches implement the new API for the Audio Gateway in BlueZ. It
> follows the last version of the HandsfreeGateway and HandsfreeAgent
> Intefaces API.
> The first two patches is for BlueZ and the other for oFono. You can
> test it with using enable-modem and test-voicecall scripts into the
> test dir of oFono.
> Feel free to test it and send me your comments. We have some bugs yet.

New version of the patches: now we do not handle tty stuff on the BlueZ side.

>
> The audio part is not working yet. We are going to work on pulseaudio
> this week to get this done soon.
>
> Regards,
>
> --
> Gustavo F. Padovan
> ProFUSION embedded systems - http://profusion.mobi
>



-- 
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi

[-- Attachment #2: 0001-clean-up-audio-gateway.c.patch --]
[-- Type: application/octet-stream, Size: 24620 bytes --]

From 7eea257a7b4a0cbc026aab6a60df9bb580065831 Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 6 Nov 2009 18:08:58 -0200
Subject: [PATCH 1/2] clean up audio/gateway.c

remove all code related to the AT engine
---
 audio/gateway.c |  760 +------------------------------------------------------
 1 files changed, 1 insertions(+), 759 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index a1c1ea2..3dc09ff 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -53,52 +53,6 @@
 #include "dbus-common.h"
 
 #define RFCOMM_BUF_SIZE 256
-/* not-more-then-16 defined by GSM + 1 for NULL + padding */
-#define AG_INDICATOR_DESCR_SIZE 20
-#define AG_CALLER_NUM_SIZE 64	/* size of number + type */
-
-/* commands */
-#define AG_FEATURES "AT+BRSF=26\r"     /* = 0x7F = All features supported */
-#define AG_INDICATORS_SUPP "AT+CIND=?\r"
-#define AG_INDICATORS_VAL "AT+CIND?\r"
-#define AG_INDICATORS_ENABLE "AT+CMER=3,0,0,1\r"
-#define AG_HOLD_MPTY_SUPP "AT+CHLD=?\r"
-#define AG_CALLER_IDENT_ENABLE "AT+CLIP=1\r"
-#define AG_CARRIER_FORMAT "AT+COPS=3,0\r"
-#define AG_EXTENDED_RESULT_CODE "AT+CMEE=1\r"
-
-#define AG_FEATURE_3WAY 0x1
-#define AG_FEATURE_EXTENDED_RES_CODE 0x100
-/* Hold and multipary AG features.
- * Comments below are copied from hands-free spec for reference */
-/* Releases all held calls or sets User Determined User Busy (UDUB)
- * for a waiting call */
-#define AG_CHLD_0 0x01
-/* Releases all active calls (if any exist) and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_1 0x02
-/* Releases specified active call only <x> */
-#define AG_CHLD_1x 0x04
-/* Places all active calls (if any exist) on hold and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_2 0x08
-/* Request private consultation mode with specified call <x> (Place all
- * calls on hold EXCEPT the call <x>) */
-#define AG_CHLD_2x 0x10
-/* Adds a held call to the conversation */
-#define AG_CHLD_3 0x20
-/* Connects the two calls and disconnects the subscriber from both calls
- * (Explicit Call Transfer). Support for this value and its associated
- * functionality is optional for the HF. */
-#define AG_CHLD_4 0x40
-
-#define OK_RESPONSE "\r\nOK\r\n"
-#define ERROR_RESPONSE "\r\nERROR\r\n"
-
-struct indicator {
-	gchar descr[AG_INDICATOR_DESCR_SIZE];
-	gint value;
-};
 
 struct gateway {
 	gateway_state_t state;
@@ -108,387 +62,10 @@ struct gateway {
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
 	DBusMessage *connect_message;
-	guint ag_features;
-	guint hold_multiparty_features;
-	GSList *indies;
-	gboolean is_dialing;
-	gboolean call_active;
-
-	int sp_gain;
-	int mic_gain;
 };
 
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device);
-
 int gateway_close(struct audio_device *device);
 
-static void rfcomm_start_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	gw->rfcomm_watch_id = g_io_add_watch(gw->rfcomm,
-			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-			(GIOFunc) rfcomm_ag_data_cb, dev);
-}
-
-static void rfcomm_stop_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	g_source_remove(gw->rfcomm_watch_id);
-}
-
-static gboolean io_channel_write_all(GIOChannel *io, gchar *data,
-					gsize count)
-{
-	gsize written = 0;
-	GIOStatus status;
-
-	while (count > 0) {
-		status = g_io_channel_write_chars(io, data, count, &written,
-						NULL);
-		if (status != G_IO_STATUS_NORMAL)
-			return FALSE;
-
-		data += written;
-		count -= written;
-	}
-	return TRUE;
-}
-
-/* it's worth to mention that data and response could be the same pointers */
-static gboolean rfcomm_send_and_read(struct gateway *gw, gchar *data,
-                                    gchar *response, gsize count)
-{
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read = 0;
-	gboolean got_ok = FALSE;
-	gboolean got_error = FALSE;
-	gchar *resp_buf = response;
-	gsize toread = RFCOMM_BUF_SIZE - 1;
-	GIOStatus status;
-
-	if (!io_channel_write_all(rfcomm, data, count))
-		return FALSE;
-
-	while (!(got_ok || got_error)) {
-		status = g_io_channel_read_chars(rfcomm, resp_buf, toread,
-						&read, NULL);
-		if (status == G_IO_STATUS_NORMAL)
-			resp_buf[read] = '\0';
-		else {
-			debug("rfcomm_send_and_read(): %m");
-			return FALSE;
-		}
-		got_ok = NULL != strstr(resp_buf, OK_RESPONSE);
-		got_error = NULL != strstr(resp_buf, ERROR_RESPONSE);
-		resp_buf += read;
-		toread -= read;
-	}
-	return TRUE;
-}
-
-/* get <descr> from the names: (<descr>, (<values>)), (<descr>, (<values>))
- * ... */
-static GSList *parse_indicator_names(gchar *names, GSList *indies)
-{
-	gchar *current = names - 1;
-	GSList *result = indies;
-	gchar *next;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 2;
-		next = strstr(current, ",(");
-		ind = g_slice_new(struct indicator);
-		strncpy(ind->descr, current, 20);
-		ind->descr[(intptr_t) next - (intptr_t) current] = '\0';
-		result = g_slist_append(result, (gpointer) ind);
-		current = strstr(next + 1, ",(");
-	}
-	return result;
-}
-
-/* get values from <val0>,<val1>,... */
-static GSList *parse_indicator_values(gchar *values, GSList *indies)
-{
-	gint val;
-	gchar *current = values - 1;
-	GSList *runner = indies;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 1;
-		sscanf(current, "%d", &val);
-		current = strchr(current, ',');
-		ind = g_slist_nth_data(runner, 0);
-		ind->value = val;
-		runner = g_slist_next(runner);
-	}
-	return indies;
-}
-
-/* get values from <val0>,<val1>,... */
-static guint get_hold_mpty_features(gchar *features)
-{
-	guint result = 0;
-
-	if (strstr(features, "0"))
-		result |= AG_CHLD_0;
-
-	if (strstr(features, "1"))
-		result |= AG_CHLD_1;
-
-	if (strstr(features, "1x"))
-		result |= AG_CHLD_1x;
-
-	if (strstr(features, "2"))
-		result |= AG_CHLD_2;
-
-	if (strstr(features, "2x"))
-		result |= AG_CHLD_2x;
-
-	if (strstr(features, "3"))
-		result |= AG_CHLD_3;
-
-	if (strstr(features, "4"))
-		result |= AG_CHLD_4;
-
-	return result;
-}
-
-static gboolean establish_service_level_conn(struct gateway *gw)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	gboolean res;
-
-	debug("at the begin of establish_service_level_conn()");
-	res = rfcomm_send_and_read(gw, AG_FEATURES, buf,
-				sizeof(AG_FEATURES) - 1);
-	if (!res || sscanf(buf, "\r\n+BRSF:%d", &gw->ag_features) != 1)
-		return FALSE;
-
-	debug("features are 0x%X", gw->ag_features);
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_SUPP, buf,
-				sizeof(AG_INDICATORS_SUPP) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_names(strchr(buf, '('), NULL);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_VAL, buf,
-		sizeof(AG_INDICATORS_VAL) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_values(strchr(buf, ':') + 1, gw->indies);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_ENABLE, buf,
-				sizeof(AG_INDICATORS_ENABLE) - 1);
-	if (!res || !strstr(buf, "OK"))
-		return FALSE;
-
-	if ((gw->ag_features & AG_FEATURE_3WAY) != 0) {
-		res = rfcomm_send_and_read(gw, AG_HOLD_MPTY_SUPP, buf,
-				sizeof(AG_HOLD_MPTY_SUPP) - 1);
-		if (!res || !strstr(buf, "+CHLD:")) {
-			g_slice_free1(RFCOMM_BUF_SIZE, buf);
-			return FALSE;
-		}
-		gw->hold_multiparty_features = get_hold_mpty_features(
-							strchr(buf, '('));
-
-	} else
-		gw->hold_multiparty_features = 0;
-
-	debug("Service layer connection successfully established!");
-	rfcomm_send_and_read(gw, AG_CALLER_IDENT_ENABLE, buf,
-			sizeof(AG_CALLER_IDENT_ENABLE) - 1);
-	rfcomm_send_and_read(gw, AG_CARRIER_FORMAT, buf,
-			sizeof(AG_CARRIER_FORMAT) - 1);
-	if ((gw->ag_features & AG_FEATURE_EXTENDED_RES_CODE) != 0)
-		rfcomm_send_and_read(gw, AG_EXTENDED_RESULT_CODE, buf,
-			sizeof(AG_EXTENDED_RESULT_CODE) - 1);
-
-	return TRUE;
-}
-
-static void process_ind_change(struct audio_device *dev, guint index,
-							gint value)
-{
-	struct gateway *gw = dev->gateway;
-	struct indicator *ind = g_slist_nth_data(gw->indies, index - 1);
-	gchar *name = ind->descr;
-
-	ind->value = value;
-
-	debug("at the begin of process_ind_change, name is %s", name);
-	if (!strcmp(name, "\"call\"")) {
-		if (value > 0) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallStarted", DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-			gw->call_active = TRUE;
-		} else {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallEnded", DBUS_TYPE_INVALID);
-			gw->call_active = FALSE;
-		}
-
-	} else if (!strcmp(name, "\"callsetup\"")) {
-		if (value == 0 && gw->is_dialing) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-		} else if (!gw->is_dialing && value > 0)
-			gw->is_dialing = TRUE;
-
-	} else if (!strcmp(name, "\"callheld\"")) {
-		/* FIXME: The following code is based on assumptions only.
-		 * Has to be tested for interoperability
-		 * I assume that callheld=2 would be sent when dial from HF
-		 * failed in case of 3-way call
-		 * Unfortunately this path is not covered by the HF spec so
-		 * the code has to be tested for interop
-		*/
-		/* '2' means: all calls held, no active calls */
-		if (value == 2) {
-			if (gw->is_dialing) {
-				g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-				gw->is_dialing = FALSE;
-			}
-		}
-	} else if (!strcmp(name, "\"service\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RegistrationStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"signal\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "SignalStrength",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"roam\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RoamingStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"battchg\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "BatteryCharge",
-				DBUS_TYPE_UINT16, &value);
-}
-
-static void process_ring(struct audio_device *device, GIOChannel *chan,
-			gchar *buf)
-{
-	gchar number[AG_CALLER_NUM_SIZE];
-	gchar *cli;
-	gchar *sep;
-	gsize read;
-	GIOStatus status;
-
-	rfcomm_stop_watch(device);
-	status = g_io_channel_read_chars(chan, buf, RFCOMM_BUF_SIZE - 1, &read, NULL);
-	if (status != G_IO_STATUS_NORMAL)
-		return;
-
-	debug("at the begin of process_ring");
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 10)
-		error("process_ring(): buf is too long '%s'", buf);
-	else if ((cli = strstr(buf, "\r\n+CLIP"))) {
-		if (sscanf(cli, "\r\n+CLIP: \"%s", number) == 1) {
-			sep = strchr(number, '"');
-			sep[0] = '\0';
-
-			/* FIXME:signal will be emitted on each RING+CLIP.
-			 * That's bad */
-			cli = number;
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE, "Ring",
-					DBUS_TYPE_STRING, &cli,
-					DBUS_TYPE_INVALID);
-			device->gateway->is_dialing = TRUE;
-		} else
-			error("process_ring(): '%s' in place of +CLIP after RING", buf);
-
-	}
-
-	rfcomm_start_watch(device);
-}
-
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	struct gateway *gw;
-	gsize read;
-	/* some space for value */
-	gchar indicator[AG_INDICATOR_DESCR_SIZE + 4];
-	gint value;
-	guint index;
-	gchar *sep;
-
-	debug("at the begin of rfcomm_ag_data_cb()");
-	if (cond & G_IO_NVAL)
-		return FALSE;
-
-	gw = device->gateway;
-
-	if (cond & (G_IO_ERR | G_IO_HUP)) {
-		debug("connection with remote BT is closed");
-		gateway_close(device);
-		return FALSE;
-	}
-
-	if (g_io_channel_read_chars(chan, buf, sizeof(buf) - 1, &read, NULL)
-			!= G_IO_STATUS_NORMAL)
-		return TRUE;
-	buf[read] = '\0';
-
-	if (strlen(buf) > AG_INDICATOR_DESCR_SIZE + 14)
-		error("rfcomm_ag_data_cb(): buf is too long '%s'", buf);
-	else if (sscanf(buf, "\r\n+CIEV:%s\r\n", indicator) == 1) {
-		sep = strchr(indicator, ',');
-		sep[0] = '\0';
-		sep += 1;
-		index = atoi(indicator);
-		value = atoi(sep);
-		process_ind_change(device, index, value);
-	} else if (strstr(buf, "RING"))
-		process_ring(device, chan, buf);
-	else if (sscanf(buf, "\r\n+BVRA:%d\r\n", &value) == 1) {
-		if (value == 0)
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionActive",
-					DBUS_TYPE_INVALID);
-		else
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionInactive",
-					DBUS_TYPE_INVALID);
-	} else if (sscanf(buf, "\r\n+VGS:%d\r\n", &value) == 1) {
-		gw->sp_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "SpeakerGain",
-				DBUS_TYPE_UINT16, &value);
-	} else if (sscanf(buf, "\r\n+VGM:%d\r\n", &value) == 1) {
-		gw->mic_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "MicrophoneGain",
-				DBUS_TYPE_UINT16, &value);
-	} else
-		error("rfcomm_ag_data_cb(): read wrong data '%s'", buf);
-
-	return TRUE;
-}
-
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
 {
@@ -541,7 +118,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	DBusMessage *conn_mes = gw->connect_message;
 	gchar gw_addr[18];
 	GIOFlags flags;
 
@@ -563,29 +139,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 	if (!gw->rfcomm)
 		gw->rfcomm = g_io_channel_ref(chan);
 
-	if (establish_service_level_conn(dev->gateway)) {
-		gboolean value = TRUE;
-
-		debug("%s: Connected to %s", dev->path, gw_addr);
-		rfcomm_start_watch(dev);
-		if (conn_mes) {
-			DBusMessage *reply =
-				dbus_message_new_method_return(conn_mes);
-			dbus_connection_send(dev->conn, reply, NULL);
-			dbus_message_unref(reply);
-			dbus_message_unref(conn_mes);
-			gw->connect_message = NULL;
-		}
-
-		gw->state = GATEWAY_STATE_CONNECTED;
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN,	&value);
-		return;
-	} else
-		error("%s: Failed to establish service layer connection to %s",
-			dev->path, gw_addr);
-
 	if (NULL != gw->sco_start_cb)
 		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 
@@ -732,321 +285,20 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
-static DBusMessage *process_ag_reponse(DBusMessage *msg, gchar *response)
-{
-	DBusMessage *reply;
-
-
-	debug("in process_ag_reponse, response is %s", response);
-	if (strstr(response, OK_RESPONSE))
-		reply = dbus_message_new_method_return(msg);
-	else {
-		/* FIXME: some code should be here to processes errors
-		 *  in better fasion */
-		debug("AG responded with '%s' to %s method call", response,
-				dbus_message_get_member(msg));
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".OperationFailed",
-					"Operation failed.See log for details");
-	}
-	return reply;
-}
-
-static DBusMessage *process_simple(DBusMessage *msg, struct audio_device *dev,
-					gchar *data)
-{
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-
-	rfcomm_stop_watch(dev);
-	rfcomm_send_and_read(gw, data, buf, strlen(data));
-	rfcomm_start_watch(dev);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_ANSWER "ATA\r"
-
-static DBusMessage *ag_answer(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	if (gw->call_active)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".CallAlreadyAnswered",
-					"Call AlreadyAnswered");
-
-	return process_simple(msg, dev, AG_ANSWER);
-}
-
-#define AG_HANGUP "AT+CHUP\r"
-
-static DBusMessage *ag_terminate_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	return process_simple(msg, dev, AG_HANGUP);
-}
-
-/* according to GSM spec */
-#define ALLOWED_NUMBER_SYMBOLS "1234567890*#ABCD"
-#define AG_PLACE_CALL "ATD%s;\r"
-/* dialing from memory is not supported as headset spec doesn't define a way
- * to retreive phone memory entries.
- */
-static DBusMessage *ag_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint atd_len;
-	DBusMessage *result;
-
-	debug("at the begin of ag_call()");
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	atd_len = sprintf(buf, AG_PLACE_CALL, number);
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, buf, buf, atd_len);
-	rfcomm_start_watch(device);
-
-	result = process_ag_reponse(msg, buf);
-	return result;
-}
-
-#define AG_GET_CARRIER "AT+COPS?\r"
-
-static DBusMessage *ag_get_operator(DBusConnection *conn, DBusMessage *msg,
-					void *data)
-{
-	struct audio_device *dev = (struct audio_device *) data;
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read;
-	gchar *result, *sep;
-	DBusMessage *reply;
-	GIOStatus status;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(dev);
-	io_channel_write_all(rfcomm, AG_GET_CARRIER, strlen(AG_GET_CARRIER));
-
-	status = g_io_channel_read_chars(rfcomm, buf, RFCOMM_BUF_SIZE - 1,
-						&read, NULL);
-	rfcomm_start_watch(dev);
-	if (G_IO_STATUS_NORMAL == status) {
-		buf[read] = '\0';
-		if (strstr(buf, "+COPS")) {
-			if (!strrchr(buf, ','))
-				result = "0";
-			else {
-				result = strchr(buf, '\"') + 1;
-				sep = strchr(result, '\"');
-				sep[0] = '\0';
-			}
-
-			reply = dbus_message_new_method_return(msg);
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&result, DBUS_TYPE_INVALID);
-		} else {
-			info("ag_get_operator(): '+COPS' expected but"
-				" '%s' received", buf);
-			reply = dbus_message_new_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Unexpected response from AG");
-		}
-	} else {
-		error("ag_get_operator(): %m");
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".ConnectionFailed",
-					"Failed to receive response from AG");
-	}
-
-	return reply;
-}
-
-#define AG_SEND_DTMF "AT+VTS=%c\r"
-static DBusMessage *ag_send_dtmf(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint com_len;
-	gboolean got_ok = TRUE;
-	gint num_len;
-	gint i = 0;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	num_len = strlen(number);
-	rfcomm_stop_watch(device);
-	while (i < num_len && got_ok) {
-		com_len = sprintf(buf, AG_SEND_DTMF, number[i]);
-		rfcomm_send_and_read(gw, buf, buf, com_len);
-		got_ok = NULL != strstr(buf, OK_RESPONSE);
-		i += 1;
-	}
-	rfcomm_start_watch(device);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_GET_SUBSCRIBER_NUMS "AT+CNUM\r"
-#define CNUM_LEN 5             /* length of "+CNUM" string */
-#define MAX_NUMBER_CNT 16
-static DBusMessage *ag_get_subscriber_num(DBusConnection *conn,
-					DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number, *end;
-	DBusMessage *reply = dbus_message_new_method_return(msg);
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, AG_GET_SUBSCRIBER_NUMS, buf,
-			strlen(AG_GET_SUBSCRIBER_NUMS));
-	rfcomm_start_watch(device);
-
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 21)
-		error("ag_get_subscriber_num(): buf is too long '%s'", buf);
-	else if (strstr(buf, "+CNUM")) {
-		number = strchr(buf, ',');
-		number++;
-		end = strchr(number, ',');
-		if (end) {
-			*end = '\0';
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&number, DBUS_TYPE_INVALID);
-		}
-	} else
-		error("ag_get_subscriber_num(): read wrong data '%s'", buf);
-
-	return reply;
-}
-
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	DBusMessage *reply;
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	gboolean value;
-	guint index = 0;
-	struct indicator *ind;
-
-	reply = dbus_message_new_method_return(msg);
-	if (!reply)
-		return NULL;
-
-	dbus_message_iter_init_append(reply, &iter);
-
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
-	/* Connected */
-	value = gateway_is_connected(device);
-	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
-
-	if (!value)
-		goto done;
-
-	while ((ind = g_slist_nth_data(gw->indies, index))) {
-		if(!strcmp(ind->descr, "\"service\""))
-			dict_append_entry(&dict, "RegistrationStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"signal\""))
-			dict_append_entry(&dict, "SignalStrength",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"roam\""))
-			dict_append_entry(&dict, "RoamingStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"battchg\""))
-			dict_append_entry(&dict, "BatteryCharge",
-					DBUS_TYPE_UINT16, &ind->value);
-		index++;
-	}
-
-	/* SpeakerGain */
-	dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16,
-				&device->gateway->sp_gain);
-
-	/* MicrophoneGain */
-	dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16,
-				&device->gateway->mic_gain);
-done:
-	dbus_message_iter_close_container(&iter, &dict);
-	return reply;
+	return NULL;
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "Disconnect", "", "", ag_disconnect },
-	{ "AnswerCall", "", "", ag_answer },
-	{ "TerminateCall", "", "", ag_terminate_call },
-	{ "Call", "s", "", ag_call },
-	{ "GetOperatorName", "", "s", ag_get_operator },
-	{ "SendDTMF", "s", "", ag_send_dtmf },
-	{ "GetSubscriberNumber", "", "s", ag_get_subscriber_num },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
 	{ NULL, NULL, NULL, NULL }
 };
 
 static GDBusSignalTable gateway_signals[] = {
-	{ "Ring", "s" },
-	{ "CallTerminated", "" },
-	{ "CallStarted", "" },
-	{ "CallEnded", "" },
 	{ "PropertyChanged", "sv" },
 	{ NULL, NULL }
 };
@@ -1063,9 +315,6 @@ struct gateway *gateway_init(struct audio_device *dev)
 
 	debug("in gateway_init, dev is %p", dev);
 	gw = g_new0(struct gateway, 1);
-	gw->indies = NULL;
-	gw->is_dialing = FALSE;
-	gw->call_active = FALSE;
 	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
 
@@ -1107,11 +356,6 @@ void gateway_start_service(struct audio_device *device)
 	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
 }
 
-static void indicator_slice_free(gpointer mem)
-{
-	g_slice_free(struct indicator, mem);
-}
-
 int gateway_close(struct audio_device *device)
 {
 	struct gateway *gw = device->gateway;
@@ -1119,8 +363,6 @@ int gateway_close(struct audio_device *device)
 	GIOChannel *sco = gw->sco;
 	gboolean value = FALSE;
 
-	g_slist_foreach(gw->indies, (GFunc) indicator_slice_free, NULL);
-	g_slist_free(gw->indies);
 	if (rfcomm) {
 		g_io_channel_shutdown(rfcomm, TRUE, NULL);
 		g_io_channel_unref(rfcomm);
-- 
1.6.4.4


[-- Attachment #3: 0002-Implement-HandsfreeGateway-Interface.patch --]
[-- Type: application/octet-stream, Size: 21795 bytes --]

From 56757bb836d58caecd9fd6366e3bb1b70e778841 Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 8 Jan 2010 17:38:21 -0200
Subject: [PATCH 2/2] Implement HandsfreeGateway Interface

Create a interface where a Handsfree agent can register and use BlueZ to
handle the rfcomm and sco links.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for his
prototype on this code.
---
 audio/gateway.c |  447 +++++++++++++++++++++++++++++++++++++++++++------------
 audio/gateway.h |   12 +-
 audio/manager.c |   20 +--
 audio/unix.c    |    8 +
 4 files changed, 377 insertions(+), 110 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index 3dc09ff..7e517b5 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -5,6 +5,8 @@
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
  *  Copyright (C) 2008-2009  Leonid Movshovich <event.riga@gmail.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
+ *  Copyright (C) 2010  Gustavo F. Padovan <padovan@profusion.mobi>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -32,12 +34,15 @@
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
 
 #include <glib.h>
 #include <dbus/dbus.h>
 #include <gdbus.h>
 
 #include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
 #include <bluetooth/sco.h>
@@ -52,19 +57,101 @@
 #include "btio.h"
 #include "dbus-common.h"
 
-#define RFCOMM_BUF_SIZE 256
+#define MAX_OPEN_TRIES 5
+#define OPEN_WAIT 300
+
+struct agent {
+	char *name;	/* Bus id */
+	char *path;	/* D-Bus path */
+	guint watch;	/* Disconnect watch */
+};
 
 struct gateway {
 	gateway_state_t state;
-	GIOChannel *rfcomm;
-	guint rfcomm_watch_id;
+	int channel;
+	GIOChannel *rfcomm;	/* remote AG requested rfcomm connection */
 	GIOChannel *sco;
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
-	DBusMessage *connect_message;
+	struct agent *agent;
+	DBusMessage *msg;
 };
 
-int gateway_close(struct audio_device *device);
+static DBusConnection *connection;
+
+static gboolean gateway_close(gpointer data);
+
+static const char *connected2str(int i)
+{
+	switch (i) {
+		case GATEWAY_STATE_DISCONNECTED:
+			return "disconnected";
+		case GATEWAY_STATE_CONNECTING:
+			return "connecting";
+		case GATEWAY_STATE_CONNECTED:
+			return "connected";
+		case GATEWAY_STATE_PLAYING:
+			return "playing";
+		default:
+			return "";
+	}
+}
+
+static void agent_free(struct agent *agent)
+{
+	if (!agent)
+		return;
+
+	g_free(agent->name);
+	g_free(agent->path);
+	g_free(agent);
+}
+
+static void agent_disconnect(struct agent *agent)
+{
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "Release");
+
+	dbus_message_set_no_reply(msg, TRUE);
+	g_dbus_send_message(connection, msg);
+
+	g_dbus_remove_watch(connection, agent->watch);
+}
+
+static gboolean agent_sendfd(struct agent *agent, int fd,
+		DBusPendingCallNotifyFunction notify, void *data)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "NewConnection");
+
+	dbus_message_append_args(msg, DBUS_TYPE_UNIX_FD, &fd,
+					DBUS_TYPE_INVALID);
+
+	if (dbus_connection_send_with_reply(connection, msg, &call, -1) == FALSE)
+		return FALSE;
+
+	dbus_pending_call_set_notify(call, notify, data, NULL);
+	dbus_pending_call_unref(call);
+
+	return TRUE;
+}
+
+static void change_state(struct audio_device *dev, int new_state)
+{
+	struct gateway *gw = dev->gateway;
+	const char *val = connected2str(new_state);
+
+	gw->state = new_state;
+
+	emit_property_changed(connection,dev->path,
+			AUDIO_GATEWAY_INTERFACE, "Connected",
+			DBUS_TYPE_STRING, &val);
+}
 
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
@@ -79,6 +166,7 @@ static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 		g_io_channel_shutdown(gw->sco, TRUE, NULL);
 		g_io_channel_unref(gw->sco);
 		gw->sco = NULL;
+		change_state(dev, GATEWAY_STATE_CONNECTED);
 		return FALSE;
 	}
 
@@ -92,63 +180,101 @@ static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
 
 	debug("at the begin of sco_connect_cb() in gateway.c");
 
+	gw->sco = chan;
+	g_io_channel_ref(chan);
+
 	if (err) {
 		error("sco_connect_cb(): %s", err->message);
-		/* not sure, but from other point of view,
-		 * what is the reason to have headset which
-		 * cannot play audio? */
-		if (gw->sco_start_cb)
-			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 		gateway_close(dev);
 		return;
 	}
 
-	gw->sco = g_io_channel_ref(chan);
 	if (gw->sco_start_cb)
 		gw->sco_start_cb(dev, gw->sco_start_cb_data);
 
-	/* why is this here? */
 	fcntl(g_io_channel_unix_get_fd(chan), F_SETFL, 0);
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
 				(GIOFunc) sco_io_cb, dev);
 }
 
+static void newconnection_reply(DBusPendingCall *call, void *data)
+{
+	struct gateway *gw = data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError derr;
+
+	if (!gw->rfcomm) {
+		debug("RFCOMM disconnected from server before agent reply");
+		return;
+	}
+
+	dbus_error_init(&derr);
+	if (!dbus_set_error_from_message(&derr, reply)) {
+		info("Agent reply: file descriptor passed successfuly");
+		return;
+	}
+
+	debug("Agent reply: %s", derr.message);
+
+	dbus_error_free(&derr);
+	g_idle_add(gateway_close, gw);
+}
+
 static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 				gpointer user_data)
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	gchar gw_addr[18];
-	GIOFlags flags;
+	struct rfcomm_dev_req req;
+	DBusMessage *reply;
+	int sk;
+	char src[18], dst[18];
 
 	if (err) {
 		error("connect(): %s", err->message);
 		if (gw->sco_start_cb)
 			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
-		return;
+		goto fail;
 	}
 
-	ba2str(&dev->dst, gw_addr);
-	/* Blocking mode should be default, but just in case: */
-	flags = g_io_channel_get_flags(chan);
-	flags &= ~G_IO_FLAG_NONBLOCK;
-	flags &= G_IO_FLAG_MASK;
-	g_io_channel_set_flags(chan, flags, NULL);
-	g_io_channel_set_encoding(chan, NULL, NULL);
-	g_io_channel_set_buffered(chan, FALSE);
-	if (!gw->rfcomm)
-		gw->rfcomm = g_io_channel_ref(chan);
+	ba2str(&dev->src, src);
+	ba2str(&dev->dst, dst);
 
-	if (NULL != gw->sco_start_cb)
-		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
+	/* make /dev/rfcomm serial port from chan */
+	memset(&req, 0, sizeof(req));
+	req.dev_id = -1;
+	req.flags = (1 << RFCOMM_REUSE_DLC);
+	bacpy(&req.src, &dev->src);
+	bacpy(&req.dst, &dev->dst);
+	req.channel = gw->channel;
 
-	gateway_close(dev);
+	sk = g_io_channel_unix_get_fd(chan);
+
+	change_state(dev, GATEWAY_STATE_CONNECTED);
+
+	if (!agent_sendfd(gw->agent, sk, newconnection_reply, gw)) {
+		reply = g_dbus_create_error(gw->msg, ERROR_INTERFACE ".Failed",
+				"Can not pass file descriptor");
+	} else
+		reply = dbus_message_new_method_return(gw->msg);
+
+	return;
+
+fail:
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".Failed", ".Connection attempt failed");
+		dbus_message_unref(gw->msg);
+		gw->msg = NULL;
+	}
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
+	return;
 }
 
 static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 {
 	struct audio_device *dev = user_data;
-	DBusMessage *msg = dev->gateway->connect_message;
+	struct gateway *gw = dev->gateway;
 	int ch = -1;
 	sdp_list_t *protos, *classes;
 	uuid_t uuid;
@@ -182,8 +308,6 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 
 	if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
 			uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
-		sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free,
-									NULL);
 		sdp_list_free(protos, NULL);
 		error("Invalid service record or not HFP");
 		goto fail;
@@ -197,6 +321,8 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 		goto fail;
 	}
 
+	gw->channel = ch;
+
 	io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -204,25 +330,26 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 				BT_IO_OPT_INVALID);
 	if (!io) {
 		error("Unable to connect: %s", err->message);
-		if (msg) {
-			error_common_reply(dev->conn, msg, ERROR_INTERFACE
-						".ConnectionAttemptFailed",
-						err->message);
-			msg = NULL;
-		}
-		g_error_free(err);
+		if (err)
+			g_error_free(err);
 		gateway_close(dev);
+		goto fail;
 	}
 
 	g_io_channel_unref(io);
+
+	change_state(dev, GATEWAY_STATE_CONNECTING);
 	return;
 
 fail:
-	if (msg)
-		error_common_reply(dev->conn, msg, ERROR_INTERFACE
-					".NotSupported", "Not supported");
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".NotSupported", "Not supported");
+		dbus_message_unref(gw->msg);
+		gw->msg = NULL;
+	}
 
-	dev->gateway->connect_message = NULL;
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
 
 	sco_cb = dev->gateway->sco_start_cb;
 	if (sco_cb)
@@ -243,22 +370,57 @@ static DBusMessage *ag_connect(DBusConnection *conn, DBusMessage *msg,
 {
 	struct audio_device *au_dev = (struct audio_device *) data;
 	struct gateway *gw = au_dev->gateway;
+	DBusMessage *reply;
 
 	debug("at the begin of ag_connect()");
-	if (gw->rfcomm)
+
+	if (!gw->agent)
 		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".AlreadyConnected",
-					"Already Connected");
+				".Failed", "Agent not assined");
 
-	gw->connect_message = dbus_message_ref(msg);
+	gw->msg = dbus_message_ref(msg);
 	if (get_records(au_dev) < 0) {
-		dbus_message_unref(gw->connect_message);
+		dbus_message_unref(gw->msg);
 		return g_dbus_create_error(msg, ERROR_INTERFACE
 					".ConnectAttemptFailed",
 					"Connect Attempt Failed");
 	}
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
 	debug("at the end of ag_connect()");
-	return NULL;
+
+	return reply;
+}
+
+static gboolean gateway_close(gpointer data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	gboolean value = FALSE;
+
+	if (gw->rfcomm) {
+		g_io_channel_shutdown(gw->rfcomm, TRUE, NULL);
+		g_io_channel_unref(gw->rfcomm);
+		gw->rfcomm = NULL;
+	}
+
+	if (gw->sco) {
+		g_io_channel_shutdown(gw->sco, TRUE, NULL);
+		g_io_channel_unref(gw->sco);
+		gw->sco = NULL;
+		gw->sco_start_cb = NULL;
+		gw->sco_start_cb_data = NULL;
+	}
+
+	gw->state = GATEWAY_STATE_DISCONNECTED;
+
+	emit_property_changed(device->conn, device->path,
+				AUDIO_GATEWAY_INTERFACE,
+				"Connected", DBUS_TYPE_BOOLEAN, &value);
+	return FALSE;
 }
 
 static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
@@ -269,6 +431,9 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	DBusMessage *reply = NULL;
 	char gw_addr[18];
 
+	if (!connection)
+		return NULL;
+
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
 		return NULL;
@@ -285,16 +450,117 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
+static void agent_exited(DBusConnection *conn, void *data)
+{
+	struct gateway *gateway = data;
+	struct agent *agent = gateway->agent;
+
+	debug("Agent %s exited", agent->name);
+
+	agent_free(agent);
+	gateway->agent = NULL;
+}
+
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	return NULL;
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	const char *value;
+
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	value = connected2str(gw->state);
+	dict_append_entry(&dict, "Connected",
+			DBUS_TYPE_STRING, &value);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct agent *agent;
+	const char *path, *name;
+
+
+	if (gw->agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".AlreadyExists",
+				"Agent already exists");
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+			DBUS_TYPE_INVALID))
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+				".InvalidArguments", "Invalid argument");
+
+	name = dbus_message_get_sender(msg);
+	agent = g_new0(struct agent, 1);
+	if (!agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed",
+				"Failed to create a new agent");
+
+	agent->name = strdup(name);
+	agent->path = strdup(path);
+
+	agent->watch = g_dbus_add_disconnect_watch(conn, name,
+			agent_exited, gw, NULL);
+
+	gw->agent = agent;
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+				DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct agent *agent;
+
+	if (!gw->agent)
+		goto done;
+
+	agent = gw->agent;
+	if (strcmp(agent->name, dbus_message_get_sender(msg)) != 0)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed", "Permission denied");
+
+	agent_disconnect(gw->agent);
+
+	g_dbus_remove_watch(conn, agent->watch);
+
+	agent_free(agent);
+	gw->agent = NULL;
+
+done:
+	return dbus_message_new_method_return(msg);
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
-	{ "Disconnect", "", "", ag_disconnect },
+	{ "Disconnect", "", "", ag_disconnect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
+	{ "RegisterAgent", "o", "", register_agent },
+	{ "UnregisterAgent", "o", "", unregister_agent },
 	{ NULL, NULL, NULL, NULL }
 };
 
@@ -307,17 +573,23 @@ struct gateway *gateway_init(struct audio_device *dev)
 {
 	struct gateway *gw;
 
+	gw = g_new0(struct gateway, 1);
+
+	connection = dbus_connection_ref(dev->conn);
+
 	if (!g_dbus_register_interface(dev->conn, dev->path,
 					AUDIO_GATEWAY_INTERFACE,
 					gateway_methods, gateway_signals,
 					NULL, dev, NULL))
 		return NULL;
 
-	debug("in gateway_init, dev is %p", dev);
-	gw = g_new0(struct gateway, 1);
-	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
+}
 
+void gateway_exit()
+{
+	dbus_connection_unref(connection);
+	connection = NULL;
 }
 
 gboolean gateway_is_connected(struct audio_device *dev)
@@ -326,17 +598,6 @@ gboolean gateway_is_connected(struct audio_device *dev)
 			dev->gateway->state == GATEWAY_STATE_CONNECTED);
 }
 
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
-{
-	if (!io)
-		return -EINVAL;
-
-	g_io_channel_ref(io);
-	dev->gateway->rfcomm = io;
-
-	return 0;
-}
-
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 {
 	struct gateway *gw = dev->gateway;
@@ -347,42 +608,39 @@ int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 	gw->sco = g_io_channel_ref(io);
 
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                                (GIOFunc) sco_io_cb, dev);
+				 (GIOFunc) sco_io_cb, dev);
+
+	change_state(dev, GATEWAY_STATE_PLAYING);
+
 	return 0;
 }
 
-void gateway_start_service(struct audio_device *device)
+int gateway_connect_rfcomm(struct audio_device *dev,
+		GIOChannel *chan, int ch)
 {
-	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
+	if (!chan)
+		return -EINVAL;
+
+	g_io_channel_ref(chan);
+	dev->gateway->rfcomm = chan;
+	dev->gateway->channel = ch;
+
+	return 0;
 }
 
-int gateway_close(struct audio_device *device)
+void gateway_start_service(struct audio_device *dev)
 {
-	struct gateway *gw = device->gateway;
-	GIOChannel *rfcomm = gw->rfcomm;
-	GIOChannel *sco = gw->sco;
-	gboolean value = FALSE;
+	struct gateway *gw = dev->gateway;
+	GError *err = NULL;
 
-	if (rfcomm) {
-		g_io_channel_shutdown(rfcomm, TRUE, NULL);
-		g_io_channel_unref(rfcomm);
-		gw->rfcomm = NULL;
-	}
+	if (gw->rfcomm == NULL)
+		return;
 
-	if (sco) {
-		g_io_channel_shutdown(sco, TRUE, NULL);
-		g_io_channel_unref(sco);
-		gw->sco = NULL;
-		gw->sco_start_cb = NULL;
-		gw->sco_start_cb_data = NULL;
+	if (!bt_io_accept(gw->rfcomm, rfcomm_connect_cb, dev, NULL,
+							&err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
 	}
-
-	gw->state = GATEWAY_STATE_DISCONNECTED;
-
-	emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN, &value);
-	return 0;
 }
 
 /* These are functions to be called from unix.c for audio system
@@ -399,8 +657,12 @@ gboolean gateway_request_stream(struct audio_device *dev,
 		gw->sco_start_cb_data = user_data;
 		get_records(dev);
 	} else if (!gw->sco) {
+		char source[128], destination[128];
+
 		gw->sco_start_cb = cb;
 		gw->sco_start_cb_data = user_data;
+		ba2str(&dev->src, source);
+		ba2str(&dev->dst, destination);
 		io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -418,8 +680,8 @@ gboolean gateway_request_stream(struct audio_device *dev,
 	return TRUE;
 }
 
-int gateway_config_stream(struct audio_device *dev, gateway_stream_cb_t sco_cb,
-				void *user_data)
+int gateway_config_stream(struct audio_device *dev,
+				gateway_stream_cb_t sco_cb, void *user_data)
 {
 	struct gateway *gw = dev->gateway;
 
@@ -464,4 +726,3 @@ void gateway_suspend_stream(struct audio_device *dev)
 	gw->sco_start_cb = NULL;
 	gw->sco_start_cb_data = NULL;
 }
-
diff --git a/audio/gateway.h b/audio/gateway.h
index 3b0457f..eaf6801 100644
--- a/audio/gateway.h
+++ b/audio/gateway.h
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -22,20 +23,25 @@
  *
  */
 
-#define AUDIO_GATEWAY_INTERFACE "org.bluez.HeadsetGateway"
+#define AUDIO_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
 
 #define DEFAULT_HSP_HS_CHANNEL 6
 #define DEFAULT_HFP_HS_CHANNEL 7
 
 typedef enum {
 	GATEWAY_STATE_DISCONNECTED,
-	GATEWAY_STATE_CONNECTED
+	GATEWAY_STATE_CONNECTED,
+	GATEWAY_STATE_CONNECTING,
+	GATEWAY_STATE_PLAYING,
 } gateway_state_t;
 
 typedef void (*gateway_stream_cb_t) (struct audio_device *dev, void *user_data);
+
 struct gateway *gateway_init(struct audio_device *device);
+void gateway_exit();
 gboolean gateway_is_connected(struct audio_device *dev);
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *chan);
+int gateway_connect_rfcomm(struct audio_device *dev,
+			GIOChannel *chan, int ch);
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *chan);
 void gateway_start_service(struct audio_device *device);
 gboolean gateway_request_stream(struct audio_device *dev,
diff --git a/audio/manager.c b/audio/manager.c
index 413c1f3..e768014 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -198,7 +198,7 @@ static void handle_uuid(const char *uuidstr, struct audio_device *device)
 		break;
 	case HANDSFREE_AGW_SVCLASS_ID:
 		debug("Found Handsfree AG record");
-		if (device->gateway == NULL)
+		if (enabled.gateway && (device->gateway == NULL))
 			device->gateway = gateway_init(device);
 		break;
 	case AUDIO_SINK_SVCLASS_ID:
@@ -586,7 +586,7 @@ static void hf_io_cb(GIOChannel *chan, gpointer data)
 		goto drop;
 	}
 
-	if (gateway_connect_rfcomm(device, chan) < 0) {
+	if (gateway_connect_rfcomm(device, chan, ch) < 0) {
 		error("Allocating new GIOChannel failed!");
 		goto drop;
 	}
@@ -905,22 +905,12 @@ static void headset_server_remove(struct btd_adapter *adapter)
 static int gateway_server_probe(struct btd_adapter *adapter)
 {
 	struct audio_adapter *adp;
-	const gchar *path = adapter_get_path(adapter);
-	int ret;
-
-	DBG("path %s", path);
 
 	adp = audio_adapter_get(adapter);
 	if (!adp)
 		return -EINVAL;
 
-	ret = gateway_server_init(adp);
-	if (ret < 0) {
-		audio_adapter_ref(adp);
-		return ret;
-	}
-
-	return 0;
+	return gateway_server_init(adp);
 }
 
 static void gateway_server_remove(struct btd_adapter *adapter)
@@ -1166,8 +1156,10 @@ void audio_manager_exit(void)
 		telephony_exit();
 	}
 
-	if (enabled.gateway)
+	if (enabled.gateway) {
 		btd_unregister_adapter_driver(&gateway_server_driver);
+		gateway_exit();
+	}
 
 	if (enabled.source || enabled.sink)
 		btd_unregister_adapter_driver(&a2dp_server_driver);
diff --git a/audio/unix.c b/audio/unix.c
index 5cf4f94..bd1a415 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -395,6 +395,9 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	struct bt_start_stream_rsp *rsp = (void *) buf;
 	struct bt_new_stream_ind *ind = (void *) buf;
 
+	if (!dev)
+		goto failed;
+
 	memset(buf, 0, sizeof(buf));
 	rsp->h.type = BT_RESPONSE;
 	rsp->h.name = BT_START_STREAM;
@@ -416,6 +419,11 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	}
 
 	client->req_id = 0;
+	return;
+
+failed:
+	error("gateway_resume_complete: resume failed");
+	unix_ipc_error(client, BT_START_STREAM, EIO);
 }
 
 static void headset_suspend_complete(struct audio_device *dev, void *user_data)
-- 
1.6.4.4


[-- Attachment #4: 0001-Add-HFP-support-in-oFono.patch --]
[-- Type: application/octet-stream, Size: 13008 bytes --]

From 2cf8501a52bc7c2747127d399bab41e63660283a Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 8 Jan 2010 17:25:43 -0200
Subject: [PATCH] Add HFP support in oFono

It uses BlueZ through to get HFP working following the
org.bluez.HandsfreeGateway and org.bluez.HandsfreeAgent from the BlueZ
D-Bus API.

You need the HFP patch into BlueZ too.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for its prototype
on this code.
---
 gatchat/gattty.c    |   23 ++++
 gatchat/gattty.h    |    1 +
 include/modem.h     |    2 +
 plugins/hfp.c       |  299 +++++++++++++++++++++++++++++++++++++++++++++++++--
 plugins/modemconf.c |    3 +-
 src/modem.c         |   15 +++
 6 files changed, 331 insertions(+), 12 deletions(-)

diff --git a/gatchat/gattty.c b/gatchat/gattty.c
index 02ca389..ae12662 100644
--- a/gatchat/gattty.c
+++ b/gatchat/gattty.c
@@ -260,3 +260,26 @@ GIOChannel *g_at_tty_open(const char *tty, GHashTable *options)
 
 	return channel;
 }
+
+GIOChannel *g_at_get_channel_from_fd(int fd)
+{
+	GIOChannel *channel;
+	struct termios ti;
+
+	/* Switch TTY to raw mode */
+	memset(&ti, 0, sizeof(ti));
+	cfmakeraw(&ti);
+	tcflush(fd, TCIOFLUSH);
+	tcsetattr(fd, TCSANOW, &ti);
+
+	channel = g_io_channel_unix_new(fd);
+
+	if (channel == NULL) {
+		close(fd);
+		return NULL;
+	}
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+
+	return channel;
+}
diff --git a/gatchat/gattty.h b/gatchat/gattty.h
index dc3fe16..730f1dc 100644
--- a/gatchat/gattty.h
+++ b/gatchat/gattty.h
@@ -43,6 +43,7 @@ extern "C" {
  */
 GIOChannel *g_at_tty_open(const char *tty, GHashTable *options);
 
+GIOChannel *g_at_get_channel_from_fd(int fd);
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/modem.h b/include/modem.h
index b2aa607..bf28515 100644
--- a/include/modem.h
+++ b/include/modem.h
@@ -47,6 +47,8 @@ struct ofono_modem *ofono_modem_create(const char *name, const char *type);
 int ofono_modem_register(struct ofono_modem *modem);
 void ofono_modem_remove(struct ofono_modem *modem);
 
+struct ofono_modem *ofono_modem_get_by_path(const char *path);
+
 void ofono_modem_set_powered(struct ofono_modem *modem, ofono_bool_t powered);
 ofono_bool_t ofono_modem_get_powered(struct ofono_modem *modem);
 
diff --git a/plugins/hfp.c b/plugins/hfp.c
index 3bbd922..2e5e2a1 100644
--- a/plugins/hfp.c
+++ b/plugins/hfp.c
@@ -30,6 +30,8 @@
 #include <glib.h>
 #include <gatchat.h>
 #include <gattty.h>
+#include <gdbus.h>
+#include <ofono.h>
 
 #define OFONO_API_SUBJECT_TO_CHANGE
 #include <ofono/plugin.h>
@@ -52,11 +54,28 @@
 
 #include <drivers/hfpmodem/hfpmodem.h>
 
+#include <ofono/dbus.h>
+
+#define BLUEZ_SERVICE "org.bluez"
+#define BLUEZ_PATH "/"
+#define BLUEZ_MANAGER_INTERFACE "org.bluez.Manager"
+#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter"
+#define BLUEZ_DEVICE_INTERFACE "org.bluez.Device"
+#define BLUEZ_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
+
+#define HFP_AGENT_INTERFACE "org.bluez.HandsfreeAgent"
+
 static const char *brsf_prefix[] = { "+BRSF:", NULL };
 static const char *cind_prefix[] = { "+CIND:", NULL };
 static const char *cmer_prefix[] = { "+CMER:", NULL };
 static const char *chld_prefix[] = { "+CHLD:", NULL };
 
+static DBusConnection *connection;
+static char *ofono_handsfree_path;
+
+
+static int timeout;
+
 static int hfp_disable(struct ofono_modem *modem);
 
 static void hfp_debug(const char *str, void *user_data)
@@ -135,6 +154,60 @@ static void cmer_cb(gboolean ok, GAtResult *result, gpointer user_data)
 		sevice_level_conn_established(modem);
 }
 
+static int send_method_call(const char *dest, const char *path,
+				const char *interface, const char *method,
+				DBusPendingCallNotifyFunction cb,
+				void *user_data, int type, ...)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+	va_list args;
+
+	msg = dbus_message_new_method_call(dest, path, interface, method);
+	if (!msg) {
+		ofono_error("Unable to allocate new D-Bus %s message", method);
+		return -ENOMEM;
+	}
+
+	va_start(args, type);
+
+	if (!dbus_message_append_args_valist(msg, type, args)) {
+		dbus_message_unref(msg);
+		va_end(args);
+		return -EIO;
+	}
+
+	va_end(args);
+
+	if (!cb) {
+		g_dbus_send_message(connection, msg);
+		return 0;
+	}
+
+	if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+		ofono_error("Sending %s failed", method);
+		dbus_message_unref(msg);
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(call, cb, user_data, NULL);
+	dbus_pending_call_unref(call);
+	dbus_message_unref(msg);
+
+	return 0;
+}
+
+static gboolean hfp_enable_timeout(gpointer user)
+{
+	struct ofono_modem *modem = user;
+
+	if (ofono_modem_get_powered(modem))
+		return FALSE;
+
+	hfp_disable(modem);
+	return FALSE;
+}
+
 static void cind_status_cb(gboolean ok, GAtResult *result,
 				gpointer user_data)
 {
@@ -260,8 +333,7 @@ error:
 }
 
 /* either oFono or Phone could request SLC connection */
-static int service_level_connection(struct ofono_modem *modem,
-				const char *tty)
+static int service_level_connection(struct ofono_modem *modem, int fd)
 {
 	struct hfp_data *data = ofono_modem_get_data(modem);
 	GIOChannel *io;
@@ -269,7 +341,7 @@ static int service_level_connection(struct ofono_modem *modem,
 	GAtChat *chat;
 	char buf[64];
 
-	io = g_at_tty_open(tty, NULL);
+	io = g_at_get_channel_from_fd(fd);
 	if (!io) {
 		ofono_error("Service level connection failed: %s (%d)",
 			strerror(errno), errno);
@@ -296,9 +368,173 @@ static int service_level_connection(struct ofono_modem *modem,
 	return -EINPROGRESS;
 }
 
+static DBusMessage *hfp_agent_new_connection(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	int fd;
+	const char *path;
+	struct ofono_modem *modem = NULL;
+
+	path = dbus_message_get_path(msg);
+	modem = ofono_modem_get_by_path(path);
+
+	if (!modem)
+		return NULL;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UNIX_FD, &fd,
+				DBUS_TYPE_INVALID))
+		return __ofono_error_invalid_args(msg);
+
+	service_level_connection(modem, fd);
+
+	return NULL;
+}
+
+static DBusMessage *hfp_agent_release(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	return NULL;
+}
+
+static GDBusMethodTable agent_methods[] = {
+        { "NewConnection", "h", "", hfp_agent_new_connection,
+		G_DBUS_METHOD_FLAG_ASYNC },
+	{ "Release", "", "", hfp_agent_release },
+	{NULL, NULL, NULL, NULL}
+};
+
+static void agent_registered_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusMessage *reply;
+
+	reply = dbus_pending_call_steal_reply(call);
+	dbus_message_unref(reply);
+}
+
+static void agent_unregistered_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusMessage *reply;
+
+	reply = dbus_pending_call_steal_reply(call);
+	dbus_message_unref(reply);
+}
+
+static void find_device_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	struct ofono_modem *modem = user_data;
+	const char *device, *obj_path;
+	int ret;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH,
+				&device, DBUS_TYPE_INVALID) == FALSE) {
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s", err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	ofono_debug("Using device %s", device);
+	ofono_handsfree_path = g_strdup(device);
+
+	obj_path = ofono_modem_get_path(modem);
+
+	ret = send_method_call(BLUEZ_SERVICE, device,
+				BLUEZ_GATEWAY_INTERFACE, "RegisterAgent",
+				agent_registered_cb, NULL, DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+
+	if (ret < 0)
+		ofono_error("agent_register failed(%d)", ret);
+
+done:
+	dbus_message_unref(reply);;
+}
+
+static void get_adapter_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	struct ofono_modem *modem = user_data;
+	const char *adapter = NULL;
+	const char *address;
+	int ret;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+	if (dbus_message_get_args(reply, &err,
+				DBUS_TYPE_OBJECT_PATH,
+				&adapter, DBUS_TYPE_INVALID) == FALSE) {
+		if (adapter == NULL) {
+			ofono_error("bluetooth adapter is not enabled");
+			dbus_error_free(&err);
+			goto done;
+		}
+
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s %s", adapter, err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	address = ofono_modem_get_string(modem, "Address");
+
+	DBG("address %s", address);
+
+	if (address == NULL)
+		return;
+
+	ret = send_method_call(BLUEZ_SERVICE, adapter,
+				BLUEZ_ADAPTER_INTERFACE, "FindDevice",
+				find_device_cb, modem,
+				DBUS_TYPE_STRING, &address,
+				DBUS_TYPE_INVALID);
+
+	if (ret < 0)
+		ofono_error("find_device failed(%d)", ret);
+
+done:
+	dbus_message_unref(reply);
+
+}
+
+static int hfp_register_ofono_handsfree(struct ofono_modem *modem)
+{
+	ofono_debug("Register oFono Agent to bluetooth daemon");
+
+	return send_method_call(BLUEZ_SERVICE, BLUEZ_PATH,
+				BLUEZ_MANAGER_INTERFACE, "DefaultAdapter",
+				get_adapter_cb, modem,
+				DBUS_TYPE_INVALID);
+}
+
+static int hfp_unregister_ofono_handsfree(struct ofono_modem *modem)
+{
+	const char *obj_path = ofono_modem_get_path(modem);
+
+	ofono_debug("Unregister oFono Agent to bluetooth daemon");
+
+	if (!ofono_handsfree_path)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, ofono_handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "UnregisterAgent",
+				agent_unregistered_cb, NULL,
+				DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+}
+
 static int hfp_probe(struct ofono_modem *modem)
 {
 	struct hfp_data *data;
+	const char *obj_path;
+	DBusConnection *conn = ofono_dbus_get_connection();
 
 	data = g_try_new0(struct hfp_data, 1);
 	if (!data)
@@ -312,6 +548,15 @@ static int hfp_probe(struct ofono_modem *modem)
 
 	ofono_modem_set_data(modem, data);
 
+	connection = dbus_connection_ref(conn);
+
+	obj_path = ofono_modem_get_path(modem);
+	g_dbus_register_interface(conn, obj_path, HFP_AGENT_INTERFACE,
+			agent_methods, NULL, NULL, data, NULL);
+
+	if (hfp_register_ofono_handsfree(modem) != 0)
+		return -EINVAL;
+
 	return 0;
 }
 
@@ -322,24 +567,57 @@ static void hfp_remove(struct ofono_modem *modem)
 	if (data)
 		g_free(data);
 
+	hfp_unregister_ofono_handsfree(modem);
+
+	if (ofono_handsfree_path)
+		g_free(ofono_handsfree_path);
+
+	dbus_connection_unref(connection);
+
 	ofono_modem_set_data(modem, NULL);
 }
 
+static void port_connect_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusMessage *reply;
+
+	reply = dbus_pending_call_steal_reply(call);
+	dbus_message_unref(reply);
+}
+
+static int hfp_connect_ofono_handsfree()
+{
+	ofono_debug("Connect to bluetooth daemon");
+
+	if (!ofono_handsfree_path || !connection)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, ofono_handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Connect",
+				port_connect_cb, NULL, DBUS_TYPE_INVALID);
+}
+
 /* power up hardware */
 static int hfp_enable(struct ofono_modem *modem)
 {
-	const char *tty;
-	int ret;
-
 	DBG("%p", modem);
 
-	tty = ofono_modem_get_string(modem, "Device");
-	if (tty == NULL)
+	timeout = g_timeout_add_seconds(10, hfp_enable_timeout, modem);
+
+	if (hfp_connect_ofono_handsfree() < 0)
 		return -EINVAL;
 
-	ret = service_level_connection(modem, tty);
+	return -EINPROGRESS;
+}
+
+static int hfp_disconnect_ofono_handsfree()
+{
+	if (!ofono_handsfree_path || !connection)
+		return -EINVAL;
 
-	return ret;
+	return send_method_call(BLUEZ_SERVICE, ofono_handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Disconnect",
+				NULL, NULL, DBUS_TYPE_INVALID);
 }
 
 static int hfp_disable(struct ofono_modem *modem)
@@ -359,6 +637,7 @@ static int hfp_disable(struct ofono_modem *modem)
 
 	ofono_modem_set_powered(modem, FALSE);
 
+	hfp_disconnect_ofono_handsfree();
 	return 0;
 }
 
diff --git a/plugins/modemconf.c b/plugins/modemconf.c
index c8c261f..b4c1600 100644
--- a/plugins/modemconf.c
+++ b/plugins/modemconf.c
@@ -124,12 +124,11 @@ static struct ofono_modem *create_modem(GKeyFile *keyfile, const char *group)
 
 	modem = ofono_modem_create(group, driver);
 
-	if (!g_strcmp0(driver, "phonesim"))
+	if (!g_strcmp0(driver, "phonesim") || !g_strcmp0(driver, "hfp"))
 		set_address(modem, keyfile, group);
 
 	if (!g_strcmp0(driver, "atgen") || !g_strcmp0(driver, "g1") ||
 						!g_strcmp0(driver, "calypso") ||
-						!g_strcmp0(driver, "hfp") ||
 						!g_strcmp0(driver, "palmpre"))
 		set_device(modem, keyfile, group);
 
diff --git a/src/modem.c b/src/modem.c
index bbc9905..9fab722 100644
--- a/src/modem.c
+++ b/src/modem.c
@@ -1074,6 +1074,21 @@ struct ofono_modem *ofono_modem_create(const char *name, const char *type)
 	return modem;
 }
 
+struct ofono_modem *ofono_modem_get_by_path(const char *path)
+{
+	GSList *l;
+	struct ofono_modem *modem;
+
+	for (l = g_modem_list; l; l = l->next) {
+		modem = l->data;
+
+		if (!g_strcmp0(modem->path, path))
+			return modem;
+	}
+
+	return NULL;
+}
+
 static void emit_modems()
 {
 	DBusConnection *conn = ofono_dbus_get_connection();
-- 
1.6.4.4


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

* Re: [RFC] HFP support into oFono and BlueZ
  2010-01-11 17:08 ` Gustavo F. Padovan
  (?)
  (?)
@ 2010-01-13  0:54 ` Denis Kenzior
  2010-01-13  1:44   ` Marcel Holtmann
  -1 siblings, 1 reply; 51+ messages in thread
From: Denis Kenzior @ 2010-01-13  0:54 UTC (permalink / raw)
  To: ofono

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

Hi Padovan,

Initial impressions:

+static DBusConnection *connection;
+static char *ofono_handsfree_path;
+
+
+static int timeout;
+

These globals seem to belong in per-modem user data unless you only want 1 HFP 
device per system?

+
+GIOChannel *g_at_get_channel_from_fd(int fd)
+{
+	GIOChannel *channel;
+	struct termios ti;
+
+	/* Switch TTY to raw mode */
+	memset(&ti, 0, sizeof(ti));
+	cfmakeraw(&ti);
+	tcflush(fd, TCIOFLUSH);
+	tcsetattr(fd, TCSANOW, &ti);
+
+	channel = g_io_channel_unix_new(fd);
+
+	if (channel == NULL) {
+		close(fd);
+		return NULL;
+	}
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+
+	return channel;
+}

Seriously, what are you trying to do here?  If you want a GIOChannel simply 
use g_io_channel_unix_new(fd) and pass it to gatchat.  See plugins/phonesim.c 
phonesim_enable() for details.  That operates on a TCP socket.

+struct ofono_modem *ofono_modem_get_by_path(const char *path);
+

You don't need this, see below.

+	g_dbus_register_interface(conn, obj_path, HFP_AGENT_INTERFACE,
+			agent_methods, NULL, NULL, data, NULL);
+

You can pass the modem object as userdata for the agent when registering the 
interface.  The modem will thus be available as 'data' in 
hfp_agent_new_connection, etc.

Regards,
-Denis

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

* Re: [RFC] HFP support into oFono and BlueZ
  2010-01-13  0:54 ` [RFC] HFP support into oFono and BlueZ Denis Kenzior
@ 2010-01-13  1:44   ` Marcel Holtmann
  0 siblings, 0 replies; 51+ messages in thread
From: Marcel Holtmann @ 2010-01-13  1:44 UTC (permalink / raw)
  To: ofono

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

Hi Denis,

> Initial impressions:
> 
> +static DBusConnection *connection;
> +static char *ofono_handsfree_path;
> +
> +
> +static int timeout;
> +
> 
> These globals seem to belong in per-modem user data unless you only want 1 HFP 
> device per system?

the DBusConnection is fine as a global since it is shared for the whole
process anyway.

For the other two, I agree, they have to be per modem.

Regards

Marcel



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

* Re: [RFC] HFP support into oFono and BlueZ
  2010-01-11 19:05   ` Gustavo F. Padovan
@ 2010-01-13 23:39     ` Gustavo F. Padovan
  -1 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-13 23:39 UTC (permalink / raw)
  To: linux-bluetooth, ofono, zhenhua.zhang

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

Hi,

On Mon, Jan 11, 2010 at 5:05 PM, Gustavo F. Padovan
<padovan@profusion.mobi> wrote:
> On Mon, Jan 11, 2010 at 3:08 PM, Gustavo F. Padovan
> <padovan@profusion.mobi> wrote:
>> Hi,
>>
>> These patches implement the new API for the Audio Gateway in BlueZ. It
>> follows the last version of the HandsfreeGateway and HandsfreeAgent
>> Intefaces API.
>> The first two patches is for BlueZ and the other for oFono. You can
>> test it with using enable-modem and test-voicecall scripts into the
>> test dir of oFono.
>> Feel free to test it and send me your comments. We have some bugs yet.
>
> New version of the patches: now we do not handle tty stuff on the BlueZ side.

New version of the patches. Known issues:
"'org.bluez.HandsfreeAgent.Release' part not implemented" and it's not
working with more than one bluetooth devices in some cases. Comments
are welcome.

>
>>
>> The audio part is not working yet. We are going to work on pulseaudio
>> this week to get this done soon.
>>
>> Regards,
>>
>> --
>> Gustavo F. Padovan
>> ProFUSION embedded systems - http://profusion.mobi
>>
>
>
>
> --
> Gustavo F. Padovan
> ProFUSION embedded systems - http://profusion.mobi
>



-- 
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi

[-- Attachment #2: 0001-clean-up-audio-gateway.c.patch --]
[-- Type: application/octet-stream, Size: 24620 bytes --]

From 7eea257a7b4a0cbc026aab6a60df9bb580065831 Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 6 Nov 2009 18:08:58 -0200
Subject: [PATCH 1/2] clean up audio/gateway.c

remove all code related to the AT engine
---
 audio/gateway.c |  760 +------------------------------------------------------
 1 files changed, 1 insertions(+), 759 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index a1c1ea2..3dc09ff 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -53,52 +53,6 @@
 #include "dbus-common.h"
 
 #define RFCOMM_BUF_SIZE 256
-/* not-more-then-16 defined by GSM + 1 for NULL + padding */
-#define AG_INDICATOR_DESCR_SIZE 20
-#define AG_CALLER_NUM_SIZE 64	/* size of number + type */
-
-/* commands */
-#define AG_FEATURES "AT+BRSF=26\r"     /* = 0x7F = All features supported */
-#define AG_INDICATORS_SUPP "AT+CIND=?\r"
-#define AG_INDICATORS_VAL "AT+CIND?\r"
-#define AG_INDICATORS_ENABLE "AT+CMER=3,0,0,1\r"
-#define AG_HOLD_MPTY_SUPP "AT+CHLD=?\r"
-#define AG_CALLER_IDENT_ENABLE "AT+CLIP=1\r"
-#define AG_CARRIER_FORMAT "AT+COPS=3,0\r"
-#define AG_EXTENDED_RESULT_CODE "AT+CMEE=1\r"
-
-#define AG_FEATURE_3WAY 0x1
-#define AG_FEATURE_EXTENDED_RES_CODE 0x100
-/* Hold and multipary AG features.
- * Comments below are copied from hands-free spec for reference */
-/* Releases all held calls or sets User Determined User Busy (UDUB)
- * for a waiting call */
-#define AG_CHLD_0 0x01
-/* Releases all active calls (if any exist) and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_1 0x02
-/* Releases specified active call only <x> */
-#define AG_CHLD_1x 0x04
-/* Places all active calls (if any exist) on hold and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_2 0x08
-/* Request private consultation mode with specified call <x> (Place all
- * calls on hold EXCEPT the call <x>) */
-#define AG_CHLD_2x 0x10
-/* Adds a held call to the conversation */
-#define AG_CHLD_3 0x20
-/* Connects the two calls and disconnects the subscriber from both calls
- * (Explicit Call Transfer). Support for this value and its associated
- * functionality is optional for the HF. */
-#define AG_CHLD_4 0x40
-
-#define OK_RESPONSE "\r\nOK\r\n"
-#define ERROR_RESPONSE "\r\nERROR\r\n"
-
-struct indicator {
-	gchar descr[AG_INDICATOR_DESCR_SIZE];
-	gint value;
-};
 
 struct gateway {
 	gateway_state_t state;
@@ -108,387 +62,10 @@ struct gateway {
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
 	DBusMessage *connect_message;
-	guint ag_features;
-	guint hold_multiparty_features;
-	GSList *indies;
-	gboolean is_dialing;
-	gboolean call_active;
-
-	int sp_gain;
-	int mic_gain;
 };
 
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device);
-
 int gateway_close(struct audio_device *device);
 
-static void rfcomm_start_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	gw->rfcomm_watch_id = g_io_add_watch(gw->rfcomm,
-			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-			(GIOFunc) rfcomm_ag_data_cb, dev);
-}
-
-static void rfcomm_stop_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	g_source_remove(gw->rfcomm_watch_id);
-}
-
-static gboolean io_channel_write_all(GIOChannel *io, gchar *data,
-					gsize count)
-{
-	gsize written = 0;
-	GIOStatus status;
-
-	while (count > 0) {
-		status = g_io_channel_write_chars(io, data, count, &written,
-						NULL);
-		if (status != G_IO_STATUS_NORMAL)
-			return FALSE;
-
-		data += written;
-		count -= written;
-	}
-	return TRUE;
-}
-
-/* it's worth to mention that data and response could be the same pointers */
-static gboolean rfcomm_send_and_read(struct gateway *gw, gchar *data,
-                                    gchar *response, gsize count)
-{
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read = 0;
-	gboolean got_ok = FALSE;
-	gboolean got_error = FALSE;
-	gchar *resp_buf = response;
-	gsize toread = RFCOMM_BUF_SIZE - 1;
-	GIOStatus status;
-
-	if (!io_channel_write_all(rfcomm, data, count))
-		return FALSE;
-
-	while (!(got_ok || got_error)) {
-		status = g_io_channel_read_chars(rfcomm, resp_buf, toread,
-						&read, NULL);
-		if (status == G_IO_STATUS_NORMAL)
-			resp_buf[read] = '\0';
-		else {
-			debug("rfcomm_send_and_read(): %m");
-			return FALSE;
-		}
-		got_ok = NULL != strstr(resp_buf, OK_RESPONSE);
-		got_error = NULL != strstr(resp_buf, ERROR_RESPONSE);
-		resp_buf += read;
-		toread -= read;
-	}
-	return TRUE;
-}
-
-/* get <descr> from the names: (<descr>, (<values>)), (<descr>, (<values>))
- * ... */
-static GSList *parse_indicator_names(gchar *names, GSList *indies)
-{
-	gchar *current = names - 1;
-	GSList *result = indies;
-	gchar *next;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 2;
-		next = strstr(current, ",(");
-		ind = g_slice_new(struct indicator);
-		strncpy(ind->descr, current, 20);
-		ind->descr[(intptr_t) next - (intptr_t) current] = '\0';
-		result = g_slist_append(result, (gpointer) ind);
-		current = strstr(next + 1, ",(");
-	}
-	return result;
-}
-
-/* get values from <val0>,<val1>,... */
-static GSList *parse_indicator_values(gchar *values, GSList *indies)
-{
-	gint val;
-	gchar *current = values - 1;
-	GSList *runner = indies;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 1;
-		sscanf(current, "%d", &val);
-		current = strchr(current, ',');
-		ind = g_slist_nth_data(runner, 0);
-		ind->value = val;
-		runner = g_slist_next(runner);
-	}
-	return indies;
-}
-
-/* get values from <val0>,<val1>,... */
-static guint get_hold_mpty_features(gchar *features)
-{
-	guint result = 0;
-
-	if (strstr(features, "0"))
-		result |= AG_CHLD_0;
-
-	if (strstr(features, "1"))
-		result |= AG_CHLD_1;
-
-	if (strstr(features, "1x"))
-		result |= AG_CHLD_1x;
-
-	if (strstr(features, "2"))
-		result |= AG_CHLD_2;
-
-	if (strstr(features, "2x"))
-		result |= AG_CHLD_2x;
-
-	if (strstr(features, "3"))
-		result |= AG_CHLD_3;
-
-	if (strstr(features, "4"))
-		result |= AG_CHLD_4;
-
-	return result;
-}
-
-static gboolean establish_service_level_conn(struct gateway *gw)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	gboolean res;
-
-	debug("at the begin of establish_service_level_conn()");
-	res = rfcomm_send_and_read(gw, AG_FEATURES, buf,
-				sizeof(AG_FEATURES) - 1);
-	if (!res || sscanf(buf, "\r\n+BRSF:%d", &gw->ag_features) != 1)
-		return FALSE;
-
-	debug("features are 0x%X", gw->ag_features);
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_SUPP, buf,
-				sizeof(AG_INDICATORS_SUPP) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_names(strchr(buf, '('), NULL);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_VAL, buf,
-		sizeof(AG_INDICATORS_VAL) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_values(strchr(buf, ':') + 1, gw->indies);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_ENABLE, buf,
-				sizeof(AG_INDICATORS_ENABLE) - 1);
-	if (!res || !strstr(buf, "OK"))
-		return FALSE;
-
-	if ((gw->ag_features & AG_FEATURE_3WAY) != 0) {
-		res = rfcomm_send_and_read(gw, AG_HOLD_MPTY_SUPP, buf,
-				sizeof(AG_HOLD_MPTY_SUPP) - 1);
-		if (!res || !strstr(buf, "+CHLD:")) {
-			g_slice_free1(RFCOMM_BUF_SIZE, buf);
-			return FALSE;
-		}
-		gw->hold_multiparty_features = get_hold_mpty_features(
-							strchr(buf, '('));
-
-	} else
-		gw->hold_multiparty_features = 0;
-
-	debug("Service layer connection successfully established!");
-	rfcomm_send_and_read(gw, AG_CALLER_IDENT_ENABLE, buf,
-			sizeof(AG_CALLER_IDENT_ENABLE) - 1);
-	rfcomm_send_and_read(gw, AG_CARRIER_FORMAT, buf,
-			sizeof(AG_CARRIER_FORMAT) - 1);
-	if ((gw->ag_features & AG_FEATURE_EXTENDED_RES_CODE) != 0)
-		rfcomm_send_and_read(gw, AG_EXTENDED_RESULT_CODE, buf,
-			sizeof(AG_EXTENDED_RESULT_CODE) - 1);
-
-	return TRUE;
-}
-
-static void process_ind_change(struct audio_device *dev, guint index,
-							gint value)
-{
-	struct gateway *gw = dev->gateway;
-	struct indicator *ind = g_slist_nth_data(gw->indies, index - 1);
-	gchar *name = ind->descr;
-
-	ind->value = value;
-
-	debug("at the begin of process_ind_change, name is %s", name);
-	if (!strcmp(name, "\"call\"")) {
-		if (value > 0) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallStarted", DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-			gw->call_active = TRUE;
-		} else {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallEnded", DBUS_TYPE_INVALID);
-			gw->call_active = FALSE;
-		}
-
-	} else if (!strcmp(name, "\"callsetup\"")) {
-		if (value == 0 && gw->is_dialing) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-		} else if (!gw->is_dialing && value > 0)
-			gw->is_dialing = TRUE;
-
-	} else if (!strcmp(name, "\"callheld\"")) {
-		/* FIXME: The following code is based on assumptions only.
-		 * Has to be tested for interoperability
-		 * I assume that callheld=2 would be sent when dial from HF
-		 * failed in case of 3-way call
-		 * Unfortunately this path is not covered by the HF spec so
-		 * the code has to be tested for interop
-		*/
-		/* '2' means: all calls held, no active calls */
-		if (value == 2) {
-			if (gw->is_dialing) {
-				g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-				gw->is_dialing = FALSE;
-			}
-		}
-	} else if (!strcmp(name, "\"service\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RegistrationStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"signal\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "SignalStrength",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"roam\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RoamingStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"battchg\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "BatteryCharge",
-				DBUS_TYPE_UINT16, &value);
-}
-
-static void process_ring(struct audio_device *device, GIOChannel *chan,
-			gchar *buf)
-{
-	gchar number[AG_CALLER_NUM_SIZE];
-	gchar *cli;
-	gchar *sep;
-	gsize read;
-	GIOStatus status;
-
-	rfcomm_stop_watch(device);
-	status = g_io_channel_read_chars(chan, buf, RFCOMM_BUF_SIZE - 1, &read, NULL);
-	if (status != G_IO_STATUS_NORMAL)
-		return;
-
-	debug("at the begin of process_ring");
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 10)
-		error("process_ring(): buf is too long '%s'", buf);
-	else if ((cli = strstr(buf, "\r\n+CLIP"))) {
-		if (sscanf(cli, "\r\n+CLIP: \"%s", number) == 1) {
-			sep = strchr(number, '"');
-			sep[0] = '\0';
-
-			/* FIXME:signal will be emitted on each RING+CLIP.
-			 * That's bad */
-			cli = number;
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE, "Ring",
-					DBUS_TYPE_STRING, &cli,
-					DBUS_TYPE_INVALID);
-			device->gateway->is_dialing = TRUE;
-		} else
-			error("process_ring(): '%s' in place of +CLIP after RING", buf);
-
-	}
-
-	rfcomm_start_watch(device);
-}
-
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	struct gateway *gw;
-	gsize read;
-	/* some space for value */
-	gchar indicator[AG_INDICATOR_DESCR_SIZE + 4];
-	gint value;
-	guint index;
-	gchar *sep;
-
-	debug("at the begin of rfcomm_ag_data_cb()");
-	if (cond & G_IO_NVAL)
-		return FALSE;
-
-	gw = device->gateway;
-
-	if (cond & (G_IO_ERR | G_IO_HUP)) {
-		debug("connection with remote BT is closed");
-		gateway_close(device);
-		return FALSE;
-	}
-
-	if (g_io_channel_read_chars(chan, buf, sizeof(buf) - 1, &read, NULL)
-			!= G_IO_STATUS_NORMAL)
-		return TRUE;
-	buf[read] = '\0';
-
-	if (strlen(buf) > AG_INDICATOR_DESCR_SIZE + 14)
-		error("rfcomm_ag_data_cb(): buf is too long '%s'", buf);
-	else if (sscanf(buf, "\r\n+CIEV:%s\r\n", indicator) == 1) {
-		sep = strchr(indicator, ',');
-		sep[0] = '\0';
-		sep += 1;
-		index = atoi(indicator);
-		value = atoi(sep);
-		process_ind_change(device, index, value);
-	} else if (strstr(buf, "RING"))
-		process_ring(device, chan, buf);
-	else if (sscanf(buf, "\r\n+BVRA:%d\r\n", &value) == 1) {
-		if (value == 0)
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionActive",
-					DBUS_TYPE_INVALID);
-		else
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionInactive",
-					DBUS_TYPE_INVALID);
-	} else if (sscanf(buf, "\r\n+VGS:%d\r\n", &value) == 1) {
-		gw->sp_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "SpeakerGain",
-				DBUS_TYPE_UINT16, &value);
-	} else if (sscanf(buf, "\r\n+VGM:%d\r\n", &value) == 1) {
-		gw->mic_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "MicrophoneGain",
-				DBUS_TYPE_UINT16, &value);
-	} else
-		error("rfcomm_ag_data_cb(): read wrong data '%s'", buf);
-
-	return TRUE;
-}
-
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
 {
@@ -541,7 +118,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	DBusMessage *conn_mes = gw->connect_message;
 	gchar gw_addr[18];
 	GIOFlags flags;
 
@@ -563,29 +139,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 	if (!gw->rfcomm)
 		gw->rfcomm = g_io_channel_ref(chan);
 
-	if (establish_service_level_conn(dev->gateway)) {
-		gboolean value = TRUE;
-
-		debug("%s: Connected to %s", dev->path, gw_addr);
-		rfcomm_start_watch(dev);
-		if (conn_mes) {
-			DBusMessage *reply =
-				dbus_message_new_method_return(conn_mes);
-			dbus_connection_send(dev->conn, reply, NULL);
-			dbus_message_unref(reply);
-			dbus_message_unref(conn_mes);
-			gw->connect_message = NULL;
-		}
-
-		gw->state = GATEWAY_STATE_CONNECTED;
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN,	&value);
-		return;
-	} else
-		error("%s: Failed to establish service layer connection to %s",
-			dev->path, gw_addr);
-
 	if (NULL != gw->sco_start_cb)
 		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 
@@ -732,321 +285,20 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
-static DBusMessage *process_ag_reponse(DBusMessage *msg, gchar *response)
-{
-	DBusMessage *reply;
-
-
-	debug("in process_ag_reponse, response is %s", response);
-	if (strstr(response, OK_RESPONSE))
-		reply = dbus_message_new_method_return(msg);
-	else {
-		/* FIXME: some code should be here to processes errors
-		 *  in better fasion */
-		debug("AG responded with '%s' to %s method call", response,
-				dbus_message_get_member(msg));
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".OperationFailed",
-					"Operation failed.See log for details");
-	}
-	return reply;
-}
-
-static DBusMessage *process_simple(DBusMessage *msg, struct audio_device *dev,
-					gchar *data)
-{
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-
-	rfcomm_stop_watch(dev);
-	rfcomm_send_and_read(gw, data, buf, strlen(data));
-	rfcomm_start_watch(dev);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_ANSWER "ATA\r"
-
-static DBusMessage *ag_answer(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	if (gw->call_active)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".CallAlreadyAnswered",
-					"Call AlreadyAnswered");
-
-	return process_simple(msg, dev, AG_ANSWER);
-}
-
-#define AG_HANGUP "AT+CHUP\r"
-
-static DBusMessage *ag_terminate_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	return process_simple(msg, dev, AG_HANGUP);
-}
-
-/* according to GSM spec */
-#define ALLOWED_NUMBER_SYMBOLS "1234567890*#ABCD"
-#define AG_PLACE_CALL "ATD%s;\r"
-/* dialing from memory is not supported as headset spec doesn't define a way
- * to retreive phone memory entries.
- */
-static DBusMessage *ag_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint atd_len;
-	DBusMessage *result;
-
-	debug("at the begin of ag_call()");
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	atd_len = sprintf(buf, AG_PLACE_CALL, number);
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, buf, buf, atd_len);
-	rfcomm_start_watch(device);
-
-	result = process_ag_reponse(msg, buf);
-	return result;
-}
-
-#define AG_GET_CARRIER "AT+COPS?\r"
-
-static DBusMessage *ag_get_operator(DBusConnection *conn, DBusMessage *msg,
-					void *data)
-{
-	struct audio_device *dev = (struct audio_device *) data;
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read;
-	gchar *result, *sep;
-	DBusMessage *reply;
-	GIOStatus status;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(dev);
-	io_channel_write_all(rfcomm, AG_GET_CARRIER, strlen(AG_GET_CARRIER));
-
-	status = g_io_channel_read_chars(rfcomm, buf, RFCOMM_BUF_SIZE - 1,
-						&read, NULL);
-	rfcomm_start_watch(dev);
-	if (G_IO_STATUS_NORMAL == status) {
-		buf[read] = '\0';
-		if (strstr(buf, "+COPS")) {
-			if (!strrchr(buf, ','))
-				result = "0";
-			else {
-				result = strchr(buf, '\"') + 1;
-				sep = strchr(result, '\"');
-				sep[0] = '\0';
-			}
-
-			reply = dbus_message_new_method_return(msg);
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&result, DBUS_TYPE_INVALID);
-		} else {
-			info("ag_get_operator(): '+COPS' expected but"
-				" '%s' received", buf);
-			reply = dbus_message_new_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Unexpected response from AG");
-		}
-	} else {
-		error("ag_get_operator(): %m");
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".ConnectionFailed",
-					"Failed to receive response from AG");
-	}
-
-	return reply;
-}
-
-#define AG_SEND_DTMF "AT+VTS=%c\r"
-static DBusMessage *ag_send_dtmf(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint com_len;
-	gboolean got_ok = TRUE;
-	gint num_len;
-	gint i = 0;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	num_len = strlen(number);
-	rfcomm_stop_watch(device);
-	while (i < num_len && got_ok) {
-		com_len = sprintf(buf, AG_SEND_DTMF, number[i]);
-		rfcomm_send_and_read(gw, buf, buf, com_len);
-		got_ok = NULL != strstr(buf, OK_RESPONSE);
-		i += 1;
-	}
-	rfcomm_start_watch(device);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_GET_SUBSCRIBER_NUMS "AT+CNUM\r"
-#define CNUM_LEN 5             /* length of "+CNUM" string */
-#define MAX_NUMBER_CNT 16
-static DBusMessage *ag_get_subscriber_num(DBusConnection *conn,
-					DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number, *end;
-	DBusMessage *reply = dbus_message_new_method_return(msg);
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, AG_GET_SUBSCRIBER_NUMS, buf,
-			strlen(AG_GET_SUBSCRIBER_NUMS));
-	rfcomm_start_watch(device);
-
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 21)
-		error("ag_get_subscriber_num(): buf is too long '%s'", buf);
-	else if (strstr(buf, "+CNUM")) {
-		number = strchr(buf, ',');
-		number++;
-		end = strchr(number, ',');
-		if (end) {
-			*end = '\0';
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&number, DBUS_TYPE_INVALID);
-		}
-	} else
-		error("ag_get_subscriber_num(): read wrong data '%s'", buf);
-
-	return reply;
-}
-
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	DBusMessage *reply;
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	gboolean value;
-	guint index = 0;
-	struct indicator *ind;
-
-	reply = dbus_message_new_method_return(msg);
-	if (!reply)
-		return NULL;
-
-	dbus_message_iter_init_append(reply, &iter);
-
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
-	/* Connected */
-	value = gateway_is_connected(device);
-	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
-
-	if (!value)
-		goto done;
-
-	while ((ind = g_slist_nth_data(gw->indies, index))) {
-		if(!strcmp(ind->descr, "\"service\""))
-			dict_append_entry(&dict, "RegistrationStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"signal\""))
-			dict_append_entry(&dict, "SignalStrength",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"roam\""))
-			dict_append_entry(&dict, "RoamingStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"battchg\""))
-			dict_append_entry(&dict, "BatteryCharge",
-					DBUS_TYPE_UINT16, &ind->value);
-		index++;
-	}
-
-	/* SpeakerGain */
-	dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16,
-				&device->gateway->sp_gain);
-
-	/* MicrophoneGain */
-	dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16,
-				&device->gateway->mic_gain);
-done:
-	dbus_message_iter_close_container(&iter, &dict);
-	return reply;
+	return NULL;
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "Disconnect", "", "", ag_disconnect },
-	{ "AnswerCall", "", "", ag_answer },
-	{ "TerminateCall", "", "", ag_terminate_call },
-	{ "Call", "s", "", ag_call },
-	{ "GetOperatorName", "", "s", ag_get_operator },
-	{ "SendDTMF", "s", "", ag_send_dtmf },
-	{ "GetSubscriberNumber", "", "s", ag_get_subscriber_num },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
 	{ NULL, NULL, NULL, NULL }
 };
 
 static GDBusSignalTable gateway_signals[] = {
-	{ "Ring", "s" },
-	{ "CallTerminated", "" },
-	{ "CallStarted", "" },
-	{ "CallEnded", "" },
 	{ "PropertyChanged", "sv" },
 	{ NULL, NULL }
 };
@@ -1063,9 +315,6 @@ struct gateway *gateway_init(struct audio_device *dev)
 
 	debug("in gateway_init, dev is %p", dev);
 	gw = g_new0(struct gateway, 1);
-	gw->indies = NULL;
-	gw->is_dialing = FALSE;
-	gw->call_active = FALSE;
 	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
 
@@ -1107,11 +356,6 @@ void gateway_start_service(struct audio_device *device)
 	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
 }
 
-static void indicator_slice_free(gpointer mem)
-{
-	g_slice_free(struct indicator, mem);
-}
-
 int gateway_close(struct audio_device *device)
 {
 	struct gateway *gw = device->gateway;
@@ -1119,8 +363,6 @@ int gateway_close(struct audio_device *device)
 	GIOChannel *sco = gw->sco;
 	gboolean value = FALSE;
 
-	g_slist_foreach(gw->indies, (GFunc) indicator_slice_free, NULL);
-	g_slist_free(gw->indies);
 	if (rfcomm) {
 		g_io_channel_shutdown(rfcomm, TRUE, NULL);
 		g_io_channel_unref(rfcomm);
-- 
1.6.4.4


[-- Attachment #3: 0002-Implement-HandsfreeGateway-Interface.patch --]
[-- Type: application/octet-stream, Size: 24005 bytes --]

From 48f3b333a82bdbb0e3be46d23b24012a1b4fd440 Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 8 Jan 2010 17:38:21 -0200
Subject: [PATCH 2/2] Implement HandsfreeGateway Interface

Create a interface where a Handsfree agent can register and use BlueZ to
handle the rfcomm and sco links.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for his
prototype on this code.
---
 audio/gateway.c |  443 +++++++++++++++++++++++++++++++++++++++++++-----------
 audio/gateway.h |   12 +-
 audio/manager.c |   20 +--
 audio/unix.c    |    8 +
 doc/hfp-api.txt |   81 ++++++++++
 5 files changed, 456 insertions(+), 108 deletions(-)
 create mode 100644 doc/hfp-api.txt

diff --git a/audio/gateway.c b/audio/gateway.c
index 3dc09ff..d364fdc 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -5,6 +5,8 @@
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
  *  Copyright (C) 2008-2009  Leonid Movshovich <event.riga@gmail.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
+ *  Copyright (C) 2010  Gustavo F. Padovan <padovan@profusion.mobi>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -32,12 +34,15 @@
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
 
 #include <glib.h>
 #include <dbus/dbus.h>
 #include <gdbus.h>
 
 #include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
 #include <bluetooth/sco.h>
@@ -52,19 +57,101 @@
 #include "btio.h"
 #include "dbus-common.h"
 
-#define RFCOMM_BUF_SIZE 256
+#define MAX_OPEN_TRIES 5
+#define OPEN_WAIT 300
+
+struct agent {
+	char *name;	/* Bus id */
+	char *path;	/* D-Bus path */
+	guint watch;	/* Disconnect watch */
+};
 
 struct gateway {
 	gateway_state_t state;
-	GIOChannel *rfcomm;
-	guint rfcomm_watch_id;
+	int channel;
+	GIOChannel *rfcomm;	/* remote AG requested rfcomm connection */
 	GIOChannel *sco;
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
-	DBusMessage *connect_message;
+	struct agent *agent;
+	DBusMessage *msg;
 };
 
-int gateway_close(struct audio_device *device);
+static DBusConnection *connection;
+
+int gateway_close(gpointer data);
+
+static const char *connected2str(int i)
+{
+	switch (i) {
+		case GATEWAY_STATE_DISCONNECTED:
+			return "disconnected";
+		case GATEWAY_STATE_CONNECTING:
+			return "connecting";
+		case GATEWAY_STATE_CONNECTED:
+			return "connected";
+		case GATEWAY_STATE_PLAYING:
+			return "playing";
+		default:
+			return "";
+	}
+}
+
+static void agent_free(struct agent *agent)
+{
+	if (!agent)
+		return;
+
+	g_free(agent->name);
+	g_free(agent->path);
+	g_free(agent);
+}
+
+static void agent_disconnect(struct agent *agent)
+{
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "Release");
+
+	dbus_message_set_no_reply(msg, TRUE);
+	g_dbus_send_message(connection, msg);
+
+	g_dbus_remove_watch(connection, agent->watch);
+}
+
+static gboolean agent_sendfd(struct agent *agent, int fd,
+		DBusPendingCallNotifyFunction notify, void *data)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "NewConnection");
+
+	dbus_message_append_args(msg, DBUS_TYPE_UNIX_FD, &fd,
+					DBUS_TYPE_INVALID);
+
+	if (dbus_connection_send_with_reply(connection, msg, &call, -1) == FALSE)
+		return FALSE;
+
+	dbus_pending_call_set_notify(call, notify, data, NULL);
+	dbus_pending_call_unref(call);
+
+	return TRUE;
+}
+
+static void change_state(struct audio_device *dev, int new_state)
+{
+	struct gateway *gw = dev->gateway;
+	const char *val = connected2str(new_state);
+
+	gw->state = new_state;
+
+	emit_property_changed(connection,dev->path,
+			AUDIO_GATEWAY_INTERFACE, "State",
+			DBUS_TYPE_STRING, &val);
+}
 
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
@@ -79,6 +166,7 @@ static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 		g_io_channel_shutdown(gw->sco, TRUE, NULL);
 		g_io_channel_unref(gw->sco);
 		gw->sco = NULL;
+		change_state(dev, GATEWAY_STATE_CONNECTED);
 		return FALSE;
 	}
 
@@ -92,63 +180,101 @@ static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
 
 	debug("at the begin of sco_connect_cb() in gateway.c");
 
+	gw->sco = chan;
+	g_io_channel_ref(chan);
+
 	if (err) {
 		error("sco_connect_cb(): %s", err->message);
-		/* not sure, but from other point of view,
-		 * what is the reason to have headset which
-		 * cannot play audio? */
-		if (gw->sco_start_cb)
-			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 		gateway_close(dev);
 		return;
 	}
 
-	gw->sco = g_io_channel_ref(chan);
 	if (gw->sco_start_cb)
 		gw->sco_start_cb(dev, gw->sco_start_cb_data);
 
-	/* why is this here? */
 	fcntl(g_io_channel_unix_get_fd(chan), F_SETFL, 0);
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
 				(GIOFunc) sco_io_cb, dev);
 }
 
+static void newconnection_reply(DBusPendingCall *call, void *data)
+{
+	struct gateway *gw = data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError derr;
+
+	if (!gw->rfcomm) {
+		debug("RFCOMM disconnected from server before agent reply");
+		return;
+	}
+
+	dbus_error_init(&derr);
+	if (!dbus_set_error_from_message(&derr, reply)) {
+		info("Agent reply: file descriptor passed successfuly");
+		return;
+	}
+
+	debug("Agent reply: %s", derr.message);
+
+	dbus_error_free(&derr);
+	g_idle_add(gateway_close, gw);
+}
+
 static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 				gpointer user_data)
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	gchar gw_addr[18];
-	GIOFlags flags;
+	struct rfcomm_dev_req req;
+	DBusMessage *reply;
+	int sk;
+	char src[18], dst[18];
 
 	if (err) {
 		error("connect(): %s", err->message);
 		if (gw->sco_start_cb)
 			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
-		return;
+		goto fail;
 	}
 
-	ba2str(&dev->dst, gw_addr);
-	/* Blocking mode should be default, but just in case: */
-	flags = g_io_channel_get_flags(chan);
-	flags &= ~G_IO_FLAG_NONBLOCK;
-	flags &= G_IO_FLAG_MASK;
-	g_io_channel_set_flags(chan, flags, NULL);
-	g_io_channel_set_encoding(chan, NULL, NULL);
-	g_io_channel_set_buffered(chan, FALSE);
-	if (!gw->rfcomm)
-		gw->rfcomm = g_io_channel_ref(chan);
+	ba2str(&dev->src, src);
+	ba2str(&dev->dst, dst);
 
-	if (NULL != gw->sco_start_cb)
-		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
+	/* make /dev/rfcomm serial port from chan */
+	memset(&req, 0, sizeof(req));
+	req.dev_id = -1;
+	req.flags = (1 << RFCOMM_REUSE_DLC);
+	bacpy(&req.src, &dev->src);
+	bacpy(&req.dst, &dev->dst);
+	req.channel = gw->channel;
 
-	gateway_close(dev);
+	sk = g_io_channel_unix_get_fd(chan);
+
+	change_state(dev, GATEWAY_STATE_CONNECTED);
+
+	if (!agent_sendfd(gw->agent, sk, newconnection_reply, gw)) {
+		reply = g_dbus_create_error(gw->msg, ERROR_INTERFACE ".Failed",
+				"Can not pass file descriptor");
+	} else
+		reply = dbus_message_new_method_return(gw->msg);
+
+	return;
+
+fail:
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".Failed", ".Connection attempt failed");
+		dbus_message_unref(gw->msg);
+		gw->msg = NULL;
+	}
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
+	return;
 }
 
 static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 {
 	struct audio_device *dev = user_data;
-	DBusMessage *msg = dev->gateway->connect_message;
+	struct gateway *gw = dev->gateway;
 	int ch = -1;
 	sdp_list_t *protos, *classes;
 	uuid_t uuid;
@@ -182,8 +308,6 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 
 	if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
 			uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
-		sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free,
-									NULL);
 		sdp_list_free(protos, NULL);
 		error("Invalid service record or not HFP");
 		goto fail;
@@ -197,6 +321,8 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 		goto fail;
 	}
 
+	gw->channel = ch;
+
 	io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -204,25 +330,26 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 				BT_IO_OPT_INVALID);
 	if (!io) {
 		error("Unable to connect: %s", err->message);
-		if (msg) {
-			error_common_reply(dev->conn, msg, ERROR_INTERFACE
-						".ConnectionAttemptFailed",
-						err->message);
-			msg = NULL;
-		}
-		g_error_free(err);
+		if (err)
+			g_error_free(err);
 		gateway_close(dev);
+		goto fail;
 	}
 
 	g_io_channel_unref(io);
+
+	change_state(dev, GATEWAY_STATE_CONNECTING);
 	return;
 
 fail:
-	if (msg)
-		error_common_reply(dev->conn, msg, ERROR_INTERFACE
-					".NotSupported", "Not supported");
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".NotSupported", "Not supported");
+		dbus_message_unref(gw->msg);
+		gw->msg = NULL;
+	}
 
-	dev->gateway->connect_message = NULL;
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
 
 	sco_cb = dev->gateway->sco_start_cb;
 	if (sco_cb)
@@ -243,22 +370,57 @@ static DBusMessage *ag_connect(DBusConnection *conn, DBusMessage *msg,
 {
 	struct audio_device *au_dev = (struct audio_device *) data;
 	struct gateway *gw = au_dev->gateway;
+	DBusMessage *reply;
 
 	debug("at the begin of ag_connect()");
-	if (gw->rfcomm)
+
+	if (!gw->agent)
 		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".AlreadyConnected",
-					"Already Connected");
+				".Failed", "Agent not assined");
 
-	gw->connect_message = dbus_message_ref(msg);
+	gw->msg = dbus_message_ref(msg);
 	if (get_records(au_dev) < 0) {
-		dbus_message_unref(gw->connect_message);
+		dbus_message_unref(gw->msg);
 		return g_dbus_create_error(msg, ERROR_INTERFACE
 					".ConnectAttemptFailed",
 					"Connect Attempt Failed");
 	}
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
 	debug("at the end of ag_connect()");
-	return NULL;
+
+	return reply;
+}
+
+int gateway_close(gpointer data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	gboolean value = FALSE;
+
+	if (gw->rfcomm) {
+		g_io_channel_shutdown(gw->rfcomm, TRUE, NULL);
+		g_io_channel_unref(gw->rfcomm);
+		gw->rfcomm = NULL;
+	}
+
+	if (gw->sco) {
+		g_io_channel_shutdown(gw->sco, TRUE, NULL);
+		g_io_channel_unref(gw->sco);
+		gw->sco = NULL;
+		gw->sco_start_cb = NULL;
+		gw->sco_start_cb_data = NULL;
+	}
+
+	gw->state = GATEWAY_STATE_DISCONNECTED;
+
+	emit_property_changed(device->conn, device->path,
+				AUDIO_GATEWAY_INTERFACE,
+				"Connected", DBUS_TYPE_BOOLEAN, &value);
+	return 0;
 }
 
 static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
@@ -269,6 +431,9 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	DBusMessage *reply = NULL;
 	char gw_addr[18];
 
+	if (!connection)
+		return NULL;
+
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
 		return NULL;
@@ -285,16 +450,117 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
+static void agent_exited(DBusConnection *conn, void *data)
+{
+	struct gateway *gateway = data;
+	struct agent *agent = gateway->agent;
+
+	debug("Agent %s exited", agent->name);
+
+	agent_free(agent);
+	gateway->agent = NULL;
+}
+
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	return NULL;
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	const char *value;
+
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	value = connected2str(gw->state);
+	dict_append_entry(&dict, "Connected",
+			DBUS_TYPE_STRING, &value);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct agent *agent;
+	const char *path, *name;
+
+
+	if (gw->agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".AlreadyExists",
+				"Agent already exists");
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+			DBUS_TYPE_INVALID))
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+				".InvalidArguments", "Invalid argument");
+
+	name = dbus_message_get_sender(msg);
+	agent = g_new0(struct agent, 1);
+	if (!agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed",
+				"Failed to create a new agent");
+
+	agent->name = strdup(name);
+	agent->path = strdup(path);
+
+	agent->watch = g_dbus_add_disconnect_watch(conn, name,
+			agent_exited, gw, NULL);
+
+	gw->agent = agent;
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+				DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct agent *agent;
+
+	if (!gw->agent)
+		goto done;
+
+	agent = gw->agent;
+	if (strcmp(agent->name, dbus_message_get_sender(msg)) != 0)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed", "Permission denied");
+
+	agent_disconnect(gw->agent);
+
+	g_dbus_remove_watch(conn, agent->watch);
+
+	agent_free(agent);
+	gw->agent = NULL;
+
+done:
+	return dbus_message_new_method_return(msg);
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
-	{ "Disconnect", "", "", ag_disconnect },
+	{ "Disconnect", "", "", ag_disconnect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
+	{ "RegisterAgent", "o", "", register_agent },
+	{ "UnregisterAgent", "o", "", unregister_agent },
 	{ NULL, NULL, NULL, NULL }
 };
 
@@ -307,17 +573,23 @@ struct gateway *gateway_init(struct audio_device *dev)
 {
 	struct gateway *gw;
 
+	gw = g_new0(struct gateway, 1);
+
+	connection = dbus_connection_ref(dev->conn);
+
 	if (!g_dbus_register_interface(dev->conn, dev->path,
 					AUDIO_GATEWAY_INTERFACE,
 					gateway_methods, gateway_signals,
 					NULL, dev, NULL))
 		return NULL;
 
-	debug("in gateway_init, dev is %p", dev);
-	gw = g_new0(struct gateway, 1);
-	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
+}
 
+void gateway_exit()
+{
+	dbus_connection_unref(connection);
+	connection = NULL;
 }
 
 gboolean gateway_is_connected(struct audio_device *dev)
@@ -326,17 +598,6 @@ gboolean gateway_is_connected(struct audio_device *dev)
 			dev->gateway->state == GATEWAY_STATE_CONNECTED);
 }
 
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
-{
-	if (!io)
-		return -EINVAL;
-
-	g_io_channel_ref(io);
-	dev->gateway->rfcomm = io;
-
-	return 0;
-}
-
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 {
 	struct gateway *gw = dev->gateway;
@@ -347,42 +608,39 @@ int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 	gw->sco = g_io_channel_ref(io);
 
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                                (GIOFunc) sco_io_cb, dev);
+				 (GIOFunc) sco_io_cb, dev);
+
+	change_state(dev, GATEWAY_STATE_PLAYING);
+
 	return 0;
 }
 
-void gateway_start_service(struct audio_device *device)
+int gateway_connect_rfcomm(struct audio_device *dev,
+		GIOChannel *chan, int ch)
 {
-	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
+	if (!chan)
+		return -EINVAL;
+
+	g_io_channel_ref(chan);
+	dev->gateway->rfcomm = chan;
+	dev->gateway->channel = ch;
+
+	return 0;
 }
 
-int gateway_close(struct audio_device *device)
+void gateway_start_service(struct audio_device *dev)
 {
-	struct gateway *gw = device->gateway;
-	GIOChannel *rfcomm = gw->rfcomm;
-	GIOChannel *sco = gw->sco;
-	gboolean value = FALSE;
+	struct gateway *gw = dev->gateway;
+	GError *err = NULL;
 
-	if (rfcomm) {
-		g_io_channel_shutdown(rfcomm, TRUE, NULL);
-		g_io_channel_unref(rfcomm);
-		gw->rfcomm = NULL;
-	}
+	if (gw->rfcomm == NULL)
+		return;
 
-	if (sco) {
-		g_io_channel_shutdown(sco, TRUE, NULL);
-		g_io_channel_unref(sco);
-		gw->sco = NULL;
-		gw->sco_start_cb = NULL;
-		gw->sco_start_cb_data = NULL;
+	if (!bt_io_accept(gw->rfcomm, rfcomm_connect_cb, dev, NULL,
+							&err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
 	}
-
-	gw->state = GATEWAY_STATE_DISCONNECTED;
-
-	emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN, &value);
-	return 0;
 }
 
 /* These are functions to be called from unix.c for audio system
@@ -399,8 +657,12 @@ gboolean gateway_request_stream(struct audio_device *dev,
 		gw->sco_start_cb_data = user_data;
 		get_records(dev);
 	} else if (!gw->sco) {
+		char source[128], destination[128];
+
 		gw->sco_start_cb = cb;
 		gw->sco_start_cb_data = user_data;
+		ba2str(&dev->src, source);
+		ba2str(&dev->dst, destination);
 		io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -464,4 +726,3 @@ void gateway_suspend_stream(struct audio_device *dev)
 	gw->sco_start_cb = NULL;
 	gw->sco_start_cb_data = NULL;
 }
-
diff --git a/audio/gateway.h b/audio/gateway.h
index 3b0457f..eaf6801 100644
--- a/audio/gateway.h
+++ b/audio/gateway.h
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -22,20 +23,25 @@
  *
  */
 
-#define AUDIO_GATEWAY_INTERFACE "org.bluez.HeadsetGateway"
+#define AUDIO_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
 
 #define DEFAULT_HSP_HS_CHANNEL 6
 #define DEFAULT_HFP_HS_CHANNEL 7
 
 typedef enum {
 	GATEWAY_STATE_DISCONNECTED,
-	GATEWAY_STATE_CONNECTED
+	GATEWAY_STATE_CONNECTED,
+	GATEWAY_STATE_CONNECTING,
+	GATEWAY_STATE_PLAYING,
 } gateway_state_t;
 
 typedef void (*gateway_stream_cb_t) (struct audio_device *dev, void *user_data);
+
 struct gateway *gateway_init(struct audio_device *device);
+void gateway_exit();
 gboolean gateway_is_connected(struct audio_device *dev);
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *chan);
+int gateway_connect_rfcomm(struct audio_device *dev,
+			GIOChannel *chan, int ch);
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *chan);
 void gateway_start_service(struct audio_device *device);
 gboolean gateway_request_stream(struct audio_device *dev,
diff --git a/audio/manager.c b/audio/manager.c
index 413c1f3..e768014 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -198,7 +198,7 @@ static void handle_uuid(const char *uuidstr, struct audio_device *device)
 		break;
 	case HANDSFREE_AGW_SVCLASS_ID:
 		debug("Found Handsfree AG record");
-		if (device->gateway == NULL)
+		if (enabled.gateway && (device->gateway == NULL))
 			device->gateway = gateway_init(device);
 		break;
 	case AUDIO_SINK_SVCLASS_ID:
@@ -586,7 +586,7 @@ static void hf_io_cb(GIOChannel *chan, gpointer data)
 		goto drop;
 	}
 
-	if (gateway_connect_rfcomm(device, chan) < 0) {
+	if (gateway_connect_rfcomm(device, chan, ch) < 0) {
 		error("Allocating new GIOChannel failed!");
 		goto drop;
 	}
@@ -905,22 +905,12 @@ static void headset_server_remove(struct btd_adapter *adapter)
 static int gateway_server_probe(struct btd_adapter *adapter)
 {
 	struct audio_adapter *adp;
-	const gchar *path = adapter_get_path(adapter);
-	int ret;
-
-	DBG("path %s", path);
 
 	adp = audio_adapter_get(adapter);
 	if (!adp)
 		return -EINVAL;
 
-	ret = gateway_server_init(adp);
-	if (ret < 0) {
-		audio_adapter_ref(adp);
-		return ret;
-	}
-
-	return 0;
+	return gateway_server_init(adp);
 }
 
 static void gateway_server_remove(struct btd_adapter *adapter)
@@ -1166,8 +1156,10 @@ void audio_manager_exit(void)
 		telephony_exit();
 	}
 
-	if (enabled.gateway)
+	if (enabled.gateway) {
 		btd_unregister_adapter_driver(&gateway_server_driver);
+		gateway_exit();
+	}
 
 	if (enabled.source || enabled.sink)
 		btd_unregister_adapter_driver(&a2dp_server_driver);
diff --git a/audio/unix.c b/audio/unix.c
index 5cf4f94..bd1a415 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -395,6 +395,9 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	struct bt_start_stream_rsp *rsp = (void *) buf;
 	struct bt_new_stream_ind *ind = (void *) buf;
 
+	if (!dev)
+		goto failed;
+
 	memset(buf, 0, sizeof(buf));
 	rsp->h.type = BT_RESPONSE;
 	rsp->h.name = BT_START_STREAM;
@@ -416,6 +419,11 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	}
 
 	client->req_id = 0;
+	return;
+
+failed:
+	error("gateway_resume_complete: resume failed");
+	unix_ipc_error(client, BT_START_STREAM, EIO);
 }
 
 static void headset_suspend_complete(struct audio_device *dev, void *user_data)
diff --git a/doc/hfp-api.txt b/doc/hfp-api.txt
new file mode 100644
index 0000000..ed52be1
--- /dev/null
+++ b/doc/hfp-api.txt
@@ -0,0 +1,81 @@
+Gateway hierarchy
+========================
+
+Service		org.bluez
+Interface	org.bluez.HandsfreeGateway
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+This interface is available for remote devices which can function in the Audio
+Gateway role of the HFP profiles.  It is intended to be used with external
+telephony stacks / handlers of the HFP protocol.
+
+Methods		void Connect()
+
+			Connect to the AG service on the remote device.
+
+		void Disconnect()
+
+			Disconnect from the AG service on the remote device
+
+		dict GetProperties()
+
+			Returns all properties for the interface. See the
+			properties section for available properties.
+
+		void RegisterAgent(object path)
+
+			The object path defines the path the of the agent
+			that will be called when a new Handsfree connection
+			is established.
+			
+			If an application disconnects from the bus all of its
+			registered agents will be removed.
+
+		void UnregisterAgent(object path)
+
+			This unregisters the agent that has been previously
+			registered. The object path parameter must match the
+			same value that has been used on registration.
+
+Signals		PropertyChanged(string name, variant value)
+
+			This signal indicates a changed value of the given
+			property.
+
+Properties	string State [readonly]
+
+			Indicates the state of the connection.  Possible
+			values are:
+				"disconnected"
+				"connecting"
+				"connected"
+				"playing"
+
+HandsfreeAgent hierarchy
+===============
+
+Service         unique name
+Interface       org.bluez.HandsfreeAgent
+Object path     freely definable
+
+Methods		void NewConnection(filedescriptor fd)
+
+			This method gets called whenever a new handsfree
+			connection has been established.  The objectpath
+			contains the object path of the remote device.  This
+			method assumes that DBus daemon with file descriptor
+			passing capability is being used.
+
+			The agent should only return successfully once the
+			establishment of the service level connection (SLC)
+			has been completed.  In the case of Handsfree this
+			means that BRSF exchange has been performed and
+			necessary initialization has been done.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.Failed
+
+		void Release()
+
+			This method gets called whenever the service daemon
+			unregisters the agent.
-- 
1.6.4.4


[-- Attachment #4: 0001-Add-HFP-support-in-oFono.patch --]
[-- Type: application/octet-stream, Size: 13597 bytes --]

From 1d349ded8c38831b36600f53a8dd9b781d9e13eb Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 8 Jan 2010 17:25:43 -0200
Subject: [PATCH] Add HFP support in oFono

It uses BlueZ through to get HFP working following the
org.bluez.HandsfreeGateway and org.bluez.HandsfreeAgent from the BlueZ
D-Bus API.

You need the HFP patch into BlueZ too.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for its prototype
on this code.
---
 drivers/hfpmodem/hfpmodem.h |    2 +
 gatchat/gattty.c            |   23 ++++
 gatchat/gattty.h            |    1 +
 include/modem.h             |    2 +
 plugins/hfp.c               |  284 +++++++++++++++++++++++++++++++++++++++++--
 plugins/modemconf.c         |    3 +-
 src/modem.c                 |   15 +++
 7 files changed, 318 insertions(+), 12 deletions(-)

diff --git a/drivers/hfpmodem/hfpmodem.h b/drivers/hfpmodem/hfpmodem.h
index 509846b..5fee68f 100644
--- a/drivers/hfpmodem/hfpmodem.h
+++ b/drivers/hfpmodem/hfpmodem.h
@@ -63,11 +63,13 @@ enum hfp_indicator {
 
 struct hfp_data {
 	GAtChat *chat;
+	char *handsfree_path;
 	unsigned int ag_features;
 	unsigned int ag_mpty_features;
 	unsigned int hf_features;
 	unsigned char cind_pos[HFP_INDICATOR_LAST];
 	unsigned int cind_val[HFP_INDICATOR_LAST];
+	unsigned int at_timeout;
 };
 
 extern void hfp_netreg_init();
diff --git a/gatchat/gattty.c b/gatchat/gattty.c
index 02ca389..ae12662 100644
--- a/gatchat/gattty.c
+++ b/gatchat/gattty.c
@@ -260,3 +260,26 @@ GIOChannel *g_at_tty_open(const char *tty, GHashTable *options)
 
 	return channel;
 }
+
+GIOChannel *g_at_get_channel_from_fd(int fd)
+{
+	GIOChannel *channel;
+	struct termios ti;
+
+	/* Switch TTY to raw mode */
+	memset(&ti, 0, sizeof(ti));
+	cfmakeraw(&ti);
+	tcflush(fd, TCIOFLUSH);
+	tcsetattr(fd, TCSANOW, &ti);
+
+	channel = g_io_channel_unix_new(fd);
+
+	if (channel == NULL) {
+		close(fd);
+		return NULL;
+	}
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+
+	return channel;
+}
diff --git a/gatchat/gattty.h b/gatchat/gattty.h
index dc3fe16..730f1dc 100644
--- a/gatchat/gattty.h
+++ b/gatchat/gattty.h
@@ -43,6 +43,7 @@ extern "C" {
  */
 GIOChannel *g_at_tty_open(const char *tty, GHashTable *options);
 
+GIOChannel *g_at_get_channel_from_fd(int fd);
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/modem.h b/include/modem.h
index b2aa607..bf28515 100644
--- a/include/modem.h
+++ b/include/modem.h
@@ -47,6 +47,8 @@ struct ofono_modem *ofono_modem_create(const char *name, const char *type);
 int ofono_modem_register(struct ofono_modem *modem);
 void ofono_modem_remove(struct ofono_modem *modem);
 
+struct ofono_modem *ofono_modem_get_by_path(const char *path);
+
 void ofono_modem_set_powered(struct ofono_modem *modem, ofono_bool_t powered);
 ofono_bool_t ofono_modem_get_powered(struct ofono_modem *modem);
 
diff --git a/plugins/hfp.c b/plugins/hfp.c
index 3bbd922..1e4eeb3 100644
--- a/plugins/hfp.c
+++ b/plugins/hfp.c
@@ -30,6 +30,8 @@
 #include <glib.h>
 #include <gatchat.h>
 #include <gattty.h>
+#include <gdbus.h>
+#include <ofono.h>
 
 #define OFONO_API_SUBJECT_TO_CHANGE
 #include <ofono/plugin.h>
@@ -52,11 +54,24 @@
 
 #include <drivers/hfpmodem/hfpmodem.h>
 
+#include <ofono/dbus.h>
+
+#define BLUEZ_SERVICE "org.bluez"
+#define BLUEZ_PATH "/"
+#define BLUEZ_MANAGER_INTERFACE "org.bluez.Manager"
+#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter"
+#define BLUEZ_DEVICE_INTERFACE "org.bluez.Device"
+#define BLUEZ_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
+
+#define HFP_AGENT_INTERFACE "org.bluez.HandsfreeAgent"
+
 static const char *brsf_prefix[] = { "+BRSF:", NULL };
 static const char *cind_prefix[] = { "+CIND:", NULL };
 static const char *cmer_prefix[] = { "+CMER:", NULL };
 static const char *chld_prefix[] = { "+CHLD:", NULL };
 
+static DBusConnection *connection;
+
 static int hfp_disable(struct ofono_modem *modem);
 
 static void hfp_debug(const char *str, void *user_data)
@@ -135,6 +150,60 @@ static void cmer_cb(gboolean ok, GAtResult *result, gpointer user_data)
 		sevice_level_conn_established(modem);
 }
 
+static int send_method_call(const char *dest, const char *path,
+				const char *interface, const char *method,
+				DBusPendingCallNotifyFunction cb,
+				void *user_data, int type, ...)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+	va_list args;
+
+	msg = dbus_message_new_method_call(dest, path, interface, method);
+	if (!msg) {
+		ofono_error("Unable to allocate new D-Bus %s message", method);
+		return -ENOMEM;
+	}
+
+	va_start(args, type);
+
+	if (!dbus_message_append_args_valist(msg, type, args)) {
+		dbus_message_unref(msg);
+		va_end(args);
+		return -EIO;
+	}
+
+	va_end(args);
+
+	if (!cb) {
+		g_dbus_send_message(connection, msg);
+		return 0;
+	}
+
+	if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+		ofono_error("Sending %s failed", method);
+		dbus_message_unref(msg);
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(call, cb, user_data, NULL);
+	dbus_pending_call_unref(call);
+	dbus_message_unref(msg);
+
+	return 0;
+}
+
+static gboolean hfp_enable_timeout(gpointer user)
+{
+	struct ofono_modem *modem = user;
+
+	if (ofono_modem_get_powered(modem))
+		return FALSE;
+
+	hfp_disable(modem);
+	return FALSE;
+}
+
 static void cind_status_cb(gboolean ok, GAtResult *result,
 				gpointer user_data)
 {
@@ -260,8 +329,7 @@ error:
 }
 
 /* either oFono or Phone could request SLC connection */
-static int service_level_connection(struct ofono_modem *modem,
-				const char *tty)
+static int service_level_connection(struct ofono_modem *modem, int fd)
 {
 	struct hfp_data *data = ofono_modem_get_data(modem);
 	GIOChannel *io;
@@ -269,7 +337,7 @@ static int service_level_connection(struct ofono_modem *modem,
 	GAtChat *chat;
 	char buf[64];
 
-	io = g_at_tty_open(tty, NULL);
+	io = g_at_get_channel_from_fd(fd);
 	if (!io) {
 		ofono_error("Service level connection failed: %s (%d)",
 			strerror(errno), errno);
@@ -296,9 +364,158 @@ static int service_level_connection(struct ofono_modem *modem,
 	return -EINPROGRESS;
 }
 
+static DBusMessage *hfp_agent_new_connection(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	int fd;
+	const char *path;
+	struct ofono_modem *modem = NULL;
+
+	path = dbus_message_get_path(msg);
+	modem = ofono_modem_get_by_path(path);
+
+	if (!modem)
+		return NULL;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UNIX_FD, &fd,
+				DBUS_TYPE_INVALID))
+		return __ofono_error_invalid_args(msg);
+
+	service_level_connection(modem, fd);
+
+	return NULL;
+}
+
+static DBusMessage *hfp_agent_release(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	return NULL;
+}
+
+static GDBusMethodTable agent_methods[] = {
+        { "NewConnection", "h", "", hfp_agent_new_connection,
+		G_DBUS_METHOD_FLAG_ASYNC },
+	{ "Release", "", "", hfp_agent_release },
+	{NULL, NULL, NULL, NULL}
+};
+
+static void find_device_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	struct ofono_modem *modem = user_data;
+	struct hfp_data *data = ofono_modem_get_data(modem);
+	const char *device, *obj_path;
+	int ret;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH,
+				&device, DBUS_TYPE_INVALID) == FALSE) {
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s", err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	ofono_debug("Using device %s", device);
+	data->handsfree_path = g_strdup(device);
+
+	obj_path = ofono_modem_get_path(modem);
+
+	ret = send_method_call(BLUEZ_SERVICE, device,
+				BLUEZ_GATEWAY_INTERFACE, "RegisterAgent",
+				NULL, NULL, DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+
+	if (ret < 0)
+		ofono_error("agent_register failed(%d)", ret);
+
+done:
+	dbus_message_unref(reply);
+}
+
+static void get_adapter_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	struct ofono_modem *modem = user_data;
+	const char *adapter = NULL;
+	const char *address;
+	int ret;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+	if (dbus_message_get_args(reply, &err,
+				DBUS_TYPE_OBJECT_PATH,
+				&adapter, DBUS_TYPE_INVALID) == FALSE) {
+		if (adapter == NULL) {
+			ofono_error("bluetooth adapter is not enabled");
+			dbus_error_free(&err);
+			goto done;
+		}
+
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s %s", adapter, err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	address = ofono_modem_get_string(modem, "Address");
+
+	DBG("address %s", address);
+
+	if (address == NULL)
+		return;
+
+	ret = send_method_call(BLUEZ_SERVICE, adapter,
+				BLUEZ_ADAPTER_INTERFACE, "FindDevice",
+				find_device_cb, modem,
+				DBUS_TYPE_STRING, &address,
+				DBUS_TYPE_INVALID);
+
+	if (ret < 0)
+		ofono_error("find_device failed(%d)", ret);
+
+done:
+	dbus_message_unref(reply);
+
+}
+
+static int hfp_register_ofono_handsfree(struct ofono_modem *modem)
+{
+	ofono_debug("Register oFono Agent to bluetooth daemon");
+
+	return send_method_call(BLUEZ_SERVICE, BLUEZ_PATH,
+				BLUEZ_MANAGER_INTERFACE, "DefaultAdapter",
+				get_adapter_cb, modem,
+				DBUS_TYPE_INVALID);
+}
+
+static int hfp_unregister_ofono_handsfree(struct ofono_modem *modem)
+{
+	const char *obj_path = ofono_modem_get_path(modem);
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	ofono_debug("Unregister oFono Agent to bluetooth daemon");
+
+	if (!data->handsfree_path)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "UnregisterAgent",
+				NULL, NULL, DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+}
+
 static int hfp_probe(struct ofono_modem *modem)
 {
 	struct hfp_data *data;
+	const char *obj_path;
+	DBusConnection *conn = ofono_dbus_get_connection();
 
 	data = g_try_new0(struct hfp_data, 1);
 	if (!data)
@@ -312,39 +529,81 @@ static int hfp_probe(struct ofono_modem *modem)
 
 	ofono_modem_set_data(modem, data);
 
+	connection = dbus_connection_ref(conn);
+
+	obj_path = ofono_modem_get_path(modem);
+	g_dbus_register_interface(conn, obj_path, HFP_AGENT_INTERFACE,
+			agent_methods, NULL, NULL, data, NULL);
+
+	if (hfp_register_ofono_handsfree(modem) != 0)
+		return -EINVAL;
+
 	return 0;
 }
 
 static void hfp_remove(struct ofono_modem *modem)
 {
-	gpointer data = ofono_modem_get_data(modem);
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	hfp_unregister_ofono_handsfree(modem);
+
+	if (data->handsfree_path)
+		g_free(data->handsfree_path);
 
 	if (data)
 		g_free(data);
 
+	dbus_connection_unref(connection);
+
 	ofono_modem_set_data(modem, NULL);
 }
 
+static int hfp_connect_ofono_handsfree(struct ofono_modem *modem)
+{
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	ofono_debug("Connect to bluetooth daemon");
+
+	if (!data->handsfree_path || !connection)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Connect",
+				NULL, NULL, DBUS_TYPE_INVALID);
+}
+
 /* power up hardware */
 static int hfp_enable(struct ofono_modem *modem)
 {
-	const char *tty;
-	int ret;
+	struct hfp_data *data = ofono_modem_get_data(modem);
 
 	DBG("%p", modem);
 
-	tty = ofono_modem_get_string(modem, "Device");
-	if (tty == NULL)
+	data->at_timeout =
+		g_timeout_add_seconds(10, hfp_enable_timeout, modem);
+
+	if (hfp_connect_ofono_handsfree(modem) < 0)
 		return -EINVAL;
 
-	ret = service_level_connection(modem, tty);
+	return -EINPROGRESS;
+}
+
+static int hfp_disconnect_ofono_handsfree(struct ofono_modem *modem)
+{
+	struct hfp_data *data = ofono_modem_get_data(modem);
 
-	return ret;
+	if (!data->handsfree_path || !connection)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Disconnect",
+				NULL, NULL, DBUS_TYPE_INVALID);
 }
 
 static int hfp_disable(struct ofono_modem *modem)
 {
 	struct hfp_data *data = ofono_modem_get_data(modem);
+	GSource *source;
 
 	DBG("%p", modem);
 
@@ -359,6 +618,11 @@ static int hfp_disable(struct ofono_modem *modem)
 
 	ofono_modem_set_powered(modem, FALSE);
 
+	source = g_main_context_find_source_by_id(NULL, data->at_timeout);
+	if (source)
+		g_source_destroy(source);
+
+	hfp_disconnect_ofono_handsfree(modem);
 	return 0;
 }
 
diff --git a/plugins/modemconf.c b/plugins/modemconf.c
index c8c261f..b4c1600 100644
--- a/plugins/modemconf.c
+++ b/plugins/modemconf.c
@@ -124,12 +124,11 @@ static struct ofono_modem *create_modem(GKeyFile *keyfile, const char *group)
 
 	modem = ofono_modem_create(group, driver);
 
-	if (!g_strcmp0(driver, "phonesim"))
+	if (!g_strcmp0(driver, "phonesim") || !g_strcmp0(driver, "hfp"))
 		set_address(modem, keyfile, group);
 
 	if (!g_strcmp0(driver, "atgen") || !g_strcmp0(driver, "g1") ||
 						!g_strcmp0(driver, "calypso") ||
-						!g_strcmp0(driver, "hfp") ||
 						!g_strcmp0(driver, "palmpre"))
 		set_device(modem, keyfile, group);
 
diff --git a/src/modem.c b/src/modem.c
index bbc9905..9fab722 100644
--- a/src/modem.c
+++ b/src/modem.c
@@ -1074,6 +1074,21 @@ struct ofono_modem *ofono_modem_create(const char *name, const char *type)
 	return modem;
 }
 
+struct ofono_modem *ofono_modem_get_by_path(const char *path)
+{
+	GSList *l;
+	struct ofono_modem *modem;
+
+	for (l = g_modem_list; l; l = l->next) {
+		modem = l->data;
+
+		if (!g_strcmp0(modem->path, path))
+			return modem;
+	}
+
+	return NULL;
+}
+
 static void emit_modems()
 {
 	DBusConnection *conn = ofono_dbus_get_connection();
-- 
1.6.4.4


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

* Re: [RFC] HFP support into oFono and BlueZ
@ 2010-01-13 23:39     ` Gustavo F. Padovan
  0 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-13 23:39 UTC (permalink / raw)
  To: ofono

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

Hi,

On Mon, Jan 11, 2010 at 5:05 PM, Gustavo F. Padovan
<padovan@profusion.mobi> wrote:
> On Mon, Jan 11, 2010 at 3:08 PM, Gustavo F. Padovan
> <padovan@profusion.mobi> wrote:
>> Hi,
>>
>> These patches implement the new API for the Audio Gateway in BlueZ. It
>> follows the last version of the HandsfreeGateway and HandsfreeAgent
>> Intefaces API.
>> The first two patches is for BlueZ and the other for oFono. You can
>> test it with using enable-modem and test-voicecall scripts into the
>> test dir of oFono.
>> Feel free to test it and send me your comments. We have some bugs yet.
>
> New version of the patches: now we do not handle tty stuff on the BlueZ side.

New version of the patches. Known issues:
"'org.bluez.HandsfreeAgent.Release' part not implemented" and it's not
working with more than one bluetooth devices in some cases. Comments
are welcome.

>
>>
>> The audio part is not working yet. We are going to work on pulseaudio
>> this week to get this done soon.
>>
>> Regards,
>>
>> --
>> Gustavo F. Padovan
>> ProFUSION embedded systems - http://profusion.mobi
>>
>
>
>
> --
> Gustavo F. Padovan
> ProFUSION embedded systems - http://profusion.mobi
>



-- 
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi

[-- Attachment #2: 0001-clean-up-audio-gateway.c.patch --]
[-- Type: application/octet-stream, Size: 24620 bytes --]

From 7eea257a7b4a0cbc026aab6a60df9bb580065831 Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 6 Nov 2009 18:08:58 -0200
Subject: [PATCH 1/2] clean up audio/gateway.c

remove all code related to the AT engine
---
 audio/gateway.c |  760 +------------------------------------------------------
 1 files changed, 1 insertions(+), 759 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index a1c1ea2..3dc09ff 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -53,52 +53,6 @@
 #include "dbus-common.h"
 
 #define RFCOMM_BUF_SIZE 256
-/* not-more-then-16 defined by GSM + 1 for NULL + padding */
-#define AG_INDICATOR_DESCR_SIZE 20
-#define AG_CALLER_NUM_SIZE 64	/* size of number + type */
-
-/* commands */
-#define AG_FEATURES "AT+BRSF=26\r"     /* = 0x7F = All features supported */
-#define AG_INDICATORS_SUPP "AT+CIND=?\r"
-#define AG_INDICATORS_VAL "AT+CIND?\r"
-#define AG_INDICATORS_ENABLE "AT+CMER=3,0,0,1\r"
-#define AG_HOLD_MPTY_SUPP "AT+CHLD=?\r"
-#define AG_CALLER_IDENT_ENABLE "AT+CLIP=1\r"
-#define AG_CARRIER_FORMAT "AT+COPS=3,0\r"
-#define AG_EXTENDED_RESULT_CODE "AT+CMEE=1\r"
-
-#define AG_FEATURE_3WAY 0x1
-#define AG_FEATURE_EXTENDED_RES_CODE 0x100
-/* Hold and multipary AG features.
- * Comments below are copied from hands-free spec for reference */
-/* Releases all held calls or sets User Determined User Busy (UDUB)
- * for a waiting call */
-#define AG_CHLD_0 0x01
-/* Releases all active calls (if any exist) and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_1 0x02
-/* Releases specified active call only <x> */
-#define AG_CHLD_1x 0x04
-/* Places all active calls (if any exist) on hold and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_2 0x08
-/* Request private consultation mode with specified call <x> (Place all
- * calls on hold EXCEPT the call <x>) */
-#define AG_CHLD_2x 0x10
-/* Adds a held call to the conversation */
-#define AG_CHLD_3 0x20
-/* Connects the two calls and disconnects the subscriber from both calls
- * (Explicit Call Transfer). Support for this value and its associated
- * functionality is optional for the HF. */
-#define AG_CHLD_4 0x40
-
-#define OK_RESPONSE "\r\nOK\r\n"
-#define ERROR_RESPONSE "\r\nERROR\r\n"
-
-struct indicator {
-	gchar descr[AG_INDICATOR_DESCR_SIZE];
-	gint value;
-};
 
 struct gateway {
 	gateway_state_t state;
@@ -108,387 +62,10 @@ struct gateway {
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
 	DBusMessage *connect_message;
-	guint ag_features;
-	guint hold_multiparty_features;
-	GSList *indies;
-	gboolean is_dialing;
-	gboolean call_active;
-
-	int sp_gain;
-	int mic_gain;
 };
 
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device);
-
 int gateway_close(struct audio_device *device);
 
-static void rfcomm_start_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	gw->rfcomm_watch_id = g_io_add_watch(gw->rfcomm,
-			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-			(GIOFunc) rfcomm_ag_data_cb, dev);
-}
-
-static void rfcomm_stop_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	g_source_remove(gw->rfcomm_watch_id);
-}
-
-static gboolean io_channel_write_all(GIOChannel *io, gchar *data,
-					gsize count)
-{
-	gsize written = 0;
-	GIOStatus status;
-
-	while (count > 0) {
-		status = g_io_channel_write_chars(io, data, count, &written,
-						NULL);
-		if (status != G_IO_STATUS_NORMAL)
-			return FALSE;
-
-		data += written;
-		count -= written;
-	}
-	return TRUE;
-}
-
-/* it's worth to mention that data and response could be the same pointers */
-static gboolean rfcomm_send_and_read(struct gateway *gw, gchar *data,
-                                    gchar *response, gsize count)
-{
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read = 0;
-	gboolean got_ok = FALSE;
-	gboolean got_error = FALSE;
-	gchar *resp_buf = response;
-	gsize toread = RFCOMM_BUF_SIZE - 1;
-	GIOStatus status;
-
-	if (!io_channel_write_all(rfcomm, data, count))
-		return FALSE;
-
-	while (!(got_ok || got_error)) {
-		status = g_io_channel_read_chars(rfcomm, resp_buf, toread,
-						&read, NULL);
-		if (status == G_IO_STATUS_NORMAL)
-			resp_buf[read] = '\0';
-		else {
-			debug("rfcomm_send_and_read(): %m");
-			return FALSE;
-		}
-		got_ok = NULL != strstr(resp_buf, OK_RESPONSE);
-		got_error = NULL != strstr(resp_buf, ERROR_RESPONSE);
-		resp_buf += read;
-		toread -= read;
-	}
-	return TRUE;
-}
-
-/* get <descr> from the names: (<descr>, (<values>)), (<descr>, (<values>))
- * ... */
-static GSList *parse_indicator_names(gchar *names, GSList *indies)
-{
-	gchar *current = names - 1;
-	GSList *result = indies;
-	gchar *next;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 2;
-		next = strstr(current, ",(");
-		ind = g_slice_new(struct indicator);
-		strncpy(ind->descr, current, 20);
-		ind->descr[(intptr_t) next - (intptr_t) current] = '\0';
-		result = g_slist_append(result, (gpointer) ind);
-		current = strstr(next + 1, ",(");
-	}
-	return result;
-}
-
-/* get values from <val0>,<val1>,... */
-static GSList *parse_indicator_values(gchar *values, GSList *indies)
-{
-	gint val;
-	gchar *current = values - 1;
-	GSList *runner = indies;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 1;
-		sscanf(current, "%d", &val);
-		current = strchr(current, ',');
-		ind = g_slist_nth_data(runner, 0);
-		ind->value = val;
-		runner = g_slist_next(runner);
-	}
-	return indies;
-}
-
-/* get values from <val0>,<val1>,... */
-static guint get_hold_mpty_features(gchar *features)
-{
-	guint result = 0;
-
-	if (strstr(features, "0"))
-		result |= AG_CHLD_0;
-
-	if (strstr(features, "1"))
-		result |= AG_CHLD_1;
-
-	if (strstr(features, "1x"))
-		result |= AG_CHLD_1x;
-
-	if (strstr(features, "2"))
-		result |= AG_CHLD_2;
-
-	if (strstr(features, "2x"))
-		result |= AG_CHLD_2x;
-
-	if (strstr(features, "3"))
-		result |= AG_CHLD_3;
-
-	if (strstr(features, "4"))
-		result |= AG_CHLD_4;
-
-	return result;
-}
-
-static gboolean establish_service_level_conn(struct gateway *gw)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	gboolean res;
-
-	debug("at the begin of establish_service_level_conn()");
-	res = rfcomm_send_and_read(gw, AG_FEATURES, buf,
-				sizeof(AG_FEATURES) - 1);
-	if (!res || sscanf(buf, "\r\n+BRSF:%d", &gw->ag_features) != 1)
-		return FALSE;
-
-	debug("features are 0x%X", gw->ag_features);
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_SUPP, buf,
-				sizeof(AG_INDICATORS_SUPP) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_names(strchr(buf, '('), NULL);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_VAL, buf,
-		sizeof(AG_INDICATORS_VAL) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_values(strchr(buf, ':') + 1, gw->indies);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_ENABLE, buf,
-				sizeof(AG_INDICATORS_ENABLE) - 1);
-	if (!res || !strstr(buf, "OK"))
-		return FALSE;
-
-	if ((gw->ag_features & AG_FEATURE_3WAY) != 0) {
-		res = rfcomm_send_and_read(gw, AG_HOLD_MPTY_SUPP, buf,
-				sizeof(AG_HOLD_MPTY_SUPP) - 1);
-		if (!res || !strstr(buf, "+CHLD:")) {
-			g_slice_free1(RFCOMM_BUF_SIZE, buf);
-			return FALSE;
-		}
-		gw->hold_multiparty_features = get_hold_mpty_features(
-							strchr(buf, '('));
-
-	} else
-		gw->hold_multiparty_features = 0;
-
-	debug("Service layer connection successfully established!");
-	rfcomm_send_and_read(gw, AG_CALLER_IDENT_ENABLE, buf,
-			sizeof(AG_CALLER_IDENT_ENABLE) - 1);
-	rfcomm_send_and_read(gw, AG_CARRIER_FORMAT, buf,
-			sizeof(AG_CARRIER_FORMAT) - 1);
-	if ((gw->ag_features & AG_FEATURE_EXTENDED_RES_CODE) != 0)
-		rfcomm_send_and_read(gw, AG_EXTENDED_RESULT_CODE, buf,
-			sizeof(AG_EXTENDED_RESULT_CODE) - 1);
-
-	return TRUE;
-}
-
-static void process_ind_change(struct audio_device *dev, guint index,
-							gint value)
-{
-	struct gateway *gw = dev->gateway;
-	struct indicator *ind = g_slist_nth_data(gw->indies, index - 1);
-	gchar *name = ind->descr;
-
-	ind->value = value;
-
-	debug("at the begin of process_ind_change, name is %s", name);
-	if (!strcmp(name, "\"call\"")) {
-		if (value > 0) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallStarted", DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-			gw->call_active = TRUE;
-		} else {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallEnded", DBUS_TYPE_INVALID);
-			gw->call_active = FALSE;
-		}
-
-	} else if (!strcmp(name, "\"callsetup\"")) {
-		if (value == 0 && gw->is_dialing) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-		} else if (!gw->is_dialing && value > 0)
-			gw->is_dialing = TRUE;
-
-	} else if (!strcmp(name, "\"callheld\"")) {
-		/* FIXME: The following code is based on assumptions only.
-		 * Has to be tested for interoperability
-		 * I assume that callheld=2 would be sent when dial from HF
-		 * failed in case of 3-way call
-		 * Unfortunately this path is not covered by the HF spec so
-		 * the code has to be tested for interop
-		*/
-		/* '2' means: all calls held, no active calls */
-		if (value == 2) {
-			if (gw->is_dialing) {
-				g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-				gw->is_dialing = FALSE;
-			}
-		}
-	} else if (!strcmp(name, "\"service\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RegistrationStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"signal\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "SignalStrength",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"roam\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RoamingStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"battchg\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "BatteryCharge",
-				DBUS_TYPE_UINT16, &value);
-}
-
-static void process_ring(struct audio_device *device, GIOChannel *chan,
-			gchar *buf)
-{
-	gchar number[AG_CALLER_NUM_SIZE];
-	gchar *cli;
-	gchar *sep;
-	gsize read;
-	GIOStatus status;
-
-	rfcomm_stop_watch(device);
-	status = g_io_channel_read_chars(chan, buf, RFCOMM_BUF_SIZE - 1, &read, NULL);
-	if (status != G_IO_STATUS_NORMAL)
-		return;
-
-	debug("at the begin of process_ring");
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 10)
-		error("process_ring(): buf is too long '%s'", buf);
-	else if ((cli = strstr(buf, "\r\n+CLIP"))) {
-		if (sscanf(cli, "\r\n+CLIP: \"%s", number) == 1) {
-			sep = strchr(number, '"');
-			sep[0] = '\0';
-
-			/* FIXME:signal will be emitted on each RING+CLIP.
-			 * That's bad */
-			cli = number;
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE, "Ring",
-					DBUS_TYPE_STRING, &cli,
-					DBUS_TYPE_INVALID);
-			device->gateway->is_dialing = TRUE;
-		} else
-			error("process_ring(): '%s' in place of +CLIP after RING", buf);
-
-	}
-
-	rfcomm_start_watch(device);
-}
-
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	struct gateway *gw;
-	gsize read;
-	/* some space for value */
-	gchar indicator[AG_INDICATOR_DESCR_SIZE + 4];
-	gint value;
-	guint index;
-	gchar *sep;
-
-	debug("at the begin of rfcomm_ag_data_cb()");
-	if (cond & G_IO_NVAL)
-		return FALSE;
-
-	gw = device->gateway;
-
-	if (cond & (G_IO_ERR | G_IO_HUP)) {
-		debug("connection with remote BT is closed");
-		gateway_close(device);
-		return FALSE;
-	}
-
-	if (g_io_channel_read_chars(chan, buf, sizeof(buf) - 1, &read, NULL)
-			!= G_IO_STATUS_NORMAL)
-		return TRUE;
-	buf[read] = '\0';
-
-	if (strlen(buf) > AG_INDICATOR_DESCR_SIZE + 14)
-		error("rfcomm_ag_data_cb(): buf is too long '%s'", buf);
-	else if (sscanf(buf, "\r\n+CIEV:%s\r\n", indicator) == 1) {
-		sep = strchr(indicator, ',');
-		sep[0] = '\0';
-		sep += 1;
-		index = atoi(indicator);
-		value = atoi(sep);
-		process_ind_change(device, index, value);
-	} else if (strstr(buf, "RING"))
-		process_ring(device, chan, buf);
-	else if (sscanf(buf, "\r\n+BVRA:%d\r\n", &value) == 1) {
-		if (value == 0)
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionActive",
-					DBUS_TYPE_INVALID);
-		else
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionInactive",
-					DBUS_TYPE_INVALID);
-	} else if (sscanf(buf, "\r\n+VGS:%d\r\n", &value) == 1) {
-		gw->sp_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "SpeakerGain",
-				DBUS_TYPE_UINT16, &value);
-	} else if (sscanf(buf, "\r\n+VGM:%d\r\n", &value) == 1) {
-		gw->mic_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "MicrophoneGain",
-				DBUS_TYPE_UINT16, &value);
-	} else
-		error("rfcomm_ag_data_cb(): read wrong data '%s'", buf);
-
-	return TRUE;
-}
-
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
 {
@@ -541,7 +118,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	DBusMessage *conn_mes = gw->connect_message;
 	gchar gw_addr[18];
 	GIOFlags flags;
 
@@ -563,29 +139,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 	if (!gw->rfcomm)
 		gw->rfcomm = g_io_channel_ref(chan);
 
-	if (establish_service_level_conn(dev->gateway)) {
-		gboolean value = TRUE;
-
-		debug("%s: Connected to %s", dev->path, gw_addr);
-		rfcomm_start_watch(dev);
-		if (conn_mes) {
-			DBusMessage *reply =
-				dbus_message_new_method_return(conn_mes);
-			dbus_connection_send(dev->conn, reply, NULL);
-			dbus_message_unref(reply);
-			dbus_message_unref(conn_mes);
-			gw->connect_message = NULL;
-		}
-
-		gw->state = GATEWAY_STATE_CONNECTED;
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN,	&value);
-		return;
-	} else
-		error("%s: Failed to establish service layer connection to %s",
-			dev->path, gw_addr);
-
 	if (NULL != gw->sco_start_cb)
 		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 
@@ -732,321 +285,20 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
-static DBusMessage *process_ag_reponse(DBusMessage *msg, gchar *response)
-{
-	DBusMessage *reply;
-
-
-	debug("in process_ag_reponse, response is %s", response);
-	if (strstr(response, OK_RESPONSE))
-		reply = dbus_message_new_method_return(msg);
-	else {
-		/* FIXME: some code should be here to processes errors
-		 *  in better fasion */
-		debug("AG responded with '%s' to %s method call", response,
-				dbus_message_get_member(msg));
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".OperationFailed",
-					"Operation failed.See log for details");
-	}
-	return reply;
-}
-
-static DBusMessage *process_simple(DBusMessage *msg, struct audio_device *dev,
-					gchar *data)
-{
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-
-	rfcomm_stop_watch(dev);
-	rfcomm_send_and_read(gw, data, buf, strlen(data));
-	rfcomm_start_watch(dev);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_ANSWER "ATA\r"
-
-static DBusMessage *ag_answer(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	if (gw->call_active)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".CallAlreadyAnswered",
-					"Call AlreadyAnswered");
-
-	return process_simple(msg, dev, AG_ANSWER);
-}
-
-#define AG_HANGUP "AT+CHUP\r"
-
-static DBusMessage *ag_terminate_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	return process_simple(msg, dev, AG_HANGUP);
-}
-
-/* according to GSM spec */
-#define ALLOWED_NUMBER_SYMBOLS "1234567890*#ABCD"
-#define AG_PLACE_CALL "ATD%s;\r"
-/* dialing from memory is not supported as headset spec doesn't define a way
- * to retreive phone memory entries.
- */
-static DBusMessage *ag_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint atd_len;
-	DBusMessage *result;
-
-	debug("at the begin of ag_call()");
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	atd_len = sprintf(buf, AG_PLACE_CALL, number);
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, buf, buf, atd_len);
-	rfcomm_start_watch(device);
-
-	result = process_ag_reponse(msg, buf);
-	return result;
-}
-
-#define AG_GET_CARRIER "AT+COPS?\r"
-
-static DBusMessage *ag_get_operator(DBusConnection *conn, DBusMessage *msg,
-					void *data)
-{
-	struct audio_device *dev = (struct audio_device *) data;
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read;
-	gchar *result, *sep;
-	DBusMessage *reply;
-	GIOStatus status;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(dev);
-	io_channel_write_all(rfcomm, AG_GET_CARRIER, strlen(AG_GET_CARRIER));
-
-	status = g_io_channel_read_chars(rfcomm, buf, RFCOMM_BUF_SIZE - 1,
-						&read, NULL);
-	rfcomm_start_watch(dev);
-	if (G_IO_STATUS_NORMAL == status) {
-		buf[read] = '\0';
-		if (strstr(buf, "+COPS")) {
-			if (!strrchr(buf, ','))
-				result = "0";
-			else {
-				result = strchr(buf, '\"') + 1;
-				sep = strchr(result, '\"');
-				sep[0] = '\0';
-			}
-
-			reply = dbus_message_new_method_return(msg);
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&result, DBUS_TYPE_INVALID);
-		} else {
-			info("ag_get_operator(): '+COPS' expected but"
-				" '%s' received", buf);
-			reply = dbus_message_new_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Unexpected response from AG");
-		}
-	} else {
-		error("ag_get_operator(): %m");
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".ConnectionFailed",
-					"Failed to receive response from AG");
-	}
-
-	return reply;
-}
-
-#define AG_SEND_DTMF "AT+VTS=%c\r"
-static DBusMessage *ag_send_dtmf(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint com_len;
-	gboolean got_ok = TRUE;
-	gint num_len;
-	gint i = 0;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	num_len = strlen(number);
-	rfcomm_stop_watch(device);
-	while (i < num_len && got_ok) {
-		com_len = sprintf(buf, AG_SEND_DTMF, number[i]);
-		rfcomm_send_and_read(gw, buf, buf, com_len);
-		got_ok = NULL != strstr(buf, OK_RESPONSE);
-		i += 1;
-	}
-	rfcomm_start_watch(device);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_GET_SUBSCRIBER_NUMS "AT+CNUM\r"
-#define CNUM_LEN 5             /* length of "+CNUM" string */
-#define MAX_NUMBER_CNT 16
-static DBusMessage *ag_get_subscriber_num(DBusConnection *conn,
-					DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number, *end;
-	DBusMessage *reply = dbus_message_new_method_return(msg);
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, AG_GET_SUBSCRIBER_NUMS, buf,
-			strlen(AG_GET_SUBSCRIBER_NUMS));
-	rfcomm_start_watch(device);
-
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 21)
-		error("ag_get_subscriber_num(): buf is too long '%s'", buf);
-	else if (strstr(buf, "+CNUM")) {
-		number = strchr(buf, ',');
-		number++;
-		end = strchr(number, ',');
-		if (end) {
-			*end = '\0';
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&number, DBUS_TYPE_INVALID);
-		}
-	} else
-		error("ag_get_subscriber_num(): read wrong data '%s'", buf);
-
-	return reply;
-}
-
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	DBusMessage *reply;
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	gboolean value;
-	guint index = 0;
-	struct indicator *ind;
-
-	reply = dbus_message_new_method_return(msg);
-	if (!reply)
-		return NULL;
-
-	dbus_message_iter_init_append(reply, &iter);
-
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
-	/* Connected */
-	value = gateway_is_connected(device);
-	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
-
-	if (!value)
-		goto done;
-
-	while ((ind = g_slist_nth_data(gw->indies, index))) {
-		if(!strcmp(ind->descr, "\"service\""))
-			dict_append_entry(&dict, "RegistrationStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"signal\""))
-			dict_append_entry(&dict, "SignalStrength",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"roam\""))
-			dict_append_entry(&dict, "RoamingStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"battchg\""))
-			dict_append_entry(&dict, "BatteryCharge",
-					DBUS_TYPE_UINT16, &ind->value);
-		index++;
-	}
-
-	/* SpeakerGain */
-	dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16,
-				&device->gateway->sp_gain);
-
-	/* MicrophoneGain */
-	dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16,
-				&device->gateway->mic_gain);
-done:
-	dbus_message_iter_close_container(&iter, &dict);
-	return reply;
+	return NULL;
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "Disconnect", "", "", ag_disconnect },
-	{ "AnswerCall", "", "", ag_answer },
-	{ "TerminateCall", "", "", ag_terminate_call },
-	{ "Call", "s", "", ag_call },
-	{ "GetOperatorName", "", "s", ag_get_operator },
-	{ "SendDTMF", "s", "", ag_send_dtmf },
-	{ "GetSubscriberNumber", "", "s", ag_get_subscriber_num },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
 	{ NULL, NULL, NULL, NULL }
 };
 
 static GDBusSignalTable gateway_signals[] = {
-	{ "Ring", "s" },
-	{ "CallTerminated", "" },
-	{ "CallStarted", "" },
-	{ "CallEnded", "" },
 	{ "PropertyChanged", "sv" },
 	{ NULL, NULL }
 };
@@ -1063,9 +315,6 @@ struct gateway *gateway_init(struct audio_device *dev)
 
 	debug("in gateway_init, dev is %p", dev);
 	gw = g_new0(struct gateway, 1);
-	gw->indies = NULL;
-	gw->is_dialing = FALSE;
-	gw->call_active = FALSE;
 	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
 
@@ -1107,11 +356,6 @@ void gateway_start_service(struct audio_device *device)
 	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
 }
 
-static void indicator_slice_free(gpointer mem)
-{
-	g_slice_free(struct indicator, mem);
-}
-
 int gateway_close(struct audio_device *device)
 {
 	struct gateway *gw = device->gateway;
@@ -1119,8 +363,6 @@ int gateway_close(struct audio_device *device)
 	GIOChannel *sco = gw->sco;
 	gboolean value = FALSE;
 
-	g_slist_foreach(gw->indies, (GFunc) indicator_slice_free, NULL);
-	g_slist_free(gw->indies);
 	if (rfcomm) {
 		g_io_channel_shutdown(rfcomm, TRUE, NULL);
 		g_io_channel_unref(rfcomm);
-- 
1.6.4.4


[-- Attachment #3: 0002-Implement-HandsfreeGateway-Interface.patch --]
[-- Type: application/octet-stream, Size: 24005 bytes --]

From 48f3b333a82bdbb0e3be46d23b24012a1b4fd440 Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 8 Jan 2010 17:38:21 -0200
Subject: [PATCH 2/2] Implement HandsfreeGateway Interface

Create a interface where a Handsfree agent can register and use BlueZ to
handle the rfcomm and sco links.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for his
prototype on this code.
---
 audio/gateway.c |  443 +++++++++++++++++++++++++++++++++++++++++++-----------
 audio/gateway.h |   12 +-
 audio/manager.c |   20 +--
 audio/unix.c    |    8 +
 doc/hfp-api.txt |   81 ++++++++++
 5 files changed, 456 insertions(+), 108 deletions(-)
 create mode 100644 doc/hfp-api.txt

diff --git a/audio/gateway.c b/audio/gateway.c
index 3dc09ff..d364fdc 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -5,6 +5,8 @@
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
  *  Copyright (C) 2008-2009  Leonid Movshovich <event.riga@gmail.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
+ *  Copyright (C) 2010  Gustavo F. Padovan <padovan@profusion.mobi>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -32,12 +34,15 @@
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
 
 #include <glib.h>
 #include <dbus/dbus.h>
 #include <gdbus.h>
 
 #include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
 #include <bluetooth/sco.h>
@@ -52,19 +57,101 @@
 #include "btio.h"
 #include "dbus-common.h"
 
-#define RFCOMM_BUF_SIZE 256
+#define MAX_OPEN_TRIES 5
+#define OPEN_WAIT 300
+
+struct agent {
+	char *name;	/* Bus id */
+	char *path;	/* D-Bus path */
+	guint watch;	/* Disconnect watch */
+};
 
 struct gateway {
 	gateway_state_t state;
-	GIOChannel *rfcomm;
-	guint rfcomm_watch_id;
+	int channel;
+	GIOChannel *rfcomm;	/* remote AG requested rfcomm connection */
 	GIOChannel *sco;
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
-	DBusMessage *connect_message;
+	struct agent *agent;
+	DBusMessage *msg;
 };
 
-int gateway_close(struct audio_device *device);
+static DBusConnection *connection;
+
+int gateway_close(gpointer data);
+
+static const char *connected2str(int i)
+{
+	switch (i) {
+		case GATEWAY_STATE_DISCONNECTED:
+			return "disconnected";
+		case GATEWAY_STATE_CONNECTING:
+			return "connecting";
+		case GATEWAY_STATE_CONNECTED:
+			return "connected";
+		case GATEWAY_STATE_PLAYING:
+			return "playing";
+		default:
+			return "";
+	}
+}
+
+static void agent_free(struct agent *agent)
+{
+	if (!agent)
+		return;
+
+	g_free(agent->name);
+	g_free(agent->path);
+	g_free(agent);
+}
+
+static void agent_disconnect(struct agent *agent)
+{
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "Release");
+
+	dbus_message_set_no_reply(msg, TRUE);
+	g_dbus_send_message(connection, msg);
+
+	g_dbus_remove_watch(connection, agent->watch);
+}
+
+static gboolean agent_sendfd(struct agent *agent, int fd,
+		DBusPendingCallNotifyFunction notify, void *data)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "NewConnection");
+
+	dbus_message_append_args(msg, DBUS_TYPE_UNIX_FD, &fd,
+					DBUS_TYPE_INVALID);
+
+	if (dbus_connection_send_with_reply(connection, msg, &call, -1) == FALSE)
+		return FALSE;
+
+	dbus_pending_call_set_notify(call, notify, data, NULL);
+	dbus_pending_call_unref(call);
+
+	return TRUE;
+}
+
+static void change_state(struct audio_device *dev, int new_state)
+{
+	struct gateway *gw = dev->gateway;
+	const char *val = connected2str(new_state);
+
+	gw->state = new_state;
+
+	emit_property_changed(connection,dev->path,
+			AUDIO_GATEWAY_INTERFACE, "State",
+			DBUS_TYPE_STRING, &val);
+}
 
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
@@ -79,6 +166,7 @@ static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 		g_io_channel_shutdown(gw->sco, TRUE, NULL);
 		g_io_channel_unref(gw->sco);
 		gw->sco = NULL;
+		change_state(dev, GATEWAY_STATE_CONNECTED);
 		return FALSE;
 	}
 
@@ -92,63 +180,101 @@ static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
 
 	debug("at the begin of sco_connect_cb() in gateway.c");
 
+	gw->sco = chan;
+	g_io_channel_ref(chan);
+
 	if (err) {
 		error("sco_connect_cb(): %s", err->message);
-		/* not sure, but from other point of view,
-		 * what is the reason to have headset which
-		 * cannot play audio? */
-		if (gw->sco_start_cb)
-			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 		gateway_close(dev);
 		return;
 	}
 
-	gw->sco = g_io_channel_ref(chan);
 	if (gw->sco_start_cb)
 		gw->sco_start_cb(dev, gw->sco_start_cb_data);
 
-	/* why is this here? */
 	fcntl(g_io_channel_unix_get_fd(chan), F_SETFL, 0);
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
 				(GIOFunc) sco_io_cb, dev);
 }
 
+static void newconnection_reply(DBusPendingCall *call, void *data)
+{
+	struct gateway *gw = data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError derr;
+
+	if (!gw->rfcomm) {
+		debug("RFCOMM disconnected from server before agent reply");
+		return;
+	}
+
+	dbus_error_init(&derr);
+	if (!dbus_set_error_from_message(&derr, reply)) {
+		info("Agent reply: file descriptor passed successfuly");
+		return;
+	}
+
+	debug("Agent reply: %s", derr.message);
+
+	dbus_error_free(&derr);
+	g_idle_add(gateway_close, gw);
+}
+
 static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 				gpointer user_data)
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	gchar gw_addr[18];
-	GIOFlags flags;
+	struct rfcomm_dev_req req;
+	DBusMessage *reply;
+	int sk;
+	char src[18], dst[18];
 
 	if (err) {
 		error("connect(): %s", err->message);
 		if (gw->sco_start_cb)
 			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
-		return;
+		goto fail;
 	}
 
-	ba2str(&dev->dst, gw_addr);
-	/* Blocking mode should be default, but just in case: */
-	flags = g_io_channel_get_flags(chan);
-	flags &= ~G_IO_FLAG_NONBLOCK;
-	flags &= G_IO_FLAG_MASK;
-	g_io_channel_set_flags(chan, flags, NULL);
-	g_io_channel_set_encoding(chan, NULL, NULL);
-	g_io_channel_set_buffered(chan, FALSE);
-	if (!gw->rfcomm)
-		gw->rfcomm = g_io_channel_ref(chan);
+	ba2str(&dev->src, src);
+	ba2str(&dev->dst, dst);
 
-	if (NULL != gw->sco_start_cb)
-		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
+	/* make /dev/rfcomm serial port from chan */
+	memset(&req, 0, sizeof(req));
+	req.dev_id = -1;
+	req.flags = (1 << RFCOMM_REUSE_DLC);
+	bacpy(&req.src, &dev->src);
+	bacpy(&req.dst, &dev->dst);
+	req.channel = gw->channel;
 
-	gateway_close(dev);
+	sk = g_io_channel_unix_get_fd(chan);
+
+	change_state(dev, GATEWAY_STATE_CONNECTED);
+
+	if (!agent_sendfd(gw->agent, sk, newconnection_reply, gw)) {
+		reply = g_dbus_create_error(gw->msg, ERROR_INTERFACE ".Failed",
+				"Can not pass file descriptor");
+	} else
+		reply = dbus_message_new_method_return(gw->msg);
+
+	return;
+
+fail:
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".Failed", ".Connection attempt failed");
+		dbus_message_unref(gw->msg);
+		gw->msg = NULL;
+	}
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
+	return;
 }
 
 static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 {
 	struct audio_device *dev = user_data;
-	DBusMessage *msg = dev->gateway->connect_message;
+	struct gateway *gw = dev->gateway;
 	int ch = -1;
 	sdp_list_t *protos, *classes;
 	uuid_t uuid;
@@ -182,8 +308,6 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 
 	if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
 			uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
-		sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free,
-									NULL);
 		sdp_list_free(protos, NULL);
 		error("Invalid service record or not HFP");
 		goto fail;
@@ -197,6 +321,8 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 		goto fail;
 	}
 
+	gw->channel = ch;
+
 	io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -204,25 +330,26 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 				BT_IO_OPT_INVALID);
 	if (!io) {
 		error("Unable to connect: %s", err->message);
-		if (msg) {
-			error_common_reply(dev->conn, msg, ERROR_INTERFACE
-						".ConnectionAttemptFailed",
-						err->message);
-			msg = NULL;
-		}
-		g_error_free(err);
+		if (err)
+			g_error_free(err);
 		gateway_close(dev);
+		goto fail;
 	}
 
 	g_io_channel_unref(io);
+
+	change_state(dev, GATEWAY_STATE_CONNECTING);
 	return;
 
 fail:
-	if (msg)
-		error_common_reply(dev->conn, msg, ERROR_INTERFACE
-					".NotSupported", "Not supported");
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".NotSupported", "Not supported");
+		dbus_message_unref(gw->msg);
+		gw->msg = NULL;
+	}
 
-	dev->gateway->connect_message = NULL;
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
 
 	sco_cb = dev->gateway->sco_start_cb;
 	if (sco_cb)
@@ -243,22 +370,57 @@ static DBusMessage *ag_connect(DBusConnection *conn, DBusMessage *msg,
 {
 	struct audio_device *au_dev = (struct audio_device *) data;
 	struct gateway *gw = au_dev->gateway;
+	DBusMessage *reply;
 
 	debug("at the begin of ag_connect()");
-	if (gw->rfcomm)
+
+	if (!gw->agent)
 		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".AlreadyConnected",
-					"Already Connected");
+				".Failed", "Agent not assined");
 
-	gw->connect_message = dbus_message_ref(msg);
+	gw->msg = dbus_message_ref(msg);
 	if (get_records(au_dev) < 0) {
-		dbus_message_unref(gw->connect_message);
+		dbus_message_unref(gw->msg);
 		return g_dbus_create_error(msg, ERROR_INTERFACE
 					".ConnectAttemptFailed",
 					"Connect Attempt Failed");
 	}
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
 	debug("at the end of ag_connect()");
-	return NULL;
+
+	return reply;
+}
+
+int gateway_close(gpointer data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	gboolean value = FALSE;
+
+	if (gw->rfcomm) {
+		g_io_channel_shutdown(gw->rfcomm, TRUE, NULL);
+		g_io_channel_unref(gw->rfcomm);
+		gw->rfcomm = NULL;
+	}
+
+	if (gw->sco) {
+		g_io_channel_shutdown(gw->sco, TRUE, NULL);
+		g_io_channel_unref(gw->sco);
+		gw->sco = NULL;
+		gw->sco_start_cb = NULL;
+		gw->sco_start_cb_data = NULL;
+	}
+
+	gw->state = GATEWAY_STATE_DISCONNECTED;
+
+	emit_property_changed(device->conn, device->path,
+				AUDIO_GATEWAY_INTERFACE,
+				"Connected", DBUS_TYPE_BOOLEAN, &value);
+	return 0;
 }
 
 static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
@@ -269,6 +431,9 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	DBusMessage *reply = NULL;
 	char gw_addr[18];
 
+	if (!connection)
+		return NULL;
+
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
 		return NULL;
@@ -285,16 +450,117 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
+static void agent_exited(DBusConnection *conn, void *data)
+{
+	struct gateway *gateway = data;
+	struct agent *agent = gateway->agent;
+
+	debug("Agent %s exited", agent->name);
+
+	agent_free(agent);
+	gateway->agent = NULL;
+}
+
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	return NULL;
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	const char *value;
+
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	value = connected2str(gw->state);
+	dict_append_entry(&dict, "Connected",
+			DBUS_TYPE_STRING, &value);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct agent *agent;
+	const char *path, *name;
+
+
+	if (gw->agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".AlreadyExists",
+				"Agent already exists");
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+			DBUS_TYPE_INVALID))
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+				".InvalidArguments", "Invalid argument");
+
+	name = dbus_message_get_sender(msg);
+	agent = g_new0(struct agent, 1);
+	if (!agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed",
+				"Failed to create a new agent");
+
+	agent->name = strdup(name);
+	agent->path = strdup(path);
+
+	agent->watch = g_dbus_add_disconnect_watch(conn, name,
+			agent_exited, gw, NULL);
+
+	gw->agent = agent;
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+				DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct agent *agent;
+
+	if (!gw->agent)
+		goto done;
+
+	agent = gw->agent;
+	if (strcmp(agent->name, dbus_message_get_sender(msg)) != 0)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed", "Permission denied");
+
+	agent_disconnect(gw->agent);
+
+	g_dbus_remove_watch(conn, agent->watch);
+
+	agent_free(agent);
+	gw->agent = NULL;
+
+done:
+	return dbus_message_new_method_return(msg);
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
-	{ "Disconnect", "", "", ag_disconnect },
+	{ "Disconnect", "", "", ag_disconnect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
+	{ "RegisterAgent", "o", "", register_agent },
+	{ "UnregisterAgent", "o", "", unregister_agent },
 	{ NULL, NULL, NULL, NULL }
 };
 
@@ -307,17 +573,23 @@ struct gateway *gateway_init(struct audio_device *dev)
 {
 	struct gateway *gw;
 
+	gw = g_new0(struct gateway, 1);
+
+	connection = dbus_connection_ref(dev->conn);
+
 	if (!g_dbus_register_interface(dev->conn, dev->path,
 					AUDIO_GATEWAY_INTERFACE,
 					gateway_methods, gateway_signals,
 					NULL, dev, NULL))
 		return NULL;
 
-	debug("in gateway_init, dev is %p", dev);
-	gw = g_new0(struct gateway, 1);
-	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
+}
 
+void gateway_exit()
+{
+	dbus_connection_unref(connection);
+	connection = NULL;
 }
 
 gboolean gateway_is_connected(struct audio_device *dev)
@@ -326,17 +598,6 @@ gboolean gateway_is_connected(struct audio_device *dev)
 			dev->gateway->state == GATEWAY_STATE_CONNECTED);
 }
 
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
-{
-	if (!io)
-		return -EINVAL;
-
-	g_io_channel_ref(io);
-	dev->gateway->rfcomm = io;
-
-	return 0;
-}
-
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 {
 	struct gateway *gw = dev->gateway;
@@ -347,42 +608,39 @@ int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 	gw->sco = g_io_channel_ref(io);
 
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                                (GIOFunc) sco_io_cb, dev);
+				 (GIOFunc) sco_io_cb, dev);
+
+	change_state(dev, GATEWAY_STATE_PLAYING);
+
 	return 0;
 }
 
-void gateway_start_service(struct audio_device *device)
+int gateway_connect_rfcomm(struct audio_device *dev,
+		GIOChannel *chan, int ch)
 {
-	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
+	if (!chan)
+		return -EINVAL;
+
+	g_io_channel_ref(chan);
+	dev->gateway->rfcomm = chan;
+	dev->gateway->channel = ch;
+
+	return 0;
 }
 
-int gateway_close(struct audio_device *device)
+void gateway_start_service(struct audio_device *dev)
 {
-	struct gateway *gw = device->gateway;
-	GIOChannel *rfcomm = gw->rfcomm;
-	GIOChannel *sco = gw->sco;
-	gboolean value = FALSE;
+	struct gateway *gw = dev->gateway;
+	GError *err = NULL;
 
-	if (rfcomm) {
-		g_io_channel_shutdown(rfcomm, TRUE, NULL);
-		g_io_channel_unref(rfcomm);
-		gw->rfcomm = NULL;
-	}
+	if (gw->rfcomm == NULL)
+		return;
 
-	if (sco) {
-		g_io_channel_shutdown(sco, TRUE, NULL);
-		g_io_channel_unref(sco);
-		gw->sco = NULL;
-		gw->sco_start_cb = NULL;
-		gw->sco_start_cb_data = NULL;
+	if (!bt_io_accept(gw->rfcomm, rfcomm_connect_cb, dev, NULL,
+							&err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
 	}
-
-	gw->state = GATEWAY_STATE_DISCONNECTED;
-
-	emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN, &value);
-	return 0;
 }
 
 /* These are functions to be called from unix.c for audio system
@@ -399,8 +657,12 @@ gboolean gateway_request_stream(struct audio_device *dev,
 		gw->sco_start_cb_data = user_data;
 		get_records(dev);
 	} else if (!gw->sco) {
+		char source[128], destination[128];
+
 		gw->sco_start_cb = cb;
 		gw->sco_start_cb_data = user_data;
+		ba2str(&dev->src, source);
+		ba2str(&dev->dst, destination);
 		io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -464,4 +726,3 @@ void gateway_suspend_stream(struct audio_device *dev)
 	gw->sco_start_cb = NULL;
 	gw->sco_start_cb_data = NULL;
 }
-
diff --git a/audio/gateway.h b/audio/gateway.h
index 3b0457f..eaf6801 100644
--- a/audio/gateway.h
+++ b/audio/gateway.h
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -22,20 +23,25 @@
  *
  */
 
-#define AUDIO_GATEWAY_INTERFACE "org.bluez.HeadsetGateway"
+#define AUDIO_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
 
 #define DEFAULT_HSP_HS_CHANNEL 6
 #define DEFAULT_HFP_HS_CHANNEL 7
 
 typedef enum {
 	GATEWAY_STATE_DISCONNECTED,
-	GATEWAY_STATE_CONNECTED
+	GATEWAY_STATE_CONNECTED,
+	GATEWAY_STATE_CONNECTING,
+	GATEWAY_STATE_PLAYING,
 } gateway_state_t;
 
 typedef void (*gateway_stream_cb_t) (struct audio_device *dev, void *user_data);
+
 struct gateway *gateway_init(struct audio_device *device);
+void gateway_exit();
 gboolean gateway_is_connected(struct audio_device *dev);
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *chan);
+int gateway_connect_rfcomm(struct audio_device *dev,
+			GIOChannel *chan, int ch);
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *chan);
 void gateway_start_service(struct audio_device *device);
 gboolean gateway_request_stream(struct audio_device *dev,
diff --git a/audio/manager.c b/audio/manager.c
index 413c1f3..e768014 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -198,7 +198,7 @@ static void handle_uuid(const char *uuidstr, struct audio_device *device)
 		break;
 	case HANDSFREE_AGW_SVCLASS_ID:
 		debug("Found Handsfree AG record");
-		if (device->gateway == NULL)
+		if (enabled.gateway && (device->gateway == NULL))
 			device->gateway = gateway_init(device);
 		break;
 	case AUDIO_SINK_SVCLASS_ID:
@@ -586,7 +586,7 @@ static void hf_io_cb(GIOChannel *chan, gpointer data)
 		goto drop;
 	}
 
-	if (gateway_connect_rfcomm(device, chan) < 0) {
+	if (gateway_connect_rfcomm(device, chan, ch) < 0) {
 		error("Allocating new GIOChannel failed!");
 		goto drop;
 	}
@@ -905,22 +905,12 @@ static void headset_server_remove(struct btd_adapter *adapter)
 static int gateway_server_probe(struct btd_adapter *adapter)
 {
 	struct audio_adapter *adp;
-	const gchar *path = adapter_get_path(adapter);
-	int ret;
-
-	DBG("path %s", path);
 
 	adp = audio_adapter_get(adapter);
 	if (!adp)
 		return -EINVAL;
 
-	ret = gateway_server_init(adp);
-	if (ret < 0) {
-		audio_adapter_ref(adp);
-		return ret;
-	}
-
-	return 0;
+	return gateway_server_init(adp);
 }
 
 static void gateway_server_remove(struct btd_adapter *adapter)
@@ -1166,8 +1156,10 @@ void audio_manager_exit(void)
 		telephony_exit();
 	}
 
-	if (enabled.gateway)
+	if (enabled.gateway) {
 		btd_unregister_adapter_driver(&gateway_server_driver);
+		gateway_exit();
+	}
 
 	if (enabled.source || enabled.sink)
 		btd_unregister_adapter_driver(&a2dp_server_driver);
diff --git a/audio/unix.c b/audio/unix.c
index 5cf4f94..bd1a415 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -395,6 +395,9 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	struct bt_start_stream_rsp *rsp = (void *) buf;
 	struct bt_new_stream_ind *ind = (void *) buf;
 
+	if (!dev)
+		goto failed;
+
 	memset(buf, 0, sizeof(buf));
 	rsp->h.type = BT_RESPONSE;
 	rsp->h.name = BT_START_STREAM;
@@ -416,6 +419,11 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	}
 
 	client->req_id = 0;
+	return;
+
+failed:
+	error("gateway_resume_complete: resume failed");
+	unix_ipc_error(client, BT_START_STREAM, EIO);
 }
 
 static void headset_suspend_complete(struct audio_device *dev, void *user_data)
diff --git a/doc/hfp-api.txt b/doc/hfp-api.txt
new file mode 100644
index 0000000..ed52be1
--- /dev/null
+++ b/doc/hfp-api.txt
@@ -0,0 +1,81 @@
+Gateway hierarchy
+========================
+
+Service		org.bluez
+Interface	org.bluez.HandsfreeGateway
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+This interface is available for remote devices which can function in the Audio
+Gateway role of the HFP profiles.  It is intended to be used with external
+telephony stacks / handlers of the HFP protocol.
+
+Methods		void Connect()
+
+			Connect to the AG service on the remote device.
+
+		void Disconnect()
+
+			Disconnect from the AG service on the remote device
+
+		dict GetProperties()
+
+			Returns all properties for the interface. See the
+			properties section for available properties.
+
+		void RegisterAgent(object path)
+
+			The object path defines the path the of the agent
+			that will be called when a new Handsfree connection
+			is established.
+			
+			If an application disconnects from the bus all of its
+			registered agents will be removed.
+
+		void UnregisterAgent(object path)
+
+			This unregisters the agent that has been previously
+			registered. The object path parameter must match the
+			same value that has been used on registration.
+
+Signals		PropertyChanged(string name, variant value)
+
+			This signal indicates a changed value of the given
+			property.
+
+Properties	string State [readonly]
+
+			Indicates the state of the connection.  Possible
+			values are:
+				"disconnected"
+				"connecting"
+				"connected"
+				"playing"
+
+HandsfreeAgent hierarchy
+===============
+
+Service         unique name
+Interface       org.bluez.HandsfreeAgent
+Object path     freely definable
+
+Methods		void NewConnection(filedescriptor fd)
+
+			This method gets called whenever a new handsfree
+			connection has been established.  The objectpath
+			contains the object path of the remote device.  This
+			method assumes that DBus daemon with file descriptor
+			passing capability is being used.
+
+			The agent should only return successfully once the
+			establishment of the service level connection (SLC)
+			has been completed.  In the case of Handsfree this
+			means that BRSF exchange has been performed and
+			necessary initialization has been done.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.Failed
+
+		void Release()
+
+			This method gets called whenever the service daemon
+			unregisters the agent.
-- 
1.6.4.4


[-- Attachment #4: 0001-Add-HFP-support-in-oFono.patch --]
[-- Type: application/octet-stream, Size: 13597 bytes --]

From 1d349ded8c38831b36600f53a8dd9b781d9e13eb Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 8 Jan 2010 17:25:43 -0200
Subject: [PATCH] Add HFP support in oFono

It uses BlueZ through to get HFP working following the
org.bluez.HandsfreeGateway and org.bluez.HandsfreeAgent from the BlueZ
D-Bus API.

You need the HFP patch into BlueZ too.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for its prototype
on this code.
---
 drivers/hfpmodem/hfpmodem.h |    2 +
 gatchat/gattty.c            |   23 ++++
 gatchat/gattty.h            |    1 +
 include/modem.h             |    2 +
 plugins/hfp.c               |  284 +++++++++++++++++++++++++++++++++++++++++--
 plugins/modemconf.c         |    3 +-
 src/modem.c                 |   15 +++
 7 files changed, 318 insertions(+), 12 deletions(-)

diff --git a/drivers/hfpmodem/hfpmodem.h b/drivers/hfpmodem/hfpmodem.h
index 509846b..5fee68f 100644
--- a/drivers/hfpmodem/hfpmodem.h
+++ b/drivers/hfpmodem/hfpmodem.h
@@ -63,11 +63,13 @@ enum hfp_indicator {
 
 struct hfp_data {
 	GAtChat *chat;
+	char *handsfree_path;
 	unsigned int ag_features;
 	unsigned int ag_mpty_features;
 	unsigned int hf_features;
 	unsigned char cind_pos[HFP_INDICATOR_LAST];
 	unsigned int cind_val[HFP_INDICATOR_LAST];
+	unsigned int at_timeout;
 };
 
 extern void hfp_netreg_init();
diff --git a/gatchat/gattty.c b/gatchat/gattty.c
index 02ca389..ae12662 100644
--- a/gatchat/gattty.c
+++ b/gatchat/gattty.c
@@ -260,3 +260,26 @@ GIOChannel *g_at_tty_open(const char *tty, GHashTable *options)
 
 	return channel;
 }
+
+GIOChannel *g_at_get_channel_from_fd(int fd)
+{
+	GIOChannel *channel;
+	struct termios ti;
+
+	/* Switch TTY to raw mode */
+	memset(&ti, 0, sizeof(ti));
+	cfmakeraw(&ti);
+	tcflush(fd, TCIOFLUSH);
+	tcsetattr(fd, TCSANOW, &ti);
+
+	channel = g_io_channel_unix_new(fd);
+
+	if (channel == NULL) {
+		close(fd);
+		return NULL;
+	}
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+
+	return channel;
+}
diff --git a/gatchat/gattty.h b/gatchat/gattty.h
index dc3fe16..730f1dc 100644
--- a/gatchat/gattty.h
+++ b/gatchat/gattty.h
@@ -43,6 +43,7 @@ extern "C" {
  */
 GIOChannel *g_at_tty_open(const char *tty, GHashTable *options);
 
+GIOChannel *g_at_get_channel_from_fd(int fd);
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/modem.h b/include/modem.h
index b2aa607..bf28515 100644
--- a/include/modem.h
+++ b/include/modem.h
@@ -47,6 +47,8 @@ struct ofono_modem *ofono_modem_create(const char *name, const char *type);
 int ofono_modem_register(struct ofono_modem *modem);
 void ofono_modem_remove(struct ofono_modem *modem);
 
+struct ofono_modem *ofono_modem_get_by_path(const char *path);
+
 void ofono_modem_set_powered(struct ofono_modem *modem, ofono_bool_t powered);
 ofono_bool_t ofono_modem_get_powered(struct ofono_modem *modem);
 
diff --git a/plugins/hfp.c b/plugins/hfp.c
index 3bbd922..1e4eeb3 100644
--- a/plugins/hfp.c
+++ b/plugins/hfp.c
@@ -30,6 +30,8 @@
 #include <glib.h>
 #include <gatchat.h>
 #include <gattty.h>
+#include <gdbus.h>
+#include <ofono.h>
 
 #define OFONO_API_SUBJECT_TO_CHANGE
 #include <ofono/plugin.h>
@@ -52,11 +54,24 @@
 
 #include <drivers/hfpmodem/hfpmodem.h>
 
+#include <ofono/dbus.h>
+
+#define BLUEZ_SERVICE "org.bluez"
+#define BLUEZ_PATH "/"
+#define BLUEZ_MANAGER_INTERFACE "org.bluez.Manager"
+#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter"
+#define BLUEZ_DEVICE_INTERFACE "org.bluez.Device"
+#define BLUEZ_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
+
+#define HFP_AGENT_INTERFACE "org.bluez.HandsfreeAgent"
+
 static const char *brsf_prefix[] = { "+BRSF:", NULL };
 static const char *cind_prefix[] = { "+CIND:", NULL };
 static const char *cmer_prefix[] = { "+CMER:", NULL };
 static const char *chld_prefix[] = { "+CHLD:", NULL };
 
+static DBusConnection *connection;
+
 static int hfp_disable(struct ofono_modem *modem);
 
 static void hfp_debug(const char *str, void *user_data)
@@ -135,6 +150,60 @@ static void cmer_cb(gboolean ok, GAtResult *result, gpointer user_data)
 		sevice_level_conn_established(modem);
 }
 
+static int send_method_call(const char *dest, const char *path,
+				const char *interface, const char *method,
+				DBusPendingCallNotifyFunction cb,
+				void *user_data, int type, ...)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+	va_list args;
+
+	msg = dbus_message_new_method_call(dest, path, interface, method);
+	if (!msg) {
+		ofono_error("Unable to allocate new D-Bus %s message", method);
+		return -ENOMEM;
+	}
+
+	va_start(args, type);
+
+	if (!dbus_message_append_args_valist(msg, type, args)) {
+		dbus_message_unref(msg);
+		va_end(args);
+		return -EIO;
+	}
+
+	va_end(args);
+
+	if (!cb) {
+		g_dbus_send_message(connection, msg);
+		return 0;
+	}
+
+	if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+		ofono_error("Sending %s failed", method);
+		dbus_message_unref(msg);
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(call, cb, user_data, NULL);
+	dbus_pending_call_unref(call);
+	dbus_message_unref(msg);
+
+	return 0;
+}
+
+static gboolean hfp_enable_timeout(gpointer user)
+{
+	struct ofono_modem *modem = user;
+
+	if (ofono_modem_get_powered(modem))
+		return FALSE;
+
+	hfp_disable(modem);
+	return FALSE;
+}
+
 static void cind_status_cb(gboolean ok, GAtResult *result,
 				gpointer user_data)
 {
@@ -260,8 +329,7 @@ error:
 }
 
 /* either oFono or Phone could request SLC connection */
-static int service_level_connection(struct ofono_modem *modem,
-				const char *tty)
+static int service_level_connection(struct ofono_modem *modem, int fd)
 {
 	struct hfp_data *data = ofono_modem_get_data(modem);
 	GIOChannel *io;
@@ -269,7 +337,7 @@ static int service_level_connection(struct ofono_modem *modem,
 	GAtChat *chat;
 	char buf[64];
 
-	io = g_at_tty_open(tty, NULL);
+	io = g_at_get_channel_from_fd(fd);
 	if (!io) {
 		ofono_error("Service level connection failed: %s (%d)",
 			strerror(errno), errno);
@@ -296,9 +364,158 @@ static int service_level_connection(struct ofono_modem *modem,
 	return -EINPROGRESS;
 }
 
+static DBusMessage *hfp_agent_new_connection(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	int fd;
+	const char *path;
+	struct ofono_modem *modem = NULL;
+
+	path = dbus_message_get_path(msg);
+	modem = ofono_modem_get_by_path(path);
+
+	if (!modem)
+		return NULL;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UNIX_FD, &fd,
+				DBUS_TYPE_INVALID))
+		return __ofono_error_invalid_args(msg);
+
+	service_level_connection(modem, fd);
+
+	return NULL;
+}
+
+static DBusMessage *hfp_agent_release(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	return NULL;
+}
+
+static GDBusMethodTable agent_methods[] = {
+        { "NewConnection", "h", "", hfp_agent_new_connection,
+		G_DBUS_METHOD_FLAG_ASYNC },
+	{ "Release", "", "", hfp_agent_release },
+	{NULL, NULL, NULL, NULL}
+};
+
+static void find_device_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	struct ofono_modem *modem = user_data;
+	struct hfp_data *data = ofono_modem_get_data(modem);
+	const char *device, *obj_path;
+	int ret;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH,
+				&device, DBUS_TYPE_INVALID) == FALSE) {
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s", err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	ofono_debug("Using device %s", device);
+	data->handsfree_path = g_strdup(device);
+
+	obj_path = ofono_modem_get_path(modem);
+
+	ret = send_method_call(BLUEZ_SERVICE, device,
+				BLUEZ_GATEWAY_INTERFACE, "RegisterAgent",
+				NULL, NULL, DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+
+	if (ret < 0)
+		ofono_error("agent_register failed(%d)", ret);
+
+done:
+	dbus_message_unref(reply);
+}
+
+static void get_adapter_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	struct ofono_modem *modem = user_data;
+	const char *adapter = NULL;
+	const char *address;
+	int ret;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	dbus_error_init(&err);
+	if (dbus_message_get_args(reply, &err,
+				DBUS_TYPE_OBJECT_PATH,
+				&adapter, DBUS_TYPE_INVALID) == FALSE) {
+		if (adapter == NULL) {
+			ofono_error("bluetooth adapter is not enabled");
+			dbus_error_free(&err);
+			goto done;
+		}
+
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s %s", adapter, err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	address = ofono_modem_get_string(modem, "Address");
+
+	DBG("address %s", address);
+
+	if (address == NULL)
+		return;
+
+	ret = send_method_call(BLUEZ_SERVICE, adapter,
+				BLUEZ_ADAPTER_INTERFACE, "FindDevice",
+				find_device_cb, modem,
+				DBUS_TYPE_STRING, &address,
+				DBUS_TYPE_INVALID);
+
+	if (ret < 0)
+		ofono_error("find_device failed(%d)", ret);
+
+done:
+	dbus_message_unref(reply);
+
+}
+
+static int hfp_register_ofono_handsfree(struct ofono_modem *modem)
+{
+	ofono_debug("Register oFono Agent to bluetooth daemon");
+
+	return send_method_call(BLUEZ_SERVICE, BLUEZ_PATH,
+				BLUEZ_MANAGER_INTERFACE, "DefaultAdapter",
+				get_adapter_cb, modem,
+				DBUS_TYPE_INVALID);
+}
+
+static int hfp_unregister_ofono_handsfree(struct ofono_modem *modem)
+{
+	const char *obj_path = ofono_modem_get_path(modem);
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	ofono_debug("Unregister oFono Agent to bluetooth daemon");
+
+	if (!data->handsfree_path)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "UnregisterAgent",
+				NULL, NULL, DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+}
+
 static int hfp_probe(struct ofono_modem *modem)
 {
 	struct hfp_data *data;
+	const char *obj_path;
+	DBusConnection *conn = ofono_dbus_get_connection();
 
 	data = g_try_new0(struct hfp_data, 1);
 	if (!data)
@@ -312,39 +529,81 @@ static int hfp_probe(struct ofono_modem *modem)
 
 	ofono_modem_set_data(modem, data);
 
+	connection = dbus_connection_ref(conn);
+
+	obj_path = ofono_modem_get_path(modem);
+	g_dbus_register_interface(conn, obj_path, HFP_AGENT_INTERFACE,
+			agent_methods, NULL, NULL, data, NULL);
+
+	if (hfp_register_ofono_handsfree(modem) != 0)
+		return -EINVAL;
+
 	return 0;
 }
 
 static void hfp_remove(struct ofono_modem *modem)
 {
-	gpointer data = ofono_modem_get_data(modem);
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	hfp_unregister_ofono_handsfree(modem);
+
+	if (data->handsfree_path)
+		g_free(data->handsfree_path);
 
 	if (data)
 		g_free(data);
 
+	dbus_connection_unref(connection);
+
 	ofono_modem_set_data(modem, NULL);
 }
 
+static int hfp_connect_ofono_handsfree(struct ofono_modem *modem)
+{
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	ofono_debug("Connect to bluetooth daemon");
+
+	if (!data->handsfree_path || !connection)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Connect",
+				NULL, NULL, DBUS_TYPE_INVALID);
+}
+
 /* power up hardware */
 static int hfp_enable(struct ofono_modem *modem)
 {
-	const char *tty;
-	int ret;
+	struct hfp_data *data = ofono_modem_get_data(modem);
 
 	DBG("%p", modem);
 
-	tty = ofono_modem_get_string(modem, "Device");
-	if (tty == NULL)
+	data->at_timeout =
+		g_timeout_add_seconds(10, hfp_enable_timeout, modem);
+
+	if (hfp_connect_ofono_handsfree(modem) < 0)
 		return -EINVAL;
 
-	ret = service_level_connection(modem, tty);
+	return -EINPROGRESS;
+}
+
+static int hfp_disconnect_ofono_handsfree(struct ofono_modem *modem)
+{
+	struct hfp_data *data = ofono_modem_get_data(modem);
 
-	return ret;
+	if (!data->handsfree_path || !connection)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Disconnect",
+				NULL, NULL, DBUS_TYPE_INVALID);
 }
 
 static int hfp_disable(struct ofono_modem *modem)
 {
 	struct hfp_data *data = ofono_modem_get_data(modem);
+	GSource *source;
 
 	DBG("%p", modem);
 
@@ -359,6 +618,11 @@ static int hfp_disable(struct ofono_modem *modem)
 
 	ofono_modem_set_powered(modem, FALSE);
 
+	source = g_main_context_find_source_by_id(NULL, data->at_timeout);
+	if (source)
+		g_source_destroy(source);
+
+	hfp_disconnect_ofono_handsfree(modem);
 	return 0;
 }
 
diff --git a/plugins/modemconf.c b/plugins/modemconf.c
index c8c261f..b4c1600 100644
--- a/plugins/modemconf.c
+++ b/plugins/modemconf.c
@@ -124,12 +124,11 @@ static struct ofono_modem *create_modem(GKeyFile *keyfile, const char *group)
 
 	modem = ofono_modem_create(group, driver);
 
-	if (!g_strcmp0(driver, "phonesim"))
+	if (!g_strcmp0(driver, "phonesim") || !g_strcmp0(driver, "hfp"))
 		set_address(modem, keyfile, group);
 
 	if (!g_strcmp0(driver, "atgen") || !g_strcmp0(driver, "g1") ||
 						!g_strcmp0(driver, "calypso") ||
-						!g_strcmp0(driver, "hfp") ||
 						!g_strcmp0(driver, "palmpre"))
 		set_device(modem, keyfile, group);
 
diff --git a/src/modem.c b/src/modem.c
index bbc9905..9fab722 100644
--- a/src/modem.c
+++ b/src/modem.c
@@ -1074,6 +1074,21 @@ struct ofono_modem *ofono_modem_create(const char *name, const char *type)
 	return modem;
 }
 
+struct ofono_modem *ofono_modem_get_by_path(const char *path)
+{
+	GSList *l;
+	struct ofono_modem *modem;
+
+	for (l = g_modem_list; l; l = l->next) {
+		modem = l->data;
+
+		if (!g_strcmp0(modem->path, path))
+			return modem;
+	}
+
+	return NULL;
+}
+
 static void emit_modems()
 {
 	DBusConnection *conn = ofono_dbus_get_connection();
-- 
1.6.4.4


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

* Re: [RFC] HFP support into oFono and BlueZ
  2010-01-18 11:38       ` Luiz Augusto von Dentz
@ 2010-01-17 22:37         ` Denis Kenzior
  -1 siblings, 0 replies; 51+ messages in thread
From: Denis Kenzior @ 2010-01-17 22:37 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: ofono

Hi Luiz,

> having it as a MediaEndpoint I guess it is probably a good idea to
> have a similar design as we will have in endpoints, so the
> registration actually happen in adapter path rather than device path,
> which probably should avoid the having to resolve the device path
> while probing if we just send the device path as a parameter of
> NewConnection so we can do while in DEFER_SETUP stage so we don't risk
> missing or getting timeouts while transferring the the fd to ofono.

Personally I like this to be a per-device agent rather than per-adapter as it 
will make agent code inside oFono a bit easier to write.  Marcel / Johan, care 
to weigh in?

Regards,
-Denis

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

* Re: [RFC] HFP support into oFono and BlueZ
@ 2010-01-17 22:37         ` Denis Kenzior
  0 siblings, 0 replies; 51+ messages in thread
From: Denis Kenzior @ 2010-01-17 22:37 UTC (permalink / raw)
  To: ofono

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

Hi Luiz,

> having it as a MediaEndpoint I guess it is probably a good idea to
> have a similar design as we will have in endpoints, so the
> registration actually happen in adapter path rather than device path,
> which probably should avoid the having to resolve the device path
> while probing if we just send the device path as a parameter of
> NewConnection so we can do while in DEFER_SETUP stage so we don't risk
> missing or getting timeouts while transferring the the fd to ofono.

Personally I like this to be a per-device agent rather than per-adapter as it 
will make agent code inside oFono a bit easier to write.  Marcel / Johan, care 
to weigh in?

Regards,
-Denis

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

* Re: [RFC] HFP support into oFono and BlueZ
  2010-01-13 23:39     ` Gustavo F. Padovan
@ 2010-01-18 11:38       ` Luiz Augusto von Dentz
  -1 siblings, 0 replies; 51+ messages in thread
From: Luiz Augusto von Dentz @ 2010-01-18 11:38 UTC (permalink / raw)
  To: Gustavo F. Padovan; +Cc: linux-bluetooth, ofono, zhenhua.zhang

Hi,

On Thu, Jan 14, 2010 at 1:39 AM, Gustavo F. Padovan
<padovan@profusion.mobi> wrote:
> Hi,
>
> On Mon, Jan 11, 2010 at 5:05 PM, Gustavo F. Padovan
> <padovan@profusion.mobi> wrote:
>> On Mon, Jan 11, 2010 at 3:08 PM, Gustavo F. Padovan
>> <padovan@profusion.mobi> wrote:
>>> Hi,
>>>
>>> These patches implement the new API for the Audio Gateway in BlueZ. It
>>> follows the last version of the HandsfreeGateway and HandsfreeAgent
>>> Intefaces API.
>>> The first two patches is for BlueZ and the other for oFono. You can
>>> test it with using enable-modem and test-voicecall scripts into the
>>> test dir of oFono.
>>> Feel free to test it and send me your comments. We have some bugs yet.
>>
>> New version of the patches: now we do not handle tty stuff on the BlueZ side.
>
> New version of the patches. Known issues:
> "'org.bluez.HandsfreeAgent.Release' part not implemented" and it's not
> working with more than one bluetooth devices in some cases. Comments
> are welcome.

Since nobody really respond me in the thread about merging endpoints
with this spec I will comment in this one, although Johan disagree on
having it as a MediaEndpoint I guess it is probably a good idea to
have a similar design as we will have in endpoints, so the
registration actually happen in adapter path rather than device path,
which probably should avoid the having to resolve the device path
while probing if we just send the device path as a parameter of
NewConnection so we can do while in DEFER_SETUP stage so we don't risk
missing or getting timeouts while transferring the the fd to ofono.

>>
>>>
>>> The audio part is not working yet. We are going to work on pulseaudio
>>> this week to get this done soon.

Well I suppose we will be using the endpoint to handle this.

>>> Regards,
>>>
>>> --
>>> Gustavo F. Padovan
>>> ProFUSION embedded systems - http://profusion.mobi
>>>
>>
>>
>>
>> --
>> Gustavo F. Padovan
>> ProFUSION embedded systems - http://profusion.mobi
>>
>
>
>
> --
> Gustavo F. Padovan
> ProFUSION embedded systems - http://profusion.mobi
>



-- 
Luiz Augusto von Dentz
Engenheiro de Computação

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

* Re: [RFC] HFP support into oFono and BlueZ
@ 2010-01-18 11:38       ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 51+ messages in thread
From: Luiz Augusto von Dentz @ 2010-01-18 11:38 UTC (permalink / raw)
  To: ofono

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

Hi,

On Thu, Jan 14, 2010 at 1:39 AM, Gustavo F. Padovan
<padovan@profusion.mobi> wrote:
> Hi,
>
> On Mon, Jan 11, 2010 at 5:05 PM, Gustavo F. Padovan
> <padovan@profusion.mobi> wrote:
>> On Mon, Jan 11, 2010 at 3:08 PM, Gustavo F. Padovan
>> <padovan@profusion.mobi> wrote:
>>> Hi,
>>>
>>> These patches implement the new API for the Audio Gateway in BlueZ. It
>>> follows the last version of the HandsfreeGateway and HandsfreeAgent
>>> Intefaces API.
>>> The first two patches is for BlueZ and the other for oFono. You can
>>> test it with using enable-modem and test-voicecall scripts into the
>>> test dir of oFono.
>>> Feel free to test it and send me your comments. We have some bugs yet.
>>
>> New version of the patches: now we do not handle tty stuff on the BlueZ side.
>
> New version of the patches. Known issues:
> "'org.bluez.HandsfreeAgent.Release' part not implemented" and it's not
> working with more than one bluetooth devices in some cases. Comments
> are welcome.

Since nobody really respond me in the thread about merging endpoints
with this spec I will comment in this one, although Johan disagree on
having it as a MediaEndpoint I guess it is probably a good idea to
have a similar design as we will have in endpoints, so the
registration actually happen in adapter path rather than device path,
which probably should avoid the having to resolve the device path
while probing if we just send the device path as a parameter of
NewConnection so we can do while in DEFER_SETUP stage so we don't risk
missing or getting timeouts while transferring the the fd to ofono.

>>
>>>
>>> The audio part is not working yet. We are going to work on pulseaudio
>>> this week to get this done soon.

Well I suppose we will be using the endpoint to handle this.

>>> Regards,
>>>
>>> --
>>> Gustavo F. Padovan
>>> ProFUSION embedded systems - http://profusion.mobi
>>>
>>
>>
>>
>> --
>> Gustavo F. Padovan
>> ProFUSION embedded systems - http://profusion.mobi
>>
>
>
>
> --
> Gustavo F. Padovan
> ProFUSION embedded systems - http://profusion.mobi
>



-- 
Luiz Augusto von Dentz
Engenheiro de Computação

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

* Re: [RFC] HFP support into oFono and BlueZ
  2010-01-17 22:37         ` Denis Kenzior
@ 2010-01-19  8:02           ` Johan Hedberg
  -1 siblings, 0 replies; 51+ messages in thread
From: Johan Hedberg @ 2010-01-19  8:02 UTC (permalink / raw)
  To: Denis Kenzior; +Cc: linux-bluetooth, ofono

Hi Denis,

On Sun, Jan 17, 2010, Denis Kenzior wrote:
> > having it as a MediaEndpoint I guess it is probably a good idea to
> > have a similar design as we will have in endpoints, so the
> > registration actually happen in adapter path rather than device path,
> > which probably should avoid the having to resolve the device path
> > while probing if we just send the device path as a parameter of
> > NewConnection so we can do while in DEFER_SETUP stage so we don't risk
> > missing or getting timeouts while transferring the the fd to ofono.
> 
> Personally I like this to be a per-device agent rather than per-adapter as it 
> will make agent code inside oFono a bit easier to write.  Marcel / Johan, care 
> to weigh in?

My initial gut feeling is that this should be adapter based, though that
might be partly fueled by the fact that that's also how the current
pairing and authorization agent is designed. However, I do remember us
discussing this earlier and the outcome back then was (at least based on
your API suggestion) to have a per-device agent. I can't remember the
exact supporting arguments for this, but at least the API does get
simpler since then we don't need a new interface for both adapter and
device objects and the parameter lists of the HandsfreeAgent interface
methods become shorter since we don't need to have the remote device
object path there.

Johan
_______________________________________________
ofono mailing list
ofono@ofono.org
http://lists.ofono.org/listinfo/ofono

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

* Re: [RFC] HFP support into oFono and BlueZ
@ 2010-01-19  8:02           ` Johan Hedberg
  0 siblings, 0 replies; 51+ messages in thread
From: Johan Hedberg @ 2010-01-19  8:02 UTC (permalink / raw)
  To: ofono

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

Hi Denis,

On Sun, Jan 17, 2010, Denis Kenzior wrote:
> > having it as a MediaEndpoint I guess it is probably a good idea to
> > have a similar design as we will have in endpoints, so the
> > registration actually happen in adapter path rather than device path,
> > which probably should avoid the having to resolve the device path
> > while probing if we just send the device path as a parameter of
> > NewConnection so we can do while in DEFER_SETUP stage so we don't risk
> > missing or getting timeouts while transferring the the fd to ofono.
> 
> Personally I like this to be a per-device agent rather than per-adapter as it 
> will make agent code inside oFono a bit easier to write.  Marcel / Johan, care 
> to weigh in?

My initial gut feeling is that this should be adapter based, though that
might be partly fueled by the fact that that's also how the current
pairing and authorization agent is designed. However, I do remember us
discussing this earlier and the outcome back then was (at least based on
your API suggestion) to have a per-device agent. I can't remember the
exact supporting arguments for this, but at least the API does get
simpler since then we don't need a new interface for both adapter and
device objects and the parameter lists of the HandsfreeAgent interface
methods become shorter since we don't need to have the remote device
object path there.

Johan

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

* Re: [RFC] HFP support into oFono and BlueZ
  2010-01-17 22:37         ` Denis Kenzior
@ 2010-01-19  9:30           ` Luiz Augusto von Dentz
  -1 siblings, 0 replies; 51+ messages in thread
From: Luiz Augusto von Dentz @ 2010-01-19  9:30 UTC (permalink / raw)
  To: Denis Kenzior; +Cc: linux-bluetooth, ofono

Hi Denis,

On Mon, Jan 18, 2010 at 12:37 AM, Denis Kenzior <denkenz@gmail.com> wrote:
> Hi Luiz,
>
>> having it as a MediaEndpoint I guess it is probably a good idea to
>> have a similar design as we will have in endpoints, so the
>> registration actually happen in adapter path rather than device path,
>> which probably should avoid the having to resolve the device path
>> while probing if we just send the device path as a parameter of
>> NewConnection so we can do while in DEFER_SETUP stage so we don't risk
>> missing or getting timeouts while transferring the the fd to ofono.
>
> Personally I like this to be a per-device agent rather than per-adapter as it
> will make agent code inside oFono a bit easier to write.  Marcel / Johan, care
> to weigh in?

>From Gustavo's patch it seems that it became more difficult rather
than easier, since we only have the device address but not the path
there is always 2 extras calls to bluetoothd in order to resolve the
device path, it looks quite bad since this is being done for every
single device and no result is being cached in memory. I don't know
why this is the case, what is being used to enumerate the devices?

Also the problem with this being in the device path is that ofono may
not be registered as an agent on connect (both ways) which will cause
the connection to drop and the round trips to resolve device path only
makes it worse, if this happen to be on adapter path this would not be
a problem since we know before hand who to call.


-- 
Luiz Augusto von Dentz
Engenheiro de Computação

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

* Re: [RFC] HFP support into oFono and BlueZ
@ 2010-01-19  9:30           ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 51+ messages in thread
From: Luiz Augusto von Dentz @ 2010-01-19  9:30 UTC (permalink / raw)
  To: ofono

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

Hi Denis,

On Mon, Jan 18, 2010 at 12:37 AM, Denis Kenzior <denkenz@gmail.com> wrote:
> Hi Luiz,
>
>> having it as a MediaEndpoint I guess it is probably a good idea to
>> have a similar design as we will have in endpoints, so the
>> registration actually happen in adapter path rather than device path,
>> which probably should avoid the having to resolve the device path
>> while probing if we just send the device path as a parameter of
>> NewConnection so we can do while in DEFER_SETUP stage so we don't risk
>> missing or getting timeouts while transferring the the fd to ofono.
>
> Personally I like this to be a per-device agent rather than per-adapter as it
> will make agent code inside oFono a bit easier to write.  Marcel / Johan, care
> to weigh in?

From Gustavo's patch it seems that it became more difficult rather
than easier, since we only have the device address but not the path
there is always 2 extras calls to bluetoothd in order to resolve the
device path, it looks quite bad since this is being done for every
single device and no result is being cached in memory. I don't know
why this is the case, what is being used to enumerate the devices?

Also the problem with this being in the device path is that ofono may
not be registered as an agent on connect (both ways) which will cause
the connection to drop and the round trips to resolve device path only
makes it worse, if this happen to be on adapter path this would not be
a problem since we know before hand who to call.


-- 
Luiz Augusto von Dentz
Engenheiro de Computação

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

* Re: [RFC] HFP support into oFono and BlueZ
  2010-01-19  9:30           ` Luiz Augusto von Dentz
@ 2010-01-19 10:33             ` Johan Hedberg
  -1 siblings, 0 replies; 51+ messages in thread
From: Johan Hedberg @ 2010-01-19 10:33 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: Denis Kenzior, linux-bluetooth, ofono

Hi Luiz,

On Tue, Jan 19, 2010, Luiz Augusto von Dentz wrote:
> Also the problem with this being in the device path is that ofono may
> not be registered as an agent on connect (both ways) which will cause
> the connection to drop and the round trips to resolve device path only
> makes it worse, if this happen to be on adapter path this would not be
> a problem since we know before hand who to call.

I don't think this is an issue in the case of a per-adapter handsfree
agent. Typically the agent would be registered upon the creation of the
device object. Yes, in theory a HFP connection establishment may occur
before ofono manages to register an agent for the device but the
connection doesn't need to be dropped because of it. Since it's the
HF-role device (i.e. us) that sends the initial AT command in HFP SLC
establishment bluetoothd could just sit quietly waiting until an agent
registers itself. When an agent finally gets registered bluetoothd would
(after replying to the registration method call) immediately call the
NewConnection() method for the agent.

Johan

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

* Re: [RFC] HFP support into oFono and BlueZ
@ 2010-01-19 10:33             ` Johan Hedberg
  0 siblings, 0 replies; 51+ messages in thread
From: Johan Hedberg @ 2010-01-19 10:33 UTC (permalink / raw)
  To: ofono

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

Hi Luiz,

On Tue, Jan 19, 2010, Luiz Augusto von Dentz wrote:
> Also the problem with this being in the device path is that ofono may
> not be registered as an agent on connect (both ways) which will cause
> the connection to drop and the round trips to resolve device path only
> makes it worse, if this happen to be on adapter path this would not be
> a problem since we know before hand who to call.

I don't think this is an issue in the case of a per-adapter handsfree
agent. Typically the agent would be registered upon the creation of the
device object. Yes, in theory a HFP connection establishment may occur
before ofono manages to register an agent for the device but the
connection doesn't need to be dropped because of it. Since it's the
HF-role device (i.e. us) that sends the initial AT command in HFP SLC
establishment bluetoothd could just sit quietly waiting until an agent
registers itself. When an agent finally gets registered bluetoothd would
(after replying to the registration method call) immediately call the
NewConnection() method for the agent.

Johan

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

* Re: [RFC] HFP support into oFono and BlueZ
  2010-01-19 10:33             ` Johan Hedberg
@ 2010-01-19 12:26               ` Luiz Augusto von Dentz
  -1 siblings, 0 replies; 51+ messages in thread
From: Luiz Augusto von Dentz @ 2010-01-19 12:26 UTC (permalink / raw)
  To: Luiz Augusto von Dentz, Denis Kenzior, linux-bluetooth, ofono

Hi Johan,

On Tue, Jan 19, 2010 at 12:33 PM, Johan Hedberg <johan.hedberg@gmail.com> wrote:
> Hi Luiz,
>
> On Tue, Jan 19, 2010, Luiz Augusto von Dentz wrote:
>> Also the problem with this being in the device path is that ofono may
>> not be registered as an agent on connect (both ways) which will cause
>> the connection to drop and the round trips to resolve device path only
>> makes it worse, if this happen to be on adapter path this would not be
>> a problem since we know before hand who to call.
>
> I don't think this is an issue in the case of a per-adapter handsfree
> agent. Typically the agent would be registered upon the creation of the
> device object. Yes, in theory a HFP connection establishment may occur
> before ofono manages to register an agent for the device but the
> connection doesn't need to be dropped because of it. Since it's the
> HF-role device (i.e. us) that sends the initial AT command in HFP SLC
> establishment bluetoothd could just sit quietly waiting until an agent
> registers itself. When an agent finally gets registered bluetoothd would
> (after replying to the registration method call) immediately call the
> NewConnection() method for the agent.

Well it might work, but does it worth? Also I got the impression that
this is not optimal, specially if the we are simulating a combo device
hfp+a2dp this may delay the setup indefinitely until somebody register
an agent , so if I got it right our current code behind Audio.Connect
will not even attempt to connect the sink until headset state
transition from connecting.

Maybe and being paranoid, but I guess this might be troublesome for
mobile hardware where dbus is not that fast.


-- 
Luiz Augusto von Dentz
Engenheiro de Computação

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

* Re: [RFC] HFP support into oFono and BlueZ
@ 2010-01-19 12:26               ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 51+ messages in thread
From: Luiz Augusto von Dentz @ 2010-01-19 12:26 UTC (permalink / raw)
  To: ofono

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

Hi Johan,

On Tue, Jan 19, 2010 at 12:33 PM, Johan Hedberg <johan.hedberg@gmail.com> wrote:
> Hi Luiz,
>
> On Tue, Jan 19, 2010, Luiz Augusto von Dentz wrote:
>> Also the problem with this being in the device path is that ofono may
>> not be registered as an agent on connect (both ways) which will cause
>> the connection to drop and the round trips to resolve device path only
>> makes it worse, if this happen to be on adapter path this would not be
>> a problem since we know before hand who to call.
>
> I don't think this is an issue in the case of a per-adapter handsfree
> agent. Typically the agent would be registered upon the creation of the
> device object. Yes, in theory a HFP connection establishment may occur
> before ofono manages to register an agent for the device but the
> connection doesn't need to be dropped because of it. Since it's the
> HF-role device (i.e. us) that sends the initial AT command in HFP SLC
> establishment bluetoothd could just sit quietly waiting until an agent
> registers itself. When an agent finally gets registered bluetoothd would
> (after replying to the registration method call) immediately call the
> NewConnection() method for the agent.

Well it might work, but does it worth? Also I got the impression that
this is not optimal, specially if the we are simulating a combo device
hfp+a2dp this may delay the setup indefinitely until somebody register
an agent , so if I got it right our current code behind Audio.Connect
will not even attempt to connect the sink until headset state
transition from connecting.

Maybe and being paranoid, but I guess this might be troublesome for
mobile hardware where dbus is not that fast.


-- 
Luiz Augusto von Dentz
Engenheiro de Computação

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

* Re: [RFC] HFP support into oFono and BlueZ
  2010-01-13 23:39     ` Gustavo F. Padovan
@ 2010-01-20 19:58       ` Gustavo F. Padovan
  -1 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-20 19:58 UTC (permalink / raw)
  To: linux-bluetooth, ofono, zhenhua.zhang

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

> New version of the patches. Known issues:
> "'org.bluez.HandsfreeAgent.Release' part not implemented" and it's not
> working with more than one bluetooth devices in some cases. Comments
> are welcome.
>

Another new version of the patches. Now ofono query all bluetooth
devices and searches for the ones with Gateway interface.
Bluez has some fixes and now supports connection coming from the AG.
The patches are using a per-device agent as described by the last
version of Handsfree API.

I did not implemented yet 'org.bluez.HandsfreeAgent.Release'.

Comments are welcome. :)

-- 
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi

[-- Attachment #2: 0001-clean-up-audio-gateway.c.patch --]
[-- Type: application/octet-stream, Size: 24620 bytes --]

From 1dee86673257625823137717b06d0757d5715781 Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 6 Nov 2009 18:08:58 -0200
Subject: [PATCH 1/2] clean up audio/gateway.c

remove all code related to the AT engine
---
 audio/gateway.c |  760 +------------------------------------------------------
 1 files changed, 1 insertions(+), 759 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index a1c1ea2..3dc09ff 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -53,52 +53,6 @@
 #include "dbus-common.h"
 
 #define RFCOMM_BUF_SIZE 256
-/* not-more-then-16 defined by GSM + 1 for NULL + padding */
-#define AG_INDICATOR_DESCR_SIZE 20
-#define AG_CALLER_NUM_SIZE 64	/* size of number + type */
-
-/* commands */
-#define AG_FEATURES "AT+BRSF=26\r"     /* = 0x7F = All features supported */
-#define AG_INDICATORS_SUPP "AT+CIND=?\r"
-#define AG_INDICATORS_VAL "AT+CIND?\r"
-#define AG_INDICATORS_ENABLE "AT+CMER=3,0,0,1\r"
-#define AG_HOLD_MPTY_SUPP "AT+CHLD=?\r"
-#define AG_CALLER_IDENT_ENABLE "AT+CLIP=1\r"
-#define AG_CARRIER_FORMAT "AT+COPS=3,0\r"
-#define AG_EXTENDED_RESULT_CODE "AT+CMEE=1\r"
-
-#define AG_FEATURE_3WAY 0x1
-#define AG_FEATURE_EXTENDED_RES_CODE 0x100
-/* Hold and multipary AG features.
- * Comments below are copied from hands-free spec for reference */
-/* Releases all held calls or sets User Determined User Busy (UDUB)
- * for a waiting call */
-#define AG_CHLD_0 0x01
-/* Releases all active calls (if any exist) and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_1 0x02
-/* Releases specified active call only <x> */
-#define AG_CHLD_1x 0x04
-/* Places all active calls (if any exist) on hold and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_2 0x08
-/* Request private consultation mode with specified call <x> (Place all
- * calls on hold EXCEPT the call <x>) */
-#define AG_CHLD_2x 0x10
-/* Adds a held call to the conversation */
-#define AG_CHLD_3 0x20
-/* Connects the two calls and disconnects the subscriber from both calls
- * (Explicit Call Transfer). Support for this value and its associated
- * functionality is optional for the HF. */
-#define AG_CHLD_4 0x40
-
-#define OK_RESPONSE "\r\nOK\r\n"
-#define ERROR_RESPONSE "\r\nERROR\r\n"
-
-struct indicator {
-	gchar descr[AG_INDICATOR_DESCR_SIZE];
-	gint value;
-};
 
 struct gateway {
 	gateway_state_t state;
@@ -108,387 +62,10 @@ struct gateway {
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
 	DBusMessage *connect_message;
-	guint ag_features;
-	guint hold_multiparty_features;
-	GSList *indies;
-	gboolean is_dialing;
-	gboolean call_active;
-
-	int sp_gain;
-	int mic_gain;
 };
 
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device);
-
 int gateway_close(struct audio_device *device);
 
-static void rfcomm_start_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	gw->rfcomm_watch_id = g_io_add_watch(gw->rfcomm,
-			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-			(GIOFunc) rfcomm_ag_data_cb, dev);
-}
-
-static void rfcomm_stop_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	g_source_remove(gw->rfcomm_watch_id);
-}
-
-static gboolean io_channel_write_all(GIOChannel *io, gchar *data,
-					gsize count)
-{
-	gsize written = 0;
-	GIOStatus status;
-
-	while (count > 0) {
-		status = g_io_channel_write_chars(io, data, count, &written,
-						NULL);
-		if (status != G_IO_STATUS_NORMAL)
-			return FALSE;
-
-		data += written;
-		count -= written;
-	}
-	return TRUE;
-}
-
-/* it's worth to mention that data and response could be the same pointers */
-static gboolean rfcomm_send_and_read(struct gateway *gw, gchar *data,
-                                    gchar *response, gsize count)
-{
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read = 0;
-	gboolean got_ok = FALSE;
-	gboolean got_error = FALSE;
-	gchar *resp_buf = response;
-	gsize toread = RFCOMM_BUF_SIZE - 1;
-	GIOStatus status;
-
-	if (!io_channel_write_all(rfcomm, data, count))
-		return FALSE;
-
-	while (!(got_ok || got_error)) {
-		status = g_io_channel_read_chars(rfcomm, resp_buf, toread,
-						&read, NULL);
-		if (status == G_IO_STATUS_NORMAL)
-			resp_buf[read] = '\0';
-		else {
-			debug("rfcomm_send_and_read(): %m");
-			return FALSE;
-		}
-		got_ok = NULL != strstr(resp_buf, OK_RESPONSE);
-		got_error = NULL != strstr(resp_buf, ERROR_RESPONSE);
-		resp_buf += read;
-		toread -= read;
-	}
-	return TRUE;
-}
-
-/* get <descr> from the names: (<descr>, (<values>)), (<descr>, (<values>))
- * ... */
-static GSList *parse_indicator_names(gchar *names, GSList *indies)
-{
-	gchar *current = names - 1;
-	GSList *result = indies;
-	gchar *next;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 2;
-		next = strstr(current, ",(");
-		ind = g_slice_new(struct indicator);
-		strncpy(ind->descr, current, 20);
-		ind->descr[(intptr_t) next - (intptr_t) current] = '\0';
-		result = g_slist_append(result, (gpointer) ind);
-		current = strstr(next + 1, ",(");
-	}
-	return result;
-}
-
-/* get values from <val0>,<val1>,... */
-static GSList *parse_indicator_values(gchar *values, GSList *indies)
-{
-	gint val;
-	gchar *current = values - 1;
-	GSList *runner = indies;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 1;
-		sscanf(current, "%d", &val);
-		current = strchr(current, ',');
-		ind = g_slist_nth_data(runner, 0);
-		ind->value = val;
-		runner = g_slist_next(runner);
-	}
-	return indies;
-}
-
-/* get values from <val0>,<val1>,... */
-static guint get_hold_mpty_features(gchar *features)
-{
-	guint result = 0;
-
-	if (strstr(features, "0"))
-		result |= AG_CHLD_0;
-
-	if (strstr(features, "1"))
-		result |= AG_CHLD_1;
-
-	if (strstr(features, "1x"))
-		result |= AG_CHLD_1x;
-
-	if (strstr(features, "2"))
-		result |= AG_CHLD_2;
-
-	if (strstr(features, "2x"))
-		result |= AG_CHLD_2x;
-
-	if (strstr(features, "3"))
-		result |= AG_CHLD_3;
-
-	if (strstr(features, "4"))
-		result |= AG_CHLD_4;
-
-	return result;
-}
-
-static gboolean establish_service_level_conn(struct gateway *gw)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	gboolean res;
-
-	debug("at the begin of establish_service_level_conn()");
-	res = rfcomm_send_and_read(gw, AG_FEATURES, buf,
-				sizeof(AG_FEATURES) - 1);
-	if (!res || sscanf(buf, "\r\n+BRSF:%d", &gw->ag_features) != 1)
-		return FALSE;
-
-	debug("features are 0x%X", gw->ag_features);
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_SUPP, buf,
-				sizeof(AG_INDICATORS_SUPP) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_names(strchr(buf, '('), NULL);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_VAL, buf,
-		sizeof(AG_INDICATORS_VAL) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_values(strchr(buf, ':') + 1, gw->indies);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_ENABLE, buf,
-				sizeof(AG_INDICATORS_ENABLE) - 1);
-	if (!res || !strstr(buf, "OK"))
-		return FALSE;
-
-	if ((gw->ag_features & AG_FEATURE_3WAY) != 0) {
-		res = rfcomm_send_and_read(gw, AG_HOLD_MPTY_SUPP, buf,
-				sizeof(AG_HOLD_MPTY_SUPP) - 1);
-		if (!res || !strstr(buf, "+CHLD:")) {
-			g_slice_free1(RFCOMM_BUF_SIZE, buf);
-			return FALSE;
-		}
-		gw->hold_multiparty_features = get_hold_mpty_features(
-							strchr(buf, '('));
-
-	} else
-		gw->hold_multiparty_features = 0;
-
-	debug("Service layer connection successfully established!");
-	rfcomm_send_and_read(gw, AG_CALLER_IDENT_ENABLE, buf,
-			sizeof(AG_CALLER_IDENT_ENABLE) - 1);
-	rfcomm_send_and_read(gw, AG_CARRIER_FORMAT, buf,
-			sizeof(AG_CARRIER_FORMAT) - 1);
-	if ((gw->ag_features & AG_FEATURE_EXTENDED_RES_CODE) != 0)
-		rfcomm_send_and_read(gw, AG_EXTENDED_RESULT_CODE, buf,
-			sizeof(AG_EXTENDED_RESULT_CODE) - 1);
-
-	return TRUE;
-}
-
-static void process_ind_change(struct audio_device *dev, guint index,
-							gint value)
-{
-	struct gateway *gw = dev->gateway;
-	struct indicator *ind = g_slist_nth_data(gw->indies, index - 1);
-	gchar *name = ind->descr;
-
-	ind->value = value;
-
-	debug("at the begin of process_ind_change, name is %s", name);
-	if (!strcmp(name, "\"call\"")) {
-		if (value > 0) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallStarted", DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-			gw->call_active = TRUE;
-		} else {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallEnded", DBUS_TYPE_INVALID);
-			gw->call_active = FALSE;
-		}
-
-	} else if (!strcmp(name, "\"callsetup\"")) {
-		if (value == 0 && gw->is_dialing) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-		} else if (!gw->is_dialing && value > 0)
-			gw->is_dialing = TRUE;
-
-	} else if (!strcmp(name, "\"callheld\"")) {
-		/* FIXME: The following code is based on assumptions only.
-		 * Has to be tested for interoperability
-		 * I assume that callheld=2 would be sent when dial from HF
-		 * failed in case of 3-way call
-		 * Unfortunately this path is not covered by the HF spec so
-		 * the code has to be tested for interop
-		*/
-		/* '2' means: all calls held, no active calls */
-		if (value == 2) {
-			if (gw->is_dialing) {
-				g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-				gw->is_dialing = FALSE;
-			}
-		}
-	} else if (!strcmp(name, "\"service\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RegistrationStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"signal\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "SignalStrength",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"roam\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RoamingStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"battchg\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "BatteryCharge",
-				DBUS_TYPE_UINT16, &value);
-}
-
-static void process_ring(struct audio_device *device, GIOChannel *chan,
-			gchar *buf)
-{
-	gchar number[AG_CALLER_NUM_SIZE];
-	gchar *cli;
-	gchar *sep;
-	gsize read;
-	GIOStatus status;
-
-	rfcomm_stop_watch(device);
-	status = g_io_channel_read_chars(chan, buf, RFCOMM_BUF_SIZE - 1, &read, NULL);
-	if (status != G_IO_STATUS_NORMAL)
-		return;
-
-	debug("at the begin of process_ring");
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 10)
-		error("process_ring(): buf is too long '%s'", buf);
-	else if ((cli = strstr(buf, "\r\n+CLIP"))) {
-		if (sscanf(cli, "\r\n+CLIP: \"%s", number) == 1) {
-			sep = strchr(number, '"');
-			sep[0] = '\0';
-
-			/* FIXME:signal will be emitted on each RING+CLIP.
-			 * That's bad */
-			cli = number;
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE, "Ring",
-					DBUS_TYPE_STRING, &cli,
-					DBUS_TYPE_INVALID);
-			device->gateway->is_dialing = TRUE;
-		} else
-			error("process_ring(): '%s' in place of +CLIP after RING", buf);
-
-	}
-
-	rfcomm_start_watch(device);
-}
-
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	struct gateway *gw;
-	gsize read;
-	/* some space for value */
-	gchar indicator[AG_INDICATOR_DESCR_SIZE + 4];
-	gint value;
-	guint index;
-	gchar *sep;
-
-	debug("at the begin of rfcomm_ag_data_cb()");
-	if (cond & G_IO_NVAL)
-		return FALSE;
-
-	gw = device->gateway;
-
-	if (cond & (G_IO_ERR | G_IO_HUP)) {
-		debug("connection with remote BT is closed");
-		gateway_close(device);
-		return FALSE;
-	}
-
-	if (g_io_channel_read_chars(chan, buf, sizeof(buf) - 1, &read, NULL)
-			!= G_IO_STATUS_NORMAL)
-		return TRUE;
-	buf[read] = '\0';
-
-	if (strlen(buf) > AG_INDICATOR_DESCR_SIZE + 14)
-		error("rfcomm_ag_data_cb(): buf is too long '%s'", buf);
-	else if (sscanf(buf, "\r\n+CIEV:%s\r\n", indicator) == 1) {
-		sep = strchr(indicator, ',');
-		sep[0] = '\0';
-		sep += 1;
-		index = atoi(indicator);
-		value = atoi(sep);
-		process_ind_change(device, index, value);
-	} else if (strstr(buf, "RING"))
-		process_ring(device, chan, buf);
-	else if (sscanf(buf, "\r\n+BVRA:%d\r\n", &value) == 1) {
-		if (value == 0)
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionActive",
-					DBUS_TYPE_INVALID);
-		else
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionInactive",
-					DBUS_TYPE_INVALID);
-	} else if (sscanf(buf, "\r\n+VGS:%d\r\n", &value) == 1) {
-		gw->sp_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "SpeakerGain",
-				DBUS_TYPE_UINT16, &value);
-	} else if (sscanf(buf, "\r\n+VGM:%d\r\n", &value) == 1) {
-		gw->mic_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "MicrophoneGain",
-				DBUS_TYPE_UINT16, &value);
-	} else
-		error("rfcomm_ag_data_cb(): read wrong data '%s'", buf);
-
-	return TRUE;
-}
-
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
 {
@@ -541,7 +118,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	DBusMessage *conn_mes = gw->connect_message;
 	gchar gw_addr[18];
 	GIOFlags flags;
 
@@ -563,29 +139,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 	if (!gw->rfcomm)
 		gw->rfcomm = g_io_channel_ref(chan);
 
-	if (establish_service_level_conn(dev->gateway)) {
-		gboolean value = TRUE;
-
-		debug("%s: Connected to %s", dev->path, gw_addr);
-		rfcomm_start_watch(dev);
-		if (conn_mes) {
-			DBusMessage *reply =
-				dbus_message_new_method_return(conn_mes);
-			dbus_connection_send(dev->conn, reply, NULL);
-			dbus_message_unref(reply);
-			dbus_message_unref(conn_mes);
-			gw->connect_message = NULL;
-		}
-
-		gw->state = GATEWAY_STATE_CONNECTED;
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN,	&value);
-		return;
-	} else
-		error("%s: Failed to establish service layer connection to %s",
-			dev->path, gw_addr);
-
 	if (NULL != gw->sco_start_cb)
 		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 
@@ -732,321 +285,20 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
-static DBusMessage *process_ag_reponse(DBusMessage *msg, gchar *response)
-{
-	DBusMessage *reply;
-
-
-	debug("in process_ag_reponse, response is %s", response);
-	if (strstr(response, OK_RESPONSE))
-		reply = dbus_message_new_method_return(msg);
-	else {
-		/* FIXME: some code should be here to processes errors
-		 *  in better fasion */
-		debug("AG responded with '%s' to %s method call", response,
-				dbus_message_get_member(msg));
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".OperationFailed",
-					"Operation failed.See log for details");
-	}
-	return reply;
-}
-
-static DBusMessage *process_simple(DBusMessage *msg, struct audio_device *dev,
-					gchar *data)
-{
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-
-	rfcomm_stop_watch(dev);
-	rfcomm_send_and_read(gw, data, buf, strlen(data));
-	rfcomm_start_watch(dev);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_ANSWER "ATA\r"
-
-static DBusMessage *ag_answer(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	if (gw->call_active)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".CallAlreadyAnswered",
-					"Call AlreadyAnswered");
-
-	return process_simple(msg, dev, AG_ANSWER);
-}
-
-#define AG_HANGUP "AT+CHUP\r"
-
-static DBusMessage *ag_terminate_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	return process_simple(msg, dev, AG_HANGUP);
-}
-
-/* according to GSM spec */
-#define ALLOWED_NUMBER_SYMBOLS "1234567890*#ABCD"
-#define AG_PLACE_CALL "ATD%s;\r"
-/* dialing from memory is not supported as headset spec doesn't define a way
- * to retreive phone memory entries.
- */
-static DBusMessage *ag_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint atd_len;
-	DBusMessage *result;
-
-	debug("at the begin of ag_call()");
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	atd_len = sprintf(buf, AG_PLACE_CALL, number);
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, buf, buf, atd_len);
-	rfcomm_start_watch(device);
-
-	result = process_ag_reponse(msg, buf);
-	return result;
-}
-
-#define AG_GET_CARRIER "AT+COPS?\r"
-
-static DBusMessage *ag_get_operator(DBusConnection *conn, DBusMessage *msg,
-					void *data)
-{
-	struct audio_device *dev = (struct audio_device *) data;
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read;
-	gchar *result, *sep;
-	DBusMessage *reply;
-	GIOStatus status;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(dev);
-	io_channel_write_all(rfcomm, AG_GET_CARRIER, strlen(AG_GET_CARRIER));
-
-	status = g_io_channel_read_chars(rfcomm, buf, RFCOMM_BUF_SIZE - 1,
-						&read, NULL);
-	rfcomm_start_watch(dev);
-	if (G_IO_STATUS_NORMAL == status) {
-		buf[read] = '\0';
-		if (strstr(buf, "+COPS")) {
-			if (!strrchr(buf, ','))
-				result = "0";
-			else {
-				result = strchr(buf, '\"') + 1;
-				sep = strchr(result, '\"');
-				sep[0] = '\0';
-			}
-
-			reply = dbus_message_new_method_return(msg);
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&result, DBUS_TYPE_INVALID);
-		} else {
-			info("ag_get_operator(): '+COPS' expected but"
-				" '%s' received", buf);
-			reply = dbus_message_new_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Unexpected response from AG");
-		}
-	} else {
-		error("ag_get_operator(): %m");
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".ConnectionFailed",
-					"Failed to receive response from AG");
-	}
-
-	return reply;
-}
-
-#define AG_SEND_DTMF "AT+VTS=%c\r"
-static DBusMessage *ag_send_dtmf(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint com_len;
-	gboolean got_ok = TRUE;
-	gint num_len;
-	gint i = 0;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	num_len = strlen(number);
-	rfcomm_stop_watch(device);
-	while (i < num_len && got_ok) {
-		com_len = sprintf(buf, AG_SEND_DTMF, number[i]);
-		rfcomm_send_and_read(gw, buf, buf, com_len);
-		got_ok = NULL != strstr(buf, OK_RESPONSE);
-		i += 1;
-	}
-	rfcomm_start_watch(device);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_GET_SUBSCRIBER_NUMS "AT+CNUM\r"
-#define CNUM_LEN 5             /* length of "+CNUM" string */
-#define MAX_NUMBER_CNT 16
-static DBusMessage *ag_get_subscriber_num(DBusConnection *conn,
-					DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number, *end;
-	DBusMessage *reply = dbus_message_new_method_return(msg);
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, AG_GET_SUBSCRIBER_NUMS, buf,
-			strlen(AG_GET_SUBSCRIBER_NUMS));
-	rfcomm_start_watch(device);
-
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 21)
-		error("ag_get_subscriber_num(): buf is too long '%s'", buf);
-	else if (strstr(buf, "+CNUM")) {
-		number = strchr(buf, ',');
-		number++;
-		end = strchr(number, ',');
-		if (end) {
-			*end = '\0';
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&number, DBUS_TYPE_INVALID);
-		}
-	} else
-		error("ag_get_subscriber_num(): read wrong data '%s'", buf);
-
-	return reply;
-}
-
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	DBusMessage *reply;
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	gboolean value;
-	guint index = 0;
-	struct indicator *ind;
-
-	reply = dbus_message_new_method_return(msg);
-	if (!reply)
-		return NULL;
-
-	dbus_message_iter_init_append(reply, &iter);
-
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
-	/* Connected */
-	value = gateway_is_connected(device);
-	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
-
-	if (!value)
-		goto done;
-
-	while ((ind = g_slist_nth_data(gw->indies, index))) {
-		if(!strcmp(ind->descr, "\"service\""))
-			dict_append_entry(&dict, "RegistrationStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"signal\""))
-			dict_append_entry(&dict, "SignalStrength",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"roam\""))
-			dict_append_entry(&dict, "RoamingStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"battchg\""))
-			dict_append_entry(&dict, "BatteryCharge",
-					DBUS_TYPE_UINT16, &ind->value);
-		index++;
-	}
-
-	/* SpeakerGain */
-	dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16,
-				&device->gateway->sp_gain);
-
-	/* MicrophoneGain */
-	dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16,
-				&device->gateway->mic_gain);
-done:
-	dbus_message_iter_close_container(&iter, &dict);
-	return reply;
+	return NULL;
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "Disconnect", "", "", ag_disconnect },
-	{ "AnswerCall", "", "", ag_answer },
-	{ "TerminateCall", "", "", ag_terminate_call },
-	{ "Call", "s", "", ag_call },
-	{ "GetOperatorName", "", "s", ag_get_operator },
-	{ "SendDTMF", "s", "", ag_send_dtmf },
-	{ "GetSubscriberNumber", "", "s", ag_get_subscriber_num },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
 	{ NULL, NULL, NULL, NULL }
 };
 
 static GDBusSignalTable gateway_signals[] = {
-	{ "Ring", "s" },
-	{ "CallTerminated", "" },
-	{ "CallStarted", "" },
-	{ "CallEnded", "" },
 	{ "PropertyChanged", "sv" },
 	{ NULL, NULL }
 };
@@ -1063,9 +315,6 @@ struct gateway *gateway_init(struct audio_device *dev)
 
 	debug("in gateway_init, dev is %p", dev);
 	gw = g_new0(struct gateway, 1);
-	gw->indies = NULL;
-	gw->is_dialing = FALSE;
-	gw->call_active = FALSE;
 	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
 
@@ -1107,11 +356,6 @@ void gateway_start_service(struct audio_device *device)
 	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
 }
 
-static void indicator_slice_free(gpointer mem)
-{
-	g_slice_free(struct indicator, mem);
-}
-
 int gateway_close(struct audio_device *device)
 {
 	struct gateway *gw = device->gateway;
@@ -1119,8 +363,6 @@ int gateway_close(struct audio_device *device)
 	GIOChannel *sco = gw->sco;
 	gboolean value = FALSE;
 
-	g_slist_foreach(gw->indies, (GFunc) indicator_slice_free, NULL);
-	g_slist_free(gw->indies);
 	if (rfcomm) {
 		g_io_channel_shutdown(rfcomm, TRUE, NULL);
 		g_io_channel_unref(rfcomm);
-- 
1.6.4.4


[-- Attachment #3: 0002-Implement-HandsfreeGateway-Interface.patch --]
[-- Type: application/octet-stream, Size: 24074 bytes --]

From 0755bce0e732a73a7d1d68772aa2090603003784 Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 8 Jan 2010 17:38:21 -0200
Subject: [PATCH 2/2] Implement HandsfreeGateway Interface

Create a interface where a Handsfree agent can register and use BlueZ to
handle the rfcomm and sco links.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for his
prototype on this code.
---
 audio/gateway.c |  442 +++++++++++++++++++++++++++++++++++++++++++------------
 audio/gateway.h |   12 +-
 audio/manager.c |   20 +--
 audio/unix.c    |    8 +
 doc/hfp-api.txt |   81 ++++++++++
 5 files changed, 453 insertions(+), 110 deletions(-)
 create mode 100644 doc/hfp-api.txt

diff --git a/audio/gateway.c b/audio/gateway.c
index 3dc09ff..d927a6c 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -5,6 +5,8 @@
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
  *  Copyright (C) 2008-2009  Leonid Movshovich <event.riga@gmail.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
+ *  Copyright (C) 2010  Gustavo F. Padovan <padovan@profusion.mobi>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -32,12 +34,15 @@
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
 
 #include <glib.h>
 #include <dbus/dbus.h>
 #include <gdbus.h>
 
 #include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
 #include <bluetooth/sco.h>
@@ -52,19 +57,100 @@
 #include "btio.h"
 #include "dbus-common.h"
 
-#define RFCOMM_BUF_SIZE 256
+#define MAX_OPEN_TRIES 5
+#define OPEN_WAIT 300
+
+struct agent {
+	char *name;	/* Bus id */
+	char *path;	/* D-Bus path */
+	guint watch;	/* Disconnect watch */
+};
 
 struct gateway {
 	gateway_state_t state;
-	GIOChannel *rfcomm;
-	guint rfcomm_watch_id;
+	int channel;
+	GIOChannel *rfcomm;	/* remote AG requested rfcomm connection */
 	GIOChannel *sco;
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
-	DBusMessage *connect_message;
+	struct agent *agent;
+	DBusMessage *msg;
 };
 
-int gateway_close(struct audio_device *device);
+int gateway_close(gpointer data);
+
+static const char *connected2str(int i)
+{
+	switch (i) {
+		case GATEWAY_STATE_DISCONNECTED:
+			return "disconnected";
+		case GATEWAY_STATE_CONNECTING:
+			return "connecting";
+		case GATEWAY_STATE_CONNECTED:
+			return "connected";
+		case GATEWAY_STATE_PLAYING:
+			return "playing";
+		default:
+			return "";
+	}
+}
+
+static void agent_free(struct agent *agent)
+{
+	if (!agent)
+		return;
+
+	g_free(agent->name);
+	g_free(agent->path);
+	g_free(agent);
+}
+
+static void change_state(struct audio_device *dev, int new_state)
+{
+	struct gateway *gw = dev->gateway;
+	const char *val = connected2str(new_state);
+
+	gw->state = new_state;
+
+	emit_property_changed(dev->conn, dev->path,
+			AUDIO_GATEWAY_INTERFACE, "State",
+			DBUS_TYPE_STRING, &val);
+}
+
+static void agent_disconnect(struct audio_device *dev, struct agent *agent)
+{
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "Release");
+
+	dbus_message_set_no_reply(msg, TRUE);
+	g_dbus_send_message(dev->conn, msg);
+
+	g_dbus_remove_watch(dev->conn, agent->watch);
+}
+
+static gboolean agent_sendfd(struct agent *agent, int fd,
+		DBusPendingCallNotifyFunction notify, void *data)
+{
+	struct audio_device *dev = data;
+	DBusMessage *msg;
+	DBusPendingCall *call;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "NewConnection");
+
+	dbus_message_append_args(msg, DBUS_TYPE_UNIX_FD, &fd,
+					DBUS_TYPE_INVALID);
+
+	if (dbus_connection_send_with_reply(dev->conn, msg, &call, -1) == FALSE)
+		return FALSE;
+
+	dbus_pending_call_set_notify(call, notify, dev->gateway, NULL);
+	dbus_pending_call_unref(call);
+
+	return TRUE;
+}
 
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
@@ -79,6 +165,7 @@ static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 		g_io_channel_shutdown(gw->sco, TRUE, NULL);
 		g_io_channel_unref(gw->sco);
 		gw->sco = NULL;
+		change_state(dev, GATEWAY_STATE_CONNECTED);
 		return FALSE;
 	}
 
@@ -92,63 +179,105 @@ static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
 
 	debug("at the begin of sco_connect_cb() in gateway.c");
 
+	gw->sco = chan;
+	g_io_channel_ref(chan);
+
 	if (err) {
 		error("sco_connect_cb(): %s", err->message);
-		/* not sure, but from other point of view,
-		 * what is the reason to have headset which
-		 * cannot play audio? */
-		if (gw->sco_start_cb)
-			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 		gateway_close(dev);
 		return;
 	}
 
-	gw->sco = g_io_channel_ref(chan);
 	if (gw->sco_start_cb)
 		gw->sco_start_cb(dev, gw->sco_start_cb_data);
 
-	/* why is this here? */
 	fcntl(g_io_channel_unix_get_fd(chan), F_SETFL, 0);
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
 				(GIOFunc) sco_io_cb, dev);
 }
 
+static void newconnection_reply(DBusPendingCall *call, void *data)
+{
+	struct gateway *gw = data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError derr;
+
+	if (!gw->rfcomm) {
+		debug("RFCOMM disconnected from server before agent reply");
+		return;
+	}
+
+	dbus_error_init(&derr);
+	if (!dbus_set_error_from_message(&derr, reply)) {
+		info("Agent reply: file descriptor passed successfuly");
+		return;
+	}
+
+	debug("Agent reply: %s", derr.message);
+
+	dbus_error_free(&derr);
+	g_idle_add(gateway_close, gw);
+}
+
 static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 				gpointer user_data)
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	gchar gw_addr[18];
-	GIOFlags flags;
+	struct rfcomm_dev_req req;
+	DBusMessage *reply;
+	int sk;
+	char src[18], dst[18];
 
 	if (err) {
 		error("connect(): %s", err->message);
 		if (gw->sco_start_cb)
 			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
-		return;
+		goto fail;
 	}
 
-	ba2str(&dev->dst, gw_addr);
-	/* Blocking mode should be default, but just in case: */
-	flags = g_io_channel_get_flags(chan);
-	flags &= ~G_IO_FLAG_NONBLOCK;
-	flags &= G_IO_FLAG_MASK;
-	g_io_channel_set_flags(chan, flags, NULL);
-	g_io_channel_set_encoding(chan, NULL, NULL);
-	g_io_channel_set_buffered(chan, FALSE);
-	if (!gw->rfcomm)
-		gw->rfcomm = g_io_channel_ref(chan);
+	if (!gw->agent)
+		error("Handfree Agent not registered");
 
-	if (NULL != gw->sco_start_cb)
-		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
+	ba2str(&dev->src, src);
+	ba2str(&dev->dst, dst);
 
-	gateway_close(dev);
+	/* make /dev/rfcomm serial port from chan */
+	memset(&req, 0, sizeof(req));
+	req.dev_id = -1;
+	req.flags = (1 << RFCOMM_REUSE_DLC);
+	bacpy(&req.src, &dev->src);
+	bacpy(&req.dst, &dev->dst);
+	req.channel = gw->channel;
+
+	sk = g_io_channel_unix_get_fd(chan);
+
+	gw->rfcomm = chan;
+	g_io_channel_ref(chan);
+
+	change_state(dev, GATEWAY_STATE_CONNECTED);
+
+	if (!agent_sendfd(gw->agent, sk, newconnection_reply, dev)) {
+		reply = g_dbus_create_error(gw->msg, ERROR_INTERFACE ".Failed",
+				"Can not pass file descriptor");
+	} else
+		reply = dbus_message_new_method_return(gw->msg);
+
+	return;
+
+fail:
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".Failed", ".Connection attempt failed");
+	}
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
+	return;
 }
 
 static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 {
 	struct audio_device *dev = user_data;
-	DBusMessage *msg = dev->gateway->connect_message;
+	struct gateway *gw = dev->gateway;
 	int ch = -1;
 	sdp_list_t *protos, *classes;
 	uuid_t uuid;
@@ -182,8 +311,6 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 
 	if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
 			uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
-		sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free,
-									NULL);
 		sdp_list_free(protos, NULL);
 		error("Invalid service record or not HFP");
 		goto fail;
@@ -197,6 +324,8 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 		goto fail;
 	}
 
+	gw->channel = ch;
+
 	io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -204,25 +333,24 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 				BT_IO_OPT_INVALID);
 	if (!io) {
 		error("Unable to connect: %s", err->message);
-		if (msg) {
-			error_common_reply(dev->conn, msg, ERROR_INTERFACE
-						".ConnectionAttemptFailed",
-						err->message);
-			msg = NULL;
-		}
-		g_error_free(err);
+		if (err)
+			g_error_free(err);
 		gateway_close(dev);
+		goto fail;
 	}
 
 	g_io_channel_unref(io);
+
+	change_state(dev, GATEWAY_STATE_CONNECTING);
 	return;
 
 fail:
-	if (msg)
-		error_common_reply(dev->conn, msg, ERROR_INTERFACE
-					".NotSupported", "Not supported");
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".NotSupported", "Not supported");
+	}
 
-	dev->gateway->connect_message = NULL;
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
 
 	sco_cb = dev->gateway->sco_start_cb;
 	if (sco_cb)
@@ -243,22 +371,56 @@ static DBusMessage *ag_connect(DBusConnection *conn, DBusMessage *msg,
 {
 	struct audio_device *au_dev = (struct audio_device *) data;
 	struct gateway *gw = au_dev->gateway;
+	DBusMessage *reply;
 
 	debug("at the begin of ag_connect()");
-	if (gw->rfcomm)
+
+	if (!gw->agent)
 		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".AlreadyConnected",
-					"Already Connected");
+				".Failed", "Agent not assined");
 
-	gw->connect_message = dbus_message_ref(msg);
 	if (get_records(au_dev) < 0) {
-		dbus_message_unref(gw->connect_message);
 		return g_dbus_create_error(msg, ERROR_INTERFACE
 					".ConnectAttemptFailed",
 					"Connect Attempt Failed");
 	}
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
 	debug("at the end of ag_connect()");
-	return NULL;
+
+	return reply;
+}
+
+int gateway_close(gpointer data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	gboolean value = FALSE;
+
+	if (gw->rfcomm) {
+		g_io_channel_shutdown(gw->rfcomm, TRUE, NULL);
+		g_io_channel_unref(gw->rfcomm);
+		gw->rfcomm = NULL;
+	}
+
+	if (gw->sco) {
+		g_io_channel_shutdown(gw->sco, TRUE, NULL);
+		g_io_channel_unref(gw->sco);
+		gw->sco = NULL;
+		gw->sco_start_cb = NULL;
+		gw->sco_start_cb_data = NULL;
+	}
+
+	gw->state = GATEWAY_STATE_DISCONNECTED;
+
+	emit_property_changed(device->conn, device->path,
+				AUDIO_GATEWAY_INTERFACE,
+				"Connected", DBUS_TYPE_BOOLEAN, &value);
+
+	return 0;
 }
 
 static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
@@ -269,6 +431,9 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	DBusMessage *reply = NULL;
 	char gw_addr[18];
 
+	if (!device->conn)
+		return NULL;
+
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
 		return NULL;
@@ -279,22 +444,128 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 						"Device not Connected");
 
 	gateway_close(device);
+
 	ba2str(&device->dst, gw_addr);
 	debug("Disconnected from %s, %s", gw_addr, device->path);
 
 	return reply;
 }
 
+static void agent_exited(DBusConnection *conn, void *data)
+{
+	struct gateway *gateway = data;
+	struct agent *agent = gateway->agent;
+
+	debug("Agent %s exited", agent->name);
+
+	agent_free(agent);
+	gateway->agent = NULL;
+}
+
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	return NULL;
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	const char *value;
+
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	value = connected2str(gw->state);
+	dict_append_entry(&dict, "Connected",
+			DBUS_TYPE_STRING, &value);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct agent *agent;
+	const char *path, *name;
+
+
+	if (gw->agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".AlreadyExists",
+				"Agent already exists");
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+			DBUS_TYPE_INVALID))
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+				".InvalidArguments", "Invalid argument");
+
+	name = dbus_message_get_sender(msg);
+	agent = g_new0(struct agent, 1);
+	if (!agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed",
+				"Failed to create a new agent");
+
+	agent->name = strdup(name);
+	agent->path = strdup(path);
+
+	agent->watch = g_dbus_add_disconnect_watch(conn, name,
+			agent_exited, gw, NULL);
+
+	gw->agent = agent;
+
+	gw->msg = dbus_message_ref(msg);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+				DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct agent *agent;
+
+	if (!gw->agent)
+		goto done;
+
+	agent = gw->agent;
+	if (strcmp(agent->name, dbus_message_get_sender(msg)) != 0)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed", "Permission denied");
+
+	agent_disconnect(device, gw->agent);
+
+	g_dbus_remove_watch(conn, agent->watch);
+
+	dbus_message_unref(gw->msg);
+
+	agent_free(agent);
+	gw->agent = NULL;
+
+done:
+	return dbus_message_new_method_return(msg);
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
-	{ "Disconnect", "", "", ag_disconnect },
+	{ "Disconnect", "", "", ag_disconnect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
+	{ "RegisterAgent", "o", "", register_agent },
+	{ "UnregisterAgent", "o", "", unregister_agent },
 	{ NULL, NULL, NULL, NULL }
 };
 
@@ -307,17 +578,15 @@ struct gateway *gateway_init(struct audio_device *dev)
 {
 	struct gateway *gw;
 
+	gw = g_new0(struct gateway, 1);
+
 	if (!g_dbus_register_interface(dev->conn, dev->path,
 					AUDIO_GATEWAY_INTERFACE,
 					gateway_methods, gateway_signals,
 					NULL, dev, NULL))
 		return NULL;
 
-	debug("in gateway_init, dev is %p", dev);
-	gw = g_new0(struct gateway, 1);
-	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
-
 }
 
 gboolean gateway_is_connected(struct audio_device *dev)
@@ -326,17 +595,6 @@ gboolean gateway_is_connected(struct audio_device *dev)
 			dev->gateway->state == GATEWAY_STATE_CONNECTED);
 }
 
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
-{
-	if (!io)
-		return -EINVAL;
-
-	g_io_channel_ref(io);
-	dev->gateway->rfcomm = io;
-
-	return 0;
-}
-
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 {
 	struct gateway *gw = dev->gateway;
@@ -347,42 +605,39 @@ int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 	gw->sco = g_io_channel_ref(io);
 
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                                (GIOFunc) sco_io_cb, dev);
+				 (GIOFunc) sco_io_cb, dev);
+
+	change_state(dev, GATEWAY_STATE_PLAYING);
+
 	return 0;
 }
 
-void gateway_start_service(struct audio_device *device)
+int gateway_connect_rfcomm(struct audio_device *dev,
+		GIOChannel *chan, int ch)
 {
-	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
+	if (!chan)
+		return -EINVAL;
+
+	g_io_channel_ref(chan);
+	dev->gateway->rfcomm = chan;
+	dev->gateway->channel = ch;
+
+	return 0;
 }
 
-int gateway_close(struct audio_device *device)
+void gateway_start_service(struct audio_device *dev)
 {
-	struct gateway *gw = device->gateway;
-	GIOChannel *rfcomm = gw->rfcomm;
-	GIOChannel *sco = gw->sco;
-	gboolean value = FALSE;
+	struct gateway *gw = dev->gateway;
+	GError *err = NULL;
 
-	if (rfcomm) {
-		g_io_channel_shutdown(rfcomm, TRUE, NULL);
-		g_io_channel_unref(rfcomm);
-		gw->rfcomm = NULL;
-	}
+	if (gw->rfcomm == NULL)
+		return;
 
-	if (sco) {
-		g_io_channel_shutdown(sco, TRUE, NULL);
-		g_io_channel_unref(sco);
-		gw->sco = NULL;
-		gw->sco_start_cb = NULL;
-		gw->sco_start_cb_data = NULL;
+	if (!bt_io_accept(gw->rfcomm, rfcomm_connect_cb, dev, NULL,
+							&err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
 	}
-
-	gw->state = GATEWAY_STATE_DISCONNECTED;
-
-	emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN, &value);
-	return 0;
 }
 
 /* These are functions to be called from unix.c for audio system
@@ -399,8 +654,12 @@ gboolean gateway_request_stream(struct audio_device *dev,
 		gw->sco_start_cb_data = user_data;
 		get_records(dev);
 	} else if (!gw->sco) {
+		char source[128], destination[128];
+
 		gw->sco_start_cb = cb;
 		gw->sco_start_cb_data = user_data;
+		ba2str(&dev->src, source);
+		ba2str(&dev->dst, destination);
 		io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -464,4 +723,3 @@ void gateway_suspend_stream(struct audio_device *dev)
 	gw->sco_start_cb = NULL;
 	gw->sco_start_cb_data = NULL;
 }
-
diff --git a/audio/gateway.h b/audio/gateway.h
index 3b0457f..eaf6801 100644
--- a/audio/gateway.h
+++ b/audio/gateway.h
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -22,20 +23,25 @@
  *
  */
 
-#define AUDIO_GATEWAY_INTERFACE "org.bluez.HeadsetGateway"
+#define AUDIO_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
 
 #define DEFAULT_HSP_HS_CHANNEL 6
 #define DEFAULT_HFP_HS_CHANNEL 7
 
 typedef enum {
 	GATEWAY_STATE_DISCONNECTED,
-	GATEWAY_STATE_CONNECTED
+	GATEWAY_STATE_CONNECTED,
+	GATEWAY_STATE_CONNECTING,
+	GATEWAY_STATE_PLAYING,
 } gateway_state_t;
 
 typedef void (*gateway_stream_cb_t) (struct audio_device *dev, void *user_data);
+
 struct gateway *gateway_init(struct audio_device *device);
+void gateway_exit();
 gboolean gateway_is_connected(struct audio_device *dev);
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *chan);
+int gateway_connect_rfcomm(struct audio_device *dev,
+			GIOChannel *chan, int ch);
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *chan);
 void gateway_start_service(struct audio_device *device);
 gboolean gateway_request_stream(struct audio_device *dev,
diff --git a/audio/manager.c b/audio/manager.c
index 413c1f3..3ac1a60 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -198,7 +198,7 @@ static void handle_uuid(const char *uuidstr, struct audio_device *device)
 		break;
 	case HANDSFREE_AGW_SVCLASS_ID:
 		debug("Found Handsfree AG record");
-		if (device->gateway == NULL)
+		if (enabled.gateway && (device->gateway == NULL))
 			device->gateway = gateway_init(device);
 		break;
 	case AUDIO_SINK_SVCLASS_ID:
@@ -567,8 +567,8 @@ static void hf_io_cb(GIOChannel *chan, gpointer data)
 		return;
 	}
 
-	server_uuid = HFP_HS_UUID;
-	remote_uuid = HFP_AG_UUID;
+	server_uuid = HFP_AG_UUID;
+	remote_uuid = HFP_HS_UUID;
 	svclass = HANDSFREE_AGW_SVCLASS_ID;
 
 	device = manager_get_device(&src, &dst, TRUE);
@@ -586,7 +586,7 @@ static void hf_io_cb(GIOChannel *chan, gpointer data)
 		goto drop;
 	}
 
-	if (gateway_connect_rfcomm(device, chan) < 0) {
+	if (gateway_connect_rfcomm(device, chan, ch) < 0) {
 		error("Allocating new GIOChannel failed!");
 		goto drop;
 	}
@@ -905,22 +905,12 @@ static void headset_server_remove(struct btd_adapter *adapter)
 static int gateway_server_probe(struct btd_adapter *adapter)
 {
 	struct audio_adapter *adp;
-	const gchar *path = adapter_get_path(adapter);
-	int ret;
-
-	DBG("path %s", path);
 
 	adp = audio_adapter_get(adapter);
 	if (!adp)
 		return -EINVAL;
 
-	ret = gateway_server_init(adp);
-	if (ret < 0) {
-		audio_adapter_ref(adp);
-		return ret;
-	}
-
-	return 0;
+	return gateway_server_init(adp);
 }
 
 static void gateway_server_remove(struct btd_adapter *adapter)
diff --git a/audio/unix.c b/audio/unix.c
index 5cf4f94..bd1a415 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -395,6 +395,9 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	struct bt_start_stream_rsp *rsp = (void *) buf;
 	struct bt_new_stream_ind *ind = (void *) buf;
 
+	if (!dev)
+		goto failed;
+
 	memset(buf, 0, sizeof(buf));
 	rsp->h.type = BT_RESPONSE;
 	rsp->h.name = BT_START_STREAM;
@@ -416,6 +419,11 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	}
 
 	client->req_id = 0;
+	return;
+
+failed:
+	error("gateway_resume_complete: resume failed");
+	unix_ipc_error(client, BT_START_STREAM, EIO);
 }
 
 static void headset_suspend_complete(struct audio_device *dev, void *user_data)
diff --git a/doc/hfp-api.txt b/doc/hfp-api.txt
new file mode 100644
index 0000000..ed52be1
--- /dev/null
+++ b/doc/hfp-api.txt
@@ -0,0 +1,81 @@
+Gateway hierarchy
+========================
+
+Service		org.bluez
+Interface	org.bluez.HandsfreeGateway
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+This interface is available for remote devices which can function in the Audio
+Gateway role of the HFP profiles.  It is intended to be used with external
+telephony stacks / handlers of the HFP protocol.
+
+Methods		void Connect()
+
+			Connect to the AG service on the remote device.
+
+		void Disconnect()
+
+			Disconnect from the AG service on the remote device
+
+		dict GetProperties()
+
+			Returns all properties for the interface. See the
+			properties section for available properties.
+
+		void RegisterAgent(object path)
+
+			The object path defines the path the of the agent
+			that will be called when a new Handsfree connection
+			is established.
+			
+			If an application disconnects from the bus all of its
+			registered agents will be removed.
+
+		void UnregisterAgent(object path)
+
+			This unregisters the agent that has been previously
+			registered. The object path parameter must match the
+			same value that has been used on registration.
+
+Signals		PropertyChanged(string name, variant value)
+
+			This signal indicates a changed value of the given
+			property.
+
+Properties	string State [readonly]
+
+			Indicates the state of the connection.  Possible
+			values are:
+				"disconnected"
+				"connecting"
+				"connected"
+				"playing"
+
+HandsfreeAgent hierarchy
+===============
+
+Service         unique name
+Interface       org.bluez.HandsfreeAgent
+Object path     freely definable
+
+Methods		void NewConnection(filedescriptor fd)
+
+			This method gets called whenever a new handsfree
+			connection has been established.  The objectpath
+			contains the object path of the remote device.  This
+			method assumes that DBus daemon with file descriptor
+			passing capability is being used.
+
+			The agent should only return successfully once the
+			establishment of the service level connection (SLC)
+			has been completed.  In the case of Handsfree this
+			means that BRSF exchange has been performed and
+			necessary initialization has been done.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.Failed
+
+		void Release()
+
+			This method gets called whenever the service daemon
+			unregisters the agent.
-- 
1.6.4.4


[-- Attachment #4: 0001-Add-HFP-support-through-BlueZ.patch --]
[-- Type: application/octet-stream, Size: 14998 bytes --]

From 61f4d5df1d54e318eeb039bc048c22763ca3e20b Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 8 Jan 2010 17:25:43 -0200
Subject: [PATCH] Add HFP support through BlueZ

It uses BlueZ through to get HFP working following the
org.bluez.HandsfreeGateway and org.bluez.HandsfreeAgent from the BlueZ
D-Bus API.

You need the HFP patch into BlueZ and dbus 1.3.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for its prototype
on this code.
---
 drivers/hfpmodem/hfpmodem.h |    2 +
 plugins/hfp.c               |  419 +++++++++++++++++++++++++++++++++++++++++--
 plugins/modemconf.c         |    1 -
 3 files changed, 407 insertions(+), 15 deletions(-)

diff --git a/drivers/hfpmodem/hfpmodem.h b/drivers/hfpmodem/hfpmodem.h
index 509846b..5fee68f 100644
--- a/drivers/hfpmodem/hfpmodem.h
+++ b/drivers/hfpmodem/hfpmodem.h
@@ -63,11 +63,13 @@ enum hfp_indicator {
 
 struct hfp_data {
 	GAtChat *chat;
+	char *handsfree_path;
 	unsigned int ag_features;
 	unsigned int ag_mpty_features;
 	unsigned int hf_features;
 	unsigned char cind_pos[HFP_INDICATOR_LAST];
 	unsigned int cind_val[HFP_INDICATOR_LAST];
+	unsigned int at_timeout;
 };
 
 extern void hfp_netreg_init();
diff --git a/plugins/hfp.c b/plugins/hfp.c
index 3bbd922..42269f2 100644
--- a/plugins/hfp.c
+++ b/plugins/hfp.c
@@ -30,6 +30,8 @@
 #include <glib.h>
 #include <gatchat.h>
 #include <gattty.h>
+#include <gdbus.h>
+#include <ofono.h>
 
 #define OFONO_API_SUBJECT_TO_CHANGE
 #include <ofono/plugin.h>
@@ -52,11 +54,26 @@
 
 #include <drivers/hfpmodem/hfpmodem.h>
 
+#include <ofono/dbus.h>
+
+#define BLUEZ_SERVICE "org.bluez"
+#define BLUEZ_PATH "/"
+#define BLUEZ_MANAGER_INTERFACE "org.bluez.Manager"
+#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter"
+#define BLUEZ_DEVICE_INTERFACE "org.bluez.Device"
+#define BLUEZ_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
+
+#define HFP_AGENT_INTERFACE "org.bluez.HandsfreeAgent"
+
+#define HFP_AG_UUID	"0000111F-0000-1000-8000-00805F9B34FB"
+
 static const char *brsf_prefix[] = { "+BRSF:", NULL };
 static const char *cind_prefix[] = { "+CIND:", NULL };
 static const char *cmer_prefix[] = { "+CMER:", NULL };
 static const char *chld_prefix[] = { "+CHLD:", NULL };
 
+static DBusConnection *connection;
+
 static int hfp_disable(struct ofono_modem *modem);
 
 static void hfp_debug(const char *str, void *user_data)
@@ -135,6 +152,60 @@ static void cmer_cb(gboolean ok, GAtResult *result, gpointer user_data)
 		sevice_level_conn_established(modem);
 }
 
+static int send_method_call(const char *dest, const char *path,
+				const char *interface, const char *method,
+				DBusPendingCallNotifyFunction cb,
+				void *user_data, int type, ...)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+	va_list args;
+
+	msg = dbus_message_new_method_call(dest, path, interface, method);
+	if (!msg) {
+		ofono_error("Unable to allocate new D-Bus %s message", method);
+		return -ENOMEM;
+	}
+
+	va_start(args, type);
+
+	if (!dbus_message_append_args_valist(msg, type, args)) {
+		dbus_message_unref(msg);
+		va_end(args);
+		return -EIO;
+	}
+
+	va_end(args);
+
+	if (!cb) {
+		g_dbus_send_message(connection, msg);
+		return 0;
+	}
+
+	if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+		ofono_error("Sending %s failed", method);
+		dbus_message_unref(msg);
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(call, cb, user_data, NULL);
+	dbus_pending_call_unref(call);
+	dbus_message_unref(msg);
+
+	return 0;
+}
+
+static gboolean hfp_enable_timeout(gpointer user)
+{
+	struct ofono_modem *modem = user;
+
+	if (ofono_modem_get_powered(modem))
+		return FALSE;
+
+	hfp_disable(modem);
+	return FALSE;
+}
+
 static void cind_status_cb(gboolean ok, GAtResult *result,
 				gpointer user_data)
 {
@@ -260,8 +331,7 @@ error:
 }
 
 /* either oFono or Phone could request SLC connection */
-static int service_level_connection(struct ofono_modem *modem,
-				const char *tty)
+static int service_level_connection(struct ofono_modem *modem, int fd)
 {
 	struct hfp_data *data = ofono_modem_get_data(modem);
 	GIOChannel *io;
@@ -269,7 +339,7 @@ static int service_level_connection(struct ofono_modem *modem,
 	GAtChat *chat;
 	char buf[64];
 
-	io = g_at_tty_open(tty, NULL);
+	io = g_io_channel_unix_new(fd);
 	if (!io) {
 		ofono_error("Service level connection failed: %s (%d)",
 			strerror(errno), errno);
@@ -288,7 +358,6 @@ static int service_level_connection(struct ofono_modem *modem,
 		g_at_chat_set_debug(chat, hfp_debug, NULL);
 
 	sprintf(buf, "AT+BRSF=%d", data->hf_features);
-
 	g_at_chat_send(chat, buf, brsf_prefix,
 				brsf_cb, modem, NULL);
 	data->chat = chat;
@@ -296,10 +365,46 @@ static int service_level_connection(struct ofono_modem *modem,
 	return -EINPROGRESS;
 }
 
-static int hfp_probe(struct ofono_modem *modem)
+static DBusMessage *hfp_agent_new_connection(DBusConnection *conn, DBusMessage *msg, void *data)
 {
+	int fd;
+	struct ofono_modem *modem = data;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UNIX_FD, &fd,
+				DBUS_TYPE_INVALID))
+		return __ofono_error_invalid_args(msg);
+
+	service_level_connection(modem, fd);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *hfp_agent_release(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	struct ofono_modem *modem = data;
+	struct hfp_data *hfp_data = ofono_modem_get_data(modem);
+
+	g_at_chat_shutdown(hfp_data->chat);
+
+	return NULL;
+}
+
+static GDBusMethodTable agent_methods[] = {
+        { "NewConnection", "h", "", hfp_agent_new_connection,
+		G_DBUS_METHOD_FLAG_ASYNC },
+	{ "Release", "", "", hfp_agent_release },
+	{NULL, NULL, NULL, NULL}
+};
+
+static int hfp_create_modem(const char *device)
+{
+	struct ofono_modem *modem;
 	struct hfp_data *data;
 
+	ofono_info("Using device: %s", device);
+
+	modem = ofono_modem_create(NULL, "hfp");
+
 	data = g_try_new0(struct hfp_data, 1);
 	if (!data)
 		return -ENOMEM;
@@ -310,36 +415,315 @@ static int hfp_probe(struct ofono_modem *modem)
 	data->hf_features |= HF_FEATURE_ENHANCED_CALL_STATUS;
 	data->hf_features |= HF_FEATURE_ENHANCED_CALL_CONTROL;
 
+	data->handsfree_path = g_strdup(device);
+
 	ofono_modem_set_data(modem, data);
 
+	ofono_modem_register(modem);
+
+	return 0;
+}
+
+static void parse_uuids(DBusMessageIter *i, const char *device)
+{
+	DBusMessageIter variant, ai;
+	const char *value;
+
+	dbus_message_iter_recurse(i, &variant);
+	dbus_message_iter_recurse(&variant, &ai);
+
+	while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
+		dbus_message_iter_get_basic(&ai, &value);
+		if (!strcasecmp(value, HFP_AG_UUID))
+			hfp_create_modem(device);
+
+		if (!dbus_message_iter_next(&ai))
+			return;
+	}
+}
+
+static void parse_get_properties(DBusMessage *reply, const char *device)
+{
+	DBusMessageIter arg, element, variant;
+	const char *key;
+
+	if (!dbus_message_iter_init(reply, &arg)) {
+		ofono_debug("GetProperties reply has no arguments.");
+		return;
+	}
+
+	if (dbus_message_iter_get_arg_type(&arg) != DBUS_TYPE_ARRAY) {
+		ofono_debug("GetProperties argument is not an array.");
+		return;
+	}
+
+	dbus_message_iter_recurse(&arg, &element);
+	while (dbus_message_iter_get_arg_type(&element) != DBUS_TYPE_INVALID) {
+		if (dbus_message_iter_get_arg_type(&element) ==
+				DBUS_TYPE_DICT_ENTRY) {
+			DBusMessageIter dict;
+
+			dbus_message_iter_recurse(&element, &dict);
+
+			if (dbus_message_iter_get_arg_type(&dict) !=
+					DBUS_TYPE_STRING) {
+				ofono_debug("Property name not a string.");
+				return;
+			}
+
+			dbus_message_iter_get_basic(&dict, &key);
+
+			if (!dbus_message_iter_next(&dict))  {
+				ofono_debug("Property value missing");
+				return;
+			}
+
+			if (dbus_message_iter_get_arg_type(&dict) !=
+					DBUS_TYPE_VARIANT) {
+				ofono_debug("Property value not a variant.");
+				return;
+			}
+
+			if (!strcmp(key, "UUIDs"))
+				parse_uuids(&dict, device);
+
+			dbus_message_iter_recurse(&dict, &variant);
+		}
+
+		if (!dbus_message_iter_next(&element))
+			return;
+	}
+}
+
+static void get_properties_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	const char *device = user_data;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	if (dbus_message_is_error(reply, DBUS_ERROR_SERVICE_UNKNOWN)) {
+		ofono_debug("Bluetooth daemon is apparently not available.");
+		goto done;
+	}
+
+	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+		if (!dbus_message_is_error(reply, DBUS_ERROR_UNKNOWN_METHOD))
+			ofono_info("Error from GetProperties reply: %s",
+					dbus_message_get_error_name(reply));
+
+		goto done;
+	}
+
+	parse_get_properties(reply, device);
+
+done:
+	dbus_message_unref(reply);
+}
+
+static void list_devices_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	struct ofono_modem *modem = user_data;
+	const char **device = NULL;
+	int num, ret, i;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	if (dbus_message_is_error(reply, DBUS_ERROR_SERVICE_UNKNOWN)) {
+		ofono_debug("Bluetooth daemon is apparently not available.");
+		goto done;
+	}
+
+	dbus_error_init(&err);
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_ARRAY,
+				DBUS_TYPE_OBJECT_PATH, &device,
+				&num, DBUS_TYPE_INVALID) == FALSE) {
+		if (device == NULL) {
+			dbus_error_free(&err);
+			goto done;
+		}
+
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s", err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	for (i = 0 ; i < num ; i++) {
+		ret = send_method_call(BLUEZ_SERVICE, device[i],
+				BLUEZ_DEVICE_INTERFACE, "GetProperties",
+				get_properties_cb, (void *)device[i],
+				DBUS_TYPE_INVALID);
+		if (ret < 0)
+			ofono_error("GetProperties failed(%d)", ret);
+	}
+
+done:
+	dbus_message_unref(reply);
+}
+
+static void list_adapters_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	const char **adapter = NULL;
+	int num, ret, i;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	if (dbus_message_is_error(reply, DBUS_ERROR_SERVICE_UNKNOWN)) {
+		ofono_debug("Bluetooth daemon is apparently not available.");
+		goto done;
+	}
+
+	dbus_error_init(&err);
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_ARRAY,
+				DBUS_TYPE_OBJECT_PATH, &adapter,
+				&num, DBUS_TYPE_INVALID) == FALSE) {
+		if (adapter == NULL) {
+			dbus_error_free(&err);
+			goto done;
+		}
+
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s", err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	for (i = 0 ; i < num ; i++) {
+		ret = send_method_call(BLUEZ_SERVICE, adapter[i],
+				BLUEZ_ADAPTER_INTERFACE, "ListDevices",
+				list_devices_cb, NULL,
+				DBUS_TYPE_INVALID);
+
+		if (ret < 0)
+			ofono_error("ListDevices failed(%d)", ret);
+	}
+
+done:
+	dbus_message_unref(reply);
+
+}
+
+static int hfp_load_modems()
+{
+	return send_method_call(BLUEZ_SERVICE, BLUEZ_PATH,
+				BLUEZ_MANAGER_INTERFACE, "ListAdapters",
+				list_adapters_cb, NULL,
+				DBUS_TYPE_INVALID);
+}
+
+static int hfp_register_ofono_handsfree(struct ofono_modem *modem)
+{
+	const char *obj_path = ofono_modem_get_path(modem);
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	ofono_debug("Registering oFono Agent to bluetooth daemon");
+
+	if (!data->handsfree_path)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "RegisterAgent",
+				NULL, NULL, DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+}
+
+static int hfp_unregister_ofono_handsfree(struct ofono_modem *modem)
+{
+	const char *obj_path = ofono_modem_get_path(modem);
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	ofono_debug("Unregistering oFono Agent from bluetooth daemon");
+
+	if (!data->handsfree_path)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "UnregisterAgent",
+				NULL, NULL, DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+}
+
+static int hfp_probe(struct ofono_modem *modem)
+{
+	struct hfp_data *data;
+	const char *obj_path;
+
+	obj_path = ofono_modem_get_path(modem);
+
+	g_dbus_register_interface(connection, obj_path, HFP_AGENT_INTERFACE,
+			agent_methods, NULL, NULL, modem, NULL);
+
+	if (hfp_register_ofono_handsfree(modem) != 0)
+		return -EINVAL;
+
 	return 0;
 }
 
 static void hfp_remove(struct ofono_modem *modem)
 {
-	gpointer data = ofono_modem_get_data(modem);
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	hfp_unregister_ofono_handsfree(modem);
+
+	if (data->handsfree_path)
+		g_free(data->handsfree_path);
 
 	if (data)
 		g_free(data);
 
+	dbus_connection_unref(connection);
+
 	ofono_modem_set_data(modem, NULL);
 }
 
+static int hfp_connect_ofono_handsfree(struct ofono_modem *modem)
+{
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	ofono_debug("Connect to bluetooth daemon");
+
+	if (!data->handsfree_path || !connection)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Connect",
+				NULL, NULL, DBUS_TYPE_INVALID);
+}
+
 /* power up hardware */
 static int hfp_enable(struct ofono_modem *modem)
 {
-	const char *tty;
-	int ret;
+	struct hfp_data *data = ofono_modem_get_data(modem);
 
 	DBG("%p", modem);
 
-	tty = ofono_modem_get_string(modem, "Device");
-	if (tty == NULL)
+	data->at_timeout =
+		g_timeout_add_seconds(10, hfp_enable_timeout, modem);
+
+	if (hfp_connect_ofono_handsfree(modem) < 0)
 		return -EINVAL;
 
-	ret = service_level_connection(modem, tty);
+	return -EINPROGRESS;
+}
+
+static int hfp_disconnect_ofono_handsfree(struct ofono_modem *modem)
+{
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	if (!data->handsfree_path || !connection)
+		return -EINVAL;
 
-	return ret;
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Disconnect",
+				NULL, NULL, DBUS_TYPE_INVALID);
 }
 
 static int hfp_disable(struct ofono_modem *modem)
@@ -359,6 +743,7 @@ static int hfp_disable(struct ofono_modem *modem)
 
 	ofono_modem_set_powered(modem, FALSE);
 
+	hfp_disconnect_ofono_handsfree(modem);
 	return 0;
 }
 
@@ -390,8 +775,14 @@ static struct ofono_modem_driver hfp_driver = {
 
 static int hfp_init(void)
 {
-	DBG("");
-	return ofono_modem_driver_register(&hfp_driver);
+	DBusConnection *conn = ofono_dbus_get_connection();
+
+	ofono_modem_driver_register(&hfp_driver);
+
+	connection = dbus_connection_ref(conn);
+	hfp_load_modems();
+
+	return 0;
 }
 
 static void hfp_exit(void)
diff --git a/plugins/modemconf.c b/plugins/modemconf.c
index c8c261f..932d610 100644
--- a/plugins/modemconf.c
+++ b/plugins/modemconf.c
@@ -129,7 +129,6 @@ static struct ofono_modem *create_modem(GKeyFile *keyfile, const char *group)
 
 	if (!g_strcmp0(driver, "atgen") || !g_strcmp0(driver, "g1") ||
 						!g_strcmp0(driver, "calypso") ||
-						!g_strcmp0(driver, "hfp") ||
 						!g_strcmp0(driver, "palmpre"))
 		set_device(modem, keyfile, group);
 
-- 
1.6.4.4


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

* Re: [RFC] HFP support into oFono and BlueZ
@ 2010-01-20 19:58       ` Gustavo F. Padovan
  0 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-20 19:58 UTC (permalink / raw)
  To: ofono

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

> New version of the patches. Known issues:
> "'org.bluez.HandsfreeAgent.Release' part not implemented" and it's not
> working with more than one bluetooth devices in some cases. Comments
> are welcome.
>

Another new version of the patches. Now ofono query all bluetooth
devices and searches for the ones with Gateway interface.
Bluez has some fixes and now supports connection coming from the AG.
The patches are using a per-device agent as described by the last
version of Handsfree API.

I did not implemented yet 'org.bluez.HandsfreeAgent.Release'.

Comments are welcome. :)

-- 
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi

[-- Attachment #2: 0001-clean-up-audio-gateway.c.patch --]
[-- Type: application/octet-stream, Size: 24620 bytes --]

From 1dee86673257625823137717b06d0757d5715781 Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 6 Nov 2009 18:08:58 -0200
Subject: [PATCH 1/2] clean up audio/gateway.c

remove all code related to the AT engine
---
 audio/gateway.c |  760 +------------------------------------------------------
 1 files changed, 1 insertions(+), 759 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index a1c1ea2..3dc09ff 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -53,52 +53,6 @@
 #include "dbus-common.h"
 
 #define RFCOMM_BUF_SIZE 256
-/* not-more-then-16 defined by GSM + 1 for NULL + padding */
-#define AG_INDICATOR_DESCR_SIZE 20
-#define AG_CALLER_NUM_SIZE 64	/* size of number + type */
-
-/* commands */
-#define AG_FEATURES "AT+BRSF=26\r"     /* = 0x7F = All features supported */
-#define AG_INDICATORS_SUPP "AT+CIND=?\r"
-#define AG_INDICATORS_VAL "AT+CIND?\r"
-#define AG_INDICATORS_ENABLE "AT+CMER=3,0,0,1\r"
-#define AG_HOLD_MPTY_SUPP "AT+CHLD=?\r"
-#define AG_CALLER_IDENT_ENABLE "AT+CLIP=1\r"
-#define AG_CARRIER_FORMAT "AT+COPS=3,0\r"
-#define AG_EXTENDED_RESULT_CODE "AT+CMEE=1\r"
-
-#define AG_FEATURE_3WAY 0x1
-#define AG_FEATURE_EXTENDED_RES_CODE 0x100
-/* Hold and multipary AG features.
- * Comments below are copied from hands-free spec for reference */
-/* Releases all held calls or sets User Determined User Busy (UDUB)
- * for a waiting call */
-#define AG_CHLD_0 0x01
-/* Releases all active calls (if any exist) and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_1 0x02
-/* Releases specified active call only <x> */
-#define AG_CHLD_1x 0x04
-/* Places all active calls (if any exist) on hold and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_2 0x08
-/* Request private consultation mode with specified call <x> (Place all
- * calls on hold EXCEPT the call <x>) */
-#define AG_CHLD_2x 0x10
-/* Adds a held call to the conversation */
-#define AG_CHLD_3 0x20
-/* Connects the two calls and disconnects the subscriber from both calls
- * (Explicit Call Transfer). Support for this value and its associated
- * functionality is optional for the HF. */
-#define AG_CHLD_4 0x40
-
-#define OK_RESPONSE "\r\nOK\r\n"
-#define ERROR_RESPONSE "\r\nERROR\r\n"
-
-struct indicator {
-	gchar descr[AG_INDICATOR_DESCR_SIZE];
-	gint value;
-};
 
 struct gateway {
 	gateway_state_t state;
@@ -108,387 +62,10 @@ struct gateway {
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
 	DBusMessage *connect_message;
-	guint ag_features;
-	guint hold_multiparty_features;
-	GSList *indies;
-	gboolean is_dialing;
-	gboolean call_active;
-
-	int sp_gain;
-	int mic_gain;
 };
 
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device);
-
 int gateway_close(struct audio_device *device);
 
-static void rfcomm_start_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	gw->rfcomm_watch_id = g_io_add_watch(gw->rfcomm,
-			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-			(GIOFunc) rfcomm_ag_data_cb, dev);
-}
-
-static void rfcomm_stop_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	g_source_remove(gw->rfcomm_watch_id);
-}
-
-static gboolean io_channel_write_all(GIOChannel *io, gchar *data,
-					gsize count)
-{
-	gsize written = 0;
-	GIOStatus status;
-
-	while (count > 0) {
-		status = g_io_channel_write_chars(io, data, count, &written,
-						NULL);
-		if (status != G_IO_STATUS_NORMAL)
-			return FALSE;
-
-		data += written;
-		count -= written;
-	}
-	return TRUE;
-}
-
-/* it's worth to mention that data and response could be the same pointers */
-static gboolean rfcomm_send_and_read(struct gateway *gw, gchar *data,
-                                    gchar *response, gsize count)
-{
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read = 0;
-	gboolean got_ok = FALSE;
-	gboolean got_error = FALSE;
-	gchar *resp_buf = response;
-	gsize toread = RFCOMM_BUF_SIZE - 1;
-	GIOStatus status;
-
-	if (!io_channel_write_all(rfcomm, data, count))
-		return FALSE;
-
-	while (!(got_ok || got_error)) {
-		status = g_io_channel_read_chars(rfcomm, resp_buf, toread,
-						&read, NULL);
-		if (status == G_IO_STATUS_NORMAL)
-			resp_buf[read] = '\0';
-		else {
-			debug("rfcomm_send_and_read(): %m");
-			return FALSE;
-		}
-		got_ok = NULL != strstr(resp_buf, OK_RESPONSE);
-		got_error = NULL != strstr(resp_buf, ERROR_RESPONSE);
-		resp_buf += read;
-		toread -= read;
-	}
-	return TRUE;
-}
-
-/* get <descr> from the names: (<descr>, (<values>)), (<descr>, (<values>))
- * ... */
-static GSList *parse_indicator_names(gchar *names, GSList *indies)
-{
-	gchar *current = names - 1;
-	GSList *result = indies;
-	gchar *next;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 2;
-		next = strstr(current, ",(");
-		ind = g_slice_new(struct indicator);
-		strncpy(ind->descr, current, 20);
-		ind->descr[(intptr_t) next - (intptr_t) current] = '\0';
-		result = g_slist_append(result, (gpointer) ind);
-		current = strstr(next + 1, ",(");
-	}
-	return result;
-}
-
-/* get values from <val0>,<val1>,... */
-static GSList *parse_indicator_values(gchar *values, GSList *indies)
-{
-	gint val;
-	gchar *current = values - 1;
-	GSList *runner = indies;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 1;
-		sscanf(current, "%d", &val);
-		current = strchr(current, ',');
-		ind = g_slist_nth_data(runner, 0);
-		ind->value = val;
-		runner = g_slist_next(runner);
-	}
-	return indies;
-}
-
-/* get values from <val0>,<val1>,... */
-static guint get_hold_mpty_features(gchar *features)
-{
-	guint result = 0;
-
-	if (strstr(features, "0"))
-		result |= AG_CHLD_0;
-
-	if (strstr(features, "1"))
-		result |= AG_CHLD_1;
-
-	if (strstr(features, "1x"))
-		result |= AG_CHLD_1x;
-
-	if (strstr(features, "2"))
-		result |= AG_CHLD_2;
-
-	if (strstr(features, "2x"))
-		result |= AG_CHLD_2x;
-
-	if (strstr(features, "3"))
-		result |= AG_CHLD_3;
-
-	if (strstr(features, "4"))
-		result |= AG_CHLD_4;
-
-	return result;
-}
-
-static gboolean establish_service_level_conn(struct gateway *gw)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	gboolean res;
-
-	debug("at the begin of establish_service_level_conn()");
-	res = rfcomm_send_and_read(gw, AG_FEATURES, buf,
-				sizeof(AG_FEATURES) - 1);
-	if (!res || sscanf(buf, "\r\n+BRSF:%d", &gw->ag_features) != 1)
-		return FALSE;
-
-	debug("features are 0x%X", gw->ag_features);
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_SUPP, buf,
-				sizeof(AG_INDICATORS_SUPP) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_names(strchr(buf, '('), NULL);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_VAL, buf,
-		sizeof(AG_INDICATORS_VAL) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_values(strchr(buf, ':') + 1, gw->indies);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_ENABLE, buf,
-				sizeof(AG_INDICATORS_ENABLE) - 1);
-	if (!res || !strstr(buf, "OK"))
-		return FALSE;
-
-	if ((gw->ag_features & AG_FEATURE_3WAY) != 0) {
-		res = rfcomm_send_and_read(gw, AG_HOLD_MPTY_SUPP, buf,
-				sizeof(AG_HOLD_MPTY_SUPP) - 1);
-		if (!res || !strstr(buf, "+CHLD:")) {
-			g_slice_free1(RFCOMM_BUF_SIZE, buf);
-			return FALSE;
-		}
-		gw->hold_multiparty_features = get_hold_mpty_features(
-							strchr(buf, '('));
-
-	} else
-		gw->hold_multiparty_features = 0;
-
-	debug("Service layer connection successfully established!");
-	rfcomm_send_and_read(gw, AG_CALLER_IDENT_ENABLE, buf,
-			sizeof(AG_CALLER_IDENT_ENABLE) - 1);
-	rfcomm_send_and_read(gw, AG_CARRIER_FORMAT, buf,
-			sizeof(AG_CARRIER_FORMAT) - 1);
-	if ((gw->ag_features & AG_FEATURE_EXTENDED_RES_CODE) != 0)
-		rfcomm_send_and_read(gw, AG_EXTENDED_RESULT_CODE, buf,
-			sizeof(AG_EXTENDED_RESULT_CODE) - 1);
-
-	return TRUE;
-}
-
-static void process_ind_change(struct audio_device *dev, guint index,
-							gint value)
-{
-	struct gateway *gw = dev->gateway;
-	struct indicator *ind = g_slist_nth_data(gw->indies, index - 1);
-	gchar *name = ind->descr;
-
-	ind->value = value;
-
-	debug("at the begin of process_ind_change, name is %s", name);
-	if (!strcmp(name, "\"call\"")) {
-		if (value > 0) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallStarted", DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-			gw->call_active = TRUE;
-		} else {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallEnded", DBUS_TYPE_INVALID);
-			gw->call_active = FALSE;
-		}
-
-	} else if (!strcmp(name, "\"callsetup\"")) {
-		if (value == 0 && gw->is_dialing) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-		} else if (!gw->is_dialing && value > 0)
-			gw->is_dialing = TRUE;
-
-	} else if (!strcmp(name, "\"callheld\"")) {
-		/* FIXME: The following code is based on assumptions only.
-		 * Has to be tested for interoperability
-		 * I assume that callheld=2 would be sent when dial from HF
-		 * failed in case of 3-way call
-		 * Unfortunately this path is not covered by the HF spec so
-		 * the code has to be tested for interop
-		*/
-		/* '2' means: all calls held, no active calls */
-		if (value == 2) {
-			if (gw->is_dialing) {
-				g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-				gw->is_dialing = FALSE;
-			}
-		}
-	} else if (!strcmp(name, "\"service\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RegistrationStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"signal\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "SignalStrength",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"roam\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RoamingStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"battchg\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "BatteryCharge",
-				DBUS_TYPE_UINT16, &value);
-}
-
-static void process_ring(struct audio_device *device, GIOChannel *chan,
-			gchar *buf)
-{
-	gchar number[AG_CALLER_NUM_SIZE];
-	gchar *cli;
-	gchar *sep;
-	gsize read;
-	GIOStatus status;
-
-	rfcomm_stop_watch(device);
-	status = g_io_channel_read_chars(chan, buf, RFCOMM_BUF_SIZE - 1, &read, NULL);
-	if (status != G_IO_STATUS_NORMAL)
-		return;
-
-	debug("at the begin of process_ring");
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 10)
-		error("process_ring(): buf is too long '%s'", buf);
-	else if ((cli = strstr(buf, "\r\n+CLIP"))) {
-		if (sscanf(cli, "\r\n+CLIP: \"%s", number) == 1) {
-			sep = strchr(number, '"');
-			sep[0] = '\0';
-
-			/* FIXME:signal will be emitted on each RING+CLIP.
-			 * That's bad */
-			cli = number;
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE, "Ring",
-					DBUS_TYPE_STRING, &cli,
-					DBUS_TYPE_INVALID);
-			device->gateway->is_dialing = TRUE;
-		} else
-			error("process_ring(): '%s' in place of +CLIP after RING", buf);
-
-	}
-
-	rfcomm_start_watch(device);
-}
-
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	struct gateway *gw;
-	gsize read;
-	/* some space for value */
-	gchar indicator[AG_INDICATOR_DESCR_SIZE + 4];
-	gint value;
-	guint index;
-	gchar *sep;
-
-	debug("at the begin of rfcomm_ag_data_cb()");
-	if (cond & G_IO_NVAL)
-		return FALSE;
-
-	gw = device->gateway;
-
-	if (cond & (G_IO_ERR | G_IO_HUP)) {
-		debug("connection with remote BT is closed");
-		gateway_close(device);
-		return FALSE;
-	}
-
-	if (g_io_channel_read_chars(chan, buf, sizeof(buf) - 1, &read, NULL)
-			!= G_IO_STATUS_NORMAL)
-		return TRUE;
-	buf[read] = '\0';
-
-	if (strlen(buf) > AG_INDICATOR_DESCR_SIZE + 14)
-		error("rfcomm_ag_data_cb(): buf is too long '%s'", buf);
-	else if (sscanf(buf, "\r\n+CIEV:%s\r\n", indicator) == 1) {
-		sep = strchr(indicator, ',');
-		sep[0] = '\0';
-		sep += 1;
-		index = atoi(indicator);
-		value = atoi(sep);
-		process_ind_change(device, index, value);
-	} else if (strstr(buf, "RING"))
-		process_ring(device, chan, buf);
-	else if (sscanf(buf, "\r\n+BVRA:%d\r\n", &value) == 1) {
-		if (value == 0)
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionActive",
-					DBUS_TYPE_INVALID);
-		else
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionInactive",
-					DBUS_TYPE_INVALID);
-	} else if (sscanf(buf, "\r\n+VGS:%d\r\n", &value) == 1) {
-		gw->sp_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "SpeakerGain",
-				DBUS_TYPE_UINT16, &value);
-	} else if (sscanf(buf, "\r\n+VGM:%d\r\n", &value) == 1) {
-		gw->mic_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "MicrophoneGain",
-				DBUS_TYPE_UINT16, &value);
-	} else
-		error("rfcomm_ag_data_cb(): read wrong data '%s'", buf);
-
-	return TRUE;
-}
-
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
 {
@@ -541,7 +118,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	DBusMessage *conn_mes = gw->connect_message;
 	gchar gw_addr[18];
 	GIOFlags flags;
 
@@ -563,29 +139,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 	if (!gw->rfcomm)
 		gw->rfcomm = g_io_channel_ref(chan);
 
-	if (establish_service_level_conn(dev->gateway)) {
-		gboolean value = TRUE;
-
-		debug("%s: Connected to %s", dev->path, gw_addr);
-		rfcomm_start_watch(dev);
-		if (conn_mes) {
-			DBusMessage *reply =
-				dbus_message_new_method_return(conn_mes);
-			dbus_connection_send(dev->conn, reply, NULL);
-			dbus_message_unref(reply);
-			dbus_message_unref(conn_mes);
-			gw->connect_message = NULL;
-		}
-
-		gw->state = GATEWAY_STATE_CONNECTED;
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN,	&value);
-		return;
-	} else
-		error("%s: Failed to establish service layer connection to %s",
-			dev->path, gw_addr);
-
 	if (NULL != gw->sco_start_cb)
 		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 
@@ -732,321 +285,20 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
-static DBusMessage *process_ag_reponse(DBusMessage *msg, gchar *response)
-{
-	DBusMessage *reply;
-
-
-	debug("in process_ag_reponse, response is %s", response);
-	if (strstr(response, OK_RESPONSE))
-		reply = dbus_message_new_method_return(msg);
-	else {
-		/* FIXME: some code should be here to processes errors
-		 *  in better fasion */
-		debug("AG responded with '%s' to %s method call", response,
-				dbus_message_get_member(msg));
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".OperationFailed",
-					"Operation failed.See log for details");
-	}
-	return reply;
-}
-
-static DBusMessage *process_simple(DBusMessage *msg, struct audio_device *dev,
-					gchar *data)
-{
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-
-	rfcomm_stop_watch(dev);
-	rfcomm_send_and_read(gw, data, buf, strlen(data));
-	rfcomm_start_watch(dev);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_ANSWER "ATA\r"
-
-static DBusMessage *ag_answer(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	if (gw->call_active)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".CallAlreadyAnswered",
-					"Call AlreadyAnswered");
-
-	return process_simple(msg, dev, AG_ANSWER);
-}
-
-#define AG_HANGUP "AT+CHUP\r"
-
-static DBusMessage *ag_terminate_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	return process_simple(msg, dev, AG_HANGUP);
-}
-
-/* according to GSM spec */
-#define ALLOWED_NUMBER_SYMBOLS "1234567890*#ABCD"
-#define AG_PLACE_CALL "ATD%s;\r"
-/* dialing from memory is not supported as headset spec doesn't define a way
- * to retreive phone memory entries.
- */
-static DBusMessage *ag_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint atd_len;
-	DBusMessage *result;
-
-	debug("at the begin of ag_call()");
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	atd_len = sprintf(buf, AG_PLACE_CALL, number);
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, buf, buf, atd_len);
-	rfcomm_start_watch(device);
-
-	result = process_ag_reponse(msg, buf);
-	return result;
-}
-
-#define AG_GET_CARRIER "AT+COPS?\r"
-
-static DBusMessage *ag_get_operator(DBusConnection *conn, DBusMessage *msg,
-					void *data)
-{
-	struct audio_device *dev = (struct audio_device *) data;
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read;
-	gchar *result, *sep;
-	DBusMessage *reply;
-	GIOStatus status;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(dev);
-	io_channel_write_all(rfcomm, AG_GET_CARRIER, strlen(AG_GET_CARRIER));
-
-	status = g_io_channel_read_chars(rfcomm, buf, RFCOMM_BUF_SIZE - 1,
-						&read, NULL);
-	rfcomm_start_watch(dev);
-	if (G_IO_STATUS_NORMAL == status) {
-		buf[read] = '\0';
-		if (strstr(buf, "+COPS")) {
-			if (!strrchr(buf, ','))
-				result = "0";
-			else {
-				result = strchr(buf, '\"') + 1;
-				sep = strchr(result, '\"');
-				sep[0] = '\0';
-			}
-
-			reply = dbus_message_new_method_return(msg);
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&result, DBUS_TYPE_INVALID);
-		} else {
-			info("ag_get_operator(): '+COPS' expected but"
-				" '%s' received", buf);
-			reply = dbus_message_new_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Unexpected response from AG");
-		}
-	} else {
-		error("ag_get_operator(): %m");
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".ConnectionFailed",
-					"Failed to receive response from AG");
-	}
-
-	return reply;
-}
-
-#define AG_SEND_DTMF "AT+VTS=%c\r"
-static DBusMessage *ag_send_dtmf(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint com_len;
-	gboolean got_ok = TRUE;
-	gint num_len;
-	gint i = 0;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	num_len = strlen(number);
-	rfcomm_stop_watch(device);
-	while (i < num_len && got_ok) {
-		com_len = sprintf(buf, AG_SEND_DTMF, number[i]);
-		rfcomm_send_and_read(gw, buf, buf, com_len);
-		got_ok = NULL != strstr(buf, OK_RESPONSE);
-		i += 1;
-	}
-	rfcomm_start_watch(device);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_GET_SUBSCRIBER_NUMS "AT+CNUM\r"
-#define CNUM_LEN 5             /* length of "+CNUM" string */
-#define MAX_NUMBER_CNT 16
-static DBusMessage *ag_get_subscriber_num(DBusConnection *conn,
-					DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number, *end;
-	DBusMessage *reply = dbus_message_new_method_return(msg);
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, AG_GET_SUBSCRIBER_NUMS, buf,
-			strlen(AG_GET_SUBSCRIBER_NUMS));
-	rfcomm_start_watch(device);
-
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 21)
-		error("ag_get_subscriber_num(): buf is too long '%s'", buf);
-	else if (strstr(buf, "+CNUM")) {
-		number = strchr(buf, ',');
-		number++;
-		end = strchr(number, ',');
-		if (end) {
-			*end = '\0';
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&number, DBUS_TYPE_INVALID);
-		}
-	} else
-		error("ag_get_subscriber_num(): read wrong data '%s'", buf);
-
-	return reply;
-}
-
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	DBusMessage *reply;
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	gboolean value;
-	guint index = 0;
-	struct indicator *ind;
-
-	reply = dbus_message_new_method_return(msg);
-	if (!reply)
-		return NULL;
-
-	dbus_message_iter_init_append(reply, &iter);
-
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
-	/* Connected */
-	value = gateway_is_connected(device);
-	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
-
-	if (!value)
-		goto done;
-
-	while ((ind = g_slist_nth_data(gw->indies, index))) {
-		if(!strcmp(ind->descr, "\"service\""))
-			dict_append_entry(&dict, "RegistrationStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"signal\""))
-			dict_append_entry(&dict, "SignalStrength",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"roam\""))
-			dict_append_entry(&dict, "RoamingStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"battchg\""))
-			dict_append_entry(&dict, "BatteryCharge",
-					DBUS_TYPE_UINT16, &ind->value);
-		index++;
-	}
-
-	/* SpeakerGain */
-	dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16,
-				&device->gateway->sp_gain);
-
-	/* MicrophoneGain */
-	dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16,
-				&device->gateway->mic_gain);
-done:
-	dbus_message_iter_close_container(&iter, &dict);
-	return reply;
+	return NULL;
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "Disconnect", "", "", ag_disconnect },
-	{ "AnswerCall", "", "", ag_answer },
-	{ "TerminateCall", "", "", ag_terminate_call },
-	{ "Call", "s", "", ag_call },
-	{ "GetOperatorName", "", "s", ag_get_operator },
-	{ "SendDTMF", "s", "", ag_send_dtmf },
-	{ "GetSubscriberNumber", "", "s", ag_get_subscriber_num },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
 	{ NULL, NULL, NULL, NULL }
 };
 
 static GDBusSignalTable gateway_signals[] = {
-	{ "Ring", "s" },
-	{ "CallTerminated", "" },
-	{ "CallStarted", "" },
-	{ "CallEnded", "" },
 	{ "PropertyChanged", "sv" },
 	{ NULL, NULL }
 };
@@ -1063,9 +315,6 @@ struct gateway *gateway_init(struct audio_device *dev)
 
 	debug("in gateway_init, dev is %p", dev);
 	gw = g_new0(struct gateway, 1);
-	gw->indies = NULL;
-	gw->is_dialing = FALSE;
-	gw->call_active = FALSE;
 	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
 
@@ -1107,11 +356,6 @@ void gateway_start_service(struct audio_device *device)
 	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
 }
 
-static void indicator_slice_free(gpointer mem)
-{
-	g_slice_free(struct indicator, mem);
-}
-
 int gateway_close(struct audio_device *device)
 {
 	struct gateway *gw = device->gateway;
@@ -1119,8 +363,6 @@ int gateway_close(struct audio_device *device)
 	GIOChannel *sco = gw->sco;
 	gboolean value = FALSE;
 
-	g_slist_foreach(gw->indies, (GFunc) indicator_slice_free, NULL);
-	g_slist_free(gw->indies);
 	if (rfcomm) {
 		g_io_channel_shutdown(rfcomm, TRUE, NULL);
 		g_io_channel_unref(rfcomm);
-- 
1.6.4.4


[-- Attachment #3: 0002-Implement-HandsfreeGateway-Interface.patch --]
[-- Type: application/octet-stream, Size: 24074 bytes --]

From 0755bce0e732a73a7d1d68772aa2090603003784 Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 8 Jan 2010 17:38:21 -0200
Subject: [PATCH 2/2] Implement HandsfreeGateway Interface

Create a interface where a Handsfree agent can register and use BlueZ to
handle the rfcomm and sco links.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for his
prototype on this code.
---
 audio/gateway.c |  442 +++++++++++++++++++++++++++++++++++++++++++------------
 audio/gateway.h |   12 +-
 audio/manager.c |   20 +--
 audio/unix.c    |    8 +
 doc/hfp-api.txt |   81 ++++++++++
 5 files changed, 453 insertions(+), 110 deletions(-)
 create mode 100644 doc/hfp-api.txt

diff --git a/audio/gateway.c b/audio/gateway.c
index 3dc09ff..d927a6c 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -5,6 +5,8 @@
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
  *  Copyright (C) 2008-2009  Leonid Movshovich <event.riga@gmail.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
+ *  Copyright (C) 2010  Gustavo F. Padovan <padovan@profusion.mobi>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -32,12 +34,15 @@
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
 
 #include <glib.h>
 #include <dbus/dbus.h>
 #include <gdbus.h>
 
 #include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
 #include <bluetooth/sco.h>
@@ -52,19 +57,100 @@
 #include "btio.h"
 #include "dbus-common.h"
 
-#define RFCOMM_BUF_SIZE 256
+#define MAX_OPEN_TRIES 5
+#define OPEN_WAIT 300
+
+struct agent {
+	char *name;	/* Bus id */
+	char *path;	/* D-Bus path */
+	guint watch;	/* Disconnect watch */
+};
 
 struct gateway {
 	gateway_state_t state;
-	GIOChannel *rfcomm;
-	guint rfcomm_watch_id;
+	int channel;
+	GIOChannel *rfcomm;	/* remote AG requested rfcomm connection */
 	GIOChannel *sco;
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
-	DBusMessage *connect_message;
+	struct agent *agent;
+	DBusMessage *msg;
 };
 
-int gateway_close(struct audio_device *device);
+int gateway_close(gpointer data);
+
+static const char *connected2str(int i)
+{
+	switch (i) {
+		case GATEWAY_STATE_DISCONNECTED:
+			return "disconnected";
+		case GATEWAY_STATE_CONNECTING:
+			return "connecting";
+		case GATEWAY_STATE_CONNECTED:
+			return "connected";
+		case GATEWAY_STATE_PLAYING:
+			return "playing";
+		default:
+			return "";
+	}
+}
+
+static void agent_free(struct agent *agent)
+{
+	if (!agent)
+		return;
+
+	g_free(agent->name);
+	g_free(agent->path);
+	g_free(agent);
+}
+
+static void change_state(struct audio_device *dev, int new_state)
+{
+	struct gateway *gw = dev->gateway;
+	const char *val = connected2str(new_state);
+
+	gw->state = new_state;
+
+	emit_property_changed(dev->conn, dev->path,
+			AUDIO_GATEWAY_INTERFACE, "State",
+			DBUS_TYPE_STRING, &val);
+}
+
+static void agent_disconnect(struct audio_device *dev, struct agent *agent)
+{
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "Release");
+
+	dbus_message_set_no_reply(msg, TRUE);
+	g_dbus_send_message(dev->conn, msg);
+
+	g_dbus_remove_watch(dev->conn, agent->watch);
+}
+
+static gboolean agent_sendfd(struct agent *agent, int fd,
+		DBusPendingCallNotifyFunction notify, void *data)
+{
+	struct audio_device *dev = data;
+	DBusMessage *msg;
+	DBusPendingCall *call;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "NewConnection");
+
+	dbus_message_append_args(msg, DBUS_TYPE_UNIX_FD, &fd,
+					DBUS_TYPE_INVALID);
+
+	if (dbus_connection_send_with_reply(dev->conn, msg, &call, -1) == FALSE)
+		return FALSE;
+
+	dbus_pending_call_set_notify(call, notify, dev->gateway, NULL);
+	dbus_pending_call_unref(call);
+
+	return TRUE;
+}
 
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
@@ -79,6 +165,7 @@ static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 		g_io_channel_shutdown(gw->sco, TRUE, NULL);
 		g_io_channel_unref(gw->sco);
 		gw->sco = NULL;
+		change_state(dev, GATEWAY_STATE_CONNECTED);
 		return FALSE;
 	}
 
@@ -92,63 +179,105 @@ static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
 
 	debug("at the begin of sco_connect_cb() in gateway.c");
 
+	gw->sco = chan;
+	g_io_channel_ref(chan);
+
 	if (err) {
 		error("sco_connect_cb(): %s", err->message);
-		/* not sure, but from other point of view,
-		 * what is the reason to have headset which
-		 * cannot play audio? */
-		if (gw->sco_start_cb)
-			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 		gateway_close(dev);
 		return;
 	}
 
-	gw->sco = g_io_channel_ref(chan);
 	if (gw->sco_start_cb)
 		gw->sco_start_cb(dev, gw->sco_start_cb_data);
 
-	/* why is this here? */
 	fcntl(g_io_channel_unix_get_fd(chan), F_SETFL, 0);
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
 				(GIOFunc) sco_io_cb, dev);
 }
 
+static void newconnection_reply(DBusPendingCall *call, void *data)
+{
+	struct gateway *gw = data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError derr;
+
+	if (!gw->rfcomm) {
+		debug("RFCOMM disconnected from server before agent reply");
+		return;
+	}
+
+	dbus_error_init(&derr);
+	if (!dbus_set_error_from_message(&derr, reply)) {
+		info("Agent reply: file descriptor passed successfuly");
+		return;
+	}
+
+	debug("Agent reply: %s", derr.message);
+
+	dbus_error_free(&derr);
+	g_idle_add(gateway_close, gw);
+}
+
 static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 				gpointer user_data)
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	gchar gw_addr[18];
-	GIOFlags flags;
+	struct rfcomm_dev_req req;
+	DBusMessage *reply;
+	int sk;
+	char src[18], dst[18];
 
 	if (err) {
 		error("connect(): %s", err->message);
 		if (gw->sco_start_cb)
 			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
-		return;
+		goto fail;
 	}
 
-	ba2str(&dev->dst, gw_addr);
-	/* Blocking mode should be default, but just in case: */
-	flags = g_io_channel_get_flags(chan);
-	flags &= ~G_IO_FLAG_NONBLOCK;
-	flags &= G_IO_FLAG_MASK;
-	g_io_channel_set_flags(chan, flags, NULL);
-	g_io_channel_set_encoding(chan, NULL, NULL);
-	g_io_channel_set_buffered(chan, FALSE);
-	if (!gw->rfcomm)
-		gw->rfcomm = g_io_channel_ref(chan);
+	if (!gw->agent)
+		error("Handfree Agent not registered");
 
-	if (NULL != gw->sco_start_cb)
-		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
+	ba2str(&dev->src, src);
+	ba2str(&dev->dst, dst);
 
-	gateway_close(dev);
+	/* make /dev/rfcomm serial port from chan */
+	memset(&req, 0, sizeof(req));
+	req.dev_id = -1;
+	req.flags = (1 << RFCOMM_REUSE_DLC);
+	bacpy(&req.src, &dev->src);
+	bacpy(&req.dst, &dev->dst);
+	req.channel = gw->channel;
+
+	sk = g_io_channel_unix_get_fd(chan);
+
+	gw->rfcomm = chan;
+	g_io_channel_ref(chan);
+
+	change_state(dev, GATEWAY_STATE_CONNECTED);
+
+	if (!agent_sendfd(gw->agent, sk, newconnection_reply, dev)) {
+		reply = g_dbus_create_error(gw->msg, ERROR_INTERFACE ".Failed",
+				"Can not pass file descriptor");
+	} else
+		reply = dbus_message_new_method_return(gw->msg);
+
+	return;
+
+fail:
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".Failed", ".Connection attempt failed");
+	}
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
+	return;
 }
 
 static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 {
 	struct audio_device *dev = user_data;
-	DBusMessage *msg = dev->gateway->connect_message;
+	struct gateway *gw = dev->gateway;
 	int ch = -1;
 	sdp_list_t *protos, *classes;
 	uuid_t uuid;
@@ -182,8 +311,6 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 
 	if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
 			uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
-		sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free,
-									NULL);
 		sdp_list_free(protos, NULL);
 		error("Invalid service record or not HFP");
 		goto fail;
@@ -197,6 +324,8 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 		goto fail;
 	}
 
+	gw->channel = ch;
+
 	io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -204,25 +333,24 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 				BT_IO_OPT_INVALID);
 	if (!io) {
 		error("Unable to connect: %s", err->message);
-		if (msg) {
-			error_common_reply(dev->conn, msg, ERROR_INTERFACE
-						".ConnectionAttemptFailed",
-						err->message);
-			msg = NULL;
-		}
-		g_error_free(err);
+		if (err)
+			g_error_free(err);
 		gateway_close(dev);
+		goto fail;
 	}
 
 	g_io_channel_unref(io);
+
+	change_state(dev, GATEWAY_STATE_CONNECTING);
 	return;
 
 fail:
-	if (msg)
-		error_common_reply(dev->conn, msg, ERROR_INTERFACE
-					".NotSupported", "Not supported");
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".NotSupported", "Not supported");
+	}
 
-	dev->gateway->connect_message = NULL;
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
 
 	sco_cb = dev->gateway->sco_start_cb;
 	if (sco_cb)
@@ -243,22 +371,56 @@ static DBusMessage *ag_connect(DBusConnection *conn, DBusMessage *msg,
 {
 	struct audio_device *au_dev = (struct audio_device *) data;
 	struct gateway *gw = au_dev->gateway;
+	DBusMessage *reply;
 
 	debug("at the begin of ag_connect()");
-	if (gw->rfcomm)
+
+	if (!gw->agent)
 		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".AlreadyConnected",
-					"Already Connected");
+				".Failed", "Agent not assined");
 
-	gw->connect_message = dbus_message_ref(msg);
 	if (get_records(au_dev) < 0) {
-		dbus_message_unref(gw->connect_message);
 		return g_dbus_create_error(msg, ERROR_INTERFACE
 					".ConnectAttemptFailed",
 					"Connect Attempt Failed");
 	}
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
 	debug("at the end of ag_connect()");
-	return NULL;
+
+	return reply;
+}
+
+int gateway_close(gpointer data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	gboolean value = FALSE;
+
+	if (gw->rfcomm) {
+		g_io_channel_shutdown(gw->rfcomm, TRUE, NULL);
+		g_io_channel_unref(gw->rfcomm);
+		gw->rfcomm = NULL;
+	}
+
+	if (gw->sco) {
+		g_io_channel_shutdown(gw->sco, TRUE, NULL);
+		g_io_channel_unref(gw->sco);
+		gw->sco = NULL;
+		gw->sco_start_cb = NULL;
+		gw->sco_start_cb_data = NULL;
+	}
+
+	gw->state = GATEWAY_STATE_DISCONNECTED;
+
+	emit_property_changed(device->conn, device->path,
+				AUDIO_GATEWAY_INTERFACE,
+				"Connected", DBUS_TYPE_BOOLEAN, &value);
+
+	return 0;
 }
 
 static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
@@ -269,6 +431,9 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	DBusMessage *reply = NULL;
 	char gw_addr[18];
 
+	if (!device->conn)
+		return NULL;
+
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
 		return NULL;
@@ -279,22 +444,128 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 						"Device not Connected");
 
 	gateway_close(device);
+
 	ba2str(&device->dst, gw_addr);
 	debug("Disconnected from %s, %s", gw_addr, device->path);
 
 	return reply;
 }
 
+static void agent_exited(DBusConnection *conn, void *data)
+{
+	struct gateway *gateway = data;
+	struct agent *agent = gateway->agent;
+
+	debug("Agent %s exited", agent->name);
+
+	agent_free(agent);
+	gateway->agent = NULL;
+}
+
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	return NULL;
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	const char *value;
+
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	value = connected2str(gw->state);
+	dict_append_entry(&dict, "Connected",
+			DBUS_TYPE_STRING, &value);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct agent *agent;
+	const char *path, *name;
+
+
+	if (gw->agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".AlreadyExists",
+				"Agent already exists");
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+			DBUS_TYPE_INVALID))
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+				".InvalidArguments", "Invalid argument");
+
+	name = dbus_message_get_sender(msg);
+	agent = g_new0(struct agent, 1);
+	if (!agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed",
+				"Failed to create a new agent");
+
+	agent->name = strdup(name);
+	agent->path = strdup(path);
+
+	agent->watch = g_dbus_add_disconnect_watch(conn, name,
+			agent_exited, gw, NULL);
+
+	gw->agent = agent;
+
+	gw->msg = dbus_message_ref(msg);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+				DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct agent *agent;
+
+	if (!gw->agent)
+		goto done;
+
+	agent = gw->agent;
+	if (strcmp(agent->name, dbus_message_get_sender(msg)) != 0)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed", "Permission denied");
+
+	agent_disconnect(device, gw->agent);
+
+	g_dbus_remove_watch(conn, agent->watch);
+
+	dbus_message_unref(gw->msg);
+
+	agent_free(agent);
+	gw->agent = NULL;
+
+done:
+	return dbus_message_new_method_return(msg);
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
-	{ "Disconnect", "", "", ag_disconnect },
+	{ "Disconnect", "", "", ag_disconnect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
+	{ "RegisterAgent", "o", "", register_agent },
+	{ "UnregisterAgent", "o", "", unregister_agent },
 	{ NULL, NULL, NULL, NULL }
 };
 
@@ -307,17 +578,15 @@ struct gateway *gateway_init(struct audio_device *dev)
 {
 	struct gateway *gw;
 
+	gw = g_new0(struct gateway, 1);
+
 	if (!g_dbus_register_interface(dev->conn, dev->path,
 					AUDIO_GATEWAY_INTERFACE,
 					gateway_methods, gateway_signals,
 					NULL, dev, NULL))
 		return NULL;
 
-	debug("in gateway_init, dev is %p", dev);
-	gw = g_new0(struct gateway, 1);
-	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
-
 }
 
 gboolean gateway_is_connected(struct audio_device *dev)
@@ -326,17 +595,6 @@ gboolean gateway_is_connected(struct audio_device *dev)
 			dev->gateway->state == GATEWAY_STATE_CONNECTED);
 }
 
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
-{
-	if (!io)
-		return -EINVAL;
-
-	g_io_channel_ref(io);
-	dev->gateway->rfcomm = io;
-
-	return 0;
-}
-
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 {
 	struct gateway *gw = dev->gateway;
@@ -347,42 +605,39 @@ int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 	gw->sco = g_io_channel_ref(io);
 
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                                (GIOFunc) sco_io_cb, dev);
+				 (GIOFunc) sco_io_cb, dev);
+
+	change_state(dev, GATEWAY_STATE_PLAYING);
+
 	return 0;
 }
 
-void gateway_start_service(struct audio_device *device)
+int gateway_connect_rfcomm(struct audio_device *dev,
+		GIOChannel *chan, int ch)
 {
-	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
+	if (!chan)
+		return -EINVAL;
+
+	g_io_channel_ref(chan);
+	dev->gateway->rfcomm = chan;
+	dev->gateway->channel = ch;
+
+	return 0;
 }
 
-int gateway_close(struct audio_device *device)
+void gateway_start_service(struct audio_device *dev)
 {
-	struct gateway *gw = device->gateway;
-	GIOChannel *rfcomm = gw->rfcomm;
-	GIOChannel *sco = gw->sco;
-	gboolean value = FALSE;
+	struct gateway *gw = dev->gateway;
+	GError *err = NULL;
 
-	if (rfcomm) {
-		g_io_channel_shutdown(rfcomm, TRUE, NULL);
-		g_io_channel_unref(rfcomm);
-		gw->rfcomm = NULL;
-	}
+	if (gw->rfcomm == NULL)
+		return;
 
-	if (sco) {
-		g_io_channel_shutdown(sco, TRUE, NULL);
-		g_io_channel_unref(sco);
-		gw->sco = NULL;
-		gw->sco_start_cb = NULL;
-		gw->sco_start_cb_data = NULL;
+	if (!bt_io_accept(gw->rfcomm, rfcomm_connect_cb, dev, NULL,
+							&err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
 	}
-
-	gw->state = GATEWAY_STATE_DISCONNECTED;
-
-	emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN, &value);
-	return 0;
 }
 
 /* These are functions to be called from unix.c for audio system
@@ -399,8 +654,12 @@ gboolean gateway_request_stream(struct audio_device *dev,
 		gw->sco_start_cb_data = user_data;
 		get_records(dev);
 	} else if (!gw->sco) {
+		char source[128], destination[128];
+
 		gw->sco_start_cb = cb;
 		gw->sco_start_cb_data = user_data;
+		ba2str(&dev->src, source);
+		ba2str(&dev->dst, destination);
 		io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -464,4 +723,3 @@ void gateway_suspend_stream(struct audio_device *dev)
 	gw->sco_start_cb = NULL;
 	gw->sco_start_cb_data = NULL;
 }
-
diff --git a/audio/gateway.h b/audio/gateway.h
index 3b0457f..eaf6801 100644
--- a/audio/gateway.h
+++ b/audio/gateway.h
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -22,20 +23,25 @@
  *
  */
 
-#define AUDIO_GATEWAY_INTERFACE "org.bluez.HeadsetGateway"
+#define AUDIO_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
 
 #define DEFAULT_HSP_HS_CHANNEL 6
 #define DEFAULT_HFP_HS_CHANNEL 7
 
 typedef enum {
 	GATEWAY_STATE_DISCONNECTED,
-	GATEWAY_STATE_CONNECTED
+	GATEWAY_STATE_CONNECTED,
+	GATEWAY_STATE_CONNECTING,
+	GATEWAY_STATE_PLAYING,
 } gateway_state_t;
 
 typedef void (*gateway_stream_cb_t) (struct audio_device *dev, void *user_data);
+
 struct gateway *gateway_init(struct audio_device *device);
+void gateway_exit();
 gboolean gateway_is_connected(struct audio_device *dev);
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *chan);
+int gateway_connect_rfcomm(struct audio_device *dev,
+			GIOChannel *chan, int ch);
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *chan);
 void gateway_start_service(struct audio_device *device);
 gboolean gateway_request_stream(struct audio_device *dev,
diff --git a/audio/manager.c b/audio/manager.c
index 413c1f3..3ac1a60 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -198,7 +198,7 @@ static void handle_uuid(const char *uuidstr, struct audio_device *device)
 		break;
 	case HANDSFREE_AGW_SVCLASS_ID:
 		debug("Found Handsfree AG record");
-		if (device->gateway == NULL)
+		if (enabled.gateway && (device->gateway == NULL))
 			device->gateway = gateway_init(device);
 		break;
 	case AUDIO_SINK_SVCLASS_ID:
@@ -567,8 +567,8 @@ static void hf_io_cb(GIOChannel *chan, gpointer data)
 		return;
 	}
 
-	server_uuid = HFP_HS_UUID;
-	remote_uuid = HFP_AG_UUID;
+	server_uuid = HFP_AG_UUID;
+	remote_uuid = HFP_HS_UUID;
 	svclass = HANDSFREE_AGW_SVCLASS_ID;
 
 	device = manager_get_device(&src, &dst, TRUE);
@@ -586,7 +586,7 @@ static void hf_io_cb(GIOChannel *chan, gpointer data)
 		goto drop;
 	}
 
-	if (gateway_connect_rfcomm(device, chan) < 0) {
+	if (gateway_connect_rfcomm(device, chan, ch) < 0) {
 		error("Allocating new GIOChannel failed!");
 		goto drop;
 	}
@@ -905,22 +905,12 @@ static void headset_server_remove(struct btd_adapter *adapter)
 static int gateway_server_probe(struct btd_adapter *adapter)
 {
 	struct audio_adapter *adp;
-	const gchar *path = adapter_get_path(adapter);
-	int ret;
-
-	DBG("path %s", path);
 
 	adp = audio_adapter_get(adapter);
 	if (!adp)
 		return -EINVAL;
 
-	ret = gateway_server_init(adp);
-	if (ret < 0) {
-		audio_adapter_ref(adp);
-		return ret;
-	}
-
-	return 0;
+	return gateway_server_init(adp);
 }
 
 static void gateway_server_remove(struct btd_adapter *adapter)
diff --git a/audio/unix.c b/audio/unix.c
index 5cf4f94..bd1a415 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -395,6 +395,9 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	struct bt_start_stream_rsp *rsp = (void *) buf;
 	struct bt_new_stream_ind *ind = (void *) buf;
 
+	if (!dev)
+		goto failed;
+
 	memset(buf, 0, sizeof(buf));
 	rsp->h.type = BT_RESPONSE;
 	rsp->h.name = BT_START_STREAM;
@@ -416,6 +419,11 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	}
 
 	client->req_id = 0;
+	return;
+
+failed:
+	error("gateway_resume_complete: resume failed");
+	unix_ipc_error(client, BT_START_STREAM, EIO);
 }
 
 static void headset_suspend_complete(struct audio_device *dev, void *user_data)
diff --git a/doc/hfp-api.txt b/doc/hfp-api.txt
new file mode 100644
index 0000000..ed52be1
--- /dev/null
+++ b/doc/hfp-api.txt
@@ -0,0 +1,81 @@
+Gateway hierarchy
+========================
+
+Service		org.bluez
+Interface	org.bluez.HandsfreeGateway
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+This interface is available for remote devices which can function in the Audio
+Gateway role of the HFP profiles.  It is intended to be used with external
+telephony stacks / handlers of the HFP protocol.
+
+Methods		void Connect()
+
+			Connect to the AG service on the remote device.
+
+		void Disconnect()
+
+			Disconnect from the AG service on the remote device
+
+		dict GetProperties()
+
+			Returns all properties for the interface. See the
+			properties section for available properties.
+
+		void RegisterAgent(object path)
+
+			The object path defines the path the of the agent
+			that will be called when a new Handsfree connection
+			is established.
+			
+			If an application disconnects from the bus all of its
+			registered agents will be removed.
+
+		void UnregisterAgent(object path)
+
+			This unregisters the agent that has been previously
+			registered. The object path parameter must match the
+			same value that has been used on registration.
+
+Signals		PropertyChanged(string name, variant value)
+
+			This signal indicates a changed value of the given
+			property.
+
+Properties	string State [readonly]
+
+			Indicates the state of the connection.  Possible
+			values are:
+				"disconnected"
+				"connecting"
+				"connected"
+				"playing"
+
+HandsfreeAgent hierarchy
+===============
+
+Service         unique name
+Interface       org.bluez.HandsfreeAgent
+Object path     freely definable
+
+Methods		void NewConnection(filedescriptor fd)
+
+			This method gets called whenever a new handsfree
+			connection has been established.  The objectpath
+			contains the object path of the remote device.  This
+			method assumes that DBus daemon with file descriptor
+			passing capability is being used.
+
+			The agent should only return successfully once the
+			establishment of the service level connection (SLC)
+			has been completed.  In the case of Handsfree this
+			means that BRSF exchange has been performed and
+			necessary initialization has been done.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.Failed
+
+		void Release()
+
+			This method gets called whenever the service daemon
+			unregisters the agent.
-- 
1.6.4.4


[-- Attachment #4: 0001-Add-HFP-support-through-BlueZ.patch --]
[-- Type: application/octet-stream, Size: 14998 bytes --]

From 61f4d5df1d54e318eeb039bc048c22763ca3e20b Mon Sep 17 00:00:00 2001
From: Gustavo F. Padovan <padovan@profusion.mobi>
Date: Fri, 8 Jan 2010 17:25:43 -0200
Subject: [PATCH] Add HFP support through BlueZ

It uses BlueZ through to get HFP working following the
org.bluez.HandsfreeGateway and org.bluez.HandsfreeAgent from the BlueZ
D-Bus API.

You need the HFP patch into BlueZ and dbus 1.3.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for its prototype
on this code.
---
 drivers/hfpmodem/hfpmodem.h |    2 +
 plugins/hfp.c               |  419 +++++++++++++++++++++++++++++++++++++++++--
 plugins/modemconf.c         |    1 -
 3 files changed, 407 insertions(+), 15 deletions(-)

diff --git a/drivers/hfpmodem/hfpmodem.h b/drivers/hfpmodem/hfpmodem.h
index 509846b..5fee68f 100644
--- a/drivers/hfpmodem/hfpmodem.h
+++ b/drivers/hfpmodem/hfpmodem.h
@@ -63,11 +63,13 @@ enum hfp_indicator {
 
 struct hfp_data {
 	GAtChat *chat;
+	char *handsfree_path;
 	unsigned int ag_features;
 	unsigned int ag_mpty_features;
 	unsigned int hf_features;
 	unsigned char cind_pos[HFP_INDICATOR_LAST];
 	unsigned int cind_val[HFP_INDICATOR_LAST];
+	unsigned int at_timeout;
 };
 
 extern void hfp_netreg_init();
diff --git a/plugins/hfp.c b/plugins/hfp.c
index 3bbd922..42269f2 100644
--- a/plugins/hfp.c
+++ b/plugins/hfp.c
@@ -30,6 +30,8 @@
 #include <glib.h>
 #include <gatchat.h>
 #include <gattty.h>
+#include <gdbus.h>
+#include <ofono.h>
 
 #define OFONO_API_SUBJECT_TO_CHANGE
 #include <ofono/plugin.h>
@@ -52,11 +54,26 @@
 
 #include <drivers/hfpmodem/hfpmodem.h>
 
+#include <ofono/dbus.h>
+
+#define BLUEZ_SERVICE "org.bluez"
+#define BLUEZ_PATH "/"
+#define BLUEZ_MANAGER_INTERFACE "org.bluez.Manager"
+#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter"
+#define BLUEZ_DEVICE_INTERFACE "org.bluez.Device"
+#define BLUEZ_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
+
+#define HFP_AGENT_INTERFACE "org.bluez.HandsfreeAgent"
+
+#define HFP_AG_UUID	"0000111F-0000-1000-8000-00805F9B34FB"
+
 static const char *brsf_prefix[] = { "+BRSF:", NULL };
 static const char *cind_prefix[] = { "+CIND:", NULL };
 static const char *cmer_prefix[] = { "+CMER:", NULL };
 static const char *chld_prefix[] = { "+CHLD:", NULL };
 
+static DBusConnection *connection;
+
 static int hfp_disable(struct ofono_modem *modem);
 
 static void hfp_debug(const char *str, void *user_data)
@@ -135,6 +152,60 @@ static void cmer_cb(gboolean ok, GAtResult *result, gpointer user_data)
 		sevice_level_conn_established(modem);
 }
 
+static int send_method_call(const char *dest, const char *path,
+				const char *interface, const char *method,
+				DBusPendingCallNotifyFunction cb,
+				void *user_data, int type, ...)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+	va_list args;
+
+	msg = dbus_message_new_method_call(dest, path, interface, method);
+	if (!msg) {
+		ofono_error("Unable to allocate new D-Bus %s message", method);
+		return -ENOMEM;
+	}
+
+	va_start(args, type);
+
+	if (!dbus_message_append_args_valist(msg, type, args)) {
+		dbus_message_unref(msg);
+		va_end(args);
+		return -EIO;
+	}
+
+	va_end(args);
+
+	if (!cb) {
+		g_dbus_send_message(connection, msg);
+		return 0;
+	}
+
+	if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+		ofono_error("Sending %s failed", method);
+		dbus_message_unref(msg);
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(call, cb, user_data, NULL);
+	dbus_pending_call_unref(call);
+	dbus_message_unref(msg);
+
+	return 0;
+}
+
+static gboolean hfp_enable_timeout(gpointer user)
+{
+	struct ofono_modem *modem = user;
+
+	if (ofono_modem_get_powered(modem))
+		return FALSE;
+
+	hfp_disable(modem);
+	return FALSE;
+}
+
 static void cind_status_cb(gboolean ok, GAtResult *result,
 				gpointer user_data)
 {
@@ -260,8 +331,7 @@ error:
 }
 
 /* either oFono or Phone could request SLC connection */
-static int service_level_connection(struct ofono_modem *modem,
-				const char *tty)
+static int service_level_connection(struct ofono_modem *modem, int fd)
 {
 	struct hfp_data *data = ofono_modem_get_data(modem);
 	GIOChannel *io;
@@ -269,7 +339,7 @@ static int service_level_connection(struct ofono_modem *modem,
 	GAtChat *chat;
 	char buf[64];
 
-	io = g_at_tty_open(tty, NULL);
+	io = g_io_channel_unix_new(fd);
 	if (!io) {
 		ofono_error("Service level connection failed: %s (%d)",
 			strerror(errno), errno);
@@ -288,7 +358,6 @@ static int service_level_connection(struct ofono_modem *modem,
 		g_at_chat_set_debug(chat, hfp_debug, NULL);
 
 	sprintf(buf, "AT+BRSF=%d", data->hf_features);
-
 	g_at_chat_send(chat, buf, brsf_prefix,
 				brsf_cb, modem, NULL);
 	data->chat = chat;
@@ -296,10 +365,46 @@ static int service_level_connection(struct ofono_modem *modem,
 	return -EINPROGRESS;
 }
 
-static int hfp_probe(struct ofono_modem *modem)
+static DBusMessage *hfp_agent_new_connection(DBusConnection *conn, DBusMessage *msg, void *data)
 {
+	int fd;
+	struct ofono_modem *modem = data;
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UNIX_FD, &fd,
+				DBUS_TYPE_INVALID))
+		return __ofono_error_invalid_args(msg);
+
+	service_level_connection(modem, fd);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *hfp_agent_release(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	struct ofono_modem *modem = data;
+	struct hfp_data *hfp_data = ofono_modem_get_data(modem);
+
+	g_at_chat_shutdown(hfp_data->chat);
+
+	return NULL;
+}
+
+static GDBusMethodTable agent_methods[] = {
+        { "NewConnection", "h", "", hfp_agent_new_connection,
+		G_DBUS_METHOD_FLAG_ASYNC },
+	{ "Release", "", "", hfp_agent_release },
+	{NULL, NULL, NULL, NULL}
+};
+
+static int hfp_create_modem(const char *device)
+{
+	struct ofono_modem *modem;
 	struct hfp_data *data;
 
+	ofono_info("Using device: %s", device);
+
+	modem = ofono_modem_create(NULL, "hfp");
+
 	data = g_try_new0(struct hfp_data, 1);
 	if (!data)
 		return -ENOMEM;
@@ -310,36 +415,315 @@ static int hfp_probe(struct ofono_modem *modem)
 	data->hf_features |= HF_FEATURE_ENHANCED_CALL_STATUS;
 	data->hf_features |= HF_FEATURE_ENHANCED_CALL_CONTROL;
 
+	data->handsfree_path = g_strdup(device);
+
 	ofono_modem_set_data(modem, data);
 
+	ofono_modem_register(modem);
+
+	return 0;
+}
+
+static void parse_uuids(DBusMessageIter *i, const char *device)
+{
+	DBusMessageIter variant, ai;
+	const char *value;
+
+	dbus_message_iter_recurse(i, &variant);
+	dbus_message_iter_recurse(&variant, &ai);
+
+	while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
+		dbus_message_iter_get_basic(&ai, &value);
+		if (!strcasecmp(value, HFP_AG_UUID))
+			hfp_create_modem(device);
+
+		if (!dbus_message_iter_next(&ai))
+			return;
+	}
+}
+
+static void parse_get_properties(DBusMessage *reply, const char *device)
+{
+	DBusMessageIter arg, element, variant;
+	const char *key;
+
+	if (!dbus_message_iter_init(reply, &arg)) {
+		ofono_debug("GetProperties reply has no arguments.");
+		return;
+	}
+
+	if (dbus_message_iter_get_arg_type(&arg) != DBUS_TYPE_ARRAY) {
+		ofono_debug("GetProperties argument is not an array.");
+		return;
+	}
+
+	dbus_message_iter_recurse(&arg, &element);
+	while (dbus_message_iter_get_arg_type(&element) != DBUS_TYPE_INVALID) {
+		if (dbus_message_iter_get_arg_type(&element) ==
+				DBUS_TYPE_DICT_ENTRY) {
+			DBusMessageIter dict;
+
+			dbus_message_iter_recurse(&element, &dict);
+
+			if (dbus_message_iter_get_arg_type(&dict) !=
+					DBUS_TYPE_STRING) {
+				ofono_debug("Property name not a string.");
+				return;
+			}
+
+			dbus_message_iter_get_basic(&dict, &key);
+
+			if (!dbus_message_iter_next(&dict))  {
+				ofono_debug("Property value missing");
+				return;
+			}
+
+			if (dbus_message_iter_get_arg_type(&dict) !=
+					DBUS_TYPE_VARIANT) {
+				ofono_debug("Property value not a variant.");
+				return;
+			}
+
+			if (!strcmp(key, "UUIDs"))
+				parse_uuids(&dict, device);
+
+			dbus_message_iter_recurse(&dict, &variant);
+		}
+
+		if (!dbus_message_iter_next(&element))
+			return;
+	}
+}
+
+static void get_properties_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	const char *device = user_data;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	if (dbus_message_is_error(reply, DBUS_ERROR_SERVICE_UNKNOWN)) {
+		ofono_debug("Bluetooth daemon is apparently not available.");
+		goto done;
+	}
+
+	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+		if (!dbus_message_is_error(reply, DBUS_ERROR_UNKNOWN_METHOD))
+			ofono_info("Error from GetProperties reply: %s",
+					dbus_message_get_error_name(reply));
+
+		goto done;
+	}
+
+	parse_get_properties(reply, device);
+
+done:
+	dbus_message_unref(reply);
+}
+
+static void list_devices_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	struct ofono_modem *modem = user_data;
+	const char **device = NULL;
+	int num, ret, i;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	if (dbus_message_is_error(reply, DBUS_ERROR_SERVICE_UNKNOWN)) {
+		ofono_debug("Bluetooth daemon is apparently not available.");
+		goto done;
+	}
+
+	dbus_error_init(&err);
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_ARRAY,
+				DBUS_TYPE_OBJECT_PATH, &device,
+				&num, DBUS_TYPE_INVALID) == FALSE) {
+		if (device == NULL) {
+			dbus_error_free(&err);
+			goto done;
+		}
+
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s", err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	for (i = 0 ; i < num ; i++) {
+		ret = send_method_call(BLUEZ_SERVICE, device[i],
+				BLUEZ_DEVICE_INTERFACE, "GetProperties",
+				get_properties_cb, (void *)device[i],
+				DBUS_TYPE_INVALID);
+		if (ret < 0)
+			ofono_error("GetProperties failed(%d)", ret);
+	}
+
+done:
+	dbus_message_unref(reply);
+}
+
+static void list_adapters_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	const char **adapter = NULL;
+	int num, ret, i;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	if (dbus_message_is_error(reply, DBUS_ERROR_SERVICE_UNKNOWN)) {
+		ofono_debug("Bluetooth daemon is apparently not available.");
+		goto done;
+	}
+
+	dbus_error_init(&err);
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_ARRAY,
+				DBUS_TYPE_OBJECT_PATH, &adapter,
+				&num, DBUS_TYPE_INVALID) == FALSE) {
+		if (adapter == NULL) {
+			dbus_error_free(&err);
+			goto done;
+		}
+
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s", err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	for (i = 0 ; i < num ; i++) {
+		ret = send_method_call(BLUEZ_SERVICE, adapter[i],
+				BLUEZ_ADAPTER_INTERFACE, "ListDevices",
+				list_devices_cb, NULL,
+				DBUS_TYPE_INVALID);
+
+		if (ret < 0)
+			ofono_error("ListDevices failed(%d)", ret);
+	}
+
+done:
+	dbus_message_unref(reply);
+
+}
+
+static int hfp_load_modems()
+{
+	return send_method_call(BLUEZ_SERVICE, BLUEZ_PATH,
+				BLUEZ_MANAGER_INTERFACE, "ListAdapters",
+				list_adapters_cb, NULL,
+				DBUS_TYPE_INVALID);
+}
+
+static int hfp_register_ofono_handsfree(struct ofono_modem *modem)
+{
+	const char *obj_path = ofono_modem_get_path(modem);
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	ofono_debug("Registering oFono Agent to bluetooth daemon");
+
+	if (!data->handsfree_path)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "RegisterAgent",
+				NULL, NULL, DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+}
+
+static int hfp_unregister_ofono_handsfree(struct ofono_modem *modem)
+{
+	const char *obj_path = ofono_modem_get_path(modem);
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	ofono_debug("Unregistering oFono Agent from bluetooth daemon");
+
+	if (!data->handsfree_path)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "UnregisterAgent",
+				NULL, NULL, DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+}
+
+static int hfp_probe(struct ofono_modem *modem)
+{
+	struct hfp_data *data;
+	const char *obj_path;
+
+	obj_path = ofono_modem_get_path(modem);
+
+	g_dbus_register_interface(connection, obj_path, HFP_AGENT_INTERFACE,
+			agent_methods, NULL, NULL, modem, NULL);
+
+	if (hfp_register_ofono_handsfree(modem) != 0)
+		return -EINVAL;
+
 	return 0;
 }
 
 static void hfp_remove(struct ofono_modem *modem)
 {
-	gpointer data = ofono_modem_get_data(modem);
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	hfp_unregister_ofono_handsfree(modem);
+
+	if (data->handsfree_path)
+		g_free(data->handsfree_path);
 
 	if (data)
 		g_free(data);
 
+	dbus_connection_unref(connection);
+
 	ofono_modem_set_data(modem, NULL);
 }
 
+static int hfp_connect_ofono_handsfree(struct ofono_modem *modem)
+{
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	ofono_debug("Connect to bluetooth daemon");
+
+	if (!data->handsfree_path || !connection)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Connect",
+				NULL, NULL, DBUS_TYPE_INVALID);
+}
+
 /* power up hardware */
 static int hfp_enable(struct ofono_modem *modem)
 {
-	const char *tty;
-	int ret;
+	struct hfp_data *data = ofono_modem_get_data(modem);
 
 	DBG("%p", modem);
 
-	tty = ofono_modem_get_string(modem, "Device");
-	if (tty == NULL)
+	data->at_timeout =
+		g_timeout_add_seconds(10, hfp_enable_timeout, modem);
+
+	if (hfp_connect_ofono_handsfree(modem) < 0)
 		return -EINVAL;
 
-	ret = service_level_connection(modem, tty);
+	return -EINPROGRESS;
+}
+
+static int hfp_disconnect_ofono_handsfree(struct ofono_modem *modem)
+{
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	if (!data->handsfree_path || !connection)
+		return -EINVAL;
 
-	return ret;
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Disconnect",
+				NULL, NULL, DBUS_TYPE_INVALID);
 }
 
 static int hfp_disable(struct ofono_modem *modem)
@@ -359,6 +743,7 @@ static int hfp_disable(struct ofono_modem *modem)
 
 	ofono_modem_set_powered(modem, FALSE);
 
+	hfp_disconnect_ofono_handsfree(modem);
 	return 0;
 }
 
@@ -390,8 +775,14 @@ static struct ofono_modem_driver hfp_driver = {
 
 static int hfp_init(void)
 {
-	DBG("");
-	return ofono_modem_driver_register(&hfp_driver);
+	DBusConnection *conn = ofono_dbus_get_connection();
+
+	ofono_modem_driver_register(&hfp_driver);
+
+	connection = dbus_connection_ref(conn);
+	hfp_load_modems();
+
+	return 0;
 }
 
 static void hfp_exit(void)
diff --git a/plugins/modemconf.c b/plugins/modemconf.c
index c8c261f..932d610 100644
--- a/plugins/modemconf.c
+++ b/plugins/modemconf.c
@@ -129,7 +129,6 @@ static struct ofono_modem *create_modem(GKeyFile *keyfile, const char *group)
 
 	if (!g_strcmp0(driver, "atgen") || !g_strcmp0(driver, "g1") ||
 						!g_strcmp0(driver, "calypso") ||
-						!g_strcmp0(driver, "hfp") ||
 						!g_strcmp0(driver, "palmpre"))
 		set_device(modem, keyfile, group);
 
-- 
1.6.4.4


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

* Re: [RFC] HFP support into oFono and BlueZ
  2010-01-21  7:54         ` Zhao Forrest
  (?)
@ 2010-01-21  2:56         ` Denis Kenzior
  -1 siblings, 0 replies; 51+ messages in thread
From: Denis Kenzior @ 2010-01-21  2:56 UTC (permalink / raw)
  To: Zhao Forrest; +Cc: Gustavo F. Padovan, linux-bluetooth, ofono, zhenhua.zhang

Hi Forrest,

> Your patch removes many DBus APIs provided by audio/gateway.c, which
> was originally designed to provide a set of DBus functions/signals for
> ease of application development. In other words, your patch is too
> oFono-specific, and is not a general support for HFP HF unit role in
> BlueZ IMO. The DBus interface provided by your patch is not easy to
> develop the application because the application developer has to parse
> the AT commands by himself. I know that oFono could parse AT commands.

That is actually the point :)

> But I don't think it a common case for other application developers.
> Why not extend audio/gateway.c instead of removing its DBus APIs or
> create a new file to support HFP for oFono in blueZ?

If we were to extend the current BlueZ API we'd be duplicating all of what 
oFono has already solved (particularly 3-way calls, multiparty and other 
advanced features) and force the developers to support yet-another DBus API.  
This makes no sense and oFono already supports the full HFP protocol along 
with most of the nasty corner cases.  This in fact simplifies things quite a 
bit...

Regards,
-Denis

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

* RE: [RFC] HFP support into oFono and BlueZ
  2010-01-20 19:58       ` Gustavo F. Padovan
@ 2010-01-21  6:28         ` Zhang, Zhenhua
  -1 siblings, 0 replies; 51+ messages in thread
From: Zhang, Zhenhua @ 2010-01-21  6:28 UTC (permalink / raw)
  To: Gustavo F. Padovan; +Cc: linux-bluetooth, ofono

SGkgUGFkb3ZhbiwNCg0KR3VzdGF2byBGLiBQYWRvdmFuIHdyb3RlOg0KPj4gTmV3IHZlcnNpb24g
b2YgdGhlIHBhdGNoZXMuIEtub3duIGlzc3VlczoNCj4+ICInb3JnLmJsdWV6LkhhbmRzZnJlZUFn
ZW50LlJlbGVhc2UnIHBhcnQgbm90IGltcGxlbWVudGVkIiBhbmQgaXQncw0KPj4gbm90IHdvcmtp
bmcgd2l0aCBtb3JlIHRoYW4gb25lIGJsdWV0b290aCBkZXZpY2VzIGluIHNvbWUgY2FzZXMuDQo+
PiBDb21tZW50cyBhcmUgd2VsY29tZS4gDQo+PiANCj4gDQo+IEFub3RoZXIgbmV3IHZlcnNpb24g
b2YgdGhlIHBhdGNoZXMuIE5vdyBvZm9ubyBxdWVyeSBhbGwgYmx1ZXRvb3RoDQo+IGRldmljZXMg
YW5kIHNlYXJjaGVzIGZvciB0aGUgb25lcyB3aXRoIEdhdGV3YXkgaW50ZXJmYWNlLiBCbHVleiBo
YXMNCj4gc29tZSBmaXhlcyBhbmQgbm93IHN1cHBvcnRzIGNvbm5lY3Rpb24gY29taW5nIGZyb20g
dGhlIEFHLiANCj4gVGhlIHBhdGNoZXMgYXJlIHVzaW5nIGEgcGVyLWRldmljZSBhZ2VudCBhcyBk
ZXNjcmliZWQgYnkgdGhlIGxhc3QNCj4gdmVyc2lvbiBvZiBIYW5kc2ZyZWUgQVBJLiANCj4gDQo+
IEkgZGlkIG5vdCBpbXBsZW1lbnRlZCB5ZXQgJ29yZy5ibHVlei5IYW5kc2ZyZWVBZ2VudC5SZWxl
YXNlJy4NCj4gDQo+IENvbW1lbnRzIGFyZSB3ZWxjb21lLiA6KQ0KDQpUd28gc21hbGwgY29tbWVu
dHMuDQoNCjEuIFJlbW92ZSB1bnVzZWQgdmFyaWFibGVzLg0KDQptYWtlIC0tbm8tcHJpbnQtZGly
ZWN0b3J5IGFsbC1hbQ0KICBDQyAgICAgcGx1Z2lucy9oZnAubw0KY2MxOiB3YXJuaW5ncyBiZWlu
ZyB0cmVhdGVkIGFzIGVycm9ycw0KcGx1Z2lucy9oZnAuYzogSW4gZnVuY3Rpb24g4oCYZ2V0X3By
b3BlcnRpZXNfY2LigJk6DQpwbHVnaW5zL2hmcC5jOjUwMDogZXJyb3I6IHVudXNlZCB2YXJpYWJs
ZSDigJhlcnLigJkNCnBsdWdpbnMvaGZwLmM6IEluIGZ1bmN0aW9uIOKAmGxpc3RfZGV2aWNlc19j
YuKAmToNCnBsdWdpbnMvaGZwLmM6NTI5OiBlcnJvcjogdW51c2VkIHZhcmlhYmxlIOKAmG1vZGVt
4oCZDQpwbHVnaW5zL2hmcC5jOiBJbiBmdW5jdGlvbiDigJhoZnBfcHJvYmXigJk6DQpwbHVnaW5z
L2hmcC5jOjY1NjogZXJyb3I6IHVudXNlZCB2YXJpYWJsZSDigJhkYXRh4oCZDQptYWtlWzFdOiAq
KiogW3BsdWdpbnMvaGZwLm9dIEVycm9yIDENCm1ha2U6ICoqKiBbYWxsXSBFcnJvciAyDQoNCjIu
IFlvdSBtYXkgdXNlIExpbnV4IGtlcm5lbCAvc2NyaXB0cy9jaGVja3BhdGNoLnBsIHRvIGNoZWNr
IHlvdXIgcGF0Y2guDQpFLmcsIHRoZXJlJ3JlIHRyYWlsaW5nIHNwYWNlIGluIDAwMDIucGF0Y2gg
bGluZSA4MzIuDQoNClJlZ2FyZHMsDQpaaGVuaHVhDQpfX19fX19fX19fX19fX19fX19fX19fX19f
X19fX19fX19fX19fX19fX19fX19fXwpvZm9ubyBtYWlsaW5nIGxpc3QKb2Zvbm9Ab2Zvbm8ub3Jn
Cmh0dHA6Ly9saXN0cy5vZm9uby5vcmcvbGlzdGluZm8vb2Zvbm8K

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

* RE: [RFC] HFP support into oFono and BlueZ
@ 2010-01-21  6:28         ` Zhang, Zhenhua
  0 siblings, 0 replies; 51+ messages in thread
From: Zhang, Zhenhua @ 2010-01-21  6:28 UTC (permalink / raw)
  To: ofono

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

Hi Padovan,

Gustavo F. Padovan wrote:
>> New version of the patches. Known issues:
>> "'org.bluez.HandsfreeAgent.Release' part not implemented" and it's
>> not working with more than one bluetooth devices in some cases.
>> Comments are welcome. 
>> 
> 
> Another new version of the patches. Now ofono query all bluetooth
> devices and searches for the ones with Gateway interface. Bluez has
> some fixes and now supports connection coming from the AG. 
> The patches are using a per-device agent as described by the last
> version of Handsfree API. 
> 
> I did not implemented yet 'org.bluez.HandsfreeAgent.Release'.
> 
> Comments are welcome. :)

Two small comments.

1. Remove unused variables.

make --no-print-directory all-am
  CC     plugins/hfp.o
cc1: warnings being treated as errors
plugins/hfp.c: In function ‘get_properties_cb’:
plugins/hfp.c:500: error: unused variable ‘err’
plugins/hfp.c: In function ‘list_devices_cb’:
plugins/hfp.c:529: error: unused variable ‘modem’
plugins/hfp.c: In function ‘hfp_probe’:
plugins/hfp.c:656: error: unused variable ‘data’
make[1]: *** [plugins/hfp.o] Error 1
make: *** [all] Error 2

2. You may use Linux kernel /scripts/checkpatch.pl to check your patch.
E.g, there're trailing space in 0002.patch line 832.

Regards,
Zhenhua

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

* Re: [RFC] HFP support into oFono and BlueZ
  2010-01-20 19:58       ` Gustavo F. Padovan
@ 2010-01-21  7:54         ` Zhao Forrest
  -1 siblings, 0 replies; 51+ messages in thread
From: Zhao Forrest @ 2010-01-21  7:54 UTC (permalink / raw)
  To: Gustavo F. Padovan; +Cc: linux-bluetooth, ofono, zhenhua.zhang

Your patch removes many DBus APIs provided by audio/gateway.c, which
was originally designed to provide a set of DBus functions/signals for
ease of application development. In other words, your patch is too
oFono-specific, and is not a general support for HFP HF unit role in
BlueZ IMO. The DBus interface provided by your patch is not easy to
develop the application because the application developer has to parse
the AT commands by himself. I know that oFono could parse AT commands.
But I don't think it a common case for other application developers.
Why not extend audio/gateway.c instead of removing its DBus APIs or
create a new file to support HFP for oFono in blueZ?

I think maybe Marcel would have more comments on this to give a clear direction.

Thanks,
Forrest

On Thu, Jan 21, 2010 at 3:58 AM, Gustavo F. Padovan
<padovan@profusion.mobi> wrote:
>> New version of the patches. Known issues:
>> "'org.bluez.HandsfreeAgent.Release' part not implemented" and it's not
>> working with more than one bluetooth devices in some cases. Comments
>> are welcome.
>>
>
> Another new version of the patches. Now ofono query all bluetooth
> devices and searches for the ones with Gateway interface.
> Bluez has some fixes and now supports connection coming from the AG.
> The patches are using a per-device agent as described by the last
> version of Handsfree API.
>
> I did not implemented yet 'org.bluez.HandsfreeAgent.Release'.
>
> Comments are welcome. :)
>
> --
> Gustavo F. Padovan
> ProFUSION embedded systems - http://profusion.mobi
>

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

* Re: [RFC] HFP support into oFono and BlueZ
@ 2010-01-21  7:54         ` Zhao Forrest
  0 siblings, 0 replies; 51+ messages in thread
From: Zhao Forrest @ 2010-01-21  7:54 UTC (permalink / raw)
  To: ofono

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

Your patch removes many DBus APIs provided by audio/gateway.c, which
was originally designed to provide a set of DBus functions/signals for
ease of application development. In other words, your patch is too
oFono-specific, and is not a general support for HFP HF unit role in
BlueZ IMO. The DBus interface provided by your patch is not easy to
develop the application because the application developer has to parse
the AT commands by himself. I know that oFono could parse AT commands.
But I don't think it a common case for other application developers.
Why not extend audio/gateway.c instead of removing its DBus APIs or
create a new file to support HFP for oFono in blueZ?

I think maybe Marcel would have more comments on this to give a clear direction.

Thanks,
Forrest

On Thu, Jan 21, 2010 at 3:58 AM, Gustavo F. Padovan
<padovan@profusion.mobi> wrote:
>> New version of the patches. Known issues:
>> "'org.bluez.HandsfreeAgent.Release' part not implemented" and it's not
>> working with more than one bluetooth devices in some cases. Comments
>> are welcome.
>>
>
> Another new version of the patches. Now ofono query all bluetooth
> devices and searches for the ones with Gateway interface.
> Bluez has some fixes and now supports connection coming from the AG.
> The patches are using a per-device agent as described by the last
> version of Handsfree API.
>
> I did not implemented yet 'org.bluez.HandsfreeAgent.Release'.
>
> Comments are welcome. :)
>
> --
> Gustavo F. Padovan
> ProFUSION embedded systems - http://profusion.mobi
>

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

* Re: [RFC] HFP support into oFono and BlueZ
  2010-01-20 19:58       ` Gustavo F. Padovan
@ 2010-01-21 19:22         ` Johan Hedberg
  -1 siblings, 0 replies; 51+ messages in thread
From: Johan Hedberg @ 2010-01-21 19:22 UTC (permalink / raw)
  To: Gustavo F. Padovan; +Cc: linux-bluetooth, ofono, zhenhua.zhang

[-- Attachment #1: 0002-Implement-HandsfreeGateway-Interface.patch --]
[-- Type: application/octet-stream, Size: 3009 bytes --]

Hi Gustavo,=0A=0ASorry about the delay in getting more comments from me. He=
re's the=0Aresult of an initial (and rather quick) review:=0A=0A+static con=
st char *connected2str(int i)=0A+{=0A+	switch (i) {=0A+		case GATEWAY_STATE=
_DISCONNECTED:=0A+			return "disconnected";=0A+		case GATEWAY_STATE_CONNECT=
ING:=0A+			return "connecting";=0A+		case GATEWAY_STATE_CONNECTED:=0A+			re=
turn "connected";=0A+		case GATEWAY_STATE_PLAYING:=0A+			return "playing";=
=0A+		default:=0A+			return "";=0A+	}=0A+}=0A=0AThis should be called state=
2str and the state is gateway_state_t and not=0Aint. Also name the variable=
 more apropriately (e.g. "state").=0A=0A+static void change_state(struct au=
dio_device *dev, int new_state)=0A=0ASame goes here. gateway_state_t and no=
t int.=0A=0A+	gw->sco =3D chan;=0A+	g_io_channel_ref(chan);=0A=0AThe conven=
tion is to do the variable assignment through the return value=0Aof the _re=
f function. Please do that.=0A=0A-	/* why is this here? */=0A 	fcntl(g_io_c=
hannel_unix_get_fd(chan), F_SETFL, 0);=0A=0AWhy are you removing this comme=
nt? Either remove the fcntl call=0Aaltogether, fix the comment to describe =
why the call is needed or then=0Adon't do anything if you don't know what i=
t's for.=0A=0A+	if (!dbus_set_error_from_message(&derr, reply)) {=0A+		info=
("Agent reply: file descriptor passed successfuly");=0A+		return;=0A+	}=0A=
=0AI don't think this is worth an info(). Use debug() instead.=0A=0A+	g_idl=
e_add(gateway_close, gw);=0A=0ADo you really need to call gateway_close thr=
ough an idle callback?=0AWhat's the problem with calling it directly?=0A=0A=
+	/* make /dev/rfcomm serial port from chan */=0A+	memset(&req, 0, sizeof(r=
eq));=0A+	req.dev_id =3D -1;=0A+	req.flags =3D (1 << RFCOMM_REUSE_DLC);=0A+=
	bacpy(&req.src, &dev->src);=0A+	bacpy(&req.dst, &dev->dst);=0A+	req.channe=
l =3D gw->channel;=0A=0AWhat's this? I thought you removed all the RFCOMM T=
TY codde.=0A=0A+	return;=0A }=0A=0AUnnecessary return at the end of a void =
function.=0A=0A+	agent =3D g_new0(struct agent, 1);=0A+	if (!agent)=0A+		re=
turn g_dbus_create_error(msg,=0A+				ERROR_INTERFACE ".Failed",=0A+				"Fai=
led to create a new agent");=0A=0Ag_new0() will either assert or always ret=
urn non-NULL. Use g_try_new0=0Aif you want to check for failure, but probab=
ly you can just remove the=0Acheck and keep using g_new0. Btw, src/agent.h =
already defines a struct=0Aagent so it'd be better if you use another name,=
 e.g. hf_agent.=0A=0A=0A+	agent->name =3D strdup(name);=0A+	agent->path =3D=
 strdup(path);=0A=0AUse g_strdup.=0A=0A+	g_io_channel_ref(chan);=0A+	dev->g=
ateway->rfcomm =3D chan;=0A=0AAgain, please do the assignment through the r=
eturn value of _ref()=0A=0AThere are probably more issues, but these are th=
e ones I found after an=0Ainitial glance-through. Please send an updated pa=
tch, preferably inline=0A(since as you notice I didn't manage to get my mai=
ler to properly quote=0Athis time) and I'll take a new look. Thanks.=0A=0AJ=
ohan=0A

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

* Re: [RFC] HFP support into oFono and BlueZ
@ 2010-01-21 19:22         ` Johan Hedberg
  0 siblings, 0 replies; 51+ messages in thread
From: Johan Hedberg @ 2010-01-21 19:22 UTC (permalink / raw)
  To: ofono

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

Hi Gustavo,

Sorry about the delay in getting more comments from me. Here's the
result of an initial (and rather quick) review:

+static const char *connected2str(int i)
+{
+	switch (i) {
+		case GATEWAY_STATE_DISCONNECTED:
+			return "disconnected";
+		case GATEWAY_STATE_CONNECTING:
+			return "connecting";
+		case GATEWAY_STATE_CONNECTED:
+			return "connected";
+		case GATEWAY_STATE_PLAYING:
+			return "playing";
+		default:
+			return "";
+	}
+}

This should be called state2str and the state is gateway_state_t and not
int. Also name the variable more apropriately (e.g. "state").

+static void change_state(struct audio_device *dev, int new_state)

Same goes here. gateway_state_t and not int.

+	gw->sco = chan;
+	g_io_channel_ref(chan);

The convention is to do the variable assignment through the return value
of the _ref function. Please do that.

-	/* why is this here? */
 	fcntl(g_io_channel_unix_get_fd(chan), F_SETFL, 0);

Why are you removing this comment? Either remove the fcntl call
altogether, fix the comment to describe why the call is needed or then
don't do anything if you don't know what it's for.

+	if (!dbus_set_error_from_message(&derr, reply)) {
+		info("Agent reply: file descriptor passed successfuly");
+		return;
+	}

I don't think this is worth an info(). Use debug() instead.

+	g_idle_add(gateway_close, gw);

Do you really need to call gateway_close through an idle callback?
What's the problem with calling it directly?

+	/* make /dev/rfcomm serial port from chan */
+	memset(&req, 0, sizeof(req));
+	req.dev_id = -1;
+	req.flags = (1 << RFCOMM_REUSE_DLC);
+	bacpy(&req.src, &dev->src);
+	bacpy(&req.dst, &dev->dst);
+	req.channel = gw->channel;

What's this? I thought you removed all the RFCOMM TTY codde.

+	return;
 }

Unnecessary return at the end of a void function.

+	agent = g_new0(struct agent, 1);
+	if (!agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed",
+				"Failed to create a new agent");

g_new0() will either assert or always return non-NULL. Use g_try_new0
if you want to check for failure, but probably you can just remove the
check and keep using g_new0. Btw, src/agent.h already defines a struct
agent so it'd be better if you use another name, e.g. hf_agent.


+	agent->name = strdup(name);
+	agent->path = strdup(path);

Use g_strdup.

+	g_io_channel_ref(chan);
+	dev->gateway->rfcomm = chan;

Again, please do the assignment through the return value of _ref()

There are probably more issues, but these are the ones I found after an
initial glance-through. Please send an updated patch, preferably inline
(since as you notice I didn't manage to get my mailer to properly quote
this time) and I'll take a new look. Thanks.

Johan


[-- Attachment #2: 0002-Implement-HandsfreeGateway-Interface.patch --]
[-- Type: application/octet-stream, Size: 2723 bytes --]

Hi Gustavo,

Sorry about the delay in getting more comments from me. Here's the
result of an initial (and rather quick) review:

+static const char *connected2str(int i)
+{
+	switch (i) {
+		case GATEWAY_STATE_DISCONNECTED:
+			return "disconnected";
+		case GATEWAY_STATE_CONNECTING:
+			return "connecting";
+		case GATEWAY_STATE_CONNECTED:
+			return "connected";
+		case GATEWAY_STATE_PLAYING:
+			return "playing";
+		default:
+			return "";
+	}
+}

This should be called state2str and the state is gateway_state_t and not
int. Also name the variable more apropriately (e.g. "state").

+static void change_state(struct audio_device *dev, int new_state)

Same goes here. gateway_state_t and not int.

+	gw->sco = chan;
+	g_io_channel_ref(chan);

The convention is to do the variable assignment through the return value
of the _ref function. Please do that.

-	/* why is this here? */
 	fcntl(g_io_channel_unix_get_fd(chan), F_SETFL, 0);

Why are you removing this comment? Either remove the fcntl call
altogether, fix the comment to describe why the call is needed or then
don't do anything if you don't know what it's for.

+	if (!dbus_set_error_from_message(&derr, reply)) {
+		info("Agent reply: file descriptor passed successfuly");
+		return;
+	}

I don't think this is worth an info(). Use debug() instead.

+	g_idle_add(gateway_close, gw);

Do you really need to call gateway_close through an idle callback?
What's the problem with calling it directly?

+	/* make /dev/rfcomm serial port from chan */
+	memset(&req, 0, sizeof(req));
+	req.dev_id = -1;
+	req.flags = (1 << RFCOMM_REUSE_DLC);
+	bacpy(&req.src, &dev->src);
+	bacpy(&req.dst, &dev->dst);
+	req.channel = gw->channel;

What's this? I thought you removed all the RFCOMM TTY codde.

+	return;
 }

Unnecessary return at the end of a void function.

+	agent = g_new0(struct agent, 1);
+	if (!agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed",
+				"Failed to create a new agent");

g_new0() will either assert or always return non-NULL. Use g_try_new0
if you want to check for failure, but probably you can just remove the
check and keep using g_new0. Btw, src/agent.h already defines a struct
agent so it'd be better if you use another name, e.g. hf_agent.


+	agent->name = strdup(name);
+	agent->path = strdup(path);

Use g_strdup.

+	g_io_channel_ref(chan);
+	dev->gateway->rfcomm = chan;

Again, please do the assignment through the return value of _ref()

There are probably more issues, but these are the ones I found after an
initial glance-through. Please send an updated patch, preferably inline
(since as you notice I didn't manage to get my mailer to properly quote
this time) and I'll take a new look. Thanks.

Johan


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

* Re: [RFC] HFP support into oFono and BlueZ
  2010-01-21 19:22         ` Johan Hedberg
@ 2010-01-21 19:27           ` Johan Hedberg
  -1 siblings, 0 replies; 51+ messages in thread
From: Johan Hedberg @ 2010-01-21 19:27 UTC (permalink / raw)
  To: Gustavo F. Padovan, linux-bluetooth, ofono, zhenhua.zhang

Hi Gustavo,

Sorry, not only did I manage not to quote the original patch properly
but the mime-type in my mail was scrwed up. Here's a second try:

=================

Sorry about the delay in getting more comments from me. Here's the
result of an initial (and rather quick) review:

+static const char *connected2str(int i)
+{
+	switch (i) {
+		case GATEWAY_STATE_DISCONNECTED:
+			return "disconnected";
+		case GATEWAY_STATE_CONNECTING:
+			return "connecting";
+		case GATEWAY_STATE_CONNECTED:
+			return "connected";
+		case GATEWAY_STATE_PLAYING:
+			return "playing";
+		default:
+			return "";
+	}
+}

This should be called state2str and the state is gateway_state_t and not
int. Also name the variable more apropriately (e.g. "state").

+static void change_state(struct audio_device *dev, int new_state)

Same goes here. gateway_state_t and not int.

+	gw->sco = chan;
+	g_io_channel_ref(chan);

The convention is to do the variable assignment through the return value
of the _ref function. Please do that.

-	/* why is this here? */
 	fcntl(g_io_channel_unix_get_fd(chan), F_SETFL, 0);

Why are you removing this comment? Either remove the fcntl call
altogether, fix the comment to describe why the call is needed or then
don't do anything if you don't know what it's for.

+	if (!dbus_set_error_from_message(&derr, reply)) {
+		info("Agent reply: file descriptor passed successfuly");
+		return;
+	}

I don't think this is worth an info(). Use debug() instead.

+	g_idle_add(gateway_close, gw);

Do you really need to call gateway_close through an idle callback?
What's the problem with calling it directly?

+	/* make /dev/rfcomm serial port from chan */
+	memset(&req, 0, sizeof(req));
+	req.dev_id = -1;
+	req.flags = (1 << RFCOMM_REUSE_DLC);
+	bacpy(&req.src, &dev->src);
+	bacpy(&req.dst, &dev->dst);
+	req.channel = gw->channel;

What's this? I thought you removed all the RFCOMM TTY codde.

+	return;
 }

Unnecessary return at the end of a void function.

+	agent = g_new0(struct agent, 1);
+	if (!agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed",
+				"Failed to create a new agent");

g_new0() will either assert or always return non-NULL. Use g_try_new0
if you want to check for failure, but probably you can just remove the
check and keep using g_new0. Btw, src/agent.h already defines a struct
agent so it'd be better if you use another name, e.g. hf_agent.


+	agent->name = strdup(name);
+	agent->path = strdup(path);

Use g_strdup.

+	g_io_channel_ref(chan);
+	dev->gateway->rfcomm = chan;

Again, please do the assignment through the return value of _ref()

There are probably more issues, but these are the ones I found after an
initial glance-through. Please send an updated patch, preferably inline
(since as you notice I didn't manage to get my mailer to properly quote
this time) and I'll take a new look. Thanks.

Johan


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

* Re: [RFC] HFP support into oFono and BlueZ
@ 2010-01-21 19:27           ` Johan Hedberg
  0 siblings, 0 replies; 51+ messages in thread
From: Johan Hedberg @ 2010-01-21 19:27 UTC (permalink / raw)
  To: ofono

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

Hi Gustavo,

Sorry, not only did I manage not to quote the original patch properly
but the mime-type in my mail was scrwed up. Here's a second try:

=================

Sorry about the delay in getting more comments from me. Here's the
result of an initial (and rather quick) review:

+static const char *connected2str(int i)
+{
+	switch (i) {
+		case GATEWAY_STATE_DISCONNECTED:
+			return "disconnected";
+		case GATEWAY_STATE_CONNECTING:
+			return "connecting";
+		case GATEWAY_STATE_CONNECTED:
+			return "connected";
+		case GATEWAY_STATE_PLAYING:
+			return "playing";
+		default:
+			return "";
+	}
+}

This should be called state2str and the state is gateway_state_t and not
int. Also name the variable more apropriately (e.g. "state").

+static void change_state(struct audio_device *dev, int new_state)

Same goes here. gateway_state_t and not int.

+	gw->sco = chan;
+	g_io_channel_ref(chan);

The convention is to do the variable assignment through the return value
of the _ref function. Please do that.

-	/* why is this here? */
 	fcntl(g_io_channel_unix_get_fd(chan), F_SETFL, 0);

Why are you removing this comment? Either remove the fcntl call
altogether, fix the comment to describe why the call is needed or then
don't do anything if you don't know what it's for.

+	if (!dbus_set_error_from_message(&derr, reply)) {
+		info("Agent reply: file descriptor passed successfuly");
+		return;
+	}

I don't think this is worth an info(). Use debug() instead.

+	g_idle_add(gateway_close, gw);

Do you really need to call gateway_close through an idle callback?
What's the problem with calling it directly?

+	/* make /dev/rfcomm serial port from chan */
+	memset(&req, 0, sizeof(req));
+	req.dev_id = -1;
+	req.flags = (1 << RFCOMM_REUSE_DLC);
+	bacpy(&req.src, &dev->src);
+	bacpy(&req.dst, &dev->dst);
+	req.channel = gw->channel;

What's this? I thought you removed all the RFCOMM TTY codde.

+	return;
 }

Unnecessary return at the end of a void function.

+	agent = g_new0(struct agent, 1);
+	if (!agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed",
+				"Failed to create a new agent");

g_new0() will either assert or always return non-NULL. Use g_try_new0
if you want to check for failure, but probably you can just remove the
check and keep using g_new0. Btw, src/agent.h already defines a struct
agent so it'd be better if you use another name, e.g. hf_agent.


+	agent->name = strdup(name);
+	agent->path = strdup(path);

Use g_strdup.

+	g_io_channel_ref(chan);
+	dev->gateway->rfcomm = chan;

Again, please do the assignment through the return value of _ref()

There are probably more issues, but these are the ones I found after an
initial glance-through. Please send an updated patch, preferably inline
(since as you notice I didn't manage to get my mailer to properly quote
this time) and I'll take a new look. Thanks.

Johan


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

* HFP support into BlueZ and oFono
  2010-01-20 19:58       ` Gustavo F. Padovan
@ 2010-01-27 19:12         ` Gustavo F. Padovan
  -1 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-27 19:12 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: ofono

Hi All,

New patches for the HFP support. The BlueZ part was updated with the Johan's comments and the oFono patch supports load/remove of bluetooth devices dynamically.
We still have bug: the link RFCOMM on the AG side is closed only when bluetoothd shuts down. Any idea on that?

Comments are welcome.

-- 
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi 


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

* HFP support into BlueZ and oFono
@ 2010-01-27 19:12         ` Gustavo F. Padovan
  0 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-27 19:12 UTC (permalink / raw)
  To: ofono

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

Hi All,

New patches for the HFP support. The BlueZ part was updated with the Johan's comments and the oFono patch supports load/remove of bluetooth devices dynamically.
We still have bug: the link RFCOMM on the AG side is closed only when bluetoothd shuts down. Any idea on that?

Comments are welcome.

-- 
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi 


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

* [PATCH 1/2] clean up audio/gateway.c
  2010-01-27 19:12         ` Gustavo F. Padovan
@ 2010-01-27 19:12           ` Gustavo F. Padovan
  -1 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-27 19:12 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: ofono

remove all code related to the AT engine
---
 audio/gateway.c |  760 +------------------------------------------------------
 1 files changed, 1 insertions(+), 759 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index a1c1ea2..3dc09ff 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -53,52 +53,6 @@
 #include "dbus-common.h"
 
 #define RFCOMM_BUF_SIZE 256
-/* not-more-then-16 defined by GSM + 1 for NULL + padding */
-#define AG_INDICATOR_DESCR_SIZE 20
-#define AG_CALLER_NUM_SIZE 64	/* size of number + type */
-
-/* commands */
-#define AG_FEATURES "AT+BRSF=26\r"     /* = 0x7F = All features supported */
-#define AG_INDICATORS_SUPP "AT+CIND=?\r"
-#define AG_INDICATORS_VAL "AT+CIND?\r"
-#define AG_INDICATORS_ENABLE "AT+CMER=3,0,0,1\r"
-#define AG_HOLD_MPTY_SUPP "AT+CHLD=?\r"
-#define AG_CALLER_IDENT_ENABLE "AT+CLIP=1\r"
-#define AG_CARRIER_FORMAT "AT+COPS=3,0\r"
-#define AG_EXTENDED_RESULT_CODE "AT+CMEE=1\r"
-
-#define AG_FEATURE_3WAY 0x1
-#define AG_FEATURE_EXTENDED_RES_CODE 0x100
-/* Hold and multipary AG features.
- * Comments below are copied from hands-free spec for reference */
-/* Releases all held calls or sets User Determined User Busy (UDUB)
- * for a waiting call */
-#define AG_CHLD_0 0x01
-/* Releases all active calls (if any exist) and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_1 0x02
-/* Releases specified active call only <x> */
-#define AG_CHLD_1x 0x04
-/* Places all active calls (if any exist) on hold and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_2 0x08
-/* Request private consultation mode with specified call <x> (Place all
- * calls on hold EXCEPT the call <x>) */
-#define AG_CHLD_2x 0x10
-/* Adds a held call to the conversation */
-#define AG_CHLD_3 0x20
-/* Connects the two calls and disconnects the subscriber from both calls
- * (Explicit Call Transfer). Support for this value and its associated
- * functionality is optional for the HF. */
-#define AG_CHLD_4 0x40
-
-#define OK_RESPONSE "\r\nOK\r\n"
-#define ERROR_RESPONSE "\r\nERROR\r\n"
-
-struct indicator {
-	gchar descr[AG_INDICATOR_DESCR_SIZE];
-	gint value;
-};
 
 struct gateway {
 	gateway_state_t state;
@@ -108,387 +62,10 @@ struct gateway {
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
 	DBusMessage *connect_message;
-	guint ag_features;
-	guint hold_multiparty_features;
-	GSList *indies;
-	gboolean is_dialing;
-	gboolean call_active;
-
-	int sp_gain;
-	int mic_gain;
 };
 
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device);
-
 int gateway_close(struct audio_device *device);
 
-static void rfcomm_start_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	gw->rfcomm_watch_id = g_io_add_watch(gw->rfcomm,
-			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-			(GIOFunc) rfcomm_ag_data_cb, dev);
-}
-
-static void rfcomm_stop_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	g_source_remove(gw->rfcomm_watch_id);
-}
-
-static gboolean io_channel_write_all(GIOChannel *io, gchar *data,
-					gsize count)
-{
-	gsize written = 0;
-	GIOStatus status;
-
-	while (count > 0) {
-		status = g_io_channel_write_chars(io, data, count, &written,
-						NULL);
-		if (status != G_IO_STATUS_NORMAL)
-			return FALSE;
-
-		data += written;
-		count -= written;
-	}
-	return TRUE;
-}
-
-/* it's worth to mention that data and response could be the same pointers */
-static gboolean rfcomm_send_and_read(struct gateway *gw, gchar *data,
-                                    gchar *response, gsize count)
-{
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read = 0;
-	gboolean got_ok = FALSE;
-	gboolean got_error = FALSE;
-	gchar *resp_buf = response;
-	gsize toread = RFCOMM_BUF_SIZE - 1;
-	GIOStatus status;
-
-	if (!io_channel_write_all(rfcomm, data, count))
-		return FALSE;
-
-	while (!(got_ok || got_error)) {
-		status = g_io_channel_read_chars(rfcomm, resp_buf, toread,
-						&read, NULL);
-		if (status == G_IO_STATUS_NORMAL)
-			resp_buf[read] = '\0';
-		else {
-			debug("rfcomm_send_and_read(): %m");
-			return FALSE;
-		}
-		got_ok = NULL != strstr(resp_buf, OK_RESPONSE);
-		got_error = NULL != strstr(resp_buf, ERROR_RESPONSE);
-		resp_buf += read;
-		toread -= read;
-	}
-	return TRUE;
-}
-
-/* get <descr> from the names: (<descr>, (<values>)), (<descr>, (<values>))
- * ... */
-static GSList *parse_indicator_names(gchar *names, GSList *indies)
-{
-	gchar *current = names - 1;
-	GSList *result = indies;
-	gchar *next;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 2;
-		next = strstr(current, ",(");
-		ind = g_slice_new(struct indicator);
-		strncpy(ind->descr, current, 20);
-		ind->descr[(intptr_t) next - (intptr_t) current] = '\0';
-		result = g_slist_append(result, (gpointer) ind);
-		current = strstr(next + 1, ",(");
-	}
-	return result;
-}
-
-/* get values from <val0>,<val1>,... */
-static GSList *parse_indicator_values(gchar *values, GSList *indies)
-{
-	gint val;
-	gchar *current = values - 1;
-	GSList *runner = indies;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 1;
-		sscanf(current, "%d", &val);
-		current = strchr(current, ',');
-		ind = g_slist_nth_data(runner, 0);
-		ind->value = val;
-		runner = g_slist_next(runner);
-	}
-	return indies;
-}
-
-/* get values from <val0>,<val1>,... */
-static guint get_hold_mpty_features(gchar *features)
-{
-	guint result = 0;
-
-	if (strstr(features, "0"))
-		result |= AG_CHLD_0;
-
-	if (strstr(features, "1"))
-		result |= AG_CHLD_1;
-
-	if (strstr(features, "1x"))
-		result |= AG_CHLD_1x;
-
-	if (strstr(features, "2"))
-		result |= AG_CHLD_2;
-
-	if (strstr(features, "2x"))
-		result |= AG_CHLD_2x;
-
-	if (strstr(features, "3"))
-		result |= AG_CHLD_3;
-
-	if (strstr(features, "4"))
-		result |= AG_CHLD_4;
-
-	return result;
-}
-
-static gboolean establish_service_level_conn(struct gateway *gw)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	gboolean res;
-
-	debug("at the begin of establish_service_level_conn()");
-	res = rfcomm_send_and_read(gw, AG_FEATURES, buf,
-				sizeof(AG_FEATURES) - 1);
-	if (!res || sscanf(buf, "\r\n+BRSF:%d", &gw->ag_features) != 1)
-		return FALSE;
-
-	debug("features are 0x%X", gw->ag_features);
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_SUPP, buf,
-				sizeof(AG_INDICATORS_SUPP) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_names(strchr(buf, '('), NULL);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_VAL, buf,
-		sizeof(AG_INDICATORS_VAL) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_values(strchr(buf, ':') + 1, gw->indies);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_ENABLE, buf,
-				sizeof(AG_INDICATORS_ENABLE) - 1);
-	if (!res || !strstr(buf, "OK"))
-		return FALSE;
-
-	if ((gw->ag_features & AG_FEATURE_3WAY) != 0) {
-		res = rfcomm_send_and_read(gw, AG_HOLD_MPTY_SUPP, buf,
-				sizeof(AG_HOLD_MPTY_SUPP) - 1);
-		if (!res || !strstr(buf, "+CHLD:")) {
-			g_slice_free1(RFCOMM_BUF_SIZE, buf);
-			return FALSE;
-		}
-		gw->hold_multiparty_features = get_hold_mpty_features(
-							strchr(buf, '('));
-
-	} else
-		gw->hold_multiparty_features = 0;
-
-	debug("Service layer connection successfully established!");
-	rfcomm_send_and_read(gw, AG_CALLER_IDENT_ENABLE, buf,
-			sizeof(AG_CALLER_IDENT_ENABLE) - 1);
-	rfcomm_send_and_read(gw, AG_CARRIER_FORMAT, buf,
-			sizeof(AG_CARRIER_FORMAT) - 1);
-	if ((gw->ag_features & AG_FEATURE_EXTENDED_RES_CODE) != 0)
-		rfcomm_send_and_read(gw, AG_EXTENDED_RESULT_CODE, buf,
-			sizeof(AG_EXTENDED_RESULT_CODE) - 1);
-
-	return TRUE;
-}
-
-static void process_ind_change(struct audio_device *dev, guint index,
-							gint value)
-{
-	struct gateway *gw = dev->gateway;
-	struct indicator *ind = g_slist_nth_data(gw->indies, index - 1);
-	gchar *name = ind->descr;
-
-	ind->value = value;
-
-	debug("at the begin of process_ind_change, name is %s", name);
-	if (!strcmp(name, "\"call\"")) {
-		if (value > 0) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallStarted", DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-			gw->call_active = TRUE;
-		} else {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallEnded", DBUS_TYPE_INVALID);
-			gw->call_active = FALSE;
-		}
-
-	} else if (!strcmp(name, "\"callsetup\"")) {
-		if (value == 0 && gw->is_dialing) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-		} else if (!gw->is_dialing && value > 0)
-			gw->is_dialing = TRUE;
-
-	} else if (!strcmp(name, "\"callheld\"")) {
-		/* FIXME: The following code is based on assumptions only.
-		 * Has to be tested for interoperability
-		 * I assume that callheld=2 would be sent when dial from HF
-		 * failed in case of 3-way call
-		 * Unfortunately this path is not covered by the HF spec so
-		 * the code has to be tested for interop
-		*/
-		/* '2' means: all calls held, no active calls */
-		if (value == 2) {
-			if (gw->is_dialing) {
-				g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-				gw->is_dialing = FALSE;
-			}
-		}
-	} else if (!strcmp(name, "\"service\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RegistrationStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"signal\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "SignalStrength",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"roam\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RoamingStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"battchg\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "BatteryCharge",
-				DBUS_TYPE_UINT16, &value);
-}
-
-static void process_ring(struct audio_device *device, GIOChannel *chan,
-			gchar *buf)
-{
-	gchar number[AG_CALLER_NUM_SIZE];
-	gchar *cli;
-	gchar *sep;
-	gsize read;
-	GIOStatus status;
-
-	rfcomm_stop_watch(device);
-	status = g_io_channel_read_chars(chan, buf, RFCOMM_BUF_SIZE - 1, &read, NULL);
-	if (status != G_IO_STATUS_NORMAL)
-		return;
-
-	debug("at the begin of process_ring");
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 10)
-		error("process_ring(): buf is too long '%s'", buf);
-	else if ((cli = strstr(buf, "\r\n+CLIP"))) {
-		if (sscanf(cli, "\r\n+CLIP: \"%s", number) == 1) {
-			sep = strchr(number, '"');
-			sep[0] = '\0';
-
-			/* FIXME:signal will be emitted on each RING+CLIP.
-			 * That's bad */
-			cli = number;
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE, "Ring",
-					DBUS_TYPE_STRING, &cli,
-					DBUS_TYPE_INVALID);
-			device->gateway->is_dialing = TRUE;
-		} else
-			error("process_ring(): '%s' in place of +CLIP after RING", buf);
-
-	}
-
-	rfcomm_start_watch(device);
-}
-
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	struct gateway *gw;
-	gsize read;
-	/* some space for value */
-	gchar indicator[AG_INDICATOR_DESCR_SIZE + 4];
-	gint value;
-	guint index;
-	gchar *sep;
-
-	debug("at the begin of rfcomm_ag_data_cb()");
-	if (cond & G_IO_NVAL)
-		return FALSE;
-
-	gw = device->gateway;
-
-	if (cond & (G_IO_ERR | G_IO_HUP)) {
-		debug("connection with remote BT is closed");
-		gateway_close(device);
-		return FALSE;
-	}
-
-	if (g_io_channel_read_chars(chan, buf, sizeof(buf) - 1, &read, NULL)
-			!= G_IO_STATUS_NORMAL)
-		return TRUE;
-	buf[read] = '\0';
-
-	if (strlen(buf) > AG_INDICATOR_DESCR_SIZE + 14)
-		error("rfcomm_ag_data_cb(): buf is too long '%s'", buf);
-	else if (sscanf(buf, "\r\n+CIEV:%s\r\n", indicator) == 1) {
-		sep = strchr(indicator, ',');
-		sep[0] = '\0';
-		sep += 1;
-		index = atoi(indicator);
-		value = atoi(sep);
-		process_ind_change(device, index, value);
-	} else if (strstr(buf, "RING"))
-		process_ring(device, chan, buf);
-	else if (sscanf(buf, "\r\n+BVRA:%d\r\n", &value) == 1) {
-		if (value == 0)
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionActive",
-					DBUS_TYPE_INVALID);
-		else
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionInactive",
-					DBUS_TYPE_INVALID);
-	} else if (sscanf(buf, "\r\n+VGS:%d\r\n", &value) == 1) {
-		gw->sp_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "SpeakerGain",
-				DBUS_TYPE_UINT16, &value);
-	} else if (sscanf(buf, "\r\n+VGM:%d\r\n", &value) == 1) {
-		gw->mic_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "MicrophoneGain",
-				DBUS_TYPE_UINT16, &value);
-	} else
-		error("rfcomm_ag_data_cb(): read wrong data '%s'", buf);
-
-	return TRUE;
-}
-
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
 {
@@ -541,7 +118,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	DBusMessage *conn_mes = gw->connect_message;
 	gchar gw_addr[18];
 	GIOFlags flags;
 
@@ -563,29 +139,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 	if (!gw->rfcomm)
 		gw->rfcomm = g_io_channel_ref(chan);
 
-	if (establish_service_level_conn(dev->gateway)) {
-		gboolean value = TRUE;
-
-		debug("%s: Connected to %s", dev->path, gw_addr);
-		rfcomm_start_watch(dev);
-		if (conn_mes) {
-			DBusMessage *reply =
-				dbus_message_new_method_return(conn_mes);
-			dbus_connection_send(dev->conn, reply, NULL);
-			dbus_message_unref(reply);
-			dbus_message_unref(conn_mes);
-			gw->connect_message = NULL;
-		}
-
-		gw->state = GATEWAY_STATE_CONNECTED;
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN,	&value);
-		return;
-	} else
-		error("%s: Failed to establish service layer connection to %s",
-			dev->path, gw_addr);
-
 	if (NULL != gw->sco_start_cb)
 		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 
@@ -732,321 +285,20 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
-static DBusMessage *process_ag_reponse(DBusMessage *msg, gchar *response)
-{
-	DBusMessage *reply;
-
-
-	debug("in process_ag_reponse, response is %s", response);
-	if (strstr(response, OK_RESPONSE))
-		reply = dbus_message_new_method_return(msg);
-	else {
-		/* FIXME: some code should be here to processes errors
-		 *  in better fasion */
-		debug("AG responded with '%s' to %s method call", response,
-				dbus_message_get_member(msg));
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".OperationFailed",
-					"Operation failed.See log for details");
-	}
-	return reply;
-}
-
-static DBusMessage *process_simple(DBusMessage *msg, struct audio_device *dev,
-					gchar *data)
-{
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-
-	rfcomm_stop_watch(dev);
-	rfcomm_send_and_read(gw, data, buf, strlen(data));
-	rfcomm_start_watch(dev);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_ANSWER "ATA\r"
-
-static DBusMessage *ag_answer(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	if (gw->call_active)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".CallAlreadyAnswered",
-					"Call AlreadyAnswered");
-
-	return process_simple(msg, dev, AG_ANSWER);
-}
-
-#define AG_HANGUP "AT+CHUP\r"
-
-static DBusMessage *ag_terminate_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	return process_simple(msg, dev, AG_HANGUP);
-}
-
-/* according to GSM spec */
-#define ALLOWED_NUMBER_SYMBOLS "1234567890*#ABCD"
-#define AG_PLACE_CALL "ATD%s;\r"
-/* dialing from memory is not supported as headset spec doesn't define a way
- * to retreive phone memory entries.
- */
-static DBusMessage *ag_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint atd_len;
-	DBusMessage *result;
-
-	debug("at the begin of ag_call()");
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	atd_len = sprintf(buf, AG_PLACE_CALL, number);
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, buf, buf, atd_len);
-	rfcomm_start_watch(device);
-
-	result = process_ag_reponse(msg, buf);
-	return result;
-}
-
-#define AG_GET_CARRIER "AT+COPS?\r"
-
-static DBusMessage *ag_get_operator(DBusConnection *conn, DBusMessage *msg,
-					void *data)
-{
-	struct audio_device *dev = (struct audio_device *) data;
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read;
-	gchar *result, *sep;
-	DBusMessage *reply;
-	GIOStatus status;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(dev);
-	io_channel_write_all(rfcomm, AG_GET_CARRIER, strlen(AG_GET_CARRIER));
-
-	status = g_io_channel_read_chars(rfcomm, buf, RFCOMM_BUF_SIZE - 1,
-						&read, NULL);
-	rfcomm_start_watch(dev);
-	if (G_IO_STATUS_NORMAL == status) {
-		buf[read] = '\0';
-		if (strstr(buf, "+COPS")) {
-			if (!strrchr(buf, ','))
-				result = "0";
-			else {
-				result = strchr(buf, '\"') + 1;
-				sep = strchr(result, '\"');
-				sep[0] = '\0';
-			}
-
-			reply = dbus_message_new_method_return(msg);
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&result, DBUS_TYPE_INVALID);
-		} else {
-			info("ag_get_operator(): '+COPS' expected but"
-				" '%s' received", buf);
-			reply = dbus_message_new_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Unexpected response from AG");
-		}
-	} else {
-		error("ag_get_operator(): %m");
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".ConnectionFailed",
-					"Failed to receive response from AG");
-	}
-
-	return reply;
-}
-
-#define AG_SEND_DTMF "AT+VTS=%c\r"
-static DBusMessage *ag_send_dtmf(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint com_len;
-	gboolean got_ok = TRUE;
-	gint num_len;
-	gint i = 0;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	num_len = strlen(number);
-	rfcomm_stop_watch(device);
-	while (i < num_len && got_ok) {
-		com_len = sprintf(buf, AG_SEND_DTMF, number[i]);
-		rfcomm_send_and_read(gw, buf, buf, com_len);
-		got_ok = NULL != strstr(buf, OK_RESPONSE);
-		i += 1;
-	}
-	rfcomm_start_watch(device);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_GET_SUBSCRIBER_NUMS "AT+CNUM\r"
-#define CNUM_LEN 5             /* length of "+CNUM" string */
-#define MAX_NUMBER_CNT 16
-static DBusMessage *ag_get_subscriber_num(DBusConnection *conn,
-					DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number, *end;
-	DBusMessage *reply = dbus_message_new_method_return(msg);
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, AG_GET_SUBSCRIBER_NUMS, buf,
-			strlen(AG_GET_SUBSCRIBER_NUMS));
-	rfcomm_start_watch(device);
-
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 21)
-		error("ag_get_subscriber_num(): buf is too long '%s'", buf);
-	else if (strstr(buf, "+CNUM")) {
-		number = strchr(buf, ',');
-		number++;
-		end = strchr(number, ',');
-		if (end) {
-			*end = '\0';
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&number, DBUS_TYPE_INVALID);
-		}
-	} else
-		error("ag_get_subscriber_num(): read wrong data '%s'", buf);
-
-	return reply;
-}
-
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	DBusMessage *reply;
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	gboolean value;
-	guint index = 0;
-	struct indicator *ind;
-
-	reply = dbus_message_new_method_return(msg);
-	if (!reply)
-		return NULL;
-
-	dbus_message_iter_init_append(reply, &iter);
-
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
-	/* Connected */
-	value = gateway_is_connected(device);
-	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
-
-	if (!value)
-		goto done;
-
-	while ((ind = g_slist_nth_data(gw->indies, index))) {
-		if(!strcmp(ind->descr, "\"service\""))
-			dict_append_entry(&dict, "RegistrationStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"signal\""))
-			dict_append_entry(&dict, "SignalStrength",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"roam\""))
-			dict_append_entry(&dict, "RoamingStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"battchg\""))
-			dict_append_entry(&dict, "BatteryCharge",
-					DBUS_TYPE_UINT16, &ind->value);
-		index++;
-	}
-
-	/* SpeakerGain */
-	dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16,
-				&device->gateway->sp_gain);
-
-	/* MicrophoneGain */
-	dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16,
-				&device->gateway->mic_gain);
-done:
-	dbus_message_iter_close_container(&iter, &dict);
-	return reply;
+	return NULL;
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "Disconnect", "", "", ag_disconnect },
-	{ "AnswerCall", "", "", ag_answer },
-	{ "TerminateCall", "", "", ag_terminate_call },
-	{ "Call", "s", "", ag_call },
-	{ "GetOperatorName", "", "s", ag_get_operator },
-	{ "SendDTMF", "s", "", ag_send_dtmf },
-	{ "GetSubscriberNumber", "", "s", ag_get_subscriber_num },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
 	{ NULL, NULL, NULL, NULL }
 };
 
 static GDBusSignalTable gateway_signals[] = {
-	{ "Ring", "s" },
-	{ "CallTerminated", "" },
-	{ "CallStarted", "" },
-	{ "CallEnded", "" },
 	{ "PropertyChanged", "sv" },
 	{ NULL, NULL }
 };
@@ -1063,9 +315,6 @@ struct gateway *gateway_init(struct audio_device *dev)
 
 	debug("in gateway_init, dev is %p", dev);
 	gw = g_new0(struct gateway, 1);
-	gw->indies = NULL;
-	gw->is_dialing = FALSE;
-	gw->call_active = FALSE;
 	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
 
@@ -1107,11 +356,6 @@ void gateway_start_service(struct audio_device *device)
 	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
 }
 
-static void indicator_slice_free(gpointer mem)
-{
-	g_slice_free(struct indicator, mem);
-}
-
 int gateway_close(struct audio_device *device)
 {
 	struct gateway *gw = device->gateway;
@@ -1119,8 +363,6 @@ int gateway_close(struct audio_device *device)
 	GIOChannel *sco = gw->sco;
 	gboolean value = FALSE;
 
-	g_slist_foreach(gw->indies, (GFunc) indicator_slice_free, NULL);
-	g_slist_free(gw->indies);
 	if (rfcomm) {
 		g_io_channel_shutdown(rfcomm, TRUE, NULL);
 		g_io_channel_unref(rfcomm);
-- 
1.6.4.4


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

* [PATCH 1/2] clean up audio/gateway.c
@ 2010-01-27 19:12           ` Gustavo F. Padovan
  0 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-27 19:12 UTC (permalink / raw)
  To: ofono

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

remove all code related to the AT engine
---
 audio/gateway.c |  760 +------------------------------------------------------
 1 files changed, 1 insertions(+), 759 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index a1c1ea2..3dc09ff 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -53,52 +53,6 @@
 #include "dbus-common.h"
 
 #define RFCOMM_BUF_SIZE 256
-/* not-more-then-16 defined by GSM + 1 for NULL + padding */
-#define AG_INDICATOR_DESCR_SIZE 20
-#define AG_CALLER_NUM_SIZE 64	/* size of number + type */
-
-/* commands */
-#define AG_FEATURES "AT+BRSF=26\r"     /* = 0x7F = All features supported */
-#define AG_INDICATORS_SUPP "AT+CIND=?\r"
-#define AG_INDICATORS_VAL "AT+CIND?\r"
-#define AG_INDICATORS_ENABLE "AT+CMER=3,0,0,1\r"
-#define AG_HOLD_MPTY_SUPP "AT+CHLD=?\r"
-#define AG_CALLER_IDENT_ENABLE "AT+CLIP=1\r"
-#define AG_CARRIER_FORMAT "AT+COPS=3,0\r"
-#define AG_EXTENDED_RESULT_CODE "AT+CMEE=1\r"
-
-#define AG_FEATURE_3WAY 0x1
-#define AG_FEATURE_EXTENDED_RES_CODE 0x100
-/* Hold and multipary AG features.
- * Comments below are copied from hands-free spec for reference */
-/* Releases all held calls or sets User Determined User Busy (UDUB)
- * for a waiting call */
-#define AG_CHLD_0 0x01
-/* Releases all active calls (if any exist) and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_1 0x02
-/* Releases specified active call only <x> */
-#define AG_CHLD_1x 0x04
-/* Places all active calls (if any exist) on hold and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_2 0x08
-/* Request private consultation mode with specified call <x> (Place all
- * calls on hold EXCEPT the call <x>) */
-#define AG_CHLD_2x 0x10
-/* Adds a held call to the conversation */
-#define AG_CHLD_3 0x20
-/* Connects the two calls and disconnects the subscriber from both calls
- * (Explicit Call Transfer). Support for this value and its associated
- * functionality is optional for the HF. */
-#define AG_CHLD_4 0x40
-
-#define OK_RESPONSE "\r\nOK\r\n"
-#define ERROR_RESPONSE "\r\nERROR\r\n"
-
-struct indicator {
-	gchar descr[AG_INDICATOR_DESCR_SIZE];
-	gint value;
-};
 
 struct gateway {
 	gateway_state_t state;
@@ -108,387 +62,10 @@ struct gateway {
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
 	DBusMessage *connect_message;
-	guint ag_features;
-	guint hold_multiparty_features;
-	GSList *indies;
-	gboolean is_dialing;
-	gboolean call_active;
-
-	int sp_gain;
-	int mic_gain;
 };
 
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device);
-
 int gateway_close(struct audio_device *device);
 
-static void rfcomm_start_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	gw->rfcomm_watch_id = g_io_add_watch(gw->rfcomm,
-			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-			(GIOFunc) rfcomm_ag_data_cb, dev);
-}
-
-static void rfcomm_stop_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	g_source_remove(gw->rfcomm_watch_id);
-}
-
-static gboolean io_channel_write_all(GIOChannel *io, gchar *data,
-					gsize count)
-{
-	gsize written = 0;
-	GIOStatus status;
-
-	while (count > 0) {
-		status = g_io_channel_write_chars(io, data, count, &written,
-						NULL);
-		if (status != G_IO_STATUS_NORMAL)
-			return FALSE;
-
-		data += written;
-		count -= written;
-	}
-	return TRUE;
-}
-
-/* it's worth to mention that data and response could be the same pointers */
-static gboolean rfcomm_send_and_read(struct gateway *gw, gchar *data,
-                                    gchar *response, gsize count)
-{
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read = 0;
-	gboolean got_ok = FALSE;
-	gboolean got_error = FALSE;
-	gchar *resp_buf = response;
-	gsize toread = RFCOMM_BUF_SIZE - 1;
-	GIOStatus status;
-
-	if (!io_channel_write_all(rfcomm, data, count))
-		return FALSE;
-
-	while (!(got_ok || got_error)) {
-		status = g_io_channel_read_chars(rfcomm, resp_buf, toread,
-						&read, NULL);
-		if (status == G_IO_STATUS_NORMAL)
-			resp_buf[read] = '\0';
-		else {
-			debug("rfcomm_send_and_read(): %m");
-			return FALSE;
-		}
-		got_ok = NULL != strstr(resp_buf, OK_RESPONSE);
-		got_error = NULL != strstr(resp_buf, ERROR_RESPONSE);
-		resp_buf += read;
-		toread -= read;
-	}
-	return TRUE;
-}
-
-/* get <descr> from the names: (<descr>, (<values>)), (<descr>, (<values>))
- * ... */
-static GSList *parse_indicator_names(gchar *names, GSList *indies)
-{
-	gchar *current = names - 1;
-	GSList *result = indies;
-	gchar *next;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 2;
-		next = strstr(current, ",(");
-		ind = g_slice_new(struct indicator);
-		strncpy(ind->descr, current, 20);
-		ind->descr[(intptr_t) next - (intptr_t) current] = '\0';
-		result = g_slist_append(result, (gpointer) ind);
-		current = strstr(next + 1, ",(");
-	}
-	return result;
-}
-
-/* get values from <val0>,<val1>,... */
-static GSList *parse_indicator_values(gchar *values, GSList *indies)
-{
-	gint val;
-	gchar *current = values - 1;
-	GSList *runner = indies;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 1;
-		sscanf(current, "%d", &val);
-		current = strchr(current, ',');
-		ind = g_slist_nth_data(runner, 0);
-		ind->value = val;
-		runner = g_slist_next(runner);
-	}
-	return indies;
-}
-
-/* get values from <val0>,<val1>,... */
-static guint get_hold_mpty_features(gchar *features)
-{
-	guint result = 0;
-
-	if (strstr(features, "0"))
-		result |= AG_CHLD_0;
-
-	if (strstr(features, "1"))
-		result |= AG_CHLD_1;
-
-	if (strstr(features, "1x"))
-		result |= AG_CHLD_1x;
-
-	if (strstr(features, "2"))
-		result |= AG_CHLD_2;
-
-	if (strstr(features, "2x"))
-		result |= AG_CHLD_2x;
-
-	if (strstr(features, "3"))
-		result |= AG_CHLD_3;
-
-	if (strstr(features, "4"))
-		result |= AG_CHLD_4;
-
-	return result;
-}
-
-static gboolean establish_service_level_conn(struct gateway *gw)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	gboolean res;
-
-	debug("at the begin of establish_service_level_conn()");
-	res = rfcomm_send_and_read(gw, AG_FEATURES, buf,
-				sizeof(AG_FEATURES) - 1);
-	if (!res || sscanf(buf, "\r\n+BRSF:%d", &gw->ag_features) != 1)
-		return FALSE;
-
-	debug("features are 0x%X", gw->ag_features);
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_SUPP, buf,
-				sizeof(AG_INDICATORS_SUPP) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_names(strchr(buf, '('), NULL);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_VAL, buf,
-		sizeof(AG_INDICATORS_VAL) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_values(strchr(buf, ':') + 1, gw->indies);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_ENABLE, buf,
-				sizeof(AG_INDICATORS_ENABLE) - 1);
-	if (!res || !strstr(buf, "OK"))
-		return FALSE;
-
-	if ((gw->ag_features & AG_FEATURE_3WAY) != 0) {
-		res = rfcomm_send_and_read(gw, AG_HOLD_MPTY_SUPP, buf,
-				sizeof(AG_HOLD_MPTY_SUPP) - 1);
-		if (!res || !strstr(buf, "+CHLD:")) {
-			g_slice_free1(RFCOMM_BUF_SIZE, buf);
-			return FALSE;
-		}
-		gw->hold_multiparty_features = get_hold_mpty_features(
-							strchr(buf, '('));
-
-	} else
-		gw->hold_multiparty_features = 0;
-
-	debug("Service layer connection successfully established!");
-	rfcomm_send_and_read(gw, AG_CALLER_IDENT_ENABLE, buf,
-			sizeof(AG_CALLER_IDENT_ENABLE) - 1);
-	rfcomm_send_and_read(gw, AG_CARRIER_FORMAT, buf,
-			sizeof(AG_CARRIER_FORMAT) - 1);
-	if ((gw->ag_features & AG_FEATURE_EXTENDED_RES_CODE) != 0)
-		rfcomm_send_and_read(gw, AG_EXTENDED_RESULT_CODE, buf,
-			sizeof(AG_EXTENDED_RESULT_CODE) - 1);
-
-	return TRUE;
-}
-
-static void process_ind_change(struct audio_device *dev, guint index,
-							gint value)
-{
-	struct gateway *gw = dev->gateway;
-	struct indicator *ind = g_slist_nth_data(gw->indies, index - 1);
-	gchar *name = ind->descr;
-
-	ind->value = value;
-
-	debug("at the begin of process_ind_change, name is %s", name);
-	if (!strcmp(name, "\"call\"")) {
-		if (value > 0) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallStarted", DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-			gw->call_active = TRUE;
-		} else {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallEnded", DBUS_TYPE_INVALID);
-			gw->call_active = FALSE;
-		}
-
-	} else if (!strcmp(name, "\"callsetup\"")) {
-		if (value == 0 && gw->is_dialing) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-		} else if (!gw->is_dialing && value > 0)
-			gw->is_dialing = TRUE;
-
-	} else if (!strcmp(name, "\"callheld\"")) {
-		/* FIXME: The following code is based on assumptions only.
-		 * Has to be tested for interoperability
-		 * I assume that callheld=2 would be sent when dial from HF
-		 * failed in case of 3-way call
-		 * Unfortunately this path is not covered by the HF spec so
-		 * the code has to be tested for interop
-		*/
-		/* '2' means: all calls held, no active calls */
-		if (value == 2) {
-			if (gw->is_dialing) {
-				g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-				gw->is_dialing = FALSE;
-			}
-		}
-	} else if (!strcmp(name, "\"service\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RegistrationStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"signal\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "SignalStrength",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"roam\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RoamingStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"battchg\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "BatteryCharge",
-				DBUS_TYPE_UINT16, &value);
-}
-
-static void process_ring(struct audio_device *device, GIOChannel *chan,
-			gchar *buf)
-{
-	gchar number[AG_CALLER_NUM_SIZE];
-	gchar *cli;
-	gchar *sep;
-	gsize read;
-	GIOStatus status;
-
-	rfcomm_stop_watch(device);
-	status = g_io_channel_read_chars(chan, buf, RFCOMM_BUF_SIZE - 1, &read, NULL);
-	if (status != G_IO_STATUS_NORMAL)
-		return;
-
-	debug("at the begin of process_ring");
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 10)
-		error("process_ring(): buf is too long '%s'", buf);
-	else if ((cli = strstr(buf, "\r\n+CLIP"))) {
-		if (sscanf(cli, "\r\n+CLIP: \"%s", number) == 1) {
-			sep = strchr(number, '"');
-			sep[0] = '\0';
-
-			/* FIXME:signal will be emitted on each RING+CLIP.
-			 * That's bad */
-			cli = number;
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE, "Ring",
-					DBUS_TYPE_STRING, &cli,
-					DBUS_TYPE_INVALID);
-			device->gateway->is_dialing = TRUE;
-		} else
-			error("process_ring(): '%s' in place of +CLIP after RING", buf);
-
-	}
-
-	rfcomm_start_watch(device);
-}
-
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	struct gateway *gw;
-	gsize read;
-	/* some space for value */
-	gchar indicator[AG_INDICATOR_DESCR_SIZE + 4];
-	gint value;
-	guint index;
-	gchar *sep;
-
-	debug("at the begin of rfcomm_ag_data_cb()");
-	if (cond & G_IO_NVAL)
-		return FALSE;
-
-	gw = device->gateway;
-
-	if (cond & (G_IO_ERR | G_IO_HUP)) {
-		debug("connection with remote BT is closed");
-		gateway_close(device);
-		return FALSE;
-	}
-
-	if (g_io_channel_read_chars(chan, buf, sizeof(buf) - 1, &read, NULL)
-			!= G_IO_STATUS_NORMAL)
-		return TRUE;
-	buf[read] = '\0';
-
-	if (strlen(buf) > AG_INDICATOR_DESCR_SIZE + 14)
-		error("rfcomm_ag_data_cb(): buf is too long '%s'", buf);
-	else if (sscanf(buf, "\r\n+CIEV:%s\r\n", indicator) == 1) {
-		sep = strchr(indicator, ',');
-		sep[0] = '\0';
-		sep += 1;
-		index = atoi(indicator);
-		value = atoi(sep);
-		process_ind_change(device, index, value);
-	} else if (strstr(buf, "RING"))
-		process_ring(device, chan, buf);
-	else if (sscanf(buf, "\r\n+BVRA:%d\r\n", &value) == 1) {
-		if (value == 0)
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionActive",
-					DBUS_TYPE_INVALID);
-		else
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionInactive",
-					DBUS_TYPE_INVALID);
-	} else if (sscanf(buf, "\r\n+VGS:%d\r\n", &value) == 1) {
-		gw->sp_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "SpeakerGain",
-				DBUS_TYPE_UINT16, &value);
-	} else if (sscanf(buf, "\r\n+VGM:%d\r\n", &value) == 1) {
-		gw->mic_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "MicrophoneGain",
-				DBUS_TYPE_UINT16, &value);
-	} else
-		error("rfcomm_ag_data_cb(): read wrong data '%s'", buf);
-
-	return TRUE;
-}
-
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
 {
@@ -541,7 +118,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	DBusMessage *conn_mes = gw->connect_message;
 	gchar gw_addr[18];
 	GIOFlags flags;
 
@@ -563,29 +139,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 	if (!gw->rfcomm)
 		gw->rfcomm = g_io_channel_ref(chan);
 
-	if (establish_service_level_conn(dev->gateway)) {
-		gboolean value = TRUE;
-
-		debug("%s: Connected to %s", dev->path, gw_addr);
-		rfcomm_start_watch(dev);
-		if (conn_mes) {
-			DBusMessage *reply =
-				dbus_message_new_method_return(conn_mes);
-			dbus_connection_send(dev->conn, reply, NULL);
-			dbus_message_unref(reply);
-			dbus_message_unref(conn_mes);
-			gw->connect_message = NULL;
-		}
-
-		gw->state = GATEWAY_STATE_CONNECTED;
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN,	&value);
-		return;
-	} else
-		error("%s: Failed to establish service layer connection to %s",
-			dev->path, gw_addr);
-
 	if (NULL != gw->sco_start_cb)
 		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 
@@ -732,321 +285,20 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
-static DBusMessage *process_ag_reponse(DBusMessage *msg, gchar *response)
-{
-	DBusMessage *reply;
-
-
-	debug("in process_ag_reponse, response is %s", response);
-	if (strstr(response, OK_RESPONSE))
-		reply = dbus_message_new_method_return(msg);
-	else {
-		/* FIXME: some code should be here to processes errors
-		 *  in better fasion */
-		debug("AG responded with '%s' to %s method call", response,
-				dbus_message_get_member(msg));
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".OperationFailed",
-					"Operation failed.See log for details");
-	}
-	return reply;
-}
-
-static DBusMessage *process_simple(DBusMessage *msg, struct audio_device *dev,
-					gchar *data)
-{
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-
-	rfcomm_stop_watch(dev);
-	rfcomm_send_and_read(gw, data, buf, strlen(data));
-	rfcomm_start_watch(dev);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_ANSWER "ATA\r"
-
-static DBusMessage *ag_answer(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	if (gw->call_active)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".CallAlreadyAnswered",
-					"Call AlreadyAnswered");
-
-	return process_simple(msg, dev, AG_ANSWER);
-}
-
-#define AG_HANGUP "AT+CHUP\r"
-
-static DBusMessage *ag_terminate_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	return process_simple(msg, dev, AG_HANGUP);
-}
-
-/* according to GSM spec */
-#define ALLOWED_NUMBER_SYMBOLS "1234567890*#ABCD"
-#define AG_PLACE_CALL "ATD%s;\r"
-/* dialing from memory is not supported as headset spec doesn't define a way
- * to retreive phone memory entries.
- */
-static DBusMessage *ag_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint atd_len;
-	DBusMessage *result;
-
-	debug("at the begin of ag_call()");
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	atd_len = sprintf(buf, AG_PLACE_CALL, number);
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, buf, buf, atd_len);
-	rfcomm_start_watch(device);
-
-	result = process_ag_reponse(msg, buf);
-	return result;
-}
-
-#define AG_GET_CARRIER "AT+COPS?\r"
-
-static DBusMessage *ag_get_operator(DBusConnection *conn, DBusMessage *msg,
-					void *data)
-{
-	struct audio_device *dev = (struct audio_device *) data;
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read;
-	gchar *result, *sep;
-	DBusMessage *reply;
-	GIOStatus status;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(dev);
-	io_channel_write_all(rfcomm, AG_GET_CARRIER, strlen(AG_GET_CARRIER));
-
-	status = g_io_channel_read_chars(rfcomm, buf, RFCOMM_BUF_SIZE - 1,
-						&read, NULL);
-	rfcomm_start_watch(dev);
-	if (G_IO_STATUS_NORMAL == status) {
-		buf[read] = '\0';
-		if (strstr(buf, "+COPS")) {
-			if (!strrchr(buf, ','))
-				result = "0";
-			else {
-				result = strchr(buf, '\"') + 1;
-				sep = strchr(result, '\"');
-				sep[0] = '\0';
-			}
-
-			reply = dbus_message_new_method_return(msg);
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&result, DBUS_TYPE_INVALID);
-		} else {
-			info("ag_get_operator(): '+COPS' expected but"
-				" '%s' received", buf);
-			reply = dbus_message_new_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Unexpected response from AG");
-		}
-	} else {
-		error("ag_get_operator(): %m");
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".ConnectionFailed",
-					"Failed to receive response from AG");
-	}
-
-	return reply;
-}
-
-#define AG_SEND_DTMF "AT+VTS=%c\r"
-static DBusMessage *ag_send_dtmf(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint com_len;
-	gboolean got_ok = TRUE;
-	gint num_len;
-	gint i = 0;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	num_len = strlen(number);
-	rfcomm_stop_watch(device);
-	while (i < num_len && got_ok) {
-		com_len = sprintf(buf, AG_SEND_DTMF, number[i]);
-		rfcomm_send_and_read(gw, buf, buf, com_len);
-		got_ok = NULL != strstr(buf, OK_RESPONSE);
-		i += 1;
-	}
-	rfcomm_start_watch(device);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_GET_SUBSCRIBER_NUMS "AT+CNUM\r"
-#define CNUM_LEN 5             /* length of "+CNUM" string */
-#define MAX_NUMBER_CNT 16
-static DBusMessage *ag_get_subscriber_num(DBusConnection *conn,
-					DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number, *end;
-	DBusMessage *reply = dbus_message_new_method_return(msg);
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, AG_GET_SUBSCRIBER_NUMS, buf,
-			strlen(AG_GET_SUBSCRIBER_NUMS));
-	rfcomm_start_watch(device);
-
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 21)
-		error("ag_get_subscriber_num(): buf is too long '%s'", buf);
-	else if (strstr(buf, "+CNUM")) {
-		number = strchr(buf, ',');
-		number++;
-		end = strchr(number, ',');
-		if (end) {
-			*end = '\0';
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&number, DBUS_TYPE_INVALID);
-		}
-	} else
-		error("ag_get_subscriber_num(): read wrong data '%s'", buf);
-
-	return reply;
-}
-
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	DBusMessage *reply;
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	gboolean value;
-	guint index = 0;
-	struct indicator *ind;
-
-	reply = dbus_message_new_method_return(msg);
-	if (!reply)
-		return NULL;
-
-	dbus_message_iter_init_append(reply, &iter);
-
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
-	/* Connected */
-	value = gateway_is_connected(device);
-	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
-
-	if (!value)
-		goto done;
-
-	while ((ind = g_slist_nth_data(gw->indies, index))) {
-		if(!strcmp(ind->descr, "\"service\""))
-			dict_append_entry(&dict, "RegistrationStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"signal\""))
-			dict_append_entry(&dict, "SignalStrength",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"roam\""))
-			dict_append_entry(&dict, "RoamingStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"battchg\""))
-			dict_append_entry(&dict, "BatteryCharge",
-					DBUS_TYPE_UINT16, &ind->value);
-		index++;
-	}
-
-	/* SpeakerGain */
-	dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16,
-				&device->gateway->sp_gain);
-
-	/* MicrophoneGain */
-	dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16,
-				&device->gateway->mic_gain);
-done:
-	dbus_message_iter_close_container(&iter, &dict);
-	return reply;
+	return NULL;
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "Disconnect", "", "", ag_disconnect },
-	{ "AnswerCall", "", "", ag_answer },
-	{ "TerminateCall", "", "", ag_terminate_call },
-	{ "Call", "s", "", ag_call },
-	{ "GetOperatorName", "", "s", ag_get_operator },
-	{ "SendDTMF", "s", "", ag_send_dtmf },
-	{ "GetSubscriberNumber", "", "s", ag_get_subscriber_num },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
 	{ NULL, NULL, NULL, NULL }
 };
 
 static GDBusSignalTable gateway_signals[] = {
-	{ "Ring", "s" },
-	{ "CallTerminated", "" },
-	{ "CallStarted", "" },
-	{ "CallEnded", "" },
 	{ "PropertyChanged", "sv" },
 	{ NULL, NULL }
 };
@@ -1063,9 +315,6 @@ struct gateway *gateway_init(struct audio_device *dev)
 
 	debug("in gateway_init, dev is %p", dev);
 	gw = g_new0(struct gateway, 1);
-	gw->indies = NULL;
-	gw->is_dialing = FALSE;
-	gw->call_active = FALSE;
 	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
 
@@ -1107,11 +356,6 @@ void gateway_start_service(struct audio_device *device)
 	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
 }
 
-static void indicator_slice_free(gpointer mem)
-{
-	g_slice_free(struct indicator, mem);
-}
-
 int gateway_close(struct audio_device *device)
 {
 	struct gateway *gw = device->gateway;
@@ -1119,8 +363,6 @@ int gateway_close(struct audio_device *device)
 	GIOChannel *sco = gw->sco;
 	gboolean value = FALSE;
 
-	g_slist_foreach(gw->indies, (GFunc) indicator_slice_free, NULL);
-	g_slist_free(gw->indies);
 	if (rfcomm) {
 		g_io_channel_shutdown(rfcomm, TRUE, NULL);
 		g_io_channel_unref(rfcomm);
-- 
1.6.4.4


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

* [PATCH 2/2] Implement HandsfreeGateway Interface
  2010-01-27 19:12           ` Gustavo F. Padovan
@ 2010-01-27 19:12             ` Gustavo F. Padovan
  -1 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-27 19:12 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: ofono

Create a interface where a Handsfree agent can register and use BlueZ to
handle the rfcomm and sco links.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for his
prototype on this code.
---
 audio/gateway.c |  430 +++++++++++++++++++++++++++++++++++++++++++------------
 audio/gateway.h |   12 ++-
 audio/manager.c |   23 +--
 audio/unix.c    |    8 +
 doc/hfp-api.txt |   82 +++++++++++
 5 files changed, 443 insertions(+), 112 deletions(-)
 create mode 100644 doc/hfp-api.txt

diff --git a/audio/gateway.c b/audio/gateway.c
index 3dc09ff..9017074 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -5,6 +5,8 @@
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
  *  Copyright (C) 2008-2009  Leonid Movshovich <event.riga@gmail.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
+ *  Copyright (C) 2010  Gustavo F. Padovan <padovan@profusion.mobi>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -32,12 +34,15 @@
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
 
 #include <glib.h>
 #include <dbus/dbus.h>
 #include <gdbus.h>
 
 #include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
 #include <bluetooth/sco.h>
@@ -52,20 +57,108 @@
 #include "btio.h"
 #include "dbus-common.h"
 
-#define RFCOMM_BUF_SIZE 256
+#define MAX_OPEN_TRIES 5
+#define OPEN_WAIT 300
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
+struct hf_agent {
+	char *name;	/* Bus id */
+	char *path;	/* D-Bus path */
+	guint watch;	/* Disconnect watch */
+};
 
 struct gateway {
 	gateway_state_t state;
-	GIOChannel *rfcomm;
-	guint rfcomm_watch_id;
+	int channel;
+	GIOChannel *rfcomm;	/* remote AG requested rfcomm connection */
 	GIOChannel *sco;
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
-	DBusMessage *connect_message;
+	struct hf_agent *agent;
+	DBusMessage *msg;
 };
 
 int gateway_close(struct audio_device *device);
 
+static const char *state2str(gateway_state_t state)
+{
+	switch (state) {
+	case GATEWAY_STATE_DISCONNECTED:
+		return "disconnected";
+	case GATEWAY_STATE_CONNECTING:
+		return "connecting";
+	case GATEWAY_STATE_CONNECTED:
+		return "connected";
+	case GATEWAY_STATE_PLAYING:
+		return "playing";
+	default:
+		return "";
+	}
+}
+
+static void agent_free(struct hf_agent *agent)
+{
+	if (!agent)
+		return;
+
+	g_free(agent->name);
+	g_free(agent->path);
+	g_free(agent);
+}
+
+static void change_state(struct audio_device *dev, gateway_state_t new_state)
+{
+	struct gateway *gw = dev->gateway;
+	const char *val = state2str(new_state);
+
+	gw->state = new_state;
+
+	emit_property_changed(dev->conn, dev->path,
+			AUDIO_GATEWAY_INTERFACE, "State",
+			DBUS_TYPE_STRING, &val);
+}
+
+static void agent_disconnect(struct audio_device *dev, struct hf_agent *agent)
+{
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "Release");
+
+	dbus_message_set_no_reply(msg, TRUE);
+	g_dbus_send_message(dev->conn, msg);
+
+	g_dbus_remove_watch(dev->conn, agent->watch);
+
+	agent_free(agent);
+	dev->gateway->agent = NULL;
+}
+
+static gboolean agent_sendfd(struct hf_agent *agent, int fd,
+		DBusPendingCallNotifyFunction notify, void *data)
+{
+	struct audio_device *dev = data;
+	DBusMessage *msg;
+	DBusPendingCall *call;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "NewConnection");
+
+	dbus_message_append_args(msg, DBUS_TYPE_UNIX_FD, &fd,
+					DBUS_TYPE_INVALID);
+
+	if (dbus_connection_send_with_reply(dev->conn, msg, &call, -1) == FALSE)
+		return FALSE;
+
+	dbus_pending_call_set_notify(call, notify, dev, NULL);
+	dbus_pending_call_unref(call);
+
+	return TRUE;
+}
+
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
 {
@@ -79,6 +172,7 @@ static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 		g_io_channel_shutdown(gw->sco, TRUE, NULL);
 		g_io_channel_unref(gw->sco);
 		gw->sco = NULL;
+		change_state(dev, GATEWAY_STATE_CONNECTED);
 		return FALSE;
 	}
 
@@ -92,63 +186,91 @@ static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
 
 	debug("at the begin of sco_connect_cb() in gateway.c");
 
+	gw->sco = g_io_channel_ref(chan);
+
 	if (err) {
 		error("sco_connect_cb(): %s", err->message);
-		/* not sure, but from other point of view,
-		 * what is the reason to have headset which
-		 * cannot play audio? */
-		if (gw->sco_start_cb)
-			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 		gateway_close(dev);
 		return;
 	}
 
-	gw->sco = g_io_channel_ref(chan);
 	if (gw->sco_start_cb)
 		gw->sco_start_cb(dev, gw->sco_start_cb_data);
 
-	/* why is this here? */
-	fcntl(g_io_channel_unix_get_fd(chan), F_SETFL, 0);
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
 				(GIOFunc) sco_io_cb, dev);
 }
 
+static void newconnection_reply(DBusPendingCall *call, void *data)
+{
+	struct audio_device *dev = data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError derr;
+
+	if (!dev->gateway->rfcomm) {
+		debug("RFCOMM disconnected from server before agent reply");
+		return;
+	}
+
+	dbus_error_init(&derr);
+	if (!dbus_set_error_from_message(&derr, reply)) {
+		debug("Agent reply: file descriptor passed successfuly");
+		change_state(dev, GATEWAY_STATE_CONNECTED);
+		return;
+	}
+
+	debug("Agent reply: %s", derr.message);
+
+	dbus_error_free(&derr);
+	gateway_close(dev);
+}
+
 static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 				gpointer user_data)
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	gchar gw_addr[18];
-	GIOFlags flags;
+	DBusMessage *reply;
+	int sk;
+	char src[18], dst[18];
 
 	if (err) {
 		error("connect(): %s", err->message);
 		if (gw->sco_start_cb)
 			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
-		return;
+		goto fail;
 	}
 
-	ba2str(&dev->dst, gw_addr);
-	/* Blocking mode should be default, but just in case: */
-	flags = g_io_channel_get_flags(chan);
-	flags &= ~G_IO_FLAG_NONBLOCK;
-	flags &= G_IO_FLAG_MASK;
-	g_io_channel_set_flags(chan, flags, NULL);
-	g_io_channel_set_encoding(chan, NULL, NULL);
-	g_io_channel_set_buffered(chan, FALSE);
-	if (!gw->rfcomm)
-		gw->rfcomm = g_io_channel_ref(chan);
+	if (!gw->agent)
+		error("Handfree Agent not registered");
 
-	if (NULL != gw->sco_start_cb)
-		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
+	ba2str(&dev->src, src);
+	ba2str(&dev->dst, dst);
 
-	gateway_close(dev);
+	sk = g_io_channel_unix_get_fd(chan);
+
+	gw->rfcomm = g_io_channel_ref(chan);
+
+	if (!agent_sendfd(gw->agent, sk, newconnection_reply, dev)) {
+		reply = g_dbus_create_error(gw->msg, ERROR_INTERFACE ".Failed",
+				"Can not pass file descriptor");
+	} else
+		reply = dbus_message_new_method_return(gw->msg);
+
+	return;
+
+fail:
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".Failed", ".Connection attempt failed");
+	}
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
 }
 
 static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 {
 	struct audio_device *dev = user_data;
-	DBusMessage *msg = dev->gateway->connect_message;
+	struct gateway *gw = dev->gateway;
 	int ch = -1;
 	sdp_list_t *protos, *classes;
 	uuid_t uuid;
@@ -182,8 +304,6 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 
 	if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
 			uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
-		sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free,
-									NULL);
 		sdp_list_free(protos, NULL);
 		error("Invalid service record or not HFP");
 		goto fail;
@@ -197,6 +317,8 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 		goto fail;
 	}
 
+	gw->channel = ch;
+
 	io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -204,25 +326,24 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 				BT_IO_OPT_INVALID);
 	if (!io) {
 		error("Unable to connect: %s", err->message);
-		if (msg) {
-			error_common_reply(dev->conn, msg, ERROR_INTERFACE
-						".ConnectionAttemptFailed",
-						err->message);
-			msg = NULL;
-		}
-		g_error_free(err);
+		if (err)
+			g_error_free(err);
 		gateway_close(dev);
+		goto fail;
 	}
 
 	g_io_channel_unref(io);
+
+	change_state(dev, GATEWAY_STATE_CONNECTING);
 	return;
 
 fail:
-	if (msg)
-		error_common_reply(dev->conn, msg, ERROR_INTERFACE
-					".NotSupported", "Not supported");
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".NotSupported", "Not supported");
+	}
 
-	dev->gateway->connect_message = NULL;
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
 
 	sco_cb = dev->gateway->sco_start_cb;
 	if (sco_cb)
@@ -243,22 +364,60 @@ static DBusMessage *ag_connect(DBusConnection *conn, DBusMessage *msg,
 {
 	struct audio_device *au_dev = (struct audio_device *) data;
 	struct gateway *gw = au_dev->gateway;
+	DBusMessage *reply;
 
 	debug("at the begin of ag_connect()");
-	if (gw->rfcomm)
+
+	if (!gw->agent)
 		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".AlreadyConnected",
-					"Already Connected");
+				".Failed", "Agent not assined");
 
-	gw->connect_message = dbus_message_ref(msg);
 	if (get_records(au_dev) < 0) {
-		dbus_message_unref(gw->connect_message);
 		return g_dbus_create_error(msg, ERROR_INTERFACE
 					".ConnectAttemptFailed",
 					"Connect Attempt Failed");
 	}
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
 	debug("at the end of ag_connect()");
-	return NULL;
+
+	return reply;
+}
+
+void gateway_exit(struct audio_device *dev)
+{
+	agent_disconnect(dev, dev->gateway->agent);
+}
+
+int gateway_close(struct audio_device *device)
+{
+	struct gateway *gw = device->gateway;
+	gboolean value = FALSE;
+
+	if (gw->rfcomm) {
+		g_io_channel_shutdown(gw->rfcomm, TRUE, NULL);
+		g_io_channel_unref(gw->rfcomm);
+		gw->rfcomm = NULL;
+	}
+
+	if (gw->sco) {
+		g_io_channel_shutdown(gw->sco, TRUE, NULL);
+		g_io_channel_unref(gw->sco);
+		gw->sco = NULL;
+		gw->sco_start_cb = NULL;
+		gw->sco_start_cb_data = NULL;
+	}
+
+	gw->state = GATEWAY_STATE_DISCONNECTED;
+
+	emit_property_changed(device->conn, device->path,
+				AUDIO_GATEWAY_INTERFACE,
+				"Connected", DBUS_TYPE_BOOLEAN, &value);
+
+	return 0;
 }
 
 static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
@@ -269,6 +428,9 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	DBusMessage *reply = NULL;
 	char gw_addr[18];
 
+	if (!device->conn)
+		return NULL;
+
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
 		return NULL;
@@ -279,22 +441,119 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 						"Device not Connected");
 
 	gateway_close(device);
+
 	ba2str(&device->dst, gw_addr);
 	debug("Disconnected from %s, %s", gw_addr, device->path);
 
 	return reply;
 }
 
+static void agent_exited(DBusConnection *conn, void *data)
+{
+	struct gateway *gateway = data;
+	struct hf_agent *agent = gateway->agent;
+
+	debug("Agent %s exited", agent->name);
+
+	agent_free(agent);
+	gateway->agent = NULL;
+}
+
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	return NULL;
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	const char *value;
+
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	value = state2str(gw->state);
+	dict_append_entry(&dict, "Connected",
+			DBUS_TYPE_STRING, &value);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct hf_agent *agent;
+	const char *path, *name;
+
+
+	if (gw->agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".AlreadyExists",
+				"Agent already exists");
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+			DBUS_TYPE_INVALID))
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+				".InvalidArguments", "Invalid argument");
+
+	name = dbus_message_get_sender(msg);
+	agent = g_new0(struct hf_agent, 1);
+
+	agent->name = g_strdup(name);
+	agent->path = g_strdup(path);
+
+	agent->watch = g_dbus_add_disconnect_watch(conn, name,
+			agent_exited, gw, NULL);
+
+	gw->agent = agent;
+
+	gw->msg = dbus_message_ref(msg);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+				DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct hf_agent *agent;
+
+	if (!gw->agent)
+		goto done;
+
+	agent = gw->agent;
+	if (strcmp(agent->name, dbus_message_get_sender(msg)) != 0)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed", "Permission denied");
+
+	agent_disconnect(device, gw->agent);
+
+	dbus_message_unref(gw->msg);
+
+done:
+	return dbus_message_new_method_return(msg);
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
-	{ "Disconnect", "", "", ag_disconnect },
+	{ "Disconnect", "", "", ag_disconnect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
+	{ "RegisterAgent", "o", "", register_agent },
+	{ "UnregisterAgent", "o", "", unregister_agent },
 	{ NULL, NULL, NULL, NULL }
 };
 
@@ -305,18 +564,13 @@ static GDBusSignalTable gateway_signals[] = {
 
 struct gateway *gateway_init(struct audio_device *dev)
 {
-	struct gateway *gw;
-
 	if (!g_dbus_register_interface(dev->conn, dev->path,
 					AUDIO_GATEWAY_INTERFACE,
 					gateway_methods, gateway_signals,
 					NULL, dev, NULL))
 		return NULL;
 
-	debug("in gateway_init, dev is %p", dev);
-	gw = g_new0(struct gateway, 1);
-	gw->state = GATEWAY_STATE_DISCONNECTED;
-	return gw;
+	return g_new0(struct gateway, 1);
 
 }
 
@@ -326,17 +580,6 @@ gboolean gateway_is_connected(struct audio_device *dev)
 			dev->gateway->state == GATEWAY_STATE_CONNECTED);
 }
 
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
-{
-	if (!io)
-		return -EINVAL;
-
-	g_io_channel_ref(io);
-	dev->gateway->rfcomm = io;
-
-	return 0;
-}
-
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 {
 	struct gateway *gw = dev->gateway;
@@ -347,42 +590,38 @@ int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 	gw->sco = g_io_channel_ref(io);
 
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                                (GIOFunc) sco_io_cb, dev);
+				 (GIOFunc) sco_io_cb, dev);
+
+	change_state(dev, GATEWAY_STATE_PLAYING);
+
 	return 0;
 }
 
-void gateway_start_service(struct audio_device *device)
+int gateway_connect_rfcomm(struct audio_device *dev,
+		GIOChannel *chan, int ch)
 {
-	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
+	if (!chan)
+		return -EINVAL;
+
+	dev->gateway->rfcomm = g_io_channel_ref(chan);
+	dev->gateway->channel = ch;
+
+	return 0;
 }
 
-int gateway_close(struct audio_device *device)
+void gateway_start_service(struct audio_device *dev)
 {
-	struct gateway *gw = device->gateway;
-	GIOChannel *rfcomm = gw->rfcomm;
-	GIOChannel *sco = gw->sco;
-	gboolean value = FALSE;
+	struct gateway *gw = dev->gateway;
+	GError *err = NULL;
 
-	if (rfcomm) {
-		g_io_channel_shutdown(rfcomm, TRUE, NULL);
-		g_io_channel_unref(rfcomm);
-		gw->rfcomm = NULL;
-	}
+	if (gw->rfcomm == NULL)
+		return;
 
-	if (sco) {
-		g_io_channel_shutdown(sco, TRUE, NULL);
-		g_io_channel_unref(sco);
-		gw->sco = NULL;
-		gw->sco_start_cb = NULL;
-		gw->sco_start_cb_data = NULL;
+	if (!bt_io_accept(gw->rfcomm, rfcomm_connect_cb, dev, NULL,
+							&err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
 	}
-
-	gw->state = GATEWAY_STATE_DISCONNECTED;
-
-	emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN, &value);
-	return 0;
 }
 
 /* These are functions to be called from unix.c for audio system
@@ -399,8 +638,12 @@ gboolean gateway_request_stream(struct audio_device *dev,
 		gw->sco_start_cb_data = user_data;
 		get_records(dev);
 	} else if (!gw->sco) {
+		char source[128], destination[128];
+
 		gw->sco_start_cb = cb;
 		gw->sco_start_cb_data = user_data;
+		ba2str(&dev->src, source);
+		ba2str(&dev->dst, destination);
 		io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -464,4 +707,3 @@ void gateway_suspend_stream(struct audio_device *dev)
 	gw->sco_start_cb = NULL;
 	gw->sco_start_cb_data = NULL;
 }
-
diff --git a/audio/gateway.h b/audio/gateway.h
index 3b0457f..bf17a4f 100644
--- a/audio/gateway.h
+++ b/audio/gateway.h
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -22,20 +23,25 @@
  *
  */
 
-#define AUDIO_GATEWAY_INTERFACE "org.bluez.HeadsetGateway"
+#define AUDIO_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
 
 #define DEFAULT_HSP_HS_CHANNEL 6
 #define DEFAULT_HFP_HS_CHANNEL 7
 
 typedef enum {
 	GATEWAY_STATE_DISCONNECTED,
-	GATEWAY_STATE_CONNECTED
+	GATEWAY_STATE_CONNECTED,
+	GATEWAY_STATE_CONNECTING,
+	GATEWAY_STATE_PLAYING,
 } gateway_state_t;
 
 typedef void (*gateway_stream_cb_t) (struct audio_device *dev, void *user_data);
+
 struct gateway *gateway_init(struct audio_device *device);
+void gateway_exit(struct audio_device *device);
 gboolean gateway_is_connected(struct audio_device *dev);
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *chan);
+int gateway_connect_rfcomm(struct audio_device *dev,
+			GIOChannel *chan, int ch);
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *chan);
 void gateway_start_service(struct audio_device *device);
 gboolean gateway_request_stream(struct audio_device *dev,
diff --git a/audio/manager.c b/audio/manager.c
index 413c1f3..8c854fc 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -198,7 +198,7 @@ static void handle_uuid(const char *uuidstr, struct audio_device *device)
 		break;
 	case HANDSFREE_AGW_SVCLASS_ID:
 		debug("Found Handsfree AG record");
-		if (device->gateway == NULL)
+		if (enabled.gateway && (device->gateway == NULL))
 			device->gateway = gateway_init(device);
 		break;
 	case AUDIO_SINK_SVCLASS_ID:
@@ -567,8 +567,8 @@ static void hf_io_cb(GIOChannel *chan, gpointer data)
 		return;
 	}
 
-	server_uuid = HFP_HS_UUID;
-	remote_uuid = HFP_AG_UUID;
+	server_uuid = HFP_AG_UUID;
+	remote_uuid = HFP_HS_UUID;
 	svclass = HANDSFREE_AGW_SVCLASS_ID;
 
 	device = manager_get_device(&src, &dst, TRUE);
@@ -586,7 +586,7 @@ static void hf_io_cb(GIOChannel *chan, gpointer data)
 		goto drop;
 	}
 
-	if (gateway_connect_rfcomm(device, chan) < 0) {
+	if (gateway_connect_rfcomm(device, chan, ch) < 0) {
 		error("Allocating new GIOChannel failed!");
 		goto drop;
 	}
@@ -791,6 +791,9 @@ static void audio_remove(struct btd_device *device)
 	if (!dev)
 		return;
 
+	if (dev->gateway)
+		gateway_exit(dev);
+
 	devices = g_slist_remove(devices, dev);
 
 	audio_device_unregister(dev);
@@ -905,22 +908,12 @@ static void headset_server_remove(struct btd_adapter *adapter)
 static int gateway_server_probe(struct btd_adapter *adapter)
 {
 	struct audio_adapter *adp;
-	const gchar *path = adapter_get_path(adapter);
-	int ret;
-
-	DBG("path %s", path);
 
 	adp = audio_adapter_get(adapter);
 	if (!adp)
 		return -EINVAL;
 
-	ret = gateway_server_init(adp);
-	if (ret < 0) {
-		audio_adapter_ref(adp);
-		return ret;
-	}
-
-	return 0;
+	return gateway_server_init(adp);
 }
 
 static void gateway_server_remove(struct btd_adapter *adapter)
diff --git a/audio/unix.c b/audio/unix.c
index 5cf4f94..bd1a415 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -395,6 +395,9 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	struct bt_start_stream_rsp *rsp = (void *) buf;
 	struct bt_new_stream_ind *ind = (void *) buf;
 
+	if (!dev)
+		goto failed;
+
 	memset(buf, 0, sizeof(buf));
 	rsp->h.type = BT_RESPONSE;
 	rsp->h.name = BT_START_STREAM;
@@ -416,6 +419,11 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	}
 
 	client->req_id = 0;
+	return;
+
+failed:
+	error("gateway_resume_complete: resume failed");
+	unix_ipc_error(client, BT_START_STREAM, EIO);
 }
 
 static void headset_suspend_complete(struct audio_device *dev, void *user_data)
diff --git a/doc/hfp-api.txt b/doc/hfp-api.txt
new file mode 100644
index 0000000..8180de0
--- /dev/null
+++ b/doc/hfp-api.txt
@@ -0,0 +1,82 @@
+Gateway hierarchy
+========================
+
+Service		org.bluez
+Interface	org.bluez.HandsfreeGateway
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+This interface is available for remote devices which can function in the Audio
+Gateway role of the HFP profiles.  It is intended to be used with external
+telephony stacks / handlers of the HFP protocol.
+
+Methods		void Connect()
+
+			Connect to the AG service on the remote device.
+
+		void Disconnect()
+
+			Disconnect from the AG service on the remote device
+
+		dict GetProperties()
+
+			Returns all properties for the interface. See the
+			properties section for available properties.
+
+		void RegisterAgent(object path)
+
+			The object path defines the path the of the agent
+			that will be called when a new Handsfree connection
+			is established.
+
+			If an application disconnects from the bus all of its
+			registered agents will be removed.
+
+		void UnregisterAgent(object path)
+
+			This unregisters the agent that has been previously
+			registered. The object path parameter must match the
+			same value that has been used on registration.
+
+Signals		PropertyChanged(string name, variant value)
+
+			This signal indicates a changed value of the given
+			property.
+
+Properties	string State [readonly]
+
+			Indicates the state of the connection.  Possible
+			values are:
+				"disconnected"
+				"connecting"
+				"connected"
+				"playing"
+
+HandsfreeAgent hierarchy
+===============
+
+Service         unique name
+Interface       org.bluez.HandsfreeAgent
+Object path     freely definable
+
+Methods		void NewConnection(filedescriptor fd)
+
+			This method gets called whenever a new handsfree
+			connection has been established.  The objectpath
+			contains the object path of the remote device.  This
+			method assumes that DBus daemon with file descriptor
+			passing capability is being used.
+
+			The agent should only return successfully once the
+			establishment of the service level connection (SLC)
+			has been completed.  In the case of Handsfree this
+			means that BRSF exchange has been performed and
+			necessary initialization has been done.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.Failed
+
+		void Release()
+
+			This method gets called whenever the service daemon
+			unregisters the agent or whenever the Adapter where
+			the HandsfreeAgent registers itself is removed.
-- 
1.6.4.4


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

* [PATCH 2/2] Implement HandsfreeGateway Interface
@ 2010-01-27 19:12             ` Gustavo F. Padovan
  0 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-27 19:12 UTC (permalink / raw)
  To: ofono

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

Create a interface where a Handsfree agent can register and use BlueZ to
handle the rfcomm and sco links.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for his
prototype on this code.
---
 audio/gateway.c |  430 +++++++++++++++++++++++++++++++++++++++++++------------
 audio/gateway.h |   12 ++-
 audio/manager.c |   23 +--
 audio/unix.c    |    8 +
 doc/hfp-api.txt |   82 +++++++++++
 5 files changed, 443 insertions(+), 112 deletions(-)
 create mode 100644 doc/hfp-api.txt

diff --git a/audio/gateway.c b/audio/gateway.c
index 3dc09ff..9017074 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -5,6 +5,8 @@
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
  *  Copyright (C) 2008-2009  Leonid Movshovich <event.riga@gmail.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
+ *  Copyright (C) 2010  Gustavo F. Padovan <padovan@profusion.mobi>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -32,12 +34,15 @@
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
 
 #include <glib.h>
 #include <dbus/dbus.h>
 #include <gdbus.h>
 
 #include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
 #include <bluetooth/sco.h>
@@ -52,20 +57,108 @@
 #include "btio.h"
 #include "dbus-common.h"
 
-#define RFCOMM_BUF_SIZE 256
+#define MAX_OPEN_TRIES 5
+#define OPEN_WAIT 300
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
+struct hf_agent {
+	char *name;	/* Bus id */
+	char *path;	/* D-Bus path */
+	guint watch;	/* Disconnect watch */
+};
 
 struct gateway {
 	gateway_state_t state;
-	GIOChannel *rfcomm;
-	guint rfcomm_watch_id;
+	int channel;
+	GIOChannel *rfcomm;	/* remote AG requested rfcomm connection */
 	GIOChannel *sco;
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
-	DBusMessage *connect_message;
+	struct hf_agent *agent;
+	DBusMessage *msg;
 };
 
 int gateway_close(struct audio_device *device);
 
+static const char *state2str(gateway_state_t state)
+{
+	switch (state) {
+	case GATEWAY_STATE_DISCONNECTED:
+		return "disconnected";
+	case GATEWAY_STATE_CONNECTING:
+		return "connecting";
+	case GATEWAY_STATE_CONNECTED:
+		return "connected";
+	case GATEWAY_STATE_PLAYING:
+		return "playing";
+	default:
+		return "";
+	}
+}
+
+static void agent_free(struct hf_agent *agent)
+{
+	if (!agent)
+		return;
+
+	g_free(agent->name);
+	g_free(agent->path);
+	g_free(agent);
+}
+
+static void change_state(struct audio_device *dev, gateway_state_t new_state)
+{
+	struct gateway *gw = dev->gateway;
+	const char *val = state2str(new_state);
+
+	gw->state = new_state;
+
+	emit_property_changed(dev->conn, dev->path,
+			AUDIO_GATEWAY_INTERFACE, "State",
+			DBUS_TYPE_STRING, &val);
+}
+
+static void agent_disconnect(struct audio_device *dev, struct hf_agent *agent)
+{
+	DBusMessage *msg;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "Release");
+
+	dbus_message_set_no_reply(msg, TRUE);
+	g_dbus_send_message(dev->conn, msg);
+
+	g_dbus_remove_watch(dev->conn, agent->watch);
+
+	agent_free(agent);
+	dev->gateway->agent = NULL;
+}
+
+static gboolean agent_sendfd(struct hf_agent *agent, int fd,
+		DBusPendingCallNotifyFunction notify, void *data)
+{
+	struct audio_device *dev = data;
+	DBusMessage *msg;
+	DBusPendingCall *call;
+
+	msg = dbus_message_new_method_call(agent->name, agent->path,
+			"org.bluez.HandsfreeAgent", "NewConnection");
+
+	dbus_message_append_args(msg, DBUS_TYPE_UNIX_FD, &fd,
+					DBUS_TYPE_INVALID);
+
+	if (dbus_connection_send_with_reply(dev->conn, msg, &call, -1) == FALSE)
+		return FALSE;
+
+	dbus_pending_call_set_notify(call, notify, dev, NULL);
+	dbus_pending_call_unref(call);
+
+	return TRUE;
+}
+
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
 {
@@ -79,6 +172,7 @@ static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 		g_io_channel_shutdown(gw->sco, TRUE, NULL);
 		g_io_channel_unref(gw->sco);
 		gw->sco = NULL;
+		change_state(dev, GATEWAY_STATE_CONNECTED);
 		return FALSE;
 	}
 
@@ -92,63 +186,91 @@ static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
 
 	debug("at the begin of sco_connect_cb() in gateway.c");
 
+	gw->sco = g_io_channel_ref(chan);
+
 	if (err) {
 		error("sco_connect_cb(): %s", err->message);
-		/* not sure, but from other point of view,
-		 * what is the reason to have headset which
-		 * cannot play audio? */
-		if (gw->sco_start_cb)
-			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 		gateway_close(dev);
 		return;
 	}
 
-	gw->sco = g_io_channel_ref(chan);
 	if (gw->sco_start_cb)
 		gw->sco_start_cb(dev, gw->sco_start_cb_data);
 
-	/* why is this here? */
-	fcntl(g_io_channel_unix_get_fd(chan), F_SETFL, 0);
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
 				(GIOFunc) sco_io_cb, dev);
 }
 
+static void newconnection_reply(DBusPendingCall *call, void *data)
+{
+	struct audio_device *dev = data;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	DBusError derr;
+
+	if (!dev->gateway->rfcomm) {
+		debug("RFCOMM disconnected from server before agent reply");
+		return;
+	}
+
+	dbus_error_init(&derr);
+	if (!dbus_set_error_from_message(&derr, reply)) {
+		debug("Agent reply: file descriptor passed successfuly");
+		change_state(dev, GATEWAY_STATE_CONNECTED);
+		return;
+	}
+
+	debug("Agent reply: %s", derr.message);
+
+	dbus_error_free(&derr);
+	gateway_close(dev);
+}
+
 static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 				gpointer user_data)
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	gchar gw_addr[18];
-	GIOFlags flags;
+	DBusMessage *reply;
+	int sk;
+	char src[18], dst[18];
 
 	if (err) {
 		error("connect(): %s", err->message);
 		if (gw->sco_start_cb)
 			gw->sco_start_cb(NULL, gw->sco_start_cb_data);
-		return;
+		goto fail;
 	}
 
-	ba2str(&dev->dst, gw_addr);
-	/* Blocking mode should be default, but just in case: */
-	flags = g_io_channel_get_flags(chan);
-	flags &= ~G_IO_FLAG_NONBLOCK;
-	flags &= G_IO_FLAG_MASK;
-	g_io_channel_set_flags(chan, flags, NULL);
-	g_io_channel_set_encoding(chan, NULL, NULL);
-	g_io_channel_set_buffered(chan, FALSE);
-	if (!gw->rfcomm)
-		gw->rfcomm = g_io_channel_ref(chan);
+	if (!gw->agent)
+		error("Handfree Agent not registered");
 
-	if (NULL != gw->sco_start_cb)
-		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
+	ba2str(&dev->src, src);
+	ba2str(&dev->dst, dst);
 
-	gateway_close(dev);
+	sk = g_io_channel_unix_get_fd(chan);
+
+	gw->rfcomm = g_io_channel_ref(chan);
+
+	if (!agent_sendfd(gw->agent, sk, newconnection_reply, dev)) {
+		reply = g_dbus_create_error(gw->msg, ERROR_INTERFACE ".Failed",
+				"Can not pass file descriptor");
+	} else
+		reply = dbus_message_new_method_return(gw->msg);
+
+	return;
+
+fail:
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".Failed", ".Connection attempt failed");
+	}
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
 }
 
 static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 {
 	struct audio_device *dev = user_data;
-	DBusMessage *msg = dev->gateway->connect_message;
+	struct gateway *gw = dev->gateway;
 	int ch = -1;
 	sdp_list_t *protos, *classes;
 	uuid_t uuid;
@@ -182,8 +304,6 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 
 	if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
 			uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
-		sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free,
-									NULL);
 		sdp_list_free(protos, NULL);
 		error("Invalid service record or not HFP");
 		goto fail;
@@ -197,6 +317,8 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 		goto fail;
 	}
 
+	gw->channel = ch;
+
 	io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -204,25 +326,24 @@ static void get_record_cb(sdp_list_t *recs, int perr, gpointer user_data)
 				BT_IO_OPT_INVALID);
 	if (!io) {
 		error("Unable to connect: %s", err->message);
-		if (msg) {
-			error_common_reply(dev->conn, msg, ERROR_INTERFACE
-						".ConnectionAttemptFailed",
-						err->message);
-			msg = NULL;
-		}
-		g_error_free(err);
+		if (err)
+			g_error_free(err);
 		gateway_close(dev);
+		goto fail;
 	}
 
 	g_io_channel_unref(io);
+
+	change_state(dev, GATEWAY_STATE_CONNECTING);
 	return;
 
 fail:
-	if (msg)
-		error_common_reply(dev->conn, msg, ERROR_INTERFACE
-					".NotSupported", "Not supported");
+	if (gw->msg) {
+		error_common_reply(dev->conn, gw->msg, ERROR_INTERFACE
+				".NotSupported", "Not supported");
+	}
 
-	dev->gateway->connect_message = NULL;
+	change_state(dev, GATEWAY_STATE_DISCONNECTED);
 
 	sco_cb = dev->gateway->sco_start_cb;
 	if (sco_cb)
@@ -243,22 +364,60 @@ static DBusMessage *ag_connect(DBusConnection *conn, DBusMessage *msg,
 {
 	struct audio_device *au_dev = (struct audio_device *) data;
 	struct gateway *gw = au_dev->gateway;
+	DBusMessage *reply;
 
 	debug("at the begin of ag_connect()");
-	if (gw->rfcomm)
+
+	if (!gw->agent)
 		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".AlreadyConnected",
-					"Already Connected");
+				".Failed", "Agent not assined");
 
-	gw->connect_message = dbus_message_ref(msg);
 	if (get_records(au_dev) < 0) {
-		dbus_message_unref(gw->connect_message);
 		return g_dbus_create_error(msg, ERROR_INTERFACE
 					".ConnectAttemptFailed",
 					"Connect Attempt Failed");
 	}
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
 	debug("at the end of ag_connect()");
-	return NULL;
+
+	return reply;
+}
+
+void gateway_exit(struct audio_device *dev)
+{
+	agent_disconnect(dev, dev->gateway->agent);
+}
+
+int gateway_close(struct audio_device *device)
+{
+	struct gateway *gw = device->gateway;
+	gboolean value = FALSE;
+
+	if (gw->rfcomm) {
+		g_io_channel_shutdown(gw->rfcomm, TRUE, NULL);
+		g_io_channel_unref(gw->rfcomm);
+		gw->rfcomm = NULL;
+	}
+
+	if (gw->sco) {
+		g_io_channel_shutdown(gw->sco, TRUE, NULL);
+		g_io_channel_unref(gw->sco);
+		gw->sco = NULL;
+		gw->sco_start_cb = NULL;
+		gw->sco_start_cb_data = NULL;
+	}
+
+	gw->state = GATEWAY_STATE_DISCONNECTED;
+
+	emit_property_changed(device->conn, device->path,
+				AUDIO_GATEWAY_INTERFACE,
+				"Connected", DBUS_TYPE_BOOLEAN, &value);
+
+	return 0;
 }
 
 static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
@@ -269,6 +428,9 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	DBusMessage *reply = NULL;
 	char gw_addr[18];
 
+	if (!device->conn)
+		return NULL;
+
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
 		return NULL;
@@ -279,22 +441,119 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 						"Device not Connected");
 
 	gateway_close(device);
+
 	ba2str(&device->dst, gw_addr);
 	debug("Disconnected from %s, %s", gw_addr, device->path);
 
 	return reply;
 }
 
+static void agent_exited(DBusConnection *conn, void *data)
+{
+	struct gateway *gateway = data;
+	struct hf_agent *agent = gateway->agent;
+
+	debug("Agent %s exited", agent->name);
+
+	agent_free(agent);
+	gateway->agent = NULL;
+}
+
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	return NULL;
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	const char *value;
+
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+	value = state2str(gw->state);
+	dict_append_entry(&dict, "Connected",
+			DBUS_TYPE_STRING, &value);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct hf_agent *agent;
+	const char *path, *name;
+
+
+	if (gw->agent)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".AlreadyExists",
+				"Agent already exists");
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+			DBUS_TYPE_INVALID))
+		return g_dbus_create_error(msg, ERROR_INTERFACE
+				".InvalidArguments", "Invalid argument");
+
+	name = dbus_message_get_sender(msg);
+	agent = g_new0(struct hf_agent, 1);
+
+	agent->name = g_strdup(name);
+	agent->path = g_strdup(path);
+
+	agent->watch = g_dbus_add_disconnect_watch(conn, name,
+			agent_exited, gw, NULL);
+
+	gw->agent = agent;
+
+	gw->msg = dbus_message_ref(msg);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+				DBusMessage *msg, void *data)
+{
+	struct audio_device *device = data;
+	struct gateway *gw = device->gateway;
+	struct hf_agent *agent;
+
+	if (!gw->agent)
+		goto done;
+
+	agent = gw->agent;
+	if (strcmp(agent->name, dbus_message_get_sender(msg)) != 0)
+		return g_dbus_create_error(msg,
+				ERROR_INTERFACE ".Failed", "Permission denied");
+
+	agent_disconnect(device, gw->agent);
+
+	dbus_message_unref(gw->msg);
+
+done:
+	return dbus_message_new_method_return(msg);
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
-	{ "Disconnect", "", "", ag_disconnect },
+	{ "Disconnect", "", "", ag_disconnect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
+	{ "RegisterAgent", "o", "", register_agent },
+	{ "UnregisterAgent", "o", "", unregister_agent },
 	{ NULL, NULL, NULL, NULL }
 };
 
@@ -305,18 +564,13 @@ static GDBusSignalTable gateway_signals[] = {
 
 struct gateway *gateway_init(struct audio_device *dev)
 {
-	struct gateway *gw;
-
 	if (!g_dbus_register_interface(dev->conn, dev->path,
 					AUDIO_GATEWAY_INTERFACE,
 					gateway_methods, gateway_signals,
 					NULL, dev, NULL))
 		return NULL;
 
-	debug("in gateway_init, dev is %p", dev);
-	gw = g_new0(struct gateway, 1);
-	gw->state = GATEWAY_STATE_DISCONNECTED;
-	return gw;
+	return g_new0(struct gateway, 1);
 
 }
 
@@ -326,17 +580,6 @@ gboolean gateway_is_connected(struct audio_device *dev)
 			dev->gateway->state == GATEWAY_STATE_CONNECTED);
 }
 
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
-{
-	if (!io)
-		return -EINVAL;
-
-	g_io_channel_ref(io);
-	dev->gateway->rfcomm = io;
-
-	return 0;
-}
-
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 {
 	struct gateway *gw = dev->gateway;
@@ -347,42 +590,38 @@ int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
 	gw->sco = g_io_channel_ref(io);
 
 	g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                                (GIOFunc) sco_io_cb, dev);
+				 (GIOFunc) sco_io_cb, dev);
+
+	change_state(dev, GATEWAY_STATE_PLAYING);
+
 	return 0;
 }
 
-void gateway_start_service(struct audio_device *device)
+int gateway_connect_rfcomm(struct audio_device *dev,
+		GIOChannel *chan, int ch)
 {
-	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
+	if (!chan)
+		return -EINVAL;
+
+	dev->gateway->rfcomm = g_io_channel_ref(chan);
+	dev->gateway->channel = ch;
+
+	return 0;
 }
 
-int gateway_close(struct audio_device *device)
+void gateway_start_service(struct audio_device *dev)
 {
-	struct gateway *gw = device->gateway;
-	GIOChannel *rfcomm = gw->rfcomm;
-	GIOChannel *sco = gw->sco;
-	gboolean value = FALSE;
+	struct gateway *gw = dev->gateway;
+	GError *err = NULL;
 
-	if (rfcomm) {
-		g_io_channel_shutdown(rfcomm, TRUE, NULL);
-		g_io_channel_unref(rfcomm);
-		gw->rfcomm = NULL;
-	}
+	if (gw->rfcomm == NULL)
+		return;
 
-	if (sco) {
-		g_io_channel_shutdown(sco, TRUE, NULL);
-		g_io_channel_unref(sco);
-		gw->sco = NULL;
-		gw->sco_start_cb = NULL;
-		gw->sco_start_cb_data = NULL;
+	if (!bt_io_accept(gw->rfcomm, rfcomm_connect_cb, dev, NULL,
+							&err)) {
+		error("bt_io_accept: %s", err->message);
+		g_error_free(err);
 	}
-
-	gw->state = GATEWAY_STATE_DISCONNECTED;
-
-	emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN, &value);
-	return 0;
 }
 
 /* These are functions to be called from unix.c for audio system
@@ -399,8 +638,12 @@ gboolean gateway_request_stream(struct audio_device *dev,
 		gw->sco_start_cb_data = user_data;
 		get_records(dev);
 	} else if (!gw->sco) {
+		char source[128], destination[128];
+
 		gw->sco_start_cb = cb;
 		gw->sco_start_cb_data = user_data;
+		ba2str(&dev->src, source);
+		ba2str(&dev->dst, destination);
 		io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err,
 				BT_IO_OPT_SOURCE_BDADDR, &dev->src,
 				BT_IO_OPT_DEST_BDADDR, &dev->dst,
@@ -464,4 +707,3 @@ void gateway_suspend_stream(struct audio_device *dev)
 	gw->sco_start_cb = NULL;
 	gw->sco_start_cb_data = NULL;
 }
-
diff --git a/audio/gateway.h b/audio/gateway.h
index 3b0457f..bf17a4f 100644
--- a/audio/gateway.h
+++ b/audio/gateway.h
@@ -4,6 +4,7 @@
  *
  *  Copyright (C) 2006-2010  Nokia Corporation
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2009  Zhenhua Zhang <zhenhua.zhang@intel.com>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -22,20 +23,25 @@
  *
  */
 
-#define AUDIO_GATEWAY_INTERFACE "org.bluez.HeadsetGateway"
+#define AUDIO_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
 
 #define DEFAULT_HSP_HS_CHANNEL 6
 #define DEFAULT_HFP_HS_CHANNEL 7
 
 typedef enum {
 	GATEWAY_STATE_DISCONNECTED,
-	GATEWAY_STATE_CONNECTED
+	GATEWAY_STATE_CONNECTED,
+	GATEWAY_STATE_CONNECTING,
+	GATEWAY_STATE_PLAYING,
 } gateway_state_t;
 
 typedef void (*gateway_stream_cb_t) (struct audio_device *dev, void *user_data);
+
 struct gateway *gateway_init(struct audio_device *device);
+void gateway_exit(struct audio_device *device);
 gboolean gateway_is_connected(struct audio_device *dev);
-int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *chan);
+int gateway_connect_rfcomm(struct audio_device *dev,
+			GIOChannel *chan, int ch);
 int gateway_connect_sco(struct audio_device *dev, GIOChannel *chan);
 void gateway_start_service(struct audio_device *device);
 gboolean gateway_request_stream(struct audio_device *dev,
diff --git a/audio/manager.c b/audio/manager.c
index 413c1f3..8c854fc 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -198,7 +198,7 @@ static void handle_uuid(const char *uuidstr, struct audio_device *device)
 		break;
 	case HANDSFREE_AGW_SVCLASS_ID:
 		debug("Found Handsfree AG record");
-		if (device->gateway == NULL)
+		if (enabled.gateway && (device->gateway == NULL))
 			device->gateway = gateway_init(device);
 		break;
 	case AUDIO_SINK_SVCLASS_ID:
@@ -567,8 +567,8 @@ static void hf_io_cb(GIOChannel *chan, gpointer data)
 		return;
 	}
 
-	server_uuid = HFP_HS_UUID;
-	remote_uuid = HFP_AG_UUID;
+	server_uuid = HFP_AG_UUID;
+	remote_uuid = HFP_HS_UUID;
 	svclass = HANDSFREE_AGW_SVCLASS_ID;
 
 	device = manager_get_device(&src, &dst, TRUE);
@@ -586,7 +586,7 @@ static void hf_io_cb(GIOChannel *chan, gpointer data)
 		goto drop;
 	}
 
-	if (gateway_connect_rfcomm(device, chan) < 0) {
+	if (gateway_connect_rfcomm(device, chan, ch) < 0) {
 		error("Allocating new GIOChannel failed!");
 		goto drop;
 	}
@@ -791,6 +791,9 @@ static void audio_remove(struct btd_device *device)
 	if (!dev)
 		return;
 
+	if (dev->gateway)
+		gateway_exit(dev);
+
 	devices = g_slist_remove(devices, dev);
 
 	audio_device_unregister(dev);
@@ -905,22 +908,12 @@ static void headset_server_remove(struct btd_adapter *adapter)
 static int gateway_server_probe(struct btd_adapter *adapter)
 {
 	struct audio_adapter *adp;
-	const gchar *path = adapter_get_path(adapter);
-	int ret;
-
-	DBG("path %s", path);
 
 	adp = audio_adapter_get(adapter);
 	if (!adp)
 		return -EINVAL;
 
-	ret = gateway_server_init(adp);
-	if (ret < 0) {
-		audio_adapter_ref(adp);
-		return ret;
-	}
-
-	return 0;
+	return gateway_server_init(adp);
 }
 
 static void gateway_server_remove(struct btd_adapter *adapter)
diff --git a/audio/unix.c b/audio/unix.c
index 5cf4f94..bd1a415 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -395,6 +395,9 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	struct bt_start_stream_rsp *rsp = (void *) buf;
 	struct bt_new_stream_ind *ind = (void *) buf;
 
+	if (!dev)
+		goto failed;
+
 	memset(buf, 0, sizeof(buf));
 	rsp->h.type = BT_RESPONSE;
 	rsp->h.name = BT_START_STREAM;
@@ -416,6 +419,11 @@ static void gateway_resume_complete(struct audio_device *dev, void *user_data)
 	}
 
 	client->req_id = 0;
+	return;
+
+failed:
+	error("gateway_resume_complete: resume failed");
+	unix_ipc_error(client, BT_START_STREAM, EIO);
 }
 
 static void headset_suspend_complete(struct audio_device *dev, void *user_data)
diff --git a/doc/hfp-api.txt b/doc/hfp-api.txt
new file mode 100644
index 0000000..8180de0
--- /dev/null
+++ b/doc/hfp-api.txt
@@ -0,0 +1,82 @@
+Gateway hierarchy
+========================
+
+Service		org.bluez
+Interface	org.bluez.HandsfreeGateway
+Object path	[variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+This interface is available for remote devices which can function in the Audio
+Gateway role of the HFP profiles.  It is intended to be used with external
+telephony stacks / handlers of the HFP protocol.
+
+Methods		void Connect()
+
+			Connect to the AG service on the remote device.
+
+		void Disconnect()
+
+			Disconnect from the AG service on the remote device
+
+		dict GetProperties()
+
+			Returns all properties for the interface. See the
+			properties section for available properties.
+
+		void RegisterAgent(object path)
+
+			The object path defines the path the of the agent
+			that will be called when a new Handsfree connection
+			is established.
+
+			If an application disconnects from the bus all of its
+			registered agents will be removed.
+
+		void UnregisterAgent(object path)
+
+			This unregisters the agent that has been previously
+			registered. The object path parameter must match the
+			same value that has been used on registration.
+
+Signals		PropertyChanged(string name, variant value)
+
+			This signal indicates a changed value of the given
+			property.
+
+Properties	string State [readonly]
+
+			Indicates the state of the connection.  Possible
+			values are:
+				"disconnected"
+				"connecting"
+				"connected"
+				"playing"
+
+HandsfreeAgent hierarchy
+===============
+
+Service         unique name
+Interface       org.bluez.HandsfreeAgent
+Object path     freely definable
+
+Methods		void NewConnection(filedescriptor fd)
+
+			This method gets called whenever a new handsfree
+			connection has been established.  The objectpath
+			contains the object path of the remote device.  This
+			method assumes that DBus daemon with file descriptor
+			passing capability is being used.
+
+			The agent should only return successfully once the
+			establishment of the service level connection (SLC)
+			has been completed.  In the case of Handsfree this
+			means that BRSF exchange has been performed and
+			necessary initialization has been done.
+
+			Possible Errors: org.bluez.Error.InvalidArguments
+					 org.bluez.Error.Failed
+
+		void Release()
+
+			This method gets called whenever the service daemon
+			unregisters the agent or whenever the Adapter where
+			the HandsfreeAgent registers itself is removed.
-- 
1.6.4.4


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

* [PATCH] Add HFP support through BlueZ
  2010-01-27 19:12             ` Gustavo F. Padovan
@ 2010-01-27 19:12               ` Gustavo F. Padovan
  -1 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-27 19:12 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: ofono

It uses BlueZ through to get HFP working following the
org.bluez.HandsfreeGateway and org.bluez.HandsfreeAgent from the BlueZ
D-Bus API.

You need the HFP suport into BlueZ and the new dbus 1.3 with fd-passing
support.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for its prototype
on this code.
---
 drivers/hfpmodem/hfpmodem.h |    4 +
 plugins/hfp.c               |  477 +++++++++++++++++++++++++++++++++++++++++--
 plugins/modemconf.c         |    1 -
 3 files changed, 466 insertions(+), 16 deletions(-)

diff --git a/drivers/hfpmodem/hfpmodem.h b/drivers/hfpmodem/hfpmodem.h
index 509846b..6f0ab1f 100644
--- a/drivers/hfpmodem/hfpmodem.h
+++ b/drivers/hfpmodem/hfpmodem.h
@@ -22,6 +22,7 @@
 #define __BLUETOOTH_H__
 
 #include <drivers/atmodem/atutil.h>
+#include <ofono/dbus.h>
 
 /* AG supported features bitmap. Bluetooth HFP 1.5 spec page 77 */
 #define AG_FEATURE_3WAY 0x1
@@ -63,11 +64,14 @@ enum hfp_indicator {
 
 struct hfp_data {
 	GAtChat *chat;
+	char *handsfree_path;
+	DBusMessage *slc_msg;
 	unsigned int ag_features;
 	unsigned int ag_mpty_features;
 	unsigned int hf_features;
 	unsigned char cind_pos[HFP_INDICATOR_LAST];
 	unsigned int cind_val[HFP_INDICATOR_LAST];
+	unsigned int at_timeout;
 };
 
 extern void hfp_netreg_init();
diff --git a/plugins/hfp.c b/plugins/hfp.c
index 3bbd922..12b7f0a 100644
--- a/plugins/hfp.c
+++ b/plugins/hfp.c
@@ -30,6 +30,8 @@
 #include <glib.h>
 #include <gatchat.h>
 #include <gattty.h>
+#include <gdbus.h>
+#include <ofono.h>
 
 #define OFONO_API_SUBJECT_TO_CHANGE
 #include <ofono/plugin.h>
@@ -52,12 +54,31 @@
 
 #include <drivers/hfpmodem/hfpmodem.h>
 
+#include <ofono/dbus.h>
+
+#define	BLUEZ_SERVICE "org.bluez"
+#define	BLUEZ_MANAGER_INTERFACE		BLUEZ_SERVICE ".Manager"
+#define	BLUEZ_ADAPTER_INTERFACE		BLUEZ_SERVICE ".Adapter"
+#define	BLUEZ_DEVICE_INTERFACE		BLUEZ_SERVICE ".Device"
+#define	BLUEZ_GATEWAY_INTERFACE		BLUEZ_SERVICE ".HandsfreeGateway"
+
+#define HFP_AGENT_INTERFACE "org.bluez.HandsfreeAgent"
+
+#define HFP_AG_UUID	"0000111F-0000-1000-8000-00805F9B34FB"
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
 static const char *brsf_prefix[] = { "+BRSF:", NULL };
 static const char *cind_prefix[] = { "+CIND:", NULL };
 static const char *cmer_prefix[] = { "+CMER:", NULL };
 static const char *chld_prefix[] = { "+CHLD:", NULL };
 
+static DBusConnection *connection;
+
 static int hfp_disable(struct ofono_modem *modem);
+static void hfp_remove(struct ofono_modem *modem);
 
 static void hfp_debug(const char *str, void *user_data)
 {
@@ -66,11 +87,17 @@ static void hfp_debug(const char *str, void *user_data)
 
 static void sevice_level_conn_established(struct ofono_modem *modem)
 {
+	DBusMessage *msg;
 	struct hfp_data *data = ofono_modem_get_data(modem);
 
-	ofono_info("Service level connection established");
 	ofono_modem_set_powered(modem, TRUE);
 
+	msg = dbus_message_new_method_return(data->slc_msg);
+	g_dbus_send_message(connection, msg);
+	dbus_message_unref(data->slc_msg);
+
+	ofono_info("Service level connection established");
+
 	g_at_chat_send(data->chat, "AT+CMEE=1", NULL, NULL, NULL, NULL);
 }
 
@@ -135,6 +162,60 @@ static void cmer_cb(gboolean ok, GAtResult *result, gpointer user_data)
 		sevice_level_conn_established(modem);
 }
 
+static int send_method_call(const char *dest, const char *path,
+				const char *interface, const char *method,
+				DBusPendingCallNotifyFunction cb,
+				void *user_data, int type, ...)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+	va_list args;
+
+	msg = dbus_message_new_method_call(dest, path, interface, method);
+	if (!msg) {
+		ofono_error("Unable to allocate new D-Bus %s message", method);
+		return -ENOMEM;
+	}
+
+	va_start(args, type);
+
+	if (!dbus_message_append_args_valist(msg, type, args)) {
+		dbus_message_unref(msg);
+		va_end(args);
+		return -EIO;
+	}
+
+	va_end(args);
+
+	if (!cb) {
+		g_dbus_send_message(connection, msg);
+		return 0;
+	}
+
+	if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+		ofono_error("Sending %s failed", method);
+		dbus_message_unref(msg);
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(call, cb, user_data, NULL);
+	dbus_pending_call_unref(call);
+	dbus_message_unref(msg);
+
+	return 0;
+}
+
+static gboolean hfp_enable_timeout(gpointer user)
+{
+	struct ofono_modem *modem = user;
+
+	if (ofono_modem_get_powered(modem))
+		return FALSE;
+
+	hfp_disable(modem);
+	return FALSE;
+}
+
 static void cind_status_cb(gboolean ok, GAtResult *result,
 				gpointer user_data)
 {
@@ -260,8 +341,7 @@ error:
 }
 
 /* either oFono or Phone could request SLC connection */
-static int service_level_connection(struct ofono_modem *modem,
-				const char *tty)
+static int service_level_connection(struct ofono_modem *modem, int fd)
 {
 	struct hfp_data *data = ofono_modem_get_data(modem);
 	GIOChannel *io;
@@ -269,7 +349,7 @@ static int service_level_connection(struct ofono_modem *modem,
 	GAtChat *chat;
 	char buf[64];
 
-	io = g_at_tty_open(tty, NULL);
+	io = g_io_channel_unix_new(fd);
 	if (!io) {
 		ofono_error("Service level connection failed: %s (%d)",
 			strerror(errno), errno);
@@ -288,7 +368,6 @@ static int service_level_connection(struct ofono_modem *modem,
 		g_at_chat_set_debug(chat, hfp_debug, NULL);
 
 	sprintf(buf, "AT+BRSF=%d", data->hf_features);
-
 	g_at_chat_send(chat, buf, brsf_prefix,
 				brsf_cb, modem, NULL);
 	data->chat = chat;
@@ -296,10 +375,52 @@ static int service_level_connection(struct ofono_modem *modem,
 	return -EINPROGRESS;
 }
 
-static int hfp_probe(struct ofono_modem *modem)
+static DBusMessage *hfp_agent_new_connection(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	int fd;
+	struct ofono_modem *modem = data;
+	struct hfp_data *hfp_data = ofono_modem_get_data(modem);
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UNIX_FD, &fd,
+				DBUS_TYPE_INVALID))
+		return __ofono_error_invalid_args(msg);
+
+	service_level_connection(modem, fd);
+
+	hfp_data->slc_msg = msg;
+	dbus_message_ref(msg);
+
+	return NULL;
+}
+
+static DBusMessage *hfp_agent_release(DBusConnection *conn, DBusMessage *msg, void *data)
 {
+	struct ofono_modem *modem = data;
+
+	if (ofono_modem_get_powered(modem))
+		hfp_disable(modem);
+
+	ofono_modem_remove(modem);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable agent_methods[] = {
+	{ "NewConnection", "h", "", hfp_agent_new_connection,
+		G_DBUS_METHOD_FLAG_ASYNC },
+	{ "Release", "", "", hfp_agent_release },
+	{NULL, NULL, NULL, NULL}
+};
+
+static int hfp_create_modem(const char *device)
+{
+	struct ofono_modem *modem;
 	struct hfp_data *data;
 
+	ofono_info("Using device: %s", device);
+
+	modem = ofono_modem_create(NULL, "hfp");
+
 	data = g_try_new0(struct hfp_data, 1);
 	if (!data)
 		return -ENOMEM;
@@ -310,14 +431,282 @@ static int hfp_probe(struct ofono_modem *modem)
 	data->hf_features |= HF_FEATURE_ENHANCED_CALL_STATUS;
 	data->hf_features |= HF_FEATURE_ENHANCED_CALL_CONTROL;
 
+	data->handsfree_path = g_strdup(device);
+
 	ofono_modem_set_data(modem, data);
 
+	ofono_modem_register(modem);
+
+	return 0;
+}
+
+static void parse_uuids(DBusMessageIter *i, const char *device)
+{
+	DBusMessageIter variant, ai;
+	const char *value;
+
+	dbus_message_iter_recurse(i, &variant);
+	dbus_message_iter_recurse(&variant, &ai);
+
+	while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
+		dbus_message_iter_get_basic(&ai, &value);
+		if (!strcasecmp(value, HFP_AG_UUID))
+			hfp_create_modem(device);
+
+		if (!dbus_message_iter_next(&ai))
+			return;
+	}
+}
+
+static void parse_get_properties(DBusMessage *reply, const char *device)
+{
+	DBusMessageIter arg, element, variant;
+	const char *key;
+
+	if (!dbus_message_iter_init(reply, &arg)) {
+		ofono_debug("GetProperties reply has no arguments.");
+		return;
+	}
+
+	if (dbus_message_iter_get_arg_type(&arg) != DBUS_TYPE_ARRAY) {
+		ofono_debug("GetProperties argument is not an array.");
+		return;
+	}
+
+	dbus_message_iter_recurse(&arg, &element);
+	while (dbus_message_iter_get_arg_type(&element) != DBUS_TYPE_INVALID) {
+		if (dbus_message_iter_get_arg_type(&element) ==
+				DBUS_TYPE_DICT_ENTRY) {
+			DBusMessageIter dict;
+
+			dbus_message_iter_recurse(&element, &dict);
+
+			if (dbus_message_iter_get_arg_type(&dict) !=
+					DBUS_TYPE_STRING) {
+				ofono_debug("Property name not a string.");
+				return;
+			}
+
+			dbus_message_iter_get_basic(&dict, &key);
+
+			if (!dbus_message_iter_next(&dict))  {
+				ofono_debug("Property value missing");
+				return;
+			}
+
+			if (dbus_message_iter_get_arg_type(&dict) !=
+					DBUS_TYPE_VARIANT) {
+				ofono_debug("Property value not a variant.");
+				return;
+			}
+
+			if (!strcmp(key, "UUIDs"))
+				parse_uuids(&dict, device);
+
+			dbus_message_iter_recurse(&dict, &variant);
+		}
+
+		if (!dbus_message_iter_next(&element))
+			return;
+	}
+}
+
+static void get_properties_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusMessage *reply;
+	const char *device = user_data;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	if (dbus_message_is_error(reply, DBUS_ERROR_SERVICE_UNKNOWN)) {
+		ofono_debug("Bluetooth daemon is apparently not available.");
+		goto done;
+	}
+
+	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+		if (!dbus_message_is_error(reply, DBUS_ERROR_UNKNOWN_METHOD))
+			ofono_info("Error from GetProperties reply: %s",
+					dbus_message_get_error_name(reply));
+
+		goto done;
+	}
+
+	parse_get_properties(reply, device);
+
+done:
+	dbus_message_unref(reply);
+}
+
+static void list_devices_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	const char **device = NULL;
+	int num, ret, i;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	if (dbus_message_is_error(reply, DBUS_ERROR_SERVICE_UNKNOWN)) {
+		ofono_debug("Bluetooth daemon is apparently not available.");
+		goto done;
+	}
+
+	dbus_error_init(&err);
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_ARRAY,
+				DBUS_TYPE_OBJECT_PATH, &device,
+				&num, DBUS_TYPE_INVALID) == FALSE) {
+		if (device == NULL) {
+			dbus_error_free(&err);
+			goto done;
+		}
+
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s", err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	for (i = 0 ; i < num ; i++) {
+		ret = send_method_call(BLUEZ_SERVICE, device[i],
+				BLUEZ_DEVICE_INTERFACE, "GetProperties",
+				get_properties_cb, (void *)device[i],
+				DBUS_TYPE_INVALID);
+		if (ret < 0)
+			ofono_error("GetProperties failed(%d)", ret);
+	}
+
+done:
+	dbus_message_unref(reply);
+}
+
+static gboolean adapter_added(DBusConnection *connection, DBusMessage *message,
+				void *user_data)
+{
+	const char *path;
+	int ret;
+
+	dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+				DBUS_TYPE_INVALID);
+
+	ret = send_method_call(BLUEZ_SERVICE, path,
+			BLUEZ_ADAPTER_INTERFACE, "ListDevices",
+			list_devices_cb, NULL,
+			DBUS_TYPE_INVALID);
+
+	if (ret < 0)
+		ofono_error("ListDevices failed(%d)", ret);
+
+	return TRUE;
+}
+
+static void list_adapters_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	char **adapter = NULL;
+	int num, ret, i;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	if (dbus_message_is_error(reply, DBUS_ERROR_SERVICE_UNKNOWN)) {
+		ofono_debug("Bluetooth daemon is apparently not available.");
+		goto done;
+	}
+
+	dbus_error_init(&err);
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_ARRAY,
+				DBUS_TYPE_OBJECT_PATH, &adapter,
+				&num, DBUS_TYPE_INVALID) == FALSE) {
+		if (adapter == NULL) {
+			dbus_error_free(&err);
+			goto done;
+		}
+
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s", err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	for (i = 0 ; i < num ; i++) {
+		ret = send_method_call(BLUEZ_SERVICE, adapter[i],
+				BLUEZ_ADAPTER_INTERFACE, "ListDevices",
+				list_devices_cb, NULL,
+				DBUS_TYPE_INVALID);
+
+		if (ret < 0)
+			ofono_error("ListDevices failed(%d)", ret);
+	}
+
+	g_strfreev(adapter);
+
+done:
+	dbus_message_unref(reply);
+}
+
+static int hfp_load_modems()
+{
+	return send_method_call(BLUEZ_SERVICE, "/",
+				BLUEZ_MANAGER_INTERFACE, "ListAdapters",
+				list_adapters_cb, NULL,
+				DBUS_TYPE_INVALID);
+}
+
+static int hfp_register_ofono_handsfree(struct ofono_modem *modem)
+{
+	const char *obj_path = ofono_modem_get_path(modem);
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	ofono_debug("Registering oFono Agent to bluetooth daemon");
+
+	if (!data->handsfree_path)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "RegisterAgent",
+				NULL, NULL, DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+}
+
+static int hfp_unregister_ofono_handsfree(struct ofono_modem *modem)
+{
+	const char *obj_path = ofono_modem_get_path(modem);
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	ofono_debug("Unregistering oFono Agent from bluetooth daemon");
+
+	if (!data->handsfree_path)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "UnregisterAgent",
+				NULL, NULL, DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+}
+
+static int hfp_probe(struct ofono_modem *modem)
+{
+	const char *obj_path = ofono_modem_get_path(modem);
+
+	g_dbus_register_interface(connection, obj_path, HFP_AGENT_INTERFACE,
+			agent_methods, NULL, NULL, modem, NULL);
+
+	if (hfp_register_ofono_handsfree(modem) != 0)
+		return -EINVAL;
+
 	return 0;
 }
 
 static void hfp_remove(struct ofono_modem *modem)
 {
-	gpointer data = ofono_modem_get_data(modem);
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	hfp_unregister_ofono_handsfree(modem);
+
+	if (data->handsfree_path)
+		g_free(data->handsfree_path);
 
 	if (data)
 		g_free(data);
@@ -325,21 +714,46 @@ static void hfp_remove(struct ofono_modem *modem)
 	ofono_modem_set_data(modem, NULL);
 }
 
+static int hfp_connect_ofono_handsfree(struct ofono_modem *modem)
+{
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	ofono_debug("Connect to bluetooth daemon");
+
+	if (!data->handsfree_path || !connection)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Connect",
+				NULL, NULL, DBUS_TYPE_INVALID);
+}
+
 /* power up hardware */
 static int hfp_enable(struct ofono_modem *modem)
 {
-	const char *tty;
-	int ret;
+	struct hfp_data *data = ofono_modem_get_data(modem);
 
 	DBG("%p", modem);
 
-	tty = ofono_modem_get_string(modem, "Device");
-	if (tty == NULL)
+	data->at_timeout =
+		g_timeout_add_seconds(10, hfp_enable_timeout, modem);
+
+	if (hfp_connect_ofono_handsfree(modem) < 0)
 		return -EINVAL;
 
-	ret = service_level_connection(modem, tty);
+	return -EINPROGRESS;
+}
+
+static int hfp_disconnect_ofono_handsfree(struct ofono_modem *modem)
+{
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	if (!data->handsfree_path || !connection)
+		return -EINVAL;
 
-	return ret;
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Disconnect",
+				NULL, NULL, DBUS_TYPE_INVALID);
 }
 
 static int hfp_disable(struct ofono_modem *modem)
@@ -357,8 +771,11 @@ static int hfp_disable(struct ofono_modem *modem)
 	memset(data->cind_val, 0, sizeof(data->cind_val));
 	memset(data->cind_pos, 0, sizeof(data->cind_pos));
 
+	g_source_remove(data->at_timeout);
+
 	ofono_modem_set_powered(modem, FALSE);
 
+	hfp_disconnect_ofono_handsfree(modem);
 	return 0;
 }
 
@@ -388,14 +805,44 @@ static struct ofono_modem_driver hfp_driver = {
 	.post_sim	= hfp_post_sim,
 };
 
+static guint added_watch;
+
 static int hfp_init(void)
 {
-	DBG("");
-	return ofono_modem_driver_register(&hfp_driver);
+	int err;
+
+	connection = ofono_dbus_get_connection();
+
+	added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+						BLUEZ_MANAGER_INTERFACE,
+						"AdapterAdded",
+						adapter_added, NULL, NULL);
+
+	if (added_watch == 0) {
+		err = -EIO;
+		goto remove;
+	}
+
+	err = ofono_modem_driver_register(&hfp_driver);
+	if (err < 0)
+		goto remove;
+
+	hfp_load_modems();
+
+	return 0;
+
+remove:
+	g_dbus_remove_watch(connection, added_watch);
+
+	dbus_connection_unref(connection);
+
+	return err;
 }
 
 static void hfp_exit(void)
 {
+	g_dbus_remove_watch(connection, added_watch);
+
 	ofono_modem_driver_unregister(&hfp_driver);
 }
 
diff --git a/plugins/modemconf.c b/plugins/modemconf.c
index c8c261f..932d610 100644
--- a/plugins/modemconf.c
+++ b/plugins/modemconf.c
@@ -129,7 +129,6 @@ static struct ofono_modem *create_modem(GKeyFile *keyfile, const char *group)
 
 	if (!g_strcmp0(driver, "atgen") || !g_strcmp0(driver, "g1") ||
 						!g_strcmp0(driver, "calypso") ||
-						!g_strcmp0(driver, "hfp") ||
 						!g_strcmp0(driver, "palmpre"))
 		set_device(modem, keyfile, group);
 
-- 
1.6.4.4


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

* [PATCH] Add HFP support through BlueZ
@ 2010-01-27 19:12               ` Gustavo F. Padovan
  0 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-27 19:12 UTC (permalink / raw)
  To: ofono

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

It uses BlueZ through to get HFP working following the
org.bluez.HandsfreeGateway and org.bluez.HandsfreeAgent from the BlueZ
D-Bus API.

You need the HFP suport into BlueZ and the new dbus 1.3 with fd-passing
support.

Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for its prototype
on this code.
---
 drivers/hfpmodem/hfpmodem.h |    4 +
 plugins/hfp.c               |  477 +++++++++++++++++++++++++++++++++++++++++--
 plugins/modemconf.c         |    1 -
 3 files changed, 466 insertions(+), 16 deletions(-)

diff --git a/drivers/hfpmodem/hfpmodem.h b/drivers/hfpmodem/hfpmodem.h
index 509846b..6f0ab1f 100644
--- a/drivers/hfpmodem/hfpmodem.h
+++ b/drivers/hfpmodem/hfpmodem.h
@@ -22,6 +22,7 @@
 #define __BLUETOOTH_H__
 
 #include <drivers/atmodem/atutil.h>
+#include <ofono/dbus.h>
 
 /* AG supported features bitmap. Bluetooth HFP 1.5 spec page 77 */
 #define AG_FEATURE_3WAY 0x1
@@ -63,11 +64,14 @@ enum hfp_indicator {
 
 struct hfp_data {
 	GAtChat *chat;
+	char *handsfree_path;
+	DBusMessage *slc_msg;
 	unsigned int ag_features;
 	unsigned int ag_mpty_features;
 	unsigned int hf_features;
 	unsigned char cind_pos[HFP_INDICATOR_LAST];
 	unsigned int cind_val[HFP_INDICATOR_LAST];
+	unsigned int at_timeout;
 };
 
 extern void hfp_netreg_init();
diff --git a/plugins/hfp.c b/plugins/hfp.c
index 3bbd922..12b7f0a 100644
--- a/plugins/hfp.c
+++ b/plugins/hfp.c
@@ -30,6 +30,8 @@
 #include <glib.h>
 #include <gatchat.h>
 #include <gattty.h>
+#include <gdbus.h>
+#include <ofono.h>
 
 #define OFONO_API_SUBJECT_TO_CHANGE
 #include <ofono/plugin.h>
@@ -52,12 +54,31 @@
 
 #include <drivers/hfpmodem/hfpmodem.h>
 
+#include <ofono/dbus.h>
+
+#define	BLUEZ_SERVICE "org.bluez"
+#define	BLUEZ_MANAGER_INTERFACE		BLUEZ_SERVICE ".Manager"
+#define	BLUEZ_ADAPTER_INTERFACE		BLUEZ_SERVICE ".Adapter"
+#define	BLUEZ_DEVICE_INTERFACE		BLUEZ_SERVICE ".Device"
+#define	BLUEZ_GATEWAY_INTERFACE		BLUEZ_SERVICE ".HandsfreeGateway"
+
+#define HFP_AGENT_INTERFACE "org.bluez.HandsfreeAgent"
+
+#define HFP_AG_UUID	"0000111F-0000-1000-8000-00805F9B34FB"
+
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
 static const char *brsf_prefix[] = { "+BRSF:", NULL };
 static const char *cind_prefix[] = { "+CIND:", NULL };
 static const char *cmer_prefix[] = { "+CMER:", NULL };
 static const char *chld_prefix[] = { "+CHLD:", NULL };
 
+static DBusConnection *connection;
+
 static int hfp_disable(struct ofono_modem *modem);
+static void hfp_remove(struct ofono_modem *modem);
 
 static void hfp_debug(const char *str, void *user_data)
 {
@@ -66,11 +87,17 @@ static void hfp_debug(const char *str, void *user_data)
 
 static void sevice_level_conn_established(struct ofono_modem *modem)
 {
+	DBusMessage *msg;
 	struct hfp_data *data = ofono_modem_get_data(modem);
 
-	ofono_info("Service level connection established");
 	ofono_modem_set_powered(modem, TRUE);
 
+	msg = dbus_message_new_method_return(data->slc_msg);
+	g_dbus_send_message(connection, msg);
+	dbus_message_unref(data->slc_msg);
+
+	ofono_info("Service level connection established");
+
 	g_at_chat_send(data->chat, "AT+CMEE=1", NULL, NULL, NULL, NULL);
 }
 
@@ -135,6 +162,60 @@ static void cmer_cb(gboolean ok, GAtResult *result, gpointer user_data)
 		sevice_level_conn_established(modem);
 }
 
+static int send_method_call(const char *dest, const char *path,
+				const char *interface, const char *method,
+				DBusPendingCallNotifyFunction cb,
+				void *user_data, int type, ...)
+{
+	DBusMessage *msg;
+	DBusPendingCall *call;
+	va_list args;
+
+	msg = dbus_message_new_method_call(dest, path, interface, method);
+	if (!msg) {
+		ofono_error("Unable to allocate new D-Bus %s message", method);
+		return -ENOMEM;
+	}
+
+	va_start(args, type);
+
+	if (!dbus_message_append_args_valist(msg, type, args)) {
+		dbus_message_unref(msg);
+		va_end(args);
+		return -EIO;
+	}
+
+	va_end(args);
+
+	if (!cb) {
+		g_dbus_send_message(connection, msg);
+		return 0;
+	}
+
+	if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+		ofono_error("Sending %s failed", method);
+		dbus_message_unref(msg);
+		return -EIO;
+	}
+
+	dbus_pending_call_set_notify(call, cb, user_data, NULL);
+	dbus_pending_call_unref(call);
+	dbus_message_unref(msg);
+
+	return 0;
+}
+
+static gboolean hfp_enable_timeout(gpointer user)
+{
+	struct ofono_modem *modem = user;
+
+	if (ofono_modem_get_powered(modem))
+		return FALSE;
+
+	hfp_disable(modem);
+	return FALSE;
+}
+
 static void cind_status_cb(gboolean ok, GAtResult *result,
 				gpointer user_data)
 {
@@ -260,8 +341,7 @@ error:
 }
 
 /* either oFono or Phone could request SLC connection */
-static int service_level_connection(struct ofono_modem *modem,
-				const char *tty)
+static int service_level_connection(struct ofono_modem *modem, int fd)
 {
 	struct hfp_data *data = ofono_modem_get_data(modem);
 	GIOChannel *io;
@@ -269,7 +349,7 @@ static int service_level_connection(struct ofono_modem *modem,
 	GAtChat *chat;
 	char buf[64];
 
-	io = g_at_tty_open(tty, NULL);
+	io = g_io_channel_unix_new(fd);
 	if (!io) {
 		ofono_error("Service level connection failed: %s (%d)",
 			strerror(errno), errno);
@@ -288,7 +368,6 @@ static int service_level_connection(struct ofono_modem *modem,
 		g_at_chat_set_debug(chat, hfp_debug, NULL);
 
 	sprintf(buf, "AT+BRSF=%d", data->hf_features);
-
 	g_at_chat_send(chat, buf, brsf_prefix,
 				brsf_cb, modem, NULL);
 	data->chat = chat;
@@ -296,10 +375,52 @@ static int service_level_connection(struct ofono_modem *modem,
 	return -EINPROGRESS;
 }
 
-static int hfp_probe(struct ofono_modem *modem)
+static DBusMessage *hfp_agent_new_connection(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	int fd;
+	struct ofono_modem *modem = data;
+	struct hfp_data *hfp_data = ofono_modem_get_data(modem);
+
+	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UNIX_FD, &fd,
+				DBUS_TYPE_INVALID))
+		return __ofono_error_invalid_args(msg);
+
+	service_level_connection(modem, fd);
+
+	hfp_data->slc_msg = msg;
+	dbus_message_ref(msg);
+
+	return NULL;
+}
+
+static DBusMessage *hfp_agent_release(DBusConnection *conn, DBusMessage *msg, void *data)
 {
+	struct ofono_modem *modem = data;
+
+	if (ofono_modem_get_powered(modem))
+		hfp_disable(modem);
+
+	ofono_modem_remove(modem);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable agent_methods[] = {
+	{ "NewConnection", "h", "", hfp_agent_new_connection,
+		G_DBUS_METHOD_FLAG_ASYNC },
+	{ "Release", "", "", hfp_agent_release },
+	{NULL, NULL, NULL, NULL}
+};
+
+static int hfp_create_modem(const char *device)
+{
+	struct ofono_modem *modem;
 	struct hfp_data *data;
 
+	ofono_info("Using device: %s", device);
+
+	modem = ofono_modem_create(NULL, "hfp");
+
 	data = g_try_new0(struct hfp_data, 1);
 	if (!data)
 		return -ENOMEM;
@@ -310,14 +431,282 @@ static int hfp_probe(struct ofono_modem *modem)
 	data->hf_features |= HF_FEATURE_ENHANCED_CALL_STATUS;
 	data->hf_features |= HF_FEATURE_ENHANCED_CALL_CONTROL;
 
+	data->handsfree_path = g_strdup(device);
+
 	ofono_modem_set_data(modem, data);
 
+	ofono_modem_register(modem);
+
+	return 0;
+}
+
+static void parse_uuids(DBusMessageIter *i, const char *device)
+{
+	DBusMessageIter variant, ai;
+	const char *value;
+
+	dbus_message_iter_recurse(i, &variant);
+	dbus_message_iter_recurse(&variant, &ai);
+
+	while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
+		dbus_message_iter_get_basic(&ai, &value);
+		if (!strcasecmp(value, HFP_AG_UUID))
+			hfp_create_modem(device);
+
+		if (!dbus_message_iter_next(&ai))
+			return;
+	}
+}
+
+static void parse_get_properties(DBusMessage *reply, const char *device)
+{
+	DBusMessageIter arg, element, variant;
+	const char *key;
+
+	if (!dbus_message_iter_init(reply, &arg)) {
+		ofono_debug("GetProperties reply has no arguments.");
+		return;
+	}
+
+	if (dbus_message_iter_get_arg_type(&arg) != DBUS_TYPE_ARRAY) {
+		ofono_debug("GetProperties argument is not an array.");
+		return;
+	}
+
+	dbus_message_iter_recurse(&arg, &element);
+	while (dbus_message_iter_get_arg_type(&element) != DBUS_TYPE_INVALID) {
+		if (dbus_message_iter_get_arg_type(&element) ==
+				DBUS_TYPE_DICT_ENTRY) {
+			DBusMessageIter dict;
+
+			dbus_message_iter_recurse(&element, &dict);
+
+			if (dbus_message_iter_get_arg_type(&dict) !=
+					DBUS_TYPE_STRING) {
+				ofono_debug("Property name not a string.");
+				return;
+			}
+
+			dbus_message_iter_get_basic(&dict, &key);
+
+			if (!dbus_message_iter_next(&dict))  {
+				ofono_debug("Property value missing");
+				return;
+			}
+
+			if (dbus_message_iter_get_arg_type(&dict) !=
+					DBUS_TYPE_VARIANT) {
+				ofono_debug("Property value not a variant.");
+				return;
+			}
+
+			if (!strcmp(key, "UUIDs"))
+				parse_uuids(&dict, device);
+
+			dbus_message_iter_recurse(&dict, &variant);
+		}
+
+		if (!dbus_message_iter_next(&element))
+			return;
+	}
+}
+
+static void get_properties_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusMessage *reply;
+	const char *device = user_data;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	if (dbus_message_is_error(reply, DBUS_ERROR_SERVICE_UNKNOWN)) {
+		ofono_debug("Bluetooth daemon is apparently not available.");
+		goto done;
+	}
+
+	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+		if (!dbus_message_is_error(reply, DBUS_ERROR_UNKNOWN_METHOD))
+			ofono_info("Error from GetProperties reply: %s",
+					dbus_message_get_error_name(reply));
+
+		goto done;
+	}
+
+	parse_get_properties(reply, device);
+
+done:
+	dbus_message_unref(reply);
+}
+
+static void list_devices_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	const char **device = NULL;
+	int num, ret, i;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	if (dbus_message_is_error(reply, DBUS_ERROR_SERVICE_UNKNOWN)) {
+		ofono_debug("Bluetooth daemon is apparently not available.");
+		goto done;
+	}
+
+	dbus_error_init(&err);
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_ARRAY,
+				DBUS_TYPE_OBJECT_PATH, &device,
+				&num, DBUS_TYPE_INVALID) == FALSE) {
+		if (device == NULL) {
+			dbus_error_free(&err);
+			goto done;
+		}
+
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s", err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	for (i = 0 ; i < num ; i++) {
+		ret = send_method_call(BLUEZ_SERVICE, device[i],
+				BLUEZ_DEVICE_INTERFACE, "GetProperties",
+				get_properties_cb, (void *)device[i],
+				DBUS_TYPE_INVALID);
+		if (ret < 0)
+			ofono_error("GetProperties failed(%d)", ret);
+	}
+
+done:
+	dbus_message_unref(reply);
+}
+
+static gboolean adapter_added(DBusConnection *connection, DBusMessage *message,
+				void *user_data)
+{
+	const char *path;
+	int ret;
+
+	dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+				DBUS_TYPE_INVALID);
+
+	ret = send_method_call(BLUEZ_SERVICE, path,
+			BLUEZ_ADAPTER_INTERFACE, "ListDevices",
+			list_devices_cb, NULL,
+			DBUS_TYPE_INVALID);
+
+	if (ret < 0)
+		ofono_error("ListDevices failed(%d)", ret);
+
+	return TRUE;
+}
+
+static void list_adapters_cb(DBusPendingCall *call, gpointer user_data)
+{
+	DBusError err;
+	DBusMessage *reply;
+	char **adapter = NULL;
+	int num, ret, i;
+
+	reply = dbus_pending_call_steal_reply(call);
+
+	if (dbus_message_is_error(reply, DBUS_ERROR_SERVICE_UNKNOWN)) {
+		ofono_debug("Bluetooth daemon is apparently not available.");
+		goto done;
+	}
+
+	dbus_error_init(&err);
+	if (dbus_message_get_args(reply, &err, DBUS_TYPE_ARRAY,
+				DBUS_TYPE_OBJECT_PATH, &adapter,
+				&num, DBUS_TYPE_INVALID) == FALSE) {
+		if (adapter == NULL) {
+			dbus_error_free(&err);
+			goto done;
+		}
+
+		if (dbus_error_is_set(&err) == TRUE) {
+			ofono_error("%s", err.message);
+			dbus_error_free(&err);
+		}
+		goto done;
+	}
+
+	for (i = 0 ; i < num ; i++) {
+		ret = send_method_call(BLUEZ_SERVICE, adapter[i],
+				BLUEZ_ADAPTER_INTERFACE, "ListDevices",
+				list_devices_cb, NULL,
+				DBUS_TYPE_INVALID);
+
+		if (ret < 0)
+			ofono_error("ListDevices failed(%d)", ret);
+	}
+
+	g_strfreev(adapter);
+
+done:
+	dbus_message_unref(reply);
+}
+
+static int hfp_load_modems()
+{
+	return send_method_call(BLUEZ_SERVICE, "/",
+				BLUEZ_MANAGER_INTERFACE, "ListAdapters",
+				list_adapters_cb, NULL,
+				DBUS_TYPE_INVALID);
+}
+
+static int hfp_register_ofono_handsfree(struct ofono_modem *modem)
+{
+	const char *obj_path = ofono_modem_get_path(modem);
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	ofono_debug("Registering oFono Agent to bluetooth daemon");
+
+	if (!data->handsfree_path)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "RegisterAgent",
+				NULL, NULL, DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+}
+
+static int hfp_unregister_ofono_handsfree(struct ofono_modem *modem)
+{
+	const char *obj_path = ofono_modem_get_path(modem);
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	ofono_debug("Unregistering oFono Agent from bluetooth daemon");
+
+	if (!data->handsfree_path)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "UnregisterAgent",
+				NULL, NULL, DBUS_TYPE_OBJECT_PATH,
+				&obj_path, DBUS_TYPE_INVALID);
+}
+
+static int hfp_probe(struct ofono_modem *modem)
+{
+	const char *obj_path = ofono_modem_get_path(modem);
+
+	g_dbus_register_interface(connection, obj_path, HFP_AGENT_INTERFACE,
+			agent_methods, NULL, NULL, modem, NULL);
+
+	if (hfp_register_ofono_handsfree(modem) != 0)
+		return -EINVAL;
+
 	return 0;
 }
 
 static void hfp_remove(struct ofono_modem *modem)
 {
-	gpointer data = ofono_modem_get_data(modem);
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	hfp_unregister_ofono_handsfree(modem);
+
+	if (data->handsfree_path)
+		g_free(data->handsfree_path);
 
 	if (data)
 		g_free(data);
@@ -325,21 +714,46 @@ static void hfp_remove(struct ofono_modem *modem)
 	ofono_modem_set_data(modem, NULL);
 }
 
+static int hfp_connect_ofono_handsfree(struct ofono_modem *modem)
+{
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	ofono_debug("Connect to bluetooth daemon");
+
+	if (!data->handsfree_path || !connection)
+		return -EINVAL;
+
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Connect",
+				NULL, NULL, DBUS_TYPE_INVALID);
+}
+
 /* power up hardware */
 static int hfp_enable(struct ofono_modem *modem)
 {
-	const char *tty;
-	int ret;
+	struct hfp_data *data = ofono_modem_get_data(modem);
 
 	DBG("%p", modem);
 
-	tty = ofono_modem_get_string(modem, "Device");
-	if (tty == NULL)
+	data->at_timeout =
+		g_timeout_add_seconds(10, hfp_enable_timeout, modem);
+
+	if (hfp_connect_ofono_handsfree(modem) < 0)
 		return -EINVAL;
 
-	ret = service_level_connection(modem, tty);
+	return -EINPROGRESS;
+}
+
+static int hfp_disconnect_ofono_handsfree(struct ofono_modem *modem)
+{
+	struct hfp_data *data = ofono_modem_get_data(modem);
+
+	if (!data->handsfree_path || !connection)
+		return -EINVAL;
 
-	return ret;
+	return send_method_call(BLUEZ_SERVICE, data->handsfree_path,
+				BLUEZ_GATEWAY_INTERFACE, "Disconnect",
+				NULL, NULL, DBUS_TYPE_INVALID);
 }
 
 static int hfp_disable(struct ofono_modem *modem)
@@ -357,8 +771,11 @@ static int hfp_disable(struct ofono_modem *modem)
 	memset(data->cind_val, 0, sizeof(data->cind_val));
 	memset(data->cind_pos, 0, sizeof(data->cind_pos));
 
+	g_source_remove(data->at_timeout);
+
 	ofono_modem_set_powered(modem, FALSE);
 
+	hfp_disconnect_ofono_handsfree(modem);
 	return 0;
 }
 
@@ -388,14 +805,44 @@ static struct ofono_modem_driver hfp_driver = {
 	.post_sim	= hfp_post_sim,
 };
 
+static guint added_watch;
+
 static int hfp_init(void)
 {
-	DBG("");
-	return ofono_modem_driver_register(&hfp_driver);
+	int err;
+
+	connection = ofono_dbus_get_connection();
+
+	added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
+						BLUEZ_MANAGER_INTERFACE,
+						"AdapterAdded",
+						adapter_added, NULL, NULL);
+
+	if (added_watch == 0) {
+		err = -EIO;
+		goto remove;
+	}
+
+	err = ofono_modem_driver_register(&hfp_driver);
+	if (err < 0)
+		goto remove;
+
+	hfp_load_modems();
+
+	return 0;
+
+remove:
+	g_dbus_remove_watch(connection, added_watch);
+
+	dbus_connection_unref(connection);
+
+	return err;
 }
 
 static void hfp_exit(void)
 {
+	g_dbus_remove_watch(connection, added_watch);
+
 	ofono_modem_driver_unregister(&hfp_driver);
 }
 
diff --git a/plugins/modemconf.c b/plugins/modemconf.c
index c8c261f..932d610 100644
--- a/plugins/modemconf.c
+++ b/plugins/modemconf.c
@@ -129,7 +129,6 @@ static struct ofono_modem *create_modem(GKeyFile *keyfile, const char *group)
 
 	if (!g_strcmp0(driver, "atgen") || !g_strcmp0(driver, "g1") ||
 						!g_strcmp0(driver, "calypso") ||
-						!g_strcmp0(driver, "hfp") ||
 						!g_strcmp0(driver, "palmpre"))
 		set_device(modem, keyfile, group);
 
-- 
1.6.4.4


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

* Re: HFP support into BlueZ and oFono
  2010-01-27 19:12         ` Gustavo F. Padovan
@ 2010-01-27 19:17           ` Gustavo F. Padovan
  -1 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-27 19:17 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: ofono

On Wed, Jan 27, 2010 at 5:12 PM, Gustavo F. Padovan
<padovan@profusion.mobi> wrote:
> Hi All,
>
> New patches for the HFP support. The BlueZ part was updated with the Johan's comments and the oFono patch supports load/remove of bluetooth devices dynamically.
> We still have bug: the link RFCOMM on the AG side is closed only when bluetoothd shuts down. Any idea on that?
>
> Comments are welcome.

I forgot to mention, but the first two patches is for BlueZ the other
one is for oFono.

>
> --
> Gustavo F. Padovan
> ProFUSION embedded systems - http://profusion.mobi
>
>



-- 
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi

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

* Re: HFP support into BlueZ and oFono
@ 2010-01-27 19:17           ` Gustavo F. Padovan
  0 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-27 19:17 UTC (permalink / raw)
  To: ofono

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

On Wed, Jan 27, 2010 at 5:12 PM, Gustavo F. Padovan
<padovan@profusion.mobi> wrote:
> Hi All,
>
> New patches for the HFP support. The BlueZ part was updated with the Johan's comments and the oFono patch supports load/remove of bluetooth devices dynamically.
> We still have bug: the link RFCOMM on the AG side is closed only when bluetoothd shuts down. Any idea on that?
>
> Comments are welcome.

I forgot to mention, but the first two patches is for BlueZ the other
one is for oFono.

>
> --
> Gustavo F. Padovan
> ProFUSION embedded systems - http://profusion.mobi
>
>



-- 
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi

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

* Re: [PATCH] Add HFP support through BlueZ
  2010-01-27 19:12               ` Gustavo F. Padovan
@ 2010-01-27 20:17                 ` Marcel Holtmann
  -1 siblings, 0 replies; 51+ messages in thread
From: Marcel Holtmann @ 2010-01-27 20:17 UTC (permalink / raw)
  To: Gustavo F. Padovan; +Cc: linux-bluetooth, ofono

Hi Gustavo,

> It uses BlueZ through to get HFP working following the
> org.bluez.HandsfreeGateway and org.bluez.HandsfreeAgent from the BlueZ
> D-Bus API.
> 
> You need the HFP suport into BlueZ and the new dbus 1.3 with fd-passing
> support.
> 
> Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for its prototype
> on this code.

this patch has been applied to the oFono tree. Thanks.

Regards

Marcel



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

* Re: [PATCH] Add HFP support through BlueZ
@ 2010-01-27 20:17                 ` Marcel Holtmann
  0 siblings, 0 replies; 51+ messages in thread
From: Marcel Holtmann @ 2010-01-27 20:17 UTC (permalink / raw)
  To: ofono

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

Hi Gustavo,

> It uses BlueZ through to get HFP working following the
> org.bluez.HandsfreeGateway and org.bluez.HandsfreeAgent from the BlueZ
> D-Bus API.
> 
> You need the HFP suport into BlueZ and the new dbus 1.3 with fd-passing
> support.
> 
> Many thanks to Zhenhua Zhang <zhenhua.zhang@intel.com> for its prototype
> on this code.

this patch has been applied to the oFono tree. Thanks.

Regards

Marcel



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

* Re: [PATCH 1/2] clean up audio/gateway.c
  2010-01-27 19:12           ` Gustavo F. Padovan
@ 2010-02-01  3:15             ` Zhenhua Zhang
  -1 siblings, 0 replies; 51+ messages in thread
From: Zhenhua Zhang @ 2010-02-01  3:15 UTC (permalink / raw)
  To: ofono, Gustavo F. Padovan; +Cc: linux-bluetooth

Hi Padovan,

On 01/28/2010 03:12 AM, Gustavo F. Padovan wrote:
> remove all code related to the AT engine
> ---
>   audio/gateway.c |  760 +------------------------------------------------------
>   1 files changed, 1 insertions(+), 759 deletions(-)
>
> diff --git a/audio/gateway.c b/audio/gateway.c
> index a1c1ea2..3dc09ff 100644
> --- a/audio/gateway.c
> +++ b/audio/gateway.c
> @@ -53,52 +53,6 @@
>   #include "dbus-common.h"
>
>    

I cannot apply your patch locally. Below is the error:

zzhan17@zzhan17-mobl:~/bluez$ git apply clean_up_audio_gateway.c.patch
error: patch failed: audio/gateway.c:53
error: audio/gateway.c: patch does not apply

Does any one have the same issue?

Thanks.

>   #define RFCOMM_BUF_SIZE 256
> -/* not-more-then-16 defined by GSM + 1 for NULL + padding */
> -#define AG_INDICATOR_DESCR_SIZE 20
> -#define AG_CALLER_NUM_SIZE 64  /* size of number + type */
> -
> -/* commands */
> -#define AG_FEATURES "AT+BRSF=26\r"     /* = 0x7F = All features supported */
> -#define AG_INDICATORS_SUPP "AT+CIND=?\r"
> -#define AG_INDICATORS_VAL "AT+CIND?\r"
> -#define AG_INDICATORS_ENABLE "AT+CMER=3,0,0,1\r"
> -#define AG_HOLD_MPTY_SUPP "AT+CHLD=?\r"
> -#define AG_CALLER_IDENT_ENABLE "AT+CLIP=1\r"
> -#define AG_CARRIER_FORMAT "AT+COPS=3,0\r"
> -#define AG_EXTENDED_RESULT_CODE "AT+CMEE=1\r"
> -
> -#define AG_FEATURE_3WAY 0x1
> -#define AG_FEATURE_EXTENDED_RES_CODE 0x100
> -/* Hold and multipary AG features.
> - * Comments below are copied from hands-free spec for reference */
> -/* Releases all held calls or sets User Determined User Busy (UDUB)
> - * for a waiting call */
> -#define AG_CHLD_0 0x01
> -/* Releases all active calls (if any exist) and accepts the other
> - * (held or waiting) call */
> -#define AG_CHLD_1 0x02
> -/* Releases specified active call only<x>  */
> -#define AG_CHLD_1x 0x04
> -/* Places all active calls (if any exist) on hold and accepts the other
> - * (held or waiting) call */
> -#define AG_CHLD_2 0x08
> -/* Request private consultation mode with specified call<x>  (Place all
> - * calls on hold EXCEPT the call<x>) */
> -#define AG_CHLD_2x 0x10
> -/* Adds a held call to the conversation */
> -#define AG_CHLD_3 0x20
> -/* Connects the two calls and disconnects the subscriber from both calls
> - * (Explicit Call Transfer). Support for this value and its associated
> - * functionality is optional for the HF. */
> -#define AG_CHLD_4 0x40
> -
> -#define OK_RESPONSE "\r\nOK\r\n"
> -#define ERROR_RESPONSE "\r\nERROR\r\n"
> -
> -struct indicator {
> -       gchar descr[AG_INDICATOR_DESCR_SIZE];
> -       gint value;
> -};
>
>   struct gateway {
>          gateway_state_t state;
> @@ -108,387 +62,10 @@ struct gateway {
>          gateway_stream_cb_t sco_start_cb;
>          void *sco_start_cb_data;
>          DBusMessage *connect_message;
> -       guint ag_features;
> -       guint hold_multiparty_features;
> -       GSList *indies;
> -       gboolean is_dialing;
> -       gboolean call_active;
> -
> -       int sp_gain;
> -       int mic_gain;
>   };
>
> -static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
> -                                       struct audio_device *device);
> -
>   int gateway_close(struct audio_device *device);
>
> -static void rfcomm_start_watch(struct audio_device *dev)
> -{
> -       struct gateway *gw = dev->gateway;
> -
> -       gw->rfcomm_watch_id = g_io_add_watch(gw->rfcomm,
> -                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
> -                       (GIOFunc) rfcomm_ag_data_cb, dev);
> -}
> -
> -static void rfcomm_stop_watch(struct audio_device *dev)
> -{
> -       struct gateway *gw = dev->gateway;
> -
> -       g_source_remove(gw->rfcomm_watch_id);
> -}
> -
> -static gboolean io_channel_write_all(GIOChannel *io, gchar *data,
> -                                       gsize count)
> -{
> -       gsize written = 0;
> -       GIOStatus status;
> -
> -       while (count>  0) {
> -               status = g_io_channel_write_chars(io, data, count,&written,
> -                                               NULL);
> -               if (status != G_IO_STATUS_NORMAL)
> -                       return FALSE;
> -
> -               data += written;
> -               count -= written;
> -       }
> -       return TRUE;
> -}
> -
> -/* it's worth to mention that data and response could be the same pointers */
> -static gboolean rfcomm_send_and_read(struct gateway *gw, gchar *data,
> -                                    gchar *response, gsize count)
> -{
> -       GIOChannel *rfcomm = gw->rfcomm;
> -       gsize read = 0;
> -       gboolean got_ok = FALSE;
> -       gboolean got_error = FALSE;
> -       gchar *resp_buf = response;
> -       gsize toread = RFCOMM_BUF_SIZE - 1;
> -       GIOStatus status;
> -
> -       if (!io_channel_write_all(rfcomm, data, count))
> -               return FALSE;
> -
> -       while (!(got_ok || got_error)) {
> -               status = g_io_channel_read_chars(rfcomm, resp_buf, toread,
> -&read, NULL);
> -               if (status == G_IO_STATUS_NORMAL)
> -                       resp_buf[read] = '\0';
> -               else {
> -                       debug("rfcomm_send_and_read(): %m");
> -                       return FALSE;
> -               }
> -               got_ok = NULL != strstr(resp_buf, OK_RESPONSE);
> -               got_error = NULL != strstr(resp_buf, ERROR_RESPONSE);
> -               resp_buf += read;
> -               toread -= read;
> -       }
> -       return TRUE;
> -}
> -
> -/* get<descr>  from the names: (<descr>, (<values>)), (<descr>, (<values>))
> - * ... */
> -static GSList *parse_indicator_names(gchar *names, GSList *indies)
> -{
> -       gchar *current = names - 1;
> -       GSList *result = indies;
> -       gchar *next;
> -       struct indicator *ind;
> -
> -       while (current != NULL) {
> -               current += 2;
> -               next = strstr(current, ",(");
> -               ind = g_slice_new(struct indicator);
> -               strncpy(ind->descr, current, 20);
> -               ind->descr[(intptr_t) next - (intptr_t) current] = '\0';
> -               result = g_slist_append(result, (gpointer) ind);
> -               current = strstr(next + 1, ",(");
> -       }
> -       return result;
> -}
> -
> -/* get values from<val0>,<val1>,... */
> -static GSList *parse_indicator_values(gchar *values, GSList *indies)
> -{
> -       gint val;
> -       gchar *current = values - 1;
> -       GSList *runner = indies;
> -       struct indicator *ind;
> -
> -       while (current != NULL) {
> -               current += 1;
> -               sscanf(current, "%d",&val);
> -               current = strchr(current, ',');
> -               ind = g_slist_nth_data(runner, 0);
> -               ind->value = val;
> -               runner = g_slist_next(runner);
> -       }
> -       return indies;
> -}
> -
> -/* get values from<val0>,<val1>,... */
> -static guint get_hold_mpty_features(gchar *features)
> -{
> -       guint result = 0;
> -
> -       if (strstr(features, "0"))
> -               result |= AG_CHLD_0;
> -
> -       if (strstr(features, "1"))
> -               result |= AG_CHLD_1;
> -
> -       if (strstr(features, "1x"))
> -               result |= AG_CHLD_1x;
> -
> -       if (strstr(features, "2"))
> -               result |= AG_CHLD_2;
> -
> -       if (strstr(features, "2x"))
> -               result |= AG_CHLD_2x;
> -
> -       if (strstr(features, "3"))
> -               result |= AG_CHLD_3;
> -
> -       if (strstr(features, "4"))
> -               result |= AG_CHLD_4;
> -
> -       return result;
> -}
> -
> -static gboolean establish_service_level_conn(struct gateway *gw)
> -{
> -       gchar buf[RFCOMM_BUF_SIZE];
> -       gboolean res;
> -
> -       debug("at the begin of establish_service_level_conn()");
> -       res = rfcomm_send_and_read(gw, AG_FEATURES, buf,
> -                               sizeof(AG_FEATURES) - 1);
> -       if (!res || sscanf(buf, "\r\n+BRSF:%d",&gw->ag_features) != 1)
> -               return FALSE;
> -
> -       debug("features are 0x%X", gw->ag_features);
> -       res = rfcomm_send_and_read(gw, AG_INDICATORS_SUPP, buf,
> -                               sizeof(AG_INDICATORS_SUPP) - 1);
> -       if (!res || !strstr(buf, "+CIND:"))
> -               return FALSE;
> -
> -       gw->indies = parse_indicator_names(strchr(buf, '('), NULL);
> -
> -       res = rfcomm_send_and_read(gw, AG_INDICATORS_VAL, buf,
> -               sizeof(AG_INDICATORS_VAL) - 1);
> -       if (!res || !strstr(buf, "+CIND:"))
> -               return FALSE;
> -
> -       gw->indies = parse_indicator_values(strchr(buf, ':') + 1, gw->indies);
> -
> -       res = rfcomm_send_and_read(gw, AG_INDICATORS_ENABLE, buf,
> -                               sizeof(AG_INDICATORS_ENABLE) - 1);
> -       if (!res || !strstr(buf, "OK"))
> -               return FALSE;
> -
> -       if ((gw->ag_features&  AG_FEATURE_3WAY) != 0) {
> -               res = rfcomm_send_and_read(gw, AG_HOLD_MPTY_SUPP, buf,
> -                               sizeof(AG_HOLD_MPTY_SUPP) - 1);
> -               if (!res || !strstr(buf, "+CHLD:")) {
> -                       g_slice_free1(RFCOMM_BUF_SIZE, buf);
> -                       return FALSE;
> -               }
> -               gw->hold_multiparty_features = get_hold_mpty_features(
> -                                                       strchr(buf, '('));
> -
> -       } else
> -               gw->hold_multiparty_features = 0;
> -
> -       debug("Service layer connection successfully established!");
> -       rfcomm_send_and_read(gw, AG_CALLER_IDENT_ENABLE, buf,
> -                       sizeof(AG_CALLER_IDENT_ENABLE) - 1);
> -       rfcomm_send_and_read(gw, AG_CARRIER_FORMAT, buf,
> -                       sizeof(AG_CARRIER_FORMAT) - 1);
> -       if ((gw->ag_features&  AG_FEATURE_EXTENDED_RES_CODE) != 0)
> -               rfcomm_send_and_read(gw, AG_EXTENDED_RESULT_CODE, buf,
> -                       sizeof(AG_EXTENDED_RESULT_CODE) - 1);
> -
> -       return TRUE;
> -}
> -
> -static void process_ind_change(struct audio_device *dev, guint index,
> -                                                       gint value)
> -{
> -       struct gateway *gw = dev->gateway;
> -       struct indicator *ind = g_slist_nth_data(gw->indies, index - 1);
> -       gchar *name = ind->descr;
> -
> -       ind->value = value;
> -
> -       debug("at the begin of process_ind_change, name is %s", name);
> -       if (!strcmp(name, "\"call\"")) {
> -               if (value>  0) {
> -                       g_dbus_emit_signal(dev->conn, dev->path,
> -                                       AUDIO_GATEWAY_INTERFACE,
> -                                       "CallStarted", DBUS_TYPE_INVALID);
> -                       gw->is_dialing = FALSE;
> -                       gw->call_active = TRUE;
> -               } else {
> -                       g_dbus_emit_signal(dev->conn, dev->path,
> -                                       AUDIO_GATEWAY_INTERFACE,
> -                                       "CallEnded", DBUS_TYPE_INVALID);
> -                       gw->call_active = FALSE;
> -               }
> -
> -       } else if (!strcmp(name, "\"callsetup\"")) {
> -               if (value == 0&&  gw->is_dialing) {
> -                       g_dbus_emit_signal(dev->conn, dev->path,
> -                                       AUDIO_GATEWAY_INTERFACE,
> -                                       "CallTerminated",
> -                                       DBUS_TYPE_INVALID);
> -                       gw->is_dialing = FALSE;
> -               } else if (!gw->is_dialing&&  value>  0)
> -                       gw->is_dialing = TRUE;
> -
> -       } else if (!strcmp(name, "\"callheld\"")) {
> -               /* FIXME: The following code is based on assumptions only.
> -                * Has to be tested for interoperability
> -                * I assume that callheld=2 would be sent when dial from HF
> -                * failed in case of 3-way call
> -                * Unfortunately this path is not covered by the HF spec so
> -                * the code has to be tested for interop
> -               */
> -               /* '2' means: all calls held, no active calls */
> -               if (value == 2) {
> -                       if (gw->is_dialing) {
> -                               g_dbus_emit_signal(dev->conn, dev->path,
> -                                       AUDIO_GATEWAY_INTERFACE,
> -                                       "CallTerminated",
> -                                       DBUS_TYPE_INVALID);
> -                               gw->is_dialing = FALSE;
> -                       }
> -               }
> -       } else if (!strcmp(name, "\"service\""))
> -               emit_property_changed(dev->conn, dev->path,
> -                               AUDIO_GATEWAY_INTERFACE, "RegistrationStatus",
> -                               DBUS_TYPE_UINT16,&value);
> -       else if (!strcmp(name, "\"signal\""))
> -               emit_property_changed(dev->conn, dev->path,
> -                               AUDIO_GATEWAY_INTERFACE, "SignalStrength",
> -                               DBUS_TYPE_UINT16,&value);
> -       else if (!strcmp(name, "\"roam\""))
> -               emit_property_changed(dev->conn, dev->path,
> -                               AUDIO_GATEWAY_INTERFACE, "RoamingStatus",
> -                               DBUS_TYPE_UINT16,&value);
> -       else if (!strcmp(name, "\"battchg\""))
> -               emit_property_changed(dev->conn, dev->path,
> -                               AUDIO_GATEWAY_INTERFACE, "BatteryCharge",
> -                               DBUS_TYPE_UINT16,&value);
> -}
> -
> -static void process_ring(struct audio_device *device, GIOChannel *chan,
> -                       gchar *buf)
> -{
> -       gchar number[AG_CALLER_NUM_SIZE];
> -       gchar *cli;
> -       gchar *sep;
> -       gsize read;
> -       GIOStatus status;
> -
> -       rfcomm_stop_watch(device);
> -       status = g_io_channel_read_chars(chan, buf, RFCOMM_BUF_SIZE - 1,&read, NULL);
> -       if (status != G_IO_STATUS_NORMAL)
> -               return;
> -
> -       debug("at the begin of process_ring");
> -       if (strlen(buf)>  AG_CALLER_NUM_SIZE + 10)
> -               error("process_ring(): buf is too long '%s'", buf);
> -       else if ((cli = strstr(buf, "\r\n+CLIP"))) {
> -               if (sscanf(cli, "\r\n+CLIP: \"%s", number) == 1) {
> -                       sep = strchr(number, '"');
> -                       sep[0] = '\0';
> -
> -                       /* FIXME:signal will be emitted on each RING+CLIP.
> -                        * That's bad */
> -                       cli = number;
> -                       g_dbus_emit_signal(device->conn, device->path,
> -                                       AUDIO_GATEWAY_INTERFACE, "Ring",
> -                                       DBUS_TYPE_STRING,&cli,
> -                                       DBUS_TYPE_INVALID);
> -                       device->gateway->is_dialing = TRUE;
> -               } else
> -                       error("process_ring(): '%s' in place of +CLIP after RING", buf);
> -
> -       }
> -
> -       rfcomm_start_watch(device);
> -}
> -
> -static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
> -                                       struct audio_device *device)
> -{
> -       gchar buf[RFCOMM_BUF_SIZE];
> -       struct gateway *gw;
> -       gsize read;
> -       /* some space for value */
> -       gchar indicator[AG_INDICATOR_DESCR_SIZE + 4];
> -       gint value;
> -       guint index;
> -       gchar *sep;
> -
> -       debug("at the begin of rfcomm_ag_data_cb()");
> -       if (cond&  G_IO_NVAL)
> -               return FALSE;
> -
> -       gw = device->gateway;
> -
> -       if (cond&  (G_IO_ERR | G_IO_HUP)) {
> -               debug("connection with remote BT is closed");
> -               gateway_close(device);
> -               return FALSE;
> -       }
> -
> -       if (g_io_channel_read_chars(chan, buf, sizeof(buf) - 1,&read, NULL)
> -                       != G_IO_STATUS_NORMAL)
> -               return TRUE;
> -       buf[read] = '\0';
> -
> -       if (strlen(buf)>  AG_INDICATOR_DESCR_SIZE + 14)
> -               error("rfcomm_ag_data_cb(): buf is too long '%s'", buf);
> -       else if (sscanf(buf, "\r\n+CIEV:%s\r\n", indicator) == 1) {
> -               sep = strchr(indicator, ',');
> -               sep[0] = '\0';
> -               sep += 1;
> -               index = atoi(indicator);
> -               value = atoi(sep);
> -               process_ind_change(device, index, value);
> -       } else if (strstr(buf, "RING"))
> -               process_ring(device, chan, buf);
> -       else if (sscanf(buf, "\r\n+BVRA:%d\r\n",&value) == 1) {
> -               if (value == 0)
> -                       g_dbus_emit_signal(device->conn, device->path,
> -                                       AUDIO_GATEWAY_INTERFACE,
> -                                       "VoiceRecognitionActive",
> -                                       DBUS_TYPE_INVALID);
> -               else
> -                       g_dbus_emit_signal(device->conn, device->path,
> -                                       AUDIO_GATEWAY_INTERFACE,
> -                                       "VoiceRecognitionInactive",
> -                                       DBUS_TYPE_INVALID);
> -       } else if (sscanf(buf, "\r\n+VGS:%d\r\n",&value) == 1) {
> -               gw->sp_gain = value;
> -               emit_property_changed(device->conn, device->path,
> -                               AUDIO_GATEWAY_INTERFACE, "SpeakerGain",
> -                               DBUS_TYPE_UINT16,&value);
> -       } else if (sscanf(buf, "\r\n+VGM:%d\r\n",&value) == 1) {
> -               gw->mic_gain = value;
> -               emit_property_changed(device->conn, device->path,
> -                               AUDIO_GATEWAY_INTERFACE, "MicrophoneGain",
> -                               DBUS_TYPE_UINT16,&value);
> -       } else
> -               error("rfcomm_ag_data_cb(): read wrong data '%s'", buf);
> -
> -       return TRUE;
> -}
> -
>   static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
>                          struct audio_device *dev)
>   {
> @@ -541,7 +118,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
>   {
>          struct audio_device *dev = user_data;
>          struct gateway *gw = dev->gateway;
> -       DBusMessage *conn_mes = gw->connect_message;
>          gchar gw_addr[18];
>          GIOFlags flags;
>
> @@ -563,29 +139,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
>          if (!gw->rfcomm)
>                  gw->rfcomm = g_io_channel_ref(chan);
>
> -       if (establish_service_level_conn(dev->gateway)) {
> -               gboolean value = TRUE;
> -
> -               debug("%s: Connected to %s", dev->path, gw_addr);
> -               rfcomm_start_watch(dev);
> -               if (conn_mes) {
> -                       DBusMessage *reply =
> -                               dbus_message_new_method_return(conn_mes);
> -                       dbus_connection_send(dev->conn, reply, NULL);
> -                       dbus_message_unref(reply);
> -                       dbus_message_unref(conn_mes);
> -                       gw->connect_message = NULL;
> -               }
> -
> -               gw->state = GATEWAY_STATE_CONNECTED;
> -               emit_property_changed(dev->conn, dev->path,
> -                               AUDIO_GATEWAY_INTERFACE,
> -                               "Connected", DBUS_TYPE_BOOLEAN,&value);
> -               return;
> -       } else
> -               error("%s: Failed to establish service layer connection to %s",
> -                       dev->path, gw_addr);
> -
>          if (NULL != gw->sco_start_cb)
>                  gw->sco_start_cb(NULL, gw->sco_start_cb_data);
>
> @@ -732,321 +285,20 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
>          return reply;
>   }
>
> -static DBusMessage *process_ag_reponse(DBusMessage *msg, gchar *response)
> -{
> -       DBusMessage *reply;
> -
> -
> -       debug("in process_ag_reponse, response is %s", response);
> -       if (strstr(response, OK_RESPONSE))
> -               reply = dbus_message_new_method_return(msg);
> -       else {
> -               /* FIXME: some code should be here to processes errors
> -                *  in better fasion */
> -               debug("AG responded with '%s' to %s method call", response,
> -                               dbus_message_get_member(msg));
> -               reply = dbus_message_new_error(msg, ERROR_INTERFACE
> -                                       ".OperationFailed",
> -                                       "Operation failed.See log for details");
> -       }
> -       return reply;
> -}
> -
> -static DBusMessage *process_simple(DBusMessage *msg, struct audio_device *dev,
> -                                       gchar *data)
> -{
> -       struct gateway *gw = dev->gateway;
> -       gchar buf[RFCOMM_BUF_SIZE];
> -
> -       rfcomm_stop_watch(dev);
> -       rfcomm_send_and_read(gw, data, buf, strlen(data));
> -       rfcomm_start_watch(dev);
> -       return process_ag_reponse(msg, buf);
> -}
> -
> -#define AG_ANSWER "ATA\r"
> -
> -static DBusMessage *ag_answer(DBusConnection *conn, DBusMessage *msg,
> -                               void *data)
> -{
> -       struct audio_device *dev = data;
> -       struct gateway *gw = dev->gateway;
> -
> -       if (!gw->rfcomm)
> -               return g_dbus_create_error(msg, ERROR_INTERFACE
> -                                       ".NotConnected",
> -                                       "Not Connected");
> -
> -       if (gw->call_active)
> -               return g_dbus_create_error(msg, ERROR_INTERFACE
> -                                       ".CallAlreadyAnswered",
> -                                       "Call AlreadyAnswered");
> -
> -       return process_simple(msg, dev, AG_ANSWER);
> -}
> -
> -#define AG_HANGUP "AT+CHUP\r"
> -
> -static DBusMessage *ag_terminate_call(DBusConnection *conn, DBusMessage *msg,
> -                               void *data)
> -{
> -       struct audio_device *dev = data;
> -       struct gateway *gw = dev->gateway;
> -
> -       if (!gw->rfcomm)
> -               return g_dbus_create_error(msg, ERROR_INTERFACE
> -                                       ".NotConnected",
> -                                       "Not Connected");
> -
> -       return process_simple(msg, dev, AG_HANGUP);
> -}
> -
> -/* according to GSM spec */
> -#define ALLOWED_NUMBER_SYMBOLS "1234567890*#ABCD"
> -#define AG_PLACE_CALL "ATD%s;\r"
> -/* dialing from memory is not supported as headset spec doesn't define a way
> - * to retreive phone memory entries.
> - */
> -static DBusMessage *ag_call(DBusConnection *conn, DBusMessage *msg,
> -                               void *data)
> -{
> -       struct audio_device *device = data;
> -       struct gateway *gw = device->gateway;
> -       gchar buf[RFCOMM_BUF_SIZE];
> -       gchar *number;
> -       gint atd_len;
> -       DBusMessage *result;
> -
> -       debug("at the begin of ag_call()");
> -       if (!gw->rfcomm)
> -               return g_dbus_create_error(msg, ERROR_INTERFACE
> -                                       ".NotConnected",
> -                                       "Not Connected");
> -
> -       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING,&number,
> -                               DBUS_TYPE_INVALID);
> -       if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
> -               return dbus_message_new_error(msg,
> -                       ERROR_INTERFACE ".BadNumber",
> -                       "Number contains characters which are not allowed");
> -
> -       atd_len = sprintf(buf, AG_PLACE_CALL, number);
> -       rfcomm_stop_watch(device);
> -       rfcomm_send_and_read(gw, buf, buf, atd_len);
> -       rfcomm_start_watch(device);
> -
> -       result = process_ag_reponse(msg, buf);
> -       return result;
> -}
> -
> -#define AG_GET_CARRIER "AT+COPS?\r"
> -
> -static DBusMessage *ag_get_operator(DBusConnection *conn, DBusMessage *msg,
> -                                       void *data)
> -{
> -       struct audio_device *dev = (struct audio_device *) data;
> -       struct gateway *gw = dev->gateway;
> -       gchar buf[RFCOMM_BUF_SIZE];
> -       GIOChannel *rfcomm = gw->rfcomm;
> -       gsize read;
> -       gchar *result, *sep;
> -       DBusMessage *reply;
> -       GIOStatus status;
> -
> -       if (!gw->rfcomm)
> -               return g_dbus_create_error(msg, ERROR_INTERFACE
> -                                       ".NotConnected",
> -                                       "Not Connected");
> -
> -       rfcomm_stop_watch(dev);
> -       io_channel_write_all(rfcomm, AG_GET_CARRIER, strlen(AG_GET_CARRIER));
> -
> -       status = g_io_channel_read_chars(rfcomm, buf, RFCOMM_BUF_SIZE - 1,
> -&read, NULL);
> -       rfcomm_start_watch(dev);
> -       if (G_IO_STATUS_NORMAL == status) {
> -               buf[read] = '\0';
> -               if (strstr(buf, "+COPS")) {
> -                       if (!strrchr(buf, ','))
> -                               result = "0";
> -                       else {
> -                               result = strchr(buf, '\"') + 1;
> -                               sep = strchr(result, '\"');
> -                               sep[0] = '\0';
> -                       }
> -
> -                       reply = dbus_message_new_method_return(msg);
> -                       dbus_message_append_args(reply, DBUS_TYPE_STRING,
> -&result, DBUS_TYPE_INVALID);
> -               } else {
> -                       info("ag_get_operator(): '+COPS' expected but"
> -                               " '%s' received", buf);
> -                       reply = dbus_message_new_error(msg, ERROR_INTERFACE
> -                                               ".Failed",
> -                                               "Unexpected response from AG");
> -               }
> -       } else {
> -               error("ag_get_operator(): %m");
> -               reply = dbus_message_new_error(msg, ERROR_INTERFACE
> -                                       ".ConnectionFailed",
> -                                       "Failed to receive response from AG");
> -       }
> -
> -       return reply;
> -}
> -
> -#define AG_SEND_DTMF "AT+VTS=%c\r"
> -static DBusMessage *ag_send_dtmf(DBusConnection *conn, DBusMessage *msg,
> -                               void *data)
> -{
> -       struct audio_device *device = data;
> -       struct gateway *gw = device->gateway;
> -       gchar buf[RFCOMM_BUF_SIZE];
> -       gchar *number;
> -       gint com_len;
> -       gboolean got_ok = TRUE;
> -       gint num_len;
> -       gint i = 0;
> -
> -       if (!gw->rfcomm)
> -               return g_dbus_create_error(msg, ERROR_INTERFACE
> -                                       ".NotConnected",
> -                                       "Not Connected");
> -
> -       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING,&number,
> -                               DBUS_TYPE_INVALID);
> -       if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
> -               return dbus_message_new_error(msg,
> -                       ERROR_INTERFACE ".BadNumber",
> -                       "Number contains characters which are not allowed");
> -
> -       num_len = strlen(number);
> -       rfcomm_stop_watch(device);
> -       while (i<  num_len&&  got_ok) {
> -               com_len = sprintf(buf, AG_SEND_DTMF, number[i]);
> -               rfcomm_send_and_read(gw, buf, buf, com_len);
> -               got_ok = NULL != strstr(buf, OK_RESPONSE);
> -               i += 1;
> -       }
> -       rfcomm_start_watch(device);
> -       return process_ag_reponse(msg, buf);
> -}
> -
> -#define AG_GET_SUBSCRIBER_NUMS "AT+CNUM\r"
> -#define CNUM_LEN 5             /* length of "+CNUM" string */
> -#define MAX_NUMBER_CNT 16
> -static DBusMessage *ag_get_subscriber_num(DBusConnection *conn,
> -                                       DBusMessage *msg, void *data)
> -{
> -       struct audio_device *device = data;
> -       struct gateway *gw = device->gateway;
> -       gchar buf[RFCOMM_BUF_SIZE];
> -       gchar *number, *end;
> -       DBusMessage *reply = dbus_message_new_method_return(msg);
> -
> -       if (!gw->rfcomm)
> -               return g_dbus_create_error(msg, ERROR_INTERFACE
> -                                       ".NotConnected",
> -                                       "Not Connected");
> -
> -       rfcomm_stop_watch(device);
> -       rfcomm_send_and_read(gw, AG_GET_SUBSCRIBER_NUMS, buf,
> -                       strlen(AG_GET_SUBSCRIBER_NUMS));
> -       rfcomm_start_watch(device);
> -
> -       if (strlen(buf)>  AG_CALLER_NUM_SIZE + 21)
> -               error("ag_get_subscriber_num(): buf is too long '%s'", buf);
> -       else if (strstr(buf, "+CNUM")) {
> -               number = strchr(buf, ',');
> -               number++;
> -               end = strchr(number, ',');
> -               if (end) {
> -                       *end = '\0';
> -                       dbus_message_append_args(reply, DBUS_TYPE_STRING,
> -&number, DBUS_TYPE_INVALID);
> -               }
> -       } else
> -               error("ag_get_subscriber_num(): read wrong data '%s'", buf);
> -
> -       return reply;
> -}
> -
>   static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
>                                          void *data)
>   {
> -       struct audio_device *device = data;
> -       struct gateway *gw = device->gateway;
> -       DBusMessage *reply;
> -       DBusMessageIter iter;
> -       DBusMessageIter dict;
> -       gboolean value;
> -       guint index = 0;
> -       struct indicator *ind;
> -
> -       reply = dbus_message_new_method_return(msg);
> -       if (!reply)
> -               return NULL;
> -
> -       dbus_message_iter_init_append(reply,&iter);
> -
> -       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
> -                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
> -                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
> -                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING,&dict);
> -
> -       /* Connected */
> -       value = gateway_is_connected(device);
> -       dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN,&value);
> -
> -       if (!value)
> -               goto done;
> -
> -       while ((ind = g_slist_nth_data(gw->indies, index))) {
> -               if(!strcmp(ind->descr, "\"service\""))
> -                       dict_append_entry(&dict, "RegistrationStatus",
> -                                       DBUS_TYPE_UINT16,&ind->value);
> -               else if (!strcmp(ind->descr, "\"signal\""))
> -                       dict_append_entry(&dict, "SignalStrength",
> -                                       DBUS_TYPE_UINT16,&ind->value);
> -               else if (!strcmp(ind->descr, "\"roam\""))
> -                       dict_append_entry(&dict, "RoamingStatus",
> -                                       DBUS_TYPE_UINT16,&ind->value);
> -               else if (!strcmp(ind->descr, "\"battchg\""))
> -                       dict_append_entry(&dict, "BatteryCharge",
> -                                       DBUS_TYPE_UINT16,&ind->value);
> -               index++;
> -       }
> -
> -       /* SpeakerGain */
> -       dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16,
> -&device->gateway->sp_gain);
> -
> -       /* MicrophoneGain */
> -       dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16,
> -&device->gateway->mic_gain);
> -done:
> -       dbus_message_iter_close_container(&iter,&dict);
> -       return reply;
> +       return NULL;
>   }
>
>   static GDBusMethodTable gateway_methods[] = {
>          { "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
>          { "Disconnect", "", "", ag_disconnect },
> -       { "AnswerCall", "", "", ag_answer },
> -       { "TerminateCall", "", "", ag_terminate_call },
> -       { "Call", "s", "", ag_call },
> -       { "GetOperatorName", "", "s", ag_get_operator },
> -       { "SendDTMF", "s", "", ag_send_dtmf },
> -       { "GetSubscriberNumber", "", "s", ag_get_subscriber_num },
>          { "GetProperties", "", "a{sv}", ag_get_properties },
>          { NULL, NULL, NULL, NULL }
>   };
>
>   static GDBusSignalTable gateway_signals[] = {
> -       { "Ring", "s" },
> -       { "CallTerminated", "" },
> -       { "CallStarted", "" },
> -       { "CallEnded", "" },
>          { "PropertyChanged", "sv" },
>          { NULL, NULL }
>   };
> @@ -1063,9 +315,6 @@ struct gateway *gateway_init(struct audio_device *dev)
>
>          debug("in gateway_init, dev is %p", dev);
>          gw = g_new0(struct gateway, 1);
> -       gw->indies = NULL;
> -       gw->is_dialing = FALSE;
> -       gw->call_active = FALSE;
>          gw->state = GATEWAY_STATE_DISCONNECTED;
>          return gw;
>
> @@ -1107,11 +356,6 @@ void gateway_start_service(struct audio_device *device)
>          rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
>   }
>
> -static void indicator_slice_free(gpointer mem)
> -{
> -       g_slice_free(struct indicator, mem);
> -}
> -
>   int gateway_close(struct audio_device *device)
>   {
>          struct gateway *gw = device->gateway;
> @@ -1119,8 +363,6 @@ int gateway_close(struct audio_device *device)
>          GIOChannel *sco = gw->sco;
>          gboolean value = FALSE;
>
> -       g_slist_foreach(gw->indies, (GFunc) indicator_slice_free, NULL);
> -       g_slist_free(gw->indies);
>          if (rfcomm) {
>                  g_io_channel_shutdown(rfcomm, TRUE, NULL);
>                  g_io_channel_unref(rfcomm);
> --
> 1.6.4.4
>
> _______________________________________________
> ofono mailing list
> ofono@ofono.org
> http://lists.ofono.org/listinfo/ofono
>
>    


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

* Re: [PATCH 1/2] clean up audio/gateway.c
@ 2010-02-01  3:15             ` Zhenhua Zhang
  0 siblings, 0 replies; 51+ messages in thread
From: Zhenhua Zhang @ 2010-02-01  3:15 UTC (permalink / raw)
  To: ofono

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

Hi Padovan,

On 01/28/2010 03:12 AM, Gustavo F. Padovan wrote:
> remove all code related to the AT engine
> ---
>   audio/gateway.c |  760 +------------------------------------------------------
>   1 files changed, 1 insertions(+), 759 deletions(-)
>
> diff --git a/audio/gateway.c b/audio/gateway.c
> index a1c1ea2..3dc09ff 100644
> --- a/audio/gateway.c
> +++ b/audio/gateway.c
> @@ -53,52 +53,6 @@
>   #include "dbus-common.h"
>
>    

I cannot apply your patch locally. Below is the error:

zzhan17(a)zzhan17-mobl:~/bluez$ git apply clean_up_audio_gateway.c.patch
error: patch failed: audio/gateway.c:53
error: audio/gateway.c: patch does not apply

Does any one have the same issue?

Thanks.

>   #define RFCOMM_BUF_SIZE 256
> -/* not-more-then-16 defined by GSM + 1 for NULL + padding */
> -#define AG_INDICATOR_DESCR_SIZE 20
> -#define AG_CALLER_NUM_SIZE 64  /* size of number + type */
> -
> -/* commands */
> -#define AG_FEATURES "AT+BRSF=26\r"     /* = 0x7F = All features supported */
> -#define AG_INDICATORS_SUPP "AT+CIND=?\r"
> -#define AG_INDICATORS_VAL "AT+CIND?\r"
> -#define AG_INDICATORS_ENABLE "AT+CMER=3,0,0,1\r"
> -#define AG_HOLD_MPTY_SUPP "AT+CHLD=?\r"
> -#define AG_CALLER_IDENT_ENABLE "AT+CLIP=1\r"
> -#define AG_CARRIER_FORMAT "AT+COPS=3,0\r"
> -#define AG_EXTENDED_RESULT_CODE "AT+CMEE=1\r"
> -
> -#define AG_FEATURE_3WAY 0x1
> -#define AG_FEATURE_EXTENDED_RES_CODE 0x100
> -/* Hold and multipary AG features.
> - * Comments below are copied from hands-free spec for reference */
> -/* Releases all held calls or sets User Determined User Busy (UDUB)
> - * for a waiting call */
> -#define AG_CHLD_0 0x01
> -/* Releases all active calls (if any exist) and accepts the other
> - * (held or waiting) call */
> -#define AG_CHLD_1 0x02
> -/* Releases specified active call only<x>  */
> -#define AG_CHLD_1x 0x04
> -/* Places all active calls (if any exist) on hold and accepts the other
> - * (held or waiting) call */
> -#define AG_CHLD_2 0x08
> -/* Request private consultation mode with specified call<x>  (Place all
> - * calls on hold EXCEPT the call<x>) */
> -#define AG_CHLD_2x 0x10
> -/* Adds a held call to the conversation */
> -#define AG_CHLD_3 0x20
> -/* Connects the two calls and disconnects the subscriber from both calls
> - * (Explicit Call Transfer). Support for this value and its associated
> - * functionality is optional for the HF. */
> -#define AG_CHLD_4 0x40
> -
> -#define OK_RESPONSE "\r\nOK\r\n"
> -#define ERROR_RESPONSE "\r\nERROR\r\n"
> -
> -struct indicator {
> -       gchar descr[AG_INDICATOR_DESCR_SIZE];
> -       gint value;
> -};
>
>   struct gateway {
>          gateway_state_t state;
> @@ -108,387 +62,10 @@ struct gateway {
>          gateway_stream_cb_t sco_start_cb;
>          void *sco_start_cb_data;
>          DBusMessage *connect_message;
> -       guint ag_features;
> -       guint hold_multiparty_features;
> -       GSList *indies;
> -       gboolean is_dialing;
> -       gboolean call_active;
> -
> -       int sp_gain;
> -       int mic_gain;
>   };
>
> -static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
> -                                       struct audio_device *device);
> -
>   int gateway_close(struct audio_device *device);
>
> -static void rfcomm_start_watch(struct audio_device *dev)
> -{
> -       struct gateway *gw = dev->gateway;
> -
> -       gw->rfcomm_watch_id = g_io_add_watch(gw->rfcomm,
> -                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
> -                       (GIOFunc) rfcomm_ag_data_cb, dev);
> -}
> -
> -static void rfcomm_stop_watch(struct audio_device *dev)
> -{
> -       struct gateway *gw = dev->gateway;
> -
> -       g_source_remove(gw->rfcomm_watch_id);
> -}
> -
> -static gboolean io_channel_write_all(GIOChannel *io, gchar *data,
> -                                       gsize count)
> -{
> -       gsize written = 0;
> -       GIOStatus status;
> -
> -       while (count>  0) {
> -               status = g_io_channel_write_chars(io, data, count,&written,
> -                                               NULL);
> -               if (status != G_IO_STATUS_NORMAL)
> -                       return FALSE;
> -
> -               data += written;
> -               count -= written;
> -       }
> -       return TRUE;
> -}
> -
> -/* it's worth to mention that data and response could be the same pointers */
> -static gboolean rfcomm_send_and_read(struct gateway *gw, gchar *data,
> -                                    gchar *response, gsize count)
> -{
> -       GIOChannel *rfcomm = gw->rfcomm;
> -       gsize read = 0;
> -       gboolean got_ok = FALSE;
> -       gboolean got_error = FALSE;
> -       gchar *resp_buf = response;
> -       gsize toread = RFCOMM_BUF_SIZE - 1;
> -       GIOStatus status;
> -
> -       if (!io_channel_write_all(rfcomm, data, count))
> -               return FALSE;
> -
> -       while (!(got_ok || got_error)) {
> -               status = g_io_channel_read_chars(rfcomm, resp_buf, toread,
> -&read, NULL);
> -               if (status == G_IO_STATUS_NORMAL)
> -                       resp_buf[read] = '\0';
> -               else {
> -                       debug("rfcomm_send_and_read(): %m");
> -                       return FALSE;
> -               }
> -               got_ok = NULL != strstr(resp_buf, OK_RESPONSE);
> -               got_error = NULL != strstr(resp_buf, ERROR_RESPONSE);
> -               resp_buf += read;
> -               toread -= read;
> -       }
> -       return TRUE;
> -}
> -
> -/* get<descr>  from the names: (<descr>, (<values>)), (<descr>, (<values>))
> - * ... */
> -static GSList *parse_indicator_names(gchar *names, GSList *indies)
> -{
> -       gchar *current = names - 1;
> -       GSList *result = indies;
> -       gchar *next;
> -       struct indicator *ind;
> -
> -       while (current != NULL) {
> -               current += 2;
> -               next = strstr(current, ",(");
> -               ind = g_slice_new(struct indicator);
> -               strncpy(ind->descr, current, 20);
> -               ind->descr[(intptr_t) next - (intptr_t) current] = '\0';
> -               result = g_slist_append(result, (gpointer) ind);
> -               current = strstr(next + 1, ",(");
> -       }
> -       return result;
> -}
> -
> -/* get values from<val0>,<val1>,... */
> -static GSList *parse_indicator_values(gchar *values, GSList *indies)
> -{
> -       gint val;
> -       gchar *current = values - 1;
> -       GSList *runner = indies;
> -       struct indicator *ind;
> -
> -       while (current != NULL) {
> -               current += 1;
> -               sscanf(current, "%d",&val);
> -               current = strchr(current, ',');
> -               ind = g_slist_nth_data(runner, 0);
> -               ind->value = val;
> -               runner = g_slist_next(runner);
> -       }
> -       return indies;
> -}
> -
> -/* get values from<val0>,<val1>,... */
> -static guint get_hold_mpty_features(gchar *features)
> -{
> -       guint result = 0;
> -
> -       if (strstr(features, "0"))
> -               result |= AG_CHLD_0;
> -
> -       if (strstr(features, "1"))
> -               result |= AG_CHLD_1;
> -
> -       if (strstr(features, "1x"))
> -               result |= AG_CHLD_1x;
> -
> -       if (strstr(features, "2"))
> -               result |= AG_CHLD_2;
> -
> -       if (strstr(features, "2x"))
> -               result |= AG_CHLD_2x;
> -
> -       if (strstr(features, "3"))
> -               result |= AG_CHLD_3;
> -
> -       if (strstr(features, "4"))
> -               result |= AG_CHLD_4;
> -
> -       return result;
> -}
> -
> -static gboolean establish_service_level_conn(struct gateway *gw)
> -{
> -       gchar buf[RFCOMM_BUF_SIZE];
> -       gboolean res;
> -
> -       debug("at the begin of establish_service_level_conn()");
> -       res = rfcomm_send_and_read(gw, AG_FEATURES, buf,
> -                               sizeof(AG_FEATURES) - 1);
> -       if (!res || sscanf(buf, "\r\n+BRSF:%d",&gw->ag_features) != 1)
> -               return FALSE;
> -
> -       debug("features are 0x%X", gw->ag_features);
> -       res = rfcomm_send_and_read(gw, AG_INDICATORS_SUPP, buf,
> -                               sizeof(AG_INDICATORS_SUPP) - 1);
> -       if (!res || !strstr(buf, "+CIND:"))
> -               return FALSE;
> -
> -       gw->indies = parse_indicator_names(strchr(buf, '('), NULL);
> -
> -       res = rfcomm_send_and_read(gw, AG_INDICATORS_VAL, buf,
> -               sizeof(AG_INDICATORS_VAL) - 1);
> -       if (!res || !strstr(buf, "+CIND:"))
> -               return FALSE;
> -
> -       gw->indies = parse_indicator_values(strchr(buf, ':') + 1, gw->indies);
> -
> -       res = rfcomm_send_and_read(gw, AG_INDICATORS_ENABLE, buf,
> -                               sizeof(AG_INDICATORS_ENABLE) - 1);
> -       if (!res || !strstr(buf, "OK"))
> -               return FALSE;
> -
> -       if ((gw->ag_features&  AG_FEATURE_3WAY) != 0) {
> -               res = rfcomm_send_and_read(gw, AG_HOLD_MPTY_SUPP, buf,
> -                               sizeof(AG_HOLD_MPTY_SUPP) - 1);
> -               if (!res || !strstr(buf, "+CHLD:")) {
> -                       g_slice_free1(RFCOMM_BUF_SIZE, buf);
> -                       return FALSE;
> -               }
> -               gw->hold_multiparty_features = get_hold_mpty_features(
> -                                                       strchr(buf, '('));
> -
> -       } else
> -               gw->hold_multiparty_features = 0;
> -
> -       debug("Service layer connection successfully established!");
> -       rfcomm_send_and_read(gw, AG_CALLER_IDENT_ENABLE, buf,
> -                       sizeof(AG_CALLER_IDENT_ENABLE) - 1);
> -       rfcomm_send_and_read(gw, AG_CARRIER_FORMAT, buf,
> -                       sizeof(AG_CARRIER_FORMAT) - 1);
> -       if ((gw->ag_features&  AG_FEATURE_EXTENDED_RES_CODE) != 0)
> -               rfcomm_send_and_read(gw, AG_EXTENDED_RESULT_CODE, buf,
> -                       sizeof(AG_EXTENDED_RESULT_CODE) - 1);
> -
> -       return TRUE;
> -}
> -
> -static void process_ind_change(struct audio_device *dev, guint index,
> -                                                       gint value)
> -{
> -       struct gateway *gw = dev->gateway;
> -       struct indicator *ind = g_slist_nth_data(gw->indies, index - 1);
> -       gchar *name = ind->descr;
> -
> -       ind->value = value;
> -
> -       debug("at the begin of process_ind_change, name is %s", name);
> -       if (!strcmp(name, "\"call\"")) {
> -               if (value>  0) {
> -                       g_dbus_emit_signal(dev->conn, dev->path,
> -                                       AUDIO_GATEWAY_INTERFACE,
> -                                       "CallStarted", DBUS_TYPE_INVALID);
> -                       gw->is_dialing = FALSE;
> -                       gw->call_active = TRUE;
> -               } else {
> -                       g_dbus_emit_signal(dev->conn, dev->path,
> -                                       AUDIO_GATEWAY_INTERFACE,
> -                                       "CallEnded", DBUS_TYPE_INVALID);
> -                       gw->call_active = FALSE;
> -               }
> -
> -       } else if (!strcmp(name, "\"callsetup\"")) {
> -               if (value == 0&&  gw->is_dialing) {
> -                       g_dbus_emit_signal(dev->conn, dev->path,
> -                                       AUDIO_GATEWAY_INTERFACE,
> -                                       "CallTerminated",
> -                                       DBUS_TYPE_INVALID);
> -                       gw->is_dialing = FALSE;
> -               } else if (!gw->is_dialing&&  value>  0)
> -                       gw->is_dialing = TRUE;
> -
> -       } else if (!strcmp(name, "\"callheld\"")) {
> -               /* FIXME: The following code is based on assumptions only.
> -                * Has to be tested for interoperability
> -                * I assume that callheld=2 would be sent when dial from HF
> -                * failed in case of 3-way call
> -                * Unfortunately this path is not covered by the HF spec so
> -                * the code has to be tested for interop
> -               */
> -               /* '2' means: all calls held, no active calls */
> -               if (value == 2) {
> -                       if (gw->is_dialing) {
> -                               g_dbus_emit_signal(dev->conn, dev->path,
> -                                       AUDIO_GATEWAY_INTERFACE,
> -                                       "CallTerminated",
> -                                       DBUS_TYPE_INVALID);
> -                               gw->is_dialing = FALSE;
> -                       }
> -               }
> -       } else if (!strcmp(name, "\"service\""))
> -               emit_property_changed(dev->conn, dev->path,
> -                               AUDIO_GATEWAY_INTERFACE, "RegistrationStatus",
> -                               DBUS_TYPE_UINT16,&value);
> -       else if (!strcmp(name, "\"signal\""))
> -               emit_property_changed(dev->conn, dev->path,
> -                               AUDIO_GATEWAY_INTERFACE, "SignalStrength",
> -                               DBUS_TYPE_UINT16,&value);
> -       else if (!strcmp(name, "\"roam\""))
> -               emit_property_changed(dev->conn, dev->path,
> -                               AUDIO_GATEWAY_INTERFACE, "RoamingStatus",
> -                               DBUS_TYPE_UINT16,&value);
> -       else if (!strcmp(name, "\"battchg\""))
> -               emit_property_changed(dev->conn, dev->path,
> -                               AUDIO_GATEWAY_INTERFACE, "BatteryCharge",
> -                               DBUS_TYPE_UINT16,&value);
> -}
> -
> -static void process_ring(struct audio_device *device, GIOChannel *chan,
> -                       gchar *buf)
> -{
> -       gchar number[AG_CALLER_NUM_SIZE];
> -       gchar *cli;
> -       gchar *sep;
> -       gsize read;
> -       GIOStatus status;
> -
> -       rfcomm_stop_watch(device);
> -       status = g_io_channel_read_chars(chan, buf, RFCOMM_BUF_SIZE - 1,&read, NULL);
> -       if (status != G_IO_STATUS_NORMAL)
> -               return;
> -
> -       debug("at the begin of process_ring");
> -       if (strlen(buf)>  AG_CALLER_NUM_SIZE + 10)
> -               error("process_ring(): buf is too long '%s'", buf);
> -       else if ((cli = strstr(buf, "\r\n+CLIP"))) {
> -               if (sscanf(cli, "\r\n+CLIP: \"%s", number) == 1) {
> -                       sep = strchr(number, '"');
> -                       sep[0] = '\0';
> -
> -                       /* FIXME:signal will be emitted on each RING+CLIP.
> -                        * That's bad */
> -                       cli = number;
> -                       g_dbus_emit_signal(device->conn, device->path,
> -                                       AUDIO_GATEWAY_INTERFACE, "Ring",
> -                                       DBUS_TYPE_STRING,&cli,
> -                                       DBUS_TYPE_INVALID);
> -                       device->gateway->is_dialing = TRUE;
> -               } else
> -                       error("process_ring(): '%s' in place of +CLIP after RING", buf);
> -
> -       }
> -
> -       rfcomm_start_watch(device);
> -}
> -
> -static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
> -                                       struct audio_device *device)
> -{
> -       gchar buf[RFCOMM_BUF_SIZE];
> -       struct gateway *gw;
> -       gsize read;
> -       /* some space for value */
> -       gchar indicator[AG_INDICATOR_DESCR_SIZE + 4];
> -       gint value;
> -       guint index;
> -       gchar *sep;
> -
> -       debug("at the begin of rfcomm_ag_data_cb()");
> -       if (cond&  G_IO_NVAL)
> -               return FALSE;
> -
> -       gw = device->gateway;
> -
> -       if (cond&  (G_IO_ERR | G_IO_HUP)) {
> -               debug("connection with remote BT is closed");
> -               gateway_close(device);
> -               return FALSE;
> -       }
> -
> -       if (g_io_channel_read_chars(chan, buf, sizeof(buf) - 1,&read, NULL)
> -                       != G_IO_STATUS_NORMAL)
> -               return TRUE;
> -       buf[read] = '\0';
> -
> -       if (strlen(buf)>  AG_INDICATOR_DESCR_SIZE + 14)
> -               error("rfcomm_ag_data_cb(): buf is too long '%s'", buf);
> -       else if (sscanf(buf, "\r\n+CIEV:%s\r\n", indicator) == 1) {
> -               sep = strchr(indicator, ',');
> -               sep[0] = '\0';
> -               sep += 1;
> -               index = atoi(indicator);
> -               value = atoi(sep);
> -               process_ind_change(device, index, value);
> -       } else if (strstr(buf, "RING"))
> -               process_ring(device, chan, buf);
> -       else if (sscanf(buf, "\r\n+BVRA:%d\r\n",&value) == 1) {
> -               if (value == 0)
> -                       g_dbus_emit_signal(device->conn, device->path,
> -                                       AUDIO_GATEWAY_INTERFACE,
> -                                       "VoiceRecognitionActive",
> -                                       DBUS_TYPE_INVALID);
> -               else
> -                       g_dbus_emit_signal(device->conn, device->path,
> -                                       AUDIO_GATEWAY_INTERFACE,
> -                                       "VoiceRecognitionInactive",
> -                                       DBUS_TYPE_INVALID);
> -       } else if (sscanf(buf, "\r\n+VGS:%d\r\n",&value) == 1) {
> -               gw->sp_gain = value;
> -               emit_property_changed(device->conn, device->path,
> -                               AUDIO_GATEWAY_INTERFACE, "SpeakerGain",
> -                               DBUS_TYPE_UINT16,&value);
> -       } else if (sscanf(buf, "\r\n+VGM:%d\r\n",&value) == 1) {
> -               gw->mic_gain = value;
> -               emit_property_changed(device->conn, device->path,
> -                               AUDIO_GATEWAY_INTERFACE, "MicrophoneGain",
> -                               DBUS_TYPE_UINT16,&value);
> -       } else
> -               error("rfcomm_ag_data_cb(): read wrong data '%s'", buf);
> -
> -       return TRUE;
> -}
> -
>   static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
>                          struct audio_device *dev)
>   {
> @@ -541,7 +118,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
>   {
>          struct audio_device *dev = user_data;
>          struct gateway *gw = dev->gateway;
> -       DBusMessage *conn_mes = gw->connect_message;
>          gchar gw_addr[18];
>          GIOFlags flags;
>
> @@ -563,29 +139,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
>          if (!gw->rfcomm)
>                  gw->rfcomm = g_io_channel_ref(chan);
>
> -       if (establish_service_level_conn(dev->gateway)) {
> -               gboolean value = TRUE;
> -
> -               debug("%s: Connected to %s", dev->path, gw_addr);
> -               rfcomm_start_watch(dev);
> -               if (conn_mes) {
> -                       DBusMessage *reply =
> -                               dbus_message_new_method_return(conn_mes);
> -                       dbus_connection_send(dev->conn, reply, NULL);
> -                       dbus_message_unref(reply);
> -                       dbus_message_unref(conn_mes);
> -                       gw->connect_message = NULL;
> -               }
> -
> -               gw->state = GATEWAY_STATE_CONNECTED;
> -               emit_property_changed(dev->conn, dev->path,
> -                               AUDIO_GATEWAY_INTERFACE,
> -                               "Connected", DBUS_TYPE_BOOLEAN,&value);
> -               return;
> -       } else
> -               error("%s: Failed to establish service layer connection to %s",
> -                       dev->path, gw_addr);
> -
>          if (NULL != gw->sco_start_cb)
>                  gw->sco_start_cb(NULL, gw->sco_start_cb_data);
>
> @@ -732,321 +285,20 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
>          return reply;
>   }
>
> -static DBusMessage *process_ag_reponse(DBusMessage *msg, gchar *response)
> -{
> -       DBusMessage *reply;
> -
> -
> -       debug("in process_ag_reponse, response is %s", response);
> -       if (strstr(response, OK_RESPONSE))
> -               reply = dbus_message_new_method_return(msg);
> -       else {
> -               /* FIXME: some code should be here to processes errors
> -                *  in better fasion */
> -               debug("AG responded with '%s' to %s method call", response,
> -                               dbus_message_get_member(msg));
> -               reply = dbus_message_new_error(msg, ERROR_INTERFACE
> -                                       ".OperationFailed",
> -                                       "Operation failed.See log for details");
> -       }
> -       return reply;
> -}
> -
> -static DBusMessage *process_simple(DBusMessage *msg, struct audio_device *dev,
> -                                       gchar *data)
> -{
> -       struct gateway *gw = dev->gateway;
> -       gchar buf[RFCOMM_BUF_SIZE];
> -
> -       rfcomm_stop_watch(dev);
> -       rfcomm_send_and_read(gw, data, buf, strlen(data));
> -       rfcomm_start_watch(dev);
> -       return process_ag_reponse(msg, buf);
> -}
> -
> -#define AG_ANSWER "ATA\r"
> -
> -static DBusMessage *ag_answer(DBusConnection *conn, DBusMessage *msg,
> -                               void *data)
> -{
> -       struct audio_device *dev = data;
> -       struct gateway *gw = dev->gateway;
> -
> -       if (!gw->rfcomm)
> -               return g_dbus_create_error(msg, ERROR_INTERFACE
> -                                       ".NotConnected",
> -                                       "Not Connected");
> -
> -       if (gw->call_active)
> -               return g_dbus_create_error(msg, ERROR_INTERFACE
> -                                       ".CallAlreadyAnswered",
> -                                       "Call AlreadyAnswered");
> -
> -       return process_simple(msg, dev, AG_ANSWER);
> -}
> -
> -#define AG_HANGUP "AT+CHUP\r"
> -
> -static DBusMessage *ag_terminate_call(DBusConnection *conn, DBusMessage *msg,
> -                               void *data)
> -{
> -       struct audio_device *dev = data;
> -       struct gateway *gw = dev->gateway;
> -
> -       if (!gw->rfcomm)
> -               return g_dbus_create_error(msg, ERROR_INTERFACE
> -                                       ".NotConnected",
> -                                       "Not Connected");
> -
> -       return process_simple(msg, dev, AG_HANGUP);
> -}
> -
> -/* according to GSM spec */
> -#define ALLOWED_NUMBER_SYMBOLS "1234567890*#ABCD"
> -#define AG_PLACE_CALL "ATD%s;\r"
> -/* dialing from memory is not supported as headset spec doesn't define a way
> - * to retreive phone memory entries.
> - */
> -static DBusMessage *ag_call(DBusConnection *conn, DBusMessage *msg,
> -                               void *data)
> -{
> -       struct audio_device *device = data;
> -       struct gateway *gw = device->gateway;
> -       gchar buf[RFCOMM_BUF_SIZE];
> -       gchar *number;
> -       gint atd_len;
> -       DBusMessage *result;
> -
> -       debug("at the begin of ag_call()");
> -       if (!gw->rfcomm)
> -               return g_dbus_create_error(msg, ERROR_INTERFACE
> -                                       ".NotConnected",
> -                                       "Not Connected");
> -
> -       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING,&number,
> -                               DBUS_TYPE_INVALID);
> -       if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
> -               return dbus_message_new_error(msg,
> -                       ERROR_INTERFACE ".BadNumber",
> -                       "Number contains characters which are not allowed");
> -
> -       atd_len = sprintf(buf, AG_PLACE_CALL, number);
> -       rfcomm_stop_watch(device);
> -       rfcomm_send_and_read(gw, buf, buf, atd_len);
> -       rfcomm_start_watch(device);
> -
> -       result = process_ag_reponse(msg, buf);
> -       return result;
> -}
> -
> -#define AG_GET_CARRIER "AT+COPS?\r"
> -
> -static DBusMessage *ag_get_operator(DBusConnection *conn, DBusMessage *msg,
> -                                       void *data)
> -{
> -       struct audio_device *dev = (struct audio_device *) data;
> -       struct gateway *gw = dev->gateway;
> -       gchar buf[RFCOMM_BUF_SIZE];
> -       GIOChannel *rfcomm = gw->rfcomm;
> -       gsize read;
> -       gchar *result, *sep;
> -       DBusMessage *reply;
> -       GIOStatus status;
> -
> -       if (!gw->rfcomm)
> -               return g_dbus_create_error(msg, ERROR_INTERFACE
> -                                       ".NotConnected",
> -                                       "Not Connected");
> -
> -       rfcomm_stop_watch(dev);
> -       io_channel_write_all(rfcomm, AG_GET_CARRIER, strlen(AG_GET_CARRIER));
> -
> -       status = g_io_channel_read_chars(rfcomm, buf, RFCOMM_BUF_SIZE - 1,
> -&read, NULL);
> -       rfcomm_start_watch(dev);
> -       if (G_IO_STATUS_NORMAL == status) {
> -               buf[read] = '\0';
> -               if (strstr(buf, "+COPS")) {
> -                       if (!strrchr(buf, ','))
> -                               result = "0";
> -                       else {
> -                               result = strchr(buf, '\"') + 1;
> -                               sep = strchr(result, '\"');
> -                               sep[0] = '\0';
> -                       }
> -
> -                       reply = dbus_message_new_method_return(msg);
> -                       dbus_message_append_args(reply, DBUS_TYPE_STRING,
> -&result, DBUS_TYPE_INVALID);
> -               } else {
> -                       info("ag_get_operator(): '+COPS' expected but"
> -                               " '%s' received", buf);
> -                       reply = dbus_message_new_error(msg, ERROR_INTERFACE
> -                                               ".Failed",
> -                                               "Unexpected response from AG");
> -               }
> -       } else {
> -               error("ag_get_operator(): %m");
> -               reply = dbus_message_new_error(msg, ERROR_INTERFACE
> -                                       ".ConnectionFailed",
> -                                       "Failed to receive response from AG");
> -       }
> -
> -       return reply;
> -}
> -
> -#define AG_SEND_DTMF "AT+VTS=%c\r"
> -static DBusMessage *ag_send_dtmf(DBusConnection *conn, DBusMessage *msg,
> -                               void *data)
> -{
> -       struct audio_device *device = data;
> -       struct gateway *gw = device->gateway;
> -       gchar buf[RFCOMM_BUF_SIZE];
> -       gchar *number;
> -       gint com_len;
> -       gboolean got_ok = TRUE;
> -       gint num_len;
> -       gint i = 0;
> -
> -       if (!gw->rfcomm)
> -               return g_dbus_create_error(msg, ERROR_INTERFACE
> -                                       ".NotConnected",
> -                                       "Not Connected");
> -
> -       dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING,&number,
> -                               DBUS_TYPE_INVALID);
> -       if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
> -               return dbus_message_new_error(msg,
> -                       ERROR_INTERFACE ".BadNumber",
> -                       "Number contains characters which are not allowed");
> -
> -       num_len = strlen(number);
> -       rfcomm_stop_watch(device);
> -       while (i<  num_len&&  got_ok) {
> -               com_len = sprintf(buf, AG_SEND_DTMF, number[i]);
> -               rfcomm_send_and_read(gw, buf, buf, com_len);
> -               got_ok = NULL != strstr(buf, OK_RESPONSE);
> -               i += 1;
> -       }
> -       rfcomm_start_watch(device);
> -       return process_ag_reponse(msg, buf);
> -}
> -
> -#define AG_GET_SUBSCRIBER_NUMS "AT+CNUM\r"
> -#define CNUM_LEN 5             /* length of "+CNUM" string */
> -#define MAX_NUMBER_CNT 16
> -static DBusMessage *ag_get_subscriber_num(DBusConnection *conn,
> -                                       DBusMessage *msg, void *data)
> -{
> -       struct audio_device *device = data;
> -       struct gateway *gw = device->gateway;
> -       gchar buf[RFCOMM_BUF_SIZE];
> -       gchar *number, *end;
> -       DBusMessage *reply = dbus_message_new_method_return(msg);
> -
> -       if (!gw->rfcomm)
> -               return g_dbus_create_error(msg, ERROR_INTERFACE
> -                                       ".NotConnected",
> -                                       "Not Connected");
> -
> -       rfcomm_stop_watch(device);
> -       rfcomm_send_and_read(gw, AG_GET_SUBSCRIBER_NUMS, buf,
> -                       strlen(AG_GET_SUBSCRIBER_NUMS));
> -       rfcomm_start_watch(device);
> -
> -       if (strlen(buf)>  AG_CALLER_NUM_SIZE + 21)
> -               error("ag_get_subscriber_num(): buf is too long '%s'", buf);
> -       else if (strstr(buf, "+CNUM")) {
> -               number = strchr(buf, ',');
> -               number++;
> -               end = strchr(number, ',');
> -               if (end) {
> -                       *end = '\0';
> -                       dbus_message_append_args(reply, DBUS_TYPE_STRING,
> -&number, DBUS_TYPE_INVALID);
> -               }
> -       } else
> -               error("ag_get_subscriber_num(): read wrong data '%s'", buf);
> -
> -       return reply;
> -}
> -
>   static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
>                                          void *data)
>   {
> -       struct audio_device *device = data;
> -       struct gateway *gw = device->gateway;
> -       DBusMessage *reply;
> -       DBusMessageIter iter;
> -       DBusMessageIter dict;
> -       gboolean value;
> -       guint index = 0;
> -       struct indicator *ind;
> -
> -       reply = dbus_message_new_method_return(msg);
> -       if (!reply)
> -               return NULL;
> -
> -       dbus_message_iter_init_append(reply,&iter);
> -
> -       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
> -                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
> -                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
> -                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING,&dict);
> -
> -       /* Connected */
> -       value = gateway_is_connected(device);
> -       dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN,&value);
> -
> -       if (!value)
> -               goto done;
> -
> -       while ((ind = g_slist_nth_data(gw->indies, index))) {
> -               if(!strcmp(ind->descr, "\"service\""))
> -                       dict_append_entry(&dict, "RegistrationStatus",
> -                                       DBUS_TYPE_UINT16,&ind->value);
> -               else if (!strcmp(ind->descr, "\"signal\""))
> -                       dict_append_entry(&dict, "SignalStrength",
> -                                       DBUS_TYPE_UINT16,&ind->value);
> -               else if (!strcmp(ind->descr, "\"roam\""))
> -                       dict_append_entry(&dict, "RoamingStatus",
> -                                       DBUS_TYPE_UINT16,&ind->value);
> -               else if (!strcmp(ind->descr, "\"battchg\""))
> -                       dict_append_entry(&dict, "BatteryCharge",
> -                                       DBUS_TYPE_UINT16,&ind->value);
> -               index++;
> -       }
> -
> -       /* SpeakerGain */
> -       dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16,
> -&device->gateway->sp_gain);
> -
> -       /* MicrophoneGain */
> -       dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16,
> -&device->gateway->mic_gain);
> -done:
> -       dbus_message_iter_close_container(&iter,&dict);
> -       return reply;
> +       return NULL;
>   }
>
>   static GDBusMethodTable gateway_methods[] = {
>          { "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
>          { "Disconnect", "", "", ag_disconnect },
> -       { "AnswerCall", "", "", ag_answer },
> -       { "TerminateCall", "", "", ag_terminate_call },
> -       { "Call", "s", "", ag_call },
> -       { "GetOperatorName", "", "s", ag_get_operator },
> -       { "SendDTMF", "s", "", ag_send_dtmf },
> -       { "GetSubscriberNumber", "", "s", ag_get_subscriber_num },
>          { "GetProperties", "", "a{sv}", ag_get_properties },
>          { NULL, NULL, NULL, NULL }
>   };
>
>   static GDBusSignalTable gateway_signals[] = {
> -       { "Ring", "s" },
> -       { "CallTerminated", "" },
> -       { "CallStarted", "" },
> -       { "CallEnded", "" },
>          { "PropertyChanged", "sv" },
>          { NULL, NULL }
>   };
> @@ -1063,9 +315,6 @@ struct gateway *gateway_init(struct audio_device *dev)
>
>          debug("in gateway_init, dev is %p", dev);
>          gw = g_new0(struct gateway, 1);
> -       gw->indies = NULL;
> -       gw->is_dialing = FALSE;
> -       gw->call_active = FALSE;
>          gw->state = GATEWAY_STATE_DISCONNECTED;
>          return gw;
>
> @@ -1107,11 +356,6 @@ void gateway_start_service(struct audio_device *device)
>          rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
>   }
>
> -static void indicator_slice_free(gpointer mem)
> -{
> -       g_slice_free(struct indicator, mem);
> -}
> -
>   int gateway_close(struct audio_device *device)
>   {
>          struct gateway *gw = device->gateway;
> @@ -1119,8 +363,6 @@ int gateway_close(struct audio_device *device)
>          GIOChannel *sco = gw->sco;
>          gboolean value = FALSE;
>
> -       g_slist_foreach(gw->indies, (GFunc) indicator_slice_free, NULL);
> -       g_slist_free(gw->indies);
>          if (rfcomm) {
>                  g_io_channel_shutdown(rfcomm, TRUE, NULL);
>                  g_io_channel_unref(rfcomm);
> --
> 1.6.4.4
>
> _______________________________________________
> ofono mailing list
> ofono(a)ofono.org
> http://lists.ofono.org/listinfo/ofono
>
>    


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

* Re: [RFC] HFP support into oFono and BlueZ
  2010-01-21  7:54         ` Zhao Forrest
@ 2012-08-21 22:46           ` Lucas De Marchi
  -1 siblings, 0 replies; 51+ messages in thread
From: Lucas De Marchi @ 2012-08-21 22:46 UTC (permalink / raw)
  To: ofono; +Cc: Gustavo F. Padovan, linux-bluetooth, zhenhua.zhang

Hi Zhao,

On Thu, Jan 21, 2010 at 5:54 AM, Zhao Forrest <forrest.zhao@gmail.com> wrote:
> Your patch removes many DBus APIs provided by audio/gateway.c, which
> was originally designed to provide a set of DBus functions/signals for
> ease of application development. In other words, your patch is too
> oFono-specific, and is not a general support for HFP HF unit role in
> BlueZ IMO. The DBus interface provided by your patch is not easy to
> develop the application because the application developer has to parse
> the AT commands by himself. I know that oFono could parse AT commands.
> But I don't think it a common case for other application developers.
> Why not extend audio/gateway.c instead of removing its DBus APIs or
> create a new file to support HFP for oFono in blueZ?
>
> I think maybe Marcel would have more comments on this to give a clear direction.

First of all, please don't top-post to this list.

Applications developers don't have to parse the AT commands by
themselves but rather use a telephony stack that implements the DBus
APIs.

This is kind of an agent (as in the pairing process)  that anyone
could implement. oFono implements it, so it's 1 option.


Lucas De Marchi

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

* Re: [RFC] HFP support into oFono and BlueZ
@ 2012-08-21 22:46           ` Lucas De Marchi
  0 siblings, 0 replies; 51+ messages in thread
From: Lucas De Marchi @ 2012-08-21 22:46 UTC (permalink / raw)
  To: ofono

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

Hi Zhao,

On Thu, Jan 21, 2010 at 5:54 AM, Zhao Forrest <forrest.zhao@gmail.com> wrote:
> Your patch removes many DBus APIs provided by audio/gateway.c, which
> was originally designed to provide a set of DBus functions/signals for
> ease of application development. In other words, your patch is too
> oFono-specific, and is not a general support for HFP HF unit role in
> BlueZ IMO. The DBus interface provided by your patch is not easy to
> develop the application because the application developer has to parse
> the AT commands by himself. I know that oFono could parse AT commands.
> But I don't think it a common case for other application developers.
> Why not extend audio/gateway.c instead of removing its DBus APIs or
> create a new file to support HFP for oFono in blueZ?
>
> I think maybe Marcel would have more comments on this to give a clear direction.

First of all, please don't top-post to this list.

Applications developers don't have to parse the AT commands by
themselves but rather use a telephony stack that implements the DBus
APIs.

This is kind of an agent (as in the pairing process)  that anyone
could implement. oFono implements it, so it's 1 option.


Lucas De Marchi

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

* [PATCH 1/2] clean up audio/gateway.c
@ 2010-02-02 21:41 ` Gustavo F. Padovan
  0 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-02-02 21:41 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: ofono

remove all code related to the AT engine
---
 audio/gateway.c |  760 +------------------------------------------------------
 1 files changed, 1 insertions(+), 759 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index a1c1ea2..3dc09ff 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -53,52 +53,6 @@
 #include "dbus-common.h"
 
 #define RFCOMM_BUF_SIZE 256
-/* not-more-then-16 defined by GSM + 1 for NULL + padding */
-#define AG_INDICATOR_DESCR_SIZE 20
-#define AG_CALLER_NUM_SIZE 64	/* size of number + type */
-
-/* commands */
-#define AG_FEATURES "AT+BRSF=26\r"     /* = 0x7F = All features supported */
-#define AG_INDICATORS_SUPP "AT+CIND=?\r"
-#define AG_INDICATORS_VAL "AT+CIND?\r"
-#define AG_INDICATORS_ENABLE "AT+CMER=3,0,0,1\r"
-#define AG_HOLD_MPTY_SUPP "AT+CHLD=?\r"
-#define AG_CALLER_IDENT_ENABLE "AT+CLIP=1\r"
-#define AG_CARRIER_FORMAT "AT+COPS=3,0\r"
-#define AG_EXTENDED_RESULT_CODE "AT+CMEE=1\r"
-
-#define AG_FEATURE_3WAY 0x1
-#define AG_FEATURE_EXTENDED_RES_CODE 0x100
-/* Hold and multipary AG features.
- * Comments below are copied from hands-free spec for reference */
-/* Releases all held calls or sets User Determined User Busy (UDUB)
- * for a waiting call */
-#define AG_CHLD_0 0x01
-/* Releases all active calls (if any exist) and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_1 0x02
-/* Releases specified active call only <x> */
-#define AG_CHLD_1x 0x04
-/* Places all active calls (if any exist) on hold and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_2 0x08
-/* Request private consultation mode with specified call <x> (Place all
- * calls on hold EXCEPT the call <x>) */
-#define AG_CHLD_2x 0x10
-/* Adds a held call to the conversation */
-#define AG_CHLD_3 0x20
-/* Connects the two calls and disconnects the subscriber from both calls
- * (Explicit Call Transfer). Support for this value and its associated
- * functionality is optional for the HF. */
-#define AG_CHLD_4 0x40
-
-#define OK_RESPONSE "\r\nOK\r\n"
-#define ERROR_RESPONSE "\r\nERROR\r\n"
-
-struct indicator {
-	gchar descr[AG_INDICATOR_DESCR_SIZE];
-	gint value;
-};
 
 struct gateway {
 	gateway_state_t state;
@@ -108,387 +62,10 @@ struct gateway {
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
 	DBusMessage *connect_message;
-	guint ag_features;
-	guint hold_multiparty_features;
-	GSList *indies;
-	gboolean is_dialing;
-	gboolean call_active;
-
-	int sp_gain;
-	int mic_gain;
 };
 
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device);
-
 int gateway_close(struct audio_device *device);
 
-static void rfcomm_start_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	gw->rfcomm_watch_id = g_io_add_watch(gw->rfcomm,
-			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-			(GIOFunc) rfcomm_ag_data_cb, dev);
-}
-
-static void rfcomm_stop_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	g_source_remove(gw->rfcomm_watch_id);
-}
-
-static gboolean io_channel_write_all(GIOChannel *io, gchar *data,
-					gsize count)
-{
-	gsize written = 0;
-	GIOStatus status;
-
-	while (count > 0) {
-		status = g_io_channel_write_chars(io, data, count, &written,
-						NULL);
-		if (status != G_IO_STATUS_NORMAL)
-			return FALSE;
-
-		data += written;
-		count -= written;
-	}
-	return TRUE;
-}
-
-/* it's worth to mention that data and response could be the same pointers */
-static gboolean rfcomm_send_and_read(struct gateway *gw, gchar *data,
-                                    gchar *response, gsize count)
-{
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read = 0;
-	gboolean got_ok = FALSE;
-	gboolean got_error = FALSE;
-	gchar *resp_buf = response;
-	gsize toread = RFCOMM_BUF_SIZE - 1;
-	GIOStatus status;
-
-	if (!io_channel_write_all(rfcomm, data, count))
-		return FALSE;
-
-	while (!(got_ok || got_error)) {
-		status = g_io_channel_read_chars(rfcomm, resp_buf, toread,
-						&read, NULL);
-		if (status == G_IO_STATUS_NORMAL)
-			resp_buf[read] = '\0';
-		else {
-			debug("rfcomm_send_and_read(): %m");
-			return FALSE;
-		}
-		got_ok = NULL != strstr(resp_buf, OK_RESPONSE);
-		got_error = NULL != strstr(resp_buf, ERROR_RESPONSE);
-		resp_buf += read;
-		toread -= read;
-	}
-	return TRUE;
-}
-
-/* get <descr> from the names: (<descr>, (<values>)), (<descr>, (<values>))
- * ... */
-static GSList *parse_indicator_names(gchar *names, GSList *indies)
-{
-	gchar *current = names - 1;
-	GSList *result = indies;
-	gchar *next;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 2;
-		next = strstr(current, ",(");
-		ind = g_slice_new(struct indicator);
-		strncpy(ind->descr, current, 20);
-		ind->descr[(intptr_t) next - (intptr_t) current] = '\0';
-		result = g_slist_append(result, (gpointer) ind);
-		current = strstr(next + 1, ",(");
-	}
-	return result;
-}
-
-/* get values from <val0>,<val1>,... */
-static GSList *parse_indicator_values(gchar *values, GSList *indies)
-{
-	gint val;
-	gchar *current = values - 1;
-	GSList *runner = indies;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 1;
-		sscanf(current, "%d", &val);
-		current = strchr(current, ',');
-		ind = g_slist_nth_data(runner, 0);
-		ind->value = val;
-		runner = g_slist_next(runner);
-	}
-	return indies;
-}
-
-/* get values from <val0>,<val1>,... */
-static guint get_hold_mpty_features(gchar *features)
-{
-	guint result = 0;
-
-	if (strstr(features, "0"))
-		result |= AG_CHLD_0;
-
-	if (strstr(features, "1"))
-		result |= AG_CHLD_1;
-
-	if (strstr(features, "1x"))
-		result |= AG_CHLD_1x;
-
-	if (strstr(features, "2"))
-		result |= AG_CHLD_2;
-
-	if (strstr(features, "2x"))
-		result |= AG_CHLD_2x;
-
-	if (strstr(features, "3"))
-		result |= AG_CHLD_3;
-
-	if (strstr(features, "4"))
-		result |= AG_CHLD_4;
-
-	return result;
-}
-
-static gboolean establish_service_level_conn(struct gateway *gw)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	gboolean res;
-
-	debug("at the begin of establish_service_level_conn()");
-	res = rfcomm_send_and_read(gw, AG_FEATURES, buf,
-				sizeof(AG_FEATURES) - 1);
-	if (!res || sscanf(buf, "\r\n+BRSF:%d", &gw->ag_features) != 1)
-		return FALSE;
-
-	debug("features are 0x%X", gw->ag_features);
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_SUPP, buf,
-				sizeof(AG_INDICATORS_SUPP) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_names(strchr(buf, '('), NULL);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_VAL, buf,
-		sizeof(AG_INDICATORS_VAL) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_values(strchr(buf, ':') + 1, gw->indies);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_ENABLE, buf,
-				sizeof(AG_INDICATORS_ENABLE) - 1);
-	if (!res || !strstr(buf, "OK"))
-		return FALSE;
-
-	if ((gw->ag_features & AG_FEATURE_3WAY) != 0) {
-		res = rfcomm_send_and_read(gw, AG_HOLD_MPTY_SUPP, buf,
-				sizeof(AG_HOLD_MPTY_SUPP) - 1);
-		if (!res || !strstr(buf, "+CHLD:")) {
-			g_slice_free1(RFCOMM_BUF_SIZE, buf);
-			return FALSE;
-		}
-		gw->hold_multiparty_features = get_hold_mpty_features(
-							strchr(buf, '('));
-
-	} else
-		gw->hold_multiparty_features = 0;
-
-	debug("Service layer connection successfully established!");
-	rfcomm_send_and_read(gw, AG_CALLER_IDENT_ENABLE, buf,
-			sizeof(AG_CALLER_IDENT_ENABLE) - 1);
-	rfcomm_send_and_read(gw, AG_CARRIER_FORMAT, buf,
-			sizeof(AG_CARRIER_FORMAT) - 1);
-	if ((gw->ag_features & AG_FEATURE_EXTENDED_RES_CODE) != 0)
-		rfcomm_send_and_read(gw, AG_EXTENDED_RESULT_CODE, buf,
-			sizeof(AG_EXTENDED_RESULT_CODE) - 1);
-
-	return TRUE;
-}
-
-static void process_ind_change(struct audio_device *dev, guint index,
-							gint value)
-{
-	struct gateway *gw = dev->gateway;
-	struct indicator *ind = g_slist_nth_data(gw->indies, index - 1);
-	gchar *name = ind->descr;
-
-	ind->value = value;
-
-	debug("at the begin of process_ind_change, name is %s", name);
-	if (!strcmp(name, "\"call\"")) {
-		if (value > 0) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallStarted", DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-			gw->call_active = TRUE;
-		} else {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallEnded", DBUS_TYPE_INVALID);
-			gw->call_active = FALSE;
-		}
-
-	} else if (!strcmp(name, "\"callsetup\"")) {
-		if (value == 0 && gw->is_dialing) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-		} else if (!gw->is_dialing && value > 0)
-			gw->is_dialing = TRUE;
-
-	} else if (!strcmp(name, "\"callheld\"")) {
-		/* FIXME: The following code is based on assumptions only.
-		 * Has to be tested for interoperability
-		 * I assume that callheld=2 would be sent when dial from HF
-		 * failed in case of 3-way call
-		 * Unfortunately this path is not covered by the HF spec so
-		 * the code has to be tested for interop
-		*/
-		/* '2' means: all calls held, no active calls */
-		if (value == 2) {
-			if (gw->is_dialing) {
-				g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-				gw->is_dialing = FALSE;
-			}
-		}
-	} else if (!strcmp(name, "\"service\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RegistrationStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"signal\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "SignalStrength",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"roam\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RoamingStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"battchg\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "BatteryCharge",
-				DBUS_TYPE_UINT16, &value);
-}
-
-static void process_ring(struct audio_device *device, GIOChannel *chan,
-			gchar *buf)
-{
-	gchar number[AG_CALLER_NUM_SIZE];
-	gchar *cli;
-	gchar *sep;
-	gsize read;
-	GIOStatus status;
-
-	rfcomm_stop_watch(device);
-	status = g_io_channel_read_chars(chan, buf, RFCOMM_BUF_SIZE - 1, &read, NULL);
-	if (status != G_IO_STATUS_NORMAL)
-		return;
-
-	debug("at the begin of process_ring");
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 10)
-		error("process_ring(): buf is too long '%s'", buf);
-	else if ((cli = strstr(buf, "\r\n+CLIP"))) {
-		if (sscanf(cli, "\r\n+CLIP: \"%s", number) == 1) {
-			sep = strchr(number, '"');
-			sep[0] = '\0';
-
-			/* FIXME:signal will be emitted on each RING+CLIP.
-			 * That's bad */
-			cli = number;
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE, "Ring",
-					DBUS_TYPE_STRING, &cli,
-					DBUS_TYPE_INVALID);
-			device->gateway->is_dialing = TRUE;
-		} else
-			error("process_ring(): '%s' in place of +CLIP after RING", buf);
-
-	}
-
-	rfcomm_start_watch(device);
-}
-
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	struct gateway *gw;
-	gsize read;
-	/* some space for value */
-	gchar indicator[AG_INDICATOR_DESCR_SIZE + 4];
-	gint value;
-	guint index;
-	gchar *sep;
-
-	debug("at the begin of rfcomm_ag_data_cb()");
-	if (cond & G_IO_NVAL)
-		return FALSE;
-
-	gw = device->gateway;
-
-	if (cond & (G_IO_ERR | G_IO_HUP)) {
-		debug("connection with remote BT is closed");
-		gateway_close(device);
-		return FALSE;
-	}
-
-	if (g_io_channel_read_chars(chan, buf, sizeof(buf) - 1, &read, NULL)
-			!= G_IO_STATUS_NORMAL)
-		return TRUE;
-	buf[read] = '\0';
-
-	if (strlen(buf) > AG_INDICATOR_DESCR_SIZE + 14)
-		error("rfcomm_ag_data_cb(): buf is too long '%s'", buf);
-	else if (sscanf(buf, "\r\n+CIEV:%s\r\n", indicator) == 1) {
-		sep = strchr(indicator, ',');
-		sep[0] = '\0';
-		sep += 1;
-		index = atoi(indicator);
-		value = atoi(sep);
-		process_ind_change(device, index, value);
-	} else if (strstr(buf, "RING"))
-		process_ring(device, chan, buf);
-	else if (sscanf(buf, "\r\n+BVRA:%d\r\n", &value) == 1) {
-		if (value == 0)
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionActive",
-					DBUS_TYPE_INVALID);
-		else
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionInactive",
-					DBUS_TYPE_INVALID);
-	} else if (sscanf(buf, "\r\n+VGS:%d\r\n", &value) == 1) {
-		gw->sp_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "SpeakerGain",
-				DBUS_TYPE_UINT16, &value);
-	} else if (sscanf(buf, "\r\n+VGM:%d\r\n", &value) == 1) {
-		gw->mic_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "MicrophoneGain",
-				DBUS_TYPE_UINT16, &value);
-	} else
-		error("rfcomm_ag_data_cb(): read wrong data '%s'", buf);
-
-	return TRUE;
-}
-
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
 {
@@ -541,7 +118,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	DBusMessage *conn_mes = gw->connect_message;
 	gchar gw_addr[18];
 	GIOFlags flags;
 
@@ -563,29 +139,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 	if (!gw->rfcomm)
 		gw->rfcomm = g_io_channel_ref(chan);
 
-	if (establish_service_level_conn(dev->gateway)) {
-		gboolean value = TRUE;
-
-		debug("%s: Connected to %s", dev->path, gw_addr);
-		rfcomm_start_watch(dev);
-		if (conn_mes) {
-			DBusMessage *reply =
-				dbus_message_new_method_return(conn_mes);
-			dbus_connection_send(dev->conn, reply, NULL);
-			dbus_message_unref(reply);
-			dbus_message_unref(conn_mes);
-			gw->connect_message = NULL;
-		}
-
-		gw->state = GATEWAY_STATE_CONNECTED;
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN,	&value);
-		return;
-	} else
-		error("%s: Failed to establish service layer connection to %s",
-			dev->path, gw_addr);
-
 	if (NULL != gw->sco_start_cb)
 		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 
@@ -732,321 +285,20 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
-static DBusMessage *process_ag_reponse(DBusMessage *msg, gchar *response)
-{
-	DBusMessage *reply;
-
-
-	debug("in process_ag_reponse, response is %s", response);
-	if (strstr(response, OK_RESPONSE))
-		reply = dbus_message_new_method_return(msg);
-	else {
-		/* FIXME: some code should be here to processes errors
-		 *  in better fasion */
-		debug("AG responded with '%s' to %s method call", response,
-				dbus_message_get_member(msg));
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".OperationFailed",
-					"Operation failed.See log for details");
-	}
-	return reply;
-}
-
-static DBusMessage *process_simple(DBusMessage *msg, struct audio_device *dev,
-					gchar *data)
-{
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-
-	rfcomm_stop_watch(dev);
-	rfcomm_send_and_read(gw, data, buf, strlen(data));
-	rfcomm_start_watch(dev);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_ANSWER "ATA\r"
-
-static DBusMessage *ag_answer(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	if (gw->call_active)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".CallAlreadyAnswered",
-					"Call AlreadyAnswered");
-
-	return process_simple(msg, dev, AG_ANSWER);
-}
-
-#define AG_HANGUP "AT+CHUP\r"
-
-static DBusMessage *ag_terminate_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	return process_simple(msg, dev, AG_HANGUP);
-}
-
-/* according to GSM spec */
-#define ALLOWED_NUMBER_SYMBOLS "1234567890*#ABCD"
-#define AG_PLACE_CALL "ATD%s;\r"
-/* dialing from memory is not supported as headset spec doesn't define a way
- * to retreive phone memory entries.
- */
-static DBusMessage *ag_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint atd_len;
-	DBusMessage *result;
-
-	debug("at the begin of ag_call()");
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	atd_len = sprintf(buf, AG_PLACE_CALL, number);
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, buf, buf, atd_len);
-	rfcomm_start_watch(device);
-
-	result = process_ag_reponse(msg, buf);
-	return result;
-}
-
-#define AG_GET_CARRIER "AT+COPS?\r"
-
-static DBusMessage *ag_get_operator(DBusConnection *conn, DBusMessage *msg,
-					void *data)
-{
-	struct audio_device *dev = (struct audio_device *) data;
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read;
-	gchar *result, *sep;
-	DBusMessage *reply;
-	GIOStatus status;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(dev);
-	io_channel_write_all(rfcomm, AG_GET_CARRIER, strlen(AG_GET_CARRIER));
-
-	status = g_io_channel_read_chars(rfcomm, buf, RFCOMM_BUF_SIZE - 1,
-						&read, NULL);
-	rfcomm_start_watch(dev);
-	if (G_IO_STATUS_NORMAL == status) {
-		buf[read] = '\0';
-		if (strstr(buf, "+COPS")) {
-			if (!strrchr(buf, ','))
-				result = "0";
-			else {
-				result = strchr(buf, '\"') + 1;
-				sep = strchr(result, '\"');
-				sep[0] = '\0';
-			}
-
-			reply = dbus_message_new_method_return(msg);
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&result, DBUS_TYPE_INVALID);
-		} else {
-			info("ag_get_operator(): '+COPS' expected but"
-				" '%s' received", buf);
-			reply = dbus_message_new_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Unexpected response from AG");
-		}
-	} else {
-		error("ag_get_operator(): %m");
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".ConnectionFailed",
-					"Failed to receive response from AG");
-	}
-
-	return reply;
-}
-
-#define AG_SEND_DTMF "AT+VTS=%c\r"
-static DBusMessage *ag_send_dtmf(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint com_len;
-	gboolean got_ok = TRUE;
-	gint num_len;
-	gint i = 0;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	num_len = strlen(number);
-	rfcomm_stop_watch(device);
-	while (i < num_len && got_ok) {
-		com_len = sprintf(buf, AG_SEND_DTMF, number[i]);
-		rfcomm_send_and_read(gw, buf, buf, com_len);
-		got_ok = NULL != strstr(buf, OK_RESPONSE);
-		i += 1;
-	}
-	rfcomm_start_watch(device);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_GET_SUBSCRIBER_NUMS "AT+CNUM\r"
-#define CNUM_LEN 5             /* length of "+CNUM" string */
-#define MAX_NUMBER_CNT 16
-static DBusMessage *ag_get_subscriber_num(DBusConnection *conn,
-					DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number, *end;
-	DBusMessage *reply = dbus_message_new_method_return(msg);
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, AG_GET_SUBSCRIBER_NUMS, buf,
-			strlen(AG_GET_SUBSCRIBER_NUMS));
-	rfcomm_start_watch(device);
-
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 21)
-		error("ag_get_subscriber_num(): buf is too long '%s'", buf);
-	else if (strstr(buf, "+CNUM")) {
-		number = strchr(buf, ',');
-		number++;
-		end = strchr(number, ',');
-		if (end) {
-			*end = '\0';
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&number, DBUS_TYPE_INVALID);
-		}
-	} else
-		error("ag_get_subscriber_num(): read wrong data '%s'", buf);
-
-	return reply;
-}
-
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	DBusMessage *reply;
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	gboolean value;
-	guint index = 0;
-	struct indicator *ind;
-
-	reply = dbus_message_new_method_return(msg);
-	if (!reply)
-		return NULL;
-
-	dbus_message_iter_init_append(reply, &iter);
-
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
-	/* Connected */
-	value = gateway_is_connected(device);
-	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
-
-	if (!value)
-		goto done;
-
-	while ((ind = g_slist_nth_data(gw->indies, index))) {
-		if(!strcmp(ind->descr, "\"service\""))
-			dict_append_entry(&dict, "RegistrationStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"signal\""))
-			dict_append_entry(&dict, "SignalStrength",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"roam\""))
-			dict_append_entry(&dict, "RoamingStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"battchg\""))
-			dict_append_entry(&dict, "BatteryCharge",
-					DBUS_TYPE_UINT16, &ind->value);
-		index++;
-	}
-
-	/* SpeakerGain */
-	dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16,
-				&device->gateway->sp_gain);
-
-	/* MicrophoneGain */
-	dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16,
-				&device->gateway->mic_gain);
-done:
-	dbus_message_iter_close_container(&iter, &dict);
-	return reply;
+	return NULL;
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "Disconnect", "", "", ag_disconnect },
-	{ "AnswerCall", "", "", ag_answer },
-	{ "TerminateCall", "", "", ag_terminate_call },
-	{ "Call", "s", "", ag_call },
-	{ "GetOperatorName", "", "s", ag_get_operator },
-	{ "SendDTMF", "s", "", ag_send_dtmf },
-	{ "GetSubscriberNumber", "", "s", ag_get_subscriber_num },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
 	{ NULL, NULL, NULL, NULL }
 };
 
 static GDBusSignalTable gateway_signals[] = {
-	{ "Ring", "s" },
-	{ "CallTerminated", "" },
-	{ "CallStarted", "" },
-	{ "CallEnded", "" },
 	{ "PropertyChanged", "sv" },
 	{ NULL, NULL }
 };
@@ -1063,9 +315,6 @@ struct gateway *gateway_init(struct audio_device *dev)
 
 	debug("in gateway_init, dev is %p", dev);
 	gw = g_new0(struct gateway, 1);
-	gw->indies = NULL;
-	gw->is_dialing = FALSE;
-	gw->call_active = FALSE;
 	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
 
@@ -1107,11 +356,6 @@ void gateway_start_service(struct audio_device *device)
 	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
 }
 
-static void indicator_slice_free(gpointer mem)
-{
-	g_slice_free(struct indicator, mem);
-}
-
 int gateway_close(struct audio_device *device)
 {
 	struct gateway *gw = device->gateway;
@@ -1119,8 +363,6 @@ int gateway_close(struct audio_device *device)
 	GIOChannel *sco = gw->sco;
 	gboolean value = FALSE;
 
-	g_slist_foreach(gw->indies, (GFunc) indicator_slice_free, NULL);
-	g_slist_free(gw->indies);
 	if (rfcomm) {
 		g_io_channel_shutdown(rfcomm, TRUE, NULL);
 		g_io_channel_unref(rfcomm);
-- 
1.6.4.4


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

* [PATCH 1/2] clean up audio/gateway.c
@ 2010-02-02 21:41 ` Gustavo F. Padovan
  0 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-02-02 21:41 UTC (permalink / raw)
  To: ofono

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

remove all code related to the AT engine
---
 audio/gateway.c |  760 +------------------------------------------------------
 1 files changed, 1 insertions(+), 759 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index a1c1ea2..3dc09ff 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -53,52 +53,6 @@
 #include "dbus-common.h"
 
 #define RFCOMM_BUF_SIZE 256
-/* not-more-then-16 defined by GSM + 1 for NULL + padding */
-#define AG_INDICATOR_DESCR_SIZE 20
-#define AG_CALLER_NUM_SIZE 64	/* size of number + type */
-
-/* commands */
-#define AG_FEATURES "AT+BRSF=26\r"     /* = 0x7F = All features supported */
-#define AG_INDICATORS_SUPP "AT+CIND=?\r"
-#define AG_INDICATORS_VAL "AT+CIND?\r"
-#define AG_INDICATORS_ENABLE "AT+CMER=3,0,0,1\r"
-#define AG_HOLD_MPTY_SUPP "AT+CHLD=?\r"
-#define AG_CALLER_IDENT_ENABLE "AT+CLIP=1\r"
-#define AG_CARRIER_FORMAT "AT+COPS=3,0\r"
-#define AG_EXTENDED_RESULT_CODE "AT+CMEE=1\r"
-
-#define AG_FEATURE_3WAY 0x1
-#define AG_FEATURE_EXTENDED_RES_CODE 0x100
-/* Hold and multipary AG features.
- * Comments below are copied from hands-free spec for reference */
-/* Releases all held calls or sets User Determined User Busy (UDUB)
- * for a waiting call */
-#define AG_CHLD_0 0x01
-/* Releases all active calls (if any exist) and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_1 0x02
-/* Releases specified active call only <x> */
-#define AG_CHLD_1x 0x04
-/* Places all active calls (if any exist) on hold and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_2 0x08
-/* Request private consultation mode with specified call <x> (Place all
- * calls on hold EXCEPT the call <x>) */
-#define AG_CHLD_2x 0x10
-/* Adds a held call to the conversation */
-#define AG_CHLD_3 0x20
-/* Connects the two calls and disconnects the subscriber from both calls
- * (Explicit Call Transfer). Support for this value and its associated
- * functionality is optional for the HF. */
-#define AG_CHLD_4 0x40
-
-#define OK_RESPONSE "\r\nOK\r\n"
-#define ERROR_RESPONSE "\r\nERROR\r\n"
-
-struct indicator {
-	gchar descr[AG_INDICATOR_DESCR_SIZE];
-	gint value;
-};
 
 struct gateway {
 	gateway_state_t state;
@@ -108,387 +62,10 @@ struct gateway {
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
 	DBusMessage *connect_message;
-	guint ag_features;
-	guint hold_multiparty_features;
-	GSList *indies;
-	gboolean is_dialing;
-	gboolean call_active;
-
-	int sp_gain;
-	int mic_gain;
 };
 
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device);
-
 int gateway_close(struct audio_device *device);
 
-static void rfcomm_start_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	gw->rfcomm_watch_id = g_io_add_watch(gw->rfcomm,
-			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-			(GIOFunc) rfcomm_ag_data_cb, dev);
-}
-
-static void rfcomm_stop_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	g_source_remove(gw->rfcomm_watch_id);
-}
-
-static gboolean io_channel_write_all(GIOChannel *io, gchar *data,
-					gsize count)
-{
-	gsize written = 0;
-	GIOStatus status;
-
-	while (count > 0) {
-		status = g_io_channel_write_chars(io, data, count, &written,
-						NULL);
-		if (status != G_IO_STATUS_NORMAL)
-			return FALSE;
-
-		data += written;
-		count -= written;
-	}
-	return TRUE;
-}
-
-/* it's worth to mention that data and response could be the same pointers */
-static gboolean rfcomm_send_and_read(struct gateway *gw, gchar *data,
-                                    gchar *response, gsize count)
-{
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read = 0;
-	gboolean got_ok = FALSE;
-	gboolean got_error = FALSE;
-	gchar *resp_buf = response;
-	gsize toread = RFCOMM_BUF_SIZE - 1;
-	GIOStatus status;
-
-	if (!io_channel_write_all(rfcomm, data, count))
-		return FALSE;
-
-	while (!(got_ok || got_error)) {
-		status = g_io_channel_read_chars(rfcomm, resp_buf, toread,
-						&read, NULL);
-		if (status == G_IO_STATUS_NORMAL)
-			resp_buf[read] = '\0';
-		else {
-			debug("rfcomm_send_and_read(): %m");
-			return FALSE;
-		}
-		got_ok = NULL != strstr(resp_buf, OK_RESPONSE);
-		got_error = NULL != strstr(resp_buf, ERROR_RESPONSE);
-		resp_buf += read;
-		toread -= read;
-	}
-	return TRUE;
-}
-
-/* get <descr> from the names: (<descr>, (<values>)), (<descr>, (<values>))
- * ... */
-static GSList *parse_indicator_names(gchar *names, GSList *indies)
-{
-	gchar *current = names - 1;
-	GSList *result = indies;
-	gchar *next;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 2;
-		next = strstr(current, ",(");
-		ind = g_slice_new(struct indicator);
-		strncpy(ind->descr, current, 20);
-		ind->descr[(intptr_t) next - (intptr_t) current] = '\0';
-		result = g_slist_append(result, (gpointer) ind);
-		current = strstr(next + 1, ",(");
-	}
-	return result;
-}
-
-/* get values from <val0>,<val1>,... */
-static GSList *parse_indicator_values(gchar *values, GSList *indies)
-{
-	gint val;
-	gchar *current = values - 1;
-	GSList *runner = indies;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 1;
-		sscanf(current, "%d", &val);
-		current = strchr(current, ',');
-		ind = g_slist_nth_data(runner, 0);
-		ind->value = val;
-		runner = g_slist_next(runner);
-	}
-	return indies;
-}
-
-/* get values from <val0>,<val1>,... */
-static guint get_hold_mpty_features(gchar *features)
-{
-	guint result = 0;
-
-	if (strstr(features, "0"))
-		result |= AG_CHLD_0;
-
-	if (strstr(features, "1"))
-		result |= AG_CHLD_1;
-
-	if (strstr(features, "1x"))
-		result |= AG_CHLD_1x;
-
-	if (strstr(features, "2"))
-		result |= AG_CHLD_2;
-
-	if (strstr(features, "2x"))
-		result |= AG_CHLD_2x;
-
-	if (strstr(features, "3"))
-		result |= AG_CHLD_3;
-
-	if (strstr(features, "4"))
-		result |= AG_CHLD_4;
-
-	return result;
-}
-
-static gboolean establish_service_level_conn(struct gateway *gw)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	gboolean res;
-
-	debug("at the begin of establish_service_level_conn()");
-	res = rfcomm_send_and_read(gw, AG_FEATURES, buf,
-				sizeof(AG_FEATURES) - 1);
-	if (!res || sscanf(buf, "\r\n+BRSF:%d", &gw->ag_features) != 1)
-		return FALSE;
-
-	debug("features are 0x%X", gw->ag_features);
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_SUPP, buf,
-				sizeof(AG_INDICATORS_SUPP) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_names(strchr(buf, '('), NULL);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_VAL, buf,
-		sizeof(AG_INDICATORS_VAL) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_values(strchr(buf, ':') + 1, gw->indies);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_ENABLE, buf,
-				sizeof(AG_INDICATORS_ENABLE) - 1);
-	if (!res || !strstr(buf, "OK"))
-		return FALSE;
-
-	if ((gw->ag_features & AG_FEATURE_3WAY) != 0) {
-		res = rfcomm_send_and_read(gw, AG_HOLD_MPTY_SUPP, buf,
-				sizeof(AG_HOLD_MPTY_SUPP) - 1);
-		if (!res || !strstr(buf, "+CHLD:")) {
-			g_slice_free1(RFCOMM_BUF_SIZE, buf);
-			return FALSE;
-		}
-		gw->hold_multiparty_features = get_hold_mpty_features(
-							strchr(buf, '('));
-
-	} else
-		gw->hold_multiparty_features = 0;
-
-	debug("Service layer connection successfully established!");
-	rfcomm_send_and_read(gw, AG_CALLER_IDENT_ENABLE, buf,
-			sizeof(AG_CALLER_IDENT_ENABLE) - 1);
-	rfcomm_send_and_read(gw, AG_CARRIER_FORMAT, buf,
-			sizeof(AG_CARRIER_FORMAT) - 1);
-	if ((gw->ag_features & AG_FEATURE_EXTENDED_RES_CODE) != 0)
-		rfcomm_send_and_read(gw, AG_EXTENDED_RESULT_CODE, buf,
-			sizeof(AG_EXTENDED_RESULT_CODE) - 1);
-
-	return TRUE;
-}
-
-static void process_ind_change(struct audio_device *dev, guint index,
-							gint value)
-{
-	struct gateway *gw = dev->gateway;
-	struct indicator *ind = g_slist_nth_data(gw->indies, index - 1);
-	gchar *name = ind->descr;
-
-	ind->value = value;
-
-	debug("at the begin of process_ind_change, name is %s", name);
-	if (!strcmp(name, "\"call\"")) {
-		if (value > 0) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallStarted", DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-			gw->call_active = TRUE;
-		} else {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallEnded", DBUS_TYPE_INVALID);
-			gw->call_active = FALSE;
-		}
-
-	} else if (!strcmp(name, "\"callsetup\"")) {
-		if (value == 0 && gw->is_dialing) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-		} else if (!gw->is_dialing && value > 0)
-			gw->is_dialing = TRUE;
-
-	} else if (!strcmp(name, "\"callheld\"")) {
-		/* FIXME: The following code is based on assumptions only.
-		 * Has to be tested for interoperability
-		 * I assume that callheld=2 would be sent when dial from HF
-		 * failed in case of 3-way call
-		 * Unfortunately this path is not covered by the HF spec so
-		 * the code has to be tested for interop
-		*/
-		/* '2' means: all calls held, no active calls */
-		if (value == 2) {
-			if (gw->is_dialing) {
-				g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-				gw->is_dialing = FALSE;
-			}
-		}
-	} else if (!strcmp(name, "\"service\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RegistrationStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"signal\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "SignalStrength",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"roam\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RoamingStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"battchg\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "BatteryCharge",
-				DBUS_TYPE_UINT16, &value);
-}
-
-static void process_ring(struct audio_device *device, GIOChannel *chan,
-			gchar *buf)
-{
-	gchar number[AG_CALLER_NUM_SIZE];
-	gchar *cli;
-	gchar *sep;
-	gsize read;
-	GIOStatus status;
-
-	rfcomm_stop_watch(device);
-	status = g_io_channel_read_chars(chan, buf, RFCOMM_BUF_SIZE - 1, &read, NULL);
-	if (status != G_IO_STATUS_NORMAL)
-		return;
-
-	debug("at the begin of process_ring");
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 10)
-		error("process_ring(): buf is too long '%s'", buf);
-	else if ((cli = strstr(buf, "\r\n+CLIP"))) {
-		if (sscanf(cli, "\r\n+CLIP: \"%s", number) == 1) {
-			sep = strchr(number, '"');
-			sep[0] = '\0';
-
-			/* FIXME:signal will be emitted on each RING+CLIP.
-			 * That's bad */
-			cli = number;
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE, "Ring",
-					DBUS_TYPE_STRING, &cli,
-					DBUS_TYPE_INVALID);
-			device->gateway->is_dialing = TRUE;
-		} else
-			error("process_ring(): '%s' in place of +CLIP after RING", buf);
-
-	}
-
-	rfcomm_start_watch(device);
-}
-
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	struct gateway *gw;
-	gsize read;
-	/* some space for value */
-	gchar indicator[AG_INDICATOR_DESCR_SIZE + 4];
-	gint value;
-	guint index;
-	gchar *sep;
-
-	debug("at the begin of rfcomm_ag_data_cb()");
-	if (cond & G_IO_NVAL)
-		return FALSE;
-
-	gw = device->gateway;
-
-	if (cond & (G_IO_ERR | G_IO_HUP)) {
-		debug("connection with remote BT is closed");
-		gateway_close(device);
-		return FALSE;
-	}
-
-	if (g_io_channel_read_chars(chan, buf, sizeof(buf) - 1, &read, NULL)
-			!= G_IO_STATUS_NORMAL)
-		return TRUE;
-	buf[read] = '\0';
-
-	if (strlen(buf) > AG_INDICATOR_DESCR_SIZE + 14)
-		error("rfcomm_ag_data_cb(): buf is too long '%s'", buf);
-	else if (sscanf(buf, "\r\n+CIEV:%s\r\n", indicator) == 1) {
-		sep = strchr(indicator, ',');
-		sep[0] = '\0';
-		sep += 1;
-		index = atoi(indicator);
-		value = atoi(sep);
-		process_ind_change(device, index, value);
-	} else if (strstr(buf, "RING"))
-		process_ring(device, chan, buf);
-	else if (sscanf(buf, "\r\n+BVRA:%d\r\n", &value) == 1) {
-		if (value == 0)
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionActive",
-					DBUS_TYPE_INVALID);
-		else
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionInactive",
-					DBUS_TYPE_INVALID);
-	} else if (sscanf(buf, "\r\n+VGS:%d\r\n", &value) == 1) {
-		gw->sp_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "SpeakerGain",
-				DBUS_TYPE_UINT16, &value);
-	} else if (sscanf(buf, "\r\n+VGM:%d\r\n", &value) == 1) {
-		gw->mic_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "MicrophoneGain",
-				DBUS_TYPE_UINT16, &value);
-	} else
-		error("rfcomm_ag_data_cb(): read wrong data '%s'", buf);
-
-	return TRUE;
-}
-
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
 {
@@ -541,7 +118,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	DBusMessage *conn_mes = gw->connect_message;
 	gchar gw_addr[18];
 	GIOFlags flags;
 
@@ -563,29 +139,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 	if (!gw->rfcomm)
 		gw->rfcomm = g_io_channel_ref(chan);
 
-	if (establish_service_level_conn(dev->gateway)) {
-		gboolean value = TRUE;
-
-		debug("%s: Connected to %s", dev->path, gw_addr);
-		rfcomm_start_watch(dev);
-		if (conn_mes) {
-			DBusMessage *reply =
-				dbus_message_new_method_return(conn_mes);
-			dbus_connection_send(dev->conn, reply, NULL);
-			dbus_message_unref(reply);
-			dbus_message_unref(conn_mes);
-			gw->connect_message = NULL;
-		}
-
-		gw->state = GATEWAY_STATE_CONNECTED;
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN,	&value);
-		return;
-	} else
-		error("%s: Failed to establish service layer connection to %s",
-			dev->path, gw_addr);
-
 	if (NULL != gw->sco_start_cb)
 		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 
@@ -732,321 +285,20 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
-static DBusMessage *process_ag_reponse(DBusMessage *msg, gchar *response)
-{
-	DBusMessage *reply;
-
-
-	debug("in process_ag_reponse, response is %s", response);
-	if (strstr(response, OK_RESPONSE))
-		reply = dbus_message_new_method_return(msg);
-	else {
-		/* FIXME: some code should be here to processes errors
-		 *  in better fasion */
-		debug("AG responded with '%s' to %s method call", response,
-				dbus_message_get_member(msg));
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".OperationFailed",
-					"Operation failed.See log for details");
-	}
-	return reply;
-}
-
-static DBusMessage *process_simple(DBusMessage *msg, struct audio_device *dev,
-					gchar *data)
-{
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-
-	rfcomm_stop_watch(dev);
-	rfcomm_send_and_read(gw, data, buf, strlen(data));
-	rfcomm_start_watch(dev);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_ANSWER "ATA\r"
-
-static DBusMessage *ag_answer(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	if (gw->call_active)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".CallAlreadyAnswered",
-					"Call AlreadyAnswered");
-
-	return process_simple(msg, dev, AG_ANSWER);
-}
-
-#define AG_HANGUP "AT+CHUP\r"
-
-static DBusMessage *ag_terminate_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	return process_simple(msg, dev, AG_HANGUP);
-}
-
-/* according to GSM spec */
-#define ALLOWED_NUMBER_SYMBOLS "1234567890*#ABCD"
-#define AG_PLACE_CALL "ATD%s;\r"
-/* dialing from memory is not supported as headset spec doesn't define a way
- * to retreive phone memory entries.
- */
-static DBusMessage *ag_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint atd_len;
-	DBusMessage *result;
-
-	debug("at the begin of ag_call()");
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	atd_len = sprintf(buf, AG_PLACE_CALL, number);
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, buf, buf, atd_len);
-	rfcomm_start_watch(device);
-
-	result = process_ag_reponse(msg, buf);
-	return result;
-}
-
-#define AG_GET_CARRIER "AT+COPS?\r"
-
-static DBusMessage *ag_get_operator(DBusConnection *conn, DBusMessage *msg,
-					void *data)
-{
-	struct audio_device *dev = (struct audio_device *) data;
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read;
-	gchar *result, *sep;
-	DBusMessage *reply;
-	GIOStatus status;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(dev);
-	io_channel_write_all(rfcomm, AG_GET_CARRIER, strlen(AG_GET_CARRIER));
-
-	status = g_io_channel_read_chars(rfcomm, buf, RFCOMM_BUF_SIZE - 1,
-						&read, NULL);
-	rfcomm_start_watch(dev);
-	if (G_IO_STATUS_NORMAL == status) {
-		buf[read] = '\0';
-		if (strstr(buf, "+COPS")) {
-			if (!strrchr(buf, ','))
-				result = "0";
-			else {
-				result = strchr(buf, '\"') + 1;
-				sep = strchr(result, '\"');
-				sep[0] = '\0';
-			}
-
-			reply = dbus_message_new_method_return(msg);
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&result, DBUS_TYPE_INVALID);
-		} else {
-			info("ag_get_operator(): '+COPS' expected but"
-				" '%s' received", buf);
-			reply = dbus_message_new_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Unexpected response from AG");
-		}
-	} else {
-		error("ag_get_operator(): %m");
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".ConnectionFailed",
-					"Failed to receive response from AG");
-	}
-
-	return reply;
-}
-
-#define AG_SEND_DTMF "AT+VTS=%c\r"
-static DBusMessage *ag_send_dtmf(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint com_len;
-	gboolean got_ok = TRUE;
-	gint num_len;
-	gint i = 0;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	num_len = strlen(number);
-	rfcomm_stop_watch(device);
-	while (i < num_len && got_ok) {
-		com_len = sprintf(buf, AG_SEND_DTMF, number[i]);
-		rfcomm_send_and_read(gw, buf, buf, com_len);
-		got_ok = NULL != strstr(buf, OK_RESPONSE);
-		i += 1;
-	}
-	rfcomm_start_watch(device);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_GET_SUBSCRIBER_NUMS "AT+CNUM\r"
-#define CNUM_LEN 5             /* length of "+CNUM" string */
-#define MAX_NUMBER_CNT 16
-static DBusMessage *ag_get_subscriber_num(DBusConnection *conn,
-					DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number, *end;
-	DBusMessage *reply = dbus_message_new_method_return(msg);
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, AG_GET_SUBSCRIBER_NUMS, buf,
-			strlen(AG_GET_SUBSCRIBER_NUMS));
-	rfcomm_start_watch(device);
-
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 21)
-		error("ag_get_subscriber_num(): buf is too long '%s'", buf);
-	else if (strstr(buf, "+CNUM")) {
-		number = strchr(buf, ',');
-		number++;
-		end = strchr(number, ',');
-		if (end) {
-			*end = '\0';
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&number, DBUS_TYPE_INVALID);
-		}
-	} else
-		error("ag_get_subscriber_num(): read wrong data '%s'", buf);
-
-	return reply;
-}
-
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	DBusMessage *reply;
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	gboolean value;
-	guint index = 0;
-	struct indicator *ind;
-
-	reply = dbus_message_new_method_return(msg);
-	if (!reply)
-		return NULL;
-
-	dbus_message_iter_init_append(reply, &iter);
-
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
-	/* Connected */
-	value = gateway_is_connected(device);
-	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
-
-	if (!value)
-		goto done;
-
-	while ((ind = g_slist_nth_data(gw->indies, index))) {
-		if(!strcmp(ind->descr, "\"service\""))
-			dict_append_entry(&dict, "RegistrationStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"signal\""))
-			dict_append_entry(&dict, "SignalStrength",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"roam\""))
-			dict_append_entry(&dict, "RoamingStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"battchg\""))
-			dict_append_entry(&dict, "BatteryCharge",
-					DBUS_TYPE_UINT16, &ind->value);
-		index++;
-	}
-
-	/* SpeakerGain */
-	dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16,
-				&device->gateway->sp_gain);
-
-	/* MicrophoneGain */
-	dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16,
-				&device->gateway->mic_gain);
-done:
-	dbus_message_iter_close_container(&iter, &dict);
-	return reply;
+	return NULL;
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "Disconnect", "", "", ag_disconnect },
-	{ "AnswerCall", "", "", ag_answer },
-	{ "TerminateCall", "", "", ag_terminate_call },
-	{ "Call", "s", "", ag_call },
-	{ "GetOperatorName", "", "s", ag_get_operator },
-	{ "SendDTMF", "s", "", ag_send_dtmf },
-	{ "GetSubscriberNumber", "", "s", ag_get_subscriber_num },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
 	{ NULL, NULL, NULL, NULL }
 };
 
 static GDBusSignalTable gateway_signals[] = {
-	{ "Ring", "s" },
-	{ "CallTerminated", "" },
-	{ "CallStarted", "" },
-	{ "CallEnded", "" },
 	{ "PropertyChanged", "sv" },
 	{ NULL, NULL }
 };
@@ -1063,9 +315,6 @@ struct gateway *gateway_init(struct audio_device *dev)
 
 	debug("in gateway_init, dev is %p", dev);
 	gw = g_new0(struct gateway, 1);
-	gw->indies = NULL;
-	gw->is_dialing = FALSE;
-	gw->call_active = FALSE;
 	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
 
@@ -1107,11 +356,6 @@ void gateway_start_service(struct audio_device *device)
 	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
 }
 
-static void indicator_slice_free(gpointer mem)
-{
-	g_slice_free(struct indicator, mem);
-}
-
 int gateway_close(struct audio_device *device)
 {
 	struct gateway *gw = device->gateway;
@@ -1119,8 +363,6 @@ int gateway_close(struct audio_device *device)
 	GIOChannel *sco = gw->sco;
 	gboolean value = FALSE;
 
-	g_slist_foreach(gw->indies, (GFunc) indicator_slice_free, NULL);
-	g_slist_free(gw->indies);
 	if (rfcomm) {
 		g_io_channel_shutdown(rfcomm, TRUE, NULL);
 		g_io_channel_unref(rfcomm);
-- 
1.6.4.4


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

* [PATCH 1/2] clean up audio/gateway.c
@ 2010-01-21 20:31 ` Gustavo F. Padovan
  0 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-21 20:31 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: ofono, johan.hedberg

remove all code related to the AT engine
---
 audio/gateway.c |  760 +------------------------------------------------------
 1 files changed, 1 insertions(+), 759 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index a1c1ea2..3dc09ff 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -53,52 +53,6 @@
 #include "dbus-common.h"
 
 #define RFCOMM_BUF_SIZE 256
-/* not-more-then-16 defined by GSM + 1 for NULL + padding */
-#define AG_INDICATOR_DESCR_SIZE 20
-#define AG_CALLER_NUM_SIZE 64	/* size of number + type */
-
-/* commands */
-#define AG_FEATURES "AT+BRSF=26\r"     /* = 0x7F = All features supported */
-#define AG_INDICATORS_SUPP "AT+CIND=?\r"
-#define AG_INDICATORS_VAL "AT+CIND?\r"
-#define AG_INDICATORS_ENABLE "AT+CMER=3,0,0,1\r"
-#define AG_HOLD_MPTY_SUPP "AT+CHLD=?\r"
-#define AG_CALLER_IDENT_ENABLE "AT+CLIP=1\r"
-#define AG_CARRIER_FORMAT "AT+COPS=3,0\r"
-#define AG_EXTENDED_RESULT_CODE "AT+CMEE=1\r"
-
-#define AG_FEATURE_3WAY 0x1
-#define AG_FEATURE_EXTENDED_RES_CODE 0x100
-/* Hold and multipary AG features.
- * Comments below are copied from hands-free spec for reference */
-/* Releases all held calls or sets User Determined User Busy (UDUB)
- * for a waiting call */
-#define AG_CHLD_0 0x01
-/* Releases all active calls (if any exist) and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_1 0x02
-/* Releases specified active call only <x> */
-#define AG_CHLD_1x 0x04
-/* Places all active calls (if any exist) on hold and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_2 0x08
-/* Request private consultation mode with specified call <x> (Place all
- * calls on hold EXCEPT the call <x>) */
-#define AG_CHLD_2x 0x10
-/* Adds a held call to the conversation */
-#define AG_CHLD_3 0x20
-/* Connects the two calls and disconnects the subscriber from both calls
- * (Explicit Call Transfer). Support for this value and its associated
- * functionality is optional for the HF. */
-#define AG_CHLD_4 0x40
-
-#define OK_RESPONSE "\r\nOK\r\n"
-#define ERROR_RESPONSE "\r\nERROR\r\n"
-
-struct indicator {
-	gchar descr[AG_INDICATOR_DESCR_SIZE];
-	gint value;
-};
 
 struct gateway {
 	gateway_state_t state;
@@ -108,387 +62,10 @@ struct gateway {
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
 	DBusMessage *connect_message;
-	guint ag_features;
-	guint hold_multiparty_features;
-	GSList *indies;
-	gboolean is_dialing;
-	gboolean call_active;
-
-	int sp_gain;
-	int mic_gain;
 };
 
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device);
-
 int gateway_close(struct audio_device *device);
 
-static void rfcomm_start_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	gw->rfcomm_watch_id = g_io_add_watch(gw->rfcomm,
-			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-			(GIOFunc) rfcomm_ag_data_cb, dev);
-}
-
-static void rfcomm_stop_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	g_source_remove(gw->rfcomm_watch_id);
-}
-
-static gboolean io_channel_write_all(GIOChannel *io, gchar *data,
-					gsize count)
-{
-	gsize written = 0;
-	GIOStatus status;
-
-	while (count > 0) {
-		status = g_io_channel_write_chars(io, data, count, &written,
-						NULL);
-		if (status != G_IO_STATUS_NORMAL)
-			return FALSE;
-
-		data += written;
-		count -= written;
-	}
-	return TRUE;
-}
-
-/* it's worth to mention that data and response could be the same pointers */
-static gboolean rfcomm_send_and_read(struct gateway *gw, gchar *data,
-                                    gchar *response, gsize count)
-{
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read = 0;
-	gboolean got_ok = FALSE;
-	gboolean got_error = FALSE;
-	gchar *resp_buf = response;
-	gsize toread = RFCOMM_BUF_SIZE - 1;
-	GIOStatus status;
-
-	if (!io_channel_write_all(rfcomm, data, count))
-		return FALSE;
-
-	while (!(got_ok || got_error)) {
-		status = g_io_channel_read_chars(rfcomm, resp_buf, toread,
-						&read, NULL);
-		if (status == G_IO_STATUS_NORMAL)
-			resp_buf[read] = '\0';
-		else {
-			debug("rfcomm_send_and_read(): %m");
-			return FALSE;
-		}
-		got_ok = NULL != strstr(resp_buf, OK_RESPONSE);
-		got_error = NULL != strstr(resp_buf, ERROR_RESPONSE);
-		resp_buf += read;
-		toread -= read;
-	}
-	return TRUE;
-}
-
-/* get <descr> from the names: (<descr>, (<values>)), (<descr>, (<values>))
- * ... */
-static GSList *parse_indicator_names(gchar *names, GSList *indies)
-{
-	gchar *current = names - 1;
-	GSList *result = indies;
-	gchar *next;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 2;
-		next = strstr(current, ",(");
-		ind = g_slice_new(struct indicator);
-		strncpy(ind->descr, current, 20);
-		ind->descr[(intptr_t) next - (intptr_t) current] = '\0';
-		result = g_slist_append(result, (gpointer) ind);
-		current = strstr(next + 1, ",(");
-	}
-	return result;
-}
-
-/* get values from <val0>,<val1>,... */
-static GSList *parse_indicator_values(gchar *values, GSList *indies)
-{
-	gint val;
-	gchar *current = values - 1;
-	GSList *runner = indies;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 1;
-		sscanf(current, "%d", &val);
-		current = strchr(current, ',');
-		ind = g_slist_nth_data(runner, 0);
-		ind->value = val;
-		runner = g_slist_next(runner);
-	}
-	return indies;
-}
-
-/* get values from <val0>,<val1>,... */
-static guint get_hold_mpty_features(gchar *features)
-{
-	guint result = 0;
-
-	if (strstr(features, "0"))
-		result |= AG_CHLD_0;
-
-	if (strstr(features, "1"))
-		result |= AG_CHLD_1;
-
-	if (strstr(features, "1x"))
-		result |= AG_CHLD_1x;
-
-	if (strstr(features, "2"))
-		result |= AG_CHLD_2;
-
-	if (strstr(features, "2x"))
-		result |= AG_CHLD_2x;
-
-	if (strstr(features, "3"))
-		result |= AG_CHLD_3;
-
-	if (strstr(features, "4"))
-		result |= AG_CHLD_4;
-
-	return result;
-}
-
-static gboolean establish_service_level_conn(struct gateway *gw)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	gboolean res;
-
-	debug("at the begin of establish_service_level_conn()");
-	res = rfcomm_send_and_read(gw, AG_FEATURES, buf,
-				sizeof(AG_FEATURES) - 1);
-	if (!res || sscanf(buf, "\r\n+BRSF:%d", &gw->ag_features) != 1)
-		return FALSE;
-
-	debug("features are 0x%X", gw->ag_features);
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_SUPP, buf,
-				sizeof(AG_INDICATORS_SUPP) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_names(strchr(buf, '('), NULL);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_VAL, buf,
-		sizeof(AG_INDICATORS_VAL) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_values(strchr(buf, ':') + 1, gw->indies);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_ENABLE, buf,
-				sizeof(AG_INDICATORS_ENABLE) - 1);
-	if (!res || !strstr(buf, "OK"))
-		return FALSE;
-
-	if ((gw->ag_features & AG_FEATURE_3WAY) != 0) {
-		res = rfcomm_send_and_read(gw, AG_HOLD_MPTY_SUPP, buf,
-				sizeof(AG_HOLD_MPTY_SUPP) - 1);
-		if (!res || !strstr(buf, "+CHLD:")) {
-			g_slice_free1(RFCOMM_BUF_SIZE, buf);
-			return FALSE;
-		}
-		gw->hold_multiparty_features = get_hold_mpty_features(
-							strchr(buf, '('));
-
-	} else
-		gw->hold_multiparty_features = 0;
-
-	debug("Service layer connection successfully established!");
-	rfcomm_send_and_read(gw, AG_CALLER_IDENT_ENABLE, buf,
-			sizeof(AG_CALLER_IDENT_ENABLE) - 1);
-	rfcomm_send_and_read(gw, AG_CARRIER_FORMAT, buf,
-			sizeof(AG_CARRIER_FORMAT) - 1);
-	if ((gw->ag_features & AG_FEATURE_EXTENDED_RES_CODE) != 0)
-		rfcomm_send_and_read(gw, AG_EXTENDED_RESULT_CODE, buf,
-			sizeof(AG_EXTENDED_RESULT_CODE) - 1);
-
-	return TRUE;
-}
-
-static void process_ind_change(struct audio_device *dev, guint index,
-							gint value)
-{
-	struct gateway *gw = dev->gateway;
-	struct indicator *ind = g_slist_nth_data(gw->indies, index - 1);
-	gchar *name = ind->descr;
-
-	ind->value = value;
-
-	debug("at the begin of process_ind_change, name is %s", name);
-	if (!strcmp(name, "\"call\"")) {
-		if (value > 0) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallStarted", DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-			gw->call_active = TRUE;
-		} else {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallEnded", DBUS_TYPE_INVALID);
-			gw->call_active = FALSE;
-		}
-
-	} else if (!strcmp(name, "\"callsetup\"")) {
-		if (value == 0 && gw->is_dialing) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-		} else if (!gw->is_dialing && value > 0)
-			gw->is_dialing = TRUE;
-
-	} else if (!strcmp(name, "\"callheld\"")) {
-		/* FIXME: The following code is based on assumptions only.
-		 * Has to be tested for interoperability
-		 * I assume that callheld=2 would be sent when dial from HF
-		 * failed in case of 3-way call
-		 * Unfortunately this path is not covered by the HF spec so
-		 * the code has to be tested for interop
-		*/
-		/* '2' means: all calls held, no active calls */
-		if (value == 2) {
-			if (gw->is_dialing) {
-				g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-				gw->is_dialing = FALSE;
-			}
-		}
-	} else if (!strcmp(name, "\"service\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RegistrationStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"signal\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "SignalStrength",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"roam\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RoamingStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"battchg\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "BatteryCharge",
-				DBUS_TYPE_UINT16, &value);
-}
-
-static void process_ring(struct audio_device *device, GIOChannel *chan,
-			gchar *buf)
-{
-	gchar number[AG_CALLER_NUM_SIZE];
-	gchar *cli;
-	gchar *sep;
-	gsize read;
-	GIOStatus status;
-
-	rfcomm_stop_watch(device);
-	status = g_io_channel_read_chars(chan, buf, RFCOMM_BUF_SIZE - 1, &read, NULL);
-	if (status != G_IO_STATUS_NORMAL)
-		return;
-
-	debug("at the begin of process_ring");
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 10)
-		error("process_ring(): buf is too long '%s'", buf);
-	else if ((cli = strstr(buf, "\r\n+CLIP"))) {
-		if (sscanf(cli, "\r\n+CLIP: \"%s", number) == 1) {
-			sep = strchr(number, '"');
-			sep[0] = '\0';
-
-			/* FIXME:signal will be emitted on each RING+CLIP.
-			 * That's bad */
-			cli = number;
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE, "Ring",
-					DBUS_TYPE_STRING, &cli,
-					DBUS_TYPE_INVALID);
-			device->gateway->is_dialing = TRUE;
-		} else
-			error("process_ring(): '%s' in place of +CLIP after RING", buf);
-
-	}
-
-	rfcomm_start_watch(device);
-}
-
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	struct gateway *gw;
-	gsize read;
-	/* some space for value */
-	gchar indicator[AG_INDICATOR_DESCR_SIZE + 4];
-	gint value;
-	guint index;
-	gchar *sep;
-
-	debug("at the begin of rfcomm_ag_data_cb()");
-	if (cond & G_IO_NVAL)
-		return FALSE;
-
-	gw = device->gateway;
-
-	if (cond & (G_IO_ERR | G_IO_HUP)) {
-		debug("connection with remote BT is closed");
-		gateway_close(device);
-		return FALSE;
-	}
-
-	if (g_io_channel_read_chars(chan, buf, sizeof(buf) - 1, &read, NULL)
-			!= G_IO_STATUS_NORMAL)
-		return TRUE;
-	buf[read] = '\0';
-
-	if (strlen(buf) > AG_INDICATOR_DESCR_SIZE + 14)
-		error("rfcomm_ag_data_cb(): buf is too long '%s'", buf);
-	else if (sscanf(buf, "\r\n+CIEV:%s\r\n", indicator) == 1) {
-		sep = strchr(indicator, ',');
-		sep[0] = '\0';
-		sep += 1;
-		index = atoi(indicator);
-		value = atoi(sep);
-		process_ind_change(device, index, value);
-	} else if (strstr(buf, "RING"))
-		process_ring(device, chan, buf);
-	else if (sscanf(buf, "\r\n+BVRA:%d\r\n", &value) == 1) {
-		if (value == 0)
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionActive",
-					DBUS_TYPE_INVALID);
-		else
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionInactive",
-					DBUS_TYPE_INVALID);
-	} else if (sscanf(buf, "\r\n+VGS:%d\r\n", &value) == 1) {
-		gw->sp_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "SpeakerGain",
-				DBUS_TYPE_UINT16, &value);
-	} else if (sscanf(buf, "\r\n+VGM:%d\r\n", &value) == 1) {
-		gw->mic_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "MicrophoneGain",
-				DBUS_TYPE_UINT16, &value);
-	} else
-		error("rfcomm_ag_data_cb(): read wrong data '%s'", buf);
-
-	return TRUE;
-}
-
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
 {
@@ -541,7 +118,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	DBusMessage *conn_mes = gw->connect_message;
 	gchar gw_addr[18];
 	GIOFlags flags;
 
@@ -563,29 +139,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 	if (!gw->rfcomm)
 		gw->rfcomm = g_io_channel_ref(chan);
 
-	if (establish_service_level_conn(dev->gateway)) {
-		gboolean value = TRUE;
-
-		debug("%s: Connected to %s", dev->path, gw_addr);
-		rfcomm_start_watch(dev);
-		if (conn_mes) {
-			DBusMessage *reply =
-				dbus_message_new_method_return(conn_mes);
-			dbus_connection_send(dev->conn, reply, NULL);
-			dbus_message_unref(reply);
-			dbus_message_unref(conn_mes);
-			gw->connect_message = NULL;
-		}
-
-		gw->state = GATEWAY_STATE_CONNECTED;
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN,	&value);
-		return;
-	} else
-		error("%s: Failed to establish service layer connection to %s",
-			dev->path, gw_addr);
-
 	if (NULL != gw->sco_start_cb)
 		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 
@@ -732,321 +285,20 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
-static DBusMessage *process_ag_reponse(DBusMessage *msg, gchar *response)
-{
-	DBusMessage *reply;
-
-
-	debug("in process_ag_reponse, response is %s", response);
-	if (strstr(response, OK_RESPONSE))
-		reply = dbus_message_new_method_return(msg);
-	else {
-		/* FIXME: some code should be here to processes errors
-		 *  in better fasion */
-		debug("AG responded with '%s' to %s method call", response,
-				dbus_message_get_member(msg));
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".OperationFailed",
-					"Operation failed.See log for details");
-	}
-	return reply;
-}
-
-static DBusMessage *process_simple(DBusMessage *msg, struct audio_device *dev,
-					gchar *data)
-{
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-
-	rfcomm_stop_watch(dev);
-	rfcomm_send_and_read(gw, data, buf, strlen(data));
-	rfcomm_start_watch(dev);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_ANSWER "ATA\r"
-
-static DBusMessage *ag_answer(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	if (gw->call_active)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".CallAlreadyAnswered",
-					"Call AlreadyAnswered");
-
-	return process_simple(msg, dev, AG_ANSWER);
-}
-
-#define AG_HANGUP "AT+CHUP\r"
-
-static DBusMessage *ag_terminate_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	return process_simple(msg, dev, AG_HANGUP);
-}
-
-/* according to GSM spec */
-#define ALLOWED_NUMBER_SYMBOLS "1234567890*#ABCD"
-#define AG_PLACE_CALL "ATD%s;\r"
-/* dialing from memory is not supported as headset spec doesn't define a way
- * to retreive phone memory entries.
- */
-static DBusMessage *ag_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint atd_len;
-	DBusMessage *result;
-
-	debug("at the begin of ag_call()");
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	atd_len = sprintf(buf, AG_PLACE_CALL, number);
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, buf, buf, atd_len);
-	rfcomm_start_watch(device);
-
-	result = process_ag_reponse(msg, buf);
-	return result;
-}
-
-#define AG_GET_CARRIER "AT+COPS?\r"
-
-static DBusMessage *ag_get_operator(DBusConnection *conn, DBusMessage *msg,
-					void *data)
-{
-	struct audio_device *dev = (struct audio_device *) data;
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read;
-	gchar *result, *sep;
-	DBusMessage *reply;
-	GIOStatus status;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(dev);
-	io_channel_write_all(rfcomm, AG_GET_CARRIER, strlen(AG_GET_CARRIER));
-
-	status = g_io_channel_read_chars(rfcomm, buf, RFCOMM_BUF_SIZE - 1,
-						&read, NULL);
-	rfcomm_start_watch(dev);
-	if (G_IO_STATUS_NORMAL == status) {
-		buf[read] = '\0';
-		if (strstr(buf, "+COPS")) {
-			if (!strrchr(buf, ','))
-				result = "0";
-			else {
-				result = strchr(buf, '\"') + 1;
-				sep = strchr(result, '\"');
-				sep[0] = '\0';
-			}
-
-			reply = dbus_message_new_method_return(msg);
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&result, DBUS_TYPE_INVALID);
-		} else {
-			info("ag_get_operator(): '+COPS' expected but"
-				" '%s' received", buf);
-			reply = dbus_message_new_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Unexpected response from AG");
-		}
-	} else {
-		error("ag_get_operator(): %m");
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".ConnectionFailed",
-					"Failed to receive response from AG");
-	}
-
-	return reply;
-}
-
-#define AG_SEND_DTMF "AT+VTS=%c\r"
-static DBusMessage *ag_send_dtmf(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint com_len;
-	gboolean got_ok = TRUE;
-	gint num_len;
-	gint i = 0;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	num_len = strlen(number);
-	rfcomm_stop_watch(device);
-	while (i < num_len && got_ok) {
-		com_len = sprintf(buf, AG_SEND_DTMF, number[i]);
-		rfcomm_send_and_read(gw, buf, buf, com_len);
-		got_ok = NULL != strstr(buf, OK_RESPONSE);
-		i += 1;
-	}
-	rfcomm_start_watch(device);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_GET_SUBSCRIBER_NUMS "AT+CNUM\r"
-#define CNUM_LEN 5             /* length of "+CNUM" string */
-#define MAX_NUMBER_CNT 16
-static DBusMessage *ag_get_subscriber_num(DBusConnection *conn,
-					DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number, *end;
-	DBusMessage *reply = dbus_message_new_method_return(msg);
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, AG_GET_SUBSCRIBER_NUMS, buf,
-			strlen(AG_GET_SUBSCRIBER_NUMS));
-	rfcomm_start_watch(device);
-
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 21)
-		error("ag_get_subscriber_num(): buf is too long '%s'", buf);
-	else if (strstr(buf, "+CNUM")) {
-		number = strchr(buf, ',');
-		number++;
-		end = strchr(number, ',');
-		if (end) {
-			*end = '\0';
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&number, DBUS_TYPE_INVALID);
-		}
-	} else
-		error("ag_get_subscriber_num(): read wrong data '%s'", buf);
-
-	return reply;
-}
-
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	DBusMessage *reply;
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	gboolean value;
-	guint index = 0;
-	struct indicator *ind;
-
-	reply = dbus_message_new_method_return(msg);
-	if (!reply)
-		return NULL;
-
-	dbus_message_iter_init_append(reply, &iter);
-
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
-	/* Connected */
-	value = gateway_is_connected(device);
-	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
-
-	if (!value)
-		goto done;
-
-	while ((ind = g_slist_nth_data(gw->indies, index))) {
-		if(!strcmp(ind->descr, "\"service\""))
-			dict_append_entry(&dict, "RegistrationStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"signal\""))
-			dict_append_entry(&dict, "SignalStrength",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"roam\""))
-			dict_append_entry(&dict, "RoamingStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"battchg\""))
-			dict_append_entry(&dict, "BatteryCharge",
-					DBUS_TYPE_UINT16, &ind->value);
-		index++;
-	}
-
-	/* SpeakerGain */
-	dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16,
-				&device->gateway->sp_gain);
-
-	/* MicrophoneGain */
-	dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16,
-				&device->gateway->mic_gain);
-done:
-	dbus_message_iter_close_container(&iter, &dict);
-	return reply;
+	return NULL;
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "Disconnect", "", "", ag_disconnect },
-	{ "AnswerCall", "", "", ag_answer },
-	{ "TerminateCall", "", "", ag_terminate_call },
-	{ "Call", "s", "", ag_call },
-	{ "GetOperatorName", "", "s", ag_get_operator },
-	{ "SendDTMF", "s", "", ag_send_dtmf },
-	{ "GetSubscriberNumber", "", "s", ag_get_subscriber_num },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
 	{ NULL, NULL, NULL, NULL }
 };
 
 static GDBusSignalTable gateway_signals[] = {
-	{ "Ring", "s" },
-	{ "CallTerminated", "" },
-	{ "CallStarted", "" },
-	{ "CallEnded", "" },
 	{ "PropertyChanged", "sv" },
 	{ NULL, NULL }
 };
@@ -1063,9 +315,6 @@ struct gateway *gateway_init(struct audio_device *dev)
 
 	debug("in gateway_init, dev is %p", dev);
 	gw = g_new0(struct gateway, 1);
-	gw->indies = NULL;
-	gw->is_dialing = FALSE;
-	gw->call_active = FALSE;
 	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
 
@@ -1107,11 +356,6 @@ void gateway_start_service(struct audio_device *device)
 	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
 }
 
-static void indicator_slice_free(gpointer mem)
-{
-	g_slice_free(struct indicator, mem);
-}
-
 int gateway_close(struct audio_device *device)
 {
 	struct gateway *gw = device->gateway;
@@ -1119,8 +363,6 @@ int gateway_close(struct audio_device *device)
 	GIOChannel *sco = gw->sco;
 	gboolean value = FALSE;
 
-	g_slist_foreach(gw->indies, (GFunc) indicator_slice_free, NULL);
-	g_slist_free(gw->indies);
 	if (rfcomm) {
 		g_io_channel_shutdown(rfcomm, TRUE, NULL);
 		g_io_channel_unref(rfcomm);
-- 
1.6.4.4


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

* [PATCH 1/2] clean up audio/gateway.c
@ 2010-01-21 20:31 ` Gustavo F. Padovan
  0 siblings, 0 replies; 51+ messages in thread
From: Gustavo F. Padovan @ 2010-01-21 20:31 UTC (permalink / raw)
  To: ofono

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

remove all code related to the AT engine
---
 audio/gateway.c |  760 +------------------------------------------------------
 1 files changed, 1 insertions(+), 759 deletions(-)

diff --git a/audio/gateway.c b/audio/gateway.c
index a1c1ea2..3dc09ff 100644
--- a/audio/gateway.c
+++ b/audio/gateway.c
@@ -53,52 +53,6 @@
 #include "dbus-common.h"
 
 #define RFCOMM_BUF_SIZE 256
-/* not-more-then-16 defined by GSM + 1 for NULL + padding */
-#define AG_INDICATOR_DESCR_SIZE 20
-#define AG_CALLER_NUM_SIZE 64	/* size of number + type */
-
-/* commands */
-#define AG_FEATURES "AT+BRSF=26\r"     /* = 0x7F = All features supported */
-#define AG_INDICATORS_SUPP "AT+CIND=?\r"
-#define AG_INDICATORS_VAL "AT+CIND?\r"
-#define AG_INDICATORS_ENABLE "AT+CMER=3,0,0,1\r"
-#define AG_HOLD_MPTY_SUPP "AT+CHLD=?\r"
-#define AG_CALLER_IDENT_ENABLE "AT+CLIP=1\r"
-#define AG_CARRIER_FORMAT "AT+COPS=3,0\r"
-#define AG_EXTENDED_RESULT_CODE "AT+CMEE=1\r"
-
-#define AG_FEATURE_3WAY 0x1
-#define AG_FEATURE_EXTENDED_RES_CODE 0x100
-/* Hold and multipary AG features.
- * Comments below are copied from hands-free spec for reference */
-/* Releases all held calls or sets User Determined User Busy (UDUB)
- * for a waiting call */
-#define AG_CHLD_0 0x01
-/* Releases all active calls (if any exist) and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_1 0x02
-/* Releases specified active call only <x> */
-#define AG_CHLD_1x 0x04
-/* Places all active calls (if any exist) on hold and accepts the other
- * (held or waiting) call */
-#define AG_CHLD_2 0x08
-/* Request private consultation mode with specified call <x> (Place all
- * calls on hold EXCEPT the call <x>) */
-#define AG_CHLD_2x 0x10
-/* Adds a held call to the conversation */
-#define AG_CHLD_3 0x20
-/* Connects the two calls and disconnects the subscriber from both calls
- * (Explicit Call Transfer). Support for this value and its associated
- * functionality is optional for the HF. */
-#define AG_CHLD_4 0x40
-
-#define OK_RESPONSE "\r\nOK\r\n"
-#define ERROR_RESPONSE "\r\nERROR\r\n"
-
-struct indicator {
-	gchar descr[AG_INDICATOR_DESCR_SIZE];
-	gint value;
-};
 
 struct gateway {
 	gateway_state_t state;
@@ -108,387 +62,10 @@ struct gateway {
 	gateway_stream_cb_t sco_start_cb;
 	void *sco_start_cb_data;
 	DBusMessage *connect_message;
-	guint ag_features;
-	guint hold_multiparty_features;
-	GSList *indies;
-	gboolean is_dialing;
-	gboolean call_active;
-
-	int sp_gain;
-	int mic_gain;
 };
 
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device);
-
 int gateway_close(struct audio_device *device);
 
-static void rfcomm_start_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	gw->rfcomm_watch_id = g_io_add_watch(gw->rfcomm,
-			G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-			(GIOFunc) rfcomm_ag_data_cb, dev);
-}
-
-static void rfcomm_stop_watch(struct audio_device *dev)
-{
-	struct gateway *gw = dev->gateway;
-
-	g_source_remove(gw->rfcomm_watch_id);
-}
-
-static gboolean io_channel_write_all(GIOChannel *io, gchar *data,
-					gsize count)
-{
-	gsize written = 0;
-	GIOStatus status;
-
-	while (count > 0) {
-		status = g_io_channel_write_chars(io, data, count, &written,
-						NULL);
-		if (status != G_IO_STATUS_NORMAL)
-			return FALSE;
-
-		data += written;
-		count -= written;
-	}
-	return TRUE;
-}
-
-/* it's worth to mention that data and response could be the same pointers */
-static gboolean rfcomm_send_and_read(struct gateway *gw, gchar *data,
-                                    gchar *response, gsize count)
-{
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read = 0;
-	gboolean got_ok = FALSE;
-	gboolean got_error = FALSE;
-	gchar *resp_buf = response;
-	gsize toread = RFCOMM_BUF_SIZE - 1;
-	GIOStatus status;
-
-	if (!io_channel_write_all(rfcomm, data, count))
-		return FALSE;
-
-	while (!(got_ok || got_error)) {
-		status = g_io_channel_read_chars(rfcomm, resp_buf, toread,
-						&read, NULL);
-		if (status == G_IO_STATUS_NORMAL)
-			resp_buf[read] = '\0';
-		else {
-			debug("rfcomm_send_and_read(): %m");
-			return FALSE;
-		}
-		got_ok = NULL != strstr(resp_buf, OK_RESPONSE);
-		got_error = NULL != strstr(resp_buf, ERROR_RESPONSE);
-		resp_buf += read;
-		toread -= read;
-	}
-	return TRUE;
-}
-
-/* get <descr> from the names: (<descr>, (<values>)), (<descr>, (<values>))
- * ... */
-static GSList *parse_indicator_names(gchar *names, GSList *indies)
-{
-	gchar *current = names - 1;
-	GSList *result = indies;
-	gchar *next;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 2;
-		next = strstr(current, ",(");
-		ind = g_slice_new(struct indicator);
-		strncpy(ind->descr, current, 20);
-		ind->descr[(intptr_t) next - (intptr_t) current] = '\0';
-		result = g_slist_append(result, (gpointer) ind);
-		current = strstr(next + 1, ",(");
-	}
-	return result;
-}
-
-/* get values from <val0>,<val1>,... */
-static GSList *parse_indicator_values(gchar *values, GSList *indies)
-{
-	gint val;
-	gchar *current = values - 1;
-	GSList *runner = indies;
-	struct indicator *ind;
-
-	while (current != NULL) {
-		current += 1;
-		sscanf(current, "%d", &val);
-		current = strchr(current, ',');
-		ind = g_slist_nth_data(runner, 0);
-		ind->value = val;
-		runner = g_slist_next(runner);
-	}
-	return indies;
-}
-
-/* get values from <val0>,<val1>,... */
-static guint get_hold_mpty_features(gchar *features)
-{
-	guint result = 0;
-
-	if (strstr(features, "0"))
-		result |= AG_CHLD_0;
-
-	if (strstr(features, "1"))
-		result |= AG_CHLD_1;
-
-	if (strstr(features, "1x"))
-		result |= AG_CHLD_1x;
-
-	if (strstr(features, "2"))
-		result |= AG_CHLD_2;
-
-	if (strstr(features, "2x"))
-		result |= AG_CHLD_2x;
-
-	if (strstr(features, "3"))
-		result |= AG_CHLD_3;
-
-	if (strstr(features, "4"))
-		result |= AG_CHLD_4;
-
-	return result;
-}
-
-static gboolean establish_service_level_conn(struct gateway *gw)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	gboolean res;
-
-	debug("at the begin of establish_service_level_conn()");
-	res = rfcomm_send_and_read(gw, AG_FEATURES, buf,
-				sizeof(AG_FEATURES) - 1);
-	if (!res || sscanf(buf, "\r\n+BRSF:%d", &gw->ag_features) != 1)
-		return FALSE;
-
-	debug("features are 0x%X", gw->ag_features);
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_SUPP, buf,
-				sizeof(AG_INDICATORS_SUPP) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_names(strchr(buf, '('), NULL);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_VAL, buf,
-		sizeof(AG_INDICATORS_VAL) - 1);
-	if (!res || !strstr(buf, "+CIND:"))
-		return FALSE;
-
-	gw->indies = parse_indicator_values(strchr(buf, ':') + 1, gw->indies);
-
-	res = rfcomm_send_and_read(gw, AG_INDICATORS_ENABLE, buf,
-				sizeof(AG_INDICATORS_ENABLE) - 1);
-	if (!res || !strstr(buf, "OK"))
-		return FALSE;
-
-	if ((gw->ag_features & AG_FEATURE_3WAY) != 0) {
-		res = rfcomm_send_and_read(gw, AG_HOLD_MPTY_SUPP, buf,
-				sizeof(AG_HOLD_MPTY_SUPP) - 1);
-		if (!res || !strstr(buf, "+CHLD:")) {
-			g_slice_free1(RFCOMM_BUF_SIZE, buf);
-			return FALSE;
-		}
-		gw->hold_multiparty_features = get_hold_mpty_features(
-							strchr(buf, '('));
-
-	} else
-		gw->hold_multiparty_features = 0;
-
-	debug("Service layer connection successfully established!");
-	rfcomm_send_and_read(gw, AG_CALLER_IDENT_ENABLE, buf,
-			sizeof(AG_CALLER_IDENT_ENABLE) - 1);
-	rfcomm_send_and_read(gw, AG_CARRIER_FORMAT, buf,
-			sizeof(AG_CARRIER_FORMAT) - 1);
-	if ((gw->ag_features & AG_FEATURE_EXTENDED_RES_CODE) != 0)
-		rfcomm_send_and_read(gw, AG_EXTENDED_RESULT_CODE, buf,
-			sizeof(AG_EXTENDED_RESULT_CODE) - 1);
-
-	return TRUE;
-}
-
-static void process_ind_change(struct audio_device *dev, guint index,
-							gint value)
-{
-	struct gateway *gw = dev->gateway;
-	struct indicator *ind = g_slist_nth_data(gw->indies, index - 1);
-	gchar *name = ind->descr;
-
-	ind->value = value;
-
-	debug("at the begin of process_ind_change, name is %s", name);
-	if (!strcmp(name, "\"call\"")) {
-		if (value > 0) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallStarted", DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-			gw->call_active = TRUE;
-		} else {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallEnded", DBUS_TYPE_INVALID);
-			gw->call_active = FALSE;
-		}
-
-	} else if (!strcmp(name, "\"callsetup\"")) {
-		if (value == 0 && gw->is_dialing) {
-			g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-			gw->is_dialing = FALSE;
-		} else if (!gw->is_dialing && value > 0)
-			gw->is_dialing = TRUE;
-
-	} else if (!strcmp(name, "\"callheld\"")) {
-		/* FIXME: The following code is based on assumptions only.
-		 * Has to be tested for interoperability
-		 * I assume that callheld=2 would be sent when dial from HF
-		 * failed in case of 3-way call
-		 * Unfortunately this path is not covered by the HF spec so
-		 * the code has to be tested for interop
-		*/
-		/* '2' means: all calls held, no active calls */
-		if (value == 2) {
-			if (gw->is_dialing) {
-				g_dbus_emit_signal(dev->conn, dev->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"CallTerminated",
-					DBUS_TYPE_INVALID);
-				gw->is_dialing = FALSE;
-			}
-		}
-	} else if (!strcmp(name, "\"service\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RegistrationStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"signal\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "SignalStrength",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"roam\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "RoamingStatus",
-				DBUS_TYPE_UINT16, &value);
-	else if (!strcmp(name, "\"battchg\""))
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE, "BatteryCharge",
-				DBUS_TYPE_UINT16, &value);
-}
-
-static void process_ring(struct audio_device *device, GIOChannel *chan,
-			gchar *buf)
-{
-	gchar number[AG_CALLER_NUM_SIZE];
-	gchar *cli;
-	gchar *sep;
-	gsize read;
-	GIOStatus status;
-
-	rfcomm_stop_watch(device);
-	status = g_io_channel_read_chars(chan, buf, RFCOMM_BUF_SIZE - 1, &read, NULL);
-	if (status != G_IO_STATUS_NORMAL)
-		return;
-
-	debug("at the begin of process_ring");
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 10)
-		error("process_ring(): buf is too long '%s'", buf);
-	else if ((cli = strstr(buf, "\r\n+CLIP"))) {
-		if (sscanf(cli, "\r\n+CLIP: \"%s", number) == 1) {
-			sep = strchr(number, '"');
-			sep[0] = '\0';
-
-			/* FIXME:signal will be emitted on each RING+CLIP.
-			 * That's bad */
-			cli = number;
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE, "Ring",
-					DBUS_TYPE_STRING, &cli,
-					DBUS_TYPE_INVALID);
-			device->gateway->is_dialing = TRUE;
-		} else
-			error("process_ring(): '%s' in place of +CLIP after RING", buf);
-
-	}
-
-	rfcomm_start_watch(device);
-}
-
-static gboolean rfcomm_ag_data_cb(GIOChannel *chan, GIOCondition cond,
-					struct audio_device *device)
-{
-	gchar buf[RFCOMM_BUF_SIZE];
-	struct gateway *gw;
-	gsize read;
-	/* some space for value */
-	gchar indicator[AG_INDICATOR_DESCR_SIZE + 4];
-	gint value;
-	guint index;
-	gchar *sep;
-
-	debug("at the begin of rfcomm_ag_data_cb()");
-	if (cond & G_IO_NVAL)
-		return FALSE;
-
-	gw = device->gateway;
-
-	if (cond & (G_IO_ERR | G_IO_HUP)) {
-		debug("connection with remote BT is closed");
-		gateway_close(device);
-		return FALSE;
-	}
-
-	if (g_io_channel_read_chars(chan, buf, sizeof(buf) - 1, &read, NULL)
-			!= G_IO_STATUS_NORMAL)
-		return TRUE;
-	buf[read] = '\0';
-
-	if (strlen(buf) > AG_INDICATOR_DESCR_SIZE + 14)
-		error("rfcomm_ag_data_cb(): buf is too long '%s'", buf);
-	else if (sscanf(buf, "\r\n+CIEV:%s\r\n", indicator) == 1) {
-		sep = strchr(indicator, ',');
-		sep[0] = '\0';
-		sep += 1;
-		index = atoi(indicator);
-		value = atoi(sep);
-		process_ind_change(device, index, value);
-	} else if (strstr(buf, "RING"))
-		process_ring(device, chan, buf);
-	else if (sscanf(buf, "\r\n+BVRA:%d\r\n", &value) == 1) {
-		if (value == 0)
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionActive",
-					DBUS_TYPE_INVALID);
-		else
-			g_dbus_emit_signal(device->conn, device->path,
-					AUDIO_GATEWAY_INTERFACE,
-					"VoiceRecognitionInactive",
-					DBUS_TYPE_INVALID);
-	} else if (sscanf(buf, "\r\n+VGS:%d\r\n", &value) == 1) {
-		gw->sp_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "SpeakerGain",
-				DBUS_TYPE_UINT16, &value);
-	} else if (sscanf(buf, "\r\n+VGM:%d\r\n", &value) == 1) {
-		gw->mic_gain = value;
-		emit_property_changed(device->conn, device->path,
-				AUDIO_GATEWAY_INTERFACE, "MicrophoneGain",
-				DBUS_TYPE_UINT16, &value);
-	} else
-		error("rfcomm_ag_data_cb(): read wrong data '%s'", buf);
-
-	return TRUE;
-}
-
 static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
 			struct audio_device *dev)
 {
@@ -541,7 +118,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 {
 	struct audio_device *dev = user_data;
 	struct gateway *gw = dev->gateway;
-	DBusMessage *conn_mes = gw->connect_message;
 	gchar gw_addr[18];
 	GIOFlags flags;
 
@@ -563,29 +139,6 @@ static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
 	if (!gw->rfcomm)
 		gw->rfcomm = g_io_channel_ref(chan);
 
-	if (establish_service_level_conn(dev->gateway)) {
-		gboolean value = TRUE;
-
-		debug("%s: Connected to %s", dev->path, gw_addr);
-		rfcomm_start_watch(dev);
-		if (conn_mes) {
-			DBusMessage *reply =
-				dbus_message_new_method_return(conn_mes);
-			dbus_connection_send(dev->conn, reply, NULL);
-			dbus_message_unref(reply);
-			dbus_message_unref(conn_mes);
-			gw->connect_message = NULL;
-		}
-
-		gw->state = GATEWAY_STATE_CONNECTED;
-		emit_property_changed(dev->conn, dev->path,
-				AUDIO_GATEWAY_INTERFACE,
-				"Connected", DBUS_TYPE_BOOLEAN,	&value);
-		return;
-	} else
-		error("%s: Failed to establish service layer connection to %s",
-			dev->path, gw_addr);
-
 	if (NULL != gw->sco_start_cb)
 		gw->sco_start_cb(NULL, gw->sco_start_cb_data);
 
@@ -732,321 +285,20 @@ static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
 	return reply;
 }
 
-static DBusMessage *process_ag_reponse(DBusMessage *msg, gchar *response)
-{
-	DBusMessage *reply;
-
-
-	debug("in process_ag_reponse, response is %s", response);
-	if (strstr(response, OK_RESPONSE))
-		reply = dbus_message_new_method_return(msg);
-	else {
-		/* FIXME: some code should be here to processes errors
-		 *  in better fasion */
-		debug("AG responded with '%s' to %s method call", response,
-				dbus_message_get_member(msg));
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".OperationFailed",
-					"Operation failed.See log for details");
-	}
-	return reply;
-}
-
-static DBusMessage *process_simple(DBusMessage *msg, struct audio_device *dev,
-					gchar *data)
-{
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-
-	rfcomm_stop_watch(dev);
-	rfcomm_send_and_read(gw, data, buf, strlen(data));
-	rfcomm_start_watch(dev);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_ANSWER "ATA\r"
-
-static DBusMessage *ag_answer(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	if (gw->call_active)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".CallAlreadyAnswered",
-					"Call AlreadyAnswered");
-
-	return process_simple(msg, dev, AG_ANSWER);
-}
-
-#define AG_HANGUP "AT+CHUP\r"
-
-static DBusMessage *ag_terminate_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *dev = data;
-	struct gateway *gw = dev->gateway;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	return process_simple(msg, dev, AG_HANGUP);
-}
-
-/* according to GSM spec */
-#define ALLOWED_NUMBER_SYMBOLS "1234567890*#ABCD"
-#define AG_PLACE_CALL "ATD%s;\r"
-/* dialing from memory is not supported as headset spec doesn't define a way
- * to retreive phone memory entries.
- */
-static DBusMessage *ag_call(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint atd_len;
-	DBusMessage *result;
-
-	debug("at the begin of ag_call()");
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	atd_len = sprintf(buf, AG_PLACE_CALL, number);
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, buf, buf, atd_len);
-	rfcomm_start_watch(device);
-
-	result = process_ag_reponse(msg, buf);
-	return result;
-}
-
-#define AG_GET_CARRIER "AT+COPS?\r"
-
-static DBusMessage *ag_get_operator(DBusConnection *conn, DBusMessage *msg,
-					void *data)
-{
-	struct audio_device *dev = (struct audio_device *) data;
-	struct gateway *gw = dev->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	GIOChannel *rfcomm = gw->rfcomm;
-	gsize read;
-	gchar *result, *sep;
-	DBusMessage *reply;
-	GIOStatus status;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(dev);
-	io_channel_write_all(rfcomm, AG_GET_CARRIER, strlen(AG_GET_CARRIER));
-
-	status = g_io_channel_read_chars(rfcomm, buf, RFCOMM_BUF_SIZE - 1,
-						&read, NULL);
-	rfcomm_start_watch(dev);
-	if (G_IO_STATUS_NORMAL == status) {
-		buf[read] = '\0';
-		if (strstr(buf, "+COPS")) {
-			if (!strrchr(buf, ','))
-				result = "0";
-			else {
-				result = strchr(buf, '\"') + 1;
-				sep = strchr(result, '\"');
-				sep[0] = '\0';
-			}
-
-			reply = dbus_message_new_method_return(msg);
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&result, DBUS_TYPE_INVALID);
-		} else {
-			info("ag_get_operator(): '+COPS' expected but"
-				" '%s' received", buf);
-			reply = dbus_message_new_error(msg, ERROR_INTERFACE
-						".Failed",
-						"Unexpected response from AG");
-		}
-	} else {
-		error("ag_get_operator(): %m");
-		reply = dbus_message_new_error(msg, ERROR_INTERFACE
-					".ConnectionFailed",
-					"Failed to receive response from AG");
-	}
-
-	return reply;
-}
-
-#define AG_SEND_DTMF "AT+VTS=%c\r"
-static DBusMessage *ag_send_dtmf(DBusConnection *conn, DBusMessage *msg,
-				void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number;
-	gint com_len;
-	gboolean got_ok = TRUE;
-	gint num_len;
-	gint i = 0;
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
-				DBUS_TYPE_INVALID);
-	if (strlen(number) != strspn(number, ALLOWED_NUMBER_SYMBOLS))
-		return dbus_message_new_error(msg,
-			ERROR_INTERFACE ".BadNumber",
-			"Number contains characters which are not allowed");
-
-	num_len = strlen(number);
-	rfcomm_stop_watch(device);
-	while (i < num_len && got_ok) {
-		com_len = sprintf(buf, AG_SEND_DTMF, number[i]);
-		rfcomm_send_and_read(gw, buf, buf, com_len);
-		got_ok = NULL != strstr(buf, OK_RESPONSE);
-		i += 1;
-	}
-	rfcomm_start_watch(device);
-	return process_ag_reponse(msg, buf);
-}
-
-#define AG_GET_SUBSCRIBER_NUMS "AT+CNUM\r"
-#define CNUM_LEN 5             /* length of "+CNUM" string */
-#define MAX_NUMBER_CNT 16
-static DBusMessage *ag_get_subscriber_num(DBusConnection *conn,
-					DBusMessage *msg, void *data)
-{
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	gchar buf[RFCOMM_BUF_SIZE];
-	gchar *number, *end;
-	DBusMessage *reply = dbus_message_new_method_return(msg);
-
-	if (!gw->rfcomm)
-		return g_dbus_create_error(msg, ERROR_INTERFACE
-					".NotConnected",
-					"Not Connected");
-
-	rfcomm_stop_watch(device);
-	rfcomm_send_and_read(gw, AG_GET_SUBSCRIBER_NUMS, buf,
-			strlen(AG_GET_SUBSCRIBER_NUMS));
-	rfcomm_start_watch(device);
-
-	if (strlen(buf) > AG_CALLER_NUM_SIZE + 21)
-		error("ag_get_subscriber_num(): buf is too long '%s'", buf);
-	else if (strstr(buf, "+CNUM")) {
-		number = strchr(buf, ',');
-		number++;
-		end = strchr(number, ',');
-		if (end) {
-			*end = '\0';
-			dbus_message_append_args(reply, DBUS_TYPE_STRING,
-						&number, DBUS_TYPE_INVALID);
-		}
-	} else
-		error("ag_get_subscriber_num(): read wrong data '%s'", buf);
-
-	return reply;
-}
-
 static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
-	struct audio_device *device = data;
-	struct gateway *gw = device->gateway;
-	DBusMessage *reply;
-	DBusMessageIter iter;
-	DBusMessageIter dict;
-	gboolean value;
-	guint index = 0;
-	struct indicator *ind;
-
-	reply = dbus_message_new_method_return(msg);
-	if (!reply)
-		return NULL;
-
-	dbus_message_iter_init_append(reply, &iter);
-
-	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
-			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
-			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
-	/* Connected */
-	value = gateway_is_connected(device);
-	dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
-
-	if (!value)
-		goto done;
-
-	while ((ind = g_slist_nth_data(gw->indies, index))) {
-		if(!strcmp(ind->descr, "\"service\""))
-			dict_append_entry(&dict, "RegistrationStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"signal\""))
-			dict_append_entry(&dict, "SignalStrength",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"roam\""))
-			dict_append_entry(&dict, "RoamingStatus",
-					DBUS_TYPE_UINT16, &ind->value);
-		else if (!strcmp(ind->descr, "\"battchg\""))
-			dict_append_entry(&dict, "BatteryCharge",
-					DBUS_TYPE_UINT16, &ind->value);
-		index++;
-	}
-
-	/* SpeakerGain */
-	dict_append_entry(&dict, "SpeakerGain", DBUS_TYPE_UINT16,
-				&device->gateway->sp_gain);
-
-	/* MicrophoneGain */
-	dict_append_entry(&dict, "MicrophoneGain", DBUS_TYPE_UINT16,
-				&device->gateway->mic_gain);
-done:
-	dbus_message_iter_close_container(&iter, &dict);
-	return reply;
+	return NULL;
 }
 
 static GDBusMethodTable gateway_methods[] = {
 	{ "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
 	{ "Disconnect", "", "", ag_disconnect },
-	{ "AnswerCall", "", "", ag_answer },
-	{ "TerminateCall", "", "", ag_terminate_call },
-	{ "Call", "s", "", ag_call },
-	{ "GetOperatorName", "", "s", ag_get_operator },
-	{ "SendDTMF", "s", "", ag_send_dtmf },
-	{ "GetSubscriberNumber", "", "s", ag_get_subscriber_num },
 	{ "GetProperties", "", "a{sv}", ag_get_properties },
 	{ NULL, NULL, NULL, NULL }
 };
 
 static GDBusSignalTable gateway_signals[] = {
-	{ "Ring", "s" },
-	{ "CallTerminated", "" },
-	{ "CallStarted", "" },
-	{ "CallEnded", "" },
 	{ "PropertyChanged", "sv" },
 	{ NULL, NULL }
 };
@@ -1063,9 +315,6 @@ struct gateway *gateway_init(struct audio_device *dev)
 
 	debug("in gateway_init, dev is %p", dev);
 	gw = g_new0(struct gateway, 1);
-	gw->indies = NULL;
-	gw->is_dialing = FALSE;
-	gw->call_active = FALSE;
 	gw->state = GATEWAY_STATE_DISCONNECTED;
 	return gw;
 
@@ -1107,11 +356,6 @@ void gateway_start_service(struct audio_device *device)
 	rfcomm_connect_cb(device->gateway->rfcomm, NULL, device);
 }
 
-static void indicator_slice_free(gpointer mem)
-{
-	g_slice_free(struct indicator, mem);
-}
-
 int gateway_close(struct audio_device *device)
 {
 	struct gateway *gw = device->gateway;
@@ -1119,8 +363,6 @@ int gateway_close(struct audio_device *device)
 	GIOChannel *sco = gw->sco;
 	gboolean value = FALSE;
 
-	g_slist_foreach(gw->indies, (GFunc) indicator_slice_free, NULL);
-	g_slist_free(gw->indies);
 	if (rfcomm) {
 		g_io_channel_shutdown(rfcomm, TRUE, NULL);
 		g_io_channel_unref(rfcomm);
-- 
1.6.4.4


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

end of thread, other threads:[~2012-08-21 22:46 UTC | newest]

Thread overview: 51+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-01-11 17:08 [RFC] HFP support into oFono and BlueZ Gustavo F. Padovan
2010-01-11 17:08 ` Gustavo F. Padovan
2010-01-11 19:05 ` Gustavo F. Padovan
2010-01-11 19:05   ` Gustavo F. Padovan
2010-01-13 23:39   ` Gustavo F. Padovan
2010-01-13 23:39     ` Gustavo F. Padovan
2010-01-18 11:38     ` Luiz Augusto von Dentz
2010-01-18 11:38       ` Luiz Augusto von Dentz
2010-01-17 22:37       ` Denis Kenzior
2010-01-17 22:37         ` Denis Kenzior
2010-01-19  8:02         ` Johan Hedberg
2010-01-19  8:02           ` Johan Hedberg
2010-01-19  9:30         ` Luiz Augusto von Dentz
2010-01-19  9:30           ` Luiz Augusto von Dentz
2010-01-19 10:33           ` Johan Hedberg
2010-01-19 10:33             ` Johan Hedberg
2010-01-19 12:26             ` Luiz Augusto von Dentz
2010-01-19 12:26               ` Luiz Augusto von Dentz
2010-01-20 19:58     ` Gustavo F. Padovan
2010-01-20 19:58       ` Gustavo F. Padovan
2010-01-21  6:28       ` Zhang, Zhenhua
2010-01-21  6:28         ` Zhang, Zhenhua
2010-01-21  7:54       ` Zhao Forrest
2010-01-21  7:54         ` Zhao Forrest
2010-01-21  2:56         ` Denis Kenzior
2012-08-21 22:46         ` Lucas De Marchi
2012-08-21 22:46           ` Lucas De Marchi
2010-01-21 19:22       ` Johan Hedberg
2010-01-21 19:22         ` Johan Hedberg
2010-01-21 19:27         ` Johan Hedberg
2010-01-21 19:27           ` Johan Hedberg
2010-01-27 19:12       ` HFP support into BlueZ and oFono Gustavo F. Padovan
2010-01-27 19:12         ` Gustavo F. Padovan
2010-01-27 19:12         ` [PATCH 1/2] clean up audio/gateway.c Gustavo F. Padovan
2010-01-27 19:12           ` Gustavo F. Padovan
2010-01-27 19:12           ` [PATCH 2/2] Implement HandsfreeGateway Interface Gustavo F. Padovan
2010-01-27 19:12             ` Gustavo F. Padovan
2010-01-27 19:12             ` [PATCH] Add HFP support through BlueZ Gustavo F. Padovan
2010-01-27 19:12               ` Gustavo F. Padovan
2010-01-27 20:17               ` Marcel Holtmann
2010-01-27 20:17                 ` Marcel Holtmann
2010-02-01  3:15           ` [PATCH 1/2] clean up audio/gateway.c Zhenhua Zhang
2010-02-01  3:15             ` Zhenhua Zhang
2010-01-27 19:17         ` HFP support into BlueZ and oFono Gustavo F. Padovan
2010-01-27 19:17           ` Gustavo F. Padovan
2010-01-13  0:54 ` [RFC] HFP support into oFono and BlueZ Denis Kenzior
2010-01-13  1:44   ` Marcel Holtmann
2010-01-21 20:31 [PATCH 1/2] clean up audio/gateway.c Gustavo F. Padovan
2010-01-21 20:31 ` Gustavo F. Padovan
2010-02-02 21:41 Gustavo F. Padovan
2010-02-02 21:41 ` Gustavo F. Padovan

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.