2010-01-11 17:08:04

by Gustavo Padovan

[permalink] [raw]
Subject: [RFC] HFP support into oFono and BlueZ

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


Attachments:
0001-clean-up-audio-gateway.c.patch (24.04 kB)
0002-Implement-HandsfreeGateway-Interface.patch (24.97 kB)
0001-Add-HFP-support-in-oFono.patch (13.03 kB)
Download all attachments

2010-01-27 20:17:15

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH] Add HFP support through BlueZ

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 <[email protected]> for its prototype
> on this code.

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

Regards

Marcel



2010-01-27 19:17:04

by Gustavo Padovan

[permalink] [raw]
Subject: Re: HFP support into BlueZ and oFono

On Wed, Jan 27, 2010 at 5:12 PM, Gustavo F. Padovan
<[email protected]> 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

2010-01-27 19:12:49

by Gustavo Padovan

[permalink] [raw]
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 suport into BlueZ and the new dbus 1.3 with fd-passing
support.

Many thanks to Zhenhua Zhang <[email protected]> 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


2010-01-27 19:12:48

by Gustavo Padovan

[permalink] [raw]
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 <[email protected]> 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 <[email protected]>
* Copyright (C) 2008-2009 Leonid Movshovich <[email protected]>
+ * Copyright (C) 2009-2009 Zhenhua Zhang <[email protected]>
+ * Copyright (C) 2010 Gustavo F. Padovan <[email protected]>
*
*
* 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 <[email protected]>
+ * Copyright (C) 2009-2009 Zhenhua Zhang <[email protected]>
*
*
* 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


2010-01-27 19:12:47

by Gustavo Padovan

[permalink] [raw]
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


2010-01-27 19:12:46

by Gustavo Padovan

[permalink] [raw]
Subject: HFP support into BlueZ and 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


2010-01-21 21:25:19

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [PATCH 2/2] Implement HandsfreeGateway Interface

On Thu, Jan 21, 2010 at 7:20 PM, Vinicius Gomes
<[email protected]> wrote:
> Hi Gustavo,
>
> On Thu, Jan 21, 2010 at 5:31 PM, Gustavo F. Padovan
> <[email protected]> wrote:
>> +       gw->sco = chan;
>> +       g_io_channel_ref(chan);
>> +
>
> You must have forgotten to change this.
>
>> @@ -307,17 +565,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;
>>
>
> Seems like gw is leaking if registration fails. Move it down a little, perhaps?

Sounds good. Thanks. :)

>
>> -       debug("in gateway_init, dev is %p", dev);
>> -       gw = g_new0(struct gateway, 1);
>> -       gw->state = GATEWAY_STATE_DISCONNECTED;
>>        return gw;
>> -
>>  }
>>
>
>
> Cheers,
> --
> Vinicius Gomes
> INdT - Instituto Nokia de Tecnologia
>



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

2010-01-21 21:20:23

by Vinicius Costa Gomes

[permalink] [raw]
Subject: Re: [PATCH 2/2] Implement HandsfreeGateway Interface

SGkgR3VzdGF2bywKCk9uIFRodSwgSmFuIDIxLCAyMDEwIGF0IDU6MzEgUE0sIEd1c3Rhdm8gRi4g
UGFkb3Zhbgo8cGFkb3ZhbkBwcm9mdXNpb24ubW9iaT4gd3JvdGU6Cj4gKyDCoCDCoCDCoCBndy0+
c2NvID0gY2hhbjsKPiArIMKgIMKgIMKgIGdfaW9fY2hhbm5lbF9yZWYoY2hhbik7Cj4gKwoKWW91
IG11c3QgaGF2ZSBmb3Jnb3R0ZW4gdG8gY2hhbmdlIHRoaXMuCgo+IEBAIC0zMDcsMTcgKzU2NSwx
NSBAQCBzdHJ1Y3QgZ2F0ZXdheSAqZ2F0ZXdheV9pbml0KHN0cnVjdCBhdWRpb19kZXZpY2UgKmRl
dikKPiDCoHsKPiDCoCDCoCDCoCDCoHN0cnVjdCBnYXRld2F5ICpndzsKPgo+ICsgwqAgwqAgwqAg
Z3cgPSBnX25ldzAoc3RydWN0IGdhdGV3YXksIDEpOwo+ICsKPiDCoCDCoCDCoCDCoGlmICghZ19k
YnVzX3JlZ2lzdGVyX2ludGVyZmFjZShkZXYtPmNvbm4sIGRldi0+cGF0aCwKPiDCoCDCoCDCoCDC
oCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoCDCoEFVRElPX0dB
VEVXQVlfSU5URVJGQUNFLAo+IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKg
IMKgIMKgIMKgIMKgIMKgIMKgIMKgZ2F0ZXdheV9tZXRob2RzLCBnYXRld2F5X3NpZ25hbHMsCj4g
wqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAgwqAg
wqBOVUxMLCBkZXYsIE5VTEwpKQo+IMKgIMKgIMKgIMKgIMKgIMKgIMKgIMKgcmV0dXJuIE5VTEw7
Cj4KClNlZW1zIGxpa2UgZ3cgaXMgbGVha2luZyBpZiByZWdpc3RyYXRpb24gZmFpbHMuIE1vdmUg
aXQgZG93biBhIGxpdHRsZSwgcGVyaGFwcz8KCj4gLSDCoCDCoCDCoCBkZWJ1ZygiaW4gZ2F0ZXdh
eV9pbml0LCBkZXYgaXMgJXAiLCBkZXYpOwo+IC0gwqAgwqAgwqAgZ3cgPSBnX25ldzAoc3RydWN0
IGdhdGV3YXksIDEpOwo+IC0gwqAgwqAgwqAgZ3ctPnN0YXRlID0gR0FURVdBWV9TVEFURV9ESVND
T05ORUNURUQ7Cj4gwqAgwqAgwqAgwqByZXR1cm4gZ3c7Cj4gLQo+IMKgfQo+CgoKQ2hlZXJzLAot
LSAKVmluaWNpdXMgR29tZXMKSU5kVCAtIEluc3RpdHV0byBOb2tpYSBkZSBUZWNub2xvZ2lhCg==

2010-01-21 19:27:34

by Johan Hedberg

[permalink] [raw]
Subject: Re: [RFC] HFP support into oFono and BlueZ

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


2010-01-21 02:56:33

by Denis Kenzior

[permalink] [raw]
Subject: Re: [RFC] HFP support into oFono and BlueZ

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

2010-01-21 07:54:40

by Zhao Forrest

[permalink] [raw]
Subject: Re: [RFC] HFP support into oFono and BlueZ

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
<[email protected]> 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
>

2010-01-21 06:28:08

by Zhenhua Zhang

[permalink] [raw]
Subject: RE: [RFC] HFP support into oFono and BlueZ

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

2010-01-20 19:58:18

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [RFC] HFP support into oFono and BlueZ

> 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


Attachments:
0001-clean-up-audio-gateway.c.patch (24.04 kB)
0002-Implement-HandsfreeGateway-Interface.patch (23.51 kB)
0001-Add-HFP-support-through-BlueZ.patch (14.65 kB)
Download all attachments

2010-01-19 12:26:51

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [RFC] HFP support into oFono and BlueZ

Hi Johan,

On Tue, Jan 19, 2010 at 12:33 PM, Johan Hedberg <[email protected]> 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

2010-01-19 10:33:38

by Johan Hedberg

[permalink] [raw]
Subject: Re: [RFC] HFP support into oFono and BlueZ

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

2010-01-19 09:30:03

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [RFC] HFP support into oFono and BlueZ

Hi Denis,

On Mon, Jan 18, 2010 at 12:37 AM, Denis Kenzior <[email protected]> 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

2010-01-19 08:02:23

by Johan Hedberg

[permalink] [raw]
Subject: Re: [RFC] HFP support into oFono and BlueZ

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
[email protected]
http://lists.ofono.org/listinfo/ofono

2010-01-17 22:37:21

by Denis Kenzior

[permalink] [raw]
Subject: Re: [RFC] HFP support into oFono and BlueZ

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

2010-01-18 11:38:16

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [RFC] HFP support into oFono and BlueZ

Hi,

On Thu, Jan 14, 2010 at 1:39 AM, Gustavo F. Padovan
<[email protected]> wrote:
> Hi,
>
> On Mon, Jan 11, 2010 at 5:05 PM, Gustavo F. Padovan
> <[email protected]> wrote:
>> On Mon, Jan 11, 2010 at 3:08 PM, Gustavo F. Padovan
>> <[email protected]> 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

2010-01-13 23:39:45

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [RFC] HFP support into oFono and BlueZ

Hi,

On Mon, Jan 11, 2010 at 5:05 PM, Gustavo F. Padovan
<[email protected]> wrote:
> On Mon, Jan 11, 2010 at 3:08 PM, Gustavo F. Padovan
> <[email protected]> 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


Attachments:
0001-clean-up-audio-gateway.c.patch (24.04 kB)
0002-Implement-HandsfreeGateway-Interface.patch (23.44 kB)
0001-Add-HFP-support-in-oFono.patch (13.28 kB)
Download all attachments

2010-01-11 19:05:03

by Gustavo Padovan

[permalink] [raw]
Subject: Re: [RFC] HFP support into oFono and BlueZ

On Mon, Jan 11, 2010 at 3:08 PM, Gustavo F. Padovan
<[email protected]> 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


Attachments:
0001-clean-up-audio-gateway.c.patch (24.04 kB)
0002-Implement-HandsfreeGateway-Interface.patch (21.28 kB)
0001-Add-HFP-support-in-oFono.patch (12.70 kB)
Download all attachments

2010-02-01 03:15:27

by Zhenhua Zhang

[permalink] [raw]
Subject: Re: [PATCH 1/2] clean up audio/gateway.c

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
> [email protected]
> http://lists.ofono.org/listinfo/ofono
>
>


2012-08-21 22:46:04

by Lucas De Marchi

[permalink] [raw]
Subject: Re: [RFC] HFP support into oFono and BlueZ

Hi Zhao,

On Thu, Jan 21, 2010 at 5:54 AM, Zhao Forrest <[email protected]> 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