1 /*
2  * Copyright (c) 2024 Witekio
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/logging/log.h>
9 #include <zephyr/net/net_mgmt.h>
10 #include <zephyr/net/socket.h>
11 #include <zephyr/net/coap_client.h>
12 #include <inttypes.h>
13 #include <errno.h>
14 
15 LOG_MODULE_REGISTER(coap_download, LOG_LEVEL_INF);
16 
17 static K_SEM_DEFINE(net_connected_sem, 0, 1);
18 static K_SEM_DEFINE(coap_done_sem, 0, 1);
19 static int64_t start_time;
20 
21 /* This struct contains (potentially large) TX and RX buffers, so allocate statically */
22 static struct coap_client client = {0};
23 
net_event_handler(uint32_t mgmt_event,struct net_if * iface,void * info,size_t info_length,void * user_data)24 static void net_event_handler(uint32_t mgmt_event, struct net_if *iface, void *info,
25 			      size_t info_length, void *user_data)
26 {
27 	if (NET_EVENT_L4_CONNECTED == mgmt_event) {
28 		k_sem_give(&net_connected_sem);
29 	}
30 }
31 
32 NET_MGMT_REGISTER_EVENT_HANDLER(l4_conn_handler, NET_EVENT_L4_CONNECTED, net_event_handler, NULL);
33 
on_coap_response(int16_t result_code,size_t offset,const uint8_t * payload,size_t len,bool last_block,void * user_data)34 static void on_coap_response(int16_t result_code, size_t offset, const uint8_t *payload, size_t len,
35 			     bool last_block, void *user_data)
36 {
37 	LOG_INF("CoAP response, result_code=%d, offset=%u, len=%u", result_code, offset, len);
38 
39 	if ((COAP_RESPONSE_CODE_CONTENT == result_code) && last_block) {
40 		int64_t elapsed_time = k_uptime_delta(&start_time);
41 		size_t total_size = offset + len;
42 
43 		LOG_INF("CoAP download done, got %u bytes in %" PRId64 " ms", total_size,
44 			elapsed_time);
45 		k_sem_give(&coap_done_sem);
46 	} else if (COAP_RESPONSE_CODE_CONTENT != result_code) {
47 		LOG_ERR("Error during CoAP download, result_code=%d", result_code);
48 		k_sem_give(&coap_done_sem);
49 	}
50 }
51 
do_coap_download(struct sockaddr * sa)52 static void do_coap_download(struct sockaddr *sa)
53 {
54 	int ret;
55 	int sockfd;
56 	struct coap_client_request request = {.method = COAP_METHOD_GET,
57 					      .confirmable = true,
58 					      .path = CONFIG_NET_SAMPLE_COAP_RESOURCE_PATH,
59 					      .payload = NULL,
60 					      .len = 0,
61 					      .cb = on_coap_response,
62 					      .options = NULL,
63 					      .num_options = 0,
64 					      .user_data = NULL};
65 
66 	LOG_INF("Starting CoAP download using %s", (AF_INET == sa->sa_family) ? "IPv4" : "IPv6");
67 
68 	sockfd = zsock_socket(sa->sa_family, SOCK_DGRAM, 0);
69 	if (sockfd < 0) {
70 		LOG_ERR("Failed to create socket, err %d", errno);
71 		return;
72 	}
73 
74 	start_time = k_uptime_get();
75 
76 	ret = coap_client_req(&client, sockfd, sa, &request, NULL);
77 	if (ret) {
78 		LOG_ERR("Failed to send CoAP request, err %d", ret);
79 		return;
80 	}
81 
82 	/* Wait for CoAP request to complete */
83 	k_sem_take(&coap_done_sem, K_FOREVER);
84 
85 	zsock_close(sockfd);
86 }
87 
main(void)88 int main(void)
89 {
90 	int ret;
91 
92 	ret = coap_client_init(&client, NULL);
93 	if (ret) {
94 		LOG_ERR("Failed to init coap client, err %d", ret);
95 		return ret;
96 	}
97 
98 	k_sem_take(&net_connected_sem, K_FOREVER);
99 	LOG_INF("Network L4 is connected");
100 
101 	struct sockaddr sa;
102 
103 #if defined(CONFIG_NET_IPV4)
104 	struct sockaddr_in *addr4 = (struct sockaddr_in *)&sa;
105 
106 	addr4->sin_family = AF_INET;
107 	addr4->sin_port = htons(CONFIG_NET_SAMPLE_COAP_SERVER_PORT);
108 	zsock_inet_pton(AF_INET, CONFIG_NET_CONFIG_PEER_IPV4_ADDR, &addr4->sin_addr);
109 
110 	do_coap_download(&sa);
111 #endif
112 
113 #if defined(CONFIG_NET_IPV6)
114 	struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&sa;
115 
116 	addr6->sin6_family = AF_INET6;
117 	addr6->sin6_port = htons(CONFIG_NET_SAMPLE_COAP_SERVER_PORT);
118 	zsock_inet_pton(AF_INET6, CONFIG_NET_CONFIG_PEER_IPV6_ADDR, &addr6->sin6_addr);
119 
120 	do_coap_download(&sa);
121 #endif
122 
123 	return 0;
124 }
125