Skip to content

Commit

Permalink
Add sta mode connection tracking to network driver
Browse files Browse the repository at this point in the history
Adds a new function `sta_status/0` to get the current connection state of the sta interface, which
is now tracked by the gen_server internally.

Signed-off-by: Winford <winford@object.stream>
  • Loading branch information
UncleGrumpy committed Jan 4, 2025
1 parent fc7ab3b commit 56c6847
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 26 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `net:gethostname/0` on platforms with gethostname(3).
- Added `socket:getopt/2`
- Added `network:connect/0,1` and `network:disconnect/0` to ESP32 network driver.
- Added `network:sta_status/0` to get the current connection state of the sta interface.

### Fixed
- ESP32: improved sntp sync speed from a cold boot.
Expand Down
80 changes: 54 additions & 26 deletions libs/eavmlib/src/network.erl
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
wait_for_ap/0, wait_for_ap/1, wait_for_ap/2,
sta_rssi/0,
sta_disconnect/0,
sta_connect/0,
sta_connect/1
sta_connect/0, sta_connect/1,
sta_status/0
]).
-export([start/1, start_link/1, stop/0]).
-export([
Expand Down Expand Up @@ -164,13 +164,16 @@
-type network_config() :: [sta_config() | ap_config() | sntp_config()].

-type db() :: integer().
-type sta_status() ::
associated | connected | connecting | degraded | disconnected | disconnecting | undefined.

-record(state, {
cb_ref :: reference(),
config :: network_config(),
port :: port(),
ref :: reference(),
sta_ip_info :: ip_info()
sta_ip_info :: ip_info(),
sta_state :: sta_status()
}).

%%-----------------------------------------------------------------------------
Expand Down Expand Up @@ -394,33 +397,63 @@ sta_rssi() ->
Other -> {error, Other}
end.

%%-----------------------------------------------------------------------------
%% @returns ConnectionState :: sta_status().
%%
%% @doc Get the connection status of the sta interface.
%%
%% Results will be one of: `associated', `connected', `connecting', `degraded',
%% `disconnected', `disconnecting', or `undefined'. The state `associated' indicates
%% that the station is connected to an access point, but does not yet have an IP address.
%% A status of `degraded' indicates that the connection has experienced at least one
%% beacon timeout event during the current connection session. This does not necessarily
%% mean the connection is still in a poor state, but it might be helpful diagnosing
%% problems with networked applications.
%% @end
%%-----------------------------------------------------------------------------
-spec sta_status() -> Status :: sta_status().
sta_status() ->
gen_server:call(?SERVER, sta_status).

%%
%% gen_server callbacks
%%

%% @hidden
init(Config) ->
{ok, #state{config = Config}}.
{ok, #state{config = Config, sta_state = disconnected}}.

%% @hidden
handle_call(start, From, #state{config = Config} = State) ->
Port = get_port(),
Ref = make_ref(),
Port ! {self(), Ref, {start, Config}},
wait_start_reply(Ref, From, Port, State);
case proplists:get_value(sta, Config) of
false ->
wait_start_reply(Ref, From, Port, State);
true ->
case proplists:get_value(managed, Config) of
false ->
wait_start_reply(Ref, From, Port, State#state{sta_state = connecting});
true ->
wait_start_reply(Ref, From, Port, State)
end
end;
handle_call(halt_sta, From, State) ->
Ref = make_ref(),
network_port ! {From, Ref, halt_sta},
wait_halt_sta_reply(Ref, From, State);
wait_halt_sta_reply(Ref, From, State#state{ref = Ref, sta_state = disconnecting});
handle_call(connect, From, #state{config = Config} = State) ->
Ref = make_ref(),
network_port ! {self(), Ref, connect},
wait_connect_reply(Ref, From, Config, State);
wait_connect_reply(Ref, From, Config, State#state{sta_state = connecting});
handle_call({connect, Config}, From, #state{config = OldConfig} = State) ->
Ref = make_ref(),
NewConfig = update_config(OldConfig, Config),
network_port ! {self(), Ref, {connect, Config}},
wait_connect_reply(Ref, From, NewConfig, State);
network_port ! {self(), Ref, {connect, NewConfig}},
wait_connect_reply(Ref, From, NewConfig, State#state{ref = Ref, sta_state = connecting});
handle_call(sta_status, From, State) ->
{reply, State#state.sta_state, State};
handle_call(_Msg, _From, State) ->
{reply, {error, unknown_message}, State}.

Expand All @@ -432,36 +465,31 @@ wait_start_reply(Ref, From, Port, State) ->
{noreply, State#state{port = Port, ref = Ref, cb_ref = Ref}};
{Ref, {error, Reason} = ER} ->
gen_server:reply(From, {error, Reason}),
{stop, {start_failed, Reason}, ER, State}
{stop, {start_failed, Reason}, ER, State#state{sta_state = disconnected}}
end.

%% @private
wait_connect_reply(Ref, From, NewConfig, State) ->
receive
{Ref, ok} ->
gen_server:reply(From, ok),
{noreply, State#state{ref = Ref, config = NewConfig}};
{noreply, State#state{ref = Ref, config = NewConfig, sta_state = connected}};
{Ref, {error, _Reason} = ER} ->
gen_server:reply(From, ER),
{noreply, State#state{ref = Ref}}
{noreply, State#state{sta_state = disconnected}}
end.

wait_halt_sta_reply(
Ref, From, #state{config = Config, port = Port, ref = Ref, sta_ip_info = IpInfo} = _State
) ->
wait_halt_sta_reply(Ref, From, State) ->
receive
{Ref, ok} ->
gen_server:reply(From, ok),
NewState = #state{config = Config, port = Port, ref = Ref, sta_ip_info = IpInfo},
{noreply, NewState};
{noreply, State#state{ref = Ref, sta_state = disconnected}};
{Ref, {error, _Reason} = Error} ->
gen_server:reply(From, Error),
NewState = #state{config = Config, port = Port, ref = Ref, sta_ip_info = IpInfo},
{noreply, NewState};
{noreply, State};
Other ->
gen_server:reply(From, {unhandled_message, Other}),
NewState = #state{config = Config, port = Port, ref = Ref, sta_ip_info = IpInfo},
{noreply, NewState}
{noreply, State}
end.

%% @hidden
Expand All @@ -471,16 +499,16 @@ handle_cast(_Msg, State) ->
%% @hidden
handle_info({Ref, sta_connected} = _Msg, #state{cb_ref = Ref, config = Config} = State) ->
maybe_sta_connected_callback(Config),
{noreply, State};
handle_info({Ref, sta_beacon_timeout} = _Msg, #state{ref = Ref, config = Config} = State) ->
{noreply, State#state{sta_state = associated}};
handle_info({Ref, sta_beacon_timeout} = _Msg, #state{cb_ref = Ref, config = Config} = State) ->
maybe_sta_beacon_timeout_callback(Config),
{noreply, State};
{noreply, State#state{sta_state = degraded}};
handle_info({Ref, sta_disconnected} = _Msg, #state{cb_ref = Ref, config = Config} = State) ->
maybe_sta_disconnected_callback(Config),
{noreply, State};
{noreply, State#state{sta_state = disconnected, sta_ip_info = undefined}};
handle_info({Ref, {sta_got_ip, IpInfo}} = _Msg, #state{cb_ref = Ref, config = Config} = State) ->
maybe_sta_got_ip_callback(Config, IpInfo),
{noreply, State#state{sta_ip_info = IpInfo}};
{noreply, State#state{sta_ip_info = IpInfo, sta_state = connected}};
handle_info({Ref, ap_started} = _Msg, #state{cb_ref = Ref, config = Config} = State) ->
maybe_ap_started_callback(Config),
{noreply, State};
Expand Down

0 comments on commit 56c6847

Please sign in to comment.