2014-12-09 15:11:22

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 00/25] 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 4 patches)

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

* 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 9 - 25 solve this issue.

Lukasz Rymanowski (25):
android/bluetooth: Minor typo fix
android/bluetooth: Minor fix, add missing NULL assignment
android/gatt: Fix for setting conn timeout
android/hog: Minor coding style fixes
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: Add guards to the callbacks
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/bas: Add guard to GATT callbacks
android/dis: Add queue to keep track on GATT operations
android/dis: Keep track on discover and read characteristic
android/dis: Add guard for GATT callbacks
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/scpp: Add guard for GATT callbacks

android/bas.c | 192 +++++++++++++++++++++++--
android/bluetooth.c | 52 ++++++-
android/bluetooth.h | 5 +
android/dis.c | 123 +++++++++++++++-
android/gatt.c | 70 ++++++++-
android/hog.c | 401 +++++++++++++++++++++++++++++++++++++++++++++-------
android/scpp.c | 171 ++++++++++++++++++++--
7 files changed, 932 insertions(+), 82 deletions(-)

--
1.8.4



2014-12-10 07:23:50

by Luiz Augusto von Dentz

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

Hi Lukasz,

On Tue, Dec 9, 2014 at 5:11 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 4 patches)
>
> * Changed GATT behaviour during dedicated bonding. Now GATT will not start
> search services until bonding is done. That fixes PTS connection issue
> (patches 5-8)
>
> * 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 9 - 25 solve this issue.

I remember applying a patch from Arman that should have fixed the
problem above: shared/att: cancel_all before calling disconnect cb. In
any if that doesn't really solve the problem the client should cancel
their pending operations before releasing its reference which means no
callback should be called after that.

> Lukasz Rymanowski (25):
> android/bluetooth: Minor typo fix
> android/bluetooth: Minor fix, add missing NULL assignment
> android/gatt: Fix for setting conn timeout
> android/hog: Minor coding style fixes
> 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: Add guards to the callbacks
> 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/bas: Add guard to GATT callbacks
> android/dis: Add queue to keep track on GATT operations
> android/dis: Keep track on discover and read characteristic
> android/dis: Add guard for GATT callbacks
> 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/scpp: Add guard for GATT callbacks
>
> android/bas.c | 192 +++++++++++++++++++++++--
> android/bluetooth.c | 52 ++++++-
> android/bluetooth.h | 5 +
> android/dis.c | 123 +++++++++++++++-
> android/gatt.c | 70 ++++++++-
> android/hog.c | 401 +++++++++++++++++++++++++++++++++++++++++++++-------
> android/scpp.c | 171 ++++++++++++++++++++--
> 7 files changed, 932 insertions(+), 82 deletions(-)
>
> --
> 1.8.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html



--
Luiz Augusto von Dentz

2014-12-09 15:37:03

by Lukasz Rymanowski

[permalink] [raw]
Subject: Re: [PATCH 04/25] android/hog: Minor coding style fixes

Hi,

On 9 December 2014 at 16:11, Lukasz Rymanowski
<[email protected]> wrote:
> ---
> android/hog.c | 24 ++++++++++++++++--------
> 1 file changed, 16 insertions(+), 8 deletions(-)
>
> diff --git a/android/hog.c b/android/hog.c
> index edee73f..872f4a5 100644
> --- a/android/hog.c
> +++ b/android/hog.c
> @@ -221,7 +221,8 @@ static void report_reference_cb(guint8 status, const guint8 *pdu,
> static void external_report_reference_cb(guint8 status, const guint8 *pdu,
> guint16 plen, gpointer user_data);
>
> -static void discover_external_cb(uint8_t status, GSList *descs, void *user_data)
> +static void discover_external_cb(uint8_t status, GSList *descs,
> + void *user_data)
> {
> struct bt_hog *hog = user_data;
>
> @@ -253,7 +254,8 @@ static void discover_external(GAttrib *attrib, uint16_t start, uint16_t end,
> user_data);
> }
>
> -static void discover_report_cb(uint8_t status, GSList *descs, void *user_data)
> +static void discover_report_cb(uint8_t status, GSList *descs,
> + void *user_data)
> {
> struct report *report = user_data;
> struct bt_hog *hog = report->hog;
> @@ -399,7 +401,8 @@ static int report_cmp(gconstpointer a, gconstpointer b)
> return ra->id - rb->id;
> }
>
> -static struct report *find_report(struct bt_hog *hog, uint8_t type, uint8_t id)
> +static struct report *find_report(struct bt_hog *hog, uint8_t type,
> + uint8_t id)
> {
> struct report cmp;
> GSList *l;
> @@ -499,8 +502,8 @@ done:
> error("bt_uhid_send: %s", strerror(-err));
> }
>
> -static bool get_descriptor_item_info(uint8_t *buf, ssize_t blen, ssize_t *len,
> - bool *is_long)
> +static bool get_descriptor_item_info(uint8_t *buf, ssize_t blen,
> + ssize_t *len, bool *is_long)
> {
> if (!blen)
> return false;
> @@ -696,7 +699,8 @@ static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
> DBG("HoG is operating in Report Protocol Mode");
> }
>
> -static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data)
> +static void char_discovered_cb(uint8_t status, GSList *chars,
> + void *user_data)
> {
> struct bt_hog *hog = user_data;
> struct gatt_primary *primary = hog->primary;
> @@ -827,13 +831,16 @@ void bt_hog_unref(struct bt_hog *hog)
> if (!hog)
> return;
>
> + DBG("ref %d", hog->ref_count - 1);
> +
> if (__sync_sub_and_fetch(&hog->ref_count, 1))
> return;
>
> hog_free(hog);
> }
>
> -static void find_included_cb(uint8_t status, GSList *services, void *user_data)
> +static void find_included_cb(uint8_t status, GSList *services,
> + void *user_data)
> {
> struct bt_hog *hog = user_data;
> struct gatt_included *include;
> @@ -881,7 +888,8 @@ static void find_included_cb(uint8_t status, GSList *services, void *user_data)
> char_discovered_cb, hog);
> }
>
> -static void hog_attach_scpp(struct bt_hog *hog, struct gatt_primary *primary)
> +static void hog_attach_scpp(struct bt_hog *hog,
> + struct gatt_primary *primary)
> {
> if (hog->scpp) {
> bt_scpp_attach(hog->scpp, hog->attrib);
> --
> 1.8.4
>

Seems like this patch can be ignored. Looks like my editor just
incorrectly shows me 80 bound. It does not take font width into
account correctly.
Thanks Szymon for pointing it out. Hope that other patches are not
broked due to this.

\Lukasz

2014-12-09 15:11:47

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 25/25] android/scpp: Add guard for GATT callbacks

---
android/scpp.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

diff --git a/android/scpp.c b/android/scpp.c
index 448c7a5..5c75496 100644
--- a/android/scpp.c
+++ b/android/scpp.c
@@ -142,6 +142,15 @@ static void discover_desc(struct bt_scpp *scpp, GAttrib *attrib,
free(req);
}

+static bool scpp_is_connected(struct bt_scpp *scpp)
+{
+ if (scpp && scpp->attrib)
+ return true;
+
+ info("scpp: SCPP disconnected!");
+ return false;
+}
+
static void write_char(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle,
const uint8_t *value, size_t vlen,
GAttribResultFunc func,
@@ -248,6 +257,9 @@ static void ccc_written_cb(guint8 status, const guint8 *pdu,

destroy_gatt_req(req);

+ if (!scpp_is_connected(scan))
+ return;
+
if (status != 0) {
error("Write Scan Refresh CCC failed: %s",
att_ecode2str(status));
@@ -281,6 +293,9 @@ static void discover_descriptor_cb(uint8_t status, GSList *descs,

destroy_gatt_req(req);

+ if (!scpp_is_connected(scan))
+ return;
+
if (status != 0) {
error("Discover descriptors failed: %s", att_ecode2str(status));
return;
@@ -303,6 +318,9 @@ static void refresh_discovered_cb(uint8_t status, GSList *chars,

destroy_gatt_req(req);

+ if (!scpp_is_connected(scan))
+ return;
+
if (status) {
error("Scan Refresh %s", att_ecode2str(status));
return;
@@ -339,6 +357,9 @@ static void iwin_discovered_cb(uint8_t status, GSList *chars, void *user_data)

destroy_gatt_req(req);

+ if (!scpp_is_connected(scan))
+ return;
+
if (status) {
error("Discover Scan Interval Window: %s",
att_ecode2str(status));
--
1.8.4


2014-12-09 15:11:46

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 24/25] 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 d83ac51..448c7a5 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-09 15:11:44

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 22/25] 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..f8587af 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 req_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->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-09 15:11:45

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 23/25] 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 f8587af..d83ac51 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->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-09 15:11:43

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 21/25] android/dis: Add guard for GATT callbacks

---
android/dis.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/android/dis.c b/android/dis.c
index 0200ab8..e754084 100644
--- a/android/dis.c
+++ b/android/dis.c
@@ -75,6 +75,15 @@ static void destroy_gatt_req(struct gatt_request *req)
free(req);
}

+static bool dis_is_connected(struct bt_dis *dis)
+{
+ if (dis && dis->attrib)
+ return true;
+
+ info("dis: DIS disconnected!");
+ return false;
+}
+
static void dis_free(struct bt_dis *dis)
{
bt_dis_detach(dis);
@@ -158,6 +167,9 @@ static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len,

destroy_gatt_req(req);

+ if (!dis_is_connected(dis))
+ return;
+
if (status != 0) {
error("Error reading PNP_ID value: %s", att_ecode2str(status));
return;
@@ -238,6 +250,9 @@ static void configure_deviceinfo_cb(uint8_t status, GSList *characteristics,

destroy_gatt_req(req);

+ if (!dis_is_connected(d))
+ return;
+
if (status != 0) {
error("Discover deviceinfo characteristics: %s",
att_ecode2str(status));
--
1.8.4


2014-12-09 15:11:42

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 20/25] 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 2f4a3b9..0200ab8 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->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-09 15:11:41

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 19/25] 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..2f4a3b9 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 req_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->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-09 15:11:40

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 18/25] android/bas: Add guard to GATT callbacks

---
android/bas.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

diff --git a/android/bas.c b/android/bas.c
index 8dcf4af..49ec8a5 100644
--- a/android/bas.c
+++ b/android/bas.c
@@ -225,6 +225,15 @@ static void discover_desc(struct bt_bas *bas, GAttrib *attrib,
free(req);
}

+static bool bas_is_connected(struct bt_bas *bas)
+{
+ if (bas && bas->attrib)
+ return true;
+
+ info("bas: BAS is not connected");
+ return false;
+}
+
static void value_cb(const guint8 *pdu, guint16 len, gpointer user_data)
{
DBG("Battery Level at %u", pdu[ATT_NOTIFICATION_HEADER_SIZE]);
@@ -238,6 +247,9 @@ static void ccc_written_cb(guint8 status, const guint8 *pdu,

destroy_gatt_req(req);

+ if (!bas_is_connected(bas))
+ return;
+
if (status != 0) {
error("Write Scan Refresh CCC failed: %s",
att_ecode2str(status));
@@ -269,6 +281,9 @@ static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len,

destroy_gatt_req(req);

+ if (!bas_is_connected(bas))
+ return;
+
if (status != 0) {
error("Error reading CCC value: %s", att_ecode2str(status));
return;
@@ -286,6 +301,9 @@ static void discover_descriptor_cb(uint8_t status, GSList *descs,

destroy_gatt_req(req);

+ if (!bas_is_connected(bas))
+ return;
+
if (status != 0) {
error("Discover descriptors failed: %s", att_ecode2str(status));
return;
@@ -308,6 +326,9 @@ static void bas_discovered_cb(uint8_t status, GSList *chars, void *user_data)

destroy_gatt_req(req);

+ if (!bas_is_connected(bas))
+ return;
+
if (status) {
error("Battery: %s", att_ecode2str(status));
return;
--
1.8.4


2014-12-09 15:11:39

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 17/25] 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 9056095..8dcf4af 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-09 15:11:36

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 14/25] 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 ea80db4..d698e39 100644
--- a/android/hog.c
+++ b/android/hog.c
@@ -156,6 +156,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)
@@ -286,9 +328,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 (!hog_is_connected(hog))
return;

@@ -306,20 +351,24 @@ 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 (!hog_is_connected(report->hog))
return;
@@ -329,13 +378,16 @@ static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len,
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 (!hog_is_connected(report->hog))
return;
@@ -357,7 +409,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);
}

@@ -384,7 +436,7 @@ static void discover_external_cb(uint8_t status, GSList *descs,
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);
}
@@ -431,7 +483,7 @@ static void discover_report_cb(uint8_t status, GSList *descs,
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;
}
@@ -451,7 +503,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 (!hog_is_connected(report->hog))
return;
@@ -477,7 +532,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;
}
@@ -522,10 +577,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 (!hog_is_connected(hog))
return;

@@ -586,6 +644,10 @@ static struct report *find_report(struct bt_hog *hog, uint8_t type,
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;
@@ -632,7 +694,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,
@@ -735,7 +797,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;
@@ -743,6 +806,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 (!hog_is_connected(hog))
return;

@@ -816,10 +881,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 (!hog_is_connected(hog))
return;

@@ -846,10 +914,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 (!hog_is_connected(hog))
return;

@@ -925,7 +996,7 @@ static void char_discovered_cb(uint8_t status, GSList *chars,
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)
@@ -938,12 +1009,12 @@ static void char_discovered_cb(uint8_t status, GSList *chars,

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)
@@ -1341,7 +1412,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-09 15:11:38

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 16/25] 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 bbcd505..9056095 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->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-09 15:11:37

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 15/25] 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..bbcd505 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 req_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->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-09 15:11:34

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 12/25] 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 8823f21..cc81bfa 100644
--- a/android/hog.c
+++ b/android/hog.c
@@ -126,6 +126,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->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);
@@ -133,6 +156,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;
@@ -920,12 +985,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_is_connected(hog))
return;

@@ -954,8 +1022,10 @@ static void find_included_cb(uint8_t status, GSList *services,
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;
}
@@ -1041,12 +1111,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 (!hog_is_connected(hog))
return;

@@ -1089,9 +1162,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);
}
}

@@ -1106,7 +1178,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-09 15:11:35

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 13/25] 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 cc81bfa..ea80db4 100644
--- a/android/hog.c
+++ b/android/hog.c
@@ -156,6 +156,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)
@@ -325,7 +367,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 (!hog_is_connected(hog))
return;
@@ -340,12 +385,14 @@ static void discover_external_cb(uint8_t status, GSList *descs,
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;

@@ -354,16 +401,19 @@ 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 (!hog_is_connected(hog))
return;

@@ -388,14 +438,14 @@ static void discover_report_cb(uint8_t status, GSList *descs,
}
}

-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,
@@ -435,11 +485,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 (!hog_is_connected(hog))
return;

@@ -462,7 +515,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);
}
}

@@ -496,11 +549,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;
@@ -827,7 +879,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;
@@ -835,9 +888,11 @@ static void char_discovered_cb(uint8_t status, GSList *chars,
GSList *l;
uint16_t info_handle = 0, proto_mode_handle = 0;

+ destroy_gatt_req(req);

if (!hog_is_connected(hog))
return;
+
if (status != 0) {
const char *str = att_ecode2str(status);
DBG("Discover all characteristics failed: %s", str);
@@ -868,11 +923,11 @@ static void char_discovered_cb(uint8_t status, GSList *chars,

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)
@@ -1034,7 +1089,7 @@ static void find_included_cb(uint8_t status, GSList *services,
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);
}
@@ -1094,7 +1149,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;
@@ -1198,7 +1253,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-09 15:11:33

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 11/25] android/hog: Add guards to the callbacks

It might happen that there is ongoing gatt operation for HOG but HOG is
already disconnected. Let's check that before processing callback
---
android/hog.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)

diff --git a/android/hog.c b/android/hog.c
index 80bcc2e..8823f21 100644
--- a/android/hog.c
+++ b/android/hog.c
@@ -111,6 +111,15 @@ struct report {
uint8_t *value;
};

+static bool hog_is_connected(struct bt_hog *hog)
+{
+ if (hog && hog->attrib)
+ return true;
+
+ info("hog: HoG disconnected!");
+ return false;
+}
+
struct gatt_request {
unsigned int req_id;
struct bt_hog *hog;
@@ -132,6 +141,9 @@ static void report_value_cb(const guint8 *pdu, guint16 len, gpointer user_data)
uint8_t *buf;
int err;

+ if (!hog_is_connected(hog))
+ return;
+
if (len < ATT_NOTIFICATION_HEADER_SIZE) {
error("Malformed ATT notification");
return;
@@ -170,6 +182,9 @@ static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
struct report *report = user_data;
struct bt_hog *hog = report->hog;

+ if (!hog_is_connected(hog))
+ return;
+
if (status != 0) {
error("Write report characteristic descriptor failed: %s",
att_ecode2str(status));
@@ -199,6 +214,9 @@ static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len,
{
struct report *report = user_data;

+ if (!hog_is_connected(report->hog))
+ return;
+
if (status != 0) {
error("Error reading CCC value: %s", att_ecode2str(status));
return;
@@ -212,6 +230,9 @@ static void report_reference_cb(guint8 status, const guint8 *pdu,
{
struct report *report = user_data;

+ if (!hog_is_connected(report->hog))
+ return;
+
if (status != 0) {
error("Read Report Reference descriptor failed: %s",
att_ecode2str(status));
@@ -241,6 +262,9 @@ static void discover_external_cb(uint8_t status, GSList *descs,
{
struct bt_hog *hog = user_data;

+ if (!hog_is_connected(hog))
+ return;
+
if (status != 0) {
error("Discover external descriptors failed: %s",
att_ecode2str(status));
@@ -275,6 +299,9 @@ static void discover_report_cb(uint8_t status, GSList *descs,
struct report *report = user_data;
struct bt_hog *hog = report->hog;

+ if (!hog_is_connected(hog))
+ return;
+
if (status != 0) {
error("Discover report descriptors failed: %s",
att_ecode2str(status));
@@ -311,6 +338,9 @@ static void report_read_cb(guint8 status, const guint8 *pdu, guint16 len,
{
struct report *report = user_data;

+ if (!hog_is_connected(report->hog))
+ return;
+
if (status != 0) {
error("Error reading Report value: %s", att_ecode2str(status));
return;
@@ -345,6 +375,9 @@ static void external_service_char_cb(uint8_t status, GSList *chars,
struct report *report;
GSList *l;

+ if (!hog_is_connected(hog))
+ return;
+
if (status != 0) {
const char *str = att_ecode2str(status);
DBG("Discover external service characteristic failed: %s", str);
@@ -375,6 +408,9 @@ static void external_report_reference_cb(guint8 status, const guint8 *pdu,
uint16_t uuid16;
bt_uuid_t uuid;

+ if (!hog_is_connected(hog))
+ return;
+
if (status != 0) {
error("Read External Report Reference descriptor failed: %s",
att_ecode2str(status));
@@ -590,6 +626,9 @@ static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
int i, err;
GError *gerr = NULL;

+ if (!hog_is_connected(hog))
+ return;
+
if (status != 0) {
error("Report Map read failed: %s", att_ecode2str(status));
return;
@@ -664,6 +703,9 @@ static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
uint8_t value[HID_INFO_SIZE];
ssize_t vlen;

+ if (!hog_is_connected(hog))
+ return;
+
if (status != 0) {
error("HID Information read failed: %s",
att_ecode2str(status));
@@ -691,6 +733,9 @@ static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
uint8_t value;
ssize_t vlen;

+ if (!hog_is_connected(hog))
+ return;
+
if (status != 0) {
error("Protocol Mode characteristic read failed: %s",
att_ecode2str(status));
@@ -725,6 +770,9 @@ static void char_discovered_cb(uint8_t status, GSList *chars,
GSList *l;
uint16_t info_handle = 0, proto_mode_handle = 0;

+
+ if (!hog_is_connected(hog))
+ return;
if (status != 0) {
const char *str = att_ecode2str(status);
DBG("Discover all characteristics failed: %s", str);
@@ -878,6 +926,9 @@ static void find_included_cb(uint8_t status, GSList *services,

DBG("");

+ if (!hog_is_connected(hog))
+ return;
+
if (hog->primary)
return;

@@ -996,6 +1047,9 @@ static void primary_cb(uint8_t status, GSList *services, void *user_data)

DBG("");

+ if (!hog_is_connected(hog))
+ return;
+
if (status) {
const char *str = att_ecode2str(status);
DBG("Discover primary failed: %s", str);
--
1.8.4


2014-12-09 15:11:31

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 09/25] 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 872f4a5..95b7cb0 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 {
@@ -787,6 +789,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);
}

@@ -799,9 +802,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-09 15:11:32

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 10/25] 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 | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/android/hog.c b/android/hog.c
index 95b7cb0..80bcc2e 100644
--- a/android/hog.c
+++ b/android/hog.c
@@ -111,6 +111,19 @@ struct report {
uint8_t *value;
};

+struct gatt_request {
+ unsigned int req_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;
@@ -774,6 +787,13 @@ static void report_free(void *data)
g_free(report);
}

+static void cancel_gatt_req(struct gatt_request *req)
+{
+ DBG("Canceling ID %u", req->req_id);
+ if (g_attrib_cancel(req->hog->attrib, req->req_id))
+ destroy_gatt_req(req);
+}
+
static void hog_free(void *data)
{
struct bt_hog *hog = data;
@@ -789,7 +809,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);
}

@@ -1101,6 +1121,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-09 15:11:30

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 08/25] 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 4c16ce6..c28d6d2 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[] = {
@@ -1392,6 +1393,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;
@@ -1525,7 +1537,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;
@@ -6961,6 +6988,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)
@@ -6972,7 +7000,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-09 15:11:29

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 07/25] 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 7c4adb8..6e678e4 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -1765,6 +1765,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-09 15:11:28

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 06/25] 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 9c2260c..4c16ce6 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -6957,6 +6957,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;
@@ -6980,6 +6998,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;
@@ -7029,6 +7052,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-09 15:11:27

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 05/25] 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 5652d82..7c4adb8 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -221,6 +221,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)
{
@@ -896,7 +897,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,
@@ -1744,6 +1752,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 */
@@ -4279,6 +4300,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)
@@ -5234,6 +5258,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;

@@ -5301,6 +5333,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;
}
@@ -5320,4 +5354,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-09 15:11:26

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 04/25] android/hog: Minor coding style fixes

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

diff --git a/android/hog.c b/android/hog.c
index edee73f..872f4a5 100644
--- a/android/hog.c
+++ b/android/hog.c
@@ -221,7 +221,8 @@ static void report_reference_cb(guint8 status, const guint8 *pdu,
static void external_report_reference_cb(guint8 status, const guint8 *pdu,
guint16 plen, gpointer user_data);

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

@@ -253,7 +254,8 @@ static void discover_external(GAttrib *attrib, uint16_t start, uint16_t end,
user_data);
}

-static void discover_report_cb(uint8_t status, GSList *descs, void *user_data)
+static void discover_report_cb(uint8_t status, GSList *descs,
+ void *user_data)
{
struct report *report = user_data;
struct bt_hog *hog = report->hog;
@@ -399,7 +401,8 @@ static int report_cmp(gconstpointer a, gconstpointer b)
return ra->id - rb->id;
}

-static struct report *find_report(struct bt_hog *hog, uint8_t type, uint8_t id)
+static struct report *find_report(struct bt_hog *hog, uint8_t type,
+ uint8_t id)
{
struct report cmp;
GSList *l;
@@ -499,8 +502,8 @@ done:
error("bt_uhid_send: %s", strerror(-err));
}

-static bool get_descriptor_item_info(uint8_t *buf, ssize_t blen, ssize_t *len,
- bool *is_long)
+static bool get_descriptor_item_info(uint8_t *buf, ssize_t blen,
+ ssize_t *len, bool *is_long)
{
if (!blen)
return false;
@@ -696,7 +699,8 @@ static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
DBG("HoG is operating in Report Protocol Mode");
}

-static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data)
+static void char_discovered_cb(uint8_t status, GSList *chars,
+ void *user_data)
{
struct bt_hog *hog = user_data;
struct gatt_primary *primary = hog->primary;
@@ -827,13 +831,16 @@ void bt_hog_unref(struct bt_hog *hog)
if (!hog)
return;

+ DBG("ref %d", hog->ref_count - 1);
+
if (__sync_sub_and_fetch(&hog->ref_count, 1))
return;

hog_free(hog);
}

-static void find_included_cb(uint8_t status, GSList *services, void *user_data)
+static void find_included_cb(uint8_t status, GSList *services,
+ void *user_data)
{
struct bt_hog *hog = user_data;
struct gatt_included *include;
@@ -881,7 +888,8 @@ static void find_included_cb(uint8_t status, GSList *services, void *user_data)
char_discovered_cb, hog);
}

-static void hog_attach_scpp(struct bt_hog *hog, struct gatt_primary *primary)
+static void hog_attach_scpp(struct bt_hog *hog,
+ struct gatt_primary *primary)
{
if (hog->scpp) {
bt_scpp_attach(hog->scpp, hog->attrib);
--
1.8.4


2014-12-09 15:11:24

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 02/25] 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 ddd3a7b..5652d82 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -5300,6 +5300,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-09 15:11:25

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 03/25] android/gatt: Fix for setting conn timeout

---
android/gatt.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/android/gatt.c b/android/gatt.c
index 720a205..9c2260c 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -1344,6 +1344,9 @@ static void discover_primary_cb(uint8_t status, GSList *services,

free(cb_data);

+ if (conn->timeout_id > 0)
+ g_source_remove(conn->timeout_id);
+
conn->timeout_id = g_timeout_add_seconds(GATT_CONN_TIMEOUT,
connection_timeout, conn);
}
--
1.8.4


2014-12-09 15:11:23

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH 01/25] 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 ca3dd24..ddd3a7b 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -5230,7 +5230,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