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, ¶ms);
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