|
Packit |
34410b |
/*
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
* BlueZ - Bluetooth protocol stack for Linux
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
* Copyright (C) 2014 Google Inc.
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
* This library is free software; you can redistribute it and/or
|
|
Packit |
34410b |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
34410b |
* License as published by the Free Software Foundation; either
|
|
Packit |
34410b |
* version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
* This library is distributed in the hope that it will be useful,
|
|
Packit |
34410b |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
34410b |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
34410b |
* Lesser General Public License for more details.
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
34410b |
* License along with this library; if not, write to the Free Software
|
|
Packit |
34410b |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#ifdef HAVE_CONFIG_H
|
|
Packit |
34410b |
#include <config.h>
|
|
Packit |
34410b |
#endif
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#include "src/shared/queue.h"
|
|
Packit |
34410b |
#include "src/shared/att.h"
|
|
Packit |
34410b |
#include "lib/bluetooth.h"
|
|
Packit |
34410b |
#include "lib/uuid.h"
|
|
Packit |
34410b |
#include "src/shared/gatt-helpers.h"
|
|
Packit |
34410b |
#include "src/shared/util.h"
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#ifndef MIN
|
|
Packit |
34410b |
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
Packit |
34410b |
#endif
|
|
Packit |
34410b |
|
|
Packit |
34410b |
struct bt_gatt_result {
|
|
Packit |
34410b |
uint8_t opcode;
|
|
Packit |
34410b |
void *pdu;
|
|
Packit |
34410b |
uint16_t pdu_len;
|
|
Packit |
34410b |
uint16_t data_len;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
void *op; /* Discovery operation data */
|
|
Packit |
34410b |
|
|
Packit |
34410b |
struct bt_gatt_result *next;
|
|
Packit |
34410b |
};
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static struct bt_gatt_result *result_create(uint8_t opcode, const void *pdu,
|
|
Packit |
34410b |
uint16_t pdu_len,
|
|
Packit |
34410b |
uint16_t data_len,
|
|
Packit |
34410b |
void *op)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_result *result;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
result = new0(struct bt_gatt_result, 1);
|
|
Packit |
34410b |
result->pdu = malloc(pdu_len);
|
|
Packit |
34410b |
if (!result->pdu) {
|
|
Packit |
34410b |
free(result);
|
|
Packit |
34410b |
return NULL;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
result->opcode = opcode;
|
|
Packit |
34410b |
result->pdu_len = pdu_len;
|
|
Packit |
34410b |
result->data_len = data_len;
|
|
Packit |
34410b |
result->op = op;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
memcpy(result->pdu, pdu, pdu_len);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return result;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void result_destroy(struct bt_gatt_result *result)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_result *next;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
while (result) {
|
|
Packit |
34410b |
next = result->next;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
free(result->pdu);
|
|
Packit |
34410b |
free(result);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
result = next;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static unsigned int result_element_count(struct bt_gatt_result *result)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
unsigned int count = 0;
|
|
Packit |
34410b |
struct bt_gatt_result *cur;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
cur = result;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
while (cur) {
|
|
Packit |
34410b |
count += cur->pdu_len / cur->data_len;
|
|
Packit |
34410b |
cur = cur->next;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return count;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
unsigned int bt_gatt_result_service_count(struct bt_gatt_result *result)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
if (!result)
|
|
Packit |
34410b |
return 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (result->opcode != BT_ATT_OP_READ_BY_GRP_TYPE_RSP &&
|
|
Packit |
34410b |
result->opcode != BT_ATT_OP_FIND_BY_TYPE_RSP)
|
|
Packit |
34410b |
return 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return result_element_count(result);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
unsigned int bt_gatt_result_characteristic_count(struct bt_gatt_result *result)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
if (!result)
|
|
Packit |
34410b |
return 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP)
|
|
Packit |
34410b |
return 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* Data length contains 7 or 21 octets:
|
|
Packit |
34410b |
* 2 octets: Attribute handle
|
|
Packit |
34410b |
* 1 octet: Characteristic properties
|
|
Packit |
34410b |
* 2 octets: Characteristic value handle
|
|
Packit |
34410b |
* 2 or 16 octets: characteristic UUID
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if (result->data_len != 21 && result->data_len != 7)
|
|
Packit |
34410b |
return 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return result_element_count(result);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
unsigned int bt_gatt_result_descriptor_count(struct bt_gatt_result *result)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
if (!result)
|
|
Packit |
34410b |
return 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (result->opcode != BT_ATT_OP_FIND_INFO_RSP)
|
|
Packit |
34410b |
return 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return result_element_count(result);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
unsigned int bt_gatt_result_included_count(struct bt_gatt_result *result)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_result *cur;
|
|
Packit |
34410b |
unsigned int count = 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!result)
|
|
Packit |
34410b |
return 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP)
|
|
Packit |
34410b |
return 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* Data length can be of length 6 or 8 octets:
|
|
Packit |
34410b |
* 2 octets - include service handle
|
|
Packit |
34410b |
* 2 octets - start handle of included service
|
|
Packit |
34410b |
* 2 octets - end handle of included service
|
|
Packit |
34410b |
* 2 octets (optionally) - 16 bit Bluetooth UUID
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if (result->data_len != 6 && result->data_len != 8)
|
|
Packit |
34410b |
return 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
for (cur = result; cur; cur = cur->next)
|
|
Packit |
34410b |
if (cur->opcode == BT_ATT_OP_READ_BY_TYPE_RSP)
|
|
Packit |
34410b |
count += cur->pdu_len / cur->data_len;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return count;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
bool bt_gatt_iter_init(struct bt_gatt_iter *iter, struct bt_gatt_result *result)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
if (!iter || !result)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
iter->result = result;
|
|
Packit |
34410b |
iter->pos = 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return true;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static const uint8_t bt_base_uuid[16] = {
|
|
Packit |
34410b |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
|
|
Packit |
34410b |
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
|
|
Packit |
34410b |
};
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static bool convert_uuid_le(const uint8_t *src, size_t len, uint8_t dst[16])
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
if (len == 16) {
|
|
Packit |
34410b |
bswap_128(src, dst);
|
|
Packit |
34410b |
return true;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (len != 2)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
memcpy(dst, bt_base_uuid, sizeof(bt_base_uuid));
|
|
Packit |
34410b |
dst[2] = src[1];
|
|
Packit |
34410b |
dst[3] = src[0];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return true;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
struct bt_gatt_request {
|
|
Packit |
34410b |
struct bt_att *att;
|
|
Packit |
34410b |
unsigned int id;
|
|
Packit |
34410b |
uint16_t start_handle;
|
|
Packit |
34410b |
uint16_t end_handle;
|
|
Packit |
34410b |
int ref_count;
|
|
Packit |
34410b |
bt_uuid_t uuid;
|
|
Packit |
34410b |
uint16_t service_type;
|
|
Packit |
34410b |
struct bt_gatt_result *result_head;
|
|
Packit |
34410b |
struct bt_gatt_result *result_tail;
|
|
Packit |
34410b |
bt_gatt_request_callback_t callback;
|
|
Packit |
34410b |
void *user_data;
|
|
Packit |
34410b |
bt_gatt_destroy_func_t destroy;
|
|
Packit |
34410b |
};
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static struct bt_gatt_result *result_append(uint8_t opcode, const void *pdu,
|
|
Packit |
34410b |
uint16_t pdu_len,
|
|
Packit |
34410b |
uint16_t data_len,
|
|
Packit |
34410b |
struct bt_gatt_request *op)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_result *result;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
result = result_create(opcode, pdu, pdu_len, data_len, op);
|
|
Packit |
34410b |
if (!result)
|
|
Packit |
34410b |
return NULL;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!op->result_head)
|
|
Packit |
34410b |
op->result_head = op->result_tail = result;
|
|
Packit |
34410b |
else {
|
|
Packit |
34410b |
op->result_tail->next = result;
|
|
Packit |
34410b |
op->result_tail = result;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return result;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
bool bt_gatt_iter_next_included_service(struct bt_gatt_iter *iter,
|
|
Packit |
34410b |
uint16_t *handle, uint16_t *start_handle,
|
|
Packit |
34410b |
uint16_t *end_handle, uint8_t uuid[16])
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_result *read_result;
|
|
Packit |
34410b |
struct bt_gatt_request *op;
|
|
Packit |
34410b |
const void *pdu_ptr;
|
|
Packit |
34410b |
int i = 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!iter || !iter->result || !handle || !start_handle || !end_handle
|
|
Packit |
34410b |
|| !uuid)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (iter->result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* UUID in discovery_op is set in read_by_type and service_discovery */
|
|
Packit |
34410b |
op = iter->result->op;
|
|
Packit |
34410b |
if (op->uuid.type != BT_UUID_UNSPEC)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* iter->result points to READ_BY_TYPE_RSP with data length containing:
|
|
Packit |
34410b |
* 2 octets - include service handle
|
|
Packit |
34410b |
* 2 octets - start handle of included service
|
|
Packit |
34410b |
* 2 octets - end handle of included service
|
|
Packit |
34410b |
* optional 2 octets - Bluetooth UUID
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if (iter->result->data_len != 8 && iter->result->data_len != 6)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
pdu_ptr = iter->result->pdu + iter->pos;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* This result contains 16 bit UUID */
|
|
Packit |
34410b |
if (iter->result->data_len == 8) {
|
|
Packit |
34410b |
*handle = get_le16(pdu_ptr);
|
|
Packit |
34410b |
*start_handle = get_le16(pdu_ptr + 2);
|
|
Packit |
34410b |
*end_handle = get_le16(pdu_ptr + 4);
|
|
Packit |
34410b |
convert_uuid_le(pdu_ptr + 6, 2, uuid);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
iter->pos += iter->result->data_len;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (iter->pos == iter->result->pdu_len) {
|
|
Packit |
34410b |
iter->result = iter->result->next;
|
|
Packit |
34410b |
iter->pos = 0;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return true;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
*handle = get_le16(pdu_ptr);
|
|
Packit |
34410b |
*start_handle = get_le16(pdu_ptr + 2);
|
|
Packit |
34410b |
*end_handle = get_le16(pdu_ptr + 4);
|
|
Packit |
34410b |
read_result = iter->result;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* Find READ_RSP with include service UUID.
|
|
Packit |
34410b |
* If number of current data set in READ_BY_TYPE_RSP is n, then we must
|
|
Packit |
34410b |
* go to n'th PDU next to current item->result
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
for (read_result = read_result->next; read_result; i++) {
|
|
Packit |
34410b |
if (i >= (iter->pos / iter->result->data_len))
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
read_result = read_result->next;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!read_result)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
convert_uuid_le(read_result->pdu, read_result->data_len, uuid);
|
|
Packit |
34410b |
iter->pos += iter->result->data_len;
|
|
Packit |
34410b |
if (iter->pos == iter->result->pdu_len) {
|
|
Packit |
34410b |
iter->result = read_result->next;
|
|
Packit |
34410b |
iter->pos = 0;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return true;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
bool bt_gatt_iter_next_service(struct bt_gatt_iter *iter,
|
|
Packit |
34410b |
uint16_t *start_handle, uint16_t *end_handle,
|
|
Packit |
34410b |
uint8_t uuid[16])
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_request *op;
|
|
Packit |
34410b |
const void *pdu_ptr;
|
|
Packit |
34410b |
bt_uuid_t tmp;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!iter || !iter->result || !start_handle || !end_handle || !uuid)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op = iter->result->op;
|
|
Packit |
34410b |
pdu_ptr = iter->result->pdu + iter->pos;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
switch (iter->result->opcode) {
|
|
Packit |
34410b |
case BT_ATT_OP_READ_BY_GRP_TYPE_RSP:
|
|
Packit |
34410b |
*start_handle = get_le16(pdu_ptr);
|
|
Packit |
34410b |
*end_handle = get_le16(pdu_ptr + 2);
|
|
Packit |
34410b |
convert_uuid_le(pdu_ptr + 4, iter->result->data_len - 4, uuid);
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
case BT_ATT_OP_FIND_BY_TYPE_RSP:
|
|
Packit |
34410b |
*start_handle = get_le16(pdu_ptr);
|
|
Packit |
34410b |
*end_handle = get_le16(pdu_ptr + 2);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
bt_uuid_to_uuid128(&op->uuid, &tmp);
|
|
Packit |
34410b |
memcpy(uuid, tmp.value.u128.data, 16);
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
default:
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
|
|
Packit |
34410b |
iter->pos += iter->result->data_len;
|
|
Packit |
34410b |
if (iter->pos == iter->result->pdu_len) {
|
|
Packit |
34410b |
iter->result = iter->result->next;
|
|
Packit |
34410b |
iter->pos = 0;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return true;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
bool bt_gatt_iter_next_characteristic(struct bt_gatt_iter *iter,
|
|
Packit |
34410b |
uint16_t *start_handle, uint16_t *end_handle,
|
|
Packit |
34410b |
uint16_t *value_handle, uint8_t *properties,
|
|
Packit |
34410b |
uint8_t uuid[16])
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_request *op;
|
|
Packit |
34410b |
const void *pdu_ptr;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!iter || !iter->result || !start_handle || !end_handle ||
|
|
Packit |
34410b |
!value_handle || !properties || !uuid)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (iter->result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* UUID in discovery_op is set in read_by_type and service_discovery */
|
|
Packit |
34410b |
op = iter->result->op;
|
|
Packit |
34410b |
if (op->uuid.type != BT_UUID_UNSPEC)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* Data length contains 7 or 21 octets:
|
|
Packit |
34410b |
* 2 octets: Attribute handle
|
|
Packit |
34410b |
* 1 octet: Characteristic properties
|
|
Packit |
34410b |
* 2 octets: Characteristic value handle
|
|
Packit |
34410b |
* 2 or 16 octets: characteristic UUID
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if (iter->result->data_len != 21 && iter->result->data_len != 7)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
pdu_ptr = iter->result->pdu + iter->pos;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
*start_handle = get_le16(pdu_ptr);
|
|
Packit |
34410b |
*properties = ((uint8_t *) pdu_ptr)[2];
|
|
Packit |
34410b |
*value_handle = get_le16(pdu_ptr + 3);
|
|
Packit |
34410b |
convert_uuid_le(pdu_ptr + 5, iter->result->data_len - 5, uuid);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
iter->pos += iter->result->data_len;
|
|
Packit |
34410b |
if (iter->pos == iter->result->pdu_len) {
|
|
Packit |
34410b |
iter->result = iter->result->next;
|
|
Packit |
34410b |
iter->pos = 0;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!iter->result) {
|
|
Packit |
34410b |
*end_handle = op->end_handle;
|
|
Packit |
34410b |
return true;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
*end_handle = get_le16(iter->result->pdu + iter->pos) - 1;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return true;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
bool bt_gatt_iter_next_descriptor(struct bt_gatt_iter *iter, uint16_t *handle,
|
|
Packit |
34410b |
uint8_t uuid[16])
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
const void *pdu_ptr;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!iter || !iter->result || !handle || !uuid)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (iter->result->opcode != BT_ATT_OP_FIND_INFO_RSP)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
pdu_ptr = iter->result->pdu + iter->pos;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
*handle = get_le16(pdu_ptr);
|
|
Packit |
34410b |
convert_uuid_le(pdu_ptr + 2, iter->result->data_len - 2, uuid);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
iter->pos += iter->result->data_len;
|
|
Packit |
34410b |
if (iter->pos == iter->result->pdu_len) {
|
|
Packit |
34410b |
iter->result = iter->result->next;
|
|
Packit |
34410b |
iter->pos = 0;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return true;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
bool bt_gatt_iter_next_read_by_type(struct bt_gatt_iter *iter,
|
|
Packit |
34410b |
uint16_t *handle, uint16_t *length,
|
|
Packit |
34410b |
const uint8_t **value)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_request *op;
|
|
Packit |
34410b |
const void *pdu_ptr;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!iter || !iter->result || !handle || !length || !value)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (iter->result->opcode != BT_ATT_OP_READ_BY_TYPE_RSP)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* Check if UUID is set, otherwise results can contain characteristic
|
|
Packit |
34410b |
* discovery service or included service discovery results
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
op = iter->result->op;
|
|
Packit |
34410b |
if (op->uuid.type == BT_UUID_UNSPEC)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
pdu_ptr = iter->result->pdu + iter->pos;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
*handle = get_le16(pdu_ptr);
|
|
Packit |
34410b |
*length = iter->result->data_len - 2;
|
|
Packit |
34410b |
*value = pdu_ptr + 2;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
iter->pos += iter->result->data_len;
|
|
Packit |
34410b |
if (iter->pos == iter->result->pdu_len) {
|
|
Packit |
34410b |
iter->result = iter->result->next;
|
|
Packit |
34410b |
iter->pos = 0;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return true;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
struct mtu_op {
|
|
Packit |
34410b |
struct bt_att *att;
|
|
Packit |
34410b |
uint16_t client_rx_mtu;
|
|
Packit |
34410b |
bt_gatt_result_callback_t callback;
|
|
Packit |
34410b |
void *user_data;
|
|
Packit |
34410b |
bt_gatt_destroy_func_t destroy;
|
|
Packit |
34410b |
};
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void destroy_mtu_op(void *user_data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct mtu_op *op = user_data;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (op->destroy)
|
|
Packit |
34410b |
op->destroy(op->user_data);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
free(op);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static uint8_t process_error(const void *pdu, uint16_t length)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
const struct bt_att_pdu_error_rsp *error_pdu;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!pdu || length != sizeof(struct bt_att_pdu_error_rsp))
|
|
Packit |
34410b |
return 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
error_pdu = pdu;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return error_pdu->ecode;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void mtu_cb(uint8_t opcode, const void *pdu, uint16_t length,
|
|
Packit |
34410b |
void *user_data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct mtu_op *op = user_data;
|
|
Packit |
34410b |
bool success = true;
|
|
Packit |
34410b |
uint8_t att_ecode = 0;
|
|
Packit |
34410b |
uint16_t server_rx_mtu;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (opcode == BT_ATT_OP_ERROR_RSP) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
att_ecode = process_error(pdu, length);
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (opcode != BT_ATT_OP_MTU_RSP || !pdu || length != 2) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
server_rx_mtu = get_le16(pdu);
|
|
Packit |
34410b |
bt_att_set_mtu(op->att, MIN(op->client_rx_mtu, server_rx_mtu));
|
|
Packit |
34410b |
|
|
Packit |
34410b |
done:
|
|
Packit |
34410b |
if (op->callback)
|
|
Packit |
34410b |
op->callback(success, att_ecode, op->user_data);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
unsigned int bt_gatt_exchange_mtu(struct bt_att *att, uint16_t client_rx_mtu,
|
|
Packit |
34410b |
bt_gatt_result_callback_t callback,
|
|
Packit |
34410b |
void *user_data,
|
|
Packit |
34410b |
bt_gatt_destroy_func_t destroy)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct mtu_op *op;
|
|
Packit |
34410b |
uint8_t pdu[2];
|
|
Packit |
34410b |
unsigned int id;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!att || !client_rx_mtu)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op = new0(struct mtu_op, 1);
|
|
Packit |
34410b |
op->att = att;
|
|
Packit |
34410b |
op->client_rx_mtu = client_rx_mtu;
|
|
Packit |
34410b |
op->callback = callback;
|
|
Packit |
34410b |
op->user_data = user_data;
|
|
Packit |
34410b |
op->destroy = destroy;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
put_le16(client_rx_mtu, pdu);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
id = bt_att_send(att, BT_ATT_OP_MTU_REQ, pdu, sizeof(pdu), mtu_cb, op,
|
|
Packit |
34410b |
destroy_mtu_op);
|
|
Packit |
34410b |
if (!id)
|
|
Packit |
34410b |
free(op);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return id;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static inline int get_uuid_len(const bt_uuid_t *uuid)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
if (!uuid)
|
|
Packit |
34410b |
return 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return (uuid->type == BT_UUID16) ? 2 : 16;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
struct bt_gatt_request *bt_gatt_request_ref(struct bt_gatt_request *req)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
if (!req)
|
|
Packit |
34410b |
return NULL;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
__sync_fetch_and_add(&req->ref_count, 1);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return req;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
void bt_gatt_request_unref(struct bt_gatt_request *req)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
if (!req)
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (__sync_sub_and_fetch(&req->ref_count, 1))
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
bt_gatt_request_cancel(req);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (req->destroy)
|
|
Packit |
34410b |
req->destroy(req->user_data);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
result_destroy(req->result_head);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
free(req);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
void bt_gatt_request_cancel(struct bt_gatt_request *req)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
if (!req)
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!req->id)
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
bt_att_cancel(req->att, req->id);
|
|
Packit |
34410b |
req->id = 0;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void async_req_unref(void *data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_request *req = data;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
bt_gatt_request_unref(req);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void discovery_op_complete(struct bt_gatt_request *op, bool success,
|
|
Packit |
34410b |
uint8_t ecode)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
/* Reset success if there is some result to report */
|
|
Packit |
34410b |
if (ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND && op->result_head)
|
|
Packit |
34410b |
success = true;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (op->callback)
|
|
Packit |
34410b |
op->callback(success, ecode, success ? op->result_head : NULL,
|
|
Packit |
34410b |
op->user_data);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!op->id)
|
|
Packit |
34410b |
async_req_unref(op);
|
|
Packit |
34410b |
else
|
|
Packit |
34410b |
op->id = 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
|
|
Packit |
34410b |
uint16_t length, void *user_data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_request *op = user_data;
|
|
Packit |
34410b |
bool success;
|
|
Packit |
34410b |
uint8_t att_ecode = 0;
|
|
Packit |
34410b |
struct bt_gatt_result *cur_result;
|
|
Packit |
34410b |
size_t data_length;
|
|
Packit |
34410b |
size_t list_length;
|
|
Packit |
34410b |
uint16_t last_end;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (opcode == BT_ATT_OP_ERROR_RSP) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
att_ecode = process_error(pdu, length);
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* PDU must contain at least the following (sans opcode):
|
|
Packit |
34410b |
* - Attr Data Length (1 octet)
|
|
Packit |
34410b |
* - Attr Data List (at least 6 octets):
|
|
Packit |
34410b |
* -- 2 octets: Attribute handle
|
|
Packit |
34410b |
* -- 2 octets: End group handle
|
|
Packit |
34410b |
* -- 2 or 16 octets: service UUID
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if (opcode != BT_ATT_OP_READ_BY_GRP_TYPE_RSP || !pdu || length < 7) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
data_length = ((uint8_t *) pdu)[0];
|
|
Packit |
34410b |
list_length = length - 1;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if ((data_length != 6 && data_length != 20) ||
|
|
Packit |
34410b |
(list_length % data_length)) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* PDU is correctly formatted. Get the last end handle to process the
|
|
Packit |
34410b |
* next request and store the PDU.
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
cur_result = result_append(opcode, pdu + 1, list_length, data_length,
|
|
Packit |
34410b |
op);
|
|
Packit |
34410b |
if (!cur_result) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
last_end = get_le16(pdu + length - data_length + 2);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* If last handle is lower from previous start handle then it is smth
|
|
Packit |
34410b |
* wrong. Let's stop search, otherwise we might enter infinite loop.
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if (last_end < op->start_handle) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->start_handle = last_end + 1;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (last_end < op->end_handle) {
|
|
Packit |
34410b |
uint8_t pdu[6];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
put_le16(op->start_handle, pdu);
|
|
Packit |
34410b |
put_le16(op->end_handle, pdu + 2);
|
|
Packit |
34410b |
put_le16(op->service_type, pdu + 4);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
|
|
Packit |
34410b |
pdu, sizeof(pdu),
|
|
Packit |
34410b |
read_by_grp_type_cb,
|
|
Packit |
34410b |
bt_gatt_request_ref(op),
|
|
Packit |
34410b |
async_req_unref);
|
|
Packit |
34410b |
if (op->id)
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* Some devices incorrectly return 0xffff as the end group handle when
|
|
Packit |
34410b |
* the read-by-group-type request is performed within a smaller range.
|
|
Packit |
34410b |
* Manually set the end group handle that we report in the result to the
|
|
Packit |
34410b |
* end handle in the original request.
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if (last_end == 0xffff && last_end != op->end_handle)
|
|
Packit |
34410b |
put_le16(op->end_handle,
|
|
Packit |
34410b |
cur_result->pdu + length - data_length + 1);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
success = true;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
done:
|
|
Packit |
34410b |
discovery_op_complete(op, success, att_ecode);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void find_by_type_val_cb(uint8_t opcode, const void *pdu,
|
|
Packit |
34410b |
uint16_t length, void *user_data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_request *op = user_data;
|
|
Packit |
34410b |
bool success;
|
|
Packit |
34410b |
uint8_t att_ecode = 0;
|
|
Packit |
34410b |
uint16_t last_end;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (opcode == BT_ATT_OP_ERROR_RSP) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
att_ecode = process_error(pdu, length);
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* PDU must contain 4 bytes and it must be a multiple of 4, where each
|
|
Packit |
34410b |
* 4 bytes contain the 16-bit attribute and group end handles.
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if (opcode != BT_ATT_OP_FIND_BY_TYPE_RSP || !pdu || !length ||
|
|
Packit |
34410b |
length % 4) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!result_append(opcode, pdu, length, 4, op)) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* Each data set contains:
|
|
Packit |
34410b |
* 2 octets with start handle
|
|
Packit |
34410b |
* 2 octets with end handle
|
|
Packit |
34410b |
* last_end is end handle of last data set
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
last_end = get_le16(pdu + length - 2);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* If last handle is lower from previous start handle then it is smth
|
|
Packit |
34410b |
* wrong. Let's stop search, otherwise we might enter infinite loop.
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if (last_end < op->start_handle) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->start_handle = last_end + 1;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (last_end < op->end_handle) {
|
|
Packit |
34410b |
uint8_t pdu[6 + get_uuid_len(&op->uuid)];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
put_le16(op->start_handle, pdu);
|
|
Packit |
34410b |
put_le16(op->end_handle, pdu + 2);
|
|
Packit |
34410b |
put_le16(op->service_type, pdu + 4);
|
|
Packit |
34410b |
bt_uuid_to_le(&op->uuid, pdu + 6);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->id = bt_att_send(op->att, BT_ATT_OP_FIND_BY_TYPE_REQ,
|
|
Packit |
34410b |
pdu, sizeof(pdu),
|
|
Packit |
34410b |
find_by_type_val_cb,
|
|
Packit |
34410b |
bt_gatt_request_ref(op),
|
|
Packit |
34410b |
async_req_unref);
|
|
Packit |
34410b |
if (op->id)
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
success = true;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
done:
|
|
Packit |
34410b |
discovery_op_complete(op, success, att_ecode);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static struct bt_gatt_request *discover_services(struct bt_att *att,
|
|
Packit |
34410b |
bt_uuid_t *uuid,
|
|
Packit |
34410b |
uint16_t start, uint16_t end,
|
|
Packit |
34410b |
bt_gatt_request_callback_t callback,
|
|
Packit |
34410b |
void *user_data,
|
|
Packit |
34410b |
bt_gatt_destroy_func_t destroy,
|
|
Packit |
34410b |
bool primary)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_request *op;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!att)
|
|
Packit |
34410b |
return NULL;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op = new0(struct bt_gatt_request, 1);
|
|
Packit |
34410b |
op->att = att;
|
|
Packit |
34410b |
op->start_handle = start;
|
|
Packit |
34410b |
op->end_handle = end;
|
|
Packit |
34410b |
op->callback = callback;
|
|
Packit |
34410b |
op->user_data = user_data;
|
|
Packit |
34410b |
op->destroy = destroy;
|
|
Packit |
34410b |
/* set service uuid to primary or secondary */
|
|
Packit |
34410b |
op->service_type = primary ? GATT_PRIM_SVC_UUID : GATT_SND_SVC_UUID;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* If UUID is NULL, then discover all primary services */
|
|
Packit |
34410b |
if (!uuid) {
|
|
Packit |
34410b |
uint8_t pdu[6];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
put_le16(start, pdu);
|
|
Packit |
34410b |
put_le16(end, pdu + 2);
|
|
Packit |
34410b |
put_le16(op->service_type, pdu + 4);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->id = bt_att_send(att, BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
|
|
Packit |
34410b |
pdu, sizeof(pdu),
|
|
Packit |
34410b |
read_by_grp_type_cb,
|
|
Packit |
34410b |
bt_gatt_request_ref(op),
|
|
Packit |
34410b |
async_req_unref);
|
|
Packit |
34410b |
} else {
|
|
Packit |
34410b |
uint8_t pdu[6 + get_uuid_len(uuid)];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (uuid->type == BT_UUID_UNSPEC) {
|
|
Packit |
34410b |
free(op);
|
|
Packit |
34410b |
return NULL;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* Discover by UUID */
|
|
Packit |
34410b |
op->uuid = *uuid;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
put_le16(start, pdu);
|
|
Packit |
34410b |
put_le16(end, pdu + 2);
|
|
Packit |
34410b |
put_le16(op->service_type, pdu + 4);
|
|
Packit |
34410b |
bt_uuid_to_le(&op->uuid, pdu + 6);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->id = bt_att_send(att, BT_ATT_OP_FIND_BY_TYPE_REQ,
|
|
Packit |
34410b |
pdu, sizeof(pdu),
|
|
Packit |
34410b |
find_by_type_val_cb,
|
|
Packit |
34410b |
bt_gatt_request_ref(op),
|
|
Packit |
34410b |
async_req_unref);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!op->id) {
|
|
Packit |
34410b |
free(op);
|
|
Packit |
34410b |
return NULL;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return bt_gatt_request_ref(op);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
struct bt_gatt_request *bt_gatt_discover_all_primary_services(
|
|
Packit |
34410b |
struct bt_att *att, bt_uuid_t *uuid,
|
|
Packit |
34410b |
bt_gatt_request_callback_t callback,
|
|
Packit |
34410b |
void *user_data,
|
|
Packit |
34410b |
bt_gatt_destroy_func_t destroy)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
return bt_gatt_discover_primary_services(att, uuid, 0x0001, 0xffff,
|
|
Packit |
34410b |
callback, user_data,
|
|
Packit |
34410b |
destroy);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
struct bt_gatt_request *bt_gatt_discover_primary_services(
|
|
Packit |
34410b |
struct bt_att *att, bt_uuid_t *uuid,
|
|
Packit |
34410b |
uint16_t start, uint16_t end,
|
|
Packit |
34410b |
bt_gatt_request_callback_t callback,
|
|
Packit |
34410b |
void *user_data,
|
|
Packit |
34410b |
bt_gatt_destroy_func_t destroy)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
return discover_services(att, uuid, start, end, callback, user_data,
|
|
Packit |
34410b |
destroy, true);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
struct bt_gatt_request *bt_gatt_discover_secondary_services(
|
|
Packit |
34410b |
struct bt_att *att, bt_uuid_t *uuid,
|
|
Packit |
34410b |
uint16_t start, uint16_t end,
|
|
Packit |
34410b |
bt_gatt_request_callback_t callback,
|
|
Packit |
34410b |
void *user_data,
|
|
Packit |
34410b |
bt_gatt_destroy_func_t destroy)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
return discover_services(att, uuid, start, end, callback, user_data,
|
|
Packit |
34410b |
destroy, false);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
struct read_incl_data {
|
|
Packit |
34410b |
struct bt_gatt_request *op;
|
|
Packit |
34410b |
struct bt_gatt_result *result;
|
|
Packit |
34410b |
int pos;
|
|
Packit |
34410b |
int ref_count;
|
|
Packit |
34410b |
};
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static struct read_incl_data *new_read_included(struct bt_gatt_result *res)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct read_incl_data *data;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
data = new0(struct read_incl_data, 1);
|
|
Packit |
34410b |
data->op = bt_gatt_request_ref(res->op);
|
|
Packit |
34410b |
data->result = res;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return data;
|
|
Packit |
34410b |
};
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static struct read_incl_data *read_included_ref(struct read_incl_data *data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
__sync_fetch_and_add(&data->ref_count, 1);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return data;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void read_included_unref(void *data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct read_incl_data *read_data = data;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (__sync_sub_and_fetch(&read_data->ref_count, 1))
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
async_req_unref(read_data->op);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
free(read_data);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void discover_included_cb(uint8_t opcode, const void *pdu,
|
|
Packit |
34410b |
uint16_t length, void *user_data);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void read_included_cb(uint8_t opcode, const void *pdu,
|
|
Packit |
34410b |
uint16_t length, void *user_data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct read_incl_data *data = user_data;
|
|
Packit |
34410b |
struct bt_gatt_request *op = data->op;
|
|
Packit |
34410b |
uint8_t att_ecode = 0;
|
|
Packit |
34410b |
uint8_t read_pdu[2];
|
|
Packit |
34410b |
bool success;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (opcode == BT_ATT_OP_ERROR_RSP) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
att_ecode = process_error(pdu, length);
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (opcode != BT_ATT_OP_READ_RSP || (!pdu && length)) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* UUID should be in 128 bit format, as it couldn't be read in
|
|
Packit |
34410b |
* READ_BY_TYPE request
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if (length != 16) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!result_append(opcode, pdu, length, length, op)) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (data->pos == data->result->pdu_len) {
|
|
Packit |
34410b |
uint16_t last_handle;
|
|
Packit |
34410b |
uint8_t pdu[6];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
last_handle = get_le16(data->result->pdu + data->pos -
|
|
Packit |
34410b |
data->result->data_len);
|
|
Packit |
34410b |
if (last_handle == op->end_handle) {
|
|
Packit |
34410b |
success = true;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
put_le16(last_handle + 1, pdu);
|
|
Packit |
34410b |
put_le16(op->end_handle, pdu + 2);
|
|
Packit |
34410b |
put_le16(GATT_INCLUDE_UUID, pdu + 4);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
|
|
Packit |
34410b |
pdu, sizeof(pdu),
|
|
Packit |
34410b |
discover_included_cb,
|
|
Packit |
34410b |
bt_gatt_request_ref(op),
|
|
Packit |
34410b |
async_req_unref);
|
|
Packit |
34410b |
if (op->id)
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
memcpy(read_pdu, data->result->pdu + data->pos + 2, sizeof(uint16_t));
|
|
Packit |
34410b |
|
|
Packit |
34410b |
data->pos += data->result->data_len;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (bt_att_send(op->att, BT_ATT_OP_READ_REQ, read_pdu, sizeof(read_pdu),
|
|
Packit |
34410b |
read_included_cb, read_included_ref(data),
|
|
Packit |
34410b |
read_included_unref))
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
read_included_unref(data);
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
done:
|
|
Packit |
34410b |
discovery_op_complete(op, success, att_ecode);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void read_included(struct read_incl_data *data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_request *op = data->op;
|
|
Packit |
34410b |
uint8_t pdu[2];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
memcpy(pdu, data->result->pdu + 2, sizeof(uint16_t));
|
|
Packit |
34410b |
|
|
Packit |
34410b |
data->pos += data->result->data_len;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (bt_att_send(op->att, BT_ATT_OP_READ_REQ, pdu, sizeof(pdu),
|
|
Packit |
34410b |
read_included_cb,
|
|
Packit |
34410b |
read_included_ref(data),
|
|
Packit |
34410b |
read_included_unref))
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (op->callback)
|
|
Packit |
34410b |
op->callback(false, 0, NULL, data->op->user_data);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
read_included_unref(data);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void discover_included_cb(uint8_t opcode, const void *pdu,
|
|
Packit |
34410b |
uint16_t length, void *user_data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_request *op = user_data;
|
|
Packit |
34410b |
struct bt_gatt_result *cur_result;
|
|
Packit |
34410b |
uint8_t att_ecode = 0;
|
|
Packit |
34410b |
uint16_t last_handle;
|
|
Packit |
34410b |
size_t data_length;
|
|
Packit |
34410b |
bool success;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (opcode == BT_ATT_OP_ERROR_RSP) {
|
|
Packit |
34410b |
att_ecode = process_error(pdu, length);
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto failed;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || !pdu || length < 6) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto failed;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
data_length = ((const uint8_t *) pdu)[0];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* Check if PDU contains data sets with length declared in the beginning
|
|
Packit |
34410b |
* of frame and if this length is correct.
|
|
Packit |
34410b |
* Data set length may be 6 or 8 octets:
|
|
Packit |
34410b |
* 2 octets - include service handle
|
|
Packit |
34410b |
* 2 octets - start handle of included service
|
|
Packit |
34410b |
* 2 octets - end handle of included service
|
|
Packit |
34410b |
* optional 2 octets - Bluetooth UUID of included service
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if ((data_length != 8 && data_length != 6) ||
|
|
Packit |
34410b |
(length - 1) % data_length) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto failed;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
cur_result = result_append(opcode, pdu + 1, length - 1, data_length,
|
|
Packit |
34410b |
op);
|
|
Packit |
34410b |
if (!cur_result) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto failed;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (data_length == 6) {
|
|
Packit |
34410b |
struct read_incl_data *data;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
data = new_read_included(cur_result);
|
|
Packit |
34410b |
if (!data) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto failed;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
read_included(data);
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
last_handle = get_le16(pdu + length - data_length);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* If last handle is lower from previous start handle then it is smth
|
|
Packit |
34410b |
* wrong. Let's stop search, otherwise we might enter infinite loop.
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if (last_handle < op->start_handle) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto failed;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->start_handle = last_handle + 1;
|
|
Packit |
34410b |
if (last_handle != op->end_handle) {
|
|
Packit |
34410b |
uint8_t pdu[6];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
put_le16(op->start_handle, pdu);
|
|
Packit |
34410b |
put_le16(op->end_handle, pdu + 2);
|
|
Packit |
34410b |
put_le16(GATT_INCLUDE_UUID, pdu + 4);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
|
|
Packit |
34410b |
pdu, sizeof(pdu),
|
|
Packit |
34410b |
discover_included_cb,
|
|
Packit |
34410b |
bt_gatt_request_ref(op),
|
|
Packit |
34410b |
async_req_unref);
|
|
Packit |
34410b |
if (op->id)
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto failed;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
success = true;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
failed:
|
|
Packit |
34410b |
discovery_op_complete(op, success, att_ecode);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
struct bt_gatt_request *bt_gatt_discover_included_services(struct bt_att *att,
|
|
Packit |
34410b |
uint16_t start, uint16_t end,
|
|
Packit |
34410b |
bt_gatt_request_callback_t callback,
|
|
Packit |
34410b |
void *user_data,
|
|
Packit |
34410b |
bt_gatt_destroy_func_t destroy)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_request *op;
|
|
Packit |
34410b |
uint8_t pdu[6];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!att)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op = new0(struct bt_gatt_request, 1);
|
|
Packit |
34410b |
op->att = att;
|
|
Packit |
34410b |
op->callback = callback;
|
|
Packit |
34410b |
op->user_data = user_data;
|
|
Packit |
34410b |
op->destroy = destroy;
|
|
Packit |
34410b |
op->start_handle = start;
|
|
Packit |
34410b |
op->end_handle = end;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
put_le16(start, pdu);
|
|
Packit |
34410b |
put_le16(end, pdu + 2);
|
|
Packit |
34410b |
put_le16(GATT_INCLUDE_UUID, pdu + 4);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu),
|
|
Packit |
34410b |
discover_included_cb, bt_gatt_request_ref(op),
|
|
Packit |
34410b |
async_req_unref);
|
|
Packit |
34410b |
if (!op->id) {
|
|
Packit |
34410b |
free(op);
|
|
Packit |
34410b |
return NULL;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return bt_gatt_request_ref(op);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void discover_chrcs_cb(uint8_t opcode, const void *pdu,
|
|
Packit |
34410b |
uint16_t length, void *user_data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_request *op = user_data;
|
|
Packit |
34410b |
bool success;
|
|
Packit |
34410b |
uint8_t att_ecode = 0;
|
|
Packit |
34410b |
size_t data_length;
|
|
Packit |
34410b |
uint16_t last_handle;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (opcode == BT_ATT_OP_ERROR_RSP) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
att_ecode = process_error(pdu, length);
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* PDU must contain at least the following (sans opcode):
|
|
Packit |
34410b |
* - Attr Data Length (1 octet)
|
|
Packit |
34410b |
* - Attr Data List (at least 7 octets):
|
|
Packit |
34410b |
* -- 2 octets: Attribute handle
|
|
Packit |
34410b |
* -- 1 octet: Characteristic properties
|
|
Packit |
34410b |
* -- 2 octets: Characteristic value handle
|
|
Packit |
34410b |
* -- 2 or 16 octets: characteristic UUID
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || !pdu || length < 8) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
data_length = ((uint8_t *) pdu)[0];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if ((data_length != 7 && data_length != 21) ||
|
|
Packit |
34410b |
((length - 1) % data_length)) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!result_append(opcode, pdu + 1, length - 1,
|
|
Packit |
34410b |
data_length, op)) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
last_handle = get_le16(pdu + length - data_length);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* If last handle is lower from previous start handle then it is smth
|
|
Packit |
34410b |
* wrong. Let's stop search, otherwise we might enter infinite loop.
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if (last_handle < op->start_handle) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->start_handle = last_handle + 1;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (last_handle != op->end_handle) {
|
|
Packit |
34410b |
uint8_t pdu[6];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
put_le16(op->start_handle, pdu);
|
|
Packit |
34410b |
put_le16(op->end_handle, pdu + 2);
|
|
Packit |
34410b |
put_le16(GATT_CHARAC_UUID, pdu + 4);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
|
|
Packit |
34410b |
pdu, sizeof(pdu),
|
|
Packit |
34410b |
discover_chrcs_cb,
|
|
Packit |
34410b |
bt_gatt_request_ref(op),
|
|
Packit |
34410b |
async_req_unref);
|
|
Packit |
34410b |
if (op->id)
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
success = true;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
done:
|
|
Packit |
34410b |
discovery_op_complete(op, success, att_ecode);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
struct bt_gatt_request *bt_gatt_discover_characteristics(struct bt_att *att,
|
|
Packit |
34410b |
uint16_t start, uint16_t end,
|
|
Packit |
34410b |
bt_gatt_request_callback_t callback,
|
|
Packit |
34410b |
void *user_data,
|
|
Packit |
34410b |
bt_gatt_destroy_func_t destroy)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_request *op;
|
|
Packit |
34410b |
uint8_t pdu[6];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!att)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op = new0(struct bt_gatt_request, 1);
|
|
Packit |
34410b |
op->att = att;
|
|
Packit |
34410b |
op->callback = callback;
|
|
Packit |
34410b |
op->user_data = user_data;
|
|
Packit |
34410b |
op->destroy = destroy;
|
|
Packit |
34410b |
op->start_handle = start;
|
|
Packit |
34410b |
op->end_handle = end;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
put_le16(start, pdu);
|
|
Packit |
34410b |
put_le16(end, pdu + 2);
|
|
Packit |
34410b |
put_le16(GATT_CHARAC_UUID, pdu + 4);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu),
|
|
Packit |
34410b |
discover_chrcs_cb, bt_gatt_request_ref(op),
|
|
Packit |
34410b |
async_req_unref);
|
|
Packit |
34410b |
if (!op->id) {
|
|
Packit |
34410b |
free(op);
|
|
Packit |
34410b |
return NULL;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return bt_gatt_request_ref(op);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void read_by_type_cb(uint8_t opcode, const void *pdu,
|
|
Packit |
34410b |
uint16_t length, void *user_data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_request *op = user_data;
|
|
Packit |
34410b |
bool success;
|
|
Packit |
34410b |
uint8_t att_ecode = 0;
|
|
Packit |
34410b |
size_t data_length;
|
|
Packit |
34410b |
uint16_t last_handle;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (opcode == BT_ATT_OP_ERROR_RSP) {
|
|
Packit |
34410b |
att_ecode = process_error(pdu, length);
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || !pdu) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
att_ecode = 0;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
data_length = ((uint8_t *) pdu)[0];
|
|
Packit |
34410b |
if (((length - 1) % data_length)) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
att_ecode = 0;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!result_append(opcode, pdu + 1, length - 1, data_length, op)) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
att_ecode = 0;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
last_handle = get_le16(pdu + length - data_length);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* If last handle is lower from previous start handle then it is smth
|
|
Packit |
34410b |
* wrong. Let's stop search, otherwise we might enter infinite loop.
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if (last_handle < op->start_handle) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->start_handle = last_handle + 1;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (last_handle != op->end_handle) {
|
|
Packit |
34410b |
uint8_t pdu[4 + get_uuid_len(&op->uuid)];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
put_le16(op->start_handle, pdu);
|
|
Packit |
34410b |
put_le16(op->end_handle, pdu + 2);
|
|
Packit |
34410b |
bt_uuid_to_le(&op->uuid, pdu + 4);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
|
|
Packit |
34410b |
pdu, sizeof(pdu),
|
|
Packit |
34410b |
read_by_type_cb,
|
|
Packit |
34410b |
bt_gatt_request_ref(op),
|
|
Packit |
34410b |
async_req_unref);
|
|
Packit |
34410b |
if (op->id)
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
success = true;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
done:
|
|
Packit |
34410b |
discovery_op_complete(op, success, att_ecode);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
bool bt_gatt_read_by_type(struct bt_att *att, uint16_t start, uint16_t end,
|
|
Packit |
34410b |
const bt_uuid_t *uuid,
|
|
Packit |
34410b |
bt_gatt_request_callback_t callback,
|
|
Packit |
34410b |
void *user_data,
|
|
Packit |
34410b |
bt_gatt_destroy_func_t destroy)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_request *op;
|
|
Packit |
34410b |
uint8_t pdu[4 + get_uuid_len(uuid)];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!att || !uuid || uuid->type == BT_UUID_UNSPEC)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op = new0(struct bt_gatt_request, 1);
|
|
Packit |
34410b |
op->att = att;
|
|
Packit |
34410b |
op->callback = callback;
|
|
Packit |
34410b |
op->user_data = user_data;
|
|
Packit |
34410b |
op->destroy = destroy;
|
|
Packit |
34410b |
op->start_handle = start;
|
|
Packit |
34410b |
op->end_handle = end;
|
|
Packit |
34410b |
op->uuid = *uuid;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
put_le16(start, pdu);
|
|
Packit |
34410b |
put_le16(end, pdu + 2);
|
|
Packit |
34410b |
bt_uuid_to_le(uuid, pdu + 4);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu),
|
|
Packit |
34410b |
read_by_type_cb,
|
|
Packit |
34410b |
bt_gatt_request_ref(op),
|
|
Packit |
34410b |
async_req_unref);
|
|
Packit |
34410b |
if (op->id)
|
|
Packit |
34410b |
return true;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
free(op);
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void discover_descs_cb(uint8_t opcode, const void *pdu,
|
|
Packit |
34410b |
uint16_t length, void *user_data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_request *op = user_data;
|
|
Packit |
34410b |
bool success;
|
|
Packit |
34410b |
uint8_t att_ecode = 0;
|
|
Packit |
34410b |
uint8_t format;
|
|
Packit |
34410b |
uint16_t last_handle;
|
|
Packit |
34410b |
size_t data_length;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (opcode == BT_ATT_OP_ERROR_RSP) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
att_ecode = process_error(pdu, length);
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* The PDU should contain the following data (sans opcode):
|
|
Packit |
34410b |
* - Format (1 octet)
|
|
Packit |
34410b |
* - Attr Data List (at least 4 octets):
|
|
Packit |
34410b |
* -- 2 octets: Attribute handle
|
|
Packit |
34410b |
* -- 2 or 16 octets: UUID.
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if (opcode != BT_ATT_OP_FIND_INFO_RSP || !pdu || length < 5) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
format = ((uint8_t *) pdu)[0];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (format == 0x01)
|
|
Packit |
34410b |
data_length = 4;
|
|
Packit |
34410b |
else if (format == 0x02)
|
|
Packit |
34410b |
data_length = 18;
|
|
Packit |
34410b |
else {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if ((length - 1) % data_length) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!result_append(opcode, pdu + 1, length - 1, data_length, op)) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
last_handle = get_le16(pdu + length - data_length);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/*
|
|
Packit |
34410b |
* If last handle is lower from previous start handle then it is smth
|
|
Packit |
34410b |
* wrong. Let's stop search, otherwise we might enter infinite loop.
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
if (last_handle < op->start_handle) {
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->start_handle = last_handle + 1;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (last_handle != op->end_handle) {
|
|
Packit |
34410b |
uint8_t pdu[4];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
put_le16(op->start_handle, pdu);
|
|
Packit |
34410b |
put_le16(op->end_handle, pdu + 2);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->id = bt_att_send(op->att, BT_ATT_OP_FIND_INFO_REQ,
|
|
Packit |
34410b |
pdu, sizeof(pdu),
|
|
Packit |
34410b |
discover_descs_cb,
|
|
Packit |
34410b |
bt_gatt_request_ref(op),
|
|
Packit |
34410b |
async_req_unref);
|
|
Packit |
34410b |
if (op->id)
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
success = false;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
success = true;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
done:
|
|
Packit |
34410b |
discovery_op_complete(op, success, att_ecode);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
struct bt_gatt_request *bt_gatt_discover_descriptors(struct bt_att *att,
|
|
Packit |
34410b |
uint16_t start, uint16_t end,
|
|
Packit |
34410b |
bt_gatt_request_callback_t callback,
|
|
Packit |
34410b |
void *user_data,
|
|
Packit |
34410b |
bt_gatt_destroy_func_t destroy)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
struct bt_gatt_request *op;
|
|
Packit |
34410b |
uint8_t pdu[4];
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!att)
|
|
Packit |
34410b |
return false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op = new0(struct bt_gatt_request, 1);
|
|
Packit |
34410b |
op->att = att;
|
|
Packit |
34410b |
op->callback = callback;
|
|
Packit |
34410b |
op->user_data = user_data;
|
|
Packit |
34410b |
op->destroy = destroy;
|
|
Packit |
34410b |
op->start_handle = start;
|
|
Packit |
34410b |
op->end_handle = end;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
put_le16(start, pdu);
|
|
Packit |
34410b |
put_le16(end, pdu + 2);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
op->id = bt_att_send(att, BT_ATT_OP_FIND_INFO_REQ, pdu, sizeof(pdu),
|
|
Packit |
34410b |
discover_descs_cb,
|
|
Packit |
34410b |
bt_gatt_request_ref(op),
|
|
Packit |
34410b |
async_req_unref);
|
|
Packit |
34410b |
if (!op->id) {
|
|
Packit |
34410b |
free(op);
|
|
Packit |
34410b |
return NULL;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return bt_gatt_request_ref(op);
|
|
Packit |
34410b |
}
|