This patch series introduces support for find included services.
The interactive mode of gatttool receives a new command "included" that
uses gatt_find_included to find these services in given range.
We don't have any way to test find included services with 128-bit UUID128. Yet in PTS, there is only a 16-bit UUID test case. So if anyone can test this, would be great.
We can also extend plugins/gatt-example.c to test 128-bit UUID includes.
Jefferson Delfes (2):
GATT: Add support for find included services
gatttool: Add "included" command
attrib/gatt.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++
attrib/gatt.h | 9 +++
attrib/interactive.c | 54 ++++++++++++++++
3 files changed, 236 insertions(+)
--
1.7.9.4
Lizardo,
> -----Original Message-----
> From: Anderson Lizardo [mailto:[email protected]]
> Sent: Wednesday, March 28, 2012 1:55 PM
> To: Ganir, Chen
> Cc: Jefferson Delfes; [email protected]
> Subject: Re: [RFC BlueZ 0/2] Add support for find included services
>
> Hi Chen,
>
> On Wed, Mar 28, 2012 at 3:30 AM, Ganir, Chen <[email protected]> wrote:
> > This patch set looks like it is working properly with 16 bit uuids. I
> tested it with included services as primaries and as secondaries, and I
> even looked for included services inside other included services
> (secondary services which have includes). Take a look at this :
> > [CON][64:9c:8e:e5:a0:e9][LE]> primary
> > attr handle: 0x0001, end grp handle: 0x000b uuid: 00001800-0000-1000-
> 8000-00805f9b34fb
> > attr handle: 0x000c, end grp handle: 0x0016 uuid: 00001800-0000-1000-
> 8000-00805f9b34fb
> > attr handle: 0x0017, end grp handle: 0x0019 uuid: 00001801-0000-1000-
> 8000-00805f9b34fb
> > attr handle: 0x001a, end grp handle: 0x0043 uuid: 00009000-0000-1000-
> 8000-00805f9b34fb
> > attr handle: 0x0044, end grp handle: 0x0046 uuid: 0000a000-0000-1000-
> 8000-00805f9b34fb
> > attr handle: 0x0050, end grp handle: 0x0055 uuid: 0000a002-0000-1000-
> 8000-00805f9b34fb
> > attr handle: 0x0056, end grp handle: 0x005a uuid: 0000a003-0000-1000-
> 8000-00805f9b34fb
> > attr handle: 0x005b, end grp handle: 0x005d uuid: 0000a004-0000-1000-
> 8000-00805f9b34fb
> > attr handle: 0x0064, end grp handle: 0x0069 uuid: 0000a001-0000-1000-
> 8000-00805f9b34fb
> > attr handle: 0x006a, end grp handle: 0x006c uuid: 0000a008-0000-0000-
> 0123-456789abcdef
> > attr handle: 0x0070, end grp handle: 0x0078 uuid: 0000a007-0000-0000-
> 0123-456789abcdef
> > attr handle: 0x0079, end grp handle: 0x007d uuid: 00001802-0000-1000-
> 8000-00805f9b34fb
> > attr handle: 0x007e, end grp handle: 0x0080 uuid: 00001804-0000-1000-
> 8000-00805f9b34fb
> > attr handle: 0x0081, end grp handle: 0x0083 uuid: 00001803-0000-1000-
> 8000-00805f9b34fb
> > [CON][64:9c:8e:e5:a0:e9][LE]> included 0x01 0x6c
>
> Just a note, you usually give the start-end ranges of a service. In
> your example database:
>
> 0x0001 0x000b
> 0x000c 0x0016
> ...
>
> But I think it should work with any pair of start-end handles.
>
Lizardo, I know that. I just wanted to get all the included services in the entire range.
> > [CON][64:9c:8e:e5:a0:e9][LE]>
> >
> > This is a secondary service, including another secondary service,
> both do not show up in the primaries list above, as they should:
> > handle: 0x004b, start handle: 0x0047, end handle: 0x0049 uuid:
> 0000a006-0000-1000-8000-00805f9b34fb
> >
> > handle: 0x0051, start handle: 0x004a, end handle: 0x004f uuid:
> 0000a005-0000-1000-8000-00805f9b34fb
>
> The one above should have been returned by: "included 0x0050 0x0055"
>
> > handle: 0x005f, start handle: 0x0047, end handle: 0x0049 uuid:
> 0000a006-0000-1000-8000-00805f9b34fb
>
> This one seems to be inside a secondary service. Given that the "Find
> Included Services" GATT procedure is not recursive, we need to do this
> manually, first obtaining the range for the secondary service that
> contains the 0x005f handle:
>
> included 0x0064 0x0069
>
> It should return the line below:
>
> > handle: 0x0065, start handle: 0x005e, end handle: 0x0063 uuid:
> 0000a005-0000-1000-8000-00805f9b34fb
>
> Next, run:
>
> included 0x005e 0x0063
>
> Which should finally return the previous "handle: 0x005f..." line.
>
> Chen: for Generic Attribute API, I suppose you will follow this
> recursive method. Also remember to check for cycles, and follow the
> requirements on the Core spec.
>
> > [CON][64:9c:8e:e5:a0:e9][LE]> included 0x01 0x78
> > However, when I tried to search for included services inside a 128bit
> service, which includes another 128bit secondary service, the gatttool
> first did not return any result, and then I got a segmentation fault.
>
> Can you send the valgrind output for this case? It should help
> Jefferson fixing the segfault.
>
I'll try and do that today or tomorrow and send it.
> Thanks for the tests!
>
> Best Regards,
> --
> Anderson Lizardo
> Instituto Nokia de Tecnologia - INdT
> Manaus - Brazil
Thanks,
Chen Ganir
Hi Chen,
On Wed, Mar 28, 2012 at 3:30 AM, Ganir, Chen <[email protected]> wrote:
> This patch set looks like it is working properly with 16 bit uuids. I tested it with included services as primaries and as secondaries, and I even looked for included services inside other included services (secondary services which have includes). Take a look at this :
> [CON][64:9c:8e:e5:a0:e9][LE]> primary
> attr handle: 0x0001, end grp handle: 0x000b uuid: 00001800-0000-1000-8000-00805f9b34fb
> attr handle: 0x000c, end grp handle: 0x0016 uuid: 00001800-0000-1000-8000-00805f9b34fb
> attr handle: 0x0017, end grp handle: 0x0019 uuid: 00001801-0000-1000-8000-00805f9b34fb
> attr handle: 0x001a, end grp handle: 0x0043 uuid: 00009000-0000-1000-8000-00805f9b34fb
> attr handle: 0x0044, end grp handle: 0x0046 uuid: 0000a000-0000-1000-8000-00805f9b34fb
> attr handle: 0x0050, end grp handle: 0x0055 uuid: 0000a002-0000-1000-8000-00805f9b34fb
> attr handle: 0x0056, end grp handle: 0x005a uuid: 0000a003-0000-1000-8000-00805f9b34fb
> attr handle: 0x005b, end grp handle: 0x005d uuid: 0000a004-0000-1000-8000-00805f9b34fb
> attr handle: 0x0064, end grp handle: 0x0069 uuid: 0000a001-0000-1000-8000-00805f9b34fb
> attr handle: 0x006a, end grp handle: 0x006c uuid: 0000a008-0000-0000-0123-456789abcdef
> attr handle: 0x0070, end grp handle: 0x0078 uuid: 0000a007-0000-0000-0123-456789abcdef
> attr handle: 0x0079, end grp handle: 0x007d uuid: 00001802-0000-1000-8000-00805f9b34fb
> attr handle: 0x007e, end grp handle: 0x0080 uuid: 00001804-0000-1000-8000-00805f9b34fb
> attr handle: 0x0081, end grp handle: 0x0083 uuid: 00001803-0000-1000-8000-00805f9b34fb
> [CON][64:9c:8e:e5:a0:e9][LE]> included 0x01 0x6c
Just a note, you usually give the start-end ranges of a service. In
your example database:
0x0001 0x000b
0x000c 0x0016
...
But I think it should work with any pair of start-end handles.
> [CON][64:9c:8e:e5:a0:e9][LE]>
>
> This is a secondary service, including another secondary service, both do not show up in the primaries list above, as they should:
> handle: 0x004b, start handle: 0x0047, end handle: 0x0049 uuid: 0000a006-0000-1000-8000-00805f9b34fb
>
> handle: 0x0051, start handle: 0x004a, end handle: 0x004f uuid: 0000a005-0000-1000-8000-00805f9b34fb
The one above should have been returned by: "included 0x0050 0x0055"
> handle: 0x005f, start handle: 0x0047, end handle: 0x0049 uuid: 0000a006-0000-1000-8000-00805f9b34fb
This one seems to be inside a secondary service. Given that the "Find
Included Services" GATT procedure is not recursive, we need to do this
manually, first obtaining the range for the secondary service that
contains the 0x005f handle:
included 0x0064 0x0069
It should return the line below:
> handle: 0x0065, start handle: 0x005e, end handle: 0x0063 uuid: 0000a005-0000-1000-8000-00805f9b34fb
Next, run:
included 0x005e 0x0063
Which should finally return the previous "handle: 0x005f..." line.
Chen: for Generic Attribute API, I suppose you will follow this
recursive method. Also remember to check for cycles, and follow the
requirements on the Core spec.
> [CON][64:9c:8e:e5:a0:e9][LE]> included 0x01 0x78
> However, when I tried to search for included services inside a 128bit service, which includes another 128bit secondary service, the gatttool first did not return any result, and then I got a segmentation fault.
Can you send the valgrind output for this case? It should help
Jefferson fixing the segfault.
Thanks for the tests!
Best Regards,
--
Anderson Lizardo
Instituto Nokia de Tecnologia - INdT
Manaus - Brazil
Jefferson,
> -----Original Message-----
> From: [email protected] [mailto:linux-bluetooth-
> [email protected]] On Behalf Of Jefferson Delfes
> Sent: Tuesday, March 27, 2012 4:06 PM
> To: [email protected]
> Cc: Jefferson Delfes
> Subject: [RFC BlueZ 0/2] Add support for find included services
>
> This patch series introduces support for find included services.
> The interactive mode of gatttool receives a new command "included" that
> uses gatt_find_included to find these services in given range.
>
> We don't have any way to test find included services with 128-bit
> UUID128. Yet in PTS, there is only a 16-bit UUID test case. So if
> anyone can test this, would be great.
> We can also extend plugins/gatt-example.c to test 128-bit UUID
> includes.
>
This patch set looks like it is working properly with 16 bit uuids. I tested it with included services as primaries and as secondaries, and I even looked for included services inside other included services (secondary services which have includes). Take a look at this :
[CON][64:9c:8e:e5:a0:e9][LE]> primary
attr handle: 0x0001, end grp handle: 0x000b uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x000c, end grp handle: 0x0016 uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x0017, end grp handle: 0x0019 uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle: 0x001a, end grp handle: 0x0043 uuid: 00009000-0000-1000-8000-00805f9b34fb
attr handle: 0x0044, end grp handle: 0x0046 uuid: 0000a000-0000-1000-8000-00805f9b34fb
attr handle: 0x0050, end grp handle: 0x0055 uuid: 0000a002-0000-1000-8000-00805f9b34fb
attr handle: 0x0056, end grp handle: 0x005a uuid: 0000a003-0000-1000-8000-00805f9b34fb
attr handle: 0x005b, end grp handle: 0x005d uuid: 0000a004-0000-1000-8000-00805f9b34fb
attr handle: 0x0064, end grp handle: 0x0069 uuid: 0000a001-0000-1000-8000-00805f9b34fb
attr handle: 0x006a, end grp handle: 0x006c uuid: 0000a008-0000-0000-0123-456789abcdef
attr handle: 0x0070, end grp handle: 0x0078 uuid: 0000a007-0000-0000-0123-456789abcdef
attr handle: 0x0079, end grp handle: 0x007d uuid: 00001802-0000-1000-8000-00805f9b34fb
attr handle: 0x007e, end grp handle: 0x0080 uuid: 00001804-0000-1000-8000-00805f9b34fb
attr handle: 0x0081, end grp handle: 0x0083 uuid: 00001803-0000-1000-8000-00805f9b34fb
[CON][64:9c:8e:e5:a0:e9][LE]> included 0x01 0x6c
[CON][64:9c:8e:e5:a0:e9][LE]>
This is a secondary service, including another secondary service, both do not show up in the primaries list above, as they should:
handle: 0x004b, start handle: 0x0047, end handle: 0x0049 uuid: 0000a006-0000-1000-8000-00805f9b34fb
handle: 0x0051, start handle: 0x004a, end handle: 0x004f uuid: 0000a005-0000-1000-8000-00805f9b34fb
handle: 0x005f, start handle: 0x0047, end handle: 0x0049 uuid: 0000a006-0000-1000-8000-00805f9b34fb
handle: 0x0065, start handle: 0x005e, end handle: 0x0063 uuid: 0000a005-0000-1000-8000-00805f9b34fb
[CON][64:9c:8e:e5:a0:e9][LE]> included 0x01 0x78
However, when I tried to search for included services inside a 128bit service, which includes another 128bit secondary service, the gatttool first did not return any result, and then I got a segmentation fault.
> Jefferson Delfes (2):
> GATT: Add support for find included services
> gatttool: Add "included" command
>
> attrib/gatt.c | 173
> ++++++++++++++++++++++++++++++++++++++++++++++++++
> attrib/gatt.h | 9 +++
> attrib/interactive.c | 54 ++++++++++++++++
> 3 files changed, 236 insertions(+)
>
> --
> 1.7.9.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
Thanks,
Chen Ganir
New command to find included services in interactive mode.
---
attrib/interactive.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)
diff --git a/attrib/interactive.c b/attrib/interactive.c
index 5941811..67fb7cb 100644
--- a/attrib/interactive.c
+++ b/attrib/interactive.c
@@ -190,6 +190,28 @@ 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));
+ return;
+ }
+
+ 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);
+ }
+
+ rl_forced_update_display();
+}
+
static void char_cb(GSList *characteristics, guint8 status, gpointer user_data)
{
GSList *l;
@@ -400,6 +422,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;
@@ -726,6 +778,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.9.4
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.
---
This patch is a little confuse. Any sugestions are welcome.
attrib/gatt.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
attrib/gatt.h | 9 +++
2 files changed, 182 insertions(+)
diff --git a/attrib/gatt.c b/attrib/gatt.c
index 1c3ff78..cdc7bce 100644
--- a/attrib/gatt.c
+++ b/attrib/gatt.c
@@ -47,6 +47,22 @@ struct discover_primary {
void *user_data;
};
+struct find_included {
+ GAttrib *attrib;
+ uint16_t end;
+ GSList *includes;
+ gatt_cb_t cb;
+ uint16_t n_uuid_missing;
+ gboolean final;
+ unsigned int err;
+ void *user_data;
+};
+
+struct find_include_data {
+ struct find_included *find_incl;
+ struct gatt_included *gatt_incl;
+};
+
struct discover_char {
GAttrib *attrib;
bt_uuid_t *uuid;
@@ -63,6 +79,13 @@ static void discover_primary_free(struct discover_primary *dp)
g_free(dp);
}
+static void find_included_free(struct find_included *fi)
+{
+ 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);
@@ -107,6 +130,16 @@ static guint16 encode_discover_primary(uint16_t start, uint16_t end,
return plen;
}
+static size_t encode_find_included(uint16_t start, uint16_t end,
+ uint8_t *pdu, size_t len)
+{
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
+
+ return enc_read_by_type_req(start, end, &uuid, pdu, len);
+}
+
static void primary_by_uuid_cb(guint8 status, const guint8 *ipdu,
guint16 iplen, gpointer user_data)
@@ -219,6 +252,124 @@ done:
discover_primary_free(dp);
}
+static void find_included_helper(uint8_t status, const uint8_t *pdu,
+ uint16_t len, gpointer user_data)
+{
+ struct find_include_data *fid = user_data;
+ struct find_included *fi = fid->find_incl;
+ int buflen;
+ uint8_t *buf = g_attrib_get_buffer(fi->attrib, &buflen);
+
+ fi->n_uuid_missing--;
+
+ if (status) {
+ fi->err = status;
+ goto done;
+ }
+
+ if (dec_read_resp(pdu, len, buf, &buflen) != len) {
+ fi->err = ATT_ECODE_IO;
+ goto done;
+ }
+
+ if (buflen >= 16) {
+ bt_uuid_t uuid = att_get_uuid128(buf);
+ bt_uuid_to_string(&uuid, fid->gatt_incl->uuid,
+ sizeof(fid->gatt_incl->uuid));
+ }
+
+done:
+ if (fi->final && fi->n_uuid_missing == 0) {
+ fi->cb(fi->includes, fi->err, fi->user_data);
+ find_included_free(fi);
+ }
+
+ g_free(fid);
+}
+
+static void included_cb(uint8_t status, const uint8_t *pdu, uint16_t len,
+ gpointer user_data)
+{
+ struct find_included *fi = user_data;
+ struct att_data_list *list;
+ unsigned int i;
+ uint16_t last_handle = fi->end;
+
+ if (status) {
+ if (status != ATT_ECODE_ATTR_NOT_FOUND)
+ fi->err = status;
+ fi->final = TRUE;
+ goto done;
+ }
+
+ list = dec_read_by_type_resp(pdu, len);
+ if (list == NULL) {
+ fi->err = ATT_ECODE_IO;
+ fi->final = TRUE;
+ goto done;
+ }
+
+ for (i = 0; i < list->num; i++) {
+ const uint8_t *data = list->data[i];
+ struct gatt_included *include;
+
+ /* Skipping invalid data */
+ if (list->len != 6 && list->len != 8)
+ continue;
+
+ include = g_new0(struct gatt_included, 1);
+ include->handle = att_get_u16(&data[0]);
+ include->range.start = att_get_u16(&data[2]);
+ include->range.end = att_get_u16(&data[4]);
+ fi->includes = g_slist_append(fi->includes, include);
+
+ if (list->len == 8) {
+ bt_uuid_t uuid128;
+ bt_uuid_t uuid16 = att_get_uuid16(&data[6]);
+ bt_uuid_to_uuid128(&uuid16, &uuid128);
+ bt_uuid_to_string(&uuid128, include->uuid,
+ sizeof(include->uuid));
+ } else {
+ uint8_t *buf;
+ int buflen;
+ uint16_t plen;
+ struct find_include_data *fid =
+ g_new0(struct find_include_data, 1);
+ fid->find_incl = fi;
+ fid->gatt_incl = include;
+
+ /* 128-bit UUID, we need to "resolve" it */
+ buf = g_attrib_get_buffer(fi->attrib, &buflen);
+ plen = enc_read_req(include->handle, buf, buflen);
+ fi->n_uuid_missing++;
+ g_attrib_send(fi->attrib, 0, buf[0], buf,
+ plen, find_included_helper,
+ fi, NULL);
+ }
+
+ last_handle = include->handle;
+ }
+
+ att_data_list_free(list);
+
+ if (last_handle != fi->end) {
+ int buflen = 0;
+ uint8_t *buf = g_attrib_get_buffer(fi->attrib, &buflen);
+ size_t oplen = encode_find_included(last_handle + 1,
+ fi->end, buf, buflen);
+
+ g_attrib_send(fi->attrib, 0, buf[0], buf, oplen,
+ included_cb, fi, NULL);
+ } else
+ fi->final = TRUE;
+
+done:
+ if (fi->final && fi->n_uuid_missing == 0) {
+ fi->cb(fi->includes, fi->err, fi->user_data);
+ find_included_free(fi);
+ }
+}
+
guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
gpointer user_data)
{
@@ -249,6 +400,28 @@ 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);
}
+unsigned int gatt_find_included(GAttrib *attrib, uint16_t start, uint16_t end,
+ gatt_cb_t func, gpointer user_data)
+{
+ struct find_included *fi;
+ int buflen;
+ uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
+ size_t plen;
+
+ plen = encode_find_included(start, end, buf, buflen);
+ if (plen == 0)
+ return 0;
+
+ fi = g_new0(struct find_included, 1);
+ fi->attrib = g_attrib_ref(attrib);
+ fi->end = end;
+ fi->cb = func;
+ fi->user_data = user_data;
+
+ return g_attrib_send(attrib, 0, buf[0], buf, plen, included_cb,
+ fi, NULL);
+}
+
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 1732270..b420711 100644
--- a/attrib/gatt.h
+++ b/attrib/gatt.h
@@ -30,6 +30,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;
@@ -40,6 +46,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.9.4