1 /** @file
2  *  @brief Bluetooth Channel Sounding (CS) shell
3  *
4  */
5 
6 /*
7  * Copyright (c) 2024 Nordic Semiconductor ASA
8  *
9  * SPDX-License-Identifier: Apache-2.0
10  */
11 
12 #include <errno.h>
13 #include <stdlib.h>
14 #include <stdbool.h>
15 #include <stdint.h>
16 #include <string.h>
17 
18 #include <zephyr/bluetooth/hci.h>
19 #include <zephyr/bluetooth/bluetooth.h>
20 #include <zephyr/bluetooth/conn.h>
21 #include <zephyr/bluetooth/hci_types.h>
22 #include <zephyr/bluetooth/iso.h>
23 #include <zephyr/bluetooth/cs.h>
24 #include <zephyr/kernel.h>
25 #include <zephyr/shell/shell.h>
26 #include <zephyr/shell/shell_string_conv.h>
27 #include <zephyr/sys/byteorder.h>
28 #include <zephyr/sys/util.h>
29 
30 #include "common/bt_shell_private.h"
31 #include "host/shell/bt.h"
32 
check_cs_sync_antenna_selection_input(uint16_t input)33 static int check_cs_sync_antenna_selection_input(uint16_t input)
34 {
35 	if (input != BT_LE_CS_ANTENNA_SELECTION_OPT_ONE &&
36 	    input != BT_LE_CS_ANTENNA_SELECTION_OPT_TWO &&
37 	    input != BT_LE_CS_ANTENNA_SELECTION_OPT_THREE &&
38 	    input != BT_LE_CS_ANTENNA_SELECTION_OPT_FOUR &&
39 	    input != BT_LE_CS_ANTENNA_SELECTION_OPT_REPETITIVE &&
40 	    input != BT_LE_CS_ANTENNA_SELECTION_OPT_NO_RECOMMENDATION) {
41 		return -EINVAL;
42 	}
43 
44 	return 0;
45 }
46 
cmd_read_remote_supported_capabilities(const struct shell * sh,size_t argc,char * argv[])47 static int cmd_read_remote_supported_capabilities(const struct shell *sh, size_t argc, char *argv[])
48 {
49 	int err = 0;
50 
51 	if (default_conn == NULL) {
52 		shell_error(sh, "Conn handle error, at least one connection is required.");
53 		return -ENOEXEC;
54 	}
55 
56 	err = bt_le_cs_read_remote_supported_capabilities(default_conn);
57 	if (err) {
58 		shell_error(sh, "bt_le_cs_read_remote_supported_capabilities returned error %d",
59 			    err);
60 		return -ENOEXEC;
61 	}
62 
63 	return 0;
64 }
65 
cmd_set_default_settings(const struct shell * sh,size_t argc,char * argv[])66 static int cmd_set_default_settings(const struct shell *sh, size_t argc, char *argv[])
67 {
68 	int err = 0;
69 	struct bt_le_cs_set_default_settings_param params;
70 	uint16_t antenna_input;
71 	int16_t tx_power_input;
72 
73 	if (default_conn == NULL) {
74 		shell_error(sh, "Conn handle error, at least one connection is required.");
75 		return -ENOEXEC;
76 	}
77 
78 	params.enable_initiator_role = shell_strtobool(argv[1], 10, &err);
79 	if (err) {
80 		shell_help(sh);
81 		shell_error(sh, "Could not parse input 1, Enable initiator role");
82 		return SHELL_CMD_HELP_PRINTED;
83 	}
84 
85 	params.enable_reflector_role = shell_strtobool(argv[2], 10, &err);
86 	if (err) {
87 		shell_help(sh);
88 		shell_error(sh, "Could not parse input 2, Enable reflector role");
89 		return SHELL_CMD_HELP_PRINTED;
90 	}
91 
92 	antenna_input = shell_strtoul(argv[3], 16, &err);
93 	if (err) {
94 		shell_help(sh);
95 		shell_error(sh, "Could not parse input 3, CS_SYNC antenna selection");
96 		return SHELL_CMD_HELP_PRINTED;
97 	}
98 
99 	err = check_cs_sync_antenna_selection_input(antenna_input);
100 	if (err) {
101 		shell_help(sh);
102 		shell_error(sh, "CS_SYNC antenna selection input invalid");
103 		return SHELL_CMD_HELP_PRINTED;
104 	}
105 
106 	tx_power_input = shell_strtol(argv[4], 10, &err);
107 	if (err) {
108 		shell_help(sh);
109 		shell_error(sh, "Could not parse input 4, Max TX power");
110 		return SHELL_CMD_HELP_PRINTED;
111 	}
112 
113 	params.cs_sync_antenna_selection = antenna_input;
114 	params.max_tx_power = tx_power_input;
115 
116 	err = bt_le_cs_set_default_settings(default_conn, &params);
117 	if (err) {
118 		shell_error(sh, "bt_le_cs_set_default_settings returned error %d", err);
119 		return -ENOEXEC;
120 	}
121 
122 	return 0;
123 }
124 
cmd_read_remote_fae_table(const struct shell * sh,size_t argc,char * argv[])125 static int cmd_read_remote_fae_table(const struct shell *sh, size_t argc, char *argv[])
126 {
127 	int err = 0;
128 
129 	if (default_conn == NULL) {
130 		shell_error(sh, "Conn handle error, at least one connection is required.");
131 		return -ENOEXEC;
132 	}
133 
134 	err = bt_le_cs_read_remote_fae_table(default_conn);
135 	if (err) {
136 		shell_error(sh, "bt_le_cs_read_remote_fae_table returned error %d", err);
137 		return -ENOEXEC;
138 	}
139 
140 	return 0;
141 }
142 
143 #if defined(CONFIG_BT_CHANNEL_SOUNDING_TEST)
process_step_data(struct bt_le_cs_subevent_step * step,void * user_data)144 static bool process_step_data(struct bt_le_cs_subevent_step *step, void *user_data)
145 {
146 	bt_shell_print("Subevent results contained step data: ");
147 	bt_shell_print("- Step mode %d\n"
148 		"- Step channel %d\n"
149 		"- Step data hexdump:",
150 		step->mode,
151 		step->channel);
152 	bt_shell_hexdump(step->data, step->data_len);
153 
154 	return true;
155 }
156 
cs_test_subevent_data_cb(struct bt_conn_le_cs_subevent_result * result)157 static void cs_test_subevent_data_cb(struct bt_conn_le_cs_subevent_result *result)
158 {
159 	bt_shell_print("Received subevent results.");
160 	bt_shell_print("Subevent Header:\n"
161 		"- Procedure Counter: %d\n"
162 		"- Frequency Compensation: 0x%04x\n"
163 		"- Reference Power Level: %d\n"
164 		"- Procedure Done Status: 0x%02x\n"
165 		"- Subevent Done Status: 0x%02x\n"
166 		"- Procedure Abort Reason: 0x%02x\n"
167 		"- Subevent Abort Reason: 0x%02x\n"
168 		"- Number of Antenna Paths: %d\n"
169 		"- Number of Steps Reported: %d",
170 		result->header.procedure_counter,
171 		result->header.frequency_compensation,
172 		result->header.reference_power_level,
173 		result->header.procedure_done_status,
174 		result->header.subevent_done_status,
175 		result->header.procedure_abort_reason,
176 		result->header.subevent_abort_reason,
177 		result->header.num_antenna_paths,
178 		result->header.num_steps_reported);
179 
180 	if (result->step_data_buf) {
181 		bt_le_cs_step_data_parse(result->step_data_buf, process_step_data, NULL);
182 	}
183 }
184 
cs_test_end_complete_cb(void)185 static void cs_test_end_complete_cb(void)
186 {
187 	bt_shell_print("CS Test End Complete.");
188 }
189 
cmd_cs_test_simple(const struct shell * sh,size_t argc,char * argv[])190 static int cmd_cs_test_simple(const struct shell *sh, size_t argc, char *argv[])
191 {
192 	int err = 0;
193 	struct bt_le_cs_test_param params;
194 
195 	params.mode = BT_CONN_LE_CS_MAIN_MODE_1_NO_SUB_MODE;
196 	params.main_mode_repetition = 0;
197 	params.mode_0_steps = 2;
198 
199 	params.role = shell_strtoul(argv[1], 16, &err);
200 
201 	if (err) {
202 		shell_help(sh);
203 		shell_error(sh, "Could not parse input 1, Role selection");
204 		return SHELL_CMD_HELP_PRINTED;
205 	}
206 
207 	if (params.role != BT_CONN_LE_CS_ROLE_INITIATOR &&
208 	    params.role != BT_CONN_LE_CS_ROLE_REFLECTOR) {
209 		shell_help(sh);
210 		shell_error(sh, "Role selection input invalid");
211 		return SHELL_CMD_HELP_PRINTED;
212 	}
213 
214 	params.rtt_type = BT_CONN_LE_CS_RTT_TYPE_AA_ONLY;
215 	params.cs_sync_phy = BT_CONN_LE_CS_SYNC_1M_PHY;
216 	params.cs_sync_antenna_selection = BT_LE_CS_TEST_CS_SYNC_ANTENNA_SELECTION_ONE;
217 	params.subevent_len = 3000;
218 	params.subevent_interval = 1;
219 	params.max_num_subevents = 1;
220 	params.transmit_power_level = BT_HCI_OP_LE_CS_TEST_MAXIMIZE_TX_POWER;
221 	params.t_ip1_time = 80;
222 	params.t_ip2_time = 80;
223 	params.t_fcs_time = 120;
224 	params.t_pm_time = 20;
225 	params.t_sw_time = 0;
226 	params.tone_antenna_config_selection = BT_LE_CS_TONE_ANTENNA_CONFIGURATION_A1_B1;
227 	params.initiator_snr_control = BT_LE_CS_SNR_CONTROL_NOT_USED;
228 	params.reflector_snr_control = BT_LE_CS_SNR_CONTROL_NOT_USED;
229 	params.drbg_nonce = 0x1234;
230 	params.override_config = 0;
231 	params.override_config_0.channel_map_repetition = 1;
232 	memset(params.override_config_0.not_set.channel_map, 0,
233 	       sizeof(params.override_config_0.not_set.channel_map));
234 	params.override_config_0.not_set.channel_map[1] = 0xFF;
235 	params.override_config_0.not_set.channel_map[7] = 0xFF;
236 	params.override_config_0.not_set.channel_map[8] = 0xFF;
237 	params.override_config_0.not_set.channel_selection_type = BT_CONN_LE_CS_CHSEL_TYPE_3B;
238 	params.override_config_0.not_set.ch3c_shape = BT_CONN_LE_CS_CH3C_SHAPE_HAT;
239 	params.override_config_0.not_set.ch3c_jump = 0x2;
240 
241 	struct bt_le_cs_test_cb cs_test_cb = {
242 		.le_cs_test_subevent_data_available = cs_test_subevent_data_cb,
243 		.le_cs_test_end_complete = cs_test_end_complete_cb,
244 	};
245 
246 	err = bt_le_cs_test_cb_register(cs_test_cb);
247 	if (err) {
248 		shell_error(sh, "bt_le_cs_test_cb_register returned error %d", err);
249 		return -ENOEXEC;
250 	}
251 
252 	err = bt_le_cs_start_test(&params);
253 	if (err) {
254 		shell_error(sh, "bt_le_cs_start_test returned error %d", err);
255 		return -ENOEXEC;
256 	}
257 
258 	return 0;
259 }
260 #endif /* CONFIG_BT_CHANNEL_SOUNDING_TEST */
261 
cmd_remove_config(const struct shell * sh,size_t argc,char * argv[])262 static int cmd_remove_config(const struct shell *sh, size_t argc, char *argv[])
263 {
264 	int err = 0;
265 
266 	if (default_conn == NULL) {
267 		shell_error(sh, "Conn handle error, at least one connection is required.");
268 		return -ENOEXEC;
269 	}
270 
271 	uint8_t config_id = strtoul(argv[1], NULL, 10);
272 
273 	err = bt_le_cs_remove_config(default_conn, config_id);
274 	if (err) {
275 		shell_error(sh, "bt_le_cs_remove_config returned error %d", err);
276 		return -ENOEXEC;
277 	}
278 
279 	return 0;
280 }
281 
cmd_create_config(const struct shell * sh,size_t argc,char * argv[])282 static int cmd_create_config(const struct shell *sh, size_t argc, char *argv[])
283 {
284 	int err = 0;
285 	enum bt_le_cs_create_config_context context;
286 	struct bt_le_cs_create_config_params params;
287 
288 	if (default_conn == NULL) {
289 		shell_error(sh, "Conn handle error, at least one connection is required.");
290 		return -ENOEXEC;
291 	}
292 
293 	params.id = strtoul(argv[1], NULL, 10);
294 	if (!strcmp(argv[2], "local-only")) {
295 		context = BT_LE_CS_CREATE_CONFIG_CONTEXT_LOCAL_ONLY;
296 	} else if (!strcmp(argv[2], "local-only")) {
297 		context = BT_LE_CS_CREATE_CONFIG_CONTEXT_LOCAL_AND_REMOTE;
298 	} else {
299 		shell_error(sh, "Invalid context: %s", argv[2]);
300 		shell_help(sh);
301 		return SHELL_CMD_HELP_PRINTED;
302 	}
303 
304 	if (!strcmp(argv[3], "initiator")) {
305 		params.role = BT_CONN_LE_CS_ROLE_INITIATOR;
306 	} else if (!strcmp(argv[3], "reflector")) {
307 		params.role = BT_CONN_LE_CS_ROLE_REFLECTOR;
308 	} else {
309 		shell_error(sh, "Invalid role: %s", argv[3]);
310 		shell_help(sh);
311 		return SHELL_CMD_HELP_PRINTED;
312 	}
313 
314 	/* Set the default values */
315 	params.mode = BT_CONN_LE_CS_MAIN_MODE_2_SUB_MODE_1;
316 	params.min_main_mode_steps = 0x05;
317 	params.max_main_mode_steps = 0x0A;
318 	params.main_mode_repetition = 0;
319 	params.mode_0_steps = 1;
320 	params.rtt_type = BT_CONN_LE_CS_RTT_TYPE_AA_ONLY;
321 	params.cs_sync_phy = BT_CONN_LE_CS_SYNC_2M_PHY;
322 	params.channel_map_repetition = 1;
323 	params.channel_selection_type = BT_CONN_LE_CS_CHSEL_TYPE_3B;
324 	params.ch3c_shape = BT_CONN_LE_CS_CH3C_SHAPE_HAT;
325 	params.ch3c_jump = 2;
326 
327 	bt_le_cs_set_valid_chmap_bits(params.channel_map);
328 
329 	for (int j = 4; j < argc; j++) {
330 		if (!strcmp(argv[j], "rtt-none")) {
331 			params.mode = BT_CONN_LE_CS_MAIN_MODE_1_NO_SUB_MODE;
332 		} else if (!strcmp(argv[j], "pbr-none")) {
333 			params.mode = BT_CONN_LE_CS_MAIN_MODE_2_NO_SUB_MODE;
334 		} else if (!strcmp(argv[j], "both-none")) {
335 			params.mode = BT_CONN_LE_CS_MAIN_MODE_3_NO_SUB_MODE;
336 		} else if (!strcmp(argv[j], "pbr-rtt")) {
337 			params.mode = BT_CONN_LE_CS_MAIN_MODE_2_SUB_MODE_1;
338 		} else if (!strcmp(argv[j], "pbr-both")) {
339 			params.mode = BT_CONN_LE_CS_MAIN_MODE_2_SUB_MODE_3;
340 		} else if (!strcmp(argv[j], "both-pbr")) {
341 			params.mode = BT_CONN_LE_CS_MAIN_MODE_3_SUB_MODE_2;
342 		} else if (!strcmp(argv[j], "steps")) {
343 			if (++j == argc) {
344 				shell_help(sh);
345 				return SHELL_CMD_HELP_PRINTED;
346 			}
347 
348 			params.min_main_mode_steps = strtoul(argv[j], NULL, 10);
349 			if (++j == argc) {
350 				shell_help(sh);
351 				return SHELL_CMD_HELP_PRINTED;
352 			}
353 
354 			params.max_main_mode_steps = strtoul(argv[j], NULL, 10);
355 			if (++j == argc) {
356 				shell_help(sh);
357 				return SHELL_CMD_HELP_PRINTED;
358 			}
359 
360 			params.mode_0_steps = strtoul(argv[j], NULL, 10);
361 		} else if (!strcmp(argv[j], "aa-only")) {
362 			params.rtt_type = BT_CONN_LE_CS_RTT_TYPE_AA_ONLY;
363 		} else if (!strcmp(argv[j], "32b-sound")) {
364 			params.rtt_type = BT_CONN_LE_CS_RTT_TYPE_32_BIT_SOUNDING;
365 		} else if (!strcmp(argv[j], "96b-sound")) {
366 			params.rtt_type = BT_CONN_LE_CS_RTT_TYPE_96_BIT_SOUNDING;
367 		} else if (!strcmp(argv[j], "32b-rand")) {
368 			params.rtt_type = BT_CONN_LE_CS_RTT_TYPE_32_BIT_RANDOM;
369 		} else if (!strcmp(argv[j], "64b-rand")) {
370 			params.rtt_type = BT_CONN_LE_CS_RTT_TYPE_64_BIT_RANDOM;
371 		} else if (!strcmp(argv[j], "96b-rand")) {
372 			params.rtt_type = BT_CONN_LE_CS_RTT_TYPE_96_BIT_RANDOM;
373 		} else if (!strcmp(argv[j], "128b-rand")) {
374 			params.rtt_type = BT_CONN_LE_CS_RTT_TYPE_128_BIT_RANDOM;
375 		} else if (!strcmp(argv[j], "phy-1m")) {
376 			params.cs_sync_phy = BT_CONN_LE_CS_SYNC_1M_PHY;
377 		} else if (!strcmp(argv[j], "phy-2m")) {
378 			params.cs_sync_phy = BT_CONN_LE_CS_SYNC_2M_PHY;
379 		} else if (!strcmp(argv[j], "phy-2m-2b")) {
380 			params.cs_sync_phy = BT_CONN_LE_CS_SYNC_2M_2BT_PHY;
381 		} else if (!strcmp(argv[j], "chmap-rep")) {
382 			if (++j == argc) {
383 				shell_help(sh);
384 				return SHELL_CMD_HELP_PRINTED;
385 			}
386 
387 			params.channel_map_repetition = strtoul(argv[j], NULL, 10);
388 		} else if (!strcmp(argv[j], "hat-shape")) {
389 			params.ch3c_shape = BT_CONN_LE_CS_CH3C_SHAPE_HAT;
390 		} else if (!strcmp(argv[j], "x-shape")) {
391 			params.ch3c_shape = BT_CONN_LE_CS_CH3C_SHAPE_X;
392 		} else if (!strcmp(argv[j], "chsel-3b")) {
393 			params.channel_selection_type = BT_CONN_LE_CS_CHSEL_TYPE_3B;
394 		} else if (!strcmp(argv[j], "chsel-3c")) {
395 			params.channel_selection_type = BT_CONN_LE_CS_CHSEL_TYPE_3C;
396 		} else if (!strcmp(argv[j], "ch3c-jump")) {
397 			if (++j == argc) {
398 				shell_help(sh);
399 				return SHELL_CMD_HELP_PRINTED;
400 			}
401 
402 			params.ch3c_jump = strtoul(argv[j], NULL, 10);
403 		} else if (!strcmp(argv[j], "chmap")) {
404 			if (++j == argc) {
405 				shell_help(sh);
406 				return SHELL_CMD_HELP_PRINTED;
407 			}
408 
409 			if (hex2bin(argv[j], strlen(argv[j]), params.channel_map, 10) == 0) {
410 				shell_error(sh, "Invalid channel map");
411 				return -ENOEXEC;
412 			}
413 
414 			sys_mem_swap(params.channel_map, 10);
415 		} else {
416 			shell_help(sh);
417 			return SHELL_CMD_HELP_PRINTED;
418 		}
419 	}
420 
421 	err = bt_le_cs_create_config(default_conn, &params, context);
422 	if (err) {
423 		shell_error(sh, "bt_le_cs_create_config returned error %d", err);
424 		return -ENOEXEC;
425 	}
426 
427 	return 0;
428 }
429 
430 #if defined(CONFIG_BT_CHANNEL_SOUNDING_TEST)
cmd_cs_stop_test(const struct shell * sh,size_t argc,char * argv[])431 static int cmd_cs_stop_test(const struct shell *sh, size_t argc, char *argv[])
432 {
433 	int err = 0;
434 
435 	err = bt_le_cs_stop_test();
436 	if (err) {
437 		shell_error(sh, "bt_cs_stop_test returned error %d", err);
438 		return -ENOEXEC;
439 	}
440 
441 	return 0;
442 }
443 #endif /* CONFIG_BT_CHANNEL_SOUNDING_TEST */
444 
cmd_read_local_supported_capabilities(const struct shell * sh,size_t argc,char * argv[])445 static int cmd_read_local_supported_capabilities(const struct shell *sh, size_t argc, char *argv[])
446 {
447 	int err = 0;
448 
449 	struct bt_conn_le_cs_capabilities params;
450 
451 	err = bt_le_cs_read_local_supported_capabilities(&params);
452 
453 	if (err) {
454 		shell_error(sh, "bt_le_cs_read_local_supported_capabilities returned error %d",
455 			    err);
456 
457 		return -ENOEXEC;
458 	}
459 
460 	shell_print(
461 		sh,
462 		"Local channel sounding supported capabilities:\n"
463 		"- Num CS configurations: %d\n"
464 		"- Max consecutive CS procedures: %d\n"
465 		"- Num antennas supported: %d\n"
466 		"- Max antenna paths supported: %d\n"
467 		"- Initiator role supported: %s\n"
468 		"- Reflector role supported: %s\n"
469 		"- Mode 3 supported: %s\n"
470 		"- RTT AA only supported: %s\n"
471 		"- RTT AA only is 10ns precise: %s\n"
472 		"- RTT AA only N: %d\n"
473 		"- RTT sounding supported: %s\n"
474 		"- RTT sounding is 10ns precise: %s\n"
475 		"- RTT sounding N: %d\n"
476 		"- RTT random payload supported: %s\n"
477 		"- RTT random payload is 10ns precise: %s\n"
478 		"- RTT random payload N: %d\n"
479 		"- Phase-based NADM with sounding sequences supported: %s\n"
480 		"- Phase-based NADM with random sequences supported: %s\n"
481 		"- CS Sync 2M PHY supported: %s\n"
482 		"- CS Sync 2M 2BT PHY supported: %s\n"
483 		"- CS without transmitter FAE supported: %s\n"
484 		"- Channel selection algorithm #3c supported: %s\n"
485 		"- Phase-based ranging from RTT sounding sequence supported: %s\n"
486 		"- T_IP1 times supported: 0x%04x\n"
487 		"- T_IP2 times supported: 0x%04x\n"
488 		"- T_FCS times supported: 0x%04x\n"
489 		"- T_PM times supported: 0x%04x\n"
490 		"- T_SW time supported: %d us\n"
491 		"- TX SNR capability: 0x%02x",
492 		params.num_config_supported, params.max_consecutive_procedures_supported,
493 		params.num_antennas_supported, params.max_antenna_paths_supported,
494 		params.initiator_supported ? "Yes" : "No",
495 		params.reflector_supported ? "Yes" : "No", params.mode_3_supported ? "Yes" : "No",
496 		params.rtt_aa_only_precision == BT_CONN_LE_CS_RTT_AA_ONLY_NOT_SUPP ? "No" : "Yes",
497 		params.rtt_aa_only_precision == BT_CONN_LE_CS_RTT_AA_ONLY_10NS ? "Yes" : "No",
498 		params.rtt_aa_only_n,
499 		params.rtt_sounding_precision == BT_CONN_LE_CS_RTT_SOUNDING_NOT_SUPP ? "No" : "Yes",
500 		params.rtt_sounding_precision == BT_CONN_LE_CS_RTT_SOUNDING_10NS ? "Yes" : "No",
501 		params.rtt_sounding_n,
502 		params.rtt_random_payload_precision == BT_CONN_LE_CS_RTT_RANDOM_PAYLOAD_NOT_SUPP
503 			? "No"
504 			: "Yes",
505 		params.rtt_random_payload_precision == BT_CONN_LE_CS_RTT_RANDOM_PAYLOAD_10NS ? "Yes"
506 											     : "No",
507 		params.rtt_random_payload_n,
508 		params.phase_based_nadm_sounding_supported ? "Yes" : "No",
509 		params.phase_based_nadm_random_supported ? "Yes" : "No",
510 		params.cs_sync_2m_phy_supported ? "Yes" : "No",
511 		params.cs_sync_2m_2bt_phy_supported ? "Yes" : "No",
512 		params.cs_without_fae_supported ? "Yes" : "No",
513 		params.chsel_alg_3c_supported ? "Yes" : "No",
514 		params.pbr_from_rtt_sounding_seq_supported ? "Yes" : "No",
515 		params.t_ip1_times_supported, params.t_ip2_times_supported,
516 		params.t_fcs_times_supported, params.t_pm_times_supported, params.t_sw_time,
517 		params.tx_snr_capability);
518 
519 	return 0;
520 }
521 
cmd_write_cached_remote_supported_capabilities(const struct shell * sh,size_t argc,char * argv[])522 static int cmd_write_cached_remote_supported_capabilities(const struct shell *sh, size_t argc,
523 							  char *argv[])
524 {
525 	int err = 0;
526 
527 	if (default_conn == NULL) {
528 		shell_error(sh, "Conn handle error, at least one connection is required.");
529 		return -ENOEXEC;
530 	}
531 
532 	struct bt_conn_le_cs_capabilities params;
533 
534 	params.num_config_supported = 1;
535 	params.max_consecutive_procedures_supported = 0;
536 	params.num_antennas_supported = 1;
537 	params.max_antenna_paths_supported = 1;
538 	params.initiator_supported = true;
539 	params.reflector_supported = true;
540 	params.mode_3_supported = true;
541 	params.rtt_aa_only_precision = BT_CONN_LE_CS_RTT_AA_ONLY_10NS;
542 	params.rtt_sounding_precision = BT_CONN_LE_CS_RTT_SOUNDING_10NS;
543 	params.rtt_random_payload_precision = BT_CONN_LE_CS_RTT_RANDOM_PAYLOAD_10NS;
544 	params.rtt_aa_only_n = 5;
545 	params.rtt_sounding_n = 6;
546 	params.rtt_random_payload_n = 7;
547 	params.phase_based_nadm_sounding_supported = true;
548 	params.phase_based_nadm_random_supported = true;
549 	params.cs_sync_2m_phy_supported = true;
550 	params.cs_sync_2m_2bt_phy_supported = true;
551 	params.chsel_alg_3c_supported = true;
552 	params.cs_without_fae_supported = true;
553 	params.pbr_from_rtt_sounding_seq_supported = false;
554 	params.t_ip1_times_supported = BT_HCI_LE_CS_T_IP1_TIME_10US_MASK;
555 	params.t_ip2_times_supported = BT_HCI_LE_CS_T_IP2_TIME_10US_MASK;
556 	params.t_fcs_times_supported = BT_HCI_LE_CS_T_FCS_TIME_100US_MASK;
557 	params.t_sw_time = 0x04;
558 	params.tx_snr_capability = BT_HCI_LE_CS_TX_SNR_CAPABILITY_18DB_MASK;
559 
560 	err = bt_le_cs_write_cached_remote_supported_capabilities(default_conn, &params);
561 
562 	if (err) {
563 		shell_error(sh, "bt_le_cs_set_channel_classification returned error %d", err);
564 		return -ENOEXEC;
565 	}
566 
567 	return 0;
568 }
569 
cmd_security_enable(const struct shell * sh,size_t argc,char * argv[])570 static int cmd_security_enable(const struct shell *sh, size_t argc, char *argv[])
571 {
572 	int err = 0;
573 
574 	if (default_conn == NULL) {
575 		shell_error(sh, "Conn handle error, at least one connection is required.");
576 		return -ENOEXEC;
577 	}
578 
579 	err = bt_le_cs_security_enable(default_conn);
580 
581 	if (err) {
582 		shell_error(sh, "bt_le_cs_security_enable returned error %d", err);
583 		return -ENOEXEC;
584 	}
585 
586 	return 0;
587 }
588 
cmd_set_channel_classification(const struct shell * sh,size_t argc,char * argv[])589 static int cmd_set_channel_classification(const struct shell *sh, size_t argc, char *argv[])
590 {
591 	int err = 0;
592 
593 	if (default_conn == NULL) {
594 		shell_error(sh, "Conn handle error, at least one connection is required.");
595 		return -ENOEXEC;
596 	}
597 
598 	uint8_t channel_classification[10];
599 
600 	for (int i = 0; i < 10; i++) {
601 		channel_classification[i] = shell_strtoul(argv[1 + i], 16, &err);
602 
603 		if (err) {
604 			shell_help(sh);
605 			shell_error(sh, "Could not parse input %d, Channel Classification[%d]", i,
606 				    i);
607 
608 			return SHELL_CMD_HELP_PRINTED;
609 		}
610 	}
611 
612 	err = bt_le_cs_set_channel_classification(channel_classification);
613 
614 	if (err) {
615 		shell_error(sh, "bt_le_cs_set_channel_classification returned error %d", err);
616 		return -ENOEXEC;
617 	}
618 
619 	return 0;
620 }
621 
cmd_set_procedure_parameters(const struct shell * sh,size_t argc,char * argv[])622 static int cmd_set_procedure_parameters(const struct shell *sh, size_t argc, char *argv[])
623 {
624 	int err = 0;
625 
626 	if (default_conn == NULL) {
627 		shell_error(sh, "Conn handle error, at least one connection is required.");
628 		return -ENOEXEC;
629 	}
630 
631 	struct bt_le_cs_set_procedure_parameters_param params;
632 
633 	params.config_id = 0;
634 	params.max_procedure_len = 1000;
635 	params.min_procedure_interval = 5;
636 	params.max_procedure_interval = 5000;
637 	params.max_procedure_count = 1;
638 	params.min_subevent_len = 5000;
639 	params.max_subevent_len = 4000000;
640 	params.tone_antenna_config_selection = BT_LE_CS_TONE_ANTENNA_CONFIGURATION_A1_B1;
641 	params.phy = 0x01;
642 	params.tx_power_delta = 0x80;
643 	params.preferred_peer_antenna = 1;
644 	params.snr_control_initiator = BT_LE_CS_SNR_CONTROL_18dB;
645 	params.snr_control_reflector = BT_LE_CS_SNR_CONTROL_18dB;
646 
647 	err = bt_le_cs_set_procedure_parameters(default_conn, &params);
648 
649 	if (err) {
650 		shell_error(sh, "bt_le_cs_set_procedure_parameters returned error %d", err);
651 		return -ENOEXEC;
652 	}
653 
654 	return 0;
655 }
656 
cmd_procedure_enable(const struct shell * sh,size_t argc,char * argv[])657 static int cmd_procedure_enable(const struct shell *sh, size_t argc, char *argv[])
658 {
659 	int err = 0;
660 
661 	if (default_conn == NULL) {
662 		shell_error(sh, "Conn handle error, at least one connection is required.");
663 		return -ENOEXEC;
664 	}
665 
666 	struct bt_le_cs_procedure_enable_param params;
667 
668 	params.config_id = shell_strtoul(argv[1], 16, &err);
669 
670 	if (err) {
671 		shell_help(sh);
672 		shell_error(sh, "Could not parse input 1, Config ID");
673 		return SHELL_CMD_HELP_PRINTED;
674 	}
675 
676 	params.enable = shell_strtoul(argv[2], 16, &err);
677 
678 	if (err) {
679 		shell_help(sh);
680 		shell_error(sh, "Could not parse input 2, Enable");
681 		return SHELL_CMD_HELP_PRINTED;
682 	}
683 
684 	err = bt_le_cs_procedure_enable(default_conn, &params);
685 
686 	if (err) {
687 		shell_error(sh, "bt_le_cs_procedure_enable returned error %d", err);
688 		return -ENOEXEC;
689 	}
690 
691 	return 0;
692 }
693 
694 SHELL_STATIC_SUBCMD_SET_CREATE(
695 	cs_cmds,
696 	SHELL_CMD_ARG(read_remote_supported_capabilities, NULL, "<None>",
697 		      cmd_read_remote_supported_capabilities, 1, 0),
698 	SHELL_CMD_ARG(
699 		set_default_settings, NULL,
700 		"<Enable initiator role: true, false> <Enable reflector role: true, false> "
701 		" <CS_SYNC antenna selection: 0x01 - 0x04, 0xFE, 0xFF> <Max TX power: -127 - 20>",
702 		cmd_set_default_settings, 5, 0),
703 	SHELL_CMD_ARG(read_remote_fae_table, NULL, "<None>", cmd_read_remote_fae_table, 1, 0),
704 #if defined(CONFIG_BT_CHANNEL_SOUNDING_TEST)
705 	SHELL_CMD_ARG(start_simple_cs_test, NULL, "<Role selection (initiator, reflector): 0, 1>",
706 		      cmd_cs_test_simple, 2, 0),
707 	SHELL_CMD_ARG(stop_cs_test, NULL, "<None>", cmd_cs_stop_test, 1, 0),
708 #endif /* CONFIG_BT_CHANNEL_SOUNDING_TEST */
709 	SHELL_CMD_ARG(
710 		create_config, NULL,
711 		"<id> <context: local-only, local-remote> <role: initiator, reflector> "
712 		"[rtt-none, pbr-none, both-none, pbr-rtt, pbr-both, both-pbr] [steps <min> "
713 		"<max> <mode-0>] [aa-only, 32b-sound, 96b-sound, 32b-rand, 64b-rand, 96b-rand, "
714 		"128b-rand] [phy-1m, phy-2m, phy-2m-2b] [chmap-rep <rep>] [hat-shape, x-shape] "
715 		"[ch3c-jump <jump>] [chmap <XXXXXXXXXXXXXXXX>] (78-0) [chsel-3b, chsel-3c]",
716 		cmd_create_config, 4, 15),
717 	SHELL_CMD_ARG(remove_config, NULL, "<id>", cmd_remove_config, 2, 0),
718 	SHELL_CMD_ARG(read_local_supported_capabilities, NULL, "<None>",
719 		      cmd_read_local_supported_capabilities, 1, 0),
720 	SHELL_CMD_ARG(write_cached_remote_supported_capabilities, NULL, "<None>",
721 		      cmd_write_cached_remote_supported_capabilities, 1, 0),
722 	SHELL_CMD_ARG(security_enable, NULL, "<None>", cmd_security_enable, 1, 0),
723 	SHELL_CMD_ARG(set_channel_classification, NULL,
724 		      "<Byte 0> <Byte 1> <Byte 2> <Byte 3> "
725 		      "<Byte 4> <Byte 5> <Byte 6> <Byte 7> <Byte 8> <Byte 9>",
726 		      cmd_set_channel_classification, 11, 0),
727 	SHELL_CMD_ARG(set_procedure_parameters, NULL, "<None>", cmd_set_procedure_parameters, 1, 0),
728 	SHELL_CMD_ARG(procedure_enable, NULL, "<Config ID: 0 to 3> <Enable: 0, 1>",
729 		      cmd_procedure_enable, 3, 0),
730 	SHELL_SUBCMD_SET_END);
731 
cmd_cs(const struct shell * sh,size_t argc,char ** argv)732 static int cmd_cs(const struct shell *sh, size_t argc, char **argv)
733 {
734 	if (argc == 1) {
735 		shell_help(sh);
736 
737 		return SHELL_CMD_HELP_PRINTED;
738 	}
739 
740 	shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
741 
742 	return -EINVAL;
743 }
744 
745 SHELL_CMD_ARG_REGISTER(cs, &cs_cmds, "Bluetooth CS shell commands", cmd_cs, 1, 1);
746