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
|
|
|
*
|
2015-10-06 11:00:37 -05:00
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
2015-05-12 16:03:09 +03:00
|
|
|
*
|
2015-10-06 11:00:37 -05:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2015-05-12 16:03:09 +03:00
|
|
|
*
|
2015-10-06 11:00:37 -05:00
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
2015-05-12 16:03:09 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <nanokernel.h>
|
|
|
|
#include <toolchain.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
2015-06-17 12:42:54 +03:00
|
|
|
#include <stdbool.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>
|
2016-07-19 11:55:21 +02:00
|
|
|
#include <misc/nano_work.h>
|
2015-05-12 16:03:09 +03:00
|
|
|
|
2015-06-16 17:25:37 +03:00
|
|
|
#include <bluetooth/log.h>
|
2015-05-12 16:03:09 +03:00
|
|
|
#include <bluetooth/hci.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
|
|
|
#include <bluetooth/uuid.h>
|
|
|
|
#include <bluetooth/gatt.h>
|
2015-10-15 11:53:46 +02:00
|
|
|
#include <bluetooth/driver.h>
|
2015-05-12 16:03:09 +03:00
|
|
|
|
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"
|
2015-09-14 17:48:26 +02:00
|
|
|
#include "gatt_internal.h"
|
2015-05-29 13:03:57 +03:00
|
|
|
|
2015-05-25 13:57:29 +03:00
|
|
|
#if !defined(CONFIG_BLUETOOTH_DEBUG_GATT)
|
|
|
|
#undef BT_DBG
|
|
|
|
#define BT_DBG(fmt, ...)
|
|
|
|
#endif
|
|
|
|
|
2015-10-22 20:58:20 +03:00
|
|
|
static struct bt_gatt_attr *db;
|
2015-05-12 16:03:09 +03:00
|
|
|
|
2015-09-08 12:47:33 +02:00
|
|
|
#if defined(CONFIG_BLUETOOTH_GATT_CLIENT)
|
2015-07-22 10:21:47 +03:00
|
|
|
static struct bt_gatt_subscribe_params *subscriptions;
|
2015-09-08 12:47:33 +02:00
|
|
|
#endif /* CONFIG_BLUETOOTH_GATT_CLIENT */
|
2015-07-22 10:21:47 +03:00
|
|
|
|
2016-01-15 08:33:13 -03:00
|
|
|
#if !defined(CONFIG_BLUETOOTH_GATT_DYNAMIC_DB)
|
2016-01-14 11:09:04 -03:00
|
|
|
static size_t attr_count;
|
|
|
|
#endif /* CONFIG_BLUETOOTH_GATT_DYNAMIC_DB */
|
|
|
|
|
2015-10-22 20:58:20 +03:00
|
|
|
int bt_gatt_register(struct bt_gatt_attr *attrs, size_t count)
|
2015-05-12 16:03:09 +03:00
|
|
|
{
|
2016-01-14 11:09:04 -03:00
|
|
|
#if defined(CONFIG_BLUETOOTH_GATT_DYNAMIC_DB)
|
2015-10-22 20:58:20 +03:00
|
|
|
struct bt_gatt_attr *last;
|
2016-01-14 11:09:04 -03:00
|
|
|
#endif /* CONFIG_BLUETOOTH_GATT_DYNAMIC_DB */
|
2015-10-22 20:58:20 +03:00
|
|
|
uint16_t handle;
|
|
|
|
|
|
|
|
if (!attrs || !count) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2016-01-14 11:09:04 -03:00
|
|
|
#if !defined(CONFIG_BLUETOOTH_GATT_DYNAMIC_DB)
|
|
|
|
handle = 0;
|
|
|
|
db = attrs;
|
|
|
|
attr_count = count;
|
|
|
|
#else
|
2015-10-22 20:58:20 +03:00
|
|
|
if (!db) {
|
|
|
|
db = attrs;
|
|
|
|
last = NULL;
|
|
|
|
handle = 0;
|
|
|
|
goto populate;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fast forward to last attribute in the list */
|
|
|
|
for (last = db; last->_next;) {
|
|
|
|
last = last->_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
handle = last->handle;
|
|
|
|
last->_next = attrs;
|
|
|
|
|
|
|
|
populate:
|
2016-01-14 11:09:04 -03:00
|
|
|
#endif /* CONFIG_BLUETOOTH_GATT_DYNAMIC_DB */
|
2015-10-22 20:58:20 +03:00
|
|
|
/* Populate the handles and _next pointers */
|
|
|
|
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 */
|
2016-01-14 11:09:04 -03:00
|
|
|
#if defined(CONFIG_BLUETOOTH_GATT_DYNAMIC_DB)
|
2015-10-22 20:58:20 +03:00
|
|
|
last->_next = NULL;
|
2016-01-14 11:09:04 -03:00
|
|
|
#endif
|
2015-10-22 20:58:20 +03:00
|
|
|
BT_ERR("Unable to register handle 0x%04x",
|
|
|
|
attrs->handle);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2016-01-14 11:09:04 -03:00
|
|
|
#if defined(CONFIG_BLUETOOTH_GATT_DYNAMIC_DB)
|
2015-10-22 20:58:20 +03:00
|
|
|
if (count > 1) {
|
|
|
|
attrs->_next = &attrs[1];
|
|
|
|
}
|
2016-01-14 11:09:04 -03:00
|
|
|
#endif
|
2015-10-22 20:58:20 +03:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("attr %p next %p handle 0x%04x uuid %s perm 0x%02x",
|
2016-01-15 08:33:13 -03:00
|
|
|
attrs, bt_gatt_attr_next(attrs), attrs->handle,
|
2015-10-22 20:58:20 +03:00
|
|
|
bt_uuid_str(attrs->uuid), attrs->perm);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2015-05-12 16:03:09 +03:00
|
|
|
}
|
|
|
|
|
2016-02-19 11:00:48 +02:00
|
|
|
ssize_t bt_gatt_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
|
|
|
void *buf, uint16_t buf_len, uint16_t offset,
|
|
|
|
const void *value, uint16_t value_len)
|
2015-05-12 16:03:09 +03:00
|
|
|
{
|
2015-07-28 15:50:08 +03:00
|
|
|
uint16_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
|
|
|
}
|
|
|
|
|
|
|
|
len = min(buf_len, value_len - offset);
|
|
|
|
|
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
|
|
|
|
|
|
|
memcpy(buf, value + offset, len);
|
|
|
|
|
|
|
|
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,
|
|
|
|
void *buf, uint16_t len, uint16_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) {
|
|
|
|
uint16_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 {
|
|
|
|
uint16_t start_handle;
|
|
|
|
uint16_t end_handle;
|
2015-12-22 11:21:27 +01:00
|
|
|
uint16_t uuid16;
|
2015-06-04 11:20:48 +07:00
|
|
|
} __packed;
|
2015-05-12 16:03:09 +03:00
|
|
|
|
2016-04-12 21:07:53 +02:00
|
|
|
static uint8_t get_service_handles(const struct bt_gatt_attr *attr,
|
|
|
|
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,
|
|
|
|
void *buf, uint16_t len, uint16_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;
|
|
|
|
uint8_t value_len;
|
|
|
|
|
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 {
|
|
|
|
uint8_t properties;
|
|
|
|
uint16_t value_handle;
|
|
|
|
union {
|
|
|
|
uint16_t uuid16;
|
|
|
|
uint8_t uuid[16];
|
|
|
|
};
|
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,
|
|
|
|
uint16_t len, uint16_t offset)
|
2015-05-12 16:03:09 +03:00
|
|
|
{
|
|
|
|
struct bt_gatt_chrc *chrc = attr->user_data;
|
|
|
|
struct gatt_chrc pdu;
|
2016-01-14 11:09:04 -03:00
|
|
|
const struct bt_gatt_attr *next;
|
2015-05-12 16:03:09 +03:00
|
|
|
uint8_t value_len;
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
2016-01-15 08:33:13 -03:00
|
|
|
next = bt_gatt_attr_next(attr);
|
2016-01-14 11:09:04 -03:00
|
|
|
if (!next) {
|
2015-12-22 11:50:01 +01:00
|
|
|
BT_WARN("No value for characteristic at 0x%04x", attr->handle);
|
2015-10-23 13:30:31 +03:00
|
|
|
pdu.value_handle = 0x0000;
|
|
|
|
} else {
|
2016-01-14 11:09:04 -03:00
|
|
|
pdu.value_handle = sys_cpu_to_le16(next->handle);
|
2015-10-23 13:30:31 +03:00
|
|
|
}
|
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);
|
|
|
|
value_len += 2;
|
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);
|
|
|
|
value_len += 16;
|
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
|
|
|
|
|
|
|
void bt_gatt_foreach_attr(uint16_t start_handle, uint16_t end_handle,
|
|
|
|
bt_gatt_attr_func_t func, void *user_data)
|
|
|
|
{
|
2015-10-22 20:58:20 +03:00
|
|
|
const struct bt_gatt_attr *attr;
|
2015-05-19 16:52:27 +03:00
|
|
|
|
2016-01-15 08:33:13 -03:00
|
|
|
for (attr = db; attr; attr = bt_gatt_attr_next(attr)) {
|
2015-05-19 16:52:27 +03:00
|
|
|
/* Check if attribute handle is within range */
|
2015-09-04 14:37:44 +02:00
|
|
|
if (attr->handle < start_handle || attr->handle > end_handle) {
|
2015-05-19 16:52:27 +03:00
|
|
|
continue;
|
2015-09-04 14:37:44 +02:00
|
|
|
}
|
2015-05-19 16:52:27 +03:00
|
|
|
|
2015-09-04 14:37:44 +02:00
|
|
|
if (func(attr, user_data) == BT_GATT_ITER_STOP) {
|
2015-05-19 16:52:27 +03:00
|
|
|
break;
|
2015-09-04 14:37:44 +02:00
|
|
|
}
|
2015-05-19 16:52:27 +03:00
|
|
|
}
|
|
|
|
}
|
2015-05-29 13:03:57 +03:00
|
|
|
|
2016-01-15 08:33:13 -03:00
|
|
|
struct bt_gatt_attr *bt_gatt_attr_next(const struct bt_gatt_attr *attr)
|
|
|
|
{
|
|
|
|
#if defined(CONFIG_BLUETOOTH_GATT_DYNAMIC_DB)
|
|
|
|
return attr->_next;
|
|
|
|
#else
|
|
|
|
return ((attr < db || attr > &db[attr_count - 2]) ? NULL :
|
2016-03-21 14:48:40 +01:00
|
|
|
(struct bt_gatt_attr *)&attr[1]);
|
2016-01-15 08:33:13 -03:00
|
|
|
#endif /* CONFIG_BLUETOOTH_GATT_DYNAMIC_DB */
|
|
|
|
}
|
|
|
|
|
2016-02-19 11:00:48 +02:00
|
|
|
ssize_t bt_gatt_attr_read_ccc(struct bt_conn *conn,
|
|
|
|
const struct bt_gatt_attr *attr, void *buf,
|
|
|
|
uint16_t len, uint16_t offset)
|
2015-05-29 13:03:57 +03:00
|
|
|
{
|
|
|
|
struct _bt_gatt_ccc *ccc = attr->user_data;
|
|
|
|
uint16_t value;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < ccc->cfg_len; i++) {
|
2015-11-03 12:19:01 +01:00
|
|
|
if (bt_addr_le_cmp(&ccc->cfg[i].peer, &conn->le.dst)) {
|
2015-05-29 13:03:57 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
value = sys_cpu_to_le16(ccc->cfg[i].value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Default to disable if there is no cfg for the peer */
|
|
|
|
if (i == ccc->cfg_len) {
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gatt_ccc_changed(struct _bt_gatt_ccc *ccc)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
uint16_t value = 0x0000;
|
|
|
|
|
|
|
|
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;
|
|
|
|
ccc->cfg_changed(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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,
|
2016-06-22 13:24:23 +03:00
|
|
|
uint16_t len, uint16_t offset, uint8_t flags)
|
2015-05-29 13:03:57 +03:00
|
|
|
{
|
|
|
|
struct _bt_gatt_ccc *ccc = attr->user_data;
|
|
|
|
const uint16_t *data = buf;
|
|
|
|
size_t i;
|
|
|
|
|
2015-09-23 15:54:45 +02:00
|
|
|
if (offset > sizeof(*data)) {
|
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
|
|
|
}
|
|
|
|
|
2015-09-23 15:54:45 +02:00
|
|
|
if (offset + len > sizeof(*data)) {
|
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
|
|
|
}
|
|
|
|
|
2015-05-29 13:03:57 +03:00
|
|
|
for (i = 0; i < ccc->cfg_len; i++) {
|
|
|
|
/* Check for existing configuration */
|
2015-11-03 12:19:01 +01:00
|
|
|
if (!bt_addr_le_cmp(&ccc->cfg[i].peer, &conn->le.dst)) {
|
2015-05-29 13:03:57 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == ccc->cfg_len) {
|
|
|
|
for (i = 0; i < ccc->cfg_len; i++) {
|
|
|
|
/* Check for unused configuration */
|
2016-04-04 13:38:16 +03:00
|
|
|
if (ccc->cfg[i].valid) {
|
|
|
|
continue;
|
2015-05-29 13:03:57 +03:00
|
|
|
}
|
2016-04-04 13:38:16 +03:00
|
|
|
|
|
|
|
bt_addr_le_copy(&ccc->cfg[i].peer, &conn->le.dst);
|
2016-08-27 13:46:24 +02:00
|
|
|
ccc->cfg[i].valid = true;
|
2016-04-04 13:38:16 +03:00
|
|
|
break;
|
2015-05-29 13:03:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (i == ccc->cfg_len) {
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ccc->cfg[i].value = sys_le16_to_cpu(*data);
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("handle 0x%04x value %u", attr->handle, ccc->cfg[i].value);
|
2015-05-29 13:03:57 +03:00
|
|
|
|
|
|
|
/* Update cfg if don't match */
|
|
|
|
if (ccc->cfg[i].value != ccc->value) {
|
|
|
|
gatt_ccc_changed(ccc);
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
uint16_t len, uint16_t offset)
|
2015-06-11 15:33:21 +03:00
|
|
|
{
|
|
|
|
struct bt_gatt_cep *value = attr->user_data;
|
|
|
|
uint16_t props = sys_cpu_to_le16(value->properties);
|
|
|
|
|
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,
|
|
|
|
uint16_t len, uint16_t offset)
|
2015-12-11 09:48:57 +01:00
|
|
|
{
|
|
|
|
char *value = attr->user_data;
|
|
|
|
|
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,
|
|
|
|
uint16_t len, uint16_t offset)
|
2015-12-10 14:31:25 +01:00
|
|
|
{
|
|
|
|
struct bt_gatt_cpf *value = attr->user_data;
|
|
|
|
|
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
|
|
|
|
sizeof(*value));
|
|
|
|
}
|
|
|
|
|
2015-05-26 14:29:30 +03:00
|
|
|
struct notify_data {
|
2016-03-03 12:15:05 +02:00
|
|
|
uint16_t type;
|
|
|
|
const struct bt_gatt_attr *attr;
|
2015-05-26 14:29:30 +03:00
|
|
|
const void *data;
|
2016-03-03 12:15:05 +02:00
|
|
|
uint16_t len;
|
|
|
|
struct bt_gatt_indicate_params *params;
|
2015-05-26 14:29:30 +03:00
|
|
|
};
|
|
|
|
|
2015-10-20 13:33:26 +03:00
|
|
|
static int att_notify(struct bt_conn *conn, uint16_t handle, const void *data,
|
|
|
|
size_t len)
|
|
|
|
{
|
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;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
bt_l2cap_send(conn, BT_L2CAP_CID_ATT, buf);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-03 12:15:05 +02:00
|
|
|
static void gatt_indicate_rsp(struct bt_conn *conn, uint8_t err,
|
|
|
|
const void *pdu, uint16_t length, void *user_data)
|
|
|
|
{
|
|
|
|
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 {
|
|
|
|
err = bt_att_send(conn, buf);
|
|
|
|
}
|
|
|
|
|
2016-03-03 12:15:05 +02:00
|
|
|
if (err) {
|
|
|
|
BT_ERR("Error sending ATT PDU: %d", err);
|
|
|
|
net_buf_unref(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int att_indicate(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_indicate_params *params)
|
|
|
|
{
|
|
|
|
struct net_buf *buf;
|
|
|
|
struct bt_att_indicate *ind;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
BT_DBG("conn %p handle 0x%04x", conn, params->attr->handle);
|
|
|
|
|
|
|
|
ind = net_buf_add(buf, sizeof(*ind));
|
|
|
|
ind->handle = sys_cpu_to_le16(params->attr->handle);
|
|
|
|
|
|
|
|
net_buf_add(buf, params->len);
|
|
|
|
memcpy(ind->value, params->data, params->len);
|
|
|
|
|
|
|
|
return gatt_send(conn, buf, gatt_indicate_rsp, params, NULL);
|
|
|
|
}
|
|
|
|
|
2015-05-26 14:29:30 +03:00
|
|
|
static uint8_t notify_cb(const struct bt_gatt_attr *attr, void *user_data)
|
|
|
|
{
|
|
|
|
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++) {
|
|
|
|
struct bt_conn *conn;
|
2016-03-03 12:15:05 +02:00
|
|
|
int err;
|
2015-05-26 14:29:30 +03:00
|
|
|
|
2016-03-03 12:15:05 +02:00
|
|
|
if (ccc->value != data->type) {
|
2015-06-01 12:58:49 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-05-26 14:29:30 +03:00
|
|
|
conn = bt_conn_lookup_addr_le(&ccc->cfg[i].peer);
|
2015-06-24 14:39:14 +02:00
|
|
|
if (!conn || conn->state != BT_CONN_CONNECTED) {
|
2015-05-26 14:29:30 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-03-03 12:15:05 +02:00
|
|
|
if (data->type == BT_GATT_CCC_INDICATE) {
|
|
|
|
err = att_indicate(conn, data->params);
|
|
|
|
} else {
|
|
|
|
err = att_notify(conn, data->attr->handle, data->data,
|
|
|
|
data->len);
|
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;
|
|
|
|
}
|
2015-05-26 14:29:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2016-01-19 13:18:21 +02:00
|
|
|
int bt_gatt_notify(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
|
|
|
const void *data, uint16_t len)
|
2015-05-26 14:29:30 +03:00
|
|
|
{
|
|
|
|
struct notify_data nfy;
|
|
|
|
|
2016-01-19 13:18:21 +02:00
|
|
|
if (!attr || !attr->handle) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2015-10-20 13:33:26 +03:00
|
|
|
if (conn) {
|
2016-01-19 13:18:21 +02:00
|
|
|
return att_notify(conn, attr->handle, data, len);
|
2015-10-20 13:33:26 +03:00
|
|
|
}
|
|
|
|
|
2016-03-03 12:15:05 +02:00
|
|
|
nfy.attr = attr;
|
|
|
|
nfy.type = BT_GATT_CCC_NOTIFY;
|
2015-05-26 14:29:30 +03:00
|
|
|
nfy.data = data;
|
|
|
|
nfy.len = len;
|
|
|
|
|
2016-01-19 13:18:21 +02:00
|
|
|
bt_gatt_foreach_attr(attr->handle, 0xffff, notify_cb, &nfy);
|
2015-10-20 13:33:26 +03:00
|
|
|
|
|
|
|
return 0;
|
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;
|
|
|
|
|
|
|
|
if (!params || !params->attr || !params->attr->handle) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conn) {
|
|
|
|
return att_indicate(conn, params);
|
|
|
|
}
|
|
|
|
|
|
|
|
nfy.type = BT_GATT_CCC_INDICATE;
|
|
|
|
nfy.params = params;
|
|
|
|
|
|
|
|
bt_gatt_foreach_attr(params->attr->handle, 0xffff, notify_cb, &nfy);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-17 13:31:00 +03:00
|
|
|
static uint8_t connected_cb(const struct bt_gatt_attr *attr, void *user_data)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* If already enabled skip */
|
|
|
|
if (ccc->value) {
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ccc->cfg_len; i++) {
|
|
|
|
/* Ignore configuration for different peer */
|
2015-11-03 12:19:01 +01:00
|
|
|
if (bt_addr_le_cmp(&conn->le.dst, &ccc->cfg[i].peer)) {
|
2015-06-17 13:31:00 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ccc->cfg[i].value) {
|
|
|
|
gatt_ccc_changed(ccc);
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t disconnected_cb(const struct bt_gatt_attr *attr, void *user_data)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* If already disabled skip */
|
|
|
|
if (!ccc->value) {
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ccc->cfg_len; i++) {
|
|
|
|
/* Ignore configurations with disabled value */
|
|
|
|
if (!ccc->cfg[i].value) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-06-08 15:19:37 +02:00
|
|
|
if (bt_addr_le_cmp(&conn->le.dst, &ccc->cfg[i].peer)) {
|
2015-06-17 13:31:00 +03:00
|
|
|
struct bt_conn *tmp;
|
|
|
|
|
|
|
|
/* Skip if there is another peer connected */
|
|
|
|
tmp = bt_conn_lookup_addr_le(&ccc->cfg[i].peer);
|
2016-02-16 14:31:15 +01:00
|
|
|
if (tmp) {
|
|
|
|
if (tmp->state == BT_CONN_CONNECTED) {
|
|
|
|
bt_conn_unref(tmp);
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
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 */
|
2016-08-27 13:46:24 +02:00
|
|
|
if (!bt_addr_le_is_bonded(&conn->le.dst)) {
|
|
|
|
ccc->cfg[i].valid = false;
|
2016-06-08 15:19:37 +02:00
|
|
|
memset(&ccc->cfg[i].value, 0,
|
|
|
|
sizeof(ccc->cfg[i].value));
|
2016-08-27 13:46:24 +02:00
|
|
|
}
|
2015-06-17 13:31:00 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset value while disconnected */
|
|
|
|
memset(&ccc->value, 0, sizeof(ccc->value));
|
2016-06-07 20:23:22 +02:00
|
|
|
if (ccc->cfg_changed)
|
|
|
|
ccc->cfg_changed(ccc->value);
|
2015-06-17 13:31:00 +03:00
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("ccc %p reseted", ccc);
|
2015-06-17 13:31:00 +03:00
|
|
|
|
|
|
|
return BT_GATT_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2015-09-08 12:47:33 +02:00
|
|
|
#if defined(CONFIG_BLUETOOTH_GATT_CLIENT)
|
2015-10-02 12:44:26 +02:00
|
|
|
void bt_gatt_notification(struct bt_conn *conn, uint16_t handle,
|
|
|
|
const void *data, uint16_t length)
|
|
|
|
{
|
|
|
|
struct bt_gatt_subscribe_params *params;
|
|
|
|
|
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
|
|
|
|
|
|
|
for (params = subscriptions; params; params = params->_next) {
|
2015-11-10 15:58:43 +01:00
|
|
|
if (handle != params->value_handle) {
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-21 10:47:09 +01:00
|
|
|
static void gatt_subscription_remove(struct bt_conn *conn,
|
|
|
|
struct bt_gatt_subscribe_params *prev,
|
2015-07-29 15:03:55 +03:00
|
|
|
struct bt_gatt_subscribe_params *params)
|
|
|
|
{
|
|
|
|
/* Remove subscription from the list*/
|
|
|
|
if (!prev) {
|
|
|
|
subscriptions = params->_next;
|
|
|
|
} else {
|
|
|
|
prev->_next = params->_next;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
2015-07-29 15:03:55 +03:00
|
|
|
struct bt_gatt_subscribe_params *params, *prev;
|
|
|
|
|
|
|
|
/* Lookup existing subscriptions */
|
|
|
|
for (params = subscriptions, prev = NULL; params;
|
|
|
|
prev = params, params = params->_next) {
|
2015-11-03 12:19:01 +01:00
|
|
|
if (bt_addr_le_cmp(¶ms->_peer, &conn->le.dst)) {
|
2015-07-29 15:03:55 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove subscription */
|
2016-01-21 10:47:09 +01:00
|
|
|
gatt_subscription_remove(conn, prev, params);
|
2015-07-29 15:03:55 +03:00
|
|
|
}
|
2015-06-17 13:31:00 +03:00
|
|
|
}
|
2015-07-01 11:03:54 +03:00
|
|
|
|
|
|
|
static void gatt_mtu_rsp(struct bt_conn *conn, uint8_t err, const void *pdu,
|
|
|
|
uint16_t length, void *user_data)
|
|
|
|
{
|
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;
|
2015-07-01 11:03:54 +03:00
|
|
|
uint16_t mtu;
|
|
|
|
|
2016-08-02 16:23:26 +03:00
|
|
|
if (!conn || !params || !params->func) {
|
2015-07-01 11:03:54 +03:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_MTU_REQ, sizeof(*req));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2015-11-06 12:19:26 +02:00
|
|
|
mtu = CONFIG_BLUETOOTH_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
|
|
|
|
|
|
|
static void att_find_type_rsp(struct bt_conn *conn, uint8_t err,
|
|
|
|
const void *pdu, uint16_t length,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
const struct bt_att_find_type_rsp *rsp = pdu;
|
|
|
|
struct bt_gatt_discover_params *params = user_data;
|
2015-09-07 15:40:19 +02:00
|
|
|
struct bt_gatt_service value;
|
2015-07-03 14:52:57 +03:00
|
|
|
uint8_t i;
|
|
|
|
uint16_t end_handle = 0, start_handle;
|
|
|
|
|
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 */
|
|
|
|
for (i = 0; length >= sizeof(rsp->list[i]);
|
|
|
|
i++, length -= sizeof(rsp->list[i])) {
|
2015-10-23 10:52:28 +03:00
|
|
|
struct bt_gatt_attr *attr;
|
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-07 15:40:19 +02:00
|
|
|
value.end_handle = end_handle;
|
|
|
|
value.uuid = params->uuid;
|
|
|
|
|
2015-09-21 14:01:13 +03:00
|
|
|
if (params->type == BT_GATT_DISCOVER_PRIMARY) {
|
|
|
|
attr = (&(struct bt_gatt_attr)
|
2015-10-23 10:52:28 +03:00
|
|
|
BT_GATT_PRIMARY_SERVICE(&value));
|
2015-09-21 14:01:13 +03:00
|
|
|
} else {
|
|
|
|
attr = (&(struct bt_gatt_attr)
|
2015-10-23 10:52:28 +03:00
|
|
|
BT_GATT_SECONDARY_SERVICE(&value));
|
2015-09-21 14:01:13 +03:00
|
|
|
}
|
2015-07-03 14:52:57 +03:00
|
|
|
|
2015-10-23 10:52:28 +03:00
|
|
|
attr->handle = start_handle;
|
|
|
|
|
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-03 14:52:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stop if could not parse the whole PDU */
|
|
|
|
if (length > 0) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stop if over the range or the requests */
|
|
|
|
if (end_handle >= params->end_handle) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2016-02-19 11:38:06 +02:00
|
|
|
/* Continue from the last found handle */
|
2015-07-03 14:52:57 +03:00
|
|
|
params->start_handle = end_handle;
|
2016-02-19 11:38:06 +02:00
|
|
|
if (params->start_handle < UINT16_MAX) {
|
|
|
|
params->start_handle++;
|
|
|
|
}
|
|
|
|
|
2015-07-03 14:52:57 +03:00
|
|
|
if (!bt_gatt_discover(conn, params)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2016-01-21 10:47:05 +01:00
|
|
|
params->func(conn, NULL, params);
|
2015-07-03 14:52:57 +03:00
|
|
|
}
|
|
|
|
|
2015-09-21 11:12:15 +03:00
|
|
|
static int att_find_type(struct bt_conn *conn,
|
|
|
|
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;
|
|
|
|
|
|
|
|
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) {
|
2016-01-27 21:52:58 -05:00
|
|
|
req->type = sys_cpu_to_le16(BT_UUID_GATT_PRIMARY_VAL);
|
2015-09-21 14:01:13 +03:00
|
|
|
} else {
|
2016-01-27 21:52:58 -05:00
|
|
|
req->type = sys_cpu_to_le16(BT_UUID_GATT_SECONDARY_VAL);
|
2015-09-21 14:01:13 +03:00
|
|
|
}
|
2015-07-03 14:52:57 +03:00
|
|
|
|
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:
|
|
|
|
memcpy(net_buf_add(buf, 16),
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-07-15 11:09:57 +03:00
|
|
|
return gatt_send(conn, buf, att_find_type_rsp, params, NULL);
|
2015-07-03 14:52:57 +03:00
|
|
|
}
|
|
|
|
|
2016-07-08 15:05:49 +02:00
|
|
|
static void read_included_uuid_cb(struct bt_conn *conn, uint8_t err,
|
|
|
|
const void *pdu, uint16_t length,
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (length != 16) {
|
|
|
|
BT_ERR("Invalid data len %u", length);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
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:
|
|
|
|
/* Continue from the last handle */
|
|
|
|
if (params->start_handle < UINT16_MAX) {
|
|
|
|
params->start_handle++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stop if over the requested range */
|
|
|
|
if (params->start_handle >= params->end_handle) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Continue to the next range */
|
|
|
|
if (!bt_gatt_discover(conn, params)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
params->func(conn, NULL, params);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-12-11 12:34:35 +01:00
|
|
|
static uint16_t parse_include(struct bt_conn *conn, const void *pdu,
|
2015-09-21 13:37:43 +03:00
|
|
|
struct bt_gatt_discover_params *params,
|
|
|
|
uint16_t length)
|
2015-07-06 13:36:16 +03:00
|
|
|
{
|
|
|
|
const struct bt_att_read_type_rsp *rsp = pdu;
|
|
|
|
uint16_t handle = 0;
|
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;
|
|
|
|
length -= rsp->len, 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 */
|
|
|
|
if (length == 0 && handle) {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2015-12-11 12:34:35 +01:00
|
|
|
static uint16_t parse_characteristic(struct bt_conn *conn, const void *pdu,
|
2015-09-21 13:37:43 +03:00
|
|
|
struct bt_gatt_discover_params *params,
|
|
|
|
uint16_t length)
|
|
|
|
{
|
|
|
|
const struct bt_att_read_type_rsp *rsp = pdu;
|
|
|
|
uint16_t handle = 0;
|
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;
|
|
|
|
length -= rsp->len, 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;
|
|
|
|
}
|
|
|
|
|
2016-01-27 22:43:34 -05:00
|
|
|
attr = (&(struct bt_gatt_attr)BT_GATT_CHARACTERISTIC(&u.uuid,
|
2015-10-23 15:00:52 +03:00
|
|
|
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 */
|
|
|
|
if (length == 0 && handle) {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
static void att_read_type_rsp(struct bt_conn *conn, uint8_t err,
|
|
|
|
const void *pdu, uint16_t length,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct bt_gatt_discover_params *params = user_data;
|
|
|
|
uint16_t handle;
|
|
|
|
|
2015-11-25 14:26:48 +01:00
|
|
|
BT_DBG("err 0x%02x", err);
|
2015-09-21 13:37:43 +03:00
|
|
|
|
|
|
|
if (err) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2015-07-22 14:55:06 +03:00
|
|
|
/* Continue from the last handle */
|
|
|
|
params->start_handle = handle;
|
|
|
|
if (params->start_handle < UINT16_MAX) {
|
|
|
|
params->start_handle++;
|
|
|
|
}
|
|
|
|
|
2015-07-06 13:36:16 +03:00
|
|
|
/* Stop if over the requested range */
|
|
|
|
if (params->start_handle >= params->end_handle) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Continue to the next range */
|
2015-09-21 11:12:15 +03:00
|
|
|
if (!bt_gatt_discover(conn, params)) {
|
2015-07-06 13:36:16 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2016-01-21 10:47:05 +01:00
|
|
|
params->func(conn, NULL, params);
|
2015-07-06 13:36:16 +03:00
|
|
|
}
|
|
|
|
|
2015-09-21 11:12:15 +03:00
|
|
|
static int att_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;
|
|
|
|
uint16_t *value;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2015-10-28 10:51:40 +02:00
|
|
|
value = net_buf_add(buf, sizeof(*value));
|
2015-09-21 13:37:43 +03:00
|
|
|
if (params->type == BT_GATT_DISCOVER_INCLUDE)
|
2016-01-27 21:52:58 -05:00
|
|
|
*value = sys_cpu_to_le16(BT_UUID_GATT_INCLUDE_VAL);
|
2015-09-21 13:37:43 +03:00
|
|
|
else
|
2016-01-27 21:52:58 -05:00
|
|
|
*value = sys_cpu_to_le16(BT_UUID_GATT_CHRC_VAL);
|
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);
|
|
|
|
|
2015-07-15 11:09:57 +03:00
|
|
|
return gatt_send(conn, buf, att_read_type_rsp, params, NULL);
|
2015-07-06 13:36:16 +03:00
|
|
|
}
|
|
|
|
|
2015-07-08 10:45:43 +03:00
|
|
|
static void att_find_info_rsp(struct bt_conn *conn, uint8_t err,
|
|
|
|
const void *pdu, uint16_t length,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
const struct bt_att_find_info_rsp *rsp = pdu;
|
|
|
|
struct bt_gatt_discover_params *params = user_data;
|
|
|
|
uint16_t handle = 0;
|
|
|
|
uint8_t len;
|
|
|
|
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;
|
|
|
|
length -= len, 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Next characteristic shall be after current value handle */
|
|
|
|
params->start_handle = handle;
|
|
|
|
if (params->start_handle < UINT16_MAX) {
|
|
|
|
params->start_handle++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stop if over the requested range */
|
|
|
|
if (params->start_handle >= params->end_handle) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Continue to the next range */
|
2015-09-21 11:12:15 +03:00
|
|
|
if (!bt_gatt_discover(conn, params)) {
|
2015-07-08 10:45:43 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2016-01-21 10:47:05 +01:00
|
|
|
params->func(conn, NULL, params);
|
2015-07-08 10:45:43 +03:00
|
|
|
}
|
|
|
|
|
2015-09-21 11:12:15 +03:00
|
|
|
static int att_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);
|
|
|
|
|
2015-07-15 11:09:57 +03:00
|
|
|
return gatt_send(conn, buf, att_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)
|
|
|
|
{
|
2015-11-10 16:11:10 +01:00
|
|
|
if (!conn || !params || !params->func || !params->start_handle ||
|
2015-09-21 11:12:15 +03:00
|
|
|
!params->end_handle || params->start_handle > params->end_handle) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (params->type) {
|
|
|
|
case BT_GATT_DISCOVER_PRIMARY:
|
|
|
|
case BT_GATT_DISCOVER_SECONDARY:
|
2015-09-21 14:01:13 +03:00
|
|
|
return att_find_type(conn, params);
|
2015-09-21 11:12:15 +03:00
|
|
|
case BT_GATT_DISCOVER_INCLUDE:
|
|
|
|
case BT_GATT_DISCOVER_CHARACTERISTIC:
|
|
|
|
return att_read_type(conn, params);
|
|
|
|
case BT_GATT_DISCOVER_DESCRIPTOR:
|
|
|
|
return att_find_info(conn, params);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-07-13 14:07:04 +03:00
|
|
|
static void att_read_rsp(struct bt_conn *conn, uint8_t err, const void *pdu,
|
|
|
|
uint16_t length, void *user_data)
|
|
|
|
{
|
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
|
|
|
|
|
|
|
if (err) {
|
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
|
|
|
|
2015-11-06 22:11:37 +01:00
|
|
|
return gatt_send(conn, buf, att_read_rsp, params, NULL);
|
2015-07-13 14:07:04 +03:00
|
|
|
}
|
|
|
|
|
2016-01-21 10:47:12 +01:00
|
|
|
static void att_read_multiple_rsp(struct bt_conn *conn, uint8_t err,
|
|
|
|
const void *pdu, uint16_t length,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct bt_gatt_read_params *params = user_data;
|
|
|
|
|
|
|
|
BT_DBG("err 0x%02x", err);
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
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;
|
|
|
|
uint8_t i;
|
|
|
|
|
|
|
|
buf = bt_att_create_pdu(conn, BT_ATT_OP_READ_MULT_REQ,
|
|
|
|
params->handle_count * sizeof(uint16_t));
|
|
|
|
if (!buf) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < params->handle_count; i++) {
|
|
|
|
net_buf_add_le16(buf, params->handles[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return gatt_send(conn, buf, att_read_multiple_rsp, params, NULL);
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2016-01-21 10:47:12 +01:00
|
|
|
if (!conn || !params || !params->handle_count || !params->func) {
|
2015-07-13 14:07:04 +03:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2015-11-06 22:11:37 +01:00
|
|
|
return gatt_send(conn, buf, att_read_rsp, params, NULL);
|
2015-07-13 14:07:04 +03:00
|
|
|
}
|
|
|
|
|
2015-07-15 13:28:58 +03:00
|
|
|
static void att_write_rsp(struct bt_conn *conn, uint8_t err, const void *pdu,
|
|
|
|
uint16_t length, void *user_data)
|
|
|
|
{
|
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
|
|
|
}
|
|
|
|
|
2015-09-09 17:27:16 +02:00
|
|
|
static bool write_signed_allowed(struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
#if defined(CONFIG_BLUETOOTH_SMP)
|
|
|
|
return conn->encrypt == 0;
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif /* CONFIG_BLUETOOTH_SMP */
|
|
|
|
}
|
|
|
|
|
2015-08-11 10:28:02 +03:00
|
|
|
int bt_gatt_write_without_response(struct bt_conn *conn, uint16_t handle,
|
2015-07-30 16:06:06 +03:00
|
|
|
const void *data, uint16_t length, bool sign)
|
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;
|
|
|
|
|
2015-08-11 10:28:02 +03:00
|
|
|
if (!conn || !handle) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2015-09-09 17:27:16 +02:00
|
|
|
if (sign && write_signed_allowed(conn)) {
|
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
|
|
|
|
|
|
|
return gatt_send(conn, buf, NULL, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
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-07-26 15:26:15 +03:00
|
|
|
return gatt_send(conn, buf, att_write_rsp, params, NULL);
|
2015-07-31 10:25:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void att_prepare_write_rsp(struct bt_conn *conn, uint8_t err,
|
|
|
|
const void *pdu, uint16_t length,
|
|
|
|
void *user_data)
|
|
|
|
{
|
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;
|
|
|
|
uint16_t len;
|
|
|
|
|
2016-05-10 11:28:24 +03: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;
|
|
|
|
params->data += len;
|
|
|
|
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);
|
|
|
|
|
|
|
|
return gatt_send(conn, buf, att_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;
|
|
|
|
|
2016-05-10 11:28:24 +03:00
|
|
|
if (!conn || !params || !params->handle || !params->func) {
|
2015-07-15 13:28:58 +03:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
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-07-26 15:26:15 +03:00
|
|
|
return gatt_send(conn, buf, att_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 */
|
|
|
|
params->_next = subscriptions;
|
|
|
|
subscriptions = params;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void att_write_ccc_rsp(struct bt_conn *conn, uint8_t err,
|
|
|
|
const void *pdu, uint16_t length, void *user_data)
|
|
|
|
{
|
|
|
|
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) {
|
2016-01-21 10:47:09 +01:00
|
|
|
struct bt_gatt_subscribe_params *cur, *prev;
|
2015-07-22 10:21:47 +03:00
|
|
|
|
2016-01-21 10:47:09 +01:00
|
|
|
for (cur = subscriptions, prev = NULL; cur;
|
|
|
|
prev = cur, cur = cur->_next) {
|
|
|
|
|
|
|
|
if (cur == params) {
|
|
|
|
gatt_subscription_remove(conn, prev, params);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-07-22 10:21:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int gatt_write_ccc(struct bt_conn *conn, uint16_t handle, uint16_t value,
|
|
|
|
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,
|
|
|
|
sizeof(*req) + sizeof(uint16_t));
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
struct bt_gatt_subscribe_params *tmp;
|
|
|
|
bool has_subscription = false;
|
|
|
|
|
2015-08-12 16:16:03 +03:00
|
|
|
if (!conn || conn->state != BT_CONN_CONNECTED) {
|
2015-07-22 10:21:47 +03:00
|
|
|
return -ENOTCONN;
|
|
|
|
}
|
|
|
|
|
2016-01-21 10:47:09 +01:00
|
|
|
if (!params || !params->notify ||
|
|
|
|
!params->value || !params->ccc_handle) {
|
2015-07-22 10:21:47 +03:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Lookup existing subscriptions */
|
|
|
|
for (tmp = subscriptions; tmp; tmp = tmp->_next) {
|
|
|
|
/* Fail if entry already exists */
|
|
|
|
if (tmp == params) {
|
|
|
|
return -EALREADY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if another subscription exists */
|
2015-11-03 12:19:01 +01:00
|
|
|
if (!bt_addr_le_cmp(&tmp->_peer, &conn->le.dst) &&
|
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,
|
|
|
|
att_write_ccc_rsp, NULL);
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
struct bt_gatt_subscribe_params *tmp;
|
|
|
|
bool has_subscription = false, found = false;
|
|
|
|
|
2015-08-12 16:16:03 +03:00
|
|
|
if (!conn || conn->state != BT_CONN_CONNECTED) {
|
2015-07-22 10:41:20 +03:00
|
|
|
return -ENOTCONN;
|
|
|
|
}
|
|
|
|
|
2015-11-10 15:58:43 +01:00
|
|
|
if (!params) {
|
2015-07-22 10:41:20 +03:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check head */
|
|
|
|
if (subscriptions == params) {
|
|
|
|
subscriptions = params->_next;
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Lookup existing subscriptions */
|
|
|
|
for (tmp = subscriptions; tmp; tmp = tmp->_next) {
|
|
|
|
/* Remove subscription */
|
|
|
|
if (tmp->_next == params) {
|
|
|
|
tmp->_next = params->_next;
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if there still remains any other subscription */
|
2015-11-03 12:19:01 +01:00
|
|
|
if (!bt_addr_le_cmp(&tmp->_peer, &conn->le.dst) &&
|
2015-07-22 10:41:20 +03:00
|
|
|
tmp->value_handle == params->value_handle) {
|
|
|
|
has_subscription = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (has_subscription) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-11-10 15:58:43 +01:00
|
|
|
return gatt_write_ccc(conn, params->ccc_handle, 0x0000, NULL, NULL);
|
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)
|
|
|
|
{
|
|
|
|
struct bt_gatt_subscribe_params *params, *prev;
|
|
|
|
|
|
|
|
/* Lookup existing subscriptions */
|
|
|
|
for (params = subscriptions, prev = NULL; params;
|
|
|
|
prev = params, params = params->_next) {
|
|
|
|
if (bt_addr_le_cmp(¶ms->_peer, &conn->le.dst)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Force write to CCC to workaround devices that don't track
|
|
|
|
* it properly.
|
|
|
|
*/
|
|
|
|
gatt_write_ccc(conn, params->ccc_handle, params->value,
|
|
|
|
NULL, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_BLUETOOTH_GATT_CLIENT */
|
|
|
|
|
|
|
|
void bt_gatt_connected(struct bt_conn *conn)
|
|
|
|
{
|
|
|
|
BT_DBG("conn %p", conn);
|
|
|
|
bt_gatt_foreach_attr(0x0001, 0xffff, connected_cb, conn);
|
|
|
|
#if defined(CONFIG_BLUETOOTH_GATT_CLIENT)
|
|
|
|
add_subscriptions(conn);
|
2015-09-08 12:47:33 +02:00
|
|
|
#endif /* CONFIG_BLUETOOTH_GATT_CLIENT */
|
2016-03-24 15:16:26 +02:00
|
|
|
}
|
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);
|
|
|
|
|
|
|
|
#if defined(CONFIG_BLUETOOTH_GATT_CLIENT)
|
2016-02-18 13:08:34 +01:00
|
|
|
/* If bonded don't remove subscriptions */
|
2016-04-04 13:38:16 +03:00
|
|
|
if (bt_addr_le_is_bonded(&conn->le.dst)) {
|
2015-10-02 12:44:26 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-03-03 13:48:01 +02:00
|
|
|
remove_subscriptions(conn);
|
2015-10-02 12:44:26 +02:00
|
|
|
#endif /* CONFIG_BLUETOOTH_GATT_CLIENT */
|
|
|
|
}
|