mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-08-27 11:45:22 +00:00
Update the signature of the `bt_gatt_indicate_func_t` callback type by replacing the attr pointer with a pointer to the `bt_gatt_indicate_params` struct that was used to start the indication. This allows the callback to free the `bt_gatt_indicate_params` instance if it was allocated from storage, while still allowing the `bt_gatt_attr` value to be accessed through `params->attr`. Allocating the `bt_gatt_indicate_params` instance from storage is desirable as multiple indications can be queued, however each instance must be valid until the callback is run. Implements API update from #29357 Signed-off-by: Jordan Yates <jordan.yates@data61.csiro.au>
342 lines
9.1 KiB
C
342 lines
9.1 KiB
C
/*
|
|
* Copyright (c) 2020 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/types.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <sys/printk.h>
|
|
#include <sys/byteorder.h>
|
|
#include <zephyr.h>
|
|
|
|
#include <bluetooth/gatt.h>
|
|
#include <bluetooth/services/ots.h>
|
|
#include "ots_internal.h"
|
|
|
|
#include <logging/log.h>
|
|
|
|
LOG_MODULE_DECLARE(bt_ots, CONFIG_BT_OTS_LOG_LEVEL);
|
|
|
|
#define OACP_PROC_TYPE_SIZE 1
|
|
#define OACP_RES_MAX_SIZE 3
|
|
|
|
static enum bt_gatt_ots_oacp_res_code oacp_read_proc_validate(
|
|
struct bt_conn *conn,
|
|
struct bt_ots *ots,
|
|
struct bt_gatt_ots_oacp_proc *proc)
|
|
{
|
|
struct bt_gatt_ots_oacp_read_params *params = &proc->read_params;
|
|
|
|
LOG_DBG("Validating Read procedure with offset: 0x%08X and "
|
|
"length: 0x%08X", params->offset, params->len);
|
|
|
|
if (!ots->cur_obj) {
|
|
return BT_GATT_OTS_OACP_RES_INV_OBJ;
|
|
}
|
|
|
|
if (!BT_OTS_OBJ_GET_PROP_READ(ots->cur_obj->metadata.props)) {
|
|
return BT_GATT_OTS_OACP_RES_NOT_PERMITTED;
|
|
}
|
|
|
|
if (!bt_gatt_ots_l2cap_is_open(&ots->l2cap, conn)) {
|
|
return BT_GATT_OTS_OACP_RES_CHAN_UNAVAIL;
|
|
}
|
|
|
|
if ((params->offset + (uint64_t) params->len) >
|
|
ots->cur_obj->metadata.size.cur) {
|
|
return BT_GATT_OTS_OACP_RES_INV_PARAM;
|
|
}
|
|
|
|
if (ots->cur_obj->state.type != BT_GATT_OTS_OBJECT_IDLE_STATE) {
|
|
return BT_GATT_OTS_OACP_RES_OBJ_LOCKED;
|
|
}
|
|
|
|
ots->cur_obj->state.type = BT_GATT_OTS_OBJECT_READ_OP_STATE;
|
|
ots->cur_obj->state.read_op.sent_len = 0;
|
|
memcpy(&ots->cur_obj->state.read_op.oacp_params, &proc->read_params,
|
|
sizeof(ots->cur_obj->state.read_op.oacp_params));
|
|
|
|
LOG_DBG("Read procedure is accepted");
|
|
|
|
return BT_GATT_OTS_OACP_RES_SUCCESS;
|
|
}
|
|
|
|
static enum bt_gatt_ots_oacp_res_code oacp_proc_validate(
|
|
struct bt_conn *conn,
|
|
struct bt_ots *ots,
|
|
struct bt_gatt_ots_oacp_proc *proc)
|
|
{
|
|
switch (proc->type) {
|
|
case BT_GATT_OTS_OACP_PROC_READ:
|
|
return oacp_read_proc_validate(conn, ots, proc);
|
|
case BT_GATT_OTS_OACP_PROC_CREATE:
|
|
case BT_GATT_OTS_OACP_PROC_DELETE:
|
|
case BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC:
|
|
case BT_GATT_OTS_OACP_PROC_EXECUTE:
|
|
case BT_GATT_OTS_OACP_PROC_WRITE:
|
|
case BT_GATT_OTS_OACP_PROC_ABORT:
|
|
default:
|
|
return BT_GATT_OTS_OACP_RES_OPCODE_NOT_SUP;
|
|
}
|
|
};
|
|
|
|
static enum bt_gatt_ots_oacp_res_code oacp_command_decode(
|
|
const uint8_t *buf, uint16_t len,
|
|
struct bt_gatt_ots_oacp_proc *proc)
|
|
{
|
|
struct net_buf_simple net_buf;
|
|
|
|
net_buf_simple_init_with_data(&net_buf, (void *) buf, len);
|
|
|
|
proc->type = net_buf_simple_pull_u8(&net_buf);
|
|
switch (proc->type) {
|
|
case BT_GATT_OTS_OACP_PROC_CREATE:
|
|
proc->create_params.size = net_buf_simple_pull_le32(&net_buf);
|
|
bt_uuid_create(&proc->create_params.type.uuid, net_buf.data,
|
|
net_buf.len);
|
|
net_buf_simple_pull_mem(&net_buf, net_buf.len);
|
|
break;
|
|
case BT_GATT_OTS_OACP_PROC_DELETE:
|
|
break;
|
|
case BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC:
|
|
proc->cs_calc_params.offset =
|
|
net_buf_simple_pull_le32(&net_buf);
|
|
proc->cs_calc_params.len =
|
|
net_buf_simple_pull_le32(&net_buf);
|
|
break;
|
|
case BT_GATT_OTS_OACP_PROC_EXECUTE:
|
|
break;
|
|
case BT_GATT_OTS_OACP_PROC_READ:
|
|
proc->read_params.offset =
|
|
net_buf_simple_pull_le32(&net_buf);
|
|
proc->read_params.len =
|
|
net_buf_simple_pull_le32(&net_buf);
|
|
return BT_GATT_OTS_OACP_RES_SUCCESS;
|
|
case BT_GATT_OTS_OACP_PROC_WRITE:
|
|
proc->write_params.offset =
|
|
net_buf_simple_pull_le32(&net_buf);
|
|
proc->write_params.len =
|
|
net_buf_simple_pull_le32(&net_buf);
|
|
proc->write_params.mode =
|
|
net_buf_simple_pull_u8(&net_buf);
|
|
break;
|
|
case BT_GATT_OTS_OACP_PROC_ABORT:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
LOG_WRN("OACP unsupported procedure type");
|
|
return BT_GATT_OTS_OACP_RES_OPCODE_NOT_SUP;
|
|
}
|
|
|
|
static bool oacp_command_len_verify(struct bt_gatt_ots_oacp_proc *proc,
|
|
uint16_t len)
|
|
{
|
|
uint16_t ref_len = OACP_PROC_TYPE_SIZE;
|
|
|
|
switch (proc->type) {
|
|
case BT_GATT_OTS_OACP_PROC_CREATE:
|
|
{
|
|
struct bt_ots_obj_type *type;
|
|
|
|
ref_len += sizeof(proc->create_params.size);
|
|
|
|
type = &proc->create_params.type;
|
|
if (type->uuid.type == BT_UUID_TYPE_16) {
|
|
ref_len += sizeof(type->uuid_16.val);
|
|
} else if (type->uuid.type == BT_UUID_TYPE_128) {
|
|
ref_len += sizeof(type->uuid_128.val);
|
|
} else {
|
|
return true;
|
|
}
|
|
} break;
|
|
case BT_GATT_OTS_OACP_PROC_DELETE:
|
|
break;
|
|
case BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC:
|
|
ref_len += sizeof(proc->cs_calc_params);
|
|
break;
|
|
case BT_GATT_OTS_OACP_PROC_EXECUTE:
|
|
break;
|
|
case BT_GATT_OTS_OACP_PROC_READ:
|
|
ref_len += sizeof(proc->read_params);
|
|
break;
|
|
case BT_GATT_OTS_OACP_PROC_WRITE:
|
|
ref_len += sizeof(proc->write_params);
|
|
break;
|
|
case BT_GATT_OTS_OACP_PROC_ABORT:
|
|
break;
|
|
default:
|
|
return true;
|
|
}
|
|
|
|
return (len == ref_len);
|
|
}
|
|
|
|
static void oacp_read_proc_cb(struct bt_gatt_ots_l2cap *l2cap_ctx,
|
|
struct bt_conn *conn)
|
|
{
|
|
int err;
|
|
uint8_t *obj_chunk;
|
|
uint32_t offset;
|
|
uint32_t len;
|
|
struct bt_ots *ots;
|
|
struct bt_gatt_ots_object_read_op *read_op;
|
|
|
|
ots = CONTAINER_OF(l2cap_ctx, struct bt_ots, l2cap);
|
|
read_op = &ots->cur_obj->state.read_op;
|
|
offset = read_op->oacp_params.offset + read_op->sent_len;
|
|
|
|
if (read_op->sent_len >= read_op->oacp_params.len) {
|
|
LOG_DBG("OACP Read Op over L2CAP is completed");
|
|
|
|
if (read_op->sent_len > read_op->oacp_params.len) {
|
|
LOG_WRN("More bytes sent that the client requested");
|
|
}
|
|
|
|
ots->cur_obj->state.type = BT_GATT_OTS_OBJECT_IDLE_STATE;
|
|
ots->cb->obj_read(ots, conn, ots->cur_obj->id, NULL, 0,
|
|
offset);
|
|
return;
|
|
}
|
|
|
|
len = read_op->oacp_params.len - read_op->sent_len;
|
|
len = ots->cb->obj_read(ots, conn, ots->cur_obj->id, &obj_chunk, len,
|
|
offset);
|
|
|
|
ots->l2cap.tx_done = oacp_read_proc_cb;
|
|
err = bt_gatt_ots_l2cap_send(&ots->l2cap, obj_chunk, len);
|
|
if (err) {
|
|
LOG_ERR("L2CAP CoC error: %d while trying to execute OACP "
|
|
"Read procedure", err);
|
|
ots->cur_obj->state.type = BT_GATT_OTS_OBJECT_IDLE_STATE;
|
|
} else {
|
|
read_op->sent_len += len;
|
|
}
|
|
}
|
|
|
|
static void oacp_read_proc_execute(struct bt_ots *ots,
|
|
struct bt_conn *conn)
|
|
{
|
|
struct bt_gatt_ots_oacp_read_params *params =
|
|
&ots->cur_obj->state.read_op.oacp_params;
|
|
|
|
if (!ots->cur_obj) {
|
|
LOG_ERR("Invalid Current Object on OACP Read procedure");
|
|
return;
|
|
}
|
|
|
|
LOG_DBG("Executing Read procedure with offset: 0x%08X and "
|
|
"length: 0x%08X", params->offset, params->len);
|
|
|
|
if (ots->cb->obj_read) {
|
|
oacp_read_proc_cb(&ots->l2cap, conn);
|
|
} else {
|
|
ots->cur_obj->state.type = BT_GATT_OTS_OBJECT_IDLE_STATE;
|
|
LOG_ERR("OTS Read operation failed: "
|
|
"there is no OTS Read callback");
|
|
}
|
|
}
|
|
|
|
static void oacp_ind_cb(struct bt_conn *conn,
|
|
struct bt_gatt_indicate_params *params,
|
|
uint8_t err)
|
|
{
|
|
struct bt_ots *ots = (struct bt_ots *) params->attr->user_data;
|
|
|
|
LOG_DBG("Received OACP Indication ACK with status: 0x%04X", err);
|
|
|
|
switch (ots->cur_obj->state.type) {
|
|
case BT_GATT_OTS_OBJECT_READ_OP_STATE:
|
|
oacp_read_proc_execute(ots, conn);
|
|
break;
|
|
default:
|
|
LOG_ERR("Unsupported OTS state: %d", ots->cur_obj->state.type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int oacp_ind_send(const struct bt_gatt_attr *oacp_attr,
|
|
enum bt_gatt_ots_oacp_proc_type req_op_code,
|
|
enum bt_gatt_ots_oacp_res_code oacp_status)
|
|
{
|
|
uint8_t oacp_res[OACP_RES_MAX_SIZE];
|
|
uint16_t oacp_res_len = 0;
|
|
struct bt_ots *ots = (struct bt_ots *) oacp_attr->user_data;
|
|
|
|
/* Encode OACP Response */
|
|
oacp_res[oacp_res_len++] = BT_GATT_OTS_OACP_PROC_RESP;
|
|
oacp_res[oacp_res_len++] = req_op_code;
|
|
oacp_res[oacp_res_len++] = oacp_status;
|
|
|
|
/* Prepare indication parameters */
|
|
memset(&ots->oacp_ind.params, 0, sizeof(ots->oacp_ind.params));
|
|
memcpy(&ots->oacp_ind.attr, oacp_attr, sizeof(ots->oacp_ind.attr));
|
|
ots->oacp_ind.params.attr = &ots->oacp_ind.attr;
|
|
ots->oacp_ind.params.func = oacp_ind_cb;
|
|
ots->oacp_ind.params.data = oacp_res;
|
|
ots->oacp_ind.params.len = oacp_res_len;
|
|
|
|
LOG_DBG("Sending OACP indication");
|
|
|
|
return bt_gatt_indicate(NULL, &ots->oacp_ind.params);
|
|
}
|
|
|
|
ssize_t bt_gatt_ots_oacp_write(struct bt_conn *conn,
|
|
const struct bt_gatt_attr *attr,
|
|
const void *buf, uint16_t len,
|
|
uint16_t offset, uint8_t flags)
|
|
{
|
|
enum bt_gatt_ots_oacp_res_code oacp_status;
|
|
struct bt_gatt_ots_oacp_proc oacp_proc;
|
|
struct bt_ots *ots = (struct bt_ots *) attr->user_data;
|
|
|
|
LOG_DBG("Object Action Control Point GATT Write Operation");
|
|
|
|
if (!ots->oacp_ind.is_enabled) {
|
|
LOG_WRN("OACP indications not enabled");
|
|
return BT_GATT_ERR(BT_ATT_ERR_CCC_IMPROPER_CONF);
|
|
}
|
|
|
|
if (offset != 0) {
|
|
LOG_ERR("Invalid offset of OACP Write Request");
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
|
}
|
|
|
|
oacp_status = oacp_command_decode(buf, len, &oacp_proc);
|
|
|
|
if (!oacp_command_len_verify(&oacp_proc, len)) {
|
|
LOG_ERR("Invalid length of OACP Write Request for 0x%02X "
|
|
"Op Code", oacp_proc.type);
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
|
}
|
|
|
|
if (oacp_status == BT_GATT_OTS_OACP_RES_SUCCESS) {
|
|
oacp_status = oacp_proc_validate(conn, ots, &oacp_proc);
|
|
}
|
|
|
|
if (oacp_status != BT_GATT_OTS_OACP_RES_SUCCESS) {
|
|
LOG_WRN("OACP Write error status: 0x%02X", oacp_status);
|
|
}
|
|
|
|
oacp_ind_send(attr, oacp_proc.type, oacp_status);
|
|
return len;
|
|
}
|
|
|
|
void bt_gatt_ots_oacp_cfg_changed(const struct bt_gatt_attr *attr,
|
|
uint16_t value)
|
|
{
|
|
struct bt_gatt_ots_indicate *oacp_ind =
|
|
CONTAINER_OF((struct _bt_gatt_ccc *) attr->user_data,
|
|
struct bt_gatt_ots_indicate, ccc);
|
|
|
|
LOG_DBG("Object Action Control Point CCCD value: 0x%04X", value);
|
|
|
|
oacp_ind->is_enabled = false;
|
|
if (value == BT_GATT_CCC_INDICATE) {
|
|
oacp_ind->is_enabled = true;
|
|
}
|
|
}
|