1 /**
2  * @file
3  * @brief Shell APIs for Bluetooth CSIP set member
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 <string.h>
16 #include <sys/types.h>
17 
18 #include <zephyr/autoconf.h>
19 #include <zephyr/bluetooth/addr.h>
20 #include <zephyr/bluetooth/audio/csip.h>
21 #include <zephyr/bluetooth/bluetooth.h>
22 #include <zephyr/bluetooth/gap.h>
23 #include <zephyr/bluetooth/gatt.h>
24 #include <zephyr/kernel.h>
25 #include <zephyr/shell/shell_string_conv.h>
26 #include <zephyr/sys/__assert.h>
27 #include <zephyr/sys/util.h>
28 #include <zephyr/sys/util_macro.h>
29 #include <zephyr/types.h>
30 #include <zephyr/shell/shell.h>
31 
32 #include "shell/bt.h"
33 
34 extern const struct shell *ctx_shell;
35 struct bt_csip_set_member_svc_inst *svc_inst;
36 static uint8_t sirk_read_rsp = BT_CSIP_READ_SIRK_REQ_RSP_ACCEPT;
37 
locked_cb(struct bt_conn * conn,struct bt_csip_set_member_svc_inst * inst,bool locked)38 static void locked_cb(struct bt_conn *conn,
39 		      struct bt_csip_set_member_svc_inst *inst,
40 		      bool locked)
41 {
42 	if (conn == NULL) {
43 		shell_error(ctx_shell, "Server %s the device",
44 			    locked ? "locked" : "released");
45 	} else {
46 		char addr[BT_ADDR_LE_STR_LEN];
47 
48 		conn_addr_str(conn, addr, sizeof(addr));
49 
50 		shell_print(ctx_shell, "Client %s %s the device",
51 			    addr, locked ? "locked" : "released");
52 	}
53 }
54 
sirk_read_req_cb(struct bt_conn * conn,struct bt_csip_set_member_svc_inst * inst)55 static uint8_t sirk_read_req_cb(struct bt_conn *conn,
56 				struct bt_csip_set_member_svc_inst *inst)
57 {
58 	char addr[BT_ADDR_LE_STR_LEN];
59 	static const char *const rsp_strings[] = {
60 		"Accept", "Accept Enc", "Reject", "OOB only"
61 	};
62 
63 	conn_addr_str(conn, addr, sizeof(addr));
64 
65 	shell_print(ctx_shell, "Client %s requested to read the sirk. "
66 		    "Responding with %s", addr, rsp_strings[sirk_read_rsp]);
67 
68 	return sirk_read_rsp;
69 }
70 
71 static struct bt_csip_set_member_cb csip_set_member_cbs = {
72 	.lock_changed = locked_cb,
73 	.sirk_read_req = sirk_read_req_cb,
74 };
75 
cmd_csip_set_member_register(const struct shell * sh,size_t argc,char ** argv)76 static int cmd_csip_set_member_register(const struct shell *sh, size_t argc, char **argv)
77 {
78 	int err;
79 	struct bt_csip_set_member_register_param param = {
80 		.set_size = 2,
81 		.rank = 1,
82 		.lockable = true,
83 		/* Using the CSIS test sample SIRK */
84 		.sirk = { 0xcd, 0xcc, 0x72, 0xdd, 0x86, 0x8c, 0xcd, 0xce,
85 			  0x22, 0xfd, 0xa1, 0x21, 0x09, 0x7d, 0x7d, 0x45 },
86 		.cb = &csip_set_member_cbs,
87 	};
88 
89 	for (size_t argn = 1; argn < argc; argn++) {
90 		const char *arg = argv[argn];
91 		if (strcmp(arg, "size") == 0) {
92 			unsigned long set_size;
93 
94 			argn++;
95 			if (argn == argc) {
96 				shell_help(sh);
97 				return SHELL_CMD_HELP_PRINTED;
98 			}
99 
100 			set_size = shell_strtoul(argv[argn], 0, &err);
101 			if (err != 0) {
102 				shell_error(sh, "Could not parse set_size: %d",
103 					    err);
104 
105 				return -ENOEXEC;
106 			}
107 
108 			if (set_size > UINT8_MAX) {
109 				shell_error(sh, "Invalid set_size: %lu",
110 					    set_size);
111 
112 				return -ENOEXEC;
113 			}
114 
115 			param.set_size = set_size;
116 		} else if (strcmp(arg, "rank") == 0) {
117 			unsigned long rank;
118 
119 			argn++;
120 			if (argn == argc) {
121 				shell_help(sh);
122 				return SHELL_CMD_HELP_PRINTED;
123 			}
124 
125 			rank = shell_strtoul(argv[argn], 0, &err);
126 			if (err != 0) {
127 				shell_error(sh, "Could not parse rank: %d",
128 					    err);
129 
130 				return -ENOEXEC;
131 			}
132 
133 			if (rank > UINT8_MAX) {
134 				shell_error(sh, "Invalid rank: %lu", rank);
135 
136 				return -ENOEXEC;
137 			}
138 
139 			param.rank = rank;
140 		} else if (strcmp(arg, "not-lockable") == 0) {
141 			param.lockable = false;
142 		} else if (strcmp(arg, "sirk") == 0) {
143 			size_t len;
144 
145 			argn++;
146 			if (argn == argc) {
147 				shell_help(sh);
148 				return SHELL_CMD_HELP_PRINTED;
149 			}
150 
151 			len = hex2bin(argv[argn], strlen(argv[argn]), param.sirk,
152 				      sizeof(param.sirk));
153 			if (len == 0) {
154 				shell_error(sh, "Could not parse SIRK");
155 				return -ENOEXEC;
156 			}
157 		} else {
158 			shell_help(sh);
159 			return SHELL_CMD_HELP_PRINTED;
160 		}
161 	}
162 
163 	err = bt_csip_set_member_register(&param, &svc_inst);
164 	if (err != 0) {
165 		shell_error(sh, "Could not register CSIP: %d", err);
166 		return err;
167 	}
168 
169 	return 0;
170 }
171 
cmd_csip_set_member_sirk(const struct shell * sh,size_t argc,char * argv[])172 static int cmd_csip_set_member_sirk(const struct shell *sh, size_t argc, char *argv[])
173 {
174 	uint8_t sirk[BT_CSIP_SIRK_SIZE];
175 	size_t len;
176 	int err;
177 
178 	if (svc_inst == NULL) {
179 		shell_error(sh, "CSIS not registered yet");
180 
181 		return -ENOEXEC;
182 	}
183 
184 	len = hex2bin(argv[1], strlen(argv[1]), sirk, sizeof(sirk));
185 	if (len != sizeof(sirk)) {
186 		shell_error(sh, "Invalid SIRK Length: %zu", len);
187 
188 		return -ENOEXEC;
189 	}
190 
191 	err = bt_csip_set_member_sirk(svc_inst, sirk);
192 	if (err != 0) {
193 		shell_error(sh, "Failed to set SIRK: %d", err);
194 		return -ENOEXEC;
195 	}
196 
197 	shell_print(sh, "SIRK updated");
198 
199 	return 0;
200 }
201 
cmd_csip_set_member_get_sirk(const struct shell * sh,size_t argc,char * argv[])202 static int cmd_csip_set_member_get_sirk(const struct shell *sh, size_t argc, char *argv[])
203 {
204 	uint8_t sirk[BT_CSIP_SIRK_SIZE];
205 	int err;
206 
207 	if (svc_inst == NULL) {
208 		shell_error(sh, "CSIS not registered yet");
209 
210 		return -ENOEXEC;
211 	}
212 
213 	err = bt_csip_set_member_get_sirk(svc_inst, sirk);
214 	if (err != 0) {
215 		shell_error(sh, "Failed to get SIRK: %d", err);
216 		return -ENOEXEC;
217 	}
218 
219 	shell_print(sh, "SIRK");
220 	shell_hexdump(sh, sirk, sizeof(sirk));
221 
222 	return 0;
223 }
224 
cmd_csip_set_member_lock(const struct shell * sh,size_t argc,char * argv[])225 static int cmd_csip_set_member_lock(const struct shell *sh, size_t argc, char *argv[])
226 {
227 	int err;
228 
229 	err = bt_csip_set_member_lock(svc_inst, true, false);
230 	if (err != 0) {
231 		shell_error(sh, "Failed to set lock: %d", err);
232 		return -ENOEXEC;
233 	}
234 
235 	shell_print(sh, "Set locked");
236 
237 	return 0;
238 }
239 
cmd_csip_set_member_release(const struct shell * sh,size_t argc,char * argv[])240 static int cmd_csip_set_member_release(const struct shell *sh, size_t argc,
241 			    char *argv[])
242 {
243 	bool force = false;
244 	int err;
245 
246 	if (argc > 1) {
247 		if (strcmp(argv[1], "force") == 0) {
248 			force = true;
249 		} else {
250 			shell_error(sh, "Unknown parameter: %s", argv[1]);
251 			return -ENOEXEC;
252 		}
253 	}
254 
255 	err = bt_csip_set_member_lock(svc_inst, false, force);
256 
257 	if (err != 0) {
258 		shell_error(sh, "Failed to release lock: %d", err);
259 		return -ENOEXEC;
260 	}
261 
262 	shell_print(sh, "Set released");
263 
264 	return 0;
265 }
266 
cmd_csip_set_member_sirk_rsp(const struct shell * sh,size_t argc,char * argv[])267 static int cmd_csip_set_member_sirk_rsp(const struct shell *sh, size_t argc, char *argv[])
268 {
269 	if (strcmp(argv[1], "accept") == 0) {
270 		sirk_read_rsp = BT_CSIP_READ_SIRK_REQ_RSP_ACCEPT;
271 	} else if (strcmp(argv[1], "accept_enc") == 0) {
272 		sirk_read_rsp = BT_CSIP_READ_SIRK_REQ_RSP_ACCEPT_ENC;
273 	} else if (strcmp(argv[1], "reject") == 0) {
274 		sirk_read_rsp = BT_CSIP_READ_SIRK_REQ_RSP_REJECT;
275 	} else if (strcmp(argv[1], "oob") == 0) {
276 		sirk_read_rsp = BT_CSIP_READ_SIRK_REQ_RSP_OOB_ONLY;
277 	} else {
278 		shell_error(sh, "Unknown parameter: %s", argv[1]);
279 		return -ENOEXEC;
280 	}
281 
282 	return 0;
283 }
284 
cmd_csip_set_member(const struct shell * sh,size_t argc,char ** argv)285 static int cmd_csip_set_member(const struct shell *sh, size_t argc, char **argv)
286 {
287 	shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
288 
289 	return -ENOEXEC;
290 }
291 
292 SHELL_STATIC_SUBCMD_SET_CREATE(
293 	csip_set_member_cmds,
294 	SHELL_CMD_ARG(register, NULL,
295 		      "Initialize the service and register callbacks "
296 		      "[size <int>] [rank <int>] [not-lockable] [sirk <data>]",
297 		      cmd_csip_set_member_register, 1, 4),
298 	SHELL_CMD_ARG(lock, NULL, "Lock the set", cmd_csip_set_member_lock, 1, 0),
299 	SHELL_CMD_ARG(release, NULL, "Release the set [force]", cmd_csip_set_member_release, 1, 1),
300 	SHELL_CMD_ARG(sirk, NULL, "Set the currently used SIRK <sirk>", cmd_csip_set_member_sirk, 2,
301 		      0),
302 	SHELL_CMD_ARG(get_sirk, NULL, "Get the currently used SIRK", cmd_csip_set_member_get_sirk,
303 		      1, 0),
304 	SHELL_CMD_ARG(sirk_rsp, NULL,
305 		      "Set the response used in SIRK requests "
306 		      "<accept, accept_enc, reject, oob>",
307 		      cmd_csip_set_member_sirk_rsp, 2, 0),
308 	SHELL_SUBCMD_SET_END
309 );
310 
311 SHELL_CMD_ARG_REGISTER(csip_set_member, &csip_set_member_cmds,
312 		       "Bluetooth CSIP set member shell commands",
313 		       cmd_csip_set_member, 1, 1);
314 
csis_ad_data_add(struct bt_data * data_array,const size_t data_array_size,const bool discoverable)315 ssize_t csis_ad_data_add(struct bt_data *data_array, const size_t data_array_size,
316 			 const bool discoverable)
317 {
318 	size_t ad_len = 0;
319 
320 	/* Advertise RSI in discoverable mode only */
321 	if (svc_inst != NULL && discoverable) {
322 		static uint8_t ad_rsi[BT_CSIP_RSI_SIZE];
323 		int err;
324 
325 		/* A privacy-enabled Set Member should only advertise RSI values derived
326 		 * from a SIRK that is exposed in encrypted form.
327 		 */
328 		if (IS_ENABLED(CONFIG_BT_PRIVACY) &&
329 		    !IS_ENABLED(CONFIG_BT_CSIP_SET_MEMBER_ENC_SIRK_SUPPORT)) {
330 			shell_warn(ctx_shell, "RSI derived from unencrypted SIRK");
331 		}
332 
333 		err = bt_csip_set_member_generate_rsi(svc_inst, ad_rsi);
334 		if (err != 0) {
335 			shell_error(ctx_shell, "Failed to generate RSI (err %d)", err);
336 			return err;
337 		}
338 
339 		__ASSERT(data_array_size > ad_len, "No space for AD_RSI");
340 		data_array[ad_len].type = BT_DATA_CSIS_RSI;
341 		data_array[ad_len].data_len = ARRAY_SIZE(ad_rsi);
342 		data_array[ad_len].data = &ad_rsi[0];
343 		ad_len++;
344 	}
345 
346 	return ad_len;
347 }
348