1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdlib.h>
8 #include <zephyr/bluetooth/mesh.h>
9 #include <zephyr/shell/shell.h>
10 
11 #include "utils.h"
12 
13 #include "../dfu_slot.h"
14 #include "../dfd_srv_internal.h"
15 #include "../access.h"
16 
17 static const struct bt_mesh_model *mod;
18 
print_receivers_status(const struct shell * sh,struct bt_mesh_dfd_srv * srv,enum bt_mesh_dfd_status status)19 static void print_receivers_status(const struct shell *sh, struct bt_mesh_dfd_srv *srv,
20 				   enum bt_mesh_dfd_status status)
21 {
22 	shell_print(sh, "{\"status\": %d, \"target_cnt\": %d}", status, srv->target_cnt);
23 }
24 
print_dfd_status(const struct shell * sh,struct bt_mesh_dfd_srv * srv,enum bt_mesh_dfd_status status)25 static void print_dfd_status(const struct shell *sh, struct bt_mesh_dfd_srv *srv,
26 			     enum bt_mesh_dfd_status status)
27 {
28 	shell_fprintf(sh, SHELL_NORMAL, "{ \"status\": %d, \"phase\": %d", status,
29 		      srv->phase);
30 
31 	if (srv->phase != BT_MESH_DFD_PHASE_IDLE && srv->dfu.xfer.slot) {
32 		shell_fprintf(sh, SHELL_NORMAL, ", \"group\": %d, \"app_idx\": %d, "
33 			      "\"ttl\": %d, \"timeout_base\": %d, \"xfer_mode\": %d, "
34 			      "\"apply\": %d, \"slot_idx\": %d", srv->inputs.group,
35 			      srv->inputs.app_idx, srv->inputs.ttl, srv->inputs.timeout_base,
36 			      srv->dfu.xfer.blob.mode, srv->apply, srv->slot_idx);
37 	}
38 
39 	shell_print(sh, " }");
40 }
41 
print_fw_status(const struct shell * sh,enum bt_mesh_dfd_status status,uint16_t idx,const uint8_t * fwid,size_t fwid_len)42 static void print_fw_status(const struct shell *sh, enum bt_mesh_dfd_status status,
43 			    uint16_t idx, const uint8_t *fwid, size_t fwid_len)
44 {
45 	shell_fprintf(sh, SHELL_NORMAL, "{ \"status\": %d, \"slot_cnt\": %d, \"idx\": %d",
46 		      status, bt_mesh_dfu_slot_count(), idx);
47 	if (fwid) {
48 		shell_fprintf(sh, SHELL_NORMAL, ", \"fwid\": \"");
49 		for (size_t i = 0; i < fwid_len; i++) {
50 			shell_fprintf(sh, SHELL_NORMAL, "%02x", fwid[i]);
51 		}
52 		shell_fprintf(sh, SHELL_NORMAL, "\"");
53 	}
54 	shell_print(sh, " }");
55 }
56 
slot_space_cb(const struct bt_mesh_dfu_slot * slot,void * user_data)57 static enum bt_mesh_dfu_iter slot_space_cb(const struct bt_mesh_dfu_slot *slot,
58 					   void *user_data)
59 {
60 	size_t *total = user_data;
61 
62 	*total += slot->size;
63 
64 	return BT_MESH_DFU_ITER_CONTINUE;
65 }
66 
cmd_dfd_receivers_add(const struct shell * sh,size_t argc,char * argv[])67 static int cmd_dfd_receivers_add(const struct shell *sh, size_t argc, char *argv[])
68 {
69 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
70 		return -ENODEV;
71 	}
72 
73 	struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
74 
75 	if (bt_mesh_dfu_cli_is_busy(&dfd_srv->dfu)) {
76 		print_receivers_status(sh, dfd_srv,
77 				       BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION);
78 		return -EBUSY;
79 	}
80 
81 	char *outer_state, *inner_state;
82 
83 	char *token = strtok_r(argv[1], ";", &outer_state);
84 
85 	while (token) {
86 		char *addr_str = strtok_r(token, ",", &inner_state);
87 		char *img_idx_str = strtok_r(NULL, ",", &inner_state);
88 		int err = 0;
89 
90 		if (addr_str == NULL || img_idx_str == NULL) {
91 			return -EINVAL;
92 		}
93 
94 		uint16_t addr = shell_strtoul(addr_str, 0, &err);
95 		uint8_t img_idx = shell_strtoul(img_idx_str, 0, &err);
96 
97 		if (err) {
98 			shell_warn(sh, "Unable to parse input string argument");
99 			return err;
100 		}
101 
102 		enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_receiver_add(
103 			dfd_srv, addr, img_idx);
104 
105 		if (status != BT_MESH_DFD_SUCCESS) {
106 			print_receivers_status(sh, dfd_srv, status);
107 			return status == BT_MESH_DFD_ERR_INSUFFICIENT_RESOURCES ?
108 				-ENOSPC : -EINVAL;
109 		}
110 
111 		token = strtok_r(NULL, ";", &outer_state);
112 	}
113 
114 	print_receivers_status(sh, dfd_srv, BT_MESH_DFD_SUCCESS);
115 
116 	return 0;
117 }
118 
cmd_dfd_receivers_delete_all(const struct shell * sh,size_t argc,char * argv[])119 static int cmd_dfd_receivers_delete_all(const struct shell *sh, size_t argc, char *argv[])
120 {
121 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
122 		return -ENODEV;
123 	}
124 
125 	struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
126 
127 	enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_receivers_delete_all(
128 		dfd_srv);
129 
130 	print_receivers_status(sh, dfd_srv, status);
131 
132 	if (status != BT_MESH_DFD_SUCCESS) {
133 		return status == BT_MESH_DFD_ERR_BUSY_WITH_DISTRIBUTION ? -EBUSY : -EINVAL;
134 	}
135 
136 	return 0;
137 }
138 
cmd_dfd_receivers_get(const struct shell * sh,size_t argc,char * argv[])139 static int cmd_dfd_receivers_get(const struct shell *sh, size_t argc, char *argv[])
140 {
141 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
142 		return -ENODEV;
143 	}
144 
145 	struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
146 	int err = 0;
147 
148 	uint16_t first = shell_strtoul(argv[1], 0, &err);
149 	uint16_t cnt = shell_strtoul(argv[2], 0, &err);
150 
151 	if (err) {
152 		shell_warn(sh, "Unable to parse input string argument");
153 		return err;
154 	}
155 
156 	if (cnt == 0 || dfd_srv->target_cnt <= first) {
157 		return -EINVAL;
158 	}
159 
160 	cnt = MIN(cnt, dfd_srv->target_cnt - first);
161 	uint8_t progress = bt_mesh_dfu_cli_progress(&dfd_srv->dfu) / 2;
162 
163 	shell_print(sh, "{\n\t\"target_cnt\": %d,\n\t\"targets\": {",
164 		    dfd_srv->target_cnt);
165 	for (int i = 0; i < cnt; i++) {
166 		const struct bt_mesh_dfu_target *t = &dfd_srv->targets[i + first];
167 
168 		shell_print(sh, "\t\t\"%d\": { \"blob_addr\": %d, \"phase\": %d, "
169 			    "\"status\": %d, \"blob_status\": %d, \"progress\": %d, "
170 			    "\"img_idx\": %d }%s", i + first, t->blob.addr, t->phase, t->status,
171 			    t->blob.status, progress, t->img_idx, (i == cnt - 1) ? "" : ",");
172 	}
173 	shell_print(sh, "\t}\n}");
174 
175 	return 0;
176 }
177 
cmd_dfd_capabilities_get(const struct shell * sh,size_t argc,char * argv[])178 static int cmd_dfd_capabilities_get(const struct shell *sh, size_t argc, char *argv[])
179 {
180 	size_t size = 0;
181 	/* Remaining size */
182 	(void)bt_mesh_dfu_slot_foreach(slot_space_cb, &size);
183 	size = MIN(size, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE);
184 
185 	shell_print(sh, "{ \"targets_max\": %d, \"slot_cnt\": %d, \"slot_max_size\": %d, "
186 		    "\"slot_space\": %d, \"remaining_space\": %d, \"oob_supported\": false }",
187 		    CONFIG_BT_MESH_DFD_SRV_TARGETS_MAX, CONFIG_BT_MESH_DFU_SLOT_CNT,
188 		    CONFIG_BT_MESH_DFD_SRV_SLOT_MAX_SIZE, CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE,
189 		    CONFIG_BT_MESH_DFD_SRV_SLOT_SPACE - size);
190 
191 	return 0;
192 }
193 
cmd_dfd_get(const struct shell * sh,size_t argc,char * argv[])194 static int cmd_dfd_get(const struct shell *sh, size_t argc, char *argv[])
195 {
196 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
197 		return -ENODEV;
198 	}
199 
200 	struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
201 
202 	print_dfd_status(sh, dfd_srv, BT_MESH_DFD_SUCCESS);
203 
204 	return 0;
205 }
206 
cmd_dfd_start(const struct shell * sh,size_t argc,char * argv[])207 static int cmd_dfd_start(const struct shell *sh, size_t argc, char *argv[])
208 {
209 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
210 		return -ENODEV;
211 	}
212 
213 	struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
214 	struct bt_mesh_dfd_start_params params;
215 	int err = 0;
216 
217 	params.app_idx = shell_strtoul(argv[1], 0, &err);
218 	params.slot_idx = shell_strtoul(argv[2], 0, &err);
219 	if (argc > 3) {
220 		params.group = shell_strtoul(argv[3], 0, &err);
221 	} else {
222 		params.group = BT_MESH_ADDR_UNASSIGNED;
223 	}
224 
225 	if (argc > 4) {
226 		params.apply = shell_strtobool(argv[4], 0, &err);
227 	} else {
228 		params.apply = true;
229 	}
230 
231 	if (argc > 5) {
232 		params.ttl = shell_strtoul(argv[5], 0, &err);
233 	} else {
234 		params.ttl = BT_MESH_TTL_DEFAULT;
235 	}
236 
237 	if (argc > 6) {
238 		params.timeout_base = shell_strtoul(argv[6], 0, &err);
239 	} else {
240 		params.timeout_base = 0U;
241 	}
242 
243 	if (argc > 7) {
244 		params.xfer_mode = (enum bt_mesh_blob_xfer_mode)shell_strtoul(argv[7], 0, &err);
245 	} else {
246 		params.xfer_mode = BT_MESH_BLOB_XFER_MODE_PUSH;
247 	}
248 
249 	if (err) {
250 		shell_warn(sh, "Unable to parse input string argument");
251 		return err;
252 	}
253 
254 	enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_start(dfd_srv, &params);
255 
256 	print_dfd_status(sh, dfd_srv, status);
257 	if (status != BT_MESH_DFD_SUCCESS) {
258 		return -EINVAL;
259 	}
260 
261 	return 0;
262 }
263 
cmd_dfd_suspend(const struct shell * sh,size_t argc,char * argv[])264 static int cmd_dfd_suspend(const struct shell *sh, size_t argc, char *argv[])
265 {
266 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
267 		return -ENODEV;
268 	}
269 
270 	struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
271 
272 	enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_suspend(dfd_srv);
273 
274 	print_dfd_status(sh, dfd_srv, status);
275 	if (status != BT_MESH_DFD_SUCCESS) {
276 		return -EINVAL;
277 	}
278 
279 	return 0;
280 }
281 
cmd_dfd_cancel(const struct shell * sh,size_t argc,char * argv[])282 static int cmd_dfd_cancel(const struct shell *sh, size_t argc, char *argv[])
283 {
284 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
285 		return -ENODEV;
286 	}
287 
288 	struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
289 
290 	enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_cancel(dfd_srv, NULL);
291 
292 	print_dfd_status(sh, dfd_srv, status);
293 	if (status != BT_MESH_DFD_SUCCESS) {
294 		return -EINVAL;
295 	}
296 
297 	return 0;
298 }
299 
cmd_dfd_apply(const struct shell * sh,size_t argc,char * argv[])300 static int cmd_dfd_apply(const struct shell *sh, size_t argc, char *argv[])
301 {
302 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
303 		return -ENODEV;
304 	}
305 
306 	struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
307 
308 	enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_apply(dfd_srv);
309 
310 	print_dfd_status(sh, dfd_srv, status);
311 	if (status != BT_MESH_DFD_SUCCESS) {
312 		return -EINVAL;
313 	}
314 
315 	return 0;
316 }
317 
cmd_dfd_fw_get(const struct shell * sh,size_t argc,char * argv[])318 static int cmd_dfd_fw_get(const struct shell *sh, size_t argc, char *argv[])
319 {
320 	uint8_t fwid[CONFIG_BT_MESH_DFU_FWID_MAXLEN];
321 	size_t hexlen = strlen(argv[1]);
322 	size_t fwid_len = hex2bin(argv[1], hexlen, fwid, CONFIG_BT_MESH_DFU_FWID_MAXLEN);
323 
324 	if (fwid_len != ((hexlen + 1) / 2)) {
325 		return -EINVAL;
326 	}
327 
328 	int idx = bt_mesh_dfu_slot_get(fwid, fwid_len, NULL);
329 
330 	if (idx >= 0) {
331 		print_fw_status(sh, BT_MESH_DFD_SUCCESS, idx, fwid, fwid_len);
332 	} else {
333 		print_fw_status(sh, BT_MESH_DFD_ERR_FW_NOT_FOUND, 0xffff, fwid, fwid_len);
334 		return -ENOENT;
335 	}
336 
337 	return 0;
338 }
339 
cmd_dfd_fw_get_by_idx(const struct shell * sh,size_t argc,char * argv[])340 static int cmd_dfd_fw_get_by_idx(const struct shell *sh, size_t argc, char *argv[])
341 {
342 	int err = 0;
343 	uint16_t idx = shell_strtoul(argv[1], 0, &err);
344 	const struct bt_mesh_dfu_slot *slot = bt_mesh_dfu_slot_at(idx);
345 
346 	if (err) {
347 		shell_warn(sh, "Unable to parse input string argument");
348 		return err;
349 	}
350 
351 	if (slot) {
352 		print_fw_status(sh, BT_MESH_DFD_SUCCESS, idx, slot->fwid, slot->fwid_len);
353 	} else {
354 		print_fw_status(sh, BT_MESH_DFD_ERR_FW_NOT_FOUND, idx, NULL, 0);
355 		return -ENOENT;
356 	}
357 
358 	return 0;
359 }
360 
cmd_dfd_fw_delete(const struct shell * sh,size_t argc,char * argv[])361 static int cmd_dfd_fw_delete(const struct shell *sh, size_t argc, char *argv[])
362 {
363 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
364 		return -ENODEV;
365 	}
366 
367 	struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
368 
369 	uint8_t fwid_buf[CONFIG_BT_MESH_DFU_FWID_MAXLEN];
370 	size_t hexlen = strlen(argv[1]);
371 	size_t fwid_len = hex2bin(argv[1], hexlen, fwid_buf, CONFIG_BT_MESH_DFU_FWID_MAXLEN);
372 
373 	if (fwid_len != ((hexlen + 1) / 2)) {
374 		return -EINVAL;
375 	}
376 
377 	const uint8_t *fwid = &fwid_buf[0];
378 
379 	enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_fw_delete(dfd_srv,
380 								   &fwid_len, &fwid);
381 
382 	print_fw_status(sh, status, 0xffff, fwid, fwid_len);
383 
384 	if (status != BT_MESH_DFD_SUCCESS) {
385 		return -EINVAL;
386 	}
387 
388 	return 0;
389 }
390 
cmd_dfd_fw_delete_all(const struct shell * sh,size_t argc,char * argv[])391 static int cmd_dfd_fw_delete_all(const struct shell *sh, size_t argc, char *argv[])
392 {
393 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_DFD_SRV, &mod)) {
394 		return -ENODEV;
395 	}
396 
397 	struct bt_mesh_dfd_srv *dfd_srv = mod->rt->user_data;
398 
399 	enum bt_mesh_dfd_status status = bt_mesh_dfd_srv_fw_delete_all(dfd_srv);
400 
401 	print_fw_status(sh, status, 0xffff, NULL, 0);
402 
403 	if (status != BT_MESH_DFD_SUCCESS) {
404 		return -EINVAL;
405 	}
406 
407 	return 0;
408 }
409 
410 BT_MESH_SHELL_MDL_INSTANCE_CMDS(instance_cmds, BT_MESH_MODEL_ID_DFD_SRV, mod);
411 
412 SHELL_STATIC_SUBCMD_SET_CREATE(
413 	dfd_cmds,
414 	SHELL_CMD_ARG(receivers-add, NULL, "<Addr>,<FwIdx>[;<Addr>,<FwIdx>]...",
415 		      cmd_dfd_receivers_add, 2, 0),
416 	SHELL_CMD_ARG(receivers-delete-all, NULL, NULL, cmd_dfd_receivers_delete_all, 1, 0),
417 	SHELL_CMD_ARG(receivers-get, NULL, "<First> <Count>", cmd_dfd_receivers_get, 3, 0),
418 	SHELL_CMD_ARG(capabilities-get, NULL, NULL, cmd_dfd_capabilities_get, 1, 0),
419 	SHELL_CMD_ARG(get, NULL, NULL, cmd_dfd_get, 1, 0),
420 	SHELL_CMD_ARG(start, NULL,
421 		      "<AppKeyIdx> <SlotIdx> [<Group> [<PolicyApply> [<TTL> "
422 		      "[<TimeoutBase> [<XferMode>]]]]]",
423 		      cmd_dfd_start, 3, 5),
424 	SHELL_CMD_ARG(suspend, NULL, NULL, cmd_dfd_suspend, 1, 0),
425 	SHELL_CMD_ARG(cancel, NULL, NULL, cmd_dfd_cancel, 1, 0),
426 	SHELL_CMD_ARG(apply, NULL, NULL, cmd_dfd_apply, 1, 0),
427 	SHELL_CMD_ARG(fw-get, NULL, "<FwID>", cmd_dfd_fw_get, 2, 0),
428 	SHELL_CMD_ARG(fw-get-by-idx, NULL, "<Idx>", cmd_dfd_fw_get_by_idx, 2, 0),
429 	SHELL_CMD_ARG(fw-delete, NULL, "<FwID>", cmd_dfd_fw_delete, 2, 0),
430 	SHELL_CMD_ARG(fw-delete-all, NULL, NULL, cmd_dfd_fw_delete_all, 1, 0),
431 	SHELL_CMD(instance, &instance_cmds, "Instance commands", bt_mesh_shell_mdl_cmds_help),
432 	SHELL_SUBCMD_SET_END);
433 
434 SHELL_SUBCMD_ADD((mesh, models), dfd, &dfd_cmds, "Distributor commands",
435 		 bt_mesh_shell_mdl_cmds_help, 1, 1);
436