Bluetooth: TBS: Allow multiple callbacks for client

The TBS client callbacks are just informative so we
provide the information to multiple listeners.

To support this for the long strings, the function
handle_string_long_read was refactored.

This also allows for multiple users of the TBS client.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
Emil Gydesen 2024-08-23 17:55:15 +02:00 committed by Fabio Baltieri
commit 57b35e1047
8 changed files with 420 additions and 249 deletions

View file

@ -78,6 +78,8 @@ Bluetooth
* Audio
* :c:func:`bt_tbs_client_register_cb` now supports multiple listeners and may now return an error.
* Host
* Added API :c:func:`bt_gatt_get_uatt_mtu` to get current Unenhanced ATT MTU of a given

View file

@ -30,6 +30,7 @@
#include <stdbool.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/sys/slist.h>
#include <zephyr/sys/util_macro.h>
#ifdef __cplusplus
@ -680,6 +681,9 @@ struct bt_tbs_client_cb {
/** Bearer friendly name has been read */
bt_tbs_client_read_string_cb friendly_name;
#endif /* defined(CONFIG_BT_TBS_CLIENT_CALL_FRIENDLY_NAME) */
/** @internal Internally used field for list handling */
sys_snode_t _node;
};
/**
@ -998,8 +1002,12 @@ int bt_tbs_client_read_optional_opcodes(struct bt_conn *conn,
* @brief Register the callbacks for CCP.
*
* @param cbs Pointer to the callback structure.
*
* @retval 0 Success
* @retval -EINVAL @p cbs is NULL
* @retval -EEXIST @p cbs is already registered
*/
void bt_tbs_client_register_cb(const struct bt_tbs_client_cb *cbs);
int bt_tbs_client_register_cb(struct bt_tbs_client_cb *cbs);
/**
* @brief Look up Telephone Bearer Service instance by CCID

View file

@ -155,7 +155,5 @@ struct bt_tbs_client_cb tbs_client_cb = {
int ccp_call_ctrl_init(void)
{
bt_tbs_client_register_cb(&tbs_client_cb);
return 0;
return bt_tbs_client_register_cb(&tbs_client_cb);
}

View file

@ -124,7 +124,11 @@ int ccp_call_ctrl_init(struct bt_conn *conn)
int err;
default_conn = bt_conn_ref(conn);
bt_tbs_client_register_cb(&tbs_client_cb);
err = bt_tbs_client_register_cb(&tbs_client_cb);
if (err != 0) {
return err;
}
err = bt_tbs_client_discover(conn);
if (err != 0) {
return err;

View file

@ -1,7 +1,7 @@
/* Bluetooth TBS - Telephone Bearer Service - Client
*
* Copyright (c) 2020 Bose Corporation
* Copyright (c) 2021 Nordic Semiconductor ASA
* Copyright (c) 2021-2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -26,6 +26,7 @@
#include <zephyr/net/buf.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/slist.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/util_macro.h>
#include <zephyr/toolchain.h>
@ -68,7 +69,7 @@ struct bt_tbs_server_inst {
struct bt_tbs_instance *current_inst;
};
static const struct bt_tbs_client_cb *tbs_client_cbs;
static sys_slist_t tbs_client_cbs = SYS_SLIST_STATIC_INIT(&tbs_client_cbs);
static struct bt_tbs_server_inst srv_insts[CONFIG_BT_MAX_CONN];
@ -245,6 +246,18 @@ static uint8_t net_buf_pull_call(struct net_buf_simple *buf,
return 0;
}
static void current_calls_changed(struct bt_conn *conn, int err, uint8_t inst_index,
uint8_t call_count, const struct bt_tbs_client_call *calls)
{
struct bt_tbs_client_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
if (listener->current_calls != NULL) {
listener->current_calls(conn, err, inst_index, call_count, calls);
}
}
}
static void bearer_list_current_calls(struct bt_conn *conn, const struct bt_tbs_instance *inst,
struct net_buf_simple *buf)
{
@ -276,9 +289,7 @@ static void bearer_list_current_calls(struct bt_conn *conn, const struct bt_tbs_
cnt++;
}
if (tbs_client_cbs != NULL && tbs_client_cbs->current_calls != NULL) {
tbs_client_cbs->current_calls(conn, 0, tbs_index(conn, inst), cnt, calls);
}
current_calls_changed(conn, 0, tbs_index(conn, inst), cnt, calls);
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_LIST_CURRENT_CALLS) */
@ -287,52 +298,58 @@ static void call_cp_callback_handler(struct bt_conn *conn, int err,
uint8_t index, uint8_t opcode,
uint8_t call_index)
{
bt_tbs_client_cp_cb cp_cb = NULL;
struct bt_tbs_client_cb *listener, *next;
LOG_DBG("Status: %s for the %s opcode for call 0x%02x", bt_tbs_status_str(err),
bt_tbs_opcode_str(opcode), call_index);
if (tbs_client_cbs == NULL) {
return;
}
switch (opcode) {
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
switch (opcode) {
#if defined(CONFIG_BT_TBS_CLIENT_ACCEPT_CALL)
case BT_TBS_CALL_OPCODE_ACCEPT:
cp_cb = tbs_client_cbs->accept_call;
break;
case BT_TBS_CALL_OPCODE_ACCEPT:
if (listener->accept_call != NULL) {
listener->accept_call(conn, err, index, call_index);
}
break;
#endif /* defined(CONFIG_BT_TBS_CLIENT_ACCEPT_CALL) */
#if defined(CONFIG_BT_TBS_CLIENT_TERMINATE_CALL)
case BT_TBS_CALL_OPCODE_TERMINATE:
cp_cb = tbs_client_cbs->terminate_call;
break;
case BT_TBS_CALL_OPCODE_TERMINATE:
if (listener->terminate_call != NULL) {
listener->terminate_call(conn, err, index, call_index);
}
break;
#endif /* defined(CONFIG_BT_TBS_CLIENT_TERMINATE_CALL) */
#if defined(CONFIG_BT_TBS_CLIENT_HOLD_CALL)
case BT_TBS_CALL_OPCODE_HOLD:
cp_cb = tbs_client_cbs->hold_call;
break;
case BT_TBS_CALL_OPCODE_HOLD:
if (listener->hold_call != NULL) {
listener->hold_call(conn, err, index, call_index);
}
break;
#endif /* defined(CONFIG_BT_TBS_CLIENT_HOLD_CALL) */
#if defined(CONFIG_BT_TBS_CLIENT_RETRIEVE_CALL)
case BT_TBS_CALL_OPCODE_RETRIEVE:
cp_cb = tbs_client_cbs->retrieve_call;
break;
case BT_TBS_CALL_OPCODE_RETRIEVE:
if (listener->retrieve_call != NULL) {
listener->retrieve_call(conn, err, index, call_index);
}
break;
#endif /* defined(CONFIG_BT_TBS_CLIENT_RETRIEVE_CALL) */
#if defined(CONFIG_BT_TBS_CLIENT_ORIGINATE_CALL)
case BT_TBS_CALL_OPCODE_ORIGINATE:
cp_cb = tbs_client_cbs->originate_call;
break;
case BT_TBS_CALL_OPCODE_ORIGINATE:
if (listener->originate_call != NULL) {
listener->originate_call(conn, err, index, call_index);
}
break;
#endif /* defined(CONFIG_BT_TBS_CLIENT_ORIGINATE_CALL) */
#if defined(CONFIG_BT_TBS_CLIENT_JOIN_CALLS)
case BT_TBS_CALL_OPCODE_JOIN:
cp_cb = tbs_client_cbs->join_calls;
break;
case BT_TBS_CALL_OPCODE_JOIN:
if (listener->join_calls != NULL) {
listener->join_calls(conn, err, index, call_index);
}
break;
#endif /* defined(CONFIG_BT_TBS_CLIENT_JOIN_CALLS) */
default:
break;
}
if (cp_cb != 0) {
cp_cb(conn, err, index, call_index);
default:
break;
}
}
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_OPTIONAL_OPCODES) */
@ -353,6 +370,18 @@ const char *parse_string_value(const void *data, uint16_t length,
}
#if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)
static void provider_name_changed(struct bt_conn *conn, int err, uint8_t inst_index,
const char *name)
{
struct bt_tbs_client_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
if (listener->bearer_provider_name != NULL) {
listener->bearer_provider_name(conn, err, inst_index, name);
}
}
}
static void provider_name_notify_handler(struct bt_conn *conn,
const struct bt_tbs_instance *tbs_inst,
const void *data, uint16_t length)
@ -362,13 +391,23 @@ static void provider_name_notify_handler(struct bt_conn *conn,
LOG_DBG("%s", name);
if (tbs_client_cbs != NULL && tbs_client_cbs->bearer_provider_name != NULL) {
tbs_client_cbs->bearer_provider_name(conn, 0, tbs_index(conn, tbs_inst), name);
}
provider_name_changed(conn, 0, tbs_index(conn, tbs_inst), name);
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME) */
#if defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY)
static void technology_changed(struct bt_conn *conn, int err, uint8_t inst_index,
uint8_t technology)
{
struct bt_tbs_client_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
if (listener->technology != NULL) {
listener->technology(conn, err, inst_index, technology);
}
}
}
static void technology_notify_handler(struct bt_conn *conn,
const struct bt_tbs_instance *tbs_inst,
const void *data, uint16_t length)
@ -381,14 +420,24 @@ static void technology_notify_handler(struct bt_conn *conn,
(void)memcpy(&technology, data, length);
LOG_DBG("%s (0x%02x)", bt_tbs_technology_str(technology), technology);
if (tbs_client_cbs != NULL && tbs_client_cbs->technology != NULL) {
tbs_client_cbs->technology(conn, 0, tbs_index(conn, tbs_inst), technology);
}
technology_changed(conn, 0, tbs_index(conn, tbs_inst), technology);
}
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY) */
#if defined(CONFIG_BT_TBS_CLIENT_BEARER_SIGNAL_STRENGTH)
static void signal_strength_changed(struct bt_conn *conn, int err, uint8_t inst_index,
uint8_t signal_strength)
{
struct bt_tbs_client_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
if (listener->signal_strength != NULL) {
listener->signal_strength(conn, err, inst_index, signal_strength);
}
}
}
static void signal_strength_notify_handler(struct bt_conn *conn,
const struct bt_tbs_instance *tbs_inst,
const void *data, uint16_t length)
@ -401,10 +450,7 @@ static void signal_strength_notify_handler(struct bt_conn *conn,
(void)memcpy(&signal_strength, data, length);
LOG_DBG("0x%02x", signal_strength);
if (tbs_client_cbs != NULL && tbs_client_cbs->signal_strength != NULL) {
tbs_client_cbs->signal_strength(conn, 0, tbs_index(conn, tbs_inst),
signal_strength);
}
signal_strength_changed(conn, 0, tbs_index(conn, tbs_inst), signal_strength);
}
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_SIGNAL_STRENGTH) */
@ -427,6 +473,18 @@ static void current_calls_notify_handler(struct bt_conn *conn,
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_LIST_CURRENT_CALLS) */
#if defined(CONFIG_BT_TBS_CLIENT_STATUS_FLAGS)
static void status_flags_changed(struct bt_conn *conn, int err, uint8_t inst_index,
uint16_t status_flags)
{
struct bt_tbs_client_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
if (listener->status_flags != NULL) {
listener->status_flags(conn, err, inst_index, status_flags);
}
}
}
static void status_flags_notify_handler(struct bt_conn *conn,
const struct bt_tbs_instance *tbs_inst,
const void *data, uint16_t length)
@ -438,15 +496,25 @@ static void status_flags_notify_handler(struct bt_conn *conn,
if (length == sizeof(status_flags)) {
(void)memcpy(&status_flags, data, length);
LOG_DBG("0x%04x", status_flags);
if (tbs_client_cbs != NULL && tbs_client_cbs->status_flags != NULL) {
tbs_client_cbs->status_flags(conn, 0, tbs_index(conn, tbs_inst),
status_flags);
}
status_flags_changed(conn, 0, tbs_index(conn, tbs_inst), status_flags);
}
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_STATUS_FLAGS) */
#if defined(CONFIG_BT_TBS_CLIENT_INCOMING_URI)
static void call_uri_changed(struct bt_conn *conn, int err, uint8_t inst_index,
const char *call_uri)
{
struct bt_tbs_client_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
if (listener->call_uri != NULL) {
listener->call_uri(conn, err, inst_index, call_uri);
}
}
}
static void incoming_uri_notify_handler(struct bt_conn *conn,
const struct bt_tbs_instance *tbs_inst,
const void *data, uint16_t length)
@ -456,12 +524,23 @@ static void incoming_uri_notify_handler(struct bt_conn *conn,
LOG_DBG("%s", uri);
if (tbs_client_cbs != NULL && tbs_client_cbs->call_uri != NULL) {
tbs_client_cbs->call_uri(conn, 0, tbs_index(conn, tbs_inst), uri);
}
call_uri_changed(conn, 0, tbs_index(conn, tbs_inst), uri);
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_INCOMING_URI) */
static void call_state_changed(struct bt_conn *conn, int err, uint8_t inst_index,
uint8_t call_count,
const struct bt_tbs_client_call_state *call_states)
{
struct bt_tbs_client_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
if (listener->call_state != NULL) {
listener->call_state(conn, err, inst_index, call_count, call_states);
}
}
}
static void call_state_notify_handler(struct bt_conn *conn,
const struct bt_tbs_instance *tbs_inst,
const void *data, uint16_t length)
@ -496,9 +575,7 @@ static void call_state_notify_handler(struct bt_conn *conn,
cnt++;
}
if (tbs_client_cbs != NULL && tbs_client_cbs->call_state != NULL) {
tbs_client_cbs->call_state(conn, 0, tbs_index(conn, tbs_inst), cnt, call_states);
}
call_state_changed(conn, 0, tbs_index(conn, tbs_inst), cnt, call_states);
}
#if defined(CONFIG_BT_TBS_CLIENT_CP_PROCEDURES)
@ -522,6 +599,19 @@ static void call_cp_notify_handler(struct bt_conn *conn,
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_CP_PROCEDURES) */
static void terminate_reason_changed(struct bt_conn *conn, int err, uint8_t inst_index,
struct bt_tbs_terminate_reason reason)
{
struct bt_tbs_client_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
if (listener->termination_reason != NULL) {
listener->termination_reason(conn, err, inst_index, reason.call_index,
reason.reason);
}
}
}
static void termination_reason_notify_handler(struct bt_conn *conn,
const struct bt_tbs_instance *tbs_inst,
const void *data, uint16_t length)
@ -535,14 +625,23 @@ static void termination_reason_notify_handler(struct bt_conn *conn,
LOG_DBG("ID 0x%02X, reason %s", reason.call_index,
bt_tbs_term_reason_str(reason.reason));
if (tbs_client_cbs != NULL && tbs_client_cbs->termination_reason != NULL) {
tbs_client_cbs->termination_reason(conn, 0, tbs_index(conn, tbs_inst),
reason.call_index, reason.reason);
}
terminate_reason_changed(conn, 0, tbs_index(conn, tbs_inst), reason);
}
}
#if defined(CONFIG_BT_TBS_CLIENT_INCOMING_CALL)
static void remote_uri_changed(struct bt_conn *conn, int err, uint8_t inst_index,
const char *remote_uri)
{
struct bt_tbs_client_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
if (listener->remote_uri != NULL) {
listener->remote_uri(conn, err, inst_index, remote_uri);
}
}
}
static void in_call_notify_handler(struct bt_conn *conn,
const struct bt_tbs_instance *tbs_inst,
const void *data, uint16_t length)
@ -552,13 +651,23 @@ static void in_call_notify_handler(struct bt_conn *conn,
LOG_DBG("%s", uri);
if (tbs_client_cbs != NULL && tbs_client_cbs->remote_uri != NULL) {
tbs_client_cbs->remote_uri(conn, 0, tbs_index(conn, tbs_inst), uri);
}
remote_uri_changed(conn, 0, tbs_index(conn, tbs_inst), uri);
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_INCOMING_CALL) */
#if defined(CONFIG_BT_TBS_CLIENT_CALL_FRIENDLY_NAME)
static void friendly_name_changed(struct bt_conn *conn, int err, uint8_t inst_index,
const char *friendly_name)
{
struct bt_tbs_client_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
if (listener->friendly_name != NULL) {
listener->friendly_name(conn, err, inst_index, friendly_name);
}
}
}
static void friendly_name_notify_handler(struct bt_conn *conn,
const struct bt_tbs_instance *tbs_inst,
const void *data, uint16_t length)
@ -568,9 +677,7 @@ static void friendly_name_notify_handler(struct bt_conn *conn,
LOG_DBG("%s", name);
if (tbs_client_cbs != NULL && tbs_client_cbs->friendly_name != NULL) {
tbs_client_cbs->friendly_name(conn, 0, tbs_index(conn, tbs_inst), name);
}
friendly_name_changed(conn, 0, tbs_index(conn, tbs_inst), name);
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_CALL_FRIENDLY_NAME) */
@ -713,14 +820,17 @@ static uint8_t inst_cnt(struct bt_tbs_server_inst *srv_inst)
static void tbs_client_discover_complete(struct bt_conn *conn, int err)
{
struct bt_tbs_server_inst *srv_inst = &srv_insts[bt_conn_index(conn)];
struct bt_tbs_client_cb *listener, *next;
LOG_DBG("conn %p err %d", (void *)conn, err);
/* Clear the current instance in discovery */
srv_inst->current_inst = NULL;
if (tbs_client_cbs != NULL && tbs_client_cbs->discover != NULL) {
tbs_client_cbs->discover(conn, err, inst_cnt(srv_inst), gtbs_found(srv_inst));
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
if (listener->discover != NULL) {
listener->discover(conn, err, inst_cnt(srv_inst), gtbs_found(srv_inst));
}
}
}
@ -730,105 +840,48 @@ static void tbs_client_discover_complete(struct bt_conn *conn, int err)
defined(CONFIG_BT_TBS_CLIENT_INCOMING_URI) || \
defined(CONFIG_BT_TBS_CLIENT_INCOMING_CALL) || \
defined(CONFIG_BT_TBS_CLIENT_CALL_FRIENDLY_NAME)
/* Common function to read tbs_client strings which may require long reads */
static uint8_t handle_string_long_read(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data,
uint16_t length,
bt_tbs_client_read_string_cb cb,
bool truncatable)
static bool can_add_string_to_net_buf(const struct net_buf_simple *buf, size_t len)
{
struct bt_tbs_instance *inst = CONTAINER_OF(params,
struct bt_tbs_instance,
read_params);
uint16_t offset = params->single.offset;
uint8_t inst_index = tbs_index(conn, inst);
const char *received_string;
int tbs_err = err;
return buf->len + len + sizeof('\0') <= buf->size;
}
if ((tbs_err == 0) && (data != NULL) &&
(net_buf_simple_tailroom(&inst->net_buf) < length)) {
LOG_DBG("Read length %u: String buffer full", length);
if (truncatable) {
/* Use the remaining buffer and continue reading */
LOG_DBG("Truncating string");
length = net_buf_simple_tailroom(&inst->net_buf);
} else {
tbs_err = BT_ATT_ERR_INSUFFICIENT_RESOURCES;
}
}
/* Common function to read tbs_client strings which may require long reads */
static uint8_t handle_string_long_read(struct bt_tbs_instance *inst, uint8_t err, const void *data,
uint16_t offset, uint16_t length, bool truncatable)
{
if (err != 0) {
LOG_DBG("err: %u", err);
if (tbs_err != 0) {
LOG_DBG("err: %d", tbs_err);
tbs_client_gatt_read_complete(inst);
if (cb != NULL) {
cb(conn, tbs_err, inst_index, NULL);
}
return BT_GATT_ITER_STOP;
return BT_GATT_ERR(err);
}
if (data != NULL) {
/* Get data and try to read more using read long procedure */
LOG_DBG("Read (offset %u): %s", offset, bt_hex(data, length));
net_buf_simple_add_mem(&inst->net_buf, data, length);
return BT_GATT_ITER_CONTINUE;
}
if (inst->net_buf.len == 0) {
received_string = NULL;
} else {
uint16_t str_length = inst->net_buf.len;
/* Ensure there is space for string termination */
if (net_buf_simple_tailroom(&inst->net_buf) < 1) {
LOG_DBG("Truncating string");
if (!can_add_string_to_net_buf(&inst->net_buf, length)) {
LOG_DBG("Read length %u: String buffer full", length);
if (truncatable) {
/* Truncate */
str_length--;
} else {
tbs_err = BT_ATT_ERR_INSUFFICIENT_RESOURCES;
}
}
if (tbs_err == 0) {
char *str_data;
/* Get a reference to the string buffer */
str_data = net_buf_simple_pull_mem(&inst->net_buf,
inst->net_buf.len);
/* All strings are UTF-8, truncate properly if needed */
str_data[str_length] = '\0';
received_string = utf8_trunc(str_data);
/* The string might have been truncated */
if (strlen(received_string) < str_length) {
/* Use the remaining buffer and stop reading and leave room for NULL
* terminator
*/
LOG_DBG("Truncating string");
if (!truncatable) {
tbs_err =
BT_ATT_ERR_INSUFFICIENT_RESOURCES;
}
length = net_buf_simple_tailroom(&inst->net_buf) - sizeof('\0');
net_buf_simple_add_mem(&inst->net_buf, data, length);
/* Ensure that the data is correctly truncated */
utf8_trunc(inst->net_buf.data);
} else {
return BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES);
}
} else {
net_buf_simple_add_mem(&inst->net_buf, data, length);
LOG_DBG("%s", received_string);
return BT_GATT_ITER_CONTINUE;
}
}
if (tbs_err) {
received_string = NULL;
}
tbs_client_gatt_read_complete(inst);
if (cb != NULL) {
cb(conn, tbs_err, inst_index, received_string);
}
return BT_GATT_ITER_STOP;
}
#endif /* CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME || \
@ -871,38 +924,67 @@ static uint8_t read_bearer_provider_name_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
bt_tbs_client_read_string_cb cb = NULL;
struct bt_tbs_instance *inst = CONTAINER_OF(params, struct bt_tbs_instance, read_params);
const uint8_t inst_index = tbs_index(conn, inst);
int ret;
LOG_DBG("Read bearer provider name");
LOG_DBG("");
if (tbs_client_cbs != NULL &&
tbs_client_cbs->bearer_provider_name != NULL) {
cb = tbs_client_cbs->bearer_provider_name;
ret = handle_string_long_read(inst, err, data, params->single.offset, length, true);
if (ret != BT_GATT_ITER_CONTINUE) {
if (ret == BT_GATT_ITER_STOP) {
/* At this point the inst->net_buf.data contains a NULL terminator string */
provider_name_changed(conn, 0, inst_index, (char *)inst->net_buf.data);
} else {
provider_name_changed(conn, ret, inst_index, NULL);
}
tbs_client_gatt_read_complete(inst);
return BT_GATT_ITER_STOP;
}
return handle_string_long_read(conn, err, params, data,
length, cb, true);
return BT_GATT_ITER_CONTINUE;
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME) */
#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI)
static void bearer_uci_changed(struct bt_conn *conn, int err, uint8_t inst_index, const char *uci)
{
struct bt_tbs_client_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
if (listener->bearer_uci != NULL) {
listener->bearer_uci(conn, err, inst_index, uci);
}
}
}
static uint8_t read_bearer_uci_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
bt_tbs_client_read_string_cb cb = NULL;
struct bt_tbs_instance *inst = CONTAINER_OF(params, struct bt_tbs_instance, read_params);
const uint8_t inst_index = tbs_index(conn, inst);
int ret;
LOG_DBG("Read bearer UCI");
LOG_DBG("");
if (tbs_client_cbs != NULL && tbs_client_cbs->bearer_uci != NULL) {
cb = tbs_client_cbs->bearer_uci;
ret = handle_string_long_read(inst, err, data, params->single.offset, length, true);
if (ret != BT_GATT_ITER_CONTINUE) {
if (ret == BT_GATT_ITER_STOP) {
/* At this point the inst->net_buf.data contains a NULL terminator string */
bearer_uci_changed(conn, 0, inst_index, (char *)inst->net_buf.data);
} else {
bearer_uci_changed(conn, ret, inst_index, NULL);
}
tbs_client_gatt_read_complete(inst);
return BT_GATT_ITER_STOP;
}
/* The specification does not indicate truncation as an option, so
* fail if insufficient buffer space.
*/
return handle_string_long_read(conn, err, params, data,
length, cb, false);
return BT_GATT_ITER_CONTINUE;
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) */
@ -935,29 +1017,50 @@ static uint8_t read_technology_cb(struct bt_conn *conn, uint8_t err,
tbs_client_gatt_read_complete(inst);
if (tbs_client_cbs != NULL && tbs_client_cbs->technology != NULL) {
tbs_client_cbs->technology(conn, cb_err, inst_index, technology);
}
technology_changed(conn, cb_err, inst_index, technology);
return BT_GATT_ITER_STOP;
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY) */
#if defined(CONFIG_BT_TBS_CLIENT_BEARER_URI_SCHEMES_SUPPORTED_LIST)
static void uri_list_changed(struct bt_conn *conn, int err, uint8_t inst_index,
const char *uri_list)
{
struct bt_tbs_client_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
if (listener->uri_list != NULL) {
listener->uri_list(conn, err, inst_index, uri_list);
}
}
}
static uint8_t read_uri_list_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
bt_tbs_client_read_string_cb cb = NULL;
struct bt_tbs_instance *inst = CONTAINER_OF(params, struct bt_tbs_instance, read_params);
const uint8_t inst_index = tbs_index(conn, inst);
int ret;
LOG_DBG("Read bearer UCI");
LOG_DBG("");
if (tbs_client_cbs != NULL && tbs_client_cbs->uri_list != NULL) {
cb = tbs_client_cbs->uri_list;
ret = handle_string_long_read(inst, err, data, params->single.offset, length, true);
if (ret != BT_GATT_ITER_CONTINUE) {
if (ret == BT_GATT_ITER_STOP) {
/* At this point the inst->net_buf.data contains a NULL terminator string */
uri_list_changed(conn, 0, inst_index, (char *)inst->net_buf.data);
} else {
uri_list_changed(conn, ret, inst_index, NULL);
}
tbs_client_gatt_read_complete(inst);
return BT_GATT_ITER_STOP;
}
return handle_string_long_read(conn, err, params, data,
length, cb, false);
return BT_GATT_ITER_CONTINUE;
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_URI_SCHEMES_SUPPORTED_LIST) */
@ -990,16 +1093,25 @@ static uint8_t read_signal_strength_cb(struct bt_conn *conn, uint8_t err,
tbs_client_gatt_read_complete(inst);
if (tbs_client_cbs != NULL && tbs_client_cbs->signal_strength != NULL) {
tbs_client_cbs->signal_strength(conn, cb_err, inst_index,
signal_strength);
}
signal_strength_changed(conn, cb_err, inst_index, signal_strength);
return BT_GATT_ITER_STOP;
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_SIGNAL_STRENGTH) */
#if defined(CONFIG_BT_TBS_CLIENT_READ_BEARER_SIGNAL_INTERVAL)
static void signal_interval_changed(struct bt_conn *conn, int err, uint8_t inst_index,
uint8_t signal_interval)
{
struct bt_tbs_client_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
if (listener->signal_interval != NULL) {
listener->signal_interval(conn, err, inst_index, signal_interval);
}
}
}
static uint8_t read_signal_interval_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
@ -1028,10 +1140,7 @@ static uint8_t read_signal_interval_cb(struct bt_conn *conn, uint8_t err,
tbs_client_gatt_read_complete(inst);
if (tbs_client_cbs && tbs_client_cbs->signal_interval) {
tbs_client_cbs->signal_interval(conn, cb_err, inst_index,
signal_interval);
}
signal_interval_changed(conn, cb_err, inst_index, signal_interval);
return BT_GATT_ITER_STOP;
}
@ -1060,11 +1169,7 @@ static uint8_t read_current_calls_cb(struct bt_conn *conn, uint8_t err,
tbs_client_gatt_read_complete(inst);
if (tbs_client_cbs != NULL &&
tbs_client_cbs->current_calls != NULL) {
tbs_client_cbs->current_calls(conn, tbs_err,
inst_index, 0, NULL);
}
current_calls_changed(conn, tbs_err, inst_index, 0, NULL);
return BT_GATT_ITER_STOP;
}
@ -1085,11 +1190,7 @@ static uint8_t read_current_calls_cb(struct bt_conn *conn, uint8_t err,
tbs_client_gatt_read_complete(inst);
if (inst->net_buf.len == 0) {
if (tbs_client_cbs != NULL &&
tbs_client_cbs->current_calls != NULL) {
tbs_client_cbs->current_calls(conn, 0,
inst_index, 0, NULL);
}
current_calls_changed(conn, 0, inst_index, 0, NULL);
return BT_GATT_ITER_STOP;
}
@ -1101,6 +1202,17 @@ static uint8_t read_current_calls_cb(struct bt_conn *conn, uint8_t err,
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_LIST_CURRENT_CALLS) */
#if defined(CONFIG_BT_TBS_CLIENT_CCID)
static void ccid_changed(struct bt_conn *conn, int err, uint8_t inst_index, uint8_t ccid)
{
struct bt_tbs_client_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
if (listener->ccid != NULL) {
listener->ccid(conn, err, inst_index, ccid);
}
}
}
static uint8_t read_ccid_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
@ -1129,9 +1241,7 @@ static uint8_t read_ccid_cb(struct bt_conn *conn, uint8_t err,
tbs_client_gatt_read_complete(inst);
if (tbs_client_cbs != NULL && tbs_client_cbs->ccid != NULL) {
tbs_client_cbs->ccid(conn, cb_err, inst_index, ccid);
}
ccid_changed(conn, cb_err, inst_index, ccid);
return BT_GATT_ITER_STOP;
}
@ -1166,11 +1276,7 @@ static uint8_t read_status_flags_cb(struct bt_conn *conn, uint8_t err,
tbs_client_gatt_read_complete(inst);
if (tbs_client_cbs != NULL &&
tbs_client_cbs->status_flags != NULL) {
tbs_client_cbs->status_flags(conn, cb_err, inst_index,
status_flags);
}
status_flags_changed(conn, cb_err, inst_index, status_flags);
return BT_GATT_ITER_STOP;
}
@ -1181,16 +1287,27 @@ static uint8_t read_call_uri_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
bt_tbs_client_read_string_cb cb = NULL;
struct bt_tbs_instance *inst = CONTAINER_OF(params, struct bt_tbs_instance, read_params);
const uint8_t inst_index = tbs_index(conn, inst);
int ret;
LOG_DBG("Read incoming call target bearer URI");
LOG_DBG("");
if (tbs_client_cbs != NULL && tbs_client_cbs->call_uri != NULL) {
cb = tbs_client_cbs->call_uri;
ret = handle_string_long_read(inst, err, data, params->single.offset, length, true);
if (ret != BT_GATT_ITER_CONTINUE) {
if (ret == BT_GATT_ITER_STOP) {
/* At this point the inst->net_buf.data contains a NULL terminator string */
call_uri_changed(conn, 0, inst_index, (char *)inst->net_buf.data);
} else {
call_uri_changed(conn, ret, inst_index, NULL);
}
tbs_client_gatt_read_complete(inst);
return BT_GATT_ITER_STOP;
}
return handle_string_long_read(conn, err, params, data,
length, cb, false);
return BT_GATT_ITER_CONTINUE;
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_INCOMING_URI) */
@ -1218,11 +1335,7 @@ static uint8_t read_call_state_cb(struct bt_conn *conn, uint8_t err,
tbs_client_gatt_read_complete(inst);
if (tbs_client_cbs != NULL &&
tbs_client_cbs->call_state != NULL) {
tbs_client_cbs->call_state(conn, tbs_err,
inst_index, 0, NULL);
}
call_state_changed(conn, tbs_err, inst_index, 0, NULL);
return BT_GATT_ITER_STOP;
}
@ -1240,10 +1353,7 @@ static uint8_t read_call_state_cb(struct bt_conn *conn, uint8_t err,
if (inst->net_buf.len == 0) {
tbs_client_gatt_read_complete(inst);
if (tbs_client_cbs != NULL &&
tbs_client_cbs->call_state != NULL) {
tbs_client_cbs->call_state(conn, 0, inst_index, 0, NULL);
}
call_state_changed(conn, 0, inst_index, 0, NULL);
return BT_GATT_ITER_STOP;
}
@ -1270,14 +1380,24 @@ static uint8_t read_call_state_cb(struct bt_conn *conn, uint8_t err,
tbs_client_gatt_read_complete(inst);
if (tbs_client_cbs != NULL && tbs_client_cbs->call_state != NULL) {
tbs_client_cbs->call_state(conn, tbs_err, inst_index, cnt, call_states);
}
call_state_changed(conn, tbs_err, inst_index, cnt, call_states);
return BT_GATT_ITER_STOP;
}
#if defined(CONFIG_BT_TBS_CLIENT_OPTIONAL_OPCODES)
static void optional_opcodes_changed(struct bt_conn *conn, int err, uint8_t inst_index,
uint16_t optional_opcodes)
{
struct bt_tbs_client_cb *listener, *next;
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&tbs_client_cbs, listener, next, _node) {
if (listener->optional_opcodes != NULL) {
listener->optional_opcodes(conn, err, inst_index, optional_opcodes);
}
}
}
static uint8_t read_optional_opcodes_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
@ -1304,10 +1424,7 @@ static uint8_t read_optional_opcodes_cb(struct bt_conn *conn, uint8_t err,
tbs_client_gatt_read_complete(inst);
if (tbs_client_cbs != NULL &&
tbs_client_cbs->optional_opcodes != NULL) {
tbs_client_cbs->optional_opcodes(conn, cb_err, inst_index, optional_opcodes);
}
optional_opcodes_changed(conn, cb_err, inst_index, optional_opcodes);
return BT_GATT_ITER_STOP;
}
@ -1318,17 +1435,27 @@ static uint8_t read_remote_uri_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
bt_tbs_client_read_string_cb cb = NULL;
struct bt_tbs_instance *inst = CONTAINER_OF(params, struct bt_tbs_instance, read_params);
const uint8_t inst_index = tbs_index(conn, inst);
int ret;
LOG_DBG("Read incoming call URI");
LOG_DBG("");
if (tbs_client_cbs != NULL &&
tbs_client_cbs->remote_uri != NULL) {
cb = tbs_client_cbs->remote_uri;
ret = handle_string_long_read(inst, err, data, params->single.offset, length, true);
if (ret != BT_GATT_ITER_CONTINUE) {
if (ret == BT_GATT_ITER_STOP) {
/* At this point the inst->net_buf.data contains a NULL terminator string */
remote_uri_changed(conn, 0, inst_index, (char *)inst->net_buf.data);
} else {
remote_uri_changed(conn, ret, inst_index, NULL);
}
tbs_client_gatt_read_complete(inst);
return BT_GATT_ITER_STOP;
}
return handle_string_long_read(conn, err, params, data,
length, cb, false);
return BT_GATT_ITER_CONTINUE;
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_INCOMING_CALL) */
@ -1337,17 +1464,27 @@ static uint8_t read_friendly_name_cb(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params,
const void *data, uint16_t length)
{
bt_tbs_client_read_string_cb cb = NULL;
struct bt_tbs_instance *inst = CONTAINER_OF(params, struct bt_tbs_instance, read_params);
const uint8_t inst_index = tbs_index(conn, inst);
int ret;
LOG_DBG("Read incoming call target bearer URI");
LOG_DBG("");
if (tbs_client_cbs != NULL &&
tbs_client_cbs->friendly_name != NULL) {
cb = tbs_client_cbs->friendly_name;
ret = handle_string_long_read(inst, err, data, params->single.offset, length, true);
if (ret != BT_GATT_ITER_CONTINUE) {
if (ret == BT_GATT_ITER_STOP) {
/* At this point the inst->net_buf.data contains a NULL terminator string */
friendly_name_changed(conn, 0, inst_index, (char *)inst->net_buf.data);
} else {
friendly_name_changed(conn, ret, inst_index, NULL);
}
tbs_client_gatt_read_complete(inst);
return BT_GATT_ITER_STOP;
}
return handle_string_long_read(conn, err, params, data,
length, cb, true);
return BT_GATT_ITER_CONTINUE;
}
#endif /* defined(CONFIG_BT_TBS_CLIENT_CALL_FRIENDLY_NAME) */
@ -2272,9 +2409,21 @@ int bt_tbs_client_discover(struct bt_conn *conn)
#endif /* CONFIG_BT_TBS_CLIENT_GTBS */
}
void bt_tbs_client_register_cb(const struct bt_tbs_client_cb *cbs)
int bt_tbs_client_register_cb(struct bt_tbs_client_cb *cb)
{
tbs_client_cbs = cbs;
CHECKIF(cb == NULL) {
LOG_DBG("cb is NULL");
return -EINVAL;
}
if (sys_slist_find(&tbs_client_cbs, &cb->_node, NULL)) {
return -EEXIST;
}
sys_slist_append(&tbs_client_cbs, &cb->_node);
return 0;
}
#if defined(CONFIG_BT_TBS_CLIENT_CCID)

View file

@ -301,7 +301,7 @@ struct bt_tbs_in_uri {
defined(CONFIG_BT_TBS_CLIENT_INCOMING_CALL) || \
defined(CONFIG_BT_TBS_CLIENT_CALL_FRIENDLY_NAME) || \
defined(CONFIG_BT_TBS_CLIENT_BEARER_LIST_CURRENT_CALLS)
#define BT_TBS_CLIENT_INST_READ_BUF_SIZE (BT_ATT_MAX_ATTRIBUTE_LEN)
#define BT_TBS_CLIENT_INST_READ_BUF_SIZE (BT_ATT_MAX_ATTRIBUTE_LEN + 1 /* NULL terminator*/)
#else
/* Need only be the size of call state reads which is the largest of the
* remaining characteristic values

View file

@ -277,7 +277,7 @@ static void tbs_client_cp_cb(struct bt_conn *conn, int err, uint8_t inst_index,
tbs_client_cp_ev(conn, err);
}
static const struct bt_tbs_client_cb tbs_client_callbacks = {
static struct bt_tbs_client_cb tbs_client_callbacks = {
.discover = tbs_client_discover_cb,
.originate_call = tbs_client_cp_cb,
.terminate_call = tbs_client_cp_cb,
@ -860,10 +860,15 @@ static const struct btp_handler ccp_handlers[] = {
uint8_t tester_init_ccp(void)
{
int err;
tester_register_command_handlers(BTP_SERVICE_ID_CCP, ccp_handlers,
ARRAY_SIZE(ccp_handlers));
bt_tbs_client_register_cb(&tbs_client_callbacks);
err = bt_tbs_client_register_cb(&tbs_client_callbacks);
if (err != 0) {
return BTP_STATUS_FAILED;
}
return BTP_STATUS_SUCCESS;
}

View file

@ -303,7 +303,7 @@ static void tbs_client_term_reason_cb(struct bt_conn *conn,
SET_FLAG(term_reason);
}
static const struct bt_tbs_client_cb tbs_client_cbs = {
static struct bt_tbs_client_cb tbs_client_cbs = {
.discover = tbs_client_discover_cb,
.originate_call = tbs_client_originate_call_cb,
.terminate_call = tbs_client_terminate_call_cb,
@ -492,7 +492,12 @@ static void test_main(void)
}
bt_conn_cb_register(&conn_callbacks);
bt_tbs_client_register_cb(&tbs_client_cbs);
err = bt_tbs_client_register_cb(&tbs_client_cbs);
if (err != 0) {
FAIL("Failed to register TBS client cbs (err %d)\n", err);
return;
}
WAIT_FOR_COND(bt_init);