2023-06-08 12:24:42

by Nitin Jadhav

[permalink] [raw]
Subject: [PATCH BlueZ v3 0/2] Code handling for VOCS service

Hello Maintainers

This patch series handles code implementation for mandatory features of Volume Offset Control Service.

Implementation of following features have been handled in this patch series
- Volume Offset Control Service
- Volume Offset State Characteristic (Read, Notify)
- Audio Location Characteristic (Read)
- Volume Offset Control Point Characteristic (Write)
- Audio Output Description Characteristic (Read)
- Set Volume Offset

Reference Document:
- Code implementation is based on VOCS_v1.0.pdf
- Testing is done using PTS in reference to VOCS.TS.p1.pdf
- The verdict is PASS for all mandatory test cases.

Thank you in advance for your review.

Warm Regards
Nitin Jadhav


Nitin Jadhav (2):
Title: Added initial code for handling VOCS
Make VOCS (Secondary) as an included service of VCS (Primary)

lib/uuid.h | 5 +
src/shared/vcp.c | 563 ++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 566 insertions(+), 2 deletions(-)

--
2.34.1



2023-06-08 12:24:58

by Nitin Jadhav

[permalink] [raw]
Subject: [PATCH BlueZ v3 1/2] Title: Added initial code for handling VOCS

Summary:
- This adds implementation for VOCS service and characteristics
- Implementation based on VOCS_v1.0.pdf specification
- Tested using PTS with reference to VOCS.TS.p1.pdf
---
v2: Corrected prefixs and cosmetic changes (Luiz Augusto von Dentz)
v3: Commit message modified and fixed long line length warning (Paul
Menzel)
---
lib/uuid.h | 5 +
src/shared/vcp.c | 547 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 552 insertions(+)

diff --git a/lib/uuid.h b/lib/uuid.h
index 5cdfedb4b..cd3b3655f 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -179,6 +179,11 @@ extern "C" {
#define VOL_CP_CHRC_UUID 0x2B7E
#define VOL_FLAG_CHRC_UUID 0x2B7F

+#define VOCS_STATE_CHAR_UUID 0x2B80
+#define VOCS_AUDIO_LOC_CHRC_UUID 0x2B81
+#define VOCS_CP_CHRC_UUID 0x2B82
+#define VOCS_AUDIO_OP_DESC_CHAR_UUID 0x2B83
+
#define GMCS_UUID 0x1849
#define MEDIA_PLAYER_NAME_CHRC_UUID 0x2b93
#define MEDIA_TRACK_CHNGD_CHRC_UUID 0x2b96
diff --git a/src/shared/vcp.c b/src/shared/vcp.c
index 5459cf892..aa75f498a 100644
--- a/src/shared/vcp.c
+++ b/src/shared/vcp.c
@@ -36,9 +36,40 @@
#define BT_ATT_ERROR_INVALID_CHANGE_COUNTER 0x80
#define BT_ATT_ERROR_OPCODE_NOT_SUPPORTED 0x81

+#define BT_VCP_NA 0x00000000
+#define BT_VCP_FRONT_LEFT 0x00000001
+#define BT_VCP_FRONT_RIGHT 0x00000002
+#define BT_VCP_FRONT_CENTER 0x00000004
+#define BT_VCP_LOW_FRQ_EFF_1 0x00000008
+#define BT_VCP_BACK_LEFT 0x00000010
+#define BT_VCP_BACK_RIGHT 0x00000020
+#define BT_VCP_FRONT_LEFT_CENTER 0x00000040
+#define BT_VCP_FRONT_RIGHT_CENTER 0x00000080
+#define BT_VCP_BACK_CENTER 0x00000100
+#define BT_VCP_LOW_FRQ_EFF_2 0x00000200
+#define BT_VCP_SIDE_LEFT 0x00000400
+#define BT_VCP_SIDE_RIGHT 0x00000800
+#define BT_VCP_TOP_FRONT_LEFT 0x00001000
+#define BT_VCP_TOP_FRONT_RIGHT 0x00002000
+#define BT_VCP_TOP_FRONT_CENTER 0x00004000
+#define BT_VCP_TOP_CENTER 0x00008000
+#define BT_VCP_TOP_BACK_LEFT 0x00010000
+#define BT_VCP_TOP_BACK_RIGHT 0x00020000
+#define BT_VCP_TOP_SIDE_LEFT 0x00040000
+#define BT_VCP_TOP_SIDE_RIGHT 0x00080000
+#define BT_VCP_TOP_BACK_CENTER 0x00100000
+#define BT_VCP_BOTTOM_FRONT_CENTER 0x00200000
+#define BT_VCP_BOTTOM_FRONT_LEFT 0x00400000
+#define BT_VCP_BOTTOM_FRONT_RIGHT 0x00800000
+#define BT_VCP_FRONT_LEFT_WIDE 0x01000000
+#define BT_VCP_FRONT_RIGHT_WIDE 0x02000000
+#define BT_VCP_LEFT_SURROUND 0x04000000
+#define BT_VCP_RIGHT_SURROUND 0x08000000
+
struct bt_vcp_db {
struct gatt_db *db;
struct bt_vcs *vcs;
+ struct bt_vocs *vocs;
};

typedef void (*vcp_func_t)(struct bt_vcp *vcp, bool success, uint8_t att_ecode,
@@ -57,11 +88,21 @@ struct bt_vcs_param {
uint8_t change_counter;
} __packed;

+struct bt_vocs_param {
+ uint8_t op;
+ uint8_t change_counter;
+} __packed;
+
struct bt_vcs_ab_vol {
uint8_t change_counter;
uint8_t vol_set;
} __packed;

+struct bt_vocs_set_vol_off {
+ uint8_t change_counter;
+ uint8_t set_vol_offset;
+} __packed;
+
struct bt_vcp_cb {
unsigned int id;
bt_vcp_func_t attached;
@@ -89,6 +130,10 @@ struct bt_vcp {
unsigned int vstate_id;
unsigned int vflag_id;

+ unsigned int state_id;
+ unsigned int audio_loc_id;
+ unsigned int ao_dec_id;
+
struct queue *notify;
struct queue *pending;

@@ -120,6 +165,27 @@ struct bt_vcs {
struct gatt_db_attribute *vf_ccc;
};

+/* Contains local bt_vcp_db */
+struct vol_offset_state {
+ uint16_t vol_offset;
+ uint8_t counter;
+} __packed;
+
+struct bt_vocs {
+ struct bt_vcp_db *vdb;
+ struct vol_offset_state *vostate;
+ uint32_t vocs_audio_loc;
+ char *vocs_ao_dec;
+ struct gatt_db_attribute *service;
+ struct gatt_db_attribute *vos;
+ struct gatt_db_attribute *vos_ccc;
+ struct gatt_db_attribute *voal;
+ struct gatt_db_attribute *voal_ccc;
+ struct gatt_db_attribute *vo_cp;
+ struct gatt_db_attribute *voaodec;
+ struct gatt_db_attribute *voaodec_ccc;
+};
+
static struct queue *vcp_db;
static struct queue *vcp_cbs;
static struct queue *sessions;
@@ -159,6 +225,17 @@ static struct vol_state *vdb_get_vstate(struct bt_vcp_db *vdb)
return NULL;
}

+static struct vol_offset_state *vdb_get_vostate(struct bt_vcp_db *vdb)
+{
+ if (!vdb->vocs)
+ return NULL;
+
+ if (vdb->vocs->vostate)
+ return vdb->vocs->vostate;
+
+ return NULL;
+}
+
static struct bt_vcs *vcp_get_vcs(struct bt_vcp *vcp)
{
if (!vcp)
@@ -173,6 +250,20 @@ static struct bt_vcs *vcp_get_vcs(struct bt_vcp *vcp)
return vcp->rdb->vcs;
}

+static struct bt_vocs *vcp_get_vocs(struct bt_vcp *vcp)
+{
+ if (!vcp)
+ return NULL;
+
+ if (vcp->rdb->vocs)
+ return vcp->rdb->vocs;
+
+ vcp->rdb->vocs = new0(struct bt_vocs, 1);
+ vcp->rdb->vocs->vdb = vcp->rdb;
+
+ return vcp->rdb->vocs;
+}
+
static void vcp_detached(void *data, void *user_data)
{
struct bt_vcp_cb *cb = data;
@@ -202,6 +293,7 @@ static void vcp_db_free(void *data)
gatt_db_unref(vdb->db);

free(vdb->vcs);
+ free(vdb->vocs);
free(vdb);
}

@@ -583,6 +675,45 @@ static uint8_t vcs_mute(struct bt_vcs *vcs, struct bt_vcp *vcp,
return 0;
}

+static uint8_t vocs_set_vol_offset(struct bt_vocs *vocs, struct bt_vcp *vcp,
+ struct iovec *iov)
+{
+ struct bt_vcp_db *vdb;
+ struct vol_offset_state *vstate;
+ struct bt_vocs_set_vol_off *req;
+
+ DBG(vcp, "Set Volume Offset");
+
+ vdb = vcp_get_vdb(vcp);
+ if (!vdb) {
+ DBG(vcp, "error: VDB not available");
+ return 0;
+ }
+
+ vstate = vdb_get_vostate(vdb);
+ if (!vstate) {
+ DBG(vcp, "error: VSTATE not available");
+ return 0;
+ }
+
+ req = iov_pull_mem(iov, sizeof(*req));
+ if (!req)
+ return 0;
+
+ if (req->change_counter != vstate->counter) {
+ DBG(vcp, "Change Counter Mismatch Volume not decremented!");
+ return BT_ATT_ERROR_INVALID_CHANGE_COUNTER;
+ }
+
+ vstate->vol_offset = req->set_vol_offset;
+ vstate->counter = -~vstate->counter; /*Increment Change Counter*/
+
+ gatt_db_attribute_notify(vdb->vocs->vos, (void *)vstate,
+ sizeof(struct vol_offset_state),
+ bt_vcp_get_att(vcp));
+ return 0;
+}
+
#define BT_VCS_REL_VOL_DOWN 0x00
#define BT_VCS_REL_VOL_UP 0x01
#define BT_VCS_UNMUTE_REL_VOL_DOWN 0x02
@@ -591,6 +722,8 @@ static uint8_t vcs_mute(struct bt_vcs *vcs, struct bt_vcp *vcp,
#define BT_VCS_UNMUTE 0x05
#define BT_VCS_MUTE 0x06

+#define BT_VOCS_SET_VOL_OFFSET 0x01
+
#define VCS_OP(_str, _op, _size, _func) \
{ \
.str = _str, \
@@ -623,6 +756,26 @@ struct vcs_op_handler {
{}
};

+#define VOCS_OP(_str, _op, _size, _func) \
+ { \
+ .str = _str, \
+ .op = _op, \
+ .size = _size, \
+ .func = _func, \
+ }
+
+struct vocs_op_handler {
+ const char *str;
+ uint8_t op;
+ size_t size;
+ uint8_t (*func)(struct bt_vocs *vocs, struct bt_vcp *vcp,
+ struct iovec *iov);
+} vocp_handlers[] = {
+ VOCS_OP("Set Volume Offset", BT_VOCS_SET_VOL_OFFSET,
+ sizeof(uint8_t), vocs_set_vol_offset),
+ {}
+};
+
static void vcs_cp_write(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
const uint8_t *value, size_t len,
@@ -683,6 +836,66 @@ respond:
gatt_db_attribute_write_result(attrib, id, ret);
}

+static void vocs_cp_write(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct bt_vocs *vocs = user_data;
+ struct bt_vcp *vcp = vcp_get_session(att, vocs->vdb->db);
+ struct iovec iov = {
+ .iov_base = (void *) value,
+ .iov_len = len,
+ };
+ uint8_t *vcp_op;
+ struct vocs_op_handler *handler;
+ uint8_t ret = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+
+ DBG(vcp, "VOCP Control Point Write");
+
+ if (offset) {
+ DBG(vcp, "invalid offset %d", offset);
+ ret = BT_ATT_ERROR_INVALID_OFFSET;
+ goto respond;
+ }
+
+ if (len < sizeof(*vcp_op)) {
+ DBG(vcp, "invalid len %ld < %ld sizeof(*param)", len,
+ sizeof(*vcp_op));
+ ret = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+ goto respond;
+ }
+
+ vcp_op = iov_pull_mem(&iov, sizeof(*vcp_op));
+
+ for (handler = vocp_handlers; handler && handler->str; handler++) {
+ if (handler->op != *vcp_op)
+ continue;
+
+ if (iov.iov_len < handler->size) {
+ DBG(vcp, "invalid len %ld < %ld handler->size", len,
+ handler->size);
+ ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED;
+ goto respond;
+ }
+
+ break;
+ }
+
+ if (handler && handler->str) {
+ DBG(vcp, "%s", handler->str);
+
+ ret = handler->func(vocs, vcp, &iov);
+ } else {
+ DBG(vcp, "Unknown opcode 0x%02x", *vcp_op);
+ ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED;
+ }
+
+respond:
+ gatt_db_attribute_write_result(attrib, id, ret);
+}
+
static void vcs_state_read(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, struct bt_att *att,
@@ -698,6 +911,21 @@ static void vcs_state_read(struct gatt_db_attribute *attrib,
iov.iov_len);
}

+static void vocs_state_read(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct bt_vocs *vocs = user_data;
+ struct iovec iov;
+
+ iov.iov_base = vocs->vostate;
+ iov.iov_len = sizeof(*vocs->vostate);
+
+ gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base,
+ iov.iov_len);
+}
+
static void vcs_flag_read(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, struct bt_att *att,
@@ -713,6 +941,36 @@ static void vcs_flag_read(struct gatt_db_attribute *attrib,
iov.iov_len);
}

+static void vocs_voal_read(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct bt_vocs *vocs = user_data;
+ struct iovec iov;
+
+ iov.iov_base = &vocs->vocs_audio_loc;
+ iov.iov_len = sizeof(vocs->vocs_audio_loc);
+
+ gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base,
+ iov.iov_len);
+}
+
+static void vocs_voaodec_read(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct bt_vocs *vocs = user_data;
+ struct iovec iov;
+
+ iov.iov_base = &vocs->vocs_ao_dec;
+ iov.iov_len = strlen(vocs->vocs_ao_dec);
+
+ gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base,
+ iov.iov_len);
+}
+
static struct bt_vcs *vcs_new(struct gatt_db *db)
{
struct bt_vcs *vcs;
@@ -771,6 +1029,74 @@ static struct bt_vcs *vcs_new(struct gatt_db *db)
return vcs;
}

+static struct bt_vocs *vocs_new(struct gatt_db *db)
+{
+ struct bt_vocs *vocs;
+ struct vol_offset_state *vostate;
+ bt_uuid_t uuid;
+
+ if (!db)
+ return NULL;
+
+ vocs = new0(struct bt_vocs, 1);
+
+ vostate = new0(struct vol_offset_state, 1);
+
+ vocs->vostate = vostate;
+ vocs->vocs_audio_loc = BT_VCP_FRONT_LEFT;
+ vocs->vocs_ao_dec = "Left Speaker";
+
+ /* Populate DB with VOCS attributes */
+ bt_uuid16_create(&uuid, VOL_OFFSET_CS_UUID);
+ vocs->service = gatt_db_add_service(db, &uuid, true, 9);
+
+ bt_uuid16_create(&uuid, VOCS_STATE_CHAR_UUID);
+ vocs->vos = gatt_db_service_add_characteristic(vocs->service,
+ &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_NOTIFY,
+ vocs_state_read, NULL,
+ vocs);
+
+ vocs->vos_ccc = gatt_db_service_add_ccc(vocs->service,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+
+ bt_uuid16_create(&uuid, VOCS_AUDIO_LOC_CHRC_UUID);
+ vocs->voal = gatt_db_service_add_characteristic(vocs->service,
+ &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_NOTIFY,
+ vocs_voal_read, NULL,
+ vocs);
+
+ vocs->voal_ccc = gatt_db_service_add_ccc(vocs->service,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+
+ bt_uuid16_create(&uuid, VOCS_CP_CHRC_UUID);
+ vocs->vo_cp = gatt_db_service_add_characteristic(vocs->service,
+ &uuid,
+ BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_WRITE,
+ NULL, vocs_cp_write,
+ vocs);
+
+ bt_uuid16_create(&uuid, VOCS_AUDIO_OP_DESC_CHAR_UUID);
+ vocs->voaodec = gatt_db_service_add_characteristic(vocs->service,
+ &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_NOTIFY,
+ vocs_voaodec_read, NULL,
+ vocs);
+
+ vocs->voaodec_ccc = gatt_db_service_add_ccc(vocs->service,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+
+ return vocs;
+}
+
static struct bt_vcp_db *vcp_db_new(struct gatt_db *db)
{
struct bt_vcp_db *vdb;
@@ -787,6 +1113,9 @@ static struct bt_vcp_db *vcp_db_new(struct gatt_db *db)
vdb->vcs = vcs_new(db);
vdb->vcs->vdb = vdb;

+ vdb->vocs = vocs_new(db);
+ vdb->vocs->vdb = vdb;
+
queue_push_tail(vcp_db, vdb);

return vdb;
@@ -911,6 +1240,44 @@ static void vcp_vstate_notify(struct bt_vcp *vcp, uint16_t value_handle,
DBG(vcp, "Vol Counter 0x%x", vstate.counter);
}

+static void vcp_voffset_state_notify(struct bt_vcp *vcp, uint16_t value_handle,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
+{
+ struct vol_offset_state vostate;
+
+ memcpy(&vostate, value, sizeof(struct vol_offset_state));
+
+ DBG(vcp, "Vol Offset 0x%x", vostate.vol_offset);
+ DBG(vcp, "Vol Offset Counter 0x%x", vostate.counter);
+}
+
+static void vcp_audio_loc_notify(struct bt_vcp *vcp, uint16_t value_handle,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
+{
+ uint32_t *vocs_audio_loc_n = 0;
+
+ if (value != NULL)
+ memcpy(vocs_audio_loc_n, value, sizeof(uint32_t));
+
+ DBG(vcp, "VOCS Audio Location 0x%x", *vocs_audio_loc_n);
+}
+
+
+static void vcp_audio_descriptor_notify(struct bt_vcp *vcp,
+ uint16_t value_handle,
+ const uint8_t *value,
+ uint16_t length,
+ void *user_data)
+{
+ char vocs_audio_dec_n[256] = {'\0'};
+
+ memcpy(vocs_audio_dec_n, value, length);
+
+ DBG(vcp, "VOCS Audio Descriptor 0x%s", *vocs_audio_dec_n);
+}
+
static void vcp_vflag_notify(struct bt_vcp *vcp, uint16_t value_handle,
const uint8_t *value, uint16_t length,
void *user_data)
@@ -972,6 +1339,86 @@ static void read_vol_state(struct bt_vcp *vcp, bool success, uint8_t att_ecode,
DBG(vcp, "Vol Counter:%x", vs->counter);
}

+static void read_vol_offset_state(struct bt_vcp *vcp, bool success,
+ uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
+{
+ struct vol_offset_state *vos;
+ struct iovec iov = {
+ .iov_base = (void *) value,
+ .iov_len = length,
+ };
+
+ if (!success) {
+ DBG(vcp, "Unable to read Vol Offset State: error 0x%02x",
+ att_ecode);
+ return;
+ }
+
+ vos = iov_pull_mem(&iov, sizeof(*vos));
+ if (!vos) {
+ DBG(vcp, "Unable to get Vol Offset State");
+ return;
+ }
+
+ DBG(vcp, "Vol Set:%x", vos->vol_offset);
+ DBG(vcp, "Vol Counter:%x", vos->counter);
+}
+
+static void read_vocs_audio_location(struct bt_vcp *vcp, bool success,
+ uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
+{
+ uint32_t *vocs_audio_loc;
+ struct iovec iov = {
+ .iov_base = (void *) value,
+ .iov_len = length,
+ };
+
+ if (!success) {
+ DBG(vcp, "Unable to read VOCS Audio Location: error 0x%02x",
+ att_ecode);
+ return;
+ }
+
+ vocs_audio_loc = iov_pull_mem(&iov, sizeof(uint32_t));
+ if (!*vocs_audio_loc) {
+ DBG(vcp, "Unable to get VOCS Audio Location");
+ return;
+ }
+
+ DBG(vcp, "VOCS Audio Loc:%x", *vocs_audio_loc);
+}
+
+
+static void read_vocs_audio_descriptor(struct bt_vcp *vcp, bool success,
+ uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
+{
+ char *vocs_ao_dec_r;
+ struct iovec iov = {
+ .iov_base = (void *) value,
+ .iov_len = length,
+ };
+
+ if (!success) {
+ DBG(vcp, "Unable to read VOCS Audio Descriptor: error 0x%02x",
+ att_ecode);
+ return;
+ }
+
+ vocs_ao_dec_r = iov_pull_mem(&iov, length);
+ if (!*vocs_ao_dec_r) {
+ DBG(vcp, "Unable to get VOCS Audio Descriptor");
+ return;
+ }
+
+ DBG(vcp, "VOCS Audio Descriptor:%s", *vocs_ao_dec_r);
+}
+
static void vcp_pending_destroy(void *data)
{
struct bt_vcp_pending *pending = data;
@@ -1128,6 +1575,90 @@ static void foreach_vcs_char(struct gatt_db_attribute *attr, void *user_data)
}
}

+static void foreach_vocs_char(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct bt_vcp *vcp = user_data;
+ uint16_t value_handle;
+ bt_uuid_t uuid, uuid_vostate, uuid_audio_loc, uuid_vo_cp,
+ uuid_audio_op_decs;
+ struct bt_vocs *vocs;
+
+ if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle,
+ NULL, NULL, &uuid))
+ return;
+
+ bt_uuid16_create(&uuid_vostate, VOCS_STATE_CHAR_UUID);
+ bt_uuid16_create(&uuid_audio_loc, VOCS_AUDIO_LOC_CHRC_UUID);
+ bt_uuid16_create(&uuid_vo_cp, VOCS_CP_CHRC_UUID);
+ bt_uuid16_create(&uuid_audio_op_decs, VOCS_AUDIO_OP_DESC_CHAR_UUID);
+
+ if (!bt_uuid_cmp(&uuid, &uuid_vostate)) {
+ DBG(vcp, "VOCS Vol state found: handle 0x%04x", value_handle);
+
+ vocs = vcp_get_vocs(vcp);
+ if (!vocs || vocs->vos)
+ return;
+
+ vocs->vos = attr;
+
+ vcp_read_value(vcp, value_handle, read_vol_offset_state, vcp);
+
+ vcp->state_id = vcp_register_notify(vcp, value_handle,
+ vcp_voffset_state_notify, NULL);
+
+ return;
+ }
+
+ if (!bt_uuid_cmp(&uuid, &uuid_audio_loc)) {
+ DBG(vcp, "VOCS Volume Audio Location found: handle 0x%04x",
+ value_handle);
+
+ vocs = vcp_get_vocs(vcp);
+ if (!vocs || vocs->voal)
+ return;
+
+ vocs->voal = attr;
+
+ vcp_read_value(vcp, value_handle, read_vocs_audio_location,
+ vcp);
+
+ vcp->audio_loc_id = vcp_register_notify(vcp, value_handle,
+ vcp_audio_loc_notify, NULL);
+
+ return;
+ }
+
+ if (!bt_uuid_cmp(&uuid, &uuid_vo_cp)) {
+ DBG(vcp, "VOCS Volume CP found: handle 0x%04x", value_handle);
+
+ vocs = vcp_get_vocs(vcp);
+ if (!vocs || vocs->vo_cp)
+ return;
+
+ vocs->vo_cp = attr;
+
+ return;
+ }
+
+ if (!bt_uuid_cmp(&uuid, &uuid_audio_op_decs)) {
+ DBG(vcp, "VOCS Vol Audio Descriptor found: handle 0x%04x",
+ value_handle);
+
+ vocs = vcp_get_vocs(vcp);
+ if (!vocs || vocs->voaodec)
+ return;
+
+ vocs->voaodec = attr;
+
+ vcp_read_value(vcp, value_handle, read_vocs_audio_descriptor,
+ vcp);
+ vcp->ao_dec_id = vcp_register_notify(vcp, value_handle,
+ vcp_audio_descriptor_notify, NULL);
+
+ }
+
+}
+
static void foreach_vcs_service(struct gatt_db_attribute *attr,
void *user_data)
{
@@ -1141,6 +1672,19 @@ static void foreach_vcs_service(struct gatt_db_attribute *attr,
gatt_db_service_foreach_char(attr, foreach_vcs_char, vcp);
}

+static void foreach_vocs_service(struct gatt_db_attribute *attr,
+ void *user_data)
+{
+ struct bt_vcp *vcp = user_data;
+ struct bt_vocs *vocs = vcp_get_vocs(vcp);
+
+ vocs->service = attr;
+
+ gatt_db_service_set_claimed(attr, true);
+
+ gatt_db_service_foreach_char(attr, foreach_vocs_char, vcp);
+}
+
bool bt_vcp_attach(struct bt_vcp *vcp, struct bt_gatt_client *client)
{
bt_uuid_t uuid;
@@ -1163,6 +1707,9 @@ bool bt_vcp_attach(struct bt_vcp *vcp, struct bt_gatt_client *client)
bt_uuid16_create(&uuid, VCS_UUID);
gatt_db_foreach_service(vcp->ldb->db, &uuid, foreach_vcs_service, vcp);

+ bt_uuid16_create(&uuid, VOL_OFFSET_CS_UUID);
+ gatt_db_foreach_service(vcp->ldb->db, &uuid, foreach_vocs_service, vcp);
+
return true;
}

--
2.34.1


2023-06-08 13:47:10

by bluez.test.bot

[permalink] [raw]
Subject: RE: Code handling for VOCS service

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=755311

---Test result---

Test Summary:
CheckPatch PASS 1.56 seconds
GitLint PASS 0.73 seconds
BuildEll PASS 27.18 seconds
BluezMake PASS 872.62 seconds
MakeCheck PASS 12.79 seconds
MakeDistcheck PASS 155.29 seconds
CheckValgrind PASS 255.44 seconds
CheckSmatch PASS 341.24 seconds
bluezmakeextell PASS 102.91 seconds
IncrementalBuild PASS 1436.49 seconds
ScanBuild WARNING 1046.77 seconds

Details
##############################
Test: ScanBuild - WARNING
Desc: Run Scan Build
Output:
src/shared/vcp.c:1273:3: warning: Null pointer passed to 1st parameter expecting 'nonnull'
memcpy(vocs_audio_loc_n, value, sizeof(uint32_t));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/shared/vcp.c:1275:39: warning: Dereference of null pointer (loaded from variable 'vocs_audio_loc_n')
DBG(vcp, "VOCS Audio Location 0x%x", *vocs_audio_loc_n);
^~~~~~~~~~~~~~~~~
src/shared/vcp.c:31:57: note: expanded from macro 'DBG'
vcp_debug(_vcp, "%s:%s() " fmt, __FILE__, __func__, ## arg)
^~~
2 warnings generated.



---
Regards,
Linux Bluetooth

2023-06-08 16:35:50

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: Code handling for VOCS service

Hi Nitin,

On Thu, Jun 8, 2023 at 6:47 AM <[email protected]> wrote:
>
> This is automated email and please do not reply to this email!
>
> Dear submitter,
>
> Thank you for submitting the patches to the linux bluetooth mailing list.
> This is a CI test results with your patch series:
> PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=755311
>
> ---Test result---
>
> Test Summary:
> CheckPatch PASS 1.56 seconds
> GitLint PASS 0.73 seconds
> BuildEll PASS 27.18 seconds
> BluezMake PASS 872.62 seconds
> MakeCheck PASS 12.79 seconds
> MakeDistcheck PASS 155.29 seconds
> CheckValgrind PASS 255.44 seconds
> CheckSmatch PASS 341.24 seconds
> bluezmakeextell PASS 102.91 seconds
> IncrementalBuild PASS 1436.49 seconds
> ScanBuild WARNING 1046.77 seconds
>
> Details
> ##############################
> Test: ScanBuild - WARNING
> Desc: Run Scan Build
> Output:
> src/shared/vcp.c:1273:3: warning: Null pointer passed to 1st parameter expecting 'nonnull'
> memcpy(vocs_audio_loc_n, value, sizeof(uint32_t));
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> src/shared/vcp.c:1275:39: warning: Dereference of null pointer (loaded from variable 'vocs_audio_loc_n')
> DBG(vcp, "VOCS Audio Location 0x%x", *vocs_audio_loc_n);
> ^~~~~~~~~~~~~~~~~
> src/shared/vcp.c:31:57: note: expanded from macro 'DBG'
> vcp_debug(_vcp, "%s:%s() " fmt, __FILE__, __func__, ## arg)
> ^~~
> 2 warnings generated.

Please have a look at these warnings.

>
>
> ---
> Regards,
> Linux Bluetooth
>


--
Luiz Augusto von Dentz

2023-06-08 16:35:50

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ v3 1/2] Title: Added initial code for handling VOCS

Hi Nitin,

On Thu, Jun 8, 2023 at 5:24 AM Nitin Jadhav <[email protected]> wrote:
>
> Summary:
> - This adds implementation for VOCS service and characteristics
> - Implementation based on VOCS_v1.0.pdf specification
> - Tested using PTS with reference to VOCS.TS.p1.pdf
> ---

Note, for the patch subject I usually recommend the following format:
<subdir/file>: <subject>, so in this case shared/vcp: Add initial code
for handling VOCS

> v2: Corrected prefixs and cosmetic changes (Luiz Augusto von Dentz)
> v3: Commit message modified and fixed long line length warning (Paul
> Menzel)
> ---
> lib/uuid.h | 5 +
> src/shared/vcp.c | 547 +++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 552 insertions(+)
>
> diff --git a/lib/uuid.h b/lib/uuid.h
> index 5cdfedb4b..cd3b3655f 100644
> --- a/lib/uuid.h
> +++ b/lib/uuid.h
> @@ -179,6 +179,11 @@ extern "C" {
> #define VOL_CP_CHRC_UUID 0x2B7E
> #define VOL_FLAG_CHRC_UUID 0x2B7F
>
> +#define VOCS_STATE_CHAR_UUID 0x2B80
> +#define VOCS_AUDIO_LOC_CHRC_UUID 0x2B81
> +#define VOCS_CP_CHRC_UUID 0x2B82
> +#define VOCS_AUDIO_OP_DESC_CHAR_UUID 0x2B83
> +
> #define GMCS_UUID 0x1849
> #define MEDIA_PLAYER_NAME_CHRC_UUID 0x2b93
> #define MEDIA_TRACK_CHNGD_CHRC_UUID 0x2b96

Split the uuid.h changes on its own patch.

> diff --git a/src/shared/vcp.c b/src/shared/vcp.c
> index 5459cf892..aa75f498a 100644
> --- a/src/shared/vcp.c
> +++ b/src/shared/vcp.c
> @@ -36,9 +36,40 @@
> #define BT_ATT_ERROR_INVALID_CHANGE_COUNTER 0x80
> #define BT_ATT_ERROR_OPCODE_NOT_SUPPORTED 0x81
>
> +#define BT_VCP_NA 0x00000000
> +#define BT_VCP_FRONT_LEFT 0x00000001
> +#define BT_VCP_FRONT_RIGHT 0x00000002
> +#define BT_VCP_FRONT_CENTER 0x00000004
> +#define BT_VCP_LOW_FRQ_EFF_1 0x00000008
> +#define BT_VCP_BACK_LEFT 0x00000010
> +#define BT_VCP_BACK_RIGHT 0x00000020
> +#define BT_VCP_FRONT_LEFT_CENTER 0x00000040
> +#define BT_VCP_FRONT_RIGHT_CENTER 0x00000080
> +#define BT_VCP_BACK_CENTER 0x00000100
> +#define BT_VCP_LOW_FRQ_EFF_2 0x00000200
> +#define BT_VCP_SIDE_LEFT 0x00000400
> +#define BT_VCP_SIDE_RIGHT 0x00000800
> +#define BT_VCP_TOP_FRONT_LEFT 0x00001000
> +#define BT_VCP_TOP_FRONT_RIGHT 0x00002000
> +#define BT_VCP_TOP_FRONT_CENTER 0x00004000
> +#define BT_VCP_TOP_CENTER 0x00008000
> +#define BT_VCP_TOP_BACK_LEFT 0x00010000
> +#define BT_VCP_TOP_BACK_RIGHT 0x00020000
> +#define BT_VCP_TOP_SIDE_LEFT 0x00040000
> +#define BT_VCP_TOP_SIDE_RIGHT 0x00080000
> +#define BT_VCP_TOP_BACK_CENTER 0x00100000
> +#define BT_VCP_BOTTOM_FRONT_CENTER 0x00200000
> +#define BT_VCP_BOTTOM_FRONT_LEFT 0x00400000
> +#define BT_VCP_BOTTOM_FRONT_RIGHT 0x00800000
> +#define BT_VCP_FRONT_LEFT_WIDE 0x01000000
> +#define BT_VCP_FRONT_RIGHT_WIDE 0x02000000
> +#define BT_VCP_LEFT_SURROUND 0x04000000
> +#define BT_VCP_RIGHT_SURROUND 0x08000000

You should probably use BIT macro to define the above values.

> struct bt_vcp_db {
> struct gatt_db *db;
> struct bt_vcs *vcs;
> + struct bt_vocs *vocs;
> };
>
> typedef void (*vcp_func_t)(struct bt_vcp *vcp, bool success, uint8_t att_ecode,
> @@ -57,11 +88,21 @@ struct bt_vcs_param {
> uint8_t change_counter;
> } __packed;
>
> +struct bt_vocs_param {
> + uint8_t op;
> + uint8_t change_counter;
> +} __packed;
> +
> struct bt_vcs_ab_vol {
> uint8_t change_counter;
> uint8_t vol_set;
> } __packed;
>
> +struct bt_vocs_set_vol_off {
> + uint8_t change_counter;
> + uint8_t set_vol_offset;
> +} __packed;
> +
> struct bt_vcp_cb {
> unsigned int id;
> bt_vcp_func_t attached;
> @@ -89,6 +130,10 @@ struct bt_vcp {
> unsigned int vstate_id;
> unsigned int vflag_id;
>
> + unsigned int state_id;
> + unsigned int audio_loc_id;
> + unsigned int ao_dec_id;
> +
> struct queue *notify;
> struct queue *pending;
>
> @@ -120,6 +165,27 @@ struct bt_vcs {
> struct gatt_db_attribute *vf_ccc;
> };
>
> +/* Contains local bt_vcp_db */
> +struct vol_offset_state {
> + uint16_t vol_offset;
> + uint8_t counter;
> +} __packed;
> +
> +struct bt_vocs {
> + struct bt_vcp_db *vdb;
> + struct vol_offset_state *vostate;
> + uint32_t vocs_audio_loc;
> + char *vocs_ao_dec;
> + struct gatt_db_attribute *service;
> + struct gatt_db_attribute *vos;
> + struct gatt_db_attribute *vos_ccc;
> + struct gatt_db_attribute *voal;
> + struct gatt_db_attribute *voal_ccc;
> + struct gatt_db_attribute *vo_cp;
> + struct gatt_db_attribute *voaodec;
> + struct gatt_db_attribute *voaodec_ccc;
> +};
> +
> static struct queue *vcp_db;
> static struct queue *vcp_cbs;
> static struct queue *sessions;
> @@ -159,6 +225,17 @@ static struct vol_state *vdb_get_vstate(struct bt_vcp_db *vdb)
> return NULL;
> }
>
> +static struct vol_offset_state *vdb_get_vostate(struct bt_vcp_db *vdb)
> +{
> + if (!vdb->vocs)
> + return NULL;
> +
> + if (vdb->vocs->vostate)
> + return vdb->vocs->vostate;
> +
> + return NULL;
> +}
> +
> static struct bt_vcs *vcp_get_vcs(struct bt_vcp *vcp)
> {
> if (!vcp)
> @@ -173,6 +250,20 @@ static struct bt_vcs *vcp_get_vcs(struct bt_vcp *vcp)
> return vcp->rdb->vcs;
> }
>
> +static struct bt_vocs *vcp_get_vocs(struct bt_vcp *vcp)
> +{
> + if (!vcp)
> + return NULL;
> +
> + if (vcp->rdb->vocs)
> + return vcp->rdb->vocs;
> +
> + vcp->rdb->vocs = new0(struct bt_vocs, 1);
> + vcp->rdb->vocs->vdb = vcp->rdb;
> +
> + return vcp->rdb->vocs;
> +}
> +
> static void vcp_detached(void *data, void *user_data)
> {
> struct bt_vcp_cb *cb = data;
> @@ -202,6 +293,7 @@ static void vcp_db_free(void *data)
> gatt_db_unref(vdb->db);
>
> free(vdb->vcs);
> + free(vdb->vocs);
> free(vdb);
> }
>
> @@ -583,6 +675,45 @@ static uint8_t vcs_mute(struct bt_vcs *vcs, struct bt_vcp *vcp,
> return 0;
> }
>
> +static uint8_t vocs_set_vol_offset(struct bt_vocs *vocs, struct bt_vcp *vcp,
> + struct iovec *iov)
> +{
> + struct bt_vcp_db *vdb;
> + struct vol_offset_state *vstate;
> + struct bt_vocs_set_vol_off *req;
> +
> + DBG(vcp, "Set Volume Offset");
> +
> + vdb = vcp_get_vdb(vcp);
> + if (!vdb) {
> + DBG(vcp, "error: VDB not available");
> + return 0;
> + }
> +
> + vstate = vdb_get_vostate(vdb);
> + if (!vstate) {
> + DBG(vcp, "error: VSTATE not available");
> + return 0;
> + }
> +
> + req = iov_pull_mem(iov, sizeof(*req));
> + if (!req)
> + return 0;
> +
> + if (req->change_counter != vstate->counter) {
> + DBG(vcp, "Change Counter Mismatch Volume not decremented!");
> + return BT_ATT_ERROR_INVALID_CHANGE_COUNTER;
> + }
> +
> + vstate->vol_offset = req->set_vol_offset;
> + vstate->counter = -~vstate->counter; /*Increment Change Counter*/
> +
> + gatt_db_attribute_notify(vdb->vocs->vos, (void *)vstate,
> + sizeof(struct vol_offset_state),
> + bt_vcp_get_att(vcp));
> + return 0;
> +}
> +
> #define BT_VCS_REL_VOL_DOWN 0x00
> #define BT_VCS_REL_VOL_UP 0x01
> #define BT_VCS_UNMUTE_REL_VOL_DOWN 0x02
> @@ -591,6 +722,8 @@ static uint8_t vcs_mute(struct bt_vcs *vcs, struct bt_vcp *vcp,
> #define BT_VCS_UNMUTE 0x05
> #define BT_VCS_MUTE 0x06
>
> +#define BT_VOCS_SET_VOL_OFFSET 0x01
> +
> #define VCS_OP(_str, _op, _size, _func) \
> { \
> .str = _str, \
> @@ -623,6 +756,26 @@ struct vcs_op_handler {
> {}
> };
>
> +#define VOCS_OP(_str, _op, _size, _func) \
> + { \
> + .str = _str, \
> + .op = _op, \
> + .size = _size, \
> + .func = _func, \
> + }
> +
> +struct vocs_op_handler {
> + const char *str;
> + uint8_t op;
> + size_t size;
> + uint8_t (*func)(struct bt_vocs *vocs, struct bt_vcp *vcp,
> + struct iovec *iov);
> +} vocp_handlers[] = {
> + VOCS_OP("Set Volume Offset", BT_VOCS_SET_VOL_OFFSET,
> + sizeof(uint8_t), vocs_set_vol_offset),
> + {}
> +};
> +
> static void vcs_cp_write(struct gatt_db_attribute *attrib,
> unsigned int id, uint16_t offset,
> const uint8_t *value, size_t len,
> @@ -683,6 +836,66 @@ respond:
> gatt_db_attribute_write_result(attrib, id, ret);
> }
>
> +static void vocs_cp_write(struct gatt_db_attribute *attrib,
> + unsigned int id, uint16_t offset,
> + const uint8_t *value, size_t len,
> + uint8_t opcode, struct bt_att *att,
> + void *user_data)
> +{
> + struct bt_vocs *vocs = user_data;
> + struct bt_vcp *vcp = vcp_get_session(att, vocs->vdb->db);
> + struct iovec iov = {
> + .iov_base = (void *) value,
> + .iov_len = len,
> + };
> + uint8_t *vcp_op;
> + struct vocs_op_handler *handler;
> + uint8_t ret = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
> +
> + DBG(vcp, "VOCP Control Point Write");
> +
> + if (offset) {
> + DBG(vcp, "invalid offset %d", offset);
> + ret = BT_ATT_ERROR_INVALID_OFFSET;
> + goto respond;
> + }
> +
> + if (len < sizeof(*vcp_op)) {
> + DBG(vcp, "invalid len %ld < %ld sizeof(*param)", len,
> + sizeof(*vcp_op));
> + ret = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
> + goto respond;
> + }
> +
> + vcp_op = iov_pull_mem(&iov, sizeof(*vcp_op));
> +
> + for (handler = vocp_handlers; handler && handler->str; handler++) {
> + if (handler->op != *vcp_op)
> + continue;
> +
> + if (iov.iov_len < handler->size) {
> + DBG(vcp, "invalid len %ld < %ld handler->size", len,
> + handler->size);
> + ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED;
> + goto respond;
> + }
> +
> + break;
> + }
> +
> + if (handler && handler->str) {
> + DBG(vcp, "%s", handler->str);
> +
> + ret = handler->func(vocs, vcp, &iov);
> + } else {
> + DBG(vcp, "Unknown opcode 0x%02x", *vcp_op);
> + ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED;
> + }
> +
> +respond:
> + gatt_db_attribute_write_result(attrib, id, ret);
> +}
> +
> static void vcs_state_read(struct gatt_db_attribute *attrib,
> unsigned int id, uint16_t offset,
> uint8_t opcode, struct bt_att *att,
> @@ -698,6 +911,21 @@ static void vcs_state_read(struct gatt_db_attribute *attrib,
> iov.iov_len);
> }
>
> +static void vocs_state_read(struct gatt_db_attribute *attrib,
> + unsigned int id, uint16_t offset,
> + uint8_t opcode, struct bt_att *att,
> + void *user_data)
> +{
> + struct bt_vocs *vocs = user_data;
> + struct iovec iov;
> +
> + iov.iov_base = vocs->vostate;
> + iov.iov_len = sizeof(*vocs->vostate);
> +
> + gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base,
> + iov.iov_len);
> +}
> +
> static void vcs_flag_read(struct gatt_db_attribute *attrib,
> unsigned int id, uint16_t offset,
> uint8_t opcode, struct bt_att *att,
> @@ -713,6 +941,36 @@ static void vcs_flag_read(struct gatt_db_attribute *attrib,
> iov.iov_len);
> }
>
> +static void vocs_voal_read(struct gatt_db_attribute *attrib,
> + unsigned int id, uint16_t offset,
> + uint8_t opcode, struct bt_att *att,
> + void *user_data)
> +{
> + struct bt_vocs *vocs = user_data;
> + struct iovec iov;
> +
> + iov.iov_base = &vocs->vocs_audio_loc;
> + iov.iov_len = sizeof(vocs->vocs_audio_loc);
> +
> + gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base,
> + iov.iov_len);
> +}
> +
> +static void vocs_voaodec_read(struct gatt_db_attribute *attrib,
> + unsigned int id, uint16_t offset,
> + uint8_t opcode, struct bt_att *att,
> + void *user_data)
> +{
> + struct bt_vocs *vocs = user_data;
> + struct iovec iov;
> +
> + iov.iov_base = &vocs->vocs_ao_dec;
> + iov.iov_len = strlen(vocs->vocs_ao_dec);
> +
> + gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base,
> + iov.iov_len);
> +}
> +
> static struct bt_vcs *vcs_new(struct gatt_db *db)
> {
> struct bt_vcs *vcs;
> @@ -771,6 +1029,74 @@ static struct bt_vcs *vcs_new(struct gatt_db *db)
> return vcs;
> }
>
> +static struct bt_vocs *vocs_new(struct gatt_db *db)
> +{
> + struct bt_vocs *vocs;
> + struct vol_offset_state *vostate;
> + bt_uuid_t uuid;
> +
> + if (!db)
> + return NULL;
> +
> + vocs = new0(struct bt_vocs, 1);
> +
> + vostate = new0(struct vol_offset_state, 1);
> +
> + vocs->vostate = vostate;
> + vocs->vocs_audio_loc = BT_VCP_FRONT_LEFT;
> + vocs->vocs_ao_dec = "Left Speaker";
> +
> + /* Populate DB with VOCS attributes */
> + bt_uuid16_create(&uuid, VOL_OFFSET_CS_UUID);
> + vocs->service = gatt_db_add_service(db, &uuid, true, 9);
> +
> + bt_uuid16_create(&uuid, VOCS_STATE_CHAR_UUID);
> + vocs->vos = gatt_db_service_add_characteristic(vocs->service,
> + &uuid,
> + BT_ATT_PERM_READ,
> + BT_GATT_CHRC_PROP_READ |
> + BT_GATT_CHRC_PROP_NOTIFY,
> + vocs_state_read, NULL,
> + vocs);
> +
> + vocs->vos_ccc = gatt_db_service_add_ccc(vocs->service,
> + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
> +
> + bt_uuid16_create(&uuid, VOCS_AUDIO_LOC_CHRC_UUID);
> + vocs->voal = gatt_db_service_add_characteristic(vocs->service,
> + &uuid,
> + BT_ATT_PERM_READ,
> + BT_GATT_CHRC_PROP_READ |
> + BT_GATT_CHRC_PROP_NOTIFY,
> + vocs_voal_read, NULL,
> + vocs);
> +
> + vocs->voal_ccc = gatt_db_service_add_ccc(vocs->service,
> + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
> +
> + bt_uuid16_create(&uuid, VOCS_CP_CHRC_UUID);
> + vocs->vo_cp = gatt_db_service_add_characteristic(vocs->service,
> + &uuid,
> + BT_ATT_PERM_WRITE,
> + BT_GATT_CHRC_PROP_WRITE,
> + NULL, vocs_cp_write,
> + vocs);
> +
> + bt_uuid16_create(&uuid, VOCS_AUDIO_OP_DESC_CHAR_UUID);
> + vocs->voaodec = gatt_db_service_add_characteristic(vocs->service,
> + &uuid,
> + BT_ATT_PERM_READ,
> + BT_GATT_CHRC_PROP_READ |
> + BT_GATT_CHRC_PROP_NOTIFY,
> + vocs_voaodec_read, NULL,
> + vocs);
> +
> + vocs->voaodec_ccc = gatt_db_service_add_ccc(vocs->service,
> + BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
> +
> + return vocs;
> +}
> +
> static struct bt_vcp_db *vcp_db_new(struct gatt_db *db)
> {
> struct bt_vcp_db *vdb;
> @@ -787,6 +1113,9 @@ static struct bt_vcp_db *vcp_db_new(struct gatt_db *db)
> vdb->vcs = vcs_new(db);
> vdb->vcs->vdb = vdb;
>
> + vdb->vocs = vocs_new(db);
> + vdb->vocs->vdb = vdb;
> +
> queue_push_tail(vcp_db, vdb);
>
> return vdb;
> @@ -911,6 +1240,44 @@ static void vcp_vstate_notify(struct bt_vcp *vcp, uint16_t value_handle,
> DBG(vcp, "Vol Counter 0x%x", vstate.counter);
> }
>
> +static void vcp_voffset_state_notify(struct bt_vcp *vcp, uint16_t value_handle,
> + const uint8_t *value, uint16_t length,
> + void *user_data)
> +{
> + struct vol_offset_state vostate;
> +
> + memcpy(&vostate, value, sizeof(struct vol_offset_state));
> +
> + DBG(vcp, "Vol Offset 0x%x", vostate.vol_offset);
> + DBG(vcp, "Vol Offset Counter 0x%x", vostate.counter);
> +}
> +
> +static void vcp_audio_loc_notify(struct bt_vcp *vcp, uint16_t value_handle,
> + const uint8_t *value, uint16_t length,
> + void *user_data)
> +{
> + uint32_t *vocs_audio_loc_n = 0;
> +
> + if (value != NULL)
> + memcpy(vocs_audio_loc_n, value, sizeof(uint32_t));
> +
> + DBG(vcp, "VOCS Audio Location 0x%x", *vocs_audio_loc_n);
> +}
> +
> +
> +static void vcp_audio_descriptor_notify(struct bt_vcp *vcp,
> + uint16_t value_handle,
> + const uint8_t *value,
> + uint16_t length,
> + void *user_data)
> +{
> + char vocs_audio_dec_n[256] = {'\0'};
> +
> + memcpy(vocs_audio_dec_n, value, length);
> +
> + DBG(vcp, "VOCS Audio Descriptor 0x%s", *vocs_audio_dec_n);
> +}
> +
> static void vcp_vflag_notify(struct bt_vcp *vcp, uint16_t value_handle,
> const uint8_t *value, uint16_t length,
> void *user_data)
> @@ -972,6 +1339,86 @@ static void read_vol_state(struct bt_vcp *vcp, bool success, uint8_t att_ecode,
> DBG(vcp, "Vol Counter:%x", vs->counter);
> }
>
> +static void read_vol_offset_state(struct bt_vcp *vcp, bool success,
> + uint8_t att_ecode,
> + const uint8_t *value, uint16_t length,
> + void *user_data)
> +{
> + struct vol_offset_state *vos;
> + struct iovec iov = {
> + .iov_base = (void *) value,
> + .iov_len = length,
> + };
> +
> + if (!success) {
> + DBG(vcp, "Unable to read Vol Offset State: error 0x%02x",
> + att_ecode);
> + return;
> + }
> +
> + vos = iov_pull_mem(&iov, sizeof(*vos));
> + if (!vos) {
> + DBG(vcp, "Unable to get Vol Offset State");
> + return;
> + }
> +
> + DBG(vcp, "Vol Set:%x", vos->vol_offset);
> + DBG(vcp, "Vol Counter:%x", vos->counter);
> +}
> +
> +static void read_vocs_audio_location(struct bt_vcp *vcp, bool success,
> + uint8_t att_ecode,
> + const uint8_t *value, uint16_t length,
> + void *user_data)
> +{
> + uint32_t *vocs_audio_loc;
> + struct iovec iov = {
> + .iov_base = (void *) value,
> + .iov_len = length,
> + };
> +
> + if (!success) {
> + DBG(vcp, "Unable to read VOCS Audio Location: error 0x%02x",
> + att_ecode);
> + return;
> + }
> +
> + vocs_audio_loc = iov_pull_mem(&iov, sizeof(uint32_t));
> + if (!*vocs_audio_loc) {
> + DBG(vcp, "Unable to get VOCS Audio Location");
> + return;
> + }
> +
> + DBG(vcp, "VOCS Audio Loc:%x", *vocs_audio_loc);
> +}
> +
> +
> +static void read_vocs_audio_descriptor(struct bt_vcp *vcp, bool success,
> + uint8_t att_ecode,
> + const uint8_t *value, uint16_t length,
> + void *user_data)
> +{
> + char *vocs_ao_dec_r;
> + struct iovec iov = {
> + .iov_base = (void *) value,
> + .iov_len = length,
> + };
> +
> + if (!success) {
> + DBG(vcp, "Unable to read VOCS Audio Descriptor: error 0x%02x",
> + att_ecode);
> + return;
> + }
> +
> + vocs_ao_dec_r = iov_pull_mem(&iov, length);
> + if (!*vocs_ao_dec_r) {
> + DBG(vcp, "Unable to get VOCS Audio Descriptor");
> + return;
> + }
> +
> + DBG(vcp, "VOCS Audio Descriptor:%s", *vocs_ao_dec_r);
> +}
> +
> static void vcp_pending_destroy(void *data)
> {
> struct bt_vcp_pending *pending = data;
> @@ -1128,6 +1575,90 @@ static void foreach_vcs_char(struct gatt_db_attribute *attr, void *user_data)
> }
> }
>
> +static void foreach_vocs_char(struct gatt_db_attribute *attr, void *user_data)
> +{
> + struct bt_vcp *vcp = user_data;
> + uint16_t value_handle;
> + bt_uuid_t uuid, uuid_vostate, uuid_audio_loc, uuid_vo_cp,
> + uuid_audio_op_decs;
> + struct bt_vocs *vocs;
> +
> + if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle,
> + NULL, NULL, &uuid))
> + return;
> +
> + bt_uuid16_create(&uuid_vostate, VOCS_STATE_CHAR_UUID);
> + bt_uuid16_create(&uuid_audio_loc, VOCS_AUDIO_LOC_CHRC_UUID);
> + bt_uuid16_create(&uuid_vo_cp, VOCS_CP_CHRC_UUID);
> + bt_uuid16_create(&uuid_audio_op_decs, VOCS_AUDIO_OP_DESC_CHAR_UUID);
> +
> + if (!bt_uuid_cmp(&uuid, &uuid_vostate)) {
> + DBG(vcp, "VOCS Vol state found: handle 0x%04x", value_handle);
> +
> + vocs = vcp_get_vocs(vcp);
> + if (!vocs || vocs->vos)
> + return;
> +
> + vocs->vos = attr;
> +
> + vcp_read_value(vcp, value_handle, read_vol_offset_state, vcp);
> +
> + vcp->state_id = vcp_register_notify(vcp, value_handle,
> + vcp_voffset_state_notify, NULL);
> +
> + return;
> + }
> +
> + if (!bt_uuid_cmp(&uuid, &uuid_audio_loc)) {
> + DBG(vcp, "VOCS Volume Audio Location found: handle 0x%04x",
> + value_handle);
> +
> + vocs = vcp_get_vocs(vcp);
> + if (!vocs || vocs->voal)
> + return;
> +
> + vocs->voal = attr;
> +
> + vcp_read_value(vcp, value_handle, read_vocs_audio_location,
> + vcp);
> +
> + vcp->audio_loc_id = vcp_register_notify(vcp, value_handle,
> + vcp_audio_loc_notify, NULL);
> +
> + return;
> + }
> +
> + if (!bt_uuid_cmp(&uuid, &uuid_vo_cp)) {
> + DBG(vcp, "VOCS Volume CP found: handle 0x%04x", value_handle);
> +
> + vocs = vcp_get_vocs(vcp);
> + if (!vocs || vocs->vo_cp)
> + return;
> +
> + vocs->vo_cp = attr;
> +
> + return;
> + }
> +
> + if (!bt_uuid_cmp(&uuid, &uuid_audio_op_decs)) {
> + DBG(vcp, "VOCS Vol Audio Descriptor found: handle 0x%04x",
> + value_handle);
> +
> + vocs = vcp_get_vocs(vcp);
> + if (!vocs || vocs->voaodec)
> + return;
> +
> + vocs->voaodec = attr;
> +
> + vcp_read_value(vcp, value_handle, read_vocs_audio_descriptor,
> + vcp);
> + vcp->ao_dec_id = vcp_register_notify(vcp, value_handle,
> + vcp_audio_descriptor_notify, NULL);
> +
> + }
> +
> +}
> +
> static void foreach_vcs_service(struct gatt_db_attribute *attr,
> void *user_data)
> {
> @@ -1141,6 +1672,19 @@ static void foreach_vcs_service(struct gatt_db_attribute *attr,
> gatt_db_service_foreach_char(attr, foreach_vcs_char, vcp);
> }
>
> +static void foreach_vocs_service(struct gatt_db_attribute *attr,
> + void *user_data)
> +{
> + struct bt_vcp *vcp = user_data;
> + struct bt_vocs *vocs = vcp_get_vocs(vcp);
> +
> + vocs->service = attr;
> +
> + gatt_db_service_set_claimed(attr, true);
> +
> + gatt_db_service_foreach_char(attr, foreach_vocs_char, vcp);
> +}
> +
> bool bt_vcp_attach(struct bt_vcp *vcp, struct bt_gatt_client *client)
> {
> bt_uuid_t uuid;
> @@ -1163,6 +1707,9 @@ bool bt_vcp_attach(struct bt_vcp *vcp, struct bt_gatt_client *client)
> bt_uuid16_create(&uuid, VCS_UUID);
> gatt_db_foreach_service(vcp->ldb->db, &uuid, foreach_vcs_service, vcp);
>
> + bt_uuid16_create(&uuid, VOL_OFFSET_CS_UUID);
> + gatt_db_foreach_service(vcp->ldb->db, &uuid, foreach_vocs_service, vcp);
> +
> return true;
> }
>
> --
> 2.34.1
>


--
Luiz Augusto von Dentz