Skip to content

Commit

Permalink
Add support for PicoW networking
Browse files Browse the repository at this point in the history
Port OTP Socket code to lwIP raw API
Also add socket:recv/2 and socket:recvfrom/2

Signed-off-by: Paul Guyot <[email protected]>
  • Loading branch information
pguyot committed Oct 27, 2023
1 parent bb18e1f commit 613e259
Show file tree
Hide file tree
Showing 11 changed files with 1,525 additions and 543 deletions.
2 changes: 2 additions & 0 deletions examples/erlang/rp2040/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ pack_uf2(pico_rtc pico_rtc)
pack_uf2(picow_blink picow_blink)
pack_uf2(picow_wifi_sta picow_wifi_sta)
pack_uf2(picow_wifi_ap picow_wifi_ap)
pack_uf2(picow_udp_beacon picow_udp_beacon)
pack_uf2(picow_tcp_server picow_tcp_server)
71 changes: 53 additions & 18 deletions libs/estdlib/src/socket.erl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@
sockname/1,
peername/1,
recv/1,
recv/2,
recv/3,
recvfrom/1,
recvfrom/2,
recvfrom/3,
send/2,
sendto/3,
setopt/3,
Expand Down Expand Up @@ -257,43 +261,53 @@ accept(Socket, Timeout) ->
end.

%%-----------------------------------------------------------------------------
%% @equiv socket:recv(Socket, infinity)
%% @equiv socket:recv(Socket, 0)
%% @end
%%-----------------------------------------------------------------------------
-spec recv(Socket :: socket()) -> {ok, Data :: binary()} | {error, Reason :: term()}.
recv(Socket) ->
recv(Socket, infinity).
recv(Socket, 0, infinity).

%%-----------------------------------------------------------------------------
%% @equiv socket:recv(Socket, Length, infinity)
%% @end
%%-----------------------------------------------------------------------------
-spec recv(Socket :: socket(), Length :: non_neg_integer()) -> {ok, Data :: binary()} | {error, Reason :: term()}.
recv(Socket, Length) ->
recv(Socket, Length, infinity).

%%-----------------------------------------------------------------------------
%% @param Socket the socket
%% @param Length number of bytes to receive
%% @param Timeout timeout (in milliseconds)
%% @returns `{ok, Data}' if successful; `{error, Reason}', otherwise.
%% @doc Receive data on the specified socket.
%%
%% Note that this function will block until data is received
%% on the socket.
%% This function is equivalent to `recvfrom/3' except for the return type.
%%
%% Example:
%%
%% `{ok, Data} = socket:recv(ConnectedSocket)'
%% @end
%%-----------------------------------------------------------------------------
-spec recv(Socket :: socket(), Timeout :: timeout()) ->
-spec recv(Socket :: socket(), Length :: non_neg_integer(), Timeout :: timeout()) ->
{ok, Data :: binary()} | {error, Reason :: term()}.
recv(Socket, Timeout) ->
recv(Socket, Length, Timeout) ->
Self = self(),
Ref = erlang:make_ref(),
?TRACE("select read for recv. self=~p ref=~p~n", [Self, Ref]),
case ?MODULE:nif_select_read(Socket, Self, Ref) of
ok ->
receive
{select, _AcceptedSocket, Ref, ready_input} ->
case ?MODULE:nif_recv(Socket) of
{error, closed} = E ->
case ?MODULE:nif_recv(Socket, Length) of
{error, _} = E ->
?MODULE:nif_select_stop(Socket),
E;
R ->
R
% TODO: Assemble data to have more if Length > byte_size(Data)
% as long as timeout did not expire
{ok, Data} ->
{ok, Data}
end;
{closed, Ref} ->
{error, closed}
Expand All @@ -305,16 +319,26 @@ recv(Socket, Timeout) ->
end.

%%-----------------------------------------------------------------------------
%% @equiv socket:recvfrom(Socket, infinity)
%% @equiv socket:recvfrom(Socket, 0)
%% @end
%%-----------------------------------------------------------------------------
-spec recvfrom(Socket :: socket()) ->
{ok, {Address :: sockaddr(), Data :: binary()}} | {error, Reason :: term()}.
recvfrom(Socket) ->
recvfrom(Socket, infinity).
recvfrom(Socket, 0).

%%-----------------------------------------------------------------------------
%% @equiv socket:recvfrom(Socket, Length, infinity)
%% @end
%%-----------------------------------------------------------------------------
-spec recvfrom(Socket :: socket(), Length :: non_neg_integer()) ->
{ok, {Address :: sockaddr(), Data :: binary()}} | {error, Reason :: term()}.
recvfrom(Socket, Length) ->
recvfrom(Socket, Length, infinity).

%%-----------------------------------------------------------------------------
%% @param Socket the socket
%% @param Length number of bytes to receive
%% @param Timeout timeout (in milliseconds)
%% @returns `{ok, {Address, Data}}' if successful; `{error, Reason}', otherwise.
%% @doc Receive data on the specified socket, returning the from address.
Expand All @@ -325,24 +349,35 @@ recvfrom(Socket) ->
%% Example:
%%
%% `{ok, {Address, Data}} = socket:recvfrom(ConnectedSocket)'
%%
%% If socket is UDP, the function retrieves the first available packet and
%% truncate it to Length bytes, unless Length is 0 in which case it returns
%% the whole packet ("all available").
%%
%% If socket is TCP and Length is 0, this function retrieves all available
%% data without waiting (using peek if the platform allows it).
%% If socket is TCP and Length is not 0, this function waits until Length
%% bytes are available and return these bytes.
%% @end
%%-----------------------------------------------------------------------------
-spec recvfrom(Socket :: socket(), Timeout :: timeout()) ->
-spec recvfrom(Socket :: socket(), Length :: non_neg_integer(), Timeout :: timeout()) ->
{ok, {Address :: sockaddr(), Data :: binary()}} | {error, Reason :: term()}.
recvfrom(Socket, Timeout) ->
recvfrom(Socket, Length, Timeout) ->
Self = self(),
Ref = erlang:make_ref(),
?TRACE("select read for recvfrom. self=~p ref=~p", [Self, Ref]),
case ?MODULE:nif_select_read(Socket, Self, Ref) of
ok ->
receive
{select, _AcceptedSocket, Ref, ready_input} ->
case ?MODULE:nif_recvfrom(Socket) of
{error, closed} = E ->
case ?MODULE:nif_recvfrom(Socket, Length) of
{error, _} = E ->
?MODULE:nif_select_stop(Socket),
E;
R ->
R
% TODO: Assemble data to have more if Length > byte_size(Data)
% as long as timeout did not expire
{ok, {Address, Data}} ->
{ok, {Address, Data}}
end;
{closed, Ref} ->
{error, closed}
Expand Down
2 changes: 2 additions & 0 deletions src/libAtomVM/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ define_if_symbol_exists(libAtomVM O_SEARCH "fcntl.h" PRIVATE HAVE_O_SEARCH)
define_if_symbol_exists(libAtomVM O_TTY_INIT "fcntl.h" PRIVATE HAVE_O_TTY_INIT)
define_if_symbol_exists(libAtomVM clock_settime "time.h" PRIVATE HAVE_CLOCK_SETTIME)
define_if_symbol_exists(libAtomVM settimeofday "sys/time.h" PRIVATE HAVE_SETTIMEOFDAY)
define_if_symbol_exists(libAtomVM socket "sys/socket.h" PUBLIC HAVE_SOCKET)
define_if_symbol_exists(libAtomVM select "sys/select.h" PUBLIC HAVE_SELECT)

if (AVM_USE_32BIT_FLOAT)
target_compile_definitions(libAtomVM PUBLIC AVM_USE_SINGLE_PRECISION)
Expand Down
Loading

0 comments on commit 613e259

Please sign in to comment.