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(¶m, &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