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