[PATCH v5 2/8] station: improve scan_freqs_order channel subsets
Alexander Ganslandt <[email protected]> Fri, 28 Nov 2025 12:54:23 +0100
| Newsgroups | dev.linux.lists.iwd |
|---|---|
| Message-ID | <[email protected]> |
From: Alexander Ganslandt <[email protected]> Splits the scan frequencies into more subsets that have been ordered such that the more common frequencies appear first, and the more uncommon frequencies last. Non-DFS channels are also added to the earlier subsets to prioritize fast-scanning frequencies. This approach allows iwd to scan the frequencies with the statistically highest chance for BSSes first, resulting in shorter scan times until a good BSS is found. In future patches this will also be used when roaming. --- src/station.c | 137 +++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 88 insertions(+), 49 deletions(-) diff --git a/src/station.c b/src/station.c index f8069d89..15937fbe 100644 --- a/src/station.c +++ b/src/station.c @@ -125,7 +125,7 @@ struct station { struct l_queue *roam_bss_list; /* Frequencies split into subsets by priority */ - struct scan_freq_set *scan_freqs_order[3]; + struct scan_freq_set *scan_freqs_order[5]; unsigned int dbus_scan_subset_idx; uint32_t wiphy_watch; @@ -4500,8 +4500,7 @@ static bool station_dbus_scan_results(int err, struct l_queue *bss_list, return false; } - last_subset = next_idx >= L_ARRAY_SIZE(station->scan_freqs_order) || - station->scan_freqs_order[next_idx] == NULL; + last_subset = next_idx >= L_ARRAY_SIZE(station->scan_freqs_order); station->dbus_scan_subset_idx = next_idx; station_set_scan_results(station, bss_list, freqs, false); @@ -4516,6 +4515,15 @@ static bool station_dbus_scan_subset(struct station *station) { unsigned int idx = station->dbus_scan_subset_idx; + /* Find the next non-empty subset */ + while (idx < L_ARRAY_SIZE(station->scan_freqs_order) && + scan_freq_set_isempty(station->scan_freqs_order[idx])) + idx++; + station->dbus_scan_subset_idx = idx; + + if (idx >= L_ARRAY_SIZE(station->scan_freqs_order)) + return false; + station->dbus_scan_id = station_scan_trigger(station, station->scan_freqs_order[idx], station_dbus_scan_triggered, @@ -5046,67 +5054,101 @@ int station_hide_network(struct station *station, struct network *network) return 0; } -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); unsigned int subset_idx = 0; - /* - * 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[subset_idx] = scan_freq_set_new(); + + /* Subset 0: 2.4GHz "social channels" and low 5GHz non-DFS channels */ if (allowed_bands & BAND_FREQ_2_4_GHZ) { - station->scan_freqs_order[subset_idx] = scan_freq_set_new(); + /* Channels 1, 6, 11 */ 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 (allowed_bands & (BAND_FREQ_5_GHZ | BAND_FREQ_6_GHZ)) { - uint32_t mask = allowed_bands & - (BAND_FREQ_5_GHZ | BAND_FREQ_6_GHZ); - struct scan_freq_set *set = scan_freq_set_clone(supported, - 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; - } + if (allowed_bands & BAND_FREQ_5_GHZ) + /* Channels 32 - 48 */ + for (int i = 5160; i <= 5240; i+=20) + scan_freq_set_add(station->scan_freqs_order[subset_idx], i); + + scan_freq_set_constrain(station->scan_freqs_order[subset_idx], supported); + station->scan_freqs_order[++subset_idx] = scan_freq_set_new(); + + /* Subset 1: Remaining common 2.4GHz channels and high 5GHz non-DFS channels */ + if (allowed_bands & BAND_FREQ_2_4_GHZ) + /* Channels 2 - 10, except 6 */ + for (int i = 2417; i < 2462; i+=5) + if (i != 2437) + scan_freq_set_add(station->scan_freqs_order[subset_idx], i); + + if (allowed_bands & BAND_FREQ_5_GHZ) + /* Channels 149 - 177 */ + for (int i = 5745; i <= 5885; i+=20) + scan_freq_set_add(station->scan_freqs_order[subset_idx], i); - /* Add remaining 2.4ghz channels to subset */ + scan_freq_set_constrain(station->scan_freqs_order[subset_idx], supported); + station->scan_freqs_order[++subset_idx] = scan_freq_set_new(); + + /* Subset 2: Uncommon 2.4GHz channels and 5GHz DFS channels */ if (allowed_bands & BAND_FREQ_2_4_GHZ) { - station->scan_freqs_order[subset_idx] = scan_freq_set_new(); - scan_freq_set_foreach(supported, station_add_2_4ghz_freq, - station->scan_freqs_order[subset_idx]); + /* Channels 12 - 14 */ + scan_freq_set_add(station->scan_freqs_order[subset_idx], 2467); + scan_freq_set_add(station->scan_freqs_order[subset_idx], 2472); + scan_freq_set_add(station->scan_freqs_order[subset_idx], 2484); + } + + if (allowed_bands & BAND_FREQ_5_GHZ) { + /* Channels 52 - 68 */ + for (int i = 5260; i <= 5340; i+=20) + scan_freq_set_add(station->scan_freqs_order[subset_idx], i); + + /* Channels 96 - 144 */ + for (int i = 5480; i <= 5720; i+=20) + scan_freq_set_add(station->scan_freqs_order[subset_idx], i); + } + + scan_freq_set_constrain(station->scan_freqs_order[subset_idx], supported); + station->scan_freqs_order[++subset_idx] = scan_freq_set_new(); + + /* Subset 3: 6GHz channels */ + if (allowed_bands & BAND_FREQ_6_GHZ) { + struct scan_freq_set *set = scan_freq_set_clone(supported, BAND_FREQ_6_GHZ); + + if (!scan_freq_set_isempty(set)) + scan_freq_set_merge(station->scan_freqs_order[subset_idx], set); + + scan_freq_set_free(set); } + scan_freq_set_constrain(station->scan_freqs_order[subset_idx], supported); + station->scan_freqs_order[++subset_idx] = scan_freq_set_clone(supported, allowed_bands); + + /* All channels that are both supported and allowed should be in the subsets, + * if this is not the case then some new channel has been added that we are + * not tracking, put it in the last subset to make sure it's scanned */ + for (unsigned int i = 0; i < L_ARRAY_SIZE(station->scan_freqs_order) - 1; i++) + scan_freq_set_subtract(station->scan_freqs_order[subset_idx], + station->scan_freqs_order[i]); + + if (!scan_freq_set_isempty(station->scan_freqs_order[subset_idx])) + l_warn("Final subset is not empty"); + /* - * This has the unintended consequence of allowing DBus scans to - * scan the entire spectrum rather than cause IWD to be completely - * non-functional. Rather than prevent DBus scans from working at all - * print a warning here. + * Loop through all subsets to see that there's at least one non-empty + * subset, otherwise iwd will not function as expected and the user + * should be warned. */ - if (station->scan_freqs_order[0] == NULL) + for (subset_idx = 0; subset_idx < L_ARRAY_SIZE(station->scan_freqs_order); subset_idx++) + if (!scan_freq_set_isempty(station->scan_freqs_order[subset_idx])) + break; + + if (subset_idx == L_ARRAY_SIZE(station->scan_freqs_order)) l_warn("All supported bands were disabled by user! IWD will not" " function as expected"); - } static void station_wiphy_watch(struct wiphy *wiphy, @@ -5273,11 +5315,8 @@ static void station_free(struct station *station) l_queue_destroy(station->anqp_pending, remove_anqp); - scan_freq_set_free(station->scan_freqs_order[0]); - scan_freq_set_free(station->scan_freqs_order[1]); - - if (station->scan_freqs_order[2]) - scan_freq_set_free(station->scan_freqs_order[2]); + for (uint8_t i = 0; i < L_ARRAY_SIZE(station->scan_freqs_order); i++) + scan_freq_set_free(station->scan_freqs_order[i]); wiphy_state_watch_remove(station->wiphy, station->wiphy_watch); -- 2.39.5