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