2022-09-15 10:10:35

by Sathish Narasimman

[permalink] [raw]
Subject: [PATCH BlueZ v2 1/4] lib/uuid: Add VCS UUIDs

This adds Volume Control Service UUIDs which will be used by
Volume Control Profile.
---
lib/uuid.h | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/lib/uuid.h b/lib/uuid.h
index cb9294be8c6e..f667a74b9b73 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -164,6 +164,13 @@ extern "C" {
#define ASE_SOURCE_UUID 0x2bc5
#define ASE_CP_UUID 0x2bc6

+#define VCS_UUID 0x1844
+#define VOL_OFFSET_CS_UUID 0x1845
+#define AUDIO_INPUT_CS_UUID 0x1843
+#define VOL_STATE_CHRC_UUID 0x2B7D
+#define VOL_CP_CHRC_UUID 0x2B7E
+#define VOL_FLAG_CHRC_UUID 0x2B7F
+
typedef struct {
enum {
BT_UUID_UNSPEC = 0,
--
2.25.1


2022-09-15 10:10:35

by Sathish Narasimman

[permalink] [raw]
Subject: [PATCH BlueZ v2 2/4] shared/vcp: Add initial code for handling VCP

This adds initial code for Volume Control Profile.
---
Makefile.am | 1 +
src/shared/vcp.c | 1041 ++++++++++++++++++++++++++++++++++++++++++++++
src/shared/vcp.h | 58 +++
3 files changed, 1100 insertions(+)
create mode 100644 src/shared/vcp.c
create mode 100644 src/shared/vcp.h

diff --git a/Makefile.am b/Makefile.am
index 960bf21bc726..27715c73d76f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -231,6 +231,7 @@ shared_sources = src/shared/io.h src/shared/timeout.h \
src/shared/gap.h src/shared/gap.c \
src/shared/log.h src/shared/log.c \
src/shared/bap.h src/shared/bap.c src/shared/ascs.h \
+ src/shared/vcp.c src/shared/vcp.h \
src/shared/lc3.h src/shared/tty.h

if READLINE
diff --git a/src/shared/vcp.c b/src/shared/vcp.c
new file mode 100644
index 000000000000..2d718cfff174
--- /dev/null
+++ b/src/shared/vcp.c
@@ -0,0 +1,1041 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2022 Intel Corporation. All rights reserved.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+#include "src/shared/timeout.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-server.h"
+#include "src/shared/gatt-client.h"
+#include "src/shared/vcp.h"
+#include "src/log.h"
+
+#define VCP_STEP_SIZE 1
+
+/* Apllication Error Code */
+#define BT_ATT_ERROR_INVALID_CHANGE_COUNTER 0x80
+#define BT_ATT_ERROR_OPCODE_NOT_SUPPORTED 0x81
+
+struct bt_vcp_db {
+ struct gatt_db *db;
+ struct bt_vcs *vcs;
+};
+
+typedef void (*vcp_func_t)(struct bt_vcp *vcp, bool success, uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data);
+
+struct bt_vcp_pending {
+ unsigned int id;
+ struct bt_vcp *vcp;
+ vcp_func_t func;
+ void *user_data;
+};
+
+struct bt_vcs_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_vcp_cb {
+ unsigned int id;
+ bt_vcp_func_t attached;
+ bt_vcp_func_t detached;
+ void *user_data;
+};
+
+typedef void (*vcp_notify_t)(struct bt_vcp *vcp, uint16_t value_handle,
+ const uint8_t *value, uint16_t length,
+ void *user_data);
+
+struct bt_vcp_notify {
+ unsigned int id;
+ struct bt_vcp *vcp;
+ vcp_notify_t func;
+ void *user_data;
+};
+
+struct bt_vcp {
+ int ref_count;
+ struct bt_vcp_db *ldb;
+ struct bt_vcp_db *rdb;
+ struct bt_gatt_client *client;
+ struct bt_att *att;
+ unsigned int vstate_id;
+ unsigned int vflag_id;
+
+ struct queue *pending;
+
+ void *debug_data;
+ void *user_data;
+};
+
+#define RESET_VOLUME_SETTING 0x00
+#define USERSET_VOLUME_SETTING 0x01
+
+/* Contains local bt_vcp_db */
+struct vol_state {
+ uint8_t vol_set;
+ uint8_t mute;
+ uint8_t counter;
+} __packed;
+
+struct bt_vcs {
+ struct bt_vcp_db *vdb;
+ struct vol_state *vstate;
+ uint8_t vol_flag;
+ struct gatt_db_attribute *service;
+ struct gatt_db_attribute *vs;
+ struct gatt_db_attribute *vs_ccc;
+ struct gatt_db_attribute *vol_cp;
+ struct gatt_db_attribute *vf;
+ struct gatt_db_attribute *vf_ccc;
+};
+
+static struct queue *vcp_db;
+static struct queue *vcp_cbs;
+static struct queue *sessions;
+
+static void *iov_pull_mem(struct iovec *iov, size_t len)
+{
+ void *data = iov->iov_base;
+
+ if (iov->iov_len < len)
+ return NULL;
+
+ iov->iov_base += len;
+ iov->iov_len -= len;
+
+ return data;
+}
+
+static struct bt_vcs *vcp_get_vcs(struct bt_vcp *vcp)
+{
+ if (!vcp)
+ return NULL;
+
+ if (vcp->rdb->vcs)
+ return vcp->rdb->vcs;
+
+ vcp->rdb->vcs = new0(struct bt_vcs, 1);
+ vcp->rdb->vcs->vdb = vcp->rdb;
+
+ return vcp->rdb->vcs;
+}
+
+static void vcp_detached(void *data, void *user_data)
+{
+ struct bt_vcp_cb *cb = data;
+ struct bt_vcp *vcp = user_data;
+
+ cb->detached(vcp, cb->user_data);
+}
+
+void bt_vcp_detach(struct bt_vcp *vcp)
+{
+ if (!queue_remove(sessions, vcp))
+ return;
+
+ bt_gatt_client_unref(vcp->client);
+ vcp->client = NULL;
+
+ queue_foreach(vcp_cbs, vcp_detached, vcp);
+}
+
+static void vcp_db_free(void *data)
+{
+ struct bt_vcp_db *vdb = data;
+
+ if (!vdb)
+ return;
+
+ gatt_db_unref(vdb->db);
+
+ free(vdb->vcs);
+ free(vdb);
+}
+
+static void vcp_free(void *data)
+{
+ struct bt_vcp *vcp = data;
+
+ bt_vcp_detach(vcp);
+
+ vcp_db_free(vcp->rdb);
+
+ queue_destroy(vcp->pending, NULL);
+
+ free(vcp);
+}
+bool bt_vcp_set_user_data(struct bt_vcp *vcp, void *user_data)
+{
+ if (!vcp)
+ return false;
+
+ vcp->user_data = user_data;
+
+ return true;
+}
+
+static bool vcp_db_match(const void *data, const void *match_data)
+{
+ const struct bt_vcp_db *vdb = data;
+ const struct gatt_db *db = match_data;
+
+ return (vdb->db == db);
+}
+
+struct bt_att *bt_vcp_get_att(struct bt_vcp *vcp)
+{
+ if (!vcp)
+ return NULL;
+
+ if (vcp->att)
+ return vcp->att;
+
+ return bt_gatt_client_get_att(vcp->client);
+}
+
+struct bt_vcp *bt_vcp_ref(struct bt_vcp *vcp)
+{
+ if (!vcp)
+ return NULL;
+
+ __sync_fetch_and_add(&vcp->ref_count, 1);
+
+ return vcp;
+}
+
+void bt_vcp_unref(struct bt_vcp *vcp)
+{
+ if (!vcp)
+ return;
+
+ if (__sync_sub_and_fetch(&vcp->ref_count, 1))
+ return;
+
+ vcp_free(vcp);
+}
+
+static void vcp_disconnected(int err, void *user_data)
+{
+ struct bt_vcp *vcp = user_data;
+
+ DBG("vcp %p disconnected err %d", vcp, err);
+
+ bt_vcp_detach(vcp);
+}
+
+static struct bt_vcp *vcp_get_session(struct bt_att *att, struct gatt_db *db)
+{
+ const struct queue_entry *entry;
+ struct bt_vcp *vcp;
+
+ for (entry = queue_get_entries(sessions); entry; entry = entry->next) {
+ struct bt_vcp *vcp = entry->data;
+
+ if (att == bt_vcp_get_att(vcp))
+ return vcp;
+ }
+
+ vcp = bt_vcp_new(db, NULL);
+ vcp->att = att;
+
+ bt_att_register_disconnect(att, vcp_disconnected, vcp, NULL);
+
+ bt_vcp_attach(vcp, NULL);
+
+ return vcp;
+
+}
+
+static uint8_t vcs_rel_vol_down(struct bt_vcs *vcs, struct bt_vcp *vcp,
+ struct iovec *iov)
+{
+ struct bt_vcp_db *vdb;
+ struct vol_state *vstate;
+ uint8_t *change_counter;
+
+ DBG("");
+
+ vdb = vcp->ldb;
+ if (!vdb && !vdb->vcs)
+ DBG("error: vcp database not available!!!!");
+
+ vstate = vdb->vcs->vstate;
+ if (!vstate)
+ DBG("error: vstate not availalbe!!!!");
+
+ change_counter = iov_pull_mem(iov, sizeof(*change_counter));
+
+ if (*change_counter != vstate->counter) {
+ DBG("Change Counter Mismatch Volume not decremented!");
+ return BT_ATT_ERROR_INVALID_CHANGE_COUNTER;
+ }
+
+ vstate->vol_set = MAX((vstate->vol_set - VCP_STEP_SIZE), 0);
+ vstate->counter = -~vstate->counter; /*Increment Change Counter*/
+
+ gatt_db_attribute_notify(vdb->vcs->vs, (void *)vstate,
+ sizeof(struct vol_state),
+ bt_vcp_get_att(vcp));
+ return 0;
+}
+
+static uint8_t vcs_rel_vol_up(struct bt_vcs *vcs, struct bt_vcp *vcp,
+ struct iovec *iov)
+{
+ struct bt_vcp_db *vdb;
+ struct vol_state *vstate;
+ uint8_t *change_counter;
+
+ DBG("");
+
+ vdb = vcp->ldb;
+ if (!vdb && !vdb->vcs)
+ DBG("error: vcp database not available!!!!");
+
+ vstate = vdb->vcs->vstate;
+ if (!vstate)
+ DBG("error: vstate not availalbe!!!!");
+
+ change_counter = iov_pull_mem(iov, sizeof(*change_counter));
+
+ if (*change_counter != vstate->counter) {
+ DBG("Change Counter Mismatch Volume not decremented!");
+ return BT_ATT_ERROR_INVALID_CHANGE_COUNTER;
+ }
+
+ vstate->vol_set = MIN((vstate->vol_set + VCP_STEP_SIZE), 255);
+ vstate->counter = -~vstate->counter; /*Increment Change Counter*/
+
+ gatt_db_attribute_notify(vdb->vcs->vs, (void *)vstate,
+ sizeof(struct vol_state),
+ bt_vcp_get_att(vcp));
+ return 0;
+}
+
+static uint8_t vcs_unmute_rel_vol_down(struct bt_vcs *vcs, struct bt_vcp *vcp,
+ struct iovec *iov)
+{
+ struct bt_vcp_db *vdb;
+ struct vol_state *vstate;
+ uint8_t *change_counter;
+
+ DBG("");
+
+ vdb = vcp->ldb;
+ if (!vdb && !vdb->vcs)
+ DBG("error: vcp database not available!!!!");
+
+ vstate = vdb->vcs->vstate;
+ if (!vstate)
+ DBG("error: vstate not availalbe!!!!");
+
+ change_counter = iov_pull_mem(iov, sizeof(*change_counter));
+
+ if (*change_counter != vstate->counter) {
+ DBG("Change Counter Mismatch Volume not decremented!");
+ return BT_ATT_ERROR_INVALID_CHANGE_COUNTER;
+ }
+
+ vstate->mute = 0x00;
+ vstate->vol_set = MAX((vstate->vol_set - VCP_STEP_SIZE), 0);
+ vstate->counter = -~vstate->counter; /*Increment Change Counter*/
+
+ gatt_db_attribute_notify(vdb->vcs->vs, (void *)vstate,
+ sizeof(struct vol_state),
+ bt_vcp_get_att(vcp));
+ return 0;
+}
+
+static uint8_t vcs_unmute_rel_vol_up(struct bt_vcs *vcs, struct bt_vcp *vcp,
+ struct iovec *iov)
+{
+ struct bt_vcp_db *vdb;
+ struct vol_state *vstate;
+ uint8_t *change_counter;
+
+ DBG("");
+
+ vdb = vcp->ldb;
+ if (!vdb && !vdb->vcs)
+ DBG("error: vcp database not available!!!!");
+
+ vstate = vdb->vcs->vstate;
+ if (!vstate)
+ DBG("error: vstate not availalbe!!!!");
+
+ change_counter = iov_pull_mem(iov, sizeof(*change_counter));
+
+ if (*change_counter != vstate->counter) {
+ DBG("Change Counter Mismatch Volume not decremented!");
+ return BT_ATT_ERROR_INVALID_CHANGE_COUNTER;
+ }
+
+ vstate->mute = 0x00;
+ vstate->vol_set = MIN((vstate->vol_set + VCP_STEP_SIZE), 255);
+ vstate->counter = -~vstate->counter; /*Increment Change Counter*/
+
+ gatt_db_attribute_notify(vdb->vcs->vs, (void *)vstate,
+ sizeof(struct vol_state),
+ bt_vcp_get_att(vcp));
+ return 0;
+}
+
+static uint8_t vcs_set_absolute_vol(struct bt_vcs *vcs, struct bt_vcp *vcp,
+ struct iovec *iov)
+{
+ struct bt_vcp_db *vdb;
+ struct vol_state *vstate;
+ struct bt_vcs_ab_vol *req;
+
+ DBG("");
+
+ vdb = vcp->ldb;
+ if (!vdb && !vdb->vcs)
+ DBG("error: vcp database not available!!!!");
+
+ vstate = vdb->vcs->vstate;
+ if (!vstate)
+ DBG("error: vstate not availalbe!!!!");
+
+ req = iov_pull_mem(iov, sizeof(*req));
+
+ if (req->change_counter != vstate->counter) {
+ DBG("Change Counter Mismatch Volume not decremented!");
+ return BT_ATT_ERROR_INVALID_CHANGE_COUNTER;
+ }
+
+ vstate->vol_set = req->vol_set;
+ vstate->counter = -~vstate->counter; /*Increment Change Counter*/
+
+ gatt_db_attribute_notify(vdb->vcs->vs, (void *)vstate,
+ sizeof(struct vol_state),
+ bt_vcp_get_att(vcp));
+ return 0;
+}
+
+static uint8_t vcs_unmute(struct bt_vcs *vcs, struct bt_vcp *vcp,
+ struct iovec *iov)
+{
+ struct bt_vcp_db *vdb;
+ struct vol_state *vstate;
+ uint8_t *change_counter;
+
+ DBG("");
+
+ vdb = vcp->ldb;
+ if (!vdb && !vdb->vcs)
+ DBG("error: vcp database not available!!!!");
+
+ vstate = vdb->vcs->vstate;
+ if (!vstate)
+ DBG("error: vstate not availalbe!!!!");
+
+ change_counter = iov_pull_mem(iov, sizeof(*change_counter));
+
+ if (*change_counter != vstate->counter) {
+ DBG("Change Counter Mismatch Volume not decremented!");
+ return BT_ATT_ERROR_INVALID_CHANGE_COUNTER;
+ }
+
+ vstate->mute = 0x00;
+ vstate->counter = -~vstate->counter; /*Increment Change Counter*/
+
+ gatt_db_attribute_notify(vdb->vcs->vs, (void *)vstate,
+ sizeof(struct vol_state),
+ bt_vcp_get_att(vcp));
+ return 0;
+}
+
+static uint8_t vcs_mute(struct bt_vcs *vcs, struct bt_vcp *vcp,
+ struct iovec *iov)
+{
+ struct bt_vcp_db *vdb;
+ struct vol_state *vstate;
+ uint8_t *change_counter;
+
+ DBG("");
+
+ vdb = vcp->ldb;
+ if (!vdb && !vdb->vcs)
+ DBG("error: vcp database not available!!!!");
+
+ vstate = vdb->vcs->vstate;
+ if (!vstate)
+ DBG("error: vstate not availalbe!!!!");
+
+ change_counter = iov_pull_mem(iov, sizeof(*change_counter));
+
+ if (*change_counter != vstate->counter) {
+ DBG("Change Counter Mismatch Volume not decremented!");
+ return BT_ATT_ERROR_INVALID_CHANGE_COUNTER;
+ }
+
+ vstate->mute = 0x01;
+ vstate->counter = -~vstate->counter; /*Increment Change Counter*/
+
+ return 0;
+}
+
+
+#define BT_VCS_REL_VOL_DOWN 0x00
+#define BT_VCS_REL_VOL_UP 0x01
+#define BT_VCS_UNMUTE_REL_VOL_DOWN 0x02
+#define BT_VCS_UNMUTE_REL_VOL_UP 0x03
+#define BT_VCS_SET_ABSOLUTE_VOL 0x04
+#define BT_VCS_UNMUTE 0x05
+#define BT_VCS_MUTE 0x06
+
+#define VCS_OP(_str, _op, _size, _func) \
+ { \
+ .str = _str, \
+ .op = _op, \
+ .size = _size, \
+ .func = _func, \
+ }
+
+struct vcs_op_handler {
+ const char *str;
+ uint8_t op;
+ size_t size;
+ uint8_t (*func)(struct bt_vcs *vcs, struct bt_vcp *vcp,
+ struct iovec *iov);
+} vcp_handlers[] = {
+ VCS_OP("Relative Volume Down", BT_VCS_REL_VOL_DOWN,
+ sizeof(uint8_t), vcs_rel_vol_down),
+ VCS_OP("Relative Volume Up", BT_VCS_REL_VOL_UP,
+ sizeof(uint8_t), vcs_rel_vol_up),
+ VCS_OP("Unmute - Relative Volume Down", BT_VCS_UNMUTE_REL_VOL_DOWN,
+ sizeof(uint8_t), vcs_unmute_rel_vol_down),
+ VCS_OP("Unmute - Relative Volume Up", BT_VCS_UNMUTE_REL_VOL_UP,
+ sizeof(uint8_t), vcs_unmute_rel_vol_up),
+ VCS_OP("Set Absolute Volume", BT_VCS_SET_ABSOLUTE_VOL,
+ sizeof(struct bt_vcs_ab_vol), vcs_set_absolute_vol),
+ VCS_OP("UnMute", BT_VCS_UNMUTE,
+ sizeof(uint8_t), vcs_unmute),
+ VCS_OP("Mute", BT_VCS_MUTE,
+ sizeof(uint8_t), vcs_mute),
+ {}
+};
+
+static void vcs_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_vcs *vcs = user_data;
+ struct bt_vcp *vcp = vcp_get_session(att, vcs->vdb->db);
+ struct iovec iov = {
+ .iov_base = (void *) value,
+ .iov_len = len,
+ };
+ uint8_t *vcp_op;
+ struct vcs_op_handler *handler;
+ uint8_t ret = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+
+ DBG("");
+ if (offset) {
+ DBG("invalid offset %d", offset);
+ ret = BT_ATT_ERROR_INVALID_OFFSET;
+ goto respond;
+ }
+
+ if (len < sizeof(*vcp_op)) {
+ DBG("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));
+
+ DBG("vcp_op: %x", *vcp_op);
+
+ for (handler = vcp_handlers; handler && handler->str; handler++) {
+ if (handler->op != *vcp_op)
+ continue;
+
+ if (iov.iov_len < handler->size) {
+ DBG("invalid len %ld < %ld handler->size", len,
+ handler->size);
+ ret = BT_ATT_ERROR_OPCODE_NOT_SUPPORTED;
+ goto respond;
+ }
+
+ break;
+ }
+
+ if (handler && handler->str) {
+ DBG("%s", handler->str);
+
+ ret = handler->func(vcs, vcp, &iov);
+ } else {
+ DBG("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,
+ void *user_data)
+{
+ struct bt_vcs *vcs = user_data;
+ struct iovec iov;
+
+ DBG("");
+
+ iov.iov_base = vcs->vstate;
+ iov.iov_len = sizeof(*vcs->vstate);
+
+ 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,
+ void *user_data)
+{
+ struct bt_vcs *vcs = user_data;
+ struct iovec iov;
+
+ DBG("vf: %x", vcs->vol_flag);
+
+ iov.iov_base = &vcs->vol_flag;
+ iov.iov_len = sizeof(vcs->vol_flag);
+
+ 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;
+ struct vol_state *vstate;
+ bt_uuid_t uuid;
+
+ if (!db)
+ return NULL;
+
+ vcs = new0(struct bt_vcs, 1);
+
+ vstate = new0(struct vol_state, 1);
+
+ vcs->vstate = vstate;
+ vcs->vol_flag = USERSET_VOLUME_SETTING;
+
+ /* Populate DB with VCS attributes */
+ bt_uuid16_create(&uuid, VCS_UUID);
+ vcs->service = gatt_db_add_service(db, &uuid, true, 9);
+
+ bt_uuid16_create(&uuid, VOL_STATE_CHRC_UUID);
+ vcs->vs = gatt_db_service_add_characteristic(vcs->service,
+ &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_NOTIFY,
+ vcs_state_read, NULL,
+ vcs);
+
+ vcs->vs_ccc = gatt_db_service_add_ccc(vcs->service,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+
+ bt_uuid16_create(&uuid, VOL_CP_CHRC_UUID);
+ vcs->vol_cp = gatt_db_service_add_characteristic(vcs->service,
+ &uuid,
+ BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_WRITE,
+ NULL, vcs_cp_write,
+ vcs);
+
+ bt_uuid16_create(&uuid, VOL_FLAG_CHRC_UUID);
+ vcs->vf = gatt_db_service_add_characteristic(vcs->service,
+ &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_NOTIFY,
+ vcs_flag_read, NULL,
+ vcs);
+
+ vcs->vf_ccc = gatt_db_service_add_ccc(vcs->service,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+
+
+ gatt_db_service_set_active(vcs->service, true);
+
+ return vcs;
+}
+
+static struct bt_vcp_db *vcp_db_new(struct gatt_db *db)
+{
+ struct bt_vcp_db *vdb;
+
+ if (!db)
+ return NULL;
+
+ vdb = new0(struct bt_vcp_db, 1);
+ vdb->db = gatt_db_ref(db);
+
+ if (!vcp_db)
+ vcp_db = queue_new();
+
+ vdb->vcs = vcs_new(db);
+ vdb->vcs->vdb = vdb;
+
+ queue_push_tail(vcp_db, vdb);
+
+ return vdb;
+}
+
+static struct bt_vcp_db *vcp_get_db(struct gatt_db *db)
+{
+ struct bt_vcp_db *vdb;
+
+ vdb = queue_find(vcp_db, vcp_db_match, db);
+ if (vdb)
+ return vdb;
+
+ return vcp_db_new(db);
+}
+
+void bt_vcp_add_db(struct gatt_db *db)
+{
+ vcp_db_new(db);
+}
+
+unsigned int bt_vcp_register(bt_vcp_func_t attached, bt_vcp_func_t detached,
+ void *user_data)
+{
+ struct bt_vcp_cb *cb;
+ static unsigned int id;
+
+ if (!attached && !detached)
+ return 0;
+
+ if (!vcp_cbs)
+ vcp_cbs = queue_new();
+
+ cb = new0(struct bt_vcp_cb, 1);
+ cb->id = ++id ? id : ++id;
+ cb->attached = attached;
+ cb->detached = detached;
+ cb->user_data = user_data;
+
+ queue_push_tail(vcp_cbs, cb);
+
+ return cb->id;
+}
+
+static bool match_id(const void *data, const void *match_data)
+{
+ const struct bt_vcp_cb *cb = data;
+ unsigned int id = PTR_TO_UINT(match_data);
+
+ return (cb->id == id);
+}
+
+bool bt_vcp_unregister(unsigned int id)
+{
+ struct bt_vcp_cb *cb;
+
+ cb = queue_remove_if(vcp_cbs, match_id, UINT_TO_PTR(id));
+ if (!cb)
+ return false;
+
+ free(cb);
+
+ return true;
+}
+
+struct bt_vcp *bt_vcp_new(struct gatt_db *ldb, struct gatt_db *rdb)
+{
+ struct bt_vcp *vcp;
+ struct bt_vcp_db *vdb;
+
+ if (!ldb)
+ return NULL;
+
+ vdb = vcp_get_db(ldb);
+ if (!vdb)
+ return NULL;
+
+ vcp = new0(struct bt_vcp, 1);
+ vcp->ldb = vdb;
+ vcp->pending = queue_new();
+
+ if (!rdb)
+ goto done;
+
+ vdb = new0(struct bt_vcp_db, 1);
+ vdb->db = gatt_db_ref(rdb);
+
+ vcp->rdb = vdb;
+
+done:
+ bt_vcp_ref(vcp);
+
+ return vcp;
+}
+
+static void vcp_vstate_register(uint16_t att_ecode, void *user_data)
+{
+ DBG("");
+ if (att_ecode)
+ DBG("ASE register failed: 0x%04x", att_ecode);
+}
+
+static void vcp_vflag_register(uint16_t att_ecode, void *user_data)
+{
+ DBG("");
+ if (att_ecode)
+ DBG("ASE register failed: 0x%04x", att_ecode);
+}
+
+static void vcp_vstate_notify(uint16_t value_handle, const uint8_t *value,
+ uint16_t length, void *user_data)
+{
+ struct vol_state vstate;
+
+ memcpy(&vstate, value, sizeof(struct vol_state));
+
+ DBG("Vol Settings 0x%x", vstate.vol_set);
+ DBG("Mute Status 0x%x", vstate.mute);
+ DBG("Vol Counter 0x%x", vstate.counter);
+}
+
+static void vcp_vflag_notify(uint16_t value_handle, const uint8_t *value,
+ uint16_t length, void *user_data)
+{
+ uint8_t vflag;
+
+ memcpy(&vflag, value, sizeof(vflag));
+
+ DBG("Vol Flag 0x%x", vflag);
+}
+
+static void read_vol_flag(struct bt_vcp *vcp, bool success, uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
+{
+ uint8_t *vol_flag;
+ struct iovec iov = {
+ .iov_base = (void *) value,
+ .iov_len = length,
+ };
+
+ if (!success) {
+ DBG("Unable to read VCP Vol State: error 0x%02x", att_ecode);
+ return;
+ }
+
+ vol_flag = iov_pull_mem(&iov, sizeof(*vol_flag));
+ if (!vol_flag) {
+ DBG("Unable to get Vol State");
+ return;
+ }
+
+ DBG("Vol Flag:%x", *vol_flag);
+}
+
+static void read_vol_state(struct bt_vcp *vcp, bool success, uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
+{
+ struct vol_state *vs;
+ struct iovec iov = {
+ .iov_base = (void *) value,
+ .iov_len = length,
+ };
+
+ if (!success) {
+ DBG("Unable to read VCP Vol State: error 0x%02x", att_ecode);
+ return;
+ }
+
+ vs = iov_pull_mem(&iov, sizeof(*vs));
+ if (!vs) {
+ DBG("Unable to get Vol State");
+ return;
+ }
+
+ DBG("Vol Set:%x", vs->vol_set);
+ DBG("Vol Mute:%x", vs->mute);
+ DBG("Vol Counter:%x", vs->counter);
+
+}
+
+static void vcp_pending_destroy(void *data)
+{
+ struct bt_vcp_pending *pending = data;
+ struct bt_vcp *vcp = pending->vcp;
+
+ if (queue_remove_if(vcp->pending, NULL, pending))
+ free(pending);
+}
+
+static void vcp_pending_complete(bool success, uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
+{
+ struct bt_vcp_pending *pending = user_data;
+
+ if (pending->func)
+ pending->func(pending->vcp, success, att_ecode, value, length,
+ pending->user_data);
+}
+
+static void vcp_read_value(struct bt_vcp *vcp, uint16_t value_handle,
+ vcp_func_t func, void *user_data)
+{
+ struct bt_vcp_pending *pending;
+
+ pending = new0(struct bt_vcp_pending, 1);
+ pending->vcp = vcp;
+ pending->func = func;
+ pending->user_data = user_data;
+
+ pending->id = bt_gatt_client_read_value(vcp->client, value_handle,
+ vcp_pending_complete, pending,
+ vcp_pending_destroy);
+ if (!pending->id) {
+ DBG("Unable to send Read request");
+ free(pending);
+ return;
+ }
+
+ queue_push_tail(vcp->pending, pending);
+}
+
+static void foreach_vcs_char(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct bt_vcp *vcp = user_data;
+ uint16_t value_handle;
+ bt_uuid_t uuid, uuid_vstate, uuid_cp, uuid_vflag;
+ struct bt_vcs *vcs;
+
+ DBG("");
+ if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle,
+ NULL, NULL, &uuid))
+ return;
+
+ bt_uuid16_create(&uuid_vstate, VOL_STATE_CHRC_UUID);
+ bt_uuid16_create(&uuid_cp, VOL_CP_CHRC_UUID);
+ bt_uuid16_create(&uuid_vflag, VOL_FLAG_CHRC_UUID);
+
+ if (!bt_uuid_cmp(&uuid, &uuid_vstate)) {
+ DBG("VCS Volume state found: handle 0x%04x", value_handle);
+
+ vcs = vcp_get_vcs(vcp);
+ if (!vcs || vcs->vs)
+ return;
+
+ vcs->vs = attr;
+
+ vcp_read_value(vcp, value_handle, read_vol_state, vcp);
+ vcp->vstate_id = bt_gatt_client_register_notify(vcp->client,
+ value_handle,
+ vcp_vstate_register,
+ vcp_vstate_notify, vcp, NULL);
+
+ return;
+ }
+
+ if (!bt_uuid_cmp(&uuid, &uuid_cp)) {
+ DBG("VCS Volume CP found: handle 0x%04x", value_handle);
+
+ vcs = vcp_get_vcs(vcp);
+ if (!vcs || vcs->vol_cp)
+ return;
+
+ vcs->vol_cp = attr;
+
+ return;
+ }
+
+ if (!bt_uuid_cmp(&uuid, &uuid_vflag)) {
+ DBG("VCS Vol Flaf found: handle 0x%04x", value_handle);
+
+ vcs = vcp_get_vcs(vcp);
+ if (!vcs || vcs->vf)
+ return;
+
+ vcs->vf = attr;
+
+ vcp_read_value(vcp, value_handle, read_vol_flag, vcp);
+ vcp->vflag_id = bt_gatt_client_register_notify(vcp->client,
+ value_handle,
+ vcp_vflag_register,
+ vcp_vflag_notify, vcp, NULL);
+ }
+
+}
+
+static void foreach_vcs_service(struct gatt_db_attribute *attr,
+ void *user_data)
+{
+ struct bt_vcp *vcp = user_data;
+ struct bt_vcs *vcs = vcp_get_vcs(vcp);
+
+ DBG("");
+ vcs->service = attr;
+
+ gatt_db_service_set_claimed(attr, true);
+
+ gatt_db_service_foreach_char(attr, foreach_vcs_char, vcp);
+}
+
+bool bt_vcp_attach(struct bt_vcp *vcp, struct bt_gatt_client *client)
+{
+ bt_uuid_t uuid;
+
+ if (!sessions)
+ sessions = queue_new();
+
+ queue_push_tail(sessions, vcp);
+
+ if (!client)
+ return true;
+
+ if (vcp->client)
+ return false;
+
+ vcp->client = bt_gatt_client_clone(client);
+ if (!vcp->client)
+ return false;
+
+ bt_uuid16_create(&uuid, VCS_UUID);
+ gatt_db_foreach_service(vcp->ldb->db, &uuid, foreach_vcs_service, vcp);
+
+ return true;
+}
+
diff --git a/src/shared/vcp.h b/src/shared/vcp.h
new file mode 100644
index 000000000000..456ad8041162
--- /dev/null
+++ b/src/shared/vcp.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2020 Intel Corporation. All rights reserved.
+ *
+ */
+
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include "src/shared/io.h"
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+#define BT_VCP_RENDERER 0x01
+#define BT_VCP_CONTROLLER 0x02
+
+#define BT_VCP_RELATIVE_VOL_DOWN 0x00
+#define BT_VCP_RELATIVE_VOL_UP 0x01
+#define BT_VCP_UNMUTE_RELATIVE_VOL_DOWN 0x02
+#define BT_VCP_UNMUTE_RELATIVE_VOL_UP 0x03
+#define BT_VCP_SET_ABOSULTE_VOL 0x04
+#define BT_VCP_UNMUTE 0x05
+#define BT_VCP_MUTE 0x06
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+struct bt_vcp;
+
+typedef void (*bt_vcp_func_t)(struct bt_vcp *vcp, void *user_data);
+
+struct bt_vcp *bt_vcp_ref(struct bt_vcp *vcp);
+void bt_vcp_unref(struct bt_vcp *vcp);
+
+void bt_vcp_add_db(struct gatt_db *db);
+
+bool bt_vcp_attach(struct bt_vcp *vcp, struct bt_gatt_client *client);
+void bt_vcp_detach(struct bt_vcp *vcp);
+
+struct bt_att *bt_vcp_get_att(struct bt_vcp *vcp);
+
+bool bt_vcp_set_user_data(struct bt_vcp *vcp, void *user_data);
+
+/* Session related function */
+unsigned int bt_vcp_register(bt_vcp_func_t added, bt_vcp_func_t removed,
+ void *user_data);
+bool bt_vcp_unregister(unsigned int id);
+struct bt_vcp *bt_vcp_new(struct gatt_db *ldb, struct gatt_db *rdb);
--
2.25.1

2022-09-15 10:10:47

by Sathish Narasimman

[permalink] [raw]
Subject: [PATCH BlueZ v2 3/4] profiles: Add initial code for vcp plugin

This adds initial code for vcp plugin which handles Volume Control
Profile and Volume Control Service.
---
Makefile.plugins | 5 +
configure.ac | 4 +
profiles/audio/vcp.c | 312 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 321 insertions(+)
create mode 100644 profiles/audio/vcp.c

diff --git a/Makefile.plugins b/Makefile.plugins
index 213ed99edf2d..a3654980f86d 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -121,3 +121,8 @@ if BAP
builtin_modules += bap
builtin_sources += profiles/audio/bap.c
endif
+
+if VCP
+builtin_modules += vcp
+builtin_sources += profiles/audio/vcp.c
+endif
diff --git a/configure.ac b/configure.ac
index 1f76915b4349..79645e6917cc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -199,6 +199,10 @@ AC_ARG_ENABLE(bap, AS_HELP_STRING([--disable-bap],
[disable BAP profile]), [enable_bap=${enableval}])
AM_CONDITIONAL(BAP, test "${enable_bap}" != "no")

+AC_ARG_ENABLE(vcp, AS_HELP_STRING([--disable-vcp],
+ [disable VCP profile]), [enable_vcp=${enableval}])
+AM_CONDITIONAL(VCP, test "${enable_vcp}" != "no")
+
AC_ARG_ENABLE(tools, AS_HELP_STRING([--disable-tools],
[disable Bluetooth tools]), [enable_tools=${enableval}])
AM_CONDITIONAL(TOOLS, test "${enable_tools}" != "no")
diff --git a/profiles/audio/vcp.c b/profiles/audio/vcp.c
new file mode 100644
index 000000000000..34950d4070f2
--- /dev/null
+++ b/profiles/audio/vcp.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2022 Intel Corporation. All rights reserved.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/dbus-common.h"
+#include "src/shared/util.h"
+#include "src/shared/att.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-client.h"
+#include "src/shared/gatt-server.h"
+#include "src/shared/vcp.h"
+
+#include "btio/btio.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/gatt-database.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/log.h"
+#include "src/error.h"
+
+#define VCS_UUID_STR "00001844-0000-1000-8000-00805f9b34fb"
+#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1"
+
+struct vcp_data {
+ struct btd_device *device;
+ struct btd_service *service;
+ struct bt_vcp *vcp;
+};
+
+static struct queue *sessions;
+
+static int vcp_disconnect(struct btd_service *service)
+{
+ DBG("");
+ return 0;
+}
+
+static struct vcp_data *vcp_data_new(struct btd_device *device)
+{
+ struct vcp_data *data;
+
+ data = new0(struct vcp_data, 1);
+ data->device = device;
+
+ return data;
+}
+
+static void vcp_data_add(struct vcp_data *data)
+{
+ DBG("data %p", data);
+
+ if (queue_find(sessions, NULL, data)) {
+ error("data %p already added", data);
+ return;
+ }
+
+ if (!sessions)
+ sessions = queue_new();
+
+ queue_push_tail(sessions, data);
+
+ if (data->service)
+ btd_service_set_user_data(data->service, data);
+}
+
+static bool match_data(const void *data, const void *match_data)
+{
+ const struct vcp_data *vdata = data;
+ const struct bt_vcp *vcp = match_data;
+
+ return vdata->vcp == vcp;
+}
+
+static void vcp_data_free(struct vcp_data *data)
+{
+ if (data->service) {
+ btd_service_set_user_data(data->service, NULL);
+ bt_vcp_set_user_data(data->vcp, NULL);
+ }
+
+ bt_vcp_unref(data->vcp);
+ free(data);
+}
+
+static void vcp_data_remove(struct vcp_data *data)
+{
+ DBG("data %p", data);
+
+ if (!queue_remove(sessions, data))
+ return;
+
+ vcp_data_free(data);
+
+ if (queue_isempty(sessions)) {
+ queue_destroy(sessions, NULL);
+ sessions = NULL;
+ }
+}
+
+static void vcp_detached(struct bt_vcp *vcp, void *user_data)
+{
+ struct vcp_data *data;
+
+ DBG("%p", vcp);
+
+ data = queue_find(sessions, match_data, vcp);
+ if (!data) {
+ error("Unable to find vcp session");
+ return;
+ }
+
+ vcp_data_remove(data);
+}
+
+static void vcp_attached(struct bt_vcp *vcp, void *user_data)
+{
+ struct vcp_data *data;
+ struct bt_att *att;
+ struct btd_device *device;
+
+ DBG("%p", vcp);
+
+ data = queue_find(sessions, match_data, vcp);
+ if (data)
+ return;
+
+ att = bt_vcp_get_att(vcp);
+ if (!att)
+ return;
+
+ device = btd_adapter_find_device_by_fd(bt_att_get_fd(att));
+ if (!device) {
+ error("Unable to find device");
+ return;
+ }
+
+ data = vcp_data_new(device);
+ data->vcp = vcp;
+
+ vcp_data_add(data);
+
+}
+
+static int vcp_probe(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct btd_adapter *adapter = device_get_adapter(device);
+ struct btd_gatt_database *database = btd_adapter_get_database(adapter);
+ struct vcp_data *data = btd_service_get_user_data(service);
+ char addr[18];
+
+ ba2str(device_get_address(device), addr);
+ DBG("%s", addr);
+
+ /* Ignore, if we were probed for this device already */
+ if (data) {
+ error("Profile probed twice for the same device!");
+ return -EINVAL;
+ }
+
+ data = vcp_data_new(device);
+ data->service = service;
+
+ data->vcp = bt_vcp_new(btd_gatt_database_get_db(database),
+ btd_device_get_gatt_db(device));
+ if (!data->vcp) {
+ error("Unable to create VCP instance");
+ free(data);
+ return -EINVAL;
+ }
+
+ vcp_data_add(data);
+
+ bt_vcp_set_user_data(data->vcp, service);
+
+ return 0;
+}
+
+static void vcp_remove(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct vcp_data *data;
+ char addr[18];
+
+ ba2str(device_get_address(device), addr);
+ DBG("%s", addr);
+
+ data = btd_service_get_user_data(service);
+ if (!data) {
+ error("VCP service not handled by profile");
+ return;
+ }
+
+ vcp_data_remove(data);
+}
+
+static int vcp_accept(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct bt_gatt_client *client = btd_device_get_gatt_client(device);
+ struct vcp_data *data = btd_service_get_user_data(service);
+ char addr[18];
+
+ ba2str(device_get_address(device), addr);
+ DBG("%s", addr);
+
+ if (!data) {
+ error("VCP service not handled by profile");
+ return -EINVAL;
+ }
+
+ if (!bt_vcp_attach(data->vcp, client)) {
+ error("VCP unable to attach");
+ return -EINVAL;
+ }
+
+ btd_service_connecting_complete(service, 0);
+
+ return 0;
+}
+
+static int vcp_server_probe(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ struct btd_gatt_database *database = btd_adapter_get_database(adapter);
+
+ DBG("VCP path %s", adapter_get_path(adapter));
+
+ bt_vcp_add_db(btd_gatt_database_get_db(database));
+
+ return 0;
+}
+
+static void vcp_server_remove(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ DBG("VCP remove Adapter");
+}
+
+static struct btd_profile vcp_profile = {
+ .name = "vcp",
+ .priority = BTD_PROFILE_PRIORITY_MEDIUM,
+ .remote_uuid = VCS_UUID_STR,
+
+ .device_probe = vcp_probe,
+ .device_remove = vcp_remove,
+
+ .accept = vcp_accept,
+ .disconnect = vcp_disconnect,
+
+ .adapter_probe = vcp_server_probe,
+ .adapter_remove = vcp_server_remove,
+};
+
+static unsigned int vcp_id = 0;
+
+static int vcp_init(void)
+{
+ if (!(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL)) {
+ warn("D-Bus experimental not enabled");
+ return -ENOTSUP;
+ }
+
+ btd_profile_register(&vcp_profile);
+ vcp_id = bt_vcp_register(vcp_attached, vcp_detached, NULL);
+
+ return 0;
+}
+
+static void vcp_exit(void)
+{
+ if (g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL) {
+ btd_profile_unregister(&vcp_profile);
+ bt_vcp_unregister(vcp_id);
+ }
+}
+
+BLUETOOTH_PLUGIN_DEFINE(vcp, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ vcp_init, vcp_exit)
--
2.25.1

2022-09-15 10:11:44

by Sathish Narasimman

[permalink] [raw]
Subject: [PATCH BlueZ v2 4/4] monitor/att: Add decoding support for Volume Control Serice

This adds decoding support for VCS attributes

> ACL Data RX: Handle 3585 flags 0x02 dlen 7
ATT: Read Request (0x0a) len 2
Handle: 0x0017 Type: Volume State (0x2b7d)
< ACL Data TX: Handle 3585 flags 0x00 dlen 8
ATT: Read Response (0x0b) len 3
Value: 000000
Handle: 0x0017 Type: Volume State (0x2b7d)
Volume Setting: 0
Not Muted: 0
Change Counter: 0
> HCI Event: Number of Completed Packets (0x13) plen 5
Num handles: 1
Handle: 3585 Address: 49:71:FC:C0:66:C6 (Resolvable)
Count: 1
> ACL Data RX: Handle 3585 flags 0x02 dlen 7
ATT: Read Request (0x0a) len 2
Handle: 0x001c Type: Volume Flags (0x2b7f)
< ACL Data TX: Handle 3585 flags 0x00 dlen 6
ATT: Read Response (0x0b) len 1
Value: 01
Handle: 0x001c Type: Volume Flags (0x2b7f)
Volume Falg: 1
---
monitor/att.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 159 insertions(+)

diff --git a/monitor/att.c b/monitor/att.c
index b7470f7a2ff4..3c1ff2e2aaa0 100644
--- a/monitor/att.c
+++ b/monitor/att.c
@@ -1590,6 +1590,162 @@ static void pac_context_notify(const struct l2cap_frame *frame)
print_pac_context(frame);
}

+static void print_vcs_state(const struct l2cap_frame *frame)
+{
+ uint8_t vol_set, mute, chng_ctr;
+
+ if (!l2cap_frame_get_u8((void *)frame, &vol_set)) {
+ print_text(COLOR_ERROR, "Volume Settings: invalid size");
+ goto done;
+ }
+ print_field(" Volume Setting: %u", vol_set);
+
+ if (!l2cap_frame_get_u8((void *)frame, &mute)) {
+ print_text(COLOR_ERROR, "Mute Filed: invalid size");
+ goto done;
+ }
+
+ switch (mute) {
+ case 0x00:
+ print_field(" Not Muted: %u", mute);
+ break;
+ case 0x01:
+ print_field(" Muted: %u", mute);
+ break;
+ default:
+ print_field(" Unknown Mute Value: %u", mute);
+ break;
+ }
+
+ if (!l2cap_frame_get_u8((void *)frame, &chng_ctr)) {
+ print_text(COLOR_ERROR, "Change Counter: invalid size");
+ goto done;
+ }
+ print_field(" Change Counter: %u", chng_ctr);
+
+done:
+ if (frame->size)
+ print_hex_field(" Data", frame->data, frame->size);
+}
+
+static void vol_state_read(const struct l2cap_frame *frame)
+{
+ print_vcs_state(frame);
+}
+
+static void vol_state_notify(const struct l2cap_frame *frame)
+{
+ print_vcs_state(frame);
+}
+
+static bool vcs_config_cmd(const struct l2cap_frame *frame)
+{
+ if (!l2cap_frame_print_u8((void *)frame, " Change Counter"))
+ return false;
+
+ return true;
+}
+
+static bool vcs_absolute_cmd(const struct l2cap_frame *frame)
+{
+ if (!l2cap_frame_print_u8((void *)frame, " Change Counter"))
+ return false;
+
+ if (!l2cap_frame_print_u8((void *)frame, " Volume Setting"))
+ return false;
+
+ return true;
+}
+
+#define ASE_CMD(_op, _desc, _func) \
+[_op] = { \
+ .desc = _desc, \
+ .func = _func, \
+}
+
+struct vcs_cmd {
+ const char *desc;
+ bool (*func)(const struct l2cap_frame *frame);
+} vcs_cmd_table[] = {
+ /* Opcode = 0x00 (Relative Volume Down) */
+ ASE_CMD(0x00, "Relative Volume Down", vcs_config_cmd),
+ /* Opcode = 0x01 (Relative Volume Up) */
+ ASE_CMD(0x01, "Relative Volume Up", vcs_config_cmd),
+ /* Opcode = 0x02 (Unmute/Relative Volume Down) */
+ ASE_CMD(0x02, "Unmute/Relative Volume Down", vcs_config_cmd),
+ /* Opcode = 0x03 (Unmute/Relative Volume Up) */
+ ASE_CMD(0x03, "Unmute/Relative Volume Up", vcs_config_cmd),
+ /* Opcode = 0x04 (Set Absolute Volume) */
+ ASE_CMD(0x04, "Set Absolute Volume", vcs_absolute_cmd),
+ /* Opcode = 0x05 (Unmute) */
+ ASE_CMD(0x05, "Unmute", vcs_config_cmd),
+ /* Opcode = 0x06 (Mute) */
+ ASE_CMD(0x06, "Mute", vcs_config_cmd),
+};
+
+static struct vcs_cmd *vcs_get_cmd(uint8_t op)
+{
+ if (op > ARRAY_SIZE(vcs_cmd_table))
+ return NULL;
+
+ return &vcs_cmd_table[op];
+}
+
+static void print_vcs_cmd(const struct l2cap_frame *frame)
+{
+ uint8_t op;
+ struct vcs_cmd *cmd;
+
+ if (!l2cap_frame_get_u8((void *)frame, &op)) {
+ print_text(COLOR_ERROR, "opcode: invalid size");
+ goto done;
+ }
+
+ cmd = vcs_get_cmd(op);
+ if (!cmd) {
+ print_field(" Opcode: Reserved (0x%2.2x)", op);
+ goto done;
+ }
+
+ print_field(" Opcode: %s (0x%2.2x)", cmd->desc, op);
+ if (!cmd->func(frame))
+ print_field(" Unknown Opcode");
+
+done:
+ if (frame->size)
+ print_hex_field(" Data", frame->data, frame->size);
+}
+
+static void vol_cp_write(const struct l2cap_frame *frame)
+{
+ print_vcs_cmd(frame);
+}
+
+static void print_vcs_flag(const struct l2cap_frame *frame)
+{
+ uint8_t vol_flag;
+
+ if (!l2cap_frame_get_u8((void *)frame, &vol_flag)) {
+ print_text(COLOR_ERROR, "Volume Flag: invalid size");
+ goto done;
+ }
+ print_field(" Volume Falg: %u", vol_flag);
+
+done:
+ if (frame->size)
+ print_hex_field(" Data", frame->data, frame->size);
+}
+
+static void vol_flag_read(const struct l2cap_frame *frame)
+{
+ print_vcs_flag(frame);
+}
+
+static void vol_flag_notify(const struct l2cap_frame *frame)
+{
+ print_vcs_flag(frame);
+}
+
#define GATT_HANDLER(_uuid, _read, _write, _notify) \
{ \
.uuid = { \
@@ -1617,6 +1773,9 @@ struct gatt_handler {
GATT_HANDLER(0x2bcc, pac_loc_read, NULL, pac_loc_notify),
GATT_HANDLER(0x2bcd, pac_context_read, NULL, pac_context_notify),
GATT_HANDLER(0x2bce, pac_context_read, NULL, pac_context_notify),
+ GATT_HANDLER(0x2b7d, vol_state_read, NULL, vol_state_notify),
+ GATT_HANDLER(0x2b7e, NULL, vol_cp_write, NULL),
+ GATT_HANDLER(0x2b7f, vol_flag_read, NULL, vol_flag_notify),
};

static struct gatt_handler *get_handler(struct gatt_db_attribute *attr)
--
2.25.1

2022-09-15 11:41:08

by bluez.test.bot

[permalink] [raw]
Subject: RE: [BlueZ,v2,1/4] lib/uuid: Add VCS UUIDs

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=677268

---Test result---

Test Summary:
CheckPatch FAIL 4.02 seconds
GitLint PASS 1.89 seconds
Prep - Setup ELL PASS 33.60 seconds
Build - Prep PASS 0.77 seconds
Build - Configure PASS 10.44 seconds
Build - Make PASS 1169.04 seconds
Make Check PASS 12.60 seconds
Make Check w/Valgrind PASS 356.41 seconds
Make Distcheck PASS 304.10 seconds
Build w/ext ELL - Configure PASS 11.04 seconds
Build w/ext ELL - Make PASS 108.76 seconds
Incremental Build w/ patches PASS 511.45 seconds
Scan Build WARNING 1239.48 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script with rule in .checkpatch.conf
Output:
[BlueZ,v2,2/4] shared/vcp: Add initial code for handling VCP
WARNING:PREFER_DEFINED_ATTRIBUTE_MACRO: Prefer __packed over __attribute__((packed))
#1154: FILE: src/shared/vcp.h:16:
+#define __packed __attribute__((packed))

/github/workspace/src/12977204.patch total: 0 errors, 1 warnings, 1106 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/12977204.patch has style problems, please review.

NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO

NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.

[BlueZ,v2,3/4] profiles: Add initial code for vcp plugin
ERROR:INITIALISED_STATIC: do not initialise statics to 0
#395: FILE: profiles/audio/vcp.c:288:
+static unsigned int vcp_id = 0;

/github/workspace/src/12977206.patch total: 1 errors, 0 warnings, 330 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/12977206.patch has style problems, please review.

NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO

NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.


##############################
Test: Scan Build - WARNING
Desc: Run Scan Build with patches
Output:
*****************************************************************************
The bugs reported by the scan-build may or may not be caused by your patches.
Please check the list and fix the bugs if they are caused by your patch.
*****************************************************************************
src/shared/vcp.c:285:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
if (!vdb && !vdb->vcs)
^~~~~~~~
src/shared/vcp.c:294:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
if (*change_counter != vstate->counter) {
^~~~~~~~~~~~~~~
src/shared/vcp.c:318:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
if (!vdb && !vdb->vcs)
^~~~~~~~
src/shared/vcp.c:327:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
if (*change_counter != vstate->counter) {
^~~~~~~~~~~~~~~
src/shared/vcp.c:351:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
if (!vdb && !vdb->vcs)
^~~~~~~~
src/shared/vcp.c:360:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
if (*change_counter != vstate->counter) {
^~~~~~~~~~~~~~~
src/shared/vcp.c:385:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
if (!vdb && !vdb->vcs)
^~~~~~~~
src/shared/vcp.c:394:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
if (*change_counter != vstate->counter) {
^~~~~~~~~~~~~~~
src/shared/vcp.c:419:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
if (!vdb && !vdb->vcs)
^~~~~~~~
src/shared/vcp.c:428:29: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
if (req->change_counter != vstate->counter) {
^~~~~~~~~~~~~~~
src/shared/vcp.c:452:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
if (!vdb && !vdb->vcs)
^~~~~~~~
src/shared/vcp.c:461:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
if (*change_counter != vstate->counter) {
^~~~~~~~~~~~~~~
src/shared/vcp.c:485:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
if (!vdb && !vdb->vcs)
^~~~~~~~
src/shared/vcp.c:494:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
if (*change_counter != vstate->counter) {
^~~~~~~~~~~~~~~
14 warnings generated.




---
Regards,
Linux Bluetooth

2022-09-15 21:06:42

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [BlueZ,v2,1/4] lib/uuid: Add VCS UUIDs

Hi Sathish,

On Thu, Sep 15, 2022 at 4:41 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=677268
>
> ---Test result---
>
> Test Summary:
> CheckPatch FAIL 4.02 seconds
> GitLint PASS 1.89 seconds
> Prep - Setup ELL PASS 33.60 seconds
> Build - Prep PASS 0.77 seconds
> Build - Configure PASS 10.44 seconds
> Build - Make PASS 1169.04 seconds
> Make Check PASS 12.60 seconds
> Make Check w/Valgrind PASS 356.41 seconds
> Make Distcheck PASS 304.10 seconds
> Build w/ext ELL - Configure PASS 11.04 seconds
> Build w/ext ELL - Make PASS 108.76 seconds
> Incremental Build w/ patches PASS 511.45 seconds
> Scan Build WARNING 1239.48 seconds
>
> Details
> ##############################
> Test: CheckPatch - FAIL
> Desc: Run checkpatch.pl script with rule in .checkpatch.conf
> Output:
> [BlueZ,v2,2/4] shared/vcp: Add initial code for handling VCP
> WARNING:PREFER_DEFINED_ATTRIBUTE_MACRO: Prefer __packed over __attribute__((packed))
> #1154: FILE: src/shared/vcp.h:16:
> +#define __packed __attribute__((packed))
>
> /github/workspace/src/12977204.patch total: 0 errors, 1 warnings, 1106 lines checked
>
> NOTE: For some of the reported defects, checkpatch may be able to
> mechanically convert to the typical style using --fix or --fix-inplace.
>
> /github/workspace/src/12977204.patch has style problems, please review.
>
> NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
>
> NOTE: If any of the errors are false positives, please report
> them to the maintainer, see CHECKPATCH in MAINTAINERS.
>
> [BlueZ,v2,3/4] profiles: Add initial code for vcp plugin
> ERROR:INITIALISED_STATIC: do not initialise statics to 0
> #395: FILE: profiles/audio/vcp.c:288:
> +static unsigned int vcp_id = 0;
>
> /github/workspace/src/12977206.patch total: 1 errors, 0 warnings, 330 lines checked
>
> NOTE: For some of the reported defects, checkpatch may be able to
> mechanically convert to the typical style using --fix or --fix-inplace.
>
> /github/workspace/src/12977206.patch has style problems, please review.
>
> NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
>
> NOTE: If any of the errors are false positives, please report
> them to the maintainer, see CHECKPATCH in MAINTAINERS.
>
>
> ##############################
> Test: Scan Build - WARNING
> Desc: Run Scan Build with patches
> Output:
> *****************************************************************************
> The bugs reported by the scan-build may or may not be caused by your patches.
> Please check the list and fix the bugs if they are caused by your patch.
> *****************************************************************************
> src/shared/vcp.c:285:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
> if (!vdb && !vdb->vcs)
> ^~~~~~~~
> src/shared/vcp.c:294:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
> if (*change_counter != vstate->counter) {
> ^~~~~~~~~~~~~~~
> src/shared/vcp.c:318:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
> if (!vdb && !vdb->vcs)
> ^~~~~~~~
> src/shared/vcp.c:327:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
> if (*change_counter != vstate->counter) {
> ^~~~~~~~~~~~~~~
> src/shared/vcp.c:351:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
> if (!vdb && !vdb->vcs)
> ^~~~~~~~
> src/shared/vcp.c:360:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
> if (*change_counter != vstate->counter) {
> ^~~~~~~~~~~~~~~
> src/shared/vcp.c:385:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
> if (!vdb && !vdb->vcs)
> ^~~~~~~~
> src/shared/vcp.c:394:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
> if (*change_counter != vstate->counter) {
> ^~~~~~~~~~~~~~~
> src/shared/vcp.c:419:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
> if (!vdb && !vdb->vcs)
> ^~~~~~~~
> src/shared/vcp.c:428:29: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
> if (req->change_counter != vstate->counter) {
> ^~~~~~~~~~~~~~~
> src/shared/vcp.c:452:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
> if (!vdb && !vdb->vcs)
> ^~~~~~~~
> src/shared/vcp.c:461:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
> if (*change_counter != vstate->counter) {
> ^~~~~~~~~~~~~~~
> src/shared/vcp.c:485:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
> if (!vdb && !vdb->vcs)
> ^~~~~~~~
> src/shared/vcp.c:494:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
> if (*change_counter != vstate->counter) {
> ^~~~~~~~~~~~~~~
> 14 warnings generated.

Lets have these warnings fixed so vstate needs to be checked.

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


--
Luiz Augusto von Dentz

2022-09-15 21:06:42

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ v2 4/4] monitor/att: Add decoding support for Volume Control Serice

Hi Sathish,

On Thu, Sep 15, 2022 at 3:11 AM Sathish Narasimman
<[email protected]> wrote:
>
> This adds decoding support for VCS attributes
>
> > ACL Data RX: Handle 3585 flags 0x02 dlen 7
> ATT: Read Request (0x0a) len 2
> Handle: 0x0017 Type: Volume State (0x2b7d)
> < ACL Data TX: Handle 3585 flags 0x00 dlen 8
> ATT: Read Response (0x0b) len 3
> Value: 000000
> Handle: 0x0017 Type: Volume State (0x2b7d)
> Volume Setting: 0
> Not Muted: 0
> Change Counter: 0
> > HCI Event: Number of Completed Packets (0x13) plen 5
> Num handles: 1
> Handle: 3585 Address: 49:71:FC:C0:66:C6 (Resolvable)
> Count: 1
> > ACL Data RX: Handle 3585 flags 0x02 dlen 7
> ATT: Read Request (0x0a) len 2
> Handle: 0x001c Type: Volume Flags (0x2b7f)
> < ACL Data TX: Handle 3585 flags 0x00 dlen 6
> ATT: Read Response (0x0b) len 1
> Value: 01
> Handle: 0x001c Type: Volume Flags (0x2b7f)
> Volume Falg: 1
> ---
> monitor/att.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 159 insertions(+)
>
> diff --git a/monitor/att.c b/monitor/att.c
> index b7470f7a2ff4..3c1ff2e2aaa0 100644
> --- a/monitor/att.c
> +++ b/monitor/att.c
> @@ -1590,6 +1590,162 @@ static void pac_context_notify(const struct l2cap_frame *frame)
> print_pac_context(frame);
> }
>
> +static void print_vcs_state(const struct l2cap_frame *frame)
> +{
> + uint8_t vol_set, mute, chng_ctr;
> +
> + if (!l2cap_frame_get_u8((void *)frame, &vol_set)) {
> + print_text(COLOR_ERROR, "Volume Settings: invalid size");
> + goto done;
> + }
> + print_field(" Volume Setting: %u", vol_set);
> +
> + if (!l2cap_frame_get_u8((void *)frame, &mute)) {
> + print_text(COLOR_ERROR, "Mute Filed: invalid size");
> + goto done;
> + }
> +
> + switch (mute) {
> + case 0x00:
> + print_field(" Not Muted: %u", mute);
> + break;
> + case 0x01:
> + print_field(" Muted: %u", mute);
> + break;
> + default:
> + print_field(" Unknown Mute Value: %u", mute);
> + break;
> + }
> +
> + if (!l2cap_frame_get_u8((void *)frame, &chng_ctr)) {
> + print_text(COLOR_ERROR, "Change Counter: invalid size");
> + goto done;
> + }
> + print_field(" Change Counter: %u", chng_ctr);
> +
> +done:
> + if (frame->size)
> + print_hex_field(" Data", frame->data, frame->size);
> +}
> +
> +static void vol_state_read(const struct l2cap_frame *frame)
> +{
> + print_vcs_state(frame);
> +}
> +
> +static void vol_state_notify(const struct l2cap_frame *frame)
> +{
> + print_vcs_state(frame);
> +}
> +
> +static bool vcs_config_cmd(const struct l2cap_frame *frame)
> +{
> + if (!l2cap_frame_print_u8((void *)frame, " Change Counter"))
> + return false;
> +
> + return true;
> +}
> +
> +static bool vcs_absolute_cmd(const struct l2cap_frame *frame)
> +{
> + if (!l2cap_frame_print_u8((void *)frame, " Change Counter"))
> + return false;
> +
> + if (!l2cap_frame_print_u8((void *)frame, " Volume Setting"))
> + return false;
> +
> + return true;
> +}
> +
> +#define ASE_CMD(_op, _desc, _func) \
> +[_op] = { \
> + .desc = _desc, \
> + .func = _func, \
> +}

Well those are not really ASE commands so Id probably reword that to
be VCS_CMD instead.

> +struct vcs_cmd {
> + const char *desc;
> + bool (*func)(const struct l2cap_frame *frame);
> +} vcs_cmd_table[] = {
> + /* Opcode = 0x00 (Relative Volume Down) */
> + ASE_CMD(0x00, "Relative Volume Down", vcs_config_cmd),
> + /* Opcode = 0x01 (Relative Volume Up) */
> + ASE_CMD(0x01, "Relative Volume Up", vcs_config_cmd),
> + /* Opcode = 0x02 (Unmute/Relative Volume Down) */
> + ASE_CMD(0x02, "Unmute/Relative Volume Down", vcs_config_cmd),
> + /* Opcode = 0x03 (Unmute/Relative Volume Up) */
> + ASE_CMD(0x03, "Unmute/Relative Volume Up", vcs_config_cmd),
> + /* Opcode = 0x04 (Set Absolute Volume) */
> + ASE_CMD(0x04, "Set Absolute Volume", vcs_absolute_cmd),
> + /* Opcode = 0x05 (Unmute) */
> + ASE_CMD(0x05, "Unmute", vcs_config_cmd),
> + /* Opcode = 0x06 (Mute) */
> + ASE_CMD(0x06, "Mute", vcs_config_cmd),
> +};
> +
> +static struct vcs_cmd *vcs_get_cmd(uint8_t op)
> +{
> + if (op > ARRAY_SIZE(vcs_cmd_table))
> + return NULL;
> +
> + return &vcs_cmd_table[op];
> +}
> +
> +static void print_vcs_cmd(const struct l2cap_frame *frame)
> +{
> + uint8_t op;
> + struct vcs_cmd *cmd;
> +
> + if (!l2cap_frame_get_u8((void *)frame, &op)) {
> + print_text(COLOR_ERROR, "opcode: invalid size");
> + goto done;
> + }
> +
> + cmd = vcs_get_cmd(op);
> + if (!cmd) {
> + print_field(" Opcode: Reserved (0x%2.2x)", op);
> + goto done;
> + }
> +
> + print_field(" Opcode: %s (0x%2.2x)", cmd->desc, op);
> + if (!cmd->func(frame))
> + print_field(" Unknown Opcode");
> +
> +done:
> + if (frame->size)
> + print_hex_field(" Data", frame->data, frame->size);
> +}
> +
> +static void vol_cp_write(const struct l2cap_frame *frame)
> +{
> + print_vcs_cmd(frame);
> +}
> +
> +static void print_vcs_flag(const struct l2cap_frame *frame)
> +{
> + uint8_t vol_flag;
> +
> + if (!l2cap_frame_get_u8((void *)frame, &vol_flag)) {
> + print_text(COLOR_ERROR, "Volume Flag: invalid size");
> + goto done;
> + }
> + print_field(" Volume Falg: %u", vol_flag);
> +
> +done:
> + if (frame->size)
> + print_hex_field(" Data", frame->data, frame->size);
> +}
> +
> +static void vol_flag_read(const struct l2cap_frame *frame)
> +{
> + print_vcs_flag(frame);
> +}
> +
> +static void vol_flag_notify(const struct l2cap_frame *frame)
> +{
> + print_vcs_flag(frame);
> +}
> +
> #define GATT_HANDLER(_uuid, _read, _write, _notify) \
> { \
> .uuid = { \
> @@ -1617,6 +1773,9 @@ struct gatt_handler {
> GATT_HANDLER(0x2bcc, pac_loc_read, NULL, pac_loc_notify),
> GATT_HANDLER(0x2bcd, pac_context_read, NULL, pac_context_notify),
> GATT_HANDLER(0x2bce, pac_context_read, NULL, pac_context_notify),
> + GATT_HANDLER(0x2b7d, vol_state_read, NULL, vol_state_notify),
> + GATT_HANDLER(0x2b7e, NULL, vol_cp_write, NULL),
> + GATT_HANDLER(0x2b7f, vol_flag_read, NULL, vol_flag_notify),
> };
>
> static struct gatt_handler *get_handler(struct gatt_db_attribute *attr)
> --
> 2.25.1
>


--
Luiz Augusto von Dentz

2022-09-15 21:28:52

by patchwork-bot+bluetooth

[permalink] [raw]
Subject: Re: [PATCH BlueZ v2 1/4] lib/uuid: Add VCS UUIDs

Hello:

This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <[email protected]>:

On Thu, 15 Sep 2022 15:24:09 +0530 you wrote:
> This adds Volume Control Service UUIDs which will be used by
> Volume Control Profile.
> ---
> lib/uuid.h | 7 +++++++
> 1 file changed, 7 insertions(+)

Here is the summary with links:
- [BlueZ,v2,1/4] lib/uuid: Add VCS UUIDs
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=af1bfbb470c0
- [BlueZ,v2,2/4] shared/vcp: Add initial code for handling VCP
(no matching commit)
- [BlueZ,v2,3/4] profiles: Add initial code for vcp plugin
(no matching commit)
- [BlueZ,v2,4/4] monitor/att: Add decoding support for Volume Control Serice
(no matching commit)

You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html


2022-09-16 05:52:09

by Sathish Narasimman

[permalink] [raw]
Subject: Re: [BlueZ,v2,1/4] lib/uuid: Add VCS UUIDs

Hi Luiz

On Fri, Sep 16, 2022 at 2:37 AM Luiz Augusto von Dentz
<[email protected]> wrote:
>
> Hi Sathish,
>
> On Thu, Sep 15, 2022 at 4:41 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=677268
> >
> > ---Test result---
> >
> > Test Summary:
> > CheckPatch FAIL 4.02 seconds
> > GitLint PASS 1.89 seconds
> > Prep - Setup ELL PASS 33.60 seconds
> > Build - Prep PASS 0.77 seconds
> > Build - Configure PASS 10.44 seconds
> > Build - Make PASS 1169.04 seconds
> > Make Check PASS 12.60 seconds
> > Make Check w/Valgrind PASS 356.41 seconds
> > Make Distcheck PASS 304.10 seconds
> > Build w/ext ELL - Configure PASS 11.04 seconds
> > Build w/ext ELL - Make PASS 108.76 seconds
> > Incremental Build w/ patches PASS 511.45 seconds
> > Scan Build WARNING 1239.48 seconds
> >
> > Details
> > ##############################
> > Test: CheckPatch - FAIL
> > Desc: Run checkpatch.pl script with rule in .checkpatch.conf
> > Output:
> > [BlueZ,v2,2/4] shared/vcp: Add initial code for handling VCP
> > WARNING:PREFER_DEFINED_ATTRIBUTE_MACRO: Prefer __packed over __attribute__((packed))
> > #1154: FILE: src/shared/vcp.h:16:
> > +#define __packed __attribute__((packed))
> >
> > /github/workspace/src/12977204.patch total: 0 errors, 1 warnings, 1106 lines checked
> >
> > NOTE: For some of the reported defects, checkpatch may be able to
> > mechanically convert to the typical style using --fix or --fix-inplace.
> >
> > /github/workspace/src/12977204.patch has style problems, please review.
> >
> > NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
> >
> > NOTE: If any of the errors are false positives, please report
> > them to the maintainer, see CHECKPATCH in MAINTAINERS.
> >
> > [BlueZ,v2,3/4] profiles: Add initial code for vcp plugin
> > ERROR:INITIALISED_STATIC: do not initialise statics to 0
> > #395: FILE: profiles/audio/vcp.c:288:
> > +static unsigned int vcp_id = 0;
> >
> > /github/workspace/src/12977206.patch total: 1 errors, 0 warnings, 330 lines checked
> >
> > NOTE: For some of the reported defects, checkpatch may be able to
> > mechanically convert to the typical style using --fix or --fix-inplace.
> >
> > /github/workspace/src/12977206.patch has style problems, please review.
> >
> > NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO
> >
> > NOTE: If any of the errors are false positives, please report
> > them to the maintainer, see CHECKPATCH in MAINTAINERS.
> >
> >
> > ##############################
> > Test: Scan Build - WARNING
> > Desc: Run Scan Build with patches
> > Output:
> > *****************************************************************************
> > The bugs reported by the scan-build may or may not be caused by your patches.
> > Please check the list and fix the bugs if they are caused by your patch.
> > *****************************************************************************
> > src/shared/vcp.c:285:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
> > if (!vdb && !vdb->vcs)
> > ^~~~~~~~
> > src/shared/vcp.c:294:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
> > if (*change_counter != vstate->counter) {
> > ^~~~~~~~~~~~~~~
> > src/shared/vcp.c:318:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
> > if (!vdb && !vdb->vcs)
> > ^~~~~~~~
> > src/shared/vcp.c:327:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
> > if (*change_counter != vstate->counter) {
> > ^~~~~~~~~~~~~~~
> > src/shared/vcp.c:351:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
> > if (!vdb && !vdb->vcs)
> > ^~~~~~~~
> > src/shared/vcp.c:360:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
> > if (*change_counter != vstate->counter) {
> > ^~~~~~~~~~~~~~~
> > src/shared/vcp.c:385:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
> > if (!vdb && !vdb->vcs)
> > ^~~~~~~~
> > src/shared/vcp.c:394:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
> > if (*change_counter != vstate->counter) {
> > ^~~~~~~~~~~~~~~
> > src/shared/vcp.c:419:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
> > if (!vdb && !vdb->vcs)
> > ^~~~~~~~
> > src/shared/vcp.c:428:29: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
> > if (req->change_counter != vstate->counter) {
> > ^~~~~~~~~~~~~~~
> > src/shared/vcp.c:452:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
> > if (!vdb && !vdb->vcs)
> > ^~~~~~~~
> > src/shared/vcp.c:461:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
> > if (*change_counter != vstate->counter) {
> > ^~~~~~~~~~~~~~~
> > src/shared/vcp.c:485:15: warning: Access to field 'vcs' results in a dereference of a null pointer (loaded from variable 'vdb')
> > if (!vdb && !vdb->vcs)
> > ^~~~~~~~
> > src/shared/vcp.c:494:25: warning: Access to field 'counter' results in a dereference of a null pointer (loaded from variable 'vstate')
> > if (*change_counter != vstate->counter) {
> > ^~~~~~~~~~~~~~~
> > 14 warnings generated.
>
> Lets have these warnings fixed so vstate needs to be checked.
Thanks for the review. Will Update for sure.
Is there a way I can run Checkpatch.pl locally?
>
> >
> >
> >
> > ---
> > Regards,
> > Linux Bluetooth
> >
>
>
> --
> Luiz Augusto von Dentz

Sathish N