blob: c1c5f32d650bcd3216d3e0cf6b4566b466b83cf2 [file] [log] [blame]
/** @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 <zephyr/types.h>
#include <zephyr/bluetooth/audio/tbs.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 */
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";
case BT_TBS_TECHNOLOGY_IP:
return "IP";
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 a string 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 char *uri, size_t len)
{
if (!uri) {
return false;
}
if (len > CONFIG_BT_TBS_MAX_URI_LENGTH ||
len < BT_TBS_MIN_URI_LEN) {
return false;
} else if (uri[0] < FIRST_PRINTABLE_ASCII_CHAR) {
/* Invalid first char */
return false;
}
for (int i = 1; i < 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)
#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) */
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;
bool busy;
#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;
};
#endif /* CONFIG_BT_TBS_CLIENT */