Skip to content

Commit

Permalink
Fix driver usage of ports
Browse files Browse the repository at this point in the history
- Fix i2c and spi esp32 native code to use ports
- Fix `whereis/1` and `register/2` to work with ports
- Fix specifications related to ports
- Fix documentation of `register/2`

Signed-off-by: Paul Guyot <pguyot@kallisys.net>
  • Loading branch information
pguyot committed Jan 18, 2025
1 parent f435dc1 commit 2a9280d
Show file tree
Hide file tree
Showing 17 changed files with 106 additions and 90 deletions.
20 changes: 10 additions & 10 deletions libs/eavmlib/src/console.erl
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
%%-----------------------------------------------------------------------------
-spec puts(iodata()) -> ok | {error, term()}.
puts(Text) ->
puts(get_pid(), Text).
puts(get_port(), Text).

%% @hidden
-spec puts(pid(), iodata()) -> ok.
Expand All @@ -55,7 +55,7 @@ puts(Console, Text) ->
%%-----------------------------------------------------------------------------
-spec flush() -> ok | {error, term()}.
flush() ->
flush(get_pid()).
flush(get_port()).

%% @hidden
-spec flush(pid()) -> ok.
Expand All @@ -77,18 +77,18 @@ print(_Text) ->
%% Internal operations

%% @private
-spec get_pid() -> pid().
get_pid() ->
-spec get_port() -> port().
get_port() ->
case whereis(console) of
undefined ->
start();
Pid when is_pid(Pid) ->
Pid
Port when is_port(Port) ->
Port
end.

%% @private
-spec start() -> pid().
-spec start() -> port().
start() ->
Pid = erlang:open_port({spawn, "console"}, []),
erlang:register(console, Pid),
Pid.
Port = erlang:open_port({spawn, "console"}, []),
erlang:register(console, Port),
Port.
2 changes: 1 addition & 1 deletion libs/eavmlib/src/gpio.erl
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
detach_interrupt/1
]).

-type gpio() :: pid().
-type gpio() :: port().
%% This is the pid returned by `gpio:start/0'.
-type pin() :: non_neg_integer() | pin_tuple().
%% The pin definition for ESP32 and PR2040 is a non-negative integer. A tuple is used on the STM32 platform and for the extra "WL" pins on the Pico-W.
Expand Down
2 changes: 1 addition & 1 deletion libs/eavmlib/src/i2c.erl
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
| {use_nif, boolean()}
| {send_timeout_ms, non_neg_integer()}.
-type params() :: [param()].
-type i2c() :: pid() | {'$i2c', term(), reference()}.
-type i2c() :: port() | {'$i2c', term(), reference()}.
-type address() :: non_neg_integer().
-type register() :: non_neg_integer().

Expand Down
12 changes: 7 additions & 5 deletions libs/eavmlib/src/network.erl
Original file line number Diff line number Diff line change
Expand Up @@ -462,20 +462,22 @@ maybe_callback1({Key, Arg} = Msg, Config) ->
end.

%% @private
-spec get_port() -> port().
get_port() ->
case whereis(network_port) of
undefined ->
open_port();
Pid ->
Pid
Port ->
Port
end.

%% @private
-spec open_port() -> port().
open_port() ->
Pid = erlang:open_port({spawn, "network"}, []),
Port = erlang:open_port({spawn, "network"}, []),
%Pid = spawn(?MODULE, simulation_loop, []),
erlang:register(network_port, Pid),
Pid.
erlang:register(network_port, Port),
Port.

%% @private
wait_for_ip(Timeout) ->
Expand Down
18 changes: 9 additions & 9 deletions libs/eavmlib/src/port.erl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
%% The port driver should be initialized with:
%% `open_port({spawn, "Name"}, Param)'
%% Where Name is an atom(), and is the name of the driver. The return from `open_port/2'
%% will be the Pid that will be required for future `port:call/2' or `port:call/3' use.
%% will be the Port that will be required for future `port:call/2' or `port:call/3' use.
%%
%% Examples:
%% ```open_port({spawn, "i2c"}, Param)'''
Expand All @@ -41,31 +41,31 @@
-export([call/2, call/3]).

%%-----------------------------------------------------------------------------
%% @param Port Pid to which to send messages
%% @param Port Port to which to send messages
%% @param Message the message to send
%% @returns term() | {error, Reason}.
%% @doc Send a message to a given port driver pid.
%% @doc Send a message to a given port driver.
%%
%% This function is used to send a message to an open port divers pid and will
%% This function is used to send a message to an open port drivers port and will
%% return a term or `{error, Reason}'.
%% @end
%%-----------------------------------------------------------------------------
-spec call(Port :: pid(), Message :: term()) -> term().
-spec call(Port :: port(), Message :: term()) -> term().
call(Port, Message) ->
call(Port, Message, infinity).

%%-----------------------------------------------------------------------------
%% @param Port Pid to which to send messages
%% @param Port Port to which to send messages
%% @param Message the message to send
%% @param Timeout the timeout value in milliseconds
%% @returns term() | {error, Reason}.
%% @doc Send a message to a given port driver pid with a timeout.
%% @doc Send a message to a given port driver with a timeout.
%%
%% This function is used to send a message to an open port divers pid and will return
%% This function is used to send a message to an open port drivers port and will return
%% a term or `{error, Reason}', or`{error, timeout}' if the TimeoutMs is reached first.
%% @end
%%-----------------------------------------------------------------------------
-spec call(Port :: pid(), Message :: term(), Timeout :: timeout()) -> term() | {error, timeout}.
-spec call(Port :: port(), Message :: term(), Timeout :: timeout()) -> term() | {error, timeout}.
call(Port, Message, Timeout) ->
MonitorRef = monitor(port, Port),
Port ! {'$call', {self(), MonitorRef}, Message},
Expand Down
6 changes: 3 additions & 3 deletions libs/eavmlib/src/spi.erl
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
| {device_config, [{device_name(), device_config()}]}
].

-type spi() :: pid().
-type spi() :: port().
-type address() :: non_neg_integer().

-type transaction() :: #{
Expand Down Expand Up @@ -244,7 +244,7 @@ write_at(SPI, DeviceName, Address, Len, Data) ->
-spec write(SPI :: spi(), DeviceName :: device_name(), Transaction :: transaction()) ->
ok | {error, Reason :: term()}.
write(SPI, DeviceName, Transaction) when
is_pid(SPI) andalso is_atom(DeviceName) andalso is_map(Transaction)
is_port(SPI) andalso is_atom(DeviceName) andalso is_map(Transaction)
->
port:call(SPI, {write, DeviceName, Transaction}).

Expand Down Expand Up @@ -279,7 +279,7 @@ write(SPI, DeviceName, Transaction) when
-spec write_read(SPI :: spi(), DeviceName :: device_name(), Transaction :: transaction()) ->
{ok, ReadData :: binary()} | {error, Reason :: term()}.
write_read(SPI, DeviceName, Transaction) when
is_pid(SPI) andalso is_atom(DeviceName) andalso is_map(Transaction)
is_port(SPI) andalso is_atom(DeviceName) andalso is_map(Transaction)
->
port:call(SPI, {write_read, DeviceName, Transaction}).

Expand Down
17 changes: 11 additions & 6 deletions libs/eavmlib/src/uart.erl
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,27 @@
-module(uart).
-export([open/1, open/2, close/1, read/1, write/2]).

-spec open(integer() | list() | binary(), [tuple()]) -> port().
open(Name, Opts) ->
open([{peripheral, Name} | Opts]).

-spec open([tuple()]) -> port().
open(Opts) ->
open_port({spawn, "uart"}, migrate_config(Opts)).

close(Pid) ->
port:call(Pid, close).
-spec close(Port :: port()) -> ok | {error, any()}.
close(Port) ->
port:call(Port, close).

read(Pid) ->
port:call(Pid, read).
-spec read(Port :: port()) -> {ok, binary()} | {error, any()}.
read(Port) ->
port:call(Port, read).

write(Pid, B) ->
-spec write(Port :: port(), B :: iolist()) -> ok | {error, any()}.
write(Port, B) ->
case is_iolist(B) of
true ->
port:call(Pid, {write, B});
port:call(Port, {write, B});
false ->
throw(badarg)
end.
Expand Down
36 changes: 21 additions & 15 deletions libs/estdlib/src/erlang.erl
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@
| link
| monitor.

-type send_destination() ::
pid()
| port()
| atom().

%%-----------------------------------------------------------------------------
%% @param Time time in milliseconds after which to send the timeout message.
%% @param Dest Pid or server name to which to send the timeout message.
Expand Down Expand Up @@ -851,17 +856,16 @@ ref_to_list(_Ref) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param Name name of the process to register
%% @param Pid pid of the process to register
%% @param Name name of the process to register
%% @param PidOrPort pid or port of the process to register
%% @returns `true'
%% @doc Register a name for a given process.
%% Processes can be registered with several names.
%% Unlike Erlang/OTP, ports are not distinguished from processes.
%% Errors with `badarg' if the name is already registered.
%% Errors with `badarg' if the name is already registered or if the process
%% is already registered.
%% @end
%%-----------------------------------------------------------------------------
-spec register(Name :: atom(), Pid :: pid()) -> true.
register(_Name, _Pid) ->
-spec register(Name :: atom(), PidOrPort :: pid() | port()) -> true.
register(_Name, _PidOrPort) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
Expand All @@ -882,7 +886,7 @@ unregister(_Name) ->
%% @doc Lookup a process by name.
%% @end
%%-----------------------------------------------------------------------------
-spec whereis(Name :: atom()) -> pid() | undefined.
-spec whereis(Name :: atom()) -> pid() | port() | undefined.
whereis(_Name) ->
erlang:nif_error(undefined).

Expand Down Expand Up @@ -994,13 +998,13 @@ make_ref() ->
%% @doc Send a message to a given process
%% @end
%%-----------------------------------------------------------------------------
-spec send(Pid :: pid(), Message :: Message) -> Message.
send(_Pid, _Message) ->
-spec send(Target :: send_destination(), Message :: Message) -> Message.
send(_Target, _Message) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param Type type of monitor to create
%% @param Pid pid of the object to monitor
%% @param Type type of monitor to create
%% @param PidOrPort pid or port of the object to monitor
%% @returns a monitor reference
%% @doc Create a monitor on a process or on a port.
%% When the process or the port terminates, the following message is sent to
Expand All @@ -1011,8 +1015,10 @@ send(_Pid, _Message) ->
%% Unlike Erlang/OTP, monitors are only supported for processes and ports.
%% @end
%%-----------------------------------------------------------------------------
-spec monitor(Type :: process | port, Pid :: pid()) -> reference().
monitor(_Type, _Pid) ->
-spec monitor
(Type :: process, Pid :: pid()) -> reference();
(Type :: port, Port :: port()) -> reference().
monitor(_Type, _PidOrPort) ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
Expand Down Expand Up @@ -1094,7 +1100,7 @@ exit(_Process, _Reason) ->
%% Unlike Erlang/OTP, ports are identified by pids.
%% @end
%%-----------------------------------------------------------------------------
-spec open_port(PortName :: {spawn, iodata()}, Options :: [any()] | map()) -> pid().
-spec open_port(PortName :: {spawn, iodata()}, Options :: [any()] | map()) -> port().
open_port(_PortName, _Options) ->
erlang:nif_error(undefined).

Expand Down
17 changes: 9 additions & 8 deletions src/libAtomVM/globalcontext.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "smp.h"
#include "synclist.h"
#include "sys.h"
#include "term.h"
#include "utils.h"
#include "valueshashtable.h"

Expand All @@ -55,7 +56,7 @@ struct RegisteredProcess
struct ListHead registered_processes_list_head;

int atom_index;
int local_process_id;
term local_pid_or_port;
};

GlobalContext *globalcontext_new()
Expand Down Expand Up @@ -505,7 +506,7 @@ void globalcontext_init_process(GlobalContext *glb, Context *ctx)
SMP_SPINLOCK_UNLOCK(&glb->processes_spinlock);
}

bool globalcontext_register_process(GlobalContext *glb, int atom_index, int local_process_id)
bool globalcontext_register_process(GlobalContext *glb, int atom_index, term local_pid_or_port)
{
struct ListHead *registered_processes_list = synclist_wrlock(&glb->registered_processes);
struct ListHead *item;
Expand All @@ -523,7 +524,7 @@ bool globalcontext_register_process(GlobalContext *glb, int atom_index, int loca
AVM_ABORT();
}
registered_process->atom_index = atom_index;
registered_process->local_process_id = local_process_id;
registered_process->local_pid_or_port = local_pid_or_port;

list_append(registered_processes_list, &registered_process->registered_processes_list_head);
synclist_unlock(&glb->registered_processes);
Expand Down Expand Up @@ -557,30 +558,30 @@ void globalcontext_maybe_unregister_process_id(GlobalContext *glb, int target_pr
struct ListHead *tmp;
MUTABLE_LIST_FOR_EACH (item, tmp, registered_processes_list) {
struct RegisteredProcess *registered_process = GET_LIST_ENTRY(item, struct RegisteredProcess, registered_processes_list_head);
if (registered_process->local_process_id == target_process_id) {
if (term_to_local_process_id(registered_process->local_pid_or_port) == target_process_id) {
list_remove(item);
free(registered_process);
}
}
synclist_unlock(&glb->registered_processes);
}

int globalcontext_get_registered_process(GlobalContext *glb, int atom_index)
term globalcontext_get_registered_process(GlobalContext *glb, int atom_index)
{
struct ListHead *registered_processes_list = synclist_rdlock(&glb->registered_processes);
struct ListHead *item;
LIST_FOR_EACH (item, registered_processes_list) {
const struct RegisteredProcess *registered_process = GET_LIST_ENTRY(item, struct RegisteredProcess, registered_processes_list_head);
if (registered_process->atom_index == atom_index) {
int result = registered_process->local_process_id;
term result = registered_process->local_pid_or_port;
synclist_unlock(&glb->registered_processes);
return result;
}
}

synclist_unlock(&glb->registered_processes);

return 0;
return UNDEFINED_ATOM;
}

term globalcontext_get_registered_name_process(GlobalContext *glb, int local_process_id)
Expand All @@ -590,7 +591,7 @@ term globalcontext_get_registered_name_process(GlobalContext *glb, int local_pro
struct ListHead *tmp;
MUTABLE_LIST_FOR_EACH (item, tmp, registered_processes_list) {
struct RegisteredProcess *registered_process = GET_LIST_ENTRY(item, struct RegisteredProcess, registered_processes_list_head);
if (registered_process->local_process_id == local_process_id) {
if (term_to_local_process_id(registered_process->local_pid_or_port) == local_process_id) {
int result = registered_process->atom_index;
synclist_unlock(&glb->registered_processes);
return term_from_atom_index(result);
Expand Down
8 changes: 4 additions & 4 deletions src/libAtomVM/globalcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,10 +338,10 @@ void globalcontext_init_process(GlobalContext *glb, Context *ctx);
* @details Register a process with a certain name (atom) so it can be easily retrieved later.
* @param glb the global context, each registered process will be globally available for that context.
* @param atom_index the atom table index.
* @param local_process_id the process local id.
* @param local_pid_or_port the local pid or port
* @returns \c true if the process was registered, \c false if another process with the same name already existed
*/
bool globalcontext_register_process(GlobalContext *glb, int atom_index, int local_process_id);
bool globalcontext_register_process(GlobalContext *glb, int atom_index, term local_pid_or_port);

/**
* @brief Get registered name for a process/port
Expand All @@ -361,9 +361,9 @@ term globalcontext_get_registered_name_process(GlobalContext *glb, int local_pro
* @details Returns the local process id of a previously registered process.
* @param glb the global context.
* @param atom_index the atom table index.
* @returns a previously registered process local id.
* @returns a previously registered process local id or UNDEFINED_ATOM
*/
int globalcontext_get_registered_process(GlobalContext *glb, int atom_index);
term globalcontext_get_registered_process(GlobalContext *glb, int atom_index);

/**
* @brief Unregister a process by name
Expand Down
Loading

0 comments on commit 2a9280d

Please sign in to comment.