Skip to content

Commit f63bd01

Browse files
committed
network: add UDP wrapper
Tested with NTPClient and ArduinoMDNS TODO: properly test multicast
1 parent 1085fd5 commit f63bd01

File tree

2 files changed

+338
-2
lines changed

2 files changed

+338
-2
lines changed

Diff for: libraries/SocketWrapper/ZephyrUDP.h

+330
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
/*
2+
MbedUdp.h - UDP implementation using mbed Sockets
3+
Copyright (c) 2021 Arduino SA. All right reserved.
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
20+
#include "Arduino.h"
21+
#include "SocketWrapper.h"
22+
#include "api/Udp.h"
23+
#include "sys/socket.h"
24+
#include "zephyr/net/net_ip.h"
25+
#include "zephyr/net/net_if.h"
26+
27+
#include <list>
28+
#include <deque>
29+
#include <vector>
30+
#include <memory>
31+
32+
class ZephyrUDP : public arduino::UDP {
33+
private:
34+
int _socket;
35+
36+
public:
37+
ZephyrUDP() : _socket(-1) {} // Constructor
38+
~ZephyrUDP() {
39+
stop();
40+
}
41+
42+
// initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use
43+
virtual uint8_t begin(uint16_t port) {
44+
45+
struct sockaddr_in addr;
46+
addr.sin_family = AF_INET;
47+
addr.sin_port = htons(port);
48+
addr.sin_addr.s_addr = INADDR_ANY;
49+
50+
_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
51+
52+
zsock_ioctl(_socket, ZFD_IOCTL_FIONBIO);
53+
54+
if (::bind(_socket, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
55+
::close(_socket);
56+
_socket = -1;
57+
return false;
58+
}
59+
60+
return true;
61+
}
62+
63+
// initialize, start listening on specified multicast IP address and port. Returns 1 if successful, 0 if there are no sockets available to use
64+
virtual uint8_t beginMulticast(IPAddress ip, uint16_t port) {
65+
bool ret = begin(port);
66+
if (ret == false) {
67+
return false;
68+
}
69+
70+
struct sockaddr_in addr;
71+
addr.sin_family = AF_INET;
72+
addr.sin_addr.s_addr = ip;
73+
74+
net_if_ipv4_maddr_join(net_if_get_by_index(1), net_if_ipv4_maddr_add(net_if_get_by_index(1), (struct in_addr*)&addr));
75+
return true;
76+
}
77+
78+
// Finish with the UDP socket
79+
virtual void stop() {
80+
if (_socket != -1) {
81+
::close(_socket);
82+
_socket = -1;
83+
}
84+
}
85+
86+
// Sending UDP packets
87+
88+
// Start building up a packet to send to the remote host specific in ip and port
89+
// Returns 1 if successful, 0 if there was a problem with the supplied IP address or port
90+
virtual int beginPacket(IPAddress ip, uint16_t port) {
91+
_send_to_ip = ip;
92+
_send_to_port = port;
93+
94+
/* Make sure that the transmit data buffer is empty. */
95+
_tx_data.clear();
96+
return true;
97+
}
98+
99+
// Start building up a packet to send to the remote host specific in host and port
100+
// Returns 1 if successful, 0 if there was a problem resolving the hostname or port
101+
virtual int beginPacket(const char* host, uint16_t port) {
102+
// Resolve address
103+
struct addrinfo hints;
104+
struct addrinfo *res;
105+
106+
hints.ai_family = AF_INET;
107+
hints.ai_socktype = SOCK_DGRAM;
108+
109+
int resolve_attempts = 100;
110+
int ret;
111+
112+
while (resolve_attempts--) {
113+
ret = getaddrinfo(host, String(port).c_str(), &hints, &res);
114+
115+
if (ret == 0) {
116+
break;
117+
} else {
118+
k_sleep(K_MSEC(1));
119+
}
120+
}
121+
122+
if (ret != 0) {
123+
return false;
124+
}
125+
126+
return beginPacket(IPAddress(((sockaddr_in*)(res->ai_addr))->sin_addr.s_addr), port);
127+
}
128+
129+
// Finish off this packet and send it
130+
// Returns 1 if the packet was sent successfully, 0 if there was an error
131+
virtual int endPacket() {
132+
struct sockaddr_in addr;
133+
addr.sin_family = AF_INET;
134+
addr.sin_port = htons(_send_to_port);
135+
addr.sin_addr.s_addr = _send_to_ip;
136+
return ::sendto(_socket, _tx_data.data(), _tx_data.size(), 0, (sockaddr*)&addr, sizeof(addr));
137+
}
138+
139+
// Write a single byte into the packet
140+
virtual size_t write(uint8_t data) {
141+
_tx_data.push_back(data);
142+
return 1;
143+
}
144+
145+
// Write size bytes from buffer into the packet
146+
virtual size_t write(uint8_t* buffer, size_t size) {
147+
std::copy(buffer, buffer + size, std::back_inserter(_tx_data));
148+
return size;
149+
}
150+
151+
// Write size bytes from buffer into the packet
152+
virtual size_t write(const uint8_t* buffer, size_t size) {
153+
std::copy(buffer, buffer + size, std::back_inserter(_tx_data));
154+
return size;
155+
}
156+
157+
using Print::write;
158+
159+
int parsePacket()
160+
{
161+
struct sockaddr_in addr;
162+
socklen_t addrlen = sizeof(addr);
163+
uint8_t tmp_buf[512];
164+
165+
int ret = ::recvfrom(_socket, tmp_buf, sizeof(tmp_buf), 0, (sockaddr*)&addr, &addrlen);
166+
if (ret > 0)
167+
{
168+
auto pkt = std::make_shared<UdpRxPacket>(
169+
IPAddress(addr.sin_addr.s_addr),
170+
ntohs(addr.sin_port), tmp_buf, ret);
171+
172+
_rx_pkt_list.push_back(pkt);
173+
174+
// drop the oldest packet if the list is full
175+
if(_rx_pkt_list.size() > _rx_pkt_list_size) {
176+
_rx_pkt_list.pop_front();
177+
}
178+
}
179+
180+
if (_rx_pkt_list.size())
181+
{
182+
/* Discard UdpRxPacket object previously held by _rx_pkt
183+
* and replace it with the new one.
184+
*/
185+
_rx_pkt = _rx_pkt_list.front();
186+
_rx_pkt_list.pop_front();
187+
return _rx_pkt->totalSize();
188+
}
189+
else
190+
{
191+
/* Otherwise ensure that _rx_pkt definitely
192+
* does not hold any UdpRxPacket object anymore.
193+
*/
194+
_rx_pkt.reset();
195+
return 0;
196+
}
197+
}
198+
199+
int available()
200+
{
201+
if (_rx_pkt)
202+
return _rx_pkt->available();
203+
else
204+
return 0;
205+
}
206+
207+
int read()
208+
{
209+
if (_rx_pkt)
210+
return _rx_pkt->read();
211+
else
212+
return -1;
213+
}
214+
215+
int read(unsigned char* buffer, size_t len)
216+
{
217+
if (_rx_pkt)
218+
return _rx_pkt->read(buffer, len);
219+
else
220+
return -1;
221+
}
222+
223+
int read(char* buffer, size_t len)
224+
{
225+
if (_rx_pkt)
226+
return _rx_pkt->read(buffer, len);
227+
else
228+
return -1;
229+
}
230+
231+
int peek()
232+
{
233+
if (_rx_pkt)
234+
return _rx_pkt->peek();
235+
else
236+
return -1;
237+
}
238+
239+
void flush()
240+
{
241+
/* Delete UdpRxPacket object held by _rx_pkt. */
242+
if (_rx_pkt)
243+
_rx_pkt.reset();
244+
}
245+
246+
virtual IPAddress remoteIP() {
247+
if (_rx_pkt)
248+
return _rx_pkt->remoteIP();
249+
else
250+
return IPAddress();
251+
}
252+
253+
virtual uint16_t remotePort() {
254+
if (_rx_pkt)
255+
return _rx_pkt->remotePort();
256+
else
257+
return 0;
258+
}
259+
260+
private:
261+
262+
/* UDP TRANSMISSION */
263+
IPAddress _send_to_ip;
264+
uint16_t _send_to_port;
265+
std::vector<uint8_t> _tx_data;
266+
int _rx_pkt_list_size = 10;
267+
/* UDP RECEPTION */
268+
class UdpRxPacket
269+
{
270+
private:
271+
IPAddress const _remote_ip;
272+
uint16_t const _remote_port;
273+
size_t const _rx_data_len;
274+
std::deque<uint8_t> _rx_data;
275+
276+
public:
277+
UdpRxPacket(
278+
IPAddress const remote_ip,
279+
uint16_t const remote_port,
280+
uint8_t const * p_data,
281+
size_t const data_len)
282+
: _remote_ip(remote_ip)
283+
, _remote_port(remote_port)
284+
, _rx_data_len(data_len)
285+
, _rx_data(p_data, p_data + data_len)
286+
{
287+
}
288+
289+
typedef std::shared_ptr<UdpRxPacket> SharedPtr;
290+
291+
IPAddress remoteIP() const { return _remote_ip; }
292+
uint16_t remotePort() const { return _remote_port; }
293+
size_t totalSize() const { return _rx_data_len; }
294+
295+
int available()
296+
{
297+
return _rx_data.size();
298+
}
299+
300+
int read()
301+
{
302+
uint8_t const data = _rx_data.front();
303+
_rx_data.pop_front();
304+
return data;
305+
}
306+
307+
int read(unsigned char* buffer, size_t len)
308+
{
309+
size_t bytes_read = 0;
310+
for (; bytes_read < len && !_rx_data.empty(); bytes_read++)
311+
{
312+
buffer[bytes_read] = _rx_data.front();
313+
_rx_data.pop_front();
314+
}
315+
return bytes_read;
316+
}
317+
318+
int read(char* buffer, size_t len)
319+
{
320+
return read((unsigned char*)buffer, len);
321+
}
322+
323+
int peek()
324+
{
325+
return _rx_data.front();
326+
}
327+
};
328+
std::list<UdpRxPacket::SharedPtr> _rx_pkt_list;
329+
UdpRxPacket::SharedPtr _rx_pkt;
330+
};

Diff for: loader/llext_exports.c

+8-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ EXPORT_SYMBOL(strtoul);
2121
EXPORT_SYMBOL(strcmp);
2222
EXPORT_SYMBOL(strlen);
2323
EXPORT_SYMBOL(strchr);
24+
EXPORT_SYMBOL(strcat);
2425
EXPORT_SYMBOL(memmove);
2526

27+
EXPORT_SYMBOL(k_malloc);
2628
EXPORT_SYMBOL(malloc);
2729
EXPORT_SYMBOL(realloc);
2830
EXPORT_SYMBOL(calloc);
@@ -54,6 +56,8 @@ EXPORT_SYMBOL(z_log_msg_runtime_vcreate);
5456
#if defined(CONFIG_NETWORKING)
5557
FORCE_EXPORT_SYM(net_if_foreach);
5658
FORCE_EXPORT_SYM(net_if_get_by_iface);
59+
FORCE_EXPORT_SYM(net_if_ipv4_maddr_add);
60+
FORCE_EXPORT_SYM(net_if_ipv4_maddr_join);
5761
#endif
5862

5963
#if defined(CONFIG_NET_DHCPV4)
@@ -64,14 +68,14 @@ FORCE_EXPORT_SYM(net_dhcpv4_add_option_callback);
6468
#endif
6569

6670
#if defined(CONFIG_NET_MGMT_EVENT)
67-
FORCE_EXPORT_SYM(net_mgmt_NET_REQUEST_WIFI_CONNECT);
68-
FORCE_EXPORT_SYM(net_mgmt_NET_REQUEST_WIFI_IFACE_STATUS);
6971
FORCE_EXPORT_SYM(net_mgmt_add_event_callback);
7072
FORCE_EXPORT_SYM(net_mgmt_event_wait_on_iface);
7173
#endif
7274

7375
#if defined(CONFIG_WIFI)
7476
FORCE_EXPORT_SYM(net_if_get_wifi_sta);
77+
FORCE_EXPORT_SYM(net_mgmt_NET_REQUEST_WIFI_CONNECT);
78+
FORCE_EXPORT_SYM(net_mgmt_NET_REQUEST_WIFI_IFACE_STATUS);
7579
#endif
7680

7781
#if defined(CONFIG_BT)
@@ -105,6 +109,8 @@ FORCE_EXPORT_SYM(bind);
105109
FORCE_EXPORT_SYM(listen);
106110
EXPORT_SYMBOL(exit);
107111
FORCE_EXPORT_SYM(inet_pton);
112+
FORCE_EXPORT_SYM(sendto);
113+
FORCE_EXPORT_SYM(recvfrom);
108114
#endif
109115

110116
#if defined(CONFIG_CDC_ACM_DTE_RATE_CALLBACK_SUPPORT)

0 commit comments

Comments
 (0)