/** @file * @brief Bluetooth Call Control Profile (CCP) Call Controller role. * * Copyright (c) 2020 Nordic Semiconductor ASA * Copyright (c) 2022 Codecoup * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include enum { CCP_FLAG_PROFILE_CONNECTED, CCP_FLAG_GTBS_DISCOVER, CCP_FLAG_CCID_READ, CCP_FLAG_STATUS_FLAGS_READ, CCP_FLAG_CALL_STATE_READ, CCP_FLAG_NUM, }; static ATOMIC_DEFINE(flags, CCP_FLAG_NUM)[CONFIG_BT_MAX_CONN]; static int process_profile_connection(struct bt_conn *conn) { atomic_t *flags_for_conn = flags[bt_conn_index(conn)]; int err = 0; if (!atomic_test_and_set_bit(flags_for_conn, CCP_FLAG_GTBS_DISCOVER)) { err = bt_tbs_client_discover(conn); if (err != 0) { printk("bt_tbs_client_discover (err %d)\n", err); } } else if (!atomic_test_and_set_bit(flags_for_conn, CCP_FLAG_CCID_READ)) { err = bt_tbs_client_read_ccid(conn, BT_TBS_GTBS_INDEX); if (err != 0) { printk("bt_tbs_client_read_ccid (err %d)\n", err); } } else if (!atomic_test_and_set_bit(flags_for_conn, CCP_FLAG_STATUS_FLAGS_READ)) { err = bt_tbs_client_read_status_flags(conn, BT_TBS_GTBS_INDEX); if (err != 0) { printk("bt_tbs_client_read_status_flags (err %d)\n", err); } } else if (!atomic_test_and_set_bit(flags_for_conn, CCP_FLAG_CALL_STATE_READ)) { err = bt_tbs_client_read_call_state(conn, BT_TBS_GTBS_INDEX); if (err != 0) { printk("bt_tbs_client_read_call_state (err %d)\n", err); } } else if (!atomic_test_and_set_bit(flags_for_conn, CCP_FLAG_PROFILE_CONNECTED)) { printk("CCP Profile connected\n"); } return err; } static void connected(struct bt_conn *conn, uint8_t err) { if (err) { printk("Connection failed (err %d)\n", err); return; } atomic_set(flags[bt_conn_index(conn)], 0); if (process_profile_connection(conn) != 0) { printk("Profile connection failed"); } } BT_CONN_CB_DEFINE(conn_callbacks) = { .connected = connected, }; static void discover_cb(struct bt_conn *conn, int err, uint8_t tbs_count, bool gtbs_found) { if (!gtbs_found) { printk("Failed to discover GTBS\n"); return; } if (err) { printk("%s (err %d)\n", __func__, err); return; } if (!atomic_test_bit(flags[bt_conn_index(conn)], CCP_FLAG_PROFILE_CONNECTED)) { process_profile_connection(conn); } } static void ccid_cb(struct bt_conn *conn, int err, uint8_t inst_index, uint32_t value) { if (inst_index != BT_TBS_GTBS_INDEX) { printk("Unexpected %s for instance %u\n", __func__, inst_index); return; } if (err) { printk("%s (err %d)\n", __func__, err); return; } if (!atomic_test_bit(flags[bt_conn_index(conn)], CCP_FLAG_PROFILE_CONNECTED)) { process_profile_connection(conn); } } static void status_flags_cb(struct bt_conn *conn, int err, uint8_t inst_index, uint32_t value) { if (inst_index != BT_TBS_GTBS_INDEX) { printk("Unexpected %s for instance %u\n", __func__, inst_index); return; } if (err) { printk("%s (err %d)\n", __func__, err); return; } if (!atomic_test_bit(flags[bt_conn_index(conn)], CCP_FLAG_PROFILE_CONNECTED)) { process_profile_connection(conn); } } static void call_state_cb(struct bt_conn *conn, int err, uint8_t inst_index, uint8_t call_count, const struct bt_tbs_client_call_state *call_states) { if (inst_index != BT_TBS_GTBS_INDEX) { printk("Unexpected %s for instance %u\n", __func__, inst_index); return; } if (err) { printk("%s (err %d)\n", __func__, err); return; } if (!atomic_test_bit(flags[bt_conn_index(conn)], CCP_FLAG_PROFILE_CONNECTED)) { process_profile_connection(conn); } } struct bt_tbs_client_cb tbs_client_cb = { .discover = discover_cb, .ccid = ccid_cb, .status_flags = status_flags_cb, .call_state = call_state_cb, }; int ccp_call_ctrl_init(void) { bt_tbs_client_register_cb(&tbs_client_cb); return 0; }