Bluetooth: OTS - Add Calculate Checksum support

OTS add Calculate checksum feature support.
OTS client add object calculate checksum function.

Signed-off-by: Pirun Lee <pirun.lee@nordicsemi.no>
This commit is contained in:
Pirun Lee 2023-01-10 09:52:10 +08:00 committed by Carles Cufí
commit 1e6f36cca7
5 changed files with 285 additions and 17 deletions

View file

@ -41,6 +41,9 @@ config BT_OTS_OACP_CREATE_SUPPORT
depends on BT_OTS_OACP_WRITE_SUPPORT
depends on BT_OTS_OBJ_NAME_WRITE_SUPPORT
config BT_OTS_OACP_CHECKSUM_SUPPORT
bool "Support OACP Calculate Checksum operation"
config BT_OTS_OACP_DELETE_SUPPORT
bool "Support OACP Delete Operation"

View file

@ -42,6 +42,12 @@ LOG_MODULE_REGISTER(bt_ots, CONFIG_BT_OTS_LOG_LEVEL);
#define OACP_FEAT_BIT_DELETE 0
#endif
#if defined(BT_OTS_OACP_CHECKSUM_SUPPORT)
#define OACP_FEAT_BIT_CRC BIT(BT_OTS_OACP_FEAT_CHECKSUM)
#else
#define OACP_FEAT_BIT_CRC 0
#endif
#if defined(CONFIG_BT_OTS_OACP_READ_SUPPORT)
#define OACP_FEAT_BIT_READ BIT(BT_OTS_OACP_FEAT_READ)
#else
@ -64,6 +70,7 @@ LOG_MODULE_REGISTER(bt_ots, CONFIG_BT_OTS_LOG_LEVEL);
#define OACP_FEAT ( \
OACP_FEAT_BIT_CREATE | \
OACP_FEAT_BIT_DELETE | \
OACP_FEAT_BIT_CRC | \
OACP_FEAT_BIT_READ | \
OACP_FEAT_BIT_WRITE | \
OACP_FEAT_BIT_PATCH)
@ -443,6 +450,10 @@ int bt_ots_init(struct bt_ots *ots,
__ASSERT(ots_init->cb->obj_deleted ||
!BT_OTS_OACP_GET_FEAT_CREATE(ots_init->features.oacp),
"Callback for object deletion is not set and object creation is enabled");
#if defined(CONFIG_BT_OTS_OACP_CHECKSUM_SUPPORT)
__ASSERT(ots_init->cb->obj_cal_checksum,
"Callback for object calculate checksum is not set");
#endif
__ASSERT(ots_init->cb->obj_read ||
!BT_OTS_OACP_GET_FEAT_READ(ots_init->features.oacp),
"Callback for object reading is not set");

View file

@ -125,6 +125,8 @@ static int oacp_read(struct bt_conn *conn,
static int oacp_write(struct bt_conn *conn, struct bt_otc_internal_instance_t *inst,
const void *buf, uint32_t len, uint32_t offset,
enum bt_ots_oacp_write_op_mode mode);
static int oacp_checksum(struct bt_conn *conn, struct bt_otc_internal_instance_t *inst,
uint32_t offset, uint32_t len);
static void read_next_metadata(struct bt_conn *conn,
struct bt_otc_internal_instance_t *inst);
static int read_attr(struct bt_conn *conn,
@ -356,6 +358,9 @@ static void oacp_ind_handler(struct bt_conn *conn,
const void *data, uint16_t length)
{
enum bt_gatt_ots_oacp_proc_type op_code;
enum bt_gatt_ots_oacp_proc_type req_opcode;
enum bt_gatt_ots_oacp_res_code result_code;
uint32_t checksum;
struct net_buf_simple net_buf;
net_buf_simple_init_with_data(&net_buf, (void *)data, length);
@ -365,10 +370,29 @@ static void oacp_ind_handler(struct bt_conn *conn,
LOG_DBG("OACP indication");
if (op_code == BT_GATT_OTS_OACP_PROC_RESP) {
enum bt_gatt_ots_oacp_proc_type req_opcode =
net_buf_simple_pull_u8(&net_buf);
enum bt_gatt_ots_oacp_res_code result_code =
net_buf_simple_pull_u8(&net_buf);
if (net_buf.len >= (sizeof(req_opcode) + sizeof(result_code))) {
req_opcode = net_buf_simple_pull_u8(&net_buf);
result_code = net_buf_simple_pull_u8(&net_buf);
} else {
LOG_ERR("Invalid indication data len %u", net_buf.len);
return;
}
if (req_opcode == BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC) {
if (net_buf.len == sizeof(checksum)) {
checksum = net_buf_simple_pull_le32(&net_buf);
LOG_DBG("Object checksum 0x%08x\n", checksum);
if (otc_inst->cb->obj_checksum_calculated) {
otc_inst->cb->obj_checksum_calculated(
otc_inst, conn, result_code, checksum);
}
} else {
LOG_ERR("Invalid indication data len %u after opcode and result "
"pulled", net_buf.len);
return;
}
}
print_oacp_response(req_opcode, result_code);
} else {
LOG_DBG("Invalid indication opcode %u", op_code);
@ -1263,6 +1287,49 @@ static int oacp_write(struct bt_conn *conn, struct bt_otc_internal_instance_t *i
return err;
}
static int oacp_checksum(struct bt_conn *conn, struct bt_otc_internal_instance_t *inst,
uint32_t offset, uint32_t len)
{
int err;
if (!inst->otc_inst->oacp_handle) {
LOG_DBG("Handle not set");
return -EINVAL;
} else if (inst->busy) {
LOG_DBG("Client is busy");
return -EBUSY;
} else if (cur_inst) {
LOG_DBG("Previous operation is not finished");
return -EBUSY;
}
net_buf_simple_reset(&otc_tx_buf);
/* OP Code */
net_buf_simple_add_u8(&otc_tx_buf, BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC);
/* Offset */
net_buf_simple_add_le32(&otc_tx_buf, offset);
/* Len */
net_buf_simple_add_le32(&otc_tx_buf, len);
inst->otc_inst->write_params.offset = 0;
inst->otc_inst->write_params.data = otc_tx_buf.data;
inst->otc_inst->write_params.length = otc_tx_buf.len;
inst->otc_inst->write_params.handle = inst->otc_inst->oacp_handle;
inst->otc_inst->write_params.func = write_oacp_cp_cb;
err = bt_gatt_write(conn, &inst->otc_inst->write_params);
if (err != 0) {
inst->busy = true;
cur_inst = inst;
}
return err;
}
int bt_ots_client_read_object_data(struct bt_ots_client *otc_inst,
struct bt_conn *conn)
{
@ -1317,7 +1384,7 @@ int bt_ots_client_write_object_data(struct bt_ots_client *otc_inst,
* Offset and Length field are UINT32 Length
*/
CHECKIF(len > UINT32_MAX) {
LOG_ERR("length exceeds UINT32");
LOG_ERR("length %zu exceeds UINT32", len);
return -EINVAL;
}
@ -1327,7 +1394,7 @@ int bt_ots_client_write_object_data(struct bt_ots_client *otc_inst,
}
CHECKIF((offset > UINT32_MAX) || (offset < 0)) {
LOG_ERR("offset exceeds UINT32");
LOG_ERR("offset %ld exceeds UINT32 and must be >= 0", offset);
return -EINVAL;
}
@ -1359,6 +1426,54 @@ int bt_ots_client_write_object_data(struct bt_ots_client *otc_inst,
return oacp_write(conn, inst, buf, (uint32_t)len, (uint32_t)offset, mode);
}
int bt_ots_client_get_object_checksum(struct bt_ots_client *otc_inst, struct bt_conn *conn,
off_t offset, size_t len)
{
struct bt_otc_internal_instance_t *inst;
CHECKIF(!conn) {
LOG_DBG("Invalid Connection");
return -ENOTCONN;
}
CHECKIF(!otc_inst) {
LOG_DBG("Invalid OTC instance");
return -EINVAL;
}
/* OTS_v10.pdf Table 3.9: Object Action Control Point Procedure Requirements
* Offset and Length field are UINT32 Length
*/
CHECKIF(len > UINT32_MAX) {
LOG_DBG("length %zu exceeds UINT32", len);
return -EINVAL;
}
CHECKIF(len == 0) {
LOG_DBG("length equals zero");
return -EINVAL;
}
CHECKIF((offset > UINT32_MAX) || (offset < 0)) {
LOG_DBG("offset exceeds %ld UINT32 and must be >= 0", offset);
return -EINVAL;
}
CHECKIF((len + offset) > otc_inst->cur_object.size.cur) {
LOG_DBG("The sum of offset (%ld) and length (%zu) exceed the Current Size %lu "
"alloc %zu.", offset, len, (len + offset), otc_inst->cur_object.size.cur);
return -EINVAL;
}
inst = lookup_inst_by_handle(otc_inst->start_handle);
if (!inst) {
LOG_DBG("Invalid OTC instance");
return -EINVAL;
}
return oacp_checksum(conn, inst, (uint32_t)offset, (uint32_t)len);
}
static void read_next_metadata(struct bt_conn *conn,
struct bt_otc_internal_instance_t *inst)
{

View file

@ -10,7 +10,9 @@
#include <errno.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/check.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/crc.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/services/ots.h>
@ -23,7 +25,16 @@
LOG_MODULE_DECLARE(bt_ots, CONFIG_BT_OTS_LOG_LEVEL);
#define OACP_PROC_TYPE_SIZE 1
#define OACP_RES_MAX_SIZE 3
/**
* OTS_v10.pdf Table 3.10: Format of OACP Response V
* OACP Response Value contains
* 1 octet Procedure code
* 1 octet Request op code
* 1 octet Result Code
* 4 octet CRC checksum (if present)
* Execute operation is not supported
**/
#define OACP_RES_MAX_SIZE (3 + sizeof(uint32_t))
#if defined(CONFIG_BT_OTS_OACP_WRITE_SUPPORT)
static ssize_t oacp_write_proc_cb(struct bt_gatt_ots_l2cap *l2cap_ctx,
@ -161,12 +172,62 @@ exit:
}
#endif
#if defined(CONFIG_BT_OTS_OACP_CHECKSUM_SUPPORT)
static enum bt_gatt_ots_oacp_res_code oacp_checksum_proc_validate(
struct bt_conn *conn,
struct bt_ots *ots,
struct bt_gatt_ots_oacp_proc *proc,
struct net_buf_simple *resp_param)
{
struct bt_gatt_ots_oacp_cs_calc_params *params = &proc->cs_calc_params;
void *obj_data;
int err;
uint32_t checksum;
LOG_DBG("Validating Checksum 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 (params->offset > ots->cur_obj->metadata.size.cur) {
return BT_GATT_OTS_OACP_RES_INV_PARAM;
}
if ((params->offset + (uint64_t) params->len) > ots->cur_obj->metadata.size.alloc) {
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;
}
if (ots->cb->obj_cal_checksum) {
err = ots->cb->obj_cal_checksum(ots, conn, ots->cur_obj->id, params->offset,
params->len, &obj_data);
if (err != 0) {
return BT_GATT_OTS_OACP_RES_OPER_FAILED;
}
checksum = bt_ots_client_calc_checksum((const uint8_t *)obj_data, params->len);
net_buf_simple_reserve(resp_param, sizeof(uint32_t));
net_buf_simple_add_le32(resp_param, checksum);
LOG_DBG("Calculate from offset %u len %u checksum 0x%08x\n", params->offset,
params->len, checksum);
return BT_GATT_OTS_OACP_RES_SUCCESS;
} else {
return BT_GATT_OTS_OACP_RES_OPER_FAILED;
}
}
#endif
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)
const struct bt_gatt_ots_oacp_proc *proc)
{
struct bt_gatt_ots_oacp_read_params *params = &proc->read_params;
const 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);
@ -273,7 +334,8 @@ static enum bt_gatt_ots_oacp_res_code oacp_write_proc_validate(
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)
struct bt_gatt_ots_oacp_proc *proc,
struct net_buf_simple *resp_param)
{
switch (proc->type) {
case BT_GATT_OTS_OACP_PROC_READ:
@ -290,7 +352,10 @@ static enum bt_gatt_ots_oacp_res_code oacp_proc_validate(
case BT_GATT_OTS_OACP_PROC_DELETE:
return oacp_delete_proc_validate(conn, ots, proc);
#endif
#if defined(CONFIG_BT_OTS_OACP_CHECKSUM_SUPPORT)
case BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC:
return oacp_checksum_proc_validate(conn, ots, proc, resp_param);
#endif
case BT_GATT_OTS_OACP_PROC_EXECUTE:
case BT_GATT_OTS_OACP_PROC_ABORT:
default:
@ -581,8 +646,9 @@ static void oacp_ind_cb(struct bt_conn *conn,
}
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)
struct bt_gatt_ots_oacp_proc oacp_proc,
enum bt_gatt_ots_oacp_res_code oacp_status,
struct net_buf_simple *resp_param)
{
uint8_t oacp_res[OACP_RES_MAX_SIZE];
uint16_t oacp_res_len = 0;
@ -590,9 +656,14 @@ static int oacp_ind_send(const struct bt_gatt_attr *oacp_attr,
/* 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_proc.type;
oacp_res[oacp_res_len++] = oacp_status;
if (oacp_proc.type == BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC) {
sys_put_le32(net_buf_simple_pull_le32(resp_param), (oacp_res + oacp_res_len));
oacp_res_len += sizeof(uint32_t);
}
/* 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));
@ -613,8 +684,9 @@ ssize_t bt_gatt_ots_oacp_write(struct bt_conn *conn,
{
enum bt_gatt_ots_oacp_res_code oacp_status;
int decode_status;
struct bt_gatt_ots_oacp_proc oacp_proc;
struct bt_gatt_ots_oacp_proc oacp_proc = {0};
struct bt_ots *ots = (struct bt_ots *) attr->user_data;
struct net_buf_simple resp_param;
LOG_DBG("Object Action Control Point GATT Write Operation");
@ -631,7 +703,7 @@ ssize_t bt_gatt_ots_oacp_write(struct bt_conn *conn,
decode_status = oacp_command_decode(buf, len, &oacp_proc);
switch (decode_status) {
case 0:
oacp_status = oacp_proc_validate(conn, ots, &oacp_proc);
oacp_status = oacp_proc_validate(conn, ots, &oacp_proc, &resp_param);
if (oacp_status != BT_GATT_OTS_OACP_RES_SUCCESS) {
LOG_WRN("OACP Write error status: 0x%02X", oacp_status);
}
@ -652,7 +724,7 @@ ssize_t bt_gatt_ots_oacp_write(struct bt_conn *conn,
return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
}
oacp_ind_send(attr, oacp_proc.type, oacp_status);
oacp_ind_send(attr, oacp_proc, oacp_status, &resp_param);
return len;
}