* [PATCH v3 1/7] wiphy: track GET_REG ID
@ 2022-08-04 18:51 James Prestwood
2022-08-04 18:51 ` [PATCH v3 2/7] wiphy: dump wiphy's on regulatory domain change James Prestwood
` (6 more replies)
0 siblings, 7 replies; 9+ messages in thread
From: James Prestwood @ 2022-08-04 18:51 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
Keep track of the GET_REG call so it can be canceled if the wiphy
goes down.
---
src/wiphy.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
v3:
* Moved this out into its own patch
diff --git a/src/wiphy.c b/src/wiphy.c
index 8cb9eb39..30b860da 100644
--- a/src/wiphy.c
+++ b/src/wiphy.c
@@ -121,6 +121,7 @@ struct wiphy {
/* Work queue for this radio */
struct l_queue *work;
bool work_in_callback;
+ unsigned int get_reg_id;
bool support_scheduled_scan:1;
bool support_rekey_offload:1;
@@ -341,6 +342,9 @@ static void wiphy_free(void *data)
l_debug("Freeing wiphy %s[%u]", wiphy->name, wiphy->id);
+ if (wiphy->get_reg_id)
+ l_genl_family_cancel(nl80211, wiphy->get_reg_id);
+
for (i = 0; i < NUM_NL80211_IFTYPES; i++)
l_free(wiphy->iftype_extended_capabilities[i]);
@@ -1875,6 +1879,8 @@ static void wiphy_get_reg_cb(struct l_genl_msg *msg, void *user_data)
uint32_t tmp;
bool global;
+ wiphy->get_reg_id = 0;
+
/*
* NL80211_CMD_GET_REG contains an NL80211_ATTR_WIPHY iff the wiphy
* uses a self-managed regulatory domain.
@@ -1892,8 +1898,9 @@ static void wiphy_get_reg_domain(struct wiphy *wiphy)
msg = l_genl_msg_new(NL80211_CMD_GET_REG);
l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY, 4, &wiphy->id);
- if (!l_genl_family_send(wiphy->nl80211, msg, wiphy_get_reg_cb, wiphy,
- NULL)) {
+ wiphy->get_reg_id = l_genl_family_send(wiphy->nl80211, msg,
+ wiphy_get_reg_cb, wiphy, NULL);
+ if (!wiphy->get_reg_id) {
l_error("Error sending NL80211_CMD_GET_REG for %s", wiphy->name);
l_genl_msg_unref(msg);
}
--
2.34.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 2/7] wiphy: dump wiphy's on regulatory domain change
2022-08-04 18:51 [PATCH v3 1/7] wiphy: track GET_REG ID James Prestwood
@ 2022-08-04 18:51 ` James Prestwood
2022-08-04 18:51 ` [PATCH v3 3/7] wiphy: add wiphy_regdom_is_updating James Prestwood
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: James Prestwood @ 2022-08-04 18:51 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
A change in regulatory domain can result in frequencies being
enabled or disabled depending on the domain. This effects the
frequencies stored in wiphy which other modules depend on
such as scanning, offchannel work etc.
When the regulatory domain changes re-dump the wiphy in order
to update any frequency restrictions.
---
src/wiphy.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 195 insertions(+), 7 deletions(-)
v3:
* Removed the GET_REG handling and instead only dump wiphy from
the notify callback directly
diff --git a/src/wiphy.c b/src/wiphy.c
index 30b860da..dfdca989 100644
--- a/src/wiphy.c
+++ b/src/wiphy.c
@@ -66,6 +66,7 @@ static char **blacklist_filter;
static int mac_randomize_bytes = 6;
static char regdom_country[2];
static uint32_t work_ids;
+static unsigned int wiphy_dump_id;
enum driver_flag {
DEFAULT_IF = 0x1,
@@ -105,6 +106,7 @@ struct wiphy {
uint16_t supported_ciphers;
struct scan_freq_set *supported_freqs;
struct scan_freq_set *disabled_freqs;
+ struct scan_freq_set *pending_freqs;
struct band *band_2g;
struct band *band_5g;
struct band *band_6g;
@@ -122,6 +124,7 @@ struct wiphy {
struct l_queue *work;
bool work_in_callback;
unsigned int get_reg_id;
+ unsigned int dump_id;
bool support_scheduled_scan:1;
bool support_rekey_offload:1;
@@ -342,6 +345,9 @@ static void wiphy_free(void *data)
l_debug("Freeing wiphy %s[%u]", wiphy->name, wiphy->id);
+ if (wiphy->dump_id)
+ l_genl_family_cancel(nl80211, wiphy->dump_id);
+
if (wiphy->get_reg_id)
l_genl_family_cancel(nl80211, wiphy->get_reg_id);
@@ -1839,6 +1845,182 @@ static void wiphy_setup_rm_enabled_capabilities(struct wiphy *wiphy)
*/
}
+static void wiphy_dump_done(void *user_data)
+{
+ struct wiphy *wiphy = user_data;
+ const struct l_queue_entry *e;
+
+ /* This dump was canceled due to another dump */
+ if ((wiphy && !wiphy->dump_id) || (!wiphy && !wiphy_dump_id))
+ return;
+
+ if (wiphy) {
+ wiphy->dump_id = 0;
+ scan_freq_set_free(wiphy->disabled_freqs);
+ wiphy->disabled_freqs = wiphy->pending_freqs;
+ wiphy->pending_freqs = NULL;
+
+ WATCHLIST_NOTIFY(&wiphy->state_watches,
+ wiphy_state_watch_func_t, wiphy,
+ WIPHY_STATE_WATCH_EVENT_REGDOM_DONE);
+
+ return;
+ }
+
+ wiphy_dump_id = 0;
+
+ for (e = l_queue_get_entries(wiphy_list); e; e = e->next) {
+ wiphy = e->data;
+
+ if (!wiphy->pending_freqs || wiphy->self_managed)
+ continue;
+
+ scan_freq_set_free(wiphy->disabled_freqs);
+ wiphy->disabled_freqs = wiphy->pending_freqs;
+ wiphy->pending_freqs = NULL;
+
+ WATCHLIST_NOTIFY(&wiphy->state_watches,
+ wiphy_state_watch_func_t, wiphy,
+ WIPHY_STATE_WATCH_EVENT_REGDOM_DONE);
+ }
+}
+
+/* We are dumping wiphy(s) due to a regulatory change */
+static void wiphy_dump_callback(struct l_genl_msg *msg,
+ void *user_data)
+{
+ struct wiphy *wiphy;
+ uint32_t id;
+ struct l_genl_attr bands;
+ struct l_genl_attr attr;
+ uint16_t type;
+
+ if (nl80211_parse_attrs(msg, NL80211_ATTR_WIPHY, &id,
+ NL80211_ATTR_WIPHY_BANDS, &bands,
+ NL80211_ATTR_UNSPEC) < 0)
+ return;
+
+ wiphy = wiphy_find(id);
+ if (L_WARN_ON(!wiphy))
+ return;
+
+ while (l_genl_attr_next(&bands, NULL, NULL, NULL)) {
+ if (!l_genl_attr_recurse(&bands, &attr))
+ return;
+
+ while (l_genl_attr_next(&attr, &type, NULL, NULL)) {
+ if (type != NL80211_BAND_ATTR_FREQS)
+ continue;
+
+ nl80211_parse_supported_frequencies(&attr, NULL,
+ wiphy->pending_freqs);
+ }
+ }
+}
+
+static bool wiphy_cancel_last_dump(struct wiphy *wiphy)
+{
+ const struct l_queue_entry *e;
+ unsigned int id = 0;
+
+ /*
+ * Zero command ID to signal that wiphy_dump_done doesn't need to do
+ * anything. For a self-managed wiphy just free/NULL pending_freqs. For
+ * a global dump each wiphy needs to be checked and dealt with.
+ */
+ if (wiphy && wiphy->dump_id) {
+ id = wiphy->dump_id;
+ wiphy->dump_id = 0;
+
+ scan_freq_set_free(wiphy->pending_freqs);
+ wiphy->pending_freqs = NULL;
+ } else if (!wiphy && wiphy_dump_id) {
+ id = wiphy_dump_id;
+ wiphy_dump_id = 0;
+
+ for (e = l_queue_get_entries(wiphy_list); e; e = e->next) {
+ struct wiphy *w = e->data;
+
+ if (!w->pending_freqs || w->self_managed)
+ continue;
+
+ scan_freq_set_free(w->pending_freqs);
+ w->pending_freqs = NULL;
+ }
+ }
+
+ if (id) {
+ l_debug("Canceling pending regdom wiphy dump (%s)",
+ wiphy ? wiphy->name : "global");
+
+ l_genl_family_cancel(nl80211, id);
+ }
+
+ return id != 0;
+}
+
+static void wiphy_dump_after_regdom(struct wiphy *wiphy)
+{
+ const struct l_queue_entry *e;
+ struct l_genl_msg *msg;
+ unsigned int id;
+ bool no_start_event;
+
+ msg = l_genl_msg_new_sized(NL80211_CMD_GET_WIPHY, 128);
+
+ if (wiphy)
+ l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY, 4, &wiphy->id);
+
+ l_genl_msg_append_attr(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP, 0, NULL);
+ id = l_genl_family_dump(nl80211, msg, wiphy_dump_callback,
+ wiphy, wiphy_dump_done);
+ if (!id) {
+ l_error("Wiphy information dump failed");
+ l_genl_msg_unref(msg);
+ return;
+ }
+
+ /*
+ * Another update while dumping wiphy. This next dump should supercede
+ * the first and not result in a DONE event until this new dump is
+ * finished. This is because the disabled frequencies are in an unknown
+ * state and could cause incorrect behavior by any watchers.
+ */
+ no_start_event = wiphy_cancel_last_dump(wiphy);
+
+ /* Limited dump so just emit the event for this wiphy */
+ if (wiphy) {
+ wiphy->dump_id = id;
+ wiphy->pending_freqs = scan_freq_set_new();
+
+ if (no_start_event)
+ return;
+
+ WATCHLIST_NOTIFY(&wiphy->state_watches,
+ wiphy_state_watch_func_t, wiphy,
+ WIPHY_STATE_WATCH_EVENT_REGDOM_STARTED);
+ return;
+ }
+
+ wiphy_dump_id = id;
+
+ /* Otherwise for a global regdom change notify for all wiphy's */
+ for (e = l_queue_get_entries(wiphy_list); e; e = e->next) {
+ struct wiphy *w = e->data;
+
+ if (w->self_managed)
+ continue;
+
+ w->pending_freqs = scan_freq_set_new();
+
+ if (no_start_event)
+ continue;
+
+ WATCHLIST_NOTIFY(&w->state_watches, wiphy_state_watch_func_t,
+ w, WIPHY_STATE_WATCH_EVENT_REGDOM_STARTED);
+ }
+}
+
static void wiphy_update_reg_domain(struct wiphy *wiphy, bool global,
struct l_genl_msg *msg)
{
@@ -2110,6 +2292,8 @@ static void setup_wiphy_interface(struct l_dbus_interface *interface)
static void wiphy_reg_notify(struct l_genl_msg *msg, void *user_data)
{
uint8_t cmd = l_genl_msg_get_command(msg);
+ struct wiphy *wiphy = NULL;
+ uint32_t wiphy_id;
l_debug("Notification of command %s(%u)",
nl80211cmd_to_string(cmd), cmd);
@@ -2119,22 +2303,21 @@ static void wiphy_reg_notify(struct l_genl_msg *msg, void *user_data)
wiphy_update_reg_domain(NULL, true, msg);
break;
case NL80211_CMD_WIPHY_REG_CHANGE:
- {
- uint32_t wiphy_id;
- struct wiphy *wiphy;
-
if (nl80211_parse_attrs(msg, NL80211_ATTR_WIPHY, &wiphy_id,
NL80211_ATTR_UNSPEC) < 0)
- break;
+ return;
wiphy = wiphy_find(wiphy_id);
if (!wiphy)
- break;
+ return;
wiphy_update_reg_domain(wiphy, false, msg);
break;
+ default:
+ return;
}
- }
+
+ wiphy_dump_after_regdom(wiphy);
}
static void wiphy_radio_work_next(struct wiphy *wiphy)
@@ -2313,6 +2496,11 @@ static void wiphy_exit(void)
l_strfreev(whitelist_filter);
l_strfreev(blacklist_filter);
+ if (wiphy_dump_id) {
+ l_genl_family_cancel(nl80211, wiphy_dump_id);
+ wiphy_dump_id = 0;
+ }
+
l_queue_destroy(wiphy_list, wiphy_free);
wiphy_list = NULL;
--
2.34.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 3/7] wiphy: add wiphy_regdom_is_updating
2022-08-04 18:51 [PATCH v3 1/7] wiphy: track GET_REG ID James Prestwood
2022-08-04 18:51 ` [PATCH v3 2/7] wiphy: dump wiphy's on regulatory domain change James Prestwood
@ 2022-08-04 18:51 ` James Prestwood
2022-08-04 18:51 ` [PATCH v3 4/7] station: do full passive scan if 6GHz is supported but disabled James Prestwood
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: James Prestwood @ 2022-08-04 18:51 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
This allows a module to check the current status of the regdom
in case it misses the wiphy event (e.g. registers after the
STARTED event).
---
src/wiphy.c | 5 +++++
src/wiphy.h | 1 +
2 files changed, 6 insertions(+)
diff --git a/src/wiphy.c b/src/wiphy.c
index dfdca989..3beb351c 100644
--- a/src/wiphy.c
+++ b/src/wiphy.c
@@ -921,6 +921,11 @@ int wiphy_estimate_data_rate(struct wiphy *wiphy,
out_data_rate);
}
+bool wiphy_regdom_is_updating(struct wiphy *wiphy)
+{
+ return wiphy->pending_freqs != NULL;
+}
+
uint32_t wiphy_state_watch_add(struct wiphy *wiphy,
wiphy_state_watch_func_t func,
void *user_data, wiphy_destroy_func_t destroy)
diff --git a/src/wiphy.h b/src/wiphy.h
index da2eca20..2159fc00 100644
--- a/src/wiphy.h
+++ b/src/wiphy.h
@@ -134,6 +134,7 @@ int wiphy_estimate_data_rate(struct wiphy *wiphy,
const void *ies, uint16_t ies_len,
const struct scan_bss *bss,
uint64_t *out_data_rate);
+bool wiphy_regdom_is_updating(struct wiphy *wiphy);
uint32_t wiphy_state_watch_add(struct wiphy *wiphy,
wiphy_state_watch_func_t func, void *user_data,
--
2.34.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 4/7] station: do full passive scan if 6GHz is supported but disabled.
2022-08-04 18:51 [PATCH v3 1/7] wiphy: track GET_REG ID James Prestwood
2022-08-04 18:51 ` [PATCH v3 2/7] wiphy: dump wiphy's on regulatory domain change James Prestwood
2022-08-04 18:51 ` [PATCH v3 3/7] wiphy: add wiphy_regdom_is_updating James Prestwood
@ 2022-08-04 18:51 ` James Prestwood
2022-08-04 19:20 ` Denis Kenzior
2022-08-04 18:51 ` [PATCH v3 5/7] scan: split full scans by band to enable 6GHz James Prestwood
` (3 subsequent siblings)
6 siblings, 1 reply; 9+ messages in thread
From: James Prestwood @ 2022-08-04 18:51 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
The kernel handles setting the regulatory domain by receiving beacons which
set the country IE. Presumably since most regulatory domains disallow 6GHz
the default (world) domain also disables it. This means until the country
is set, 6GHz is disabled.
This poses a problem for IWD's quick scanning since it only scans a few
frequencies and this likely isn't enough beacons for the firmware to update
the country, leaving 6Ghz inaccessable to the user without manual intervention
(e.g. iw scan passive, or periodic scans by IWD).
To try and work around this limitation the quick scan logic has been updated
to check if a 6GHz AP has been connected to before and if that frequency is
disabled (but supported). If this is the case IWD will opt for a full passive
scan rather than scanning a limited set of frequencies.
---
src/station.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 68 insertions(+), 1 deletion(-)
v3:
* Return -EAGAIN in the case of a regdom update rather than 0. Check this
along with 0 in both calls.
* Re-worked the wiphy state event to focus only on the DONE event, and set
regdom_updated based on wiphy_regdom_is_updating. This avoids a switch
statement when its mainly the DONE event we care about.
* Check quick_scan_id if regdom update so we aren't accidentally re-triggering
a quick scan.
diff --git a/src/station.c b/src/station.c
index e39e9d4b..e3a36f46 100644
--- a/src/station.c
+++ b/src/station.c
@@ -112,6 +112,8 @@ struct station {
struct scan_freq_set *scan_freqs_order[3];
unsigned int dbus_scan_subset_idx;
+ uint32_t wiphy_watch;
+
bool preparing_roam : 1;
bool roam_scan_full : 1;
bool signal_low : 1;
@@ -119,6 +121,7 @@ struct station {
bool scanning : 1;
bool autoconnect : 1;
bool autoconnect_can_start : 1;
+ bool regdom_updating : 1;
};
struct anqp_entry {
@@ -1353,11 +1356,36 @@ static void station_quick_scan_destroy(void *userdata)
static int station_quick_scan_trigger(struct station *station)
{
struct scan_freq_set *known_freq_set;
+ bool known_6ghz;
+ const struct scan_freq_set *disabled = wiphy_get_disabled_freqs(
+ station->wiphy);
+
+ if (station->regdom_updating) {
+ l_debug("regdom is updating, delaying quick scan");
+
+ return -EAGAIN;
+ }
known_freq_set = known_networks_get_recent_frequencies(5);
if (!known_freq_set)
return -ENODATA;
+ known_6ghz = scan_freq_set_get_bands(known_freq_set) & BAND_FREQ_6_GHZ;
+
+ /*
+ * This means IWD has previously connected to a 6GHz AP before, but now
+ * the regulatory domain disallows 6GHz likely caused by a reboot, the
+ * firmware going down, or a regulatory update. The only way to
+ * re-enable 6GHz is to get enough beacons via scanning for the firmware
+ * to set the regulatory domain. A quick scan is very unlikely to do
+ * this since its so limited, so return an error which will fall back to
+ * full autoconnect.
+ */
+ if ((scan_freq_set_get_bands(disabled) & BAND_FREQ_6_GHZ) &&
+ wiphy_country_is_unknown(station->wiphy) &&
+ known_6ghz)
+ return -ENOTSUP;
+
if (!wiphy_constrain_freq_set(station->wiphy, known_freq_set)) {
scan_freq_set_free(known_freq_set);
return -ENOTSUP;
@@ -1446,6 +1474,7 @@ static void station_enter_state(struct station *station,
uint64_t id = netdev_get_wdev_id(station->netdev);
struct l_dbus *dbus = dbus_get_bus();
bool disconnected;
+ int ret;
l_debug("Old State: %s, new state: %s",
station_state_to_string(station->state),
@@ -1462,7 +1491,8 @@ static void station_enter_state(struct station *station,
switch (state) {
case STATION_STATE_AUTOCONNECT_QUICK:
- if (!station_quick_scan_trigger(station))
+ ret = station_quick_scan_trigger(station);
+ if (ret == 0 || ret == -EAGAIN)
break;
station->state = STATION_STATE_AUTOCONNECT_FULL;
@@ -3988,6 +4018,36 @@ static void station_fill_scan_freq_subsets(struct station *station)
}
}
+static void station_wiphy_watch(struct wiphy *wiphy,
+ enum wiphy_state_watch_event event,
+ void *user_data)
+{
+ struct station *station = user_data;
+ int ret;
+
+ station->regdom_updating = wiphy_regdom_is_updating(wiphy);
+
+ if (event != WIPHY_STATE_WATCH_EVENT_REGDOM_DONE)
+ return;
+
+ /*
+ * The only state that requires special handling is for
+ * quick scans since the previous quick scan was delayed until
+ * the regulatory domain updated. Try again in case 6Ghz is now
+ * unlocked (unlikely), or advance to full autoconnect. Just in
+ * case this update came during a quick scan, ignore it.
+ */
+ if (station->state != STATION_STATE_AUTOCONNECT_QUICK ||
+ station->quick_scan_id)
+ return;
+
+ ret = station_quick_scan_trigger(station);
+ if (ret == 0 || ret == -EAGAIN)
+ return;
+
+ station_enter_state(station, STATION_STATE_AUTOCONNECT_FULL);
+}
+
static struct station *station_create(struct netdev *netdev)
{
struct station *station;
@@ -4008,6 +4068,11 @@ static struct station *station_create(struct netdev *netdev)
station->wiphy = netdev_get_wiphy(netdev);
station->netdev = netdev;
+ station->wiphy_watch = wiphy_state_watch_add(station->wiphy,
+ station_wiphy_watch,
+ station, NULL);
+ station->regdom_updating = wiphy_regdom_is_updating(station->wiphy);
+
l_queue_push_head(station_list, station);
l_dbus_object_add_interface(dbus, netdev_get_path(netdev),
@@ -4117,6 +4182,8 @@ static void station_free(struct station *station)
if (station->scan_freqs_order[2])
scan_freq_set_free(station->scan_freqs_order[2]);
+ wiphy_state_watch_remove(station->wiphy, station->wiphy_watch);
+
l_free(station);
}
--
2.34.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 5/7] scan: split full scans by band to enable 6GHz
2022-08-04 18:51 [PATCH v3 1/7] wiphy: track GET_REG ID James Prestwood
` (2 preceding siblings ...)
2022-08-04 18:51 ` [PATCH v3 4/7] station: do full passive scan if 6GHz is supported but disabled James Prestwood
@ 2022-08-04 18:51 ` James Prestwood
2022-08-04 18:51 ` [PATCH v3 6/7] scan: watch for regdom updates " James Prestwood
` (2 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: James Prestwood @ 2022-08-04 18:51 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
The kernel's regulatory domain updates after some number of beacons
are processed. This triggers a regulatory domain update (and wiphy
dump) but only after a scan request. This means a full scan started
prior to the regdom being set will not include any 6Ghz BSS's even
if the regdom was unlocked during the scan.
This can be worked around by splitting up a large scan request into
multiple requests allowing one of the first commands to trigger a
regdom update. Once the regdom updates (and wiphy dumps) we are
hopefully still scanning and could append an additional request to
scan 6GHz.
---
src/scan.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 89 insertions(+), 8 deletions(-)
diff --git a/src/scan.c b/src/scan.c
index b666ba2e..e016bd4d 100644
--- a/src/scan.c
+++ b/src/scan.c
@@ -352,9 +352,24 @@ static bool scan_mac_address_randomization_is_disabled(void)
return disabled;
}
+static struct scan_freq_set *scan_get_allowed_freqs(struct scan_context *sc)
+{
+ struct scan_freq_set *allowed = scan_freq_set_new();
+
+ scan_freq_set_merge(allowed, wiphy_get_supported_freqs(sc->wiphy));
+
+ if (!wiphy_constrain_freq_set(sc->wiphy, allowed)) {
+ scan_freq_set_free(allowed);
+ allowed = NULL;
+ }
+
+ return allowed;
+}
+
static struct l_genl_msg *scan_build_cmd(struct scan_context *sc,
bool ignore_flush_flag, bool is_passive,
- const struct scan_parameters *params)
+ const struct scan_parameters *params,
+ const struct scan_freq_set *freqs)
{
struct l_genl_msg *msg;
uint32_t flags = 0;
@@ -366,8 +381,8 @@ static struct l_genl_msg *scan_build_cmd(struct scan_context *sc,
if (wiphy_get_max_scan_ie_len(sc->wiphy))
scan_build_attr_ie(msg, sc, params);
- if (params->freqs)
- scan_build_attr_scan_frequencies(msg, params->freqs);
+ if (freqs)
+ scan_build_attr_scan_frequencies(msg, freqs);
if (params->flush && !ignore_flush_flag && wiphy_has_feature(sc->wiphy,
NL80211_FEATURE_SCAN_FLUSH))
@@ -524,16 +539,36 @@ static bool scan_cmds_add_hidden(const struct network_info *network,
* of all scans in the batch after the last scan is finished.
*/
*data->cmd = scan_build_cmd(data->sc, true, false,
- data->params);
+ data->params,
+ data->params->freqs);
l_genl_msg_enter_nested(*data->cmd, NL80211_ATTR_SCAN_SSIDS);
}
return true;
}
-static void scan_cmds_add(struct l_queue *cmds, struct scan_context *sc,
+static void scan_foreach_freq_split_bands(uint32_t freq, void *user_data)
+{
+ struct scan_freq_set **subsets = user_data;
+ int idx;
+
+ if (freq < 3000)
+ idx = 0;
+ else if (freq < 6000)
+ idx = 1;
+ else
+ idx = 2;
+
+ if (!subsets[idx])
+ subsets[idx] = scan_freq_set_new();
+
+ scan_freq_set_add(subsets[idx], freq);
+}
+
+static void scan_build_next_cmd(struct l_queue *cmds, struct scan_context *sc,
bool passive,
- const struct scan_parameters *params)
+ const struct scan_parameters *params,
+ const struct scan_freq_set *freqs)
{
struct l_genl_msg *cmd;
struct scan_cmds_add_data data = {
@@ -544,7 +579,7 @@ static void scan_cmds_add(struct l_queue *cmds, struct scan_context *sc,
wiphy_get_max_num_ssids_per_scan(sc->wiphy),
};
- cmd = scan_build_cmd(sc, false, passive, params);
+ cmd = scan_build_cmd(sc, false, passive, params, freqs);
if (passive) {
/* passive scan */
@@ -572,6 +607,52 @@ static void scan_cmds_add(struct l_queue *cmds, struct scan_context *sc,
l_queue_push_tail(cmds, cmd);
}
+static void scan_cmds_add(struct l_queue *cmds, struct scan_context *sc,
+ bool passive,
+ const struct scan_parameters *params)
+{
+ unsigned int i;
+ struct scan_freq_set *subsets[3] = { 0 };
+ struct scan_freq_set *allowed;
+
+ /*
+ * If the frequencies are explicit don't break up the request
+ */
+ if (params->freqs) {
+ scan_build_next_cmd(cmds, sc, passive, params, params->freqs);
+ return;
+ }
+
+ /*
+ * Otherwise a full spectrum scan will likely open up the 6GHz
+ * band. The problem is the regdom update occurs after an
+ * individual scan request so a single request isn't going to
+ * include potential 6GHz results.
+ *
+ * Instead we can break this full scan up into individual bands
+ * and increase our chances of the regdom updating after one of
+ * the earlier requests. If it does update to allow 6GHz an
+ * extra 6GHz-only passive scan can be appended to this request
+ * at that time.
+ */
+
+ allowed = scan_get_allowed_freqs(sc);
+ if (L_WARN_ON(!allowed))
+ return;
+
+ scan_freq_set_foreach(allowed, scan_foreach_freq_split_bands, subsets);
+ scan_freq_set_free(allowed);
+
+ for(i = 0; i < L_ARRAY_SIZE(subsets); i++) {
+ if (!subsets[i])
+ continue;
+
+ scan_build_next_cmd(cmds, sc, passive, params,
+ subsets[i]);
+ scan_freq_set_free(subsets[i]);
+ }
+}
+
static int scan_request_send_trigger(struct scan_context *sc,
struct scan_request *sr)
{
@@ -743,7 +824,7 @@ static void add_owe_scan_cmd(struct scan_context *sc, struct scan_request *sr,
params.ssid_len = bss->owe_trans->ssid_len;
params.flush = true;
- cmd = scan_build_cmd(sc, ignore_flush, false, ¶ms);
+ cmd = scan_build_cmd(sc, ignore_flush, false, ¶ms, params.freqs);
l_genl_msg_enter_nested(cmd, NL80211_ATTR_SCAN_SSIDS);
l_genl_msg_append_attr(cmd, 0, params.ssid_len, params.ssid);
--
2.34.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 6/7] scan: watch for regdom updates to enable 6GHz
2022-08-04 18:51 [PATCH v3 1/7] wiphy: track GET_REG ID James Prestwood
` (3 preceding siblings ...)
2022-08-04 18:51 ` [PATCH v3 5/7] scan: split full scans by band to enable 6GHz James Prestwood
@ 2022-08-04 18:51 ` James Prestwood
2022-08-04 18:51 ` [PATCH v3 7/7] wiphy: don't re-dump wiphy if the regdom didn't change James Prestwood
2022-08-04 19:18 ` [PATCH v3 1/7] wiphy: track GET_REG ID Denis Kenzior
6 siblings, 0 replies; 9+ messages in thread
From: James Prestwood @ 2022-08-04 18:51 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
This functionality works around the kernel's behavior of allowing
6GHz only after a regulatory domain update. If the regdom updates
scan.c needs to be aware in order to split up periodic scans, or
insert 6GHz frequencies into an ongoing periodic scan. Doing this
allows any 6GHz BSS's to show up in the scan results rather than
needing to issue an entirely new scan to see these BSS's.
---
src/scan.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 147 insertions(+), 18 deletions(-)
diff --git a/src/scan.c b/src/scan.c
index e016bd4d..40e527d9 100644
--- a/src/scan.c
+++ b/src/scan.c
@@ -71,6 +71,8 @@ struct scan_periodic {
void *userdata;
uint32_t id;
bool needs_active_scan:1;
+ /* Delay periodic scan results until regulatory domain updates */
+ bool wait_on_regdom:1;
};
struct scan_request {
@@ -108,6 +110,7 @@ struct scan_request {
struct scan_context {
uint64_t wdev_id;
+ uint32_t wiphy_watch_id;
/*
* Tells us whether a scan, our own or external, is running.
* Set when scan gets triggered, cleared when scan done and
@@ -126,6 +129,8 @@ struct scan_context {
*/
unsigned int get_fw_scan_cmd_id;
struct wiphy *wiphy;
+ /* 6GHz may become available after a regdom update */
+ bool expect_6ghz : 1;
};
struct scan_results {
@@ -184,24 +189,6 @@ static void scan_request_failed(struct scan_context *sc,
wiphy_radio_work_done(sc->wiphy, sr->work.id);
}
-static struct scan_context *scan_context_new(uint64_t wdev_id)
-{
- struct wiphy *wiphy = wiphy_find_by_wdev(wdev_id);
- struct scan_context *sc;
-
- if (!wiphy)
- return NULL;
-
- sc = l_new(struct scan_context, 1);
-
- sc->wdev_id = wdev_id;
- sc->wiphy = wiphy;
- sc->state = SCAN_STATE_NOT_RUNNING;
- sc->requests = l_queue_new();
-
- return sc;
-}
-
static void scan_request_cancel(void *data)
{
struct scan_request *sr = data;
@@ -227,6 +214,8 @@ static void scan_context_free(struct scan_context *sc)
if (sc->get_fw_scan_cmd_id && nl80211)
l_genl_family_cancel(nl80211, sc->get_fw_scan_cmd_id);
+ wiphy_state_watch_remove(sc->wiphy, sc->wiphy_watch_id);
+
l_free(sc);
}
@@ -477,6 +466,142 @@ done:
return msg;
}
+static void scan_add_6ghz(uint32_t freq, void *user_data)
+{
+ struct scan_freq_set *freqs = user_data;
+
+ if (freq < 6000)
+ return;
+
+ scan_freq_set_add(freqs, freq);
+}
+
+static void scan_wiphy_watch(struct wiphy *wiphy,
+ enum wiphy_state_watch_event event,
+ void *user_data)
+{
+ struct scan_context *sc = user_data;
+ struct scan_request *sr = NULL;
+ struct l_genl_msg *msg;
+ struct scan_parameters params = { 0 };
+ struct scan_freq_set *freqs_6ghz;
+ struct scan_freq_set *allowed;
+ bool allow_6g;
+ const struct scan_freq_set *supported =
+ wiphy_get_supported_freqs(wiphy);
+
+ /* Only care about regulatory events, and if 6GHz capable */
+ if ((event != WIPHY_STATE_WATCH_EVENT_REGDOM_STARTED &&
+ event != WIPHY_STATE_WATCH_EVENT_REGDOM_DONE) ||
+ !(scan_freq_set_get_bands(supported) & BAND_FREQ_6_GHZ))
+ return;
+
+ allowed = scan_get_allowed_freqs(sc);
+ allow_6g = scan_freq_set_get_bands(allowed) & BAND_FREQ_6_GHZ;
+
+ switch (event) {
+ case WIPHY_STATE_WATCH_EVENT_REGDOM_STARTED:
+ if (allow_6g)
+ goto done;
+
+ sc->expect_6ghz = true;
+
+ /*
+ * If there is a running/queued periodic scan we may need to
+ * delay the results if 6GHz becomes available.
+ */
+ if (sc->sp.id)
+ sc->sp.wait_on_regdom = true;
+
+ goto done;
+ case WIPHY_STATE_WATCH_EVENT_REGDOM_DONE:
+ if (!sc->expect_6ghz)
+ goto done;
+
+ sc->expect_6ghz = false;
+
+ if (!sc->sp.id)
+ goto done;
+
+ sr = l_queue_find(sc->requests, scan_request_match,
+ L_UINT_TO_PTR(sc->sp.id));
+ if (!sr)
+ goto done;
+
+ /* This update did not allow 6GHz */
+ if (!allow_6g)
+ goto check_periodic;
+
+ /*
+ * At this point we know there is an ongoing periodic scan.
+ * Create a new 6GHz passive scan request and append to the
+ * command list
+ */
+ freqs_6ghz = scan_freq_set_new();
+
+ scan_freq_set_foreach(allowed, scan_add_6ghz, freqs_6ghz);
+
+ msg = scan_build_cmd(sc, false, true, ¶ms, freqs_6ghz);
+ l_queue_push_tail(sr->cmds, msg);
+
+ scan_freq_set_free(freqs_6ghz);
+
+check_periodic:
+ if (sc->sp.wait_on_regdom) {
+ /* Periodic scan results delayed until this update */
+ if (sr && l_queue_peek_head(sc->requests) == sr)
+ start_next_scan_request(&sr->work);
+
+ sc->sp.wait_on_regdom = false;
+ }
+
+ break;
+ default:
+ return;
+ }
+
+done:
+ scan_freq_set_free(allowed);
+}
+
+/*
+ * Should only used to initialize 'expect_6ghz'. This is only to cover the case
+ * of a scan context being created during a regulatory update, which means it
+ * would miss the START event. If a DONE event comes, and expect_6ghz is false,
+ * no further action would be taken which may be incorrect.
+ */
+static bool scan_expect_6ghz(struct wiphy *wiphy)
+{
+ const struct scan_freq_set *supported =
+ wiphy_get_supported_freqs(wiphy);
+
+ if (!(scan_freq_set_get_bands(supported) & BAND_FREQ_6_GHZ))
+ return false;
+
+ return wiphy_regdom_is_updating(wiphy);
+}
+
+static struct scan_context *scan_context_new(uint64_t wdev_id)
+{
+ struct wiphy *wiphy = wiphy_find_by_wdev(wdev_id);
+ struct scan_context *sc;
+
+ if (!wiphy)
+ return NULL;
+
+ sc = l_new(struct scan_context, 1);
+
+ sc->wdev_id = wdev_id;
+ sc->wiphy = wiphy;
+ sc->state = SCAN_STATE_NOT_RUNNING;
+ sc->requests = l_queue_new();
+ sc->expect_6ghz = scan_expect_6ghz(wiphy);
+ sc->wiphy_watch_id = wiphy_state_watch_add(wiphy, scan_wiphy_watch,
+ sc, NULL);
+
+ return sc;
+}
+
struct l_genl_msg *scan_build_trigger_scan_bss(uint32_t ifindex,
struct wiphy *wiphy,
uint32_t frequency,
@@ -2120,6 +2245,10 @@ static void scan_notify(struct l_genl_msg *msg, void *user_data)
if (sr && sr->triggered) {
sr->triggered = false;
+ /* Regdom changed during a periodic scan */
+ if (sc->sp.id == sr->work.id && sc->sp.wait_on_regdom)
+ return;
+
if (!sr->callback) {
scan_finished(sc, -ECANCELED, NULL, NULL, sr);
break;
--
2.34.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v3 7/7] wiphy: don't re-dump wiphy if the regdom didn't change
2022-08-04 18:51 [PATCH v3 1/7] wiphy: track GET_REG ID James Prestwood
` (4 preceding siblings ...)
2022-08-04 18:51 ` [PATCH v3 6/7] scan: watch for regdom updates " James Prestwood
@ 2022-08-04 18:51 ` James Prestwood
2022-08-04 19:18 ` [PATCH v3 1/7] wiphy: track GET_REG ID Denis Kenzior
6 siblings, 0 replies; 9+ messages in thread
From: James Prestwood @ 2022-08-04 18:51 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
For whatever reason the kernel will send regdom updates even if
the regdom didn't change. This ends up causing wiphy to dump
which isn't needed since there should be no changes in disabled
frequencies.
Now the previous country is checked against the new one, and if
they match the wiphy is not dumped again.
---
src/wiphy.c | 39 ++++++++++++++++++++++++++++-----------
1 file changed, 28 insertions(+), 11 deletions(-)
diff --git a/src/wiphy.c b/src/wiphy.c
index 3beb351c..28d4b66e 100644
--- a/src/wiphy.c
+++ b/src/wiphy.c
@@ -2026,10 +2026,19 @@ static void wiphy_dump_after_regdom(struct wiphy *wiphy)
}
}
-static void wiphy_update_reg_domain(struct wiphy *wiphy, bool global,
+static bool wiphy_update_reg_domain(struct wiphy *wiphy, bool global,
struct l_genl_msg *msg)
{
- char *out_country;
+ char out_country[2];
+ char *orig;
+
+ /*
+ * Write the new country code or XX if the reg domain is not a
+ * country domain.
+ */
+ if (nl80211_parse_attrs(msg, NL80211_ATTR_REG_ALPHA2, out_country,
+ NL80211_ATTR_UNSPEC) < 0)
+ out_country[0] = out_country[1] = 'X';
if (global)
/*
@@ -2043,21 +2052,26 @@ static void wiphy_update_reg_domain(struct wiphy *wiphy, bool global,
* wiphy created (that is not self-managed anyway) and we
* haven't received any REG_CHANGE events yet.
*/
- out_country = regdom_country;
+ orig = regdom_country;
+
else
- out_country = wiphy->regdom_country;
+ orig = wiphy->regdom_country;
/*
- * Write the new country code or XX if the reg domain is not a
- * country domain.
+ * The kernel seems to send regdom updates even if the country didn't
+ * change. Skip these as there is no reason to re-dump.
*/
- if (nl80211_parse_attrs(msg, NL80211_ATTR_REG_ALPHA2, out_country,
- NL80211_ATTR_UNSPEC) < 0)
- out_country[0] = out_country[1] = 'X';
+ if (orig[0] == out_country[0] && orig[1] == out_country[1])
+ return false;
l_debug("New reg domain country code for %s is %c%c",
global ? "(global)" : wiphy->name,
out_country[0], out_country[1]);
+
+ orig[0] = out_country[0];
+ orig[1] = out_country[1];
+
+ return true;
}
static void wiphy_get_reg_cb(struct l_genl_msg *msg, void *user_data)
@@ -2305,7 +2319,8 @@ static void wiphy_reg_notify(struct l_genl_msg *msg, void *user_data)
switch (cmd) {
case NL80211_CMD_REG_CHANGE:
- wiphy_update_reg_domain(NULL, true, msg);
+ if (!wiphy_update_reg_domain(NULL, true, msg))
+ return;
break;
case NL80211_CMD_WIPHY_REG_CHANGE:
if (nl80211_parse_attrs(msg, NL80211_ATTR_WIPHY, &wiphy_id,
@@ -2316,7 +2331,9 @@ static void wiphy_reg_notify(struct l_genl_msg *msg, void *user_data)
if (!wiphy)
return;
- wiphy_update_reg_domain(wiphy, false, msg);
+ if (!wiphy_update_reg_domain(wiphy, false, msg))
+ return;
+
break;
default:
return;
--
2.34.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v3 1/7] wiphy: track GET_REG ID
2022-08-04 18:51 [PATCH v3 1/7] wiphy: track GET_REG ID James Prestwood
` (5 preceding siblings ...)
2022-08-04 18:51 ` [PATCH v3 7/7] wiphy: don't re-dump wiphy if the regdom didn't change James Prestwood
@ 2022-08-04 19:18 ` Denis Kenzior
6 siblings, 0 replies; 9+ messages in thread
From: Denis Kenzior @ 2022-08-04 19:18 UTC (permalink / raw)
To: James Prestwood, iwd
Hi James,
On 8/4/22 13:51, James Prestwood wrote:
> Keep track of the GET_REG call so it can be canceled if the wiphy
> goes down.
> ---
> src/wiphy.c | 11 +++++++++--
> 1 file changed, 9 insertions(+), 2 deletions(-)
>
> v3:
> * Moved this out into its own patch
>
Patch 1,2,3 and 7 applied, thanks.
Regards,
-Denis
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3 4/7] station: do full passive scan if 6GHz is supported but disabled.
2022-08-04 18:51 ` [PATCH v3 4/7] station: do full passive scan if 6GHz is supported but disabled James Prestwood
@ 2022-08-04 19:20 ` Denis Kenzior
0 siblings, 0 replies; 9+ messages in thread
From: Denis Kenzior @ 2022-08-04 19:20 UTC (permalink / raw)
To: James Prestwood, iwd
Hi James,
On 8/4/22 13:51, James Prestwood wrote:
> The kernel handles setting the regulatory domain by receiving beacons which
> set the country IE. Presumably since most regulatory domains disallow 6GHz
> the default (world) domain also disables it. This means until the country
> is set, 6GHz is disabled.
>
> This poses a problem for IWD's quick scanning since it only scans a few
> frequencies and this likely isn't enough beacons for the firmware to update
> the country, leaving 6Ghz inaccessable to the user without manual intervention
> (e.g. iw scan passive, or periodic scans by IWD).
>
> To try and work around this limitation the quick scan logic has been updated
> to check if a 6GHz AP has been connected to before and if that frequency is
> disabled (but supported). If this is the case IWD will opt for a full passive
> scan rather than scanning a limited set of frequencies.
I tweaked the patch header and re-flowed the description to keep gitlint happy.
> ---
> src/station.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 68 insertions(+), 1 deletion(-)
>
> v3:
> * Return -EAGAIN in the case of a regdom update rather than 0. Check this
> along with 0 in both calls.
> * Re-worked the wiphy state event to focus only on the DONE event, and set
> regdom_updated based on wiphy_regdom_is_updating. This avoids a switch
> statement when its mainly the DONE event we care about.
> * Check quick_scan_id if regdom update so we aren't accidentally re-triggering
> a quick scan.
>
<snip>
> @@ -119,6 +121,7 @@ struct station {
> bool scanning : 1;
> bool autoconnect : 1;
> bool autoconnect_can_start : 1;
> + bool regdom_updating : 1;
I also got rid of this bit to use wiphy_regdom_is_updating() directly.
> };
>
> struct anqp_entry {
<snip>
> +static void station_wiphy_watch(struct wiphy *wiphy,
> + enum wiphy_state_watch_event event,
> + void *user_data)
> +{
> + struct station *station = user_data;
> + int ret;
> +
> + station->regdom_updating = wiphy_regdom_is_updating(wiphy);
> +
> + if (event != WIPHY_STATE_WATCH_EVENT_REGDOM_DONE)
> + return;
> +
> + /*
> + * The only state that requires special handling is for
> + * quick scans since the previous quick scan was delayed until
> + * the regulatory domain updated. Try again in case 6Ghz is now
> + * unlocked (unlikely), or advance to full autoconnect. Just in
> + * case this update came during a quick scan, ignore it.
> + */
> + if (station->state != STATION_STATE_AUTOCONNECT_QUICK ||
> + station->quick_scan_id)
> + return;
> +
> + ret = station_quick_scan_trigger(station);
> + if (ret == 0 || ret == -EAGAIN)
Since -EAGAIN shouldn't actually be possible here, I changed this to:
if (!ret)
and added an L_WARN for -EAGAIN afterwards
> + return;
> +
> + station_enter_state(station, STATION_STATE_AUTOCONNECT_FULL);
> +}
> +
Applied, thanks.
Regards,
-Denis
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2022-08-04 19:35 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-08-04 18:51 [PATCH v3 1/7] wiphy: track GET_REG ID James Prestwood
2022-08-04 18:51 ` [PATCH v3 2/7] wiphy: dump wiphy's on regulatory domain change James Prestwood
2022-08-04 18:51 ` [PATCH v3 3/7] wiphy: add wiphy_regdom_is_updating James Prestwood
2022-08-04 18:51 ` [PATCH v3 4/7] station: do full passive scan if 6GHz is supported but disabled James Prestwood
2022-08-04 19:20 ` Denis Kenzior
2022-08-04 18:51 ` [PATCH v3 5/7] scan: split full scans by band to enable 6GHz James Prestwood
2022-08-04 18:51 ` [PATCH v3 6/7] scan: watch for regdom updates " James Prestwood
2022-08-04 18:51 ` [PATCH v3 7/7] wiphy: don't re-dump wiphy if the regdom didn't change James Prestwood
2022-08-04 19:18 ` [PATCH v3 1/7] wiphy: track GET_REG ID Denis Kenzior
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox