* [PATCH 1/3] wiphy: add flag for supporting remain on channel
@ 2024-07-26 16:37 James Prestwood
2024-07-26 16:37 ` [PATCH 2/3] station: don't allow FT-over-Air without offchannel support James Prestwood
2024-07-26 16:37 ` [PATCH 3/3] netdev: fall back to RSSI polling if SET_CQM fails James Prestwood
0 siblings, 2 replies; 3+ messages in thread
From: James Prestwood @ 2024-07-26 16:37 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
---
src/wiphy.c | 9 +++++++++
src/wiphy.h | 1 +
2 files changed, 10 insertions(+)
diff --git a/src/wiphy.c b/src/wiphy.c
index fb36ebb2..ad9bf59b 100644
--- a/src/wiphy.c
+++ b/src/wiphy.c
@@ -149,6 +149,7 @@ struct wiphy {
bool self_managed : 1;
bool ap_probe_resp_offload : 1;
bool supports_uapsd : 1;
+ bool supports_cmd_offchannel : 1;
};
static struct l_queue *wiphy_list = NULL;
@@ -938,6 +939,11 @@ bool wiphy_supports_uapsd(const struct wiphy *wiphy)
return wiphy->supports_uapsd;
}
+bool wiphy_supports_cmd_offchannel(const struct wiphy *wiphy)
+{
+ return wiphy->supports_cmd_offchannel;
+}
+
const uint8_t *wiphy_get_ht_capabilities(const struct wiphy *wiphy,
enum band_freq band,
size_t *size)
@@ -1384,6 +1390,9 @@ static void parse_supported_commands(struct wiphy *wiphy,
case NL80211_CMD_ASSOCIATE:
assoc = true;
break;
+ case NL80211_CMD_REMAIN_ON_CHANNEL:
+ wiphy->supports_cmd_offchannel = true;
+ break;
}
}
diff --git a/src/wiphy.h b/src/wiphy.h
index bc82a007..17e53075 100644
--- a/src/wiphy.h
+++ b/src/wiphy.h
@@ -141,6 +141,7 @@ bool wiphy_get_rsnxe(const struct wiphy *wiphy, uint8_t *buf, size_t len);
void wiphy_get_reg_domain_country(struct wiphy *wiphy, char *out);
bool wiphy_country_is_unknown(struct wiphy *wiphy);
bool wiphy_supports_uapsd(const struct wiphy *wiphy);
+bool wiphy_supports_cmd_offchannel(const struct wiphy *wiphy);
const uint8_t *wiphy_get_ht_capabilities(const struct wiphy *wiphy,
enum band_freq band,
--
2.34.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 2/3] station: don't allow FT-over-Air without offchannel support
2024-07-26 16:37 [PATCH 1/3] wiphy: add flag for supporting remain on channel James Prestwood
@ 2024-07-26 16:37 ` James Prestwood
2024-07-26 16:37 ` [PATCH 3/3] netdev: fall back to RSSI polling if SET_CQM fails James Prestwood
1 sibling, 0 replies; 3+ messages in thread
From: James Prestwood @ 2024-07-26 16:37 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
If CMD_REMAIN_ON_CHANNEL isn't supported, don't allow FT-over-Air
---
src/station.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/src/station.c b/src/station.c
index db069981..44f8d33c 100644
--- a/src/station.c
+++ b/src/station.c
@@ -2004,7 +2004,8 @@ static void station_early_neighbor_report_cb(struct netdev *netdev, int err,
&station->roam_freqs);
}
-static bool station_can_fast_transition(struct handshake_state *hs,
+static bool station_can_fast_transition(struct station *station,
+ struct handshake_state *hs,
struct scan_bss *bss)
{
uint16_t mdid;
@@ -2032,6 +2033,16 @@ static bool station_can_fast_transition(struct handshake_state *hs,
return false;
}
+ /*
+ * FT-over-Air in its current form relies on CMD_REMAIN_ON_CHANNEL. Some
+ * drivers do not support this so only allow over-DS if this is the case
+ */
+ if (!(hs->mde[4] & 1) &&
+ !wiphy_supports_cmd_offchannel(station->wiphy)) {
+ l_debug("FT-over-Air needs offchannel, using reassociation");
+ return false;
+ }
+
return true;
}
@@ -2495,7 +2506,7 @@ static bool station_try_next_transition(struct station *station,
station->ap_directed_roaming = false;
/* Can we use Fast Transition? */
- if (station_can_fast_transition(hs, bss) && !no_ft)
+ if (station_can_fast_transition(station, hs, bss) && !no_ft)
return station_fast_transition(station, bss);
/* Non-FT transition */
--
2.34.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 3/3] netdev: fall back to RSSI polling if SET_CQM fails
2024-07-26 16:37 [PATCH 1/3] wiphy: add flag for supporting remain on channel James Prestwood
2024-07-26 16:37 ` [PATCH 2/3] station: don't allow FT-over-Air without offchannel support James Prestwood
@ 2024-07-26 16:37 ` James Prestwood
1 sibling, 0 replies; 3+ messages in thread
From: James Prestwood @ 2024-07-26 16:37 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
Some drivers fail to set a CQM threshold and report not supported.
Its unclear exactly why but if this happens roaming is effectively
broken.
To work around this enable RSSI polling if -ENOTSUP is returned.
The polling callback has been changed to emit the HIGH/LOW signal
threshold events instead of just the RSSI level index, just as if
a CQM event came from the kernel.
---
src/netdev.c | 155 ++++++++++++++++++++++++++++++++++-----------------
1 file changed, 104 insertions(+), 51 deletions(-)
diff --git a/src/netdev.c b/src/netdev.c
index 2d70fc38..c0e91e31 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -187,6 +187,7 @@ struct netdev {
bool retry_auth : 1;
bool in_reassoc : 1;
bool privacy : 1;
+ bool cqm_poll_fallback : 1;
};
struct netdev_preauth_state {
@@ -206,6 +207,9 @@ static struct l_genl_family *nl80211;
static struct l_queue *netdev_list;
static struct watchlist netdev_watches;
static bool mac_per_ssid;
+/* Threshold RSSI for roaming to trigger, configurable in main.conf */
+static int LOW_SIGNAL_THRESHOLD;
+static int LOW_SIGNAL_THRESHOLD_5GHZ;
static unsigned int iov_ie_append(struct iovec *iov,
unsigned int n_iov, unsigned int c,
@@ -642,6 +646,46 @@ static void netdev_set_rssi_level_idx(struct netdev *netdev)
netdev->cur_rssi_level_idx = new_level;
}
+static void netdev_cqm_event_rssi_value(struct netdev *netdev, int rssi_val)
+{
+ bool new_rssi_low;
+ uint8_t prev_rssi_level_idx = netdev->cur_rssi_level_idx;
+ int threshold = netdev->frequency > 4000 ? LOW_SIGNAL_THRESHOLD_5GHZ :
+ LOW_SIGNAL_THRESHOLD;
+
+ if (!netdev->connected)
+ return;
+
+ if (rssi_val > 127)
+ rssi_val = 127;
+ else if (rssi_val < -127)
+ rssi_val = -127;
+
+ netdev->cur_rssi = rssi_val;
+
+ if (!netdev->event_filter)
+ return;
+
+ new_rssi_low = rssi_val < threshold;
+ if (netdev->cur_rssi_low != new_rssi_low) {
+ int event = new_rssi_low ?
+ NETDEV_EVENT_RSSI_THRESHOLD_LOW :
+ NETDEV_EVENT_RSSI_THRESHOLD_HIGH;
+
+ netdev->cur_rssi_low = new_rssi_low;
+ netdev->event_filter(netdev, event, NULL, netdev->user_data);
+ }
+
+ if (!netdev->rssi_levels_num)
+ return;
+
+ 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->cur_rssi_level_idx,
+ netdev->user_data);
+}
+
static void netdev_rssi_poll_cb(struct l_genl_msg *msg, void *user_data)
{
struct netdev *netdev = user_data;
@@ -678,11 +722,16 @@ static void netdev_rssi_poll_cb(struct l_genl_msg *msg, void *user_data)
netdev->cur_rssi = info.cur_rssi;
/*
- * 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.
+ * If the CMD_SET_CQM call failed RSSI polling was started. In this case
+ * we should behave just like its a CQM event and check both the RSSI
+ * level indexes and the HIGH/LOW thresholds.
*/
+ if (netdev->cqm_poll_fallback) {
+ netdev_cqm_event_rssi_value(netdev, info.cur_rssi);
+ goto done;
+ }
+
+ /* Otherwise just update the level notifications, CQM events work */
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,
@@ -1027,6 +1076,9 @@ static void netdev_free(void *data)
netdev->get_link_cmd_id = 0;
}
+ if (netdev->rssi_poll_timeout)
+ l_timeout_remove(netdev->rssi_poll_timeout);
+
scan_wdev_remove(netdev->wdev_id);
watchlist_destroy(&netdev->station_watches);
@@ -1058,10 +1110,6 @@ struct netdev *netdev_find(int ifindex)
return l_queue_find(netdev_list, netdev_match, L_UINT_TO_PTR(ifindex));
}
-/* Threshold RSSI for roaming to trigger, configurable in main.conf */
-static int LOW_SIGNAL_THRESHOLD;
-static int LOW_SIGNAL_THRESHOLD_5GHZ;
-
static void netdev_cqm_event_rssi_threshold(struct netdev *netdev,
uint32_t rssi_event)
{
@@ -1085,46 +1133,6 @@ static void netdev_cqm_event_rssi_threshold(struct netdev *netdev,
netdev->event_filter(netdev, event, NULL, netdev->user_data);
}
-static void netdev_cqm_event_rssi_value(struct netdev *netdev, int rssi_val)
-{
- bool new_rssi_low;
- uint8_t prev_rssi_level_idx = netdev->cur_rssi_level_idx;
- int threshold = netdev->frequency > 4000 ? LOW_SIGNAL_THRESHOLD_5GHZ :
- LOW_SIGNAL_THRESHOLD;
-
- if (!netdev->connected)
- return;
-
- if (rssi_val > 127)
- rssi_val = 127;
- else if (rssi_val < -127)
- rssi_val = -127;
-
- netdev->cur_rssi = rssi_val;
-
- if (!netdev->event_filter)
- return;
-
- new_rssi_low = rssi_val < threshold;
- if (netdev->cur_rssi_low != new_rssi_low) {
- int event = new_rssi_low ?
- NETDEV_EVENT_RSSI_THRESHOLD_LOW :
- NETDEV_EVENT_RSSI_THRESHOLD_HIGH;
-
- netdev->cur_rssi_low = new_rssi_low;
- netdev->event_filter(netdev, event, NULL, netdev->user_data);
- }
-
- if (!netdev->rssi_levels_num)
- return;
-
- 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->cur_rssi_level_idx,
- netdev->user_data);
-}
-
static void netdev_cqm_event(struct l_genl_msg *msg, struct netdev *netdev)
{
struct l_genl_attr attr;
@@ -1372,6 +1380,9 @@ static void netdev_operstate_cb(int error, uint16_t type,
strerror(-error));
}
+static int netdev_cqm_rssi_update(struct netdev *netdev);
+
+
static void netdev_connect_ok(struct netdev *netdev)
{
l_debug("");
@@ -1383,6 +1394,8 @@ static void netdev_connect_ok(struct netdev *netdev)
netdev->operational = true;
+ netdev_cqm_rssi_update(netdev);
+
if (netdev->fw_roam_bss) {
if (netdev->event_filter)
netdev->event_filter(netdev, NETDEV_EVENT_ROAMED,
@@ -3634,11 +3647,48 @@ static struct l_genl_msg *netdev_build_cmd_cqm_rssi_update(
static void netdev_cmd_set_cqm_cb(struct l_genl_msg *msg, void *user_data)
{
+ struct netdev *netdev = user_data;
int err = l_genl_msg_get_error(msg);
const char *ext_error;
- if (err >= 0)
+ if (err >= 0) {
+ /*
+ * Looking at some driver code it appears that the -ENOTSUP CQM
+ * failure could be transient. Just in case, reset the fallback
+ * flag if CQM happens to start working again.
+ */
+ if (netdev->cqm_poll_fallback) {
+ l_debug("CMD_SET_CQM succeeded, stop polling fallback");
+
+ if (netdev->rssi_poll_timeout) {
+ l_timeout_remove(netdev->rssi_poll_timeout);
+ netdev->rssi_poll_timeout = NULL;
+ }
+
+ netdev->cqm_poll_fallback = false;
+ }
+
return;
+ }
+
+ /*
+ * Some drivers enable beacon filtering but also use software CQM which
+ * mac80211 detects and returns -ENOTSUP. There is no way to check this
+ * ahead of time so if we see this start polling in order to get RSSI
+ * updates.
+ */
+ if (err == -ENOTSUP) {
+ l_debug("CMD_SET_CQM not supported, falling back to polling");
+ netdev->cqm_poll_fallback = true;
+
+ if (netdev->rssi_poll_timeout)
+ return;
+
+ netdev->rssi_poll_timeout = l_timeout_create(1,
+ netdev_rssi_poll, netdev, NULL);
+
+ return;
+ }
ext_error = l_genl_msg_get_extended_error(msg);
l_error("CMD_SET_CQM failed: %s",
@@ -3662,7 +3712,7 @@ static int netdev_cqm_rssi_update(struct netdev *netdev)
return -EINVAL;
if (!l_genl_family_send(nl80211, msg, netdev_cmd_set_cqm_cb,
- NULL, NULL)) {
+ netdev, NULL)) {
l_genl_msg_unref(msg);
return -EIO;
}
@@ -4204,18 +4254,21 @@ static int netdev_tx_ft_frame(uint32_t ifindex, uint16_t frame_type,
uint32_t frequency, const uint8_t *dest,
struct iovec *iov, size_t iov_len)
{
+
struct netdev *netdev = netdev_find(ifindex);
struct l_genl_msg *msg = nl80211_build_cmd_frame(netdev->index,
frame_type,
netdev->addr, dest,
frequency,
iov, iov_len);
+ uint32_t duration = 200;
/*
* Even though the kernel is doing offchannel for Authentication this
* flag is still required otherwise the kernel gives -EBUSY.
*/
l_genl_msg_append_attr(msg, NL80211_ATTR_OFFCHANNEL_TX_OK, 0, NULL);
+ l_genl_msg_append_attr(msg, NL80211_ATTR_DURATION, 4, &duration);
if (!l_genl_family_send(nl80211, msg, netdev_ft_frame_cb,
netdev, NULL)) {
@@ -5260,7 +5313,7 @@ int netdev_set_rssi_report_levels(struct netdev *netdev, const int8_t *levels,
return -EINVAL;
if (!l_genl_family_send(nl80211, cmd_set_cqm, netdev_cmd_set_cqm_cb,
- NULL, NULL)) {
+ netdev, NULL)) {
l_genl_msg_unref(cmd_set_cqm);
return -EIO;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2024-07-26 16:38 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-07-26 16:37 [PATCH 1/3] wiphy: add flag for supporting remain on channel James Prestwood
2024-07-26 16:37 ` [PATCH 2/3] station: don't allow FT-over-Air without offchannel support James Prestwood
2024-07-26 16:37 ` [PATCH 3/3] netdev: fall back to RSSI polling if SET_CQM fails James Prestwood
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox