zephyr/subsys/bluetooth/audio/tbs_internal.h
Emil Gydesen 42602e6465 Bluetooth: TBS: Replace busy bool with atomic
Replace the busy boolean flag with an atomic value.
This prevents any race conditions with the implementation.

The discovery procedure is also now properly guarded with it
so that in case that any procedure is currently in progress
for the specific connection, then a new discovery procedure
cannot happen until all other procedures are finished.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
2024-09-12 14:48:23 +02:00

397 lines
12 KiB
C

/** @file
* @brief Internal APIs for Bluetooth TBS.
*/
/*
* Copyright (c) 2019 Bose Corporation
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <zephyr/autoconf.h>
#include <zephyr/bluetooth/att.h>
#include <zephyr/bluetooth/audio/tbs.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/net_buf.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/types.h>
#define BT_TBS_MAX_UCI_SIZE 6
#define BT_TBS_MIN_URI_LEN 3 /* a:b */
#define BT_TBS_FREE_CALL_INDEX 0
/* Call Control Point Opcodes */
#define BT_TBS_CALL_OPCODE_ACCEPT 0x00
#define BT_TBS_CALL_OPCODE_TERMINATE 0x01
#define BT_TBS_CALL_OPCODE_HOLD 0x02
#define BT_TBS_CALL_OPCODE_RETRIEVE 0x03
#define BT_TBS_CALL_OPCODE_ORIGINATE 0x04
#define BT_TBS_CALL_OPCODE_JOIN 0x05
/* Local Control Points - Used to do local control operations but still being
* able to determine if it is a local or remote operation
*/
#define BT_TBS_LOCAL_OPCODE_ANSWER 0x80
#define BT_TBS_LOCAL_OPCODE_HOLD 0x81
#define BT_TBS_LOCAL_OPCODE_RETRIEVE 0x82
#define BT_TBS_LOCAL_OPCODE_TERMINATE 0x83
#define BT_TBS_LOCAL_OPCODE_INCOMING 0x84
#define BT_TBS_LOCAL_OPCODE_SERVER_TERMINATE 0x85
#define FIRST_PRINTABLE_ASCII_CHAR ' ' /* space */
#define BT_TBS_CALL_FLAG_SET_INCOMING(flag) (flag &= ~BT_TBS_CALL_FLAG_OUTGOING)
#define BT_TBS_CALL_FLAG_SET_OUTGOING(flag) (flag |= BT_TBS_CALL_FLAG_OUTGOING)
const char *parse_string_value(const void *data, uint16_t length,
uint16_t max_len);
static inline const char *bt_tbs_state_str(uint8_t state)
{
switch (state) {
case BT_TBS_CALL_STATE_INCOMING:
return "incoming";
case BT_TBS_CALL_STATE_DIALING:
return "dialing";
case BT_TBS_CALL_STATE_ALERTING:
return "alerting";
case BT_TBS_CALL_STATE_ACTIVE:
return "active";
case BT_TBS_CALL_STATE_LOCALLY_HELD:
return "locally held";
case BT_TBS_CALL_STATE_REMOTELY_HELD:
return "remote held";
case BT_TBS_CALL_STATE_LOCALLY_AND_REMOTELY_HELD:
return "locally and remotely held";
default:
return "unknown";
}
}
static inline const char *bt_tbs_opcode_str(uint8_t opcode)
{
switch (opcode) {
case BT_TBS_CALL_OPCODE_ACCEPT:
return "accept";
case BT_TBS_CALL_OPCODE_TERMINATE:
return "terminate";
case BT_TBS_CALL_OPCODE_HOLD:
return "hold";
case BT_TBS_CALL_OPCODE_RETRIEVE:
return "retrieve";
case BT_TBS_CALL_OPCODE_ORIGINATE:
return "originate";
case BT_TBS_CALL_OPCODE_JOIN:
return "join";
case BT_TBS_LOCAL_OPCODE_ANSWER:
return "remote answer";
case BT_TBS_LOCAL_OPCODE_HOLD:
return "remote hold";
case BT_TBS_LOCAL_OPCODE_RETRIEVE:
return "remote retrieve";
case BT_TBS_LOCAL_OPCODE_TERMINATE:
return "remote terminate";
case BT_TBS_LOCAL_OPCODE_INCOMING:
return "remote incoming";
case BT_TBS_LOCAL_OPCODE_SERVER_TERMINATE:
return "server terminate";
default:
return "unknown";
}
}
static inline const char *bt_tbs_status_str(uint8_t status)
{
switch (status) {
case BT_TBS_RESULT_CODE_SUCCESS:
return "success";
case BT_TBS_RESULT_CODE_OPCODE_NOT_SUPPORTED:
return "opcode not supported";
case BT_TBS_RESULT_CODE_OPERATION_NOT_POSSIBLE:
return "operation not possible";
case BT_TBS_RESULT_CODE_INVALID_CALL_INDEX:
return "invalid call index";
case BT_TBS_RESULT_CODE_STATE_MISMATCH:
return "state mismatch";
case BT_TBS_RESULT_CODE_OUT_OF_RESOURCES:
return "out of resources";
case BT_TBS_RESULT_CODE_INVALID_URI:
return "invalid URI";
default:
return "ATT err";
}
}
static inline const char *bt_tbs_technology_str(uint8_t status)
{
switch (status) {
case BT_TBS_TECHNOLOGY_3G:
return "3G";
case BT_TBS_TECHNOLOGY_4G:
return "4G";
case BT_TBS_TECHNOLOGY_LTE:
return "LTE";
case BT_TBS_TECHNOLOGY_WIFI:
return "WIFI";
case BT_TBS_TECHNOLOGY_5G:
return "5G";
case BT_TBS_TECHNOLOGY_GSM:
return "GSM";
case BT_TBS_TECHNOLOGY_CDMA:
return "CDMA";
case BT_TBS_TECHNOLOGY_2G:
return "2G";
case BT_TBS_TECHNOLOGY_WCDMA:
return "WCDMA";
default:
return "unknown technology";
}
}
static inline const char *bt_tbs_term_reason_str(uint8_t reason)
{
switch (reason) {
case BT_TBS_REASON_BAD_REMOTE_URI:
return "bad remote URI";
case BT_TBS_REASON_CALL_FAILED:
return "call failed";
case BT_TBS_REASON_REMOTE_ENDED_CALL:
return "remote ended call";
case BT_TBS_REASON_SERVER_ENDED_CALL:
return "server ended call";
case BT_TBS_REASON_LINE_BUSY:
return "line busy";
case BT_TBS_REASON_NETWORK_CONGESTED:
return "network congested";
case BT_TBS_REASON_CLIENT_TERMINATED:
return "client terminated";
case BT_TBS_REASON_UNSPECIFIED:
return "unspecified";
default:
return "unknown reason";
}
}
/**
* @brief Checks if @p uri contains a colon (':') followed by a printable
* character. Minimal uri is "a:b".
*
* @param uri The uri "scheme:id"
* @param len The length of uri
* @return true If the above is true
* @return false If the above is not true
*/
static inline bool bt_tbs_valid_uri(const uint8_t *uri, size_t uri_len)
{
if (!uri) {
return false;
}
if (uri_len > CONFIG_BT_TBS_MAX_URI_LENGTH || uri_len < BT_TBS_MIN_URI_LEN) {
return false;
} else if (uri[0] < FIRST_PRINTABLE_ASCII_CHAR) {
/* Invalid first char */
return false;
}
for (size_t i = 1; i < uri_len; i++) {
if (uri[i] == ':' && uri[i + 1] >= FIRST_PRINTABLE_ASCII_CHAR) {
return true;
}
}
return false;
}
/* TODO: The bt_tbs_call could use the bt_tbs_call_state struct for the first
* 3 fields
*/
struct bt_tbs_call {
uint8_t index;
uint8_t state;
uint8_t flags;
char remote_uri[CONFIG_BT_TBS_MAX_URI_LENGTH + 1];
} __packed;
struct bt_tbs_call_state {
uint8_t index;
uint8_t state;
uint8_t flags;
} __packed;
struct bt_tbs_call_cp_acc {
uint8_t opcode;
uint8_t call_index;
} __packed;
struct bt_tbs_call_cp_term {
uint8_t opcode;
uint8_t call_index;
} __packed;
struct bt_tbs_call_cp_hold {
uint8_t opcode;
uint8_t call_index;
} __packed;
struct bt_tbs_call_cp_retrieve {
uint8_t opcode;
uint8_t call_index;
} __packed;
struct bt_tbs_call_cp_originate {
uint8_t opcode;
uint8_t uri[0];
} __packed;
struct bt_tbs_call_cp_join {
uint8_t opcode;
uint8_t call_indexes[0];
} __packed;
union bt_tbs_call_cp_t {
uint8_t opcode;
struct bt_tbs_call_cp_acc accept;
struct bt_tbs_call_cp_term terminate;
struct bt_tbs_call_cp_hold hold;
struct bt_tbs_call_cp_retrieve retrieve;
struct bt_tbs_call_cp_originate originate;
struct bt_tbs_call_cp_join join;
} __packed;
struct bt_tbs_call_cp_notify {
uint8_t opcode;
uint8_t call_index;
uint8_t status;
} __packed;
struct bt_tbs_call_state_notify {
uint8_t call_index;
uint8_t state;
} __packed;
struct bt_tbs_terminate_reason {
uint8_t call_index;
uint8_t reason;
} __packed;
struct bt_tbs_current_call_item {
uint8_t length;
uint8_t call_index;
uint8_t call_state;
uint8_t uri[CONFIG_BT_TBS_MAX_URI_LENGTH];
} __packed;
struct bt_tbs_in_uri {
uint8_t call_index;
char uri[CONFIG_BT_TBS_MAX_URI_LENGTH + 1];
} __packed;
#if defined(CONFIG_BT_TBS_CLIENT)
/* Features which may require long string reads */
#if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME) || \
defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) || \
defined(CONFIG_BT_TBS_CLIENT_BEARER_URI_SCHEMES_SUPPORTED_LIST) || \
defined(CONFIG_BT_TBS_CLIENT_INCOMING_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 + 1 /* NULL terminator*/)
#else
/* Need only be the size of call state reads which is the largest of the
* remaining characteristic values
*/
#define BT_TBS_CLIENT_INST_READ_BUF_SIZE \
MIN(BT_ATT_MAX_ATTRIBUTE_LEN, \
(CONFIG_BT_TBS_CLIENT_MAX_CALLS \
* sizeof(struct bt_tbs_client_call_state)))
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_LIST_CURRENT_CALLS) */
enum bt_tbs_client_flag {
BT_TBS_CLIENT_FLAG_BUSY,
BT_TBS_CLIENT_FLAG_NUM_FLAGS, /* keep as last */
};
struct bt_tbs_instance {
struct bt_tbs_client_call_state calls[CONFIG_BT_TBS_CLIENT_MAX_CALLS];
uint16_t start_handle;
uint16_t end_handle;
#if defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI)
uint16_t bearer_uci_handle;
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_UCI) */
#if defined(CONFIG_BT_TBS_CLIENT_BEARER_URI_SCHEMES_SUPPORTED_LIST)
uint16_t uri_list_handle;
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_URI_SCHEMES_SUPPORTED_LIST) */
#if defined(CONFIG_BT_TBS_CLIENT_READ_BEARER_SIGNAL_INTERVAL) \
|| defined(CONFIG_BT_TBS_CLIENT_SET_BEARER_SIGNAL_INTERVAL)
uint16_t signal_interval_handle;
#endif /* defined(CONFIG_BT_TBS_CLIENT_READ_BEARER_SIGNAL_INTERVAL) */
/* || defined(CONFIG_BT_TBS_CLIENT_SET_BEARER_SIGNAL_INTERVAL) */
#if defined(CONFIG_BT_TBS_CLIENT_CCID)
uint16_t ccid_handle;
#endif /* defined(CONFIG_BT_TBS_CLIENT_CCID) */
#if defined(CONFIG_BT_TBS_CLIENT_OPTIONAL_OPCODES)
uint16_t optional_opcodes_handle;
#endif /* defined(CONFIG_BT_TBS_CLIENT_OPTIONAL_OPCODES) */
uint16_t termination_reason_handle;
#if defined(CONFIG_BT_TBS_CLIENT_CCID)
uint8_t ccid;
#endif /* defined(CONFIG_BT_TBS_CLIENT_CCID) */
#if defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME)
struct bt_gatt_subscribe_params name_sub_params;
struct bt_gatt_discover_params name_sub_disc_params;
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_PROVIDER_NAME) */
#if defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY)
struct bt_gatt_subscribe_params technology_sub_params;
struct bt_gatt_discover_params technology_sub_disc_params;
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_TECHNOLOGY) */
#if defined(CONFIG_BT_TBS_CLIENT_BEARER_SIGNAL_STRENGTH)
struct bt_gatt_subscribe_params signal_strength_sub_params;
struct bt_gatt_discover_params signal_strength_sub_disc_params;
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_SIGNAL_STRENGTH) */
#if defined(CONFIG_BT_TBS_CLIENT_BEARER_LIST_CURRENT_CALLS)
struct bt_gatt_subscribe_params current_calls_sub_params;
struct bt_gatt_discover_params current_calls_sub_disc_params;
#endif /* defined(CONFIG_BT_TBS_CLIENT_BEARER_LIST_CURRENT_CALLS) */
#if defined(CONFIG_BT_TBS_CLIENT_STATUS_FLAGS)
struct bt_gatt_subscribe_params status_flags_sub_params;
struct bt_gatt_discover_params status_sub_disc_params;
#endif /* defined(CONFIG_BT_TBS_CLIENT_STATUS_FLAGS) */
#if defined(CONFIG_BT_TBS_CLIENT_INCOMING_URI)
struct bt_gatt_subscribe_params in_target_uri_sub_params;
struct bt_gatt_discover_params in_target_uri_sub_disc_params;
#endif /* defined(CONFIG_BT_TBS_CLIENT_INCOMING_URI) */
#if defined(CONFIG_BT_TBS_CLIENT_CP_PROCEDURES)
struct bt_gatt_subscribe_params call_cp_sub_params;
struct bt_gatt_discover_params call_cp_sub_disc_params;
#endif /* defined(CONFIG_BT_TBS_CLIENT_OPTIONAL_OPCODES) */
#if defined(CONFIG_BT_TBS_CLIENT_CALL_FRIENDLY_NAME)
struct bt_gatt_subscribe_params friendly_name_sub_params;
struct bt_gatt_discover_params friendly_name_sub_disc_params;
#endif /* defined(CONFIG_BT_TBS_CLIENT_CALL_FRIENDLY_NAME) */
#if defined(CONFIG_BT_TBS_CLIENT_INCOMING_CALL)
struct bt_gatt_subscribe_params incoming_call_sub_params;
struct bt_gatt_discover_params incoming_call_sub_disc_params;
#endif /* defined(CONFIG_BT_TBS_CLIENT_INCOMING_CALL) */
struct bt_gatt_subscribe_params call_state_sub_params;
struct bt_gatt_discover_params call_state_sub_disc_params;
struct bt_gatt_subscribe_params termination_sub_params;
struct bt_gatt_discover_params termination_sub_disc_params;
struct bt_gatt_read_params read_params;
uint8_t read_buf[BT_TBS_CLIENT_INST_READ_BUF_SIZE];
struct net_buf_simple net_buf;
ATOMIC_DEFINE(flags, BT_TBS_CLIENT_FLAG_NUM_FLAGS);
};
#endif /* CONFIG_BT_TBS_CLIENT */