1 /**
2 * @file
3 * @brief Shell APIs for Bluetooth CAP acceptor
4 *
5 * Copyright (c) 2022 Nordic Semiconductor ASA
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9 #include <errno.h>
10 #include <stdbool.h>
11 #include <stddef.h>
12 #include <stdint.h>
13 #include <string.h>
14
15 #include <zephyr/autoconf.h>
16 #include <zephyr/bluetooth/addr.h>
17 #include <zephyr/bluetooth/audio/audio.h>
18 #include <zephyr/bluetooth/audio/csip.h>
19 #include <zephyr/bluetooth/gap.h>
20 #include <zephyr/bluetooth/uuid.h>
21 #include <zephyr/shell/shell_string_conv.h>
22 #include <zephyr/sys/__assert.h>
23 #include <zephyr/sys/util.h>
24 #include <zephyr/sys/util_macro.h>
25 #include <zephyr/types.h>
26 #include <zephyr/shell/shell.h>
27 #include <zephyr/bluetooth/gatt.h>
28 #include <zephyr/bluetooth/bluetooth.h>
29 #include <zephyr/bluetooth/audio/cap.h>
30 #include "host/shell/bt.h"
31 #include "common/bt_shell_private.h"
32
ad_cap_announcement_data_add(struct bt_data data[],size_t data_size)33 static size_t ad_cap_announcement_data_add(struct bt_data data[], size_t data_size)
34 {
35 static const uint8_t ad_cap_announcement[3] = {
36 BT_UUID_16_ENCODE(BT_UUID_CAS_VAL),
37 BT_AUDIO_UNICAST_ANNOUNCEMENT_TARGETED,
38 };
39
40 __ASSERT(data_size > 0, "No space for AD_CAP_ANNOUNCEMENT");
41 data[0].type = BT_DATA_SVC_DATA16;
42 data[0].data_len = ARRAY_SIZE(ad_cap_announcement);
43 data[0].data = &ad_cap_announcement[0];
44
45 return 1U;
46 }
47
48 #if defined(CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER)
49 static struct bt_csip_set_member_svc_inst *cap_csip_svc_inst;
50 static uint8_t sirk_read_rsp = BT_CSIP_READ_SIRK_REQ_RSP_ACCEPT;
51
locked_cb(struct bt_conn * conn,struct bt_csip_set_member_svc_inst * svc_inst,bool locked)52 static void locked_cb(struct bt_conn *conn,
53 struct bt_csip_set_member_svc_inst *svc_inst,
54 bool locked)
55 {
56 if (conn == NULL) {
57 bt_shell_error("Server %s the device",
58 locked ? "locked" : "released");
59 } else {
60 char addr[BT_ADDR_LE_STR_LEN];
61
62 conn_addr_str(conn, addr, sizeof(addr));
63
64 bt_shell_print("Client %s %s the device",
65 addr, locked ? "locked" : "released");
66 }
67 }
68
sirk_read_req_cb(struct bt_conn * conn,struct bt_csip_set_member_svc_inst * svc_inst)69 static uint8_t sirk_read_req_cb(struct bt_conn *conn,
70 struct bt_csip_set_member_svc_inst *svc_inst)
71 {
72 char addr[BT_ADDR_LE_STR_LEN];
73 static const char *const rsp_strings[] = {
74 "Accept", "Accept Enc", "Reject", "OOB only"
75 };
76
77 conn_addr_str(conn, addr, sizeof(addr));
78
79 bt_shell_print("Client %s requested to read the sirk. Responding with %s",
80 addr, rsp_strings[sirk_read_rsp]);
81
82 return sirk_read_rsp;
83 }
84
85 static struct bt_csip_set_member_cb csip_set_member_cbs = {
86 .lock_changed = locked_cb,
87 .sirk_read_req = sirk_read_req_cb,
88 };
89
cmd_cap_acceptor_init(const struct shell * sh,size_t argc,char ** argv)90 static int cmd_cap_acceptor_init(const struct shell *sh, size_t argc,
91 char **argv)
92 {
93 struct bt_csip_set_member_register_param param = {
94 .set_size = 2,
95 .rank = 1,
96 .lockable = true,
97 /* Using the CSIS test sample SIRK */
98 .sirk = { 0xcd, 0xcc, 0x72, 0xdd, 0x86, 0x8c, 0xcd, 0xce,
99 0x22, 0xfd, 0xa1, 0x21, 0x09, 0x7d, 0x7d, 0x45 },
100 .cb = &csip_set_member_cbs,
101 };
102 int err = 0;
103
104 for (size_t argn = 1; argn < argc; argn++) {
105 const char *arg = argv[argn];
106
107 if (strcmp(arg, "size") == 0) {
108 unsigned long set_size;
109
110 argn++;
111 if (argn == argc) {
112 shell_help(sh);
113 return SHELL_CMD_HELP_PRINTED;
114 }
115
116 set_size = shell_strtoul(argv[argn], 0, &err);
117 if (err != 0) {
118 shell_error(sh, "Could not parse set_size: %d",
119 err);
120
121 return -ENOEXEC;
122 }
123
124 if (set_size > UINT8_MAX) {
125 shell_error(sh, "Invalid set_size: %lu",
126 set_size);
127
128 return -ENOEXEC;
129 }
130
131 param.set_size = set_size;
132 } else if (strcmp(arg, "rank") == 0) {
133 unsigned long rank;
134
135 argn++;
136 if (argn == argc) {
137 shell_help(sh);
138 return SHELL_CMD_HELP_PRINTED;
139 }
140
141 rank = shell_strtoul(argv[argn], 0, &err);
142 if (err != 0) {
143 shell_error(sh, "Could not parse rank: %d",
144 err);
145
146 return -ENOEXEC;
147 }
148
149 if (rank > UINT8_MAX) {
150 shell_error(sh, "Invalid rank: %lu", rank);
151
152 return -ENOEXEC;
153 }
154
155 param.rank = rank;
156 } else if (strcmp(arg, "not-lockable") == 0) {
157 param.lockable = false;
158 } else if (strcmp(arg, "sirk") == 0) {
159 size_t len;
160
161 argn++;
162 if (argn == argc) {
163 shell_help(sh);
164 return SHELL_CMD_HELP_PRINTED;
165 }
166
167 len = hex2bin(argv[argn], strlen(argv[argn]), param.sirk,
168 sizeof(param.sirk));
169 if (len == 0) {
170 shell_error(sh, "Could not parse SIRK");
171
172 return -ENOEXEC;
173 }
174 } else {
175 shell_help(sh);
176
177 return SHELL_CMD_HELP_PRINTED;
178 }
179 }
180
181 err = bt_cap_acceptor_register(¶m, &cap_csip_svc_inst);
182 if (err != 0) {
183 shell_error(sh, "Could not register CAS: %d", err);
184
185 return err;
186 }
187
188 return 0;
189 }
190
cmd_cap_acceptor_lock(const struct shell * sh,size_t argc,char * argv[])191 static int cmd_cap_acceptor_lock(const struct shell *sh, size_t argc,
192 char *argv[])
193 {
194 int err;
195
196 err = bt_csip_set_member_lock(cap_csip_svc_inst, true, false);
197 if (err != 0) {
198 shell_error(sh, "Failed to set lock: %d", err);
199
200 return -ENOEXEC;
201 }
202
203 shell_print(sh, "Set locked");
204
205 return 0;
206 }
207
cmd_cap_acceptor_release(const struct shell * sh,size_t argc,char * argv[])208 static int cmd_cap_acceptor_release(const struct shell *sh, size_t argc,
209 char *argv[])
210 {
211 bool force = false;
212 int err;
213
214 if (argc > 1) {
215 if (strcmp(argv[1], "force") == 0) {
216 force = true;
217 } else {
218 shell_error(sh, "Unknown parameter: %s", argv[1]);
219
220 return -ENOEXEC;
221 }
222 }
223
224 err = bt_csip_set_member_lock(cap_csip_svc_inst, false, force);
225
226 if (err != 0) {
227 shell_error(sh, "Failed to release lock: %d", err);
228
229 return -ENOEXEC;
230 }
231
232 shell_print(sh, "Set released");
233
234 return 0;
235 }
236
cmd_cap_acceptor_sirk(const struct shell * sh,size_t argc,char * argv[])237 static int cmd_cap_acceptor_sirk(const struct shell *sh, size_t argc, char *argv[])
238 {
239 uint8_t sirk[BT_CSIP_SIRK_SIZE];
240 size_t len;
241 int err;
242
243 if (cap_csip_svc_inst == NULL) {
244 shell_error(sh, "CSIS not registered");
245
246 return -ENOEXEC;
247 }
248
249 len = hex2bin(argv[1], strlen(argv[1]), sirk, sizeof(sirk));
250 if (len != sizeof(sirk)) {
251 shell_error(sh, "Invalid SIRK Length: %zu", len);
252
253 return -ENOEXEC;
254 }
255
256 err = bt_csip_set_member_sirk(cap_csip_svc_inst, sirk);
257 if (err != 0) {
258 shell_error(sh, "Failed to set SIRK: %d", err);
259 return -ENOEXEC;
260 }
261
262 shell_print(sh, "SIRK updated");
263
264 return 0;
265 }
266
cmd_cap_acceptor_get_info(const struct shell * sh,size_t argc,char * argv[])267 static int cmd_cap_acceptor_get_info(const struct shell *sh, size_t argc, char *argv[])
268 {
269 struct bt_csip_set_member_set_info info;
270 uint8_t sirk[BT_CSIP_SIRK_SIZE];
271 int err;
272
273 if (cap_csip_svc_inst == NULL) {
274 shell_error(sh, "CSIS not registered yet");
275
276 return -ENOEXEC;
277 }
278
279 err = bt_csip_set_member_get_info(cap_csip_svc_inst, &info);
280 if (err != 0) {
281 shell_error(sh, "Failed to get SIRK: %d", err);
282 return -ENOEXEC;
283 }
284
285 shell_print(sh, "Info for %p", cap_csip_svc_inst);
286 shell_print(sh, "\tSIRK");
287 shell_hexdump(sh, sirk, sizeof(sirk));
288 shell_print(sh, "\tSet size: %u", info.set_size);
289 shell_print(sh, "\tRank: %u", info.rank);
290 shell_print(sh, "\tLockable: %s", info.lockable ? "true" : "false");
291 shell_print(sh, "\tLocked: %s", info.locked ? "true" : "false");
292 if (info.locked) {
293 char addr_str[BT_ADDR_LE_STR_LEN];
294
295 bt_addr_le_to_str(&info.lock_client_addr, addr_str, sizeof(addr_str));
296 shell_print(sh, "\tLock owner: %s", addr_str);
297 }
298
299 return 0;
300 }
301
cmd_cap_acceptor_sirk_rsp(const struct shell * sh,size_t argc,char * argv[])302 static int cmd_cap_acceptor_sirk_rsp(const struct shell *sh, size_t argc, char *argv[])
303 {
304 if (strcmp(argv[1], "accept") == 0) {
305 sirk_read_rsp = BT_CSIP_READ_SIRK_REQ_RSP_ACCEPT;
306 } else if (strcmp(argv[1], "accept_enc") == 0) {
307 sirk_read_rsp = BT_CSIP_READ_SIRK_REQ_RSP_ACCEPT_ENC;
308 } else if (strcmp(argv[1], "reject") == 0) {
309 sirk_read_rsp = BT_CSIP_READ_SIRK_REQ_RSP_REJECT;
310 } else if (strcmp(argv[1], "oob") == 0) {
311 sirk_read_rsp = BT_CSIP_READ_SIRK_REQ_RSP_OOB_ONLY;
312 } else {
313 shell_error(sh, "Unknown parameter: %s", argv[1]);
314 return -ENOEXEC;
315 }
316
317 return 0;
318 }
319
cmd_cap_acceptor(const struct shell * sh,size_t argc,char ** argv)320 static int cmd_cap_acceptor(const struct shell *sh, size_t argc, char **argv)
321 {
322 shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
323
324 return -ENOEXEC;
325 }
326
327 SHELL_STATIC_SUBCMD_SET_CREATE(
328 cap_acceptor_cmds,
329 SHELL_CMD_ARG(init, NULL,
330 "Initialize the service and register callbacks "
331 "[size <int>] [rank <int>] [not-lockable] [sirk <data>]",
332 cmd_cap_acceptor_init, 1, 4),
333 SHELL_CMD_ARG(lock, NULL, "Lock the set", cmd_cap_acceptor_lock, 1, 0),
334 SHELL_CMD_ARG(release, NULL, "Release the set [force]", cmd_cap_acceptor_release, 1, 1),
335 SHELL_CMD_ARG(sirk, NULL, "Set the currently used SIRK <sirk>", cmd_cap_acceptor_sirk, 2,
336 0),
337 SHELL_CMD_ARG(get_info, NULL, "Get CSIS info", cmd_cap_acceptor_get_info, 1, 0),
338 SHELL_CMD_ARG(sirk_rsp, NULL,
339 "Set the response used in SIRK requests "
340 "<accept, accept_enc, reject, oob>",
341 cmd_cap_acceptor_sirk_rsp, 2, 0),
342 SHELL_SUBCMD_SET_END);
343
344 SHELL_CMD_ARG_REGISTER(cap_acceptor, &cap_acceptor_cmds, "Bluetooth CAP acceptor shell commands",
345 cmd_cap_acceptor, 1, 1);
346
cap_acceptor_ad_data_add(struct bt_data data[],size_t data_size,bool discoverable)347 size_t cap_acceptor_ad_data_add(struct bt_data data[], size_t data_size, bool discoverable)
348 {
349 size_t ad_len = 0;
350
351 if (!discoverable) {
352 return ad_len;
353 }
354
355 if (IS_ENABLED(CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER) && cap_csip_svc_inst != NULL) {
356 static uint8_t ad_rsi[BT_CSIP_RSI_SIZE];
357 int err;
358
359 ad_len += ad_cap_announcement_data_add(data, data_size);
360
361 /* A privacy-enabled Set Member should only advertise RSI values derived
362 * from a SIRK that is exposed in encrypted form.
363 */
364 if (IS_ENABLED(CONFIG_BT_PRIVACY) &&
365 !IS_ENABLED(CONFIG_BT_CSIP_SET_MEMBER_ENC_SIRK_SUPPORT)) {
366 bt_shell_warn("RSI derived from unencrypted SIRK");
367 }
368
369 err = bt_csip_set_member_generate_rsi(cap_csip_svc_inst, ad_rsi);
370 if (err != 0) {
371 bt_shell_error("Failed to generate RSI (err %d)", err);
372
373 return err;
374 }
375
376 __ASSERT(data_size > ad_len, "No space for AD_RSI");
377 data[ad_len].type = BT_DATA_CSIS_RSI;
378 data[ad_len].data_len = ARRAY_SIZE(ad_rsi);
379 data[ad_len].data = &ad_rsi[0];
380 ad_len++;
381 }
382
383 return ad_len;
384 }
385
386 #else /* !CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER */
387
cap_acceptor_ad_data_add(struct bt_data data[],size_t data_size,bool discoverable)388 size_t cap_acceptor_ad_data_add(struct bt_data data[], size_t data_size, bool discoverable)
389 {
390 if (!discoverable) {
391 return 0U;
392 }
393
394 return ad_cap_announcement_data_add(data, data_size);
395 }
396
397 #endif /* CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER */
398