wlan-ap-Telecominfraproject/feeds/ipq95xx/hostapd.old/patches/q02-053-hostapd-awgn-fix-new-channel-selection-logic.patch
John Crispin b9b03a6e38 ipq95xx: add Qualcomm wifi-7 support
Signed-off-by: John Crispin <john@phrozen.org>
2023-04-10 14:25:48 +02:00

422 lines
13 KiB
Diff

From 0fffe1c539734f9dc26a80fb80eddb7ea1f1af46 Mon Sep 17 00:00:00 2001
From: Hari Chandrakanthan <quic_haric@quicinc.com>
Date: Mon, 31 Oct 2022 19:51:32 +0530
Subject: [PATCH] hostapd : awgn - fix new channel selection logic
From the list of channels allowed by Australia,
only one 320 MHz operable channel is available.
So if awgn is detected when operating in 320 MHz, the ap
needs to reduce its bandwidth to next max available bw 160 MHz.
But with current design if no other 320 Mhz channel is available,
hostapd bails out with a log saying that no channel with 320 MHz is available.
we don't check for next max available bandwidth in our current design and
this leads to ap to remain in the same channel even if awgn is detected.
So the channel selection logic is changed in a way that the next max
available bandwidth is searched if no new channel is available for current
bandwidth. The search continues till 20 MHz bandwidth.
This generic logic will help to fix similar issues in future.
Also optimized the channel selection logic by looping over the list of all
the channels only once.
Signed-off-by: Hari Chandrakanthan <quic_haric@quicinc.com>
---
src/ap/interference.c | 249 ++++++++++++++++++++++++++++++++------------------
1 file changed, 160 insertions(+), 89 deletions(-)
diff --git a/src/ap/interference.c b/src/ap/interference.c
index 4148489..1d66d6f 100644
--- a/src/ap/interference.c
+++ b/src/ap/interference.c
@@ -46,6 +46,23 @@
#include "eloop.h"
#include "hw_features.h"
+static bool is_chan_disabled(struct hostapd_hw_modes *mode, int chan_num)
+{
+ int chan_disabled = 1;
+ int i;
+ struct hostapd_channel_data *temp_chan;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ temp_chan = &mode->channels[i];
+ if (temp_chan->chan == chan_num &&
+ (!(temp_chan->flag & HOSTAPD_CHAN_DISABLED))) {
+ chan_disabled = 0;
+ break;
+ }
+ }
+ return chan_disabled;
+}
+
/*
* intf_awgn_chan_range_available - check whether the channel can operate
* in the given bandwidth in 6Ghz
@@ -63,8 +80,11 @@ static int intf_awgn_chan_range_available(struct hostapd_hw_modes *mode,
193, 209};
int allowed_160_6g[] = {1, 33, 65, 97, 129, 161, 193};
int allowed_320_6g[] = {1, 65, 129, 33, 97, 161};
+ int allowed_arr_size = 0;
+ int *allowed_arr = NULL;
int chan_idx_match = 0;
- int i;
+ int chan_disabled = 0;
+ int i, j;
first_chan = &mode->channels[first_chan_idx];
@@ -77,33 +97,40 @@ static int intf_awgn_chan_range_available(struct hostapd_hw_modes *mode,
if (num_chans == 1)
return 1;
- if (num_chans == 2) { /* 40Mhz channel */
- for (i = 0; i < ARRAY_SIZE(allowed_40_6g); i++) {
- if (first_chan->chan == allowed_40_6g[i]) {
- chan_idx_match = 1;
- break;
- }
- }
- } else if (num_chans == 4) { /* 80Mhz channel */
- for (i = 0; i < ARRAY_SIZE(allowed_80_6g); i++) {
- if (first_chan->chan == allowed_80_6g[i]) {
- chan_idx_match = 1;
- break;
- }
- }
- } else if (num_chans == 8) { /* 160Mhz channel */
- for (i = 0; i < ARRAY_SIZE(allowed_160_6g); i++) {
- if (first_chan->chan == allowed_160_6g[i]) {
- chan_idx_match = 1;
- break;
+ switch (num_chans) {
+ case 2:
+ allowed_arr_size = ARRAY_SIZE(allowed_40_6g);
+ allowed_arr = allowed_40_6g;
+ break;
+ case 4:
+ allowed_arr_size = ARRAY_SIZE(allowed_80_6g);
+ allowed_arr = allowed_80_6g;
+ break;
+ case 8:
+ allowed_arr_size = ARRAY_SIZE(allowed_160_6g);
+ allowed_arr = allowed_160_6g;
+ break;
+ case 16:
+ allowed_arr_size = ARRAY_SIZE(allowed_320_6g);
+ allowed_arr = allowed_320_6g;
+ break;
+ default:
+ allowed_arr_size = 0;
+ break;
+ }
+
+ /* check whether all the 20 MHz channels in the given operating range is enabled */
+ for (i = 0; i < allowed_arr_size; i++) {
+ if (first_chan->chan == allowed_arr[i]) {
+ for (j = 1; j <= num_chans - 1; j++) {
+ if (is_chan_disabled(mode, first_chan->chan + j * 4)) {
+ chan_disabled = 1;
+ break;
+ }
}
- }
- } else if (num_chans == 16) { /* 320Mhz channel */
- for (i = 0; i < ARRAY_SIZE(allowed_320_6g); i++) {
- if (first_chan->chan == allowed_320_6g[i]) {
+ if (!chan_disabled)
chan_idx_match = 1;
- break;
- }
+ break;
}
}
@@ -159,21 +186,28 @@ int get_centre_freq_6g(int chan_idx, int chan_width, int *centre_freq)
/*
- *intf_awgn_find_channel - find the channel that can operate with bandwidth chan_width.
- If idx doesn't match with index of any of the existing channel, then the api
- returns the total number of available chandefs that supports the provided bandwidth
- * @idx - index of the channel
- * @chan_width - bandwidth of the channel
+ * intf_awgn_find_channel_list - find the list of channels that can operate with
+ channel width chan_width and not present within the range of current operating range.
+ returns the total number of available chandefs that supports the provided bandwidth
+ * @chan_width - channel width to be checked
+ * @curr_centre_freq - current centre frequency
+ * @curr_chan_width - current channel width
+ * @chandef_list - pointer array to hold the list of valid available chandef
*/
-static int intf_awgn_find_channel(struct hostapd_iface *iface,
- struct hostapd_channel_data **ret_chan,
- int idx, int chan_width, int cs1)
+static int intf_awgn_find_channel_list(struct hostapd_iface *iface,
+ int chan_width, int curr_centre_freq,
+ int curr_chan_width,
+ struct hostapd_channel_data ***chandef_list)
{
struct hostapd_hw_modes *mode = iface->current_mode;
struct hostapd_channel_data *chan;
int i, channel_idx = 0, n_chans;
- int temp_centre_freq;
+ int new_centre_freq;
int ret;
+ int curr_start_freq;
+ int curr_end_freq;
+ int new_start_freq;
+ int new_end_freq;
switch (chan_width) {
case CHAN_WIDTH_20_NOHT:
@@ -198,6 +232,9 @@ static int intf_awgn_find_channel(struct hostapd_iface *iface,
break;
}
+ curr_start_freq = curr_centre_freq - channel_width_to_int(curr_chan_width) / 2;
+ curr_end_freq = curr_centre_freq + channel_width_to_int(curr_chan_width) / 2;
+
for (i = 0; i < mode->num_channels; i++) {
chan = &mode->channels[i];
@@ -216,44 +253,49 @@ static int intf_awgn_find_channel(struct hostapd_iface *iface,
continue;
}
- if (chan_width == CHAN_WIDTH_320) {
- ret = get_centre_freq_6g(chan->chan, CHAN_WIDTH_320,
- &temp_centre_freq);
- if (ret) {
- wpa_printf(MSG_ERROR,
- "AWGN : couldn't find centre freq for chan : %d"
- " chan_width : %d", chan->chan, CHAN_WIDTH_320);
- return 0;
- }
-
- if (abs(temp_centre_freq - cs1) < 320) {
- wpa_printf(MSG_DEBUG,
- "AWGN: channel %d is a overlapping channel so skipping",
- chan->freq);
- continue;
- }
+ ret = get_centre_freq_6g(chan->chan, chan_width,
+ &new_centre_freq);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "AWGN : couldn't find centre freq for chan : %d"
+ " chan_width : %d", chan->chan, chan_width);
+ return 0;
}
- if (ret_chan && idx == channel_idx) {
- wpa_printf(MSG_DEBUG, "AWGN: Selected channel %d (%d)",
- chan->freq, chan->chan);
- *ret_chan = chan;
- return idx;
+ new_start_freq = new_centre_freq - channel_width_to_int(chan_width) / 2;
+ new_end_freq = new_centre_freq + channel_width_to_int(chan_width) / 2;
+
+ /* check whether new operating range is outside the current
+ * operating range since current operating range contains awgn
+ * interference
+ */
+ if (((new_start_freq > curr_start_freq) && (new_start_freq < curr_end_freq)) ||
+ ((new_end_freq > curr_start_freq) && (new_end_freq < curr_end_freq)) ||
+ ((new_centre_freq > curr_start_freq && new_centre_freq < curr_end_freq))) {
+ wpa_printf(MSG_DEBUG,
+ "AWGN : freq range overlap new_start_freq : %d"
+ " new_end_freq : %d curr_start_freq : %d"
+ " curr_end_freq : %d centre_freq : %d",
+ new_start_freq, new_end_freq, curr_start_freq,
+ curr_end_freq, new_centre_freq);
+ continue;
}
- wpa_printf(MSG_DEBUG, "AWGN: Adding channel %d (%d)",
+ wpa_printf(MSG_DEBUG, "AWGN: Adding channel %d (%d) to valid chandef list",
chan->freq, chan->chan);
+ (*chandef_list)[channel_idx] = chan;
channel_idx++;
}
+
return channel_idx;
}
enum chan_seg {
- SEG_PRI20 = 0x1,
- SEG_SEC20 = 0x2,
- SEG_SEC40_LOW = 0x4,
- SEG_SEC40_UP = 0x8,
- SEG_SEC40 = 0xC,
+ SEG_PRI20 = 0x1,
+ SEG_SEC20 = 0x2,
+ SEG_SEC40_LOW = 0x4,
+ SEG_SEC40_UP = 0x8,
+ SEG_SEC40 = 0xC,
SEG_SEC80_LOW = 0x10,
SEG_SEC80_LOW_UP = 0x20,
SEG_SEC80_UP_LOW = 0x40,
@@ -270,6 +312,31 @@ enum chan_seg {
SEG_SEC160 = 0xFF00,
};
+int get_next_max_width(int chan_width)
+{
+ int next_max_width;
+
+ switch (chan_width) {
+ case CHAN_WIDTH_320:
+ next_max_width = CHAN_WIDTH_160;
+ break;
+ case CHAN_WIDTH_160:
+ next_max_width = CHAN_WIDTH_80;
+ break;
+ case CHAN_WIDTH_80:
+ next_max_width = CHAN_WIDTH_40;
+ break;
+ case CHAN_WIDTH_40:
+ next_max_width = CHAN_WIDTH_20;
+ break;
+ default:
+ next_max_width = CHAN_WIDTH_20_NOHT;
+ break;
+ }
+
+ return next_max_width;
+}
+
/*
* hostapd_intf_awgn_detected - awgn interference is detected in the operating channel.
* The interference channel information is available as a
@@ -283,14 +350,16 @@ int hostapd_intf_awgn_detected(struct hostapd_iface *iface, int freq, int chan_w
struct csa_settings settings;
struct hostapd_channel_data *chan_data = NULL;
struct hostapd_channel_data *chan_temp = NULL;
+ struct hostapd_channel_data **available_chandef_list = NULL;
int ret;
unsigned int i;
u32 _rand;
u32 chan_idx;
- int num_available_chandefs;
+ int num_available_chandefs = 0;
u8 channel_switch = 0;
int new_chan_width;
int new_centre_freq;
+ int temp_width;
struct hostapd_hw_modes *mode = iface->current_mode;
wpa_printf(MSG_DEBUG,
@@ -302,7 +371,7 @@ int hostapd_intf_awgn_detected(struct hostapd_iface *iface, int freq, int chan_w
if (iface->conf->discard_6g_awgn_event) {
wpa_printf(MSG_DEBUG, "discard_6g_awgn_event set ignoring"
- "AWGN DETECT event from driver");
+ " AWGN DETECT event from driver");
return 0;
}
@@ -310,47 +379,47 @@ int hostapd_intf_awgn_detected(struct hostapd_iface *iface, int freq, int chan_w
if (!chan_bw_interference_bitmap || (chan_bw_interference_bitmap & SEG_PRI20))
channel_switch = 1;
+ available_chandef_list = os_zalloc(sizeof(struct hostapd_channel_data *) *
+ mode->num_channels);
if (channel_switch) {
- /* Find a random channel to be switched */
- num_available_chandefs = intf_awgn_find_channel(iface, NULL, 0,
- chan_width, cf1);
+ /* find a random channel to be switched */
+ temp_width = chan_width;
+
+ while (temp_width > CHAN_WIDTH_20_NOHT) {
+ num_available_chandefs = intf_awgn_find_channel_list(iface,
+ temp_width,
+ cf1, chan_width,
+ &available_chandef_list);
+ if (num_available_chandefs == 0)
+ temp_width = get_next_max_width(temp_width);
+ else
+ break;
+ }
+
if (num_available_chandefs == 0) {
wpa_printf(MSG_ERROR, "AWGN: no available_chandefs");
- return 0;
+ goto exit;
}
if (os_get_random((u8 *)&_rand, sizeof(_rand)) < 0) {
wpa_printf(MSG_ERROR, "AWGN: couldn't get random number");
- return 0;
+ goto exit;
}
chan_idx = _rand % num_available_chandefs;
- intf_awgn_find_channel(iface, &chan_data, chan_idx, chan_width, cf1);
+
+ chan_data = available_chandef_list[chan_idx];
if (!chan_data) {
- wpa_printf(MSG_ERROR, "AWGN: no random channel found, chan idx : %d",
+ wpa_printf(MSG_ERROR, "AWGN: channel info not available for chan_idx : %d",
chan_idx);
- return 0;
+ goto exit;
}
- if(chan_data->freq == freq) {
- /* New random channel is same as operating channel
- * so choose another channel
- */
- chan_data = NULL;
- chan_idx = (chan_idx + 1) % num_available_chandefs;
- intf_awgn_find_channel(iface, &chan_data, chan_idx, chan_width, cf1);
- if (!chan_data) {
- wpa_printf(MSG_ERROR,
- "AWGN: random channel not found, chan idx : %d",
- chan_idx);
- return 0;
- }
- }
+ new_chan_width = temp_width;
wpa_printf(MSG_DEBUG, "AWGN: got random channel %d (%d)",
chan_data->freq, chan_data->chan);
- new_chan_width = chan_width;
} else {
/* interference is not present in the primary 20Mhz, so reduce bandwidth*/
for (i = 0; i < mode->num_channels; i++) {
@@ -360,7 +429,7 @@ int hostapd_intf_awgn_detected(struct hostapd_iface *iface, int freq, int chan_w
}
if (!chan_data) {
wpa_printf(MSG_ERROR, "AWGN : no channel found");
- return 0;
+ goto exit;
}
if ((chan_width > CHAN_WIDTH_160) &&
@@ -386,7 +455,7 @@ int hostapd_intf_awgn_detected(struct hostapd_iface *iface, int freq, int chan_w
wpa_printf(MSG_ERROR,
"AWGN : couldn't find centre freq for chan : %d"
" chan_width : %d", chan_data->chan, new_chan_width);
- return 0;
+ goto exit;
}
} else {
new_centre_freq = chan_data->freq;
@@ -442,5 +511,7 @@ int hostapd_intf_awgn_detected(struct hostapd_iface *iface, int freq, int chan_w
}
}
+exit:
+ os_free(available_chandef_list);
return 0;
}
--
2.7.4