These are mostly cosmetic changes, shall work with firmwares niko-0214 and niko-0215. Change-Id: Id39c6b9cee6e759f77a05259632e453492ffe498 Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
513 lines
12 KiB
C
513 lines
12 KiB
C
/*
|
|
* Copyright (c) 2016 Intel Corporation
|
|
*
|
|
* 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
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <atomic.h>
|
|
#include <misc/byteorder.h>
|
|
|
|
#include <bluetooth/gatt.h>
|
|
#include <bluetooth/log.h>
|
|
|
|
#include "conn_internal.h"
|
|
#include "gatt_internal.h"
|
|
|
|
#if !defined(CONFIG_BLUETOOTH_DEBUG_GATT)
|
|
#undef BT_DBG
|
|
#define BT_DBG(fmt, ...)
|
|
#endif
|
|
|
|
#define NBLE_BUF_SIZE 384
|
|
|
|
struct nble_gatt_service {
|
|
const struct bt_gatt_attr *attrs;
|
|
uint16_t attr_count;
|
|
};
|
|
|
|
static struct nble_gatt_service svc_db[BLE_GATTS_MAX_SERVICES];
|
|
static uint8_t svc_count;
|
|
|
|
/**
|
|
* Copy a UUID in a buffer using the smallest memory length
|
|
* @param buf Pointer to the memory where the UUID shall be copied
|
|
* @param uuid Pointer to the UUID to copy
|
|
* @return The length required to store the UUID in the memory
|
|
*/
|
|
static uint8_t bt_gatt_uuid_memcpy(uint8_t *buf, const struct bt_uuid *uuid)
|
|
{
|
|
uint8_t *ptr = buf;
|
|
|
|
/* Store the type of the UUID */
|
|
*ptr = uuid->type;
|
|
ptr++;
|
|
|
|
/* Store the UUID data */
|
|
if (uuid->type == BT_UUID_TYPE_16) {
|
|
uint16_t le16;
|
|
|
|
le16 = sys_cpu_to_le16(BT_UUID_16(uuid)->val);
|
|
memcpy(ptr, &le16, sizeof(le16));
|
|
ptr += sizeof(le16);
|
|
} else {
|
|
memcpy(ptr, BT_UUID_128(uuid)->val, 16);
|
|
ptr += 16;
|
|
}
|
|
|
|
return ptr - buf;
|
|
}
|
|
|
|
/* These attributes need the value to be read */
|
|
static struct bt_uuid *whitelist[] = {
|
|
BT_UUID_GATT_PRIMARY,
|
|
BT_UUID_GATT_SECONDARY,
|
|
BT_UUID_GATT_INCLUDE,
|
|
BT_UUID_GATT_CHRC,
|
|
BT_UUID_GATT_CEP,
|
|
BT_UUID_GATT_CUD,
|
|
BT_UUID_GATT_CPF,
|
|
BT_UUID_GAP_DEVICE_NAME,
|
|
BT_UUID_GAP_APPEARANCE,
|
|
BT_UUID_GAP_PPCP
|
|
};
|
|
|
|
static int attr_read(struct bt_gatt_attr *attr, uint8_t *data, size_t len)
|
|
{
|
|
uint8_t i;
|
|
int data_size;
|
|
|
|
if (!data) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
data_size = bt_gatt_uuid_memcpy(data, attr->uuid);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(whitelist); i++) {
|
|
if (!bt_uuid_cmp(attr->uuid, whitelist[i])) {
|
|
int read;
|
|
|
|
read = attr->read(NULL, attr, data + data_size, len, 0);
|
|
if (read < 0) {
|
|
return read;
|
|
}
|
|
|
|
data_size += read;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return data_size;
|
|
}
|
|
|
|
int bt_gatt_register(struct bt_gatt_attr *attrs, size_t count)
|
|
{
|
|
struct nble_gatt_register_req param;
|
|
size_t i;
|
|
/* TODO: Replace the following with net_buf */
|
|
uint8_t attr_table[NBLE_BUF_SIZE];
|
|
uint8_t attr_table_size;
|
|
|
|
if (!attrs || !count) {
|
|
return -EINVAL;
|
|
}
|
|
BT_ASSERT(svc_count < BLE_GATTS_MAX_SERVICES);
|
|
|
|
svc_db[svc_count].attrs = attrs;
|
|
svc_db[svc_count].attr_count = count;
|
|
svc_count++;
|
|
param.attr_base = attrs;
|
|
param.attr_count = count;
|
|
|
|
attr_table_size = 0;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
struct bt_gatt_attr *attr = &attrs[i];
|
|
struct nble_gatt_attr *att;
|
|
int err;
|
|
|
|
if (attr_table_size + sizeof(*att) > sizeof(attr_table)) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
att = (void *)&attr_table[attr_table_size];
|
|
att->perm = attr->perm;
|
|
|
|
attr_table_size += sizeof(*att);
|
|
|
|
/* Read attribute data */
|
|
err = attr_read(attr, att->data,
|
|
sizeof(attr_table) - attr_table_size);
|
|
if (err < 0) {
|
|
BT_ERR("Failed to read attr: %d", err);
|
|
return err;
|
|
}
|
|
|
|
att->data_size = err;
|
|
|
|
/* Compute the new element size and align it on upper 4 bytes
|
|
* boundary.
|
|
*/
|
|
attr_table_size += (att->data_size + 3) & ~3;
|
|
|
|
BT_DBG("table size = %u attr data_size = %u", attr_table_size,
|
|
att->data_size);
|
|
}
|
|
|
|
nble_gatt_register_req(¶m, attr_table, attr_table_size);
|
|
return 0;
|
|
}
|
|
|
|
void on_ble_gatt_register_rsp(const struct nble_gatt_register_rsp *rsp,
|
|
const struct nble_gatt_attr_handles *handles,
|
|
uint8_t len)
|
|
{
|
|
BT_DBG("status %u", rsp->status);
|
|
|
|
if (rsp->status != 0) {
|
|
return;
|
|
}
|
|
#if defined(CONFIG_BLUETOOTH_DEBUG_GATT)
|
|
{
|
|
int idx;
|
|
|
|
for (idx = 0; idx < rsp->attr_count; idx++) {
|
|
/* The following order of declaration is assumed for
|
|
* this to work (otherwise idx-2 will fail!):
|
|
* BT_GATT_CHARACTERISTIC -> ble core returns invalid
|
|
* handle.
|
|
* BT_GATT_DESCRIPTOR -> value handle of characteristic
|
|
* BT_GATT_CCC -> cccd handle is ignored as no storage
|
|
* but reference value is updated in CCC with value
|
|
* handle from descriptor.
|
|
*/
|
|
if (handles[idx].handle != 0) {
|
|
char uuid[37];
|
|
|
|
bt_uuid_to_str(rsp->attr_base[idx].uuid,
|
|
uuid, sizeof(uuid));
|
|
BT_DBG("handle %u uuid %s", handles[idx].handle,
|
|
uuid);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void bt_gatt_foreach_attr(uint16_t start_handle, uint16_t end_handle,
|
|
bt_gatt_attr_func_t func, void *user_data)
|
|
{
|
|
}
|
|
|
|
struct bt_gatt_attr *bt_gatt_attr_next(const struct bt_gatt_attr *attr)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
int 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)
|
|
{
|
|
uint16_t len;
|
|
|
|
BT_DBG("handle 0x%04x offset %u", attr->handle, offset);
|
|
|
|
/* simply return the value length. used as max_value. */
|
|
if (buf == NULL) {
|
|
return value_len;
|
|
}
|
|
|
|
if (offset > value_len) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
len = min(buf_len, value_len - offset);
|
|
|
|
memcpy(buf, value + offset, len);
|
|
|
|
return len;
|
|
}
|
|
|
|
int bt_gatt_attr_read_service(struct bt_conn *conn,
|
|
const struct bt_gatt_attr *attr,
|
|
void *buf, uint16_t len, uint16_t offset)
|
|
{
|
|
struct bt_uuid *uuid = attr->user_data;
|
|
|
|
if (uuid->type == BT_UUID_TYPE_16) {
|
|
uint16_t uuid16 = sys_cpu_to_le16(BT_UUID_16(uuid)->val);
|
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset,
|
|
&uuid16, 2);
|
|
}
|
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset,
|
|
BT_UUID_128(uuid)->val, 16);
|
|
}
|
|
|
|
int bt_gatt_attr_read_included(struct bt_conn *conn,
|
|
const struct bt_gatt_attr *attr,
|
|
void *buf, uint16_t len, uint16_t offset)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
struct gatt_chrc {
|
|
uint8_t properties;
|
|
uint16_t value_handle;
|
|
union {
|
|
uint16_t uuid16;
|
|
uint8_t uuid[16];
|
|
};
|
|
} __packed;
|
|
|
|
int bt_gatt_attr_read_chrc(struct bt_conn *conn,
|
|
const struct bt_gatt_attr *attr, void *buf,
|
|
uint16_t len, uint16_t offset)
|
|
{
|
|
struct bt_gatt_chrc *chrc = attr->user_data;
|
|
struct gatt_chrc pdu;
|
|
uint8_t value_len;
|
|
|
|
pdu.properties = chrc->properties;
|
|
|
|
/* Handle cannot be read at this point */
|
|
pdu.value_handle = 0x0000;
|
|
|
|
value_len = sizeof(pdu.properties) + sizeof(pdu.value_handle);
|
|
|
|
if (chrc->uuid->type == BT_UUID_TYPE_16) {
|
|
pdu.uuid16 = sys_cpu_to_le16(BT_UUID_16(chrc->uuid)->val);
|
|
value_len += 2;
|
|
} else {
|
|
memcpy(pdu.uuid, BT_UUID_128(chrc->uuid)->val, 16);
|
|
value_len += 16;
|
|
}
|
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &pdu, value_len);
|
|
}
|
|
|
|
int bt_gatt_attr_read_ccc(struct bt_conn *conn,
|
|
const struct bt_gatt_attr *attr, void *buf,
|
|
uint16_t len, uint16_t offset)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
int bt_gatt_attr_write_ccc(struct bt_conn *conn,
|
|
const struct bt_gatt_attr *attr, const void *buf,
|
|
uint16_t len, uint16_t offset)
|
|
{
|
|
struct _bt_gatt_ccc *ccc = attr->user_data;
|
|
const uint16_t *data = buf;
|
|
|
|
if (offset > sizeof(*data)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (offset + len > sizeof(*data)) {
|
|
return -EFBIG;
|
|
}
|
|
|
|
/* We expect to receive this only when the has really changed */
|
|
ccc->value = sys_le16_to_cpu(*data);
|
|
|
|
if (ccc->cfg_changed) {
|
|
ccc->cfg_changed(ccc->value);
|
|
}
|
|
|
|
BT_DBG("handle 0x%04x value %u", attr->handle, ccc->value);
|
|
|
|
return len;
|
|
}
|
|
|
|
int bt_gatt_attr_read_cep(struct bt_conn *conn,
|
|
const struct bt_gatt_attr *attr, void *buf,
|
|
uint16_t len, uint16_t offset)
|
|
{
|
|
struct bt_gatt_cep *value = attr->user_data;
|
|
uint16_t props = sys_cpu_to_le16(value->properties);
|
|
|
|
return bt_gatt_attr_read(conn, attr, buf, len, offset, &props,
|
|
sizeof(props));
|
|
}
|
|
|
|
int bt_gatt_attr_read_cud(struct bt_conn *conn,
|
|
const struct bt_gatt_attr *attr, void *buf,
|
|
uint16_t len, uint16_t offset)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
int bt_gatt_attr_read_cpf(struct bt_conn *conn,
|
|
const struct bt_gatt_attr *attr, void *buf,
|
|
uint16_t len, uint16_t offset)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
int bt_gatt_notify(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
|
const void *data, uint16_t len)
|
|
{
|
|
struct nble_gatt_send_notif_ind_params notif;
|
|
|
|
if (conn) {
|
|
notif.conn_handle = conn->handle;
|
|
} else {
|
|
notif.conn_handle = 0xffff;
|
|
}
|
|
|
|
notif.params.attr = (struct bt_gatt_attr *)attr;
|
|
notif.params.offset = 0;
|
|
|
|
nble_gatt_send_notif_req(¬if, (uint8_t *)data, len);
|
|
return 0;
|
|
}
|
|
|
|
int bt_gatt_exchange_mtu(struct bt_conn *conn, bt_gatt_rsp_func_t func)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
int bt_gatt_discover(struct bt_conn *conn,
|
|
struct bt_gatt_discover_params *params)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
int bt_gatt_write(struct bt_conn *conn, uint16_t handle, uint16_t offset,
|
|
const void *data, uint16_t length, bt_gatt_rsp_func_t func)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
int bt_gatt_write_without_response(struct bt_conn *conn, uint16_t handle,
|
|
const void *data, uint16_t length,
|
|
bool sign)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
int bt_gatt_subscribe(struct bt_conn *conn,
|
|
struct bt_gatt_subscribe_params *params)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
int bt_gatt_unsubscribe(struct bt_conn *conn,
|
|
struct bt_gatt_subscribe_params *params)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
void bt_gatt_cancel(struct bt_conn *conn)
|
|
{
|
|
}
|
|
|
|
void on_nble_gattc_write_rsp(const struct nble_gattc_write_rsp *ev,
|
|
void *priv)
|
|
{
|
|
BT_DBG("");
|
|
}
|
|
|
|
void on_nble_gattc_read_rsp(const struct nble_gattc_read_rsp *ev,
|
|
uint8_t *data, uint8_t data_len, void *priv)
|
|
{
|
|
BT_DBG("");
|
|
}
|
|
|
|
void on_nble_gattc_value_evt(const struct nble_gattc_value_evt *ev,
|
|
uint8_t *buf, uint8_t buflen)
|
|
{
|
|
BT_DBG("");
|
|
}
|
|
|
|
void on_nble_gatts_write_evt(const struct nble_gatt_wr_evt *evt,
|
|
const uint8_t *buf, uint8_t buflen)
|
|
{
|
|
const struct bt_gatt_attr *attr = evt->attr;
|
|
struct nble_gatts_rw_reply_params reply_data;
|
|
|
|
BT_DBG("handle 0x%04x buf %p len %u", attr->handle, buf, buflen);
|
|
|
|
if (attr->write) {
|
|
reply_data.status = attr->write(NULL, attr, buf, buflen,
|
|
evt->offset);
|
|
}
|
|
|
|
if (evt->reply) {
|
|
reply_data.conn_handle = evt->conn_handle;
|
|
reply_data.offset = evt->offset;
|
|
reply_data.write_reply = 1;
|
|
|
|
nble_gatts_authorize_reply_req(&reply_data, NULL,
|
|
reply_data.status);
|
|
}
|
|
}
|
|
|
|
void on_nble_gatts_get_attribute_value_rsp(const struct nble_gatts_attribute_rsp *rsp,
|
|
uint8_t *data, uint8_t length)
|
|
{
|
|
BT_DBG("");
|
|
}
|
|
|
|
void on_nble_gattc_discover_rsp(const struct nble_gattc_disc_rsp *rsp,
|
|
const uint8_t *data, uint8_t len)
|
|
{
|
|
BT_DBG("");
|
|
}
|
|
|
|
void on_nble_gatts_send_svc_changed_rsp(const struct nble_core_response *par)
|
|
{
|
|
BT_DBG("");
|
|
}
|
|
|
|
void on_nble_gatts_set_attribute_value_rsp(const struct nble_gatts_attribute_rsp *rsp)
|
|
{
|
|
BT_DBG("");
|
|
}
|
|
|
|
void on_nble_gatts_send_notif_ind_rsp(const struct nble_gatt_notif_ind_rsp *par)
|
|
{
|
|
BT_DBG("");
|
|
}
|
|
|
|
void on_nble_gatts_read_evt(const struct nble_gatt_rd_evt *evt)
|
|
{
|
|
struct nble_gatts_rw_reply_params reply_data;
|
|
const struct bt_gatt_attr *attr;
|
|
/* TODO: Replace the following with net_buf */
|
|
uint8_t data[NBLE_BUF_SIZE];
|
|
|
|
reply_data.status = -EACCES;
|
|
|
|
attr = evt->attr;
|
|
|
|
BT_DBG("handle %p", attr);
|
|
|
|
if (attr->read) {
|
|
reply_data.status = attr->read(NULL, attr, data, sizeof(data),
|
|
evt->offset);
|
|
}
|
|
|
|
reply_data.conn_handle = evt->conn_handle;
|
|
reply_data.offset = evt->offset;
|
|
reply_data.write_reply = 0;
|
|
|
|
nble_gatts_authorize_reply_req(&reply_data, data, reply_data.status);
|
|
}
|