Commit 1e6f36cc authored by Pirun Lee's avatar Pirun Lee Committed by Carles Cufi
Browse files

Bluetooth: OTS - Add Calculate Checksum support



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

Signed-off-by: default avatarPirun Lee <pirun.lee@nordicsemi.no>
parent 4858f8d6
Loading
Loading
Loading
Loading
+68 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ extern "C" {
#include <zephyr/sys/byteorder.h>
#include <sys/types.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/crc.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
@@ -725,6 +726,24 @@ struct bt_ots_cb {
	 */
	void (*obj_name_written)(struct bt_ots *ots, struct bt_conn *conn,
				 uint64_t id, const char *cur_name, const char *new_name);

	/** @brief Object Calculate checksum callback
	 *
	 *  This callback is called when the OACP Calculate Checksum procedure is performed.
	 *  Because object data is opaque to OTS, the application is the only one who
	 *  knows where data is and should return pointer of actual object data.
	 *
	 *  @param[in]	ots      OTS instance.
	 *  @param[in]	conn     The connection that wrote object.
	 *  @param[in]	id       Object ID.
	 *  @param[in]	offset   The first octet of the object contents need to be calculated.
	 *  @param[in]	len      The length number of octets object name.
	 *  @param[out] data     Pointer of actual object data.
	 *
	 *  @return 0 to accept, or any negative value to reject.
	 */
	int (*obj_cal_checksum)(struct bt_ots *ots, struct bt_conn *conn, uint64_t id,
				off_t offset, size_t len, void **data);
};

/** @brief Descriptor for OTS initialization. */
@@ -888,6 +907,20 @@ struct bt_ots_client_cb {
	 */
	void (*obj_data_written)(struct bt_ots_client *ots_inst,
				 struct bt_conn *conn, size_t len);

	/** @brief Callback function when checksum indication is received.
	 *
	 *  Called when the oacp_ind_handler received response of
	 *  OP BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC.
	 *
	 *  @param ots_inst          Pointer to the OTC instance.
	 *  @param conn              The connection to the peer device.
	 *  @param err               Error code (bt_gatt_ots_oacp_res_code).
	 *  @param checksum          Checksum if error code is BT_GATT_OTS_OACP_RES_SUCCESS,
	 *                           otherwise 0.
	 */
	void (*obj_checksum_calculated)(struct bt_ots_client *ots_inst, struct bt_conn *conn,
					int err, uint32_t checksum);
};

/** @brief Register an Object Transfer Service Instance.
@@ -1011,7 +1044,7 @@ int bt_ots_client_read_object_data(struct bt_ots_client *otc_inst,

/** @brief Write the data of the current selected object.
 *
 *  This will trigger an OACP write operation for the current size of the object
 *  This will trigger an OACP write operation for the current object
 *  with a specified offset and then expect transferring the content via the L2CAP CoC.
 *
 *  The length of the data written to object is returned in the obj_data_written() callback.
@@ -1029,6 +1062,24 @@ int bt_ots_client_write_object_data(struct bt_ots_client *otc_inst, struct bt_co
				    const void *buf, size_t len, off_t offset,
				    enum bt_ots_oacp_write_op_mode mode);

/** @brief Get the checksum of the current selected object.
 *
 *  This will trigger an OACP calculate checksum operation for the current object
 *  with a specified offset and length.
 *
 *  The checksum goes to OACP IND and obj_checksum_calculated() callback.
 *
 *  @param otc_inst     Pointer to the OTC instance.
 *  @param conn         Pointer to the connection object.
 *  @param offset       Offset to calculate, usually 0.
 *  @param len          Len of data to calculate checksum for. May be less than the current object's
 *                      size, but shall not be larger.
 *
 *  @return int         0 if success, ERRNO on failure.
 */
int bt_ots_client_get_object_checksum(struct bt_ots_client *otc_inst, struct bt_conn *conn,
				      off_t offset, size_t len);

/** @brief Directory listing object metadata callback
 *
 * If a directory listing is decoded using bt_ots_client_decode_dirlisting(),
@@ -1082,6 +1133,22 @@ static inline int bt_ots_obj_id_to_str(uint64_t obj_id, char *str, size_t len)
void bt_ots_metadata_display(struct bt_ots_obj_metadata *metadata,
			     uint16_t count);

/**
 * @brief  Generate IEEE conform CRC32 checksum.
 *
 * To abstract IEEE implementation to service layer.
 *
 * @param  data        Pointer to data on which the CRC should be calculated.
 * @param  len          Data length.
 *
 * @return CRC32 value.
 *
 */
static inline uint32_t bt_ots_client_calc_checksum(const uint8_t *data, size_t len)
{
	return crc32_ieee(data, len);
}

#ifdef __cplusplus
}
#endif
+3 −0
Original line number Diff line number Diff line
@@ -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"

+11 −0
Original line number Diff line number Diff line
@@ -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");
+121 −6
Original line number Diff line number Diff line
@@ -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)
{
+82 −10
Original line number Diff line number Diff line
@@ -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;
}