1 /** @file
2 * @brief Bluetooth Call Control Profile (CCP) Call Controller role.
3 *
4 * Copyright (c) 2020 Nordic Semiconductor ASA
5 * Copyright (c) 2022 Codecoup
6 * Copyright 2023 NXP
7 *
8 * SPDX-License-Identifier: Apache-2.0
9 */
10
11 #include <zephyr/kernel.h>
12 #include <zephyr/sys/printk.h>
13 #include <zephyr/sys/util.h>
14 #include <string.h>
15
16 #include <zephyr/bluetooth/conn.h>
17 #include <zephyr/bluetooth/audio/tbs.h>
18
19 #define URI_SEPARATOR ":"
20 #define CALLER_ID "friend"
21
22 static uint8_t new_call_index;
23 static char remote_uri[CONFIG_BT_TBS_MAX_URI_LENGTH];
24
25 static K_SEM_DEFINE(sem_discovery_done, 0, 1);
26
27 static struct bt_conn *default_conn;
28
discover_cb(struct bt_conn * conn,int err,uint8_t tbs_count,bool gtbs_found)29 static void discover_cb(struct bt_conn *conn, int err, uint8_t tbs_count, bool gtbs_found)
30 {
31 if (!gtbs_found) {
32 printk("CCP: Failed to discover GTBS\n");
33 return;
34 }
35
36 printk("CCP: Discovered GTBS\n");
37
38 if (err) {
39 printk("%s (err %d)\n", __func__, err);
40 return;
41 }
42
43 /* Read Bearer URI Schemes Supported List Characteristic */
44 bt_tbs_client_read_uri_list(conn, BT_TBS_GTBS_INDEX);
45 }
46
originate_call_cb(struct bt_conn * conn,int err,uint8_t inst_index,uint8_t call_index)47 static void originate_call_cb(struct bt_conn *conn, int err, uint8_t inst_index, uint8_t call_index)
48 {
49 if (inst_index != BT_TBS_GTBS_INDEX) {
50 printk("Unexpected %s for instance %u\n", __func__, inst_index);
51 return;
52 }
53
54 if (err) {
55 printk("%s (err %d)\n", __func__, err);
56 return;
57 }
58 printk("CCP: Call originate successful\n");
59 new_call_index = call_index;
60 }
61
terminate_call_cb(struct bt_conn * conn,int err,uint8_t inst_index,uint8_t call_index)62 static void terminate_call_cb(struct bt_conn *conn, int err,
63 uint8_t inst_index, uint8_t call_index)
64 {
65 if (inst_index != BT_TBS_GTBS_INDEX) {
66 printk("Unexpected %s for instance %u\n", __func__, inst_index);
67 return;
68 }
69
70 if (err) {
71 printk("%s (err %d)\n", __func__, err);
72 return;
73 }
74 printk("CCP: Call with id %d terminated\n", call_index);
75 }
76
read_uri_schemes_string_cb(struct bt_conn * conn,int err,uint8_t inst_index,const char * value)77 static void read_uri_schemes_string_cb(struct bt_conn *conn, int err,
78 uint8_t inst_index, const char *value)
79 {
80 size_t i;
81
82 if (inst_index != BT_TBS_GTBS_INDEX) {
83 printk("Unexpected %s for instance %u\n", __func__, inst_index);
84 return;
85 }
86
87 if (err) {
88 printk("%s (err %d)\n", __func__, err);
89 return;
90 }
91
92 /* Save first remote URI
93 *
94 * First search for the first comma (separator), and use that to determine the end of the
95 * first (or only) URI. Then use that length to copy the URI to `remote_uri` for later use.
96 */
97 for (i = 0U; i < strlen(value); i++) {
98 if (value[i] == ',') {
99 break;
100 }
101 }
102
103 if (i >= sizeof(remote_uri)) {
104 printk("Cannot store URI of length %zu: %s\n", i, value);
105 return;
106 }
107
108 strncpy(remote_uri, value, i);
109 remote_uri[i] = '\0';
110
111 printk("CCP: Discovered remote URI: %s\n", remote_uri);
112 k_sem_give(&sem_discovery_done);
113 }
114
115 struct bt_tbs_client_cb tbs_client_cb = {
116 .discover = discover_cb,
117 .uri_list = read_uri_schemes_string_cb,
118 .originate_call = originate_call_cb,
119 .terminate_call = terminate_call_cb,
120 };
121
ccp_call_ctrl_init(struct bt_conn * conn)122 int ccp_call_ctrl_init(struct bt_conn *conn)
123 {
124 int err;
125
126 default_conn = bt_conn_ref(conn);
127 bt_tbs_client_register_cb(&tbs_client_cb);
128 err = bt_tbs_client_discover(conn);
129 if (err != 0) {
130 return err;
131 }
132 k_sem_take(&sem_discovery_done, K_FOREVER);
133
134 return err;
135 }
136
ccp_originate_call(void)137 int ccp_originate_call(void)
138 {
139 int err;
140 char uri[CONFIG_BT_TBS_MAX_URI_LENGTH];
141
142 strcpy(uri, remote_uri);
143 strcat(uri, URI_SEPARATOR);
144 strcat(uri, CALLER_ID);
145
146 err = bt_tbs_client_originate_call(default_conn, BT_TBS_GTBS_INDEX, uri);
147 if (err != BT_TBS_RESULT_CODE_SUCCESS) {
148 printk("TBS originate call failed: %d\n", err);
149 }
150
151 return err;
152 }
153
ccp_terminate_call(void)154 int ccp_terminate_call(void)
155 {
156 int err;
157
158 err = bt_tbs_client_terminate_call(default_conn, BT_TBS_GTBS_INDEX, new_call_index);
159 if (err != BT_TBS_RESULT_CODE_SUCCESS) {
160 printk("TBS terminate call failed: %d\n", err);
161 }
162
163 return err;
164 }
165