This patch set contains implementation of Read by UUID command
in bt-gatt-client and support in btgatt-client tool.
Unit test for test cases added:
TP/GAR/CL/BV-03-C-1, TP/GAR/CL/BV-03-C-2, TP/GAR/CL/BI-06-C,
TP/GAR/CL/BI-07-C, TP/GAR/CL/BI-09-C, TP/GAR/CL/BI-10-C,
TP/GAR/CL/BI-11-C.
It was also tested against PTS.
Comments are welcome
BR
Marcin
Marcin Kraglak (11):
shared/gatt-client: Add read-by-uuid function
tools/btgatt-client: Add read-by-uuid cmd
unit/test-gatt: Fix not calling test step function
unit/test-gatt: Create macro with PDU corresponding to service data
unit/test-gatt: Add /TP/GAR/CL/BV-03-C-1 test
unit/test-gatt: Add /TP/GAR/CL/BV-03-C-2 test
unit/test-gatt: Add /TP/GAR/CL/BI-06-C test
unit/test-gatt: Add /TP/GAR/CL/BI-07-C test
unit/test-gatt: Add /TP/GAR/CL/BI-09-C test
unit/test-gatt: Add /TP/GAR/CL/BI-10-C test
unit/test-gatt: Add /TP/GAR/CL/BI-11-C test
src/shared/gatt-client.c | 271 +++++++++++++++++++++++++++
src/shared/gatt-client.h | 18 ++
tools/btgatt-client.c | 84 +++++++++
unit/test-gatt.c | 479 +++++++++++++++++++++++++++++++++++------------
4 files changed, 730 insertions(+), 122 deletions(-)
--
1.9.3
Hi Marcin,
> On Tue, Nov 18, 2014 at 8:33 AM, Marcin Kraglak <[email protected]> wrote:
> Hi Arman,
>
> On 18 November 2014 08:09, Marcin Kraglak <[email protected]> wrote:
>> Hi Arman,
>>
>> On 18 November 2014 00:36, Arman Uguray <[email protected]> wrote:
>>> Hi Luiz,
>>>
>>>> On Fri, Nov 14, 2014 at 5:51 AM, Luiz Augusto von Dentz <[email protected]> wrote:
>>>> Hi Marcin,
>>>>
>>>> On Fri, Nov 14, 2014 at 3:13 PM, Marcin Kraglak
>>>> <[email protected]> wrote:
>>>>> Hi Luiz,
>>>>>
>>>>> On 14 November 2014 13:44, Luiz Augusto von Dentz <[email protected]> wrote:
>>>>>> Hi Marcin,
>>>>>>
>>>>>> On Thu, Nov 13, 2014 at 1:27 PM, Marcin Kraglak
>>>>>> <[email protected]> wrote:
>>>>>>> This is exposed to allow client reading characteristic values
>>>>>>> with known uuid.
>>>>>>> ---
>>>>>>> src/shared/gatt-client.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>> src/shared/gatt-client.h | 18 ++++
>>>>>>> 2 files changed, 289 insertions(+)
>>>>>>>
>>>>>>> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
>>>>>>> index 30b271e..0493b05 100644
>>>>>>> --- a/src/shared/gatt-client.c
>>>>>>> +++ b/src/shared/gatt-client.c
>>>>>>> @@ -44,6 +44,11 @@
>>>>>>> #define GATT_SVC_UUID 0x1801
>>>>>>> #define SVC_CHNGD_UUID 0x2a05
>>>>>>>
>>>>>>> +static const uint8_t bt_base_uuid[16] = {
>>>>>>> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
>>>>>>> + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
>>>>>>> +};
>>>>>>> +
>>>>>>> struct chrc_data {
>>>>>>> /* The public characteristic entry. */
>>>>>>> bt_gatt_characteristic_t chrc_external;
>>>>>>> @@ -122,6 +127,15 @@ struct bt_gatt_client {
>>>>>>> bool in_svc_chngd;
>>>>>>> };
>>>>>>>
>>>>>>> +static bool is_bluetooth_uuid(const uint8_t uuid[BT_GATT_UUID_SIZE])
>>>>>>> +{
>>>>>>> + if (memcmp(uuid, bt_base_uuid, 2) ||
>>>>>>> + memcmp(&uuid[4], &bt_base_uuid[4], 12))
>>>>>>> + return false;
>>>>>>> +
>>>>>>> + return true;
>>>>>>> +}
>>>>>>> +
>>>>>>
>>>>>> Is this really different from uuid_cmp? Perhaps you use set uuid16
>>>>>> properly it should just do the same you are doing above.
>>>>> Ok, I'll use it.
>>>>>>
>>>>>>> struct notify_data {
>>>>>>> struct bt_gatt_client *client;
>>>>>>> bool removed;
>>>>>>> @@ -2001,6 +2015,263 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
>>>>>>> return true;
>>>>>>> }
>>>>>>>
>>>>>>> +struct read_by_uuid_res {
>>>>>>> + uint16_t handle;
>>>>>>> + uint8_t length;
>>>>>>> + uint8_t *data;
>>>>>>> + struct read_by_uuid_res *next;
>>>>>>> +};
>>>>>>> +
>>>>>>> +bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
>>>>>>> + uint8_t *length, uint8_t **data)
>>>>>>> +{
>>>>>>> + if (!result)
>>>>>>> + return false;
>>>>>>> +
>>>>>>> + *length = result->length;
>>>>>>> + *handle = result->handle;
>>>>>>> + *data = result->data;
>>>>>>> +
>>>>>>> + return true;
>>>>>>> +}
>>>>>>> +
>>>>>>> +struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result)
>>>>>>> +{
>>>>>>> + if (!result)
>>>>>>> + return NULL;
>>>>>>> +
>>>>>>> + return result->next;
>>>>>>> +}
>>>>>>> +
>>>>>>> +struct read_by_uuid_op {
>>>>>>> + struct bt_gatt_client *client;
>>>>>>> + bt_gatt_client_read_by_uuid_callback_t callback;
>>>>>>> + int ref_count;
>>>>>>> + uint16_t start_handle;
>>>>>>> + uint16_t end_handle;
>>>>>>> + uint8_t uuid[BT_GATT_UUID_SIZE];
>>>>>>> + struct read_by_uuid_res *result_head;
>>>>>>> + struct read_by_uuid_res *result_tail;
>>>>>>> + void *user_data;
>>>>>>> + bt_gatt_client_destroy_func_t destroy;
>>>>>>> +};
>>>>>>
>>>>>> Im not really sure this lists are really helping us, perhaps this is
>>>>>> the effect of using iterators in the API but that seems to create
>>>>>> duplicated code.
>>>>>>
>>>>>>> +static bool append_read_by_uuid_result(struct read_by_uuid_op *op,
>>>>>>> + uint16_t handle, uint16_t length,
>>>>>>> + const uint8_t *data)
>>>>>>> +{
>>>>>>> + struct read_by_uuid_res *result;
>>>>>>> +
>>>>>>> + result = new0(struct read_by_uuid_res, 1);
>>>>>>> + if (!result)
>>>>>>> + return false;
>>>>>>> +
>>>>>>> + result->data = malloc(length);
>>>>>>> + if (!result->data) {
>>>>>>> + free(result);
>>>>>>> + return false;
>>>>>>> + }
>>>>>>> +
>>>>>>> + result->handle = handle;
>>>>>>> + result->length = length;
>>>>>>> + memcpy(result->data, data, length);
>>>>>>> +
>>>>>>> + if (op->result_head) {
>>>>>>> + op->result_tail->next = result;
>>>>>>> + op->result_tail = result;
>>>>>>> + } else {
>>>>>>> + op->result_head = op->result_tail = result;
>>>>>>> + }
>>>>>>> +
>>>>>>> + return true;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void destroy_read_by_uuid_result(void *data)
>>>>>>> +{
>>>>>>> + struct read_by_uuid_res *result = data;
>>>>>>> +
>>>>>>> + free(result->data);
>>>>>>> + free(result);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static struct read_by_uuid_op *read_by_uuid_op_ref(struct read_by_uuid_op *op)
>>>>>>> +{
>>>>>>> + __sync_fetch_and_add(&op->ref_count, 1);
>>>>>>> +
>>>>>>> + return op;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void read_by_uuid_op_unref(void *data)
>>>>>>> +{
>>>>>>> + struct read_by_uuid_res *next, *temp;
>>>>>>> + struct read_by_uuid_op *op = data;
>>>>>>> +
>>>>>>> + if (__sync_sub_and_fetch(&op->ref_count, 1))
>>>>>>> + return;
>>>>>>> +
>>>>>>> + if (op->destroy)
>>>>>>> + op->destroy(op->user_data);
>>>>>>> +
>>>>>>> + for (temp = op->result_head; temp; temp = next) {
>>>>>>> + next = temp->next;
>>>>>>> + destroy_read_by_uuid_result(temp);
>>>>>>> + }
>>>>>>> +
>>>>>>> + free(op);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void read_by_uuid_cb(uint8_t opcode, const void *pdu,
>>>>>>> + uint16_t length, void *user_data)
>>>>>>> +{
>>>>>>> + struct read_by_uuid_op *op = user_data;
>>>>>>> + size_t data_length;
>>>>>>> + uint8_t att_ecode;
>>>>>>> + uint16_t offset, last_handle;
>>>>>>> + const uint8_t *data;
>>>>>>> + bool success;
>>>>>>> +
>>>>>>> + if (opcode == BT_ATT_OP_ERROR_RSP) {
>>>>>>> + att_ecode = process_error(pdu, length);
>>>>>>> + if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND)
>>>>>>> + success = true;
>>>>>>> + else
>>>>>>> + success = false;
>>>>>>> +
>>>>>>> + goto done;
>>>>>>> + }
>>>>>>> +
>>>>>>> + if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || (!pdu && length)) {
>>>>>>> + success = false;
>>>>>>> + att_ecode = 0;
>>>>>>> + goto done;
>>>>>>> + }
>>>>>>> +
>>>>>>> + data_length = ((const uint8_t *) pdu)[0];
>>>>>>> + if ((length - 1) % data_length) {
>>>>>>> + success = false;
>>>>>>> + att_ecode = 0;
>>>>>>> + goto done;
>>>>>>> + }
>>>>>>> +
>>>>>>> + for (offset = 1; offset < length; offset += data_length) {
>>>>>>> + data = pdu + offset;
>>>>>>> + if (!append_read_by_uuid_result(op, get_le16(data),
>>>>>>> + data_length - 2, data + 2)) {
>>>>>>> + success = false;
>>>>>>> + att_ecode = 0;
>>>>>>> + goto done;
>>>>>>> + }
>>>>>>> + }
>>>>>>> +
>>>>>>> + last_handle = get_le16(pdu + length - data_length);
>>>>>>> + if (last_handle < op->end_handle) {
>>>>>>> + uint8_t pdu[20];
>>>>>>> +
>>>>>>> + put_le16(last_handle + 1, pdu);
>>>>>>> + put_le16(op->end_handle, pdu + 2);
>>>>>>> +
>>>>>>> + if (is_bluetooth_uuid(op->uuid)) {
>>>>>>> + put_le16((op->uuid[2] << 8) + op->uuid[3], pdu + 4);
>>>>>>> + /*
>>>>>>> + * 2 octets - start handle
>>>>>>> + * 2 octets - end handle
>>>>>>> + * 2 octets - 16-bit uuid
>>>>>>> + */
>>>>>>> + length = 6;
>>>>>>> + } else {
>>>>>>> + bswap_128(&op->uuid, pdu + 4);
>>>>>>> + /*
>>>>>>> + * 2 octets - start handle
>>>>>>> + * 2 octets - end handle
>>>>>>> + * 16 octets - 128-bit uuid
>>>>>>> + */
>>>>>>> + length = 20;
>>>>>>> + }
>>>>>>> +
>>>>>>> + if (!bt_att_send(op->client->att, BT_ATT_OP_READ_BY_TYPE_REQ,
>>>>>>> + pdu, length, read_by_uuid_cb,
>>>>>>> + read_by_uuid_op_ref(op),
>>>>>>> + read_by_uuid_op_unref)) {
>>>>>>> + read_by_uuid_op_unref(op);
>>>>>>> + success = false;
>>>>>>> + att_ecode = 0;
>>>>>>> + goto done;
>>>>>>> + }
>>>>>>> +
>>>>>>> + return;
>>>>>>> + }
>>>>>>> +
>>>>>>> + success = true;
>>>>>>> + att_ecode = 0;
>>>>>>> +
>>>>>>> +done:
>>>>>>> + if (op->callback)
>>>>>>> + op->callback(success, att_ecode, op->result_head,
>>>>>>> + op->user_data);
>>>>>>> +}
>>>>>>> +
>>>>>>> +bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
>>>>>>> + uint16_t start_handle, uint16_t end_handle,
>>>>>>> + const uint8_t uuid[BT_GATT_UUID_SIZE],
>>>>>>> + bt_gatt_client_read_by_uuid_callback_t callback,
>>>>>>> + void *user_data,
>>>>>>> + bt_gatt_client_destroy_func_t destroy)
>>>>>>> +{
>>>>>>> + struct read_by_uuid_op *op;
>>>>>>> + uint16_t length;
>>>>>>> + uint8_t pdu[20];
>>>>>>> +
>>>>>>> + if (!client)
>>>>>>> + return false;
>>>>>>> +
>>>>>>> + if (start_handle > end_handle || start_handle == 0x0000)
>>>>>>> + return false;
>>>>>>> +
>>>>>>> + op = new0(struct read_by_uuid_op, 1);
>>>>>>> + if (!op)
>>>>>>> + return false;
>>>>>>> +
>>>>>>> + op->client = client;
>>>>>>> + op->start_handle = start_handle;
>>>>>>> + op->end_handle = end_handle;
>>>>>>> + op->destroy = destroy;
>>>>>>> + op->callback = callback;
>>>>>>> + op->user_data = user_data;
>>>>>>> + memcpy(&op->uuid, uuid, 16);
>>>>>>> +
>>>>>>> + /* Convert to 16-bit if it is Bluetooth UUID */
>>>>>>> + if (is_bluetooth_uuid(uuid)) {
>>>>>>> + put_le16((uuid[2] << 8) + uuid[3], pdu + 4);
>>>>>>> + /*
>>>>>>> + * 2 octets - start handle
>>>>>>> + * 2 octets - end handle
>>>>>>> + * 2 octets - 16-bit uuid
>>>>>>> + */
>>>>>>> + length = 6;
>>>>>>> + } else {
>>>>>>> + bswap_128(uuid, pdu + 4);
>>>>>>> + /*
>>>>>>> + * 2 octets - start handle
>>>>>>> + * 2 octets - end handle
>>>>>>> + * 16 octets - 128-bit uuid
>>>>>>> + */
>>>>>>> + length = 20;
>>>>>>> + }
>>>>>>> +
>>>>>>> + put_le16(start_handle, pdu);
>>>>>>> + put_le16(end_handle, pdu + 2);
>>>>>>> +
>>>>>>> + if (!bt_att_send(client->att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu,
>>>>>>> + length, read_by_uuid_cb,
>>>>>>> + read_by_uuid_op_ref(op),
>>>>>>> + read_by_uuid_op_unref)) {
>>>>>>> + free(op);
>>>>>>> + return false;
>>>>>>> + }
>>>>>>> +
>>>>>>> + return true;
>>>>>>> +}
>>>>>>> +
>>>>>>> bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
>>>>>>> uint16_t value_handle,
>>>>>>> bool signed_write,
>>>>>>> diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
>>>>>>> index adccfc5..921ed75 100644
>>>>>>> --- a/src/shared/gatt-client.h
>>>>>>> +++ b/src/shared/gatt-client.h
>>>>>>> @@ -145,6 +145,24 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
>>>>>>> void *user_data,
>>>>>>> bt_gatt_client_destroy_func_t destroy);
>>>>>>>
>>>>>>> +struct read_by_uuid_res;
>>>>>>> +
>>>>>>> +struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result);
>>>>>>> +bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
>>>>>>> + uint8_t *length, uint8_t **data);
>>>>>>> +
>>>>>>> +typedef void (*bt_gatt_client_read_by_uuid_callback_t)(bool success,
>>>>>>> + uint8_t att_ecode,
>>>>>>> + struct read_by_uuid_res *result,
>>>>>>> + void *user_data);
>>>>>>> +
>>>>>>> +bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
>>>>>>> + uint16_t start_handle, uint16_t end_handle,
>>>>>>> + const uint8_t uuid[BT_GATT_UUID_SIZE],
>>>>>>> + bt_gatt_client_read_by_uuid_callback_t callback,
>>>>>>> + void *user_data,
>>>>>>> + bt_gatt_client_destroy_func_t destroy);
>>>>>>
>>>>>> Isn't this essentially duplicating bt_gatt_service_iter_next_by_uuid?
>>>>>> I mean we should be reading everything already but this doesn't seems
>>>>>> to using the cache and in case it is just to fulfill a test perhaps it
>>>>>> is better to do it in gatt-helpers so we keep bt_gatt_client API
>>>>>> clean.
>>>>>
>>>>> It is functionality of GATT client described in chapter 4.8.2 "Read
>>>>> Using Characteristic UUID".
>>>>> Difference is that client can read attributes by passing UUID, and
>>>>> results can contain
>>>>> different valu lengths. And client interprets results instead of gatt-helpers.
>>>>> I implemented it here because all read/write operations on
>>>>> characteristics and descriptors
>>>>> are exposed in bt_gatt_client, and all discovery stuff is implemented
>>>>> in gatt_helpers. But if
>>>>> you don't agree I can change it.
>>>>
>>>> I see, it does actually end up reading, perhaps it is a good idea to
>>>> keep it here then. In the other hand this API seems to be going in the
>>>> same direction that gatt-db had with access by handles, perhaps we
>>>> should do an API facelift to access by attribute such as we did with
>>>> gatt-db, also the iterator could be replaced with queues which I guess
>>>> is not that different in the end.
>>>>
>>>
>>> The reason we have the iterators and not queues is because Marcel
>>> wanted to keep shared/queue as an internal utility of shared code
>>> (although shared/gatt-db exposes it to upper layers). In this case
>>> performing the read/write operations by handle isn't bad since the
>>> code simply constructs the request and sends it out and doesn't need
>>> to do an internal lookup for it.
>>>
>>> I commented on Marcin's new patch set that this code should go to
>>> gatt-helpers. I don't think it makes sense to put it in gatt-client,
>>> especially since the characteristic and include declaration discovery
>>
>> Include service discovery can't be based on that API, because it
>> performs read_by_type
>> and read requests during discovery. It can be shared by characteristic
>> discovery only.
>> Additionally in characteristic discovery gatt-client interprets
>> results and cancel if length of
>> response is not valid. Also, you will be able to use include service
>> iterator and characteristic iterators
>> to read results of read-by-uuid (because it checks type of response
>> and length). I don't see benefit
>> having this in one place.
>>
I guess you're right in that with what I'm proposing we can't do any
of the early cancellations in case of malformed PDUs and I guess you
have to perform the read requests in between read by type requests for
included discovery? Anyway, I'm fine with a standalone API for now
then, though I'd like to see us reduce duplicated code where we can.
>
> One more thing: I moved it to gatt-helpers, and once I've added public
> API to read-by-type,
> I had to write iterator to handle results, and next iterator in
> gatt-client, to expose results there.
> And it would make sense if it will be used somewhere else (I mean
> read-by-uuid in gatt-helpers),
> but it will be used only in read-by-uuid() in gatt-client. Any thoughts?
>
Actually what is the use case here? I think users of
shared/gatt-client won't really need this API since gatt-client
already will have populated its cache of handles and UUIDs, so the
users can just iterate through that and send out a single read request
to read only one handle. So I'm assuming this is mainly for the unit
tests?
Anyway, use your best judgement. I think it would be enough here to
just have a function in gatt-helpers and none in gatt-client. We would
then have a simple PDU iterator in gatt-helpers for getting the
handles and values out. If we figure later that this belongs in
gatt-client then we can move it over or wrap around it there.
>>> procedures are already based on Read By Type and we can create a
>>> cleaner API by having this in gatt-helpers and basing the char/incl
>>> discovery functions on that instead.
>>>
>>>>>>
>>>>>>> bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
>>>>>>> uint16_t value_handle,
>>>>>>> bool signed_write,
>>>>>>> --
>>>>>>> 1.9.3
>>>>>>>
>>>>>>> --
>>>>>>> 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
>>>>>
>>>>> BR
>>>>> Marcin
>>>>
>>>>
>>>>
>>>> --
>>>> Luiz Augusto von Dentz
>>>> --
>>>> 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
>>>
>>> Cheers,
>>> Arman
>>
>> BR
>> Marcin
>
> BR
> Marcin
Cheers,
Arman
Hi Arman,
On 18 November 2014 08:09, Marcin Kraglak <[email protected]> wrote:
> Hi Arman,
>
> On 18 November 2014 00:36, Arman Uguray <[email protected]> wrote:
>> Hi Luiz,
>>
>>> On Fri, Nov 14, 2014 at 5:51 AM, Luiz Augusto von Dentz <[email protected]> wrote:
>>> Hi Marcin,
>>>
>>> On Fri, Nov 14, 2014 at 3:13 PM, Marcin Kraglak
>>> <[email protected]> wrote:
>>>> Hi Luiz,
>>>>
>>>> On 14 November 2014 13:44, Luiz Augusto von Dentz <[email protected]> wrote:
>>>>> Hi Marcin,
>>>>>
>>>>> On Thu, Nov 13, 2014 at 1:27 PM, Marcin Kraglak
>>>>> <[email protected]> wrote:
>>>>>> This is exposed to allow client reading characteristic values
>>>>>> with known uuid.
>>>>>> ---
>>>>>> src/shared/gatt-client.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++
>>>>>> src/shared/gatt-client.h | 18 ++++
>>>>>> 2 files changed, 289 insertions(+)
>>>>>>
>>>>>> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
>>>>>> index 30b271e..0493b05 100644
>>>>>> --- a/src/shared/gatt-client.c
>>>>>> +++ b/src/shared/gatt-client.c
>>>>>> @@ -44,6 +44,11 @@
>>>>>> #define GATT_SVC_UUID 0x1801
>>>>>> #define SVC_CHNGD_UUID 0x2a05
>>>>>>
>>>>>> +static const uint8_t bt_base_uuid[16] = {
>>>>>> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
>>>>>> + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
>>>>>> +};
>>>>>> +
>>>>>> struct chrc_data {
>>>>>> /* The public characteristic entry. */
>>>>>> bt_gatt_characteristic_t chrc_external;
>>>>>> @@ -122,6 +127,15 @@ struct bt_gatt_client {
>>>>>> bool in_svc_chngd;
>>>>>> };
>>>>>>
>>>>>> +static bool is_bluetooth_uuid(const uint8_t uuid[BT_GATT_UUID_SIZE])
>>>>>> +{
>>>>>> + if (memcmp(uuid, bt_base_uuid, 2) ||
>>>>>> + memcmp(&uuid[4], &bt_base_uuid[4], 12))
>>>>>> + return false;
>>>>>> +
>>>>>> + return true;
>>>>>> +}
>>>>>> +
>>>>>
>>>>> Is this really different from uuid_cmp? Perhaps you use set uuid16
>>>>> properly it should just do the same you are doing above.
>>>> Ok, I'll use it.
>>>>>
>>>>>> struct notify_data {
>>>>>> struct bt_gatt_client *client;
>>>>>> bool removed;
>>>>>> @@ -2001,6 +2015,263 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
>>>>>> return true;
>>>>>> }
>>>>>>
>>>>>> +struct read_by_uuid_res {
>>>>>> + uint16_t handle;
>>>>>> + uint8_t length;
>>>>>> + uint8_t *data;
>>>>>> + struct read_by_uuid_res *next;
>>>>>> +};
>>>>>> +
>>>>>> +bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
>>>>>> + uint8_t *length, uint8_t **data)
>>>>>> +{
>>>>>> + if (!result)
>>>>>> + return false;
>>>>>> +
>>>>>> + *length = result->length;
>>>>>> + *handle = result->handle;
>>>>>> + *data = result->data;
>>>>>> +
>>>>>> + return true;
>>>>>> +}
>>>>>> +
>>>>>> +struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result)
>>>>>> +{
>>>>>> + if (!result)
>>>>>> + return NULL;
>>>>>> +
>>>>>> + return result->next;
>>>>>> +}
>>>>>> +
>>>>>> +struct read_by_uuid_op {
>>>>>> + struct bt_gatt_client *client;
>>>>>> + bt_gatt_client_read_by_uuid_callback_t callback;
>>>>>> + int ref_count;
>>>>>> + uint16_t start_handle;
>>>>>> + uint16_t end_handle;
>>>>>> + uint8_t uuid[BT_GATT_UUID_SIZE];
>>>>>> + struct read_by_uuid_res *result_head;
>>>>>> + struct read_by_uuid_res *result_tail;
>>>>>> + void *user_data;
>>>>>> + bt_gatt_client_destroy_func_t destroy;
>>>>>> +};
>>>>>
>>>>> Im not really sure this lists are really helping us, perhaps this is
>>>>> the effect of using iterators in the API but that seems to create
>>>>> duplicated code.
>>>>>
>>>>>> +static bool append_read_by_uuid_result(struct read_by_uuid_op *op,
>>>>>> + uint16_t handle, uint16_t length,
>>>>>> + const uint8_t *data)
>>>>>> +{
>>>>>> + struct read_by_uuid_res *result;
>>>>>> +
>>>>>> + result = new0(struct read_by_uuid_res, 1);
>>>>>> + if (!result)
>>>>>> + return false;
>>>>>> +
>>>>>> + result->data = malloc(length);
>>>>>> + if (!result->data) {
>>>>>> + free(result);
>>>>>> + return false;
>>>>>> + }
>>>>>> +
>>>>>> + result->handle = handle;
>>>>>> + result->length = length;
>>>>>> + memcpy(result->data, data, length);
>>>>>> +
>>>>>> + if (op->result_head) {
>>>>>> + op->result_tail->next = result;
>>>>>> + op->result_tail = result;
>>>>>> + } else {
>>>>>> + op->result_head = op->result_tail = result;
>>>>>> + }
>>>>>> +
>>>>>> + return true;
>>>>>> +}
>>>>>> +
>>>>>> +static void destroy_read_by_uuid_result(void *data)
>>>>>> +{
>>>>>> + struct read_by_uuid_res *result = data;
>>>>>> +
>>>>>> + free(result->data);
>>>>>> + free(result);
>>>>>> +}
>>>>>> +
>>>>>> +static struct read_by_uuid_op *read_by_uuid_op_ref(struct read_by_uuid_op *op)
>>>>>> +{
>>>>>> + __sync_fetch_and_add(&op->ref_count, 1);
>>>>>> +
>>>>>> + return op;
>>>>>> +}
>>>>>> +
>>>>>> +static void read_by_uuid_op_unref(void *data)
>>>>>> +{
>>>>>> + struct read_by_uuid_res *next, *temp;
>>>>>> + struct read_by_uuid_op *op = data;
>>>>>> +
>>>>>> + if (__sync_sub_and_fetch(&op->ref_count, 1))
>>>>>> + return;
>>>>>> +
>>>>>> + if (op->destroy)
>>>>>> + op->destroy(op->user_data);
>>>>>> +
>>>>>> + for (temp = op->result_head; temp; temp = next) {
>>>>>> + next = temp->next;
>>>>>> + destroy_read_by_uuid_result(temp);
>>>>>> + }
>>>>>> +
>>>>>> + free(op);
>>>>>> +}
>>>>>> +
>>>>>> +static void read_by_uuid_cb(uint8_t opcode, const void *pdu,
>>>>>> + uint16_t length, void *user_data)
>>>>>> +{
>>>>>> + struct read_by_uuid_op *op = user_data;
>>>>>> + size_t data_length;
>>>>>> + uint8_t att_ecode;
>>>>>> + uint16_t offset, last_handle;
>>>>>> + const uint8_t *data;
>>>>>> + bool success;
>>>>>> +
>>>>>> + if (opcode == BT_ATT_OP_ERROR_RSP) {
>>>>>> + att_ecode = process_error(pdu, length);
>>>>>> + if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND)
>>>>>> + success = true;
>>>>>> + else
>>>>>> + success = false;
>>>>>> +
>>>>>> + goto done;
>>>>>> + }
>>>>>> +
>>>>>> + if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || (!pdu && length)) {
>>>>>> + success = false;
>>>>>> + att_ecode = 0;
>>>>>> + goto done;
>>>>>> + }
>>>>>> +
>>>>>> + data_length = ((const uint8_t *) pdu)[0];
>>>>>> + if ((length - 1) % data_length) {
>>>>>> + success = false;
>>>>>> + att_ecode = 0;
>>>>>> + goto done;
>>>>>> + }
>>>>>> +
>>>>>> + for (offset = 1; offset < length; offset += data_length) {
>>>>>> + data = pdu + offset;
>>>>>> + if (!append_read_by_uuid_result(op, get_le16(data),
>>>>>> + data_length - 2, data + 2)) {
>>>>>> + success = false;
>>>>>> + att_ecode = 0;
>>>>>> + goto done;
>>>>>> + }
>>>>>> + }
>>>>>> +
>>>>>> + last_handle = get_le16(pdu + length - data_length);
>>>>>> + if (last_handle < op->end_handle) {
>>>>>> + uint8_t pdu[20];
>>>>>> +
>>>>>> + put_le16(last_handle + 1, pdu);
>>>>>> + put_le16(op->end_handle, pdu + 2);
>>>>>> +
>>>>>> + if (is_bluetooth_uuid(op->uuid)) {
>>>>>> + put_le16((op->uuid[2] << 8) + op->uuid[3], pdu + 4);
>>>>>> + /*
>>>>>> + * 2 octets - start handle
>>>>>> + * 2 octets - end handle
>>>>>> + * 2 octets - 16-bit uuid
>>>>>> + */
>>>>>> + length = 6;
>>>>>> + } else {
>>>>>> + bswap_128(&op->uuid, pdu + 4);
>>>>>> + /*
>>>>>> + * 2 octets - start handle
>>>>>> + * 2 octets - end handle
>>>>>> + * 16 octets - 128-bit uuid
>>>>>> + */
>>>>>> + length = 20;
>>>>>> + }
>>>>>> +
>>>>>> + if (!bt_att_send(op->client->att, BT_ATT_OP_READ_BY_TYPE_REQ,
>>>>>> + pdu, length, read_by_uuid_cb,
>>>>>> + read_by_uuid_op_ref(op),
>>>>>> + read_by_uuid_op_unref)) {
>>>>>> + read_by_uuid_op_unref(op);
>>>>>> + success = false;
>>>>>> + att_ecode = 0;
>>>>>> + goto done;
>>>>>> + }
>>>>>> +
>>>>>> + return;
>>>>>> + }
>>>>>> +
>>>>>> + success = true;
>>>>>> + att_ecode = 0;
>>>>>> +
>>>>>> +done:
>>>>>> + if (op->callback)
>>>>>> + op->callback(success, att_ecode, op->result_head,
>>>>>> + op->user_data);
>>>>>> +}
>>>>>> +
>>>>>> +bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
>>>>>> + uint16_t start_handle, uint16_t end_handle,
>>>>>> + const uint8_t uuid[BT_GATT_UUID_SIZE],
>>>>>> + bt_gatt_client_read_by_uuid_callback_t callback,
>>>>>> + void *user_data,
>>>>>> + bt_gatt_client_destroy_func_t destroy)
>>>>>> +{
>>>>>> + struct read_by_uuid_op *op;
>>>>>> + uint16_t length;
>>>>>> + uint8_t pdu[20];
>>>>>> +
>>>>>> + if (!client)
>>>>>> + return false;
>>>>>> +
>>>>>> + if (start_handle > end_handle || start_handle == 0x0000)
>>>>>> + return false;
>>>>>> +
>>>>>> + op = new0(struct read_by_uuid_op, 1);
>>>>>> + if (!op)
>>>>>> + return false;
>>>>>> +
>>>>>> + op->client = client;
>>>>>> + op->start_handle = start_handle;
>>>>>> + op->end_handle = end_handle;
>>>>>> + op->destroy = destroy;
>>>>>> + op->callback = callback;
>>>>>> + op->user_data = user_data;
>>>>>> + memcpy(&op->uuid, uuid, 16);
>>>>>> +
>>>>>> + /* Convert to 16-bit if it is Bluetooth UUID */
>>>>>> + if (is_bluetooth_uuid(uuid)) {
>>>>>> + put_le16((uuid[2] << 8) + uuid[3], pdu + 4);
>>>>>> + /*
>>>>>> + * 2 octets - start handle
>>>>>> + * 2 octets - end handle
>>>>>> + * 2 octets - 16-bit uuid
>>>>>> + */
>>>>>> + length = 6;
>>>>>> + } else {
>>>>>> + bswap_128(uuid, pdu + 4);
>>>>>> + /*
>>>>>> + * 2 octets - start handle
>>>>>> + * 2 octets - end handle
>>>>>> + * 16 octets - 128-bit uuid
>>>>>> + */
>>>>>> + length = 20;
>>>>>> + }
>>>>>> +
>>>>>> + put_le16(start_handle, pdu);
>>>>>> + put_le16(end_handle, pdu + 2);
>>>>>> +
>>>>>> + if (!bt_att_send(client->att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu,
>>>>>> + length, read_by_uuid_cb,
>>>>>> + read_by_uuid_op_ref(op),
>>>>>> + read_by_uuid_op_unref)) {
>>>>>> + free(op);
>>>>>> + return false;
>>>>>> + }
>>>>>> +
>>>>>> + return true;
>>>>>> +}
>>>>>> +
>>>>>> bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
>>>>>> uint16_t value_handle,
>>>>>> bool signed_write,
>>>>>> diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
>>>>>> index adccfc5..921ed75 100644
>>>>>> --- a/src/shared/gatt-client.h
>>>>>> +++ b/src/shared/gatt-client.h
>>>>>> @@ -145,6 +145,24 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
>>>>>> void *user_data,
>>>>>> bt_gatt_client_destroy_func_t destroy);
>>>>>>
>>>>>> +struct read_by_uuid_res;
>>>>>> +
>>>>>> +struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result);
>>>>>> +bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
>>>>>> + uint8_t *length, uint8_t **data);
>>>>>> +
>>>>>> +typedef void (*bt_gatt_client_read_by_uuid_callback_t)(bool success,
>>>>>> + uint8_t att_ecode,
>>>>>> + struct read_by_uuid_res *result,
>>>>>> + void *user_data);
>>>>>> +
>>>>>> +bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
>>>>>> + uint16_t start_handle, uint16_t end_handle,
>>>>>> + const uint8_t uuid[BT_GATT_UUID_SIZE],
>>>>>> + bt_gatt_client_read_by_uuid_callback_t callback,
>>>>>> + void *user_data,
>>>>>> + bt_gatt_client_destroy_func_t destroy);
>>>>>
>>>>> Isn't this essentially duplicating bt_gatt_service_iter_next_by_uuid?
>>>>> I mean we should be reading everything already but this doesn't seems
>>>>> to using the cache and in case it is just to fulfill a test perhaps it
>>>>> is better to do it in gatt-helpers so we keep bt_gatt_client API
>>>>> clean.
>>>>
>>>> It is functionality of GATT client described in chapter 4.8.2 "Read
>>>> Using Characteristic UUID".
>>>> Difference is that client can read attributes by passing UUID, and
>>>> results can contain
>>>> different valu lengths. And client interprets results instead of gatt-helpers.
>>>> I implemented it here because all read/write operations on
>>>> characteristics and descriptors
>>>> are exposed in bt_gatt_client, and all discovery stuff is implemented
>>>> in gatt_helpers. But if
>>>> you don't agree I can change it.
>>>
>>> I see, it does actually end up reading, perhaps it is a good idea to
>>> keep it here then. In the other hand this API seems to be going in the
>>> same direction that gatt-db had with access by handles, perhaps we
>>> should do an API facelift to access by attribute such as we did with
>>> gatt-db, also the iterator could be replaced with queues which I guess
>>> is not that different in the end.
>>>
>>
>> The reason we have the iterators and not queues is because Marcel
>> wanted to keep shared/queue as an internal utility of shared code
>> (although shared/gatt-db exposes it to upper layers). In this case
>> performing the read/write operations by handle isn't bad since the
>> code simply constructs the request and sends it out and doesn't need
>> to do an internal lookup for it.
>>
>> I commented on Marcin's new patch set that this code should go to
>> gatt-helpers. I don't think it makes sense to put it in gatt-client,
>> especially since the characteristic and include declaration discovery
>
> Include service discovery can't be based on that API, because it
> performs read_by_type
> and read requests during discovery. It can be shared by characteristic
> discovery only.
> Additionally in characteristic discovery gatt-client interprets
> results and cancel if length of
> response is not valid. Also, you will be able to use include service
> iterator and characteristic iterators
> to read results of read-by-uuid (because it checks type of response
> and length). I don't see benefit
> having this in one place.
>
One more thing: I moved it to gatt-helpers, and once I've added public
API to read-by-type,
I had to write iterator to handle results, and next iterator in
gatt-client, to expose results there.
And it would make sense if it will be used somewhere else (I mean
read-by-uuid in gatt-helpers),
but it will be used only in read-by-uuid() in gatt-client. Any thoughts?
>> procedures are already based on Read By Type and we can create a
>> cleaner API by having this in gatt-helpers and basing the char/incl
>> discovery functions on that instead.
>>
>>>>>
>>>>>> bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
>>>>>> uint16_t value_handle,
>>>>>> bool signed_write,
>>>>>> --
>>>>>> 1.9.3
>>>>>>
>>>>>> --
>>>>>> 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
>>>>
>>>> BR
>>>> Marcin
>>>
>>>
>>>
>>> --
>>> Luiz Augusto von Dentz
>>> --
>>> 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
>>
>> Cheers,
>> Arman
>
> BR
> Marcin
BR
Marcin
Hi Arman,
On 18 November 2014 00:36, Arman Uguray <[email protected]> wrote:
> Hi Luiz,
>
>> On Fri, Nov 14, 2014 at 5:51 AM, Luiz Augusto von Dentz <[email protected]> wrote:
>> Hi Marcin,
>>
>> On Fri, Nov 14, 2014 at 3:13 PM, Marcin Kraglak
>> <[email protected]> wrote:
>>> Hi Luiz,
>>>
>>> On 14 November 2014 13:44, Luiz Augusto von Dentz <[email protected]> wrote:
>>>> Hi Marcin,
>>>>
>>>> On Thu, Nov 13, 2014 at 1:27 PM, Marcin Kraglak
>>>> <[email protected]> wrote:
>>>>> This is exposed to allow client reading characteristic values
>>>>> with known uuid.
>>>>> ---
>>>>> src/shared/gatt-client.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++
>>>>> src/shared/gatt-client.h | 18 ++++
>>>>> 2 files changed, 289 insertions(+)
>>>>>
>>>>> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
>>>>> index 30b271e..0493b05 100644
>>>>> --- a/src/shared/gatt-client.c
>>>>> +++ b/src/shared/gatt-client.c
>>>>> @@ -44,6 +44,11 @@
>>>>> #define GATT_SVC_UUID 0x1801
>>>>> #define SVC_CHNGD_UUID 0x2a05
>>>>>
>>>>> +static const uint8_t bt_base_uuid[16] = {
>>>>> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
>>>>> + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
>>>>> +};
>>>>> +
>>>>> struct chrc_data {
>>>>> /* The public characteristic entry. */
>>>>> bt_gatt_characteristic_t chrc_external;
>>>>> @@ -122,6 +127,15 @@ struct bt_gatt_client {
>>>>> bool in_svc_chngd;
>>>>> };
>>>>>
>>>>> +static bool is_bluetooth_uuid(const uint8_t uuid[BT_GATT_UUID_SIZE])
>>>>> +{
>>>>> + if (memcmp(uuid, bt_base_uuid, 2) ||
>>>>> + memcmp(&uuid[4], &bt_base_uuid[4], 12))
>>>>> + return false;
>>>>> +
>>>>> + return true;
>>>>> +}
>>>>> +
>>>>
>>>> Is this really different from uuid_cmp? Perhaps you use set uuid16
>>>> properly it should just do the same you are doing above.
>>> Ok, I'll use it.
>>>>
>>>>> struct notify_data {
>>>>> struct bt_gatt_client *client;
>>>>> bool removed;
>>>>> @@ -2001,6 +2015,263 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
>>>>> return true;
>>>>> }
>>>>>
>>>>> +struct read_by_uuid_res {
>>>>> + uint16_t handle;
>>>>> + uint8_t length;
>>>>> + uint8_t *data;
>>>>> + struct read_by_uuid_res *next;
>>>>> +};
>>>>> +
>>>>> +bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
>>>>> + uint8_t *length, uint8_t **data)
>>>>> +{
>>>>> + if (!result)
>>>>> + return false;
>>>>> +
>>>>> + *length = result->length;
>>>>> + *handle = result->handle;
>>>>> + *data = result->data;
>>>>> +
>>>>> + return true;
>>>>> +}
>>>>> +
>>>>> +struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result)
>>>>> +{
>>>>> + if (!result)
>>>>> + return NULL;
>>>>> +
>>>>> + return result->next;
>>>>> +}
>>>>> +
>>>>> +struct read_by_uuid_op {
>>>>> + struct bt_gatt_client *client;
>>>>> + bt_gatt_client_read_by_uuid_callback_t callback;
>>>>> + int ref_count;
>>>>> + uint16_t start_handle;
>>>>> + uint16_t end_handle;
>>>>> + uint8_t uuid[BT_GATT_UUID_SIZE];
>>>>> + struct read_by_uuid_res *result_head;
>>>>> + struct read_by_uuid_res *result_tail;
>>>>> + void *user_data;
>>>>> + bt_gatt_client_destroy_func_t destroy;
>>>>> +};
>>>>
>>>> Im not really sure this lists are really helping us, perhaps this is
>>>> the effect of using iterators in the API but that seems to create
>>>> duplicated code.
>>>>
>>>>> +static bool append_read_by_uuid_result(struct read_by_uuid_op *op,
>>>>> + uint16_t handle, uint16_t length,
>>>>> + const uint8_t *data)
>>>>> +{
>>>>> + struct read_by_uuid_res *result;
>>>>> +
>>>>> + result = new0(struct read_by_uuid_res, 1);
>>>>> + if (!result)
>>>>> + return false;
>>>>> +
>>>>> + result->data = malloc(length);
>>>>> + if (!result->data) {
>>>>> + free(result);
>>>>> + return false;
>>>>> + }
>>>>> +
>>>>> + result->handle = handle;
>>>>> + result->length = length;
>>>>> + memcpy(result->data, data, length);
>>>>> +
>>>>> + if (op->result_head) {
>>>>> + op->result_tail->next = result;
>>>>> + op->result_tail = result;
>>>>> + } else {
>>>>> + op->result_head = op->result_tail = result;
>>>>> + }
>>>>> +
>>>>> + return true;
>>>>> +}
>>>>> +
>>>>> +static void destroy_read_by_uuid_result(void *data)
>>>>> +{
>>>>> + struct read_by_uuid_res *result = data;
>>>>> +
>>>>> + free(result->data);
>>>>> + free(result);
>>>>> +}
>>>>> +
>>>>> +static struct read_by_uuid_op *read_by_uuid_op_ref(struct read_by_uuid_op *op)
>>>>> +{
>>>>> + __sync_fetch_and_add(&op->ref_count, 1);
>>>>> +
>>>>> + return op;
>>>>> +}
>>>>> +
>>>>> +static void read_by_uuid_op_unref(void *data)
>>>>> +{
>>>>> + struct read_by_uuid_res *next, *temp;
>>>>> + struct read_by_uuid_op *op = data;
>>>>> +
>>>>> + if (__sync_sub_and_fetch(&op->ref_count, 1))
>>>>> + return;
>>>>> +
>>>>> + if (op->destroy)
>>>>> + op->destroy(op->user_data);
>>>>> +
>>>>> + for (temp = op->result_head; temp; temp = next) {
>>>>> + next = temp->next;
>>>>> + destroy_read_by_uuid_result(temp);
>>>>> + }
>>>>> +
>>>>> + free(op);
>>>>> +}
>>>>> +
>>>>> +static void read_by_uuid_cb(uint8_t opcode, const void *pdu,
>>>>> + uint16_t length, void *user_data)
>>>>> +{
>>>>> + struct read_by_uuid_op *op = user_data;
>>>>> + size_t data_length;
>>>>> + uint8_t att_ecode;
>>>>> + uint16_t offset, last_handle;
>>>>> + const uint8_t *data;
>>>>> + bool success;
>>>>> +
>>>>> + if (opcode == BT_ATT_OP_ERROR_RSP) {
>>>>> + att_ecode = process_error(pdu, length);
>>>>> + if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND)
>>>>> + success = true;
>>>>> + else
>>>>> + success = false;
>>>>> +
>>>>> + goto done;
>>>>> + }
>>>>> +
>>>>> + if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || (!pdu && length)) {
>>>>> + success = false;
>>>>> + att_ecode = 0;
>>>>> + goto done;
>>>>> + }
>>>>> +
>>>>> + data_length = ((const uint8_t *) pdu)[0];
>>>>> + if ((length - 1) % data_length) {
>>>>> + success = false;
>>>>> + att_ecode = 0;
>>>>> + goto done;
>>>>> + }
>>>>> +
>>>>> + for (offset = 1; offset < length; offset += data_length) {
>>>>> + data = pdu + offset;
>>>>> + if (!append_read_by_uuid_result(op, get_le16(data),
>>>>> + data_length - 2, data + 2)) {
>>>>> + success = false;
>>>>> + att_ecode = 0;
>>>>> + goto done;
>>>>> + }
>>>>> + }
>>>>> +
>>>>> + last_handle = get_le16(pdu + length - data_length);
>>>>> + if (last_handle < op->end_handle) {
>>>>> + uint8_t pdu[20];
>>>>> +
>>>>> + put_le16(last_handle + 1, pdu);
>>>>> + put_le16(op->end_handle, pdu + 2);
>>>>> +
>>>>> + if (is_bluetooth_uuid(op->uuid)) {
>>>>> + put_le16((op->uuid[2] << 8) + op->uuid[3], pdu + 4);
>>>>> + /*
>>>>> + * 2 octets - start handle
>>>>> + * 2 octets - end handle
>>>>> + * 2 octets - 16-bit uuid
>>>>> + */
>>>>> + length = 6;
>>>>> + } else {
>>>>> + bswap_128(&op->uuid, pdu + 4);
>>>>> + /*
>>>>> + * 2 octets - start handle
>>>>> + * 2 octets - end handle
>>>>> + * 16 octets - 128-bit uuid
>>>>> + */
>>>>> + length = 20;
>>>>> + }
>>>>> +
>>>>> + if (!bt_att_send(op->client->att, BT_ATT_OP_READ_BY_TYPE_REQ,
>>>>> + pdu, length, read_by_uuid_cb,
>>>>> + read_by_uuid_op_ref(op),
>>>>> + read_by_uuid_op_unref)) {
>>>>> + read_by_uuid_op_unref(op);
>>>>> + success = false;
>>>>> + att_ecode = 0;
>>>>> + goto done;
>>>>> + }
>>>>> +
>>>>> + return;
>>>>> + }
>>>>> +
>>>>> + success = true;
>>>>> + att_ecode = 0;
>>>>> +
>>>>> +done:
>>>>> + if (op->callback)
>>>>> + op->callback(success, att_ecode, op->result_head,
>>>>> + op->user_data);
>>>>> +}
>>>>> +
>>>>> +bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
>>>>> + uint16_t start_handle, uint16_t end_handle,
>>>>> + const uint8_t uuid[BT_GATT_UUID_SIZE],
>>>>> + bt_gatt_client_read_by_uuid_callback_t callback,
>>>>> + void *user_data,
>>>>> + bt_gatt_client_destroy_func_t destroy)
>>>>> +{
>>>>> + struct read_by_uuid_op *op;
>>>>> + uint16_t length;
>>>>> + uint8_t pdu[20];
>>>>> +
>>>>> + if (!client)
>>>>> + return false;
>>>>> +
>>>>> + if (start_handle > end_handle || start_handle == 0x0000)
>>>>> + return false;
>>>>> +
>>>>> + op = new0(struct read_by_uuid_op, 1);
>>>>> + if (!op)
>>>>> + return false;
>>>>> +
>>>>> + op->client = client;
>>>>> + op->start_handle = start_handle;
>>>>> + op->end_handle = end_handle;
>>>>> + op->destroy = destroy;
>>>>> + op->callback = callback;
>>>>> + op->user_data = user_data;
>>>>> + memcpy(&op->uuid, uuid, 16);
>>>>> +
>>>>> + /* Convert to 16-bit if it is Bluetooth UUID */
>>>>> + if (is_bluetooth_uuid(uuid)) {
>>>>> + put_le16((uuid[2] << 8) + uuid[3], pdu + 4);
>>>>> + /*
>>>>> + * 2 octets - start handle
>>>>> + * 2 octets - end handle
>>>>> + * 2 octets - 16-bit uuid
>>>>> + */
>>>>> + length = 6;
>>>>> + } else {
>>>>> + bswap_128(uuid, pdu + 4);
>>>>> + /*
>>>>> + * 2 octets - start handle
>>>>> + * 2 octets - end handle
>>>>> + * 16 octets - 128-bit uuid
>>>>> + */
>>>>> + length = 20;
>>>>> + }
>>>>> +
>>>>> + put_le16(start_handle, pdu);
>>>>> + put_le16(end_handle, pdu + 2);
>>>>> +
>>>>> + if (!bt_att_send(client->att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu,
>>>>> + length, read_by_uuid_cb,
>>>>> + read_by_uuid_op_ref(op),
>>>>> + read_by_uuid_op_unref)) {
>>>>> + free(op);
>>>>> + return false;
>>>>> + }
>>>>> +
>>>>> + return true;
>>>>> +}
>>>>> +
>>>>> bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
>>>>> uint16_t value_handle,
>>>>> bool signed_write,
>>>>> diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
>>>>> index adccfc5..921ed75 100644
>>>>> --- a/src/shared/gatt-client.h
>>>>> +++ b/src/shared/gatt-client.h
>>>>> @@ -145,6 +145,24 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
>>>>> void *user_data,
>>>>> bt_gatt_client_destroy_func_t destroy);
>>>>>
>>>>> +struct read_by_uuid_res;
>>>>> +
>>>>> +struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result);
>>>>> +bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
>>>>> + uint8_t *length, uint8_t **data);
>>>>> +
>>>>> +typedef void (*bt_gatt_client_read_by_uuid_callback_t)(bool success,
>>>>> + uint8_t att_ecode,
>>>>> + struct read_by_uuid_res *result,
>>>>> + void *user_data);
>>>>> +
>>>>> +bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
>>>>> + uint16_t start_handle, uint16_t end_handle,
>>>>> + const uint8_t uuid[BT_GATT_UUID_SIZE],
>>>>> + bt_gatt_client_read_by_uuid_callback_t callback,
>>>>> + void *user_data,
>>>>> + bt_gatt_client_destroy_func_t destroy);
>>>>
>>>> Isn't this essentially duplicating bt_gatt_service_iter_next_by_uuid?
>>>> I mean we should be reading everything already but this doesn't seems
>>>> to using the cache and in case it is just to fulfill a test perhaps it
>>>> is better to do it in gatt-helpers so we keep bt_gatt_client API
>>>> clean.
>>>
>>> It is functionality of GATT client described in chapter 4.8.2 "Read
>>> Using Characteristic UUID".
>>> Difference is that client can read attributes by passing UUID, and
>>> results can contain
>>> different valu lengths. And client interprets results instead of gatt-helpers.
>>> I implemented it here because all read/write operations on
>>> characteristics and descriptors
>>> are exposed in bt_gatt_client, and all discovery stuff is implemented
>>> in gatt_helpers. But if
>>> you don't agree I can change it.
>>
>> I see, it does actually end up reading, perhaps it is a good idea to
>> keep it here then. In the other hand this API seems to be going in the
>> same direction that gatt-db had with access by handles, perhaps we
>> should do an API facelift to access by attribute such as we did with
>> gatt-db, also the iterator could be replaced with queues which I guess
>> is not that different in the end.
>>
>
> The reason we have the iterators and not queues is because Marcel
> wanted to keep shared/queue as an internal utility of shared code
> (although shared/gatt-db exposes it to upper layers). In this case
> performing the read/write operations by handle isn't bad since the
> code simply constructs the request and sends it out and doesn't need
> to do an internal lookup for it.
>
> I commented on Marcin's new patch set that this code should go to
> gatt-helpers. I don't think it makes sense to put it in gatt-client,
> especially since the characteristic and include declaration discovery
Include service discovery can't be based on that API, because it
performs read_by_type
and read requests during discovery. It can be shared by characteristic
discovery only.
Additionally in characteristic discovery gatt-client interprets
results and cancel if length of
response is not valid. Also, you will be able to use include service
iterator and characteristic iterators
to read results of read-by-uuid (because it checks type of response
and length). I don't see benefit
having this in one place.
> procedures are already based on Read By Type and we can create a
> cleaner API by having this in gatt-helpers and basing the char/incl
> discovery functions on that instead.
>
>>>>
>>>>> bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
>>>>> uint16_t value_handle,
>>>>> bool signed_write,
>>>>> --
>>>>> 1.9.3
>>>>>
>>>>> --
>>>>> 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
>>>
>>> BR
>>> Marcin
>>
>>
>>
>> --
>> Luiz Augusto von Dentz
>> --
>> 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
>
> Cheers,
> Arman
BR
Marcin
Hi Luiz,
> On Fri, Nov 14, 2014 at 5:51 AM, Luiz Augusto von Dentz <[email protected]> wrote:
> Hi Marcin,
>
> On Fri, Nov 14, 2014 at 3:13 PM, Marcin Kraglak
> <[email protected]> wrote:
>> Hi Luiz,
>>
>> On 14 November 2014 13:44, Luiz Augusto von Dentz <[email protected]> wrote:
>>> Hi Marcin,
>>>
>>> On Thu, Nov 13, 2014 at 1:27 PM, Marcin Kraglak
>>> <[email protected]> wrote:
>>>> This is exposed to allow client reading characteristic values
>>>> with known uuid.
>>>> ---
>>>> src/shared/gatt-client.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++
>>>> src/shared/gatt-client.h | 18 ++++
>>>> 2 files changed, 289 insertions(+)
>>>>
>>>> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
>>>> index 30b271e..0493b05 100644
>>>> --- a/src/shared/gatt-client.c
>>>> +++ b/src/shared/gatt-client.c
>>>> @@ -44,6 +44,11 @@
>>>> #define GATT_SVC_UUID 0x1801
>>>> #define SVC_CHNGD_UUID 0x2a05
>>>>
>>>> +static const uint8_t bt_base_uuid[16] = {
>>>> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
>>>> + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
>>>> +};
>>>> +
>>>> struct chrc_data {
>>>> /* The public characteristic entry. */
>>>> bt_gatt_characteristic_t chrc_external;
>>>> @@ -122,6 +127,15 @@ struct bt_gatt_client {
>>>> bool in_svc_chngd;
>>>> };
>>>>
>>>> +static bool is_bluetooth_uuid(const uint8_t uuid[BT_GATT_UUID_SIZE])
>>>> +{
>>>> + if (memcmp(uuid, bt_base_uuid, 2) ||
>>>> + memcmp(&uuid[4], &bt_base_uuid[4], 12))
>>>> + return false;
>>>> +
>>>> + return true;
>>>> +}
>>>> +
>>>
>>> Is this really different from uuid_cmp? Perhaps you use set uuid16
>>> properly it should just do the same you are doing above.
>> Ok, I'll use it.
>>>
>>>> struct notify_data {
>>>> struct bt_gatt_client *client;
>>>> bool removed;
>>>> @@ -2001,6 +2015,263 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
>>>> return true;
>>>> }
>>>>
>>>> +struct read_by_uuid_res {
>>>> + uint16_t handle;
>>>> + uint8_t length;
>>>> + uint8_t *data;
>>>> + struct read_by_uuid_res *next;
>>>> +};
>>>> +
>>>> +bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
>>>> + uint8_t *length, uint8_t **data)
>>>> +{
>>>> + if (!result)
>>>> + return false;
>>>> +
>>>> + *length = result->length;
>>>> + *handle = result->handle;
>>>> + *data = result->data;
>>>> +
>>>> + return true;
>>>> +}
>>>> +
>>>> +struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result)
>>>> +{
>>>> + if (!result)
>>>> + return NULL;
>>>> +
>>>> + return result->next;
>>>> +}
>>>> +
>>>> +struct read_by_uuid_op {
>>>> + struct bt_gatt_client *client;
>>>> + bt_gatt_client_read_by_uuid_callback_t callback;
>>>> + int ref_count;
>>>> + uint16_t start_handle;
>>>> + uint16_t end_handle;
>>>> + uint8_t uuid[BT_GATT_UUID_SIZE];
>>>> + struct read_by_uuid_res *result_head;
>>>> + struct read_by_uuid_res *result_tail;
>>>> + void *user_data;
>>>> + bt_gatt_client_destroy_func_t destroy;
>>>> +};
>>>
>>> Im not really sure this lists are really helping us, perhaps this is
>>> the effect of using iterators in the API but that seems to create
>>> duplicated code.
>>>
>>>> +static bool append_read_by_uuid_result(struct read_by_uuid_op *op,
>>>> + uint16_t handle, uint16_t length,
>>>> + const uint8_t *data)
>>>> +{
>>>> + struct read_by_uuid_res *result;
>>>> +
>>>> + result = new0(struct read_by_uuid_res, 1);
>>>> + if (!result)
>>>> + return false;
>>>> +
>>>> + result->data = malloc(length);
>>>> + if (!result->data) {
>>>> + free(result);
>>>> + return false;
>>>> + }
>>>> +
>>>> + result->handle = handle;
>>>> + result->length = length;
>>>> + memcpy(result->data, data, length);
>>>> +
>>>> + if (op->result_head) {
>>>> + op->result_tail->next = result;
>>>> + op->result_tail = result;
>>>> + } else {
>>>> + op->result_head = op->result_tail = result;
>>>> + }
>>>> +
>>>> + return true;
>>>> +}
>>>> +
>>>> +static void destroy_read_by_uuid_result(void *data)
>>>> +{
>>>> + struct read_by_uuid_res *result = data;
>>>> +
>>>> + free(result->data);
>>>> + free(result);
>>>> +}
>>>> +
>>>> +static struct read_by_uuid_op *read_by_uuid_op_ref(struct read_by_uuid_op *op)
>>>> +{
>>>> + __sync_fetch_and_add(&op->ref_count, 1);
>>>> +
>>>> + return op;
>>>> +}
>>>> +
>>>> +static void read_by_uuid_op_unref(void *data)
>>>> +{
>>>> + struct read_by_uuid_res *next, *temp;
>>>> + struct read_by_uuid_op *op = data;
>>>> +
>>>> + if (__sync_sub_and_fetch(&op->ref_count, 1))
>>>> + return;
>>>> +
>>>> + if (op->destroy)
>>>> + op->destroy(op->user_data);
>>>> +
>>>> + for (temp = op->result_head; temp; temp = next) {
>>>> + next = temp->next;
>>>> + destroy_read_by_uuid_result(temp);
>>>> + }
>>>> +
>>>> + free(op);
>>>> +}
>>>> +
>>>> +static void read_by_uuid_cb(uint8_t opcode, const void *pdu,
>>>> + uint16_t length, void *user_data)
>>>> +{
>>>> + struct read_by_uuid_op *op = user_data;
>>>> + size_t data_length;
>>>> + uint8_t att_ecode;
>>>> + uint16_t offset, last_handle;
>>>> + const uint8_t *data;
>>>> + bool success;
>>>> +
>>>> + if (opcode == BT_ATT_OP_ERROR_RSP) {
>>>> + att_ecode = process_error(pdu, length);
>>>> + if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND)
>>>> + success = true;
>>>> + else
>>>> + success = false;
>>>> +
>>>> + goto done;
>>>> + }
>>>> +
>>>> + if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || (!pdu && length)) {
>>>> + success = false;
>>>> + att_ecode = 0;
>>>> + goto done;
>>>> + }
>>>> +
>>>> + data_length = ((const uint8_t *) pdu)[0];
>>>> + if ((length - 1) % data_length) {
>>>> + success = false;
>>>> + att_ecode = 0;
>>>> + goto done;
>>>> + }
>>>> +
>>>> + for (offset = 1; offset < length; offset += data_length) {
>>>> + data = pdu + offset;
>>>> + if (!append_read_by_uuid_result(op, get_le16(data),
>>>> + data_length - 2, data + 2)) {
>>>> + success = false;
>>>> + att_ecode = 0;
>>>> + goto done;
>>>> + }
>>>> + }
>>>> +
>>>> + last_handle = get_le16(pdu + length - data_length);
>>>> + if (last_handle < op->end_handle) {
>>>> + uint8_t pdu[20];
>>>> +
>>>> + put_le16(last_handle + 1, pdu);
>>>> + put_le16(op->end_handle, pdu + 2);
>>>> +
>>>> + if (is_bluetooth_uuid(op->uuid)) {
>>>> + put_le16((op->uuid[2] << 8) + op->uuid[3], pdu + 4);
>>>> + /*
>>>> + * 2 octets - start handle
>>>> + * 2 octets - end handle
>>>> + * 2 octets - 16-bit uuid
>>>> + */
>>>> + length = 6;
>>>> + } else {
>>>> + bswap_128(&op->uuid, pdu + 4);
>>>> + /*
>>>> + * 2 octets - start handle
>>>> + * 2 octets - end handle
>>>> + * 16 octets - 128-bit uuid
>>>> + */
>>>> + length = 20;
>>>> + }
>>>> +
>>>> + if (!bt_att_send(op->client->att, BT_ATT_OP_READ_BY_TYPE_REQ,
>>>> + pdu, length, read_by_uuid_cb,
>>>> + read_by_uuid_op_ref(op),
>>>> + read_by_uuid_op_unref)) {
>>>> + read_by_uuid_op_unref(op);
>>>> + success = false;
>>>> + att_ecode = 0;
>>>> + goto done;
>>>> + }
>>>> +
>>>> + return;
>>>> + }
>>>> +
>>>> + success = true;
>>>> + att_ecode = 0;
>>>> +
>>>> +done:
>>>> + if (op->callback)
>>>> + op->callback(success, att_ecode, op->result_head,
>>>> + op->user_data);
>>>> +}
>>>> +
>>>> +bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
>>>> + uint16_t start_handle, uint16_t end_handle,
>>>> + const uint8_t uuid[BT_GATT_UUID_SIZE],
>>>> + bt_gatt_client_read_by_uuid_callback_t callback,
>>>> + void *user_data,
>>>> + bt_gatt_client_destroy_func_t destroy)
>>>> +{
>>>> + struct read_by_uuid_op *op;
>>>> + uint16_t length;
>>>> + uint8_t pdu[20];
>>>> +
>>>> + if (!client)
>>>> + return false;
>>>> +
>>>> + if (start_handle > end_handle || start_handle == 0x0000)
>>>> + return false;
>>>> +
>>>> + op = new0(struct read_by_uuid_op, 1);
>>>> + if (!op)
>>>> + return false;
>>>> +
>>>> + op->client = client;
>>>> + op->start_handle = start_handle;
>>>> + op->end_handle = end_handle;
>>>> + op->destroy = destroy;
>>>> + op->callback = callback;
>>>> + op->user_data = user_data;
>>>> + memcpy(&op->uuid, uuid, 16);
>>>> +
>>>> + /* Convert to 16-bit if it is Bluetooth UUID */
>>>> + if (is_bluetooth_uuid(uuid)) {
>>>> + put_le16((uuid[2] << 8) + uuid[3], pdu + 4);
>>>> + /*
>>>> + * 2 octets - start handle
>>>> + * 2 octets - end handle
>>>> + * 2 octets - 16-bit uuid
>>>> + */
>>>> + length = 6;
>>>> + } else {
>>>> + bswap_128(uuid, pdu + 4);
>>>> + /*
>>>> + * 2 octets - start handle
>>>> + * 2 octets - end handle
>>>> + * 16 octets - 128-bit uuid
>>>> + */
>>>> + length = 20;
>>>> + }
>>>> +
>>>> + put_le16(start_handle, pdu);
>>>> + put_le16(end_handle, pdu + 2);
>>>> +
>>>> + if (!bt_att_send(client->att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu,
>>>> + length, read_by_uuid_cb,
>>>> + read_by_uuid_op_ref(op),
>>>> + read_by_uuid_op_unref)) {
>>>> + free(op);
>>>> + return false;
>>>> + }
>>>> +
>>>> + return true;
>>>> +}
>>>> +
>>>> bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
>>>> uint16_t value_handle,
>>>> bool signed_write,
>>>> diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
>>>> index adccfc5..921ed75 100644
>>>> --- a/src/shared/gatt-client.h
>>>> +++ b/src/shared/gatt-client.h
>>>> @@ -145,6 +145,24 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
>>>> void *user_data,
>>>> bt_gatt_client_destroy_func_t destroy);
>>>>
>>>> +struct read_by_uuid_res;
>>>> +
>>>> +struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result);
>>>> +bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
>>>> + uint8_t *length, uint8_t **data);
>>>> +
>>>> +typedef void (*bt_gatt_client_read_by_uuid_callback_t)(bool success,
>>>> + uint8_t att_ecode,
>>>> + struct read_by_uuid_res *result,
>>>> + void *user_data);
>>>> +
>>>> +bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
>>>> + uint16_t start_handle, uint16_t end_handle,
>>>> + const uint8_t uuid[BT_GATT_UUID_SIZE],
>>>> + bt_gatt_client_read_by_uuid_callback_t callback,
>>>> + void *user_data,
>>>> + bt_gatt_client_destroy_func_t destroy);
>>>
>>> Isn't this essentially duplicating bt_gatt_service_iter_next_by_uuid?
>>> I mean we should be reading everything already but this doesn't seems
>>> to using the cache and in case it is just to fulfill a test perhaps it
>>> is better to do it in gatt-helpers so we keep bt_gatt_client API
>>> clean.
>>
>> It is functionality of GATT client described in chapter 4.8.2 "Read
>> Using Characteristic UUID".
>> Difference is that client can read attributes by passing UUID, and
>> results can contain
>> different valu lengths. And client interprets results instead of gatt-helpers.
>> I implemented it here because all read/write operations on
>> characteristics and descriptors
>> are exposed in bt_gatt_client, and all discovery stuff is implemented
>> in gatt_helpers. But if
>> you don't agree I can change it.
>
> I see, it does actually end up reading, perhaps it is a good idea to
> keep it here then. In the other hand this API seems to be going in the
> same direction that gatt-db had with access by handles, perhaps we
> should do an API facelift to access by attribute such as we did with
> gatt-db, also the iterator could be replaced with queues which I guess
> is not that different in the end.
>
The reason we have the iterators and not queues is because Marcel
wanted to keep shared/queue as an internal utility of shared code
(although shared/gatt-db exposes it to upper layers). In this case
performing the read/write operations by handle isn't bad since the
code simply constructs the request and sends it out and doesn't need
to do an internal lookup for it.
I commented on Marcin's new patch set that this code should go to
gatt-helpers. I don't think it makes sense to put it in gatt-client,
especially since the characteristic and include declaration discovery
procedures are already based on Read By Type and we can create a
cleaner API by having this in gatt-helpers and basing the char/incl
discovery functions on that instead.
>>>
>>>> bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
>>>> uint16_t value_handle,
>>>> bool signed_write,
>>>> --
>>>> 1.9.3
>>>>
>>>> --
>>>> 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
>>
>> BR
>> Marcin
>
>
>
> --
> Luiz Augusto von Dentz
> --
> 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
Cheers,
Arman
Hi Marcin,
On Fri, Nov 14, 2014 at 3:13 PM, Marcin Kraglak
<[email protected]> wrote:
> Hi Luiz,
>
> On 14 November 2014 13:44, Luiz Augusto von Dentz <[email protected]> wrote:
>> Hi Marcin,
>>
>> On Thu, Nov 13, 2014 at 1:27 PM, Marcin Kraglak
>> <[email protected]> wrote:
>>> This is exposed to allow client reading characteristic values
>>> with known uuid.
>>> ---
>>> src/shared/gatt-client.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++
>>> src/shared/gatt-client.h | 18 ++++
>>> 2 files changed, 289 insertions(+)
>>>
>>> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
>>> index 30b271e..0493b05 100644
>>> --- a/src/shared/gatt-client.c
>>> +++ b/src/shared/gatt-client.c
>>> @@ -44,6 +44,11 @@
>>> #define GATT_SVC_UUID 0x1801
>>> #define SVC_CHNGD_UUID 0x2a05
>>>
>>> +static const uint8_t bt_base_uuid[16] = {
>>> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
>>> + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
>>> +};
>>> +
>>> struct chrc_data {
>>> /* The public characteristic entry. */
>>> bt_gatt_characteristic_t chrc_external;
>>> @@ -122,6 +127,15 @@ struct bt_gatt_client {
>>> bool in_svc_chngd;
>>> };
>>>
>>> +static bool is_bluetooth_uuid(const uint8_t uuid[BT_GATT_UUID_SIZE])
>>> +{
>>> + if (memcmp(uuid, bt_base_uuid, 2) ||
>>> + memcmp(&uuid[4], &bt_base_uuid[4], 12))
>>> + return false;
>>> +
>>> + return true;
>>> +}
>>> +
>>
>> Is this really different from uuid_cmp? Perhaps you use set uuid16
>> properly it should just do the same you are doing above.
> Ok, I'll use it.
>>
>>> struct notify_data {
>>> struct bt_gatt_client *client;
>>> bool removed;
>>> @@ -2001,6 +2015,263 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
>>> return true;
>>> }
>>>
>>> +struct read_by_uuid_res {
>>> + uint16_t handle;
>>> + uint8_t length;
>>> + uint8_t *data;
>>> + struct read_by_uuid_res *next;
>>> +};
>>> +
>>> +bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
>>> + uint8_t *length, uint8_t **data)
>>> +{
>>> + if (!result)
>>> + return false;
>>> +
>>> + *length = result->length;
>>> + *handle = result->handle;
>>> + *data = result->data;
>>> +
>>> + return true;
>>> +}
>>> +
>>> +struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result)
>>> +{
>>> + if (!result)
>>> + return NULL;
>>> +
>>> + return result->next;
>>> +}
>>> +
>>> +struct read_by_uuid_op {
>>> + struct bt_gatt_client *client;
>>> + bt_gatt_client_read_by_uuid_callback_t callback;
>>> + int ref_count;
>>> + uint16_t start_handle;
>>> + uint16_t end_handle;
>>> + uint8_t uuid[BT_GATT_UUID_SIZE];
>>> + struct read_by_uuid_res *result_head;
>>> + struct read_by_uuid_res *result_tail;
>>> + void *user_data;
>>> + bt_gatt_client_destroy_func_t destroy;
>>> +};
>>
>> Im not really sure this lists are really helping us, perhaps this is
>> the effect of using iterators in the API but that seems to create
>> duplicated code.
>>
>>> +static bool append_read_by_uuid_result(struct read_by_uuid_op *op,
>>> + uint16_t handle, uint16_t length,
>>> + const uint8_t *data)
>>> +{
>>> + struct read_by_uuid_res *result;
>>> +
>>> + result = new0(struct read_by_uuid_res, 1);
>>> + if (!result)
>>> + return false;
>>> +
>>> + result->data = malloc(length);
>>> + if (!result->data) {
>>> + free(result);
>>> + return false;
>>> + }
>>> +
>>> + result->handle = handle;
>>> + result->length = length;
>>> + memcpy(result->data, data, length);
>>> +
>>> + if (op->result_head) {
>>> + op->result_tail->next = result;
>>> + op->result_tail = result;
>>> + } else {
>>> + op->result_head = op->result_tail = result;
>>> + }
>>> +
>>> + return true;
>>> +}
>>> +
>>> +static void destroy_read_by_uuid_result(void *data)
>>> +{
>>> + struct read_by_uuid_res *result = data;
>>> +
>>> + free(result->data);
>>> + free(result);
>>> +}
>>> +
>>> +static struct read_by_uuid_op *read_by_uuid_op_ref(struct read_by_uuid_op *op)
>>> +{
>>> + __sync_fetch_and_add(&op->ref_count, 1);
>>> +
>>> + return op;
>>> +}
>>> +
>>> +static void read_by_uuid_op_unref(void *data)
>>> +{
>>> + struct read_by_uuid_res *next, *temp;
>>> + struct read_by_uuid_op *op = data;
>>> +
>>> + if (__sync_sub_and_fetch(&op->ref_count, 1))
>>> + return;
>>> +
>>> + if (op->destroy)
>>> + op->destroy(op->user_data);
>>> +
>>> + for (temp = op->result_head; temp; temp = next) {
>>> + next = temp->next;
>>> + destroy_read_by_uuid_result(temp);
>>> + }
>>> +
>>> + free(op);
>>> +}
>>> +
>>> +static void read_by_uuid_cb(uint8_t opcode, const void *pdu,
>>> + uint16_t length, void *user_data)
>>> +{
>>> + struct read_by_uuid_op *op = user_data;
>>> + size_t data_length;
>>> + uint8_t att_ecode;
>>> + uint16_t offset, last_handle;
>>> + const uint8_t *data;
>>> + bool success;
>>> +
>>> + if (opcode == BT_ATT_OP_ERROR_RSP) {
>>> + att_ecode = process_error(pdu, length);
>>> + if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND)
>>> + success = true;
>>> + else
>>> + success = false;
>>> +
>>> + goto done;
>>> + }
>>> +
>>> + if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || (!pdu && length)) {
>>> + success = false;
>>> + att_ecode = 0;
>>> + goto done;
>>> + }
>>> +
>>> + data_length = ((const uint8_t *) pdu)[0];
>>> + if ((length - 1) % data_length) {
>>> + success = false;
>>> + att_ecode = 0;
>>> + goto done;
>>> + }
>>> +
>>> + for (offset = 1; offset < length; offset += data_length) {
>>> + data = pdu + offset;
>>> + if (!append_read_by_uuid_result(op, get_le16(data),
>>> + data_length - 2, data + 2)) {
>>> + success = false;
>>> + att_ecode = 0;
>>> + goto done;
>>> + }
>>> + }
>>> +
>>> + last_handle = get_le16(pdu + length - data_length);
>>> + if (last_handle < op->end_handle) {
>>> + uint8_t pdu[20];
>>> +
>>> + put_le16(last_handle + 1, pdu);
>>> + put_le16(op->end_handle, pdu + 2);
>>> +
>>> + if (is_bluetooth_uuid(op->uuid)) {
>>> + put_le16((op->uuid[2] << 8) + op->uuid[3], pdu + 4);
>>> + /*
>>> + * 2 octets - start handle
>>> + * 2 octets - end handle
>>> + * 2 octets - 16-bit uuid
>>> + */
>>> + length = 6;
>>> + } else {
>>> + bswap_128(&op->uuid, pdu + 4);
>>> + /*
>>> + * 2 octets - start handle
>>> + * 2 octets - end handle
>>> + * 16 octets - 128-bit uuid
>>> + */
>>> + length = 20;
>>> + }
>>> +
>>> + if (!bt_att_send(op->client->att, BT_ATT_OP_READ_BY_TYPE_REQ,
>>> + pdu, length, read_by_uuid_cb,
>>> + read_by_uuid_op_ref(op),
>>> + read_by_uuid_op_unref)) {
>>> + read_by_uuid_op_unref(op);
>>> + success = false;
>>> + att_ecode = 0;
>>> + goto done;
>>> + }
>>> +
>>> + return;
>>> + }
>>> +
>>> + success = true;
>>> + att_ecode = 0;
>>> +
>>> +done:
>>> + if (op->callback)
>>> + op->callback(success, att_ecode, op->result_head,
>>> + op->user_data);
>>> +}
>>> +
>>> +bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
>>> + uint16_t start_handle, uint16_t end_handle,
>>> + const uint8_t uuid[BT_GATT_UUID_SIZE],
>>> + bt_gatt_client_read_by_uuid_callback_t callback,
>>> + void *user_data,
>>> + bt_gatt_client_destroy_func_t destroy)
>>> +{
>>> + struct read_by_uuid_op *op;
>>> + uint16_t length;
>>> + uint8_t pdu[20];
>>> +
>>> + if (!client)
>>> + return false;
>>> +
>>> + if (start_handle > end_handle || start_handle == 0x0000)
>>> + return false;
>>> +
>>> + op = new0(struct read_by_uuid_op, 1);
>>> + if (!op)
>>> + return false;
>>> +
>>> + op->client = client;
>>> + op->start_handle = start_handle;
>>> + op->end_handle = end_handle;
>>> + op->destroy = destroy;
>>> + op->callback = callback;
>>> + op->user_data = user_data;
>>> + memcpy(&op->uuid, uuid, 16);
>>> +
>>> + /* Convert to 16-bit if it is Bluetooth UUID */
>>> + if (is_bluetooth_uuid(uuid)) {
>>> + put_le16((uuid[2] << 8) + uuid[3], pdu + 4);
>>> + /*
>>> + * 2 octets - start handle
>>> + * 2 octets - end handle
>>> + * 2 octets - 16-bit uuid
>>> + */
>>> + length = 6;
>>> + } else {
>>> + bswap_128(uuid, pdu + 4);
>>> + /*
>>> + * 2 octets - start handle
>>> + * 2 octets - end handle
>>> + * 16 octets - 128-bit uuid
>>> + */
>>> + length = 20;
>>> + }
>>> +
>>> + put_le16(start_handle, pdu);
>>> + put_le16(end_handle, pdu + 2);
>>> +
>>> + if (!bt_att_send(client->att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu,
>>> + length, read_by_uuid_cb,
>>> + read_by_uuid_op_ref(op),
>>> + read_by_uuid_op_unref)) {
>>> + free(op);
>>> + return false;
>>> + }
>>> +
>>> + return true;
>>> +}
>>> +
>>> bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
>>> uint16_t value_handle,
>>> bool signed_write,
>>> diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
>>> index adccfc5..921ed75 100644
>>> --- a/src/shared/gatt-client.h
>>> +++ b/src/shared/gatt-client.h
>>> @@ -145,6 +145,24 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
>>> void *user_data,
>>> bt_gatt_client_destroy_func_t destroy);
>>>
>>> +struct read_by_uuid_res;
>>> +
>>> +struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result);
>>> +bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
>>> + uint8_t *length, uint8_t **data);
>>> +
>>> +typedef void (*bt_gatt_client_read_by_uuid_callback_t)(bool success,
>>> + uint8_t att_ecode,
>>> + struct read_by_uuid_res *result,
>>> + void *user_data);
>>> +
>>> +bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
>>> + uint16_t start_handle, uint16_t end_handle,
>>> + const uint8_t uuid[BT_GATT_UUID_SIZE],
>>> + bt_gatt_client_read_by_uuid_callback_t callback,
>>> + void *user_data,
>>> + bt_gatt_client_destroy_func_t destroy);
>>
>> Isn't this essentially duplicating bt_gatt_service_iter_next_by_uuid?
>> I mean we should be reading everything already but this doesn't seems
>> to using the cache and in case it is just to fulfill a test perhaps it
>> is better to do it in gatt-helpers so we keep bt_gatt_client API
>> clean.
>
> It is functionality of GATT client described in chapter 4.8.2 "Read
> Using Characteristic UUID".
> Difference is that client can read attributes by passing UUID, and
> results can contain
> different valu lengths. And client interprets results instead of gatt-helpers.
> I implemented it here because all read/write operations on
> characteristics and descriptors
> are exposed in bt_gatt_client, and all discovery stuff is implemented
> in gatt_helpers. But if
> you don't agree I can change it.
I see, it does actually end up reading, perhaps it is a good idea to
keep it here then. In the other hand this API seems to be going in the
same direction that gatt-db had with access by handles, perhaps we
should do an API facelift to access by attribute such as we did with
gatt-db, also the iterator could be replaced with queues which I guess
is not that different in the end.
>>
>>> bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
>>> uint16_t value_handle,
>>> bool signed_write,
>>> --
>>> 1.9.3
>>>
>>> --
>>> 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
>
> BR
> Marcin
--
Luiz Augusto von Dentz
Hi Luiz,
On 14 November 2014 13:44, Luiz Augusto von Dentz <[email protected]> wrote:
> Hi Marcin,
>
> On Thu, Nov 13, 2014 at 1:27 PM, Marcin Kraglak
> <[email protected]> wrote:
>> This is exposed to allow client reading characteristic values
>> with known uuid.
>> ---
>> src/shared/gatt-client.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++
>> src/shared/gatt-client.h | 18 ++++
>> 2 files changed, 289 insertions(+)
>>
>> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
>> index 30b271e..0493b05 100644
>> --- a/src/shared/gatt-client.c
>> +++ b/src/shared/gatt-client.c
>> @@ -44,6 +44,11 @@
>> #define GATT_SVC_UUID 0x1801
>> #define SVC_CHNGD_UUID 0x2a05
>>
>> +static const uint8_t bt_base_uuid[16] = {
>> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
>> + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
>> +};
>> +
>> struct chrc_data {
>> /* The public characteristic entry. */
>> bt_gatt_characteristic_t chrc_external;
>> @@ -122,6 +127,15 @@ struct bt_gatt_client {
>> bool in_svc_chngd;
>> };
>>
>> +static bool is_bluetooth_uuid(const uint8_t uuid[BT_GATT_UUID_SIZE])
>> +{
>> + if (memcmp(uuid, bt_base_uuid, 2) ||
>> + memcmp(&uuid[4], &bt_base_uuid[4], 12))
>> + return false;
>> +
>> + return true;
>> +}
>> +
>
> Is this really different from uuid_cmp? Perhaps you use set uuid16
> properly it should just do the same you are doing above.
Ok, I'll use it.
>
>> struct notify_data {
>> struct bt_gatt_client *client;
>> bool removed;
>> @@ -2001,6 +2015,263 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
>> return true;
>> }
>>
>> +struct read_by_uuid_res {
>> + uint16_t handle;
>> + uint8_t length;
>> + uint8_t *data;
>> + struct read_by_uuid_res *next;
>> +};
>> +
>> +bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
>> + uint8_t *length, uint8_t **data)
>> +{
>> + if (!result)
>> + return false;
>> +
>> + *length = result->length;
>> + *handle = result->handle;
>> + *data = result->data;
>> +
>> + return true;
>> +}
>> +
>> +struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result)
>> +{
>> + if (!result)
>> + return NULL;
>> +
>> + return result->next;
>> +}
>> +
>> +struct read_by_uuid_op {
>> + struct bt_gatt_client *client;
>> + bt_gatt_client_read_by_uuid_callback_t callback;
>> + int ref_count;
>> + uint16_t start_handle;
>> + uint16_t end_handle;
>> + uint8_t uuid[BT_GATT_UUID_SIZE];
>> + struct read_by_uuid_res *result_head;
>> + struct read_by_uuid_res *result_tail;
>> + void *user_data;
>> + bt_gatt_client_destroy_func_t destroy;
>> +};
>
> Im not really sure this lists are really helping us, perhaps this is
> the effect of using iterators in the API but that seems to create
> duplicated code.
>
>> +static bool append_read_by_uuid_result(struct read_by_uuid_op *op,
>> + uint16_t handle, uint16_t length,
>> + const uint8_t *data)
>> +{
>> + struct read_by_uuid_res *result;
>> +
>> + result = new0(struct read_by_uuid_res, 1);
>> + if (!result)
>> + return false;
>> +
>> + result->data = malloc(length);
>> + if (!result->data) {
>> + free(result);
>> + return false;
>> + }
>> +
>> + result->handle = handle;
>> + result->length = length;
>> + memcpy(result->data, data, length);
>> +
>> + if (op->result_head) {
>> + op->result_tail->next = result;
>> + op->result_tail = result;
>> + } else {
>> + op->result_head = op->result_tail = result;
>> + }
>> +
>> + return true;
>> +}
>> +
>> +static void destroy_read_by_uuid_result(void *data)
>> +{
>> + struct read_by_uuid_res *result = data;
>> +
>> + free(result->data);
>> + free(result);
>> +}
>> +
>> +static struct read_by_uuid_op *read_by_uuid_op_ref(struct read_by_uuid_op *op)
>> +{
>> + __sync_fetch_and_add(&op->ref_count, 1);
>> +
>> + return op;
>> +}
>> +
>> +static void read_by_uuid_op_unref(void *data)
>> +{
>> + struct read_by_uuid_res *next, *temp;
>> + struct read_by_uuid_op *op = data;
>> +
>> + if (__sync_sub_and_fetch(&op->ref_count, 1))
>> + return;
>> +
>> + if (op->destroy)
>> + op->destroy(op->user_data);
>> +
>> + for (temp = op->result_head; temp; temp = next) {
>> + next = temp->next;
>> + destroy_read_by_uuid_result(temp);
>> + }
>> +
>> + free(op);
>> +}
>> +
>> +static void read_by_uuid_cb(uint8_t opcode, const void *pdu,
>> + uint16_t length, void *user_data)
>> +{
>> + struct read_by_uuid_op *op = user_data;
>> + size_t data_length;
>> + uint8_t att_ecode;
>> + uint16_t offset, last_handle;
>> + const uint8_t *data;
>> + bool success;
>> +
>> + if (opcode == BT_ATT_OP_ERROR_RSP) {
>> + att_ecode = process_error(pdu, length);
>> + if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND)
>> + success = true;
>> + else
>> + success = false;
>> +
>> + goto done;
>> + }
>> +
>> + if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || (!pdu && length)) {
>> + success = false;
>> + att_ecode = 0;
>> + goto done;
>> + }
>> +
>> + data_length = ((const uint8_t *) pdu)[0];
>> + if ((length - 1) % data_length) {
>> + success = false;
>> + att_ecode = 0;
>> + goto done;
>> + }
>> +
>> + for (offset = 1; offset < length; offset += data_length) {
>> + data = pdu + offset;
>> + if (!append_read_by_uuid_result(op, get_le16(data),
>> + data_length - 2, data + 2)) {
>> + success = false;
>> + att_ecode = 0;
>> + goto done;
>> + }
>> + }
>> +
>> + last_handle = get_le16(pdu + length - data_length);
>> + if (last_handle < op->end_handle) {
>> + uint8_t pdu[20];
>> +
>> + put_le16(last_handle + 1, pdu);
>> + put_le16(op->end_handle, pdu + 2);
>> +
>> + if (is_bluetooth_uuid(op->uuid)) {
>> + put_le16((op->uuid[2] << 8) + op->uuid[3], pdu + 4);
>> + /*
>> + * 2 octets - start handle
>> + * 2 octets - end handle
>> + * 2 octets - 16-bit uuid
>> + */
>> + length = 6;
>> + } else {
>> + bswap_128(&op->uuid, pdu + 4);
>> + /*
>> + * 2 octets - start handle
>> + * 2 octets - end handle
>> + * 16 octets - 128-bit uuid
>> + */
>> + length = 20;
>> + }
>> +
>> + if (!bt_att_send(op->client->att, BT_ATT_OP_READ_BY_TYPE_REQ,
>> + pdu, length, read_by_uuid_cb,
>> + read_by_uuid_op_ref(op),
>> + read_by_uuid_op_unref)) {
>> + read_by_uuid_op_unref(op);
>> + success = false;
>> + att_ecode = 0;
>> + goto done;
>> + }
>> +
>> + return;
>> + }
>> +
>> + success = true;
>> + att_ecode = 0;
>> +
>> +done:
>> + if (op->callback)
>> + op->callback(success, att_ecode, op->result_head,
>> + op->user_data);
>> +}
>> +
>> +bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
>> + uint16_t start_handle, uint16_t end_handle,
>> + const uint8_t uuid[BT_GATT_UUID_SIZE],
>> + bt_gatt_client_read_by_uuid_callback_t callback,
>> + void *user_data,
>> + bt_gatt_client_destroy_func_t destroy)
>> +{
>> + struct read_by_uuid_op *op;
>> + uint16_t length;
>> + uint8_t pdu[20];
>> +
>> + if (!client)
>> + return false;
>> +
>> + if (start_handle > end_handle || start_handle == 0x0000)
>> + return false;
>> +
>> + op = new0(struct read_by_uuid_op, 1);
>> + if (!op)
>> + return false;
>> +
>> + op->client = client;
>> + op->start_handle = start_handle;
>> + op->end_handle = end_handle;
>> + op->destroy = destroy;
>> + op->callback = callback;
>> + op->user_data = user_data;
>> + memcpy(&op->uuid, uuid, 16);
>> +
>> + /* Convert to 16-bit if it is Bluetooth UUID */
>> + if (is_bluetooth_uuid(uuid)) {
>> + put_le16((uuid[2] << 8) + uuid[3], pdu + 4);
>> + /*
>> + * 2 octets - start handle
>> + * 2 octets - end handle
>> + * 2 octets - 16-bit uuid
>> + */
>> + length = 6;
>> + } else {
>> + bswap_128(uuid, pdu + 4);
>> + /*
>> + * 2 octets - start handle
>> + * 2 octets - end handle
>> + * 16 octets - 128-bit uuid
>> + */
>> + length = 20;
>> + }
>> +
>> + put_le16(start_handle, pdu);
>> + put_le16(end_handle, pdu + 2);
>> +
>> + if (!bt_att_send(client->att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu,
>> + length, read_by_uuid_cb,
>> + read_by_uuid_op_ref(op),
>> + read_by_uuid_op_unref)) {
>> + free(op);
>> + return false;
>> + }
>> +
>> + return true;
>> +}
>> +
>> bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
>> uint16_t value_handle,
>> bool signed_write,
>> diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
>> index adccfc5..921ed75 100644
>> --- a/src/shared/gatt-client.h
>> +++ b/src/shared/gatt-client.h
>> @@ -145,6 +145,24 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
>> void *user_data,
>> bt_gatt_client_destroy_func_t destroy);
>>
>> +struct read_by_uuid_res;
>> +
>> +struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result);
>> +bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
>> + uint8_t *length, uint8_t **data);
>> +
>> +typedef void (*bt_gatt_client_read_by_uuid_callback_t)(bool success,
>> + uint8_t att_ecode,
>> + struct read_by_uuid_res *result,
>> + void *user_data);
>> +
>> +bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
>> + uint16_t start_handle, uint16_t end_handle,
>> + const uint8_t uuid[BT_GATT_UUID_SIZE],
>> + bt_gatt_client_read_by_uuid_callback_t callback,
>> + void *user_data,
>> + bt_gatt_client_destroy_func_t destroy);
>
> Isn't this essentially duplicating bt_gatt_service_iter_next_by_uuid?
> I mean we should be reading everything already but this doesn't seems
> to using the cache and in case it is just to fulfill a test perhaps it
> is better to do it in gatt-helpers so we keep bt_gatt_client API
> clean.
It is functionality of GATT client described in chapter 4.8.2 "Read
Using Characteristic UUID".
Difference is that client can read attributes by passing UUID, and
results can contain
different valu lengths. And client interprets results instead of gatt-helpers.
I implemented it here because all read/write operations on
characteristics and descriptors
are exposed in bt_gatt_client, and all discovery stuff is implemented
in gatt_helpers. But if
you don't agree I can change it.
>
>> bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
>> uint16_t value_handle,
>> bool signed_write,
>> --
>> 1.9.3
>>
>> --
>> 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
BR
Marcin
Hi Marcin,
On Thu, Nov 13, 2014 at 1:27 PM, Marcin Kraglak
<[email protected]> wrote:
> ---
> unit/test-gatt.c | 29 +++++++++++++++++++----------
> 1 file changed, 19 insertions(+), 10 deletions(-)
>
> diff --git a/unit/test-gatt.c b/unit/test-gatt.c
> index 39cdda3..8898762 100644
> --- a/unit/test-gatt.c
> +++ b/unit/test-gatt.c
> @@ -323,6 +323,18 @@ static void compare_chars(const bt_gatt_characteristic_t *a,
> compare_descs(&a->descs[i], &b->descs[i]);
> }
>
> +typedef void (*test_step_t)(struct context *context);
> +
> +struct test_step {
> + test_step_t func;
> + uint16_t handle;
> + uint16_t end_handle;
> + uint8_t uuid[16];
> + uint8_t expected_att_ecode;
> + const uint8_t *value;
> + uint16_t length;
> +};
> +
> static void client_ready_cb(bool success, uint8_t att_ecode, void *user_data)
> {
> struct context *context = user_data;
> @@ -357,6 +369,13 @@ static void client_ready_cb(bool success, uint8_t att_ecode, void *user_data)
>
> g_assert(!bt_gatt_service_iter_next(&iter, &service));
>
> + if (context->data->step) {
> + const struct test_step *step = context->data->step;
> +
> + step->func(context);
> + return;
> + }
> +
> context_quit(context);
> }
>
> @@ -453,16 +472,6 @@ static void execute_context(struct context *context)
> destroy_context(context);
> }
>
> -typedef void (*test_step_t)(struct context *context);
> -
> -struct test_step {
> - test_step_t func;
> - uint16_t handle;
> - uint8_t expected_att_ecode;
> - const uint8_t *value;
> - uint16_t length;
> -};
> -
> static void test_read_cb(bool success, uint8_t att_ecode,
> const uint8_t *value, uint16_t length,
> void *user_data)
> --
> 1.9.3
I went ahead and applied this one, next time please send fixes separately.
--
Luiz Augusto von Dentz
Hi Marcin,
On Thu, Nov 13, 2014 at 1:27 PM, Marcin Kraglak
<[email protected]> wrote:
> This is exposed to allow client reading characteristic values
> with known uuid.
> ---
> src/shared/gatt-client.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++
> src/shared/gatt-client.h | 18 ++++
> 2 files changed, 289 insertions(+)
>
> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
> index 30b271e..0493b05 100644
> --- a/src/shared/gatt-client.c
> +++ b/src/shared/gatt-client.c
> @@ -44,6 +44,11 @@
> #define GATT_SVC_UUID 0x1801
> #define SVC_CHNGD_UUID 0x2a05
>
> +static const uint8_t bt_base_uuid[16] = {
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
> + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
> +};
> +
> struct chrc_data {
> /* The public characteristic entry. */
> bt_gatt_characteristic_t chrc_external;
> @@ -122,6 +127,15 @@ struct bt_gatt_client {
> bool in_svc_chngd;
> };
>
> +static bool is_bluetooth_uuid(const uint8_t uuid[BT_GATT_UUID_SIZE])
> +{
> + if (memcmp(uuid, bt_base_uuid, 2) ||
> + memcmp(&uuid[4], &bt_base_uuid[4], 12))
> + return false;
> +
> + return true;
> +}
> +
Is this really different from uuid_cmp? Perhaps you use set uuid16
properly it should just do the same you are doing above.
> struct notify_data {
> struct bt_gatt_client *client;
> bool removed;
> @@ -2001,6 +2015,263 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
> return true;
> }
>
> +struct read_by_uuid_res {
> + uint16_t handle;
> + uint8_t length;
> + uint8_t *data;
> + struct read_by_uuid_res *next;
> +};
> +
> +bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
> + uint8_t *length, uint8_t **data)
> +{
> + if (!result)
> + return false;
> +
> + *length = result->length;
> + *handle = result->handle;
> + *data = result->data;
> +
> + return true;
> +}
> +
> +struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result)
> +{
> + if (!result)
> + return NULL;
> +
> + return result->next;
> +}
> +
> +struct read_by_uuid_op {
> + struct bt_gatt_client *client;
> + bt_gatt_client_read_by_uuid_callback_t callback;
> + int ref_count;
> + uint16_t start_handle;
> + uint16_t end_handle;
> + uint8_t uuid[BT_GATT_UUID_SIZE];
> + struct read_by_uuid_res *result_head;
> + struct read_by_uuid_res *result_tail;
> + void *user_data;
> + bt_gatt_client_destroy_func_t destroy;
> +};
Im not really sure this lists are really helping us, perhaps this is
the effect of using iterators in the API but that seems to create
duplicated code.
> +static bool append_read_by_uuid_result(struct read_by_uuid_op *op,
> + uint16_t handle, uint16_t length,
> + const uint8_t *data)
> +{
> + struct read_by_uuid_res *result;
> +
> + result = new0(struct read_by_uuid_res, 1);
> + if (!result)
> + return false;
> +
> + result->data = malloc(length);
> + if (!result->data) {
> + free(result);
> + return false;
> + }
> +
> + result->handle = handle;
> + result->length = length;
> + memcpy(result->data, data, length);
> +
> + if (op->result_head) {
> + op->result_tail->next = result;
> + op->result_tail = result;
> + } else {
> + op->result_head = op->result_tail = result;
> + }
> +
> + return true;
> +}
> +
> +static void destroy_read_by_uuid_result(void *data)
> +{
> + struct read_by_uuid_res *result = data;
> +
> + free(result->data);
> + free(result);
> +}
> +
> +static struct read_by_uuid_op *read_by_uuid_op_ref(struct read_by_uuid_op *op)
> +{
> + __sync_fetch_and_add(&op->ref_count, 1);
> +
> + return op;
> +}
> +
> +static void read_by_uuid_op_unref(void *data)
> +{
> + struct read_by_uuid_res *next, *temp;
> + struct read_by_uuid_op *op = data;
> +
> + if (__sync_sub_and_fetch(&op->ref_count, 1))
> + return;
> +
> + if (op->destroy)
> + op->destroy(op->user_data);
> +
> + for (temp = op->result_head; temp; temp = next) {
> + next = temp->next;
> + destroy_read_by_uuid_result(temp);
> + }
> +
> + free(op);
> +}
> +
> +static void read_by_uuid_cb(uint8_t opcode, const void *pdu,
> + uint16_t length, void *user_data)
> +{
> + struct read_by_uuid_op *op = user_data;
> + size_t data_length;
> + uint8_t att_ecode;
> + uint16_t offset, last_handle;
> + const uint8_t *data;
> + bool success;
> +
> + if (opcode == BT_ATT_OP_ERROR_RSP) {
> + att_ecode = process_error(pdu, length);
> + if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND)
> + success = true;
> + else
> + success = false;
> +
> + goto done;
> + }
> +
> + if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || (!pdu && length)) {
> + success = false;
> + att_ecode = 0;
> + goto done;
> + }
> +
> + data_length = ((const uint8_t *) pdu)[0];
> + if ((length - 1) % data_length) {
> + success = false;
> + att_ecode = 0;
> + goto done;
> + }
> +
> + for (offset = 1; offset < length; offset += data_length) {
> + data = pdu + offset;
> + if (!append_read_by_uuid_result(op, get_le16(data),
> + data_length - 2, data + 2)) {
> + success = false;
> + att_ecode = 0;
> + goto done;
> + }
> + }
> +
> + last_handle = get_le16(pdu + length - data_length);
> + if (last_handle < op->end_handle) {
> + uint8_t pdu[20];
> +
> + put_le16(last_handle + 1, pdu);
> + put_le16(op->end_handle, pdu + 2);
> +
> + if (is_bluetooth_uuid(op->uuid)) {
> + put_le16((op->uuid[2] << 8) + op->uuid[3], pdu + 4);
> + /*
> + * 2 octets - start handle
> + * 2 octets - end handle
> + * 2 octets - 16-bit uuid
> + */
> + length = 6;
> + } else {
> + bswap_128(&op->uuid, pdu + 4);
> + /*
> + * 2 octets - start handle
> + * 2 octets - end handle
> + * 16 octets - 128-bit uuid
> + */
> + length = 20;
> + }
> +
> + if (!bt_att_send(op->client->att, BT_ATT_OP_READ_BY_TYPE_REQ,
> + pdu, length, read_by_uuid_cb,
> + read_by_uuid_op_ref(op),
> + read_by_uuid_op_unref)) {
> + read_by_uuid_op_unref(op);
> + success = false;
> + att_ecode = 0;
> + goto done;
> + }
> +
> + return;
> + }
> +
> + success = true;
> + att_ecode = 0;
> +
> +done:
> + if (op->callback)
> + op->callback(success, att_ecode, op->result_head,
> + op->user_data);
> +}
> +
> +bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
> + uint16_t start_handle, uint16_t end_handle,
> + const uint8_t uuid[BT_GATT_UUID_SIZE],
> + bt_gatt_client_read_by_uuid_callback_t callback,
> + void *user_data,
> + bt_gatt_client_destroy_func_t destroy)
> +{
> + struct read_by_uuid_op *op;
> + uint16_t length;
> + uint8_t pdu[20];
> +
> + if (!client)
> + return false;
> +
> + if (start_handle > end_handle || start_handle == 0x0000)
> + return false;
> +
> + op = new0(struct read_by_uuid_op, 1);
> + if (!op)
> + return false;
> +
> + op->client = client;
> + op->start_handle = start_handle;
> + op->end_handle = end_handle;
> + op->destroy = destroy;
> + op->callback = callback;
> + op->user_data = user_data;
> + memcpy(&op->uuid, uuid, 16);
> +
> + /* Convert to 16-bit if it is Bluetooth UUID */
> + if (is_bluetooth_uuid(uuid)) {
> + put_le16((uuid[2] << 8) + uuid[3], pdu + 4);
> + /*
> + * 2 octets - start handle
> + * 2 octets - end handle
> + * 2 octets - 16-bit uuid
> + */
> + length = 6;
> + } else {
> + bswap_128(uuid, pdu + 4);
> + /*
> + * 2 octets - start handle
> + * 2 octets - end handle
> + * 16 octets - 128-bit uuid
> + */
> + length = 20;
> + }
> +
> + put_le16(start_handle, pdu);
> + put_le16(end_handle, pdu + 2);
> +
> + if (!bt_att_send(client->att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu,
> + length, read_by_uuid_cb,
> + read_by_uuid_op_ref(op),
> + read_by_uuid_op_unref)) {
> + free(op);
> + return false;
> + }
> +
> + return true;
> +}
> +
> bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
> uint16_t value_handle,
> bool signed_write,
> diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
> index adccfc5..921ed75 100644
> --- a/src/shared/gatt-client.h
> +++ b/src/shared/gatt-client.h
> @@ -145,6 +145,24 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
> void *user_data,
> bt_gatt_client_destroy_func_t destroy);
>
> +struct read_by_uuid_res;
> +
> +struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result);
> +bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
> + uint8_t *length, uint8_t **data);
> +
> +typedef void (*bt_gatt_client_read_by_uuid_callback_t)(bool success,
> + uint8_t att_ecode,
> + struct read_by_uuid_res *result,
> + void *user_data);
> +
> +bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
> + uint16_t start_handle, uint16_t end_handle,
> + const uint8_t uuid[BT_GATT_UUID_SIZE],
> + bt_gatt_client_read_by_uuid_callback_t callback,
> + void *user_data,
> + bt_gatt_client_destroy_func_t destroy);
Isn't this essentially duplicating bt_gatt_service_iter_next_by_uuid?
I mean we should be reading everything already but this doesn't seems
to using the cache and in case it is just to fulfill a test perhaps it
is better to do it in gatt-helpers so we keep bt_gatt_client API
clean.
> bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
> uint16_t value_handle,
> bool signed_write,
> --
> 1.9.3
>
> --
> 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
Verify Generic Attribute Profile client behavior when the Read Using
Characteristic UUID procedure fails due to insufficient encryption
key size.
---
unit/test-gatt.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index a2607ad..79deedf 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -798,6 +798,15 @@ const struct test_step test_read_by_uuid_6 = {
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}
};
+const struct test_step test_read_by_uuid_7 = {
+ .handle = 0x0001,
+ .end_handle = 0xffff,
+ .func = test_read_by_uuid,
+ .expected_att_ecode = 0x0c,
+ .uuid = {0x00, 0x00, 0x2b, 0x0d, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}
+};
+
static void test_client(gconstpointer data)
{
struct context *context = create_context(512, data);
@@ -1026,5 +1035,11 @@ int main(int argc, char *argv[])
raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2b),
raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x05));
+ define_test_client("/TP/GAR/CL/BI-11-C", test_client, service_data_2,
+ &test_read_by_uuid_7,
+ SERVICE_DATA_2_PDU,
+ raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2b),
+ raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x0c));
+
return g_test_run();
}
--
1.9.3
Verify Generic Attribute Profile client behavior when the Read Using
Characteristic UUID procedure fails due to insufficient authentication
---
unit/test-gatt.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 952f74e..a2607ad 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -789,6 +789,15 @@ const struct test_step test_read_by_uuid_5 = {
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}
};
+const struct test_step test_read_by_uuid_6 = {
+ .handle = 0x0001,
+ .end_handle = 0xffff,
+ .func = test_read_by_uuid,
+ .expected_att_ecode = 0x05,
+ .uuid = {0x00, 0x00, 0x2b, 0x0d, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}
+};
+
static void test_client(gconstpointer data)
{
struct context *context = create_context(512, data);
@@ -1011,5 +1020,11 @@ int main(int argc, char *argv[])
raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2b),
raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x08));
+ define_test_client("/TP/GAR/CL/BI-10-C", test_client, service_data_2,
+ &test_read_by_uuid_6,
+ SERVICE_DATA_2_PDU,
+ raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2b),
+ raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x05));
+
return g_test_run();
}
--
1.9.3
Verify Generic Attribute Profile client behavior when the Read
Using Characteristic UUID procedure fails due to read not permitted.
---
unit/test-gatt.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index a0f40cc..d86a77b 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -762,6 +762,15 @@ const struct test_step test_read_by_uuid_2 = {
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e}
};
+const struct test_step test_read_by_uuid_3 = {
+ .handle = 0x0001,
+ .end_handle = 0xffff,
+ .func = test_read_by_uuid,
+ .expected_att_ecode = 0x02,
+ .uuid = {0x00, 0x00, 0x2b, 0x0d, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}
+};
+
static void test_client(gconstpointer data)
{
struct context *context = create_context(512, data);
@@ -966,5 +975,11 @@ int main(int argc, char *argv[])
0x01, 0x00, 0x00),
raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x0a));
+ define_test_client("/TP/GAR/CL/BI-06-C", test_client, service_data_2,
+ &test_read_by_uuid_3,
+ SERVICE_DATA_2_PDU,
+ raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2b),
+ raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x02));
+
return g_test_run();
}
--
1.9.3
Verify Generic Attribute Profile client behavior when the Read Using
Characteristic UUID procedure fails due to insufficient authorization.
---
unit/test-gatt.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 82c0886..952f74e 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -780,6 +780,15 @@ const struct test_step test_read_by_uuid_4 = {
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}
};
+const struct test_step test_read_by_uuid_5 = {
+ .handle = 0x0001,
+ .end_handle = 0xffff,
+ .func = test_read_by_uuid,
+ .expected_att_ecode = 0x08,
+ .uuid = {0x00, 0x00, 0x2b, 0x0d, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}
+};
+
static void test_client(gconstpointer data)
{
struct context *context = create_context(512, data);
@@ -996,5 +1005,11 @@ int main(int argc, char *argv[])
raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2b),
raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x0a));
+ define_test_client("/TP/GAR/CL/BI-09-C", test_client, service_data_2,
+ &test_read_by_uuid_5,
+ SERVICE_DATA_2_PDU,
+ raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2b),
+ raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x08));
+
return g_test_run();
}
--
1.9.3
Verify Generic Attribute Profile client behavior when
the Read Using Characteristic UUID procedure fails due to
attribute not found.
---
unit/test-gatt.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index d86a77b..82c0886 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -771,6 +771,15 @@ const struct test_step test_read_by_uuid_3 = {
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}
};
+const struct test_step test_read_by_uuid_4 = {
+ .handle = 0x0001,
+ .end_handle = 0xffff,
+ .func = test_read_by_uuid,
+ .expected_att_ecode = 0x0a,
+ .uuid = {0x00, 0x00, 0x2b, 0x0d, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}
+};
+
static void test_client(gconstpointer data)
{
struct context *context = create_context(512, data);
@@ -981,5 +990,11 @@ int main(int argc, char *argv[])
raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2b),
raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x02));
+ define_test_client("/TP/GAR/CL/BI-07-C", test_client, service_data_2,
+ &test_read_by_uuid_4,
+ SERVICE_DATA_2_PDU,
+ raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2b),
+ raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x0a));
+
return g_test_run();
}
--
1.9.3
Verify that a Generic Attribute Profile client can read
a Characteristic Value selected by UUID using a 128-bit UUID.
---
unit/test-gatt.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 72 insertions(+)
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 6ffcc2f..a0f40cc 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -179,6 +179,32 @@ struct context {
raw_pdu(0x04, 0x0b, 0x00, 0x0b, 0x00), \
raw_pdu(0x05, 0x01, 0x0b, 0x00, 0x02, 0x29)
+#define SERVICE_DATA_3_PDU \
+ raw_pdu(0x02, 0x00, 0x02), \
+ raw_pdu(0x03, 0x00, 0x02), \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x07, 0x00, 0x01, 0x18),\
+ raw_pdu(0x10, 0x08, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x01, 0x10, 0x08, 0x00, 0x0a), \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28), \
+ raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x01, 0x00, 0x07, 0x00, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x01, 0x00, 0x07, 0x00, 0x03, 0x28), \
+ raw_pdu(0x09, 0x15, 0x02, 0x00, 0x02, 0x03, 0x00, 0x0e, \
+ 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, \
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, \
+ 0x01, 0x00, 0x00), \
+ raw_pdu(0x08, 0x03, 0x00, 0x07, 0x00, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x05, 0x00, 0x02, 0x06, 0x00, 0x00, \
+ 0x2a), \
+ raw_pdu(0x08, 0x06, 0x00, 0x07, 0x00, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x06, 0x00, 0x0a), \
+ raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00), \
+ raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29), \
+ raw_pdu(0x04, 0x07, 0x00, 0x07, 0x00), \
+ raw_pdu(0x05, 0x01, 0x07, 0x00, 0x03, 0x29)
+
static bt_uuid_t uuid_16 = {
.type = BT_UUID16,
.value.u16 = 0x1800
@@ -309,9 +335,22 @@ const bt_gatt_characteristic_t characteristic_4 = {
.num_descs = 1
};
+const bt_gatt_characteristic_t characteristic_5 = {
+ .start_handle = 0x0002,
+ .end_handle = 0x0004,
+ .value_handle = 0x0003,
+ .properties = 0x02,
+ .uuid = {0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e},
+ .descs = &descriptor_1,
+ .num_descs = 1
+};
+
const bt_gatt_characteristic_t *characteristics_3[] = {&characteristic_1,
&characteristic_3};
const bt_gatt_characteristic_t *characteristics_4[] = {&characteristic_4};
+const bt_gatt_characteristic_t *characteristics_5[] = {&characteristic_5,
+ &characteristic_3};
const struct gatt_service gatt_service_3 = {
.service = &service_3,
@@ -325,9 +364,17 @@ const struct gatt_service gatt_service_4 = {
.chars = characteristics_4
};
+const struct gatt_service gatt_service_5 = {
+ .service = &service_3,
+ .num_chars = sizeof(characteristics_5) / sizeof(characteristics_5[0]),
+ .chars = characteristics_5
+};
+
const struct gatt_service *service_data_2[] = {&gatt_service_3,
&gatt_service_4};
+const struct gatt_service *service_data_3[] = {&gatt_service_5};
+
static void test_debug(const char *str, void *user_data)
{
const char *prefix = user_data;
@@ -704,6 +751,17 @@ const struct test_step test_read_by_uuid_1 = {
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}
};
+const struct test_step test_read_by_uuid_2 = {
+ .handle = 0x0001,
+ .end_handle = 0xffff,
+ .func = test_read_by_uuid,
+ .expected_att_ecode = 0x0a,
+ .value = read_data_1,
+ .length = 0x03,
+ .uuid = {0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e}
+};
+
static void test_client(gconstpointer data)
{
struct context *context = create_context(512, data);
@@ -894,5 +952,19 @@ int main(int argc, char *argv[])
raw_pdu(0x08, 0x0b, 0x00, 0xff, 0xff, 0x0d, 0x2b),
raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x0a));
+ define_test_client("/TP/GAR/CL/BV-03-C-2", test_client, service_data_3,
+ &test_read_by_uuid_2,
+ SERVICE_DATA_3_PDU,
+ raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0e,
+ 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
+ 0x01, 0x00, 0x00),
+ raw_pdu(0x09, 0x05, 0x0a, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x08, 0x0b, 0x00, 0xff, 0xff, 0x0e,
+ 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
+ 0x01, 0x00, 0x00),
+ raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x0a));
+
return g_test_run();
}
--
1.9.3
Verify that a Generic Attribute Profile client can read a
Characteristic Value selected by UUID using a 16-bit UUID.
---
unit/test-gatt.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 157 insertions(+)
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 276857b..6ffcc2f 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -144,6 +144,41 @@ struct context {
raw_pdu(0x04, 0x08, 0x00, 0x08, 0x00), \
raw_pdu(0x05, 0x01, 0x08, 0x00, 0x01, 0x29)
+#define SERVICE_DATA_2_PDU \
+ raw_pdu(0x02, 0x00, 0x02), \
+ raw_pdu(0x03, 0x00, 0x02), \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x07, 0x00, 0x01, 0x18),\
+ raw_pdu(0x10, 0x08, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x18),\
+ raw_pdu(0x10, 0x0c, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x01, 0x10, 0x0c, 0x00, 0x0a), \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28), \
+ raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x01, 0x00, 0x07, 0x00, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x08, 0x00, 0x0b, 0x00, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x01, 0x00, 0x07, 0x00, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00, \
+ 0x2a), \
+ raw_pdu(0x08, 0x03, 0x00, 0x07, 0x00, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x05, 0x00, 0x02, 0x06, 0x00, 0x00, \
+ 0x2a), \
+ raw_pdu(0x08, 0x06, 0x00, 0x07, 0x00, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x06, 0x00, 0x0a), \
+ raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00), \
+ raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29), \
+ raw_pdu(0x04, 0x07, 0x00, 0x07, 0x00), \
+ raw_pdu(0x05, 0x01, 0x07, 0x00, 0x03, 0x29), \
+ raw_pdu(0x08, 0x08, 0x00, 0x0b, 0x00, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x09, 0x00, 0x02, 0x0a, 0x00, 0x0d, \
+ 0x2b), \
+ raw_pdu(0x08, 0x0a, 0x00, 0x0b, 0x00, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x0a, 0x00, 0x0a), \
+ raw_pdu(0x04, 0x0b, 0x00, 0x0b, 0x00), \
+ raw_pdu(0x05, 0x01, 0x0b, 0x00, 0x02, 0x29)
+
static bt_uuid_t uuid_16 = {
.type = BT_UUID16,
.value.u16 = 0x1800
@@ -223,6 +258,76 @@ const struct gatt_service gatt_service_2 = {
const struct gatt_service *service_data_1[] = {&gatt_service_1,
&gatt_service_2};
+const bt_gatt_service_t service_3 = {
+ .primary = true,
+ .start_handle = 0x0001,
+ .end_handle = 0x0007,
+ .uuid = {0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}
+};
+
+const bt_gatt_service_t service_4 = {
+ .primary = true,
+ .start_handle = 0x0008,
+ .end_handle = 0x000b,
+ .uuid = {0x00, 0x00, 0x18, 0x0d, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}
+};
+
+const bt_gatt_descriptor_t descriptor_3 = {
+ .handle = 0x0007,
+ .uuid = {0x00, 0x00, 0x29, 0x03, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}
+};
+
+
+const bt_gatt_characteristic_t characteristic_3 = {
+ .start_handle = 0x0005,
+ .end_handle = 0x0007,
+ .value_handle = 0x0006,
+ .properties = 0x02,
+ .uuid = {0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb},
+ .descs = &descriptor_3,
+ .num_descs = 1
+};
+
+const bt_gatt_descriptor_t descriptor_4 = {
+ .handle = 0x000b,
+ .uuid = {0x00, 0x00, 0x29, 0x02, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}
+};
+
+const bt_gatt_characteristic_t characteristic_4 = {
+ .start_handle = 0x0009,
+ .end_handle = 0x000b,
+ .value_handle = 0x000a,
+ .properties = 0x02,
+ .uuid = {0x00, 0x00, 0x2b, 0x0d, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb},
+ .descs = &descriptor_4,
+ .num_descs = 1
+};
+
+const bt_gatt_characteristic_t *characteristics_3[] = {&characteristic_1,
+ &characteristic_3};
+const bt_gatt_characteristic_t *characteristics_4[] = {&characteristic_4};
+
+const struct gatt_service gatt_service_3 = {
+ .service = &service_3,
+ .num_chars = sizeof(characteristics_3) / sizeof(characteristics_3[0]),
+ .chars = characteristics_3
+};
+
+const struct gatt_service gatt_service_4 = {
+ .service = &service_4,
+ .num_chars = sizeof(characteristics_4) / sizeof(characteristics_4[0]),
+ .chars = characteristics_4
+};
+
+const struct gatt_service *service_data_2[] = {&gatt_service_3,
+ &gatt_service_4};
+
static void test_debug(const char *str, void *user_data)
{
const char *prefix = user_data;
@@ -555,6 +660,50 @@ const struct test_step test_read_4 = {
.expected_att_ecode = 0x08,
};
+static void read_by_uuid_cb(bool success, uint8_t att_ecode,
+ struct read_by_uuid_res *result,
+ void *user_data)
+{
+ struct context *context = user_data;
+ const struct test_step *step = context->data->step;
+
+ g_assert(att_ecode == step->expected_att_ecode);
+
+ if (success && step->value) {
+ uint16_t handle;
+ uint8_t length;
+ uint8_t *value;
+
+ read_by_uuid_res_get(result, &handle, &length, &value);
+ g_assert(length == step->length);
+ g_assert(memcmp(value, step->value, length) == 0);
+ }
+
+ g_assert(read_by_uuid_res_next(result) == NULL);
+
+ context_quit(context);
+}
+
+static void test_read_by_uuid(struct context *context)
+{
+ const struct test_step *step = context->data->step;
+
+ g_assert(bt_gatt_client_read_by_uuid(context->client, step->handle,
+ step->end_handle, step->uuid, read_by_uuid_cb, context,
+ NULL));
+}
+
+const struct test_step test_read_by_uuid_1 = {
+ .handle = 0x0001,
+ .end_handle = 0xffff,
+ .func = test_read_by_uuid,
+ .expected_att_ecode = 0x0a,
+ .value = read_data_1,
+ .length = 0x03,
+ .uuid = {0x00, 0x00, 0x2b, 0x0d, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}
+};
+
static void test_client(gconstpointer data)
{
struct context *context = create_context(512, data);
@@ -737,5 +886,13 @@ int main(int argc, char *argv[])
raw_pdu(0x0a, 0x03, 0x00),
raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x08));
+ define_test_client("/TP/GAR/CL/BV-03-C", test_client, service_data_2,
+ &test_read_by_uuid_1,
+ SERVICE_DATA_2_PDU,
+ raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2b),
+ raw_pdu(0x09, 0x05, 0x0a, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x08, 0x0b, 0x00, 0xff, 0xff, 0x0d, 0x2b),
+ raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x0a));
+
return g_test_run();
}
--
1.9.3
It is used to read characteristic values with known UUID.
---
tools/btgatt-client.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+)
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 7a1204f..f069189 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -401,6 +401,89 @@ static void cmd_services(struct client *cli, char *cmd_str)
services_usage();
}
+static void read_by_uuid_usage(void)
+{
+ printf("Usage: read-by-uuid <start_handle> <end_handle> <uuid>\n");
+}
+
+static void read_by_uuid_cb(bool success, uint8_t att_ecode,
+ struct read_by_uuid_res *result, void *user_data)
+{
+ uint16_t handle;
+ uint8_t length;
+ uint8_t *data;
+ int i;
+
+ if (!success) {
+ PRLOG("\nRead by UUID request failed: 0x%02x\n", att_ecode);
+ return;
+ }
+
+ if (!result) {
+ PRLOG("\nNo attribute found\n");
+ return;
+ }
+
+ for (; result; result = read_by_uuid_res_next(result)) {
+ read_by_uuid_res_get(result, &handle, &length, &data);
+ printf("\nHandle 0x%04X Read value", handle);
+
+ if (length == 0) {
+ PRLOG(": 0 bytes\n");
+ return;
+ }
+
+ printf(" (%u bytes): ", length);
+
+ for (i = 0; i < length; i++)
+ printf("%02x ", data[i]);
+
+ PRLOG("\n");
+ }
+}
+
+static void cmd_read_by_uuid(struct client *cli, char *cmd_str)
+{
+ char *argv[3];
+ int argc = 0;
+ uint16_t start_handle, end_handle;
+ bt_uuid_t tmp, uuid;
+ char *endptr = NULL;
+
+ if (!bt_gatt_client_is_ready(cli->gatt)) {
+ printf("GATT client not initialized\n");
+ return;
+ }
+
+ if (!parse_args(cmd_str, 3, argv, &argc) || argc != 3) {
+ read_by_uuid_usage();
+ return;
+ }
+
+ start_handle = strtol(argv[0], &endptr, 0);
+ if (!endptr || *endptr != '\0' || !start_handle) {
+ printf("Invalid value handle: %s\n", argv[0]);
+ return;
+ }
+
+ end_handle = strtol(argv[1], &endptr, 0);
+ if (!endptr || *endptr != '\0' || !end_handle) {
+ printf("Invalid value handle: %s\n", argv[1]);
+ return;
+ }
+
+ if (bt_string_to_uuid(&tmp, argv[2]) < 0) {
+ printf("Invalid UUID: %s\n", argv[2]);
+ return;
+ }
+
+ bt_uuid_to_uuid128(&tmp, &uuid);
+
+ if (!bt_gatt_client_read_by_uuid(cli->gatt, start_handle, end_handle,
+ uuid.value.u128.data, read_by_uuid_cb, NULL, NULL))
+ printf("Failed to initiate read value procedure\n");
+}
+
static void read_value_usage(void)
{
printf("Usage: read-value <value_handle>\n");
@@ -873,6 +956,7 @@ static struct {
"\tRead a characteristic or descriptor value" },
{ "read-long-value", cmd_read_long_value,
"\tRead a long characteristic or desctriptor value" },
+ { "read-by-uuid", cmd_read_by_uuid, "\tRead by UUID" },
{ "write-value", cmd_write_value,
"\tWrite a characteristic or descriptor value" },
{ "write-long-value", cmd_write_long_value,
--
1.9.3
This is exposed to allow client reading characteristic values
with known uuid.
---
src/shared/gatt-client.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++
src/shared/gatt-client.h | 18 ++++
2 files changed, 289 insertions(+)
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 30b271e..0493b05 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -44,6 +44,11 @@
#define GATT_SVC_UUID 0x1801
#define SVC_CHNGD_UUID 0x2a05
+static const uint8_t bt_base_uuid[16] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+};
+
struct chrc_data {
/* The public characteristic entry. */
bt_gatt_characteristic_t chrc_external;
@@ -122,6 +127,15 @@ struct bt_gatt_client {
bool in_svc_chngd;
};
+static bool is_bluetooth_uuid(const uint8_t uuid[BT_GATT_UUID_SIZE])
+{
+ if (memcmp(uuid, bt_base_uuid, 2) ||
+ memcmp(&uuid[4], &bt_base_uuid[4], 12))
+ return false;
+
+ return true;
+}
+
struct notify_data {
struct bt_gatt_client *client;
bool removed;
@@ -2001,6 +2015,263 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
return true;
}
+struct read_by_uuid_res {
+ uint16_t handle;
+ uint8_t length;
+ uint8_t *data;
+ struct read_by_uuid_res *next;
+};
+
+bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
+ uint8_t *length, uint8_t **data)
+{
+ if (!result)
+ return false;
+
+ *length = result->length;
+ *handle = result->handle;
+ *data = result->data;
+
+ return true;
+}
+
+struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result)
+{
+ if (!result)
+ return NULL;
+
+ return result->next;
+}
+
+struct read_by_uuid_op {
+ struct bt_gatt_client *client;
+ bt_gatt_client_read_by_uuid_callback_t callback;
+ int ref_count;
+ uint16_t start_handle;
+ uint16_t end_handle;
+ uint8_t uuid[BT_GATT_UUID_SIZE];
+ struct read_by_uuid_res *result_head;
+ struct read_by_uuid_res *result_tail;
+ void *user_data;
+ bt_gatt_client_destroy_func_t destroy;
+};
+
+static bool append_read_by_uuid_result(struct read_by_uuid_op *op,
+ uint16_t handle, uint16_t length,
+ const uint8_t *data)
+{
+ struct read_by_uuid_res *result;
+
+ result = new0(struct read_by_uuid_res, 1);
+ if (!result)
+ return false;
+
+ result->data = malloc(length);
+ if (!result->data) {
+ free(result);
+ return false;
+ }
+
+ result->handle = handle;
+ result->length = length;
+ memcpy(result->data, data, length);
+
+ if (op->result_head) {
+ op->result_tail->next = result;
+ op->result_tail = result;
+ } else {
+ op->result_head = op->result_tail = result;
+ }
+
+ return true;
+}
+
+static void destroy_read_by_uuid_result(void *data)
+{
+ struct read_by_uuid_res *result = data;
+
+ free(result->data);
+ free(result);
+}
+
+static struct read_by_uuid_op *read_by_uuid_op_ref(struct read_by_uuid_op *op)
+{
+ __sync_fetch_and_add(&op->ref_count, 1);
+
+ return op;
+}
+
+static void read_by_uuid_op_unref(void *data)
+{
+ struct read_by_uuid_res *next, *temp;
+ struct read_by_uuid_op *op = data;
+
+ if (__sync_sub_and_fetch(&op->ref_count, 1))
+ return;
+
+ if (op->destroy)
+ op->destroy(op->user_data);
+
+ for (temp = op->result_head; temp; temp = next) {
+ next = temp->next;
+ destroy_read_by_uuid_result(temp);
+ }
+
+ free(op);
+}
+
+static void read_by_uuid_cb(uint8_t opcode, const void *pdu,
+ uint16_t length, void *user_data)
+{
+ struct read_by_uuid_op *op = user_data;
+ size_t data_length;
+ uint8_t att_ecode;
+ uint16_t offset, last_handle;
+ const uint8_t *data;
+ bool success;
+
+ if (opcode == BT_ATT_OP_ERROR_RSP) {
+ att_ecode = process_error(pdu, length);
+ if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND)
+ success = true;
+ else
+ success = false;
+
+ goto done;
+ }
+
+ if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || (!pdu && length)) {
+ success = false;
+ att_ecode = 0;
+ goto done;
+ }
+
+ data_length = ((const uint8_t *) pdu)[0];
+ if ((length - 1) % data_length) {
+ success = false;
+ att_ecode = 0;
+ goto done;
+ }
+
+ for (offset = 1; offset < length; offset += data_length) {
+ data = pdu + offset;
+ if (!append_read_by_uuid_result(op, get_le16(data),
+ data_length - 2, data + 2)) {
+ success = false;
+ att_ecode = 0;
+ goto done;
+ }
+ }
+
+ last_handle = get_le16(pdu + length - data_length);
+ if (last_handle < op->end_handle) {
+ uint8_t pdu[20];
+
+ put_le16(last_handle + 1, pdu);
+ put_le16(op->end_handle, pdu + 2);
+
+ if (is_bluetooth_uuid(op->uuid)) {
+ put_le16((op->uuid[2] << 8) + op->uuid[3], pdu + 4);
+ /*
+ * 2 octets - start handle
+ * 2 octets - end handle
+ * 2 octets - 16-bit uuid
+ */
+ length = 6;
+ } else {
+ bswap_128(&op->uuid, pdu + 4);
+ /*
+ * 2 octets - start handle
+ * 2 octets - end handle
+ * 16 octets - 128-bit uuid
+ */
+ length = 20;
+ }
+
+ if (!bt_att_send(op->client->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+ pdu, length, read_by_uuid_cb,
+ read_by_uuid_op_ref(op),
+ read_by_uuid_op_unref)) {
+ read_by_uuid_op_unref(op);
+ success = false;
+ att_ecode = 0;
+ goto done;
+ }
+
+ return;
+ }
+
+ success = true;
+ att_ecode = 0;
+
+done:
+ if (op->callback)
+ op->callback(success, att_ecode, op->result_head,
+ op->user_data);
+}
+
+bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
+ uint16_t start_handle, uint16_t end_handle,
+ const uint8_t uuid[BT_GATT_UUID_SIZE],
+ bt_gatt_client_read_by_uuid_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy)
+{
+ struct read_by_uuid_op *op;
+ uint16_t length;
+ uint8_t pdu[20];
+
+ if (!client)
+ return false;
+
+ if (start_handle > end_handle || start_handle == 0x0000)
+ return false;
+
+ op = new0(struct read_by_uuid_op, 1);
+ if (!op)
+ return false;
+
+ op->client = client;
+ op->start_handle = start_handle;
+ op->end_handle = end_handle;
+ op->destroy = destroy;
+ op->callback = callback;
+ op->user_data = user_data;
+ memcpy(&op->uuid, uuid, 16);
+
+ /* Convert to 16-bit if it is Bluetooth UUID */
+ if (is_bluetooth_uuid(uuid)) {
+ put_le16((uuid[2] << 8) + uuid[3], pdu + 4);
+ /*
+ * 2 octets - start handle
+ * 2 octets - end handle
+ * 2 octets - 16-bit uuid
+ */
+ length = 6;
+ } else {
+ bswap_128(uuid, pdu + 4);
+ /*
+ * 2 octets - start handle
+ * 2 octets - end handle
+ * 16 octets - 128-bit uuid
+ */
+ length = 20;
+ }
+
+ put_le16(start_handle, pdu);
+ put_le16(end_handle, pdu + 2);
+
+ if (!bt_att_send(client->att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu,
+ length, read_by_uuid_cb,
+ read_by_uuid_op_ref(op),
+ read_by_uuid_op_unref)) {
+ free(op);
+ return false;
+ }
+
+ return true;
+}
+
bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
uint16_t value_handle,
bool signed_write,
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index adccfc5..921ed75 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -145,6 +145,24 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
+struct read_by_uuid_res;
+
+struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result);
+bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
+ uint8_t *length, uint8_t **data);
+
+typedef void (*bt_gatt_client_read_by_uuid_callback_t)(bool success,
+ uint8_t att_ecode,
+ struct read_by_uuid_res *result,
+ void *user_data);
+
+bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
+ uint16_t start_handle, uint16_t end_handle,
+ const uint8_t uuid[BT_GATT_UUID_SIZE],
+ bt_gatt_client_read_by_uuid_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy);
+
bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
uint16_t value_handle,
bool signed_write,
--
1.9.3
---
unit/test-gatt.c | 146 +++++++++++++------------------------------------------
1 file changed, 34 insertions(+), 112 deletions(-)
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 8898762..276857b 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -114,6 +114,36 @@ struct context {
#define define_test_client(name, function, bt_services, test_step, args...)\
define_test(name, function, CLIENT, NULL, bt_services, test_step, args)
+#define SERVICE_DATA_1_PDU \
+ raw_pdu(0x02, 0x00, 0x02), \
+ raw_pdu(0x03, 0x00, 0x02), \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),\
+ raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x05, 0x00, 0x08, 0x00, 0x0d, 0x18),\
+ raw_pdu(0x10, 0x09, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x01, 0x10, 0x09, 0x00, 0x0a), \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28), \
+ raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00, \
+ 0x2a), \
+ raw_pdu(0x08, 0x03, 0x00, 0x04, 0x00, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a), \
+ raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00), \
+ raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29), \
+ raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x06, 0x00, 0x02, 0x07, 0x00, 0x29, \
+ 0x2a), \
+ raw_pdu(0x08, 0x07, 0x00, 0x08, 0x00, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x07, 0x00, 0x0a), \
+ raw_pdu(0x04, 0x08, 0x00, 0x08, 0x00), \
+ raw_pdu(0x05, 0x01, 0x08, 0x00, 0x01, 0x29)
+
static bt_uuid_t uuid_16 = {
.type = BT_UUID16,
.value.u16 = 0x1800
@@ -685,133 +715,25 @@ int main(int argc, char *argv[])
define_test_client("/TP/GAR/CL/BV-01-C", test_client, service_data_1,
&test_read_1,
- raw_pdu(0x02, 0x00, 0x02),
- raw_pdu(0x03, 0x00, 0x02),
- raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
- raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),
- raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28),
- raw_pdu(0x11, 0x06, 0x05, 0x00, 0x08, 0x00, 0x0d, 0x18),
- raw_pdu(0x10, 0x09, 0x00, 0xff, 0xff, 0x00, 0x28),
- raw_pdu(0x01, 0x10, 0x09, 0x00, 0x0a),
- raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28),
- raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a),
- raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28),
- raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
- raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x02, 0x28),
- raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a),
- raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28),
- raw_pdu(0x09, 0x07, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00,
- 0x2a),
- raw_pdu(0x08, 0x03, 0x00, 0x04, 0x00, 0x03, 0x28),
- raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a),
- raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00),
- raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29),
- raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x03, 0x28),
- raw_pdu(0x09, 0x07, 0x06, 0x00, 0x02, 0x07, 0x00, 0x29,
- 0x2a),
- raw_pdu(0x08, 0x07, 0x00, 0x08, 0x00, 0x03, 0x28),
- raw_pdu(0x01, 0x08, 0x07, 0x00, 0x0a),
- raw_pdu(0x04, 0x08, 0x00, 0x08, 0x00),
- raw_pdu(0x05, 0x01, 0x08, 0x00, 0x01, 0x29),
+ SERVICE_DATA_1_PDU,
raw_pdu(0x0a, 0x03, 0x00),
raw_pdu(0x0b, 0x01, 0x02, 0x03));
define_test_client("/TP/GAR/CL/BI-01-C", test_client, service_data_1,
&test_read_2,
- raw_pdu(0x02, 0x00, 0x02),
- raw_pdu(0x03, 0x00, 0x02),
- raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
- raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),
- raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28),
- raw_pdu(0x11, 0x06, 0x05, 0x00, 0x08, 0x00, 0x0d, 0x18),
- raw_pdu(0x10, 0x09, 0x00, 0xff, 0xff, 0x00, 0x28),
- raw_pdu(0x01, 0x10, 0x09, 0x00, 0x0a),
- raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28),
- raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a),
- raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28),
- raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
- raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x02, 0x28),
- raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a),
- raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28),
- raw_pdu(0x09, 0x07, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00,
- 0x2a),
- raw_pdu(0x08, 0x03, 0x00, 0x04, 0x00, 0x03, 0x28),
- raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a),
- raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00),
- raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29),
- raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x03, 0x28),
- raw_pdu(0x09, 0x07, 0x06, 0x00, 0x02, 0x07, 0x00, 0x29,
- 0x2a),
- raw_pdu(0x08, 0x07, 0x00, 0x08, 0x00, 0x03, 0x28),
- raw_pdu(0x01, 0x08, 0x07, 0x00, 0x0a),
- raw_pdu(0x04, 0x08, 0x00, 0x08, 0x00),
- raw_pdu(0x05, 0x01, 0x08, 0x00, 0x01, 0x29),
+ SERVICE_DATA_1_PDU,
raw_pdu(0x0a, 0x00, 0x00),
raw_pdu(0x01, 0x0a, 0x00, 0x00, 0x01));
define_test_client("/TP/GAR/CL/BI-02-C", test_client, service_data_1,
&test_read_3,
- raw_pdu(0x02, 0x00, 0x02),
- raw_pdu(0x03, 0x00, 0x02),
- raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
- raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),
- raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28),
- raw_pdu(0x11, 0x06, 0x05, 0x00, 0x08, 0x00, 0x0d, 0x18),
- raw_pdu(0x10, 0x09, 0x00, 0xff, 0xff, 0x00, 0x28),
- raw_pdu(0x01, 0x10, 0x09, 0x00, 0x0a),
- raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28),
- raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a),
- raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28),
- raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
- raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x02, 0x28),
- raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a),
- raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28),
- raw_pdu(0x09, 0x07, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00,
- 0x2a),
- raw_pdu(0x08, 0x03, 0x00, 0x04, 0x00, 0x03, 0x28),
- raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a),
- raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00),
- raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29),
- raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x03, 0x28),
- raw_pdu(0x09, 0x07, 0x06, 0x00, 0x02, 0x07, 0x00, 0x29,
- 0x2a),
- raw_pdu(0x08, 0x07, 0x00, 0x08, 0x00, 0x03, 0x28),
- raw_pdu(0x01, 0x08, 0x07, 0x00, 0x0a),
- raw_pdu(0x04, 0x08, 0x00, 0x08, 0x00),
- raw_pdu(0x05, 0x01, 0x08, 0x00, 0x01, 0x29),
+ SERVICE_DATA_1_PDU,
raw_pdu(0x0a, 0x03, 0x00),
raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x02));
define_test_client("/TP/GAR/CL/BI-03-C", test_client, service_data_1,
&test_read_4,
- raw_pdu(0x02, 0x00, 0x02),
- raw_pdu(0x03, 0x00, 0x02),
- raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
- raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),
- raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28),
- raw_pdu(0x11, 0x06, 0x05, 0x00, 0x08, 0x00, 0x0d, 0x18),
- raw_pdu(0x10, 0x09, 0x00, 0xff, 0xff, 0x00, 0x28),
- raw_pdu(0x01, 0x10, 0x09, 0x00, 0x0a),
- raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28),
- raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a),
- raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28),
- raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
- raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x02, 0x28),
- raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a),
- raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28),
- raw_pdu(0x09, 0x07, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00,
- 0x2a),
- raw_pdu(0x08, 0x03, 0x00, 0x04, 0x00, 0x03, 0x28),
- raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a),
- raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00),
- raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29),
- raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x03, 0x28),
- raw_pdu(0x09, 0x07, 0x06, 0x00, 0x02, 0x07, 0x00, 0x29,
- 0x2a),
- raw_pdu(0x08, 0x07, 0x00, 0x08, 0x00, 0x03, 0x28),
- raw_pdu(0x01, 0x08, 0x07, 0x00, 0x0a),
- raw_pdu(0x04, 0x08, 0x00, 0x08, 0x00),
- raw_pdu(0x05, 0x01, 0x08, 0x00, 0x01, 0x29),
+ SERVICE_DATA_1_PDU,
raw_pdu(0x0a, 0x03, 0x00),
raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x08));
--
1.9.3
---
unit/test-gatt.c | 29 +++++++++++++++++++----------
1 file changed, 19 insertions(+), 10 deletions(-)
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 39cdda3..8898762 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -323,6 +323,18 @@ static void compare_chars(const bt_gatt_characteristic_t *a,
compare_descs(&a->descs[i], &b->descs[i]);
}
+typedef void (*test_step_t)(struct context *context);
+
+struct test_step {
+ test_step_t func;
+ uint16_t handle;
+ uint16_t end_handle;
+ uint8_t uuid[16];
+ uint8_t expected_att_ecode;
+ const uint8_t *value;
+ uint16_t length;
+};
+
static void client_ready_cb(bool success, uint8_t att_ecode, void *user_data)
{
struct context *context = user_data;
@@ -357,6 +369,13 @@ static void client_ready_cb(bool success, uint8_t att_ecode, void *user_data)
g_assert(!bt_gatt_service_iter_next(&iter, &service));
+ if (context->data->step) {
+ const struct test_step *step = context->data->step;
+
+ step->func(context);
+ return;
+ }
+
context_quit(context);
}
@@ -453,16 +472,6 @@ static void execute_context(struct context *context)
destroy_context(context);
}
-typedef void (*test_step_t)(struct context *context);
-
-struct test_step {
- test_step_t func;
- uint16_t handle;
- uint8_t expected_att_ecode;
- const uint8_t *value;
- uint16_t length;
-};
-
static void test_read_cb(bool success, uint8_t att_ecode,
const uint8_t *value, uint16_t length,
void *user_data)
--
1.9.3