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(&param, &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