Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH V2 net-next] Bluetooth: fix shadow warning in hci_disconnect()
From: Fabian Frederick @ 2014-10-25  8:48 UTC (permalink / raw)
  To: linux-kernel
  Cc: Fabian Frederick, Marcel Holtmann, Gustavo Padovan, Johan Hedberg,
	David S. Miller, linux-bluetooth, netdev

use clkoff_cp for hci_cp_read_clock_offset instead of cp
(already defined above).

Suggested-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Fabian Frederick <fabf@skynet.be>
---
V2: use clkoff_cp instead of cpr (suggested by Marcel Holtmann)

 net/bluetooth/hci_conn.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index b9517bd..62f4ac6 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -141,10 +141,11 @@ int hci_disconnect(struct hci_conn *conn, __u8 reason)
 	 */
 	if (conn->type == ACL_LINK && conn->role == HCI_ROLE_MASTER) {
 		struct hci_dev *hdev = conn->hdev;
-		struct hci_cp_read_clock_offset cp;
+		struct hci_cp_read_clock_offset clkoff_cp;
 
-		cp.handle = cpu_to_le16(conn->handle);
-		hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET, sizeof(cp), &cp);
+		clkoff_cp.handle = cpu_to_le16(conn->handle);
+		hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET, sizeof(clkoff_cp),
+			     &clkoff_cp);
 	}
 
 	conn->state = BT_DISCONN;
-- 
1.9.3

^ permalink raw reply related

* [PATCH BlueZ v2 7/7] attrib: fix GATTRIB_ALL_REQS behavior
From: Michael Janssen @ 2014-10-24 20:36 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Michael Janssen
In-Reply-To: <1414182983-23959-1-git-send-email-jamuraa@chromium.org>

g_attrib_register(..., GATTRIB_ALL_REQS, ...) behavior would previously
include indications, this fixes it to only include requests and
commands.
---
 attrib/gattrib.c | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index a65d1ca..f678435 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -147,6 +147,27 @@ static bool is_response(guint8 opcode)
 	return false;
 }
 
+static bool is_request(guint8 opcode)
+{
+	switch (opcode) {
+	case ATT_OP_MTU_REQ:
+	case ATT_OP_FIND_INFO_REQ:
+	case ATT_OP_FIND_BY_TYPE_REQ:
+	case ATT_OP_READ_BY_TYPE_REQ:
+	case ATT_OP_READ_REQ:
+	case ATT_OP_READ_BLOB_REQ:
+	case ATT_OP_READ_MULTI_REQ:
+	case ATT_OP_READ_BY_GROUP_REQ:
+	case ATT_OP_WRITE_REQ:
+	case ATT_OP_WRITE_CMD:
+	case ATT_OP_PREP_WRITE_REQ:
+	case ATT_OP_EXEC_WRITE_REQ:
+		return true;
+	}
+
+	return false;
+}
+
 GAttrib *g_attrib_ref(GAttrib *attrib)
 {
 	int refs;
@@ -373,7 +394,7 @@ static bool match_event(struct event *evt, const uint8_t *pdu, gsize len)
 	if (evt->expected == GATTRIB_ALL_EVENTS)
 		return true;
 
-	if (!is_response(pdu[0]) && evt->expected == GATTRIB_ALL_REQS)
+	if (is_request(pdu[0]) && evt->expected == GATTRIB_ALL_REQS)
 		return true;
 
 	if (evt->expected == pdu[0] && evt->handle == GATTRIB_ALL_HANDLES)
-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply related

* [PATCH BlueZ v2 6/7] attrib: Add unit tests for g_attrib_register
From: Michael Janssen @ 2014-10-24 20:36 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Michael Janssen
In-Reply-To: <1414182983-23959-1-git-send-email-jamuraa@chromium.org>

---
 unit/test-gattrib.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 156 insertions(+), 1 deletion(-)

diff --git a/unit/test-gattrib.c b/unit/test-gattrib.c
index 23a5485..61db4a1 100644
--- a/unit/test-gattrib.c
+++ b/unit/test-gattrib.c
@@ -45,6 +45,23 @@
 
 #define data(args...) ((const unsigned char[]) { args })
 
+struct test_pdu {
+	bool valid;
+	bool sent;
+	bool received;
+	const uint8_t *data;
+	size_t size;
+};
+
+#define pdu(args...)				\
+	{					\
+		.valid = true,			\
+		.sent = false,			\
+		.received = false,		\
+		.data = data(args),		\
+		.size = sizeof(data(args)),	\
+	}
+
 struct context {
 	GMainLoop *main_loop;
 	GIOChannel *att_io;
@@ -298,9 +315,147 @@ static void test_cancel(struct context *cxt, gconstpointer unused)
 	g_assert(!canceled);
 }
 
+static void send_test_pdus(gpointer context, struct test_pdu *pdus)
+{
+	struct context *cxt = context;
+	size_t len;
+	int fd;
+	struct test_pdu *cur_pdu;
+
+	fd = g_io_channel_unix_get_fd(cxt->server_io);
+
+	for (cur_pdu = pdus; cur_pdu->valid; cur_pdu++)
+		cur_pdu->sent = false;
+
+	for (cur_pdu = pdus; cur_pdu->valid; cur_pdu++) {
+		if (g_test_verbose())
+			util_hexdump('>', cur_pdu->data, cur_pdu->size,
+						 test_debug, "send_test_pdus: ");
+		len = write(fd, cur_pdu->data, cur_pdu->size);
+		g_assert_cmpint(len, ==, cur_pdu->size);
+		do {
+			g_main_context_iteration(NULL, TRUE);
+		} while (g_main_context_pending(NULL));
+		cur_pdu->sent = true;
+	}
+}
+
+#define PDU_MTU_RESP pdu(ATT_OP_MTU_RESP, 0x17)
+#define PDU_FIND_INFO_REQ pdu(ATT_OP_FIND_INFO_REQ, 0x01, 0x00, 0xFF, 0xFF)
+#define PDU_IND_NODATA pdu(ATT_OP_HANDLE_IND, 0x01, 0x00)
+#define PDU_INVALID_IND pdu(ATT_OP_HANDLE_IND, 0x14)
+#define PDU_IND_DATA pdu(ATT_OP_HANDLE_IND, 0x14, 0x00, 0x01)
+
+static void notify_canary_expect(const guint8 *pdu, guint16 len, gpointer data)
+{
+	struct test_pdu *expected = data;
+	int cmp;
+
+	if (g_test_verbose())
+		util_hexdump('<', pdu, len, test_debug,
+						      "notify_canary_expect: ");
+
+	while (expected->valid && expected->received)
+		expected++;
+
+	g_assert(expected->valid);
+
+	if (g_test_verbose())
+		util_hexdump('?', expected->data, expected->size, test_debug,
+						      "notify_canary_expect: ");
+
+	g_assert_cmpint(expected->size, ==, len);
+
+	cmp = memcmp(pdu, expected->data, expected->size);
+
+	g_assert(cmp == 0);
+
+	expected->received = true;
+}
+
 static void test_register(struct context *cxt, gconstpointer user_data)
 {
-	/* TODO */
+	guint reg_id;
+	gboolean success;
+	struct test_pdu pdus[] = {
+		/* Unmatched by any (GATTRIB_ALL_EVENTS) */
+		PDU_MTU_RESP,
+		/*
+		 * Unmatched PDU opcode
+		 * Unmatched handle (GATTRIB_ALL_REQS) */
+		PDU_FIND_INFO_REQ,
+		/*
+		 * Matched PDU opcode
+		 * Unmatched handle (GATTRIB_ALL_HANDLES) */
+		PDU_IND_NODATA,
+		/*
+		 * Matched PDU opcode
+		 * Invalid length? */
+		PDU_INVALID_IND,
+		/*
+		 * Matched PDU opcode
+		 * Matched handle */
+		PDU_IND_DATA,
+		{ },
+	};
+	struct test_pdu req_pdus[] = { PDU_FIND_INFO_REQ, { } };
+	struct test_pdu all_ind_pdus[] = {
+		PDU_IND_NODATA,
+		PDU_INVALID_IND,
+		PDU_IND_DATA,
+		{ },
+	};
+	struct test_pdu followed_ind_pdus[] = { PDU_IND_DATA, { } };
+
+	/*
+	 * Without registering anything, should be able to ignore everything but
+	 * an unexpected response. */
+	send_test_pdus(cxt, pdus + 1);
+
+	reg_id = g_attrib_register(cxt->att, GATTRIB_ALL_EVENTS,
+				      GATTRIB_ALL_HANDLES, notify_canary_expect,
+								    pdus, NULL);
+
+	send_test_pdus(cxt, pdus);
+
+	g_attrib_unregister(cxt->att, reg_id);
+
+	if (g_test_verbose())
+		g_print("ALL_REQS, ALL_HANDLES\r\n");
+
+	reg_id = g_attrib_register(cxt->att, GATTRIB_ALL_REQS,
+				      GATTRIB_ALL_HANDLES, notify_canary_expect,
+								req_pdus, NULL);
+
+	send_test_pdus(cxt, pdus);
+
+	g_attrib_unregister(cxt->att, reg_id);
+
+	if (g_test_verbose())
+		g_print("IND, ALL_HANDLES\r\n");
+
+	reg_id = g_attrib_register(cxt->att, ATT_OP_HANDLE_IND,
+				      GATTRIB_ALL_HANDLES, notify_canary_expect,
+							    all_ind_pdus, NULL);
+
+	send_test_pdus(cxt, pdus);
+
+	g_attrib_unregister(cxt->att, reg_id);
+
+	if (g_test_verbose())
+		g_print("IND, 0x0014\r\n");
+
+	reg_id = g_attrib_register(cxt->att, ATT_OP_HANDLE_IND, 0x0014,
+					notify_canary_expect, followed_ind_pdus,
+									  NULL);
+
+	send_test_pdus(cxt, pdus);
+
+	g_attrib_unregister(cxt->att, reg_id);
+
+	success = g_attrib_unregister(cxt->att, reg_id);
+
+	g_assert(!success);
 }
 
 static void test_buffers(struct context *cxt, gconstpointer unused)
-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply related

* [PATCH BlueZ v2 5/7] Add unit tests for gattrib
From: Michael Janssen @ 2014-10-24 20:36 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Michael Janssen
In-Reply-To: <1414182983-23959-1-git-send-email-jamuraa@chromium.org>

This will ensure that the API behavior of gattrib is preserved.
---
 .gitignore          |   1 +
 Makefile.am         |   7 ++
 unit/test-gattrib.c | 350 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 358 insertions(+)
 create mode 100644 unit/test-gattrib.c

diff --git a/.gitignore b/.gitignore
index 97daa9f..164cc97 100644
--- a/.gitignore
+++ b/.gitignore
@@ -121,6 +121,7 @@ unit/test-avdtp
 unit/test-avctp
 unit/test-avrcp
 unit/test-gatt
+unit/test-gattrib
 unit/test-*.log
 unit/test-*.trs
 
diff --git a/Makefile.am b/Makefile.am
index 2dfea28..3fddb80 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -377,6 +377,13 @@ unit_test_gatt_SOURCES = unit/test-gatt.c
 unit_test_gatt_LDADD = src/libshared-glib.la \
 				lib/libbluetooth-internal.la @GLIB_LIBS@
 
+unit_tests += unit/test-gattrib
+
+unit_test_gattrib_SOURCES = unit/test-gattrib.c attrib/gattrib.c $(btio_sources) src/log.h src/log.c
+unit_test_gattrib_LDADD = lib/libbluetooth-internal.la \
+			src/libshared-glib.la \
+			@GLIB_LIBS@ @DBUS_LIBS@ -ldl -lrt
+
 if MAINTAINER_MODE
 noinst_PROGRAMS += $(unit_tests)
 endif
diff --git a/unit/test-gattrib.c b/unit/test-gattrib.c
new file mode 100644
index 0000000..23a5485
--- /dev/null
+++ b/unit/test-gattrib.c
@@ -0,0 +1,350 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google, Inc.
+ *
+ *
+ *  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "lib/uuid.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "src/log.h"
+
+#define DEFAULT_MTU 23
+
+#define data(args...) ((const unsigned char[]) { args })
+
+struct context {
+	GMainLoop *main_loop;
+	GIOChannel *att_io;
+	GIOChannel *server_io;
+	GAttrib *att;
+};
+
+static void setup_context(struct context *cxt, gconstpointer data)
+{
+	int err, sv[2];
+
+	cxt->main_loop = g_main_loop_new(NULL, FALSE);
+	g_assert(cxt->main_loop != NULL);
+
+	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+	g_assert(err == 0);
+
+	cxt->att_io = g_io_channel_unix_new(sv[0]);
+	g_assert(cxt->att_io != NULL);
+
+	g_io_channel_set_close_on_unref(cxt->att_io, TRUE);
+
+	cxt->server_io = g_io_channel_unix_new(sv[1]);
+	g_assert(cxt->server_io != NULL);
+
+	g_io_channel_set_close_on_unref(cxt->server_io, TRUE);
+	g_io_channel_set_encoding(cxt->server_io, NULL, NULL);
+	g_io_channel_set_buffered(cxt->server_io, FALSE);
+
+	cxt->att = g_attrib_new(cxt->att_io, DEFAULT_MTU);
+	g_assert(cxt->att != NULL);
+}
+
+static void teardown_context(struct context *cxt, gconstpointer data)
+{
+	if (cxt->att)
+		g_attrib_unref(cxt->att);
+
+	g_io_channel_unref(cxt->server_io);
+
+	g_io_channel_unref(cxt->att_io);
+
+	g_main_loop_unref(cxt->main_loop);
+}
+
+
+static void test_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	g_print("%s%s\n", prefix, str);
+}
+
+static void destroy_canary_increment(gpointer data)
+{
+	int *canary = data;
+	(*canary)++;
+}
+
+static void test_refcount(struct context *cxt, gconstpointer unused)
+{
+	GAttrib *extra_ref;
+	int destroy_canary;
+
+	g_attrib_set_destroy_function(cxt->att, destroy_canary_increment,
+							       &destroy_canary);
+
+	extra_ref = g_attrib_ref(cxt->att);
+
+	g_assert(extra_ref == cxt->att);
+
+	g_assert(destroy_canary == 0);
+
+	g_attrib_unref(extra_ref);
+
+	g_assert(destroy_canary == 0);
+
+	g_attrib_unref(cxt->att);
+
+	g_assert(destroy_canary == 1);
+
+	/* Avoid a double-free from the teardown function */
+	cxt->att = NULL;
+}
+
+static void test_get_channel(struct context *cxt, gconstpointer unused)
+{
+	GIOChannel *chan;
+
+	chan = g_attrib_get_channel(cxt->att);
+
+	g_assert(chan == cxt->att_io);
+}
+
+struct challenge_response {
+	const uint8_t *expect;
+	const size_t expect_len;
+	const uint8_t *respond;
+	const size_t respond_len;
+	bool received;
+};
+
+static gboolean test_client(GIOChannel *channel, GIOCondition cond,
+								  gpointer data)
+{
+	struct challenge_response *cr = data;
+	int fd;
+	uint8_t buf[256];
+	ssize_t len;
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		return FALSE;
+	}
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	len = read(fd, buf, sizeof(buf));
+
+	g_assert(len > 0);
+	g_assert_cmpint(len, ==, cr->expect_len);
+
+	if (g_test_verbose())
+		util_hexdump('>', buf, len, test_debug, "test_client: ");
+
+	cr->received = true;
+
+	if (cr->respond != NULL) {
+		if (g_test_verbose())
+			util_hexdump('<', cr->respond, cr->respond_len,
+						   test_debug, "test_client: ");
+		len = write(fd, cr->respond, cr->respond_len);
+
+		g_assert_cmpint(len, ==, cr->respond_len);
+	}
+
+	return TRUE;
+}
+
+struct result_data {
+	guint8 status;
+	const guint8 *pdu;
+	guint16 len;
+	bool done;
+};
+
+static void result_canary(guint8 status, const guint8 *pdu, guint16 len,
+								gpointer data)
+{
+	struct result_data *result = data;
+	result->status = status;
+	result->pdu = pdu;
+	result->len = len;
+
+	if (g_test_verbose())
+		util_hexdump('<', pdu, len, test_debug, "result_canary: ");
+
+	result->done = true;
+}
+
+static void test_send(struct context *cxt, gconstpointer unused)
+{
+	int cmp;
+	struct result_data results;
+	struct challenge_response data = {
+		.expect = data(0x02, 0x00, 0x02),
+		.expect_len = 3,
+		.respond = data(0x01, 0x02, 0x03, 0x04),
+		.respond_len = 4,
+		.received = false,
+	};
+
+	g_io_add_watch(cxt->server_io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+							    test_client, &data);
+
+	results.done = false;
+
+	g_attrib_send(cxt->att, 0, data.expect, data.expect_len, result_canary,
+						     (gpointer) &results, NULL);
+
+	/* Spin the main loop until we are ready. */
+	do {
+		g_main_context_iteration(NULL, TRUE);
+	} while (!results.done);
+
+	g_assert_cmpint(results.len, ==, data.respond_len);
+
+	cmp = memcmp(results.pdu, data.respond, results.len);
+
+	g_assert(cmp == 0);
+}
+
+static void test_cancel(struct context *cxt, gconstpointer unused)
+{
+	guint event_id;
+	gboolean canceled;
+	struct result_data results;
+	struct challenge_response data = {
+		.expect = data(0x02, 0x00, 0x02),
+		.expect_len = 3,
+		.respond = data(0x01, 0x02, 0x03, 0x04),
+		.respond_len = 4,
+		.received = false,
+	};
+
+	g_io_add_watch(cxt->server_io,
+				      G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+							    test_client, &data);
+
+	results.done = false;
+	event_id = g_attrib_send(cxt->att, 0, data.expect, data.expect_len,
+						 result_canary, &results, NULL);
+
+	/* Spin the main loop until we receive the first message */
+	do {
+		g_main_context_iteration(NULL, FALSE);
+	} while (!data.received);
+
+	canceled = g_attrib_cancel(cxt->att, event_id);
+	g_assert(canceled);
+
+	/*
+	 * Spin the main loop until all events are processed,
+	 * no result should be called because it was cancelled */
+	do {
+		g_main_context_iteration(NULL, TRUE);
+	} while (g_main_context_pending(NULL));
+
+	g_assert(!results.done);
+
+	results.done = false;
+	data.received = false;
+	event_id = g_attrib_send(cxt->att, 0, data.expect, data.expect_len,
+						 result_canary, &results, NULL);
+
+	canceled = g_attrib_cancel(cxt->att, event_id);
+	g_assert(canceled);
+
+	/*
+	 * Spin the main loop until all events are processed,
+	 * the message should never be delivered.
+	 */
+	do {
+		g_main_context_iteration(NULL, TRUE);
+	} while (g_main_context_pending(NULL));
+
+	g_assert(!data.received);
+	g_assert(!results.done);
+
+	/* Invalid ID */
+	canceled = g_attrib_cancel(cxt->att, 42);
+	g_assert(!canceled);
+}
+
+static void test_register(struct context *cxt, gconstpointer user_data)
+{
+	/* TODO */
+}
+
+static void test_buffers(struct context *cxt, gconstpointer unused)
+{
+	size_t buflen;
+	uint8_t *buf;
+	gboolean success;
+
+	buf = g_attrib_get_buffer(cxt->att, &buflen);
+	g_assert(buf != 0);
+	g_assert_cmpint(buflen, ==, DEFAULT_MTU);
+
+	success = g_attrib_set_mtu(cxt->att, 5);
+	g_assert(!success);
+
+	success = g_attrib_set_mtu(cxt->att, 255);
+	g_assert(success);
+
+	buf = g_attrib_get_buffer(cxt->att, &buflen);
+	g_assert(buf != 0);
+	g_assert_cmpint(buflen, ==, 255);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	__btd_log_init("*", 0);
+
+	/*
+	 * Test the GAttrib API behavior
+	 */
+	g_test_add("/gattrib/refcount", struct context, NULL, setup_context,
+					      test_refcount, teardown_context);
+	g_test_add("/gattrib/get_channel", struct context, NULL, setup_context,
+					    test_get_channel, teardown_context);
+	g_test_add("/gattrib/send", struct context, NULL, setup_context,
+						   test_send, teardown_context);
+	g_test_add("/gattrib/cancel", struct context, NULL, setup_context,
+						 test_cancel, teardown_context);
+	g_test_add("/gattrib/register", struct context, NULL, setup_context,
+					       test_register, teardown_context);
+	g_test_add("/gattrib/buffers", struct context, NULL, setup_context,
+						test_buffers, teardown_context);
+
+	return g_test_run();
+}
-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply related

* [PATCH BlueZ v2 4/7] attrib: remove g_attrib_is_encrypted
From: Michael Janssen @ 2014-10-24 20:36 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Michael Janssen
In-Reply-To: <1414182983-23959-1-git-send-email-jamuraa@chromium.org>

This function is only used in one place and encryption is the
responsibility of the channel, not the attribute.
---
 attrib/gattrib.c    | 12 ------------
 attrib/gattrib.h    |  2 --
 src/attrib-server.c |  9 +++++++--
 3 files changed, 7 insertions(+), 16 deletions(-)

diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index a6511a2..a65d1ca 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -690,18 +690,6 @@ static int event_cmp_by_id(gconstpointer a, gconstpointer b)
 	return evt->id - id;
 }
 
-gboolean g_attrib_is_encrypted(GAttrib *attrib)
-{
-	BtIOSecLevel sec_level;
-
-	if (!bt_io_get(attrib->io, NULL,
-			BT_IO_OPT_SEC_LEVEL, &sec_level,
-			BT_IO_OPT_INVALID))
-		return FALSE;
-
-	return sec_level > BT_IO_SEC_LOW;
-}
-
 gboolean g_attrib_unregister(GAttrib *attrib, guint id)
 {
 	struct event *evt;
diff --git a/attrib/gattrib.h b/attrib/gattrib.h
index 99b8b37..1557b99 100644
--- a/attrib/gattrib.h
+++ b/attrib/gattrib.h
@@ -62,8 +62,6 @@ guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle,
 				GAttribNotifyFunc func, gpointer user_data,
 				GDestroyNotify notify);
 
-gboolean g_attrib_is_encrypted(GAttrib *attrib);
-
 uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len);
 gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu);
 
diff --git a/src/attrib-server.c b/src/attrib-server.c
index e65fff2..1ccfc65 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -375,12 +375,17 @@ static struct attribute *attrib_db_add_new(struct gatt_server *server,
 static uint8_t att_check_reqs(struct gatt_channel *channel, uint8_t opcode,
 								int reqs)
 {
+	BtIOSecLevel sec_level;
+	GIOChannel *io = g_attrib_get_channel(channel->attrib);
+
 	/* FIXME: currently, it is assumed an encrypted link is enough for
 	 * authentication. This will allow to enable the SMP negotiation once
 	 * it is on upstream kernel. High security level should be mapped
 	 * to authentication and medium to encryption permission. */
-	if (!channel->encrypted)
-		channel->encrypted = g_attrib_is_encrypted(channel->attrib);
+	if (!channel->encrypted &&
+			    bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
+							     BT_IO_OPT_INVALID))
+		channel->encrypted = sec_level > BT_IO_SEC_LOW;
 	if (reqs == ATT_AUTHENTICATION && !channel->encrypted)
 		return ATT_ECODE_AUTHENTICATION;
 	else if (reqs == ATT_AUTHORIZATION)
-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply related

* [PATCH BlueZ v2 3/7] attrib: Add mtu argument to g_attrib_new
From: Michael Janssen @ 2014-10-24 20:36 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Michael Janssen
In-Reply-To: <1414182983-23959-1-git-send-email-jamuraa@chromium.org>

Instead of using the default MTU, use one passed
in by the user, and detect it from the channel when
it is created.
---
 attrib/gattrib.c     | 10 +++-------
 attrib/gattrib.h     |  2 +-
 attrib/gatttool.c    | 17 ++++++++++++++++-
 attrib/interactive.c | 17 ++++++++++++++++-
 src/device.c         | 11 ++++++++++-
 5 files changed, 46 insertions(+), 11 deletions(-)

diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index fa41ade..a6511a2 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -473,11 +473,9 @@ done:
 	return TRUE;
 }
 
-GAttrib *g_attrib_new(GIOChannel *io)
+GAttrib *g_attrib_new(GIOChannel *io, guint16 mtu)
 {
 	struct _GAttrib *attrib;
-	uint16_t att_mtu;
-	uint16_t cid;
 
 	g_io_channel_set_encoding(io, NULL, NULL);
 	g_io_channel_set_buffered(io, FALSE);
@@ -486,10 +484,8 @@ GAttrib *g_attrib_new(GIOChannel *io)
 	if (attrib == NULL)
 		return NULL;
 
-	att_mtu = ATT_DEFAULT_LE_MTU;
-
-	attrib->buf = g_malloc0(att_mtu);
-	attrib->buflen = att_mtu;
+	attrib->buf = g_malloc0(mtu);
+	attrib->buflen = mtu;
 
 	attrib->io = g_io_channel_ref(io);
 	attrib->requests = g_queue_new();
diff --git a/attrib/gattrib.h b/attrib/gattrib.h
index 18df2ad..99b8b37 100644
--- a/attrib/gattrib.h
+++ b/attrib/gattrib.h
@@ -42,7 +42,7 @@ typedef void (*GAttribDebugFunc)(const char *str, gpointer user_data);
 typedef void (*GAttribNotifyFunc)(const guint8 *pdu, guint16 len,
 							gpointer user_data);
 
-GAttrib *g_attrib_new(GIOChannel *io);
+GAttrib *g_attrib_new(GIOChannel *io, guint16 mtu);
 GAttrib *g_attrib_ref(GAttrib *attrib);
 void g_attrib_unref(GAttrib *attrib);
 
diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index db5da62..8f92d62 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -123,6 +123,9 @@ static gboolean listen_start(gpointer user_data)
 static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
 {
 	GAttrib *attrib;
+	uint16_t mtu;
+	uint16_t cid;
+	GError *gerr = NULL;
 
 	if (err) {
 		g_printerr("%s\n", err->message);
@@ -130,7 +133,19 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
 		g_main_loop_quit(event_loop);
 	}
 
-	attrib = g_attrib_new(io);
+	bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &mtu,
+				BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID);
+
+	if (gerr) {
+		g_printerr("Can't detect MTU, using default: %s", gerr->message);
+		g_error_free(gerr);
+		mtu = ATT_DEFAULT_LE_MTU;
+	}
+
+	if (cid == ATT_CID)
+		mtu = ATT_DEFAULT_LE_MTU;
+
+	attrib = g_attrib_new(io, mtu);
 
 	if (opt_listen)
 		g_idle_add(listen_start, attrib);
diff --git a/attrib/interactive.c b/attrib/interactive.c
index 08f39f7..7911ba5 100644
--- a/attrib/interactive.c
+++ b/attrib/interactive.c
@@ -150,13 +150,28 @@ static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
 
 static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
 {
+	uint16_t mtu;
+	uint16_t cid;
+
 	if (err) {
 		set_state(STATE_DISCONNECTED);
 		error("%s\n", err->message);
 		return;
 	}
 
-	attrib = g_attrib_new(iochannel);
+	bt_io_get(io, &err, BT_IO_OPT_IMTU, &mtu,
+				BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID);
+
+	if (err) {
+		g_printerr("Can't detect MTU, using default: %s", err->message);
+		g_error_free(err);
+		mtu = ATT_DEFAULT_LE_MTU;
+	}
+
+	if (cid == ATT_CID)
+		mtu = ATT_DEFAULT_LE_MTU;
+
+	attrib = g_attrib_new(iochannel, mtu);
 	g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, GATTRIB_ALL_HANDLES,
 						events_handler, attrib, NULL);
 	g_attrib_register(attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES,
diff --git a/src/device.c b/src/device.c
index 875a5c5..0925951 100644
--- a/src/device.c
+++ b/src/device.c
@@ -3627,11 +3627,17 @@ bool device_attach_attrib(struct btd_device *dev, GIOChannel *io)
 	GError *gerr = NULL;
 	GAttrib *attrib;
 	BtIOSecLevel sec_level;
+	uint16_t mtu;
+	uint16_t cid;
 
 	bt_io_get(io, &gerr, BT_IO_OPT_SEC_LEVEL, &sec_level,
+						BT_IO_OPT_IMTU, &mtu,
+						BT_IO_OPT_CID, &cid,
 						BT_IO_OPT_INVALID);
+
 	if (gerr) {
 		error("bt_io_get: %s", gerr->message);
+		mtu = ATT_DEFAULT_LE_MTU;
 		g_error_free(gerr);
 		return false;
 	}
@@ -3649,7 +3655,10 @@ bool device_attach_attrib(struct btd_device *dev, GIOChannel *io)
 		}
 	}
 
-	attrib = g_attrib_new(io);
+	if (cid == ATT_CID)
+		mtu = ATT_DEFAULT_LE_MTU;
+
+	attrib = g_attrib_new(io, mtu);
 	if (!attrib) {
 		error("Unable to create new GAttrib instance");
 		return false;
-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply related

* [PATCH BlueZ v2 2/7] attrib: Remove MTU-probing code
From: Michael Janssen @ 2014-10-24 20:36 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Michael Janssen
In-Reply-To: <1414182983-23959-1-git-send-email-jamuraa@chromium.org>

Probing for the MTU using bt_io is problematic for
testing because you cannot impersonate AF_BLUETOOTH sockets
with a socketpair.
---
 attrib/gattrib.c | 13 +------------
 1 file changed, 1 insertion(+), 12 deletions(-)

diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index 71d1cef..fa41ade 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -476,27 +476,17 @@ done:
 GAttrib *g_attrib_new(GIOChannel *io)
 {
 	struct _GAttrib *attrib;
-	uint16_t imtu;
 	uint16_t att_mtu;
 	uint16_t cid;
-	GError *gerr = NULL;
 
 	g_io_channel_set_encoding(io, NULL, NULL);
 	g_io_channel_set_buffered(io, FALSE);
 
-	bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu,
-				BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID);
-	if (gerr) {
-		error("%s", gerr->message);
-		g_error_free(gerr);
-		return NULL;
-	}
-
 	attrib = g_try_new0(struct _GAttrib, 1);
 	if (attrib == NULL)
 		return NULL;
 
-	att_mtu = (cid == ATT_CID) ? ATT_DEFAULT_LE_MTU : imtu;
+	att_mtu = ATT_DEFAULT_LE_MTU;
 
 	attrib->buf = g_malloc0(att_mtu);
 	attrib->buflen = att_mtu;
@@ -651,7 +641,6 @@ gboolean g_attrib_cancel_all(GAttrib *attrib)
 	return ret;
 }
 
-
 uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len)
 {
 	if (len == NULL)
-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply related

* [PATCH BlueZ v2 1/7] Remove unused g_attrib_set_debug function
From: Michael Janssen @ 2014-10-24 20:36 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Michael Janssen
In-Reply-To: <1414182983-23959-1-git-send-email-jamuraa@chromium.org>

This function is not used, and also not implemented.
---
 attrib/gattrib.c | 5 -----
 attrib/gattrib.h | 3 ---
 2 files changed, 8 deletions(-)

diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index 912dffb..71d1cef 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -651,11 +651,6 @@ gboolean g_attrib_cancel_all(GAttrib *attrib)
 	return ret;
 }
 
-gboolean g_attrib_set_debug(GAttrib *attrib,
-		GAttribDebugFunc func, gpointer user_data)
-{
-	return TRUE;
-}
 
 uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len)
 {
diff --git a/attrib/gattrib.h b/attrib/gattrib.h
index 3fe92c7..18df2ad 100644
--- a/attrib/gattrib.h
+++ b/attrib/gattrib.h
@@ -58,9 +58,6 @@ guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len,
 gboolean g_attrib_cancel(GAttrib *attrib, guint id);
 gboolean g_attrib_cancel_all(GAttrib *attrib);
 
-gboolean g_attrib_set_debug(GAttrib *attrib,
-		GAttribDebugFunc func, gpointer user_data);
-
 guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle,
 				GAttribNotifyFunc func, gpointer user_data,
 				GDestroyNotify notify);
-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply related

* [PATCH BlueZ v2 0/7] Unit tests for GAttrib
From: Michael Janssen @ 2014-10-24 20:36 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Michael Janssen

Since we want to replace GAttrib with a bt_att shim for a smooth transition 
to bt_att profiles, this patch cleans up the API and adds unit tests for
GAttrib functions.

This adds tests for all but the register / unregister functions, which
I will send in a separate patch since they are slightly more complicated due
to catchalls.

v1 -> v2:
 * Add g_attrib_register tests
 * Cleanup of unit/test-gattrib.c
 * Bugfix for GATTRIB_ALL_REQS

Michael Janssen (7):
  Remove unused g_attrib_set_debug function
  attrib: Remove MTU-probing code
  attrib: Add mtu argument to g_attrib_new
  attrib: remove g_attrib_is_encrypted
  Add unit tests for gattrib
  attrib: Add unit tests for g_attrib_register
  attrib: fix GATTRIB_ALL_REQS behavior

 .gitignore           |   1 +
 Makefile.am          |   7 +
 attrib/gattrib.c     |  61 +++----
 attrib/gattrib.h     |   7 +-
 attrib/gatttool.c    |  17 +-
 attrib/interactive.c |  17 +-
 src/attrib-server.c  |   9 +-
 src/device.c         |  11 +-
 unit/test-gattrib.c  | 505 +++++++++++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 588 insertions(+), 47 deletions(-)
 create mode 100644 unit/test-gattrib.c

-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply

* Re: [PATCHv5 00/14] Included service discovery
From: Arman Uguray @ 2014-10-24 19:32 UTC (permalink / raw)
  To: Luiz Augusto von Dentz
  Cc: Szymon Janc, Marcin Kraglak,
	linux-bluetooth@vger.kernel.org development
In-Reply-To: <CABBYNZKYSfpWCvK=3_VaKTz8nC-dWonkXesCibiuX1DPv+e3vA@mail.gmail.com>

Hi Luiz,

On Thu, Oct 23, 2014 at 12:55 AM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> Hi Szymon,
>
> On Wed, Oct 22, 2014 at 9:39 PM, Szymon Janc <szymon.janc@gmail.com> wrote:
>> Hi,
>>
>> On Wednesday 22 October 2014 08:35:05 Arman Uguray wrote:
>>> Hi Luiz,
>>>
>>> On Wed, Oct 22, 2014 at 7:54 AM, Luiz Augusto von Dentz
>>>
>>> <luiz.dentz@gmail.com> wrote:
>>> > Hi Marcin,
>>> >
>>> > On Wed, Oct 22, 2014 at 9:25 AM, Marcin Kraglak
>>> >
>>> > <marcin.kraglak@tieto.com> wrote:
>>> >> On 16 October 2014 12:17, Marcin Kraglak <marcin.kraglak@tieto.com>
>> wrote:
>>> >>> v3:
>>> >>> In this version after primary service discovery,
>>> >>> secondary services are discovered. Next included
>>> >>> services are resolved. With this approach we
>>> >>> don't have recursively search for included service,
>>> >>> like it was TODO in previous proposal.
>>> >>> There is also small coding style fix suggested by Arman.
>>> >>>
>>> >>> v4:
>>> >>> If no secondary services found, continue include services search (fixed
>>> >>> in gatt-client.c).
>>> >>> Fixed wrong debug logs (primary->secondary).
>>> >>> Fixed searching descriptors
>>> >>>
>>> >>> v5:
>>> >>> Ignore Unsupported Group Type Error in response to secondary service
>>> >>> discovery and continue included services discovery.
>>> >>>
>>> >>> Marcin Kraglak (14):
>>> >>>   shared/gatt: Add discover_secondary_services()
>>> >>>   shared/gatt: Add initial implementation of discover_included_services
>>> >>>   shared/gatt: Discover included services 128 bit UUIDS
>>> >>>   shared/gatt: Add extra check in characteristic iterator
>>> >>>   shared/gatt: Add included service iterator
>>> >>>   shared/gatt: Remove not needed function parameter
>>> >>>   shared/gatt: Distinguish Primary from Secondary services
>>> >>>   tools/btgatt-client: Print type of service
>>> >>>   shared/gatt: Discover secondary services
>>> >>>   shared/gatt: Discover included services
>>> >>>   shared/gatt: Add gatt-client include service iterator
>>> >>>   tools/btgatt-client: Print found include services
>>> >>>   shared/gatt: Fix searching descriptors
>>> >>>   shared/gatt: Add function bt_gatt_result_included_count()
>>> >>>
>>> >>>  src/shared/gatt-client.c  | 263 +++++++++++++++++++++++++++--
>>> >>>  src/shared/gatt-client.h  |  18 ++
>>> >>>  src/shared/gatt-helpers.c | 418
>>> >>>  +++++++++++++++++++++++++++++++++++++++++++---
>>> >>>  src/shared/gatt-helpers.h |  10 +-
>>> >>>  tools/btgatt-client.c     |  17 +-
>>> >>>  5 files changed, 690 insertions(+), 36 deletions(-)
>>> >>>
>>> >>> --
>>> >>> 1.9.3
>>> >
>>> > Patches looks fine to me, but I would like Arman to ack before applying.
>>>
>>> I'm fine with the patches as they are, though I saw that Szymon has
>>> left comments on some of them (I'm guessing he's a doing a pass of the
>>> patch set now). I'm good with it as long as his comments are
>>> addressed.
>>
>> I really think we should have more comments and less magic numbers there.
>> Otherwise this code will be hard to maintain in long term.
>
> Yep, we could actually start creating packed structs for PDU as it was
> done for AVRCP so we could do sizeof etc, it makes the code much
> easier to understand and maintain.
>

I agree, though I'd be happier if we added unit tests first. We can
then introduce packed structs and convert all code, though the unit
tests would at least give us confidence that we didn't accidentally
break any of the PDU encoding.

Cheers,
Arman

^ permalink raw reply

* Re: Attribute security permissions and CCC descriptors in shared/gatt-server.
From: Arman Uguray @ 2014-10-24 19:26 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: BlueZ development, Marcin Kraglak, Marcel Holtmann
In-Reply-To: <CABBYNZLE_xxoaPXZ=9LY2AwT-Pa_mqEYRD8v4_TgMTdo77j-Qw@mail.gmail.com>

Hi Luiz,

On Thu, Oct 23, 2014 at 12:51 AM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> Hi Arman,
>
> On Wed, Oct 22, 2014 at 11:12 PM, Arman Uguray <armansito@chromium.org> wrote:
>> Hi all,
>>
>> I have some unresolved questions about two aspects of
>> shared/gatt-server and I thought it might be better to discuss them
>> before moving on with the related parts of the implementation.
>>
>> *** 1. Who will be responsible for Attribute Permission checks? ***
>>
>> These mainly come in play when the remote end wants to read or write
>> the value of a characteristic/descriptor. We currently have read &
>> write callbacks that get assigned by the upper-layer to a
>> characteristic/descriptor definition in shared/gatt-db. Since these
>> callbacks already handle the read/write it initially made sense to me
>> that this is where encryption/authentication/authorization permission
>> checks can be performed. I then realized that there are cases where we
>> may want to perform this check before we invoke the read/write
>> callback. For instance, the Prepare Write request requires attribute
>> permission checks to be performed, however (in the current design) we
>> wouldn't actually call the write callback until a subsequent Execute
>> Write request is received.
>
> I believe this should be handle by the db, perhaps with a dedicated
> prepare function that should probably call a callback so the
> implementation can really tell when a write is about to happen. The
> weird thing here is that the gatt_db_add_characteristic take
> permissions but never really use it for anything except in
> gatt_db_get_attribute_permissions, I thought the idea would be to do
> the permission checks with it or Im missing something.
>

My impression was that gatt-db simple acts as a place to store the
permission data. It currently doesn't define a standard format for
storing those permissions and android uses the Android platform API's
permission enum values directly. I'm now defining standard values for
these, though again, gatt-db still would only be able to check for the
access permissions and not encryption/authentication/authorization
permissions, which should really be checked by bt_gatt_server since
it's already associated with the relevant bt_att.


>> The main problem here is the fact that shared/att is designed to be
>> transport agnostic: it doesn't know whether the underlying connection
>> is AF_BLUETOOTH or something else, so it can't make assumptions on the
>> nature of the connection to obtain security level information. We
>> could add some sort of delegate callback to this end, perhaps
>> something like:
>
> Then we should probably have some functions to set and get the
> security level, but Im sure it needs to be a callback as it does not
> looks like it gonna change without us noticing it, then we can
> automatically check with security level against the permissions
> provided by the profile when registering the characteristic.
>

Do you mean that you're sure it needs to be a callback or you're NOT sure? :)


>> static uint8_t my_sec_handler(void *user_data)
>> {
>>         ....
>>         return sec;
>> }
>> ...
>> bt_att_register_security_handler(att, my_sec_handler);
>>
>> And then bt_gatt_server can obtain the current security level like this:
>>
>> uint8_t sec = bt_att_get_security_level(att);
>>
>> where bt_att_get_security_level is implemented as:
>>
>> static uint8_t bt_att_get_security_level(struct bt_att *att)
>> {
>>       if (!att || !att->security_callback)
>>               return 0;
>>
>>       return att->security_callback(att->security_data);
>> }
>>
>>
>> I'm mostly thinking out loud here; would something like this make sense?
>>
>>
>> *** 2. Is shared/gatt-server responsible for keeping track of Client
>> Characteristic Configuration descriptor states for each bonded client?
>> ***
>>
>> Currently, the descriptor read/write operations need to be handled by
>> the upper layer via the read/write callbacks anyway, since the
>> side-effects of a writing to a particular CCC descriptor need to be
>> implemented by the related profile. In the bonding case, the
>> read/write callbacks could determine what value to return and how to
>> cache the value on a per-device basis without shared/gatt-db having a
>> notion of "per-client CCC descriptors".
>
> Well making gatt_db have the notion of CCC would make things a little
> bit simpler for storing and reloading but it would probably force us
> to rethink how the db access the values, perhaps the db could cache
> CCC values per client so it could detect changes and send
> notifications automatically whenever the profiles tell it values has
> changed or when a write succeeds.
>

Things start getting tricky here since automatically sending
notifications to the correct clients requires gatt-db to send these
through the correct bt_gatt_server/bt_att structures, which means then
it needs to somehow keep track of or access these. I think this goes
beyond what gatt-db was built for which is to just act as an attribute
cache.

Regardless, all profiles will already have to separately implement the
side effects of writing to a CCC descriptor and sending out the
notifications/indications when needed. That said, leaving the
management of which clients enabled notifications and which didn't
entirely to profiles will lead to a lot of duplicate code among
profile implementations.

One thing we could do is have bt_gatt_server keep track of CCC
descriptors, similar to how bt_gatt_client takes care of writing to a
remote CCC. Since bt_gatt_server is already a per-connection entity,
calling bt_gatt_server_send_notification could check to see if the
given handle matches a characteristic with a CCC descriptor and send
the notification only if that CCC descriptor was written earlier
through a write request through its internal bt_att.

Also, think of how all of this code will fit together in the future.
Take bluetoothd for example. It makes sense to me that a gatt-db will
be initialized by src/adapter, while there will be a separate instance
of bt_gatt_server for each src/device. So if a profile wants to notify
all connected GATT clients of a characteristic value with a single
function call, it'll probably have to call some API function through
src/adapter. So either there will need to be a higher-level entity
that owns gatt-db and has access to all bt_gatt_server instances, or
all bt_gatt_server instances will need to be registered with gatt-db
somehow. So, we will have to think of an interesting GATT-specific
client & server role profile API.

We should probably keep things all simple for now and revisit this in
the future. So I'm not going to focus on managing per-client CCC
descriptors for now and leave the notion of CCC up to the higher
levels of the stack until we have enough solid code to actually
implement something.


>> Does this make sense? If so, is there a point of keeping the "bdaddr_t
>> *bdaddr" arguments of gatt_db_read_t, gatt_db_write_t, gatt_db_read,
>> and gatt_db_write? Note that I'm currently passing NULL to all of
>> these calls for this parameter in shared/gatt-server, since bt_att
>> doesn't have a notion of a BD_ADDR.
>
> I guess this was inspired by Android, I would agree that if you
> register a characteristic and set the permission properly then it does
> not matter who is attempting to read/write as long as they are fulfill
> the requirements it should be okay but perhaps Android have pushed the
> notion of CCC up in the stack which force it to expose the remote
> address. Anyway for unit test I will have to address it since that
> will be using a socket pair, perhaps passing NULL is fine if we have
> CCC caching but in case of Android I don't think we can do much about
> it.
>

Well, in the Android case, could the device address be somehow
propagated as part of user_data?

>
> --
> Luiz Augusto von Dentz

Cheers,
Arman

^ permalink raw reply

* Re: GATT + DBus API Questions
From: Arman Uguray @ 2014-10-24 17:16 UTC (permalink / raw)
  To: Ryan Du Bois; +Cc: BlueZ development
In-Reply-To: <68B9F3E4-5167-413F-9907-7733B8B404B2@kamama.com>

Hi Ryan,

On Thu, Oct 23, 2014 at 5:21 PM, Ryan Du Bois <rdub@kamama.com> wrote:
> Hey Arman,
>
> On Oct 23, 2014, at 5:00 PM, Arman Uguray <armansito@chromium.org> wrote:
>
>> Hi Ryan,
>>
>> On Thu, Oct 23, 2014 at 4:41 PM, Ryan Du Bois <rdub@kamama.com> wrote:
>>> Hey There,
>>>
>>> I've been trying to create a GATT service via the DBus BlueZ API (org.bluez.GattManager1 + org.bluez.GattService1/GattCharacteristic1).  I'm currently using dbus-python to do this.
>>>
>>> What I have been able to accomplish thus far (with much exploring of the source code, and additional DBG() messages added to illuminate where I had been going wrong) is the following:
>>>        - Created proper DBus Proxy objects for GattService1 & GattCharacteristic1
>>>                - Each object vends the org.freedesktop.DBus.Properties interface, to allow BlueZ to retrieve the UUID and other properties on each instance.
>>>        - Created a proper ObjectManager proxy object to allow BlueZ to retrieve the aforementioned GattService1 and GattCharacteristic1 objects.
>>>        - Called GattManager1's RegisterService() method with appropriate arguments.
>>>        - Observed that BlueZ's GattManager1 instance calls my ObjectManager's GetManagedObjects() method, and receives the appropriate object paths/handles/properties.
>>>        - Observed that BlueZ accomplishes all the internal machinery necessary to register the GATT Service in src/gatt-dbus.c (namely, I get the "Added GATT service /com/kamama/blargh..." printout on the console, when running in debug mode).
>>>
>>> What I'm not able to see is the GATT service being listed when I explore the GATT services on my device via a BTLE exploration tool (I'm using LightBlue on Mac OS X to explore this device).
>>>
>>> Looking closer at the code, it looks to me like src/gatt-dbus.c is not hooked up to attrib/gatt-service.c, which is probably why my GATT Service and Characteristic are not showing up when exploring the device.
>>>
>>> Am I missing something, or is this not yet implemented?
>>>
>>> Thanks!
>>> --
>>> +Ryan Du Bois
>>> rdub@kamama.com
>>>
>>
>> As far as I know, gatt-dbus isn't actually wired up to attrib-server
>> as you said. So it populates an internal linked list of attributes but
>> then doesn't actually do anything over the listening socket it
>> creates.
>>
>> The thing is, we're currently in the process of rewriting all of
>> bluez's internal GATT/ATT tools and we'll probably entirely rewrite
>> src/gatt-dbus as well. An experimental implementation should appear
>> for both GATT client and server roles in the upcoming months, but
>> until then there's no good way to host GATT services over D-Bus.
>>
>> Thanks,
>> Arman
>
> Okay, that correlates with what I'm seeing. Most examples I've seen online
> have been implemented in C, calling gatt_service_add().
>
> In the time between now and a working DBus GATT API, would that
> approach be the proper method to host a GATT service with BlueZ?
> I'm on 5.21 at the moment, but could easily upgrade to something else
> if it's better for GATT services.
>

Yes, until there's a working GATT DBus API, you can follow the C examples.


> Thanks!
> --
> +Ryan Du Bois
> rdub@kamama.com
>

Cheers,
Arman

^ permalink raw reply

* Re: [PATCH BlueZ 6/8] shared/gatt-server: Implement "Read By Type" request.
From: Arman Uguray @ 2014-10-24 17:12 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth@vger.kernel.org
In-Reply-To: <CABBYNZLkhzGgdZk2YSxbhdKHXeX2f_2OZi6sXvcbj75P8NgRtw@mail.gmail.com>

Hi Luiz,


On Fri, Oct 24, 2014 at 2:25 AM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> Hi Arman,
>
> On Tue, Oct 21, 2014 at 12:00 AM, Arman Uguray <armansito@chromium.org> wrote:
>> This patch implements the ATT protocol "Read By Type" request for
>> shared/gatt-server. Logic is implemented that allows asynchronous
>> reading of non-standard attribute values via the registered read and
>> read completion callbacks.
>> ---
>>  src/shared/gatt-server.c | 273 ++++++++++++++++++++++++++++++++++++++++++++++-
>>  1 file changed, 270 insertions(+), 3 deletions(-)
>>
>> diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
>> index 3233b21..6c5ea2b 100644
>> --- a/src/shared/gatt-server.c
>> +++ b/src/shared/gatt-server.c
>> @@ -38,6 +38,16 @@
>>  #define MIN(a, b) ((a) < (b) ? (a) : (b))
>>  #endif
>>
>> +struct async_read_op {
>> +       struct bt_gatt_server *server;
>> +       uint8_t opcode;
>> +       bool done;
>> +       uint8_t *pdu;
>> +       size_t pdu_len;
>> +       int value_len;
>> +       struct queue *db_data;
>> +};
>> +
>>  struct bt_gatt_server {
>>         struct gatt_db *db;
>>         struct bt_att *att;
>> @@ -46,6 +56,9 @@ struct bt_gatt_server {
>>
>>         unsigned int mtu_id;
>>         unsigned int read_by_grp_type_id;
>> +       unsigned int read_by_type_id;
>> +
>> +       struct async_read_op *pending_read_op;
>>
>>         bt_gatt_server_debug_func_t debug_callback;
>>         bt_gatt_server_destroy_func_t debug_destroy;
>> @@ -59,8 +72,12 @@ static void bt_gatt_server_free(struct bt_gatt_server *server)
>>
>>         bt_att_unregister(server->att, server->mtu_id);
>>         bt_att_unregister(server->att, server->read_by_grp_type_id);
>> +       bt_att_unregister(server->att, server->read_by_type_id);
>>         bt_att_unref(server->att);
>>
>> +       if (server->pending_read_op)
>> +               server->pending_read_op->server = NULL;
>> +
>>         free(server);
>>  }
>>
>> @@ -124,21 +141,21 @@ static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q,
>>                  * value is seen.
>>                  */
>>                 if (iter == 0) {
>> -                       data_val_len = value_len;
>> +                       data_val_len = MIN(MIN(mtu - 6, 251), value_len);
>>                         pdu[0] = data_val_len + 4;
>>                         iter++;
>>                 } else if (value_len != data_val_len)
>>                         break;
>>
>>                 /* Stop if this unit would surpass the MTU */
>> -               if (iter + data_val_len + 4 > mtu)
>> +               if (iter + data_val_len + 4 > mtu - 1)
>>                         break;
>>
>>                 end_handle = gatt_db_get_end_handle(db, start_handle);
>>
>>                 put_le16(start_handle, pdu + iter);
>>                 put_le16(end_handle, pdu + iter + 2);
>> -               memcpy(pdu + iter + 4, value, value_len);
>> +               memcpy(pdu + iter + 4, value, data_val_len);
>>
>>                 iter += data_val_len + 4;
>>         }
>> @@ -235,6 +252,248 @@ done:
>>                                                         NULL, NULL, NULL);
>>  }
>>
>> +static void async_read_op_destroy(struct async_read_op *op)
>> +{
>> +       if (op->server)
>> +               op->server->pending_read_op = NULL;
>> +
>> +       queue_destroy(op->db_data, NULL);
>> +       free(op->pdu);
>> +       free(op);
>> +}
>> +
>> +static void process_read_by_type(struct async_read_op *op);
>> +
>> +static void read_by_type_read_complete_cb(uint16_t handle, uint16_t att_ecode,
>> +                                       const uint8_t *value, size_t len,
>> +                                       void *complete_data)
>> +{
>> +       struct async_read_op *op = complete_data;
>> +       struct bt_gatt_server *server = op->server;
>> +       uint16_t mtu;
>> +
>> +       if (!server) {
>> +               async_read_op_destroy(op);
>> +               return;
>> +       }
>> +
>> +       mtu = bt_att_get_mtu(server->att);
>> +
>> +       /* Terminate the operation if there was an error */
>> +       if (att_ecode) {
>> +               uint8_t pdu[4];
>> +
>> +               encode_error_rsp(BT_ATT_OP_READ_BY_TYPE_REQ, handle, att_ecode,
>> +                                                                       pdu);
>> +               bt_att_send(server->att, BT_ATT_OP_ERROR_RSP, pdu, 4, NULL,
>> +                                                               NULL, NULL);
>> +               async_read_op_destroy(op);
>> +               return;
>> +       }
>> +
>> +       if (op->pdu_len == 0) {
>> +               op->value_len = MIN(MIN(mtu - 4, 253), len);
>> +               op->pdu[0] = op->value_len + 2;
>> +               op->pdu_len++;
>> +       } else if (len != op->value_len) {
>> +               op->done = true;
>> +               goto done;
>> +       }
>> +
>> +       /* Stop if this would surpass the MTU */
>> +       if (op->pdu_len + op->value_len + 2 > mtu - 1) {
>> +               op->done = true;
>> +               goto done;
>> +       }
>> +
>> +       /* Encode the current value */
>> +       put_le16(handle, op->pdu + op->pdu_len);
>> +       memcpy(op->pdu + op->pdu_len + 2, value, op->value_len);
>> +
>> +       op->pdu_len += op->value_len + 2;
>> +
>> +       if (op->pdu_len == mtu - 1)
>> +               op->done = true;
>
> The code above is duplicated and it is actually causing some warnings
> comparing unsigned with signed (usually cause by '-' operation) which
> I started fixing myself but stop once I realize this was the result of
> gatt_db_read having 2 modes to read, sync and async, which I don't
> think is a good idea.
>

Yes, please see my response to your other comment in this patch set.


>> +done:
>> +       process_read_by_type(op);
>> +}
>> +
>> +static void process_read_by_type(struct async_read_op *op)
>> +{
>> +       struct bt_gatt_server *server = op->server;
>> +       uint16_t mtu = bt_att_get_mtu(server->att);
>> +       uint8_t rsp_opcode;
>> +       uint8_t rsp_len;
>> +       uint8_t ecode;
>> +       uint16_t ehandle;
>> +       uint16_t start_handle;
>> +       uint8_t *value;
>> +       int value_len;
>> +       uint32_t perm;
>> +
>> +       if (op->done) {
>> +               rsp_opcode = BT_ATT_OP_READ_BY_TYPE_RSP;
>> +               rsp_len = op->pdu_len;
>> +               goto done;
>> +       }
>> +
>> +       while (queue_peek_head(op->db_data)) {
>> +               start_handle = PTR_TO_UINT(queue_pop_head(op->db_data));
>> +               value = NULL;
>> +               value_len = 0;
>> +
>> +               if (!gatt_db_get_attribute_permissions(server->db, start_handle,
>> +                                                               &perm)) {
>> +                       ecode = BT_ATT_ERROR_UNLIKELY;
>> +                       goto error;
>> +               }
>> +
>> +               /*
>> +                * Check for the READ access permission. Encryption,
>> +                * authentication, and authorization permissions need to be
>> +                * checked by the read handler, since bt_att is agnostic to
>> +                * connection type and doesn't have security information on it.
>> +                */
>> +               if (perm && !(perm & BT_ATT_PERM_READ)) {
>> +                       ecode = BT_ATT_ERROR_READ_NOT_PERMITTED;
>> +                       goto error;
>> +               }
>
> I would suggest moving this check to gatt_db_read which should return
> -EPERM or the actual code if it is too hard to have errno mapping for
> each error code.
>

gatt_db currently has this nice property that the permissions field is
just a uint32_t. This means that while shared/gatt-server can use the
new permission macros I added to shared/att-types, the android code
can still use the Android platform definitions. Of course, once
android starts using shared/gatt-server it'll have to use the new
macros but for now we can have gatt-db support both types by keeping
the permission checks outside of gatt-db and this would make the whole
transition easier. Makes sense?


>> +               if (!gatt_db_read(server->db, start_handle, 0, op->opcode, NULL,
>> +                                       &value, &value_len,
>> +                                       read_by_type_read_complete_cb, op)) {
>> +                       ecode = BT_ATT_ERROR_UNLIKELY;
>> +                       goto error;
>> +               }
>> +
>> +               /* The read has been deferred to the upper layer if |value_len|
>> +                * is less than 0.
>> +                */
>> +               if (value_len < 0)
>> +                       return;
>> +
>> +               if (op->pdu_len == 0) {
>> +                       op->value_len = MIN(MIN(mtu - 4, 253), value_len);
>> +                       op->pdu[0] = op->value_len + 2;
>> +                       op->pdu_len++;
>> +               } else if (value_len != op->value_len)
>> +                       break;
>> +
>> +               /* Stop if this would surpass the MTU */
>> +               if (op->pdu_len + op->value_len + 2 > mtu - 1)
>> +                       break;
>> +
>> +               /* Encode the current value */
>> +               put_le16(start_handle, op->pdu + op->pdu_len);
>> +               memcpy(op->pdu + op->pdu_len + 2, value, op->value_len);
>> +
>> +               op->pdu_len += op->value_len + 2;
>> +
>> +               if (op->pdu_len == mtu - 1)
>> +                       break;
>> +       }
>> +
>> +       rsp_opcode = BT_ATT_OP_READ_BY_TYPE_RSP;
>> +       rsp_len = op->pdu_len;
>> +
>> +       goto done;
>> +
>> +error:
>> +       rsp_opcode = BT_ATT_OP_ERROR_RSP;
>> +       rsp_len = 4;
>> +       encode_error_rsp(BT_ATT_OP_READ_BY_TYPE_REQ, ehandle, ecode, op->pdu);
>> +
>> +done:
>> +       bt_att_send(server->att, rsp_opcode, op->pdu, rsp_len, NULL,
>> +                                                               NULL, NULL);
>> +       async_read_op_destroy(op);
>> +}
>> +
>> +static void read_by_type_cb(uint8_t opcode, const void *pdu,
>> +                                       uint16_t length, void *user_data)
>> +{
>> +       struct bt_gatt_server *server = user_data;
>> +       uint16_t start, end;
>> +       bt_uuid_t type;
>> +       uint8_t rsp_pdu[4];
>> +       uint16_t ehandle = 0;
>> +       uint8_t ecode;
>> +       struct queue *q = NULL;
>> +       struct async_read_op *op;
>> +
>> +       if (length != 6 && length != 20) {
>> +               ecode = BT_ATT_ERROR_INVALID_PDU;
>> +               goto error;
>> +       }
>> +
>> +       q = queue_new();
>> +       if (!q) {
>> +               ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
>> +               goto error;
>> +       }
>> +
>> +       start = get_le16(pdu);
>> +       end = get_le16(pdu + 2);
>> +       get_uuid_le(pdu + 4, length - 4, &type);
>> +
>> +       util_debug(server->debug_callback, server->debug_data,
>> +                               "Read By Type - start: 0x%04x end: 0x%04x",
>> +                               start, end);
>> +
>> +       if (!start || !end) {
>> +               ecode = BT_ATT_ERROR_INVALID_HANDLE;
>> +               goto error;
>> +       }
>> +
>> +       ehandle = start;
>> +
>> +       if (start > end) {
>> +               ecode = BT_ATT_ERROR_INVALID_HANDLE;
>> +               goto error;
>> +       }
>> +
>> +       gatt_db_read_by_type(server->db, start, end, type, q);
>> +
>> +       if (queue_isempty(q)) {
>> +               ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND;
>> +               goto error;
>> +       }
>> +
>> +       if (server->pending_read_op) {
>> +               ecode = BT_ATT_ERROR_UNLIKELY;
>> +               goto error;
>> +       }
>> +
>> +       op = new0(struct async_read_op, 1);
>> +       if (!op) {
>> +               ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
>> +               goto error;
>> +       }
>> +
>> +       op->pdu = malloc(bt_att_get_mtu(server->att));
>> +       if (!op->pdu) {
>> +               free(op);
>> +               ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
>> +               goto error;
>> +       }
>> +
>> +       op->opcode = opcode;
>> +       op->server = server;
>> +       op->db_data = q;
>> +       server->pending_read_op = op;
>> +
>> +       process_read_by_type(op);
>> +
>> +       return;
>> +
>> +error:
>> +       encode_error_rsp(opcode, ehandle, ecode, rsp_pdu);
>> +       queue_destroy(q, NULL);
>> +       bt_att_send(server->att, BT_ATT_OP_ERROR_RSP, rsp_pdu, 4,
>> +                                                       NULL, NULL, NULL);
>> +}
>> +
>>  static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
>>                                         uint16_t length, void *user_data)
>>  {
>> @@ -283,6 +542,14 @@ static bool gatt_server_register_att_handlers(struct bt_gatt_server *server)
>>         if (!server->read_by_grp_type_id)
>>                 return false;
>>
>> +       /* Read By Type */
>> +       server->read_by_type_id = bt_att_register(server->att,
>> +                                               BT_ATT_OP_READ_BY_TYPE_REQ,
>> +                                               read_by_type_cb,
>> +                                               server, NULL);
>> +       if (!server->read_by_type_id)
>> +               return false;
>> +
>>         return true;
>>  }
>>
>> --
>> 2.1.0.rc2.206.gedb03e5
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
>
>
> --
> Luiz Augusto von Dentz

Cheers,
Arman

^ permalink raw reply

* Re: [PATCH BlueZ 2/8] shared/gatt-db: Add complete callback to gatt_db_read.
From: Arman Uguray @ 2014-10-24 17:02 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: Arman Uguray, linux-bluetooth@vger.kernel.org
In-Reply-To: <CABBYNZ+rxN6GrQTi1Fh5NYdRb9FXgf-w5cQrb+LEBdVZpSzjWg@mail.gmail.com>

Hi Luiz,


On Fri, Oct 24, 2014 at 2:31 AM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> Hi Arman,
>
> On Tue, Oct 21, 2014 at 12:00 AM, Arman Uguray <armansito@chromium.org> wrote:
>> This patch introduces a completion callback parameter to gatt_db_read,
>> which is meant to be used by a gatt_db_read_t implementation to signal
>> the end of an asynchronous read operation performed in the upper layer.
>> ---
>>  android/gatt.c           | 21 ++++++++++++++++++---
>>  src/shared/gatt-db.c     |  7 +++++--
>>  src/shared/gatt-db.h     |  9 ++++++++-
>>  src/shared/gatt-server.c |  4 ++--
>>  4 files changed, 33 insertions(+), 8 deletions(-)
>>
>> diff --git a/android/gatt.c b/android/gatt.c
>> index ea20941..e2aa686 100644
>> --- a/android/gatt.c
>> +++ b/android/gatt.c
>> @@ -4694,7 +4694,8 @@ static void read_requested_attributes(void *data, void *user_data)
>>                                                 resp_data->offset,
>>                                                 process_data->opcode,
>>                                                 &process_data->device->bdaddr,
>> -                                               &value, &value_len))
>> +                                               &value, &value_len,
>> +                                               NULL, NULL))
>>                 error = ATT_ECODE_UNLIKELY;
>>
>>         /* We have value here already if no callback will be called */
>> @@ -4744,7 +4745,10 @@ static struct pending_trans_data *conn_add_transact(struct app_connection *conn,
>>  }
>>
>>  static void read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode,
>> -                                       bdaddr_t *bdaddr, void *user_data)
>> +                                       bdaddr_t *bdaddr,
>> +                                       gatt_db_read_complete_t complete_func,
>> +                                       void *complete_data,
>> +                                       void *user_data)
>>  {
>>         struct pending_trans_data *transaction;
>>         struct hal_ev_gatt_server_request_read ev;
>> @@ -6266,7 +6270,10 @@ static struct gap_srvc_handles gap_srvc_data;
>>  #define PERIPHERAL_PRIVACY_DISABLE 0x00
>>
>>  static void gap_read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode,
>> -                                       bdaddr_t *bdaddr, void *user_data)
>> +                                       bdaddr_t *bdaddr,
>> +                                       gatt_db_read_complete_t complete_func,
>> +                                       void *complete_data,
>> +                                       void *user_data)
>>  {
>>         struct pending_request *entry;
>>         struct gatt_device *dev;
>> @@ -6373,6 +6380,8 @@ static void register_gap_service(void)
>>
>>  static void device_info_read_cb(uint16_t handle, uint16_t offset,
>>                                         uint8_t att_opcode, bdaddr_t *bdaddr,
>> +                                       gatt_db_read_complete_t complete_func,
>> +                                       void *complete_data,
>>                                         void *user_data)
>>  {
>>         struct pending_request *entry;
>> @@ -6406,6 +6415,8 @@ done:
>>
>>  static void device_info_read_system_id_cb(uint16_t handle, uint16_t offset,
>>                                         uint8_t att_opcode, bdaddr_t *bdaddr,
>> +                                       gatt_db_read_complete_t complete_func,
>> +                                       void *complete_data,
>>                                         void *user_data)
>>  {
>>         struct pending_request *entry;
>> @@ -6438,6 +6449,8 @@ done:
>>
>>  static void device_info_read_pnp_id_cb(uint16_t handle, uint16_t offset,
>>                                         uint8_t att_opcode, bdaddr_t *bdaddr,
>> +                                       gatt_db_read_complete_t complete_func,
>> +                                       void *complete_data,
>>                                         void *user_data)
>>  {
>>         struct pending_request *entry;
>> @@ -6602,6 +6615,8 @@ static void gatt_srvc_change_write_cb(uint16_t handle, uint16_t offset,
>>
>>  static void gatt_srvc_change_read_cb(uint16_t handle, uint16_t offset,
>>                                         uint8_t att_opcode, bdaddr_t *bdaddr,
>> +                                       gatt_db_read_complete_t complete_func,
>> +                                       void *complete_data,
>>                                         void *user_data)
>>  {
>>         struct pending_request *entry;
>> diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
>> index b3f95d2..c342e32 100644
>> --- a/src/shared/gatt-db.c
>> +++ b/src/shared/gatt-db.c
>> @@ -638,7 +638,9 @@ static bool find_service_for_handle(const void *data, const void *user_data)
>>
>>  bool gatt_db_read(struct gatt_db *db, uint16_t handle, uint16_t offset,
>>                                 uint8_t att_opcode, bdaddr_t *bdaddr,
>> -                               uint8_t **value, int *length)
>> +                               uint8_t **value, int *length,
>> +                               gatt_db_read_complete_t complete_func,
>> +                               void *complete_data)
>>  {
>>         struct gatt_db_service *service;
>>         uint16_t service_handle;
>> @@ -665,7 +667,8 @@ bool gatt_db_read(struct gatt_db *db, uint16_t handle, uint16_t offset,
>>         if (a->read_func) {
>>                 *value = NULL;
>>                 *length = -1;
>> -               a->read_func(handle, offset, att_opcode, bdaddr, a->user_data);
>> +               a->read_func(handle, offset, att_opcode, bdaddr, complete_func,
>> +                                               complete_data, a->user_data);
>>         } else {
>>                 if (offset > a->value_len)
>>                         return false;
>> diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
>> index 8d18434..1c8739e 100644
>> --- a/src/shared/gatt-db.h
>> +++ b/src/shared/gatt-db.h
>> @@ -30,8 +30,13 @@ uint16_t gatt_db_add_service(struct gatt_db *db, const bt_uuid_t *uuid,
>>                                         bool primary, uint16_t num_handles);
>>  bool gatt_db_remove_service(struct gatt_db *db, uint16_t handle);
>>
>> +typedef void (*gatt_db_read_complete_t)(uint16_t handle, uint16_t att_ecode,
>> +                                       const uint8_t *value, size_t len,
>> +                                       void *complete_data);
>>  typedef void (*gatt_db_read_t) (uint16_t handle, uint16_t offset,
>>                                         uint8_t att_opcode, bdaddr_t *bdaddr,
>> +                                       gatt_db_read_complete_t complete_func,
>> +                                       void *complete_data,
>>                                         void *user_data);
>>
>>  typedef void (*gatt_db_write_t) (uint16_t handle, uint16_t offset,
>> @@ -81,7 +86,9 @@ void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,
>>
>>  bool gatt_db_read(struct gatt_db *db, uint16_t handle, uint16_t offset,
>>                                         uint8_t att_opcode, bdaddr_t *bdaddr,
>> -                                       uint8_t **value, int *length);
>> +                                       uint8_t **value, int *length,
>> +                                       gatt_db_read_complete_t complete_func,
>> +                                       void *complete_data);
>
> Lets remove value and length here and make it always return via
> callback, we can probably use a idle callback if there is a problem to
> return the values immediately.
>

I agree with this, since this'll get rid of a lot duplicate code, and
we can probably get away with not using an idle callback initially.
One problem though is that the android code makes use of these
functions and I don't have an Android build or devices set up to test
this. So would you mind taking on this task (or someone that has an
android build), change the gatt_db_read signature and make sure all
the android code works with passing unit tests, and then I'll rebase
my gatt-server patches on top of it?

Or if we don't want to hold back on my current patch set, you can
merge them and then do the refactor. Up to you.


>>  bool gatt_db_write(struct gatt_db *db, uint16_t handle, uint16_t offset,
>>                                         const uint8_t *value, size_t len,
>> diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
>> index 657b564..3233b21 100644
>> --- a/src/shared/gatt-server.c
>> +++ b/src/shared/gatt-server.c
>> @@ -114,8 +114,8 @@ static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q,
>>                  */
>>                 if (!gatt_db_read(db, start_handle, 0,
>>                                                 BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
>> -                                               NULL, &value,
>> -                                               &value_len) || value_len < 0)
>> +                                               NULL, &value, &value_len,
>> +                                               NULL, NULL) || value_len < 0)
>>                         return false;
>>
>>                 /*
>> --
>> 2.1.0.rc2.206.gedb03e5
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
>
>
> --
> Luiz Augusto von Dentz

Cheers,
Arman

^ permalink raw reply

* Re: [PATCH BlueZ] shared/gatt-db: Expose gatt_db_attribute
From: Arman Uguray @ 2014-10-24 16:56 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: BlueZ development
In-Reply-To: <1414158494-12020-1-git-send-email-luiz.dentz@gmail.com>

Hi Luiz,

On Fri, Oct 24, 2014 at 6:48 AM, Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
>
> This is just a draft API to reduce the lookups in gatt_db and make it
> a little bit more convenient for batch operations, so the general idea
> is to be able to get a hold of it via gatt_db_get_attribute but also
> replace the handles in the queues with proper attributes so the server
> code don't have to lookup again when reading/writing, checking
> permissions, or any other operation that can be done directly.
> ---
>  src/shared/gatt-db.h | 22 ++++++++++++++++++++++
>  1 file changed, 22 insertions(+)
>
> diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
> index 8d18434..ab7469a 100644
> --- a/src/shared/gatt-db.h
> +++ b/src/shared/gatt-db.h
> @@ -96,3 +96,25 @@ bool gatt_db_get_service_uuid(struct gatt_db *db, uint16_t handle,
>
>  bool gatt_db_get_attribute_permissions(struct gatt_db *db, uint16_t handle,
>                                                         uint32_t *permissions);
> +
> +struct gatt_db_attribute;
> +
> +struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *attrib,
> +                                       uint16_t handle, uint16_t offset);
> +

What's the offset parameter here for? Why not just get the attribute by handle?


> +const bt_uuid_t *gatt_db_attribute_get_type(struct gatt_db_attribute *attrib);
> +
> +bool gatt_db_attribute_get_service_uuid(struct gatt_db_attribute *attrib,
> +                                                       bt_uuid_t *uuid);
> +
> +bool gatt_db_attribute_get_permissions(struct gatt_db_attribute *attrib,
> +                                                       uint32_t *permissions);
> +
> +bool gatt_db_attribute_read(struct gatt_db_attribute *attrib,
> +                                       uint8_t opcode, bdaddr_t *bdaddr,
> +                                       void *callback, void *user_data);
> +
> +bool gatt_db_attribute_write(struct gatt_db_attribute *attrib,
> +                                       const uint8_t *value, size_t len,
> +                                       uint8_t opcode, bdaddr_t *bdaddr,
> +                                       void *callback, void *user_data);
> --

Are these functions meant to replace the existing gatt_db_read, etc?
We might as well just replace all of those with functions that accept
a struct gatt_db_attribute. So that all functions would be preceded by
a call to gatt_db_get_attribute, followed by one of these functions.
Unless you want to keep the other functions as helpers that accomplish
the same thing.

> 1.9.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

Cheers,
Arman

^ permalink raw reply

* [PATCH v2] android/pts: Interim PTS tests results for L2CAP
From: Sebastian Chlad @ 2014-10-24 15:45 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Sebastian Chlad

---
 android/pts-l2cap.txt | 60 +++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 44 insertions(+), 16 deletions(-)

diff --git a/android/pts-l2cap.txt b/android/pts-l2cap.txt
index 0746ea7..9706d69 100644
--- a/android/pts-l2cap.txt
+++ b/android/pts-l2cap.txt
@@ -1,7 +1,7 @@
 PTS test results for L2CAP
 
-PTS version: 5.2
-Tested: 14-August-2014
+PTS version: 5.3
+Tested: 24-October-2014
 Android version: 4.4.4
 Kernel version: 3.18
 
@@ -15,28 +15,37 @@ N/A    test is disabled due to PICS setup
 Test Name		Result	Notes
 -------------------------------------------------------------------------------
 TC_COS_CED_BV_01_C	PASS	l2test -n -P 4113 <bdaddr>
-TC_COS_CED_BV_03_C	PASS	l2test -w -N 1 -P 4113
-TC_COS_CED_BV_04_C	PASS	l2test -r -P 4113
-TC_COS_CED_BV_05_C	PASS	l2test -r -P 4113
-TC_COS_CED_BV_07_C	PASS	l2test -r -P 4113
-TC_COS_CED_BV_08_C	PASS	l2test -r -P 4113
+TC_COS_CED_BV_03_C	PASS	l2test -y -N 1 -P 4113 <bdaddr>
+TC_COS_CED_BV_04_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CED_BV_05_C	PASS	PTS issue #12351
+				btmgmt ssp off
+				l2test -r -P 4113
+TC_COS_CED_BV_07_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CED_BV_08_C	PASS	l2test -n -P 4113 <bdaddr>
 TC_COS_CED_BV_09_C	PASS	l2test -n -P 4113 <bdaddr>
 TC_COS_CED_BV_10_C	N/A
-TC_COS_CED_BV_11_C	PASS	l2test -r -P 4113
+TC_COS_CED_BV_11_C	PASS	l2test -u -P 4113 <bdaddr>
 TC_COS_CED_BI_01_C	PASS
-TC_COS_CFD_BV_01_C	PASS	l2test -r -P 4113
-TC_COS_CFD_BV_02_C	PASS	l2test -r -P 4113
-TC_COS_CFD_BV_03_C	PASS	l2test -r -P 4113
+TC_COS_CFD_BV_01_C	PASS	PTS issue #12351
+				btmgmt ssp off
+				l2test -r -P 4113
+TC_COS_CFD_BV_02_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CFD_BV_03_C	PASS	l2test -n -P 4113 <bdaddr>
 TC_COS_CFD_BV_08_C	PASS	l2test -n -P 4113 <bdaddr>
 TC_COS_CFD_BV_09_C	PASS	l2test -n -P 4113 <bdaddr>
 TC_COS_CFD_BV_10_C	N/A
-TC_COS_CFD_BI_11_C	PASS	l2test -r -P 4113
-TC_COS_CFD_BV_12_C	PASS	l2test -r -P 4113
+TC_COS_CFD_BV_11_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CFD_BV_12_C	PASS	l2test -n -P 4113 <bdaddr>
 TC_COS_CFD_BV_13_C	N/A
 TC_COS_IEX_BV_01_C	PASS	l2test -n -P 4113 <bdaddr>
 TC_COS_IEX_BV_02_C	PASS
 TC_COS_ECH_BV_01_C	PASS
 TC_COS_ECH_BV_02_C	PASS	l2ping -c 1 <bdaddr>
+TC_COS_CFC_BV_01_C	INC	JIRA #BA-181
+TC_COS_CFC_BV_02_C	INC	JIRA #BA-181
+TC_COS_CFC_BV_03_C	PASS	l2test -u -V le_public <bdaddr>
+TC_COS_CFC_BV_04_C	PASS	l2test -u -V le_public <bdaddr>
+TC_COS_CFC_BV_05_C	PASS	l2test -m -V le_public <bdaddr>
 TC_CLS_CLR_BV_01_C	N/A
 TC_CLS_UCD_BV_01_C	PASS
 TC_CLS_UCD_BV_02_C	PASS	l2test -s -G -N 1 -P 4113 <bdaddr>
@@ -75,10 +84,12 @@ TC_OFS_BV_01_C		PASS	l2test -x -X ertm -P 4113 -F 0 -N 1
 TC_OFS_BV_02_C		PASS	l2test -r -X ertm -P 4113 -F 0
 TC_OFS_BV_03_C		PASS	l2test -x -X streaming -P 4113 -F 0 -N 1
 TC_OFS_BV_04_C		PASS	l2test -r -X streaming -P 4113 -F 0
+				JIRA issue #BA-180
 TC_OFS_BV_05_C		PASS	l2test -x -X ertm -P 4113 -N 1
 TC_OFS_BV_06_C		PASS	l2test -r -X ertm -P 4113
 TC_OFS_BV_07_C		PASS	l2test -x -X streaming -P 4113 -F 0 -N 1
 TC_OFS_BV_08_C		PASS	l2test -r -X streaming -P 4113
+				JIRA issue #BA-180
 TC_ERM_BV_01_C		PASS	l2test -x -X ertm -P 4113 -N 3 -Y 3
 TC_ERM_BV_02_C		PASS	l2test -r -X ertm -P 4113
 TC_ERM_BV_03_C		PASS	l2test -r -X ertm -P 4113
@@ -92,7 +103,7 @@ TC_ERM_BV_11_C		PASS	l2test -x -X ertm -P 4113 -N 1 -Q 1
 TC_ERM_BV_12_C		PASS	l2test -x -X ertm -P 4113 -R -N 1 -Q 1
 TC_ERM_BV_13_C		PASS	l2test -x -X ertm -P 4113 -N 2
 TC_ERM_BV_14_C		PASS	l2test -x -X ertm -P 4113 -N 4
-TC_ERM_BV_15_C		PASS	l2test -X ertm -P 4113 -N 4
+TC_ERM_BV_15_C		PASS	l2test -x -X ertm -P 4113 -N 4
 TC_ERM_BV_16_C		N/A
 TC_ERM_BV_17_C		PASS	l2test -X ertm -P 4113
 TC_ERM_BV_18_C		PASS	l2test -x -X ertm -P 4113 -N 1
@@ -147,6 +158,23 @@ TC_ECF_BV_08_C		N/A
 TC_LE_CPU_BV_01_C	PASS	l2test -r -V le_public -J 4
 TC_LE_CPU_BV_02_C	PASS	l2test -n -V le_public -J 4 <braddr>
 TC_LE_CPU_BI_01_C	PASS	l2test -n -V le_public -J 4 <braddr>
-TC_LE_CPU_BI_02_C	INC	PTS issue #12339
+TC_LE_CPU_BI_02_C	PASS	PTS issue #12339
+				Note: use Common.dll and ETS for L2CAP
+				from #12339
 				l2test -r -V le_public -J 4
-TC_LE_REJ_BV_01_C	PASS	l2test -n -V le_public -J 4 <braddr>
+TC_LE_REJ_BI_01_C	PASS	l2test -n -V le_public -J 4 <braddr>
+TC_LE_REJ_BI_02_C	PASS	l2test -n -V le_public -J 4 <braddr>
+TC_LE_CFC_BV_01_C	PASS	l2test -n -V le_public -P 37 <braddr>
+TC_LE_CFC_BV_02_C	PASS	l2test -n -V le_public -P 37 <braddr>
+TC_LE_CFC_BV_03_C	INC	PTS issue #12665
+TC_LE_CFC_BV_04_C	PASS	l2test -n -V le_public -P 241  <braddr>
+TC_LE_CFC_BV_05_C	INC	PTS issue #12665
+TC_LE_CFC_BV_06_C	INC	JIRA #BA-182
+TC_LE_CFC_BV_07_C	INC	JIRA #BA-183
+TC_LE_CFC_BI_01_C	INC	JIRA #BA-183
+TC_LE_CFC_BV_08_C	PASS	l2test -n -V le_public -P 37 <braddr>
+TC_LE_CFC_BV_09_C	PASS	l2test -n -V le_public -P 37 <braddr>
+TC_LE_CFC_BV_16_C	PASS	l2test -n -V le_public -P 37 <braddr>
+TC_LE_CFC_BV_17_C	N/A
+TC_LE_CID_BV_01_C	INC
+TC_LE_CID_BV_02_I	INC
-- 
1.8.5.3


^ permalink raw reply related

* [PATCH] android/pts: Interim PTS tests results for L2CAP
From: Sebastian Chlad @ 2014-10-24 14:23 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Sebastian Chlad

---
 android/pts-l2cap.txt | 60 +++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 44 insertions(+), 16 deletions(-)

diff --git a/android/pts-l2cap.txt b/android/pts-l2cap.txt
index 0746ea7..3c664f7 100644
--- a/android/pts-l2cap.txt
+++ b/android/pts-l2cap.txt
@@ -1,7 +1,7 @@
 PTS test results for L2CAP
 
-PTS version: 5.2
-Tested: 14-August-2014
+PTS version: 5.3
+Tested: 24-October-2014
 Android version: 4.4.4
 Kernel version: 3.18
 
@@ -15,28 +15,37 @@ N/A    test is disabled due to PICS setup
 Test Name		Result	Notes
 -------------------------------------------------------------------------------
 TC_COS_CED_BV_01_C	PASS	l2test -n -P 4113 <bdaddr>
-TC_COS_CED_BV_03_C	PASS	l2test -w -N 1 -P 4113
-TC_COS_CED_BV_04_C	PASS	l2test -r -P 4113
-TC_COS_CED_BV_05_C	PASS	l2test -r -P 4113
-TC_COS_CED_BV_07_C	PASS	l2test -r -P 4113
-TC_COS_CED_BV_08_C	PASS	l2test -r -P 4113
+TC_COS_CED_BV_03_C	PASS	l2test -y -N 1 -P 4113 <bdaddr>
+TC_COS_CED_BV_04_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CED_BV_05_C	PASS	PTS issue #12351
+				btmgmt ssp off
+				l2test -r -P 4113
+TC_COS_CED_BV_07_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CED_BV_08_C	PASS	l2test -n -P 4113 <bdaddr>
 TC_COS_CED_BV_09_C	PASS	l2test -n -P 4113 <bdaddr>
 TC_COS_CED_BV_10_C	N/A
-TC_COS_CED_BV_11_C	PASS	l2test -r -P 4113
+TC_COS_CED_BV_11_C	PASS	l2test -u -P 4113 <bdaddr>
 TC_COS_CED_BI_01_C	PASS
-TC_COS_CFD_BV_01_C	PASS	l2test -r -P 4113
-TC_COS_CFD_BV_02_C	PASS	l2test -r -P 4113
-TC_COS_CFD_BV_03_C	PASS	l2test -r -P 4113
+TC_COS_CFD_BV_01_C	PASS	PTS issue #12351
+				btmgmt ssp off
+				l2test -r -P 4113
+TC_COS_CFD_BV_02_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CFD_BV_03_C	PASS	l2test -n -P 4113 <bdaddr>
 TC_COS_CFD_BV_08_C	PASS	l2test -n -P 4113 <bdaddr>
 TC_COS_CFD_BV_09_C	PASS	l2test -n -P 4113 <bdaddr>
 TC_COS_CFD_BV_10_C	N/A
-TC_COS_CFD_BI_11_C	PASS	l2test -r -P 4113
-TC_COS_CFD_BV_12_C	PASS	l2test -r -P 4113
+TC_COS_CFD_BV_11_C	PASS	l2test -n -P 4113 <bdaddr>
+TC_COS_CFD_BV_12_C	PASS	l2test -n -P 4113 <bdaddr>
 TC_COS_CFD_BV_13_C	N/A
 TC_COS_IEX_BV_01_C	PASS	l2test -n -P 4113 <bdaddr>
 TC_COS_IEX_BV_02_C	PASS
 TC_COS_ECH_BV_01_C	PASS
 TC_COS_ECH_BV_02_C	PASS	l2ping -c 1 <bdaddr>
+TC_COS_CFC_BV_01_C	INC	JIRA #BA-181
+TC_COS_CFC_BV_02_C	INC	JIRA #BA-181
+TC_COS_CFC_BV_03_C	PASS	l2test -u -V le_public 00:1B:dc:07:2f:f0
+TC_COS_CFC_BV_04_C	PASS	l2test -u -V le_public 00:1B:dc:07:2f:f0
+TC_COS_CFC_BV_05_C	PASS	l2test -m -V le_public 00:1B:dc:07:2f:f0
 TC_CLS_CLR_BV_01_C	N/A
 TC_CLS_UCD_BV_01_C	PASS
 TC_CLS_UCD_BV_02_C	PASS	l2test -s -G -N 1 -P 4113 <bdaddr>
@@ -75,10 +84,12 @@ TC_OFS_BV_01_C		PASS	l2test -x -X ertm -P 4113 -F 0 -N 1
 TC_OFS_BV_02_C		PASS	l2test -r -X ertm -P 4113 -F 0
 TC_OFS_BV_03_C		PASS	l2test -x -X streaming -P 4113 -F 0 -N 1
 TC_OFS_BV_04_C		PASS	l2test -r -X streaming -P 4113 -F 0
+				JIRA issue #BA-180
 TC_OFS_BV_05_C		PASS	l2test -x -X ertm -P 4113 -N 1
 TC_OFS_BV_06_C		PASS	l2test -r -X ertm -P 4113
 TC_OFS_BV_07_C		PASS	l2test -x -X streaming -P 4113 -F 0 -N 1
 TC_OFS_BV_08_C		PASS	l2test -r -X streaming -P 4113
+				JIRA issue #BA-180
 TC_ERM_BV_01_C		PASS	l2test -x -X ertm -P 4113 -N 3 -Y 3
 TC_ERM_BV_02_C		PASS	l2test -r -X ertm -P 4113
 TC_ERM_BV_03_C		PASS	l2test -r -X ertm -P 4113
@@ -92,7 +103,7 @@ TC_ERM_BV_11_C		PASS	l2test -x -X ertm -P 4113 -N 1 -Q 1
 TC_ERM_BV_12_C		PASS	l2test -x -X ertm -P 4113 -R -N 1 -Q 1
 TC_ERM_BV_13_C		PASS	l2test -x -X ertm -P 4113 -N 2
 TC_ERM_BV_14_C		PASS	l2test -x -X ertm -P 4113 -N 4
-TC_ERM_BV_15_C		PASS	l2test -X ertm -P 4113 -N 4
+TC_ERM_BV_15_C		PASS	l2test -x -X ertm -P 4113 -N 4
 TC_ERM_BV_16_C		N/A
 TC_ERM_BV_17_C		PASS	l2test -X ertm -P 4113
 TC_ERM_BV_18_C		PASS	l2test -x -X ertm -P 4113 -N 1
@@ -147,6 +158,23 @@ TC_ECF_BV_08_C		N/A
 TC_LE_CPU_BV_01_C	PASS	l2test -r -V le_public -J 4
 TC_LE_CPU_BV_02_C	PASS	l2test -n -V le_public -J 4 <braddr>
 TC_LE_CPU_BI_01_C	PASS	l2test -n -V le_public -J 4 <braddr>
-TC_LE_CPU_BI_02_C	INC	PTS issue #12339
+TC_LE_CPU_BI_02_C	PASS	PTS issue #12339
+				Note: use Common.dll and ETS for L2CAP
+				from #12339
 				l2test -r -V le_public -J 4
-TC_LE_REJ_BV_01_C	PASS	l2test -n -V le_public -J 4 <braddr>
+TC_LE_REJ_BI_01_C	PASS	l2test -n -V le_public -J 4 <braddr>
+TC_LE_REJ_BI_02_C	PASS	l2test -n -V le_public -J 4 <braddr>
+TC_LE_CFC_BV_01_C	PASS	l2test -n -V le_public -P 37 <braddr>
+TC_LE_CFC_BV_02_C	PASS	l2test -n -V le_public -P 37 <braddr>
+TC_LE_CFC_BV_03_C	INC	PTS issue #12665
+TC_LE_CFC_BV_04_C	PASS	l2test -n -V le_public -P 241  <braddr>
+TC_LE_CFC_BV_05_C	INC	PTS issue #12665
+TC_LE_CFC_BV_06_C	INC	JIRA #BA-182
+TC_LE_CFC_BV_07_C	INC	JIRA #BA-183
+TC_LE_CFC_BI_01_C	INC	JIRA #BA-183
+TC_LE_CFC_BV_08_C	PASS	l2test -n -V le_public -P 37 <braddr>
+TC_LE_CFC_BV_09_C	PASS	l2test -n -V le_public -P 37 <braddr>
+TC_LE_CFC_BV_16_C	PASS	l2test -n -V le_public -P 37 <braddr>
+TC_LE_CFC_BV_17_C	N/A
+TC_LE_CID_BV_01_C	INC
+TC_LE_CID_BV_02_I	INC
-- 
1.8.5.3


^ permalink raw reply related

* [bluetooth] btusb duplicate filtering
From: Patrick Shirkey @ 2014-10-24 14:22 UTC (permalink / raw)
  To: linux-bluetooth

Hi,

I am seeing the following behaviour with an android device running the
8723au chipset with bluez + backports + btusb.c (new branch).

Step 1:

1: enable BT via settings UI
2: open BLE scanner app
3: app finds all BLE devices

Step 2:

4: close BLE scanner app
5: open BLE scanner app
6: app cannot find any BLE device


- Szymon has suggested it is related to duplicate filtering and may be
handled at the driver or chipset firmware level.

- I found this post which might provide some useful background info.

http://stackoverflow.com/questions/19502853/android-4-3-ble-filtering-behaviour-of-startlescan

- My question is can I do anything at the driver/kernel/bluez level so
that BLE devices always show up?


LOGS

- I see the following output in logcat:

Step 1:


10-24 07:06:27.450 D/TAG     (13289): ~ Starting Scan
10-24 07:06:27.450 D/BluetoothAdapter(13289): startLeScan(): null
10-24 07:06:27.450 D/BtGatt.GattService(13351): registerClient() -
UUID=3ed977cb-dddb-4518-af26-eb41adb97c38
10-24 07:06:27.450 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/gatt.c:handle_client_register()
10-24 07:06:27.460 D/BtGatt.GattService(13351): onClientRegistered() -
UUID=3ed977cb-dddb-4518-af26-eb41adb97c38, clientIf=1
10-24 07:06:27.460 D/BluetoothAdapter(13289): onClientRegistered() -
status=0 clientIf=1
10-24 07:06:27.460 D/BtGatt.GattService(13351): startScan() - queue=0
10-24 07:06:27.460 D/BtGatt.GattService(13351): startScan() - adding client=1
10-24 07:06:27.470 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/gatt.c:handle_client_scan() new state 1
10-24 07:06:27.470 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:start_discovery() type=0x6
10-24 07:06:27.470 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:mgmt_discovering_event() type
6 discovering 1
10-24 07:06:27.480 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:check_discovery_state() 6 0
10-24 07:06:27.510 W/InputMethodManagerService( 1612): Window already
focused, ignoring focus gain of:
com.android.internal.view.IInputMethodClient$Stub$Proxy@41a51080
attribute=null, token = android.os.BinderProxy@41aa49c0
10-24 07:06:27.530 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:mgmt_device_found_event()
hci0 addr BC:6A:29:AB:2C:AA, rssi -36 flags 0x0000 eir_len 25
10-24 07:06:27.530 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/gatt.c:le_device_found_handler() LE
Device found: BC:6A:29:AB:2C:AA, rssi: -36, adv_data: 1
10-24 07:06:27.540 D/BlueZ   (13351):
external/bluetooth/bluez/android/hal-bluetooth.c:handle_device_found()
10-24 07:06:27.550 D/BlueZ   (13351):
external/bluetooth/bluez/android/hal-bluetooth.c:device_props_to_hal()
prop[0]: type=BT_PROPERTY_BDADDR len=6 val=bc:6a:29:ab:2c:aa
10-24 07:06:27.550 D/BlueZ   (13351):
external/bluetooth/bluez/android/hal-bluetooth.c:device_props_to_hal()
prop[1]: type=BT_PROPERTY_TYPE_OF_DEVICE len=4 val=BT_DEVICE_DEVTYPE_BLE
10-24 07:06:27.550 D/BlueZ   (13351):
external/bluetooth/bluez/android/hal-bluetooth.c:device_props_to_hal()
prop[2]: type=BT_PROPERTY_REMOTE_RSSI len=4 val=220
10-24 07:06:27.550 D/BlueZ   (13351):
external/bluetooth/bluez/android/hal-bluetooth.c:device_props_to_hal()
prop[3]: type=BT_PROPERTY_BDNAME len=17 val=BC:6A:29:AB:2C:AA
10-24 07:06:27.580 D/BtGatt.GattService(13351): onScanResult() -
address=BC:6A:29:AB:2C:AA, rssi=-36
10-24 07:06:27.580 D/BluetoothAdapter(13289): onScanResult() -
Device=BC:6A:29:AB:2C:AA RSSI=-36
10-24 07:06:27.690 D/BluetoothEventManager( 1767): DeviceFoundHandler
created new CachedBluetoothDevice: BC:6A:29:AB:2C:AA
10-24 07:06:27.730 D/android.widget.GridLayout(13289): horizontal
constraints: x2-x0>=376, x2-x1<=187, x1-x0<=81 are inconsistent;
permanently removing: x2-x1<=187.
10-24 07:06:37.720 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:mgmt_device_found_event()
hci0 addr BC:6A:29:AB:2D:17, rssi -48 flags 0x0000 eir_len 25
10-24 07:06:37.720 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/gatt.c:le_device_found_handler() LE
Device found: BC:6A:29:AB:2D:17, rssi: -48, adv_data: 1
10-24 07:06:37.730 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:mgmt_discovering_event() type
6 discovering 0
10-24 07:06:37.730 D/BlueZ   (13351):
external/bluetooth/bluez/android/hal-bluetooth.c:handle_device_found()
10-24 07:06:37.730 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:check_discovery_state() 0 6
10-24 07:06:37.730 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:start_discovery() type=0x6
10-24 07:06:37.740 D/BlueZ   (13351):
external/bluetooth/bluez/android/hal-bluetooth.c:device_props_to_hal()
prop[0]: type=BT_PROPERTY_BDADDR len=6 val=bc:6a:29:ab:2d:17
10-24 07:06:37.740 D/BlueZ   (13351):
external/bluetooth/bluez/android/hal-bluetooth.c:device_props_to_hal()
prop[1]: type=BT_PROPERTY_TYPE_OF_DEVICE len=4 val=BT_DEVICE_DEVTYPE_BLE
10-24 07:06:37.740 D/BlueZ   (13351):
external/bluetooth/bluez/android/hal-bluetooth.c:device_props_to_hal()
prop[2]: type=BT_PROPERTY_REMOTE_RSSI len=4 val=208
10-24 07:06:37.740 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:mgmt_discovering_event() type
6 discovering 1
10-24 07:06:37.740 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:check_discovery_state() 6 0
10-24 07:06:37.750 D/BlueZ   (13351):
external/bluetooth/bluez/android/hal-bluetooth.c:device_props_to_hal()
prop[3]: type=BT_PROPERTY_BDNAME len=17 val=BC:6A:29:AB:2D:17
10-24 07:06:37.770 D/BtGatt.GattService(13351): onScanResult() -
address=BC:6A:29:AB:2D:17, rssi=-48
10-24 07:06:37.780 D/BluetoothAdapter(13289): onScanResult() -
Device=BC:6A:29:AB:2D:17 RSSI=-48
10-24 07:06:37.870 D/android.widget.GridLayout(13289): horizontal
constraints: x2-x0>=376, x2-x1<=187, x1-x0<=81 are inconsistent;
permanently removing: x2-x1<=187.
10-24 07:06:37.870 D/BluetoothEventManager( 1767): DeviceFoundHandler
created new CachedBluetoothDevice: BC:6A:29:AB:2D:17



Step 2:



10-24 07:15:09.150 D/TAG     (13289): ~ Starting Scan
10-24 07:15:09.150 D/BluetoothAdapter(13289): startLeScan(): null
10-24 07:15:09.170 D/BtGatt.GattService(13351): registerClient() -
UUID=e423968e-1bfe-4822-a62b-83804fb6998d
10-24 07:15:09.170 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/gatt.c:handle_client_register()
10-24 07:15:09.170 D/BtGatt.GattService(13351): onClientRegistered() -
UUID=e423968e-1bfe-4822-a62b-83804fb6998d, clientIf=2
10-24 07:15:09.170 D/BluetoothAdapter(13289): onClientRegistered() -
status=0 clientIf=2
10-24 07:15:09.170 D/BtGatt.GattService(13351): startScan() - queue=0
10-24 07:15:09.180 D/BtGatt.GattService(13351): startScan() - adding client=2
10-24 07:15:09.180 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/gatt.c:handle_client_scan() new state 1
10-24 07:15:09.180 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:start_discovery() type=0x6
10-24 07:15:09.190 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:mgmt_discovering_event() type
6 discovering 1
10-24 07:15:09.190 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:check_discovery_state() 6 0
10-24 07:15:09.250 W/InputMethodManagerService( 1612): Window already
focused, ignoring focus gain of:
com.android.internal.view.IInputMethodClient$Stub$Proxy@41b85ab8
attribute=null, token = android.os.BinderProxy@41afac00
10-24 07:15:19.440 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:mgmt_discovering_event() type
6 discovering 0
10-24 07:15:19.440 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:check_discovery_state() 0 6
10-24 07:15:19.450 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:start_discovery() type=0x6
10-24 07:15:19.460 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:mgmt_discovering_event() type
6 discovering 1
10-24 07:15:19.460 I/bluetoothd(13365): bluetoothd[13366]:
external/bluetooth/bluez/android/bluetooth.c:check_discovery_state() 6 0


- I see the following output in btmon:

Step 1:

# btmon
Bluetooth monitor ver 5.23
= New Index: CC:D2:9C:73:CB:45 (BR/EDR,USB,hci0)                [hci0]
0.921673
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2          [hci0]
6.885250
        Scanning: Disabled (0x00)
        Filter duplicates: Disabled (0x00)
> HCI Event: Command Complete (0x0e) plen 4                     [hci0]
6.886777
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x00 (6)
< HCI Command: LE Set Random Address (0x08|0x0005) plen 6       [hci0]
6.891384
        Address: 33:31:38:D9:1B:2E (Non-Resolvable)
> HCI Event: Command Complete (0x0e) plen 4                     [hci0]
6.892774
      LE Set Random Address (0x08|0x0005) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Parameters (0x08|0x000b) plen 7      [hci0]
6.892944
        Type: Active (0x01)
        Interval: 11.250 msec (0x0012)
        Window: 11.250 msec (0x0012)
        Own address type: Random (0x01)
        Filter policy: Accept all advertisement (0x00)
> HCI Event: Command Complete (0x0e) plen 4                     [hci0]
6.894777
      LE Set Scan Parameters (0x08|0x000b) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2          [hci0]
6.895268
        Scanning: Enabled (0x01)
        Filter duplicates: Enabled (0x01)
> HCI Event: Command Complete (0x0e) plen 4                     [hci0]
6.896768
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x01 (6)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2         [hci0]
17.165233
        Scanning: Disabled (0x00)
        Filter duplicates: Disabled (0x00)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
17.166804
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x00 (6)
< HCI Command: LE Set Random Address (0x08|0x0005) plen 6      [hci0]
17.172436
        Address: 30:D0:1B:39:11:67 (Non-Resolvable)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
17.173779
      LE Set Random Address (0x08|0x0005) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Parameters (0x08|0x000b) plen 7     [hci0]
17.173957
        Type: Active (0x01)
        Interval: 11.250 msec (0x0012)
        Window: 11.250 msec (0x0012)
        Own address type: Random (0x01)
        Filter policy: Accept all advertisement (0x00)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
17.175784
      LE Set Scan Parameters (0x08|0x000b) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2         [hci0]
17.175969
        Scanning: Enabled (0x01)
        Filter duplicates: Enabled (0x01)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
17.182437
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x01 (6)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2         [hci0]
27.445182
        Scanning: Disabled (0x00)
        Filter duplicates: Disabled (0x00)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
27.446769
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x00 (6)
< HCI Command: LE Set Random Address (0x08|0x0005) plen 6      [hci0]
27.452355
        Address: 1F:D8:B6:3C:C6:59 (Non-Resolvable)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
27.453758
      LE Set Random Address (0x08|0x0005) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Parameters (0x08|0x000b) plen 7     [hci0]
27.453928
        Type: Active (0x01)
        Interval: 11.250 msec (0x0012)
        Window: 11.250 msec (0x0012)
        Own address type: Random (0x01)
        Filter policy: Accept all advertisement (0x00)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
27.461737
      LE Set Scan Parameters (0x08|0x000b) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2         [hci0]
27.462099
        Scanning: Enabled (0x01)
        Filter duplicates: Enabled (0x01)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
27.464075
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x01 (6)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2         [hci0]
37.725179
        Scanning: Disabled (0x00)
        Filter duplicates: Disabled (0x00)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
37.726766
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x00 (6)
< HCI Command: LE Set Random Address (0x08|0x0005) plen 6      [hci0]
37.732082
        Address: 26:B7:AE:E6:75:57 (Non-Resolvable)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
37.733782
      LE Set Random Address (0x08|0x0005) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Parameters (0x08|0x000b) plen 7     [hci0]
37.734090
        Type: Active (0x01)
        Interval: 11.250 msec (0x0012)
        Window: 11.250 msec (0x0012)
        Own address type: Random (0x01)
        Filter policy: Accept all advertisement (0x00)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
37.735758
      LE Set Scan Parameters (0x08|0x000b) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2         [hci0]
37.735871
        Scanning: Enabled (0x01)
        Filter duplicates: Enabled (0x01)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
37.736753
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x01 (6)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2         [hci0]
48.005204
        Scanning: Disabled (0x00)
        Filter duplicates: Disabled (0x00)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
48.006786
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x00 (6)
< HCI Command: LE Set Random Address (0x08|0x0005) plen 6      [hci0]
48.016702
        Address: 06:D6:55:3D:29:5F (Non-Resolvable)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
48.017777
      LE Set Random Address (0x08|0x0005) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Parameters (0x08|0x000b) plen 7     [hci0]
48.018674
        Type: Active (0x01)
        Interval: 11.250 msec (0x0012)
        Window: 11.250 msec (0x0012)
        Own address type: Random (0x01)
        Filter policy: Accept all advertisement (0x00)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
48.019771
      LE Set Scan Parameters (0x08|0x000b) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2         [hci0]
48.019967
        Scanning: Enabled (0x01)
        Filter duplicates: Enabled (0x01)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
48.021775
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x01 (6)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2         [hci0]
58.290259
        Scanning: Disabled (0x00)
        Filter duplicates: Disabled (0x00)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
58.291852
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x00 (6)
< HCI Command: LE Set Random Address (0x08|0x0005) plen 6      [hci0]
58.297162
        Address: 06:E6:71:FF:BA:41 (Non-Resolvable)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
58.298778
      LE Set Random Address (0x08|0x0005) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Parameters (0x08|0x000b) plen 7     [hci0]
58.299040
        Type: Active (0x01)
        Interval: 11.250 msec (0x0012)
        Window: 11.250 msec (0x0012)
        Own address type: Random (0x01)
        Filter policy: Accept all advertisement (0x00)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
58.300779
      LE Set Scan Parameters (0x08|0x000b) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2         [hci0]
58.301042
        Scanning: Enabled (0x01)
        Filter duplicates: Enabled (0x01)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
58.302784
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x01 (6)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2         [hci0]
68.565182
        Scanning: Disabled (0x00)
        Filter duplicates: Disabled (0x00)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
68.566772
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x00 (6)
< HCI Command: LE Set Random Address (0x08|0x0005) plen 6      [hci0]
68.573095
        Address: 25:42:7A:0D:BF:7D (Non-Resolvable)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
68.574788
      LE Set Random Address (0x08|0x0005) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Parameters (0x08|0x000b) plen 7     [hci0]
68.575193
        Type: Active (0x01)
        Interval: 11.250 msec (0x0012)
        Window: 11.250 msec (0x0012)
        Own address type: Random (0x01)
        Filter policy: Accept all advertisement (0x00)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
68.576785
      LE Set Scan Parameters (0x08|0x000b) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2         [hci0]
68.577027
        Scanning: Enabled (0x01)
        Filter duplicates: Enabled (0x01)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
68.584697
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x01 (6)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2         [hci0]
78.845191
        Scanning: Disabled (0x00)
        Filter duplicates: Disabled (0x00)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
78.846780
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x00 (6)
< HCI Command: LE Set Random Address (0x08|0x0005) plen 6      [hci0]
78.853717
        Address: 3A:B8:26:AA:9B:12 (Non-Resolvable)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
78.854825
      LE Set Random Address (0x08|0x0005) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Parameters (0x08|0x000b) plen 7     [hci0]
78.856067
        Type: Active (0x01)
        Interval: 11.250 msec (0x0012)
        Window: 11.250 msec (0x0012)
        Own address type: Random (0x01)
        Filter policy: Accept all advertisement (0x00)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
78.857777
      LE Set Scan Parameters (0x08|0x000b) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2         [hci0]
78.859335
        Scanning: Enabled (0x01)
        Filter duplicates: Enabled (0x01)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
78.864484
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x01 (6)


///  Step 2:


< HCI Command: LE Set Random Address (0x08|0x0005) plen 6      [hci0]
99.375289
        Address: 25:61:0F:37:30:99 (Non-Resolvable)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
99.378071
      LE Set Random Address (0x08|0x0005) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Parameters (0x08|0x000b) plen 7     [hci0]
99.379701
        Type: Active (0x01)
        Interval: 11.250 msec (0x0012)
        Window: 11.250 msec (0x0012)
        Own address type: Random (0x01)
        Filter policy: Accept all advertisement (0x00)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
99.380778
      LE Set Scan Parameters (0x08|0x000b) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2         [hci0]
99.380939
        Scanning: Enabled (0x01)
        Filter duplicates: Enabled (0x01)
> HCI Event: Command Complete (0x0e) plen 4                    [hci0]
99.382768
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x01 (6)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2        [hci0]
109.645175
        Scanning: Disabled (0x00)
        Filter duplicates: Disabled (0x00)
> HCI Event: Command Complete (0x0e) plen 4                   [hci0]
109.646778
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x00 (6)
< HCI Command: LE Set Random Address (0x08|0x0005) plen 6     [hci0]
109.652523
        Address: 3F:4D:C3:01:6A:72 (Non-Resolvable)
> HCI Event: Command Complete (0x0e) plen 4                   [hci0]
109.653782
      LE Set Random Address (0x08|0x0005) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Parameters (0x08|0x000b) plen 7    [hci0]
109.654052
        Type: Active (0x01)
        Interval: 11.250 msec (0x0012)
        Window: 11.250 msec (0x0012)
        Own address type: Random (0x01)
        Filter policy: Accept all advertisement (0x00)
> HCI Event: Command Complete (0x0e) plen 4                   [hci0]
109.655774
      LE Set Scan Parameters (0x08|0x000b) ncmd 2
        Status: Success (0x00)
< HCI Command: LE Set Scan Enable (0x08|0x000c) plen 2        [hci0]
109.656344
        Scanning: Enabled (0x01)
        Filter duplicates: Enabled (0x01)
> HCI Event: Command Complete (0x0e) plen 4                   [hci0]
109.657769
      LE Set Scan Enable (0x08|0x000c) ncmd 2
        Status: Success (0x00)
@ Discovering: 0x01 (6)




--
Patrick Shirkey
Boost Hardware Ltd

^ permalink raw reply

* [PATCH BlueZ] shared/gatt-db: Expose gatt_db_attribute
From: Luiz Augusto von Dentz @ 2014-10-24 13:48 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This is just a draft API to reduce the lookups in gatt_db and make it
a little bit more convenient for batch operations, so the general idea
is to be able to get a hold of it via gatt_db_get_attribute but also
replace the handles in the queues with proper attributes so the server
code don't have to lookup again when reading/writing, checking
permissions, or any other operation that can be done directly.
---
 src/shared/gatt-db.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 8d18434..ab7469a 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -96,3 +96,25 @@ bool gatt_db_get_service_uuid(struct gatt_db *db, uint16_t handle,
 
 bool gatt_db_get_attribute_permissions(struct gatt_db *db, uint16_t handle,
 							uint32_t *permissions);
+
+struct gatt_db_attribute;
+
+struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *attrib,
+					uint16_t handle, uint16_t offset);
+
+const bt_uuid_t *gatt_db_attribute_get_type(struct gatt_db_attribute *attrib);
+
+bool gatt_db_attribute_get_service_uuid(struct gatt_db_attribute *attrib,
+							bt_uuid_t *uuid);
+
+bool gatt_db_attribute_get_permissions(struct gatt_db_attribute *attrib,
+							uint32_t *permissions);
+
+bool gatt_db_attribute_read(struct gatt_db_attribute *attrib,
+					uint8_t opcode, bdaddr_t *bdaddr,
+					void *callback, void *user_data);
+
+bool gatt_db_attribute_write(struct gatt_db_attribute *attrib,
+					const uint8_t *value, size_t len,
+					uint8_t opcode, bdaddr_t *bdaddr,
+					void *callback, void *user_data);
-- 
1.9.3


^ permalink raw reply related

* Re: [PATCH 1/8] android/gatt: Fix return status if cannot alloc memory
From: Szymon Janc @ 2014-10-24 12:18 UTC (permalink / raw)
  To: Grzegorz Kolodziejczyk; +Cc: linux-bluetooth
In-Reply-To: <1413553099-19689-1-git-send-email-grzegorz.kolodziejczyk@tieto.com>

Hi Grzegorz,

On Friday 17 of October 2014 15:38:12 Grzegorz Kolodziejczyk wrote:
> NOMEM HAL status should be returned in case if it's not possible to
> alloc memory for notifiaction data.
> ---
>  android/gatt.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/android/gatt.c b/android/gatt.c
> index ea20941..c26d45a 100644
> --- a/android/gatt.c
> +++ b/android/gatt.c
> @@ -3759,7 +3759,7 @@ static void handle_client_register_for_notification(const void *buf,
>  
>  	notification = new0(struct notification_data, 1);
>  	if (!notification) {
> -		status = HAL_STATUS_FAILED;
> +		status = HAL_STATUS_NOMEM;
>  		goto failed;
>  	}
>  

All patches have been applied. Thanks. 

-- 
Best regards, 
Szymon Janc

^ permalink raw reply

* [PATCH v5 11/11] unit/test-hfp: Add some robustness tests for HFP HF
From: Lukasz Rymanowski @ 2014-10-24 11:59 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski
In-Reply-To: <1414151975-20588-1-git-send-email-lukasz.rymanowski@tieto.com>

This patch adds folowing tests:
/hfp/test_hf_corrupted_1
/hfp/test_hf_corrupted_2
/hfp/test_hf_empty
/hfp/test_hf_unknown
---
 unit/test-hfp.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index ab695d7..24ea402 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -563,6 +563,25 @@ static void test_hf_unsolicited(gconstpointer data)
 	execute_context(context);
 }
 
+static void test_hf_robustness(gconstpointer data)
+{
+	struct context *context = create_context(data);
+	bool ret;
+
+	context->hfp_hf = hfp_hf_new(context->fd_client);
+	g_assert(context->hfp_hf);
+
+	ret = hfp_hf_set_close_on_unref(context->hfp_hf, true);
+	g_assert(ret);
+
+	send_pdu(context);
+
+	hfp_hf_unref(context->hfp_hf);
+	context->hfp_hf = NULL;
+
+	execute_context(context);
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -674,5 +693,26 @@ int main(int argc, char *argv[])
 			frg_pdu('\n'),
 			data_end());
 
+	define_hf_test("/hfp_hf/test_corrupted_1", test_hf_unsolicited,
+			hf_result_handler, NULL,
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\0'),
+			frg_pdu('\r', 'X', '\r', '\n'),
+			frg_pdu('+', 'C', 'L', 'C', 'C', ':', '1', ',', '3'),
+			frg_pdu(',', '0', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp_hf/test_corrupted_2", test_hf_unsolicited,
+			hf_result_handler, NULL,
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\0'),
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp_hf/test_empty", test_hf_robustness, NULL, NULL,
+			raw_pdu('\r'), data_end());
+
+	define_hf_test("/hfp_hf/test_unknown", test_hf_robustness, NULL, NULL,
+			raw_pdu('\r', '\n', 'B', 'R', '\r', '\n'),
+			data_end());
+
 	return g_test_run();
 }
-- 
1.8.4


^ permalink raw reply related

* [PATCH v5 10/11] unit/test-hfp: Add tests for unsolicited results for HFP HF
From: Lukasz Rymanowski @ 2014-10-24 11:59 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski
In-Reply-To: <1414151975-20588-1-git-send-email-lukasz.rymanowski@tieto.com>

This patch adds three test case:
/hfp_hf/test_unsolicited_1
/hfp_hf/test_unsolicited_2
/hfp_hf/test_unsolicited_3
---
 unit/test-hfp.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index 3c25691..ab695d7 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -527,6 +527,42 @@ static void test_hf_send_command(gconstpointer data)
 	execute_context(context);
 }
 
+static void hf_result_handler(struct hfp_hf_result *result,
+							void *user_data)
+{
+	struct context *context = user_data;
+
+	hfp_hf_disconnect(context->hfp_hf);
+}
+
+static void test_hf_unsolicited(gconstpointer data)
+{
+	struct context *context = create_context(data);
+	bool ret;
+
+	context->hfp_hf = hfp_hf_new(context->fd_client);
+	g_assert(context->hfp_hf);
+
+	ret = hfp_hf_set_close_on_unref(context->hfp_hf, true);
+	g_assert(ret);
+
+	if (context->data->hf_result_func) {
+		const struct test_pdu *pdu;
+
+		pdu = &context->data->pdu_list[context->pdu_offset++];
+
+		ret = hfp_hf_register(context->hfp_hf,
+						context->data->hf_result_func,
+						(char *)pdu->data, context,
+						NULL);
+		g_assert(ret);
+	}
+
+	send_pdu(context);
+
+	execute_context(context);
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -614,5 +650,29 @@ int main(int argc, char *argv[])
 			frg_pdu('\r', '\n', 'O', 'k', '\r', '\n'),
 			data_end());
 
+	define_hf_test("/hfp_hf/test_unsolicited_1", test_hf_unsolicited,
+			hf_result_handler, NULL,
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\0'),
+			frg_pdu('\r', '\n', '+', 'C', 'L', 'C'),
+			frg_pdu('C', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp_hf/test_unsolicited_2", test_hf_unsolicited,
+			hf_result_handler, NULL,
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\0'),
+			frg_pdu('\r', '\n', '+', 'C', 'L', 'C', 'C', ':', '1'),
+			frg_pdu(',', '3', ',', '0', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp_hf/test_unsolicited_3", test_hf_unsolicited,
+			hf_result_handler, NULL,
+			raw_pdu('+', 'C', 'L', 'C', 'C', '\0'),
+			frg_pdu('\r'), frg_pdu('\n'), frg_pdu('+'),
+			frg_pdu('C'), frg_pdu('L'), frg_pdu('C'), frg_pdu('C'),
+			frg_pdu(':'), frg_pdu('1'), frg_pdu(','), frg_pdu('3'),
+			frg_pdu(','), frg_pdu('0'), frg_pdu('\r'),
+			frg_pdu('\n'),
+			data_end());
+
 	return g_test_run();
 }
-- 
1.8.4


^ permalink raw reply related

* [PATCH v5 09/11] unit/test-hfp: Add send command tests for HFP HF
From: Lukasz Rymanowski @ 2014-10-24 11:59 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski
In-Reply-To: <1414151975-20588-1-git-send-email-lukasz.rymanowski@tieto.com>

This patch adds following tests:
/hfp_hf/test_send_command_1
/hfp_hf/test_send_command_2
---
 unit/test-hfp.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 74 insertions(+)

diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index 1a8548e..3c25691 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -468,6 +468,65 @@ static void test_hf_init(gconstpointer data)
 	execute_context(context);
 }
 
+static bool unsolicited_resp = false;
+
+static void hf_unsolicited_resp_cb(struct hfp_hf_result *result,
+							void *user_data) {
+	unsolicited_resp = true;
+}
+
+static void hf_response_with_data(enum hfp_result res,
+							void *user_data)
+{
+	struct context *context = user_data;
+
+	g_assert(unsolicited_resp);
+	unsolicited_resp = false;
+
+	hfp_hf_disconnect(context->hfp_hf);
+}
+
+static void hf_response_cb(enum hfp_result res, void *user_data)
+{
+	struct context *context = user_data;
+
+	hfp_hf_disconnect(context->hfp_hf);
+}
+
+static void test_hf_send_command(gconstpointer data)
+{
+	struct context *context = create_context(data);
+	const struct test_pdu *pdu;
+	bool ret;
+
+	context->hfp_hf = hfp_hf_new(context->fd_client);
+	g_assert(context->hfp_hf);
+
+	ret = hfp_hf_set_close_on_unref(context->hfp_hf, true);
+	g_assert(ret);
+
+	if (context->data->response_func) {
+		if (context->data->hf_result_func) {
+			pdu = &context->data->pdu_list[context->pdu_offset++];
+
+			ret = hfp_hf_register(context->hfp_hf,
+						context->data->hf_result_func,
+						(char *)pdu->data,
+						NULL, NULL);
+			g_assert(ret);
+		}
+
+		pdu = &context->data->pdu_list[context->pdu_offset++];
+
+		ret = hfp_hf_send_command(context->hfp_hf,
+						context->data->response_func,
+						context, (char *)pdu->data);
+		g_assert(ret);
+	}
+
+	execute_context(context);
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -539,6 +598,21 @@ int main(int argc, char *argv[])
 
 	define_hf_test("/hfp_hf/test_init", test_hf_init, NULL, NULL,
 			data_end());
+	define_hf_test("/hfp_hf/test_send_command_1", test_hf_send_command,
+			NULL, hf_response_cb,
+			raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '\0'),
+			raw_pdu('\r', '\n', 'O', 'k', '\r', '\n'),
+			data_end());
+
+	define_hf_test("/hfp_hf/test_send_command_2", test_hf_send_command,
+			hf_unsolicited_resp_cb,
+			hf_response_with_data,
+			raw_pdu('+', 'B', 'R', 'S', 'F', '\0'),
+			raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '\0'),
+			frg_pdu('\r', '\n', '+', 'B', 'R', 'S', 'F', '\r',
+									'\n'),
+			frg_pdu('\r', '\n', 'O', 'k', '\r', '\n'),
+			data_end());
 
 	return g_test_run();
 }
-- 
1.8.4


^ permalink raw reply related

* [PATCH v5 08/11] unit/test-hfp: Add init test for HFP HF
From: Lukasz Rymanowski @ 2014-10-24 11:59 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski
In-Reply-To: <1414151975-20588-1-git-send-email-lukasz.rymanowski@tieto.com>

This patch adds basic infrastruction for HFP HF test plus
init test.

It also moves send_pdu function in the file so it can be used by
test_hf_handler
---
 unit/test-hfp.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 85 insertions(+), 18 deletions(-)

diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index 4b3473b..1a8548e 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -36,6 +36,7 @@ struct context {
 	int fd_server;
 	int fd_client;
 	struct hfp_gw *hfp;
+	struct hfp_hf *hfp_hf;
 	const struct test_data *data;
 	unsigned int pdu_offset;
 };
@@ -52,6 +53,8 @@ struct test_data {
 	char *test_name;
 	struct test_pdu *pdu_list;
 	hfp_result_func_t result_func;
+	hfp_response_func_t response_func;
+	hfp_hf_result_func_t hf_result_func;
 	GIOFunc test_handler;
 };
 
@@ -99,6 +102,22 @@ struct test_data {
 		data.test_handler = test_handler;			\
 	} while (0)
 
+#define define_hf_test(name, function, result_func, response_function,	\
+								args...)\
+	do {								\
+		const struct test_pdu pdus[] = {			\
+			args, { }					\
+		};							\
+		static struct test_data data;				\
+		data.test_name = g_strdup(name);			\
+		data.pdu_list = g_malloc(sizeof(pdus));			\
+		data.hf_result_func = result_func;			\
+		data.response_func = response_function;			\
+		memcpy(data.pdu_list, pdus, sizeof(pdus));		\
+		g_test_add_data_func(name, &data, function);		\
+		data.test_handler = test_hf_handler;			\
+	} while (0)
+
 static void context_quit(struct context *context)
 {
 	g_main_loop_quit(context->main_loop);
@@ -128,6 +147,52 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
 	return FALSE;
 }
 
+static gboolean send_pdu(gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	ssize_t len;
+
+	pdu = &context->data->pdu_list[context->pdu_offset++];
+
+	if (pdu && !pdu->valid)
+		return FALSE;
+
+	len = write(context->fd_server, pdu->data, pdu->size);
+	g_assert_cmpint(len, ==, pdu->size);
+
+	pdu = &context->data->pdu_list[context->pdu_offset];
+	if (pdu->fragmented)
+		g_idle_add(send_pdu, context);
+
+	return FALSE;
+}
+
+static gboolean test_hf_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+	gchar buf[60];
+	gsize bytes_read;
+	GError *error = NULL;
+
+	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+		goto done;
+
+	/* dummy read */
+	g_io_channel_read_chars(channel, buf, 60, &bytes_read, &error);
+
+	send_pdu(context);
+
+	return TRUE;
+
+done:
+	context_quit(context);
+	context->watch_id = 0;
+
+	return FALSE;
+}
+
 static void cmd_handler(const char *command, void *user_data)
 {
 	struct context *context = user_data;
@@ -203,6 +268,9 @@ static void execute_context(struct context *context)
 	if (context->hfp)
 		hfp_gw_unref(context->hfp);
 
+	if (context->hfp_hf)
+		hfp_hf_unref(context->hfp_hf);
+
 	g_free(context);
 }
 
@@ -275,24 +343,6 @@ static void test_register(gconstpointer data)
 	execute_context(context);
 }
 
-static gboolean send_pdu(gpointer user_data)
-{
-	struct context *context = user_data;
-	const struct test_pdu *pdu;
-	ssize_t len;
-
-	pdu = &context->data->pdu_list[context->pdu_offset++];
-
-	len = write(context->fd_server, pdu->data, pdu->size);
-	g_assert_cmpint(len, ==, pdu->size);
-
-	pdu = &context->data->pdu_list[context->pdu_offset];
-	if (pdu->fragmented)
-		g_idle_add(send_pdu, context);
-
-	return FALSE;
-}
-
 static void test_fragmented(gconstpointer data)
 {
 	struct context *context = create_context(data);
@@ -404,6 +454,20 @@ static void check_string_2(struct hfp_gw_result *result,
 	hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
 }
 
+static void test_hf_init(gconstpointer data)
+{
+	struct context *context = create_context(data);
+
+	context->hfp_hf = hfp_hf_new(context->fd_client);
+	g_assert(context->hfp_hf);
+	g_assert(hfp_hf_set_close_on_unref(context->hfp_hf, true));
+
+	hfp_hf_unref(context->hfp_hf);
+	context->hfp_hf = NULL;
+
+	execute_context(context);
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -473,5 +537,8 @@ int main(int argc, char *argv[])
 			raw_pdu('\r'),
 			data_end());
 
+	define_hf_test("/hfp_hf/test_init", test_hf_init, NULL, NULL,
+			data_end());
+
 	return g_test_run();
 }
-- 
1.8.4


^ permalink raw reply related

* [PATCH v5 07/11] unit/test-hfp: Provide test_handler function via struct data
From: Lukasz Rymanowski @ 2014-10-24 11:59 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Lukasz Rymanowski
In-Reply-To: <1414151975-20588-1-git-send-email-lukasz.rymanowski@tieto.com>

This patch allows us to use user defined test handler depends on needs.
Will use it in following patches which implements tests for HFP HF.
---
 unit/test-hfp.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index a8801b0..4b3473b 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -52,6 +52,7 @@ struct test_data {
 	char *test_name;
 	struct test_pdu *pdu_list;
 	hfp_result_func_t result_func;
+	GIOFunc test_handler;
 };
 
 #define data(args...) ((const unsigned char[]) { args })
@@ -95,6 +96,7 @@ struct test_data {
 		data.result_func = result_function;			\
 		memcpy(data.pdu_list, pdus, sizeof(pdus));		\
 		g_test_add_data_func(name, &data, function);		\
+		data.test_handler = test_handler;			\
 	} while (0)
 
 static void context_quit(struct context *context)
@@ -158,6 +160,7 @@ static struct context *create_context(gconstpointer data)
 	struct context *context = g_new0(struct context, 1);
 	GIOChannel *channel;
 	int err, sv[2];
+	const struct test_data *d = data;
 
 	context->main_loop = g_main_loop_new(NULL, FALSE);
 	g_assert(context->main_loop);
@@ -173,7 +176,8 @@ static struct context *create_context(gconstpointer data)
 
 	context->watch_id = g_io_add_watch(channel,
 				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-				test_handler, context);
+				d->test_handler, context);
+
 	g_assert(context->watch_id > 0);
 
 	g_io_channel_unref(channel);
-- 
1.8.4


^ permalink raw reply related


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