2012-09-24 22:33:35

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH v3 BlueZ 0/4] Support for GATT Find Included

Hi,

Changes from v2:
- Rebased against latest git tree;

Changes from v1:
- Fixed problems found during PTS tests;

- Added a reference count for requests that are running in "parallel"
(if a included service has a 128 bit UUID, it needs to make an
aditional request to resolve this included service);

--

Jefferson Delfes (1):
gatttool: Add "included" command

Vinicius Costa Gomes (3):
gatt: Add support for find included services
core: Add support for included services
attrib: Remove opcode parameter from g_attrib_send()

attrib/client.c | 3 +-
attrib/gatt.c | 242 ++++++++++++++++++++++++++++++++-----
attrib/gatt.h | 9 ++
attrib/gattrib.c | 9 +-
attrib/gattrib.h | 6 +-
attrib/gatttool.c | 2 +-
attrib/interactive.c | 62 +++++++++-
profiles/gatt/gas.c | 2 +-
profiles/thermometer/thermometer.c | 3 +-
src/attrib-server.c | 3 +-
src/device.c | 109 +++++++++++++++--
11 files changed, 394 insertions(+), 56 deletions(-)

--
1.7.12.1



2012-09-28 11:32:12

by Johan Hedberg

[permalink] [raw]
Subject: Re: [PATCH v3 BlueZ 1/4] gatt: Add support for find included services

Hi Lizardo,

On Fri, Sep 28, 2012, Anderson Lizardo wrote:
> On Fri, Sep 28, 2012 at 7:08 AM, Johan Hedberg <[email protected]> wrote:
> > Btw, if you guys are wondering why the stricter coding style checks,
> > I've added the following to .git/hooks/pre-{commit,applypatch}:
> >
> > git diff --cached | ~/src/linux/scripts/checkpatch.pl --no-signoff --ignore INITIALISED_STATIC,NEW_TYPEDEFS,VOLATILE --show-types --mailback -
> >
> > You might want to do the same (with the correct path to checkpatch.pl)).
> > And thanks to Luiz for the idea (he's using the same when applying
> > patches).
>
> Thanks for the very useful tip! But which checkpatch.pl version are
> you using? Mine (from bluetooth-next tree) lacks --ignore and
> --show-types options.

I'm using Linus' tree.

Johan

2012-09-28 11:28:21

by Anderson Lizardo

[permalink] [raw]
Subject: Re: [PATCH v3 BlueZ 1/4] gatt: Add support for find included services

Hi Johan,

On Fri, Sep 28, 2012 at 7:08 AM, Johan Hedberg <[email protected]> wrote:
> Btw, if you guys are wondering why the stricter coding style checks,
> I've added the following to .git/hooks/pre-{commit,applypatch}:
>
> git diff --cached | ~/src/linux/scripts/checkpatch.pl --no-signoff --ignore INITIALISED_STATIC,NEW_TYPEDEFS,VOLATILE --show-types --mailback -
>
> You might want to do the same (with the correct path to checkpatch.pl)).
> And thanks to Luiz for the idea (he's using the same when applying
> patches).

Thanks for the very useful tip! But which checkpatch.pl version are
you using? Mine (from bluetooth-next tree) lacks --ignore and
--show-types options.

Regards,
--
Anderson Lizardo
Instituto Nokia de Tecnologia - INdT
Manaus - Brazil

2012-09-28 11:08:33

by Johan Hedberg

[permalink] [raw]
Subject: Re: [PATCH v3 BlueZ 1/4] gatt: Add support for find included services

Hi Vinicius,

On Mon, Sep 24, 2012, Vinicius Costa Gomes wrote:
> Some services like HID over LE can reference another service using
> included services.
>
> See Vol 3, Part G, section 2.6.3 of Core specification for more
> details.
> ---
> attrib/gatt.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> attrib/gatt.h | 9 +++
> 2 files changed, 203 insertions(+)

This one doesn't apply:

Applying: gatt: Add support for find included services
WARNING:LONG_LINE: line over 80 characters
#107: FILE: attrib/gatt.c:335:
+ resolve_included_uuid_cb, resolve, NULL);

Btw, if you guys are wondering why the stricter coding style checks,
I've added the following to .git/hooks/pre-{commit,applypatch}:

git diff --cached | ~/src/linux/scripts/checkpatch.pl --no-signoff --ignore INITIALISED_STATIC,NEW_TYPEDEFS,VOLATILE --show-types --mailback -

You might want to do the same (with the correct path to checkpatch.pl)).
And thanks to Luiz for the idea (he's using the same when applying
patches).

Johan

2012-09-24 22:33:39

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH v3 BlueZ 4/4] attrib: Remove opcode parameter from g_attrib_send()

In all uses of g_attrib_send() the opcode of the command/event is
already clear because of the att.h functions used to build the ATT
PDU.
---
attrib/client.c | 3 +-
attrib/gatt.c | 56 ++++++++++++++++----------------------
attrib/gattrib.c | 9 ++++--
attrib/gattrib.h | 6 ++--
attrib/gatttool.c | 2 +-
attrib/interactive.c | 2 +-
profiles/gatt/gas.c | 2 +-
profiles/thermometer/thermometer.c | 3 +-
src/attrib-server.c | 3 +-
9 files changed, 39 insertions(+), 47 deletions(-)

diff --git a/attrib/client.c b/attrib/client.c
index 2423fad..a1cc2f3 100644
--- a/attrib/client.c
+++ b/attrib/client.c
@@ -365,8 +365,7 @@ static void events_handler(const uint8_t *pdu, uint16_t len,
case ATT_OP_HANDLE_IND:
opdu = g_attrib_get_buffer(gatt->attrib, &plen);
olen = enc_confirmation(opdu, plen);
- g_attrib_send(gatt->attrib, 0, opdu[0], opdu, olen,
- NULL, NULL, NULL);
+ g_attrib_send(gatt->attrib, 0, opdu, olen, NULL, NULL, NULL);
case ATT_OP_HANDLE_NOTIFY:
if (characteristic_set_value(chr, &pdu[3], len - 3) < 0)
DBG("Can't change Characteristic 0x%02x", handle);
diff --git a/attrib/gatt.c b/attrib/gatt.c
index 902ac23..e6db227 100644
--- a/attrib/gatt.c
+++ b/attrib/gatt.c
@@ -176,8 +176,7 @@ static void primary_by_uuid_cb(guint8 status, const guint8 *ipdu,
if (oplen == 0)
goto done;

- g_attrib_send(dp->attrib, 0, buf[0], buf, oplen, primary_by_uuid_cb,
- dp, NULL);
+ g_attrib_send(dp->attrib, 0, buf, oplen, primary_by_uuid_cb, dp, NULL);
return;

done:
@@ -242,7 +241,7 @@ static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
guint16 oplen = encode_discover_primary(end + 1, 0xffff, NULL,
buf, buflen);

- g_attrib_send(dp->attrib, 0, buf[0], buf, oplen, primary_all_cb,
+ g_attrib_send(dp->attrib, 0, buf, oplen, primary_all_cb,
dp, NULL);

return;
@@ -280,7 +279,7 @@ guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
} else
cb = primary_all_cb;

- return g_attrib_send(attrib, 0, buf[0], buf, plen, cb, dp, NULL);
+ return g_attrib_send(attrib, 0, buf, plen, cb, dp, NULL);
}

static void resolve_included_uuid_cb(uint8_t status, const uint8_t *pdu,
@@ -331,8 +330,8 @@ static guint resolve_included_uuid(struct find_included *fi,
resolve->fi = find_included_ref(fi);
resolve->included = incl;

- return g_attrib_send(fi->attrib, 0, buf[0], buf, oplen,
- resolve_included_uuid_cb, resolve, NULL);
+ return g_attrib_send(fi->attrib, 0, buf, oplen,
+ resolve_included_uuid_cb, resolve, NULL);
}

static struct gatt_included *included_from_buf(const uint8_t *buf, gsize buflen)
@@ -370,8 +369,8 @@ static guint find_included(struct find_included *fi, uint16_t start)

find_included_ref(fi);

- return g_attrib_send(fi->attrib, 0, buf[0], buf, oplen,
- find_included_cb, fi, NULL);
+ return g_attrib_send(fi->attrib, 0, buf, oplen, find_included_cb,
+ fi, NULL);
}

static void find_included_cb(uint8_t status, const uint8_t *pdu, uint16_t len,
@@ -508,8 +507,8 @@ static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
if (oplen == 0)
return;

- g_attrib_send(dc->attrib, 0, buf[0], buf, oplen,
- char_discovered_cb, dc, NULL);
+ g_attrib_send(dc->attrib, 0, buf, oplen, char_discovered_cb,
+ dc, NULL);

return;
}
@@ -547,7 +546,7 @@ guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
dc->end = end;
dc->uuid = g_memdup(uuid, sizeof(bt_uuid_t));

- return g_attrib_send(attrib, 0, buf[0], buf, plen, char_discovered_cb,
+ return g_attrib_send(attrib, 0, buf, plen, char_discovered_cb,
dc, NULL);
}

@@ -563,8 +562,7 @@ guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end,
if (plen == 0)
return 0;

- return g_attrib_send(attrib, 0, ATT_OP_READ_BY_TYPE_REQ,
- buf, plen, func, user_data, NULL);
+ return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL);
}

struct read_long_data {
@@ -623,8 +621,7 @@ static void read_blob_helper(guint8 status, const guint8 *rpdu, guint16 rlen,

plen = enc_read_blob_req(long_read->handle, long_read->size - 1,
buf, buflen);
- id = g_attrib_send(long_read->attrib, long_read->id,
- ATT_OP_READ_BLOB_REQ, buf, plen,
+ id = g_attrib_send(long_read->attrib, long_read->id, buf, plen,
read_blob_helper, long_read, read_long_destroy);

if (id != 0) {
@@ -660,9 +657,8 @@ static void read_char_helper(guint8 status, const guint8 *rpdu,
long_read->size = rlen;

plen = enc_read_blob_req(long_read->handle, rlen - 1, buf, buflen);
- id = g_attrib_send(long_read->attrib, long_read->id,
- ATT_OP_READ_BLOB_REQ, buf, plen, read_blob_helper,
- long_read, read_long_destroy);
+ id = g_attrib_send(long_read->attrib, long_read->id, buf, plen,
+ read_blob_helper, long_read, read_long_destroy);

if (id != 0) {
g_atomic_int_inc(&long_read->ref);
@@ -698,12 +694,12 @@ guint gatt_read_char(GAttrib *attrib, uint16_t handle, uint16_t offset,
if (offset > 0) {
plen = enc_read_blob_req(long_read->handle, offset, buf,
buflen);
- id = g_attrib_send(attrib, 0, ATT_OP_READ_BLOB_REQ, buf, plen,
- read_blob_helper, long_read, read_long_destroy);
+ id = g_attrib_send(attrib, 0, buf, plen, read_blob_helper,
+ long_read, read_long_destroy);
} else {
plen = enc_read_req(handle, buf, buflen);
- id = g_attrib_send(attrib, 0, ATT_OP_READ_REQ, buf, plen,
- read_char_helper, long_read, read_long_destroy);
+ id = g_attrib_send(attrib, 0, buf, plen, read_char_helper,
+ long_read, read_long_destroy);
}

if (id == 0)
@@ -738,8 +734,7 @@ static guint execute_write(GAttrib *attrib, uint8_t flags,
if (plen == 0)
return 0;

- return g_attrib_send(attrib, 0, buf[0], buf, plen, func, user_data,
- NULL);
+ return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL);
}

static guint prepare_write(GAttrib *attrib, uint16_t handle, uint16_t offset,
@@ -788,7 +783,7 @@ static guint prepare_write(GAttrib *attrib, uint16_t handle, uint16_t offset,
if (plen == 0)
return 0;

- return g_attrib_send(attrib, 0, buf[0], buf, plen, prepare_write_cb,
+ return g_attrib_send(attrib, 0, buf, plen, prepare_write_cb,
user_data, NULL);
}

@@ -812,7 +807,7 @@ guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
plen = enc_write_cmd(handle, value, vlen, buf,
buflen);

- return g_attrib_send(attrib, 0, buf[0], buf, plen, func,
+ return g_attrib_send(attrib, 0, buf, plen, func,
user_data, NULL);
}

@@ -841,8 +836,7 @@ guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func,

buf = g_attrib_get_buffer(attrib, &buflen);
plen = enc_mtu_req(mtu, buf, buflen);
- return g_attrib_send(attrib, 0, ATT_OP_MTU_REQ, buf, plen, func,
- user_data, NULL);
+ return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL);
}

guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
@@ -857,8 +851,7 @@ guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
if (plen == 0)
return 0;

- return g_attrib_send(attrib, 0, ATT_OP_FIND_INFO_REQ, buf, plen, func,
- user_data, NULL);
+ return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL);
}

guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen,
@@ -870,8 +863,7 @@ guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen,

buf = g_attrib_get_buffer(attrib, &buflen);
plen = enc_write_cmd(handle, value, vlen, buf, buflen);
- return g_attrib_send(attrib, 0, ATT_OP_WRITE_CMD, buf, plen, NULL,
- user_data, notify);
+ return g_attrib_send(attrib, 0, buf, plen, NULL, user_data, notify);
}

static sdp_data_t *proto_seq_find(sdp_list_t *proto_list)
diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index 108d1d3..6f6942f 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -472,12 +472,13 @@ GAttrib *g_attrib_new(GIOChannel *io)
return g_attrib_ref(attrib);
}

-guint g_attrib_send(GAttrib *attrib, guint id, guint8 opcode,
- const guint8 *pdu, guint16 len, GAttribResultFunc func,
- gpointer user_data, GDestroyNotify notify)
+guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len,
+ GAttribResultFunc func, gpointer user_data,
+ GDestroyNotify notify)
{
struct command *c;
GQueue *queue;
+ uint8_t opcode;

if (attrib->stale)
return 0;
@@ -486,6 +487,8 @@ guint g_attrib_send(GAttrib *attrib, guint id, guint8 opcode,
if (c == NULL)
return 0;

+ opcode = pdu[0];
+
c->opcode = opcode;
c->expected = opcode2expected(opcode);
c->pdu = g_malloc(len);
diff --git a/attrib/gattrib.h b/attrib/gattrib.h
index bcff039..bca966f 100644
--- a/attrib/gattrib.h
+++ b/attrib/gattrib.h
@@ -50,9 +50,9 @@ GIOChannel *g_attrib_get_channel(GAttrib *attrib);
gboolean g_attrib_set_destroy_function(GAttrib *attrib,
GDestroyNotify destroy, gpointer user_data);

-guint g_attrib_send(GAttrib *attrib, guint id, guint8 opcode,
- const guint8 *pdu, guint16 len, GAttribResultFunc func,
- gpointer user_data, GDestroyNotify notify);
+guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len,
+ GAttribResultFunc func, gpointer user_data,
+ GDestroyNotify notify);

gboolean g_attrib_cancel(GAttrib *attrib, guint id);
gboolean g_attrib_cancel_all(GAttrib *attrib);
diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index 416bb71..dea1fc4 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -105,7 +105,7 @@ static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
olen = enc_confirmation(opdu, plen);

if (olen > 0)
- g_attrib_send(attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL);
+ g_attrib_send(attrib, 0, opdu, olen, NULL, NULL, NULL);
}

static gboolean listen_start(gpointer user_data)
diff --git a/attrib/interactive.c b/attrib/interactive.c
index 9f1192e..1b0b9cf 100644
--- a/attrib/interactive.c
+++ b/attrib/interactive.c
@@ -135,7 +135,7 @@ static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
olen = enc_confirmation(opdu, plen);

if (olen > 0)
- g_attrib_send(attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL);
+ g_attrib_send(attrib, 0, opdu, olen, NULL, NULL, NULL);
}

static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
diff --git a/profiles/gatt/gas.c b/profiles/gatt/gas.c
index 28d7fbf..4d8797e 100644
--- a/profiles/gatt/gas.c
+++ b/profiles/gatt/gas.c
@@ -150,7 +150,7 @@ static void indication_cb(const uint8_t *pdu, uint16_t len, gpointer user_data)
/* Confirming indication received */
opdu = g_attrib_get_buffer(gas->attrib, &plen);
olen = enc_confirmation(opdu, plen);
- g_attrib_send(gas->attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL);
+ g_attrib_send(gas->attrib, 0, opdu, olen, NULL, NULL, NULL);

if (gas->changed.start == start && gas->changed.end == end)
return;
diff --git a/profiles/thermometer/thermometer.c b/profiles/thermometer/thermometer.c
index 77dcb26..e9f61b1 100644
--- a/profiles/thermometer/thermometer.c
+++ b/profiles/thermometer/thermometer.c
@@ -1167,8 +1167,7 @@ static void ind_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
olen = enc_confirmation(opdu, plen);

if (olen > 0)
- g_attrib_send(t->attrib, 0, opdu[0], opdu, olen, NULL, NULL,
- NULL);
+ g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL);
}

static void notif_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
diff --git a/src/attrib-server.c b/src/attrib-server.c
index 9b03e54..afe5e48 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -1031,8 +1031,7 @@ done:
length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
channel->mtu);

- g_attrib_send(channel->attrib, 0, opdu[0], opdu, length,
- NULL, NULL, NULL);
+ g_attrib_send(channel->attrib, 0, opdu, length, NULL, NULL, NULL);
}

guint attrib_channel_attach(GAttrib *attrib)
--
1.7.12.1


2012-09-24 22:33:38

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH v3 BlueZ 3/4] core: Add support for included services

Soon after the primary service discovery is complete, we do a included
services discovery for all found services and add each included service
to the 'services' list so they can be probe()'d as a normal profile.

This will also make these services to appear on the D-Bus object tree.
---
src/device.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 96 insertions(+), 13 deletions(-)

diff --git a/src/device.c b/src/device.c
index aa3a607..932f1f9 100644
--- a/src/device.c
+++ b/src/device.c
@@ -104,6 +104,12 @@ struct browse_req {
guint listener_id;
};

+struct included_search {
+ struct browse_req *req;
+ GSList *services;
+ GSList *current;
+};
+
struct attio_data {
guint id;
attio_connect_cb cfunc;
@@ -1822,21 +1828,10 @@ static void device_unregister_services(struct btd_device *device)
device->services = NULL;
}

-static void primary_cb(GSList *services, guint8 status, gpointer user_data)
+static void register_all_services(struct browse_req *req, GSList *services)
{
- struct browse_req *req = user_data;
struct btd_device *device = req->device;

- if (status) {
- if (req->msg) {
- DBusMessage *reply;
- reply = btd_error_failed(req->msg,
- att_ecode2str(status));
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- }
- goto done;
- }
-
device_set_temporary(device, FALSE);

if (device->services)
@@ -1861,11 +1856,99 @@ static void primary_cb(GSList *services, guint8 status, gpointer user_data)

store_services(device);

-done:
device->browse = NULL;
browse_request_free(req);
}

+static int service_by_range_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct gatt_primary *prim = a;
+ const struct att_range *range = b;
+
+ return memcmp(&prim->range, range, sizeof(*range));
+}
+
+static void find_included_cb(GSList *includes, uint8_t status,
+ gpointer user_data)
+{
+ struct included_search *search = user_data;
+ struct btd_device *device = search->req->device;
+ struct gatt_primary *prim;
+ GSList *l;
+
+ if (includes == NULL)
+ goto done;
+
+ for (l = includes; l; l = l->next) {
+ struct gatt_included *incl = l->data;
+
+ if (g_slist_find_custom(search->services, &incl->range,
+ service_by_range_cmp))
+ continue;
+
+ prim = g_new0(struct gatt_primary, 1);
+ memcpy(prim->uuid, incl->uuid, sizeof(prim->uuid));
+ memcpy(&prim->range, &incl->range, sizeof(prim->range));
+
+ search->services = g_slist_append(search->services, prim);
+ }
+
+done:
+ search->current = search->current->next;
+ if (search->current == NULL) {
+ register_all_services(search->req, search->services);
+ g_slist_free(search->services);
+ g_free(search);
+ return;
+ }
+
+ prim = search->current->data;
+ gatt_find_included(device->attrib, prim->range.start, prim->range.end,
+ find_included_cb, search);
+}
+
+static void find_included_services(struct browse_req *req, GSList *services)
+{
+ struct btd_device *device = req->device;
+ struct included_search *search;
+ struct gatt_primary *prim;
+
+ if (services == NULL)
+ return;
+
+ search = g_new0(struct included_search, 1);
+ search->req = req;
+ search->services = g_slist_copy(services);
+ search->current = search->services;
+
+ prim = search->current->data;
+ gatt_find_included(device->attrib, prim->range.start, prim->range.end,
+ find_included_cb, search);
+
+}
+
+static void primary_cb(GSList *services, guint8 status, gpointer user_data)
+{
+ struct browse_req *req = user_data;
+
+ if (status) {
+ struct btd_device *device = req->device;
+
+ if (req->msg) {
+ DBusMessage *reply;
+ reply = btd_error_failed(req->msg,
+ att_ecode2str(status));
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+ }
+
+ device->browse = NULL;
+ browse_request_free(req);
+ return;
+ }
+
+ find_included_services(req, services);
+}
+
static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
{
struct att_callbacks *attcb = user_data;
--
1.7.12.1


2012-09-24 22:33:37

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH v3 BlueZ 2/4] gatttool: Add "included" command

From: Jefferson Delfes <[email protected]>

New command to find included services in interactive mode.
---
attrib/interactive.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)

diff --git a/attrib/interactive.c b/attrib/interactive.c
index 38ac30f..9f1192e 100644
--- a/attrib/interactive.c
+++ b/attrib/interactive.c
@@ -211,6 +211,34 @@ static void primary_by_uuid_cb(GSList *ranges, guint8 status,
rl_forced_update_display();
}

+static void included_cb(GSList *includes, guint8 status, gpointer user_data)
+{
+ GSList *l;
+
+ if (status) {
+ printf("Find included services failed: %s\n",
+ att_ecode2str(status));
+ goto done;
+ }
+
+ if (includes == NULL) {
+ printf("No included services found for this range\n");
+ goto done;
+ }
+
+ printf("\n");
+ for (l = includes; l; l = l->next) {
+ struct gatt_included *incl = l->data;
+ printf("handle: 0x%04x, start handle: 0x%04x, "
+ "end handle: 0x%04x uuid: %s\n",
+ incl->handle, incl->range.start,
+ incl->range.end, incl->uuid);
+ }
+
+done:
+ rl_forced_update_display();
+}
+
static void char_cb(GSList *characteristics, guint8 status, gpointer user_data)
{
GSList *l;
@@ -431,6 +459,36 @@ static int strtohandle(const char *src)
return dst;
}

+static void cmd_included(int argcp, char **argvp)
+{
+ int start = 0x0001;
+ int end = 0xffff;
+
+ if (conn_state != STATE_CONNECTED) {
+ printf("Command failed: disconnected\n");
+ return;
+ }
+
+ if (argcp > 1) {
+ start = strtohandle(argvp[1]);
+ if (start < 0) {
+ printf("Invalid start handle: %s\n", argvp[1]);
+ return;
+ }
+ end = start;
+ }
+
+ if (argcp > 2) {
+ end = strtohandle(argvp[2]);
+ if (end < 0) {
+ printf("Invalid end handle: %s\n", argvp[2]);
+ return;
+ }
+ }
+
+ gatt_find_included(attrib, start, end, included_cb, NULL);
+}
+
static void cmd_char(int argcp, char **argvp)
{
int start = 0x0001;
@@ -755,6 +813,8 @@ static struct {
"Disconnect from a remote device" },
{ "primary", cmd_primary, "[UUID]",
"Primary Service Discovery" },
+ { "included", cmd_included, "[start hnd [end hnd]]",
+ "Find Included Services" },
{ "characteristics", cmd_char, "[start hnd [end hnd [UUID]]]",
"Characteristics Discovery" },
{ "char-desc", cmd_char_desc, "[start hnd] [end hnd]",
--
1.7.12.1


2012-09-24 22:33:36

by Vinicius Costa Gomes

[permalink] [raw]
Subject: [PATCH v3 BlueZ 1/4] gatt: Add support for find included services

Some services like HID over LE can reference another service using
included services.

See Vol 3, Part G, section 2.6.3 of Core specification for more
details.
---
attrib/gatt.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
attrib/gatt.h | 9 +++
2 files changed, 203 insertions(+)

diff --git a/attrib/gatt.c b/attrib/gatt.c
index 6880e2d..902ac23 100644
--- a/attrib/gatt.c
+++ b/attrib/gatt.c
@@ -45,6 +45,21 @@ struct discover_primary {
void *user_data;
};

+struct find_included {
+ GAttrib *attrib;
+ int refs;
+ int err;
+ uint16_t end_handle;
+ GSList *includes;
+ gatt_cb_t cb;
+ void *user_data;
+};
+
+struct included_uuid_resolve {
+ struct find_included *fi;
+ struct gatt_included *included;
+};
+
struct discover_char {
GAttrib *attrib;
bt_uuid_t *uuid;
@@ -61,6 +76,26 @@ static void discover_primary_free(struct discover_primary *dp)
g_free(dp);
}

+static struct find_included *find_included_ref(struct find_included *fi)
+{
+ g_atomic_int_inc(&fi->refs);
+
+ return fi;
+}
+
+static void find_included_unref(struct find_included *fi)
+{
+ if (g_atomic_int_dec_and_test(&fi->refs) == FALSE)
+ return;
+
+ fi->cb(fi->includes, fi->err, fi->user_data);
+
+ g_slist_free_full(fi->includes, g_free);
+ g_attrib_unref(fi->attrib);
+ g_free(fi);
+}
+
+
static void discover_char_free(struct discover_char *dc)
{
g_slist_free_full(dc->characteristics, g_free);
@@ -248,6 +283,165 @@ guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
return g_attrib_send(attrib, 0, buf[0], buf, plen, cb, dp, NULL);
}

+static void resolve_included_uuid_cb(uint8_t status, const uint8_t *pdu,
+ uint16_t len, gpointer user_data)
+{
+ struct included_uuid_resolve *resolve = user_data;
+ struct find_included *fi = resolve->fi;
+ struct gatt_included *incl = resolve->included;
+ bt_uuid_t uuid;
+ unsigned int err = status;
+ size_t buflen;
+ uint8_t *buf;
+
+ if (err)
+ goto done;
+
+ buf = g_attrib_get_buffer(fi->attrib, &buflen);
+ if (dec_read_resp(pdu, len, buf, buflen) != 16) {
+ err = ATT_ECODE_IO;
+ goto done;
+ }
+
+ uuid = att_get_uuid128(buf);
+ bt_uuid_to_string(&uuid, incl->uuid, sizeof(incl->uuid));
+ fi->includes = g_slist_append(fi->includes, incl);
+
+done:
+ if (err)
+ g_free(incl);
+
+ if (fi->err == 0)
+ fi->err = err;
+
+ find_included_unref(fi);
+
+ g_free(resolve);
+}
+
+static guint resolve_included_uuid(struct find_included *fi,
+ struct gatt_included *incl)
+{
+ size_t buflen;
+ uint8_t *buf = g_attrib_get_buffer(fi->attrib, &buflen);
+ guint16 oplen = enc_read_req(incl->range.start, buf, buflen);
+ struct included_uuid_resolve *resolve;
+
+ resolve = g_new0(struct included_uuid_resolve, 1);
+ resolve->fi = find_included_ref(fi);
+ resolve->included = incl;
+
+ return g_attrib_send(fi->attrib, 0, buf[0], buf, oplen,
+ resolve_included_uuid_cb, resolve, NULL);
+}
+
+static struct gatt_included *included_from_buf(const uint8_t *buf, gsize buflen)
+{
+ struct gatt_included *incl = g_new0(struct gatt_included, 1);
+
+ incl->handle = att_get_u16(&buf[0]);
+ incl->range.start = att_get_u16(&buf[2]);
+ incl->range.end = att_get_u16(&buf[4]);
+
+ if (buflen == 8) {
+ bt_uuid_t uuid128;
+ bt_uuid_t uuid16 = att_get_uuid16(&buf[6]);
+
+ bt_uuid_to_uuid128(&uuid16, &uuid128);
+ bt_uuid_to_string(&uuid128, incl->uuid, sizeof(incl->uuid));
+ }
+
+ return incl;
+}
+
+static void find_included_cb(uint8_t status, const uint8_t *pdu, uint16_t len,
+ gpointer user_data);
+
+static guint find_included(struct find_included *fi, uint16_t start)
+{
+ bt_uuid_t uuid;
+ size_t buflen;
+ uint8_t *buf = g_attrib_get_buffer(fi->attrib, &buflen);
+ guint16 oplen;
+
+ bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
+ oplen = enc_read_by_type_req(start, fi->end_handle, &uuid,
+ buf, buflen);
+
+ find_included_ref(fi);
+
+ return g_attrib_send(fi->attrib, 0, buf[0], buf, oplen,
+ find_included_cb, fi, NULL);
+}
+
+static void find_included_cb(uint8_t status, const uint8_t *pdu, uint16_t len,
+ gpointer user_data)
+{
+ struct find_included *fi = user_data;
+ uint16_t last_handle = fi->end_handle;
+ unsigned int err = status;
+ struct att_data_list *list;
+ int i;
+
+ if (err == ATT_ECODE_ATTR_NOT_FOUND)
+ err = 0;
+
+ if (status)
+ goto done;
+
+ list = dec_read_by_type_resp(pdu, len);
+ if (list == NULL) {
+ err = ATT_ECODE_IO;
+ goto done;
+ }
+
+ if (list->len != 6 && list->len != 8) {
+ err = ATT_ECODE_IO;
+ att_data_list_free(list);
+ goto done;
+ }
+
+ for (i = 0; i < list->num; i++) {
+ struct gatt_included *incl;
+
+ incl = included_from_buf(list->data[i], list->len);
+ last_handle = incl->handle;
+
+ /* 128 bit UUID, needs resolving */
+ if (list->len == 6) {
+ resolve_included_uuid(fi, incl);
+ continue;
+ }
+
+ fi->includes = g_slist_append(fi->includes, incl);
+ }
+
+ att_data_list_free(list);
+
+ if (last_handle < fi->end_handle)
+ find_included(fi, last_handle + 1);
+
+done:
+ if (fi->err == 0)
+ fi->err = err;
+
+ find_included_unref(fi);
+}
+
+unsigned int gatt_find_included(GAttrib *attrib, uint16_t start, uint16_t end,
+ gatt_cb_t func, gpointer user_data)
+{
+ struct find_included *fi;
+
+ fi = g_new0(struct find_included, 1);
+ fi->attrib = g_attrib_ref(attrib);
+ fi->end_handle = end;
+ fi->cb = func;
+ fi->user_data = user_data;
+
+ return find_included(fi, start);
+}
+
static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
gpointer user_data)
{
diff --git a/attrib/gatt.h b/attrib/gatt.h
index 6bb6f0f..2897482 100644
--- a/attrib/gatt.h
+++ b/attrib/gatt.h
@@ -61,6 +61,12 @@ struct gatt_primary {
struct att_range range;
};

+struct gatt_included {
+ char uuid[MAX_LEN_UUID_STR + 1];
+ uint16_t handle;
+ struct att_range range;
+};
+
struct gatt_char {
char uuid[MAX_LEN_UUID_STR + 1];
uint16_t handle;
@@ -71,6 +77,9 @@ struct gatt_char {
guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
gpointer user_data);

+unsigned int gatt_find_included(GAttrib *attrib, uint16_t start, uint16_t end,
+ gatt_cb_t func, gpointer user_data);
+
guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
bt_uuid_t *uuid, gatt_cb_t func,
gpointer user_data);
--
1.7.12.1