Skip to content

Commit fbdadd6

Browse files
committed
Added LwIP (socket mode) transport
1 parent cb7aace commit fbdadd6

4 files changed

Lines changed: 545 additions & 0 deletions

File tree

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* @file erpc_setup_lwip_tcp.cpp
3+
* @brief TCP Transport setup code.
4+
*
5+
*
6+
* @author Andrej Hýroš, xhyros00@stud.fit.vut.cz
7+
* @date 7th of May, 2025
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*/
11+
12+
13+
#include "erpc_transport_setup.h"
14+
#include "erpc_lwip_tcp_transport.hpp"
15+
#include "ethernetif.h"
16+
17+
18+
using namespace erpc;
19+
20+
21+
// TODO Missing support for static allocation policy
22+
erpc_transport_t erpc_transport_lwip_tcp_init(const char *host, uint16_t port, bool isServer) {
23+
erpc_transport_t transport;
24+
LwipTCPTransport *lwipTCPTransport;
25+
26+
#if ERPC_ALLOCATION_POLICY == ERPC_ALLOCATION_POLICY_DYNAMIC
27+
lwipTCPTransport = new LwipTCPTransport(host, port, isServer);
28+
#else
29+
#error "Only dynamic allocation policy supported for lwipTCPTransport!"
30+
#endif
31+
32+
transport = reinterpret_cast<erpc_transport_t>(lwipTCPTransport);
33+
34+
if(lwipTCPTransport != NULL) {
35+
if(lwipTCPTransport->openTransport() != kErpcStatus_Success) {
36+
LWIPTCP_DEBUG_PRINT("[erpc_transport_lwip_tcp_init()] Setup failed.");
37+
transport = NULL;
38+
}
39+
}
40+
41+
return transport;
42+
}
43+
44+
45+
void erpc_transport_lwip_tcp_close(erpc_transport_t transport) {
46+
erpc_assert(transport != NULL);
47+
48+
LwipTCPTransport *lwipTcpTransport = reinterpret_cast<LwipTCPTransport *>(transport);
49+
50+
lwipTcpTransport->closeTransport();
51+
}
52+
53+
void erpc_transport_lwip_tcp_deinit(erpc_transport_t transport) {
54+
#if ERPC_ALLOCATION_POLICY == ERPC_ALLOCATION_POLICY_DYNAMIC
55+
erpc_assert(transport != NULL);
56+
57+
LwipTCPTransport *lwipTcpTransport = reinterpret_cast<LwipTCPTransport *>(transport);
58+
59+
delete lwipTcpTransport;
60+
#else
61+
#error "Only dynamic allocation policy supported for lwipTCPTransport!"
62+
#endif
63+
}

erpc_c/setup/erpc_transport_setup.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,14 @@ void erpc_transport_tcp_deinit(erpc_transport_t transport);
486486

487487
//@}
488488

489+
490+
erpc_transport_t erpc_transport_lwip_tcp_init(const char *host, uint16_t port, bool isServer);
491+
492+
void erpc_transport_lwip_tcp_deinit();
493+
494+
495+
496+
489497
//! @name CMSIS UART transport setup
490498
//@{
491499

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
/**
2+
* @file erpc_lwip_tcp_transport.cpp
3+
* @brief TCP Transport implementation on LwIP TCP/IP stack.
4+
*
5+
*
6+
* @author Andrej Hýroš, xhyros00@stud.fit.vut.cz
7+
* @date 7th of May, 2025
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*/
11+
12+
#include "erpc_lwip_tcp_transport.hpp"
13+
14+
using namespace erpc;
15+
16+
17+
LwipTCPTransport::LwipTCPTransport(const char *host, uint16_t port, bool isServer) :
18+
m_host(host),
19+
m_isServer(isServer),
20+
m_port(port),
21+
m_runServer(false),
22+
m_clientSock(-1),
23+
m_serverThread(serverThreadStub, DEFAULT_THREAD_PRIO, DEFAULT_THREAD_STACKSIZE)
24+
{
25+
}
26+
27+
LwipTCPTransport::~LwipTCPTransport() {
28+
closeTransport();
29+
}
30+
31+
32+
33+
void LwipTCPTransport::configure(const char* host, uint16_t port, bool isServer) {
34+
m_host = host;
35+
m_isServer = isServer;
36+
m_port = port;
37+
}
38+
39+
40+
erpc_status_t LwipTCPTransport::openTransport() {
41+
erpc_status_t status = kErpcStatus_Success;
42+
/*
43+
* Open transport as a server or as a client.
44+
*/
45+
if(m_isServer) {
46+
LWIPTCP_DEBUG_PRINT("[openTransport()] Starting server...");
47+
m_runServer = true;
48+
m_serverThread.start(this);
49+
} else {
50+
status = connectClient();
51+
}
52+
return status;
53+
}
54+
55+
56+
57+
erpc_status_t LwipTCPTransport::closeTransport() {
58+
/*
59+
* End server loop and close connection to the client.
60+
*/
61+
if (m_isServer) {
62+
m_runServer = false;
63+
}
64+
lwip_close(m_clientSock);
65+
66+
67+
return kErpcStatus_Success;
68+
}
69+
70+
71+
72+
73+
74+
erpc_status_t LwipTCPTransport::connectClient() {
75+
76+
77+
if(m_clientSock != -1) {
78+
LWIPTCP_DEBUG_PRINT("[connectClient()] Client is already connected!");
79+
return kErpcStatus_Success;
80+
}
81+
82+
83+
struct sockaddr_in srv;
84+
85+
/*
86+
* Configure and create socket.
87+
*/
88+
memset(&srv, 0, sizeof(srv));
89+
srv.sin_family = AF_INET;
90+
srv.sin_addr.s_addr = inet_addr(m_host);
91+
srv.sin_port = htons(m_port);
92+
93+
m_clientSock = lwip_socket(AF_INET, SOCK_STREAM, 0);
94+
if(m_clientSock < 0) {
95+
LWIPTCP_DEBUG_PRINT("[connectClient()] Error creating socket...");
96+
return kErpcStatus_Fail;
97+
}
98+
99+
/*
100+
* TCP_NODELAY disables Nagle's algorithm, which should reduce latency for small packets.
101+
* Without this setting, TCP would wait for additional data, so it can send multiple packets
102+
* together. This helps with congestion controll, but in eRPC case, we often want to send
103+
* small messages.
104+
*/
105+
int yes = 1;
106+
lwip_setsockopt(m_clientSock, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
107+
108+
109+
/*
110+
* Connect to server.
111+
*/
112+
if (lwip_connect(m_clientSock, (struct sockaddr *)&srv, sizeof(srv)) < 0) {
113+
LWIPTCP_DEBUG_PRINT("[connectClient()] Connection failed...");
114+
lwip_close(m_clientSock);
115+
return kErpcStatus_ConnectionFailure;
116+
}
117+
return kErpcStatus_Success;
118+
}
119+
120+
121+
erpc_status_t LwipTCPTransport::disconnect() {
122+
/*
123+
* If socket is valid, close it and mark it as invalid.
124+
*/
125+
if (m_clientSock >= 0) {
126+
lwip_close(m_clientSock);
127+
m_clientSock = -1;
128+
}
129+
return kErpcStatus_Success;
130+
}
131+
132+
133+
erpc_status_t LwipTCPTransport::startServer()
134+
{
135+
struct sockaddr_in srv;
136+
struct sockaddr_in cli;
137+
socklen_t cliInfoLen = sizeof(struct sockaddr_in);
138+
memset(&srv, 0, sizeof(srv));
139+
memset(&cli, 0, sizeof(cli));
140+
int incomingSocket;
141+
142+
/*
143+
* Define address.
144+
*/
145+
srv.sin_family = AF_INET;
146+
srv.sin_port = htons(m_port);
147+
srv.sin_addr.s_addr = INADDR_ANY;
148+
149+
/*
150+
* Create listening socket.
151+
*/
152+
if((m_serverSock = lwip_socket(AF_INET, SOCK_STREAM, 0)) < 0) {
153+
LWIPTCP_DEBUG_PRINT("[startServer()] Error creating socket...");
154+
return kErpcStatus_Fail;
155+
}
156+
157+
/*
158+
* SO_REUSEADDR makes the port available right after closing.
159+
*/
160+
int yes = 1;
161+
lwip_setsockopt(m_serverSock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
162+
163+
164+
/*
165+
* Bind socket to address.
166+
*/
167+
if(lwip_bind(m_serverSock, (struct sockaddr *)&srv, sizeof(srv)) == -1) {
168+
LWIPTCP_DEBUG_PRINT("[startServer()] Failed on bind()...");
169+
lwip_close(m_serverSock);
170+
return kErpcStatus_Fail;
171+
}
172+
173+
/*
174+
* Starting listening.
175+
*/
176+
if(lwip_listen(m_serverSock, 1) == -1) {
177+
LWIPTCP_DEBUG_PRINT("[startServer()] Failed on listen()...");
178+
lwip_close(m_serverSock);
179+
return kErpcStatus_Fail;
180+
}
181+
182+
183+
while(m_runServer) {
184+
incomingSocket = lwip_accept(m_serverSock, (struct sockaddr *)&cli, &cliInfoLen);
185+
186+
/*
187+
* In case of accept failure, wait some time and try again.
188+
*/
189+
if(incomingSocket < 0) {
190+
LWIPTCP_DEBUG_PRINT("[startServer()] Failed on accept()...");
191+
Thread::sleep(1000); // 1ms
192+
continue;
193+
} else {
194+
m_clientSock = incomingSocket;
195+
}
196+
197+
}
198+
199+
lwip_close(m_serverSock);
200+
m_serverSock = -1;
201+
202+
return kErpcStatus_Success;
203+
}
204+
205+
206+
erpc_status_t LwipTCPTransport::underlyingReceive(uint8_t *data, uint32_t size) {
207+
ssize_t read;
208+
209+
/*
210+
* Repeat reading from socket until we read all that we want.
211+
*/
212+
while(size > 0) {
213+
read = lwip_read(m_clientSock, data, size);
214+
if(read > 0) {
215+
size -= read;
216+
data += read;
217+
}
218+
/*
219+
* Connection was closed by peer.
220+
*/
221+
if(read == 0) {
222+
lwip_close(m_clientSock);
223+
return kErpcStatus_ConnectionClosed;
224+
}
225+
226+
/*
227+
* Read error.
228+
*/
229+
if(read < 0) {
230+
return kErpcStatus_ReceiveFailed;
231+
}
232+
}
233+
return kErpcStatus_Success;
234+
}
235+
236+
erpc_status_t LwipTCPTransport::underlyingSend(const uint8_t *data, uint32_t size) {
237+
ssize_t toSend = size;
238+
ssize_t sent;
239+
240+
241+
/*
242+
* Socket is invalid.
243+
*/
244+
if(m_clientSock < 0) {
245+
return kErpcStatus_ConnectionFailure;
246+
}
247+
248+
/*
249+
* Write into socket until we sent all that we wanted.
250+
*/
251+
while(toSend > 0) {
252+
sent = lwip_write(m_clientSock, data, toSend);
253+
254+
if(sent > 0) {
255+
toSend -= sent;
256+
data += sent;
257+
} else {
258+
return kErpcStatus_SendFailed;
259+
}
260+
}
261+
return kErpcStatus_Success;
262+
}
263+
264+
bool LwipTCPTransport::hasMessage(void) {
265+
/*
266+
* Return true if server is connected to client.
267+
*/
268+
return (m_clientSock >= 0);
269+
}
270+
271+
void LwipTCPTransport::serverThreadStub(void *arg) {
272+
/*
273+
* Start server as a separate task/thread.
274+
*/
275+
LwipTCPTransport *instance = reinterpret_cast<LwipTCPTransport *>(arg);
276+
if (instance != NULL)
277+
{
278+
instance->startServer();
279+
} else {
280+
LWIPTCP_DEBUG_PRINT("[serverThreadStub()] Failed on thread creation...");
281+
}
282+
}
283+
284+
285+
286+
287+
288+
289+
290+
291+
292+
293+
294+
295+
296+
297+

0 commit comments

Comments
 (0)