[][MAC80211][hostapd][Add hostapd patches for WiFi Certification MBO test plan]
[Description]
Add hostapd patches for WiFi Certification MBO test plan
[Release-log]
N/A
Change-Id: I486709bd1ac8e5b0cdafcada325634ce1d49eb07
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/5541016
diff --git a/openwrt_patches-21.02/414-master-hostapd-patches-for-WiFi-Certification-MBO-test.patch b/openwrt_patches-21.02/414-master-hostapd-patches-for-WiFi-Certification-MBO-test.patch
new file mode 100644
index 0000000..13c14c6
--- /dev/null
+++ b/openwrt_patches-21.02/414-master-hostapd-patches-for-WiFi-Certification-MBO-test.patch
@@ -0,0 +1,1638 @@
+From 38040b75986d6efe3d6c846cd3850c21bff94f62 Mon Sep 17 00:00:00 2001
+From: "howard.hsu" <howard-yh.hsu@mediatek.com>
+Date: Thu, 20 Jan 2022 16:39:04 +0800
+Subject: [PATCH] Add hostapd patches for WiFi Certification MBO test plan
+
+---
+ ...hbor_count-and-hostapd_neighbor_inse.patch | 72 ++
+ ...g-neighbor-report-elements-in-ANQP-r.patch | 95 ++
+ ...ster-sync-include-uapi-linux-nl80211.patch | 57 ++
+ ...g-neignbor-report-elements-in-BTM-re.patch | 68 ++
+ .../patches/904-master-zero-wait_dfs.patch | 851 ++++++++++++++++++
+ ...ing-BSS-Termination-TSF-by-using-hos.patch | 66 ++
+ ...erface-if-BSS-Termination-TSF-is-set.patch | 47 +
+ ...assoc_frame_timer-to-send-disassocia.patch | 63 ++
+ ...g-neighbor-report-elements-in-BTM-re.patch | 31 +
+ ...hostapd_neighbor_set_own_report_pref.patch | 88 ++
+ ...d_neighbor_set_pref_by_non_pref_chan.patch | 101 +++
+ 11 files changed, 1539 insertions(+)
+ create mode 100644 package/network/services/hostapd/patches/902-master-Add-hostapd_neighbor_count-and-hostapd_neighbor_inse.patch
+ create mode 100644 package/network/services/hostapd/patches/903-master-Support-including-neighbor-report-elements-in-ANQP-r.patch
+ create mode 100644 package/network/services/hostapd/patches/903-master-sync-include-uapi-linux-nl80211.patch
+ create mode 100644 package/network/services/hostapd/patches/904-master-Support-including-neignbor-report-elements-in-BTM-re.patch
+ create mode 100644 package/network/services/hostapd/patches/904-master-zero-wait_dfs.patch
+ create mode 100644 package/network/services/hostapd/patches/905-master-Support-configuring-BSS-Termination-TSF-by-using-hos.patch
+ create mode 100644 package/network/services/hostapd/patches/906-master-Disable-interface-if-BSS-Termination-TSF-is-set.patch
+ create mode 100644 package/network/services/hostapd/patches/907-master-Add-set_send_disassoc_frame_timer-to-send-disassocia.patch
+ create mode 100644 package/network/services/hostapd/patches/908-master-Support-including-neighbor-report-elements-in-BTM-re.patch
+ create mode 100644 package/network/services/hostapd/patches/909-master-Add-hostapd_neighbor_set_own_report_pref.patch
+ create mode 100644 package/network/services/hostapd/patches/910-master-Add-hostapd_neighbor_set_pref_by_non_pref_chan.patch
+
+diff --git a/package/network/services/hostapd/patches/902-master-Add-hostapd_neighbor_count-and-hostapd_neighbor_inse.patch b/package/network/services/hostapd/patches/902-master-Add-hostapd_neighbor_count-and-hostapd_neighbor_inse.patch
+new file mode 100644
+index 0000000..3a7e018
+--- /dev/null
++++ b/package/network/services/hostapd/patches/902-master-Add-hostapd_neighbor_count-and-hostapd_neighbor_inse.patch
+@@ -0,0 +1,72 @@
++From e53d12c69846446bd38a8d05f5a99d79e7907733 Mon Sep 17 00:00:00 2001
++From: "howard.hsu" <howard-yh.hsu@mediatek.com>
++Date: Wed, 19 Jan 2022 19:18:07 +0800
++Subject: [PATCH 1/9] Add hostapd_neighbor_count() and
++ hostapd_neighbor_insert_buffer ()
++
++The first function can count the number of neighbor report in neighbore report
++database. The second can iterate neighbor report database to build up neighbor
++report data.
++---
++ src/ap/neighbor_db.c | 32 ++++++++++++++++++++++++++++++++
++ src/ap/neighbor_db.h | 3 +++
++ 2 files changed, 35 insertions(+)
++
++diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
++index 2bbe318..a47afd4 100644
++--- a/src/ap/neighbor_db.c
+++++ b/src/ap/neighbor_db.c
++@@ -88,6 +88,38 @@ int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen)
++ }
++
++
+++int hostapd_neighbor_count(struct hostapd_data *hapd)
+++{
+++ struct hostapd_neighbor_entry *nr;
+++ int count = 0;
+++
+++ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+++ list) {
+++ count++;
+++ }
+++ return count;
+++}
+++
+++
+++int hostapd_neighbor_insert_buffer(struct hostapd_data *hapd, char *buf,
+++ size_t buflen)
+++{
+++ struct hostapd_neighbor_entry *nr;
+++ char *pos = buf;
+++
+++ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+++ list) {
+++ /* For neighbor report IE, we only need bssid and nr*/
+++ *pos++ = WLAN_EID_NEIGHBOR_REPORT;
+++ *pos++ = wpabuf_len(nr->nr);
+++ os_memcpy(pos, wpabuf_head(nr->nr), wpabuf_len(nr->nr));
+++ pos += wpabuf_len(nr->nr);
+++ }
+++
+++ return pos - buf;
+++}
+++
+++
++ static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
++ {
++ wpabuf_free(nr->nr);
++diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
++index bed0a2f..e93d1d5 100644
++--- a/src/ap/neighbor_db.h
+++++ b/src/ap/neighbor_db.h
++@@ -23,4 +23,7 @@ int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
++ const struct wpa_ssid_value *ssid);
++ void hostapd_free_neighbor_db(struct hostapd_data *hapd);
++
+++int hostapd_neighbor_count(struct hostapd_data *hapd);
+++int hostapd_neighbor_insert_buffer(struct hostapd_data *hapd, char *buf,
+++ size_t buflen);
++ #endif /* NEIGHBOR_DB_H */
++--
++2.18.0
++
+diff --git a/package/network/services/hostapd/patches/903-master-Support-including-neighbor-report-elements-in-ANQP-r.patch b/package/network/services/hostapd/patches/903-master-Support-including-neighbor-report-elements-in-ANQP-r.patch
+new file mode 100644
+index 0000000..73c0052
+--- /dev/null
++++ b/package/network/services/hostapd/patches/903-master-Support-including-neighbor-report-elements-in-ANQP-r.patch
+@@ -0,0 +1,95 @@
++From 3111458bed644db0795942e825c65a14c7c12b7b Mon Sep 17 00:00:00 2001
++From: "howard.hsu" <howard-yh.hsu@mediatek.com>
++Date: Wed, 19 Jan 2022 19:25:05 +0800
++Subject: [PATCH 2/9] Support including neighbor report elements in ANQP
++ response
++
++---
++ src/ap/gas_serv.c | 29 +++++++++++++++++++++++++++++
++ src/ap/gas_serv.h | 2 ++
++ 2 files changed, 31 insertions(+)
++
++diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
++index 90f1577..5845ff8 100644
++--- a/src/ap/gas_serv.c
+++++ b/src/ap/gas_serv.c
++@@ -19,6 +19,7 @@
++ #include "dpp_hostapd.h"
++ #include "sta_info.h"
++ #include "gas_serv.h"
+++#include "neighbor_db.h"
++
++
++ #ifdef CONFIG_DPP
++@@ -369,6 +370,24 @@ static void anqp_add_network_auth_type(struct hostapd_data *hapd,
++ }
++ }
++
+++static void anqp_add_neighbor_report(struct hostapd_data *hapd,
+++ struct wpabuf *buf)
+++{
+++ struct hostapd_neighbor_entry *nr;
+++ u8 *len_pos = gas_anqp_add_element(buf, ANQP_NEIGHBOR_REPORT);
+++ if (dl_list_empty(&hapd->nr_db)) {
+++ wpabuf_put_le16(buf, 0);
+++ }
+++ else {
+++ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, list ) {
+++ wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
+++ wpabuf_put_u8(buf, wpabuf_len(nr->nr));
+++ wpabuf_put_buf(buf, nr->nr);
+++ }
+++ }
+++ gas_anqp_set_element_len(buf, len_pos);
+++}
+++
++
++ static void anqp_add_roaming_consortium(struct hostapd_data *hapd,
++ struct wpabuf *buf)
++@@ -986,6 +1005,9 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
++ len += 1000;
++ if (request & ANQP_REQ_ICON_REQUEST)
++ len += 65536;
+++ if (request & ANQP_REQ_NEIGHBOR_REPORT) {
+++ len += (40 * hostapd_neighbor_count(hapd));
+++ }
++ #ifdef CONFIG_FILS
++ if (request & ANQP_FILS_REALM_INFO)
++ len += 2 * dl_list_len(&hapd->conf->fils_realms);
++@@ -1028,6 +1050,8 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
++ anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
++ if (request & ANQP_REQ_EMERGENCY_NAI)
++ anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
+++ if (request & ANQP_REQ_NEIGHBOR_REPORT)
+++ anqp_add_neighbor_report(hapd, buf);
++
++ for (i = 0; i < num_extra_req; i++) {
++ #ifdef CONFIG_FILS
++@@ -1172,6 +1196,11 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
++ "Emergency NAI",
++ get_anqp_elem(hapd, info_id) != NULL, qi);
++ break;
+++ case ANQP_NEIGHBOR_REPORT:
+++ set_anqp_req(ANQP_REQ_NEIGHBOR_REPORT,
+++ "Neighbor Report",
+++ get_anqp_elem(hapd, info_id) != NULL, qi);
+++ break;
++ default:
++ #ifdef CONFIG_FILS
++ if (info_id == ANQP_FILS_REALM_INFO &&
++diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
++index 1528af4..d0241f2 100644
++--- a/src/ap/gas_serv.h
+++++ b/src/ap/gas_serv.h
++@@ -40,6 +40,8 @@
++ (1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST))
++ #define ANQP_REQ_EMERGENCY_NAI \
++ (1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
+++#define ANQP_REQ_NEIGHBOR_REPORT \
+++ (1 << (ANQP_NEIGHBOR_REPORT - ANQP_QUERY_LIST))
++ /*
++ * First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the
++ * optimized bitmap.
++--
++2.18.0
++
+diff --git a/package/network/services/hostapd/patches/903-master-sync-include-uapi-linux-nl80211.patch b/package/network/services/hostapd/patches/903-master-sync-include-uapi-linux-nl80211.patch
+new file mode 100644
+index 0000000..fe47b57
+--- /dev/null
++++ b/package/network/services/hostapd/patches/903-master-sync-include-uapi-linux-nl80211.patch
+@@ -0,0 +1,57 @@
++diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
++index f962c06..f7be755 100644
++--- a/src/drivers/nl80211_copy.h
+++++ b/src/drivers/nl80211_copy.h
++@@ -2560,6 +2560,19 @@ enum nl80211_commands {
++ * disassoc events to indicate that an immediate reconnect to the AP
++ * is desired.
++ *
+++ * @NL80211_ATTR_OBSS_COLOR_BITMAP: bitmap of the u64 BSS colors for the
+++ * %NL80211_CMD_OBSS_COLOR_COLLISION event.
+++ *
+++ * @NL80211_ATTR_COLOR_CHANGE_COUNT: u8 attribute specifying the number of TBTT's
+++ * until the color switch event.
+++ * @NL80211_ATTR_COLOR_CHANGE_COLOR: u8 attribute specifying the color that we are
+++ * switching to
+++ * @NL80211_ATTR_COLOR_CHANGE_ELEMS: Nested set of attributes containing the IE
+++ * information for the time while performing a color switch.
+++ *
+++ * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce
+++ * transmit power to stay within regulatory limits. u32, dBi.
+++ *
++ * @NUM_NL80211_ATTR: total number of nl80211_attrs available
++ * @NL80211_ATTR_MAX: highest attribute number currently defined
++ * @__NL80211_ATTR_AFTER_LAST: internal use
++@@ -3057,6 +3070,14 @@ enum nl80211_attrs {
++
++ NL80211_ATTR_DISABLE_HE,
++
+++ NL80211_ATTR_OBSS_COLOR_BITMAP,
+++
+++ NL80211_ATTR_COLOR_CHANGE_COUNT,
+++ NL80211_ATTR_COLOR_CHANGE_COLOR,
+++ NL80211_ATTR_COLOR_CHANGE_ELEMS,
+++
+++ NL80211_ATTR_WIPHY_ANTENNA_GAIN,
+++
++ /* add attributes here, update the policy in nl80211.c */
++
++ __NL80211_ATTR_AFTER_LAST,
++@@ -5950,6 +5971,9 @@ enum nl80211_feature_flags {
++ * frame protection for all management frames exchanged during the
++ * negotiation and range measurement procedure.
++ *
+++ * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
+++ * detection and change announcemnts.
+++ *
++ * @NUM_NL80211_EXT_FEATURES: number of extended features.
++ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
++ */
++@@ -6014,6 +6038,7 @@ enum nl80211_ext_feature_index {
++ NL80211_EXT_FEATURE_SECURE_LTF,
++ NL80211_EXT_FEATURE_SECURE_RTT,
++ NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
+++ NL80211_EXT_FEATURE_BSS_COLOR,
++
++ /* add new features before the definition below */
++ NUM_NL80211_EXT_FEATURES,
+diff --git a/package/network/services/hostapd/patches/904-master-Support-including-neignbor-report-elements-in-BTM-re.patch b/package/network/services/hostapd/patches/904-master-Support-including-neignbor-report-elements-in-BTM-re.patch
+new file mode 100644
+index 0000000..504c738
+--- /dev/null
++++ b/package/network/services/hostapd/patches/904-master-Support-including-neignbor-report-elements-in-BTM-re.patch
+@@ -0,0 +1,68 @@
++From 67e43b33cc70e63e6eabf571ac2c134a5e25b665 Mon Sep 17 00:00:00 2001
++From: "howard.hsu" <howard-yh.hsu@mediatek.com>
++Date: Wed, 19 Jan 2022 19:49:09 +0800
++Subject: [PATCH 3/9] Support including neignbor report elements in BTM
++ response
++
++---
++ src/ap/wnm_ap.c | 25 +++++++++++++++++++++++--
++ 1 file changed, 23 insertions(+), 2 deletions(-)
++
++diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
++index 2f66a54..e37ddd0 100644
++--- a/src/ap/wnm_ap.c
+++++ b/src/ap/wnm_ap.c
++@@ -20,6 +20,7 @@
++ #include "ap/wpa_auth.h"
++ #include "mbo_ap.h"
++ #include "wnm_ap.h"
+++#include "ap/neighbor_db.h"
++
++ #define MAX_TFS_IE_LEN 1024
++
++@@ -370,9 +371,21 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
++ u8 *pos;
++ int res;
++
++- mgmt = os_zalloc(sizeof(*mgmt));
++- if (mgmt == NULL)
+++ int nr_num = hostapd_neighbor_count(hapd);
+++ int nr_size = ETH_ALEN + 4 + 1 + 1 + 1 + 5;
+++ int total_nr_size = nr_num * nr_size;
+++ u8 *nr_data = os_malloc(total_nr_size);
+++ int nr_data_len = 0;
+++ if(nr_data == NULL) {
+++ wpa_printf (MSG_ERROR, "Failed to allocate memory");
+++ } else {
+++ nr_data_len = hostapd_neighbor_insert_buffer(hapd, nr_data, total_nr_size);
+++ }
+++ mgmt = os_zalloc(sizeof(*mgmt) + nr_data_len);
+++ if (mgmt == NULL) {
+++ wpa_printf (MSG_ERROR, "Failed to allocate memory for mgmt frame");
++ return -1;
+++ }
++ os_memcpy(mgmt->da, addr, ETH_ALEN);
++ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
++ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
++@@ -382,10 +395,18 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
++ mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
++ mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
++ mgmt->u.action.u.bss_tm_req.req_mode = 0;
+++ if(nr_num) {
+++ mgmt->u.action.u.bss_tm_req.req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
+++ }
++ mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
++ mgmt->u.action.u.bss_tm_req.validity_interval = 1;
++ pos = mgmt->u.action.u.bss_tm_req.variable;
++
+++ if(nr_num) {
+++ os_memcpy(pos, nr_data, nr_data_len);
+++ pos += nr_data_len;
+++ }
+++
++ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
++ wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
++ MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
++--
++2.18.0
++
+diff --git a/package/network/services/hostapd/patches/904-master-zero-wait_dfs.patch b/package/network/services/hostapd/patches/904-master-zero-wait_dfs.patch
+new file mode 100644
+index 0000000..cb11aee
+--- /dev/null
++++ b/package/network/services/hostapd/patches/904-master-zero-wait_dfs.patch
+@@ -0,0 +1,851 @@
++diff --git a/hostapd/config_file.c b/hostapd/config_file.c
++index 1e1b685..8f6281a 100644
++--- a/hostapd/config_file.c
+++++ b/hostapd/config_file.c
++@@ -2476,6 +2476,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
++ conf->ieee80211d = atoi(pos);
++ } else if (os_strcmp(buf, "ieee80211h") == 0) {
++ conf->ieee80211h = atoi(pos);
+++ } else if (os_strcmp(buf, "radar_offchan") == 0) {
+++ conf->radar_offchan = atoi(pos);
++ } else if (os_strcmp(buf, "ieee8021x") == 0) {
++ bss->ieee802_1x = atoi(pos);
++ } else if (os_strcmp(buf, "eapol_version") == 0) {
++diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
++index a89ce9b..0c951a9 100644
++--- a/hostapd/hostapd.conf
+++++ b/hostapd/hostapd.conf
++@@ -143,6 +143,13 @@ ssid=test
++ # ieee80211d=1 and local_pwr_constraint configured.
++ #spectrum_mgmt_required=1
++
+++# Enable radar/CAC detection through a dedicated offchannel chain available on
+++# some hw. The chain can't be used to transmits or receives frames.
+++# This feature allows to avoid CAC downtime switching on a different channel
+++# during CAC detection on the selected radar channel.
+++# (default: 0 = disabled, 1 = enabled)
+++#radar_offchan=0
+++
++ # Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz),
++ # g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used
++ # with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this
++diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
++index 28b7efe..ffc3c2c 100644
++--- a/src/ap/ap_config.h
+++++ b/src/ap/ap_config.h
++@@ -993,6 +993,7 @@ struct hostapd_config {
++ int ieee80211d;
++
++ int ieee80211h; /* DFS */
+++ int radar_offchan;
++
++ /*
++ * Local power constraint is an octet encoded as an unsigned integer in
++diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
++index bc49079..c97ee39 100644
++--- a/src/ap/ap_drv_ops.c
+++++ b/src/ap/ap_drv_ops.c
++@@ -810,7 +810,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
++ int channel, int ht_enabled, int vht_enabled,
++ int he_enabled,
++ int sec_channel_offset, int oper_chwidth,
++- int center_segment0, int center_segment1)
+++ int center_segment0, int center_segment1,
+++ int radar_offchan)
++ {
++ struct hostapd_data *hapd = iface->bss[0];
++ struct hostapd_freq_params data;
++@@ -836,10 +837,14 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
++ wpa_printf(MSG_ERROR, "Can't set freq params");
++ return -1;
++ }
+++ data.radar_offchan = radar_offchan;
++
++ res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
++ if (!res) {
++- iface->cac_started = 1;
+++ if (radar_offchan)
+++ iface->radar_offchan.cac_started = 1;
+++ else
+++ iface->cac_started = 1;
++ os_get_reltime(&iface->dfs_cac_start);
++ }
++
++diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
++index 61c8f64..92842a1 100644
++--- a/src/ap/ap_drv_ops.h
+++++ b/src/ap/ap_drv_ops.h
++@@ -130,7 +130,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
++ int channel, int ht_enabled, int vht_enabled,
++ int he_enabled,
++ int sec_channel_offset, int oper_chwidth,
++- int center_segment0, int center_segment1);
+++ int center_segment0, int center_segment1,
+++ int radar_offchan);
++ int hostapd_drv_do_acs(struct hostapd_data *hapd);
++ int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
++ u16 reason_code, const u8 *ie, size_t ielen);
++diff --git a/src/ap/dfs.c b/src/ap/dfs.c
++index eccda1a..3b1276f 100644
++--- a/src/ap/dfs.c
+++++ b/src/ap/dfs.c
++@@ -51,16 +51,31 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
++ return n_chans;
++ }
++
++-
+++/*
+++ * flags:
+++ * - 0: any channel
+++ * - 1: non-radar channel or radar available one
+++ * - 2: radar-only channel not yet available
+++ */
++ static int dfs_channel_available(struct hostapd_channel_data *chan,
++- int skip_radar)
+++ int flags)
++ {
+++ if (flags == 2) {
+++ /* Select only radar channel where CAC has not been
+++ * performed yet
+++ */
+++ if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+++ (chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+++ HOSTAPD_CHAN_DFS_USABLE)
+++ return 1;
+++ return 0;
+++ }
++ /*
++ * When radar detection happens, CSA is performed. However, there's no
++ * time for CAC, so radar channels must be skipped when finding a new
++ * channel for CSA, unless they are available for immediate use.
++ */
++- if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) &&
+++ if (flags && (chan->flag & HOSTAPD_CHAN_RADAR) &&
++ ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
++ HOSTAPD_CHAN_DFS_AVAILABLE))
++ return 0;
++@@ -136,10 +151,15 @@ dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx)
++ return NULL;
++ }
++
++-
+++/*
+++ * flags:
+++ * - 0: any channel
+++ * - 1: non-radar channel or radar available one
+++ * - 2: radar-only channel not yet available
+++ */
++ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
++ int first_chan_idx, int num_chans,
++- int skip_radar)
+++ int flags)
++ {
++ struct hostapd_channel_data *first_chan, *chan;
++ int i;
++@@ -178,7 +198,7 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
++ return 0;
++ }
++
++- if (!dfs_channel_available(chan, skip_radar)) {
+++ if (!dfs_channel_available(chan, flags)) {
++ wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
++ first_chan->freq + i * 20);
++ return 0;
++@@ -205,10 +225,15 @@ static int is_in_chanlist(struct hostapd_iface *iface,
++ * - hapd->secondary_channel
++ * - hapd->vht/he_oper_centr_freq_seg0_idx
++ * - hapd->vht/he_oper_centr_freq_seg1_idx
+++ *
+++ * flags:
+++ * - 0: any channel
+++ * - 1: non-radar channel or radar available one
+++ * - 2: radar-only channel not yet available
++ */
++ static int dfs_find_channel(struct hostapd_iface *iface,
++ struct hostapd_channel_data **ret_chan,
++- int idx, int skip_radar)
+++ int idx, int flags)
++ {
++ struct hostapd_hw_modes *mode;
++ struct hostapd_channel_data *chan;
++@@ -233,7 +258,7 @@ static int dfs_find_channel(struct hostapd_iface *iface,
++ }
++
++ /* Skip incompatible chandefs */
++- if (!dfs_chan_range_available(mode, i, n_chans, skip_radar)) {
+++ if (!dfs_chan_range_available(mode, i, n_chans, flags)) {
++ wpa_printf(MSG_DEBUG,
++ "DFS: range not available for %d (%d)",
++ chan->freq, chan->chan);
++@@ -467,13 +492,18 @@ static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
++ return res;
++ }
++
++-
+++/*
+++ * flags:
+++ * - 0: any channel
+++ * - 1: non-radar channel or radar available one
+++ * - 2: radar-only channel not yet available
+++ */
++ static struct hostapd_channel_data *
++ dfs_get_valid_channel(struct hostapd_iface *iface,
++ int *secondary_channel,
++ u8 *oper_centr_freq_seg0_idx,
++ u8 *oper_centr_freq_seg1_idx,
++- int skip_radar)
+++ int flags)
++ {
++ struct hostapd_hw_modes *mode;
++ struct hostapd_channel_data *chan = NULL;
++@@ -502,7 +532,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
++ return NULL;
++
++ /* Get the count first */
++- num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
+++ num_available_chandefs = dfs_find_channel(iface, NULL, 0, flags);
++ wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d",
++ num_available_chandefs);
++ if (num_available_chandefs == 0)
++@@ -523,7 +553,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
++ return NULL;
++
++ chan_idx = _rand % num_available_chandefs;
++- dfs_find_channel(iface, &chan, chan_idx, skip_radar);
+++ dfs_find_channel(iface, &chan, chan_idx, flags);
++ if (!chan) {
++ wpa_printf(MSG_DEBUG, "DFS: no random channel found");
++ return NULL;
++@@ -552,7 +582,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
++ for (i = 0; i < num_available_chandefs - 1; i++) {
++ /* start from chan_idx + 1, end when chan_idx - 1 */
++ chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs;
++- dfs_find_channel(iface, &chan2, chan_idx2, skip_radar);
+++ dfs_find_channel(iface, &chan2, chan_idx2, flags);
++ if (chan2 && abs(chan2->chan - chan->chan) > 12) {
++ /* two channels are not adjacent */
++ sec_chan_idx_80p80 = chan2->chan;
++@@ -582,6 +612,27 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
++ return chan;
++ }
++
+++static int dfs_set_valid_channel(struct hostapd_iface *iface, int skip_radar)
+++{
+++ struct hostapd_channel_data *channel;
+++ u8 cf1 = 0, cf2 = 0;
+++ int sec = 0;
+++
+++ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
+++ skip_radar);
+++ if (!channel) {
+++ wpa_printf(MSG_ERROR, "could not get valid channel");
+++ return -1;
+++ }
+++
+++ iface->freq = channel->freq;
+++ iface->conf->channel = channel->chan;
+++ iface->conf->secondary_channel = sec;
+++ hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
+++ hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
+++
+++ return 0;
+++}
++
++ static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
++ {
++@@ -761,6 +812,11 @@ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
++ return cac_time_ms;
++ }
++
+++static int hostapd_is_radar_offchan_enabled(struct hostapd_iface *iface)
+++{
+++ return (iface->drv_flags2 & WPA_DRIVER_RADAR_OFFCHAN) &&
+++ iface->conf->radar_offchan;
+++}
++
++ /*
++ * Main DFS handler
++@@ -770,9 +826,8 @@ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
++ */
++ int hostapd_handle_dfs(struct hostapd_iface *iface)
++ {
++- struct hostapd_channel_data *channel;
++ int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
++- int skip_radar = 0;
+++ int skip_radar = 0, radar_offchan;
++
++ if (is_6ghz_freq(iface->freq))
++ return 1;
++@@ -825,28 +880,18 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
++ wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
++ res, res ? "yes": "no");
++ if (res) {
++- int sec = 0;
++- u8 cf1 = 0, cf2 = 0;
++-
++- channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
++- skip_radar);
++- if (!channel) {
++- wpa_printf(MSG_ERROR, "could not get valid channel");
+++ if (dfs_set_valid_channel(iface, skip_radar) < 0) {
++ hostapd_set_state(iface, HAPD_IFACE_DFS);
++ return 0;
++ }
++-
++- iface->freq = channel->freq;
++- iface->conf->channel = channel->chan;
++- iface->conf->secondary_channel = sec;
++- hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
++- hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
++ }
++ } while (res);
++
++ /* Finally start CAC */
++ hostapd_set_state(iface, HAPD_IFACE_DFS);
++- wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
+++ radar_offchan = hostapd_is_radar_offchan_enabled(iface);
+++ wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz offchan %d",
+++ iface->freq, radar_offchan);
++ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
++ "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
++ iface->freq,
++@@ -863,13 +908,37 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
++ iface->conf->secondary_channel,
++ hostapd_get_oper_chwidth(iface->conf),
++ hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
++- hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
+++ hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+++ radar_offchan);
++
++ if (res) {
++ wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
++ return -1;
++ }
++
+++ if (radar_offchan) {
+++ /* Cache offchannel radar parameters */
+++ iface->radar_offchan.channel = iface->conf->channel;
+++ iface->radar_offchan.secondary_channel =
+++ iface->conf->secondary_channel;
+++ iface->radar_offchan.freq = iface->freq;
+++ iface->radar_offchan.centr_freq_seg0_idx =
+++ hostapd_get_oper_centr_freq_seg0_idx(iface->conf);
+++ iface->radar_offchan.centr_freq_seg1_idx =
+++ hostapd_get_oper_centr_freq_seg1_idx(iface->conf);
+++
+++ /*
+++ * Let's select a random channel for the moment
+++ * and perform CAC on dedicated radar chain
+++ */
+++ res = dfs_set_valid_channel(iface, 1);
+++ if (res < 0)
+++ return res;
+++
+++ iface->radar_offchan.temp_ch = 1;
+++ return 1;
+++ }
+++
++ return 0;
++ }
++
++@@ -890,6 +959,157 @@ int hostapd_is_dfs_chan_available(struct hostapd_iface *iface)
++ return dfs_check_chans_available(iface, start_chan_idx, n_chans);
++ }
++
+++static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+++ int channel, int freq,
+++ int secondary_channel,
+++ u8 oper_centr_freq_seg0_idx,
+++ u8 oper_centr_freq_seg1_idx)
+++{
+++ struct hostapd_hw_modes *cmode = iface->current_mode;
+++ int ieee80211_mode = IEEE80211_MODE_AP, err, i;
+++ struct csa_settings csa_settings;
+++ u8 new_vht_oper_chwidth;
+++
+++ wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel);
+++ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
+++ "freq=%d chan=%d sec_chan=%d", freq, channel,
+++ secondary_channel);
+++
+++ new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+++ hostapd_set_oper_chwidth(iface->conf,
+++ hostapd_get_oper_chwidth(iface->conf));
+++
+++ /* Setup CSA request */
+++ os_memset(&csa_settings, 0, sizeof(csa_settings));
+++ csa_settings.cs_count = 5;
+++ csa_settings.block_tx = 1;
+++#ifdef CONFIG_MESH
+++ if (iface->mconf)
+++ ieee80211_mode = IEEE80211_MODE_MESH;
+++#endif /* CONFIG_MESH */
+++ err = hostapd_set_freq_params(&csa_settings.freq_params,
+++ iface->conf->hw_mode,
+++ freq, channel,
+++ iface->conf->enable_edmg,
+++ iface->conf->edmg_channel,
+++ iface->conf->ieee80211n,
+++ iface->conf->ieee80211ac,
+++ iface->conf->ieee80211ax,
+++ secondary_channel,
+++ new_vht_oper_chwidth,
+++ oper_centr_freq_seg0_idx,
+++ oper_centr_freq_seg1_idx,
+++ cmode->vht_capab,
+++ &cmode->he_capab[ieee80211_mode]);
+++
+++ if (err) {
+++ wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
+++ hostapd_disable_iface(iface);
+++ return err;
+++ }
+++
+++ for (i = 0; i < iface->num_bss; i++) {
+++ err = hostapd_switch_channel(iface->bss[i], &csa_settings);
+++ if (err)
+++ break;
+++ }
+++
+++ if (err) {
+++ wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
+++ err);
+++ iface->freq = freq;
+++ iface->conf->channel = channel;
+++ iface->conf->secondary_channel = secondary_channel;
+++ hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
+++ hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+++ oper_centr_freq_seg0_idx);
+++ hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+++ oper_centr_freq_seg1_idx);
+++
+++ hostapd_disable_iface(iface);
+++ hostapd_enable_iface(iface);
+++
+++ return 0;
+++ }
+++
+++ /* Channel configuration will be updated once CSA completes and
+++ * ch_switch_notify event is received */
+++ wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
+++
+++ return 0;
+++}
+++
+++static struct hostapd_channel_data *
+++dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
+++ u8 *oper_centr_freq_seg0_idx,
+++ u8 *oper_centr_freq_seg1_idx, int *skip_radar);
+++
+++static void
+++hostpad_dfs_update_offchannel_chain(struct hostapd_iface *iface)
+++{
+++ struct hostapd_channel_data *channel;
+++ int sec = 0, flags = 2;
+++ u8 cf1 = 0, cf2 = 0;
+++
+++ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, 2);
+++ if (!channel || channel->chan == iface->conf->channel)
+++ channel = dfs_downgrade_bandwidth(iface, &sec, &cf1, &cf2,
+++ &flags);
+++ if (!channel ||
+++ hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
+++ channel->freq, channel->chan,
+++ iface->conf->ieee80211n,
+++ iface->conf->ieee80211ac,
+++ iface->conf->ieee80211ax,
+++ sec, hostapd_get_oper_chwidth(iface->conf),
+++ cf1, cf2, 1)) {
+++ /*
+++ * Toggle interface state to enter DFS state
+++ * until NOP is finished.
+++ */
+++ wpa_printf(MSG_ERROR, "DFS failed start CAC offchannel");
+++ return;
+++ }
+++
+++ wpa_printf(MSG_DEBUG, "%s: setting offchannel chain to chan %d (%d MHz)",
+++ __func__, channel->chan, channel->freq);
+++
+++ iface->radar_offchan.channel = channel->chan;
+++ iface->radar_offchan.freq = channel->freq;
+++ iface->radar_offchan.secondary_channel = sec;
+++ iface->radar_offchan.centr_freq_seg0_idx = cf1;
+++ iface->radar_offchan.centr_freq_seg1_idx = cf2;
+++}
+++
+++/* FIXME: check if all channel bandwith */
+++static int
+++hostapd_dfs_is_offchan_event(struct hostapd_iface *iface, int freq)
+++{
+++ if (iface->radar_offchan.freq != freq)
+++ return 0;
+++
+++ return 1;
+++}
+++
+++static int
+++hostapd_dfs_start_channel_switch_offchan(struct hostapd_iface *iface)
+++{
+++ iface->conf->channel = iface->radar_offchan.channel;
+++ iface->freq = iface->radar_offchan.freq;
+++ iface->conf->secondary_channel =
+++ iface->radar_offchan.secondary_channel;
+++ hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+++ iface->radar_offchan.centr_freq_seg0_idx);
+++ hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+++ iface->radar_offchan.centr_freq_seg1_idx);
+++
+++ hostpad_dfs_update_offchannel_chain(iface);
+++
+++ return hostapd_dfs_request_channel_switch(iface, iface->conf->channel,
+++ iface->freq, iface->conf->secondary_channel,
+++ hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+++ hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
+++}
++
++ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
++ int ht_enabled, int chan_offset, int chan_width,
++@@ -911,6 +1131,23 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
++ set_dfs_state(iface, freq, ht_enabled, chan_offset,
++ chan_width, cf1, cf2,
++ HOSTAPD_CHAN_DFS_AVAILABLE);
+++
+++ /*
+++ * radar event from offchannel chain for selected
+++ * channel. Perfrom CSA, move main chain to selected
+++ * channel and configure offchannel chain to a new DFS
+++ * channel
+++ */
+++ if (hostapd_is_radar_offchan_enabled(iface) &&
+++ hostapd_dfs_is_offchan_event(iface, freq)) {
+++ iface->radar_offchan.cac_started = 0;
+++ if (iface->radar_offchan.temp_ch) {
+++ iface->radar_offchan.temp_ch = 0;
+++ return hostapd_dfs_start_channel_switch_offchan(iface);
+++ }
+++ return 0;
+++ }
+++
++ /*
++ * Just mark the channel available when CAC completion
++ * event is received in enabled state. CAC result could
++@@ -927,6 +1164,10 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
++ iface->cac_started = 0;
++ }
++ }
+++ } else if (hostapd_is_radar_offchan_enabled(iface) &&
+++ hostapd_dfs_is_offchan_event(iface, freq)) {
+++ iface->radar_offchan.cac_started = 0;
+++ hostpad_dfs_update_offchannel_chain(iface);
++ }
++
++ return 0;
++@@ -1036,6 +1277,44 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
++ return err;
++ }
++
+++static int
+++hostapd_dfs_offchan_start_channel_switch(struct hostapd_iface *iface, int freq)
+++{
+++ if (!hostapd_is_radar_offchan_enabled(iface))
+++ return -1; /* Offchannel chain not supported */
+++
+++ wpa_printf(MSG_DEBUG,
+++ "%s called (offchannel CAC active: %s, CSA active: %s)",
+++ __func__, iface->radar_offchan.cac_started ? "yes" : "no",
+++ hostapd_csa_in_progress(iface) ? "yes" : "no");
+++
+++ /* Check if CSA in progress */
+++ if (hostapd_csa_in_progress(iface))
+++ return 0;
+++
+++ /*
+++ * If offchannel radar detation is supported and radar channel
+++ * monitored by offchain is available switch to it without waiting
+++ * for the CAC otherwise let's keep a random channel.
+++ * If radar pattern is reported on offchannel chain, just switch to
+++ * monitor another radar channel.
+++ */
+++ if (hostapd_dfs_is_offchan_event(iface, freq)) {
+++ hostpad_dfs_update_offchannel_chain(iface);
+++ return 0;
+++ }
+++
+++ /* Offchannel not availanle yet. Perform CAC on main chain */
+++ if (iface->radar_offchan.cac_started) {
+++ /* We want to switch to monitored channel as soon as
+++ * CAC is completed.
+++ */
+++ iface->radar_offchan.temp_ch = 1;
+++ return -1;
+++ }
+++
+++ return hostapd_dfs_start_channel_switch_offchan(iface);
+++}
++
++ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
++ {
++@@ -1043,13 +1322,7 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
++ int secondary_channel;
++ u8 oper_centr_freq_seg0_idx;
++ u8 oper_centr_freq_seg1_idx;
++- u8 new_vht_oper_chwidth;
++ int skip_radar = 1;
++- struct csa_settings csa_settings;
++- unsigned int i;
++- int err = 1;
++- struct hostapd_hw_modes *cmode = iface->current_mode;
++- u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
++ int ieee80211_mode = IEEE80211_MODE_AP;
++
++ wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
++@@ -1113,73 +1386,16 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
++ }
++ }
++
++- wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
++- channel->chan);
++- wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
++- "freq=%d chan=%d sec_chan=%d", channel->freq,
++- channel->chan, secondary_channel);
++-
++- new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
++- hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth);
++-
++- /* Setup CSA request */
++- os_memset(&csa_settings, 0, sizeof(csa_settings));
++- csa_settings.cs_count = 5;
++- csa_settings.block_tx = 1;
++ #ifdef CONFIG_MESH
++ if (iface->mconf)
++ ieee80211_mode = IEEE80211_MODE_MESH;
++ #endif /* CONFIG_MESH */
++- err = hostapd_set_freq_params(&csa_settings.freq_params,
++- iface->conf->hw_mode,
++- channel->freq,
++- channel->chan,
++- iface->conf->enable_edmg,
++- iface->conf->edmg_channel,
++- iface->conf->ieee80211n,
++- iface->conf->ieee80211ac,
++- iface->conf->ieee80211ax,
++- secondary_channel,
++- new_vht_oper_chwidth,
++- oper_centr_freq_seg0_idx,
++- oper_centr_freq_seg1_idx,
++- cmode->vht_capab,
++- &cmode->he_capab[ieee80211_mode]);
++-
++- if (err) {
++- wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
++- hostapd_disable_iface(iface);
++- return err;
++- }
++
++- for (i = 0; i < iface->num_bss; i++) {
++- err = hostapd_switch_channel(iface->bss[i], &csa_settings);
++- if (err)
++- break;
++- }
++-
++- if (err) {
++- wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
++- err);
++- iface->freq = channel->freq;
++- iface->conf->channel = channel->chan;
++- iface->conf->secondary_channel = secondary_channel;
++- hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
++- hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
++- oper_centr_freq_seg0_idx);
++- hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
++- oper_centr_freq_seg1_idx);
++-
++- hostapd_disable_iface(iface);
++- hostapd_enable_iface(iface);
++- return 0;
++- }
++-
++- /* Channel configuration will be updated once CSA completes and
++- * ch_switch_notify event is received */
++-
++- wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
++- return 0;
+++ return hostapd_dfs_request_channel_switch(iface, channel->chan,
+++ channel->freq,
+++ secondary_channel,
+++ oper_centr_freq_seg0_idx,
+++ oper_centr_freq_seg1_idx);
++ }
++
++
++@@ -1208,15 +1424,19 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
++ if (!res)
++ return 0;
++
++- /* Skip if reported radar event not overlapped our channels */
++- res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
++- if (!res)
++- return 0;
+++ if (!hostapd_dfs_is_offchan_event(iface, freq)) {
+++ /* Skip if reported radar event not overlapped our channels */
+++ res = dfs_are_channels_overlapped(iface, freq, chan_width,
+++ cf1, cf2);
+++ if (!res)
+++ return 0;
+++ }
++
++- /* radar detected while operating, switch the channel. */
++- res = hostapd_dfs_start_channel_switch(iface);
+++ if (hostapd_dfs_offchan_start_channel_switch(iface, freq))
+++ /* radar detected while operating, switch the channel. */
+++ return hostapd_dfs_start_channel_switch(iface);
++
++- return res;
+++ return 0;
++ }
++
++
++@@ -1284,7 +1504,11 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
++ "seg1=%d cac_time=%ds",
++ freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
++ iface->dfs_cac_ms / 1000);
++- iface->cac_started = 1;
+++
+++ if (hostapd_dfs_is_offchan_event(iface, freq))
+++ iface->radar_offchan.cac_started = 1;
+++ else
+++ iface->cac_started = 1;
++ os_get_reltime(&iface->dfs_cac_start);
++ return 0;
++ }
++diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
++index 27b985d..1c6c94e 100644
++--- a/src/ap/hostapd.h
+++++ b/src/ap/hostapd.h
++@@ -521,6 +521,21 @@ struct hostapd_iface {
++ int *basic_rates;
++ int freq;
++
+++ /* Offchanel chain configuration */
+++ struct {
+++ int channel;
+++ int secondary_channel;
+++ int freq;
+++ int centr_freq_seg0_idx;
+++ int centr_freq_seg1_idx;
+++ /* Main chain is on temporary channel during
+++ * CAC detection on radar offchain
+++ */
+++ unsigned int temp_ch:1;
+++ /* CAC started on radar offchain */
+++ unsigned int cac_started:1;
+++ } radar_offchan;
+++
++ u16 hw_flags;
++
++ /* Number of associated Non-ERP stations (i.e., stations using 802.11b
++diff --git a/src/drivers/driver.h b/src/drivers/driver.h
++index 6d9194f..7ed47c0 100644
++--- a/src/drivers/driver.h
+++++ b/src/drivers/driver.h
++@@ -777,6 +777,11 @@ struct hostapd_freq_params {
++ * for IEEE 802.11ay EDMG configuration.
++ */
++ struct ieee80211_edmg_config edmg;
+++
+++ /**
+++ * radar_offchan - Whether radar/CAC offchannel is requested
+++ */
+++ int radar_offchan;
++ };
++
++ /**
++@@ -2026,6 +2031,8 @@ struct wpa_driver_capa {
++ #define WPA_DRIVER_FLAGS2_OCV 0x0000000000000080ULL
++ /** Driver expects user space implementation of SME in AP mode */
++ #define WPA_DRIVER_FLAGS2_AP_SME 0x0000000000000100ULL
+++/** Driver supports offchannel radar/CAC detection */
+++#define WPA_DRIVER_RADAR_OFFCHAN 0x0000000000000200ULL
++ u64 flags2;
++
++ #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
++diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
++index 4db8cce..62c3cd8 100644
++--- a/src/drivers/driver_nl80211.c
+++++ b/src/drivers/driver_nl80211.c
++@@ -4885,6 +4885,7 @@ static int nl80211_put_freq_params(struct nl_msg *msg,
++ wpa_printf(MSG_DEBUG, " * he_enabled=%d", freq->he_enabled);
++ wpa_printf(MSG_DEBUG, " * vht_enabled=%d", freq->vht_enabled);
++ wpa_printf(MSG_DEBUG, " * ht_enabled=%d", freq->ht_enabled);
+++ wpa_printf(MSG_DEBUG, " * radar_offchan=%d", freq->radar_offchan);
++
++ hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
++ is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
++@@ -4962,6 +4963,9 @@ static int nl80211_put_freq_params(struct nl_msg *msg,
++ NL80211_CHAN_NO_HT))
++ return -ENOBUFS;
++ }
+++ if (freq->radar_offchan)
+++ nla_put_flag(msg, NL80211_ATTR_RADAR_OFFCHAN);
+++
++ return 0;
++ }
++
++diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
++index cd596e3..e370ef3 100644
++--- a/src/drivers/driver_nl80211_capa.c
+++++ b/src/drivers/driver_nl80211_capa.c
++@@ -665,6 +665,10 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
++ if (ext_feature_isset(ext_features, len,
++ NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION))
++ capa->flags2 |= WPA_DRIVER_FLAGS2_OCV;
+++
+++ if (ext_feature_isset(ext_features, len,
+++ NL80211_EXT_FEATURE_RADAR_OFFCHAN))
+++ capa->flags2 |= WPA_DRIVER_RADAR_OFFCHAN;
++ }
++
++
++diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
++index f7be755..736b483 100644
++--- a/src/drivers/nl80211_copy.h
+++++ b/src/drivers/nl80211_copy.h
++@@ -2573,6 +2573,10 @@ enum nl80211_commands {
++ * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce
++ * transmit power to stay within regulatory limits. u32, dBi.
++ *
+++ * @NL80211_ATTR_RADAR_OFFCHAN: Configure dedicated chain available for radar
+++ * detection on some hw. The chain can't be used to transmits or receives
+++ * frames. The driver is supposed to implement CAC management in sw or fw.
+++ *
++ * @NUM_NL80211_ATTR: total number of nl80211_attrs available
++ * @NL80211_ATTR_MAX: highest attribute number currently defined
++ * @__NL80211_ATTR_AFTER_LAST: internal use
++@@ -3078,6 +3082,8 @@ enum nl80211_attrs {
++
++ NL80211_ATTR_WIPHY_ANTENNA_GAIN,
++
+++ NL80211_ATTR_RADAR_OFFCHAN,
+++
++ /* add attributes here, update the policy in nl80211.c */
++
++ __NL80211_ATTR_AFTER_LAST,
++@@ -5974,6 +5980,9 @@ enum nl80211_feature_flags {
++ * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
++ * detection and change announcemnts.
++ *
+++ * @NL80211_EXT_FEATURE_RADAR_OFFCHAN: Device supports offchannel radar/CAC
+++ * detection.
+++ *
++ * @NUM_NL80211_EXT_FEATURES: number of extended features.
++ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
++ */
++@@ -6039,6 +6048,7 @@ enum nl80211_ext_feature_index {
++ NL80211_EXT_FEATURE_SECURE_RTT,
++ NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
++ NL80211_EXT_FEATURE_BSS_COLOR,
+++ NL80211_EXT_FEATURE_RADAR_OFFCHAN,
++
++ /* add new features before the definition below */
++ NUM_NL80211_EXT_FEATURES,
+diff --git a/package/network/services/hostapd/patches/905-master-Support-configuring-BSS-Termination-TSF-by-using-hos.patch b/package/network/services/hostapd/patches/905-master-Support-configuring-BSS-Termination-TSF-by-using-hos.patch
+new file mode 100644
+index 0000000..2f9b174
+--- /dev/null
++++ b/package/network/services/hostapd/patches/905-master-Support-configuring-BSS-Termination-TSF-by-using-hos.patch
+@@ -0,0 +1,66 @@
++From b97cdd75ea3f0c15a6d76cd6483941ee73fa400c Mon Sep 17 00:00:00 2001
++From: "howard.hsu" <howard-yh.hsu@mediatek.com>
++Date: Wed, 19 Jan 2022 20:20:03 +0800
++Subject: [PATCH 4/9] Support configuring BSS Termination TSF by using
++ hostapd_cli command
++
++---
++ hostapd/ctrl_iface.c | 9 +++++++++
++ src/ap/ap_config.c | 1 +
++ src/ap/ap_config.h | 1 +
++ 3 files changed, 11 insertions(+)
++
++diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
++index 568232a..cb2bdbf 100644
++--- a/hostapd/ctrl_iface.c
+++++ b/hostapd/ctrl_iface.c
++@@ -954,6 +954,10 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
++ wpa_printf(MSG_DEBUG, "Invalid bss_term data");
++ return -1;
++ }
+++ if (hapd->conf->bss_termination_tsf) {
+++ WPA_PUT_LE64(&bss_term_dur[2], hapd->conf->bss_termination_tsf);
+++ }
+++
++ end++;
++ WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
++ }
++@@ -1589,6 +1593,11 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
++ #endif /* CONFIG_DPP */
++ } else if (os_strcasecmp(cmd, "setband") == 0) {
++ ret = hostapd_ctrl_iface_set_band(hapd, value);
+++ } else if (os_strcasecmp(cmd, "bss_termination_tsf") == 0) {
+++ int termination_sec = atoi(value);
+++ hapd->conf->bss_termination_tsf = termination_sec;
+++ wpa_printf(MSG_DEBUG, "BSS Termination TSF: value = %d",
+++ termination_sec);
++ } else {
++ ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
++ if (ret)
++diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
++index ce281ac..fc1372a 100644
++--- a/src/ap/ap_config.c
+++++ b/src/ap/ap_config.c
++@@ -170,6 +170,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
++ /* comeback after 10 TUs */
++ bss->pasn_comeback_after = 10;
++ #endif /* CONFIG_PASN */
+++ bss->bss_termination_tsf = 0;
++ }
++
++
++diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
++index ffc3c2c..1f3f3d8 100644
++--- a/src/ap/ap_config.h
+++++ b/src/ap/ap_config.h
++@@ -549,6 +549,7 @@ struct hostapd_bss_config {
++ int wnm_sleep_mode;
++ int wnm_sleep_mode_no_keys;
++ int bss_transition;
+++ unsigned int bss_termination_tsf;
++
++ /* IEEE 802.11u - Interworking */
++ int interworking;
++--
++2.18.0
++
+diff --git a/package/network/services/hostapd/patches/906-master-Disable-interface-if-BSS-Termination-TSF-is-set.patch b/package/network/services/hostapd/patches/906-master-Disable-interface-if-BSS-Termination-TSF-is-set.patch
+new file mode 100644
+index 0000000..18b44fa
+--- /dev/null
++++ b/package/network/services/hostapd/patches/906-master-Disable-interface-if-BSS-Termination-TSF-is-set.patch
+@@ -0,0 +1,47 @@
++From d084074baaa8f9df15810323aeeeba0e98b0dbd5 Mon Sep 17 00:00:00 2001
++From: "howard.hsu" <howard-yh.hsu@mediatek.com>
++Date: Wed, 19 Jan 2022 21:03:38 +0800
++Subject: [PATCH 5/9] Disable interface if BSS Termination TSF is set
++
++---
++ src/ap/wnm_ap.c | 17 +++++++++++++++++
++ 1 file changed, 17 insertions(+)
++
++diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
++index e37ddd0..ce7aca2 100644
++--- a/src/ap/wnm_ap.c
+++++ b/src/ap/wnm_ap.c
++@@ -767,6 +767,22 @@ static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
++ }
++
++
+++void bss_termination_disable_iface(void *eloop_ctx, void *timeout_ctx)
+++{
+++ struct hostapd_data *hapd = eloop_ctx;
+++ hostapd_disable_iface(hapd->iface);
+++}
+++
+++
+++static void set_disable_iface_timer(struct hostapd_data *hapd, struct sta_info *sta,
+++ int disable_iface_timer)
+++{
+++ wpa_printf(MSG_DEBUG, "Disable interface timer set to %d secs", disable_iface_timer);
+++ eloop_register_timeout(disable_iface_timer, 0,
+++ bss_termination_disable_iface, hapd, NULL);
+++}
+++
+++
++ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
++ struct sta_info *sta, const char *url,
++ int disassoc_timer)
++@@ -855,6 +871,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
++ bss_term_dur) {
++ os_memcpy(pos, bss_term_dur, 12);
++ pos += 12;
+++ set_disable_iface_timer(hapd, sta, hapd->conf->bss_termination_tsf);
++ }
++
++ if (url) {
++--
++2.18.0
++
+diff --git a/package/network/services/hostapd/patches/907-master-Add-set_send_disassoc_frame_timer-to-send-disassocia.patch b/package/network/services/hostapd/patches/907-master-Add-set_send_disassoc_frame_timer-to-send-disassocia.patch
+new file mode 100644
+index 0000000..8839c25
+--- /dev/null
++++ b/package/network/services/hostapd/patches/907-master-Add-set_send_disassoc_frame_timer-to-send-disassocia.patch
+@@ -0,0 +1,63 @@
++From 1f4ff04758932b773df99a51055373f7610046ce Mon Sep 17 00:00:00 2001
++From: "howard.hsu" <howard-yh.hsu@mediatek.com>
++Date: Wed, 19 Jan 2022 21:15:07 +0800
++Subject: [PATCH 6/9] Add set_send_disassoc_frame_timer() to send disassociate
++ frame
++
++Function set_disassoc_timer() may fail if key was deleted first. This new
++function will not ask to delete key as set_disassoc_timer() did.
++---
++ src/ap/wnm_ap.c | 30 +++++++++++++++++++++++++++++-
++ 1 file changed, 29 insertions(+), 1 deletion(-)
++
++diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
++index ce7aca2..2ee9fb7 100644
++--- a/src/ap/wnm_ap.c
+++++ b/src/ap/wnm_ap.c
++@@ -767,6 +767,34 @@ static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
++ }
++
++
+++static void set_send_disassoc_frame_timer(struct hostapd_data *hapd, struct sta_info *sta,
+++ int disassoc_timer)
+++{
+++ int timeout, beacon_int;
+++
+++ /*
+++ * Prevent STA from reconnecting using cached PMKSA to force
+++ * full authentication with the authentication server (which may
+++ * decide to reject the connection),
+++ */
+++ wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+++
+++ beacon_int = hapd->iconf->beacon_int;
+++ if (beacon_int < 1)
+++ beacon_int = 100; /* best guess */
+++ /* Calculate timeout in ms based on beacon_int in TU */
+++ timeout = disassoc_timer * beacon_int * 128 / 125;
+++ wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
+++ " set to %d ms", MAC2STR(sta->addr), timeout);
+++
+++ u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
+++
+++ hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
+++ if (sta)
+++ ap_sta_disassociate(hapd, sta, reason);
+++}
+++
+++
++ void bss_termination_disable_iface(void *eloop_ctx, void *timeout_ctx)
++ {
++ struct hostapd_data *hapd = eloop_ctx;
++@@ -908,7 +936,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
++ hapd->openwrt_stats.wnm.bss_transition_request_tx++;
++ if (disassoc_timer) {
++ /* send disassociation frame after time-out */
++- set_disassoc_timer(hapd, sta, disassoc_timer);
+++ set_send_disassoc_frame_timer(hapd, sta, disassoc_timer);
++ }
++
++ return 0;
++--
++2.18.0
++
+diff --git a/package/network/services/hostapd/patches/908-master-Support-including-neighbor-report-elements-in-BTM-re.patch b/package/network/services/hostapd/patches/908-master-Support-including-neighbor-report-elements-in-BTM-re.patch
+new file mode 100644
+index 0000000..dd6a859
+--- /dev/null
++++ b/package/network/services/hostapd/patches/908-master-Support-including-neighbor-report-elements-in-BTM-re.patch
+@@ -0,0 +1,31 @@
++From f0b8ae8248f026c31e19a9c04423013a720f6136 Mon Sep 17 00:00:00 2001
++From: "howard.hsu" <howard-yh.hsu@mediatek.com>
++Date: Wed, 19 Jan 2022 21:16:45 +0800
++Subject: [PATCH 7/9] Support including neighbor report elements in BTM request
++
++---
++ hostapd/ctrl_iface.c | 7 ++++++-
++ 1 file changed, 6 insertions(+), 1 deletion(-)
++
++diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
++index cb2bdbf..2c5cf4b 100644
++--- a/hostapd/ctrl_iface.c
+++++ b/hostapd/ctrl_iface.c
++@@ -984,8 +984,13 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
++ req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
++ }
++
++- if (os_strstr(cmd, " pref=1"))
+++ if (os_strstr(cmd, " pref=1")) {
++ req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
+++ if (nei_len == 0) {
+++ // Add neigibor report from neighbor report db to nei_rep buffer
+++ nei_len = hostapd_neighbor_insert_buffer (hapd, nei_rep, 1000);
+++ }
+++ }
++ if (os_strstr(cmd, " abridged=1"))
++ req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
++ if (os_strstr(cmd, " disassoc_imminent=1"))
++--
++2.18.0
++
+diff --git a/package/network/services/hostapd/patches/909-master-Add-hostapd_neighbor_set_own_report_pref.patch b/package/network/services/hostapd/patches/909-master-Add-hostapd_neighbor_set_own_report_pref.patch
+new file mode 100644
+index 0000000..4e233d4
+--- /dev/null
++++ b/package/network/services/hostapd/patches/909-master-Add-hostapd_neighbor_set_own_report_pref.patch
+@@ -0,0 +1,88 @@
++From de5785cfdbc90d789a9b20e160b7311a2a3841e9 Mon Sep 17 00:00:00 2001
++From: "howard.hsu" <howard-yh.hsu@mediatek.com>
++Date: Wed, 19 Jan 2022 21:27:55 +0800
++Subject: [PATCH 8/9] Add hostapd_neighbor_set_own_report_pref()
++
++If my own BSS is going to terminate itself, the preference value of neighbor
++report must be set to 0.
++---
++ hostapd/ctrl_iface.c | 5 ++++-
++ src/ap/neighbor_db.c | 36 ++++++++++++++++++++++++++++++++++++
++ src/ap/neighbor_db.h | 2 ++
++ 3 files changed, 42 insertions(+), 1 deletion(-)
++
++diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
++index 2c5cf4b..5be75e5 100644
++--- a/hostapd/ctrl_iface.c
+++++ b/hostapd/ctrl_iface.c
++@@ -993,8 +993,11 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
++ }
++ if (os_strstr(cmd, " abridged=1"))
++ req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
++- if (os_strstr(cmd, " disassoc_imminent=1"))
+++ if (os_strstr(cmd, " disassoc_imminent=1")) {
++ req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+++ /* Set own BSS neighbor report preference value as 0 */
+++ hostapd_neighbor_set_own_report_pref(hapd, nei_rep, nei_len, 0);
+++ }
++
++ #ifdef CONFIG_MBO
++ pos = os_strstr(cmd, "mbo=");
++diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
++index a47afd4..6473800 100644
++--- a/src/ap/neighbor_db.c
+++++ b/src/ap/neighbor_db.c
++@@ -348,3 +348,39 @@ void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
++ wpabuf_free(nr);
++ #endif /* NEED_AP_MLME */
++ }
+++
+++
+++void hostapd_neighbor_set_own_report_pref(struct hostapd_data *hapd, char *nei_buf,
+++ size_t buflen, const int pref)
+++{
+++ struct hostapd_neighbor_entry *nr;
+++ char *pos, *next_nr;
+++
+++ pos = nei_buf;
+++ next_nr = nei_buf;
+++
+++ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+++ list) {
+++ pos = next_nr;
+++ next_nr = pos + 2 + wpabuf_len(nr->nr);
+++ /* Shift 2 bytes for Element ID and Neighbor report length */
+++ pos = pos + 2;
+++ if(os_memcmp(pos, hapd->own_addr, ETH_ALEN) == 0) {
+++ /* Shift for BSSID + BSSID info + Op_class + channel num + PHY type */
+++ pos = pos + 6 + 4 + 1 + 1 + 1;
+++
+++ /* Iterate Subelement */
+++ while (next_nr - pos > 0) {
+++ if (*pos == 3) {
+++ pos = pos + 2;
+++ *pos = pref;
+++ return;
+++ } else {
+++ pos++;
+++ int shift_len = *pos++;
+++ pos = pos + shift_len;
+++ }
+++ }
+++ }
+++ }
+++}
++diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
++index e93d1d5..dc6807b 100644
++--- a/src/ap/neighbor_db.h
+++++ b/src/ap/neighbor_db.h
++@@ -26,4 +26,6 @@ void hostapd_free_neighbor_db(struct hostapd_data *hapd);
++ int hostapd_neighbor_count(struct hostapd_data *hapd);
++ int hostapd_neighbor_insert_buffer(struct hostapd_data *hapd, char *buf,
++ size_t buflen);
+++void hostapd_neighbor_set_own_report_pref(struct hostapd_data *hapd, char *nei_buf,
+++ size_t buflen, const int pref);
++ #endif /* NEIGHBOR_DB_H */
++--
++2.18.0
++
+diff --git a/package/network/services/hostapd/patches/910-master-Add-hostapd_neighbor_set_pref_by_non_pref_chan.patch b/package/network/services/hostapd/patches/910-master-Add-hostapd_neighbor_set_pref_by_non_pref_chan.patch
+new file mode 100644
+index 0000000..b38b216
+--- /dev/null
++++ b/package/network/services/hostapd/patches/910-master-Add-hostapd_neighbor_set_pref_by_non_pref_chan.patch
+@@ -0,0 +1,101 @@
++From 3306cdad16655901c1b76372fa939eae20ac8b26 Mon Sep 17 00:00:00 2001
++From: "howard.hsu" <howard-yh.hsu@mediatek.com>
++Date: Wed, 19 Jan 2022 21:32:17 +0800
++Subject: [PATCH 9/9] Add hostapd_neighbor_set_pref_by_non_pref_chan()
++
++The preference value of neighbor report shall be modified according to struct
++non_pref_chan_info.
++---
++ hostapd/ctrl_iface.c | 2 ++
++ src/ap/neighbor_db.c | 51 ++++++++++++++++++++++++++++++++++++++++++++
++ src/ap/neighbor_db.h | 4 ++++
++ 3 files changed, 57 insertions(+)
++
++diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
++index 5be75e5..2e11e7b 100644
++--- a/hostapd/ctrl_iface.c
+++++ b/hostapd/ctrl_iface.c
++@@ -999,6 +999,8 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
++ hostapd_neighbor_set_own_report_pref(hapd, nei_rep, nei_len, 0);
++ }
++
+++ hostapd_neighbor_set_pref_by_non_pref_chan(hapd, sta, nei_rep, nei_len);
+++
++ #ifdef CONFIG_MBO
++ pos = os_strstr(cmd, "mbo=");
++ if (pos) {
++diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
++index 6473800..254dc39 100644
++--- a/src/ap/neighbor_db.c
+++++ b/src/ap/neighbor_db.c
++@@ -384,3 +384,54 @@ void hostapd_neighbor_set_own_report_pref(struct hostapd_data *hapd, char *nei_b
++ }
++ }
++ }
+++
+++#ifdef CONFIG_MBO
+++void hostapd_neighbor_set_pref_by_non_pref_chan(struct hostapd_data *hapd,
+++ struct sta_info* sta, char *nei_buf, size_t buflen)
+++{
+++ struct hostapd_neighbor_entry *nr;
+++ struct mbo_non_pref_chan_info *info;
+++ u8 i;
+++
+++ for(info = sta->non_pref_chan; info; info = info->next) {
+++ /* Check OP_Class and Channel num */
+++ for(i = 0; i < info->num_channels; i++) {
+++ char *pos, *next_nr;
+++
+++ pos = nei_buf;
+++ next_nr = nei_buf;
+++
+++ /* Iterate Neighbor report database */
+++ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+++ list) {
+++ pos = next_nr;
+++ next_nr = pos + 2 + wpabuf_len(nr->nr);
+++ /**
+++ * Shift 12 bytes for Element ID, Neighbor report length,
+++ * BSSID and BSSID info.
+++ */
+++ pos = pos + 12;
+++ int nr_op_class = *pos++;
+++ int nr_channel = *pos;
+++ if(info->op_class == nr_op_class && info->channels[i] == nr_channel) {
+++ /* Shift for Channel Num + PHY type */
+++ pos = pos + 1 + 1;
+++
+++ // Iterate Subelement
+++ while(next_nr - pos > 0) {
+++ if(*pos == 3) {
+++ pos = pos + 2;
+++ *pos = info->pref;
+++ break;
+++ }else {
+++ pos++;
+++ int shift_len = *pos++;
+++ pos = pos + shift_len;
+++ }
+++ }
+++ }
+++ }
+++ }
+++ }
+++}
+++#endif
++diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
++index dc6807b..731d1b8 100644
++--- a/src/ap/neighbor_db.h
+++++ b/src/ap/neighbor_db.h
++@@ -28,4 +28,8 @@ int hostapd_neighbor_insert_buffer(struct hostapd_data *hapd, char *buf,
++ size_t buflen);
++ void hostapd_neighbor_set_own_report_pref(struct hostapd_data *hapd, char *nei_buf,
++ size_t buflen, const int pref);
+++#ifdef CONFIG_MBO
+++void hostapd_neighbor_set_pref_by_non_pref_chan(struct hostapd_data *hapd,
+++ struct sta_info* sta, char *nei_buf, size_t buflen);
+++#endif
++ #endif /* NEIGHBOR_DB_H */
++--
++2.18.0
++
+--
+2.18.0
+