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
ad_cap_announcement_data_add(struct bt_data data[],size_t data_size)32 static size_t ad_cap_announcement_data_add(struct bt_data data[], size_t data_size)
33 {
34 static const uint8_t ad_cap_announcement[3] = {
35 BT_UUID_16_ENCODE(BT_UUID_CAS_VAL),
36 BT_AUDIO_UNICAST_ANNOUNCEMENT_TARGETED,
37 };
38
39 __ASSERT(data_size > 0, "No space for AD_CAP_ANNOUNCEMENT");
40 data[0].type = BT_DATA_SVC_DATA16;
41 data[0].data_len = ARRAY_SIZE(ad_cap_announcement);
42 data[0].data = &ad_cap_announcement[0];
43
44 return 1U;
45 }
46
47 #if defined(CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER)
48 extern const struct shell *ctx_shell;
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 shell_error(ctx_shell, "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 shell_print(ctx_shell, "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 shell_print(ctx_shell, "Client %s requested to read the sirk. "
80 "Responding with %s", 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_sirk(const struct shell * sh,size_t argc,char * argv[])267 static int cmd_cap_acceptor_get_sirk(const struct shell *sh, size_t argc, char *argv[])
268 {
269 uint8_t sirk[BT_CSIP_SIRK_SIZE];
270 int err;
271
272 if (cap_csip_svc_inst == NULL) {
273 shell_error(sh, "CSIS not registered");
274
275 return -ENOEXEC;
276 }
277
278 err = bt_csip_set_member_get_sirk(cap_csip_svc_inst, sirk);
279 if (err != 0) {
280 shell_error(sh, "Failed to get SIRK: %d", err);
281 return -ENOEXEC;
282 }
283
284 shell_print(sh, "SIRK");
285 shell_hexdump(sh, sirk, sizeof(sirk));
286
287 return 0;
288 }
289
cmd_cap_acceptor_sirk_rsp(const struct shell * sh,size_t argc,char * argv[])290 static int cmd_cap_acceptor_sirk_rsp(const struct shell *sh, size_t argc, char *argv[])
291 {
292 if (strcmp(argv[1], "accept") == 0) {
293 sirk_read_rsp = BT_CSIP_READ_SIRK_REQ_RSP_ACCEPT;
294 } else if (strcmp(argv[1], "accept_enc") == 0) {
295 sirk_read_rsp = BT_CSIP_READ_SIRK_REQ_RSP_ACCEPT_ENC;
296 } else if (strcmp(argv[1], "reject") == 0) {
297 sirk_read_rsp = BT_CSIP_READ_SIRK_REQ_RSP_REJECT;
298 } else if (strcmp(argv[1], "oob") == 0) {
299 sirk_read_rsp = BT_CSIP_READ_SIRK_REQ_RSP_OOB_ONLY;
300 } else {
301 shell_error(sh, "Unknown parameter: %s", argv[1]);
302 return -ENOEXEC;
303 }
304
305 return 0;
306 }
307
cmd_cap_acceptor(const struct shell * sh,size_t argc,char ** argv)308 static int cmd_cap_acceptor(const struct shell *sh, size_t argc, char **argv)
309 {
310 shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
311
312 return -ENOEXEC;
313 }
314
315 SHELL_STATIC_SUBCMD_SET_CREATE(
316 cap_acceptor_cmds,
317 SHELL_CMD_ARG(init, NULL,
318 "Initialize the service and register callbacks "
319 "[size <int>] [rank <int>] [not-lockable] [sirk <data>]",
320 cmd_cap_acceptor_init, 1, 4),
321 SHELL_CMD_ARG(lock, NULL, "Lock the set", cmd_cap_acceptor_lock, 1, 0),
322 SHELL_CMD_ARG(release, NULL, "Release the set [force]", cmd_cap_acceptor_release, 1, 1),
323 SHELL_CMD_ARG(sirk, NULL, "Set the currently used SIRK <sirk>", cmd_cap_acceptor_sirk, 2,
324 0),
325 SHELL_CMD_ARG(get_sirk, NULL, "Get the currently used SIRK", cmd_cap_acceptor_get_sirk, 1,
326 0),
327 SHELL_CMD_ARG(sirk_rsp, NULL,
328 "Set the response used in SIRK requests "
329 "<accept, accept_enc, reject, oob>",
330 cmd_cap_acceptor_sirk_rsp, 2, 0),
331 SHELL_SUBCMD_SET_END
332 );
333
334 SHELL_CMD_ARG_REGISTER(cap_acceptor, &cap_acceptor_cmds, "Bluetooth CAP acceptor shell commands",
335 cmd_cap_acceptor, 1, 1);
336
cap_acceptor_ad_data_add(struct bt_data data[],size_t data_size,bool discoverable)337 size_t cap_acceptor_ad_data_add(struct bt_data data[], size_t data_size, bool discoverable)
338 {
339 size_t ad_len = 0;
340
341 if (!discoverable) {
342 return ad_len;
343 }
344
345 if (IS_ENABLED(CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER) && cap_csip_svc_inst != NULL) {
346 static uint8_t ad_rsi[BT_CSIP_RSI_SIZE];
347 int err;
348
349 ad_len += ad_cap_announcement_data_add(data, data_size);
350
351 /* A privacy-enabled Set Member should only advertise RSI values derived
352 * from a SIRK that is exposed in encrypted form.
353 */
354 if (IS_ENABLED(CONFIG_BT_PRIVACY) &&
355 !IS_ENABLED(CONFIG_BT_CSIP_SET_MEMBER_ENC_SIRK_SUPPORT)) {
356 shell_warn(ctx_shell, "RSI derived from unencrypted SIRK");
357 }
358
359 err = bt_csip_set_member_generate_rsi(cap_csip_svc_inst, ad_rsi);
360 if (err != 0) {
361 shell_error(ctx_shell, "Failed to generate RSI (err %d)", err);
362
363 return err;
364 }
365
366 __ASSERT(data_size > ad_len, "No space for AD_RSI");
367 data[ad_len].type = BT_DATA_CSIS_RSI;
368 data[ad_len].data_len = ARRAY_SIZE(ad_rsi);
369 data[ad_len].data = &ad_rsi[0];
370 ad_len++;
371 }
372
373 return ad_len;
374 }
375
376 #else /* !CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER */
377
cap_acceptor_ad_data_add(struct bt_data data[],size_t data_size,bool discoverable)378 size_t cap_acceptor_ad_data_add(struct bt_data data[], size_t data_size, bool discoverable)
379 {
380 if (!discoverable) {
381 return 0U;
382 }
383
384 return ad_cap_announcement_data_add(data, data_size);
385 }
386
387 #endif /* CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER */
388