bluetooth: ots: Add Create and Delete procedures

Add the ability to perform an OTS Create or Delete Procedure.

Signed-off-by: Abe Kohandel <abe.kohandel@gmail.com>
This commit is contained in:
Abe Kohandel 2021-11-22 06:32:47 -08:00 committed by Johan Hedberg
commit a6f932a194
9 changed files with 572 additions and 163 deletions

View file

@ -215,21 +215,6 @@ struct bt_ots_obj_size {
uint32_t alloc; uint32_t alloc;
} __packed; } __packed;
/** @brief Descriptor for OTS object initialization. */
struct bt_ots_obj_metadata {
/* Object Name */
char *name;
/* Object Type */
struct bt_ots_obj_type type;
/* Object Size */
struct bt_ots_obj_size size;
/* Object Properties */
uint32_t props;
};
/** @brief Object Action Control Point Feature bits. */ /** @brief Object Action Control Point Feature bits. */
enum { enum {
/** Bit 0 OACP Create Op Code Supported */ /** Bit 0 OACP Create Op Code Supported */
@ -486,6 +471,48 @@ struct bt_ots_feat {
/** @brief Opaque OTS instance. */ /** @brief Opaque OTS instance. */
struct bt_ots; struct bt_ots;
/** @brief Descriptor for OTS object addition */
struct bt_ots_obj_add_param {
/** @brief Object size to allocate */
uint32_t size;
/** @brief Object type */
struct bt_ots_obj_type type;
};
/** @brief Descriptor for OTS created object.
*
* Descriptor for OTS object created by the application. This descriptor is
* returned by @ref bt_ots_cb.obj_created callback which contains further
* documentation on distinguishing between server and client object creation.
*/
struct bt_ots_obj_created_desc {
/** @brief Object name
*
* The object name as a NULL terminated string.
*
* When the server creates a new object the name
* shall be > 0 and <= BT_OTS_OBJ_MAX_NAME_LEN
* When the client creates a new object the name
* shall be an empty string
*/
char *name;
/** @brief Object size
*
* @ref bt_ots_obj_size.alloc shall be >= @ref bt_ots_obj_add_param.size
*
* When the server creates a new object @ref bt_ots_obj_size.cur
* shall be <= @ref bt_ots_obj_add_param.size
* When the client creates a new object @ref bt_ots_obj_size.cur
* shall be 0
*/
struct bt_ots_obj_size size;
/** @brief Object properties */
uint32_t props;
};
/** @brief OTS callback structure. */ /** @brief OTS callback structure. */
struct bt_ots_cb { struct bt_ots_cb {
/** @brief Object created callback /** @brief Object created callback
@ -498,18 +525,21 @@ struct bt_ots_cb {
* *
* @param ots OTS instance. * @param ots OTS instance.
* @param conn The connection that is requesting object creation or * @param conn The connection that is requesting object creation or
* NULL if object is created by the following function: * NULL if object is created by bt_ots_obj_add().
* bt_ots_obj_add().
* @param id Object ID. * @param id Object ID.
* @param init Object initialization metadata. * @param add_param Object creation requested parameters.
* @param created_desc Created object descriptor that shall be filled by the
* receiver of this callback.
* *
* @return 0 in case of success or negative value in case of error. * @return 0 in case of success or negative value in case of error.
* Possible return values: * @return -ENOTSUP if object type is not supported
* -ENOMEM if no available space for new object. * @return -ENOMEM if no available space for new object.
* @return -EINVAL if an invalid parameter is provided
* @return other negative values are treated as a generic operation failure
*/ */
int (*obj_created)(struct bt_ots *ots, struct bt_conn *conn, int (*obj_created)(struct bt_ots *ots, struct bt_conn *conn, uint64_t id,
uint64_t id, const struct bt_ots_obj_add_param *add_param,
const struct bt_ots_obj_metadata *init); struct bt_ots_obj_created_desc *created_desc);
/** @brief Object deleted callback /** @brief Object deleted callback
* *
@ -521,8 +551,18 @@ struct bt_ots_cb {
* @param conn The connection that deleted the object or NULL if * @param conn The connection that deleted the object or NULL if
* this request came from the server. * this request came from the server.
* @param id Object ID. * @param id Object ID.
*
* @retval When an error is indicated by using a negative value, the
* object delete procedure is aborted and a corresponding failed
* status is returned to the client.
* @return 0 in case of success.
* @return -EBUSY if the object is locked. This is generally not expected
* to be returned by the application as the OTS layer tracks object
* accesses. An object locked status is returned to the client.
* @return Other negative values in case of error. A generic operation
* failed status is returned to the client.
*/ */
void (*obj_deleted)(struct bt_ots *ots, struct bt_conn *conn, int (*obj_deleted)(struct bt_ots *ots, struct bt_conn *conn,
uint64_t id); uint64_t id);
/** @brief Object selected callback /** @brief Object selected callback
@ -621,11 +661,12 @@ struct bt_ots_init {
* to notify the user about a new object ID. * to notify the user about a new object ID.
* *
* @param ots OTS instance. * @param ots OTS instance.
* @param obj_init Meta data of the object. * @param param Object addition parameters.
* *
* @return 0 in case of success or negative value in case of error. * @return ID of created object in case of success.
* @return negative value in case of error.
*/ */
int bt_ots_obj_add(struct bt_ots *ots, struct bt_ots_obj_metadata *obj_init); int bt_ots_obj_add(struct bt_ots *ots, const struct bt_ots_obj_add_param *param);
/** @brief Delete an object from the OTS instance. /** @brief Delete an object from the OTS instance.
* *

View file

@ -6,6 +6,8 @@ CONFIG_BT_OTS_DIR_LIST_OBJ=y
CONFIG_BT_OTS_OBJ_NAME_WRITE_SUPPORT=y CONFIG_BT_OTS_OBJ_NAME_WRITE_SUPPORT=y
CONFIG_BT_OTS_OACP_WRITE_SUPPORT=y CONFIG_BT_OTS_OACP_WRITE_SUPPORT=y
CONFIG_BT_OTS_OACP_PATCH_SUPPORT=y CONFIG_BT_OTS_OACP_PATCH_SUPPORT=y
CONFIG_BT_OTS_OACP_CREATE_SUPPORT=y
CONFIG_BT_OTS_OACP_DELETE_SUPPORT=y
CONFIG_LOG=y CONFIG_LOG=y
CONFIG_ASSERT=y CONFIG_ASSERT=y

View file

@ -19,7 +19,7 @@
#define DEVICE_NAME CONFIG_BT_DEVICE_NAME #define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
#define OBJ_POOL_SIZE 2 #define OBJ_POOL_SIZE CONFIG_BT_OTS_MAX_OBJ_CNT
#define OBJ_MAX_SIZE 100 #define OBJ_MAX_SIZE 100
static const struct bt_data ad[] = { static const struct bt_data ad[] = {
@ -37,6 +37,14 @@ static struct {
} objects[OBJ_POOL_SIZE]; } objects[OBJ_POOL_SIZE];
static uint32_t obj_cnt; static uint32_t obj_cnt;
struct object_creation_data {
struct bt_ots_obj_size size;
char *name;
uint32_t props;
};
static struct object_creation_data *object_being_created;
static void connected(struct bt_conn *conn, uint8_t err) static void connected(struct bt_conn *conn, uint8_t err)
{ {
if (err) { if (err) {
@ -57,11 +65,12 @@ BT_CONN_CB_DEFINE(conn_callbacks) = {
.disconnected = disconnected, .disconnected = disconnected,
}; };
static int ots_obj_created(struct bt_ots *ots, struct bt_conn *conn, static int ots_obj_created(struct bt_ots *ots, struct bt_conn *conn, uint64_t id,
uint64_t id, const struct bt_ots_obj_add_param *add_param,
const struct bt_ots_obj_metadata *init) struct bt_ots_obj_created_desc *created_desc)
{ {
char id_str[BT_OTS_OBJ_ID_STR_LEN]; char id_str[BT_OTS_OBJ_ID_STR_LEN];
uint64_t index;
bt_ots_obj_id_to_str(id, id_str, sizeof(id_str)); bt_ots_obj_id_to_str(id, id_str, sizeof(id_str));
@ -71,19 +80,35 @@ static int ots_obj_created(struct bt_ots *ots, struct bt_conn *conn,
return -ENOMEM; return -ENOMEM;
} }
if (init->size.alloc > OBJ_MAX_SIZE) { if (add_param->size > OBJ_MAX_SIZE) {
printk("Object pool item is too small for Object with %s ID\n", printk("Object pool item is too small for Object with %s ID\n",
id_str); id_str);
return -ENOMEM; return -ENOMEM;
} }
if (object_being_created) {
created_desc->name = object_being_created->name;
created_desc->size = object_being_created->size;
created_desc->props = object_being_created->props;
} else {
index = id - BT_OTS_OBJ_ID_MIN;
objects[index].name[0] = '\0';
created_desc->name = objects[index].name;
created_desc->size.alloc = OBJ_MAX_SIZE;
BT_OTS_OBJ_SET_PROP_READ(created_desc->props);
BT_OTS_OBJ_SET_PROP_WRITE(created_desc->props);
BT_OTS_OBJ_SET_PROP_PATCH(created_desc->props);
BT_OTS_OBJ_SET_PROP_DELETE(created_desc->props);
}
printk("Object with %s ID has been created\n", id_str); printk("Object with %s ID has been created\n", id_str);
obj_cnt++; obj_cnt++;
return 0; return 0;
} }
static void ots_obj_deleted(struct bt_ots *ots, struct bt_conn *conn, static int ots_obj_deleted(struct bt_ots *ots, struct bt_conn *conn,
uint64_t id) uint64_t id)
{ {
char id_str[BT_OTS_OBJ_ID_STR_LEN]; char id_str[BT_OTS_OBJ_ID_STR_LEN];
@ -93,6 +118,8 @@ static void ots_obj_deleted(struct bt_ots *ots, struct bt_conn *conn,
printk("Object with %s ID has been deleted\n", id_str); printk("Object with %s ID has been deleted\n", id_str);
obj_cnt--; obj_cnt--;
return 0;
} }
static void ots_obj_selected(struct bt_ots *ots, struct bt_conn *conn, static void ots_obj_selected(struct bt_ots *ots, struct bt_conn *conn,
@ -150,7 +177,7 @@ static ssize_t ots_obj_write(struct bt_ots *ots, struct bt_conn *conn,
"Offset = %lu, Length = %zu, Remaining= %zu\n", "Offset = %lu, Length = %zu, Remaining= %zu\n",
id_str, (long)offset, len, rem); id_str, (long)offset, len, rem);
memcpy(&objects[obj_index].data[offset], data, len); (void)memcpy(&objects[obj_index].data[offset], data, len);
return len; return len;
} }
@ -177,8 +204,9 @@ static int ots_init(void)
{ {
int err; int err;
struct bt_ots *ots; struct bt_ots *ots;
struct object_creation_data obj_data;
struct bt_ots_init ots_init; struct bt_ots_init ots_init;
struct bt_ots_obj_metadata obj_init; struct bt_ots_obj_add_param param;
const char * const first_object_name = "first_object.txt"; const char * const first_object_name = "first_object.txt";
const char * const second_object_name = "second_object.gif"; const char * const second_object_name = "second_object.gif";
uint32_t cur_size; uint32_t cur_size;
@ -191,9 +219,11 @@ static int ots_init(void)
} }
/* Configure OTS initialization. */ /* Configure OTS initialization. */
memset(&ots_init, 0, sizeof(ots_init)); (void)memset(&ots_init, 0, sizeof(ots_init));
BT_OTS_OACP_SET_FEAT_READ(ots_init.features.oacp); BT_OTS_OACP_SET_FEAT_READ(ots_init.features.oacp);
BT_OTS_OACP_SET_FEAT_WRITE(ots_init.features.oacp); BT_OTS_OACP_SET_FEAT_WRITE(ots_init.features.oacp);
BT_OTS_OACP_SET_FEAT_CREATE(ots_init.features.oacp);
BT_OTS_OACP_SET_FEAT_DELETE(ots_init.features.oacp);
BT_OTS_OACP_SET_FEAT_PATCH(ots_init.features.oacp); BT_OTS_OACP_SET_FEAT_PATCH(ots_init.features.oacp);
BT_OTS_OLCP_SET_FEAT_GO_TO(ots_init.features.olcp); BT_OTS_OLCP_SET_FEAT_GO_TO(ots_init.features.olcp);
ots_init.cb = &ots_callbacks; ots_init.cb = &ots_callbacks;
@ -212,22 +242,25 @@ static int ots_init(void)
objects[0].data[i] = i + 1; objects[0].data[i] = i + 1;
} }
memset(&obj_init, 0, sizeof(obj_init)); (void)memset(&obj_data, 0, sizeof(obj_data));
__ASSERT(strlen(first_object_name) <= CONFIG_BT_OTS_OBJ_MAX_NAME_LEN, __ASSERT(strlen(first_object_name) <= CONFIG_BT_OTS_OBJ_MAX_NAME_LEN,
"Object name length is larger than the allowed maximum of %u", "Object name length is larger than the allowed maximum of %u",
CONFIG_BT_OTS_OBJ_MAX_NAME_LEN); CONFIG_BT_OTS_OBJ_MAX_NAME_LEN);
strcpy(objects[0].name, first_object_name); (void)strcpy(objects[0].name, first_object_name);
obj_init.name = objects[0].name; obj_data.name = objects[0].name;
obj_init.type.uuid.type = BT_UUID_TYPE_16; obj_data.size.cur = cur_size;
obj_init.type.uuid_16.val = BT_UUID_OTS_TYPE_UNSPECIFIED_VAL; obj_data.size.alloc = alloc_size;
obj_init.size.cur = cur_size; BT_OTS_OBJ_SET_PROP_READ(obj_data.props);
obj_init.size.alloc = alloc_size; BT_OTS_OBJ_SET_PROP_WRITE(obj_data.props);
BT_OTS_OBJ_SET_PROP_READ(obj_init.props); BT_OTS_OBJ_SET_PROP_PATCH(obj_data.props);
BT_OTS_OBJ_SET_PROP_WRITE(obj_init.props); object_being_created = &obj_data;
BT_OTS_OBJ_SET_PROP_PATCH(obj_init.props);
err = bt_ots_obj_add(ots, &obj_init); param.size = alloc_size;
if (err) { param.type.uuid.type = BT_UUID_TYPE_16;
param.type.uuid_16.val = BT_UUID_OTS_TYPE_UNSPECIFIED_VAL;
err = bt_ots_obj_add(ots, &param);
object_being_created = NULL;
if (err < 0) {
printk("Failed to add an object to OTS (err: %d)\n", err); printk("Failed to add an object to OTS (err: %d)\n", err);
return err; return err;
} }
@ -239,20 +272,23 @@ static int ots_init(void)
objects[1].data[i] = i * 2; objects[1].data[i] = i * 2;
} }
memset(&obj_init, 0, sizeof(obj_init)); (void)memset(&obj_data, 0, sizeof(obj_data));
__ASSERT(strlen(second_object_name) <= CONFIG_BT_OTS_OBJ_MAX_NAME_LEN, __ASSERT(strlen(second_object_name) <= CONFIG_BT_OTS_OBJ_MAX_NAME_LEN,
"Object name length is larger than the allowed maximum of %u", "Object name length is larger than the allowed maximum of %u",
CONFIG_BT_OTS_OBJ_MAX_NAME_LEN); CONFIG_BT_OTS_OBJ_MAX_NAME_LEN);
strcpy(objects[1].name, second_object_name); (void)strcpy(objects[1].name, second_object_name);
obj_init.name = objects[1].name; obj_data.name = objects[1].name;
obj_init.type.uuid.type = BT_UUID_TYPE_16; obj_data.size.cur = cur_size;
obj_init.type.uuid_16.val = BT_UUID_OTS_TYPE_UNSPECIFIED_VAL; obj_data.size.alloc = alloc_size;
obj_init.size.cur = cur_size; BT_OTS_OBJ_SET_PROP_READ(obj_data.props);
obj_init.size.alloc = alloc_size; object_being_created = &obj_data;
BT_OTS_OBJ_SET_PROP_READ(obj_init.props);
err = bt_ots_obj_add(ots, &obj_init); param.size = alloc_size;
if (err) { param.type.uuid.type = BT_UUID_TYPE_16;
param.type.uuid_16.val = BT_UUID_OTS_TYPE_UNSPECIFIED_VAL;
err = bt_ots_obj_add(ots, &param);
object_being_created = NULL;
if (err < 0) {
printk("Failed to add an object to OTS (err: %d)\n", err); printk("Failed to add an object to OTS (err: %d)\n", err);
return err; return err;
} }

View file

@ -261,12 +261,22 @@ enum mpl_objects {
/* And, except for the icon object, all objects can be created dynamically. */ /* And, except for the icon object, all objects can be created dynamically. */
/* So a single buffer to hold object content is sufficient. */ /* So a single buffer to hold object content is sufficient. */
struct obj_t { struct obj_t {
uint64_t selected_id; /* ID of the currently selected object*/ /* ID of the currently selected object*/
uint64_t selected_id;
bool busy; bool busy;
uint8_t add_type; /* Type of object being added, e.g. MPL_OBJ_ICON */
/* Type of object being added, e.g. MPL_OBJ_ICON */
uint8_t add_type;
/* Descriptor of object being added */
struct bt_ots_obj_created_desc *desc;
union { union {
struct mpl_track *add_track; /* Pointer to track being added */ /* Pointer to track being added */
struct mpl_group *add_group; /* Pointer to group being added */ struct mpl_track *add_track;
/* Pointer to group being added */
struct mpl_group *add_group;
}; };
struct net_buf_simple *content; struct net_buf_simple *content;
}; };
@ -440,7 +450,8 @@ static uint32_t setup_group_object(struct mpl_group *group)
static int add_icon_object(struct mpl_mediaplayer *pl) static int add_icon_object(struct mpl_mediaplayer *pl)
{ {
int ret; int ret;
struct bt_ots_obj_metadata icon = {0}; struct bt_ots_obj_add_param add_param = {};
struct bt_ots_obj_created_desc created_desc = {};
struct bt_uuid *icon_type = BT_UUID_OTS_TYPE_MPL_ICON; struct bt_uuid *icon_type = BT_UUID_OTS_TYPE_MPL_ICON;
static char *icon_name = "Icon"; static char *icon_name = "Icon";
@ -452,14 +463,17 @@ static int add_icon_object(struct mpl_mediaplayer *pl)
} }
obj.busy = true; obj.busy = true;
obj.add_type = MPL_OBJ_ICON; obj.add_type = MPL_OBJ_ICON;
obj.desc = &created_desc;
icon.size.alloc = icon.size.cur = setup_icon_object(); obj.desc->size.alloc = obj.desc->size.cur = setup_icon_object();
icon.name = icon_name; obj.desc->name = icon_name;
icon.type.uuid.type = BT_UUID_TYPE_16; BT_OTS_OBJ_SET_PROP_READ(obj.desc->props);
icon.type.uuid_16.val = BT_UUID_16(icon_type)->val;
BT_OTS_OBJ_SET_PROP_READ(icon.props);
ret = bt_ots_obj_add(bt_mcs_get_ots(), &icon); add_param.size = obj.desc->size.alloc;
add_param.type.uuid.type = BT_UUID_TYPE_16;
add_param.type.uuid_16.val = BT_UUID_16(icon_type)->val;
ret = bt_ots_obj_add(bt_mcs_get_ots(), &add_param);
if (ret) { if (ret) {
BT_WARN("Unable to add icon object, error %d", ret); BT_WARN("Unable to add icon object, error %d", ret);
@ -472,7 +486,8 @@ static int add_icon_object(struct mpl_mediaplayer *pl)
static int add_current_track_segments_object(struct mpl_mediaplayer *pl) static int add_current_track_segments_object(struct mpl_mediaplayer *pl)
{ {
int ret; int ret;
struct bt_ots_obj_metadata segs = {0}; struct bt_ots_obj_add_param add_param = {};
struct bt_ots_obj_created_desc created_desc = {};
struct bt_uuid *segs_type = BT_UUID_OTS_TYPE_TRACK_SEGMENT; struct bt_uuid *segs_type = BT_UUID_OTS_TYPE_TRACK_SEGMENT;
if (obj.busy) { if (obj.busy) {
@ -481,14 +496,17 @@ static int add_current_track_segments_object(struct mpl_mediaplayer *pl)
} }
obj.busy = true; obj.busy = true;
obj.add_type = MPL_OBJ_TRACK_SEGMENTS; obj.add_type = MPL_OBJ_TRACK_SEGMENTS;
obj.desc = &created_desc;
segs.size.alloc = segs.size.cur = setup_segments_object(pl->group->track); obj.desc->size.alloc = obj.desc->size.cur = setup_segments_object(pl->group->track);
segs.name = pl->group->track->title; obj.desc->name = pl->group->track->title;
segs.type.uuid.type = BT_UUID_TYPE_16; BT_OTS_OBJ_SET_PROP_READ(obj.desc->props);
segs.type.uuid_16.val = BT_UUID_16(segs_type)->val;
BT_OTS_OBJ_SET_PROP_READ(segs.props);
ret = bt_ots_obj_add(bt_mcs_get_ots(), &segs); add_param.size = obj.desc->size.alloc;
add_param.type.uuid.type = BT_UUID_TYPE_16;
add_param.type.uuid_16.val = BT_UUID_16(segs_type)->val;
ret = bt_ots_obj_add(bt_mcs_get_ots(), &add_param);
if (ret) { if (ret) {
BT_WARN("Unable to add track segments object: %d", ret); BT_WARN("Unable to add track segments object: %d", ret);
obj.busy = false; obj.busy = false;
@ -499,7 +517,8 @@ static int add_current_track_segments_object(struct mpl_mediaplayer *pl)
/* Add a single track to the OTS */ /* Add a single track to the OTS */
static int add_track_object(struct mpl_track *track) static int add_track_object(struct mpl_track *track)
{ {
struct bt_ots_obj_metadata track_meta = {0}; struct bt_ots_obj_add_param add_param = {};
struct bt_ots_obj_created_desc created_desc = {};
struct bt_uuid *track_type = BT_UUID_OTS_TYPE_TRACK; struct bt_uuid *track_type = BT_UUID_OTS_TYPE_TRACK;
int ret; int ret;
@ -516,13 +535,17 @@ static int add_track_object(struct mpl_track *track)
obj.add_type = MPL_OBJ_TRACK; obj.add_type = MPL_OBJ_TRACK;
obj.add_track = track; obj.add_track = track;
obj.desc = &created_desc;
track_meta.size.alloc = track_meta.size.cur = setup_track_object(track); obj.desc->size.alloc = obj.desc->size.cur = setup_track_object(track);
track_meta.name = track->title; obj.desc->name = track->title;
track_meta.type.uuid.type = BT_UUID_TYPE_16; BT_OTS_OBJ_SET_PROP_READ(obj.desc->props);
track_meta.type.uuid_16.val = BT_UUID_16(track_type)->val;
BT_OTS_OBJ_SET_PROP_READ(track_meta.props); add_param.size = obj.desc->size.alloc;
ret = bt_ots_obj_add(bt_mcs_get_ots(), &track_meta); add_param.type.uuid.type = BT_UUID_TYPE_16;
add_param.type.uuid_16.val = BT_UUID_16(track_type)->val;
ret = bt_ots_obj_add(bt_mcs_get_ots(), &add_param);
if (ret) { if (ret) {
BT_WARN("Unable to add track object: %d", ret); BT_WARN("Unable to add track object: %d", ret);
@ -536,7 +559,8 @@ static int add_track_object(struct mpl_track *track)
static int add_parent_group_object(struct mpl_mediaplayer *pl) static int add_parent_group_object(struct mpl_mediaplayer *pl)
{ {
int ret; int ret;
struct bt_ots_obj_metadata group_meta = {0}; struct bt_ots_obj_add_param add_param = {};
struct bt_ots_obj_created_desc created_desc = {};
struct bt_uuid *group_type = BT_UUID_OTS_TYPE_GROUP; struct bt_uuid *group_type = BT_UUID_OTS_TYPE_GROUP;
if (obj.busy) { if (obj.busy) {
@ -545,14 +569,17 @@ static int add_parent_group_object(struct mpl_mediaplayer *pl)
} }
obj.busy = true; obj.busy = true;
obj.add_type = MPL_OBJ_PARENT_GROUP; obj.add_type = MPL_OBJ_PARENT_GROUP;
obj.desc = &created_desc;
group_meta.size.alloc = group_meta.size.cur = setup_parent_group_object(pl->group); obj.desc->size.alloc = obj.desc->size.cur = setup_parent_group_object(pl->group);
group_meta.name = pl->group->parent->title; obj.desc->name = pl->group->parent->title;
group_meta.type.uuid.type = BT_UUID_TYPE_16; BT_OTS_OBJ_SET_PROP_READ(obj.desc->props);
group_meta.type.uuid_16.val = BT_UUID_16(group_type)->val;
BT_OTS_OBJ_SET_PROP_READ(group_meta.props);
ret = bt_ots_obj_add(bt_mcs_get_ots(), &group_meta); add_param.size = obj.desc->size.alloc;
add_param.type.uuid.type = BT_UUID_TYPE_16;
add_param.type.uuid_16.val = BT_UUID_16(group_type)->val;
ret = bt_ots_obj_add(bt_mcs_get_ots(), &add_param);
if (ret) { if (ret) {
BT_WARN("Unable to add parent group object"); BT_WARN("Unable to add parent group object");
obj.busy = false; obj.busy = false;
@ -563,7 +590,8 @@ static int add_parent_group_object(struct mpl_mediaplayer *pl)
/* Add a single group to the OTS */ /* Add a single group to the OTS */
static int add_group_object(struct mpl_group *group) static int add_group_object(struct mpl_group *group)
{ {
struct bt_ots_obj_metadata group_meta = {0}; struct bt_ots_obj_add_param add_param = {};
struct bt_ots_obj_created_desc created_desc = {};
struct bt_uuid *group_type = BT_UUID_OTS_TYPE_GROUP; struct bt_uuid *group_type = BT_UUID_OTS_TYPE_GROUP;
int ret; int ret;
@ -581,14 +609,17 @@ static int add_group_object(struct mpl_group *group)
obj.add_type = MPL_OBJ_GROUP; obj.add_type = MPL_OBJ_GROUP;
obj.add_group = group; obj.add_group = group;
obj.desc = &created_desc;
group_meta.size.alloc = group_meta.size.cur = setup_group_object(group); obj.desc->size.alloc = obj.desc->size.cur = setup_group_object(group);
group_meta.name = group->title; obj.desc->name = group->title;
group_meta.type.uuid.type = BT_UUID_TYPE_16; BT_OTS_OBJ_SET_PROP_READ(obj.desc->props);
group_meta.type.uuid_16.val = BT_UUID_16(group_type)->val;
BT_OTS_OBJ_SET_PROP_READ(group_meta.props);
ret = bt_ots_obj_add(bt_mcs_get_ots(), &group_meta); add_param.size = obj.desc->size.alloc;
add_param.type.uuid.type = BT_UUID_TYPE_16;
add_param.type.uuid_16.val = BT_UUID_16(group_type)->val;
ret = bt_ots_obj_add(bt_mcs_get_ots(), &add_param);
if (ret) { if (ret) {
BT_WARN("Unable to add group object: %d", ret); BT_WARN("Unable to add group object: %d", ret);
@ -657,10 +688,12 @@ static int add_group_and_track_objects(struct mpl_mediaplayer *pl)
/**** Callbacks from the object transfer service ******************************/ /**** Callbacks from the object transfer service ******************************/
static void on_obj_deleted(struct bt_ots *ots, struct bt_conn *conn, static int on_obj_deleted(struct bt_ots *ots, struct bt_conn *conn,
uint64_t id) uint64_t id)
{ {
BT_DBG_OBJ_ID("Object Id deleted: ", id); BT_DBG_OBJ_ID("Object Id deleted: ", id);
return 0;
} }
static void on_obj_selected(struct bt_ots *ots, struct bt_conn *conn, static void on_obj_selected(struct bt_ots *ots, struct bt_conn *conn,
@ -709,13 +742,15 @@ static void on_obj_selected(struct bt_ots *ots, struct bt_conn *conn,
obj.busy = false; obj.busy = false;
} }
static int on_obj_created(struct bt_ots *ots, struct bt_conn *conn, static int on_obj_created(struct bt_ots *ots, struct bt_conn *conn, uint64_t id,
uint64_t id, const struct bt_ots_obj_add_param *add_param,
const struct bt_ots_obj_metadata *metadata) struct bt_ots_obj_created_desc *created_desc)
{ {
BT_DBG_OBJ_ID("Object Id created: ", id); BT_DBG_OBJ_ID("Object Id created: ", id);
if (!bt_uuid_cmp(&metadata->type.uuid, BT_UUID_OTS_TYPE_MPL_ICON)) { *created_desc = *obj.desc;
if (!bt_uuid_cmp(&add_param->type.uuid, BT_UUID_OTS_TYPE_MPL_ICON)) {
BT_DBG("Icon Obj Type"); BT_DBG("Icon Obj Type");
if (obj.add_type == MPL_OBJ_ICON) { if (obj.add_type == MPL_OBJ_ICON) {
obj.add_type = MPL_OBJ_NONE; obj.add_type = MPL_OBJ_NONE;
@ -724,7 +759,7 @@ static int on_obj_created(struct bt_ots *ots, struct bt_conn *conn,
BT_DBG("Unexpected object creation"); BT_DBG("Unexpected object creation");
} }
} else if (!bt_uuid_cmp(&metadata->type.uuid, } else if (!bt_uuid_cmp(&add_param->type.uuid,
BT_UUID_OTS_TYPE_TRACK_SEGMENT)) { BT_UUID_OTS_TYPE_TRACK_SEGMENT)) {
BT_DBG("Track Segments Obj Type"); BT_DBG("Track Segments Obj Type");
if (obj.add_type == MPL_OBJ_TRACK_SEGMENTS) { if (obj.add_type == MPL_OBJ_TRACK_SEGMENTS) {
@ -734,7 +769,7 @@ static int on_obj_created(struct bt_ots *ots, struct bt_conn *conn,
BT_DBG("Unexpected object creation"); BT_DBG("Unexpected object creation");
} }
} else if (!bt_uuid_cmp(&metadata->type.uuid, } else if (!bt_uuid_cmp(&add_param->type.uuid,
BT_UUID_OTS_TYPE_TRACK)) { BT_UUID_OTS_TYPE_TRACK)) {
BT_DBG("Track Obj Type"); BT_DBG("Track Obj Type");
if (obj.add_type == MPL_OBJ_TRACK) { if (obj.add_type == MPL_OBJ_TRACK) {
@ -745,7 +780,7 @@ static int on_obj_created(struct bt_ots *ots, struct bt_conn *conn,
BT_DBG("Unexpected object creation"); BT_DBG("Unexpected object creation");
} }
} else if (!bt_uuid_cmp(&metadata->type.uuid, } else if (!bt_uuid_cmp(&add_param->type.uuid,
BT_UUID_OTS_TYPE_GROUP)) { BT_UUID_OTS_TYPE_GROUP)) {
BT_DBG("Group Obj Type"); BT_DBG("Group Obj Type");
if (obj.add_type == MPL_OBJ_PARENT_GROUP) { if (obj.add_type == MPL_OBJ_PARENT_GROUP) {

View file

@ -46,6 +46,14 @@ config BT_OTS_MAX_OBJ_CNT
config BT_OTS_SECONDARY_SVC config BT_OTS_SECONDARY_SVC
bool "Register OTS as Secondary Service" bool "Register OTS as Secondary Service"
config BT_OTS_OACP_CREATE_SUPPORT
bool "Support OACP Create Operation"
depends on BT_OTS_OACP_WRITE_SUPPORT
depends on BT_OTS_OBJ_NAME_WRITE_SUPPORT
config BT_OTS_OACP_DELETE_SUPPORT
bool "Support OACP Delete Operation"
config BT_OTS_OACP_READ_SUPPORT config BT_OTS_OACP_READ_SUPPORT
bool "Support OACP Read Operation" bool "Support OACP Read Operation"
default y default y

View file

@ -30,6 +30,18 @@
LOG_MODULE_REGISTER(bt_ots, CONFIG_BT_OTS_LOG_LEVEL); LOG_MODULE_REGISTER(bt_ots, CONFIG_BT_OTS_LOG_LEVEL);
#if defined(CONFIG_BT_OTS_OACP_CREATE_SUPPORT)
#define OACP_FEAT_BIT_CREATE BIT(BT_OTS_OACP_FEAT_CREATE)
#else
#define OACP_FEAT_BIT_CREATE 0
#endif
#if defined(CONFIG_BT_OTS_OACP_DELETE_SUPPORT)
#define OACP_FEAT_BIT_DELETE BIT(BT_OTS_OACP_FEAT_DELETE)
#else
#define OACP_FEAT_BIT_DELETE 0
#endif
#if defined(CONFIG_BT_OTS_OACP_READ_SUPPORT) #if defined(CONFIG_BT_OTS_OACP_READ_SUPPORT)
#define OACP_FEAT_BIT_READ BIT(BT_OTS_OACP_FEAT_READ) #define OACP_FEAT_BIT_READ BIT(BT_OTS_OACP_FEAT_READ)
#else #else
@ -50,6 +62,8 @@ LOG_MODULE_REGISTER(bt_ots, CONFIG_BT_OTS_LOG_LEVEL);
/* OACP features supported by Kconfig */ /* OACP features supported by Kconfig */
#define OACP_FEAT ( \ #define OACP_FEAT ( \
OACP_FEAT_BIT_CREATE | \
OACP_FEAT_BIT_DELETE | \
OACP_FEAT_BIT_READ | \ OACP_FEAT_BIT_READ | \
OACP_FEAT_BIT_WRITE | \ OACP_FEAT_BIT_WRITE | \
OACP_FEAT_BIT_PATCH) OACP_FEAT_BIT_PATCH)
@ -200,14 +214,17 @@ static ssize_t ots_obj_type_read(struct bt_conn *conn,
} }
obj_meta = &ots->cur_obj->metadata; obj_meta = &ots->cur_obj->metadata;
if (obj_meta->type.uuid.type == BT_UUID_TYPE_128) { switch (obj_meta->type.uuid.type) {
return bt_gatt_attr_read(conn, attr, buf, len, offset, case BT_UUID_TYPE_16:
obj_meta->type.uuid_128.val,
sizeof(obj_meta->type.uuid_128.val));
} else {
return bt_gatt_attr_read(conn, attr, buf, len, offset, return bt_gatt_attr_read(conn, attr, buf, len, offset,
&obj_meta->type.uuid_16.val, &obj_meta->type.uuid_16.val,
sizeof(obj_meta->type.uuid_16.val)); sizeof(obj_meta->type.uuid_16.val));
case BT_UUID_TYPE_128:
return bt_gatt_attr_read(conn, attr, buf, len, offset,
obj_meta->type.uuid_128.val,
sizeof(obj_meta->type.uuid_128.val));
default:
return -EINVAL;
} }
} }
@ -271,12 +288,13 @@ static ssize_t ots_obj_prop_read(struct bt_conn *conn,
sizeof(ots->cur_obj->metadata.props)); sizeof(ots->cur_obj->metadata.props));
} }
int bt_ots_obj_add(struct bt_ots *ots, int bt_ots_obj_add_internal(struct bt_ots *ots, struct bt_conn *conn,
struct bt_ots_obj_metadata *obj_init) const struct bt_ots_obj_add_param *param,
struct bt_gatt_ots_object **obj)
{ {
int err; int err;
struct bt_gatt_ots_object *obj; struct bt_gatt_ots_object *new_obj;
size_t name_len; struct bt_ots_obj_created_desc created_desc;
if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ) && ots->dir_list && if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ) && ots->dir_list &&
ots->dir_list->dir_list_obj->state.type != BT_GATT_OTS_OBJECT_IDLE_STATE) { ots->dir_list->dir_list_obj->state.type != BT_GATT_OTS_OBJECT_IDLE_STATE) {
@ -284,50 +302,96 @@ int bt_ots_obj_add(struct bt_ots *ots,
return -EBUSY; return -EBUSY;
} }
name_len = strlen(obj_init->name); err = bt_gatt_ots_obj_manager_obj_add(ots->obj_manager, &new_obj);
CHECKIF(name_len == 0 || name_len > CONFIG_BT_OTS_OBJ_MAX_NAME_LEN) {
LOG_DBG("Invalid name length %zu", name_len);
return -EINVAL;
}
CHECKIF(!ots_obj_validate_prop_against_oacp(obj_init->props, ots->features.oacp)) {
LOG_DBG("Object properties (0x%04X) are not a subset of OACP (0x%04X)",
obj_init->props, ots->features.oacp);
return -ENOTSUP;
}
err = bt_gatt_ots_obj_manager_obj_add(ots->obj_manager, &obj);
if (err) { if (err) {
LOG_ERR("No space available in the object manager"); LOG_ERR("No space available in the object manager");
return err; return err;
} }
/* Initialize object. */
memcpy(&obj->metadata, obj_init, sizeof(obj->metadata));
if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ)) { if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ)) {
bt_ots_dir_list_obj_add(ots->dir_list, ots->obj_manager, ots->cur_obj, obj); bt_ots_dir_list_obj_add(ots->dir_list, ots->obj_manager, ots->cur_obj, new_obj);
} }
/* Request object data. */ (void)memset(&created_desc, 0, sizeof(created_desc));
if (ots->cb->obj_created) { if (ots->cb->obj_created) {
err = ots->cb->obj_created(ots, NULL, obj->id, obj_init); err = ots->cb->obj_created(ots, NULL, new_obj->id, param, &created_desc);
if (err) { if (err) {
bt_gatt_ots_obj_manager_obj_delete(obj); (void)bt_gatt_ots_obj_manager_obj_delete(new_obj);
if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ)) { if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ)) {
bt_ots_dir_list_obj_remove(ots->dir_list, ots->obj_manager, bt_ots_dir_list_obj_remove(ots->dir_list, ots->obj_manager,
ots->cur_obj, obj); ots->cur_obj, new_obj);
} }
return err; return err;
} }
if (!ots_obj_validate_prop_against_oacp(created_desc.props, ots->features.oacp)) {
LOG_ERR("Object properties (0x%04X) are not a subset of OACP (0x%04X)",
created_desc.props, ots->features.oacp);
(void)bt_ots_obj_delete(ots, new_obj->id);
return -ECANCELED;
}
if (created_desc.name == NULL) {
LOG_ERR("Object name must be set by application after object creation.");
(void)bt_ots_obj_delete(ots, new_obj->id);
return -ECANCELED;
}
if (created_desc.size.alloc < param->size) {
LOG_ERR("Object allocated size must >= requested size.");
(void)bt_ots_obj_delete(ots, new_obj->id);
return -ECANCELED;
}
}
new_obj->metadata.type = param->type;
new_obj->metadata.name = created_desc.name;
new_obj->metadata.size = created_desc.size;
new_obj->metadata.props = created_desc.props;
if (obj) {
*obj = new_obj;
} }
return 0; return 0;
} }
int bt_ots_obj_add(struct bt_ots *ots, const struct bt_ots_obj_add_param *param)
{
int err;
size_t name_len;
struct bt_gatt_ots_object *obj;
err = bt_ots_obj_add_internal(ots, NULL, param, &obj);
if (err) {
return err;
}
name_len = strlen(obj->metadata.name);
if (name_len == 0 || name_len > CONFIG_BT_OTS_OBJ_MAX_NAME_LEN) {
LOG_ERR("Invalid name length %zu", name_len);
(void)bt_ots_obj_delete(ots, obj->id);
return -ECANCELED;
}
if (obj->metadata.size.cur > param->size) {
LOG_ERR("Object current size must be less than or equal to requested size.");
(void)bt_ots_obj_delete(ots, obj->id);
return -ECANCELED;
}
return obj->id;
}
int bt_ots_obj_delete(struct bt_ots *ots, uint64_t id) int bt_ots_obj_delete(struct bt_ots *ots, uint64_t id)
{ {
int err; int err;
@ -338,12 +402,9 @@ int bt_ots_obj_delete(struct bt_ots *ots, uint64_t id)
return err; return err;
} }
if (ots->cur_obj == obj) {
if (obj->state.type != BT_GATT_OTS_OBJECT_IDLE_STATE) { if (obj->state.type != BT_GATT_OTS_OBJECT_IDLE_STATE) {
return -EBUSY; return -EBUSY;
} }
ots->cur_obj = NULL;
}
if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ) && ots->dir_list && if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ) && ots->dir_list &&
ots->dir_list->dir_list_obj->state.type != BT_GATT_OTS_OBJECT_IDLE_STATE) { ots->dir_list->dir_list_obj->state.type != BT_GATT_OTS_OBJECT_IDLE_STATE) {
@ -351,6 +412,13 @@ int bt_ots_obj_delete(struct bt_ots *ots, uint64_t id)
return -EBUSY; return -EBUSY;
} }
if (ots->cb->obj_deleted) {
err = ots->cb->obj_deleted(ots, NULL, obj->id);
if (err) {
return err;
}
}
err = bt_gatt_ots_obj_manager_obj_delete(obj); err = bt_gatt_ots_obj_manager_obj_delete(obj);
if (err) { if (err) {
return err; return err;
@ -360,8 +428,8 @@ int bt_ots_obj_delete(struct bt_ots *ots, uint64_t id)
bt_ots_dir_list_obj_remove(ots->dir_list, ots->obj_manager, ots->cur_obj, obj); bt_ots_dir_list_obj_remove(ots->dir_list, ots->obj_manager, ots->cur_obj, obj);
} }
if (ots->cb->obj_deleted) { if (ots->cur_obj == obj) {
ots->cb->obj_deleted(ots, NULL, obj->id); ots->cur_obj = NULL;
} }
return 0; return 0;
@ -385,6 +453,9 @@ int bt_ots_init(struct bt_ots *ots,
__ASSERT(ots_init->cb->obj_created, __ASSERT(ots_init->cb->obj_created,
"Callback for object creation is not set"); "Callback for object creation is not set");
__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");
__ASSERT(ots_init->cb->obj_read || __ASSERT(ots_init->cb->obj_read ||
!BT_OTS_OACP_GET_FEAT_READ(ots_init->features.oacp), !BT_OTS_OACP_GET_FEAT_READ(ots_init->features.oacp),
"Callback for object reading is not set"); "Callback for object reading is not set");
@ -400,6 +471,10 @@ int bt_ots_init(struct bt_ots *ots,
return -ENOTSUP; return -ENOTSUP;
} }
__ASSERT(!BT_OTS_OACP_GET_FEAT_CREATE(ots_init->features.oacp) ||
BT_OTS_OACP_GET_FEAT_WRITE(ots_init->features.oacp),
"Object creation requires object write to be supported");
ots->features.oacp = ots_init->features.oacp; ots->features.oacp = ots_init->features.oacp;
LOG_DBG("OACP features: 0x%04X", ots->features.oacp); LOG_DBG("OACP features: 0x%04X", ots->features.oacp);
@ -495,6 +570,69 @@ BT_GATT_SERVICE_INSTANCE_DEFINE(ots_service_list, ots_instances,
CONFIG_BT_OTS_MAX_INST_CNT, CONFIG_BT_OTS_MAX_INST_CNT,
BT_GATT_OTS_ATTRS); BT_GATT_OTS_ATTRS);
static void ots_delete_empty_name_objects(struct bt_ots *ots, struct bt_conn *conn)
{
char id_str[BT_OTS_OBJ_ID_STR_LEN];
struct bt_gatt_ots_object *obj;
struct bt_gatt_ots_object *next_obj;
int err;
err = bt_gatt_ots_obj_manager_first_obj_get(ots->obj_manager, &next_obj);
while (!err) {
obj = next_obj;
/* Get the next object before we potentially delete the current object and
* no longer can get the next object
*/
err = bt_gatt_ots_obj_manager_next_obj_get(ots->obj_manager, obj, &next_obj);
if (strlen(obj->metadata.name) == 0) {
bt_ots_obj_id_to_str(obj->id, id_str, sizeof(id_str));
LOG_DBG("Deleting object with %s ID due to empty name", log_strdup(id_str));
if (ots->cb && ots->cb->obj_deleted) {
ots->cb->obj_deleted(ots, conn, obj->id);
}
if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ)) {
bt_ots_dir_list_obj_remove(ots->dir_list, ots->obj_manager,
ots->cur_obj, obj);
}
if (bt_gatt_ots_obj_manager_obj_delete(obj)) {
LOG_ERR("Failed to remove object with %s ID from object manager",
log_strdup(id_str));
}
}
}
}
static void ots_conn_disconnected(struct bt_conn *conn, uint8_t reason)
{
uint32_t index;
struct bt_ots *instance;
for (instance = BT_GATT_OTS_INSTANCE_LIST_START, index = 0;
index < instance_cnt;
instance++, index++) {
LOG_DBG("Processing disconnect for OTS instance %u", index);
if (instance->cur_obj != NULL) {
__ASSERT(instance->cur_obj->state.type == BT_GATT_OTS_OBJECT_IDLE_STATE,
"The current object is expected to be in idle state as part "
"of cleanup of the L2CAP channel connection close.");
instance->cur_obj = NULL;
}
ots_delete_empty_name_objects(instance, conn);
}
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.disconnected = ots_conn_disconnected,
};
struct bt_ots *bt_ots_free_instance_get(void) struct bt_ots *bt_ots_free_instance_get(void)
{ {
if (instance_cnt >= BT_GATT_OTS_INSTANCE_LIST_SIZE) { if (instance_cnt >= BT_GATT_OTS_INSTANCE_LIST_SIZE) {

View file

@ -64,6 +64,21 @@ struct bt_gatt_ots_object_state {
}; };
}; };
/** @brief Descriptor for OTS object initialization. */
struct bt_ots_obj_metadata {
/* Object Name */
char *name;
/* Object Type */
struct bt_ots_obj_type type;
/* Object Size */
struct bt_ots_obj_size size;
/* Object Properties */
uint32_t props;
};
struct bt_gatt_ots_object { struct bt_gatt_ots_object {
uint64_t id; uint64_t id;
struct bt_ots_obj_metadata metadata; struct bt_ots_obj_metadata metadata;
@ -89,6 +104,9 @@ struct bt_ots {
void *obj_manager; void *obj_manager;
}; };
int bt_ots_obj_add_internal(struct bt_ots *ots, struct bt_conn *conn,
const struct bt_ots_obj_add_param *param,
struct bt_gatt_ots_object **obj);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -16,6 +16,7 @@
#include <bluetooth/services/ots.h> #include <bluetooth/services/ots.h>
#include "ots_internal.h" #include "ots_internal.h"
#include "ots_dir_list_internal.h" #include "ots_dir_list_internal.h"
#include "ots_obj_manager_internal.h"
#include <logging/log.h> #include <logging/log.h>
@ -45,6 +46,121 @@ static void oacp_l2cap_closed(struct bt_gatt_ots_l2cap *l2cap_ctx,
} }
#endif #endif
#if defined(CONFIG_BT_OTS_OACP_CREATE_SUPPORT)
static enum bt_gatt_ots_oacp_res_code oacp_create_proc_validate(
struct bt_conn *conn,
struct bt_ots *ots,
struct bt_gatt_ots_oacp_proc *proc)
{
char str[BT_UUID_STR_LEN];
int err;
struct bt_gatt_ots_object *obj;
const struct bt_ots_obj_add_param param = {
.size = proc->create_params.size,
.type = proc->create_params.type,
};
bt_uuid_to_str(&param.type.uuid, str, BT_UUID_STR_LEN);
LOG_DBG("Validating Create procedure with size: 0x%08X and "
"type: %s", param.size, log_strdup(str));
if (!BT_OTS_OACP_GET_FEAT_CREATE(ots->features.oacp)) {
LOG_DBG("Create Procedure is not supported.");
return BT_GATT_OTS_OACP_RES_OPCODE_NOT_SUP;
}
err = bt_ots_obj_add_internal(ots, conn, &param, &obj);
if (err) {
goto exit;
}
/* Verify Initialization Metadata */
if (strlen(obj->metadata.name) > 0) {
LOG_ERR("Object name shall be a zero length string after object creation.");
(void)bt_ots_obj_delete(ots, obj->id);
err = -ECANCELED;
goto exit;
}
if (obj->metadata.size.cur > 0) {
LOG_ERR("Object current size must be 0.");
(void)bt_ots_obj_delete(ots, obj->id);
err = -ECANCELED;
goto exit;
}
if (!BT_OTS_OBJ_GET_PROP_WRITE(obj->metadata.props)) {
LOG_ERR("Created object must have write property.");
(void)bt_ots_obj_delete(ots, obj->id);
err = -ECANCELED;
goto exit;
}
ots->cur_obj = obj;
ots->cur_obj->state.type = BT_GATT_OTS_OBJECT_IDLE_STATE;
LOG_DBG("Create procedure is complete");
exit:
switch (err) {
case 0:
return BT_GATT_OTS_OACP_RES_SUCCESS;
case -ENOTSUP:
return BT_GATT_OTS_OACP_RES_UNSUP_TYPE;
case -ENOMEM:
return BT_GATT_OTS_OACP_RES_INSUFF_RES;
case -EINVAL:
return BT_GATT_OTS_OACP_RES_INV_PARAM;
case -ECANCELED:
default:
return BT_GATT_OTS_OACP_RES_OPER_FAILED;
}
}
#endif
#if defined(CONFIG_BT_OTS_OACP_DELETE_SUPPORT)
static enum bt_gatt_ots_oacp_res_code oacp_delete_proc_validate(
struct bt_conn *conn,
struct bt_ots *ots,
struct bt_gatt_ots_oacp_proc *proc)
{
int err;
if (!BT_OTS_OACP_GET_FEAT_DELETE(ots->features.oacp)) {
LOG_DBG("Delete Procedure is not supported.");
return BT_GATT_OTS_OACP_RES_OPCODE_NOT_SUP;
}
if (!ots->cur_obj) {
LOG_DBG("No object is selected.");
return BT_GATT_OTS_OACP_RES_INV_OBJ;
}
if (!BT_OTS_OBJ_GET_PROP_DELETE(ots->cur_obj->metadata.props)) {
LOG_DBG("Object properties do not permit deletion.");
return BT_GATT_OTS_OACP_RES_NOT_PERMITTED;
}
err = bt_ots_obj_delete(ots, ots->cur_obj->id);
if (err) {
LOG_ERR("Deleting object during Delete procedure failed: %d", err);
goto exit;
}
LOG_DBG("Delete procedure is complete");
exit:
switch (err) {
case 0:
return BT_GATT_OTS_OACP_RES_SUCCESS;
case -EBUSY:
return BT_GATT_OTS_OACP_RES_OBJ_LOCKED;
default:
return BT_GATT_OTS_OACP_RES_OPER_FAILED;
}
}
#endif
static enum bt_gatt_ots_oacp_res_code oacp_read_proc_validate( static enum bt_gatt_ots_oacp_res_code oacp_read_proc_validate(
struct bt_conn *conn, struct bt_conn *conn,
struct bt_ots *ots, struct bt_ots *ots,
@ -162,12 +278,18 @@ static enum bt_gatt_ots_oacp_res_code oacp_proc_validate(
switch (proc->type) { switch (proc->type) {
case BT_GATT_OTS_OACP_PROC_READ: case BT_GATT_OTS_OACP_PROC_READ:
return oacp_read_proc_validate(conn, ots, proc); return oacp_read_proc_validate(conn, ots, proc);
case BT_GATT_OTS_OACP_PROC_WRITE:
#if defined(CONFIG_BT_OTS_OACP_WRITE_SUPPORT) #if defined(CONFIG_BT_OTS_OACP_WRITE_SUPPORT)
case BT_GATT_OTS_OACP_PROC_WRITE:
return oacp_write_proc_validate(conn, ots, proc); return oacp_write_proc_validate(conn, ots, proc);
#endif #endif
#if defined(CONFIG_BT_OTS_OACP_CREATE_SUPPORT)
case BT_GATT_OTS_OACP_PROC_CREATE: case BT_GATT_OTS_OACP_PROC_CREATE:
return oacp_create_proc_validate(conn, ots, proc);
#endif
#if defined(CONFIG_BT_OTS_OACP_DELETE_SUPPORT)
case BT_GATT_OTS_OACP_PROC_DELETE: case BT_GATT_OTS_OACP_PROC_DELETE:
return oacp_delete_proc_validate(conn, ots, proc);
#endif
case BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC: case BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC:
case BT_GATT_OTS_OACP_PROC_EXECUTE: case BT_GATT_OTS_OACP_PROC_EXECUTE:
case BT_GATT_OTS_OACP_PROC_ABORT: case BT_GATT_OTS_OACP_PROC_ABORT:
@ -186,14 +308,18 @@ static enum bt_gatt_ots_oacp_res_code oacp_command_decode(
proc->type = net_buf_simple_pull_u8(&net_buf); proc->type = net_buf_simple_pull_u8(&net_buf);
switch (proc->type) { switch (proc->type) {
#if defined(CONFIG_BT_OTS_OACP_CREATE_SUPPORT)
case BT_GATT_OTS_OACP_PROC_CREATE: case BT_GATT_OTS_OACP_PROC_CREATE:
proc->create_params.size = net_buf_simple_pull_le32(&net_buf); proc->create_params.size = net_buf_simple_pull_le32(&net_buf);
bt_uuid_create(&proc->create_params.type.uuid, net_buf.data, bt_uuid_create(&proc->create_params.type.uuid, net_buf.data,
net_buf.len); net_buf.len);
net_buf_simple_pull_mem(&net_buf, net_buf.len); net_buf_simple_pull_mem(&net_buf, net_buf.len);
break; return BT_GATT_OTS_OACP_RES_SUCCESS;
#endif
#if defined(CONFIG_BT_OTS_OACP_DELETE_SUPPORT)
case BT_GATT_OTS_OACP_PROC_DELETE: case BT_GATT_OTS_OACP_PROC_DELETE:
break; return BT_GATT_OTS_OACP_RES_SUCCESS;
#endif
case BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC: case BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC:
proc->cs_calc_params.offset = proc->cs_calc_params.offset =
net_buf_simple_pull_le32(&net_buf); net_buf_simple_pull_le32(&net_buf);
@ -208,8 +334,8 @@ static enum bt_gatt_ots_oacp_res_code oacp_command_decode(
proc->read_params.len = proc->read_params.len =
net_buf_simple_pull_le32(&net_buf); net_buf_simple_pull_le32(&net_buf);
return BT_GATT_OTS_OACP_RES_SUCCESS; return BT_GATT_OTS_OACP_RES_SUCCESS;
case BT_GATT_OTS_OACP_PROC_WRITE:
#if defined(CONFIG_BT_OTS_OACP_WRITE_SUPPORT) #if defined(CONFIG_BT_OTS_OACP_WRITE_SUPPORT)
case BT_GATT_OTS_OACP_PROC_WRITE:
proc->write_params.offset = proc->write_params.offset =
net_buf_simple_pull_le32(&net_buf); net_buf_simple_pull_le32(&net_buf);
proc->write_params.len = proc->write_params.len =
@ -217,8 +343,6 @@ static enum bt_gatt_ots_oacp_res_code oacp_command_decode(
proc->write_params.mode = proc->write_params.mode =
net_buf_simple_pull_u8(&net_buf); net_buf_simple_pull_u8(&net_buf);
return BT_GATT_OTS_OACP_RES_SUCCESS; return BT_GATT_OTS_OACP_RES_SUCCESS;
#else
break;
#endif #endif
case BT_GATT_OTS_OACP_PROC_ABORT: case BT_GATT_OTS_OACP_PROC_ABORT:
default: default:
@ -243,9 +367,6 @@ static bool oacp_command_len_verify(struct bt_gatt_ots_oacp_proc *proc,
case BT_UUID_TYPE_16: case BT_UUID_TYPE_16:
ref_len += BT_GATT_OTS_OACP_CREATE_UUID16_PARAMS_SIZE; ref_len += BT_GATT_OTS_OACP_CREATE_UUID16_PARAMS_SIZE;
break; break;
case BT_UUID_TYPE_32:
ref_len += BT_GATT_OTS_OACP_CREATE_UUID32_PARAMS_SIZE;
break;
case BT_UUID_TYPE_128: case BT_UUID_TYPE_128:
ref_len += BT_GATT_OTS_OACP_CREATE_UUID128_PARAMS_SIZE; ref_len += BT_GATT_OTS_OACP_CREATE_UUID128_PARAMS_SIZE;
break; break;
@ -443,6 +564,11 @@ static void oacp_ind_cb(struct bt_conn *conn,
LOG_DBG("Received OACP Indication ACK with status: 0x%04X", err); LOG_DBG("Received OACP Indication ACK with status: 0x%04X", err);
if (!ots->cur_obj) {
LOG_DBG("There is no object associated with this ACK");
return;
}
switch (ots->cur_obj->state.type) { switch (ots->cur_obj->state.type) {
case BT_GATT_OTS_OBJECT_READ_OP_STATE: case BT_GATT_OTS_OBJECT_READ_OP_STATE:
oacp_read_proc_execute(ots, conn); oacp_read_proc_execute(ots, conn);
@ -450,6 +576,9 @@ static void oacp_ind_cb(struct bt_conn *conn,
case BT_GATT_OTS_OBJECT_WRITE_OP_STATE: case BT_GATT_OTS_OBJECT_WRITE_OP_STATE:
/* procedure execution is driven by L2CAP socket receive */ /* procedure execution is driven by L2CAP socket receive */
break; break;
case BT_GATT_OTS_OBJECT_IDLE_STATE:
/* procedure is not in progress and was already completed */
break;
default: default:
LOG_ERR("Unsupported OTS state: %d", ots->cur_obj->state.type); LOG_ERR("Unsupported OTS state: %d", ots->cur_obj->state.type);
break; break;

View file

@ -92,14 +92,16 @@ struct bt_gatt_ots_oacp_proc {
}; };
}; };
/* Size of Object Action Control Point create procedure with 16-bit UUID object type */ /* Size of the generic part of the Object Action Control Point create procedure */
#define BT_GATT_OTS_OACP_CREATE_UUID16_PARAMS_SIZE 7 #define BT_GATT_OTS_OACP_CREATE_GENERIC_PARAMS_SIZE 4
/* Size of Object Action Control Point create procedure with 32-bit UUID object type */ /* Size of Object Action Control Point create procedure with 16-bit UUID object type */
#define BT_GATT_OTS_OACP_CREATE_UUID32_PARAMS_SIZE 9 #define BT_GATT_OTS_OACP_CREATE_UUID16_PARAMS_SIZE \
(BT_GATT_OTS_OACP_CREATE_GENERIC_PARAMS_SIZE + BT_UUID_SIZE_16)
/* Size of Object Action Control Point create procedure with 128-bit UUID object type */ /* Size of Object Action Control Point create procedure with 128-bit UUID object type */
#define BT_GATT_OTS_OACP_CREATE_UUID128_PARAMS_SIZE 21 #define BT_GATT_OTS_OACP_CREATE_UUID128_PARAMS_SIZE \
(BT_GATT_OTS_OACP_CREATE_GENERIC_PARAMS_SIZE + BT_UUID_SIZE_128)
/* Size of Object Action Control Point checksum calculation procedure */ /* Size of Object Action Control Point checksum calculation procedure */
#define BT_GATT_OTS_OACP_CS_CALC_PARAMS_SIZE 8 #define BT_GATT_OTS_OACP_CS_CALC_PARAMS_SIZE 8