blob: 03c99d935d3029f81ec5d0e6500185a49dd593de [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 <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 */
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"
* @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;
}
len = strlen(uri);
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;