diff mbox series

[7/8] station: support user-disabled bands

Message ID 20230925185422.2242494-7-prestwoj@gmail.com (mailing list archive)
State New
Headers show
Series [1/8] scan: add [Rank].BandModifier2_4Ghz | expand

Commit Message

James Prestwood Sept. 25, 2023, 6:54 p.m. UTC
This adds support to allow users to disable entire bands, preventing
scanning and connecting on those frequencies. If the
[Rank].BandModifier* options are set to 0.0 it will imply those
bands should not be used for scanning, connecting or roaming. This
now applies to autoconnect, quick, hidden, roam, and dbus scans.

This is a station only feature meaning other modules like RRM, DPP,
WSC or P2P may still utilize those bands. Trying to limit bands in
those modules may sometimes conflict with the spec which is why it
was not added there. In addition modules like DPP/WSC are only used
in limited capacity for connecting so there is little benefit gained
to disallowing those bands.
---
 src/station.c | 134 +++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 110 insertions(+), 24 deletions(-)

Comments

Denis Kenzior Sept. 27, 2023, 8:49 p.m. UTC | #1
Hi James,

On 9/25/23 13:54, James Prestwood wrote:
> This adds support to allow users to disable entire bands, preventing
> scanning and connecting on those frequencies. If the
> [Rank].BandModifier* options are set to 0.0 it will imply those
> bands should not be used for scanning, connecting or roaming. This
> now applies to autoconnect, quick, hidden, roam, and dbus scans.
> 
> This is a station only feature meaning other modules like RRM, DPP,
> WSC or P2P may still utilize those bands. Trying to limit bands in
> those modules may sometimes conflict with the spec which is why it
> was not added there. In addition modules like DPP/WSC are only used
> in limited capacity for connecting so there is little benefit gained
> to disallowing those bands.
> ---
>   src/station.c | 134 +++++++++++++++++++++++++++++++++++++++++---------
>   1 file changed, 110 insertions(+), 24 deletions(-)
> 

<snip>

> @@ -1326,6 +1329,20 @@ static bool station_needs_hidden_network_scan(struct station *station)
>   	return !l_queue_isempty(station->hidden_bss_list_sorted);
>   }
>   
> +static struct scan_freq_set *station_get_allowed_freqs(struct station *station)
> +{
> +	uint32_t band_mask = 0;
> +
> +	if (!band_2_4ghz_disabled)
> +		band_mask |= BAND_FREQ_2_4_GHZ;
> +	if (!band_5ghz_disabled)
> +		band_mask |= BAND_FREQ_5_GHZ;
> +	if (!band_6ghz_disabled)
> +		band_mask |= BAND_FREQ_6_GHZ;

You might as well compute the band_mask in station_init directly.

> +
> +	return wiphy_get_allowed_freqs(station->wiphy, band_mask);

Same question here as the previous patch, should this be supported instead of 
allowed?

> +}
> +
>   static uint32_t station_scan_trigger(struct station *station,
>   					struct scan_freq_set *freqs,
>   					scan_trigger_func_t triggered,
> @@ -1411,6 +1428,7 @@ static void station_quick_scan_destroy(void *userdata)
>   static int station_quick_scan_trigger(struct station *station)
>   {
>   	_auto_(scan_freq_set_free) struct scan_freq_set *known_freq_set = NULL;
> +	_auto_(scan_freq_set_free) struct scan_freq_set *allowed = NULL;
>   	bool known_6ghz;
>   
>   	if (wiphy_regdom_is_updating(station->wiphy)) {
> @@ -1440,9 +1458,14 @@ static int station_quick_scan_trigger(struct station *station)
>   			known_6ghz)
>   		return -ENOTSUP;
>   
> -	if (!wiphy_constrain_freq_set(station->wiphy, known_freq_set)) {
> +	allowed = station_get_allowed_freqs(station);
> +	if (L_WARN_ON(!allowed))
> +		return -ENOTSUP;
> +
> +	scan_freq_set_constrain(known_freq_set, allowed);

This looks fine, but does bring up the question.  If we're now enabling 6G 
frequencies to be put into scan requests (even if they're not allowed now, in 
hope that regdom allows them after the split scan) should this logic be using 
supported frequencies and not allowed ones?

> +
> +	if (scan_freq_set_isempty(known_freq_set))
>   		return -ENOTSUP;
> -	}
>   
>   	station->quick_scan_id = station_scan_trigger(station,
>   						known_freq_set,
> @@ -2656,6 +2679,11 @@ static int station_roam_scan(struct station *station,
>   				struct scan_freq_set *freq_set)
>   {
>   	struct scan_parameters params = { .freqs = freq_set, .flush = true };
> +	_auto_(scan_freq_set_free) struct scan_freq_set *allowed =
> +					station_get_allowed_freqs(station);
> +
> +	if (L_WARN_ON(!allowed))
> +		return -ENOTSUP;
>   
>   	l_debug("ifindex: %u", netdev_get_ifindex(station->netdev));
>   
> @@ -2666,8 +2694,14 @@ static int station_roam_scan(struct station *station,
>   		params.ssid_len = strlen(ssid);
>   	}
>   
> -	if (!freq_set)
> +	if (!freq_set) {
>   		station->roam_scan_full = true;
> +		params.freqs = allowed;
> +	} else
> +		scan_freq_set_constrain(freq_set, allowed);
> +

Same as above?

> +	if (L_WARN_ON(scan_freq_set_isempty(params.freqs)))
> +		return -ENOTSUP;
>   
>   	station->roam_scan_id =
>   		scan_active_full(netdev_get_wdev_id(station->netdev), &params,

<snip>

> @@ -4268,38 +4308,77 @@ int station_hide_network(struct station *station, struct network *network)
>   	return 0;
>   }
>   
> -static void station_add_one_freq(uint32_t freq, void *user_data)
> +static void station_add_6ghz_freq(uint32_t freq, void *user_data)
>   {
> -	struct station *station = user_data;
> +	struct scan_freq_set *set = user_data;
>   
> -	if (freq > 3000)
> -		scan_freq_set_add(station->scan_freqs_order[1], freq);
> -	else if (!scan_freq_set_contains(station->scan_freqs_order[0], freq))
> -		scan_freq_set_add(station->scan_freqs_order[2], freq);
> +	if (freq > 5935)
> +		scan_freq_set_add(set, freq);
> +}
> +
> +static void station_add_5ghz_freq(uint32_t freq, void *user_data)
> +{
> +	struct scan_freq_set *set = user_data;
> +
> +	if (freq > 3000 && freq < 5935)
> +		scan_freq_set_add(set, freq);
> +}
> +
> +static void station_add_2_4ghz_freq(uint32_t freq, void *user_data)
> +{
> +	struct scan_freq_set *set = user_data;
> +
> +	/* exclude social channels added in initial scan request */
> +	if (freq < 3000 && freq != 2412 && freq != 2437 && freq != 2462)
> +		scan_freq_set_add(set, freq);
>   }
>   
>   static void station_fill_scan_freq_subsets(struct station *station)
>   {
> -	const struct scan_freq_set *supported =
> -		wiphy_get_supported_freqs(station->wiphy);
> +	_auto_(scan_freq_set_free) struct scan_freq_set *allowed =
> +					station_get_allowed_freqs(station);
> +	unsigned int subset_idx = 0;
>   
> +	if (L_WARN_ON(!allowed))
> +		return;
>   	/*
>   	 * Scan the 2.4GHz "social channels" first, 5GHz second, if supported,
>   	 * all other 2.4GHz channels last.  To be refined as needed.
>   	 */
> -	station->scan_freqs_order[0] = scan_freq_set_new();
> -	scan_freq_set_add(station->scan_freqs_order[0], 2412);
> -	scan_freq_set_add(station->scan_freqs_order[0], 2437);
> -	scan_freq_set_add(station->scan_freqs_order[0], 2462);
> -
> -	station->scan_freqs_order[1] = scan_freq_set_new();
> -	station->scan_freqs_order[2] = scan_freq_set_new();
> -	scan_freq_set_foreach(supported, station_add_one_freq, station);
> -
> -	if (scan_freq_set_isempty(station->scan_freqs_order[1])) {
> -		scan_freq_set_free(station->scan_freqs_order[1]);
> -		station->scan_freqs_order[1] = station->scan_freqs_order[2];
> -		station->scan_freqs_order[2] = NULL;
> +	if (!band_2_4ghz_disabled) {
> +		station->scan_freqs_order[subset_idx] = scan_freq_set_new();
> +		scan_freq_set_add(station->scan_freqs_order[subset_idx], 2412);
> +		scan_freq_set_add(station->scan_freqs_order[subset_idx], 2437);
> +		scan_freq_set_add(station->scan_freqs_order[subset_idx], 2462);
> +		subset_idx++;
> +	}
> +
> +	/*
> +	 * TODO: It may might sense to split up 5 and 6ghz into separate subsets
> +	 *       since the channel set is so large.
> +	 */
> +	if (!band_5ghz_disabled || !band_6ghz_disabled) {
> +		struct scan_freq_set *set = scan_freq_set_new();
> +
> +		if (!band_5ghz_disabled)
> +			scan_freq_set_foreach(allowed,
> +						station_add_5ghz_freq, set);
> +		if (!band_6ghz_disabled)
> +			scan_freq_set_foreach(allowed,
> +						station_add_6ghz_freq, set);

Is there a better way to generate 5G/6G freqs than scan_freq_set_foreach?  Isn't 
this wiphy_get_allowed_freqs() with a relevant band_mask?

> +
> +		/* 5/6ghz didn't add any frequencies */
> +		if (scan_freq_set_isempty(set)) {
> +			scan_freq_set_free(set);
> +		} else
> +			station->scan_freqs_order[subset_idx++] = set;
> +	}
> +
> +	/* Add remaining 2.4ghz channels to subset */
> +	if (!band_2_4ghz_disabled) {
> +		station->scan_freqs_order[subset_idx] = scan_freq_set_new();
> +		scan_freq_set_foreach(allowed, station_add_2_4ghz_freq,
> +					station->scan_freqs_order[subset_idx]);
>   	}
>   }
>   

Regards,
-Denis
James Prestwood Sept. 28, 2023, 11:35 a.m. UTC | #2
Hi Denis,

On 9/27/23 1:49 PM, Denis Kenzior wrote:
> Hi James,
> 
> On 9/25/23 13:54, James Prestwood wrote:
>> This adds support to allow users to disable entire bands, preventing
>> scanning and connecting on those frequencies. If the
>> [Rank].BandModifier* options are set to 0.0 it will imply those
>> bands should not be used for scanning, connecting or roaming. This
>> now applies to autoconnect, quick, hidden, roam, and dbus scans.
>>
>> This is a station only feature meaning other modules like RRM, DPP,
>> WSC or P2P may still utilize those bands. Trying to limit bands in
>> those modules may sometimes conflict with the spec which is why it
>> was not added there. In addition modules like DPP/WSC are only used
>> in limited capacity for connecting so there is little benefit gained
>> to disallowing those bands.
>> ---
>>   src/station.c | 134 +++++++++++++++++++++++++++++++++++++++++---------
>>   1 file changed, 110 insertions(+), 24 deletions(-)
>>
> 
> <snip>
> 
>> @@ -1326,6 +1329,20 @@ static bool 
>> station_needs_hidden_network_scan(struct station *station)
>>       return !l_queue_isempty(station->hidden_bss_list_sorted);
>>   }
>> +static struct scan_freq_set *station_get_allowed_freqs(struct station 
>> *station)
>> +{
>> +    uint32_t band_mask = 0;
>> +
>> +    if (!band_2_4ghz_disabled)
>> +        band_mask |= BAND_FREQ_2_4_GHZ;
>> +    if (!band_5ghz_disabled)
>> +        band_mask |= BAND_FREQ_5_GHZ;
>> +    if (!band_6ghz_disabled)
>> +        band_mask |= BAND_FREQ_6_GHZ;
> 
> You might as well compute the band_mask in station_init directly.
> 
>> +
>> +    return wiphy_get_allowed_freqs(station->wiphy, band_mask);
> 
> Same question here as the previous patch, should this be supported 
> instead of allowed?
> 
>> +}
>> +
>>   static uint32_t station_scan_trigger(struct station *station,
>>                       struct scan_freq_set *freqs,
>>                       scan_trigger_func_t triggered,
>> @@ -1411,6 +1428,7 @@ static void station_quick_scan_destroy(void 
>> *userdata)
>>   static int station_quick_scan_trigger(struct station *station)
>>   {
>>       _auto_(scan_freq_set_free) struct scan_freq_set *known_freq_set 
>> = NULL;
>> +    _auto_(scan_freq_set_free) struct scan_freq_set *allowed = NULL;
>>       bool known_6ghz;
>>       if (wiphy_regdom_is_updating(station->wiphy)) {
>> @@ -1440,9 +1458,14 @@ static int station_quick_scan_trigger(struct 
>> station *station)
>>               known_6ghz)
>>           return -ENOTSUP;
>> -    if (!wiphy_constrain_freq_set(station->wiphy, known_freq_set)) {
>> +    allowed = station_get_allowed_freqs(station);
>> +    if (L_WARN_ON(!allowed))
>> +        return -ENOTSUP;
>> +
>> +    scan_freq_set_constrain(known_freq_set, allowed);
> 
> This looks fine, but does bring up the question.  If we're now enabling 
> 6G frequencies to be put into scan requests (even if they're not allowed 
> now, in hope that regdom allows them after the split scan) should this 
> logic be using supported frequencies and not allowed ones?

I suppose there isn't any harm in that. I was more or less keeping 
things the same as they were, where only periodic scans are responsible 
for splitting and re-issuing 6ghz scans. But if IWD is doing a full scan 
via roaming, dbus, hidden etc. we might as well try and use 6ghz.

And here specifically, yes, I should retain 6ghz frequencies in the set.

> 
>> +
>> +    if (scan_freq_set_isempty(known_freq_set))
>>           return -ENOTSUP;
>> -    }
>>       station->quick_scan_id = station_scan_trigger(station,
>>                           known_freq_set,
>> @@ -2656,6 +2679,11 @@ static int station_roam_scan(struct station 
>> *station,
>>                   struct scan_freq_set *freq_set)
>>   {
>>       struct scan_parameters params = { .freqs = freq_set, .flush = 
>> true };
>> +    _auto_(scan_freq_set_free) struct scan_freq_set *allowed =
>> +                    station_get_allowed_freqs(station);
>> +
>> +    if (L_WARN_ON(!allowed))
>> +        return -ENOTSUP;
>>       l_debug("ifindex: %u", netdev_get_ifindex(station->netdev));
>> @@ -2666,8 +2694,14 @@ static int station_roam_scan(struct station 
>> *station,
>>           params.ssid_len = strlen(ssid);
>>       }
>> -    if (!freq_set)
>> +    if (!freq_set) {
>>           station->roam_scan_full = true;
>> +        params.freqs = allowed;
>> +    } else
>> +        scan_freq_set_constrain(freq_set, allowed);
>> +
> 
> Same as above?
> 
>> +    if (L_WARN_ON(scan_freq_set_isempty(params.freqs)))
>> +        return -ENOTSUP;
>>       station->roam_scan_id =
>>           scan_active_full(netdev_get_wdev_id(station->netdev), &params,
> 
> <snip>
> 
>> @@ -4268,38 +4308,77 @@ int station_hide_network(struct station 
>> *station, struct network *network)
>>       return 0;
>>   }
>> -static void station_add_one_freq(uint32_t freq, void *user_data)
>> +static void station_add_6ghz_freq(uint32_t freq, void *user_data)
>>   {
>> -    struct station *station = user_data;
>> +    struct scan_freq_set *set = user_data;
>> -    if (freq > 3000)
>> -        scan_freq_set_add(station->scan_freqs_order[1], freq);
>> -    else if (!scan_freq_set_contains(station->scan_freqs_order[0], 
>> freq))
>> -        scan_freq_set_add(station->scan_freqs_order[2], freq);
>> +    if (freq > 5935)
>> +        scan_freq_set_add(set, freq);
>> +}
>> +
>> +static void station_add_5ghz_freq(uint32_t freq, void *user_data)
>> +{
>> +    struct scan_freq_set *set = user_data;
>> +
>> +    if (freq > 3000 && freq < 5935)
>> +        scan_freq_set_add(set, freq);
>> +}
>> +
>> +static void station_add_2_4ghz_freq(uint32_t freq, void *user_data)
>> +{
>> +    struct scan_freq_set *set = user_data;
>> +
>> +    /* exclude social channels added in initial scan request */
>> +    if (freq < 3000 && freq != 2412 && freq != 2437 && freq != 2462)
>> +        scan_freq_set_add(set, freq);
>>   }
>>   static void station_fill_scan_freq_subsets(struct station *station)
>>   {
>> -    const struct scan_freq_set *supported =
>> -        wiphy_get_supported_freqs(station->wiphy);
>> +    _auto_(scan_freq_set_free) struct scan_freq_set *allowed =
>> +                    station_get_allowed_freqs(station);
>> +    unsigned int subset_idx = 0;
>> +    if (L_WARN_ON(!allowed))
>> +        return;
>>       /*
>>        * Scan the 2.4GHz "social channels" first, 5GHz second, if 
>> supported,
>>        * all other 2.4GHz channels last.  To be refined as needed.
>>        */
>> -    station->scan_freqs_order[0] = scan_freq_set_new();
>> -    scan_freq_set_add(station->scan_freqs_order[0], 2412);
>> -    scan_freq_set_add(station->scan_freqs_order[0], 2437);
>> -    scan_freq_set_add(station->scan_freqs_order[0], 2462);
>> -
>> -    station->scan_freqs_order[1] = scan_freq_set_new();
>> -    station->scan_freqs_order[2] = scan_freq_set_new();
>> -    scan_freq_set_foreach(supported, station_add_one_freq, station);
>> -
>> -    if (scan_freq_set_isempty(station->scan_freqs_order[1])) {
>> -        scan_freq_set_free(station->scan_freqs_order[1]);
>> -        station->scan_freqs_order[1] = station->scan_freqs_order[2];
>> -        station->scan_freqs_order[2] = NULL;
>> +    if (!band_2_4ghz_disabled) {
>> +        station->scan_freqs_order[subset_idx] = scan_freq_set_new();
>> +        scan_freq_set_add(station->scan_freqs_order[subset_idx], 2412);
>> +        scan_freq_set_add(station->scan_freqs_order[subset_idx], 2437);
>> +        scan_freq_set_add(station->scan_freqs_order[subset_idx], 2462);
>> +        subset_idx++;
>> +    }
>> +
>> +    /*
>> +     * TODO: It may might sense to split up 5 and 6ghz into separate 
>> subsets
>> +     *       since the channel set is so large.
>> +     */
>> +    if (!band_5ghz_disabled || !band_6ghz_disabled) {
>> +        struct scan_freq_set *set = scan_freq_set_new();
>> +
>> +        if (!band_5ghz_disabled)
>> +            scan_freq_set_foreach(allowed,
>> +                        station_add_5ghz_freq, set);
>> +        if (!band_6ghz_disabled)
>> +            scan_freq_set_foreach(allowed,
>> +                        station_add_6ghz_freq, set);
> 
> Is there a better way to generate 5G/6G freqs than 
> scan_freq_set_foreach?  Isn't this wiphy_get_allowed_freqs() with a 
> relevant band_mask?
> 
>> +
>> +        /* 5/6ghz didn't add any frequencies */
>> +        if (scan_freq_set_isempty(set)) {
>> +            scan_freq_set_free(set);
>> +        } else
>> +            station->scan_freqs_order[subset_idx++] = set;
>> +    }
>> +
>> +    /* Add remaining 2.4ghz channels to subset */
>> +    if (!band_2_4ghz_disabled) {
>> +        station->scan_freqs_order[subset_idx] = scan_freq_set_new();
>> +        scan_freq_set_foreach(allowed, station_add_2_4ghz_freq,
>> +                    station->scan_freqs_order[subset_idx]);
>>       }
>>   }
> 
> Regards,
> -Denis
diff mbox series

Patch

diff --git a/src/station.c b/src/station.c
index 9e4ee69a..8e552f84 100644
--- a/src/station.c
+++ b/src/station.c
@@ -73,6 +73,9 @@  static bool supports_arp_evict_nocarrier;
 static bool supports_ndisc_evict_nocarrier;
 static struct watchlist event_watches;
 static uint32_t known_networks_watch;
+static bool band_2_4ghz_disabled;
+static bool band_5ghz_disabled;
+static bool band_6ghz_disabled;
 
 struct station {
 	enum station_state state;
@@ -1326,6 +1329,20 @@  static bool station_needs_hidden_network_scan(struct station *station)
 	return !l_queue_isempty(station->hidden_bss_list_sorted);
 }
 
+static struct scan_freq_set *station_get_allowed_freqs(struct station *station)
+{
+	uint32_t band_mask = 0;
+
+	if (!band_2_4ghz_disabled)
+		band_mask |= BAND_FREQ_2_4_GHZ;
+	if (!band_5ghz_disabled)
+		band_mask |= BAND_FREQ_5_GHZ;
+	if (!band_6ghz_disabled)
+		band_mask |= BAND_FREQ_6_GHZ;
+
+	return wiphy_get_allowed_freqs(station->wiphy, band_mask);
+}
+
 static uint32_t station_scan_trigger(struct station *station,
 					struct scan_freq_set *freqs,
 					scan_trigger_func_t triggered,
@@ -1411,6 +1428,7 @@  static void station_quick_scan_destroy(void *userdata)
 static int station_quick_scan_trigger(struct station *station)
 {
 	_auto_(scan_freq_set_free) struct scan_freq_set *known_freq_set = NULL;
+	_auto_(scan_freq_set_free) struct scan_freq_set *allowed = NULL;
 	bool known_6ghz;
 
 	if (wiphy_regdom_is_updating(station->wiphy)) {
@@ -1440,9 +1458,14 @@  static int station_quick_scan_trigger(struct station *station)
 			known_6ghz)
 		return -ENOTSUP;
 
-	if (!wiphy_constrain_freq_set(station->wiphy, known_freq_set)) {
+	allowed = station_get_allowed_freqs(station);
+	if (L_WARN_ON(!allowed))
+		return -ENOTSUP;
+
+	scan_freq_set_constrain(known_freq_set, allowed);
+
+	if (scan_freq_set_isempty(known_freq_set))
 		return -ENOTSUP;
-	}
 
 	station->quick_scan_id = station_scan_trigger(station,
 						known_freq_set,
@@ -2656,6 +2679,11 @@  static int station_roam_scan(struct station *station,
 				struct scan_freq_set *freq_set)
 {
 	struct scan_parameters params = { .freqs = freq_set, .flush = true };
+	_auto_(scan_freq_set_free) struct scan_freq_set *allowed =
+					station_get_allowed_freqs(station);
+
+	if (L_WARN_ON(!allowed))
+		return -ENOTSUP;
 
 	l_debug("ifindex: %u", netdev_get_ifindex(station->netdev));
 
@@ -2666,8 +2694,14 @@  static int station_roam_scan(struct station *station,
 		params.ssid_len = strlen(ssid);
 	}
 
-	if (!freq_set)
+	if (!freq_set) {
 		station->roam_scan_full = true;
+		params.freqs = allowed;
+	} else
+		scan_freq_set_constrain(freq_set, allowed);
+
+	if (L_WARN_ON(scan_freq_set_isempty(params.freqs)))
+		return -ENOTSUP;
 
 	station->roam_scan_id =
 		scan_active_full(netdev_get_wdev_id(station->netdev), &params,
@@ -3597,6 +3631,7 @@  static struct l_dbus_message *station_dbus_connect_hidden_network(
 	};
 	const char *ssid;
 	struct network *network;
+	_auto_(scan_freq_set_free) struct scan_freq_set *allowed = NULL;
 
 	l_debug("");
 
@@ -3647,6 +3682,11 @@  not_hidden:
 		return dbus_error_not_hidden(message);
 	}
 
+	allowed = station_get_allowed_freqs(station);
+	if (L_WARN_ON(!allowed))
+		return dbus_error_not_supported(message);
+
+	params.freqs = allowed;
 	params.ssid = (const uint8_t *)ssid;
 	params.ssid_len = strlen(ssid);
 
@@ -4268,38 +4308,77 @@  int station_hide_network(struct station *station, struct network *network)
 	return 0;
 }
 
-static void station_add_one_freq(uint32_t freq, void *user_data)
+static void station_add_6ghz_freq(uint32_t freq, void *user_data)
 {
-	struct station *station = user_data;
+	struct scan_freq_set *set = user_data;
 
-	if (freq > 3000)
-		scan_freq_set_add(station->scan_freqs_order[1], freq);
-	else if (!scan_freq_set_contains(station->scan_freqs_order[0], freq))
-		scan_freq_set_add(station->scan_freqs_order[2], freq);
+	if (freq > 5935)
+		scan_freq_set_add(set, freq);
+}
+
+static void station_add_5ghz_freq(uint32_t freq, void *user_data)
+{
+	struct scan_freq_set *set = user_data;
+
+	if (freq > 3000 && freq < 5935)
+		scan_freq_set_add(set, freq);
+}
+
+static void station_add_2_4ghz_freq(uint32_t freq, void *user_data)
+{
+	struct scan_freq_set *set = user_data;
+
+	/* exclude social channels added in initial scan request */
+	if (freq < 3000 && freq != 2412 && freq != 2437 && freq != 2462)
+		scan_freq_set_add(set, freq);
 }
 
 static void station_fill_scan_freq_subsets(struct station *station)
 {
-	const struct scan_freq_set *supported =
-		wiphy_get_supported_freqs(station->wiphy);
+	_auto_(scan_freq_set_free) struct scan_freq_set *allowed =
+					station_get_allowed_freqs(station);
+	unsigned int subset_idx = 0;
 
+	if (L_WARN_ON(!allowed))
+		return;
 	/*
 	 * Scan the 2.4GHz "social channels" first, 5GHz second, if supported,
 	 * all other 2.4GHz channels last.  To be refined as needed.
 	 */
-	station->scan_freqs_order[0] = scan_freq_set_new();
-	scan_freq_set_add(station->scan_freqs_order[0], 2412);
-	scan_freq_set_add(station->scan_freqs_order[0], 2437);
-	scan_freq_set_add(station->scan_freqs_order[0], 2462);
-
-	station->scan_freqs_order[1] = scan_freq_set_new();
-	station->scan_freqs_order[2] = scan_freq_set_new();
-	scan_freq_set_foreach(supported, station_add_one_freq, station);
-
-	if (scan_freq_set_isempty(station->scan_freqs_order[1])) {
-		scan_freq_set_free(station->scan_freqs_order[1]);
-		station->scan_freqs_order[1] = station->scan_freqs_order[2];
-		station->scan_freqs_order[2] = NULL;
+	if (!band_2_4ghz_disabled) {
+		station->scan_freqs_order[subset_idx] = scan_freq_set_new();
+		scan_freq_set_add(station->scan_freqs_order[subset_idx], 2412);
+		scan_freq_set_add(station->scan_freqs_order[subset_idx], 2437);
+		scan_freq_set_add(station->scan_freqs_order[subset_idx], 2462);
+		subset_idx++;
+	}
+
+	/*
+	 * TODO: It may might sense to split up 5 and 6ghz into separate subsets
+	 *       since the channel set is so large.
+	 */
+	if (!band_5ghz_disabled || !band_6ghz_disabled) {
+		struct scan_freq_set *set = scan_freq_set_new();
+
+		if (!band_5ghz_disabled)
+			scan_freq_set_foreach(allowed,
+						station_add_5ghz_freq, set);
+		if (!band_6ghz_disabled)
+			scan_freq_set_foreach(allowed,
+						station_add_6ghz_freq, set);
+
+		/* 5/6ghz didn't add any frequencies */
+		if (scan_freq_set_isempty(set)) {
+			scan_freq_set_free(set);
+		} else
+			station->scan_freqs_order[subset_idx++] = set;
+	}
+
+	/* Add remaining 2.4ghz channels to subset */
+	if (!band_2_4ghz_disabled) {
+		station->scan_freqs_order[subset_idx] = scan_freq_set_new();
+		scan_freq_set_foreach(allowed, station_add_2_4ghz_freq,
+					station->scan_freqs_order[subset_idx]);
 	}
 }
 
@@ -5194,6 +5273,13 @@  static int station_init(void)
 						station_known_networks_changed,
 						NULL, NULL);
 
+	band_2_4ghz_disabled =
+			scan_get_band_rank_modifier(BAND_FREQ_2_4_GHZ) == 0.0;
+	band_5ghz_disabled =
+			scan_get_band_rank_modifier(BAND_FREQ_5_GHZ) == 0.0;
+	band_6ghz_disabled =
+			scan_get_band_rank_modifier(BAND_FREQ_6_GHZ) == 0.0;
+
 	return 0;
 }