Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH v3 6/8] android/handsfree: Add support for HSP AG
From: Szymon Janc @ 2014-03-02 23:50 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Szymon Janc
In-Reply-To: <1393804249-12392-1-git-send-email-szymon.janc@tieto.com>

---
 android/handsfree.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 135 insertions(+), 1 deletion(-)

diff --git a/android/handsfree.c b/android/handsfree.c
index 7a1d697..0b97a5d 100644
--- a/android/handsfree.c
+++ b/android/handsfree.c
@@ -46,7 +46,9 @@
 #include "src/log.h"
 #include "utils.h"
 
+#define HSP_AG_CHANNEL 12
 #define HFP_AG_CHANNEL 13
+
 #define HFP_AG_FEATURES 0
 
 static struct {
@@ -61,6 +63,9 @@ static struct ipc *hal_ipc = NULL;
 static uint32_t hfp_record_id = 0;
 static GIOChannel *hfp_server = NULL;
 
+static uint32_t hsp_record_id = 0;
+static GIOChannel *hsp_server = NULL;
+
 static void device_set_state(uint8_t state)
 {
 	struct hal_ev_handsfree_conn_state ev;
@@ -460,6 +465,129 @@ static const struct ipc_handler cmd_handlers[] = {
 			sizeof(struct hal_cmd_handsfree_phone_state_change)},
 };
 
+static sdp_record_t *headset_ag_record(void)
+{
+	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+	uuid_t l2cap_uuid, rfcomm_uuid;
+	sdp_profile_desc_t profile;
+	sdp_list_t *aproto, *proto[2];
+	sdp_record_t *record;
+	sdp_data_t *channel;
+	uint8_t netid = 0x01;
+	sdp_data_t *network;
+	uint8_t ch = HSP_AG_CHANNEL;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	network = sdp_data_alloc(SDP_UINT8, &netid);
+	if (!network) {
+		sdp_record_free(record);
+		return NULL;
+	}
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(0, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID);
+	svclass_id = sdp_list_append(0, &svclass_uuid);
+	sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+	svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+	sdp_set_service_classes(record, svclass_id);
+
+	sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
+	profile.version = 0x0102;
+	pfseq = sdp_list_append(0, &profile);
+	sdp_set_profile_descs(record, pfseq);
+
+	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+	proto[0] = sdp_list_append(0, &l2cap_uuid);
+	apseq = sdp_list_append(0, proto[0]);
+
+	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+	proto[1] = sdp_list_append(0, &rfcomm_uuid);
+	channel = sdp_data_alloc(SDP_UINT8, &ch);
+	proto[1] = sdp_list_append(proto[1], channel);
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(0, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	sdp_set_info_attr(record, "Voice Gateway", 0, 0);
+
+	sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+	sdp_data_free(channel);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(pfseq, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(svclass_id, NULL);
+
+	return record;
+}
+
+static bool enable_hsp_ag(void)
+{
+	sdp_record_t *rec;
+	GError *err = NULL;
+
+	DBG("");
+
+	hsp_server =  bt_io_listen(NULL, confirm_cb, NULL, NULL, &err,
+					BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+					BT_IO_OPT_CHANNEL, HSP_AG_CHANNEL,
+					BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+					BT_IO_OPT_INVALID);
+	if (!hsp_server) {
+		error("Failed to listen on Headset rfcomm: %s", err->message);
+		g_error_free(err);
+		return false;
+	}
+
+	rec = headset_ag_record();
+	if (!rec) {
+		error("Failed to allocate Headset record");
+		goto failed;
+	}
+
+	if (bt_adapter_add_record(rec, 0) < 0) {
+		error("Failed to register Headset record");
+		sdp_record_free(rec);
+		goto failed;
+	}
+
+	hsp_record_id = rec->handle;
+
+	return true;
+
+failed:
+	g_io_channel_shutdown(hsp_server, TRUE, NULL);
+	g_io_channel_unref(hsp_server);
+	hsp_server = NULL;
+
+	return false;
+}
+
+static void cleanup_hsp_ag(void)
+{
+	if (hsp_server) {
+		g_io_channel_shutdown(hsp_server, TRUE, NULL);
+		g_io_channel_unref(hsp_server);
+		hsp_server = NULL;
+	}
+
+	if (hsp_record_id > 0) {
+		bt_adapter_remove_record(hsp_record_id);
+		hsp_record_id = 0;
+	}
+}
+
 static sdp_record_t *hfp_ag_record(void)
 {
 	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
@@ -597,9 +725,14 @@ bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
 
 	bacpy(&adapter_addr, addr);
 
-	if (!enable_hfp_ag())
+	if (!enable_hsp_ag())
 		return false;
 
+	if (!enable_hfp_ag()) {
+		cleanup_hsp_ag();
+		return false;
+	}
+
 	hal_ipc = ipc;
 	ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE, cmd_handlers,
 						G_N_ELEMENTS(cmd_handlers));
@@ -615,4 +748,5 @@ void bt_handsfree_unregister(void)
 	hal_ipc = NULL;
 
 	cleanup_hfp_ag();
+	cleanup_hsp_ag();
 }
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH v3 7/8] android/handsfree: Allow to connect to HSP or HFP handsfree unit
From: Szymon Janc @ 2014-03-02 23:50 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Szymon Janc
In-Reply-To: <1393804249-12392-1-git-send-email-szymon.janc@tieto.com>

This allows to connect to HSP (it HFP is not enabled) or fallback to
HSP if HFP is not supported by remote device.
---
 android/handsfree.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 106 insertions(+), 7 deletions(-)

diff --git a/android/handsfree.c b/android/handsfree.c
index 0b97a5d..6149c62 100644
--- a/android/handsfree.c
+++ b/android/handsfree.c
@@ -184,7 +184,7 @@ drop:
 	g_io_channel_shutdown(chan, TRUE, NULL);
 }
 
-static void sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
+static void sdp_hsp_search_cb(sdp_list_t *recs, int err, gpointer data)
 {
 	sdp_list_t *protos, *classes;
 	GError *gerr = NULL;
@@ -195,12 +195,13 @@ static void sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
 	DBG("");
 
 	if (err < 0) {
-		error("handsfree: unable to get SDP record: %s", strerror(-err));
+		error("handsfree: unable to get SDP record: %s",
+								strerror(-err));
 		goto fail;
 	}
 
 	if (!recs || !recs->data) {
-		error("handsfree: no SDP records found");
+		info("handsfree: no HSP SDP records found");
 		goto fail;
 	}
 
@@ -216,6 +217,94 @@ static void sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
 	}
 
 	/* TODO read remote version? */
+	/* TODO read volume control support */
+
+	memcpy(&uuid, classes->data, sizeof(uuid));
+	sdp_list_free(classes, free);
+
+	if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
+			uuid.value.uuid16 != HEADSET_SVCLASS_ID) {
+		sdp_list_free(protos, NULL);
+		error("handsfree: invalid service record or not HSP");
+		goto fail;
+	}
+
+	channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+	sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+	sdp_list_free(protos, NULL);
+	if (channel <= 0) {
+		error("handsfree: unable to get RFCOMM channel from record");
+		goto fail;
+	}
+
+	io = bt_io_connect(connect_cb, NULL, NULL, &gerr,
+				BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+				BT_IO_OPT_DEST_BDADDR, &device.bdaddr,
+				BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+				BT_IO_OPT_CHANNEL, channel,
+				BT_IO_OPT_INVALID);
+	if (!io) {
+		error("handsfree: unable to connect: %s", gerr->message);
+		g_error_free(gerr);
+		goto fail;
+	}
+
+	g_io_channel_unref(io);
+	return;
+
+fail:
+	device_cleanup();
+}
+
+static int sdp_search_hsp(void)
+{
+	uuid_t uuid;
+
+	sdp_uuid16_create(&uuid, HEADSET_SVCLASS_ID);
+
+	return bt_search_service(&adapter_addr, &device.bdaddr, &uuid,
+					sdp_hsp_search_cb, NULL, NULL, 0);
+}
+
+static void sdp_hfp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+	sdp_list_t *protos, *classes;
+	GError *gerr = NULL;
+	GIOChannel *io;
+	uuid_t uuid;
+	int channel;
+
+	DBG("");
+
+	if (err < 0) {
+		error("handsfree: unable to get SDP record: %s",
+								strerror(-err));
+		goto fail;
+	}
+
+	if (!recs || !recs->data) {
+		info("handsfree: no HFP SDP records found, trying HSP");
+
+		if (sdp_search_hsp() < 0) {
+			error("handsfree: HSP SDP search failed");
+			goto fail;
+		}
+
+		return;
+	}
+
+	if (sdp_get_service_classes(recs->data, &classes) < 0) {
+		error("handsfree: unable to get service classes from record");
+		goto fail;
+	}
+
+	if (sdp_get_access_protos(recs->data, &protos) < 0) {
+		error("handsfree: unable to get access protocols from record");
+		sdp_list_free(classes, free);
+		goto fail;
+	}
+
+	/* TODO read remote version? */
 
 	memcpy(&uuid, classes->data, sizeof(uuid));
 	sdp_list_free(classes, free);
@@ -254,13 +343,23 @@ fail:
 	device_cleanup();
 }
 
+static int sdp_search_hfp(void)
+{
+	uuid_t uuid;
+
+	sdp_uuid16_create(&uuid, HANDSFREE_SVCLASS_ID);
+
+	return bt_search_service(&adapter_addr, &device.bdaddr, &uuid,
+					sdp_hfp_search_cb, NULL, NULL, 0);
+}
+
 static void handle_connect(const void *buf, uint16_t len)
 {
 	const struct hal_cmd_handsfree_connect *cmd = buf;
 	char addr[18];
 	uint8_t status;
-	uuid_t uuid;
 	bdaddr_t bdaddr;
+	int ret;
 
 	DBG("");
 
@@ -276,9 +375,9 @@ static void handle_connect(const void *buf, uint16_t len)
 
 	device_init(&bdaddr);
 
-	sdp_uuid16_create(&uuid, HANDSFREE_SVCLASS_ID);
-	if (bt_search_service(&adapter_addr, &device.bdaddr, &uuid,
-					sdp_search_cb, NULL, NULL, 0) < 0) {
+	/* prefer HFP over HSP */
+	ret = hfp_server ? sdp_search_hfp() : sdp_search_hsp();
+	if (ret < 0) {
 		error("handsfree: SDP search failed");
 		device_cleanup();
 		status = HAL_STATUS_FAILED;
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH v3 8/8] android/handsfree: Add support for disabling HSP or HFP AGs
From: Szymon Janc @ 2014-03-02 23:50 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Szymon Janc
In-Reply-To: <1393804249-12392-1-git-send-email-szymon.janc@tieto.com>

This allows to tune what profiles are supported by handsfree HAL.
---
 android/cutils/properties.h |  8 ++++++++
 android/hal-handsfree.c     | 15 ++++++++++++++-
 android/hal-msg.h           |  2 ++
 android/handsfree.c         |  4 ++--
 4 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/android/cutils/properties.h b/android/cutils/properties.h
index dff812f..66a4a84 100644
--- a/android/cutils/properties.h
+++ b/android/cutils/properties.h
@@ -27,6 +27,14 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 
+#define PROPERTY_VALUE_MAX 32
+
+static inline int property_get(const char *key, char *value,
+						const char *default_value)
+{
+	return 0;
+}
+
 /* property_set: returns 0 on success, < 0 on failure
 */
 static inline int property_set(const char *key, const char *value)
diff --git a/android/hal-handsfree.c b/android/hal-handsfree.c
index 7a28e0f..4117ed0 100644
--- a/android/hal-handsfree.c
+++ b/android/hal-handsfree.c
@@ -20,6 +20,8 @@
 #include <string.h>
 #include <stdlib.h>
 
+#include <cutils/properties.h>
+
 #include "hal-log.h"
 #include "hal.h"
 #include "hal-msg.h"
@@ -196,6 +198,17 @@ static const struct hal_ipc_handler ev_handlers[] = {
 	{handle_hsp_key_press, false, 0},
 };
 
+static uint8_t get_mode(void)
+{
+	char value[PROPERTY_VALUE_MAX];
+
+	if (property_get("bluetooth.handsfree_mode", value, "") > 0 &&
+					(!strcasecmp(value, "hsp_only")))
+		return HAL_MODE_HANDSFREE_HSP_ONLY;
+
+	return HAL_MODE_DEFAULT;
+}
+
 static bt_status_t init(bthf_callbacks_t *callbacks)
 {
 	struct hal_cmd_register_module cmd;
@@ -212,7 +225,7 @@ static bt_status_t init(bthf_callbacks_t *callbacks)
 				sizeof(ev_handlers)/sizeof(ev_handlers[0]));
 
 	cmd.service_id = HAL_SERVICE_ID_HANDSFREE;
-	cmd.mode = HAL_MODE_DEFAULT;
+	cmd.mode = get_mode();
 
 	ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
 					sizeof(cmd), &cmd, 0, NULL, NULL);
diff --git a/android/hal-msg.h b/android/hal-msg.h
index 8eb97c3..dd25f6e 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -385,6 +385,8 @@ struct hal_cmd_pan_disconnect {
 
 /* Handsfree HAL API */
 
+#define HAL_MODE_HANDSFREE_HSP_ONLY		0x01
+
 #define HAL_OP_HANDSFREE_CONNECT		0x01
 struct hal_cmd_handsfree_connect {
 	uint8_t bdaddr[6];
diff --git a/android/handsfree.c b/android/handsfree.c
index 6149c62..4f69e68 100644
--- a/android/handsfree.c
+++ b/android/handsfree.c
@@ -820,14 +820,14 @@ static void cleanup_hfp_ag(void)
 
 bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
 {
-	DBG("");
+	DBG("mode 0x%x", mode);
 
 	bacpy(&adapter_addr, addr);
 
 	if (!enable_hsp_ag())
 		return false;
 
-	if (!enable_hfp_ag()) {
+	if (mode != HAL_MODE_HANDSFREE_HSP_ONLY && !enable_hfp_ag()) {
 		cleanup_hsp_ag();
 		return false;
 	}
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH] android/pts: Correct PICS and PTS tests
From: Sebastian Chlad @ 2014-03-03  6:48 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Sebastian Chlad

Adjusting some optional PICS settings effecting in disabling some
PTS test cases.
---
 android/pics-pan.txt |  4 ++--
 android/pts-pan.txt  | 29 ++++++++++++++++++-----------
 2 files changed, 20 insertions(+), 13 deletions(-)

diff --git a/android/pics-pan.txt b/android/pics-pan.txt
index 16457bf..21c16b2 100644
--- a/android/pics-pan.txt
+++ b/android/pics-pan.txt
@@ -121,8 +121,8 @@ Parameter Name	Selected	Description
 -------------------------------------------------------------------------------
 TSPC_PAN_4_1	True (#)	PANU: Support BNEP (M)
 TSPC_PAN_4_2	True (*#)	PANU: Support IPv4 (C.1)
-TSPC_PAN_4_3	True (*#)	PANU: Support ping client for IPv4 (O)
-TSPC_PAN_4_4	True (*#)	PANU: Support DHCP client for  IPv4 (O)
+TSPC_PAN_4_3	False		PANU: Support ping client for IPv4 (O)
+TSPC_PAN_4_4	False		PANU: Support DHCP client for  IPv4 (O)
 TSPC_PAN_4_5	False		PANU: Support DNS/LLMNR Resolver for IPv4 (O)
 TSPC_PAN_4_5a	True (#)	PANU: Support LLMNR Sender for IPv4 (C.2)
 TSPC_PAN_4_5b	False		PANU: Support LLMNR Responder for IPv4 (O)
diff --git a/android/pts-pan.txt b/android/pts-pan.txt
index 8e8ae41..37c78fc 100644
--- a/android/pts-pan.txt
+++ b/android/pts-pan.txt
@@ -10,9 +10,9 @@ FAIL	test failed
 INC	test is inconclusive
 N/A	test is disabled due to PICS setup
 
--------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------
 Test Name				Result	Notes
--------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------
 TC_BNEP_GN_BROADCAST_0_BV_03_C		N/A
 TC_GN_Ipv4_Autonet_BV_01_I		N/A
 TC_GN_Ipv6_Autonet_BV_02_I		N/A
@@ -34,12 +34,15 @@ TC_BNEP_NAP_FORWARD_UNICAST_BV_05_C	N/A
 TC_BNEP_NAP_FORWARD_UNICAST_BV_06_C	N/A
 TC_BNEP_NAP_MULTICAST_0_BV_03_C		N/A
 TC_BNEP_NAP_MULTICAST_0_BV_04_C		N/A
-TC_BNEP_BRIDGE_RX_BV_02_I		INC
+TC_BNEP_BRIDGE_RX_BV_02_I		INC	Tester needs to send one forward-unicast
+							BNEP packet with given data (5 times)
+							0x00, 0x01, 0xFF followed by
+							0x00, 0x01, 0x9E, 0x9F
 TC_BNEP_BRIDGE_TX_BV_01_I		PASS
 TC_NAP_Ipv4_Autonet_BV_01_I		N/A
 TC_NAP_Ipv6_Autonet_BV_02_I		N/A
 TC_NAP_IP_DHCP_BV_03_I			N/A
-TC_NAP_IP_LLMNR_BV_01_I			INC
+TC_NAP_IP_LLMNR_BV_01_I			INC	From IUT: send LLMNR request command
 TC_NAP_IP_LLMNR_BV_02_I			N/A
 TC_NAP_IP_DNS_BV_01_I			N/A
 TC_NAP_IP_APP_BV_01_I			N/A
@@ -51,20 +54,24 @@ TC_SDP_NAP_BV_01_C			PASS
 TC_MISC_NAP_UUID_BV_01_C		PASS
 TC_MISC_NAP_UUID_BV_02_C		PASS
 TC_BNEP_PANU_BROADCAST_0_BV_04_C	N/A
-TC_PANU_Ipv4_Autonet_BV_01_I		INC
+TC_PANU_Ipv4_Autonet_BV_01_I		INC	When prompted connect to PTS. From IUT:
+							issue ARP request
 TC_PANU_Ipv6_Autonet_BV_02_I		N/A
-TC_PANU_IP_LLMNR_BV_01_I		INC
+TC_PANU_IP_LLMNR_BV_01_I		INC	When prompted connect to PTS. From IUT:
+							send LLMNR request command
 TC_PANU_IP_LLMNR_BV_02_I		N/A
-TC_PANU_IP_DHCP_BV_03_I			PASS
+TC_PANU_IP_DHCP_BV_03_I			N/A
 TC_PANU_IP_DNS_BV_01_I			N/A
 TC_PANU_IP_APP_BV_01_I			N/A
 TC_PANU_IP_APP_BV_02_I			N/A
-TC_PANU_IP_APP_BV_03_I			INC
-TC_PANU_IP_APP_BV_04_I			INC
-TC_PANU_IP_APP_BV_05_I			PASS
+TC_PANU_IP_APP_BV_03_I			N/A
+TC_PANU_IP_APP_BV_04_I			N/A
+TC_PANU_IP_APP_BV_05_I			PASS	When prompted, connect to PTS and then
+							when prompted terminate connection
+							(IUT side)
 TC_SDP_PANU_BV_01_C			N/A
 TC_MISC_PANU_UUID_BV_01_C		N/A
 TC_MISC_PANU_UUID_BV_02_C		N/A
 TC_MISC_ROLE_BV_01_C			N/A
 TC_MISC_ROLE_BV_BV_02_C			N/A
--------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------
-- 
1.8.5.3


^ permalink raw reply related

* Re: [PATCH BlueZ] core: retry connect_dbus() several times
From: Adam Lee @ 2014-03-03  7:15 UTC (permalink / raw)
  To: Luiz Augusto von Dentz
  Cc: Anderson Lizardo, Marcel Holtmann, BlueZ development
In-Reply-To: <CABBYNZLP8oPywHhMDK1D9FATFi=gMAgdfde+SjwzCp=VSGy05A@mail.gmail.com>

On Fri, Feb 28, 2014 at 03:10:41PM +0100, Luiz Augusto von Dentz wrote:
> Hi Adam,
> 
> On Fri, Feb 28, 2014 at 1:22 PM, Anderson Lizardo
> <anderson.lizardo@openbossa.org> wrote:
> > Hi Adam,
> >
> > On Fri, Feb 28, 2014 at 3:31 AM, Adam Lee <adam8157@gmail.com> wrote:
> >> connect_dbus() fails sometimes with Intel 7260 bluetooth, sleeping 0.5s
> >> before executing bluetoothd workarounds it. I assume the firmware
> >> patching slows down hardware initialization then fails to connect.
> >>
> >> Any other possibility? I'm not a D-Bus guru :)
> >
> > Please provide more details, e.g. the bluetoothd logs showing the
> > problems, btmon output and which steps you are using to reproduce it.
> > If you are using bluetoothctl, tell us which commands you are using.
> 
> My guess is that either your driver is not using .setup hook to do the
> firmware patching or it is returning before it has fully initialize
> the hardware, so please also add more information what is happening on
> the driver side.

Hi, all

Please check https://bugs.launchpad.net/bugs/1221618

It's already fixed in btusb. However, if it's related to initialization
as Luiz and I guessed, maybe other hardwares have this issue too, that's
the reason I submitted that harmless retry "workaround" into BlueZ.
(have no objection to NAK)

-- 
Adam Lee
http://adam8157.info

^ permalink raw reply

* Re: [PATCH 1/5] android/hal-audio: Add open/close_endpoint helpers
From: Andrzej Kaczmarek @ 2014-03-03  8:32 UTC (permalink / raw)
  To: Szymon Janc; +Cc: linux-bluetooth
In-Reply-To: <1913683.ZfTpeeEFIY@leonov>

Hi Szymon,

On 1 March 2014 12:51, Szymon Janc <szymon.janc@tieto.com> wrote:
> Hi Andrzej,
>
> On Friday 28 of February 2014 19:01:16 Andrzej Kaczmarek wrote:

<snip>

>> +     }
>> +
>> +     if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
>> +             error("fcntl(F_SETFL): %s (%d)", strerror(errno), errno);
>> +             return -errno;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static bool open_endpoint(struct audio_endpoint *ep,
>> +                                             struct audio_input_config *cfg)
>> +{
>> +     struct audio_preset *preset;
>> +     const struct audio_codec *codec;
>> +     uint16_t mtu;
>> +     int fd;
>> +
>> +     if (ipc_open_stream_cmd(ep->id, &mtu, &fd, &preset) !=
>> +                                                     AUDIO_STATUS_SUCCESS)
>> +             return false;
>> +
>> +     if (!preset || fd < 0)
>
> If this can happen despite ipc_open_stream_cmd() returned success then freeing
> preset or closing fd should be handled here.

I'll remove this check since ipc_open_stream_cmd guarantees that both
preset and fd are valid on success.

BR,
Andrzej

^ permalink raw reply

* [PATCH 1/7] shared/hfp: Fix not freeing cmd_handler prefix
From: Marcin Kraglak @ 2014-03-03  8:36 UTC (permalink / raw)
  To: linux-bluetooth

It should be freed while destroy.
---
 src/shared/hfp.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/shared/hfp.c b/src/shared/hfp.c
index b10316e..41973b7 100644
--- a/src/shared/hfp.c
+++ b/src/shared/hfp.c
@@ -82,6 +82,8 @@ static void destroy_cmd_handler(void *data)
 	if (handler->destroy)
 		handler->destroy(handler->user_data);
 
+	free(handler->prefix);
+
 	free(handler);
 }
 
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH 2/7] unit: Initial version of test-hfp
From: Marcin Kraglak @ 2014-03-03  8:36 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1393835812-24238-1-git-send-email-marcin.kraglak@tieto.com>

This is skeleton of hfp unit tests with basic init test
for hfp_gw_new().
Basic test with hfp_gw_new() and hfp_gw_unref.
---
 .gitignore      |   1 +
 Makefile.am     |  12 ++++++
 unit/test-hfp.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 134 insertions(+)
 create mode 100644 unit/test-hfp.c

diff --git a/.gitignore b/.gitignore
index 94c0c78..6719be8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -84,6 +84,7 @@ unit/test-gdbus-client
 unit/test-sdp
 unit/test-lib
 unit/test-mgmt
+unit/test-hfp
 tools/mgmt-tester
 tools/smp-tester
 tools/gap-tester
diff --git a/Makefile.am b/Makefile.am
index 0c79e58..0107cef 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -293,6 +293,18 @@ unit_test_avrcp_SOURCES = unit/test-avrcp.c \
 				android/avrcp-lib.c android/avrcp-lib.h
 unit_test_avrcp_LDADD = @GLIB_LIBS@ lib/libbluetooth-internal.la
 
+unit_tests += unit/test-hfp
+
+unit_test_hfp_SOURCES = unit/test-hfp.c \
+				src/shared/io.h src/shared/io-glib.c \
+				src/shared/queue.h src/shared/queue.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/mgmt.h src/shared/mgmt.c \
+				src/shared/ringbuf.h src/shared/ringbuf.c \
+				src/shared/hfp.h src/shared/hfp.c
+
+unit_test_hfp_LDADD = @GLIB_LIBS@
+
 unit_tests += unit/test-gdbus-client
 
 unit_test_gdbus_client_SOURCES = unit/test-gdbus-client.c
diff --git a/unit/test-hfp.c b/unit/test-hfp.c
new file mode 100644
index 0000000..092f255
--- /dev/null
+++ b/unit/test-hfp.c
@@ -0,0 +1,121 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+#include "src/shared/hfp.h"
+
+struct context {
+	GMainLoop *main_loop;
+	guint watch_id;
+	int fd_server;
+	int fd_client;
+	struct hfp_gw *hfp;
+	const struct test_data *data;
+};
+
+static void context_quit(struct context *context)
+{
+	g_main_loop_quit(context->main_loop);
+}
+
+static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+
+	context_quit(context);
+
+	return FALSE;
+}
+
+static struct context *create_context(gconstpointer data)
+{
+	struct context *context = g_new0(struct context, 1);
+	GIOChannel *channel;
+	int err, sv[2];
+
+	context->main_loop = g_main_loop_new(NULL, FALSE);
+	g_assert(context->main_loop);
+
+	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+	g_assert(err == 0);
+
+	channel = g_io_channel_unix_new(sv[1]);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	context->watch_id = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				test_handler, context);
+	g_assert(context->watch_id > 0);
+
+	g_io_channel_unref(channel);
+
+	context->fd_server = sv[1];
+	context->fd_client = sv[0];
+	context->data = data;
+
+	return context;
+}
+
+static void execute_context(struct context *context)
+{
+	g_main_loop_run(context->main_loop);
+
+	g_source_remove(context->watch_id);
+
+	g_main_loop_unref(context->main_loop);
+
+	g_free(context);
+}
+
+static void test_init(gconstpointer data)
+{
+	struct context *context = create_context(data);
+
+	context->hfp = hfp_gw_new(context->fd_client);
+
+	g_assert(context->hfp);
+	g_assert(hfp_gw_set_close_on_unref(context->hfp, true));
+
+	hfp_gw_unref(context->hfp);
+
+	execute_context(context);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_data_func("/hfp/test_init", NULL, test_init);
+
+	return g_test_run();
+}
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH 3/7] unit/test-hfp: Add /hfp/test_cmd_handler_1
From: Marcin Kraglak @ 2014-03-03  8:36 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1393835812-24238-1-git-send-email-marcin.kraglak@tieto.com>

---
 unit/test-hfp.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 76 insertions(+)

diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index 092f255..8cbb524 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -39,6 +39,14 @@ struct context {
 	const struct test_data *data;
 };
 
+struct test_data {
+	hfp_command_func_t command_cb;
+	const char *server_send;
+	int data_len;
+	const char *expected_rsp;
+	int expected_rsp_len;
+};
+
 static void context_quit(struct context *context)
 {
 	g_main_loop_quit(context->main_loop);
@@ -49,11 +57,35 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
 {
 	struct context *context = user_data;
 
+	if (context->data && context->data->expected_rsp) {
+		int fd = g_io_channel_unix_get_fd(channel);
+		char buf[1024];
+		int len;
+
+		len = read(fd, buf, sizeof(buf));
+		g_assert(context->data->expected_rsp_len == len);
+		g_assert(!memcmp(context->data->expected_rsp, buf, len));
+	}
+
 	context_quit(context);
 
 	return FALSE;
 }
 
+static void cmd_handler(const char *command, void *user_data)
+{
+	struct context *context = user_data;
+	int cmd_len;
+
+	cmd_len = strlen(command);
+
+	g_assert(cmd_len < context->data->data_len);
+
+	g_assert(!memcmp(command, context->data->server_send, cmd_len));
+
+	hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
+}
+
 static struct context *create_context(gconstpointer data)
 {
 	struct context *context = g_new0(struct context, 1);
@@ -94,6 +126,9 @@ static void execute_context(struct context *context)
 
 	g_main_loop_unref(context->main_loop);
 
+	if (context->hfp)
+		hfp_gw_unref(context->hfp);
+
 	g_free(context);
 }
 
@@ -107,15 +142,56 @@ static void test_init(gconstpointer data)
 	g_assert(hfp_gw_set_close_on_unref(context->hfp, true));
 
 	hfp_gw_unref(context->hfp);
+	context->hfp = NULL;
+
+	execute_context(context);
+}
+
+static void test_command_handler(gconstpointer data)
+{
+	struct context *context = create_context(data);
+
+	context->hfp = hfp_gw_new(context->fd_client);
+
+	g_assert(context->hfp);
+	g_assert(hfp_gw_set_close_on_unref(context->hfp, true));
+
+	if (context->data->command_cb)
+		g_assert(hfp_gw_set_command_handler(context->hfp,
+						context->data->command_cb,
+						context, NULL));
+
+	if (context->data->server_send) {
+		ssize_t len;
+		len = write(context->fd_server, context->data->server_send,
+						context->data->data_len);
+
+		g_assert(len);
+	}
 
 	execute_context(context);
 }
 
+static const char err_rsp[] = {'\r', '\n', 'E', 'R', 'R', 'O', 'R', '\r', '\n'};
+
+static const char atbrsf[] = {'A', 'T', '+', 'B', 'R', 'S', 'F', '\r'};
+
+static const struct test_data test_cmd_handler_1 = {
+	.command_cb = cmd_handler,
+	.server_send = atbrsf,
+	.data_len = sizeof(atbrsf),
+	.expected_rsp = err_rsp,
+	.expected_rsp_len = sizeof(err_rsp)
+};
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
 
 	g_test_add_data_func("/hfp/test_init", NULL, test_init);
 
+	g_test_add_data_func("/hfp/test_cmd_handler_1", &test_cmd_handler_1,
+							test_command_handler);
+
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH 4/7] unit/test-hfp: Add /hfp/test_cmd_handler_2
From: Marcin Kraglak @ 2014-03-03  8:36 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1393835812-24238-1-git-send-email-marcin.kraglak@tieto.com>

---
 unit/test-hfp.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index 8cbb524..ef49b04 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -184,6 +184,16 @@ static const struct test_data test_cmd_handler_1 = {
 	.expected_rsp_len = sizeof(err_rsp)
 };
 
+static const char atd[] = {'A', 'T', 'D', '1', '2', '3', '4', '\r'};
+
+static const struct test_data test_cmd_handler_2 = {
+	.command_cb = cmd_handler,
+	.server_send = atd,
+	.data_len = sizeof(atd),
+	.expected_rsp = err_rsp,
+	.expected_rsp_len = sizeof(err_rsp)
+};
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -192,6 +202,8 @@ int main(int argc, char *argv[])
 
 	g_test_add_data_func("/hfp/test_cmd_handler_1", &test_cmd_handler_1,
 							test_command_handler);
+	g_test_add_data_func("/hfp/test_cmd_handler_2", &test_cmd_handler_2,
+							test_command_handler);
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH 5/7] unit/test-hfp: Add /hfp/test_hfp_gw_register_1
From: Marcin Kraglak @ 2014-03-03  8:36 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1393835812-24238-1-git-send-email-marcin.kraglak@tieto.com>

---
 unit/test-hfp.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index ef49b04..cbc3a12 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -41,6 +41,9 @@ struct context {
 
 struct test_data {
 	hfp_command_func_t command_cb;
+	hfp_result_func_t result_cb;
+	const char *prefix;
+	enum hfp_gw_cmd_type expected_cmd_type;
 	const char *server_send;
 	int data_len;
 	const char *expected_rsp;
@@ -86,6 +89,16 @@ static void cmd_handler(const char *command, void *user_data)
 	hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
 }
 
+static void prefix_handler(struct hfp_gw_result *result,
+				enum hfp_gw_cmd_type type, void *user_data)
+{
+	struct context *context = user_data;
+
+	g_assert(type == context->data->expected_cmd_type);
+
+	hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
+}
+
 static struct context *create_context(gconstpointer data)
 {
 	struct context *context = g_new0(struct context, 1);
@@ -172,6 +185,37 @@ static void test_command_handler(gconstpointer data)
 	execute_context(context);
 }
 
+static void test_register(gconstpointer data)
+{
+	struct context *context = create_context(data);
+
+	context->hfp = hfp_gw_new(context->fd_client);
+
+	g_assert(context->hfp);
+	g_assert(hfp_gw_set_close_on_unref(context->hfp, true));
+
+	if (context->data->command_cb)
+		g_assert(hfp_gw_set_command_handler(context->hfp,
+						context->data->command_cb,
+						context, NULL));
+
+	if (context->data->result_cb)
+		g_assert(hfp_gw_register(context->hfp,
+						context->data->result_cb,
+						context->data->prefix,
+						context, NULL));
+
+	if (context->data->data_len) {
+		ssize_t len;
+		len = write(context->fd_server, context->data->server_send,
+						context->data->data_len);
+
+		g_assert(len);
+	}
+
+	execute_context(context);
+}
+
 static const char err_rsp[] = {'\r', '\n', 'E', 'R', 'R', 'O', 'R', '\r', '\n'};
 
 static const char atbrsf[] = {'A', 'T', '+', 'B', 'R', 'S', 'F', '\r'};
@@ -194,6 +238,20 @@ static const struct test_data test_cmd_handler_2 = {
 	.expected_rsp_len = sizeof(err_rsp)
 };
 
+static const char prefix_brsf[] = "+BRSF";
+static const char atbrsf_set[] = {'A', 'T', '+', 'B', 'R', 'S', 'F', '=', '\r'};
+
+static const struct test_data test_register_1 = {
+	.command_cb = cmd_handler,
+	.server_send = atbrsf_set,
+	.data_len = sizeof(atbrsf_set),
+	.expected_rsp = err_rsp,
+	.expected_rsp_len = sizeof(err_rsp),
+	.result_cb = prefix_handler,
+	.expected_cmd_type = HFP_GW_CMD_TYPE_SET,
+	.prefix = prefix_brsf
+};
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -205,5 +263,8 @@ int main(int argc, char *argv[])
 	g_test_add_data_func("/hfp/test_cmd_handler_2", &test_cmd_handler_2,
 							test_command_handler);
 
+	g_test_add_data_func("/hfp/test_hfp_gw_register_1", &test_register_1,
+							test_register);
+
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH 6/7] unit/test-hfp: Add /hfp/test_hfp_gw_register_2
From: Marcin Kraglak @ 2014-03-03  8:36 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1393835812-24238-1-git-send-email-marcin.kraglak@tieto.com>

---
 unit/test-hfp.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index cbc3a12..5e4fc11 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -252,6 +252,20 @@ static const struct test_data test_register_1 = {
 	.prefix = prefix_brsf
 };
 
+static const char atbrsf_test[] = {'A', 'T', '+', 'B', 'R', 'S', 'F', '=', '?',
+									'\r'};
+
+static const struct test_data test_register_2 = {
+	.command_cb = cmd_handler,
+	.server_send = atbrsf_test,
+	.data_len = sizeof(atbrsf_test),
+	.expected_rsp = err_rsp,
+	.expected_rsp_len = sizeof(err_rsp),
+	.result_cb = prefix_handler,
+	.expected_cmd_type = HFP_GW_CMD_TYPE_TEST,
+	.prefix = prefix_brsf
+};
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -265,6 +279,8 @@ int main(int argc, char *argv[])
 
 	g_test_add_data_func("/hfp/test_hfp_gw_register_1", &test_register_1,
 							test_register);
+	g_test_add_data_func("/hfp/test_hfp_gw_register_2", &test_register_2,
+							test_register);
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH 7/7] Add /hfp/test_hfp_gw_register_3 and /hfp/test_hfp_gw_register_4
From: Marcin Kraglak @ 2014-03-03  8:36 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1393835812-24238-1-git-send-email-marcin.kraglak@tieto.com>

---
 unit/test-hfp.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index 5e4fc11..64ecc60 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -266,6 +266,33 @@ static const struct test_data test_register_2 = {
 	.prefix = prefix_brsf
 };
 
+static const char atbrsf_read[] = {'A', 'T', '+', 'B', 'R', 'S', 'F', '?',
+									'\r'};
+
+static const struct test_data test_register_3 = {
+	.command_cb = cmd_handler,
+	.server_send = atbrsf_read,
+	.data_len = sizeof(atbrsf_read),
+	.expected_rsp = err_rsp,
+	.expected_rsp_len = sizeof(err_rsp),
+	.result_cb = prefix_handler,
+	.expected_cmd_type = HFP_GW_CMD_TYPE_READ,
+	.prefix = prefix_brsf
+};
+
+static const char prefix_d[] = "D";
+
+static const struct test_data test_register_4 = {
+	.command_cb = cmd_handler,
+	.server_send = atd,
+	.data_len = sizeof(atd),
+	.expected_rsp = err_rsp,
+	.expected_rsp_len = sizeof(err_rsp),
+	.result_cb = prefix_handler,
+	.expected_cmd_type = HFP_GW_CMD_TYPE_SET,
+	.prefix = prefix_d
+};
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -281,6 +308,10 @@ int main(int argc, char *argv[])
 							test_register);
 	g_test_add_data_func("/hfp/test_hfp_gw_register_2", &test_register_2,
 							test_register);
+	g_test_add_data_func("/hfp/test_hfp_gw_register_3", &test_register_3,
+							test_register);
+	g_test_add_data_func("/hfp/test_hfp_gw_register_4", &test_register_4,
+							test_register);
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH v2 1/5] android/hal-audio: Add open/close_endpoint helpers
From: Andrzej Kaczmarek @ 2014-03-03  8:56 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek

---
 android/hal-audio.c | 117 +++++++++++++++++++++++++++++-----------------------
 1 file changed, 66 insertions(+), 51 deletions(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 79a74b8..f6bc998 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -914,6 +914,70 @@ static void unregister_endpoints(void)
 	}
 }
 
+static int set_blocking(int fd)
+{
+	int flags;
+
+	flags = fcntl(fd, F_GETFL, 0);
+	if (flags < 0) {
+		int err = -errno;
+		error("fcntl(F_GETFL): %s (%d)", strerror(-err), -err);
+		return err;
+	}
+
+	if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
+		int err = -errno;
+		error("fcntl(F_SETFL): %s (%d)", strerror(-err), -err);
+		return err;
+	}
+
+	return 0;
+}
+
+static bool open_endpoint(struct audio_endpoint *ep,
+						struct audio_input_config *cfg)
+{
+	struct audio_preset *preset;
+	const struct audio_codec *codec;
+	uint16_t mtu;
+	int fd;
+
+	if (ipc_open_stream_cmd(ep->id, &mtu, &fd, &preset) !=
+							AUDIO_STATUS_SUCCESS)
+		return false;
+
+	if (set_blocking(fd) < 0)
+		goto failed;
+
+	ep->fd = fd;
+
+	codec = ep->codec;
+	codec->init(preset, mtu, &ep->codec_data);
+	codec->get_config(ep->codec_data, cfg);
+
+	free(preset);
+
+	return true;
+
+failed:
+	close(fd);
+	free(preset);
+
+	return false;
+}
+
+static void close_endpoint(struct audio_endpoint *ep)
+{
+	ipc_close_stream_cmd(ep->id);
+	if (ep->fd >= 0) {
+		close(ep->fd);
+		ep->fd = -1;
+	}
+
+	ep->codec->cleanup(ep->codec_data);
+	ep->codec_data = NULL;
+}
+
 static void downmix_to_mono(struct a2dp_stream_out *out, const uint8_t *buffer,
 								size_t bytes)
 {
@@ -1260,26 +1324,6 @@ static int in_remove_audio_effect(const struct audio_stream *stream,
 	return -ENOSYS;
 }
 
-static int set_blocking(int fd)
-{
-	int flags;
-
-	flags = fcntl(fd, F_GETFL, 0);
-	if (flags < 0) {
-		int err = -errno;
-		error("fcntl(F_GETFL): %s (%d)", strerror(-err), -err);
-		return err;
-	}
-
-	if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
-		int err = -errno;
-		error("fcntl(F_SETFL): %s (%d)", strerror(-err), -err);
-		return err;
-	}
-
-	return 0;
-}
-
 static int audio_open_output_stream(struct audio_hw_device *dev,
 					audio_io_handle_t handle,
 					audio_devices_t devices,
@@ -1290,10 +1334,6 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
 {
 	struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
 	struct a2dp_stream_out *out;
-	struct audio_preset *preset;
-	const struct audio_codec *codec;
-	uint16_t mtu;
-	int fd;
 
 	out = calloc(1, sizeof(struct a2dp_stream_out));
 	if (!out)
@@ -1321,29 +1361,12 @@ static int audio_open_output_stream(struct audio_hw_device *dev,
 	/* TODO: for now we always use endpoint 0 */
 	out->ep = &audio_endpoints[0];
 
-	if (ipc_open_stream_cmd(out->ep->id, &mtu, &fd, &preset) !=
-							AUDIO_STATUS_SUCCESS)
-		goto fail;
-
-	if (!preset || fd < 0)
-		goto fail;
-
-	if (set_blocking(fd) < 0) {
-		free(preset);
+	if (!open_endpoint(out->ep, &out->cfg))
 		goto fail;
-	}
-
-	out->ep->fd = fd;
-	codec = out->ep->codec;
-
-	codec->init(preset, mtu, &out->ep->codec_data);
-	codec->get_config(out->ep->codec_data, &out->cfg);
 
 	DBG("rate=%d channels=%d format=%d", out->cfg.rate,
 					out->cfg.channels, out->cfg.format);
 
-	free(preset);
-
 	if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) {
 		out->downmix_buf = malloc(FIXED_BUFFER_SIZE / 2);
 		if (!out->downmix_buf)
@@ -1369,18 +1392,10 @@ static void audio_close_output_stream(struct audio_hw_device *dev,
 {
 	struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
 	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
-	struct audio_endpoint *ep = a2dp_dev->out->ep;
 
 	DBG("");
 
-	ipc_close_stream_cmd(ep->id);
-	if (ep->fd >= 0) {
-		close(ep->fd);
-		ep->fd = -1;
-	}
-
-	ep->codec->cleanup(ep->codec_data);
-	ep->codec_data = NULL;
+	close_endpoint(a2dp_dev->out->ep);
 
 	free(out->downmix_buf);
 
-- 
1.8.5.4


^ permalink raw reply related

* [PATCH v2 2/5] android/hal-audio: Add encode_mediapacket function
From: Andrzej Kaczmarek @ 2014-03-03  8:56 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1393837002-29703-1-git-send-email-andrzej.kaczmarek@tieto.com>

This patch moves code which encodes data for media packet into single
function. It will simply try to put as much encoded frames as will fit
in provided media packet buffer. This is first step to make common code
responsible for media stream write and synchronizarion instead of codec
abstraction code as it is handled now.
---
 android/hal-audio.c | 102 +++++++++++++++++++++++++++++-----------------------
 1 file changed, 57 insertions(+), 45 deletions(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index f6bc998..cf49514 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -129,6 +129,7 @@ struct sbc_data {
 	size_t in_frame_len;
 	size_t in_buf_size;
 
+	size_t out_frame_len;
 	size_t out_buf_size;
 	uint8_t *out_buf;
 
@@ -417,6 +418,7 @@ static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu,
 	sbc_data->in_frame_len = in_frame_len;
 	sbc_data->in_buf_size = num_frames * in_frame_len;
 
+	sbc_data->out_frame_len = out_frame_len;
 	sbc_data->out_buf_size = hdr_len + num_frames * out_frame_len;
 	sbc_data->out_buf = calloc(1, sbc_data->out_buf_size);
 
@@ -536,75 +538,85 @@ static int write_media_packet(int fd, struct sbc_data *sbc_data,
 	return ret;
 }
 
+static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
+					size_t len, struct media_packet *mp,
+					size_t mp_data_len, size_t *written)
+{
+	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+	size_t consumed = 0;
+	size_t encoded = 0;
+	uint8_t frame_count = 0;
+
+	while (len - consumed >= sbc_data->in_frame_len &&
+			mp_data_len - encoded >= sbc_data->out_frame_len &&
+			frame_count < MAX_FRAMES_IN_PAYLOAD) {
+		ssize_t read;
+		ssize_t written = 0;
+
+		read = sbc_encode(&sbc_data->enc, buffer + consumed,
+				sbc_data->in_frame_len, mp->data + encoded,
+				mp_data_len - encoded, &written);
+
+		if (read < 0) {
+			error("SBC: failed to encode block at frame %d (%zd)",
+							frame_count, read);
+			break;
+		}
+
+		frame_count++;
+		consumed += read;
+		encoded += written;
+	}
+
+	*written = encoded;
+	mp->payload.frame_count = frame_count;
+
+	return consumed;
+}
+
 static ssize_t sbc_write_data(void *codec_data, const void *buffer,
 							size_t bytes, int fd)
 {
 	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
 	size_t consumed = 0;
-	size_t encoded = 0;
 	struct media_packet *mp = (struct media_packet *) sbc_data->out_buf;
 	size_t free_space = sbc_data->out_buf_size - sizeof(*mp);
-	int ret;
-	ssize_t bytes_read;
 
 	mp->hdr.v = 2;
 	mp->hdr.pt = 1;
 	mp->hdr.ssrc = htonl(1);
-	mp->hdr.timestamp = htonl(sbc_data->timestamp);
-	mp->payload.frame_count = 0;
 
-	while (bytes - consumed >= sbc_data->in_frame_len) {
-		ssize_t written = 0;
+	while (consumed < bytes) {
+		size_t written = 0;
+		ssize_t read;
+		int ret;
 
-		bytes_read = sbc_encode(&sbc_data->enc, buffer + consumed,
-					sbc_data->in_frame_len,
-					mp->data + encoded, free_space,
-					&written);
+		read = sbc_encode_mediapacket(codec_data, buffer + consumed,
+						bytes - consumed, mp,
+						free_space,
+						&written);
 
-		if (bytes_read < 0) {
-			error("SBC: failed to encode block (%zd)", bytes_read);
-			break;
+		if (read <= 0) {
+			error("sbc_encode_mediapacket failed (%zd)", read);
+			goto done;
 		}
 
-		mp->payload.frame_count++;
+		consumed += read;
 
-		consumed += bytes_read;
-		encoded += written;
-		free_space -= written;
+		mp->hdr.sequence_number = htons(sbc_data->seq++);
+		mp->hdr.timestamp = htonl(sbc_data->timestamp);
 
 		/* AudioFlinger provides PCM 16bit stereo only, thus sample size
 		 * is always 4 bytes
 		 */
-		sbc_data->timestamp += (bytes_read / 4);
+		sbc_data->timestamp += (read / 4);
 
-		/* write data if we either filled media packed or encoded all
-		 * input data
-		 */
-		if (mp->payload.frame_count == sbc_data->frames_per_packet ||
-				bytes == consumed ||
-				mp->payload.frame_count ==
-							MAX_FRAMES_IN_PAYLOAD) {
-			mp->hdr.sequence_number = htons(sbc_data->seq++);
-
-			ret = write_media_packet(fd, sbc_data, mp, encoded);
-			if (ret < 0)
-				return ret;
-
-			encoded = 0;
-			free_space = sbc_data->out_buf_size - sizeof(*mp);
-			mp->hdr.timestamp = htonl(sbc_data->timestamp);
-			mp->payload.frame_count = 0;
-		}
-	}
-
-	if (consumed != bytes) {
-		/* we should encode all input data
-		 * if we did not, something went wrong but we can't really
-		 * handle this so this is just sanity check
-		 */
-		error("SBC: failed to encode complete input buffer");
+		ret = write_media_packet(fd, sbc_data, mp, written);
+		if (ret < 0)
+			return ret;
 	}
 
+done:
 	/* we always assume that all data was processed and sent */
 	return bytes;
 }
-- 
1.8.5.4


^ permalink raw reply related

* [PATCH v2 3/5] android/hal-audio: Write and sync in common code
From: Andrzej Kaczmarek @ 2014-03-03  8:56 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1393837002-29703-1-git-send-email-andrzej.kaczmarek@tieto.com>

There's no need for codec abstraction to take care of writing and
synchronizing actual media stream since this is something that common
code can do regardless of codec used.

Data are synchronized based on number of samples consumed from input
stream instead of frames encoded to output stream which is also more
reliable since it's not affected by encoder errors.
---
 android/hal-audio.c | 188 +++++++++++++++++++++++++---------------------------
 1 file changed, 91 insertions(+), 97 deletions(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index cf49514..78f89bd 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -130,17 +130,9 @@ struct sbc_data {
 	size_t in_buf_size;
 
 	size_t out_frame_len;
-	size_t out_buf_size;
-	uint8_t *out_buf;
 
 	unsigned frame_duration;
 	unsigned frames_per_packet;
-
-	struct timespec start;
-	unsigned frames_sent;
-	uint32_t timestamp;
-
-	uint16_t seq;
 };
 
 static inline void timespec_diff(struct timespec *a, struct timespec *b,
@@ -162,9 +154,9 @@ static int sbc_cleanup(void *codec_data);
 static int sbc_get_config(void *codec_data, struct audio_input_config *config);
 static size_t sbc_get_buffer_size(void *codec_data);
 static size_t sbc_get_mediapacket_duration(void *codec_data);
-static void sbc_resume(void *codec_data);
-static ssize_t sbc_write_data(void *codec_data, const void *buffer,
-							size_t bytes, int fd);
+static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
+					size_t len, struct media_packet *mp,
+					size_t mp_data_len, size_t *written);
 
 struct audio_codec {
 	uint8_t type;
@@ -178,9 +170,9 @@ struct audio_codec {
 					struct audio_input_config *config);
 	size_t (*get_buffer_size) (void *codec_data);
 	size_t (*get_mediapacket_duration) (void *codec_data);
-	void (*resume) (void *codec_data);
-	ssize_t (*write_data) (void *codec_data, const void *buffer,
-							size_t bytes, int fd);
+	ssize_t (*encode_mediapacket) (void *codec_data, const uint8_t *buffer,
+					size_t len, struct media_packet *mp,
+					size_t mp_data_len, size_t *written);
 };
 
 static const struct audio_codec audio_codecs[] = {
@@ -194,8 +186,7 @@ static const struct audio_codec audio_codecs[] = {
 		.get_config = sbc_get_config,
 		.get_buffer_size = sbc_get_buffer_size,
 		.get_mediapacket_duration = sbc_get_mediapacket_duration,
-		.resume = sbc_resume,
-		.write_data = sbc_write_data,
+		.encode_mediapacket = sbc_encode_mediapacket,
 	}
 };
 
@@ -208,6 +199,13 @@ struct audio_endpoint {
 	const struct audio_codec *codec;
 	void *codec_data;
 	int fd;
+
+	struct media_packet *mp;
+	size_t mp_data_len;
+
+	uint16_t seq;
+	uint32_t samples;
+	struct timespec start;
 };
 
 static struct audio_endpoint audio_endpoints[MAX_AUDIO_ENDPOINTS];
@@ -419,8 +417,6 @@ static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu,
 	sbc_data->in_buf_size = num_frames * in_frame_len;
 
 	sbc_data->out_frame_len = out_frame_len;
-	sbc_data->out_buf_size = hdr_len + num_frames * out_frame_len;
-	sbc_data->out_buf = calloc(1, sbc_data->out_buf_size);
 
 	sbc_data->frame_duration = sbc_get_frame_duration(&sbc_data->enc);
 	sbc_data->frames_per_packet = num_frames;
@@ -438,7 +434,6 @@ static int sbc_cleanup(void *codec_data)
 	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
 
 	sbc_finish(&sbc_data->enc);
-	free(sbc_data->out_buf);
 	free(codec_data);
 
 	return AUDIO_STATUS_SUCCESS;
@@ -486,28 +481,18 @@ static size_t sbc_get_mediapacket_duration(void *codec_data)
 	return sbc_data->frame_duration * sbc_data->frames_per_packet;
 }
 
-static void sbc_resume(void *codec_data)
-{
-	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
-
-	DBG("");
-
-	clock_gettime(CLOCK_MONOTONIC, &sbc_data->start);
-
-	sbc_data->frames_sent = 0;
-	sbc_data->timestamp = 0;
-}
-
-static int write_media_packet(int fd, struct sbc_data *sbc_data,
-				struct media_packet *mp, size_t data_len)
+static int write_media_packet(struct a2dp_stream_out *out, size_t mp_data_len,
+							uint32_t input_samples)
 {
+	struct audio_endpoint *ep = out->ep;
+	struct media_packet *mp = ep->mp;
 	struct timespec cur;
 	struct timespec diff;
-	unsigned expected_frames;
+	uint32_t expected_samples;
 	int ret;
 
 	while (true) {
-		ret = write(fd, mp, sizeof(*mp) + data_len);
+		ret = write(ep->fd, mp, sizeof(*mp) + mp_data_len);
 		if (ret >= 0)
 			break;
 
@@ -515,12 +500,10 @@ static int write_media_packet(int fd, struct sbc_data *sbc_data,
 			return -errno;
 	}
 
-	sbc_data->frames_sent += mp->payload.frame_count;
-
 	clock_gettime(CLOCK_MONOTONIC, &cur);
-	timespec_diff(&cur, &sbc_data->start, &diff);
-	expected_frames = (diff.tv_sec * 1000000 + diff.tv_nsec / 1000) /
-						sbc_data->frame_duration;
+	timespec_diff(&cur, &ep->start, &diff);
+	expected_samples = (diff.tv_sec * 1000000ll + diff.tv_nsec / 1000ll) *
+						out->cfg.rate / 1000000ll;
 
 	/* AudioFlinger does not seem to provide any *working*
 	 * API to provide data in some interval and will just
@@ -531,9 +514,8 @@ static int write_media_packet(int fd, struct sbc_data *sbc_data,
 	 * lagging behind audio stream, we can sleep for
 	 * duration of single media packet.
 	 */
-	if (sbc_data->frames_sent >= expected_frames)
-		usleep(sbc_data->frame_duration *
-				mp->payload.frame_count);
+	if (ep->samples >= expected_samples)
+		usleep(input_samples * 1000000 / out->cfg.rate);
 
 	return ret;
 }
@@ -574,53 +556,6 @@ static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
 	return consumed;
 }
 
-static ssize_t sbc_write_data(void *codec_data, const void *buffer,
-							size_t bytes, int fd)
-{
-	struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
-	size_t consumed = 0;
-	struct media_packet *mp = (struct media_packet *) sbc_data->out_buf;
-	size_t free_space = sbc_data->out_buf_size - sizeof(*mp);
-
-	mp->hdr.v = 2;
-	mp->hdr.pt = 1;
-	mp->hdr.ssrc = htonl(1);
-
-	while (consumed < bytes) {
-		size_t written = 0;
-		ssize_t read;
-		int ret;
-
-		read = sbc_encode_mediapacket(codec_data, buffer + consumed,
-						bytes - consumed, mp,
-						free_space,
-						&written);
-
-		if (read <= 0) {
-			error("sbc_encode_mediapacket failed (%zd)", read);
-			goto done;
-		}
-
-		consumed += read;
-
-		mp->hdr.sequence_number = htons(sbc_data->seq++);
-		mp->hdr.timestamp = htonl(sbc_data->timestamp);
-
-		/* AudioFlinger provides PCM 16bit stereo only, thus sample size
-		 * is always 4 bytes
-		 */
-		sbc_data->timestamp += (read / 4);
-
-		ret = write_media_packet(fd, sbc_data, mp, written);
-		if (ret < 0)
-			return ret;
-	}
-
-done:
-	/* we always assume that all data was processed and sent */
-	return bytes;
-}
-
 static int audio_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len,
 			void *param, size_t *rsp_len, void *rsp, int *fd)
 {
@@ -967,6 +902,15 @@ static bool open_endpoint(struct audio_endpoint *ep,
 	codec->init(preset, mtu, &ep->codec_data);
 	codec->get_config(ep->codec_data, cfg);
 
+	ep->mp = calloc(mtu, 1);
+	if (!ep->mp)
+		goto failed;
+	ep->mp->hdr.v = 2;
+	ep->mp->hdr.pt = 1;
+	ep->mp->hdr.ssrc = htonl(1);
+
+	ep->mp_data_len = mtu - sizeof(*ep->mp);
+
 	free(preset);
 
 	return true;
@@ -986,6 +930,8 @@ static void close_endpoint(struct audio_endpoint *ep)
 		ep->fd = -1;
 	}
 
+	free(ep->mp);
+
 	ep->codec->cleanup(ep->codec_data);
 	ep->codec_data = NULL;
 }
@@ -1005,10 +951,57 @@ static void downmix_to_mono(struct a2dp_stream_out *out, const uint8_t *buffer,
 	}
 }
 
+static bool write_data(struct a2dp_stream_out *out, const void *buffer,
+								size_t bytes)
+{
+	struct audio_endpoint *ep = out->ep;
+	struct media_packet *mp = (struct media_packet *) ep->mp;
+	size_t free_space = ep->mp_data_len;
+	size_t consumed = 0;
+
+	while (consumed < bytes) {
+		size_t written = 0;
+		ssize_t read;
+		uint32_t samples;
+		int ret;
+
+		read = ep->codec->encode_mediapacket(ep->codec_data,
+							buffer + consumed,
+							bytes - consumed, mp,
+							free_space, &written);
+
+		/* This is non-fatal and we can just assume buffer was processed
+		 * properly and wait for next one.
+		 */
+		if (read <= 0)
+			return true;
+
+		consumed += read;
+
+		mp->hdr.sequence_number = htons(ep->seq++);
+		mp->hdr.timestamp = htonl(ep->samples);
+
+		/* AudioFlinger provides 16bit PCM, so sample size is 2 bytes
+		 * multipled by number of channels. Number of channels is simply
+		 * number of bits set in channels mask.
+		 */
+		samples = read / (2 * popcount(out->cfg.channels));
+		ep->samples += samples;
+
+		ret = write_media_packet(out, written, samples);
+		if (ret < 0)
+			return false;
+	}
+
+	return true;
+}
+
 static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
 								size_t bytes)
 {
 	struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+	const void *in_buf = buffer;
+	size_t in_len = bytes;
 
 	/* just return in case we're closing */
 	if (out->audio_state == AUDIO_A2DP_STATE_NONE)
@@ -1021,7 +1014,8 @@ static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
 		if (ipc_resume_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS)
 			return -1;
 
-		out->ep->codec->resume(out->ep->codec_data);
+		clock_gettime(CLOCK_MONOTONIC, &out->ep->start);
+		out->ep->samples = 0;
 
 		out->audio_state = AUDIO_A2DP_STATE_STARTED;
 	}
@@ -1051,14 +1045,14 @@ static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
 
 		downmix_to_mono(out, buffer, bytes);
 
-		return out->ep->codec->write_data(out->ep->codec_data,
-							out->downmix_buf,
-							bytes / 2,
-							out->ep->fd) * 2;
+		in_buf = out->downmix_buf;
+		in_len = bytes / 2;
 	}
 
-	return out->ep->codec->write_data(out->ep->codec_data, buffer,
-							bytes, out->ep->fd);
+	if (!write_data(out, in_buf, in_len))
+		return -1;
+
+	return bytes;
 }
 
 static uint32_t out_get_sample_rate(const struct audio_stream *stream)
-- 
1.8.5.4


^ permalink raw reply related

* [PATCH v2 4/5] android/hal-audio: Use payload length for codec init
From: Andrzej Kaczmarek @ 2014-03-03  8:56 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1393837002-29703-1-git-send-email-andrzej.kaczmarek@tieto.com>

It makes more sense to pass maximum payload length to codec since this
is actually used for calculations. MTU value is used only to allocate
proper buffer.
---
 android/hal-audio.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 78f89bd..5a69b52 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -387,11 +387,10 @@ static void sbc_init_encoder(struct sbc_data *sbc_data)
 			in->min_bitpool, in->max_bitpool);
 }
 
-static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu,
+static int sbc_codec_init(struct audio_preset *preset, uint16_t payload_len,
 							void **codec_data)
 {
 	struct sbc_data *sbc_data;
-	size_t hdr_len = sizeof(struct media_packet);
 	size_t in_frame_len;
 	size_t out_frame_len;
 	size_t num_frames;
@@ -411,7 +410,7 @@ static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu,
 
 	in_frame_len = sbc_get_codesize(&sbc_data->enc);
 	out_frame_len = sbc_get_frame_length(&sbc_data->enc);
-	num_frames = (mtu - hdr_len) / out_frame_len;
+	num_frames = payload_len / out_frame_len;
 
 	sbc_data->in_frame_len = in_frame_len;
 	sbc_data->in_buf_size = num_frames * in_frame_len;
@@ -421,8 +420,8 @@ static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu,
 	sbc_data->frame_duration = sbc_get_frame_duration(&sbc_data->enc);
 	sbc_data->frames_per_packet = num_frames;
 
-	DBG("mtu=%u in_frame_len=%zu out_frame_len=%zu frames_per_packet=%zu",
-			mtu, in_frame_len, out_frame_len, num_frames);
+	DBG("in_frame_len=%zu out_frame_len=%zu frames_per_packet=%zu",
+				in_frame_len, out_frame_len, num_frames);
 
 	*codec_data = sbc_data;
 
@@ -887,6 +886,7 @@ static bool open_endpoint(struct audio_endpoint *ep,
 	struct audio_preset *preset;
 	const struct audio_codec *codec;
 	uint16_t mtu;
+	uint16_t payload_len;
 	int fd;
 
 	if (ipc_open_stream_cmd(ep->id, &mtu, &fd, &preset) !=
@@ -896,10 +896,14 @@ static bool open_endpoint(struct audio_endpoint *ep,
 	if (set_blocking(fd) < 0)
 		goto failed;
 
+	DBG("mtu=%u", mtu);
+
+	payload_len = mtu - sizeof(*ep->mp);
+
 	ep->fd = fd;
 
 	codec = ep->codec;
-	codec->init(preset, mtu, &ep->codec_data);
+	codec->init(preset, payload_len, &ep->codec_data);
 	codec->get_config(ep->codec_data, cfg);
 
 	ep->mp = calloc(mtu, 1);
@@ -909,7 +913,7 @@ static bool open_endpoint(struct audio_endpoint *ep,
 	ep->mp->hdr.pt = 1;
 	ep->mp->hdr.ssrc = htonl(1);
 
-	ep->mp_data_len = mtu - sizeof(*ep->mp);
+	ep->mp_data_len = payload_len;
 
 	free(preset);
 
-- 
1.8.5.4


^ permalink raw reply related

* [PATCH v2 5/5] android/hal-audio: Provide better audio synchronization
From: Andrzej Kaczmarek @ 2014-03-03  8:56 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrzej Kaczmarek
In-Reply-To: <1393837002-29703-1-git-send-email-andrzej.kaczmarek@tieto.com>

Instead of waiting some fixed amount of useconds before next media
packet is encoded we use absolute time to wait precisely for a moment
when next packet is expected to be produced. This greatly improves flow
as is can compensate for time spent on encoding and writing data.
---
 android/hal-audio.c | 91 ++++++++++++++++++++++++++++-------------------------
 1 file changed, 49 insertions(+), 42 deletions(-)

diff --git a/android/hal-audio.c b/android/hal-audio.c
index 5a69b52..00dde49 100644
--- a/android/hal-audio.c
+++ b/android/hal-audio.c
@@ -147,6 +147,27 @@ static inline void timespec_diff(struct timespec *a, struct timespec *b,
 	}
 }
 
+static void timespec_add(struct timespec *base, uint64_t time_us,
+							struct timespec *res)
+{
+	res->tv_sec = base->tv_sec + time_us / 1000000;
+	res->tv_nsec = base->tv_nsec + (time_us % 1000000) * 1000;
+
+	if (res->tv_nsec >= 1000000000) {
+		res->tv_sec++;
+		res->tv_nsec -= 1000000000;
+	}
+}
+
+#if defined(ANDROID)
+/* Bionic does not have clock_nanosleep() prototype in time.h even though
+ * it provides its implementation.
+ */
+extern int clock_nanosleep(clockid_t clock_id, int flags,
+					const struct timespec *request,
+					struct timespec *remain);
+#endif
+
 static int sbc_get_presets(struct audio_preset *preset, size_t *len);
 static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu,
 							void **codec_data);
@@ -480,45 +501,6 @@ static size_t sbc_get_mediapacket_duration(void *codec_data)
 	return sbc_data->frame_duration * sbc_data->frames_per_packet;
 }
 
-static int write_media_packet(struct a2dp_stream_out *out, size_t mp_data_len,
-							uint32_t input_samples)
-{
-	struct audio_endpoint *ep = out->ep;
-	struct media_packet *mp = ep->mp;
-	struct timespec cur;
-	struct timespec diff;
-	uint32_t expected_samples;
-	int ret;
-
-	while (true) {
-		ret = write(ep->fd, mp, sizeof(*mp) + mp_data_len);
-		if (ret >= 0)
-			break;
-
-		if (errno != EINTR)
-			return -errno;
-	}
-
-	clock_gettime(CLOCK_MONOTONIC, &cur);
-	timespec_diff(&cur, &ep->start, &diff);
-	expected_samples = (diff.tv_sec * 1000000ll + diff.tv_nsec / 1000ll) *
-						out->cfg.rate / 1000000ll;
-
-	/* AudioFlinger does not seem to provide any *working*
-	 * API to provide data in some interval and will just
-	 * send another buffer as soon as we process current
-	 * one. To prevent overflowing L2CAP socket, we need to
-	 * introduce some artificial delay here base on how many
-	 * audio frames were sent so far, i.e. if we're not
-	 * lagging behind audio stream, we can sleep for
-	 * duration of single media packet.
-	 */
-	if (ep->samples >= expected_samples)
-		usleep(input_samples * 1000000 / out->cfg.rate);
-
-	return ret;
-}
-
 static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
 					size_t len, struct media_packet *mp,
 					size_t mp_data_len, size_t *written)
@@ -968,6 +950,25 @@ static bool write_data(struct a2dp_stream_out *out, const void *buffer,
 		ssize_t read;
 		uint32_t samples;
 		int ret;
+		uint64_t time_us;
+		struct timespec anchor;
+
+		time_us = ep->samples * 1000000ll / out->cfg.rate;
+
+		timespec_add(&ep->start, time_us, &anchor);
+
+		while (true) {
+			ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME,
+								&anchor, NULL);
+
+			if (!ret)
+				break;
+
+			if (ret != EINTR) {
+				error("clock_nanosleep failed (%d)", ret);
+				return false;
+			}
+		}
 
 		read = ep->codec->encode_mediapacket(ep->codec_data,
 							buffer + consumed,
@@ -992,9 +993,15 @@ static bool write_data(struct a2dp_stream_out *out, const void *buffer,
 		samples = read / (2 * popcount(out->cfg.channels));
 		ep->samples += samples;
 
-		ret = write_media_packet(out, written, samples);
-		if (ret < 0)
-			return false;
+		while (true) {
+			ret = write(ep->fd, mp, sizeof(*mp) + written);
+
+			if (ret >= 0)
+				break;
+
+			if (errno != EINTR)
+				return false;
+		}
 	}
 
 	return true;
-- 
1.8.5.4


^ permalink raw reply related

* Re: Receiving data in BLE non-connectable undirected advertisements
From: Benjamin Adler @ 2014-03-03  9:10 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <D7B9B661-DFF0-4AB7-8CAD-F2ACC8FDDBE8@holtmann.org>

Hello Marcel,

On 28.02.2014 15:46, Marcel Holtmann wrote:
> You can just write an advertising receiver by using Bluetooth HCI
 > User Channel feature from the 3.13 kernel.  With the help of
 > src/shared/hci.c (if that license is acceptable to you), this is
 > trivial. If the license is not acceptable, then you have to
> write your own HCI handling.
>
> The BlueZ source code contains samples in form of tools/ibeacon.c
 > and others on how to write such small program for direct access
> of the HCI interface. If you use this sample code, then please
 > comply with the license as well here.

regarding the license, this is more of a hobby project, so GPL is perfect.

Looking at hci.{c,h}, there is (except for the license) not a single 
comment that would help explain things (to bluetooth-newcomers). The 
functions might have descriptive names, but I'm missing the bigger picture.

Reading ibeacon.c, I get the impression the code is made for sending 
advertisements, not for receiving them. I might be wrong though, there's 
no comments either. None of the other filenames in tools/ made me hope I 
could find sample code for receiving non-connectable advertisements.

Marcel, could you either point me to some sample code, or help me 
understand hci.c (or list the required steps) so that I can write my own?

thanks!
ben

^ permalink raw reply

* Re: [PATCH 3/7] unit/test-hfp: Add /hfp/test_cmd_handler_1
From: Luiz Augusto von Dentz @ 2014-03-03  9:10 UTC (permalink / raw)
  To: Marcin Kraglak; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <1393835812-24238-3-git-send-email-marcin.kraglak@tieto.com>

Hi Marcin,

On Mon, Mar 3, 2014 at 10:36 AM, Marcin Kraglak
<marcin.kraglak@tieto.com> wrote:
> ---
>  unit/test-hfp.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 76 insertions(+)
>
> diff --git a/unit/test-hfp.c b/unit/test-hfp.c
> index 092f255..8cbb524 100644
> --- a/unit/test-hfp.c
> +++ b/unit/test-hfp.c
> @@ -39,6 +39,14 @@ struct context {
>         const struct test_data *data;
>  };
>
> +struct test_data {
> +       hfp_command_func_t command_cb;
> +       const char *server_send;
> +       int data_len;
> +       const char *expected_rsp;
> +       int expected_rsp_len;
> +};
> +
>  static void context_quit(struct context *context)
>  {
>         g_main_loop_quit(context->main_loop);
> @@ -49,11 +57,35 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
>  {
>         struct context *context = user_data;
>
> +       if (context->data && context->data->expected_rsp) {
> +               int fd = g_io_channel_unix_get_fd(channel);
> +               char buf[1024];
> +               int len;
> +
> +               len = read(fd, buf, sizeof(buf));
> +               g_assert(context->data->expected_rsp_len == len);
> +               g_assert(!memcmp(context->data->expected_rsp, buf, len));
> +       }
> +
>         context_quit(context);
>
>         return FALSE;
>  }
>
> +static void cmd_handler(const char *command, void *user_data)
> +{
> +       struct context *context = user_data;
> +       int cmd_len;
> +
> +       cmd_len = strlen(command);
> +
> +       g_assert(cmd_len < context->data->data_len);
> +
> +       g_assert(!memcmp(command, context->data->server_send, cmd_len));
> +
> +       hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
> +}
> +
>  static struct context *create_context(gconstpointer data)
>  {
>         struct context *context = g_new0(struct context, 1);
> @@ -94,6 +126,9 @@ static void execute_context(struct context *context)
>
>         g_main_loop_unref(context->main_loop);
>
> +       if (context->hfp)
> +               hfp_gw_unref(context->hfp);
> +
>         g_free(context);
>  }
>
> @@ -107,15 +142,56 @@ static void test_init(gconstpointer data)
>         g_assert(hfp_gw_set_close_on_unref(context->hfp, true));
>
>         hfp_gw_unref(context->hfp);
> +       context->hfp = NULL;
> +
> +       execute_context(context);
> +}
> +
> +static void test_command_handler(gconstpointer data)
> +{
> +       struct context *context = create_context(data);
> +
> +       context->hfp = hfp_gw_new(context->fd_client);
> +
> +       g_assert(context->hfp);
> +       g_assert(hfp_gw_set_close_on_unref(context->hfp, true));
> +
> +       if (context->data->command_cb)
> +               g_assert(hfp_gw_set_command_handler(context->hfp,
> +                                               context->data->command_cb,
> +                                               context, NULL));
> +
> +       if (context->data->server_send) {
> +               ssize_t len;
> +               len = write(context->fd_server, context->data->server_send,
> +                                               context->data->data_len);
> +
> +               g_assert(len);
> +       }
>
>         execute_context(context);
>  }
>
> +static const char err_rsp[] = {'\r', '\n', 'E', 'R', 'R', 'O', 'R', '\r', '\n'};
> +
> +static const char atbrsf[] = {'A', 'T', '+', 'B', 'R', 'S', 'F', '\r'};
> +
> +static const struct test_data test_cmd_handler_1 = {
> +       .command_cb = cmd_handler,
> +       .server_send = atbrsf,
> +       .data_len = sizeof(atbrsf),
> +       .expected_rsp = err_rsp,
> +       .expected_rsp_len = sizeof(err_rsp)
> +};
> +
>  int main(int argc, char *argv[])
>  {
>         g_test_init(&argc, &argv, NULL);
>
>         g_test_add_data_func("/hfp/test_init", NULL, test_init);
>
> +       g_test_add_data_func("/hfp/test_cmd_handler_1", &test_cmd_handler_1,
> +                                                       test_command_handler);
> +
>         return g_test_run();
>  }
> --
> 1.8.3.1

I would try to keep this aligned with other unit tests defining raw
PDUs such as test-sdp, test-avdtp and test-avctp, so you don't have to
write a whole different way to handle PDUs on every test. In fact we
can even make this code common at some point.


-- 
Luiz Augusto von Dentz

^ permalink raw reply

* [PATCH] tools: Fix using uninitialized value
From: Andrei Emeltchenko @ 2014-03-03  9:13 UTC (permalink / raw)
  To: linux-bluetooth

From: Andrei Emeltchenko <andrei.emeltchenko@intel.com>

Fixes warning:
tools/ibeacon.c:150:14: warning: The left expression of the compound
assignment is an uninitialized value. The computed value will also be
garbage
        cmd.data[2] |= 0x02;            /* LE General Discoverable Mode
*/
        ~~~~~~~~~~~ ^
1 warning generated.
---
 tools/ibeacon.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/ibeacon.c b/tools/ibeacon.c
index 458f9e7..2071c6b 100644
--- a/tools/ibeacon.c
+++ b/tools/ibeacon.c
@@ -147,7 +147,7 @@ static void adv_tx_power_callback(const void *data, uint8_t size,
 
 	cmd.data[0] = 0x02;		/* Field length */
 	cmd.data[1] = 0x01;		/* Flags */
-	cmd.data[2] |= 0x02;		/* LE General Discoverable Mode */
+	cmd.data[2] = 0x02;		/* LE General Discoverable Mode */
 	cmd.data[2] |= 0x04;		/* BR/EDR Not Supported */
 
 	cmd.data[3] = 0x1a;		/* Field length */
-- 
1.8.3.2


^ permalink raw reply related

* Re: [PATCH 3/7] unit/test-hfp: Add /hfp/test_cmd_handler_1
From: Marcin Kraglak @ 2014-03-03  9:21 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <CABBYNZ+fyMrY+sTzNgx_KJ5KQALL30JOKji5xHjuBK9k2cagOQ@mail.gmail.com>

Hi Luiz,

On 3 March 2014 10:10, Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
> Hi Marcin,
>
> On Mon, Mar 3, 2014 at 10:36 AM, Marcin Kraglak
> <marcin.kraglak@tieto.com> wrote:
>> ---
>>  unit/test-hfp.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 76 insertions(+)
>>
>> diff --git a/unit/test-hfp.c b/unit/test-hfp.c
>> index 092f255..8cbb524 100644
>> --- a/unit/test-hfp.c
>> +++ b/unit/test-hfp.c
>> @@ -39,6 +39,14 @@ struct context {
>>         const struct test_data *data;
>>  };
>>
>> +struct test_data {
>> +       hfp_command_func_t command_cb;
>> +       const char *server_send;
>> +       int data_len;
>> +       const char *expected_rsp;
>> +       int expected_rsp_len;
>> +};
>> +
>>  static void context_quit(struct context *context)
>>  {
>>         g_main_loop_quit(context->main_loop);
>> @@ -49,11 +57,35 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
>>  {
>>         struct context *context = user_data;
>>
>> +       if (context->data && context->data->expected_rsp) {
>> +               int fd = g_io_channel_unix_get_fd(channel);
>> +               char buf[1024];
>> +               int len;
>> +
>> +               len = read(fd, buf, sizeof(buf));
>> +               g_assert(context->data->expected_rsp_len == len);
>> +               g_assert(!memcmp(context->data->expected_rsp, buf, len));
>> +       }
>> +
>>         context_quit(context);
>>
>>         return FALSE;
>>  }
>>
>> +static void cmd_handler(const char *command, void *user_data)
>> +{
>> +       struct context *context = user_data;
>> +       int cmd_len;
>> +
>> +       cmd_len = strlen(command);
>> +
>> +       g_assert(cmd_len < context->data->data_len);
>> +
>> +       g_assert(!memcmp(command, context->data->server_send, cmd_len));
>> +
>> +       hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
>> +}
>> +
>>  static struct context *create_context(gconstpointer data)
>>  {
>>         struct context *context = g_new0(struct context, 1);
>> @@ -94,6 +126,9 @@ static void execute_context(struct context *context)
>>
>>         g_main_loop_unref(context->main_loop);
>>
>> +       if (context->hfp)
>> +               hfp_gw_unref(context->hfp);
>> +
>>         g_free(context);
>>  }
>>
>> @@ -107,15 +142,56 @@ static void test_init(gconstpointer data)
>>         g_assert(hfp_gw_set_close_on_unref(context->hfp, true));
>>
>>         hfp_gw_unref(context->hfp);
>> +       context->hfp = NULL;
>> +
>> +       execute_context(context);
>> +}
>> +
>> +static void test_command_handler(gconstpointer data)
>> +{
>> +       struct context *context = create_context(data);
>> +
>> +       context->hfp = hfp_gw_new(context->fd_client);
>> +
>> +       g_assert(context->hfp);
>> +       g_assert(hfp_gw_set_close_on_unref(context->hfp, true));
>> +
>> +       if (context->data->command_cb)
>> +               g_assert(hfp_gw_set_command_handler(context->hfp,
>> +                                               context->data->command_cb,
>> +                                               context, NULL));
>> +
>> +       if (context->data->server_send) {
>> +               ssize_t len;
>> +               len = write(context->fd_server, context->data->server_send,
>> +                                               context->data->data_len);
>> +
>> +               g_assert(len);
>> +       }
>>
>>         execute_context(context);
>>  }
>>
>> +static const char err_rsp[] = {'\r', '\n', 'E', 'R', 'R', 'O', 'R', '\r', '\n'};
>> +
>> +static const char atbrsf[] = {'A', 'T', '+', 'B', 'R', 'S', 'F', '\r'};
>> +
>> +static const struct test_data test_cmd_handler_1 = {
>> +       .command_cb = cmd_handler,
>> +       .server_send = atbrsf,
>> +       .data_len = sizeof(atbrsf),
>> +       .expected_rsp = err_rsp,
>> +       .expected_rsp_len = sizeof(err_rsp)
>> +};
>> +
>>  int main(int argc, char *argv[])
>>  {
>>         g_test_init(&argc, &argv, NULL);
>>
>>         g_test_add_data_func("/hfp/test_init", NULL, test_init);
>>
>> +       g_test_add_data_func("/hfp/test_cmd_handler_1", &test_cmd_handler_1,
>> +                                                       test_command_handler);
>> +
>>         return g_test_run();
>>  }
>> --
>> 1.8.3.1
>
> I would try to keep this aligned with other unit tests defining raw
> PDUs such as test-sdp, test-avdtp and test-avctp, so you don't have to
> write a whole different way to handle PDUs on every test. In fact we
> can even make this code common at some point.
>
>
> --
> Luiz Augusto von Dentz

right, it woult be better to use common code, so I'll send v2

BR
Marcin

^ permalink raw reply

* [PATCH 1/2] android/gatt: Add initial files
From: Grzegorz Kolodziejczyk @ 2014-03-03  9:29 UTC (permalink / raw)
  To: linux-bluetooth

This adds initial daemon code for GATT profile.
---
 android/Android.mk  |  1 +
 android/Makefile.am |  1 +
 android/gatt.c      | 44 ++++++++++++++++++++++++++++++++++++++++++++
 android/gatt.h      | 25 +++++++++++++++++++++++++
 android/main.c      | 13 +++++++++++++
 5 files changed, 84 insertions(+)
 create mode 100644 android/gatt.c
 create mode 100644 android/gatt.h

diff --git a/android/Android.mk b/android/Android.mk
index 56b86ba..429a4b8 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -42,6 +42,7 @@ LOCAL_SRC_FILES := \
 	bluez/android/avrcp-lib.c \
 	bluez/android/pan.c \
 	bluez/android/handsfree.c \
+	bluez/android/gatt.c
 	bluez/src/log.c \
 	bluez/src/shared/mgmt.c \
 	bluez/src/shared/util.c \
diff --git a/android/Makefile.am b/android/Makefile.am
index 31f905c..6be6cda 100644
--- a/android/Makefile.am
+++ b/android/Makefile.am
@@ -40,6 +40,7 @@ android_bluetoothd_SOURCES = android/main.c \
 				android/socket.h android/socket.c \
 				android/pan.h android/pan.c \
 				android/handsfree.h android/handsfree.c \
+				android/gatt.h android/gatt.c \
 				btio/btio.h btio/btio.c \
 				src/sdp-client.h src/sdp-client.c \
 				profiles/network/bnep.h profiles/network/bnep.c
diff --git a/android/gatt.c b/android/gatt.c
new file mode 100644
index 0000000..f577446
--- /dev/null
+++ b/android/gatt.c
@@ -0,0 +1,44 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "ipc.h"
+#include "lib/bluetooth.h"
+#include "gatt.h"
+
+bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
+{
+	return false;
+}
+
+void bt_gatt_unregister(void)
+{
+
+}
diff --git a/android/gatt.h b/android/gatt.h
new file mode 100644
index 0000000..d4392d9
--- /dev/null
+++ b/android/gatt.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr);
+void bt_gatt_unregister(void);
diff --git a/android/main.c b/android/main.c
index a6742ef..7b5ca9c 100644
--- a/android/main.c
+++ b/android/main.c
@@ -58,6 +58,7 @@
 #include "pan.h"
 #include "avrcp.h"
 #include "handsfree.h"
+#include "gatt.h"
 
 #define STARTUP_GRACE_SECONDS 5
 #define SHUTDOWN_GRACE_SECONDS 10
@@ -125,6 +126,12 @@ static void service_register(const void *buf, uint16_t len)
 			goto failed;
 		}
 
+	case HAL_SERVICE_ID_GATT:
+		if (!bt_gatt_register(hal_ipc, &adapter_bdaddr)) {
+			status = HAL_STATUS_FAILED;
+			goto failed;
+		}
+
 		break;
 	default:
 		DBG("service %u not supported", m->service_id);
@@ -175,6 +182,9 @@ static void service_unregister(const void *buf, uint16_t len)
 	case HAL_SERVICE_ID_HANDSFREE:
 		bt_handsfree_unregister();
 		break;
+	case HAL_SERVICE_ID_GATT:
+		bt_gatt_unregister();
+		break;
 	default:
 		/* This would indicate bug in HAL, as unregister should not be
 		 * called in init failed */
@@ -371,6 +381,9 @@ static void cleanup_services(void)
 		case HAL_SERVICE_ID_HANDSFREE:
 			bt_handsfree_unregister();
 			break;
+		case HAL_SERVICE_ID_GATT:
+			bt_gatt_unregister();
+			break;
 		}
 
 		services[i] = false;
-- 
1.8.5.2


^ permalink raw reply related

* [PATCH 2/2] android/gatt: Add stubs for GATT commands handlers
From: Grzegorz Kolodziejczyk @ 2014-03-03  9:29 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1393838957-26880-1-git-send-email-grzegorz.kolodziejczyk@tieto.com>

Add empty handlers for GATT IPC commands.
---
 android/gatt.c | 414 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 413 insertions(+), 1 deletion(-)

diff --git a/android/gatt.c b/android/gatt.c
index f577446..7a01d15 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -28,17 +28,429 @@
 #include <stdbool.h>
 #include <stdlib.h>
 #include <stdint.h>
+#include <glib.h>
 
 #include "ipc.h"
 #include "lib/bluetooth.h"
 #include "gatt.h"
+#include "src/log.h"
+#include "hal-msg.h"
+
+static struct ipc *hal_ipc = NULL;
+static bdaddr_t adapter_addr;
+
+static void handle_client_register(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER,
+							HAL_STATUS_FAILED);
+}
+
+static void handle_client_unregister(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_UNREGISTER, HAL_STATUS_FAILED);
+}
+
+static void handle_client_scan(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN,
+							HAL_STATUS_FAILED);
+}
+
+static void handle_client_connect(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT,
+							HAL_STATUS_FAILED);
+}
+
+static void handle_client_disconnect(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_DISCONNECT, HAL_STATUS_FAILED);
+}
+
+static void handle_client_listen(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN,
+							HAL_STATUS_FAILED);
+}
+
+static void handle_client_refresh(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH,
+							HAL_STATUS_FAILED);
+}
+
+static void handle_client_search_service(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_SEARCH_SERVICE, HAL_STATUS_FAILED);
+}
+
+static void handle_client_get_included_service(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE,
+					HAL_STATUS_FAILED);
+}
+
+static void handle_client_get_characteristic(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC,
+					HAL_STATUS_FAILED);
+}
+
+static void handle_client_get_descriptor(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, HAL_STATUS_FAILED);
+}
+
+static void handle_client_read_characteristic(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC,
+					HAL_STATUS_FAILED);
+}
+
+static void handle_client_write_characteristic(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC,
+					HAL_STATUS_FAILED);
+}
+
+static void handle_client_read_descriptor(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_READ_DESCRIPTOR, HAL_STATUS_FAILED);
+}
+
+static void handle_client_write_descriptor(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR, HAL_STATUS_FAILED);
+}
+
+static void handle_client_execute_write(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_EXECUTE_WRITE, HAL_STATUS_FAILED);
+}
+
+static void handle_client_register_for_notification(const void *buf,
+								uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION,
+				HAL_STATUS_FAILED);
+}
+
+static void handle_client_deregister_for_notification(const void *buf,
+								uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+				HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION,
+				HAL_STATUS_FAILED);
+}
+
+static void handle_client_read_remote_rssi(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI, HAL_STATUS_FAILED);
+}
+
+static void handle_client_get_device_type(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE, HAL_STATUS_FAILED);
+}
+
+static void handle_client_set_adv_data(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_SET_ADV_DATA, HAL_STATUS_FAILED);
+}
+
+static void handle_client_test_command(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_CLIENT_TEST_COMMAND, HAL_STATUS_FAILED);
+}
+
+static void handle_server_register(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER,
+							HAL_STATUS_FAILED);
+}
+
+static void handle_server_unregister(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_UNREGISTER, HAL_STATUS_FAILED);
+}
+
+static void handle_server_connect(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT,
+							HAL_STATUS_FAILED);
+}
+
+static void handle_server_disconnect(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_DISCONNECT, HAL_STATUS_FAILED);
+}
+
+static void handle_server_add_service(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_ADD_SERVICE, HAL_STATUS_FAILED);
+}
+
+static void handle_server_add_included_service(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_ADD_INC_SERVICE, HAL_STATUS_FAILED);
+}
+
+static void handle_server_add_characteristic(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC,
+					HAL_STATUS_FAILED);
+}
+
+static void handle_server_add_descriptor(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_ADD_DESCRIPTOR, HAL_STATUS_FAILED);
+}
+
+static void handle_server_start_service(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_START_SERVICE, HAL_STATUS_FAILED);
+}
+
+static void handle_server_stop_service(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_STOP_SERVICE, HAL_STATUS_FAILED);
+}
+
+static void handle_server_delete_service(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_DELETE_SERVICE, HAL_STATUS_FAILED);
+}
+
+static void handle_server_send_indication(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_SEND_INDICATION, HAL_STATUS_FAILED);
+}
+
+static void handle_server_send_response(const void *buf, uint16_t len)
+{
+	DBG("");
+
+	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+			HAL_OP_GATT_SERVER_SEND_RESPONSE, HAL_STATUS_FAILED);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+	/* HAL_OP_GATT_CLIENT_REGISTER */
+	{handle_client_register, false,
+				sizeof(struct hal_cmd_gatt_client_register)},
+	/* HAL_OP_GATT_CLIENT_UNREGISTER */
+	{handle_client_unregister, false,
+				sizeof(struct hal_cmd_gatt_client_unregister)},
+	/* HAL_OP_GATT_CLIENT_SCAN */
+	{handle_client_scan, false,
+				sizeof(struct hal_cmd_gatt_client_scan)},
+	/* HAL_OP_GATT_CLIENT_CONNECT */
+	{handle_client_connect, false,
+				sizeof(struct hal_cmd_gatt_client_connect)},
+	/* HAL_OP_GATT_CLIENT_DISCONNECT */
+	{handle_client_disconnect, false,
+				sizeof(struct hal_cmd_gatt_client_disconnect)},
+	/* HAL_OP_GATT_CLIENT_LISTEN */
+	{handle_client_listen, false,
+				sizeof(struct hal_cmd_gatt_client_listen)},
+	/* HAL_OP_GATT_CLIENT_REFRESH */
+	{handle_client_refresh, false,
+				sizeof(struct hal_cmd_gatt_client_refresh)},
+	/* HAL_OP_GATT_CLIENT_SEARCH_SERVICE */
+	{handle_client_search_service, true,
+			sizeof(struct hal_cmd_gatt_client_search_service)},
+	/* HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE */
+	{handle_client_get_included_service, true,
+		sizeof(struct hal_cmd_gatt_client_get_included_service)},
+	/* HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC */
+	{handle_client_get_characteristic, true,
+			sizeof(struct hal_cmd_gatt_client_get_characteristic)},
+	/* HAL_OP_GATT_CLIENT_GET_DESCRIPTOR */
+	{handle_client_get_descriptor, true,
+			sizeof(struct hal_cmd_gatt_client_get_descriptor)},
+	/* HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC */
+	{handle_client_read_characteristic, false,
+			sizeof(struct hal_cmd_gatt_client_read_characteristic)},
+	/* HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC */
+	{handle_client_write_characteristic, true,
+		sizeof(struct hal_cmd_gatt_client_write_characteristic)},
+	/* HAL_OP_GATT_CLIENT_READ_DESCRIPTOR */
+	{handle_client_read_descriptor, false,
+			sizeof(struct hal_cmd_gatt_client_read_descriptor)},
+	/* HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR */
+	{handle_client_write_descriptor, true,
+			sizeof(struct hal_cmd_gatt_client_write_descriptor)},
+	/* HAL_OP_GATT_CLIENT_EXECUTE_WRITE */
+	{handle_client_execute_write, false,
+			sizeof(struct hal_cmd_gatt_client_execute_write)},
+	/* HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION */
+	{handle_client_register_for_notification, false,
+		sizeof(struct hal_cmd_gatt_client_register_for_notification)},
+	/* HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION */
+	{handle_client_deregister_for_notification, false,
+		sizeof(struct hal_cmd_gatt_client_deregister_for_notification)},
+	/* HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI */
+	{handle_client_read_remote_rssi, false,
+			sizeof(struct hal_cmd_gatt_client_read_remote_rssi)},
+	/* HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE */
+	{handle_client_get_device_type, false,
+			sizeof(struct hal_cmd_gatt_client_get_device_type)},
+	/* HAL_OP_GATT_CLIENT_SET_ADV_DATA */
+	{handle_client_set_adv_data, true,
+			sizeof(struct hal_cmd_gatt_client_set_adv_data)},
+	/* HAL_OP_GATT_CLIENT_TEST_COMMAND */
+	{handle_client_test_command, false,
+			sizeof(struct hal_cmd_gatt_client_test_command)},
+	/* HAL_OP_GATT_SERVER_REGISTER */
+	{handle_server_register, false,
+				sizeof(struct hal_cmd_gatt_server_register)},
+	/* HAL_OP_GATT_SERVER_UNREGISTER */
+	{handle_server_unregister, false,
+				sizeof(struct hal_cmd_gatt_server_unregister)},
+	/* HAL_OP_GATT_SERVER_CONNECT */
+	{handle_server_connect, false,
+				sizeof(struct hal_cmd_gatt_server_connect)},
+	/* HAL_OP_GATT_SERVER_DISCONNECT */
+	{handle_server_disconnect, false,
+				sizeof(struct hal_cmd_gatt_server_disconnect)},
+	/* HAL_OP_GATT_SERVER_ADD_SERVICE */
+	{handle_server_add_service, false,
+				sizeof(struct hal_cmd_gatt_server_add_service)},
+	/* HAL_OP_GATT_SERVER_ADD_INC_SERVICE */
+	{handle_server_add_included_service, false,
+			sizeof(struct hal_cmd_gatt_server_add_inc_service)},
+	/* HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC */
+	{handle_server_add_characteristic, false,
+			sizeof(struct hal_cmd_gatt_server_add_characteristic)},
+	/* HAL_OP_GATT_SERVER_ADD_DESCRIPTOR */
+	{handle_server_add_descriptor, false,
+			sizeof(struct hal_cmd_gatt_server_add_descriptor)},
+	/* HAL_OP_GATT_SERVER_START_SERVICE */
+	{handle_server_start_service, false,
+			sizeof(struct hal_cmd_gatt_server_start_service)},
+	/* HAL_OP_GATT_SERVER_STOP_SERVICE */
+	{handle_server_stop_service, false,
+			sizeof(struct hal_cmd_gatt_server_stop_service)},
+	/* HAL_OP_GATT_SERVER_DELETE_SERVICE */
+	{handle_server_delete_service, false,
+			sizeof(struct hal_cmd_gatt_server_delete_service)},
+	/* HAL_OP_GATT_SERVER_SEND_INDICATION */
+	{handle_server_send_indication, true,
+			sizeof(struct hal_cmd_gatt_server_send_indication)},
+	/* HAL_OP_GATT_SERVER_SEND_RESPONSE */
+	{handle_server_send_response, true,
+			sizeof(struct hal_cmd_gatt_server_send_response)},
+};
 
 bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
 {
-	return false;
+	DBG("");
+
+	bacpy(&adapter_addr, addr);
+
+	ipc_register(hal_ipc, HAL_SERVICE_ID_GATT, cmd_handlers,
+						G_N_ELEMENTS(cmd_handlers));
+
+	return true;
 }
 
 void bt_gatt_unregister(void)
 {
+	DBG("");
 
+	ipc_unregister(hal_ipc, HAL_SERVICE_ID_GATT);
 }
-- 
1.8.5.2


^ permalink raw reply related

* Re: [PATCH BlueZ 03/16] android/avrcp-lib: Rework handler callback parameters
From: Andrei Emeltchenko @ 2014-03-03  9:52 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <1393786109-6554-3-git-send-email-luiz.dentz@gmail.com>

Hi Luiz,

On Sun, Mar 02, 2014 at 08:48:18PM +0200, Luiz Augusto von Dentz wrote:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> 
> This rework handler callback parameters to make it able to return
> proper error such as -EAGAIN to implement asynchronous responses.
> ---
>  android/avrcp-lib.c | 12 +++++++++---
>  android/avrcp-lib.h |  5 +++--
>  unit/test-avrcp.c   | 42 +++++++++++++++++++++---------------------
>  3 files changed, 33 insertions(+), 26 deletions(-)
> 
> diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
> index cdad6ac..339be16 100644
> --- a/android/avrcp-lib.c
> +++ b/android/avrcp-lib.c
> @@ -125,6 +125,7 @@ static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
>  	struct avrcp_header *pdu = (void *) operands;
>  	uint32_t company_id = ntoh24(pdu->company_id);
>  	uint16_t params_len = ntohs(pdu->params_len);
> +	ssize_t ret;
>  
>  	if (company_id != IEEEID_BTSIG) {
>  		*code = AVC_CTYPE_NOT_IMPLEMENTED;
> @@ -159,12 +160,17 @@ static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
>  		goto reject;
>  	}
>  
> -	*code = handler->func(session, transaction, &params_len,
> +	ret = handler->func(session, transaction, code, params_len,
>  					pdu->params, session->control_data);
> +	if (ret < 0) {
> +		if (ret == -EAGAIN)
> +			return ret;
> +		goto reject;
> +	}
>  
> -	pdu->params_len = htons(params_len);
> +	pdu->params_len = htons(ret);
>  
> -	return AVRCP_HEADER_LENGTH + params_len;
> +	return AVRCP_HEADER_LENGTH + ret;
>  
>  reject:
>  	pdu->params_len = htons(1);
> diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
> index a33bdfe..d7a805b 100644
> --- a/android/avrcp-lib.h
> +++ b/android/avrcp-lib.h
> @@ -79,8 +79,9 @@ struct avrcp;
>  struct avrcp_control_handler {
>  	uint8_t id;
>  	uint8_t code;
> -	uint8_t (*func) (struct avrcp *session, uint8_t transaction,
> -			uint16_t *params_len, uint8_t *params, void *user_data);
> +	ssize_t (*func) (struct avrcp *session, uint8_t transaction,
> +			uint8_t *code, uint16_t params_len, uint8_t *params,
> +			void *user_data);
>  };
>  
>  struct avrcp_passthrough_handler {
> diff --git a/unit/test-avrcp.c b/unit/test-avrcp.c
> index 302c331..2d5711b 100644
> --- a/unit/test-avrcp.c
> +++ b/unit/test-avrcp.c
> @@ -292,54 +292,54 @@ static const struct avrcp_passthrough_handler passthrough_handlers[] = {
>  		{ },
>  };
>  
> -static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
> -				uint8_t transaction, uint16_t *params_len,
> -				uint8_t *params, void *user_data)
> +static ssize_t avrcp_handle_get_capabilities(struct avrcp *session,
> +					uint8_t transaction, uint8_t *code,
> +					uint16_t params_len, uint8_t *params,
> +					void *user_data)
>  {
>  	uint32_t id = 0x001958;
>  
> -	DBG("params[0] %d params_len %d", params[0], *params_len);
> -
> -	if (*params_len != 1)
> +	if (params_len != 1)
>  		goto fail;
>  
>  	switch (params[0]) {
>  	case CAP_COMPANY_ID:
> -		*params_len = 5;
>  		params[1] = 1;
>  		hton24(&params[2], id);
> -		return AVC_CTYPE_STABLE;
> +		*code = AVC_CTYPE_STABLE;
> +		return 5;

What is 5? Shall we define constants or use formulas?

Best regards 
Andrei Emeltchenko 


^ permalink raw reply


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