2015-05-12 16:03:09 +03:00
|
|
|
/* gatt.c - Generic Attribute Profile handling */
|
|
|
|
|
|
|
|
/*
|
2016-06-10 12:10:18 +03:00
|
|
|
* Copyright (c) 2015-2016 Intel Corporation
|
2015-05-12 16:03:09 +03:00
|
|
|
*
|
2017-01-18 17:01:01 -08:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2015-05-12 16:03:09 +03:00
|
|
|
*/
|
|
|
|
|
2016-10-21 12:04:56 +02:00
|
|
|
#include <zephyr.h>
|
2015-05-12 16:03:09 +03:00
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
2015-06-17 12:42:54 +03:00
|
|
|
#include <stdbool.h>
|
2018-07-04 12:58:10 +03:00
|
|
|
#include <stdlib.h>
|
2015-09-15 10:08:04 +02:00
|
|
|
#include <atomic.h>
|
2015-05-12 16:03:09 +03:00
|
|
|
#include <misc/byteorder.h>
|
|
|
|
#include <misc/util.h>
|
|
|
|
|
2018-05-08 14:58:00 +03:00
|
|
|
#include <settings/settings.h>
|
|
|
|
|
2019-02-01 16:07:41 +02:00
|
|
|
#include <tinycrypt/constants.h>
|
|
|
|
#include <tinycrypt/utils.h>
|
|
|
|
#include <tinycrypt/aes.h>
|
|
|
|
#include <tinycrypt/cmac_mode.h>
|
|
|
|
#include <tinycrypt/ccm_mode.h>
|
|
|
|
|
2015-05-12 16:03:09 +03:00
|
|
|
#include <bluetooth/hci.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
2018-11-26 09:10:06 +01:00
|
|
|
#include <bluetooth/conn.h>
|
2015-05-12 16:03:09 +03:00
|
|
|
#include <bluetooth/uuid.h>
|
|
|
|
#include <bluetooth/gatt.h>
|
2016-10-27 16:55:01 +03:00
|
|
|
#include <bluetooth/hci_driver.h>
|
2015-05-12 16:03:09 +03:00
|
|
|
|
2017-08-09 09:21:11 +03:00
|
|
|
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_GATT)
|
2018-07-17 10:35:52 +03:00
|
|
|
#define LOG_MODULE_NAME bt_gatt
|
2017-05-10 16:27:16 +02:00
|
|
|
#include "common/log.h"
|
|
|
|
|
2015-05-29 13:03:57 +03:00
|
|
|
#include "hci_core.h"
|
2015-06-15 11:05:35 +03:00
|
|
|
#include "conn_internal.h"
|
2015-06-17 12:42:54 +03:00
|
|
|
#include "keys.h"
|
2015-10-05 13:53:03 +03:00
|
|
|
#include "l2cap_internal.h"
|
2016-02-17 11:18:10 +02:00
|
|
|
#include "att_internal.h"
|
2015-07-30 16:06:06 +03:00
|
|
|
#include "smp.h"
|
2018-05-08 14:58:00 +03:00
|
|
|
#include "settings.h"
|
2015-09-14 17:48:26 +02:00
|
|
|
#include "gatt_internal.h"
|
2015-05-29 13:03:57 +03:00
|
|
|
|
2018-10-08 14:33:40 +02:00
|
|
|
#define SC_TIMEOUT K_MSEC(10)
|
|
|
|
#define CCC_STORE_DELAY K_SECONDS(1)
|
2017-07-07 15:10:27 +03:00
|
|
|
|
2019-02-01 16:07:41 +02:00
|
|
|
#define DB_HASH_TIMEOUT K_MSEC(10)
|
|
|
|
|
2018-05-08 14:58:00 +03:00
|
|
|
/* Persistent storage format for GATT CCC */
|
|
|
|
struct ccc_store {
|
|
|
|
u16_t handle;
|
|
|
|
u16_t value;
|
|
|
|
};
|
|
|
|
|
2017-08-09 09:21:11 +03:00
|
|
|
#if defined(CONFIG_BT_GATT_CLIENT)
|
2017-02-03 14:19:08 +02:00
|
|
|
static sys_slist_t subscriptions;
|
2017-08-09 09:21:11 +03:00
|
|
|
#endif /* CONFIG_BT_GATT_CLIENT */
|
2015-07-22 10:21:47 +03:00
|
|
|
|
2017-08-09 09:21:11 +03:00
|
|
|
static const u16_t gap_appearance = CONFIG_BT_DEVICE_APPEARANCE;
|
2017-06-02 15:51:54 +03:00
|
|
|
|
2017-03-24 11:37:32 +02:00
|
|
|
static sys_slist_t db;
|
2018-09-07 12:44:40 +03:00
|
|
|
static atomic_t init;
|
2016-01-14 11:09:04 -03:00
|
|
|
|
2017-06-02 15:51:54 +03:00
|
|
|
static ssize_t read_name(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
|
|
|
void *buf, u16_t len, u16_t offset)
|
|
|
|
{
|
2018-07-31 19:57:08 +02:00
|
|
|
const char *name = bt_get_name();
|
2018-07-09 16:14:56 +03:00
|
|
|
|
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, name,
|
|
|
|
strlen(name));
|
|
|
|
}
|
|
|
|
|
2018-07-31 19:57:08 +02:00
|
|
|
#if defined(CONFIG_BT_DEVICE_NAME_GATT_WRITABLE)
|
2018-07-09 16:14:56 +03:00
|
|
|
|
|
|
|
static ssize_t write_name(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
|
|
|
const void *buf, u16_t len, u16_t offset,
|
|
|
|
u8_t flags)
|
|
|
|
{
|
|
|
|
char value[CONFIG_BT_DEVICE_NAME_MAX] = {};
|
|
|
|
|
|
|
|
if (offset) {
|
|
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len >= sizeof(value)) {
|
|
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(value, buf, len);
|
|
|
|
|
|
|
|
bt_set_name(value);
|
|
|
|
|
|
|
|
return len;
|
2017-06-02 15:51:54 +03:00
|
|
|
}
|
|
|
|
|
2018-07-31 19:57:08 +02:00
|
|
|
#endif /* CONFIG_BT_DEVICE_NAME_GATT_WRITABLE */
|
2018-07-09 16:14:56 +03:00
|
|
|
|
2017-06-02 15:51:54 +03:00
|
|
|
static ssize_t read_appearance(struct bt_conn *conn,
|
|
|
|
const struct bt_gatt_attr *attr, void *buf,
|
|
|
|
u16_t len, u16_t offset)
|
|
|
|
{
|
|
|
|
u16_t appearance = sys_cpu_to_le16(gap_appearance);
|
|
|
|
|
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &appearance,
|
|
|
|
sizeof(appearance));
|
|
|
|
}
|
|
|
|
|
2018-09-24 11:20:20 +02:00
|
|
|
#if defined (CONFIG_BT_GAP_PERIPHERAL_PREF_PARAMS)
|
2019-04-02 14:32:31 +03:00
|
|
|
/* This checks if the range entered is valid */
|
|
|
|
BUILD_ASSERT(!(CONFIG_BT_PERIPHERAL_PREF_MIN_INT > 3200 &&
|
|
|
|
CONFIG_BT_PERIPHERAL_PREF_MIN_INT < 0xffff));
|
|
|
|
BUILD_ASSERT(!(CONFIG_BT_PERIPHERAL_PREF_MAX_INT > 3200 &&
|
|
|
|
CONFIG_BT_PERIPHERAL_PREF_MAX_INT < 0xffff));
|
|
|
|
BUILD_ASSERT(!(CONFIG_BT_PERIPHERAL_PREF_TIMEOUT > 3200 &&
|
|
|
|
CONFIG_BT_PERIPHERAL_PREF_TIMEOUT < 0xffff));
|
|
|
|
BUILD_ASSERT((CONFIG_BT_PERIPHERAL_PREF_MIN_INT == 0xffff) ||
|
|
|
|
(CONFIG_BT_PERIPHERAL_PREF_MIN_INT <=
|
|
|
|
CONFIG_BT_PERIPHERAL_PREF_MAX_INT));
|
|
|
|
|
2018-09-24 11:20:20 +02:00
|
|
|
static ssize_t read_ppcp(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
|
|
|
void *buf, u16_t len, u16_t offset)
|
|
|
|
{
|
|
|
|
struct __packed {
|
|
|
|
uint16_t min_int;
|
|
|
|
uint16_t max_int;
|
|
|
|
uint16_t latency;
|
|
|
|
uint16_t timeout;
|
|
|
|
} ppcp;
|
|
|
|
|
|
|
|
ppcp.min_int = sys_cpu_to_le16(CONFIG_BT_PERIPHERAL_PREF_MIN_INT);
|
|
|
|
ppcp.max_int = sys_cpu_to_le16(CONFIG_BT_PERIPHERAL_PREF_MAX_INT);
|
|
|
|
ppcp.latency = sys_cpu_to_le16(CONFIG_BT_PERIPHERAL_PREF_SLAVE_LATENCY);
|
|
|
|
ppcp.timeout = sys_cpu_to_le16(CONFIG_BT_PERIPHERAL_PREF_TIMEOUT);
|
|
|
|
|
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &ppcp,
|
|
|
|
sizeof(ppcp));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-09-07 15:16:33 +02:00
|
|
|
#if defined(CONFIG_BT_CENTRAL) && defined(CONFIG_BT_PRIVACY)
|
|
|
|
static ssize_t read_central_addr_res(struct bt_conn *conn,
|
|
|
|
const struct bt_gatt_attr *attr, void *buf,
|
|
|
|
u16_t len, u16_t offset)
|
|
|
|
{
|
|
|
|
u8_t central_addr_res = BT_GATT_CENTRAL_ADDR_RES_SUPP;
|
|
|
|
|
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset,
|
|
|
|
¢ral_addr_res, sizeof(central_addr_res));
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_BT_CENTRAL && CONFIG_BT_PRIVACY */
|
|
|
|
|
2017-06-02 15:51:54 +03:00
|
|
|
static struct bt_gatt_attr gap_attrs[] = {
|
|
|
|
BT_GATT_PRIMARY_SERVICE(BT_UUID_GAP),
|
2018-07-31 19:57:08 +02:00
|
|
|
#if defined(CONFIG_BT_DEVICE_NAME_GATT_WRITABLE)
|
2018-07-09 16:14:56 +03:00
|
|
|
/* Require pairing for writes to device name */
|
|
|
|
BT_GATT_CHARACTERISTIC(BT_UUID_GAP_DEVICE_NAME,
|
|
|
|
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
|
|
|
|
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE_ENCRYPT,
|
|
|
|
read_name, write_name, bt_dev.name),
|
|
|
|
#else
|
2018-05-11 21:53:02 +03:00
|
|
|
BT_GATT_CHARACTERISTIC(BT_UUID_GAP_DEVICE_NAME, BT_GATT_CHRC_READ,
|
2018-07-31 19:57:08 +02:00
|
|
|
BT_GATT_PERM_READ, read_name, NULL, NULL),
|
|
|
|
#endif /* CONFIG_BT_DEVICE_NAME_GATT_WRITABLE */
|
2018-05-11 21:53:02 +03:00
|
|
|
BT_GATT_CHARACTERISTIC(BT_UUID_GAP_APPEARANCE, BT_GATT_CHRC_READ,
|
|
|
|
BT_GATT_PERM_READ, read_appearance, NULL, NULL),
|
2018-09-07 15:16:33 +02:00
|
|
|
#if defined(CONFIG_BT_CENTRAL) && defined(CONFIG_BT_PRIVACY)
|
|
|
|
BT_GATT_CHARACTERISTIC(BT_UUID_CENTRAL_ADDR_RES,
|
|
|
|
BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
|
|
|
|
read_central_addr_res, NULL, NULL),
|
|
|
|
#endif /* CONFIG_BT_CENTRAL && CONFIG_BT_PRIVACY */
|
2018-09-24 11:20:20 +02:00
|
|
|
#if defined(CONFIG_BT_GAP_PERIPHERAL_PREF_PARAMS)
|
|
|
|
BT_GATT_CHARACTERISTIC(BT_UUID_GAP_PPCP, BT_GATT_CHRC_READ,
|
|
|
|
BT_GATT_PERM_READ, read_ppcp, NULL, NULL),
|
|
|
|
#endif
|
2017-06-02 15:51:54 +03:00
|
|
|
};
|
|
|
|
|
2017-06-12 00:26:19 +03:00
|
|
|
static struct bt_gatt_service gap_svc = BT_GATT_SERVICE(gap_attrs);
|
|
|
|
|
2017-07-04 16:46:13 +03:00
|
|
|
static struct bt_gatt_ccc_cfg sc_ccc_cfg[BT_GATT_CCC_MAX] = {};
|
2017-06-11 13:30:31 +03:00
|
|
|
|
|
|
|
static void sc_ccc_cfg_changed(const struct bt_gatt_attr *attr,
|
|
|
|
u16_t value)
|
|
|
|
{
|
|
|
|
BT_DBG("value 0x%04x", value);
|
|
|
|
}
|
|
|
|
|
2019-02-05 14:28:10 +02:00
|
|
|
enum {
|
|
|
|
CF_CHANGE_AWARE, /* Client is changed aware */
|
|
|
|
CF_OUT_OF_SYNC, /* Client is out of sync */
|
|
|
|
|
|
|
|
/* Total number of flags - must be at the end of the enum */
|
|
|
|
CF_NUM_FLAGS,
|
|
|
|
};
|
|
|
|
|
|
|
|
#define CF_ROBUST_CACHING(_cfg) (_cfg->data[0] & BIT(0))
|
|
|
|
|
2019-01-31 17:05:05 +02:00
|
|
|
struct gatt_cf_cfg {
|
|
|
|
u8_t id;
|
|
|
|
bt_addr_le_t peer;
|
|
|
|
u8_t data[1];
|
2019-02-05 14:28:10 +02:00
|
|
|
ATOMIC_DEFINE(flags, CF_NUM_FLAGS);
|
2019-01-31 17:05:05 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
#if defined(CONFIG_BT_GATT_CACHING)
|
|
|
|
#define CF_CFG_MAX (CONFIG_BT_MAX_PAIRED + CONFIG_BT_MAX_CONN)
|
|
|
|
static struct gatt_cf_cfg cf_cfg[CF_CFG_MAX] = {};
|
|
|
|
|
2019-02-05 15:06:59 +02:00
|
|
|
static struct gatt_cf_cfg *find_cf_cfg(struct bt_conn *conn)
|
2019-01-31 17:05:05 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cf_cfg); i++) {
|
2019-02-05 15:06:59 +02:00
|
|
|
if (!conn) {
|
|
|
|
if (!bt_addr_le_cmp(&cf_cfg[i].peer, BT_ADDR_LE_ANY)) {
|
|
|
|
return &cf_cfg[i];
|
|
|
|
}
|
|
|
|
} else if (!bt_conn_addr_le_cmp(conn, &cf_cfg[i].peer)) {
|
2019-01-31 17:05:05 +02:00
|
|
|
return &cf_cfg[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t cf_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
|
|
|
void *buf, u16_t len, u16_t offset)
|
|
|
|
{
|
|
|
|
struct gatt_cf_cfg *cfg;
|
|
|
|
u8_t data[1] = {};
|
|
|
|
|
2019-02-05 15:06:59 +02:00
|
|
|
cfg = find_cf_cfg(conn);
|
2019-01-31 17:05:05 +02:00
|
|
|
if (cfg) {
|
|
|
|
memcpy(data, cfg->data, sizeof(data));
|
|
|
|
}
|
|
|
|
|
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, data,
|
|
|
|
sizeof(data));
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool cf_set_value(struct gatt_cf_cfg *cfg, const u8_t *value, u16_t len)
|
|
|
|
{
|
|
|
|
u16_t i;
|
2019-03-26 19:57:45 -06:00
|
|
|
u8_t last_byte = 1U;
|
|
|
|
u8_t last_bit = 1U;
|
2019-01-31 17:05:05 +02:00
|
|
|
|
|
|
|
/* Validate the bits */
|
2019-03-26 19:57:45 -06:00
|
|
|
for (i = 0U; i < len && i < last_byte; i++) {
|
2019-01-31 17:05:05 +02:00
|
|
|
u8_t chg_bits = value[i] ^ cfg->data[i];
|
|
|
|
u8_t bit;
|
|
|
|
|
2019-03-26 19:57:45 -06:00
|
|
|
for (bit = 0U; bit < last_bit; bit++) {
|
2019-01-31 17:05:05 +02:00
|
|
|
/* A client shall never clear a bit it has set */
|
|
|
|
if ((BIT(bit) & chg_bits) &&
|
|
|
|
(BIT(bit) & cfg->data[i])) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the bits for each octect */
|
2019-03-26 19:57:45 -06:00
|
|
|
for (i = 0U; i < len && i < last_byte; i++) {
|
2019-02-05 14:28:10 +02:00
|
|
|
cfg->data[i] |= value[i] & ((1 << last_bit) - 1);
|
2019-01-31 17:05:05 +02:00
|
|
|
BT_DBG("byte %u: data 0x%02x value 0x%02x", i, cfg->data[i],
|
|
|
|
value[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t cf_write(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
|
|
|
const void *buf, u16_t len, u16_t offset, u8_t flags)
|
|
|
|
{
|
|
|
|
struct gatt_cf_cfg *cfg;
|
|
|
|
const u8_t *value = buf;
|
|
|
|
|
|
|
|
if (offset > sizeof(cfg->data)) {
|
|
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset + len > sizeof(cfg->data)) {
|
|
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
|
|
|
}
|
|
|
|
|
2019-02-05 15:06:59 +02:00
|
|
|
cfg = find_cf_cfg(conn);
|
2019-01-31 17:05:05 +02:00
|
|
|
if (!cfg) {
|
2019-02-05 15:06:59 +02:00
|
|
|
cfg = find_cf_cfg(NULL);
|
2019-01-31 17:05:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg) {
|
|
|
|
BT_WARN("No space to store Client Supported Features");
|
|
|
|
return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("handle 0x%04x len %u", attr->handle, len);
|
|
|
|
|
|
|
|
if (!cf_set_value(cfg, value, len)) {
|
|
|
|
return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
|
|
|
|
}
|
|
|
|
|
|
|
|
bt_addr_le_copy(&cfg->peer, &conn->le.dst);
|
2019-02-05 14:28:10 +02:00
|
|
|
atomic_set_bit(cfg->flags, CF_CHANGE_AWARE);
|
2019-01-31 17:05:05 +02:00
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
2019-02-01 16:07:41 +02:00
|
|
|
|
|
|
|
static u8_t db_hash[16];
|
|
|
|
struct k_delayed_work db_hash_work;
|
|
|
|
|
|
|
|
struct gen_hash_state {
|
|
|
|
struct tc_cmac_struct state;
|
|
|
|
int err;
|
|
|
|
};
|
|
|
|
|
|
|
|
static u8_t gen_hash_m(const struct bt_gatt_attr *attr, void *user_data)
|
|
|
|
{
|
|
|
|
struct gen_hash_state *state = user_data;
|
|
|
|
struct bt_uuid_16 *u16;
|
|
|
|
u8_t data[16];
|
2019-03-27 23:06:15 +01:00
|
|
|
ssize_t len;
|
2019-02-01 16:07:41 +02:00
|
|
|
u16_t value;
|
|
|
|
|
|
|
|
if (attr->uuid->type != BT_UUID_TYPE_16)
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
|
|
|
|
u16 = (struct bt_uuid_16 *)attr->uuid;
|
|
|
|
|
|
|
|
switch (u16->val) {
|
|
|
|
/* Attributes to hash: handle + UUID + value */
|
|
|
|
case 0x2800: /* GATT Primary Service */
|
|
|
|
case 0x2801: /* GATT Secondary Service */
|
|
|
|
case 0x2802: /* GATT Include Service */
|
|
|
|
case 0x2803: /* GATT Characteristic */
|
|
|
|
case 0x2900: /* GATT Characteristic Extended Properties */
|
|
|
|
value = sys_cpu_to_le16(attr->handle);
|
|
|
|
if (tc_cmac_update(&state->state, (uint8_t *)&value,
|
|
|
|
sizeof(attr->handle)) == TC_CRYPTO_FAIL) {
|
|
|
|
state->err = -EINVAL;
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
value = sys_cpu_to_le16(u16->val);
|
|
|
|
if (tc_cmac_update(&state->state, (uint8_t *)&value,
|
|
|
|
sizeof(u16->val)) == TC_CRYPTO_FAIL) {
|
|
|
|
state->err = -EINVAL;
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = attr->read(NULL, attr, data, sizeof(data), 0);
|
|
|
|
if (len < 0) {
|
|
|
|
state->err = len;
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tc_cmac_update(&state->state, data, len) ==
|
|
|
|
TC_CRYPTO_FAIL) {
|
|
|
|
state->err = -EINVAL;
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
/* Attributes to hash: handle + UUID */
|
|
|
|
case 0x2901: /* GATT Characteristic User Descriptor */
|
|
|
|
case 0x2902: /* GATT Client Characteristic Configuration */
|
|
|
|
case 0x2903: /* GATT Server Characteristic Configuration */
|
|
|
|
case 0x2904: /* GATT Characteristic Presentation Format */
|
|
|
|
case 0x2905: /* GATT Characteristic Aggregated Format */
|
|
|
|
value = sys_cpu_to_le16(attr->handle);
|
|
|
|
if (tc_cmac_update(&state->state, (uint8_t *)&value,
|
|
|
|
sizeof(attr->handle)) == TC_CRYPTO_FAIL) {
|
|
|
|
state->err = -EINVAL;
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
value = sys_cpu_to_le16(u16->val);
|
|
|
|
if (tc_cmac_update(&state->state, (uint8_t *)&value,
|
|
|
|
sizeof(u16->val)) == TC_CRYPTO_FAIL) {
|
|
|
|
state->err = -EINVAL;
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void db_hash_gen(void)
|
|
|
|
{
|
|
|
|
u8_t key[16] = {};
|
|
|
|
struct tc_aes_key_sched_struct sched;
|
|
|
|
struct gen_hash_state state;
|
|
|
|
|
|
|
|
if (tc_cmac_setup(&state.state, key, &sched) == TC_CRYPTO_FAIL) {
|
|
|
|
BT_ERR("Unable to setup AES CMAC");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bt_gatt_foreach_attr(0x0001, 0xffff, gen_hash_m, &state);
|
|
|
|
|
|
|
|
if (tc_cmac_final(db_hash, &state.state) == TC_CRYPTO_FAIL) {
|
|
|
|
BT_ERR("Unable to calculate hash");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_HEXDUMP_DBG(db_hash, sizeof(db_hash), "Hash: ");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void db_hash_process(struct k_work *work)
|
|
|
|
{
|
|
|
|
db_hash_gen();
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t db_hash_read(struct bt_conn *conn,
|
|
|
|
const struct bt_gatt_attr *attr,
|
|
|
|
void *buf, u16_t len, u16_t offset)
|
|
|
|
{
|
|
|
|
/* Check if db_hash is already pending in which case it shall be
|
|
|
|
* generated immediately instead of waiting the work to complete.
|
|
|
|
*/
|
|
|
|
if (k_delayed_work_remaining_get(&db_hash_work)) {
|
|
|
|
k_delayed_work_cancel(&db_hash_work);
|
|
|
|
db_hash_gen();
|
|
|
|
}
|
|
|
|
|
2019-02-05 14:28:10 +02:00
|
|
|
/* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G page 2347:
|
|
|
|
* 2.5.2.1 Robust Caching
|
|
|
|
* A connected client becomes change-aware when...
|
|
|
|
* The client reads the Database Hash characteristic and then the server
|
|
|
|
* receives another ATT request from the client.
|
|
|
|
*/
|
|
|
|
bt_gatt_change_aware(conn, true);
|
|
|
|
|
2019-02-01 16:07:41 +02:00
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, db_hash,
|
|
|
|
sizeof(db_hash));
|
|
|
|
}
|
2019-02-05 15:06:59 +02:00
|
|
|
|
2019-02-07 12:38:02 +02:00
|
|
|
static void clear_cf_cfg(struct gatt_cf_cfg *cfg)
|
|
|
|
{
|
|
|
|
bt_addr_le_copy(&cfg->peer, BT_ADDR_LE_ANY);
|
|
|
|
memset(cfg->data, 0, sizeof(cfg->data));
|
|
|
|
atomic_set(cfg->flags, 0);
|
|
|
|
}
|
|
|
|
|
2019-02-05 15:06:59 +02:00
|
|
|
static void remove_cf_cfg(struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
struct gatt_cf_cfg *cfg;
|
|
|
|
|
|
|
|
cfg = find_cf_cfg(conn);
|
|
|
|
if (!cfg) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G page 2405:
|
|
|
|
* For clients with a trusted relationship, the characteristic value
|
|
|
|
* shall be persistent across connections. For clients without a
|
|
|
|
* trusted relationship the characteristic value shall be set to the
|
|
|
|
* default value at each connection.
|
|
|
|
*/
|
|
|
|
if (!bt_addr_le_is_bonded(conn->id, &conn->le.dst)) {
|
2019-02-07 12:38:02 +02:00
|
|
|
clear_cf_cfg(cfg);
|
2019-02-05 15:06:59 +02:00
|
|
|
} else {
|
|
|
|
/* Update address in case it has changed */
|
|
|
|
bt_addr_le_copy(&cfg->peer, &conn->le.dst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_BT_GATT_CACHING */
|
2019-01-31 17:05:05 +02:00
|
|
|
|
2017-06-11 13:30:31 +03:00
|
|
|
static struct bt_gatt_attr gatt_attrs[] = {
|
|
|
|
BT_GATT_PRIMARY_SERVICE(BT_UUID_GATT),
|
2018-11-22 13:37:34 +02:00
|
|
|
/* Bluetooth 5.0, Vol3 Part G:
|
|
|
|
* The Service Changed characteristic Attribute Handle on the server
|
|
|
|
* shall not change if the server has a trusted relationship with any
|
|
|
|
* client.
|
|
|
|
*/
|
2018-05-11 21:53:02 +03:00
|
|
|
BT_GATT_CHARACTERISTIC(BT_UUID_GATT_SC, BT_GATT_CHRC_INDICATE,
|
|
|
|
BT_GATT_PERM_NONE, NULL, NULL, NULL),
|
2017-06-11 13:30:31 +03:00
|
|
|
BT_GATT_CCC(sc_ccc_cfg, sc_ccc_cfg_changed),
|
2019-01-31 17:05:05 +02:00
|
|
|
#if defined(CONFIG_BT_GATT_CACHING)
|
|
|
|
BT_GATT_CHARACTERISTIC(BT_UUID_GATT_CLIENT_FEATURES,
|
|
|
|
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
|
|
|
|
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
|
|
|
|
cf_read, cf_write, NULL),
|
2019-02-01 16:07:41 +02:00
|
|
|
BT_GATT_CHARACTERISTIC(BT_UUID_GATT_DB_HASH,
|
|
|
|
BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
|
|
|
|
db_hash_read, NULL, NULL),
|
2019-01-31 17:05:05 +02:00
|
|
|
#endif /* COFNIG_BT_GATT_CACHING */
|
2019-02-01 16:07:41 +02:00
|
|
|
|
2017-06-11 13:30:31 +03:00
|
|
|
};
|
|
|
|
|
2017-06-12 00:26:19 +03:00
|
|
|
static struct bt_gatt_service gatt_svc = BT_GATT_SERVICE(gatt_attrs);
|
|
|
|
|
|
|
|
static int gatt_register(struct bt_gatt_service *svc)
|
2015-05-12 16:03:09 +03:00
|
|
|
{
|
2017-06-12 00:26:19 +03:00
|
|
|
struct bt_gatt_service *last;
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t handle;
|
2017-06-12 00:26:19 +03:00
|
|
|
struct bt_gatt_attr *attrs = svc->attrs;
|
|
|
|
u16_t count = svc->attr_count;
|
2017-03-24 11:37:32 +02:00
|
|
|
|
2017-04-10 20:30:57 +03:00
|
|
|
if (sys_slist_is_empty(&db)) {
|
2018-11-29 11:23:03 -08:00
|
|
|
handle = 0U;
|
2015-10-22 20:58:20 +03:00
|
|
|
goto populate;
|
|
|
|
}
|
|
|
|
|
2017-03-24 11:37:32 +02:00
|
|
|
last = SYS_SLIST_PEEK_TAIL_CONTAINER(&db, last, node);
|
2017-06-12 00:26:19 +03:00
|
|
|
handle = last->attrs[last->attr_count - 1].handle;
|
2015-10-22 20:58:20 +03:00
|
|
|
|
|
|
|
populate:
|
2017-03-24 11:37:32 +02:00
|
|
|
/* Populate the handles and append them to the list */
|
2015-10-22 20:58:20 +03:00
|
|
|
for (; attrs && count; attrs++, count--) {
|
|
|
|
if (!attrs->handle) {
|
|
|
|
/* Allocate handle if not set already */
|
|
|
|
attrs->handle = ++handle;
|
|
|
|
} else if (attrs->handle > handle) {
|
|
|
|
/* Use existing handle if valid */
|
|
|
|
handle = attrs->handle;
|
|
|
|
} else {
|
|
|
|
/* Service has conflicting handles */
|
|
|
|
BT_ERR("Unable to register handle 0x%04x",
|
|
|
|
attrs->handle);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-03-24 11:37:32 +02:00
|
|
|
BT_DBG("attr %p handle 0x%04x uuid %s perm 0x%02x",
|
|
|
|
attrs, attrs->handle, bt_uuid_str(attrs->uuid),
|
|
|
|
attrs->perm);
|
2015-10-22 20:58:20 +03:00
|
|
|
}
|
|
|
|
|
2017-06-12 00:26:19 +03:00
|
|
|
sys_slist_append(&db, &svc->node);
|
2017-03-24 11:37:32 +02:00
|
|
|
|
2015-10-22 20:58:20 +03:00
|
|
|
return 0;
|
2015-05-12 16:03:09 +03:00
|
|
|
}
|
|
|
|
|
2017-07-07 15:10:27 +03:00
|
|
|
enum {
|
|
|
|
SC_RANGE_CHANGED, /* SC range changed */
|
|
|
|
SC_INDICATE_PENDING, /* SC indicate pending */
|
|
|
|
|
|
|
|
/* Total number of flags - must be at the end of the enum */
|
|
|
|
SC_NUM_FLAGS,
|
|
|
|
};
|
2017-06-02 15:51:54 +03:00
|
|
|
|
2017-07-07 15:10:27 +03:00
|
|
|
static struct gatt_sc {
|
|
|
|
struct bt_gatt_indicate_params params;
|
|
|
|
u16_t start;
|
|
|
|
u16_t end;
|
|
|
|
struct k_delayed_work work;
|
|
|
|
ATOMIC_DEFINE(flags, SC_NUM_FLAGS);
|
|
|
|
} gatt_sc;
|
2017-06-11 13:44:14 +03:00
|
|
|
|
|
|
|
static void sc_indicate_rsp(struct bt_conn *conn,
|
|
|
|
const struct bt_gatt_attr *attr, u8_t err)
|
|
|
|
{
|
2019-02-05 14:28:10 +02:00
|
|
|
#if defined(CONFIG_BT_GATT_CACHING)
|
|
|
|
struct gatt_cf_cfg *cfg;
|
|
|
|
#endif
|
|
|
|
|
2017-06-11 13:44:14 +03:00
|
|
|
BT_DBG("err 0x%02x", err);
|
|
|
|
|
2017-07-07 15:10:27 +03:00
|
|
|
atomic_clear_bit(gatt_sc.flags, SC_INDICATE_PENDING);
|
|
|
|
|
|
|
|
/* Check if there is new change in the meantime */
|
|
|
|
if (atomic_test_bit(gatt_sc.flags, SC_RANGE_CHANGED)) {
|
|
|
|
/* Reschedule without any delay since it is waiting already */
|
|
|
|
k_delayed_work_submit(&gatt_sc.work, K_NO_WAIT);
|
|
|
|
}
|
2019-02-05 14:28:10 +02:00
|
|
|
|
|
|
|
#if defined(CONFIG_BT_GATT_CACHING)
|
|
|
|
/* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G page 2347:
|
|
|
|
* 2.5.2.1 Robust Caching
|
|
|
|
* A connected client becomes change-aware when...
|
|
|
|
* The client receives and confirms a Service Changed indication.
|
|
|
|
*/
|
2019-02-05 15:06:59 +02:00
|
|
|
cfg = find_cf_cfg(conn);
|
2019-02-05 14:28:10 +02:00
|
|
|
if (cfg && CF_ROBUST_CACHING(cfg)) {
|
|
|
|
atomic_set_bit(cfg->flags, CF_CHANGE_AWARE);
|
|
|
|
BT_DBG("%s change-aware", bt_addr_le_str(&cfg->peer));
|
|
|
|
}
|
|
|
|
#endif
|
2017-06-11 13:44:14 +03:00
|
|
|
}
|
|
|
|
|
2017-07-07 15:10:27 +03:00
|
|
|
static void sc_process(struct k_work *work)
|
2017-06-11 13:44:14 +03:00
|
|
|
{
|
2017-07-07 15:10:27 +03:00
|
|
|
struct gatt_sc *sc = CONTAINER_OF(work, struct gatt_sc, work);
|
2017-06-11 13:44:14 +03:00
|
|
|
u16_t sc_range[2];
|
|
|
|
|
2017-07-07 15:10:27 +03:00
|
|
|
__ASSERT(!atomic_test_bit(sc->flags, SC_INDICATE_PENDING),
|
|
|
|
"Indicate already pending");
|
|
|
|
|
|
|
|
BT_DBG("start 0x%04x end 0x%04x", sc->start, sc->end);
|
|
|
|
|
|
|
|
sc_range[0] = sys_cpu_to_le16(sc->start);
|
|
|
|
sc_range[1] = sys_cpu_to_le16(sc->end);
|
|
|
|
|
|
|
|
atomic_clear_bit(sc->flags, SC_RANGE_CHANGED);
|
2018-11-29 11:23:03 -08:00
|
|
|
sc->start = 0U;
|
|
|
|
sc->end = 0U;
|
2017-07-07 15:10:27 +03:00
|
|
|
|
|
|
|
sc->params.attr = &gatt_attrs[2];
|
|
|
|
sc->params.func = sc_indicate_rsp;
|
|
|
|
sc->params.data = &sc_range[0];
|
|
|
|
sc->params.len = sizeof(sc_range);
|
|
|
|
|
2017-07-21 12:55:33 +03:00
|
|
|
if (bt_gatt_indicate(NULL, &sc->params)) {
|
2017-07-07 15:10:27 +03:00
|
|
|
/* No connections to indicate */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
atomic_set_bit(sc->flags, SC_INDICATE_PENDING);
|
|
|
|
}
|
|
|
|
|
2018-10-08 14:33:40 +02:00
|
|
|
#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE)
|
|
|
|
static struct gatt_ccc_store {
|
|
|
|
struct bt_conn *conn_list[CONFIG_BT_MAX_CONN];
|
|
|
|
struct k_delayed_work work;
|
|
|
|
} gatt_ccc_store;
|
|
|
|
|
|
|
|
static bool gatt_ccc_conn_is_queued(struct bt_conn *conn)
|
|
|
|
{
|
2018-11-26 09:10:06 +01:00
|
|
|
return (conn == gatt_ccc_store.conn_list[bt_conn_index(conn)]);
|
2018-10-08 14:33:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void gatt_ccc_conn_unqueue(struct bt_conn *conn)
|
|
|
|
{
|
2018-11-26 09:10:06 +01:00
|
|
|
u8_t index = bt_conn_index(conn);
|
2018-10-08 14:33:40 +02:00
|
|
|
|
|
|
|
if (gatt_ccc_store.conn_list[index] != NULL) {
|
|
|
|
bt_conn_unref(gatt_ccc_store.conn_list[index]);
|
|
|
|
gatt_ccc_store.conn_list[index] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool gatt_ccc_conn_queue_is_empty(void)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < CONFIG_BT_MAX_CONN; i++) {
|
|
|
|
if (gatt_ccc_store.conn_list[i]) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ccc_delayed_store(struct k_work *work)
|
|
|
|
{
|
|
|
|
struct gatt_ccc_store *ccc_store =
|
|
|
|
CONTAINER_OF(work, struct gatt_ccc_store, work);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < CONFIG_BT_MAX_CONN; i++) {
|
|
|
|
struct bt_conn *conn = ccc_store->conn_list[i];
|
|
|
|
|
|
|
|
if (!conn) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bt_addr_le_is_bonded(conn->id, &conn->le.dst)) {
|
|
|
|
bt_gatt_store_ccc(conn->id, &conn->le.dst);
|
|
|
|
bt_conn_unref(conn);
|
|
|
|
ccc_store->conn_list[i] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-07-07 15:10:27 +03:00
|
|
|
void bt_gatt_init(void)
|
|
|
|
{
|
2018-09-07 12:44:40 +03:00
|
|
|
if (!atomic_cas(&init, 0, 1)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-07-07 15:10:27 +03:00
|
|
|
/* Register mandatory services */
|
|
|
|
gatt_register(&gatt_svc);
|
2018-11-22 13:37:34 +02:00
|
|
|
gatt_register(&gap_svc);
|
2017-07-07 15:10:27 +03:00
|
|
|
|
2019-02-01 16:07:41 +02:00
|
|
|
#if defined(CONFIG_BT_GATT_CACHING)
|
|
|
|
k_delayed_work_init(&db_hash_work, db_hash_process);
|
|
|
|
db_hash_gen();
|
|
|
|
#endif /* COFNIG_BT_GATT_CACHING */
|
|
|
|
|
2017-07-07 15:10:27 +03:00
|
|
|
k_delayed_work_init(&gatt_sc.work, sc_process);
|
2018-10-08 14:33:40 +02:00
|
|
|
#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE)
|
|
|
|
k_delayed_work_init(&gatt_ccc_store.work, ccc_delayed_store);
|
|
|
|
#endif
|
2017-07-07 15:10:27 +03:00
|
|
|
}
|
|
|
|
|
2017-07-21 12:49:51 +03:00
|
|
|
static bool update_range(u16_t *start, u16_t *end, u16_t new_start,
|
|
|
|
u16_t new_end)
|
2017-07-07 15:10:27 +03:00
|
|
|
{
|
2017-07-21 12:49:51 +03:00
|
|
|
BT_DBG("start 0x%04x end 0x%04x new_start 0x%04x new_end 0x%04x",
|
|
|
|
*start, *end, new_start, new_end);
|
|
|
|
|
|
|
|
/* Check if inside existing range */
|
|
|
|
if (new_start >= *start && new_end <= *end) {
|
|
|
|
return false;
|
2017-06-11 13:44:14 +03:00
|
|
|
}
|
|
|
|
|
2017-07-21 12:49:51 +03:00
|
|
|
/* Update range */
|
|
|
|
if (*start > new_start) {
|
|
|
|
*start = new_start;
|
2017-07-07 15:10:27 +03:00
|
|
|
}
|
|
|
|
|
2017-07-21 12:49:51 +03:00
|
|
|
if (*end < new_end) {
|
|
|
|
*end = new_end;
|
2017-07-07 15:10:27 +03:00
|
|
|
}
|
|
|
|
|
2017-07-21 12:49:51 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sc_indicate(struct gatt_sc *sc, uint16_t start, uint16_t end)
|
|
|
|
{
|
|
|
|
if (!atomic_test_and_set_bit(sc->flags, SC_RANGE_CHANGED)) {
|
|
|
|
sc->start = start;
|
|
|
|
sc->end = end;
|
|
|
|
goto submit;
|
2017-07-07 15:10:27 +03:00
|
|
|
}
|
2017-06-11 13:44:14 +03:00
|
|
|
|
2017-07-21 12:49:51 +03:00
|
|
|
if (!update_range(&sc->start, &sc->end, start, end)) {
|
|
|
|
return;
|
|
|
|
}
|
2017-06-11 13:44:14 +03:00
|
|
|
|
2017-07-07 15:10:27 +03:00
|
|
|
submit:
|
|
|
|
if (atomic_test_bit(sc->flags, SC_INDICATE_PENDING)) {
|
|
|
|
BT_DBG("indicate pending, waiting until complete...");
|
2017-06-11 13:44:14 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-07-07 15:10:27 +03:00
|
|
|
/* Reschedule since the range has changed */
|
|
|
|
k_delayed_work_submit(&sc->work, SC_TIMEOUT);
|
2017-06-11 13:44:14 +03:00
|
|
|
}
|
|
|
|
|
2019-02-05 14:28:10 +02:00
|
|
|
static void db_changed(void)
|
|
|
|
{
|
|
|
|
#if defined(CONFIG_BT_GATT_CACHING)
|
|
|
|
int i;
|
|
|
|
|
|
|
|
k_delayed_work_submit(&db_hash_work, DB_HASH_TIMEOUT);
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cf_cfg); i++) {
|
|
|
|
struct gatt_cf_cfg *cfg = &cf_cfg[i];
|
|
|
|
|
|
|
|
if (!bt_addr_le_cmp(&cfg->peer, BT_ADDR_LE_ANY)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CF_ROBUST_CACHING(cfg) &&
|
|
|
|
atomic_test_and_clear_bit(cfg->flags, CF_CHANGE_AWARE)) {
|
|
|
|
BT_DBG("%s change-unaware", bt_addr_le_str(&cfg->peer));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2017-06-12 00:26:19 +03:00
|
|
|
int bt_gatt_service_register(struct bt_gatt_service *svc)
|
2017-06-02 15:51:54 +03:00
|
|
|
{
|
2017-06-11 13:44:14 +03:00
|
|
|
int err;
|
|
|
|
|
2017-06-12 00:26:19 +03:00
|
|
|
__ASSERT(svc, "invalid parameters\n");
|
|
|
|
__ASSERT(svc->attrs, "invalid parameters\n");
|
|
|
|
__ASSERT(svc->attr_count, "invalid parameters\n");
|
2017-06-02 15:51:54 +03:00
|
|
|
|
2018-09-07 12:44:40 +03:00
|
|
|
/* Init GATT core services */
|
|
|
|
bt_gatt_init();
|
|
|
|
|
2017-06-02 15:51:54 +03:00
|
|
|
/* Do no allow to register mandatory services twice */
|
2017-06-12 00:26:19 +03:00
|
|
|
if (!bt_uuid_cmp(svc->attrs[0].uuid, BT_UUID_GAP) ||
|
|
|
|
!bt_uuid_cmp(svc->attrs[0].uuid, BT_UUID_GATT)) {
|
2017-06-02 15:51:54 +03:00
|
|
|
return -EALREADY;
|
|
|
|
}
|
|
|
|
|
2017-06-12 00:26:19 +03:00
|
|
|
err = gatt_register(svc);
|
2017-06-11 13:44:14 +03:00
|
|
|
if (err < 0) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2017-07-21 12:49:51 +03:00
|
|
|
sc_indicate(&gatt_sc, svc->attrs[0].handle,
|
|
|
|
svc->attrs[svc->attr_count - 1].handle);
|
2017-06-11 13:44:14 +03:00
|
|
|
|
2019-02-05 14:28:10 +02:00
|
|
|
db_changed();
|
2019-02-01 16:07:41 +02:00
|
|
|
|
2017-06-11 13:44:14 +03:00
|
|
|
return 0;
|
2017-06-02 15:51:54 +03:00
|
|
|
}
|
|
|
|
|
2017-06-19 20:00:35 +03:00
|
|
|
int bt_gatt_service_unregister(struct bt_gatt_service *svc)
|
2017-06-12 14:38:39 +03:00
|
|
|
{
|
|
|
|
__ASSERT(svc, "invalid parameters\n");
|
|
|
|
|
|
|
|
if (!sys_slist_find_and_remove(&db, &svc->node)) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2017-07-21 12:49:51 +03:00
|
|
|
sc_indicate(&gatt_sc, svc->attrs[0].handle,
|
|
|
|
svc->attrs[svc->attr_count - 1].handle);
|
2017-06-12 14:38:39 +03:00
|
|
|
|
2019-02-05 14:28:10 +02:00
|
|
|
db_changed();
|
2019-02-01 16:07:41 +02:00
|
|
|
|
2017-06-12 14:38:39 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-02-19 11:00:48 +02:00
|
|
|
ssize_t bt_gatt_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
2017-04-20 12:00:29 -05:00
|
|
|
void *buf, u16_t buf_len, u16_t offset,
|
|
|
|
const void *value, u16_t value_len)
|
2015-05-12 16:03:09 +03:00
|
|
|
{
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t len;
|
2015-05-12 16:03:09 +03:00
|
|
|
|
|
|
|
if (offset > value_len) {
|
2016-02-19 12:51:27 +02:00
|
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
2015-05-12 16:03:09 +03:00
|
|
|
}
|
|
|
|
|
2019-02-11 17:14:19 +00:00
|
|
|
len = MIN(buf_len, value_len - offset);
|
2015-05-12 16:03:09 +03:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x offset %u length %u", attr->handle, offset,
|
2015-06-17 13:50:29 +03:00
|
|
|
len);
|
2015-05-12 16:03:09 +03:00
|
|
|
|
2018-09-14 14:24:09 +02:00
|
|
|
memcpy(buf, (u8_t *)value + offset, len);
|
2015-05-12 16:03:09 +03:00
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2016-02-19 11:00:48 +02:00
|
|
|
ssize_t bt_gatt_attr_read_service(struct bt_conn *conn,
|
|
|
|
const struct bt_gatt_attr *attr,
|
2017-04-20 12:00:29 -05:00
|
|
|
void *buf, u16_t len, u16_t offset)
|
2015-05-12 16:03:09 +03:00
|
|
|
{
|
|
|
|
struct bt_uuid *uuid = attr->user_data;
|
|
|
|
|
2016-01-27 22:43:34 -05:00
|
|
|
if (uuid->type == BT_UUID_TYPE_16) {
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t uuid16 = sys_cpu_to_le16(BT_UUID_16(uuid)->val);
|
2015-05-12 16:03:09 +03:00
|
|
|
|
2016-01-27 22:43:34 -05:00
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset,
|
|
|
|
&uuid16, 2);
|
2015-05-12 16:03:09 +03:00
|
|
|
}
|
|
|
|
|
2016-01-27 22:43:34 -05:00
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset,
|
|
|
|
BT_UUID_128(uuid)->val, 16);
|
2015-05-12 16:03:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
struct gatt_incl {
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t start_handle;
|
|
|
|
u16_t end_handle;
|
|
|
|
u16_t uuid16;
|
2015-06-04 11:20:48 +07:00
|
|
|
} __packed;
|
2015-05-12 16:03:09 +03:00
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static u8_t get_service_handles(const struct bt_gatt_attr *attr,
|
2016-04-12 21:07:53 +02:00
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct gatt_incl *include = user_data;
|
|
|
|
|
|
|
|
/* Stop if attribute is a service */
|
|
|
|
if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_PRIMARY) ||
|
|
|
|
!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_SECONDARY)) {
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
include->end_handle = attr->handle;
|
|
|
|
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2016-02-19 11:00:48 +02:00
|
|
|
ssize_t bt_gatt_attr_read_included(struct bt_conn *conn,
|
|
|
|
const struct bt_gatt_attr *attr,
|
2017-04-20 12:00:29 -05:00
|
|
|
void *buf, u16_t len, u16_t offset)
|
2015-05-12 16:03:09 +03:00
|
|
|
{
|
2016-04-12 21:07:53 +02:00
|
|
|
struct bt_gatt_attr *incl = attr->user_data;
|
|
|
|
struct bt_uuid *uuid = incl->user_data;
|
2015-05-12 16:03:09 +03:00
|
|
|
struct gatt_incl pdu;
|
2017-04-20 12:00:29 -05:00
|
|
|
u8_t value_len;
|
2015-05-12 16:03:09 +03:00
|
|
|
|
2016-04-12 21:07:53 +02:00
|
|
|
/* first attr points to the start handle */
|
|
|
|
pdu.start_handle = sys_cpu_to_le16(incl->handle);
|
2015-05-12 16:03:09 +03:00
|
|
|
value_len = sizeof(pdu.start_handle) + sizeof(pdu.end_handle);
|
|
|
|
|
2015-12-22 11:21:27 +01:00
|
|
|
/*
|
|
|
|
* Core 4.2, Vol 3, Part G, 3.2,
|
2016-04-12 21:07:53 +02:00
|
|
|
* The Service UUID shall only be present when the UUID is a
|
|
|
|
* 16-bit Bluetooth UUID.
|
2015-12-22 11:21:27 +01:00
|
|
|
*/
|
2016-04-12 21:07:53 +02:00
|
|
|
if (uuid->type == BT_UUID_TYPE_16) {
|
|
|
|
pdu.uuid16 = sys_cpu_to_le16(BT_UUID_16(uuid)->val);
|
2015-05-12 16:03:09 +03:00
|
|
|
value_len += sizeof(pdu.uuid16);
|
|
|
|
}
|
|
|
|
|
2016-04-12 21:07:53 +02:00
|
|
|
/* Lookup for service end handle */
|
|
|
|
bt_gatt_foreach_attr(incl->handle + 1, 0xffff, get_service_handles,
|
|
|
|
&pdu);
|
|
|
|
|
2015-06-25 13:54:17 +03:00
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &pdu, value_len);
|
2015-05-12 16:03:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
struct gatt_chrc {
|
2017-04-20 12:00:29 -05:00
|
|
|
u8_t properties;
|
|
|
|
u16_t value_handle;
|
2015-05-12 16:03:09 +03:00
|
|
|
union {
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t uuid16;
|
|
|
|
u8_t uuid[16];
|
2015-05-12 16:03:09 +03:00
|
|
|
};
|
2015-06-04 11:20:48 +07:00
|
|
|
} __packed;
|
2015-05-12 16:03:09 +03:00
|
|
|
|
2016-02-19 11:00:48 +02:00
|
|
|
ssize_t bt_gatt_attr_read_chrc(struct bt_conn *conn,
|
|
|
|
const struct bt_gatt_attr *attr, void *buf,
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t len, u16_t offset)
|
2015-05-12 16:03:09 +03:00
|
|
|
{
|
|
|
|
struct bt_gatt_chrc *chrc = attr->user_data;
|
|
|
|
struct gatt_chrc pdu;
|
2017-04-20 12:00:29 -05:00
|
|
|
u8_t value_len;
|
2015-05-12 16:03:09 +03:00
|
|
|
|
|
|
|
pdu.properties = chrc->properties;
|
2015-10-23 13:30:31 +03:00
|
|
|
/* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] page 534:
|
|
|
|
* 3.3.2 Characteristic Value Declaration
|
|
|
|
* The Characteristic Value declaration contains the value of the
|
|
|
|
* characteristic. It is the first Attribute after the characteristic
|
|
|
|
* declaration. All characteristic definitions shall have a
|
|
|
|
* Characteristic Value declaration.
|
|
|
|
*/
|
2019-01-02 08:50:18 -03:00
|
|
|
pdu.value_handle = sys_cpu_to_le16(attr->handle + 1);
|
|
|
|
|
2015-05-12 16:03:09 +03:00
|
|
|
value_len = sizeof(pdu.properties) + sizeof(pdu.value_handle);
|
|
|
|
|
2016-01-27 22:43:34 -05:00
|
|
|
if (chrc->uuid->type == BT_UUID_TYPE_16) {
|
|
|
|
pdu.uuid16 = sys_cpu_to_le16(BT_UUID_16(chrc->uuid)->val);
|
2019-03-26 19:57:45 -06:00
|
|
|
value_len += 2U;
|
2015-05-12 16:03:09 +03:00
|
|
|
} else {
|
2016-01-27 22:43:34 -05:00
|
|
|
memcpy(pdu.uuid, BT_UUID_128(chrc->uuid)->val, 16);
|
2019-03-26 19:57:45 -06:00
|
|
|
value_len += 16U;
|
2015-05-12 16:03:09 +03:00
|
|
|
}
|
|
|
|
|
2015-06-25 13:54:17 +03:00
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &pdu, value_len);
|
2015-05-12 16:03:09 +03:00
|
|
|
}
|
2015-05-19 16:52:27 +03:00
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
void bt_gatt_foreach_attr(u16_t start_handle, u16_t end_handle,
|
2015-05-19 16:52:27 +03:00
|
|
|
bt_gatt_attr_func_t func, void *user_data)
|
|
|
|
{
|
2017-06-12 00:26:19 +03:00
|
|
|
struct bt_gatt_service *svc;
|
2015-05-19 16:52:27 +03:00
|
|
|
|
2017-06-12 00:26:19 +03:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&db, svc, node) {
|
|
|
|
int i;
|
2015-05-19 16:52:27 +03:00
|
|
|
|
2017-06-12 00:26:19 +03:00
|
|
|
for (i = 0; i < svc->attr_count; i++) {
|
|
|
|
struct bt_gatt_attr *attr = &svc->attrs[i];
|
|
|
|
|
|
|
|
/* Check if attribute handle is within range */
|
|
|
|
if (attr->handle < start_handle ||
|
|
|
|
attr->handle > end_handle) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (func(attr, user_data) == BT_GATT_ITER_STOP) {
|
|
|
|
return;
|
|
|
|
}
|
2015-09-04 14:37:44 +02:00
|
|
|
}
|
2015-05-19 16:52:27 +03:00
|
|
|
}
|
|
|
|
}
|
2015-05-29 13:03:57 +03:00
|
|
|
|
2017-06-12 00:26:19 +03:00
|
|
|
static u8_t find_next(const struct bt_gatt_attr *attr, void *user_data)
|
|
|
|
{
|
|
|
|
struct bt_gatt_attr **next = user_data;
|
|
|
|
|
|
|
|
*next = (struct bt_gatt_attr *)attr;
|
|
|
|
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
2016-01-15 08:33:13 -03:00
|
|
|
struct bt_gatt_attr *bt_gatt_attr_next(const struct bt_gatt_attr *attr)
|
|
|
|
{
|
2017-06-12 00:26:19 +03:00
|
|
|
struct bt_gatt_attr *next = NULL;
|
|
|
|
|
|
|
|
bt_gatt_foreach_attr(attr->handle + 1, attr->handle + 1, find_next,
|
|
|
|
&next);
|
|
|
|
|
|
|
|
return next;
|
2016-01-15 08:33:13 -03:00
|
|
|
}
|
|
|
|
|
2019-02-18 18:05:09 +02:00
|
|
|
static struct bt_gatt_ccc_cfg *find_ccc_cfg(struct bt_conn *conn,
|
|
|
|
struct _bt_gatt_ccc *ccc)
|
2015-05-29 13:03:57 +03:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < ccc->cfg_len; i++) {
|
2019-02-18 18:05:09 +02:00
|
|
|
if (conn) {
|
|
|
|
if (conn->id == ccc->cfg[i].id &&
|
|
|
|
!bt_conn_addr_le_cmp(conn, &ccc->cfg[i].peer)) {
|
|
|
|
return &ccc->cfg[i];
|
|
|
|
}
|
|
|
|
} else if (!bt_addr_le_cmp(&ccc->cfg[i].peer, BT_ADDR_LE_ANY)) {
|
|
|
|
return &ccc->cfg[i];
|
2015-05-29 13:03:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-18 18:05:09 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t bt_gatt_attr_read_ccc(struct bt_conn *conn,
|
|
|
|
const struct bt_gatt_attr *attr, void *buf,
|
|
|
|
u16_t len, u16_t offset)
|
|
|
|
{
|
|
|
|
struct _bt_gatt_ccc *ccc = attr->user_data;
|
|
|
|
struct bt_gatt_ccc_cfg *cfg;
|
|
|
|
u16_t value;
|
|
|
|
|
|
|
|
cfg = find_ccc_cfg(conn, ccc);
|
|
|
|
if (cfg) {
|
|
|
|
value = sys_cpu_to_le16(cfg->value);
|
|
|
|
} else {
|
|
|
|
/* Default to disable if there is no cfg for the peer */
|
2015-05-29 13:03:57 +03:00
|
|
|
value = 0x0000;
|
|
|
|
}
|
|
|
|
|
2015-06-25 13:54:17 +03:00
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &value,
|
2015-05-29 13:03:57 +03:00
|
|
|
sizeof(value));
|
|
|
|
}
|
|
|
|
|
2016-10-12 12:26:10 +03:00
|
|
|
static void gatt_ccc_changed(const struct bt_gatt_attr *attr,
|
|
|
|
struct _bt_gatt_ccc *ccc)
|
2015-05-29 13:03:57 +03:00
|
|
|
{
|
|
|
|
int i;
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t value = 0x0000;
|
2015-05-29 13:03:57 +03:00
|
|
|
|
|
|
|
for (i = 0; i < ccc->cfg_len; i++) {
|
|
|
|
if (ccc->cfg[i].value > value) {
|
|
|
|
value = ccc->cfg[i].value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("ccc %p value 0x%04x", ccc, value);
|
2015-06-17 13:31:00 +03:00
|
|
|
|
2015-05-29 13:03:57 +03:00
|
|
|
if (value != ccc->value) {
|
|
|
|
ccc->value = value;
|
2018-08-21 16:36:37 +03:00
|
|
|
if (ccc->cfg_changed) {
|
|
|
|
ccc->cfg_changed(attr, value);
|
|
|
|
}
|
2015-05-29 13:03:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-19 11:00:48 +02:00
|
|
|
ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn,
|
|
|
|
const struct bt_gatt_attr *attr, const void *buf,
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t len, u16_t offset, u8_t flags)
|
2015-05-29 13:03:57 +03:00
|
|
|
{
|
|
|
|
struct _bt_gatt_ccc *ccc = attr->user_data;
|
2019-02-18 18:05:09 +02:00
|
|
|
struct bt_gatt_ccc_cfg *cfg;
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t value;
|
2015-05-29 13:03:57 +03:00
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
if (offset > sizeof(u16_t)) {
|
2016-02-17 14:03:20 +02:00
|
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
2015-05-29 13:03:57 +03:00
|
|
|
}
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
if (offset + len > sizeof(u16_t)) {
|
2016-02-17 14:03:20 +02:00
|
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
2015-09-23 15:54:45 +02:00
|
|
|
}
|
|
|
|
|
2016-09-07 06:37:37 +02:00
|
|
|
value = sys_get_le16(buf);
|
|
|
|
|
2019-02-18 18:05:09 +02:00
|
|
|
cfg = find_ccc_cfg(conn, ccc);
|
|
|
|
if (!cfg) {
|
2018-04-30 20:27:36 +01:00
|
|
|
/* If there's no existing entry, but the new value is zero,
|
|
|
|
* we don't need to do anything, since a disabled CCC is
|
|
|
|
* behavioraly the same as no written CCC.
|
|
|
|
*/
|
|
|
|
if (!value) {
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2019-02-18 18:05:09 +02:00
|
|
|
cfg = find_ccc_cfg(NULL, ccc);
|
|
|
|
if (!cfg) {
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_WARN("No space to store CCC cfg");
|
2016-02-17 14:03:20 +02:00
|
|
|
return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
|
2015-05-29 13:03:57 +03:00
|
|
|
}
|
2019-02-18 18:05:09 +02:00
|
|
|
|
|
|
|
bt_addr_le_copy(&cfg->peer, &conn->le.dst);
|
2015-05-29 13:03:57 +03:00
|
|
|
}
|
|
|
|
|
2019-02-18 16:26:13 +02:00
|
|
|
/* Confirm write if cfg is managed by application */
|
|
|
|
if (ccc->cfg_write && !ccc->cfg_write(conn, attr, value)) {
|
|
|
|
return BT_GATT_ERR(BT_ATT_ERR_WRITE_NOT_PERMITTED);
|
|
|
|
}
|
|
|
|
|
2019-02-18 18:05:09 +02:00
|
|
|
cfg->value = value;
|
2015-05-29 13:03:57 +03:00
|
|
|
|
2019-02-18 18:05:09 +02:00
|
|
|
BT_DBG("handle 0x%04x value %u", attr->handle, cfg->value);
|
2015-05-29 13:03:57 +03:00
|
|
|
|
|
|
|
/* Update cfg if don't match */
|
2019-02-18 18:05:09 +02:00
|
|
|
if (cfg->value != ccc->value) {
|
2016-10-12 12:26:10 +03:00
|
|
|
gatt_ccc_changed(attr, ccc);
|
2018-10-08 14:33:40 +02:00
|
|
|
|
|
|
|
#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE)
|
|
|
|
if ((!gatt_ccc_conn_is_queued(conn)) &&
|
|
|
|
bt_addr_le_is_bonded(conn->id, &conn->le.dst)) {
|
|
|
|
/* Store the connection with the same index it has in
|
|
|
|
* the conns array
|
|
|
|
*/
|
2018-11-26 09:10:06 +01:00
|
|
|
gatt_ccc_store.conn_list[bt_conn_index(conn)] =
|
2018-10-08 14:33:40 +02:00
|
|
|
bt_conn_ref(conn);
|
|
|
|
k_delayed_work_submit(&gatt_ccc_store.work,
|
|
|
|
CCC_STORE_DELAY);
|
|
|
|
}
|
|
|
|
#endif
|
2015-05-29 13:03:57 +03:00
|
|
|
}
|
|
|
|
|
2018-04-30 20:27:36 +01:00
|
|
|
/* Disabled CCC is the same as no configured CCC, so clear the entry */
|
|
|
|
if (!value) {
|
2019-02-18 18:05:09 +02:00
|
|
|
bt_addr_le_copy(&cfg->peer, BT_ADDR_LE_ANY);
|
2019-03-26 19:57:45 -06:00
|
|
|
cfg->value = 0U;
|
2018-04-30 20:27:36 +01:00
|
|
|
}
|
|
|
|
|
2015-05-29 13:03:57 +03:00
|
|
|
return len;
|
|
|
|
}
|
2015-05-26 14:29:30 +03:00
|
|
|
|
2016-02-19 11:00:48 +02:00
|
|
|
ssize_t bt_gatt_attr_read_cep(struct bt_conn *conn,
|
|
|
|
const struct bt_gatt_attr *attr, void *buf,
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t len, u16_t offset)
|
2015-06-11 15:33:21 +03:00
|
|
|
{
|
2018-09-18 08:47:15 +03:00
|
|
|
const struct bt_gatt_cep *value = attr->user_data;
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t props = sys_cpu_to_le16(value->properties);
|
2015-06-11 15:33:21 +03:00
|
|
|
|
2015-06-25 13:54:17 +03:00
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &props,
|
2015-06-11 15:33:21 +03:00
|
|
|
sizeof(props));
|
|
|
|
}
|
|
|
|
|
2016-02-19 11:00:48 +02:00
|
|
|
ssize_t bt_gatt_attr_read_cud(struct bt_conn *conn,
|
|
|
|
const struct bt_gatt_attr *attr, void *buf,
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t len, u16_t offset)
|
2015-12-11 09:48:57 +01:00
|
|
|
{
|
2018-09-18 08:47:15 +03:00
|
|
|
const char *value = attr->user_data;
|
2015-12-11 09:48:57 +01:00
|
|
|
|
2016-05-09 17:36:29 +03:00
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
|
|
|
|
strlen(value));
|
2015-12-11 09:48:57 +01:00
|
|
|
}
|
|
|
|
|
2016-02-19 11:00:48 +02:00
|
|
|
ssize_t bt_gatt_attr_read_cpf(struct bt_conn *conn,
|
|
|
|
const struct bt_gatt_attr *attr, void *buf,
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t len, u16_t offset)
|
2015-12-10 14:31:25 +01:00
|
|
|
{
|
2018-09-18 08:47:15 +03:00
|
|
|
const struct bt_gatt_cpf *value = attr->user_data;
|
2015-12-10 14:31:25 +01:00
|
|
|
|
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
|
|
|
|
sizeof(*value));
|
|
|
|
}
|
|
|
|
|
2015-05-26 14:29:30 +03:00
|
|
|
struct notify_data {
|
2017-06-11 13:44:14 +03:00
|
|
|
int err;
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t type;
|
2016-03-03 12:15:05 +02:00
|
|
|
const struct bt_gatt_attr *attr;
|
2019-01-14 10:55:33 -03:00
|
|
|
bt_gatt_complete_func_t func;
|
2015-05-26 14:29:30 +03:00
|
|
|
const void *data;
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t len;
|
2016-03-03 12:15:05 +02:00
|
|
|
struct bt_gatt_indicate_params *params;
|
2015-05-26 14:29:30 +03:00
|
|
|
};
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static int gatt_notify(struct bt_conn *conn, u16_t handle, const void *data,
|
2019-01-14 10:55:33 -03:00
|
|
|
size_t len, bt_gatt_complete_func_t cb)
|
2015-10-20 13:33:26 +03:00
|
|
|
{
|
2015-10-28 10:51:40 +02:00
|
|
|
struct net_buf *buf;
|
2015-10-20 13:33:26 +03:00
|
|
|
struct bt_att_notify *nfy;
|
|
|
|
|
2019-02-08 14:10:50 +02:00
|
|
|
#if defined(CONFIG_BT_GATT_ENFORCE_CHANGE_UNAWARE)
|
|
|
|
/* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G page 2350:
|
|
|
|
* Except for the Handle Value indication, the server shall not send
|
|
|
|
* notifications and indications to such a client until it becomes
|
|
|
|
* change-aware.
|
|
|
|
*/
|
|
|
|
if (!bt_gatt_change_aware(conn, false)) {
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-10-20 13:33:26 +03:00
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_NOTIFY, sizeof(*nfy) + len);
|
|
|
|
if (!buf) {
|
|
|
|
BT_WARN("No buffer available to send notification");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("conn %p handle 0x%04x", conn, handle);
|
2015-10-20 13:33:26 +03:00
|
|
|
|
2015-10-28 10:51:40 +02:00
|
|
|
nfy = net_buf_add(buf, sizeof(*nfy));
|
2015-10-20 13:33:26 +03:00
|
|
|
nfy->handle = sys_cpu_to_le16(handle);
|
|
|
|
|
2015-10-28 10:51:40 +02:00
|
|
|
net_buf_add(buf, len);
|
2015-10-20 13:33:26 +03:00
|
|
|
memcpy(nfy->value, data, len);
|
|
|
|
|
2019-01-14 10:55:33 -03:00
|
|
|
return bt_att_send(conn, buf, cb);
|
2015-10-20 13:33:26 +03:00
|
|
|
}
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static void gatt_indicate_rsp(struct bt_conn *conn, u8_t err,
|
|
|
|
const void *pdu, u16_t length, void *user_data)
|
2016-03-03 12:15:05 +02:00
|
|
|
{
|
|
|
|
struct bt_gatt_indicate_params *params = user_data;
|
|
|
|
|
|
|
|
params->func(conn, params->attr, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gatt_send(struct bt_conn *conn, struct net_buf *buf,
|
2016-08-02 16:23:26 +03:00
|
|
|
bt_att_func_t func, void *params,
|
2016-03-03 12:15:05 +02:00
|
|
|
bt_att_destroy_t destroy)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2016-08-02 16:23:26 +03:00
|
|
|
if (params) {
|
|
|
|
struct bt_att_req *req = params;
|
|
|
|
|
|
|
|
req->buf = buf;
|
|
|
|
req->func = func;
|
|
|
|
req->destroy = destroy;
|
|
|
|
|
|
|
|
err = bt_att_req_send(conn, req);
|
|
|
|
} else {
|
2019-01-14 10:55:33 -03:00
|
|
|
err = bt_att_send(conn, buf, NULL);
|
2016-08-02 16:23:26 +03:00
|
|
|
}
|
|
|
|
|
2016-03-03 12:15:05 +02:00
|
|
|
if (err) {
|
|
|
|
BT_ERR("Error sending ATT PDU: %d", err);
|
|
|
|
net_buf_unref(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-11-15 12:39:25 +02:00
|
|
|
static int gatt_indicate(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_indicate_params *params)
|
2016-03-03 12:15:05 +02:00
|
|
|
{
|
|
|
|
struct net_buf *buf;
|
|
|
|
struct bt_att_indicate *ind;
|
2018-06-07 12:58:05 +03:00
|
|
|
u16_t value_handle = params->attr->handle;
|
|
|
|
|
2019-02-08 14:10:50 +02:00
|
|
|
#if defined(CONFIG_BT_GATT_ENFORCE_CHANGE_UNAWARE)
|
|
|
|
/* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G page 2350:
|
|
|
|
* Except for the Handle Value indication, the server shall not send
|
|
|
|
* notifications and indications to such a client until it becomes
|
|
|
|
* change-aware.
|
|
|
|
*/
|
|
|
|
if (!(params->func && params->func == sc_indicate_rsp) &&
|
|
|
|
!bt_gatt_change_aware(conn, false)) {
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2018-06-07 12:58:05 +03:00
|
|
|
/* Check if attribute is a characteristic then adjust the handle */
|
|
|
|
if (!bt_uuid_cmp(params->attr->uuid, BT_UUID_GATT_CHRC)) {
|
|
|
|
struct bt_gatt_chrc *chrc = params->attr->user_data;
|
|
|
|
|
|
|
|
if (!(chrc->properties & BT_GATT_CHRC_INDICATE)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-03-26 19:57:45 -06:00
|
|
|
value_handle += 1U;
|
2018-06-07 12:58:05 +03:00
|
|
|
}
|
2016-03-03 12:15:05 +02:00
|
|
|
|
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_INDICATE,
|
|
|
|
sizeof(*ind) + params->len);
|
|
|
|
if (!buf) {
|
|
|
|
BT_WARN("No buffer available to send indication");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2018-06-07 12:58:05 +03:00
|
|
|
BT_DBG("conn %p handle 0x%04x", conn, value_handle);
|
2016-03-03 12:15:05 +02:00
|
|
|
|
|
|
|
ind = net_buf_add(buf, sizeof(*ind));
|
2018-06-07 12:58:05 +03:00
|
|
|
ind->handle = sys_cpu_to_le16(value_handle);
|
2016-03-03 12:15:05 +02:00
|
|
|
|
|
|
|
net_buf_add(buf, params->len);
|
|
|
|
memcpy(ind->value, params->data, params->len);
|
|
|
|
|
|
|
|
return gatt_send(conn, buf, gatt_indicate_rsp, params, NULL);
|
|
|
|
}
|
|
|
|
|
2017-07-21 12:49:51 +03:00
|
|
|
struct sc_data {
|
|
|
|
u16_t start;
|
|
|
|
u16_t end;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void sc_save(struct bt_gatt_ccc_cfg *cfg,
|
|
|
|
struct bt_gatt_indicate_params *params)
|
|
|
|
{
|
|
|
|
struct sc_data data;
|
|
|
|
struct sc_data *stored;
|
|
|
|
|
|
|
|
memcpy(&data, params->data, params->len);
|
|
|
|
|
|
|
|
data.start = sys_le16_to_cpu(data.start);
|
|
|
|
data.end = sys_le16_to_cpu(data.end);
|
|
|
|
|
|
|
|
/* Load data stored */
|
|
|
|
stored = (struct sc_data *)cfg->data;
|
|
|
|
|
|
|
|
/* Check if there is any change stored */
|
|
|
|
if (!stored->start && !stored->end) {
|
|
|
|
*stored = data;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
update_range(&stored->start, &stored->end,
|
|
|
|
data.start, data.end);
|
|
|
|
|
|
|
|
done:
|
|
|
|
BT_DBG("peer %s start 0x%04x end 0x%04x", bt_addr_le_str(&cfg->peer),
|
|
|
|
stored->start, stored->end);
|
|
|
|
}
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static u8_t notify_cb(const struct bt_gatt_attr *attr, void *user_data)
|
2015-05-26 14:29:30 +03:00
|
|
|
{
|
|
|
|
struct notify_data *data = user_data;
|
|
|
|
struct _bt_gatt_ccc *ccc;
|
|
|
|
size_t i;
|
|
|
|
|
2015-12-08 15:27:43 -02:00
|
|
|
if (bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CCC)) {
|
2015-05-26 14:29:30 +03:00
|
|
|
/* Stop if we reach the next characteristic */
|
2015-12-08 15:27:43 -02:00
|
|
|
if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CHRC)) {
|
2015-05-26 14:29:30 +03:00
|
|
|
return BT_GATT_ITER_STOP;
|
2015-06-03 13:46:47 +03:00
|
|
|
}
|
2015-05-26 14:29:30 +03:00
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check attribute user_data must be of type struct _bt_gatt_ccc */
|
|
|
|
if (attr->write != bt_gatt_attr_write_ccc) {
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ccc = attr->user_data;
|
|
|
|
|
|
|
|
/* Notify all peers configured */
|
|
|
|
for (i = 0; i < ccc->cfg_len; i++) {
|
2018-07-04 12:58:10 +03:00
|
|
|
struct bt_gatt_ccc_cfg *cfg = &ccc->cfg[i];
|
2015-05-26 14:29:30 +03:00
|
|
|
struct bt_conn *conn;
|
2016-03-03 12:15:05 +02:00
|
|
|
int err;
|
2015-05-26 14:29:30 +03:00
|
|
|
|
2017-07-21 12:49:51 +03:00
|
|
|
/* Check if config value matches data type since consolidated
|
|
|
|
* value may be for a different peer.
|
|
|
|
*/
|
2018-07-04 12:58:10 +03:00
|
|
|
if (cfg->value != data->type) {
|
2017-07-21 12:49:51 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-07-04 12:58:10 +03:00
|
|
|
conn = bt_conn_lookup_addr_le(cfg->id, &cfg->peer);
|
2016-09-12 21:37:39 +03:00
|
|
|
if (!conn) {
|
2017-07-21 12:49:51 +03:00
|
|
|
if (ccc->cfg == sc_ccc_cfg) {
|
2018-07-04 12:58:10 +03:00
|
|
|
sc_save(cfg, data->params);
|
2017-07-21 12:49:51 +03:00
|
|
|
}
|
2016-09-12 21:37:39 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
|
|
|
bt_conn_unref(conn);
|
2015-05-26 14:29:30 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-02-18 16:26:13 +02:00
|
|
|
/* Confirm match if cfg is managed by application */
|
|
|
|
if (ccc->cfg_match && !ccc->cfg_match(conn, attr)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-03-03 12:15:05 +02:00
|
|
|
if (data->type == BT_GATT_CCC_INDICATE) {
|
2016-11-15 12:39:25 +02:00
|
|
|
err = gatt_indicate(conn, data->params);
|
2016-03-03 12:15:05 +02:00
|
|
|
} else {
|
2018-06-12 16:42:59 +02:00
|
|
|
err = gatt_notify(conn, data->attr->handle,
|
2018-09-12 09:58:43 +02:00
|
|
|
data->data, data->len,
|
|
|
|
data->func);
|
2015-05-26 14:29:30 +03:00
|
|
|
}
|
|
|
|
|
2015-10-23 12:45:33 +03:00
|
|
|
bt_conn_unref(conn);
|
2016-03-03 12:15:05 +02:00
|
|
|
|
|
|
|
if (err < 0) {
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
2017-06-11 13:44:14 +03:00
|
|
|
|
|
|
|
data->err = 0;
|
2015-05-26 14:29:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2018-09-12 09:58:43 +02:00
|
|
|
int bt_gatt_notify_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
|
|
|
const void *data, u16_t len,
|
2019-01-14 10:55:33 -03:00
|
|
|
bt_gatt_complete_func_t func)
|
2015-05-26 14:29:30 +03:00
|
|
|
{
|
|
|
|
struct notify_data nfy;
|
|
|
|
|
2018-09-12 09:58:43 +02:00
|
|
|
__ASSERT(attr && attr->handle,
|
|
|
|
"invalid parameters\n");
|
2016-01-19 13:18:21 +02:00
|
|
|
|
2018-06-07 12:41:34 +03:00
|
|
|
/* Check if attribute is a characteristic then adjust the handle */
|
|
|
|
if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CHRC)) {
|
|
|
|
struct bt_gatt_chrc *chrc = attr->user_data;
|
|
|
|
|
|
|
|
if (!(chrc->properties & BT_GATT_CHRC_NOTIFY)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-06-12 16:42:59 +02:00
|
|
|
attr++;
|
2018-06-07 12:41:34 +03:00
|
|
|
}
|
|
|
|
|
2015-10-20 13:33:26 +03:00
|
|
|
if (conn) {
|
2018-09-12 09:58:43 +02:00
|
|
|
return gatt_notify(conn, attr->handle, data,
|
|
|
|
len, func);
|
2015-10-20 13:33:26 +03:00
|
|
|
}
|
|
|
|
|
2017-06-11 13:44:14 +03:00
|
|
|
nfy.err = -ENOTCONN;
|
2016-03-03 12:15:05 +02:00
|
|
|
nfy.attr = attr;
|
2018-09-12 09:58:43 +02:00
|
|
|
nfy.func = func;
|
2016-03-03 12:15:05 +02:00
|
|
|
nfy.type = BT_GATT_CCC_NOTIFY;
|
2015-05-26 14:29:30 +03:00
|
|
|
nfy.data = data;
|
|
|
|
nfy.len = len;
|
|
|
|
|
2018-06-12 16:42:59 +02:00
|
|
|
bt_gatt_foreach_attr(attr->handle, 0xffff, notify_cb, &nfy);
|
2015-10-20 13:33:26 +03:00
|
|
|
|
2017-06-11 13:44:14 +03:00
|
|
|
return nfy.err;
|
2015-05-26 14:29:30 +03:00
|
|
|
}
|
2015-06-17 13:31:00 +03:00
|
|
|
|
2016-03-03 12:15:05 +02:00
|
|
|
int bt_gatt_indicate(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_indicate_params *params)
|
|
|
|
{
|
|
|
|
struct notify_data nfy;
|
|
|
|
|
2017-02-14 19:55:00 +02:00
|
|
|
__ASSERT(params, "invalid parameters\n");
|
|
|
|
__ASSERT(params->attr && params->attr->handle, "invalid parameters\n");
|
2016-03-03 12:15:05 +02:00
|
|
|
|
|
|
|
if (conn) {
|
2016-11-15 12:39:25 +02:00
|
|
|
return gatt_indicate(conn, params);
|
2016-03-03 12:15:05 +02:00
|
|
|
}
|
|
|
|
|
2017-06-11 13:44:14 +03:00
|
|
|
nfy.err = -ENOTCONN;
|
2016-03-03 12:15:05 +02:00
|
|
|
nfy.type = BT_GATT_CCC_INDICATE;
|
|
|
|
nfy.params = params;
|
|
|
|
|
|
|
|
bt_gatt_foreach_attr(params->attr->handle, 0xffff, notify_cb, &nfy);
|
|
|
|
|
2017-06-11 13:44:14 +03:00
|
|
|
return nfy.err;
|
2016-03-03 12:15:05 +02:00
|
|
|
}
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t bt_gatt_get_mtu(struct bt_conn *conn)
|
2017-02-27 13:28:36 +02:00
|
|
|
{
|
|
|
|
return bt_att_get_mtu(conn);
|
|
|
|
}
|
|
|
|
|
2017-07-21 12:49:51 +03:00
|
|
|
static void sc_restore(struct bt_gatt_ccc_cfg *cfg)
|
|
|
|
{
|
|
|
|
struct sc_data *data = (struct sc_data *)cfg->data;
|
|
|
|
|
|
|
|
if (!data->start && !data->end) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("peer %s start 0x%04x end 0x%04x", bt_addr_le_str(&cfg->peer),
|
|
|
|
data->start, data->end);
|
|
|
|
|
|
|
|
sc_indicate(&gatt_sc, data->start, data->end);
|
|
|
|
|
|
|
|
/* Reset config data */
|
2018-09-11 19:09:03 -07:00
|
|
|
(void)memset(cfg->data, 0, sizeof(cfg->data));
|
2017-07-21 12:49:51 +03:00
|
|
|
}
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static u8_t connected_cb(const struct bt_gatt_attr *attr, void *user_data)
|
2015-06-17 13:31:00 +03:00
|
|
|
{
|
|
|
|
struct bt_conn *conn = user_data;
|
|
|
|
struct _bt_gatt_ccc *ccc;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
/* Check attribute user_data must be of type struct _bt_gatt_ccc */
|
|
|
|
if (attr->write != bt_gatt_attr_write_ccc) {
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ccc = attr->user_data;
|
|
|
|
|
|
|
|
for (i = 0; i < ccc->cfg_len; i++) {
|
|
|
|
/* Ignore configuration for different peer */
|
2016-11-30 14:35:54 +02:00
|
|
|
if (bt_conn_addr_le_cmp(conn, &ccc->cfg[i].peer)) {
|
2015-06-17 13:31:00 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ccc->cfg[i].value) {
|
2016-10-12 12:26:10 +03:00
|
|
|
gatt_ccc_changed(attr, ccc);
|
2017-07-21 12:49:51 +03:00
|
|
|
if (ccc->cfg == sc_ccc_cfg) {
|
|
|
|
sc_restore(&ccc->cfg[i]);
|
|
|
|
}
|
2015-06-17 13:31:00 +03:00
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static u8_t disconnected_cb(const struct bt_gatt_attr *attr, void *user_data)
|
2015-06-17 13:31:00 +03:00
|
|
|
{
|
|
|
|
struct bt_conn *conn = user_data;
|
|
|
|
struct _bt_gatt_ccc *ccc;
|
2019-02-28 17:44:10 +11:00
|
|
|
bool value_used;
|
2015-06-17 13:31:00 +03:00
|
|
|
size_t i;
|
|
|
|
|
|
|
|
/* Check attribute user_data must be of type struct _bt_gatt_ccc */
|
|
|
|
if (attr->write != bt_gatt_attr_write_ccc) {
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ccc = attr->user_data;
|
|
|
|
|
|
|
|
/* If already disabled skip */
|
|
|
|
if (!ccc->value) {
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2019-02-28 17:44:10 +11:00
|
|
|
/* Checking if all values are disabled */
|
|
|
|
value_used = false;
|
|
|
|
|
2015-06-17 13:31:00 +03:00
|
|
|
for (i = 0; i < ccc->cfg_len; i++) {
|
2018-07-04 12:58:10 +03:00
|
|
|
struct bt_gatt_ccc_cfg *cfg = &ccc->cfg[i];
|
|
|
|
|
2015-06-17 13:31:00 +03:00
|
|
|
/* Ignore configurations with disabled value */
|
2018-07-04 12:58:10 +03:00
|
|
|
if (!cfg->value) {
|
2015-06-17 13:31:00 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-07-04 12:58:10 +03:00
|
|
|
if (conn->id != cfg->id ||
|
|
|
|
bt_conn_addr_le_cmp(conn, &cfg->peer)) {
|
2015-06-17 13:31:00 +03:00
|
|
|
struct bt_conn *tmp;
|
|
|
|
|
|
|
|
/* Skip if there is another peer connected */
|
2018-07-04 12:58:10 +03:00
|
|
|
tmp = bt_conn_lookup_addr_le(cfg->id, &cfg->peer);
|
2016-02-16 14:31:15 +01:00
|
|
|
if (tmp) {
|
|
|
|
if (tmp->state == BT_CONN_CONNECTED) {
|
2019-02-28 17:44:10 +11:00
|
|
|
value_used = true;
|
2016-02-16 14:31:15 +01:00
|
|
|
}
|
|
|
|
|
2015-10-23 12:45:33 +03:00
|
|
|
bt_conn_unref(tmp);
|
2015-06-17 13:31:00 +03:00
|
|
|
}
|
2016-06-08 15:19:37 +02:00
|
|
|
} else {
|
|
|
|
/* Clear value if not paired */
|
2018-07-04 12:58:10 +03:00
|
|
|
if (!bt_addr_le_is_bonded(conn->id, &conn->le.dst)) {
|
|
|
|
bt_addr_le_copy(&cfg->peer, BT_ADDR_LE_ANY);
|
2019-03-26 19:57:45 -06:00
|
|
|
cfg->value = 0U;
|
2016-11-30 14:47:17 +02:00
|
|
|
} else {
|
|
|
|
/* Update address in case it has changed */
|
2018-07-04 12:58:10 +03:00
|
|
|
bt_addr_le_copy(&cfg->peer, &conn->le.dst);
|
2016-08-27 13:46:24 +02:00
|
|
|
}
|
2015-06-17 13:31:00 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-28 17:44:10 +11:00
|
|
|
/* If all values are now disabled, reset value while disconnected */
|
|
|
|
if (!value_used) {
|
2019-03-26 19:57:45 -06:00
|
|
|
ccc->value = 0U;
|
2019-02-28 17:44:10 +11:00
|
|
|
if (ccc->cfg_changed) {
|
|
|
|
ccc->cfg_changed(attr, ccc->value);
|
|
|
|
}
|
2015-06-17 13:31:00 +03:00
|
|
|
|
2019-02-28 17:44:10 +11:00
|
|
|
BT_DBG("ccc %p reseted", ccc);
|
|
|
|
}
|
2015-06-17 13:31:00 +03:00
|
|
|
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2017-08-09 09:21:11 +03:00
|
|
|
#if defined(CONFIG_BT_GATT_CLIENT)
|
2017-04-20 12:00:29 -05:00
|
|
|
void bt_gatt_notification(struct bt_conn *conn, u16_t handle,
|
|
|
|
const void *data, u16_t length)
|
2015-10-02 12:44:26 +02:00
|
|
|
{
|
2017-02-15 15:51:50 +01:00
|
|
|
struct bt_gatt_subscribe_params *params, *tmp;
|
2015-10-02 12:44:26 +02:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x length %u", handle, length);
|
2015-10-02 12:44:26 +02:00
|
|
|
|
2017-02-08 16:48:26 +02:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subscriptions, params, tmp, node) {
|
2017-02-03 14:19:08 +02:00
|
|
|
if (bt_conn_addr_le_cmp(conn, ¶ms->_peer) ||
|
|
|
|
handle != params->value_handle) {
|
2015-11-10 15:58:43 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-01-21 10:47:09 +01:00
|
|
|
if (params->notify(conn, params, data, length) ==
|
|
|
|
BT_GATT_ITER_STOP) {
|
2015-11-10 15:58:43 +01:00
|
|
|
bt_gatt_unsubscribe(conn, params);
|
2015-10-02 12:44:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-02 16:13:58 +01:00
|
|
|
static void update_subscription(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_subscribe_params *params)
|
|
|
|
{
|
|
|
|
if (params->_peer.type == BT_ADDR_LE_PUBLIC) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update address */
|
|
|
|
bt_addr_le_copy(¶ms->_peer, &conn->le.dst);
|
|
|
|
}
|
|
|
|
|
2017-02-03 14:19:08 +02:00
|
|
|
static void gatt_subscription_remove(struct bt_conn *conn, sys_snode_t *prev,
|
2015-07-29 15:03:55 +03:00
|
|
|
struct bt_gatt_subscribe_params *params)
|
|
|
|
{
|
|
|
|
/* Remove subscription from the list*/
|
2017-02-03 14:19:08 +02:00
|
|
|
sys_slist_remove(&subscriptions, prev, ¶ms->node);
|
2015-07-29 15:03:55 +03:00
|
|
|
|
2016-01-21 10:47:09 +01:00
|
|
|
params->notify(conn, params, NULL, 0);
|
2015-07-29 15:03:55 +03:00
|
|
|
}
|
|
|
|
|
2016-03-03 13:48:01 +02:00
|
|
|
static void remove_subscriptions(struct bt_conn *conn)
|
2015-06-17 13:31:00 +03:00
|
|
|
{
|
2017-02-15 15:52:38 +01:00
|
|
|
struct bt_gatt_subscribe_params *params, *tmp;
|
|
|
|
sys_snode_t *prev = NULL;
|
2015-07-29 15:03:55 +03:00
|
|
|
|
|
|
|
/* Lookup existing subscriptions */
|
2017-02-08 16:48:26 +02:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subscriptions, params, tmp, node) {
|
2016-11-30 14:35:54 +02:00
|
|
|
if (bt_conn_addr_le_cmp(conn, ¶ms->_peer)) {
|
2017-02-15 15:52:38 +01:00
|
|
|
prev = ¶ms->node;
|
2015-07-29 15:03:55 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-07-04 12:58:10 +03:00
|
|
|
if (!bt_addr_le_is_bonded(conn->id, &conn->le.dst) ||
|
2017-02-02 16:13:58 +01:00
|
|
|
(params->flags & BT_GATT_SUBSCRIBE_FLAG_VOLATILE)) {
|
|
|
|
/* Remove subscription */
|
2019-03-26 19:57:45 -06:00
|
|
|
params->value = 0U;
|
2017-02-15 15:52:38 +01:00
|
|
|
gatt_subscription_remove(conn, prev, params);
|
2017-02-02 16:13:58 +01:00
|
|
|
} else {
|
|
|
|
update_subscription(conn, params);
|
2017-02-15 15:52:38 +01:00
|
|
|
prev = ¶ms->node;
|
2017-02-02 16:13:58 +01:00
|
|
|
}
|
2015-07-29 15:03:55 +03:00
|
|
|
}
|
2015-06-17 13:31:00 +03:00
|
|
|
}
|
2015-07-01 11:03:54 +03:00
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static void gatt_mtu_rsp(struct bt_conn *conn, u8_t err, const void *pdu,
|
|
|
|
u16_t length, void *user_data)
|
2015-07-01 11:03:54 +03:00
|
|
|
{
|
2016-08-02 16:23:26 +03:00
|
|
|
struct bt_gatt_exchange_params *params = user_data;
|
2015-07-01 11:03:54 +03:00
|
|
|
|
2016-08-02 16:23:26 +03:00
|
|
|
params->func(conn, err, params);
|
2015-07-01 11:03:54 +03:00
|
|
|
}
|
|
|
|
|
2016-08-02 16:23:26 +03:00
|
|
|
int bt_gatt_exchange_mtu(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_exchange_params *params)
|
2015-07-01 11:03:54 +03:00
|
|
|
{
|
|
|
|
struct bt_att_exchange_mtu_req *req;
|
2015-10-28 10:51:40 +02:00
|
|
|
struct net_buf *buf;
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t mtu;
|
2015-07-01 11:03:54 +03:00
|
|
|
|
2017-02-14 19:55:00 +02:00
|
|
|
__ASSERT(conn, "invalid parameter\n");
|
|
|
|
__ASSERT(params && params->func, "invalid parameters\n");
|
2015-07-01 11:03:54 +03:00
|
|
|
|
2016-10-27 14:52:15 +03:00
|
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
|
|
|
return -ENOTCONN;
|
|
|
|
}
|
|
|
|
|
2015-07-01 11:03:54 +03:00
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_MTU_REQ, sizeof(*req));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2017-01-07 17:05:58 +02:00
|
|
|
mtu = BT_ATT_MTU;
|
2015-07-01 11:03:54 +03:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("Client MTU %u", mtu);
|
2015-07-01 11:03:54 +03:00
|
|
|
|
2015-10-28 10:51:40 +02:00
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
2015-07-01 11:03:54 +03:00
|
|
|
req->mtu = sys_cpu_to_le16(mtu);
|
|
|
|
|
2016-08-02 16:23:26 +03:00
|
|
|
return gatt_send(conn, buf, gatt_mtu_rsp, params, NULL);
|
2015-07-01 11:03:54 +03:00
|
|
|
}
|
2015-07-03 14:52:57 +03:00
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static void gatt_discover_next(struct bt_conn *conn, u16_t last_handle,
|
2016-10-27 14:45:57 +03:00
|
|
|
struct bt_gatt_discover_params *params)
|
|
|
|
{
|
|
|
|
/* Skip if last_handle is not set */
|
|
|
|
if (!last_handle)
|
|
|
|
goto discover;
|
|
|
|
|
|
|
|
/* Continue from the last found handle */
|
|
|
|
params->start_handle = last_handle;
|
|
|
|
if (params->start_handle < UINT16_MAX) {
|
|
|
|
params->start_handle++;
|
2018-09-20 09:21:01 +02:00
|
|
|
} else {
|
|
|
|
goto done;
|
2016-10-27 14:45:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Stop if over the range or the requests */
|
2018-09-20 09:21:01 +02:00
|
|
|
if (params->start_handle > params->end_handle) {
|
2016-10-27 14:45:57 +03:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
discover:
|
|
|
|
/* Discover next range */
|
|
|
|
if (!bt_gatt_discover(conn, params)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
params->func(conn, NULL, params);
|
|
|
|
}
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static void gatt_find_type_rsp(struct bt_conn *conn, u8_t err,
|
|
|
|
const void *pdu, u16_t length,
|
2016-11-15 12:39:25 +02:00
|
|
|
void *user_data)
|
2015-07-03 14:52:57 +03:00
|
|
|
{
|
|
|
|
const struct bt_att_find_type_rsp *rsp = pdu;
|
|
|
|
struct bt_gatt_discover_params *params = user_data;
|
2017-04-20 12:00:29 -05:00
|
|
|
u8_t i;
|
2018-11-29 11:23:03 -08:00
|
|
|
u16_t end_handle = 0U, start_handle;
|
2015-07-03 14:52:57 +03:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("err 0x%02x", err);
|
2015-07-03 14:52:57 +03:00
|
|
|
|
|
|
|
if (err) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse attributes found */
|
2018-11-29 11:23:03 -08:00
|
|
|
for (i = 0U; length >= sizeof(rsp->list[i]);
|
2015-07-03 14:52:57 +03:00
|
|
|
i++, length -= sizeof(rsp->list[i])) {
|
2016-11-15 16:52:38 +02:00
|
|
|
struct bt_gatt_attr attr = {};
|
2017-06-12 00:26:19 +03:00
|
|
|
struct bt_gatt_service_val value;
|
2015-07-03 14:52:57 +03:00
|
|
|
|
|
|
|
start_handle = sys_le16_to_cpu(rsp->list[i].start_handle);
|
|
|
|
end_handle = sys_le16_to_cpu(rsp->list[i].end_handle);
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("start_handle 0x%04x end_handle 0x%04x", start_handle,
|
2015-07-03 14:52:57 +03:00
|
|
|
end_handle);
|
|
|
|
|
2015-09-21 14:01:13 +03:00
|
|
|
if (params->type == BT_GATT_DISCOVER_PRIMARY) {
|
2016-11-15 16:52:38 +02:00
|
|
|
attr.uuid = BT_UUID_GATT_PRIMARY;
|
2015-09-21 14:01:13 +03:00
|
|
|
} else {
|
2016-11-15 16:52:38 +02:00
|
|
|
attr.uuid = BT_UUID_GATT_SECONDARY;
|
2015-09-21 14:01:13 +03:00
|
|
|
}
|
2015-07-03 14:52:57 +03:00
|
|
|
|
2016-11-24 17:31:45 +01:00
|
|
|
value.end_handle = end_handle;
|
|
|
|
value.uuid = params->uuid;
|
|
|
|
|
2016-11-15 16:52:38 +02:00
|
|
|
attr.handle = start_handle;
|
2016-11-24 17:31:45 +01:00
|
|
|
attr.user_data = &value;
|
2015-10-23 10:52:28 +03:00
|
|
|
|
2016-11-15 16:52:38 +02:00
|
|
|
if (params->func(conn, &attr, params) == BT_GATT_ITER_STOP) {
|
2016-01-21 10:47:05 +01:00
|
|
|
return;
|
2015-07-03 14:52:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stop if could not parse the whole PDU */
|
|
|
|
if (length > 0) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2016-10-27 14:45:57 +03:00
|
|
|
gatt_discover_next(conn, end_handle, params);
|
2015-07-03 14:52:57 +03:00
|
|
|
|
2016-10-27 14:45:57 +03:00
|
|
|
return;
|
2015-07-03 14:52:57 +03:00
|
|
|
done:
|
2016-01-21 10:47:05 +01:00
|
|
|
params->func(conn, NULL, params);
|
2015-07-03 14:52:57 +03:00
|
|
|
}
|
|
|
|
|
2016-11-15 12:39:25 +02:00
|
|
|
static int gatt_find_type(struct bt_conn *conn,
|
2015-09-21 11:12:15 +03:00
|
|
|
struct bt_gatt_discover_params *params)
|
2015-07-03 14:52:57 +03:00
|
|
|
{
|
2015-10-28 10:51:40 +02:00
|
|
|
struct net_buf *buf;
|
2015-07-03 14:52:57 +03:00
|
|
|
struct bt_att_find_type_req *req;
|
2018-02-12 16:11:33 +02:00
|
|
|
struct bt_uuid *uuid;
|
2015-07-03 14:52:57 +03:00
|
|
|
|
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_FIND_TYPE_REQ, sizeof(*req));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2015-10-28 10:51:40 +02:00
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
2015-07-03 14:52:57 +03:00
|
|
|
req->start_handle = sys_cpu_to_le16(params->start_handle);
|
|
|
|
req->end_handle = sys_cpu_to_le16(params->end_handle);
|
2015-09-21 14:01:13 +03:00
|
|
|
|
|
|
|
if (params->type == BT_GATT_DISCOVER_PRIMARY) {
|
2018-02-12 16:11:33 +02:00
|
|
|
uuid = BT_UUID_GATT_PRIMARY;
|
2015-09-21 14:01:13 +03:00
|
|
|
} else {
|
2018-02-12 16:11:33 +02:00
|
|
|
uuid = BT_UUID_GATT_SECONDARY;
|
2015-09-21 14:01:13 +03:00
|
|
|
}
|
2015-07-03 14:52:57 +03:00
|
|
|
|
2018-02-12 16:11:33 +02:00
|
|
|
req->type = sys_cpu_to_le16(BT_UUID_16(uuid)->val);
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("uuid %s start_handle 0x%04x end_handle 0x%04x",
|
2015-10-21 13:34:21 +03:00
|
|
|
bt_uuid_str(params->uuid), params->start_handle,
|
|
|
|
params->end_handle);
|
2015-07-03 14:52:57 +03:00
|
|
|
|
|
|
|
switch (params->uuid->type) {
|
2016-01-27 22:43:34 -05:00
|
|
|
case BT_UUID_TYPE_16:
|
|
|
|
net_buf_add_le16(buf, BT_UUID_16(params->uuid)->val);
|
2015-07-03 14:52:57 +03:00
|
|
|
break;
|
2016-01-27 22:43:34 -05:00
|
|
|
case BT_UUID_TYPE_128:
|
2016-12-25 09:55:58 +02:00
|
|
|
net_buf_add_mem(buf, BT_UUID_128(params->uuid)->val, 16);
|
2015-07-03 14:52:57 +03:00
|
|
|
break;
|
|
|
|
default:
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_ERR("Unknown UUID type %u", params->uuid->type);
|
2015-10-28 10:51:40 +02:00
|
|
|
net_buf_unref(buf);
|
2015-07-03 14:52:57 +03:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2016-11-15 12:39:25 +02:00
|
|
|
return gatt_send(conn, buf, gatt_find_type_rsp, params, NULL);
|
2015-07-03 14:52:57 +03:00
|
|
|
}
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static void read_included_uuid_cb(struct bt_conn *conn, u8_t err,
|
|
|
|
const void *pdu, u16_t length,
|
2016-07-08 15:05:49 +02:00
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct bt_gatt_discover_params *params = user_data;
|
|
|
|
struct bt_gatt_include value;
|
|
|
|
struct bt_gatt_attr *attr;
|
|
|
|
union {
|
|
|
|
struct bt_uuid uuid;
|
|
|
|
struct bt_uuid_128 u128;
|
|
|
|
} u;
|
|
|
|
|
2019-03-26 19:57:45 -06:00
|
|
|
if (length != 16U) {
|
2016-07-08 15:05:49 +02:00
|
|
|
BT_ERR("Invalid data len %u", length);
|
2016-10-27 14:45:57 +03:00
|
|
|
params->func(conn, NULL, params);
|
|
|
|
return;
|
2016-07-08 15:05:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
value.start_handle = params->_included.start_handle;
|
|
|
|
value.end_handle = params->_included.end_handle;
|
|
|
|
value.uuid = &u.uuid;
|
|
|
|
u.uuid.type = BT_UUID_TYPE_128;
|
|
|
|
memcpy(u.u128.val, pdu, length);
|
|
|
|
|
|
|
|
BT_DBG("handle 0x%04x uuid %s start_handle 0x%04x "
|
|
|
|
"end_handle 0x%04x\n", params->_included.attr_handle,
|
|
|
|
bt_uuid_str(&u.uuid), value.start_handle, value.end_handle);
|
|
|
|
|
|
|
|
/* Skip if UUID is set but doesn't match */
|
|
|
|
if (params->uuid && bt_uuid_cmp(&u.uuid, params->uuid)) {
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
|
|
|
|
attr = (&(struct bt_gatt_attr) {
|
|
|
|
.uuid = BT_UUID_GATT_INCLUDE,
|
|
|
|
.user_data = &value, });
|
|
|
|
attr->handle = params->_included.attr_handle;
|
|
|
|
|
|
|
|
if (params->func(conn, attr, params) == BT_GATT_ITER_STOP) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
next:
|
2016-10-27 14:45:57 +03:00
|
|
|
gatt_discover_next(conn, params->start_handle, params);
|
2016-07-08 15:05:49 +02:00
|
|
|
|
2016-10-27 14:45:57 +03:00
|
|
|
return;
|
2016-07-08 15:05:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int read_included_uuid(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_discover_params *params)
|
|
|
|
{
|
|
|
|
struct net_buf *buf;
|
|
|
|
struct bt_att_read_req *req;
|
|
|
|
|
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_REQ, sizeof(*req));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
|
|
|
req->handle = sys_cpu_to_le16(params->_included.start_handle);
|
|
|
|
|
|
|
|
BT_DBG("handle 0x%04x", params->_included.start_handle);
|
|
|
|
|
|
|
|
return gatt_send(conn, buf, read_included_uuid_cb, params, NULL);
|
|
|
|
}
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static u16_t parse_include(struct bt_conn *conn, const void *pdu,
|
2017-04-21 09:01:26 +03:00
|
|
|
struct bt_gatt_discover_params *params,
|
|
|
|
u16_t length)
|
2015-07-06 13:36:16 +03:00
|
|
|
{
|
|
|
|
const struct bt_att_read_type_rsp *rsp = pdu;
|
2018-11-29 11:23:03 -08:00
|
|
|
u16_t handle = 0U;
|
2015-09-21 13:37:43 +03:00
|
|
|
struct bt_gatt_include value;
|
2016-01-27 22:43:34 -05:00
|
|
|
union {
|
|
|
|
struct bt_uuid uuid;
|
|
|
|
struct bt_uuid_16 u16;
|
|
|
|
struct bt_uuid_128 u128;
|
|
|
|
} u;
|
2015-07-06 13:36:16 +03:00
|
|
|
|
2015-09-21 13:37:43 +03:00
|
|
|
/* Data can be either in UUID16 or UUID128 */
|
|
|
|
switch (rsp->len) {
|
|
|
|
case 8: /* UUID16 */
|
2016-01-27 22:43:34 -05:00
|
|
|
u.uuid.type = BT_UUID_TYPE_16;
|
2015-09-21 13:37:43 +03:00
|
|
|
break;
|
|
|
|
case 6: /* UUID128 */
|
|
|
|
/* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] page 550
|
|
|
|
* To get the included service UUID when the included service
|
|
|
|
* uses a 128-bit UUID, the Read Request is used.
|
|
|
|
*/
|
2016-01-27 22:43:34 -05:00
|
|
|
u.uuid.type = BT_UUID_TYPE_128;
|
2015-09-21 13:37:43 +03:00
|
|
|
break;
|
|
|
|
default:
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_ERR("Invalid data len %u", rsp->len);
|
2015-07-06 13:36:16 +03:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2015-09-21 13:37:43 +03:00
|
|
|
/* Parse include found */
|
|
|
|
for (length--, pdu = rsp->data; length >= rsp->len;
|
2018-09-14 14:24:09 +02:00
|
|
|
length -= rsp->len, pdu = (const u8_t *)pdu + rsp->len) {
|
2015-10-23 10:52:28 +03:00
|
|
|
struct bt_gatt_attr *attr;
|
2015-09-21 13:37:43 +03:00
|
|
|
const struct bt_att_data *data = pdu;
|
|
|
|
struct gatt_incl *incl = (void *)data->value;
|
|
|
|
|
|
|
|
handle = sys_le16_to_cpu(data->handle);
|
|
|
|
/* Handle 0 is invalid */
|
|
|
|
if (!handle) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert include data, bt_gatt_incl and gatt_incl
|
|
|
|
* have different formats so the conversion have to be done
|
|
|
|
* field by field.
|
|
|
|
*/
|
2016-07-06 15:14:18 +02:00
|
|
|
value.start_handle = sys_le16_to_cpu(incl->start_handle);
|
|
|
|
value.end_handle = sys_le16_to_cpu(incl->end_handle);
|
2015-09-21 13:37:43 +03:00
|
|
|
|
2016-01-27 22:43:34 -05:00
|
|
|
switch (u.uuid.type) {
|
|
|
|
case BT_UUID_TYPE_16:
|
|
|
|
value.uuid = &u.uuid;
|
|
|
|
u.u16.val = sys_le16_to_cpu(incl->uuid16);
|
2015-09-21 13:37:43 +03:00
|
|
|
break;
|
2016-01-27 22:43:34 -05:00
|
|
|
case BT_UUID_TYPE_128:
|
2016-07-08 15:05:49 +02:00
|
|
|
params->_included.attr_handle = handle;
|
|
|
|
params->_included.start_handle = value.start_handle;
|
|
|
|
params->_included.end_handle = value.end_handle;
|
|
|
|
|
|
|
|
return read_included_uuid(conn, params);
|
2015-09-21 13:37:43 +03:00
|
|
|
}
|
|
|
|
|
2015-10-21 13:34:21 +03:00
|
|
|
BT_DBG("handle 0x%04x uuid %s start_handle 0x%04x "
|
2016-01-27 22:43:34 -05:00
|
|
|
"end_handle 0x%04x\n", handle, bt_uuid_str(&u.uuid),
|
2015-10-21 13:34:21 +03:00
|
|
|
value.start_handle, value.end_handle);
|
2015-09-21 13:37:43 +03:00
|
|
|
|
|
|
|
/* Skip if UUID is set but doesn't match */
|
2016-01-27 22:43:34 -05:00
|
|
|
if (params->uuid && bt_uuid_cmp(&u.uuid, params->uuid)) {
|
2015-09-21 13:37:43 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-04-12 21:07:53 +02:00
|
|
|
attr = (&(struct bt_gatt_attr) {
|
|
|
|
.uuid = BT_UUID_GATT_INCLUDE,
|
|
|
|
.user_data = &value, });
|
2015-10-23 10:52:28 +03:00
|
|
|
attr->handle = handle;
|
2015-09-21 13:37:43 +03:00
|
|
|
|
2015-12-11 12:34:35 +01:00
|
|
|
if (params->func(conn, attr, params) == BT_GATT_ITER_STOP) {
|
2016-01-21 10:47:12 +01:00
|
|
|
return 0;
|
2015-09-21 13:37:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-21 10:47:12 +01:00
|
|
|
/* Whole PDU read without error */
|
2019-03-26 19:57:45 -06:00
|
|
|
if (length == 0U && handle) {
|
2016-01-21 10:47:12 +01:00
|
|
|
return handle;
|
2015-09-21 13:37:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2016-01-21 10:47:12 +01:00
|
|
|
params->func(conn, NULL, params);
|
|
|
|
return 0;
|
2015-09-21 13:37:43 +03:00
|
|
|
}
|
|
|
|
|
2018-05-11 21:53:02 +03:00
|
|
|
#define BT_GATT_CHRC(_uuid, _props) \
|
|
|
|
BT_GATT_ATTRIBUTE(BT_UUID_GATT_CHRC, BT_GATT_PERM_READ, \
|
|
|
|
bt_gatt_attr_read_chrc, NULL, \
|
|
|
|
(&(struct bt_gatt_chrc) { .uuid = _uuid, \
|
|
|
|
.properties = _props, }))
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static u16_t parse_characteristic(struct bt_conn *conn, const void *pdu,
|
2017-04-21 09:01:26 +03:00
|
|
|
struct bt_gatt_discover_params *params,
|
|
|
|
u16_t length)
|
2015-09-21 13:37:43 +03:00
|
|
|
{
|
|
|
|
const struct bt_att_read_type_rsp *rsp = pdu;
|
2018-11-29 11:23:03 -08:00
|
|
|
u16_t handle = 0U;
|
2016-01-27 22:43:34 -05:00
|
|
|
union {
|
|
|
|
struct bt_uuid uuid;
|
|
|
|
struct bt_uuid_16 u16;
|
|
|
|
struct bt_uuid_128 u128;
|
|
|
|
} u;
|
2015-09-21 13:37:43 +03:00
|
|
|
|
2015-07-06 13:36:16 +03:00
|
|
|
/* Data can be either in UUID16 or UUID128 */
|
|
|
|
switch (rsp->len) {
|
|
|
|
case 7: /* UUID16 */
|
2016-01-27 22:43:34 -05:00
|
|
|
u.uuid.type = BT_UUID_TYPE_16;
|
2015-07-06 13:36:16 +03:00
|
|
|
break;
|
|
|
|
case 21: /* UUID128 */
|
2016-01-27 22:43:34 -05:00
|
|
|
u.uuid.type = BT_UUID_TYPE_128;
|
2015-07-06 13:36:16 +03:00
|
|
|
break;
|
|
|
|
default:
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_ERR("Invalid data len %u", rsp->len);
|
2015-07-06 13:36:16 +03:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse characteristics found */
|
|
|
|
for (length--, pdu = rsp->data; length >= rsp->len;
|
2018-09-14 14:24:09 +02:00
|
|
|
length -= rsp->len, pdu = (const u8_t *)pdu + rsp->len) {
|
2015-10-23 10:52:28 +03:00
|
|
|
struct bt_gatt_attr *attr;
|
2015-07-06 13:36:16 +03:00
|
|
|
const struct bt_att_data *data = pdu;
|
|
|
|
struct gatt_chrc *chrc = (void *)data->value;
|
|
|
|
|
|
|
|
handle = sys_le16_to_cpu(data->handle);
|
|
|
|
/* Handle 0 is invalid */
|
|
|
|
if (!handle) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2016-01-27 22:43:34 -05:00
|
|
|
switch (u.uuid.type) {
|
|
|
|
case BT_UUID_TYPE_16:
|
|
|
|
u.u16.val = sys_le16_to_cpu(chrc->uuid16);
|
2015-07-06 13:36:16 +03:00
|
|
|
break;
|
2016-01-27 22:43:34 -05:00
|
|
|
case BT_UUID_TYPE_128:
|
|
|
|
memcpy(u.u128.val, chrc->uuid, sizeof(chrc->uuid));
|
2015-07-06 13:36:16 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x uuid %s properties 0x%02x", handle,
|
2016-01-27 22:43:34 -05:00
|
|
|
bt_uuid_str(&u.uuid), chrc->properties);
|
2015-07-06 13:36:16 +03:00
|
|
|
|
|
|
|
/* Skip if UUID is set but doesn't match */
|
2016-01-27 22:43:34 -05:00
|
|
|
if (params->uuid && bt_uuid_cmp(&u.uuid, params->uuid)) {
|
2015-07-06 13:36:16 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-05-11 21:53:02 +03:00
|
|
|
attr = (&(struct bt_gatt_attr)BT_GATT_CHRC(&u.uuid,
|
|
|
|
chrc->properties));
|
2015-10-23 10:52:28 +03:00
|
|
|
attr->handle = handle;
|
2015-07-06 13:36:16 +03:00
|
|
|
|
2015-12-11 12:34:35 +01:00
|
|
|
if (params->func(conn, attr, params) == BT_GATT_ITER_STOP) {
|
2016-01-21 10:47:12 +01:00
|
|
|
return 0;
|
2015-07-06 13:36:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-21 10:47:12 +01:00
|
|
|
/* Whole PDU read without error */
|
2019-03-26 19:57:45 -06:00
|
|
|
if (length == 0U && handle) {
|
2016-01-21 10:47:12 +01:00
|
|
|
return handle;
|
2015-09-21 13:37:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2016-01-21 10:47:12 +01:00
|
|
|
params->func(conn, NULL, params);
|
|
|
|
return 0;
|
2015-09-21 13:37:43 +03:00
|
|
|
}
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static void gatt_read_type_rsp(struct bt_conn *conn, u8_t err,
|
|
|
|
const void *pdu, u16_t length,
|
2016-11-15 12:39:25 +02:00
|
|
|
void *user_data)
|
2015-09-21 13:37:43 +03:00
|
|
|
{
|
|
|
|
struct bt_gatt_discover_params *params = user_data;
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t handle;
|
2015-09-21 13:37:43 +03:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("err 0x%02x", err);
|
2015-09-21 13:37:43 +03:00
|
|
|
|
|
|
|
if (err) {
|
2016-10-27 14:45:57 +03:00
|
|
|
params->func(conn, NULL, params);
|
|
|
|
return;
|
2015-09-21 13:37:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (params->type == BT_GATT_DISCOVER_INCLUDE) {
|
2015-12-11 12:34:35 +01:00
|
|
|
handle = parse_include(conn, pdu, params, length);
|
2015-09-21 13:37:43 +03:00
|
|
|
} else {
|
2015-12-11 12:34:35 +01:00
|
|
|
handle = parse_characteristic(conn, pdu, params, length);
|
2015-09-21 13:37:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!handle) {
|
2016-01-21 10:47:12 +01:00
|
|
|
return;
|
2015-07-06 13:36:16 +03:00
|
|
|
}
|
|
|
|
|
2016-10-27 14:45:57 +03:00
|
|
|
gatt_discover_next(conn, handle, params);
|
2015-07-06 13:36:16 +03:00
|
|
|
}
|
|
|
|
|
2016-11-15 12:39:25 +02:00
|
|
|
static int gatt_read_type(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_discover_params *params)
|
2015-07-06 13:36:16 +03:00
|
|
|
{
|
2015-10-28 10:51:40 +02:00
|
|
|
struct net_buf *buf;
|
2015-07-06 13:36:16 +03:00
|
|
|
struct bt_att_read_type_req *req;
|
|
|
|
|
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_TYPE_REQ, sizeof(*req));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2015-10-28 10:51:40 +02:00
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
2015-07-06 13:36:16 +03:00
|
|
|
req->start_handle = sys_cpu_to_le16(params->start_handle);
|
|
|
|
req->end_handle = sys_cpu_to_le16(params->end_handle);
|
|
|
|
|
2016-10-16 14:57:41 +03:00
|
|
|
if (params->type == BT_GATT_DISCOVER_INCLUDE) {
|
2018-02-12 16:11:33 +02:00
|
|
|
net_buf_add_le16(buf, BT_UUID_16(BT_UUID_GATT_INCLUDE)->val);
|
2016-10-16 14:57:41 +03:00
|
|
|
} else {
|
2018-02-12 16:11:33 +02:00
|
|
|
net_buf_add_le16(buf, BT_UUID_16(BT_UUID_GATT_CHRC)->val);
|
2016-10-16 14:57:41 +03:00
|
|
|
}
|
2015-07-06 13:36:16 +03:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("start_handle 0x%04x end_handle 0x%04x", params->start_handle,
|
2015-07-06 13:36:16 +03:00
|
|
|
params->end_handle);
|
|
|
|
|
2016-11-15 12:39:25 +02:00
|
|
|
return gatt_send(conn, buf, gatt_read_type_rsp, params, NULL);
|
2015-07-06 13:36:16 +03:00
|
|
|
}
|
|
|
|
|
2018-09-14 16:09:59 +03:00
|
|
|
static u16_t parse_service(struct bt_conn *conn, const void *pdu,
|
|
|
|
struct bt_gatt_discover_params *params,
|
|
|
|
u16_t length)
|
|
|
|
{
|
|
|
|
const struct bt_att_read_group_rsp *rsp = pdu;
|
2018-11-29 11:23:03 -08:00
|
|
|
u16_t start_handle, end_handle = 0U;
|
2018-09-14 16:09:59 +03:00
|
|
|
union {
|
|
|
|
struct bt_uuid uuid;
|
|
|
|
struct bt_uuid_16 u16;
|
|
|
|
struct bt_uuid_128 u128;
|
|
|
|
} u;
|
|
|
|
|
|
|
|
/* Data can be either in UUID16 or UUID128 */
|
|
|
|
switch (rsp->len) {
|
|
|
|
case 6: /* UUID16 */
|
|
|
|
u.uuid.type = BT_UUID_TYPE_16;
|
|
|
|
break;
|
|
|
|
case 20: /* UUID128 */
|
|
|
|
u.uuid.type = BT_UUID_TYPE_128;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BT_ERR("Invalid data len %u", rsp->len);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse services found */
|
|
|
|
for (length--, pdu = rsp->data; length >= rsp->len;
|
2018-09-14 14:24:09 +02:00
|
|
|
length -= rsp->len, pdu = (const u8_t *)pdu + rsp->len) {
|
2018-09-14 16:09:59 +03:00
|
|
|
struct bt_gatt_attr attr = {};
|
|
|
|
struct bt_gatt_service_val value;
|
|
|
|
const struct bt_att_group_data *data = pdu;
|
|
|
|
|
|
|
|
start_handle = sys_le16_to_cpu(data->start_handle);
|
|
|
|
if (!start_handle) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
end_handle = sys_le16_to_cpu(data->end_handle);
|
|
|
|
if (!end_handle || end_handle < start_handle) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (u.uuid.type) {
|
|
|
|
case BT_UUID_TYPE_16:
|
|
|
|
memcpy(&u.u16.val, data->value, sizeof(u.u16.val));
|
|
|
|
u.u16.val = sys_le16_to_cpu(u.u16.val);
|
|
|
|
break;
|
|
|
|
case BT_UUID_TYPE_128:
|
|
|
|
memcpy(u.u128.val, data->value, sizeof(u.u128.val));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("start_handle 0x%04x end_handle 0x%04x uuid %s",
|
|
|
|
start_handle, end_handle, bt_uuid_str(&u.uuid));
|
|
|
|
|
|
|
|
if (params->type == BT_GATT_DISCOVER_PRIMARY) {
|
|
|
|
attr.uuid = BT_UUID_GATT_PRIMARY;
|
|
|
|
} else {
|
|
|
|
attr.uuid = BT_UUID_GATT_SECONDARY;
|
|
|
|
}
|
|
|
|
|
|
|
|
value.end_handle = end_handle;
|
|
|
|
value.uuid = &u.uuid;
|
|
|
|
|
|
|
|
attr.handle = start_handle;
|
|
|
|
attr.user_data = &value;
|
|
|
|
|
|
|
|
if (params->func(conn, &attr, params) == BT_GATT_ITER_STOP) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Whole PDU read without error */
|
2019-03-26 19:57:45 -06:00
|
|
|
if (length == 0U && end_handle) {
|
2018-09-14 16:09:59 +03:00
|
|
|
return end_handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
params->func(conn, NULL, params);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gatt_read_group_rsp(struct bt_conn *conn, u8_t err,
|
|
|
|
const void *pdu, u16_t length,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct bt_gatt_discover_params *params = user_data;
|
|
|
|
u16_t handle;
|
|
|
|
|
|
|
|
BT_DBG("err 0x%02x", err);
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
params->func(conn, NULL, params);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
handle = parse_service(conn, pdu, params, length);
|
|
|
|
if (!handle) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
gatt_discover_next(conn, handle, params);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gatt_read_group(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_discover_params *params)
|
|
|
|
{
|
|
|
|
struct net_buf *buf;
|
|
|
|
struct bt_att_read_group_req *req;
|
|
|
|
|
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_GROUP_REQ, sizeof(*req));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
|
|
|
req->start_handle = sys_cpu_to_le16(params->start_handle);
|
|
|
|
req->end_handle = sys_cpu_to_le16(params->end_handle);
|
|
|
|
|
|
|
|
if (params->type == BT_GATT_DISCOVER_PRIMARY) {
|
|
|
|
net_buf_add_le16(buf, BT_UUID_16(BT_UUID_GATT_PRIMARY)->val);
|
|
|
|
} else {
|
|
|
|
net_buf_add_le16(buf, BT_UUID_16(BT_UUID_GATT_SECONDARY)->val);
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("start_handle 0x%04x end_handle 0x%04x", params->start_handle,
|
|
|
|
params->end_handle);
|
|
|
|
|
|
|
|
return gatt_send(conn, buf, gatt_read_group_rsp, params, NULL);
|
|
|
|
}
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static void gatt_find_info_rsp(struct bt_conn *conn, u8_t err,
|
|
|
|
const void *pdu, u16_t length,
|
2016-11-15 12:39:25 +02:00
|
|
|
void *user_data)
|
2015-07-08 10:45:43 +03:00
|
|
|
{
|
|
|
|
const struct bt_att_find_info_rsp *rsp = pdu;
|
|
|
|
struct bt_gatt_discover_params *params = user_data;
|
2018-11-29 11:23:03 -08:00
|
|
|
u16_t handle = 0U;
|
2017-04-20 12:00:29 -05:00
|
|
|
u8_t len;
|
2015-07-08 10:45:43 +03:00
|
|
|
union {
|
|
|
|
const struct bt_att_info_16 *i16;
|
|
|
|
const struct bt_att_info_128 *i128;
|
|
|
|
} info;
|
2016-01-27 22:43:34 -05:00
|
|
|
union {
|
|
|
|
struct bt_uuid uuid;
|
|
|
|
struct bt_uuid_16 u16;
|
|
|
|
struct bt_uuid_128 u128;
|
|
|
|
} u;
|
2015-07-08 10:45:43 +03:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("err 0x%02x", err);
|
2015-07-08 10:45:43 +03:00
|
|
|
|
|
|
|
if (err) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Data can be either in UUID16 or UUID128 */
|
|
|
|
switch (rsp->format) {
|
|
|
|
case BT_ATT_INFO_16:
|
2016-01-27 22:43:34 -05:00
|
|
|
u.uuid.type = BT_UUID_TYPE_16;
|
2015-10-13 11:53:56 +02:00
|
|
|
len = sizeof(*info.i16);
|
2015-07-08 10:45:43 +03:00
|
|
|
break;
|
|
|
|
case BT_ATT_INFO_128:
|
2016-01-27 22:43:34 -05:00
|
|
|
u.uuid.type = BT_UUID_TYPE_128;
|
2015-10-13 11:53:56 +02:00
|
|
|
len = sizeof(*info.i128);
|
2015-07-08 10:45:43 +03:00
|
|
|
break;
|
|
|
|
default:
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_ERR("Invalid format %u", rsp->format);
|
2015-07-08 10:45:43 +03:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse descriptors found */
|
|
|
|
for (length--, pdu = rsp->info; length >= len;
|
2018-09-14 14:24:09 +02:00
|
|
|
length -= len, pdu = (const u8_t *)pdu + len) {
|
2015-10-23 10:52:28 +03:00
|
|
|
struct bt_gatt_attr *attr;
|
2015-07-08 10:45:43 +03:00
|
|
|
|
|
|
|
info.i16 = pdu;
|
|
|
|
handle = sys_le16_to_cpu(info.i16->handle);
|
|
|
|
|
2016-01-27 22:43:34 -05:00
|
|
|
switch (u.uuid.type) {
|
|
|
|
case BT_UUID_TYPE_16:
|
|
|
|
u.u16.val = sys_le16_to_cpu(info.i16->uuid);
|
2015-07-08 10:45:43 +03:00
|
|
|
break;
|
2016-01-27 22:43:34 -05:00
|
|
|
case BT_UUID_TYPE_128:
|
|
|
|
memcpy(u.u128.val, info.i128->uuid, 16);
|
2015-07-08 10:45:43 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-01-27 22:43:34 -05:00
|
|
|
BT_DBG("handle 0x%04x uuid %s", handle, bt_uuid_str(&u.uuid));
|
2015-07-08 10:45:43 +03:00
|
|
|
|
|
|
|
/* Skip if UUID is set but doesn't match */
|
2016-01-27 22:43:34 -05:00
|
|
|
if (params->uuid && bt_uuid_cmp(&u.uuid, params->uuid)) {
|
2015-07-08 10:45:43 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-03-13 10:09:41 +02:00
|
|
|
if (params->type == BT_GATT_DISCOVER_DESCRIPTOR) {
|
|
|
|
/* Skip attributes that are not considered
|
|
|
|
* descriptors.
|
|
|
|
*/
|
|
|
|
if (!bt_uuid_cmp(&u.uuid, BT_UUID_GATT_PRIMARY) ||
|
|
|
|
!bt_uuid_cmp(&u.uuid, BT_UUID_GATT_SECONDARY) ||
|
|
|
|
!bt_uuid_cmp(&u.uuid, BT_UUID_GATT_INCLUDE)) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-03-12 18:25:58 +02:00
|
|
|
|
2019-03-13 10:09:41 +02:00
|
|
|
/* If Characteristic Declaration skip ahead as the next
|
|
|
|
* entry must be its value.
|
|
|
|
*/
|
|
|
|
if (!bt_uuid_cmp(&u.uuid, BT_UUID_GATT_CHRC)) {
|
|
|
|
if (length >= len) {
|
|
|
|
pdu = (const u8_t *)pdu + len;
|
|
|
|
length -= len;
|
|
|
|
}
|
|
|
|
continue;
|
2019-03-12 18:25:58 +02:00
|
|
|
}
|
2019-03-11 23:07:10 +02:00
|
|
|
}
|
|
|
|
|
2015-07-08 10:45:43 +03:00
|
|
|
attr = (&(struct bt_gatt_attr)
|
2016-01-27 22:43:34 -05:00
|
|
|
BT_GATT_DESCRIPTOR(&u.uuid, 0, NULL, NULL, NULL));
|
2015-10-23 10:52:28 +03:00
|
|
|
attr->handle = handle;
|
2015-07-08 10:45:43 +03:00
|
|
|
|
2015-12-11 12:34:35 +01:00
|
|
|
if (params->func(conn, attr, params) == BT_GATT_ITER_STOP) {
|
2016-01-21 10:47:05 +01:00
|
|
|
return;
|
2015-07-08 10:45:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stop if could not parse the whole PDU */
|
|
|
|
if (length > 0) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2016-10-27 14:45:57 +03:00
|
|
|
gatt_discover_next(conn, handle, params);
|
2015-07-08 10:45:43 +03:00
|
|
|
|
2016-10-27 14:45:57 +03:00
|
|
|
return;
|
2015-07-08 10:45:43 +03:00
|
|
|
|
|
|
|
done:
|
2016-01-21 10:47:05 +01:00
|
|
|
params->func(conn, NULL, params);
|
2015-07-08 10:45:43 +03:00
|
|
|
}
|
|
|
|
|
2016-11-15 12:39:25 +02:00
|
|
|
static int gatt_find_info(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_discover_params *params)
|
2015-07-08 10:45:43 +03:00
|
|
|
{
|
2015-10-28 10:51:40 +02:00
|
|
|
struct net_buf *buf;
|
2015-07-08 10:45:43 +03:00
|
|
|
struct bt_att_find_info_req *req;
|
|
|
|
|
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_FIND_INFO_REQ, sizeof(*req));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2015-10-28 10:51:40 +02:00
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
2015-07-08 10:45:43 +03:00
|
|
|
req->start_handle = sys_cpu_to_le16(params->start_handle);
|
|
|
|
req->end_handle = sys_cpu_to_le16(params->end_handle);
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("start_handle 0x%04x end_handle 0x%04x", params->start_handle,
|
2015-07-08 10:45:43 +03:00
|
|
|
params->end_handle);
|
|
|
|
|
2016-11-15 12:39:25 +02:00
|
|
|
return gatt_send(conn, buf, gatt_find_info_rsp, params, NULL);
|
2015-07-08 10:45:43 +03:00
|
|
|
}
|
|
|
|
|
2015-09-21 11:12:15 +03:00
|
|
|
int bt_gatt_discover(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_discover_params *params)
|
|
|
|
{
|
2017-02-14 19:55:00 +02:00
|
|
|
__ASSERT(conn, "invalid parameters\n");
|
|
|
|
__ASSERT(params && params->func, "invalid parameters\n");
|
|
|
|
__ASSERT((params->start_handle && params->end_handle),
|
|
|
|
"invalid parameters\n");
|
2018-06-15 14:43:04 +02:00
|
|
|
__ASSERT((params->start_handle <= params->end_handle),
|
2017-02-14 19:55:00 +02:00
|
|
|
"invalid parameters\n");
|
2015-09-21 11:12:15 +03:00
|
|
|
|
2016-10-27 14:52:15 +03:00
|
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
|
|
|
return -ENOTCONN;
|
|
|
|
}
|
|
|
|
|
2015-09-21 11:12:15 +03:00
|
|
|
switch (params->type) {
|
|
|
|
case BT_GATT_DISCOVER_PRIMARY:
|
|
|
|
case BT_GATT_DISCOVER_SECONDARY:
|
2018-09-14 16:09:59 +03:00
|
|
|
if (params->uuid) {
|
|
|
|
return gatt_find_type(conn, params);
|
|
|
|
}
|
|
|
|
return gatt_read_group(conn, params);
|
2015-09-21 11:12:15 +03:00
|
|
|
case BT_GATT_DISCOVER_INCLUDE:
|
|
|
|
case BT_GATT_DISCOVER_CHARACTERISTIC:
|
2016-11-15 12:39:25 +02:00
|
|
|
return gatt_read_type(conn, params);
|
2015-09-21 11:12:15 +03:00
|
|
|
case BT_GATT_DISCOVER_DESCRIPTOR:
|
2019-03-11 23:07:10 +02:00
|
|
|
/* Only descriptors can be filtered */
|
|
|
|
if (!bt_uuid_cmp(params->uuid, BT_UUID_GATT_PRIMARY) ||
|
|
|
|
!bt_uuid_cmp(params->uuid, BT_UUID_GATT_SECONDARY) ||
|
|
|
|
!bt_uuid_cmp(params->uuid, BT_UUID_GATT_INCLUDE) ||
|
|
|
|
!bt_uuid_cmp(params->uuid, BT_UUID_GATT_CHRC)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2019-03-13 10:09:41 +02:00
|
|
|
/* Fallthrough. */
|
|
|
|
case BT_GATT_DISCOVER_ATTRIBUTE:
|
2016-11-15 12:39:25 +02:00
|
|
|
return gatt_find_info(conn, params);
|
2015-09-21 11:12:15 +03:00
|
|
|
default:
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_ERR("Invalid discovery type: %u", params->type);
|
2015-09-21 11:12:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static void gatt_read_rsp(struct bt_conn *conn, u8_t err, const void *pdu,
|
|
|
|
u16_t length, void *user_data)
|
2015-07-13 14:07:04 +03:00
|
|
|
{
|
2015-11-06 22:11:37 +01:00
|
|
|
struct bt_gatt_read_params *params = user_data;
|
2015-07-13 14:07:04 +03:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("err 0x%02x", err);
|
2015-07-13 14:07:04 +03:00
|
|
|
|
2016-11-04 12:18:08 +01:00
|
|
|
if (err || !length) {
|
2016-01-21 10:47:12 +01:00
|
|
|
params->func(conn, err, params, NULL, 0);
|
|
|
|
return;
|
2015-07-13 14:07:04 +03:00
|
|
|
}
|
|
|
|
|
2016-01-21 10:47:12 +01:00
|
|
|
if (params->func(conn, 0, params, pdu, length) == BT_GATT_ITER_STOP) {
|
|
|
|
return;
|
2015-11-06 22:11:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Core Spec 4.2, Vol. 3, Part G, 4.8.1
|
2016-07-19 11:10:20 +02:00
|
|
|
* If the Characteristic Value is greater than (ATT_MTU - 1) octets
|
2015-11-06 22:11:37 +01:00
|
|
|
* in length, the Read Long Characteristic Value procedure may be used
|
|
|
|
* if the rest of the Characteristic Value is required.
|
|
|
|
*/
|
2016-04-11 15:47:31 +02:00
|
|
|
if (length < (bt_att_get_mtu(conn) - 1)) {
|
2016-01-21 10:47:12 +01:00
|
|
|
params->func(conn, 0, params, NULL, 0);
|
|
|
|
return;
|
2015-11-06 22:11:37 +01:00
|
|
|
}
|
|
|
|
|
2016-01-21 10:47:12 +01:00
|
|
|
params->single.offset += length;
|
2015-11-06 22:11:37 +01:00
|
|
|
|
|
|
|
/* Continue reading the attribute */
|
2015-11-16 12:14:58 +01:00
|
|
|
if (bt_gatt_read(conn, params) < 0) {
|
2016-01-21 10:47:12 +01:00
|
|
|
params->func(conn, BT_ATT_ERR_UNLIKELY, params, NULL, 0);
|
2015-11-06 22:11:37 +01:00
|
|
|
}
|
2015-07-13 14:07:04 +03:00
|
|
|
}
|
|
|
|
|
2015-11-06 22:11:37 +01:00
|
|
|
static int gatt_read_blob(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_read_params *params)
|
2015-07-13 14:07:04 +03:00
|
|
|
{
|
2015-10-28 10:51:40 +02:00
|
|
|
struct net_buf *buf;
|
2015-07-13 14:07:04 +03:00
|
|
|
struct bt_att_read_blob_req *req;
|
|
|
|
|
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_BLOB_REQ, sizeof(*req));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2015-10-28 10:51:40 +02:00
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
2016-01-21 10:47:12 +01:00
|
|
|
req->handle = sys_cpu_to_le16(params->single.handle);
|
|
|
|
req->offset = sys_cpu_to_le16(params->single.offset);
|
2015-07-13 14:07:04 +03:00
|
|
|
|
2016-01-21 10:47:12 +01:00
|
|
|
BT_DBG("handle 0x%04x offset 0x%04x", params->single.handle,
|
|
|
|
params->single.offset);
|
2015-07-13 14:07:04 +03:00
|
|
|
|
2016-11-15 12:39:25 +02:00
|
|
|
return gatt_send(conn, buf, gatt_read_rsp, params, NULL);
|
2015-07-13 14:07:04 +03:00
|
|
|
}
|
|
|
|
|
2018-06-21 17:17:56 +02:00
|
|
|
#if defined(CONFIG_BT_GATT_READ_MULTIPLE)
|
2017-04-20 12:00:29 -05:00
|
|
|
static void gatt_read_multiple_rsp(struct bt_conn *conn, u8_t err,
|
|
|
|
const void *pdu, u16_t length,
|
2016-11-15 12:39:25 +02:00
|
|
|
void *user_data)
|
2016-01-21 10:47:12 +01:00
|
|
|
{
|
|
|
|
struct bt_gatt_read_params *params = user_data;
|
|
|
|
|
|
|
|
BT_DBG("err 0x%02x", err);
|
|
|
|
|
2016-11-04 12:18:08 +01:00
|
|
|
if (err || !length) {
|
2016-01-21 10:47:12 +01:00
|
|
|
params->func(conn, err, params, NULL, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
params->func(conn, 0, params, pdu, length);
|
|
|
|
|
|
|
|
/* mark read as complete since read multiple is single response */
|
|
|
|
params->func(conn, 0, params, NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gatt_read_multiple(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_read_params *params)
|
|
|
|
{
|
|
|
|
struct net_buf *buf;
|
2017-04-20 12:00:29 -05:00
|
|
|
u8_t i;
|
2016-01-21 10:47:12 +01:00
|
|
|
|
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_MULT_REQ,
|
2017-04-20 12:00:29 -05:00
|
|
|
params->handle_count * sizeof(u16_t));
|
2016-01-21 10:47:12 +01:00
|
|
|
if (!buf) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2018-11-29 11:23:03 -08:00
|
|
|
for (i = 0U; i < params->handle_count; i++) {
|
2016-01-21 10:47:12 +01:00
|
|
|
net_buf_add_le16(buf, params->handles[i]);
|
|
|
|
}
|
|
|
|
|
2016-11-15 12:39:25 +02:00
|
|
|
return gatt_send(conn, buf, gatt_read_multiple_rsp, params, NULL);
|
2016-01-21 10:47:12 +01:00
|
|
|
}
|
2018-06-21 17:17:56 +02:00
|
|
|
#else
|
|
|
|
static int gatt_read_multiple(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_read_params *params)
|
|
|
|
{
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_BT_GATT_READ_MULTIPLE */
|
2016-01-21 10:47:12 +01:00
|
|
|
|
2015-11-06 22:11:37 +01:00
|
|
|
int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params)
|
2015-07-13 14:07:04 +03:00
|
|
|
{
|
2015-10-28 10:51:40 +02:00
|
|
|
struct net_buf *buf;
|
2015-07-13 14:07:04 +03:00
|
|
|
struct bt_att_read_req *req;
|
|
|
|
|
2017-02-14 19:55:00 +02:00
|
|
|
__ASSERT(conn, "invalid parameters\n");
|
|
|
|
__ASSERT(params && params->func, "invalid parameters\n");
|
|
|
|
__ASSERT(params->handle_count, "invalid parameters\n");
|
2015-07-13 14:07:04 +03:00
|
|
|
|
2016-10-27 14:52:15 +03:00
|
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
|
|
|
return -ENOTCONN;
|
|
|
|
}
|
|
|
|
|
2016-01-21 10:47:12 +01:00
|
|
|
if (params->handle_count > 1) {
|
|
|
|
return gatt_read_multiple(conn, params);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (params->single.offset) {
|
2015-11-06 22:11:37 +01:00
|
|
|
return gatt_read_blob(conn, params);
|
2015-07-13 14:07:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_REQ, sizeof(*req));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2015-10-28 10:51:40 +02:00
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
2016-01-21 10:47:12 +01:00
|
|
|
req->handle = sys_cpu_to_le16(params->single.handle);
|
2015-07-13 14:07:04 +03:00
|
|
|
|
2016-01-21 10:47:12 +01:00
|
|
|
BT_DBG("handle 0x%04x", params->single.handle);
|
2015-07-13 14:07:04 +03:00
|
|
|
|
2016-11-15 12:39:25 +02:00
|
|
|
return gatt_send(conn, buf, gatt_read_rsp, params, NULL);
|
2015-07-13 14:07:04 +03:00
|
|
|
}
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static void gatt_write_rsp(struct bt_conn *conn, u8_t err, const void *pdu,
|
|
|
|
u16_t length, void *user_data)
|
2015-07-15 13:28:58 +03:00
|
|
|
{
|
2016-07-26 15:26:15 +03:00
|
|
|
struct bt_gatt_write_params *params = user_data;
|
2015-07-15 13:28:58 +03:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("err 0x%02x", err);
|
2015-07-15 13:28:58 +03:00
|
|
|
|
2016-07-26 15:26:15 +03:00
|
|
|
params->func(conn, err, params);
|
2015-07-15 13:28:58 +03:00
|
|
|
}
|
|
|
|
|
2019-01-14 10:55:33 -03:00
|
|
|
int bt_gatt_write_without_response_cb(struct bt_conn *conn, u16_t handle,
|
|
|
|
const void *data, u16_t length, bool sign,
|
|
|
|
bt_gatt_complete_func_t func)
|
2015-07-15 13:28:58 +03:00
|
|
|
{
|
2015-10-28 10:51:40 +02:00
|
|
|
struct net_buf *buf;
|
2015-07-15 13:28:58 +03:00
|
|
|
struct bt_att_write_cmd *cmd;
|
|
|
|
|
2017-02-14 19:55:00 +02:00
|
|
|
__ASSERT(conn, "invalid parameters\n");
|
|
|
|
__ASSERT(handle, "invalid parameters\n");
|
2015-08-11 10:28:02 +03:00
|
|
|
|
2016-10-27 14:52:15 +03:00
|
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
|
|
|
return -ENOTCONN;
|
|
|
|
}
|
|
|
|
|
2017-08-09 09:21:11 +03:00
|
|
|
#if defined(CONFIG_BT_SMP)
|
2017-03-30 15:01:59 +03:00
|
|
|
if (conn->encrypt) {
|
|
|
|
/* Don't need to sign if already encrypted */
|
|
|
|
sign = false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (sign) {
|
2015-07-30 16:06:06 +03:00
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_SIGNED_WRITE_CMD,
|
|
|
|
sizeof(*cmd) + length + 12);
|
2015-09-09 17:27:16 +02:00
|
|
|
} else {
|
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_WRITE_CMD,
|
|
|
|
sizeof(*cmd) + length);
|
2015-07-30 16:06:06 +03:00
|
|
|
}
|
2015-07-15 13:28:58 +03:00
|
|
|
if (!buf) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2015-10-28 10:51:40 +02:00
|
|
|
cmd = net_buf_add(buf, sizeof(*cmd));
|
2015-07-15 13:28:58 +03:00
|
|
|
cmd->handle = sys_cpu_to_le16(handle);
|
|
|
|
memcpy(cmd->value, data, length);
|
2015-10-28 10:51:40 +02:00
|
|
|
net_buf_add(buf, length);
|
2015-07-15 13:28:58 +03:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x length %u", handle, length);
|
2015-07-15 13:28:58 +03:00
|
|
|
|
2019-01-14 10:55:33 -03:00
|
|
|
return bt_att_send(conn, buf, func);
|
2015-07-15 13:28:58 +03:00
|
|
|
}
|
|
|
|
|
2016-05-10 11:28:24 +03:00
|
|
|
static int gatt_exec_write(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_write_params *params)
|
2015-07-31 10:25:38 +03:00
|
|
|
{
|
2015-10-28 10:51:40 +02:00
|
|
|
struct net_buf *buf;
|
2015-07-31 10:25:38 +03:00
|
|
|
struct bt_att_exec_write_req *req;
|
|
|
|
|
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_EXEC_WRITE_REQ, sizeof(*req));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2015-10-28 10:51:40 +02:00
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
2015-07-31 10:25:38 +03:00
|
|
|
req->flags = BT_ATT_FLAG_EXEC;
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("");
|
2015-07-31 10:25:38 +03:00
|
|
|
|
2016-11-15 12:39:25 +02:00
|
|
|
return gatt_send(conn, buf, gatt_write_rsp, params, NULL);
|
2015-07-31 10:25:38 +03:00
|
|
|
}
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static void gatt_prepare_write_rsp(struct bt_conn *conn, u8_t err,
|
|
|
|
const void *pdu, u16_t length,
|
2016-11-15 12:39:25 +02:00
|
|
|
void *user_data)
|
2015-07-31 10:25:38 +03:00
|
|
|
{
|
2016-05-10 11:28:24 +03:00
|
|
|
struct bt_gatt_write_params *params = user_data;
|
2015-07-31 10:25:38 +03:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("err 0x%02x", err);
|
2015-07-31 10:25:38 +03:00
|
|
|
|
|
|
|
|
|
|
|
/* Don't continue in case of error */
|
|
|
|
if (err) {
|
2016-07-20 11:42:26 +03:00
|
|
|
params->func(conn, err, params);
|
2015-07-31 10:25:38 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If there is no more data execute */
|
2016-05-10 11:28:24 +03:00
|
|
|
if (!params->length) {
|
|
|
|
gatt_exec_write(conn, params);
|
2015-07-31 10:25:38 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write next chunk */
|
2016-05-10 11:28:24 +03:00
|
|
|
bt_gatt_write(conn, params);
|
2015-07-31 10:25:38 +03:00
|
|
|
}
|
|
|
|
|
2016-05-10 11:28:24 +03:00
|
|
|
static int gatt_prepare_write(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_write_params *params)
|
2015-07-31 10:25:38 +03:00
|
|
|
{
|
2015-10-28 10:51:40 +02:00
|
|
|
struct net_buf *buf;
|
2015-07-31 10:25:38 +03:00
|
|
|
struct bt_att_prepare_write_req *req;
|
2017-04-20 12:00:29 -05:00
|
|
|
u16_t len;
|
2015-07-31 10:25:38 +03:00
|
|
|
|
2019-02-11 17:14:19 +00:00
|
|
|
len = MIN(params->length, bt_att_get_mtu(conn) - sizeof(*req) - 1);
|
2015-07-31 10:25:38 +03:00
|
|
|
|
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_PREPARE_WRITE_REQ,
|
|
|
|
sizeof(*req) + len);
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2015-10-28 10:51:40 +02:00
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
2016-05-10 11:28:24 +03:00
|
|
|
req->handle = sys_cpu_to_le16(params->handle);
|
|
|
|
req->offset = sys_cpu_to_le16(params->offset);
|
|
|
|
memcpy(req->value, params->data, len);
|
2015-10-28 10:51:40 +02:00
|
|
|
net_buf_add(buf, len);
|
2015-07-31 10:25:38 +03:00
|
|
|
|
2016-05-10 11:28:24 +03:00
|
|
|
/* Update params */
|
|
|
|
params->offset += len;
|
2018-09-14 14:24:09 +02:00
|
|
|
params->data = (const u8_t *)params->data + len;
|
2016-05-10 11:28:24 +03:00
|
|
|
params->length -= len;
|
2015-07-31 10:25:38 +03:00
|
|
|
|
2016-05-10 11:28:24 +03:00
|
|
|
BT_DBG("handle 0x%04x offset %u len %u", params->handle, params->offset,
|
|
|
|
params->length);
|
|
|
|
|
2016-11-15 12:39:25 +02:00
|
|
|
return gatt_send(conn, buf, gatt_prepare_write_rsp, params, NULL);
|
2015-07-31 10:25:38 +03:00
|
|
|
}
|
|
|
|
|
2016-05-10 11:28:24 +03:00
|
|
|
int bt_gatt_write(struct bt_conn *conn, struct bt_gatt_write_params *params)
|
2015-07-15 13:28:58 +03:00
|
|
|
{
|
2015-10-28 10:51:40 +02:00
|
|
|
struct net_buf *buf;
|
2015-07-15 13:28:58 +03:00
|
|
|
struct bt_att_write_req *req;
|
|
|
|
|
2017-02-14 19:55:00 +02:00
|
|
|
__ASSERT(conn, "invalid parameters\n");
|
|
|
|
__ASSERT(params && params->func, "invalid parameters\n");
|
|
|
|
__ASSERT(params->handle, "invalid parameters\n");
|
2015-07-15 13:28:58 +03:00
|
|
|
|
2016-10-27 14:52:15 +03:00
|
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
|
|
|
return -ENOTCONN;
|
|
|
|
}
|
|
|
|
|
2015-07-31 10:25:38 +03:00
|
|
|
/* Use Prepare Write if offset is set or Long Write is required */
|
2016-05-10 11:28:24 +03:00
|
|
|
if (params->offset ||
|
|
|
|
params->length > (bt_att_get_mtu(conn) - sizeof(*req) - 1)) {
|
|
|
|
return gatt_prepare_write(conn, params);
|
2015-07-31 10:25:38 +03:00
|
|
|
}
|
|
|
|
|
2015-07-15 13:28:58 +03:00
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_WRITE_REQ,
|
2016-05-10 11:28:24 +03:00
|
|
|
sizeof(*req) + params->length);
|
2015-07-15 13:28:58 +03:00
|
|
|
if (!buf) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2015-10-28 10:51:40 +02:00
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
2016-05-10 11:28:24 +03:00
|
|
|
req->handle = sys_cpu_to_le16(params->handle);
|
|
|
|
memcpy(req->value, params->data, params->length);
|
|
|
|
net_buf_add(buf, params->length);
|
2015-07-15 13:28:58 +03:00
|
|
|
|
2016-05-10 11:28:24 +03:00
|
|
|
BT_DBG("handle 0x%04x length %u", params->handle, params->length);
|
2015-07-15 13:28:58 +03:00
|
|
|
|
2016-11-15 12:39:25 +02:00
|
|
|
return gatt_send(conn, buf, gatt_write_rsp, params, NULL);
|
2015-07-15 13:28:58 +03:00
|
|
|
}
|
|
|
|
|
2015-07-22 10:21:47 +03:00
|
|
|
static void gatt_subscription_add(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_subscribe_params *params)
|
|
|
|
{
|
2015-11-03 12:19:01 +01:00
|
|
|
bt_addr_le_copy(¶ms->_peer, &conn->le.dst);
|
2015-07-22 10:21:47 +03:00
|
|
|
|
|
|
|
/* Prepend subscription */
|
2017-02-03 14:19:08 +02:00
|
|
|
sys_slist_prepend(&subscriptions, ¶ms->node);
|
2015-07-22 10:21:47 +03:00
|
|
|
}
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static void gatt_write_ccc_rsp(struct bt_conn *conn, u8_t err,
|
|
|
|
const void *pdu, u16_t length,
|
2016-11-15 12:39:25 +02:00
|
|
|
void *user_data)
|
2015-07-22 10:21:47 +03:00
|
|
|
{
|
|
|
|
struct bt_gatt_subscribe_params *params = user_data;
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("err 0x%02x", err);
|
2015-07-22 10:21:47 +03:00
|
|
|
|
2016-01-21 10:47:09 +01:00
|
|
|
/* if write to CCC failed we remove subscription and notify app */
|
2015-07-22 10:21:47 +03:00
|
|
|
if (err) {
|
2017-02-03 14:19:08 +02:00
|
|
|
sys_snode_t *node, *tmp, *prev = NULL;
|
2015-07-22 10:21:47 +03:00
|
|
|
|
2017-02-03 14:19:08 +02:00
|
|
|
SYS_SLIST_FOR_EACH_NODE_SAFE(&subscriptions, node, tmp) {
|
|
|
|
if (node == ¶ms->node) {
|
|
|
|
gatt_subscription_remove(conn, tmp, params);
|
2016-01-21 10:47:09 +01:00
|
|
|
break;
|
|
|
|
}
|
2017-02-03 14:19:08 +02:00
|
|
|
|
|
|
|
prev = node;
|
2016-01-21 10:47:09 +01:00
|
|
|
}
|
2017-05-30 11:24:37 +03:00
|
|
|
} else if (!params->value) {
|
|
|
|
/* Notify with NULL data to complete unsubscribe */
|
|
|
|
params->notify(conn, params, NULL, 0);
|
2016-01-21 10:47:09 +01:00
|
|
|
}
|
2015-07-22 10:21:47 +03:00
|
|
|
}
|
|
|
|
|
2017-04-20 12:00:29 -05:00
|
|
|
static int gatt_write_ccc(struct bt_conn *conn, u16_t handle, u16_t value,
|
2015-07-22 10:21:47 +03:00
|
|
|
bt_att_func_t func,
|
|
|
|
struct bt_gatt_subscribe_params *params)
|
|
|
|
{
|
2015-10-28 10:51:40 +02:00
|
|
|
struct net_buf *buf;
|
2015-07-22 10:21:47 +03:00
|
|
|
struct bt_att_write_req *req;
|
|
|
|
|
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_WRITE_REQ,
|
2017-04-20 12:00:29 -05:00
|
|
|
sizeof(*req) + sizeof(u16_t));
|
2015-07-22 10:21:47 +03:00
|
|
|
if (!buf) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2015-10-28 10:51:40 +02:00
|
|
|
req = net_buf_add(buf, sizeof(*req));
|
2015-07-22 10:21:47 +03:00
|
|
|
req->handle = sys_cpu_to_le16(handle);
|
2015-10-28 10:51:40 +02:00
|
|
|
net_buf_add_le16(buf, value);
|
2015-07-22 10:21:47 +03:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x value 0x%04x", handle, value);
|
2015-07-22 10:21:47 +03:00
|
|
|
|
|
|
|
return gatt_send(conn, buf, func, params, NULL);
|
|
|
|
}
|
|
|
|
|
2015-11-10 15:58:43 +01:00
|
|
|
int bt_gatt_subscribe(struct bt_conn *conn,
|
2015-07-22 10:21:47 +03:00
|
|
|
struct bt_gatt_subscribe_params *params)
|
|
|
|
{
|
2017-02-08 16:48:26 +02:00
|
|
|
struct bt_gatt_subscribe_params *tmp;
|
2015-07-22 10:21:47 +03:00
|
|
|
bool has_subscription = false;
|
|
|
|
|
2017-02-14 19:55:00 +02:00
|
|
|
__ASSERT(conn, "invalid parameters\n");
|
|
|
|
__ASSERT(params && params->notify, "invalid parameters\n");
|
|
|
|
__ASSERT(params->value, "invalid parameters\n");
|
|
|
|
__ASSERT(params->ccc_handle, "invalid parameters\n");
|
2015-07-22 10:21:47 +03:00
|
|
|
|
2016-10-27 14:52:15 +03:00
|
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
|
|
|
return -ENOTCONN;
|
|
|
|
}
|
|
|
|
|
2015-07-22 10:21:47 +03:00
|
|
|
/* Lookup existing subscriptions */
|
2017-02-08 16:48:26 +02:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&subscriptions, tmp, node) {
|
2015-07-22 10:21:47 +03:00
|
|
|
/* Fail if entry already exists */
|
|
|
|
if (tmp == params) {
|
|
|
|
return -EALREADY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if another subscription exists */
|
2016-11-30 14:35:54 +02:00
|
|
|
if (!bt_conn_addr_le_cmp(conn, &tmp->_peer) &&
|
2015-09-15 10:40:03 +03:00
|
|
|
tmp->value_handle == params->value_handle &&
|
|
|
|
tmp->value >= params->value) {
|
2015-07-22 10:21:47 +03:00
|
|
|
has_subscription = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-21 10:47:09 +01:00
|
|
|
/* Skip write if already subscribed */
|
|
|
|
if (!has_subscription) {
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = gatt_write_ccc(conn, params->ccc_handle, params->value,
|
2017-05-30 11:24:37 +03:00
|
|
|
gatt_write_ccc_rsp, params);
|
2016-01-21 10:47:09 +01:00
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
2015-07-22 10:21:47 +03:00
|
|
|
}
|
|
|
|
|
2016-01-21 10:47:09 +01:00
|
|
|
/*
|
|
|
|
* Add subscription before write complete as some implementation were
|
|
|
|
* reported to send notification before reply to CCC write.
|
|
|
|
*/
|
|
|
|
gatt_subscription_add(conn, params);
|
|
|
|
|
|
|
|
return 0;
|
2015-07-22 10:21:47 +03:00
|
|
|
}
|
|
|
|
|
2015-11-10 15:58:43 +01:00
|
|
|
int bt_gatt_unsubscribe(struct bt_conn *conn,
|
2015-07-22 10:41:20 +03:00
|
|
|
struct bt_gatt_subscribe_params *params)
|
|
|
|
{
|
2017-02-15 15:52:38 +01:00
|
|
|
struct bt_gatt_subscribe_params *tmp, *next;
|
2015-07-22 10:41:20 +03:00
|
|
|
bool has_subscription = false, found = false;
|
2017-02-15 15:52:38 +01:00
|
|
|
sys_snode_t *prev = NULL;
|
2015-07-22 10:41:20 +03:00
|
|
|
|
2017-02-14 19:55:00 +02:00
|
|
|
__ASSERT(conn, "invalid parameters\n");
|
|
|
|
__ASSERT(params, "invalid parameters\n");
|
2015-07-22 10:41:20 +03:00
|
|
|
|
2016-10-27 14:52:15 +03:00
|
|
|
if (conn->state != BT_CONN_CONNECTED) {
|
|
|
|
return -ENOTCONN;
|
2015-07-22 10:41:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Lookup existing subscriptions */
|
2017-02-08 16:48:26 +02:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&subscriptions, tmp, next, node) {
|
2015-07-22 10:41:20 +03:00
|
|
|
/* Remove subscription */
|
2017-02-03 14:19:08 +02:00
|
|
|
if (params == tmp) {
|
2015-07-22 10:41:20 +03:00
|
|
|
found = true;
|
2017-02-15 15:52:38 +01:00
|
|
|
sys_slist_remove(&subscriptions, prev, &tmp->node);
|
2017-02-08 10:11:41 +01:00
|
|
|
continue;
|
2017-02-03 14:19:08 +02:00
|
|
|
} else {
|
2017-02-15 15:52:38 +01:00
|
|
|
prev = &tmp->node;
|
2015-07-22 10:41:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if there still remains any other subscription */
|
2016-11-30 14:35:54 +02:00
|
|
|
if (!bt_conn_addr_le_cmp(conn, &tmp->_peer) &&
|
2015-07-22 10:41:20 +03:00
|
|
|
tmp->value_handle == params->value_handle) {
|
|
|
|
has_subscription = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (has_subscription) {
|
2017-05-30 11:24:37 +03:00
|
|
|
/* Notify with NULL data to complete unsubscribe */
|
|
|
|
params->notify(conn, params, NULL, 0);
|
2015-07-22 10:41:20 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-05-30 11:24:37 +03:00
|
|
|
params->value = 0x0000;
|
|
|
|
|
|
|
|
return gatt_write_ccc(conn, params->ccc_handle, params->value,
|
|
|
|
gatt_write_ccc_rsp, params);
|
2015-07-22 10:41:20 +03:00
|
|
|
}
|
|
|
|
|
2016-08-02 16:23:26 +03:00
|
|
|
void bt_gatt_cancel(struct bt_conn *conn, void *params)
|
2015-07-03 14:52:57 +03:00
|
|
|
{
|
2016-08-02 16:23:26 +03:00
|
|
|
bt_att_req_cancel(conn, params);
|
2015-07-03 14:52:57 +03:00
|
|
|
}
|
2015-07-09 10:14:31 +02:00
|
|
|
|
2016-03-24 15:16:26 +02:00
|
|
|
static void add_subscriptions(struct bt_conn *conn)
|
|
|
|
{
|
2017-02-08 16:48:26 +02:00
|
|
|
struct bt_gatt_subscribe_params *params;
|
2016-03-24 15:16:26 +02:00
|
|
|
|
|
|
|
/* Lookup existing subscriptions */
|
2017-02-08 16:48:26 +02:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&subscriptions, params, node) {
|
2016-11-30 14:35:54 +02:00
|
|
|
if (bt_conn_addr_le_cmp(conn, ¶ms->_peer)) {
|
2016-03-24 15:16:26 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Force write to CCC to workaround devices that don't track
|
|
|
|
* it properly.
|
|
|
|
*/
|
|
|
|
gatt_write_ccc(conn, params->ccc_handle, params->value,
|
2018-03-06 15:38:36 +02:00
|
|
|
gatt_write_ccc_rsp, params);
|
2016-03-24 15:16:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-09 09:21:11 +03:00
|
|
|
#endif /* CONFIG_BT_GATT_CLIENT */
|
2016-03-24 15:16:26 +02:00
|
|
|
|
|
|
|
void bt_gatt_connected(struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
BT_DBG("conn %p", conn);
|
|
|
|
bt_gatt_foreach_attr(0x0001, 0xffff, connected_cb, conn);
|
2017-08-09 09:21:11 +03:00
|
|
|
#if defined(CONFIG_BT_GATT_CLIENT)
|
2016-03-24 15:16:26 +02:00
|
|
|
add_subscriptions(conn);
|
2017-08-09 09:21:11 +03:00
|
|
|
#endif /* CONFIG_BT_GATT_CLIENT */
|
2016-03-24 15:16:26 +02:00
|
|
|
}
|
2015-10-02 12:44:26 +02:00
|
|
|
|
2019-02-05 14:28:10 +02:00
|
|
|
bool bt_gatt_change_aware(struct bt_conn *conn, bool req)
|
|
|
|
{
|
|
|
|
#if defined(CONFIG_BT_GATT_CACHING)
|
|
|
|
struct gatt_cf_cfg *cfg;
|
|
|
|
|
2019-02-05 15:06:59 +02:00
|
|
|
cfg = find_cf_cfg(conn);
|
2019-02-05 14:28:10 +02:00
|
|
|
if (!cfg || !CF_ROBUST_CACHING(cfg)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (atomic_test_bit(cfg->flags, CF_CHANGE_AWARE)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G page 2350:
|
|
|
|
* If a change-unaware client sends an ATT command, the server shall
|
|
|
|
* ignore it.
|
|
|
|
*/
|
|
|
|
if (!req) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* BLUETOOTH CORE SPECIFICATION Version 5.1 | Vol 3, Part G page 2347:
|
|
|
|
* 2.5.2.1 Robust Caching
|
|
|
|
* A connected client becomes change-aware when...
|
|
|
|
* The server sends the client a response with the error code set to
|
|
|
|
* Database Out Of Sync and then the server receives another ATT
|
|
|
|
* request from the client.
|
|
|
|
*/
|
|
|
|
if (atomic_test_bit(cfg->flags, CF_OUT_OF_SYNC)) {
|
|
|
|
atomic_clear_bit(cfg->flags, CF_OUT_OF_SYNC);
|
|
|
|
atomic_set_bit(cfg->flags, CF_CHANGE_AWARE);
|
|
|
|
BT_DBG("%s change-aware", bt_addr_le_str(&cfg->peer));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
atomic_set_bit(cfg->flags, CF_OUT_OF_SYNC);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
#else
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-02-07 12:38:02 +02:00
|
|
|
static int bt_gatt_store_cf(struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
#if defined(CONFIG_BT_GATT_CACHING)
|
|
|
|
struct gatt_cf_cfg *cfg;
|
|
|
|
char key[BT_SETTINGS_KEY_MAX];
|
|
|
|
char *str;
|
|
|
|
size_t len;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
cfg = find_cf_cfg(conn);
|
|
|
|
if (!cfg) {
|
2019-02-21 11:40:27 +01:00
|
|
|
/* No cfg found, just clear it */
|
|
|
|
BT_DBG("No config for CF");
|
2019-02-07 12:38:02 +02:00
|
|
|
str = NULL;
|
|
|
|
len = 0;
|
2019-02-21 11:40:27 +01:00
|
|
|
} else {
|
|
|
|
str = (char *)cfg->data;
|
|
|
|
len = sizeof(cfg->data);
|
2019-02-07 12:38:02 +02:00
|
|
|
|
2019-02-21 11:40:27 +01:00
|
|
|
if (conn->id) {
|
|
|
|
char id_str[4];
|
2019-02-07 12:38:02 +02:00
|
|
|
|
2019-02-21 11:40:27 +01:00
|
|
|
snprintk(id_str, sizeof(id_str), "%u", conn->id);
|
|
|
|
bt_settings_encode_key(key, sizeof(key), "cf",
|
|
|
|
&conn->le.dst, id_str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg || !conn->id) {
|
2019-02-07 12:38:02 +02:00
|
|
|
bt_settings_encode_key(key, sizeof(key), "cf",
|
|
|
|
&conn->le.dst, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = settings_save_one(key, str, len);
|
|
|
|
if (err) {
|
|
|
|
BT_ERR("Failed to store Client Features (err %d)", err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-02-21 11:44:23 +01:00
|
|
|
BT_DBG("Stored CF for %s (%s)", bt_addr_le_str(&conn->le.dst), log_strdup(key));
|
2019-02-07 12:38:02 +02:00
|
|
|
#endif /* CONFIG_BT_GATT_CACHING */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-10-02 12:44:26 +02:00
|
|
|
void bt_gatt_disconnected(struct bt_conn *conn)
|
|
|
|
{
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("conn %p", conn);
|
2015-10-02 12:44:26 +02:00
|
|
|
bt_gatt_foreach_attr(0x0001, 0xffff, disconnected_cb, conn);
|
|
|
|
|
2018-10-08 14:33:40 +02:00
|
|
|
#if defined(CONFIG_BT_SETTINGS_CCC_STORE_ON_WRITE)
|
|
|
|
gatt_ccc_conn_unqueue(conn);
|
|
|
|
|
|
|
|
if (gatt_ccc_conn_queue_is_empty()) {
|
|
|
|
k_delayed_work_cancel(&gatt_ccc_store.work);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-05-08 14:58:00 +03:00
|
|
|
if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
|
2018-07-04 12:58:10 +03:00
|
|
|
bt_addr_le_is_bonded(conn->id, &conn->le.dst)) {
|
|
|
|
bt_gatt_store_ccc(conn->id, &conn->le.dst);
|
2019-02-07 12:38:02 +02:00
|
|
|
bt_gatt_store_cf(conn);
|
2018-05-08 14:58:00 +03:00
|
|
|
}
|
|
|
|
|
2017-08-09 09:21:11 +03:00
|
|
|
#if defined(CONFIG_BT_GATT_CLIENT)
|
2016-03-03 13:48:01 +02:00
|
|
|
remove_subscriptions(conn);
|
2017-08-09 09:21:11 +03:00
|
|
|
#endif /* CONFIG_BT_GATT_CLIENT */
|
2019-02-05 15:06:59 +02:00
|
|
|
|
|
|
|
#if defined(CONFIG_BT_GATT_CACHING)
|
|
|
|
remove_cf_cfg(conn);
|
|
|
|
#endif
|
2015-10-02 12:44:26 +02:00
|
|
|
}
|
2018-05-08 14:58:00 +03:00
|
|
|
|
|
|
|
#if defined(CONFIG_BT_SETTINGS)
|
|
|
|
|
|
|
|
#define CCC_STORE_MAX 48
|
|
|
|
|
|
|
|
static struct bt_gatt_ccc_cfg *ccc_find_cfg(struct _bt_gatt_ccc *ccc,
|
|
|
|
const bt_addr_le_t *addr)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ccc->cfg_len; i++) {
|
|
|
|
if (!bt_addr_le_cmp(&ccc->cfg[i].peer, addr)) {
|
|
|
|
return &ccc->cfg[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ccc_save {
|
|
|
|
const bt_addr_le_t *addr;
|
|
|
|
struct ccc_store store[CCC_STORE_MAX];
|
|
|
|
size_t count;
|
|
|
|
};
|
|
|
|
|
|
|
|
static u8_t ccc_save(const struct bt_gatt_attr *attr, void *user_data)
|
|
|
|
{
|
|
|
|
struct ccc_save *save = user_data;
|
|
|
|
struct _bt_gatt_ccc *ccc;
|
|
|
|
struct bt_gatt_ccc_cfg *cfg;
|
|
|
|
|
|
|
|
/* Check if attribute is a CCC */
|
|
|
|
if (attr->write != bt_gatt_attr_write_ccc) {
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ccc = attr->user_data;
|
|
|
|
|
|
|
|
/* Check if there is a cfg for the peer */
|
|
|
|
cfg = ccc_find_cfg(ccc, save->addr);
|
|
|
|
if (!cfg) {
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("Storing CCCs handle 0x%04x value 0x%04x", attr->handle,
|
|
|
|
cfg->value);
|
|
|
|
|
|
|
|
save->store[save->count].handle = attr->handle;
|
|
|
|
save->store[save->count].value = cfg->value;
|
|
|
|
save->count++;
|
|
|
|
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2018-07-04 12:58:10 +03:00
|
|
|
int bt_gatt_store_ccc(u8_t id, const bt_addr_le_t *addr)
|
2018-05-08 14:58:00 +03:00
|
|
|
{
|
|
|
|
struct ccc_save save;
|
|
|
|
char key[BT_SETTINGS_KEY_MAX];
|
2018-11-24 16:32:41 +01:00
|
|
|
size_t len;
|
2018-05-08 14:58:00 +03:00
|
|
|
char *str;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
save.addr = addr;
|
|
|
|
save.count = 0;
|
|
|
|
|
|
|
|
bt_gatt_foreach_attr(0x0001, 0xffff, ccc_save, &save);
|
|
|
|
|
2018-07-04 12:58:10 +03:00
|
|
|
if (id) {
|
|
|
|
char id_str[4];
|
|
|
|
|
|
|
|
snprintk(id_str, sizeof(id_str), "%u", id);
|
|
|
|
bt_settings_encode_key(key, sizeof(key), "ccc",
|
|
|
|
(bt_addr_le_t *)addr, id_str);
|
|
|
|
} else {
|
|
|
|
bt_settings_encode_key(key, sizeof(key), "ccc",
|
|
|
|
(bt_addr_le_t *)addr, NULL);
|
|
|
|
}
|
2018-05-08 14:58:00 +03:00
|
|
|
|
2019-02-28 10:33:39 +01:00
|
|
|
if (save.count) {
|
|
|
|
str = (char *)save.store;
|
|
|
|
len = save.count * sizeof(*save.store);
|
|
|
|
} else {
|
|
|
|
/* No entries to encode, just clear */
|
2018-11-21 14:57:29 +02:00
|
|
|
str = NULL;
|
2018-11-24 16:32:41 +01:00
|
|
|
len = 0;
|
2018-11-21 14:57:29 +02:00
|
|
|
}
|
|
|
|
|
2018-11-24 16:32:41 +01:00
|
|
|
err = settings_save_one(key, str, len);
|
2018-05-08 14:58:00 +03:00
|
|
|
if (err) {
|
|
|
|
BT_ERR("Failed to store CCCs (err %d)", err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2018-11-24 16:32:41 +01:00
|
|
|
BT_DBG("Stored CCCs for %s (%s)", bt_addr_le_str(addr), key);
|
|
|
|
if (len) {
|
|
|
|
for (int i = 0; i < save.count; i++) {
|
|
|
|
BT_DBG(" CCC: handle 0x%04x value 0x%04x",
|
|
|
|
save.store[i].handle, save.store[i].value);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
BT_DBG(" CCC: NULL");
|
|
|
|
}
|
2018-05-08 14:58:00 +03:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-11-26 17:07:21 +01:00
|
|
|
static u8_t remove_peer_from_attr(const struct bt_gatt_attr *attr,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
const bt_addr_le_t *peer = user_data;
|
|
|
|
struct _bt_gatt_ccc *ccc;
|
|
|
|
struct bt_gatt_ccc_cfg *cfg;
|
|
|
|
|
|
|
|
/* Check if attribute is a CCC */
|
|
|
|
if (attr->write != bt_gatt_attr_write_ccc) {
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ccc = attr->user_data;
|
|
|
|
|
|
|
|
/* Check if there is a cfg for the peer */
|
|
|
|
cfg = ccc_find_cfg(ccc, peer);
|
|
|
|
if (cfg) {
|
|
|
|
memset(cfg, 0, sizeof(*cfg));
|
|
|
|
}
|
|
|
|
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2019-04-10 13:50:36 +03:00
|
|
|
static int bt_gatt_clear_ccc(u8_t id, const bt_addr_le_t *addr)
|
2018-05-08 14:58:00 +03:00
|
|
|
{
|
|
|
|
char key[BT_SETTINGS_KEY_MAX];
|
|
|
|
|
2018-07-04 12:58:10 +03:00
|
|
|
if (id) {
|
|
|
|
char id_str[4];
|
|
|
|
|
|
|
|
snprintk(id_str, sizeof(id_str), "%u", id);
|
|
|
|
bt_settings_encode_key(key, sizeof(key), "ccc",
|
|
|
|
(bt_addr_le_t *)addr, id_str);
|
|
|
|
} else {
|
|
|
|
bt_settings_encode_key(key, sizeof(key), "ccc",
|
|
|
|
(bt_addr_le_t *)addr, NULL);
|
|
|
|
}
|
2018-05-08 14:58:00 +03:00
|
|
|
|
2018-11-26 17:07:21 +01:00
|
|
|
bt_gatt_foreach_attr(0x0001, 0xffff, remove_peer_from_attr,
|
|
|
|
(void *)addr);
|
|
|
|
|
2018-11-24 16:32:41 +01:00
|
|
|
return settings_delete(key);
|
2018-05-08 14:58:00 +03:00
|
|
|
}
|
|
|
|
|
2019-04-10 13:50:36 +03:00
|
|
|
#if defined(CONFIG_BT_GATT_CACHING)
|
|
|
|
static struct gatt_cf_cfg *find_cf_cfg_by_addr(const bt_addr_le_t *addr)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cf_cfg); i++) {
|
|
|
|
if (!bt_addr_le_cmp(addr, &cf_cfg[i].peer)) {
|
|
|
|
return &cf_cfg[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_BT_GATT_CACHING */
|
|
|
|
|
|
|
|
static int bt_gatt_clear_cf(u8_t id, const bt_addr_le_t *addr)
|
|
|
|
{
|
|
|
|
#if defined(CONFIG_BT_GATT_CACHING)
|
|
|
|
char key[BT_SETTINGS_KEY_MAX];
|
|
|
|
struct gatt_cf_cfg *cfg;
|
|
|
|
|
|
|
|
if (id) {
|
|
|
|
char id_str[4];
|
|
|
|
|
|
|
|
snprintk(id_str, sizeof(id_str), "%u", id);
|
|
|
|
bt_settings_encode_key(key, sizeof(key), "cf",
|
|
|
|
(bt_addr_le_t *)addr, id_str);
|
|
|
|
} else {
|
|
|
|
bt_settings_encode_key(key, sizeof(key), "cf",
|
|
|
|
(bt_addr_le_t *)addr, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg = find_cf_cfg_by_addr(addr);
|
|
|
|
if (cfg) {
|
|
|
|
clear_cf_cfg(cfg);
|
|
|
|
}
|
|
|
|
|
|
|
|
return settings_delete(key);
|
|
|
|
#endif /* CONFIG_BT_GATT_CACHING */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bt_gatt_clear(u8_t id, const bt_addr_le_t *addr)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = bt_gatt_clear_ccc(id, addr);
|
|
|
|
if (err < 0) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bt_gatt_clear_cf(id, addr);
|
|
|
|
}
|
|
|
|
|
2018-05-08 14:58:00 +03:00
|
|
|
static void ccc_clear(struct _bt_gatt_ccc *ccc, bt_addr_le_t *addr)
|
|
|
|
{
|
|
|
|
struct bt_gatt_ccc_cfg *cfg;
|
|
|
|
|
|
|
|
cfg = ccc_find_cfg(ccc, addr);
|
|
|
|
if (!cfg) {
|
|
|
|
BT_DBG("Unable to clear CCC: cfg not found");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bt_addr_le_copy(&cfg->peer, BT_ADDR_LE_ANY);
|
2019-03-26 19:57:45 -06:00
|
|
|
cfg->value = 0U;
|
2018-05-08 14:58:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
struct ccc_load {
|
2018-07-04 12:58:10 +03:00
|
|
|
u8_t id;
|
2018-05-08 14:58:00 +03:00
|
|
|
bt_addr_le_t addr;
|
|
|
|
struct ccc_store *entry;
|
|
|
|
size_t count;
|
|
|
|
};
|
|
|
|
|
|
|
|
static u8_t ccc_load(const struct bt_gatt_attr *attr, void *user_data)
|
|
|
|
{
|
|
|
|
struct ccc_load *load = user_data;
|
|
|
|
struct _bt_gatt_ccc *ccc;
|
|
|
|
struct bt_gatt_ccc_cfg *cfg;
|
|
|
|
|
|
|
|
/* Check if attribute is a CCC */
|
|
|
|
if (attr->write != bt_gatt_attr_write_ccc) {
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
ccc = attr->user_data;
|
|
|
|
|
|
|
|
/* Clear if value was invalidade */
|
|
|
|
if (!load->entry) {
|
|
|
|
ccc_clear(ccc, &load->addr);
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
} else if (!load->count) {
|
|
|
|
return BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip if value is not for the given attribute */
|
|
|
|
if (load->entry->handle != attr->handle) {
|
|
|
|
/* If attribute handle is bigger then it means
|
|
|
|
* the attribute no longer exists and cannot
|
|
|
|
* be restored.
|
|
|
|
*/
|
|
|
|
if (load->entry->handle < attr->handle) {
|
|
|
|
BT_DBG("Unable to restore CCC: handle 0x%04x cannot be"
|
|
|
|
" found", load->entry->handle);
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("Restoring CCC: handle 0x%04x value 0x%04x", load->entry->handle,
|
|
|
|
load->entry->value);
|
|
|
|
|
2018-11-21 15:49:42 +02:00
|
|
|
cfg = ccc_find_cfg(ccc, &load->addr);
|
2018-05-08 14:58:00 +03:00
|
|
|
if (!cfg) {
|
2018-11-21 15:49:42 +02:00
|
|
|
cfg = ccc_find_cfg(ccc, BT_ADDR_LE_ANY);
|
|
|
|
if (!cfg) {
|
|
|
|
BT_DBG("Unable to restore CCC: no cfg left");
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
bt_addr_le_copy(&cfg->peer, &load->addr);
|
2018-05-08 14:58:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
cfg->value = load->entry->value;
|
|
|
|
|
|
|
|
next:
|
|
|
|
load->entry++;
|
|
|
|
load->count--;
|
|
|
|
|
|
|
|
return load->count ? BT_GATT_ITER_CONTINUE : BT_GATT_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
2018-11-24 16:32:41 +01:00
|
|
|
static int ccc_set(int argc, char **argv, void *val_ctx)
|
2018-05-08 14:58:00 +03:00
|
|
|
{
|
|
|
|
struct ccc_store ccc_store[CCC_STORE_MAX];
|
|
|
|
struct ccc_load load;
|
|
|
|
int len, err;
|
|
|
|
|
|
|
|
if (argc < 1) {
|
|
|
|
BT_ERR("Insufficient number of arguments");
|
|
|
|
return -EINVAL;
|
2018-07-04 12:58:10 +03:00
|
|
|
} else if (argc == 1) {
|
|
|
|
load.id = BT_ID_DEFAULT;
|
|
|
|
} else {
|
|
|
|
load.id = strtol(argv[1], NULL, 10);
|
2018-05-08 14:58:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
err = bt_settings_decode_key(argv[0], &load.addr);
|
|
|
|
if (err) {
|
|
|
|
BT_ERR("Unable to decode address %s", argv[0]);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-11-24 16:32:41 +01:00
|
|
|
if (settings_val_get_len_cb(val_ctx)) {
|
|
|
|
len = settings_val_read_cb(val_ctx, ccc_store,
|
|
|
|
sizeof(ccc_store));
|
|
|
|
|
|
|
|
if (len < 0) {
|
|
|
|
BT_ERR("Failed to decode value (err %d)", len);
|
|
|
|
return len;
|
2018-05-08 14:58:00 +03:00
|
|
|
}
|
2018-07-04 12:58:10 +03:00
|
|
|
|
2018-05-08 14:58:00 +03:00
|
|
|
load.entry = ccc_store;
|
|
|
|
load.count = len / sizeof(*ccc_store);
|
2018-11-24 16:32:41 +01:00
|
|
|
|
|
|
|
for (int i = 0; i < load.count; i++) {
|
|
|
|
BT_DBG("Read CCC: handle 0x%04x value 0x%04x",
|
|
|
|
ccc_store[i].handle, ccc_store[i].value);
|
|
|
|
}
|
2018-07-04 12:58:10 +03:00
|
|
|
} else {
|
|
|
|
load.entry = NULL;
|
|
|
|
load.count = 0;
|
2018-05-08 14:58:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bt_gatt_foreach_attr(0x0001, 0xffff, ccc_load, &load);
|
|
|
|
|
|
|
|
BT_DBG("Restored CCC for %s", bt_addr_le_str(&load.addr));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_SETTINGS_DEFINE(ccc, ccc_set, NULL, NULL);
|
2019-02-07 12:38:02 +02:00
|
|
|
|
|
|
|
#if defined(CONFIG_BT_GATT_CACHING)
|
|
|
|
static int cf_set(int argc, char **argv, void *val_ctx)
|
|
|
|
{
|
|
|
|
struct gatt_cf_cfg *cfg;
|
|
|
|
bt_addr_le_t addr;
|
|
|
|
int len, err;
|
|
|
|
|
|
|
|
if (argc < 1) {
|
|
|
|
BT_ERR("Insufficient number of arguments");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = bt_settings_decode_key(argv[0], &addr);
|
|
|
|
if (err) {
|
|
|
|
BT_ERR("Unable to decode address %s", argv[0]);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg = find_cf_cfg_by_addr(&addr);
|
|
|
|
if (!cfg) {
|
|
|
|
cfg = find_cf_cfg(NULL);
|
|
|
|
if (!cfg) {
|
|
|
|
BT_ERR("Unable to restore CF: no cfg left");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (settings_val_get_len_cb(val_ctx)) {
|
|
|
|
len = settings_val_read_cb(val_ctx, cfg->data,
|
|
|
|
sizeof(cfg->data));
|
|
|
|
if (len < 0) {
|
|
|
|
BT_ERR("Failed to decode value (err %d)", len);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("Read CF: len %d", len);
|
|
|
|
} else {
|
|
|
|
bt_addr_le_copy(&cfg->peer, BT_ADDR_LE_ANY);
|
|
|
|
memset(cfg->data, 0, sizeof(cfg->data));
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("Restored CF for %s", bt_addr_le_str(&addr));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_SETTINGS_DEFINE(cf, cf_set, NULL, NULL);
|
|
|
|
#endif /*CONFIG_BT_GATT_CACHING */
|
2018-05-08 14:58:00 +03:00
|
|
|
#endif /* CONFIG_BT_SETTINGS */
|