1 /*
2 * Copyright 2023 NXP
3 * Copyright (c) 2024 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <errno.h>
9 #include <stdbool.h>
10 #include <stdint.h>
11 #include <string.h>
12 #include <sys/types.h>
13
14 #include <zephyr/autoconf.h>
15 #include <zephyr/bluetooth/att.h>
16 #include <zephyr/bluetooth/audio/tmap.h>
17 #include <zephyr/bluetooth/bluetooth.h>
18 #include <zephyr/bluetooth/conn.h>
19 #include <zephyr/bluetooth/gatt.h>
20 #include <zephyr/bluetooth/uuid.h>
21 #include <zephyr/device.h>
22 #include <zephyr/init.h>
23 #include <zephyr/kernel.h>
24 #include <zephyr/logging/log.h>
25 #include <zephyr/sys/byteorder.h>
26 #include <zephyr/sys/check.h>
27 #include <zephyr/sys/util_macro.h>
28 #include <zephyr/types.h>
29
30 #include "audio_internal.h"
31
32 LOG_MODULE_REGISTER(bt_tmap, CONFIG_BT_TMAP_LOG_LEVEL);
33
34 /* Hex value if all TMAP role bits are set */
35 #define TMAP_ALL_ROLES \
36 (BT_TMAP_ROLE_CG | BT_TMAP_ROLE_CT | BT_TMAP_ROLE_UMS | BT_TMAP_ROLE_UMR | \
37 BT_TMAP_ROLE_BMS | BT_TMAP_ROLE_BMR)
38
39 static uint16_t tmap_role;
40 static const struct bt_tmap_cb *cb;
41 static bool tmas_found;
42
43 static struct bt_uuid_16 uuid[CONFIG_BT_MAX_CONN] = {BT_UUID_INIT_16(0)};
44 static struct bt_gatt_discover_params discover_params[CONFIG_BT_MAX_CONN];
45 static struct bt_gatt_read_params read_params[CONFIG_BT_MAX_CONN];
46
tmap_char_read(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)47 uint8_t tmap_char_read(struct bt_conn *conn, uint8_t err,
48 struct bt_gatt_read_params *params,
49 const void *data, uint16_t length)
50 {
51 uint16_t peer_role;
52
53 /* Check read request result */
54 if (err != BT_ATT_ERR_SUCCESS) {
55 if (cb->discovery_complete) {
56 cb->discovery_complete(0, conn, err);
57 }
58
59 return BT_GATT_ITER_STOP;
60 }
61
62 /* Check data length */
63 if (length != sizeof(peer_role)) {
64 if (cb->discovery_complete) {
65 cb->discovery_complete(0, conn, BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
66 }
67
68 return BT_GATT_ITER_STOP;
69 }
70
71 /* Extract the TMAP role of the peer and inform application of the value found */
72 peer_role = sys_get_le16(data);
73
74 if ((peer_role > 0U) && (peer_role <= TMAP_ALL_ROLES)) {
75 if (cb->discovery_complete) {
76 cb->discovery_complete((enum bt_tmap_role)peer_role, conn, 0);
77 }
78 } else {
79 if (cb->discovery_complete) {
80 cb->discovery_complete(0, conn, BT_ATT_ERR_VALUE_NOT_ALLOWED);
81 }
82 }
83
84 return BT_GATT_ITER_STOP;
85 }
86
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)87 static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
88 struct bt_gatt_discover_params *params)
89 {
90 int err;
91 uint8_t conn_id = bt_conn_index(conn);
92
93 if (!attr) {
94 (void)memset(params, 0, sizeof(*params));
95 if (!tmas_found) {
96 /* TMAS not found on peer */
97 if (cb->discovery_complete) {
98 cb->discovery_complete(0, conn, BT_ATT_ERR_ATTRIBUTE_NOT_FOUND);
99 }
100 }
101
102 tmas_found = false;
103
104 return BT_GATT_ITER_STOP;
105 }
106
107 if (!bt_uuid_cmp(discover_params[conn_id].uuid, BT_UUID_TMAS)) {
108 LOG_DBG("Discovered TMAS\n");
109 tmas_found = true;
110 memcpy(&uuid[conn_id], BT_UUID_GATT_TMAPR, sizeof(uuid[conn_id]));
111 discover_params[conn_id].uuid = &uuid[conn_id].uuid;
112 discover_params[conn_id].start_handle = attr->handle + 1;
113 discover_params[conn_id].type = BT_GATT_DISCOVER_CHARACTERISTIC;
114
115 /* Discovered TMAS - Search for TMAP Role characteristic */
116 err = bt_gatt_discover(conn, &discover_params[conn_id]);
117 if (err) {
118 LOG_DBG("Discover failed (err %d)\n", err);
119 }
120 } else if (!bt_uuid_cmp(discover_params[conn_id].uuid, BT_UUID_GATT_TMAPR)) {
121 /* Use 0 for now, will expand later */
122 read_params[conn_id].func = tmap_char_read;
123 read_params[conn_id].handle_count = 1u;
124 read_params[conn_id].single.handle = bt_gatt_attr_value_handle(attr);
125 read_params[conn_id].single.offset = 0;
126
127 /* Discovered TMAP Role characteristic - read value */
128 err = bt_gatt_read(conn, &read_params[0]);
129 if (err != 0) {
130 LOG_DBG("Could not read peer TMAP Role");
131 }
132 } else {
133 return BT_GATT_ITER_CONTINUE;
134 }
135
136 return BT_GATT_ITER_STOP;
137 }
138
read_role(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)139 static ssize_t read_role(struct bt_conn *conn,
140 const struct bt_gatt_attr *attr, void *buf,
141 uint16_t len, uint16_t offset)
142 {
143 uint16_t role;
144
145 role = sys_cpu_to_le16(tmap_role);
146 LOG_DBG("TMAP: role 0x%04X", role);
147
148 return bt_gatt_attr_read(conn, attr, buf, len, offset,
149 &role, sizeof(role));
150 }
151
152 /* Telephony and Media Audio Service attributes */
153 #define BT_TMAS_SERVICE_DEFINITION \
154 BT_GATT_PRIMARY_SERVICE(BT_UUID_TMAS), \
155 BT_GATT_CHARACTERISTIC(BT_UUID_GATT_TMAPR, \
156 BT_GATT_CHRC_READ, \
157 BT_GATT_PERM_READ, \
158 read_role, NULL, NULL)
159
160 static struct bt_gatt_attr svc_attrs[] = { BT_TMAS_SERVICE_DEFINITION };
161 static struct bt_gatt_service tmas;
162
valid_tmap_role(enum bt_tmap_role role)163 static bool valid_tmap_role(enum bt_tmap_role role)
164 {
165 if (role == 0 || (role & TMAP_ALL_ROLES) != role) {
166 LOG_DBG("Invalid role %d", role);
167 }
168
169 if ((role & BT_TMAP_ROLE_CG) != 0 && !IS_ENABLED(CONFIG_BT_TMAP_CG_SUPPORTED)) {
170 LOG_DBG("Device does not support the CG role");
171
172 return false;
173 }
174
175 if ((role & BT_TMAP_ROLE_CT) != 0 && !IS_ENABLED(CONFIG_BT_TMAP_CT_SUPPORTED)) {
176 LOG_DBG("Device does not support the CT role");
177
178 return false;
179 }
180
181 if ((role & BT_TMAP_ROLE_UMS) != 0 && !IS_ENABLED(CONFIG_BT_TMAP_UMS_SUPPORTED)) {
182 LOG_DBG("Device does not support the UMS role");
183
184 return false;
185 }
186
187 if ((role & BT_TMAP_ROLE_UMR) != 0 && !IS_ENABLED(CONFIG_BT_TMAP_UMR_SUPPORTED)) {
188 LOG_DBG("Device does not support the UMR role");
189
190 return false;
191 }
192
193 if ((role & BT_TMAP_ROLE_BMS) != 0 && !IS_ENABLED(CONFIG_BT_TMAP_BMS_SUPPORTED)) {
194 LOG_DBG("Device does not support the BMS role");
195
196 return false;
197 }
198
199 if ((role & BT_TMAP_ROLE_BMR) != 0 && !IS_ENABLED(CONFIG_BT_TMAP_BMR_SUPPORTED)) {
200 LOG_DBG("Device does not support the BMR role");
201
202 return false;
203 }
204
205 return true;
206 }
207
bt_tmap_register(enum bt_tmap_role role)208 int bt_tmap_register(enum bt_tmap_role role)
209 {
210 int err;
211
212 CHECKIF(!valid_tmap_role(role)) {
213 LOG_DBG("Invalid role: %d", role);
214
215 return -EINVAL;
216 }
217
218 tmas = (struct bt_gatt_service)BT_GATT_SERVICE(svc_attrs);
219
220 err = bt_gatt_service_register(&tmas);
221 if (err) {
222 LOG_DBG("Could not register the TMAS service");
223 return -ENOEXEC;
224 }
225
226 tmap_role = role;
227 tmas_found = false;
228
229 return 0;
230 }
231
bt_tmap_discover(struct bt_conn * conn,const struct bt_tmap_cb * tmap_cb)232 int bt_tmap_discover(struct bt_conn *conn, const struct bt_tmap_cb *tmap_cb)
233 {
234 int err = 0;
235 uint8_t conn_id = bt_conn_index(conn);
236
237 cb = tmap_cb;
238
239 memcpy(&uuid[conn_id], BT_UUID_TMAS, sizeof(uuid[conn_id]));
240 discover_params[conn_id].uuid = &uuid[conn_id].uuid;
241 discover_params[conn_id].func = discover_func;
242 discover_params[conn_id].start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
243 discover_params[conn_id].end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
244 discover_params[conn_id].type = BT_GATT_DISCOVER_PRIMARY;
245
246 err = bt_gatt_discover(conn, &discover_params[conn_id]);
247
248 return err;
249 }
250
bt_tmap_set_role(enum bt_tmap_role role)251 void bt_tmap_set_role(enum bt_tmap_role role)
252 {
253 tmap_role = role;
254 }
255