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/shell/shell.h>
9 #include <zephyr/bluetooth/mesh.h>
10 
11 #include "mesh/net.h"
12 #include "mesh/access.h"
13 #include "utils.h"
14 #include <zephyr/bluetooth/mesh/shell.h>
15 
16 static const struct bt_mesh_model *mod;
17 
show_faults(const struct shell * sh,uint8_t test_id,uint16_t cid,uint8_t * faults,size_t fault_count)18 static void show_faults(const struct shell *sh, uint8_t test_id, uint16_t cid, uint8_t *faults,
19 			size_t fault_count)
20 {
21 	size_t i;
22 
23 	if (!fault_count) {
24 		shell_print(sh, "Health Test ID 0x%02x Company ID 0x%04x: no faults\n", test_id,
25 			    cid);
26 		return;
27 	}
28 
29 	shell_print(sh, "Health Test ID 0x%02x Company ID 0x%04x Fault Count %zu:\n", test_id, cid,
30 		    fault_count);
31 
32 	for (i = 0; i < fault_count; i++) {
33 		shell_print(sh, "\t0x%02x\n", faults[i]);
34 	}
35 }
36 
cmd_fault_get(const struct shell * sh,size_t argc,char * argv[])37 static int cmd_fault_get(const struct shell *sh, size_t argc, char *argv[])
38 {
39 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_HEALTH_CLI, &mod)) {
40 		return -ENODEV;
41 	}
42 
43 	struct bt_mesh_health_cli *cli = mod->rt->user_data;
44 	struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_APP(bt_mesh_shell_target_ctx.app_idx,
45 							      bt_mesh_shell_target_ctx.dst);
46 	uint8_t faults[32];
47 	size_t fault_count;
48 	uint8_t test_id;
49 	uint16_t cid;
50 	int err = 0;
51 
52 	cid = shell_strtoul(argv[1], 0, &err);
53 	if (err) {
54 		shell_warn(sh, "Unable to parse input string argument");
55 		return err;
56 	}
57 
58 	fault_count = sizeof(faults);
59 
60 	err = bt_mesh_health_cli_fault_get(cli, ctx.addr ? &ctx : NULL, cid, &test_id, faults,
61 					   &fault_count);
62 	if (err) {
63 		shell_error(sh, "Failed to send Health Fault Get (err %d)", err);
64 	} else {
65 		show_faults(sh, test_id, cid, faults, fault_count);
66 	}
67 
68 	return 0;
69 }
70 
fault_clear(const struct shell * sh,size_t argc,char * argv[],bool acked)71 static int fault_clear(const struct shell *sh, size_t argc, char *argv[], bool acked)
72 {
73 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_HEALTH_CLI, &mod)) {
74 		return -ENODEV;
75 	}
76 
77 	struct bt_mesh_health_cli *cli = mod->rt->user_data;
78 	struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_APP(bt_mesh_shell_target_ctx.app_idx,
79 							      bt_mesh_shell_target_ctx.dst);
80 	uint8_t test_id;
81 	uint16_t cid;
82 	int err = 0;
83 
84 	cid = shell_strtoul(argv[1], 0, &err);
85 	if (err) {
86 		shell_warn(sh, "Unable to parse input string argument");
87 		return err;
88 	}
89 
90 	if (acked) {
91 		uint8_t faults[32];
92 		size_t fault_count = sizeof(faults);
93 
94 		err = bt_mesh_health_cli_fault_clear(cli, ctx.addr ? &ctx : NULL, cid, &test_id,
95 						     faults, &fault_count);
96 		if (err) {
97 			shell_error(sh, "Failed to send Health Fault Clear (err %d)", err);
98 		} else {
99 			show_faults(sh, test_id, cid, faults, fault_count);
100 		}
101 
102 		return err;
103 	}
104 
105 	err = bt_mesh_health_cli_fault_clear_unack(cli, ctx.addr ? &ctx : NULL, cid);
106 	if (err) {
107 		shell_error(sh, "Health Fault Clear Unacknowledged failed (err %d)", err);
108 	}
109 
110 	return err;
111 }
112 
cmd_fault_clear(const struct shell * sh,size_t argc,char * argv[])113 static int cmd_fault_clear(const struct shell *sh, size_t argc, char *argv[])
114 {
115 	return fault_clear(sh, argc, argv, true);
116 }
117 
cmd_fault_clear_unack(const struct shell * sh,size_t argc,char * argv[])118 static int cmd_fault_clear_unack(const struct shell *sh, size_t argc, char *argv[])
119 {
120 	return fault_clear(sh, argc, argv, false);
121 }
122 
fault_test(const struct shell * sh,size_t argc,char * argv[],bool acked)123 static int fault_test(const struct shell *sh, size_t argc, char *argv[], bool acked)
124 {
125 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_HEALTH_CLI, &mod)) {
126 		return -ENODEV;
127 	}
128 
129 	struct bt_mesh_health_cli *cli = mod->rt->user_data;
130 	struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_APP(bt_mesh_shell_target_ctx.app_idx,
131 							      bt_mesh_shell_target_ctx.dst);
132 	uint8_t test_id;
133 	uint16_t cid;
134 	int err = 0;
135 
136 	cid = shell_strtoul(argv[1], 0, &err);
137 	test_id = shell_strtoul(argv[2], 0, &err);
138 	if (err) {
139 		shell_warn(sh, "Unable to parse input string argument");
140 		return err;
141 	}
142 
143 	if (acked) {
144 		uint8_t faults[32];
145 		size_t fault_count = sizeof(faults);
146 
147 		err = bt_mesh_health_cli_fault_test(cli, ctx.addr ? &ctx : NULL, cid, test_id,
148 						    faults, &fault_count);
149 		if (err) {
150 			shell_error(sh, "Failed to send Health Fault Test (err %d)", err);
151 		} else {
152 			show_faults(sh, test_id, cid, faults, fault_count);
153 		}
154 
155 		return err;
156 	}
157 
158 	err = bt_mesh_health_cli_fault_test_unack(cli, ctx.addr ? &ctx : NULL, cid, test_id);
159 	if (err) {
160 		shell_error(sh, "Health Fault Test Unacknowledged failed (err %d)", err);
161 	}
162 
163 	return err;
164 }
165 
cmd_fault_test(const struct shell * sh,size_t argc,char * argv[])166 static int cmd_fault_test(const struct shell *sh, size_t argc, char *argv[])
167 {
168 	return fault_test(sh, argc, argv, true);
169 }
170 
cmd_fault_test_unack(const struct shell * sh,size_t argc,char * argv[])171 static int cmd_fault_test_unack(const struct shell *sh, size_t argc, char *argv[])
172 {
173 	return fault_test(sh, argc, argv, false);
174 }
175 
cmd_period_get(const struct shell * sh,size_t argc,char * argv[])176 static int cmd_period_get(const struct shell *sh, size_t argc, char *argv[])
177 {
178 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_HEALTH_CLI, &mod)) {
179 		return -ENODEV;
180 	}
181 
182 	struct bt_mesh_health_cli *cli = mod->rt->user_data;
183 	struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_APP(bt_mesh_shell_target_ctx.app_idx,
184 							      bt_mesh_shell_target_ctx.dst);
185 	uint8_t divisor;
186 	int err;
187 
188 	err = bt_mesh_health_cli_period_get(cli, ctx.addr ? &ctx : NULL, &divisor);
189 	if (err) {
190 		shell_error(sh, "Failed to send Health Period Get (err %d)", err);
191 	} else {
192 		shell_print(sh, "Health FastPeriodDivisor: %u", divisor);
193 	}
194 
195 	return 0;
196 }
197 
period_set(const struct shell * sh,size_t argc,char * argv[],bool acked)198 static int period_set(const struct shell *sh, size_t argc, char *argv[], bool acked)
199 {
200 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_HEALTH_CLI, &mod)) {
201 		return -ENODEV;
202 	}
203 
204 	struct bt_mesh_health_cli *cli = mod->rt->user_data;
205 	struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_APP(bt_mesh_shell_target_ctx.app_idx,
206 							      bt_mesh_shell_target_ctx.dst);
207 	uint8_t divisor;
208 	int err = 0;
209 
210 	divisor = shell_strtoul(argv[1], 0, &err);
211 	if (err) {
212 		shell_warn(sh, "Unable to parse input string argument");
213 		return err;
214 	}
215 
216 	if (acked) {
217 		uint8_t updated_divisor;
218 
219 		err = bt_mesh_health_cli_period_set(cli, ctx.addr ? &ctx : NULL, divisor,
220 						    &updated_divisor);
221 		if (err) {
222 			shell_error(sh, "Failed to send Health Period Set (err %d)", err);
223 		} else {
224 			shell_print(sh, "Health FastPeriodDivisor: %u", updated_divisor);
225 		}
226 
227 		return err;
228 	}
229 
230 	err = bt_mesh_health_cli_period_set_unack(cli, ctx.addr ? &ctx : NULL, divisor);
231 	if (err) {
232 		shell_print(sh, "Failed to send Health Period Set (err %d)", err);
233 	}
234 
235 	return err;
236 }
237 
cmd_period_set(const struct shell * sh,size_t argc,char * argv[])238 static int cmd_period_set(const struct shell *sh, size_t argc, char *argv[])
239 {
240 	return period_set(sh, argc, argv, true);
241 }
242 
cmd_period_set_unack(const struct shell * sh,size_t argc,char * argv[])243 static int cmd_period_set_unack(const struct shell *sh, size_t argc, char *argv[])
244 {
245 	return period_set(sh, argc, argv, false);
246 }
247 
cmd_attention_get(const struct shell * sh,size_t argc,char * argv[])248 static int cmd_attention_get(const struct shell *sh, size_t argc, char *argv[])
249 {
250 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_HEALTH_CLI, &mod)) {
251 		return -ENODEV;
252 	}
253 
254 	struct bt_mesh_health_cli *cli = mod->rt->user_data;
255 	struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_APP(bt_mesh_shell_target_ctx.app_idx,
256 							      bt_mesh_shell_target_ctx.dst);
257 	uint8_t attention;
258 	int err;
259 
260 	err = bt_mesh_health_cli_attention_get(cli, ctx.addr ? &ctx : NULL, &attention);
261 	if (err) {
262 		shell_error(sh, "Failed to send Health Attention Get (err %d)", err);
263 	} else {
264 		shell_print(sh, "Health Attention Timer: %u", attention);
265 	}
266 
267 	return 0;
268 }
269 
attention_set(const struct shell * sh,size_t argc,char * argv[],bool acked)270 static int attention_set(const struct shell *sh, size_t argc, char *argv[], bool acked)
271 {
272 	if (!mod && !bt_mesh_shell_mdl_first_get(BT_MESH_MODEL_ID_HEALTH_CLI, &mod)) {
273 		return -ENODEV;
274 	}
275 
276 	struct bt_mesh_health_cli *cli = mod->rt->user_data;
277 	struct bt_mesh_msg_ctx ctx = BT_MESH_MSG_CTX_INIT_APP(bt_mesh_shell_target_ctx.app_idx,
278 							      bt_mesh_shell_target_ctx.dst);
279 	uint8_t attention;
280 	int err = 0;
281 
282 	attention = shell_strtoul(argv[1], 0, &err);
283 	if (err) {
284 		shell_warn(sh, "Unable to parse input string argument");
285 		return err;
286 	}
287 
288 	if (acked) {
289 		uint8_t updated_attention;
290 
291 		err = bt_mesh_health_cli_attention_set(cli, ctx.addr ? &ctx : NULL, attention,
292 						       &updated_attention);
293 		if (err) {
294 			shell_error(sh, "Failed to send Health Attention Set (err %d)", err);
295 		} else {
296 			shell_print(sh, "Health Attention Timer: %u", updated_attention);
297 		}
298 
299 		return err;
300 	}
301 
302 	err = bt_mesh_health_cli_attention_set_unack(cli, ctx.addr ? &ctx : NULL, attention);
303 	if (err) {
304 		shell_error(sh, "Failed to send Health Attention Set (err % d) ", err);
305 	}
306 
307 	return err;
308 }
309 
cmd_attention_set(const struct shell * sh,size_t argc,char * argv[])310 static int cmd_attention_set(const struct shell *sh, size_t argc, char *argv[])
311 {
312 	return attention_set(sh, argc, argv, true);
313 }
314 
cmd_attention_set_unack(const struct shell * sh,size_t argc,char * argv[])315 static int cmd_attention_set_unack(const struct shell *sh, size_t argc, char *argv[])
316 {
317 	return attention_set(sh, argc, argv, false);
318 }
319 
320 BT_MESH_SHELL_MDL_INSTANCE_CMDS(instance_cmds, BT_MESH_MODEL_ID_HEALTH_CLI, mod);
321 
322 SHELL_STATIC_SUBCMD_SET_CREATE(
323 	health_cli_cmds,
324 	/* Health Client Model Operations */
325 	SHELL_CMD_ARG(fault-get, NULL, "<CID>", cmd_fault_get, 2, 0),
326 	SHELL_CMD_ARG(fault-clear, NULL, "<CID>", cmd_fault_clear, 2, 0),
327 	SHELL_CMD_ARG(fault-clear-unack, NULL, "<CID>", cmd_fault_clear_unack, 2, 0),
328 	SHELL_CMD_ARG(fault-test, NULL, "<CID> <TestID>", cmd_fault_test, 3, 0),
329 	SHELL_CMD_ARG(fault-test-unack, NULL, "<CID> <TestID>", cmd_fault_test_unack, 3,
330 		      0),
331 	SHELL_CMD_ARG(period-get, NULL, NULL, cmd_period_get, 1, 0),
332 	SHELL_CMD_ARG(period-set, NULL, "<Divisor>", cmd_period_set, 2, 0),
333 	SHELL_CMD_ARG(period-set-unack, NULL, "<Divisor>", cmd_period_set_unack, 2, 0),
334 	SHELL_CMD_ARG(attention-get, NULL, NULL, cmd_attention_get, 1, 0),
335 	SHELL_CMD_ARG(attention-set, NULL, "<Time(s)>", cmd_attention_set, 2, 0),
336 	SHELL_CMD_ARG(attention-set-unack, NULL, "<Time(s)>", cmd_attention_set_unack, 2, 0),
337 	SHELL_CMD(instance, &instance_cmds, "Instance commands", bt_mesh_shell_mdl_cmds_help),
338 	SHELL_SUBCMD_SET_END);
339 
340 SHELL_SUBCMD_ADD((mesh, models), health, &health_cli_cmds, "Health Cli commands",
341 		 bt_mesh_shell_mdl_cmds_help, 1, 1);
342