Skip to content

Commit

Permalink
Merge pull request #1470 from pguyot/w03/fix-spi-driver
Browse files Browse the repository at this point in the history
Fix driver usage of ports

- 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`

These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
  • Loading branch information
bettio committed Jan 19, 2025
2 parents bf94fd8 + af32e12 commit 6cdbb3d
Show file tree
Hide file tree
Showing 21 changed files with 135 additions and 114 deletions.
2 changes: 2 additions & 0 deletions UPDATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
by using `term_port_from_local_process_id` instead of `term_from_local_process_id`. Sockets, from
port socket driver, are also represented by a port and some matching code may need to be updated from
`is_pid/1` to `is_port/1`.
- Ports and pids can be registered. Function `globalcontext_get_registered_process` result now is
a term that can be a `port()` or a `pid()`.

## v0.6.4 -> v0.6.5

Expand Down
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 @@ -160,6 +160,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 @@ -859,17 +864,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 @@ -890,7 +894,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 @@ -1002,13 +1006,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 @@ -1019,8 +1023,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 @@ -1102,7 +1108,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
11 changes: 6 additions & 5 deletions src/libAtomVM/dist_nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -376,10 +376,10 @@ static term nif_erlang_dist_ctrl_put_data(Context *ctx, int argc, term argv[])
RAISE_ERROR(BADARG_ATOM);
}
term to_name = term_get_tuple_element(control, 3);
int target_process_id = globalcontext_get_registered_process(ctx->global, term_to_atom_index(to_name));
if (target_process_id) {
term target_process_pid = globalcontext_get_registered_process(ctx->global, term_to_atom_index(to_name));
if (term_is_local_pid(target_process_pid)) {
term payload = externalterm_to_term_with_roots(data + 1 + bytes_read, binary_len - 1 - bytes_read, ctx, ExternalTermCopy, &bytes_read, 2, argv);
globalcontext_send_message(ctx->global, target_process_id, payload);
globalcontext_send_message(ctx->global, term_to_local_process_id(target_process_pid), payload);
}
break;
}
Expand All @@ -390,11 +390,12 @@ static term nif_erlang_dist_ctrl_put_data(Context *ctx, int argc, term argv[])
term from_pid = term_get_tuple_element(control, 1);
term target_proc = term_get_tuple_element(control, 2);
term monitor_ref = term_get_tuple_element(control, 3);
if (term_is_atom(target_proc)) {
target_proc = globalcontext_get_registered_process(ctx->global, term_to_atom_index(target_proc));
}
int target_process_id = 0;
if (term_is_local_pid(target_proc)) {
target_process_id = term_to_local_process_id(target_proc);
} else if (term_is_atom(target_proc)) {
target_process_id = globalcontext_get_registered_process(ctx->global, term_to_atom_index(target_proc));
} else {
RAISE_ERROR(BADARG_ATOM);
}
Expand Down
Loading

0 comments on commit 6cdbb3d

Please sign in to comment.