1 /**
2  * @file
3  * @brief Shell APIs for Bluetooth CSIP set coordinator
4  *
5  * Copyright (c) 2020 Bose Corporation
6  * Copyright (c) 2021-2022 Nordic Semiconductor ASA
7  *
8  * SPDX-License-Identifier: Apache-2.0
9  */
10 
11 #include <errno.h>
12 #include <stdbool.h>
13 #include <stddef.h>
14 #include <stdint.h>
15 #include <stdlib.h>
16 #include <string.h>
17 
18 #include <zephyr/autoconf.h>
19 #include <zephyr/bluetooth/audio/csip.h>
20 #include <zephyr/bluetooth/addr.h>
21 #include <zephyr/bluetooth/conn.h>
22 #include <zephyr/bluetooth/gap.h>
23 #include <zephyr/bluetooth/gatt.h>
24 #include <zephyr/bluetooth/bluetooth.h>
25 #include <zephyr/net_buf.h>
26 #include <zephyr/shell/shell_string_conv.h>
27 #include <zephyr/sys/printk.h>
28 #include <zephyr/sys/util.h>
29 #include <zephyr/types.h>
30 #include <zephyr/kernel.h>
31 #include <zephyr/types.h>
32 #include <zephyr/shell/shell.h>
33 
34 #include "host/shell/bt.h"
35 #include "common/bt_shell_private.h"
36 
37 static uint8_t members_found;
38 static struct k_work_delayable discover_members_timer;
39 static struct bt_conn *conns[CONFIG_BT_MAX_CONN];
40 static const struct bt_csip_set_coordinator_set_member *set_members[CONFIG_BT_MAX_CONN];
41 static struct bt_csip_set_coordinator_csis_inst *cur_inst;
42 static bt_addr_le_t addr_found[CONFIG_BT_MAX_CONN];
43 static const struct bt_csip_set_coordinator_set_member *locked_members[CONFIG_BT_MAX_CONN];
44 
is_discovered(const bt_addr_le_t * addr)45 static bool is_discovered(const bt_addr_le_t *addr)
46 {
47 	for (int i = 0; i < members_found; i++) {
48 		if (bt_addr_le_eq(addr, &addr_found[i])) {
49 			return true;
50 		}
51 	}
52 	return false;
53 }
54 
connected_cb(struct bt_conn * conn,uint8_t err)55 static void connected_cb(struct bt_conn *conn, uint8_t err)
56 {
57 	char addr[BT_ADDR_LE_STR_LEN];
58 	uint8_t conn_index;
59 
60 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
61 
62 	if (err != 0) {
63 		bt_shell_error("Failed to connect to %s (%u)", addr, err);
64 		return;
65 	}
66 
67 	conn_index = bt_conn_index(conn);
68 
69 	bt_shell_print("[%u]: Connected to %s", conn_index, addr);
70 
71 	/* TODO: Handle RPAs */
72 
73 	conns[conn_index] = bt_conn_ref(conn);
74 	bt_shell_print("Member[%u] connected", conn_index);
75 }
76 
disconnected_cb(struct bt_conn * conn,uint8_t reason)77 static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
78 {
79 	uint8_t conn_index = bt_conn_index(conn);
80 
81 	bt_conn_unref(conns[conn_index]);
82 	conns[conn_index] = NULL;
83 }
84 
85 BT_CONN_CB_DEFINE(conn_callbacks) = {
86 	.connected = connected_cb,
87 	.disconnected = disconnected_cb
88 };
89 
csip_discover_cb(struct bt_conn * conn,const struct bt_csip_set_coordinator_set_member * member,int err,size_t set_count)90 static void csip_discover_cb(struct bt_conn *conn,
91 			     const struct bt_csip_set_coordinator_set_member *member,
92 			     int err, size_t set_count)
93 {
94 	uint8_t conn_index;
95 
96 	if (err != 0) {
97 		bt_shell_error("discover failed (%d)", err);
98 		return;
99 	}
100 
101 	if (set_count == 0) {
102 		bt_shell_warn("Device has no sets");
103 		return;
104 	}
105 
106 	conn_index = bt_conn_index(conn);
107 
108 	bt_shell_print("Found %zu sets on member[%u]", set_count, conn_index);
109 
110 	for (size_t i = 0U; i < set_count; i++) {
111 		bt_shell_print("CSIS[%zu]: %p", i, &member->insts[i]);
112 		bt_shell_print("\tRank: %u", member->insts[i].info.rank);
113 		bt_shell_print("\tSet Size: %u", member->insts[i].info.set_size);
114 		bt_shell_print("\tLockable: %u", member->insts[i].info.lockable);
115 	}
116 
117 	set_members[conn_index] = member;
118 }
119 
csip_set_coordinator_lock_set_cb(int err)120 static void csip_set_coordinator_lock_set_cb(int err)
121 {
122 	if (err != 0) {
123 		bt_shell_error("Lock sets failed (%d)", err);
124 		return;
125 	}
126 
127 	bt_shell_print("Set locked");
128 }
129 
csip_set_coordinator_release_set_cb(int err)130 static void csip_set_coordinator_release_set_cb(int err)
131 {
132 	if (err != 0) {
133 		bt_shell_error("Lock sets failed (%d)", err);
134 		return;
135 	}
136 
137 	bt_shell_print("Set released");
138 }
139 
csip_set_coordinator_ordered_access_cb(const struct bt_csip_set_coordinator_set_info * set_info,int err,bool locked,struct bt_csip_set_coordinator_set_member * member)140 static void csip_set_coordinator_ordered_access_cb(
141 	const struct bt_csip_set_coordinator_set_info *set_info, int err,
142 	bool locked, struct bt_csip_set_coordinator_set_member *member)
143 {
144 	if (err) {
145 		printk("Ordered access failed with err %d\n", err);
146 	} else if (locked) {
147 		printk("Cannot do ordered access as member %p is locked\n",
148 		       member);
149 	} else {
150 		printk("Ordered access procedure finished\n");
151 	}
152 }
153 
csip_set_coordinator_lock_changed_cb(struct bt_csip_set_coordinator_csis_inst * inst,bool locked)154 static void csip_set_coordinator_lock_changed_cb(struct bt_csip_set_coordinator_csis_inst *inst,
155 						 bool locked)
156 {
157 	bt_shell_print("Inst %p %s\n", inst, locked ? "locked" : "released");
158 }
159 
csip_set_coordinator_sirk_changed_cb(struct bt_csip_set_coordinator_csis_inst * inst)160 static void csip_set_coordinator_sirk_changed_cb(struct bt_csip_set_coordinator_csis_inst *inst)
161 {
162 	bt_shell_print("Inst %p SIRK changed\n", inst);
163 	bt_shell_hexdump(inst->info.sirk, BT_CSIP_SIRK_SIZE);
164 }
165 
166 static void
csip_set_coordinator_size_changed_cb(struct bt_conn * conn,const struct bt_csip_set_coordinator_csis_inst * inst)167 csip_set_coordinator_size_changed_cb(struct bt_conn *conn,
168 				     const struct bt_csip_set_coordinator_csis_inst *inst)
169 {
170 	bt_shell_print("Inst %p size changed: %u\n", inst, inst->info.set_size);
171 }
172 
173 static struct bt_csip_set_coordinator_cb cbs = {
174 	.lock_set = csip_set_coordinator_lock_set_cb,
175 	.release_set = csip_set_coordinator_release_set_cb,
176 	.discover = csip_discover_cb,
177 	.ordered_access = csip_set_coordinator_ordered_access_cb,
178 	.lock_changed = csip_set_coordinator_lock_changed_cb,
179 	.sirk_changed = csip_set_coordinator_sirk_changed_cb,
180 	.size_changed = csip_set_coordinator_size_changed_cb,
181 };
182 
csip_set_coordinator_oap_cb(const struct bt_csip_set_coordinator_set_info * set_info,struct bt_csip_set_coordinator_set_member * members[],size_t count)183 static bool csip_set_coordinator_oap_cb(const struct bt_csip_set_coordinator_set_info *set_info,
184 					struct bt_csip_set_coordinator_set_member *members[],
185 					size_t count)
186 {
187 	for (size_t i = 0; i < count; i++) {
188 		printk("Ordered access for members[%zu]: %p\n", i, members[i]);
189 	}
190 
191 	return true;
192 }
193 
194 static bool csip_found(struct bt_data *data, void *user_data);
csip_set_coordinator_scan_recv(const struct bt_le_scan_recv_info * info,struct net_buf_simple * ad)195 static void csip_set_coordinator_scan_recv(const struct bt_le_scan_recv_info *info,
196 					   struct net_buf_simple *ad)
197 {
198 	/* We're only interested in connectable events */
199 	if (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) {
200 		if (!passes_scan_filter(info, ad)) {
201 			return;
202 		}
203 
204 		if (cur_inst != NULL) {
205 			bt_data_parse(ad, csip_found, (void *)info->addr);
206 		}
207 	}
208 }
209 
210 static struct bt_le_scan_cb csip_set_coordinator_scan_callbacks = {
211 	.recv = csip_set_coordinator_scan_recv
212 };
213 
csip_found(struct bt_data * data,void * user_data)214 static bool csip_found(struct bt_data *data, void *user_data)
215 {
216 	if (bt_csip_set_coordinator_is_set_member(cur_inst->info.sirk, data)) {
217 		bt_addr_le_t *addr = user_data;
218 		char addr_str[BT_ADDR_LE_STR_LEN];
219 
220 		bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
221 		bt_shell_print("Found CSIP advertiser with address %s", addr_str);
222 
223 		if (is_discovered(addr)) {
224 			bt_shell_print("Set member already found");
225 			/* Stop parsing */
226 			return false;
227 		}
228 
229 		bt_addr_le_copy(&addr_found[members_found++], addr);
230 
231 		bt_shell_print("Found member (%u / %u)",
232 			       members_found, cur_inst->info.set_size);
233 
234 		if (members_found == cur_inst->info.set_size) {
235 			int err;
236 
237 			(void)k_work_cancel_delayable(&discover_members_timer);
238 
239 			bt_le_scan_cb_unregister(&csip_set_coordinator_scan_callbacks);
240 
241 			err = bt_le_scan_stop();
242 			if (err != 0) {
243 				bt_shell_error("Failed to stop scan: %d", err);
244 			}
245 		}
246 
247 		/* Stop parsing */
248 		return false;
249 	}
250 	/* Continue parsing */
251 	return true;
252 }
253 
discover_members_timer_handler(struct k_work * work)254 static void discover_members_timer_handler(struct k_work *work)
255 {
256 	int err;
257 
258 	bt_shell_error("Could not find all members (%u / %u)",
259 		       members_found, cur_inst->info.set_size);
260 
261 	bt_le_scan_cb_unregister(&csip_set_coordinator_scan_callbacks);
262 
263 	err = bt_le_scan_stop();
264 	if (err != 0) {
265 		bt_shell_error("Failed to stop scan: %d", err);
266 	}
267 }
268 
cmd_csip_set_coordinator_discover(const struct shell * sh,size_t argc,char * argv[])269 static int cmd_csip_set_coordinator_discover(const struct shell *sh,
270 					     size_t argc, char *argv[])
271 {
272 	unsigned long member_index = 0U;
273 	char addr[BT_ADDR_LE_STR_LEN];
274 	static bool initialized;
275 	struct bt_conn *conn;
276 	int err = 0;
277 
278 	if (!initialized) {
279 		k_work_init_delayable(&discover_members_timer,
280 				      discover_members_timer_handler);
281 		bt_csip_set_coordinator_register_cb(&cbs);
282 		initialized = true;
283 	}
284 
285 	if (argc > 1) {
286 		member_index = shell_strtoul(argv[1], 0, &err);
287 		if (err != 0) {
288 			shell_error(sh, "Could not parse member_index: %d",
289 				    err);
290 
291 			return -ENOEXEC;
292 		}
293 
294 		if (member_index > ARRAY_SIZE(conns)) {
295 			shell_error(sh, "Invalid member_index: %lu",
296 				    member_index);
297 
298 			return -ENOEXEC;
299 		}
300 	}
301 
302 	conn = conns[member_index];
303 
304 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
305 
306 	shell_print(sh, "Discovering for member[%u] (%s)",
307 		    (uint8_t)member_index, addr);
308 	err = bt_csip_set_coordinator_discover(conn);
309 	if (err != 0) {
310 		shell_error(sh, "Fail: %d", err);
311 	}
312 
313 	return err;
314 }
315 
cmd_csip_set_coordinator_discover_members(const struct shell * sh,size_t argc,char * argv[])316 static int cmd_csip_set_coordinator_discover_members(const struct shell *sh,
317 						     size_t argc, char *argv[])
318 {
319 	int err;
320 
321 	cur_inst = (struct bt_csip_set_coordinator_csis_inst *)strtol(argv[1], NULL, 0);
322 
323 	if (cur_inst == NULL) {
324 		shell_error(sh, "NULL set");
325 		return -EINVAL;
326 	}
327 
328 	if (cur_inst->info.set_size > ARRAY_SIZE(set_members)) {
329 		/*
330 		 * TODO Handle case where set size is larger than
331 		 * number of possible connections
332 		 */
333 		shell_error(sh,
334 			    "Set size (%u) larger than max connections (%u)",
335 			    cur_inst->info.set_size, ARRAY_SIZE(set_members));
336 		return -EINVAL;
337 	}
338 
339 	/* Reset and populate based on current connections */
340 	memset(addr_found, 0, sizeof(addr_found));
341 	members_found = 0;
342 	for (size_t i = 0U; i < ARRAY_SIZE(set_members); i++) {
343 		const struct bt_csip_set_coordinator_set_member *set_member = set_members[i];
344 
345 		if (set_member == NULL) {
346 			continue;
347 		}
348 
349 		for (size_t j = 0U; j < ARRAY_SIZE(set_members[i]->insts); j++) {
350 			const struct bt_csip_set_coordinator_csis_inst *inst =
351 				&set_members[i]->insts[j];
352 
353 			if (memcmp(inst->info.sirk, cur_inst->info.sirk, BT_CSIP_SIRK_SIZE) == 0) {
354 				bt_addr_le_copy(&addr_found[members_found++],
355 						bt_conn_get_dst(conns[i]));
356 				break;
357 			}
358 		}
359 	}
360 
361 	if (cur_inst->info.set_size > 0) {
362 		if (members_found == cur_inst->info.set_size) {
363 			shell_print(sh, "All members already known");
364 
365 			return 0;
366 		} else if (members_found > cur_inst->info.set_size) {
367 			shell_error(sh, "Found %u members but set size is %u", members_found,
368 				    cur_inst->info.set_size);
369 
370 			return -ENOEXEC;
371 		}
372 	}
373 
374 	shell_print(sh, "Already know %u/%u members, start scanning for remaining", members_found,
375 		    cur_inst->info.set_size);
376 
377 	err = k_work_reschedule(&discover_members_timer,
378 				BT_CSIP_SET_COORDINATOR_DISCOVER_TIMER_VALUE);
379 	if (err < 0) { /* Can return 0, 1 and 2 for success */
380 		shell_error(sh,
381 			    "Could not schedule discover_members_timer %d",
382 			    err);
383 		return err;
384 	}
385 
386 	bt_le_scan_cb_register(&csip_set_coordinator_scan_callbacks);
387 
388 	err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
389 	if (err != 0) {
390 		shell_error(sh, "Could not start scan: %d", err);
391 	}
392 
393 	return err;
394 }
395 
396 #if defined(CONFIG_BT_BONDABLE)
cmd_csip_set_coordinator_lock_set(const struct shell * sh,size_t argc,char * argv[])397 static int cmd_csip_set_coordinator_lock_set(const struct shell *sh,
398 					     size_t argc, char *argv[])
399 {
400 	int err;
401 	int conn_count = 0;
402 
403 	if (cur_inst == NULL) {
404 		shell_error(sh, "No set selected");
405 		return -ENOEXEC;
406 	}
407 
408 	for (size_t i = 0; i < ARRAY_SIZE(locked_members); i++) {
409 		if (set_members[i] != NULL) {
410 			locked_members[conn_count++] = set_members[i];
411 		}
412 	}
413 
414 	err = bt_csip_set_coordinator_lock(locked_members, conn_count,
415 					   &cur_inst->info);
416 	if (err != 0) {
417 		shell_error(sh, "Fail: %d", err);
418 	}
419 
420 	return err;
421 }
422 
cmd_csip_set_coordinator_release_set(const struct shell * sh,size_t argc,char * argv[])423 static int cmd_csip_set_coordinator_release_set(const struct shell *sh,
424 						size_t argc, char *argv[])
425 {
426 	int err;
427 	int conn_count = 0;
428 
429 	if (cur_inst == NULL) {
430 		shell_error(sh, "No set selected");
431 		return -ENOEXEC;
432 	}
433 
434 	for (size_t i = 0; i < ARRAY_SIZE(locked_members); i++) {
435 		if (set_members[i] != NULL) {
436 			locked_members[conn_count++] = set_members[i];
437 		}
438 	}
439 
440 	err = bt_csip_set_coordinator_release(locked_members, conn_count,
441 					      &cur_inst->info);
442 	if (err != 0) {
443 		shell_error(sh, "Fail: %d", err);
444 	}
445 
446 	return err;
447 }
448 
cmd_csip_set_coordinator_lock(const struct shell * sh,size_t argc,char * argv[])449 static int cmd_csip_set_coordinator_lock(const struct shell *sh, size_t argc,
450 					 char *argv[])
451 {
452 	int err;
453 	unsigned long member_index = 0;
454 	const struct bt_csip_set_coordinator_set_member *lock_member[1];
455 
456 	if (cur_inst == NULL) {
457 		shell_error(sh, "No set selected");
458 		return -ENOEXEC;
459 	}
460 
461 	if (argc > 1) {
462 		member_index = shell_strtoul(argv[1], 0, &err);
463 		if (err != 0) {
464 			shell_error(sh, "Could not parse member_index: %d",
465 				    err);
466 
467 			return -ENOEXEC;
468 		}
469 
470 		if (member_index > ARRAY_SIZE(set_members)) {
471 			shell_error(sh, "Invalid member_index: %lu",
472 				    member_index);
473 
474 			return -ENOEXEC;
475 		}
476 	}
477 
478 	lock_member[0] = set_members[member_index];
479 
480 	err = bt_csip_set_coordinator_lock(lock_member, 1, &cur_inst->info);
481 	if (err != 0) {
482 		shell_error(sh, "Fail: %d", err);
483 	}
484 
485 	return err;
486 }
487 
cmd_csip_set_coordinator_release(const struct shell * sh,size_t argc,char * argv[])488 static int cmd_csip_set_coordinator_release(const struct shell *sh, size_t argc,
489 					    char *argv[])
490 {
491 	int err;
492 	unsigned long member_index = 0;
493 	const struct bt_csip_set_coordinator_set_member *lock_member[1];
494 
495 	if (cur_inst == NULL) {
496 		shell_error(sh, "No set selected");
497 		return -ENOEXEC;
498 	}
499 
500 	if (argc > 1) {
501 		member_index = shell_strtoul(argv[1], 0, &err);
502 		if (err != 0) {
503 			shell_error(sh, "Could not parse member_index: %d",
504 				    err);
505 
506 			return -ENOEXEC;
507 		}
508 
509 		if (member_index > ARRAY_SIZE(set_members)) {
510 			shell_error(sh, "Invalid member_index: %lu",
511 				    member_index);
512 
513 			return -ENOEXEC;
514 		}
515 	}
516 
517 	lock_member[0] = set_members[member_index];
518 
519 	err = bt_csip_set_coordinator_release(lock_member, 1, &cur_inst->info);
520 	if (err != 0) {
521 		shell_error(sh, "Fail: %d", err);
522 	}
523 
524 	return err;
525 }
526 #endif /* CONFIG_BT_BONDABLE */
527 
cmd_csip_set_coordinator_ordered_access(const struct shell * sh,size_t argc,char * argv[])528 static int cmd_csip_set_coordinator_ordered_access(const struct shell *sh,
529 						   size_t argc, char *argv[])
530 {
531 	int err;
532 	unsigned long member_count = (unsigned long)ARRAY_SIZE(set_members);
533 	const struct bt_csip_set_coordinator_set_member *members[ARRAY_SIZE(set_members)];
534 
535 	if (argc > 1) {
536 		member_count = shell_strtoul(argv[1], 0, &err);
537 		if (err != 0) {
538 			shell_error(sh, "Could not parse member_count: %d",
539 				    err);
540 
541 			return -ENOEXEC;
542 		}
543 
544 		if (member_count > ARRAY_SIZE(members)) {
545 			shell_error(sh, "Invalid member_count: %lu",
546 				    member_count);
547 
548 			return -ENOEXEC;
549 		}
550 	}
551 
552 	for (size_t i = 0; i < (size_t)member_count; i++) {
553 		members[i] = set_members[i];
554 	}
555 
556 	err = bt_csip_set_coordinator_ordered_access(members,
557 						     ARRAY_SIZE(members),
558 						     &cur_inst->info,
559 						     csip_set_coordinator_oap_cb);
560 	if (err != 0) {
561 		shell_error(sh, "Fail: %d", err);
562 	}
563 
564 	return err;
565 }
566 
cmd_csip_set_coordinator(const struct shell * sh,size_t argc,char ** argv)567 static int cmd_csip_set_coordinator(const struct shell *sh, size_t argc,
568 				    char **argv)
569 {
570 	if (argc > 1) {
571 		shell_error(sh, "%s unknown parameter: %s",
572 			    argv[0], argv[1]);
573 	} else {
574 		shell_error(sh, "%s Missing subcommand", argv[0]);
575 	}
576 
577 	return -ENOEXEC;
578 }
579 
580 SHELL_STATIC_SUBCMD_SET_CREATE(csip_set_coordinator_cmds,
581 	SHELL_CMD_ARG(discover, NULL,
582 		      "Run discover for CSIS on peer device [member_index]",
583 		      cmd_csip_set_coordinator_discover, 1, 1),
584 	SHELL_CMD_ARG(discover_members, NULL,
585 		      "Scan for set members <set_pointer>",
586 		      cmd_csip_set_coordinator_discover_members, 2, 0),
587 #if defined(CONFIG_BT_BONDABLE)
588 	SHELL_CMD_ARG(lock_set, NULL,
589 		      "Lock set",
590 		      cmd_csip_set_coordinator_lock_set, 1, 0),
591 	SHELL_CMD_ARG(release_set, NULL,
592 		      "Release set",
593 		      cmd_csip_set_coordinator_release_set, 1, 0),
594 	SHELL_CMD_ARG(lock, NULL,
595 		      "Lock specific member [member_index]",
596 		      cmd_csip_set_coordinator_lock, 1, 1),
597 	SHELL_CMD_ARG(release, NULL,
598 		      "Release specific member [member_index]",
599 		      cmd_csip_set_coordinator_release, 1, 1),
600 #endif /* CONFIG_BT_BONDABLE */
601 	SHELL_CMD_ARG(ordered_access, NULL,
602 		      "Perform dummy ordered access procedure [member_count]",
603 		      cmd_csip_set_coordinator_ordered_access, 1, 1),
604 	SHELL_SUBCMD_SET_END
605 );
606 
607 SHELL_CMD_ARG_REGISTER(csip_set_coordinator, &csip_set_coordinator_cmds,
608 		       "Bluetooth csip_set_coordinator shell commands",
609 		       cmd_csip_set_coordinator, 1, 1);
610