Skip to content

Commit fbc52e8

Browse files
committed
Add support to connect to loopback address on RP2 platform
Loopback connection is implemented by defining LWIP_NETIF_LOOPBACK_MULTITHREADING and implementing `tcpip_try_callback` to call `netif_poll` in a queue. - Also fix socket compatibility issue with RP2 where nif_recv would return eagain instead of timeout. - Also add tcp_err_cb for LwIP stack. Signed-off-by: Paul Guyot <pguyot@kallisys.net>
1 parent 65f16e8 commit fbc52e8

File tree

4 files changed

+59
-1
lines changed

4 files changed

+59
-1
lines changed

libs/estdlib/src/socket.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ recv0(Socket, Length, Timeout) ->
400400

401401
recv0_nowait(Socket, Length, Ref) ->
402402
case ?MODULE:nif_recv(Socket, Length) of
403-
{error, timeout} ->
403+
{error, Reason} when Reason =:= timeout orelse Reason =:= eagain ->
404404
case ?MODULE:nif_select_read(Socket, Ref) of
405405
ok ->
406406
{select, {select_info, recv, Ref}};

src/libAtomVM/otp_socket.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ struct UDPReceivedItem
146146

147147
static err_t tcp_recv_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);
148148
static void udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
149+
static void tcp_err_cb(void *arg, err_t err);
149150

150151
#endif
151152

@@ -620,6 +621,7 @@ static term nif_socket_open(Context *ctx, int argc, term argv[])
620621
if (rsrc_obj->socket_state & SocketStateTCP) {
621622
LWIP_BEGIN();
622623
tcp_arg(rsrc_obj->tcp_pcb, rsrc_obj);
624+
tcp_err(rsrc_obj->tcp_pcb, tcp_err_cb);
623625
tcp_recv(rsrc_obj->tcp_pcb, tcp_recv_cb);
624626
LWIP_END();
625627
} else {
@@ -996,6 +998,23 @@ static void udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip
996998
otp_socket_lwip_enqueue(&event);
997999
} // Otherwise socket was closed.
9981000
}
1001+
1002+
// LwIP tcpip_try_callback uses queue.
1003+
static void tcpip_try_callback_handler(struct LWIPEvent *event)
1004+
{
1005+
event->tcpip_try_callback.function(event->tcpip_try_callback.ctx);
1006+
}
1007+
1008+
int tcpip_try_callback(tcpip_callback_fn function, void *ctx)
1009+
{
1010+
struct LWIPEvent event;
1011+
event.handler = tcpip_try_callback_handler;
1012+
event.tcpip_try_callback.function = function;
1013+
event.tcpip_try_callback.ctx = ctx;
1014+
otp_socket_lwip_enqueue(&event);
1015+
return ERR_OK;
1016+
}
1017+
9991018
#endif
10001019

10011020
static term nif_socket_select_read(Context *ctx, int argc, term argv[])
@@ -2639,6 +2658,25 @@ static err_t tcp_connected_cb(void *arg, struct tcp_pcb *tpcb, err_t err)
26392658
} // else: sender died
26402659
return ERR_OK;
26412660
}
2661+
2662+
static void tcp_err_cb(void *arg, err_t err)
2663+
{
2664+
UNUSED(err);
2665+
2666+
struct SocketResource *rsrc_obj = (struct SocketResource *) arg;
2667+
struct RefcBinary *rsrc_refc = refc_binary_from_data(rsrc_obj);
2668+
GlobalContext *global = rsrc_refc->resource_type->global;
2669+
int32_t target_pid = rsrc_obj->selecting_process_id;
2670+
rsrc_obj->selecting_process_id = INVALID_PROCESS_ID;
2671+
rsrc_obj->socket_state = SocketStateTCPConnected;
2672+
if (target_pid != INVALID_PROCESS_ID) {
2673+
struct LWIPEvent event;
2674+
event.handler = trap_answer_closed;
2675+
event.trap_answer_closed.global = global;
2676+
event.trap_answer_closed.target_pid = target_pid;
2677+
otp_socket_lwip_enqueue(&event);
2678+
} // else: sender died
2679+
}
26422680
#endif
26432681

26442682
static term nif_socket_connect(Context *ctx, int argc, term argv[])
@@ -2741,6 +2779,9 @@ static term nif_socket_connect(Context *ctx, int argc, term argv[])
27412779
LWIP_END();
27422780

27432781
if (UNLIKELY(err != ERR_OK)) {
2782+
if (err == ERR_USE || err == ERR_RTE) {
2783+
return make_error_tuple(CLOSED_ATOM, ctx);
2784+
}
27442785
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
27452786
}
27462787
if (rsrc_obj->socket_state & SocketStateUDP) {

src/libAtomVM/otp_socket.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ struct LWIPEvent
131131
{
132132
struct SocketResource *rsrc_obj;
133133
} finalize_close;
134+
struct
135+
{
136+
tcpip_callback_fn function;
137+
void *ctx;
138+
} tcpip_try_callback;
134139
// Used by otp_net
135140
struct
136141
{

src/platforms/rp2/src/lib/lwipopts.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,16 @@ void sntp_set_system_time_us(unsigned long sec, unsigned long usec);
107107
#define SNTP_SET_SYSTEM_TIME_US(sec, usec) sntp_set_system_time_us(sec, usec)
108108

109109
#define TCP_LISTEN_BACKLOG 1
110+
111+
#define LWIP_NETIF_LOOPBACK 1
112+
#define LWIP_NETIF_LOOPBACK_MULTITHREADING 1
113+
#define LWIP_HAVE_LOOPIF 0
114+
115+
// To support loopback, we implement our own version of tcpip_try_callback that
116+
// enqueues a call to netif_poll.
117+
struct netif;
118+
void netif_poll(struct netif *netif);
119+
typedef void (*tcpip_callback_fn)(void *ctx);
120+
int tcpip_try_callback(tcpip_callback_fn function, void *ctx);
121+
110122
#endif /* __LWIPOPTS_H__ */

0 commit comments

Comments
 (0)