[][MAC80211][WiFi6][hostapd][Backport hostapd ACS patches and some ACS fixes]

[Description]
Fix ACS issues in WiFi6 Codebase.

Backport the upstream hostapd commits about ACS.
 ACS: Allow selecting a better channel when using 40/80/160 MHz
 ACS: introduce acs_adjust_secondary
 ACS: Introduce acs_get_bw_center_chan()
 ACS: Extract bw40/80/160 freqs out of acs_usable_bwXXX_chan()

Fix Issue#1. Add 2.4G HT40- support
    1. Add 2.4G HT40- support
    2. Fix issue: selected best channel is out of channels

Fix Issue#2. Fix chan_switch to usable DFS channel fail due to ACS

[Release-log]
N/A

Change-Id: I56ff786744da83791f0134e0a1185abdf9c49bfb
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/8237859
diff --git a/autobuild_mac80211_release/package/network/services/hostapd/patches/bp-0001-ACS-Extract-bw40-80-160-freqs-out-of-acs_usable_bwXX.patch b/autobuild_mac80211_release/package/network/services/hostapd/patches/bp-0001-ACS-Extract-bw40-80-160-freqs-out-of-acs_usable_bwXX.patch
new file mode 100644
index 0000000..be0bc74
--- /dev/null
+++ b/autobuild_mac80211_release/package/network/services/hostapd/patches/bp-0001-ACS-Extract-bw40-80-160-freqs-out-of-acs_usable_bwXX.patch
@@ -0,0 +1,172 @@
+From 8df5520f49796e4e292de6895aa0bc518978b377 Mon Sep 17 00:00:00 2001
+From: Nicolas Escande <nico.escande@gmail.com>
+Date: Wed, 27 Apr 2022 15:36:59 +0200
+Subject: [PATCH 01/50] ACS: Extract bw40/80/160 freqs out of
+ acs_usable_bwXXX_chan()
+
+This extracts the 3 lists of allowed channels for 40/80/160 MHz
+bandwidth out of their respective functions. It also adds for each
+segment the frequency of the segment's last channel and the index of the
+segment's "center" channel.
+
+This is preparative work to allow selecting a channel which is not the
+first of its segment for 40/80/160 MHz. In addition, this adds the 5 GHz
+160 MHz channel defined for 5735-5895 MHz (channels 149-177).
+
+Signed-off-by: Nicolas Escande <nico.escande@gmail.com>
+---
+ src/ap/acs.c | 107 ++++++++++++++++++++++++++++++---------------------
+ 1 file changed, 63 insertions(+), 44 deletions(-)
+
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index 4a0a4c7..60c90e9 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -241,6 +241,57 @@
+  * [1] http://en.wikipedia.org/wiki/Near_and_far_field
+  */
+ 
++enum bw_type {
++	ACS_BW40,
++	ACS_BW80,
++	ACS_BW160,
++};
++
++struct bw_item {
++	int first;
++	int last;
++	int center_chan;
++};
++
++static const struct bw_item bw_40[] = {
++	{ 5180, 5200, 38 }, { 5220, 5240, 46 }, { 5260, 5280, 54 },
++	{ 5300, 5320, 62 }, { 5500, 5520, 102 }, { 5540, 5560, 110 },
++	{ 5580, 5600, 110 }, { 5620, 5640, 126}, { 5660, 5680, 134 },
++	{ 5700, 5720, 142 }, { 5745, 5765, 151 }, { 5785, 5805, 159 },
++	{ 5825, 5845, 167 }, { 5865, 5885, 175 },
++	{ 5955, 5975, 3 }, { 5995, 6015, 11 }, { 6035, 6055, 19 },
++	{ 6075, 6095, 27 }, { 6115, 6135, 35 }, { 6155, 6175, 43 },
++	{ 6195, 6215, 51 }, { 6235, 6255, 59 }, { 6275, 6295, 67 },
++	{ 6315, 6335, 75 }, { 6355, 6375, 83 }, { 6395, 6415, 91 },
++	{ 6435, 6455, 99 }, { 6475, 6495, 107 }, { 6515, 6535, 115 },
++	{ 6555, 6575, 123 }, { 6595, 6615, 131 }, { 6635, 6655, 139 },
++	{ 6675, 6695, 147 }, { 6715, 6735, 155 }, { 6755, 6775, 163 },
++	{ 6795, 6815, 171 }, { 6835, 6855, 179 }, { 6875, 6895, 187 },
++	{ 6915, 6935, 195 }, { 6955, 6975, 203 }, { 6995, 7015, 211 },
++	{ 7035, 7055, 219 }, { 7075, 7095, 227}, { -1, -1, -1 }
++};
++static const struct bw_item bw_80[] = {
++	{ 5180, 5240, 42 }, { 5260, 5320, 58 }, { 5500, 5560, 106 },
++	{ 5580, 5640, 122 }, { 5660, 5720, 138 }, { 5745, 5805, 155 },
++	{ 5825, 5885, 171},
++	{ 5955, 6015, 7 }, { 6035, 6095, 23 }, { 6115, 6175, 39 },
++	{ 6195, 6255, 55 }, { 6275, 6335, 71 }, { 6355, 6415, 87 },
++	{ 6435, 6495, 103 }, { 6515, 6575, 119 }, { 6595, 6655, 135 },
++	{ 6675, 6735, 151 }, { 6755, 6815, 167 }, { 6835, 6895, 183 },
++	{ 6915, 6975, 199 }, { 6995, 7055, 215 }, { -1, -1, -1 }
++};
++static const struct bw_item bw_160[] = {
++	{ 5180, 5320, 50 }, { 5500, 5640, 114 }, { 5745, 5885, 163 },
++	{ 5955, 6095, 15 }, { 6115, 6255, 47 }, { 6275, 6415, 79 },
++	{ 6435, 6575, 111 }, { 6595, 6735, 143 },
++	{ 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
++};
++static const struct bw_item *bw_desc[] = {
++	[ACS_BW40] = bw_40,
++	[ACS_BW80] = bw_80,
++	[ACS_BW160] = bw_160,
++};
++
+ 
+ static int acs_request_scan(struct hostapd_iface *iface);
+ static int acs_survey_is_sufficient(struct freq_survey *survey);
+@@ -370,50 +421,18 @@ acs_survey_chan_interference_factor(struct hostapd_iface *iface,
+ }
+ 
+ 
+-static int acs_usable_bw40_chan(const struct hostapd_channel_data *chan)
++static bool acs_usable_bw_chan(const struct hostapd_channel_data *chan,
++			       enum bw_type bw)
+ {
+-	const int allowed[] = { 5180, 5220, 5260, 5300, 5500, 5540, 5580, 5620,
+-				5660, 5745, 5785, 4920, 4960, 5955, 5995, 6035,
+-				6075, 6115, 6155, 6195, 6235, 6275, 6315, 6355,
+-				6395, 6435, 6475, 6515, 6555, 6595, 6635, 6675,
+-				6715, 6755, 6795, 6835, 6875, 6915, 6955, 6995,
+-				7035, 7075 };
+-	unsigned int i;
+-
+-	for (i = 0; i < ARRAY_SIZE(allowed); i++)
+-		if (chan->freq == allowed[i])
+-			return 1;
+-
+-	return 0;
+-}
+-
+-
+-static int acs_usable_bw80_chan(const struct hostapd_channel_data *chan)
+-{
+-	const int allowed[] = { 5180, 5260, 5500, 5580, 5660, 5745, 5955, 6035,
+-				6115, 6195, 6275, 6355, 6435, 6515, 6595, 6675,
+-				6755, 6835, 6915, 6995 };
+-	unsigned int i;
+-
+-	for (i = 0; i < ARRAY_SIZE(allowed); i++)
+-		if (chan->freq == allowed[i])
+-			return 1;
+-
+-	return 0;
+-}
+-
+-
+-static int acs_usable_bw160_chan(const struct hostapd_channel_data *chan)
+-{
+-	const int allowed[] = { 5180, 5500, 5955, 6115, 6275, 6435, 6595, 6755,
+-				6915 };
+-	unsigned int i;
++	unsigned int i = 0;
+ 
+-	for (i = 0; i < ARRAY_SIZE(allowed); i++)
+-		if (chan->freq == allowed[i])
+-			return 1;
++	while (bw_desc[bw][i].first != -1) {
++		if (chan->freq == bw_desc[bw][i].first)
++			return true;
++		i++;
++	}
+ 
+-	return 0;
++	return false;
+ }
+ 
+ 
+@@ -705,7 +724,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 		    ((iface->conf->ieee80211n &&
+ 		      iface->conf->secondary_channel) ||
+ 		     is_6ghz_freq(chan->freq)) &&
+-		    !acs_usable_bw40_chan(chan)) {
++		    !acs_usable_bw_chan(chan, ACS_BW40)) {
+ 			wpa_printf(MSG_DEBUG,
+ 				   "ACS: Channel %d: not allowed as primary channel for 40 MHz bandwidth",
+ 				   chan->chan);
+@@ -716,7 +735,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 		    (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
+ 			if (hostapd_get_oper_chwidth(iface->conf) ==
+ 			    CONF_OPER_CHWIDTH_80MHZ &&
+-			    !acs_usable_bw80_chan(chan)) {
++			    !acs_usable_bw_chan(chan, ACS_BW80)) {
+ 				wpa_printf(MSG_DEBUG,
+ 					   "ACS: Channel %d: not allowed as primary channel for 80 MHz bandwidth",
+ 					   chan->chan);
+@@ -725,7 +744,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 
+ 			if (hostapd_get_oper_chwidth(iface->conf) ==
+ 			    CONF_OPER_CHWIDTH_160MHZ &&
+-			    !acs_usable_bw160_chan(chan)) {
++			    !acs_usable_bw_chan(chan, ACS_BW160)) {
+ 				wpa_printf(MSG_DEBUG,
+ 					   "ACS: Channel %d: not allowed as primary channel for 160 MHz bandwidth",
+ 					   chan->chan);
+-- 
+2.18.0
+
diff --git a/autobuild_mac80211_release/package/network/services/hostapd/patches/bp-0002-ACS-Introduce-acs_get_bw_center_chan.patch b/autobuild_mac80211_release/package/network/services/hostapd/patches/bp-0002-ACS-Introduce-acs_get_bw_center_chan.patch
new file mode 100644
index 0000000..15e8c98
--- /dev/null
+++ b/autobuild_mac80211_release/package/network/services/hostapd/patches/bp-0002-ACS-Introduce-acs_get_bw_center_chan.patch
@@ -0,0 +1,87 @@
+From 3e2fa0f4ab02538c66e61058749272565d94d111 Mon Sep 17 00:00:00 2001
+From: Nicolas Escande <nico.escande@gmail.com>
+Date: Wed, 27 Apr 2022 15:37:00 +0200
+Subject: [PATCH 02/50] ACS: Introduce acs_get_bw_center_chan()
+
+When using 40/80/160 MHz bandwidth, instead of computing the index of
+the segment center freq based on the selected channel, lets look it up
+in the bw_desc[] table.
+
+This is preparative work to allow selecting a primary channel which is
+not the first of the segment.
+
+Signed-off-by: Nicolas Escande <nico.escande@gmail.com>
+---
+ src/ap/acs.c | 33 +++++++++++++++++++++++++++------
+ 1 file changed, 27 insertions(+), 6 deletions(-)
+
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index 60c90e9..511200d 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -436,6 +436,21 @@ static bool acs_usable_bw_chan(const struct hostapd_channel_data *chan,
+ }
+ 
+ 
++static int acs_get_bw_center_chan(int freq, enum bw_type bw)
++{
++	unsigned int i = 0;
++
++	while (bw_desc[bw][i].first != -1) {
++		if (freq >= bw_desc[bw][i].first &&
++		    freq <= bw_desc[bw][i].last)
++			return bw_desc[bw][i].center_chan;
++		i++;
++	}
++
++	return 0;
++}
++
++
+ static int acs_survey_is_sufficient(struct freq_survey *survey)
+ {
+ 	if (!(survey->filled & SURVEY_HAS_NF)) {
+@@ -936,19 +951,26 @@ bw_selected:
+ 
+ static void acs_adjust_center_freq(struct hostapd_iface *iface)
+ {
+-	int offset;
++	int center;
+ 
+ 	wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
+ 
+ 	switch (hostapd_get_oper_chwidth(iface->conf)) {
+ 	case CONF_OPER_CHWIDTH_USE_HT:
+-		offset = 2 * iface->conf->secondary_channel;
++		if (iface->conf->secondary_channel &&
++		    iface->freq >= 2400 && iface->freq < 2500)
++			center = iface->conf->channel +
++				2 * iface->conf->secondary_channel;
++		else if (iface->conf->secondary_channel)
++			center = acs_get_bw_center_chan(iface->freq, ACS_BW40);
++		else
++			center = iface->conf->channel;
+ 		break;
+ 	case CONF_OPER_CHWIDTH_80MHZ:
+-		offset = 6;
++		center = acs_get_bw_center_chan(iface->freq, ACS_BW80);
+ 		break;
+ 	case CONF_OPER_CHWIDTH_160MHZ:
+-		offset = 14;
++		center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
+ 		break;
+ 	default:
+ 		/* TODO: How can this be calculated? Adjust
+@@ -958,8 +980,7 @@ static void acs_adjust_center_freq(struct hostapd_iface *iface)
+ 		return;
+ 	}
+ 
+-	hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+-					     iface->conf->channel + offset);
++	hostapd_set_oper_centr_freq_seg0_idx(iface->conf, center);
+ }
+ 
+ 
+-- 
+2.18.0
+
diff --git a/autobuild_mac80211_release/package/network/services/hostapd/patches/bp-0003-ACS-introduce-acs_adjust_secondary.patch b/autobuild_mac80211_release/package/network/services/hostapd/patches/bp-0003-ACS-introduce-acs_adjust_secondary.patch
new file mode 100644
index 0000000..b4ac1f0
--- /dev/null
+++ b/autobuild_mac80211_release/package/network/services/hostapd/patches/bp-0003-ACS-introduce-acs_adjust_secondary.patch
@@ -0,0 +1,92 @@
+From 03dd213b918218fec270e781d284ec394fb63ef1 Mon Sep 17 00:00:00 2001
+From: Nicolas Escande <nico.escande@gmail.com>
+Date: Wed, 27 Apr 2022 15:37:01 +0200
+Subject: [PATCH 03/50] ACS: introduce acs_adjust_secondary
+
+When using 40/80/160 MHz bandwidth on the 5 GHz or 6 GHz band, enforce
+the secondary channel to be the other channel of the corresponding 40
+MHz segment.
+
+Even if this is useless for now, this is preparatory work to allow ACS
+to select a primary channel which is not the first of its segment.
+
+Signed-off-by: Nicolas Escande <nico.escande@gmail.com>
+---
+ src/ap/acs.c | 46 +++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 45 insertions(+), 1 deletion(-)
+
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index 511200d..78d1feb 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -634,6 +634,26 @@ acs_find_chan_mode(struct hostapd_hw_modes *mode, int freq)
+ }
+ 
+ 
++static enum hostapd_hw_mode
++acs_find_mode(struct hostapd_iface *iface, int freq)
++{
++	int i;
++	struct hostapd_hw_modes *mode;
++	struct hostapd_channel_data *chan;
++
++	for (i = 0; i < iface->num_hw_features; i++) {
++		mode = &iface->hw_features[i];
++		if (!hostapd_hw_skip_mode(iface, mode)) {
++			chan = acs_find_chan_mode(mode, freq);
++			if (chan)
++				return mode->mode;
++		}
++	}
++
++	return HOSTAPD_MODE_IEEE80211ANY;
++}
++
++
+ static struct hostapd_channel_data *
+ acs_find_chan(struct hostapd_iface *iface, int freq)
+ {
+@@ -949,6 +969,28 @@ bw_selected:
+ }
+ 
+ 
++static void acs_adjust_secondary(struct hostapd_iface *iface)
++{
++	unsigned int i;
++
++	/* When working with bandwidth over 20 MHz on the 5 GHz or 6 GHz band,
++	 * ACS can return a secondary channel which is not the first channel of
++	 * the segment and we need to adjust. */
++	if (!iface->conf->secondary_channel ||
++	    acs_find_mode(iface, iface->freq) != HOSTAPD_MODE_IEEE80211A)
++		return;
++
++	wpa_printf(MSG_DEBUG, "ACS: Adjusting HT/VHT/HE secondary frequency");
++
++	for (i = 0; bw_desc[ACS_BW40][i].first != -1; i++) {
++		if (iface->freq == bw_desc[ACS_BW40][i].first)
++			iface->conf->secondary_channel = 1;
++		else if (iface->freq == bw_desc[ACS_BW40][i].last)
++			iface->conf->secondary_channel = -1;
++	}
++}
++
++
+ static void acs_adjust_center_freq(struct hostapd_iface *iface)
+ {
+ 	int center;
+@@ -1036,8 +1078,10 @@ static void acs_study(struct hostapd_iface *iface)
+ 	iface->conf->channel = ideal_chan->chan;
+ 	iface->freq = ideal_chan->freq;
+ 
+-	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax)
++	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
++		acs_adjust_secondary(iface);
+ 		acs_adjust_center_freq(iface);
++	}
+ 
+ 	err = 0;
+ fail:
+-- 
+2.18.0
+
diff --git a/autobuild_mac80211_release/package/network/services/hostapd/patches/bp-0004-ACS-Allow-selecting-a-better-channel-when-using-40-8.patch b/autobuild_mac80211_release/package/network/services/hostapd/patches/bp-0004-ACS-Allow-selecting-a-better-channel-when-using-40-8.patch
new file mode 100644
index 0000000..8da5856
--- /dev/null
+++ b/autobuild_mac80211_release/package/network/services/hostapd/patches/bp-0004-ACS-Allow-selecting-a-better-channel-when-using-40-8.patch
@@ -0,0 +1,78 @@
+From a615aa302064919791f3912a5cf1a908390414df Mon Sep 17 00:00:00 2001
+From: Nicolas Escande <nico.escande@gmail.com>
+Date: Wed, 27 Apr 2022 15:37:02 +0200
+Subject: [PATCH 04/50] ACS: Allow selecting a better channel when using
+ 40/80/160 MHz
+
+When considering a channel for a bandwidth of 40/80/160 MHZ on the 5 GHz
+or 6 GHz band, allow selecting one of the other channels in the segment
+instead of the first one. This is done only if the other channel's
+interference_factor is lower than the first one's.
+
+Signed-off-by: Nicolas Escande <nico.escande@gmail.com>
+---
+ src/ap/acs.c | 25 +++++++++++++++++++++++--
+ 1 file changed, 23 insertions(+), 2 deletions(-)
+
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index 78d1feb..130e135 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -712,7 +712,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 			 struct hostapd_channel_data **ideal_chan,
+ 			 long double *ideal_factor)
+ {
+-	struct hostapd_channel_data *chan, *adj_chan = NULL;
++	struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
+ 	long double factor;
+ 	int i, j;
+ 	unsigned int k;
+@@ -720,8 +720,9 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 	for (i = 0; i < mode->num_channels; i++) {
+ 		double total_weight;
+ 		struct acs_bias *bias, tmp_bias;
++		bool update_best = true;
+ 
+-		chan = &mode->channels[i];
++		best = chan = &mode->channels[i];
+ 
+ 		/* Since in the current ACS implementation the first channel is
+ 		 * always a primary channel, skip channels not available as
+@@ -807,7 +808,15 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 			if (acs_usable_chan(adj_chan)) {
+ 				factor += adj_chan->interference_factor;
+ 				total_weight += 1;
++			} else {
++				update_best = false;
+ 			}
++
++			/* find the best channel in this segment */
++			if (update_best &&
++			    adj_chan->interference_factor <
++			    best->interference_factor)
++				best = adj_chan;
+ 		}
+ 
+ 		if (j != n_chans) {
+@@ -816,6 +825,18 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 			continue;
+ 		}
+ 
++		/* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
++		 * crowded primary channel if one was found in the segment */
++		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
++		    chan != best) {
++			wpa_printf(MSG_DEBUG,
++				   "ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
++				   best->chan, chan->chan,
++				   chan->interference_factor,
++				   best->interference_factor);
++			chan = best;
++		}
++
+ 		/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
+ 		 * channel interference factor. */
+ 		if (is_24ghz_mode(mode->mode)) {
+-- 
+2.18.0
+
diff --git a/autobuild_mac80211_release/package/network/services/hostapd/patches/mtk-0046-hostapd-mtk-ACS-Fix-2.4GHz-HT40-case-and-channel-swi.patch b/autobuild_mac80211_release/package/network/services/hostapd/patches/mtk-0046-hostapd-mtk-ACS-Fix-2.4GHz-HT40-case-and-channel-swi.patch
new file mode 100644
index 0000000..31e92e6
--- /dev/null
+++ b/autobuild_mac80211_release/package/network/services/hostapd/patches/mtk-0046-hostapd-mtk-ACS-Fix-2.4GHz-HT40-case-and-channel-swi.patch
@@ -0,0 +1,333 @@
+From 97b4dbb136ff86dd6ca7fac5fb4ce9356337a8bd Mon Sep 17 00:00:00 2001
+From: Evelyn Tsai <evelyn.tsai@mediatek.com>
+Date: Sat, 11 Nov 2023 11:42:59 +0800
+Subject: [PATCH] hostapd: mtk: ACS: Fix 2.4GHz HT40 case and channel switch
+ fail
+
+Issue#1. Add 2.4G HT40- support
+1. Add 2.4G HT40- support
+2. Fix issue: selected best channel is out of channels
+
+Issue#2. Fix chan_switch to usable DFS channel fail due to ACS
+
+Step and issue:
+1. Enable ACS in hostapd config;
+2. Bootup and then use hostapd_cli cmd switch channel to a DFS channel;
+3. Will do ACS again, and no work on channel specified in step 2.
+
+Root cause:
+When need do DFS-CAC, hostapd will do intf disable, then set the new
+channel into running config settings, and finally enable intf;
+In the test case, new DFS channel is set to runnint config settings, but
+another param "acs" is still 1 (enable), caused the ACS running when
+intf enabled.
+
+CR-Id: WCNCR00240772
+---
+ src/ap/acs.c     | 170 +++++++++++++++++++++++++----------------------
+ src/ap/hostapd.c |   3 +
+ 2 files changed, 93 insertions(+), 80 deletions(-)
+
+diff --git a/src/ap/acs.c b/src/ap/acs.c
+index 130e135..73d2195 100644
+--- a/src/ap/acs.c
++++ b/src/ap/acs.c
+@@ -577,12 +577,6 @@ static void acs_survey_mode_interference_factor(
+ 		    iface->conf->acs_exclude_dfs)
+ 			continue;
+ 
+-		if (!is_in_chanlist(iface, chan))
+-			continue;
+-
+-		if (!is_in_freqlist(iface, chan))
+-			continue;
+-
+ 		if (chan->max_tx_power < iface->conf->min_tx_power)
+ 			continue;
+ 
+@@ -712,17 +706,29 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 			 struct hostapd_channel_data **ideal_chan,
+ 			 long double *ideal_factor)
+ {
+-	struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
++	struct hostapd_channel_data *chan, *adj_chan = NULL, *tmp_chan = NULL, *best;
+ 	long double factor;
+ 	int i, j;
+ 	unsigned int k;
++	int ht40_plus = 1, sec_ch_factor = 1;
++
++	if (is_24ghz_mode(mode->mode)) {
++		ht40_plus = (iface->conf->secondary_channel == -1) ? 0 : 1;
++		sec_ch_factor = (iface->conf->secondary_channel == -1) ? -1 : 1;
++	}
++
++	wpa_printf(MSG_INFO, "%s:%d, bw(%u), n_chans(%d), num_channels(%d), sec_ch(%d)",
++		__func__, __LINE__, bw, n_chans, mode->num_channels, iface->conf->secondary_channel);
+ 
+ 	for (i = 0; i < mode->num_channels; i++) {
+ 		double total_weight;
+ 		struct acs_bias *bias, tmp_bias;
+-		bool update_best = true;
++		bool update_best = true, has_candidate = true;
+ 
+ 		best = chan = &mode->channels[i];
++		wpa_printf(MSG_INFO,
++			   "ACS: Channel[%d] %d: interference_factor %Lg",
++			   i, chan->chan, chan->interference_factor);
+ 
+ 		/* Since in the current ACS implementation the first channel is
+ 		 * always a primary channel, skip channels not available as
+@@ -735,11 +741,12 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 		    iface->conf->acs_exclude_dfs)
+ 			continue;
+ 
+-		if (!is_in_chanlist(iface, chan))
+-			continue;
+-
+-		if (!is_in_freqlist(iface, chan))
+-			continue;
++		if (!is_in_chanlist(iface, chan) || !is_in_freqlist(iface, chan)) {
++			if (is_24ghz_mode(mode->mode))
++				continue;
++			else
++				has_candidate = false;
++		}
+ 
+ 		if (chan->max_tx_power < iface->conf->min_tx_power)
+ 			continue;
+@@ -747,7 +754,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 		if (chan->flag & HOSTAPD_CHAN_INDOOR_ONLY && iface->conf->country[2] == 0x4f)
+ 			continue;
+ 
+-		if (!chan_bw_allowed(chan, bw, 1, 1)) {
++		if (!chan_bw_allowed(chan, bw, ht40_plus, 1)) {
+ 			wpa_printf(MSG_DEBUG,
+ 				   "ACS: Channel %d: BW %u is not supported",
+ 				   chan->chan, bw);
+@@ -789,60 +796,76 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 		}
+ 
+ 		factor = 0;
+-		if (acs_usable_chan(chan))
+-			factor = chan->interference_factor;
+-		total_weight = 1;
++		total_weight = 0;
+ 
+-		for (j = 1; j < n_chans; j++) {
+-			adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
+-			if (!adj_chan)
+-				break;
+-
+-			if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
+-				wpa_printf(MSG_DEBUG,
+-					   "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
+-					   chan->chan, adj_chan->chan, bw);
+-				break;
+-			}
+-
+-			if (acs_usable_chan(adj_chan)) {
+-				factor += adj_chan->interference_factor;
++		if (!is_24ghz_mode(mode->mode)) {
++			/* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
++			 * crowded primary channel if one was found in the segment */
++			if (acs_usable_chan(chan)) {
++				factor += chan->interference_factor;
+ 				total_weight += 1;
+-			} else {
+-				update_best = false;
+ 			}
+ 
+-			/* find the best channel in this segment */
+-			if (update_best &&
+-			    adj_chan->interference_factor <
+-			    best->interference_factor)
+-				best = adj_chan;
+-		}
++			for (j = 1; j < n_chans; j++) {
++				adj_chan = acs_find_chan(iface, chan->freq + j * 20);
++				if (!adj_chan)
++					break;
+ 
+-		if (j != n_chans) {
+-			wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
+-				   chan->chan);
+-			continue;
+-		}
++				if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
++					wpa_printf(MSG_DEBUG,
++						   "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
++						   chan->chan, adj_chan->chan, bw);
++					break;
++				}
+ 
+-		/* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
+-		 * crowded primary channel if one was found in the segment */
+-		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+-		    chan != best) {
+-			wpa_printf(MSG_DEBUG,
+-				   "ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
+-				   best->chan, chan->chan,
+-				   chan->interference_factor,
+-				   best->interference_factor);
+-			chan = best;
+-		}
++				update_best = true;
++				if (acs_usable_chan(adj_chan)) {
++					factor += adj_chan->interference_factor;
++					total_weight += 1;
++
++					if (!is_in_chanlist(iface, adj_chan) ||
++						!is_in_freqlist(iface, adj_chan))
++						update_best = false;
++				} else {
++					update_best = false;
++				}
++
++				/* find the best channel in this segment */
++				if (update_best && (!has_candidate ||
++					adj_chan->interference_factor < best->interference_factor)) {
++					best = adj_chan;
++					has_candidate = true;
++				}
++			}
+ 
+-		/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
+-		 * channel interference factor. */
+-		if (is_24ghz_mode(mode->mode)) {
++			if (j != n_chans || !has_candidate) {
++				wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
++					   chan->chan);
++				continue;
++			}
++
++			if (chan != best) {
++				wpa_printf(MSG_INFO,
++					   "ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
++					   best->chan, chan->chan,
++					   chan->interference_factor,
++					   best->interference_factor);
++				chan = best;
++			}
++		} else {
+ 			for (j = 0; j < n_chans; j++) {
++				/* Will set primary_channel / secondary_channel(40M case) weight to 1 */
++				tmp_chan = acs_find_chan(iface, chan->freq +
++							 (j * 20) * sec_ch_factor);
++				if (tmp_chan && acs_usable_chan(tmp_chan)) {
++					factor += tmp_chan->interference_factor;
++					total_weight += 1;
++				}
++
++				/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent channel
++				interference factor, separately for primary/secondary channel. */
+ 				adj_chan = acs_find_chan(iface, chan->freq +
+-							 (j * 20) - 5);
++							 ((j * 20) - 5) * sec_ch_factor);
+ 				if (adj_chan && acs_usable_chan(adj_chan)) {
+ 					factor += ACS_ADJ_WEIGHT *
+ 						adj_chan->interference_factor;
+@@ -850,7 +873,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 				}
+ 
+ 				adj_chan = acs_find_chan(iface, chan->freq +
+-							 (j * 20) - 10);
++							 ((j * 20) - 10) * sec_ch_factor);
+ 				if (adj_chan && acs_usable_chan(adj_chan)) {
+ 					factor += ACS_NEXT_ADJ_WEIGHT *
+ 						adj_chan->interference_factor;
+@@ -858,7 +881,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 				}
+ 
+ 				adj_chan = acs_find_chan(iface, chan->freq +
+-							 (j * 20) + 5);
++							 ((j * 20) + 5) * sec_ch_factor);
+ 				if (adj_chan && acs_usable_chan(adj_chan)) {
+ 					factor += ACS_ADJ_WEIGHT *
+ 						adj_chan->interference_factor;
+@@ -866,7 +889,7 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 				}
+ 
+ 				adj_chan = acs_find_chan(iface, chan->freq +
+-							 (j * 20) + 10);
++							 ((j * 20) + 10) * sec_ch_factor);
+ 				if (adj_chan && acs_usable_chan(adj_chan)) {
+ 					factor += ACS_NEXT_ADJ_WEIGHT *
+ 						adj_chan->interference_factor;
+@@ -875,7 +898,8 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 			}
+ 		}
+ 
+-		factor /= total_weight;
++		if (total_weight)
++			factor /= total_weight;
+ 
+ 		bias = NULL;
+ 		if (iface->conf->acs_chan_bias) {
+@@ -894,11 +918,11 @@ acs_find_ideal_chan_mode(struct hostapd_iface *iface,
+ 
+ 		if (bias) {
+ 			factor *= bias->bias;
+-			wpa_printf(MSG_DEBUG,
++			wpa_printf(MSG_INFO,
+ 				   "ACS:  * channel %d: total interference = %Lg (%f bias)",
+ 				   chan->chan, factor, bias->bias);
+ 		} else {
+-			wpa_printf(MSG_DEBUG,
++			wpa_printf(MSG_INFO,
+ 				   "ACS:  * channel %d: total interference = %Lg",
+ 				   chan->chan, factor);
+ 		}
+@@ -939,14 +963,6 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
+ 		goto bw_selected;
+ 	}
+ 
+-	/* TODO: HT40- support */
+-
+-	if (iface->conf->ieee80211n &&
+-	    iface->conf->secondary_channel == -1) {
+-		wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
+-		return NULL;
+-	}
+-
+ 	if (iface->conf->ieee80211n &&
+ 	    iface->conf->secondary_channel)
+ 		n_chans = 2;
+@@ -981,7 +997,7 @@ bw_selected:
+ 	}
+ 
+ 	if (ideal_chan) {
+-		wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
++		wpa_printf(MSG_INFO, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
+ 			   ideal_chan->chan, ideal_chan->freq, ideal_factor);
+ 		return ideal_chan;
+ 	}
+@@ -1169,12 +1185,6 @@ static int * acs_request_scan_add_freqs(struct hostapd_iface *iface,
+ 		     iface->conf->acs_exclude_dfs))
+ 			continue;
+ 
+-		if (!is_in_chanlist(iface, chan))
+-			continue;
+-
+-		if (!is_in_freqlist(iface, chan))
+-			continue;
+-
+ 		if (chan->max_tx_power < iface->conf->min_tx_power)
+ 			continue;
+ 
+diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
+index f2eb638..250c168 100644
+--- a/src/ap/hostapd.c
++++ b/src/ap/hostapd.c
+@@ -3895,6 +3895,9 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ 
+ 	iface->freq = freq_params->freq;
+ 	iface->conf->channel = freq_params->channel;
++	if (iface->conf->channel != 0) /* If channel not zero, will disable acs. */
++		iface->conf->acs = 0;
++
+ 	iface->conf->secondary_channel = freq_params->sec_channel_offset;
+ 	hostapd_set_oper_centr_freq_seg0_idx(iface->conf, seg0_idx);
+ 	hostapd_set_oper_centr_freq_seg1_idx(iface->conf, seg1_idx);
+-- 
+2.18.0
+