linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC] HFP support into oFono and BlueZ
@ 2010-01-11 17:08 Gustavo F. Padovan
  2010-01-11 19:05 ` Gustavo F. Padovan
  0 siblings, 1 reply; 23+ 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] 23+ messages in thread

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

Thread overview: 23+ 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 19:05 ` Gustavo F. Padovan
2010-01-13 23:39   ` Gustavo F. Padovan
2010-01-18 11:38     ` Luiz Augusto von Dentz
2010-01-17 22:37       ` Denis Kenzior
2010-01-19  8:02         ` Johan Hedberg
2010-01-19  9:30         ` Luiz Augusto von Dentz
2010-01-19 10:33           ` Johan Hedberg
2010-01-19 12:26             ` Luiz Augusto von Dentz
2010-01-20 19:58     ` Gustavo F. Padovan
2010-01-21  6:28       ` Zhang, Zhenhua
2010-01-21  7:54       ` Zhao Forrest
2010-01-21  2:56         ` Denis Kenzior
2012-08-21 22:46         ` Lucas De Marchi
2010-01-21 19:22       ` 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         ` [PATCH 1/2] clean up audio/gateway.c Gustavo F. Padovan
2010-01-27 19:12           ` [PATCH 2/2] Implement HandsfreeGateway Interface Gustavo F. Padovan
2010-01-27 19:12             ` [PATCH] Add HFP support through BlueZ Gustavo F. Padovan
2010-01-27 20:17               ` Marcel Holtmann
2010-02-01  3:15           ` [PATCH 1/2] clean up audio/gateway.c Zhenhua Zhang
2010-01-27 19:17         ` HFP support into BlueZ and oFono Gustavo F. Padovan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).