bluetooth: ots: Add object name write capability

Add the ability to perform a write on the object name GATT
Characteristic with a notification callback to the application
that the name has been written.

In order for this operation to work the memory backing the
object name must be modifiable. To prevent forcing the user
to always allocate 120 bytes for the name, the maximum name
length is changed from a define to a configuration parameter.

Signed-off-by: Abe Kohandel <abe.kohandel@gmail.com>
This commit is contained in:
Abe Kohandel 2021-06-06 10:15:56 -07:00 committed by Anas Nashif
commit d6314fa456
6 changed files with 134 additions and 22 deletions

View file

@ -33,9 +33,6 @@ extern "C" {
/** @brief Length of OTS object ID string (in bytes). */
#define BT_OTS_OBJ_ID_STR_LEN 15
/** @brief Maximum object name length */
#define BT_OTS_OBJ_MAX_NAME_LEN 120
/** @brief Type of an OTS object. */
struct bt_ots_obj_type {
union {
@ -548,6 +545,20 @@ struct bt_ots_cb {
uint32_t (*obj_read)(struct bt_ots *ots, struct bt_conn *conn,
uint64_t id, uint8_t **data, uint32_t len,
uint32_t offset);
/** @brief Object name written callback
*
* This callback is called when the object name is written.
* This is a notification to the application that the object name
* has been updated by the OTS service implementation.
*
* @param ots OTS instance.
* @param conn The connection that wrote object name.
* @param id Object ID.
* @param name Object name.
*/
void (*obj_name_written)(struct bt_ots *ots, struct bt_conn *conn,
uint64_t id, const char *name);
};
/** @brief Descriptor for OTS initialization. */

View file

@ -3,5 +3,6 @@ CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="Zephyr OTS"
CONFIG_BT_OTS=y
CONFIG_BT_OTS_DIR_LIST_OBJ=y
CONFIG_BT_OTS_OBJ_NAME_WRITE_SUPPORT=y
CONFIG_LOG=y

View file

@ -31,7 +31,10 @@ static const struct bt_data sd[] = {
BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_OTS_VAL)),
};
static uint8_t objects[OBJ_POOL_SIZE][OBJ_MAX_SIZE];
static struct {
uint8_t data[OBJ_MAX_SIZE];
char name[CONFIG_BT_OTS_OBJ_MAX_NAME_LEN + 1];
} objects[OBJ_POOL_SIZE];
static uint32_t obj_cnt;
static void connected(struct bt_conn *conn, uint8_t err)
@ -118,7 +121,7 @@ static uint32_t ots_obj_read(struct bt_ots *ots, struct bt_conn *conn,
return 0;
}
*data = &objects[obj_index][offset];
*data = &objects[obj_index].data[offset];
/* Send even-indexed objects in 20 byte packets
* to demonstrate fragmented transmission.
@ -134,11 +137,21 @@ static uint32_t ots_obj_read(struct bt_ots *ots, struct bt_conn *conn,
return len;
}
void ots_obj_name_written(struct bt_ots *ots, struct bt_conn *conn, uint64_t id, const char *name)
{
char id_str[BT_OTS_OBJ_ID_STR_LEN];
bt_ots_obj_id_to_str(id, id_str, sizeof(id_str));
printk("Name for object with %s ID has been written\n", id_str);
}
static struct bt_ots_cb ots_callbacks = {
.obj_created = ots_obj_created,
.obj_deleted = ots_obj_deleted,
.obj_selected = ots_obj_selected,
.obj_read = ots_obj_read,
.obj_name_written = ots_obj_name_written,
};
static int ots_init(void)
@ -147,6 +160,8 @@ static int ots_init(void)
struct bt_ots *ots;
struct bt_ots_init ots_init;
struct bt_ots_obj_metadata obj_init;
const char * const first_object_name = "first_object.txt";
const char * const second_object_name = "second_object.gif";
ots = bt_ots_free_instance_get();
if (!ots) {
@ -168,16 +183,20 @@ static int ots_init(void)
}
/* Prepare first object demo data and add it to the instance. */
for (uint32_t i = 0; i < sizeof(objects[0]); i++) {
objects[0][i] = i + 1;
for (uint32_t i = 0; i < sizeof(objects[0].data); i++) {
objects[0].data[i] = i + 1;
}
memset(&obj_init, 0, sizeof(obj_init));
obj_init.name = "first_object.txt";
__ASSERT(strlen(first_object_name) <= CONFIG_BT_OTS_OBJ_MAX_NAME_LEN,
"Object name length is larger than the allowed maximum of %u",
CONFIG_BT_OTS_OBJ_MAX_NAME_LEN);
strcpy(objects[0].name, first_object_name);
obj_init.name = objects[0].name;
obj_init.type.uuid.type = BT_UUID_TYPE_16;
obj_init.type.uuid_16.val = BT_UUID_OTS_TYPE_UNSPECIFIED_VAL;
obj_init.size.cur = sizeof(objects[0]);
obj_init.size.alloc = sizeof(objects[0]);
obj_init.size.cur = sizeof(objects[0].data);
obj_init.size.alloc = sizeof(objects[0].data);
BT_OTS_OBJ_SET_PROP_READ(obj_init.props);
err = bt_ots_obj_add(ots, &obj_init);
@ -187,16 +206,20 @@ static int ots_init(void)
}
/* Prepare second object demo data and add it to the instance. */
for (uint32_t i = 0; i < sizeof(objects[1]); i++) {
objects[1][i] = i * 2;
for (uint32_t i = 0; i < sizeof(objects[1].data); i++) {
objects[1].data[i] = i * 2;
}
memset(&obj_init, 0, sizeof(obj_init));
obj_init.name = "second_object.gif";
__ASSERT(strlen(second_object_name) <= CONFIG_BT_OTS_OBJ_MAX_NAME_LEN,
"Object name length is larger than the allowed maximum of %u",
CONFIG_BT_OTS_OBJ_MAX_NAME_LEN);
strcpy(objects[1].name, second_object_name);
obj_init.name = objects[1].name;
obj_init.type.uuid.type = BT_UUID_TYPE_16;
obj_init.type.uuid_16.val = BT_UUID_OTS_TYPE_UNSPECIFIED_VAL;
obj_init.size.cur = sizeof(objects[1]);
obj_init.size.alloc = sizeof(objects[1]);
obj_init.size.cur = sizeof(objects[1].data);
obj_init.size.alloc = sizeof(objects[1].data);
BT_OTS_OBJ_SET_PROP_READ(obj_init.props);
err = bt_ots_obj_add(ots, &obj_init);

View file

@ -59,6 +59,14 @@ config BT_OTS_L2CAP_CHAN_RX_MTU
default BT_BUF_ACL_RX_SIZE
range 21 BT_BUF_ACL_RX_SIZE
config BT_OTS_OBJ_MAX_NAME_LEN
int "Maximum object name length"
default 120
range 1 120
config BT_OTS_OBJ_NAME_WRITE_SUPPORT
bool "Support object name write"
module = BT_OTS
module-str = BT_OTS
source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config"

View file

@ -78,6 +78,65 @@ static ssize_t ots_obj_name_read(struct bt_conn *conn,
strlen(ots->cur_obj->metadata.name));
}
#if defined(CONFIG_BT_OTS_OBJ_NAME_WRITE_SUPPORT)
ssize_t ots_obj_name_write(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
const void *buf, uint16_t len,
uint16_t offset, uint8_t flags)
{
struct bt_ots *ots = (struct bt_ots *) attr->user_data;
struct bt_gatt_ots_object *obj = NULL;
int rc = 0;
char name[CONFIG_BT_OTS_OBJ_MAX_NAME_LEN + 1];
LOG_DBG("OTS Object Name GATT Write Operation");
if (!ots->cur_obj) {
LOG_DBG("No Current Object selected in OTS!");
return BT_GATT_ERR(BT_GATT_OTS_OBJECT_NOT_SELECTED);
}
if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ) &&
ots->cur_obj->id == OTS_OBJ_ID_DIR_LIST) {
LOG_DBG("Rejecting name write for the directory list object.");
return BT_GATT_ERR(BT_GATT_OTS_WRITE_REQUEST_REJECTED);
}
if (offset > 0) {
LOG_DBG("Rejecting a long write, offset must be 0!");
return BT_GATT_ERR(BT_GATT_OTS_WRITE_REQUEST_REJECTED);
}
if (len > CONFIG_BT_OTS_OBJ_MAX_NAME_LEN) {
LOG_DBG("Object name is too long!");
return BT_GATT_ERR(BT_GATT_OTS_WRITE_REQUEST_REJECTED);
}
/* Construct a temporary name for duplication detection */
memcpy(name, buf, len);
name[len] = '\0';
rc = bt_gatt_ots_obj_manager_first_obj_get(ots->obj_manager, &obj);
while (rc == 0) {
if (obj != ots->cur_obj && strcmp(name, obj->metadata.name) == 0) {
LOG_DBG("Object name is duplicated!");
return BT_GATT_ERR(BT_GATT_OTS_OBJECT_NAME_ALREADY_EXISTS);
}
rc = bt_gatt_ots_obj_manager_next_obj_get(ots->obj_manager, obj, &obj);
}
/* Update real object name after no duplicate detected */
strcpy(ots->cur_obj->metadata.name, name);
if (ots->cb->obj_name_written) {
ots->cb->obj_name_written(ots, conn, ots->cur_obj->id,
ots->cur_obj->metadata.name);
}
return len;
}
#endif
static ssize_t ots_obj_type_read(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
@ -179,7 +238,7 @@ int bt_ots_obj_add(struct bt_ots *ots,
name_len = strlen(obj_init->name);
CHECKIF(name_len == 0 || name_len > BT_OTS_OBJ_MAX_NAME_LEN) {
CHECKIF(name_len == 0 || name_len > CONFIG_BT_OTS_OBJ_MAX_NAME_LEN) {
LOG_DBG("Invalid name length %zu", name_len);
return -EINVAL;
}
@ -322,14 +381,24 @@ int bt_ots_init(struct bt_ots *ots,
#define BT_GATT_OTS_SERVICE BT_GATT_PRIMARY_SERVICE
#endif
#if defined(CONFIG_BT_OTS_OBJ_NAME_WRITE_SUPPORT)
#define BT_OTS_OBJ_NAME_GATT_CHRC (BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE)
#define BT_OTS_OBJ_NAME_GATT_PERM (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)
#define BT_OTS_OBJ_NAME_GATT_WRITE (ots_obj_name_write)
#else
#define BT_OTS_OBJ_NAME_GATT_CHRC (BT_GATT_CHRC_READ)
#define BT_OTS_OBJ_NAME_GATT_PERM (BT_GATT_PERM_READ)
#define BT_OTS_OBJ_NAME_GATT_WRITE (NULL)
#endif
#define BT_GATT_OTS_ATTRS(_ots) { \
BT_GATT_OTS_SERVICE(BT_UUID_OTS), \
BT_GATT_CHARACTERISTIC(BT_UUID_OTS_FEATURE, \
BT_GATT_CHRC_READ, BT_GATT_PERM_READ, \
ots_feature_read, NULL, &_ots), \
BT_GATT_CHARACTERISTIC(BT_UUID_OTS_NAME, \
BT_GATT_CHRC_READ, BT_GATT_PERM_READ, \
ots_obj_name_read, NULL, &_ots), \
BT_OTS_OBJ_NAME_GATT_CHRC, BT_OTS_OBJ_NAME_GATT_PERM, \
ots_obj_name_read, BT_OTS_OBJ_NAME_GATT_WRITE, &_ots), \
BT_GATT_CHARACTERISTIC(BT_UUID_OTS_TYPE, \
BT_GATT_CHRC_READ, BT_GATT_PERM_READ, \
ots_obj_type_read, NULL, &_ots), \

View file

@ -44,7 +44,7 @@ static void dir_list_object_encode(struct bt_gatt_ots_object *obj,
/* Name length */
obj_name_len = strlen(obj->metadata.name);
__ASSERT(obj_name_len > 0 && obj_name_len <= BT_OTS_OBJ_MAX_NAME_LEN,
__ASSERT(obj_name_len > 0 && obj_name_len <= CONFIG_BT_OTS_OBJ_MAX_NAME_LEN,
"Dir list object len is incorrect %zu", len);
net_buf_simple_add_u8(net_buf, obj_name_len);
@ -220,9 +220,9 @@ void bt_ots_dir_list_init(struct bt_ots_dir_list **dir_list, void *obj_manager)
__ASSERT(*dir_list, "Could not assign Directory Listing Object");
__ASSERT(strlen(dir_list_obj_name) <= BT_OTS_OBJ_MAX_NAME_LEN,
"BT_OTS_DIR_LIST_OBJ_NAME shall be less than %u octets",
BT_OTS_OBJ_MAX_NAME_LEN);
__ASSERT(strlen(dir_list_obj_name) <= CONFIG_BT_OTS_OBJ_MAX_NAME_LEN,
"BT_OTS_DIR_LIST_OBJ_NAME shall be less than or equal to %u octets",
CONFIG_BT_OTS_OBJ_MAX_NAME_LEN);
err = bt_gatt_ots_obj_manager_obj_add(obj_manager, &dir_list_obj);