Skip to content

Commit

Permalink
Fix concurrency issues and error handling in ESP32 UART driver
Browse files Browse the repository at this point in the history
Fixes possible concurrency problems by using a mutex for access to `reader_process_pid` and
`reader_ref_ticks` in `uart_data`.

Avoid possible overflow of the queue by handling all mesages in the interrupt callback.

Now cleans up and returns errors, with improved log messages when initalization fails with a badarg
in the configuration parameters, rather than aborting.

Fixes incorrect pin integers being cast to terms.

Pin numbers are validated for the chip the VM is compiled for before use to mitigate VM crashes from
bad input parameters.

Signed-off-by: Winford <[email protected]>
  • Loading branch information
UncleGrumpy committed Feb 20, 2025
1 parent e583192 commit f5bea67
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 54 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ bug when handling errors from BIFs used as NIFs (when called with `CALL_EXT` and
- Fixed potential crashes or memory leaks caused by a mistake in calculation of reference counts
and a race condition in otp_socket code
- Fixed an out of memory issue by forcing GC to copy data from message fragments
- Fixed possible concurrency problems in ESP32 UART driver

### Changed

- ESP32 UART driver no longer aborts because of badargs in configuration, instead raising an error

## [0.6.5] - 2024-10-15

Expand Down
38 changes: 37 additions & 1 deletion libs/eavmlib/src/uart.erl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
%

-module(uart).
-export([open/1, open/2, close/1, read/1, write/2]).
-export([open/1, open/2, close/1, read/1, read/2, write/2]).

-type peripheral() :: string() | binary().
% The peripheral `Name' may be one of: `"UART0"' | `"UART1"' | `"UART2"' | `<<"UART0">>' | `<<"UART1">>' | `<<"UART2">>'.
Expand Down Expand Up @@ -104,6 +104,42 @@ close(Pid) when is_pid(Pid) ->
read(Pid) when is_pid(Pid) ->
port:call(Pid, read).

%%-----------------------------------------------------------------------------
%% @param Pid of the uart port to be read
%% @param Timeout millisecond to wait for data to become available
%% @returns `{ok, Data}', or `{error, Reason}'
%% @doc Read data from a UART port
%%
%% This function will return any data that is available within the
%% timeout period to the process. After the timeout has expired a new
%% read command may be used regardless of whether the last read was
%% sent a payload.
%% Example:
%% ```
%% Data = case uart:read(Uart, 3000) of
%% {ok, Bin} -> Bin;
%% {error, timeout} -> <<"">>;
%% Error -> error_handler_fun(Uart, Error)
%% end,
%% '''
%% Any data sent to the esp32 over uart between reads with a timeout will
%% be lost, so be sure this is what you want. Most applications will want
%% a single process to read from UART and continue to listen until a payload
%% is received, and likely pass the payload off for processing and
%% immediately begin another read.
%% @end
%%-----------------------------------------------------------------------------
-spec read(Pid :: pid(), Timeout :: pos_integer()) ->
{ok, Data :: iodata()} | {error, _Reason :: term()}.
read(Pid, Timeout) when is_pid(Pid) ->
case port:call(Pid, read, Timeout) of
{error, timeout} ->
port:call(Pid, cancel_read),
{error, timeout};
Result ->
Result
end.

%%-----------------------------------------------------------------------------
%% @param Pid of the uart port to be written to
%% @param Data to be written to the given uart port
Expand Down
Loading

0 comments on commit f5bea67

Please sign in to comment.