2014-12-17 15:48:58

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 00/19] android/gatt: Fix HOG connect/disconnect

When testing HOG against PTS we got failing testes if devices were
not bonded before the test.
With those patches this issue is not valid anymore.

This set address couple of issues:
* Some minor fixes found during work on those patches (first 2 patches)

* Changed GATT behaviour during dedicated bonding. Now GATT will not start
search services until bonding is done. That fixes PTS connection issue
(patches 3-6)

* Tracking of GATT operations in hog/dis/bas/scpp. After I solved PTS
connection issue I could very easily observe many of crashes in BfA when
disconnecting HOG during service search session. This is because HOG
talks directly to gattrib and clear his attrib instance on HOG disconnect
even there are ongoing GATT operations. Patches 7 - 19 solve this issue


v2:
* Remove couple of not needed patches
* Note that this series should go on top of: [PATCH 0/2] attrib/gattrib: Add tracking request id
* some minor self review cleanups


Lukasz Rymanowski (19):
android/bluetooth: Minor typo fix
android/bluetooth: Minor fix, add missing NULL assignment
android/bluetooth: Add possibility to register for bonded event
android/gatt: Add paired cb to gatt.c
android/bluetooth: Add API to check if device is bonding
android/gatt: Improve LE connection after pair
android/hog: Add support to track gatt operations
android/hog: Cancel all GATT operations on disconnect
android/hog: Keep track on primary and include service search
android/hog: Keep track on discover characteristic and descriptors
android/hog: Keep track on read/write char and descr
android/bas: Add queue to keep track on GATT operations
android/bas: Keep track read/write GATT operations
android/bas: Keep track on discover characteristic and descriptor
android/dis: Add queue to keep track on GATT operations
android/dis: Keep track on discover and read characteristic
android/scpp: Add queue to keep GATT operations
android/scpp: Keep track on discover characteristic and descriptor
android/scpp: Keep track on write operation

android/bas.c | 171 ++++++++++++++++++++++++++--
android/bluetooth.c | 52 ++++++++-
android/bluetooth.h | 5 +
android/dis.c | 108 +++++++++++++++++-
android/gatt.c | 67 ++++++++++-
android/hog.c | 322 ++++++++++++++++++++++++++++++++++++++++++++--------
android/scpp.c | 150 ++++++++++++++++++++++--
7 files changed, 801 insertions(+), 74 deletions(-)

--
1.8.4



2014-12-19 12:57:01

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH v2 00/19] android/gatt: Fix HOG connect/disconnect

Hi Lukasz,

On Wed, Dec 17, 2014 at 1:48 PM, Lukasz Rymanowski
<[email protected]> wrote:
> When testing HOG against PTS we got failing testes if devices were
> not bonded before the test.
> With those patches this issue is not valid anymore.
>
> This set address couple of issues:
> * Some minor fixes found during work on those patches (first 2 patches)
>
> * Changed GATT behaviour during dedicated bonding. Now GATT will not start
> search services until bonding is done. That fixes PTS connection issue
> (patches 3-6)
>
> * Tracking of GATT operations in hog/dis/bas/scpp. After I solved PTS
> connection issue I could very easily observe many of crashes in BfA when
> disconnecting HOG during service search session. This is because HOG
> talks directly to gattrib and clear his attrib instance on HOG disconnect
> even there are ongoing GATT operations. Patches 7 - 19 solve this issue
>
>
> v2:
> * Remove couple of not needed patches
> * Note that this series should go on top of: [PATCH 0/2] attrib/gattrib: Add tracking request id
> * some minor self review cleanups
>
>
> Lukasz Rymanowski (19):
> android/bluetooth: Minor typo fix
> android/bluetooth: Minor fix, add missing NULL assignment
> android/bluetooth: Add possibility to register for bonded event
> android/gatt: Add paired cb to gatt.c
> android/bluetooth: Add API to check if device is bonding
> android/gatt: Improve LE connection after pair
> android/hog: Add support to track gatt operations
> android/hog: Cancel all GATT operations on disconnect
> android/hog: Keep track on primary and include service search
> android/hog: Keep track on discover characteristic and descriptors
> android/hog: Keep track on read/write char and descr
> android/bas: Add queue to keep track on GATT operations
> android/bas: Keep track read/write GATT operations
> android/bas: Keep track on discover characteristic and descriptor
> android/dis: Add queue to keep track on GATT operations
> android/dis: Keep track on discover and read characteristic
> android/scpp: Add queue to keep GATT operations
> android/scpp: Keep track on discover characteristic and descriptor
> android/scpp: Keep track on write operation
>
> android/bas.c | 171 ++++++++++++++++++++++++++--
> android/bluetooth.c | 52 ++++++++-
> android/bluetooth.h | 5 +
> android/dis.c | 108 +++++++++++++++++-
> android/gatt.c | 67 ++++++++++-
> android/hog.c | 322 ++++++++++++++++++++++++++++++++++++++++++++--------
> android/scpp.c | 150 ++++++++++++++++++++++--
> 7 files changed, 801 insertions(+), 74 deletions(-)
>
> --
> 1.8.4

Applied, thanks. I took the opportunity and synchronized the
implementation with profiles/input/hog.c


--
Luiz Augusto von Dentz

2014-12-17 15:49:17

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 19/19] android/scpp: Keep track on write operation

---
android/scpp.c | 33 +++++++++++++++++++++++++++++----
1 file changed, 29 insertions(+), 4 deletions(-)

diff --git a/android/scpp.c b/android/scpp.c
index 8087ada..77f48cd 100644
--- a/android/scpp.c
+++ b/android/scpp.c
@@ -142,6 +142,28 @@ static void discover_desc(struct bt_scpp *scpp, GAttrib *attrib,
free(req);
}

+static void write_char(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle,
+ const uint8_t *value, size_t vlen,
+ GAttribResultFunc func,
+ gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(scan, user_data);
+ if (!req)
+ return;
+
+ id = gatt_write_char(attrib, handle, value, vlen, func, req);
+
+ if (set_and_store_gatt_req(scan, req, id))
+ return;
+
+ error("scpp: Could not read char");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
static void scpp_free(struct bt_scpp *scan)
{
bt_scpp_detach(scan);
@@ -239,22 +261,25 @@ static void ccc_written_cb(guint8 status, const guint8 *pdu,
refresh_value_cb, scan, NULL);
}

-static void write_ccc(GAttrib *attrib, uint16_t handle, void *user_data)
+static void write_ccc(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle,
+ void *user_data)
{
uint8_t value[2];

put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);

- gatt_write_char(attrib, handle, value, sizeof(value), ccc_written_cb,
+ write_char(scan, attrib, handle, value, sizeof(value), ccc_written_cb,
user_data);
}

static void discover_descriptor_cb(uint8_t status, GSList *descs,
void *user_data)
{
- struct bt_scpp *scan = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_scpp *scan = req->user_data;
struct gatt_desc *desc;

+ destroy_gatt_req(req);

if (status != 0) {
error("Discover descriptors failed: %s", att_ecode2str(status));
@@ -264,7 +289,7 @@ static void discover_descriptor_cb(uint8_t status, GSList *descs,
/* There will be only one descriptor on list and it will be CCC */
desc = descs->data;

- write_ccc(scan->attrib, desc->handle, scan);
+ write_ccc(scan, scan->attrib, desc->handle, scan);
}

static void refresh_discovered_cb(uint8_t status, GSList *chars,
--
1.8.4


2014-12-17 15:49:16

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 18/19] android/scpp: Keep track on discover characteristic and descriptor

---
android/scpp.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 82 insertions(+), 6 deletions(-)

diff --git a/android/scpp.c b/android/scpp.c
index 69bb717..8087ada 100644
--- a/android/scpp.c
+++ b/android/scpp.c
@@ -75,6 +75,73 @@ static void destroy_gatt_req(struct gatt_request *req)
free(req);
}

+static struct gatt_request *create_request(struct bt_scpp *scpp,
+ void *user_data)
+{
+ struct gatt_request *req;
+
+ DBG("");
+
+ req = new0(struct gatt_request, 1);
+ if (!req)
+ return NULL;
+
+ req->user_data = user_data;
+ req->scpp = bt_scpp_ref(scpp);
+
+ return req;
+}
+
+static bool set_and_store_gatt_req(struct bt_scpp *scpp,
+ struct gatt_request *req,
+ unsigned int id)
+{
+ req->id = id;
+ return queue_push_head(scpp->gatt_op, req);
+}
+
+static void discover_char(struct bt_scpp *scpp, GAttrib *attrib,
+ uint16_t start, uint16_t end,
+ bt_uuid_t *uuid, gatt_cb_t func,
+ gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(scpp, user_data);
+ if (!req)
+ return;
+
+ id = gatt_discover_char(attrib, start, end, uuid, func, req);
+
+ if (set_and_store_gatt_req(scpp, req, id))
+ return;
+
+ error("scpp: Could not discover characteristic");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void discover_desc(struct bt_scpp *scpp, GAttrib *attrib,
+ uint16_t start, uint16_t end, bt_uuid_t *uuid,
+ gatt_cb_t func, gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(scpp, user_data);
+ if (!req)
+ return;
+
+ id = gatt_discover_desc(attrib, start, end, uuid, func, req);
+ if (set_and_store_gatt_req(scpp, req, id))
+ return;
+
+ error("scpp: Could not discover descriptor");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
static void scpp_free(struct bt_scpp *scan)
{
bt_scpp_detach(scan);
@@ -154,7 +221,10 @@ static void refresh_value_cb(const uint8_t *pdu, uint16_t len,
static void ccc_written_cb(guint8 status, const guint8 *pdu,
guint16 plen, gpointer user_data)
{
- struct bt_scpp *scan = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_scpp *scan = req->user_data;
+
+ destroy_gatt_req(req);

if (status != 0) {
error("Write Scan Refresh CCC failed: %s",
@@ -200,11 +270,14 @@ static void discover_descriptor_cb(uint8_t status, GSList *descs,
static void refresh_discovered_cb(uint8_t status, GSList *chars,
void *user_data)
{
- struct bt_scpp *scan = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_scpp *scan = req->user_data;
struct gatt_char *chr;
uint16_t start, end;
bt_uuid_t uuid;

+ destroy_gatt_req(req);
+
if (status) {
error("Scan Refresh %s", att_ecode2str(status));
return;
@@ -229,15 +302,18 @@ static void refresh_discovered_cb(uint8_t status, GSList *chars,

bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);

- gatt_discover_desc(scan->attrib, start, end, &uuid,
+ discover_desc(scan, scan->attrib, start, end, &uuid,
discover_descriptor_cb, user_data);
}

static void iwin_discovered_cb(uint8_t status, GSList *chars, void *user_data)
{
- struct bt_scpp *scan = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_scpp *scan = req->user_data;
struct gatt_char *chr;

+ destroy_gatt_req(req);
+
if (status) {
error("Discover Scan Interval Window: %s",
att_ecode2str(status));
@@ -267,7 +343,7 @@ bool bt_scpp_attach(struct bt_scpp *scan, void *attrib)
scan->window);
else {
bt_uuid16_create(&iwin_uuid, SCAN_INTERVAL_WIN_UUID);
- gatt_discover_char(scan->attrib, scan->primary->range.start,
+ discover_char(scan, scan->attrib, scan->primary->range.start,
scan->primary->range.end, &iwin_uuid,
iwin_discovered_cb, scan);
}
@@ -278,7 +354,7 @@ bool bt_scpp_attach(struct bt_scpp *scan, void *attrib)
refresh_value_cb, scan, NULL);
else {
bt_uuid16_create(&refresh_uuid, SCAN_REFRESH_UUID);
- gatt_discover_char(scan->attrib, scan->primary->range.start,
+ discover_char(scan, scan->attrib, scan->primary->range.start,
scan->primary->range.end, &refresh_uuid,
refresh_discovered_cb, scan);
}
--
1.8.4


2014-12-17 15:49:15

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 17/19] android/scpp: Add queue to keep GATT operations

With this patch SCPP gets queue to track GATT operations and also cancel
requests on SCPP disconnect
---
android/scpp.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)

diff --git a/android/scpp.c b/android/scpp.c
index 9d8bb10..69bb717 100644
--- a/android/scpp.c
+++ b/android/scpp.c
@@ -35,6 +35,7 @@

#include "lib/uuid.h"
#include "src/shared/util.h"
+#include "src/shared/queue.h"

#include "attrib/att.h"
#include "attrib/gattrib.h"
@@ -58,13 +59,28 @@ struct bt_scpp {
uint16_t iwhandle;
uint16_t refresh_handle;
guint refresh_cb_id;
+ struct queue *gatt_op;
};

+struct gatt_request {
+ unsigned int id;
+ struct bt_scpp *scpp;
+ void *user_data;
+};
+
+static void destroy_gatt_req(struct gatt_request *req)
+{
+ queue_remove(req->scpp->gatt_op, req);
+ bt_scpp_unref(req->scpp);
+ free(req);
+}
+
static void scpp_free(struct bt_scpp *scan)
{
bt_scpp_detach(scan);

g_free(scan->primary);
+ queue_destroy(scan->gatt_op, (void *) destroy_gatt_req);
g_free(scan);
}

@@ -79,6 +95,12 @@ struct bt_scpp *bt_scpp_new(void *primary)
scan->interval = SCAN_INTERVAL;
scan->window = SCAN_WINDOW;

+ scan->gatt_op = queue_new();
+ if (!scan->gatt_op) {
+ scpp_free(scan);
+ return NULL;
+ }
+
if (primary)
scan->primary = g_memdup(primary, sizeof(*scan->primary));

@@ -264,6 +286,12 @@ bool bt_scpp_attach(struct bt_scpp *scan, void *attrib)
return true;
}

+static void cancel_gatt_req(struct gatt_request *req)
+{
+ if (g_attrib_cancel(req->scpp->attrib, req->id))
+ destroy_gatt_req(req);
+}
+
void bt_scpp_detach(struct bt_scpp *scan)
{
if (!scan || !scan->attrib)
@@ -274,6 +302,7 @@ void bt_scpp_detach(struct bt_scpp *scan)
scan->refresh_cb_id = 0;
}

+ queue_foreach(scan->gatt_op, (void *) cancel_gatt_req, NULL);
g_attrib_unref(scan->attrib);
scan->attrib = NULL;
}
--
1.8.4


2014-12-17 15:49:14

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 16/19] android/dis: Keep track on discover and read characteristic

---
android/dis.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 75 insertions(+), 4 deletions(-)

diff --git a/android/dis.c b/android/dis.c
index 8a103b7..635ba90 100644
--- a/android/dis.c
+++ b/android/dis.c
@@ -125,13 +125,39 @@ void bt_dis_unref(struct bt_dis *dis)
dis_free(dis);
}

+static struct gatt_request *create_request(struct bt_dis *dis,
+ void *user_data)
+{
+ struct gatt_request *req;
+
+ req = new0(struct gatt_request, 1);
+ if (!req)
+ return NULL;
+
+ req->user_data = user_data;
+ req->dis = bt_dis_ref(dis);
+
+ return req;
+}
+
+static bool set_and_store_gatt_req(struct bt_dis *dis,
+ struct gatt_request *req,
+ unsigned int id)
+{
+ req->id = id;
+ return queue_push_head(dis->gatt_op, req);
+}
+
static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len,
gpointer user_data)
{
- struct bt_dis *dis = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_dis *dis = req->user_data;
uint8_t value[PNP_ID_SIZE];
ssize_t vlen;

+ destroy_gatt_req(req);
+
if (status != 0) {
error("Error reading PNP_ID value: %s", att_ecode2str(status));
return;
@@ -161,12 +187,57 @@ static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len,
dis->version, dis->notify_data);
}

+static void read_char(struct bt_dis *dis, GAttrib *attrib, uint16_t handle,
+ GAttribResultFunc func, gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(dis, user_data);
+ if (!req)
+ return;
+
+ id = gatt_read_char(attrib, handle, func, req);
+
+ if (set_and_store_gatt_req(dis, req, id))
+ return;
+
+ error("dis: Could not read characteristic");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void discover_char(struct bt_dis *dis, GAttrib *attrib,
+ uint16_t start, uint16_t end,
+ bt_uuid_t *uuid, gatt_cb_t func,
+ gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(dis, user_data);
+ if (!req)
+ return;
+
+ id = gatt_discover_char(attrib, start, end, uuid, func, req);
+
+ if (set_and_store_gatt_req(dis, req, id))
+ return;
+
+ error("dis: Could not send discover characteristic");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
static void configure_deviceinfo_cb(uint8_t status, GSList *characteristics,
void *user_data)
{
- struct bt_dis *d = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_dis *d = req->user_data;
GSList *l;

+ destroy_gatt_req(req);
+
if (status != 0) {
error("Discover deviceinfo characteristics: %s",
att_ecode2str(status));
@@ -178,7 +249,7 @@ static void configure_deviceinfo_cb(uint8_t status, GSList *characteristics,

if (strcmp(c->uuid, PNPID_UUID) == 0) {
d->handle = c->value_handle;
- gatt_read_char(d->attrib, d->handle, read_pnpid_cb, d);
+ read_char(d, d->attrib, d->handle, read_pnpid_cb, d);
break;
}
}
@@ -194,7 +265,7 @@ bool bt_dis_attach(struct bt_dis *dis, void *attrib)
dis->attrib = g_attrib_ref(attrib);

if (!dis->handle)
- gatt_discover_char(dis->attrib, primary->range.start,
+ discover_char(dis, dis->attrib, primary->range.start,
primary->range.end, NULL,
configure_deviceinfo_cb, dis);

--
1.8.4


2014-12-17 15:49:13

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 15/19] android/dis: Add queue to keep track on GATT operations

With this patch GATT operation queue has been added together with
canceling ongoing GATT operations on disconnect
---
android/dis.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)

diff --git a/android/dis.c b/android/dis.c
index 0a2a18e..8a103b7 100644
--- a/android/dis.c
+++ b/android/dis.c
@@ -33,6 +33,7 @@

#include "lib/uuid.h"
#include "src/shared/util.h"
+#include "src/shared/queue.h"

#include "attrib/gattrib.h"
#include "attrib/att.h"
@@ -53,6 +54,7 @@ struct bt_dis {
struct gatt_primary *primary; /* Primary details */
bt_dis_notify notify;
void *notify_data;
+ struct queue *gatt_op;
};

struct characteristic {
@@ -60,11 +62,25 @@ struct characteristic {
struct bt_dis *d; /* deviceinfo where the char belongs */
};

+struct gatt_request {
+ unsigned int id;
+ struct bt_dis *dis;
+ void *user_data;
+};
+
+static void destroy_gatt_req(struct gatt_request *req)
+{
+ queue_remove(req->dis->gatt_op, req);
+ bt_dis_unref(req->dis);
+ free(req);
+}
+
static void dis_free(struct bt_dis *dis)
{
bt_dis_detach(dis);

g_free(dis->primary);
+ queue_destroy(dis->gatt_op, (void *) destroy_gatt_req);
g_free(dis);
}

@@ -76,6 +92,12 @@ struct bt_dis *bt_dis_new(void *primary)
if (!dis)
return NULL;

+ dis->gatt_op = queue_new();
+ if (!dis->gatt_op) {
+ dis_free(dis);
+ return NULL;
+ }
+
if (primary)
dis->primary = g_memdup(primary, sizeof(*dis->primary));

@@ -179,11 +201,18 @@ bool bt_dis_attach(struct bt_dis *dis, void *attrib)
return true;
}

+static void cancel_gatt_req(struct gatt_request *req)
+{
+ if (g_attrib_cancel(req->dis->attrib, req->id))
+ destroy_gatt_req(req);
+}
+
void bt_dis_detach(struct bt_dis *dis)
{
if (!dis->attrib)
return;

+ queue_foreach(dis->gatt_op, (void *) cancel_gatt_req, NULL);
g_attrib_unref(dis->attrib);
dis->attrib = NULL;
}
--
1.8.4


2014-12-17 15:49:12

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 14/19] android/bas: Keep track on discover characteristic and descriptor

---
android/bas.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 52 insertions(+), 4 deletions(-)

diff --git a/android/bas.c b/android/bas.c
index 87402c1..e5e9837 100644
--- a/android/bas.c
+++ b/android/bas.c
@@ -183,6 +183,48 @@ static void read_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
free(req);
}

+static void discover_char(struct bt_bas *bas, GAttrib *attrib,
+ uint16_t start, uint16_t end,
+ bt_uuid_t *uuid, gatt_cb_t func,
+ gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(bas, user_data);
+ if (!req)
+ return;
+
+ id = gatt_discover_char(attrib, start, end, uuid, func, req);
+
+ if (set_and_store_gatt_req(bas, req, id))
+ return;
+
+ error("bas: Could not discover characteristic");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void discover_desc(struct bt_bas *bas, GAttrib *attrib,
+ uint16_t start, uint16_t end, bt_uuid_t *uuid,
+ gatt_cb_t func, gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(bas, user_data);
+ if (!req)
+ return;
+
+ id = gatt_discover_desc(attrib, start, end, uuid, func, req);
+ if (set_and_store_gatt_req(bas, req, id))
+ return;
+
+ error("bas: Could not discover descriptor");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
static void value_cb(const guint8 *pdu, guint16 len, gpointer user_data)
{
DBG("Battery Level at %u", pdu[ATT_NOTIFICATION_HEADER_SIZE]);
@@ -238,9 +280,12 @@ static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len,
static void discover_descriptor_cb(uint8_t status, GSList *descs,
void *user_data)
{
- struct bt_bas *bas = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_bas *bas = req->user_data;
struct gatt_desc *desc;

+ destroy_gatt_req(req);
+
if (status != 0) {
error("Discover descriptors failed: %s", att_ecode2str(status));
return;
@@ -255,11 +300,14 @@ static void discover_descriptor_cb(uint8_t status, GSList *descs,

static void bas_discovered_cb(uint8_t status, GSList *chars, void *user_data)
{
- struct bt_bas *bas = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_bas *bas = req->user_data;
struct gatt_char *chr;
uint16_t start, end;
bt_uuid_t uuid;

+ destroy_gatt_req(req);
+
if (status) {
error("Battery: %s", att_ecode2str(status));
return;
@@ -275,7 +323,7 @@ static void bas_discovered_cb(uint8_t status, GSList *chars, void *user_data)

bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);

- gatt_discover_desc(bas->attrib, start, end, &uuid,
+ discover_desc(bas, bas->attrib, start, end, &uuid,
discover_descriptor_cb, bas);
}

@@ -289,7 +337,7 @@ bool bt_bas_attach(struct bt_bas *bas, void *attrib)
if (bas->handle > 0)
return true;

- gatt_discover_char(bas->attrib, bas->primary->range.start,
+ discover_char(bas, bas->attrib, bas->primary->range.start,
bas->primary->range.end, NULL,
bas_discovered_cb, bas);

--
1.8.4


2014-12-17 15:49:11

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 13/19] android/bas: Keep track read/write GATT operations

---
android/bas.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 79 insertions(+), 7 deletions(-)

diff --git a/android/bas.c b/android/bas.c
index dde05ee..87402c1 100644
--- a/android/bas.c
+++ b/android/bas.c
@@ -117,6 +117,72 @@ void bt_bas_unref(struct bt_bas *bas)
bas_free(bas);
}

+static struct gatt_request *create_request(struct bt_bas *bas,
+ void *user_data)
+{
+ struct gatt_request *req;
+
+ req = new0(struct gatt_request, 1);
+ if (!req)
+ return NULL;
+
+ req->user_data = user_data;
+ req->bas = bt_bas_ref(bas);
+
+ return req;
+}
+
+static bool set_and_store_gatt_req(struct bt_bas *bas,
+ struct gatt_request *req,
+ unsigned int id)
+{
+ req->id = id;
+ return queue_push_head(bas->gatt_op, req);
+}
+
+static void write_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
+ const uint8_t *value, size_t vlen,
+ GAttribResultFunc func,
+ gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(bas, user_data);
+ if (!req)
+ return;
+
+ id = gatt_write_char(attrib, handle, value, vlen, func, req);
+
+ if (set_and_store_gatt_req(bas, req, id))
+ return;
+
+ error("bas: Could not write characteristic");
+ g_attrib_cancel(attrib, id);
+ free(req);
+
+}
+
+static void read_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
+ GAttribResultFunc func, gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(bas, user_data);
+ if (!req)
+ return;
+
+ id = gatt_read_char(attrib, handle, func, req);
+
+ if (set_and_store_gatt_req(bas, req, id))
+ return;
+
+ error("bas: Could not read characteristic");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
static void value_cb(const guint8 *pdu, guint16 len, gpointer user_data)
{
DBG("Battery Level at %u", pdu[ATT_NOTIFICATION_HEADER_SIZE]);
@@ -125,7 +191,10 @@ static void value_cb(const guint8 *pdu, guint16 len, gpointer user_data)
static void ccc_written_cb(guint8 status, const guint8 *pdu,
guint16 plen, gpointer user_data)
{
- struct bt_bas *bas = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_bas *bas = req->user_data;
+
+ destroy_gatt_req(req);

if (status != 0) {
error("Write Scan Refresh CCC failed: %s",
@@ -139,28 +208,31 @@ static void ccc_written_cb(guint8 status, const guint8 *pdu,
bas->handle, value_cb, bas, NULL);
}

-static void write_ccc(GAttrib *attrib, uint16_t handle, void *user_data)
+static void write_ccc(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
+ void *user_data)
{
uint8_t value[2];

put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);

- gatt_write_char(attrib, handle, value, sizeof(value), ccc_written_cb,
+ write_char(bas, attrib, handle, value, sizeof(value), ccc_written_cb,
user_data);
}

-
static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len,
gpointer user_data)
{
- struct bt_bas *bas = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_bas *bas = req->user_data;
+
+ destroy_gatt_req(req);

if (status != 0) {
error("Error reading CCC value: %s", att_ecode2str(status));
return;
}

- write_ccc(bas->attrib, bas->ccc_handle, bas);
+ write_ccc(bas, bas->attrib, bas->ccc_handle, bas);
}

static void discover_descriptor_cb(uint8_t status, GSList *descs,
@@ -178,7 +250,7 @@ static void discover_descriptor_cb(uint8_t status, GSList *descs,
desc = descs->data;
bas->ccc_handle = desc->handle;

- gatt_read_char(bas->attrib, desc->handle, ccc_read_cb, bas);
+ read_char(bas, bas->attrib, desc->handle, ccc_read_cb, bas);
}

static void bas_discovered_cb(uint8_t status, GSList *chars, void *user_data)
--
1.8.4


2014-12-17 15:49:10

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 12/19] android/bas: Add queue to keep track on GATT operations

With this patch BAS gets queue to keep GATT operations and cancel
operations on disconnect.
---
android/bas.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)

diff --git a/android/bas.c b/android/bas.c
index 7342895..dde05ee 100644
--- a/android/bas.c
+++ b/android/bas.c
@@ -34,6 +34,7 @@

#include "lib/uuid.h"
#include "src/shared/util.h"
+#include "src/shared/queue.h"

#include "attrib/gattrib.h"
#include "attrib/att.h"
@@ -50,13 +51,28 @@ struct bt_bas {
uint16_t handle;
uint16_t ccc_handle;
guint id;
+ struct queue *gatt_op;
};

+struct gatt_request {
+ unsigned int id;
+ struct bt_bas *bas;
+ void *user_data;
+};
+
+static void destroy_gatt_req(struct gatt_request *req)
+{
+ queue_remove(req->bas->gatt_op, req);
+ bt_bas_unref(req->bas);
+ free(req);
+}
+
static void bas_free(struct bt_bas *bas)
{
bt_bas_detach(bas);

g_free(bas->primary);
+ queue_destroy(bas->gatt_op, (void *) destroy_gatt_req);
g_free(bas);
}

@@ -68,6 +84,12 @@ struct bt_bas *bt_bas_new(void *primary)
if (!bas)
return NULL;

+ bas->gatt_op = queue_new();
+ if (!bas->gatt_op) {
+ bas_free(bas);
+ return NULL;
+ }
+
if (primary)
bas->primary = g_memdup(primary, sizeof(*bas->primary));

@@ -202,6 +224,12 @@ bool bt_bas_attach(struct bt_bas *bas, void *attrib)
return true;
}

+static void cancel_gatt_req(struct gatt_request *req)
+{
+ if (g_attrib_cancel(req->bas->attrib, req->id))
+ destroy_gatt_req(req);
+}
+
void bt_bas_detach(struct bt_bas *bas)
{
if (!bas || !bas->attrib)
@@ -212,6 +240,7 @@ void bt_bas_detach(struct bt_bas *bas)
bas->id = 0;
}

+ queue_foreach(bas->gatt_op, (void *) cancel_gatt_req, NULL);
g_attrib_unref(bas->attrib);
bas->attrib = NULL;
}
--
1.8.4


2014-12-17 15:49:09

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 11/19] android/hog: Keep track on read/write char and descr

---
android/hog.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 91 insertions(+), 20 deletions(-)

diff --git a/android/hog.c b/android/hog.c
index 766b66a..bb770c8 100644
--- a/android/hog.c
+++ b/android/hog.c
@@ -147,6 +147,48 @@ static void destroy_gatt_req(struct gatt_request *req)
free(req);
}

+static void write_char(struct bt_hog *hog, GAttrib *attrib, uint16_t handle,
+ const uint8_t *value, size_t vlen,
+ GAttribResultFunc func,
+ gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(hog, user_data);
+ if (!req)
+ return;
+
+ id = gatt_write_char(attrib, handle, value, vlen, func, req);
+
+ if (set_and_store_gatt_req(hog, req, id))
+ return;
+
+ error("hog: Could not read char");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void read_char(struct bt_hog *hog, GAttrib *attrib, uint16_t handle,
+ GAttribResultFunc func, gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(hog, user_data);
+ if (!req)
+ return;
+
+ id = gatt_read_char(attrib, handle, func, req);
+
+ if (set_and_store_gatt_req(hog, req, id))
+ return;
+
+ error("hog: Could not read char");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
static void discover_desc(struct bt_hog *hog, GAttrib *attrib,
uint16_t start, uint16_t end, gatt_cb_t func,
gpointer user_data)
@@ -274,9 +316,12 @@ static void report_value_cb(const guint8 *pdu, guint16 len, gpointer user_data)
static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
guint16 plen, gpointer user_data)
{
- struct report *report = user_data;
+ struct gatt_request *req = user_data;
+ struct report *report = req->user_data;
struct bt_hog *hog = report->hog;

+ destroy_gatt_req(req);
+
if (status != 0) {
error("Write report characteristic descriptor failed: %s",
att_ecode2str(status));
@@ -291,33 +336,40 @@ static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
DBG("Report characteristic descriptor written: notifications enabled");
}

-static void write_ccc(GAttrib *attrib, uint16_t handle, void *user_data)
+static void write_ccc(struct bt_hog *hog, GAttrib *attrib, uint16_t handle,
+ void *user_data)
{
uint8_t value[2];

put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);

- gatt_write_char(attrib, handle, value, sizeof(value),
+ write_char(hog, attrib, handle, value, sizeof(value),
report_ccc_written_cb, user_data);
}

static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len,
gpointer user_data)
{
- struct report *report = user_data;
+ struct gatt_request *req = user_data;
+ struct report *report = req->user_data;
+
+ destroy_gatt_req(req);

if (status != 0) {
error("Error reading CCC value: %s", att_ecode2str(status));
return;
}

- write_ccc(report->hog->attrib, report->ccc_handle, report);
+ write_ccc(report->hog, report->hog->attrib, report->ccc_handle, report);
}

static void report_reference_cb(guint8 status, const guint8 *pdu,
guint16 plen, gpointer user_data)
{
- struct report *report = user_data;
+ struct gatt_request *req = user_data;
+ struct report *report = req->user_data;
+
+ destroy_gatt_req(req);

if (status != 0) {
error("Read Report Reference descriptor failed: %s",
@@ -336,7 +388,7 @@ static void report_reference_cb(guint8 status, const guint8 *pdu,

/* Enable notifications only for Input Reports */
if (report->type == HOG_REPORT_TYPE_INPUT)
- gatt_read_char(report->hog->attrib, report->ccc_handle,
+ read_char(report->hog, report->hog->attrib, report->ccc_handle,
ccc_read_cb, report);
}

@@ -359,7 +411,7 @@ static void discover_external_cb(uint8_t status, GSList *descs, void *user_data)
for ( ; descs; descs = descs->next) {
struct gatt_desc *desc = descs->data;

- gatt_read_char(hog->attrib, desc->handle,
+ read_char(hog, hog->attrib, desc->handle,
external_report_reference_cb,
hog);
}
@@ -402,7 +454,7 @@ static void discover_report_cb(uint8_t status, GSList *descs, void *user_data)
report->ccc_handle = desc->handle;
break;
case GATT_REPORT_REFERENCE:
- gatt_read_char(hog->attrib, desc->handle,
+ read_char(hog, hog->attrib, desc->handle,
report_reference_cb, report);
break;
}
@@ -422,7 +474,10 @@ static void discover_report(struct bt_hog *hog, GAttrib *attrib,
static void report_read_cb(guint8 status, const guint8 *pdu, guint16 len,
gpointer user_data)
{
- struct report *report = user_data;
+ struct gatt_request *req = user_data;
+ struct report *report = req->user_data;
+
+ destroy_gatt_req(req);

if (status != 0) {
error("Error reading Report value: %s", att_ecode2str(status));
@@ -445,7 +500,7 @@ static struct report *report_new(struct bt_hog *hog, struct gatt_char *chr)
report->decl = g_memdup(chr, sizeof(*chr));
hog->reports = g_slist_append(hog->reports, report);

- gatt_read_char(hog->attrib, chr->value_handle, report_read_cb, report);
+ read_char(hog, hog->attrib, chr->value_handle, report_read_cb, report);

return report;
}
@@ -487,10 +542,13 @@ static void external_service_char_cb(uint8_t status, GSList *chars,
static void external_report_reference_cb(guint8 status, const guint8 *pdu,
guint16 plen, gpointer user_data)
{
- struct bt_hog *hog = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
uint16_t uuid16;
bt_uuid_t uuid;

+ destroy_gatt_req(req);
+
if (status != 0) {
error("Read External Report Reference descriptor failed: %s",
att_ecode2str(status));
@@ -547,6 +605,10 @@ static struct report *find_report(struct bt_hog *hog, uint8_t type, uint8_t id)
static void output_written_cb(guint8 status, const guint8 *pdu,
guint16 plen, gpointer user_data)
{
+ struct gatt_request *req = user_data;
+
+ destroy_gatt_req(req);
+
if (status != 0) {
error("Write output report failed: %s", att_ecode2str(status));
return;
@@ -593,7 +655,7 @@ static void forward_report(struct uhid_event *ev, void *user_data)
return;

if (report->decl->properties & GATT_CHR_PROP_WRITE)
- gatt_write_char(hog->attrib, report->decl->value_handle,
+ write_char(hog, hog->attrib, report->decl->value_handle,
data, size, output_written_cb, hog);
else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
gatt_write_cmd(hog->attrib, report->decl->value_handle,
@@ -696,7 +758,8 @@ static char *item2string(char *str, uint8_t *buf, uint8_t len)
static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
gpointer user_data)
{
- struct bt_hog *hog = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
uint8_t value[HOG_REPORT_MAP_MAX_SIZE];
struct uhid_event ev;
ssize_t vlen;
@@ -704,6 +767,8 @@ static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
int i, err;
GError *gerr = NULL;

+ destroy_gatt_req(req);
+
if (status != 0) {
error("Report Map read failed: %s", att_ecode2str(status));
return;
@@ -774,10 +839,13 @@ static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
gpointer user_data)
{
- struct bt_hog *hog = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
uint8_t value[HID_INFO_SIZE];
ssize_t vlen;

+ destroy_gatt_req(req);
+
if (status != 0) {
error("HID Information read failed: %s",
att_ecode2str(status));
@@ -801,10 +869,13 @@ static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
gpointer user_data)
{
- struct bt_hog *hog = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
uint8_t value;
ssize_t vlen;

+ destroy_gatt_req(req);
+
if (status != 0) {
error("Protocol Mode characteristic read failed: %s",
att_ecode2str(status));
@@ -873,7 +944,7 @@ static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data)
report = report_new(hog, chr);
discover_report(hog, hog->attrib, start, end, report);
} else if (bt_uuid_cmp(&uuid, &report_map_uuid) == 0) {
- gatt_read_char(hog->attrib, chr->value_handle,
+ read_char(hog, hog->attrib, chr->value_handle,
report_map_read_cb, hog);
discover_external(hog, hog->attrib, start, end, hog);
} else if (bt_uuid_cmp(&uuid, &info_uuid) == 0)
@@ -886,12 +957,12 @@ static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data)

if (proto_mode_handle) {
hog->proto_mode_handle = proto_mode_handle;
- gatt_read_char(hog->attrib, proto_mode_handle,
+ read_char(hog, hog->attrib, proto_mode_handle,
proto_mode_read_cb, hog);
}

if (info_handle)
- gatt_read_char(hog->attrib, info_handle, info_read_cb, hog);
+ read_char(hog, hog->attrib, info_handle, info_read_cb, hog);
}

static void report_free(void *data)
@@ -1278,7 +1349,7 @@ int bt_hog_send_report(struct bt_hog *hog, void *data, size_t size, int type)
DBG("hog: Write report, handle 0x%X", report->decl->value_handle);

if (report->decl->properties & GATT_CHR_PROP_WRITE)
- gatt_write_char(hog->attrib, report->decl->value_handle,
+ write_char(hog, hog->attrib, report->decl->value_handle,
data, size, output_written_cb, hog);

if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
--
1.8.4


2014-12-17 15:49:08

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 10/19] android/hog: Keep track on discover characteristic and descriptors

---
android/hog.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 74 insertions(+), 19 deletions(-)

diff --git a/android/hog.c b/android/hog.c
index 66b117d..766b66a 100644
--- a/android/hog.c
+++ b/android/hog.c
@@ -147,6 +147,48 @@ static void destroy_gatt_req(struct gatt_request *req)
free(req);
}

+static void discover_desc(struct bt_hog *hog, GAttrib *attrib,
+ uint16_t start, uint16_t end, gatt_cb_t func,
+ gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(hog, user_data);
+ if (!req)
+ return;
+
+ id = gatt_discover_desc(attrib, start, end, NULL, func, req);
+ if (set_and_store_gatt_req(hog, req, id))
+ return;
+
+ error("hog: Could not discover descriptors");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void discover_char(struct bt_hog *hog, GAttrib *attrib,
+ uint16_t start, uint16_t end,
+ bt_uuid_t *uuid, gatt_cb_t func,
+ gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(hog, user_data);
+ if (!req)
+ return;
+
+ id = gatt_discover_char(attrib, start, end, uuid, func, req);
+
+ if (set_and_store_gatt_req(hog, req, id))
+ return;
+
+ error("hog: Could not discover characteristic");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
static void discover_primary(struct bt_hog *hog, GAttrib *attrib,
bt_uuid_t *uuid, gatt_cb_t func,
gpointer user_data)
@@ -303,7 +345,10 @@ static void external_report_reference_cb(guint8 status, const guint8 *pdu,

static void discover_external_cb(uint8_t status, GSList *descs, void *user_data)
{
- struct bt_hog *hog = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
+
+ destroy_gatt_req(req);

if (status != 0) {
error("Discover external descriptors failed: %s",
@@ -315,12 +360,14 @@ static void discover_external_cb(uint8_t status, GSList *descs, void *user_data)
struct gatt_desc *desc = descs->data;

gatt_read_char(hog->attrib, desc->handle,
- external_report_reference_cb, hog);
+ external_report_reference_cb,
+ hog);
}
}

-static void discover_external(GAttrib *attrib, uint16_t start, uint16_t end,
- gpointer user_data)
+static void discover_external(struct bt_hog *hog, GAttrib *attrib,
+ uint16_t start, uint16_t end,
+ gpointer user_data)
{
bt_uuid_t uuid;

@@ -329,15 +376,18 @@ static void discover_external(GAttrib *attrib, uint16_t start, uint16_t end,

bt_uuid16_create(&uuid, GATT_EXTERNAL_REPORT_REFERENCE);

- gatt_discover_desc(attrib, start, end, NULL, discover_external_cb,
+ discover_desc(hog, attrib, start, end, discover_external_cb,
user_data);
}

static void discover_report_cb(uint8_t status, GSList *descs, void *user_data)
{
- struct report *report = user_data;
+ struct gatt_request *req = user_data;
+ struct report *report = req->user_data;
struct bt_hog *hog = report->hog;

+ destroy_gatt_req(req);
+
if (status != 0) {
error("Discover report descriptors failed: %s",
att_ecode2str(status));
@@ -359,14 +409,14 @@ static void discover_report_cb(uint8_t status, GSList *descs, void *user_data)
}
}

-static void discover_report(GAttrib *attrib, uint16_t start, uint16_t end,
+static void discover_report(struct bt_hog *hog, GAttrib *attrib,
+ uint16_t start, uint16_t end,
gpointer user_data)
{
if (start > end)
return;

- gatt_discover_desc(attrib, start, end, NULL, discover_report_cb,
- user_data);
+ discover_desc(hog, attrib, start, end, discover_report_cb, user_data);
}

static void report_read_cb(guint8 status, const guint8 *pdu, guint16 len,
@@ -403,11 +453,14 @@ static struct report *report_new(struct bt_hog *hog, struct gatt_char *chr)
static void external_service_char_cb(uint8_t status, GSList *chars,
void *user_data)
{
- struct bt_hog *hog = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
struct gatt_primary *primary = hog->primary;
struct report *report;
GSList *l;

+ destroy_gatt_req(req);
+
if (status != 0) {
const char *str = att_ecode2str(status);
DBG("Discover external service characteristic failed: %s", str);
@@ -427,7 +480,7 @@ static void external_service_char_cb(uint8_t status, GSList *chars,
report = report_new(hog, chr);
start = chr->value_handle + 1;
end = (next ? next->handle - 1 : primary->range.end);
- discover_report(hog->attrib, start, end, report);
+ discover_report(hog, hog->attrib, start, end, report);
}
}

@@ -458,11 +511,10 @@ static void external_report_reference_cb(guint8 status, const guint8 *pdu,
return;

bt_uuid16_create(&uuid, uuid16);
- gatt_discover_char(hog->attrib, 0x0001, 0xffff, &uuid,
+ discover_char(hog, hog->attrib, 0x0001, 0xffff, &uuid,
external_service_char_cb, hog);
}

-
static int report_cmp(gconstpointer a, gconstpointer b)
{
const struct report *ra = a, *rb = b;
@@ -778,7 +830,8 @@ static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen,

static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data)
{
- struct bt_hog *hog = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
struct gatt_primary *primary = hog->primary;
bt_uuid_t report_uuid, report_map_uuid, info_uuid;
bt_uuid_t proto_mode_uuid, ctrlpt_uuid;
@@ -786,6 +839,8 @@ static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data)
GSList *l;
uint16_t info_handle = 0, proto_mode_handle = 0;

+ destroy_gatt_req(req);
+
if (status != 0) {
const char *str = att_ecode2str(status);
DBG("Discover all characteristics failed: %s", str);
@@ -816,11 +871,11 @@ static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data)

if (bt_uuid_cmp(&uuid, &report_uuid) == 0) {
report = report_new(hog, chr);
- discover_report(hog->attrib, start, end, report);
+ discover_report(hog, hog->attrib, start, end, report);
} else if (bt_uuid_cmp(&uuid, &report_map_uuid) == 0) {
gatt_read_char(hog->attrib, chr->value_handle,
report_map_read_cb, hog);
- discover_external(hog->attrib, start, end, hog);
+ discover_external(hog, hog->attrib, start, end, hog);
} else if (bt_uuid_cmp(&uuid, &info_uuid) == 0)
info_handle = chr->value_handle;
else if (bt_uuid_cmp(&uuid, &proto_mode_uuid) == 0)
@@ -975,7 +1030,7 @@ static void find_included_cb(uint8_t status, GSList *services, void *user_data)
memcpy(hog->primary->uuid, include->uuid, sizeof(include->uuid));
memcpy(&hog->primary->range, &include->range, sizeof(include->range));

- gatt_discover_char(hog->attrib, hog->primary->range.start,
+ discover_char(hog, hog->attrib, hog->primary->range.start,
hog->primary->range.end, NULL,
char_discovered_cb, hog);
}
@@ -1034,7 +1089,7 @@ static void hog_attach_hog(struct bt_hog *hog, struct gatt_primary *primary)

if (!hog->primary) {
hog->primary = g_memdup(primary, sizeof(*primary));
- gatt_discover_char(hog->attrib, primary->range.start,
+ discover_char(hog, hog->attrib, primary->range.start,
primary->range.end, NULL,
char_discovered_cb, hog);
return;
@@ -1135,7 +1190,7 @@ bool bt_hog_attach(struct bt_hog *hog, void *gatt)
}

if (hog->reports == NULL) {
- gatt_discover_char(hog->attrib, primary->range.start,
+ discover_char(hog, hog->attrib, primary->range.start,
primary->range.end, NULL,
char_discovered_cb, hog);
return true;
--
1.8.4


2014-12-17 15:49:07

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 09/19] android/hog: Keep track on primary and include service search

---
android/hog.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 80 insertions(+), 8 deletions(-)

diff --git a/android/hog.c b/android/hog.c
index 0e0eafe..66b117d 100644
--- a/android/hog.c
+++ b/android/hog.c
@@ -117,6 +117,29 @@ struct gatt_request {
void *user_data;
};

+static struct gatt_request *create_request(struct bt_hog *hog,
+ void *user_data)
+{
+ struct gatt_request *req;
+
+ req = new0(struct gatt_request, 1);
+ if (!req)
+ return NULL;
+
+ req->user_data = user_data;
+ req->hog = bt_hog_ref(hog);
+
+ return req;
+}
+
+static bool set_and_store_gatt_req(struct bt_hog *hog,
+ struct gatt_request *req,
+ unsigned int id)
+{
+ req->id = id;
+ return queue_push_head(hog->gatt_op, req);
+}
+
static void destroy_gatt_req(struct gatt_request *req)
{
queue_remove(req->hog->gatt_op, req);
@@ -124,6 +147,48 @@ static void destroy_gatt_req(struct gatt_request *req)
free(req);
}

+static void discover_primary(struct bt_hog *hog, GAttrib *attrib,
+ bt_uuid_t *uuid, gatt_cb_t func,
+ gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(hog, user_data);
+ if (!req)
+ return;
+
+ id = gatt_discover_primary(attrib, uuid, func, req);
+
+ if (set_and_store_gatt_req(hog, req, id))
+ return;
+
+ error("hog: Could not send discover primary");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void find_included(struct bt_hog *hog, GAttrib *attrib,
+ uint16_t start, uint16_t end,
+ gatt_cb_t func, gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(hog, user_data);
+ if (!req)
+ return;
+
+ id = gatt_find_included(attrib, start, end, func, req);
+
+ if (set_and_store_gatt_req(hog, req, id))
+ return;
+
+ error("Could not find included");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
static void report_value_cb(const guint8 *pdu, guint16 len, gpointer user_data)
{
struct report *report = user_data;
@@ -864,12 +929,15 @@ void bt_hog_unref(struct bt_hog *hog)

static void find_included_cb(uint8_t status, GSList *services, void *user_data)
{
- struct bt_hog *hog = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
struct gatt_included *include;
GSList *l;

DBG("");

+ destroy_gatt_req(req);
+
if (hog->primary)
return;

@@ -895,8 +963,10 @@ static void find_included_cb(uint8_t status, GSList *services, void *user_data)
for (l = services; l; l = l->next) {
include = l->data;

- gatt_find_included(hog->attrib, include->range.start,
- include->range.end, find_included_cb, hog);
+ find_included(hog, hog->attrib,
+ include->range.start,
+ include->range.end, find_included_cb,
+ hog);
}
return;
}
@@ -981,12 +1051,15 @@ static void hog_attach_hog(struct bt_hog *hog, struct gatt_primary *primary)

static void primary_cb(uint8_t status, GSList *services, void *user_data)
{
- struct bt_hog *hog = user_data;
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
struct gatt_primary *primary;
GSList *l;

DBG("");

+ destroy_gatt_req(req);
+
if (status) {
const char *str = att_ecode2str(status);
DBG("Discover primary failed: %s", str);
@@ -1026,9 +1099,8 @@ static void primary_cb(uint8_t status, GSList *services, void *user_data)
for (l = services; l; l = l->next) {
primary = l->data;

- gatt_find_included(hog->attrib, primary->range.start,
- primary->range.end, find_included_cb,
- hog);
+ find_included(hog, hog->attrib, primary->range.start,
+ primary->range.end, find_included_cb, hog);
}
}

@@ -1043,7 +1115,7 @@ bool bt_hog_attach(struct bt_hog *hog, void *gatt)
hog->attrib = g_attrib_ref(gatt);

if (!primary) {
- gatt_discover_primary(hog->attrib, NULL, primary_cb, hog);
+ discover_primary(hog, hog->attrib, NULL, primary_cb, hog);
return true;
}

--
1.8.4


2014-12-17 15:49:06

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 08/19] android/hog: Cancel all GATT operations on disconnect

This patch makes sure that HOG cancels its GATT requests before
removing his attrib reference.

However some request might not be possible to cancel. It will be handled
in following patches
---
android/hog.c | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/android/hog.c b/android/hog.c
index b87fd2f..0e0eafe 100644
--- a/android/hog.c
+++ b/android/hog.c
@@ -111,6 +111,19 @@ struct report {
uint8_t *value;
};

+struct gatt_request {
+ unsigned int id;
+ struct bt_hog *hog;
+ void *user_data;
+};
+
+static void destroy_gatt_req(struct gatt_request *req)
+{
+ queue_remove(req->hog->gatt_op, req);
+ bt_hog_unref(req->hog);
+ free(req);
+}
+
static void report_value_cb(const guint8 *pdu, guint16 len, gpointer user_data)
{
struct report *report = user_data;
@@ -770,6 +783,12 @@ static void report_free(void *data)
g_free(report);
}

+static void cancel_gatt_req(struct gatt_request *req)
+{
+ if (g_attrib_cancel(req->hog->attrib, req->id))
+ destroy_gatt_req(req);
+}
+
static void hog_free(void *data)
{
struct bt_hog *hog = data;
@@ -785,7 +804,7 @@ static void hog_free(void *data)
g_slist_free_full(hog->reports, report_free);
g_free(hog->name);
g_free(hog->primary);
- queue_destroy(hog->gatt_op, NULL);
+ queue_destroy(hog->gatt_op, (void *) destroy_gatt_req);
g_free(hog);
}

@@ -1093,6 +1112,7 @@ void bt_hog_detach(struct bt_hog *hog)
if (hog->bas)
bt_bas_detach(hog->bas);

+ queue_foreach(hog->gatt_op, (void *) cancel_gatt_req, NULL);
g_attrib_unref(hog->attrib);
hog->attrib = NULL;
}
--
1.8.4


2014-12-17 15:49:05

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 07/19] android/hog: Add support to track gatt operations

---
android/hog.c | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/android/hog.c b/android/hog.c
index edee73f..b87fd2f 100644
--- a/android/hog.c
+++ b/android/hog.c
@@ -45,6 +45,7 @@
#include "lib/uuid.h"
#include "src/shared/util.h"
#include "src/shared/uhid.h"
+#include "src/shared/queue.h"

#include "attrib/att.h"
#include "attrib/gattrib.h"
@@ -96,6 +97,7 @@ struct bt_hog {
struct bt_dis *dis;
struct bt_bas *bas;
GSList *instances;
+ struct queue *gatt_op;
};

struct report {
@@ -783,6 +785,7 @@ static void hog_free(void *data)
g_slist_free_full(hog->reports, report_free);
g_free(hog->name);
g_free(hog->primary);
+ queue_destroy(hog->gatt_op, NULL);
g_free(hog);
}

@@ -795,9 +798,16 @@ struct bt_hog *bt_hog_new(const char *name, uint16_t vendor, uint16_t product,
if (!hog)
return NULL;

+ hog->gatt_op = queue_new();
+ if (!hog->gatt_op) {
+ hog_free(hog);
+ return NULL;
+ }
+
hog->uhid = bt_uhid_new_default();
if (!hog->uhid) {
hog_free(hog);
+ queue_destroy(hog->gatt_op, NULL);
return NULL;
}

--
1.8.4


2014-12-17 15:49:04

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 06/19] android/gatt: Improve LE connection after pair

With this patch, GATT keeps ATT connection for 30 sec during bonding,
and once bonding is completed GATT starts service search.

It is needed for example when user choose HOG device from Settings
Application. So far scenario looks like that:

1. Bonding started
2. GATT Service search
3. Bonding ended and LE connection dropped
4. Initiated connection to HOG device

Now it looks like that:

1. Bonding Started
2. Bonding Ended
3. GATT Search Service
4. Connect to HOG device on existing LE connection
---
android/gatt.c | 42 ++++++++++++++++++++++++++++++++++++++++--
1 file changed, 40 insertions(+), 2 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index c47e948..a19c907 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -70,6 +70,7 @@
#define GATT_PERM_WRITE_SIGNED_MITM 0x00020000
#define GATT_PERM_NONE 0x10000000

+#define GATT_PAIR_CONN_TIMEOUT 30
#define GATT_CONN_TIMEOUT 2

static const uint8_t BLUETOOTH_UUID[] = {
@@ -1404,6 +1405,17 @@ static void send_app_connect_notifications(void *data, void *user_data)
send_app_connect_notify(conn, con_data->status);
}

+static struct app_connection *find_conn_without_app(struct gatt_device *dev)
+{
+ struct app_connection conn_match;
+
+ conn_match.device = dev;
+ conn_match.app = NULL;
+
+ return queue_find(app_connections, match_connection_by_device_and_app,
+ &conn_match);
+}
+
static struct app_connection *find_conn(const bdaddr_t *addr, int32_t app_id)
{
struct app_connection conn_match;
@@ -1561,7 +1573,22 @@ reply:
if (!conn)
return;

- search_dev_for_srvc(conn, NULL);
+ if (bt_is_pairing(&dev->bdaddr))
+ /*
+ * If there is bonding ongoing lets wait for paired
+ * callback. Once we get that we can start search
+ * services
+ */
+ conn->timeout_id = g_timeout_add_seconds(
+ GATT_PAIR_CONN_TIMEOUT,
+ connection_timeout, conn);
+ else
+ /*
+ * There is no ongoing bonding, lets search for primary
+ * services
+ *
+ */
+ search_dev_for_srvc(conn, NULL);
}

data.dev = dev;
@@ -7041,6 +7068,7 @@ static void gatt_paired_cb(const bdaddr_t *addr, uint8_t type)
{
struct gatt_device *dev;
char address[18];
+ struct app_connection *conn;

dev = find_device_by_addr(addr);
if (!dev)
@@ -7052,7 +7080,17 @@ static void gatt_paired_cb(const bdaddr_t *addr, uint8_t type)
ba2str(addr, address);
DBG("Paired device %s", address);

- /* Find Service Changed and register for it.*/
+ /* conn without app is internal one used for search primary services */
+ conn = find_conn_without_app(dev);
+ if (!conn)
+ return;
+
+ if (conn->timeout_id > 0) {
+ g_source_remove(conn->timeout_id);
+ conn->timeout_id = 0;
+ }
+
+ search_dev_for_srvc(conn, NULL);
}

static void gatt_unpaired_cb(const bdaddr_t *addr, uint8_t type)
--
1.8.4


2014-12-17 15:49:02

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 04/19] android/gatt: Add paired cb to gatt.c

This will be required in order to register for Service Changed on remote
device
---
android/gatt.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)

diff --git a/android/gatt.c b/android/gatt.c
index 2aa52b0..c47e948 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -7037,6 +7037,24 @@ static bool start_listening(void)
return true;
}

+static void gatt_paired_cb(const bdaddr_t *addr, uint8_t type)
+{
+ struct gatt_device *dev;
+ char address[18];
+
+ dev = find_device_by_addr(addr);
+ if (!dev)
+ return;
+
+ if (dev->bdaddr_type != type)
+ return;
+
+ ba2str(addr, address);
+ DBG("Paired device %s", address);
+
+ /* Find Service Changed and register for it.*/
+}
+
static void gatt_unpaired_cb(const bdaddr_t *addr, uint8_t type)
{
struct gatt_device *dev;
@@ -7060,6 +7078,11 @@ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
{
DBG("");

+ if (!bt_paired_register(gatt_paired_cb)) {
+ error("gatt: Could not register paired callback");
+ return false;
+ }
+
if (!bt_unpaired_register(gatt_unpaired_cb)) {
error("gatt: Could not register unpaired callback");
return false;
@@ -7109,6 +7132,10 @@ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
return true;

failed:
+
+ bt_paired_unregister(gatt_paired_cb);
+ bt_unpaired_unregister(gatt_unpaired_cb);
+
queue_destroy(gatt_apps, NULL);
gatt_apps = NULL;

--
1.8.4


2014-12-17 15:49:03

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 05/19] android/bluetooth: Add API to check if device is bonding

Some modules like GATT might be interested in information about ongoing
bonding on device. It can be useful e.g. to take decision if GATT can do
service search or should wait until bonding is done.
---
android/bluetooth.c | 11 +++++++++++
android/bluetooth.h | 1 +
2 files changed, 12 insertions(+)

diff --git a/android/bluetooth.c b/android/bluetooth.c
index 096085d..910397f 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -1778,6 +1778,17 @@ void bt_paired_unregister(bt_paired_device_cb cb)
queue_remove(paired_cb_list, cb);
}

+bool bt_is_pairing(const bdaddr_t *addr)
+{
+ struct device *dev;
+
+ dev = find_device(addr);
+ if (!dev)
+ return false;
+
+ return dev->pairing;
+}
+
static bool rssi_above_threshold(int old, int new)
{
/* only 8 dBm or more */
diff --git a/android/bluetooth.h b/android/bluetooth.h
index 8e0e6e2..884aed3 100644
--- a/android/bluetooth.h
+++ b/android/bluetooth.h
@@ -94,3 +94,4 @@ void bt_unpaired_unregister(bt_unpaired_device_cb cb);
typedef void (*bt_paired_device_cb)(const bdaddr_t *addr, uint8_t type);
bool bt_paired_register(bt_paired_device_cb cb);
void bt_paired_unregister(bt_paired_device_cb cb);
+bool bt_is_pairing(const bdaddr_t *addr);
--
1.8.4


2014-12-17 15:49:01

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 03/19] android/bluetooth: Add possibility to register for bonded event

Some modules might be interested in fact that device has been bonded.
This patch allows to register for that event

Note that callback is called only on success bonding.
---
android/bluetooth.c | 37 +++++++++++++++++++++++++++++++++++++
android/bluetooth.h | 4 ++++
2 files changed, 41 insertions(+)

diff --git a/android/bluetooth.c b/android/bluetooth.c
index 3d60a49..096085d 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -234,6 +234,7 @@ static struct ipc *hal_ipc = NULL;
static bool kernel_conn_control = false;

static struct queue *unpaired_cb_list = NULL;
+static struct queue *paired_cb_list = NULL;

static void get_device_android_addr(struct device *dev, uint8_t *addr)
{
@@ -909,7 +910,14 @@ static void update_bond_state(struct device *dev, uint8_t status,
HAL_BOND_STATE_BONDING);

send_bond_state_change(dev, status, new_bond);
+}
+
+static void send_paired_notification(void *data, void *user_data)
+{
+ bt_paired_device_cb cb = data;
+ struct device *dev = user_data;

+ cb(&dev->bdaddr, dev->bdaddr_type);
}

static void update_device_state(struct device *dev, uint8_t addr_type,
@@ -1757,6 +1765,19 @@ void bt_unpaired_unregister(bt_unpaired_device_cb cb)
queue_remove(unpaired_cb_list, cb);
}

+bool bt_paired_register(bt_paired_device_cb cb)
+{
+ if (queue_find(paired_cb_list, match_by_value, cb))
+ return false;
+
+ return queue_push_head(paired_cb_list, cb);
+}
+
+void bt_paired_unregister(bt_paired_device_cb cb)
+{
+ queue_remove(paired_cb_list, cb);
+}
+
static bool rssi_above_threshold(int old, int new)
{
/* only 8 dBm or more */
@@ -4327,6 +4348,9 @@ static void pair_device_complete(uint8_t status, uint16_t length,
*/
update_device_state(dev, rp->addr.type, status_mgmt2hal(status), false,
!status, false);
+
+ if (status == MGMT_STATUS_SUCCESS)
+ queue_foreach(paired_cb_list, send_paired_notification, dev);
}

static uint8_t select_device_bearer(struct device *dev)
@@ -5282,6 +5306,14 @@ bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode)
return false;
}

+ paired_cb_list = queue_new();
+ if (!paired_cb_list) {
+ error("Cannot allocate queue for paired callbacks");
+ queue_destroy(unpaired_cb_list, NULL);
+ unpaired_cb_list = NULL;
+ return false;
+ }
+
missing_settings = adapter.current_settings ^
adapter.supported_settings;

@@ -5349,6 +5381,8 @@ bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode)
failed:
queue_destroy(unpaired_cb_list, NULL);
unpaired_cb_list = NULL;
+ queue_destroy(paired_cb_list, NULL);
+ paired_cb_list = NULL;

return false;
}
@@ -5368,4 +5402,7 @@ void bt_bluetooth_unregister(void)

queue_destroy(unpaired_cb_list, NULL);
unpaired_cb_list = NULL;
+
+ queue_destroy(paired_cb_list, NULL);
+ paired_cb_list = NULL;
}
diff --git a/android/bluetooth.h b/android/bluetooth.h
index 8970559..8e0e6e2 100644
--- a/android/bluetooth.h
+++ b/android/bluetooth.h
@@ -90,3 +90,7 @@ void bt_auto_connect_remove(const bdaddr_t *addr);
typedef void (*bt_unpaired_device_cb)(const bdaddr_t *addr, uint8_t type);
bool bt_unpaired_register(bt_unpaired_device_cb cb);
void bt_unpaired_unregister(bt_unpaired_device_cb cb);
+
+typedef void (*bt_paired_device_cb)(const bdaddr_t *addr, uint8_t type);
+bool bt_paired_register(bt_paired_device_cb cb);
+void bt_paired_unregister(bt_paired_device_cb cb);
--
1.8.4


2014-12-17 15:49:00

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 02/19] android/bluetooth: Minor fix, add missing NULL assignment

---
android/bluetooth.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/android/bluetooth.c b/android/bluetooth.c
index 56405d1..3d60a49 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -5348,6 +5348,8 @@ bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode)

failed:
queue_destroy(unpaired_cb_list, NULL);
+ unpaired_cb_list = NULL;
+
return false;
}

--
1.8.4


2014-12-17 15:48:59

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 01/19] android/bluetooth: Minor typo fix

---
android/bluetooth.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/android/bluetooth.c b/android/bluetooth.c
index 169f0bf..56405d1 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -5278,7 +5278,7 @@ bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode)

unpaired_cb_list = queue_new();
if (!unpaired_cb_list) {
- error("Can not allocate queue for unpaired callbacks");
+ error("Cannot allocate queue for unpaired callbacks");
return false;
}

--
1.8.4