1 /*
2 * Copyright (c) 2023 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/net/net_if.h>
8 #include <zephyr/net/conn_mgr_connectivity.h>
9 #include "test_conn_impl.h"
10
11 /* Event simulation */
12 static int simulated_event;
13 static struct net_if *simulated_event_iface;
14 static K_MUTEX_DEFINE(simulated_event_mutex);
15
16 /* Static storage for fatal error info */
17 static int fatal_error;
18
simulate_event_handler(struct k_work * work)19 void simulate_event_handler(struct k_work *work)
20 {
21 ARG_UNUSED(*work);
22
23 k_mutex_lock(&simulated_event_mutex, K_FOREVER);
24
25 if (simulated_event == 0) {
26 net_mgmt_event_notify(
27 NET_EVENT_CONN_IF_TIMEOUT,
28 simulated_event_iface
29 );
30 } else {
31 fatal_error = simulated_event;
32 net_mgmt_event_notify_with_info(
33 NET_EVENT_CONN_IF_FATAL_ERROR,
34 simulated_event_iface, &fatal_error, sizeof(fatal_error)
35 );
36 }
37
38 k_mutex_unlock(&simulated_event_mutex);
39 }
40
41 static K_WORK_DELAYABLE_DEFINE(simulate_event_work, simulate_event_handler);
42
43 /**
44 * @brief Simulates an event on the target iface.
45 *
46 * Do not attempt to simulate multiple events simultaneously -- only the last event requested
47 * will be fired.
48 *
49 * @param target - iface to simulate the event on.
50 * @param event - Event to simulate.
51 * If 0, simulate a timeout.
52 * Otherwise, simulate a fatal error with this value as the reason/info.
53 */
simulate_event(struct net_if * target,int event)54 static void simulate_event(struct net_if *target, int event)
55 {
56 k_mutex_lock(&simulated_event_mutex, K_FOREVER);
57
58 simulated_event = event;
59 simulated_event_iface = target;
60 k_work_reschedule(&simulate_event_work, SIMULATED_EVENT_DELAY_TIME);
61
62 k_mutex_unlock(&simulated_event_mutex);
63 }
64
simulate_timeout(struct net_if * target)65 static void simulate_timeout(struct net_if *target)
66 {
67 simulate_event(target, 0);
68 }
69
simulate_fatal_error(struct net_if * target,int reason)70 void simulate_fatal_error(struct net_if *target, int reason)
71 {
72 simulate_event(target, reason);
73 }
74
simulate_connection_loss(struct net_if * target)75 void simulate_connection_loss(struct net_if *target)
76 {
77 net_if_dormant_on(target);
78 }
79
80 /* Connectivity implementations */
81
inc_call_count(struct test_conn_data * data,bool a)82 static void inc_call_count(struct test_conn_data *data, bool a)
83 {
84 if (a) {
85 data->call_cnt_a += 1;
86 } else {
87 data->call_cnt_b += 1;
88 }
89 }
90
test_connect(struct conn_mgr_conn_binding * const binding,bool a)91 static int test_connect(struct conn_mgr_conn_binding *const binding, bool a)
92 {
93 struct test_conn_data *data = binding->ctx;
94
95 inc_call_count(data, a);
96
97 /* Fail immediately if requested */
98 if (data->api_err != 0) {
99 return data->api_err;
100 }
101
102 /* Fail after a delay if requested */
103 if (data->fatal_error) {
104 simulate_fatal_error(binding->iface, data->fatal_error);
105 return 0;
106 }
107
108 if (data->timeout) {
109 simulate_timeout(binding->iface);
110 return 0;
111 }
112
113 /* Succeed otherwise */
114
115 data->conn_bal += 1;
116
117 /* Mark iface as connected */
118 net_if_dormant_off(binding->iface);
119 return 0;
120 }
121
test_disconnect(struct conn_mgr_conn_binding * const binding,bool a)122 static int test_disconnect(struct conn_mgr_conn_binding *const binding, bool a)
123 {
124 struct test_conn_data *data = binding->ctx;
125
126 inc_call_count(data, a);
127
128 if (data->api_err != 0) {
129 return data->api_err;
130 }
131
132 data->conn_bal -= 1;
133
134 /* Mark iface as dormant (disconnected) */
135 net_if_dormant_on(binding->iface);
136 return 0;
137 }
138
opt_pointer(struct test_conn_data * data,int optname)139 char *opt_pointer(struct test_conn_data *data, int optname)
140 {
141 switch (optname) {
142 case TEST_CONN_OPT_X:
143 return data->data_x;
144 case TEST_CONN_OPT_Y:
145 return data->data_y;
146 }
147 return NULL;
148 }
149
test_set_opt_a(struct conn_mgr_conn_binding * const binding,int optname,const void * optval,size_t optlen)150 int test_set_opt_a(struct conn_mgr_conn_binding *const binding, int optname,
151 const void *optval, size_t optlen)
152 {
153 struct test_conn_data *data = binding->ctx;
154 char *target = opt_pointer(data, optname);
155 int len = MIN(optlen, TEST_CONN_DATA_LEN);
156
157 /* get/set opt are only implemented for implementation A */
158 inc_call_count(data, true);
159
160 if (target == NULL) {
161 return -ENOPROTOOPT;
162 }
163
164 if (data->api_err) {
165 return data->api_err;
166 }
167
168 (void)memset(target, 0, TEST_CONN_DATA_LEN);
169 (void)memcpy(target, optval, len);
170
171 return 0;
172 }
173
test_get_opt_a(struct conn_mgr_conn_binding * const binding,int optname,void * optval,size_t * optlen)174 int test_get_opt_a(struct conn_mgr_conn_binding *const binding, int optname,
175 void *optval, size_t *optlen)
176 {
177 struct test_conn_data *data = binding->ctx;
178 char *target = opt_pointer(data, optname);
179 int len;
180
181 /* get/set opt are only implemented for implementation A */
182 inc_call_count(data, true);
183
184 if (target == NULL) {
185 *optlen = 0;
186 return -ENOPROTOOPT;
187 }
188
189 len = MIN(strlen(target) + 1, *optlen);
190
191 if (data->api_err) {
192 *optlen = 0;
193 return data->api_err;
194 }
195 *optlen = len;
196 (void)memset(optval, 0, len);
197 (void)memcpy(optval, target, len-1);
198
199 return 0;
200 }
201
test_init(struct conn_mgr_conn_binding * const binding,bool a)202 static void test_init(struct conn_mgr_conn_binding *const binding, bool a)
203 {
204 struct test_conn_data *data = binding->ctx;
205
206 if (a) {
207 data->init_calls_a += 1;
208 } else {
209 data->init_calls_b += 1;
210 }
211
212 /* Mark the iface dormant (disconnected) on initialization */
213 net_if_dormant_on(binding->iface);
214 }
215
test_init_a(struct conn_mgr_conn_binding * const binding)216 static void test_init_a(struct conn_mgr_conn_binding *const binding)
217 {
218 test_init(binding, true);
219 }
220
test_init_b(struct conn_mgr_conn_binding * const binding)221 static void test_init_b(struct conn_mgr_conn_binding *const binding)
222 {
223 test_init(binding, false);
224 }
225
test_connect_a(struct conn_mgr_conn_binding * const binding)226 static int test_connect_a(struct conn_mgr_conn_binding *const binding)
227 {
228 return test_connect(binding, true);
229 }
230
test_connect_b(struct conn_mgr_conn_binding * const binding)231 static int test_connect_b(struct conn_mgr_conn_binding *const binding)
232 {
233 return test_connect(binding, false);
234 }
235
test_disconnect_a(struct conn_mgr_conn_binding * const binding)236 static int test_disconnect_a(struct conn_mgr_conn_binding *const binding)
237 {
238 return test_disconnect(binding, true);
239 }
240
test_disconnect_b(struct conn_mgr_conn_binding * const binding)241 static int test_disconnect_b(struct conn_mgr_conn_binding *const binding)
242 {
243 return test_disconnect(binding, false);
244 }
245
246 static struct conn_mgr_conn_api test_conn_api_a = {
247 .connect = test_connect_a,
248 .disconnect = test_disconnect_a,
249 .init = test_init_a,
250 .get_opt = test_get_opt_a,
251 .set_opt = test_set_opt_a
252 };
253
254 static struct conn_mgr_conn_api test_conn_api_b = {
255 .connect = test_connect_b,
256 .disconnect = test_disconnect_b,
257 .init = test_init_b,
258 };
259
260 static struct conn_mgr_conn_api test_conn_api_ni = {
261 .connect = test_connect_a,
262 .disconnect = test_disconnect_a,
263 };
264
265 /* Equivalent but distinct implementations */
266 CONN_MGR_CONN_DEFINE(TEST_L2_CONN_IMPL_A, &test_conn_api_a);
267 CONN_MGR_CONN_DEFINE(TEST_L2_CONN_IMPL_B, &test_conn_api_b);
268
269 /* Implementation without init */
270 CONN_MGR_CONN_DEFINE(TEST_L2_CONN_IMPL_NI, &test_conn_api_ni);
271
272 /* Bad implementation, should be handled gracefully */
273 CONN_MGR_CONN_DEFINE(TEST_L2_CONN_IMPL_N, NULL);
274