mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-26 18:59:22 +01:00
netdev: RSSI polling support for less capable drivers
If the kernel device driver or the kernel nl80211 version doesn't support the new RSSI threshold list CQM monitoring, implement similar logic in iwd with periodic polling. This is only active when an RSSI agent is registered to receive the events. I tested this with the same testRSSIAgent autotests that tests the driver-side rssi monitoring except with all timeouts multiplied by ~20.
This commit is contained in:
parent
417367e272
commit
452e174802
145
src/netdev.c
145
src/netdev.c
@ -86,6 +86,8 @@ struct netdev {
|
||||
uint8_t rssi_levels_num;
|
||||
uint8_t cur_rssi_level_idx;
|
||||
int8_t cur_rssi;
|
||||
struct l_timeout *rssi_poll_timeout;
|
||||
uint32_t rssi_poll_cmd_id;
|
||||
|
||||
struct l_queue *watches;
|
||||
uint32_t next_watch_id;
|
||||
@ -284,6 +286,117 @@ int netdev_set_powered(struct netdev *netdev, bool powered,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void netdev_set_rssi_level_idx(struct netdev *netdev)
|
||||
{
|
||||
uint8_t new_level;
|
||||
|
||||
for (new_level = 0; new_level < netdev->rssi_levels_num; new_level++)
|
||||
if (netdev->cur_rssi >= netdev->rssi_levels[new_level])
|
||||
break;
|
||||
|
||||
netdev->cur_rssi_level_idx = new_level;
|
||||
}
|
||||
|
||||
static void netdev_rssi_poll_cb(struct l_genl_msg *msg, void *user_data)
|
||||
{
|
||||
struct netdev *netdev = user_data;
|
||||
struct l_genl_attr attr, nested;
|
||||
uint16_t type, len;
|
||||
const void *data;
|
||||
bool found;
|
||||
uint8_t prev_rssi_level_idx = netdev->cur_rssi_level_idx;
|
||||
|
||||
netdev->rssi_poll_cmd_id = 0;
|
||||
|
||||
if (!l_genl_attr_init(&attr, msg))
|
||||
goto done;
|
||||
|
||||
found = false;
|
||||
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
||||
if (type != NL80211_ATTR_STA_INFO)
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found || !l_genl_attr_recurse(&attr, &nested))
|
||||
goto done;
|
||||
|
||||
found = false;
|
||||
while (l_genl_attr_next(&nested, &type, &len, &data)) {
|
||||
if (type != NL80211_STA_INFO_SIGNAL_AVG)
|
||||
continue;
|
||||
|
||||
if (len != 1)
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
netdev->cur_rssi = *(const int8_t *) data;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
goto done;
|
||||
|
||||
/*
|
||||
* Note we don't have to handle LOW_SIGNAL_THRESHOLD here. The
|
||||
* CQM single threshold RSSI monitoring should work even if the
|
||||
* kernel driver doesn't support multiple thresholds. So the
|
||||
* polling only handles the client-supplied threshold list.
|
||||
*/
|
||||
netdev_set_rssi_level_idx(netdev);
|
||||
if (netdev->cur_rssi_level_idx != prev_rssi_level_idx)
|
||||
netdev->event_filter(netdev, NETDEV_EVENT_RSSI_LEVEL_NOTIFY,
|
||||
netdev->user_data);
|
||||
|
||||
done:
|
||||
/* Rearm timer */
|
||||
l_timeout_modify(netdev->rssi_poll_timeout, 6);
|
||||
}
|
||||
|
||||
static void netdev_rssi_poll(struct l_timeout *timeout, void *user_data)
|
||||
{
|
||||
struct netdev *netdev = user_data;
|
||||
struct l_genl_msg *msg;
|
||||
|
||||
msg = l_genl_msg_new_sized(NL80211_CMD_GET_STATION, 64);
|
||||
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
||||
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN,
|
||||
netdev->handshake->aa);
|
||||
|
||||
netdev->rssi_poll_cmd_id = l_genl_family_send(nl80211, msg,
|
||||
netdev_rssi_poll_cb,
|
||||
netdev, NULL);
|
||||
}
|
||||
|
||||
/* To be called whenever operational or rssi_levels_num are updated */
|
||||
static void netdev_rssi_polling_update(struct netdev *netdev)
|
||||
{
|
||||
if (wiphy_get_ext_feature(netdev->wiphy,
|
||||
NL80211_EXT_FEATURE_CQM_RSSI_LIST))
|
||||
return;
|
||||
|
||||
if (netdev->operational && netdev->rssi_levels_num > 0) {
|
||||
if (netdev->rssi_poll_timeout)
|
||||
return;
|
||||
|
||||
netdev->rssi_poll_timeout =
|
||||
l_timeout_create(1, netdev_rssi_poll, netdev, NULL);
|
||||
} else {
|
||||
if (!netdev->rssi_poll_timeout)
|
||||
return;
|
||||
|
||||
l_timeout_remove(netdev->rssi_poll_timeout);
|
||||
netdev->rssi_poll_timeout = NULL;
|
||||
|
||||
if (netdev->rssi_poll_cmd_id) {
|
||||
l_genl_family_cancel(nl80211, netdev->rssi_poll_cmd_id);
|
||||
netdev->rssi_poll_cmd_id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void netdev_linkmode_dormant_cb(bool success, void *user_data)
|
||||
{
|
||||
struct netdev *netdev = user_data;
|
||||
@ -331,6 +444,8 @@ static void netdev_connect_free(struct netdev *netdev)
|
||||
netdev->result = NETDEV_RESULT_OK;
|
||||
netdev->in_ft = false;
|
||||
|
||||
netdev_rssi_polling_update(netdev);
|
||||
|
||||
if (netdev->pairwise_new_key_cmd_id) {
|
||||
l_genl_family_cancel(nl80211, netdev->pairwise_new_key_cmd_id);
|
||||
netdev->pairwise_new_key_cmd_id = 0;
|
||||
@ -468,23 +583,12 @@ static void netdev_cqm_event_rssi_threshold(struct netdev *netdev,
|
||||
|
||||
netdev->cur_rssi_low =
|
||||
(rssi_event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW);
|
||||
event = netdev->cur_rssi_low ? NETDEV_EVENT_RSSI_THRESHOLD_LOW :
|
||||
event = netdev->cur_rssi_low ? NETDEV_EVENT_RSSI_THRESHOLD_LOW :
|
||||
NETDEV_EVENT_RSSI_THRESHOLD_HIGH;
|
||||
|
||||
netdev->event_filter(netdev, event, netdev->user_data);
|
||||
}
|
||||
|
||||
static void netdev_set_rssi_level_idx(struct netdev *netdev)
|
||||
{
|
||||
uint8_t new_level;
|
||||
|
||||
for (new_level = 0; new_level < netdev->rssi_levels_num; new_level++)
|
||||
if (netdev->cur_rssi >= netdev->rssi_levels[new_level])
|
||||
break;
|
||||
|
||||
netdev->cur_rssi_level_idx = new_level;
|
||||
}
|
||||
|
||||
static void netdev_rssi_level_init(struct netdev *netdev)
|
||||
{
|
||||
if (netdev->connected && netdev->rssi_levels_num)
|
||||
@ -752,6 +856,8 @@ static void netdev_connect_ok(struct netdev *netdev)
|
||||
netdev->connect_cb(netdev, NETDEV_RESULT_OK, netdev->user_data);
|
||||
netdev->connect_cb = NULL;
|
||||
}
|
||||
|
||||
netdev_rssi_polling_update(netdev);
|
||||
}
|
||||
|
||||
static void netdev_setting_keys_failed(struct netdev *netdev,
|
||||
@ -2221,6 +2327,8 @@ int netdev_reassociate(struct netdev *netdev, struct scan_bss *target_bss,
|
||||
|
||||
netdev->operational = false;
|
||||
|
||||
netdev_rssi_polling_update(netdev);
|
||||
|
||||
/*
|
||||
* Cancel commands that could be running because of EAPoL activity
|
||||
* like re-keying, this way the callbacks for those commands don't
|
||||
@ -2439,6 +2547,8 @@ int netdev_fast_transition(struct netdev *netdev, struct scan_bss *target_bss,
|
||||
netdev->group_management_new_key_cmd_id = 0;
|
||||
}
|
||||
|
||||
netdev_rssi_polling_update(netdev);
|
||||
|
||||
return 0;
|
||||
|
||||
restore_snonce:
|
||||
@ -2851,13 +2961,13 @@ int netdev_set_rssi_report_levels(struct netdev *netdev, const int8_t *levels,
|
||||
{
|
||||
struct l_genl_msg *cmd_set_cqm;
|
||||
|
||||
if (!wiphy_get_ext_feature(netdev->wiphy,
|
||||
NL80211_EXT_FEATURE_CQM_RSSI_LIST))
|
||||
return levels_num ? -ENOTSUP : 0;
|
||||
|
||||
if (levels_num > L_ARRAY_SIZE(netdev->rssi_levels))
|
||||
return -ENOSPC;
|
||||
|
||||
if (!wiphy_get_ext_feature(netdev->wiphy,
|
||||
NL80211_EXT_FEATURE_CQM_RSSI_LIST))
|
||||
goto done;
|
||||
|
||||
cmd_set_cqm = netdev_build_cmd_cqm_rssi_update(netdev, levels,
|
||||
levels_num);
|
||||
if (!cmd_set_cqm)
|
||||
@ -2872,10 +2982,13 @@ int netdev_set_rssi_report_levels(struct netdev *netdev, const int8_t *levels,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
done:
|
||||
memcpy(netdev->rssi_levels, levels, levels_num);
|
||||
netdev->rssi_levels_num = levels_num;
|
||||
netdev_rssi_level_init(netdev);
|
||||
|
||||
netdev_rssi_polling_update(netdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user