1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/net_buf.h>
9 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
10 #include <zephyr/mgmt/mcumgr/transport/smp_dummy.h>
11 #include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
12 #include <zephyr/mgmt/mcumgr/grp/os_mgmt/os_mgmt.h>
13 #include <zcbor_common.h>
14 #include <zcbor_decode.h>
15 #include <zcbor_encode.h>
16 #include <mgmt/mcumgr/util/zcbor_bulk.h>
17 #include <zephyr/version.h>
18 #include <string.h>
19 #include <smp_internal.h>
20 #include "smp_test_util.h"
21 
22 #define SMP_RESPONSE_WAIT_TIME 3
23 #define ZCBOR_BUFFER_SIZE 64
24 #define OUTPUT_BUFFER_SIZE 64
25 #define ZCBOR_HISTORY_ARRAY_SIZE 4
26 
27 /* Test sets */
28 enum {
29 	CB_NOTIFICATION_TEST_CALLBACK_DISABLED,
30 	CB_NOTIFICATION_TEST_CALLBACK_ENABLED,
31 	CB_NOTIFICATION_TEST_CALLBACK_DISABLED_VERIFY,
32 
33 	CB_NOTIFICATION_TEST_SET_COUNT
34 };
35 
36 static struct net_buf *nb;
37 
38 struct state {
39 	uint8_t test_set;
40 };
41 
42 static struct state test_state = {
43 	.test_set = 0,
44 };
45 
46 static bool cmd_recv_got;
47 static bool cmd_status_got;
48 static bool cmd_done_got;
49 static bool cmd_other_got;
50 
51 /* Responses to commands */
52 
mgmt_event_cmd_callback(uint32_t event,enum mgmt_cb_return prev_status,int32_t * rc,uint16_t * group,bool * abort_more,void * data,size_t data_size)53 static enum mgmt_cb_return mgmt_event_cmd_callback(uint32_t event, enum mgmt_cb_return prev_status,
54 						   int32_t *rc, uint16_t *group, bool *abort_more,
55 						   void *data, size_t data_size)
56 {
57 	if (event == MGMT_EVT_OP_CMD_RECV) {
58 		cmd_recv_got = true;
59 	} else if (event == MGMT_EVT_OP_CMD_STATUS) {
60 		cmd_status_got = true;
61 	} else if (event == MGMT_EVT_OP_CMD_DONE) {
62 		cmd_done_got = true;
63 	} else {
64 		cmd_other_got = true;
65 	}
66 
67 	return MGMT_CB_OK;
68 }
69 
70 static struct mgmt_callback mgmt_event_callback = {
71 	.callback = mgmt_event_cmd_callback,
72 	.event_id = (MGMT_EVT_OP_CMD_RECV | MGMT_EVT_OP_CMD_STATUS | MGMT_EVT_OP_CMD_DONE),
73 };
74 
setup_callbacks(void)75 static void *setup_callbacks(void)
76 {
77 	mgmt_callback_register(&mgmt_event_callback);
78 	return NULL;
79 }
80 
destroy_callbacks(void * p)81 static void destroy_callbacks(void *p)
82 {
83 	mgmt_callback_unregister(&mgmt_event_callback);
84 }
85 
wait_for_sync(void)86 static inline void wait_for_sync(void)
87 {
88 #ifdef CONFIG_SMP
89 	/* For SMP systems, it is possible that a dummy response is fully received and processed
90 	 * prior to the callback code being executed, therefore implement a dummy wait to wait
91 	 * for callback synchronisation to take place.
92 	 */
93 	k_sleep(K_MSEC(1));
94 #endif
95 }
96 
ZTEST(callback_disabled,test_notifications_disabled)97 ZTEST(callback_disabled, test_notifications_disabled)
98 {
99 	uint8_t buffer[ZCBOR_BUFFER_SIZE];
100 	uint8_t buffer_out[OUTPUT_BUFFER_SIZE];
101 	bool ok;
102 	uint16_t buffer_size;
103 	zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 };
104 	zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 };
105 	bool received;
106 
107 	memset(buffer, 0, sizeof(buffer));
108 	memset(buffer_out, 0, sizeof(buffer_out));
109 	buffer_size = 0;
110 	memset(zse, 0, sizeof(zse));
111 	memset(zsd, 0, sizeof(zsd));
112 
113 	zcbor_new_encode_state(zse, 2, buffer, ARRAY_SIZE(buffer), 0);
114 
115 	ok = create_mcumgr_format_packet(zse, buffer, buffer_out, &buffer_size);
116 	zassert_true(ok, "Expected packet creation to be successful\n");
117 
118 	/* Enable dummy SMP backend and ready for usage */
119 	smp_dummy_enable();
120 	smp_dummy_clear_state();
121 
122 	/* Send query command to dummy SMP backend */
123 	(void)smp_dummy_tx_pkt(buffer_out, buffer_size);
124 	smp_dummy_add_data();
125 
126 	/* For a short duration to see if response has been received */
127 	received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME);
128 
129 	zassert_true(received, "Expected to receive data but timed out\n");
130 
131 	/* Retrieve response buffer */
132 	nb = smp_dummy_get_outgoing();
133 	smp_dummy_disable();
134 
135 	/* Check events */
136 	wait_for_sync();
137 	zassert_false(cmd_recv_got, "Did not expect received command callback\n");
138 	zassert_false(cmd_status_got, "Did not expect IMG status callback\n");
139 	zassert_false(cmd_done_got, "Did not expect done command callback\n");
140 	zassert_false(cmd_other_got, "Did not expect other callback(s)\n");
141 }
142 
ZTEST(callback_enabled,test_notifications_enabled)143 ZTEST(callback_enabled, test_notifications_enabled)
144 {
145 	uint8_t buffer[ZCBOR_BUFFER_SIZE];
146 	uint8_t buffer_out[OUTPUT_BUFFER_SIZE];
147 	bool ok;
148 	uint16_t buffer_size;
149 	zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 };
150 	zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 };
151 	bool received;
152 
153 	memset(buffer, 0, sizeof(buffer));
154 	memset(buffer_out, 0, sizeof(buffer_out));
155 	buffer_size = 0;
156 	memset(zse, 0, sizeof(zse));
157 	memset(zsd, 0, sizeof(zsd));
158 
159 	zcbor_new_encode_state(zse, 2, buffer, ARRAY_SIZE(buffer), 0);
160 
161 	ok = create_mcumgr_format_packet(zse, buffer, buffer_out, &buffer_size);
162 	zassert_true(ok, "Expected packet creation to be successful\n");
163 
164 	/* Enable dummy SMP backend and ready for usage */
165 	smp_dummy_enable();
166 	smp_dummy_clear_state();
167 
168 	/* Send query command to dummy SMP backend */
169 	(void)smp_dummy_tx_pkt(buffer_out, buffer_size);
170 	smp_dummy_add_data();
171 
172 	/* For a short duration to see if response has been received */
173 	received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME);
174 
175 	zassert_true(received, "Expected to receive data but timed out\n");
176 
177 	/* Retrieve response buffer */
178 	nb = smp_dummy_get_outgoing();
179 	smp_dummy_disable();
180 
181 	/* Check events */
182 	wait_for_sync();
183 	zassert_true(cmd_recv_got, "Expected received command callback\n");
184 	zassert_false(cmd_status_got, "Did not expect IMG status callback\n");
185 	zassert_true(cmd_done_got, "Expected done command callback\n");
186 	zassert_false(cmd_other_got, "Did not expect other callback(s)\n");
187 }
188 
ZTEST(callback_disabled_verify,test_notifications_disabled_verify)189 ZTEST(callback_disabled_verify, test_notifications_disabled_verify)
190 {
191 	uint8_t buffer[ZCBOR_BUFFER_SIZE];
192 	uint8_t buffer_out[OUTPUT_BUFFER_SIZE];
193 	bool ok;
194 	uint16_t buffer_size;
195 	zcbor_state_t zse[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 };
196 	zcbor_state_t zsd[ZCBOR_HISTORY_ARRAY_SIZE] = { 0 };
197 	bool received;
198 
199 	memset(buffer, 0, sizeof(buffer));
200 	memset(buffer_out, 0, sizeof(buffer_out));
201 	buffer_size = 0;
202 	memset(zse, 0, sizeof(zse));
203 	memset(zsd, 0, sizeof(zsd));
204 
205 	zcbor_new_encode_state(zse, 2, buffer, ARRAY_SIZE(buffer), 0);
206 
207 	ok = create_mcumgr_format_packet(zse, buffer, buffer_out, &buffer_size);
208 	zassert_true(ok, "Expected packet creation to be successful\n");
209 
210 	/* Enable dummy SMP backend and ready for usage */
211 	smp_dummy_enable();
212 	smp_dummy_clear_state();
213 
214 	/* Send query command to dummy SMP backend */
215 	(void)smp_dummy_tx_pkt(buffer_out, buffer_size);
216 	smp_dummy_add_data();
217 
218 	/* For a short duration to see if response has been received */
219 	received = smp_dummy_wait_for_data(SMP_RESPONSE_WAIT_TIME);
220 
221 	zassert_true(received, "Expected to receive data but timed out\n");
222 
223 	/* Retrieve response buffer */
224 	nb = smp_dummy_get_outgoing();
225 	smp_dummy_disable();
226 
227 	/* Check events */
228 	wait_for_sync();
229 	zassert_false(cmd_recv_got, "Did not expect received command callback\n");
230 	zassert_false(cmd_status_got, "Did not expect IMG status callback\n");
231 	zassert_false(cmd_done_got, "Did not expect done command callback\n");
232 	zassert_false(cmd_other_got, "Did not expect other callback(s)\n");
233 }
234 
cleanup_test(void * p)235 static void cleanup_test(void *p)
236 {
237 	if (nb != NULL) {
238 		net_buf_unref(nb);
239 		nb = NULL;
240 	}
241 
242 	cmd_recv_got = false;
243 	cmd_status_got = false;
244 	cmd_done_got = false;
245 	cmd_other_got = false;
246 }
247 
test_main(void)248 void test_main(void)
249 {
250 	while (test_state.test_set < CB_NOTIFICATION_TEST_SET_COUNT) {
251 		ztest_run_all(&test_state, false, 1, 1);
252 		++test_state.test_set;
253 	}
254 
255 	ztest_verify_all_test_suites_ran();
256 }
257 
callback_disabled_predicate(const void * state)258 static bool callback_disabled_predicate(const void *state)
259 {
260 	return ((struct state *)state)->test_set == CB_NOTIFICATION_TEST_CALLBACK_DISABLED;
261 }
262 
callback_enabled_predicate(const void * state)263 static bool callback_enabled_predicate(const void *state)
264 {
265 	return ((struct state *)state)->test_set == CB_NOTIFICATION_TEST_CALLBACK_ENABLED;
266 }
267 
callback_disabled_verify_predicate(const void * state)268 static bool callback_disabled_verify_predicate(const void *state)
269 {
270 	return ((struct state *)state)->test_set == CB_NOTIFICATION_TEST_CALLBACK_DISABLED_VERIFY;
271 }
272 
273 ZTEST_SUITE(callback_disabled, callback_disabled_predicate, NULL, NULL, cleanup_test, NULL);
274 ZTEST_SUITE(callback_enabled, callback_enabled_predicate, setup_callbacks, NULL, cleanup_test,
275 	    destroy_callbacks);
276 ZTEST_SUITE(callback_disabled_verify, callback_disabled_verify_predicate, NULL, NULL,
277 	    cleanup_test, NULL);
278