driver/wifi: update the connected signal quality

This commit introduces support for querying and updating the signal
quality of the established connection to the current accesspoint.

By setting the 'update_quality_interval' to a non-zero value specified
in seconds the 'state' report will be updated to incorporate the
current signal quality. It uses the same approximation as is already
in use by the scan results.

Fixes #5262.
This commit is contained in:
Josef Söntgen 2024-07-01 18:02:49 +02:00 committed by Christian Helmuth
parent 672179c3b8
commit bc64d53a77
2 changed files with 180 additions and 34 deletions

View File

@ -115,6 +115,12 @@ attribute, which specifies the interval for connected scans in
seconds and directly influences any roaming decision, i.e., select
a better fit accesspoint for the configured network.
In addition, by specifing 'update_quality_interval', the driver will
every so often update the current signal quality of the established
connection to the accesspoint. Note that this option is only useable when
the 'connected_scan_interval' is set to '0' as both options are mutually
exclusive.
Also, the driver can be switched to verbose logging during runtime
by setting the 'verbose' or 'verbose_state' attribute to 'true'.
@ -146,7 +152,8 @@ on the state, there are additional attributes that can be checked. In case
of an authentication error, e.g. the passphrase is wrong, the 'auth_failure'
attribute will be set to 'true'. The 'rfkilled' attribute is set to 'true'
if a disconnect was triggered by disabling the radio activity via setting
the 'rfkill' attribute.
the 'rfkill' attribute. It can also contain the optional 'quality' attribute
to denote the current signal quality (see 'update_quality_interval').
By subscribing to both reports and providing the required 'wifi_config' ROM
module, a component is able control the wireless driver.

View File

@ -350,10 +350,10 @@ struct Wifi::Frontend : Wifi::Rfkill_notification_handler
/* re-enable scan timer */
if (!_rfkilled) {
_scan_timer.sigh(_scan_timer_sigh);
_arm_scan_timer(false);
_timer.sigh(_timer_sigh);
_try_arming_any_timer();
} else {
_scan_timer.sigh(Genode::Signal_context_capability());
_timer.sigh(Genode::Signal_context_capability());
}
if (_rfkilled && _state != State::IDLE) {
@ -374,6 +374,7 @@ struct Wifi::Frontend : Wifi::Rfkill_notification_handler
Genode::uint64_t _connected_scan_interval { 30 };
Genode::uint64_t _scan_interval { 5 };
Genode::uint64_t _update_quality_interval { 0 };
void _config_update(bool signal)
{
@ -396,21 +397,35 @@ struct Wifi::Frontend : Wifi::Rfkill_notification_handler
_scan_interval),
5, 15*60);
Genode::uint64_t const update_quality_interval =
Util::check_time(config.attribute_value("update_quality_interval",
_update_quality_interval),
0, 15*60);
bool const new_connected_scan_interval =
connected_scan_interval != _connected_scan_interval;
bool const new_scan_interval =
connected_scan_interval != _scan_interval;
bool const new_update_quality_interval =
update_quality_interval != _update_quality_interval;
_connected_scan_interval = connected_scan_interval;
_scan_interval = scan_interval;
_update_quality_interval = update_quality_interval;
/*
* Arm again if intervals changed, implicitly discards
* an already scheduled timer.
*
* First try to arm scanning and if that fails try arming
* signal-strength polling.
*/
if (new_connected_scan_interval || new_scan_interval)
_arm_scan_timer(_connected_ap.bssid_valid());
if ( new_connected_scan_interval
|| new_scan_interval
|| new_update_quality_interval)
_try_arming_any_timer();
/*
* Always handle rfkill, regardless in which state we are currently in.
@ -563,6 +578,7 @@ struct Wifi::Frontend : Wifi::Rfkill_notification_handler
CONNECT = 0x03,
STATUS = 0x04,
INFO = 0x05,
SIGNAL = 0x06,
INITIATE_SCAN = 0x00|SCAN,
PENDING_RESULTS = 0x10|SCAN,
@ -606,6 +622,7 @@ struct Wifi::Frontend : Wifi::Rfkill_notification_handler
case LIST_NETWORKS: return "list networks";
case INFO: return "info";
case SET_NETWORK_PMF: return "set network pmf";
case SIGNAL: return "signal poll";
default: return "unknown";
};
}
@ -641,26 +658,47 @@ struct Wifi::Frontend : Wifi::Rfkill_notification_handler
/* scan */
Timer::Connection _scan_timer;
Genode::Signal_handler<Wifi::Frontend> _scan_timer_sigh;
enum class Timer_type : uint8_t { CONNECTED_SCAN, SCAN, SIGNAL_POLL };
void _handle_scan_timer()
Genode::uint64_t _seconds_from_type(Timer_type const type)
{
/*
* If we are blocked or currently trying to join a network
* suspend scanning.
*/
if (_rfkilled || _connecting.length() > 1) {
if (_verbose) { Genode::log("Suspend scan timer"); }
return;
switch (type) {
case Timer_type::CONNECTED_SCAN: return _connected_scan_interval;
case Timer_type::SCAN: return _scan_interval;
case Timer_type::SIGNAL_POLL: return _update_quality_interval;
}
/* never reached */
return 0;
}
/* scanning was disabled, ignore current request */
if (!_arm_scan_timer(_connected_ap.bssid_valid())) {
if (_verbose) { Genode::log("Scanning disabled, ignore current scan request"); }
return;
static char const *_name_from_type(Timer_type const type)
{
switch (type) {
case Timer_type::CONNECTED_SCAN: return "connected-scan";
case Timer_type::SCAN: return "scan";
case Timer_type::SIGNAL_POLL: return "signal-poll";
}
/* never reached */
return nullptr;
}
Timer::Connection _timer;
Genode::Signal_handler<Wifi::Frontend> _timer_sigh;
bool _arm_timer(Timer_type const type)
{
Genode::uint64_t const sec = _seconds_from_type(type);
if (!sec) { return false; }
if (_verbose)
Genode::log("Arm timer for ", _name_from_type(type));
_timer.trigger_once(sec * (1000 * 1000));
return true;
}
void _request_scan()
{
/* skip as we will be scheduled some time soon(tm) anyway */
if (_state != State::IDLE) {
if (_verbose) {
@ -709,18 +747,78 @@ struct Wifi::Frontend : Wifi::Rfkill_notification_handler
_submit_cmd(Cmd_str("SCAN", (char const*)ssid_buffer));
}
bool _arm_scan_timer(bool connected)
void _poll_signal_strength()
{
Genode::uint64_t const sec = connected ? _connected_scan_interval : _scan_interval;
if (!sec) { return false; }
if (_verbose) {
Genode::log("Arm ", connected ? "connected " : "",
"scan: ", sec, " sec");
if (_state != State::IDLE) {
if (_verbose)
Genode::log("Not idle, ignore signal-poll request, state: ",
Genode::Hex((unsigned)_state));
return;
}
_scan_timer.trigger_once(sec * (1000 * 1000));
return true;
_state_transition(_state, State::SIGNAL);
_submit_cmd(Cmd_str("SIGNAL_POLL"));
}
void _handle_timer()
{
/*
* If we are blocked or currently trying to join a network
* suspend scanning.
*/
if (_rfkilled || _connecting.length() > 1) {
if (_verbose)
Genode::log("Timer: suspend due to RFKILL or connection"
" attempt");
return;
}
/*
* First check if (connected-)scanning is enabled, re-arm
* the timer again and try to submit the request. In case we
* are not able to submit it the timer will trigger another
* attempt later on.
*/
if (_arm_scan_timer()) {
_request_scan();
return;
} else
if (_verbose)
Genode::log("Timer: scanning disabled");
/*
* We arm the poll timer only when we are not scanning.
* So connected-scan MUST be disabled for the signal-strength
* polling to be active.
*/
if (_arm_poll_timer()) {
_poll_signal_strength();
return;
} else
if (_verbose)
Genode::log("Timer: signal-strength polling disabled");
}
bool _arm_scan_timer()
{
Timer_type const type = _connected_ap.bssid_valid()
? Timer_type::CONNECTED_SCAN
: Timer_type::SCAN;
return _arm_timer(type);
}
bool _arm_poll_timer()
{
if (!_connected_ap.bssid_valid())
return false;
return _arm_timer(Timer_type::SIGNAL_POLL);
}
void _try_arming_any_timer()
{
if (!_arm_scan_timer())
(void)_arm_poll_timer();
}
Genode::Constructible<Genode::Expanding_reporter> _ap_reporter { };
@ -1238,6 +1336,14 @@ struct Wifi::Frontend : Wifi::Rfkill_notification_handler
xml.attribute("bssid", ap.bssid);
xml.attribute("freq", ap.freq);
xml.attribute("state", "connected");
/*
* Only add the attribute when we have something
* to report so that a consumer of the state report
* may take appropriate actions.
*/
if (_connected_ap.signal)
xml.attribute("quality", _connected_ap.signal);
});
});
}
@ -1332,6 +1438,36 @@ struct Wifi::Frontend : Wifi::Rfkill_notification_handler
}
}
void _handle_signal_poll_result(State &state, char const *msg)
{
_state_transition(state, State::IDLE);
using Rssi = Genode::String<5>;
Rssi rssi { };
auto get_rssi = [&] (char const *line) {
if (Genode::strcmp(line, "RSSI=", 5) != 0)
return;
rssi = Rssi(line + 5);
};
for_each_line(msg, get_rssi);
/*
* Use the same simplified approximation for denoting
* the quality to be in line with the scan results.
*/
_connected_ap.signal =
Util::approximate_quality(rssi.valid() ? rssi.string()
: "-100");
/*
* Query the status to incorporate the newly acquired
* quality into a new state report.
*/
_state_transition(state, State::STATUS);
_submit_cmd(Cmd_str("STATUS"));
}
/* connection state */
Genode::Constructible<Genode::Reporter> _state_reporter { };
@ -1435,7 +1571,7 @@ struct Wifi::Frontend : Wifi::Rfkill_notification_handler
_submit_cmd(Cmd_str("BSS ", bssid));
}
_arm_scan_timer(connected);
_try_arming_any_timer();
}
/*
@ -1565,6 +1701,9 @@ struct Wifi::Frontend : Wifi::Rfkill_notification_handler
case State::INFO:
_handle_info_result(_state, msg);
break;
case State::SIGNAL:
_handle_signal_poll_result(_state, msg);
break;
case State::IDLE:
default:
break;
@ -1603,13 +1742,13 @@ struct Wifi::Frontend : Wifi::Rfkill_notification_handler
_rfkill_handler(env.ep(), *this, &Wifi::Frontend::_handle_rfkill),
_config_rom(env, "wifi_config"),
_config_sigh(env.ep(), *this, &Wifi::Frontend::_handle_config_update),
_scan_timer(env),
_scan_timer_sigh(env.ep(), *this, &Wifi::Frontend::_handle_scan_timer),
_timer(env),
_timer_sigh(env.ep(), *this, &Wifi::Frontend::_handle_timer),
_events_handler(env.ep(), *this, &Wifi::Frontend::_handle_events),
_cmd_handler(env.ep(), *this, &Wifi::Frontend::_handle_cmds)
{
_config_rom.sigh(_config_sigh);
_scan_timer.sigh(_scan_timer_sigh);
_timer.sigh(_timer_sigh);
/* set/initialize as unblocked */
_notify_blockade.wakeup();
@ -1644,7 +1783,7 @@ struct Wifi::Frontend : Wifi::Rfkill_notification_handler
_handle_rfkill();
/* kick-off initial scanning */
_handle_scan_timer();
_handle_timer();
}
/**