The following patches are for CSIP (Co-ordinated set identification
profile) Client role. The patches also includes using SIRK and adding it
to ADV data. Configuring CSIP Server values.
Sathish Narasimman (6):
lib/uuid: Add CSIS UUIDs
main.conf: Add CSIP profile configurable options
shared/csip: Add initial code for handling CSIP
profiles: Add initial code for csip plugin
monitor/att: Add decoding support for CSIP
tools: Add support to generate RSI using SIRK
Makefile.am | 1 +
Makefile.plugins | 5 +
configure.ac | 4 +
lib/uuid.h | 7 +
monitor/att.c | 73 ++++++
profiles/audio/csip.c | 319 ++++++++++++++++++++++++
src/btd.h | 9 +
src/main.c | 113 +++++++++
src/main.conf | 24 ++
src/shared/csip.c | 554 ++++++++++++++++++++++++++++++++++++++++++
src/shared/csip.h | 44 ++++
tools/advtest.c | 80 +++++-
12 files changed, 1231 insertions(+), 2 deletions(-)
create mode 100644 profiles/audio/csip.c
create mode 100644 src/shared/csip.c
create mode 100644 src/shared/csip.h
--
2.25.1
This introduces option to configure main.conf that can be used to
configure co-ordinated set identification profile.
---
src/btd.h | 9 ++++
src/main.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/main.conf | 24 +++++++++++
3 files changed, 146 insertions(+)
diff --git a/src/btd.h b/src/btd.h
index 63be6d8d4b3f..7c40492e5a1b 100644
--- a/src/btd.h
+++ b/src/btd.h
@@ -86,6 +86,13 @@ struct btd_defaults {
struct btd_le_defaults le;
};
+struct btd_csis {
+ uint8_t sirk_type;
+ uint8_t sirk_val[16];
+ uint8_t cs_size;
+ uint8_t cs_rank;
+};
+
struct btd_avdtp_opts {
uint8_t session_mode;
uint8_t stream_mode;
@@ -135,6 +142,8 @@ struct btd_opts {
enum jw_repairing_t jw_repairing;
struct btd_advmon_opts advmon;
+
+ struct btd_csis csis_defaults;
};
extern struct btd_opts btd_opts;
diff --git a/src/main.c b/src/main.c
index 1d357161feec..d05036a3380a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -60,6 +60,9 @@
#define DEFAULT_TEMPORARY_TIMEOUT 30 /* 30 seconds */
#define DEFAULT_NAME_REQUEST_RETRY_DELAY 300 /* 5 minutes */
+/*CSIP Profile - Server */
+#define DEFAULT_SIRK "761FAE703ED681F0C50B34155B6434FB"
+
#define SHUTDOWN_GRACE_SECONDS 10
struct btd_opts btd_opts;
@@ -145,6 +148,14 @@ static const char *gatt_options[] = {
NULL
};
+static const char *csip_options[] = {
+ "CsisSirkType",
+ "CsisSirkValue",
+ "CsisSize",
+ "CsisRank",
+ NULL
+};
+
static const char *avdtp_options[] = {
"SessionMode",
"StreamMode",
@@ -165,11 +176,55 @@ static const struct group_table {
{ "LE", le_options },
{ "Policy", policy_options },
{ "GATT", gatt_options },
+ { "CSIP", csip_options },
{ "AVDTP", avdtp_options },
{ "AdvMon", advmon_options },
{ }
};
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+static int8_t check_sirk_alpha_numeric(char *str)
+{
+ int8_t val;
+ char *s = str;
+
+ if (strlen(s) != 32) /* 32 Bytes of Alpha numeric string */
+ return 0;
+
+ for ( ; *s; s++) {
+ if (((*s >= '0') & (*s <= '9'))
+ || ((*s >= 'a') && (*s <= 'z'))
+ || ((*s >= 'A') && (*s <= 'Z'))) {
+ val = 1;
+ } else {
+ val = 0;
+ break;
+ }
+ }
+
+ return val;
+}
+
+static size_t hex2bin(const char *hexstr, uint8_t *buf, size_t buflen)
+{
+ size_t i, len;
+
+ if (!hexstr)
+ return 0;
+
+ len = MIN((strlen(hexstr) / 2), buflen);
+ memset(buf, 0, len);
+
+ for (i = 0; i < len; i++) {
+ if (sscanf(hexstr + (i * 2), "%02hhX", &buf[i]) != 1)
+ continue;
+ }
+
+ return len;
+}
GKeyFile *btd_get_main_conf(void)
{
@@ -925,6 +980,58 @@ static void parse_config(GKeyFile *config)
btd_opts.gatt_channels = val;
}
+ val = g_key_file_get_integer(config, "CSIP", "CsisSirkType", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ val = MIN(val, 2);
+ val = MAX(val, 1);
+ DBG("Csis Type: %u", val);
+ btd_opts.csis_defaults.cs_size = val;
+ }
+
+ str = g_key_file_get_string(config, "CSIP", "CsisSirkValue", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ DBG("Csis Sirk: %s", str);
+
+ if (!check_sirk_alpha_numeric(str)) {
+ DBG("SIRK is not apha numeric Value");
+ return;
+ }
+
+ btd_opts.csis_defaults.sirk_type = 1; /* Plain Text - Type*/
+ hex2bin(str, btd_opts.csis_defaults.sirk_val,
+ sizeof(btd_opts.csis_defaults.sirk_val));
+
+ g_free(str);
+ }
+
+ val = g_key_file_get_integer(config, "CSIP", "CsisSize", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ val = MIN(val, 0xFF);
+ val = MAX(val, 0);
+ DBG("Csis Size: %u", val);
+ btd_opts.csis_defaults.cs_size = val;
+ }
+
+ val = g_key_file_get_integer(config, "CSIP", "CsisRank", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ val = MIN(val, 0xFF);
+ val = MAX(val, 0);
+ DBG("Csis Rank: %u", val);
+ btd_opts.csis_defaults.cs_rank = val;
+ }
+
str = g_key_file_get_string(config, "AVDTP", "SessionMode", &err);
if (err) {
DBG("%s", err->message);
@@ -999,6 +1106,12 @@ static void init_defaults(void)
btd_opts.defaults.br.scan_type = 0xFFFF;
btd_opts.defaults.le.enable_advmon_interleave_scan = 0xFF;
+ btd_opts.csis_defaults.sirk_type = 1;
+ hex2bin(DEFAULT_SIRK, btd_opts.csis_defaults.sirk_val,
+ sizeof(btd_opts.csis_defaults.sirk_val));
+ btd_opts.csis_defaults.cs_size = 1;
+ btd_opts.csis_defaults.cs_rank = 1;
+
if (sscanf(VERSION, "%hhu.%hhu", &major, &minor) != 2)
return;
diff --git a/src/main.conf b/src/main.conf
index 2796f155ebaa..0cc0812f8587 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -247,6 +247,30 @@
# Default to 3
#Channels = 3
+[CSIP]
+# CSIP - Co-ordinated Set Identification Profile
+# SIRK Types which determines the value type for CsisSirkValue
+# Possible values:
+# 1 - Plain text
+# 2 - encrypted
+#CsisSirkType = 1
+
+# CSIP - Co-ordinated Set Identification Profile
+# SIRK - Set Identification resolution key which is common for all the
+# sets. They SIRK key is used to identify its sets. This can be any
+# 128 bit value.
+# Possible Values:
+# 16 byte hexadecimal value
+#CsisSirkValue = 861FAE703ED681F0C50B34155B6434FB
+
+#CSIP - Size
+#Total no of sets belongs to this Profile
+#CsisSize = 1
+
+#CSIP - Rank
+#Rank for the device
+#CsisRank = 1
+
[AVDTP]
# AVDTP L2CAP Signalling Channel Mode.
# Possible values:
--
2.25.1
This adds initial code for Coordinated Set Identification Profile.
---
Makefile.am | 1 +
src/shared/csip.c | 554 ++++++++++++++++++++++++++++++++++++++++++++++
src/shared/csip.h | 44 ++++
3 files changed, 599 insertions(+)
create mode 100644 src/shared/csip.c
create mode 100644 src/shared/csip.h
diff --git a/Makefile.am b/Makefile.am
index aa3a5e053cd8..b546a1803dfd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -233,6 +233,7 @@ shared_sources = src/shared/io.h src/shared/timeout.h \
src/shared/bap.h src/shared/bap.c src/shared/ascs.h \
src/shared/mcs.h src/shared/mcp.h src/shared/mcp.c \
src/shared/vcp.c src/shared/vcp.h \
+ src/shared/csip.c src/shared/csip.h \
src/shared/lc3.h src/shared/tty.h
if READLINE
diff --git a/src/shared/csip.c b/src/shared/csip.c
new file mode 100644
index 000000000000..98e42d914b16
--- /dev/null
+++ b/src/shared/csip.c
@@ -0,0 +1,554 @@
+// 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 <glib.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/csip.h"
+#include "src/btd.h"
+
+#define DBG(_csip, fmt, arg...) \
+ csip_debug(_csip, "%s:%s() " fmt, __FILE__, __func__, ## arg)
+
+/* SIRK is now hardcoded in the code. This can be moved
+ * to a configuration file. Since the code is to validate
+ * the CSIP use case of set member
+ */
+#define SIRK "761FAE703ED681F0C50B34155B6434FB"
+#define CSIS_SIZE 0x02
+#define CSIS_LOCK 0x01
+#define CSIS_RANK 0x01
+#define CSIS_PLAINTEXT 0x01
+#define CSIS_ENC 0x02
+
+struct bt_csip_db {
+ struct gatt_db *db;
+ struct bt_csis *csis;
+};
+
+struct csis_sirk {
+ uint8_t type;
+ uint8_t val[16];
+} __packed;
+
+struct bt_csis {
+ struct bt_csip_db *cdb;
+ struct csis_sirk *sirk;
+ uint8_t cs_size;
+ uint8_t cs_lock;
+ uint8_t cs_rank;
+ struct gatt_db_attribute *service;
+ struct gatt_db_attribute *csirk;
+ struct gatt_db_attribute *csize;
+ struct gatt_db_attribute *cslock;
+ struct gatt_db_attribute *cslock_ccc;
+ struct gatt_db_attribute *crank;
+};
+
+struct bt_csip_cb {
+ unsigned int id;
+ bt_csip_func_t attached;
+ bt_csip_func_t detached;
+ void *user_data;
+};
+
+struct bt_csip {
+ int ref_count;
+ struct bt_csip_db *ldb;
+ struct bt_csip_db *rdb;
+ struct bt_gatt_client *client;
+ struct bt_att *att;
+
+ struct queue *pending;
+
+ bt_csip_debug_func_t debug_func;
+ bt_csip_destroy_func_t debug_destroy;
+ void *debug_data;
+ void *user_data;
+};
+
+static struct queue *csip_db;
+static struct queue *csip_cbs;
+static struct queue *sessions;
+
+static void csip_detached(void *data, void *user_data)
+{
+ struct bt_csip_cb *cb = data;
+ struct bt_csip *csip = user_data;
+
+ cb->detached(csip, cb->user_data);
+}
+
+void bt_csip_detach(struct bt_csip *csip)
+{
+ if (!queue_remove(sessions, csip))
+ return;
+
+ bt_gatt_client_unref(csip->client);
+ csip->client = NULL;
+
+ queue_foreach(csip_cbs, csip_detached, csip);
+}
+
+static void csip_db_free(void *data)
+{
+ struct bt_csip_db *cdb = data;
+
+ if (!cdb)
+ return;
+
+ gatt_db_unref(cdb->db);
+
+ free(cdb->csis);
+ free(cdb);
+}
+static void csip_free(void *data)
+{
+ struct bt_csip *csip = data;
+
+ bt_csip_detach(csip);
+
+ csip_db_free(csip->rdb);
+
+ queue_destroy(csip->pending, NULL);
+
+ free(csip);
+}
+
+struct bt_att *bt_csip_get_att(struct bt_csip *csip)
+{
+ if (!csip)
+ return NULL;
+
+ if (csip->att)
+ return csip->att;
+
+ return bt_gatt_client_get_att(csip->client);
+}
+
+struct bt_csip *bt_csip_ref(struct bt_csip *csip)
+{
+ if (!csip)
+ return NULL;
+
+ __sync_fetch_and_add(&csip->ref_count, 1);
+
+ return csip;
+}
+
+void bt_csip_unref(struct bt_csip *csip)
+{
+ if (!csip)
+ return;
+
+ if (__sync_sub_and_fetch(&csip->ref_count, 1))
+ return;
+
+ csip_free(csip);
+}
+
+static void csip_debug(struct bt_csip *csip, const char *format, ...)
+{
+ va_list ap;
+
+ if (!csip || !format || !csip->debug_func)
+ return;
+
+ va_start(ap, format);
+ util_debug_va(csip->debug_func, csip->debug_data, format, ap);
+ va_end(ap);
+}
+
+static void csis_sirk_read(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct bt_csis *csis = user_data;
+ struct iovec iov;
+
+ iov.iov_base = csis->sirk;
+ iov.iov_len = sizeof(struct csis_sirk);
+
+ gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base,
+ iov.iov_len);
+}
+
+static void csis_size_read(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct bt_csis *csis = user_data;
+ struct iovec iov;
+
+ iov.iov_base = &csis->cs_size;
+ iov.iov_len = sizeof(csis->cs_size);
+
+ gatt_db_attribute_read_result(attrib, id, 0, iov.iov_base,
+ iov.iov_len);
+}
+
+static void csis_lock_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ uint8_t value = CSIS_LOCK;
+
+ gatt_db_attribute_read_result(attrib, id, 0, &value, sizeof(value));
+}
+
+static void csis_lock_write_cb(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)
+{
+ gatt_db_attribute_write_result(attrib, id, 0);
+}
+
+static void csis_rank_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ uint8_t value = CSIS_RANK;
+
+ gatt_db_attribute_read_result(attrib, id, 0, &value, sizeof(value));
+}
+
+static struct bt_csis *csis_new(struct gatt_db *db)
+{
+ struct bt_csis *csis;
+ struct csis_sirk *sirk;
+ bt_uuid_t uuid;
+
+ /* For Common Audio Service*/
+ struct gatt_db_attribute *service;
+
+ if (!db)
+ return NULL;
+
+ csis = new0(struct bt_csis, 1);
+ sirk = new0(struct csis_sirk, 1);
+
+ sirk->type = btd_opts.csis_defaults.sirk_type;
+ memcpy(sirk->val, btd_opts.csis_defaults.sirk_val,
+ sizeof(sirk->val));
+ csis->sirk = sirk;
+ csis->cs_size = btd_opts.csis_defaults.cs_size;
+ csis->cs_lock = 1;
+ csis->cs_rank = btd_opts.csis_defaults.cs_rank;
+
+ /* Populate DB with CSIS attributes */
+ bt_uuid16_create(&uuid, CSIS_UUID);
+ csis->service = gatt_db_add_service(db, &uuid, true, 10);
+
+ bt_uuid16_create(&uuid, CS_SIRK);
+ csis->csirk = gatt_db_service_add_characteristic(csis->service,
+ &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ,
+ csis_sirk_read, NULL,
+ csis);
+
+ bt_uuid16_create(&uuid, CS_SIZE);
+ csis->csize = gatt_db_service_add_characteristic(csis->service,
+ &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ,
+ csis_size_read, NULL,
+ csis);
+
+ /* Lock */
+ bt_uuid16_create(&uuid, CS_LOCK);
+ csis->cslock = gatt_db_service_add_characteristic(csis->service, &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE |
+ BT_GATT_CHRC_PROP_NOTIFY,
+ csis_lock_read_cb,
+ csis_lock_write_cb,
+ csis);
+
+ csis->cslock_ccc = gatt_db_service_add_ccc(csis->service,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE);
+
+ /* Rank */
+ bt_uuid16_create(&uuid, CS_RANK);
+ csis->crank = gatt_db_service_add_characteristic(csis->service, &uuid,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ,
+ csis_rank_read_cb,
+ NULL, csis);
+
+ /* Add the CAS service */
+ bt_uuid16_create(&uuid, 0x1853);
+ service = gatt_db_add_service(db, &uuid, true, 2);
+ gatt_db_service_add_included(service, csis->service);
+ gatt_db_service_set_active(service, true);
+ gatt_db_service_add_included(service, csis->service);
+
+ gatt_db_service_set_active(csis->service, true);
+
+ return csis;
+}
+
+static struct bt_csip_db *csip_db_new(struct gatt_db *db)
+{
+ struct bt_csip_db *cdb;
+
+ if (!db)
+ return NULL;
+
+ cdb = new0(struct bt_csip_db, 1);
+ cdb->db = gatt_db_ref(db);
+
+ if (!csip_db)
+ csip_db = queue_new();
+
+ cdb->csis = csis_new(db);
+ cdb->csis->cdb = cdb;
+
+ queue_push_tail(csip_db, cdb);
+
+ return cdb;
+}
+
+bool bt_csip_set_user_data(struct bt_csip *csip, void *user_data)
+{
+ if (!csip)
+ return false;
+
+ csip->user_data = user_data;
+
+ return true;
+}
+
+static bool csip_db_match(const void *data, const void *match_data)
+{
+ const struct bt_csip_db *cdb = data;
+ const struct gatt_db *db = match_data;
+
+ return (cdb->db == db);
+}
+
+static struct bt_csip_db *csip_get_db(struct gatt_db *db)
+{
+ struct bt_csip_db *cdb;
+
+ cdb = queue_find(csip_db, csip_db_match, db);
+ if (cdb)
+ return cdb;
+
+ return csip_db_new(db);
+}
+
+void bt_csip_add_db(struct gatt_db *db)
+{
+ csip_db_new(db);
+}
+
+bool bt_csip_set_debug(struct bt_csip *csip, bt_csip_debug_func_t func,
+ void *user_data, bt_csip_destroy_func_t destroy)
+{
+ if (!csip)
+ return false;
+
+ if (csip->debug_destroy)
+ csip->debug_destroy(csip->debug_data);
+
+ csip->debug_func = func;
+ csip->debug_destroy = destroy;
+ csip->debug_data = user_data;
+
+ return true;
+}
+
+unsigned int bt_csip_register(bt_csip_func_t attached, bt_csip_func_t detached,
+ void *user_data)
+{
+ struct bt_csip_cb *cb;
+ static unsigned int id;
+
+ if (!attached && !detached)
+ return 0;
+
+ if (!csip_cbs)
+ csip_cbs = queue_new();
+
+ cb = new0(struct bt_csip_cb, 1);
+ cb->id = ++id ? id : ++id;
+ cb->attached = attached;
+ cb->detached = detached;
+ cb->user_data = user_data;
+
+ queue_push_tail(csip_cbs, cb);
+
+ return cb->id;
+}
+
+static bool match_id(const void *data, const void *match_data)
+{
+ const struct bt_csip_cb *cb = data;
+ unsigned int id = PTR_TO_UINT(match_data);
+
+ return (cb->id == id);
+}
+
+bool bt_csip_unregister(unsigned int id)
+{
+ struct bt_csip_cb *cb;
+
+ cb = queue_remove_if(csip_cbs, match_id, UINT_TO_PTR(id));
+ if (!cb)
+ return false;
+
+ free(cb);
+
+ return true;
+}
+
+struct bt_csip *bt_csip_new(struct gatt_db *ldb, struct gatt_db *rdb)
+{
+ struct bt_csip *csip;
+ struct bt_csip_db *db;
+
+ if (!ldb)
+ return NULL;
+
+ db = csip_get_db(ldb);
+ if (!db)
+ return NULL;
+
+ csip = new0(struct bt_csip, 1);
+ csip->ldb = db;
+ csip->pending = queue_new();
+
+ if (!rdb)
+ goto done;
+
+ db = new0(struct bt_csip_db, 1);
+ db->db = gatt_db_ref(rdb);
+
+ csip->rdb = db;
+
+done:
+ bt_csip_ref(csip);
+
+ return csip;
+}
+
+static struct bt_csis *csip_get_csis(struct bt_csip *csip)
+{
+ if (!csip)
+ return NULL;
+
+ if (csip->rdb->csis)
+ return csip->rdb->csis;
+
+ csip->rdb->csis = new0(struct bt_csis, 1);
+ csip->rdb->csis->cdb = csip->rdb;
+
+ return csip->rdb->csis;
+}
+
+static void foreach_csis_char(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct bt_csip *csip = user_data;
+ uint16_t value_handle;
+ bt_uuid_t uuid, uuid_csirk, uuid_csize;
+ struct bt_csis *csis;
+
+ if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle,
+ NULL, NULL, &uuid))
+ return;
+
+ bt_uuid16_create(&uuid_csirk, CS_SIRK);
+ bt_uuid16_create(&uuid_csize, CS_SIZE);
+
+ if (!bt_uuid_cmp(&uuid, &uuid_csirk)) {
+ DBG(csip, "CSIS IRK found: handle 0x%04x", value_handle);
+
+ csis = csip_get_csis(csip);
+ if (!csis || csis->sirk)
+ return;
+
+ csis->csirk = attr;
+ return;
+ }
+
+ if (!bt_uuid_cmp(&uuid, &uuid_csize)) {
+ DBG(csip, "CSIS SIZE found: handle 0x%04x", value_handle);
+
+ csis = csip_get_csis(csip);
+ if (!csis)
+ return;
+
+ csis->csize = attr;
+ }
+
+}
+static void foreach_csis_service(struct gatt_db_attribute *attr,
+ void *user_data)
+{
+ struct bt_csip *csip = user_data;
+ struct bt_csis *csis = csip_get_csis(csip);
+
+ csis->service = attr;
+
+ gatt_db_service_set_claimed(attr, true);
+
+ gatt_db_service_foreach_char(attr, foreach_csis_char, csip);
+}
+
+bool bt_csip_attach(struct bt_csip *csip, struct bt_gatt_client *client)
+{
+ bt_uuid_t uuid;
+
+ if (!sessions)
+ sessions = queue_new();
+
+ queue_push_tail(sessions, csip);
+
+ if (!client)
+ return true;
+
+ if (csip->client)
+ return false;
+
+ csip->client = bt_gatt_client_clone(client);
+ if (!csip->client)
+ return false;
+
+ bt_uuid16_create(&uuid, CSIS_UUID);
+ gatt_db_foreach_service(csip->ldb->db, &uuid, foreach_csis_service,
+ csip);
+
+ return true;
+}
+
diff --git a/src/shared/csip.h b/src/shared/csip.h
new file mode 100644
index 000000000000..bd88ccf3a0b2
--- /dev/null
+++ b/src/shared/csip.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2022 Intel Corporation. All rights reserved.
+ *
+ */
+
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include "src/shared/io.h"
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+struct bt_csip;
+
+typedef void (*bt_csip_destroy_func_t)(void *user_data);
+typedef void (*bt_csip_debug_func_t)(const char *str, void *user_data);
+typedef void (*bt_csip_func_t)(struct bt_csip *csip, void *user_data);
+struct bt_csip *bt_csip_ref(struct bt_csip *csip);
+void bt_csip_unref(struct bt_csip *csip);
+
+void bt_csip_add_db(struct gatt_db *db);
+
+bool bt_csip_attach(struct bt_csip *csip, struct bt_gatt_client *client);
+void bt_csip_detach(struct bt_csip *csip);
+
+bool bt_csip_set_debug(struct bt_csip *csip, bt_csip_debug_func_t func,
+ void *user_data, bt_csip_destroy_func_t destroy);
+
+struct bt_att *bt_csip_get_att(struct bt_csip *csip);
+
+bool bt_csip_set_user_data(struct bt_csip *csip, void *user_data);
+
+/* Session related function */
+unsigned int bt_csip_register(bt_csip_func_t added, bt_csip_func_t removed,
+ void *user_data);
+bool bt_csip_unregister(unsigned int id);
+struct bt_csip *bt_csip_new(struct gatt_db *ldb, struct gatt_db *rdb);
+
--
2.25.1
This adds decoding support for CSIS attributes
---
monitor/att.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/monitor/att.c b/monitor/att.c
index d14cbd165697..6fd740aa4cd4 100644
--- a/monitor/att.c
+++ b/monitor/att.c
@@ -1733,6 +1733,73 @@ static void pac_context_notify(const struct l2cap_frame *frame)
print_pac_context(frame);
}
+static void csip_rank_read(const struct l2cap_frame *frame)
+{
+ uint8_t rank;
+
+ if (!l2cap_frame_get_u8((void *)frame, &rank)) {
+ print_text(COLOR_ERROR, "CSIP Rank: invalid size");
+ goto done;
+ }
+ print_field(" CSIP Rank: %u", rank);
+
+done:
+ if (frame->size)
+ print_hex_field(" Data", frame->data, frame->size);
+}
+
+static void csip_lock_read(const struct l2cap_frame *frame)
+{
+ uint8_t lock;
+
+ if (!l2cap_frame_get_u8((void *)frame, &lock)) {
+ print_text(COLOR_ERROR, "CSIP LOCK: invalid size");
+ goto done;
+ }
+ print_field(" CSIP LOCK: %u", lock);
+
+done:
+ if (frame->size)
+ print_hex_field(" Data", frame->data, frame->size);
+}
+
+static void print_csip_size(const struct l2cap_frame *frame)
+{
+ uint8_t size;
+
+ if (!l2cap_frame_get_u8((void *)frame, &size)) {
+ print_text(COLOR_ERROR, "CSIP SIZE: invalid size");
+ goto done;
+ }
+ print_field(" CSIP SIZE: %u", size);
+
+done:
+ if (frame->size)
+ print_hex_field(" Data", frame->data, frame->size);
+}
+
+static void csip_size_read(const struct l2cap_frame *frame)
+{
+ print_csip_size(frame);
+}
+
+static void csip_size_notify(const struct l2cap_frame *frame)
+{
+ print_csip_size(frame);
+}
+
+static void csip_sirk_read(const struct l2cap_frame *frame)
+{
+ if (frame->size)
+ print_hex_field(" SIRK", frame->data, frame->size);
+}
+
+static void csip_sirk_notify(const struct l2cap_frame *frame)
+{
+ if (frame->size)
+ print_hex_field(" SIRK", frame->data, frame->size);
+}
+
static void print_vcs_state(const struct l2cap_frame *frame)
{
uint8_t vol_set, mute, chng_ctr;
@@ -2413,6 +2480,12 @@ struct gatt_handler {
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),
+
+ GATT_HANDLER(0x2b84, csip_sirk_read, NULL, csip_sirk_notify),
+ GATT_HANDLER(0x2b85, csip_size_read, NULL, csip_size_notify),
+ GATT_HANDLER(0x2b86, csip_lock_read, NULL, NULL),
+ GATT_HANDLER(0x2b87, csip_rank_read, NULL, NULL),
+
GATT_HANDLER(0x2b93, mp_name_read, NULL, mp_name_notify),
GATT_HANDLER(0x2b96, NULL, NULL, track_changed_notify),
GATT_HANDLER(0x2b97, track_title_read, NULL, track_title_notify),
--
2.25.1
This adds initial code for csip plugin which handles Coordinated
set identification Profile and Coordinated Set Identification
Service.
---
Makefile.plugins | 5 +
configure.ac | 4 +
profiles/audio/csip.c | 319 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 328 insertions(+)
create mode 100644 profiles/audio/csip.c
diff --git a/Makefile.plugins b/Makefile.plugins
index 20cac384ef44..0f119e8714b7 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -131,3 +131,8 @@ if VCP
builtin_modules += vcp
builtin_sources += profiles/audio/vcp.c
endif
+
+if CSIP
+builtin_modules += csip
+builtin_sources += profiles/audio/csip.c
+endif
diff --git a/configure.ac b/configure.ac
index f9f0faf573ca..17c5f904a5c2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -207,6 +207,10 @@ 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(csip, AS_HELP_STRING([--disable-csip],
+ [disable CSIP profile]), [enable_csip=${enableval}])
+AM_CONDITIONAL(CSIP, test "${enable_csip}" != "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/csip.c b/profiles/audio/csip.c
new file mode 100644
index 000000000000..7b50d5cec88e
--- /dev/null
+++ b/profiles/audio/csip.c
@@ -0,0 +1,319 @@
+// 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/csip.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 CSIS_UUID_STR "00001846-0000-1000-8000-00805f9b34fb"
+
+struct csip_data {
+ struct btd_device *device;
+ struct btd_service *service;
+ struct bt_csip *csip;
+};
+
+static struct queue *sessions;
+
+static void csip_debug(const char *str, void *user_data)
+{
+ DBG_IDX(0xffff, "%s", str);
+}
+
+static struct csip_data *csip_data_new(struct btd_device *device)
+{
+ struct csip_data *data;
+
+ data = new0(struct csip_data, 1);
+ data->device = device;
+
+ return data;
+}
+
+static void csip_data_add(struct csip_data *data)
+{
+ DBG("data %p", data);
+
+ if (queue_find(sessions, NULL, data)) {
+ error("data %p already added", data);
+ return;
+ }
+
+ bt_csip_set_debug(data->csip, csip_debug, NULL, NULL);
+
+ if (!sessions)
+ sessions = queue_new();
+
+ queue_push_tail(sessions, data);
+
+ if (data->service)
+ btd_service_set_user_data(data->service, data);
+}
+
+static int csip_disconnect(struct btd_service *service)
+{
+ DBG("");
+ return 0;
+}
+
+static bool match_data(const void *data, const void *match_data)
+{
+ const struct csip_data *vdata = data;
+ const struct bt_csip *csip = match_data;
+
+ return vdata->csip == csip;
+}
+
+static void csip_data_free(struct csip_data *data)
+{
+ if (data->service) {
+ btd_service_set_user_data(data->service, NULL);
+ bt_csip_set_user_data(data->csip, NULL);
+ }
+
+ bt_csip_unref(data->csip);
+ free(data);
+}
+
+
+static void csip_data_remove(struct csip_data *data)
+{
+ DBG("data %p", data);
+
+ if (!queue_remove(sessions, data))
+ return;
+
+ csip_data_free(data);
+
+ if (queue_isempty(sessions)) {
+ queue_destroy(sessions, NULL);
+ sessions = NULL;
+ }
+}
+
+static void csip_detached(struct bt_csip *csip, void *user_data)
+{
+ struct csip_data *data;
+
+ DBG("%p", csip);
+
+ data = queue_find(sessions, match_data, csip);
+ if (!data) {
+ error("Unable to find csip session");
+ return;
+ }
+
+ csip_data_remove(data);
+}
+
+static void csip_attached(struct bt_csip *csip, void *user_data)
+{
+ struct csip_data *data;
+ struct bt_att *att;
+ struct btd_device *device;
+
+ DBG("%p", csip);
+
+ data = queue_find(sessions, match_data, csip);
+ if (data)
+ return;
+
+ att = bt_csip_get_att(csip);
+ if (!att)
+ return;
+
+ device = btd_adapter_find_device_by_fd(bt_att_get_fd(att));
+ if (!device) {
+ error("Unable to find device");
+ return;
+ }
+
+ data = csip_data_new(device);
+ data->csip = csip;
+
+ csip_data_add(data);
+
+}
+
+static int csip_server_probe(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ struct btd_gatt_database *database = btd_adapter_get_database(adapter);
+
+ DBG("CSIP path %s", adapter_get_path(adapter));
+
+ bt_csip_add_db(btd_gatt_database_get_db(database));
+
+ return 0;
+}
+
+static void csip_server_remove(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ DBG("CSIP remove Adapter");
+}
+
+static int csip_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 csip_data *data = btd_service_get_user_data(service);
+ char addr[18];
+
+ ba2str(device_get_address(device), addr);
+ DBG("%s", addr);
+
+ if (!data) {
+ error("CSIP service not handled by profile");
+ return -EINVAL;
+ }
+
+ if (!bt_csip_attach(data->csip, client)) {
+ error("CSIP unable to attach");
+ return -EINVAL;
+ }
+
+ btd_service_connecting_complete(service, 0);
+
+ return 0;
+}
+
+static int csip_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 csip_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 = csip_data_new(device);
+ data->service = service;
+
+ data->csip = bt_csip_new(btd_gatt_database_get_db(database),
+ btd_device_get_gatt_db(device));
+ if (!data->csip) {
+ error("Unable to create CSIP instance");
+ free(data);
+ return -EINVAL;
+ }
+
+ csip_data_add(data);
+
+ bt_csip_set_user_data(data->csip, service);
+
+ return 0;
+}
+
+static void csip_remove(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct csip_data *data;
+ char addr[18];
+
+ ba2str(device_get_address(device), addr);
+ DBG("%s", addr);
+
+ data = btd_service_get_user_data(service);
+ if (!data) {
+ error("CSIP service not handled by profile");
+ return;
+ }
+
+ csip_data_remove(data);
+}
+
+static struct btd_profile csip_profile = {
+ .name = "csip",
+ .priority = BTD_PROFILE_PRIORITY_MEDIUM,
+ .remote_uuid = CSIS_UUID_STR,
+
+ .device_probe = csip_probe,
+ .device_remove = csip_remove,
+
+ .accept = csip_accept,
+ .disconnect = csip_disconnect,
+
+ .adapter_probe = csip_server_probe,
+ .adapter_remove = csip_server_remove,
+};
+
+static unsigned int csip_id = 0;
+
+static int csip_init(void)
+{
+ if (!(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL)) {
+ warn("D-Bus experimental not enabled");
+ return -ENOTSUP;
+ }
+
+ btd_profile_register(&csip_profile);
+ csip_id = bt_csip_register(csip_attached, csip_detached, NULL);
+
+ return 0;
+}
+
+static void csip_exit(void)
+{
+ if (g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL) {
+ btd_profile_unregister(&csip_profile);
+ bt_csip_unregister(csip_id);
+ }
+}
+
+BLUETOOTH_PLUGIN_DEFINE(csip, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ csip_init, csip_exit)
--
2.25.1
The patch helps to generate Resolvable set identifier adv data.
which can be used as ADV data during advertisement.
It will be used to identify the device as part of setmember for
Coordinated set identification profile.
Example:
$<path to advtest/>advtest -i "761FAE703ED681F0C50B34155B6434FB"
SIRK: 761FAE703ED681F0C50B34155B6434FB
RSI: 0x71 0xcb 0xbc 0x7e 0x01 0x84
Random: bccb71
Hash: 84017e
---
tools/advtest.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 78 insertions(+), 2 deletions(-)
diff --git a/tools/advtest.c b/tools/advtest.c
index de036e783325..1ac9dd9e1226 100644
--- a/tools/advtest.c
+++ b/tools/advtest.c
@@ -13,6 +13,13 @@
#include <config.h>
#endif
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
#include <getopt.h>
#include "lib/bluetooth.h"
@@ -32,6 +39,9 @@
"\xe1\x23\x99\xc1\xca\x9a\xc3\x31"
#define SCAN_IRK "\xfa\x73\x09\x11\x3f\x03\x37\x0f" \
"\xf4\xf9\x93\x1e\xf9\xa3\x63\xa6"
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
static struct mgmt *mgmt;
static uint16_t index1 = MGMT_INDEX_NONE;
@@ -43,13 +53,73 @@ static struct bt_hci *scan_dev;
static void print_rpa(const uint8_t addr[6])
{
- printf(" Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ printf(" RSI:\t0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
addr[5], addr[4], addr[3],
addr[2], addr[1], addr[0]);
printf(" Random: %02x%02x%02x\n", addr[3], addr[4], addr[5]);
printf(" Hash: %02x%02x%02x\n", addr[0], addr[1], addr[2]);
}
+static size_t hex2bin(const char *hexstr, uint8_t *buf, size_t buflen)
+{
+ size_t i, len;
+
+ len = MIN((strlen(hexstr) / 2), buflen);
+ memset(buf, 0, len);
+
+ for (i = 0; i < len; i++)
+ if (sscanf(hexstr + (i * 2), "%02hhX", &buf[i]) != 1)
+ continue;
+
+
+ return len;
+}
+
+static bool get_random_bytes(void *buf, size_t num_bytes)
+{
+ ssize_t len;
+ int fd;
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0)
+ return false;
+
+ len = read(fd, buf, num_bytes);
+
+ close(fd);
+
+ if (len < 0)
+ return false;
+
+ return true;
+}
+
+static void generate_rsi(char *val)
+{
+ uint8_t sirk[16], hash[3];
+ uint8_t rsi[6];
+
+ hex2bin(val, sirk, sizeof(sirk));
+
+ get_random_bytes(&rsi[3], 3);
+
+ rsi[5] &= 0x3f; /* Clear 2 msb */
+ rsi[5] |= 0x40; /* Set 2nd msb */
+
+ crypto = bt_crypto_new();
+ if (!crypto) {
+ fprintf(stderr, "Failed to open crypto interface\n");
+ mainloop_exit_failure();
+ return;
+ }
+
+ bt_crypto_ah(crypto, sirk, rsi + 3, hash);
+ memcpy(rsi, hash, 3);
+
+ print_rpa(rsi);
+}
+
+
static void scan_le_adv_report(const void *data, uint8_t size,
void *user_data)
{
@@ -351,9 +421,11 @@ static void usage(void)
printf("\tadvtest [options]\n");
printf("options:\n"
"\t-h, --help Show help options\n");
+ printf(" \t-i <128bit SIRK>, Generate RSI ADV Data\n");
}
static const struct option main_options[] = {
+ { "hash", no_argument, NULL, 'i' },
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ }
@@ -366,11 +438,15 @@ int main(int argc ,char *argv[])
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "vh", main_options, NULL);
+ opt = getopt_long(argc, argv, "i:vh", main_options, NULL);
if (opt < 0)
break;
switch (opt) {
+ case 'i':
+ printf("SIRK: %s\n", optarg);
+ generate_rsi(optarg);
+ return EXIT_SUCCESS;
case 'v':
printf("%s\n", VERSION);
return EXIT_SUCCESS;
--
2.25.1
Hello:
This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <[email protected]>:
On Tue, 22 Nov 2022 05:16:18 +0530 you wrote:
> The following patches are for CSIP (Co-ordinated set identification
> profile) Client role. The patches also includes using SIRK and adding it
> to ADV data. Configuring CSIP Server values.
>
> Sathish Narasimman (6):
> lib/uuid: Add CSIS UUIDs
> main.conf: Add CSIP profile configurable options
> shared/csip: Add initial code for handling CSIP
> profiles: Add initial code for csip plugin
> monitor/att: Add decoding support for CSIP
> tools: Add support to generate RSI using SIRK
>
> [...]
Here is the summary with links:
- [BlueZ,1/6] lib/uuid: Add CSIS UUIDs
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=b709058c6008
- [BlueZ,2/6] main.conf: Add CSIP profile configurable options
(no matching commit)
- [BlueZ,3/6] shared/csip: Add initial code for handling CSIP
(no matching commit)
- [BlueZ,4/6] profiles: Add initial code for csip plugin
(no matching commit)
- [BlueZ,5/6] monitor/att: Add decoding support for CSIP
(no matching commit)
- [BlueZ,6/6] tools: Add support to generate RSI using SIRK
(no matching commit)
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html