Commit 76e17857 authored by Mariusz Skamra's avatar Mariusz Skamra Committed by Fabio Baltieri
Browse files

Bluetooth: audio: vocs: Refactor bt_vocs object



This adds refactor of bt_vocs object that is common interface for client
and server commands. As the bt_vocs_client is significantly larger than
bt_vocs_server the memory usage is not optimized when bt_vocs has an
union of those. Thus RAM consumption has been minimized by using
CONTAINER_OF macro to reference either to bt_vocs_client or
bt_vocs_server instance that bt_vocs is a member of.

Signed-off-by: default avatarMariusz Skamra <mariusz.skamra@codecoup.pl>
parent e84d76f9
Loading
Loading
Loading
Loading
+111 −81
Original line number Diff line number Diff line
@@ -38,11 +38,11 @@ static void offset_state_cfg_changed(const struct bt_gatt_attr *attr, uint16_t v
static ssize_t read_offset_state(struct bt_conn *conn, const struct bt_gatt_attr *attr,
				 void *buf, uint16_t len, uint16_t offset)
{
	struct bt_vocs *inst = BT_AUDIO_CHRC_USER_DATA(attr);
	struct bt_vocs_server *inst = BT_AUDIO_CHRC_USER_DATA(attr);

	LOG_DBG("offset %d, counter %u", inst->srv.state.offset, inst->srv.state.change_counter);
	return bt_gatt_attr_read(conn, attr, buf, len, offset, &inst->srv.state,
				 sizeof(inst->srv.state));
	LOG_DBG("offset %d, counter %u", inst->state.offset, inst->state.change_counter);
	return bt_gatt_attr_read(conn, attr, buf, len, offset, &inst->state,
				 sizeof(inst->state));
}

static void location_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
@@ -55,14 +55,14 @@ static void location_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value
static ssize_t write_location(struct bt_conn *conn, const struct bt_gatt_attr *attr,
			      const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
	struct bt_vocs *inst = BT_AUDIO_CHRC_USER_DATA(attr);
	struct bt_vocs_server *inst = BT_AUDIO_CHRC_USER_DATA(attr);
	enum bt_audio_location new_location;

	if (offset) {
		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
	}

	if (len != sizeof(inst->srv.location)) {
	if (len != sizeof(inst->location)) {
		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
	}

@@ -74,15 +74,15 @@ static ssize_t write_location(struct bt_conn *conn, const struct bt_gatt_attr *a
		return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
	}

	if (new_location != inst->srv.location) {
		inst->srv.location = new_location;
	if (new_location != inst->location) {
		inst->location = new_location;
		(void)bt_gatt_notify_uuid(NULL, BT_UUID_VOCS_LOCATION,
					  inst->srv.service_p->attrs,
					  &inst->srv.location,
					  sizeof(inst->srv.location));
					  inst->service_p->attrs,
					  &inst->location,
					  sizeof(inst->location));

		if (inst->srv.cb && inst->srv.cb->location) {
			inst->srv.cb->location(inst, 0, inst->srv.location);
		if (inst->cb && inst->cb->location) {
			inst->cb->location(&inst->vocs, 0, inst->location);
		}
	}

@@ -93,18 +93,18 @@ static ssize_t write_location(struct bt_conn *conn, const struct bt_gatt_attr *a
static ssize_t read_location(struct bt_conn *conn, const struct bt_gatt_attr *attr,
			     void *buf, uint16_t len, uint16_t offset)
{
	struct bt_vocs *inst = BT_AUDIO_CHRC_USER_DATA(attr);
	struct bt_vocs_server *inst = BT_AUDIO_CHRC_USER_DATA(attr);

	LOG_DBG("0x%08x", inst->srv.location);
	return bt_gatt_attr_read(conn, attr, buf, len, offset, &inst->srv.location,
				 sizeof(inst->srv.location));
	LOG_DBG("0x%08x", inst->location);
	return bt_gatt_attr_read(conn, attr, buf, len, offset, &inst->location,
				 sizeof(inst->location));
}
#endif /* CONFIG_BT_VOCS */

static ssize_t write_vocs_control(struct bt_conn *conn, const struct bt_gatt_attr *attr,
				  const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
	struct bt_vocs *inst = BT_AUDIO_CHRC_USER_DATA(attr);
	struct bt_vocs_server *inst = BT_AUDIO_CHRC_USER_DATA(attr);
	const struct bt_vocs_control *cp = buf;
	bool notify = false;

@@ -129,7 +129,7 @@ static ssize_t write_vocs_control(struct bt_conn *conn, const struct bt_gatt_att
	LOG_DBG("Opcode %u, counter %u", cp->opcode, cp->counter);


	if (cp->counter != inst->srv.state.change_counter) {
	if (cp->counter != inst->state.change_counter) {
		return BT_GATT_ERR(BT_VOCS_ERR_INVALID_COUNTER);
	}

@@ -140,8 +140,8 @@ static ssize_t write_vocs_control(struct bt_conn *conn, const struct bt_gatt_att
			return BT_GATT_ERR(BT_VOCS_ERR_OUT_OF_RANGE);
		}

		if (inst->srv.state.offset != sys_le16_to_cpu(cp->offset)) {
			inst->srv.state.offset = sys_le16_to_cpu(cp->offset);
		if (inst->state.offset != sys_le16_to_cpu(cp->offset)) {
			inst->state.offset = sys_le16_to_cpu(cp->offset);
			notify = true;
		}
		break;
@@ -150,15 +150,15 @@ static ssize_t write_vocs_control(struct bt_conn *conn, const struct bt_gatt_att
	}

	if (notify) {
		inst->srv.state.change_counter++;
		LOG_DBG("New state: offset %d, counter %u", inst->srv.state.offset,
			inst->srv.state.change_counter);
		inst->state.change_counter++;
		LOG_DBG("New state: offset %d, counter %u", inst->state.offset,
			inst->state.change_counter);
		(void)bt_gatt_notify_uuid(NULL, BT_UUID_VOCS_STATE,
					  inst->srv.service_p->attrs,
					  &inst->srv.state, sizeof(inst->srv.state));
					  inst->service_p->attrs,
					  &inst->state, sizeof(inst->state));

		if (inst->srv.cb && inst->srv.cb->state) {
			inst->srv.cb->state(inst, 0, inst->srv.state.offset);
		if (inst->cb && inst->cb->state) {
			inst->cb->state(&inst->vocs, 0, inst->state.offset);
		}

	}
@@ -176,39 +176,39 @@ static void output_desc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t va
static ssize_t write_output_desc(struct bt_conn *conn, const struct bt_gatt_attr *attr,
				 const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
	struct bt_vocs *inst = BT_AUDIO_CHRC_USER_DATA(attr);
	struct bt_vocs_server *inst = BT_AUDIO_CHRC_USER_DATA(attr);

	if (offset) {
		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
	}

	if (len >= sizeof(inst->srv.output_desc)) {
	if (len >= sizeof(inst->output_desc)) {
		LOG_DBG("Output desc was clipped from length %u to %zu", len,
			sizeof(inst->srv.output_desc) - 1);
			sizeof(inst->output_desc) - 1);
		/* We just clip the string value if it's too long */
		len = (uint16_t)sizeof(inst->srv.output_desc) - 1;
		len = (uint16_t)sizeof(inst->output_desc) - 1;
	}

	if (len != strlen(inst->srv.output_desc) || memcmp(buf, inst->srv.output_desc, len)) {
		memcpy(inst->srv.output_desc, buf, len);
		inst->srv.output_desc[len] = '\0';
	if (len != strlen(inst->output_desc) || memcmp(buf, inst->output_desc, len)) {
		memcpy(inst->output_desc, buf, len);
		inst->output_desc[len] = '\0';

		(void)bt_gatt_notify_uuid(NULL, BT_UUID_VOCS_DESCRIPTION,
					  inst->srv.service_p->attrs,
					  &inst->srv.output_desc,
					  strlen(inst->srv.output_desc));
					  inst->service_p->attrs,
					  &inst->output_desc,
					  strlen(inst->output_desc));

		if (inst->srv.cb && inst->srv.cb->description) {
			inst->srv.cb->description(inst, 0, inst->srv.output_desc);
		if (inst->cb && inst->cb->description) {
			inst->cb->description(&inst->vocs, 0, inst->output_desc);
		}
	}

	LOG_DBG("%s", inst->srv.output_desc);
	LOG_DBG("%s", inst->output_desc);

	return len;
}

static int vocs_write(struct bt_vocs *inst,
static int vocs_write(struct bt_vocs_server *inst,
		      ssize_t (*write)(struct bt_conn *conn,
				       const struct bt_gatt_attr *attr,
				       const void *buf, uint16_t len,
@@ -235,11 +235,11 @@ static int vocs_write(struct bt_vocs *inst,
static ssize_t read_output_desc(struct bt_conn *conn, const struct bt_gatt_attr *attr,
				void *buf, uint16_t len, uint16_t offset)
{
	struct bt_vocs *inst = BT_AUDIO_CHRC_USER_DATA(attr);
	struct bt_vocs_server *inst = BT_AUDIO_CHRC_USER_DATA(attr);

	LOG_DBG("%s", inst->srv.output_desc);
	return bt_gatt_attr_read(conn, attr, buf, len, offset, &inst->srv.output_desc,
				 strlen(inst->srv.output_desc));
	LOG_DBG("%s", inst->output_desc);
	return bt_gatt_attr_read(conn, attr, buf, len, offset, &inst->output_desc,
				 strlen(inst->output_desc));
}

#define BT_VOCS_SERVICE_DEFINITION(_vocs) { \
@@ -265,7 +265,7 @@ static ssize_t read_output_desc(struct bt_conn *conn, const struct bt_gatt_attr
	BT_AUDIO_CCC(output_desc_cfg_changed) \
	}

static struct bt_vocs vocs_insts[CONFIG_BT_VOCS_MAX_INSTANCE_COUNT];
static struct bt_vocs_server vocs_insts[CONFIG_BT_VOCS_MAX_INSTANCE_COUNT];
BT_GATT_SERVICE_INSTANCE_DEFINE(vocs_service_list, vocs_insts, CONFIG_BT_VOCS_MAX_INSTANCE_COUNT,
				BT_VOCS_SERVICE_DEFINITION);

@@ -277,11 +277,13 @@ struct bt_vocs *bt_vocs_free_instance_get(void)
		return NULL;
	}

	return &vocs_insts[instance_cnt++];
	return &vocs_insts[instance_cnt++].vocs;
}

void *bt_vocs_svc_decl_get(struct bt_vocs *vocs)
{
	struct bt_vocs_server *inst;

	CHECKIF(!vocs) {
		LOG_DBG("Null VOCS pointer");
		return NULL;
@@ -292,19 +294,22 @@ void *bt_vocs_svc_decl_get(struct bt_vocs *vocs)
		return NULL;
	}

	return vocs->srv.service_p->attrs;
	inst = CONTAINER_OF(vocs, struct bt_vocs_server, vocs);

	return inst->service_p->attrs;
}

static void prepare_vocs_instances(void)
{
	for (int i = 0; i < ARRAY_SIZE(vocs_insts); i++) {
		vocs_insts[i].srv.service_p = &vocs_service_list[i];
		vocs_insts[i].service_p = &vocs_service_list[i];
	}
}

int bt_vocs_register(struct bt_vocs *vocs,
		     const struct bt_vocs_register_param *param)
{
	struct bt_vocs_server *inst;
	int err;
	struct bt_gatt_attr *attr;
	struct bt_gatt_chrc *chrc;
@@ -320,6 +325,8 @@ int bt_vocs_register(struct bt_vocs *vocs,
		return -EINVAL;
	}

	inst = CONTAINER_OF(vocs, struct bt_vocs_server, vocs);

	CHECKIF(!param) {
		LOG_DBG("NULL params pointer");
		return -EINVAL;
@@ -330,7 +337,7 @@ int bt_vocs_register(struct bt_vocs *vocs,
		instances_prepared = true;
	}

	CHECKIF(vocs->srv.initialized) {
	CHECKIF(inst->initialized) {
		LOG_DBG("Already initialized VOCS instance");
		return -EALREADY;
	}
@@ -340,18 +347,18 @@ int bt_vocs_register(struct bt_vocs *vocs,
		return -EINVAL;
	}

	vocs->srv.location = param->location;
	vocs->srv.state.offset = param->offset;
	vocs->srv.cb = param->cb;
	inst->location = param->location;
	inst->state.offset = param->offset;
	inst->cb = param->cb;

	if (param->output_desc) {
		strncpy(vocs->srv.output_desc, param->output_desc,
			sizeof(vocs->srv.output_desc) - 1);
		strncpy(inst->output_desc, param->output_desc,
			sizeof(inst->output_desc) - 1);
		/* strncpy may not always null-terminate */
		vocs->srv.output_desc[sizeof(vocs->srv.output_desc) - 1] = '\0';
		inst->output_desc[sizeof(inst->output_desc) - 1] = '\0';
		if (IS_ENABLED(CONFIG_BT_VOCS_LOG_LEVEL_DBG) &&
		    strcmp(vocs->srv.output_desc, param->output_desc)) {
			LOG_DBG("Output desc clipped to %s", vocs->srv.output_desc);
		    strcmp(inst->output_desc, param->output_desc)) {
			LOG_DBG("Output desc clipped to %s", inst->output_desc);
		}
	}

@@ -361,30 +368,30 @@ int bt_vocs_register(struct bt_vocs *vocs,
	 * also update the characteristic declaration (always found at [i - 1]) with the
	 * BT_GATT_CHRC_WRITE_WITHOUT_RESP property.
	 */
	for (int i = 1; i < vocs->srv.service_p->attr_count; i++) {
		attr = &vocs->srv.service_p->attrs[i];
	for (int i = 1; i < inst->service_p->attr_count; i++) {
		attr = &inst->service_p->attrs[i];

		if (param->location_writable && !bt_uuid_cmp(attr->uuid, BT_UUID_VOCS_LOCATION)) {
			/* Update attr and chrc to be writable */
			chrc = vocs->srv.service_p->attrs[i - 1].user_data;
			chrc = inst->service_p->attrs[i - 1].user_data;
			attr->perm |= BT_GATT_PERM_WRITE_ENCRYPT;
			chrc->properties |= BT_GATT_CHRC_WRITE_WITHOUT_RESP;
		} else if (param->desc_writable &&
			   !bt_uuid_cmp(attr->uuid, BT_UUID_VOCS_DESCRIPTION)) {
			/* Update attr and chrc to be writable */
			chrc = vocs->srv.service_p->attrs[i - 1].user_data;
			chrc = inst->service_p->attrs[i - 1].user_data;
			attr->perm |= BT_GATT_PERM_WRITE_ENCRYPT;
			chrc->properties |= BT_GATT_CHRC_WRITE_WITHOUT_RESP;
		}
	}

	err = bt_gatt_service_register(vocs->srv.service_p);
	err = bt_gatt_service_register(inst->service_p);
	if (err) {
		LOG_DBG("Could not register VOCS service");
		return err;
	}

	vocs->srv.initialized = true;
	inst->initialized = true;
	return 0;
}
#endif /* CONFIG_BT_VOCS */
@@ -397,10 +404,14 @@ int bt_vocs_state_get(struct bt_vocs *inst)
	}

	if (IS_ENABLED(CONFIG_BT_VOCS_CLIENT) && inst->client_instance) {
		return bt_vocs_client_state_get(inst);
		struct bt_vocs_client *cli = CONTAINER_OF(inst, struct bt_vocs_client, vocs);

		return bt_vocs_client_state_get(cli);
	} else if (IS_ENABLED(CONFIG_BT_VOCS) && !inst->client_instance) {
		if (inst->srv.cb && inst->srv.cb->state) {
			inst->srv.cb->state(inst, 0, inst->srv.state.offset);
		struct bt_vocs_server *srv = CONTAINER_OF(inst, struct bt_vocs_server, vocs);

		if (srv->cb && srv->cb->state) {
			srv->cb->state(inst, 0, srv->state.offset);
		}
		return 0;
	}
@@ -416,10 +427,14 @@ int bt_vocs_location_get(struct bt_vocs *inst)
	}

	if (IS_ENABLED(CONFIG_BT_VOCS_CLIENT) && inst->client_instance) {
		return bt_vocs_client_location_get(inst);
		struct bt_vocs_client *cli = CONTAINER_OF(inst, struct bt_vocs_client, vocs);

		return bt_vocs_client_location_get(cli);
	} else if (IS_ENABLED(CONFIG_BT_VOCS) && !inst->client_instance) {
		if (inst->srv.cb && inst->srv.cb->location) {
			inst->srv.cb->location(inst, 0, inst->srv.location);
		struct bt_vocs_server *srv = CONTAINER_OF(inst, struct bt_vocs_server, vocs);

		if (srv->cb && srv->cb->location) {
			srv->cb->location(inst, 0, srv->location);
		}
		return 0;
	}
@@ -435,9 +450,13 @@ int bt_vocs_location_set(struct bt_vocs *inst, uint32_t location)
	}

	if (IS_ENABLED(CONFIG_BT_VOCS_CLIENT) && inst->client_instance) {
		return bt_vocs_client_location_set(inst, location);
		struct bt_vocs_client *cli = CONTAINER_OF(inst, struct bt_vocs_client, vocs);

		return bt_vocs_client_location_set(cli, location);
	} else if (IS_ENABLED(CONFIG_BT_VOCS) && !inst->client_instance) {
		return vocs_write(inst, write_location, &location, sizeof(location));
		struct bt_vocs_server *srv = CONTAINER_OF(inst, struct bt_vocs_server, vocs);

		return vocs_write(srv, write_location, &location, sizeof(location));
	}

	return -ENOTSUP;
@@ -451,15 +470,18 @@ int bt_vocs_state_set(struct bt_vocs *inst, int16_t offset)
	}

	if (IS_ENABLED(CONFIG_BT_VOCS_CLIENT) && inst->client_instance) {
		return bt_vocs_client_state_set(inst, offset);
		struct bt_vocs_client *cli = CONTAINER_OF(inst, struct bt_vocs_client, vocs);

		return bt_vocs_client_state_set(cli, offset);
	} else if (IS_ENABLED(CONFIG_BT_VOCS) && !inst->client_instance) {
		struct bt_vocs_server *srv = CONTAINER_OF(inst, struct bt_vocs_server, vocs);
		struct bt_vocs_control cp;

		cp.opcode = BT_VOCS_OPCODE_SET_OFFSET;
		cp.counter = inst->srv.state.change_counter;
		cp.counter = srv->state.change_counter;
		cp.offset = sys_cpu_to_le16(offset);

		return vocs_write(inst, write_vocs_control, &cp, sizeof(cp));
		return vocs_write(srv, write_vocs_control, &cp, sizeof(cp));
	}

	return -ENOTSUP;
@@ -473,10 +495,14 @@ int bt_vocs_description_get(struct bt_vocs *inst)
	}

	if (IS_ENABLED(CONFIG_BT_VOCS_CLIENT) && inst->client_instance) {
		return bt_vocs_client_description_get(inst);
		struct bt_vocs_client *cli = CONTAINER_OF(inst, struct bt_vocs_client, vocs);

		return bt_vocs_client_description_get(cli);
	} else if (IS_ENABLED(CONFIG_BT_VOCS) && !inst->client_instance) {
		if (inst->srv.cb && inst->srv.cb->description) {
			inst->srv.cb->description(inst, 0, inst->srv.output_desc);
		struct bt_vocs_server *srv = CONTAINER_OF(inst, struct bt_vocs_server, vocs);

		if (srv->cb && srv->cb->description) {
			srv->cb->description(inst, 0, srv->output_desc);
		}
		return 0;
	}
@@ -497,9 +523,13 @@ int bt_vocs_description_set(struct bt_vocs *inst, const char *description)
	}

	if (IS_ENABLED(CONFIG_BT_VOCS_CLIENT) && inst->client_instance) {
		return bt_vocs_client_description_set(inst, description);
		struct bt_vocs_client *cli = CONTAINER_OF(inst, struct bt_vocs_client, vocs);

		return bt_vocs_client_description_set(cli, description);
	} else if (IS_ENABLED(CONFIG_BT_VOCS) && !inst->client_instance) {
		return vocs_write(inst, write_output_desc, description, strlen(description));
		struct bt_vocs_server *srv = CONTAINER_OF(inst, struct bt_vocs_server, vocs);

		return vocs_write(srv, write_output_desc, description, strlen(description));
	}

	return -ENOTSUP;
+202 −197

File changed.

Preview size limit exceeded, changes collapsed.

+12 −15
Original line number Diff line number Diff line
@@ -30,7 +30,12 @@ struct bt_vocs_state {
	uint8_t change_counter;
} __packed;

struct bt_vocs {
	bool client_instance;
};

struct bt_vocs_client {
	struct bt_vocs vocs;
	struct bt_vocs_state state;
	bool location_writable;
	uint32_t location;
@@ -58,6 +63,7 @@ struct bt_vocs_client {
};

struct bt_vocs_server {
	struct bt_vocs vocs;
	struct bt_vocs_state state;
	uint32_t location;
	bool initialized;
@@ -67,20 +73,11 @@ struct bt_vocs_server {
	struct bt_gatt_service *service_p;
};

struct bt_vocs {
	bool client_instance;
	union {
		struct bt_vocs_server srv;
		struct bt_vocs_client cli;
	};
};

int bt_vocs_client_state_get(struct bt_vocs *inst);
int bt_vocs_client_state_set(struct bt_vocs *inst, int16_t offset);
int bt_vocs_client_location_get(struct bt_vocs *inst);
int bt_vocs_client_location_set(struct bt_vocs *inst, uint32_t location);
int bt_vocs_client_description_get(struct bt_vocs *inst);
int bt_vocs_client_description_set(struct bt_vocs *inst,
				   const char *description);
int bt_vocs_client_state_get(struct bt_vocs_client *inst);
int bt_vocs_client_state_set(struct bt_vocs_client *inst, int16_t offset);
int bt_vocs_client_location_get(struct bt_vocs_client *inst);
int bt_vocs_client_location_set(struct bt_vocs_client *inst, uint32_t location);
int bt_vocs_client_description_get(struct bt_vocs_client *inst);
int bt_vocs_client_description_set(struct bt_vocs_client *inst, const char *description);

#endif /* ZEPHYR_INCLUDE_BLUETOOTH_AUDIO_VOCS_INTERNAL_ */