1 /*
2  * Copyright (c) 2023, Bjarki Arge Andreasen
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/device.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/net/socket.h>
10 #include <zephyr/net/net_if.h>
11 #include <zephyr/net/dns_resolve.h>
12 #include <zephyr/pm/device.h>
13 #include <zephyr/pm/device_runtime.h>
14 #include <string.h>
15 
16 #include <zephyr/drivers/cellular.h>
17 
18 #define SAMPLE_TEST_ENDPOINT_HOSTNAME		("test-endpoint.com")
19 #define SAMPLE_TEST_ENDPOINT_UDP_ECHO_PORT	(7780)
20 #define SAMPLE_TEST_ENDPOINT_UDP_RECEIVE_PORT	(7781)
21 #define SAMPLE_TEST_PACKET_SIZE			(1024)
22 #define SAMPLE_TEST_ECHO_PACKETS		(16)
23 #define SAMPLE_TEST_TRANSMIT_PACKETS		(128)
24 
25 const struct device *modem = DEVICE_DT_GET(DT_ALIAS(modem));
26 
27 static uint8_t sample_test_packet[SAMPLE_TEST_PACKET_SIZE];
28 static uint8_t sample_recv_buffer[SAMPLE_TEST_PACKET_SIZE];
29 static bool sample_test_dns_in_progress;
30 static struct dns_addrinfo sample_test_dns_addrinfo;
31 
32 K_SEM_DEFINE(dns_query_sem, 0, 1);
33 
sample_prng_random(void)34 static uint8_t sample_prng_random(void)
35 {
36 	static uint32_t prng_state = 1234;
37 
38 	prng_state = ((1103515245 * prng_state) + 12345) % (1U << 31);
39 	return (uint8_t)(prng_state & 0xFF);
40 }
41 
init_sample_test_packet(void)42 static void init_sample_test_packet(void)
43 {
44 	for (size_t i = 0; i < sizeof(sample_test_packet); i++) {
45 		sample_test_packet[i] = sample_prng_random();
46 	}
47 }
48 
print_cellular_info(void)49 static void print_cellular_info(void)
50 {
51 	int rc;
52 	int16_t rssi;
53 	char buffer[64];
54 
55 	rc = cellular_get_signal(modem, CELLULAR_SIGNAL_RSSI, &rssi);
56 	if (!rc) {
57 		printk("RSSI %d\n", rssi);
58 	}
59 
60 	rc = cellular_get_modem_info(modem, CELLULAR_MODEM_INFO_IMEI, &buffer[0], sizeof(buffer));
61 	if (!rc) {
62 		printk("IMEI: %s\n", buffer);
63 	}
64 	rc = cellular_get_modem_info(modem, CELLULAR_MODEM_INFO_MODEL_ID, &buffer[0],
65 				     sizeof(buffer));
66 	if (!rc) {
67 		printk("MODEL_ID: %s\n", buffer);
68 	}
69 	rc = cellular_get_modem_info(modem, CELLULAR_MODEM_INFO_MANUFACTURER, &buffer[0],
70 				     sizeof(buffer));
71 	if (!rc) {
72 		printk("MANUFACTURER: %s\n", buffer);
73 	}
74 	rc = cellular_get_modem_info(modem, CELLULAR_MODEM_INFO_SIM_IMSI, &buffer[0],
75 				     sizeof(buffer));
76 	if (!rc) {
77 		printk("SIM_IMSI: %s\n", buffer);
78 	}
79 	rc = cellular_get_modem_info(modem, CELLULAR_MODEM_INFO_SIM_ICCID, &buffer[0],
80 				     sizeof(buffer));
81 	if (!rc) {
82 		printk("SIM_ICCID: %s\n", buffer);
83 	}
84 	rc = cellular_get_modem_info(modem, CELLULAR_MODEM_INFO_FW_VERSION, &buffer[0],
85 				     sizeof(buffer));
86 	if (!rc) {
87 		printk("FW_VERSION: %s\n", buffer);
88 	}
89 }
90 
sample_dns_request_result(enum dns_resolve_status status,struct dns_addrinfo * info,void * user_data)91 static void sample_dns_request_result(enum dns_resolve_status status, struct dns_addrinfo *info,
92 				      void *user_data)
93 {
94 	if (sample_test_dns_in_progress == false) {
95 		return;
96 	}
97 
98 	if (status != DNS_EAI_INPROGRESS) {
99 		return;
100 	}
101 
102 	sample_test_dns_in_progress = false;
103 	sample_test_dns_addrinfo = *info;
104 	k_sem_give(&dns_query_sem);
105 }
106 
sample_dns_request(void)107 static int sample_dns_request(void)
108 {
109 	static uint16_t dns_id;
110 	int ret;
111 
112 	sample_test_dns_in_progress = true;
113 	ret = dns_get_addr_info(SAMPLE_TEST_ENDPOINT_HOSTNAME,
114 				DNS_QUERY_TYPE_A,
115 				&dns_id,
116 				sample_dns_request_result,
117 				NULL,
118 				19000);
119 	if (ret < 0) {
120 		return -EAGAIN;
121 	}
122 
123 	if (k_sem_take(&dns_query_sem, K_SECONDS(20)) < 0) {
124 		return -EAGAIN;
125 	}
126 
127 	return 0;
128 }
129 
sample_echo_packet(struct sockaddr * ai_addr,socklen_t ai_addrlen,uint16_t * port)130 int sample_echo_packet(struct sockaddr *ai_addr, socklen_t ai_addrlen, uint16_t *port)
131 {
132 	int ret;
133 	int socket_fd;
134 	uint32_t packets_sent = 0;
135 	uint32_t send_start_ms;
136 	uint32_t echo_received_ms;
137 	uint32_t accumulated_ms = 0;
138 
139 	printk("Opening UDP socket\n");
140 
141 	socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
142 	if (socket_fd < 0) {
143 		printk("Failed to open socket (%d)\n", errno);
144 		return -1;
145 	}
146 
147 	{
148 		const struct timeval tv = { .tv_sec = 10 };
149 
150 		if (zsock_setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
151 			printk("Failed to set socket receive timeout (%d)\n", errno);
152 			return -1;
153 		}
154 	}
155 
156 	printk("Socket opened\n");
157 
158 	*port = htons(SAMPLE_TEST_ENDPOINT_UDP_ECHO_PORT);
159 
160 	for (uint32_t i = 0; i < SAMPLE_TEST_ECHO_PACKETS; i++) {
161 		printk("Sending echo packet\n");
162 		send_start_ms = k_uptime_get_32();
163 
164 		ret = sendto(socket_fd, sample_test_packet, sizeof(sample_test_packet), 0,
165 			     ai_addr, ai_addrlen);
166 
167 		if (ret < sizeof(sample_test_packet)) {
168 			printk("Failed to send sample test packet\n");
169 			continue;
170 		}
171 
172 		printk("Receiving echoed packet\n");
173 		ret = recv(socket_fd, sample_recv_buffer, sizeof(sample_recv_buffer), 0);
174 		if (ret != sizeof(sample_test_packet)) {
175 			if (ret == -1) {
176 				printk("Failed to receive echoed sample test packet (%d)\n", errno);
177 			} else {
178 				printk("Echoed sample test packet has incorrect size (%d)\n", ret);
179 			}
180 			continue;
181 		}
182 
183 		echo_received_ms = k_uptime_get_32();
184 
185 		if (memcmp(sample_test_packet, sample_recv_buffer,
186 			   sizeof(sample_recv_buffer)) != 0) {
187 			printk("Echoed sample test packet data mismatch\n");
188 			continue;
189 		}
190 
191 		packets_sent++;
192 		accumulated_ms += echo_received_ms - send_start_ms;
193 
194 		printk("Echo transmit time %ums\n", echo_received_ms - send_start_ms);
195 	}
196 
197 	printk("Successfully sent and received %u of %u packets\n", packets_sent,
198 	       SAMPLE_TEST_ECHO_PACKETS);
199 
200 	if (packets_sent > 0) {
201 		printk("Average time per successful echo: %u ms\n",
202 		accumulated_ms / packets_sent);
203 	}
204 
205 	printk("Close UDP socket\n");
206 
207 	ret = close(socket_fd);
208 	if (ret < 0) {
209 		printk("Failed to close socket\n");
210 		return -1;
211 	}
212 
213 	return 0;
214 }
215 
216 
sample_transmit_packets(struct sockaddr * ai_addr,socklen_t ai_addrlen,uint16_t * port)217 int sample_transmit_packets(struct sockaddr *ai_addr, socklen_t ai_addrlen, uint16_t *port)
218 {
219 	int ret;
220 	int socket_fd;
221 	uint32_t packets_sent = 0;
222 	uint32_t packets_received;
223 	uint32_t packets_dropped;
224 	uint32_t send_start_ms;
225 	uint32_t send_end_ms;
226 
227 	printk("Opening UDP socket\n");
228 
229 	socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
230 	if (socket_fd < 0) {
231 		printk("Failed to open socket\n");
232 		return -1;
233 	}
234 
235 	printk("Socket opened\n");
236 
237 	*port = htons(SAMPLE_TEST_ENDPOINT_UDP_RECEIVE_PORT);
238 
239 	printk("Sending %u packets\n", SAMPLE_TEST_TRANSMIT_PACKETS);
240 	send_start_ms = k_uptime_get_32();
241 	for (uint32_t i = 0; i < SAMPLE_TEST_TRANSMIT_PACKETS; i++) {
242 		ret = sendto(socket_fd, sample_test_packet, sizeof(sample_test_packet), 0,
243 			     ai_addr, ai_addrlen);
244 
245 		if (ret < sizeof(sample_test_packet)) {
246 			printk("Failed to send sample test packet\n");
247 			break;
248 		}
249 
250 		packets_sent++;
251 	}
252 	send_end_ms = k_uptime_get_32();
253 
254 	printk("Awaiting response from server\n");
255 	ret = recv(socket_fd, sample_recv_buffer, sizeof(sample_recv_buffer), 0);
256 	if (ret != 2) {
257 		printk("Invalid response\n");
258 		return -1;
259 	}
260 
261 	packets_received = sample_recv_buffer[0];
262 	packets_dropped = sample_recv_buffer[1];
263 	printk("Server received %u/%u packets\n", packets_received, packets_sent);
264 	printk("Server dropped %u packets\n", packets_dropped);
265 	printk("Time elapsed sending packets %ums\n", send_end_ms - send_start_ms);
266 	printk("Throughput %u bytes/s\n",
267 	       ((SAMPLE_TEST_PACKET_SIZE * SAMPLE_TEST_TRANSMIT_PACKETS) * 1000) /
268 	       (send_end_ms - send_start_ms));
269 
270 	printk("Close UDP socket\n");
271 	ret = close(socket_fd);
272 	if (ret < 0) {
273 		printk("Failed to close socket\n");
274 		return -1;
275 	}
276 
277 	return 0;
278 }
279 
main(void)280 int main(void)
281 {
282 	struct net_if *const iface = net_if_get_first_by_type(&NET_L2_GET_NAME(PPP));
283 	uint16_t *port;
284 	int ret;
285 
286 	init_sample_test_packet();
287 
288 	printk("Powering on modem\n");
289 	pm_device_action_run(modem, PM_DEVICE_ACTION_RESUME);
290 
291 	printk("Bring up network interface\n");
292 	ret = net_if_up(iface);
293 	if (ret < 0) {
294 		printk("Failed to bring up network interface\n");
295 		return -1;
296 	}
297 
298 	printk("Waiting for L4 connected\n");
299 	ret = net_mgmt_event_wait_on_iface(iface, NET_EVENT_L4_CONNECTED, NULL, NULL, NULL,
300 					   K_SECONDS(120));
301 
302 	if (ret != 0) {
303 		printk("L4 was not connected in time\n");
304 		return -1;
305 	}
306 
307 	printk("Waiting for DNS server added\n");
308 	ret = net_mgmt_event_wait_on_iface(iface, NET_EVENT_DNS_SERVER_ADD, NULL, NULL, NULL,
309 					   K_SECONDS(10));
310 	if (ret) {
311 		printk("DNS server was not added in time\n");
312 		return -1;
313 	}
314 
315 	printk("Retrieving cellular info\n");
316 	print_cellular_info();
317 
318 	printk("Performing DNS lookup of %s\n", SAMPLE_TEST_ENDPOINT_HOSTNAME);
319 	ret = sample_dns_request();
320 	if (ret < 0) {
321 		printk("DNS query failed\n");
322 		return -1;
323 	}
324 
325 	{
326 		char ip_str[INET6_ADDRSTRLEN];
327 		const void *src;
328 
329 		switch (sample_test_dns_addrinfo.ai_addr.sa_family) {
330 		case AF_INET:
331 			src = &net_sin(&sample_test_dns_addrinfo.ai_addr)->sin_addr;
332 			port = &net_sin(&sample_test_dns_addrinfo.ai_addr)->sin_port;
333 			break;
334 		case AF_INET6:
335 			src = &net_sin6(&sample_test_dns_addrinfo.ai_addr)->sin6_addr;
336 			port = &net_sin6(&sample_test_dns_addrinfo.ai_addr)->sin6_port;
337 			break;
338 		default:
339 			printk("Unsupported address family\n");
340 			return -1;
341 		}
342 		inet_ntop(sample_test_dns_addrinfo.ai_addr.sa_family, src, ip_str, sizeof(ip_str));
343 		printk("Resolved to %s\n", ip_str);
344 	}
345 
346 	ret = sample_echo_packet(&sample_test_dns_addrinfo.ai_addr,
347 				 sample_test_dns_addrinfo.ai_addrlen, port);
348 
349 	if (ret < 0) {
350 		printk("Failed to send echos\n");
351 		return -1;
352 	}
353 
354 	ret = sample_transmit_packets(&sample_test_dns_addrinfo.ai_addr,
355 				      sample_test_dns_addrinfo.ai_addrlen, port);
356 
357 	if (ret < 0) {
358 		printk("Failed to send packets\n");
359 		return -1;
360 	}
361 
362 	printk("Restart modem\n");
363 	ret = pm_device_action_run(modem, PM_DEVICE_ACTION_SUSPEND);
364 	if (ret != 0) {
365 		printk("Failed to power down modem\n");
366 		return -1;
367 	}
368 
369 	pm_device_action_run(modem, PM_DEVICE_ACTION_RESUME);
370 
371 	printk("Waiting for L4 connected\n");
372 	ret = net_mgmt_event_wait_on_iface(iface, NET_EVENT_L4_CONNECTED, NULL, NULL, NULL,
373 					   K_SECONDS(60));
374 	if (ret != 0) {
375 		printk("L4 was not connected in time\n");
376 		return -1;
377 	}
378 	printk("L4 connected\n");
379 
380 	/* Wait a bit to avoid (unsuccessfully) trying to send the first echo packet too quickly. */
381 	k_sleep(K_SECONDS(5));
382 
383 	ret = sample_echo_packet(&sample_test_dns_addrinfo.ai_addr,
384 				 sample_test_dns_addrinfo.ai_addrlen, port);
385 
386 	if (ret < 0) {
387 		printk("Failed to send echos after restart\n");
388 		return -1;
389 	}
390 
391 	ret = net_if_down(iface);
392 	if (ret < 0) {
393 		printk("Failed to bring down network interface\n");
394 		return -1;
395 	}
396 
397 	printk("Powering down modem\n");
398 	ret = pm_device_action_run(modem, PM_DEVICE_ACTION_SUSPEND);
399 	if (ret != 0) {
400 		printk("Failed to power down modem\n");
401 		return -1;
402 	}
403 
404 	printk("Sample complete\n");
405 	return 0;
406 }
407