| /** @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 */ |