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/posix/netinet/in.h>
17 #include <zephyr/posix/sys/socket.h>
18 #include <zephyr/posix/arpa/inet.h>
19 #include <zephyr/posix/unistd.h>
20 #include <zephyr/posix/poll.h>
21 
22 #include <zephyr/drivers/cellular.h>
23 
24 #define L4_EVENT_MASK \
25 	(NET_EVENT_L4_CONNECTED | NET_EVENT_L4_DISCONNECTED | NET_EVENT_DNS_SERVER_ADD)
26 #define SAMPLE_TEST_ENDPOINT_HOSTNAME           CONFIG_SAMPLE_CELLULAR_MODEM_ENDPOINT_HOSTNAME
27 #define SAMPLE_TEST_ENDPOINT_UDP_ECHO_PORT	(7780)
28 #define SAMPLE_TEST_ENDPOINT_UDP_RECEIVE_PORT	(7781)
29 #define SAMPLE_TEST_PACKET_SIZE			(1024)
30 #define SAMPLE_TEST_ECHO_PACKETS		(16)
31 #define SAMPLE_TEST_TRANSMIT_PACKETS		(128)
32 #define L4_CONNECTED 1
33 #define L4_DNS_ADDED 2
34 
35 const struct device *modem = DEVICE_DT_GET(DT_ALIAS(modem));
36 
37 static uint8_t sample_test_packet[SAMPLE_TEST_PACKET_SIZE];
38 static uint8_t sample_recv_buffer[SAMPLE_TEST_PACKET_SIZE];
39 static bool sample_test_dns_in_progress;
40 static struct dns_addrinfo sample_test_dns_addrinfo;
41 struct net_if *ppp_iface;
42 K_EVENT_DEFINE(l4_event);
43 K_SEM_DEFINE(dns_query_sem, 0, 1);
44 
sample_prng_random(void)45 static uint8_t sample_prng_random(void)
46 {
47 	static uint32_t prng_state = 1234;
48 
49 	prng_state = ((1103515245 * prng_state) + 12345) % (1U << 31);
50 	return (uint8_t)(prng_state & 0xFF);
51 }
52 
init_sample_test_packet(void)53 static void init_sample_test_packet(void)
54 {
55 	for (size_t i = 0; i < sizeof(sample_test_packet); i++) {
56 		sample_test_packet[i] = sample_prng_random();
57 	}
58 }
59 
print_cellular_info(void)60 static void print_cellular_info(void)
61 {
62 	int rc;
63 	int16_t rssi;
64 	char buffer[64];
65 
66 	rc = cellular_get_signal(modem, CELLULAR_SIGNAL_RSSI, &rssi);
67 	if (!rc) {
68 		printk("RSSI %d\n", rssi);
69 	}
70 
71 	rc = cellular_get_modem_info(modem, CELLULAR_MODEM_INFO_IMEI, &buffer[0], sizeof(buffer));
72 	if (!rc) {
73 		printk("IMEI: %s\n", buffer);
74 	}
75 	rc = cellular_get_modem_info(modem, CELLULAR_MODEM_INFO_MODEL_ID, &buffer[0],
76 				     sizeof(buffer));
77 	if (!rc) {
78 		printk("MODEL_ID: %s\n", buffer);
79 	}
80 	rc = cellular_get_modem_info(modem, CELLULAR_MODEM_INFO_MANUFACTURER, &buffer[0],
81 				     sizeof(buffer));
82 	if (!rc) {
83 		printk("MANUFACTURER: %s\n", buffer);
84 	}
85 	rc = cellular_get_modem_info(modem, CELLULAR_MODEM_INFO_SIM_IMSI, &buffer[0],
86 				     sizeof(buffer));
87 	if (!rc) {
88 		printk("SIM_IMSI: %s\n", buffer);
89 	}
90 	rc = cellular_get_modem_info(modem, CELLULAR_MODEM_INFO_SIM_ICCID, &buffer[0],
91 				     sizeof(buffer));
92 	if (!rc) {
93 		printk("SIM_ICCID: %s\n", buffer);
94 	}
95 	rc = cellular_get_modem_info(modem, CELLULAR_MODEM_INFO_FW_VERSION, &buffer[0],
96 				     sizeof(buffer));
97 	if (!rc) {
98 		printk("FW_VERSION: %s\n", buffer);
99 	}
100 }
101 
102 #ifdef CONFIG_SAMPLE_CELLULAR_MODEM_AUTO_APN
103 
104 struct apn_profile {
105 	const char *apn;
106 	const char *imsi_list;
107 };
108 
109 /* Build the static table */
110 static const struct apn_profile apn_profiles[] = {
111 	{ CONFIG_SAMPLE_CELLULAR_APN_0, CONFIG_SAMPLE_CELLULAR_IMSI_LIST_0 },
112 	{ CONFIG_SAMPLE_CELLULAR_APN_1, CONFIG_SAMPLE_CELLULAR_IMSI_LIST_1 },
113 	{ CONFIG_SAMPLE_CELLULAR_APN_2, CONFIG_SAMPLE_CELLULAR_IMSI_LIST_2 },
114 	{ CONFIG_SAMPLE_CELLULAR_APN_3, CONFIG_SAMPLE_CELLULAR_IMSI_LIST_3 },
115 };
116 
117 
118 /* Helper function to skip whitespace */
skip_whitespace(const char * ptr)119 static const char *skip_whitespace(const char *ptr)
120 {
121 	while (*ptr == ' ' || *ptr == '\t') {
122 		++ptr;
123 	}
124 	return ptr;
125 }
126 
127 /* Helper function to find the end of current profile entry */
list_matches_imsi(const char * list,const char * imsi)128 static bool list_matches_imsi(const char *list, const char *imsi)
129 {
130 	for (const char *p = list; *p; ) {
131 		p = skip_whitespace(p);
132 		if (!*p) {
133 			break;
134 		}
135 
136 		/* copy one token from the list */
137 		char tok[7];
138 		size_t len = 0;
139 
140 		while (*p && *p != ' ' && *p != '\t' && *p != ',' && len < sizeof(tok) - 1) {
141 			tok[len++] = *p++;
142 		}
143 		tok[len] = '\0';
144 
145 		if (len >= 5 && len <= 6 && !strncmp(imsi, tok, len)) {
146 			return true;    /* prefix matches */
147 		}
148 	}
149 	return false;
150 }
151 
modem_cellular_find_apn(char * dst,size_t dst_sz,const char * key)152 static int modem_cellular_find_apn(char *dst, size_t dst_sz, const char *key)
153 {
154 	for (size_t i = 0; i < ARRAY_SIZE(apn_profiles); i++) {
155 		const struct apn_profile *p = &apn_profiles[i];
156 
157 		if (p->apn[0] == '\0') {
158 			continue;
159 		}
160 
161 		if (p->apn[0] && list_matches_imsi(p->imsi_list, key)) {
162 			strncpy(dst, p->apn, dst_sz - 1);
163 			dst[dst_sz - 1] = '\0';
164 			return 0;
165 		}
166 	}
167 
168 	return -ENOENT;
169 }
170 
modem_event_cb(const struct device * dev,enum cellular_event evt,const void * payload,void * user_data)171 static void modem_event_cb(const struct device *dev, enum cellular_event evt, const void *payload,
172 			   void *user_data)
173 {
174 	ARG_UNUSED(user_data);
175 
176 	if (evt != CELLULAR_EVENT_MODEM_INFO_CHANGED) {
177 		return;
178 	}
179 
180 	const struct cellular_evt_modem_info *mi = payload;
181 
182 	if (!mi || mi->field != CELLULAR_MODEM_INFO_SIM_IMSI) {
183 		return; /* not the IMSI notification */
184 	}
185 
186 	char imsi[32] = {0};
187 
188 	if (cellular_get_modem_info(dev, CELLULAR_MODEM_INFO_SIM_IMSI, imsi, sizeof(imsi)) != 0) {
189 		return;
190 	}
191 
192 	/* Buffer for the APN we may discover */
193 	char apn[32] = {0};
194 
195 	/* Try MCC+MNC with 6 digits first, then 5 digits */
196 	for (size_t len = 6; len >= 5; len--) {
197 		if (strlen(imsi) < len) {
198 			continue;
199 		}
200 
201 		char key[7] = {0};
202 
203 		memcpy(key, imsi, len);
204 
205 		if (modem_cellular_find_apn(apn, sizeof(apn), key) == 0) {
206 			int rc = cellular_set_apn(dev, apn);
207 
208 			switch (rc) {
209 			case 0:
210 				printk("Auto-selected APN: %s\n", apn);
211 				break;
212 			case -EALREADY:
213 				printk("APN %s already active\n", apn);
214 				break;
215 			case -EBUSY:
216 				printk("Driver busy, cannot change APN now\n");
217 				break;
218 			default:
219 				printk("Driver rejected APN %s (err %d)\n", apn, rc);
220 				break;
221 			}
222 			return;
223 		}
224 	}
225 
226 	printk("No APN profile matches IMSI %s - waiting for manual APN\n", imsi);
227 }
228 
229 #endif
230 
sample_dns_request_result(enum dns_resolve_status status,struct dns_addrinfo * info,void * user_data)231 static void sample_dns_request_result(enum dns_resolve_status status, struct dns_addrinfo *info,
232 				      void *user_data)
233 {
234 	if (sample_test_dns_in_progress == false) {
235 		return;
236 	}
237 
238 	if (status != DNS_EAI_INPROGRESS) {
239 		return;
240 	}
241 
242 	sample_test_dns_in_progress = false;
243 	sample_test_dns_addrinfo = *info;
244 	k_sem_give(&dns_query_sem);
245 }
246 
sample_dns_request(void)247 static int sample_dns_request(void)
248 {
249 	static uint16_t dns_id;
250 	int ret;
251 
252 	sample_test_dns_in_progress = true;
253 	ret = dns_get_addr_info(SAMPLE_TEST_ENDPOINT_HOSTNAME,
254 				DNS_QUERY_TYPE_A,
255 				&dns_id,
256 				sample_dns_request_result,
257 				NULL,
258 				19000);
259 	if (ret < 0) {
260 		return -EAGAIN;
261 	}
262 
263 	if (k_sem_take(&dns_query_sem, K_SECONDS(20)) < 0) {
264 		return -EAGAIN;
265 	}
266 
267 	return 0;
268 }
269 
sample_echo_packet(struct sockaddr * ai_addr,socklen_t ai_addrlen,uint16_t * port)270 int sample_echo_packet(struct sockaddr *ai_addr, socklen_t ai_addrlen, uint16_t *port)
271 {
272 	int ret;
273 	int socket_fd;
274 	uint32_t packets_sent = 0;
275 	uint32_t send_start_ms;
276 	uint32_t echo_received_ms;
277 	uint32_t accumulated_ms = 0;
278 
279 	printk("Opening UDP socket\n");
280 
281 	socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
282 	if (socket_fd < 0) {
283 		printk("Failed to open socket (%d)\n", errno);
284 		return -1;
285 	}
286 
287 	{
288 		const struct timeval tv = { .tv_sec = 10 };
289 
290 		if (setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
291 			printk("Failed to set socket receive timeout (%d)\n", errno);
292 			return -1;
293 		}
294 	}
295 
296 	printk("Socket opened\n");
297 
298 	*port = htons(SAMPLE_TEST_ENDPOINT_UDP_ECHO_PORT);
299 
300 	for (uint32_t i = 0; i < SAMPLE_TEST_ECHO_PACKETS; i++) {
301 		printk("Sending echo packet\n");
302 		send_start_ms = k_uptime_get_32();
303 
304 		ret = sendto(socket_fd, sample_test_packet, sizeof(sample_test_packet), 0,
305 			     ai_addr, ai_addrlen);
306 
307 		if (ret < sizeof(sample_test_packet)) {
308 			printk("Failed to send sample test packet\n");
309 			continue;
310 		}
311 
312 		printk("Receiving echoed packet\n");
313 		ret = recv(socket_fd, sample_recv_buffer, sizeof(sample_recv_buffer), 0);
314 		if (ret != sizeof(sample_test_packet)) {
315 			if (ret == -1) {
316 				printk("Failed to receive echoed sample test packet (%d)\n", errno);
317 			} else {
318 				printk("Echoed sample test packet has incorrect size (%d)\n", ret);
319 			}
320 			continue;
321 		}
322 
323 		echo_received_ms = k_uptime_get_32();
324 
325 		if (memcmp(sample_test_packet, sample_recv_buffer,
326 			   sizeof(sample_recv_buffer)) != 0) {
327 			printk("Echoed sample test packet data mismatch\n");
328 			continue;
329 		}
330 
331 		packets_sent++;
332 		accumulated_ms += echo_received_ms - send_start_ms;
333 
334 		printk("Echo transmit time %ums\n", echo_received_ms - send_start_ms);
335 	}
336 
337 	printk("Successfully sent and received %u of %u packets\n", packets_sent,
338 	       SAMPLE_TEST_ECHO_PACKETS);
339 
340 	if (packets_sent > 0) {
341 		printk("Average time per successful echo: %u ms\n",
342 		accumulated_ms / packets_sent);
343 	}
344 
345 	printk("Close UDP socket\n");
346 
347 	ret = close(socket_fd);
348 	if (ret < 0) {
349 		printk("Failed to close socket\n");
350 		return -1;
351 	}
352 
353 	return 0;
354 }
355 
356 
sample_transmit_packets(struct sockaddr * ai_addr,socklen_t ai_addrlen,uint16_t * port)357 int sample_transmit_packets(struct sockaddr *ai_addr, socklen_t ai_addrlen, uint16_t *port)
358 {
359 	int ret;
360 	int socket_fd;
361 	uint32_t packets_sent = 0;
362 	uint32_t packets_received;
363 	uint32_t packets_dropped;
364 	uint32_t send_start_ms;
365 	uint32_t send_end_ms;
366 
367 	printk("Opening UDP socket\n");
368 
369 	socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
370 	if (socket_fd < 0) {
371 		printk("Failed to open socket\n");
372 		return -1;
373 	}
374 
375 	printk("Socket opened\n");
376 
377 	*port = htons(SAMPLE_TEST_ENDPOINT_UDP_RECEIVE_PORT);
378 
379 	printk("Sending %u packets\n", SAMPLE_TEST_TRANSMIT_PACKETS);
380 	send_start_ms = k_uptime_get_32();
381 	for (uint32_t i = 0; i < SAMPLE_TEST_TRANSMIT_PACKETS; i++) {
382 		ret = sendto(socket_fd, sample_test_packet, sizeof(sample_test_packet), 0,
383 			     ai_addr, ai_addrlen);
384 
385 		if (ret < sizeof(sample_test_packet)) {
386 			printk("Failed to send sample test packet\n");
387 			break;
388 		}
389 
390 		packets_sent++;
391 	}
392 	send_end_ms = k_uptime_get_32();
393 
394 	printk("Awaiting response from server\n");
395 	ret = recv(socket_fd, sample_recv_buffer, sizeof(sample_recv_buffer), 0);
396 	if (ret != 2) {
397 		printk("Invalid response\n");
398 		return -1;
399 	}
400 
401 	packets_received = sample_recv_buffer[0];
402 	packets_dropped = sample_recv_buffer[1];
403 	printk("Server received %u/%u packets\n", packets_received, packets_sent);
404 	printk("Server dropped %u packets\n", packets_dropped);
405 	printk("Time elapsed sending packets %ums\n", send_end_ms - send_start_ms);
406 	printk("Throughput %u bytes/s\n",
407 	       ((SAMPLE_TEST_PACKET_SIZE * SAMPLE_TEST_TRANSMIT_PACKETS) * 1000) /
408 	       (send_end_ms - send_start_ms));
409 
410 	printk("Close UDP socket\n");
411 	ret = close(socket_fd);
412 	if (ret < 0) {
413 		printk("Failed to close socket\n");
414 		return -1;
415 	}
416 
417 	return 0;
418 }
419 
l4_event_handler(uint64_t event,struct net_if * iface,void * info,size_t info_length,void * user_data)420 static void l4_event_handler(uint64_t event, struct net_if *iface, void *info, size_t info_length,
421 			     void *user_data)
422 {
423 	if (iface != ppp_iface) {
424 		return;
425 	}
426 
427 	switch (event) {
428 	case NET_EVENT_L4_CONNECTED:
429 		k_event_post(&l4_event, L4_CONNECTED);
430 		break;
431 	case NET_EVENT_DNS_SERVER_ADD:
432 		k_event_post(&l4_event, L4_DNS_ADDED);
433 		break;
434 	case NET_EVENT_L4_DISCONNECTED:
435 		k_event_set(&l4_event, 0);
436 		break;
437 	default:
438 		break;
439 	}
440 }
441 
442 NET_MGMT_REGISTER_EVENT_HANDLER(l4_events, L4_EVENT_MASK, l4_event_handler, NULL);
443 
main(void)444 int main(void)
445 {
446 	uint16_t *port;
447 	int ret;
448 
449 #ifdef CONFIG_SAMPLE_CELLULAR_MODEM_AUTO_APN
450 	/* subscribe before powering the modem so we catch the IMSI event */
451 	cellular_set_callback(modem, CELLULAR_EVENT_MODEM_INFO_CHANGED, modem_event_cb, NULL);
452 #endif
453 
454 	init_sample_test_packet();
455 
456 	ppp_iface = net_if_get_first_by_type(&NET_L2_GET_NAME(PPP));
457 
458 	printk("Powering on modem\n");
459 	pm_device_action_run(modem, PM_DEVICE_ACTION_RESUME);
460 
461 	printk("Bring up network interface\n");
462 	ret = net_if_up(ppp_iface);
463 	if (ret < 0) {
464 		printk("Failed to bring up network interface\n");
465 		return -1;
466 	}
467 
468 	printk("Waiting for L4 connected\n");
469 	ret = k_event_wait(&l4_event, L4_CONNECTED, false, K_SECONDS(120));
470 
471 	if (ret != L4_CONNECTED) {
472 		printk("L4 was not connected in time\n");
473 		return -1;
474 	}
475 
476 	printk("Waiting for DNS server added\n");
477 	ret = k_event_wait(&l4_event, L4_DNS_ADDED, false, K_SECONDS(10));
478 	if (ret != L4_DNS_ADDED) {
479 		printk("DNS server was not added in time\n");
480 		return -1;
481 	}
482 
483 	printk("Retrieving cellular info\n");
484 	print_cellular_info();
485 
486 	printk("Performing DNS lookup of %s\n", SAMPLE_TEST_ENDPOINT_HOSTNAME);
487 	ret = sample_dns_request();
488 	if (ret < 0) {
489 		printk("DNS query failed\n");
490 		return -1;
491 	}
492 
493 	{
494 		char ip_str[INET6_ADDRSTRLEN];
495 		const void *src;
496 
497 		switch (sample_test_dns_addrinfo.ai_addr.sa_family) {
498 		case AF_INET:
499 			src = &net_sin(&sample_test_dns_addrinfo.ai_addr)->sin_addr;
500 			port = &net_sin(&sample_test_dns_addrinfo.ai_addr)->sin_port;
501 			break;
502 		case AF_INET6:
503 			src = &net_sin6(&sample_test_dns_addrinfo.ai_addr)->sin6_addr;
504 			port = &net_sin6(&sample_test_dns_addrinfo.ai_addr)->sin6_port;
505 			break;
506 		default:
507 			printk("Unsupported address family\n");
508 			return -1;
509 		}
510 		inet_ntop(sample_test_dns_addrinfo.ai_addr.sa_family, src, ip_str, sizeof(ip_str));
511 		printk("Resolved to %s\n", ip_str);
512 	}
513 
514 	ret = sample_echo_packet(&sample_test_dns_addrinfo.ai_addr,
515 				 sample_test_dns_addrinfo.ai_addrlen, port);
516 
517 	if (ret < 0) {
518 		printk("Failed to send echos\n");
519 		return -1;
520 	}
521 
522 	ret = sample_transmit_packets(&sample_test_dns_addrinfo.ai_addr,
523 				      sample_test_dns_addrinfo.ai_addrlen, port);
524 
525 	if (ret < 0) {
526 		printk("Failed to send packets\n");
527 		return -1;
528 	}
529 
530 	printk("Restart modem\n");
531 	ret = pm_device_action_run(modem, PM_DEVICE_ACTION_SUSPEND);
532 	if (ret != 0) {
533 		printk("Failed to power down modem\n");
534 		return -1;
535 	}
536 
537 	pm_device_action_run(modem, PM_DEVICE_ACTION_RESUME);
538 
539 	printk("Waiting for L4 connected\n");
540 	ret = k_event_wait(&l4_event, L4_CONNECTED, false, K_SECONDS(120));
541 	if (ret != L4_CONNECTED) {
542 		printk("L4 was not connected in time\n");
543 		return -1;
544 	}
545 	printk("L4 connected\n");
546 
547 	/* Wait a bit to avoid (unsuccessfully) trying to send the first echo packet too quickly. */
548 	k_sleep(K_SECONDS(5));
549 
550 	ret = sample_echo_packet(&sample_test_dns_addrinfo.ai_addr,
551 				 sample_test_dns_addrinfo.ai_addrlen, port);
552 
553 	if (ret < 0) {
554 		printk("Failed to send echos after restart\n");
555 		return -1;
556 	}
557 
558 	ret = net_if_down(ppp_iface);
559 	if (ret < 0) {
560 		printk("Failed to bring down network interface\n");
561 		return -1;
562 	}
563 
564 	printk("Powering down modem\n");
565 	ret = pm_device_action_run(modem, PM_DEVICE_ACTION_SUSPEND);
566 	if (ret != 0) {
567 		printk("Failed to power down modem\n");
568 		return -1;
569 	}
570 
571 	printk("Sample complete\n");
572 	return 0;
573 }
574